AirFixPartialRegisterStalls.cpp [plain text]
#include "config.h"
#include "AirFixPartialRegisterStalls.h"
#if ENABLE(B3_JIT)
#include "AirBasicBlock.h"
#include "AirCode.h"
#include "AirInsertionSet.h"
#include "AirInst.h"
#include "AirInstInlines.h"
#include "AirPhaseScope.h"
#include "MacroAssembler.h"
#include <wtf/IndexMap.h>
#include <wtf/IndexSet.h>
#include <wtf/Vector.h>
namespace JSC { namespace B3 { namespace Air {
namespace {
bool hasPartialXmmRegUpdate(const Inst& inst)
{
switch (inst.kind.opcode) {
case ConvertDoubleToFloat:
case ConvertFloatToDouble:
case ConvertInt32ToDouble:
case ConvertInt64ToDouble:
case ConvertInt32ToFloat:
case ConvertInt64ToFloat:
case SqrtDouble:
case SqrtFloat:
case CeilDouble:
case CeilFloat:
case FloorDouble:
case FloorFloat:
return true;
default:
break;
}
return false;
}
bool isDependencyBreaking(const Inst& inst)
{
return inst.kind.opcode == MoveZeroToDouble;
}
unsigned char minimumSafeDistance = 16;
struct FPDefDistance {
FPDefDistance()
{
for (unsigned i = 0; i < MacroAssembler::numberOfFPRegisters(); ++i)
distance[i] = 255;
}
void reset(FPRReg reg)
{
unsigned index = MacroAssembler::fpRegisterIndex(reg);
distance[index] = 255;
}
void add(FPRReg reg, unsigned registerDistance)
{
unsigned index = MacroAssembler::fpRegisterIndex(reg);
if (registerDistance < distance[index])
distance[index] = static_cast<unsigned char>(registerDistance);
}
bool updateFromPrecessor(FPDefDistance& precessorDistance, unsigned constantOffset = 0)
{
bool changed = false;
for (unsigned i = 0; i < MacroAssembler::numberOfFPRegisters(); ++i) {
unsigned regDistance = precessorDistance.distance[i] + constantOffset;
if (regDistance < minimumSafeDistance && regDistance < distance[i]) {
distance[i] = regDistance;
changed = true;
}
}
return changed;
}
unsigned char distance[MacroAssembler::numberOfFPRegisters()];
};
void updateDistances(Inst& inst, FPDefDistance& localDistance, unsigned& distanceToBlockEnd)
{
--distanceToBlockEnd;
if (isDependencyBreaking(inst)) {
localDistance.reset(inst.args[0].tmp().fpr());
return;
}
inst.forEachTmp([&] (Tmp& tmp, Arg::Role role, Bank, Width) {
ASSERT_WITH_MESSAGE(tmp.isReg(), "This phase must be run after register allocation.");
if (tmp.isFPR() && Arg::isAnyDef(role))
localDistance.add(tmp.fpr(), distanceToBlockEnd);
});
}
}
void fixPartialRegisterStalls(Code& code)
{
if (!isX86())
return;
PhaseScope phaseScope(code, "fixPartialRegisterStalls");
Vector<BasicBlock*> candidates;
for (BasicBlock* block : code) {
for (const Inst& inst : *block) {
if (hasPartialXmmRegUpdate(inst)) {
candidates.append(block);
break;
}
}
}
if (candidates.isEmpty())
return;
IndexMap<BasicBlock*, FPDefDistance> lastDefDistance(code.size());
IndexSet<BasicBlock*> dirty;
for (BasicBlock* block : code) {
FPDefDistance localDistance;
unsigned distanceToBlockEnd = block->size();
for (Inst& inst : *block)
updateDistances(inst, localDistance, distanceToBlockEnd);
for (BasicBlock* successor : block->successorBlocks()) {
if (lastDefDistance[successor].updateFromPrecessor(localDistance))
dirty.add(successor);
}
}
bool changed;
do {
changed = false;
for (BasicBlock* block : code) {
if (!dirty.remove(block))
continue;
if (block->size() >= minimumSafeDistance)
continue;
unsigned blockSize = block->size();
FPDefDistance& blockDistance = lastDefDistance[block];
for (BasicBlock* successor : block->successorBlocks()) {
if (lastDefDistance[successor].updateFromPrecessor(blockDistance, blockSize)) {
dirty.add(successor);
changed = true;
}
}
}
} while (changed);
InsertionSet insertionSet(code);
for (BasicBlock* block : candidates) {
unsigned distanceToBlockEnd = block->size();
FPDefDistance& localDistance = lastDefDistance[block];
for (unsigned i = 0; i < block->size(); ++i) {
Inst& inst = block->at(i);
if (hasPartialXmmRegUpdate(inst)) {
RegisterSet defs;
RegisterSet uses;
inst.forEachTmp([&] (Tmp& tmp, Arg::Role role, Bank, Width) {
if (tmp.isFPR()) {
if (Arg::isAnyDef(role))
defs.set(tmp.fpr());
if (Arg::isAnyUse(role))
uses.set(tmp.fpr());
}
});
defs.exclude(uses);
defs.forEach([&] (Reg reg) {
if (localDistance.distance[MacroAssembler::fpRegisterIndex(reg.fpr())] < minimumSafeDistance)
insertionSet.insert(i, MoveZeroToDouble, inst.origin, Tmp(reg));
});
}
updateDistances(inst, localDistance, distanceToBlockEnd);
}
insertionSet.execute(block);
}
}
} } }
#endif // ENABLE(B3_JIT)