Bug 1131403 - Optimize uses of ObjectOrNull properties of unboxed objects better, r=jandem.

This commit is contained in:
Brian Hackett 2015-02-14 14:55:48 -07:00
parent 0784d3889f
commit 6a09ea4902
19 changed files with 360 additions and 129 deletions

View File

@ -0,0 +1,17 @@
function foo(p) {
this.f = p;
}
function use(v, a, b) {
var f = v.f;
g = f;
g = a + b;
if (f != null)
return;
}
with({}){}
for (var i = 0; i < 2000; i++)
use(new foo(i % 2 ? {} : null), 1, 2);
use(new foo(null), 2147483548, 1000);

View File

@ -2209,13 +2209,17 @@ CodeGenerator::visitStoreSlotT(LStoreSlotT *lir)
emitPreBarrier(dest);
MIRType valueType = lir->mir()->value()->type();
ConstantOrRegister value;
if (lir->value()->isConstant())
value = ConstantOrRegister(*lir->value()->toConstant());
else
value = TypedOrValueRegister(valueType, ToAnyRegister(lir->value()));
masm.storeUnboxedValue(value, valueType, dest, lir->mir()->slotType());
if (valueType == MIRType_ObjectOrNull) {
masm.storeObjectOrNull(ToRegister(lir->value()), dest);
} else {
ConstantOrRegister value;
if (lir->value()->isConstant())
value = ConstantOrRegister(*lir->value()->toConstant());
else
value = TypedOrValueRegister(valueType, ToAnyRegister(lir->value()));
masm.storeUnboxedValue(value, valueType, dest, lir->mir()->slotType());
}
}
void
@ -3731,7 +3735,7 @@ CodeGenerator::emitObjectOrStringResultChecks(LInstruction *lir, MDefinition *mi
// We have a result TypeSet, assert this object is in it.
Label miss, ok;
if (mir->type() == MIRType_ObjectOrNull)
masm.branchPtr(Assembler::NotEqual, output, ImmWord(0), &ok);
masm.branchPtr(Assembler::Equal, output, ImmWord(0), &ok);
if (mir->resultTypeSet()->getObjectCount() > 0)
masm.guardObjectType(output, mir->resultTypeSet(), temp, &miss);
else
@ -5335,14 +5339,14 @@ CodeGenerator::visitCompareVM(LCompareVM *lir)
}
void
CodeGenerator::visitIsNullOrLikeUndefined(LIsNullOrLikeUndefined *lir)
CodeGenerator::visitIsNullOrLikeUndefinedV(LIsNullOrLikeUndefinedV *lir)
{
JSOp op = lir->mir()->jsop();
MCompare::CompareType compareType = lir->mir()->compareType();
MOZ_ASSERT(compareType == MCompare::Compare_Undefined ||
compareType == MCompare::Compare_Null);
const ValueOperand value = ToValue(lir, LIsNullOrLikeUndefined::Value);
const ValueOperand value = ToValue(lir, LIsNullOrLikeUndefinedV::Value);
Register output = ToRegister(lir->output());
if (op == JSOP_EQ || op == JSOP_NE) {
@ -5409,14 +5413,14 @@ CodeGenerator::visitIsNullOrLikeUndefined(LIsNullOrLikeUndefined *lir)
}
void
CodeGenerator::visitIsNullOrLikeUndefinedAndBranch(LIsNullOrLikeUndefinedAndBranch *lir)
CodeGenerator::visitIsNullOrLikeUndefinedAndBranchV(LIsNullOrLikeUndefinedAndBranchV *lir)
{
JSOp op = lir->cmpMir()->jsop();
MCompare::CompareType compareType = lir->cmpMir()->compareType();
MOZ_ASSERT(compareType == MCompare::Compare_Undefined ||
compareType == MCompare::Compare_Null);
const ValueOperand value = ToValue(lir, LIsNullOrLikeUndefinedAndBranch::Value);
const ValueOperand value = ToValue(lir, LIsNullOrLikeUndefinedAndBranchV::Value);
if (op == JSOP_EQ || op == JSOP_NE) {
MBasicBlock *ifTrue;
@ -5476,76 +5480,110 @@ CodeGenerator::visitIsNullOrLikeUndefinedAndBranch(LIsNullOrLikeUndefinedAndBran
}
void
CodeGenerator::visitEmulatesUndefined(LEmulatesUndefined *lir)
CodeGenerator::visitIsNullOrLikeUndefinedT(LIsNullOrLikeUndefinedT * lir)
{
MOZ_ASSERT(lir->mir()->compareType() == MCompare::Compare_Undefined ||
lir->mir()->compareType() == MCompare::Compare_Null);
MOZ_ASSERT(lir->mir()->lhs()->type() == MIRType_Object);
MOZ_ASSERT(lir->mir()->operandMightEmulateUndefined(),
"If the object couldn't emulate undefined, this should have been folded.");
MIRType lhsType = lir->mir()->lhs()->type();
MOZ_ASSERT(lhsType == MIRType_Object || lhsType == MIRType_ObjectOrNull);
JSOp op = lir->mir()->jsop();
MOZ_ASSERT(op == JSOP_EQ || op == JSOP_NE, "Strict equality should have been folded");
MOZ_ASSERT(lhsType == MIRType_ObjectOrNull || op == JSOP_EQ || op == JSOP_NE,
"Strict equality should have been folded");
OutOfLineTestObjectWithLabels *ool = new(alloc()) OutOfLineTestObjectWithLabels();
addOutOfLineCode(ool, lir->mir());
Label *emulatesUndefined = ool->label1();
Label *doesntEmulateUndefined = ool->label2();
MOZ_ASSERT(lhsType == MIRType_ObjectOrNull || lir->mir()->operandMightEmulateUndefined(),
"If the object couldn't emulate undefined, this should have been folded.");
Register objreg = ToRegister(lir->input());
Register output = ToRegister(lir->output());
branchTestObjectEmulatesUndefined(objreg, emulatesUndefined, doesntEmulateUndefined,
output, ool);
Label done;
if ((op == JSOP_EQ || op == JSOP_NE) && lir->mir()->operandMightEmulateUndefined()) {
OutOfLineTestObjectWithLabels *ool = new(alloc()) OutOfLineTestObjectWithLabels();
addOutOfLineCode(ool, lir->mir());
masm.move32(Imm32(op == JSOP_NE), output);
masm.jump(&done);
Label *emulatesUndefined = ool->label1();
Label *doesntEmulateUndefined = ool->label2();
masm.bind(emulatesUndefined);
masm.move32(Imm32(op == JSOP_EQ), output);
masm.bind(&done);
if (lhsType == MIRType_ObjectOrNull)
masm.branchTestPtr(Assembler::Zero, objreg, objreg, emulatesUndefined);
branchTestObjectEmulatesUndefined(objreg, emulatesUndefined, doesntEmulateUndefined,
output, ool);
Label done;
masm.move32(Imm32(op == JSOP_NE), output);
masm.jump(&done);
masm.bind(emulatesUndefined);
masm.move32(Imm32(op == JSOP_EQ), output);
masm.bind(&done);
} else {
MOZ_ASSERT(lhsType == MIRType_ObjectOrNull);
Label isNull, done;
masm.branchTestPtr(Assembler::Zero, objreg, objreg, &isNull);
masm.move32(Imm32(op == JSOP_NE || op == JSOP_STRICTNE), output);
masm.jump(&done);
masm.bind(&isNull);
masm.move32(Imm32(op == JSOP_EQ || op == JSOP_STRICTEQ), output);
masm.bind(&done);
}
}
void
CodeGenerator::visitEmulatesUndefinedAndBranch(LEmulatesUndefinedAndBranch *lir)
CodeGenerator::visitIsNullOrLikeUndefinedAndBranchT(LIsNullOrLikeUndefinedAndBranchT *lir)
{
MOZ_ASSERT(lir->cmpMir()->compareType() == MCompare::Compare_Undefined ||
lir->cmpMir()->compareType() == MCompare::Compare_Null);
MOZ_ASSERT(lir->cmpMir()->operandMightEmulateUndefined(),
"Operands which can't emulate undefined should have been folded");
DebugOnly<MCompare::CompareType> compareType = lir->cmpMir()->compareType();
MOZ_ASSERT(compareType == MCompare::Compare_Undefined ||
compareType == MCompare::Compare_Null);
MIRType lhsType = lir->cmpMir()->lhs()->type();
MOZ_ASSERT(lhsType == MIRType_Object || lhsType == MIRType_ObjectOrNull);
JSOp op = lir->cmpMir()->jsop();
MOZ_ASSERT(op == JSOP_EQ || op == JSOP_NE, "Strict equality should have been folded");
MOZ_ASSERT(lhsType == MIRType_ObjectOrNull || op == JSOP_EQ || op == JSOP_NE,
"Strict equality should have been folded");
OutOfLineTestObject *ool = new(alloc()) OutOfLineTestObject();
addOutOfLineCode(ool, lir->cmpMir());
MOZ_ASSERT(lhsType == MIRType_ObjectOrNull || lir->cmpMir()->operandMightEmulateUndefined(),
"If the object couldn't emulate undefined, this should have been folded.");
Label *equal;
Label *unequal;
MBasicBlock *ifTrue;
MBasicBlock *ifFalse;
{
MBasicBlock *ifTrue;
MBasicBlock *ifFalse;
if (op == JSOP_EQ) {
ifTrue = lir->ifTrue();
ifFalse = lir->ifFalse();
} else {
// Swap branches.
ifTrue = lir->ifFalse();
ifFalse = lir->ifTrue();
op = JSOP_EQ;
}
equal = getJumpLabelForBranch(ifTrue);
unequal = getJumpLabelForBranch(ifFalse);
if (op == JSOP_EQ || op == JSOP_STRICTEQ) {
ifTrue = lir->ifTrue();
ifFalse = lir->ifFalse();
} else {
// Swap branches.
ifTrue = lir->ifFalse();
ifFalse = lir->ifTrue();
}
Register objreg = ToRegister(lir->input());
Register input = ToRegister(lir->getOperand(0));
testObjectEmulatesUndefined(objreg, equal, unequal, ToRegister(lir->temp()), ool);
if ((op == JSOP_EQ || op == JSOP_NE) && lir->cmpMir()->operandMightEmulateUndefined()) {
OutOfLineTestObject *ool = new(alloc()) OutOfLineTestObject();
addOutOfLineCode(ool, lir->cmpMir());
Label *ifTrueLabel = getJumpLabelForBranch(ifTrue);
Label *ifFalseLabel = getJumpLabelForBranch(ifFalse);
if (lhsType == MIRType_ObjectOrNull)
masm.branchTestPtr(Assembler::Zero, input, input, ifTrueLabel);
// Objects that emulate undefined are loosely equal to null/undefined.
Register scratch = ToRegister(lir->temp());
testObjectEmulatesUndefined(input, ifTrueLabel, ifFalseLabel, scratch, ool);
} else {
MOZ_ASSERT(lhsType == MIRType_ObjectOrNull);
testZeroEmitBranch(Assembler::Equal, input, ifTrue, ifFalse);
}
}
typedef JSString *(*ConcatStringsFn)(ExclusiveContext *, HandleString, HandleString);
@ -7705,15 +7743,19 @@ CodeGenerator::visitStoreFixedSlotT(LStoreFixedSlotT *ins)
const LAllocation *value = ins->value();
MIRType valueType = ins->mir()->value()->type();
ConstantOrRegister nvalue = value->isConstant()
? ConstantOrRegister(*value->toConstant())
: TypedOrValueRegister(valueType, ToAnyRegister(value));
Address address(obj, NativeObject::getFixedSlotOffset(slot));
if (ins->mir()->needsBarrier())
emitPreBarrier(address);
masm.storeConstantOrRegister(nvalue, address);
if (valueType == MIRType_ObjectOrNull) {
Register nvalue = ToRegister(value);
masm.storeObjectOrNull(nvalue, address);
} else {
ConstantOrRegister nvalue = value->isConstant()
? ConstantOrRegister(*value->toConstant())
: TypedOrValueRegister(valueType, ToAnyRegister(value));
masm.storeConstantOrRegister(nvalue, address);
}
}
void
@ -8471,8 +8513,8 @@ CodeGenerator::visitLoadUnboxedPointerT(LLoadUnboxedPointerT *lir)
bool bailOnNull;
int32_t offsetAdjustment;
if (lir->mir()->isLoadUnboxedObjectOrNull()) {
bailOnNull = lir->mir()->toLoadUnboxedObjectOrNull()->nullBehavior() !=
MLoadUnboxedObjectOrNull::NullNotPossible;
bailOnNull = lir->mir()->toLoadUnboxedObjectOrNull()->nullBehavior() ==
MLoadUnboxedObjectOrNull::BailOnNull;
offsetAdjustment = lir->mir()->toLoadUnboxedObjectOrNull()->offsetAdjustment();
} else if (lir->mir()->isLoadUnboxedString()) {
bailOnNull = false;
@ -8496,6 +8538,18 @@ CodeGenerator::visitLoadUnboxedPointerT(LLoadUnboxedPointerT *lir)
}
}
void
CodeGenerator::visitUnboxObjectOrNull(LUnboxObjectOrNull *lir)
{
Register obj = ToRegister(lir->input());
if (lir->mir()->fallible()) {
Label bail;
masm.branchTestPtr(Assembler::Zero, obj, obj, &bail);
bailoutFrom(&bail, lir->snapshot());
}
}
void
CodeGenerator::visitLoadTypedArrayElement(LLoadTypedArrayElement *lir)
{

View File

@ -221,10 +221,10 @@ class CodeGenerator : public CodeGeneratorSpecific
void visitCompareS(LCompareS *lir);
void visitCompareStrictS(LCompareStrictS *lir);
void visitCompareVM(LCompareVM *lir);
void visitIsNullOrLikeUndefined(LIsNullOrLikeUndefined *lir);
void visitIsNullOrLikeUndefinedAndBranch(LIsNullOrLikeUndefinedAndBranch *lir);
void visitEmulatesUndefined(LEmulatesUndefined *lir);
void visitEmulatesUndefinedAndBranch(LEmulatesUndefinedAndBranch *lir);
void visitIsNullOrLikeUndefinedV(LIsNullOrLikeUndefinedV *lir);
void visitIsNullOrLikeUndefinedT(LIsNullOrLikeUndefinedT *lir);
void visitIsNullOrLikeUndefinedAndBranchV(LIsNullOrLikeUndefinedAndBranchV *lir);
void visitIsNullOrLikeUndefinedAndBranchT(LIsNullOrLikeUndefinedAndBranchT *lir);
void emitConcat(LInstruction *lir, Register lhs, Register rhs, Register output);
void visitConcat(LConcat *lir);
void visitCharCodeAt(LCharCodeAt *lir);
@ -245,6 +245,7 @@ class CodeGenerator : public CodeGeneratorSpecific
void visitLoadElementHole(LLoadElementHole *lir);
void visitLoadUnboxedPointerV(LLoadUnboxedPointerV *lir);
void visitLoadUnboxedPointerT(LLoadUnboxedPointerT *lir);
void visitUnboxObjectOrNull(LUnboxObjectOrNull *lir);
void visitStoreElementT(LStoreElementT *lir);
void visitStoreElementV(LStoreElementV *lir);
void visitStoreElementHoleT(LStoreElementHoleT *lir);

View File

@ -2518,6 +2518,51 @@ TryEliminateTypeBarrier(MTypeBarrier *barrier, bool *eliminated)
return true;
}
static bool
TryOptimizeLoadUnboxedObjectOrNull(MLoadUnboxedObjectOrNull *def, MDefinitionVector *peliminateList)
{
if (def->type() != MIRType_Value)
return true;
MDefinitionVector eliminateList(def->block()->graph().alloc());
for (MUseDefIterator iter(def); iter; ++iter) {
MDefinition *ndef = iter.def();
switch (ndef->op()) {
case MDefinition::Op_Compare:
if (ndef->toCompare()->compareType() != MCompare::Compare_Null)
return true;
break;
case MDefinition::Op_PostWriteBarrier:
break;
case MDefinition::Op_StoreFixedSlot:
break;
case MDefinition::Op_StoreSlot:
break;
case MDefinition::Op_ToObjectOrNull:
if (!eliminateList.append(ndef->toToObjectOrNull()))
return false;
break;
case MDefinition::Op_Unbox:
MOZ_ASSERT(ndef->type() == MIRType_Object);
break;
default:
return true;
}
}
def->setResultType(MIRType_ObjectOrNull);
for (size_t i = 0; i < eliminateList.length(); i++) {
MDefinition *ndef = eliminateList[i];
ndef->replaceAllUsesWith(def);
if (!peliminateList->append(ndef))
return false;
}
return true;
}
static inline MDefinition *
PassthroughOperand(MDefinition *def)
{
@ -2565,6 +2610,8 @@ jit::EliminateRedundantChecks(MIRGraph &graph)
}
}
MDefinitionVector eliminateList(graph.alloc());
// Starting from each self-dominating block, traverse the CFG in pre-order.
while (!worklist.empty()) {
MBasicBlock *block = worklist.popCopy();
@ -2586,6 +2633,9 @@ jit::EliminateRedundantChecks(MIRGraph &graph)
} else if (def->isTypeBarrier()) {
if (!TryEliminateTypeBarrier(def->toTypeBarrier(), &eliminated))
return false;
} else if (def->isLoadUnboxedObjectOrNull()) {
if (!TryOptimizeLoadUnboxedObjectOrNull(def->toLoadUnboxedObjectOrNull(), &eliminateList))
return false;
} else {
// Now that code motion passes have finished, replace
// instructions which pass through one of their operands
@ -2601,6 +2651,12 @@ jit::EliminateRedundantChecks(MIRGraph &graph)
}
MOZ_ASSERT(index == graph.numBlocks());
for (size_t i = 0; i < eliminateList.length(); i++) {
MDefinition *def = eliminateList[i];
def->block()->discardDef(def);
}
return true;
}

View File

@ -1728,7 +1728,9 @@ SnapshotIterator::fromStack(int32_t offset) const
static Value
FromObjectPayload(uintptr_t payload)
{
return ObjectValue(*reinterpret_cast<JSObject *>(payload));
// Note: Both MIRType_Object and MIRType_ObjectOrNull are encoded in
// snapshots using JSVAL_TYPE_OBJECT.
return ObjectOrNullValue(reinterpret_cast<JSObject *>(payload));
}
static Value

View File

@ -2498,12 +2498,15 @@ class LBitAndAndBranch : public LControlInstructionHelper<2, 2, 0>
}
};
class LIsNullOrLikeUndefined : public LInstructionHelper<1, BOX_PIECES, 2>
// Takes a value and tests whether it is null, undefined, or is an object that
// emulates |undefined|, as determined by the JSCLASS_EMULATES_UNDEFINED class
// flag on unwrapped objects. See also js::EmulatesUndefined.
class LIsNullOrLikeUndefinedV : public LInstructionHelper<1, BOX_PIECES, 2>
{
public:
LIR_HEADER(IsNullOrLikeUndefined)
LIR_HEADER(IsNullOrLikeUndefinedV)
LIsNullOrLikeUndefined(const LDefinition &temp, const LDefinition &tempToUnbox)
LIsNullOrLikeUndefinedV(const LDefinition &temp, const LDefinition &tempToUnbox)
{
setTemp(0, temp);
setTemp(1, tempToUnbox);
@ -2524,15 +2527,32 @@ class LIsNullOrLikeUndefined : public LInstructionHelper<1, BOX_PIECES, 2>
}
};
class LIsNullOrLikeUndefinedAndBranch : public LControlInstructionHelper<2, BOX_PIECES, 2>
// Takes an object or object-or-null pointer and tests whether it is null or is
// an object that emulates |undefined|, as above.
class LIsNullOrLikeUndefinedT : public LInstructionHelper<1, 1, 0>
{
public:
LIR_HEADER(IsNullOrLikeUndefinedT)
explicit LIsNullOrLikeUndefinedT(const LAllocation &input)
{
setOperand(0, input);
}
MCompare *mir() {
return mir_->toCompare();
}
};
class LIsNullOrLikeUndefinedAndBranchV : public LControlInstructionHelper<2, BOX_PIECES, 2>
{
MCompare *cmpMir_;
public:
LIR_HEADER(IsNullOrLikeUndefinedAndBranch)
LIR_HEADER(IsNullOrLikeUndefinedAndBranchV)
LIsNullOrLikeUndefinedAndBranch(MCompare *cmpMir, MBasicBlock *ifTrue, MBasicBlock *ifFalse,
const LDefinition &temp, const LDefinition &tempToUnbox)
LIsNullOrLikeUndefinedAndBranchV(MCompare *cmpMir, MBasicBlock *ifTrue, MBasicBlock *ifFalse,
const LDefinition &temp, const LDefinition &tempToUnbox)
: cmpMir_(cmpMir)
{
setSuccessor(0, ifTrue);
@ -2563,34 +2583,16 @@ class LIsNullOrLikeUndefinedAndBranch : public LControlInstructionHelper<2, BOX_
}
};
// Takes an object and tests whether it emulates |undefined|, as determined by
// the JSCLASS_EMULATES_UNDEFINED class flag on unwrapped objects. See also
// js::EmulatesUndefined.
class LEmulatesUndefined : public LInstructionHelper<1, 1, 0>
{
public:
LIR_HEADER(EmulatesUndefined)
explicit LEmulatesUndefined(const LAllocation &input)
{
setOperand(0, input);
}
MCompare *mir() {
return mir_->toCompare();
}
};
class LEmulatesUndefinedAndBranch : public LControlInstructionHelper<2, 1, 1>
class LIsNullOrLikeUndefinedAndBranchT : public LControlInstructionHelper<2, 1, 1>
{
MCompare *cmpMir_;
public:
LIR_HEADER(EmulatesUndefinedAndBranch)
LIR_HEADER(IsNullOrLikeUndefinedAndBranchT)
LEmulatesUndefinedAndBranch(MCompare *cmpMir, const LAllocation &input,
MBasicBlock *ifTrue, MBasicBlock *ifFalse,
const LDefinition &temp)
LIsNullOrLikeUndefinedAndBranchT(MCompare *cmpMir, const LAllocation &input,
MBasicBlock *ifTrue, MBasicBlock *ifFalse,
const LDefinition &temp)
: cmpMir_(cmpMir)
{
setOperand(0, input);
@ -4484,6 +4486,24 @@ class LLoadUnboxedPointerT : public LInstructionHelper<1, 2, 0>
}
};
class LUnboxObjectOrNull : public LInstructionHelper<1, 1, 0>
{
public:
LIR_HEADER(UnboxObjectOrNull);
explicit LUnboxObjectOrNull(const LAllocation &input)
{
setOperand(0, input);
}
MUnbox *mir() const {
return mir_->toUnbox();
}
const LAllocation *input() {
return getOperand(0);
}
};
// Store a boxed value to a dense array's element vector.
class LStoreElementV : public LInstructionHelper<0, 2 + BOX_PIECES, 0>
{

View File

@ -118,10 +118,10 @@
_(CompareVAndBranch) \
_(CompareVM) \
_(BitAndAndBranch) \
_(IsNullOrLikeUndefined) \
_(IsNullOrLikeUndefinedAndBranch)\
_(EmulatesUndefined) \
_(EmulatesUndefinedAndBranch) \
_(IsNullOrLikeUndefinedV) \
_(IsNullOrLikeUndefinedT) \
_(IsNullOrLikeUndefinedAndBranchV)\
_(IsNullOrLikeUndefinedAndBranchT)\
_(MinMaxI) \
_(MinMaxD) \
_(MinMaxF) \
@ -219,6 +219,7 @@
_(LoadElementHole) \
_(LoadUnboxedPointerV) \
_(LoadUnboxedPointerT) \
_(UnboxObjectOrNull) \
_(StoreElementV) \
_(StoreElementT) \
_(StoreUnboxedPointer) \

View File

@ -703,13 +703,16 @@ LIRGenerator::visitTest(MTest *test)
if (comp->compareType() == MCompare::Compare_Null ||
comp->compareType() == MCompare::Compare_Undefined)
{
if (left->type() == MIRType_Object) {
MOZ_ASSERT(comp->operandMightEmulateUndefined(),
if (left->type() == MIRType_Object || left->type() == MIRType_ObjectOrNull) {
MOZ_ASSERT(left->type() == MIRType_ObjectOrNull ||
comp->operandMightEmulateUndefined(),
"MCompare::tryFold should handle the never-emulates-undefined case");
LEmulatesUndefinedAndBranch *lir =
new(alloc()) LEmulatesUndefinedAndBranch(comp, useRegister(left),
ifTrue, ifFalse, temp());
LDefinition tmp =
comp->operandMightEmulateUndefined() ? temp() : LDefinition::BogusTemp();
LIsNullOrLikeUndefinedAndBranchT *lir =
new(alloc()) LIsNullOrLikeUndefinedAndBranchT(comp, useRegister(left),
ifTrue, ifFalse, tmp);
add(lir, test);
return;
}
@ -723,10 +726,10 @@ LIRGenerator::visitTest(MTest *test)
tmpToUnbox = LDefinition::BogusTemp();
}
LIsNullOrLikeUndefinedAndBranch *lir =
new(alloc()) LIsNullOrLikeUndefinedAndBranch(comp, ifTrue, ifFalse,
tmp, tmpToUnbox);
useBox(lir, LIsNullOrLikeUndefinedAndBranch::Value, left);
LIsNullOrLikeUndefinedAndBranchV *lir =
new(alloc()) LIsNullOrLikeUndefinedAndBranchV(comp, ifTrue, ifFalse,
tmp, tmpToUnbox);
useBox(lir, LIsNullOrLikeUndefinedAndBranchV::Value, left);
add(lir, test);
return;
}
@ -939,11 +942,12 @@ LIRGenerator::visitCompare(MCompare *comp)
if (comp->compareType() == MCompare::Compare_Null ||
comp->compareType() == MCompare::Compare_Undefined)
{
if (left->type() == MIRType_Object) {
MOZ_ASSERT(comp->operandMightEmulateUndefined(),
if (left->type() == MIRType_Object || left->type() == MIRType_ObjectOrNull) {
MOZ_ASSERT(left->type() == MIRType_ObjectOrNull ||
comp->operandMightEmulateUndefined(),
"MCompare::tryFold should have folded this away");
define(new(alloc()) LEmulatesUndefined(useRegister(left)), comp);
define(new(alloc()) LIsNullOrLikeUndefinedT(useRegister(left)), comp);
return;
}
@ -956,8 +960,8 @@ LIRGenerator::visitCompare(MCompare *comp)
tmpToUnbox = LDefinition::BogusTemp();
}
LIsNullOrLikeUndefined *lir = new(alloc()) LIsNullOrLikeUndefined(tmp, tmpToUnbox);
useBox(lir, LIsNullOrLikeUndefined::Value, left);
LIsNullOrLikeUndefinedV *lir = new(alloc()) LIsNullOrLikeUndefinedV(tmp, tmpToUnbox);
useBox(lir, LIsNullOrLikeUndefinedV::Value, left);
define(lir, comp);
return;
}
@ -2627,7 +2631,7 @@ LIRGenerator::visitLoadUnboxedObjectOrNull(MLoadUnboxedObjectOrNull *ins)
MOZ_ASSERT(IsValidElementsType(ins->elements(), ins->offsetAdjustment()));
MOZ_ASSERT(ins->index()->type() == MIRType_Int32);
if (ins->type() == MIRType_Object) {
if (ins->type() == MIRType_Object || ins->type() == MIRType_ObjectOrNull) {
LLoadUnboxedPointerT *lir = new(alloc()) LLoadUnboxedPointerT(useRegister(ins->elements()),
useRegisterOrConstant(ins->index()));
if (ins->nullBehavior() == MLoadUnboxedObjectOrNull::BailOnNull)

View File

@ -616,14 +616,18 @@ class MDefinition : public MNode
bool mightBeType(MIRType type) const {
MOZ_ASSERT(type != MIRType_Value);
MOZ_ASSERT(type != MIRType_ObjectOrNull);
if (type == this->type())
return true;
if (MIRType_Value != this->type())
return false;
if (this->type() == MIRType_ObjectOrNull)
return type == MIRType_Object || type == MIRType_Null;
return !resultTypeSet() || resultTypeSet()->mightBeMIRType(type);
if (this->type() == MIRType_Value)
return !resultTypeSet() || resultTypeSet()->mightBeMIRType(type);
return false;
}
bool mightBeMagicType() const;

View File

@ -424,6 +424,17 @@ class MacroAssembler : public MacroAssemblerSpecific
}
}
template <typename T>
void storeObjectOrNull(Register src, const T &dest) {
Label notNull, done;
branchTestPtr(Assembler::NonZero, src, src, &notNull);
storeValue(NullValue(), dest);
jump(&done);
bind(&notNull);
storeValue(JSVAL_TYPE_OBJECT, src, dest);
bind(&done);
}
template <typename T>
void storeConstantOrRegister(ConstantOrRegister src, const T &dest) {
if (src.constant())

View File

@ -102,6 +102,13 @@ class CodeGeneratorARM : public CodeGeneratorShared
cond = masm.testObject(cond, value);
emitBranch(cond, ifTrue, ifFalse);
}
void testZeroEmitBranch(Assembler::Condition cond, Register reg,
MBasicBlock *ifTrue, MBasicBlock *ifFalse)
{
MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
masm.cmpPtr(reg, ImmWord(0));
emitBranch(cond, ifTrue, ifFalse);
}
void emitTableSwitchDispatch(MTableSwitch *mir, Register index, Register base);

View File

@ -125,10 +125,19 @@ LIRGeneratorARM::visitBox(MBox *box)
void
LIRGeneratorARM::visitUnbox(MUnbox *unbox)
{
// An unbox on arm reads in a type tag (either in memory or a register) and
// a payload. Unlike most instructions conusming a box, we ask for the type
// second, so that the result can re-use the first input.
MDefinition *inner = unbox->getOperand(0);
if (inner->type() == MIRType_ObjectOrNull) {
LUnboxObjectOrNull *lir = new(alloc()) LUnboxObjectOrNull(useRegisterAtStart(inner));
if (unbox->fallible())
assignSnapshot(lir, unbox->bailoutKind());
defineReuseInput(lir, unbox, 0);
return;
}
// An unbox on arm reads in a type tag (either in memory or a register) and
// a payload. Unlike most instructions consuming a box, we ask for the type
// second, so that the result can re-use the first input.
MOZ_ASSERT(inner->type() == MIRType_Value);
ensureDefined(inner);

View File

@ -160,6 +160,11 @@ class CodeGeneratorMIPS : public CodeGeneratorShared
{
emitBranch(value.typeReg(), (Imm32)ImmType(JSVAL_TYPE_OBJECT), cond, ifTrue, ifFalse);
}
void testZeroEmitBranch(Assembler::Condition cond, Register reg,
MBasicBlock *ifTrue, MBasicBlock *ifFalse)
{
emitBranch(reg, Imm32(0), cond, ifTrue, ifFalse);
}
void emitTableSwitchDispatch(MTableSwitch *mir, Register index, Register base);

View File

@ -127,10 +127,19 @@ LIRGeneratorMIPS::visitBox(MBox *box)
void
LIRGeneratorMIPS::visitUnbox(MUnbox *unbox)
{
MDefinition *inner = unbox->getOperand(0);
if (inner->type() == MIRType_ObjectOrNull) {
LUnboxObjectOrNull *lir = new(alloc()) LUnboxObjectOrNull(useRegisterAtStart(inner));
if (unbox->fallible())
assignSnapshot(lir, unbox->bailoutKind());
defineReuseInput(lir, unbox, 0);
return;
}
// An unbox on mips reads in a type tag (either in memory or a register) and
// a payload. Unlike most instructions consuming a box, we ask for the type
// second, so that the result can re-use the first input.
MDefinition *inner = unbox->getOperand(0);
MOZ_ASSERT(inner->type() == MIRType_Value);
ensureDefined(inner);

View File

@ -48,6 +48,9 @@ class CodeGeneratorNone : public CodeGeneratorShared
void testObjectEmitBranch(Assembler::Condition, ValueOperand, MBasicBlock *, MBasicBlock *) {
MOZ_CRASH();
}
void testZeroEmitBranch(Assembler::Condition, Register, MBasicBlock *, MBasicBlock *) {
MOZ_CRASH();
}
void emitTableSwitchDispatch(MTableSwitch *, Register, Register) { MOZ_CRASH(); }
ValueOperand ToValue(LInstruction *, size_t) { MOZ_CRASH(); }
ValueOperand ToOutValue(LInstruction *) { MOZ_CRASH(); }

View File

@ -378,12 +378,14 @@ CodeGeneratorShared::encodeAllocation(LSnapshot *snapshot, MDefinition *mir,
case MIRType_String:
case MIRType_Symbol:
case MIRType_Object:
case MIRType_ObjectOrNull:
case MIRType_Boolean:
case MIRType_Double:
case MIRType_Float32:
{
LAllocation *payload = snapshot->payloadOfSlot(*allocIndex);
JSValueType valueType = ValueTypeFromMIRType(type);
JSValueType valueType =
(type == MIRType_ObjectOrNull) ? JSVAL_TYPE_OBJECT : ValueTypeFromMIRType(type);
if (payload->isMemory()) {
if (type == MIRType_Float32)
alloc = RValueAllocation::Float32(ToStackIndex(payload));

View File

@ -147,6 +147,14 @@ class CodeGeneratorX86Shared : public CodeGeneratorShared
emitBranch(cond, ifTrue, ifFalse);
}
void testZeroEmitBranch(Assembler::Condition cond, Register reg,
MBasicBlock *ifTrue, MBasicBlock *ifFalse)
{
MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
masm.cmpPtr(reg, ImmWord(0));
emitBranch(cond, ifTrue, ifFalse);
}
void emitTableSwitchDispatch(MTableSwitch *mir, Register index, Register base);
public:

View File

@ -80,6 +80,15 @@ void
LIRGeneratorX64::visitUnbox(MUnbox *unbox)
{
MDefinition *box = unbox->getOperand(0);
if (box->type() == MIRType_ObjectOrNull) {
LUnboxObjectOrNull *lir = new(alloc()) LUnboxObjectOrNull(useRegisterAtStart(box));
if (unbox->fallible())
assignSnapshot(lir, unbox->bailoutKind());
defineReuseInput(lir, unbox, 0);
return;
}
MOZ_ASSERT(box->type() == MIRType_Value);
LUnboxBase *lir;

View File

@ -98,10 +98,19 @@ LIRGeneratorX86::visitBox(MBox *box)
void
LIRGeneratorX86::visitUnbox(MUnbox *unbox)
{
// An unbox on x86 reads in a type tag (either in memory or a register) and
// a payload. Unlike most instructions conusming a box, we ask for the type
// second, so that the result can re-use the first input.
MDefinition *inner = unbox->getOperand(0);
if (inner->type() == MIRType_ObjectOrNull) {
LUnboxObjectOrNull *lir = new(alloc()) LUnboxObjectOrNull(useRegisterAtStart(inner));
if (unbox->fallible())
assignSnapshot(lir, unbox->bailoutKind());
defineReuseInput(lir, unbox, 0);
return;
}
// An unbox on x86 reads in a type tag (either in memory or a register) and
// a payload. Unlike most instructions consuming a box, we ask for the type
// second, so that the result can re-use the first input.
MOZ_ASSERT(inner->type() == MIRType_Value);
ensureDefined(inner);