Index: src/arm/codegen-arm.cc |
=================================================================== |
--- src/arm/codegen-arm.cc (revision 2110) |
+++ src/arm/codegen-arm.cc (working copy) |
@@ -675,6 +675,7 @@ |
} |
void Generate(MacroAssembler* masm); |
+ void HandleNonSmiBitwiseOp(MacroAssembler* masm); |
const char* GetName() { |
switch (op_) { |
@@ -3591,7 +3592,10 @@ |
break; |
case Token::SUB: { |
- UnarySubStub stub; |
+ bool overwrite = |
+ (node->AsBinaryOperation() != NULL && |
+ node->AsBinaryOperation()->ResultOverwriteAllowed()); |
+ UnarySubStub stub(overwrite); |
frame_->CallStub(&stub, 0); |
break; |
} |
@@ -4361,6 +4365,221 @@ |
} |
+// Count leading zeros in a 32 bit word. On ARM5 and later it uses the clz |
+// instruction. On pre-ARM5 hardware this routine gives the wrong answer for 0 |
+// (31 instead of 32). |
+static void CountLeadingZeros( |
+ MacroAssembler* masm, |
+ Register source, |
+ Register scratch, |
+ Register zeros) { |
+#ifdef __ARM_ARCH_5__ |
+ __ clz(zeros, source); // This instruction is only supported after ARM5. |
+#else |
+ __ mov(zeros, Operand(0)); |
+ __ mov(scratch, source); |
+ // Top 16. |
+ __ tst(scratch, Operand(0xffff0000)); |
+ __ add(zeros, zeros, Operand(16), LeaveCC, eq); |
+ __ mov(scratch, Operand(scratch, LSL, 16), LeaveCC, eq); |
+ // Top 8. |
+ __ tst(scratch, Operand(0xff000000)); |
+ __ add(zeros, zeros, Operand(8), LeaveCC, eq); |
+ __ mov(scratch, Operand(scratch, LSL, 8), LeaveCC, eq); |
+ // Top 4. |
+ __ tst(scratch, Operand(0xf0000000)); |
+ __ add(zeros, zeros, Operand(4), LeaveCC, eq); |
+ __ mov(scratch, Operand(scratch, LSL, 4), LeaveCC, eq); |
+ // Top 2. |
+ __ tst(scratch, Operand(0xc0000000)); |
+ __ add(zeros, zeros, Operand(2), LeaveCC, eq); |
+ __ mov(scratch, Operand(scratch, LSL, 2), LeaveCC, eq); |
+ // Top bit. |
+ __ tst(scratch, Operand(0x80000000)); |
+ __ add(zeros, zeros, Operand(1), LeaveCC, eq); |
+#endif |
+} |
+ |
+ |
+// Takes a Smi and converts to an IEEE 64 bit floating point value in two |
+// registers. The format is 1 sign bit, 11 exponent bits (biased 1023) and |
+// 52 fraction bits (20 in the first word, 32 in the second). Zeros is a |
+// scratch register. Destroys the source register. No GC occurs during this |
+// stub so you don't have to set up the frame. |
+class ConvertToDoubleStub : public CodeStub { |
+ public: |
+ ConvertToDoubleStub(Register result_reg_1, |
+ Register result_reg_2, |
+ Register source_reg, |
+ Register scratch_reg) |
+ : result1_(result_reg_1), |
+ result2_(result_reg_2), |
+ source_(source_reg), |
+ zeros_(scratch_reg) { } |
+ |
+ private: |
+ Register result1_; |
+ Register result2_; |
+ Register source_; |
+ Register zeros_; |
+ |
+ // Minor key encoding in 16 bits. |
+ class ModeBits: public BitField<OverwriteMode, 0, 2> {}; |
+ class OpBits: public BitField<Token::Value, 2, 14> {}; |
+ |
+ Major MajorKey() { return ConvertToDouble; } |
+ int MinorKey() { |
+ // Encode the parameters in a unique 16 bit value. |
+ return result1_.code() + |
+ (result2_.code() << 4) + |
+ (source_.code() << 8) + |
+ (zeros_.code() << 12); |
+ } |
+ |
+ void Generate(MacroAssembler* masm); |
+ |
+ const char* GetName() { return "ConvertToDoubleStub"; } |
+ |
+#ifdef DEBUG |
+ void Print() { PrintF("ConvertToDoubleStub\n"); } |
+#endif |
+}; |
+ |
+ |
+void ConvertToDoubleStub::Generate(MacroAssembler* masm) { |
+ Label not_special, done; |
+ // Convert from Smi to integer. |
+ __ mov(source_, Operand(source_, ASR, kSmiTagSize)); |
+ // Move sign bit from source to destination. This works because the sign bit |
+ // in the exponent word of the double has the same position and polarity as |
+ // the 2's complement sign bit in a Smi. |
+ ASSERT(HeapNumber::kSignMask == 0x80000000u); |
+ __ and_(result1_, source_, Operand(HeapNumber::kSignMask), SetCC); |
+ // Subtract from 0 if source was negative. |
+ __ rsb(source_, source_, Operand(0), LeaveCC, ne); |
+ __ cmp(source_, Operand(1)); |
+ __ b(gt, ¬_special); |
+ |
+ // We have -1, 0 or 1, which we treat specially. |
+ __ cmp(source_, Operand(0)); |
+ // For 1 or -1 we need to or in the 0 exponent (biased to 1023). |
+ static const uint32_t exponent_word_for_1 = |
+ HeapNumber::kExponentBias << HeapNumber::kExponentShift; |
+ __ orr(result1_, result1_, Operand(exponent_word_for_1), LeaveCC, ne); |
+ // 1, 0 and -1 all have 0 for the second word. |
+ __ mov(result2_, Operand(0)); |
+ __ jmp(&done); |
+ |
+ __ bind(¬_special); |
+ // Count leading zeros. Uses result2 for a scratch register on pre-ARM5. |
+ // Gets the wrong answer for 0, but we already checked for that case above. |
+ CountLeadingZeros(masm, source_, result2_, zeros_); |
+ // Compute exponent and or it into the exponent register. |
+ // We use result2 as a scratch register here. |
+ __ rsb(result2_, zeros_, Operand(31 + HeapNumber::kExponentBias)); |
+ __ orr(result1_, result1_, Operand(result2_, LSL, 20)); |
iposva
2009/06/10 04:30:48
Please use kExponentShift, which you added to the
|
+ // Shift up the source chopping the top bit off. |
+ __ add(zeros_, zeros_, Operand(1)); |
+ // This wouldn't work for 1.0 or -1.0 as the shift would be 32 which means 0. |
+ __ mov(source_, Operand(source_, LSL, zeros_)); |
+ // Compute lower part of fraction (last 12 bits). |
+ __ mov(result2_, Operand(source_, LSL, HeapNumber::kMantissaBitsInTopWord)); |
+ // And the top (top 20 bits). |
+ __ orr(result1_, |
+ result1_, |
+ Operand(source_, LSR, 32 - HeapNumber::kMantissaBitsInTopWord)); |
+ __ bind(&done); |
+ __ Ret(); |
+} |
+ |
+ |
+// This stub can convert a signed int32 to a heap number (double). It does |
+// not work for int32s that are in Smi range! No GC occurs during this stub |
+// so you don't have to set up the frame. |
+class WriteInt32ToHeapNumberStub : public CodeStub { |
+ public: |
+ WriteInt32ToHeapNumberStub(Register the_int, |
+ Register the_heap_number, |
+ Register scratch) |
+ : the_int_(the_int), |
+ the_heap_number_(the_heap_number), |
+ scratch_(scratch) { } |
+ |
+ private: |
+ Register the_int_; |
+ Register the_heap_number_; |
+ Register scratch_; |
+ |
+ // Minor key encoding in 16 bits. |
+ class ModeBits: public BitField<OverwriteMode, 0, 2> {}; |
+ class OpBits: public BitField<Token::Value, 2, 14> {}; |
+ |
+ Major MajorKey() { return WriteInt32ToHeapNumber; } |
+ int MinorKey() { |
+ // Encode the parameters in a unique 16 bit value. |
+ return the_int_.code() + |
+ (the_heap_number_.code() << 4) + |
+ (scratch_.code() << 8); |
+ } |
+ |
+ void Generate(MacroAssembler* masm); |
+ |
+ const char* GetName() { return "WriteInt32ToHeapNumberStub"; } |
+ |
+#ifdef DEBUG |
+ void Print() { PrintF("WriteInt32ToHeapNumberStub\n"); } |
+#endif |
+}; |
+ |
+ |
+// See comment for class. |
+void WriteInt32ToHeapNumberStub::Generate(MacroAssembler *masm) { |
+ Label max_negative_int; |
+ // the_int_ has the answer which is a signed int32 but not a Smi. |
+ // We test for the special value that has a different exponent. This test |
+ // has the neat side effect of setting the flags according to the sign. |
+ ASSERT(HeapNumber::kSignMask == 0x80000000u); |
+ __ cmp(the_int_, Operand(0x80000000)); |
+ __ b(eq, &max_negative_int); |
+ // Set up the correct exponent in scratch_. All non-Smi int32s have the same. |
+ // A non-Smi integer is 1.xxx * 2^30 so the exponent is 30 (biased). |
+ uint32_t non_smi_exponent = |
+ (HeapNumber::kExponentBias + 30) << HeapNumber::kExponentShift; |
+ __ mov(scratch_, Operand(non_smi_exponent)); |
+ // Set the sign bit in scratch_ if the value was negative. |
+ __ orr(scratch_, scratch_, Operand(HeapNumber::kSignMask), LeaveCC, cs); |
+ // Subtract from 0 if the value was negative. |
+ __ rsb(the_int_, the_int_, Operand(0), LeaveCC, cs); |
+ // We should be masking the implict first digit of the mantissa away here, |
+ // but it just ends up combining harmlessly with the last digit of the |
+ // exponent that happens to be 1. The sign bit is 0 so we shift 10 to get |
+ // the most significant 1 to hit the last bit of the 12 bit sign and exponent. |
+ ASSERT(((1 << HeapNumber::kExponentShift) & non_smi_exponent) != 0); |
+ const int shift_distance = HeapNumber::kNonMantissaBitsInTopWord - 2; |
+ __ orr(scratch_, scratch_, Operand(the_int_, LSR, shift_distance)); |
+ __ str(scratch_, FieldMemOperand(the_heap_number_, |
+ HeapNumber::kExponentOffset)); |
+ __ mov(scratch_, Operand(the_int_, LSL, 32 - shift_distance)); |
+ __ str(scratch_, FieldMemOperand(the_heap_number_, |
+ HeapNumber::kMantissaOffset)); |
+ __ Ret(); |
+ |
+ __ bind(&max_negative_int); |
+ // The max negative int32 is stored as a positive number in the mantissa of |
+ // a double because it uses a sign bit instead of using two's complement. |
+ // The actual mantissa bits stored are all 0 because the implicit most |
+ // significant 1 bit is not stored. |
+ non_smi_exponent += 1 << HeapNumber::kExponentShift; |
+ __ mov(ip, Operand(HeapNumber::kSignMask | non_smi_exponent)); |
+ __ str(ip, FieldMemOperand(the_heap_number_, HeapNumber::kExponentOffset)); |
+ __ mov(ip, Operand(0)); |
+ __ str(ip, FieldMemOperand(the_heap_number_, HeapNumber::kMantissaOffset)); |
+ __ Ret(); |
+} |
+ |
+ |
+// Allocates a heap number or jumps to the label if the young space is full and |
+// a scavenge is needed. |
static void AllocateHeapNumber( |
MacroAssembler* masm, |
Label* need_gc, // Jump here if young space is full. |
@@ -4397,59 +4616,122 @@ |
} |
+// Checks that the object register (which is assumed not to be a Smi) points to |
+// a heap number. Jumps to the label if it is not. |
+void CheckForHeapNumber(MacroAssembler* masm, |
+ Register object, |
+ Register scratch, |
+ Label* slow) { |
+ // Get map of object into scratch. |
+ __ ldr(scratch, FieldMemOperand(object, HeapObject::kMapOffset)); |
+ // Get type of object into scratch. |
+ __ ldrb(scratch, FieldMemOperand(scratch, Map::kInstanceTypeOffset)); |
+ __ cmp(scratch, Operand(HEAP_NUMBER_TYPE)); |
+ __ b(ne, slow); |
+} |
+ |
+ |
// We fall into this code if the operands were Smis, but the result was |
// not (eg. overflow). We branch into this code (to the not_smi label) if |
-// the operands were not both Smi. |
+// the operands were not both Smi. The operands are in r0 and r1. In order |
+// to call the C-implemented binary fp operation routines we need to end up |
+// with the double precision floating point operands in r0 and r1 (for the |
+// value in r1) and r2 and r3 (for the value in r0). |
static void HandleBinaryOpSlowCases(MacroAssembler* masm, |
Label* not_smi, |
const Builtins::JavaScript& builtin, |
Token::Value operation, |
int swi_number, |
OverwriteMode mode) { |
- Label slow; |
+ Label slow, slow_pop_2_first, do_the_call; |
+ Label r0_is_smi, r1_is_smi, finished_loading_r0, finished_loading_r1; |
+ // Smi-smi case (overflow). |
+ // Since both are Smis there is no heap number to overwrite, so allocate. |
+ // The new heap number is in r5. r6 and r7 are scratch. |
+ AllocateHeapNumber(masm, &slow, r5, r6, r7); |
+ // Write Smi from r0 to r3 and r2 in double format. r6 is scratch. |
+ ConvertToDoubleStub stub1(r3, r2, r0, r6); |
+ __ push(lr); |
+ __ Call(stub1.GetCode(), RelocInfo::CODE_TARGET); |
+ // Write Smi from r1 to r1 and r0 in double format. r6 is scratch. |
+ __ mov(r7, Operand(r1)); |
+ ConvertToDoubleStub stub2(r1, r0, r7, r6); |
+ __ Call(stub2.GetCode(), RelocInfo::CODE_TARGET); |
+ __ pop(lr); |
+ __ jmp(&do_the_call); // Tail call. No return. |
+ |
+ // We jump to here if something goes wrong (one param is not a number of any |
+ // sort or new-space allocation fails). |
__ bind(&slow); |
__ push(r1); |
__ push(r0); |
__ mov(r0, Operand(1)); // Set number of arguments. |
- __ InvokeBuiltin(builtin, JUMP_JS); // Tail call. |
+ __ InvokeBuiltin(builtin, JUMP_JS); // Tail call. No return. |
+ // We branch here if at least one of r0 and r1 is not a Smi. |
__ bind(not_smi); |
+ if (mode == NO_OVERWRITE) { |
+ // In the case where there is no chance of an overwritable float we may as |
+ // well do the allocation immediately while r0 and r1 are untouched. |
+ AllocateHeapNumber(masm, &slow, r5, r6, r7); |
+ } |
+ |
+ // Move r0 to a double in r2-r3. |
__ tst(r0, Operand(kSmiTagMask)); |
- __ b(eq, &slow); // We can't handle a Smi-double combination yet. |
- __ tst(r1, Operand(kSmiTagMask)); |
- __ b(eq, &slow); // We can't handle a Smi-double combination yet. |
- // Get map of r0 into r2. |
- __ ldr(r2, FieldMemOperand(r0, HeapObject::kMapOffset)); |
- // Get type of r0 into r3. |
- __ ldrb(r3, FieldMemOperand(r2, Map::kInstanceTypeOffset)); |
- __ cmp(r3, Operand(HEAP_NUMBER_TYPE)); |
- __ b(ne, &slow); |
- // Get type of r1 into r3. |
- __ ldr(r3, FieldMemOperand(r1, HeapObject::kMapOffset)); |
- // Check they are both the same map (heap number map). |
- __ cmp(r2, r3); |
- __ b(ne, &slow); |
- // Both are doubles. |
+ __ b(eq, &r0_is_smi); // It's a Smi so don't check it's a heap number. |
+ CheckForHeapNumber(masm, r0, r4, &slow); |
+ if (mode == OVERWRITE_RIGHT) { |
+ __ mov(r5, Operand(r0)); // Overwrite this heap number. |
+ } |
// Calling convention says that second double is in r2 and r3. |
- __ ldr(r2, FieldMemOperand(r0, HeapNumber::kValueOffset)); |
- __ ldr(r3, FieldMemOperand(r0, HeapNumber::kValueOffset + kPointerSize)); |
+ __ ldr(r2, FieldMemOperand(r0, HeapNumber::kMantissaOffset)); |
+ __ ldr(r3, FieldMemOperand(r0, HeapNumber::kExponentOffset)); |
+ __ jmp(&finished_loading_r0); |
+ __ bind(&r0_is_smi); |
+ if (mode == OVERWRITE_RIGHT) { |
+ // We can't overwrite a Smi so get address of new heap number into r5. |
+ AllocateHeapNumber(masm, &slow, r5, r6, r7); |
+ } |
+ // Write Smi from r0 to r3 and r2 in double format. |
+ __ mov(r7, Operand(r0)); |
+ ConvertToDoubleStub stub3(r3, r2, r7, r6); |
+ __ push(lr); |
+ __ Call(stub3.GetCode(), RelocInfo::CODE_TARGET); |
+ __ pop(lr); |
+ __ bind(&finished_loading_r0); |
- if (mode == NO_OVERWRITE) { |
- // Get address of new heap number into r5. |
+ // Move r1 to a double in r0-r1. |
+ __ tst(r1, Operand(kSmiTagMask)); |
+ __ b(eq, &r1_is_smi); // It's a Smi so don't check it's a heap number. |
+ CheckForHeapNumber(masm, r1, r4, &slow); |
+ if (mode == OVERWRITE_LEFT) { |
+ __ mov(r5, Operand(r1)); // Overwrite this heap number. |
+ } |
+ // Calling convention says that first double is in r0 and r1. |
+ __ ldr(r0, FieldMemOperand(r1, HeapNumber::kMantissaOffset)); |
+ __ ldr(r1, FieldMemOperand(r1, HeapNumber::kExponentOffset)); |
+ __ jmp(&finished_loading_r1); |
+ __ bind(&r1_is_smi); |
+ if (mode == OVERWRITE_LEFT) { |
+ // We can't overwrite a Smi so get address of new heap number into r5. |
AllocateHeapNumber(masm, &slow, r5, r6, r7); |
- __ push(lr); |
- __ push(r5); |
- } else if (mode == OVERWRITE_LEFT) { |
- __ push(lr); |
- __ push(r1); |
- } else { |
- ASSERT(mode == OVERWRITE_RIGHT); |
- __ push(lr); |
- __ push(r0); |
} |
- // Calling convention says that first double is in r0 and r1. |
- __ ldr(r0, FieldMemOperand(r1, HeapNumber::kValueOffset)); |
- __ ldr(r1, FieldMemOperand(r1, HeapNumber::kValueOffset + kPointerSize)); |
+ // Write Smi from r1 to r1 and r0 in double format. |
+ __ mov(r7, Operand(r1)); |
+ ConvertToDoubleStub stub4(r1, r0, r7, r6); |
+ __ push(lr); |
+ __ Call(stub4.GetCode(), RelocInfo::CODE_TARGET); |
+ __ pop(lr); |
+ __ bind(&finished_loading_r1); |
+ |
+ __ bind(&do_the_call); |
+ // r0: Left value (least significant part of mantissa). |
+ // r1: Left value (sign, exponent, top of mantissa). |
+ // r2: Right value (least significant part of mantissa). |
+ // r3: Right value (sign, exponent, top of mantissa). |
+ // r5: Address of heap number for result. |
+ __ push(lr); // For later. |
+ __ push(r5); // Address of heap number that is answer. |
// Call C routine that may not cause GC or other trouble. |
__ mov(r5, Operand(ExternalReference::double_fp_operation(operation))); |
#if !defined(__arm__) |
@@ -4469,8 +4751,8 @@ |
__ stc(p1, cr8, MemOperand(r5, HeapNumber::kValueOffset)); |
#else |
// Double returned in fp coprocessor register 0 and 1. |
- __ str(r0, FieldMemOperand(r4, HeapNumber::kValueOffset)); |
- __ str(r1, FieldMemOperand(r4, HeapNumber::kValueOffset + kPointerSize)); |
+ __ str(r0, FieldMemOperand(r4, HeapNumber::kMantissaOffset)); |
+ __ str(r1, FieldMemOperand(r4, HeapNumber::kExponentOffset)); |
#endif |
__ mov(r0, Operand(r4)); |
// And we are done. |
@@ -4478,6 +4760,183 @@ |
} |
+// Tries to get a signed int32 out of a double precision floating point heap |
+// number. Rounds towards 0. Only succeeds for doubles that are in the ranges |
+// -0x7fffffff to -0x40000000 or 0x40000000 to 0x7fffffff. This corresponds |
+// almost to the range of signed int32 values that are not Smis. Jumps to the |
+// label if the double isn't in the range it can cope with. |
+static void GetInt32(MacroAssembler* masm, |
+ Register source, |
+ Register dest, |
+ Register scratch, |
+ Label* slow) { |
+ Register scratch2 = dest; |
+ // Get exponent word. |
+ __ ldr(scratch, FieldMemOperand(source, HeapNumber::kExponentOffset)); |
+ // Get exponent alone in scratch2. |
+ __ and_(scratch2, scratch, Operand(HeapNumber::kExponentMask)); |
+ // Check whether the exponent matches a 32 bit signed int that is not a Smi. |
+ // A non-Smi integer is 1.xxx * 2^30 so the exponent is 30 (biased). |
+ const uint32_t non_smi_exponent = |
+ (HeapNumber::kExponentBias + 30) << HeapNumber::kExponentShift; |
+ __ cmp(scratch2, Operand(non_smi_exponent)); |
+ // If not, then we go slow. |
+ __ b(ne, slow); |
+ // Get the top bits of the mantissa. |
+ __ and_(scratch2, scratch, Operand(HeapNumber::kMantissaMask)); |
+ // Put back the implicit 1. |
+ __ orr(scratch2, scratch2, Operand(1 << HeapNumber::kExponentShift)); |
+ // Shift up the mantissa bits to take up the space the exponent used to take. |
+ // We just orred in the implicit bit so that took care of one and we want to |
+ // leave the sign bit 0 so we subtract 2 bits from the shift distance. |
+ const int shift_distance = HeapNumber::kNonMantissaBitsInTopWord - 2; |
+ __ mov(scratch2, Operand(scratch2, LSL, shift_distance)); |
+ // Put sign in zero flag. |
+ __ tst(scratch, Operand(HeapNumber::kSignMask)); |
+ // Get the second half of the double. |
+ __ ldr(scratch, FieldMemOperand(source, HeapNumber::kMantissaOffset)); |
+ // Shift down 22 bits to get the last 10 bits. |
+ __ orr(dest, scratch2, Operand(scratch, LSR, 32 - shift_distance)); |
+ // Fix sign if sign bit was set. |
+ __ rsb(dest, dest, Operand(0), LeaveCC, ne); |
+} |
+ |
+ |
+// For bitwise ops where the inputs are not both Smis we here try to determine |
+// whether both inputs are either Smis or at least heap numbers that can be |
+// represented by a 32 bit signed value. We truncate towards zero as required |
+// by the ES spec. If this is the case we do the bitwise op and see if the |
+// result is a Smi. If so, great, otherwise we try to find a heap number to |
+// write the answer into (either by allocating or by overwriting). |
+// On entry the operands are in r0 and r1. On exit the answer is in r0. |
+void GenericBinaryOpStub::HandleNonSmiBitwiseOp(MacroAssembler* masm) { |
+ Label slow, result_not_a_smi; |
+ Label r0_is_smi, r1_is_smi; |
+ Label done_checking_r0, done_checking_r1; |
+ |
+ __ tst(r1, Operand(kSmiTagMask)); |
+ __ b(eq, &r1_is_smi); // It's a Smi so don't check it's a heap number. |
+ CheckForHeapNumber(masm, r1, r4, &slow); |
+ GetInt32(masm, r1, r3, r4, &slow); |
+ __ jmp(&done_checking_r1); |
+ __ bind(&r1_is_smi); |
+ __ mov(r3, Operand(r1, ASR, 1)); |
+ __ bind(&done_checking_r1); |
+ |
+ __ tst(r0, Operand(kSmiTagMask)); |
+ __ b(eq, &r0_is_smi); // It's a Smi so don't check it's a heap number. |
+ CheckForHeapNumber(masm, r0, r4, &slow); |
+ GetInt32(masm, r0, r2, r4, &slow); |
+ __ jmp(&done_checking_r0); |
+ __ bind(&r0_is_smi); |
+ __ mov(r2, Operand(r0, ASR, 1)); |
+ __ bind(&done_checking_r0); |
+ |
+ // r0 and r1: Original operands (Smi or heap numbers). |
+ // r2 and r3: Signed int32 operands. |
+ switch (op_) { |
+ case Token::BIT_OR: __ orr(r2, r2, Operand(r3)); break; |
+ case Token::BIT_XOR: __ eor(r2, r2, Operand(r3)); break; |
+ case Token::BIT_AND: __ and_(r2, r2, Operand(r3)); break; |
+ case Token::SAR: |
+ // Use only the 5 least significant bits of the shift count. |
+ __ and_(r2, r2, Operand(0x1f)); |
+ __ mov(r2, Operand(r3, ASR, r2)); |
+ break; |
+ case Token::SHR: |
+ // Use only the 5 least significant bits of the shift count. |
+ __ and_(r2, r2, Operand(0x1f)); |
+ __ mov(r2, Operand(r3, LSR, r2), SetCC); |
+ // SHR is special because it is required to produce a positive answer. |
+ // The code below for writing into heap numbers isn't capable of writing |
+ // the register as an unsigned int so we go to slow case if we hit this |
+ // case. |
+ __ b(mi, &slow); |
+ break; |
+ case Token::SHL: |
+ // Use only the 5 least significant bits of the shift count. |
+ __ and_(r2, r2, Operand(0x1f)); |
+ __ mov(r2, Operand(r3, LSL, r2)); |
+ break; |
+ default: UNREACHABLE(); |
+ } |
+ // check that the *signed* result fits in a smi |
+ __ add(r3, r2, Operand(0x40000000), SetCC); |
+ __ b(mi, &result_not_a_smi); |
+ __ mov(r0, Operand(r2, LSL, kSmiTagSize)); |
+ __ Ret(); |
+ |
+ Label have_to_allocate, got_a_heap_number; |
+ __ bind(&result_not_a_smi); |
+ switch (mode_) { |
+ case OVERWRITE_RIGHT: { |
+ __ tst(r0, Operand(kSmiTagMask)); |
+ __ b(eq, &have_to_allocate); |
+ __ mov(r5, Operand(r0)); |
+ break; |
+ } |
+ case OVERWRITE_LEFT: { |
+ __ tst(r1, Operand(kSmiTagMask)); |
+ __ b(eq, &have_to_allocate); |
+ __ mov(r5, Operand(r1)); |
+ break; |
+ } |
+ case NO_OVERWRITE: { |
+ // Get a new heap number in r5. r6 and r7 are scratch. |
+ AllocateHeapNumber(masm, &slow, r5, r6, r7); |
+ } |
+ default: break; |
+ } |
+ __ bind(&got_a_heap_number); |
+ // r2: Answer as signed int32. |
+ // r5: Heap number to write answer into. |
+ |
+ // Nothing can go wrong now, so move the heap number to r0, which is the |
+ // result. |
+ __ mov(r0, Operand(r5)); |
+ |
+ // Tail call that writes the int32 in r2 to the heap number in r0, using |
+ // r3 as scratch. r0 is preserved and returned. |
+ WriteInt32ToHeapNumberStub stub(r2, r0, r3); |
+ __ Jump(stub.GetCode(), RelocInfo::CODE_TARGET); |
+ |
+ if (mode_ != NO_OVERWRITE) { |
+ __ bind(&have_to_allocate); |
+ // Get a new heap number in r5. r6 and r7 are scratch. |
+ AllocateHeapNumber(masm, &slow, r5, r6, r7); |
+ __ jmp(&got_a_heap_number); |
+ } |
+ |
+ // If all else failed then we go to the runtime system. |
+ __ bind(&slow); |
+ __ push(r1); // restore stack |
+ __ push(r0); |
+ __ mov(r0, Operand(1)); // 1 argument (not counting receiver). |
+ switch (op_) { |
+ case Token::BIT_OR: |
+ __ InvokeBuiltin(Builtins::BIT_OR, JUMP_JS); |
+ break; |
+ case Token::BIT_AND: |
+ __ InvokeBuiltin(Builtins::BIT_AND, JUMP_JS); |
+ break; |
+ case Token::BIT_XOR: |
+ __ InvokeBuiltin(Builtins::BIT_XOR, JUMP_JS); |
+ break; |
+ case Token::SAR: |
+ __ InvokeBuiltin(Builtins::SAR, JUMP_JS); |
+ break; |
+ case Token::SHR: |
+ __ InvokeBuiltin(Builtins::SHR, JUMP_JS); |
+ break; |
+ case Token::SHL: |
+ __ InvokeBuiltin(Builtins::SHL, JUMP_JS); |
+ break; |
+ default: |
+ UNREACHABLE(); |
+ } |
+} |
+ |
+ |
void GenericBinaryOpStub::Generate(MacroAssembler* masm) { |
// r1 : x |
// r0 : y |
@@ -4553,13 +5012,16 @@ |
Builtins::MUL, |
Token::MUL, |
assembler::arm::simulator_fp_mul, |
- mode_); |
+ mode_); |
break; |
} |
case Token::BIT_OR: |
case Token::BIT_AND: |
- case Token::BIT_XOR: { |
+ case Token::BIT_XOR: |
+ case Token::SAR: |
+ case Token::SHR: |
+ case Token::SHL: { |
Label slow; |
ASSERT(kSmiTag == 0); // adjust code below |
__ tst(r2, Operand(kSmiTagMask)); |
@@ -4568,84 +5030,47 @@ |
case Token::BIT_OR: __ orr(r0, r0, Operand(r1)); break; |
case Token::BIT_AND: __ and_(r0, r0, Operand(r1)); break; |
case Token::BIT_XOR: __ eor(r0, r0, Operand(r1)); break; |
- default: UNREACHABLE(); |
- } |
- __ Ret(); |
- __ bind(&slow); |
- __ push(r1); // restore stack |
- __ push(r0); |
- __ mov(r0, Operand(1)); // 1 argument (not counting receiver). |
- switch (op_) { |
- case Token::BIT_OR: |
- __ InvokeBuiltin(Builtins::BIT_OR, JUMP_JS); |
- break; |
- case Token::BIT_AND: |
- __ InvokeBuiltin(Builtins::BIT_AND, JUMP_JS); |
- break; |
- case Token::BIT_XOR: |
- __ InvokeBuiltin(Builtins::BIT_XOR, JUMP_JS); |
- break; |
- default: |
- UNREACHABLE(); |
- } |
- break; |
- } |
- |
- case Token::SHL: |
- case Token::SHR: |
- case Token::SAR: { |
- Label slow; |
- ASSERT(kSmiTag == 0); // adjust code below |
- __ tst(r2, Operand(kSmiTagMask)); |
- __ b(ne, &slow); |
- // remove tags from operands (but keep sign) |
- __ mov(r3, Operand(r1, ASR, kSmiTagSize)); // x |
- __ mov(r2, Operand(r0, ASR, kSmiTagSize)); // y |
- // use only the 5 least significant bits of the shift count |
- __ and_(r2, r2, Operand(0x1f)); |
- // perform operation |
- switch (op_) { |
case Token::SAR: |
- __ mov(r3, Operand(r3, ASR, r2)); |
- // no checks of result necessary |
+ // Remove tags from right operand. |
+ __ mov(r2, Operand(r0, ASR, kSmiTagSize)); // y |
+ // Use only the 5 least significant bits of the shift count. |
+ __ and_(r2, r2, Operand(0x1f)); |
+ __ mov(r0, Operand(r1, ASR, r2)); |
+ // Smi tag result. |
+ __ and_(r0, r0, Operand(~kSmiTagMask)); |
break; |
- |
case Token::SHR: |
+ // Remove tags from operands. We can't do this on a 31 bit number |
+ // because then the 0s get shifted into bit 30 instead of bit 31. |
+ __ mov(r3, Operand(r1, ASR, kSmiTagSize)); // x |
+ __ mov(r2, Operand(r0, ASR, kSmiTagSize)); // y |
+ // Use only the 5 least significant bits of the shift count. |
+ __ and_(r2, r2, Operand(0x1f)); |
__ mov(r3, Operand(r3, LSR, r2)); |
- // check that the *unsigned* result fits in a smi |
- // neither of the two high-order bits can be set: |
- // - 0x80000000: high bit would be lost when smi tagging |
- // - 0x40000000: this number would convert to negative when |
- // smi tagging these two cases can only happen with shifts |
- // by 0 or 1 when handed a valid smi |
- __ and_(r2, r3, Operand(0xc0000000), SetCC); |
+ // Unsigned shift is not allowed to produce a negative number, so |
+ // check the sign bit and the sign bit after Smi tagging. |
+ __ tst(r3, Operand(0xc0000000)); |
__ b(ne, &slow); |
+ // Smi tag result. |
+ __ mov(r0, Operand(r3, LSL, kSmiTagSize)); |
break; |
- |
case Token::SHL: |
+ // Remove tags from operands. |
+ __ mov(r3, Operand(r1, ASR, kSmiTagSize)); // x |
+ __ mov(r2, Operand(r0, ASR, kSmiTagSize)); // y |
+ // Use only the 5 least significant bits of the shift count. |
+ __ and_(r2, r2, Operand(0x1f)); |
__ mov(r3, Operand(r3, LSL, r2)); |
- // check that the *signed* result fits in a smi |
+ // Check that the signed result fits in a Smi. |
__ add(r2, r3, Operand(0x40000000), SetCC); |
__ b(mi, &slow); |
+ __ mov(r0, Operand(r3, LSL, kSmiTagSize)); |
break; |
- |
default: UNREACHABLE(); |
} |
- // tag result and store it in r0 |
- ASSERT(kSmiTag == 0); // adjust code below |
- __ mov(r0, Operand(r3, LSL, kSmiTagSize)); |
__ Ret(); |
- // slow case |
__ bind(&slow); |
- __ push(r1); // restore stack |
- __ push(r0); |
- __ mov(r0, Operand(1)); // 1 argument (not counting receiver). |
- switch (op_) { |
- case Token::SAR: __ InvokeBuiltin(Builtins::SAR, JUMP_JS); break; |
- case Token::SHR: __ InvokeBuiltin(Builtins::SHR, JUMP_JS); break; |
- case Token::SHL: __ InvokeBuiltin(Builtins::SHL, JUMP_JS); break; |
- default: UNREACHABLE(); |
- } |
+ HandleNonSmiBitwiseOp(masm); |
break; |
} |
@@ -4677,10 +5102,11 @@ |
Label undo; |
Label slow; |
Label done; |
+ Label not_smi; |
// Enter runtime system if the value is not a smi. |
__ tst(r0, Operand(kSmiTagMask)); |
- __ b(ne, &slow); |
+ __ b(ne, ¬_smi); |
// Enter runtime system if the value of the expression is zero |
// to make sure that we switch between 0 and -0. |
@@ -4692,19 +5118,35 @@ |
__ rsb(r1, r0, Operand(0), SetCC); |
__ b(vs, &slow); |
- // If result is a smi we are done. |
- __ tst(r1, Operand(kSmiTagMask)); |
- __ mov(r0, Operand(r1), LeaveCC, eq); // conditionally set r0 to result |
- __ b(eq, &done); |
+ __ mov(r0, Operand(r1)); // Set r0 to result. |
+ __ StubReturn(1); |
// Enter runtime system. |
__ bind(&slow); |
__ push(r0); |
- __ mov(r0, Operand(0)); // set number of arguments |
+ __ mov(r0, Operand(0)); // Set number of arguments. |
__ InvokeBuiltin(Builtins::UNARY_MINUS, JUMP_JS); |
__ bind(&done); |
__ StubReturn(1); |
+ |
+ __ bind(¬_smi); |
+ CheckForHeapNumber(masm, r0, r1, &slow); |
+ // r0 is a heap number. Get a new heap number in r1. |
+ if (overwrite_) { |
+ __ ldr(r2, FieldMemOperand(r0, HeapNumber::kExponentOffset)); |
+ __ eor(r2, r2, Operand(HeapNumber::kSignMask)); // Flip sign. |
+ __ str(r2, FieldMemOperand(r0, HeapNumber::kExponentOffset)); |
+ } else { |
+ AllocateHeapNumber(masm, &slow, r1, r2, r3); |
+ __ ldr(r2, FieldMemOperand(r0, HeapNumber::kMantissaOffset)); |
+ __ str(r2, FieldMemOperand(r1, HeapNumber::kMantissaOffset)); |
+ __ ldr(r2, FieldMemOperand(r0, HeapNumber::kExponentOffset)); |
+ __ eor(r2, r2, Operand(HeapNumber::kSignMask)); // Flip sign. |
+ __ str(r2, FieldMemOperand(r1, HeapNumber::kExponentOffset)); |
+ __ mov(r0, Operand(r1)); |
+ } |
+ __ StubReturn(1); |
} |