Index: src/ia32/codegen-ia32.cc |
=================================================================== |
--- src/ia32/codegen-ia32.cc (revision 3709) |
+++ src/ia32/codegen-ia32.cc (working copy) |
@@ -763,7 +763,7 @@ |
ArgLocation arg_location = ARGS_ON_STACK); |
// Similar to LoadFloatOperand but assumes that both operands are smis. |
- // Accepts operands in eax, ebx. |
+ // Expects operands in edx, eax. |
static void LoadFloatSmis(MacroAssembler* masm, Register scratch); |
// Test if operands are smi or number objects (fp). Requirements: |
@@ -781,11 +781,11 @@ |
// them into xmm0 and xmm1 if they are. Jump to label not_numbers if |
// either operand is not a number. Operands are in edx and eax. |
// Leaves operands unchanged. |
- static void LoadSse2Operands(MacroAssembler* masm, Label* not_numbers); |
+ static void LoadSSE2Operands(MacroAssembler* masm, Label* not_numbers); |
- // Similar to LoadSse2Operands but assumes that both operands are smis. |
- // Accepts operands in eax, ebx. |
- static void LoadSse2Smis(MacroAssembler* masm, Register scratch); |
+ // Similar to LoadSSE2Operands but assumes that both operands are smis. |
+ // Expects operands in edx, eax. |
+ static void LoadSSE2Smis(MacroAssembler* masm, Register scratch); |
}; |
@@ -7084,91 +7084,180 @@ |
void GenericBinaryOpStub::GenerateSmiCode(MacroAssembler* masm, Label* slow) { |
- if (HasArgsInRegisters()) { |
- __ mov(ebx, eax); |
- __ mov(eax, edx); |
- } else { |
- __ mov(ebx, Operand(esp, 1 * kPointerSize)); |
- __ mov(eax, Operand(esp, 2 * kPointerSize)); |
+ // 1. Move arguments into edx, eax except for DIV and MOD, which need the |
+ // dividend in eax and edx free for the division. Use eax, ebx for those. |
+ Comment load_comment(masm, "-- Load arguments"); |
+ Register left = edx; |
+ Register right = eax; |
+ if (op_ == Token::DIV || op_ == Token::MOD) { |
+ left = eax; |
+ right = ebx; |
+ if (HasArgsInRegisters()) { |
+ __ mov(ebx, eax); |
+ __ mov(eax, edx); |
+ } |
} |
+ if (!HasArgsInRegisters()) { |
+ __ mov(right, Operand(esp, 1 * kPointerSize)); |
+ __ mov(left, Operand(esp, 2 * kPointerSize)); |
+ } |
- Label not_smis, not_smis_or_overflow, not_smis_undo_optimistic; |
- Label use_fp_on_smis, done; |
+ // 2. Prepare the smi check of both operands by oring them together. |
+ Comment smi_check_comment(masm, "-- Smi check arguments"); |
+ Label not_smis; |
+ Register combined = ecx; |
+ ASSERT(!left.is(combined) && !right.is(combined)); |
+ switch (op_) { |
+ case Token::BIT_OR: |
+ // Perform the operation into eax and smi check the result. Preserve |
+ // eax in case the result is not a smi. |
+ ASSERT(!left.is(ecx) && !right.is(ecx)); |
+ __ mov(ecx, right); |
+ __ or_(right, Operand(left)); // Bitwise or is commutative. |
+ combined = right; |
+ break; |
- // Perform fast-case smi code for the operation (eax <op> ebx) and |
- // leave result in register eax. |
+ case Token::BIT_XOR: |
+ case Token::BIT_AND: |
+ case Token::ADD: |
+ case Token::SUB: |
+ case Token::MUL: |
+ case Token::DIV: |
+ case Token::MOD: |
+ __ mov(combined, right); |
+ __ or_(combined, Operand(left)); |
+ break; |
- // Prepare the smi check of both operands by or'ing them together |
- // before checking against the smi mask. |
- __ mov(ecx, Operand(ebx)); |
- __ or_(ecx, Operand(eax)); |
+ case Token::SHL: |
+ case Token::SAR: |
+ case Token::SHR: |
+ // Move the right operand into ecx for the shift operation, use eax |
+ // for the smi check register. |
+ ASSERT(!left.is(ecx) && !right.is(ecx)); |
+ __ mov(ecx, right); |
+ __ or_(right, Operand(left)); |
+ combined = right; |
+ break; |
+ default: |
+ break; |
+ } |
+ |
+ // 3. Perform the smi check of the operands. |
+ ASSERT(kSmiTag == 0); // Adjust zero check if not the case. |
+ __ test(combined, Immediate(kSmiTagMask)); |
+ __ j(not_zero, ¬_smis, not_taken); |
+ |
+ // 4. Operands are both smis, perform the operation leaving the result in |
+ // eax and check the result if necessary. |
+ Comment perform_smi(masm, "-- Perform smi operation"); |
+ Label use_fp_on_smis; |
switch (op_) { |
- case Token::ADD: |
- __ add(eax, Operand(ebx)); // add optimistically |
- __ j(overflow, ¬_smis_or_overflow, not_taken); |
+ case Token::BIT_OR: |
+ // Nothing to do. |
break; |
- case Token::SUB: |
- __ sub(eax, Operand(ebx)); // subtract optimistically |
- __ j(overflow, ¬_smis_or_overflow, not_taken); |
+ case Token::BIT_XOR: |
+ ASSERT(right.is(eax)); |
+ __ xor_(right, Operand(left)); // Bitwise xor is commutative. |
break; |
- case Token::MUL: |
- __ mov(edi, eax); // Backup the left operand. |
+ case Token::BIT_AND: |
+ ASSERT(right.is(eax)); |
+ __ and_(right, Operand(left)); // Bitwise and is commutative. |
break; |
- case Token::DIV: |
- __ mov(edi, eax); // Backup the left operand. |
- // Fall through. |
- case Token::MOD: |
- // Sign extend eax into edx:eax. |
- __ cdq(); |
- // Check for 0 divisor. |
- __ test(ebx, Operand(ebx)); |
- __ j(zero, ¬_smis_or_overflow, not_taken); |
+ case Token::SHL: |
+ // Remove tags from operands (but keep sign). |
+ __ SmiUntag(left); |
+ __ SmiUntag(ecx); |
+ // Perform the operation. |
+ __ shl_cl(left); |
+ // Check that the *signed* result fits in a smi. |
+ __ cmp(left, 0xc0000000); |
+ __ j(sign, &use_fp_on_smis, not_taken); |
+ // Tag the result and store it in register eax. |
+ __ SmiTag(left); |
+ __ mov(eax, left); |
break; |
- default: |
- // Fall-through to smi check. |
+ case Token::SAR: |
+ // Remove tags from operands (but keep sign). |
+ __ SmiUntag(left); |
+ __ SmiUntag(ecx); |
+ // Perform the operation. |
+ __ sar_cl(left); |
+ // Tag the result and store it in register eax. |
+ __ SmiTag(left); |
+ __ mov(eax, left); |
break; |
- } |
- // Perform the actual smi check. |
- ASSERT(kSmiTag == 0); // adjust zero check if not the case |
- __ test(ecx, Immediate(kSmiTagMask)); |
- __ j(not_zero, ¬_smis_undo_optimistic, not_taken); |
+ case Token::SHR: |
+ // Remove tags from operands (but keep sign). |
+ __ SmiUntag(left); |
+ __ SmiUntag(ecx); |
+ // Perform the operation. |
+ __ shr_cl(left); |
+ // 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. |
+ __ test(left, Immediate(0xc0000000)); |
+ __ j(not_zero, slow, not_taken); |
+ // Tag the result and store it in register eax. |
+ __ SmiTag(left); |
+ __ mov(eax, left); |
+ break; |
- switch (op_) { |
case Token::ADD: |
+ ASSERT(right.is(eax)); |
+ __ add(right, Operand(left)); // Addition is commutative. |
+ __ j(overflow, &use_fp_on_smis, not_taken); |
+ break; |
+ |
case Token::SUB: |
- // Do nothing here. |
+ __ sub(left, Operand(right)); |
+ __ j(overflow, &use_fp_on_smis, not_taken); |
+ __ mov(eax, left); |
break; |
case Token::MUL: |
// If the smi tag is 0 we can just leave the tag on one operand. |
- ASSERT(kSmiTag == 0); // adjust code below if not the case |
+ ASSERT(kSmiTag == 0); // Adjust code below if not the case. |
+ // We can't revert the multiplication if the result is not a smi |
+ // so save the right operand. |
+ __ mov(ebx, right); |
// Remove tag from one of the operands (but keep sign). |
- __ SmiUntag(eax); |
+ __ SmiUntag(right); |
// Do multiplication. |
- __ imul(eax, Operand(ebx)); // multiplication of smis; result in eax |
- // Go slow on overflows. |
+ __ imul(right, Operand(left)); // Multiplication is commutative. |
__ j(overflow, &use_fp_on_smis, not_taken); |
- // Check for negative zero result. |
- __ NegativeZeroTest(eax, ecx, &use_fp_on_smis); // use ecx = x | y |
+ // Check for negative zero result. Use combined = left | right. |
+ __ NegativeZeroTest(right, combined, &use_fp_on_smis); |
break; |
case Token::DIV: |
- // Divide edx:eax by ebx. |
- __ idiv(ebx); |
- // Check for the corner case of dividing the most negative smi |
- // by -1. We cannot use the overflow flag, since it is not set |
- // by idiv instruction. |
+ // We can't revert the division if the result is not a smi so |
+ // save the left operand. |
+ __ mov(edi, left); |
+ // Check for 0 divisor. |
+ __ test(right, Operand(right)); |
+ __ j(zero, &use_fp_on_smis, not_taken); |
+ // Sign extend left into edx:eax. |
+ ASSERT(left.is(eax)); |
+ __ cdq(); |
+ // Divide edx:eax by right. |
+ __ idiv(right); |
+ // Check for the corner case of dividing the most negative smi by |
+ // -1. We cannot use the overflow flag, since it is not set by idiv |
+ // instruction. |
ASSERT(kSmiTag == 0 && kSmiTagSize == 1); |
__ cmp(eax, 0x40000000); |
__ j(equal, &use_fp_on_smis); |
- // Check for negative zero result. |
- __ NegativeZeroTest(eax, ecx, &use_fp_on_smis); // use ecx = x | y |
+ // Check for negative zero result. Use combined = left | right. |
+ __ NegativeZeroTest(eax, combined, &use_fp_on_smis); |
// Check that the remainder is zero. |
__ test(edx, Operand(edx)); |
__ j(not_zero, &use_fp_on_smis); |
@@ -7177,97 +7266,87 @@ |
break; |
case Token::MOD: |
- // Divide edx:eax by ebx. |
- __ idiv(ebx); |
- // Check for negative zero result. |
- __ NegativeZeroTest(edx, ecx, slow); // use ecx = x | y |
+ // Check for 0 divisor. |
+ __ test(right, Operand(right)); |
+ __ j(zero, ¬_smis, not_taken); |
+ |
+ // Sign extend left into edx:eax. |
+ ASSERT(left.is(eax)); |
+ __ cdq(); |
+ // Divide edx:eax by right. |
+ __ idiv(right); |
+ // Check for negative zero result. Use combined = left | right. |
+ __ NegativeZeroTest(edx, combined, slow); |
// Move remainder to register eax. |
- __ mov(eax, Operand(edx)); |
+ __ mov(eax, edx); |
break; |
- case Token::BIT_OR: |
- __ or_(eax, Operand(ebx)); |
- break; |
+ default: |
+ UNREACHABLE(); |
+ } |
- case Token::BIT_AND: |
- __ and_(eax, Operand(ebx)); |
- break; |
+ // 5. Emit return of result in eax. |
+ GenerateReturn(masm); |
- case Token::BIT_XOR: |
- __ xor_(eax, Operand(ebx)); |
+ // 6. For some operations emit inline code to perform floating point |
+ // operations on known smis (e.g., if the result of the operation |
+ // overflowed the smi range). |
+ switch (op_) { |
+ case Token::SHL: { |
+ Comment perform_float(masm, "-- Perform float operation on smis"); |
+ __ bind(&use_fp_on_smis); |
+ // Result we want is in left == edx, so we can put the allocated heap |
+ // number in eax. |
+ __ AllocateHeapNumber(eax, ecx, ebx, slow); |
+ // Store the result in the HeapNumber and return. |
+ if (CpuFeatures::IsSupported(SSE2)) { |
+ CpuFeatures::Scope use_sse2(SSE2); |
+ __ cvtsi2sd(xmm0, Operand(left)); |
+ __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0); |
+ } else { |
+ // It's OK to overwrite the right argument on the stack because we |
+ // are about to return. |
+ __ mov(Operand(esp, 1 * kPointerSize), left); |
+ __ fild_s(Operand(esp, 1 * kPointerSize)); |
+ __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset)); |
+ } |
+ GenerateReturn(masm); |
break; |
+ } |
- case Token::SHL: |
- case Token::SHR: |
- case Token::SAR: |
- // Move the second operand into register ecx. |
- __ mov(ecx, Operand(ebx)); |
- // Remove tags from operands (but keep sign). |
- __ SmiUntag(eax); |
- __ SmiUntag(ecx); |
- // Perform the operation. |
+ case Token::ADD: |
+ case Token::SUB: |
+ case Token::MUL: |
+ case Token::DIV: { |
+ Comment perform_float(masm, "-- Perform float operation on smis"); |
+ __ bind(&use_fp_on_smis); |
+ // Restore arguments to edx, eax. |
switch (op_) { |
- case Token::SAR: |
- __ sar_cl(eax); |
- // No checks of result necessary |
+ case Token::ADD: |
+ // Revert right = right + left. |
+ __ sub(right, Operand(left)); |
break; |
- case Token::SHR: |
- __ shr_cl(eax); |
- // 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. |
- __ test(eax, Immediate(0xc0000000)); |
- __ j(not_zero, slow, not_taken); |
+ case Token::SUB: |
+ // Revert left = left - right. |
+ __ add(left, Operand(right)); |
break; |
- case Token::SHL: |
- __ shl_cl(eax); |
- // Check that the *signed* result fits in a smi. |
- __ cmp(eax, 0xc0000000); |
- __ j(sign, &use_fp_on_smis, not_taken); |
+ case Token::MUL: |
+ // Right was clobbered but a copy is in ebx. |
+ __ mov(right, ebx); |
break; |
- default: |
- UNREACHABLE(); |
+ case Token::DIV: |
+ // Left was clobbered but a copy is in edi. Right is in ebx for |
+ // division. |
+ __ mov(edx, edi); |
+ __ mov(eax, right); |
+ break; |
+ default: UNREACHABLE(); |
+ break; |
} |
- // Tag the result and store it in register eax. |
- __ SmiTag(eax); |
- break; |
- |
- default: |
- UNREACHABLE(); |
- break; |
- } |
- GenerateReturn(masm); |
- |
- __ bind(¬_smis_or_overflow); |
- // Revert optimistic operation. |
- switch (op_) { |
- case Token::ADD: __ sub(eax, Operand(ebx)); break; |
- case Token::SUB: __ add(eax, Operand(ebx)); break; |
- default: break; |
- } |
- ASSERT(kSmiTag == 0); // Adjust zero check if not the case. |
- __ test(ecx, Immediate(kSmiTagMask)); |
- __ j(not_zero, ¬_smis, not_taken); |
- // Correct operand values are in eax, ebx at this point. |
- |
- __ bind(&use_fp_on_smis); |
- // Both operands are known to be SMIs but the result does not fit into a SMI. |
- switch (op_) { |
- case Token::MUL: |
- case Token::DIV: |
- __ mov(eax, edi); // Restore the left operand. |
- // Fall through. |
- case Token::ADD: |
- case Token::SUB: { |
- Label after_alloc_failure; |
- __ AllocateHeapNumber(edx, ecx, no_reg, &after_alloc_failure); |
- |
+ __ AllocateHeapNumber(ecx, ebx, no_reg, slow); |
if (CpuFeatures::IsSupported(SSE2)) { |
CpuFeatures::Scope use_sse2(SSE2); |
- FloatingPointHelper::LoadSse2Smis(masm, ecx); |
+ FloatingPointHelper::LoadSSE2Smis(masm, ebx); |
switch (op_) { |
case Token::ADD: __ addsd(xmm0, xmm1); break; |
case Token::SUB: __ subsd(xmm0, xmm1); break; |
@@ -7275,9 +7354,9 @@ |
case Token::DIV: __ divsd(xmm0, xmm1); break; |
default: UNREACHABLE(); |
} |
- __ movdbl(FieldOperand(edx, HeapNumber::kValueOffset), xmm0); |
+ __ movdbl(FieldOperand(ecx, HeapNumber::kValueOffset), xmm0); |
} else { // SSE2 not available, use FPU. |
- FloatingPointHelper::LoadFloatSmis(masm, ecx); |
+ FloatingPointHelper::LoadFloatSmis(masm, ebx); |
switch (op_) { |
case Token::ADD: __ faddp(1); break; |
case Token::SUB: __ fsubp(1); break; |
@@ -7285,62 +7364,41 @@ |
case Token::DIV: __ fdivp(1); break; |
default: UNREACHABLE(); |
} |
- __ fstp_d(FieldOperand(edx, HeapNumber::kValueOffset)); |
+ __ fstp_d(FieldOperand(ecx, HeapNumber::kValueOffset)); |
} |
- __ mov(eax, edx); |
+ __ mov(eax, ecx); |
GenerateReturn(masm); |
- |
- __ bind(&after_alloc_failure); |
- __ mov(edx, eax); |
- __ mov(eax, ebx); |
- __ jmp(slow); |
break; |
} |
+ default: |
+ break; |
+ } |
+ |
+ // 7. Non-smi operands, fall out to the non-smi code with the operands in |
+ // edx and eax. |
+ Comment done_comment(masm, "-- Enter non-smi code"); |
+ __ bind(¬_smis); |
+ switch (op_) { |
case Token::BIT_OR: |
- case Token::BIT_AND: |
- case Token::BIT_XOR: |
+ case Token::SHL: |
case Token::SAR: |
- // Do nothing here as these operations always succeed on a pair of smis. |
+ case Token::SHR: |
+ // Right operand is saved in ecx and eax was destroyed by the smi |
+ // check. |
+ __ mov(eax, ecx); |
break; |
+ case Token::DIV: |
case Token::MOD: |
- case Token::SHR: |
- // Do nothing here as these go directly to runtime. |
+ // Operands are in eax, ebx at this point. |
+ __ mov(edx, eax); |
+ __ mov(eax, ebx); |
break; |
- case Token::SHL: { |
- __ AllocateHeapNumber(ebx, ecx, edx, slow); |
- // Store the result in the HeapNumber and return. |
- if (CpuFeatures::IsSupported(SSE2)) { |
- CpuFeatures::Scope use_sse2(SSE2); |
- __ cvtsi2sd(xmm0, Operand(eax)); |
- __ movdbl(FieldOperand(ebx, HeapNumber::kValueOffset), xmm0); |
- } else { |
- __ mov(Operand(esp, 1 * kPointerSize), eax); |
- __ fild_s(Operand(esp, 1 * kPointerSize)); |
- __ fstp_d(FieldOperand(ebx, HeapNumber::kValueOffset)); |
- } |
- __ mov(eax, ebx); |
- GenerateReturn(masm); |
+ default: |
break; |
- } |
- |
- default: UNREACHABLE(); break; |
} |
- |
- __ bind(¬_smis_undo_optimistic); |
- switch (op_) { |
- case Token::ADD: __ sub(eax, Operand(ebx)); break; |
- case Token::SUB: __ add(eax, Operand(ebx)); break; |
- default: break; |
- } |
- |
- __ bind(¬_smis); |
- __ mov(edx, eax); |
- __ mov(eax, ebx); |
- |
- __ bind(&done); |
} |
@@ -7366,7 +7424,7 @@ |
case Token::DIV: { |
if (CpuFeatures::IsSupported(SSE2)) { |
CpuFeatures::Scope use_sse2(SSE2); |
- FloatingPointHelper::LoadSse2Operands(masm, &call_runtime); |
+ FloatingPointHelper::LoadSSE2Operands(masm, &call_runtime); |
switch (op_) { |
case Token::ADD: __ addsd(xmm0, xmm1); break; |
@@ -7849,7 +7907,7 @@ |
} |
-void FloatingPointHelper::LoadSse2Operands(MacroAssembler* masm, |
+void FloatingPointHelper::LoadSSE2Operands(MacroAssembler* masm, |
Label* not_numbers) { |
Label load_smi_edx, load_eax, load_smi_eax, load_float_eax, done; |
// Load operand in edx into xmm0, or branch to not_numbers. |
@@ -7881,14 +7939,17 @@ |
} |
-void FloatingPointHelper::LoadSse2Smis(MacroAssembler* masm, |
+void FloatingPointHelper::LoadSSE2Smis(MacroAssembler* masm, |
Register scratch) { |
- __ mov(scratch, eax); |
- __ SmiUntag(scratch); // Untag smi before converting to float. |
+ const Register left = edx; |
+ const Register right = eax; |
+ __ mov(scratch, left); |
+ ASSERT(!scratch.is(right)); // We're about to clobber scratch. |
+ __ SmiUntag(scratch); |
__ cvtsi2sd(xmm0, Operand(scratch)); |
- __ mov(scratch, ebx); |
- __ SmiUntag(scratch); // Untag smi before converting to float. |
+ __ mov(scratch, right); |
+ __ SmiUntag(scratch); |
__ cvtsi2sd(xmm1, Operand(scratch)); |
} |
@@ -7936,15 +7997,17 @@ |
void FloatingPointHelper::LoadFloatSmis(MacroAssembler* masm, |
Register scratch) { |
- __ mov(scratch, eax); |
+ const Register left = edx; |
+ const Register right = eax; |
+ __ mov(scratch, left); |
+ ASSERT(!scratch.is(right)); // We're about to clobber scratch. |
__ SmiUntag(scratch); |
__ push(scratch); |
__ fild_s(Operand(esp, 0)); |
- __ pop(scratch); |
- __ mov(scratch, ebx); |
+ __ mov(scratch, right); |
__ SmiUntag(scratch); |
- __ push(scratch); |
+ __ mov(Operand(esp, 0), scratch); |
__ fild_s(Operand(esp, 0)); |
__ pop(scratch); |
} |
@@ -8743,7 +8806,7 @@ |
CpuFeatures::Scope use_sse2(SSE2); |
CpuFeatures::Scope use_cmov(CMOV); |
- FloatingPointHelper::LoadSse2Operands(masm, &check_for_symbols); |
+ FloatingPointHelper::LoadSSE2Operands(masm, &check_for_symbols); |
__ comisd(xmm0, xmm1); |
// Jump to builtin for NaN. |