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

1//! Unwind information for System V ABI (Riscv64).
2
3use crate::isa::riscv64::inst::regs;
4use crate::isa::unwind::systemv::RegisterMappingError;
5use crate::machinst::Reg;
6use gimli::{write::CommonInformationEntry, Encoding, Format, Register};
7use regalloc2::RegClass;
8
9/// Creates a new riscv64 common information entry (CIE).
10pub fn create_cie() -> CommonInformationEntry {
11    use gimli::write::CallFrameInstruction;
12
13    let mut entry = CommonInformationEntry::new(
14        Encoding {
15            address_size: 8,
16            format: Format::Dwarf32,
17            version: 1,
18        },
19        2,  // Code alignment factor
20        -8, // Data alignment factor
21        Register(regs::link_reg().to_real_reg().unwrap().hw_enc() as u16),
22    );
23
24    // Every frame will start with the call frame address (CFA) at SP
25    let sp = Register(regs::stack_reg().to_real_reg().unwrap().hw_enc().into());
26    entry.add_instruction(CallFrameInstruction::Cfa(sp, 0));
27
28    entry
29}
30
31/// Map Cranelift registers to their corresponding Gimli registers.
32pub fn map_reg(reg: Reg) -> Result<Register, RegisterMappingError> {
33    let reg_offset = match reg.class() {
34        RegClass::Int => 0,
35        RegClass::Float => 32,
36        RegClass::Vector => 64,
37    };
38
39    let reg = reg.to_real_reg().unwrap().hw_enc() as u16;
40    Ok(Register(reg_offset + reg))
41}
42
43pub(crate) struct RegisterMapper;
44
45impl crate::isa::unwind::systemv::RegisterMapper<Reg> for RegisterMapper {
46    fn map(&self, reg: Reg) -> Result<u16, RegisterMappingError> {
47        Ok(map_reg(reg)?.0)
48    }
49    fn fp(&self) -> Option<u16> {
50        Some(regs::fp_reg().to_real_reg().unwrap().hw_enc() as u16)
51    }
52    fn lr(&self) -> Option<u16> {
53        Some(regs::link_reg().to_real_reg().unwrap().hw_enc() as u16)
54    }
55    fn lr_offset(&self) -> Option<u32> {
56        Some(8)
57    }
58}
59
60#[cfg(test)]
61mod tests {
62    use crate::cursor::{Cursor, FuncCursor};
63
64    use crate::ir::{
65        types, AbiParam, Function, InstBuilder, Signature, StackSlotData, StackSlotKind,
66        UserFuncName,
67    };
68    use crate::isa::{lookup, CallConv};
69    use crate::settings::{builder, Flags};
70    use crate::Context;
71    use gimli::write::Address;
72    use target_lexicon::triple;
73
74    #[test]
75    fn test_simple_func() {
76        let isa = lookup(triple!("riscv64"))
77            .expect("expect riscv64 ISA")
78            .finish(Flags::new(builder()))
79            .expect("Creating compiler backend");
80
81        let mut context = Context::for_function(create_function(
82            CallConv::SystemV,
83            Some(StackSlotData::new(StackSlotKind::ExplicitSlot, 64, 0)),
84        ));
85
86        let code = context
87            .compile(&*isa, &mut Default::default())
88            .expect("expected compilation");
89
90        let fde = match code
91            .create_unwind_info(isa.as_ref())
92            .expect("can create unwind info")
93        {
94            Some(crate::isa::unwind::UnwindInfo::SystemV(info)) => {
95                info.to_fde(Address::Constant(1234))
96            }
97            _ => panic!("expected unwind information"),
98        };
99
100        assert_eq!(format!("{fde:?}"), "FrameDescriptionEntry { address: Constant(1234), length: 40, lsda: None, instructions: [(12, CfaOffset(16)), (12, Offset(Register(8), -16)), (12, Offset(Register(1), -8)), (16, CfaRegister(Register(8)))] }");
101    }
102
103    fn create_function(call_conv: CallConv, stack_slot: Option<StackSlotData>) -> Function {
104        let mut func =
105            Function::with_name_signature(UserFuncName::user(0, 0), Signature::new(call_conv));
106
107        let block0 = func.dfg.make_block();
108        let mut pos = FuncCursor::new(&mut func);
109        pos.insert_block(block0);
110        pos.ins().return_(&[]);
111
112        if let Some(stack_slot) = stack_slot {
113            func.sized_stack_slots.push(stack_slot);
114        }
115
116        func
117    }
118
119    #[test]
120    fn test_multi_return_func() {
121        let isa = lookup(triple!("riscv64"))
122            .expect("expect riscv64 ISA")
123            .finish(Flags::new(builder()))
124            .expect("Creating compiler backend");
125
126        let mut context = Context::for_function(create_multi_return_function(CallConv::SystemV));
127
128        let code = context
129            .compile(&*isa, &mut Default::default())
130            .expect("expected compilation");
131
132        let fde = match code
133            .create_unwind_info(isa.as_ref())
134            .expect("can create unwind info")
135        {
136            Some(crate::isa::unwind::UnwindInfo::SystemV(info)) => {
137                info.to_fde(Address::Constant(4321))
138            }
139            _ => panic!("expected unwind information"),
140        };
141
142        assert_eq!(
143            format!("{fde:?}"),
144            "FrameDescriptionEntry { address: Constant(4321), length: 16, lsda: None, instructions: [] }"
145        );
146    }
147
148    fn create_multi_return_function(call_conv: CallConv) -> Function {
149        let mut sig = Signature::new(call_conv);
150        sig.params.push(AbiParam::new(types::I32));
151        let mut func = Function::with_name_signature(UserFuncName::user(0, 0), sig);
152
153        let block0 = func.dfg.make_block();
154        let v0 = func.dfg.append_block_param(block0, types::I32);
155        let block1 = func.dfg.make_block();
156        let block2 = func.dfg.make_block();
157
158        let mut pos = FuncCursor::new(&mut func);
159        pos.insert_block(block0);
160        pos.ins().brif(v0, block2, &[], block1, &[]);
161
162        pos.insert_block(block1);
163        pos.ins().return_(&[]);
164
165        pos.insert_block(block2);
166        pos.ins().return_(&[]);
167
168        func
169    }
170}