| Index: src/ia32/codegen-ia32.cc
|
| ===================================================================
|
| --- src/ia32/codegen-ia32.cc (revision 2099)
|
| +++ src/ia32/codegen-ia32.cc (working copy)
|
| @@ -42,6 +42,35 @@
|
| #define __ ACCESS_MASM(masm_)
|
|
|
| // -------------------------------------------------------------------------
|
| +// Platform-specific DeferredCode functions.
|
| +
|
| +void DeferredCode::SaveRegisters() {
|
| + for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) {
|
| + int action = registers_[i];
|
| + if (action == kPush) {
|
| + __ push(RegisterAllocator::ToRegister(i));
|
| + } else if (action != kIgnore && (action & kSyncedFlag) == 0) {
|
| + __ mov(Operand(ebp, action), RegisterAllocator::ToRegister(i));
|
| + }
|
| + }
|
| +}
|
| +
|
| +
|
| +void DeferredCode::RestoreRegisters() {
|
| + // Restore registers in reverse order due to the stack.
|
| + for (int i = RegisterAllocator::kNumRegisters - 1; i >= 0; i--) {
|
| + int action = registers_[i];
|
| + if (action == kPush) {
|
| + __ pop(RegisterAllocator::ToRegister(i));
|
| + } else if (action != kIgnore) {
|
| + action &= ~kSyncedFlag;
|
| + __ mov(RegisterAllocator::ToRegister(i), Operand(ebp, action));
|
| + }
|
| + }
|
| +}
|
| +
|
| +
|
| +// -------------------------------------------------------------------------
|
| // CodeGenState implementation.
|
|
|
| CodeGenState::CodeGenState(CodeGenerator* owner)
|
| @@ -73,7 +102,8 @@
|
| // -------------------------------------------------------------------------
|
| // CodeGenerator implementation
|
|
|
| -CodeGenerator::CodeGenerator(int buffer_size, Handle<Script> script,
|
| +CodeGenerator::CodeGenerator(int buffer_size,
|
| + Handle<Script> script,
|
| bool is_eval)
|
| : is_eval_(is_eval),
|
| script_(script),
|
| @@ -779,8 +809,12 @@
|
| // Call the specialized stub for a binary operation.
|
| class DeferredInlineBinaryOperation: public DeferredCode {
|
| public:
|
| - DeferredInlineBinaryOperation(Token::Value op, OverwriteMode mode)
|
| - : op_(op), mode_(mode) {
|
| + DeferredInlineBinaryOperation(Token::Value op,
|
| + Register dst,
|
| + Register left,
|
| + Register right,
|
| + OverwriteMode mode)
|
| + : op_(op), dst_(dst), left_(left), right_(right), mode_(mode) {
|
| set_comment("[ DeferredInlineBinaryOperation");
|
| }
|
|
|
| @@ -788,19 +822,19 @@
|
|
|
| private:
|
| Token::Value op_;
|
| + Register dst_;
|
| + Register left_;
|
| + Register right_;
|
| OverwriteMode mode_;
|
| };
|
|
|
|
|
| void DeferredInlineBinaryOperation::Generate() {
|
| - Result left;
|
| - Result right;
|
| - enter()->Bind(&left, &right);
|
| - cgen()->frame()->Push(&left);
|
| - cgen()->frame()->Push(&right);
|
| + __ push(left_);
|
| + __ push(right_);
|
| GenericBinaryOpStub stub(op_, mode_, SMI_CODE_INLINED);
|
| - Result answer = cgen()->frame()->CallStub(&stub, 2);
|
| - exit_.Jump(&answer);
|
| + __ CallStub(&stub);
|
| + if (!dst_.is(eax)) __ mov(dst_, eax);
|
| }
|
|
|
|
|
| @@ -996,15 +1030,12 @@
|
| }
|
|
|
|
|
| +// Implements a binary operation using a deferred code object and some
|
| +// inline code to operate on smis quickly.
|
| 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.
|
| - DeferredInlineBinaryOperation* deferred =
|
| - 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
|
| @@ -1069,30 +1100,34 @@
|
|
|
| left->ToRegister();
|
| right->ToRegister();
|
| - frame_->Spill(quotient.reg());
|
| - frame_->Spill(remainder.reg());
|
| + frame_->Spill(eax);
|
| + frame_->Spill(edx);
|
|
|
| // Check that left and right are smi tagged.
|
| + DeferredInlineBinaryOperation* deferred =
|
| + new DeferredInlineBinaryOperation(op,
|
| + (op == Token::DIV) ? eax : edx,
|
| + left->reg(),
|
| + right->reg(),
|
| + overwrite_mode);
|
| 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()));
|
| + if (!left_is_in_eax) __ mov(eax, left->reg());
|
| + left_is_in_eax = false; // About to destroy the value in eax.
|
| + __ or_(eax, Operand(right->reg()));
|
| ASSERT(kSmiTag == 0); // Adjust test if not the case.
|
| - __ test(quotient.reg(), Immediate(kSmiTagMask));
|
| + __ test(eax, Immediate(kSmiTagMask));
|
| }
|
| - deferred->SetEntryFrame(left, right);
|
| - deferred->enter()->Branch(not_zero, left, right);
|
| + deferred->Branch(not_zero);
|
|
|
| - if (!left_is_in_eax) __ mov(quotient.reg(), left->reg());
|
| -
|
| + if (!left_is_in_eax) __ mov(eax, 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);
|
| + deferred->Branch(zero);
|
| // Divide edx:eax by the right operand.
|
| __ idiv(right->reg());
|
|
|
| @@ -1106,42 +1141,39 @@
|
| __ test(left->reg(), Operand(left->reg()));
|
| __ j(not_zero, &non_zero_result);
|
| __ test(right->reg(), Operand(right->reg()));
|
| - deferred->enter()->Branch(negative, left, right);
|
| + deferred->Branch(negative);
|
| __ 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);
|
| + __ cmp(eax, 0x40000000);
|
| + deferred->Branch(equal);
|
| // Check that the remainder is zero.
|
| - __ test(remainder.reg(), Operand(remainder.reg()));
|
| - remainder.Unuse();
|
| - deferred->enter()->Branch(not_zero, left, right);
|
| + __ test(edx, Operand(edx));
|
| + deferred->Branch(not_zero);
|
| + // Tag the result and store it in the quotient register.
|
| + ASSERT(kSmiTagSize == times_2); // adjust code if not the case
|
| + __ lea(eax, Operand(eax, eax, times_1, kSmiTag));
|
| + deferred->BindExit();
|
| 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()));
|
| + __ test(edx, Operand(edx));
|
| __ j(not_zero, &non_zero_result, taken);
|
| __ test(left->reg(), Operand(left->reg()));
|
| - deferred->enter()->Branch(negative, left, right);
|
| + deferred->Branch(negative);
|
| + __ bind(&non_zero_result);
|
| + deferred->BindExit();
|
| left->Unuse();
|
| right->Unuse();
|
| - __ bind(&non_zero_result);
|
| - deferred->BindExit(&remainder);
|
| frame_->Push(&remainder);
|
| }
|
| return;
|
| @@ -1169,10 +1201,16 @@
|
| ASSERT(answer.is_valid());
|
| // Check that both operands are smis using the answer register as a
|
| // temporary.
|
| + DeferredInlineBinaryOperation* deferred =
|
| + new DeferredInlineBinaryOperation(op,
|
| + answer.reg(),
|
| + left->reg(),
|
| + ecx,
|
| + overwrite_mode);
|
| __ mov(answer.reg(), left->reg());
|
| __ or_(answer.reg(), Operand(ecx));
|
| __ test(answer.reg(), Immediate(kSmiTagMask));
|
| - deferred->enter()->Branch(not_zero, left, right);
|
| + deferred->Branch(not_zero);
|
|
|
| // Untag both operands.
|
| __ mov(answer.reg(), left->reg());
|
| @@ -1185,7 +1223,7 @@
|
| // No checks of result necessary
|
| break;
|
| case Token::SHR: {
|
| - JumpTarget result_ok;
|
| + Label result_ok;
|
| __ shr(answer.reg());
|
| // Check that the *unsigned* result fits in a smi. Neither of
|
| // the two high-order bits can be set:
|
| @@ -1198,37 +1236,35 @@
|
| // case. The low bit of the left argument may be lost, but only
|
| // in a case where it is dropped anyway.
|
| __ test(answer.reg(), Immediate(0xc0000000));
|
| - result_ok.Branch(zero, &answer);
|
| + __ j(zero, &result_ok);
|
| ASSERT(kSmiTag == 0);
|
| __ shl(ecx, kSmiTagSize);
|
| - answer.Unuse();
|
| - deferred->enter()->Jump(left, right);
|
| - result_ok.Bind(&answer);
|
| + deferred->Jump();
|
| + __ bind(&result_ok);
|
| break;
|
| }
|
| case Token::SHL: {
|
| - JumpTarget result_ok;
|
| + Label result_ok;
|
| __ shl(answer.reg());
|
| // Check that the *signed* result fits in a smi.
|
| __ cmp(answer.reg(), 0xc0000000);
|
| - result_ok.Branch(positive, &answer);
|
| + __ j(positive, &result_ok);
|
| ASSERT(kSmiTag == 0);
|
| __ shl(ecx, kSmiTagSize);
|
| - answer.Unuse();
|
| - deferred->enter()->Jump(left, right);
|
| - result_ok.Bind(&answer);
|
| + deferred->Jump();
|
| + __ bind(&result_ok);
|
| break;
|
| }
|
| default:
|
| UNREACHABLE();
|
| }
|
| - left->Unuse();
|
| - right->Unuse();
|
| // Smi-tag the result in answer.
|
| ASSERT(kSmiTagSize == 1); // Adjust code if not the case.
|
| __ lea(answer.reg(),
|
| Operand(answer.reg(), answer.reg(), times_1, kSmiTag));
|
| - deferred->BindExit(&answer);
|
| + deferred->BindExit();
|
| + left->Unuse();
|
| + right->Unuse();
|
| frame_->Push(&answer);
|
| return;
|
| }
|
| @@ -1240,9 +1276,15 @@
|
| // registers containing left and right are not modified so they don't
|
| // need to be spilled in the fast case.
|
| Result answer = allocator_->Allocate();
|
| + ASSERT(answer.is_valid());
|
|
|
| - ASSERT(answer.is_valid());
|
| // Perform the smi tag check.
|
| + DeferredInlineBinaryOperation* deferred =
|
| + new DeferredInlineBinaryOperation(op,
|
| + answer.reg(),
|
| + left->reg(),
|
| + right->reg(),
|
| + overwrite_mode);
|
| if (left->reg().is(right->reg())) {
|
| __ test(left->reg(), Immediate(kSmiTagMask));
|
| } else {
|
| @@ -1251,27 +1293,20 @@
|
| ASSERT(kSmiTag == 0); // Adjust test if not the case.
|
| __ test(answer.reg(), Immediate(kSmiTagMask));
|
| }
|
| + deferred->Branch(not_zero);
|
| + __ mov(answer.reg(), left->reg());
|
| 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);
|
| + deferred->Branch(overflow);
|
| 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);
|
| + deferred->Branch(overflow);
|
| 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).
|
| @@ -1280,7 +1315,7 @@
|
| // 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);
|
| + deferred->Branch(overflow);
|
| // 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
|
| @@ -1290,27 +1325,21 @@
|
| __ 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);
|
| + deferred->Branch(negative);
|
| __ 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;
|
|
|
| @@ -1318,19 +1347,25 @@
|
| UNREACHABLE();
|
| break;
|
| }
|
| + deferred->BindExit();
|
| left->Unuse();
|
| right->Unuse();
|
| - deferred->BindExit(&answer);
|
| frame_->Push(&answer);
|
| }
|
|
|
|
|
| +// Call the appropriate binary operation stub to compute src op value
|
| +// and leave the result in dst.
|
| class DeferredInlineSmiOperation: public DeferredCode {
|
| public:
|
| DeferredInlineSmiOperation(Token::Value op,
|
| + Register dst,
|
| + Register src,
|
| Smi* value,
|
| OverwriteMode overwrite_mode)
|
| : op_(op),
|
| + dst_(dst),
|
| + src_(src),
|
| value_(value),
|
| overwrite_mode_(overwrite_mode) {
|
| set_comment("[ DeferredInlineSmiOperation");
|
| @@ -1340,29 +1375,35 @@
|
|
|
| private:
|
| Token::Value op_;
|
| + Register dst_;
|
| + Register src_;
|
| Smi* value_;
|
| OverwriteMode overwrite_mode_;
|
| };
|
|
|
|
|
| void DeferredInlineSmiOperation::Generate() {
|
| - Result left;
|
| - enter()->Bind(&left);
|
| - cgen()->frame()->Push(&left);
|
| - cgen()->frame()->Push(value_);
|
| - GenericBinaryOpStub igostub(op_, overwrite_mode_, SMI_CODE_INLINED);
|
| - Result answer = cgen()->frame()->CallStub(&igostub, 2);
|
| - exit_.Jump(&answer);
|
| + __ push(src_);
|
| + __ push(Immediate(value_));
|
| + GenericBinaryOpStub stub(op_, overwrite_mode_, SMI_CODE_INLINED);
|
| + __ CallStub(&stub);
|
| + if (!dst_.is(eax)) __ mov(dst_, eax);
|
| }
|
|
|
|
|
| +// Call the appropriate binary operation stub to compute value op src
|
| +// and leave the result in dst.
|
| class DeferredInlineSmiOperationReversed: public DeferredCode {
|
| public:
|
| DeferredInlineSmiOperationReversed(Token::Value op,
|
| + Register dst,
|
| Smi* value,
|
| + Register src,
|
| OverwriteMode overwrite_mode)
|
| : op_(op),
|
| + dst_(dst),
|
| value_(value),
|
| + src_(src),
|
| overwrite_mode_(overwrite_mode) {
|
| set_comment("[ DeferredInlineSmiOperationReversed");
|
| }
|
| @@ -1371,152 +1412,116 @@
|
|
|
| private:
|
| Token::Value op_;
|
| + Register dst_;
|
| Smi* value_;
|
| + Register src_;
|
| OverwriteMode overwrite_mode_;
|
| };
|
|
|
|
|
| void DeferredInlineSmiOperationReversed::Generate() {
|
| - Result right;
|
| - enter()->Bind(&right);
|
| - cgen()->frame()->Push(value_);
|
| - cgen()->frame()->Push(&right);
|
| + __ push(Immediate(value_));
|
| + __ push(src_);
|
| GenericBinaryOpStub igostub(op_, overwrite_mode_, SMI_CODE_INLINED);
|
| - Result answer = cgen()->frame()->CallStub(&igostub, 2);
|
| - exit_.Jump(&answer);
|
| + __ CallStub(&igostub);
|
| + if (!dst_.is(eax)) __ mov(dst_, eax);
|
| }
|
|
|
|
|
| +// The result of src + value is in dst. It either overflowed or was not
|
| +// smi tagged. Undo the speculative addition and call the appropriate
|
| +// specialized stub for add. The result is left in dst.
|
| class DeferredInlineSmiAdd: public DeferredCode {
|
| public:
|
| - DeferredInlineSmiAdd(Smi* value,
|
| + DeferredInlineSmiAdd(Register dst,
|
| + Smi* value,
|
| OverwriteMode overwrite_mode)
|
| - : value_(value),
|
| - overwrite_mode_(overwrite_mode) {
|
| + : dst_(dst), value_(value), overwrite_mode_(overwrite_mode) {
|
| set_comment("[ DeferredInlineSmiAdd");
|
| }
|
|
|
| virtual void Generate();
|
|
|
| private:
|
| + Register dst_;
|
| Smi* value_;
|
| OverwriteMode overwrite_mode_;
|
| };
|
|
|
|
|
| +void DeferredInlineSmiAdd::Generate() {
|
| + // Undo the optimistic add operation and call the shared stub.
|
| + __ sub(Operand(dst_), Immediate(value_));
|
| + __ push(dst_);
|
| + __ push(Immediate(value_));
|
| + GenericBinaryOpStub igostub(Token::ADD, overwrite_mode_, SMI_CODE_INLINED);
|
| + __ CallStub(&igostub);
|
| + if (!dst_.is(eax)) __ mov(dst_, eax);
|
| +}
|
| +
|
| +
|
| +// The result of value + src is in dst. It either overflowed or was not
|
| +// smi tagged. Undo the speculative addition and call the appropriate
|
| +// specialized stub for add. The result is left in dst.
|
| class DeferredInlineSmiAddReversed: public DeferredCode {
|
| public:
|
| - DeferredInlineSmiAddReversed(Smi* value,
|
| + DeferredInlineSmiAddReversed(Register dst,
|
| + Smi* value,
|
| OverwriteMode overwrite_mode)
|
| - : value_(value),
|
| - overwrite_mode_(overwrite_mode) {
|
| + : dst_(dst), value_(value), overwrite_mode_(overwrite_mode) {
|
| set_comment("[ DeferredInlineSmiAddReversed");
|
| }
|
|
|
| virtual void Generate();
|
|
|
| private:
|
| + Register dst_;
|
| Smi* value_;
|
| OverwriteMode overwrite_mode_;
|
| };
|
|
|
|
|
| -class DeferredInlineSmiSub: public DeferredCode {
|
| - public:
|
| - DeferredInlineSmiSub(Smi* value,
|
| - OverwriteMode overwrite_mode)
|
| - : value_(value),
|
| - overwrite_mode_(overwrite_mode) {
|
| - set_comment("[ DeferredInlineSmiSub");
|
| - }
|
| -
|
| - virtual void Generate();
|
| -
|
| - private:
|
| - Smi* value_;
|
| - OverwriteMode overwrite_mode_;
|
| -};
|
| -
|
| -
|
| -#undef __
|
| -#define __ ACCESS_MASM(cgen()->masm())
|
| -
|
| -
|
| -void DeferredInlineSmiAdd::Generate() {
|
| - // Undo the optimistic add operation and call the shared stub.
|
| - Result left; // Initially left + value_.
|
| - enter()->Bind(&left);
|
| - left.ToRegister();
|
| - cgen()->frame()->Spill(left.reg());
|
| - __ sub(Operand(left.reg()), Immediate(value_));
|
| - cgen()->frame()->Push(&left);
|
| - cgen()->frame()->Push(value_);
|
| - GenericBinaryOpStub igostub(Token::ADD, overwrite_mode_, SMI_CODE_INLINED);
|
| - Result answer = cgen()->frame()->CallStub(&igostub, 2);
|
| - exit_.Jump(&answer);
|
| -}
|
| -
|
| -
|
| void DeferredInlineSmiAddReversed::Generate() {
|
| // Undo the optimistic add operation and call the shared stub.
|
| - Result right; // Initially value_ + right.
|
| - enter()->Bind(&right);
|
| - right.ToRegister();
|
| - cgen()->frame()->Spill(right.reg());
|
| - __ sub(Operand(right.reg()), Immediate(value_));
|
| - cgen()->frame()->Push(value_);
|
| - cgen()->frame()->Push(&right);
|
| + __ sub(Operand(dst_), Immediate(value_));
|
| + __ push(Immediate(value_));
|
| + __ push(dst_);
|
| GenericBinaryOpStub igostub(Token::ADD, overwrite_mode_, SMI_CODE_INLINED);
|
| - Result answer = cgen()->frame()->CallStub(&igostub, 2);
|
| - exit_.Jump(&answer);
|
| + __ CallStub(&igostub);
|
| + if (!dst_.is(eax)) __ mov(dst_, eax);
|
| }
|
|
|
|
|
| -void DeferredInlineSmiSub::Generate() {
|
| - // Undo the optimistic sub operation and call the shared stub.
|
| - Result left; // Initially left - value_.
|
| - enter()->Bind(&left);
|
| - left.ToRegister();
|
| - cgen()->frame()->Spill(left.reg());
|
| - __ add(Operand(left.reg()), Immediate(value_));
|
| - cgen()->frame()->Push(&left);
|
| - cgen()->frame()->Push(value_);
|
| - GenericBinaryOpStub igostub(Token::SUB, overwrite_mode_, SMI_CODE_INLINED);
|
| - Result answer = cgen()->frame()->CallStub(&igostub, 2);
|
| - exit_.Jump(&answer);
|
| -}
|
| -
|
| -
|
| -#undef __
|
| -#define __ ACCESS_MASM(masm_)
|
| -
|
| -
|
| -class DeferredInlineSmiSubReversed: public DeferredCode {
|
| +// The result of src - value is in dst. It either overflowed or was not
|
| +// smi tagged. Undo the speculative subtraction and call the
|
| +// appropriate specialized stub for subtract. The result is left in
|
| +// dst.
|
| +class DeferredInlineSmiSub: public DeferredCode {
|
| public:
|
| - DeferredInlineSmiSubReversed(Smi* value,
|
| - OverwriteMode overwrite_mode)
|
| - : value_(value),
|
| - overwrite_mode_(overwrite_mode) {
|
| - set_comment("[ DeferredInlineSmiSubReversed");
|
| + DeferredInlineSmiSub(Register dst,
|
| + Smi* value,
|
| + OverwriteMode overwrite_mode)
|
| + : dst_(dst), value_(value), overwrite_mode_(overwrite_mode) {
|
| + set_comment("[ DeferredInlineSmiSub");
|
| }
|
|
|
| virtual void Generate();
|
|
|
| private:
|
| + Register dst_;
|
| Smi* value_;
|
| OverwriteMode overwrite_mode_;
|
| };
|
|
|
|
|
| -void DeferredInlineSmiSubReversed::Generate() {
|
| - // Call the shared stub.
|
| - Result right;
|
| - enter()->Bind(&right);
|
| - cgen()->frame()->Push(value_);
|
| - cgen()->frame()->Push(&right);
|
| +void DeferredInlineSmiSub::Generate() {
|
| + // Undo the optimistic sub operation and call the shared stub.
|
| + __ add(Operand(dst_), Immediate(value_));
|
| + __ push(dst_);
|
| + __ push(Immediate(value_));
|
| GenericBinaryOpStub igostub(Token::SUB, overwrite_mode_, SMI_CODE_INLINED);
|
| - Result answer = cgen()->frame()->CallStub(&igostub, 2);
|
| - exit_.Jump(&answer);
|
| + __ CallStub(&igostub);
|
| + if (!dst_.is(eax)) __ mov(dst_, eax);
|
| }
|
|
|
|
|
| @@ -1554,19 +1559,24 @@
|
| case Token::ADD: {
|
| operand->ToRegister();
|
| frame_->Spill(operand->reg());
|
| - __ add(Operand(operand->reg()), Immediate(value));
|
|
|
| + // Optimistically add. Call the specialized add stub if the
|
| + // result is not a smi or overflows.
|
| DeferredCode* deferred = NULL;
|
| if (reversed) {
|
| - deferred = new DeferredInlineSmiAddReversed(smi_value, overwrite_mode);
|
| + deferred = new DeferredInlineSmiAddReversed(operand->reg(),
|
| + smi_value,
|
| + overwrite_mode);
|
| } else {
|
| - deferred = new DeferredInlineSmiAdd(smi_value, overwrite_mode);
|
| + deferred = new DeferredInlineSmiAdd(operand->reg(),
|
| + smi_value,
|
| + overwrite_mode);
|
| }
|
| - deferred->SetEntryFrame(operand);
|
| - deferred->enter()->Branch(overflow, operand, not_taken);
|
| + __ add(Operand(operand->reg()), Immediate(value));
|
| + deferred->Branch(overflow);
|
| __ test(operand->reg(), Immediate(kSmiTagMask));
|
| - deferred->enter()->Branch(not_zero, operand, not_taken);
|
| - deferred->BindExit(operand);
|
| + deferred->Branch(not_zero);
|
| + deferred->BindExit();
|
| frame_->Push(operand);
|
| break;
|
| }
|
| @@ -1575,31 +1585,37 @@
|
| DeferredCode* deferred = NULL;
|
| Result answer; // Only allocate a new register if reversed.
|
| if (reversed) {
|
| + // The reversed case is only hit when the right operand is not a
|
| + // constant.
|
| + ASSERT(operand->is_register());
|
| answer = allocator()->Allocate();
|
| ASSERT(answer.is_valid());
|
| - deferred = new DeferredInlineSmiSubReversed(smi_value, overwrite_mode);
|
| __ Set(answer.reg(), Immediate(value));
|
| - // We are in the reversed case so they can't both be Smi constants.
|
| - ASSERT(operand->is_register());
|
| + deferred = new DeferredInlineSmiOperationReversed(op,
|
| + answer.reg(),
|
| + smi_value,
|
| + operand->reg(),
|
| + overwrite_mode);
|
| __ sub(answer.reg(), Operand(operand->reg()));
|
| } else {
|
| operand->ToRegister();
|
| frame_->Spill(operand->reg());
|
| - deferred = new DeferredInlineSmiSub(smi_value, overwrite_mode);
|
| + answer = *operand;
|
| + deferred = new DeferredInlineSmiSub(operand->reg(),
|
| + smi_value,
|
| + overwrite_mode);
|
| __ sub(Operand(operand->reg()), Immediate(value));
|
| - answer = *operand;
|
| }
|
| - deferred->SetEntryFrame(operand);
|
| - deferred->enter()->Branch(overflow, operand, not_taken);
|
| + deferred->Branch(overflow);
|
| __ test(answer.reg(), Immediate(kSmiTagMask));
|
| - deferred->enter()->Branch(not_zero, operand, not_taken);
|
| + deferred->Branch(not_zero);
|
| + deferred->BindExit();
|
| operand->Unuse();
|
| - deferred->BindExit(&answer);
|
| frame_->Push(&answer);
|
| break;
|
| }
|
|
|
| - case Token::SAR: {
|
| + case Token::SAR:
|
| if (reversed) {
|
| Result constant_operand(value);
|
| LikelySmiBinaryOperation(op, &constant_operand, operand,
|
| @@ -1608,23 +1624,26 @@
|
| // Only the least significant 5 bits of the shift value are used.
|
| // In the slow case, this masking is done inside the runtime call.
|
| int shift_value = int_value & 0x1f;
|
| - DeferredCode* deferred =
|
| - new DeferredInlineSmiOperation(op, smi_value, overwrite_mode);
|
| operand->ToRegister();
|
| + frame_->Spill(operand->reg());
|
| + DeferredInlineSmiOperation* deferred =
|
| + new DeferredInlineSmiOperation(op,
|
| + operand->reg(),
|
| + operand->reg(),
|
| + smi_value,
|
| + overwrite_mode);
|
| __ test(operand->reg(), Immediate(kSmiTagMask));
|
| - deferred->enter()->Branch(not_zero, operand, not_taken);
|
| + deferred->Branch(not_zero);
|
| if (shift_value > 0) {
|
| - frame_->Spill(operand->reg());
|
| __ sar(operand->reg(), shift_value);
|
| __ and_(operand->reg(), ~kSmiTagMask);
|
| }
|
| - deferred->BindExit(operand);
|
| + deferred->BindExit();
|
| frame_->Push(operand);
|
| }
|
| break;
|
| - }
|
|
|
| - case Token::SHR: {
|
| + case Token::SHR:
|
| if (reversed) {
|
| Result constant_operand(value);
|
| LikelySmiBinaryOperation(op, &constant_operand, operand,
|
| @@ -1633,32 +1652,35 @@
|
| // Only the least significant 5 bits of the shift value are used.
|
| // In the slow case, this masking is done inside the runtime call.
|
| int shift_value = int_value & 0x1f;
|
| - DeferredCode* deferred =
|
| - new DeferredInlineSmiOperation(op, smi_value, overwrite_mode);
|
| operand->ToRegister();
|
| - __ test(operand->reg(), Immediate(kSmiTagMask));
|
| - deferred->enter()->Branch(not_zero, operand, not_taken);
|
| Result answer = allocator()->Allocate();
|
| ASSERT(answer.is_valid());
|
| + DeferredInlineSmiOperation* deferred =
|
| + new DeferredInlineSmiOperation(op,
|
| + answer.reg(),
|
| + operand->reg(),
|
| + smi_value,
|
| + overwrite_mode);
|
| + __ test(operand->reg(), Immediate(kSmiTagMask));
|
| + deferred->Branch(not_zero);
|
| __ 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->Branch(not_zero);
|
| }
|
| operand->Unuse();
|
| ASSERT(kSmiTagSize == times_2); // Adjust the code if not true.
|
| __ lea(answer.reg(),
|
| Operand(answer.reg(), answer.reg(), times_1, kSmiTag));
|
| - deferred->BindExit(&answer);
|
| + deferred->BindExit();
|
| frame_->Push(&answer);
|
| }
|
| break;
|
| - }
|
|
|
| - case Token::SHL: {
|
| + case Token::SHL:
|
| if (reversed) {
|
| Result constant_operand(value);
|
| LikelySmiBinaryOperation(op, &constant_operand, operand,
|
| @@ -1667,14 +1689,30 @@
|
| // Only the least significant 5 bits of the shift value are used.
|
| // In the slow case, this masking is done inside the runtime call.
|
| int shift_value = int_value & 0x1f;
|
| - DeferredCode* deferred =
|
| - new DeferredInlineSmiOperation(op, smi_value, overwrite_mode);
|
| operand->ToRegister();
|
| - __ test(operand->reg(), Immediate(kSmiTagMask));
|
| - deferred->enter()->Branch(not_zero, operand, not_taken);
|
| - if (shift_value != 0) {
|
| + if (shift_value == 0) {
|
| + DeferredInlineSmiOperation* deferred =
|
| + new DeferredInlineSmiOperation(op,
|
| + operand->reg(),
|
| + operand->reg(),
|
| + smi_value,
|
| + overwrite_mode);
|
| + __ test(operand->reg(), Immediate(kSmiTagMask));
|
| + deferred->Branch(not_zero);
|
| + deferred->BindExit();
|
| + frame_->Push(operand);
|
| + } else {
|
| + // Use a fresh temporary for nonzero shift values.
|
| Result answer = allocator()->Allocate();
|
| ASSERT(answer.is_valid());
|
| + DeferredInlineSmiOperation* deferred =
|
| + new DeferredInlineSmiOperation(op,
|
| + answer.reg(),
|
| + operand->reg(),
|
| + smi_value,
|
| + overwrite_mode);
|
| + __ test(operand->reg(), Immediate(kSmiTagMask));
|
| + deferred->Branch(not_zero);
|
| __ 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.
|
| @@ -1682,35 +1720,37 @@
|
| __ shl(answer.reg(), shift_value - 1);
|
| }
|
| // Convert int result to Smi, checking that it is in int range.
|
| - ASSERT(kSmiTagSize == times_2); // adjust code if not the case
|
| + ASSERT(kSmiTagSize == 1); // adjust code if not the case
|
| __ add(answer.reg(), Operand(answer.reg()));
|
| - deferred->enter()->Branch(overflow, operand, not_taken);
|
| + deferred->Branch(overflow);
|
| + deferred->BindExit();
|
| operand->Unuse();
|
| - deferred->BindExit(&answer);
|
| frame_->Push(&answer);
|
| - } else {
|
| - deferred->BindExit(operand);
|
| - frame_->Push(operand);
|
| }
|
| }
|
| break;
|
| - }
|
|
|
| case Token::BIT_OR:
|
| case Token::BIT_XOR:
|
| case Token::BIT_AND: {
|
| + operand->ToRegister();
|
| + frame_->Spill(operand->reg());
|
| DeferredCode* deferred = NULL;
|
| if (reversed) {
|
| - deferred = new DeferredInlineSmiOperationReversed(op, smi_value,
|
| + deferred = new DeferredInlineSmiOperationReversed(op,
|
| + operand->reg(),
|
| + smi_value,
|
| + operand->reg(),
|
| overwrite_mode);
|
| } else {
|
| - deferred = new DeferredInlineSmiOperation(op, smi_value,
|
| + deferred = new DeferredInlineSmiOperation(op,
|
| + operand->reg(),
|
| + operand->reg(),
|
| + smi_value,
|
| overwrite_mode);
|
| }
|
| - operand->ToRegister();
|
| __ test(operand->reg(), Immediate(kSmiTagMask));
|
| - deferred->enter()->Branch(not_zero, operand, not_taken);
|
| - frame_->Spill(operand->reg());
|
| + deferred->Branch(not_zero);
|
| if (op == Token::BIT_AND) {
|
| __ and_(Operand(operand->reg()), Immediate(value));
|
| } else if (op == Token::BIT_XOR) {
|
| @@ -1723,7 +1763,7 @@
|
| __ or_(Operand(operand->reg()), Immediate(value));
|
| }
|
| }
|
| - deferred->BindExit(operand);
|
| + deferred->BindExit();
|
| frame_->Push(operand);
|
| break;
|
| }
|
| @@ -1990,7 +2030,7 @@
|
|
|
| class DeferredStackCheck: public DeferredCode {
|
| public:
|
| - explicit DeferredStackCheck() {
|
| + DeferredStackCheck() {
|
| set_comment("[ DeferredStackCheck");
|
| }
|
|
|
| @@ -1999,11 +2039,8 @@
|
|
|
|
|
| void DeferredStackCheck::Generate() {
|
| - enter()->Bind();
|
| StackCheckStub stub;
|
| - Result ignored = cgen()->frame()->CallStub(&stub, 0);
|
| - ignored.Unuse();
|
| - exit_.Jump();
|
| + __ CallStub(&stub);
|
| }
|
|
|
|
|
| @@ -2013,7 +2050,7 @@
|
| ExternalReference stack_guard_limit =
|
| ExternalReference::address_of_stack_guard_limit();
|
| __ cmp(esp, Operand::StaticVariable(stack_guard_limit));
|
| - deferred->enter()->Branch(below, not_taken);
|
| + deferred->Branch(below);
|
| deferred->BindExit();
|
| }
|
| }
|
| @@ -3865,43 +3902,45 @@
|
| }
|
|
|
|
|
| +// Materialize the regexp literal 'node' in the literals array
|
| +// 'literals' of the function. Leave the regexp boilerplate in
|
| +// 'boilerplate'.
|
| class DeferredRegExpLiteral: public DeferredCode {
|
| public:
|
| - explicit DeferredRegExpLiteral(RegExpLiteral* node) : node_(node) {
|
| + DeferredRegExpLiteral(Register boilerplate,
|
| + Register literals,
|
| + RegExpLiteral* node)
|
| + : boilerplate_(boilerplate), literals_(literals), node_(node) {
|
| set_comment("[ DeferredRegExpLiteral");
|
| }
|
|
|
| - virtual void Generate();
|
| + void Generate();
|
|
|
| private:
|
| + Register boilerplate_;
|
| + Register literals_;
|
| RegExpLiteral* node_;
|
| };
|
|
|
|
|
| void DeferredRegExpLiteral::Generate() {
|
| - Result literals;
|
| - enter()->Bind(&literals);
|
| // Since the entry is undefined we call the runtime system to
|
| // compute the literal.
|
| -
|
| - VirtualFrame* frame = cgen()->frame();
|
| // Literal array (0).
|
| - frame->Push(&literals);
|
| + __ push(literals_);
|
| // Literal index (1).
|
| - frame->Push(Smi::FromInt(node_->literal_index()));
|
| + __ push(Immediate(Smi::FromInt(node_->literal_index())));
|
| // RegExp pattern (2).
|
| - frame->Push(node_->pattern());
|
| + __ push(Immediate(node_->pattern()));
|
| // RegExp flags (3).
|
| - frame->Push(node_->flags());
|
| - Result boilerplate =
|
| - frame->CallRuntime(Runtime::kMaterializeRegExpLiteral, 4);
|
| - exit_.Jump(&boilerplate);
|
| + __ push(Immediate(node_->flags()));
|
| + __ CallRuntime(Runtime::kMaterializeRegExpLiteral, 4);
|
| + if (!boilerplate_.is(eax)) __ mov(boilerplate_, eax);
|
| }
|
|
|
|
|
| void CodeGenerator::VisitRegExpLiteral(RegExpLiteral* node) {
|
| Comment cmnt(masm_, "[ RegExp Literal");
|
| - DeferredRegExpLiteral* deferred = new DeferredRegExpLiteral(node);
|
|
|
| // Retrieve the literals array and check the allocated entry. Begin
|
| // with a writable copy of the function of this activation in a
|
| @@ -3916,65 +3955,63 @@
|
| FieldOperand(literals.reg(), JSFunction::kLiteralsOffset));
|
|
|
| // Load the literal at the ast saved index.
|
| + Result boilerplate = allocator_->Allocate();
|
| + ASSERT(boilerplate.is_valid());
|
| int literal_offset =
|
| FixedArray::kHeaderSize + node->literal_index() * kPointerSize;
|
| - Result boilerplate = allocator_->Allocate();
|
| - ASSERT(boilerplate.is_valid());
|
| __ mov(boilerplate.reg(), FieldOperand(literals.reg(), literal_offset));
|
|
|
| // Check whether we need to materialize the RegExp object. If so,
|
| // jump to the deferred code passing the literals array.
|
| + DeferredRegExpLiteral* deferred =
|
| + new DeferredRegExpLiteral(boilerplate.reg(), literals.reg(), node);
|
| __ cmp(boilerplate.reg(), Factory::undefined_value());
|
| - deferred->enter()->Branch(equal, &literals, not_taken);
|
| -
|
| + deferred->Branch(equal);
|
| + deferred->BindExit();
|
| literals.Unuse();
|
| - // The deferred code returns the boilerplate object.
|
| - deferred->BindExit(&boilerplate);
|
|
|
| // Push the boilerplate object.
|
| frame_->Push(&boilerplate);
|
| }
|
|
|
|
|
| -// This deferred code stub will be used for creating the boilerplate
|
| -// by calling Runtime_CreateObjectLiteral.
|
| -// Each created boilerplate is stored in the JSFunction and they are
|
| -// therefore context dependent.
|
| +// Materialize the object literal 'node' in the literals array
|
| +// 'literals' of the function. Leave the object boilerplate in
|
| +// 'boilerplate'.
|
| class DeferredObjectLiteral: public DeferredCode {
|
| public:
|
| - explicit DeferredObjectLiteral(ObjectLiteral* node) : node_(node) {
|
| + DeferredObjectLiteral(Register boilerplate,
|
| + Register literals,
|
| + ObjectLiteral* node)
|
| + : boilerplate_(boilerplate), literals_(literals), node_(node) {
|
| set_comment("[ DeferredObjectLiteral");
|
| }
|
|
|
| - virtual void Generate();
|
| + void Generate();
|
|
|
| private:
|
| + Register boilerplate_;
|
| + Register literals_;
|
| ObjectLiteral* node_;
|
| };
|
|
|
|
|
| void DeferredObjectLiteral::Generate() {
|
| - Result literals;
|
| - enter()->Bind(&literals);
|
| // Since the entry is undefined we call the runtime system to
|
| // compute the literal.
|
| -
|
| - VirtualFrame* frame = cgen()->frame();
|
| // Literal array (0).
|
| - frame->Push(&literals);
|
| + __ push(literals_);
|
| // Literal index (1).
|
| - frame->Push(Smi::FromInt(node_->literal_index()));
|
| + __ push(Immediate(Smi::FromInt(node_->literal_index())));
|
| // Constant properties (2).
|
| - frame->Push(node_->constant_properties());
|
| - Result boilerplate =
|
| - frame->CallRuntime(Runtime::kCreateObjectLiteralBoilerplate, 3);
|
| - exit_.Jump(&boilerplate);
|
| + __ push(Immediate(node_->constant_properties()));
|
| + __ CallRuntime(Runtime::kCreateObjectLiteralBoilerplate, 3);
|
| + if (!boilerplate_.is(eax)) __ mov(boilerplate_, eax);
|
| }
|
|
|
|
|
| void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) {
|
| Comment cmnt(masm_, "[ ObjectLiteral");
|
| - DeferredObjectLiteral* deferred = new DeferredObjectLiteral(node);
|
|
|
| // Retrieve the literals array and check the allocated entry. Begin
|
| // with a writable copy of the function of this activation in a
|
| @@ -3989,20 +4026,20 @@
|
| FieldOperand(literals.reg(), JSFunction::kLiteralsOffset));
|
|
|
| // Load the literal at the ast saved index.
|
| + Result boilerplate = allocator_->Allocate();
|
| + ASSERT(boilerplate.is_valid());
|
| int literal_offset =
|
| FixedArray::kHeaderSize + node->literal_index() * kPointerSize;
|
| - Result boilerplate = allocator_->Allocate();
|
| - ASSERT(boilerplate.is_valid());
|
| __ mov(boilerplate.reg(), FieldOperand(literals.reg(), literal_offset));
|
|
|
| // Check whether we need to materialize the object literal boilerplate.
|
| // If so, jump to the deferred code passing the literals array.
|
| + DeferredObjectLiteral* deferred =
|
| + new DeferredObjectLiteral(boilerplate.reg(), literals.reg(), node);
|
| __ cmp(boilerplate.reg(), Factory::undefined_value());
|
| - deferred->enter()->Branch(equal, &literals, not_taken);
|
| -
|
| + deferred->Branch(equal);
|
| + deferred->BindExit();
|
| literals.Unuse();
|
| - // The deferred code returns the boilerplate object.
|
| - deferred->BindExit(&boilerplate);
|
|
|
| // Push the boilerplate object.
|
| frame_->Push(&boilerplate);
|
| @@ -4072,45 +4109,42 @@
|
| }
|
|
|
|
|
| -// This deferred code stub will be used for creating the boilerplate
|
| -// by calling Runtime_CreateArrayLiteralBoilerplate.
|
| -// Each created boilerplate is stored in the JSFunction and they are
|
| -// therefore context dependent.
|
| +// Materialize the array literal 'node' in the literals array 'literals'
|
| +// of the function. Leave the array boilerplate in 'boilerplate'.
|
| class DeferredArrayLiteral: public DeferredCode {
|
| public:
|
| - explicit DeferredArrayLiteral(ArrayLiteral* node) : node_(node) {
|
| + DeferredArrayLiteral(Register boilerplate,
|
| + Register literals,
|
| + ArrayLiteral* node)
|
| + : boilerplate_(boilerplate), literals_(literals), node_(node) {
|
| set_comment("[ DeferredArrayLiteral");
|
| }
|
|
|
| - virtual void Generate();
|
| + void Generate();
|
|
|
| private:
|
| + Register boilerplate_;
|
| + Register literals_;
|
| ArrayLiteral* node_;
|
| };
|
|
|
|
|
| void DeferredArrayLiteral::Generate() {
|
| - Result literals;
|
| - enter()->Bind(&literals);
|
| // Since the entry is undefined we call the runtime system to
|
| // compute the literal.
|
| -
|
| - VirtualFrame* frame = cgen()->frame();
|
| // Literal array (0).
|
| - frame->Push(&literals);
|
| + __ push(literals_);
|
| // Literal index (1).
|
| - frame->Push(Smi::FromInt(node_->literal_index()));
|
| + __ push(Immediate(Smi::FromInt(node_->literal_index())));
|
| // Constant properties (2).
|
| - frame->Push(node_->literals());
|
| - Result boilerplate =
|
| - frame->CallRuntime(Runtime::kCreateArrayLiteralBoilerplate, 3);
|
| - exit_.Jump(&boilerplate);
|
| + __ push(Immediate(node_->literals()));
|
| + __ CallRuntime(Runtime::kCreateArrayLiteralBoilerplate, 3);
|
| + if (!boilerplate_.is(eax)) __ mov(boilerplate_, eax);
|
| }
|
|
|
|
|
| void CodeGenerator::VisitArrayLiteral(ArrayLiteral* node) {
|
| Comment cmnt(masm_, "[ ArrayLiteral");
|
| - DeferredArrayLiteral* deferred = new DeferredArrayLiteral(node);
|
|
|
| // Retrieve the literals array and check the allocated entry. Begin
|
| // with a writable copy of the function of this activation in a
|
| @@ -4125,24 +4159,23 @@
|
| FieldOperand(literals.reg(), JSFunction::kLiteralsOffset));
|
|
|
| // Load the literal at the ast saved index.
|
| + Result boilerplate = allocator_->Allocate();
|
| + ASSERT(boilerplate.is_valid());
|
| int literal_offset =
|
| FixedArray::kHeaderSize + node->literal_index() * kPointerSize;
|
| - Result boilerplate = allocator_->Allocate();
|
| - ASSERT(boilerplate.is_valid());
|
| __ mov(boilerplate.reg(), FieldOperand(literals.reg(), literal_offset));
|
|
|
| // Check whether we need to materialize the object literal boilerplate.
|
| // If so, jump to the deferred code passing the literals array.
|
| + DeferredArrayLiteral* deferred =
|
| + new DeferredArrayLiteral(boilerplate.reg(), literals.reg(), node);
|
| __ cmp(boilerplate.reg(), Factory::undefined_value());
|
| - deferred->enter()->Branch(equal, &literals, not_taken);
|
| -
|
| + deferred->Branch(equal);
|
| + deferred->BindExit();
|
| literals.Unuse();
|
| - // The deferred code returns the boilerplate object.
|
| - deferred->BindExit(&boilerplate);
|
|
|
| - // Push the resulting array literal on the stack.
|
| + // Push the resulting array literal boilerplate on the stack.
|
| frame_->Push(&boilerplate);
|
| -
|
| // Clone the boilerplate object.
|
| Runtime::FunctionId clone_function_id = Runtime::kCloneLiteralBoilerplate;
|
| if (node->depth() == 1) {
|
| @@ -5063,65 +5096,92 @@
|
| }
|
|
|
|
|
| -class DeferredCountOperation: public DeferredCode {
|
| +// The value in dst was optimistically incremented or decremented. The
|
| +// result overflowed or was not smi tagged. Undo the operation, call
|
| +// into the runtime to convert the argument to a number, and call the
|
| +// specialized add or subtract stub. The result is left in dst.
|
| +class DeferredPrefixCountOperation: public DeferredCode {
|
| public:
|
| - DeferredCountOperation(bool is_postfix,
|
| - bool is_increment,
|
| - int target_size)
|
| - : is_postfix_(is_postfix),
|
| - is_increment_(is_increment),
|
| - target_size_(target_size) {
|
| + DeferredPrefixCountOperation(Register dst, bool is_increment)
|
| + : dst_(dst), is_increment_(is_increment) {
|
| set_comment("[ DeferredCountOperation");
|
| }
|
|
|
| virtual void Generate();
|
|
|
| private:
|
| - bool is_postfix_;
|
| + Register dst_;
|
| bool is_increment_;
|
| - int target_size_;
|
| };
|
|
|
|
|
| -#undef __
|
| -#define __ ACCESS_MASM(cgen()->masm())
|
| +void DeferredPrefixCountOperation::Generate() {
|
| + // Undo the optimistic smi operation.
|
| + if (is_increment_) {
|
| + __ sub(Operand(dst_), Immediate(Smi::FromInt(1)));
|
| + } else {
|
| + __ add(Operand(dst_), Immediate(Smi::FromInt(1)));
|
| + }
|
| + __ push(dst_);
|
| + __ InvokeBuiltin(Builtins::TO_NUMBER, CALL_FUNCTION);
|
| + __ push(eax);
|
| + __ push(Immediate(Smi::FromInt(1)));
|
| + if (is_increment_) {
|
| + __ CallRuntime(Runtime::kNumberAdd, 2);
|
| + } else {
|
| + __ CallRuntime(Runtime::kNumberSub, 2);
|
| + }
|
| + if (!dst_.is(eax)) __ mov(dst_, eax);
|
| +}
|
|
|
|
|
| -void DeferredCountOperation::Generate() {
|
| - Result value;
|
| - enter()->Bind(&value);
|
| - VirtualFrame* frame = cgen()->frame();
|
| +// The value in dst was optimistically incremented or decremented. The
|
| +// result overflowed or was not smi tagged. Undo the operation and call
|
| +// into the runtime to convert the argument to a number. Update the
|
| +// original value in old. Call the specialized add or subtract stub.
|
| +// The result is left in dst.
|
| +class DeferredPostfixCountOperation: public DeferredCode {
|
| + public:
|
| + DeferredPostfixCountOperation(Register dst, Register old, bool is_increment)
|
| + : dst_(dst), old_(old), is_increment_(is_increment) {
|
| + set_comment("[ DeferredCountOperation");
|
| + }
|
| +
|
| + virtual void Generate();
|
| +
|
| + private:
|
| + Register dst_;
|
| + Register old_;
|
| + bool is_increment_;
|
| +};
|
| +
|
| +
|
| +void DeferredPostfixCountOperation::Generate() {
|
| // Undo the optimistic smi operation.
|
| - value.ToRegister();
|
| - frame->Spill(value.reg());
|
| if (is_increment_) {
|
| - __ sub(Operand(value.reg()), Immediate(Smi::FromInt(1)));
|
| + __ sub(Operand(dst_), Immediate(Smi::FromInt(1)));
|
| } else {
|
| - __ add(Operand(value.reg()), Immediate(Smi::FromInt(1)));
|
| + __ add(Operand(dst_), Immediate(Smi::FromInt(1)));
|
| }
|
| - frame->Push(&value);
|
| - value = frame->InvokeBuiltin(Builtins::TO_NUMBER, CALL_FUNCTION, 1);
|
| - frame->Push(&value);
|
| - if (is_postfix_) { // Fix up copy of old value with ToNumber(value).
|
| - // This is only safe because VisitCountOperation makes this frame slot
|
| - // beneath the reference a register, which is spilled at the above call.
|
| - // We cannot safely write to constants or copies below the water line.
|
| - frame->StoreToElementAt(target_size_ + 1);
|
| - }
|
| - frame->Push(Smi::FromInt(1));
|
| + __ push(dst_);
|
| + __ InvokeBuiltin(Builtins::TO_NUMBER, CALL_FUNCTION);
|
| +
|
| + // Save the result of ToNumber to use as the old value.
|
| + __ push(eax);
|
| +
|
| + // Call the runtime for the addition or subtraction.
|
| + __ push(eax);
|
| + __ push(Immediate(Smi::FromInt(1)));
|
| if (is_increment_) {
|
| - value = frame->CallRuntime(Runtime::kNumberAdd, 2);
|
| + __ CallRuntime(Runtime::kNumberAdd, 2);
|
| } else {
|
| - value = frame->CallRuntime(Runtime::kNumberSub, 2);
|
| + __ CallRuntime(Runtime::kNumberSub, 2);
|
| }
|
| - exit_.Jump(&value);
|
| + if (!dst_.is(eax)) __ mov(dst_, eax);
|
| + __ pop(old_);
|
| }
|
|
|
|
|
| -#undef __
|
| -#define __ ACCESS_MASM(masm_)
|
| -
|
| -
|
| void CodeGenerator::VisitCountOperation(CountOperation* node) {
|
| Comment cmnt(masm_, "[ CountOperation");
|
|
|
| @@ -5131,96 +5191,93 @@
|
| Variable* var = node->expression()->AsVariableProxy()->AsVariable();
|
| bool is_const = (var != NULL && var->mode() == Variable::CONST);
|
|
|
| - // Postfix operators need a stack slot under the reference to hold
|
| - // the old value while the new one is being stored.
|
| - if (is_postfix) {
|
| - frame_->Push(Smi::FromInt(0));
|
| - }
|
| + // Postfix operations need a stack slot under the reference to hold
|
| + // the old value while the new value is being stored. This is so that
|
| + // in the case that storing the new value requires a call, the old
|
| + // value will be in the frame to be spilled.
|
| + if (is_postfix) frame_->Push(Smi::FromInt(0));
|
|
|
| { Reference target(this, node->expression());
|
| if (target.is_illegal()) {
|
| // Spoof the virtual frame to have the expected height (one higher
|
| // than on entry).
|
| - if (!is_postfix) {
|
| - frame_->Push(Smi::FromInt(0));
|
| - }
|
| + if (!is_postfix) frame_->Push(Smi::FromInt(0));
|
| return;
|
| }
|
| target.TakeValue(NOT_INSIDE_TYPEOF);
|
|
|
| - DeferredCountOperation* deferred =
|
| - new DeferredCountOperation(is_postfix, is_increment, target.size());
|
| + Result new_value = frame_->Pop();
|
| + new_value.ToRegister();
|
|
|
| - Result value = frame_->Pop();
|
| - value.ToRegister();
|
| -
|
| - // Postfix: Store the old value as the result.
|
| + Result old_value; // Only allocated in the postfix case.
|
| if (is_postfix) {
|
| - // Explicitly back the slot for the old value with a new register.
|
| - // This improves performance in some cases.
|
| - Result old_value = allocator_->Allocate();
|
| + // Allocate a temporary to preserve the old value.
|
| + old_value = allocator_->Allocate();
|
| ASSERT(old_value.is_valid());
|
| - __ mov(old_value.reg(), value.reg());
|
| - // SetElement must not create a constant element or a copy in this slot,
|
| - // since we will write to it, below the waterline, in deferred code.
|
| - frame_->SetElementAt(target.size(), &old_value);
|
| + __ mov(old_value.reg(), new_value.reg());
|
| }
|
| + // Ensure the new value is writable.
|
| + frame_->Spill(new_value.reg());
|
|
|
| - // Perform optimistic increment/decrement. Ensure the value is
|
| - // writable.
|
| - frame_->Spill(value.reg());
|
| - ASSERT(allocator_->count(value.reg()) == 1);
|
| -
|
| - // In order to combine the overflow and the smi check, we need to
|
| - // be able to allocate a byte register. We attempt to do so
|
| - // without spilling. If we fail, we will generate separate
|
| - // overflow and smi checks.
|
| + // In order to combine the overflow and the smi tag check, we need
|
| + // to be able to allocate a byte register. We attempt to do so
|
| + // without spilling. If we fail, we will generate separate overflow
|
| + // and smi tag checks.
|
| //
|
| - // We need to allocate and clear the temporary byte register
|
| - // before performing the count operation since clearing the
|
| - // register using xor will clear the overflow flag.
|
| + // We allocate and clear the temporary byte register before
|
| + // performing the count operation since clearing the register using
|
| + // xor will clear the overflow flag.
|
| Result tmp = allocator_->AllocateByteRegisterWithoutSpilling();
|
| if (tmp.is_valid()) {
|
| __ Set(tmp.reg(), Immediate(0));
|
| }
|
|
|
| + DeferredCode* deferred = NULL;
|
| + if (is_postfix) {
|
| + deferred = new DeferredPostfixCountOperation(new_value.reg(),
|
| + old_value.reg(),
|
| + is_increment);
|
| + } else {
|
| + deferred = new DeferredPrefixCountOperation(new_value.reg(),
|
| + is_increment);
|
| + }
|
| +
|
| if (is_increment) {
|
| - __ add(Operand(value.reg()), Immediate(Smi::FromInt(1)));
|
| + __ add(Operand(new_value.reg()), Immediate(Smi::FromInt(1)));
|
| } else {
|
| - __ sub(Operand(value.reg()), Immediate(Smi::FromInt(1)));
|
| + __ sub(Operand(new_value.reg()), Immediate(Smi::FromInt(1)));
|
| }
|
|
|
| - // If the count operation didn't overflow and the result is a
|
| - // valid smi, we're done. Otherwise, we jump to the deferred
|
| - // slow-case code.
|
| - //
|
| - // We combine the overflow and the smi check if we could
|
| - // successfully allocate a temporary byte register.
|
| + // If the count operation didn't overflow and the result is a valid
|
| + // smi, we're done. Otherwise, we jump to the deferred slow-case
|
| + // code.
|
| if (tmp.is_valid()) {
|
| + // We combine the overflow and the smi tag check if we could
|
| + // successfully allocate a temporary byte register.
|
| __ setcc(overflow, tmp.reg());
|
| - __ or_(Operand(tmp.reg()), value.reg());
|
| + __ or_(Operand(tmp.reg()), new_value.reg());
|
| __ test(tmp.reg(), Immediate(kSmiTagMask));
|
| tmp.Unuse();
|
| - deferred->enter()->Branch(not_zero, &value, not_taken);
|
| - } else { // Otherwise we test separately for overflow and smi check.
|
| - deferred->SetEntryFrame(&value);
|
| - deferred->enter()->Branch(overflow, &value, not_taken);
|
| - __ test(value.reg(), Immediate(kSmiTagMask));
|
| - deferred->enter()->Branch(not_zero, &value, not_taken);
|
| + deferred->Branch(not_zero);
|
| + } else {
|
| + // Otherwise we test separately for overflow and smi tag.
|
| + deferred->Branch(overflow);
|
| + __ test(new_value.reg(), Immediate(kSmiTagMask));
|
| + deferred->Branch(not_zero);
|
| }
|
| + deferred->BindExit();
|
|
|
| - // Store the new value in the target if not const.
|
| - deferred->BindExit(&value);
|
| - frame_->Push(&value);
|
| - if (!is_const) {
|
| - target.SetValue(NOT_CONST_INIT);
|
| - }
|
| + // Postfix: store the old value in the allocated slot under the
|
| + // reference.
|
| + if (is_postfix) frame_->SetElementAt(target.size(), &old_value);
|
| +
|
| + frame_->Push(&new_value);
|
| + // Non-constant: update the reference.
|
| + if (!is_const) target.SetValue(NOT_CONST_INIT);
|
| }
|
|
|
| - // Postfix: Discard the new value and use the old.
|
| - if (is_postfix) {
|
| - frame_->Drop();
|
| - }
|
| + // Postfix: drop the new value and use the old.
|
| + if (is_postfix) frame_->Drop();
|
| }
|
|
|
|
|
| @@ -5571,9 +5628,14 @@
|
| #endif
|
|
|
|
|
| +// Emit a LoadIC call to get the value from receiver and leave it in
|
| +// dst. The receiver register is restored after the call.
|
| class DeferredReferenceGetNamedValue: public DeferredCode {
|
| public:
|
| - explicit DeferredReferenceGetNamedValue(Handle<String> name) : name_(name) {
|
| + DeferredReferenceGetNamedValue(Register dst,
|
| + Register receiver,
|
| + Handle<String> name)
|
| + : dst_(dst), receiver_(receiver), name_(name) {
|
| set_comment("[ DeferredReferenceGetNamedValue");
|
| }
|
|
|
| @@ -5583,14 +5645,41 @@
|
|
|
| private:
|
| Label patch_site_;
|
| + Register dst_;
|
| + Register receiver_;
|
| Handle<String> name_;
|
| };
|
|
|
|
|
| +void DeferredReferenceGetNamedValue::Generate() {
|
| + __ push(receiver_);
|
| + __ Set(ecx, Immediate(name_));
|
| + Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize));
|
| + __ call(ic, RelocInfo::CODE_TARGET);
|
| + // The call must be followed by a test eax instruction to indicate
|
| + // that the inobject property case was inlined.
|
| + //
|
| + // Store the delta to the map check instruction here in the test
|
| + // instruction. Use masm_-> instead of the __ macro since the
|
| + // latter can't return a value.
|
| + int delta_to_patch_site = masm_->SizeOfCodeGeneratedSince(patch_site());
|
| + // Here we use masm_-> instead of the __ macro because this is the
|
| + // instruction that gets patched and coverage code gets in the way.
|
| + masm_->test(eax, Immediate(-delta_to_patch_site));
|
| + __ IncrementCounter(&Counters::named_load_inline_miss, 1);
|
| +
|
| + if (!dst_.is(eax)) __ mov(dst_, eax);
|
| + __ pop(receiver_);
|
| +}
|
| +
|
| +
|
| class DeferredReferenceGetKeyedValue: public DeferredCode {
|
| public:
|
| - explicit DeferredReferenceGetKeyedValue(bool is_global)
|
| - : is_global_(is_global) {
|
| + explicit DeferredReferenceGetKeyedValue(Register dst,
|
| + Register receiver,
|
| + Register key,
|
| + bool is_global)
|
| + : dst_(dst), receiver_(receiver), key_(key), is_global_(is_global) {
|
| set_comment("[ DeferredReferenceGetKeyedValue");
|
| }
|
|
|
| @@ -5600,45 +5689,16 @@
|
|
|
| private:
|
| Label patch_site_;
|
| + Register dst_;
|
| + Register receiver_;
|
| + Register key_;
|
| bool is_global_;
|
| };
|
|
|
|
|
| -#undef __
|
| -#define __ ACCESS_MASM(cgen()->masm())
|
| -
|
| -
|
| -void DeferredReferenceGetNamedValue::Generate() {
|
| - Result receiver;
|
| - enter()->Bind(&receiver);
|
| -
|
| - cgen()->frame()->Push(&receiver);
|
| - cgen()->frame()->Push(name_);
|
| - Result answer = cgen()->frame()->CallLoadIC(RelocInfo::CODE_TARGET);
|
| - // The call must be followed by a test eax instruction to indicate
|
| - // that the inobject property case was inlined.
|
| - ASSERT(answer.is_register() && answer.reg().is(eax));
|
| - // Store the delta to the map check instruction here in the test
|
| - // instruction. Use cgen()->masm()-> instead of the __ macro since
|
| - // the latter can't return a value.
|
| - int delta_to_patch_site =
|
| - cgen()->masm()->SizeOfCodeGeneratedSince(patch_site());
|
| - // Here we use cgen()->masm()-> instead of the __ macro because this
|
| - // is the instruction that gets patched and coverage code gets in the
|
| - // way.
|
| - cgen()->masm()->test(answer.reg(), Immediate(-delta_to_patch_site));
|
| - __ IncrementCounter(&Counters::named_load_inline_miss, 1);
|
| - receiver = cgen()->frame()->Pop();
|
| - exit_.Jump(&receiver, &answer);
|
| -}
|
| -
|
| -
|
| void DeferredReferenceGetKeyedValue::Generate() {
|
| - Result receiver;
|
| - Result key;
|
| - enter()->Bind(&receiver, &key);
|
| - cgen()->frame()->Push(&receiver); // First IC argument.
|
| - cgen()->frame()->Push(&key); // Second IC argument.
|
| + __ push(receiver_); // First IC argument.
|
| + __ push(key_); // Second IC argument.
|
|
|
| // Calculate the delta from the IC call instruction to the map check
|
| // cmp instruction in the inlined version. This delta is stored in
|
| @@ -5646,34 +5706,25 @@
|
| // it in the IC initialization code and patch the cmp instruction.
|
| // This means that we cannot allow test instructions after calls to
|
| // KeyedLoadIC stubs in other places.
|
| + Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
|
| RelocInfo::Mode mode = is_global_
|
| ? RelocInfo::CODE_TARGET_CONTEXT
|
| : RelocInfo::CODE_TARGET;
|
| - Result value = cgen()->frame()->CallKeyedLoadIC(mode);
|
| - // The result needs to be specifically the eax register because the
|
| - // offset to the patch site will be expected in a test eax
|
| - // instruction.
|
| - ASSERT(value.is_register() && value.reg().is(eax));
|
| - // The delta from the start of the map-compare instruction to the test
|
| - // instruction. We use cgen()->masm() directly here instead of the __
|
| - // macro because the macro sometimes uses macro expansion to turn into
|
| - // something that can't return a value. This is encountered when
|
| - // doing generated code coverage tests.
|
| - int delta_to_patch_site =
|
| - cgen()->masm()->SizeOfCodeGeneratedSince(patch_site());
|
| - // Here we use cgen()->masm()-> instead of the __ macro because this
|
| - // is the instruction that gets patched and coverage code gets in the
|
| - // way.
|
| - cgen()->masm()->test(value.reg(), Immediate(-delta_to_patch_site));
|
| + __ call(ic, mode);
|
| + // The delta from the start of the map-compare instruction to the
|
| + // test instruction. We use masm_-> directly here instead of the __
|
| + // macro because the macro sometimes uses macro expansion to turn
|
| + // into something that can't return a value. This is encountered
|
| + // when doing generated code coverage tests.
|
| + int delta_to_patch_site = masm_->SizeOfCodeGeneratedSince(patch_site());
|
| + // Here we use masm_-> instead of the __ macro because this is the
|
| + // instruction that gets patched and coverage code gets in the way.
|
| + masm_->test(eax, Immediate(-delta_to_patch_site));
|
| __ IncrementCounter(&Counters::keyed_load_inline_miss, 1);
|
|
|
| - // The receiver and key were spilled by the call, so their state as
|
| - // constants or copies has been changed. Thus, they need to be
|
| - // "mergable" in the block at the exit label and are therefore
|
| - // passed as return results here.
|
| - key = cgen()->frame()->Pop();
|
| - receiver = cgen()->frame()->Pop();
|
| - exit_.Jump(&receiver, &key, &value);
|
| + if (!dst_.is(eax)) __ mov(dst_, eax);
|
| + __ pop(key_);
|
| + __ pop(receiver_);
|
| }
|
|
|
|
|
| @@ -5744,30 +5795,20 @@
|
| } else {
|
| // Inline the inobject property case.
|
| Comment cmnt(masm, "[ Inlined named property load");
|
| - DeferredReferenceGetNamedValue* deferred =
|
| - new DeferredReferenceGetNamedValue(GetName());
|
| Result receiver = cgen_->frame()->Pop();
|
| receiver.ToRegister();
|
|
|
| - // Try to preallocate the value register so that all frames
|
| - // reaching the deferred code are identical.
|
| - Result value = cgen_->allocator()->AllocateWithoutSpilling();
|
| - if (value.is_valid()) {
|
| - deferred->SetEntryFrame(&receiver);
|
| - }
|
| + Result value = cgen_->allocator()->Allocate();
|
| + ASSERT(value.is_valid());
|
| + DeferredReferenceGetNamedValue* deferred =
|
| + new DeferredReferenceGetNamedValue(value.reg(),
|
| + receiver.reg(),
|
| + GetName());
|
|
|
| // Check that the receiver is a heap object.
|
| __ test(receiver.reg(), Immediate(kSmiTagMask));
|
| - deferred->enter()->Branch(zero, &receiver, not_taken);
|
| + deferred->Branch(zero);
|
|
|
| - // Do not allocate the value register after binding the patch
|
| - // site label. The distance from the patch site to the offset
|
| - // must be constant.
|
| - if (!value.is_valid()) {
|
| - value = cgen_->allocator()->Allocate();
|
| - ASSERT(value.is_valid());
|
| - }
|
| -
|
| __ bind(deferred->patch_site());
|
| // This is the map check instruction that will be patched (so we can't
|
| // use the double underscore macro that may insert instructions).
|
| @@ -5776,7 +5817,7 @@
|
| Immediate(Factory::null_value()));
|
| // This branch is always a forwards branch so it's always a fixed
|
| // size which allows the assert below to succeed and patching to work.
|
| - deferred->enter()->Branch(not_equal, &receiver, not_taken);
|
| + deferred->Branch(not_equal);
|
|
|
| // The delta from the patch label to the load offset must be
|
| // statically known.
|
| @@ -5789,7 +5830,7 @@
|
| masm->mov(value.reg(), FieldOperand(receiver.reg(), offset));
|
|
|
| __ IncrementCounter(&Counters::named_load_inline, 1);
|
| - deferred->BindExit(&receiver, &value);
|
| + deferred->BindExit();
|
| cgen_->frame()->Push(&receiver);
|
| cgen_->frame()->Push(&value);
|
| }
|
| @@ -5809,28 +5850,34 @@
|
| // patch the map check if appropriate.
|
| if (cgen_->loop_nesting() > 0) {
|
| Comment cmnt(masm, "[ Inlined array index load");
|
| - DeferredReferenceGetKeyedValue* deferred =
|
| - new DeferredReferenceGetKeyedValue(is_global);
|
|
|
| Result key = cgen_->frame()->Pop();
|
| Result receiver = cgen_->frame()->Pop();
|
| key.ToRegister();
|
| receiver.ToRegister();
|
|
|
| - // Try to preallocate the elements and index scratch registers
|
| - // so that all frames reaching the deferred code are identical.
|
| - Result elements = cgen_->allocator()->AllocateWithoutSpilling();
|
| - Result index = cgen_->allocator()->AllocateWithoutSpilling();
|
| - if (elements.is_valid() && index.is_valid()) {
|
| - deferred->SetEntryFrame(&receiver, &key);
|
| - }
|
| + // Use a fresh temporary to load the elements without destroying
|
| + // the receiver which is needed for the deferred slow case.
|
| + Result elements = cgen_->allocator()->Allocate();
|
| + ASSERT(elements.is_valid());
|
|
|
| + // Use a fresh temporary for the index and later the loaded
|
| + // value.
|
| + Result index = cgen_->allocator()->Allocate();
|
| + ASSERT(index.is_valid());
|
| +
|
| + DeferredReferenceGetKeyedValue* deferred =
|
| + new DeferredReferenceGetKeyedValue(index.reg(),
|
| + receiver.reg(),
|
| + key.reg(),
|
| + is_global);
|
| +
|
| // Check that the receiver is not a smi (only needed if this
|
| // is not a load from the global context) and that it has the
|
| // expected map.
|
| if (!is_global) {
|
| __ test(receiver.reg(), Immediate(kSmiTagMask));
|
| - deferred->enter()->Branch(zero, &receiver, &key, not_taken);
|
| + deferred->Branch(zero);
|
| }
|
|
|
| // Initially, use an invalid map. The map is patched in the IC
|
| @@ -5839,36 +5886,28 @@
|
| // Use masm-> here instead of the double underscore macro since extra
|
| // coverage code can interfere with the patching.
|
| masm->cmp(FieldOperand(receiver.reg(), HeapObject::kMapOffset),
|
| - Immediate(Factory::null_value()));
|
| - deferred->enter()->Branch(not_equal, &receiver, &key, not_taken);
|
| + Immediate(Factory::null_value()));
|
| + deferred->Branch(not_equal);
|
|
|
| // Check that the key is a smi.
|
| __ test(key.reg(), Immediate(kSmiTagMask));
|
| - deferred->enter()->Branch(not_zero, &receiver, &key, not_taken);
|
| + deferred->Branch(not_zero);
|
|
|
| // Get the elements array from the receiver and check that it
|
| // is not a dictionary.
|
| - if (!elements.is_valid()) {
|
| - elements = cgen_->allocator()->Allocate();
|
| - ASSERT(elements.is_valid());
|
| - }
|
| __ mov(elements.reg(),
|
| FieldOperand(receiver.reg(), JSObject::kElementsOffset));
|
| __ cmp(FieldOperand(elements.reg(), HeapObject::kMapOffset),
|
| Immediate(Factory::hash_table_map()));
|
| - deferred->enter()->Branch(equal, &receiver, &key, not_taken);
|
| + deferred->Branch(equal);
|
|
|
| // Shift the key to get the actual index value and check that
|
| // it is within bounds.
|
| - if (!index.is_valid()) {
|
| - index = cgen_->allocator()->Allocate();
|
| - ASSERT(index.is_valid());
|
| - }
|
| __ mov(index.reg(), key.reg());
|
| __ sar(index.reg(), kSmiTagSize);
|
| __ cmp(index.reg(),
|
| FieldOperand(elements.reg(), Array::kLengthOffset));
|
| - deferred->enter()->Branch(above_equal, &receiver, &key, not_taken);
|
| + deferred->Branch(above_equal);
|
|
|
| // Load and check that the result is not the hole. We could
|
| // reuse the index or elements register for the value.
|
| @@ -5885,12 +5924,12 @@
|
| elements.Unuse();
|
| index.Unuse();
|
| __ cmp(Operand(value.reg()), Immediate(Factory::the_hole_value()));
|
| - deferred->enter()->Branch(equal, &receiver, &key, not_taken);
|
| + deferred->Branch(equal);
|
| __ IncrementCounter(&Counters::keyed_load_inline, 1);
|
|
|
| + deferred->BindExit();
|
| // Restore the receiver and key to the frame and push the
|
| // result on top of it.
|
| - deferred->BindExit(&receiver, &key, &value);
|
| cgen_->frame()->Push(&receiver);
|
| cgen_->frame()->Push(&key);
|
| cgen_->frame()->Push(&value);
|
|
|