// Copyright 2006-2008 Google Inc. All Rights Reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following // disclaimer in the documentation and/or other materials provided // with the distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "v8.h" #include "codegen-inl.h" #include "debug.h" #include "runtime.h" namespace v8 { namespace internal { #define __ masm-> void Builtins::Generate_Adaptor(MacroAssembler* masm, int argc, CFunctionId id) { // r0 contains the number of arguments excluding the receiver. // JumpToBuiltin expects r0 to contains the number of arguments // including the receiver. __ add(r0, r0, Operand(1)); __ JumpToBuiltin(ExternalReference(id)); } void Builtins::Generate_JSConstructCall(MacroAssembler* masm) { // r0: number of arguments __ EnterJSFrame(0); // Allocate the new receiver object. __ ldr(r0, MemOperand(pp, JavaScriptFrameConstants::kFunctionOffset)); __ push(r0); __ CallRuntime(Runtime::kNewObject, 1); __ push(r0); // save the receiver // Push the function and the allocated receiver from the stack. __ ldr(r1, MemOperand(pp, JavaScriptFrameConstants::kFunctionOffset)); __ push(r1); // function __ push(r0); // receiver // Restore the arguments length from the stack. __ ldr(r0, MemOperand(fp, JavaScriptFrameConstants::kArgsLengthOffset)); // Setup pointer to last argument - receiver is not counted. __ sub(r2, pp, Operand(r0, LSL, kPointerSizeLog2)); __ sub(r2, r2, Operand(kPointerSize)); // Copy arguments and receiver to the expression stack. Label loop, entry; __ mov(r1, Operand(r0)); __ b(&entry); __ bind(&loop); __ ldr(r3, MemOperand(r2, r1, LSL, kPointerSizeLog2)); __ push(r3); __ bind(&entry); __ sub(r1, r1, Operand(1), SetCC); __ b(ge, &loop); // Get the function to call from the stack. __ ldr(r1, MemOperand(pp, JavaScriptFrameConstants::kFunctionOffset)); // Call the function. Label return_site; __ RecordPosition(position); ParameterCount actual(r0); __ InvokeFunction(r1, actual, CALL_FUNCTION); __ bind(&return_site); // Restore context from the frame and discard the function. __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); __ pop(); // 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. __ tst(r0, Operand(kSmiTagMask)); __ b(eq, &use_receiver); // If the type of the result (stored in its map) is less than // JS_OBJECT type, it is not an object in the ECMA sense. __ ldr(r2, FieldMemOperand(r0, HeapObject::kMapOffset)); __ ldrb(r2, FieldMemOperand(r2, Map::kInstanceTypeOffset)); __ cmp(r2, Operand(JS_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); __ ExitJSFrame(RETURN); // Compute the offset from the beginning of the JSConstructCall // builtin code object to the return address after the call. ASSERT(return_site.is_bound()); construct_call_pc_offset_ = return_site.pos() + Code::kHeaderSize; } static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm, bool is_construct) { // Called from Generate_JS_Entry // r0: code entry // r1: function // r2: receiver // r3: argc // r4: argv // r5-r7, cp may be clobbered // Enter the JS frame // compute parameter pointer before making changes __ mov(ip, Operand(sp)); // ip == caller_sp == new pp __ mov(r5, Operand(0)); // spare slot to store caller code object during GC __ mov(r6, Operand(0)); // no context __ mov(r7, Operand(0)); // no incoming parameters __ mov(r8, Operand(0)); // caller_pp == NULL for trampoline frames ASSERT(cp.bit() == r8.bit()); // adjust the code otherwise // push in reverse order: // code (r5==0), context (r6==0), args_len (r7==0), caller_pp (r8==0), // caller_fp, sp_on_exit (caller_sp), caller_pc __ stm(db_w, sp, r5.bit() | r6.bit() | r7.bit() | r8.bit() | fp.bit() | ip.bit() | lr.bit()); // Setup new frame pointer. __ add(fp, sp, Operand(-StandardFrameConstants::kCodeOffset)); __ mov(pp, Operand(ip)); // setup new parameter pointer // Setup the context from the function argument. __ ldr(cp, FieldMemOperand(r1, JSFunction::kContextOffset)); // Push the function and the receiver onto the stack. __ push(r1); __ push(r2); // Copy arguments to the stack in a loop. // r1: function // r3: argc // r4: argv, i.e. points to first arg Label loop, entry; __ add(r2, r4, Operand(r3, LSL, kPointerSizeLog2)); // r2 points past last arg. __ b(&entry); __ bind(&loop); __ ldr(r0, MemOperand(r4, kPointerSize, PostIndex)); // read next parameter __ ldr(r0, MemOperand(r0)); // dereference handle __ push(r0); // push parameter __ bind(&entry); __ cmp(r4, Operand(r2)); __ b(ne, &loop); // Initialize all JavaScript callee-saved registers, since they will be seen // by the garbage collector as part of handlers. __ mov(r4, Operand(Factory::undefined_value())); __ mov(r5, Operand(r4)); __ mov(r6, Operand(r4)); __ mov(r7, Operand(r4)); if (kR9Available == 1) __ mov(r9, Operand(r4)); // Invoke the code and pass argc as r0. if (is_construct) { __ mov(r0, Operand(r3)); __ Call(Handle(Builtins::builtin(Builtins::JSConstructCall)), code_target); } else { ParameterCount actual(r3); __ InvokeFunction(r1, actual, CALL_FUNCTION); } // Exit the JS frame and remove the parameters (except function), and return. // Respect ABI stack constraint. __ add(sp, fp, Operand(StandardFrameConstants::kCallerFPOffset)); __ ldm(ia, sp, fp.bit() | sp.bit() | pc.bit()); // r0: result // pp: not restored, should not be used anymore } void Builtins::Generate_JSEntryTrampoline(MacroAssembler* masm) { Generate_JSEntryTrampolineHelper(masm, false); } void Builtins::Generate_JSConstructEntryTrampoline(MacroAssembler* masm) { Generate_JSEntryTrampolineHelper(masm, true); } void Builtins::Generate_FunctionApply(MacroAssembler* masm) { // TODO(1233523): Implement. Unused for now. __ stop("Builtins::Generate_FunctionApply"); } void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { // TODO(1233523): Implement. Unused for now. __ stop("Builtins::Generate_ArgumentsAdaptorTrampoline"); Label return_site; __ bind(&return_site); // Compute the offset from the beginning of the ArgumentsAdaptorTrampoline // builtin code object to the return address after the call. ASSERT(return_site.is_bound()); arguments_adaptor_call_pc_offset_ = return_site.pos() + Code::kHeaderSize; } static void Generate_DebugBreakCallHelper(MacroAssembler* masm, RegList pointer_regs) { // Save the content of all general purpose registers in memory. This copy in // memory is later pushed onto the JS expression stack for the fake JS frame // generated and also to the C frame generated on top of that. In the JS // frame ONLY the registers containing pointers will be pushed on the // expression stack. This causes the GC to update these pointers so that // they will have the correct value when returning from the debugger. __ SaveRegistersToMemory(kJSCallerSaved); // This is a direct call from a debug breakpoint. To build a fake JS frame // with no parameters push a function and a receiver, keep the current // return address in lr, and set r0 to zero. __ mov(ip, Operand(ExternalReference::the_hole_value_location())); __ ldr(r3, MemOperand(ip)); __ mov(r0, Operand(0)); // Null receiver and zero arguments. __ stm(db_w, sp, r0.bit() | r3.bit()); // push function and receiver // r0: number of arguments. // What follows is an inlined version of EnterJSFrame(0, 0). // It needs to be kept in sync if any calling conventions are changed. // Compute parameter pointer before making changes // ip = sp + kPointerSize*(args_len+1); // +1 for receiver, args_len == 0 __ add(ip, sp, Operand(kPointerSize)); __ mov(r3, Operand(0)); // args_len to be saved __ mov(r2, Operand(cp)); // context to be saved // push in reverse order: context (r2), args_len (r3), caller_pp, caller_fp, // sp_on_exit (ip == pp), return address __ stm(db_w, sp, r2.bit() | r3.bit() | pp.bit() | fp.bit() | ip.bit() | lr.bit()); // Setup new frame pointer. __ add(fp, sp, Operand(-StandardFrameConstants::kContextOffset)); __ mov(pp, Operand(ip)); // setup new parameter pointer // r0 is already set to 0 as spare slot to store caller code object during GC __ push(r0); // code pointer // Inlined EnterJSFrame ends here. // Store the registers containing object pointers on the expression stack to // make sure that these are correctly updated during GC. // Use sp as base to push. __ CopyRegistersFromMemoryToStack(sp, pointer_regs); #ifdef DEBUG __ RecordComment("// Calling from debug break to runtime - come in - over"); #endif // r0 is already 0, no arguments __ mov(r1, Operand(ExternalReference::debug_break())); CEntryDebugBreakStub ceb; __ CallStub(&ceb); // Restore the register values containing object pointers from the expression // stack in the reverse order as they where pushed. // Use sp as base to pop. __ CopyRegistersFromStackToMemory(sp, r3, pointer_regs); // What follows is an inlined version of ExitJSFrame(0). // It needs to be kept in sync if any calling conventions are changed. // NOTE: loading the return address to lr and discarding the (fake) function // is an addition to this inlined copy. __ mov(sp, Operand(fp)); // respect ABI stack constraint __ ldm(ia, sp, pp.bit() | fp.bit() | sp.bit() | lr.bit()); __ pop(); // discard fake function // Inlined ExitJSFrame ends here. // Finally restore all registers. __ RestoreRegistersFromMemory(kJSCallerSaved); // Now that the break point has been handled, resume normal execution by // jumping to the target address intended by the caller and that was // overwritten by the address of DebugBreakXXX. __ mov(ip, Operand(ExternalReference(Debug_Address::AfterBreakTarget()))); __ ldr(ip, MemOperand(ip)); __ Jump(ip); } void Builtins::Generate_LoadIC_DebugBreak(MacroAssembler* masm) { // Calling convention for IC load (from ic-arm.cc). // ----------- S t a t e ------------- // -- r0 : receiver // -- r2 : name // -- lr : return address // -- [sp] : receiver // ----------------------------------- // Registers r0 and r2 contain objects that needs to be pushed on the // expression stack of the fake JS frame. Generate_DebugBreakCallHelper(masm, r0.bit() | r2.bit()); } void Builtins::Generate_StoreIC_DebugBreak(MacroAssembler* masm) { // Calling convention for IC store (from ic-arm.cc). // ----------- S t a t e ------------- // -- r0 : receiver // -- r2 : name // -- lr : return address // -- [sp] : receiver // ----------------------------------- // Registers r0 and r2 contain objects that needs to be pushed on the // expression stack of the fake JS frame. Generate_DebugBreakCallHelper(masm, r0.bit() | r2.bit()); } void Builtins::Generate_KeyedLoadIC_DebugBreak(MacroAssembler* masm) { // Keyed load IC not implemented on ARM. } void Builtins::Generate_KeyedStoreIC_DebugBreak(MacroAssembler* masm) { // Keyed store IC not implemented on ARM. } void Builtins::Generate_CallIC_DebugBreak(MacroAssembler* masm) { // Calling convention for IC call (from ic-arm.cc) // ----------- S t a t e ------------- // -- r0: number of arguments // -- r1: receiver // -- lr: return address // ----------------------------------- // Register r1 contains an object that needs to be pushed on the expression // stack of the fake JS frame. r0 is the actual number of arguments not // encoded as a smi, therefore it cannot be on the expression stack of the // fake JS frame as it can easily be an invalid pointer (e.g. 1). r0 will be // pushed on the stack of the C frame and restored from there. Generate_DebugBreakCallHelper(masm, r1.bit()); } void Builtins::Generate_ConstructCall_DebugBreak(MacroAssembler* masm) { // In places other than IC call sites it is expected that r0 is TOS which // is an object - this is not generally the case so this should be used with // care. Generate_DebugBreakCallHelper(masm, r0.bit()); } void Builtins::Generate_Return_DebugBreak(MacroAssembler* masm) { // In places other than IC call sites it is expected that r0 is TOS which // is an object - this is not generally the case so this should be used with // care. Generate_DebugBreakCallHelper(masm, r0.bit()); } void Builtins::Generate_Return_DebugBreakEntry(MacroAssembler* masm) { // Generate nothing as this handling of debug break return is not done this // way on ARM - yet. } void Builtins::Generate_StubNoRegisters_DebugBreak(MacroAssembler* masm) { // Generate nothing as CodeStub CallFunction is not used on ARM. } #undef __ } } // namespace v8::internal