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