cranelift_codegen/isa/aarch64/inst/unwind/
systemv.rs

1//! Unwind information for System V ABI (Aarch64).
2
3use crate::isa::aarch64::inst::regs;
4use crate::isa::unwind::systemv::RegisterMappingError;
5use crate::machinst::{Reg, RegClass};
6use gimli::{write::CommonInformationEntry, Encoding, Format, Register};
7
8/// Creates a new aarch64 common information entry (CIE).
9pub fn create_cie() -> CommonInformationEntry {
10    use gimli::write::CallFrameInstruction;
11
12    let mut entry = CommonInformationEntry::new(
13        Encoding {
14            address_size: 8,
15            format: Format::Dwarf32,
16            version: 1,
17        },
18        4,  // Code alignment factor
19        -8, // Data alignment factor
20        Register(regs::link_reg().to_real_reg().unwrap().hw_enc().into()),
21    );
22
23    // Every frame will start with the call frame address (CFA) at SP
24    let sp = Register((regs::stack_reg().to_real_reg().unwrap().hw_enc() & 31).into());
25    entry.add_instruction(CallFrameInstruction::Cfa(sp, 0));
26
27    entry
28}
29
30/// Map Cranelift registers to their corresponding Gimli registers.
31pub fn map_reg(reg: Reg) -> Result<Register, RegisterMappingError> {
32    // For AArch64 DWARF register mappings, see:
33    //
34    // https://developer.arm.com/documentation/ihi0057/e/?lang=en#dwarf-register-names
35    //
36    // X0--X31 is 0--31; V0--V31 is 64--95.
37    match reg.class() {
38        RegClass::Int => {
39            let reg = (reg.to_real_reg().unwrap().hw_enc() & 31) as u16;
40            Ok(Register(reg))
41        }
42        RegClass::Float => {
43            let reg = reg.to_real_reg().unwrap().hw_enc() as u16;
44            Ok(Register(64 + reg))
45        }
46        RegClass::Vector => unreachable!(),
47    }
48}
49
50pub(crate) struct RegisterMapper;
51
52impl crate::isa::unwind::systemv::RegisterMapper<Reg> for RegisterMapper {
53    fn map(&self, reg: Reg) -> Result<u16, RegisterMappingError> {
54        Ok(map_reg(reg)?.0)
55    }
56    fn fp(&self) -> Option<u16> {
57        Some(regs::fp_reg().to_real_reg().unwrap().hw_enc().into())
58    }
59    fn lr(&self) -> Option<u16> {
60        Some(regs::link_reg().to_real_reg().unwrap().hw_enc().into())
61    }
62    fn lr_offset(&self) -> Option<u32> {
63        Some(8)
64    }
65}
66
67#[cfg(test)]
68mod tests {
69    use crate::cursor::{Cursor, FuncCursor};
70    use crate::ir::{
71        types, AbiParam, Function, InstBuilder, Signature, StackSlotData, StackSlotKind,
72    };
73    use crate::isa::{lookup, CallConv};
74    use crate::settings::{builder, Flags};
75    use crate::Context;
76    use gimli::write::Address;
77    use target_lexicon::triple;
78
79    #[test]
80    fn test_simple_func() {
81        let isa = lookup(triple!("aarch64"))
82            .expect("expect aarch64 ISA")
83            .finish(Flags::new(builder()))
84            .expect("Creating compiler backend");
85
86        let mut context = Context::for_function(create_function(
87            CallConv::SystemV,
88            Some(StackSlotData::new(StackSlotKind::ExplicitSlot, 64, 0)),
89        ));
90
91        let code = context
92            .compile(&*isa, &mut Default::default())
93            .expect("expected compilation");
94
95        let fde = match code
96            .create_unwind_info(isa.as_ref())
97            .expect("can create unwind info")
98        {
99            Some(crate::isa::unwind::UnwindInfo::SystemV(info)) => {
100                info.to_fde(Address::Constant(1234))
101            }
102            _ => panic!("expected unwind information"),
103        };
104
105        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)))] }");
106    }
107
108    fn create_function(call_conv: CallConv, stack_slot: Option<StackSlotData>) -> Function {
109        let mut func = Function::with_name_signature(Default::default(), Signature::new(call_conv));
110
111        let block0 = func.dfg.make_block();
112        let mut pos = FuncCursor::new(&mut func);
113        pos.insert_block(block0);
114        pos.ins().return_(&[]);
115
116        if let Some(stack_slot) = stack_slot {
117            func.sized_stack_slots.push(stack_slot);
118        }
119
120        func
121    }
122
123    #[test]
124    fn test_multi_return_func() {
125        let isa = lookup(triple!("aarch64"))
126            .expect("expect aarch64 ISA")
127            .finish(Flags::new(builder()))
128            .expect("Creating compiler backend");
129
130        let mut context = Context::for_function(create_multi_return_function(CallConv::SystemV));
131
132        let code = context
133            .compile(&*isa, &mut Default::default())
134            .expect("expected compilation");
135
136        let fde = match code
137            .create_unwind_info(isa.as_ref())
138            .expect("can create unwind info")
139        {
140            Some(crate::isa::unwind::UnwindInfo::SystemV(info)) => {
141                info.to_fde(Address::Constant(4321))
142            }
143            _ => panic!("expected unwind information"),
144        };
145
146        assert_eq!(
147            format!("{fde:?}"),
148            "FrameDescriptionEntry { address: Constant(4321), length: 16, lsda: None, instructions: [] }"
149        );
150    }
151
152    fn create_multi_return_function(call_conv: CallConv) -> Function {
153        let mut sig = Signature::new(call_conv);
154        sig.params.push(AbiParam::new(types::I32));
155        let mut func = Function::with_name_signature(Default::default(), sig);
156
157        let block0 = func.dfg.make_block();
158        let v0 = func.dfg.append_block_param(block0, types::I32);
159        let block1 = func.dfg.make_block();
160        let block2 = func.dfg.make_block();
161
162        let mut pos = FuncCursor::new(&mut func);
163        pos.insert_block(block0);
164        pos.ins().brif(v0, block2, &[], block1, &[]);
165
166        pos.insert_block(block1);
167        pos.ins().return_(&[]);
168
169        pos.insert_block(block2);
170        pos.ins().return_(&[]);
171
172        func
173    }
174}