1 //! Unwind information for System V ABI (x86-64).
2 
3 use crate::isa::unwind::systemv::RegisterMappingError;
4 use gimli::{write::CommonInformationEntry, Encoding, Format, Register, X86_64};
5 use regalloc::{Reg, RegClass};
6 
7 /// Creates a new x86-64 common information entry (CIE).
create_cie() -> CommonInformationEntry8 pub 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.
map_reg(reg: Reg) -> Result<Register, RegisterMappingError>33 pub 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.get_class() {
73         RegClass::I64 => {
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.get_hw_encoding() as usize])
77         }
78         RegClass::V128 => Ok(X86_XMM_REG_MAP[reg.get_hw_encoding() as usize]),
79         _ => Err(RegisterMappingError::UnsupportedRegisterBank("class?")),
80     }
81 }
82 
83 pub(crate) struct RegisterMapper;
84 
85 impl crate::isa::unwind::systemv::RegisterMapper<Reg> for RegisterMapper {
map(&self, reg: Reg) -> Result<u16, RegisterMappingError>86     fn map(&self, reg: Reg) -> Result<u16, RegisterMappingError> {
87         Ok(map_reg(reg)?.0)
88     }
sp(&self) -> u1689     fn sp(&self) -> u16 {
90         X86_64::RSP.0
91     }
fp(&self) -> Option<u16>92     fn fp(&self) -> Option<u16> {
93         Some(X86_64::RBP.0)
94     }
95 }
96 
97 #[cfg(test)]
98 mod tests {
99     use crate::cursor::{Cursor, FuncCursor};
100     use crate::ir::{
101         types, AbiParam, ExternalName, Function, InstBuilder, Signature, StackSlotData,
102         StackSlotKind,
103     };
104     use crate::isa::{lookup, CallConv};
105     use crate::settings::{builder, Flags};
106     use crate::Context;
107     use gimli::write::Address;
108     use std::str::FromStr;
109     use target_lexicon::triple;
110 
111     #[test]
112     #[cfg_attr(feature = "old-x86-backend", ignore)]
test_simple_func()113     fn test_simple_func() {
114         let isa = lookup(triple!("x86_64"))
115             .expect("expect x86 ISA")
116             .finish(Flags::new(builder()));
117 
118         let mut context = Context::for_function(create_function(
119             CallConv::SystemV,
120             Some(StackSlotData::new(StackSlotKind::ExplicitSlot, 64)),
121         ));
122 
123         context.compile(&*isa).expect("expected compilation");
124 
125         let fde = match context
126             .create_unwind_info(isa.as_ref())
127             .expect("can create unwind info")
128         {
129             Some(crate::isa::unwind::UnwindInfo::SystemV(info)) => {
130                 info.to_fde(Address::Constant(1234))
131             }
132             _ => panic!("expected unwind information"),
133         };
134 
135         assert_eq!(format!("{:?}", fde), "FrameDescriptionEntry { address: Constant(1234), length: 17, lsda: None, instructions: [(1, CfaOffset(16)), (1, Offset(Register(6), -16)), (4, CfaRegister(Register(6)))] }");
136     }
137 
create_function(call_conv: CallConv, stack_slot: Option<StackSlotData>) -> Function138     fn create_function(call_conv: CallConv, stack_slot: Option<StackSlotData>) -> Function {
139         let mut func =
140             Function::with_name_signature(ExternalName::user(0, 0), Signature::new(call_conv));
141 
142         let block0 = func.dfg.make_block();
143         let mut pos = FuncCursor::new(&mut func);
144         pos.insert_block(block0);
145         pos.ins().return_(&[]);
146 
147         if let Some(stack_slot) = stack_slot {
148             func.stack_slots.push(stack_slot);
149         }
150 
151         func
152     }
153 
154     #[test]
155     #[cfg_attr(feature = "old-x86-backend", ignore)]
test_multi_return_func()156     fn test_multi_return_func() {
157         let isa = lookup(triple!("x86_64"))
158             .expect("expect x86 ISA")
159             .finish(Flags::new(builder()));
160 
161         let mut context = Context::for_function(create_multi_return_function(CallConv::SystemV));
162 
163         context.compile(&*isa).expect("expected compilation");
164 
165         let fde = match context
166             .create_unwind_info(isa.as_ref())
167             .expect("can create unwind info")
168         {
169             Some(crate::isa::unwind::UnwindInfo::SystemV(info)) => {
170                 info.to_fde(Address::Constant(4321))
171             }
172             _ => panic!("expected unwind information"),
173         };
174 
175         assert_eq!(format!("{:?}", fde), "FrameDescriptionEntry { address: Constant(4321), length: 22, lsda: None, instructions: [(1, CfaOffset(16)), (1, Offset(Register(6), -16)), (4, CfaRegister(Register(6)))] }");
176     }
177 
create_multi_return_function(call_conv: CallConv) -> Function178     fn create_multi_return_function(call_conv: CallConv) -> Function {
179         let mut sig = Signature::new(call_conv);
180         sig.params.push(AbiParam::new(types::I32));
181         let mut func = Function::with_name_signature(ExternalName::user(0, 0), sig);
182 
183         let block0 = func.dfg.make_block();
184         let v0 = func.dfg.append_block_param(block0, types::I32);
185         let block1 = func.dfg.make_block();
186         let block2 = func.dfg.make_block();
187 
188         let mut pos = FuncCursor::new(&mut func);
189         pos.insert_block(block0);
190         pos.ins().brnz(v0, block2, &[]);
191         pos.ins().jump(block1, &[]);
192 
193         pos.insert_block(block1);
194         pos.ins().return_(&[]);
195 
196         pos.insert_block(block2);
197         pos.ins().return_(&[]);
198 
199         func
200     }
201 }
202