cranelift_codegen/isa/s390x/inst/unwind/
systemv.rs

1//! Unwind information for System V ABI (s390x).
2
3use crate::isa::unwind::systemv::RegisterMappingError;
4use crate::machinst::{Reg, RegClass};
5use gimli::{Encoding, Format, Register, write::CommonInformationEntry};
6
7/// Creates a new s390x common information entry (CIE).
8pub 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.
29pub 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 VR_MAP: [gimli::Register; 32] = [
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        Register(68),
66        Register(72),
67        Register(69),
68        Register(73),
69        Register(70),
70        Register(74),
71        Register(71),
72        Register(75),
73        Register(76),
74        Register(80),
75        Register(77),
76        Register(81),
77        Register(78),
78        Register(82),
79        Register(79),
80        Register(83),
81    ];
82
83    match reg.class() {
84        RegClass::Int => Ok(GPR_MAP[reg.to_real_reg().unwrap().hw_enc() as usize]),
85        RegClass::Float => Ok(VR_MAP[reg.to_real_reg().unwrap().hw_enc() as usize]),
86        RegClass::Vector => unreachable!(),
87    }
88}
89
90pub(crate) struct RegisterMapper;
91
92impl crate::isa::unwind::systemv::RegisterMapper<Reg> for RegisterMapper {
93    fn map(&self, reg: Reg) -> Result<u16, RegisterMappingError> {
94        Ok(map_reg(reg)?.0)
95    }
96}
97
98#[cfg(test)]
99mod tests {
100    use crate::Context;
101    use crate::cursor::{Cursor, FuncCursor};
102    use crate::ir::{
103        AbiParam, Function, InstBuilder, Signature, StackSlotData, StackSlotKind, types,
104    };
105    use crate::isa::{CallConv, lookup};
106    use crate::settings::{Flags, builder};
107    use gimli::write::Address;
108    use target_lexicon::triple;
109
110    #[test]
111    fn test_simple_func() {
112        let isa = lookup(triple!("s390x"))
113            .expect("expect s390x ISA")
114            .finish(Flags::new(builder()))
115            .expect("Creating compiler backend");
116
117        let mut context = Context::for_function(create_function(
118            CallConv::SystemV,
119            Some(StackSlotData::new(StackSlotKind::ExplicitSlot, 64, 0)),
120        ));
121
122        let code = context
123            .compile(&*isa, &mut Default::default())
124            .expect("expected compilation");
125
126        let fde = match code
127            .create_unwind_info(isa.as_ref())
128            .expect("can create unwind info")
129        {
130            Some(crate::isa::unwind::UnwindInfo::SystemV(info)) => {
131                info.to_fde(Address::Constant(1234))
132            }
133            _ => panic!("expected unwind information"),
134        };
135
136        assert_eq!(
137            format!("{fde:?}"),
138            "FrameDescriptionEntry { address: Constant(1234), length: 10, lsda: None, instructions: [(4, CfaOffset(224))] }"
139        );
140    }
141
142    fn create_function(call_conv: CallConv, stack_slot: Option<StackSlotData>) -> Function {
143        let mut func = Function::with_name_signature(Default::default(), Signature::new(call_conv));
144
145        let block0 = func.dfg.make_block();
146        let mut pos = FuncCursor::new(&mut func);
147        pos.insert_block(block0);
148        pos.ins().return_(&[]);
149
150        if let Some(stack_slot) = stack_slot {
151            func.sized_stack_slots.push(stack_slot);
152        }
153
154        func
155    }
156
157    #[test]
158    fn test_multi_return_func() {
159        let isa = lookup(triple!("s390x"))
160            .expect("expect s390x ISA")
161            .finish(Flags::new(builder()))
162            .expect("Creating compiler backend");
163
164        let mut context = Context::for_function(create_multi_return_function(
165            CallConv::SystemV,
166            Some(StackSlotData::new(StackSlotKind::ExplicitSlot, 64, 0)),
167        ));
168
169        let code = context
170            .compile(&*isa, &mut Default::default())
171            .expect("expected compilation");
172
173        let fde = match code
174            .create_unwind_info(isa.as_ref())
175            .expect("can create unwind info")
176        {
177            Some(crate::isa::unwind::UnwindInfo::SystemV(info)) => {
178                info.to_fde(Address::Constant(4321))
179            }
180            _ => panic!("expected unwind information"),
181        };
182
183        assert_eq!(
184            format!("{fde:?}"),
185            "FrameDescriptionEntry { address: Constant(4321), length: 26, lsda: None, instructions: [(4, CfaOffset(224))] }"
186        );
187    }
188
189    fn create_multi_return_function(
190        call_conv: CallConv,
191        stack_slot: Option<StackSlotData>,
192    ) -> Function {
193        let mut sig = Signature::new(call_conv);
194        sig.params.push(AbiParam::new(types::I32));
195        let mut func = Function::with_name_signature(Default::default(), sig);
196
197        let block0 = func.dfg.make_block();
198        let v0 = func.dfg.append_block_param(block0, types::I32);
199        let block1 = func.dfg.make_block();
200        let block2 = func.dfg.make_block();
201
202        let mut pos = FuncCursor::new(&mut func);
203        pos.insert_block(block0);
204        pos.ins().brif(v0, block2, &[], block1, &[]);
205
206        pos.insert_block(block1);
207        pos.ins().return_(&[]);
208
209        pos.insert_block(block2);
210        pos.ins().return_(&[]);
211
212        if let Some(stack_slot) = stack_slot {
213            func.sized_stack_slots.push(stack_slot);
214        }
215
216        func
217    }
218}