Index: src/ia32/codegen-ia32.cc |
=================================================================== |
--- src/ia32/codegen-ia32.cc (revision 2087) |
+++ src/ia32/codegen-ia32.cc (working copy) |
@@ -776,27 +776,20 @@ |
} |
-// A deferred code class implementing binary operations on likely smis. |
-// This class generates both inline code and deferred code. |
-// The fastest path is implemented inline. Deferred code calls |
-// the GenericBinaryOpStub stub for slow cases. |
+// Call the specialized stub for a binary operation. |
class DeferredInlineBinaryOperation: public DeferredCode { |
public: |
DeferredInlineBinaryOperation(Token::Value op, |
- OverwriteMode mode, |
- GenericBinaryFlags flags) |
- : stub_(op, mode, flags), op_(op) { |
+ OverwriteMode mode) |
+ : op_(op), mode_(mode) { |
set_comment("[ DeferredInlineBinaryOperation"); |
} |
- // Consumes its arguments, left and right, leaving them invalid. |
- Result GenerateInlineCode(Result* left, Result* right); |
- |
virtual void Generate(); |
private: |
- GenericBinaryOpStub stub_; |
Token::Value op_; |
+ OverwriteMode mode_; |
}; |
@@ -806,7 +799,8 @@ |
enter()->Bind(&left, &right); |
cgen()->frame()->Push(&left); |
cgen()->frame()->Push(&right); |
- Result answer = cgen()->frame()->CallStub(&stub_, 2); |
+ GenericBinaryOpStub stub(op_, mode_, SMI_CODE_INLINED); |
+ Result answer = cgen()->frame()->CallStub(&stub, 2); |
exit_.Jump(&answer); |
} |
@@ -1007,13 +1001,343 @@ |
Result* left, |
Result* right, |
OverwriteMode overwrite_mode) { |
- // Implements a binary operation using a deferred code object |
- // and some inline code to operate on smis quickly. |
+ // Implements a binary operation using a deferred code object and |
+ // some inline code to operate on smis quickly. |
DeferredInlineBinaryOperation* deferred = |
- new DeferredInlineBinaryOperation(op, overwrite_mode, SMI_CODE_INLINED); |
- // Generate the inline code that handles some smi operations, |
- // and jumps to the deferred code for everything else. |
- Result answer = deferred->GenerateInlineCode(left, right); |
+ new DeferredInlineBinaryOperation(op, overwrite_mode); |
+ |
+ // Special handling of div and mod because they use fixed registers. |
+ if (op == Token::DIV || op == Token::MOD) { |
+ // We need eax as the quotient register, edx as the remainder |
+ // register, neither left nor right in eax or edx, and left copied |
+ // to eax. |
+ Result quotient; |
+ Result remainder; |
+ bool left_is_in_eax = false; |
+ // Step 1: get eax for quotient. |
+ if ((left->is_register() && left->reg().is(eax)) || |
+ (right->is_register() && right->reg().is(eax))) { |
+ // One or both is in eax. Use a fresh non-edx register for |
+ // them. |
+ Result fresh = allocator_->Allocate(); |
+ ASSERT(fresh.is_valid()); |
+ if (fresh.reg().is(edx)) { |
+ remainder = fresh; |
+ fresh = allocator_->Allocate(); |
+ ASSERT(fresh.is_valid()); |
+ } |
+ if (left->is_register() && left->reg().is(eax)) { |
+ quotient = *left; |
+ *left = fresh; |
+ left_is_in_eax = true; |
+ } |
+ if (right->is_register() && right->reg().is(eax)) { |
+ quotient = *right; |
+ *right = fresh; |
+ } |
+ __ mov(fresh.reg(), eax); |
+ } else { |
+ // Neither left nor right is in eax. |
+ quotient = allocator_->Allocate(eax); |
+ } |
+ ASSERT(quotient.is_register() && quotient.reg().is(eax)); |
+ ASSERT(!(left->is_register() && left->reg().is(eax))); |
+ ASSERT(!(right->is_register() && right->reg().is(eax))); |
+ |
+ // Step 2: get edx for remainder if necessary. |
+ if (!remainder.is_valid()) { |
+ if ((left->is_register() && left->reg().is(edx)) || |
+ (right->is_register() && right->reg().is(edx))) { |
+ Result fresh = allocator_->Allocate(); |
+ ASSERT(fresh.is_valid()); |
+ if (left->is_register() && left->reg().is(edx)) { |
+ remainder = *left; |
+ *left = fresh; |
+ } |
+ if (right->is_register() && right->reg().is(edx)) { |
+ remainder = *right; |
+ *right = fresh; |
+ } |
+ __ mov(fresh.reg(), edx); |
+ } else { |
+ // Neither left nor right is in edx. |
+ remainder = allocator_->Allocate(edx); |
+ } |
+ } |
+ ASSERT(remainder.is_register() && remainder.reg().is(edx)); |
+ ASSERT(!(left->is_register() && left->reg().is(edx))); |
+ ASSERT(!(right->is_register() && right->reg().is(edx))); |
+ |
+ left->ToRegister(); |
+ right->ToRegister(); |
+ frame_->Spill(quotient.reg()); |
+ frame_->Spill(remainder.reg()); |
+ |
+ // Check that left and right are smi tagged. |
+ if (left->reg().is(right->reg())) { |
+ __ test(left->reg(), Immediate(kSmiTagMask)); |
+ } else { |
+ // Use the quotient register as a scratch for the tag check. |
+ if (!left_is_in_eax) __ mov(quotient.reg(), left->reg()); |
+ left_is_in_eax = false; |
+ __ or_(quotient.reg(), Operand(right->reg())); |
+ ASSERT(kSmiTag == 0); // Adjust test if not the case. |
+ __ test(quotient.reg(), Immediate(kSmiTagMask)); |
+ } |
+ deferred->SetEntryFrame(left, right); |
+ deferred->enter()->Branch(not_zero, left, right); |
+ |
+ if (!left_is_in_eax) __ mov(quotient.reg(), left->reg()); |
+ |
+ // Sign extend eax into edx:eax. |
+ __ cdq(); |
+ // Check for 0 divisor. |
+ __ test(right->reg(), Operand(right->reg())); |
+ deferred->enter()->Branch(zero, left, right); |
+ // Divide edx:eax by the right operand. |
+ __ idiv(right->reg()); |
+ |
+ // Complete the operation. |
+ if (op == Token::DIV) { |
+ // Check for negative zero result. If result is zero, and divisor |
+ // is negative, return a floating point negative zero. The |
+ // virtual frame is unchanged in this block, so local control flow |
+ // can use a Label rather than a JumpTarget. |
+ Label non_zero_result; |
+ __ test(left->reg(), Operand(left->reg())); |
+ __ j(not_zero, &non_zero_result); |
+ __ test(right->reg(), Operand(right->reg())); |
+ deferred->enter()->Branch(negative, left, right); |
+ __ bind(&non_zero_result); |
+ // 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(quotient.reg(), 0x40000000); |
+ deferred->enter()->Branch(equal, left, right); |
+ // Check that the remainder is zero. |
+ __ test(remainder.reg(), Operand(remainder.reg())); |
+ remainder.Unuse(); |
+ deferred->enter()->Branch(not_zero, left, right); |
+ left->Unuse(); |
+ right->Unuse(); |
+ // Tag the result and store it in the quotient register. |
+ ASSERT(kSmiTagSize == times_2); // adjust code if not the case |
+ __ lea(quotient.reg(), |
+ Operand(quotient.reg(), quotient.reg(), times_1, kSmiTag)); |
+ deferred->BindExit("ient); |
+ frame_->Push("ient); |
+ } else { |
+ ASSERT(op == Token::MOD); |
+ quotient.Unuse(); |
+ // Check for a negative zero result. If the result is zero, and |
+ // the dividend is negative, return a floating point negative |
+ // zero. The frame is unchanged in this block, so local control |
+ // flow can use a Label rather than a JumpTarget. |
+ Label non_zero_result; |
+ __ test(remainder.reg(), Operand(remainder.reg())); |
+ __ j(not_zero, &non_zero_result, taken); |
+ __ test(left->reg(), Operand(left->reg())); |
+ deferred->enter()->Branch(negative, left, right); |
+ left->Unuse(); |
+ right->Unuse(); |
+ __ bind(&non_zero_result); |
+ deferred->BindExit(&remainder); |
+ frame_->Push(&remainder); |
+ } |
+ return; |
+ } |
+ |
+ // Handle the other binary operations. |
+ left->ToRegister(); |
+ right->ToRegister(); |
+ // A newly allocated register answer is used to hold the answer. The |
+ // registers containing left and right are not modified in most cases, |
+ // so they usually don't need to be spilled in the fast case. |
+ Result answer = allocator_->Allocate(); |
+ |
+ ASSERT(answer.is_valid()); |
+ // Perform the smi tag check. |
+ if (left->reg().is(right->reg())) { |
+ __ test(left->reg(), Immediate(kSmiTagMask)); |
+ } else { |
+ __ mov(answer.reg(), left->reg()); |
+ __ or_(answer.reg(), Operand(right->reg())); |
+ ASSERT(kSmiTag == 0); // Adjust test if not the case. |
+ __ test(answer.reg(), Immediate(kSmiTagMask)); |
+ } |
+ switch (op) { |
+ case Token::ADD: |
+ deferred->SetEntryFrame(left, right); |
+ deferred->enter()->Branch(not_zero, left, right, not_taken); |
+ __ mov(answer.reg(), left->reg()); |
+ __ add(answer.reg(), Operand(right->reg())); // Add optimistically. |
+ deferred->enter()->Branch(overflow, left, right, not_taken); |
+ break; |
+ |
+ case Token::SUB: |
+ deferred->SetEntryFrame(left, right); |
+ deferred->enter()->Branch(not_zero, left, right, not_taken); |
+ __ mov(answer.reg(), left->reg()); |
+ __ sub(answer.reg(), Operand(right->reg())); // Subtract optimistically. |
+ deferred->enter()->Branch(overflow, left, right, not_taken); |
+ break; |
+ |
+ case Token::MUL: { |
+ deferred->SetEntryFrame(left, right); |
+ deferred->enter()->Branch(not_zero, left, right, not_taken); |
+ __ mov(answer.reg(), left->reg()); |
+ // 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. |
+ // Remove smi tag from the left operand (but keep sign). |
+ // Left-hand operand has been copied into answer. |
+ __ sar(answer.reg(), kSmiTagSize); |
+ // Do multiplication of smis, leaving result in answer. |
+ __ imul(answer.reg(), Operand(right->reg())); |
+ // Go slow on overflows. |
+ deferred->enter()->Branch(overflow, left, right, not_taken); |
+ // Check for negative zero result. If product is zero, and one |
+ // argument is negative, go to slow case. The frame is unchanged |
+ // in this block, so local control flow can use a Label rather |
+ // than a JumpTarget. |
+ Label non_zero_result; |
+ __ test(answer.reg(), Operand(answer.reg())); |
+ __ j(not_zero, &non_zero_result, taken); |
+ __ mov(answer.reg(), left->reg()); |
+ __ or_(answer.reg(), Operand(right->reg())); |
+ deferred->enter()->Branch(negative, left, right, not_taken); |
+ __ xor_(answer.reg(), Operand(answer.reg())); // Positive 0 is correct. |
+ __ bind(&non_zero_result); |
+ break; |
+ } |
+ |
+ case Token::BIT_OR: |
+ deferred->enter()->Branch(not_zero, left, right, not_taken); |
+ __ mov(answer.reg(), left->reg()); |
+ __ or_(answer.reg(), Operand(right->reg())); |
+ break; |
+ |
+ case Token::BIT_AND: |
+ deferred->enter()->Branch(not_zero, left, right, not_taken); |
+ __ mov(answer.reg(), left->reg()); |
+ __ and_(answer.reg(), Operand(right->reg())); |
+ break; |
+ |
+ case Token::BIT_XOR: |
+ deferred->enter()->Branch(not_zero, left, right, not_taken); |
+ __ mov(answer.reg(), left->reg()); |
+ __ xor_(answer.reg(), Operand(right->reg())); |
+ break; |
+ |
+ case Token::SHL: |
+ case Token::SHR: |
+ case Token::SAR: |
+ deferred->enter()->Branch(not_zero, left, right, not_taken); |
+ __ mov(answer.reg(), left->reg()); |
+ // Move right into ecx. |
+ // Left is in two registers already, so even if left or answer is ecx, |
+ // we can move right to it, and use the other one. |
+ // Right operand must be in register cl because x86 likes it that way. |
+ if (right->reg().is(ecx)) { |
+ // Right is already in the right place. Left may be in the |
+ // same register, which causes problems. Always use answer |
+ // instead of left, even if left is not ecx, since this avoids |
+ // spilling left. |
+ *left = answer; |
+ } else if (left->reg().is(ecx)) { |
+ frame_->Spill(left->reg()); |
+ __ mov(left->reg(), right->reg()); |
+ *right = *left; |
+ *left = answer; // Use copy of left in answer as left. |
+ } else if (answer.reg().is(ecx)) { |
+ __ mov(answer.reg(), right->reg()); |
+ *right = answer; |
+ } else { |
+ Result reg_ecx = allocator_->Allocate(ecx); |
+ ASSERT(reg_ecx.is_valid()); |
+ __ mov(ecx, right->reg()); |
+ *right = reg_ecx; |
+ // Answer and left both contain the left operand. Use answer, so |
+ // left is not spilled. |
+ *left = answer; |
+ } |
+ ASSERT(left->reg().is_valid()); |
+ ASSERT(!left->reg().is(ecx)); |
+ ASSERT(right->reg().is(ecx)); |
+ answer.Unuse(); // Answer may now be being used for left or right. |
+ // We will modify left and right, which we do not do in any other |
+ // binary operation. The exits to slow code need to restore the |
+ // original values of left and right, or at least values that give |
+ // the same answer. |
+ |
+ // We are modifying left and right. They must be spilled! |
+ frame_->Spill(left->reg()); |
+ frame_->Spill(right->reg()); |
+ |
+ // Remove tags from operands (but keep sign). |
+ __ sar(left->reg(), kSmiTagSize); |
+ __ sar(ecx, kSmiTagSize); |
+ // Perform the operation. |
+ switch (op) { |
+ case Token::SAR: |
+ __ sar(left->reg()); |
+ // No checks of result necessary |
+ break; |
+ case Token::SHR: { |
+ __ shr(left->reg()); |
+ // 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. |
+ // If the answer cannot be represented by a SMI, restore |
+ // the left and right arguments, and jump to slow case. |
+ // The low bit of the left argument may be lost, but only |
+ // in a case where it is dropped anyway. |
+ JumpTarget result_ok; |
+ __ test(left->reg(), Immediate(0xc0000000)); |
+ result_ok.Branch(zero, left, taken); |
+ __ shl(left->reg()); |
+ ASSERT(kSmiTag == 0); |
+ __ shl(left->reg(), kSmiTagSize); |
+ __ shl(right->reg(), kSmiTagSize); |
+ deferred->enter()->Jump(left, right); |
+ result_ok.Bind(left); |
+ break; |
+ } |
+ case Token::SHL: { |
+ __ shl(left->reg()); |
+ // Check that the *signed* result fits in a smi. |
+ JumpTarget result_ok; |
+ __ cmp(left->reg(), 0xc0000000); |
+ result_ok.Branch(positive, left, taken); |
+ |
+ __ shr(left->reg()); |
+ ASSERT(kSmiTag == 0); |
+ __ shl(left->reg(), kSmiTagSize); |
+ __ shl(right->reg(), kSmiTagSize); |
+ deferred->enter()->Jump(left, right); |
+ result_ok.Bind(left); |
+ break; |
+ } |
+ default: |
+ UNREACHABLE(); |
+ } |
+ // Smi-tag the result, in left, and make answer an alias for left-> |
+ answer = *left; |
+ answer.ToRegister(); |
+ ASSERT(kSmiTagSize == times_2); // adjust code if not the case |
+ __ lea(answer.reg(), |
+ Operand(answer.reg(), answer.reg(), times_1, kSmiTag)); |
+ break; |
+ |
+ default: |
+ UNREACHABLE(); |
+ break; |
+ } |
+ left->Unuse(); |
+ right->Unuse(); |
deferred->BindExit(&answer); |
frame_->Push(&answer); |
} |
@@ -5732,341 +6056,6 @@ |
} |
-Result DeferredInlineBinaryOperation::GenerateInlineCode(Result* left, |
- Result* right) { |
- MacroAssembler* masm = cgen()->masm(); |
- |
- // Special handling of div and mod because they use fixed registers. |
- if (op_ == Token::DIV || op_ == Token::MOD) { |
- // We need eax as the quotient register, edx as the remainder |
- // register, neither left nor right in eax or edx, and left copied |
- // to eax. |
- Result quotient; |
- Result remainder; |
- bool left_is_in_eax = false; |
- // Step 1: get eax for quotient. |
- if ((left->is_register() && left->reg().is(eax)) || |
- (right->is_register() && right->reg().is(eax))) { |
- // One or both is in eax. Use a fresh non-edx register for |
- // them. |
- Result fresh = cgen()->allocator()->Allocate(); |
- ASSERT(fresh.is_valid()); |
- if (fresh.reg().is(edx)) { |
- remainder = fresh; |
- fresh = cgen()->allocator()->Allocate(); |
- ASSERT(fresh.is_valid()); |
- } |
- if (left->is_register() && left->reg().is(eax)) { |
- quotient = *left; |
- *left = fresh; |
- left_is_in_eax = true; |
- } |
- if (right->is_register() && right->reg().is(eax)) { |
- quotient = *right; |
- *right = fresh; |
- } |
- __ mov(fresh.reg(), eax); |
- } else { |
- // Neither left nor right is in eax. |
- quotient = cgen()->allocator()->Allocate(eax); |
- } |
- ASSERT(quotient.is_register() && quotient.reg().is(eax)); |
- ASSERT(!(left->is_register() && left->reg().is(eax))); |
- ASSERT(!(right->is_register() && right->reg().is(eax))); |
- |
- // Step 2: get edx for remainder if necessary. |
- if (!remainder.is_valid()) { |
- if ((left->is_register() && left->reg().is(edx)) || |
- (right->is_register() && right->reg().is(edx))) { |
- Result fresh = cgen()->allocator()->Allocate(); |
- ASSERT(fresh.is_valid()); |
- if (left->is_register() && left->reg().is(edx)) { |
- remainder = *left; |
- *left = fresh; |
- } |
- if (right->is_register() && right->reg().is(edx)) { |
- remainder = *right; |
- *right = fresh; |
- } |
- __ mov(fresh.reg(), edx); |
- } else { |
- // Neither left nor right is in edx. |
- remainder = cgen()->allocator()->Allocate(edx); |
- } |
- } |
- ASSERT(remainder.is_register() && remainder.reg().is(edx)); |
- ASSERT(!(left->is_register() && left->reg().is(edx))); |
- ASSERT(!(right->is_register() && right->reg().is(edx))); |
- |
- left->ToRegister(); |
- right->ToRegister(); |
- cgen()->frame()->Spill(quotient.reg()); |
- cgen()->frame()->Spill(remainder.reg()); |
- |
- // Check that left and right are smi tagged. |
- if (left->reg().is(right->reg())) { |
- __ test(left->reg(), Immediate(kSmiTagMask)); |
- } else { |
- // Use the quotient register as a scratch for the tag check. |
- if (!left_is_in_eax) __ mov(quotient.reg(), left->reg()); |
- left_is_in_eax = false; |
- __ or_(quotient.reg(), Operand(right->reg())); |
- ASSERT(kSmiTag == 0); // Adjust test if not the case. |
- __ test(quotient.reg(), Immediate(kSmiTagMask)); |
- } |
- SetEntryFrame(left, right); |
- enter()->Branch(not_zero, left, right); |
- |
- if (!left_is_in_eax) __ mov(quotient.reg(), left->reg()); |
- |
- // Sign extend eax into edx:eax. |
- __ cdq(); |
- // Check for 0 divisor. |
- __ test(right->reg(), Operand(right->reg())); |
- enter()->Branch(zero, left, right); |
- // Divide edx:eax by the right operand. |
- __ idiv(right->reg()); |
- |
- // Complete the operation. |
- if (op_ == Token::DIV) { |
- // Check for negative zero result. If result is zero, and divisor |
- // is negative, return a floating point negative zero. The |
- // virtual frame is unchanged in this block, so local control flow |
- // can use a Label rather than a JumpTarget. |
- Label non_zero_result; |
- __ test(left->reg(), Operand(left->reg())); |
- __ j(not_zero, &non_zero_result); |
- __ test(right->reg(), Operand(right->reg())); |
- enter()->Branch(negative, left, right); |
- __ bind(&non_zero_result); |
- // 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(quotient.reg(), 0x40000000); |
- enter()->Branch(equal, left, right); |
- // Check that the remainder is zero. |
- __ test(remainder.reg(), Operand(remainder.reg())); |
- enter()->Branch(not_zero, left, right); |
- left->Unuse(); |
- right->Unuse(); |
- // Tag the result and store it in the quotient register. |
- ASSERT(kSmiTagSize == times_2); // adjust code if not the case |
- __ lea(quotient.reg(), |
- Operand(quotient.reg(), quotient.reg(), times_1, kSmiTag)); |
- return quotient; |
- } else { |
- ASSERT(op_ == Token::MOD); |
- // Check for a negative zero result. If the result is zero, and |
- // the dividend is negative, return a floating point negative |
- // zero. The frame is unchanged in this block, so local control |
- // flow can use a Label rather than a JumpTarget. |
- Label non_zero_result; |
- __ test(remainder.reg(), Operand(remainder.reg())); |
- __ j(not_zero, &non_zero_result, taken); |
- __ test(left->reg(), Operand(left->reg())); |
- enter()->Branch(negative, left, right); |
- left->Unuse(); |
- right->Unuse(); |
- __ bind(&non_zero_result); |
- return remainder; |
- } |
- } |
- |
- // Handle the other binary operations. |
- left->ToRegister(); |
- right->ToRegister(); |
- // A newly allocated register answer is used to hold the answer. The |
- // registers containing left and right are not modified in most cases, |
- // so they usually don't need to be spilled in the fast case. |
- Result answer = cgen()->allocator()->Allocate(); |
- |
- ASSERT(answer.is_valid()); |
- // Perform the smi tag check. |
- if (left->reg().is(right->reg())) { |
- __ test(left->reg(), Immediate(kSmiTagMask)); |
- } else { |
- __ mov(answer.reg(), left->reg()); |
- __ or_(answer.reg(), Operand(right->reg())); |
- ASSERT(kSmiTag == 0); // Adjust test if not the case. |
- __ test(answer.reg(), Immediate(kSmiTagMask)); |
- } |
- switch (op_) { |
- case Token::ADD: |
- SetEntryFrame(left, right); |
- enter()->Branch(not_zero, left, right, not_taken); |
- __ mov(answer.reg(), left->reg()); |
- __ add(answer.reg(), Operand(right->reg())); // Add optimistically. |
- enter()->Branch(overflow, left, right, not_taken); |
- break; |
- |
- case Token::SUB: |
- SetEntryFrame(left, right); |
- enter()->Branch(not_zero, left, right, not_taken); |
- __ mov(answer.reg(), left->reg()); |
- __ sub(answer.reg(), Operand(right->reg())); // Subtract optimistically. |
- enter()->Branch(overflow, left, right, not_taken); |
- break; |
- |
- case Token::MUL: { |
- SetEntryFrame(left, right); |
- enter()->Branch(not_zero, left, right, not_taken); |
- __ mov(answer.reg(), left->reg()); |
- // 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. |
- // Remove smi tag from the left operand (but keep sign). |
- // Left-hand operand has been copied into answer. |
- __ sar(answer.reg(), kSmiTagSize); |
- // Do multiplication of smis, leaving result in answer. |
- __ imul(answer.reg(), Operand(right->reg())); |
- // Go slow on overflows. |
- enter()->Branch(overflow, left, right, not_taken); |
- // Check for negative zero result. If product is zero, and one |
- // argument is negative, go to slow case. The frame is unchanged |
- // in this block, so local control flow can use a Label rather |
- // than a JumpTarget. |
- Label non_zero_result; |
- __ test(answer.reg(), Operand(answer.reg())); |
- __ j(not_zero, &non_zero_result, taken); |
- __ mov(answer.reg(), left->reg()); |
- __ or_(answer.reg(), Operand(right->reg())); |
- enter()->Branch(negative, left, right, not_taken); |
- __ xor_(answer.reg(), Operand(answer.reg())); // Positive 0 is correct. |
- __ bind(&non_zero_result); |
- break; |
- } |
- |
- case Token::BIT_OR: |
- enter()->Branch(not_zero, left, right, not_taken); |
- __ mov(answer.reg(), left->reg()); |
- __ or_(answer.reg(), Operand(right->reg())); |
- break; |
- |
- case Token::BIT_AND: |
- enter()->Branch(not_zero, left, right, not_taken); |
- __ mov(answer.reg(), left->reg()); |
- __ and_(answer.reg(), Operand(right->reg())); |
- break; |
- |
- case Token::BIT_XOR: |
- enter()->Branch(not_zero, left, right, not_taken); |
- __ mov(answer.reg(), left->reg()); |
- __ xor_(answer.reg(), Operand(right->reg())); |
- break; |
- |
- case Token::SHL: |
- case Token::SHR: |
- case Token::SAR: |
- enter()->Branch(not_zero, left, right, not_taken); |
- __ mov(answer.reg(), left->reg()); |
- // Move right into ecx. |
- // Left is in two registers already, so even if left or answer is ecx, |
- // we can move right to it, and use the other one. |
- // Right operand must be in register cl because x86 likes it that way. |
- if (right->reg().is(ecx)) { |
- // Right is already in the right place. Left may be in the |
- // same register, which causes problems. Always use answer |
- // instead of left, even if left is not ecx, since this avoids |
- // spilling left. |
- *left = answer; |
- } else if (left->reg().is(ecx)) { |
- cgen()->frame()->Spill(left->reg()); |
- __ mov(left->reg(), right->reg()); |
- *right = *left; |
- *left = answer; // Use copy of left in answer as left. |
- } else if (answer.reg().is(ecx)) { |
- __ mov(answer.reg(), right->reg()); |
- *right = answer; |
- } else { |
- Result reg_ecx = cgen()->allocator()->Allocate(ecx); |
- ASSERT(reg_ecx.is_valid()); |
- __ mov(ecx, right->reg()); |
- *right = reg_ecx; |
- // Answer and left both contain the left operand. Use answer, so |
- // left is not spilled. |
- *left = answer; |
- } |
- ASSERT(left->reg().is_valid()); |
- ASSERT(!left->reg().is(ecx)); |
- ASSERT(right->reg().is(ecx)); |
- answer.Unuse(); // Answer may now be being used for left or right. |
- // We will modify left and right, which we do not do in any other |
- // binary operation. The exits to slow code need to restore the |
- // original values of left and right, or at least values that give |
- // the same answer. |
- |
- // We are modifying left and right. They must be spilled! |
- cgen()->frame()->Spill(left->reg()); |
- cgen()->frame()->Spill(right->reg()); |
- |
- // Remove tags from operands (but keep sign). |
- __ sar(left->reg(), kSmiTagSize); |
- __ sar(ecx, kSmiTagSize); |
- // Perform the operation. |
- switch (op_) { |
- case Token::SAR: |
- __ sar(left->reg()); |
- // No checks of result necessary |
- break; |
- case Token::SHR: { |
- __ shr(left->reg()); |
- // 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. |
- // If the answer cannot be represented by a SMI, restore |
- // the left and right arguments, and jump to slow case. |
- // The low bit of the left argument may be lost, but only |
- // in a case where it is dropped anyway. |
- JumpTarget result_ok; |
- __ test(left->reg(), Immediate(0xc0000000)); |
- result_ok.Branch(zero, left, taken); |
- __ shl(left->reg()); |
- ASSERT(kSmiTag == 0); |
- __ shl(left->reg(), kSmiTagSize); |
- __ shl(right->reg(), kSmiTagSize); |
- enter()->Jump(left, right); |
- result_ok.Bind(left); |
- break; |
- } |
- case Token::SHL: { |
- __ shl(left->reg()); |
- // Check that the *signed* result fits in a smi. |
- JumpTarget result_ok; |
- __ cmp(left->reg(), 0xc0000000); |
- result_ok.Branch(positive, left, taken); |
- |
- __ shr(left->reg()); |
- ASSERT(kSmiTag == 0); |
- __ shl(left->reg(), kSmiTagSize); |
- __ shl(right->reg(), kSmiTagSize); |
- enter()->Jump(left, right); |
- result_ok.Bind(left); |
- break; |
- } |
- default: |
- UNREACHABLE(); |
- } |
- // Smi-tag the result, in left, and make answer an alias for left-> |
- answer = *left; |
- answer.ToRegister(); |
- ASSERT(kSmiTagSize == times_2); // adjust code if not the case |
- __ lea(answer.reg(), |
- Operand(answer.reg(), answer.reg(), times_1, kSmiTag)); |
- break; |
- |
- default: |
- UNREACHABLE(); |
- break; |
- } |
- left->Unuse(); |
- right->Unuse(); |
- return answer; |
-} |
- |
- |
void GenericBinaryOpStub::GenerateSmiCode(MacroAssembler* masm, Label* slow) { |
// Perform fast-case smi code for the operation (eax <op> ebx) and |
// leave result in register eax. |