From b2b7bc10e2a6297f85ac0da17384e1f6c09ee111 Mon Sep 17 00:00:00 2001 From: Yury Delendik Date: Fri, 6 Nov 2020 08:02:45 -0600 Subject: [PATCH] machinst aarch64: New backend unwind (#2313) * Unwind information for aarch64 backend. --- .../codegen/src/isa/aarch64/inst/unwind.rs | 188 +++++++++++++++++- .../src/isa/aarch64/inst/unwind/systemv.rs | 158 +++++++++++++++ cranelift/codegen/src/isa/aarch64/mod.rs | 28 ++- cranelift/codegen/src/machinst/adapter.rs | 2 +- tests/all/traps.rs | 22 +- 5 files changed, 381 insertions(+), 17 deletions(-) create mode 100644 cranelift/codegen/src/isa/aarch64/inst/unwind/systemv.rs diff --git a/cranelift/codegen/src/isa/aarch64/inst/unwind.rs b/cranelift/codegen/src/isa/aarch64/inst/unwind.rs index 9ea2cb29c0..265dd3fb60 100644 --- a/cranelift/codegen/src/isa/aarch64/inst/unwind.rs +++ b/cranelift/codegen/src/isa/aarch64/inst/unwind.rs @@ -1,14 +1,194 @@ use super::*; -use crate::isa::unwind::input::UnwindInfo; +use crate::isa::aarch64::inst::{args::PairAMode, imms::Imm12, regs, ALUOp, Inst}; +use crate::isa::unwind::input::{UnwindCode, UnwindInfo}; +use crate::machinst::UnwindInfoContext; use crate::result::CodegenResult; +use alloc::vec::Vec; +use regalloc::Reg; + +#[cfg(feature = "unwind")] +pub(crate) mod systemv; pub struct AArch64UnwindInfo; impl UnwindInfoGenerator for AArch64UnwindInfo { fn create_unwind_info( - _context: UnwindInfoContext, + context: UnwindInfoContext, ) -> CodegenResult>> { - // TODO - Ok(None) + let word_size = 8u8; + let pair_size = word_size * 2; + 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::StoreP64 { + rt, + rt2, + mem: PairAMode::PreIndexed(rn, imm7), + } if *rt == regs::fp_reg() + && *rt2 == regs::link_reg() + && *rn == regs::writable_stack_reg() + && imm7.value == -(pair_size as i16) => + { + // stp fp (x29), lr (x30), [sp, #-16]! + codes.push(( + offset, + UnwindCode::StackAlloc { + size: pair_size as u32, + }, + )); + codes.push(( + offset, + UnwindCode::SaveRegister { + reg: *rt, + stack_offset: 0, + }, + )); + codes.push(( + offset, + UnwindCode::SaveRegister { + reg: *rt2, + stack_offset: word_size as u32, + }, + )); + } + Inst::StoreP64 { + rt, + rt2, + mem: PairAMode::PreIndexed(rn, imm7), + } if rn.to_reg() == regs::stack_reg() && imm7.value % (pair_size as i16) == 0 => { + // stp r1, r2, [sp, #(i * #16)] + let stack_offset = imm7.value as u32; + codes.push(( + offset, + UnwindCode::SaveRegister { + reg: *rt, + stack_offset, + }, + )); + if *rt2 != regs::zero_reg() { + codes.push(( + offset, + UnwindCode::SaveRegister { + reg: *rt2, + stack_offset: stack_offset + word_size as u32, + }, + )); + } + } + Inst::AluRRImm12 { + alu_op: ALUOp::Add64, + rd, + rn, + imm12: + Imm12 { + bits: 0, + shift12: false, + }, + } if *rd == regs::writable_fp_reg() && *rn == regs::stack_reg() => { + // mov fp (x29), sp. + codes.push((offset, UnwindCode::SetFramePointer { reg: rd.to_reg() })); + } + Inst::VirtualSPOffsetAdj { offset: adj } if offset > 0 => { + codes.push((offset, UnwindCode::StackAlloc { size: *adj as u32 })); + } + _ => {} + } + } + + // TODO epilogues + + let prologue_size = context.insts_layout[context.prologue.end as usize - 1]; + Ok(Some(UnwindInfo { + prologue_size, + prologue_unwind_codes: codes, + epilogues_unwind_codes: vec![], + function_size: context.len, + word_size, + initial_sp_offset: 0, + })) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::cursor::{Cursor, FuncCursor}; + use crate::ir::{ExternalName, Function, InstBuilder, Signature, StackSlotData, StackSlotKind}; + use crate::isa::{lookup, CallConv}; + use crate::settings::{builder, Flags}; + use crate::Context; + use std::str::FromStr; + use target_lexicon::triple; + + #[test] + fn test_simple_func() { + let isa = lookup(triple!("aarch64")) + .expect("expect aarch64 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 result = context.mach_compile_result.unwrap(); + let unwind_info = result.unwind_info.unwrap(); + + assert_eq!( + unwind_info, + UnwindInfo { + prologue_size: 12, + prologue_unwind_codes: vec![ + (4, UnwindCode::StackAlloc { size: 16 }), + ( + 4, + UnwindCode::SaveRegister { + reg: regs::fp_reg(), + stack_offset: 0 + } + ), + ( + 4, + UnwindCode::SaveRegister { + reg: regs::link_reg(), + stack_offset: 8 + } + ), + ( + 8, + UnwindCode::SetFramePointer { + reg: regs::fp_reg() + } + ) + ], + epilogues_unwind_codes: vec![], + function_size: 24, + word_size: 8, + initial_sp_offset: 0, + } + ); + } + + 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 } } diff --git a/cranelift/codegen/src/isa/aarch64/inst/unwind/systemv.rs b/cranelift/codegen/src/isa/aarch64/inst/unwind/systemv.rs new file mode 100644 index 0000000000..b988314b1b --- /dev/null +++ b/cranelift/codegen/src/isa/aarch64/inst/unwind/systemv.rs @@ -0,0 +1,158 @@ +//! Unwind information for System V ABI (Aarch64). + +use crate::isa::aarch64::inst::regs; +use crate::isa::unwind::input; +use crate::isa::unwind::systemv::{RegisterMappingError, UnwindInfo}; +use crate::result::CodegenResult; +use gimli::{write::CommonInformationEntry, Encoding, Format, Register}; +use regalloc::{Reg, RegClass}; + +/// Creates a new aarch64 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, + }, + 4, // Code alignment factor + -8, // Data alignment factor + Register(regs::link_reg().get_hw_encoding().into()), + ); + + // Every frame will start with the call frame address (CFA) at SP + let sp = Register(regs::stack_reg().get_hw_encoding().into()); + entry.add_instruction(CallFrameInstruction::Cfa(sp, 0)); + + entry +} + +/// Map Cranelift registers to their corresponding Gimli registers. +pub fn map_reg(reg: Reg) -> Result { + match reg.get_class() { + RegClass::I64 => Ok(Register(reg.get_hw_encoding().into())), + _ => Err(RegisterMappingError::UnsupportedRegisterBank("class?")), + } +} + +pub(crate) fn create_unwind_info( + unwind: input::UnwindInfo, +) -> CodegenResult> { + struct RegisterMapper; + impl crate::isa::unwind::systemv::RegisterMapper for RegisterMapper { + fn map(&self, reg: Reg) -> Result { + Ok(map_reg(reg)?.0) + } + fn sp(&self) -> u16 { + regs::stack_reg().get_hw_encoding().into() + } + } + 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!("aarch64")) + .expect("expect aarch64 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: 24, lsda: None, instructions: [(4, CfaOffset(16)), (4, Offset(Register(29), -16)), (4, Offset(Register(30), -8)), (8, CfaRegister(Register(29)))] }"); + } + + 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!("aarch64")) + .expect("expect aarch64 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: 40, lsda: None, instructions: [(4, CfaOffset(16)), (4, Offset(Register(29), -16)), (4, Offset(Register(30), -8)), (8, CfaRegister(Register(29)))] }"); + } + + 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/aarch64/mod.rs b/cranelift/codegen/src/isa/aarch64/mod.rs index 987fc49a06..c3c56632d3 100644 --- a/cranelift/codegen/src/isa/aarch64/mod.rs +++ b/cranelift/codegen/src/isa/aarch64/mod.rs @@ -64,6 +64,7 @@ impl MachBackend for AArch64Backend { let buffer = vcode.emit(); 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(flags)))) @@ -77,7 +78,7 @@ impl MachBackend for AArch64Backend { buffer, frame_size, disasm, - unwind_info: None, + unwind_info, }) } @@ -109,6 +110,31 @@ impl MachBackend for AArch64Backend { // opposite of x86). IntCC::UnsignedLessThan } + + #[cfg(feature = "unwind")] + fn emit_unwind_info( + &self, + result: &MachCompileResult, + kind: crate::machinst::UnwindInfoKind, + ) -> CodegenResult> { + use crate::isa::unwind::UnwindInfo; + use crate::machinst::UnwindInfoKind; + Ok(match (result.unwind_info.as_ref(), kind) { + (Some(info), UnwindInfoKind::SystemV) => { + inst::unwind::systemv::create_unwind_info(info.clone())?.map(UnwindInfo::SystemV) + } + (Some(_info), UnwindInfoKind::Windows) => { + // TODO: support Windows unwind info on AArch64 + None + } + _ => None, + }) + } + + #[cfg(feature = "unwind")] + fn create_systemv_cie(&self) -> Option { + Some(inst::unwind::systemv::create_cie()) + } } /// Create a new `isa::Builder`. diff --git a/cranelift/codegen/src/machinst/adapter.rs b/cranelift/codegen/src/machinst/adapter.rs index c312f76d4a..43ff9e51a4 100644 --- a/cranelift/codegen/src/machinst/adapter.rs +++ b/cranelift/codegen/src/machinst/adapter.rs @@ -129,7 +129,7 @@ impl TargetIsa for TargetIsaAdapter { self.backend.unsigned_sub_overflow_condition() } - #[cfg(all(feature = "unwind", feature = "x64"))] + #[cfg(feature = "unwind")] fn create_systemv_cie(&self) -> Option { self.backend.create_systemv_cie() } diff --git a/tests/all/traps.rs b/tests/all/traps.rs index 0a8196a0d4..cd9b4b38d7 100644 --- a/tests/all/traps.rs +++ b/tests/all/traps.rs @@ -31,7 +31,7 @@ fn test_trap_return() -> Result<()> { } #[test] -#[cfg_attr(target_arch = "aarch64", ignore)] // FIXME(#1642) +#[cfg_attr(all(target_os = "windows", target_arch = "aarch64"), ignore)] // FIXME(#1642) #[cfg_attr(all(target_os = "windows", feature = "experimental_x64"), ignore)] // FIXME(#2079) fn test_trap_trace() -> Result<()> { let store = Store::default(); @@ -74,7 +74,7 @@ fn test_trap_trace() -> Result<()> { } #[test] -#[cfg_attr(target_arch = "aarch64", ignore)] // FIXME(#1642) +#[cfg_attr(all(target_os = "windows", target_arch = "aarch64"), ignore)] // FIXME(#1642) #[cfg_attr(all(target_os = "windows", feature = "experimental_x64"), ignore)] // FIXME(#2079) fn test_trap_trace_cb() -> Result<()> { let store = Store::default(); @@ -111,7 +111,7 @@ fn test_trap_trace_cb() -> Result<()> { } #[test] -#[cfg_attr(target_arch = "aarch64", ignore)] // FIXME(#1642) +#[cfg_attr(all(target_os = "windows", target_arch = "aarch64"), ignore)] // FIXME(#1642) #[cfg_attr(all(target_os = "windows", feature = "experimental_x64"), ignore)] // FIXME(#2079) fn test_trap_stack_overflow() -> Result<()> { let store = Store::default(); @@ -144,7 +144,7 @@ fn test_trap_stack_overflow() -> Result<()> { } #[test] -#[cfg_attr(target_arch = "aarch64", ignore)] // FIXME(#1642) +#[cfg_attr(all(target_os = "windows", target_arch = "aarch64"), ignore)] // FIXME(#1642) #[cfg_attr(all(target_os = "windows", feature = "experimental_x64"), ignore)] // FIXME(#2079) fn trap_display_pretty() -> Result<()> { let store = Store::default(); @@ -177,7 +177,7 @@ wasm backtrace: } #[test] -#[cfg_attr(target_arch = "aarch64", ignore)] // FIXME(#1642) +#[cfg_attr(all(target_os = "windows", target_arch = "aarch64"), ignore)] // FIXME(#1642) #[cfg_attr(all(target_os = "windows", feature = "experimental_x64"), ignore)] // FIXME(#2079) fn trap_display_multi_module() -> Result<()> { let store = Store::default(); @@ -223,7 +223,7 @@ wasm backtrace: } #[test] -#[cfg_attr(target_arch = "aarch64", ignore)] // FIXME(#1642) +#[cfg_attr(all(target_os = "windows", target_arch = "aarch64"), ignore)] // FIXME(#1642) #[cfg_attr(all(target_os = "windows", feature = "experimental_x64"), ignore)] // FIXME(#2079) fn trap_start_function_import() -> Result<()> { let store = Store::default(); @@ -251,7 +251,7 @@ fn trap_start_function_import() -> Result<()> { } #[test] -#[cfg_attr(target_arch = "aarch64", ignore)] // FIXME(#1642) +#[cfg_attr(all(target_os = "windows", target_arch = "aarch64"), ignore)] // FIXME(#1642) #[cfg_attr(all(target_os = "windows", feature = "experimental_x64"), ignore)] // FIXME(#2079) fn rust_panic_import() -> Result<()> { let store = Store::default(); @@ -297,7 +297,7 @@ fn rust_panic_import() -> Result<()> { } #[test] -#[cfg_attr(target_arch = "aarch64", ignore)] // FIXME(#1642) +#[cfg_attr(all(target_os = "windows", target_arch = "aarch64"), ignore)] // FIXME(#1642) #[cfg_attr(all(target_os = "windows", feature = "experimental_x64"), ignore)] // FIXME(#2079) fn rust_panic_start_function() -> Result<()> { let store = Store::default(); @@ -332,7 +332,7 @@ fn rust_panic_start_function() -> Result<()> { } #[test] -#[cfg_attr(target_arch = "aarch64", ignore)] // FIXME(#1642) +#[cfg_attr(all(target_os = "windows", target_arch = "aarch64"), ignore)] // FIXME(#1642) #[cfg_attr(all(target_os = "windows", feature = "experimental_x64"), ignore)] // FIXME(#2079) fn mismatched_arguments() -> Result<()> { let store = Store::default(); @@ -365,7 +365,7 @@ fn mismatched_arguments() -> Result<()> { } #[test] -#[cfg_attr(target_arch = "aarch64", ignore)] // FIXME(#1642) +#[cfg_attr(all(target_os = "windows", target_arch = "aarch64"), ignore)] // FIXME(#1642) #[cfg_attr(all(target_os = "windows", feature = "experimental_x64"), ignore)] // FIXME(#2079) fn call_signature_mismatch() -> Result<()> { let store = Store::default(); @@ -397,7 +397,7 @@ fn call_signature_mismatch() -> Result<()> { } #[test] -#[cfg_attr(target_arch = "aarch64", ignore)] // FIXME(#1642) +#[cfg_attr(all(target_os = "windows", target_arch = "aarch64"), ignore)] // FIXME(#1642) #[cfg_attr(all(target_os = "windows", feature = "experimental_x64"), ignore)] // FIXME(#2079) fn start_trap_pretty() -> Result<()> { let store = Store::default();