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