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