1 //! Unwind information for System V ABI (s390x).
2 
3 use crate::isa::unwind::systemv::RegisterMappingError;
4 use gimli::{write::CommonInformationEntry, Encoding, Format, Register};
5 use regalloc::{Reg, RegClass};
6 
7 /// Creates a new s390x 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         Register(14), // Return address column - register %r14
20     );
21 
22     // Every frame will start with the call frame address (CFA) at %r15 + 160.
23     entry.add_instruction(CallFrameInstruction::Cfa(Register(15), 160));
24 
25     entry
26 }
27 
28 /// Map Cranelift registers to their corresponding Gimli registers.
map_reg(reg: Reg) -> Result<Register, RegisterMappingError>29 pub fn map_reg(reg: Reg) -> Result<Register, RegisterMappingError> {
30     const GPR_MAP: [gimli::Register; 16] = [
31         Register(0),
32         Register(1),
33         Register(2),
34         Register(3),
35         Register(4),
36         Register(5),
37         Register(6),
38         Register(7),
39         Register(8),
40         Register(9),
41         Register(10),
42         Register(11),
43         Register(12),
44         Register(13),
45         Register(14),
46         Register(15),
47     ];
48     const FPR_MAP: [gimli::Register; 16] = [
49         Register(16),
50         Register(20),
51         Register(17),
52         Register(21),
53         Register(18),
54         Register(22),
55         Register(19),
56         Register(23),
57         Register(24),
58         Register(28),
59         Register(25),
60         Register(29),
61         Register(26),
62         Register(30),
63         Register(27),
64         Register(31),
65     ];
66 
67     match reg.get_class() {
68         RegClass::I64 => Ok(GPR_MAP[reg.get_hw_encoding() as usize]),
69         RegClass::F64 => Ok(FPR_MAP[reg.get_hw_encoding() as usize]),
70         _ => Err(RegisterMappingError::UnsupportedRegisterBank("class?")),
71     }
72 }
73 
74 pub(crate) struct RegisterMapper;
75 
76 impl crate::isa::unwind::systemv::RegisterMapper<Reg> for RegisterMapper {
map(&self, reg: Reg) -> Result<u16, RegisterMappingError>77     fn map(&self, reg: Reg) -> Result<u16, RegisterMappingError> {
78         Ok(map_reg(reg)?.0)
79     }
sp(&self) -> u1680     fn sp(&self) -> u16 {
81         Register(15).0
82     }
83 }
84 
85 #[cfg(test)]
86 mod tests {
87     use crate::cursor::{Cursor, FuncCursor};
88     use crate::ir::{
89         types, AbiParam, ExternalName, Function, InstBuilder, Signature, StackSlotData,
90         StackSlotKind,
91     };
92     use crate::isa::{lookup, CallConv};
93     use crate::settings::{builder, Flags};
94     use crate::Context;
95     use gimli::write::Address;
96     use std::str::FromStr;
97     use target_lexicon::triple;
98 
99     #[test]
test_simple_func()100     fn test_simple_func() {
101         let isa = lookup(triple!("s390x"))
102             .expect("expect s390x ISA")
103             .finish(Flags::new(builder()));
104 
105         let mut context = Context::for_function(create_function(
106             CallConv::SystemV,
107             Some(StackSlotData::new(StackSlotKind::ExplicitSlot, 64)),
108         ));
109 
110         context.compile(&*isa).expect("expected compilation");
111 
112         let fde = match context
113             .create_unwind_info(isa.as_ref())
114             .expect("can create unwind info")
115         {
116             Some(crate::isa::unwind::UnwindInfo::SystemV(info)) => {
117                 info.to_fde(Address::Constant(1234))
118             }
119             _ => panic!("expected unwind information"),
120         };
121 
122         assert_eq!(format!("{:?}", fde), "FrameDescriptionEntry { address: Constant(1234), length: 10, lsda: None, instructions: [(4, CfaOffset(224))] }");
123     }
124 
create_function(call_conv: CallConv, stack_slot: Option<StackSlotData>) -> Function125     fn create_function(call_conv: CallConv, stack_slot: Option<StackSlotData>) -> Function {
126         let mut func =
127             Function::with_name_signature(ExternalName::user(0, 0), Signature::new(call_conv));
128 
129         let block0 = func.dfg.make_block();
130         let mut pos = FuncCursor::new(&mut func);
131         pos.insert_block(block0);
132         pos.ins().return_(&[]);
133 
134         if let Some(stack_slot) = stack_slot {
135             func.stack_slots.push(stack_slot);
136         }
137 
138         func
139     }
140 
141     #[test]
test_multi_return_func()142     fn test_multi_return_func() {
143         let isa = lookup(triple!("s390x"))
144             .expect("expect s390x ISA")
145             .finish(Flags::new(builder()));
146 
147         let mut context = Context::for_function(create_multi_return_function(
148             CallConv::SystemV,
149             Some(StackSlotData::new(StackSlotKind::ExplicitSlot, 64)),
150         ));
151 
152         context.compile(&*isa).expect("expected compilation");
153 
154         let fde = match context
155             .create_unwind_info(isa.as_ref())
156             .expect("can create unwind info")
157         {
158             Some(crate::isa::unwind::UnwindInfo::SystemV(info)) => {
159                 info.to_fde(Address::Constant(4321))
160             }
161             _ => panic!("expected unwind information"),
162         };
163 
164         assert_eq!(format!("{:?}", fde), "FrameDescriptionEntry { address: Constant(4321), length: 26, lsda: None, instructions: [(4, CfaOffset(224))] }");
165     }
166 
create_multi_return_function( call_conv: CallConv, stack_slot: Option<StackSlotData>, ) -> Function167     fn create_multi_return_function(
168         call_conv: CallConv,
169         stack_slot: Option<StackSlotData>,
170     ) -> Function {
171         let mut sig = Signature::new(call_conv);
172         sig.params.push(AbiParam::new(types::I32));
173         let mut func = Function::with_name_signature(ExternalName::user(0, 0), sig);
174 
175         let block0 = func.dfg.make_block();
176         let v0 = func.dfg.append_block_param(block0, types::I32);
177         let block1 = func.dfg.make_block();
178         let block2 = func.dfg.make_block();
179 
180         let mut pos = FuncCursor::new(&mut func);
181         pos.insert_block(block0);
182         pos.ins().brnz(v0, block2, &[]);
183         pos.ins().jump(block1, &[]);
184 
185         pos.insert_block(block1);
186         pos.ins().return_(&[]);
187 
188         pos.insert_block(block2);
189         pos.ins().return_(&[]);
190 
191         if let Some(stack_slot) = stack_slot {
192             func.stack_slots.push(stack_slot);
193         }
194 
195         func
196     }
197 }
198