Chromium Code Reviews| 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. |