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