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/arm64/builtins-arm64.cc b/src/arm64/builtins-arm64.cc
index b6bae4a..11f66a4 100644
--- a/src/arm64/builtins-arm64.cc
+++ b/src/arm64/builtins-arm64.cc
@@ -138,6 +138,97 @@
// static
+void Builtins::Generate_MathMaxMin(MacroAssembler* masm, MathMaxMinKind kind) {
+ // ----------- S t a t e -------------
+ // -- x0 : number of arguments
+ // -- lr : return address
+ // -- sp[(argc - n) * 8] : arg[n] (zero-based)
+ // -- sp[(argc + 1) * 8] : receiver
+ // -----------------------------------
+ ASM_LOCATION("Builtins::Generate_MathMaxMin");
+
+ Heap::RootListIndex const root_index =
+ (kind == MathMaxMinKind::kMin) ? Heap::kInfinityValueRootIndex
+ : Heap::kMinusInfinityValueRootIndex;
+
+ // Load the accumulator with the default return value (either -Infinity or
+ // +Infinity), with the tagged value in x1 and the double value in d1.
+ __ LoadRoot(x1, root_index);
+ __ Ldr(d1, FieldMemOperand(x1, HeapNumber::kValueOffset));
+
+ // Remember how many slots to drop (including the receiver).
+ __ Add(x4, x0, 1);
+
+ Label done_loop, loop;
+ __ Bind(&loop);
+ {
+ // Check if all parameters done.
+ __ Subs(x0, x0, 1);
+ __ B(lt, &done_loop);
+
+ // Load the next parameter tagged value into x2.
+ __ Peek(x2, Operand(x0, LSL, kPointerSizeLog2));
+
+ // Load the double value of the parameter into d2, maybe converting the
+ // parameter to a number first using the ToNumberStub if necessary.
+ Label convert_smi, convert_number, done_convert;
+ __ JumpIfSmi(x2, &convert_smi);
+ __ JumpIfHeapNumber(x2, &convert_number);
+ {
+ // Parameter is not a Number, use the ToNumberStub to convert it.
+ FrameScope scope(masm, StackFrame::INTERNAL);
+ __ SmiTag(x0);
+ __ SmiTag(x4);
+ __ Push(x0, x1, x4);
+ __ Mov(x0, x2);
+ ToNumberStub stub(masm->isolate());
+ __ CallStub(&stub);
+ __ Mov(x2, x0);
+ __ Pop(x4, x1, x0);
+ {
+ // Restore the double accumulator value (d1).
+ Label done_restore;
+ __ SmiUntagToDouble(d1, x1, kSpeculativeUntag);
+ __ JumpIfSmi(x1, &done_restore);
+ __ Ldr(d1, FieldMemOperand(x1, HeapNumber::kValueOffset));
+ __ Bind(&done_restore);
+ }
+ __ SmiUntag(x4);
+ __ SmiUntag(x0);
+ }
+ __ AssertNumber(x2);
+ __ JumpIfSmi(x2, &convert_smi);
+
+ __ Bind(&convert_number);
+ __ Ldr(d2, FieldMemOperand(x2, HeapNumber::kValueOffset));
+ __ B(&done_convert);
+
+ __ Bind(&convert_smi);
+ __ SmiUntagToDouble(d2, x2);
+ __ Bind(&done_convert);
+
+ // We can use a single fmin/fmax for the operation itself, but we then need
+ // to work out which HeapNumber (or smi) the result came from.
+ __ Fmov(x11, d1);
+ if (kind == MathMaxMinKind::kMin) {
+ __ Fmin(d1, d1, d2);
+ } else {
+ DCHECK(kind == MathMaxMinKind::kMax);
+ __ Fmax(d1, d1, d2);
+ }
+ __ Fmov(x10, d1);
+ __ Cmp(x10, x11);
+ __ Csel(x1, x1, x2, eq);
+ __ B(&loop);
+ }
+
+ __ Bind(&done_loop);
+ __ Mov(x0, x1);
+ __ Drop(x4);
+ __ Ret();
+}
+
+// static
void Builtins::Generate_NumberConstructor(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- x0 : number of arguments
@@ -229,8 +320,9 @@
__ bind(&new_object);
{
FrameScope scope(masm, StackFrame::INTERNAL);
- __ Push(x2, x1, x3); // first argument, constructor, new target
- __ CallRuntime(Runtime::kNewObject);
+ __ Push(x2); // first argument
+ FastNewObjectStub stub(masm->isolate());
+ __ CallStub(&stub);
__ Pop(x2);
}
__ Str(x2, FieldMemOperand(x0, JSValue::kValueOffset));
@@ -356,34 +448,15 @@
__ bind(&new_object);
{
FrameScope scope(masm, StackFrame::INTERNAL);
- __ Push(x2, x1, x3); // first argument, constructor, new target
- __ CallRuntime(Runtime::kNewObject);
+ __ Push(x2); // first argument
+ FastNewObjectStub stub(masm->isolate());
+ __ CallStub(&stub);
__ Pop(x2);
}
__ Str(x2, FieldMemOperand(x0, JSValue::kValueOffset));
__ Ret();
}
-
-static void CallRuntimePassFunction(MacroAssembler* masm,
- Runtime::FunctionId function_id) {
- // ----------- S t a t e -------------
- // -- x1 : target function (preserved for callee)
- // -- x3 : new target (preserved for callee)
- // -----------------------------------
-
- FrameScope scope(masm, StackFrame::INTERNAL);
- // Push a copy of the target function and the new target.
- // Push another copy as a parameter to the runtime call.
- __ Push(x1, x3, x1);
-
- __ CallRuntime(function_id, 1);
-
- // Restore target function and new target.
- __ Pop(x3, x1);
-}
-
-
static void GenerateTailCallToSharedCode(MacroAssembler* masm) {
__ Ldr(x2, FieldMemOperand(x1, JSFunction::kSharedFunctionInfoOffset));
__ Ldr(x2, FieldMemOperand(x2, SharedFunctionInfo::kCodeOffset));
@@ -391,10 +464,30 @@
__ Br(x2);
}
+static void GenerateTailCallToReturnedCode(MacroAssembler* masm,
+ Runtime::FunctionId function_id) {
+ // ----------- S t a t e -------------
+ // -- x0 : argument count (preserved for callee)
+ // -- x1 : target function (preserved for callee)
+ // -- x3 : new target (preserved for callee)
+ // -----------------------------------
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+ // Push a copy of the target function and the new target.
+ // Push another copy as a parameter to the runtime call.
+ __ SmiTag(x0);
+ __ Push(x0, x1, x3, x1);
-static void GenerateTailCallToReturnedCode(MacroAssembler* masm) {
- __ Add(x0, x0, Code::kHeaderSize - kHeapObjectTag);
- __ Br(x0);
+ __ CallRuntime(function_id, 1);
+ __ Move(x2, x0);
+
+ // Restore target function and new target.
+ __ Pop(x3, x1, x0);
+ __ SmiUntag(x0);
+ }
+
+ __ Add(x2, x2, Code::kHeaderSize - kHeapObjectTag);
+ __ Br(x2);
}
@@ -408,8 +501,7 @@
__ CompareRoot(masm->StackPointer(), Heap::kStackLimitRootIndex);
__ B(hs, &ok);
- CallRuntimePassFunction(masm, Runtime::kTryInstallOptimizedCode);
- GenerateTailCallToReturnedCode(masm);
+ GenerateTailCallToReturnedCode(masm, Runtime::kTryInstallOptimizedCode);
__ Bind(&ok);
GenerateTailCallToSharedCode(masm);
@@ -418,7 +510,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 -------------
// -- x0 : number of arguments
// -- x1 : constructor function
@@ -448,148 +541,18 @@
__ Push(allocation_site, argc);
if (create_implicit_receiver) {
- // sp[0]: new.target
- // sp[1]: Constructor function.
- // sp[2]: number of arguments (smi-tagged)
- // sp[3]: allocation site
- // 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.
- __ JumpIfNotObjectType(new_target, x10, x11, JS_FUNCTION_TYPE,
- &rt_call);
-
- // Load the initial map and verify that it is in fact a map.
- Register init_map = x2;
- __ Ldr(init_map,
- FieldMemOperand(new_target,
- JSFunction::kPrototypeOrInitialMapOffset));
- __ JumpIfSmi(init_map, &rt_call);
- __ JumpIfNotObjectType(init_map, x10, x11, MAP_TYPE, &rt_call);
-
- // Fall back to runtime if the expected base constructor and base
- // constructor differ.
- __ Ldr(x10,
- FieldMemOperand(init_map, Map::kConstructorOrBackPointerOffset));
- __ Cmp(constructor, x10);
- __ B(ne, &rt_call);
-
- // Check that the constructor is not constructing a JSFunction (see
- // comments in Runtime_NewObject in runtime.cc). In which case the
- // initial
- // map's instance type would be JS_FUNCTION_TYPE.
- __ CompareInstanceType(init_map, x10, JS_FUNCTION_TYPE);
- __ B(eq, &rt_call);
-
- // Now allocate the JSObject on the heap.
- Register obj_size = x10;
- Register new_obj = x4;
- Register next_obj = obj_size; // May overlap.
- __ Ldrb(obj_size, FieldMemOperand(init_map, Map::kInstanceSizeOffset));
- __ Allocate(obj_size, new_obj, next_obj, x11, &rt_call, SIZE_IN_WORDS);
-
- // Allocated the JSObject, now initialize the fields. Map is set to
- // initial map and properties and elements are set to empty fixed array.
- // NB. the object pointer is not tagged, so MemOperand is used.
- Register write_address = x5;
- Register empty = x7;
- __ Mov(write_address, new_obj);
- __ LoadRoot(empty, Heap::kEmptyFixedArrayRootIndex);
- STATIC_ASSERT(0 * kPointerSize == JSObject::kMapOffset);
- __ Str(init_map, MemOperand(write_address, kPointerSize, PostIndex));
- STATIC_ASSERT(1 * kPointerSize == JSObject::kPropertiesOffset);
- STATIC_ASSERT(2 * kPointerSize == JSObject::kElementsOffset);
- __ Stp(empty, empty,
- MemOperand(write_address, 2 * kPointerSize, PostIndex));
- STATIC_ASSERT(3 * kPointerSize == 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.
- __ Add(new_obj, new_obj, kHeapObjectTag);
-
- // Fill all of the in-object properties with the appropriate filler.
- Register filler = x7;
- __ LoadRoot(filler, Heap::kUndefinedValueRootIndex);
-
- if (!is_api_function) {
- Label no_inobject_slack_tracking;
-
- Register constructon_count = x14;
- MemOperand bit_field3 =
- FieldMemOperand(init_map, Map::kBitField3Offset);
- // Check if slack tracking is enabled.
- __ Ldr(x11, bit_field3);
- __ DecodeField<Map::ConstructionCounter>(constructon_count, x11);
- __ Cmp(constructon_count, Operand(Map::kSlackTrackingCounterEnd));
- __ B(lt, &no_inobject_slack_tracking);
- // Decrease generous allocation count.
- __ Subs(x11, x11, Operand(1 << Map::ConstructionCounter::kShift));
- __ Str(x11, bit_field3);
-
- // Allocate object with a slack.
- Register unused_props = x11;
- __ Ldr(unused_props,
- FieldMemOperand(init_map, Map::kInstanceAttributesOffset));
- __ Ubfx(unused_props, unused_props,
- Map::kUnusedPropertyFieldsByte * kBitsPerByte, kBitsPerByte);
-
- Register end_of_pre_allocated = x11;
- __ Sub(end_of_pre_allocated, next_obj,
- Operand(unused_props, LSL, kPointerSizeLog2));
- unused_props = NoReg;
-
- if (FLAG_debug_code) {
- __ Cmp(write_address, end_of_pre_allocated);
- __ Assert(le, kUnexpectedNumberOfPreAllocatedPropertyFields);
- }
-
- // Fill the pre-allocated fields with undef.
- __ InitializeFieldsWithFiller(write_address, end_of_pre_allocated,
- filler);
-
- // Fill the remaining fields with one pointer filler map.
- __ LoadRoot(filler, Heap::kOnePointerFillerMapRootIndex);
- __ InitializeFieldsWithFiller(write_address, next_obj, filler);
-
- __ Cmp(constructon_count, Operand(Map::kSlackTrackingCounterEnd));
- __ B(ne, &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(constructor, new_target, new_obj, init_map);
- __ CallRuntime(Runtime::kFinalizeInstanceSize);
- __ Pop(new_obj, new_target, constructor);
-
- // Continue with JSObject being successfully allocated.
- __ B(&allocated);
-
- __ bind(&no_inobject_slack_tracking);
- }
-
- __ InitializeFieldsWithFiller(write_address, next_obj, filler);
-
- // Continue with JSObject being successfully allocated.
- __ B(&allocated);
- }
-
- // Allocate the new receiver object using the runtime call.
- // x1: constructor function
- // x3: new target
- __ Bind(&rt_call);
-
- // Push the constructor and new_target twice, second pair as arguments
- // to the runtime call.
- __ Push(constructor, new_target, constructor, new_target);
- __ CallRuntime(Runtime::kNewObject);
+ // Allocate the new receiver object.
+ __ Push(constructor, new_target);
+ FastNewObjectStub stub(masm->isolate());
+ __ CallStub(&stub);
__ Mov(x4, x0);
__ Pop(new_target, constructor);
- // Receiver for constructor call allocated.
- // x1: constructor function
- // x3: new target
- // x4: JSObject
- __ Bind(&allocated);
+ // ----------- S t a t e -------------
+ // -- x1: constructor function
+ // -- x3: new target
+ // -- x4: newly allocated object
+ // -----------------------------------
// Reload the number of arguments from the stack.
// Set it up in x0 for the function call below.
@@ -697,6 +660,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(x0, &dont_throw);
+ {
+ FrameScope scope(masm, StackFrame::INTERNAL);
+ __ CallRuntime(Runtime::kThrowDerivedConstructorReturnedNonObject);
+ }
+ __ Bind(&dont_throw);
+ }
+
__ DropBySMI(x1);
__ Drop(1);
if (create_implicit_receiver) {
@@ -707,17 +683,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);
}
@@ -877,10 +859,8 @@
// - jssp: stack pointer.
// - lr: return address.
//
-// The function builds a JS frame. Please see JavaScriptFrameConstants in
-// frames-arm64.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
@@ -888,17 +868,19 @@
FrameScope frame_scope(masm, StackFrame::MANUAL);
__ Push(lr, fp, cp, x1);
__ Add(fp, jssp, StandardFrameConstants::kFixedFrameSizeFromFp);
- __ Push(x3);
-
- // Push zero for bytecode array offset.
- __ Mov(x0, Operand(0));
- __ Push(x0);
// Get the bytecode array from the function object and load the pointer to the
// first entry into kInterpreterBytecodeRegister.
__ Ldr(x0, FieldMemOperand(x1, JSFunction::kSharedFunctionInfoOffset));
+ Register debug_info = kInterpreterBytecodeArrayRegister;
+ Label load_debug_bytecode_array, bytecode_array_loaded;
+ DCHECK(!debug_info.is(x0));
+ __ Ldr(debug_info, FieldMemOperand(x0, SharedFunctionInfo::kDebugInfoOffset));
+ __ Cmp(debug_info, Operand(DebugInfo::uninitialized()));
+ __ B(ne, &load_debug_bytecode_array);
__ Ldr(kInterpreterBytecodeArrayRegister,
FieldMemOperand(x0, SharedFunctionInfo::kFunctionDataOffset));
+ __ Bind(&bytecode_array_loaded);
if (FLAG_debug_code) {
// Check function data field is actually a BytecodeArray object.
@@ -909,6 +891,10 @@
__ Assert(eq, kFunctionDataShouldBeBytecodeArrayOnInterpreterEntry);
}
+ // Push new.target, bytecode array and zero for bytecode array offset.
+ __ Mov(x0, Operand(0));
+ __ Push(x3, kInterpreterBytecodeArrayRegister, x0);
+
// Allocate the local and temporary register file on the stack.
{
// Load frame size from the BytecodeArray object.
@@ -938,22 +924,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(jssp, Heap::kStackLimitRootIndex);
- __ B(hs, &ok);
- __ Push(kInterpreterBytecodeArrayRegister);
- __ CallRuntime(Runtime::kStackGuard);
- __ Pop(kInterpreterBytecodeArrayRegister);
- __ Bind(&ok);
- }
-
// Load accumulator, register file, bytecode offset, dispatch table into
// registers.
__ LoadRoot(kInterpreterAccumulatorRegister, Heap::kUndefinedValueRootIndex);
@@ -961,10 +934,9 @@
Operand(InterpreterFrameConstants::kRegisterFilePointerFromFp));
__ Mov(kInterpreterBytecodeOffsetRegister,
Operand(BytecodeArray::kHeaderSize - kHeapObjectTag));
- __ LoadRoot(kInterpreterDispatchTableRegister,
- Heap::kInterpreterTableRootIndex);
- __ Add(kInterpreterDispatchTableRegister, kInterpreterDispatchTableRegister,
- Operand(FixedArray::kHeaderSize - kHeapObjectTag));
+ __ Mov(kInterpreterDispatchTableRegister,
+ Operand(ExternalReference::interpreter_dispatch_table_address(
+ masm->isolate())));
// Dispatch to the first bytecode handler for the function.
__ Ldrb(x1, MemOperand(kInterpreterBytecodeArrayRegister,
@@ -975,6 +947,15 @@
// and header removal.
__ Add(ip0, ip0, Operand(Code::kHeaderSize - kHeapObjectTag));
__ Call(ip0);
+
+ // 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);
+ __ Ldr(kInterpreterBytecodeArrayRegister,
+ FieldMemOperand(debug_info, DebugInfo::kAbstractCodeIndex));
+ __ B(&bytecode_array_loaded);
}
@@ -998,47 +979,24 @@
}
-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.
- __ Mov(x1, Operand(Smi::FromInt(static_cast<int>(type))));
- __ Push(x1);
- __ CallRuntime(Runtime::kNotifyDeoptimized);
-
- __ Pop(kInterpreterAccumulatorRegister); // Restore accumulator register.
- // Tear down internal frame.
- }
-
- // Drop state (we don't use this for interpreter deopts).
- __ Drop(1);
-
+static void Generate_EnterBytecodeDispatch(MacroAssembler* masm) {
// Initialize register file register and dispatch table register.
__ Add(kInterpreterRegisterFileRegister, fp,
Operand(InterpreterFrameConstants::kRegisterFilePointerFromFp));
- __ LoadRoot(kInterpreterDispatchTableRegister,
- Heap::kInterpreterTableRootIndex);
- __ Add(kInterpreterDispatchTableRegister, kInterpreterDispatchTableRegister,
- Operand(FixedArray::kHeaderSize - kHeapObjectTag));
+ __ Mov(kInterpreterDispatchTableRegister,
+ Operand(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.
__ Ldr(kContextRegister,
MemOperand(kInterpreterRegisterFileRegister,
InterpreterFrameConstants::kContextFromRegisterPointer));
// Get the bytecode array pointer from the frame.
- __ Ldr(x1,
- MemOperand(kInterpreterRegisterFileRegister,
- InterpreterFrameConstants::kFunctionFromRegisterPointer));
- __ Ldr(x1, FieldMemOperand(x1, JSFunction::kSharedFunctionInfoOffset));
- __ Ldr(kInterpreterBytecodeArrayRegister,
- FieldMemOperand(x1, SharedFunctionInfo::kFunctionDataOffset));
+ __ Ldr(
+ kInterpreterBytecodeArrayRegister,
+ MemOperand(kInterpreterRegisterFileRegister,
+ InterpreterFrameConstants::kBytecodeArrayFromRegisterPointer));
if (FLAG_debug_code) {
// Check function data field is actually a BytecodeArray object.
@@ -1066,6 +1024,29 @@
}
+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.
+ __ Mov(x1, Operand(Smi::FromInt(static_cast<int>(type))));
+ __ Push(x1);
+ __ 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.
+ __ Drop(1);
+ __ Pop(kInterpreterAccumulatorRegister);
+
+ // Enter the bytecode dispatch.
+ Generate_EnterBytecodeDispatch(masm);
+}
+
+
void Builtins::Generate_InterpreterNotifyDeoptimized(MacroAssembler* masm) {
Generate_InterpreterNotifyDeoptimizedHelper(masm, Deoptimizer::EAGER);
}
@@ -1080,22 +1061,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.
+ __ LoadObject(lr, 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);
}
@@ -1321,14 +1310,11 @@
// Load the next prototype.
__ Bind(&next_prototype);
- __ Ldr(receiver, FieldMemOperand(map, Map::kPrototypeOffset));
- // End if the prototype is null or not hidden.
- __ CompareRoot(receiver, Heap::kNullValueRootIndex);
- __ B(eq, receiver_check_failed);
- __ Ldr(map, FieldMemOperand(receiver, HeapObject::kMapOffset));
__ Ldr(x16, FieldMemOperand(map, Map::kBitField3Offset));
- __ Tst(x16, Operand(Map::IsHiddenPrototype::kMask));
+ __ Tst(x16, Operand(Map::HasHiddenPrototype::kMask));
__ B(eq, receiver_check_failed);
+ __ Ldr(receiver, FieldMemOperand(map, Map::kPrototypeOffset));
+ __ Ldr(map, FieldMemOperand(receiver, HeapObject::kMapOffset));
// Iterate.
__ B(&prototype_loop_start);
@@ -1868,10 +1854,8 @@
// Try to create the list from an arguments object.
__ Bind(&create_arguments);
- __ Ldrsw(len, UntagSmiFieldMemOperand(
- arguments_list,
- JSObject::kHeaderSize +
- Heap::kArgumentsLengthIndex * kPointerSize));
+ __ Ldrsw(len, UntagSmiFieldMemOperand(arguments_list,
+ JSArgumentsObject::kLengthOffset));
__ Ldr(x10, FieldMemOperand(arguments_list, JSObject::kElementsOffset));
__ Ldrsw(x11, UntagSmiFieldMemOperand(x10, FixedArray::kLengthOffset));
__ CompareAndBranch(len, x11, ne, &create_runtime);
@@ -1953,10 +1937,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 <- sp (f()'s caller pc is not on the stack yet!)
+// ----------------------
+//
+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());
+ __ Mov(scratch1, Operand(debug_is_active));
+ __ Ldrb(scratch1, MemOperand(scratch1));
+ __ Cmp(scratch1, Operand(0));
+ __ B(ne, &done);
+
+ // Drop possible interpreter handler/stub frame.
+ {
+ Label no_interpreter_frame;
+ __ Ldr(scratch3, MemOperand(fp, StandardFrameConstants::kMarkerOffset));
+ __ Cmp(scratch3, Operand(Smi::FromInt(StackFrame::STUB)));
+ __ B(ne, &no_interpreter_frame);
+ __ Ldr(fp, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
+ __ bind(&no_interpreter_frame);
+ }
+
+ // Check if next frame is an arguments adaptor frame.
+ Label no_arguments_adaptor, formal_parameter_count_loaded;
+ __ Ldr(scratch2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
+ __ Ldr(scratch3,
+ MemOperand(scratch2, StandardFrameConstants::kContextOffset));
+ __ Cmp(scratch3, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
+ __ B(ne, &no_arguments_adaptor);
+
+ // Drop arguments adaptor frame and load arguments count.
+ __ mov(fp, scratch2);
+ __ Ldr(scratch1,
+ MemOperand(fp, ArgumentsAdaptorFrameConstants::kLengthOffset));
+ __ SmiUntag(scratch1);
+ __ B(&formal_parameter_count_loaded);
+
+ __ bind(&no_arguments_adaptor);
+ // Load caller's formal parameter count
+ __ Ldr(scratch1, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
+ __ Ldr(scratch1,
+ FieldMemOperand(scratch1, JSFunction::kSharedFunctionInfoOffset));
+ __ Ldrsw(scratch1,
+ FieldMemOperand(scratch1,
+ SharedFunctionInfo::kFormalParameterCountOffset));
+ __ bind(&formal_parameter_count_loaded);
+
+ // Calculate the end of destination area where we will put the arguments
+ // after we drop current frame. We add kPointerSize to count the receiver
+ // argument which is not included into formal parameters count.
+ Register dst_reg = scratch2;
+ __ add(dst_reg, fp, Operand(scratch1, LSL, kPointerSizeLog2));
+ __ add(dst_reg, dst_reg,
+ Operand(StandardFrameConstants::kCallerSPOffset + kPointerSize));
+
+ Register src_reg = scratch1;
+ __ add(src_reg, jssp, Operand(args_reg, LSL, kPointerSizeLog2));
+ // Count receiver argument as well (not included in args_reg).
+ __ add(src_reg, src_reg, Operand(kPointerSize));
+
+ if (FLAG_debug_code) {
+ __ Cmp(src_reg, dst_reg);
+ __ Check(lo, kStackAccessBelowStackPointer);
+ }
+
+ // Restore caller's frame pointer and return address now as they will be
+ // overwritten by the copying loop.
+ __ Ldr(lr, MemOperand(fp, StandardFrameConstants::kCallerPCOffset));
+ __ Ldr(fp, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
+
+ // Now copy callee arguments to the caller frame going backwards to avoid
+ // callee arguments corruption (source and destination areas could overlap).
+
+ // Both src_reg and dst_reg are pointing to the word after the one to copy,
+ // so they must be pre-decremented in the loop.
+ Register tmp_reg = scratch3;
+ Label loop, entry;
+ __ B(&entry);
+ __ bind(&loop);
+ __ Ldr(tmp_reg, MemOperand(src_reg, -kPointerSize, PreIndex));
+ __ Str(tmp_reg, MemOperand(dst_reg, -kPointerSize, PreIndex));
+ __ bind(&entry);
+ __ Cmp(jssp, src_reg);
+ __ B(ne, &loop);
+
+ // Leave current frame.
+ __ Mov(jssp, dst_reg);
+ __ SetStackPointer(jssp);
+ __ AssertStackConsistency();
+
+ __ bind(&done);
+}
+} // namespace
// static
void Builtins::Generate_CallFunction(MacroAssembler* masm,
- ConvertReceiverMode mode) {
+ ConvertReceiverMode mode,
+ TailCallMode tail_call_mode) {
ASM_LOCATION("Builtins::Generate_CallFunction");
// ----------- S t a t e -------------
// -- x0 : the number of arguments (not including the receiver)
@@ -2044,6 +2154,10 @@
// -- cp : the function context.
// -----------------------------------
+ if (tail_call_mode == TailCallMode::kAllow) {
+ PrepareForTailCall(masm, x0, x3, x4, x5);
+ }
+
__ Ldrsw(
x2, FieldMemOperand(x2, SharedFunctionInfo::kFormalParameterCountOffset));
ParameterCount actual(x0);
@@ -2140,13 +2254,18 @@
// static
-void Builtins::Generate_CallBoundFunction(MacroAssembler* masm) {
+void Builtins::Generate_CallBoundFunctionImpl(MacroAssembler* masm,
+ TailCallMode tail_call_mode) {
// ----------- S t a t e -------------
// -- x0 : the number of arguments (not including the receiver)
// -- x1 : the function to call (checked to be a JSBoundFunction)
// -----------------------------------
__ AssertBoundFunction(x1);
+ if (tail_call_mode == TailCallMode::kAllow) {
+ PrepareForTailCall(masm, x0, x3, x4, x5);
+ }
+
// Patch the receiver to [[BoundThis]].
__ Ldr(x10, FieldMemOperand(x1, JSBoundFunction::kBoundThisOffset));
__ Poke(x10, Operand(x0, LSL, kPointerSizeLog2));
@@ -2165,7 +2284,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 -------------
// -- x0 : the number of arguments (not including the receiver)
// -- x1 : the target to call (can be any Object).
@@ -2175,14 +2295,24 @@
__ JumpIfSmi(x1, &non_callable);
__ Bind(&non_smi);
__ CompareObjectType(x1, x4, x5, JS_FUNCTION_TYPE);
- __ Jump(masm->isolate()->builtins()->CallFunction(mode),
+ __ Jump(masm->isolate()->builtins()->CallFunction(mode, tail_call_mode),
RelocInfo::CODE_TARGET, eq);
__ Cmp(x5, JS_BOUND_FUNCTION_TYPE);
- __ Jump(masm->isolate()->builtins()->CallBoundFunction(),
+ __ Jump(masm->isolate()->builtins()->CallBoundFunction(tail_call_mode),
RelocInfo::CODE_TARGET, eq);
+
+ // Check if target has a [[Call]] internal method.
+ __ Ldrb(x4, FieldMemOperand(x4, Map::kBitFieldOffset));
+ __ TestAndBranchIfAllClear(x4, 1 << Map::kIsCallable, &non_callable);
+
__ Cmp(x5, JS_PROXY_TYPE);
__ B(ne, &non_function);
+ // 0. Prepare for tail call if necessary.
+ if (tail_call_mode == TailCallMode::kAllow) {
+ PrepareForTailCall(masm, x0, x3, x4, x5);
+ }
+
// 1. Runtime fallback for Proxy [[Call]].
__ Push(x1);
// Increase the arguments size to include the pushed function and the
@@ -2195,15 +2325,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.
- __ Ldrb(x4, FieldMemOperand(x4, Map::kBitFieldOffset));
- __ TestAndBranchIfAllClear(x4, 1 << Map::kIsCallable, &non_callable);
// Overwrite the original receiver with the (original) target.
__ Poke(x1, Operand(x0, LSL, kXRegSizeLog2));
// Let the "call_as_function_delegate" take care of the rest.
__ LoadNativeContextSlot(Context::CALL_AS_FUNCTION_DELEGATE_INDEX, x1);
__ Jump(masm->isolate()->builtins()->CallFunction(
- ConvertReceiverMode::kNotNullOrUndefined),
+ ConvertReceiverMode::kNotNullOrUndefined, tail_call_mode),
RelocInfo::CODE_TARGET);
// 3. Call to something that is not callable.
@@ -2341,7 +2468,8 @@
// static
-void Builtins::Generate_InterpreterPushArgsAndCall(MacroAssembler* masm) {
+void Builtins::Generate_InterpreterPushArgsAndCallImpl(
+ MacroAssembler* masm, TailCallMode tail_call_mode) {
// ----------- S t a t e -------------
// -- x0 : the number of arguments (not including the receiver)
// -- x2 : the address of the first argument to be pushed. Subsequent
@@ -2369,7 +2497,9 @@
__ B(gt, &loop_header);
// Call the target.
- __ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET);
+ __ Jump(masm->isolate()->builtins()->Call(ConvertReceiverMode::kAny,
+ tail_call_mode),
+ RelocInfo::CODE_TARGET);
}