cranelift_codegen/isa/unwind/
winx64.rs1use crate::result::{CodegenError, CodegenResult};
4use alloc::vec::Vec;
5use log::warn;
6#[cfg(feature = "enable-serde")]
7use serde_derive::{Deserialize, Serialize};
8
9use crate::binemit::CodeOffset;
10use crate::isa::unwind::UnwindInst;
11
12const SMALL_ALLOC_MAX_SIZE: u32 = 128;
14const LARGE_ALLOC_16BIT_MAX_SIZE: u32 = 524280;
16
17struct Writer<'a> {
18 buf: &'a mut [u8],
19 offset: usize,
20}
21
22impl<'a> Writer<'a> {
23 pub fn new(buf: &'a mut [u8]) -> Self {
24 Self { buf, offset: 0 }
25 }
26
27 fn write_u8(&mut self, v: u8) {
28 self.buf[self.offset] = v;
29 self.offset += 1;
30 }
31
32 fn write_u16_le(&mut self, v: u16) {
33 self.buf[self.offset..(self.offset + 2)].copy_from_slice(&v.to_le_bytes());
34 self.offset += 2;
35 }
36
37 fn write_u32_le(&mut self, v: u32) {
38 self.buf[self.offset..(self.offset + 4)].copy_from_slice(&v.to_le_bytes());
39 self.offset += 4;
40 }
41}
42
43#[allow(dead_code)]
49#[derive(Clone, Debug, PartialEq, Eq)]
50#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
51pub(crate) enum UnwindCode {
52 PushRegister {
53 instruction_offset: u8,
54 reg: u8,
55 },
56 SaveReg {
57 instruction_offset: u8,
58 reg: u8,
59 stack_offset: u32,
60 },
61 SaveXmm {
62 instruction_offset: u8,
63 reg: u8,
64 stack_offset: u32,
65 },
66 StackAlloc {
67 instruction_offset: u8,
68 size: u32,
69 },
70 SetFPReg {
71 instruction_offset: u8,
72 },
73}
74
75impl UnwindCode {
76 fn emit(&self, writer: &mut Writer) {
77 enum UnwindOperation {
78 PushNonvolatileRegister = 0,
79 LargeStackAlloc = 1,
80 SmallStackAlloc = 2,
81 SetFPReg = 3,
82 SaveNonVolatileRegister = 4,
83 SaveNonVolatileRegisterFar = 5,
84 SaveXmm128 = 8,
85 SaveXmm128Far = 9,
86 }
87
88 match self {
89 Self::PushRegister {
90 instruction_offset,
91 reg,
92 } => {
93 writer.write_u8(*instruction_offset);
94 writer.write_u8((*reg << 4) | (UnwindOperation::PushNonvolatileRegister as u8));
95 }
96 Self::SaveReg {
97 instruction_offset,
98 reg,
99 stack_offset,
100 }
101 | Self::SaveXmm {
102 instruction_offset,
103 reg,
104 stack_offset,
105 } => {
106 let is_xmm = match self {
107 Self::SaveXmm { .. } => true,
108 _ => false,
109 };
110 let (op_small, op_large) = if is_xmm {
111 (UnwindOperation::SaveXmm128, UnwindOperation::SaveXmm128Far)
112 } else {
113 (
114 UnwindOperation::SaveNonVolatileRegister,
115 UnwindOperation::SaveNonVolatileRegisterFar,
116 )
117 };
118 writer.write_u8(*instruction_offset);
119 let scaled_stack_offset = stack_offset / 16;
120 if scaled_stack_offset <= core::u16::MAX as u32 {
121 writer.write_u8((*reg << 4) | (op_small as u8));
122 writer.write_u16_le(scaled_stack_offset as u16);
123 } else {
124 writer.write_u8((*reg << 4) | (op_large as u8));
125 writer.write_u16_le(*stack_offset as u16);
126 writer.write_u16_le((stack_offset >> 16) as u16);
127 }
128 }
129 Self::StackAlloc {
130 instruction_offset,
131 size,
132 } => {
133 assert!(*size >= 8);
135 assert!((*size % 8) == 0);
136
137 writer.write_u8(*instruction_offset);
138 if *size <= SMALL_ALLOC_MAX_SIZE {
139 writer.write_u8(
140 ((((*size - 8) / 8) as u8) << 4) | UnwindOperation::SmallStackAlloc as u8,
141 );
142 } else if *size <= LARGE_ALLOC_16BIT_MAX_SIZE {
143 writer.write_u8(UnwindOperation::LargeStackAlloc as u8);
144 writer.write_u16_le((*size / 8) as u16);
145 } else {
146 writer.write_u8((1 << 4) | (UnwindOperation::LargeStackAlloc as u8));
147 writer.write_u32_le(*size);
148 }
149 }
150 Self::SetFPReg { instruction_offset } => {
151 writer.write_u8(*instruction_offset);
152 writer.write_u8(UnwindOperation::SetFPReg as u8);
153 }
154 }
155 }
156
157 fn node_count(&self) -> usize {
158 match self {
159 Self::StackAlloc { size, .. } => {
160 if *size <= SMALL_ALLOC_MAX_SIZE {
161 1
162 } else if *size <= LARGE_ALLOC_16BIT_MAX_SIZE {
163 2
164 } else {
165 3
166 }
167 }
168 Self::SaveXmm { stack_offset, .. } | Self::SaveReg { stack_offset, .. } => {
169 if *stack_offset <= core::u16::MAX as u32 {
170 2
171 } else {
172 3
173 }
174 }
175 _ => 1,
176 }
177 }
178}
179
180pub(crate) enum MappedRegister {
181 Int(u8),
182 Xmm(u8),
183}
184
185pub(crate) trait RegisterMapper<Reg> {
187 fn map(reg: Reg) -> MappedRegister;
189}
190
191#[derive(Clone, Debug, PartialEq, Eq)]
196#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
197pub struct UnwindInfo {
198 pub(crate) flags: u8,
199 pub(crate) prologue_size: u8,
200 pub(crate) frame_register: Option<u8>,
201 pub(crate) frame_register_offset: u8,
202 pub(crate) unwind_codes: Vec<UnwindCode>,
203}
204
205impl UnwindInfo {
206 pub fn emit_size(&self) -> usize {
208 let node_count = self.node_count();
209
210 assert!(self.flags == 0);
212
213 4 + (node_count * 2) + if (node_count & 1) == 1 { 2 } else { 0 }
219 }
220
221 pub fn emit(&self, buf: &mut [u8]) {
225 const UNWIND_INFO_VERSION: u8 = 1;
226
227 let node_count = self.node_count();
228 assert!(node_count <= 256);
229
230 let mut writer = Writer::new(buf);
231
232 writer.write_u8((self.flags << 3) | UNWIND_INFO_VERSION);
233 writer.write_u8(self.prologue_size);
234 writer.write_u8(node_count as u8);
235
236 if let Some(reg) = self.frame_register {
237 writer.write_u8((self.frame_register_offset << 4) | reg);
238 } else {
239 writer.write_u8(0);
240 }
241
242 for code in self.unwind_codes.iter().rev() {
244 code.emit(&mut writer);
245 }
246
247 if (node_count & 1) == 1 {
249 writer.write_u16_le(0);
250 }
251
252 assert_eq!(writer.offset, self.emit_size());
254 }
255
256 fn node_count(&self) -> usize {
257 self.unwind_codes
258 .iter()
259 .fold(0, |nodes, c| nodes + c.node_count())
260 }
261}
262
263const UNWIND_RBP_REG: u8 = 5;
264
265pub(crate) fn create_unwind_info_from_insts<MR: RegisterMapper<crate::machinst::Reg>>(
266 insts: &[(CodeOffset, UnwindInst)],
267) -> CodegenResult<UnwindInfo> {
268 let mut unwind_codes = vec![];
269 let mut frame_register_offset = 0;
270 let mut max_unwind_offset = 0;
271 for &(instruction_offset, ref inst) in insts {
272 let instruction_offset = ensure_unwind_offset(instruction_offset)?;
273 match inst {
274 &UnwindInst::PushFrameRegs { .. } => {
275 unwind_codes.push(UnwindCode::PushRegister {
276 instruction_offset,
277 reg: UNWIND_RBP_REG,
278 });
279 }
280 &UnwindInst::DefineNewFrame {
281 offset_downward_to_clobbers,
282 ..
283 } => {
284 frame_register_offset = ensure_unwind_offset(offset_downward_to_clobbers)?;
285 unwind_codes.push(UnwindCode::SetFPReg { instruction_offset });
286 }
287 &UnwindInst::StackAlloc { size } => {
288 unwind_codes.push(UnwindCode::StackAlloc {
289 instruction_offset,
290 size,
291 });
292 }
293 &UnwindInst::SaveReg {
294 clobber_offset,
295 reg,
296 } => match MR::map(reg.into()) {
297 MappedRegister::Int(reg) => {
298 unwind_codes.push(UnwindCode::SaveReg {
299 instruction_offset,
300 reg,
301 stack_offset: clobber_offset,
302 });
303 }
304 MappedRegister::Xmm(reg) => {
305 unwind_codes.push(UnwindCode::SaveXmm {
306 instruction_offset,
307 reg,
308 stack_offset: clobber_offset,
309 });
310 }
311 },
312 &UnwindInst::Aarch64SetPointerAuth { .. } => {
313 unreachable!("no aarch64 on x64");
314 }
315 }
316 max_unwind_offset = instruction_offset;
317 }
318
319 Ok(UnwindInfo {
320 flags: 0,
321 prologue_size: max_unwind_offset,
322 frame_register: Some(UNWIND_RBP_REG),
323 frame_register_offset,
324 unwind_codes,
325 })
326}
327
328fn ensure_unwind_offset(offset: u32) -> CodegenResult<u8> {
329 if offset > 255 {
330 warn!("function prologues cannot exceed 255 bytes in size for Windows x64");
331 return Err(CodegenError::CodeTooLarge);
332 }
333 Ok(offset as u8)
334}