Change ARM exit frame layout and alingment handling

Change the ARM exit frame to have the same layout as the IA32 exit frame. This basically re-arranges the order of fp and sp and changes the sp location of the entry frame to hold the sp used by the gc and not the sp for popping the arguments. This removes the option of tearing down the frame and returning using one ldm instruction.

The main motivation for this is to avoid pushing an alignment word before generating the entry frame. The GC handling of optimized frames process the registers pushed as part of a safepoint and asumes that these are at the top of the frame, so if an alignment word is pushed this processing will be one off.

The alignment handling in the C entry stub have also been simplified. Now the value of lr is stored to a stack slot already reserved avoiding pushing it and keeping track of "frame skew".

This does result in more instructions in the exit frame on ARM, but we can look into improving this later.
Review URL: http://codereview.chromium.org/6247019

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@6448 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
sgjesse@chromium.org 2011-01-25 07:49:39 +00:00
parent 2e8b8d3ddf
commit 161d631b5e
8 changed files with 74 additions and 115 deletions

View File

@ -1,4 +1,4 @@
// Copyright 2010 the V8 project authors. All rights reserved.
// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@ -2557,8 +2557,7 @@ void CEntryStub::GenerateCore(MacroAssembler* masm,
Label* throw_termination_exception,
Label* throw_out_of_memory_exception,
bool do_gc,
bool always_allocate,
int frame_alignment_skew) {
bool always_allocate) {
// r0: result parameter for PerformGC, if any
// r4: number of arguments including receiver (C callee-saved)
// r5: pointer to builtin function (C callee-saved)
@ -2584,14 +2583,13 @@ void CEntryStub::GenerateCore(MacroAssembler* masm,
__ mov(r0, Operand(r4));
__ mov(r1, Operand(r6));
#if defined(V8_HOST_ARCH_ARM)
int frame_alignment = MacroAssembler::ActivationFrameAlignment();
int frame_alignment_mask = frame_alignment - 1;
#if defined(V8_HOST_ARCH_ARM)
if (FLAG_debug_code) {
if (frame_alignment > kPointerSize) {
Label alignment_as_expected;
ASSERT(IsPowerOf2(frame_alignment));
__ sub(r2, sp, Operand(frame_alignment_skew));
__ tst(r2, Operand(frame_alignment_mask));
__ b(eq, &alignment_as_expected);
// Don't use Check here, as it will call Runtime_Abort re-entering here.
@ -2601,35 +2599,20 @@ void CEntryStub::GenerateCore(MacroAssembler* masm,
}
#endif
// Just before the call (jump) below lr is pushed, so the actual alignment is
// adding one to the current skew.
int alignment_before_call =
(frame_alignment_skew + kPointerSize) & frame_alignment_mask;
if (alignment_before_call > 0) {
// Push until the alignment before the call is met.
__ mov(r2, Operand(0, RelocInfo::NONE));
for (int i = alignment_before_call;
(i & frame_alignment_mask) != 0;
i += kPointerSize) {
__ push(r2);
}
}
// 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,
// we push it on the stack to be able to find it again, but we never
// we store it on the stack to be able to find it again, but we never
// restore from it in case of changes, which makes it impossible to
// support moving the C entry code stub. This should be fixed, but currently
// this is OK because the CEntryStub gets generated so early in the V8 boot
// sequence that it is not moving ever.
masm->add(lr, pc, Operand(4)); // Compute return address: (pc + 8) + 4
masm->push(lr);
masm->Jump(r5);
// Restore sp back to before aligning the stack.
if (alignment_before_call > 0) {
__ add(sp, sp, Operand(alignment_before_call));
}
// Compute the return address in lr to return to after the jump below. Pc is
// already at '+ 8' from the current instruction but return is after three
// instructions so add another 4 to pc to get the return address.
masm->add(lr, pc, Operand(4));
__ str(lr, MemOperand(sp, 0));
masm->Jump(r5);
if (always_allocate) {
// It's okay to clobber r2 and r3 here. Don't mess with r0 and r1
@ -2717,8 +2700,7 @@ void CEntryStub::Generate(MacroAssembler* masm) {
&throw_termination_exception,
&throw_out_of_memory_exception,
false,
false,
-kPointerSize);
false);
// Do space-specific GC and retry runtime call.
GenerateCore(masm,
@ -2726,8 +2708,7 @@ void CEntryStub::Generate(MacroAssembler* masm) {
&throw_termination_exception,
&throw_out_of_memory_exception,
true,
false,
0);
false);
// Do full GC and retry runtime call one final time.
Failure* failure = Failure::InternalError();
@ -2737,8 +2718,7 @@ void CEntryStub::Generate(MacroAssembler* masm) {
&throw_termination_exception,
&throw_out_of_memory_exception,
true,
true,
kPointerSize);
true);
__ bind(&throw_out_of_memory_exception);
GenerateThrowUncatchable(masm, OUT_OF_MEMORY);

View File

@ -1,4 +1,4 @@
// Copyright 2006-2008 the V8 project authors. All rights reserved.
// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@ -30,20 +30,13 @@
#if defined(V8_TARGET_ARCH_ARM)
#include "frames-inl.h"
#include "arm/assembler-arm-inl.h"
namespace v8 {
namespace internal {
Address ExitFrame::ComputeStackPointer(Address fp) {
Address marker = Memory::Address_at(fp + ExitFrameConstants::kMarkerOffset);
Address sp = fp + ExitFrameConstants::kSPOffset;
if (marker == NULL) {
sp -= DwVfpRegister::kNumRegisters * kDoubleSize + 2 * kPointerSize;
}
return sp;
return Memory::Address_at(fp + ExitFrameConstants::kSPOffset);
}

View File

@ -107,21 +107,17 @@ class EntryFrameConstants : public AllStatic {
class ExitFrameConstants : public AllStatic {
public:
static const int kCodeOffset = -1 * kPointerSize;
static const int kCodeOffset = -2 * kPointerSize;
static const int kSPOffset = -1 * kPointerSize;
// TODO(regis): Use a patched sp value on the stack instead.
// A marker of 0 indicates that double registers are saved.
static const int kMarkerOffset = -2 * kPointerSize;
// The caller fields are below the frame pointer on the stack.
static const int kCallerFPOffset = +0 * kPointerSize;
// The calling JS function is between FP and PC.
static const int kCallerPCOffset = +2 * kPointerSize;
static const int kCallerFPOffset = 0 * kPointerSize;
// The calling JS function is below FP.
static const int kCallerPCOffset = 1 * kPointerSize;
// FP-relative displacement of the caller's SP. It points just
// below the saved PC.
static const int kCallerSPDisplacement = +3 * kPointerSize;
static const int kCallerSPDisplacement = 2 * kPointerSize;
};
@ -131,8 +127,8 @@ class StandardFrameConstants : public AllStatic {
static const int kMarkerOffset = -2 * kPointerSize;
static const int kContextOffset = -1 * kPointerSize;
static const int kCallerFPOffset = 0 * kPointerSize;
static const int kCallerPCOffset = +1 * kPointerSize;
static const int kCallerSPOffset = +2 * kPointerSize;
static const int kCallerPCOffset = 1 * kPointerSize;
static const int kCallerSPOffset = 2 * kPointerSize;
};

View File

@ -1,4 +1,4 @@
// Copyright 2010 the V8 project authors. All rights reserved.
// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@ -615,37 +615,24 @@ void MacroAssembler::LeaveFrame(StackFrame::Type type) {
void MacroAssembler::EnterExitFrame(bool save_doubles) {
// r0 is argc.
// Compute callee's stack pointer before making changes and save it as
// ip register so that it is restored as sp register on exit, thereby
// popping the args.
// Compute the argv pointer in a callee-saved register.
add(r6, sp, Operand(r0, LSL, kPointerSizeLog2));
sub(r6, r6, Operand(kPointerSize));
// ip = sp + kPointerSize * #args;
add(ip, sp, Operand(r0, LSL, kPointerSizeLog2));
// Compute the argv pointer and keep it in a callee-saved register.
sub(r6, ip, Operand(kPointerSize));
// Prepare the stack to be aligned when calling into C. After this point there
// are 5 pushes before the call into C, so the stack needs to be aligned after
// 5 pushes.
int frame_alignment = ActivationFrameAlignment();
int frame_alignment_mask = frame_alignment - 1;
if (frame_alignment != kPointerSize) {
// The following code needs to be more general if this assert does not hold.
ASSERT(frame_alignment == 2 * kPointerSize);
// With 5 pushes left the frame must be unaligned at this point.
mov(r7, Operand(Smi::FromInt(0)));
tst(sp, Operand((frame_alignment - kPointerSize) & frame_alignment_mask));
push(r7, eq); // Push if aligned to make it unaligned.
}
// Push in reverse order: caller_fp, sp_on_exit, and caller_pc.
stm(db_w, sp, fp.bit() | ip.bit() | lr.bit());
// Setup the frame structure on the stack.
ASSERT_EQ(2 * kPointerSize, ExitFrameConstants::kCallerSPDisplacement);
ASSERT_EQ(1 * kPointerSize, ExitFrameConstants::kCallerPCOffset);
ASSERT_EQ(0 * kPointerSize, ExitFrameConstants::kCallerFPOffset);
Push(lr, fp);
mov(fp, Operand(sp)); // Setup new frame pointer.
// Reserve room for saved entry sp and code object.
sub(sp, sp, Operand(2 * kPointerSize));
if (FLAG_debug_code) {
mov(ip, Operand(0));
str(ip, MemOperand(fp, ExitFrameConstants::kSPOffset));
}
mov(ip, Operand(CodeObject()));
push(ip); // Accessed from ExitFrame::code_slot.
str(ip, MemOperand(fp, ExitFrameConstants::kCodeOffset));
// Save the frame pointer and the context in top.
mov(ip, Operand(ExternalReference(Top::k_c_entry_fp_address)));
@ -659,25 +646,30 @@ void MacroAssembler::EnterExitFrame(bool save_doubles) {
// Optionally save all double registers.
if (save_doubles) {
// TODO(regis): Use vstrm instruction.
// The stack alignment code above made sp unaligned, so add space for one
// more double register and use aligned addresses.
ASSERT(kDoubleSize == frame_alignment);
// Mark the frame as containing doubles by pushing a non-valid return
// address, i.e. 0.
ASSERT(ExitFrameConstants::kMarkerOffset == -2 * kPointerSize);
mov(ip, Operand(0)); // Marker and alignment word.
push(ip);
int space = DwVfpRegister::kNumRegisters * kDoubleSize + kPointerSize;
sub(sp, sp, Operand(space));
sub(sp, sp, Operand(DwVfpRegister::kNumRegisters * kDoubleSize));
const int offset = -2 * kPointerSize;
for (int i = 0; i < DwVfpRegister::kNumRegisters; i++) {
DwVfpRegister reg = DwVfpRegister::from_code(i);
vstr(reg, sp, i * kDoubleSize + kPointerSize);
vstr(reg, fp, offset - ((i + 1) * kDoubleSize));
}
// Note that d0 will be accessible at fp - 2*kPointerSize -
// DwVfpRegister::kNumRegisters * kDoubleSize, since the code slot and the
// alignment word were pushed after the fp.
// Note that d0 will be accessible at
// fp - 2 * kPointerSize - DwVfpRegister::kNumRegisters * kDoubleSize,
// since the sp slot and code slot were pushed after the fp.
}
// Reserve place for the return address and align the frame preparing for
// calling the runtime function.
const int frame_alignment = MacroAssembler::ActivationFrameAlignment();
sub(sp, sp, Operand(kPointerSize));
if (frame_alignment > 0) {
ASSERT(IsPowerOf2(frame_alignment));
and_(sp, sp, Operand(-frame_alignment));
}
// Set the exit frame sp value to point just before the return address
// location.
add(ip, sp, Operand(kPointerSize));
str(ip, MemOperand(fp, ExitFrameConstants::kSPOffset));
}
@ -715,12 +707,10 @@ int MacroAssembler::ActivationFrameAlignment() {
void MacroAssembler::LeaveExitFrame(bool save_doubles) {
// Optionally restore all double registers.
if (save_doubles) {
// TODO(regis): Use vldrm instruction.
for (int i = 0; i < DwVfpRegister::kNumRegisters; i++) {
DwVfpRegister reg = DwVfpRegister::from_code(i);
// Register d15 is just below the marker.
const int offset = ExitFrameConstants::kMarkerOffset;
vldr(reg, fp, (i - DwVfpRegister::kNumRegisters) * kDoubleSize + offset);
const int offset = -2 * kPointerSize;
vldr(reg, fp, offset - ((i + 1) * kDoubleSize));
}
}
@ -736,9 +726,12 @@ void MacroAssembler::LeaveExitFrame(bool save_doubles) {
str(r3, MemOperand(ip));
#endif
// Pop the arguments, restore registers, and return.
mov(sp, Operand(fp)); // respect ABI stack constraint
ldm(ia, sp, fp.bit() | sp.bit() | pc.bit());
// Tear down the exit frame, pop the arguments, and return. Callee-saved
// register r4 still holds argc.
mov(sp, Operand(fp));
ldm(ia_w, sp, fp.bit() | lr.bit());
add(sp, sp, Operand(r4, LSL, kPointerSizeLog2));
mov(pc, lr);
}

View File

@ -600,8 +600,7 @@ class CEntryStub : public CodeStub {
Label* throw_termination_exception,
Label* throw_out_of_memory_exception,
bool do_gc,
bool always_allocate_scope,
int alignment_skew = 0);
bool always_allocate_scope);
void GenerateThrowTOS(MacroAssembler* masm);
void GenerateThrowUncatchable(MacroAssembler* masm,
UncatchableExceptionType type);

View File

@ -1,4 +1,4 @@
// Copyright 2010 the V8 project authors. All rights reserved.
// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@ -4660,8 +4660,7 @@ void CEntryStub::GenerateCore(MacroAssembler* masm,
Label* throw_termination_exception,
Label* throw_out_of_memory_exception,
bool do_gc,
bool always_allocate_scope,
int /* alignment_skew */) {
bool always_allocate_scope) {
// eax: result parameter for PerformGC, if any
// ebx: pointer to C function (C callee-saved)
// ebp: frame pointer (restored after C call)

View File

@ -1,4 +1,4 @@
// Copyright 2010 the V8 project authors. All rights reserved.
// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@ -339,7 +339,7 @@ void MacroAssembler::EnterExitFrameEpilogue(int argc, bool save_doubles) {
CpuFeatures::Scope scope(SSE2);
int space = XMMRegister::kNumRegisters * kDoubleSize + argc * kPointerSize;
sub(Operand(esp), Immediate(space));
int offset = -2 * kPointerSize;
const int offset = -2 * kPointerSize;
for (int i = 0; i < XMMRegister::kNumRegisters; i++) {
XMMRegister reg = XMMRegister::from_code(i);
movdbl(Operand(ebp, offset - ((i + 1) * kDoubleSize)), reg);
@ -382,7 +382,7 @@ void MacroAssembler::LeaveExitFrame(bool save_doubles) {
// Optionally restore all XMM registers.
if (save_doubles) {
CpuFeatures::Scope scope(SSE2);
int offset = -2 * kPointerSize;
const int offset = -2 * kPointerSize;
for (int i = 0; i < XMMRegister::kNumRegisters; i++) {
XMMRegister reg = XMMRegister::from_code(i);
movdbl(reg, Operand(ebp, offset - ((i + 1) * kDoubleSize)));

View File

@ -1,4 +1,4 @@
// Copyright 2010 the V8 project authors. All rights reserved.
// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@ -2773,8 +2773,7 @@ void CEntryStub::GenerateCore(MacroAssembler* masm,
Label* throw_termination_exception,
Label* throw_out_of_memory_exception,
bool do_gc,
bool always_allocate_scope,
int /* alignment_skew */) {
bool always_allocate_scope) {
// rax: result parameter for PerformGC, if any.
// rbx: pointer to C function (C callee-saved).
// rbp: frame pointer (restored after C call).