Revision: 8859
Author: [email protected]
Date: Tue Aug 9 00:59:00 2011
Log: Implement type recording for ToBoolean on ARM.
Review URL: http://codereview.chromium.org/7491054
http://code.google.com/p/v8/source/detail?r=8859
Modified:
/branches/bleeding_edge/src/arm/code-stubs-arm.cc
/branches/bleeding_edge/src/arm/lithium-arm.cc
/branches/bleeding_edge/src/arm/lithium-codegen-arm.cc
=======================================
--- /branches/bleeding_edge/src/arm/code-stubs-arm.cc Tue Jul 19 01:19:31
2011
+++ /branches/bleeding_edge/src/arm/code-stubs-arm.cc Tue Aug 9 00:59:00
2011
@@ -1603,83 +1603,139 @@
}
-// 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);
-
- // 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);
-
- // 'null' -> false
- __ LoadRoot(ip, Heap::kNullValueRootIndex);
- __ cmp(tos_, ip);
- __ b(eq, &false_result);
-
- // Get the map of the heap object.
- __ ldr(map, FieldMemOperand(tos_, HeapObject::kMapOffset));
-
- // Undetectable -> false.
- __ ldrb(ip, FieldMemOperand(map, Map::kBitFieldOffset));
- __ tst(ip, Operand(1 << Map::kIsUndetectable));
- __ b(&false_result, ne);
-
- // 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);
-
- // String value -> false iff empty.
+ // undefined -> false.
+ CheckOddball(masm, UNDEFINED, Heap::kUndefinedValueRootIndex, false,
&patch);
+
+ // Boolean -> its value.
+ CheckOddball(masm, BOOLEAN, Heap::kFalseValueRootIndex, false, &patch);
+ CheckOddball(masm, BOOLEAN, Heap::kTrueValueRootIndex, true, &patch);
+
+ // 'null' -> false.
+ CheckOddball(masm, NULL_TYPE, Heap::kNullValueRootIndex, false, &patch);
+
+ 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);
+ }
+
+ if (types_.NeedsMap()) {
+ __ ldr(map, FieldMemOperand(tos_, 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));
+ // 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);
+ }
+
+ if (types_.Contains(STRING)) {
+ // String value -> false iff empty.
__ CompareInstanceType(map, ip, FIRST_NONSTRING_TYPE);
- __ b(¬_string, ge);
- __ ldr(tos_, FieldMemOperand(tos_, String::kLengthOffset));
- // Return string length as boolean value, i.e. return false iff length
is 0.
- __ Ret();
-
- __ bind(¬_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();
-
- // 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();
+ __ 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);
+ }
+
+ if (types_.Contains(HEAP_NUMBER)) {
+ // Heap number -> false iff +0, -0, or NaN.
+ Label not_heap_number;
+ __ CompareRoot(map, Heap::kHeapNumberMapRootIndex);
+ __ b(ne, ¬_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(¬_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);
+ }
+
+ 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);
}
=======================================
--- /branches/bleeding_edge/src/arm/lithium-arm.cc Tue Jul 19 06:04:00 2011
+++ /branches/bleeding_edge/src/arm/lithium-arm.cc Tue Aug 9 00:59:00 2011
@@ -1039,7 +1039,13 @@
: 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);
}
=======================================
--- /branches/bleeding_edge/src/arm/lithium-codegen-arm.cc Tue Aug 2
08:14:12 2011
+++ /branches/bleeding_edge/src/arm/lithium-codegen-arm.cc Tue Aug 9
00:59:00 2011
@@ -1564,52 +1564,138 @@
} 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, ¬_string);
+ __ ldr(ip, FieldMemOperand(reg, String::kLengthOffset));
+ __ cmp(ip, Operand(0));
+ __ b(ne, true_label);
+ __ b(false_label);
+ __ bind(¬_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, ¬_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(¬_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());
+ }
}
}
}
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev