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
This commit is contained in:
kasperl@chromium.org 2008-09-23 08:19:26 +00:00
parent ee7e7be35f
commit 9d30045d06
13 changed files with 166 additions and 113 deletions

View File

@ -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);
}

View File

@ -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.

View File

@ -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)

View File

@ -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

View File

@ -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).

View File

@ -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));

View File

@ -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);

View File

@ -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> code_constant,

View File

@ -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();
// ---------------------------------------------------------------------------

View File

@ -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

View File

@ -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 {

View File

@ -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);

View File

@ -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));