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

Unified Diff: src/codegen-ia32.cc

Issue 42006: Optimize binary operations in which one or both operands is a constant smi. (Closed) Base URL: http://v8.googlecode.com/svn/branches/bleeding_edge/
Patch Set: '' Created 11 years, 9 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 | « src/codegen-ia32.h ('k') | no next file » | 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 1620)
+++ src/codegen-ia32.cc (working copy)
@@ -698,11 +698,13 @@
};
-// Flag that indicates whether or not the code for dealing with smis
-// is inlined or should be dealt with in the stub.
+// Flag that indicates whether or not the code that handles smi arguments
+// should be inlined, placed in the stub, or omitted entirely.
enum GenericBinaryFlags {
SMI_CODE_IN_STUB,
- SMI_CODE_INLINED
+ SMI_CODE_INLINED,
+ // It is known at compile time that at least one argument is not a smi.
+ NO_SMI_CODE
};
@@ -733,15 +735,15 @@
// Minor key encoding in 16 bits FOOOOOOOOOOOOOMM.
class ModeBits: public BitField<OverwriteMode, 0, 2> {};
- class OpBits: public BitField<Token::Value, 2, 13> {};
- class FlagBits: public BitField<GenericBinaryFlags, 15, 1> {};
+ class OpBits: public BitField<Token::Value, 2, 12> {};
+ class FlagBits: public BitField<GenericBinaryFlags, 14, 2> {};
Major MajorKey() { return GenericBinaryOp; }
int MinorKey() {
// Encode the parameters in a unique 16 bit value.
- return OpBits::encode(op_) |
- ModeBits::encode(mode_) |
- FlagBits::encode(flags_);
+ return OpBits::encode(op_)
+ | ModeBits::encode(mode_)
+ | FlagBits::encode(flags_);
}
void Generate(MacroAssembler* masm);
};
@@ -764,6 +766,10 @@
}
+// 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.
class DeferredInlineBinaryOperation: public DeferredCode {
public:
DeferredInlineBinaryOperation(CodeGenerator* generator,
@@ -774,7 +780,8 @@
set_comment("[ DeferredInlineBinaryOperation");
}
- Result GenerateInlineCode();
+ // Consumes its arguments, left and right, leaving them invalid.
+ Result GenerateInlineCode(Result* left, Result* right);
virtual void Generate();
@@ -832,17 +839,44 @@
break;
}
+ Result right = frame_->Pop();
+ Result left = frame_->Pop();
+ bool left_is_smi = left.is_constant() && left.handle()->IsSmi();
+ bool left_is_non_smi = left.is_constant() && !left.handle()->IsSmi();
+ bool right_is_smi = right.is_constant() && right.handle()->IsSmi();
+ bool right_is_non_smi = right.is_constant() && !right.handle()->IsSmi();
+
+ if (left_is_smi && right_is_smi) {
+ // Compute the result, and return that as a constant on the frame.
+ int left_int = Smi::cast(*left.handle())->value();
+ int right_int = Smi::cast(*right.handle())->value();
+ if (FoldConstantSmis(op, left_int, right_int)) return;
+ }
+
+ if (left_is_non_smi || right_is_non_smi) {
+ // Set flag so that we go straight to the slow case, with no smi code.
+ flags = NO_SMI_CODE;
+ } else if (right_is_smi) {
+ ConstantSmiBinaryOperation(op, &left, right.handle(), type,
+ false, overwrite_mode);
+ return;
+ } else if (left_is_smi) {
+ ConstantSmiBinaryOperation(op, &right, left.handle(), type,
+ true, overwrite_mode);
+ return;
+ }
+
if (flags == SMI_CODE_INLINED) {
- // Create a new deferred code for the slow-case part.
- DeferredInlineBinaryOperation* deferred =
- new DeferredInlineBinaryOperation(this, op, overwrite_mode, flags);
- // Generate the inline part of the code.
- // The operands are on the frame.
- Result answer = deferred->GenerateInlineCode();
- deferred->BindExit(&answer);
- frame_->Push(&answer);
+ LikelySmiBinaryOperation(op, &left, &right, overwrite_mode);
} else {
- // Call the stub and push the result to the stack.
+ frame_->Push(&left);
+ frame_->Push(&right);
+ // If we know the arguments aren't smis, use the binary operation stub
+ // that does not check for the fast smi case.
+ // The same stub is used for NO_SMI_CODE and SMI_CODE_INLINED.
+ if (flags == NO_SMI_CODE) {
+ flags = SMI_CODE_INLINED;
+ }
GenericBinaryOpStub stub(op, overwrite_mode, flags);
Result answer = frame_->CallStub(&stub, 2);
frame_->Push(&answer);
@@ -850,6 +884,104 @@
}
+bool CodeGenerator::FoldConstantSmis(Token::Value op, int left, int right) {
+ Object* answer_object = Heap::undefined_value();
+ switch (op) {
+ case Token::ADD:
+ if (Smi::IsValid(left + right)) {
+ answer_object = Smi::FromInt(left + right);
+ }
+ break;
+ case Token::SUB:
+ if (Smi::IsValid(left - right)) {
+ answer_object = Smi::FromInt(left - right);
+ }
+ break;
+ case Token::MUL: {
+ double answer = static_cast<double>(left) * right;
+ if (answer >= Smi::kMinValue && answer <= Smi::kMaxValue) {
+ // If the product is zero and the non-zero factor is negative,
+ // the spec requires us to return floating point negative zero.
+ if (answer != 0 || (left >= 0 && right >= 0)) {
+ answer_object = Smi::FromInt(static_cast<int>(answer));
+ }
+ }
+ }
+ break;
+ case Token::DIV:
+ case Token::MOD:
+ break;
+ case Token::BIT_OR:
+ answer_object = Smi::FromInt(left | right);
+ break;
+ case Token::BIT_AND:
+ answer_object = Smi::FromInt(left & right);
+ break;
+ case Token::BIT_XOR:
+ answer_object = Smi::FromInt(left ^ right);
+ break;
+
+ case Token::SHL: {
+ int shift_amount = right & 0x1F;
+ if (Smi::IsValid(left << shift_amount)) {
+ answer_object = Smi::FromInt(left << shift_amount);
+ }
+ break;
+ }
+ case Token::SHR: {
+ int shift_amount = right & 0x1F;
+ unsigned int unsigned_left = left;
+ unsigned_left >>= shift_amount;
+ if (unsigned_left <= static_cast<unsigned int>(Smi::kMaxValue)) {
+ answer_object = Smi::FromInt(unsigned_left);
+ }
+ break;
+ }
+ case Token::SAR: {
+ int shift_amount = right & 0x1F;
+ unsigned int unsigned_left = left;
+ if (left < 0) {
+ // Perform arithmetic shift of a negative number by
+ // complementing number, logical shifting, complementing again.
+ unsigned_left = ~unsigned_left;
+ unsigned_left >>= shift_amount;
+ unsigned_left = ~unsigned_left;
+ } else {
+ unsigned_left >>= shift_amount;
+ }
+ ASSERT(Smi::IsValid(unsigned_left)); // Converted to signed.
+ answer_object = Smi::FromInt(unsigned_left); // Converted to signed.
+ break;
+ }
+ default:
+ UNREACHABLE();
+ break;
+ }
+ if (answer_object == Heap::undefined_value()) {
+ return false;
+ }
+ frame_->Push(Handle<Object>(answer_object));
+ return true;
+}
+
+
+void CodeGenerator::LikelySmiBinaryOperation(Token::Value op,
+ Result* left,
+ Result* right,
+ OverwriteMode overwrite_mode) {
+ // Create a new deferred code object that calls GenericBinaryOpStub
+ // in the slow case.
+ DeferredInlineBinaryOperation* deferred =
+ new DeferredInlineBinaryOperation(this, 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);
+ deferred->BindExit(&answer);
+ frame_->Push(&answer);
+}
+
+
class DeferredInlineSmiOperation: public DeferredCode {
public:
DeferredInlineSmiOperation(CodeGenerator* generator,
@@ -1049,77 +1181,85 @@
}
-void CodeGenerator::SmiOperation(Token::Value op,
- StaticType* type,
- Handle<Object> value,
- bool reversed,
- OverwriteMode overwrite_mode) {
+void CodeGenerator::ConstantSmiBinaryOperation(Token::Value op,
+ Result* operand,
+ Handle<Object> value,
+ StaticType* type,
+ bool reversed,
+ OverwriteMode overwrite_mode) {
// NOTE: This is an attempt to inline (a bit) more of the code for
// some possible smi operations (like + and -) when (at least) one
- // of the operands is a literal smi. With this optimization, the
- // performance of the system is increased by ~15%, and the generated
- // code size is increased by ~1% (measured on a combination of
- // different benchmarks).
+ // of the operands is a constant smi.
+ // Consumes the argument "operand".
// TODO(199): Optimize some special cases of operations involving a
// smi literal (multiply by 2, shift by 0, etc.).
+ if (IsUnsafeSmi(value)) {
+ Result unsafe_operand(value, this);
+ if (reversed) {
+ LikelySmiBinaryOperation(op, &unsafe_operand, operand,
+ overwrite_mode);
+ } else {
+ LikelySmiBinaryOperation(op, operand, &unsafe_operand,
+ overwrite_mode);
+ }
+ ASSERT(!operand->is_valid());
+ return;
+ }
// Get the literal value.
Smi* smi_value = Smi::cast(*value);
int int_value = smi_value->value();
- ASSERT(is_intn(int_value, kMaxSmiInlinedBits));
switch (op) {
case Token::ADD: {
DeferredCode* deferred = NULL;
- if (!reversed) {
- deferred = new DeferredInlineSmiAdd(this, smi_value, overwrite_mode);
- } else {
+ if (reversed) {
deferred = new DeferredInlineSmiAddReversed(this, smi_value,
overwrite_mode);
+ } else {
+ deferred = new DeferredInlineSmiAdd(this, smi_value, overwrite_mode);
}
- Result operand = frame_->Pop();
- operand.ToRegister();
- frame_->Spill(operand.reg());
- __ add(Operand(operand.reg()), Immediate(value));
- deferred->enter()->Branch(overflow, &operand, not_taken);
- __ test(operand.reg(), Immediate(kSmiTagMask));
- deferred->enter()->Branch(not_zero, &operand, not_taken);
- deferred->BindExit(&operand);
- frame_->Push(&operand);
+ operand->ToRegister();
+ frame_->Spill(operand->reg());
+ __ add(Operand(operand->reg()), Immediate(value));
+ deferred->enter()->Branch(overflow, operand, not_taken);
+ __ test(operand->reg(), Immediate(kSmiTagMask));
+ deferred->enter()->Branch(not_zero, operand, not_taken);
+ deferred->BindExit(operand);
+ frame_->Push(operand);
break;
}
case Token::SUB: {
DeferredCode* deferred = NULL;
- Result operand = frame_->Pop();
Result answer(this); // Only allocated a new register if reversed.
- if (!reversed) {
- operand.ToRegister();
- frame_->Spill(operand.reg());
- deferred = new DeferredInlineSmiSub(this,
- smi_value,
- overwrite_mode);
- __ sub(Operand(operand.reg()), Immediate(value));
- answer = operand;
- } else {
+ if (reversed) {
answer = allocator()->Allocate();
ASSERT(answer.is_valid());
deferred = new DeferredInlineSmiSubReversed(this,
smi_value,
overwrite_mode);
- __ mov(answer.reg(), Immediate(value));
- if (operand.is_register()) {
- __ sub(answer.reg(), Operand(operand.reg()));
+ __ Set(answer.reg(), Immediate(value));
+ if (operand->is_register()) {
+ __ sub(answer.reg(), Operand(operand->reg()));
} else {
- ASSERT(operand.is_constant());
- __ sub(Operand(answer.reg()), Immediate(operand.handle()));
+ ASSERT(operand->is_constant());
+ __ sub(Operand(answer.reg()), Immediate(operand->handle()));
}
+ } else {
+ operand->ToRegister();
+ frame_->Spill(operand->reg());
+ deferred = new DeferredInlineSmiSub(this,
+ smi_value,
+ overwrite_mode);
+ __ sub(Operand(operand->reg()), Immediate(value));
+ answer = *operand;
}
- deferred->enter()->Branch(overflow, &operand, not_taken);
+ deferred->enter()->Branch(overflow, operand, not_taken);
__ test(answer.reg(), Immediate(kSmiTagMask));
- deferred->enter()->Branch(not_zero, &operand, not_taken);
- operand.Unuse();
+ deferred->enter()->Branch(not_zero, operand, not_taken);
+ operand->Unuse();
deferred->BindExit(&answer);
frame_->Push(&answer);
break;
@@ -1127,10 +1267,9 @@
case Token::SAR: {
if (reversed) {
- Result top = frame_->Pop();
- frame_->Push(value);
- frame_->Push(&top);
- GenericBinaryOperation(op, type, overwrite_mode);
+ Result constant_operand(value, this);
+ LikelySmiBinaryOperation(op, &constant_operand, operand,
+ overwrite_mode);
} else {
// Only the least significant 5 bits of the shift value are used.
// In the slow case, this masking is done inside the runtime call.
@@ -1138,25 +1277,25 @@
DeferredCode* deferred =
new DeferredInlineSmiOperation(this, Token::SAR, smi_value,
overwrite_mode);
- Result result = frame_->Pop();
- result.ToRegister();
- __ test(result.reg(), Immediate(kSmiTagMask));
- deferred->enter()->Branch(not_zero, &result, not_taken);
- frame_->Spill(result.reg());
- __ sar(result.reg(), shift_value);
- __ and_(result.reg(), ~kSmiTagMask);
- deferred->BindExit(&result);
- frame_->Push(&result);
+ operand->ToRegister();
+ __ test(operand->reg(), Immediate(kSmiTagMask));
+ deferred->enter()->Branch(not_zero, operand, not_taken);
+ if (shift_value > 0) {
+ frame_->Spill(operand->reg());
+ __ sar(operand->reg(), shift_value);
+ __ and_(operand->reg(), ~kSmiTagMask);
+ }
+ deferred->BindExit(operand);
+ frame_->Push(operand);
}
break;
}
case Token::SHR: {
if (reversed) {
- Result top = frame_->Pop();
- frame_->Push(value);
- frame_->Push(&top);
- GenericBinaryOperation(op, type, overwrite_mode);
+ Result constant_operand(value, this);
+ LikelySmiBinaryOperation(op, &constant_operand, operand,
+ overwrite_mode);
} else {
// Only the least significant 5 bits of the shift value are used.
// In the slow case, this masking is done inside the runtime call.
@@ -1164,21 +1303,20 @@
DeferredCode* deferred =
new DeferredInlineSmiOperation(this, Token::SHR, smi_value,
overwrite_mode);
- Result operand = frame_->Pop();
- operand.ToRegister();
- __ test(operand.reg(), Immediate(kSmiTagMask));
- deferred->enter()->Branch(not_zero, &operand, not_taken);
+ operand->ToRegister();
+ __ test(operand->reg(), Immediate(kSmiTagMask));
+ deferred->enter()->Branch(not_zero, operand, not_taken);
Result answer = allocator()->Allocate();
ASSERT(answer.is_valid());
- __ mov(answer.reg(), Operand(operand.reg()));
+ __ mov(answer.reg(), operand->reg());
__ sar(answer.reg(), kSmiTagSize);
__ shr(answer.reg(), shift_value);
// A negative Smi shifted right two is in the positive Smi range.
if (shift_value < 2) {
__ test(answer.reg(), Immediate(0xc0000000));
- deferred->enter()->Branch(not_zero, &operand, not_taken);
+ deferred->enter()->Branch(not_zero, operand, not_taken);
}
- operand.Unuse();
+ operand->Unuse();
ASSERT(kSmiTagSize == times_2); // Adjust the code if not true.
__ lea(answer.reg(),
Operand(answer.reg(), answer.reg(), times_1, kSmiTag));
@@ -1190,10 +1328,9 @@
case Token::SHL: {
if (reversed) {
- Result top = frame_->Pop();
- frame_->Push(value);
- frame_->Push(&top);
- GenericBinaryOperation(op, type, overwrite_mode);
+ Result constant_operand(value, this);
+ LikelySmiBinaryOperation(op, &constant_operand, operand,
+ overwrite_mode);
} else {
// Only the least significant 5 bits of the shift value are used.
// In the slow case, this masking is done inside the runtime call.
@@ -1201,13 +1338,12 @@
DeferredCode* deferred =
new DeferredInlineSmiOperation(this, Token::SHL, smi_value,
overwrite_mode);
- Result operand = frame_->Pop();
- operand.ToRegister();
- __ test(operand.reg(), Immediate(kSmiTagMask));
- deferred->enter()->Branch(not_zero, &operand, not_taken);
+ operand->ToRegister();
+ __ test(operand->reg(), Immediate(kSmiTagMask));
+ deferred->enter()->Branch(not_zero, operand, not_taken);
Result answer = allocator()->Allocate();
ASSERT(answer.is_valid());
- __ mov(answer.reg(), Operand(operand.reg()));
+ __ mov(answer.reg(), operand->reg());
ASSERT(kSmiTag == 0); // adjust code if not the case
// We do no shifts, only the Smi conversion, if shift_value is 1.
if (shift_value == 0) {
@@ -1218,8 +1354,8 @@
// Convert int result to Smi, checking that it is in int range.
ASSERT(kSmiTagSize == times_2); // adjust code if not the case
__ add(answer.reg(), Operand(answer.reg()));
- deferred->enter()->Branch(overflow, &operand, not_taken);
- operand.Unuse();
+ deferred->enter()->Branch(overflow, operand, not_taken);
+ operand->Unuse();
deferred->BindExit(&answer);
frame_->Push(&answer);
}
@@ -1230,51 +1366,51 @@
case Token::BIT_XOR:
case Token::BIT_AND: {
DeferredCode* deferred = NULL;
- if (!reversed) {
+ if (reversed) {
+ deferred = new DeferredInlineSmiOperationReversed(this, op, smi_value,
+ overwrite_mode);
+ } else {
deferred = new DeferredInlineSmiOperation(this, op, smi_value,
overwrite_mode);
- } else {
- deferred = new DeferredInlineSmiOperationReversed(this, op, smi_value,
- overwrite_mode);
}
- Result operand = frame_->Pop();
- operand.ToRegister();
- __ test(operand.reg(), Immediate(kSmiTagMask));
- deferred->enter()->Branch(not_zero, &operand, not_taken);
- frame_->Spill(operand.reg());
+ operand->ToRegister();
+ __ test(operand->reg(), Immediate(kSmiTagMask));
+ deferred->enter()->Branch(not_zero, operand, not_taken);
+ frame_->Spill(operand->reg());
if (op == Token::BIT_AND) {
if (int_value == 0) {
- __ xor_(Operand(operand.reg()), operand.reg());
+ __ xor_(Operand(operand->reg()), operand->reg());
} else {
- __ and_(Operand(operand.reg()), Immediate(value));
+ __ and_(Operand(operand->reg()), Immediate(value));
}
} else if (op == Token::BIT_XOR) {
if (int_value != 0) {
- __ xor_(Operand(operand.reg()), Immediate(value));
+ __ xor_(Operand(operand->reg()), Immediate(value));
}
} else {
ASSERT(op == Token::BIT_OR);
if (int_value != 0) {
- __ or_(Operand(operand.reg()), Immediate(value));
+ __ or_(Operand(operand->reg()), Immediate(value));
}
}
- deferred->BindExit(&operand);
- frame_->Push(&operand);
+ deferred->BindExit(operand);
+ frame_->Push(operand);
break;
}
default: {
- if (!reversed) {
- frame_->Push(value);
+ Result constant_operand(value, this);
+ if (reversed) {
+ LikelySmiBinaryOperation(op, &constant_operand, operand,
+ overwrite_mode);
} else {
- Result top = frame_->Pop();
- frame_->Push(value);
- frame_->Push(&top);
+ LikelySmiBinaryOperation(op, operand, &constant_operand,
+ overwrite_mode);
}
- GenericBinaryOperation(op, type, overwrite_mode);
break;
}
}
+ ASSERT(!operand->is_valid());
}
@@ -3731,13 +3867,6 @@
}
-bool CodeGenerator::IsInlineSmi(Literal* literal) {
- if (literal == NULL || !literal->handle()->IsSmi()) return false;
- int int_value = Smi::cast(*literal->handle())->value();
- return is_intn(int_value, kMaxSmiInlinedBits);
-}
-
-
void CodeGenerator::VisitAssignment(Assignment* node) {
Comment cmnt(masm_, "[ Assignment");
CodeForStatementPosition(node);
@@ -3782,13 +3911,8 @@
} else {
target.GetValue(NOT_INSIDE_TYPEOF);
}
- if (IsInlineSmi(literal)) {
- SmiOperation(node->binary_op(), node->type(), literal->handle(), false,
- NO_OVERWRITE);
- } else {
- Load(node->value());
- GenericBinaryOperation(node->binary_op(), node->type());
- }
+ Load(node->value());
+ GenericBinaryOperation(node->binary_op(), node->type());
}
if (var != NULL &&
@@ -4919,24 +5043,9 @@
overwrite_mode = OVERWRITE_RIGHT;
}
- // Optimize for the case where (at least) one of the expressions
- // is a literal small integer.
- Literal* lliteral = node->left()->AsLiteral();
- Literal* rliteral = node->right()->AsLiteral();
-
- if (IsInlineSmi(rliteral)) {
- Load(node->left());
- SmiOperation(node->op(), node->type(), rliteral->handle(), false,
- overwrite_mode);
- } else if (IsInlineSmi(lliteral)) {
- Load(node->right());
- SmiOperation(node->op(), node->type(), lliteral->handle(), true,
- overwrite_mode);
- } else {
- Load(node->left());
- Load(node->right());
- GenericBinaryOperation(node->op(), node->type(), overwrite_mode);
- }
+ Load(node->left());
+ Load(node->right());
+ GenericBinaryOperation(node->op(), node->type(), overwrite_mode);
}
}
@@ -5468,60 +5577,44 @@
#undef __
#define __ masm_->
-Result DeferredInlineBinaryOperation::GenerateInlineCode() {
+Result DeferredInlineBinaryOperation::GenerateInlineCode(Result* left,
+ Result* right) {
// 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.
+ // Consumes its arguments (sets left and right to invalid and frees their
+ // registers).
- 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());
- }
+ 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 = generator()->allocator()->Allocate();
- 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()));
+ __ mov(answer.reg(), 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);
+ enter()->Branch(not_zero, left, right, not_taken);
// All operations start by copying the left argument into answer.
- __ mov(answer.reg(), Operand(left.reg()));
+ __ mov(answer.reg(), left->reg());
switch (op_) {
case Token::ADD:
- __ add(answer.reg(), Operand(right.reg())); // add optimistically
- enter()->Branch(overflow, &left, &right, not_taken);
+ __ add(answer.reg(), Operand(right->reg())); // add optimistically
+ enter()->Branch(overflow, left, right, not_taken);
break;
case Token::SUB:
- __ sub(answer.reg(), Operand(right.reg())); // subtract optimistically
- enter()->Branch(overflow, &left, &right, not_taken);
+ __ sub(answer.reg(), Operand(right->reg())); // subtract optimistically
+ enter()->Branch(overflow, left, right, not_taken);
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
@@ -5529,9 +5622,9 @@
// 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()));
+ __ imul(answer.reg(), Operand(right->reg()));
// Go slow on overflows.
- enter()->Branch(overflow, &left, &right, not_taken);
+ 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
@@ -5539,83 +5632,162 @@
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);
+ __ 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::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(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(reg_eax.reg(), 0x40000000);
- enter()->Branch(equal, &left, &right, not_taken);
- // Check that the remainder is zero.
- __ 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.
- ASSERT(kSmiTagSize == times_2); // adjust code if not the case
- __ lea(answer.reg(), Operand(eax, eax, times_1, kSmiTag));
- break;
- }
+ case Token::DIV: // Fall through.
+ case Token::MOD: {
+ // Div and mod use the registers eax and edx. Left and right must
+ // be preserved, because the original operands are needed if we switch
+ // to the slow case. Move them if either is in eax or edx.
+ // The Result answer should be changed into an alias for eax.
+ // Precondition:
+ // The Results left and right are valid. They may be the same register,
+ // and may be unspilled. The Result answer is valid and is distinct
+ // from left and right, and is spilled.
+ // The value in left is copied to answer.
- case Token::MOD: {
- // Left hand argument has been copied into answer, which is eax.
+ Result reg_eax = generator()->allocator()->Allocate(eax);
+ Result reg_edx = generator()->allocator()->Allocate(edx);
+ // These allocations may have failed, if one of left, right, or answer
+ // is in register eax or edx.
+ bool left_copied_to_eax = false; // We will make sure this becomes true.
+
+ // Part 1: Get eax
+ if (answer.reg().is(eax)) {
+ reg_eax = answer;
+ left_copied_to_eax = true;
+ } else if (right->reg().is(eax) || left->reg().is(eax)) {
+ // We need a non-edx register to move one or both of left and right to.
+ // We use answer if it is not edx, otherwise we allocate one.
+ if (answer.reg().is(edx)) {
+ reg_edx = answer;
+ answer = generator()->allocator()->Allocate();
+ ASSERT(answer.is_valid());
+ }
+
+ if (left->reg().is(eax)) {
+ reg_eax = *left;
+ left_copied_to_eax = true;
+ *left = answer;
+ }
+ if (right->reg().is(eax)) {
+ reg_eax = *right;
+ *right = answer;
+ }
+ __ mov(answer.reg(), eax);
+ }
+ // End of Part 1.
+ // reg_eax is valid, and neither left nor right is in eax.
+ ASSERT(reg_eax.is_valid());
+ ASSERT(!left->reg().is(eax));
+ ASSERT(!right->reg().is(eax));
+
+ // Part 2: Get edx
+ // reg_edx is invalid if and only if either left, right,
+ // or answer is in edx. If edx is valid, then either edx
+ // was free, or it was answer, but answer was reallocated.
+ if (answer.reg().is(edx)) {
+ reg_edx = answer;
+ } else if (right->reg().is(edx) || left->reg().is(edx)) {
+ // Is answer used?
+ if (answer.reg().is(eax) || answer.reg().is(left->reg()) ||
+ answer.reg().is(right->reg())) {
+ answer = generator()->allocator()->Allocate();
+ ASSERT(answer.is_valid()); // We cannot hit both Allocate() calls.
+ }
+ if (left->reg().is(edx)) {
+ reg_edx = *left;
+ *left = answer;
+ }
+ if (right->reg().is(edx)) {
+ reg_edx = *right;
+ *right = answer;
+ }
+ __ mov(answer.reg(), edx);
+ }
+ // End of Part 2
+ ASSERT(reg_edx.is_valid());
+ ASSERT(!left->reg().is(eax));
+ ASSERT(!right->reg().is(eax));
+
+ answer = reg_eax; // May free answer, if it was never used.
+ generator()->frame()->Spill(eax);
+ if (!left_copied_to_eax) {
+ __ mov(eax, left->reg());
+ left_copied_to_eax = true;
+ }
+ generator()->frame()->Spill(edx);
+
+ // Postcondition:
+ // reg_eax, reg_edx are valid, correct, and spilled.
+ // reg_eax contains the value originally in left
+ // left and right are not eax or edx. They may or may not be
+ // spilled or distinct.
+ // answer is an alias for reg_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(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;
+ __ test(right->reg(), Operand(right->reg()));
+ enter()->Branch(zero, left, right, not_taken);
+ // Divide edx:eax by the right operand.
+ __ idiv(right->reg());
+ if (op_ == Token::DIV) {
+ // 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, left, right, not_taken);
+ // Check that the remainder is zero.
+ __ test(edx, Operand(edx));
+ enter()->Branch(not_zero, left, right, not_taken);
+ // Tag the result and store it in register temp.
+ ASSERT(kSmiTagSize == times_2); // adjust code if not the case
+ __ lea(answer.reg(), Operand(eax, eax, times_1, kSmiTag));
+ } 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(edx, Operand(edx));
+ __ 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_(answer.reg(), Operand(right.reg()));
+ __ or_(answer.reg(), Operand(right->reg()));
break;
case Token::BIT_AND:
- __ and_(answer.reg(), Operand(right.reg()));
+ __ and_(answer.reg(), Operand(right->reg()));
break;
case Token::BIT_XOR:
- __ xor_(answer.reg(), Operand(right.reg()));
+ __ xor_(answer.reg(), Operand(right->reg()));
break;
case Token::SHL:
@@ -5625,29 +5797,29 @@
// 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)) {
+ 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;
+ if (left->reg().is(ecx)) {
+ *left = answer;
}
- } else if (left.reg().is(ecx)) {
- generator()->frame()->Spill(left.reg());
- __ mov(left.reg(), Operand(right.reg()));
- right = left;
- left = answer; // Use copy of left in answer as left.
+ } else if (left->reg().is(ecx)) {
+ generator()->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(), Operand(right.reg()));
- right = answer;
+ __ mov(answer.reg(), 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;
+ __ mov(ecx, right->reg());
+ *right = reg_ecx;
}
- ASSERT(left.reg().is_valid());
- ASSERT(!left.reg().is(ecx));
- ASSERT(right.reg().is(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
@@ -5655,20 +5827,20 @@
// the same answer.
// We are modifying left and right. They must be spilled!
- generator()->frame()->Spill(left.reg());
- generator()->frame()->Spill(right.reg());
+ generator()->frame()->Spill(left->reg());
+ generator()->frame()->Spill(right->reg());
// Remove tags from operands (but keep sign).
- __ sar(left.reg(), kSmiTagSize);
+ __ sar(left->reg(), kSmiTagSize);
__ sar(ecx, kSmiTagSize);
// Perform the operation.
switch (op_) {
case Token::SAR:
- __ sar(left.reg());
+ __ sar(left->reg());
// No checks of result necessary
break;
case Token::SHR: {
- __ shr(left.reg());
+ __ 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.
@@ -5680,18 +5852,18 @@
// 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());
+ __ 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, &right);
+ __ shl(left->reg(), kSmiTagSize);
+ __ shl(right->reg(), kSmiTagSize);
+ enter()->Jump(left, right);
+ result_ok.Bind(left);
break;
}
case Token::SHL: {
- __ shl(left.reg());
+ __ shl(left->reg());
// Check that the *signed* result fits in a smi.
//
// TODO(207): Can reduce registers from 4 to 3 by
@@ -5699,23 +5871,23 @@
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));
+ __ 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());
+ result_ok.Branch(zero, left, taken);
+ __ shr(left->reg());
ASSERT(kSmiTag == 0);
- __ shl(left.reg(), kSmiTagSize);
- __ shl(right.reg(), kSmiTagSize);
- enter()->Jump(&left, &right);
- result_ok.Bind(&left, &right);
+ __ 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;
+ // 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(),
@@ -5726,6 +5898,8 @@
UNREACHABLE();
break;
}
+ left->Unuse();
+ right->Unuse();
return answer;
}
« no previous file with comments | « src/codegen-ia32.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698