Index: src/ia32/full-codegen-ia32.cc |
=================================================================== |
--- src/ia32/full-codegen-ia32.cc (revision 5348) |
+++ src/ia32/full-codegen-ia32.cc (working copy) |
@@ -217,12 +217,28 @@ |
// Check that the size of the code used for returning matches what is |
// expected by the debugger. |
ASSERT_EQ(Assembler::kJSReturnSequenceLength, |
- masm_->SizeOfCodeGeneratedSince(&check_exit_codesize)); |
+ masm_->SizeOfCodeGeneratedSince(&check_exit_codesize)); |
#endif |
} |
} |
+FullCodeGenerator::ConstantOperand FullCodeGenerator::GetConstantOperand( |
+ Token::Value op, Expression* left, Expression* right) { |
+ ASSERT(ShouldInlineSmiCase(op)); |
+ if (op == Token::DIV || op == Token::MOD || op == Token::MUL) { |
+ // We never generate inlined constant smi operations for these. |
+ return kNoConstants; |
+ } else if (right->IsSmiLiteral()) { |
+ return kRightConstant; |
+ } else if (left->IsSmiLiteral() && !Token::IsShiftOp(op)) { |
+ return kLeftConstant; |
+ } else { |
+ return kNoConstants; |
+ } |
+} |
+ |
+ |
void FullCodeGenerator::Apply(Expression::Context context, Register reg) { |
switch (context) { |
case Expression::kUninitialized: |
@@ -1152,10 +1168,11 @@ |
// slot. Variables with rewrite to .arguments are treated as KEYED_PROPERTY. |
enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY }; |
LhsKind assign_type = VARIABLE; |
- Property* prop = expr->target()->AsProperty(); |
- if (prop != NULL) { |
- assign_type = |
- (prop->key()->IsPropertyName()) ? NAMED_PROPERTY : KEYED_PROPERTY; |
+ Property* property = expr->target()->AsProperty(); |
+ if (property != NULL) { |
+ assign_type = (property->key()->IsPropertyName()) |
+ ? NAMED_PROPERTY |
+ : KEYED_PROPERTY; |
} |
// Evaluate LHS expression. |
@@ -1166,60 +1183,70 @@ |
case NAMED_PROPERTY: |
if (expr->is_compound()) { |
// We need the receiver both on the stack and in the accumulator. |
- VisitForValue(prop->obj(), kAccumulator); |
+ VisitForValue(property->obj(), kAccumulator); |
__ push(result_register()); |
} else { |
- VisitForValue(prop->obj(), kStack); |
+ VisitForValue(property->obj(), kStack); |
} |
break; |
case KEYED_PROPERTY: |
if (expr->is_compound()) { |
- VisitForValue(prop->obj(), kStack); |
- VisitForValue(prop->key(), kAccumulator); |
+ VisitForValue(property->obj(), kStack); |
+ VisitForValue(property->key(), kAccumulator); |
__ mov(edx, Operand(esp, 0)); |
__ push(eax); |
} else { |
- VisitForValue(prop->obj(), kStack); |
- VisitForValue(prop->key(), kStack); |
+ VisitForValue(property->obj(), kStack); |
+ VisitForValue(property->key(), kStack); |
} |
break; |
} |
- // If we have a compound assignment: Get value of LHS expression and |
- // store in on top of the stack. |
if (expr->is_compound()) { |
Location saved_location = location_; |
- location_ = kStack; |
+ location_ = kAccumulator; |
switch (assign_type) { |
case VARIABLE: |
EmitVariableLoad(expr->target()->AsVariableProxy()->var(), |
Expression::kValue); |
break; |
case NAMED_PROPERTY: |
- EmitNamedPropertyLoad(prop); |
- __ push(result_register()); |
+ EmitNamedPropertyLoad(property); |
break; |
case KEYED_PROPERTY: |
- EmitKeyedPropertyLoad(prop); |
- __ push(result_register()); |
+ EmitKeyedPropertyLoad(property); |
break; |
} |
- location_ = saved_location; |
- } |
- // Evaluate RHS expression. |
- Expression* rhs = expr->value(); |
- VisitForValue(rhs, kAccumulator); |
+ Token::Value op = expr->binary_op(); |
+ ConstantOperand constant = ShouldInlineSmiCase(op) |
+ ? GetConstantOperand(op, expr->target(), expr->value()) |
+ : kNoConstants; |
+ ASSERT(constant == kRightConstant || constant == kNoConstants); |
+ if (constant == kNoConstants) { |
+ __ push(eax); // Left operand goes on the stack. |
+ VisitForValue(expr->value(), kAccumulator); |
+ } |
- // If we have a compound assignment: Apply operator. |
- if (expr->is_compound()) { |
- Location saved_location = location_; |
- location_ = kAccumulator; |
OverwriteMode mode = expr->value()->ResultOverwriteAllowed() |
? OVERWRITE_RIGHT |
: NO_OVERWRITE; |
- EmitBinaryOp(expr->binary_op(), Expression::kValue, mode); |
+ SetSourcePosition(expr->position() + 1); |
+ if (ShouldInlineSmiCase(op)) { |
+ EmitInlineSmiBinaryOp(expr, |
+ op, |
+ Expression::kValue, |
+ mode, |
+ expr->target(), |
+ expr->value(), |
+ constant); |
+ } else { |
+ EmitBinaryOp(op, Expression::kValue, mode); |
+ } |
location_ = saved_location; |
+ |
+ } else { |
+ VisitForValue(expr->value(), kAccumulator); |
} |
// Record source position before possible IC call. |
@@ -1260,6 +1287,313 @@ |
} |
+void FullCodeGenerator::EmitConstantSmiAdd(Expression* expr, |
+ Expression::Context context, |
+ OverwriteMode mode, |
+ bool left_is_constant_smi, |
+ Smi* value) { |
+ Label call_stub, done; |
+ __ add(Operand(eax), Immediate(value)); |
+ __ j(overflow, &call_stub); |
+ __ test(eax, Immediate(kSmiTagMask)); |
+ __ j(zero, &done); |
+ |
+ // Undo the optimistic add operation and call the shared stub. |
+ __ bind(&call_stub); |
+ __ sub(Operand(eax), Immediate(value)); |
+ Token::Value op = Token::ADD; |
+ GenericBinaryOpStub stub(op, mode, NO_SMI_CODE_IN_STUB, TypeInfo::Unknown()); |
+ if (left_is_constant_smi) { |
+ __ push(Immediate(value)); |
+ __ push(eax); |
+ } else { |
+ __ push(eax); |
+ __ push(Immediate(value)); |
+ } |
+ __ CallStub(&stub); |
+ __ bind(&done); |
+ Apply(context, eax); |
+} |
+ |
+ |
+void FullCodeGenerator::EmitConstantSmiSub(Expression* expr, |
+ Expression::Context context, |
+ OverwriteMode mode, |
+ bool left_is_constant_smi, |
+ Smi* value) { |
+ Label call_stub, done; |
+ if (left_is_constant_smi) { |
+ __ mov(ecx, eax); |
+ __ mov(eax, Immediate(value)); |
+ __ sub(Operand(eax), ecx); |
+ } else { |
+ __ sub(Operand(eax), Immediate(value)); |
+ } |
+ __ j(overflow, &call_stub); |
+ __ test(eax, Immediate(kSmiTagMask)); |
+ __ j(zero, &done); |
+ |
+ __ bind(&call_stub); |
+ if (left_is_constant_smi) { |
+ __ push(Immediate(value)); |
+ __ push(ecx); |
+ } else { |
+ // Undo the optimistic sub operation. |
+ __ add(Operand(eax), Immediate(value)); |
+ |
+ __ push(eax); |
+ __ push(Immediate(value)); |
+ } |
+ |
+ Token::Value op = Token::SUB; |
+ GenericBinaryOpStub stub(op, mode, NO_SMI_CODE_IN_STUB, TypeInfo::Unknown()); |
+ __ CallStub(&stub); |
+ __ bind(&done); |
+ Apply(context, eax); |
+} |
+ |
+ |
+void FullCodeGenerator::EmitConstantSmiShiftOp(Expression* expr, |
+ Token::Value op, |
+ Expression::Context context, |
+ OverwriteMode mode, |
+ Smi* value) { |
+ Label call_stub, smi_case, done; |
+ int shift_value = value->value() & 0x1f; |
+ |
+ __ test(eax, Immediate(kSmiTagMask)); |
+ __ j(zero, &smi_case); |
+ |
+ __ bind(&call_stub); |
+ GenericBinaryOpStub stub(op, mode, NO_SMI_CODE_IN_STUB, TypeInfo::Unknown()); |
+ __ push(eax); |
+ __ push(Immediate(value)); |
+ __ CallStub(&stub); |
+ __ jmp(&done); |
+ |
+ __ bind(&smi_case); |
+ switch (op) { |
+ case Token::SHL: |
+ if (shift_value != 0) { |
+ __ mov(edx, eax); |
+ if (shift_value > 1) { |
+ __ shl(edx, shift_value - 1); |
+ } |
+ // Convert int result to smi, checking that it is in int range. |
+ ASSERT(kSmiTagSize == 1); // Adjust code if not the case. |
+ __ add(edx, Operand(edx)); |
+ __ j(overflow, &call_stub); |
+ __ mov(eax, edx); // Put result back into eax. |
+ } |
+ break; |
+ case Token::SAR: |
+ if (shift_value != 0) { |
+ __ sar(eax, shift_value); |
+ __ and_(eax, ~kSmiTagMask); |
+ } |
+ break; |
+ case Token::SHR: |
+ if (shift_value < 2) { |
+ __ mov(edx, eax); |
+ __ SmiUntag(edx); |
+ __ shr(edx, shift_value); |
+ __ test(edx, Immediate(0xc0000000)); |
+ __ j(not_zero, &call_stub); |
+ __ SmiTag(edx); |
+ __ mov(eax, edx); // Put result back into eax. |
+ } else { |
+ __ SmiUntag(eax); |
+ __ shr(eax, shift_value); |
+ __ SmiTag(eax); |
+ } |
+ break; |
+ default: |
+ UNREACHABLE(); |
+ } |
+ |
+ __ bind(&done); |
+ Apply(context, eax); |
+} |
+ |
+ |
+void FullCodeGenerator::EmitConstantSmiBitOp(Expression* expr, |
+ Token::Value op, |
+ Expression::Context context, |
+ OverwriteMode mode, |
+ Smi* value) { |
+ Label smi_case, done; |
+ __ test(eax, Immediate(kSmiTagMask)); |
+ __ j(zero, &smi_case); |
+ |
+ GenericBinaryOpStub stub(op, mode, NO_SMI_CODE_IN_STUB, TypeInfo::Unknown()); |
+ // The order of the arguments does not matter for bit-ops with a |
+ // constant operand. |
+ __ push(Immediate(value)); |
+ __ push(eax); |
+ __ CallStub(&stub); |
+ __ jmp(&done); |
+ |
+ __ bind(&smi_case); |
+ switch (op) { |
+ case Token::BIT_OR: |
+ __ or_(Operand(eax), Immediate(value)); |
+ break; |
+ case Token::BIT_XOR: |
+ __ xor_(Operand(eax), Immediate(value)); |
+ break; |
+ case Token::BIT_AND: |
+ __ and_(Operand(eax), Immediate(value)); |
+ break; |
+ default: |
+ UNREACHABLE(); |
+ } |
+ |
+ __ bind(&done); |
+ Apply(context, eax); |
+} |
+ |
+ |
+void FullCodeGenerator::EmitConstantSmiBinaryOp(Expression* expr, |
+ Token::Value op, |
+ Expression::Context context, |
+ OverwriteMode mode, |
+ bool left_is_constant_smi, |
+ Smi* value) { |
+ switch (op) { |
+ case Token::BIT_OR: |
+ case Token::BIT_XOR: |
+ case Token::BIT_AND: |
+ EmitConstantSmiBitOp(expr, op, context, mode, value); |
+ break; |
+ case Token::SHL: |
+ case Token::SAR: |
+ case Token::SHR: |
+ ASSERT(!left_is_constant_smi); |
+ EmitConstantSmiShiftOp(expr, op, context, mode, value); |
+ break; |
+ case Token::ADD: |
+ EmitConstantSmiAdd(expr, context, mode, left_is_constant_smi, value); |
+ break; |
+ case Token::SUB: |
+ EmitConstantSmiSub(expr, context, mode, left_is_constant_smi, value); |
+ break; |
+ default: |
+ UNREACHABLE(); |
+ } |
+} |
+ |
+ |
+void FullCodeGenerator::EmitInlineSmiBinaryOp(Expression* expr, |
+ Token::Value op, |
+ Expression::Context context, |
+ OverwriteMode mode, |
+ Expression* left, |
+ Expression* right, |
+ ConstantOperand constant) { |
+ if (constant == kRightConstant) { |
+ Smi* value = Smi::cast(*right->AsLiteral()->handle()); |
+ EmitConstantSmiBinaryOp(expr, op, context, mode, false, value); |
+ return; |
+ } else if (constant == kLeftConstant) { |
+ Smi* value = Smi::cast(*left->AsLiteral()->handle()); |
+ EmitConstantSmiBinaryOp(expr, op, context, mode, true, value); |
+ return; |
+ } |
+ |
+ // Do combined smi check of the operands. Left operand is on the |
+ // stack. Right operand is in eax. |
+ Label done, stub_call, smi_case; |
+ __ pop(edx); |
+ __ mov(ecx, eax); |
+ __ or_(eax, Operand(edx)); |
+ __ test(eax, Immediate(kSmiTagMask)); |
+ __ j(zero, &smi_case); |
+ |
+ __ bind(&stub_call); |
+ GenericBinaryOpStub stub(op, mode, NO_SMI_CODE_IN_STUB, TypeInfo::Unknown()); |
+ if (stub.ArgsInRegistersSupported()) { |
+ stub.GenerateCall(masm_, edx, ecx); |
+ } else { |
+ __ push(edx); |
+ __ push(ecx); |
+ __ CallStub(&stub); |
+ } |
+ __ jmp(&done); |
+ |
+ __ bind(&smi_case); |
+ __ mov(eax, edx); // Copy left operand in case of a stub call. |
+ |
+ switch (op) { |
+ case Token::SAR: |
+ __ SmiUntag(eax); |
+ __ SmiUntag(ecx); |
+ __ sar_cl(eax); // No checks of result necessary |
+ __ SmiTag(eax); |
+ break; |
+ case Token::SHL: { |
+ Label result_ok; |
+ __ SmiUntag(eax); |
+ __ SmiUntag(ecx); |
+ __ shl_cl(eax); |
+ // Check that the *signed* result fits in a smi. |
+ __ cmp(eax, 0xc0000000); |
+ __ j(positive, &result_ok); |
+ __ SmiTag(ecx); |
+ __ jmp(&stub_call); |
+ __ bind(&result_ok); |
+ __ SmiTag(eax); |
+ break; |
+ } |
+ case Token::SHR: { |
+ Label result_ok; |
+ __ SmiUntag(eax); |
+ __ SmiUntag(ecx); |
+ __ shr_cl(eax); |
+ __ test(eax, Immediate(0xc0000000)); |
+ __ j(zero, &result_ok); |
+ __ SmiTag(ecx); |
+ __ jmp(&stub_call); |
+ __ bind(&result_ok); |
+ __ SmiTag(eax); |
+ break; |
+ } |
+ case Token::ADD: |
+ __ add(eax, Operand(ecx)); |
+ __ j(overflow, &stub_call); |
+ break; |
+ case Token::SUB: |
+ __ sub(eax, Operand(ecx)); |
+ __ j(overflow, &stub_call); |
+ break; |
+ case Token::MUL: { |
+ __ SmiUntag(eax); |
+ __ imul(eax, Operand(ecx)); |
+ __ j(overflow, &stub_call); |
+ __ test(eax, Operand(eax)); |
+ __ j(not_zero, &done, taken); |
+ __ mov(ebx, edx); |
+ __ or_(ebx, Operand(ecx)); |
+ __ j(negative, &stub_call); |
+ break; |
+ } |
+ case Token::BIT_OR: |
+ __ or_(eax, Operand(ecx)); |
+ break; |
+ case Token::BIT_AND: |
+ __ and_(eax, Operand(ecx)); |
+ break; |
+ case Token::BIT_XOR: |
+ __ xor_(eax, Operand(ecx)); |
+ break; |
+ default: |
+ UNREACHABLE(); |
+ } |
+ |
+ __ bind(&done); |
+ Apply(context, eax); |
+} |
+ |
+ |
void FullCodeGenerator::EmitBinaryOp(Token::Value op, |
Expression::Context context, |
OverwriteMode mode) { |