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) { ...@@ -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) { void ToBooleanStub::Generate(MacroAssembler* masm) {
// This stub uses VFP3 instructions. // This stub uses VFP3 instructions.
CpuFeatures::Scope scope(VFP3); CpuFeatures::Scope scope(VFP3);
Label false_result, true_result, not_string; Label patch;
const Register map = r9.is(tos_) ? r7 : r9; const Register map = r9.is(tos_) ? r7 : r9;
// undefined -> false // undefined -> false.
__ LoadRoot(ip, Heap::kUndefinedValueRootIndex); CheckOddball(masm, UNDEFINED, Heap::kUndefinedValueRootIndex, false, &patch);
__ 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);
// Smis: 0 -> false, all other -> true // Boolean -> its value.
__ tst(tos_, tos_); CheckOddball(masm, BOOLEAN, Heap::kFalseValueRootIndex, false, &patch);
__ b(eq, &false_result); CheckOddball(masm, BOOLEAN, Heap::kTrueValueRootIndex, true, &patch);
__ 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);
// 'null' -> false // 'null' -> false.
__ LoadRoot(ip, Heap::kNullValueRootIndex); CheckOddball(masm, NULL_TYPE, Heap::kNullValueRootIndex, false, &patch);
__ cmp(tos_, ip);
__ b(eq, &false_result);
// Get the map of the heap object. if (types_.Contains(SMI)) {
__ ldr(map, FieldMemOperand(tos_, HeapObject::kMapOffset)); // 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. if (types_.NeedsMap()) {
__ ldrb(ip, FieldMemOperand(map, Map::kBitFieldOffset)); __ ldr(map, FieldMemOperand(tos_, HeapObject::kMapOffset));
__ tst(ip, Operand(1 << Map::kIsUndetectable));
__ b(&false_result, ne);
// JavaScript object -> true. // Everything with a map could be undetectable, so check this now.
__ CompareInstanceType(map, ip, FIRST_SPEC_OBJECT_TYPE); __ ldrb(ip, FieldMemOperand(map, Map::kBitFieldOffset));
// "tos_" is a register and contains a non-zero value. Hence we implicitly __ tst(ip, Operand(1 << Map::kIsUndetectable));
// return true if the greater than condition is satisfied. // Undetectable -> false.
__ Ret(ge); __ 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); __ CompareInstanceType(map, ip, FIRST_NONSTRING_TYPE);
__ b(&not_string, ge); __ ldr(tos_, FieldMemOperand(tos_, String::kLengthOffset), lt);
__ ldr(tos_, FieldMemOperand(tos_, String::kLengthOffset)); __ Ret(lt); // the string length is OK as the return value
// Return string length as boolean value, i.e. return false iff length is 0. } else if (types_.Contains(INTERNAL_OBJECT)) {
__ Ret(); // We've seen a string for the first time -> patch
__ CompareInstanceType(map, ip, FIRST_NONSTRING_TYPE);
__ b(lt, &patch);
}
__ bind(&not_string); if (types_.Contains(HEAP_NUMBER)) {
// HeapNumber -> false iff +0, -0, or NaN. // Heap number -> false iff +0, -0, or NaN.
__ CompareRoot(map, Heap::kHeapNumberMapRootIndex); Label not_heap_number;
__ b(&true_result, ne); __ CompareRoot(map, Heap::kHeapNumberMapRootIndex);
__ vldr(d1, FieldMemOperand(tos_, HeapNumber::kValueOffset)); __ b(ne, &not_heap_number);
__ VFPCompareAndSetFlags(d1, 0.0); __ vldr(d1, FieldMemOperand(tos_, HeapNumber::kValueOffset));
// "tos_" is a register, and contains a non zero value by default. __ VFPCompareAndSetFlags(d1, 0.0);
// Hence we only need to overwrite "tos_" with zero to return false for // "tos_" is a register, and contains a non zero value by default.
// FP_ZERO or FP_NAN cases. Otherwise, by default it returns true. // Hence we only need to overwrite "tos_" with zero to return false for
__ mov(tos_, Operand(0, RelocInfo::NONE), LeaveCC, eq); // for FP_ZERO // FP_ZERO or FP_NAN cases. Otherwise, by default it returns true.
__ mov(tos_, Operand(0, RelocInfo::NONE), LeaveCC, vs); // for FP_NAN __ mov(tos_, Operand(0, RelocInfo::NONE), LeaveCC, eq); // for FP_ZERO
__ Ret(); __ 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_. if (types_.Contains(INTERNAL_OBJECT)) {
__ bind(&true_result); // Internal objects -> true.
__ mov(tos_, Operand(1, RelocInfo::NONE)); __ mov(tos_, Operand(1, RelocInfo::NONE));
__ Ret(); __ Ret();
__ bind(&false_result); }
__ mov(tos_, Operand(0, 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) { ...@@ -1039,7 +1039,13 @@ LInstruction* LChunkBuilder::DoBranch(HBranch* instr) {
: instr->SecondSuccessor(); : instr->SecondSuccessor();
return new LGoto(successor->block_id()); 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) { ...@@ -1564,52 +1564,138 @@ void LCodeGen::DoBranch(LBranch* instr) {
} else { } else {
ASSERT(r.IsTagged()); ASSERT(r.IsTagged());
Register reg = ToRegister(instr->InputAt(0)); Register reg = ToRegister(instr->InputAt(0));
if (instr->hydrogen()->value()->type().IsBoolean()) { HType type = instr->hydrogen()->value()->type();
__ LoadRoot(ip, Heap::kTrueValueRootIndex); if (type.IsBoolean()) {
__ cmp(reg, ip); __ CompareRoot(reg, Heap::kTrueValueRootIndex);
EmitBranch(true_block, false_block, eq); EmitBranch(true_block, false_block, eq);
} else if (type.IsSmi()) {
__ cmp(reg, Operand(0));
EmitBranch(true_block, false_block, ne);
} else { } else {
Label* true_label = chunk_->GetAssemblyLabel(true_block); Label* true_label = chunk_->GetAssemblyLabel(true_block);
Label* false_label = chunk_->GetAssemblyLabel(false_block); Label* false_label = chunk_->GetAssemblyLabel(false_block);
__ LoadRoot(ip, Heap::kUndefinedValueRootIndex); ToBooleanStub::Types expected = instr->hydrogen()->expected_input_types();
__ cmp(reg, ip); // Avoid deopts in the case where we've never executed this path before.
__ b(eq, false_label); if (expected.IsEmpty()) expected = ToBooleanStub::all_types();
__ LoadRoot(ip, Heap::kTrueValueRootIndex);
__ cmp(reg, ip); if (expected.Contains(ToBooleanStub::UNDEFINED)) {
__ b(eq, true_label); // undefined -> false.
__ LoadRoot(ip, Heap::kFalseValueRootIndex); __ CompareRoot(reg, Heap::kUndefinedValueRootIndex);
__ cmp(reg, ip); __ b(eq, false_label);
__ b(eq, false_label); } else if (expected.Contains(ToBooleanStub::INTERNAL_OBJECT)) {
__ cmp(reg, Operand(0)); // We've seen undefined for the first time -> deopt.
__ b(eq, false_label); __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
__ JumpIfSmi(reg, true_label); DeoptimizeIf(eq, instr->environment());
}
// Test double values. Zero and NaN are false.
Label call_stub; if (expected.Contains(ToBooleanStub::BOOLEAN)) {
DoubleRegister dbl_scratch = double_scratch0(); // Boolean -> its value.
Register scratch = scratch0(); __ CompareRoot(reg, Heap::kTrueValueRootIndex);
__ ldr(scratch, FieldMemOperand(reg, HeapObject::kMapOffset)); __ b(eq, true_label);
__ LoadRoot(ip, Heap::kHeapNumberMapRootIndex); __ CompareRoot(reg, Heap::kFalseValueRootIndex);
__ cmp(scratch, Operand(ip)); __ b(eq, false_label);
__ b(ne, &call_stub); } else if (expected.Contains(ToBooleanStub::INTERNAL_OBJECT)) {
__ sub(ip, reg, Operand(kHeapObjectTag)); // We've seen a boolean for the first time -> deopt.
__ vldr(dbl_scratch, ip, HeapNumber::kValueOffset); __ CompareRoot(reg, Heap::kTrueValueRootIndex);
__ VFPCompareAndLoadFlags(dbl_scratch, 0.0, scratch); DeoptimizeIf(eq, instr->environment());
__ tst(scratch, Operand(kVFPZConditionFlagBit | kVFPVConditionFlagBit)); __ CompareRoot(reg, Heap::kFalseValueRootIndex);
__ b(ne, false_label); DeoptimizeIf(eq, instr->environment());
__ b(true_label); }
// The conversion stub doesn't cause garbage collections so it's #if 0
// safe to not record a safepoint after the call. if (expected.Contains(ToBooleanStub::BOOLEAN)) {
__ bind(&call_stub); // false -> false.
ToBooleanStub stub(reg); __ CompareRoot(reg, Heap::kFalseValueRootIndex);
RegList saved_regs = kJSCallerSaved | kCalleeSaved; __ b(eq, false_label);
__ stm(db_w, sp, saved_regs); } else if (expected.Contains(ToBooleanStub::INTERNAL_OBJECT)) {
__ CallStub(&stub); // We've seen a boolean for the first time -> deopt.
__ cmp(reg, Operand(0)); __ CompareRoot(reg, Heap::kFalseValueRootIndex);
__ ldm(ia_w, sp, saved_regs); DeoptimizeIf(eq, instr->environment());
EmitBranch(true_block, false_block, ne); }
#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