| 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;
|
| }
|
|
|
|
|