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