From a41b41bf984534a8934f4a7b814a7eeac58afc16 Mon Sep 17 00:00:00 2001 From: "kmillikin@chromium.org" Date: Tue, 2 Jun 2009 20:11:26 +0000 Subject: [PATCH] As a simplification, manually inline the function DeferredInlineBinaryOperation::GenerateInlineCode and remove its definition. It was only called from one site and was the only deferred code object that was split that way into fast-case inline and slow-case stub. Review URL: http://codereview.chromium.org/119037 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@2090 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/ia32/codegen-ia32.cc | 695 +++++++++++++++++++-------------------- 1 file changed, 342 insertions(+), 353 deletions(-) diff --git a/src/ia32/codegen-ia32.cc b/src/ia32/codegen-ia32.cc index 089e94a5bc..33521ea755 100644 --- a/src/ia32/codegen-ia32.cc +++ b/src/ia32/codegen-ia32.cc @@ -776,27 +776,20 @@ const char* GenericBinaryOpStub::GetName() { } -// 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 @@ void DeferredInlineBinaryOperation::Generate() { 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 @@ void CodeGenerator::LikelySmiBinaryOperation(Token::Value op, 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 @@ void ToBooleanStub::Generate(MacroAssembler* masm) { } -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 ebx) and // leave result in register eax.