зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1045948 - IonMonkey: Eliminate LDefintion::PASSTHROUGH. r=bhackett
This commit is contained in:
Родитель
b112ee74ce
Коммит
64aabd5f06
|
@ -371,8 +371,6 @@ PrintDefinition(char *buf, size_t size, const LDefinition &def)
|
|||
cursor += JS_snprintf(cursor, end - cursor, ":%s", def.output()->toString());
|
||||
else if (def.policy() == LDefinition::MUST_REUSE_INPUT)
|
||||
cursor += JS_snprintf(cursor, end - cursor, ":tied(%u)", def.getReusedInput());
|
||||
else if (def.policy() == LDefinition::PASSTHROUGH)
|
||||
cursor += JS_snprintf(cursor, end - cursor, ":-");
|
||||
}
|
||||
|
||||
const char *
|
||||
|
|
|
@ -279,6 +279,7 @@ class LUse : public LAllocation
|
|||
}
|
||||
uint32_t virtualRegister() const {
|
||||
uint32_t index = (data() >> VREG_SHIFT) & VREG_MASK;
|
||||
JS_ASSERT(index != 0);
|
||||
return index;
|
||||
}
|
||||
uint32_t registerCode() const {
|
||||
|
@ -411,13 +412,7 @@ class LDefinition
|
|||
|
||||
// One definition per instruction must re-use the first input
|
||||
// allocation, which (for now) must be a register.
|
||||
MUST_REUSE_INPUT,
|
||||
|
||||
// This definition's virtual register is the same as another; this is
|
||||
// for instructions which consume a register and silently define it as
|
||||
// the same register. It is not legal to use this if doing so would
|
||||
// change the type of the virtual register.
|
||||
PASSTHROUGH
|
||||
MUST_REUSE_INPUT
|
||||
};
|
||||
|
||||
enum Type {
|
||||
|
@ -508,7 +503,9 @@ class LDefinition
|
|||
return type() == FLOAT32 || type() == DOUBLE || isSimdType();
|
||||
}
|
||||
uint32_t virtualRegister() const {
|
||||
return (bits_ >> VREG_SHIFT) & VREG_MASK;
|
||||
uint32_t index = (bits_ >> VREG_SHIFT) & VREG_MASK;
|
||||
JS_ASSERT(index != 0);
|
||||
return index;
|
||||
}
|
||||
LAllocation *output() {
|
||||
return &output_;
|
||||
|
|
|
@ -525,10 +525,10 @@ LiveRangeAllocator<VREG, forLSRA>::init()
|
|||
for (LInstructionIterator ins = block->begin(); ins != block->end(); ins++) {
|
||||
for (size_t j = 0; j < ins->numDefs(); j++) {
|
||||
LDefinition *def = ins->getDef(j);
|
||||
if (def->policy() != LDefinition::PASSTHROUGH) {
|
||||
if (!vregs[def].init(alloc(), block, *ins, def, /* isTemp */ false))
|
||||
return false;
|
||||
}
|
||||
if (def->isBogusTemp())
|
||||
continue;
|
||||
if (!vregs[def].init(alloc(), block, *ins, def, /* isTemp */ false))
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t j = 0; j < ins->numTemps(); j++) {
|
||||
|
@ -665,50 +665,50 @@ LiveRangeAllocator<VREG, forLSRA>::buildLivenessInfo()
|
|||
}
|
||||
|
||||
for (size_t i = 0; i < ins->numDefs(); i++) {
|
||||
if (ins->getDef(i)->policy() != LDefinition::PASSTHROUGH) {
|
||||
LDefinition *def = ins->getDef(i);
|
||||
LDefinition *def = ins->getDef(i);
|
||||
if (def->isBogusTemp())
|
||||
continue;
|
||||
|
||||
CodePosition from;
|
||||
if (def->policy() == LDefinition::FIXED && def->output()->isRegister() && forLSRA) {
|
||||
// The fixed range covers the current instruction so the
|
||||
// interval for the virtual register starts at the next
|
||||
// instruction. If the next instruction has a fixed use,
|
||||
// this can lead to unnecessary register moves. To avoid
|
||||
// special handling for this, assert the next instruction
|
||||
// has no fixed uses. defineFixed guarantees this by inserting
|
||||
// an LNop.
|
||||
JS_ASSERT(!NextInstructionHasFixedUses(block, *ins));
|
||||
AnyRegister reg = def->output()->toRegister();
|
||||
if (!addFixedRangeAtHead(reg, inputOf(*ins), outputOf(*ins).next()))
|
||||
return false;
|
||||
from = outputOf(*ins).next();
|
||||
} else {
|
||||
from = forLSRA ? inputOf(*ins) : outputOf(*ins);
|
||||
}
|
||||
|
||||
if (def->policy() == LDefinition::MUST_REUSE_INPUT) {
|
||||
// MUST_REUSE_INPUT is implemented by allocating an output
|
||||
// register and moving the input to it. Register hints are
|
||||
// used to avoid unnecessary moves. We give the input an
|
||||
// LUse::ANY policy to avoid allocating a register for the
|
||||
// input.
|
||||
LUse *inputUse = ins->getOperand(def->getReusedInput())->toUse();
|
||||
JS_ASSERT(inputUse->policy() == LUse::REGISTER);
|
||||
JS_ASSERT(inputUse->usedAtStart());
|
||||
*inputUse = LUse(inputUse->virtualRegister(), LUse::ANY, /* usedAtStart = */ true);
|
||||
}
|
||||
|
||||
LiveInterval *interval = vregs[def].getInterval(0);
|
||||
interval->setFrom(from);
|
||||
|
||||
// Ensure that if there aren't any uses, there's at least
|
||||
// some interval for the output to go into.
|
||||
if (interval->numRanges() == 0) {
|
||||
if (!interval->addRangeAtHead(from, from.next()))
|
||||
return false;
|
||||
}
|
||||
live->remove(def->virtualRegister());
|
||||
CodePosition from;
|
||||
if (def->policy() == LDefinition::FIXED && def->output()->isRegister() && forLSRA) {
|
||||
// The fixed range covers the current instruction so the
|
||||
// interval for the virtual register starts at the next
|
||||
// instruction. If the next instruction has a fixed use,
|
||||
// this can lead to unnecessary register moves. To avoid
|
||||
// special handling for this, assert the next instruction
|
||||
// has no fixed uses. defineFixed guarantees this by inserting
|
||||
// an LNop.
|
||||
JS_ASSERT(!NextInstructionHasFixedUses(block, *ins));
|
||||
AnyRegister reg = def->output()->toRegister();
|
||||
if (!addFixedRangeAtHead(reg, inputOf(*ins), outputOf(*ins).next()))
|
||||
return false;
|
||||
from = outputOf(*ins).next();
|
||||
} else {
|
||||
from = forLSRA ? inputOf(*ins) : outputOf(*ins);
|
||||
}
|
||||
|
||||
if (def->policy() == LDefinition::MUST_REUSE_INPUT) {
|
||||
// MUST_REUSE_INPUT is implemented by allocating an output
|
||||
// register and moving the input to it. Register hints are
|
||||
// used to avoid unnecessary moves. We give the input an
|
||||
// LUse::ANY policy to avoid allocating a register for the
|
||||
// input.
|
||||
LUse *inputUse = ins->getOperand(def->getReusedInput())->toUse();
|
||||
JS_ASSERT(inputUse->policy() == LUse::REGISTER);
|
||||
JS_ASSERT(inputUse->usedAtStart());
|
||||
*inputUse = LUse(inputUse->virtualRegister(), LUse::ANY, /* usedAtStart = */ true);
|
||||
}
|
||||
|
||||
LiveInterval *interval = vregs[def].getInterval(0);
|
||||
interval->setFrom(from);
|
||||
|
||||
// Ensure that if there aren't any uses, there's at least
|
||||
// some interval for the output to go into.
|
||||
if (interval->numRanges() == 0) {
|
||||
if (!interval->addRangeAtHead(from, from.next()))
|
||||
return false;
|
||||
}
|
||||
live->remove(def->virtualRegister());
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < ins->numTemps(); i++) {
|
||||
|
|
|
@ -185,8 +185,6 @@ DefinitionCompatibleWith(LInstruction *ins, const LDefinition *def, LAllocation
|
|||
if (!alloc.isRegister() || !ins->numOperands())
|
||||
return false;
|
||||
return alloc == *ins->getOperand(def->getReusedInput());
|
||||
case LDefinition::PASSTHROUGH:
|
||||
return true;
|
||||
default:
|
||||
MOZ_ASSUME_UNREACHABLE("Unknown definition policy");
|
||||
}
|
||||
|
@ -490,6 +488,7 @@ class VirtualRegister
|
|||
}
|
||||
bool addInterval(LiveInterval *interval) {
|
||||
JS_ASSERT(interval->numRanges());
|
||||
JS_ASSERT(interval->vreg() != 0);
|
||||
|
||||
// Preserve ascending order for faster lookups.
|
||||
LiveInterval **found = nullptr;
|
||||
|
|
|
@ -53,14 +53,18 @@ AllocationIntegrityState::record()
|
|||
InstructionInfo &info = instructions[ins->id()];
|
||||
|
||||
for (size_t k = 0; k < ins->numTemps(); k++) {
|
||||
uint32_t vreg = ins->getTemp(k)->virtualRegister();
|
||||
virtualRegisters[vreg] = ins->getTemp(k);
|
||||
if (!ins->getTemp(k)->isBogusTemp()) {
|
||||
uint32_t vreg = ins->getTemp(k)->virtualRegister();
|
||||
virtualRegisters[vreg] = ins->getTemp(k);
|
||||
}
|
||||
if (!info.temps.append(*ins->getTemp(k)))
|
||||
return false;
|
||||
}
|
||||
for (size_t k = 0; k < ins->numDefs(); k++) {
|
||||
uint32_t vreg = ins->getDef(k)->virtualRegister();
|
||||
virtualRegisters[vreg] = ins->getDef(k);
|
||||
if (!ins->getDef(k)->isBogusTemp()) {
|
||||
uint32_t vreg = ins->getDef(k)->virtualRegister();
|
||||
virtualRegisters[vreg] = ins->getDef(k);
|
||||
}
|
||||
if (!info.outputs.append(*ins->getDef(k)))
|
||||
return false;
|
||||
}
|
||||
|
@ -95,7 +99,7 @@ AllocationIntegrityState::check(bool populateSafepoints)
|
|||
|
||||
for (size_t i = 0; i < ins->numDefs(); i++) {
|
||||
LDefinition *def = ins->getDef(i);
|
||||
JS_ASSERT_IF(def->policy() != LDefinition::PASSTHROUGH, !def->output()->isUse());
|
||||
JS_ASSERT(!def->output()->isUse());
|
||||
|
||||
LDefinition oldDef = instructions[ins->id()].outputs[i];
|
||||
JS_ASSERT_IF(oldDef.policy() == LDefinition::MUST_REUSE_INPUT,
|
||||
|
@ -132,6 +136,8 @@ AllocationIntegrityState::check(bool populateSafepoints)
|
|||
LSafepoint *safepoint = ins->safepoint();
|
||||
if (safepoint) {
|
||||
for (size_t i = 0; i < ins->numTemps(); i++) {
|
||||
if (ins->getTemp(i)->isBogusTemp())
|
||||
continue;
|
||||
uint32_t vreg = info.temps[i].virtualRegister();
|
||||
LAllocation *alloc = ins->getTemp(i)->output();
|
||||
if (!checkSafepointAllocation(ins, vreg, *alloc, populateSafepoints))
|
||||
|
@ -200,7 +206,7 @@ AllocationIntegrityState::checkIntegrity(LBlock *block, LInstruction *ins,
|
|||
|
||||
for (size_t i = 0; i < ins->numDefs(); i++) {
|
||||
LDefinition *def = ins->getDef(i);
|
||||
if (def->policy() == LDefinition::PASSTHROUGH)
|
||||
if (def->isBogusTemp())
|
||||
continue;
|
||||
if (info.outputs[i].virtualRegister() == vreg) {
|
||||
JS_ASSERT(*def->output() == alloc);
|
||||
|
|
|
@ -51,8 +51,7 @@ StupidAllocator::init()
|
|||
for (LInstructionIterator ins = block->begin(); ins != block->end(); ins++) {
|
||||
for (size_t j = 0; j < ins->numDefs(); j++) {
|
||||
LDefinition *def = ins->getDef(j);
|
||||
if (def->policy() != LDefinition::PASSTHROUGH)
|
||||
virtualRegisters[def->virtualRegister()] = def;
|
||||
virtualRegisters[def->virtualRegister()] = def;
|
||||
}
|
||||
|
||||
for (size_t j = 0; j < ins->numTemps(); j++) {
|
||||
|
@ -370,8 +369,7 @@ StupidAllocator::allocateForInstruction(LInstruction *ins)
|
|||
}
|
||||
for (size_t i = 0; i < ins->numDefs(); i++) {
|
||||
LDefinition *def = ins->getDef(i);
|
||||
if (def->policy() != LDefinition::PASSTHROUGH)
|
||||
allocateForDefinition(ins, def);
|
||||
allocateForDefinition(ins, def);
|
||||
}
|
||||
|
||||
// Allocate for remaining inputs which do not need to be in registers.
|
||||
|
|
|
@ -107,15 +107,14 @@ LIRGeneratorARM::visitBox(MBox *box)
|
|||
if (vreg >= MAX_VIRTUAL_REGISTERS)
|
||||
return false;
|
||||
|
||||
// Note that because we're using PASSTHROUGH, we do not change the type of
|
||||
// Note that because we're using BogusTemp(), we do not change the type of
|
||||
// the definition. We also do not define the first output as "TYPE",
|
||||
// because it has no corresponding payload at (vreg + 1). Also note that
|
||||
// although we copy the input's original type for the payload half of the
|
||||
// definition, this is only for clarity. PASSTHROUGH definitions are
|
||||
// definition, this is only for clarity. BogusTemp() definitions are
|
||||
// ignored.
|
||||
lir->setDef(0, LDefinition(vreg, LDefinition::GENERAL));
|
||||
lir->setDef(1, LDefinition(inner->virtualRegister(), LDefinition::TypeFrom(inner->type()),
|
||||
LDefinition::PASSTHROUGH));
|
||||
lir->setDef(1, LDefinition::BogusTemp());
|
||||
box->setVirtualRegister(vreg);
|
||||
return add(lir);
|
||||
}
|
||||
|
@ -148,12 +147,11 @@ LIRGeneratorARM::visitUnbox(MUnbox *unbox)
|
|||
if (unbox->fallible() && !assignSnapshot(lir, unbox->bailoutKind()))
|
||||
return false;
|
||||
|
||||
// Note that PASSTHROUGH here is illegal, since types and payloads form two
|
||||
// separate intervals. If the type becomes dead before the payload, it
|
||||
// could be used as a Value without the type being recoverable. Unbox's
|
||||
// purpose is to eagerly kill the definition of a type tag, so keeping both
|
||||
// alive (for the purpose of gcmaps) is unappealing. Instead, we create a
|
||||
// new virtual register.
|
||||
// Types and payloads form two separate intervals. If the type becomes dead
|
||||
// before the payload, it could be used as a Value without the type being
|
||||
// recoverable. Unbox's purpose is to eagerly kill the definition of a type
|
||||
// tag, so keeping both alive (for the purpose of gcmaps) is unappealing.
|
||||
// Instead, we create a new virtual register.
|
||||
return defineReuseInput(lir, unbox, 0);
|
||||
}
|
||||
|
||||
|
|
|
@ -109,15 +109,14 @@ LIRGeneratorMIPS::visitBox(MBox *box)
|
|||
if (vreg >= MAX_VIRTUAL_REGISTERS)
|
||||
return false;
|
||||
|
||||
// Note that because we're using PASSTHROUGH, we do not change the type of
|
||||
// Note that because we're using BogusTemp(), we do not change the type of
|
||||
// the definition. We also do not define the first output as "TYPE",
|
||||
// because it has no corresponding payload at (vreg + 1). Also note that
|
||||
// although we copy the input's original type for the payload half of the
|
||||
// definition, this is only for clarity. PASSTHROUGH definitions are
|
||||
// definition, this is only for clarity. BogusTemp() definitions are
|
||||
// ignored.
|
||||
lir->setDef(0, LDefinition(vreg, LDefinition::GENERAL));
|
||||
lir->setDef(1, LDefinition(inner->virtualRegister(), LDefinition::TypeFrom(inner->type()),
|
||||
LDefinition::PASSTHROUGH));
|
||||
lir->setDef(1, LDefinition::BogusTemp());
|
||||
box->setVirtualRegister(vreg);
|
||||
return add(lir);
|
||||
}
|
||||
|
@ -151,12 +150,11 @@ LIRGeneratorMIPS::visitUnbox(MUnbox *unbox)
|
|||
if (unbox->fallible() && !assignSnapshot(lir, unbox->bailoutKind()))
|
||||
return false;
|
||||
|
||||
// Note that PASSTHROUGH here is illegal, since types and payloads form two
|
||||
// separate intervals. If the type becomes dead before the payload, it
|
||||
// could be used as a Value without the type being recoverable. Unbox's
|
||||
// purpose is to eagerly kill the definition of a type tag, so keeping both
|
||||
// alive (for the purpose of gcmaps) is unappealing. Instead, we create a
|
||||
// new virtual register.
|
||||
// Types and payloads form two separate intervals. If the type becomes dead
|
||||
// before the payload, it could be used as a Value without the type being
|
||||
// recoverable. Unbox's purpose is to eagerly kill the definition of a type
|
||||
// tag, so keeping both alive (for the purpose of gcmaps) is unappealing.
|
||||
// Instead, we create a new virtual register.
|
||||
return defineReuseInput(lir, unbox, 0);
|
||||
}
|
||||
|
||||
|
|
|
@ -225,30 +225,6 @@ LIRGeneratorShared::redefine(MDefinition *def, MDefinition *as)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
LIRGeneratorShared::defineAs(LInstruction *outLir, MDefinition *outMir, MDefinition *inMir)
|
||||
{
|
||||
uint32_t vreg = inMir->virtualRegister();
|
||||
LDefinition::Policy policy = LDefinition::PASSTHROUGH;
|
||||
|
||||
if (outMir->type() == MIRType_Value) {
|
||||
#ifdef JS_NUNBOX32
|
||||
outLir->setDef(TYPE_INDEX,
|
||||
LDefinition(vreg + VREG_TYPE_OFFSET, LDefinition::TYPE, policy));
|
||||
outLir->setDef(PAYLOAD_INDEX,
|
||||
LDefinition(vreg + VREG_DATA_OFFSET, LDefinition::PAYLOAD, policy));
|
||||
#elif JS_PUNBOX64
|
||||
outLir->setDef(0, LDefinition(vreg, LDefinition::BOX, policy));
|
||||
#else
|
||||
# error "Unexpected boxing type"
|
||||
#endif
|
||||
} else {
|
||||
outLir->setDef(0, LDefinition(vreg, LDefinition::TypeFrom(inMir->type()), policy));
|
||||
}
|
||||
outLir->setMir(outMir);
|
||||
return redefine(outMir, inMir);
|
||||
}
|
||||
|
||||
bool
|
||||
LIRGeneratorShared::ensureDefined(MDefinition *mir)
|
||||
{
|
||||
|
|
|
@ -153,10 +153,6 @@ class LIRGeneratorShared : public MDefinitionVisitorDefaultNYI
|
|||
// virtual register as |as|.
|
||||
inline bool redefine(MDefinition *ins, MDefinition *as);
|
||||
|
||||
// Defines an IR's output as the same as another IR. This is similar to
|
||||
// redefine(), but used when creating new LIR.
|
||||
inline bool defineAs(LInstruction *outLir, MDefinition *outMir, MDefinition *inMir);
|
||||
|
||||
TempAllocator &alloc() const {
|
||||
return graph.alloc();
|
||||
}
|
||||
|
|
|
@ -100,15 +100,14 @@ LIRGeneratorX86::visitBox(MBox *box)
|
|||
if (vreg >= MAX_VIRTUAL_REGISTERS)
|
||||
return false;
|
||||
|
||||
// Note that because we're using PASSTHROUGH, we do not change the type of
|
||||
// Note that because we're using BogusTemp(), we do not change the type of
|
||||
// the definition. We also do not define the first output as "TYPE",
|
||||
// because it has no corresponding payload at (vreg + 1). Also note that
|
||||
// although we copy the input's original type for the payload half of the
|
||||
// definition, this is only for clarity. PASSTHROUGH definitions are
|
||||
// definition, this is only for clarity. BogusTemp() definitions are
|
||||
// ignored.
|
||||
lir->setDef(0, LDefinition(vreg, LDefinition::GENERAL));
|
||||
lir->setDef(1, LDefinition(inner->virtualRegister(), LDefinition::TypeFrom(inner->type()),
|
||||
LDefinition::PASSTHROUGH));
|
||||
lir->setDef(1, LDefinition::BogusTemp());
|
||||
box->setVirtualRegister(vreg);
|
||||
return add(lir);
|
||||
}
|
||||
|
@ -141,12 +140,11 @@ LIRGeneratorX86::visitUnbox(MUnbox *unbox)
|
|||
if (unbox->fallible() && !assignSnapshot(lir, unbox->bailoutKind()))
|
||||
return false;
|
||||
|
||||
// Note that PASSTHROUGH here is illegal, since types and payloads form two
|
||||
// separate intervals. If the type becomes dead before the payload, it
|
||||
// could be used as a Value without the type being recoverable. Unbox's
|
||||
// purpose is to eagerly kill the definition of a type tag, so keeping both
|
||||
// alive (for the purpose of gcmaps) is unappealing. Instead, we create a
|
||||
// new virtual register.
|
||||
// Types and payloads form two separate intervals. If the type becomes dead
|
||||
// before the payload, it could be used as a Value without the type being
|
||||
// recoverable. Unbox's purpose is to eagerly kill the definition of a type
|
||||
// tag, so keeping both alive (for the purpose of gcmaps) is unappealing.
|
||||
// Instead, we create a new virtual register.
|
||||
return defineReuseInput(lir, unbox, 0);
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче