Skip to content

Commit

Permalink
Support referencing stack slots in the DWARF debug info (bytecodealli…
Browse files Browse the repository at this point in the history
…ance#6960)

* Add a test

* Disable test

* Add support for specifying stack locations in debug info

Always emit SysV-style CFI unwind info if we need debug info,
and reference it in the debug info using DW_OP_call_frame_cfa.

* Add toolchain comment to the test

* Add a comment and assert
  • Loading branch information
SingleAccretion authored Sep 5, 2023
1 parent 9377dfd commit 7b16ecc
Show file tree
Hide file tree
Showing 23 changed files with 251 additions and 87 deletions.
8 changes: 5 additions & 3 deletions cranelift/codegen/src/isa/aarch64/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -584,7 +584,7 @@ impl ABIMachineSpec for AArch64MachineDeps {
insts
}

fn gen_prologue_frame_setup(flags: &settings::Flags) -> SmallInstVec<Inst> {
fn gen_prologue_frame_setup(flags: &settings::Flags) -> (SmallInstVec<Inst>, u32) {
let mut insts = SmallVec::new();

// stp fp (x29), lr (x30), [sp, #-16]!
Expand All @@ -597,10 +597,11 @@ impl ABIMachineSpec for AArch64MachineDeps {
flags: MemFlags::trusted(),
});

let setup_area_size = 16; // FP, LR
if flags.unwind_info() {
insts.push(Inst::Unwind {
inst: UnwindInst::PushFrameRegs {
offset_upward_to_caller_sp: 16, // FP, LR
offset_upward_to_caller_sp: setup_area_size,
},
});
}
Expand All @@ -617,7 +618,8 @@ impl ABIMachineSpec for AArch64MachineDeps {
shift12: false,
},
});
insts

(insts, setup_area_size)
}

fn gen_epilogue_frame_restore(_: &settings::Flags) -> SmallInstVec<Inst> {
Expand Down
4 changes: 2 additions & 2 deletions cranelift/codegen/src/isa/aarch64/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,10 +132,10 @@ impl TargetIsa for AArch64Backend {
fn emit_unwind_info(
&self,
result: &CompiledCode,
kind: crate::machinst::UnwindInfoKind,
kind: crate::isa::unwind::UnwindInfoKind,
) -> CodegenResult<Option<crate::isa::unwind::UnwindInfo>> {
use crate::isa::unwind::UnwindInfo;
use crate::machinst::UnwindInfoKind;
use crate::isa::unwind::UnwindInfoKind;
Ok(match kind {
UnwindInfoKind::SystemV => {
let mapper = self::inst::unwind::systemv::RegisterMapper;
Expand Down
4 changes: 2 additions & 2 deletions cranelift/codegen/src/isa/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ pub use crate::isa::call_conv::CallConv;
use crate::flowgraph;
use crate::ir::{self, Function, Type};
#[cfg(feature = "unwind")]
use crate::isa::unwind::systemv::RegisterMappingError;
use crate::machinst::{CompiledCode, CompiledCodeStencil, TextSectionBuilder, UnwindInfoKind};
use crate::isa::unwind::{systemv::RegisterMappingError, UnwindInfoKind};
use crate::machinst::{CompiledCode, CompiledCodeStencil, TextSectionBuilder};
use crate::settings;
use crate::settings::SetResult;
use crate::CodegenResult;
Expand Down
9 changes: 6 additions & 3 deletions cranelift/codegen/src/isa/riscv64/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@ impl ABIMachineSpec for Riscv64MachineDeps {
}
}

fn gen_prologue_frame_setup(flags: &settings::Flags) -> SmallInstVec<Inst> {
fn gen_prologue_frame_setup(flags: &settings::Flags) -> (SmallInstVec<Self::I>, u32) {
// add sp,sp,-16 ;; alloc stack space for fp.
// sd ra,8(sp) ;; save ra.
// sd fp,0(sp) ;; store old fp.
Expand All @@ -358,10 +358,12 @@ impl ABIMachineSpec for Riscv64MachineDeps {
fp_reg(),
I64,
));

let setup_area_size = 16; // FP, LR
if flags.unwind_info() {
insts.push(Inst::Unwind {
inst: UnwindInst::PushFrameRegs {
offset_upward_to_caller_sp: 16, // FP, LR
offset_upward_to_caller_sp: setup_area_size,
},
});
}
Expand All @@ -370,7 +372,8 @@ impl ABIMachineSpec for Riscv64MachineDeps {
rm: stack_reg(),
ty: I64,
});
insts

(insts, setup_area_size)
}
/// reverse of gen_prologue_frame_setup.
fn gen_epilogue_frame_restore(_: &settings::Flags) -> SmallInstVec<Inst> {
Expand Down
4 changes: 2 additions & 2 deletions cranelift/codegen/src/isa/riscv64/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,10 +127,10 @@ impl TargetIsa for Riscv64Backend {
fn emit_unwind_info(
&self,
result: &CompiledCode,
kind: crate::machinst::UnwindInfoKind,
kind: crate::isa::unwind::UnwindInfoKind,
) -> CodegenResult<Option<crate::isa::unwind::UnwindInfo>> {
use crate::isa::unwind::UnwindInfo;
use crate::machinst::UnwindInfoKind;
use crate::isa::unwind::UnwindInfoKind;
Ok(match kind {
UnwindInfoKind::SystemV => {
let mapper = self::inst::unwind::systemv::RegisterMapper;
Expand Down
4 changes: 2 additions & 2 deletions cranelift/codegen/src/isa/s390x/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -556,8 +556,8 @@ impl ABIMachineSpec for S390xMachineDeps {
}
}

fn gen_prologue_frame_setup(_flags: &settings::Flags) -> SmallInstVec<Inst> {
SmallVec::new()
fn gen_prologue_frame_setup(_flags: &settings::Flags) -> (SmallInstVec<Inst>, u32) {
(SmallVec::new(), 0)
}

fn gen_epilogue_frame_restore(_flags: &settings::Flags) -> SmallInstVec<Inst> {
Expand Down
4 changes: 2 additions & 2 deletions cranelift/codegen/src/isa/s390x/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,10 +129,10 @@ impl TargetIsa for S390xBackend {
fn emit_unwind_info(
&self,
result: &CompiledCode,
kind: crate::machinst::UnwindInfoKind,
kind: crate::isa::unwind::UnwindInfoKind,
) -> CodegenResult<Option<crate::isa::unwind::UnwindInfo>> {
use crate::isa::unwind::UnwindInfo;
use crate::machinst::UnwindInfoKind;
use crate::isa::unwind::UnwindInfoKind;
Ok(match kind {
UnwindInfoKind::SystemV => {
let mapper = self::inst::unwind::systemv::RegisterMapper;
Expand Down
19 changes: 18 additions & 1 deletion cranelift/codegen/src/isa/unwind.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,23 @@ pub mod systemv;
#[cfg(feature = "unwind")]
pub mod winx64;

/// CFA-based unwind information used on SystemV.
pub type CfaUnwindInfo = systemv::UnwindInfo;

/// Expected unwind info type.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum UnwindInfoKind {
/// No unwind info.
None,
/// SystemV CIE/FDE unwind info.
#[cfg(feature = "unwind")]
SystemV,
/// Windows X64 Unwind info
#[cfg(feature = "unwind")]
Windows,
}

/// Represents unwind information for a single function.
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
Expand All @@ -21,7 +38,7 @@ pub enum UnwindInfo {
WindowsX64(winx64::UnwindInfo),
/// System V ABI unwind information.
#[cfg(feature = "unwind")]
SystemV(systemv::UnwindInfo),
SystemV(CfaUnwindInfo),
}

/// Unwind pseudoinstruction used in VCode backends: represents that
Expand Down
6 changes: 6 additions & 0 deletions cranelift/codegen/src/isa/unwind/systemv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,12 @@ pub struct UnwindInfo {
len: u32,
}

/// Offset from the caller's SP to CFA as we define it.
pub(crate) fn caller_sp_to_cfa_offset() -> u32 {
// Currently we define them to always be equal.
0
}

pub(crate) fn create_unwind_info_from_insts<MR: RegisterMapper<Reg>>(
insts: &[(CodeOffset, UnwindInst)],
code_len: usize,
Expand Down
8 changes: 5 additions & 3 deletions cranelift/codegen/src/isa/x64/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,7 @@ impl ABIMachineSpec for X64ABIMachineSpec {
}
}

fn gen_prologue_frame_setup(flags: &settings::Flags) -> SmallInstVec<Self::I> {
fn gen_prologue_frame_setup(flags: &settings::Flags) -> (SmallInstVec<Self::I>, u32) {
let r_rsp = regs::rsp();
let r_rbp = regs::rbp();
let w_rbp = Writable::from_reg(r_rbp);
Expand All @@ -453,18 +453,20 @@ impl ABIMachineSpec for X64ABIMachineSpec {
// RSP before the call will be 0 % 16. So here, it is 8 % 16.
insts.push(Inst::push64(RegMemImm::reg(r_rbp)));

let setup_area_size = 16; // RBP, return address
if flags.unwind_info() {
insts.push(Inst::Unwind {
inst: UnwindInst::PushFrameRegs {
offset_upward_to_caller_sp: 16, // RBP, return address
offset_upward_to_caller_sp: setup_area_size,
},
});
}

// `mov %rsp, %rbp`
// RSP is now 0 % 16
insts.push(Inst::mov_r_r(OperandSize::Size64, r_rsp, w_rbp));
insts

(insts, setup_area_size)
}

fn gen_epilogue_frame_restore(_: &settings::Flags) -> SmallInstVec<Self::I> {
Expand Down
4 changes: 2 additions & 2 deletions cranelift/codegen/src/isa/x64/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,10 +123,10 @@ impl TargetIsa for X64Backend {
fn emit_unwind_info(
&self,
result: &CompiledCode,
kind: crate::machinst::UnwindInfoKind,
kind: crate::isa::unwind::UnwindInfoKind,
) -> CodegenResult<Option<crate::isa::unwind::UnwindInfo>> {
use crate::isa::unwind::UnwindInfo;
use crate::machinst::UnwindInfoKind;
use crate::isa::unwind::UnwindInfoKind;
Ok(match kind {
UnwindInfoKind::SystemV => {
let mapper = self::inst::unwind::systemv::RegisterMapper;
Expand Down
45 changes: 30 additions & 15 deletions cranelift/codegen/src/machinst/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -522,8 +522,8 @@ pub trait ABIMachineSpec {

/// Generate the usual frame-setup sequence for this architecture: e.g.,
/// `push rbp / mov rbp, rsp` on x86-64, or `stp fp, lr, [sp, #-16]!` on
/// AArch64.
fn gen_prologue_frame_setup(flags: &settings::Flags) -> SmallInstVec<Self::I>;
/// AArch64. Return generated instructions and stack size of the setup area.
fn gen_prologue_frame_setup(flags: &settings::Flags) -> (SmallInstVec<Self::I>, u32);

/// Generate the usual frame-restore sequence for this architecture.
fn gen_epilogue_frame_restore(flags: &settings::Flags) -> SmallInstVec<Self::I>;
Expand Down Expand Up @@ -1021,6 +1021,9 @@ pub struct Callee<M: ABIMachineSpec> {
/// Storage allocated for the fixed part of the stack frame. This is
/// usually the same as the total frame size below.
fixed_frame_storage_size: u32,
/// Size of the area between the FP as defined in the prolog and caller's SP.
/// It will usually contain the saved FP/LR pair.
frame_setup_area_size: u32,
/// "Total frame size", as defined by "distance between FP and nominal SP".
/// Some items are pushed below nominal SP, so the function may actually use
/// more stack than this would otherwise imply. It is simply the initial
Expand Down Expand Up @@ -1185,6 +1188,7 @@ impl<M: ABIMachineSpec> Callee<M> {
clobbered: vec![],
spillslots: None,
fixed_frame_storage_size: 0,
frame_setup_area_size: 0,
total_frame_size: None,
ret_area_ptr: None,
arg_temp_reg: vec![],
Expand Down Expand Up @@ -1760,10 +1764,7 @@ impl<M: ABIMachineSpec> Callee<M> {
ty: Type,
into_regs: ValueRegs<Writable<Reg>>,
) -> SmallInstVec<M::I> {
// Offset from beginning of spillslot area, which is at nominal SP + stackslots_size.
let islot = slot.index() as i64;
let spill_off = islot * M::word_bytes() as i64;
let sp_off = self.stackslots_size as i64 + spill_off;
let sp_off = self.get_spillslot_offset(slot);
trace!("load_spillslot: slot {:?} -> sp_off {}", slot, sp_off);

gen_load_stack_multi::<M>(StackAMode::NominalSPOffset(sp_off, ty), into_regs, ty)
Expand All @@ -1776,10 +1777,7 @@ impl<M: ABIMachineSpec> Callee<M> {
ty: Type,
from_regs: ValueRegs<Reg>,
) -> SmallInstVec<M::I> {
// Offset from beginning of spillslot area, which is at nominal SP + stackslots_size.
let islot = slot.index() as i64;
let spill_off = islot * M::word_bytes() as i64;
let sp_off = self.stackslots_size as i64 + spill_off;
let sp_off = self.get_spillslot_offset(slot);
trace!("store_spillslot: slot {:?} -> sp_off {}", slot, sp_off);

gen_store_stack_multi::<M>(StackAMode::NominalSPOffset(sp_off, ty), from_regs, ty)
Expand Down Expand Up @@ -1890,8 +1888,9 @@ impl<M: ABIMachineSpec> Callee<M> {
);

if self.setup_frame {
// set up frame
insts.extend(M::gen_prologue_frame_setup(&self.flags).into_iter());
let (setup_insts, setup_area_size) = M::gen_prologue_frame_setup(&self.flags);
insts.extend(setup_insts.into_iter());
self.frame_setup_area_size = setup_area_size;
}

// Leaf functions with zero stack don't need a stack check if one's
Expand Down Expand Up @@ -1992,14 +1991,20 @@ impl<M: ABIMachineSpec> Callee<M> {
}

/// Returns the full frame size for the given function, after prologue
/// emission has run. This comprises the spill slots and stack-storage slots
/// (but not storage for clobbered callee-save registers, arguments pushed
/// at callsites within this function, or other ephemeral pushes).
/// emission has run. This comprises the spill slots and stack-storage
/// slots as well as storage for clobbered callee-save registers, but
/// not arguments arguments pushed at callsites within this function,
/// or other ephemeral pushes.
pub fn frame_size(&self) -> u32 {
self.total_frame_size
.expect("frame size not computed before prologue generation")
}

/// Returns offset from the nominal SP to caller's SP.
pub fn nominal_sp_to_caller_sp_offset(&self) -> u32 {
self.frame_size() + self.frame_setup_area_size
}

/// Returns the size of arguments expected on the stack.
pub fn stack_args_size(&self, sigs: &SigSet) -> u32 {
sigs[self.sig].sized_stack_arg_space
Expand All @@ -2020,6 +2025,16 @@ impl<M: ABIMachineSpec> Callee<M> {
M::get_number_of_spillslots_for_value(rc, max, &self.isa_flags)
}

/// Get the spill slot offset relative to nominal SP.
pub fn get_spillslot_offset(&self, slot: SpillSlot) -> i64 {
// Offset from beginning of spillslot area, which is at nominal SP + stackslots_size.
let islot = slot.index() as i64;
let spill_off = islot * M::word_bytes() as i64;
let sp_off = self.stackslots_size as i64 + spill_off;

sp_off
}

/// Generate a spill.
pub fn gen_spill(&self, to_slot: SpillSlot, from_reg: RealReg) -> M::I {
let ty = M::I::canonical_type_for_rc(Reg::from(from_reg).class());
Expand Down
28 changes: 14 additions & 14 deletions cranelift/codegen/src/machinst/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -482,10 +482,24 @@ impl CompiledCode {
&self,
isa: &dyn crate::isa::TargetIsa,
) -> CodegenResult<Option<crate::isa::unwind::UnwindInfo>> {
use crate::isa::unwind::UnwindInfoKind;
let unwind_info_kind = match isa.triple().operating_system {
target_lexicon::OperatingSystem::Windows => UnwindInfoKind::Windows,
_ => UnwindInfoKind::SystemV,
};
self.create_unwind_info_of_kind(isa, unwind_info_kind)
}

/// Creates unwind information for the function using the supplied
/// "kind". Supports cross-OS (but not cross-arch) generation.
///
/// Returns `None` if the function has no unwind information.
#[cfg(feature = "unwind")]
pub fn create_unwind_info_of_kind(
&self,
isa: &dyn crate::isa::TargetIsa,
unwind_info_kind: crate::isa::unwind::UnwindInfoKind,
) -> CodegenResult<Option<crate::isa::unwind::UnwindInfo>> {
isa.emit_unwind_info(self, unwind_info_kind)
}
}
Expand Down Expand Up @@ -537,17 +551,3 @@ pub trait TextSectionBuilder {
/// the bytes of the text section.
fn finish(&mut self, ctrl_plane: &mut ControlPlane) -> Vec<u8>;
}

/// Expected unwind info type.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum UnwindInfoKind {
/// No unwind info.
None,
/// SystemV CIE/FDE unwind info.
#[cfg(feature = "unwind")]
SystemV,
/// Windows X64 Unwind info
#[cfg(feature = "unwind")]
Windows,
}
Loading

0 comments on commit 7b16ecc

Please sign in to comment.