Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(36)

Unified Diff: src/codegen-ia32.cc

Issue 17380: Change inlined smi binary operations to use an unspilled frame. (Closed) Base URL: http://v8.googlecode.com/svn/branches/experimental/toiger/
Patch Set: '' Created 11 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | src/jump-target.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/codegen-ia32.cc
===================================================================
--- src/codegen-ia32.cc (revision 1083)
+++ src/codegen-ia32.cc (working copy)
@@ -181,8 +181,8 @@
frame_->EmitPush(frame_->Function());
frame_->EmitPush(eax);
frame_->EmitPush(Immediate(Smi::FromInt(scope_->num_parameters())));
- frame_->CallStub(&stub, 3);
- frame_->Push(eax);
+ Result answer = frame_->CallStub(&stub, 3);
+ frame_->Push(&answer);
}
if (scope_->num_heap_slots() > 0) {
@@ -641,11 +641,9 @@
// Call the stub for all other cases.
frame_->Push(&value); // Undo the Pop() from above.
ToBooleanStub stub;
- frame_->CallStub(&stub, 1);
- // Convert the result (eax) to condition code.
- Result temp = allocator_->Allocate(eax);
- ASSERT(temp.is_valid());
- __ test(eax, Operand(eax));
+ Result temp = frame_->CallStub(&stub, 1);
+ // Convert the result to a condition code.
+ __ test(temp.reg(), Operand(temp.reg()));
ASSERT(not_equal == not_zero);
cc_reg_ = not_equal;
@@ -751,21 +749,17 @@
op_(op) {
}
- void GenerateInlineCode();
+ // The binary operation's arguments are on top of the code generator's frame.
+ Result GenerateInlineCode();
virtual void Generate() {
- // The arguments are is actually passed in ebx and the top of the stack.
- enter()->Bind();
- VirtualFrame::SpilledScope spilled_scope(generator());
- generator()->frame()->EmitPush(ebx);
- generator()->frame()->CallStub(&stub_, 2);
- // We must preserve the eax value here, because it will be written
- // to the top-of-stack element when getting back to the fast case
- // code. See comment in GenericBinaryOperation where
- // deferred->exit() is bound.
- generator()->frame()->EmitPush(eax);
- // The result is actually returned on the top of the stack.
- exit()->Jump();
+ Result left(generator());
+ Result right(generator());
+ enter()->Bind(&left, &right);
+ generator()->frame()->Push(&left);
+ generator()->frame()->Push(&right);
+ Result answer = generator()->frame()->CallStub(&stub_, 2);
+ exit()->Jump(&answer);
}
private:
@@ -786,7 +780,6 @@
return;
}
- VirtualFrame::SpilledScope spilled_scope(this);
// Set the flags based on the operation, type and loop nesting level.
GenericBinaryFlags flags;
switch (op) {
@@ -814,28 +807,18 @@
if (flags == SMI_CODE_INLINED) {
// Create a new deferred code for the slow-case part.
- //
- // TODO(): When this code is updated to use the virtual frame, it
- // has to properly flow to the inline code from this deferred code
- // stub.
DeferredInlineBinaryOperation* deferred =
new DeferredInlineBinaryOperation(this, op, overwrite_mode, flags);
- // Fetch the operands from the stack.
- frame_->EmitPop(ebx); // get y
- __ mov(eax, frame_->Top()); // get x
// Generate the inline part of the code.
- deferred->GenerateInlineCode();
- // Put result back on the stack. It seems somewhat weird to let
- // the deferred code jump back before the assignment to the frame
- // top, but this is just to let the peephole optimizer get rid of
- // more code.
- deferred->exit()->Bind();
- __ mov(frame_->Top(), eax);
+ // The operands are on the frame.
+ Result answer = deferred->GenerateInlineCode();
+ deferred->exit()->Bind(&answer);
+ frame_->Push(&answer);
} else {
// Call the stub and push the result to the stack.
GenericBinaryOpStub stub(op, overwrite_mode, flags);
- frame_->CallStub(&stub, 2);
- frame_->EmitPush(eax);
+ Result answer = frame_->CallStub(&stub, 2);
+ frame_->Push(&answer);
}
}
@@ -1428,13 +1411,11 @@
// Use the shared code stub to call the function.
CallFunctionStub call_function(arg_count);
- frame_->CallStub(&call_function, arg_count + 1);
- Result result = allocator_->Allocate(eax);
-
+ Result answer = frame_->CallStub(&call_function, arg_count + 1);
// Restore context and replace function on the stack with the
// result of the stub invocation.
frame_->RestoreContextRegister();
- frame_->SetElementAt(0, &result);
+ frame_->SetElementAt(0, &answer);
}
@@ -4830,153 +4811,262 @@
#undef __
#define __ masm_->
-// This function's implementation is a copy of
-// GenericBinaryOpStub::GenerateSmiCode, with the slow-case label replaced
-// with the deferred code's entry target. The duplicated code is a
-// temporary intermediate stage on the way to using the virtual frame in
-// more places.
-void DeferredInlineBinaryOperation::GenerateInlineCode() {
- // Perform fast-case smi code for the operation (eax <op> ebx) and
- // leave result in register eax.
+Result DeferredInlineBinaryOperation::GenerateInlineCode() {
+ // Perform fast-case smi code for the operation (left <op> right) and
+ // returns the result in a Result.
+ // If any fast-case tests fail, it jumps to the slow-case deferred code,
+ // which calls the binary operation stub, with the arguments (in registers)
+ // on top of the frame.
- // 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));
+ VirtualFrame* frame = generator()->frame();
+ // If operation is division or modulus, ensure
+ // that the special registers needed are free.
+ Result reg_eax(generator()); // Valid only if op is DIV or MOD.
+ Result reg_edx(generator()); // Valid only if op is DIV or MOD.
+ if (op_ == Token::DIV || op_ == Token::MOD) {
+ reg_eax = generator()->allocator()->Allocate(eax);
+ ASSERT(reg_eax.is_valid());
+ reg_edx = generator()->allocator()->Allocate(edx);
+ ASSERT(reg_edx.is_valid());
+ }
+ Result right = frame->Pop();
+ Result left = frame->Pop();
+ left.ToRegister();
+ right.ToRegister();
+ // Answer is used to compute the answer, leaving left and right unchanged.
+ // It is also returned from this function.
+ // It is used as a temporary register in a few places, as well.
+ Result answer(generator());
+ if (reg_eax.is_valid()) {
+ answer = reg_eax;
+ } else {
+ answer = generator()->allocator()->Allocate();
+ }
+ ASSERT(answer.is_valid());
+ // Perform the smi check.
+ __ mov(answer.reg(), Operand(left.reg()));
+ __ or_(answer.reg(), Operand(right.reg()));
+ ASSERT(kSmiTag == 0); // adjust zero check if not the case
+ __ test(answer.reg(), Immediate(kSmiTagMask));
+ enter()->Branch(not_zero, &left, &right, not_taken);
+
+ // All operations start by copying the left argument into answer.
+ __ mov(answer.reg(), Operand(left.reg()));
switch (op_) {
case Token::ADD:
- __ add(eax, Operand(ebx)); // add optimistically
- enter()->Branch(overflow, not_taken);
+ __ add(answer.reg(), Operand(right.reg())); // add optimistically
+ enter()->Branch(overflow, &left, &right, not_taken);
break;
case Token::SUB:
- __ sub(eax, Operand(ebx)); // subtract optimistically
- enter()->Branch(overflow, not_taken);
+ __ sub(answer.reg(), Operand(right.reg())); // subtract optimistically
+ enter()->Branch(overflow, &left, &right, not_taken);
break;
- case Token::DIV:
- case Token::MOD:
- // Sign extend eax into edx:eax.
- __ cdq();
- // Check for 0 divisor.
- __ test(ebx, Operand(ebx));
- enter()->Branch(zero, not_taken);
- break;
- default:
- // Fall-through to smi check.
- break;
- }
-
- // Perform the actual smi check.
- ASSERT(kSmiTag == 0); // adjust zero check if not the case
- __ test(ecx, Immediate(kSmiTagMask));
- enter()->Branch(not_zero, not_taken);
-
- switch (op_) {
- case Token::ADD:
- case Token::SUB:
- // Do nothing here.
- break;
-
- case Token::MUL:
+ 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
- // Remove tag from one of the operands (but keep sign).
- __ sar(eax, kSmiTagSize);
- // Do multiplication.
- __ imul(eax, Operand(ebx)); // multiplication of smis; result in eax
+ // Remove 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, not_taken);
- // Check for negative zero result. Use ecx = x | y.
- __ NegativeZeroTest(generator(), eax, ecx, enter());
+ 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(), Operand(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::DIV:
+ case Token::DIV: {
+ // Left hand argument has been copied into answer, which is eax.
+ // Sign extend eax into edx:eax.
+ __ cdq();
+ // Check for 0 divisor.
+ __ test(right.reg(), Operand(right.reg()));
+ enter()->Branch(zero, &left, &right, not_taken);
// Divide edx:eax by ebx.
- __ idiv(ebx);
+ __ idiv(right.reg());
+ // Check for negative zero result. If result is zero, and divisor
+ // 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(left.reg(), Operand(left.reg()));
+ __ j(not_zero, &non_zero_result, taken);
+ __ test(right.reg(), Operand(right.reg()));
+ enter()->Branch(negative, &left, &right, not_taken);
+ __ 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(eax, 0x40000000);
- enter()->Branch(equal);
- // Check for negative zero result. Use ecx = x | y.
- __ NegativeZeroTest(generator(), eax, ecx, enter());
+ __ cmp(reg_eax.reg(), 0x40000000);
+ enter()->Branch(equal, &left, &right, not_taken);
// Check that the remainder is zero.
- __ test(edx, Operand(edx));
- enter()->Branch(not_zero);
- // Tag the result and store it in register eax.
+ __ test(reg_edx.reg(), Operand(reg_edx.reg()));
+ enter()->Branch(not_zero, &left, &right, not_taken);
+ // Tag the result and store it in register temp.
Kevin Millikin (Chromium) 2009/01/15 15:47:03 the answer register.
ASSERT(kSmiTagSize == times_2); // adjust code if not the case
- __ lea(eax, Operand(eax, eax, times_1, kSmiTag));
+ __ lea(answer.reg(), Operand(eax, eax, times_1, kSmiTag));
break;
+ }
- case Token::MOD:
+ case Token::MOD: {
+ // Left hand argument has been copied into answer, which is eax.
+ // Sign extend eax into edx:eax.
+ __ cdq();
+ // Check for 0 divisor.
+ __ test(right.reg(), Operand(right.reg()));
+ enter()->Branch(zero, &left, &right, not_taken);
+
// Divide edx:eax by ebx.
- __ idiv(ebx);
- // Check for negative zero result. Use ecx = x | y.
- __ NegativeZeroTest(generator(), edx, ecx, enter());
- // Move remainder to register eax.
- __ mov(eax, Operand(edx));
+ __ idiv(right.reg());
+ // Check for negative zero result. If result is zero, and divisor
+ // 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(reg_edx.reg(), Operand(reg_edx.reg()));
+ __ j(not_zero, &non_zero_result, taken);
+ __ test(left.reg(), Operand(left.reg()));
+ enter()->Branch(negative, &left, &right, not_taken);
+ __ bind(&non_zero_result);
+ // The answer is in edx.
+ answer = reg_edx;
break;
+ }
case Token::BIT_OR:
- __ or_(eax, Operand(ebx));
+ __ or_(answer.reg(), Operand(right.reg()));
break;
case Token::BIT_AND:
- __ and_(eax, Operand(ebx));
+ __ and_(answer.reg(), Operand(right.reg()));
break;
case Token::BIT_XOR:
- __ xor_(eax, Operand(ebx));
+ __ xor_(answer.reg(), Operand(right.reg()));
break;
case Token::SHL:
case Token::SHR:
case Token::SAR:
- // Move the second operand into register ecx.
- __ mov(ecx, Operand(ebx));
+ // 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. Use answer instead.
+ if (left.reg().is(ecx)) {
+ left = answer;
+ }
+ } else if (left.reg().is(ecx)) {
+ __ mov(left.reg(), Operand(right.reg()));
+ right = left;
+ left = answer; // Use copy of left in answer as left.
+ } else if (answer.reg().is(ecx)) {
+ __ mov(answer.reg(), Operand(right.reg()));
+ right = answer;
+ } else {
+ Result reg_ecx = generator()->allocator()->Allocate(ecx);
+ ASSERT(reg_ecx.is_valid());
+ __ mov(reg_ecx.reg(), Operand(right.reg()));
+ right = reg_ecx;
+ }
+ 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!
+ generator()->frame()->Spill(left.reg());
+ generator()->frame()->Spill(right.reg());
+
// Remove tags from operands (but keep sign).
- __ sar(eax, kSmiTagSize);
+ __ sar(left.reg(), kSmiTagSize);
__ sar(ecx, kSmiTagSize);
// Perform the operation.
switch (op_) {
case Token::SAR:
- __ sar(eax);
+ __ sar(left.reg());
// No checks of result necessary
break;
- case Token::SHR:
- __ shr(eax);
+ 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.
- __ test(eax, Immediate(0xc0000000));
- enter()->Branch(not_zero, not_taken);
+ // 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(generator());
+ __ test(left.reg(), Immediate(0xc0000000));
+ result_ok.Branch(zero, &left, &right, taken);
+ __ shl(left.reg());
+ ASSERT(kSmiTag == 0);
+ __ shl(left.reg(), kSmiTagSize);
+ __ shl(right.reg(), kSmiTagSize);
+ enter()->Jump(&left, &right);
+ result_ok.Bind(&left, &right);
break;
- case Token::SHL:
- __ shl(eax);
+ }
+ case Token::SHL: {
+ __ shl(left.reg());
// Check that the *signed* result fits in a smi.
- __ lea(ecx, Operand(eax, 0x40000000));
- __ test(ecx, Immediate(0x80000000));
- enter()->Branch(not_zero, not_taken);
+ // TODO(): Can reduce registers from 4 to 3 by preallocating ecx.
+ JumpTarget result_ok(generator());
+ Result smi_test_reg = generator()->allocator()->Allocate();
+ ASSERT(smi_test_reg.is_valid());
+ __ lea(smi_test_reg.reg(), Operand(left.reg(), 0x40000000));
+ __ test(smi_test_reg.reg(), Immediate(0x80000000));
+ smi_test_reg.Unuse();
+ result_ok.Branch(zero, &left, &right, taken);
+ __ shr(left.reg());
+ ASSERT(kSmiTag == 0);
+ __ shl(left.reg(), kSmiTagSize);
+ __ shl(right.reg(), kSmiTagSize);
+ enter()->Jump(&left, &right);
+ result_ok.Bind(&left, &right);
break;
+ }
default:
UNREACHABLE();
}
- // Tag the result and store it in register eax.
+ // 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(eax, Operand(eax, eax, times_1, kSmiTag));
+ __ lea(answer.reg(),
+ Operand(answer.reg(), answer.reg(), times_1, kSmiTag));
break;
default:
UNREACHABLE();
break;
}
+ return answer;
}
« no previous file with comments | « no previous file | src/jump-target.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698