Revert "Revert "Upgrade to 5.0.71.48"" DO NOT MERGE
This reverts commit f2e3994fa5148cc3d9946666f0b0596290192b0e,
and updates the x64 makefile properly so it doesn't break that
build.
FPIIM-449
Change-Id: Ib83e35bfbae6af627451c926a9650ec57c045605
(cherry picked from commit 109988c7ccb6f3fd1a58574fa3dfb88beaef6632)
diff --git a/src/x64/builtins-x64.cc b/src/x64/builtins-x64.cc
index cb092f2..6c4419e 100644
--- a/src/x64/builtins-x64.cc
+++ b/src/x64/builtins-x64.cc
@@ -60,27 +60,6 @@
}
-static void CallRuntimePassFunction(
- MacroAssembler* masm, Runtime::FunctionId function_id) {
- // ----------- S t a t e -------------
- // -- rdx : new target (preserved for callee)
- // -- rdi : target function (preserved for callee)
- // -----------------------------------
-
- FrameScope scope(masm, StackFrame::INTERNAL);
- // Push a copy of the target function and the new target.
- __ Push(rdi);
- __ Push(rdx);
- // Function is also the parameter to the runtime call.
- __ Push(rdi);
-
- __ CallRuntime(function_id, 1);
- // Restore target function and new target.
- __ Pop(rdx);
- __ Pop(rdi);
-}
-
-
static void GenerateTailCallToSharedCode(MacroAssembler* masm) {
__ movp(kScratchRegister,
FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset));
@@ -90,10 +69,35 @@
__ jmp(kScratchRegister);
}
+static void GenerateTailCallToReturnedCode(MacroAssembler* masm,
+ Runtime::FunctionId function_id) {
+ // ----------- S t a t e -------------
+ // -- rax : argument count (preserved for callee)
+ // -- rdx : new target (preserved for callee)
+ // -- rdi : target function (preserved for callee)
+ // -----------------------------------
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+ // Push the number of arguments to the callee.
+ __ Integer32ToSmi(rax, rax);
+ __ Push(rax);
+ // Push a copy of the target function and the new target.
+ __ Push(rdi);
+ __ Push(rdx);
+ // Function is also the parameter to the runtime call.
+ __ Push(rdi);
-static void GenerateTailCallToReturnedCode(MacroAssembler* masm) {
- __ leap(rax, FieldOperand(rax, Code::kHeaderSize));
- __ jmp(rax);
+ __ CallRuntime(function_id, 1);
+ __ movp(rbx, rax);
+
+ // Restore target function and new target.
+ __ Pop(rdx);
+ __ Pop(rdi);
+ __ Pop(rax);
+ __ SmiToInteger32(rax, rax);
+ }
+ __ leap(rbx, FieldOperand(rbx, Code::kHeaderSize));
+ __ jmp(rbx);
}
@@ -107,8 +111,7 @@
__ CompareRoot(rsp, Heap::kStackLimitRootIndex);
__ j(above_equal, &ok);
- CallRuntimePassFunction(masm, Runtime::kTryInstallOptimizedCode);
- GenerateTailCallToReturnedCode(masm);
+ GenerateTailCallToReturnedCode(masm, Runtime::kTryInstallOptimizedCode);
__ bind(&ok);
GenerateTailCallToSharedCode(masm);
@@ -117,7 +120,8 @@
static void Generate_JSConstructStubHelper(MacroAssembler* masm,
bool is_api_function,
- bool create_implicit_receiver) {
+ bool create_implicit_receiver,
+ bool check_derived_construct) {
// ----------- S t a t e -------------
// -- rax: number of arguments
// -- rdi: constructor function
@@ -136,152 +140,23 @@
__ Push(rcx);
if (create_implicit_receiver) {
- // Try to allocate the object without transitioning into C code. If any of
- // the preconditions is not met, the code bails out to the runtime call.
- Label rt_call, allocated;
- if (FLAG_inline_new) {
- // Verify that the new target is a JSFunction.
- __ CmpObjectType(rdx, JS_FUNCTION_TYPE, rbx);
- __ j(not_equal, &rt_call);
-
- // Load the initial map and verify that it is in fact a map.
- // rdx: new target
- __ movp(rax,
- FieldOperand(rdx, JSFunction::kPrototypeOrInitialMapOffset));
- // Will both indicate a NULL and a Smi
- DCHECK(kSmiTag == 0);
- __ JumpIfSmi(rax, &rt_call);
- // rdi: constructor
- // rax: initial map (if proven valid below)
- __ CmpObjectType(rax, MAP_TYPE, rbx);
- __ j(not_equal, &rt_call);
-
- // Fall back to runtime if the expected base constructor and base
- // constructor differ.
- __ cmpp(rdi, FieldOperand(rax, Map::kConstructorOrBackPointerOffset));
- __ j(not_equal, &rt_call);
-
- // Now allocate the JSObject on the heap.
- __ movzxbp(r9, FieldOperand(rax, Map::kInstanceSizeOffset));
- __ shlp(r9, Immediate(kPointerSizeLog2));
- // r9: size of new object
- __ Allocate(r9, rbx, r9, no_reg, &rt_call, NO_ALLOCATION_FLAGS);
- // Allocated the JSObject, now initialize the fields.
- // rdi: constructor
- // rdx: new target
- // rax: initial map
- // rbx: JSObject (not HeapObject tagged - the actual address).
- // r9: start of next object
- __ movp(Operand(rbx, JSObject::kMapOffset), rax);
- __ LoadRoot(rcx, Heap::kEmptyFixedArrayRootIndex);
- __ movp(Operand(rbx, JSObject::kPropertiesOffset), rcx);
- __ movp(Operand(rbx, JSObject::kElementsOffset), rcx);
- __ leap(rcx, Operand(rbx, JSObject::kHeaderSize));
-
- // Add the object tag to make the JSObject real, so that we can continue
- // and jump into the continuation code at any time from now on.
- __ orp(rbx, Immediate(kHeapObjectTag));
-
- // Fill all the in-object properties with the appropriate filler.
- // rbx: JSObject (tagged)
- // rcx: First in-object property of JSObject (not tagged)
- __ LoadRoot(r11, Heap::kUndefinedValueRootIndex);
-
- if (!is_api_function) {
- Label no_inobject_slack_tracking;
-
- // The code below relies on these assumptions.
- STATIC_ASSERT(Map::kNoSlackTracking == 0);
- STATIC_ASSERT(Map::ConstructionCounter::kNext == 32);
- // Check if slack tracking is enabled.
- __ movl(rsi, FieldOperand(rax, Map::kBitField3Offset));
- __ shrl(rsi, Immediate(Map::ConstructionCounter::kShift));
- __ j(zero, &no_inobject_slack_tracking); // Map::kNoSlackTracking
- __ Push(rsi); // Save allocation count value.
- // Decrease generous allocation count.
- __ subl(FieldOperand(rax, Map::kBitField3Offset),
- Immediate(1 << Map::ConstructionCounter::kShift));
-
- // Allocate object with a slack.
- __ movzxbp(rsi, FieldOperand(rax, Map::kUnusedPropertyFieldsOffset));
- __ negp(rsi);
- __ leap(rsi, Operand(r9, rsi, times_pointer_size, 0));
- // rsi: offset of first field after pre-allocated fields
- if (FLAG_debug_code) {
- __ cmpp(rcx, rsi);
- __ Assert(less_equal,
- kUnexpectedNumberOfPreAllocatedPropertyFields);
- }
- __ InitializeFieldsWithFiller(rcx, rsi, r11);
-
- // To allow truncation fill the remaining fields with one pointer
- // filler map.
- __ LoadRoot(r11, Heap::kOnePointerFillerMapRootIndex);
- __ InitializeFieldsWithFiller(rcx, r9, r11);
-
- __ Pop(rsi); // Restore allocation count value before decreasing.
- __ cmpl(rsi, Immediate(Map::kSlackTrackingCounterEnd));
- __ j(not_equal, &allocated);
-
- // Push the constructor, new_target and the object to the stack,
- // and then the initial map as an argument to the runtime call.
- __ Push(rdi);
- __ Push(rdx);
- __ Push(rbx);
-
- __ Push(rax); // initial map
- __ CallRuntime(Runtime::kFinalizeInstanceSize);
-
- __ Pop(rbx);
- __ Pop(rdx);
- __ Pop(rdi);
-
- // Continue with JSObject being successfully allocated.
- // rdi: constructor
- // rdx: new target
- // rbx: JSObject (tagged)
- __ jmp(&allocated);
-
- __ bind(&no_inobject_slack_tracking);
- }
-
- __ InitializeFieldsWithFiller(rcx, r9, r11);
-
- // Continue with JSObject being successfully allocated
- // rdi: constructor
- // rdx: new target
- // rbx: JSObject (tagged)
- __ jmp(&allocated);
- }
-
- // Allocate the new receiver object using the runtime call.
- // rdi: constructor
- // rdx: new target
- __ bind(&rt_call);
-
- // Must restore rsi (context) before calling runtime.
- __ movp(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
-
- // Push the constructor and new_target twice, second pair as arguments
- // to the runtime call.
+ // Allocate the new receiver object.
__ Push(rdi);
__ Push(rdx);
- __ Push(rdi); // constructor function
- __ Push(rdx); // new target
- __ CallRuntime(Runtime::kNewObject);
- __ movp(rbx, rax); // store result in rbx
+ FastNewObjectStub stub(masm->isolate());
+ __ CallStub(&stub);
+ __ movp(rbx, rax);
__ Pop(rdx);
__ Pop(rdi);
- // Receiver for constructor call allocated.
- // rdi: constructor
- // rdx: new target
- // rbx: newly allocated object
- __ bind(&allocated);
+ // ----------- S t a t e -------------
+ // -- rdi: constructor function
+ // -- rbx: newly allocated object
+ // -- rdx: new target
+ // -----------------------------------
// Retrieve smi-tagged arguments count from the stack.
- __ movp(rax, Operand(rsp, 0));
- __ SmiToInteger32(rax, rax);
+ __ SmiToInteger32(rax, Operand(rsp, 0 * kPointerSize));
}
if (create_implicit_receiver) {
@@ -357,6 +232,19 @@
// Leave construct frame.
}
+ // ES6 9.2.2. Step 13+
+ // Check that the result is not a Smi, indicating that the constructor result
+ // from a derived class is neither undefined nor an Object.
+ if (check_derived_construct) {
+ Label dont_throw;
+ __ JumpIfNotSmi(rax, &dont_throw);
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+ __ CallRuntime(Runtime::kThrowDerivedConstructorReturnedNonObject);
+ }
+ __ bind(&dont_throw);
+ }
+
// Remove caller arguments from the stack and return.
__ PopReturnAddressTo(rcx);
SmiIndex index = masm->SmiToIndex(rbx, rbx, kPointerSizeLog2);
@@ -371,17 +259,23 @@
void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) {
- Generate_JSConstructStubHelper(masm, false, true);
+ Generate_JSConstructStubHelper(masm, false, true, false);
}
void Builtins::Generate_JSConstructStubApi(MacroAssembler* masm) {
- Generate_JSConstructStubHelper(masm, true, true);
+ Generate_JSConstructStubHelper(masm, true, false, false);
}
void Builtins::Generate_JSBuiltinsConstructStub(MacroAssembler* masm) {
- Generate_JSConstructStubHelper(masm, false, false);
+ Generate_JSConstructStubHelper(masm, false, false, false);
+}
+
+
+void Builtins::Generate_JSBuiltinsConstructStubForDerived(
+ MacroAssembler* masm) {
+ Generate_JSConstructStubHelper(masm, false, false, true);
}
@@ -584,10 +478,8 @@
// o rbp: the caller's frame pointer
// o rsp: stack pointer (pointing to return address)
//
-// The function builds a JS frame. Please see JavaScriptFrameConstants in
-// frames-x64.h for its layout.
-// TODO(rmcilroy): We will need to include the current bytecode pointer in the
-// frame.
+// The function builds an interpreter frame. See InterpreterFrameConstants in
+// frames.h for its layout.
void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) {
// Open a frame scope to indicate that there is a frame on the stack. The
// MANUAL indicates that the scope shouldn't actually generate code to set up
@@ -599,14 +491,18 @@
__ Push(rdi); // Callee's JS function.
__ Push(rdx); // Callee's new target.
- // Push zero for bytecode array offset.
- __ Push(Immediate(0));
-
// Get the bytecode array from the function object and load the pointer to the
// first entry into edi (InterpreterBytecodeRegister).
__ movp(rax, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset));
+
+ Label load_debug_bytecode_array, bytecode_array_loaded;
+ DCHECK_EQ(Smi::FromInt(0), DebugInfo::uninitialized());
+ __ cmpp(FieldOperand(rax, SharedFunctionInfo::kDebugInfoOffset),
+ Immediate(0));
+ __ j(not_equal, &load_debug_bytecode_array);
__ movp(kInterpreterBytecodeArrayRegister,
FieldOperand(rax, SharedFunctionInfo::kFunctionDataOffset));
+ __ bind(&bytecode_array_loaded);
if (FLAG_debug_code) {
// Check function data field is actually a BytecodeArray object.
@@ -616,6 +512,11 @@
__ Assert(equal, kFunctionDataShouldBeBytecodeArrayOnInterpreterEntry);
}
+ // Push bytecode array.
+ __ Push(kInterpreterBytecodeArrayRegister);
+ // Push zero for bytecode array offset.
+ __ Push(Immediate(0));
+
// Allocate the local and temporary register file on the stack.
{
// Load frame size from the BytecodeArray object.
@@ -647,22 +548,9 @@
// TODO(rmcilroy): List of things not currently dealt with here but done in
// fullcodegen's prologue:
- // - Support profiler (specifically profiling_counter).
// - Call ProfileEntryHookStub when isolate has a function_entry_hook.
- // - Allow simulator stop operations if FLAG_stop_at is set.
// - Code aging of the BytecodeArray object.
- // Perform stack guard check.
- {
- Label ok;
- __ CompareRoot(rsp, Heap::kStackLimitRootIndex);
- __ j(above_equal, &ok, Label::kNear);
- __ Push(kInterpreterBytecodeArrayRegister);
- __ CallRuntime(Runtime::kStackGuard);
- __ Pop(kInterpreterBytecodeArrayRegister);
- __ bind(&ok);
- }
-
// Load accumulator, register file, bytecode offset, dispatch table into
// registers.
__ LoadRoot(kInterpreterAccumulatorRegister, Heap::kUndefinedValueRootIndex);
@@ -671,10 +559,9 @@
Immediate(InterpreterFrameConstants::kRegisterFilePointerFromFp));
__ movp(kInterpreterBytecodeOffsetRegister,
Immediate(BytecodeArray::kHeaderSize - kHeapObjectTag));
- __ LoadRoot(kInterpreterDispatchTableRegister,
- Heap::kInterpreterTableRootIndex);
- __ addp(kInterpreterDispatchTableRegister,
- Immediate(FixedArray::kHeaderSize - kHeapObjectTag));
+ __ Move(
+ kInterpreterDispatchTableRegister,
+ ExternalReference::interpreter_dispatch_table_address(masm->isolate()));
// Dispatch to the first bytecode handler for the function.
__ movzxbp(rbx, Operand(kInterpreterBytecodeArrayRegister,
@@ -685,6 +572,17 @@
// and header removal.
__ addp(rbx, Immediate(Code::kHeaderSize - kHeapObjectTag));
__ call(rbx);
+
+ // Even though the first bytecode handler was called, we will never return.
+ __ Abort(kUnexpectedReturnFromBytecodeHandler);
+
+ // Load debug copy of the bytecode array.
+ __ bind(&load_debug_bytecode_array);
+ Register debug_info = kInterpreterBytecodeArrayRegister;
+ __ movp(debug_info, FieldOperand(rax, SharedFunctionInfo::kDebugInfoOffset));
+ __ movp(kInterpreterBytecodeArrayRegister,
+ FieldOperand(debug_info, DebugInfo::kAbstractCodeIndex));
+ __ jmp(&bytecode_array_loaded);
}
@@ -742,7 +640,8 @@
// static
-void Builtins::Generate_InterpreterPushArgsAndCall(MacroAssembler* masm) {
+void Builtins::Generate_InterpreterPushArgsAndCallImpl(
+ MacroAssembler* masm, TailCallMode tail_call_mode) {
// ----------- S t a t e -------------
// -- rax : the number of arguments (not including the receiver)
// -- rbx : the address of the first argument to be pushed. Subsequent
@@ -758,7 +657,9 @@
// Call the target.
__ PushReturnAddressFrom(kScratchRegister); // Re-push return address.
- __ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET);
+ __ Jump(masm->isolate()->builtins()->Call(ConvertReceiverMode::kAny,
+ tail_call_mode),
+ RelocInfo::CODE_TARGET);
}
@@ -790,52 +691,25 @@
}
-static void Generate_InterpreterNotifyDeoptimizedHelper(
- MacroAssembler* masm, Deoptimizer::BailoutType type) {
- // Enter an internal frame.
- {
- FrameScope scope(masm, StackFrame::INTERNAL);
- __ Push(kInterpreterAccumulatorRegister); // Save accumulator register.
-
- // Pass the deoptimization type to the runtime system.
- __ Push(Smi::FromInt(static_cast<int>(type)));
-
- __ CallRuntime(Runtime::kNotifyDeoptimized);
-
- __ Pop(kInterpreterAccumulatorRegister); // Restore accumulator register.
- // Tear down internal frame.
- }
-
- // Drop state (we don't use these for interpreter deopts) and push PC at top
- // of stack (to simulate initial call to bytecode handler in interpreter entry
- // trampoline).
- __ Pop(rbx);
- __ Drop(1);
- __ Push(rbx);
-
+static void Generate_EnterBytecodeDispatch(MacroAssembler* masm) {
// Initialize register file register and dispatch table register.
__ movp(kInterpreterRegisterFileRegister, rbp);
__ addp(kInterpreterRegisterFileRegister,
Immediate(InterpreterFrameConstants::kRegisterFilePointerFromFp));
- __ LoadRoot(kInterpreterDispatchTableRegister,
- Heap::kInterpreterTableRootIndex);
- __ addp(kInterpreterDispatchTableRegister,
- Immediate(FixedArray::kHeaderSize - kHeapObjectTag));
+ __ Move(
+ kInterpreterDispatchTableRegister,
+ ExternalReference::interpreter_dispatch_table_address(masm->isolate()));
// Get the context from the frame.
- // TODO(rmcilroy): Update interpreter frame to expect current context at the
- // context slot instead of the function context.
__ movp(kContextRegister,
Operand(kInterpreterRegisterFileRegister,
InterpreterFrameConstants::kContextFromRegisterPointer));
// Get the bytecode array pointer from the frame.
- __ movp(rbx,
- Operand(kInterpreterRegisterFileRegister,
- InterpreterFrameConstants::kFunctionFromRegisterPointer));
- __ movp(rbx, FieldOperand(rbx, JSFunction::kSharedFunctionInfoOffset));
- __ movp(kInterpreterBytecodeArrayRegister,
- FieldOperand(rbx, SharedFunctionInfo::kFunctionDataOffset));
+ __ movp(
+ kInterpreterBytecodeArrayRegister,
+ Operand(kInterpreterRegisterFileRegister,
+ InterpreterFrameConstants::kBytecodeArrayFromRegisterPointer));
if (FLAG_debug_code) {
// Check function data field is actually a BytecodeArray object.
@@ -863,6 +737,32 @@
}
+static void Generate_InterpreterNotifyDeoptimizedHelper(
+ MacroAssembler* masm, Deoptimizer::BailoutType type) {
+ // Enter an internal frame.
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+
+ // Pass the deoptimization type to the runtime system.
+ __ Push(Smi::FromInt(static_cast<int>(type)));
+ __ CallRuntime(Runtime::kNotifyDeoptimized);
+ // Tear down internal frame.
+ }
+
+ // Drop state (we don't use these for interpreter deopts) and and pop the
+ // accumulator value into the accumulator register and push PC at top
+ // of stack (to simulate initial call to bytecode handler in interpreter entry
+ // trampoline).
+ __ Pop(rbx);
+ __ Drop(1);
+ __ Pop(kInterpreterAccumulatorRegister);
+ __ Push(rbx);
+
+ // Enter the bytecode dispatch.
+ Generate_EnterBytecodeDispatch(masm);
+}
+
+
void Builtins::Generate_InterpreterNotifyDeoptimized(MacroAssembler* masm) {
Generate_InterpreterNotifyDeoptimizedHelper(masm, Deoptimizer::EAGER);
}
@@ -877,22 +777,30 @@
Generate_InterpreterNotifyDeoptimizedHelper(masm, Deoptimizer::LAZY);
}
+void Builtins::Generate_InterpreterEnterBytecodeDispatch(MacroAssembler* masm) {
+ // Set the address of the interpreter entry trampoline as a return address.
+ // This simulates the initial call to bytecode handlers in interpreter entry
+ // trampoline. The return will never actually be taken, but our stack walker
+ // uses this address to determine whether a frame is interpreted.
+ __ Push(masm->isolate()->builtins()->InterpreterEntryTrampoline());
+
+ Generate_EnterBytecodeDispatch(masm);
+}
+
void Builtins::Generate_CompileLazy(MacroAssembler* masm) {
- CallRuntimePassFunction(masm, Runtime::kCompileLazy);
- GenerateTailCallToReturnedCode(masm);
+ GenerateTailCallToReturnedCode(masm, Runtime::kCompileLazy);
}
void Builtins::Generate_CompileOptimized(MacroAssembler* masm) {
- CallRuntimePassFunction(masm, Runtime::kCompileOptimized_NotConcurrent);
- GenerateTailCallToReturnedCode(masm);
+ GenerateTailCallToReturnedCode(masm,
+ Runtime::kCompileOptimized_NotConcurrent);
}
void Builtins::Generate_CompileOptimizedConcurrent(MacroAssembler* masm) {
- CallRuntimePassFunction(masm, Runtime::kCompileOptimized_Concurrent);
- GenerateTailCallToReturnedCode(masm);
+ GenerateTailCallToReturnedCode(masm, Runtime::kCompileOptimized_Concurrent);
}
@@ -1166,7 +1074,8 @@
__ Jump(masm->isolate()->builtins()->Apply(), RelocInfo::CODE_TARGET);
// 4b. The argArray is either null or undefined, so we tail call without any
- // arguments to the receiver.
+ // arguments to the receiver. Since we did not create a frame for
+ // Function.prototype.apply() yet, we use a normal Call builtin here.
__ bind(&no_arguments);
{
__ Set(rax, 0);
@@ -1230,6 +1139,8 @@
}
// 4. Call the callable.
+ // Since we did not create a frame for Function.prototype.call() yet,
+ // we use a normal Call builtin here.
__ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET);
}
@@ -1443,6 +1354,118 @@
// static
+void Builtins::Generate_MathMaxMin(MacroAssembler* masm, MathMaxMinKind kind) {
+ // ----------- S t a t e -------------
+ // -- rax : number of arguments
+ // -- rsp[0] : return address
+ // -- rsp[(argc - n) * 8] : arg[n] (zero-based)
+ // -- rsp[(argc + 1) * 8] : receiver
+ // -----------------------------------
+ Condition const cc = (kind == MathMaxMinKind::kMin) ? below : above;
+ Heap::RootListIndex const root_index =
+ (kind == MathMaxMinKind::kMin) ? Heap::kInfinityValueRootIndex
+ : Heap::kMinusInfinityValueRootIndex;
+ XMMRegister const reg = (kind == MathMaxMinKind::kMin) ? xmm1 : xmm0;
+
+ // Load the accumulator with the default return value (either -Infinity or
+ // +Infinity), with the tagged value in rdx and the double value in xmm0.
+ __ LoadRoot(rdx, root_index);
+ __ Movsd(xmm0, FieldOperand(rdx, HeapNumber::kValueOffset));
+ __ Move(rcx, rax);
+
+ Label done_loop, loop;
+ __ bind(&loop);
+ {
+ // Check if all parameters done.
+ __ testp(rcx, rcx);
+ __ j(zero, &done_loop);
+
+ // Load the next parameter tagged value into rbx.
+ __ movp(rbx, Operand(rsp, rcx, times_pointer_size, 0));
+
+ // Load the double value of the parameter into xmm1, maybe converting the
+ // parameter to a number first using the ToNumberStub if necessary.
+ Label convert, convert_smi, convert_number, done_convert;
+ __ bind(&convert);
+ __ JumpIfSmi(rbx, &convert_smi);
+ __ JumpIfRoot(FieldOperand(rbx, HeapObject::kMapOffset),
+ Heap::kHeapNumberMapRootIndex, &convert_number);
+ {
+ // Parameter is not a Number, use the ToNumberStub to convert it.
+ FrameScope scope(masm, StackFrame::INTERNAL);
+ __ Integer32ToSmi(rax, rax);
+ __ Integer32ToSmi(rcx, rcx);
+ __ Push(rax);
+ __ Push(rcx);
+ __ Push(rdx);
+ __ movp(rax, rbx);
+ ToNumberStub stub(masm->isolate());
+ __ CallStub(&stub);
+ __ movp(rbx, rax);
+ __ Pop(rdx);
+ __ Pop(rcx);
+ __ Pop(rax);
+ {
+ // Restore the double accumulator value (xmm0).
+ Label restore_smi, done_restore;
+ __ JumpIfSmi(rdx, &restore_smi, Label::kNear);
+ __ Movsd(xmm0, FieldOperand(rdx, HeapNumber::kValueOffset));
+ __ jmp(&done_restore, Label::kNear);
+ __ bind(&restore_smi);
+ __ SmiToDouble(xmm0, rdx);
+ __ bind(&done_restore);
+ }
+ __ SmiToInteger32(rcx, rcx);
+ __ SmiToInteger32(rax, rax);
+ }
+ __ jmp(&convert);
+ __ bind(&convert_number);
+ __ Movsd(xmm1, FieldOperand(rbx, HeapNumber::kValueOffset));
+ __ jmp(&done_convert, Label::kNear);
+ __ bind(&convert_smi);
+ __ SmiToDouble(xmm1, rbx);
+ __ bind(&done_convert);
+
+ // Perform the actual comparison with the accumulator value on the left hand
+ // side (xmm0) and the next parameter value on the right hand side (xmm1).
+ Label compare_equal, compare_nan, compare_swap, done_compare;
+ __ Ucomisd(xmm0, xmm1);
+ __ j(parity_even, &compare_nan, Label::kNear);
+ __ j(cc, &done_compare, Label::kNear);
+ __ j(equal, &compare_equal, Label::kNear);
+
+ // Result is on the right hand side.
+ __ bind(&compare_swap);
+ __ Movaps(xmm0, xmm1);
+ __ Move(rdx, rbx);
+ __ jmp(&done_compare, Label::kNear);
+
+ // At least one side is NaN, which means that the result will be NaN too.
+ __ bind(&compare_nan);
+ __ LoadRoot(rdx, Heap::kNanValueRootIndex);
+ __ Movsd(xmm0, FieldOperand(rdx, HeapNumber::kValueOffset));
+ __ jmp(&done_compare, Label::kNear);
+
+ // Left and right hand side are equal, check for -0 vs. +0.
+ __ bind(&compare_equal);
+ __ Movmskpd(kScratchRegister, reg);
+ __ testl(kScratchRegister, Immediate(1));
+ __ j(not_zero, &compare_swap);
+
+ __ bind(&done_compare);
+ __ decp(rcx);
+ __ jmp(&loop);
+ }
+
+ __ bind(&done_loop);
+ __ PopReturnAddressTo(rcx);
+ __ leap(rsp, Operand(rsp, rax, times_pointer_size, kPointerSize));
+ __ PushReturnAddressFrom(rcx);
+ __ movp(rax, rdx);
+ __ Ret();
+}
+
+// static
void Builtins::Generate_NumberConstructor(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- rax : number of arguments
@@ -1542,9 +1565,8 @@
{
FrameScope scope(masm, StackFrame::INTERNAL);
__ Push(rbx); // the first argument
- __ Push(rdi); // constructor function
- __ Push(rdx); // new target
- __ CallRuntime(Runtime::kNewObject);
+ FastNewObjectStub stub(masm->isolate());
+ __ CallStub(&stub);
__ Pop(FieldOperand(rax, JSValue::kValueOffset));
}
__ Ret();
@@ -1678,9 +1700,8 @@
{
FrameScope scope(masm, StackFrame::INTERNAL);
__ Push(rbx); // the first argument
- __ Push(rdi); // constructor function
- __ Push(rdx); // new target
- __ CallRuntime(Runtime::kNewObject);
+ FastNewObjectStub stub(masm->isolate());
+ __ CallStub(&stub);
__ Pop(FieldOperand(rax, JSValue::kValueOffset));
}
__ Ret();
@@ -1931,9 +1952,7 @@
// Try to create the list from an arguments object.
__ bind(&create_arguments);
- __ movp(rbx,
- FieldOperand(rax, JSObject::kHeaderSize +
- Heap::kArgumentsLengthIndex * kPointerSize));
+ __ movp(rbx, FieldOperand(rax, JSArgumentsObject::kLengthOffset));
__ movp(rcx, FieldOperand(rax, JSObject::kElementsOffset));
__ cmpp(rbx, FieldOperand(rcx, FixedArray::kLengthOffset));
__ j(not_equal, &create_runtime);
@@ -2010,10 +2029,136 @@
}
}
+namespace {
+
+// Drops top JavaScript frame and an arguments adaptor frame below it (if
+// present) preserving all the arguments prepared for current call.
+// Does nothing if debugger is currently active.
+// ES6 14.6.3. PrepareForTailCall
+//
+// Stack structure for the function g() tail calling f():
+//
+// ------- Caller frame: -------
+// | ...
+// | g()'s arg M
+// | ...
+// | g()'s arg 1
+// | g()'s receiver arg
+// | g()'s caller pc
+// ------- g()'s frame: -------
+// | g()'s caller fp <- fp
+// | g()'s context
+// | function pointer: g
+// | -------------------------
+// | ...
+// | ...
+// | f()'s arg N
+// | ...
+// | f()'s arg 1
+// | f()'s receiver arg
+// | f()'s caller pc <- sp
+// ----------------------
+//
+void PrepareForTailCall(MacroAssembler* masm, Register args_reg,
+ Register scratch1, Register scratch2,
+ Register scratch3) {
+ DCHECK(!AreAliased(args_reg, scratch1, scratch2, scratch3));
+ Comment cmnt(masm, "[ PrepareForTailCall");
+
+ // Prepare for tail call only if the debugger is not active.
+ Label done;
+ ExternalReference debug_is_active =
+ ExternalReference::debug_is_active_address(masm->isolate());
+ __ Move(kScratchRegister, debug_is_active);
+ __ cmpb(Operand(kScratchRegister, 0), Immediate(0));
+ __ j(not_equal, &done);
+
+ // Drop possible interpreter handler/stub frame.
+ {
+ Label no_interpreter_frame;
+ __ Cmp(Operand(rbp, StandardFrameConstants::kMarkerOffset),
+ Smi::FromInt(StackFrame::STUB));
+ __ j(not_equal, &no_interpreter_frame, Label::kNear);
+ __ movp(rbp, Operand(rbp, StandardFrameConstants::kCallerFPOffset));
+ __ bind(&no_interpreter_frame);
+ }
+
+ // Check if next frame is an arguments adaptor frame.
+ Label no_arguments_adaptor, formal_parameter_count_loaded;
+ __ movp(scratch2, Operand(rbp, StandardFrameConstants::kCallerFPOffset));
+ __ Cmp(Operand(scratch2, StandardFrameConstants::kContextOffset),
+ Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR));
+ __ j(not_equal, &no_arguments_adaptor, Label::kNear);
+
+ // Drop arguments adaptor frame and load arguments count.
+ __ movp(rbp, scratch2);
+ __ SmiToInteger32(
+ scratch1, Operand(rbp, ArgumentsAdaptorFrameConstants::kLengthOffset));
+ __ jmp(&formal_parameter_count_loaded, Label::kNear);
+
+ __ bind(&no_arguments_adaptor);
+ // Load caller's formal parameter count
+ __ movp(scratch1, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset));
+ __ movp(scratch1,
+ FieldOperand(scratch1, JSFunction::kSharedFunctionInfoOffset));
+ __ LoadSharedFunctionInfoSpecialField(
+ scratch1, scratch1, SharedFunctionInfo::kFormalParameterCountOffset);
+
+ __ bind(&formal_parameter_count_loaded);
+
+ // Calculate the destination address where we will put the return address
+ // after we drop current frame.
+ Register new_sp_reg = scratch2;
+ __ subp(scratch1, args_reg);
+ __ leap(new_sp_reg, Operand(rbp, scratch1, times_pointer_size,
+ StandardFrameConstants::kCallerPCOffset));
+
+ if (FLAG_debug_code) {
+ __ cmpp(rsp, new_sp_reg);
+ __ Check(below, kStackAccessBelowStackPointer);
+ }
+
+ // Copy receiver and return address as well.
+ Register count_reg = scratch1;
+ __ leap(count_reg, Operand(args_reg, 2));
+
+ // Copy return address from caller's frame to current frame's return address
+ // to avoid its trashing and let the following loop copy it to the right
+ // place.
+ Register tmp_reg = scratch3;
+ __ movp(tmp_reg, Operand(rbp, StandardFrameConstants::kCallerPCOffset));
+ __ movp(Operand(rsp, 0), tmp_reg);
+
+ // Restore caller's frame pointer now as it could be overwritten by
+ // the copying loop.
+ __ movp(rbp, Operand(rbp, StandardFrameConstants::kCallerFPOffset));
+
+ Operand src(rsp, count_reg, times_pointer_size, 0);
+ Operand dst(new_sp_reg, count_reg, times_pointer_size, 0);
+
+ // Now copy callee arguments to the caller frame going backwards to avoid
+ // callee arguments corruption (source and destination areas could overlap).
+ Label loop, entry;
+ __ jmp(&entry, Label::kNear);
+ __ bind(&loop);
+ __ decp(count_reg);
+ __ movp(tmp_reg, src);
+ __ movp(dst, tmp_reg);
+ __ bind(&entry);
+ __ cmpp(count_reg, Immediate(0));
+ __ j(not_equal, &loop, Label::kNear);
+
+ // Leave current frame.
+ __ movp(rsp, new_sp_reg);
+
+ __ bind(&done);
+}
+} // namespace
// static
void Builtins::Generate_CallFunction(MacroAssembler* masm,
- ConvertReceiverMode mode) {
+ ConvertReceiverMode mode,
+ TailCallMode tail_call_mode) {
// ----------- S t a t e -------------
// -- rax : the number of arguments (not including the receiver)
// -- rdi : the function to call (checked to be a JSFunction)
@@ -2109,6 +2254,10 @@
// -- rsi : the function context.
// -----------------------------------
+ if (tail_call_mode == TailCallMode::kAllow) {
+ PrepareForTailCall(masm, rax, rbx, rcx, r8);
+ }
+
__ LoadSharedFunctionInfoSpecialField(
rbx, rdx, SharedFunctionInfo::kFormalParameterCountOffset);
ParameterCount actual(rax);
@@ -2213,13 +2362,18 @@
// static
-void Builtins::Generate_CallBoundFunction(MacroAssembler* masm) {
+void Builtins::Generate_CallBoundFunctionImpl(MacroAssembler* masm,
+ TailCallMode tail_call_mode) {
// ----------- S t a t e -------------
// -- rax : the number of arguments (not including the receiver)
// -- rdi : the function to call (checked to be a JSBoundFunction)
// -----------------------------------
__ AssertBoundFunction(rdi);
+ if (tail_call_mode == TailCallMode::kAllow) {
+ PrepareForTailCall(masm, rax, rbx, rcx, r8);
+ }
+
// Patch the receiver to [[BoundThis]].
StackArgumentsAccessor args(rsp, rax);
__ movp(rbx, FieldOperand(rdi, JSBoundFunction::kBoundThisOffset));
@@ -2238,7 +2392,8 @@
// static
-void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode) {
+void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode,
+ TailCallMode tail_call_mode) {
// ----------- S t a t e -------------
// -- rax : the number of arguments (not including the receiver)
// -- rdi : the target to call (can be any Object)
@@ -2249,14 +2404,25 @@
__ JumpIfSmi(rdi, &non_callable);
__ bind(&non_smi);
__ CmpObjectType(rdi, JS_FUNCTION_TYPE, rcx);
- __ j(equal, masm->isolate()->builtins()->CallFunction(mode),
+ __ j(equal, masm->isolate()->builtins()->CallFunction(mode, tail_call_mode),
RelocInfo::CODE_TARGET);
__ CmpInstanceType(rcx, JS_BOUND_FUNCTION_TYPE);
- __ j(equal, masm->isolate()->builtins()->CallBoundFunction(),
+ __ j(equal, masm->isolate()->builtins()->CallBoundFunction(tail_call_mode),
RelocInfo::CODE_TARGET);
+
+ // Check if target has a [[Call]] internal method.
+ __ testb(FieldOperand(rcx, Map::kBitFieldOffset),
+ Immediate(1 << Map::kIsCallable));
+ __ j(zero, &non_callable);
+
__ CmpInstanceType(rcx, JS_PROXY_TYPE);
__ j(not_equal, &non_function);
+ // 0. Prepare for tail call if necessary.
+ if (tail_call_mode == TailCallMode::kAllow) {
+ PrepareForTailCall(masm, rax, rbx, rcx, r8);
+ }
+
// 1. Runtime fallback for Proxy [[Call]].
__ PopReturnAddressTo(kScratchRegister);
__ Push(rdi);
@@ -2271,16 +2437,12 @@
// 2. Call to something else, which might have a [[Call]] internal method (if
// not we raise an exception).
__ bind(&non_function);
- // Check if target has a [[Call]] internal method.
- __ testb(FieldOperand(rcx, Map::kBitFieldOffset),
- Immediate(1 << Map::kIsCallable));
- __ j(zero, &non_callable, Label::kNear);
// Overwrite the original receiver with the (original) target.
__ movp(args.GetReceiverOperand(), rdi);
// Let the "call_as_function_delegate" take care of the rest.
__ LoadNativeContextSlot(Context::CALL_AS_FUNCTION_DELEGATE_INDEX, rdi);
__ Jump(masm->isolate()->builtins()->CallFunction(
- ConvertReceiverMode::kNotNullOrUndefined),
+ ConvertReceiverMode::kNotNullOrUndefined, tail_call_mode),
RelocInfo::CODE_TARGET);
// 3. Call to something that is not callable.
@@ -2473,14 +2635,11 @@
// Load the next prototype.
__ bind(&next_prototype);
- __ movp(receiver, FieldOperand(map, Map::kPrototypeOffset));
- // End if the prototype is null or not hidden.
- __ CompareRoot(receiver, Heap::kNullValueRootIndex);
- __ j(equal, receiver_check_failed);
- __ movp(map, FieldOperand(receiver, HeapObject::kMapOffset));
__ testq(FieldOperand(map, Map::kBitField3Offset),
- Immediate(Map::IsHiddenPrototype::kMask));
+ Immediate(Map::HasHiddenPrototype::kMask));
__ j(zero, receiver_check_failed);
+ __ movp(receiver, FieldOperand(map, Map::kPrototypeOffset));
+ __ movp(map, FieldOperand(receiver, HeapObject::kMapOffset));
// Iterate.
__ jmp(&prototype_loop_start, Label::kNear);