New version of v8 from bleeding edge at revision 3649
diff --git a/src/x64/assembler-x64.cc b/src/x64/assembler-x64.cc
index 2d524ea..4ac3933 100644
--- a/src/x64/assembler-x64.cc
+++ b/src/x64/assembler-x64.cc
@@ -1880,6 +1880,20 @@
}
+void Assembler::testb(const Operand& op, Register reg) {
+ EnsureSpace ensure_space(this);
+ last_pc_ = pc_;
+ if (reg.code() > 3) {
+ // Register is not one of al, bl, cl, dl. Its encoding needs REX.
+ emit_rex_32(reg, op);
+ } else {
+ emit_optional_rex_32(reg, op);
+ }
+ emit(0x84);
+ emit_operand(reg, op);
+}
+
+
void Assembler::testl(Register dst, Register src) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
diff --git a/src/x64/assembler-x64.h b/src/x64/assembler-x64.h
index fa7d33b..1bddb2f 100644
--- a/src/x64/assembler-x64.h
+++ b/src/x64/assembler-x64.h
@@ -916,6 +916,10 @@
arithmetic_op_32(0x2B, dst, src);
}
+ void subl(Register dst, const Operand& src) {
+ arithmetic_op_32(0x2B, dst, src);
+ }
+
void subl(const Operand& dst, Immediate src) {
immediate_arithmetic_op_32(0x5, dst, src);
}
@@ -931,6 +935,7 @@
void testb(Register dst, Register src);
void testb(Register reg, Immediate mask);
void testb(const Operand& op, Immediate mask);
+ void testb(const Operand& op, Register reg);
void testl(Register dst, Register src);
void testl(Register reg, Immediate mask);
void testl(const Operand& op, Immediate mask);
diff --git a/src/x64/builtins-x64.cc b/src/x64/builtins-x64.cc
index f444d2c..0b95bba 100644
--- a/src/x64/builtins-x64.cc
+++ b/src/x64/builtins-x64.cc
@@ -34,16 +34,36 @@
#define __ ACCESS_MASM(masm)
-void Builtins::Generate_Adaptor(MacroAssembler* masm, CFunctionId id) {
- // TODO(428): Don't pass the function in a static variable.
- ExternalReference passed = ExternalReference::builtin_passed_function();
- __ movq(kScratchRegister, passed.address(), RelocInfo::EXTERNAL_REFERENCE);
- __ movq(Operand(kScratchRegister, 0), rdi);
- // The actual argument count has already been loaded into register
- // rax, but JumpToRuntime expects rax to contain the number of
- // arguments including the receiver.
- __ incq(rax);
+void Builtins::Generate_Adaptor(MacroAssembler* masm,
+ CFunctionId id,
+ BuiltinExtraArguments extra_args) {
+ // ----------- S t a t e -------------
+ // -- rax : number of arguments excluding receiver
+ // -- rdi : called function (only guaranteed when
+ // extra_args requires it)
+ // -- rsi : context
+ // -- rsp[0] : return address
+ // -- rsp[8] : last argument
+ // -- ...
+ // -- rsp[8 * argc] : first argument (argc == rax)
+ // -- rsp[8 * (argc +1)] : receiver
+ // -----------------------------------
+
+ // Insert extra arguments.
+ int num_extra_args = 0;
+ if (extra_args == NEEDS_CALLED_FUNCTION) {
+ num_extra_args = 1;
+ __ pop(kScratchRegister); // Save return address.
+ __ push(rdi);
+ __ push(kScratchRegister); // Restore return address.
+ } else {
+ ASSERT(extra_args == NO_EXTRA_ARGUMENTS);
+ }
+
+ // JumpToRuntime expects rax to contain the number of arguments
+ // including the receiver and the extra arguments.
+ __ addq(rax, Immediate(num_extra_args + 1));
__ JumpToRuntime(ExternalReference(id), 1);
}
@@ -888,7 +908,8 @@
}
-void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) {
+static void Generate_JSConstructStubHelper(MacroAssembler* masm,
+ bool is_api_function) {
// Enter a construct frame.
__ EnterConstructFrame();
@@ -1091,8 +1112,17 @@
__ j(greater_equal, &loop);
// Call the function.
- ParameterCount actual(rax);
- __ InvokeFunction(rdi, actual, CALL_FUNCTION);
+ if (is_api_function) {
+ __ movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset));
+ Handle<Code> code = Handle<Code>(
+ Builtins::builtin(Builtins::HandleApiCallConstruct));
+ ParameterCount expected(0);
+ __ InvokeCode(code, expected, expected,
+ RelocInfo::CODE_TARGET, CALL_FUNCTION);
+ } else {
+ ParameterCount actual(rax);
+ __ InvokeFunction(rdi, actual, CALL_FUNCTION);
+ }
// Restore context from the frame.
__ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
@@ -1129,6 +1159,16 @@
}
+void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) {
+ Generate_JSConstructStubHelper(masm, false);
+}
+
+
+void Builtins::Generate_JSConstructStubApi(MacroAssembler* masm) {
+ Generate_JSConstructStubHelper(masm, true);
+}
+
+
static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm,
bool is_construct) {
// Expects five C++ function parameters.
diff --git a/src/x64/codegen-x64-inl.h b/src/x64/codegen-x64-inl.h
index 6869fc9..60e9ab0 100644
--- a/src/x64/codegen-x64-inl.h
+++ b/src/x64/codegen-x64-inl.h
@@ -39,16 +39,6 @@
void DeferredCode::Jump() { __ jmp(&entry_label_); }
void DeferredCode::Branch(Condition cc) { __ j(cc, &entry_label_); }
-
-void CodeGenerator::GenerateMathSin(ZoneList<Expression*>* args) {
- GenerateFastMathOp(SIN, args);
-}
-
-
-void CodeGenerator::GenerateMathCos(ZoneList<Expression*>* args) {
- GenerateFastMathOp(COS, args);
-}
-
#undef __
} } // namespace v8::internal
diff --git a/src/x64/codegen-x64.cc b/src/x64/codegen-x64.cc
index 36f0e63..0cf68eb 100644
--- a/src/x64/codegen-x64.cc
+++ b/src/x64/codegen-x64.cc
@@ -326,12 +326,19 @@
function_return_is_shadowed_ = false;
// Allocate the local context if needed.
- if (scope_->num_heap_slots() > 0) {
+ int heap_slots = scope_->num_heap_slots();
+ if (heap_slots > 0) {
Comment cmnt(masm_, "[ allocate local context");
// Allocate local context.
// Get outer context and create a new context based on it.
frame_->PushFunction();
- Result context = frame_->CallRuntime(Runtime::kNewContext, 1);
+ Result context;
+ if (heap_slots <= FastNewContextStub::kMaximumSlots) {
+ FastNewContextStub stub(heap_slots);
+ context = frame_->CallStub(&stub, 1);
+ } else {
+ context = frame_->CallRuntime(Runtime::kNewContext, 1);
+ }
// Update context local.
frame_->SaveContextRegister();
@@ -393,6 +400,12 @@
StoreArgumentsObject(true);
}
+ // Initialize ThisFunction reference if present.
+ if (scope_->is_function_scope() && scope_->function() != NULL) {
+ frame_->Push(Factory::the_hole_value());
+ StoreToSlot(scope_->function()->slot(), NOT_CONST_INIT);
+ }
+
// Generate code to 'execute' declarations and initialize functions
// (source elements). In case of an illegal redeclaration we need to
// handle that instead of processing the declarations.
@@ -802,7 +815,7 @@
frame_->Push(&fn);
frame_->Push(&a1);
frame_->Push(&a2);
- CallFunctionStub call_function(2, NOT_IN_LOOP);
+ CallFunctionStub call_function(2, NOT_IN_LOOP, NO_CALL_FUNCTION_FLAGS);
Result res = frame_->CallStub(&call_function, 3);
frame_->Push(&res);
@@ -1865,13 +1878,9 @@
frame_->EmitPush(rax);
// Store the caught exception in the catch variable.
- { Reference ref(this, node->catch_var());
- ASSERT(ref.is_slot());
- // Load the exception to the top of the stack. Here we make use of the
- // convenient property that it doesn't matter whether a value is
- // immediately on top of or underneath a zero-sized reference.
- ref.SetValue(NOT_CONST_INIT);
- }
+ Variable* catch_var = node->catch_var()->var();
+ ASSERT(catch_var != NULL && catch_var->slot() != NULL);
+ StoreToSlot(catch_var->slot(), NOT_CONST_INIT);
// Remove the exception from the stack.
frame_->Drop();
@@ -2196,19 +2205,28 @@
void CodeGenerator::InstantiateBoilerplate(Handle<JSFunction> boilerplate) {
- // Call the runtime to instantiate the function boilerplate object.
+ ASSERT(boilerplate->IsBoilerplate());
+
// The inevitable call will sync frame elements to memory anyway, so
// we do it eagerly to allow us to push the arguments directly into
// place.
- ASSERT(boilerplate->IsBoilerplate());
frame_->SyncRange(0, frame_->element_count() - 1);
- // Create a new closure.
- frame_->EmitPush(rsi);
- __ movq(kScratchRegister, boilerplate, RelocInfo::EMBEDDED_OBJECT);
- frame_->EmitPush(kScratchRegister);
- Result result = frame_->CallRuntime(Runtime::kNewClosure, 2);
- frame_->Push(&result);
+ // Use the fast case closure allocation code that allocates in new
+ // space for nested functions that don't need literals cloning.
+ if (scope()->is_function_scope() && boilerplate->NumberOfLiterals() == 0) {
+ FastNewClosureStub stub;
+ frame_->Push(boilerplate);
+ Result answer = frame_->CallStub(&stub, 1);
+ frame_->Push(&answer);
+ } else {
+ // Call the runtime to instantiate the function boilerplate
+ // object.
+ frame_->EmitPush(rsi);
+ frame_->EmitPush(boilerplate);
+ Result result = frame_->CallRuntime(Runtime::kNewClosure, 2);
+ frame_->Push(&result);
+ }
}
@@ -2362,46 +2380,10 @@
}
-// Materialize the object literal 'node' in the literals array
-// 'literals' of the function. Leave the object boilerplate in
-// 'boilerplate'.
-class DeferredObjectLiteral: public DeferredCode {
- public:
- DeferredObjectLiteral(Register boilerplate,
- Register literals,
- ObjectLiteral* node)
- : boilerplate_(boilerplate), literals_(literals), node_(node) {
- set_comment("[ DeferredObjectLiteral");
- }
-
- void Generate();
-
- private:
- Register boilerplate_;
- Register literals_;
- ObjectLiteral* node_;
-};
-
-
-void DeferredObjectLiteral::Generate() {
- // Since the entry is undefined we call the runtime system to
- // compute the literal.
- // Literal array (0).
- __ push(literals_);
- // Literal index (1).
- __ Push(Smi::FromInt(node_->literal_index()));
- // Constant properties (2).
- __ Push(node_->constant_properties());
- __ CallRuntime(Runtime::kCreateObjectLiteralBoilerplate, 3);
- if (!boilerplate_.is(rax)) __ movq(boilerplate_, rax);
-}
-
-
void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) {
Comment cmnt(masm_, "[ ObjectLiteral");
- // Retrieve the literals array and check the allocated entry. Begin
- // with a writable copy of the function of this activation in a
+ // Load a writable copy of the function of this activation in a
// register.
frame_->PushFunction();
Result literals = frame_->Pop();
@@ -2411,32 +2393,18 @@
// Load the literals array of the function.
__ movq(literals.reg(),
FieldOperand(literals.reg(), JSFunction::kLiteralsOffset));
-
- // Load the literal at the ast saved index.
- Result boilerplate = allocator_->Allocate();
- ASSERT(boilerplate.is_valid());
- int literal_offset =
- FixedArray::kHeaderSize + node->literal_index() * kPointerSize;
- __ movq(boilerplate.reg(), FieldOperand(literals.reg(), literal_offset));
-
- // Check whether we need to materialize the object literal boilerplate.
- // If so, jump to the deferred code passing the literals array.
- DeferredObjectLiteral* deferred =
- new DeferredObjectLiteral(boilerplate.reg(), literals.reg(), node);
- __ CompareRoot(boilerplate.reg(), Heap::kUndefinedValueRootIndex);
- deferred->Branch(equal);
- deferred->BindExit();
- literals.Unuse();
-
- // Push the boilerplate object.
- frame_->Push(&boilerplate);
- // Clone the boilerplate object.
- Runtime::FunctionId clone_function_id = Runtime::kCloneLiteralBoilerplate;
- if (node->depth() == 1) {
- clone_function_id = Runtime::kCloneShallowLiteralBoilerplate;
+ // Literal array.
+ frame_->Push(&literals);
+ // Literal index.
+ frame_->Push(Smi::FromInt(node->literal_index()));
+ // Constant properties.
+ frame_->Push(node->constant_properties());
+ Result clone;
+ if (node->depth() > 1) {
+ clone = frame_->CallRuntime(Runtime::kCreateObjectLiteral, 3);
+ } else {
+ clone = frame_->CallRuntime(Runtime::kCreateObjectLiteralShallow, 3);
}
- Result clone = frame_->CallRuntime(clone_function_id, 1);
- // Push the newly cloned literal object as the result.
frame_->Push(&clone);
for (int i = 0; i < node->properties()->length(); i++) {
@@ -2496,45 +2464,10 @@
}
-// Materialize the array literal 'node' in the literals array 'literals'
-// of the function. Leave the array boilerplate in 'boilerplate'.
-class DeferredArrayLiteral: public DeferredCode {
- public:
- DeferredArrayLiteral(Register boilerplate,
- Register literals,
- ArrayLiteral* node)
- : boilerplate_(boilerplate), literals_(literals), node_(node) {
- set_comment("[ DeferredArrayLiteral");
- }
-
- void Generate();
-
- private:
- Register boilerplate_;
- Register literals_;
- ArrayLiteral* node_;
-};
-
-
-void DeferredArrayLiteral::Generate() {
- // Since the entry is undefined we call the runtime system to
- // compute the literal.
- // Literal array (0).
- __ push(literals_);
- // Literal index (1).
- __ Push(Smi::FromInt(node_->literal_index()));
- // Constant properties (2).
- __ Push(node_->literals());
- __ CallRuntime(Runtime::kCreateArrayLiteralBoilerplate, 3);
- if (!boilerplate_.is(rax)) __ movq(boilerplate_, rax);
-}
-
-
void CodeGenerator::VisitArrayLiteral(ArrayLiteral* node) {
Comment cmnt(masm_, "[ ArrayLiteral");
- // Retrieve the literals array and check the allocated entry. Begin
- // with a writable copy of the function of this activation in a
+ // Load a writable copy of the function of this activation in a
// register.
frame_->PushFunction();
Result literals = frame_->Pop();
@@ -2544,32 +2477,18 @@
// Load the literals array of the function.
__ movq(literals.reg(),
FieldOperand(literals.reg(), JSFunction::kLiteralsOffset));
-
- // Load the literal at the ast saved index.
- Result boilerplate = allocator_->Allocate();
- ASSERT(boilerplate.is_valid());
- int literal_offset =
- FixedArray::kHeaderSize + node->literal_index() * kPointerSize;
- __ movq(boilerplate.reg(), FieldOperand(literals.reg(), literal_offset));
-
- // Check whether we need to materialize the object literal boilerplate.
- // If so, jump to the deferred code passing the literals array.
- DeferredArrayLiteral* deferred =
- new DeferredArrayLiteral(boilerplate.reg(), literals.reg(), node);
- __ CompareRoot(boilerplate.reg(), Heap::kUndefinedValueRootIndex);
- deferred->Branch(equal);
- deferred->BindExit();
- literals.Unuse();
-
- // Push the resulting array literal boilerplate on the stack.
- frame_->Push(&boilerplate);
- // Clone the boilerplate object.
- Runtime::FunctionId clone_function_id = Runtime::kCloneLiteralBoilerplate;
- if (node->depth() == 1) {
- clone_function_id = Runtime::kCloneShallowLiteralBoilerplate;
+ // Literal array.
+ frame_->Push(&literals);
+ // Literal index.
+ frame_->Push(Smi::FromInt(node->literal_index()));
+ // Constant elements.
+ frame_->Push(node->constant_elements());
+ Result clone;
+ if (node->depth() > 1) {
+ clone = frame_->CallRuntime(Runtime::kCreateArrayLiteral, 3);
+ } else {
+ clone = frame_->CallRuntime(Runtime::kCreateArrayLiteralShallow, 3);
}
- Result clone = frame_->CallRuntime(clone_function_id, 1);
- // Push the newly cloned literal object as the result.
frame_->Push(&clone);
// Generate code to set the elements in the array that are not
@@ -2770,28 +2689,24 @@
frame_->Push(Factory::undefined_value());
}
+ // Push the receiver.
+ frame_->PushParameterAt(-1);
+
// Resolve the call.
Result result =
- frame_->CallRuntime(Runtime::kResolvePossiblyDirectEval, 2);
+ frame_->CallRuntime(Runtime::kResolvePossiblyDirectEval, 3);
- // Touch up the stack with the right values for the function and the
- // receiver. Use a scratch register to avoid destroying the result.
- Result scratch = allocator_->Allocate();
- ASSERT(scratch.is_valid());
- __ movq(scratch.reg(),
- FieldOperand(result.reg(), FixedArray::OffsetOfElementAt(0)));
- frame_->SetElementAt(arg_count + 1, &scratch);
-
- // We can reuse the result register now.
- frame_->Spill(result.reg());
- __ movq(result.reg(),
- FieldOperand(result.reg(), FixedArray::OffsetOfElementAt(1)));
- frame_->SetElementAt(arg_count, &result);
+ // The runtime call returns a pair of values in rax (function) and
+ // rdx (receiver). Touch up the stack with the right values.
+ Result receiver = allocator_->Allocate(rdx);
+ frame_->SetElementAt(arg_count + 1, &result);
+ frame_->SetElementAt(arg_count, &receiver);
+ receiver.Unuse();
// Call the function.
CodeForSourcePosition(node->position());
InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
- CallFunctionStub call_function(arg_count, in_loop);
+ CallFunctionStub call_function(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE);
result = frame_->CallStub(&call_function, arg_count + 1);
// Restore the context and overwrite the function on the stack with
@@ -2852,7 +2767,7 @@
frame_->EmitPush(rdx);
// Call the function.
- CallWithArguments(args, node->position());
+ CallWithArguments(args, NO_CALL_FUNCTION_FLAGS, node->position());
} else if (property != NULL) {
// Check if the key is a literal string.
@@ -2917,7 +2832,7 @@
}
// Call the function.
- CallWithArguments(args, node->position());
+ CallWithArguments(args, RECEIVER_MIGHT_BE_VALUE, node->position());
}
} else {
@@ -2932,7 +2847,7 @@
LoadGlobalReceiver();
// Call the function.
- CallWithArguments(args, node->position());
+ CallWithArguments(args, NO_CALL_FUNCTION_FLAGS, node->position());
}
}
@@ -3109,7 +3024,7 @@
bool overwrite =
(node->expression()->AsBinaryOperation() != NULL &&
node->expression()->AsBinaryOperation()->ResultOverwriteAllowed());
- UnarySubStub stub(overwrite);
+ GenericUnaryOpStub stub(Token::SUB, overwrite);
// TODO(1222589): remove dependency of TOS being cached inside stub
Result operand = frame_->Pop();
Result answer = frame_->CallStub(&stub, &operand);
@@ -3979,69 +3894,16 @@
}
-void CodeGenerator::GenerateFastMathOp(MathOp op, ZoneList<Expression*>* args) {
- JumpTarget done;
- JumpTarget call_runtime;
- ASSERT(args->length() == 1);
+void CodeGenerator::GenerateRegExpExec(ZoneList<Expression*>* args) {
+ ASSERT_EQ(args->length(), 4);
- // Load number and duplicate it.
+ // Load the arguments on the stack and call the runtime system.
Load(args->at(0));
- frame_->Dup();
-
- // Get the number into an unaliased register and load it onto the
- // floating point stack still leaving one copy on the frame.
- Result number = frame_->Pop();
- number.ToRegister();
- frame_->Spill(number.reg());
- FloatingPointHelper::LoadFloatOperand(masm_, number.reg());
- number.Unuse();
-
- // Perform the operation on the number.
- switch (op) {
- case SIN:
- __ fsin();
- break;
- case COS:
- __ fcos();
- break;
- }
-
- // Go slow case if argument to operation is out of range.
- Result eax_reg = allocator()->Allocate(rax);
- ASSERT(eax_reg.is_valid());
- __ fnstsw_ax();
- __ testl(rax, Immediate(0x0400)); // Bit 10 is condition flag C2.
- eax_reg.Unuse();
- call_runtime.Branch(not_zero);
-
- // Allocate heap number for result if possible.
- Result scratch = allocator()->Allocate();
- Result heap_number = allocator()->Allocate();
- __ AllocateHeapNumber(heap_number.reg(),
- scratch.reg(),
- call_runtime.entry_label());
- scratch.Unuse();
-
- // Store the result in the allocated heap number.
- __ fstp_d(FieldOperand(heap_number.reg(), HeapNumber::kValueOffset));
- // Replace the extra copy of the argument with the result.
- frame_->SetElementAt(0, &heap_number);
- done.Jump();
-
- call_runtime.Bind();
- // Free ST(0) which was not popped before calling into the runtime.
- __ ffree(0);
- Result answer;
- switch (op) {
- case SIN:
- answer = frame_->CallRuntime(Runtime::kMath_sin, 1);
- break;
- case COS:
- answer = frame_->CallRuntime(Runtime::kMath_cos, 1);
- break;
- }
- frame_->Push(&answer);
- done.Bind();
+ Load(args->at(1));
+ Load(args->at(2));
+ Load(args->at(3));
+ Result result = frame_->CallRuntime(Runtime::kRegExpExec, 4);
+ frame_->Push(&result);
}
@@ -4051,7 +3913,32 @@
Load(args->at(0));
Load(args->at(1));
- Result answer = frame_->CallRuntime(Runtime::kStringAdd, 2);
+ StringAddStub stub(NO_STRING_ADD_FLAGS);
+ Result answer = frame_->CallStub(&stub, 2);
+ frame_->Push(&answer);
+}
+
+
+void CodeGenerator::GenerateSubString(ZoneList<Expression*>* args) {
+ ASSERT_EQ(3, args->length());
+
+ Load(args->at(0));
+ Load(args->at(1));
+ Load(args->at(2));
+
+ Result answer = frame_->CallRuntime(Runtime::kSubString, 3);
+ frame_->Push(&answer);
+}
+
+
+void CodeGenerator::GenerateStringCompare(ZoneList<Expression*>* args) {
+ ASSERT_EQ(2, args->length());
+
+ Load(args->at(0));
+ Load(args->at(1));
+
+ StringCompareStub stub;
+ Result answer = frame_->CallStub(&stub, 2);
frame_->Push(&answer);
}
@@ -4379,15 +4266,7 @@
// The expression is either a property or a variable proxy that rewrites
// to a property.
Load(property->obj());
- // We use a named reference if the key is a literal symbol, unless it is
- // a string that can be legally parsed as an integer. This is because
- // otherwise we will not get into the slow case code that handles [] on
- // String objects.
- Literal* literal = property->key()->AsLiteral();
- uint32_t dummy;
- if (literal != NULL &&
- literal->handle()->IsSymbol() &&
- !String::cast(*(literal->handle()))->AsArrayIndex(&dummy)) {
+ if (property->key()->IsPropertyName()) {
ref->set_type(Reference::NAMED);
} else {
Load(property->key());
@@ -4863,36 +4742,34 @@
frame_->Push(&result);
}
- { Reference shadow_ref(this, scope_->arguments_shadow());
- Reference arguments_ref(this, scope_->arguments());
- ASSERT(shadow_ref.is_slot() && arguments_ref.is_slot());
- // Here we rely on the convenient property that references to slot
- // take up zero space in the frame (ie, it doesn't matter that the
- // stored value is actually below the reference on the frame).
- JumpTarget done;
- bool skip_arguments = false;
- if (mode == LAZY_ARGUMENTS_ALLOCATION && !initial) {
- // We have to skip storing into the arguments slot if it has
- // already been written to. This can happen if the a function
- // has a local variable named 'arguments'.
- LoadFromSlot(scope_->arguments()->var()->slot(), NOT_INSIDE_TYPEOF);
- Result arguments = frame_->Pop();
- if (arguments.is_constant()) {
- // We have to skip updating the arguments object if it has
- // been assigned a proper value.
- skip_arguments = !arguments.handle()->IsTheHole();
- } else {
- __ CompareRoot(arguments.reg(), Heap::kTheHoleValueRootIndex);
- arguments.Unuse();
- done.Branch(not_equal);
- }
+
+ Variable* arguments = scope_->arguments()->var();
+ Variable* shadow = scope_->arguments_shadow()->var();
+ ASSERT(arguments != NULL && arguments->slot() != NULL);
+ ASSERT(shadow != NULL && shadow->slot() != NULL);
+ JumpTarget done;
+ bool skip_arguments = false;
+ if (mode == LAZY_ARGUMENTS_ALLOCATION && !initial) {
+ // We have to skip storing into the arguments slot if it has
+ // already been written to. This can happen if the a function
+ // has a local variable named 'arguments'.
+ LoadFromSlot(scope_->arguments()->var()->slot(), NOT_INSIDE_TYPEOF);
+ Result probe = frame_->Pop();
+ if (probe.is_constant()) {
+ // We have to skip updating the arguments object if it has been
+ // assigned a proper value.
+ skip_arguments = !probe.handle()->IsTheHole();
+ } else {
+ __ CompareRoot(probe.reg(), Heap::kTheHoleValueRootIndex);
+ probe.Unuse();
+ done.Branch(not_equal);
}
- if (!skip_arguments) {
- arguments_ref.SetValue(NOT_CONST_INIT);
- if (mode == LAZY_ARGUMENTS_ALLOCATION) done.Bind();
- }
- shadow_ref.SetValue(NOT_CONST_INIT);
}
+ if (!skip_arguments) {
+ StoreToSlot(arguments->slot(), NOT_CONST_INIT);
+ if (mode == LAZY_ARGUMENTS_ALLOCATION) done.Bind();
+ }
+ StoreToSlot(shadow->slot(), NOT_CONST_INIT);
return frame_->Pop();
}
@@ -5126,7 +5003,7 @@
void CodeGenerator::GenericBinaryOperation(Token::Value op,
- SmiAnalysis* type,
+ StaticType* type,
OverwriteMode overwrite_mode) {
Comment cmnt(masm_, "[ BinaryOperation");
Comment cmnt_token(masm_, Token::String(op));
@@ -5315,7 +5192,7 @@
void CodeGenerator::ConstantSmiBinaryOperation(Token::Value op,
Result* operand,
Handle<Object> value,
- SmiAnalysis* type,
+ StaticType* type,
bool reversed,
OverwriteMode overwrite_mode) {
// NOTE: This is an attempt to inline (a bit) more of the code for
@@ -6098,7 +5975,7 @@
// a loop and the key is likely to be a smi.
Property* property = expression()->AsProperty();
ASSERT(property != NULL);
- SmiAnalysis* key_smi_analysis = property->key()->type();
+ StaticType* key_smi_analysis = property->key()->type();
if (cgen_->loop_nesting() > 0 && key_smi_analysis->IsLikelySmi()) {
Comment cmnt(masm, "[ Inlined store to keyed Property");
@@ -6198,6 +6075,91 @@
}
+void FastNewClosureStub::Generate(MacroAssembler* masm) {
+ // Clone the boilerplate in new space. Set the context to the
+ // current context in rsi.
+ Label gc;
+ __ AllocateInNewSpace(JSFunction::kSize, rax, rbx, rcx, &gc, TAG_OBJECT);
+
+ // Get the boilerplate function from the stack.
+ __ movq(rdx, Operand(rsp, 1 * kPointerSize));
+
+ // Compute the function map in the current global context and set that
+ // as the map of the allocated object.
+ __ movq(rcx, Operand(rsi, Context::SlotOffset(Context::GLOBAL_INDEX)));
+ __ movq(rcx, FieldOperand(rcx, GlobalObject::kGlobalContextOffset));
+ __ movq(rcx, Operand(rcx, Context::SlotOffset(Context::FUNCTION_MAP_INDEX)));
+ __ movq(FieldOperand(rax, JSObject::kMapOffset), rcx);
+
+ // Clone the rest of the boilerplate fields. We don't have to update
+ // the write barrier because the allocated object is in new space.
+ for (int offset = kPointerSize;
+ offset < JSFunction::kSize;
+ offset += kPointerSize) {
+ if (offset == JSFunction::kContextOffset) {
+ __ movq(FieldOperand(rax, offset), rsi);
+ } else {
+ __ movq(rbx, FieldOperand(rdx, offset));
+ __ movq(FieldOperand(rax, offset), rbx);
+ }
+ }
+
+ // Return and remove the on-stack parameter.
+ __ ret(1 * kPointerSize);
+
+ // Create a new closure through the slower runtime call.
+ __ bind(&gc);
+ __ pop(rcx); // Temporarily remove return address.
+ __ pop(rdx);
+ __ push(rsi);
+ __ push(rdx);
+ __ push(rcx); // Restore return address.
+ __ TailCallRuntime(ExternalReference(Runtime::kNewClosure), 2, 1);
+}
+
+
+void FastNewContextStub::Generate(MacroAssembler* masm) {
+ // Try to allocate the context in new space.
+ Label gc;
+ int length = slots_ + Context::MIN_CONTEXT_SLOTS;
+ __ AllocateInNewSpace((length * kPointerSize) + FixedArray::kHeaderSize,
+ rax, rbx, rcx, &gc, TAG_OBJECT);
+
+ // Get the function from the stack.
+ __ movq(rcx, Operand(rsp, 1 * kPointerSize));
+
+ // Setup the object header.
+ __ LoadRoot(kScratchRegister, Heap::kContextMapRootIndex);
+ __ movq(FieldOperand(rax, HeapObject::kMapOffset), kScratchRegister);
+ __ movl(FieldOperand(rax, Array::kLengthOffset), Immediate(length));
+
+ // Setup the fixed slots.
+ __ xor_(rbx, rbx); // Set to NULL.
+ __ movq(Operand(rax, Context::SlotOffset(Context::CLOSURE_INDEX)), rcx);
+ __ movq(Operand(rax, Context::SlotOffset(Context::FCONTEXT_INDEX)), rax);
+ __ movq(Operand(rax, Context::SlotOffset(Context::PREVIOUS_INDEX)), rbx);
+ __ movq(Operand(rax, Context::SlotOffset(Context::EXTENSION_INDEX)), rbx);
+
+ // Copy the global object from the surrounding context.
+ __ movq(rbx, Operand(rsi, Context::SlotOffset(Context::GLOBAL_INDEX)));
+ __ movq(Operand(rax, Context::SlotOffset(Context::GLOBAL_INDEX)), rbx);
+
+ // Initialize the rest of the slots to undefined.
+ __ LoadRoot(rbx, Heap::kUndefinedValueRootIndex);
+ for (int i = Context::MIN_CONTEXT_SLOTS; i < length; i++) {
+ __ movq(Operand(rax, Context::SlotOffset(i)), rbx);
+ }
+
+ // Return and remove the on-stack parameter.
+ __ movq(rsi, rax);
+ __ ret(1 * kPointerSize);
+
+ // Need to collect. Call into runtime system.
+ __ bind(&gc);
+ __ TailCallRuntime(ExternalReference(Runtime::kNewContext), 1, 1);
+}
+
+
void ToBooleanStub::Generate(MacroAssembler* masm) {
Label false_result, true_result, not_string;
__ movq(rax, Operand(rsp, 1 * kPointerSize));
@@ -6337,7 +6299,9 @@
// End of CodeGenerator implementation.
-void UnarySubStub::Generate(MacroAssembler* masm) {
+void GenericUnaryOpStub::Generate(MacroAssembler* masm) {
+ ASSERT(op_ == Token::SUB);
+
Label slow;
Label done;
Label try_float;
@@ -6405,34 +6369,39 @@
// Test for NaN. Sadly, we can't just compare to Factory::nan_value(),
// so we do the second best thing - test it ourselves.
- Label return_equal;
- Label heap_number;
- // If it's not a heap number, then return equal.
- __ Cmp(FieldOperand(rdx, HeapObject::kMapOffset),
- Factory::heap_number_map());
- __ j(equal, &heap_number);
- __ bind(&return_equal);
- __ xor_(rax, rax);
- __ ret(0);
+ if (never_nan_nan_) {
+ __ xor_(rax, rax);
+ __ ret(0);
+ } else {
+ Label return_equal;
+ Label heap_number;
+ // If it's not a heap number, then return equal.
+ __ Cmp(FieldOperand(rdx, HeapObject::kMapOffset),
+ Factory::heap_number_map());
+ __ j(equal, &heap_number);
+ __ bind(&return_equal);
+ __ xor_(rax, rax);
+ __ ret(0);
- __ bind(&heap_number);
- // It is a heap number, so return non-equal if it's NaN and equal if it's
- // not NaN.
- // The representation of NaN values has all exponent bits (52..62) set,
- // and not all mantissa bits (0..51) clear.
- // We only allow QNaNs, which have bit 51 set (which also rules out
- // the value being Infinity).
+ __ bind(&heap_number);
+ // It is a heap number, so return non-equal if it's NaN and equal if
+ // it's not NaN.
+ // The representation of NaN values has all exponent bits (52..62) set,
+ // and not all mantissa bits (0..51) clear.
+ // We only allow QNaNs, which have bit 51 set (which also rules out
+ // the value being Infinity).
- // Value is a QNaN if value & kQuietNaNMask == kQuietNaNMask, i.e.,
- // all bits in the mask are set. We only need to check the word
- // that contains the exponent and high bit of the mantissa.
- ASSERT_NE(0, (kQuietNaNHighBitsMask << 1) & 0x80000000u);
- __ movl(rdx, FieldOperand(rdx, HeapNumber::kExponentOffset));
- __ xorl(rax, rax);
- __ addl(rdx, rdx); // Shift value and mask so mask applies to top bits.
- __ cmpl(rdx, Immediate(kQuietNaNHighBitsMask << 1));
- __ setcc(above_equal, rax);
- __ ret(0);
+ // Value is a QNaN if value & kQuietNaNMask == kQuietNaNMask, i.e.,
+ // all bits in the mask are set. We only need to check the word
+ // that contains the exponent and high bit of the mantissa.
+ ASSERT_NE(0, (kQuietNaNHighBitsMask << 1) & 0x80000000u);
+ __ movl(rdx, FieldOperand(rdx, HeapNumber::kExponentOffset));
+ __ xorl(rax, rax);
+ __ addl(rdx, rdx); // Shift value and mask so mask applies to top bits.
+ __ cmpl(rdx, Immediate(kQuietNaNHighBitsMask << 1));
+ __ setcc(above_equal, rax);
+ __ ret(0);
+ }
__ bind(¬_identical);
}
@@ -6528,9 +6497,10 @@
// Fast negative check for symbol-to-symbol equality.
__ bind(&check_for_symbols);
+ Label check_for_strings;
if (cc_ == equal) {
- BranchIfNonSymbol(masm, &call_builtin, rax, kScratchRegister);
- BranchIfNonSymbol(masm, &call_builtin, rdx, kScratchRegister);
+ BranchIfNonSymbol(masm, &check_for_strings, rax, kScratchRegister);
+ BranchIfNonSymbol(masm, &check_for_strings, rdx, kScratchRegister);
// We've already checked for object identity, so if both operands
// are symbols they aren't equal. Register eax (not rax) already holds a
@@ -6538,6 +6508,23 @@
__ ret(2 * kPointerSize);
}
+ __ bind(&check_for_strings);
+
+ __ JumpIfNotBothSequentialAsciiStrings(rdx, rax, rcx, rbx, &call_builtin);
+
+ // Inline comparison of ascii strings.
+ StringCompareStub::GenerateCompareFlatAsciiStrings(masm,
+ rdx,
+ rax,
+ rcx,
+ rbx,
+ rdi,
+ r8);
+
+#ifdef DEBUG
+ __ Abort("Unexpected fall-through from string comparison");
+#endif
+
__ bind(&call_builtin);
// must swap argument order
__ pop(rcx);
@@ -6579,15 +6566,18 @@
__ movq(scratch, FieldOperand(object, HeapObject::kMapOffset));
__ movzxbq(scratch,
FieldOperand(scratch, Map::kInstanceTypeOffset));
- __ and_(scratch, Immediate(kIsSymbolMask | kIsNotStringMask));
- __ cmpb(scratch, Immediate(kSymbolTag | kStringTag));
- __ j(not_equal, label);
+ // Ensure that no non-strings have the symbol bit set.
+ ASSERT(kNotStringTag + kIsSymbolMask > LAST_TYPE);
+ ASSERT(kSymbolTag != 0);
+ __ testb(scratch, Immediate(kIsSymbolMask));
+ __ j(zero, label);
}
// Call the function just below TOS on the stack with the given
// arguments. The receiver is the TOS.
void CodeGenerator::CallWithArguments(ZoneList<Expression*>* args,
+ CallFunctionFlags flags,
int position) {
// Push the arguments ("left-to-right") on the stack.
int arg_count = args->length();
@@ -6600,7 +6590,7 @@
// Use the shared code stub to call the function.
InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
- CallFunctionStub call_function(arg_count, in_loop);
+ CallFunctionStub call_function(arg_count, in_loop, flags);
Result answer = frame_->CallStub(&call_function, arg_count + 1);
// Restore context and replace function on the stack with the
// result of the stub invocation.
@@ -6760,16 +6750,13 @@
__ movq(rdx, Operand(rbp, StandardFrameConstants::kCallerFPOffset));
__ SmiCompare(Operand(rdx, StandardFrameConstants::kContextOffset),
Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR));
- __ j(equal, &adaptor);
-
- // Nothing to do: The formal number of parameters has already been
- // passed in register rax by calling function. Just return it.
- __ ret(0);
// Arguments adaptor case: Read the arguments length from the
// adaptor frame and return it.
- __ bind(&adaptor);
- __ movq(rax, Operand(rdx, ArgumentsAdaptorFrameConstants::kLengthOffset));
+ // Otherwise nothing to do: The number of formal parameters has already been
+ // passed in register eax by calling function. Just return it.
+ __ cmovq(equal, rax,
+ Operand(rdx, ArgumentsAdaptorFrameConstants::kLengthOffset));
__ ret(0);
}
@@ -7001,6 +6988,32 @@
void CallFunctionStub::Generate(MacroAssembler* masm) {
Label slow;
+ // If the receiver might be a value (string, number or boolean) check for this
+ // and box it if it is.
+ if (ReceiverMightBeValue()) {
+ // Get the receiver from the stack.
+ // +1 ~ return address
+ Label receiver_is_value, receiver_is_js_object;
+ __ movq(rax, Operand(rsp, (argc_ + 1) * kPointerSize));
+
+ // Check if receiver is a smi (which is a number value).
+ __ JumpIfSmi(rax, &receiver_is_value);
+
+ // Check if the receiver is a valid JS object.
+ __ CmpObjectType(rax, FIRST_JS_OBJECT_TYPE, rdi);
+ __ j(above_equal, &receiver_is_js_object);
+
+ // Call the runtime to box the value.
+ __ bind(&receiver_is_value);
+ __ EnterInternalFrame();
+ __ push(rax);
+ __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
+ __ LeaveInternalFrame();
+ __ movq(Operand(rsp, (argc_ + 1) * kPointerSize), rax);
+
+ __ bind(&receiver_is_js_object);
+ }
+
// Get the function to call from the stack.
// +2 ~ receiver, return address
__ movq(rdi, Operand(rsp, (argc_ + 2) * kPointerSize));
@@ -7371,19 +7384,28 @@
const char* GenericBinaryOpStub::GetName() {
- switch (op_) {
- case Token::ADD: return "GenericBinaryOpStub_ADD";
- case Token::SUB: return "GenericBinaryOpStub_SUB";
- case Token::MUL: return "GenericBinaryOpStub_MUL";
- case Token::DIV: return "GenericBinaryOpStub_DIV";
- case Token::BIT_OR: return "GenericBinaryOpStub_BIT_OR";
- case Token::BIT_AND: return "GenericBinaryOpStub_BIT_AND";
- case Token::BIT_XOR: return "GenericBinaryOpStub_BIT_XOR";
- case Token::SAR: return "GenericBinaryOpStub_SAR";
- case Token::SHL: return "GenericBinaryOpStub_SHL";
- case Token::SHR: return "GenericBinaryOpStub_SHR";
- default: return "GenericBinaryOpStub";
+ if (name_ != NULL) return name_;
+ const int len = 100;
+ name_ = Bootstrapper::AllocateAutoDeletedArray(len);
+ if (name_ == NULL) return "OOM";
+ const char* op_name = Token::Name(op_);
+ const char* overwrite_name;
+ switch (mode_) {
+ case NO_OVERWRITE: overwrite_name = "Alloc"; break;
+ case OVERWRITE_RIGHT: overwrite_name = "OverwriteRight"; break;
+ case OVERWRITE_LEFT: overwrite_name = "OverwriteLeft"; break;
+ default: overwrite_name = "UnknownOverwrite"; break;
}
+
+ OS::SNPrintF(Vector<char>(name_, len),
+ "GenericBinaryOpStub_%s_%s%s_%s%s_%s",
+ op_name,
+ overwrite_name,
+ (flags_ & NO_SMI_CODE_IN_STUB) ? "_NoSmiInStub" : "",
+ args_in_registers_ ? "RegArgs" : "StackArgs",
+ args_reversed_ ? "_R" : "",
+ use_sse3_ ? "SSE3" : "SSE2");
+ return name_;
}
@@ -7796,8 +7818,8 @@
__ j(above_equal, &string1);
// First and second argument are strings.
- Runtime::Function* f = Runtime::FunctionForId(Runtime::kStringAdd);
- __ TailCallRuntime(ExternalReference(f), 2, f->result_size);
+ StringAddStub stub(NO_STRING_CHECK_IN_STUB);
+ __ TailCallStub(&stub);
// Only first argument is a string.
__ bind(&string1);
@@ -7875,9 +7897,402 @@
int CompareStub::MinorKey() {
- // Encode the two parameters in a unique 16 bit value.
- ASSERT(static_cast<unsigned>(cc_) < (1 << 15));
- return (static_cast<unsigned>(cc_) << 1) | (strict_ ? 1 : 0);
+ // Encode the three parameters in a unique 16 bit value.
+ ASSERT(static_cast<unsigned>(cc_) < (1 << 14));
+ int nnn_value = (never_nan_nan_ ? 2 : 0);
+ if (cc_ != equal) nnn_value = 0; // Avoid duplicate stubs.
+ return (static_cast<unsigned>(cc_) << 2) | nnn_value | (strict_ ? 1 : 0);
+}
+
+
+const char* CompareStub::GetName() {
+ switch (cc_) {
+ case less: return "CompareStub_LT";
+ case greater: return "CompareStub_GT";
+ case less_equal: return "CompareStub_LE";
+ case greater_equal: return "CompareStub_GE";
+ case not_equal: {
+ if (strict_) {
+ if (never_nan_nan_) {
+ return "CompareStub_NE_STRICT_NO_NAN";
+ } else {
+ return "CompareStub_NE_STRICT";
+ }
+ } else {
+ if (never_nan_nan_) {
+ return "CompareStub_NE_NO_NAN";
+ } else {
+ return "CompareStub_NE";
+ }
+ }
+ }
+ case equal: {
+ if (strict_) {
+ if (never_nan_nan_) {
+ return "CompareStub_EQ_STRICT_NO_NAN";
+ } else {
+ return "CompareStub_EQ_STRICT";
+ }
+ } else {
+ if (never_nan_nan_) {
+ return "CompareStub_EQ_NO_NAN";
+ } else {
+ return "CompareStub_EQ";
+ }
+ }
+ }
+ default: return "CompareStub";
+ }
+}
+
+
+void StringAddStub::Generate(MacroAssembler* masm) {
+ Label string_add_runtime;
+
+ // Load the two arguments.
+ __ movq(rax, Operand(rsp, 2 * kPointerSize)); // First argument.
+ __ movq(rdx, Operand(rsp, 1 * kPointerSize)); // Second argument.
+
+ // Make sure that both arguments are strings if not known in advance.
+ if (string_check_) {
+ Condition is_smi;
+ is_smi = masm->CheckSmi(rax);
+ __ j(is_smi, &string_add_runtime);
+ __ CmpObjectType(rax, FIRST_NONSTRING_TYPE, r8);
+ __ j(above_equal, &string_add_runtime);
+
+ // First argument is a a string, test second.
+ is_smi = masm->CheckSmi(rdx);
+ __ j(is_smi, &string_add_runtime);
+ __ CmpObjectType(rdx, FIRST_NONSTRING_TYPE, r9);
+ __ j(above_equal, &string_add_runtime);
+ }
+
+ // Both arguments are strings.
+ // rax: first string
+ // rdx: second string
+ // Check if either of the strings are empty. In that case return the other.
+ Label second_not_zero_length, both_not_zero_length;
+ __ movl(rcx, FieldOperand(rdx, String::kLengthOffset));
+ __ testl(rcx, rcx);
+ __ j(not_zero, &second_not_zero_length);
+ // Second string is empty, result is first string which is already in rax.
+ __ IncrementCounter(&Counters::string_add_native, 1);
+ __ ret(2 * kPointerSize);
+ __ bind(&second_not_zero_length);
+ __ movl(rbx, FieldOperand(rax, String::kLengthOffset));
+ __ testl(rbx, rbx);
+ __ j(not_zero, &both_not_zero_length);
+ // First string is empty, result is second string which is in rdx.
+ __ movq(rax, rdx);
+ __ IncrementCounter(&Counters::string_add_native, 1);
+ __ ret(2 * kPointerSize);
+
+ // Both strings are non-empty.
+ // rax: first string
+ // rbx: length of first string
+ // ecx: length of second string
+ // edx: second string
+ // r8: instance type of first string if string check was performed above
+ // r9: instance type of first string if string check was performed above
+ Label string_add_flat_result;
+ __ bind(&both_not_zero_length);
+ // Look at the length of the result of adding the two strings.
+ __ addl(rbx, rcx);
+ // Use the runtime system when adding two one character strings, as it
+ // contains optimizations for this specific case using the symbol table.
+ __ cmpl(rbx, Immediate(2));
+ __ j(equal, &string_add_runtime);
+ // If arguments where known to be strings, maps are not loaded to r8 and r9
+ // by the code above.
+ if (!string_check_) {
+ __ movq(r8, FieldOperand(rax, HeapObject::kMapOffset));
+ __ movq(r9, FieldOperand(rdx, HeapObject::kMapOffset));
+ }
+ // Get the instance types of the two strings as they will be needed soon.
+ __ movzxbl(r8, FieldOperand(r8, Map::kInstanceTypeOffset));
+ __ movzxbl(r9, FieldOperand(r9, Map::kInstanceTypeOffset));
+ // Check if resulting string will be flat.
+ __ cmpl(rbx, Immediate(String::kMinNonFlatLength));
+ __ j(below, &string_add_flat_result);
+ // Handle exceptionally long strings in the runtime system.
+ ASSERT((String::kMaxLength & 0x80000000) == 0);
+ __ cmpl(rbx, Immediate(String::kMaxLength));
+ __ j(above, &string_add_runtime);
+
+ // If result is not supposed to be flat, allocate a cons string object. If
+ // both strings are ascii the result is an ascii cons string.
+ // rax: first string
+ // ebx: length of resulting flat string
+ // rdx: second string
+ // r8: instance type of first string
+ // r9: instance type of second string
+ Label non_ascii, allocated;
+ __ movl(rcx, r8);
+ __ and_(rcx, r9);
+ ASSERT(kStringEncodingMask == kAsciiStringTag);
+ __ testl(rcx, Immediate(kAsciiStringTag));
+ __ j(zero, &non_ascii);
+ // Allocate an acsii cons string.
+ __ AllocateAsciiConsString(rcx, rdi, no_reg, &string_add_runtime);
+ __ bind(&allocated);
+ // Fill the fields of the cons string.
+ __ movl(FieldOperand(rcx, ConsString::kLengthOffset), rbx);
+ __ movl(FieldOperand(rcx, ConsString::kHashFieldOffset),
+ Immediate(String::kEmptyHashField));
+ __ movq(FieldOperand(rcx, ConsString::kFirstOffset), rax);
+ __ movq(FieldOperand(rcx, ConsString::kSecondOffset), rdx);
+ __ movq(rax, rcx);
+ __ IncrementCounter(&Counters::string_add_native, 1);
+ __ ret(2 * kPointerSize);
+ __ bind(&non_ascii);
+ // Allocate a two byte cons string.
+ __ AllocateConsString(rcx, rdi, no_reg, &string_add_runtime);
+ __ jmp(&allocated);
+
+ // Handle creating a flat result. First check that both strings are not
+ // external strings.
+ // rax: first string
+ // ebx: length of resulting flat string
+ // rdx: second string
+ // r8: instance type of first string
+ // r9: instance type of first string
+ __ bind(&string_add_flat_result);
+ __ movl(rcx, r8);
+ __ and_(rcx, Immediate(kStringRepresentationMask));
+ __ cmpl(rcx, Immediate(kExternalStringTag));
+ __ j(equal, &string_add_runtime);
+ __ movl(rcx, r9);
+ __ and_(rcx, Immediate(kStringRepresentationMask));
+ __ cmpl(rcx, Immediate(kExternalStringTag));
+ __ j(equal, &string_add_runtime);
+ // Now check if both strings are ascii strings.
+ // rax: first string
+ // ebx: length of resulting flat string
+ // rdx: second string
+ // r8: instance type of first string
+ // r9: instance type of second string
+ Label non_ascii_string_add_flat_result;
+ ASSERT(kStringEncodingMask == kAsciiStringTag);
+ __ testl(r8, Immediate(kAsciiStringTag));
+ __ j(zero, &non_ascii_string_add_flat_result);
+ __ testl(r9, Immediate(kAsciiStringTag));
+ __ j(zero, &string_add_runtime);
+ // Both strings are ascii strings. As they are short they are both flat.
+ __ AllocateAsciiString(rcx, rbx, rdi, r14, r15, &string_add_runtime);
+ // rcx: result string
+ __ movq(rbx, rcx);
+ // Locate first character of result.
+ __ addq(rcx, Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag));
+ // Locate first character of first argument
+ __ movl(rdi, FieldOperand(rax, String::kLengthOffset));
+ __ addq(rax, Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag));
+ // rax: first char of first argument
+ // rbx: result string
+ // rcx: first character of result
+ // rdx: second string
+ // rdi: length of first argument
+ GenerateCopyCharacters(masm, rcx, rax, rdi, true);
+ // Locate first character of second argument.
+ __ movl(rdi, FieldOperand(rdx, String::kLengthOffset));
+ __ addq(rdx, Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag));
+ // rbx: result string
+ // rcx: next character of result
+ // rdx: first char of second argument
+ // rdi: length of second argument
+ GenerateCopyCharacters(masm, rcx, rdx, rdi, true);
+ __ movq(rax, rbx);
+ __ IncrementCounter(&Counters::string_add_native, 1);
+ __ ret(2 * kPointerSize);
+
+ // Handle creating a flat two byte result.
+ // rax: first string - known to be two byte
+ // rbx: length of resulting flat string
+ // rdx: second string
+ // r8: instance type of first string
+ // r9: instance type of first string
+ __ bind(&non_ascii_string_add_flat_result);
+ __ and_(r9, Immediate(kAsciiStringTag));
+ __ j(not_zero, &string_add_runtime);
+ // Both strings are two byte strings. As they are short they are both
+ // flat.
+ __ AllocateTwoByteString(rcx, rbx, rdi, r14, r15, &string_add_runtime);
+ // rcx: result string
+ __ movq(rbx, rcx);
+ // Locate first character of result.
+ __ addq(rcx, Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
+ // Locate first character of first argument.
+ __ movl(rdi, FieldOperand(rax, String::kLengthOffset));
+ __ addq(rax, Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
+ // rax: first char of first argument
+ // rbx: result string
+ // rcx: first character of result
+ // rdx: second argument
+ // rdi: length of first argument
+ GenerateCopyCharacters(masm, rcx, rax, rdi, false);
+ // Locate first character of second argument.
+ __ movl(rdi, FieldOperand(rdx, String::kLengthOffset));
+ __ addq(rdx, Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
+ // rbx: result string
+ // rcx: next character of result
+ // rdx: first char of second argument
+ // rdi: length of second argument
+ GenerateCopyCharacters(masm, rcx, rdx, rdi, false);
+ __ movq(rax, rbx);
+ __ IncrementCounter(&Counters::string_add_native, 1);
+ __ ret(2 * kPointerSize);
+
+ // Just jump to runtime to add the two strings.
+ __ bind(&string_add_runtime);
+ __ TailCallRuntime(ExternalReference(Runtime::kStringAdd), 2, 1);
+}
+
+
+void StringAddStub::GenerateCopyCharacters(MacroAssembler* masm,
+ Register dest,
+ Register src,
+ Register count,
+ bool ascii) {
+ Label loop;
+ __ bind(&loop);
+ // This loop just copies one character at a time, as it is only used for very
+ // short strings.
+ if (ascii) {
+ __ movb(kScratchRegister, Operand(src, 0));
+ __ movb(Operand(dest, 0), kScratchRegister);
+ __ addq(src, Immediate(1));
+ __ addq(dest, Immediate(1));
+ } else {
+ __ movzxwl(kScratchRegister, Operand(src, 0));
+ __ movw(Operand(dest, 0), kScratchRegister);
+ __ addq(src, Immediate(2));
+ __ addq(dest, Immediate(2));
+ }
+ __ subl(count, Immediate(1));
+ __ j(not_zero, &loop);
+}
+
+
+
+void StringCompareStub::GenerateCompareFlatAsciiStrings(MacroAssembler* masm,
+ Register left,
+ Register right,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Register scratch4) {
+ // Ensure that you can always subtract a string length from a non-negative
+ // number (e.g. another length).
+ ASSERT(String::kMaxLength < 0x7fffffff);
+
+ // Find minimum length and length difference.
+ __ movl(scratch1, FieldOperand(left, String::kLengthOffset));
+ __ movl(scratch4, scratch1);
+ __ subl(scratch4, FieldOperand(right, String::kLengthOffset));
+ // Register scratch4 now holds left.length - right.length.
+ const Register length_difference = scratch4;
+ Label left_shorter;
+ __ j(less, &left_shorter);
+ // The right string isn't longer that the left one.
+ // Get the right string's length by subtracting the (non-negative) difference
+ // from the left string's length.
+ __ subl(scratch1, length_difference);
+ __ bind(&left_shorter);
+ // Register scratch1 now holds Min(left.length, right.length).
+ const Register min_length = scratch1;
+
+ Label compare_lengths;
+ // If min-length is zero, go directly to comparing lengths.
+ __ testl(min_length, min_length);
+ __ j(zero, &compare_lengths);
+
+ // Registers scratch2 and scratch3 are free.
+ Label result_not_equal;
+ Label loop;
+ {
+ // Check characters 0 .. min_length - 1 in a loop.
+ // Use scratch3 as loop index, min_length as limit and scratch2
+ // for computation.
+ const Register index = scratch3;
+ __ movl(index, Immediate(0)); // Index into strings.
+ __ bind(&loop);
+ // Compare characters.
+ // TODO(lrn): Could we load more than one character at a time?
+ __ movb(scratch2, FieldOperand(left,
+ index,
+ times_1,
+ SeqAsciiString::kHeaderSize));
+ // Increment index and use -1 modifier on next load to give
+ // the previous load extra time to complete.
+ __ addl(index, Immediate(1));
+ __ cmpb(scratch2, FieldOperand(right,
+ index,
+ times_1,
+ SeqAsciiString::kHeaderSize - 1));
+ __ j(not_equal, &result_not_equal);
+ __ cmpl(index, min_length);
+ __ j(not_equal, &loop);
+ }
+ // Completed loop without finding different characters.
+ // Compare lengths (precomputed).
+ __ bind(&compare_lengths);
+ __ testl(length_difference, length_difference);
+ __ j(not_zero, &result_not_equal);
+
+ // Result is EQUAL.
+ __ Move(rax, Smi::FromInt(EQUAL));
+ __ IncrementCounter(&Counters::string_compare_native, 1);
+ __ ret(2 * kPointerSize);
+
+ Label result_greater;
+ __ bind(&result_not_equal);
+ // Unequal comparison of left to right, either character or length.
+ __ j(greater, &result_greater);
+
+ // Result is LESS.
+ __ Move(rax, Smi::FromInt(LESS));
+ __ IncrementCounter(&Counters::string_compare_native, 1);
+ __ ret(2 * kPointerSize);
+
+ // Result is GREATER.
+ __ bind(&result_greater);
+ __ Move(rax, Smi::FromInt(GREATER));
+ __ IncrementCounter(&Counters::string_compare_native, 1);
+ __ ret(2 * kPointerSize);
+}
+
+
+void StringCompareStub::Generate(MacroAssembler* masm) {
+ Label runtime;
+
+ // Stack frame on entry.
+ // rsp[0]: return address
+ // rsp[8]: right string
+ // rsp[16]: left string
+
+ __ movq(rdx, Operand(rsp, 2 * kPointerSize)); // left
+ __ movq(rax, Operand(rsp, 1 * kPointerSize)); // right
+
+ // Check for identity.
+ Label not_same;
+ __ cmpq(rdx, rax);
+ __ j(not_equal, ¬_same);
+ __ Move(rax, Smi::FromInt(EQUAL));
+ __ IncrementCounter(&Counters::string_compare_native, 1);
+ __ ret(2 * kPointerSize);
+
+ __ bind(¬_same);
+
+ // Check that both are sequential ASCII strings.
+ __ JumpIfNotBothSequentialAsciiStrings(rdx, rax, rcx, rbx, &runtime);
+
+ // Inline comparison of ascii strings.
+ GenerateCompareFlatAsciiStrings(masm, rdx, rax, rcx, rbx, rdi, r8);
+
+ // Call the runtime; it returns -1 (less), 0 (equal), or 1 (greater)
+ // tagged as a small integer.
+ __ bind(&runtime);
+ __ TailCallRuntime(ExternalReference(Runtime::kStringCompare), 2, 1);
}
#undef __
@@ -7973,6 +8388,7 @@
#endif
+
#undef __
} } // namespace v8::internal
diff --git a/src/x64/codegen-x64.h b/src/x64/codegen-x64.h
index 8539884..50bb023 100644
--- a/src/x64/codegen-x64.h
+++ b/src/x64/codegen-x64.h
@@ -436,7 +436,7 @@
void GenericBinaryOperation(
Token::Value op,
- SmiAnalysis* type,
+ StaticType* type,
OverwriteMode overwrite_mode);
// If possible, combine two constant smi values using op to produce
@@ -449,7 +449,7 @@
void ConstantSmiBinaryOperation(Token::Value op,
Result* operand,
Handle<Object> constant_operand,
- SmiAnalysis* type,
+ StaticType* type,
bool reversed,
OverwriteMode overwrite_mode);
@@ -474,7 +474,9 @@
// at most 16 bits of user-controlled data per assembly operation.
void LoadUnsafeSmi(Register target, Handle<Object> value);
- void CallWithArguments(ZoneList<Expression*>* arguments, int position);
+ void CallWithArguments(ZoneList<Expression*>* arguments,
+ CallFunctionFlags flags,
+ int position);
// Use an optimized version of Function.prototype.apply that avoid
// allocating the arguments object and just copies the arguments
@@ -538,15 +540,18 @@
// Fast support for Math.random().
void GenerateRandomPositiveSmi(ZoneList<Expression*>* args);
- // Fast support for Math.sin and Math.cos.
- enum MathOp { SIN, COS };
- void GenerateFastMathOp(MathOp op, ZoneList<Expression*>* args);
- inline void GenerateMathSin(ZoneList<Expression*>* args);
- inline void GenerateMathCos(ZoneList<Expression*>* args);
-
// Fast support for StringAdd.
void GenerateStringAdd(ZoneList<Expression*>* args);
+ // Fast support for SubString.
+ void GenerateSubString(ZoneList<Expression*>* args);
+
+ // Fast support for StringCompare.
+ void GenerateStringCompare(ZoneList<Expression*>* args);
+
+ // Support for direct calls from JavaScript to native RegExp code.
+ void GenerateRegExpExec(ZoneList<Expression*>* args);
+
// Simple condition analysis.
enum ConditionAnalysis {
ALWAYS_TRUE,
@@ -614,46 +619,6 @@
};
-// -------------------------------------------------------------------------
-// Code stubs
-//
-// These independent code objects are created once, and used multiple
-// times by generated code to perform common tasks, often the slow
-// case of a JavaScript operation. They are all subclasses of CodeStub,
-// which is declared in code-stubs.h.
-class CallFunctionStub: public CodeStub {
- public:
- CallFunctionStub(int argc, InLoopFlag in_loop)
- : argc_(argc), in_loop_(in_loop) { }
-
- void Generate(MacroAssembler* masm);
-
- private:
- int argc_;
- InLoopFlag in_loop_;
-
-#ifdef DEBUG
- void Print() { PrintF("CallFunctionStub (args %d)\n", argc_); }
-#endif
-
- Major MajorKey() { return CallFunction; }
- int MinorKey() { return argc_; }
- InLoopFlag InLoop() { return in_loop_; }
-};
-
-
-class ToBooleanStub: public CodeStub {
- public:
- ToBooleanStub() { }
-
- void Generate(MacroAssembler* masm);
-
- private:
- Major MajorKey() { return ToBoolean; }
- int MinorKey() { return 0; }
-};
-
-
// Flag that indicates how to generate code for the stub GenericBinaryOpStub.
enum GenericBinaryFlags {
NO_GENERIC_BINARY_FLAGS = 0,
@@ -670,7 +635,8 @@
mode_(mode),
flags_(flags),
args_in_registers_(false),
- args_reversed_(false) {
+ args_reversed_(false),
+ name_(NULL) {
use_sse3_ = CpuFeatures::IsSupported(SSE3);
ASSERT(OpBits::is_valid(Token::NUM_TOKENS));
}
@@ -689,6 +655,7 @@
bool args_in_registers_; // Arguments passed in registers not on the stack.
bool args_reversed_; // Left and right argument are swapped.
bool use_sse3_;
+ char* name_;
const char* GetName();
@@ -745,6 +712,58 @@
};
+// Flag that indicates how to generate code for the stub StringAddStub.
+enum StringAddFlags {
+ NO_STRING_ADD_FLAGS = 0,
+ NO_STRING_CHECK_IN_STUB = 1 << 0 // Omit string check in stub.
+};
+
+
+class StringAddStub: public CodeStub {
+ public:
+ explicit StringAddStub(StringAddFlags flags) {
+ string_check_ = ((flags & NO_STRING_CHECK_IN_STUB) == 0);
+ }
+
+ private:
+ Major MajorKey() { return StringAdd; }
+ int MinorKey() { return string_check_ ? 0 : 1; }
+
+ void Generate(MacroAssembler* masm);
+
+ void GenerateCopyCharacters(MacroAssembler* masm,
+ Register desc,
+ Register src,
+ Register count,
+ bool ascii);
+
+ // Should the stub check whether arguments are strings?
+ bool string_check_;
+};
+
+
+class StringCompareStub: public CodeStub {
+ public:
+ explicit StringCompareStub() {}
+
+ // Compare two flat ascii strings and returns result in rax after popping two
+ // arguments from the stack.
+ static void GenerateCompareFlatAsciiStrings(MacroAssembler* masm,
+ Register left,
+ Register right,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Register scratch4);
+
+ private:
+ Major MajorKey() { return StringCompare; }
+ int MinorKey() { return 0; }
+
+ void Generate(MacroAssembler* masm);
+};
+
+
} } // namespace v8::internal
#endif // V8_X64_CODEGEN_X64_H_
diff --git a/src/x64/fast-codegen-x64.cc b/src/x64/fast-codegen-x64.cc
index 333a47d..0f28433 100644
--- a/src/x64/fast-codegen-x64.cc
+++ b/src/x64/fast-codegen-x64.cc
@@ -62,11 +62,9 @@
{ Comment cmnt(masm_, "[ Allocate locals");
int locals_count = fun->scope()->num_stack_slots();
- if (locals_count <= 1) {
- if (locals_count > 0) {
- __ PushRoot(Heap::kUndefinedValueRootIndex);
- }
- } else {
+ if (locals_count == 1) {
+ __ PushRoot(Heap::kUndefinedValueRootIndex);
+ } else if (locals_count > 1) {
__ LoadRoot(rdx, Heap::kUndefinedValueRootIndex);
for (int i = 0; i < locals_count; i++) {
__ push(rdx);
@@ -132,6 +130,10 @@
Move(dot_arguments_slot, rcx, rbx, rdx);
}
+ { Comment cmnt(masm_, "[ Declarations");
+ VisitDeclarations(fun->scope()->declarations());
+ }
+
{ Comment cmnt(masm_, "[ Stack check");
Label ok;
__ CompareRoot(rsp, Heap::kStackLimitRootIndex);
@@ -141,10 +143,6 @@
__ bind(&ok);
}
- { Comment cmnt(masm_, "[ Declarations");
- VisitDeclarations(fun->scope()->declarations());
- }
-
if (FLAG_trace) {
__ CallRuntime(Runtime::kTraceEnter, 0);
}
@@ -202,219 +200,461 @@
}
-void FastCodeGenerator::Move(Expression::Context context, Register source) {
+void FastCodeGenerator::Apply(Expression::Context context, Register reg) {
switch (context) {
case Expression::kUninitialized:
UNREACHABLE();
+
case Expression::kEffect:
+ // Nothing to do.
break;
+
case Expression::kValue:
- __ push(source);
+ // Move value into place.
+ switch (location_) {
+ case kAccumulator:
+ if (!reg.is(result_register())) __ movq(result_register(), reg);
+ break;
+ case kStack:
+ __ push(reg);
+ break;
+ }
break;
+
case Expression::kTest:
- TestAndBranch(source, true_label_, false_label_);
+ // For simplicity we always test the accumulator register.
+ if (!reg.is(result_register())) __ movq(result_register(), reg);
+ DoTest(context);
break;
- case Expression::kValueTest: {
- Label discard;
- __ push(source);
- TestAndBranch(source, true_label_, &discard);
- __ bind(&discard);
- __ addq(rsp, Immediate(kPointerSize));
- __ jmp(false_label_);
+
+ 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);
break;
- }
- case Expression::kTestValue: {
- Label discard;
- __ push(source);
- TestAndBranch(source, &discard, false_label_);
- __ bind(&discard);
- __ addq(rsp, Immediate(kPointerSize));
- __ jmp(true_label_);
- break;
- }
}
}
-template <>
-Operand FastCodeGenerator::CreateSlotOperand<Operand>(Slot* source,
- Register scratch) {
- switch (source->type()) {
- case Slot::PARAMETER:
- case Slot::LOCAL:
- return Operand(rbp, SlotOffset(source));
- case Slot::CONTEXT: {
- int context_chain_length =
- function_->scope()->ContextChainLength(source->var()->scope());
- __ LoadContext(scratch, context_chain_length);
- return CodeGenerator::ContextOperand(scratch, source->index());
- break;
- }
- case Slot::LOOKUP:
- UNIMPLEMENTED();
- // Fall-through.
- default:
- UNREACHABLE();
- return Operand(rax, 0); // Dead code to make the compiler happy.
- }
-}
-
-
-void FastCodeGenerator::Move(Register dst, Slot* source) {
- Operand location = CreateSlotOperand<Operand>(source, dst);
- __ movq(dst, location);
-}
-
-
-void FastCodeGenerator::Move(Expression::Context context,
- Slot* source,
- Register scratch) {
+void FastCodeGenerator::Apply(Expression::Context context, Slot* slot) {
switch (context) {
case Expression::kUninitialized:
UNREACHABLE();
case Expression::kEffect:
+ // Nothing to do.
break;
case Expression::kValue: {
- Operand location = CreateSlotOperand<Operand>(source, scratch);
- __ push(location);
+ MemOperand slot_operand = EmitSlotSearch(slot, result_register());
+ switch (location_) {
+ case kAccumulator:
+ __ movq(result_register(), slot_operand);
+ break;
+ case kStack:
+ // Memory operands can be pushed directly.
+ __ push(slot_operand);
+ break;
+ }
break;
}
- case Expression::kTest: // Fall through.
- case Expression::kValueTest: // Fall through.
+
+ case Expression::kTest:
+ Move(result_register(), slot);
+ DoTest(context);
+ break;
+
+ case Expression::kValueTest:
case Expression::kTestValue:
- Move(scratch, source);
- Move(context, scratch);
+ Move(result_register(), slot);
+ switch (location_) {
+ case kAccumulator:
+ break;
+ case kStack:
+ __ push(result_register());
+ break;
+ }
+ DoTest(context);
break;
}
}
-void FastCodeGenerator::Move(Expression::Context context, Literal* expr) {
+void FastCodeGenerator::Apply(Expression::Context context, Literal* lit) {
switch (context) {
case Expression::kUninitialized:
UNREACHABLE();
case Expression::kEffect:
+ // Nothing to do.
break;
case Expression::kValue:
- __ Push(expr->handle());
+ switch (location_) {
+ case kAccumulator:
+ __ Move(result_register(), lit->handle());
+ break;
+ case kStack:
+ __ Push(lit->handle());
+ break;
+ }
break;
- case Expression::kTest: // Fall through.
- case Expression::kValueTest: // Fall through.
+
+ case Expression::kTest:
+ __ Move(result_register(), lit->handle());
+ DoTest(context);
+ break;
+
+ case Expression::kValueTest:
case Expression::kTestValue:
- __ Move(rax, expr->handle());
- Move(context, rax);
+ __ Move(result_register(), lit->handle());
+ switch (location_) {
+ case kAccumulator:
+ break;
+ case kStack:
+ __ push(result_register());
+ break;
+ }
+ DoTest(context);
break;
}
}
+void FastCodeGenerator::ApplyTOS(Expression::Context context) {
+ switch (context) {
+ case Expression::kUninitialized:
+ UNREACHABLE();
+
+ case Expression::kEffect:
+ __ Drop(1);
+ break;
+
+ case Expression::kValue:
+ switch (location_) {
+ case kAccumulator:
+ __ pop(result_register());
+ break;
+ case kStack:
+ break;
+ }
+ break;
+
+ 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);
+ break;
+ }
+}
+
+
+void FastCodeGenerator::DropAndApply(int count,
+ Expression::Context context,
+ Register reg) {
+ ASSERT(count > 0);
+ ASSERT(!reg.is(rsp));
+ switch (context) {
+ case Expression::kUninitialized:
+ UNREACHABLE();
+
+ case Expression::kEffect:
+ __ Drop(count);
+ break;
+
+ case Expression::kValue:
+ 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(Operand(rsp, 0), reg);
+ break;
+ }
+ break;
+
+ 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 FastCodeGenerator::Apply(Expression::Context context,
+ Label* materialize_true,
+ Label* materialize_false) {
+ switch (context) {
+ case Expression::kUninitialized:
+
+ case Expression::kEffect:
+ ASSERT_EQ(materialize_true, materialize_false);
+ __ bind(materialize_true);
+ break;
+
+ case Expression::kValue: {
+ Label done;
+ switch (location_) {
+ case kAccumulator:
+ __ bind(materialize_true);
+ __ Move(result_register(), Factory::true_value());
+ __ jmp(&done);
+ __ bind(materialize_false);
+ __ Move(result_register(), Factory::false_value());
+ break;
+ case kStack:
+ __ bind(materialize_true);
+ __ Push(Factory::true_value());
+ __ jmp(&done);
+ __ bind(materialize_false);
+ __ Push(Factory::false_value());
+ break;
+ }
+ __ bind(&done);
+ break;
+ }
+
+ 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;
+ }
+}
+
+
+void FastCodeGenerator::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;
+ }
+
+ // Emit the inlined tests assumed by the stub.
+ __ CompareRoot(result_register(), Heap::kUndefinedValueRootIndex);
+ __ j(equal, if_false);
+ __ CompareRoot(result_register(), Heap::kTrueValueRootIndex);
+ __ j(equal, if_true);
+ __ CompareRoot(result_register(), Heap::kFalseValueRootIndex);
+ __ j(equal, if_false);
+ ASSERT_EQ(0, kSmiTag);
+ __ SmiCompare(result_register(), Smi::FromInt(0));
+ __ j(equal, if_false);
+ 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;
+
+ 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;
+ }
+}
+
+
+MemOperand FastCodeGenerator::EmitSlotSearch(Slot* slot, Register scratch) {
+ switch (slot->type()) {
+ case Slot::PARAMETER:
+ case Slot::LOCAL:
+ return Operand(rbp, SlotOffset(slot));
+ case Slot::CONTEXT: {
+ int context_chain_length =
+ function_->scope()->ContextChainLength(slot->var()->scope());
+ __ LoadContext(scratch, context_chain_length);
+ return CodeGenerator::ContextOperand(scratch, slot->index());
+ }
+ case Slot::LOOKUP:
+ UNREACHABLE();
+ }
+ UNREACHABLE();
+ return Operand(rax, 0);
+}
+
+
+void FastCodeGenerator::Move(Register destination, Slot* source) {
+ MemOperand location = EmitSlotSearch(source, destination);
+ __ movq(destination, location);
+}
+
+
void FastCodeGenerator::Move(Slot* dst,
Register src,
Register scratch1,
Register scratch2) {
- switch (dst->type()) {
- case Slot::PARAMETER:
- case Slot::LOCAL:
- __ movq(Operand(rbp, SlotOffset(dst)), src);
- break;
- case Slot::CONTEXT: {
- ASSERT(!src.is(scratch1));
- ASSERT(!src.is(scratch2));
- ASSERT(!scratch1.is(scratch2));
- int context_chain_length =
- function_->scope()->ContextChainLength(dst->var()->scope());
- __ LoadContext(scratch1, context_chain_length);
- __ movq(Operand(scratch1, Context::SlotOffset(dst->index())), src);
- int offset = FixedArray::kHeaderSize + dst->index() * kPointerSize;
- __ RecordWrite(scratch1, offset, src, scratch2);
- break;
- }
- case Slot::LOOKUP:
- UNIMPLEMENTED();
- default:
- UNREACHABLE();
+ ASSERT(dst->type() != Slot::LOOKUP); // Not yet implemented.
+ ASSERT(!scratch1.is(src) && !scratch2.is(src));
+ MemOperand location = EmitSlotSearch(dst, scratch1);
+ __ movq(location, src);
+ // Emit the write barrier code if the location is in the heap.
+ if (dst->type() == Slot::CONTEXT) {
+ int offset = FixedArray::kHeaderSize + dst->index() * kPointerSize;
+ __ RecordWrite(scratch1, offset, src, scratch2);
}
}
-void FastCodeGenerator::DropAndMove(Expression::Context context,
- Register source,
- int drop_count) {
- ASSERT(drop_count > 0);
- switch (context) {
- case Expression::kUninitialized:
- UNREACHABLE();
- case Expression::kEffect:
- __ addq(rsp, Immediate(drop_count * kPointerSize));
- break;
- case Expression::kValue:
- if (drop_count > 1) {
- __ addq(rsp, Immediate((drop_count - 1) * kPointerSize));
- }
- __ movq(Operand(rsp, 0), source);
- break;
- case Expression::kTest:
- ASSERT(!source.is(rsp));
- __ addq(rsp, Immediate(drop_count * kPointerSize));
- TestAndBranch(source, true_label_, false_label_);
- break;
- case Expression::kValueTest: {
- Label discard;
- if (drop_count > 1) {
- __ addq(rsp, Immediate((drop_count - 1) * kPointerSize));
- }
- __ movq(Operand(rsp, 0), source);
- TestAndBranch(source, true_label_, &discard);
- __ bind(&discard);
- __ addq(rsp, Immediate(kPointerSize));
- __ jmp(false_label_);
- break;
- }
- case Expression::kTestValue: {
- Label discard;
- __ movq(Operand(rsp, 0), source);
- TestAndBranch(source, &discard, false_label_);
- __ bind(&discard);
- __ addq(rsp, Immediate(kPointerSize));
- __ jmp(true_label_);
- break;
- }
- }
-}
-
-
-void FastCodeGenerator::TestAndBranch(Register source,
- Label* true_label,
- Label* false_label) {
- ASSERT_NE(NULL, true_label);
- ASSERT_NE(NULL, false_label);
- // Use the shared ToBoolean stub to compile the value in the register into
- // control flow to the code generator's true and false labels. Perform
- // the fast checks assumed by the stub.
-
- // The undefined value is false.
- __ CompareRoot(source, Heap::kUndefinedValueRootIndex);
- __ j(equal, false_label);
- __ CompareRoot(source, Heap::kTrueValueRootIndex); // True is true.
- __ j(equal, true_label);
- __ CompareRoot(source, Heap::kFalseValueRootIndex); // False is false.
- __ j(equal, false_label);
- ASSERT_EQ(0, kSmiTag);
- __ SmiCompare(source, Smi::FromInt(0)); // The smi zero is false.
- __ j(equal, false_label);
- Condition is_smi = masm_->CheckSmi(source); // All other smis are true.
- __ j(is_smi, true_label);
-
- // Call the stub for all other cases.
- __ push(source);
- ToBooleanStub stub;
- __ CallStub(&stub);
- __ testq(rax, rax); // The stub returns nonzero for true.
- __ j(not_zero, true_label);
- __ jmp(false_label);
-}
-
-
void FastCodeGenerator::VisitDeclaration(Declaration* decl) {
Comment cmnt(masm_, "[ Declaration");
Variable* var = decl->proxy()->var();
@@ -424,18 +664,21 @@
if (slot != NULL) {
switch (slot->type()) {
- case Slot::PARAMETER: // Fall through.
+ case Slot::PARAMETER:
case Slot::LOCAL:
if (decl->mode() == Variable::CONST) {
__ LoadRoot(kScratchRegister, Heap::kTheHoleValueRootIndex);
- __ movq(Operand(rbp, SlotOffset(var->slot())), kScratchRegister);
+ __ movq(Operand(rbp, SlotOffset(slot)), kScratchRegister);
} else if (decl->fun() != NULL) {
- Visit(decl->fun());
- __ pop(Operand(rbp, SlotOffset(var->slot())));
+ VisitForValue(decl->fun(), kAccumulator);
+ __ movq(Operand(rbp, SlotOffset(slot)), result_register());
}
break;
case Slot::CONTEXT:
+ // We bypass the general EmitSlotSearch because we know more about
+ // this specific context.
+
// The variable in the decl always resides in the current context.
ASSERT_EQ(0, function_->scope()->ContextChainLength(var->scope()));
if (FLAG_debug_code) {
@@ -451,11 +694,11 @@
kScratchRegister);
// No write barrier since the hole value is in old space.
} else if (decl->fun() != NULL) {
- Visit(decl->fun());
- __ pop(rax);
- __ movq(CodeGenerator::ContextOperand(rsi, slot->index()), rax);
+ VisitForValue(decl->fun(), kAccumulator);
+ __ movq(CodeGenerator::ContextOperand(rsi, slot->index()),
+ result_register());
int offset = Context::SlotOffset(slot->index());
- __ RecordWrite(rsi, offset, rax, rcx);
+ __ RecordWrite(rsi, offset, result_register(), rcx);
}
break;
@@ -475,7 +718,7 @@
if (decl->mode() == Variable::CONST) {
__ PushRoot(Heap::kTheHoleValueRootIndex);
} else if (decl->fun() != NULL) {
- Visit(decl->fun());
+ VisitForValue(decl->fun(), kStack);
} else {
__ Push(Smi::FromInt(0)); // no initial value!
}
@@ -488,28 +731,24 @@
if (decl->fun() != NULL || decl->mode() == Variable::CONST) {
// We are declaring a function or constant that rewrites to a
// property. Use (keyed) IC to set the initial value.
- ASSERT_EQ(Expression::kValue, prop->obj()->context());
- Visit(prop->obj());
- ASSERT_EQ(Expression::kValue, prop->key()->context());
- Visit(prop->key());
+ VisitForValue(prop->obj(), kStack);
+ VisitForValue(prop->key(), kStack);
if (decl->fun() != NULL) {
- ASSERT_EQ(Expression::kValue, decl->fun()->context());
- Visit(decl->fun());
- __ pop(rax);
+ VisitForValue(decl->fun(), kAccumulator);
} else {
- __ LoadRoot(rax, Heap::kTheHoleValueRootIndex);
+ __ LoadRoot(result_register(), Heap::kTheHoleValueRootIndex);
}
Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize));
__ call(ic, RelocInfo::CODE_TARGET);
-
// Absence of a test rax instruction following the call
// indicates that none of the load was inlined.
+ __ nop();
// Value in rax is ignored (declarations are statements). Receiver
// and key on stack are discarded.
- __ addq(rsp, Immediate(2 * kPointerSize));
+ __ Drop(2);
}
}
}
@@ -525,20 +764,6 @@
}
-void FastCodeGenerator::VisitReturnStatement(ReturnStatement* stmt) {
- Comment cmnt(masm_, "[ ReturnStatement");
- Expression* expr = stmt->expression();
- if (expr->AsLiteral() != NULL) {
- __ Move(rax, expr->AsLiteral()->handle());
- } else {
- Visit(expr);
- ASSERT_EQ(Expression::kValue, expr->context());
- __ pop(rax);
- }
- EmitReturnSequence(stmt->statement_pos());
-}
-
-
void FastCodeGenerator::VisitFunctionLiteral(FunctionLiteral* expr) {
Comment cmnt(masm_, "[ FunctionLiteral");
@@ -553,34 +778,39 @@
__ push(rsi);
__ Push(boilerplate);
__ CallRuntime(Runtime::kNewClosure, 2);
- Move(expr->context(), rax);
+ Apply(context_, rax);
}
void FastCodeGenerator::VisitVariableProxy(VariableProxy* expr) {
Comment cmnt(masm_, "[ VariableProxy");
- Expression* rewrite = expr->var()->rewrite();
+ EmitVariableLoad(expr->var(), context_);
+}
+
+
+void FastCodeGenerator::EmitVariableLoad(Variable* var,
+ Expression::Context context) {
+ Expression* rewrite = var->rewrite();
if (rewrite == NULL) {
- ASSERT(expr->var()->is_global());
+ ASSERT(var->is_global());
Comment cmnt(masm_, "Global variable");
// Use inline caching. Variable name is passed in rcx and the global
// object on the stack.
__ push(CodeGenerator::GlobalObject());
- __ Move(rcx, expr->name());
+ __ Move(rcx, var->name());
Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize));
__ Call(ic, RelocInfo::CODE_TARGET_CONTEXT);
// A test rax instruction following the call is used by the IC to
// indicate that the inobject property case was inlined. Ensure there
// is no test rax instruction here.
__ nop();
-
- DropAndMove(expr->context(), rax);
+ DropAndApply(1, context, rax);
} else if (rewrite->AsSlot() != NULL) {
Slot* slot = rewrite->AsSlot();
if (FLAG_debug_code) {
switch (slot->type()) {
- case Slot::LOCAL:
- case Slot::PARAMETER: {
+ case Slot::PARAMETER:
+ case Slot::LOCAL: {
Comment cmnt(masm_, "Stack slot");
break;
}
@@ -591,45 +821,45 @@
case Slot::LOOKUP:
UNIMPLEMENTED();
break;
- default:
- UNREACHABLE();
}
}
- Move(expr->context(), slot, rax);
+ Apply(context, slot);
} else {
- // A variable has been rewritten into an explicit access to
- // an object property.
+ Comment cmnt(masm_, "Variable rewritten to property");
+ // A variable has been rewritten into an explicit access to an object
+ // property.
Property* property = rewrite->AsProperty();
ASSERT_NOT_NULL(property);
- // Currently the only parameter expressions that can occur are
- // on the form "slot[literal]".
+ // The only property expressions that can occur are of the form
+ // "slot[literal]".
- // Check that the object is in a slot.
- Variable* object = property->obj()->AsVariableProxy()->AsVariable();
- ASSERT_NOT_NULL(object);
- Slot* object_slot = object->slot();
+ // Assert that the object is in a slot.
+ Variable* object_var = property->obj()->AsVariableProxy()->AsVariable();
+ ASSERT_NOT_NULL(object_var);
+ Slot* object_slot = object_var->slot();
ASSERT_NOT_NULL(object_slot);
// Load the object.
- Move(Expression::kValue, object_slot, rax);
+ MemOperand object_loc = EmitSlotSearch(object_slot, rax);
+ __ push(object_loc);
- // Check that the key is a smi.
+ // Assert that the key is a smi.
Literal* key_literal = property->key()->AsLiteral();
ASSERT_NOT_NULL(key_literal);
ASSERT(key_literal->handle()->IsSmi());
// Load the key.
- Move(Expression::kValue, key_literal);
+ __ Push(key_literal->handle());
- // Do a KEYED property load.
+ // Do a keyed property load.
Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
__ call(ic, RelocInfo::CODE_TARGET);
- // Notice: We must not have a "test rax, ..." instruction after
- // the call. It is treated specially by the LoadIC code.
-
+ // Notice: We must not have a "test rax, ..." instruction after the
+ // call. It is treated specially by the LoadIC code.
+ __ nop();
// Drop key and object left on the stack by IC, and push the result.
- DropAndMove(expr->context(), rax, 2);
+ DropAndApply(2, context, rax);
}
}
@@ -655,44 +885,25 @@
__ Push(expr->pattern());
__ Push(expr->flags());
__ CallRuntime(Runtime::kMaterializeRegExpLiteral, 4);
- // Label done:
__ bind(&done);
- Move(expr->context(), rax);
+ Apply(context_, rax);
}
void FastCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
Comment cmnt(masm_, "[ ObjectLiteral");
- Label boilerplate_exists;
-
__ movq(rdi, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset));
- __ movq(rbx, FieldOperand(rdi, JSFunction::kLiteralsOffset));
- int literal_offset =
- FixedArray::kHeaderSize + expr->literal_index() * kPointerSize;
- __ movq(rax, FieldOperand(rbx, literal_offset));
- __ CompareRoot(rax, Heap::kUndefinedValueRootIndex);
- __ j(not_equal, &boilerplate_exists);
- // Create boilerplate if it does not exist.
- // Literal array (0).
- __ push(rbx);
- // Literal index (1).
+ __ push(FieldOperand(rdi, JSFunction::kLiteralsOffset));
__ Push(Smi::FromInt(expr->literal_index()));
- // Constant properties (2).
__ Push(expr->constant_properties());
- __ CallRuntime(Runtime::kCreateObjectLiteralBoilerplate, 3);
- __ bind(&boilerplate_exists);
- // rax contains boilerplate.
- // Clone boilerplate.
- __ push(rax);
- if (expr->depth() == 1) {
- __ CallRuntime(Runtime::kCloneShallowLiteralBoilerplate, 1);
+ if (expr->depth() > 1) {
+ __ CallRuntime(Runtime::kCreateObjectLiteral, 3);
} else {
- __ CallRuntime(Runtime::kCloneLiteralBoilerplate, 1);
+ __ CallRuntime(Runtime::kCreateObjectLiteralShallow, 3);
}
- // If result_saved == true: The result is saved on top of the
- // stack and in rax.
- // If result_saved == false: The result not on the stack, just in rax.
+ // If result_saved is true the result is on top of the stack. If
+ // result_saved is false the result is in rax.
bool result_saved = false;
for (int i = 0; i < expr->properties()->length(); i++) {
@@ -706,108 +917,59 @@
result_saved = true;
}
switch (property->kind()) {
- case ObjectLiteral::Property::MATERIALIZED_LITERAL: // fall through
+ case ObjectLiteral::Property::CONSTANT:
+ UNREACHABLE();
+ case ObjectLiteral::Property::MATERIALIZED_LITERAL:
ASSERT(!CompileTimeValue::IsCompileTimeValue(value));
+ // Fall through.
case ObjectLiteral::Property::COMPUTED:
if (key->handle()->IsSymbol()) {
- Visit(value);
- ASSERT_EQ(Expression::kValue, value->context());
- __ pop(rax);
+ VisitForValue(value, kAccumulator);
__ Move(rcx, key->handle());
Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
__ call(ic, RelocInfo::CODE_TARGET);
+ __ nop();
// StoreIC leaves the receiver on the stack.
- __ movq(rax, Operand(rsp, 0)); // Restore result back into rax.
break;
}
- // fall through
+ // Fall through.
case ObjectLiteral::Property::PROTOTYPE:
- __ push(rax);
- Visit(key);
- ASSERT_EQ(Expression::kValue, key->context());
- Visit(value);
- ASSERT_EQ(Expression::kValue, value->context());
+ __ push(Operand(rsp, 0)); // Duplicate receiver.
+ VisitForValue(key, kStack);
+ VisitForValue(value, kStack);
__ CallRuntime(Runtime::kSetProperty, 3);
- __ movq(rax, Operand(rsp, 0)); // Restore result into rax.
break;
- case ObjectLiteral::Property::SETTER: // fall through
+ case ObjectLiteral::Property::SETTER:
case ObjectLiteral::Property::GETTER:
- __ push(rax);
- Visit(key);
- ASSERT_EQ(Expression::kValue, key->context());
+ __ push(Operand(rsp, 0)); // Duplicate receiver.
+ VisitForValue(key, kStack);
__ Push(property->kind() == ObjectLiteral::Property::SETTER ?
Smi::FromInt(1) :
Smi::FromInt(0));
- Visit(value);
- ASSERT_EQ(Expression::kValue, value->context());
+ VisitForValue(value, kStack);
__ CallRuntime(Runtime::kDefineAccessor, 4);
- __ movq(rax, Operand(rsp, 0)); // Restore result into rax.
break;
- default: UNREACHABLE();
}
}
- switch (expr->context()) {
- case Expression::kUninitialized:
- UNREACHABLE();
- case Expression::kEffect:
- if (result_saved) __ addq(rsp, Immediate(kPointerSize));
- break;
- case Expression::kValue:
- if (!result_saved) __ push(rax);
- break;
- case Expression::kTest:
- if (result_saved) __ pop(rax);
- TestAndBranch(rax, true_label_, false_label_);
- break;
- case Expression::kValueTest: {
- Label discard;
- if (!result_saved) __ push(rax);
- TestAndBranch(rax, true_label_, &discard);
- __ bind(&discard);
- __ addq(rsp, Immediate(kPointerSize));
- __ jmp(false_label_);
- break;
- }
- case Expression::kTestValue: {
- Label discard;
- if (!result_saved) __ push(rax);
- TestAndBranch(rax, &discard, false_label_);
- __ bind(&discard);
- __ addq(rsp, Immediate(kPointerSize));
- __ jmp(true_label_);
- break;
- }
+
+ if (result_saved) {
+ ApplyTOS(context_);
+ } else {
+ Apply(context_, rax);
}
}
void FastCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) {
Comment cmnt(masm_, "[ ArrayLiteral");
- Label make_clone;
-
- // Fetch the function's literals array.
__ movq(rbx, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset));
- __ movq(rbx, FieldOperand(rbx, JSFunction::kLiteralsOffset));
- // Check if the literal's boilerplate has been instantiated.
- int offset =
- FixedArray::kHeaderSize + (expr->literal_index() * kPointerSize);
- __ movq(rax, FieldOperand(rbx, offset));
- __ CompareRoot(rax, Heap::kUndefinedValueRootIndex);
- __ j(not_equal, &make_clone);
-
- // Instantiate the boilerplate.
- __ push(rbx);
+ __ push(FieldOperand(rbx, JSFunction::kLiteralsOffset));
__ Push(Smi::FromInt(expr->literal_index()));
- __ Push(expr->literals());
- __ CallRuntime(Runtime::kCreateArrayLiteralBoilerplate, 3);
-
- __ bind(&make_clone);
- // Clone the boilerplate.
- __ push(rax);
+ __ Push(expr->constant_elements());
if (expr->depth() > 1) {
- __ CallRuntime(Runtime::kCloneLiteralBoilerplate, 1);
+ __ CallRuntime(Runtime::kCreateArrayLiteral, 3);
} else {
- __ CallRuntime(Runtime::kCloneShallowLiteralBoilerplate, 1);
+ __ CallRuntime(Runtime::kCreateArrayLiteralShallow, 3);
}
bool result_saved = false; // Is the result saved to the stack?
@@ -828,156 +990,86 @@
__ push(rax);
result_saved = true;
}
- Visit(subexpr);
- ASSERT_EQ(Expression::kValue, subexpr->context());
+ VisitForValue(subexpr, kAccumulator);
// Store the subexpression value in the array's elements.
- __ pop(rax); // Subexpression value.
__ movq(rbx, Operand(rsp, 0)); // Copy of array literal.
__ movq(rbx, FieldOperand(rbx, JSObject::kElementsOffset));
int offset = FixedArray::kHeaderSize + (i * kPointerSize);
- __ movq(FieldOperand(rbx, offset), rax);
+ __ movq(FieldOperand(rbx, offset), result_register());
// Update the write barrier for the array store.
- __ RecordWrite(rbx, offset, rax, rcx);
+ __ RecordWrite(rbx, offset, result_register(), rcx);
}
- switch (expr->context()) {
- case Expression::kUninitialized:
- UNREACHABLE();
- case Expression::kEffect:
- if (result_saved) __ addq(rsp, Immediate(kPointerSize));
- break;
- case Expression::kValue:
- if (!result_saved) __ push(rax);
- break;
- case Expression::kTest:
- if (result_saved) __ pop(rax);
- TestAndBranch(rax, true_label_, false_label_);
- break;
- case Expression::kValueTest: {
- Label discard;
- if (!result_saved) __ push(rax);
- TestAndBranch(rax, true_label_, &discard);
- __ bind(&discard);
- __ addq(rsp, Immediate(kPointerSize));
- __ jmp(false_label_);
- break;
- }
- case Expression::kTestValue: {
- Label discard;
- if (!result_saved) __ push(rax);
- TestAndBranch(rax, &discard, false_label_);
- __ bind(&discard);
- __ addq(rsp, Immediate(kPointerSize));
- __ jmp(true_label_);
- break;
- }
+ if (result_saved) {
+ ApplyTOS(context_);
+ } else {
+ Apply(context_, rax);
}
}
-void FastCodeGenerator::EmitVariableAssignment(Assignment* expr) {
- Variable* var = expr->target()->AsVariableProxy()->AsVariable();
+void FastCodeGenerator::EmitNamedPropertyLoad(Property* prop) {
+ SetSourcePosition(prop->position());
+ Literal* key = prop->key()->AsLiteral();
+ __ Move(rcx, key->handle());
+ Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize));
+ __ Call(ic, RelocInfo::CODE_TARGET);
+ __ nop();
+}
+
+
+void FastCodeGenerator::EmitKeyedPropertyLoad(Property* prop) {
+ SetSourcePosition(prop->position());
+ Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
+ __ Call(ic, RelocInfo::CODE_TARGET);
+ __ nop();
+}
+
+
+void FastCodeGenerator::EmitBinaryOp(Token::Value op,
+ Expression::Context context) {
+ __ push(result_register());
+ GenericBinaryOpStub stub(op,
+ NO_OVERWRITE,
+ NO_GENERIC_BINARY_FLAGS);
+ __ CallStub(&stub);
+ Apply(context, rax);
+}
+
+
+void FastCodeGenerator::EmitVariableAssignment(Variable* var,
+ Expression::Context context) {
ASSERT(var != NULL);
ASSERT(var->is_global() || var->slot() != NULL);
if (var->is_global()) {
// Assignment to a global variable. Use inline caching for the
// assignment. Right-hand-side value is passed in rax, variable name in
// rcx, and the global object on the stack.
- __ pop(rax);
__ Move(rcx, var->name());
__ push(CodeGenerator::GlobalObject());
Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
__ Call(ic, RelocInfo::CODE_TARGET);
// Overwrite the global object on the stack with the result if needed.
- DropAndMove(expr->context(), rax);
+ DropAndApply(1, context, rax);
- } else if (var->slot()) {
+ } else if (var->slot() != NULL) {
Slot* slot = var->slot();
- ASSERT_NOT_NULL(slot); // Variables rewritten as properties not handled.
switch (slot->type()) {
case Slot::LOCAL:
- case Slot::PARAMETER: {
- switch (expr->context()) {
- case Expression::kUninitialized:
- UNREACHABLE();
- case Expression::kEffect:
- // Perform assignment and discard value.
- __ pop(Operand(rbp, SlotOffset(var->slot())));
- break;
- case Expression::kValue:
- // Perform assignment and preserve value.
- __ movq(rax, Operand(rsp, 0));
- __ movq(Operand(rbp, SlotOffset(var->slot())), rax);
- break;
- case Expression::kTest:
- // Perform assignment and test (and discard) value.
- __ pop(rax);
- __ movq(Operand(rbp, SlotOffset(var->slot())), rax);
- TestAndBranch(rax, true_label_, false_label_);
- break;
- case Expression::kValueTest: {
- Label discard;
- __ movq(rax, Operand(rsp, 0));
- __ movq(Operand(rbp, SlotOffset(var->slot())), rax);
- TestAndBranch(rax, true_label_, &discard);
- __ bind(&discard);
- __ addq(rsp, Immediate(kPointerSize));
- __ jmp(false_label_);
- break;
- }
- case Expression::kTestValue: {
- Label discard;
- __ movq(rax, Operand(rsp, 0));
- __ movq(Operand(rbp, SlotOffset(var->slot())), rax);
- TestAndBranch(rax, &discard, false_label_);
- __ bind(&discard);
- __ addq(rsp, Immediate(kPointerSize));
- __ jmp(true_label_);
- break;
- }
- }
+ case Slot::PARAMETER:
+ __ movq(Operand(rbp, SlotOffset(slot)), result_register());
break;
- }
case Slot::CONTEXT: {
- int chain_length =
- function_->scope()->ContextChainLength(slot->var()->scope());
- if (chain_length > 0) {
- // Move up the context chain to the context containing the slot.
- __ movq(rax,
- Operand(rsi, Context::SlotOffset(Context::CLOSURE_INDEX)));
- // Load the function context (which is the incoming, outer context).
- __ movq(rax, FieldOperand(rax, JSFunction::kContextOffset));
- for (int i = 1; i < chain_length; i++) {
- __ movq(rax,
- Operand(rax, Context::SlotOffset(Context::CLOSURE_INDEX)));
- __ movq(rax, FieldOperand(rax, JSFunction::kContextOffset));
- }
- } else { // Slot is in the current context. Generate optimized code.
- __ movq(rax, rsi); // RecordWrite destroys the object register.
- }
- if (FLAG_debug_code) {
- __ cmpq(rax,
- Operand(rax, Context::SlotOffset(Context::FCONTEXT_INDEX)));
- __ Check(equal, "Context Slot chain length wrong.");
- }
- __ pop(rcx);
- __ movq(Operand(rax, Context::SlotOffset(slot->index())), rcx);
+ MemOperand target = EmitSlotSearch(slot, rcx);
+ __ movq(target, result_register());
// RecordWrite may destroy all its register arguments.
- if (expr->context() == Expression::kValue) {
- __ push(rcx);
- } else if (expr->context() != Expression::kEffect) {
- __ movq(rdx, rcx);
- }
+ __ movq(rdx, result_register());
int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize;
- __ RecordWrite(rax, offset, rcx, rbx);
- if (expr->context() != Expression::kEffect &&
- expr->context() != Expression::kValue) {
- Move(expr->context(), rdx);
- }
+ __ RecordWrite(rcx, offset, rdx, rbx);
break;
}
@@ -985,6 +1077,11 @@
UNREACHABLE();
break;
}
+ Apply(context, result_register());
+ } else {
+ // Variables rewritten as properties are not treated as variables in
+ // assignments.
+ UNREACHABLE();
}
}
@@ -999,14 +1096,18 @@
// change to slow case to avoid the quadratic behavior of repeatedly
// adding fast properties.
if (expr->starts_initialization_block()) {
- __ push(Operand(rsp, kPointerSize)); // Receiver is under value.
+ __ push(result_register());
+ __ push(Operand(rsp, kPointerSize)); // Receiver is now under value.
__ CallRuntime(Runtime::kToSlowProperties, 1);
+ __ pop(result_register());
}
- __ pop(rax);
+ // Record source code position before IC call.
+ SetSourcePosition(expr->position());
__ Move(rcx, prop->key()->AsLiteral()->handle());
Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
__ Call(ic, RelocInfo::CODE_TARGET);
+ __ nop();
// If the assignment ends an initialization block, revert to fast case.
if (expr->ends_initialization_block()) {
@@ -1016,7 +1117,7 @@
__ pop(rax);
}
- DropAndMove(expr->context(), rax);
+ DropAndApply(1, context_, rax);
}
@@ -1027,12 +1128,15 @@
// change to slow case to avoid the quadratic behavior of repeatedly
// adding fast properties.
if (expr->starts_initialization_block()) {
- // Reciever is under the key and value.
+ __ push(result_register());
+ // Receiver is now under the key and value.
__ push(Operand(rsp, 2 * kPointerSize));
__ CallRuntime(Runtime::kToSlowProperties, 1);
+ __ pop(result_register());
}
- __ pop(rax);
+ // Record source code position before IC call.
+ SetSourcePosition(expr->position());
Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize));
__ Call(ic, RelocInfo::CODE_TARGET);
// This nop signals to the IC that there is no inlined code at the call
@@ -1042,73 +1146,57 @@
// If the assignment ends an initialization block, revert to fast case.
if (expr->ends_initialization_block()) {
__ push(rax); // Result of assignment, saved even if not needed.
- // Reciever is under the key and value.
+ // Receiver is under the key and value.
__ push(Operand(rsp, 2 * kPointerSize));
__ CallRuntime(Runtime::kToFastProperties, 1);
__ pop(rax);
}
// Receiver and key are still on stack.
- __ addq(rsp, Immediate(2 * kPointerSize));
- Move(expr->context(), rax);
+ DropAndApply(2, context_, rax);
}
void FastCodeGenerator::VisitProperty(Property* expr) {
Comment cmnt(masm_, "[ Property");
Expression* key = expr->key();
- uint32_t dummy;
-
- // Record the source position for the property load.
- SetSourcePosition(expr->position());
// Evaluate receiver.
- Visit(expr->obj());
+ VisitForValue(expr->obj(), kStack);
-
- if (key->AsLiteral() != NULL && key->AsLiteral()->handle()->IsSymbol() &&
- !String::cast(*(key->AsLiteral()->handle()))->AsArrayIndex(&dummy)) {
- // Do a NAMED property load.
- // The IC expects the property name in rcx and the receiver on the stack.
- __ Move(rcx, key->AsLiteral()->handle());
- Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize));
- __ call(ic, RelocInfo::CODE_TARGET);
- // By emitting a nop we make sure that we do not have a "test rax,..."
- // instruction after the call it is treated specially by the LoadIC code.
- __ nop();
+ if (key->IsPropertyName()) {
+ EmitNamedPropertyLoad(expr);
+ // Drop receiver left on the stack by IC.
+ DropAndApply(1, context_, rax);
} else {
- // Do a KEYED property load.
- Visit(expr->key());
- Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
- __ call(ic, RelocInfo::CODE_TARGET);
- // Notice: We must not have a "test rax, ..." instruction after
- // the call. It is treated specially by the LoadIC code.
-
- // Drop key left on the stack by IC.
- __ addq(rsp, Immediate(kPointerSize));
+ VisitForValue(expr->key(), kStack);
+ EmitKeyedPropertyLoad(expr);
+ // Drop key and receiver left on the stack by IC.
+ DropAndApply(2, context_, rax);
}
- DropAndMove(expr->context(), rax);
}
-void FastCodeGenerator::EmitCallWithIC(Call* expr, RelocInfo::Mode reloc_info) {
+void FastCodeGenerator::EmitCallWithIC(Call* expr,
+ Handle<Object> ignored,
+ RelocInfo::Mode mode) {
// Code common for calls using the IC.
ZoneList<Expression*>* args = expr->arguments();
int arg_count = args->length();
for (int i = 0; i < arg_count; i++) {
- Visit(args->at(i));
- ASSERT_EQ(Expression::kValue, args->at(i)->context());
+ VisitForValue(args->at(i), kStack);
}
// Record source position for debugger.
SetSourcePosition(expr->position());
// Call the IC initialization code.
+ InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
Handle<Code> ic = CodeGenerator::ComputeCallInitialize(arg_count,
- NOT_IN_LOOP);
- __ call(ic, reloc_info);
+ in_loop);
+ __ Call(ic, mode);
// Restore context register.
__ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
// Discard the function left on TOS.
- DropAndMove(expr->context(), rax);
+ DropAndApply(1, context_, rax);
}
@@ -1117,16 +1205,16 @@
ZoneList<Expression*>* args = expr->arguments();
int arg_count = args->length();
for (int i = 0; i < arg_count; i++) {
- Visit(args->at(i));
+ VisitForValue(args->at(i), kStack);
}
// Record source position for debugger.
SetSourcePosition(expr->position());
- CallFunctionStub stub(arg_count, NOT_IN_LOOP);
+ CallFunctionStub stub(arg_count, NOT_IN_LOOP, RECEIVER_MIGHT_BE_VALUE);
__ CallStub(&stub);
// Restore context register.
__ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
// Discard the function left on TOS.
- DropAndMove(expr->context(), rax);
+ DropAndApply(1, context_, rax);
}
@@ -1143,7 +1231,7 @@
__ Push(var->name());
// Push global object as receiver for the call IC lookup.
__ push(CodeGenerator::GlobalObject());
- EmitCallWithIC(expr, RelocInfo::CODE_TARGET_CONTEXT);
+ EmitCallWithIC(expr, var->name(), RelocInfo::CODE_TARGET_CONTEXT);
} else if (var != NULL && var->slot() != NULL &&
var->slot()->type() == Slot::LOOKUP) {
// Call to a lookup slot.
@@ -1155,13 +1243,13 @@
if (key != NULL && key->handle()->IsSymbol()) {
// Call to a named property, use call IC.
__ Push(key->handle());
- Visit(prop->obj());
- EmitCallWithIC(expr, RelocInfo::CODE_TARGET);
+ VisitForValue(prop->obj(), kStack);
+ EmitCallWithIC(expr, key->handle(), RelocInfo::CODE_TARGET);
} else {
// Call to a keyed property, use keyed load IC followed by function
// call.
- Visit(prop->obj());
- Visit(prop->key());
+ VisitForValue(prop->obj(), kStack);
+ VisitForValue(prop->key(), kStack);
// Record source code position for IC call.
SetSourcePosition(prop->position());
Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
@@ -1170,14 +1258,15 @@
// instruction after the call it is treated specially by the LoadIC code.
__ nop();
// Drop key left on the stack by IC.
- __ addq(rsp, Immediate(kPointerSize));
+ __ Drop(1);
// Pop receiver.
__ pop(rbx);
// Push result (function).
__ push(rax);
// Push receiver object on stack.
if (prop->is_synthetic()) {
- __ push(CodeGenerator::GlobalObject());
+ __ movq(rcx, CodeGenerator::GlobalObject());
+ __ push(FieldOperand(rcx, GlobalObject::kGlobalReceiverOffset));
} else {
__ push(rbx);
}
@@ -1193,7 +1282,7 @@
loop_depth() == 0) {
lit->set_try_fast_codegen(true);
}
- Visit(fun);
+ VisitForValue(fun, kStack);
// Load global receiver object.
__ movq(rbx, CodeGenerator::GlobalObject());
__ push(FieldOperand(rbx, GlobalObject::kGlobalReceiverOffset));
@@ -1209,9 +1298,7 @@
// expression in new calls must be evaluated before the
// arguments.
// Push function on the stack.
- Visit(expr->expression());
- ASSERT_EQ(Expression::kValue, expr->expression()->context());
- // If location is value, already on the stack,
+ VisitForValue(expr->expression(), kStack);
// Push global object (receiver).
__ push(CodeGenerator::GlobalObject());
@@ -1220,10 +1307,7 @@
ZoneList<Expression*>* args = expr->arguments();
int arg_count = args->length();
for (int i = 0; i < arg_count; i++) {
- Visit(args->at(i));
- ASSERT_EQ(Expression::kValue, args->at(i)->context());
- // If location is value, it is already on the stack,
- // so nothing to do here.
+ VisitForValue(args->at(i), kStack);
}
// Call the construct call builtin that handles allocation and
@@ -1239,7 +1323,7 @@
__ Call(construct_builtin, RelocInfo::CONSTRUCT_CALL);
// Replace function on TOS with result in rax, or pop it.
- DropAndMove(expr->context(), rax);
+ DropAndApply(1, context_, rax);
}
@@ -1257,8 +1341,7 @@
// Push the arguments ("left-to-right").
int arg_count = args->length();
for (int i = 0; i < arg_count; i++) {
- Visit(args->at(i));
- ASSERT_EQ(Expression::kValue, args->at(i)->context());
+ VisitForValue(args->at(i), kStack);
}
if (expr->is_jsruntime()) {
@@ -1269,82 +1352,10 @@
// Restore context register.
__ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
// Discard the function left on TOS.
- DropAndMove(expr->context(), rax);
+ DropAndApply(1, context_, rax);
} else {
__ CallRuntime(expr->function(), arg_count);
- Move(expr->context(), rax);
- }
-}
-
-void FastCodeGenerator::VisitCountOperation(CountOperation* expr) {
- Comment cmnt(masm_, "[ CountOperation");
- VariableProxy* proxy = expr->expression()->AsVariableProxy();
- ASSERT(proxy->AsVariable() != NULL);
- ASSERT(proxy->AsVariable()->is_global());
-
- Visit(proxy);
- __ InvokeBuiltin(Builtins::TO_NUMBER, CALL_FUNCTION);
-
- switch (expr->context()) {
- case Expression::kUninitialized:
- UNREACHABLE();
- case Expression::kValue: // Fall through
- case Expression::kTest: // Fall through
- case Expression::kTestValue: // Fall through
- case Expression::kValueTest:
- // Duplicate the result on the stack.
- __ push(rax);
- break;
- case Expression::kEffect:
- // Do not save result.
- break;
- }
- // Call runtime for +1/-1.
- __ push(rax);
- __ Push(Smi::FromInt(1));
- if (expr->op() == Token::INC) {
- __ CallRuntime(Runtime::kNumberAdd, 2);
- } else {
- __ CallRuntime(Runtime::kNumberSub, 2);
- }
- // Call Store IC.
- __ Move(rcx, proxy->AsVariable()->name());
- __ push(CodeGenerator::GlobalObject());
- Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
- __ call(ic, RelocInfo::CODE_TARGET);
- // Restore up stack after store IC
- __ addq(rsp, Immediate(kPointerSize));
-
- switch (expr->context()) {
- case Expression::kUninitialized:
- UNREACHABLE();
- case Expression::kEffect: // Fall through
- case Expression::kValue:
- // Do nothing. Result in either on the stack for value context
- // or discarded for effect context.
- break;
- case Expression::kTest:
- __ pop(rax);
- TestAndBranch(rax, true_label_, false_label_);
- break;
- case Expression::kValueTest: {
- Label discard;
- __ movq(rax, Operand(rsp, 0));
- TestAndBranch(rax, true_label_, &discard);
- __ bind(&discard);
- __ addq(rsp, Immediate(kPointerSize));
- __ jmp(false_label_);
- break;
- }
- case Expression::kTestValue: {
- Label discard;
- __ movq(rax, Operand(rsp, 0));
- TestAndBranch(rax, &discard, false_label_);
- __ bind(&discard);
- __ addq(rsp, Immediate(kPointerSize));
- __ jmp(true_label_);
- break;
- }
+ Apply(context_, rax);
}
}
@@ -1353,22 +1364,35 @@
switch (expr->op()) {
case Token::VOID: {
Comment cmnt(masm_, "[ UnaryOperation (VOID)");
- Visit(expr->expression());
- ASSERT_EQ(Expression::kEffect, expr->expression()->context());
- switch (expr->context()) {
+ VisitForEffect(expr->expression());
+ switch (context_) {
case Expression::kUninitialized:
UNREACHABLE();
break;
case Expression::kEffect:
break;
case Expression::kValue:
- __ PushRoot(Heap::kUndefinedValueRootIndex);
+ switch (location_) {
+ case kAccumulator:
+ __ LoadRoot(result_register(), Heap::kUndefinedValueRootIndex);
+ break;
+ case kStack:
+ __ PushRoot(Heap::kUndefinedValueRootIndex);
+ break;
+ }
break;
case Expression::kTestValue:
// Value is false so it's needed.
- __ PushRoot(Heap::kUndefinedValueRootIndex);
+ switch (location_) {
+ case kAccumulator:
+ __ LoadRoot(result_register(), Heap::kUndefinedValueRootIndex);
+ break;
+ case kStack:
+ __ PushRoot(Heap::kUndefinedValueRootIndex);
+ break;
+ }
// Fall through.
- case Expression::kTest: // Fall through.
+ case Expression::kTest:
case Expression::kValueTest:
__ jmp(false_label_);
break;
@@ -1378,70 +1402,39 @@
case Token::NOT: {
Comment cmnt(masm_, "[ UnaryOperation (NOT)");
- ASSERT_EQ(Expression::kTest, expr->expression()->context());
-
- Label push_true;
- Label push_false;
- Label done;
- Label* saved_true = true_label_;
- Label* saved_false = false_label_;
- switch (expr->context()) {
+ Label materialize_true, materialize_false, done;
+ // Initially assume a pure test context. Notice that the labels are
+ // swapped.
+ Label* if_true = false_label_;
+ Label* if_false = true_label_;
+ switch (context_) {
case Expression::kUninitialized:
UNREACHABLE();
break;
-
- case Expression::kValue:
- true_label_ = &push_false;
- false_label_ = &push_true;
- Visit(expr->expression());
- __ bind(&push_true);
- __ PushRoot(Heap::kTrueValueRootIndex);
- __ jmp(&done);
- __ bind(&push_false);
- __ PushRoot(Heap::kFalseValueRootIndex);
- __ bind(&done);
- break;
-
case Expression::kEffect:
- true_label_ = &done;
- false_label_ = &done;
- Visit(expr->expression());
- __ bind(&done);
+ if_true = &done;
+ if_false = &done;
break;
-
+ case Expression::kValue:
+ if_true = &materialize_false;
+ if_false = &materialize_true;
+ break;
case Expression::kTest:
- true_label_ = saved_false;
- false_label_ = saved_true;
- Visit(expr->expression());
break;
-
case Expression::kValueTest:
- true_label_ = saved_false;
- false_label_ = &push_true;
- Visit(expr->expression());
- __ bind(&push_true);
- __ PushRoot(Heap::kTrueValueRootIndex);
- __ jmp(saved_true);
+ if_false = &materialize_true;
break;
-
case Expression::kTestValue:
- true_label_ = &push_false;
- false_label_ = saved_true;
- Visit(expr->expression());
- __ bind(&push_false);
- __ PushRoot(Heap::kFalseValueRootIndex);
- __ jmp(saved_false);
+ if_true = &materialize_false;
break;
}
- true_label_ = saved_true;
- false_label_ = saved_false;
+ VisitForControl(expr->expression(), if_true, if_false);
+ Apply(context_, if_false, if_true); // Labels swapped.
break;
}
case Token::TYPEOF: {
Comment cmnt(masm_, "[ UnaryOperation (TYPEOF)");
- ASSERT_EQ(Expression::kValue, expr->expression()->context());
-
VariableProxy* proxy = expr->expression()->AsVariableProxy();
if (proxy != NULL &&
!proxy->var()->is_this() &&
@@ -1463,11 +1456,11 @@
__ push(rax);
} else {
// This expression cannot throw a reference error at the top level.
- Visit(expr->expression());
+ VisitForValue(expr->expression(), kStack);
}
__ CallRuntime(Runtime::kTypeof, 1);
- Move(expr->context(), rax);
+ Apply(context_, rax);
break;
}
@@ -1477,13 +1470,142 @@
}
+void FastCodeGenerator::VisitCountOperation(CountOperation* expr) {
+ Comment cmnt(masm_, "[ CountOperation");
+
+ // Expression can only be a property, a global or a (parameter or local)
+ // 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->expression()->AsProperty();
+ // In case of a property we use the uninitialized expression context
+ // of the key to detect a named property.
+ if (prop != NULL) {
+ assign_type =
+ (prop->key()->IsPropertyName()) ? NAMED_PROPERTY : KEYED_PROPERTY;
+ }
+
+ // Evaluate expression and get value.
+ if (assign_type == VARIABLE) {
+ ASSERT(expr->expression()->AsVariableProxy()->var() != NULL);
+ Location saved_location = location_;
+ location_ = kStack;
+ EmitVariableLoad(expr->expression()->AsVariableProxy()->var(),
+ Expression::kValue);
+ location_ = saved_location;
+ } else {
+ // Reserve space for result of postfix operation.
+ if (expr->is_postfix() && context_ != Expression::kEffect) {
+ __ Push(Smi::FromInt(0));
+ }
+ VisitForValue(prop->obj(), kStack);
+ if (assign_type == NAMED_PROPERTY) {
+ EmitNamedPropertyLoad(prop);
+ } else {
+ VisitForValue(prop->key(), kStack);
+ EmitKeyedPropertyLoad(prop);
+ }
+ __ push(rax);
+ }
+
+ // Convert to number.
+ __ InvokeBuiltin(Builtins::TO_NUMBER, CALL_FUNCTION);
+
+ // Save result for postfix expressions.
+ if (expr->is_postfix()) {
+ switch (context_) {
+ case Expression::kUninitialized:
+ UNREACHABLE();
+ case Expression::kEffect:
+ // Do not save result.
+ 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.
+ switch (assign_type) {
+ case VARIABLE:
+ __ push(rax);
+ break;
+ case NAMED_PROPERTY:
+ __ movq(Operand(rsp, kPointerSize), rax);
+ break;
+ case KEYED_PROPERTY:
+ __ movq(Operand(rsp, 2 * kPointerSize), rax);
+ break;
+ }
+ break;
+ }
+ }
+
+ // Call stub for +1/-1.
+ __ push(rax);
+ __ Push(Smi::FromInt(1));
+ GenericBinaryOpStub stub(expr->binary_op(),
+ NO_OVERWRITE,
+ NO_GENERIC_BINARY_FLAGS);
+ __ CallStub(&stub);
+
+ // Store the value returned in rax.
+ switch (assign_type) {
+ case VARIABLE:
+ if (expr->is_postfix()) {
+ EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(),
+ Expression::kEffect);
+ // For all contexts except kEffect: We have the result on
+ // top of the stack.
+ if (context_ != Expression::kEffect) {
+ ApplyTOS(context_);
+ }
+ } else {
+ EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(),
+ context_);
+ }
+ break;
+ case NAMED_PROPERTY: {
+ __ Move(rcx, prop->key()->AsLiteral()->handle());
+ Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
+ __ call(ic, RelocInfo::CODE_TARGET);
+ // This nop signals to the IC that there is no inlined code at the call
+ // site for it to patch.
+ __ nop();
+ if (expr->is_postfix()) {
+ __ Drop(1); // Result is on the stack under the receiver.
+ if (context_ != Expression::kEffect) {
+ ApplyTOS(context_);
+ }
+ } else {
+ DropAndApply(1, context_, rax);
+ }
+ break;
+ }
+ case KEYED_PROPERTY: {
+ Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize));
+ __ call(ic, RelocInfo::CODE_TARGET);
+ // This nop signals to the IC that there is no inlined code at the call
+ // site for it to patch.
+ __ nop();
+ if (expr->is_postfix()) {
+ __ Drop(2); // Result is on the stack under the key and the receiver.
+ if (context_ != Expression::kEffect) {
+ ApplyTOS(context_);
+ }
+ } else {
+ DropAndApply(2, context_, rax);
+ }
+ break;
+ }
+ }
+}
+
void FastCodeGenerator::VisitBinaryOperation(BinaryOperation* expr) {
Comment cmnt(masm_, "[ BinaryOperation");
switch (expr->op()) {
case Token::COMMA:
- ASSERT_EQ(Expression::kEffect, expr->left()->context());
- ASSERT_EQ(expr->context(), expr->right()->context());
- Visit(expr->left());
+ VisitForEffect(expr->left());
Visit(expr->right());
break;
@@ -1502,20 +1624,12 @@
case Token::BIT_XOR:
case Token::SHL:
case Token::SHR:
- case Token::SAR: {
- ASSERT_EQ(Expression::kValue, expr->left()->context());
- ASSERT_EQ(Expression::kValue, expr->right()->context());
-
- Visit(expr->left());
- Visit(expr->right());
- GenericBinaryOpStub stub(expr->op(),
- NO_OVERWRITE,
- NO_GENERIC_BINARY_FLAGS);
- __ CallStub(&stub);
- Move(expr->context(), rax);
-
+ case Token::SAR:
+ VisitForValue(expr->left(), kStack);
+ VisitForValue(expr->right(), kAccumulator);
+ EmitBinaryOp(expr->op(), context_);
break;
- }
+
default:
UNREACHABLE();
}
@@ -1524,95 +1638,85 @@
void FastCodeGenerator::VisitCompareOperation(CompareOperation* expr) {
Comment cmnt(masm_, "[ CompareOperation");
- ASSERT_EQ(Expression::kValue, expr->left()->context());
- ASSERT_EQ(Expression::kValue, expr->right()->context());
- Visit(expr->left());
- Visit(expr->right());
- // Convert current context to test context: Pre-test code.
- Label push_true;
- Label push_false;
- Label done;
- Label* saved_true = true_label_;
- Label* saved_false = false_label_;
- switch (expr->context()) {
+ // 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, done;
+ // Initially assume we are in a test context.
+ Label* if_true = true_label_;
+ Label* if_false = false_label_;
+ switch (context_) {
case Expression::kUninitialized:
UNREACHABLE();
break;
-
- case Expression::kValue:
- true_label_ = &push_true;
- false_label_ = &push_false;
- break;
-
case Expression::kEffect:
- true_label_ = &done;
- false_label_ = &done;
+ if_true = &done;
+ if_false = &done;
break;
-
+ case Expression::kValue:
+ if_true = &materialize_true;
+ if_false = &materialize_false;
+ break;
case Expression::kTest:
break;
-
case Expression::kValueTest:
- true_label_ = &push_true;
+ if_true = &materialize_true;
break;
-
case Expression::kTestValue:
- false_label_ = &push_false;
+ if_false = &materialize_false;
break;
}
- // Convert current context to test context: End pre-test code.
+ VisitForValue(expr->left(), kStack);
switch (expr->op()) {
- case Token::IN: {
+ case Token::IN:
+ VisitForValue(expr->right(), kStack);
__ InvokeBuiltin(Builtins::IN, CALL_FUNCTION);
__ CompareRoot(rax, Heap::kTrueValueRootIndex);
- __ j(equal, true_label_);
- __ jmp(false_label_);
+ __ j(equal, if_true);
+ __ jmp(if_false);
break;
- }
case Token::INSTANCEOF: {
+ VisitForValue(expr->right(), kStack);
InstanceofStub stub;
__ CallStub(&stub);
__ testq(rax, rax);
- __ j(zero, true_label_); // The stub returns 0 for true.
- __ jmp(false_label_);
+ __ j(zero, if_true); // The stub returns 0 for true.
+ __ jmp(if_false);
break;
}
default: {
+ VisitForValue(expr->right(), kAccumulator);
Condition cc = no_condition;
bool strict = false;
switch (expr->op()) {
case Token::EQ_STRICT:
strict = true;
- // Fall through
+ // Fall through.
case Token::EQ:
cc = equal;
- __ pop(rax);
__ pop(rdx);
break;
case Token::LT:
cc = less;
- __ pop(rax);
__ pop(rdx);
break;
case Token::GT:
// Reverse left and right sizes to obtain ECMA-262 conversion order.
cc = less;
- __ pop(rdx);
+ __ movq(rdx, result_register());
__ pop(rax);
break;
case Token::LTE:
// Reverse left and right sizes to obtain ECMA-262 conversion order.
cc = greater_equal;
- __ pop(rdx);
+ __ movq(rdx, result_register());
__ pop(rax);
break;
case Token::GTE:
cc = greater_equal;
- __ pop(rax);
__ pop(rdx);
break;
case Token::IN:
@@ -1626,61 +1730,78 @@
Label slow_case;
__ JumpIfNotBothSmi(rax, rdx, &slow_case);
__ SmiCompare(rdx, rax);
- __ j(cc, true_label_);
- __ jmp(false_label_);
+ __ j(cc, if_true);
+ __ jmp(if_false);
__ bind(&slow_case);
CompareStub stub(cc, strict);
__ CallStub(&stub);
__ testq(rax, rax);
- __ j(cc, true_label_);
- __ jmp(false_label_);
+ __ j(cc, if_true);
+ __ jmp(if_false);
}
}
- // Convert current context to test context: Post-test code.
- switch (expr->context()) {
- case Expression::kUninitialized:
- UNREACHABLE();
- break;
-
- case Expression::kValue:
- __ bind(&push_true);
- __ PushRoot(Heap::kTrueValueRootIndex);
- __ jmp(&done);
- __ bind(&push_false);
- __ PushRoot(Heap::kFalseValueRootIndex);
- __ bind(&done);
- break;
-
- case Expression::kEffect:
- __ bind(&done);
- break;
-
- case Expression::kTest:
- break;
-
- case Expression::kValueTest:
- __ bind(&push_true);
- __ PushRoot(Heap::kTrueValueRootIndex);
- __ jmp(saved_true);
- break;
-
- case Expression::kTestValue:
- __ bind(&push_false);
- __ PushRoot(Heap::kFalseValueRootIndex);
- __ jmp(saved_false);
- break;
- }
- true_label_ = saved_true;
- false_label_ = saved_false;
- // Convert current context to test context: End post-test code.
+ // Convert the result of the comparison into one expected for this
+ // expression's context.
+ Apply(context_, if_true, if_false);
}
void FastCodeGenerator::VisitThisFunction(ThisFunction* expr) {
__ movq(rax, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset));
- Move(expr->context(), rax);
+ Apply(context_, rax);
+}
+
+
+Register FastCodeGenerator::result_register() { return rax; }
+
+
+Register FastCodeGenerator::context_register() { return rsi; }
+
+
+void FastCodeGenerator::StoreToFrameField(int frame_offset, Register value) {
+ ASSERT(IsAligned(frame_offset, kPointerSize));
+ __ movq(Operand(rbp, frame_offset), value);
+}
+
+
+void FastCodeGenerator::LoadContextField(Register dst, int context_index) {
+ __ movq(dst, CodeGenerator::ContextOperand(rsi, context_index));
+}
+
+
+// ----------------------------------------------------------------------------
+// Non-local control flow support.
+
+
+void FastCodeGenerator::EnterFinallyBlock() {
+ ASSERT(!result_register().is(rdx));
+ ASSERT(!result_register().is(rcx));
+ // Cook return address on top of stack (smi encoded Code* delta)
+ __ movq(rdx, Operand(rsp, 0));
+ __ Move(rcx, masm_->CodeObject());
+ __ subq(rdx, rcx);
+ __ Integer32ToSmi(rdx, rdx);
+ __ movq(Operand(rsp, 0), rdx);
+ // Store result register while executing finally block.
+ __ push(result_register());
+}
+
+
+void FastCodeGenerator::ExitFinallyBlock() {
+ ASSERT(!result_register().is(rdx));
+ ASSERT(!result_register().is(rcx));
+ // Restore result register from stack.
+ __ pop(result_register());
+ // Uncook return address.
+ __ movq(rdx, Operand(rsp, 0));
+ __ SmiToInteger32(rdx, rdx);
+ __ Move(rcx, masm_->CodeObject());
+ __ addq(rdx, rcx);
+ __ movq(Operand(rsp, 0), rdx);
+ // And return.
+ __ ret(0);
}
diff --git a/src/x64/ic-x64.cc b/src/x64/ic-x64.cc
index ccbc615..457ece5 100644
--- a/src/x64/ic-x64.cc
+++ b/src/x64/ic-x64.cc
@@ -48,9 +48,13 @@
// must always call a backup property load that is complete.
// This function is safe to call if the receiver has fast properties,
// or if name is not a symbol, and will jump to the miss_label in that case.
-static void GenerateDictionaryLoad(MacroAssembler* masm, Label* miss_label,
- Register r0, Register r1, Register r2,
- Register name) {
+static void GenerateDictionaryLoad(MacroAssembler* masm,
+ Label* miss_label,
+ Register r0,
+ Register r1,
+ Register r2,
+ Register name,
+ DictionaryCheck check_dictionary) {
// Register use:
//
// r0 - used to hold the property dictionary.
@@ -86,10 +90,14 @@
__ cmpb(r0, Immediate(JS_BUILTINS_OBJECT_TYPE));
__ j(equal, miss_label);
- // Check that the properties array is a dictionary.
+ // Load properties array.
__ movq(r0, FieldOperand(r1, JSObject::kPropertiesOffset));
- __ Cmp(FieldOperand(r0, HeapObject::kMapOffset), Factory::hash_table_map());
- __ j(not_equal, miss_label);
+
+ if (check_dictionary == CHECK_DICTIONARY) {
+ // Check that the properties array is a dictionary.
+ __ Cmp(FieldOperand(r0, HeapObject::kMapOffset), Factory::hash_table_map());
+ __ j(not_equal, miss_label);
+ }
// Compute the capacity mask.
const int kCapacityOffset =
@@ -246,7 +254,8 @@
// -- rsp[8] : name
// -- rsp[16] : receiver
// -----------------------------------
- Label slow, check_string, index_int, index_string, check_pixel_array;
+ Label slow, check_string, index_int, index_string;
+ Label check_pixel_array, probe_dictionary;
// Load name and receiver.
__ movq(rax, Operand(rsp, kPointerSize));
@@ -319,14 +328,69 @@
__ movl(rbx, FieldOperand(rax, String::kHashFieldOffset));
__ testl(rbx, Immediate(String::kIsArrayIndexMask));
- // If the string is a symbol, do a quick inline probe of the receiver's
- // dictionary, if it exists.
+ // Is the string a symbol?
__ j(not_zero, &index_string); // The value in rbx is used at jump target.
+ ASSERT(kSymbolTag != 0);
__ testb(FieldOperand(rdx, Map::kInstanceTypeOffset),
Immediate(kIsSymbolMask));
__ j(zero, &slow);
- // Probe the dictionary leaving result in rcx.
- GenerateDictionaryLoad(masm, &slow, rbx, rcx, rdx, rax);
+
+ // If the receiver is a fast-case object, check the keyed lookup
+ // cache. Otherwise probe the dictionary leaving result in rcx.
+ __ movq(rbx, FieldOperand(rcx, JSObject::kPropertiesOffset));
+ __ Cmp(FieldOperand(rbx, HeapObject::kMapOffset), Factory::hash_table_map());
+ __ j(equal, &probe_dictionary);
+
+ // Load the map of the receiver, compute the keyed lookup cache hash
+ // based on 32 bits of the map pointer and the string hash.
+ __ movq(rbx, FieldOperand(rcx, HeapObject::kMapOffset));
+ __ movl(rdx, rbx);
+ __ shr(rdx, Immediate(KeyedLookupCache::kMapHashShift));
+ __ movl(rax, FieldOperand(rax, String::kHashFieldOffset));
+ __ shr(rax, Immediate(String::kHashShift));
+ __ xor_(rdx, rax);
+ __ and_(rdx, Immediate(KeyedLookupCache::kCapacityMask));
+
+ // Load the key (consisting of map and symbol) from the cache and
+ // check for match.
+ ExternalReference cache_keys
+ = ExternalReference::keyed_lookup_cache_keys();
+ __ movq(rdi, rdx);
+ __ shl(rdi, Immediate(kPointerSizeLog2 + 1));
+ __ movq(kScratchRegister, cache_keys);
+ __ cmpq(rbx, Operand(kScratchRegister, rdi, times_1, 0));
+ __ j(not_equal, &slow);
+ __ movq(rdi, Operand(kScratchRegister, rdi, times_1, kPointerSize));
+ __ cmpq(Operand(rsp, kPointerSize), rdi);
+ __ j(not_equal, &slow);
+
+ // Get field offset which is a 32-bit integer and check that it is
+ // an in-object property.
+ ExternalReference cache_field_offsets
+ = ExternalReference::keyed_lookup_cache_field_offsets();
+ __ movq(kScratchRegister, cache_field_offsets);
+ __ movl(rax, Operand(kScratchRegister, rdx, times_4, 0));
+ __ movzxbq(rdx, FieldOperand(rbx, Map::kInObjectPropertiesOffset));
+ __ cmpq(rax, rdx);
+ __ j(above_equal, &slow);
+
+ // Load in-object property.
+ __ subq(rax, rdx);
+ __ movzxbq(rdx, FieldOperand(rbx, Map::kInstanceSizeOffset));
+ __ addq(rax, rdx);
+ __ movq(rax, FieldOperand(rcx, rax, times_pointer_size, 0));
+ __ ret(0);
+
+ // Do a quick inline probe of the receiver's dictionary, if it
+ // exists.
+ __ bind(&probe_dictionary);
+ GenerateDictionaryLoad(masm,
+ &slow,
+ rbx,
+ rcx,
+ rdx,
+ rax,
+ DICTIONARY_CHECK_DONE);
GenerateCheckNonObjectOrLoaded(masm, &slow, rcx);
__ movq(rax, rcx);
__ IncrementCounter(&Counters::keyed_load_generic_symbol, 1);
@@ -345,6 +409,16 @@
}
+void KeyedLoadIC::GenerateString(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- rsp[0] : return address
+ // -- rsp[8] : name
+ // -- rsp[16] : receiver
+ // -----------------------------------
+ GenerateGeneric(masm);
+}
+
+
void KeyedLoadIC::GenerateExternalArray(MacroAssembler* masm,
ExternalArrayType array_type) {
// ----------- S t a t e -------------
@@ -853,9 +927,7 @@
}
-void CallIC::Generate(MacroAssembler* masm,
- int argc,
- ExternalReference const& f) {
+void CallIC::GenerateMiss(MacroAssembler* masm, int argc) {
// Get the receiver of the function from the stack; 1 ~ return address.
__ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize));
// Get the name of the function to call from the stack.
@@ -872,7 +944,7 @@
// Call the entry.
CEntryStub stub(1);
__ movq(rax, Immediate(2));
- __ movq(rbx, f);
+ __ movq(rbx, ExternalReference(IC_Utility(kCallIC_Miss)));
__ CallStub(&stub);
// Move result to rdi and exit the internal frame.
@@ -910,7 +982,7 @@
// rsp[16] argument argc - 1
// ...
// rsp[argc * 8] argument 1
- // rsp[(argc + 1) * 8] argument 0 = reciever
+ // rsp[(argc + 1) * 8] argument 0 = receiver
// rsp[(argc + 2) * 8] function name
// -----------------------------------
Label number, non_number, non_string, boolean, probe, miss;
@@ -963,7 +1035,7 @@
// Cache miss: Jump to runtime.
__ bind(&miss);
- Generate(masm, argc, ExternalReference(IC_Utility(kCallIC_Miss)));
+ GenerateMiss(masm, argc);
}
@@ -971,8 +1043,8 @@
int argc,
bool is_global_object,
Label* miss) {
- // Search dictionary - put result in register edx.
- GenerateDictionaryLoad(masm, miss, rax, rdx, rbx, rcx);
+ // Search dictionary - put result in register rdx.
+ GenerateDictionaryLoad(masm, miss, rax, rdx, rbx, rcx, CHECK_DICTIONARY);
// Move the result to register rdi and check that it isn't a smi.
__ movq(rdi, rdx);
@@ -1006,7 +1078,7 @@
// rsp[16] argument argc - 1
// ...
// rsp[argc * 8] argument 1
- // rsp[(argc + 1) * 8] argument 0 = reciever
+ // rsp[(argc + 1) * 8] argument 0 = receiver
// rsp[(argc + 2) * 8] function name
// -----------------------------------
@@ -1065,7 +1137,7 @@
// Cache miss: Jump to runtime.
__ bind(&miss);
- Generate(masm, argc, ExternalReference(IC_Utility(kCallIC_Miss)));
+ GenerateMiss(masm, argc);
}
@@ -1196,9 +1268,9 @@
Immediate(1 << Map::kIsAccessCheckNeeded));
__ j(not_zero, &miss);
- // Search the dictionary placing the result in eax.
+ // Search the dictionary placing the result in rax.
__ bind(&probe);
- GenerateDictionaryLoad(masm, &miss, rdx, rax, rbx, rcx);
+ GenerateDictionaryLoad(masm, &miss, rdx, rax, rbx, rcx, CHECK_DICTIONARY);
GenerateCheckNonObjectOrLoaded(masm, &miss, rax);
__ ret(0);
diff --git a/src/x64/macro-assembler-x64.cc b/src/x64/macro-assembler-x64.cc
index 7115791..65a408b 100644
--- a/src/x64/macro-assembler-x64.cc
+++ b/src/x64/macro-assembler-x64.cc
@@ -310,6 +310,12 @@
}
+void MacroAssembler::TailCallStub(CodeStub* stub) {
+ ASSERT(allow_stub_calls()); // calls are not allowed in some stubs
+ Jump(stub->GetCode(), RelocInfo::CODE_TARGET);
+}
+
+
void MacroAssembler::StubReturn(int argc) {
ASSERT(argc >= 1 && generating_stub());
ret((argc - 1) * kPointerSize);
@@ -575,6 +581,17 @@
}
+Condition MacroAssembler::CheckEitherSmi(Register first, Register second) {
+ if (first.is(second)) {
+ return CheckSmi(first);
+ }
+ movl(kScratchRegister, first);
+ andl(kScratchRegister, second);
+ testb(kScratchRegister, Immediate(kSmiTagMask));
+ return zero;
+}
+
+
Condition MacroAssembler::CheckIsMinSmi(Register src) {
ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
movq(kScratchRegister, src);
@@ -1275,6 +1292,39 @@
}
+void MacroAssembler::JumpIfNotBothSequentialAsciiStrings(Register first_object,
+ Register second_object,
+ Register scratch1,
+ Register scratch2,
+ Label* on_fail) {
+ // Check that both objects are not smis.
+ Condition either_smi = CheckEitherSmi(first_object, second_object);
+ j(either_smi, on_fail);
+
+ // Load instance type for both strings.
+ movq(scratch1, FieldOperand(first_object, HeapObject::kMapOffset));
+ movq(scratch2, FieldOperand(second_object, HeapObject::kMapOffset));
+ movzxbl(scratch1, FieldOperand(scratch1, Map::kInstanceTypeOffset));
+ movzxbl(scratch2, FieldOperand(scratch2, Map::kInstanceTypeOffset));
+
+ // Check that both are flat ascii strings.
+ ASSERT(kNotStringTag != 0);
+ const int kFlatAsciiStringMask =
+ kIsNotStringMask | kStringRepresentationMask | kStringEncodingMask;
+ const int kFlatAsciiStringBits =
+ kNotStringTag | kSeqStringTag | kAsciiStringTag;
+
+ andl(scratch1, Immediate(kFlatAsciiStringMask));
+ andl(scratch2, Immediate(kFlatAsciiStringMask));
+ // Interleave the bits to check both scratch1 and scratch2 in one test.
+ ASSERT_EQ(0, kFlatAsciiStringMask & (kFlatAsciiStringMask << 3));
+ lea(scratch1, Operand(scratch1, scratch2, times_8, 0));
+ cmpl(scratch1,
+ Immediate(kFlatAsciiStringBits + (kFlatAsciiStringBits << 3)));
+ j(not_equal, on_fail);
+}
+
+
void MacroAssembler::Move(Register dst, Handle<Object> source) {
ASSERT(!source->IsFailure());
if (source->IsSmi()) {
@@ -1339,6 +1389,13 @@
}
+void MacroAssembler::Drop(int stack_elements) {
+ if (stack_elements > 0) {
+ addq(rsp, Immediate(stack_elements * kPointerSize));
+ }
+}
+
+
void MacroAssembler::Test(const Operand& src, Smi* source) {
intptr_t smi = reinterpret_cast<intptr_t>(source);
if (is_int32(smi)) {
@@ -1425,6 +1482,16 @@
}
+void MacroAssembler::PopTryHandler() {
+ ASSERT_EQ(0, StackHandlerConstants::kNextOffset);
+ // Unlink this handler.
+ movq(kScratchRegister, ExternalReference(Top::k_handler_address));
+ pop(Operand(kScratchRegister, 0));
+ // Remove the remaining fields.
+ addq(rsp, Immediate(StackHandlerConstants::kSize - kPointerSize));
+}
+
+
void MacroAssembler::Ret() {
ret(0);
}
@@ -2244,12 +2311,114 @@
}
+void MacroAssembler::AllocateTwoByteString(Register result,
+ Register length,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Label* gc_required) {
+ // Calculate the number of bytes needed for the characters in the string while
+ // observing object alignment.
+ ASSERT((SeqTwoByteString::kHeaderSize & kObjectAlignmentMask) == 0);
+ ASSERT(kShortSize == 2);
+ // scratch1 = length * 2 + kObjectAlignmentMask.
+ lea(scratch1, Operand(length, length, times_1, kObjectAlignmentMask));
+ and_(scratch1, Immediate(~kObjectAlignmentMask));
+
+ // Allocate two byte string in new space.
+ AllocateInNewSpace(SeqTwoByteString::kHeaderSize,
+ times_1,
+ scratch1,
+ result,
+ scratch2,
+ scratch3,
+ gc_required,
+ TAG_OBJECT);
+
+ // Set the map, length and hash field.
+ LoadRoot(kScratchRegister, Heap::kStringMapRootIndex);
+ movq(FieldOperand(result, HeapObject::kMapOffset), kScratchRegister);
+ movl(FieldOperand(result, String::kLengthOffset), length);
+ movl(FieldOperand(result, String::kHashFieldOffset),
+ Immediate(String::kEmptyHashField));
+}
+
+
+void MacroAssembler::AllocateAsciiString(Register result,
+ Register length,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Label* gc_required) {
+ // Calculate the number of bytes needed for the characters in the string while
+ // observing object alignment.
+ ASSERT((SeqAsciiString::kHeaderSize & kObjectAlignmentMask) == 0);
+ movl(scratch1, length);
+ ASSERT(kCharSize == 1);
+ addq(scratch1, Immediate(kObjectAlignmentMask));
+ and_(scratch1, Immediate(~kObjectAlignmentMask));
+
+ // Allocate ascii string in new space.
+ AllocateInNewSpace(SeqAsciiString::kHeaderSize,
+ times_1,
+ scratch1,
+ result,
+ scratch2,
+ scratch3,
+ gc_required,
+ TAG_OBJECT);
+
+ // Set the map, length and hash field.
+ LoadRoot(kScratchRegister, Heap::kAsciiStringMapRootIndex);
+ movq(FieldOperand(result, HeapObject::kMapOffset), kScratchRegister);
+ movl(FieldOperand(result, String::kLengthOffset), length);
+ movl(FieldOperand(result, String::kHashFieldOffset),
+ Immediate(String::kEmptyHashField));
+}
+
+
+void MacroAssembler::AllocateConsString(Register result,
+ Register scratch1,
+ Register scratch2,
+ Label* gc_required) {
+ // Allocate heap number in new space.
+ AllocateInNewSpace(ConsString::kSize,
+ result,
+ scratch1,
+ scratch2,
+ gc_required,
+ TAG_OBJECT);
+
+ // Set the map. The other fields are left uninitialized.
+ LoadRoot(kScratchRegister, Heap::kConsStringMapRootIndex);
+ movq(FieldOperand(result, HeapObject::kMapOffset), kScratchRegister);
+}
+
+
+void MacroAssembler::AllocateAsciiConsString(Register result,
+ Register scratch1,
+ Register scratch2,
+ Label* gc_required) {
+ // Allocate heap number in new space.
+ AllocateInNewSpace(ConsString::kSize,
+ result,
+ scratch1,
+ scratch2,
+ gc_required,
+ TAG_OBJECT);
+
+ // Set the map. The other fields are left uninitialized.
+ LoadRoot(kScratchRegister, Heap::kConsAsciiStringMapRootIndex);
+ movq(FieldOperand(result, HeapObject::kMapOffset), kScratchRegister);
+}
+
+
void MacroAssembler::LoadContext(Register dst, int context_chain_length) {
if (context_chain_length > 0) {
// Move up the chain of contexts to the context containing the slot.
movq(dst, Operand(rsi, Context::SlotOffset(Context::CLOSURE_INDEX)));
// Load the function context (which is the incoming, outer context).
- movq(rax, FieldOperand(rax, JSFunction::kContextOffset));
+ movq(dst, FieldOperand(dst, JSFunction::kContextOffset));
for (int i = 1; i < context_chain_length; i++) {
movq(dst, Operand(dst, Context::SlotOffset(Context::CLOSURE_INDEX)));
movq(dst, FieldOperand(dst, JSFunction::kContextOffset));
diff --git a/src/x64/macro-assembler-x64.h b/src/x64/macro-assembler-x64.h
index 9e7c25c..ce2848c 100644
--- a/src/x64/macro-assembler-x64.h
+++ b/src/x64/macro-assembler-x64.h
@@ -38,6 +38,9 @@
// function calling convention.
static const Register kScratchRegister = r10;
+// Convenience for platform-independent signatures.
+typedef Operand MemOperand;
+
// Forward declaration.
class JumpTarget;
@@ -201,9 +204,12 @@
// Is the value a positive tagged smi.
Condition CheckPositiveSmi(Register src);
- // Are both values are tagged smis.
+ // Are both values tagged smis.
Condition CheckBothSmi(Register first, Register second);
+ // Are either value a tagged smi.
+ Condition CheckEitherSmi(Register first, Register second);
+
// Is the value the minimum smi value (since we are using
// two's complement numbers, negating the value is known to yield
// a non-smi value).
@@ -400,7 +406,15 @@
void Test(const Operand& dst, Smi* source);
// ---------------------------------------------------------------------------
- // Macro instructions
+ // String macros.
+ void JumpIfNotBothSequentialAsciiStrings(Register first_object,
+ Register second_object,
+ Register scratch1,
+ Register scratch2,
+ Label* on_not_both_flat_ascii);
+
+ // ---------------------------------------------------------------------------
+ // Macro instructions.
// Load a register with a long value as efficiently as possible.
void Set(Register dst, int64_t x);
@@ -413,6 +427,12 @@
void Cmp(const Operand& dst, Handle<Object> source);
void Push(Handle<Object> source);
+ // Emit code to discard a non-negative number of pointer-sized elements
+ // from the stack, clobbering only the rsp register.
+ void Drop(int stack_elements);
+
+ void Call(Label* target) { call(target); }
+
// Control Flow
void Jump(Address destination, RelocInfo::Mode rmode);
void Jump(ExternalReference ext);
@@ -443,6 +463,8 @@
// address must be pushed before calling this helper.
void PushTryHandler(CodeLocation try_location, HandlerType type);
+ // Unlink the stack handler on top of the stack from the try handler chain.
+ void PopTryHandler();
// ---------------------------------------------------------------------------
// Inline caching support
@@ -518,6 +540,32 @@
Register scratch,
Label* gc_required);
+ // Allocate a sequential string. All the header fields of the string object
+ // are initialized.
+ void AllocateTwoByteString(Register result,
+ Register length,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Label* gc_required);
+ void AllocateAsciiString(Register result,
+ Register length,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Label* gc_required);
+
+ // Allocate a raw cons string object. Only the map field of the result is
+ // initialized.
+ void AllocateConsString(Register result,
+ Register scratch1,
+ Register scratch2,
+ Label* gc_required);
+ void AllocateAsciiConsString(Register result,
+ Register scratch1,
+ Register scratch2,
+ Label* gc_required);
+
// ---------------------------------------------------------------------------
// Support functions.
@@ -557,6 +605,9 @@
// Call a code stub.
void CallStub(CodeStub* stub);
+ // Tail call a code stub (jump).
+ void TailCallStub(CodeStub* stub);
+
// Return from a code stub after popping its arguments.
void StubReturn(int argc);
diff --git a/src/x64/regexp-macro-assembler-x64.cc b/src/x64/regexp-macro-assembler-x64.cc
index 639f5e9..75bbf3e 100644
--- a/src/x64/regexp-macro-assembler-x64.cc
+++ b/src/x64/regexp-macro-assembler-x64.cc
@@ -60,20 +60,24 @@
* - r8 : code object pointer. Used to convert between absolute and
* code-object-relative addresses.
*
- * The registers rax, rbx, rcx, r9 and r11 are free to use for computations.
+ * The registers rax, rbx, r9 and r11 are free to use for computations.
* If changed to use r12+, they should be saved as callee-save registers.
*
* Each call to a C++ method should retain these registers.
*
* The stack will have the following content, in some order, indexable from the
* frame pointer (see, e.g., kStackHighEnd):
- * - stack_area_base (High end of the memory area to use as
- * backtracking stack)
- * - at_start (if 1, start at start of string, if 0, don't)
- * - int* capture_array (int[num_saved_registers_], for output).
- * - end of input (Address of end of string)
- * - start of input (Address of first character in string)
- * - String** input_string (location of a handle containing the string)
+ * - direct_call (if 1, direct call from JavaScript code, if 0 call
+ * through the runtime system)
+ * - stack_area_base (High end of the memory area to use as
+ * backtracking stack)
+ * - at_start (if 1, we are starting at the start of the
+ * string, otherwise 0)
+ * - int* capture_array (int[num_saved_registers_], for output).
+ * - end of input (Address of end of string)
+ * - start of input (Address of first character in string)
+ * - start index (character index of start)
+ * - String* input_string (input string)
* - return address
* - backup of callee save registers (rbx, possibly rsi and rdi).
* - Offset of location before start of input (effectively character
@@ -90,11 +94,13 @@
* calling the code's entry address cast to a function pointer with the
* following signature:
* int (*match)(String* input_string,
+ * int start_index,
* Address start,
* Address end,
* int* capture_output_array,
* bool at_start,
- * byte* stack_area_base)
+ * byte* stack_area_base,
+ * bool direct_call)
*/
#define __ ACCESS_MASM(masm_)
@@ -490,27 +496,22 @@
bool RegExpMacroAssemblerX64::CheckSpecialCharacterClass(uc16 type,
- int cp_offset,
- bool check_offset,
Label* on_no_match) {
// Range checks (c in min..max) are generally implemented by an unsigned
- // (c - min) <= (max - min) check
+ // (c - min) <= (max - min) check, using the sequence:
+ // lea(rax, Operand(current_character(), -min)) or sub(rax, Immediate(min))
+ // cmp(rax, Immediate(max - min))
switch (type) {
case 's':
// Match space-characters
if (mode_ == ASCII) {
// ASCII space characters are '\t'..'\r' and ' '.
- if (check_offset) {
- LoadCurrentCharacter(cp_offset, on_no_match);
- } else {
- LoadCurrentCharacterUnchecked(cp_offset, 1);
- }
Label success;
__ cmpl(current_character(), Immediate(' '));
__ j(equal, &success);
// Check range 0x09..0x0d
- __ subl(current_character(), Immediate('\t'));
- __ cmpl(current_character(), Immediate('\r' - '\t'));
+ __ lea(rax, Operand(current_character(), -'\t'));
+ __ cmpl(rax, Immediate('\r' - '\t'));
BranchOrBacktrack(above, on_no_match);
__ bind(&success);
return true;
@@ -518,72 +519,105 @@
return false;
case 'S':
// Match non-space characters.
- if (check_offset) {
- LoadCurrentCharacter(cp_offset, on_no_match, 1);
- } else {
- LoadCurrentCharacterUnchecked(cp_offset, 1);
- }
if (mode_ == ASCII) {
// ASCII space characters are '\t'..'\r' and ' '.
__ cmpl(current_character(), Immediate(' '));
BranchOrBacktrack(equal, on_no_match);
- __ subl(current_character(), Immediate('\t'));
- __ cmpl(current_character(), Immediate('\r' - '\t'));
+ __ lea(rax, Operand(current_character(), -'\t'));
+ __ cmpl(rax, Immediate('\r' - '\t'));
BranchOrBacktrack(below_equal, on_no_match);
return true;
}
return false;
case 'd':
// Match ASCII digits ('0'..'9')
- if (check_offset) {
- LoadCurrentCharacter(cp_offset, on_no_match, 1);
- } else {
- LoadCurrentCharacterUnchecked(cp_offset, 1);
- }
- __ subl(current_character(), Immediate('0'));
- __ cmpl(current_character(), Immediate('9' - '0'));
+ __ lea(rax, Operand(current_character(), -'0'));
+ __ cmpl(rax, Immediate('9' - '0'));
BranchOrBacktrack(above, on_no_match);
return true;
case 'D':
// Match non ASCII-digits
- if (check_offset) {
- LoadCurrentCharacter(cp_offset, on_no_match, 1);
- } else {
- LoadCurrentCharacterUnchecked(cp_offset, 1);
- }
- __ subl(current_character(), Immediate('0'));
- __ cmpl(current_character(), Immediate('9' - '0'));
+ __ lea(rax, Operand(current_character(), -'0'));
+ __ cmpl(rax, Immediate('9' - '0'));
BranchOrBacktrack(below_equal, on_no_match);
return true;
case '.': {
// Match non-newlines (not 0x0a('\n'), 0x0d('\r'), 0x2028 and 0x2029)
- if (check_offset) {
- LoadCurrentCharacter(cp_offset, on_no_match, 1);
- } else {
- LoadCurrentCharacterUnchecked(cp_offset, 1);
- }
- __ xor_(current_character(), Immediate(0x01));
+ __ movl(rax, current_character());
+ __ xor_(rax, Immediate(0x01));
// See if current character is '\n'^1 or '\r'^1, i.e., 0x0b or 0x0c
- __ subl(current_character(), Immediate(0x0b));
- __ cmpl(current_character(), Immediate(0x0c - 0x0b));
+ __ subl(rax, Immediate(0x0b));
+ __ cmpl(rax, Immediate(0x0c - 0x0b));
BranchOrBacktrack(below_equal, on_no_match);
if (mode_ == UC16) {
// Compare original value to 0x2028 and 0x2029, using the already
// computed (current_char ^ 0x01 - 0x0b). I.e., check for
// 0x201d (0x2028 - 0x0b) or 0x201e.
- __ subl(current_character(), Immediate(0x2028 - 0x0b));
- __ cmpl(current_character(), Immediate(1));
+ __ subl(rax, Immediate(0x2028 - 0x0b));
+ __ cmpl(rax, Immediate(0x2029 - 0x2028));
BranchOrBacktrack(below_equal, on_no_match);
}
return true;
}
- case '*':
- // Match any character.
- if (check_offset) {
- CheckPosition(cp_offset, on_no_match);
+ case 'n': {
+ // Match newlines (0x0a('\n'), 0x0d('\r'), 0x2028 and 0x2029)
+ __ movl(rax, current_character());
+ __ xor_(rax, Immediate(0x01));
+ // See if current character is '\n'^1 or '\r'^1, i.e., 0x0b or 0x0c
+ __ subl(rax, Immediate(0x0b));
+ __ cmpl(rax, Immediate(0x0c - 0x0b));
+ if (mode_ == ASCII) {
+ BranchOrBacktrack(above, on_no_match);
+ } else {
+ Label done;
+ BranchOrBacktrack(below_equal, &done);
+ // Compare original value to 0x2028 and 0x2029, using the already
+ // computed (current_char ^ 0x01 - 0x0b). I.e., check for
+ // 0x201d (0x2028 - 0x0b) or 0x201e.
+ __ subl(rax, Immediate(0x2028 - 0x0b));
+ __ cmpl(rax, Immediate(0x2029 - 0x2028));
+ BranchOrBacktrack(above, on_no_match);
+ __ bind(&done);
}
return true;
- // No custom implementation (yet): w, W, s(UC16), S(UC16).
+ }
+ case 'w': {
+ if (mode_ != ASCII) {
+ // Table is 128 entries, so all ASCII characters can be tested.
+ __ cmpl(current_character(), Immediate('z'));
+ BranchOrBacktrack(above, on_no_match);
+ }
+ __ movq(rbx, ExternalReference::re_word_character_map());
+ ASSERT_EQ(0, word_character_map[0]); // Character '\0' is not a word char.
+ ExternalReference word_map = ExternalReference::re_word_character_map();
+ __ testb(Operand(rbx, current_character(), times_1, 0),
+ current_character());
+ BranchOrBacktrack(zero, on_no_match);
+ return true;
+ }
+ case 'W': {
+ Label done;
+ if (mode_ != ASCII) {
+ // Table is 128 entries, so all ASCII characters can be tested.
+ __ cmpl(current_character(), Immediate('z'));
+ __ j(above, &done);
+ }
+ __ movq(rbx, ExternalReference::re_word_character_map());
+ ASSERT_EQ(0, word_character_map[0]); // Character '\0' is not a word char.
+ ExternalReference word_map = ExternalReference::re_word_character_map();
+ __ testb(Operand(rbx, current_character(), times_1, 0),
+ current_character());
+ BranchOrBacktrack(not_zero, on_no_match);
+ if (mode_ != ASCII) {
+ __ bind(&done);
+ }
+ return true;
+ }
+
+ case '*':
+ // Match any character.
+ return true;
+ // No custom implementation (yet): s(UC16), S(UC16).
default:
return false;
}
diff --git a/src/x64/regexp-macro-assembler-x64.h b/src/x64/regexp-macro-assembler-x64.h
index 3e6720d..694cba0 100644
--- a/src/x64/regexp-macro-assembler-x64.h
+++ b/src/x64/regexp-macro-assembler-x64.h
@@ -73,8 +73,6 @@
// the end of the string.
virtual void CheckPosition(int cp_offset, Label* on_outside_input);
virtual bool CheckSpecialCharacterClass(uc16 type,
- int cp_offset,
- bool check_offset,
Label* on_no_match);
virtual void Fail();
virtual Handle<Object> GetCode(Handle<String> source);
@@ -143,6 +141,8 @@
// AtStart is passed as 32 bit int (values 0 or 1).
static const int kAtStart = kRegisterOutput + kPointerSize;
static const int kStackHighEnd = kAtStart + kPointerSize;
+ // DirectCall is passed as 32 bit int (values 0 or 1).
+ static const int kDirectCall = kStackHighEnd + kPointerSize;
#else
// In AMD64 ABI Calling Convention, the first six integer parameters
// are passed as registers, and caller must allocate space on the stack
@@ -154,6 +154,7 @@
static const int kRegisterOutput = kInputEnd - kPointerSize;
static const int kAtStart = kRegisterOutput - kPointerSize;
static const int kStackHighEnd = kFrameAlign;
+ static const int kDirectCall = kStackHighEnd + kPointerSize;
#endif
#ifdef _WIN64
diff --git a/src/x64/simulator-x64.h b/src/x64/simulator-x64.h
index c4f3a85..015ba13 100644
--- a/src/x64/simulator-x64.h
+++ b/src/x64/simulator-x64.h
@@ -53,9 +53,9 @@
};
// Call the generated regexp code directly. The entry function pointer should
-// expect seven int/pointer sized arguments and return an int.
-#define CALL_GENERATED_REGEXP_CODE(entry, p0, p1, p2, p3, p4, p5, p6) \
- entry(p0, p1, p2, p3, p4, p5, p6)
+// expect eight int/pointer sized arguments and return an int.
+#define CALL_GENERATED_REGEXP_CODE(entry, p0, p1, p2, p3, p4, p5, p6, p7) \
+ entry(p0, p1, p2, p3, p4, p5, p6, p7)
#define TRY_CATCH_FROM_ADDRESS(try_catch_address) \
reinterpret_cast<TryCatch*>(try_catch_address)
diff --git a/src/x64/stub-cache-x64.cc b/src/x64/stub-cache-x64.cc
index 55b0b87..8d600a5 100644
--- a/src/x64/stub-cache-x64.cc
+++ b/src/x64/stub-cache-x64.cc
@@ -688,7 +688,7 @@
// rsp[16] argument argc - 1
// ...
// rsp[argc * 8] argument 1
- // rsp[(argc + 1) * 8] argument 0 = reciever
+ // rsp[(argc + 1) * 8] argument 0 = receiver
// rsp[(argc + 2) * 8] function name
Label miss;
@@ -721,47 +721,62 @@
break;
case STRING_CHECK:
- // Check that the object is a two-byte string or a symbol.
- __ CmpObjectType(rdx, FIRST_NONSTRING_TYPE, rcx);
- __ j(above_equal, &miss);
- // Check that the maps starting from the prototype haven't changed.
- GenerateLoadGlobalFunctionPrototype(masm(),
- Context::STRING_FUNCTION_INDEX,
- rcx);
- CheckPrototypes(JSObject::cast(object->GetPrototype()), rcx, holder,
- rbx, rdx, name, &miss);
+ if (!function->IsBuiltin()) {
+ // Calling non-builtins with a value as receiver requires boxing.
+ __ jmp(&miss);
+ } else {
+ // Check that the object is a two-byte string or a symbol.
+ __ CmpObjectType(rdx, FIRST_NONSTRING_TYPE, rcx);
+ __ j(above_equal, &miss);
+ // Check that the maps starting from the prototype haven't changed.
+ GenerateLoadGlobalFunctionPrototype(masm(),
+ Context::STRING_FUNCTION_INDEX,
+ rcx);
+ CheckPrototypes(JSObject::cast(object->GetPrototype()), rcx, holder,
+ rbx, rdx, name, &miss);
+ }
break;
case NUMBER_CHECK: {
- Label fast;
- // Check that the object is a smi or a heap number.
- __ JumpIfSmi(rdx, &fast);
- __ CmpObjectType(rdx, HEAP_NUMBER_TYPE, rcx);
- __ j(not_equal, &miss);
- __ bind(&fast);
- // Check that the maps starting from the prototype haven't changed.
- GenerateLoadGlobalFunctionPrototype(masm(),
- Context::NUMBER_FUNCTION_INDEX,
- rcx);
- CheckPrototypes(JSObject::cast(object->GetPrototype()), rcx, holder,
- rbx, rdx, name, &miss);
+ if (!function->IsBuiltin()) {
+ // Calling non-builtins with a value as receiver requires boxing.
+ __ jmp(&miss);
+ } else {
+ Label fast;
+ // Check that the object is a smi or a heap number.
+ __ JumpIfSmi(rdx, &fast);
+ __ CmpObjectType(rdx, HEAP_NUMBER_TYPE, rcx);
+ __ j(not_equal, &miss);
+ __ bind(&fast);
+ // Check that the maps starting from the prototype haven't changed.
+ GenerateLoadGlobalFunctionPrototype(masm(),
+ Context::NUMBER_FUNCTION_INDEX,
+ rcx);
+ CheckPrototypes(JSObject::cast(object->GetPrototype()), rcx, holder,
+ rbx, rdx, name, &miss);
+ }
break;
}
case BOOLEAN_CHECK: {
- Label fast;
- // Check that the object is a boolean.
- __ CompareRoot(rdx, Heap::kTrueValueRootIndex);
- __ j(equal, &fast);
- __ CompareRoot(rdx, Heap::kFalseValueRootIndex);
- __ j(not_equal, &miss);
- __ bind(&fast);
- // Check that the maps starting from the prototype haven't changed.
- GenerateLoadGlobalFunctionPrototype(masm(),
- Context::BOOLEAN_FUNCTION_INDEX,
- rcx);
- CheckPrototypes(JSObject::cast(object->GetPrototype()), rcx, holder,
- rbx, rdx, name, &miss);
+ if (!function->IsBuiltin()) {
+ // Calling non-builtins with a value as receiver requires boxing.
+ __ jmp(&miss);
+ } else {
+ Label fast;
+ // Check that the object is a boolean.
+ __ CompareRoot(rdx, Heap::kTrueValueRootIndex);
+ __ j(equal, &fast);
+ __ CompareRoot(rdx, Heap::kFalseValueRootIndex);
+ __ j(not_equal, &miss);
+ __ bind(&fast);
+ // Check that the maps starting from the prototype haven't changed.
+ GenerateLoadGlobalFunctionPrototype(masm(),
+ Context::BOOLEAN_FUNCTION_INDEX,
+ rcx);
+ CheckPrototypes(JSObject::cast(object->GetPrototype()), rcx, holder,
+ rbx, rdx, name, &miss);
+ }
break;
}
@@ -956,8 +971,24 @@
__ movq(rdi, FieldOperand(rdi, JSGlobalPropertyCell::kValueOffset));
// Check that the cell contains the same function.
- __ Cmp(rdi, Handle<JSFunction>(function));
- __ j(not_equal, &miss);
+ if (Heap::InNewSpace(function)) {
+ // We can't embed a pointer to a function in new space so we have
+ // to verify that the shared function info is unchanged. This has
+ // the nice side effect that multiple closures based on the same
+ // function can all use this call IC. Before we load through the
+ // function, we have to verify that it still is a function.
+ __ JumpIfSmi(rdi, &miss);
+ __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rcx);
+ __ j(not_equal, &miss);
+
+ // Check the shared function info. Make sure it hasn't changed.
+ __ Move(rcx, Handle<SharedFunctionInfo>(function->shared()));
+ __ cmpq(FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset), rcx);
+ __ j(not_equal, &miss);
+ } else {
+ __ Cmp(rdi, Handle<JSFunction>(function));
+ __ j(not_equal, &miss);
+ }
// Patch the receiver on the stack with the global proxy.
if (object->IsGlobalObject()) {
@@ -987,10 +1018,10 @@
}
-Object* LoadStubCompiler::CompileLoadCallback(JSObject* object,
+Object* LoadStubCompiler::CompileLoadCallback(String* name,
+ JSObject* object,
JSObject* holder,
- AccessorInfo* callback,
- String* name) {
+ AccessorInfo* callback) {
// ----------- S t a t e -------------
// -- rcx : name
// -- rsp[0] : return address
@@ -999,8 +1030,11 @@
Label miss;
__ movq(rax, Operand(rsp, kPointerSize));
- GenerateLoadCallback(object, holder, rax, rcx, rbx, rdx,
- callback, name, &miss);
+ Failure* failure = Failure::InternalError();
+ bool success = GenerateLoadCallback(object, holder, rax, rcx, rbx, rdx,
+ callback, name, &miss, &failure);
+ if (!success) return failure;
+
__ bind(&miss);
GenerateLoadMiss(masm(), Code::LOAD_IC);
@@ -1154,8 +1188,11 @@
__ Cmp(rax, Handle<String>(name));
__ j(not_equal, &miss);
- GenerateLoadCallback(receiver, holder, rcx, rax, rbx, rdx,
- callback, name, &miss);
+ Failure* failure = Failure::InternalError();
+ bool success = GenerateLoadCallback(receiver, holder, rcx, rax, rbx, rdx,
+ callback, name, &miss, &failure);
+ if (!success) return failure;
+
__ bind(&miss);
__ DecrementCounter(&Counters::keyed_load_callback, 1);
GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC);
@@ -1610,7 +1647,7 @@
}
-void StubCompiler::GenerateLoadCallback(JSObject* object,
+bool StubCompiler::GenerateLoadCallback(JSObject* object,
JSObject* holder,
Register receiver,
Register name_reg,
@@ -1618,7 +1655,8 @@
Register scratch2,
AccessorInfo* callback,
String* name,
- Label* miss) {
+ Label* miss,
+ Failure** failure) {
// Check that the receiver isn't a smi.
__ JumpIfSmi(receiver, miss);
@@ -1641,6 +1679,8 @@
ExternalReference load_callback_property =
ExternalReference(IC_Utility(IC::kLoadCallbackProperty));
__ TailCallRuntime(load_callback_property, 5, 1);
+
+ return true;
}
@@ -1802,17 +1842,15 @@
// depending on the this.x = ...; assignment in the function.
for (int i = 0; i < shared->this_property_assignments_count(); i++) {
if (shared->IsThisPropertyAssignmentArgument(i)) {
- Label not_passed;
- // Set the property to undefined.
- __ movq(Operand(r9, i * kPointerSize), r8);
// Check if the argument assigned to the property is actually passed.
+ // If argument is not passed the property is set to undefined,
+ // otherwise find it on the stack.
int arg_number = shared->GetThisPropertyAssignmentArgument(i);
+ __ movq(rbx, r8);
__ cmpq(rax, Immediate(arg_number));
- __ j(below_equal, ¬_passed);
- // Argument passed - find it on the stack.
- __ movq(rbx, Operand(rcx, arg_number * -kPointerSize));
+ __ cmovq(above, rbx, Operand(rcx, arg_number * -kPointerSize));
+ // Store value in the property.
__ movq(Operand(r9, i * kPointerSize), rbx);
- __ bind(¬_passed);
} else {
// Set the property to the constant value.
Handle<Object> constant(shared->GetThisPropertyAssignmentConstant(i));
diff --git a/src/x64/virtual-frame-x64.cc b/src/x64/virtual-frame-x64.cc
index fe65d34..6e84ed1 100644
--- a/src/x64/virtual-frame-x64.cc
+++ b/src/x64/virtual-frame-x64.cc
@@ -129,11 +129,29 @@
Handle<Object> undefined = Factory::undefined_value();
FrameElement initial_value =
FrameElement::ConstantElement(undefined, FrameElement::SYNCED);
- __ movq(kScratchRegister, undefined, RelocInfo::EMBEDDED_OBJECT);
+ if (count == 1) {
+ __ Push(undefined);
+ } else if (count < kLocalVarBound) {
+ // For less locals the unrolled loop is more compact.
+ __ movq(kScratchRegister, undefined, RelocInfo::EMBEDDED_OBJECT);
+ for (int i = 0; i < count; i++) {
+ __ push(kScratchRegister);
+ }
+ } else {
+ // For more locals a loop in generated code is more compact.
+ Label alloc_locals_loop;
+ Result cnt = cgen()->allocator()->Allocate();
+ ASSERT(cnt.is_valid());
+ __ movq(cnt.reg(), Immediate(count));
+ __ movq(kScratchRegister, undefined, RelocInfo::EMBEDDED_OBJECT);
+ __ bind(&alloc_locals_loop);
+ __ push(kScratchRegister);
+ __ decl(cnt.reg());
+ __ j(not_zero, &alloc_locals_loop);
+ }
for (int i = 0; i < count; i++) {
elements_.Add(initial_value);
stack_pointer_++;
- __ push(kScratchRegister);
}
}
}
diff --git a/src/x64/virtual-frame-x64.h b/src/x64/virtual-frame-x64.h
index e492305..8e3e40f 100644
--- a/src/x64/virtual-frame-x64.h
+++ b/src/x64/virtual-frame-x64.h
@@ -200,6 +200,9 @@
// shared return site. Emits code for spills.
void PrepareForReturn();
+ // Number of local variables after when we use a loop for allocating.
+ static const int kLocalVarBound = 7;
+
// Allocate and initialize the frame-allocated locals.
void AllocateStackSlots();
@@ -340,7 +343,7 @@
// of the frame. Key and receiver are not dropped.
Result CallKeyedStoreIC();
- // Call call IC. Arguments, reciever, and function name are found
+ // Call call IC. Arguments, receiver, and function name are found
// on top of the frame. Function name slot is not dropped. The
// argument count does not include the receiver.
Result CallCallIC(RelocInfo::Mode mode, int arg_count, int loop_nesting);