 Chromium Code Reviews
 Chromium Code Reviews Issue 7491054:
  Implement type recording for ToBoolean on ARM.  (Closed) 
  Base URL: http://v8.googlecode.com/svn/branches/bleeding_edge/
    
  
    Issue 7491054:
  Implement type recording for ToBoolean on ARM.  (Closed) 
  Base URL: http://v8.googlecode.com/svn/branches/bleeding_edge/| Index: src/arm/code-stubs-arm.cc | 
| =================================================================== | 
| --- src/arm/code-stubs-arm.cc (revision 8836) | 
| +++ src/arm/code-stubs-arm.cc (working copy) | 
| @@ -1603,86 +1603,142 @@ | 
| } | 
| -// 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; | 
| 
Erik Corry
2011/08/05 12:50:46
If we have ever seen an internal object you could
 
Sven Panne
2011/08/09 07:58:21
I think we can get rid of all internal object chec
 | 
| // undefined -> false | 
| 
Erik Corry
2011/08/05 12:50:46
Missing full stops on the end of comments several
 
Sven Panne
2011/08/09 07:58:21
Done.
 | 
| - __ LoadRoot(ip, Heap::kUndefinedValueRootIndex); | 
| - __ cmp(tos_, ip); | 
| - __ b(eq, &false_result); | 
| + CheckOddball(masm, UNDEFINED, Heap::kUndefinedValueRootIndex, false, &patch); | 
| // 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); | 
| + CheckOddball(masm, BOOLEAN, Heap::kFalseValueRootIndex, false, &patch); | 
| + CheckOddball(masm, BOOLEAN, Heap::kTrueValueRootIndex, true, &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); | 
| + // 'null' -> false. | 
| + CheckOddball(masm, NULL_TYPE, Heap::kNullValueRootIndex, false, &patch); | 
| - // 'null' -> false | 
| - __ LoadRoot(ip, Heap::kNullValueRootIndex); | 
| - __ cmp(tos_, ip); | 
| - __ b(eq, &false_result); | 
| + 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); | 
| + } | 
| - // Get the map of the heap object. | 
| - __ ldr(map, FieldMemOperand(tos_, HeapObject::kMapOffset)); | 
| + if (types_.NeedsMap()) { | 
| + __ ldr(map, FieldMemOperand(tos_, HeapObject::kMapOffset)); | 
| - // Undetectable -> false. | 
| - __ ldrb(ip, FieldMemOperand(map, Map::kBitFieldOffset)); | 
| - __ tst(ip, Operand(1 << Map::kIsUndetectable)); | 
| - __ b(&false_result, ne); | 
| + // Everything with a map could be undetectable, so check this now. | 
| 
Erik Corry
2011/08/05 12:50:46
Pretty sure that it is just strings and JSObjects.
 
Sven Panne
2011/08/09 07:58:21
Again, this is platform-independent, so let's hand
 | 
| + __ ldrb(ip, FieldMemOperand(map, Map::kBitFieldOffset)); | 
| + __ tst(ip, Operand(1 << Map::kIsUndetectable)); | 
| + // Undetectable -> false. | 
| + __ mov(tos_, Operand(0, RelocInfo::NONE), LeaveCC, ne); | 
| + __ Ret(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); | 
| + 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(¬_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(¬_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, ¬_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); | 
| + } | 
| - // 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 | 
| 
Erik Corry
2011/08/05 12:50:46
Caps and full stop.
 
Sven Panne
2011/08/09 07:58:21
Done.
 | 
| + __ 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); | 
| +} | 
| + | 
| + | 
| void UnaryOpStub::PrintName(StringStream* stream) { | 
| const char* op_name = Token::Name(op_); | 
| const char* overwrite_name = NULL; // Make g++ happy. |