From 9d30045d06bdccfa967b99c8b02b2c31201a37d5 Mon Sep 17 00:00:00 2001 From: "kasperl@chromium.org" Date: Tue, 23 Sep 2008 08:19:26 +0000 Subject: [PATCH] Refactored the code for entering and leaving exit frames (calls from JavaScript to C++). Includes a few slight optimizations like keeping argv in a callee-saved register. Review URL: http://codereview.chromium.org/4035 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@359 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/builtins-arm.cc | 8 +++--- src/builtins-ia32.cc | 10 +++---- src/codegen-arm.cc | 52 ++++++--------------------------- src/codegen-ia32.cc | 57 ++++++------------------------------- src/frames-ia32.h | 4 +-- src/ic-arm.cc | 2 +- src/ic-ia32.cc | 2 +- src/macro-assembler-arm.cc | 54 ++++++++++++++++++++++++++++++++++- src/macro-assembler-arm.h | 11 ++++++- src/macro-assembler-ia32.cc | 54 ++++++++++++++++++++++++++++++++++- src/macro-assembler-ia32.h | 19 +++++++++++-- src/stub-cache-arm.cc | 2 +- src/stub-cache-ia32.cc | 4 +-- 13 files changed, 166 insertions(+), 113 deletions(-) diff --git a/src/builtins-arm.cc b/src/builtins-arm.cc index ae5dd616ca..c030cde0c3 100644 --- a/src/builtins-arm.cc +++ b/src/builtins-arm.cc @@ -168,7 +168,7 @@ void Builtins::Generate_JSConstructCall(MacroAssembler* masm) { // sp[1]: constructor function // sp[2]: number of arguments (smi-tagged) __ ldr(r1, MemOperand(sp, 2 * kPointerSize)); - __ ExitInternalFrame(); + __ LeaveInternalFrame(); __ add(sp, sp, Operand(r1, LSL, kPointerSizeLog2 - 1)); __ add(sp, sp, Operand(kPointerSize)); __ mov(pc, Operand(lr)); @@ -240,7 +240,7 @@ static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm, // Exit the JS frame and remove the parameters (except function), and return. // Respect ABI stack constraint. - __ ExitInternalFrame(); + __ LeaveInternalFrame(); __ mov(pc, lr); // r0: result @@ -337,7 +337,7 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { __ pop(r0); __ mov(r0, Operand(r0, ASR, kSmiTagSize)); - __ ExitInternalFrame(); + __ LeaveInternalFrame(); __ b(&patch_receiver); // Use the global object from the called function as the receiver. @@ -525,7 +525,7 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) { __ InvokeFunction(r1, actual, CALL_FUNCTION); // Tear down the internal frame and remove function, receiver and args. - __ ExitInternalFrame(); + __ LeaveInternalFrame(); __ add(sp, sp, Operand(3 * kPointerSize)); __ mov(pc, lr); } diff --git a/src/builtins-ia32.cc b/src/builtins-ia32.cc index 4b307c974a..de642afa70 100644 --- a/src/builtins-ia32.cc +++ b/src/builtins-ia32.cc @@ -297,7 +297,7 @@ void Builtins::Generate_JSConstructCall(MacroAssembler* masm) { // Restore the arguments count and exit the internal frame. __ bind(&exit); __ mov(ebx, Operand(esp, kPointerSize)); // get arguments count - __ ExitInternalFrame(); + __ LeaveInternalFrame(); // Remove caller arguments from the stack and return. ASSERT(kSmiTagSize == 1 && kSmiTag == 0); @@ -363,7 +363,7 @@ static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm, // Exit the JS frame. Notice that this also removes the empty // context and the function left on the stack by the code // invocation. - __ ExitInternalFrame(); + __ LeaveInternalFrame(); __ ret(1 * kPointerSize); // remove receiver } @@ -450,7 +450,7 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { __ pop(eax); __ shr(eax, kSmiTagSize); - __ ExitInternalFrame(); + __ LeaveInternalFrame(); __ jmp(&patch_receiver); // Use the global object from the called function as the receiver. @@ -613,7 +613,7 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) { __ mov(edi, Operand(ebp, 4 * kPointerSize)); __ InvokeFunction(edi, actual, CALL_FUNCTION); - __ ExitInternalFrame(); + __ LeaveInternalFrame(); __ ret(3 * kPointerSize); // remove this, receiver, and arguments } @@ -771,7 +771,7 @@ static void Generate_DebugBreakCallHelper(MacroAssembler* masm, __ PopRegistersToMemory(pointer_regs); // Get rid of the internal frame. - __ ExitInternalFrame(); + __ LeaveInternalFrame(); // If this call did not replace a call but patched other code then there will // be an unwanted return address left on the stack. Here we get rid of that. diff --git a/src/codegen-arm.cc b/src/codegen-arm.cc index fc66f4d8f4..4d08e55d63 100644 --- a/src/codegen-arm.cc +++ b/src/codegen-arm.cc @@ -1634,6 +1634,7 @@ void CEntryStub::GenerateCore(MacroAssembler* masm, // r0: result parameter for PerformGC, if any // r4: number of arguments including receiver (C callee-saved) // r5: pointer to builtin function (C callee-saved) + // r6: pointer to the first argument (C callee-saved) if (do_gc) { // Passing r0. @@ -1641,11 +1642,9 @@ void CEntryStub::GenerateCore(MacroAssembler* masm, } // Call C built-in. - // r0 = argc. + // r0 = argc, r1 = argv __ mov(r0, Operand(r4)); - // r1 = argv. - __ add(r1, fp, Operand(r4, LSL, kPointerSizeLog2)); - __ add(r1, r1, Operand(ExitFrameConstants::kPPDisplacement - kPointerSize)); + __ mov(r1, Operand(r6)); // TODO(1242173): To let the GC traverse the return address of the exit // frames, we need to know where the return address is. Right now, @@ -1672,11 +1671,6 @@ void CEntryStub::GenerateCore(MacroAssembler* masm, __ tst(r2, Operand(kFailureTagMask)); __ b(eq, &failure_returned); - // clear top frame - __ mov(r3, Operand(0)); - __ mov(ip, Operand(ExternalReference(Top::k_c_entry_fp_address))); - __ str(r3, MemOperand(ip)); - // Restore the memory copy of the registers by digging them out from // the stack. if (do_restore) { @@ -1692,12 +1686,7 @@ void CEntryStub::GenerateCore(MacroAssembler* masm, // sp: stack pointer // fp: frame pointer // pp: caller's parameter pointer pp (restored as C callee-saved) - - // Restore current context from top and clear it in debug mode. - __ mov(r3, Operand(Top::context_address())); - __ ldr(cp, MemOperand(r3)); - __ mov(sp, Operand(fp)); // respect ABI stack constraint - __ ldm(ia, sp, fp.bit() | sp.bit() | pc.bit()); + __ LeaveExitFrame(); // check if we should retry or throw exception Label retry; @@ -1745,28 +1734,12 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) { // this by performing a garbage collection and retrying the // builtin once. - // Enter C frame - // Compute parameter pointer before making changes and save it as ip register - // so that it is restored as sp register on exit, thereby popping the args. - // ip = sp + kPointerSize*args_len; - __ add(ip, sp, Operand(r0, LSL, kPointerSizeLog2)); + StackFrame::Type frame_type = is_debug_break + ? StackFrame::EXIT_DEBUG + : StackFrame::EXIT; - // push in reverse order: - // caller_fp, sp_on_exit, caller_pc - __ stm(db_w, sp, fp.bit() | ip.bit() | lr.bit()); - __ mov(fp, Operand(sp)); // setup new frame pointer - - // Store the current context in top. - __ mov(ip, Operand(ExternalReference(Top::k_context_address))); - __ str(cp, MemOperand(ip)); - - // remember top frame - __ mov(ip, Operand(ExternalReference(Top::k_c_entry_fp_address))); - __ str(fp, MemOperand(ip)); - - // Push debug marker. - __ mov(ip, Operand(is_debug_break ? 1 : 0)); - __ push(ip); + // Enter the exit frame that transitions from JavaScript to C++. + __ EnterExitFrame(frame_type); if (is_debug_break) { // Save the state of all registers to the stack from the memory location. @@ -1774,13 +1747,6 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) { __ CopyRegistersFromMemoryToStack(sp, kJSCallerSaved); } - // move number of arguments (argc) into callee-saved register - __ mov(r4, Operand(r0)); - - // move pointer to builtin function into callee-saved register - __ mov(r5, Operand(r1)); - - // r0: result parameter for PerformGC, if any (setup below) // r4: number of arguments // r5: pointer to builtin function (C callee-saved) diff --git a/src/codegen-ia32.cc b/src/codegen-ia32.cc index 9f1d30ef9b..be8b11310c 100644 --- a/src/codegen-ia32.cc +++ b/src/codegen-ia32.cc @@ -5252,6 +5252,7 @@ void CEntryStub::GenerateCore(MacroAssembler* masm, // ebp: frame pointer (restored after C call) // esp: stack pointer (restored after C call) // edi: number of arguments including receiver (C callee-saved) + // esi: pointer to the first argument (C callee-saved) if (do_gc) { __ mov(Operand(esp, 0 * kPointerSize), eax); // Result. @@ -5259,12 +5260,8 @@ void CEntryStub::GenerateCore(MacroAssembler* masm, } // Call C function. - __ lea(eax, Operand(ebp, - edi, - times_4, - StandardFrameConstants::kCallerSPOffset - kPointerSize)); __ mov(Operand(esp, 0 * kPointerSize), edi); // argc. - __ mov(Operand(esp, 1 * kPointerSize), eax); // argv. + __ mov(Operand(esp, 1 * kPointerSize), esi); // argv. __ call(Operand(ebx)); // Result is in eax or edx:eax - do not destroy these registers! @@ -5276,11 +5273,6 @@ void CEntryStub::GenerateCore(MacroAssembler* masm, __ test(ecx, Immediate(kFailureTagMask)); __ j(zero, &failure_returned, not_taken); - // Restore number of arguments to ecx and clear top frame. - __ mov(ecx, Operand(edi)); - ExternalReference c_entry_fp_address(Top::k_c_entry_fp_address); - __ mov(Operand::StaticVariable(c_entry_fp_address), Immediate(0)); - // Restore the memory copy of the registers by digging them out from // the stack. if (do_restore) { @@ -5289,25 +5281,11 @@ void CEntryStub::GenerateCore(MacroAssembler* masm, const int kCallerSavedSize = kNumJSCallerSaved * kPointerSize; int kOffset = ExitFrameConstants::kDebugMarkOffset - kCallerSavedSize; __ lea(ebx, Operand(ebp, kOffset)); - __ CopyRegistersFromStackToMemory(ebx, edi, kJSCallerSaved); + __ CopyRegistersFromStackToMemory(ebx, ecx, kJSCallerSaved); } // Exit C frame. - __ lea(esp, Operand(ebp, -1 * kPointerSize)); - __ pop(ebx); - __ pop(ebp); - - // Restore current context from top and clear it in debug mode. - ExternalReference context_address(Top::k_context_address); - __ mov(esi, Operand::StaticVariable(context_address)); - if (kDebug) { - __ mov(Operand::StaticVariable(context_address), Immediate(0)); - } - - // Pop arguments from caller's stack and return. - __ pop(ebx); // Ok to clobber ebx - function pointer not needed anymore. - __ lea(esp, Operand(esp, ecx, times_4, 0)); - __ push(ebx); + __ LeaveExitFrame(); __ ret(0); // Handling of Failure. @@ -5407,25 +5385,12 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) { // this by performing a garbage collection and retrying the // builtin once. - // Enter C frame. - // Here we make the following assumptions and use them when setting - // up the top-most Frame. Adjust the code if these assumptions - // change. - ASSERT(ExitFrameConstants::kPPDisplacement == +2 * kPointerSize); - ASSERT(ExitFrameConstants::kCallerPCOffset == +1 * kPointerSize); - ASSERT(ExitFrameConstants::kCallerFPOffset == 0 * kPointerSize); - ASSERT(ExitFrameConstants::kSPOffset == -2 * kPointerSize); - __ push(ebp); // caller fp - __ mov(ebp, Operand(esp)); // C entry fp - __ push(ebx); // C function - __ push(Immediate(0)); // saved entry sp, set before call - __ push(Immediate(is_debug_break ? 1 : 0)); + StackFrame::Type frame_type = is_debug_break ? + StackFrame::EXIT_DEBUG : + StackFrame::EXIT; - // Remember top frame. - ExternalReference c_entry_fp(Top::k_c_entry_fp_address); - ExternalReference context_address(Top::k_context_address); - __ mov(Operand::StaticVariable(c_entry_fp), ebp); - __ mov(Operand::StaticVariable(context_address), esi); + // Enter the exit frame that transitions from JavaScript to C++. + __ EnterExitFrame(frame_type); if (is_debug_break) { // Save the state of all registers to the stack from the memory @@ -5440,10 +5405,6 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) { __ PushRegistersFromMemory(kJSCallerSaved); } - // Move number of arguments (argc) into callee-saved register. Note - // that edi is only available after remembering the top frame. - __ mov(edi, Operand(eax)); - // Allocate stack space for 2 arguments (argc, argv). GenerateReserveCParameterSpace(masm, 2); __ mov(Operand(ebp, ExitFrameConstants::kSPOffset), esp); // save entry sp diff --git a/src/frames-ia32.h b/src/frames-ia32.h index a39e5b947e..e31906de3b 100644 --- a/src/frames-ia32.h +++ b/src/frames-ia32.h @@ -81,8 +81,8 @@ class EntryFrameConstants : public AllStatic { class ExitFrameConstants : public AllStatic { public: - static const int kDebugMarkOffset = -3 * kPointerSize; - static const int kSPOffset = -2 * kPointerSize; + static const int kDebugMarkOffset = -2 * kPointerSize; + static const int kSPOffset = -1 * kPointerSize; // Let the parameters pointer for exit frames point just below the // frame structure on the stack (frame pointer and return address). diff --git a/src/ic-arm.cc b/src/ic-arm.cc index bece2a8660..fee95e0049 100644 --- a/src/ic-arm.cc +++ b/src/ic-arm.cc @@ -427,7 +427,7 @@ void CallIC::Generate(MacroAssembler* masm, // Move result to r1. __ mov(r1, Operand(r0)); - __ ExitInternalFrame(); + __ LeaveInternalFrame(); // Patch the function on the stack; 1 ~ receiver. __ str(r1, MemOperand(sp, (argc + 1) * kPointerSize)); diff --git a/src/ic-ia32.cc b/src/ic-ia32.cc index 37c35ee926..b9f77d5a9f 100644 --- a/src/ic-ia32.cc +++ b/src/ic-ia32.cc @@ -528,7 +528,7 @@ void CallIC::Generate(MacroAssembler* masm, // Move result to edi and exit the internal frame. __ mov(Operand(edi), eax); - __ ExitInternalFrame(); + __ LeaveInternalFrame(); // Invoke the function. ParameterCount actual(argc); diff --git a/src/macro-assembler-arm.cc b/src/macro-assembler-arm.cc index a03f2b4422..58011e0be4 100644 --- a/src/macro-assembler-arm.cc +++ b/src/macro-assembler-arm.cc @@ -264,7 +264,7 @@ void MacroAssembler::EnterInternalFrame() { } -void MacroAssembler::ExitInternalFrame() { +void MacroAssembler::LeaveInternalFrame() { // r0: preserved // r1: preserved // r2: preserved @@ -276,6 +276,58 @@ void MacroAssembler::ExitInternalFrame() { } +void MacroAssembler::EnterExitFrame(StackFrame::Type type) { + ASSERT(type == StackFrame::EXIT || type == StackFrame::EXIT_DEBUG); + // Compute parameter pointer before making changes and save it as ip + // register so that it is restored as sp register on exit, thereby + // popping the args. + + // ip = sp + kPointerSize * #args; + add(ip, sp, Operand(r0, LSL, kPointerSizeLog2)); + + // Push in reverse order: caller_fp, sp_on_exit, and caller_pc. + stm(db_w, sp, fp.bit() | ip.bit() | lr.bit()); + mov(fp, Operand(sp)); // setup new frame pointer + + // Push debug marker. + mov(ip, Operand(type == StackFrame::EXIT_DEBUG ? 1 : 0)); + push(ip); + + // Save the frame pointer and the context in top. + mov(ip, Operand(ExternalReference(Top::k_c_entry_fp_address))); + str(fp, MemOperand(ip)); + mov(ip, Operand(ExternalReference(Top::k_context_address))); + str(cp, MemOperand(ip)); + + // Setup argc and the builtin function in callee-saved registers. + mov(r4, Operand(r0)); + mov(r5, Operand(r1)); + + // Compute the argv pointer and keep it in a callee-saved register. + add(r6, fp, Operand(r4, LSL, kPointerSizeLog2)); + add(r6, r6, Operand(ExitFrameConstants::kPPDisplacement - kPointerSize)); +} + + +void MacroAssembler::LeaveExitFrame() { + // Clear top frame. + mov(r3, Operand(0)); + mov(ip, Operand(ExternalReference(Top::k_c_entry_fp_address))); + str(r3, MemOperand(ip)); + + // Restore current context from top and clear it in debug mode. + mov(ip, Operand(ExternalReference(Top::k_context_address))); + ldr(cp, MemOperand(ip)); + if (kDebug) { + str(r3, MemOperand(ip)); + } + + // Pop the arguments, restore registers, and return. + mov(sp, Operand(fp)); // respect ABI stack constraint + ldm(ia, sp, fp.bit() | sp.bit() | pc.bit()); +} + + void MacroAssembler::InvokePrologue(const ParameterCount& expected, const ParameterCount& actual, Handle code_constant, diff --git a/src/macro-assembler-arm.h b/src/macro-assembler-arm.h index 2c40d8a104..9e1bf3fbae 100644 --- a/src/macro-assembler-arm.h +++ b/src/macro-assembler-arm.h @@ -99,7 +99,16 @@ class MacroAssembler: public Assembler { // Activation frames void EnterInternalFrame(); - void ExitInternalFrame(); + void LeaveInternalFrame(); + + // Enter specific kind of exit frame; either EXIT or + // EXIT_DEBUG. Expects the number of arguments in register r0 and + // the builtin function to call in register r1. Exits with argc in + // r4, argv in r6, and and the builtin function to call in r5. + void EnterExitFrame(StackFrame::Type type); + + // Leave the current exit frame. Expects the return value in r0. + void LeaveExitFrame(); // --------------------------------------------------------------------------- diff --git a/src/macro-assembler-ia32.cc b/src/macro-assembler-ia32.cc index d93cefbb5e..a4564f35ec 100644 --- a/src/macro-assembler-ia32.cc +++ b/src/macro-assembler-ia32.cc @@ -325,7 +325,7 @@ void MacroAssembler::EnterInternalFrame() { } -void MacroAssembler::ExitInternalFrame() { +void MacroAssembler::LeaveInternalFrame() { if (FLAG_debug_code) { StackFrame::Type type = StackFrame::INTERNAL; cmp(Operand(ebp, StandardFrameConstants::kMarkerOffset), @@ -336,6 +336,58 @@ void MacroAssembler::ExitInternalFrame() { } +void MacroAssembler::EnterExitFrame(StackFrame::Type type) { + ASSERT(type == StackFrame::EXIT || type == StackFrame::EXIT_DEBUG); + + // Setup the frame structure on the stack. + ASSERT(ExitFrameConstants::kPPDisplacement == +2 * kPointerSize); + ASSERT(ExitFrameConstants::kCallerPCOffset == +1 * kPointerSize); + ASSERT(ExitFrameConstants::kCallerFPOffset == 0 * kPointerSize); + push(ebp); + mov(ebp, Operand(esp)); + + // Reserve room for entry stack pointer and push the debug marker. + ASSERT(ExitFrameConstants::kSPOffset == -1 * kPointerSize); + push(Immediate(0)); // saved entry sp, patched before call + push(Immediate(type == StackFrame::EXIT_DEBUG ? 1 : 0)); + + // Save the frame pointer and the context in top. + ExternalReference c_entry_fp_address(Top::k_c_entry_fp_address); + ExternalReference context_address(Top::k_context_address); + mov(Operand::StaticVariable(c_entry_fp_address), ebp); + mov(Operand::StaticVariable(context_address), esi); + + // Setup argc and argv in callee-saved registers. + int offset = StandardFrameConstants::kCallerSPOffset - kPointerSize; + mov(edi, Operand(eax)); + lea(esi, Operand(ebp, eax, times_4, offset)); +} + + +void MacroAssembler::LeaveExitFrame() { + // Get the return address from the stack and restore the frame pointer. + mov(ecx, Operand(ebp, 1 * kPointerSize)); + mov(ebp, Operand(ebp, 0 * kPointerSize)); + + // Pop the arguments and the receiver from the caller stack. + lea(esp, Operand(esi, 1 * kPointerSize)); + + // Restore current context from top and clear it in debug mode. + ExternalReference context_address(Top::k_context_address); + mov(esi, Operand::StaticVariable(context_address)); + if (kDebug) { + mov(Operand::StaticVariable(context_address), Immediate(0)); + } + + // Push the return address to get ready to return. + push(ecx); + + // Clear the top frame. + ExternalReference c_entry_fp_address(Top::k_c_entry_fp_address); + mov(Operand::StaticVariable(c_entry_fp_address), Immediate(0)); +} + + void MacroAssembler::PushTryHandler(CodeLocation try_location, HandlerType type) { ASSERT(StackHandlerConstants::kSize == 6 * kPointerSize); // adjust this code diff --git a/src/macro-assembler-ia32.h b/src/macro-assembler-ia32.h index da7ce389ea..7cca56cfc0 100644 --- a/src/macro-assembler-ia32.h +++ b/src/macro-assembler-ia32.h @@ -86,10 +86,19 @@ class MacroAssembler: public Assembler { // --------------------------------------------------------------------------- // Activation frames - // Enter or exit a stack frame of the given type. Cannot be used to - // construct or leave JavaScript frames. void EnterInternalFrame(); - void ExitInternalFrame(); + void LeaveInternalFrame(); + + // Enter specific kind of exit frame; either EXIT or + // EXIT_DEBUG. Expects the number of arguments in register eax and + // sets up the number of arguments in register edi and the pointer + // to the first argument in register esi. + void EnterExitFrame(StackFrame::Type type); + + // Leave the current exit frame. Expects the return value in + // register eax:edx (untouched) and the pointer to the first + // argument in register esi. + void LeaveExitFrame(); // --------------------------------------------------------------------------- @@ -198,6 +207,10 @@ class MacroAssembler: public Assembler { // Jump to the builtin routine. void JumpToBuiltin(const ExternalReference& ext); + + // --------------------------------------------------------------------------- + // Utilities + void Ret(); struct Unresolved { diff --git a/src/stub-cache-arm.cc b/src/stub-cache-arm.cc index 51cee7fde0..3513c4037d 100644 --- a/src/stub-cache-arm.cc +++ b/src/stub-cache-arm.cc @@ -178,7 +178,7 @@ Object* StubCompiler::CompileLazyCompile(Code::Flags flags) { __ pop(r1); // Tear down temporary frame. - __ ExitInternalFrame(); + __ LeaveInternalFrame(); // Do a tail-call of the compiled function. __ Jump(r2); diff --git a/src/stub-cache-ia32.cc b/src/stub-cache-ia32.cc index e5301ba691..220035bb36 100644 --- a/src/stub-cache-ia32.cc +++ b/src/stub-cache-ia32.cc @@ -476,7 +476,7 @@ Object* StubCompiler::CompileLazyCompile(Code::Flags flags) { __ CallRuntime(Runtime::kLazyCompile, 1); __ pop(edi); - __ ExitInternalFrame(); + __ LeaveInternalFrame(); // Do a tail-call of the compiled function. __ lea(ecx, FieldOperand(eax, Code::kHeaderSize)); @@ -688,7 +688,7 @@ Object* CallStubCompiler::CompileCallInterceptor(Object* object, __ mov(edx, Operand(ebp, (argc + 2) * kPointerSize)); // receiver // Exit frame. - __ ExitInternalFrame(); + __ LeaveInternalFrame(); // Check that the function really is a function. __ test(edi, Immediate(kSmiTagMask));