Implement type recording for ToBoolean on ARM.

Review URL: http://codereview.chromium.org/7491054

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@8859 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent d941053d
......@@ -1603,83 +1603,139 @@ void CompareStub::Generate(MacroAssembler* masm) {
}
// The stub returns zero for false, and a non-zero value for true.
// The stub expects its argument in the tos_ register and returns its result in
// it, too: zero for false, and a non-zero value for true.
void ToBooleanStub::Generate(MacroAssembler* masm) {
// This stub uses VFP3 instructions.
CpuFeatures::Scope scope(VFP3);
Label false_result, true_result, not_string;
Label patch;
const Register map = r9.is(tos_) ? r7 : r9;
// undefined -> false
__ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
__ cmp(tos_, ip);
__ b(eq, &false_result);
// Boolean -> its value
__ LoadRoot(ip, Heap::kFalseValueRootIndex);
__ cmp(tos_, ip);
__ b(eq, &false_result);
__ LoadRoot(ip, Heap::kTrueValueRootIndex);
__ cmp(tos_, ip);
// "tos_" is a register and contains a non-zero value. Hence we implicitly
// return true if the equal condition is satisfied.
__ Ret(eq);
// undefined -> false.
CheckOddball(masm, UNDEFINED, Heap::kUndefinedValueRootIndex, false, &patch);
// Smis: 0 -> false, all other -> true
__ tst(tos_, tos_);
__ b(eq, &false_result);
__ tst(tos_, Operand(kSmiTagMask));
// "tos_" is a register and contains a non-zero value. Hence we implicitly
// return true if the not equal condition is satisfied.
__ Ret(eq);
// Boolean -> its value.
CheckOddball(masm, BOOLEAN, Heap::kFalseValueRootIndex, false, &patch);
CheckOddball(masm, BOOLEAN, Heap::kTrueValueRootIndex, true, &patch);
// 'null' -> false
__ LoadRoot(ip, Heap::kNullValueRootIndex);
__ cmp(tos_, ip);
__ b(eq, &false_result);
// 'null' -> false.
CheckOddball(masm, NULL_TYPE, Heap::kNullValueRootIndex, false, &patch);
// Get the map of the heap object.
__ ldr(map, FieldMemOperand(tos_, HeapObject::kMapOffset));
if (types_.Contains(SMI)) {
// Smis: 0 -> false, all other -> true
__ tst(tos_, Operand(kSmiTagMask));
// tos_ contains the correct return value already
__ Ret(eq);
} else if (types_.NeedsMap()) {
// If we need a map later and have a Smi -> patch.
__ JumpIfSmi(tos_, &patch);
}
// Undetectable -> false.
__ ldrb(ip, FieldMemOperand(map, Map::kBitFieldOffset));
__ tst(ip, Operand(1 << Map::kIsUndetectable));
__ b(&false_result, ne);
if (types_.NeedsMap()) {
__ ldr(map, FieldMemOperand(tos_, HeapObject::kMapOffset));
// JavaScript object -> true.
__ CompareInstanceType(map, ip, FIRST_SPEC_OBJECT_TYPE);
// "tos_" is a register and contains a non-zero value. Hence we implicitly
// return true if the greater than condition is satisfied.
__ Ret(ge);
// Everything with a map could be undetectable, so check this now.
__ ldrb(ip, FieldMemOperand(map, Map::kBitFieldOffset));
__ tst(ip, Operand(1 << Map::kIsUndetectable));
// Undetectable -> false.
__ mov(tos_, Operand(0, RelocInfo::NONE), LeaveCC, ne);
__ Ret(ne);
}
if (types_.Contains(SPEC_OBJECT)) {
// Spec object -> true.
__ CompareInstanceType(map, ip, FIRST_SPEC_OBJECT_TYPE);
// tos_ contains the correct non-zero return value already.
__ Ret(ge);
} else if (types_.Contains(INTERNAL_OBJECT)) {
// We've seen a spec object for the first time -> patch.
__ CompareInstanceType(map, ip, FIRST_SPEC_OBJECT_TYPE);
__ b(ge, &patch);
}
// String value -> false iff empty.
if (types_.Contains(STRING)) {
// String value -> false iff empty.
__ CompareInstanceType(map, ip, FIRST_NONSTRING_TYPE);
__ b(&not_string, ge);
__ ldr(tos_, FieldMemOperand(tos_, String::kLengthOffset));
// Return string length as boolean value, i.e. return false iff length is 0.
__ Ret();
__ ldr(tos_, FieldMemOperand(tos_, String::kLengthOffset), lt);
__ Ret(lt); // the string length is OK as the return value
} else if (types_.Contains(INTERNAL_OBJECT)) {
// We've seen a string for the first time -> patch
__ CompareInstanceType(map, ip, FIRST_NONSTRING_TYPE);
__ b(lt, &patch);
}
__ bind(&not_string);
// HeapNumber -> false iff +0, -0, or NaN.
__ CompareRoot(map, Heap::kHeapNumberMapRootIndex);
__ b(&true_result, ne);
__ vldr(d1, FieldMemOperand(tos_, HeapNumber::kValueOffset));
__ VFPCompareAndSetFlags(d1, 0.0);
// "tos_" is a register, and contains a non zero value by default.
// Hence we only need to overwrite "tos_" with zero to return false for
// FP_ZERO or FP_NAN cases. Otherwise, by default it returns true.
__ mov(tos_, Operand(0, RelocInfo::NONE), LeaveCC, eq); // for FP_ZERO
__ mov(tos_, Operand(0, RelocInfo::NONE), LeaveCC, vs); // for FP_NAN
__ Ret();
if (types_.Contains(HEAP_NUMBER)) {
// Heap number -> false iff +0, -0, or NaN.
Label not_heap_number;
__ CompareRoot(map, Heap::kHeapNumberMapRootIndex);
__ b(ne, &not_heap_number);
__ vldr(d1, FieldMemOperand(tos_, HeapNumber::kValueOffset));
__ VFPCompareAndSetFlags(d1, 0.0);
// "tos_" is a register, and contains a non zero value by default.
// Hence we only need to overwrite "tos_" with zero to return false for
// FP_ZERO or FP_NAN cases. Otherwise, by default it returns true.
__ mov(tos_, Operand(0, RelocInfo::NONE), LeaveCC, eq); // for FP_ZERO
__ mov(tos_, Operand(0, RelocInfo::NONE), LeaveCC, vs); // for FP_NAN
__ Ret();
__ bind(&not_heap_number);
} else if (types_.Contains(INTERNAL_OBJECT)) {
// We've seen a heap number for the first time -> patch
__ CompareRoot(map, Heap::kHeapNumberMapRootIndex);
__ b(eq, &patch);
}
// Return 1/0 for true/false in tos_.
__ bind(&true_result);
__ mov(tos_, Operand(1, RelocInfo::NONE));
__ Ret();
__ bind(&false_result);
__ mov(tos_, Operand(0, RelocInfo::NONE));
__ Ret();
if (types_.Contains(INTERNAL_OBJECT)) {
// Internal objects -> true.
__ mov(tos_, Operand(1, RelocInfo::NONE));
__ Ret();
}
if (!types_.IsAll()) {
__ bind(&patch);
GenerateTypeTransition(masm);
}
}
void ToBooleanStub::CheckOddball(MacroAssembler* masm,
Type type,
Heap::RootListIndex value,
bool result,
Label* patch) {
if (types_.Contains(type)) {
// If we see an expected oddball, return its ToBoolean value tos_.
__ LoadRoot(ip, value);
__ cmp(tos_, ip);
// The value of a root is never NULL, so we can avoid loading a non-null
// value into tos_ when we want to return 'true'.
if (!result) {
__ mov(tos_, Operand(0, RelocInfo::NONE), LeaveCC, eq);
}
__ Ret(eq);
} else if (types_.Contains(INTERNAL_OBJECT)) {
// If we see an unexpected oddball and handle internal objects, we must
// patch because the code for internal objects doesn't handle it explictly.
__ LoadRoot(ip, value);
__ cmp(tos_, ip);
__ b(eq, patch);
}
}
void ToBooleanStub::GenerateTypeTransition(MacroAssembler* masm) {
if (!tos_.is(r3)) {
__ mov(r3, Operand(tos_));
}
__ mov(r2, Operand(Smi::FromInt(tos_.code())));
__ mov(r1, Operand(Smi::FromInt(types_.ToByte())));
__ Push(r3, r2, r1);
// Patch the caller to an appropriate specialized stub and return the
// operation result to the caller of the stub.
__ TailCallExternalReference(
ExternalReference(IC_Utility(IC::kToBoolean_Patch), masm->isolate()),
3,
1);
}
......
......@@ -1039,7 +1039,13 @@ LInstruction* LChunkBuilder::DoBranch(HBranch* instr) {
: instr->SecondSuccessor();
return new LGoto(successor->block_id());
}
return new LBranch(UseRegisterAtStart(v));
LInstruction* branch = new LBranch(UseRegister(v));
// When we handle all cases, we never deopt, so we don't need to assign the
// environment then. Note that we map the "empty" case to the "all" case in
// the code generator.
ToBooleanStub::Types types = instr->expected_input_types();
bool all_cases_handled = types.IsAll() || types.IsEmpty();
return all_cases_handled ? branch : AssignEnvironment(branch);
}
......
......@@ -1564,52 +1564,138 @@ void LCodeGen::DoBranch(LBranch* instr) {
} else {
ASSERT(r.IsTagged());
Register reg = ToRegister(instr->InputAt(0));
if (instr->hydrogen()->value()->type().IsBoolean()) {
__ LoadRoot(ip, Heap::kTrueValueRootIndex);
__ cmp(reg, ip);
HType type = instr->hydrogen()->value()->type();
if (type.IsBoolean()) {
__ CompareRoot(reg, Heap::kTrueValueRootIndex);
EmitBranch(true_block, false_block, eq);
} else if (type.IsSmi()) {
__ cmp(reg, Operand(0));
EmitBranch(true_block, false_block, ne);
} else {
Label* true_label = chunk_->GetAssemblyLabel(true_block);
Label* false_label = chunk_->GetAssemblyLabel(false_block);
__ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
__ cmp(reg, ip);
__ b(eq, false_label);
__ LoadRoot(ip, Heap::kTrueValueRootIndex);
__ cmp(reg, ip);
__ b(eq, true_label);
__ LoadRoot(ip, Heap::kFalseValueRootIndex);
__ cmp(reg, ip);
__ b(eq, false_label);
__ cmp(reg, Operand(0));
__ b(eq, false_label);
__ JumpIfSmi(reg, true_label);
// Test double values. Zero and NaN are false.
Label call_stub;
DoubleRegister dbl_scratch = double_scratch0();
Register scratch = scratch0();
__ ldr(scratch, FieldMemOperand(reg, HeapObject::kMapOffset));
__ LoadRoot(ip, Heap::kHeapNumberMapRootIndex);
__ cmp(scratch, Operand(ip));
__ b(ne, &call_stub);
__ sub(ip, reg, Operand(kHeapObjectTag));
__ vldr(dbl_scratch, ip, HeapNumber::kValueOffset);
__ VFPCompareAndLoadFlags(dbl_scratch, 0.0, scratch);
__ tst(scratch, Operand(kVFPZConditionFlagBit | kVFPVConditionFlagBit));
__ b(ne, false_label);
__ b(true_label);
// The conversion stub doesn't cause garbage collections so it's
// safe to not record a safepoint after the call.
__ bind(&call_stub);
ToBooleanStub stub(reg);
RegList saved_regs = kJSCallerSaved | kCalleeSaved;
__ stm(db_w, sp, saved_regs);
__ CallStub(&stub);
__ cmp(reg, Operand(0));
__ ldm(ia_w, sp, saved_regs);
EmitBranch(true_block, false_block, ne);
ToBooleanStub::Types expected = instr->hydrogen()->expected_input_types();
// Avoid deopts in the case where we've never executed this path before.
if (expected.IsEmpty()) expected = ToBooleanStub::all_types();
if (expected.Contains(ToBooleanStub::UNDEFINED)) {
// undefined -> false.
__ CompareRoot(reg, Heap::kUndefinedValueRootIndex);
__ b(eq, false_label);
} else if (expected.Contains(ToBooleanStub::INTERNAL_OBJECT)) {
// We've seen undefined for the first time -> deopt.
__ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
DeoptimizeIf(eq, instr->environment());
}
if (expected.Contains(ToBooleanStub::BOOLEAN)) {
// Boolean -> its value.
__ CompareRoot(reg, Heap::kTrueValueRootIndex);
__ b(eq, true_label);
__ CompareRoot(reg, Heap::kFalseValueRootIndex);
__ b(eq, false_label);
} else if (expected.Contains(ToBooleanStub::INTERNAL_OBJECT)) {
// We've seen a boolean for the first time -> deopt.
__ CompareRoot(reg, Heap::kTrueValueRootIndex);
DeoptimizeIf(eq, instr->environment());
__ CompareRoot(reg, Heap::kFalseValueRootIndex);
DeoptimizeIf(eq, instr->environment());
}
#if 0
if (expected.Contains(ToBooleanStub::BOOLEAN)) {
// false -> false.
__ CompareRoot(reg, Heap::kFalseValueRootIndex);
__ b(eq, false_label);
} else if (expected.Contains(ToBooleanStub::INTERNAL_OBJECT)) {
// We've seen a boolean for the first time -> deopt.
__ CompareRoot(reg, Heap::kFalseValueRootIndex);
DeoptimizeIf(eq, instr->environment());
}
#endif
if (expected.Contains(ToBooleanStub::NULL_TYPE)) {
// 'null' -> false.
__ CompareRoot(reg, Heap::kNullValueRootIndex);
__ b(eq, false_label);
} else if (expected.Contains(ToBooleanStub::INTERNAL_OBJECT)) {
// We've seen null for the first time -> deopt.
__ CompareRoot(reg, Heap::kNullValueRootIndex);
DeoptimizeIf(eq, instr->environment());
}
if (expected.Contains(ToBooleanStub::SMI)) {
// Smis: 0 -> false, all other -> true.
__ cmp(reg, Operand(0));
__ b(eq, false_label);
__ JumpIfSmi(reg, true_label);
} else if (expected.NeedsMap()) {
// If we need a map later and have a Smi -> deopt.
__ tst(reg, Operand(kSmiTagMask));
DeoptimizeIf(eq, instr->environment());
}
const Register map = scratch0();
if (expected.NeedsMap()) {
__ ldr(map, FieldMemOperand(reg, HeapObject::kMapOffset));
// Everything with a map could be undetectable, so check this now.
__ ldrb(ip, FieldMemOperand(map, Map::kBitFieldOffset));
__ tst(ip, Operand(1 << Map::kIsUndetectable));
__ b(ne, false_label);
}
if (expected.Contains(ToBooleanStub::SPEC_OBJECT)) {
// spec object -> true.
__ CompareInstanceType(map, ip, FIRST_SPEC_OBJECT_TYPE);
__ b(ge, true_label);
} else if (expected.Contains(ToBooleanStub::INTERNAL_OBJECT)) {
// We've seen a spec object for the first time -> deopt.
__ CompareInstanceType(map, ip, FIRST_SPEC_OBJECT_TYPE);
DeoptimizeIf(ge, instr->environment());
}
if (expected.Contains(ToBooleanStub::STRING)) {
// String value -> false iff empty.
Label not_string;
__ CompareInstanceType(map, ip, FIRST_NONSTRING_TYPE);
__ b(ge, &not_string);
__ ldr(ip, FieldMemOperand(reg, String::kLengthOffset));
__ cmp(ip, Operand(0));
__ b(ne, true_label);
__ b(false_label);
__ bind(&not_string);
} else if (expected.Contains(ToBooleanStub::INTERNAL_OBJECT)) {
// We've seen a string for the first time -> deopt
__ CompareInstanceType(map, ip, FIRST_NONSTRING_TYPE);
DeoptimizeIf(lt, instr->environment());
}
if (expected.Contains(ToBooleanStub::HEAP_NUMBER)) {
// heap number -> false iff +0, -0, or NaN.
DoubleRegister dbl_scratch = double_scratch0();
Label not_heap_number;
__ CompareRoot(map, Heap::kHeapNumberMapRootIndex);
__ b(ne, &not_heap_number);
__ vldr(dbl_scratch, FieldMemOperand(reg, HeapNumber::kValueOffset));
__ VFPCompareAndSetFlags(dbl_scratch, 0.0);
__ b(vs, false_label); // NaN -> false.
__ b(eq, false_label); // +0, -0 -> false.
__ b(true_label);
__ bind(&not_heap_number);
} else if (expected.Contains(ToBooleanStub::INTERNAL_OBJECT)) {
// We've seen a heap number for the first time -> deopt.
__ CompareRoot(map, Heap::kHeapNumberMapRootIndex);
DeoptimizeIf(eq, instr->environment());
}
if (expected.Contains(ToBooleanStub::INTERNAL_OBJECT)) {
// internal objects -> true
__ b(true_label);
} else {
// We've seen something for the first time -> deopt.
DeoptimizeIf(al, instr->environment());
}
}
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment