cranelift_codegen/isa/unwind/
winarm64.rs1use alloc::vec::Vec;
4#[cfg(feature = "enable-serde")]
5use serde_derive::{Deserialize, Serialize};
6
7use crate::binemit::CodeOffset;
8use crate::isa::unwind::UnwindInst;
9use crate::result::CodegenResult;
10
11use super::Writer;
12
13#[derive(Clone, Debug, PartialEq, Eq)]
18#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
19pub(crate) enum UnwindCode {
20 SaveReg {
22 reg: u8,
23 stack_offset: u16,
24 is_pair: bool,
25 },
26 SaveFReg {
28 reg: u8,
29 stack_offset: u16,
30 is_pair: bool,
31 },
32 SaveFpLrPair {
34 stack_offset: u16,
35 },
36 AllocS {
38 size: u16,
39 },
40 AllocM {
42 size: u16,
43 },
44 AllocL {
46 size: u32,
47 },
48 PacSignLr,
50 SetFp,
52 AddFp {
55 offset: u16,
56 },
57}
58
59#[derive(Clone, Debug, PartialEq, Eq)]
64#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
65pub struct UnwindInfo {
66 pub(crate) unwind_codes: Vec<UnwindCode>,
67}
68
69impl UnwindInfo {
70 pub fn code_words(&self) -> u8 {
72 let mut bytes = 0u16;
73 for code in self.unwind_codes.iter() {
74 let next_bytes = match code {
75 UnwindCode::SaveFpLrPair { .. }
76 | UnwindCode::AllocS { .. }
77 | UnwindCode::PacSignLr
78 | UnwindCode::SetFp => 1,
79 UnwindCode::SaveReg { .. }
80 | UnwindCode::SaveFReg { .. }
81 | UnwindCode::AllocM { .. }
82 | UnwindCode::AddFp { .. } => 2,
83 UnwindCode::AllocL { .. } => 4,
84 };
85 bytes = bytes.checked_add(next_bytes).unwrap();
86 }
87
88 bytes.div_ceil(4).try_into().unwrap()
89 }
90
91 pub fn emit(&self, buf: &mut [u8]) {
95 fn encode_stack_offset<const BITS: u8>(stack_offset: u16) -> u16 {
96 let encoded = (stack_offset / 8) - 1;
97 assert!(encoded < (1 << BITS), "Stack offset too large");
98 encoded
99 }
100
101 let mut writer = Writer::new(buf);
104 for code in self.unwind_codes.iter().rev() {
105 match code {
106 &UnwindCode::SaveReg {
107 reg,
108 stack_offset,
109 is_pair,
110 } => {
111 assert!(reg >= 19, "Can't save registers before X19");
112 let reg = u16::from(reg - 19);
113 let encoding = if is_pair {
114 let mut encoding = 0b11001100_00000000u16;
115 encoding |= reg << 6;
116 encoding |= encode_stack_offset::<6>(stack_offset);
117 encoding
118 } else {
119 let mut encoding = 0b11010100_00000000u16;
120 encoding |= reg << 5;
121 encoding |= encode_stack_offset::<5>(stack_offset);
122 encoding
123 };
124 writer.write_u16_be(encoding);
125 }
126 &UnwindCode::SaveFReg {
127 reg,
128 stack_offset,
129 is_pair,
130 } => {
131 assert!(reg >= 8, "Can't save registers before D8");
132 let reg = u16::from(reg - 8);
133 let encoding = if is_pair {
134 let mut encoding = 0b11011010_00000000u16;
135 encoding |= reg << 6;
136 encoding |= encode_stack_offset::<6>(stack_offset);
137 encoding
138 } else {
139 let mut encoding = 0b11011110_00000000u16;
140 encoding |= reg << 5;
141 encoding |= encode_stack_offset::<5>(stack_offset);
142 encoding
143 };
144 writer.write_u16_be(encoding);
145 }
146 &UnwindCode::SaveFpLrPair { stack_offset } => {
147 if stack_offset == 0 {
148 writer.write_u8(0b01000000);
149 } else {
150 let encoding = 0b10000000u8
151 | u8::try_from(encode_stack_offset::<6>(stack_offset)).unwrap();
152 writer.write_u8(encoding);
153 }
154 }
155 &UnwindCode::AllocS { size } => {
156 let encoding = size / 16;
158 assert!(encoding < (1 << 5), "Stack alloc size too large");
159 writer.write_u8(encoding.try_into().unwrap());
161 }
162 &UnwindCode::AllocM { size } => {
163 let mut encoding = size / 16;
165 assert!(encoding < (1 << 11), "Stack alloc size too large");
166 encoding |= 0b11000 << 11;
167 writer.write_u16_be(encoding);
168 }
169 &UnwindCode::AllocL { size } => {
170 let mut encoding = size / 16;
172 assert!(encoding < (1 << 24), "Stack alloc size too large");
173 encoding |= 0b11100000 << 24;
174 writer.write_u32_be(encoding);
175 }
176 UnwindCode::PacSignLr => {
177 writer.write_u8(0b11111100);
178 }
179 UnwindCode::SetFp => {
180 writer.write_u8(0b11100001);
181 }
182 &UnwindCode::AddFp { mut offset } => {
183 offset /= 8;
184 assert!(offset & !0xFF == 0, "Offset too large");
185 let encoding = (0b11100010 << 8) | offset;
186 writer.write_u16_be(encoding);
187 }
188 }
189 }
190 }
191}
192
193pub(crate) fn create_unwind_info_from_insts(
194 insts: &[(CodeOffset, UnwindInst)],
195) -> CodegenResult<UnwindInfo> {
196 let mut unwind_codes = vec![];
197 let mut last_stackalloc = None;
198 let mut last_clobber_offset = None;
199 for &(_, ref inst) in insts {
200 match inst {
201 &UnwindInst::PushFrameRegs { .. } => {
202 unwind_codes.push(UnwindCode::SaveFpLrPair { stack_offset: 16 });
203 unwind_codes.push(UnwindCode::SetFp);
204 }
205 &UnwindInst::DefineNewFrame {
206 offset_downward_to_clobbers,
207 ..
208 } => {
209 assert!(last_clobber_offset.is_none(), "More than one frame defined");
210 last_clobber_offset = Some(offset_downward_to_clobbers);
211
212 if let &Some(last_stackalloc) = &last_stackalloc {
215 assert!(last_stackalloc < (1u32 << 8) * 8);
216 unwind_codes.push(UnwindCode::AddFp {
217 offset: u16::try_from(last_stackalloc).unwrap(),
218 });
219 unwind_codes.push(UnwindCode::SaveFpLrPair { stack_offset: 0 });
220 unwind_codes.push(UnwindCode::SetFp);
221 }
222 }
223 &UnwindInst::StackAlloc { size } => {
224 last_stackalloc = Some(size);
225 assert!(size % 16 == 0, "Size must be a multiple of 16");
226 const SMALL_STACK_ALLOC_MAX: u32 = (1 << 5) * 16 - 1;
227 const MEDIUM_STACK_ALLOC_MIN: u32 = SMALL_STACK_ALLOC_MAX + 1;
228 const MEDIUM_STACK_ALLOC_MAX: u32 = (1 << 11) * 16 - 1;
229 const LARGE_STACK_ALLOC_MIN: u32 = MEDIUM_STACK_ALLOC_MAX + 1;
230 const LARGE_STACK_ALLOC_MAX: u32 = (1 << 24) * 16 - 1;
231 match size {
232 0..=SMALL_STACK_ALLOC_MAX => unwind_codes.push(UnwindCode::AllocS {
233 size: size.try_into().unwrap(),
234 }),
235 MEDIUM_STACK_ALLOC_MIN..=MEDIUM_STACK_ALLOC_MAX => {
236 unwind_codes.push(UnwindCode::AllocM {
237 size: size.try_into().unwrap(),
238 })
239 }
240 LARGE_STACK_ALLOC_MIN..=LARGE_STACK_ALLOC_MAX => {
241 unwind_codes.push(UnwindCode::AllocL { size })
242 }
243 _ => panic!("Stack allocation size too large"),
244 }
245 }
246 &UnwindInst::SaveReg {
247 clobber_offset,
248 reg,
249 } => {
250 let last_clobber_offset = last_clobber_offset.as_mut().expect("No frame defined");
254 if *last_clobber_offset > clobber_offset {
255 let stack_offset = *last_clobber_offset - clobber_offset;
256 *last_clobber_offset = clobber_offset;
257
258 assert!(stack_offset % 8 == 0, "Offset must be a multiple of 8");
259 match reg.class() {
260 regalloc2::RegClass::Int => {
261 let reg = reg.hw_enc();
262 if reg < 19 {
263 panic!("Can't save registers before X19");
264 }
265 unwind_codes.push(UnwindCode::SaveReg {
266 reg,
267 stack_offset: stack_offset.try_into().unwrap(),
268 is_pair: false,
269 });
270 }
271 regalloc2::RegClass::Float => {
272 let reg = reg.hw_enc();
273 if reg < 8 {
274 panic!("Can't save registers before D8");
275 }
276 unwind_codes.push(UnwindCode::SaveFReg {
277 reg,
278 stack_offset: stack_offset.try_into().unwrap(),
279 is_pair: false,
280 });
281 }
282 regalloc2::RegClass::Vector => unreachable!(),
283 }
284 } else {
285 let last_unwind_code = unwind_codes.last_mut().unwrap();
288 match last_unwind_code {
289 UnwindCode::SaveReg { is_pair, .. } => {
290 assert_eq!(reg.class(), regalloc2::RegClass::Int);
291 assert!(!*is_pair);
292 *is_pair = true;
293 }
294 UnwindCode::SaveFReg { is_pair, .. } => {
295 assert_eq!(reg.class(), regalloc2::RegClass::Float);
296 assert!(!*is_pair);
297 *is_pair = true;
298 }
299 _ => unreachable!("Previous code should have been a register save"),
300 }
301 }
302 }
303 &UnwindInst::RegStackOffset { .. } => {
304 unreachable!("only supported with DWARF");
305 }
306 &UnwindInst::Aarch64SetPointerAuth { return_addresses } => {
307 assert!(
308 return_addresses,
309 "Windows doesn't support explicitly disabling return address signing"
310 );
311 unwind_codes.push(UnwindCode::PacSignLr);
312 }
313 }
314 }
315
316 Ok(UnwindInfo { unwind_codes })
317}