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

1//! Unwind information for System V ABI (x86-64).
2
3use crate::isa::unwind::systemv::RegisterMappingError;
4use crate::machinst::{Reg, RegClass};
5use gimli::{Encoding, Format, Register, X86_64, write::CommonInformationEntry};
6
7/// Creates a new x86-64 common information entry (CIE).
8pub fn create_cie() -> CommonInformationEntry {
9    use gimli::write::CallFrameInstruction;
10
11    let mut entry = CommonInformationEntry::new(
12        Encoding {
13            address_size: 8,
14            format: Format::Dwarf32,
15            version: 1,
16        },
17        1,  // Code alignment factor
18        -8, // Data alignment factor
19        X86_64::RA,
20    );
21
22    // Every frame will start with the call frame address (CFA) at RSP+8
23    // It is +8 to account for the push of the return address by the call instruction
24    entry.add_instruction(CallFrameInstruction::Cfa(X86_64::RSP, 8));
25
26    // Every frame will start with the return address at RSP (CFA-8 = RSP+8-8 = RSP)
27    entry.add_instruction(CallFrameInstruction::Offset(X86_64::RA, -8));
28
29    entry
30}
31
32/// Map Cranelift registers to their corresponding Gimli registers.
33pub fn map_reg(reg: Reg) -> Result<Register, RegisterMappingError> {
34    // Mapping from https://github.com/bytecodealliance/cranelift/pull/902 by @iximeow
35    const X86_GP_REG_MAP: [gimli::Register; 16] = [
36        X86_64::RAX,
37        X86_64::RCX,
38        X86_64::RDX,
39        X86_64::RBX,
40        X86_64::RSP,
41        X86_64::RBP,
42        X86_64::RSI,
43        X86_64::RDI,
44        X86_64::R8,
45        X86_64::R9,
46        X86_64::R10,
47        X86_64::R11,
48        X86_64::R12,
49        X86_64::R13,
50        X86_64::R14,
51        X86_64::R15,
52    ];
53    const X86_XMM_REG_MAP: [gimli::Register; 16] = [
54        X86_64::XMM0,
55        X86_64::XMM1,
56        X86_64::XMM2,
57        X86_64::XMM3,
58        X86_64::XMM4,
59        X86_64::XMM5,
60        X86_64::XMM6,
61        X86_64::XMM7,
62        X86_64::XMM8,
63        X86_64::XMM9,
64        X86_64::XMM10,
65        X86_64::XMM11,
66        X86_64::XMM12,
67        X86_64::XMM13,
68        X86_64::XMM14,
69        X86_64::XMM15,
70    ];
71
72    match reg.class() {
73        RegClass::Int => {
74            // x86 GP registers have a weird mapping to DWARF registers, so we use a
75            // lookup table.
76            Ok(X86_GP_REG_MAP[reg.to_real_reg().unwrap().hw_enc() as usize])
77        }
78        RegClass::Float => Ok(X86_XMM_REG_MAP[reg.to_real_reg().unwrap().hw_enc() as usize]),
79        RegClass::Vector => unreachable!(),
80    }
81}
82
83pub(crate) struct RegisterMapper;
84
85impl crate::isa::unwind::systemv::RegisterMapper<Reg> for RegisterMapper {
86    fn map(&self, reg: Reg) -> Result<u16, RegisterMappingError> {
87        Ok(map_reg(reg)?.0)
88    }
89    fn fp(&self) -> Option<u16> {
90        Some(X86_64::RBP.0)
91    }
92}
93
94#[cfg(test)]
95mod tests {
96    use crate::Context;
97    use crate::cursor::{Cursor, FuncCursor};
98    use crate::ir::{
99        AbiParam, Function, InstBuilder, Signature, StackSlotData, StackSlotKind, types,
100    };
101    use crate::isa::{CallConv, lookup};
102    use crate::settings::{Flags, builder};
103    use gimli::write::Address;
104    use target_lexicon::triple;
105
106    #[test]
107    fn test_simple_func() {
108        let isa = lookup(triple!("x86_64"))
109            .expect("expect x86 ISA")
110            .finish(Flags::new(builder()))
111            .expect("expect backend creation to succeed");
112
113        let mut context = Context::for_function(create_function(
114            CallConv::SystemV,
115            Some(StackSlotData::new(StackSlotKind::ExplicitSlot, 64, 0)),
116        ));
117
118        let code = context
119            .compile(&*isa, &mut Default::default())
120            .expect("expected compilation");
121
122        let fde = match code
123            .create_unwind_info(isa.as_ref())
124            .expect("can create unwind info")
125        {
126            Some(crate::isa::unwind::UnwindInfo::SystemV(info)) => {
127                info.to_fde(Address::Constant(1234))
128            }
129            _ => panic!("expected unwind information"),
130        };
131
132        assert_eq!(
133            format!("{fde:?}"),
134            "FrameDescriptionEntry { address: Constant(1234), length: 17, lsda: None, instructions: [(1, CfaOffset(16)), (1, Offset(Register(6), -16)), (4, CfaRegister(Register(6)))] }"
135        );
136    }
137
138    fn create_function(call_conv: CallConv, stack_slot: Option<StackSlotData>) -> Function {
139        let mut func = Function::with_name_signature(Default::default(), Signature::new(call_conv));
140
141        let block0 = func.dfg.make_block();
142        let mut pos = FuncCursor::new(&mut func);
143        pos.insert_block(block0);
144        pos.ins().return_(&[]);
145
146        if let Some(stack_slot) = stack_slot {
147            func.sized_stack_slots.push(stack_slot);
148        }
149
150        func
151    }
152
153    #[test]
154    fn test_multi_return_func() {
155        let isa = lookup(triple!("x86_64"))
156            .expect("expect x86 ISA")
157            .finish(Flags::new(builder()))
158            .expect("expect backend creation to succeed");
159
160        let mut context = Context::for_function(create_multi_return_function(CallConv::SystemV));
161
162        let code = context
163            .compile(&*isa, &mut Default::default())
164            .expect("expected compilation");
165
166        let fde = match code
167            .create_unwind_info(isa.as_ref())
168            .expect("can create unwind info")
169        {
170            Some(crate::isa::unwind::UnwindInfo::SystemV(info)) => {
171                info.to_fde(Address::Constant(4321))
172            }
173            _ => panic!("expected unwind information"),
174        };
175
176        assert_eq!(
177            format!("{fde:?}"),
178            "FrameDescriptionEntry { address: Constant(4321), length: 22, lsda: None, instructions: [(1, CfaOffset(16)), (1, Offset(Register(6), -16)), (4, CfaRegister(Register(6)))] }"
179        );
180    }
181
182    fn create_multi_return_function(call_conv: CallConv) -> Function {
183        let mut sig = Signature::new(call_conv);
184        sig.params.push(AbiParam::new(types::I32));
185        let mut func = Function::with_name_signature(Default::default(), sig);
186
187        let block0 = func.dfg.make_block();
188        let v0 = func.dfg.append_block_param(block0, types::I32);
189        let block1 = func.dfg.make_block();
190        let block2 = func.dfg.make_block();
191
192        let mut pos = FuncCursor::new(&mut func);
193        pos.insert_block(block0);
194        pos.ins().brif(v0, block2, &[], block1, &[]);
195
196        pos.insert_block(block1);
197        pos.ins().return_(&[]);
198
199        pos.insert_block(block2);
200        pos.ins().return_(&[]);
201
202        func
203    }
204}