wasmer_compiler_singlepass/
unwind_winx64.rs1#[cfg(feature = "enable-serde")]
4use serde_derive::{Deserialize, Serialize};
5
6use crate::{
7 location::Reg,
8 unwind::{UnwindOps, UnwindRegister},
9 x64_decl::{GPR, XMM},
10};
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 = matches!(self, Self::SaveXmm { .. });
107 let (op_small, op_large) = if is_xmm {
108 (UnwindOperation::SaveXmm128, UnwindOperation::SaveXmm128Far)
109 } else {
110 (
111 UnwindOperation::SaveNonVolatileRegister,
112 UnwindOperation::SaveNonVolatileRegisterFar,
113 )
114 };
115 writer.write_u8(*instruction_offset);
116 let scaled_stack_offset = stack_offset / 16;
117 if scaled_stack_offset <= u16::MAX as u32 {
118 writer.write_u8((*reg << 4) | (op_small as u8));
119 writer.write_u16_le(scaled_stack_offset as u16);
120 } else {
121 writer.write_u8((*reg << 4) | (op_large as u8));
122 writer.write_u16_le(*stack_offset as u16);
123 writer.write_u16_le((stack_offset >> 16) as u16);
124 }
125 }
126 Self::StackAlloc {
127 instruction_offset,
128 size,
129 } => {
130 assert!(*size >= 8);
132 assert!((*size % 8) == 0);
133
134 writer.write_u8(*instruction_offset);
135 if *size <= SMALL_ALLOC_MAX_SIZE {
136 writer.write_u8(
137 ((((*size - 8) / 8) as u8) << 4) | UnwindOperation::SmallStackAlloc as u8,
138 );
139 } else if *size <= LARGE_ALLOC_16BIT_MAX_SIZE {
140 writer.write_u8(UnwindOperation::LargeStackAlloc as u8);
141 writer.write_u16_le((*size / 8) as u16);
142 } else {
143 writer.write_u8((1 << 4) | (UnwindOperation::LargeStackAlloc as u8));
144 writer.write_u32_le(*size);
145 }
146 }
147 Self::SetFPReg { instruction_offset } => {
148 writer.write_u8(*instruction_offset);
149 writer.write_u8(UnwindOperation::SetFPReg as u8);
150 }
151 }
152 }
153
154 fn node_count(&self) -> usize {
155 match self {
156 Self::StackAlloc { size, .. } => {
157 if *size <= SMALL_ALLOC_MAX_SIZE {
158 1
159 } else if *size <= LARGE_ALLOC_16BIT_MAX_SIZE {
160 2
161 } else {
162 3
163 }
164 }
165 Self::SaveXmm { stack_offset, .. } | Self::SaveReg { stack_offset, .. } => {
166 if *stack_offset <= u16::MAX as u32 {
167 2
168 } else {
169 3
170 }
171 }
172 _ => 1,
173 }
174 }
175}
176
177#[derive(Clone, Debug, PartialEq, Eq)]
182#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
183pub struct UnwindInfo {
184 pub(crate) flags: u8,
185 pub(crate) prologue_size: u8,
186 pub(crate) frame_register: Option<u8>,
187 pub(crate) frame_register_offset: u8,
188 pub(crate) unwind_codes: Vec<UnwindCode>,
189}
190
191impl UnwindInfo {
192 pub fn emit_size(&self) -> usize {
194 let node_count = self.node_count();
195
196 assert!(self.flags == 0);
198
199 4 + (node_count * 2) + if (node_count & 1) == 1 { 2 } else { 0 }
205 }
206
207 pub fn emit(&self, buf: &mut [u8]) {
211 const UNWIND_INFO_VERSION: u8 = 1;
212
213 let node_count = self.node_count();
214 assert!(node_count <= 256);
215
216 let mut writer = Writer::new(buf);
217
218 writer.write_u8((self.flags << 3) | UNWIND_INFO_VERSION);
219 writer.write_u8(self.prologue_size);
220 writer.write_u8(node_count as u8);
221
222 if let Some(reg) = self.frame_register {
223 writer.write_u8((self.frame_register_offset << 4) | reg);
224 } else {
225 writer.write_u8(0);
226 }
227
228 for code in self.unwind_codes.iter().rev() {
230 code.emit(&mut writer);
231 }
232
233 if (node_count & 1) == 1 {
235 writer.write_u16_le(0);
236 }
237
238 assert_eq!(writer.offset, self.emit_size());
240 }
241
242 fn node_count(&self) -> usize {
243 self.unwind_codes
244 .iter()
245 .fold(0, |nodes, c| nodes + c.node_count())
246 }
247}
248
249const UNWIND_RBP_REG: u8 = 5;
250
251pub(crate) fn create_unwind_info_from_insts(
252 insts: &[(usize, UnwindOps<GPR, XMM>)],
253) -> Option<UnwindInfo> {
254 let mut unwind_codes = vec![];
255 let mut frame_register_offset = 0;
256 let mut max_unwind_offset = 0;
257 for &(instruction_offset, ref inst) in insts {
258 let instruction_offset = ensure_unwind_offset(instruction_offset as u32)?;
259 match *inst {
260 UnwindOps::PushFP { .. } => {
261 unwind_codes.push(UnwindCode::PushRegister {
262 instruction_offset,
263 reg: UNWIND_RBP_REG,
264 });
265 }
266 UnwindOps::DefineNewFrame => {
267 frame_register_offset = ensure_unwind_offset(32)?;
268 unwind_codes.push(UnwindCode::SetFPReg { instruction_offset });
269 }
270 UnwindOps::SaveRegister { reg, bp_neg_offset } => match reg {
271 UnwindRegister::GPR(reg) => unwind_codes.push(UnwindCode::SaveReg {
272 instruction_offset,
273 reg: reg.into_index() as u8,
275 stack_offset: bp_neg_offset as u32,
276 }),
277 UnwindRegister::FPR(reg) => unwind_codes.push(UnwindCode::SaveXmm {
278 instruction_offset,
279 reg: reg.into_index() as u8,
281 stack_offset: bp_neg_offset as u32,
282 }),
283 },
284 UnwindOps::Push2Regs { .. } => {
285 unreachable!("no aarch64 on x64");
286 }
287 UnwindOps::SubtractFP { .. } => unreachable!(),
288 }
289 max_unwind_offset = instruction_offset;
290 }
291
292 Some(UnwindInfo {
293 flags: 0,
294 prologue_size: max_unwind_offset,
295 frame_register: Some(UNWIND_RBP_REG),
296 frame_register_offset,
297 unwind_codes,
298 })
299}
300
301fn ensure_unwind_offset(offset: u32) -> Option<u8> {
302 if offset > 255 {
303 panic!("function prologues cannot exceed 255 bytes in size for Windows x64");
304 }
305 Some(offset as u8)
306}