Bug 1045948 - IonMonkey: Eliminate LDefintion::PASSTHROUGH. r=bhackett

This commit is contained in:
Dan Gohman 2014-08-16 13:13:13 -07:00
Родитель b112ee74ce
Коммит 64aabd5f06
11 изменённых файлов: 90 добавлений и 126 удалений

Просмотреть файл

@ -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);
}