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