diff options
Diffstat (limited to 'deps/v8/src/arm/builtins-arm.cc')
-rw-r--r-- | deps/v8/src/arm/builtins-arm.cc | 874 |
1 files changed, 434 insertions, 440 deletions
diff --git a/deps/v8/src/arm/builtins-arm.cc b/deps/v8/src/arm/builtins-arm.cc index cf91753e1a..ea2c92e640 100644 --- a/deps/v8/src/arm/builtins-arm.cc +++ b/deps/v8/src/arm/builtins-arm.cc @@ -24,12 +24,19 @@ void Builtins::Generate_Adaptor(MacroAssembler* masm, // -- r0 : number of arguments excluding receiver // -- r1 : called function (only guaranteed when // extra_args requires it) - // -- cp : context // -- sp[0] : last argument // -- ... // -- sp[4 * (argc - 1)] : first argument (argc == r0) // -- sp[4 * argc] : receiver // ----------------------------------- + __ AssertFunction(r1); + + // Make sure we operate in the context of the called function (for example + // 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; @@ -136,7 +143,8 @@ void Builtins::Generate_ArrayCode(MacroAssembler* masm) { } -void Builtins::Generate_StringConstructCode(MacroAssembler* masm) { +// static +void Builtins::Generate_StringConstructor(MacroAssembler* masm) { // ----------- S t a t e ------------- // -- r0 : number of arguments // -- r1 : constructor function @@ -144,121 +152,128 @@ void Builtins::Generate_StringConstructCode(MacroAssembler* masm) { // -- sp[(argc - n - 1) * 4] : arg[n] (zero based) // -- sp[argc * 4] : receiver // ----------------------------------- - Counters* counters = masm->isolate()->counters(); - __ IncrementCounter(counters->string_ctor_calls(), 1, r2, r3); - Register function = r1; - if (FLAG_debug_code) { - __ LoadGlobalFunction(Context::STRING_FUNCTION_INDEX, r2); - __ cmp(function, Operand(r2)); - __ Assert(eq, kUnexpectedStringFunction); + // 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); } - // Load the first arguments in r0 and get rid of the rest. - Label no_arguments; - __ cmp(r0, Operand::Zero()); - __ b(eq, &no_arguments); - // First args = sp[(argc - 1) * 4]. - __ sub(r0, r0, Operand(1)); - __ ldr(r0, MemOperand(sp, r0, LSL, kPointerSizeLog2, PreIndex)); - // sp now point to args[0], drop args[0] + receiver. - __ Drop(2); - - Register argument = r2; - Label not_cached, argument_is_string; - __ LookupNumberStringCache(r0, // Input. - argument, // Result. - r3, // Scratch. - r4, // Scratch. - r5, // Scratch. - ¬_cached); - __ IncrementCounter(counters->string_ctor_cached_number(), 1, r3, r4); - __ bind(&argument_is_string); + // 2a. At least one argument, return r0 if it's a string, otherwise + // dispatch to appropriate conversion. + Label to_string, symbol_descriptive_string; + { + __ JumpIfSmi(r0, &to_string); + STATIC_ASSERT(FIRST_NONSTRING_TYPE == SYMBOL_TYPE); + __ CompareObjectType(r0, r1, r1, FIRST_NONSTRING_TYPE); + __ b(hi, &to_string); + __ b(eq, &symbol_descriptive_string); + __ Ret(); + } - // ----------- S t a t e ------------- - // -- r2 : argument converted to string - // -- r1 : constructor function - // -- lr : return address - // ----------------------------------- + // 2b. No arguments, return the empty string (and pop the receiver). + __ bind(&no_arguments); + { + __ LoadRoot(r0, Heap::kempty_stringRootIndex); + __ Ret(1); + } - Label gc_required; - __ Allocate(JSValue::kSize, - r0, // Result. - r3, // Scratch. - r4, // Scratch. - &gc_required, - TAG_OBJECT); - - // Initialising the String Object. - Register map = r3; - __ LoadGlobalFunctionInitialMap(function, map, r4); - if (FLAG_debug_code) { - __ ldrb(r4, FieldMemOperand(map, Map::kInstanceSizeOffset)); - __ cmp(r4, Operand(JSValue::kSize >> kPointerSizeLog2)); - __ Assert(eq, kUnexpectedStringWrapperInstanceSize); - __ ldrb(r4, FieldMemOperand(map, Map::kUnusedPropertyFieldsOffset)); - __ cmp(r4, Operand::Zero()); - __ Assert(eq, kUnexpectedUnusedPropertiesOfStringWrapper); + // 3a. Convert r0 to a string. + __ bind(&to_string); + { + ToStringStub stub(masm->isolate()); + __ TailCallStub(&stub); } - __ str(map, FieldMemOperand(r0, HeapObject::kMapOffset)); - __ LoadRoot(r3, Heap::kEmptyFixedArrayRootIndex); - __ str(r3, FieldMemOperand(r0, JSObject::kPropertiesOffset)); - __ str(r3, FieldMemOperand(r0, JSObject::kElementsOffset)); + // 3b. Convert symbol in r0 to a string. + __ bind(&symbol_descriptive_string); + { + __ Push(r0); + __ TailCallRuntime(Runtime::kSymbolDescriptiveString, 1, 1); + } +} - __ str(argument, FieldMemOperand(r0, JSValue::kValueOffset)); - // Ensure the object is fully initialized. - STATIC_ASSERT(JSValue::kSize == 4 * kPointerSize); +// static +void Builtins::Generate_StringConstructor_ConstructStub(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 + // ----------------------------------- - __ Ret(); + // 1. Load the first argument into r0 and get rid of the rest (including the + // receiver). + { + Label no_arguments, done; + __ sub(r0, r0, Operand(1), SetCC); + __ b(lo, &no_arguments); + __ ldr(r0, MemOperand(sp, r0, LSL, kPointerSizeLog2, PreIndex)); + __ Drop(2); + __ b(&done); + __ bind(&no_arguments); + __ LoadRoot(r0, Heap::kempty_stringRootIndex); + __ Drop(1); + __ bind(&done); + } - // The argument was not found in the number to string cache. Check - // if it's a string already before calling the conversion builtin. - Label convert_argument; - __ bind(¬_cached); - __ JumpIfSmi(r0, &convert_argument); - - // Is it a String? - __ ldr(r2, FieldMemOperand(r0, HeapObject::kMapOffset)); - __ ldrb(r3, FieldMemOperand(r2, Map::kInstanceTypeOffset)); - STATIC_ASSERT(kNotStringTag != 0); - __ tst(r3, Operand(kIsNotStringMask)); - __ b(ne, &convert_argument); - __ mov(argument, r0); - __ IncrementCounter(counters->string_ctor_conversions(), 1, r3, r4); - __ b(&argument_is_string); - - // Invoke the conversion builtin and put the result into r2. - __ bind(&convert_argument); - __ push(function); // Preserve the function. - __ IncrementCounter(counters->string_ctor_conversions(), 1, r3, r4); + // 2. Make sure r0 is a string. { - FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL); - __ push(r0); - __ InvokeBuiltin(Builtins::TO_STRING, CALL_FUNCTION); + Label convert, done_convert; + __ JumpIfSmi(r0, &convert); + __ CompareObjectType(r0, r2, r2, FIRST_NONSTRING_TYPE); + __ b(lo, &done_convert); + __ bind(&convert); + { + FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL); + ToStringStub stub(masm->isolate()); + __ Push(r1); + __ CallStub(&stub); + __ Pop(r1); + } + __ bind(&done_convert); } - __ pop(function); - __ mov(argument, r0); - __ b(&argument_is_string); - // Load the empty string into r2, remove the receiver from the - // stack, and jump back to the case where the argument is a string. - __ bind(&no_arguments); - __ LoadRoot(argument, Heap::kempty_stringRootIndex); - __ Drop(1); - __ b(&argument_is_string); - - // At this point the argument is already a string. Call runtime to - // create a string wrapper. - __ bind(&gc_required); - __ IncrementCounter(counters->string_ctor_gc_required(), 1, r3, r4); + // 3. Allocate a JSValue wrapper for the string. { - FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL); - __ push(argument); - __ CallRuntime(Runtime::kNewStringWrapper, 1); + // ----------- S t a t e ------------- + // -- r0 : the first argument + // -- r1 : constructor function + // -- lr : return address + // ----------------------------------- + + Label allocate, done_allocate; + __ Move(r2, r0); + __ 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); } - __ Ret(); } @@ -310,8 +325,7 @@ void Builtins::Generate_InOptimizationQueue(MacroAssembler* masm) { static void Generate_JSConstructStubHelper(MacroAssembler* masm, - bool is_api_function, - bool create_memento) { + bool is_api_function) { // ----------- S t a t e ------------- // -- r0 : number of arguments // -- r1 : constructor function @@ -321,9 +335,6 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm, // -- sp[...]: constructor arguments // ----------------------------------- - // Should never create mementos for api functions. - DCHECK(!is_api_function || !create_memento); - Isolate* isolate = masm->isolate(); // Enter a construct frame. @@ -398,9 +409,6 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm, // r2: initial map Label rt_call_reload_new_target; __ ldrb(r3, FieldMemOperand(r2, Map::kInstanceSizeOffset)); - if (create_memento) { - __ add(r3, r3, Operand(AllocationMemento::kSize / kPointerSize)); - } __ Allocate(r3, r4, r5, r6, &rt_call_reload_new_target, SIZE_IN_WORDS); @@ -408,7 +416,7 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm, // initial map and properties and elements are set to empty fixed array. // r1: constructor function // r2: initial map - // r3: object size (including memento if create_memento) + // r3: object size // r4: JSObject (not tagged) __ LoadRoot(r6, Heap::kEmptyFixedArrayRootIndex); __ mov(r5, r4); @@ -422,7 +430,7 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm, // Fill all the in-object properties with the appropriate filler. // r1: constructor function // r2: initial map - // r3: object size (in words, including memento if create_memento) + // r3: object size // r4: JSObject (not tagged) // r5: First in-object property of JSObject (not tagged) DCHECK_EQ(3 * kPointerSize, JSObject::kHeaderSize); @@ -461,25 +469,8 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm, __ bind(&no_inobject_slack_tracking); } - if (create_memento) { - __ sub(ip, r3, Operand(AllocationMemento::kSize / kPointerSize)); - __ add(r0, r4, Operand(ip, LSL, kPointerSizeLog2)); // End of object. - __ InitializeFieldsWithFiller(r5, r0, r6); - - // Fill in memento fields. - // r5: points to the allocated but uninitialized memento. - __ LoadRoot(r6, Heap::kAllocationMementoMapRootIndex); - DCHECK_EQ(0 * kPointerSize, AllocationMemento::kMapOffset); - __ str(r6, MemOperand(r5, kPointerSize, PostIndex)); - // Load the AllocationSite - __ ldr(r6, MemOperand(sp, 3 * kPointerSize)); - __ AssertUndefinedOrAllocationSite(r6, r0); - DCHECK_EQ(1 * kPointerSize, AllocationMemento::kAllocationSiteOffset); - __ str(r6, MemOperand(r5, kPointerSize, PostIndex)); - } else { - __ add(r0, r4, Operand(r3, LSL, kPointerSizeLog2)); // End of object. - __ InitializeFieldsWithFiller(r5, r0, r6); - } + __ add(r0, r4, Operand(r3, LSL, kPointerSizeLog2)); // End of object. + __ InitializeFieldsWithFiller(r5, r0, r6); // 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. @@ -498,47 +489,16 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm, // r1: constructor function // r3: original constructor __ bind(&rt_call); - if (create_memento) { - // Get the cell or allocation site. - __ ldr(r2, MemOperand(sp, 3 * kPointerSize)); - __ push(r2); // argument 1: allocation site - } __ push(r1); // argument 2/1: constructor function __ push(r3); // argument 3/2: original constructor - if (create_memento) { - __ CallRuntime(Runtime::kNewObjectWithAllocationSite, 3); - } else { - __ CallRuntime(Runtime::kNewObject, 2); - } + __ CallRuntime(Runtime::kNewObject, 2); __ mov(r4, r0); - // Runtime_NewObjectWithAllocationSite increments allocation count. - // Skip the increment. - Label count_incremented; - if (create_memento) { - __ jmp(&count_incremented); - } - // Receiver for constructor call allocated. // r4: JSObject __ bind(&allocated); - if (create_memento) { - __ ldr(r2, MemOperand(sp, 3 * kPointerSize)); - __ LoadRoot(r5, Heap::kUndefinedValueRootIndex); - __ cmp(r2, r5); - __ b(eq, &count_incremented); - // r2 is an AllocationSite. We are creating a memento from it, so we - // need to increment the memento create count. - __ ldr(r3, FieldMemOperand(r2, - AllocationSite::kPretenureCreateCountOffset)); - __ add(r3, r3, Operand(Smi::FromInt(1))); - __ str(r3, FieldMemOperand(r2, - AllocationSite::kPretenureCreateCountOffset)); - __ bind(&count_incremented); - } - // Restore the parameters. __ pop(r3); __ pop(r1); @@ -642,12 +602,12 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm, void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) { - Generate_JSConstructStubHelper(masm, false, FLAG_pretenuring_call_new); + Generate_JSConstructStubHelper(masm, false); } void Builtins::Generate_JSConstructStubApi(MacroAssembler* masm) { - Generate_JSConstructStubHelper(masm, true, false); + Generate_JSConstructStubHelper(masm, true); } @@ -742,8 +702,7 @@ enum IsTagged { kArgcIsSmiTagged, kArgcIsUntaggedInt }; // Clobbers r2; preserves all other registers. -static void Generate_CheckStackOverflow(MacroAssembler* masm, - const int calleeOffset, Register argc, +static void Generate_CheckStackOverflow(MacroAssembler* masm, Register argc, IsTagged argc_is_tagged) { // Check the stack for overflow. We are not trying to catch // interruptions (e.g. debug break and preemption) here, so the "real stack @@ -763,12 +722,7 @@ static void Generate_CheckStackOverflow(MacroAssembler* masm, __ b(gt, &okay); // Signed comparison. // Out of stack space. - __ ldr(r1, MemOperand(fp, calleeOffset)); - if (argc_is_tagged == kArgcIsUntaggedInt) { - __ SmiTag(argc); - } - __ Push(r1, argc); - __ InvokeBuiltin(Builtins::STACK_OVERFLOW, CALL_FUNCTION); + __ CallRuntime(Runtime::kThrowStackOverflow, 0); __ bind(&okay); } @@ -777,7 +731,7 @@ static void Generate_CheckStackOverflow(MacroAssembler* masm, static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm, bool is_construct) { // Called from Generate_JS_Entry - // r0: code entry + // r0: new.target // r1: function // r2: receiver // r3: argc @@ -792,22 +746,23 @@ static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm, { FrameScope scope(masm, StackFrame::INTERNAL); - // Set up the context from the function argument. - __ ldr(cp, FieldMemOperand(r1, JSFunction::kContextOffset)); + // Setup the context (we need to use the caller context from the isolate). + ExternalReference context_address(Isolate::kContextAddress, + masm->isolate()); + __ mov(cp, Operand(context_address)); + __ ldr(cp, MemOperand(cp)); __ InitializeRootRegister(); // Push the function and the receiver onto the stack. - __ push(r1); - __ push(r2); + __ Push(r1, r2); // Check if we have enough stack space to push all arguments. - // The function is the first thing that was pushed above after entering - // the internal frame. - const int kFunctionOffset = - InternalFrameConstants::kCodeOffset - kPointerSize; // Clobbers r2. - Generate_CheckStackOverflow(masm, kFunctionOffset, r3, kArgcIsUntaggedInt); + Generate_CheckStackOverflow(masm, r3, kArgcIsUntaggedInt); + + // Remember new.target. + __ mov(r5, r0); // Copy arguments to the stack in a loop. // r1: function @@ -825,6 +780,10 @@ static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm, __ cmp(r4, r2); __ b(ne, &loop); + // Setup new.target and argc. + __ mov(r0, Operand(r3)); + __ mov(r3, Operand(r5)); + // Initialize all JavaScript callee-saved registers, since they will be seen // by the garbage collector as part of handlers. __ LoadRoot(r4, Heap::kUndefinedValueRootIndex); @@ -837,17 +796,12 @@ static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm, __ mov(r9, Operand(r4)); } - // Invoke the code and pass argc as r0. - __ mov(r0, Operand(r3)); - if (is_construct) { - // No type feedback cell is available - __ LoadRoot(r2, Heap::kUndefinedValueRootIndex); - CallConstructStub stub(masm->isolate(), NO_CALL_CONSTRUCTOR_FLAGS); - __ CallStub(&stub); - } else { - ParameterCount actual(r0); - __ InvokeFunction(r1, actual, CALL_FUNCTION, NullCallWrapper()); - } + // Invoke the code. + Handle<Code> builtin = is_construct + ? masm->isolate()->builtins()->Construct() + : masm->isolate()->builtins()->Call(); + __ Call(builtin, RelocInfo::CODE_TARGET); + // Exit the JS frame and remove the parameters (except function), and // return. // Respect ABI stack constraint. @@ -920,7 +874,7 @@ void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) { __ LoadRoot(r2, Heap::kRealStackLimitRootIndex); __ cmp(r9, Operand(r2)); __ b(hs, &ok); - __ InvokeBuiltin(Builtins::STACK_OVERFLOW, CALL_FUNCTION); + __ CallRuntime(Runtime::kThrowStackOverflow, 0); __ bind(&ok); // If ok, push undefined as the initial value for all register file entries. @@ -1003,8 +957,11 @@ void Builtins::Generate_InterpreterExitTrampoline(MacroAssembler* masm) { // Leave the frame (also dropping the register file). __ LeaveFrame(StackFrame::JAVA_SCRIPT); - // Drop receiver + arguments. - __ Drop(1); // TODO(rmcilroy): Get number of arguments from BytecodeArray. + + // Drop receiver + arguments and return. + __ ldr(ip, FieldMemOperand(kInterpreterBytecodeArrayRegister, + BytecodeArray::kParameterSizeOffset)); + __ add(sp, sp, ip, LeaveCC); __ Jump(lr); } @@ -1253,127 +1210,30 @@ void Builtins::Generate_OsrAfterStackCheck(MacroAssembler* masm) { } +// static void Builtins::Generate_FunctionCall(MacroAssembler* masm) { // 1. Make sure we have at least one argument. // r0: actual number of arguments - { Label done; + { + Label done; __ cmp(r0, Operand::Zero()); __ b(ne, &done); - __ LoadRoot(r2, Heap::kUndefinedValueRootIndex); - __ push(r2); + __ PushRoot(Heap::kUndefinedValueRootIndex); __ add(r0, r0, Operand(1)); __ bind(&done); } - // 2. Get the function to call (passed as receiver) from the stack, check - // if it is a function. + // 2. Get the callable to call (passed as receiver) from the stack. // r0: actual number of arguments - Label slow, non_function; __ ldr(r1, MemOperand(sp, r0, LSL, kPointerSizeLog2)); - __ JumpIfSmi(r1, &non_function); - __ CompareObjectType(r1, r2, r2, JS_FUNCTION_TYPE); - __ b(ne, &slow); - - // 3a. Patch the first argument if necessary when calling a function. - // r0: actual number of arguments - // r1: function - Label shift_arguments; - __ mov(r4, Operand::Zero()); // indicate regular JS_FUNCTION - { Label convert_to_object, use_global_proxy, patch_receiver; - // Change context eagerly in case we need the global receiver. - __ ldr(cp, FieldMemOperand(r1, JSFunction::kContextOffset)); - - // Do not transform the receiver for strict mode functions. - __ ldr(r2, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset)); - __ ldr(r3, FieldMemOperand(r2, SharedFunctionInfo::kCompilerHintsOffset)); - __ tst(r3, Operand(1 << (SharedFunctionInfo::kStrictModeFunction + - kSmiTagSize))); - __ b(ne, &shift_arguments); - - // Do not transform the receiver for native (Compilerhints already in r3). - __ tst(r3, Operand(1 << (SharedFunctionInfo::kNative + kSmiTagSize))); - __ b(ne, &shift_arguments); - // Compute the receiver in sloppy mode. - __ add(r2, sp, Operand(r0, LSL, kPointerSizeLog2)); - __ ldr(r2, MemOperand(r2, -kPointerSize)); - // r0: actual number of arguments - // r1: function - // r2: first argument - __ JumpIfSmi(r2, &convert_to_object); - - __ LoadRoot(r3, Heap::kUndefinedValueRootIndex); - __ cmp(r2, r3); - __ b(eq, &use_global_proxy); - __ LoadRoot(r3, Heap::kNullValueRootIndex); - __ cmp(r2, r3); - __ b(eq, &use_global_proxy); - - STATIC_ASSERT(LAST_SPEC_OBJECT_TYPE == LAST_TYPE); - __ CompareObjectType(r2, r3, r3, FIRST_SPEC_OBJECT_TYPE); - __ b(ge, &shift_arguments); - - __ bind(&convert_to_object); - - { - // Enter an internal frame in order to preserve argument count. - FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL); - __ SmiTag(r0); - __ push(r0); - - __ mov(r0, r2); - ToObjectStub stub(masm->isolate()); - __ CallStub(&stub); - __ mov(r2, r0); - - __ pop(r0); - __ SmiUntag(r0); - - // Exit the internal frame. - } - - // Restore the function to r1, and the flag to r4. - __ ldr(r1, MemOperand(sp, r0, LSL, kPointerSizeLog2)); - __ mov(r4, Operand::Zero()); - __ jmp(&patch_receiver); - - __ bind(&use_global_proxy); - __ ldr(r2, ContextOperand(cp, Context::GLOBAL_OBJECT_INDEX)); - __ ldr(r2, FieldMemOperand(r2, GlobalObject::kGlobalProxyOffset)); - - __ bind(&patch_receiver); - __ add(r3, sp, Operand(r0, LSL, kPointerSizeLog2)); - __ str(r2, MemOperand(r3, -kPointerSize)); - - __ jmp(&shift_arguments); - } - - // 3b. Check for function proxy. - __ bind(&slow); - __ mov(r4, Operand(1, RelocInfo::NONE32)); // indicate function proxy - __ cmp(r2, Operand(JS_FUNCTION_PROXY_TYPE)); - __ b(eq, &shift_arguments); - __ bind(&non_function); - __ mov(r4, Operand(2, RelocInfo::NONE32)); // indicate non-function - - // 3c. Patch the first argument when calling a non-function. The - // CALL_NON_FUNCTION builtin expects the non-function callee as - // receiver, so overwrite the first argument which will ultimately - // become the receiver. - // r0: actual number of arguments - // r1: function - // r4: call type (0: JS function, 1: function proxy, 2: non-function) - __ add(r2, sp, Operand(r0, LSL, kPointerSizeLog2)); - __ str(r1, MemOperand(r2, -kPointerSize)); - - // 4. Shift arguments and return address one slot down on the stack + // 3. Shift arguments and return address one slot down on the stack // (overwriting the original receiver). Adjust argument count to make // the original first argument the new receiver. // r0: actual number of arguments - // r1: function - // r4: call type (0: JS function, 1: function proxy, 2: non-function) - __ bind(&shift_arguments); - { Label loop; + // r1: callable + { + Label loop; // Calculate the copy start address (destination). Copy end address is sp. __ add(r2, sp, Operand(r0, LSL, kPointerSizeLog2)); @@ -1389,49 +1249,8 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { __ pop(); } - // 5a. Call non-function via tail call to CALL_NON_FUNCTION builtin, - // or a function proxy via CALL_FUNCTION_PROXY. - // r0: actual number of arguments - // r1: function - // r4: call type (0: JS function, 1: function proxy, 2: non-function) - { Label function, non_proxy; - __ tst(r4, r4); - __ b(eq, &function); - // Expected number of arguments is 0 for CALL_NON_FUNCTION. - __ mov(r2, Operand::Zero()); - __ cmp(r4, Operand(1)); - __ b(ne, &non_proxy); - - __ push(r1); // re-add proxy object as additional argument - __ add(r0, r0, Operand(1)); - __ GetBuiltinFunction(r1, Builtins::CALL_FUNCTION_PROXY); - __ Jump(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(), - RelocInfo::CODE_TARGET); - - __ bind(&non_proxy); - __ GetBuiltinFunction(r1, Builtins::CALL_NON_FUNCTION); - __ Jump(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(), - RelocInfo::CODE_TARGET); - __ bind(&function); - } - - // 5b. Get the code to call from the function and check that the number of - // expected arguments matches what we're providing. If so, jump - // (tail-call) to the code in register edx without checking arguments. - // r0: actual number of arguments - // r1: function - __ ldr(r3, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset)); - __ ldr(r2, - FieldMemOperand(r3, SharedFunctionInfo::kFormalParameterCountOffset)); - __ SmiUntag(r2); - __ cmp(r2, r0); // Check formal and actual parameter counts. - __ Jump(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(), - RelocInfo::CODE_TARGET, - ne); - - __ ldr(r3, FieldMemOperand(r1, JSFunction::kCodeEntryOffset)); - ParameterCount expected(0); - __ InvokeCode(r3, expected, expected, JUMP_FUNCTION, NullCallWrapper()); + // 4. Call the callable. + __ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET); } @@ -1500,105 +1319,32 @@ static void Generate_ApplyHelper(MacroAssembler* masm, bool targetIsArgument) { __ Push(r1); __ ldr(r0, MemOperand(fp, kFunctionOffset)); // get the function - __ push(r0); - __ ldr(r0, MemOperand(fp, kArgumentsOffset)); // get the args array - __ push(r0); + __ ldr(r1, MemOperand(fp, kArgumentsOffset)); // get the args array + __ Push(r0, r1); if (targetIsArgument) { - __ InvokeBuiltin(Builtins::REFLECT_APPLY_PREPARE, CALL_FUNCTION); + __ InvokeBuiltin(Context::REFLECT_APPLY_PREPARE_BUILTIN_INDEX, + CALL_FUNCTION); } else { - __ InvokeBuiltin(Builtins::APPLY_PREPARE, CALL_FUNCTION); + __ InvokeBuiltin(Context::APPLY_PREPARE_BUILTIN_INDEX, CALL_FUNCTION); } - Generate_CheckStackOverflow(masm, kFunctionOffset, r0, kArgcIsSmiTagged); + 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); - - // Get the receiver. - __ ldr(r0, MemOperand(fp, kReceiverOffset)); - - // Check that the function is a JS function (otherwise it must be a proxy). - Label push_receiver; - __ ldr(r1, MemOperand(fp, kFunctionOffset)); - __ CompareObjectType(r1, r2, r2, JS_FUNCTION_TYPE); - __ b(ne, &push_receiver); - - // Change context eagerly to get the right global object if necessary. - __ ldr(cp, FieldMemOperand(r1, JSFunction::kContextOffset)); - // Load the shared function info while the function is still in r1. - __ ldr(r2, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset)); - - // Compute the receiver. - // Do not transform the receiver for strict mode functions. - Label call_to_object, use_global_proxy; - __ ldr(r2, FieldMemOperand(r2, SharedFunctionInfo::kCompilerHintsOffset)); - __ tst(r2, Operand(1 << (SharedFunctionInfo::kStrictModeFunction + - kSmiTagSize))); - __ b(ne, &push_receiver); - - // Do not transform the receiver for strict mode functions. - __ tst(r2, Operand(1 << (SharedFunctionInfo::kNative + kSmiTagSize))); - __ b(ne, &push_receiver); - - // Compute the receiver in sloppy mode. - __ JumpIfSmi(r0, &call_to_object); - __ LoadRoot(r1, Heap::kNullValueRootIndex); - __ cmp(r0, r1); - __ b(eq, &use_global_proxy); - __ LoadRoot(r1, Heap::kUndefinedValueRootIndex); - __ cmp(r0, r1); - __ b(eq, &use_global_proxy); - - // Check if the receiver is already a JavaScript object. - // r0: receiver - STATIC_ASSERT(LAST_SPEC_OBJECT_TYPE == LAST_TYPE); - __ CompareObjectType(r0, r1, r1, FIRST_SPEC_OBJECT_TYPE); - __ b(ge, &push_receiver); - - // Convert the receiver to a regular object. - // r0: receiver - __ bind(&call_to_object); - ToObjectStub stub(masm->isolate()); - __ CallStub(&stub); - __ b(&push_receiver); - - __ bind(&use_global_proxy); - __ ldr(r0, ContextOperand(cp, Context::GLOBAL_OBJECT_INDEX)); - __ ldr(r0, FieldMemOperand(r0, GlobalObject::kGlobalProxyOffset)); - - // Push the receiver. - // r0: receiver - __ bind(&push_receiver); - __ push(r0); + __ mov(r1, Operand::Zero()); + __ ldr(r2, MemOperand(fp, kReceiverOffset)); + __ Push(r0, r1, r2); // limit, initial index and receiver. // Copy all arguments from the array to the stack. Generate_PushAppliedArguments(masm, kVectorOffset, kArgumentsOffset, kIndexOffset, kLimitOffset); - // Call the function. - Label call_proxy; - ParameterCount actual(r0); + // Call the callable. + // TODO(bmeurer): This should be a tail call according to ES6. __ ldr(r1, MemOperand(fp, kFunctionOffset)); - __ CompareObjectType(r1, r2, r2, JS_FUNCTION_TYPE); - __ b(ne, &call_proxy); - __ InvokeFunction(r1, actual, CALL_FUNCTION, NullCallWrapper()); - - frame_scope.GenerateLeaveFrame(); - __ add(sp, sp, Operand(kStackSize * kPointerSize)); - __ Jump(lr); - - // Call the function proxy. - __ bind(&call_proxy); - __ push(r1); // add function proxy as last argument - __ add(r0, r0, Operand(1)); - __ mov(r2, Operand::Zero()); - __ GetBuiltinFunction(r1, Builtins::CALL_FUNCTION_PROXY); - __ Call(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(), - RelocInfo::CODE_TARGET); + __ Call(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET); // Tear down the internal frame and remove function, receiver and args. } @@ -1640,9 +1386,10 @@ static void Generate_ConstructHelper(MacroAssembler* masm) { __ push(r0); __ ldr(r0, MemOperand(fp, kNewTargetOffset)); // get the new.target __ push(r0); - __ InvokeBuiltin(Builtins::REFLECT_CONSTRUCT_PREPARE, CALL_FUNCTION); + __ InvokeBuiltin(Context::REFLECT_CONSTRUCT_PREPARE_BUILTIN_INDEX, + CALL_FUNCTION); - Generate_CheckStackOverflow(masm, kFunctionOffset, r0, kArgcIsSmiTagged); + Generate_CheckStackOverflow(masm, r0, kArgcIsSmiTagged); // Push current limit and index. const int kIndexOffset = kVectorOffset - (2 * kPointerSize); @@ -1735,6 +1482,249 @@ static void LeaveArgumentsAdaptorFrame(MacroAssembler* masm) { } +// static +void Builtins::Generate_CallFunction(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 JSFunction) + // ----------------------------------- + + Label convert, convert_global_proxy, convert_to_object, done_convert; + __ AssertFunction(r1); + // TODO(bmeurer): Throw a TypeError if function's [[FunctionKind]] internal + // slot is "classConstructor". + // Enter the context of the function; ToObject has to run in the function + // context, and we also need to take the global proxy from the function + // context in case of conversion. + // See ES6 section 9.2.1 [[Call]] ( thisArgument, argumentsList) + STATIC_ASSERT(SharedFunctionInfo::kNativeByteOffset == + SharedFunctionInfo::kStrictModeByteOffset); + __ ldr(cp, FieldMemOperand(r1, JSFunction::kContextOffset)); + __ ldr(r2, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset)); + // We need to convert the receiver for non-native sloppy mode functions. + __ ldrb(r3, FieldMemOperand(r2, SharedFunctionInfo::kNativeByteOffset)); + __ tst(r3, Operand((1 << SharedFunctionInfo::kNativeBitWithinByte) | + (1 << SharedFunctionInfo::kStrictModeBitWithinByte))); + __ b(ne, &done_convert); + { + __ ldr(r3, MemOperand(sp, r0, LSL, kPointerSizeLog2)); + + // ----------- S t a t e ------------- + // -- r0 : the number of arguments (not including the receiver) + // -- r1 : the function to call (checked to be a JSFunction) + // -- r2 : the shared function info. + // -- r3 : the receiver + // -- cp : the function context. + // ----------------------------------- + + Label convert_receiver; + __ JumpIfSmi(r3, &convert_to_object); + STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE); + __ CompareObjectType(r3, r4, r4, FIRST_JS_RECEIVER_TYPE); + __ b(hs, &done_convert); + __ JumpIfRoot(r3, Heap::kUndefinedValueRootIndex, &convert_global_proxy); + __ JumpIfNotRoot(r3, Heap::kNullValueRootIndex, &convert_to_object); + __ bind(&convert_global_proxy); + { + // Patch receiver to global proxy. + __ LoadGlobalProxy(r3); + } + __ b(&convert_receiver); + __ bind(&convert_to_object); + { + // Convert receiver using ToObject. + // TODO(bmeurer): Inline the allocation here to avoid building the frame + // in the fast case? (fall back to AllocateInNewSpace?) + FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL); + __ SmiTag(r0); + __ Push(r0, r1); + __ mov(r0, r3); + ToObjectStub stub(masm->isolate()); + __ CallStub(&stub); + __ mov(r3, r0); + __ Pop(r0, r1); + __ SmiUntag(r0); + } + __ ldr(r2, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset)); + __ bind(&convert_receiver); + __ str(r3, MemOperand(sp, r0, LSL, kPointerSizeLog2)); + } + __ bind(&done_convert); + + // ----------- S t a t e ------------- + // -- r0 : the number of arguments (not including the receiver) + // -- r1 : the function to call (checked to be a JSFunction) + // -- r2 : the shared function info. + // -- cp : the function context. + // ----------------------------------- + + __ 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()); +} + + +// static +void Builtins::Generate_Call(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- r0 : the number of arguments (not including the receiver) + // -- r1 : the target to call (can be any Object). + // ----------------------------------- + + Label non_callable, non_function, non_smi; + __ JumpIfSmi(r1, &non_callable); + __ bind(&non_smi); + __ CompareObjectType(r1, r4, r5, JS_FUNCTION_TYPE); + __ Jump(masm->isolate()->builtins()->CallFunction(), RelocInfo::CODE_TARGET, + eq); + __ cmp(r5, Operand(JS_FUNCTION_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); + + // 2. Call to something else, which might have a [[Call]] internal method (if + // not we raise an exception). + __ bind(&non_function); + // Check if target has a [[Call]] internal method. + __ ldrb(r4, FieldMemOperand(r4, Map::kBitFieldOffset)); + __ tst(r4, Operand(1 << Map::kIsCallable)); + __ b(eq, &non_callable); + // 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); + __ Jump(masm->isolate()->builtins()->CallFunction(), RelocInfo::CODE_TARGET); + + // 3. Call to something that is not callable. + __ bind(&non_callable); + { + FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL); + __ Push(r1); + __ CallRuntime(Runtime::kThrowCalledNonCallable, 1); + } +} + + +// static +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) + // ----------------------------------- + __ AssertFunction(r1); + __ AssertFunction(r3); + + // Calling convention for function specific ConstructStubs require + // r2 to contain either an AllocationSite or undefined. + __ LoadRoot(r2, Heap::kUndefinedValueRootIndex); + + // Tail call to the function-specific construct stub (still in the caller + // context at this point). + __ ldr(r4, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset)); + __ ldr(r4, FieldMemOperand(r4, SharedFunctionInfo::kConstructStubOffset)); + __ add(pc, r4, 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 + // 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); +} + + +// static +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 + // the JSFunction on which new was invoked initially) + // ----------------------------------- + + // Check if target has a [[Construct]] internal method. + Label non_constructor; + __ JumpIfSmi(r1, &non_constructor); + __ ldr(r4, FieldMemOperand(r1, HeapObject::kMapOffset)); + __ 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(), + RelocInfo::CODE_TARGET, eq); + __ cmp(r5, Operand(JS_FUNCTION_PROXY_TYPE)); + __ Jump(masm->isolate()->builtins()->ConstructProxy(), RelocInfo::CODE_TARGET, + eq); + + // Called Construct on an exotic Object with a [[Construct]] internal method. + { + // 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); + __ Jump(masm->isolate()->builtins()->CallFunction(), + RelocInfo::CODE_TARGET); + } + + // 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); + } +} + + +// static +void Builtins::Generate_PushArgsAndCall(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- r0 : the number of arguments (not including the receiver) + // -- r2 : the address of the first argument to be pushed. Subsequent + // arguments should be consecutive above this, in the same order as + // they are to be pushed onto the stack. + // -- r1 : the target to call (can be any Object). + + // Find the address of the last argument. + __ add(r3, r0, Operand(1)); // Add one for receiver. + __ mov(r3, Operand(r3, LSL, kPointerSizeLog2)); + __ sub(r3, r2, r3); + + // Push the arguments. + Label loop_header, loop_check; + __ b(al, &loop_check); + __ bind(&loop_header); + __ ldr(r4, MemOperand(r2, -kPointerSize, PostIndex)); + __ push(r4); + __ bind(&loop_check); + __ cmp(r2, r3); + __ b(gt, &loop_header); + + // Call the target. + __ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET); +} + + void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { // ----------- S t a t e ------------- // -- r0 : actual number of arguments @@ -1757,7 +1747,7 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { __ bind(&enough); EnterArgumentsAdaptorFrame(masm); - // Calculate copy start address into r0 and copy end address into r2. + // 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 @@ -1765,19 +1755,20 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { __ add(r0, fp, Operand::PointerOffsetFromSmiKey(r0)); // adjust for return address and receiver __ add(r0, r0, Operand(2 * kPointerSize)); - __ sub(r2, r0, Operand(r2, LSL, kPointerSizeLog2)); + __ sub(r4, r0, Operand(r2, LSL, kPointerSizeLog2)); // Copy the arguments (including the receiver) to the new stack frame. // r0: copy start address // r1: function - // r2: copy end address + // r2: expected number of arguments // r3: code entry to call + // r4: copy end address Label copy; __ bind(©); __ ldr(ip, MemOperand(r0, 0)); __ push(ip); - __ cmp(r0, r2); // Compare before moving to next argument. + __ cmp(r0, r4); // Compare before moving to next argument. __ sub(r0, r0, Operand(kPointerSize)); __ b(ne, ©); @@ -1835,20 +1826,23 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { // r2: expected number of arguments // r3: code entry to call __ LoadRoot(ip, Heap::kUndefinedValueRootIndex); - __ sub(r2, fp, Operand(r2, LSL, kPointerSizeLog2)); + __ sub(r4, fp, Operand(r2, LSL, kPointerSizeLog2)); // Adjust for frame. - __ sub(r2, r2, Operand(StandardFrameConstants::kFixedFrameSizeFromFp + + __ sub(r4, r4, Operand(StandardFrameConstants::kFixedFrameSizeFromFp + 2 * kPointerSize)); Label fill; __ bind(&fill); __ push(ip); - __ cmp(sp, r2); + __ cmp(sp, r4); __ b(ne, &fill); } // Call the entry point. __ bind(&invoke); + __ mov(r0, r2); + // r0 : expected number of arguments + // r1 : function (passed through to callee) __ Call(r3); // Store offset of return address for deoptimizer. @@ -1869,7 +1863,7 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { { FrameScope frame(masm, StackFrame::MANUAL); EnterArgumentsAdaptorFrame(masm); - __ InvokeBuiltin(Builtins::STACK_OVERFLOW, CALL_FUNCTION); + __ CallRuntime(Runtime::kThrowStackOverflow, 0); __ bkpt(0); } } |