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:
@@ -56,8 +56,8 @@ impl crate::isa::unwind::systemv::RegisterMapper<Reg> for RegisterMapper {
|
|||||||
fn sp(&self) -> u16 {
|
fn sp(&self) -> u16 {
|
||||||
regs::stack_reg().get_hw_encoding().into()
|
regs::stack_reg().get_hw_encoding().into()
|
||||||
}
|
}
|
||||||
fn fp(&self) -> u16 {
|
fn fp(&self) -> Option<u16> {
|
||||||
regs::fp_reg().get_hw_encoding().into()
|
Some(regs::fp_reg().get_hw_encoding().into())
|
||||||
}
|
}
|
||||||
fn lr(&self) -> Option<u16> {
|
fn lr(&self) -> Option<u16> {
|
||||||
Some(regs::link_reg().get_hw_encoding().into())
|
Some(regs::link_reg().get_hw_encoding().into())
|
||||||
|
|||||||
@@ -225,6 +225,11 @@ pub enum UnwindInst {
|
|||||||
/// the clobber area.
|
/// the clobber area.
|
||||||
offset_downward_to_clobbers: u32,
|
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
|
/// The stack slot at the given offset from the clobber-area base has been
|
||||||
/// used to save the given register.
|
/// used to save the given register.
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -122,8 +122,10 @@ pub(crate) trait RegisterMapper<Reg> {
|
|||||||
fn map(&self, reg: Reg) -> Result<Register, RegisterMappingError>;
|
fn map(&self, reg: Reg) -> Result<Register, RegisterMappingError>;
|
||||||
/// Gets stack pointer register.
|
/// Gets stack pointer register.
|
||||||
fn sp(&self) -> Register;
|
fn sp(&self) -> Register;
|
||||||
/// Gets the frame pointer register.
|
/// Gets the frame pointer register, if any.
|
||||||
fn fp(&self) -> Register;
|
fn fp(&self) -> Option<Register> {
|
||||||
|
None
|
||||||
|
}
|
||||||
/// Gets the link register, if any.
|
/// Gets the link register, if any.
|
||||||
fn lr(&self) -> Option<Register> {
|
fn lr(&self) -> Option<Register> {
|
||||||
None
|
None
|
||||||
@@ -151,6 +153,7 @@ pub(crate) fn create_unwind_info_from_insts<MR: RegisterMapper<regalloc::Reg>>(
|
|||||||
) -> CodegenResult<UnwindInfo> {
|
) -> CodegenResult<UnwindInfo> {
|
||||||
let mut instructions = vec![];
|
let mut instructions = vec![];
|
||||||
|
|
||||||
|
let mut cfa_offset = 0;
|
||||||
let mut clobber_offset_to_cfa = 0;
|
let mut clobber_offset_to_cfa = 0;
|
||||||
for &(instruction_offset, ref inst) in insts {
|
for &(instruction_offset, ref inst) in insts {
|
||||||
match inst {
|
match inst {
|
||||||
@@ -163,10 +166,14 @@ pub(crate) fn create_unwind_info_from_insts<MR: RegisterMapper<regalloc::Reg>>(
|
|||||||
instruction_offset,
|
instruction_offset,
|
||||||
CallFrameInstruction::CfaOffset(offset_upward_to_caller_sp as i32),
|
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((
|
instructions.push((
|
||||||
instruction_offset,
|
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
|
// If there is a link register on this architecture, note that
|
||||||
// we saved it as well.
|
// 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
|
// 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
|
// 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
|
// been set to the current SP, so we do not need to change the
|
||||||
// offset, only the register.
|
// offset, only the register. (This is done only if the target
|
||||||
instructions.push((
|
// defines a frame pointer register.)
|
||||||
instruction_offset,
|
if let Some(fp) = mr.fp() {
|
||||||
CallFrameInstruction::CfaRegister(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
|
// Record distance from CFA downward to clobber area so we can
|
||||||
// express clobber offsets later in terms of CFA.
|
// express clobber offsets later in terms of CFA.
|
||||||
clobber_offset_to_cfa = offset_upward_to_caller_sp + offset_downward_to_clobbers;
|
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 {
|
&UnwindInst::SaveReg {
|
||||||
clobber_offset,
|
clobber_offset,
|
||||||
reg,
|
reg,
|
||||||
|
|||||||
@@ -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)?;
|
frame_register_offset = ensure_unwind_offset(offset_downward_to_clobbers)?;
|
||||||
unwind_codes.push(UnwindCode::SetFPReg { instruction_offset });
|
unwind_codes.push(UnwindCode::SetFPReg { instruction_offset });
|
||||||
}
|
}
|
||||||
|
&UnwindInst::StackAlloc { size } => {
|
||||||
|
unwind_codes.push(UnwindCode::StackAlloc {
|
||||||
|
instruction_offset,
|
||||||
|
size,
|
||||||
|
});
|
||||||
|
}
|
||||||
&UnwindInst::SaveReg {
|
&UnwindInst::SaveReg {
|
||||||
clobber_offset,
|
clobber_offset,
|
||||||
reg,
|
reg,
|
||||||
|
|||||||
@@ -89,8 +89,8 @@ impl crate::isa::unwind::systemv::RegisterMapper<Reg> for RegisterMapper {
|
|||||||
fn sp(&self) -> u16 {
|
fn sp(&self) -> u16 {
|
||||||
X86_64::RSP.0
|
X86_64::RSP.0
|
||||||
}
|
}
|
||||||
fn fp(&self) -> u16 {
|
fn fp(&self) -> Option<u16> {
|
||||||
X86_64::RBP.0
|
Some(X86_64::RBP.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -121,8 +121,8 @@ pub(crate) fn create_unwind_info(
|
|||||||
fn sp(&self) -> u16 {
|
fn sp(&self) -> u16 {
|
||||||
X86_64::RSP.0
|
X86_64::RSP.0
|
||||||
}
|
}
|
||||||
fn fp(&self) -> u16 {
|
fn fp(&self) -> Option<u16> {
|
||||||
X86_64::RBP.0
|
Some(X86_64::RBP.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let map = RegisterMapper(isa);
|
let map = RegisterMapper(isa);
|
||||||
|
|||||||
Reference in New Issue
Block a user