Allow unwind support to work without a frame pointer

The patch extends the unwinder to support targets that do not need
to use a dedicated frame pointer register.  Specifically, the
changes include:

- Change the "fp" routine in the RegisterMapper to return an
  *optional* frame pointer regsiter via Option<Register>.

- On targets that choose to not define a FP register via the above
  routine, the UnwindInst::DefineNewFrame operation no longer switches
  the CFA to be defined in terms of the FP.  (The operation still can
  be used to define the location of the clobber area.)

- In addition, on targets that choose not to define a FP register, the
  UnwindInst::PushFrameRegs operation is not supported.

- There is a new operation UnwindInst::StackAlloc that needs to be
  called on targets without FP whenever the stack pointer is updated.
  This caused the CFA offset to be adjusted accordingly.  (On
  targets with FP this operation is a no-op.)
This commit is contained in:
Ulrich Weigand
2021-04-14 13:54:02 +02:00
parent 67cc42d4c3
commit 5904c09682
6 changed files with 47 additions and 15 deletions

View File

@@ -56,8 +56,8 @@ impl crate::isa::unwind::systemv::RegisterMapper<Reg> for RegisterMapper {
fn sp(&self) -> u16 {
regs::stack_reg().get_hw_encoding().into()
}
fn fp(&self) -> u16 {
regs::fp_reg().get_hw_encoding().into()
fn fp(&self) -> Option<u16> {
Some(regs::fp_reg().get_hw_encoding().into())
}
fn lr(&self) -> Option<u16> {
Some(regs::link_reg().get_hw_encoding().into())

View File

@@ -225,6 +225,11 @@ pub enum UnwindInst {
/// the clobber area.
offset_downward_to_clobbers: u32,
},
/// The stack pointer was adjusted to allocate the stack.
StackAlloc {
/// Size to allocate.
size: u32,
},
/// The stack slot at the given offset from the clobber-area base has been
/// used to save the given register.
///

View File

@@ -122,8 +122,10 @@ pub(crate) trait RegisterMapper<Reg> {
fn map(&self, reg: Reg) -> Result<Register, RegisterMappingError>;
/// Gets stack pointer register.
fn sp(&self) -> Register;
/// Gets the frame pointer register.
fn fp(&self) -> Register;
/// Gets the frame pointer register, if any.
fn fp(&self) -> Option<Register> {
None
}
/// Gets the link register, if any.
fn lr(&self) -> Option<Register> {
None
@@ -151,6 +153,7 @@ pub(crate) fn create_unwind_info_from_insts<MR: RegisterMapper<regalloc::Reg>>(
) -> CodegenResult<UnwindInfo> {
let mut instructions = vec![];
let mut cfa_offset = 0;
let mut clobber_offset_to_cfa = 0;
for &(instruction_offset, ref inst) in insts {
match inst {
@@ -163,10 +166,14 @@ pub(crate) fn create_unwind_info_from_insts<MR: RegisterMapper<regalloc::Reg>>(
instruction_offset,
CallFrameInstruction::CfaOffset(offset_upward_to_caller_sp as i32),
));
// Note that we saved the old FP value on the stack.
// Note that we saved the old FP value on the stack. Use of this
// operation implies that the target defines a FP register.
instructions.push((
instruction_offset,
CallFrameInstruction::Offset(mr.fp(), -(offset_upward_to_caller_sp as i32)),
CallFrameInstruction::Offset(
mr.fp().unwrap(),
-(offset_upward_to_caller_sp as i32),
),
));
// If there is a link register on this architecture, note that
// we saved it as well.
@@ -188,15 +195,29 @@ pub(crate) fn create_unwind_info_from_insts<MR: RegisterMapper<regalloc::Reg>>(
// Define CFA in terms of FP. Note that we assume it was already
// defined correctly in terms of the current SP, and FP has just
// been set to the current SP, so we do not need to change the
// offset, only the register.
instructions.push((
instruction_offset,
CallFrameInstruction::CfaRegister(mr.fp()),
));
// offset, only the register. (This is done only if the target
// defines a frame pointer register.)
if let Some(fp) = mr.fp() {
instructions.push((instruction_offset, CallFrameInstruction::CfaRegister(fp)));
}
// Record initial CFA offset. This will be used with later
// StackAlloc calls if we do not have a frame pointer.
cfa_offset = offset_upward_to_caller_sp;
// Record distance from CFA downward to clobber area so we can
// express clobber offsets later in terms of CFA.
clobber_offset_to_cfa = offset_upward_to_caller_sp + offset_downward_to_clobbers;
}
&UnwindInst::StackAlloc { size } => {
// If we do not use a frame pointer, we need to update the
// CFA offset whenever the stack pointer changes.
if mr.fp().is_none() {
cfa_offset += size;
instructions.push((
instruction_offset,
CallFrameInstruction::CfaOffset(cfa_offset as i32),
));
}
}
&UnwindInst::SaveReg {
clobber_offset,
reg,

View File

@@ -356,6 +356,12 @@ pub(crate) fn create_unwind_info_from_insts<MR: RegisterMapper<regalloc::Reg>>(
frame_register_offset = ensure_unwind_offset(offset_downward_to_clobbers)?;
unwind_codes.push(UnwindCode::SetFPReg { instruction_offset });
}
&UnwindInst::StackAlloc { size } => {
unwind_codes.push(UnwindCode::StackAlloc {
instruction_offset,
size,
});
}
&UnwindInst::SaveReg {
clobber_offset,
reg,

View File

@@ -89,8 +89,8 @@ impl crate::isa::unwind::systemv::RegisterMapper<Reg> for RegisterMapper {
fn sp(&self) -> u16 {
X86_64::RSP.0
}
fn fp(&self) -> u16 {
X86_64::RBP.0
fn fp(&self) -> Option<u16> {
Some(X86_64::RBP.0)
}
}

View File

@@ -121,8 +121,8 @@ pub(crate) fn create_unwind_info(
fn sp(&self) -> u16 {
X86_64::RSP.0
}
fn fp(&self) -> u16 {
X86_64::RBP.0
fn fp(&self) -> Option<u16> {
Some(X86_64::RBP.0)
}
}
let map = RegisterMapper(isa);