diff options
Diffstat (limited to 'deps/v8/src/arm64/builtins-arm64.cc')
-rw-r--r-- | deps/v8/src/arm64/builtins-arm64.cc | 1817 |
1 files changed, 1182 insertions, 635 deletions
diff --git a/deps/v8/src/arm64/builtins-arm64.cc b/deps/v8/src/arm64/builtins-arm64.cc index f7ea89d807..b6bae4ad0e 100644 --- a/deps/v8/src/arm64/builtins-arm64.cc +++ b/deps/v8/src/arm64/builtins-arm64.cc @@ -20,25 +20,16 @@ namespace internal { // Load the built-in Array function from the current context. static void GenerateLoadArrayFunction(MacroAssembler* masm, Register result) { - // Load the native context. - __ Ldr(result, GlobalObjectMemOperand()); - __ Ldr(result, FieldMemOperand(result, JSGlobalObject::kNativeContextOffset)); // Load the InternalArray function from the native context. - __ Ldr(result, - MemOperand(result, - Context::SlotOffset(Context::ARRAY_FUNCTION_INDEX))); + __ LoadNativeContextSlot(Context::ARRAY_FUNCTION_INDEX, result); } // Load the built-in InternalArray function from the current context. static void GenerateLoadInternalArrayFunction(MacroAssembler* masm, Register result) { - // Load the native context. - __ Ldr(result, GlobalObjectMemOperand()); - __ Ldr(result, FieldMemOperand(result, JSGlobalObject::kNativeContextOffset)); // Load the InternalArray function from the native context. - __ Ldr(result, ContextMemOperand(result, - Context::INTERNAL_ARRAY_FUNCTION_INDEX)); + __ LoadNativeContextSlot(Context::INTERNAL_ARRAY_FUNCTION_INDEX, result); } @@ -47,9 +38,8 @@ void Builtins::Generate_Adaptor(MacroAssembler* masm, BuiltinExtraArguments extra_args) { // ----------- S t a t e ------------- // -- x0 : number of arguments excluding receiver - // (only guaranteed when the called function - // is not marked as DontAdaptArguments) - // -- x1 : called function + // -- x1 : target + // -- x3 : new target // -- sp[0] : last argument // -- ... // -- sp[4 * (argc - 1)] : first argument @@ -61,27 +51,29 @@ void Builtins::Generate_Adaptor(MacroAssembler* masm, // ConstructStubs implemented in C++ will be run in the context of the caller // instead of the callee, due to the way that [[Construct]] is defined for // ordinary functions). - // TODO(bmeurer): Can we make this more robust? __ Ldr(cp, FieldMemOperand(x1, JSFunction::kContextOffset)); // Insert extra arguments. int num_extra_args = 0; - if (extra_args == NEEDS_CALLED_FUNCTION) { - num_extra_args = 1; - __ Push(x1); - } else { - DCHECK(extra_args == NO_EXTRA_ARGUMENTS); + switch (extra_args) { + case BuiltinExtraArguments::kTarget: + __ Push(x1); + ++num_extra_args; + break; + case BuiltinExtraArguments::kNewTarget: + __ Push(x3); + ++num_extra_args; + break; + case BuiltinExtraArguments::kTargetAndNewTarget: + __ Push(x1, x3); + num_extra_args += 2; + break; + case BuiltinExtraArguments::kNone: + break; } // JumpToExternalReference expects x0 to contain the number of arguments - // including the receiver and the extra arguments. But x0 is only valid - // if the called function is marked as DontAdaptArguments, otherwise we - // need to load the argument count from the SharedFunctionInfo. - __ Ldr(x2, FieldMemOperand(x1, JSFunction::kSharedFunctionInfoOffset)); - __ Ldrsw( - x2, FieldMemOperand(x2, SharedFunctionInfo::kFormalParameterCountOffset)); - __ Cmp(x2, SharedFunctionInfo::kDontAdaptArgumentsSentinel); - __ Csel(x0, x0, x2, eq); + // including the receiver and the extra arguments. __ Add(x0, x0, num_extra_args + 1); __ JumpToExternalReference(ExternalReference(id, masm->isolate())); @@ -146,6 +138,107 @@ void Builtins::Generate_ArrayCode(MacroAssembler* masm) { // static +void Builtins::Generate_NumberConstructor(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- x0 : number of arguments + // -- x1 : constructor function + // -- lr : return address + // -- sp[(argc - n - 1) * 8] : arg[n] (zero based) + // -- sp[argc * 8] : receiver + // ----------------------------------- + ASM_LOCATION("Builtins::Generate_NumberConstructor"); + + // 1. Load the first argument into x0 and get rid of the rest (including the + // receiver). + Label no_arguments; + { + __ Cbz(x0, &no_arguments); + __ Sub(x0, x0, 1); + __ Drop(x0); + __ Ldr(x0, MemOperand(jssp, 2 * kPointerSize, PostIndex)); + } + + // 2a. Convert first argument to number. + ToNumberStub stub(masm->isolate()); + __ TailCallStub(&stub); + + // 2b. No arguments, return +0 (already in x0). + __ Bind(&no_arguments); + __ Drop(1); + __ Ret(); +} + + +// static +void Builtins::Generate_NumberConstructor_ConstructStub(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- x0 : number of arguments + // -- x1 : constructor function + // -- x3 : new target + // -- lr : return address + // -- sp[(argc - n - 1) * 8] : arg[n] (zero based) + // -- sp[argc * 8] : receiver + // ----------------------------------- + ASM_LOCATION("Builtins::Generate_NumberConstructor_ConstructStub"); + + // 1. Make sure we operate in the context of the called function. + __ Ldr(cp, FieldMemOperand(x1, JSFunction::kContextOffset)); + + // 2. Load the first argument into x2 and get rid of the rest (including the + // receiver). + { + Label no_arguments, done; + __ Cbz(x0, &no_arguments); + __ Sub(x0, x0, 1); + __ Drop(x0); + __ Ldr(x2, MemOperand(jssp, 2 * kPointerSize, PostIndex)); + __ B(&done); + __ Bind(&no_arguments); + __ Drop(1); + __ Mov(x2, Smi::FromInt(0)); + __ Bind(&done); + } + + // 3. Make sure x2 is a number. + { + Label done_convert; + __ JumpIfSmi(x2, &done_convert); + __ JumpIfObjectType(x2, x4, x4, HEAP_NUMBER_TYPE, &done_convert, eq); + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ Push(x1, x3); + __ Move(x0, x2); + ToNumberStub stub(masm->isolate()); + __ CallStub(&stub); + __ Move(x2, x0); + __ Pop(x3, x1); + } + __ Bind(&done_convert); + } + + // 4. Check if new target and constructor differ. + Label new_object; + __ Cmp(x1, x3); + __ B(ne, &new_object); + + // 5. Allocate a JSValue wrapper for the number. + __ AllocateJSValue(x0, x1, x2, x4, x5, &new_object); + __ Ret(); + + // 6. Fallback to the runtime to create new object. + __ bind(&new_object); + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ Push(x2, x1, x3); // first argument, constructor, new target + __ CallRuntime(Runtime::kNewObject); + __ Pop(x2); + } + __ Str(x2, FieldMemOperand(x0, JSValue::kValueOffset)); + __ Ret(); +} + + +// static void Builtins::Generate_StringConstructor(MacroAssembler* masm) { // ----------- S t a t e ------------- // -- x0 : number of arguments @@ -197,7 +290,7 @@ void Builtins::Generate_StringConstructor(MacroAssembler* masm) { __ Bind(&symbol_descriptive_string); { __ Push(x0); - __ TailCallRuntime(Runtime::kSymbolDescriptiveString, 1, 1); + __ TailCallRuntime(Runtime::kSymbolDescriptiveString); } } @@ -207,14 +300,17 @@ void Builtins::Generate_StringConstructor_ConstructStub(MacroAssembler* masm) { // ----------- S t a t e ------------- // -- x0 : number of arguments // -- x1 : constructor function - // -- x3 : original constructor + // -- x3 : new target // -- lr : return address // -- sp[(argc - n - 1) * 8] : arg[n] (zero based) // -- sp[argc * 8] : receiver // ----------------------------------- ASM_LOCATION("Builtins::Generate_StringConstructor_ConstructStub"); - // 1. Load the first argument into x2 and get rid of the rest (including the + // 1. Make sure we operate in the context of the called function. + __ Ldr(cp, FieldMemOperand(x1, JSFunction::kContextOffset)); + + // 2. Load the first argument into x2 and get rid of the rest (including the // receiver). { Label no_arguments, done; @@ -229,7 +325,7 @@ void Builtins::Generate_StringConstructor_ConstructStub(MacroAssembler* masm) { __ Bind(&done); } - // 2. Make sure x2 is a string. + // 3. Make sure x2 is a string. { Label convert, done_convert; __ JumpIfSmi(x2, &convert); @@ -242,75 +338,49 @@ void Builtins::Generate_StringConstructor_ConstructStub(MacroAssembler* masm) { __ Move(x0, x2); __ CallStub(&stub); __ Move(x2, x0); - __ Pop(x1, x3); + __ Pop(x3, x1); } __ Bind(&done_convert); } - // 3. Allocate a JSValue wrapper for the string. - { - // ----------- S t a t e ------------- - // -- x2 : the first argument - // -- x1 : constructor function - // -- x3 : original constructor - // -- lr : return address - // ----------------------------------- - - Label allocate, done_allocate, rt_call; + // 4. Check if new target and constructor differ. + Label new_object; + __ Cmp(x1, x3); + __ B(ne, &new_object); - // Fall back to runtime if the original constructor and function differ. - __ cmp(x1, x3); - __ B(ne, &rt_call); - - __ Allocate(JSValue::kSize, x0, x3, x4, &allocate, TAG_OBJECT); - __ Bind(&done_allocate); - - // Initialize the JSValue in eax. - __ LoadGlobalFunctionInitialMap(x1, x3, x4); - __ Str(x3, FieldMemOperand(x0, HeapObject::kMapOffset)); - __ LoadRoot(x3, Heap::kEmptyFixedArrayRootIndex); - __ Str(x3, FieldMemOperand(x0, JSObject::kPropertiesOffset)); - __ Str(x3, FieldMemOperand(x0, JSObject::kElementsOffset)); - __ Str(x2, FieldMemOperand(x0, JSValue::kValueOffset)); - STATIC_ASSERT(JSValue::kSize == 4 * kPointerSize); - __ Ret(); - - // Fallback to the runtime to allocate in new space. - __ Bind(&allocate); - { - FrameScope scope(masm, StackFrame::INTERNAL); - __ Push(x1, x2); - __ Push(Smi::FromInt(JSValue::kSize)); - __ CallRuntime(Runtime::kAllocateInNewSpace, 1); - __ Pop(x2, x1); - } - __ B(&done_allocate); + // 5. Allocate a JSValue wrapper for the string. + __ AllocateJSValue(x0, x1, x2, x4, x5, &new_object); + __ Ret(); - // Fallback to the runtime to create new object. - __ bind(&rt_call); - { - FrameScope scope(masm, StackFrame::INTERNAL); - __ Push(x1, x2, x1, x3); // constructor function, original constructor - __ CallRuntime(Runtime::kNewObject, 2); - __ Pop(x2, x1); - } - __ Str(x2, FieldMemOperand(x0, JSValue::kValueOffset)); - __ Ret(); + // 6. Fallback to the runtime to create new object. + __ bind(&new_object); + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ Push(x2, x1, x3); // first argument, constructor, new target + __ CallRuntime(Runtime::kNewObject); + __ 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 function onto the stack. - // - Push another copy as a parameter to the runtime call. - __ Push(x1, x1); + // 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 receiver. - __ Pop(x1); + // Restore target function and new target. + __ Pop(x3, x1); } @@ -347,12 +417,13 @@ void Builtins::Generate_InOptimizationQueue(MacroAssembler* masm) { static void Generate_JSConstructStubHelper(MacroAssembler* masm, - bool is_api_function) { + bool is_api_function, + bool create_implicit_receiver) { // ----------- S t a t e ------------- // -- x0 : number of arguments // -- x1 : constructor function // -- x2 : allocation site or undefined - // -- x3 : original constructor + // -- x3 : new target // -- lr : return address // -- sp[...]: constructor arguments // ----------------------------------- @@ -369,194 +440,173 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm, Register argc = x0; Register constructor = x1; Register allocation_site = x2; - Register original_constructor = x3; + Register new_target = x3; // Preserve the incoming parameters on the stack. __ AssertUndefinedOrAllocationSite(allocation_site, x10); __ SmiTag(argc); - __ Push(allocation_site, argc, constructor, original_constructor); - // 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) { - ExternalReference debug_step_in_fp = - ExternalReference::debug_step_in_fp_address(isolate); - __ Mov(x2, Operand(debug_step_in_fp)); - __ Ldr(x2, MemOperand(x2)); - __ Cbnz(x2, &rt_call); - - // Verify that the original constructor is a JSFunction. - __ JumpIfNotObjectType(original_constructor, 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(original_constructor, - 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); - - Register constructon_count = x14; - if (!is_api_function) { - Label allocate; - MemOperand bit_field3 = - FieldMemOperand(init_map, Map::kBitField3Offset); - // Check if slack tracking is enabled. - __ Ldr(x4, bit_field3); - __ DecodeField<Map::Counter>(constructon_count, x4); - __ Cmp(constructon_count, Operand(Map::kSlackTrackingCounterEnd)); - __ B(lt, &allocate); - // Decrease generous allocation count. - __ Subs(x4, x4, Operand(1 << Map::Counter::kShift)); - __ Str(x4, bit_field3); - __ Cmp(constructon_count, Operand(Map::kSlackTrackingCounterEnd)); - __ B(ne, &allocate); - - // Push the constructor and map to the stack, and the map again - // as argument to the runtime call. - __ Push(constructor, init_map, init_map); - __ CallRuntime(Runtime::kFinalizeInstanceSize, 1); - __ Pop(init_map, constructor); - __ Mov(constructon_count, Operand(Map::kSlackTrackingCounterEnd - 1)); - __ Bind(&allocate); - } - - // Now allocate the JSObject on the heap. - Label rt_call_reload_new_target; - Register obj_size = x3; - Register new_obj = x4; - __ Ldrb(obj_size, FieldMemOperand(init_map, Map::kInstanceSizeOffset)); - __ Allocate(obj_size, new_obj, x10, x11, &rt_call_reload_new_target, - 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 empty = x5; - __ LoadRoot(empty, Heap::kEmptyFixedArrayRootIndex); - __ Str(init_map, MemOperand(new_obj, JSObject::kMapOffset)); - STATIC_ASSERT(JSObject::kElementsOffset == - (JSObject::kPropertiesOffset + kPointerSize)); - __ Stp(empty, empty, MemOperand(new_obj, JSObject::kPropertiesOffset)); - - Register first_prop = x5; - __ Add(first_prop, new_obj, JSObject::kHeaderSize); - - // Fill all of the in-object properties with the appropriate filler. - Register filler = x7; - __ LoadRoot(filler, Heap::kUndefinedValueRootIndex); - - // Obtain number of pre-allocated property fields and in-object - // properties. - Register unused_props = x10; - Register inobject_props = x11; - Register inst_sizes_or_attrs = x11; - Register prealloc_fields = x10; - __ Ldr(inst_sizes_or_attrs, - FieldMemOperand(init_map, Map::kInstanceAttributesOffset)); - __ Ubfx(unused_props, inst_sizes_or_attrs, - Map::kUnusedPropertyFieldsByte * kBitsPerByte, kBitsPerByte); - __ Ldr(inst_sizes_or_attrs, - FieldMemOperand(init_map, Map::kInstanceSizesOffset)); - __ Ubfx( - inobject_props, inst_sizes_or_attrs, - Map::kInObjectPropertiesOrConstructorFunctionIndexByte * kBitsPerByte, - kBitsPerByte); - __ Sub(prealloc_fields, inobject_props, unused_props); - - // Calculate number of property fields in the object. - Register prop_fields = x6; - __ Sub(prop_fields, obj_size, JSObject::kHeaderSize / kPointerSize); - - if (!is_api_function) { - Label no_inobject_slack_tracking; - - // Check if slack tracking is enabled. - __ Cmp(constructon_count, Operand(Map::kSlackTrackingCounterEnd)); - __ B(lt, &no_inobject_slack_tracking); - constructon_count = NoReg; - - // Fill the pre-allocated fields with undef. - __ FillFields(first_prop, prealloc_fields, filler); - - // Update first_prop register to be the offset of the first field after - // pre-allocated fields. - __ Add(first_prop, first_prop, - Operand(prealloc_fields, LSL, kPointerSizeLog2)); - - if (FLAG_debug_code) { - Register obj_end = x14; - __ Add(obj_end, new_obj, Operand(obj_size, LSL, kPointerSizeLog2)); - __ Cmp(first_prop, obj_end); - __ Assert(le, kUnexpectedNumberOfPreAllocatedPropertyFields); + __ 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); } - // Fill the remaining fields with one pointer filler map. - __ LoadRoot(filler, Heap::kOnePointerFillerMapRootIndex); - __ Sub(prop_fields, prop_fields, prealloc_fields); + __ InitializeFieldsWithFiller(write_address, next_obj, filler); - __ bind(&no_inobject_slack_tracking); + // Continue with JSObject being successfully allocated. + __ B(&allocated); } - // Fill all of the property fields with undef. - __ FillFields(first_prop, prop_fields, filler); - first_prop = NoReg; - prop_fields = NoReg; - - // 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); - - // Continue with JSObject being successfully allocated. - __ B(&allocated); - - // Reload the original constructor and fall-through. - __ Bind(&rt_call_reload_new_target); - __ Peek(x3, 0 * kXRegSize); + // 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); + __ Mov(x4, x0); + __ Pop(new_target, constructor); + + // Receiver for constructor call allocated. + // x1: constructor function + // x3: new target + // x4: JSObject + __ Bind(&allocated); + + // Reload the number of arguments from the stack. + // Set it up in x0 for the function call below. + // jssp[0]: number of arguments (smi-tagged) + __ Peek(argc, 0); // Load number of arguments. } - // Allocate the new receiver object using the runtime call. - // x1: constructor function - // x3: original constructor - __ Bind(&rt_call); - __ Push(constructor, original_constructor); // arguments 1-2 - __ CallRuntime(Runtime::kNewObject, 2); - __ Mov(x4, x0); - - // Receiver for constructor call allocated. - // x4: JSObject - __ Bind(&allocated); - - // Restore the parameters. - __ Pop(original_constructor); - __ Pop(constructor); - - // Reload the number of arguments from the stack. - // Set it up in x0 for the function call below. - // jssp[0]: number of arguments (smi-tagged) - __ Peek(argc, 0); // Load number of arguments. __ SmiUntag(argc); - __ Push(original_constructor, x4, x4); + if (create_implicit_receiver) { + // Push the allocated receiver to the stack. We need two copies + // because we may have to return the original one and the calling + // conventions dictate that the called function pops the receiver. + __ Push(x4, x4); + } else { + __ PushRoot(Heap::kTheHoleValueRootIndex); + } // Set up pointer to last argument. __ Add(x2, fp, StandardFrameConstants::kCallerSPOffset); @@ -566,19 +616,19 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm, // x0: number of arguments // x1: constructor function // x2: address of last argument (caller sp) + // x3: new target // jssp[0]: receiver // jssp[1]: receiver - // jssp[2]: new.target - // jssp[3]: number of arguments (smi-tagged) + // jssp[2]: number of arguments (smi-tagged) // Compute the start address of the copy in x3. - __ Add(x3, x2, Operand(argc, LSL, kPointerSizeLog2)); + __ Add(x4, x2, Operand(argc, LSL, kPointerSizeLog2)); Label loop, entry, done_copying_arguments; __ B(&entry); __ Bind(&loop); - __ Ldp(x10, x11, MemOperand(x3, -2 * kPointerSize, PreIndex)); + __ Ldp(x10, x11, MemOperand(x4, -2 * kPointerSize, PreIndex)); __ Push(x11, x10); __ Bind(&entry); - __ Cmp(x3, x2); + __ Cmp(x4, x2); __ B(gt, &loop); // Because we copied values 2 by 2 we may have copied one extra value. // Drop it if that is the case. @@ -589,6 +639,7 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm, // Call the function. // x0: number of arguments // x1: constructor function + // x3: new target if (is_api_function) { __ Ldr(cp, FieldMemOperand(constructor, JSFunction::kContextOffset)); Handle<Code> code = @@ -596,155 +647,84 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm, __ Call(code, RelocInfo::CODE_TARGET); } else { ParameterCount actual(argc); - __ InvokeFunction(constructor, actual, CALL_FUNCTION, NullCallWrapper()); + __ InvokeFunction(constructor, new_target, actual, CALL_FUNCTION, + CheckDebugStepCallWrapper()); } // Store offset of return address for deoptimizer. - if (!is_api_function) { + if (create_implicit_receiver && !is_api_function) { masm->isolate()->heap()->SetConstructStubDeoptPCOffset(masm->pc_offset()); } // Restore the context from the frame. // x0: result // jssp[0]: receiver - // jssp[1]: new.target - // jssp[2]: number of arguments (smi-tagged) - __ Ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); - - // If the result is an object (in the ECMA sense), we should get rid - // of the receiver and use the result; see ECMA-262 section 13.2.2-7 - // on page 74. - Label use_receiver, exit; - - // If the result is a smi, it is *not* an object in the ECMA sense. - // x0: result - // jssp[0]: receiver (newly allocated object) // jssp[1]: number of arguments (smi-tagged) - __ JumpIfSmi(x0, &use_receiver); - - // If the type of the result (stored in its map) is less than - // FIRST_SPEC_OBJECT_TYPE, it is not an object in the ECMA sense. - __ JumpIfObjectType(x0, x1, x3, FIRST_SPEC_OBJECT_TYPE, &exit, ge); - - // Throw away the result of the constructor invocation and use the - // on-stack receiver as the result. - __ Bind(&use_receiver); - __ Peek(x0, 0); + __ Ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); - // Remove the receiver from the stack, remove caller arguments, and - // return. - __ Bind(&exit); - // x0: result - // jssp[0]: receiver (newly allocated object) - // jssp[1]: new.target (original constructor) - // jssp[2]: number of arguments (smi-tagged) - __ Peek(x1, 2 * kXRegSize); + if (create_implicit_receiver) { + // If the result is an object (in the ECMA sense), we should get rid + // of the receiver and use the result; see ECMA-262 section 13.2.2-7 + // on page 74. + Label use_receiver, exit; + + // If the result is a smi, it is *not* an object in the ECMA sense. + // x0: result + // jssp[0]: receiver (newly allocated object) + // jssp[1]: number of arguments (smi-tagged) + __ JumpIfSmi(x0, &use_receiver); + + // If the type of the result (stored in its map) is less than + // FIRST_JS_RECEIVER_TYPE, it is not an object in the ECMA sense. + __ JumpIfObjectType(x0, x1, x3, FIRST_JS_RECEIVER_TYPE, &exit, ge); + + // Throw away the result of the constructor invocation and use the + // on-stack receiver as the result. + __ Bind(&use_receiver); + __ Peek(x0, 0); + + // Remove the receiver from the stack, remove caller arguments, and + // return. + __ Bind(&exit); + // x0: result + // jssp[0]: receiver (newly allocated object) + // jssp[1]: number of arguments (smi-tagged) + __ Peek(x1, 1 * kXRegSize); + } else { + __ Peek(x1, 0); + } // Leave construct frame. } __ DropBySMI(x1); __ Drop(1); - __ IncrementCounter(isolate->counters()->constructed_objects(), 1, x1, x2); + if (create_implicit_receiver) { + __ IncrementCounter(isolate->counters()->constructed_objects(), 1, x1, x2); + } __ Ret(); } void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) { - Generate_JSConstructStubHelper(masm, false); + Generate_JSConstructStubHelper(masm, false, true); } void Builtins::Generate_JSConstructStubApi(MacroAssembler* masm) { - Generate_JSConstructStubHelper(masm, true); + Generate_JSConstructStubHelper(masm, true, true); } -void Builtins::Generate_JSConstructStubForDerived(MacroAssembler* masm) { - // ----------- S t a t e ------------- - // -- x0 : number of arguments - // -- x1 : constructor function - // -- x2 : allocation site or undefined - // -- x3 : original constructor - // -- lr : return address - // -- sp[...]: constructor arguments - // ----------------------------------- - ASM_LOCATION("Builtins::Generate_JSConstructStubForDerived"); - - { - FrameScope frame_scope(masm, StackFrame::CONSTRUCT); - - __ AssertUndefinedOrAllocationSite(x2, x10); - __ Mov(x4, x0); - __ SmiTag(x4); - __ LoadRoot(x10, Heap::kTheHoleValueRootIndex); - __ Push(x2, x4, x3, x10); - // sp[0]: receiver (the hole) - // sp[1]: new.target - // sp[2]: number of arguments - // sp[3]: allocation site - - // Set up pointer to last argument. - __ Add(x2, fp, StandardFrameConstants::kCallerSPOffset); - - // Copy arguments and receiver to the expression stack. - // Copy 2 values every loop to use ldp/stp. - // x0: number of arguments - // x1: constructor function - // x2: address of last argument (caller sp) - // jssp[0]: receiver - // jssp[1]: new.target - // jssp[2]: number of arguments (smi-tagged) - // Compute the start address of the copy in x4. - __ Add(x4, x2, Operand(x0, LSL, kPointerSizeLog2)); - Label loop, entry, done_copying_arguments; - __ B(&entry); - __ Bind(&loop); - __ Ldp(x10, x11, MemOperand(x4, -2 * kPointerSize, PreIndex)); - __ Push(x11, x10); - __ Bind(&entry); - __ Cmp(x4, x2); - __ B(gt, &loop); - // Because we copied values 2 by 2 we may have copied one extra value. - // Drop it if that is the case. - __ B(eq, &done_copying_arguments); - __ Drop(1); - __ Bind(&done_copying_arguments); - - // Handle step in. - Label skip_step_in; - ExternalReference debug_step_in_fp = - ExternalReference::debug_step_in_fp_address(masm->isolate()); - __ Mov(x2, Operand(debug_step_in_fp)); - __ Ldr(x2, MemOperand(x2)); - __ Cbz(x2, &skip_step_in); - - __ Push(x0, x1, x1); - __ CallRuntime(Runtime::kHandleStepInForDerivedConstructors, 1); - __ Pop(x1, x0); - - __ bind(&skip_step_in); - - // Call the function. - // x0: number of arguments - // x1: constructor function - ParameterCount actual(x0); - __ InvokeFunction(x1, actual, CALL_FUNCTION, NullCallWrapper()); - - // Restore the context from the frame. - // x0: result - // jssp[0]: number of arguments (smi-tagged) - __ Ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); - - // Load number of arguments (smi), skipping over new.target. - __ Peek(x1, kPointerSize); +void Builtins::Generate_JSBuiltinsConstructStub(MacroAssembler* masm) { + Generate_JSConstructStubHelper(masm, false, false); +} - // Leave construct frame - } - __ DropBySMI(x1); - __ Drop(1); - __ Ret(); +void Builtins::Generate_ConstructedNonConstructable(MacroAssembler* masm) { + FrameScope scope(masm, StackFrame::INTERNAL); + __ Push(x1); + __ CallRuntime(Runtime::kThrowConstructedNonConstructable); } @@ -771,7 +751,7 @@ static void Generate_CheckStackOverflow(MacroAssembler* masm, Register argc, __ Cmp(x10, Operand(argc, LSL, kPointerSizeLog2)); } __ B(gt, &enough_stack_space); - __ CallRuntime(Runtime::kThrowStackOverflow, 0); + __ CallRuntime(Runtime::kThrowStackOverflow); // We should never return from the APPLY_OVERFLOW builtin. if (__ emit_debug_code()) { __ Unreachable(); @@ -891,6 +871,7 @@ void Builtins::Generate_JSConstructEntryTrampoline(MacroAssembler* masm) { // // The live registers are: // - x1: the JS function object being called. +// - x3: the new target // - cp: our context. // - fp: our caller's frame pointer. // - jssp: stack pointer. @@ -907,6 +888,11 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) { 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. @@ -935,7 +921,7 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) { __ Sub(x10, jssp, Operand(x11)); __ CompareRoot(x10, Heap::kRealStackLimitRootIndex); __ B(hs, &ok); - __ CallRuntime(Runtime::kThrowStackOverflow, 0); + __ CallRuntime(Runtime::kThrowStackOverflow); __ Bind(&ok); // If ok, push undefined as the initial value for all register file entries. @@ -963,7 +949,7 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) { __ CompareRoot(jssp, Heap::kStackLimitRootIndex); __ B(hs, &ok); __ Push(kInterpreterBytecodeArrayRegister); - __ CallRuntime(Runtime::kStackGuard, 0); + __ CallRuntime(Runtime::kStackGuard); __ Pop(kInterpreterBytecodeArrayRegister); __ Bind(&ok); } @@ -971,8 +957,8 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) { // Load accumulator, register file, bytecode offset, dispatch table into // registers. __ LoadRoot(kInterpreterAccumulatorRegister, Heap::kUndefinedValueRootIndex); - __ Sub(kInterpreterRegisterFileRegister, fp, - Operand(kPointerSize + StandardFrameConstants::kFixedFrameSizeFromFp)); + __ Add(kInterpreterRegisterFileRegister, fp, + Operand(InterpreterFrameConstants::kRegisterFilePointerFromFp)); __ Mov(kInterpreterBytecodeOffsetRegister, Operand(BytecodeArray::kHeaderSize - kHeapObjectTag)); __ LoadRoot(kInterpreterDispatchTableRegister, @@ -1012,36 +998,103 @@ void Builtins::Generate_InterpreterExitTrampoline(MacroAssembler* masm) { } -void Builtins::Generate_CompileLazy(MacroAssembler* masm) { - CallRuntimePassFunction(masm, Runtime::kCompileLazy); - GenerateTailCallToReturnedCode(masm); +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); + + // 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)); + + // 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)); + + if (FLAG_debug_code) { + // Check function data field is actually a BytecodeArray object. + __ AssertNotSmi(kInterpreterBytecodeArrayRegister, + kFunctionDataShouldBeBytecodeArrayOnInterpreterEntry); + __ CompareObjectType(kInterpreterBytecodeArrayRegister, x1, x1, + BYTECODE_ARRAY_TYPE); + __ Assert(eq, kFunctionDataShouldBeBytecodeArrayOnInterpreterEntry); + } + + // Get the target bytecode offset from the frame. + __ Ldr(kInterpreterBytecodeOffsetRegister, + MemOperand( + kInterpreterRegisterFileRegister, + InterpreterFrameConstants::kBytecodeOffsetFromRegisterPointer)); + __ SmiUntag(kInterpreterBytecodeOffsetRegister); + + // Dispatch to the target bytecode. + __ Ldrb(x1, MemOperand(kInterpreterBytecodeArrayRegister, + kInterpreterBytecodeOffsetRegister)); + __ Mov(x1, Operand(x1, LSL, kPointerSizeLog2)); + __ Ldr(ip0, MemOperand(kInterpreterDispatchTableRegister, x1)); + __ Add(ip0, ip0, Operand(Code::kHeaderSize - kHeapObjectTag)); + __ Jump(ip0); } -static void CallCompileOptimized(MacroAssembler* masm, bool concurrent) { - FrameScope scope(masm, StackFrame::INTERNAL); - Register function = x1; +void Builtins::Generate_InterpreterNotifyDeoptimized(MacroAssembler* masm) { + Generate_InterpreterNotifyDeoptimizedHelper(masm, Deoptimizer::EAGER); +} + + +void Builtins::Generate_InterpreterNotifySoftDeoptimized(MacroAssembler* masm) { + Generate_InterpreterNotifyDeoptimizedHelper(masm, Deoptimizer::SOFT); +} + - // Preserve function. At the same time, push arguments for - // kCompileOptimized. - __ LoadObject(x10, masm->isolate()->factory()->ToBoolean(concurrent)); - __ Push(function, function, x10); +void Builtins::Generate_InterpreterNotifyLazyDeoptimized(MacroAssembler* masm) { + Generate_InterpreterNotifyDeoptimizedHelper(masm, Deoptimizer::LAZY); +} - __ CallRuntime(Runtime::kCompileOptimized, 2); - // Restore receiver. - __ Pop(function); +void Builtins::Generate_CompileLazy(MacroAssembler* masm) { + CallRuntimePassFunction(masm, Runtime::kCompileLazy); + GenerateTailCallToReturnedCode(masm); } void Builtins::Generate_CompileOptimized(MacroAssembler* masm) { - CallCompileOptimized(masm, false); + CallRuntimePassFunction(masm, Runtime::kCompileOptimized_NotConcurrent); GenerateTailCallToReturnedCode(masm); } void Builtins::Generate_CompileOptimizedConcurrent(MacroAssembler* masm) { - CallCompileOptimized(masm, true); + CallRuntimePassFunction(masm, Runtime::kCompileOptimized_Concurrent); GenerateTailCallToReturnedCode(masm); } @@ -1057,16 +1110,17 @@ static void GenerateMakeCodeYoungAgainCommon(MacroAssembler* masm) { // calling through to the runtime: // x0 - The address from which to resume execution. // x1 - isolate + // x3 - new target // lr - The return address for the JSFunction itself. It has not yet been // preserved on the stack because the frame setup code was replaced // with a call to this stub, to handle code ageing. { FrameScope scope(masm, StackFrame::MANUAL); - __ Push(x0, x1, fp, lr); + __ Push(x0, x1, x3, fp, lr); __ Mov(x1, ExternalReference::isolate_address(masm->isolate())); __ CallCFunction( ExternalReference::get_make_code_young_function(masm->isolate()), 2); - __ Pop(lr, fp, x1, x0); + __ Pop(lr, fp, x3, x1, x0); } // The calling function has been made young again, so return to execute the @@ -1097,17 +1151,18 @@ void Builtins::Generate_MarkCodeAsExecutedOnce(MacroAssembler* masm) { // calling through to the runtime: // x0 - The address from which to resume execution. // x1 - isolate + // x3 - new target // lr - The return address for the JSFunction itself. It has not yet been // preserved on the stack because the frame setup code was replaced // with a call to this stub, to handle code ageing. { FrameScope scope(masm, StackFrame::MANUAL); - __ Push(x0, x1, fp, lr); + __ Push(x0, x1, x3, fp, lr); __ Mov(x1, ExternalReference::isolate_address(masm->isolate())); __ CallCFunction( ExternalReference::get_mark_code_as_executed_function( masm->isolate()), 2); - __ Pop(lr, fp, x1, x0); + __ Pop(lr, fp, x3, x1, x0); // Perform prologue operations usually performed by the young code stub. __ EmitFrameSetupForCodeAgePatching(masm); @@ -1142,7 +1197,7 @@ static void Generate_NotifyStubFailureHelper(MacroAssembler* masm, // preserve the registers with parameters. __ PushXRegList(kSafepointSavedRegisters); // Pass the function and deoptimization type to the runtime system. - __ CallRuntime(Runtime::kNotifyStubFailure, 0, save_doubles); + __ CallRuntime(Runtime::kNotifyStubFailure, save_doubles); __ PopXRegList(kSafepointSavedRegisters); } @@ -1172,7 +1227,7 @@ static void Generate_NotifyDeoptimizedHelper(MacroAssembler* masm, // Pass the deoptimization type to the runtime system. __ Mov(x0, Smi::FromInt(static_cast<int>(type))); __ Push(x0); - __ CallRuntime(Runtime::kNotifyDeoptimized, 1); + __ CallRuntime(Runtime::kNotifyDeoptimized); } // Get the full codegen state from the stack and untag it. @@ -1214,6 +1269,109 @@ void Builtins::Generate_NotifySoftDeoptimized(MacroAssembler* masm) { } +static void CompatibleReceiverCheck(MacroAssembler* masm, Register receiver, + Register function_template_info, + Register scratch0, Register scratch1, + Register scratch2, + Label* receiver_check_failed) { + Register signature = scratch0; + Register map = scratch1; + Register constructor = scratch2; + + // If there is no signature, return the holder. + __ Ldr(signature, FieldMemOperand(function_template_info, + FunctionTemplateInfo::kSignatureOffset)); + __ CompareRoot(signature, Heap::kUndefinedValueRootIndex); + Label receiver_check_passed; + __ B(eq, &receiver_check_passed); + + // Walk the prototype chain. + __ Ldr(map, FieldMemOperand(receiver, HeapObject::kMapOffset)); + Label prototype_loop_start; + __ Bind(&prototype_loop_start); + + // Get the constructor, if any + __ GetMapConstructor(constructor, map, x16, x16); + __ cmp(x16, Operand(JS_FUNCTION_TYPE)); + Label next_prototype; + __ B(ne, &next_prototype); + Register type = constructor; + __ Ldr(type, + FieldMemOperand(constructor, JSFunction::kSharedFunctionInfoOffset)); + __ Ldr(type, FieldMemOperand(type, SharedFunctionInfo::kFunctionDataOffset)); + + // Loop through the chain of inheriting function templates. + Label function_template_loop; + __ Bind(&function_template_loop); + + // If the signatures match, we have a compatible receiver. + __ Cmp(signature, type); + __ B(eq, &receiver_check_passed); + + // If the current type is not a FunctionTemplateInfo, load the next prototype + // in the chain. + __ JumpIfSmi(type, &next_prototype); + __ CompareObjectType(type, x16, x17, FUNCTION_TEMPLATE_INFO_TYPE); + __ B(ne, &next_prototype); + + // Otherwise load the parent function template and iterate. + __ Ldr(type, + FieldMemOperand(type, FunctionTemplateInfo::kParentTemplateOffset)); + __ B(&function_template_loop); + + // 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)); + __ B(eq, receiver_check_failed); + // Iterate. + __ B(&prototype_loop_start); + + __ Bind(&receiver_check_passed); +} + + +void Builtins::Generate_HandleFastApiCall(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- x0 : number of arguments excluding receiver + // -- x1 : callee + // -- lr : return address + // -- sp[0] : last argument + // -- ... + // -- sp[8 * (argc - 1)] : first argument + // -- sp[8 * argc] : receiver + // ----------------------------------- + + // Load the FunctionTemplateInfo. + __ Ldr(x3, FieldMemOperand(x1, JSFunction::kSharedFunctionInfoOffset)); + __ Ldr(x3, FieldMemOperand(x3, SharedFunctionInfo::kFunctionDataOffset)); + + // Do the compatible receiver check. + Label receiver_check_failed; + __ Ldr(x2, MemOperand(jssp, x0, LSL, kPointerSizeLog2)); + CompatibleReceiverCheck(masm, x2, x3, x4, x5, x6, &receiver_check_failed); + + // Get the callback offset from the FunctionTemplateInfo, and jump to the + // beginning of the code. + __ Ldr(x4, FieldMemOperand(x3, FunctionTemplateInfo::kCallCodeOffset)); + __ Ldr(x4, FieldMemOperand(x4, CallHandlerInfo::kFastHandlerOffset)); + __ Add(x4, x4, Operand(Code::kHeaderSize - kHeapObjectTag)); + __ Jump(x4); + + // Compatible receiver check failed: throw an Illegal Invocation exception. + __ Bind(&receiver_check_failed); + // Drop the arguments (including the receiver) + __ add(x0, x0, Operand(1)); + __ Drop(x0); + __ TailCallRuntime(Runtime::kThrowIllegalInvocation); +} + + void Builtins::Generate_OnStackReplacement(MacroAssembler* masm) { // Lookup the function in the JavaScript frame. __ Ldr(x0, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset)); @@ -1221,7 +1379,7 @@ void Builtins::Generate_OnStackReplacement(MacroAssembler* masm) { FrameScope scope(masm, StackFrame::INTERNAL); // Pass function as argument. __ Push(x0); - __ CallRuntime(Runtime::kCompileForOnStackReplacement, 1); + __ CallRuntime(Runtime::kCompileForOnStackReplacement); } // If the code object is null, just return to the unoptimized code. @@ -1257,7 +1415,7 @@ void Builtins::Generate_OsrAfterStackCheck(MacroAssembler* masm) { __ B(hs, &ok); { FrameScope scope(masm, StackFrame::INTERNAL); - __ CallRuntime(Runtime::kStackGuard, 0); + __ CallRuntime(Runtime::kStackGuard); } __ Jump(masm->isolate()->builtins()->OnStackReplacement(), RelocInfo::CODE_TARGET); @@ -1267,13 +1425,149 @@ void Builtins::Generate_OsrAfterStackCheck(MacroAssembler* masm) { } -void Builtins::Generate_FunctionCall(MacroAssembler* masm) { +// static +void Builtins::Generate_DatePrototype_GetField(MacroAssembler* masm, + int field_index) { + // ----------- S t a t e ------------- + // -- lr : return address + // -- jssp[0] : receiver + // ----------------------------------- + ASM_LOCATION("Builtins::Generate_DatePrototype_GetField"); + + // 1. Pop receiver into x0 and check that it's actually a JSDate object. + Label receiver_not_date; + { + __ Pop(x0); + __ JumpIfSmi(x0, &receiver_not_date); + __ JumpIfNotObjectType(x0, x1, x2, JS_DATE_TYPE, &receiver_not_date); + } + + // 2. Load the specified date field, falling back to the runtime as necessary. + if (field_index == JSDate::kDateValue) { + __ Ldr(x0, FieldMemOperand(x0, JSDate::kValueOffset)); + } else { + if (field_index < JSDate::kFirstUncachedField) { + Label stamp_mismatch; + __ Mov(x1, ExternalReference::date_cache_stamp(masm->isolate())); + __ Ldr(x1, MemOperand(x1)); + __ Ldr(x2, FieldMemOperand(x0, JSDate::kCacheStampOffset)); + __ Cmp(x1, x2); + __ B(ne, &stamp_mismatch); + __ Ldr(x0, FieldMemOperand( + x0, JSDate::kValueOffset + field_index * kPointerSize)); + __ Ret(); + __ Bind(&stamp_mismatch); + } + FrameScope scope(masm, StackFrame::INTERNAL); + __ Mov(x1, Smi::FromInt(field_index)); + __ CallCFunction( + ExternalReference::get_date_field_function(masm->isolate()), 2); + } + __ Ret(); + + // 3. Raise a TypeError if the receiver is not a date. + __ Bind(&receiver_not_date); + __ TailCallRuntime(Runtime::kThrowNotDateError); +} + + +// static +void Builtins::Generate_FunctionPrototypeApply(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- x0 : argc + // -- jssp[0] : argArray (if argc == 2) + // -- jssp[8] : thisArg (if argc >= 1) + // -- jssp[16] : receiver + // ----------------------------------- + ASM_LOCATION("Builtins::Generate_FunctionPrototypeApply"); + + Register argc = x0; + Register arg_array = x0; + Register receiver = x1; + Register this_arg = x2; + Register undefined_value = x3; + Register null_value = x4; + + __ LoadRoot(undefined_value, Heap::kUndefinedValueRootIndex); + __ LoadRoot(null_value, Heap::kNullValueRootIndex); + + // 1. Load receiver into x1, argArray into x0 (if present), remove all + // arguments from the stack (including the receiver), and push thisArg (if + // present) instead. + { + // Claim (2 - argc) dummy arguments from the stack, to put the stack in a + // consistent state for a simple pop operation. + __ Claim(2); + __ Drop(argc); + + // ----------- S t a t e ------------- + // -- x0 : argc + // -- jssp[0] : argArray (dummy value if argc <= 1) + // -- jssp[8] : thisArg (dummy value if argc == 0) + // -- jssp[16] : receiver + // ----------------------------------- + __ Cmp(argc, 1); + __ Pop(arg_array, this_arg); // Overwrites argc. + __ CmovX(this_arg, undefined_value, lo); // undefined if argc == 0. + __ CmovX(arg_array, undefined_value, ls); // undefined if argc <= 1. + + __ Peek(receiver, 0); + __ Poke(this_arg, 0); + } + + // ----------- S t a t e ------------- + // -- x0 : argArray + // -- x1 : receiver + // -- x3 : undefined root value + // -- jssp[0] : thisArg + // ----------------------------------- + + // 2. Make sure the receiver is actually callable. + Label receiver_not_callable; + __ JumpIfSmi(receiver, &receiver_not_callable); + __ Ldr(x10, FieldMemOperand(receiver, HeapObject::kMapOffset)); + __ Ldrb(w10, FieldMemOperand(x10, Map::kBitFieldOffset)); + __ TestAndBranchIfAllClear(x10, 1 << Map::kIsCallable, + &receiver_not_callable); + + // 3. Tail call with no arguments if argArray is null or undefined. + Label no_arguments; + __ Cmp(arg_array, null_value); + __ Ccmp(arg_array, undefined_value, ZFlag, ne); + __ B(eq, &no_arguments); + + // 4a. Apply the receiver to the given argArray (passing undefined for + // new.target in x3). + DCHECK(undefined_value.Is(x3)); + __ 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. + __ Bind(&no_arguments); + { + __ Mov(x0, 0); + DCHECK(receiver.Is(x1)); + __ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET); + } + + // 4c. The receiver is not callable, throw an appropriate TypeError. + __ Bind(&receiver_not_callable); + { + __ Poke(receiver, 0); + __ TailCallRuntime(Runtime::kThrowApplyNonFunction); + } +} + + +// static +void Builtins::Generate_FunctionPrototypeCall(MacroAssembler* masm) { Register argc = x0; Register function = x1; Register scratch1 = x10; Register scratch2 = x11; - ASM_LOCATION("Builtins::Generate_FunctionCall"); + ASM_LOCATION("Builtins::Generate_FunctionPrototypeCall"); + // 1. Make sure we have at least one argument. { Label done; @@ -1312,205 +1606,161 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { } -static void Generate_PushAppliedArguments(MacroAssembler* masm, - const int vectorOffset, - const int argumentsOffset, - const int indexOffset, - const int limitOffset) { - Label entry, loop; - Register receiver = LoadDescriptor::ReceiverRegister(); - Register key = LoadDescriptor::NameRegister(); - Register slot = LoadDescriptor::SlotRegister(); - Register vector = LoadWithVectorDescriptor::VectorRegister(); - - __ Ldr(key, MemOperand(fp, indexOffset)); - __ B(&entry); - - // Load the current argument from the arguments array. - __ Bind(&loop); - __ Ldr(receiver, MemOperand(fp, argumentsOffset)); - - // Use inline caching to speed up access to arguments. - int slot_index = TypeFeedbackVector::PushAppliedArgumentsIndex(); - __ Mov(slot, Operand(Smi::FromInt(slot_index))); - __ Ldr(vector, MemOperand(fp, vectorOffset)); - Handle<Code> ic = - KeyedLoadICStub(masm->isolate(), LoadICState(kNoExtraICState)).GetCode(); - __ Call(ic, RelocInfo::CODE_TARGET); - - // Push the nth argument. - __ Push(x0); - - __ Ldr(key, MemOperand(fp, indexOffset)); - __ Add(key, key, Smi::FromInt(1)); - __ Str(key, MemOperand(fp, indexOffset)); - - // Test if the copy loop has finished copying all the elements from the - // arguments object. - __ Bind(&entry); - __ Ldr(x1, MemOperand(fp, limitOffset)); - __ Cmp(key, x1); - __ B(ne, &loop); - - // On exit, the pushed arguments count is in x0, untagged - __ Mov(x0, key); - __ SmiUntag(x0); -} +void Builtins::Generate_ReflectApply(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- x0 : argc + // -- jssp[0] : argumentsList (if argc == 3) + // -- jssp[8] : thisArgument (if argc >= 2) + // -- jssp[16] : target (if argc >= 1) + // -- jssp[24] : receiver + // ----------------------------------- + ASM_LOCATION("Builtins::Generate_ReflectApply"); + Register argc = x0; + Register arguments_list = x0; + Register target = x1; + Register this_argument = x2; + Register undefined_value = x3; -static void Generate_ApplyHelper(MacroAssembler* masm, bool targetIsArgument) { - const int kFormalParameters = targetIsArgument ? 3 : 2; - const int kStackSize = kFormalParameters + 1; + __ LoadRoot(undefined_value, Heap::kUndefinedValueRootIndex); + // 1. Load target into x1 (if present), argumentsList into x0 (if present), + // remove all arguments from the stack (including the receiver), and push + // thisArgument (if present) instead. { - FrameScope frame_scope(masm, StackFrame::INTERNAL); - - const int kArgumentsOffset = kFPOnStackSize + kPCOnStackSize; - const int kReceiverOffset = kArgumentsOffset + kPointerSize; - const int kFunctionOffset = kReceiverOffset + kPointerSize; - const int kVectorOffset = - InternalFrameConstants::kCodeOffset - 1 * kPointerSize; - const int kIndexOffset = kVectorOffset - (2 * kPointerSize); - const int kLimitOffset = kVectorOffset - (1 * kPointerSize); - - Register args = x12; - Register receiver = x14; - Register function = x15; - Register apply_function = x1; - - // Push the vector. - __ Ldr( - apply_function, - FieldMemOperand(apply_function, JSFunction::kSharedFunctionInfoOffset)); - __ Ldr(apply_function, - FieldMemOperand(apply_function, - SharedFunctionInfo::kFeedbackVectorOffset)); - __ Push(apply_function); - - // Get the length of the arguments via a builtin call. - __ Ldr(function, MemOperand(fp, kFunctionOffset)); - __ Ldr(args, MemOperand(fp, kArgumentsOffset)); - __ Push(function, args); - if (targetIsArgument) { - __ InvokeBuiltin(Context::REFLECT_APPLY_PREPARE_BUILTIN_INDEX, - CALL_FUNCTION); - } else { - __ InvokeBuiltin(Context::APPLY_PREPARE_BUILTIN_INDEX, CALL_FUNCTION); - } - Register argc = x0; + // Claim (3 - argc) dummy arguments from the stack, to put the stack in a + // consistent state for a simple pop operation. + __ Claim(3); + __ Drop(argc); - Generate_CheckStackOverflow(masm, argc, kArgcIsSmiTagged); + // ----------- S t a t e ------------- + // -- x0 : argc + // -- jssp[0] : argumentsList (dummy value if argc <= 2) + // -- jssp[8] : thisArgument (dummy value if argc <= 1) + // -- jssp[16] : target (dummy value if argc == 0) + // -- jssp[24] : receiver + // ----------------------------------- + __ Adds(x10, argc, 0); // Preserve argc, and set the Z flag if it is zero. + __ Pop(arguments_list, this_argument, target); // Overwrites argc. + __ CmovX(target, undefined_value, eq); // undefined if argc == 0. + __ Cmp(x10, 2); + __ CmovX(this_argument, undefined_value, lo); // undefined if argc <= 1. + __ CmovX(arguments_list, undefined_value, ls); // undefined if argc <= 2. + + __ Poke(this_argument, 0); // Overwrite receiver. + } - // Push current limit, index and receiver. - __ Mov(x1, 0); // Initial index. - __ Ldr(receiver, MemOperand(fp, kReceiverOffset)); - __ Push(argc, x1, receiver); + // ----------- S t a t e ------------- + // -- x0 : argumentsList + // -- x1 : target + // -- jssp[0] : thisArgument + // ----------------------------------- - // Copy all arguments from the array to the stack. - Generate_PushAppliedArguments(masm, kVectorOffset, kArgumentsOffset, - kIndexOffset, kLimitOffset); + // 2. Make sure the target is actually callable. + Label target_not_callable; + __ JumpIfSmi(target, &target_not_callable); + __ Ldr(x10, FieldMemOperand(target, HeapObject::kMapOffset)); + __ Ldr(x10, FieldMemOperand(x10, Map::kBitFieldOffset)); + __ TestAndBranchIfAllClear(x10, 1 << Map::kIsCallable, &target_not_callable); - // At the end of the loop, the number of arguments is stored in x0, untagged + // 3a. Apply the target to the given argumentsList (passing undefined for + // new.target in x3). + DCHECK(undefined_value.Is(x3)); + __ Jump(masm->isolate()->builtins()->Apply(), RelocInfo::CODE_TARGET); - // Call the callable. - // TODO(bmeurer): This should be a tail call according to ES6. - __ Ldr(x1, MemOperand(fp, kFunctionOffset)); - __ Call(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET); + // 3b. The target is not callable, throw an appropriate TypeError. + __ Bind(&target_not_callable); + { + __ Poke(target, 0); + __ TailCallRuntime(Runtime::kThrowApplyNonFunction); } - __ Drop(kStackSize); - __ Ret(); } -static void Generate_ConstructHelper(MacroAssembler* masm) { - const int kFormalParameters = 3; - const int kStackSize = kFormalParameters + 1; - - { - FrameScope frame_scope(masm, StackFrame::INTERNAL); - - const int kNewTargetOffset = kFPOnStackSize + kPCOnStackSize; - const int kArgumentsOffset = kNewTargetOffset + kPointerSize; - const int kFunctionOffset = kArgumentsOffset + kPointerSize; - const int kVectorOffset = - InternalFrameConstants::kCodeOffset - 1 * kPointerSize; - const int kIndexOffset = kVectorOffset - (2 * kPointerSize); - const int kLimitOffset = kVectorOffset - (1 * kPointerSize); - - // Is x11 safe to use? - Register newTarget = x11; - Register args = x12; - Register function = x15; - Register construct_function = x1; - - // Push the vector. - __ Ldr(construct_function, - FieldMemOperand(construct_function, - JSFunction::kSharedFunctionInfoOffset)); - __ Ldr(construct_function, - FieldMemOperand(construct_function, - SharedFunctionInfo::kFeedbackVectorOffset)); - __ Push(construct_function); - - // If newTarget is not supplied, set it to constructor - Label validate_arguments; - __ Ldr(x0, MemOperand(fp, kNewTargetOffset)); - __ CompareRoot(x0, Heap::kUndefinedValueRootIndex); - __ B(ne, &validate_arguments); - __ Ldr(x0, MemOperand(fp, kFunctionOffset)); - __ Str(x0, MemOperand(fp, kNewTargetOffset)); - - // Validate arguments - __ Bind(&validate_arguments); - __ Ldr(function, MemOperand(fp, kFunctionOffset)); - __ Ldr(args, MemOperand(fp, kArgumentsOffset)); - __ Ldr(newTarget, MemOperand(fp, kNewTargetOffset)); - __ Push(function, args, newTarget); - __ InvokeBuiltin(Context::REFLECT_CONSTRUCT_PREPARE_BUILTIN_INDEX, - CALL_FUNCTION); - Register argc = x0; - - Generate_CheckStackOverflow(masm, argc, kArgcIsSmiTagged); - - // Push current limit and index & constructor function as callee. - __ Mov(x1, 0); // Initial index. - __ Push(argc, x1, function); +void Builtins::Generate_ReflectConstruct(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- x0 : argc + // -- jssp[0] : new.target (optional) + // -- jssp[8] : argumentsList + // -- jssp[16] : target + // -- jssp[24] : receiver + // ----------------------------------- + ASM_LOCATION("Builtins::Generate_ReflectConstruct"); - // Copy all arguments from the array to the stack. - Generate_PushAppliedArguments(masm, kVectorOffset, kArgumentsOffset, - kIndexOffset, kLimitOffset); + Register argc = x0; + Register arguments_list = x0; + Register target = x1; + Register new_target = x3; + Register undefined_value = x4; - // Use undefined feedback vector - __ LoadRoot(x2, Heap::kUndefinedValueRootIndex); - __ Ldr(x1, MemOperand(fp, kFunctionOffset)); - __ Ldr(x4, MemOperand(fp, kNewTargetOffset)); + __ LoadRoot(undefined_value, Heap::kUndefinedValueRootIndex); - // Call the function. - CallConstructStub stub(masm->isolate(), SUPER_CONSTRUCTOR_CALL); - __ Call(stub.GetCode(), RelocInfo::CONSTRUCT_CALL); + // 1. Load target into x1 (if present), argumentsList into x0 (if present), + // new.target into x3 (if present, otherwise use target), remove all + // arguments from the stack (including the receiver), and push thisArgument + // (if present) instead. + { + // Claim (3 - argc) dummy arguments from the stack, to put the stack in a + // consistent state for a simple pop operation. + __ Claim(3); + __ Drop(argc); - // Leave internal frame. + // ----------- S t a t e ------------- + // -- x0 : argc + // -- jssp[0] : new.target (dummy value if argc <= 2) + // -- jssp[8] : argumentsList (dummy value if argc <= 1) + // -- jssp[16] : target (dummy value if argc == 0) + // -- jssp[24] : receiver + // ----------------------------------- + __ Adds(x10, argc, 0); // Preserve argc, and set the Z flag if it is zero. + __ Pop(new_target, arguments_list, target); // Overwrites argc. + __ CmovX(target, undefined_value, eq); // undefined if argc == 0. + __ Cmp(x10, 2); + __ CmovX(arguments_list, undefined_value, lo); // undefined if argc <= 1. + __ CmovX(new_target, target, ls); // target if argc <= 2. + + __ Poke(undefined_value, 0); // Overwrite receiver. } - __ Drop(kStackSize); - __ Ret(); -} - - -void Builtins::Generate_FunctionApply(MacroAssembler* masm) { - ASM_LOCATION("Builtins::Generate_FunctionApply"); - Generate_ApplyHelper(masm, false); -} + // ----------- S t a t e ------------- + // -- x0 : argumentsList + // -- x1 : target + // -- x3 : new.target + // -- jssp[0] : receiver (undefined) + // ----------------------------------- -void Builtins::Generate_ReflectApply(MacroAssembler* masm) { - ASM_LOCATION("Builtins::Generate_ReflectApply"); - Generate_ApplyHelper(masm, true); -} - + // 2. Make sure the target is actually a constructor. + Label target_not_constructor; + __ JumpIfSmi(target, &target_not_constructor); + __ Ldr(x10, FieldMemOperand(target, HeapObject::kMapOffset)); + __ Ldrb(x10, FieldMemOperand(x10, Map::kBitFieldOffset)); + __ TestAndBranchIfAllClear(x10, 1 << Map::kIsConstructor, + &target_not_constructor); + + // 3. Make sure the new.target is actually a constructor. + Label new_target_not_constructor; + __ JumpIfSmi(new_target, &new_target_not_constructor); + __ Ldr(x10, FieldMemOperand(new_target, HeapObject::kMapOffset)); + __ Ldrb(x10, FieldMemOperand(x10, Map::kBitFieldOffset)); + __ TestAndBranchIfAllClear(x10, 1 << Map::kIsConstructor, + &new_target_not_constructor); + + // 4a. Construct the target with the given new.target and argumentsList. + __ Jump(masm->isolate()->builtins()->Apply(), RelocInfo::CODE_TARGET); + + // 4b. The target is not a constructor, throw an appropriate TypeError. + __ Bind(&target_not_constructor); + { + __ Poke(target, 0); + __ TailCallRuntime(Runtime::kThrowCalledNonCallable); + } -void Builtins::Generate_ReflectConstruct(MacroAssembler* masm) { - ASM_LOCATION("Builtins::Generate_ReflectConstruct"); - Generate_ConstructHelper(masm); + // 4c. The new.target is not a constructor, throw an appropriate TypeError. + __ Bind(&new_target_not_constructor); + { + __ Poke(new_target, 0); + __ TailCallRuntime(Runtime::kThrowCalledNonCallable); + } } @@ -1520,6 +1770,7 @@ static void ArgumentAdaptorStackCheck(MacroAssembler* masm, // -- x0 : actual number of arguments // -- x1 : function (passed through to callee) // -- x2 : expected number of arguments + // -- x3 : new target (passed through to callee) // ----------------------------------- // Check the stack for overflow. // We are not trying to catch interruptions (e.g. debug break and @@ -1561,8 +1812,152 @@ static void LeaveArgumentsAdaptorFrame(MacroAssembler* masm) { // static +void Builtins::Generate_Apply(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- x0 : argumentsList + // -- x1 : target + // -- x3 : new.target (checked to be constructor or undefined) + // -- jssp[0] : thisArgument + // ----------------------------------- + + Register arguments_list = x0; + Register target = x1; + Register new_target = x3; + + Register args = x0; + Register len = x2; + + // Create the list of arguments from the array-like argumentsList. + { + Label create_arguments, create_array, create_runtime, done_create; + __ JumpIfSmi(arguments_list, &create_runtime); + + // Load native context. + Register native_context = x4; + __ Ldr(native_context, NativeContextMemOperand()); + + // Load the map of argumentsList. + Register arguments_list_map = x2; + __ Ldr(arguments_list_map, + FieldMemOperand(arguments_list, HeapObject::kMapOffset)); + + // Check if argumentsList is an (unmodified) arguments object. + __ Ldr(x10, ContextMemOperand(native_context, + Context::SLOPPY_ARGUMENTS_MAP_INDEX)); + __ Ldr(x11, ContextMemOperand(native_context, + Context::STRICT_ARGUMENTS_MAP_INDEX)); + __ Cmp(arguments_list_map, x10); + __ Ccmp(arguments_list_map, x11, ZFlag, ne); + __ B(eq, &create_arguments); + + // Check if argumentsList is a fast JSArray. + __ CompareInstanceType(arguments_list_map, native_context, JS_ARRAY_TYPE); + __ B(eq, &create_array); + + // Ask the runtime to create the list (actually a FixedArray). + __ Bind(&create_runtime); + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ Push(target, new_target, arguments_list); + __ CallRuntime(Runtime::kCreateListFromArrayLike); + __ Pop(new_target, target); + __ Ldrsw(len, UntagSmiFieldMemOperand(arguments_list, + FixedArray::kLengthOffset)); + } + __ B(&done_create); + + // Try to create the list from an arguments object. + __ Bind(&create_arguments); + __ Ldrsw(len, UntagSmiFieldMemOperand( + arguments_list, + JSObject::kHeaderSize + + Heap::kArgumentsLengthIndex * kPointerSize)); + __ Ldr(x10, FieldMemOperand(arguments_list, JSObject::kElementsOffset)); + __ Ldrsw(x11, UntagSmiFieldMemOperand(x10, FixedArray::kLengthOffset)); + __ CompareAndBranch(len, x11, ne, &create_runtime); + __ Mov(args, x10); + __ B(&done_create); + + // Try to create the list from a JSArray object. + __ Bind(&create_array); + __ Ldr(x10, FieldMemOperand(arguments_list_map, Map::kBitField2Offset)); + __ DecodeField<Map::ElementsKindBits>(x10); + STATIC_ASSERT(FAST_SMI_ELEMENTS == 0); + STATIC_ASSERT(FAST_ELEMENTS == 2); + // Branch for anything that's not FAST_{SMI_}ELEMENTS. + __ TestAndBranchIfAnySet(x10, ~FAST_ELEMENTS, &create_runtime); + __ Ldrsw(len, + UntagSmiFieldMemOperand(arguments_list, JSArray::kLengthOffset)); + __ Ldr(args, FieldMemOperand(arguments_list, JSArray::kElementsOffset)); + + __ Bind(&done_create); + } + + // Check for stack overflow. + { + // Check the stack for overflow. We are not trying to catch interruptions + // (i.e. debug break and preemption) here, so check the "real stack limit". + Label done; + __ LoadRoot(x10, Heap::kRealStackLimitRootIndex); + // Make x10 the space we have left. The stack might already be overflowed + // here which will cause x10 to become negative. + __ Sub(x10, masm->StackPointer(), x10); + // Check if the arguments will overflow the stack. + __ Cmp(x10, Operand(len, LSL, kPointerSizeLog2)); + __ B(gt, &done); // Signed comparison. + __ TailCallRuntime(Runtime::kThrowStackOverflow); + __ Bind(&done); + } + + // ----------- S t a t e ------------- + // -- x0 : args (a FixedArray built from argumentsList) + // -- x1 : target + // -- x2 : len (number of elements to push from args) + // -- x3 : new.target (checked to be constructor or undefined) + // -- jssp[0] : thisArgument + // ----------------------------------- + + // Push arguments onto the stack (thisArgument is already on the stack). + { + Label done, loop; + Register src = x4; + + __ Add(src, args, FixedArray::kHeaderSize - kHeapObjectTag); + __ Mov(x0, len); // The 'len' argument for Call() or Construct(). + __ Cbz(len, &done); + __ Claim(len); + __ Bind(&loop); + __ Sub(len, len, 1); + __ Ldr(x10, MemOperand(src, kPointerSize, PostIndex)); + __ Poke(x10, Operand(len, LSL, kPointerSizeLog2)); + __ Cbnz(len, &loop); + __ Bind(&done); + } + + // ----------- S t a t e ------------- + // -- x0 : argument count (len) + // -- x1 : target + // -- x3 : new.target (checked to be constructor or undefined) + // -- jssp[0] : args[len-1] + // -- jssp[8] : args[len-2] + // ... : ... + // -- jssp[8*(len-2)] : args[1] + // -- jssp[8*(len-1)] : args[0] + // ----------------------------------- + + // Dispatch to Call or Construct depending on whether new.target is undefined. + { + __ CompareRoot(new_target, Heap::kUndefinedValueRootIndex); + __ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET, eq); + __ Jump(masm->isolate()->builtins()->Construct(), RelocInfo::CODE_TARGET); + } +} + + +// static void Builtins::Generate_CallFunction(MacroAssembler* masm, ConvertReceiverMode mode) { + ASM_LOCATION("Builtins::Generate_CallFunction"); // ----------- S t a t e ------------- // -- x0 : the number of arguments (not including the receiver) // -- x1 : the function to call (checked to be a JSFunction) @@ -1651,17 +2046,121 @@ void Builtins::Generate_CallFunction(MacroAssembler* masm, __ Ldrsw( x2, FieldMemOperand(x2, SharedFunctionInfo::kFormalParameterCountOffset)); - __ Ldr(x3, FieldMemOperand(x1, JSFunction::kCodeEntryOffset)); ParameterCount actual(x0); ParameterCount expected(x2); - __ InvokeCode(x3, expected, actual, JUMP_FUNCTION, NullCallWrapper()); + __ InvokeFunctionCode(x1, no_reg, expected, actual, JUMP_FUNCTION, + CheckDebugStepCallWrapper()); // The function is a "classConstructor", need to raise an exception. __ bind(&class_constructor); { FrameScope frame(masm, StackFrame::INTERNAL); - __ CallRuntime(Runtime::kThrowConstructorNonCallableError, 0); + __ Push(x1); + __ CallRuntime(Runtime::kThrowConstructorNonCallableError); + } +} + + +namespace { + +void Generate_PushBoundArguments(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- x0 : the number of arguments (not including the receiver) + // -- x1 : target (checked to be a JSBoundFunction) + // -- x3 : new.target (only in case of [[Construct]]) + // ----------------------------------- + + // Load [[BoundArguments]] into x2 and length of that into x4. + Label no_bound_arguments; + __ Ldr(x2, FieldMemOperand(x1, JSBoundFunction::kBoundArgumentsOffset)); + __ Ldrsw(x4, UntagSmiFieldMemOperand(x2, FixedArray::kLengthOffset)); + __ Cmp(x4, 0); + __ B(eq, &no_bound_arguments); + { + // ----------- S t a t e ------------- + // -- x0 : the number of arguments (not including the receiver) + // -- x1 : target (checked to be a JSBoundFunction) + // -- x2 : the [[BoundArguments]] (implemented as FixedArray) + // -- x3 : new.target (only in case of [[Construct]]) + // -- x4 : the number of [[BoundArguments]] + // ----------------------------------- + + // Reserve stack space for the [[BoundArguments]]. + { + Label done; + __ Claim(x4); + // Check the stack for overflow. We are not trying to catch interruptions + // (i.e. debug break and preemption) here, so check the "real stack + // limit". + __ CompareRoot(jssp, Heap::kRealStackLimitRootIndex); + __ B(gt, &done); // Signed comparison. + // Restore the stack pointer. + __ Drop(x4); + { + FrameScope scope(masm, StackFrame::MANUAL); + __ EnterFrame(StackFrame::INTERNAL); + __ CallRuntime(Runtime::kThrowStackOverflow); + } + __ Bind(&done); + } + + // Relocate arguments down the stack. + { + Label loop, done_loop; + __ Mov(x5, 0); + __ Bind(&loop); + __ Cmp(x5, x0); + __ B(gt, &done_loop); + __ Peek(x10, Operand(x4, LSL, kPointerSizeLog2)); + __ Poke(x10, Operand(x5, LSL, kPointerSizeLog2)); + __ Add(x4, x4, 1); + __ Add(x5, x5, 1); + __ B(&loop); + __ Bind(&done_loop); + } + + // Copy [[BoundArguments]] to the stack (below the arguments). + { + Label loop; + __ Ldrsw(x4, UntagSmiFieldMemOperand(x2, FixedArray::kLengthOffset)); + __ Add(x2, x2, FixedArray::kHeaderSize - kHeapObjectTag); + __ Bind(&loop); + __ Sub(x4, x4, 1); + __ Ldr(x10, MemOperand(x2, x4, LSL, kPointerSizeLog2)); + __ Poke(x10, Operand(x0, LSL, kPointerSizeLog2)); + __ Add(x0, x0, 1); + __ Cmp(x4, 0); + __ B(gt, &loop); + } } + __ Bind(&no_bound_arguments); +} + +} // namespace + + +// static +void Builtins::Generate_CallBoundFunction(MacroAssembler* masm) { + // ----------- 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); + + // Patch the receiver to [[BoundThis]]. + __ Ldr(x10, FieldMemOperand(x1, JSBoundFunction::kBoundThisOffset)); + __ Poke(x10, Operand(x0, LSL, kPointerSizeLog2)); + + // Push the [[BoundArguments]] onto the stack. + Generate_PushBoundArguments(masm); + + // Call the [[BoundTargetFunction]] via the Call builtin. + __ Ldr(x1, FieldMemOperand(x1, JSBoundFunction::kBoundTargetFunctionOffset)); + __ Mov(x10, + ExternalReference(Builtins::kCall_ReceiverIsAny, masm->isolate())); + __ Ldr(x11, MemOperand(x10)); + __ Add(x12, x11, Code::kHeaderSize - kHeapObjectTag); + __ Br(x12); } @@ -1678,14 +2177,20 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode) { __ CompareObjectType(x1, x4, x5, JS_FUNCTION_TYPE); __ Jump(masm->isolate()->builtins()->CallFunction(mode), RelocInfo::CODE_TARGET, eq); - __ Cmp(x5, JS_FUNCTION_PROXY_TYPE); + __ Cmp(x5, JS_BOUND_FUNCTION_TYPE); + __ Jump(masm->isolate()->builtins()->CallBoundFunction(), + RelocInfo::CODE_TARGET, eq); + __ Cmp(x5, JS_PROXY_TYPE); __ B(ne, &non_function); - // 1. Call to function proxy. - // TODO(neis): This doesn't match the ES6 spec for [[Call]] on proxies. - __ Ldr(x1, FieldMemOperand(x1, JSFunctionProxy::kCallTrapOffset)); - __ AssertNotSmi(x1); - __ B(&non_smi); + // 1. Runtime fallback for Proxy [[Call]]. + __ Push(x1); + // Increase the arguments size to include the pushed function and the + // existing receiver on the stack. + __ Add(x0, x0, Operand(2)); + // Tail-call to the runtime. + __ JumpToExternalReference( + ExternalReference(Runtime::kJSProxyCall, masm->isolate())); // 2. Call to something else, which might have a [[Call]] internal method (if // not we raise an exception). @@ -1696,7 +2201,7 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode) { // 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. - __ LoadGlobalFunction(Context::CALL_AS_FUNCTION_DELEGATE_INDEX, x1); + __ LoadNativeContextSlot(Context::CALL_AS_FUNCTION_DELEGATE_INDEX, x1); __ Jump(masm->isolate()->builtins()->CallFunction( ConvertReceiverMode::kNotNullOrUndefined), RelocInfo::CODE_TARGET); @@ -1706,7 +2211,7 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode) { { FrameScope scope(masm, StackFrame::INTERNAL); __ Push(x1); - __ CallRuntime(Runtime::kThrowCalledNonCallable, 1); + __ CallRuntime(Runtime::kThrowCalledNonCallable); } } @@ -1716,10 +2221,9 @@ void Builtins::Generate_ConstructFunction(MacroAssembler* masm) { // ----------- S t a t e ------------- // -- x0 : the number of arguments (not including the receiver) // -- x1 : the constructor to call (checked to be a JSFunction) - // -- x3 : the original constructor (checked to be a JSFunction) + // -- x3 : the new target (checked to be a constructor) // ----------------------------------- __ AssertFunction(x1); - __ AssertFunction(x3); // Calling convention for function specific ConstructStubs require // x2 to contain either an AllocationSite or undefined. @@ -1735,17 +2239,53 @@ void Builtins::Generate_ConstructFunction(MacroAssembler* masm) { // static +void Builtins::Generate_ConstructBoundFunction(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- x0 : the number of arguments (not including the receiver) + // -- x1 : the function to call (checked to be a JSBoundFunction) + // -- x3 : the new target (checked to be a constructor) + // ----------------------------------- + __ AssertBoundFunction(x1); + + // Push the [[BoundArguments]] onto the stack. + Generate_PushBoundArguments(masm); + + // Patch new.target to [[BoundTargetFunction]] if new.target equals target. + { + Label done; + __ Cmp(x1, x3); + __ B(ne, &done); + __ Ldr(x3, + FieldMemOperand(x1, JSBoundFunction::kBoundTargetFunctionOffset)); + __ Bind(&done); + } + + // Construct the [[BoundTargetFunction]] via the Construct builtin. + __ Ldr(x1, FieldMemOperand(x1, JSBoundFunction::kBoundTargetFunctionOffset)); + __ Mov(x10, ExternalReference(Builtins::kConstruct, masm->isolate())); + __ Ldr(x11, MemOperand(x10)); + __ Add(x12, x11, Code::kHeaderSize - kHeapObjectTag); + __ Br(x12); +} + + +// static void Builtins::Generate_ConstructProxy(MacroAssembler* masm) { // ----------- S t a t e ------------- // -- x0 : the number of arguments (not including the receiver) - // -- x1 : the constructor to call (checked to be a JSFunctionProxy) - // -- x3 : the original constructor (either the same as the constructor or + // -- x1 : the constructor to call (checked to be a JSProxy) + // -- x3 : the new target (either the same as the constructor or // the JSFunction on which new was invoked initially) // ----------------------------------- - // TODO(neis): This doesn't match the ES6 spec for [[Construct]] on proxies. - __ Ldr(x1, FieldMemOperand(x1, JSFunctionProxy::kConstructTrapOffset)); - __ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET); + // Call into the Runtime for Proxy [[Construct]]. + __ Push(x1); + __ Push(x3); + // Include the pushed new_target, constructor and the receiver. + __ Add(x0, x0, 3); + // Tail-call to the runtime. + __ JumpToExternalReference( + ExternalReference(Runtime::kJSProxyConstruct, masm->isolate())); } @@ -1754,22 +2294,31 @@ void Builtins::Generate_Construct(MacroAssembler* masm) { // ----------- S t a t e ------------- // -- x0 : the number of arguments (not including the receiver) // -- x1 : the constructor to call (can be any Object) - // -- x3 : the original constructor (either the same as the constructor or + // -- x3 : the new target (either the same as the constructor or // the JSFunction on which new was invoked initially) // ----------------------------------- - // Check if target has a [[Construct]] internal method. + // Check if target is a Smi. Label non_constructor; __ JumpIfSmi(x1, &non_constructor); - __ Ldr(x4, FieldMemOperand(x1, HeapObject::kMapOffset)); - __ Ldrb(x2, FieldMemOperand(x4, Map::kBitFieldOffset)); - __ TestAndBranchIfAllClear(x2, 1 << Map::kIsConstructor, &non_constructor); // Dispatch based on instance type. - __ CompareInstanceType(x4, x5, JS_FUNCTION_TYPE); + __ CompareObjectType(x1, x4, x5, JS_FUNCTION_TYPE); __ Jump(masm->isolate()->builtins()->ConstructFunction(), RelocInfo::CODE_TARGET, eq); - __ Cmp(x5, JS_FUNCTION_PROXY_TYPE); + + // Check if target has a [[Construct]] internal method. + __ Ldrb(x2, FieldMemOperand(x4, Map::kBitFieldOffset)); + __ TestAndBranchIfAllClear(x2, 1 << Map::kIsConstructor, &non_constructor); + + // Only dispatch to bound functions after checking whether they are + // constructors. + __ Cmp(x5, JS_BOUND_FUNCTION_TYPE); + __ Jump(masm->isolate()->builtins()->ConstructBoundFunction(), + RelocInfo::CODE_TARGET, eq); + + // Only dispatch to proxies after checking whether they are constructors. + __ Cmp(x5, JS_PROXY_TYPE); __ Jump(masm->isolate()->builtins()->ConstructProxy(), RelocInfo::CODE_TARGET, eq); @@ -1778,7 +2327,7 @@ void Builtins::Generate_Construct(MacroAssembler* masm) { // Overwrite the original receiver with the (original) target. __ Poke(x1, Operand(x0, LSL, kXRegSizeLog2)); // Let the "call_as_constructor_delegate" take care of the rest. - __ LoadGlobalFunction(Context::CALL_AS_CONSTRUCTOR_DELEGATE_INDEX, x1); + __ LoadNativeContextSlot(Context::CALL_AS_CONSTRUCTOR_DELEGATE_INDEX, x1); __ Jump(masm->isolate()->builtins()->CallFunction(), RelocInfo::CODE_TARGET); } @@ -1786,11 +2335,8 @@ void Builtins::Generate_Construct(MacroAssembler* masm) { // Called Construct on an Object that doesn't have a [[Construct]] internal // method. __ bind(&non_constructor); - { - FrameScope scope(masm, StackFrame::INTERNAL); - __ Push(x1); - __ CallRuntime(Runtime::kThrowCalledNonCallable, 1); - } + __ Jump(masm->isolate()->builtins()->ConstructedNonConstructable(), + RelocInfo::CODE_TARGET); } @@ -1831,7 +2377,7 @@ void Builtins::Generate_InterpreterPushArgsAndCall(MacroAssembler* masm) { void Builtins::Generate_InterpreterPushArgsAndConstruct(MacroAssembler* masm) { // ----------- S t a t e ------------- // -- x0 : argument count (not including receiver) - // -- x3 : original constructor + // -- x3 : new target // -- x1 : constructor to call // -- x2 : address of the first argument // ----------------------------------- @@ -1860,7 +2406,7 @@ void Builtins::Generate_InterpreterPushArgsAndConstruct(MacroAssembler* masm) { __ B(gt, &loop_header); // Call the constructor with x0, x1, and x3 unmodified. - __ Jump(masm->isolate()->builtins()->Construct(), RelocInfo::CONSTRUCT_CALL); + __ Jump(masm->isolate()->builtins()->Construct(), RelocInfo::CODE_TARGET); } @@ -1870,20 +2416,17 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { // -- x0 : actual number of arguments // -- x1 : function (passed through to callee) // -- x2 : expected number of arguments + // -- x3 : new target (passed through to callee) // ----------------------------------- - Label stack_overflow; - ArgumentAdaptorStackCheck(masm, &stack_overflow); - Register argc_actual = x0; // Excluding the receiver. Register argc_expected = x2; // Excluding the receiver. Register function = x1; - Register code_entry = x3; + Register code_entry = x10; - Label invoke, dont_adapt_arguments; + Label invoke, dont_adapt_arguments, stack_overflow; Label enough, too_few; - __ Ldr(code_entry, FieldMemOperand(function, JSFunction::kCodeEntryOffset)); __ Cmp(argc_actual, argc_expected); __ B(lt, &too_few); __ Cmp(argc_expected, SharedFunctionInfo::kDontAdaptArgumentsSentinel); @@ -1891,6 +2434,7 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { { // Enough parameters: actual >= expected EnterArgumentsAdaptorFrame(masm); + ArgumentAdaptorStackCheck(masm, &stack_overflow); Register copy_start = x10; Register copy_end = x11; @@ -1956,11 +2500,12 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { { FrameScope frame(masm, StackFrame::MANUAL); EnterArgumentsAdaptorFrame(masm); - __ CallRuntime(Runtime::kThrowStrongModeTooFewArguments, 0); + __ CallRuntime(Runtime::kThrowStrongModeTooFewArguments); } __ Bind(&no_strong_error); EnterArgumentsAdaptorFrame(masm); + ArgumentAdaptorStackCheck(masm, &stack_overflow); __ Lsl(scratch2, argc_expected, kPointerSizeLog2); __ Lsl(argc_actual, argc_actual, kPointerSizeLog2); @@ -2010,6 +2555,8 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { __ Mov(argc_actual, argc_expected); // x0 : expected number of arguments // x1 : function (passed through to callee) + // x3 : new target (passed through to callee) + __ Ldr(code_entry, FieldMemOperand(function, JSFunction::kCodeEntryOffset)); __ Call(code_entry); // Store offset of return address for deoptimizer. @@ -2021,13 +2568,13 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { // Call the entry point without adapting the arguments. __ Bind(&dont_adapt_arguments); + __ Ldr(code_entry, FieldMemOperand(function, JSFunction::kCodeEntryOffset)); __ Jump(code_entry); __ Bind(&stack_overflow); { FrameScope frame(masm, StackFrame::MANUAL); - EnterArgumentsAdaptorFrame(masm); - __ CallRuntime(Runtime::kThrowStackOverflow, 0); + __ CallRuntime(Runtime::kThrowStackOverflow); __ Unreachable(); } } |