diff options
Diffstat (limited to 'deps/v8/src/arm/builtins-arm.cc')
-rw-r--r-- | deps/v8/src/arm/builtins-arm.cc | 1724 |
1 files changed, 1107 insertions, 617 deletions
diff --git a/deps/v8/src/arm/builtins-arm.cc b/deps/v8/src/arm/builtins-arm.cc index 4464816f72..0c83f918ca 100644 --- a/deps/v8/src/arm/builtins-arm.cc +++ b/deps/v8/src/arm/builtins-arm.cc @@ -22,9 +22,8 @@ void Builtins::Generate_Adaptor(MacroAssembler* masm, BuiltinExtraArguments extra_args) { // ----------- S t a t e ------------- // -- r0 : number of arguments excluding receiver - // (only guaranteed when the called function - // is not marked as DontAdaptArguments) - // -- r1 : called function + // -- r1 : target + // -- r3 : new.target // -- sp[0] : last argument // -- ... // -- sp[4 * (argc - 1)] : first argument @@ -36,28 +35,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(r1, JSFunction::kContextOffset)); // Insert extra arguments. int num_extra_args = 0; - if (extra_args == NEEDS_CALLED_FUNCTION) { - num_extra_args = 1; - __ push(r1); - } else { - DCHECK(extra_args == NO_EXTRA_ARGUMENTS); + switch (extra_args) { + case BuiltinExtraArguments::kTarget: + __ Push(r1); + ++num_extra_args; + break; + case BuiltinExtraArguments::kNewTarget: + __ Push(r3); + ++num_extra_args; + break; + case BuiltinExtraArguments::kTargetAndNewTarget: + __ Push(r1, r3); + num_extra_args += 2; + break; + case BuiltinExtraArguments::kNone: + break; } // JumpToExternalReference expects r0 to contain the number of arguments - // including the receiver and the extra arguments. But r0 is only valid - // if the called function is marked as DontAdaptArguments, otherwise we - // need to load the argument count from the SharedFunctionInfo. - __ ldr(r2, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset)); - __ ldr(r2, - FieldMemOperand(r2, SharedFunctionInfo::kFormalParameterCountOffset)); - __ SmiUntag(r2); - __ cmp(r2, Operand(SharedFunctionInfo::kDontAdaptArgumentsSentinel)); - __ mov(r0, r2, LeaveCC, ne); + // including the receiver and the extra arguments. __ add(r0, r0, Operand(num_extra_args + 1)); __ JumpToExternalReference(ExternalReference(id, masm->isolate())); @@ -67,30 +67,15 @@ void Builtins::Generate_Adaptor(MacroAssembler* masm, // Load the built-in InternalArray function from the current context. static void GenerateLoadInternalArrayFunction(MacroAssembler* masm, Register result) { - // Load the native context. - - __ ldr(result, - MemOperand(cp, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX))); - __ ldr(result, FieldMemOperand(result, JSGlobalObject::kNativeContextOffset)); - // Load the InternalArray function from the native context. - __ ldr(result, - MemOperand(result, - Context::SlotOffset( - Context::INTERNAL_ARRAY_FUNCTION_INDEX))); + // Load the InternalArray function from the current native context. + __ LoadNativeContextSlot(Context::INTERNAL_ARRAY_FUNCTION_INDEX, result); } // Load the built-in Array function from the current context. static void GenerateLoadArrayFunction(MacroAssembler* masm, Register result) { - // Load the native context. - - __ ldr(result, - MemOperand(cp, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX))); - __ ldr(result, FieldMemOperand(result, JSGlobalObject::kNativeContextOffset)); - // Load the Array function from the native context. - __ ldr(result, - MemOperand(result, - Context::SlotOffset(Context::ARRAY_FUNCTION_INDEX))); + // Load the Array function from the current native context. + __ LoadNativeContextSlot(Context::ARRAY_FUNCTION_INDEX, result); } @@ -152,6 +137,106 @@ void Builtins::Generate_ArrayCode(MacroAssembler* masm) { // static +void Builtins::Generate_NumberConstructor(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- r0 : number of arguments + // -- r1 : constructor function + // -- lr : return address + // -- sp[(argc - n - 1) * 4] : arg[n] (zero based) + // -- sp[argc * 4] : receiver + // ----------------------------------- + + // 1. Load the first argument into r0 and get rid of the rest (including the + // receiver). + Label no_arguments; + { + __ sub(r0, r0, Operand(1), SetCC); + __ b(lo, &no_arguments); + __ ldr(r0, MemOperand(sp, r0, LSL, kPointerSizeLog2, PreIndex)); + __ Drop(2); + } + + // 2a. Convert the first argument to a number. + ToNumberStub stub(masm->isolate()); + __ TailCallStub(&stub); + + // 2b. No arguments, return +0. + __ bind(&no_arguments); + __ Move(r0, Smi::FromInt(0)); + __ Ret(1); +} + + +// static +void Builtins::Generate_NumberConstructor_ConstructStub(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- r0 : number of arguments + // -- r1 : constructor function + // -- r3 : new target + // -- lr : return address + // -- sp[(argc - n - 1) * 4] : arg[n] (zero based) + // -- sp[argc * 4] : receiver + // ----------------------------------- + + // 1. Make sure we operate in the context of the called function. + __ ldr(cp, FieldMemOperand(r1, JSFunction::kContextOffset)); + + // 2. Load the first argument into r2 and get rid of the rest (including the + // receiver). + { + Label no_arguments, done; + __ sub(r0, r0, Operand(1), SetCC); + __ b(lo, &no_arguments); + __ ldr(r2, MemOperand(sp, r0, LSL, kPointerSizeLog2, PreIndex)); + __ Drop(2); + __ b(&done); + __ bind(&no_arguments); + __ Move(r2, Smi::FromInt(0)); + __ Drop(1); + __ bind(&done); + } + + // 3. Make sure r2 is a number. + { + Label done_convert; + __ JumpIfSmi(r2, &done_convert); + __ CompareObjectType(r2, r4, r4, HEAP_NUMBER_TYPE); + __ b(eq, &done_convert); + { + FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL); + __ Push(r1, r3); + __ Move(r0, r2); + ToNumberStub stub(masm->isolate()); + __ CallStub(&stub); + __ Move(r2, r0); + __ Pop(r1, r3); + } + __ bind(&done_convert); + } + + // 4. Check if new target and constructor differ. + Label new_object; + __ cmp(r1, r3); + __ b(ne, &new_object); + + // 5. Allocate a JSValue wrapper for the number. + __ AllocateJSValue(r0, r1, r2, r4, r5, &new_object); + __ Ret(); + + // 6. Fallback to the runtime to create new object. + __ bind(&new_object); + { + FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL); + __ Push(r2, r1, r3); // first argument, constructor, new target + __ CallRuntime(Runtime::kNewObject); + __ Pop(r2); + } + __ str(r2, FieldMemOperand(r0, JSValue::kValueOffset)); + __ Ret(); +} + + +// static void Builtins::Generate_StringConstructor(MacroAssembler* masm) { // ----------- S t a t e ------------- // -- r0 : number of arguments @@ -201,7 +286,7 @@ void Builtins::Generate_StringConstructor(MacroAssembler* masm) { __ bind(&symbol_descriptive_string); { __ Push(r0); - __ TailCallRuntime(Runtime::kSymbolDescriptiveString, 1, 1); + __ TailCallRuntime(Runtime::kSymbolDescriptiveString); } } @@ -211,13 +296,16 @@ void Builtins::Generate_StringConstructor_ConstructStub(MacroAssembler* masm) { // ----------- S t a t e ------------- // -- r0 : number of arguments // -- r1 : constructor function - // -- r3 : original constructor + // -- r3 : new target // -- lr : return address // -- sp[(argc - n - 1) * 4] : arg[n] (zero based) // -- sp[argc * 4] : receiver // ----------------------------------- - // 1. Load the first argument into r2 and get rid of the rest (including the + // 1. Make sure we operate in the context of the called function. + __ ldr(cp, FieldMemOperand(r1, JSFunction::kContextOffset)); + + // 2. Load the first argument into r2 and get rid of the rest (including the // receiver). { Label no_arguments, done; @@ -232,7 +320,7 @@ void Builtins::Generate_StringConstructor_ConstructStub(MacroAssembler* masm) { __ bind(&done); } - // 2. Make sure r2 is a string. + // 3. Make sure r2 is a string. { Label convert, done_convert; __ JumpIfSmi(r2, &convert); @@ -251,70 +339,45 @@ void Builtins::Generate_StringConstructor_ConstructStub(MacroAssembler* masm) { __ bind(&done_convert); } - // 3. Allocate a JSValue wrapper for the string. - { - // ----------- S t a t e ------------- - // -- r2 : the first argument - // -- r1 : constructor function - // -- r3 : original constructor - // -- lr : return address - // ----------------------------------- - - Label allocate, done_allocate, rt_call; + // 4. Check if new target and constructor differ. + Label new_object; + __ cmp(r1, r3); + __ b(ne, &new_object); - // Fall back to runtime if the original constructor and function differ. - __ cmp(r1, r3); - __ b(ne, &rt_call); - - __ Allocate(JSValue::kSize, r0, r3, r4, &allocate, TAG_OBJECT); - __ bind(&done_allocate); - - // Initialize the JSValue in r0. - __ LoadGlobalFunctionInitialMap(r1, r3, r4); - __ str(r3, FieldMemOperand(r0, HeapObject::kMapOffset)); - __ LoadRoot(r3, Heap::kEmptyFixedArrayRootIndex); - __ str(r3, FieldMemOperand(r0, JSObject::kPropertiesOffset)); - __ str(r3, FieldMemOperand(r0, JSObject::kElementsOffset)); - __ str(r2, FieldMemOperand(r0, JSValue::kValueOffset)); - STATIC_ASSERT(JSValue::kSize == 4 * kPointerSize); - __ Ret(); - - // Fallback to the runtime to allocate in new space. - __ bind(&allocate); - { - FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL); - __ Move(r3, Smi::FromInt(JSValue::kSize)); - __ Push(r1, r2, r3); - __ CallRuntime(Runtime::kAllocateInNewSpace, 1); - __ Pop(r1, r2); - } - __ b(&done_allocate); + // 5. Allocate a JSValue wrapper for the string. + __ AllocateJSValue(r0, r1, r2, r4, r5, &new_object); + __ Ret(); - // Fallback to the runtime to create new object. - __ bind(&rt_call); - { - FrameScope scope(masm, StackFrame::INTERNAL); - __ Push(r1, r2); - __ Push(r1, r3); // constructor function, original constructor - __ CallRuntime(Runtime::kNewObject, 2); - __ Pop(r1, r2); - } - __ str(r2, FieldMemOperand(r0, JSValue::kValueOffset)); - __ Ret(); + // 6. Fallback to the runtime to create new object. + __ bind(&new_object); + { + FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL); + __ Push(r2, r1, r3); // first argument, constructor, new target + __ CallRuntime(Runtime::kNewObject); + __ Pop(r2); } + __ str(r2, FieldMemOperand(r0, JSValue::kValueOffset)); + __ Ret(); } static void CallRuntimePassFunction( MacroAssembler* masm, Runtime::FunctionId function_id) { + // ----------- S t a t e ------------- + // -- r1 : target function (preserved for callee) + // -- r3 : new target (preserved for callee) + // ----------------------------------- + FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL); - // Push a copy of the function onto the stack. + // Push a copy of the target function and the new target. __ push(r1); + __ push(r3); // Push function as parameter to the runtime call. __ Push(r1); __ CallRuntime(function_id, 1); - // Restore receiver. + // Restore target function and new target. + __ pop(r3); __ pop(r1); } @@ -353,12 +416,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 ------------- // -- r0 : number of arguments // -- r1 : constructor function // -- r2 : allocation site or undefined - // -- r3 : original constructor + // -- r3 : new target // -- lr : return address // -- sp[...]: constructor arguments // ----------------------------------- @@ -374,178 +438,168 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm, __ push(r2); __ SmiTag(r0); __ push(r0); - __ push(r1); - __ push(r3); - - // 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(r2, Operand(debug_step_in_fp)); - __ ldr(r2, MemOperand(r2)); - __ tst(r2, r2); - __ b(ne, &rt_call); - - // Verify that the original constructor is a JSFunction. - __ CompareObjectType(r3, r5, r4, JS_FUNCTION_TYPE); - __ b(ne, &rt_call); - - // Load the initial map and verify that it is in fact a map. - // r3: original constructor - __ ldr(r2, FieldMemOperand(r3, JSFunction::kPrototypeOrInitialMapOffset)); - __ JumpIfSmi(r2, &rt_call); - __ CompareObjectType(r2, r5, r4, MAP_TYPE); - __ b(ne, &rt_call); - - // Fall back to runtime if the expected base constructor and base - // constructor differ. - __ ldr(r5, FieldMemOperand(r2, Map::kConstructorOrBackPointerOffset)); - __ cmp(r1, r5); - __ 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. - // r1: constructor function - // r2: initial map - __ CompareInstanceType(r2, r5, JS_FUNCTION_TYPE); - __ b(eq, &rt_call); - - if (!is_api_function) { - Label allocate; - MemOperand bit_field3 = FieldMemOperand(r2, Map::kBitField3Offset); - // Check if slack tracking is enabled. - __ ldr(r4, bit_field3); - __ DecodeField<Map::Counter>(r3, r4); - __ cmp(r3, Operand(Map::kSlackTrackingCounterEnd)); - __ b(lt, &allocate); - // Decrease generous allocation count. - __ sub(r4, r4, Operand(1 << Map::Counter::kShift)); - __ str(r4, bit_field3); - __ cmp(r3, Operand(Map::kSlackTrackingCounterEnd)); - __ b(ne, &allocate); - - __ Push(r1, r2); - - __ push(r2); // r2 = intial map - __ CallRuntime(Runtime::kFinalizeInstanceSize, 1); - - __ pop(r2); - __ pop(r1); - - __ bind(&allocate); - } - - // Now allocate the JSObject on the heap. - // r1: constructor function - // r2: initial map - Label rt_call_reload_new_target; - __ ldrb(r3, FieldMemOperand(r2, Map::kInstanceSizeOffset)); - __ Allocate(r3, r4, r5, r6, &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. - // r1: constructor function - // r2: initial map - // r3: object size - // r4: JSObject (not tagged) - __ LoadRoot(r6, Heap::kEmptyFixedArrayRootIndex); - __ mov(r5, r4); - DCHECK_EQ(0 * kPointerSize, JSObject::kMapOffset); - __ str(r2, MemOperand(r5, kPointerSize, PostIndex)); - DCHECK_EQ(1 * kPointerSize, JSObject::kPropertiesOffset); - __ str(r6, MemOperand(r5, kPointerSize, PostIndex)); - DCHECK_EQ(2 * kPointerSize, JSObject::kElementsOffset); - __ str(r6, MemOperand(r5, kPointerSize, PostIndex)); - - // Fill all the in-object properties with the appropriate filler. - // r1: constructor function - // r2: initial map - // r3: object size - // r4: JSObject (not tagged) - // r5: First in-object property of JSObject (not tagged) - DCHECK_EQ(3 * kPointerSize, JSObject::kHeaderSize); - __ LoadRoot(r6, Heap::kUndefinedValueRootIndex); - - if (!is_api_function) { - Label no_inobject_slack_tracking; - - // Check if slack tracking is enabled. - __ ldr(ip, FieldMemOperand(r2, Map::kBitField3Offset)); - __ DecodeField<Map::Counter>(ip); - __ cmp(ip, Operand(Map::kSlackTrackingCounterEnd)); - __ b(lt, &no_inobject_slack_tracking); - - // Allocate object with a slack. - __ ldr(r0, FieldMemOperand(r2, Map::kInstanceSizesOffset)); - __ Ubfx(r0, r0, Map::kInObjectPropertiesOrConstructorFunctionIndexByte * - kBitsPerByte, - kBitsPerByte); - __ ldr(r2, FieldMemOperand(r2, Map::kInstanceAttributesOffset)); - __ Ubfx(r2, r2, Map::kUnusedPropertyFieldsByte * kBitsPerByte, - kBitsPerByte); - __ sub(r0, r0, Operand(r2)); - __ add(r0, r5, Operand(r0, LSL, kPointerSizeLog2)); - // r0: offset of first field after pre-allocated fields - if (FLAG_debug_code) { - __ add(ip, r4, Operand(r3, LSL, kPointerSizeLog2)); // End of object. - __ cmp(r0, ip); - __ Assert(le, kUnexpectedNumberOfPreAllocatedPropertyFields); + if (create_implicit_receiver) { + // Try to allocate the object without transitioning into C code. If any of + // the preconditions is not met, the code bails out to the runtime call. + Label rt_call, allocated; + if (FLAG_inline_new) { + // Verify that the new target is a JSFunction. + __ CompareObjectType(r3, r5, r4, JS_FUNCTION_TYPE); + __ b(ne, &rt_call); + + // Load the initial map and verify that it is in fact a map. + // r3: new target + __ ldr(r2, + FieldMemOperand(r3, JSFunction::kPrototypeOrInitialMapOffset)); + __ JumpIfSmi(r2, &rt_call); + __ CompareObjectType(r2, r5, r4, MAP_TYPE); + __ b(ne, &rt_call); + + // Fall back to runtime if the expected base constructor and base + // constructor differ. + __ ldr(r5, FieldMemOperand(r2, Map::kConstructorOrBackPointerOffset)); + __ cmp(r1, r5); + __ 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. + // r1: constructor function + // r2: initial map + // r3: new target + __ CompareInstanceType(r2, r5, JS_FUNCTION_TYPE); + __ b(eq, &rt_call); + + // Now allocate the JSObject on the heap. + // r1: constructor function + // r2: initial map + // r3: new target + __ ldrb(r9, FieldMemOperand(r2, Map::kInstanceSizeOffset)); + + __ Allocate(r9, r4, r9, r6, &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. + // r1: constructor function + // r2: initial map + // r3: new target + // r4: JSObject (not HeapObject tagged - the actual address). + // r9: start of next object + __ LoadRoot(r6, Heap::kEmptyFixedArrayRootIndex); + __ mov(r5, r4); + STATIC_ASSERT(0 * kPointerSize == JSObject::kMapOffset); + __ str(r2, MemOperand(r5, kPointerSize, PostIndex)); + STATIC_ASSERT(1 * kPointerSize == JSObject::kPropertiesOffset); + __ str(r6, MemOperand(r5, kPointerSize, PostIndex)); + STATIC_ASSERT(2 * kPointerSize == JSObject::kElementsOffset); + __ str(r6, MemOperand(r5, 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(r4, r4, Operand(kHeapObjectTag)); + + // Fill all the in-object properties with the appropriate filler. + // r4: JSObject (tagged) + // r5: First in-object property of JSObject (not tagged) + __ LoadRoot(r6, Heap::kUndefinedValueRootIndex); + + if (!is_api_function) { + Label no_inobject_slack_tracking; + + // Check if slack tracking is enabled. + MemOperand bit_field3 = FieldMemOperand(r2, Map::kBitField3Offset); + // Check if slack tracking is enabled. + __ ldr(r0, bit_field3); + __ DecodeField<Map::ConstructionCounter>(ip, r0); + // ip: slack tracking counter + __ cmp(ip, Operand(Map::kSlackTrackingCounterEnd)); + __ b(lt, &no_inobject_slack_tracking); + __ push(ip); // Save allocation count value. + // Decrease generous allocation count. + __ sub(r0, r0, Operand(1 << Map::ConstructionCounter::kShift)); + __ str(r0, bit_field3); + + // Allocate object with a slack. + __ ldr(r0, FieldMemOperand(r2, Map::kInstanceAttributesOffset)); + __ Ubfx(r0, r0, Map::kUnusedPropertyFieldsByte * kBitsPerByte, + kBitsPerByte); + __ sub(r0, r9, Operand(r0, LSL, kPointerSizeLog2)); + // r0: offset of first field after pre-allocated fields + if (FLAG_debug_code) { + __ cmp(r5, r0); + __ Assert(le, kUnexpectedNumberOfPreAllocatedPropertyFields); + } + __ InitializeFieldsWithFiller(r5, r0, r6); + + // To allow truncation fill the remaining fields with one pointer + // filler map. + __ LoadRoot(r6, Heap::kOnePointerFillerMapRootIndex); + __ InitializeFieldsWithFiller(r5, r9, r6); + + __ pop(r0); // Restore allocation count value before decreasing. + __ cmp(r0, 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(r1, r3, r4, r2); + __ CallRuntime(Runtime::kFinalizeInstanceSize); + __ Pop(r1, r3, r4); + + // Continue with JSObject being successfully allocated + // r1: constructor function + // r3: new target + // r4: JSObject + __ jmp(&allocated); + + __ bind(&no_inobject_slack_tracking); } - __ InitializeFieldsWithFiller(r5, r0, r6); - // To allow for truncation. - __ LoadRoot(r6, Heap::kOnePointerFillerMapRootIndex); - // Fill the remaining fields with one pointer filler map. - __ bind(&no_inobject_slack_tracking); + __ InitializeFieldsWithFiller(r5, r9, r6); + + // Continue with JSObject being successfully allocated + // r1: constructor function + // r3: new target + // r4: JSObject + __ jmp(&allocated); } - __ add(r0, r4, Operand(r3, LSL, kPointerSizeLog2)); // End of object. - __ InitializeFieldsWithFiller(r5, r0, r6); + // Allocate the new receiver object using the runtime call. + // r1: constructor function + // r3: new target + __ bind(&rt_call); - // 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(r4, r4, Operand(kHeapObjectTag)); + // Push the constructor and new_target twice, second pair as arguments + // to the runtime call. + __ Push(r1, r3); + __ Push(r1, r3); // constructor function, new target + __ CallRuntime(Runtime::kNewObject); + __ mov(r4, r0); + __ Pop(r1, r3); - // Continue with JSObject being successfully allocated + // Receiver for constructor call allocated. + // r1: constructor function + // r3: new target // r4: JSObject - __ jmp(&allocated); + __ bind(&allocated); - // Reload the original constructor and fall-through. - __ bind(&rt_call_reload_new_target); - __ ldr(r3, MemOperand(sp, 0 * kPointerSize)); + // Retrieve smi-tagged arguments count from the stack. + __ ldr(r0, MemOperand(sp)); } - // Allocate the new receiver object using the runtime call. - // r1: constructor function - // r3: original constructor - __ bind(&rt_call); - - __ push(r1); // constructor function - __ push(r3); // original constructor - __ CallRuntime(Runtime::kNewObject, 2); - __ mov(r4, r0); - - // Receiver for constructor call allocated. - // r4: JSObject - __ bind(&allocated); - - // Restore the parameters. - __ pop(r3); - __ pop(r1); - - // Retrieve smi-tagged arguments count from the stack. - __ ldr(r0, MemOperand(sp)); __ SmiUntag(r0); - // Push new.target onto the construct frame. This is stored just below the - // receiver on the stack. - __ push(r3); - __ push(r4); - __ push(r4); + 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(r4); + __ push(r4); + } else { + __ PushRoot(Heap::kTheHoleValueRootIndex); + } // Set up pointer to last argument. __ add(r2, fp, Operand(StandardFrameConstants::kCallerSPOffset)); @@ -554,24 +608,25 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm, // r0: number of arguments // r1: constructor function // r2: address of last argument (caller sp) - // r3: number of arguments (smi-tagged) + // r3: new target + // r4: number of arguments (smi-tagged) // sp[0]: receiver // sp[1]: receiver - // sp[2]: new.target - // sp[3]: number of arguments (smi-tagged) + // sp[2]: number of arguments (smi-tagged) Label loop, entry; - __ SmiTag(r3, r0); + __ SmiTag(r4, r0); __ b(&entry); __ bind(&loop); - __ ldr(ip, MemOperand(r2, r3, LSL, kPointerSizeLog2 - 1)); + __ ldr(ip, MemOperand(r2, r4, LSL, kPointerSizeLog2 - 1)); __ push(ip); __ bind(&entry); - __ sub(r3, r3, Operand(2), SetCC); + __ sub(r4, r4, Operand(2), SetCC); __ b(ge, &loop); // Call the function. // r0: number of arguments // r1: constructor function + // r3: new target if (is_api_function) { __ ldr(cp, FieldMemOperand(r1, JSFunction::kContextOffset)); Handle<Code> code = @@ -579,156 +634,85 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm, __ Call(code, RelocInfo::CODE_TARGET); } else { ParameterCount actual(r0); - __ InvokeFunction(r1, actual, CALL_FUNCTION, NullCallWrapper()); + __ InvokeFunction(r1, r3, 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 context from the frame. // r0: result // sp[0]: receiver - // sp[1]: new.target - // sp[2]: number of arguments (smi-tagged) + // sp[1]: 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. - // r0: result - // sp[0]: receiver - // sp[1]: new.target - // sp[2]: number of arguments (smi-tagged) - __ JumpIfSmi(r0, &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. - __ CompareObjectType(r0, r1, r3, FIRST_SPEC_OBJECT_TYPE); - __ b(ge, &exit); - - // Throw away the result of the constructor invocation and use the - // on-stack receiver as the result. - __ bind(&use_receiver); - __ ldr(r0, MemOperand(sp)); - - // Remove receiver from the stack, remove caller arguments, and - // return. - __ bind(&exit); - // r0: result - // sp[0]: receiver (newly allocated object) - // sp[1]: new.target (original constructor) - // sp[2]: number of arguments (smi-tagged) - __ ldr(r1, MemOperand(sp, 2 * kPointerSize)); + 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. + // r0: result + // sp[0]: receiver + // sp[1]: number of arguments (smi-tagged) + __ JumpIfSmi(r0, &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. + __ CompareObjectType(r0, r1, r3, FIRST_JS_RECEIVER_TYPE); + __ b(ge, &exit); + + // Throw away the result of the constructor invocation and use the + // on-stack receiver as the result. + __ bind(&use_receiver); + __ ldr(r0, MemOperand(sp)); + + // Remove receiver from the stack, remove caller arguments, and + // return. + __ bind(&exit); + // r0: result + // sp[0]: receiver (newly allocated object) + // sp[1]: number of arguments (smi-tagged) + __ ldr(r1, MemOperand(sp, 1 * kPointerSize)); + } else { + __ ldr(r1, MemOperand(sp)); + } // Leave construct frame. } __ add(sp, sp, Operand(r1, LSL, kPointerSizeLog2 - 1)); __ add(sp, sp, Operand(kPointerSize)); - __ IncrementCounter(isolate->counters()->constructed_objects(), 1, r1, r2); + if (create_implicit_receiver) { + __ IncrementCounter(isolate->counters()->constructed_objects(), 1, r1, r2); + } __ Jump(lr); } 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 ------------- - // -- r0 : number of arguments - // -- r1 : constructor function - // -- r2 : allocation site or undefined - // -- r3 : original constructor - // -- lr : return address - // -- sp[...]: constructor arguments - // ----------------------------------- - - { - FrameScope frame_scope(masm, StackFrame::CONSTRUCT); - - __ AssertUndefinedOrAllocationSite(r2, r4); - __ push(r2); - - __ mov(r4, r0); - __ SmiTag(r4); - __ push(r4); // Smi-tagged arguments count. - - // Push new.target. - __ push(r3); - - // receiver is the hole. - __ LoadRoot(ip, Heap::kTheHoleValueRootIndex); - __ push(ip); - - // Set up pointer to last argument. - __ add(r2, fp, Operand(StandardFrameConstants::kCallerSPOffset)); - - // Copy arguments and receiver to the expression stack. - // r0: number of arguments - // r1: constructor function - // r2: address of last argument (caller sp) - // r4: number of arguments (smi-tagged) - // sp[0]: receiver - // sp[1]: new.target - // sp[2]: number of arguments (smi-tagged) - Label loop, entry; - __ b(&entry); - __ bind(&loop); - __ ldr(ip, MemOperand(r2, r4, LSL, kPointerSizeLog2 - 1)); - __ push(ip); - __ bind(&entry); - __ sub(r4, r4, Operand(2), SetCC); - __ b(ge, &loop); - - // Handle step in. - Label skip_step_in; - ExternalReference debug_step_in_fp = - ExternalReference::debug_step_in_fp_address(masm->isolate()); - __ mov(r2, Operand(debug_step_in_fp)); - __ ldr(r2, MemOperand(r2)); - __ tst(r2, r2); - __ b(eq, &skip_step_in); - - __ Push(r0); - __ Push(r1); - __ Push(r1); - __ CallRuntime(Runtime::kHandleStepInForDerivedConstructors, 1); - __ Pop(r1); - __ Pop(r0); - - __ bind(&skip_step_in); - - // Call the function. - // r0: number of arguments - // r1: constructor function - ParameterCount actual(r0); - __ InvokeFunction(r1, actual, CALL_FUNCTION, NullCallWrapper()); - - // Restore context from the frame. - // r0: result - // sp[0]: number of arguments (smi-tagged) - __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); - // Get arguments count, skipping over new.target. - __ ldr(r1, MemOperand(sp, kPointerSize)); +void Builtins::Generate_JSBuiltinsConstructStub(MacroAssembler* masm) { + Generate_JSConstructStubHelper(masm, false, false); +} - // Leave construct frame. - } - __ add(sp, sp, Operand(r1, LSL, kPointerSizeLog2 - 1)); - __ add(sp, sp, Operand(kPointerSize)); - __ Jump(lr); +void Builtins::Generate_ConstructedNonConstructable(MacroAssembler* masm) { + FrameScope scope(masm, StackFrame::INTERNAL); + __ push(r1); + __ CallRuntime(Runtime::kThrowConstructedNonConstructable); } @@ -756,7 +740,7 @@ static void Generate_CheckStackOverflow(MacroAssembler* masm, Register argc, __ b(gt, &okay); // Signed comparison. // Out of stack space. - __ CallRuntime(Runtime::kThrowStackOverflow, 0); + __ CallRuntime(Runtime::kThrowStackOverflow); __ bind(&okay); } @@ -863,6 +847,7 @@ void Builtins::Generate_JSConstructEntryTrampoline(MacroAssembler* masm) { // // The live registers are: // o r1: the JS function object being called. +// o r3: the new target // o cp: our context // o pp: the caller's constant pool pointer (if enabled) // o fp: the caller's frame pointer @@ -880,6 +865,11 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) { FrameScope frame_scope(masm, StackFrame::MANUAL); __ PushFixedFrame(r1); __ add(fp, sp, Operand(StandardFrameConstants::kFixedFrameSizeFromFp)); + __ push(r3); + + // Push zero for bytecode array offset. + __ mov(r0, Operand(0)); + __ push(r0); // Get the bytecode array from the function object and load the pointer to the // first entry into kInterpreterBytecodeRegister. @@ -908,7 +898,7 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) { __ LoadRoot(r2, Heap::kRealStackLimitRootIndex); __ cmp(r9, Operand(r2)); __ 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. @@ -939,7 +929,7 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) { __ cmp(sp, Operand(ip)); __ b(hs, &ok); __ push(kInterpreterBytecodeArrayRegister); - __ CallRuntime(Runtime::kStackGuard, 0); + __ CallRuntime(Runtime::kStackGuard); __ pop(kInterpreterBytecodeArrayRegister); __ bind(&ok); } @@ -947,8 +937,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, @@ -1028,7 +1018,7 @@ void Builtins::Generate_InterpreterPushArgsAndCall(MacroAssembler* masm) { void Builtins::Generate_InterpreterPushArgsAndConstruct(MacroAssembler* masm) { // ----------- S t a t e ------------- // -- r0 : argument count (not including receiver) - // -- r3 : original constructor + // -- r3 : new target // -- r1 : constructor to call // -- r2 : address of the first argument // ----------------------------------- @@ -1038,47 +1028,114 @@ void Builtins::Generate_InterpreterPushArgsAndConstruct(MacroAssembler* masm) { __ sub(r4, r2, r4); // Push a slot for the receiver to be constructed. - __ push(r0); + __ mov(ip, Operand::Zero()); + __ push(ip); // Push the arguments. Generate_InterpreterPushArgs(masm, r2, r4, r5); // Call the constructor with r0, r1, and r3 unmodified. - __ Jump(masm->isolate()->builtins()->Construct(), RelocInfo::CONSTRUCT_CALL); + __ Jump(masm->isolate()->builtins()->Construct(), RelocInfo::CODE_TARGET); } -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(r1, Operand(Smi::FromInt(static_cast<int>(type)))); + __ push(r1); + __ 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(r1, + MemOperand(kInterpreterRegisterFileRegister, + InterpreterFrameConstants::kFunctionFromRegisterPointer)); + __ ldr(r1, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset)); + __ ldr(kInterpreterBytecodeArrayRegister, + FieldMemOperand(r1, SharedFunctionInfo::kFunctionDataOffset)); + + if (FLAG_debug_code) { + // Check function data field is actually a BytecodeArray object. + __ SmiTst(kInterpreterBytecodeArrayRegister); + __ Assert(ne, kFunctionDataShouldBeBytecodeArrayOnInterpreterEntry); + __ CompareObjectType(kInterpreterBytecodeArrayRegister, r1, no_reg, + 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(r1, MemOperand(kInterpreterBytecodeArrayRegister, + kInterpreterBytecodeOffsetRegister)); + __ ldr(ip, MemOperand(kInterpreterDispatchTableRegister, r1, LSL, + kPointerSizeLog2)); + __ add(ip, ip, Operand(Code::kHeaderSize - kHeapObjectTag)); + __ mov(pc, ip); } -static void CallCompileOptimized(MacroAssembler* masm, bool concurrent) { - FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL); - // Push a copy of the function onto the stack. - __ push(r1); - // Push function as parameter to the runtime call. - __ Push(r1); - // Whether to compile in a background thread. - __ LoadRoot( - ip, concurrent ? Heap::kTrueValueRootIndex : Heap::kFalseValueRootIndex); - __ push(ip); +void Builtins::Generate_InterpreterNotifyDeoptimized(MacroAssembler* masm) { + Generate_InterpreterNotifyDeoptimizedHelper(masm, Deoptimizer::EAGER); +} - __ CallRuntime(Runtime::kCompileOptimized, 2); - // Restore receiver. - __ pop(r1); + +void Builtins::Generate_InterpreterNotifySoftDeoptimized(MacroAssembler* masm) { + Generate_InterpreterNotifyDeoptimizedHelper(masm, Deoptimizer::SOFT); +} + + +void Builtins::Generate_InterpreterNotifyLazyDeoptimized(MacroAssembler* masm) { + Generate_InterpreterNotifyDeoptimizedHelper(masm, Deoptimizer::LAZY); +} + + +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); } @@ -1094,13 +1151,14 @@ static void GenerateMakeCodeYoungAgainCommon(MacroAssembler* masm) { // the runtime: // r0 - contains return address (beginning of patch sequence) // r1 - isolate + // r3 - new target FrameScope scope(masm, StackFrame::MANUAL); - __ stm(db_w, sp, r0.bit() | r1.bit() | fp.bit() | lr.bit()); + __ stm(db_w, sp, r0.bit() | r1.bit() | r3.bit() | fp.bit() | lr.bit()); __ PrepareCallCFunction(2, 0, r2); __ mov(r1, Operand(ExternalReference::isolate_address(masm->isolate()))); __ CallCFunction( ExternalReference::get_make_code_young_function(masm->isolate()), 2); - __ ldm(ia_w, sp, r0.bit() | r1.bit() | fp.bit() | lr.bit()); + __ ldm(ia_w, sp, r0.bit() | r1.bit() | r3.bit() | fp.bit() | lr.bit()); __ mov(pc, r0); } @@ -1127,13 +1185,14 @@ void Builtins::Generate_MarkCodeAsExecutedOnce(MacroAssembler* masm) { // the runtime: // r0 - contains return address (beginning of patch sequence) // r1 - isolate + // r3 - new target FrameScope scope(masm, StackFrame::MANUAL); - __ stm(db_w, sp, r0.bit() | r1.bit() | fp.bit() | lr.bit()); + __ stm(db_w, sp, r0.bit() | r1.bit() | r3.bit() | fp.bit() | lr.bit()); __ PrepareCallCFunction(2, 0, r2); __ mov(r1, Operand(ExternalReference::isolate_address(masm->isolate()))); __ CallCFunction(ExternalReference::get_mark_code_as_executed_function( masm->isolate()), 2); - __ ldm(ia_w, sp, r0.bit() | r1.bit() | fp.bit() | lr.bit()); + __ ldm(ia_w, sp, r0.bit() | r1.bit() | r3.bit() | fp.bit() | lr.bit()); // Perform prologue operations usually performed by the young code stub. __ PushFixedFrame(r1); @@ -1165,7 +1224,7 @@ static void Generate_NotifyStubFailureHelper(MacroAssembler* masm, // registers. __ stm(db_w, sp, kJSCallerSaved | kCalleeSaved); // Pass the function and deoptimization type to the runtime system. - __ CallRuntime(Runtime::kNotifyStubFailure, 0, save_doubles); + __ CallRuntime(Runtime::kNotifyStubFailure, save_doubles); __ ldm(ia_w, sp, kJSCallerSaved | kCalleeSaved); } @@ -1191,7 +1250,7 @@ static void Generate_NotifyDeoptimizedHelper(MacroAssembler* masm, // Pass the function and deoptimization type to the runtime system. __ mov(r0, Operand(Smi::FromInt(static_cast<int>(type)))); __ push(r0); - __ CallRuntime(Runtime::kNotifyDeoptimized, 1); + __ CallRuntime(Runtime::kNotifyDeoptimized); } // Get the full codegen state from the stack and untag it -> r6. @@ -1231,6 +1290,109 @@ void Builtins::Generate_NotifyLazyDeoptimized(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, ip, ip); + __ cmp(ip, 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, ip, ip, FUNCTION_TEMPLATE_INFO_TYPE); + + // Otherwise load the parent function template and iterate. + __ ldr(type, + FieldMemOperand(type, FunctionTemplateInfo::kParentTemplateOffset), + eq); + __ b(&function_template_loop, eq); + + // 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(ip, FieldMemOperand(map, Map::kBitField3Offset)); + __ tst(ip, 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 ------------- + // -- r0 : number of arguments excluding receiver + // -- r1 : callee + // -- lr : return address + // -- sp[0] : last argument + // -- ... + // -- sp[4 * (argc - 1)] : first argument + // -- sp[4 * argc] : receiver + // ----------------------------------- + + // Load the FunctionTemplateInfo. + __ ldr(r3, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset)); + __ ldr(r3, FieldMemOperand(r3, SharedFunctionInfo::kFunctionDataOffset)); + + // Do the compatible receiver check. + Label receiver_check_failed; + __ ldr(r2, MemOperand(sp, r0, LSL, kPointerSizeLog2)); + CompatibleReceiverCheck(masm, r2, r3, r4, r5, r6, &receiver_check_failed); + + // Get the callback offset from the FunctionTemplateInfo, and jump to the + // beginning of the code. + __ ldr(r4, FieldMemOperand(r3, FunctionTemplateInfo::kCallCodeOffset)); + __ ldr(r4, FieldMemOperand(r4, CallHandlerInfo::kFastHandlerOffset)); + __ add(r4, r4, Operand(Code::kHeaderSize - kHeapObjectTag)); + __ Jump(r4); + + // Compatible receiver check failed: throw an Illegal Invocation exception. + __ bind(&receiver_check_failed); + // Drop the arguments (including the receiver) + __ add(r0, r0, Operand(1)); + __ add(sp, sp, Operand(r0, LSL, kPointerSizeLog2)); + __ TailCallRuntime(Runtime::kThrowIllegalInvocation); +} + + void Builtins::Generate_OnStackReplacement(MacroAssembler* masm) { // Lookup the function in the JavaScript frame. __ ldr(r0, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset)); @@ -1238,7 +1400,7 @@ void Builtins::Generate_OnStackReplacement(MacroAssembler* masm) { FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL); // Pass function as argument. __ push(r0); - __ CallRuntime(Runtime::kCompileForOnStackReplacement, 1); + __ CallRuntime(Runtime::kCompileForOnStackReplacement); } // If the code object is null, just return to the unoptimized code. @@ -1282,7 +1444,7 @@ void Builtins::Generate_OsrAfterStackCheck(MacroAssembler* masm) { __ b(hs, &ok); { FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL); - __ CallRuntime(Runtime::kStackGuard, 0); + __ CallRuntime(Runtime::kStackGuard); } __ Jump(masm->isolate()->builtins()->OnStackReplacement(), RelocInfo::CODE_TARGET); @@ -1293,7 +1455,120 @@ void Builtins::Generate_OsrAfterStackCheck(MacroAssembler* masm) { // static -void Builtins::Generate_FunctionCall(MacroAssembler* masm) { +void Builtins::Generate_DatePrototype_GetField(MacroAssembler* masm, + int field_index) { + // ----------- S t a t e ------------- + // -- lr : return address + // -- sp[0] : receiver + // ----------------------------------- + + // 1. Pop receiver into r0 and check that it's actually a JSDate object. + Label receiver_not_date; + { + __ Pop(r0); + __ JumpIfSmi(r0, &receiver_not_date); + __ CompareObjectType(r0, r1, r2, JS_DATE_TYPE); + __ b(ne, &receiver_not_date); + } + + // 2. Load the specified date field, falling back to the runtime as necessary. + if (field_index == JSDate::kDateValue) { + __ ldr(r0, FieldMemOperand(r0, JSDate::kValueOffset)); + } else { + if (field_index < JSDate::kFirstUncachedField) { + Label stamp_mismatch; + __ mov(r1, Operand(ExternalReference::date_cache_stamp(masm->isolate()))); + __ ldr(r1, MemOperand(r1)); + __ ldr(ip, FieldMemOperand(r0, JSDate::kCacheStampOffset)); + __ cmp(r1, ip); + __ b(ne, &stamp_mismatch); + __ ldr(r0, FieldMemOperand( + r0, JSDate::kValueOffset + field_index * kPointerSize)); + __ Ret(); + __ bind(&stamp_mismatch); + } + FrameScope scope(masm, StackFrame::INTERNAL); + __ PrepareCallCFunction(2, r1); + __ mov(r1, Operand(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 ------------- + // -- r0 : argc + // -- sp[0] : argArray + // -- sp[4] : thisArg + // -- sp[8] : receiver + // ----------------------------------- + + // 1. Load receiver into r1, argArray into r0 (if present), remove all + // arguments from the stack (including the receiver), and push thisArg (if + // present) instead. + { + __ LoadRoot(r2, Heap::kUndefinedValueRootIndex); + __ mov(r3, r2); + __ ldr(r1, MemOperand(sp, r0, LSL, kPointerSizeLog2)); // receiver + __ sub(r4, r0, Operand(1), SetCC); + __ ldr(r2, MemOperand(sp, r4, LSL, kPointerSizeLog2), ge); // thisArg + __ sub(r4, r4, Operand(1), SetCC, ge); + __ ldr(r3, MemOperand(sp, r4, LSL, kPointerSizeLog2), ge); // argArray + __ add(sp, sp, Operand(r0, LSL, kPointerSizeLog2)); + __ str(r2, MemOperand(sp, 0)); + __ mov(r0, r3); + } + + // ----------- S t a t e ------------- + // -- r0 : argArray + // -- r1 : receiver + // -- sp[0] : thisArg + // ----------------------------------- + + // 2. Make sure the receiver is actually callable. + Label receiver_not_callable; + __ JumpIfSmi(r1, &receiver_not_callable); + __ ldr(r4, FieldMemOperand(r1, HeapObject::kMapOffset)); + __ ldrb(r4, FieldMemOperand(r4, Map::kBitFieldOffset)); + __ tst(r4, Operand(1 << Map::kIsCallable)); + __ b(eq, &receiver_not_callable); + + // 3. Tail call with no arguments if argArray is null or undefined. + Label no_arguments; + __ JumpIfRoot(r0, Heap::kNullValueRootIndex, &no_arguments); + __ JumpIfRoot(r0, Heap::kUndefinedValueRootIndex, &no_arguments); + + // 4a. Apply the receiver to the given argArray (passing undefined for + // new.target). + __ LoadRoot(r3, Heap::kUndefinedValueRootIndex); + __ 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(r0, Operand(0)); + __ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET); + } + + // 4c. The receiver is not callable, throw an appropriate TypeError. + __ bind(&receiver_not_callable); + { + __ str(r1, MemOperand(sp, 0)); + __ TailCallRuntime(Runtime::kThrowApplyNonFunction); + } +} + + +// static +void Builtins::Generate_FunctionPrototypeCall(MacroAssembler* masm) { // 1. Make sure we have at least one argument. // r0: actual number of arguments { @@ -1336,185 +1611,128 @@ 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(r0); - - __ ldr(key, MemOperand(fp, indexOffset)); - __ add(key, key, Operand(1 << kSmiTagSize)); - __ str(key, MemOperand(fp, indexOffset)); - - // Test if the copy loop has finished copying all the elements from the - // arguments object. - __ bind(&entry); - __ ldr(r1, MemOperand(fp, limitOffset)); - __ cmp(key, r1); - __ b(ne, &loop); - - // On exit, the pushed arguments count is in r0, untagged - __ mov(r0, key); - __ SmiUntag(r0); -} - - -// Used by FunctionApply and ReflectApply -static void Generate_ApplyHelper(MacroAssembler* masm, bool targetIsArgument) { - const int kFormalParameters = targetIsArgument ? 3 : 2; - const int kStackSize = kFormalParameters + 1; +void Builtins::Generate_ReflectApply(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- r0 : argc + // -- sp[0] : argumentsList + // -- sp[4] : thisArgument + // -- sp[8] : target + // -- sp[12] : receiver + // ----------------------------------- + // 1. Load target into r1 (if present), argumentsList into r0 (if present), + // remove all arguments from the stack (including the receiver), and push + // thisArgument (if present) instead. { - FrameAndConstantPoolScope 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; - - // Push the vector. - __ ldr(r1, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset)); - __ ldr(r1, FieldMemOperand(r1, SharedFunctionInfo::kFeedbackVectorOffset)); - __ Push(r1); - - __ ldr(r0, MemOperand(fp, kFunctionOffset)); // get the function - __ ldr(r1, MemOperand(fp, kArgumentsOffset)); // get the args array - __ Push(r0, r1); - if (targetIsArgument) { - __ InvokeBuiltin(Context::REFLECT_APPLY_PREPARE_BUILTIN_INDEX, - CALL_FUNCTION); - } else { - __ InvokeBuiltin(Context::APPLY_PREPARE_BUILTIN_INDEX, CALL_FUNCTION); - } - - Generate_CheckStackOverflow(masm, r0, kArgcIsSmiTagged); + __ LoadRoot(r1, Heap::kUndefinedValueRootIndex); + __ mov(r2, r1); + __ mov(r3, r1); + __ sub(r4, r0, Operand(1), SetCC); + __ ldr(r1, MemOperand(sp, r4, LSL, kPointerSizeLog2), ge); // target + __ sub(r4, r4, Operand(1), SetCC, ge); + __ ldr(r2, MemOperand(sp, r4, LSL, kPointerSizeLog2), ge); // thisArgument + __ sub(r4, r4, Operand(1), SetCC, ge); + __ ldr(r3, MemOperand(sp, r4, LSL, kPointerSizeLog2), ge); // argumentsList + __ add(sp, sp, Operand(r0, LSL, kPointerSizeLog2)); + __ str(r2, MemOperand(sp, 0)); + __ mov(r0, r3); + } - // Push current limit and index. - const int kIndexOffset = kVectorOffset - (2 * kPointerSize); - const int kLimitOffset = kVectorOffset - (1 * kPointerSize); - __ mov(r1, Operand::Zero()); - __ ldr(r2, MemOperand(fp, kReceiverOffset)); - __ Push(r0, r1, r2); // limit, initial index and receiver. + // ----------- S t a t e ------------- + // -- r0 : argumentsList + // -- r1 : target + // -- sp[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(r1, &target_not_callable); + __ ldr(r4, FieldMemOperand(r1, HeapObject::kMapOffset)); + __ ldrb(r4, FieldMemOperand(r4, Map::kBitFieldOffset)); + __ tst(r4, Operand(1 << Map::kIsCallable)); + __ b(eq, &target_not_callable); - // Call the callable. - // TODO(bmeurer): This should be a tail call according to ES6. - __ ldr(r1, MemOperand(fp, kFunctionOffset)); - __ Call(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET); + // 3a. Apply the target to the given argumentsList (passing undefined for + // new.target). + __ LoadRoot(r3, Heap::kUndefinedValueRootIndex); + __ Jump(masm->isolate()->builtins()->Apply(), RelocInfo::CODE_TARGET); - // Tear down the internal frame and remove function, receiver and args. + // 3b. The target is not callable, throw an appropriate TypeError. + __ bind(&target_not_callable); + { + __ str(r1, MemOperand(sp, 0)); + __ TailCallRuntime(Runtime::kThrowApplyNonFunction); } - __ add(sp, sp, Operand(kStackSize * kPointerSize)); - __ Jump(lr); } -static void Generate_ConstructHelper(MacroAssembler* masm) { - const int kFormalParameters = 3; - const int kStackSize = kFormalParameters + 1; +void Builtins::Generate_ReflectConstruct(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- r0 : argc + // -- sp[0] : new.target (optional) + // -- sp[4] : argumentsList + // -- sp[8] : target + // -- sp[12] : receiver + // ----------------------------------- + // 1. Load target into r1 (if present), argumentsList into r0 (if present), + // new.target into r3 (if present, otherwise use target), remove all + // arguments from the stack (including the receiver), and push thisArgument + // (if present) instead. { - FrameAndConstantPoolScope frame_scope(masm, StackFrame::INTERNAL); - const int kNewTargetOffset = kFPOnStackSize + kPCOnStackSize; - const int kArgumentsOffset = kNewTargetOffset + kPointerSize; - const int kFunctionOffset = kArgumentsOffset + kPointerSize; - static const int kVectorOffset = - InternalFrameConstants::kCodeOffset - 1 * kPointerSize; - - // Push the vector. - __ ldr(r1, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset)); - __ ldr(r1, FieldMemOperand(r1, SharedFunctionInfo::kFeedbackVectorOffset)); - __ Push(r1); - - // If newTarget is not supplied, set it to constructor - Label validate_arguments; - __ ldr(r0, MemOperand(fp, kNewTargetOffset)); - __ CompareRoot(r0, Heap::kUndefinedValueRootIndex); - __ b(ne, &validate_arguments); - __ ldr(r0, MemOperand(fp, kFunctionOffset)); - __ str(r0, MemOperand(fp, kNewTargetOffset)); - - // Validate arguments - __ bind(&validate_arguments); - __ ldr(r0, MemOperand(fp, kFunctionOffset)); // get the function - __ push(r0); - __ ldr(r0, MemOperand(fp, kArgumentsOffset)); // get the args array - __ push(r0); - __ ldr(r0, MemOperand(fp, kNewTargetOffset)); // get the new.target - __ push(r0); - __ InvokeBuiltin(Context::REFLECT_CONSTRUCT_PREPARE_BUILTIN_INDEX, - CALL_FUNCTION); - - Generate_CheckStackOverflow(masm, r0, kArgcIsSmiTagged); - - // Push current limit and index. - const int kIndexOffset = kVectorOffset - (2 * kPointerSize); - const int kLimitOffset = kVectorOffset - (1 * kPointerSize); - __ push(r0); // limit - __ mov(r1, Operand::Zero()); // initial index - __ push(r1); - // Push the constructor function as callee. - __ ldr(r0, MemOperand(fp, kFunctionOffset)); - __ push(r0); - - // Copy all arguments from the array to the stack. - Generate_PushAppliedArguments(masm, kVectorOffset, kArgumentsOffset, - kIndexOffset, kLimitOffset); - - // Use undefined feedback vector - __ LoadRoot(r2, Heap::kUndefinedValueRootIndex); - __ ldr(r1, MemOperand(fp, kFunctionOffset)); - __ ldr(r4, MemOperand(fp, kNewTargetOffset)); - - // Call the function. - CallConstructStub stub(masm->isolate(), SUPER_CONSTRUCTOR_CALL); - __ Call(stub.GetCode(), RelocInfo::CONSTRUCT_CALL); - - // Leave internal frame. + __ LoadRoot(r1, Heap::kUndefinedValueRootIndex); + __ mov(r2, r1); + __ str(r2, MemOperand(sp, r0, LSL, kPointerSizeLog2)); // receiver + __ sub(r4, r0, Operand(1), SetCC); + __ ldr(r1, MemOperand(sp, r4, LSL, kPointerSizeLog2), ge); // target + __ mov(r3, r1); // new.target defaults to target + __ sub(r4, r4, Operand(1), SetCC, ge); + __ ldr(r2, MemOperand(sp, r4, LSL, kPointerSizeLog2), ge); // argumentsList + __ sub(r4, r4, Operand(1), SetCC, ge); + __ ldr(r3, MemOperand(sp, r4, LSL, kPointerSizeLog2), ge); // new.target + __ add(sp, sp, Operand(r0, LSL, kPointerSizeLog2)); + __ mov(r0, r2); } - __ add(sp, sp, Operand(kStackSize * kPointerSize)); - __ Jump(lr); -} + // ----------- S t a t e ------------- + // -- r0 : argumentsList + // -- r3 : new.target + // -- r1 : target + // -- sp[0] : receiver (undefined) + // ----------------------------------- -void Builtins::Generate_FunctionApply(MacroAssembler* masm) { - Generate_ApplyHelper(masm, false); -} + // 2. Make sure the target is actually a constructor. + Label target_not_constructor; + __ JumpIfSmi(r1, &target_not_constructor); + __ ldr(r4, FieldMemOperand(r1, HeapObject::kMapOffset)); + __ ldrb(r4, FieldMemOperand(r4, Map::kBitFieldOffset)); + __ tst(r4, Operand(1 << Map::kIsConstructor)); + __ b(eq, &target_not_constructor); + // 3. Make sure the target is actually a constructor. + Label new_target_not_constructor; + __ JumpIfSmi(r3, &new_target_not_constructor); + __ ldr(r4, FieldMemOperand(r3, HeapObject::kMapOffset)); + __ ldrb(r4, FieldMemOperand(r4, Map::kBitFieldOffset)); + __ tst(r4, Operand(1 << Map::kIsConstructor)); + __ b(eq, &new_target_not_constructor); -void Builtins::Generate_ReflectApply(MacroAssembler* masm) { - Generate_ApplyHelper(masm, true); -} + // 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); + { + __ str(r1, MemOperand(sp, 0)); + __ TailCallRuntime(Runtime::kThrowCalledNonCallable); + } -void Builtins::Generate_ReflectConstruct(MacroAssembler* masm) { - Generate_ConstructHelper(masm); + // 4c. The new.target is not a constructor, throw an appropriate TypeError. + __ bind(&new_target_not_constructor); + { + __ str(r3, MemOperand(sp, 0)); + __ TailCallRuntime(Runtime::kThrowCalledNonCallable); + } } @@ -1524,6 +1742,7 @@ static void ArgumentAdaptorStackCheck(MacroAssembler* masm, // -- r0 : actual number of arguments // -- r1 : function (passed through to callee) // -- r2 : expected number of arguments + // -- r3 : new target (passed through to callee) // ----------------------------------- // Check the stack for overflow. We are not trying to catch // interruptions (e.g. debug break and preemption) here, so the "real stack @@ -1565,6 +1784,130 @@ static void LeaveArgumentsAdaptorFrame(MacroAssembler* masm) { // static +void Builtins::Generate_Apply(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- r0 : argumentsList + // -- r1 : target + // -- r3 : new.target (checked to be constructor or undefined) + // -- sp[0] : thisArgument + // ----------------------------------- + + // Create the list of arguments from the array-like argumentsList. + { + Label create_arguments, create_array, create_runtime, done_create; + __ JumpIfSmi(r0, &create_runtime); + + // Load the map of argumentsList into r2. + __ ldr(r2, FieldMemOperand(r0, HeapObject::kMapOffset)); + + // Load native context into r4. + __ ldr(r4, NativeContextMemOperand()); + + // Check if argumentsList is an (unmodified) arguments object. + __ ldr(ip, ContextMemOperand(r4, Context::SLOPPY_ARGUMENTS_MAP_INDEX)); + __ cmp(ip, r2); + __ b(eq, &create_arguments); + __ ldr(ip, ContextMemOperand(r4, Context::STRICT_ARGUMENTS_MAP_INDEX)); + __ cmp(ip, r2); + __ b(eq, &create_arguments); + + // Check if argumentsList is a fast JSArray. + __ CompareInstanceType(r2, ip, JS_ARRAY_TYPE); + __ b(eq, &create_array); + + // Ask the runtime to create the list (actually a FixedArray). + __ bind(&create_runtime); + { + FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL); + __ Push(r1, r3, r0); + __ CallRuntime(Runtime::kCreateListFromArrayLike); + __ Pop(r1, r3); + __ ldr(r2, FieldMemOperand(r0, FixedArray::kLengthOffset)); + __ SmiUntag(r2); + } + __ jmp(&done_create); + + // Try to create the list from an arguments object. + __ bind(&create_arguments); + __ ldr(r2, + FieldMemOperand(r0, JSObject::kHeaderSize + + Heap::kArgumentsLengthIndex * kPointerSize)); + __ ldr(r4, FieldMemOperand(r0, JSObject::kElementsOffset)); + __ ldr(ip, FieldMemOperand(r4, FixedArray::kLengthOffset)); + __ cmp(r2, ip); + __ b(ne, &create_runtime); + __ SmiUntag(r2); + __ mov(r0, r4); + __ b(&done_create); + + // Try to create the list from a JSArray object. + __ bind(&create_array); + __ ldr(r2, FieldMemOperand(r2, Map::kBitField2Offset)); + __ DecodeField<Map::ElementsKindBits>(r2); + STATIC_ASSERT(FAST_SMI_ELEMENTS == 0); + STATIC_ASSERT(FAST_HOLEY_SMI_ELEMENTS == 1); + STATIC_ASSERT(FAST_ELEMENTS == 2); + __ cmp(r2, Operand(FAST_ELEMENTS)); + __ b(hi, &create_runtime); + __ cmp(r2, Operand(FAST_HOLEY_SMI_ELEMENTS)); + __ b(eq, &create_runtime); + __ ldr(r2, FieldMemOperand(r0, JSArray::kLengthOffset)); + __ ldr(r0, FieldMemOperand(r0, JSArray::kElementsOffset)); + __ SmiUntag(r2); + + __ 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(ip, Heap::kRealStackLimitRootIndex); + // Make ip the space we have left. The stack might already be overflowed + // here which will cause ip to become negative. + __ sub(ip, sp, ip); + // Check if the arguments will overflow the stack. + __ cmp(ip, Operand(r2, LSL, kPointerSizeLog2)); + __ b(gt, &done); // Signed comparison. + __ TailCallRuntime(Runtime::kThrowStackOverflow); + __ bind(&done); + } + + // ----------- S t a t e ------------- + // -- r1 : target + // -- r0 : args (a FixedArray built from argumentsList) + // -- r2 : len (number of elements to push from args) + // -- r3 : new.target (checked to be constructor or undefined) + // -- sp[0] : thisArgument + // ----------------------------------- + + // Push arguments onto the stack (thisArgument is already on the stack). + { + __ mov(r4, Operand(0)); + Label done, loop; + __ bind(&loop); + __ cmp(r4, r2); + __ b(eq, &done); + __ add(ip, r0, Operand(r4, LSL, kPointerSizeLog2)); + __ ldr(ip, FieldMemOperand(ip, FixedArray::kHeaderSize)); + __ Push(ip); + __ add(r4, r4, Operand(1)); + __ b(&loop); + __ bind(&done); + __ Move(r0, r4); + } + + // Dispatch to Call or Construct depending on whether new.target is undefined. + { + __ CompareRoot(r3, 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) { // ----------- S t a t e ------------- @@ -1655,17 +1998,121 @@ void Builtins::Generate_CallFunction(MacroAssembler* masm, __ ldr(r2, FieldMemOperand(r2, SharedFunctionInfo::kFormalParameterCountOffset)); __ SmiUntag(r2); - __ ldr(r3, FieldMemOperand(r1, JSFunction::kCodeEntryOffset)); ParameterCount actual(r0); ParameterCount expected(r2); - __ InvokeCode(r3, expected, actual, JUMP_FUNCTION, NullCallWrapper()); + __ InvokeFunctionCode(r1, 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(r1); + __ CallRuntime(Runtime::kThrowConstructorNonCallableError); + } +} + + +namespace { + +void Generate_PushBoundArguments(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- r0 : the number of arguments (not including the receiver) + // -- r1 : target (checked to be a JSBoundFunction) + // -- r3 : new.target (only in case of [[Construct]]) + // ----------------------------------- + + // Load [[BoundArguments]] into r2 and length of that into r4. + Label no_bound_arguments; + __ ldr(r2, FieldMemOperand(r1, JSBoundFunction::kBoundArgumentsOffset)); + __ ldr(r4, FieldMemOperand(r2, FixedArray::kLengthOffset)); + __ SmiUntag(r4); + __ cmp(r4, Operand(0)); + __ b(eq, &no_bound_arguments); + { + // ----------- S t a t e ------------- + // -- r0 : the number of arguments (not including the receiver) + // -- r1 : target (checked to be a JSBoundFunction) + // -- r2 : the [[BoundArguments]] (implemented as FixedArray) + // -- r3 : new.target (only in case of [[Construct]]) + // -- r4 : the number of [[BoundArguments]] + // ----------------------------------- + + // Reserve stack space for the [[BoundArguments]]. + { + Label done; + __ sub(sp, sp, Operand(r4, LSL, kPointerSizeLog2)); + // 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(sp, Heap::kRealStackLimitRootIndex); + __ b(gt, &done); // Signed comparison. + // Restore the stack pointer. + __ add(sp, sp, Operand(r4, LSL, kPointerSizeLog2)); + { + FrameScope scope(masm, StackFrame::MANUAL); + __ EnterFrame(StackFrame::INTERNAL); + __ CallRuntime(Runtime::kThrowStackOverflow); + } + __ bind(&done); + } + + // Relocate arguments down the stack. + { + Label loop, done_loop; + __ mov(r5, Operand(0)); + __ bind(&loop); + __ cmp(r5, r0); + __ b(gt, &done_loop); + __ ldr(ip, MemOperand(sp, r4, LSL, kPointerSizeLog2)); + __ str(ip, MemOperand(sp, r5, LSL, kPointerSizeLog2)); + __ add(r4, r4, Operand(1)); + __ add(r5, r5, Operand(1)); + __ b(&loop); + __ bind(&done_loop); + } + + // Copy [[BoundArguments]] to the stack (below the arguments). + { + Label loop; + __ ldr(r4, FieldMemOperand(r2, FixedArray::kLengthOffset)); + __ SmiUntag(r4); + __ add(r2, r2, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); + __ bind(&loop); + __ sub(r4, r4, Operand(1), SetCC); + __ ldr(ip, MemOperand(r2, r4, LSL, kPointerSizeLog2)); + __ str(ip, MemOperand(sp, r0, LSL, kPointerSizeLog2)); + __ add(r0, r0, Operand(1)); + __ b(gt, &loop); + } } + __ bind(&no_bound_arguments); +} + +} // namespace + + +// static +void Builtins::Generate_CallBoundFunction(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- r0 : the number of arguments (not including the receiver) + // -- r1 : the function to call (checked to be a JSBoundFunction) + // ----------------------------------- + __ AssertBoundFunction(r1); + + // Patch the receiver to [[BoundThis]]. + __ ldr(ip, FieldMemOperand(r1, JSBoundFunction::kBoundThisOffset)); + __ str(ip, MemOperand(sp, r0, LSL, kPointerSizeLog2)); + + // Push the [[BoundArguments]] onto the stack. + Generate_PushBoundArguments(masm); + + // Call the [[BoundTargetFunction]] via the Call builtin. + __ ldr(r1, FieldMemOperand(r1, JSBoundFunction::kBoundTargetFunctionOffset)); + __ mov(ip, Operand(ExternalReference(Builtins::kCall_ReceiverIsAny, + masm->isolate()))); + __ ldr(ip, MemOperand(ip)); + __ add(pc, ip, Operand(Code::kHeaderSize - kHeapObjectTag)); } @@ -1682,14 +2129,20 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode) { __ CompareObjectType(r1, r4, r5, JS_FUNCTION_TYPE); __ Jump(masm->isolate()->builtins()->CallFunction(mode), RelocInfo::CODE_TARGET, eq); - __ cmp(r5, Operand(JS_FUNCTION_PROXY_TYPE)); + __ cmp(r5, Operand(JS_BOUND_FUNCTION_TYPE)); + __ Jump(masm->isolate()->builtins()->CallBoundFunction(), + RelocInfo::CODE_TARGET, eq); + __ cmp(r5, Operand(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(r1, FieldMemOperand(r1, JSFunctionProxy::kCallTrapOffset)); - __ AssertNotSmi(r1); - __ b(&non_smi); + // 1. Runtime fallback for Proxy [[Call]]. + __ Push(r1); + // Increase the arguments size to include the pushed function and the + // existing receiver on the stack. + __ add(r0, r0, 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). @@ -1701,7 +2154,7 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode) { // Overwrite the original receiver the (original) target. __ str(r1, MemOperand(sp, r0, LSL, kPointerSizeLog2)); // Let the "call_as_function_delegate" take care of the rest. - __ LoadGlobalFunction(Context::CALL_AS_FUNCTION_DELEGATE_INDEX, r1); + __ LoadNativeContextSlot(Context::CALL_AS_FUNCTION_DELEGATE_INDEX, r1); __ Jump(masm->isolate()->builtins()->CallFunction( ConvertReceiverMode::kNotNullOrUndefined), RelocInfo::CODE_TARGET); @@ -1711,7 +2164,7 @@ void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode) { { FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL); __ Push(r1); - __ CallRuntime(Runtime::kThrowCalledNonCallable, 1); + __ CallRuntime(Runtime::kThrowCalledNonCallable); } } @@ -1721,10 +2174,9 @@ void Builtins::Generate_ConstructFunction(MacroAssembler* masm) { // ----------- S t a t e ------------- // -- r0 : the number of arguments (not including the receiver) // -- r1 : the constructor to call (checked to be a JSFunction) - // -- r3 : the original constructor (checked to be a JSFunction) + // -- r3 : the new target (checked to be a constructor) // ----------------------------------- __ AssertFunction(r1); - __ AssertFunction(r3); // Calling convention for function specific ConstructStubs require // r2 to contain either an AllocationSite or undefined. @@ -1739,17 +2191,47 @@ void Builtins::Generate_ConstructFunction(MacroAssembler* masm) { // static +void Builtins::Generate_ConstructBoundFunction(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- r0 : the number of arguments (not including the receiver) + // -- r1 : the function to call (checked to be a JSBoundFunction) + // -- r3 : the new target (checked to be a constructor) + // ----------------------------------- + __ AssertBoundFunction(r1); + + // Push the [[BoundArguments]] onto the stack. + Generate_PushBoundArguments(masm); + + // Patch new.target to [[BoundTargetFunction]] if new.target equals target. + __ cmp(r1, r3); + __ ldr(r3, FieldMemOperand(r1, JSBoundFunction::kBoundTargetFunctionOffset), + eq); + + // Construct the [[BoundTargetFunction]] via the Construct builtin. + __ ldr(r1, FieldMemOperand(r1, JSBoundFunction::kBoundTargetFunctionOffset)); + __ mov(ip, Operand(ExternalReference(Builtins::kConstruct, masm->isolate()))); + __ ldr(ip, MemOperand(ip)); + __ add(pc, ip, Operand(Code::kHeaderSize - kHeapObjectTag)); +} + + +// static void Builtins::Generate_ConstructProxy(MacroAssembler* masm) { // ----------- S t a t e ------------- // -- r0 : the number of arguments (not including the receiver) - // -- r1 : the constructor to call (checked to be a JSFunctionProxy) - // -- r3 : the original constructor (either the same as the constructor or + // -- r1 : the constructor to call (checked to be a JSProxy) + // -- r3 : 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(r1, FieldMemOperand(r1, JSFunctionProxy::kConstructTrapOffset)); - __ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET); + // Call into the Runtime for Proxy [[Construct]]. + __ Push(r1); + __ Push(r3); + // Include the pushed new_target, constructor and the receiver. + __ add(r0, r0, Operand(3)); + // Tail-call to the runtime. + __ JumpToExternalReference( + ExternalReference(Runtime::kJSProxyConstruct, masm->isolate())); } @@ -1758,23 +2240,32 @@ void Builtins::Generate_Construct(MacroAssembler* masm) { // ----------- S t a t e ------------- // -- r0 : the number of arguments (not including the receiver) // -- r1 : the constructor to call (can be any Object) - // -- r3 : the original constructor (either the same as the constructor or + // -- r3 : 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(r1, &non_constructor); - __ ldr(r4, FieldMemOperand(r1, HeapObject::kMapOffset)); + + // Dispatch based on instance type. + __ CompareObjectType(r1, r4, r5, JS_FUNCTION_TYPE); + __ Jump(masm->isolate()->builtins()->ConstructFunction(), + RelocInfo::CODE_TARGET, eq); + + // Check if target has a [[Construct]] internal method. __ ldrb(r2, FieldMemOperand(r4, Map::kBitFieldOffset)); __ tst(r2, Operand(1 << Map::kIsConstructor)); __ b(eq, &non_constructor); - // Dispatch based on instance type. - __ CompareInstanceType(r4, r5, JS_FUNCTION_TYPE); - __ Jump(masm->isolate()->builtins()->ConstructFunction(), + // Only dispatch to bound functions after checking whether they are + // constructors. + __ cmp(r5, Operand(JS_BOUND_FUNCTION_TYPE)); + __ Jump(masm->isolate()->builtins()->ConstructBoundFunction(), RelocInfo::CODE_TARGET, eq); - __ cmp(r5, Operand(JS_FUNCTION_PROXY_TYPE)); + + // Only dispatch to proxies after checking whether they are constructors. + __ cmp(r5, Operand(JS_PROXY_TYPE)); __ Jump(masm->isolate()->builtins()->ConstructProxy(), RelocInfo::CODE_TARGET, eq); @@ -1783,7 +2274,7 @@ void Builtins::Generate_Construct(MacroAssembler* masm) { // Overwrite the original receiver with the (original) target. __ str(r1, MemOperand(sp, r0, LSL, kPointerSizeLog2)); // Let the "call_as_constructor_delegate" take care of the rest. - __ LoadGlobalFunction(Context::CALL_AS_CONSTRUCTOR_DELEGATE_INDEX, r1); + __ LoadNativeContextSlot(Context::CALL_AS_CONSTRUCTOR_DELEGATE_INDEX, r1); __ Jump(masm->isolate()->builtins()->CallFunction(), RelocInfo::CODE_TARGET); } @@ -1791,11 +2282,8 @@ void Builtins::Generate_Construct(MacroAssembler* masm) { // Called Construct on an Object that doesn't have a [[Construct]] internal // method. __ bind(&non_constructor); - { - FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL); - __ Push(r1); - __ CallRuntime(Runtime::kThrowCalledNonCallable, 1); - } + __ Jump(masm->isolate()->builtins()->ConstructedNonConstructable(), + RelocInfo::CODE_TARGET); } @@ -1804,14 +2292,12 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { // -- r0 : actual number of arguments // -- r1 : function (passed through to callee) // -- r2 : expected number of arguments + // -- r3 : new target (passed through to callee) // ----------------------------------- - Label stack_overflow; - ArgumentAdaptorStackCheck(masm, &stack_overflow); - Label invoke, dont_adapt_arguments; + Label invoke, dont_adapt_arguments, stack_overflow; Label enough, too_few; - __ ldr(r3, FieldMemOperand(r1, JSFunction::kCodeEntryOffset)); __ cmp(r0, r2); __ b(lt, &too_few); __ cmp(r2, Operand(SharedFunctionInfo::kDontAdaptArgumentsSentinel)); @@ -1820,12 +2306,13 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { { // Enough parameters: actual >= expected __ bind(&enough); EnterArgumentsAdaptorFrame(masm); + ArgumentAdaptorStackCheck(masm, &stack_overflow); // Calculate copy start address into r0 and copy end address into r4. // r0: actual number of arguments as a smi // r1: function // r2: expected number of arguments - // r3: code entry to call + // r3: new target (passed through to callee) __ add(r0, fp, Operand::PointerOffsetFromSmiKey(r0)); // adjust for return address and receiver __ add(r0, r0, Operand(2 * kPointerSize)); @@ -1835,7 +2322,7 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { // r0: copy start address // r1: function // r2: expected number of arguments - // r3: code entry to call + // r3: new target (passed through to callee) // r4: copy end address Label copy; @@ -1868,24 +2355,25 @@ 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); // Calculate copy start address into r0 and copy end address is fp. // r0: actual number of arguments as a smi // r1: function // r2: expected number of arguments - // r3: code entry to call + // r3: new target (passed through to callee) __ add(r0, fp, Operand::PointerOffsetFromSmiKey(r0)); // Copy the arguments (including the receiver) to the new stack frame. // r0: copy start address // r1: function // r2: expected number of arguments - // r3: code entry to call + // r3: new target (passed through to callee) Label copy; __ bind(©); // Adjust load for return address and receiver. @@ -1898,7 +2386,7 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { // Fill the remaining expected arguments with undefined. // r1: function // r2: expected number of arguments - // r3: code entry to call + // r3: new target (passed through to callee) __ LoadRoot(ip, Heap::kUndefinedValueRootIndex); __ sub(r4, fp, Operand(r2, LSL, kPointerSizeLog2)); // Adjust for frame. @@ -1917,7 +2405,9 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { __ mov(r0, r2); // r0 : expected number of arguments // r1 : function (passed through to callee) - __ Call(r3); + // r3 : new target (passed through to callee) + __ ldr(r4, FieldMemOperand(r1, JSFunction::kCodeEntryOffset)); + __ Call(r4); // Store offset of return address for deoptimizer. masm->isolate()->heap()->SetArgumentsAdaptorDeoptPCOffset(masm->pc_offset()); @@ -1931,13 +2421,13 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { // Dont adapt arguments. // ------------------------------------------- __ bind(&dont_adapt_arguments); - __ Jump(r3); + __ ldr(r4, FieldMemOperand(r1, JSFunction::kCodeEntryOffset)); + __ Jump(r4); __ bind(&stack_overflow); { FrameScope frame(masm, StackFrame::MANUAL); - EnterArgumentsAdaptorFrame(masm); - __ CallRuntime(Runtime::kThrowStackOverflow, 0); + __ CallRuntime(Runtime::kThrowStackOverflow); __ bkpt(0); } } |