1use crate::{
27 AnyfuncIndex, DefinedGlobalIndex, DefinedMemoryIndex, DefinedTableIndex, FuncIndex,
28 GlobalIndex, MemoryIndex, Module, TableIndex,
29};
30use cranelift_entity::packed_option::ReservedValue;
31use std::convert::TryFrom;
32use wasmtime_types::OwnedMemoryIndex;
33
34#[cfg(target_pointer_width = "32")]
35fn cast_to_u32(sz: usize) -> u32 {
36 u32::try_from(sz).unwrap()
37}
38#[cfg(target_pointer_width = "64")]
39fn cast_to_u32(sz: usize) -> u32 {
40 u32::try_from(sz).expect("overflow in cast from usize to u32")
41}
42
43#[inline]
45fn align(offset: u32, width: u32) -> u32 {
46 (offset + (width - 1)) / width * width
47}
48
49#[derive(Debug, Clone, Copy)]
52pub struct VMOffsets<P> {
53 pub ptr: P,
55 pub num_imported_functions: u32,
57 pub num_imported_tables: u32,
59 pub num_imported_memories: u32,
61 pub num_imported_globals: u32,
63 pub num_defined_tables: u32,
65 pub num_defined_memories: u32,
67 pub num_owned_memories: u32,
69 pub num_defined_globals: u32,
71 pub num_escaped_funcs: u32,
74
75 magic: u32,
77 runtime_limits: u32,
78 callee: u32,
79 epoch_ptr: u32,
80 externref_activations_table: u32,
81 store: u32,
82 builtin_functions: u32,
83 signature_ids: u32,
84 imported_functions: u32,
85 imported_tables: u32,
86 imported_memories: u32,
87 imported_globals: u32,
88 defined_tables: u32,
89 defined_memories: u32,
90 owned_memories: u32,
91 defined_globals: u32,
92 defined_anyfuncs: u32,
93 size: u32,
94}
95
96pub trait PtrSize {
98 fn size(&self) -> u8;
100
101 #[allow(clippy::erasing_op)]
103 #[inline]
104 fn vmcaller_checked_func_ref_func_ptr(&self) -> u8 {
105 0 * self.size()
106 }
107
108 #[allow(clippy::identity_op)]
110 #[inline]
111 fn vmcaller_checked_func_ref_type_index(&self) -> u8 {
112 1 * self.size()
113 }
114
115 #[inline]
117 fn vmcaller_checked_func_ref_vmctx(&self) -> u8 {
118 2 * self.size()
119 }
120
121 #[inline]
123 fn size_of_vmcaller_checked_func_ref(&self) -> u8 {
124 3 * self.size()
125 }
126
127 #[inline]
130 fn size_of_vmglobal_definition(&self) -> u8 {
131 16
132 }
133
134 #[inline]
138 fn vmruntime_limits_stack_limit(&self) -> u8 {
139 0
140 }
141
142 #[inline]
144 fn vmruntime_limits_fuel_consumed(&self) -> u8 {
145 self.size()
146 }
147
148 #[inline]
150 fn vmruntime_limits_epoch_deadline(&self) -> u8 {
151 self.vmruntime_limits_fuel_consumed() + 8 }
153
154 fn vmruntime_limits_last_wasm_exit_fp(&self) -> u8 {
156 self.vmruntime_limits_epoch_deadline() + 8
157 }
158
159 fn vmruntime_limits_last_wasm_exit_pc(&self) -> u8 {
161 self.vmruntime_limits_last_wasm_exit_fp() + self.size()
162 }
163
164 fn vmruntime_limits_last_wasm_entry_sp(&self) -> u8 {
166 self.vmruntime_limits_last_wasm_exit_pc() + self.size()
167 }
168
169 #[allow(clippy::erasing_op)]
173 #[inline]
174 fn vmmemory_definition_base(&self) -> u8 {
175 0 * self.size()
176 }
177
178 #[allow(clippy::identity_op)]
180 #[inline]
181 fn vmmemory_definition_current_length(&self) -> u8 {
182 1 * self.size()
183 }
184
185 #[inline]
187 fn size_of_vmmemory_definition(&self) -> u8 {
188 2 * self.size()
189 }
190
191 #[inline]
193 fn size_of_vmmemory_pointer(&self) -> u8 {
194 self.size()
195 }
196}
197
198pub struct HostPtr;
200
201impl PtrSize for HostPtr {
202 #[inline]
203 fn size(&self) -> u8 {
204 std::mem::size_of::<usize>() as u8
205 }
206}
207
208impl PtrSize for u8 {
209 #[inline]
210 fn size(&self) -> u8 {
211 *self
212 }
213}
214
215#[derive(Debug, Clone, Copy)]
217pub struct VMOffsetsFields<P> {
218 pub ptr: P,
220 pub num_imported_functions: u32,
222 pub num_imported_tables: u32,
224 pub num_imported_memories: u32,
226 pub num_imported_globals: u32,
228 pub num_defined_tables: u32,
230 pub num_defined_memories: u32,
232 pub num_owned_memories: u32,
234 pub num_defined_globals: u32,
236 pub num_escaped_funcs: u32,
239}
240
241impl<P: PtrSize> VMOffsets<P> {
242 pub fn new(ptr: P, module: &Module) -> Self {
244 let num_owned_memories = module
245 .memory_plans
246 .iter()
247 .skip(module.num_imported_memories)
248 .filter(|p| !p.1.memory.shared)
249 .count()
250 .try_into()
251 .unwrap();
252 VMOffsets::from(VMOffsetsFields {
253 ptr,
254 num_imported_functions: cast_to_u32(module.num_imported_funcs),
255 num_imported_tables: cast_to_u32(module.num_imported_tables),
256 num_imported_memories: cast_to_u32(module.num_imported_memories),
257 num_imported_globals: cast_to_u32(module.num_imported_globals),
258 num_defined_tables: cast_to_u32(module.table_plans.len() - module.num_imported_tables),
259 num_defined_memories: cast_to_u32(
260 module.memory_plans.len() - module.num_imported_memories,
261 ),
262 num_owned_memories,
263 num_defined_globals: cast_to_u32(module.globals.len() - module.num_imported_globals),
264 num_escaped_funcs: cast_to_u32(module.num_escaped_funcs),
265 })
266 }
267
268 #[inline]
270 pub fn pointer_size(&self) -> u8 {
271 self.ptr.size()
272 }
273
274 pub fn region_sizes(&self) -> impl Iterator<Item = (&str, u32)> {
279 macro_rules! calculate_sizes {
280 ($($name:ident: $desc:tt,)*) => {{
281 let VMOffsets {
282 ptr: _,
285 num_imported_functions: _,
286 num_imported_tables: _,
287 num_imported_memories: _,
288 num_imported_globals: _,
289 num_defined_tables: _,
290 num_defined_globals: _,
291 num_defined_memories: _,
292 num_owned_memories: _,
293 num_escaped_funcs: _,
294
295 size,
297
298 $($name,)*
301 } = *self;
302
303 let mut last = size;
307 $(
308 assert!($name <= last);
309 let tmp = $name;
310 let $name = last - $name;
311 last = tmp;
312 )*
313 assert_eq!(last, 0);
314 IntoIterator::into_iter([$(($desc, $name),)*])
315 }};
316 }
317
318 calculate_sizes! {
319 defined_anyfuncs: "module functions",
320 defined_globals: "defined globals",
321 owned_memories: "owned memories",
322 defined_memories: "defined memories",
323 defined_tables: "defined tables",
324 imported_globals: "imported globals",
325 imported_memories: "imported memories",
326 imported_tables: "imported tables",
327 imported_functions: "imported functions",
328 signature_ids: "module types",
329 builtin_functions: "jit builtin functions state",
330 store: "jit store state",
331 externref_activations_table: "jit host externref state",
332 epoch_ptr: "jit current epoch state",
333 callee: "callee function pointer",
334 runtime_limits: "jit runtime limits state",
335 magic: "magic value",
336 }
337 }
338}
339
340impl<P: PtrSize> From<VMOffsetsFields<P>> for VMOffsets<P> {
341 fn from(fields: VMOffsetsFields<P>) -> VMOffsets<P> {
342 let mut ret = Self {
343 ptr: fields.ptr,
344 num_imported_functions: fields.num_imported_functions,
345 num_imported_tables: fields.num_imported_tables,
346 num_imported_memories: fields.num_imported_memories,
347 num_imported_globals: fields.num_imported_globals,
348 num_defined_tables: fields.num_defined_tables,
349 num_defined_memories: fields.num_defined_memories,
350 num_owned_memories: fields.num_owned_memories,
351 num_defined_globals: fields.num_defined_globals,
352 num_escaped_funcs: fields.num_escaped_funcs,
353 magic: 0,
354 runtime_limits: 0,
355 callee: 0,
356 epoch_ptr: 0,
357 externref_activations_table: 0,
358 store: 0,
359 builtin_functions: 0,
360 signature_ids: 0,
361 imported_functions: 0,
362 imported_tables: 0,
363 imported_memories: 0,
364 imported_globals: 0,
365 defined_tables: 0,
366 defined_memories: 0,
367 owned_memories: 0,
368 defined_globals: 0,
369 defined_anyfuncs: 0,
370 size: 0,
371 };
372
373 #[inline]
378 fn cadd(count: u32, size: u32) -> u32 {
379 count.checked_add(size).unwrap()
380 }
381
382 #[inline]
383 fn cmul(count: u32, size: u8) -> u32 {
384 count.checked_mul(u32::from(size)).unwrap()
385 }
386
387 let mut next_field_offset = 0;
388
389 macro_rules! fields {
390 (size($field:ident) = $size:expr, $($rest:tt)*) => {
391 ret.$field = next_field_offset;
392 next_field_offset = cadd(next_field_offset, u32::from($size));
393 fields!($($rest)*);
394 };
395 (align($align:expr), $($rest:tt)*) => {
396 next_field_offset = align(next_field_offset, $align);
397 fields!($($rest)*);
398 };
399 () => {};
400 }
401
402 fields! {
403 size(magic) = 4u32,
404 align(u32::from(ret.ptr.size())),
405 size(runtime_limits) = ret.ptr.size(),
406 size(callee) = ret.ptr.size(),
407 size(epoch_ptr) = ret.ptr.size(),
408 size(externref_activations_table) = ret.ptr.size(),
409 size(store) = ret.ptr.size() * 2,
410 size(builtin_functions) = ret.pointer_size(),
411 size(signature_ids) = ret.ptr.size(),
412 size(imported_functions)
413 = cmul(ret.num_imported_functions, ret.size_of_vmfunction_import()),
414 size(imported_tables)
415 = cmul(ret.num_imported_tables, ret.size_of_vmtable_import()),
416 size(imported_memories)
417 = cmul(ret.num_imported_memories, ret.size_of_vmmemory_import()),
418 size(imported_globals)
419 = cmul(ret.num_imported_globals, ret.size_of_vmglobal_import()),
420 size(defined_tables)
421 = cmul(ret.num_defined_tables, ret.size_of_vmtable_definition()),
422 size(defined_memories)
423 = cmul(ret.num_defined_memories, ret.ptr.size_of_vmmemory_pointer()),
424 size(owned_memories)
425 = cmul(ret.num_owned_memories, ret.ptr.size_of_vmmemory_definition()),
426 align(16),
427 size(defined_globals)
428 = cmul(ret.num_defined_globals, ret.ptr.size_of_vmglobal_definition()),
429 size(defined_anyfuncs) = cmul(
430 ret.num_escaped_funcs,
431 ret.ptr.size_of_vmcaller_checked_func_ref(),
432 ),
433 }
434
435 ret.size = next_field_offset;
436
437 assert_eq!(ret.magic, 0);
441
442 return ret;
443 }
444}
445
446impl<P: PtrSize> VMOffsets<P> {
447 #[allow(clippy::erasing_op)]
449 #[inline]
450 pub fn vmfunction_import_body(&self) -> u8 {
451 0 * self.pointer_size()
452 }
453
454 #[allow(clippy::identity_op)]
456 #[inline]
457 pub fn vmfunction_import_vmctx(&self) -> u8 {
458 1 * self.pointer_size()
459 }
460
461 #[inline]
463 pub fn size_of_vmfunction_import(&self) -> u8 {
464 2 * self.pointer_size()
465 }
466}
467
468impl<P: PtrSize> VMOffsets<P> {
470 #[allow(clippy::identity_op)]
472 pub fn size_of_vmfunction_body_ptr(&self) -> u8 {
473 1 * self.pointer_size()
474 }
475}
476
477impl<P: PtrSize> VMOffsets<P> {
479 #[allow(clippy::erasing_op)]
481 #[inline]
482 pub fn vmtable_import_from(&self) -> u8 {
483 0 * self.pointer_size()
484 }
485
486 #[allow(clippy::identity_op)]
488 #[inline]
489 pub fn vmtable_import_vmctx(&self) -> u8 {
490 1 * self.pointer_size()
491 }
492
493 #[inline]
495 pub fn size_of_vmtable_import(&self) -> u8 {
496 2 * self.pointer_size()
497 }
498}
499
500impl<P: PtrSize> VMOffsets<P> {
502 #[allow(clippy::erasing_op)]
504 #[inline]
505 pub fn vmtable_definition_base(&self) -> u8 {
506 0 * self.pointer_size()
507 }
508
509 #[allow(clippy::identity_op)]
511 pub fn vmtable_definition_current_elements(&self) -> u8 {
512 1 * self.pointer_size()
513 }
514
515 #[inline]
517 pub fn size_of_vmtable_definition_current_elements(&self) -> u8 {
518 4
519 }
520
521 #[inline]
523 pub fn size_of_vmtable_definition(&self) -> u8 {
524 2 * self.pointer_size()
525 }
526}
527
528impl<P: PtrSize> VMOffsets<P> {
530 #[allow(clippy::erasing_op)]
532 #[inline]
533 pub fn vmmemory_import_from(&self) -> u8 {
534 0 * self.pointer_size()
535 }
536
537 #[allow(clippy::identity_op)]
539 #[inline]
540 pub fn vmmemory_import_vmctx(&self) -> u8 {
541 1 * self.pointer_size()
542 }
543
544 #[inline]
546 pub fn size_of_vmmemory_import(&self) -> u8 {
547 3 * self.pointer_size()
548 }
549}
550
551impl<P: PtrSize> VMOffsets<P> {
553 #[allow(clippy::erasing_op)]
555 #[inline]
556 pub fn vmglobal_import_from(&self) -> u8 {
557 0 * self.pointer_size()
558 }
559
560 #[allow(clippy::identity_op)]
562 #[inline]
563 pub fn size_of_vmglobal_import(&self) -> u8 {
564 1 * self.pointer_size()
565 }
566}
567
568impl<P: PtrSize> VMOffsets<P> {
570 #[inline]
572 pub fn size_of_vmshared_signature_index(&self) -> u8 {
573 4
574 }
575}
576
577impl<P: PtrSize> VMOffsets<P> {
579 #[inline]
581 pub fn vmctx_magic(&self) -> u32 {
582 self.magic
583 }
584
585 #[inline]
587 pub fn vmctx_runtime_limits(&self) -> u32 {
588 self.runtime_limits
589 }
590
591 pub fn vmctx_callee(&self) -> u32 {
593 self.callee
594 }
595
596 #[inline]
599 pub fn vmctx_epoch_ptr(&self) -> u32 {
600 self.epoch_ptr
601 }
602
603 #[inline]
605 pub fn vmctx_externref_activations_table(&self) -> u32 {
606 self.externref_activations_table
607 }
608
609 #[inline]
611 pub fn vmctx_store(&self) -> u32 {
612 self.store
613 }
614
615 #[inline]
617 pub fn vmctx_signature_ids_array(&self) -> u32 {
618 self.signature_ids
619 }
620
621 #[allow(clippy::erasing_op)]
623 #[inline]
624 pub fn vmctx_imported_functions_begin(&self) -> u32 {
625 self.imported_functions
626 }
627
628 #[allow(clippy::identity_op)]
630 #[inline]
631 pub fn vmctx_imported_tables_begin(&self) -> u32 {
632 self.imported_tables
633 }
634
635 #[inline]
637 pub fn vmctx_imported_memories_begin(&self) -> u32 {
638 self.imported_memories
639 }
640
641 #[inline]
643 pub fn vmctx_imported_globals_begin(&self) -> u32 {
644 self.imported_globals
645 }
646
647 #[inline]
649 pub fn vmctx_tables_begin(&self) -> u32 {
650 self.defined_tables
651 }
652
653 #[inline]
655 pub fn vmctx_memories_begin(&self) -> u32 {
656 self.defined_memories
657 }
658
659 #[inline]
661 pub fn vmctx_owned_memories_begin(&self) -> u32 {
662 self.owned_memories
663 }
664
665 #[inline]
667 pub fn vmctx_globals_begin(&self) -> u32 {
668 self.defined_globals
669 }
670
671 #[inline]
673 pub fn vmctx_anyfuncs_begin(&self) -> u32 {
674 self.defined_anyfuncs
675 }
676
677 #[inline]
679 pub fn vmctx_builtin_functions(&self) -> u32 {
680 self.builtin_functions
681 }
682
683 #[inline]
685 pub fn size_of_vmctx(&self) -> u32 {
686 self.size
687 }
688
689 #[inline]
691 pub fn vmctx_vmfunction_import(&self, index: FuncIndex) -> u32 {
692 assert!(index.as_u32() < self.num_imported_functions);
693 self.vmctx_imported_functions_begin()
694 + index.as_u32() * u32::from(self.size_of_vmfunction_import())
695 }
696
697 #[inline]
699 pub fn vmctx_vmtable_import(&self, index: TableIndex) -> u32 {
700 assert!(index.as_u32() < self.num_imported_tables);
701 self.vmctx_imported_tables_begin()
702 + index.as_u32() * u32::from(self.size_of_vmtable_import())
703 }
704
705 #[inline]
707 pub fn vmctx_vmmemory_import(&self, index: MemoryIndex) -> u32 {
708 assert!(index.as_u32() < self.num_imported_memories);
709 self.vmctx_imported_memories_begin()
710 + index.as_u32() * u32::from(self.size_of_vmmemory_import())
711 }
712
713 #[inline]
715 pub fn vmctx_vmglobal_import(&self, index: GlobalIndex) -> u32 {
716 assert!(index.as_u32() < self.num_imported_globals);
717 self.vmctx_imported_globals_begin()
718 + index.as_u32() * u32::from(self.size_of_vmglobal_import())
719 }
720
721 #[inline]
723 pub fn vmctx_vmtable_definition(&self, index: DefinedTableIndex) -> u32 {
724 assert!(index.as_u32() < self.num_defined_tables);
725 self.vmctx_tables_begin() + index.as_u32() * u32::from(self.size_of_vmtable_definition())
726 }
727
728 #[inline]
730 pub fn vmctx_vmmemory_pointer(&self, index: DefinedMemoryIndex) -> u32 {
731 assert!(index.as_u32() < self.num_defined_memories);
732 self.vmctx_memories_begin()
733 + index.as_u32() * u32::from(self.ptr.size_of_vmmemory_pointer())
734 }
735
736 #[inline]
738 pub fn vmctx_vmmemory_definition(&self, index: OwnedMemoryIndex) -> u32 {
739 assert!(index.as_u32() < self.num_owned_memories);
740 self.vmctx_owned_memories_begin()
741 + index.as_u32() * u32::from(self.ptr.size_of_vmmemory_definition())
742 }
743
744 #[inline]
746 pub fn vmctx_vmglobal_definition(&self, index: DefinedGlobalIndex) -> u32 {
747 assert!(index.as_u32() < self.num_defined_globals);
748 self.vmctx_globals_begin()
749 + index.as_u32() * u32::from(self.ptr.size_of_vmglobal_definition())
750 }
751
752 #[inline]
755 pub fn vmctx_anyfunc(&self, index: AnyfuncIndex) -> u32 {
756 assert!(!index.is_reserved_value());
757 assert!(index.as_u32() < self.num_escaped_funcs);
758 self.vmctx_anyfuncs_begin()
759 + index.as_u32() * u32::from(self.ptr.size_of_vmcaller_checked_func_ref())
760 }
761
762 #[inline]
764 pub fn vmctx_vmfunction_import_body(&self, index: FuncIndex) -> u32 {
765 self.vmctx_vmfunction_import(index) + u32::from(self.vmfunction_import_body())
766 }
767
768 #[inline]
770 pub fn vmctx_vmfunction_import_vmctx(&self, index: FuncIndex) -> u32 {
771 self.vmctx_vmfunction_import(index) + u32::from(self.vmfunction_import_vmctx())
772 }
773
774 #[inline]
776 pub fn vmctx_vmtable_import_from(&self, index: TableIndex) -> u32 {
777 self.vmctx_vmtable_import(index) + u32::from(self.vmtable_import_from())
778 }
779
780 #[inline]
782 pub fn vmctx_vmtable_definition_base(&self, index: DefinedTableIndex) -> u32 {
783 self.vmctx_vmtable_definition(index) + u32::from(self.vmtable_definition_base())
784 }
785
786 #[inline]
788 pub fn vmctx_vmtable_definition_current_elements(&self, index: DefinedTableIndex) -> u32 {
789 self.vmctx_vmtable_definition(index) + u32::from(self.vmtable_definition_current_elements())
790 }
791
792 #[inline]
794 pub fn vmctx_vmmemory_import_from(&self, index: MemoryIndex) -> u32 {
795 self.vmctx_vmmemory_import(index) + u32::from(self.vmmemory_import_from())
796 }
797
798 #[inline]
800 pub fn vmctx_vmmemory_import_vmctx(&self, index: MemoryIndex) -> u32 {
801 self.vmctx_vmmemory_import(index) + u32::from(self.vmmemory_import_vmctx())
802 }
803
804 #[inline]
806 pub fn vmctx_vmmemory_definition_base(&self, index: OwnedMemoryIndex) -> u32 {
807 self.vmctx_vmmemory_definition(index) + u32::from(self.ptr.vmmemory_definition_base())
808 }
809
810 #[inline]
812 pub fn vmctx_vmmemory_definition_current_length(&self, index: OwnedMemoryIndex) -> u32 {
813 self.vmctx_vmmemory_definition(index)
814 + u32::from(self.ptr.vmmemory_definition_current_length())
815 }
816
817 #[inline]
819 pub fn vmctx_vmglobal_import_from(&self, index: GlobalIndex) -> u32 {
820 self.vmctx_vmglobal_import(index) + u32::from(self.vmglobal_import_from())
821 }
822}
823
824impl<P: PtrSize> VMOffsets<P> {
826 #[inline]
828 pub fn vm_extern_data_ref_count(&self) -> u32 {
829 0
830 }
831}
832
833impl<P: PtrSize> VMOffsets<P> {
835 #[inline]
837 pub fn vm_extern_ref_activation_table_next(&self) -> u32 {
838 0
839 }
840
841 #[inline]
843 pub fn vm_extern_ref_activation_table_end(&self) -> u32 {
844 self.pointer_size().into()
845 }
846}
847
848pub const VM_HOST_FUNC_MAGIC: u32 = u32::from_le_bytes(*b"host");
853
854#[cfg(test)]
855mod tests {
856 use crate::vmoffsets::align;
857
858 #[test]
859 fn alignment() {
860 fn is_aligned(x: u32) -> bool {
861 x % 16 == 0
862 }
863 assert!(is_aligned(align(0, 16)));
864 assert!(is_aligned(align(32, 16)));
865 assert!(is_aligned(align(33, 16)));
866 assert!(is_aligned(align(31, 16)));
867 }
868}