diff --git a/cranelift/codegen/src/context.rs b/cranelift/codegen/src/context.rs index 842a48ab21..eb4cdf26f6 100644 --- a/cranelift/codegen/src/context.rs +++ b/cranelift/codegen/src/context.rs @@ -253,6 +253,14 @@ impl Context { &self, isa: &dyn TargetIsa, ) -> CodegenResult> { + if self.mach_compile_result.is_some() { + return Ok(self + .mach_compile_result + .as_ref() + .unwrap() + .unwind_info + .clone()); + } isa.create_unwind_info(&self.func) } diff --git a/cranelift/codegen/src/isa/aarch64/inst/emit.rs b/cranelift/codegen/src/isa/aarch64/inst/emit.rs index 05e76ff606..a6d93d8064 100644 --- a/cranelift/codegen/src/isa/aarch64/inst/emit.rs +++ b/cranelift/codegen/src/isa/aarch64/inst/emit.rs @@ -481,6 +481,7 @@ impl MachInstEmitInfo for EmitInfo { impl MachInstEmit for Inst { type State = EmitState; type Info = EmitInfo; + type UnwindInfo = super::unwind::AArch64UnwindInfo; fn emit(&self, sink: &mut MachBuffer, emit_info: &Self::Info, state: &mut EmitState) { // N.B.: we *must* not exceed the "worst-case size" used to compute diff --git a/cranelift/codegen/src/isa/aarch64/inst/mod.rs b/cranelift/codegen/src/isa/aarch64/inst/mod.rs index 31a15d9580..687865ebab 100644 --- a/cranelift/codegen/src/isa/aarch64/inst/mod.rs +++ b/cranelift/codegen/src/isa/aarch64/inst/mod.rs @@ -29,6 +29,7 @@ pub mod args; pub use self::args::*; pub mod emit; pub use self::emit::*; +pub mod unwind; #[cfg(test)] mod emit_tests; diff --git a/cranelift/codegen/src/isa/aarch64/inst/unwind.rs b/cranelift/codegen/src/isa/aarch64/inst/unwind.rs new file mode 100644 index 0000000000..51aad46687 --- /dev/null +++ b/cranelift/codegen/src/isa/aarch64/inst/unwind.rs @@ -0,0 +1,15 @@ +use super::*; +use crate::isa::unwind::UnwindInfo; +use crate::result::CodegenResult; + +pub struct AArch64UnwindInfo; + +impl UnwindInfoGenerator for AArch64UnwindInfo { + fn create_unwind_info( + _context: UnwindInfoContext, + _kind: UnwindInfoKind, + ) -> CodegenResult> { + // TODO + Ok(None) + } +} diff --git a/cranelift/codegen/src/isa/aarch64/mod.rs b/cranelift/codegen/src/isa/aarch64/mod.rs index 9b3fb3e91b..987fc49a06 100644 --- a/cranelift/codegen/src/isa/aarch64/mod.rs +++ b/cranelift/codegen/src/isa/aarch64/mod.rs @@ -77,6 +77,7 @@ impl MachBackend for AArch64Backend { buffer, frame_size, disasm, + unwind_info: None, }) } diff --git a/cranelift/codegen/src/isa/arm32/inst/emit.rs b/cranelift/codegen/src/isa/arm32/inst/emit.rs index c671325c72..cb5b756501 100644 --- a/cranelift/codegen/src/isa/arm32/inst/emit.rs +++ b/cranelift/codegen/src/isa/arm32/inst/emit.rs @@ -274,6 +274,7 @@ impl MachInstEmitInfo for EmitInfo { impl MachInstEmit for Inst { type Info = EmitInfo; type State = EmitState; + type UnwindInfo = super::unwind::Arm32UnwindInfo; fn emit(&self, sink: &mut MachBuffer, emit_info: &Self::Info, state: &mut EmitState) { let start_off = sink.cur_offset(); diff --git a/cranelift/codegen/src/isa/arm32/inst/mod.rs b/cranelift/codegen/src/isa/arm32/inst/mod.rs index 78aa0d784f..ba34e3dfcf 100644 --- a/cranelift/codegen/src/isa/arm32/inst/mod.rs +++ b/cranelift/codegen/src/isa/arm32/inst/mod.rs @@ -22,6 +22,7 @@ mod emit; pub use self::emit::*; mod regs; pub use self::regs::*; +pub mod unwind; #[cfg(test)] mod emit_tests; diff --git a/cranelift/codegen/src/isa/arm32/inst/unwind.rs b/cranelift/codegen/src/isa/arm32/inst/unwind.rs new file mode 100644 index 0000000000..78109ed6c1 --- /dev/null +++ b/cranelift/codegen/src/isa/arm32/inst/unwind.rs @@ -0,0 +1,15 @@ +use super::*; +use crate::isa::unwind::UnwindInfo; +use crate::result::CodegenResult; + +pub struct Arm32UnwindInfo; + +impl UnwindInfoGenerator for Arm32UnwindInfo { + fn create_unwind_info( + _context: UnwindInfoContext, + _kind: UnwindInfoKind, + ) -> CodegenResult> { + // TODO + Ok(None) + } +} diff --git a/cranelift/codegen/src/isa/arm32/mod.rs b/cranelift/codegen/src/isa/arm32/mod.rs index 0aed5e3c9c..4b9701fd1d 100644 --- a/cranelift/codegen/src/isa/arm32/mod.rs +++ b/cranelift/codegen/src/isa/arm32/mod.rs @@ -73,6 +73,7 @@ impl MachBackend for Arm32Backend { buffer, frame_size, disasm, + unwind_info: None, }) } diff --git a/cranelift/codegen/src/isa/mod.rs b/cranelift/codegen/src/isa/mod.rs index 3e16ad471e..2e56c025d0 100644 --- a/cranelift/codegen/src/isa/mod.rs +++ b/cranelift/codegen/src/isa/mod.rs @@ -87,7 +87,6 @@ mod arm32; #[cfg(feature = "arm64")] pub(crate) mod aarch64; -#[cfg(feature = "unwind")] pub mod unwind; mod call_conv; diff --git a/cranelift/codegen/src/isa/unwind.rs b/cranelift/codegen/src/isa/unwind.rs index cc53fb6a3d..1d5324976a 100644 --- a/cranelift/codegen/src/isa/unwind.rs +++ b/cranelift/codegen/src/isa/unwind.rs @@ -2,16 +2,22 @@ #[cfg(feature = "enable-serde")] use serde::{Deserialize, Serialize}; +#[cfg(feature = "unwind")] pub mod systemv; + +#[cfg(feature = "unwind")] pub mod winx64; /// Represents unwind information for a single function. #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] +#[non_exhaustive] pub enum UnwindInfo { /// Windows x64 ABI unwind information. + #[cfg(feature = "unwind")] WindowsX64(winx64::UnwindInfo), /// System V ABI unwind information. + #[cfg(feature = "unwind")] SystemV(systemv::UnwindInfo), } diff --git a/cranelift/codegen/src/isa/unwind/systemv.rs b/cranelift/codegen/src/isa/unwind/systemv.rs index 70bac9fe4d..3a3f14ab36 100644 --- a/cranelift/codegen/src/isa/unwind/systemv.rs +++ b/cranelift/codegen/src/isa/unwind/systemv.rs @@ -1,6 +1,6 @@ //! System V ABI unwind information. -use crate::isa::{unwind::input, RegUnit}; +use crate::isa::unwind::input; use crate::result::{CodegenError, CodegenResult}; use alloc::vec::Vec; use gimli::write::{Address, FrameDescriptionEntry}; @@ -95,9 +95,9 @@ impl Into for CallFrameInstruction { } /// Maps UnwindInfo register to gimli's index space. -pub(crate) trait RegisterMapper { - /// Maps RegUnit. - fn map(&self, reg: RegUnit) -> Result; +pub(crate) trait RegisterMapper { + /// Maps Reg. + fn map(&self, reg: Reg) -> Result; /// Gets RSP in gimli's index space. fn rsp(&self) -> Register; } @@ -113,9 +113,9 @@ pub struct UnwindInfo { } impl UnwindInfo { - pub(crate) fn build<'b>( - unwind: input::UnwindInfo, - map_reg: &'b dyn RegisterMapper, + pub(crate) fn build<'b, Reg: PartialEq + Copy>( + unwind: input::UnwindInfo, + map_reg: &'b dyn RegisterMapper, ) -> CodegenResult { use input::UnwindCode; let mut builder = InstructionBuilder::new(unwind.word_size, map_reg); @@ -181,16 +181,16 @@ impl UnwindInfo { } } -struct InstructionBuilder<'a> { +struct InstructionBuilder<'a, Reg: PartialEq + Copy> { sp_offset: i32, - frame_register: Option, - saved_state: Option<(i32, Option)>, - map_reg: &'a dyn RegisterMapper, + frame_register: Option, + saved_state: Option<(i32, Option)>, + map_reg: &'a dyn RegisterMapper, instructions: Vec<(u32, CallFrameInstruction)>, } -impl<'a> InstructionBuilder<'a> { - fn new(word_size: u8, map_reg: &'a (dyn RegisterMapper + 'a)) -> Self { +impl<'a, Reg: PartialEq + Copy> InstructionBuilder<'a, Reg> { + fn new(word_size: u8, map_reg: &'a (dyn RegisterMapper + 'a)) -> Self { Self { sp_offset: word_size as i32, // CFA offset starts at word size offset to account for the return address on stack saved_state: None, @@ -200,7 +200,7 @@ impl<'a> InstructionBuilder<'a> { } } - fn save_reg(&mut self, offset: u32, reg: RegUnit) -> Result<(), RegisterMappingError> { + fn save_reg(&mut self, offset: u32, reg: Reg) -> Result<(), RegisterMappingError> { // Pushes in the prologue are register saves, so record an offset of the save self.instructions.push(( offset, @@ -261,7 +261,7 @@ impl<'a> InstructionBuilder<'a> { } } - fn set_cfa_reg(&mut self, offset: u32, reg: RegUnit) -> Result<(), RegisterMappingError> { + fn set_cfa_reg(&mut self, offset: u32, reg: Reg) -> Result<(), RegisterMappingError> { self.instructions.push(( offset, CallFrameInstruction::CfaRegister(self.map_reg.map(reg)?), @@ -270,7 +270,7 @@ impl<'a> InstructionBuilder<'a> { Ok(()) } - fn restore_reg(&mut self, offset: u32, reg: RegUnit) -> Result<(), RegisterMappingError> { + fn restore_reg(&mut self, offset: u32, reg: Reg) -> Result<(), RegisterMappingError> { // Update the CFA if this is the restore of the frame pointer register. if Some(reg) == self.frame_register { self.frame_register = None; diff --git a/cranelift/codegen/src/isa/x64/inst/mod.rs b/cranelift/codegen/src/isa/x64/inst/mod.rs index 1fe0de6941..80fe173130 100644 --- a/cranelift/codegen/src/isa/x64/inst/mod.rs +++ b/cranelift/codegen/src/isa/x64/inst/mod.rs @@ -20,6 +20,7 @@ mod emit; #[cfg(test)] mod emit_tests; pub mod regs; +pub mod unwind; use args::*; use regs::{create_reg_universe_systemv, show_ireg_sized}; @@ -2706,6 +2707,7 @@ impl MachInstEmitInfo for EmitInfo { impl MachInstEmit for Inst { type State = EmitState; type Info = EmitInfo; + type UnwindInfo = unwind::X64UnwindInfo; fn emit(&self, sink: &mut MachBuffer, info: &Self::Info, state: &mut Self::State) { emit::emit(self, sink, info, state); diff --git a/cranelift/codegen/src/isa/x64/inst/unwind.rs b/cranelift/codegen/src/isa/x64/inst/unwind.rs new file mode 100644 index 0000000000..75107f058c --- /dev/null +++ b/cranelift/codegen/src/isa/x64/inst/unwind.rs @@ -0,0 +1,36 @@ +use super::Inst; +use crate::isa::unwind::UnwindInfo; +use crate::machinst::{UnwindInfoContext, UnwindInfoGenerator, UnwindInfoKind}; +use crate::result::CodegenResult; + +#[cfg(feature = "unwind")] +pub use self::systemv::create_cie; + +#[cfg(feature = "unwind")] +mod systemv; + +pub struct X64UnwindInfo; + +impl UnwindInfoGenerator for X64UnwindInfo { + #[allow(unused_variables)] + fn create_unwind_info( + context: UnwindInfoContext, + kind: UnwindInfoKind, + ) -> CodegenResult> { + // Assumption: RBP is being used as the frame pointer for both calling conventions + // In the future, we should be omitting frame pointer as an optimization, so this will change + Ok(match kind { + #[cfg(feature = "unwind")] + UnwindInfoKind::SystemV => { + const WORD_SIZE: u8 = 8; + systemv::create_unwind_info(context, WORD_SIZE)?.map(UnwindInfo::SystemV) + } + #[cfg(feature = "unwind")] + UnwindInfoKind::Windows => { + //TODO winx64::create_unwind_info(context)?.map(|u| UnwindInfo::WindowsX64(u)) + panic!(); + } + UnwindInfoKind::None => None, + }) + } +} diff --git a/cranelift/codegen/src/isa/x64/inst/unwind/systemv.rs b/cranelift/codegen/src/isa/x64/inst/unwind/systemv.rs new file mode 100644 index 0000000000..8f940753d1 --- /dev/null +++ b/cranelift/codegen/src/isa/x64/inst/unwind/systemv.rs @@ -0,0 +1,307 @@ +//! Unwind information for System V ABI (x86-64). + +use crate::isa::unwind::systemv::{RegisterMappingError, UnwindInfo}; +use crate::isa::x64::inst::{ + args::{AluRmiROpcode, Amode, RegMemImm, SyntheticAmode}, + regs, Inst, +}; +use crate::machinst::UnwindInfoContext; +use crate::result::CodegenResult; +use alloc::vec::Vec; +use gimli::{write::CommonInformationEntry, Encoding, Format, Register, X86_64}; +use regalloc::{Reg, RegClass}; + +/// Creates a new x86-64 common information entry (CIE). +pub fn create_cie() -> CommonInformationEntry { + use gimli::write::CallFrameInstruction; + + let mut entry = CommonInformationEntry::new( + Encoding { + address_size: 8, + format: Format::Dwarf32, + version: 1, + }, + 1, // Code alignment factor + -8, // Data alignment factor + X86_64::RA, + ); + + // Every frame will start with the call frame address (CFA) at RSP+8 + // It is +8 to account for the push of the return address by the call instruction + entry.add_instruction(CallFrameInstruction::Cfa(X86_64::RSP, 8)); + + // Every frame will start with the return address at RSP (CFA-8 = RSP+8-8 = RSP) + entry.add_instruction(CallFrameInstruction::Offset(X86_64::RA, -8)); + + entry +} + +/// Map Cranelift registers to their corresponding Gimli registers. +pub fn map_reg(reg: Reg) -> Result { + // Mapping from https://github.com/bytecodealliance/cranelift/pull/902 by @iximeow + const X86_GP_REG_MAP: [gimli::Register; 16] = [ + X86_64::RAX, + X86_64::RCX, + X86_64::RDX, + X86_64::RBX, + X86_64::RSP, + X86_64::RBP, + X86_64::RSI, + X86_64::RDI, + X86_64::R8, + X86_64::R9, + X86_64::R10, + X86_64::R11, + X86_64::R12, + X86_64::R13, + X86_64::R14, + X86_64::R15, + ]; + const X86_XMM_REG_MAP: [gimli::Register; 16] = [ + X86_64::XMM0, + X86_64::XMM1, + X86_64::XMM2, + X86_64::XMM3, + X86_64::XMM4, + X86_64::XMM5, + X86_64::XMM6, + X86_64::XMM7, + X86_64::XMM8, + X86_64::XMM9, + X86_64::XMM10, + X86_64::XMM11, + X86_64::XMM12, + X86_64::XMM13, + X86_64::XMM14, + X86_64::XMM15, + ]; + + match reg.get_class() { + RegClass::I64 => { + // x86 GP registers have a weird mapping to DWARF registers, so we use a + // lookup table. + Ok(X86_GP_REG_MAP[reg.get_hw_encoding() as usize]) + } + RegClass::V128 => Ok(X86_XMM_REG_MAP[reg.get_hw_encoding() as usize]), + _ => Err(RegisterMappingError::UnsupportedRegisterBank("class?")), + } +} + +pub(crate) fn create_unwind_info( + context: UnwindInfoContext, + word_size: u8, +) -> CodegenResult> { + use crate::isa::unwind::input::{self, UnwindCode}; + let mut codes = Vec::new(); + + for i in context.prologue.clone() { + let i = i as usize; + let inst = &context.insts[i]; + let offset = context.insts_layout[i]; + + match inst { + Inst::Push64 { + src: RegMemImm::Reg { reg }, + } => { + codes.push(UnwindCode::StackAlloc { + offset, + size: word_size.into(), + }); + codes.push(UnwindCode::SaveRegister { + offset, + reg: *reg, + stack_offset: 0, + }); + } + Inst::MovRR { src, dst, .. } => { + if *src == regs::rsp() { + codes.push(UnwindCode::SetFramePointer { + offset, + reg: dst.to_reg(), + }); + } + } + Inst::AluRmiR { + is_64: true, + op: AluRmiROpcode::Sub, + src: RegMemImm::Imm { simm32 }, + dst, + .. + } if dst.to_reg() == regs::rsp() => { + let imm = *simm32; + codes.push(UnwindCode::StackAlloc { offset, size: imm }); + } + Inst::MovRM { + src, + dst: SyntheticAmode::Real(Amode::ImmReg { simm32, base }), + .. + } if *base == regs::rsp() => { + // `mov reg, imm(rsp)` + let imm = *simm32; + codes.push(UnwindCode::SaveRegister { + offset, + reg: *src, + stack_offset: imm, + }); + } + Inst::AluRmiR { + is_64: true, + op: AluRmiROpcode::Add, + src: RegMemImm::Imm { simm32 }, + dst, + .. + } if dst.to_reg() == regs::rsp() => { + let imm = *simm32; + codes.push(UnwindCode::StackDealloc { offset, size: imm }); + } + _ => {} + } + } + + let last_epilogue_end = context.len; + let epilogues_unwind_codes = context + .epilogues + .iter() + .map(|epilogue| { + let end = epilogue.end as usize - 1; + let end_offset = context.insts_layout[end]; + if end_offset == last_epilogue_end { + // Do not remember/restore for very last epilogue. + return vec![]; + } + + let start = epilogue.start as usize; + let offset = context.insts_layout[start]; + vec![ + UnwindCode::RememberState { offset }, + UnwindCode::RestoreState { offset: end_offset }, + ] + }) + .collect(); + + let prologue_size = context.insts_layout[context.prologue.end as usize]; + let unwind = input::UnwindInfo { + prologue_size, + prologue_unwind_codes: codes, + epilogues_unwind_codes, + function_size: context.len, + word_size, + }; + + struct RegisterMapper; + impl crate::isa::unwind::systemv::RegisterMapper for RegisterMapper { + fn map(&self, reg: Reg) -> Result { + Ok(map_reg(reg)?.0) + } + fn rsp(&self) -> u16 { + map_reg(regs::rsp()).unwrap().0 + } + } + let map = RegisterMapper; + + Ok(Some(UnwindInfo::build(unwind, &map)?)) +} + +#[cfg(test)] +mod tests { + use crate::cursor::{Cursor, FuncCursor}; + use crate::ir::{ + types, AbiParam, ExternalName, Function, InstBuilder, Signature, StackSlotData, + StackSlotKind, + }; + use crate::isa::{lookup, CallConv}; + use crate::settings::{builder, Flags}; + use crate::Context; + use gimli::write::Address; + use std::str::FromStr; + use target_lexicon::triple; + + #[test] + fn test_simple_func() { + let isa = lookup(triple!("x86_64")) + .expect("expect x86 ISA") + .finish(Flags::new(builder())); + + let mut context = Context::for_function(create_function( + CallConv::SystemV, + Some(StackSlotData::new(StackSlotKind::ExplicitSlot, 64)), + )); + + context.compile(&*isa).expect("expected compilation"); + + let fde = match context + .create_unwind_info(isa.as_ref()) + .expect("can create unwind info") + { + Some(crate::isa::unwind::UnwindInfo::SystemV(info)) => { + info.to_fde(Address::Constant(1234)) + } + _ => panic!("expected unwind information"), + }; + + assert_eq!(format!("{:?}", fde), "FrameDescriptionEntry { address: Constant(1234), length: 13, lsda: None, instructions: [(1, CfaOffset(16)), (1, Offset(Register(6), -16)), (4, CfaRegister(Register(6)))] }"); + } + + fn create_function(call_conv: CallConv, stack_slot: Option) -> Function { + let mut func = + Function::with_name_signature(ExternalName::user(0, 0), Signature::new(call_conv)); + + let block0 = func.dfg.make_block(); + let mut pos = FuncCursor::new(&mut func); + pos.insert_block(block0); + pos.ins().return_(&[]); + + if let Some(stack_slot) = stack_slot { + func.stack_slots.push(stack_slot); + } + + func + } + + #[test] + fn test_multi_return_func() { + let isa = lookup(triple!("x86_64")) + .expect("expect x86 ISA") + .finish(Flags::new(builder())); + + let mut context = Context::for_function(create_multi_return_function(CallConv::SystemV)); + + context.compile(&*isa).expect("expected compilation"); + + let fde = match context + .create_unwind_info(isa.as_ref()) + .expect("can create unwind info") + { + Some(crate::isa::unwind::UnwindInfo::SystemV(info)) => { + info.to_fde(Address::Constant(4321)) + } + _ => panic!("expected unwind information"), + }; + + assert_eq!(format!("{:?}", fde), "FrameDescriptionEntry { address: Constant(4321), length: 23, lsda: None, instructions: [(1, CfaOffset(16)), (1, Offset(Register(6), -16)), (4, CfaRegister(Register(6))), (16, RememberState), (18, RestoreState)] }"); + } + + fn create_multi_return_function(call_conv: CallConv) -> Function { + let mut sig = Signature::new(call_conv); + sig.params.push(AbiParam::new(types::I32)); + let mut func = Function::with_name_signature(ExternalName::user(0, 0), sig); + + let block0 = func.dfg.make_block(); + let v0 = func.dfg.append_block_param(block0, types::I32); + let block1 = func.dfg.make_block(); + let block2 = func.dfg.make_block(); + + let mut pos = FuncCursor::new(&mut func); + pos.insert_block(block0); + pos.ins().brnz(v0, block2, &[]); + pos.ins().jump(block1, &[]); + + pos.insert_block(block1); + pos.ins().return_(&[]); + + pos.insert_block(block2); + pos.ins().return_(&[]); + + func + } +} diff --git a/cranelift/codegen/src/isa/x64/mod.rs b/cranelift/codegen/src/isa/x64/mod.rs index 366765c6aa..969447e923 100644 --- a/cranelift/codegen/src/isa/x64/mod.rs +++ b/cranelift/codegen/src/isa/x64/mod.rs @@ -59,6 +59,7 @@ impl MachBackend for X64Backend { let buffer = vcode.emit(); let buffer = buffer.finish(); let frame_size = vcode.frame_size(); + let unwind_info = vcode.unwind_info()?; let disasm = if want_disasm { Some(vcode.show_rru(Some(&create_reg_universe_systemv(flags)))) @@ -70,6 +71,7 @@ impl MachBackend for X64Backend { buffer, frame_size, disasm, + unwind_info, }) } @@ -100,6 +102,12 @@ impl MachBackend for X64Backend { // underflow of a subtract (carry is borrow for subtract). IntCC::UnsignedGreaterThanOrEqual } + + #[cfg(feature = "unwind")] + fn create_systemv_cie(&self) -> Option { + // By default, an ISA cannot create a System V CIE + Some(inst::unwind::create_cie()) + } } /// Create a new `isa::Builder`. diff --git a/cranelift/codegen/src/isa/x86/unwind/systemv.rs b/cranelift/codegen/src/isa/x86/unwind/systemv.rs index b08628e6fd..3b1266e72f 100644 --- a/cranelift/codegen/src/isa/x86/unwind/systemv.rs +++ b/cranelift/codegen/src/isa/x86/unwind/systemv.rs @@ -114,7 +114,7 @@ pub(crate) fn create_unwind_info( }; struct RegisterMapper<'a, 'b>(&'a (dyn TargetIsa + 'b)); - impl<'a, 'b> crate::isa::unwind::systemv::RegisterMapper for RegisterMapper<'a, 'b> { + impl<'a, 'b> crate::isa::unwind::systemv::RegisterMapper for RegisterMapper<'a, 'b> { fn map(&self, reg: RegUnit) -> Result { Ok(map_reg(self.0, reg)?.0) } diff --git a/cranelift/codegen/src/machinst/abi.rs b/cranelift/codegen/src/machinst/abi.rs index 8308cfd49d..8382ee8848 100644 --- a/cranelift/codegen/src/machinst/abi.rs +++ b/cranelift/codegen/src/machinst/abi.rs @@ -155,6 +155,9 @@ pub trait ABICallee { from_slot: SpillSlot, ty: Option, ) -> Self::I; + + /// Desired unwind info type. + fn unwind_info_kind(&self) -> UnwindInfoKind; } /// Trait implemented by an object that tracks ABI-related state and can diff --git a/cranelift/codegen/src/machinst/abi_impl.rs b/cranelift/codegen/src/machinst/abi_impl.rs index 2f218fd3a8..68024b3f08 100644 --- a/cranelift/codegen/src/machinst/abi_impl.rs +++ b/cranelift/codegen/src/machinst/abi_impl.rs @@ -1034,6 +1034,18 @@ impl ABICallee for ABICalleeImpl { let ty = ty_from_ty_hint_or_reg_class::(to_reg.to_reg().to_reg(), ty); self.load_spillslot(from_slot, ty, to_reg.map(|r| r.to_reg())) } + + fn unwind_info_kind(&self) -> UnwindInfoKind { + match self.sig.call_conv { + #[cfg(feature = "unwind")] + isa::CallConv::Fast | isa::CallConv::Cold | isa::CallConv::SystemV => { + UnwindInfoKind::SystemV + } + #[cfg(feature = "unwind")] + isa::CallConv::WindowsFastcall => UnwindInfoKind::Windows, + _ => UnwindInfoKind::None, + } + } } fn abisig_to_uses_and_defs(sig: &ABISig) -> (Vec, Vec>) { diff --git a/cranelift/codegen/src/machinst/adapter.rs b/cranelift/codegen/src/machinst/adapter.rs index 7e07143e87..43ff9e51a4 100644 --- a/cranelift/codegen/src/machinst/adapter.rs +++ b/cranelift/codegen/src/machinst/adapter.rs @@ -129,6 +129,11 @@ impl TargetIsa for TargetIsaAdapter { self.backend.unsigned_sub_overflow_condition() } + #[cfg(feature = "unwind")] + fn create_systemv_cie(&self) -> Option { + self.backend.create_systemv_cie() + } + fn as_any(&self) -> &dyn Any { self as &dyn Any } diff --git a/cranelift/codegen/src/machinst/mod.rs b/cranelift/codegen/src/machinst/mod.rs index d86e09ddbc..8d36ed6516 100644 --- a/cranelift/codegen/src/machinst/mod.rs +++ b/cranelift/codegen/src/machinst/mod.rs @@ -99,12 +99,14 @@ use crate::binemit::{CodeInfo, CodeOffset, StackMap}; use crate::ir::condcodes::IntCC; use crate::ir::{Function, Type}; +use crate::isa::unwind; use crate::result::CodegenResult; use crate::settings::Flags; use alloc::boxed::Box; use alloc::vec::Vec; use core::fmt::Debug; +use core::ops::Range; use regalloc::RegUsageCollector; use regalloc::{ RealReg, RealRegUniverse, Reg, RegClass, RegUsageMapper, SpillSlot, VirtualReg, Writable, @@ -277,6 +279,8 @@ pub trait MachInstEmit: MachInst { type State: MachInstEmitState; /// Constant information used in `emit` invocations. type Info: MachInstEmitInfo; + /// Unwind info generator. + type UnwindInfo: UnwindInfoGenerator; /// Emit the instruction. fn emit(&self, code: &mut MachBuffer, info: &Self::Info, state: &mut Self::State); /// Pretty-print the instruction. @@ -309,6 +313,8 @@ pub struct MachCompileResult { pub frame_size: u32, /// Disassembly, if requested. pub disasm: Option, + /// Unwind info. + pub unwind_info: Option, } impl MachCompileResult { @@ -353,4 +359,50 @@ pub trait MachBackend { /// Machine-specific condcode info needed by TargetIsa. /// Condition that will be true when an IsubIfcout overflows. fn unsigned_sub_overflow_condition(&self) -> IntCC; + + /// Machine-specific condcode info needed by TargetIsa. + /// Creates a new System V Common Information Entry for the ISA. + #[cfg(feature = "unwind")] + fn create_systemv_cie(&self) -> Option { + // By default, an ISA cannot create a System V CIE + None + } +} + +/// 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, +} + +/// Input data for UnwindInfoGenerator. +pub struct UnwindInfoContext<'a, Inst: MachInstEmit> { + /// Function instructions. + pub insts: &'a [Inst], + /// Instruction layout: end offsets + pub insts_layout: &'a [CodeOffset], + /// Length of the function. + pub len: CodeOffset, + /// Prologue range. + pub prologue: Range, + /// Epilogue ranges. + pub epilogues: &'a [Range], +} + +/// UnwindInfo generator/helper. +pub trait UnwindInfoGenerator { + /// Creates unwind info based on function signature and + /// emitted instructions. + fn create_unwind_info( + context: UnwindInfoContext, + kind: UnwindInfoKind, + ) -> CodegenResult>; } diff --git a/cranelift/codegen/src/machinst/vcode.rs b/cranelift/codegen/src/machinst/vcode.rs index aa9b4d4d45..df156a2d8d 100644 --- a/cranelift/codegen/src/machinst/vcode.rs +++ b/cranelift/codegen/src/machinst/vcode.rs @@ -31,6 +31,7 @@ use regalloc::{ use alloc::boxed::Box; use alloc::{borrow::Cow, vec::Vec}; +use std::cell::RefCell; use std::fmt; use std::iter; use std::string::String; @@ -39,6 +40,8 @@ use std::string::String; pub type InsnIndex = u32; /// Index referring to a basic block in VCode. pub type BlockIndex = u32; +/// Range of an instructions in VCode. +pub type InsnRange = core::ops::Range; /// VCodeInst wraps all requirements for a MachInst to be in VCode: it must be /// a `MachInst` and it must be able to emit itself at least to a `SizeCodeSink`. @@ -101,6 +104,12 @@ pub struct VCode { /// These are used to generate actual stack maps at emission. Filled in /// post-regalloc. safepoint_slots: Vec>, + + /// Ranges for prologue and epilogue instructions. + prologue_epilogue_ranges: Option<(InsnRange, Box<[InsnRange]>)>, + + /// Instruction end offsets + insts_layout: RefCell<(Vec, u32)>, } /// A builder for a VCode function body. This builder is designed for the @@ -292,6 +301,8 @@ impl VCode { emit_info, safepoint_insns: vec![], safepoint_slots: vec![], + prologue_epilogue_ranges: None, + insts_layout: RefCell::new((vec![], 0)), } } @@ -353,6 +364,10 @@ impl VCode { let mut final_safepoint_insns = vec![]; let mut safept_idx = 0; + let mut prologue_start = None; + let mut prologue_end = None; + let mut epilogue_islands = vec![]; + assert!(result.target_map.elems().len() == self.num_blocks()); for block in 0..self.num_blocks() { let start = result.target_map.elems()[block].get() as usize; @@ -365,11 +380,13 @@ impl VCode { let final_start = final_insns.len() as InsnIndex; if block == self.entry { + prologue_start = Some(final_insns.len() as InsnIndex); // Start with the prologue. let prologue = self.abi.gen_prologue(); let len = prologue.len(); final_insns.extend(prologue.into_iter()); final_srclocs.extend(iter::repeat(SourceLoc::default()).take(len)); + prologue_end = Some(final_insns.len() as InsnIndex); } for i in start..end { @@ -395,10 +412,12 @@ impl VCode { // with the epilogue. let is_ret = insn.is_term() == MachTerminator::Ret; if is_ret { + let epilogue_start = final_insns.len() as InsnIndex; let epilogue = self.abi.gen_epilogue(); let len = epilogue.len(); final_insns.extend(epilogue.into_iter()); final_srclocs.extend(iter::repeat(srcloc).take(len)); + epilogue_islands.push(epilogue_start..final_insns.len() as InsnIndex); } else { final_insns.push(insn.clone()); final_srclocs.push(srcloc); @@ -430,6 +449,11 @@ impl VCode { // for the machine backend during emission so that it can do // target-specific translations of slot numbers to stack offsets. self.safepoint_slots = result.stackmaps; + + self.prologue_epilogue_ranges = Some(( + prologue_start.unwrap()..prologue_end.unwrap(), + epilogue_islands.into_boxed_slice(), + )); } /// Emit the instructions to a `MachBuffer`, containing fixed-up code and external @@ -444,6 +468,8 @@ impl VCode { buffer.reserve_labels_for_blocks(self.num_blocks() as BlockIndex); // first N MachLabels are simply block indices. + let mut insts_layout = vec![0; self.insts.len()]; + let mut safepoint_idx = 0; let mut cur_srcloc = None; for block in 0..self.num_blocks() { @@ -482,6 +508,8 @@ impl VCode { } self.insts[iix as usize].emit(&mut buffer, &self.emit_info, &mut state); + + insts_layout[iix as usize] = buffer.cur_offset(); } if cur_srcloc.is_some() { @@ -502,9 +530,27 @@ impl VCode { } } + *self.insts_layout.borrow_mut() = (insts_layout, buffer.cur_offset()); + buffer } + /// Generates unwind info. + pub fn unwind_info( + &self, + ) -> crate::result::CodegenResult> { + let layout = &self.insts_layout.borrow(); + let (prologue, epilogues) = self.prologue_epilogue_ranges.as_ref().unwrap(); + let context = UnwindInfoContext { + insts: &self.insts, + insts_layout: &layout.0, + len: layout.1, + prologue: prologue.clone(), + epilogues, + }; + I::UnwindInfo::create_unwind_info(context, self.abi.unwind_info_kind()) + } + /// Get the IR block for a BlockIndex, if one exists. pub fn bindex_to_bb(&self, block: BlockIndex) -> Option { self.block_order.lowered_order()[block as usize].orig_block() diff --git a/cranelift/filetests/src/test_unwind.rs b/cranelift/filetests/src/test_unwind.rs index 65b93bd611..0c7124e8b2 100644 --- a/cranelift/filetests/src/test_unwind.rs +++ b/cranelift/filetests/src/test_unwind.rs @@ -61,6 +61,9 @@ impl SubTest for TestUnwind { table.write_eh_frame(&mut eh_frame).unwrap(); systemv::dump(&mut text, &eh_frame.0.into_vec(), isa.pointer_bytes()) } + Some(ui) => { + anyhow::bail!("Unexpected unwind info type: {:?}", ui); + } None => {} } diff --git a/tests/all/gc.rs b/tests/all/gc.rs index 005d5152da..0f278cd961 100644 --- a/tests/all/gc.rs +++ b/tests/all/gc.rs @@ -24,6 +24,7 @@ impl Drop for GcOnDrop { } #[test] +#[cfg_attr(feature = "experimental_x64", ignore)] // TODO #2079 investigate. fn smoke_test_gc() -> anyhow::Result<()> { let (store, module) = ref_types_module( r#" @@ -120,6 +121,7 @@ fn wasm_dropping_refs() -> anyhow::Result<()> { } #[test] +#[cfg_attr(feature = "experimental_x64", ignore)] // TODO #2079 investigate. fn many_live_refs() -> anyhow::Result<()> { let mut wat = r#" (module diff --git a/tests/all/traps.rs b/tests/all/traps.rs index c85cd98646..7ef1c05c3a 100644 --- a/tests/all/traps.rs +++ b/tests/all/traps.rs @@ -32,7 +32,7 @@ fn test_trap_return() -> Result<()> { #[test] #[cfg_attr(target_arch = "aarch64", ignore)] // FIXME(#1642) -#[cfg_attr(feature = "experimental_x64", ignore)] // FIXME(#2078) +#[cfg_attr(all(target_os = "windows", feature = "experimental_x64"), ignore)] // FIXME(#2079) fn test_trap_trace() -> Result<()> { let store = Store::default(); let wat = r#" @@ -75,7 +75,7 @@ fn test_trap_trace() -> Result<()> { #[test] #[cfg_attr(target_arch = "aarch64", ignore)] // FIXME(#1642) -#[cfg_attr(feature = "experimental_x64", ignore)] // FIXME(#2078) +#[cfg_attr(all(target_os = "windows", feature = "experimental_x64"), ignore)] // FIXME(#2079) fn test_trap_trace_cb() -> Result<()> { let store = Store::default(); let wat = r#" @@ -112,7 +112,7 @@ fn test_trap_trace_cb() -> Result<()> { #[test] #[cfg_attr(target_arch = "aarch64", ignore)] // FIXME(#1642) -#[cfg_attr(feature = "experimental_x64", ignore)] // FIXME(#2078) +#[cfg_attr(all(target_os = "windows", feature = "experimental_x64"), ignore)] // FIXME(#2079) fn test_trap_stack_overflow() -> Result<()> { let store = Store::default(); let wat = r#" @@ -145,7 +145,7 @@ fn test_trap_stack_overflow() -> Result<()> { #[test] #[cfg_attr(target_arch = "aarch64", ignore)] // FIXME(#1642) -#[cfg_attr(feature = "experimental_x64", ignore)] // FIXME(#2078) +#[cfg_attr(all(target_os = "windows", feature = "experimental_x64"), ignore)] // FIXME(#2079) fn trap_display_pretty() -> Result<()> { let store = Store::default(); let wat = r#" @@ -178,7 +178,7 @@ wasm backtrace: #[test] #[cfg_attr(target_arch = "aarch64", ignore)] // FIXME(#1642) -#[cfg_attr(feature = "experimental_x64", ignore)] // FIXME(#2078) +#[cfg_attr(all(target_os = "windows", feature = "experimental_x64"), ignore)] // FIXME(#2079) fn trap_display_multi_module() -> Result<()> { let store = Store::default(); let wat = r#" @@ -224,7 +224,7 @@ wasm backtrace: #[test] #[cfg_attr(target_arch = "aarch64", ignore)] // FIXME(#1642) -#[cfg_attr(feature = "experimental_x64", ignore)] // FIXME(#2078) +#[cfg_attr(all(target_os = "windows", feature = "experimental_x64"), ignore)] // FIXME(#2079) fn trap_start_function_import() -> Result<()> { let store = Store::default(); let binary = wat::parse_str( @@ -252,7 +252,7 @@ fn trap_start_function_import() -> Result<()> { #[test] #[cfg_attr(target_arch = "aarch64", ignore)] // FIXME(#1642) -#[cfg_attr(feature = "experimental_x64", ignore)] // FIXME(#2078) +#[cfg_attr(all(target_os = "windows", feature = "experimental_x64"), ignore)] // FIXME(#2079) fn rust_panic_import() -> Result<()> { let store = Store::default(); let binary = wat::parse_str( @@ -298,7 +298,7 @@ fn rust_panic_import() -> Result<()> { #[test] #[cfg_attr(target_arch = "aarch64", ignore)] // FIXME(#1642) -#[cfg_attr(feature = "experimental_x64", ignore)] // FIXME(#2078) +#[cfg_attr(all(target_os = "windows", feature = "experimental_x64"), ignore)] // FIXME(#2079) fn rust_panic_start_function() -> Result<()> { let store = Store::default(); let binary = wat::parse_str( @@ -333,7 +333,7 @@ fn rust_panic_start_function() -> Result<()> { #[test] #[cfg_attr(target_arch = "aarch64", ignore)] // FIXME(#1642) -#[cfg_attr(feature = "experimental_x64", ignore)] // FIXME(#2078) +#[cfg_attr(all(target_os = "windows", feature = "experimental_x64"), ignore)] // FIXME(#2079) fn mismatched_arguments() -> Result<()> { let store = Store::default(); let binary = wat::parse_str( @@ -366,7 +366,7 @@ fn mismatched_arguments() -> Result<()> { #[test] #[cfg_attr(target_arch = "aarch64", ignore)] // FIXME(#1642) -#[cfg_attr(feature = "experimental_x64", ignore)] // FIXME(#2078) +#[cfg_attr(all(target_os = "windows", feature = "experimental_x64"), ignore)] // FIXME(#2079) fn call_signature_mismatch() -> Result<()> { let store = Store::default(); let binary = wat::parse_str( @@ -398,7 +398,7 @@ fn call_signature_mismatch() -> Result<()> { #[test] #[cfg_attr(target_arch = "aarch64", ignore)] // FIXME(#1642) -#[cfg_attr(feature = "experimental_x64", ignore)] // FIXME(#2078) +#[cfg_attr(all(target_os = "windows", feature = "experimental_x64"), ignore)] // FIXME(#2079) fn start_trap_pretty() -> Result<()> { let store = Store::default(); let wat = r#"