diff options
Diffstat (limited to 'deps/v8/src/x64/full-codegen-x64.cc')
-rw-r--r-- | deps/v8/src/x64/full-codegen-x64.cc | 942 |
1 files changed, 439 insertions, 503 deletions
diff --git a/deps/v8/src/x64/full-codegen-x64.cc b/deps/v8/src/x64/full-codegen-x64.cc index 725cbb0c58..ccd0392a30 100644 --- a/deps/v8/src/x64/full-codegen-x64.cc +++ b/deps/v8/src/x64/full-codegen-x64.cc @@ -29,6 +29,7 @@ #if defined(V8_TARGET_ARCH_X64) +#include "code-stubs.h" #include "codegen-inl.h" #include "compiler.h" #include "debug.h" @@ -229,6 +230,13 @@ void FullCodeGenerator::EmitReturnSequence() { } +FullCodeGenerator::ConstantOperand FullCodeGenerator::GetConstantOperand( + Token::Value op, Expression* left, Expression* right) { + ASSERT(ShouldInlineSmiCase(op)); + return kNoConstants; +} + + void FullCodeGenerator::Apply(Expression::Context context, Register reg) { switch (context) { case Expression::kUninitialized: @@ -253,20 +261,7 @@ void FullCodeGenerator::Apply(Expression::Context context, Register reg) { case Expression::kTest: // For simplicity we always test the accumulator register. if (!reg.is(result_register())) __ movq(result_register(), reg); - DoTest(context); - break; - - case Expression::kValueTest: - case Expression::kTestValue: - if (!reg.is(result_register())) __ movq(result_register(), reg); - switch (location_) { - case kAccumulator: - break; - case kStack: - __ push(result_register()); - break; - } - DoTest(context); + DoTest(true_label_, false_label_, fall_through_); break; } } @@ -295,20 +290,7 @@ void FullCodeGenerator::Apply(Expression::Context context, Slot* slot) { case Expression::kTest: Move(result_register(), slot); - DoTest(context); - break; - - case Expression::kValueTest: - case Expression::kTestValue: - Move(result_register(), slot); - switch (location_) { - case kAccumulator: - break; - case kStack: - __ push(result_register()); - break; - } - DoTest(context); + DoTest(true_label_, false_label_, fall_through_); break; } } @@ -334,20 +316,7 @@ void FullCodeGenerator::Apply(Expression::Context context, Literal* lit) { case Expression::kTest: __ Move(result_register(), lit->handle()); - DoTest(context); - break; - - case Expression::kValueTest: - case Expression::kTestValue: - __ Move(result_register(), lit->handle()); - switch (location_) { - case kAccumulator: - break; - case kStack: - __ push(result_register()); - break; - } - DoTest(context); + DoTest(true_label_, false_label_, fall_through_); break; } } @@ -374,20 +343,7 @@ void FullCodeGenerator::ApplyTOS(Expression::Context context) { case Expression::kTest: __ pop(result_register()); - DoTest(context); - break; - - case Expression::kValueTest: - case Expression::kTestValue: - switch (location_) { - case kAccumulator: - __ pop(result_register()); - break; - case kStack: - __ movq(result_register(), Operand(rsp, 0)); - break; - } - DoTest(context); + DoTest(true_label_, false_label_, fall_through_); break; } } @@ -422,56 +378,7 @@ void FullCodeGenerator::DropAndApply(int count, case Expression::kTest: __ Drop(count); if (!reg.is(result_register())) __ movq(result_register(), reg); - DoTest(context); - break; - - case Expression::kValueTest: - case Expression::kTestValue: - switch (location_) { - case kAccumulator: - __ Drop(count); - if (!reg.is(result_register())) __ movq(result_register(), reg); - break; - case kStack: - if (count > 1) __ Drop(count - 1); - __ movq(result_register(), reg); - __ movq(Operand(rsp, 0), result_register()); - break; - } - DoTest(context); - break; - } -} - - -void FullCodeGenerator::PrepareTest(Label* materialize_true, - Label* materialize_false, - Label** if_true, - Label** if_false) { - switch (context_) { - case Expression::kUninitialized: - UNREACHABLE(); - break; - case Expression::kEffect: - // In an effect context, the true and the false case branch to the - // same label. - *if_true = *if_false = materialize_true; - break; - case Expression::kValue: - *if_true = materialize_true; - *if_false = materialize_false; - break; - case Expression::kTest: - *if_true = true_label_; - *if_false = false_label_; - break; - case Expression::kValueTest: - *if_true = materialize_true; - *if_false = false_label_; - break; - case Expression::kTestValue: - *if_true = true_label_; - *if_false = materialize_false; + DoTest(true_label_, false_label_, fall_through_); break; } } @@ -512,32 +419,6 @@ void FullCodeGenerator::Apply(Expression::Context context, case Expression::kTest: break; - - case Expression::kValueTest: - __ bind(materialize_true); - switch (location_) { - case kAccumulator: - __ Move(result_register(), Factory::true_value()); - break; - case kStack: - __ Push(Factory::true_value()); - break; - } - __ jmp(true_label_); - break; - - case Expression::kTestValue: - __ bind(materialize_false); - switch (location_) { - case kAccumulator: - __ Move(result_register(), Factory::false_value()); - break; - case kStack: - __ Push(Factory::false_value()); - break; - } - __ jmp(false_label_); - break; } } @@ -565,78 +446,19 @@ void FullCodeGenerator::Apply(Expression::Context context, bool flag) { break; } case Expression::kTest: - __ jmp(flag ? true_label_ : false_label_); - break; - case Expression::kTestValue: - switch (location_) { - case kAccumulator: - // If value is false it's needed. - if (!flag) __ LoadRoot(result_register(), Heap::kFalseValueRootIndex); - break; - case kStack: - // If value is false it's needed. - if (!flag) __ PushRoot(Heap::kFalseValueRootIndex); - break; - } - __ jmp(flag ? true_label_ : false_label_); - break; - case Expression::kValueTest: - switch (location_) { - case kAccumulator: - // If value is true it's needed. - if (flag) __ LoadRoot(result_register(), Heap::kTrueValueRootIndex); - break; - case kStack: - // If value is true it's needed. - if (flag) __ PushRoot(Heap::kTrueValueRootIndex); - break; + if (flag) { + if (true_label_ != fall_through_) __ jmp(true_label_); + } else { + if (false_label_ != fall_through_) __ jmp(false_label_); } - __ jmp(flag ? true_label_ : false_label_); break; } } -void FullCodeGenerator::DoTest(Expression::Context context) { - // The value to test is in the accumulator. If the value might be needed - // on the stack (value/test and test/value contexts with a stack location - // desired), then the value is already duplicated on the stack. - ASSERT_NE(NULL, true_label_); - ASSERT_NE(NULL, false_label_); - - // In value/test and test/value expression contexts with stack as the - // desired location, there is already an extra value on the stack. Use a - // label to discard it if unneeded. - Label discard; - Label* if_true = true_label_; - Label* if_false = false_label_; - switch (context) { - case Expression::kUninitialized: - case Expression::kEffect: - case Expression::kValue: - UNREACHABLE(); - case Expression::kTest: - break; - case Expression::kValueTest: - switch (location_) { - case kAccumulator: - break; - case kStack: - if_false = &discard; - break; - } - break; - case Expression::kTestValue: - switch (location_) { - case kAccumulator: - break; - case kStack: - if_true = &discard; - break; - } - break; - } - +void FullCodeGenerator::DoTest(Label* if_true, + Label* if_false, + Label* fall_through) { // Emit the inlined tests assumed by the stub. __ CompareRoot(result_register(), Heap::kUndefinedValueRootIndex); __ j(equal, if_false); @@ -650,83 +472,28 @@ void FullCodeGenerator::DoTest(Expression::Context context) { Condition is_smi = masm_->CheckSmi(result_register()); __ j(is_smi, if_true); - // Save a copy of the value if it may be needed and isn't already saved. - switch (context) { - case Expression::kUninitialized: - case Expression::kEffect: - case Expression::kValue: - UNREACHABLE(); - case Expression::kTest: - break; - case Expression::kValueTest: - switch (location_) { - case kAccumulator: - __ push(result_register()); - break; - case kStack: - break; - } - break; - case Expression::kTestValue: - switch (location_) { - case kAccumulator: - __ push(result_register()); - break; - case kStack: - break; - } - break; - } - // Call the ToBoolean stub for all other cases. ToBooleanStub stub; __ push(result_register()); __ CallStub(&stub); __ testq(rax, rax); - // The stub returns nonzero for true. Complete based on the context. - switch (context) { - case Expression::kUninitialized: - case Expression::kEffect: - case Expression::kValue: - UNREACHABLE(); - - case Expression::kTest: - __ j(not_zero, true_label_); - __ jmp(false_label_); - break; + // The stub returns nonzero for true. + Split(not_zero, if_true, if_false, fall_through); +} - case Expression::kValueTest: - switch (location_) { - case kAccumulator: - __ j(zero, &discard); - __ pop(result_register()); - __ jmp(true_label_); - break; - case kStack: - __ j(not_zero, true_label_); - break; - } - __ bind(&discard); - __ Drop(1); - __ jmp(false_label_); - break; - case Expression::kTestValue: - switch (location_) { - case kAccumulator: - __ j(not_zero, &discard); - __ pop(result_register()); - __ jmp(false_label_); - break; - case kStack: - __ j(zero, false_label_); - break; - } - __ bind(&discard); - __ Drop(1); - __ jmp(true_label_); - break; +void FullCodeGenerator::Split(Condition cc, + Label* if_true, + Label* if_false, + Label* fall_through) { + if (if_false == fall_through) { + __ j(cc, if_true); + } else if (if_true == fall_through) { + __ j(NegateCondition(cc), if_false); + } else { + __ j(cc, if_true); + __ jmp(if_false); } } @@ -912,17 +679,18 @@ void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) { // Compile the label expression. VisitForValue(clause->label(), kAccumulator); - // Perform the comparison as if via '==='. The comparison stub expects - // the smi vs. smi case to be handled before it is called. - Label slow_case; - __ movq(rdx, Operand(rsp, 0)); // Switch value. - __ JumpIfNotBothSmi(rdx, rax, &slow_case); - __ SmiCompare(rdx, rax); - __ j(not_equal, &next_test); - __ Drop(1); // Switch value is no longer needed. - __ jmp(clause->body_target()->entry_label()); + // Perform the comparison as if via '==='. + if (ShouldInlineSmiCase(Token::EQ_STRICT)) { + Label slow_case; + __ movq(rdx, Operand(rsp, 0)); // Switch value. + __ JumpIfNotBothSmi(rdx, rax, &slow_case); + __ SmiCompare(rdx, rax); + __ j(not_equal, &next_test); + __ Drop(1); // Switch value is no longer needed. + __ jmp(clause->body_target()->entry_label()); + __ bind(&slow_case); + } - __ bind(&slow_case); CompareStub stub(equal, true); __ CallStub(&stub); __ testq(rax, rax); @@ -1206,7 +974,7 @@ void FullCodeGenerator::VisitRegExpLiteral(RegExpLiteral* expr) { __ movq(rdi, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset)); __ movq(rcx, FieldOperand(rdi, JSFunction::kLiteralsOffset)); int literal_offset = - FixedArray::kHeaderSize + expr->literal_index() * kPointerSize; + FixedArray::kHeaderSize + expr->literal_index() * kPointerSize; __ movq(rbx, FieldOperand(rcx, literal_offset)); __ CompareRoot(rbx, Heap::kUndefinedValueRootIndex); __ j(not_equal, &materialized); @@ -1330,12 +1098,18 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { __ push(FieldOperand(rbx, JSFunction::kLiteralsOffset)); __ Push(Smi::FromInt(expr->literal_index())); __ Push(expr->constant_elements()); - if (expr->depth() > 1) { + if (expr->constant_elements()->map() == Heap::fixed_cow_array_map()) { + FastCloneShallowArrayStub stub( + FastCloneShallowArrayStub::COPY_ON_WRITE_ELEMENTS, length); + __ CallStub(&stub); + __ IncrementCounter(&Counters::cow_arrays_created_stub, 1); + } else if (expr->depth() > 1) { __ CallRuntime(Runtime::kCreateArrayLiteral, 3); - } else if (length > FastCloneShallowArrayStub::kMaximumLength) { + } else if (length > FastCloneShallowArrayStub::kMaximumClonedLength) { __ CallRuntime(Runtime::kCreateArrayLiteralShallow, 3); } else { - FastCloneShallowArrayStub stub(length); + FastCloneShallowArrayStub stub( + FastCloneShallowArrayStub::CLONE_ELEMENTS, length); __ CallStub(&stub); } @@ -1389,10 +1163,11 @@ void FullCodeGenerator::VisitAssignment(Assignment* expr) { // 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. @@ -1403,57 +1178,70 @@ void FullCodeGenerator::VisitAssignment(Assignment* expr) { 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); __ movq(rdx, Operand(rsp, 0)); __ push(rax); } 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(rax); // 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; - EmitBinaryOp(expr->binary_op(), Expression::kValue); + OverwriteMode mode = expr->value()->ResultOverwriteAllowed() + ? OVERWRITE_RIGHT + : NO_OVERWRITE; + 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. @@ -1494,13 +1282,85 @@ void FullCodeGenerator::EmitKeyedPropertyLoad(Property* prop) { } +void FullCodeGenerator::EmitInlineSmiBinaryOp(Expression* expr, + Token::Value op, + Expression::Context context, + OverwriteMode mode, + Expression* left, + Expression* right, + ConstantOperand constant) { + ASSERT(constant == kNoConstants); // Only handled case. + + // Do combined smi check of the operands. Left operand is on the + // stack (popped into rdx). Right operand is in rax but moved into + // rcx to make the shifts easier. + Label done, stub_call, smi_case; + __ pop(rdx); + __ movq(rcx, rax); + Condition smi = __ CheckBothSmi(rdx, rax); + __ j(smi, &smi_case); + + __ bind(&stub_call); + GenericBinaryOpStub stub(op, mode, NO_SMI_CODE_IN_STUB, TypeInfo::Unknown()); + if (stub.ArgsInRegistersSupported()) { + stub.GenerateCall(masm_, rdx, rcx); + } else { + __ push(rdx); + __ push(rcx); + __ CallStub(&stub); + } + __ jmp(&done); + + __ bind(&smi_case); + switch (op) { + case Token::SAR: + __ SmiShiftArithmeticRight(rax, rdx, rcx); + break; + case Token::SHL: + __ SmiShiftLeft(rax, rdx, rcx); + break; + case Token::SHR: + __ SmiShiftLogicalRight(rax, rdx, rcx, &stub_call); + break; + case Token::ADD: + __ SmiAdd(rax, rdx, rcx, &stub_call); + break; + case Token::SUB: + __ SmiSub(rax, rdx, rcx, &stub_call); + break; + case Token::MUL: + __ SmiMul(rax, rdx, rcx, &stub_call); + break; + case Token::BIT_OR: + __ SmiOr(rax, rdx, rcx); + break; + case Token::BIT_AND: + __ SmiAnd(rax, rdx, rcx); + break; + case Token::BIT_XOR: + __ SmiXor(rax, rdx, rcx); + break; + default: + UNREACHABLE(); + break; + } + + __ bind(&done); + Apply(context, rax); +} + + void FullCodeGenerator::EmitBinaryOp(Token::Value op, - Expression::Context context) { - __ push(result_register()); - GenericBinaryOpStub stub(op, - NO_OVERWRITE, - NO_GENERIC_BINARY_FLAGS); - __ CallStub(&stub); + Expression::Context context, + OverwriteMode mode) { + GenericBinaryOpStub stub(op, mode, NO_GENERIC_BINARY_FLAGS); + if (stub.ArgsInRegistersSupported()) { + __ pop(rdx); + stub.GenerateCall(masm_, rdx, rax); + } else { + __ push(result_register()); + __ CallStub(&stub); + } Apply(context, rax); } @@ -1923,11 +1783,11 @@ void FullCodeGenerator::VisitCallNew(CallNew* expr) { // According to ECMA-262, section 11.2.2, page 44, the function // expression in new calls must be evaluated before the // arguments. - // Push function on the stack. - VisitForValue(expr->expression(), kStack); - // Push global object (receiver). - __ push(CodeGenerator::GlobalObject()); + // Push constructor on the stack. If it's not a function it's used as + // receiver for CALL_NON_FUNCTION, otherwise the value on the stack is + // ignored. + VisitForValue(expr->expression(), kStack); // Push the arguments ("left-to-right") on the stack. ZoneList<Expression*>* args = expr->arguments(); @@ -1940,16 +1800,13 @@ void FullCodeGenerator::VisitCallNew(CallNew* expr) { // constructor invocation. SetSourcePosition(expr->position()); - // Load function, arg_count into rdi and rax. + // Load function and argument count into rdi and rax. __ Set(rax, arg_count); - // Function is in rsp[arg_count + 1]. - __ movq(rdi, Operand(rsp, rax, times_pointer_size, kPointerSize)); + __ movq(rdi, Operand(rsp, arg_count * kPointerSize)); Handle<Code> construct_builtin(Builtins::builtin(Builtins::JSConstructCall)); __ Call(construct_builtin, RelocInfo::CONSTRUCT_CALL); - - // Replace function on TOS with result in rax, or pop it. - DropAndApply(1, context_, rax); + Apply(context_, rax); } @@ -1961,7 +1818,9 @@ void FullCodeGenerator::EmitIsSmi(ZoneList<Expression*>* args) { Label materialize_true, materialize_false; Label* if_true = NULL; Label* if_false = NULL; - PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); + Label* fall_through = NULL; + PrepareTest(&materialize_true, &materialize_false, + &if_true, &if_false, &fall_through); __ JumpIfSmi(rax, if_true); __ jmp(if_false); @@ -1978,11 +1837,12 @@ void FullCodeGenerator::EmitIsNonNegativeSmi(ZoneList<Expression*>* args) { Label materialize_true, materialize_false; Label* if_true = NULL; Label* if_false = NULL; - PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); + Label* fall_through = NULL; + PrepareTest(&materialize_true, &materialize_false, + &if_true, &if_false, &fall_through); Condition positive_smi = __ CheckPositiveSmi(rax); - __ j(positive_smi, if_true); - __ jmp(if_false); + Split(positive_smi, if_true, if_false, fall_through); Apply(context_, if_true, if_false); } @@ -1996,7 +1856,9 @@ void FullCodeGenerator::EmitIsObject(ZoneList<Expression*>* args) { Label materialize_true, materialize_false; Label* if_true = NULL; Label* if_false = NULL; - PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); + Label* fall_through = NULL; + PrepareTest(&materialize_true, &materialize_false, + &if_true, &if_false, &fall_through); __ JumpIfSmi(rax, if_false); __ CompareRoot(rax, Heap::kNullValueRootIndex); @@ -2010,8 +1872,7 @@ void FullCodeGenerator::EmitIsObject(ZoneList<Expression*>* args) { __ cmpq(rbx, Immediate(FIRST_JS_OBJECT_TYPE)); __ j(below, if_false); __ cmpq(rbx, Immediate(LAST_JS_OBJECT_TYPE)); - __ j(below_equal, if_true); - __ jmp(if_false); + Split(below_equal, if_true, if_false, fall_through); Apply(context_, if_true, if_false); } @@ -2025,12 +1886,13 @@ void FullCodeGenerator::EmitIsSpecObject(ZoneList<Expression*>* args) { Label materialize_true, materialize_false; Label* if_true = NULL; Label* if_false = NULL; - PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); + Label* fall_through = NULL; + PrepareTest(&materialize_true, &materialize_false, + &if_true, &if_false, &fall_through); __ JumpIfSmi(rax, if_false); __ CmpObjectType(rax, FIRST_JS_OBJECT_TYPE, rbx); - __ j(above_equal, if_true); - __ jmp(if_false); + Split(above_equal, if_true, if_false, fall_through); Apply(context_, if_true, if_false); } @@ -2044,14 +1906,15 @@ void FullCodeGenerator::EmitIsUndetectableObject(ZoneList<Expression*>* args) { Label materialize_true, materialize_false; Label* if_true = NULL; Label* if_false = NULL; - PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); + Label* fall_through = NULL; + PrepareTest(&materialize_true, &materialize_false, + &if_true, &if_false, &fall_through); __ JumpIfSmi(rax, if_false); __ movq(rbx, FieldOperand(rax, HeapObject::kMapOffset)); __ testb(FieldOperand(rbx, Map::kBitFieldOffset), Immediate(1 << Map::kIsUndetectable)); - __ j(not_zero, if_true); - __ jmp(if_false); + Split(not_zero, if_true, if_false, fall_through); Apply(context_, if_true, if_false); } @@ -2066,7 +1929,9 @@ void FullCodeGenerator::EmitIsStringWrapperSafeForDefaultValueOf( Label materialize_true, materialize_false; Label* if_true = NULL; Label* if_false = NULL; - PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); + Label* fall_through = NULL; + PrepareTest(&materialize_true, &materialize_false, + &if_true, &if_false, &fall_through); // Just indicate false, as %_IsStringWrapperSafeForDefaultValueOf() is only // used in a few functions in runtime.js which should not normally be hit by @@ -2084,12 +1949,13 @@ void FullCodeGenerator::EmitIsFunction(ZoneList<Expression*>* args) { Label materialize_true, materialize_false; Label* if_true = NULL; Label* if_false = NULL; - PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); + Label* fall_through = NULL; + PrepareTest(&materialize_true, &materialize_false, + &if_true, &if_false, &fall_through); __ JumpIfSmi(rax, if_false); __ CmpObjectType(rax, JS_FUNCTION_TYPE, rbx); - __ j(equal, if_true); - __ jmp(if_false); + Split(equal, if_true, if_false, fall_through); Apply(context_, if_true, if_false); } @@ -2103,12 +1969,13 @@ void FullCodeGenerator::EmitIsArray(ZoneList<Expression*>* args) { Label materialize_true, materialize_false; Label* if_true = NULL; Label* if_false = NULL; - PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); + Label* fall_through = NULL; + PrepareTest(&materialize_true, &materialize_false, + &if_true, &if_false, &fall_through); __ JumpIfSmi(rax, if_false); __ CmpObjectType(rax, JS_ARRAY_TYPE, rbx); - __ j(equal, if_true); - __ jmp(if_false); + Split(equal, if_true, if_false, fall_through); Apply(context_, if_true, if_false); } @@ -2122,12 +1989,13 @@ void FullCodeGenerator::EmitIsRegExp(ZoneList<Expression*>* args) { Label materialize_true, materialize_false; Label* if_true = NULL; Label* if_false = NULL; - PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); + Label* fall_through = NULL; + PrepareTest(&materialize_true, &materialize_false, + &if_true, &if_false, &fall_through); __ JumpIfSmi(rax, if_false); __ CmpObjectType(rax, JS_REGEXP_TYPE, rbx); - __ j(equal, if_true); - __ jmp(if_false); + Split(equal, if_true, if_false, fall_through); Apply(context_, if_true, if_false); } @@ -2140,7 +2008,9 @@ void FullCodeGenerator::EmitIsConstructCall(ZoneList<Expression*>* args) { Label materialize_true, materialize_false; Label* if_true = NULL; Label* if_false = NULL; - PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); + Label* fall_through = NULL; + PrepareTest(&materialize_true, &materialize_false, + &if_true, &if_false, &fall_through); // Get the frame pointer for the calling frame. __ movq(rax, Operand(rbp, StandardFrameConstants::kCallerFPOffset)); @@ -2156,8 +2026,7 @@ void FullCodeGenerator::EmitIsConstructCall(ZoneList<Expression*>* args) { __ bind(&check_frame_marker); __ SmiCompare(Operand(rax, StandardFrameConstants::kMarkerOffset), Smi::FromInt(StackFrame::CONSTRUCT)); - __ j(equal, if_true); - __ jmp(if_false); + Split(equal, if_true, if_false, fall_through); Apply(context_, if_true, if_false); } @@ -2173,12 +2042,13 @@ void FullCodeGenerator::EmitObjectEquals(ZoneList<Expression*>* args) { Label materialize_true, materialize_false; Label* if_true = NULL; Label* if_false = NULL; - PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); + Label* fall_through = NULL; + PrepareTest(&materialize_true, &materialize_false, + &if_true, &if_false, &fall_through); __ pop(rbx); __ cmpq(rax, rbx); - __ j(equal, if_true); - __ jmp(if_false); + Split(equal, if_true, if_false, fall_through); Apply(context_, if_true, if_false); } @@ -2187,8 +2057,8 @@ void FullCodeGenerator::EmitObjectEquals(ZoneList<Expression*>* args) { void FullCodeGenerator::EmitArguments(ZoneList<Expression*>* args) { ASSERT(args->length() == 1); - // ArgumentsAccessStub expects the key in edx and the formal - // parameter count in eax. + // ArgumentsAccessStub expects the key in rdx and the formal + // parameter count in rax. VisitForValue(args->at(0), kAccumulator); __ movq(rdx, rax); __ Move(rax, Smi::FromInt(scope()->num_parameters())); @@ -2392,7 +2262,7 @@ void FullCodeGenerator::EmitSetValueOf(ZoneList<Expression*>* args) { VisitForValue(args->at(0), kStack); // Load the object. VisitForValue(args->at(1), kAccumulator); // Load the value. - __ pop(rbx); // rax = value. ebx = object. + __ pop(rbx); // rax = value. rbx = object. Label done; // If the object is a smi, return the value. @@ -2727,6 +2597,40 @@ void FullCodeGenerator::EmitIsRegExpEquivalent(ZoneList<Expression*>* args) { } +void FullCodeGenerator::EmitHasCachedArrayIndex(ZoneList<Expression*>* args) { + ASSERT(args->length() == 1); + + VisitForValue(args->at(0), kAccumulator); + + Label materialize_true, materialize_false; + Label* if_true = NULL; + Label* if_false = NULL; + Label* fall_through = NULL; + PrepareTest(&materialize_true, &materialize_false, + &if_true, &if_false, &fall_through); + + __ testl(FieldOperand(rax, String::kHashFieldOffset), + Immediate(String::kContainsCachedArrayIndexMask)); + __ j(zero, if_true); + __ jmp(if_false); + + Apply(context_, if_true, if_false); +} + + +void FullCodeGenerator::EmitGetCachedArrayIndex(ZoneList<Expression*>* args) { + ASSERT(args->length() == 1); + + VisitForValue(args->at(0), kAccumulator); + + __ movl(rax, FieldOperand(rax, String::kHashFieldOffset)); + ASSERT(String::kHashShift >= kSmiTagSize); + __ IndexFromHash(rax, rax); + + Apply(context_, rax); +} + + void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) { Handle<String> name = expr->name(); if (name->length() > 0 && name->Get(0) == '_') { @@ -2826,19 +2730,7 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { break; } break; - case Expression::kTestValue: - // Value is false so it's needed. - switch (location_) { - case kAccumulator: - __ LoadRoot(result_register(), Heap::kUndefinedValueRootIndex); - break; - case kStack: - __ PushRoot(Heap::kUndefinedValueRootIndex); - break; - } - // Fall through. case Expression::kTest: - case Expression::kValueTest: __ jmp(false_label_); break; } @@ -2850,42 +2742,18 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { Label materialize_true, materialize_false; Label* if_true = NULL; Label* if_false = NULL; - + Label* fall_through = NULL; // Notice that the labels are swapped. - PrepareTest(&materialize_true, &materialize_false, &if_false, &if_true); - - VisitForControl(expr->expression(), if_true, if_false); - + PrepareTest(&materialize_true, &materialize_false, + &if_false, &if_true, &fall_through); + VisitForControl(expr->expression(), if_true, if_false, fall_through); Apply(context_, if_false, if_true); // Labels swapped. break; } case Token::TYPEOF: { Comment cmnt(masm_, "[ UnaryOperation (TYPEOF)"); - VariableProxy* proxy = expr->expression()->AsVariableProxy(); - if (proxy != NULL && - !proxy->var()->is_this() && - proxy->var()->is_global()) { - Comment cmnt(masm_, "Global variable"); - __ Move(rcx, proxy->name()); - __ movq(rax, CodeGenerator::GlobalObject()); - Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize)); - // Use a regular load, not a contextual load, to avoid a reference - // error. - __ Call(ic, RelocInfo::CODE_TARGET); - __ push(rax); - } else if (proxy != NULL && - proxy->var()->slot() != NULL && - proxy->var()->slot()->type() == Slot::LOOKUP) { - __ push(rsi); - __ Push(proxy->name()); - __ CallRuntime(Runtime::kLoadContextSlotNoReferenceError, 2); - __ push(rax); - } else { - // This expression cannot throw a reference error at the top level. - VisitForValue(expr->expression(), kStack); - } - + VisitForTypeofValue(expr->expression(), kStack); __ CallRuntime(Runtime::kTypeof, 1); Apply(context_, rax); break; @@ -2906,9 +2774,7 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { case Token::SUB: { Comment cmt(masm_, "[ UnaryOperation (SUB)"); - bool can_overwrite = - (expr->expression()->AsBinaryOperation() != NULL && - expr->expression()->AsBinaryOperation()->ResultOverwriteAllowed()); + bool can_overwrite = expr->expression()->ResultOverwriteAllowed(); UnaryOverwriteMode overwrite = can_overwrite ? UNARY_OVERWRITE : UNARY_NO_OVERWRITE; GenericUnaryOpStub stub(Token::SUB, overwrite); @@ -2922,27 +2788,24 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { case Token::BIT_NOT: { Comment cmt(masm_, "[ UnaryOperation (BIT_NOT)"); - bool can_overwrite = - (expr->expression()->AsBinaryOperation() != NULL && - expr->expression()->AsBinaryOperation()->ResultOverwriteAllowed()); - UnaryOverwriteMode overwrite = - can_overwrite ? UNARY_OVERWRITE : UNARY_NO_OVERWRITE; - GenericUnaryOpStub stub(Token::BIT_NOT, overwrite); - // GenericUnaryOpStub expects the argument to be in the - // accumulator register rax. + // The generic unary operation stub expects the argument to be + // in the accumulator register rax. VisitForValue(expr->expression(), kAccumulator); - // Avoid calling the stub for Smis. - Label smi, done; - Condition is_smi = masm_->CheckSmi(result_register()); - __ j(is_smi, &smi); - // Non-smi: call stub leaving result in accumulator register. + Label done; + if (ShouldInlineSmiCase(expr->op())) { + Label call_stub; + __ JumpIfNotSmi(rax, &call_stub); + __ SmiNot(rax, rax); + __ jmp(&done); + __ bind(&call_stub); + } + bool overwrite = expr->expression()->ResultOverwriteAllowed(); + UnaryOverwriteMode mode = + overwrite ? UNARY_OVERWRITE : UNARY_NO_OVERWRITE; + GenericUnaryOpStub stub(Token::BIT_NOT, mode); __ CallStub(&stub); - __ jmp(&done); - // Perform operation directly on Smis. - __ bind(&smi); - __ SmiNot(result_register(), result_register()); __ bind(&done); - Apply(context_, result_register()); + Apply(context_, rax); break; } @@ -2954,6 +2817,7 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { Comment cmnt(masm_, "[ CountOperation"); + SetSourcePosition(expr->position()); // Invalid left-hand-sides are rewritten to have a 'throw // ReferenceError' as the left-hand side. @@ -3019,8 +2883,6 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { break; case Expression::kValue: case Expression::kTest: - case Expression::kValueTest: - case Expression::kTestValue: // Save the result on the stack. If we have a named or keyed property // we store the result under the receiver that is currently on top // of the stack. @@ -3041,7 +2903,7 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { // Inline smi case if we are in a loop. Label stub_call, done; - if (loop_depth() > 0) { + if (ShouldInlineSmiCase(expr->op())) { if (expr->op() == Token::INC) { __ SmiAddConstant(rax, rax, Smi::FromInt(1)); } else { @@ -3124,83 +2986,144 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { } } -void FullCodeGenerator::VisitBinaryOperation(BinaryOperation* expr) { - Comment cmnt(masm_, "[ BinaryOperation"); - switch (expr->op()) { - case Token::COMMA: - VisitForEffect(expr->left()); - Visit(expr->right()); - break; - - case Token::OR: - case Token::AND: - EmitLogicalOperation(expr); - break; - - case Token::ADD: - case Token::SUB: - case Token::DIV: - case Token::MOD: - case Token::MUL: - case Token::BIT_OR: - case Token::BIT_AND: - case Token::BIT_XOR: - case Token::SHL: - case Token::SHR: - case Token::SAR: - VisitForValue(expr->left(), kStack); - VisitForValue(expr->right(), kAccumulator); - EmitBinaryOp(expr->op(), context_); - break; - - default: - UNREACHABLE(); - } -} - -void FullCodeGenerator::EmitNullCompare(bool strict, - Register obj, - Register null_const, - Label* if_true, - Label* if_false, - Register scratch) { - __ cmpq(obj, null_const); - if (strict) { - __ j(equal, if_true); +void FullCodeGenerator::VisitForTypeofValue(Expression* expr, Location where) { + VariableProxy* proxy = expr->AsVariableProxy(); + if (proxy != NULL && !proxy->var()->is_this() && proxy->var()->is_global()) { + Comment cmnt(masm_, "Global variable"); + __ Move(rcx, proxy->name()); + __ movq(rax, CodeGenerator::GlobalObject()); + Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize)); + // Use a regular load, not a contextual load, to avoid a reference + // error. + __ Call(ic, RelocInfo::CODE_TARGET); + if (where == kStack) __ push(rax); + } else if (proxy != NULL && + proxy->var()->slot() != NULL && + proxy->var()->slot()->type() == Slot::LOOKUP) { + __ push(rsi); + __ Push(proxy->name()); + __ CallRuntime(Runtime::kLoadContextSlotNoReferenceError, 2); + if (where == kStack) __ push(rax); } else { + // This expression cannot throw a reference error at the top level. + VisitForValue(expr, where); + } +} + + +bool FullCodeGenerator::TryLiteralCompare(Token::Value op, + Expression* left, + Expression* right, + Label* if_true, + Label* if_false, + Label* fall_through) { + if (op != Token::EQ && op != Token::EQ_STRICT) return false; + + // Check for the pattern: typeof <expression> == <string literal>. + Literal* right_literal = right->AsLiteral(); + if (right_literal == NULL) return false; + Handle<Object> right_literal_value = right_literal->handle(); + if (!right_literal_value->IsString()) return false; + UnaryOperation* left_unary = left->AsUnaryOperation(); + if (left_unary == NULL || left_unary->op() != Token::TYPEOF) return false; + Handle<String> check = Handle<String>::cast(right_literal_value); + + VisitForTypeofValue(left_unary->expression(), kAccumulator); + if (check->Equals(Heap::number_symbol())) { + Condition is_smi = masm_->CheckSmi(rax); + __ j(is_smi, if_true); + __ movq(rax, FieldOperand(rax, HeapObject::kMapOffset)); + __ CompareRoot(rax, Heap::kHeapNumberMapRootIndex); + Split(equal, if_true, if_false, fall_through); + } else if (check->Equals(Heap::string_symbol())) { + Condition is_smi = masm_->CheckSmi(rax); + __ j(is_smi, if_false); + // Check for undetectable objects => false. + __ movq(rdx, FieldOperand(rax, HeapObject::kMapOffset)); + __ testb(FieldOperand(rdx, Map::kBitFieldOffset), + Immediate(1 << Map::kIsUndetectable)); + __ j(not_zero, if_false); + __ CmpInstanceType(rdx, FIRST_NONSTRING_TYPE); + Split(below, if_true, if_false, fall_through); + } else if (check->Equals(Heap::boolean_symbol())) { + __ CompareRoot(rax, Heap::kTrueValueRootIndex); __ j(equal, if_true); - __ CompareRoot(obj, Heap::kUndefinedValueRootIndex); + __ CompareRoot(rax, Heap::kFalseValueRootIndex); + Split(equal, if_true, if_false, fall_through); + } else if (check->Equals(Heap::undefined_symbol())) { + __ CompareRoot(rax, Heap::kUndefinedValueRootIndex); __ j(equal, if_true); - __ JumpIfSmi(obj, if_false); - // It can be an undetectable object. - __ movq(scratch, FieldOperand(obj, HeapObject::kMapOffset)); - __ testb(FieldOperand(scratch, Map::kBitFieldOffset), + Condition is_smi = masm_->CheckSmi(rax); + __ j(is_smi, if_false); + // Check for undetectable objects => true. + __ movq(rdx, FieldOperand(rax, HeapObject::kMapOffset)); + __ testb(FieldOperand(rdx, Map::kBitFieldOffset), + Immediate(1 << Map::kIsUndetectable)); + Split(not_zero, if_true, if_false, fall_through); + } else if (check->Equals(Heap::function_symbol())) { + Condition is_smi = masm_->CheckSmi(rax); + __ j(is_smi, if_false); + __ CmpObjectType(rax, JS_FUNCTION_TYPE, rdx); + __ j(equal, if_true); + // Regular expressions => 'function' (they are callable). + __ CmpInstanceType(rdx, JS_REGEXP_TYPE); + Split(equal, if_true, if_false, fall_through); + } else if (check->Equals(Heap::object_symbol())) { + Condition is_smi = masm_->CheckSmi(rax); + __ j(is_smi, if_false); + __ CompareRoot(rax, Heap::kNullValueRootIndex); + __ j(equal, if_true); + // Regular expressions => 'function', not 'object'. + __ CmpObjectType(rax, JS_REGEXP_TYPE, rdx); + __ j(equal, if_false); + // Check for undetectable objects => false. + __ testb(FieldOperand(rdx, Map::kBitFieldOffset), Immediate(1 << Map::kIsUndetectable)); - __ j(not_zero, if_true); + __ j(not_zero, if_false); + // Check for JS objects => true. + __ CmpInstanceType(rdx, FIRST_JS_OBJECT_TYPE); + __ j(below, if_false); + __ CmpInstanceType(rdx, LAST_JS_OBJECT_TYPE); + Split(below_equal, if_true, if_false, fall_through); + } else { + if (if_false != fall_through) __ jmp(if_false); } - __ jmp(if_false); + + return true; } void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { Comment cmnt(masm_, "[ CompareOperation"); + SetSourcePosition(expr->position()); // Always perform the comparison for its control flow. Pack the result // into the expression's context after the comparison is performed. Label materialize_true, materialize_false; Label* if_true = NULL; Label* if_false = NULL; - PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); + Label* fall_through = NULL; + PrepareTest(&materialize_true, &materialize_false, + &if_true, &if_false, &fall_through); + + // First we try a fast inlined version of the compare when one of + // the operands is a literal. + Token::Value op = expr->op(); + Expression* left = expr->left(); + Expression* right = expr->right(); + if (TryLiteralCompare(op, left, right, if_true, if_false, fall_through)) { + Apply(context_, if_true, if_false); + return; + } VisitForValue(expr->left(), kStack); - switch (expr->op()) { + switch (op) { case Token::IN: VisitForValue(expr->right(), kStack); __ InvokeBuiltin(Builtins::IN, CALL_FUNCTION); __ CompareRoot(rax, Heap::kTrueValueRootIndex); - __ j(equal, if_true); - __ jmp(if_false); + Split(equal, if_true, if_false, fall_through); break; case Token::INSTANCEOF: { @@ -3208,8 +3131,8 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { InstanceofStub stub; __ CallStub(&stub); __ testq(rax, rax); - __ j(zero, if_true); // The stub returns 0 for true. - __ jmp(if_false); + // The stub returns 0 for true. + Split(zero, if_true, if_false, fall_through); break; } @@ -3217,28 +3140,14 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { VisitForValue(expr->right(), kAccumulator); Condition cc = no_condition; bool strict = false; - switch (expr->op()) { + switch (op) { case Token::EQ_STRICT: strict = true; // Fall through. - case Token::EQ: { + case Token::EQ: cc = equal; __ pop(rdx); - // If either operand is constant null we do a fast compare - // against null. - Literal* right_literal = expr->right()->AsLiteral(); - Literal* left_literal = expr->left()->AsLiteral(); - if (right_literal != NULL && right_literal->handle()->IsNull()) { - EmitNullCompare(strict, rdx, rax, if_true, if_false, rcx); - Apply(context_, if_true, if_false); - return; - } else if (left_literal != NULL && left_literal->handle()->IsNull()) { - EmitNullCompare(strict, rax, rdx, if_true, if_false, rcx); - Apply(context_, if_true, if_false); - return; - } break; - } case Token::LT: cc = less; __ pop(rdx); @@ -3265,20 +3174,18 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { UNREACHABLE(); } - // The comparison stub expects the smi vs. smi case to be handled - // before it is called. - Label slow_case; - __ JumpIfNotBothSmi(rax, rdx, &slow_case); - __ SmiCompare(rdx, rax); - __ j(cc, if_true); - __ jmp(if_false); + if (ShouldInlineSmiCase(op)) { + Label slow_case; + __ JumpIfNotBothSmi(rax, rdx, &slow_case); + __ SmiCompare(rdx, rax); + Split(cc, if_true, if_false, NULL); + __ bind(&slow_case); + } - __ bind(&slow_case); CompareStub stub(cc, strict); __ CallStub(&stub); __ testq(rax, rax); - __ j(cc, if_true); - __ jmp(if_false); + Split(cc, if_true, if_false, fall_through); } } @@ -3288,6 +3195,35 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { } +void FullCodeGenerator::VisitCompareToNull(CompareToNull* expr) { + Comment cmnt(masm_, "[ CompareToNull"); + Label materialize_true, materialize_false; + Label* if_true = NULL; + Label* if_false = NULL; + Label* fall_through = NULL; + PrepareTest(&materialize_true, &materialize_false, + &if_true, &if_false, &fall_through); + + VisitForValue(expr->expression(), kAccumulator); + __ CompareRoot(rax, Heap::kNullValueRootIndex); + if (expr->is_strict()) { + Split(equal, if_true, if_false, fall_through); + } else { + __ j(equal, if_true); + __ CompareRoot(rax, Heap::kUndefinedValueRootIndex); + __ j(equal, if_true); + Condition is_smi = masm_->CheckSmi(rax); + __ j(is_smi, if_false); + // It can be an undetectable object. + __ movq(rdx, FieldOperand(rax, HeapObject::kMapOffset)); + __ testb(FieldOperand(rdx, Map::kBitFieldOffset), + Immediate(1 << Map::kIsUndetectable)); + Split(not_zero, if_true, if_false, fall_through); + } + Apply(context_, if_true, if_false); +} + + void FullCodeGenerator::VisitThisFunction(ThisFunction* expr) { __ movq(rax, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset)); Apply(context_, rax); |