diff --git a/cranelift/codegen/src/isa/aarch64/abi.rs b/cranelift/codegen/src/isa/aarch64/abi.rs index 320f7d5bcc14..15d9d6bccc33 100644 --- a/cranelift/codegen/src/isa/aarch64/abi.rs +++ b/cranelift/codegen/src/isa/aarch64/abi.rs @@ -584,7 +584,7 @@ impl ABIMachineSpec for AArch64MachineDeps { insts } - fn gen_prologue_frame_setup(flags: &settings::Flags) -> SmallInstVec { + fn gen_prologue_frame_setup(flags: &settings::Flags) -> (SmallInstVec, u32) { let mut insts = SmallVec::new(); // stp fp (x29), lr (x30), [sp, #-16]! @@ -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, }, }); } @@ -617,7 +618,8 @@ impl ABIMachineSpec for AArch64MachineDeps { shift12: false, }, }); - insts + + (insts, setup_area_size) } fn gen_epilogue_frame_restore(_: &settings::Flags) -> SmallInstVec { diff --git a/cranelift/codegen/src/isa/aarch64/mod.rs b/cranelift/codegen/src/isa/aarch64/mod.rs index b6d737be267a..930e3e26f3c6 100644 --- a/cranelift/codegen/src/isa/aarch64/mod.rs +++ b/cranelift/codegen/src/isa/aarch64/mod.rs @@ -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> { 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; diff --git a/cranelift/codegen/src/isa/mod.rs b/cranelift/codegen/src/isa/mod.rs index 860fcbd24e14..05c3b0bef3c6 100644 --- a/cranelift/codegen/src/isa/mod.rs +++ b/cranelift/codegen/src/isa/mod.rs @@ -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; diff --git a/cranelift/codegen/src/isa/riscv64/abi.rs b/cranelift/codegen/src/isa/riscv64/abi.rs index 3009a1e6d956..aeffe501dc3d 100644 --- a/cranelift/codegen/src/isa/riscv64/abi.rs +++ b/cranelift/codegen/src/isa/riscv64/abi.rs @@ -341,7 +341,7 @@ impl ABIMachineSpec for Riscv64MachineDeps { } } - fn gen_prologue_frame_setup(flags: &settings::Flags) -> SmallInstVec { + fn gen_prologue_frame_setup(flags: &settings::Flags) -> (SmallInstVec, u32) { // add sp,sp,-16 ;; alloc stack space for fp. // sd ra,8(sp) ;; save ra. // sd fp,0(sp) ;; store old fp. @@ -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, }, }); } @@ -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 { diff --git a/cranelift/codegen/src/isa/riscv64/mod.rs b/cranelift/codegen/src/isa/riscv64/mod.rs index 39682ba52115..58ba51b14626 100644 --- a/cranelift/codegen/src/isa/riscv64/mod.rs +++ b/cranelift/codegen/src/isa/riscv64/mod.rs @@ -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> { 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; diff --git a/cranelift/codegen/src/isa/s390x/abi.rs b/cranelift/codegen/src/isa/s390x/abi.rs index fdfae057eefb..5295633777cc 100644 --- a/cranelift/codegen/src/isa/s390x/abi.rs +++ b/cranelift/codegen/src/isa/s390x/abi.rs @@ -556,8 +556,8 @@ impl ABIMachineSpec for S390xMachineDeps { } } - fn gen_prologue_frame_setup(_flags: &settings::Flags) -> SmallInstVec { - SmallVec::new() + fn gen_prologue_frame_setup(_flags: &settings::Flags) -> (SmallInstVec, u32) { + (SmallVec::new(), 0) } fn gen_epilogue_frame_restore(_flags: &settings::Flags) -> SmallInstVec { diff --git a/cranelift/codegen/src/isa/s390x/mod.rs b/cranelift/codegen/src/isa/s390x/mod.rs index fc791d4fb866..1c7bdf1a3b37 100644 --- a/cranelift/codegen/src/isa/s390x/mod.rs +++ b/cranelift/codegen/src/isa/s390x/mod.rs @@ -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> { 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; diff --git a/cranelift/codegen/src/isa/unwind.rs b/cranelift/codegen/src/isa/unwind.rs index b5269b32393b..d357b64fad6e 100644 --- a/cranelift/codegen/src/isa/unwind.rs +++ b/cranelift/codegen/src/isa/unwind.rs @@ -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))] @@ -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 diff --git a/cranelift/codegen/src/isa/unwind/systemv.rs b/cranelift/codegen/src/isa/unwind/systemv.rs index 1e5ae93721ca..06e8633ef30e 100644 --- a/cranelift/codegen/src/isa/unwind/systemv.rs +++ b/cranelift/codegen/src/isa/unwind/systemv.rs @@ -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>( insts: &[(CodeOffset, UnwindInst)], code_len: usize, diff --git a/cranelift/codegen/src/isa/x64/abi.rs b/cranelift/codegen/src/isa/x64/abi.rs index 947cb0d76dac..0afc52ce4294 100644 --- a/cranelift/codegen/src/isa/x64/abi.rs +++ b/cranelift/codegen/src/isa/x64/abi.rs @@ -444,7 +444,7 @@ impl ABIMachineSpec for X64ABIMachineSpec { } } - fn gen_prologue_frame_setup(flags: &settings::Flags) -> SmallInstVec { + fn gen_prologue_frame_setup(flags: &settings::Flags) -> (SmallInstVec, u32) { let r_rsp = regs::rsp(); let r_rbp = regs::rbp(); let w_rbp = Writable::from_reg(r_rbp); @@ -453,10 +453,11 @@ 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, }, }); } @@ -464,7 +465,8 @@ impl ABIMachineSpec for X64ABIMachineSpec { // `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 { diff --git a/cranelift/codegen/src/isa/x64/mod.rs b/cranelift/codegen/src/isa/x64/mod.rs index 3429b26e1b64..9a5c6b19fe8c 100644 --- a/cranelift/codegen/src/isa/x64/mod.rs +++ b/cranelift/codegen/src/isa/x64/mod.rs @@ -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> { 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; diff --git a/cranelift/codegen/src/machinst/abi.rs b/cranelift/codegen/src/machinst/abi.rs index 2511e1994904..950096511d9b 100644 --- a/cranelift/codegen/src/machinst/abi.rs +++ b/cranelift/codegen/src/machinst/abi.rs @@ -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; + /// AArch64. Return generated instructions and stack size of the setup area. + fn gen_prologue_frame_setup(flags: &settings::Flags) -> (SmallInstVec, u32); /// Generate the usual frame-restore sequence for this architecture. fn gen_epilogue_frame_restore(flags: &settings::Flags) -> SmallInstVec; @@ -1021,6 +1021,9 @@ pub struct Callee { /// 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 @@ -1185,6 +1188,7 @@ impl Callee { 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![], @@ -1760,10 +1764,7 @@ impl Callee { ty: Type, into_regs: ValueRegs>, ) -> SmallInstVec { - // 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::(StackAMode::NominalSPOffset(sp_off, ty), into_regs, ty) @@ -1776,10 +1777,7 @@ impl Callee { ty: Type, from_regs: ValueRegs, ) -> SmallInstVec { - // 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::(StackAMode::NominalSPOffset(sp_off, ty), from_regs, ty) @@ -1890,8 +1888,9 @@ impl Callee { ); 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 @@ -1992,14 +1991,20 @@ impl Callee { } /// 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 @@ -2020,6 +2025,16 @@ impl Callee { 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()); diff --git a/cranelift/codegen/src/machinst/mod.rs b/cranelift/codegen/src/machinst/mod.rs index b39a4c85989f..fd08b6be7988 100644 --- a/cranelift/codegen/src/machinst/mod.rs +++ b/cranelift/codegen/src/machinst/mod.rs @@ -482,10 +482,24 @@ impl CompiledCode { &self, isa: &dyn crate::isa::TargetIsa, ) -> CodegenResult> { + 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> { isa.emit_unwind_info(self, unwind_info_kind) } } @@ -537,17 +551,3 @@ pub trait TextSectionBuilder { /// the bytes of the text section. fn finish(&mut self, ctrl_plane: &mut ControlPlane) -> Vec; } - -/// 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, -} diff --git a/cranelift/codegen/src/machinst/vcode.rs b/cranelift/codegen/src/machinst/vcode.rs index 4d2168f65617..b0a8d58af14a 100644 --- a/cranelift/codegen/src/machinst/vcode.rs +++ b/cranelift/codegen/src/machinst/vcode.rs @@ -1191,16 +1191,13 @@ impl VCode { let loc = if let Some(preg) = alloc.as_reg() { LabelValueLoc::Reg(Reg::from(preg)) } else { - // We can't translate spillslot locations at the - // moment because ValueLabelLoc requires an - // instantaneous SP offset, and this can *change* - // within the range we have here because of callsites - // adjusting SP temporarily. To avoid the complexity - // of accurately plumbing through nominal-SP - // adjustment sites, we just omit debug info for - // values that are spilled. Not ideal, but debug info - // is best-effort. - continue; + let slot = alloc.as_stack().unwrap(); + let sp_offset = self.abi.get_spillslot_offset(slot); + let sp_to_caller_sp_offset = self.abi.nominal_sp_to_caller_sp_offset(); + let caller_sp_to_cfa_offset = + crate::isa::unwind::systemv::caller_sp_to_cfa_offset(); + let cfa_to_sp_offset = -((sp_to_caller_sp_offset + caller_sp_to_cfa_offset) as i64); + LabelValueLoc::CFAOffset(cfa_to_sp_offset + sp_offset) }; // ValueLocRanges are recorded by *instruction-end @@ -1220,6 +1217,21 @@ impl VCode { // byte further to be sure to include it. let end = to_offset + 1; + // Coalesce adjacent ranges that for the same location + // to minimize output size here and for the consumers. + if let Some(last_loc_range) = ranges.last_mut() { + if last_loc_range.loc == loc && last_loc_range.end == start { + trace!( + "Extending debug range for VL{} in {:?} to {}", + label, + loc, + end + ); + last_loc_range.end = end; + continue; + } + } + trace!( "Recording debug range for VL{} in {:?}: [Inst {}..Inst {}) [{}..{})", label, diff --git a/cranelift/codegen/src/value_label.rs b/cranelift/codegen/src/value_label.rs index fa45bbb8d2a8..e388b8aa1948 100644 --- a/cranelift/codegen/src/value_label.rs +++ b/cranelift/codegen/src/value_label.rs @@ -22,10 +22,10 @@ pub struct ValueLocRange { #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] pub enum LabelValueLoc { - /// New-backend Reg. + /// Register. Reg(Reg), - /// New-backend offset from stack pointer. - SPOffset(i64), + /// Offset from the Canonical Frame Address (aka CFA). + CFAOffset(i64), } /// Resulting map of Value labels and their ranges/locations. diff --git a/crates/cranelift-shared/src/compiled_function.rs b/crates/cranelift-shared/src/compiled_function.rs index d9501c4da433..949c276c3b13 100644 --- a/crates/cranelift-shared/src/compiled_function.rs +++ b/crates/cranelift-shared/src/compiled_function.rs @@ -1,7 +1,7 @@ use crate::{mach_reloc_to_reloc, mach_trap_to_trap, Relocation}; use cranelift_codegen::{ - ir, ir::UserExternalNameRef, isa::unwind::UnwindInfo, Final, MachBufferFinalized, MachSrcLoc, - ValueLabelsRanges, + ir, ir::UserExternalNameRef, isa::unwind::CfaUnwindInfo, isa::unwind::UnwindInfo, Final, + MachBufferFinalized, MachSrcLoc, ValueLabelsRanges, }; use wasmtime_environ::{FilePos, InstructionAddressMap, TrapInformation}; @@ -46,6 +46,8 @@ pub struct CompiledFunctionMetadata { pub address_map: FunctionAddressMap, /// The unwind information. pub unwind_info: Option, + /// CFA-based unwind information for DWARF debugging support. + pub cfa_unwind_info: Option, /// Mapping of value labels and their locations. pub value_labels_ranges: ValueLabelsRanges, /// Allocated stack slots. @@ -154,6 +156,11 @@ where self.metadata.unwind_info = Some(unwind); } + /// Set the CFA-based unwind info in the function's metadata. + pub fn set_cfa_unwind_info(&mut self, unwind: CfaUnwindInfo) { + self.metadata.cfa_unwind_info = Some(unwind); + } + /// Set the sized stack slots. pub fn set_sized_stack_slots(&mut self, slots: ir::StackSlots) { self.metadata.sized_stack_slots = slots; diff --git a/crates/cranelift/src/compiler.rs b/crates/cranelift/src/compiler.rs index 3f4497efe98b..9bc954a4cbef 100644 --- a/crates/cranelift/src/compiler.rs +++ b/crates/cranelift/src/compiler.rs @@ -6,7 +6,10 @@ use anyhow::{Context as _, Result}; use cranelift_codegen::ir::{ self, InstBuilder, MemFlags, UserExternalName, UserExternalNameRef, UserFuncName, Value, }; -use cranelift_codegen::isa::{OwnedTargetIsa, TargetIsa}; +use cranelift_codegen::isa::{ + unwind::{UnwindInfo, UnwindInfoKind}, + OwnedTargetIsa, TargetIsa, +}; use cranelift_codegen::print_errors::pretty_error; use cranelift_codegen::Context; use cranelift_codegen::{CompiledCode, MachStackMap}; @@ -1052,13 +1055,6 @@ impl FunctionCompiler<'_> { ); } - if body_and_tunables - .map(|(_, t)| t.generate_native_debuginfo) - .unwrap_or(false) - { - compiled_function.set_value_labels_ranges(compiled_code.value_labels_ranges.clone()); - } - if isa.flags().unwind_info() { let unwind = compiled_code .create_unwind_info(isa) @@ -1069,6 +1065,27 @@ impl FunctionCompiler<'_> { } } + if body_and_tunables + .map(|(_, t)| t.generate_native_debuginfo) + .unwrap_or(false) + { + compiled_function.set_value_labels_ranges(compiled_code.value_labels_ranges.clone()); + + // DWARF debugging needs the CFA-based unwind information even on Windows. + if !matches!( + compiled_function.metadata().unwind_info, + Some(UnwindInfo::SystemV(_)) + ) { + let cfa_unwind = compiled_code + .create_unwind_info_of_kind(isa, UnwindInfoKind::SystemV) + .map_err(|error| CompileError::Codegen(pretty_error(&context.func, error)))?; + + if let Some(UnwindInfo::SystemV(cfa_unwind_info)) = cfa_unwind { + compiled_function.set_cfa_unwind_info(cfa_unwind_info); + } + } + } + let stack_maps = mach_stack_maps_to_stack_maps(compiled_code.buffer.stack_maps()); compiled_function .set_sized_stack_slots(std::mem::take(&mut context.func.sized_stack_slots)); diff --git a/crates/cranelift/src/debug/transform/attr.rs b/crates/cranelift/src/debug/transform/attr.rs index d4357f1db88d..13bf62e9fba8 100644 --- a/crates/cranelift/src/debug/transform/attr.rs +++ b/crates/cranelift/src/debug/transform/attr.rs @@ -82,9 +82,6 @@ where AttributeValue::RangeListsRef(_) if attr.name() == gimli::DW_AT_ranges => { continue; } - AttributeValue::Exprloc(_) if attr.name() == gimli::DW_AT_frame_base => { - continue; - } AttributeValue::DebugAddrBase(_) | AttributeValue::DebugStrOffsetsBase(_) => { continue; } @@ -203,6 +200,13 @@ where let list_id = out_unit.locations.add(write::LocationList(result.unwrap())); write::AttributeValue::LocationListRef(list_id) } + AttributeValue::Exprloc(_) if attr.name() == gimli::DW_AT_frame_base => { + // We do not really "rewrite" the frame base so much as replace it outright. + // References to it through the DW_OP_fbreg opcode will be expanded below. + let mut cfa = write::Expression::new(); + cfa.op(gimli::DW_OP_call_frame_cfa); + write::AttributeValue::Exprloc(cfa) + } AttributeValue::Exprloc(ref expr) => { let frame_base = if let FileAttributeContext::Children { frame_base, .. } = file_context { diff --git a/crates/cranelift/src/debug/transform/expression.rs b/crates/cranelift/src/debug/transform/expression.rs index a0428900c894..aae55791c4bc 100644 --- a/crates/cranelift/src/debug/transform/expression.rs +++ b/crates/cranelift/src/debug/transform/expression.rs @@ -6,7 +6,7 @@ use cranelift_codegen::isa::TargetIsa; use cranelift_codegen::LabelValueLoc; use cranelift_codegen::ValueLabelsRanges; use cranelift_wasm::get_vmctx_value_label; -use gimli::{self, write, Expression, Operation, Reader, ReaderOffset, X86_64}; +use gimli::{self, write, Expression, Operation, Reader, ReaderOffset}; use std::cmp::PartialEq; use std::collections::{HashMap, HashSet}; use std::hash::{Hash, Hasher}; @@ -146,9 +146,9 @@ fn translate_loc( } Some(writer.into_vec()) } - LabelValueLoc::SPOffset(off) => { + LabelValueLoc::CFAOffset(off) => { let mut writer = ExpressionWriter::new(); - writer.write_op_breg(X86_64::RSP.0)?; + writer.write_op(gimli::constants::DW_OP_fbreg)?; writer.write_sleb128(off)?; if !add_stack_value { writer.write_op(gimli::constants::DW_OP_deref)?; @@ -178,8 +178,8 @@ fn append_memory_deref( }; writer.write_sleb128(memory_offset)?; } - LabelValueLoc::SPOffset(off) => { - writer.write_op_breg(X86_64::RSP.0)?; + LabelValueLoc::CFAOffset(off) => { + writer.write_op(gimli::constants::DW_OP_fbreg)?; writer.write_sleb128(off)?; writer.write_op(gimli::constants::DW_OP_deref)?; writer.write_op(gimli::constants::DW_OP_consts)?; @@ -1169,7 +1169,7 @@ mod tests { value_ranges.insert( value_0, vec![ValueLocRange { - loc: LabelValueLoc::SPOffset(0), + loc: LabelValueLoc::CFAOffset(0), start: 0, end: 25, }], @@ -1177,7 +1177,7 @@ mod tests { value_ranges.insert( value_1, vec![ValueLocRange { - loc: LabelValueLoc::SPOffset(0), + loc: LabelValueLoc::CFAOffset(0), start: 5, end: 30, }], @@ -1186,12 +1186,12 @@ mod tests { value_2, vec![ ValueLocRange { - loc: LabelValueLoc::SPOffset(0), + loc: LabelValueLoc::CFAOffset(0), start: 0, end: 10, }, ValueLocRange { - loc: LabelValueLoc::SPOffset(0), + loc: LabelValueLoc::CFAOffset(0), start: 20, end: 30, }, diff --git a/crates/cranelift/src/debug/write_debuginfo.rs b/crates/cranelift/src/debug/write_debuginfo.rs index f54c7d6f0de3..e5e6269f8e8d 100644 --- a/crates/cranelift/src/debug/write_debuginfo.rs +++ b/crates/cranelift/src/debug/write_debuginfo.rs @@ -2,7 +2,10 @@ pub use crate::debug::transform::transform_dwarf; use crate::debug::ModuleMemoryOffset; use crate::CompiledFunctionsMetadata; use cranelift_codegen::ir::Endianness; -use cranelift_codegen::isa::{unwind::UnwindInfo, TargetIsa}; +use cranelift_codegen::isa::{ + unwind::{CfaUnwindInfo, UnwindInfo}, + TargetIsa, +}; use cranelift_entity::EntityRef; use gimli::write::{Address, Dwarf, EndianVec, FrameTable, Result, Sections, Writer}; use gimli::{RunTimeEndian, SectionId}; @@ -146,7 +149,18 @@ fn create_frame_table<'a>( let cie_id = table.add_cie(isa.create_systemv_cie()?); for (i, metadata) in funcs { + // The CFA-based unwind info will either be natively present, or we + // have generated it and placed into the "cfa_unwind_info" auxiliary + // field. We shouldn't emit both, though, it'd be wasteful. + let mut unwind_info: Option<&CfaUnwindInfo> = None; if let Some(UnwindInfo::SystemV(info)) = &metadata.unwind_info { + debug_assert!(metadata.cfa_unwind_info.is_none()); + unwind_info = Some(info); + } else if let Some(info) = &metadata.cfa_unwind_info { + unwind_info = Some(info); + } + + if let Some(info) = unwind_info { table.add_fde( cie_id, info.to_fde(Address::Symbol { diff --git a/tests/all/debug/lldb.rs b/tests/all/debug/lldb.rs index abc4eceef80e..c8a9d9bc6f73 100644 --- a/tests/all/debug/lldb.rs +++ b/tests/all/debug/lldb.rs @@ -201,3 +201,52 @@ check: exited with status )?; Ok(()) } + +#[test] +#[ignore] +#[cfg(all( + any(target_os = "linux", target_os = "macos"), + target_pointer_width = "64" +))] +pub fn test_spilled_frame_base_is_accessible() -> Result<()> { + let output = lldb_with_script( + &[ + "--disable-cache", + "-g", + "--opt-level", + "0", + "tests/all/debug/testsuite/spilled_frame_base.wasm", + ], + r#"b spilled_frame_base.c:8 +r +fr v i +n +fr v i +n +fr v i +n +fr v i +n +fr v i +n +fr v i +c +"#, + )?; + + // Check that if the frame base (shadow frame pointer) local + // is spilled, we can still read locals that reference it. + check_lldb_output( + &output, + r#" +check: i = 0 +check: i = 1 +check: i = 1 +check: i = 1 +check: i = 1 +check: i = 1 +check: exited with status +"#, + )?; + Ok(()) +} diff --git a/tests/all/debug/testsuite/spilled_frame_base.c b/tests/all/debug/testsuite/spilled_frame_base.c new file mode 100644 index 000000000000..d0733681201a --- /dev/null +++ b/tests/all/debug/testsuite/spilled_frame_base.c @@ -0,0 +1,16 @@ +// Originally built using WASI SDK 20.0, "clang spilled_frame_base.c -o spilled_frame_base.wasm -g -target wasm32-wasi" +void func_11(int x, int y, int z, int a, int b, int c, int d, int e, int f, int g, int h) { } +void func_12(int x, int y, int z, int a, int b, int c, int d, int e, int f, int g, int h, int i) { } +void func_13(int x, int y, int z, int a, int b, int c, int d, int e, int f, int g, int h, int i, int j) { } +void func_14(int x, int y, int z, int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k) { } + +int main() { + int i = 1; + + func_11(55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65); + func_12(66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77); + func_13(78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90); + func_14(91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104); + + return i + i; +} diff --git a/tests/all/debug/testsuite/spilled_frame_base.wasm b/tests/all/debug/testsuite/spilled_frame_base.wasm new file mode 100644 index 000000000000..b741d007cf0b Binary files /dev/null and b/tests/all/debug/testsuite/spilled_frame_base.wasm differ