Bug 1004198. Improve codegen in testValueTruthyKernel to emit as few tests as we can get away with given our type inference information. r=jandem

This commit is contained in:
Boris Zbarsky 2014-05-03 01:08:13 -04:00
Родитель 13ecba6a77
Коммит 2be1743b63
2 изменённых файлов: 125 добавлений и 47 удалений

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

@ -508,52 +508,122 @@ CodeGenerator::testValueTruthyKernel(const ValueOperand &value,
const LDefinition *scratch1, const LDefinition *scratch2,
FloatRegister fr,
Label *ifTruthy, Label *ifFalsy,
OutOfLineTestObject *ool)
OutOfLineTestObject *ool,
MDefinition *valueMIR)
{
Register tag = masm.splitTagForTest(value);
// Count the number of possible type tags we might have, so we'll know when
// we've checked them all and hence can avoid emitting a tag check for the
// last one. In particular, whenever tagCount is 1 that means we've tried
// all but one of them already so we know exactly what's left based on the
// mightBe* booleans.
bool mightBeUndefined = valueMIR->mightBeType(MIRType_Undefined);
bool mightBeNull = valueMIR->mightBeType(MIRType_Null);
bool mightBeBoolean = valueMIR->mightBeType(MIRType_Boolean);
bool mightBeInt32 = valueMIR->mightBeType(MIRType_Int32);
bool mightBeObject = valueMIR->mightBeType(MIRType_Object);
bool mightBeString = valueMIR->mightBeType(MIRType_String);
bool mightBeDouble = valueMIR->mightBeType(MIRType_Double);
int tagCount = int(mightBeUndefined) + int(mightBeNull) +
int(mightBeBoolean) + int(mightBeInt32) + int(mightBeObject) +
int(mightBeString) + int(mightBeDouble);
// Eventually we will want some sort of type filter here. For now, just
// emit all easy cases. For speed we use the cached tag for all comparison,
// except for doubles, which we test last (as the operation can clobber the
// tag, which may be in ScratchReg).
masm.branchTestUndefined(Assembler::Equal, tag, ifFalsy);
masm.branchTestNull(Assembler::Equal, tag, ifFalsy);
MOZ_ASSERT_IF(!valueMIR->emptyResultTypeSet(), tagCount > 0);
Label notBoolean;
masm.branchTestBoolean(Assembler::NotEqual, tag, &notBoolean);
masm.branchTestBooleanTruthy(false, value, ifFalsy);
masm.jump(ifTruthy);
masm.bind(&notBoolean);
Label notInt32;
masm.branchTestInt32(Assembler::NotEqual, tag, &notInt32);
masm.branchTestInt32Truthy(false, value, ifFalsy);
masm.jump(ifTruthy);
masm.bind(&notInt32);
if (ool) {
Label notObject;
masm.branchTestObject(Assembler::NotEqual, tag, &notObject);
Register objreg = masm.extractObject(value, ToRegister(scratch1));
testObjectEmulatesUndefined(objreg, ifFalsy, ifTruthy, ToRegister(scratch2), ool);
masm.bind(&notObject);
} else {
masm.branchTestObject(Assembler::Equal, tag, ifTruthy);
// If we know we're null or undefined, we're definitely falsy, no
// need to even check the tag.
if (int(mightBeNull) + int(mightBeUndefined) == tagCount) {
masm.jump(ifFalsy);
return;
}
// Test if a string is non-empty.
Label notString;
masm.branchTestString(Assembler::NotEqual, tag, &notString);
masm.branchTestStringTruthy(false, value, ifFalsy);
masm.jump(ifTruthy);
masm.bind(&notString);
Register tag = masm.splitTagForTest(value);
// If we reach here the value is a double.
masm.unboxDouble(value, fr);
masm.branchTestDoubleTruthy(false, fr, ifFalsy);
if (mightBeUndefined) {
MOZ_ASSERT(tagCount > 1);
masm.branchTestUndefined(Assembler::Equal, tag, ifFalsy);
--tagCount;
}
if (mightBeNull) {
MOZ_ASSERT(tagCount > 1);
masm.branchTestNull(Assembler::Equal, tag, ifFalsy);
--tagCount;
}
if (mightBeBoolean) {
MOZ_ASSERT(tagCount != 0);
Label notBoolean;
if (tagCount != 1)
masm.branchTestBoolean(Assembler::NotEqual, tag, &notBoolean);
masm.branchTestBooleanTruthy(false, value, ifFalsy);
if (tagCount != 1)
masm.jump(ifTruthy);
// Else just fall through to truthiness.
masm.bind(&notBoolean);
--tagCount;
}
if (mightBeInt32) {
MOZ_ASSERT(tagCount != 0);
Label notInt32;
if (tagCount != 1)
masm.branchTestInt32(Assembler::NotEqual, tag, &notInt32);
masm.branchTestInt32Truthy(false, value, ifFalsy);
if (tagCount != 1)
masm.jump(ifTruthy);
// Else just fall through to truthiness.
masm.bind(&notInt32);
--tagCount;
}
if (mightBeObject) {
MOZ_ASSERT(tagCount != 0);
if (ool) {
Label notObject;
if (tagCount != 1)
masm.branchTestObject(Assembler::NotEqual, tag, &notObject);
Register objreg = masm.extractObject(value, ToRegister(scratch1));
testObjectEmulatesUndefined(objreg, ifFalsy, ifTruthy, ToRegister(scratch2), ool);
masm.bind(&notObject);
} else {
if (tagCount != 1)
masm.branchTestObject(Assembler::Equal, tag, ifTruthy);
// Else just fall through to truthiness.
}
--tagCount;
} else {
MOZ_ASSERT(!ool,
"We better not have an unused OOL path, since the code generator will try to "
"generate code for it but we never set up its labels, which will cause null "
"derefs of those labels.");
}
if (mightBeString) {
// Test if a string is non-empty.
MOZ_ASSERT(tagCount != 0);
Label notString;
if (tagCount != 1)
masm.branchTestString(Assembler::NotEqual, tag, &notString);
masm.branchTestStringTruthy(false, value, ifFalsy);
if (tagCount != 1)
masm.jump(ifTruthy);
// Else just fall through to truthiness.
masm.bind(&notString);
--tagCount;
}
if (mightBeDouble) {
MOZ_ASSERT(tagCount == 1);
// If we reach here the value is a double.
masm.unboxDouble(value, fr);
masm.branchTestDoubleTruthy(false, fr, ifFalsy);
--tagCount;
}
MOZ_ASSERT(tagCount == 0);
// Fall through for truthy.
}
@ -563,9 +633,10 @@ CodeGenerator::testValueTruthy(const ValueOperand &value,
const LDefinition *scratch1, const LDefinition *scratch2,
FloatRegister fr,
Label *ifTruthy, Label *ifFalsy,
OutOfLineTestObject *ool)
OutOfLineTestObject *ool,
MDefinition *valueMIR)
{
testValueTruthyKernel(value, scratch1, scratch2, fr, ifTruthy, ifFalsy, ool);
testValueTruthyKernel(value, scratch1, scratch2, fr, ifTruthy, ifFalsy, ool, valueMIR);
masm.jump(ifTruthy);
}
@ -612,7 +683,11 @@ bool
CodeGenerator::visitTestVAndBranch(LTestVAndBranch *lir)
{
OutOfLineTestObject *ool = nullptr;
if (lir->mir()->operandMightEmulateUndefined()) {
// XXXbz operandMightEmulateUndefined lies a lot. See bug 1004169. In
// practice, we don't need the OutOfLineTestObject if the input to our test
// is not an object.
MDefinition* input = lir->mir()->input();
if (lir->mir()->operandMightEmulateUndefined() && input->mightBeType(MIRType_Object)) {
ool = new(alloc()) OutOfLineTestObject();
if (!addOutOfLineCode(ool))
return false;
@ -624,7 +699,7 @@ CodeGenerator::visitTestVAndBranch(LTestVAndBranch *lir)
testValueTruthy(ToValue(lir, LTestVAndBranch::Input),
lir->temp1(), lir->temp2(),
ToFloatRegister(lir->tempFloat()),
truthy, falsy, ool);
truthy, falsy, ool, input);
return true;
}
@ -5291,6 +5366,7 @@ CodeGenerator::visitNotV(LNotV *lir)
OutOfLineTestObjectWithLabels *ool = nullptr;
if (lir->mir()->operandMightEmulateUndefined()) {
MOZ_ASSERT(lir->mir()->operand()->mightBeType(MIRType_Object));
ool = new(alloc()) OutOfLineTestObjectWithLabels();
if (!addOutOfLineCode(ool))
return false;
@ -5305,7 +5381,7 @@ CodeGenerator::visitNotV(LNotV *lir)
testValueTruthyKernel(ToValue(lir, LNotV::Input), lir->temp1(), lir->temp2(),
ToFloatRegister(lir->tempFloat()),
ifTruthy, ifFalsy, ool);
ifTruthy, ifFalsy, ool, lir->mir()->operand());
Label join;
Register output = ToRegister(lir->output());

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

@ -389,7 +389,8 @@ class CodeGenerator : public CodeGeneratorSpecific
const LDefinition *scratch1, const LDefinition *scratch2,
FloatRegister fr,
Label *ifTruthy, Label *ifFalsy,
OutOfLineTestObject *ool);
OutOfLineTestObject *ool,
MDefinition *valueMIR);
// Test whether value is truthy or not and jump to the corresponding label.
// If the value can be an object that emulates |undefined|, |ool| must be
@ -400,7 +401,8 @@ class CodeGenerator : public CodeGeneratorSpecific
const LDefinition *scratch1, const LDefinition *scratch2,
FloatRegister fr,
Label *ifTruthy, Label *ifFalsy,
OutOfLineTestObject *ool);
OutOfLineTestObject *ool,
MDefinition *valueMIR);
// This function behaves like testObjectEmulatesUndefined with the exception
// that it can choose to let control flow fall through when the object