wasmtime_environ/compile/
frame_table.rs1use crate::{
12 FrameInstPos, FrameStackShape, FrameStateSlotOffset, FrameTableDescriptorIndex, FrameValType,
13 FuncKey, ModulePC, StaticModuleIndex, WasmHeapTopType, WasmValType, prelude::*,
14};
15use object::{LittleEndian, U32};
16use std::collections::{HashMap, hash_map::Entry};
17
18pub struct FrameStateSlotBuilder {
20 func_key: FuncKey,
22
23 pointer_size: u32,
25
26 locals: Vec<(FrameValType, FrameStateSlotOffset)>,
28
29 stacks: Vec<(Option<FrameStackShape>, FrameValType, FrameStateSlotOffset)>,
31
32 stacks_dedup:
34 HashMap<(Option<FrameStackShape>, FrameValType, FrameStateSlotOffset), FrameStackShape>,
35
36 vmctx_size: u32,
38
39 locals_size: u32,
41
42 slot_size: u32,
44}
45
46impl From<WasmValType> for FrameValType {
47 fn from(ty: WasmValType) -> FrameValType {
48 match ty {
49 WasmValType::I32 => FrameValType::I32,
50 WasmValType::I64 => FrameValType::I64,
51 WasmValType::F32 => FrameValType::F32,
52 WasmValType::F64 => FrameValType::F64,
53 WasmValType::V128 => FrameValType::V128,
54 WasmValType::Ref(r) => match r.heap_type.top() {
55 WasmHeapTopType::Any => FrameValType::AnyRef,
56 WasmHeapTopType::Extern => FrameValType::ExternRef,
57 WasmHeapTopType::Func => FrameValType::FuncRef,
58 WasmHeapTopType::Exn => FrameValType::ExnRef,
59 WasmHeapTopType::Cont => FrameValType::ContRef,
60 },
61 }
62 }
63}
64
65impl FrameStateSlotBuilder {
66 pub fn new(func_key: FuncKey, pointer_size: u32) -> FrameStateSlotBuilder {
68 FrameStateSlotBuilder {
69 func_key,
70 pointer_size,
71 locals: vec![],
72 stacks: vec![],
73 stacks_dedup: HashMap::new(),
74 vmctx_size: pointer_size,
75 locals_size: 0,
76 slot_size: pointer_size,
77 }
78 }
79
80 pub fn add_local(&mut self, ty: FrameValType) -> FrameStateSlotOffset {
86 let offset = FrameStateSlotOffset(self.vmctx_size + self.locals_size);
89 let size = ty.storage_size(self.pointer_size);
90 self.locals_size += size;
91 self.slot_size += size;
92 self.locals.push((ty, offset));
93 offset
94 }
95
96 pub fn local_offset(&self, local: u32) -> FrameStateSlotOffset {
98 let index = usize::try_from(local).unwrap();
99 self.locals[index].1
100 }
101
102 pub fn push_stack(
105 &mut self,
106 parent: Option<FrameStackShape>,
107 ty: FrameValType,
108 ) -> (FrameStackShape, FrameStateSlotOffset) {
109 let offset = parent
110 .map(|parent| {
111 let (_, ty, offset) = self.stacks[parent.index()];
112 offset.add(ty.storage_size(self.pointer_size))
113 })
114 .unwrap_or(FrameStateSlotOffset(self.vmctx_size + self.locals_size));
118
119 self.slot_size = core::cmp::max(
120 self.slot_size,
121 offset.0 + ty.storage_size(self.pointer_size),
122 );
123
124 let shape = match self.stacks_dedup.entry((parent, ty, offset)) {
125 Entry::Occupied(o) => *o.get(),
126 Entry::Vacant(v) => {
127 let shape = FrameStackShape(u32::try_from(self.stacks.len()).unwrap());
128 self.stacks.push((parent, ty, offset));
129 *v.insert(shape)
130 }
131 };
132
133 (shape, offset)
134 }
135
136 pub fn stack_last_offset(&self, shape: FrameStackShape) -> FrameStateSlotOffset {
138 self.stacks[shape.index()].2
139 }
140
141 pub fn serialize(&self) -> Vec<u8> {
144 let mut buffer = vec![];
160 let (func_key_namespace, func_key_index) = self.func_key.into_parts();
161 buffer.extend_from_slice(&u32::to_le_bytes(func_key_namespace.into_raw()));
162 buffer.extend_from_slice(&u32::to_le_bytes(func_key_index.into_raw()));
163
164 buffer.extend_from_slice(&u32::to_le_bytes(u32::try_from(self.locals.len()).unwrap()));
165 buffer.extend_from_slice(&u32::to_le_bytes(u32::try_from(self.stacks.len()).unwrap()));
166
167 for (_, offset) in &self.locals {
168 buffer.extend_from_slice(&u32::to_le_bytes(offset.0));
169 }
170 for (parent, _, _) in &self.stacks {
171 let parent = parent.map(|p| p.0).unwrap_or(u32::MAX);
172 buffer.extend_from_slice(&u32::to_le_bytes(parent));
173 }
174 for (_, _, offset) in &self.stacks {
175 buffer.extend_from_slice(&u32::to_le_bytes(offset.0));
176 }
177 for (ty, _) in &self.locals {
178 buffer.push(*ty as u8);
179 }
180 for (_, ty, _) in &self.stacks {
181 buffer.push(*ty as u8);
182 }
183
184 buffer
185 }
186
187 pub fn size(&self) -> u32 {
189 self.slot_size
190 }
191}
192
193#[derive(Default)]
235pub struct FrameTableBuilder {
236 frame_descriptor_ranges: Vec<U32<LittleEndian>>,
239 frame_descriptor_data: Vec<u8>,
240
241 frame_descriptor_fp_offsets: Vec<U32<LittleEndian>>,
243
244 progpoint_pcs: Vec<U32<LittleEndian>>,
245 progpoint_descriptor_offsets: Vec<U32<LittleEndian>>,
246 progpoint_descriptor_data: Vec<U32<LittleEndian>>,
247
248 breakpoint_modules: Vec<U32<LittleEndian>>,
249 breakpoint_pcs: Vec<U32<LittleEndian>>,
250 breakpoint_patch_offsets: Vec<U32<LittleEndian>>,
251 breakpoint_patch_data_ends: Vec<U32<LittleEndian>>,
252
253 breakpoint_patch_data: Vec<u8>,
254
255 func_starts: Vec<U32<LittleEndian>>,
257}
258
259impl FrameTableBuilder {
260 pub fn add_frame_descriptor(
264 &mut self,
265 slot_to_fp_offset: u32,
266 data: &[u8],
267 ) -> FrameTableDescriptorIndex {
268 let start = u32::try_from(self.frame_descriptor_data.len()).unwrap();
269 self.frame_descriptor_data.extend(data.iter().cloned());
270 let end = u32::try_from(self.frame_descriptor_data.len()).unwrap();
271
272 let index = FrameTableDescriptorIndex(
273 u32::try_from(self.frame_descriptor_fp_offsets.len()).unwrap(),
274 );
275 self.frame_descriptor_fp_offsets
276 .push(U32::new(LittleEndian, slot_to_fp_offset));
277 self.frame_descriptor_ranges
278 .push(U32::new(LittleEndian, start));
279 self.frame_descriptor_ranges
280 .push(U32::new(LittleEndian, end));
281
282 index
283 }
284
285 pub fn add_func_start(&mut self, native_pc: u32) {
292 debug_assert!(
293 self.func_starts
294 .last()
295 .map_or(true, |last| last.get(LittleEndian) <= native_pc)
296 );
297 self.func_starts.push(U32::new(LittleEndian, native_pc));
298 }
299
300 pub fn add_program_point(
302 &mut self,
303 native_pc: u32,
304 pos: FrameInstPos,
305 frames: &[(ModulePC, FrameTableDescriptorIndex, FrameStackShape)],
308 ) {
309 let pc_and_pos = FrameInstPos::encode(native_pc, pos);
310 while let Some(last) = self.progpoint_pcs.last()
313 && last.get(LittleEndian) == pc_and_pos
314 {
315 self.progpoint_pcs.pop();
316 self.progpoint_descriptor_offsets.pop();
317 self.progpoint_descriptor_data
318 .truncate(self.progpoint_descriptor_data.len() - 3);
319 }
320
321 let start = u32::try_from(self.progpoint_descriptor_data.len()).unwrap();
322 self.progpoint_pcs.push(U32::new(LittleEndian, pc_and_pos));
323 self.progpoint_descriptor_offsets
324 .push(U32::new(LittleEndian, start));
325
326 for (i, &(wasm_pc, frame_descriptor, stack_shape)) in frames.iter().enumerate() {
327 let wasm_pc_raw = wasm_pc.raw();
328 debug_assert!(wasm_pc_raw < 0x8000_0000);
329 let not_last = i < (frames.len() - 1);
330 let wasm_pc_raw = wasm_pc_raw | if not_last { 0x8000_0000 } else { 0 };
331 self.progpoint_descriptor_data
332 .push(U32::new(LittleEndian, wasm_pc_raw));
333 self.progpoint_descriptor_data
334 .push(U32::new(LittleEndian, frame_descriptor.0));
335 self.progpoint_descriptor_data
336 .push(U32::new(LittleEndian, stack_shape.0));
337 }
338 }
339
340 pub fn add_breakpoint_patch(
344 &mut self,
345 module: StaticModuleIndex,
346 wasm_pc: ModulePC,
347 patch_start_native_pc: u32,
348 patch: &[u8],
349 ) {
350 self.breakpoint_modules
351 .push(U32::new(LittleEndian, module.as_u32()));
352 self.breakpoint_pcs
353 .push(U32::new(LittleEndian, wasm_pc.raw()));
354 self.breakpoint_patch_offsets
355 .push(U32::new(LittleEndian, patch_start_native_pc));
356 self.breakpoint_patch_data.extend(patch.iter().cloned());
357 let end = u32::try_from(self.breakpoint_patch_data.len()).unwrap();
358 self.breakpoint_patch_data_ends
359 .push(U32::new(LittleEndian, end));
360 }
361
362 pub fn serialize<F: FnMut(&[u8])>(&mut self, mut f: F) {
365 while self.frame_descriptor_data.len() & 3 != 0 {
368 self.frame_descriptor_data.push(0);
369 }
370
371 let num_frame_descriptors = u32::try_from(self.frame_descriptor_fp_offsets.len()).unwrap();
372 f(&num_frame_descriptors.to_le_bytes());
373 let num_prog_points = u32::try_from(self.progpoint_pcs.len()).unwrap();
374 f(&num_prog_points.to_le_bytes());
375 let num_breakpoints = u32::try_from(self.breakpoint_pcs.len()).unwrap();
376 f(&num_breakpoints.to_le_bytes());
377 let num_funcs = u32::try_from(self.func_starts.len()).unwrap();
378 f(&num_funcs.to_le_bytes());
379
380 let frame_descriptor_pool_length = u32::try_from(self.frame_descriptor_data.len()).unwrap();
381 f(&frame_descriptor_pool_length.to_le_bytes());
382 let progpoint_descriptor_pool_length =
383 u32::try_from(self.progpoint_descriptor_data.len()).unwrap();
384 f(&progpoint_descriptor_pool_length.to_le_bytes());
385 let breakpoint_patch_pool_length = u32::try_from(self.breakpoint_patch_data.len()).unwrap();
386 f(&breakpoint_patch_pool_length.to_le_bytes());
387
388 f(object::bytes_of_slice(&self.frame_descriptor_ranges));
389 f(object::bytes_of_slice(&self.frame_descriptor_fp_offsets));
390 f(object::bytes_of_slice(&self.progpoint_pcs));
391 f(object::bytes_of_slice(&self.progpoint_descriptor_offsets));
392 f(object::bytes_of_slice(&self.breakpoint_modules));
393 f(object::bytes_of_slice(&self.breakpoint_pcs));
394 f(object::bytes_of_slice(&self.breakpoint_patch_offsets));
395 f(object::bytes_of_slice(&self.breakpoint_patch_data_ends));
396 f(object::bytes_of_slice(&self.func_starts));
397 f(&self.frame_descriptor_data);
398 f(object::bytes_of_slice(&self.progpoint_descriptor_data));
399 f(&self.breakpoint_patch_data);
400 }
401}