1#![deny(rustdoc::broken_intra_doc_links)]
8
9use crate::{
10 FunctionIndex, GlobalIndex, LocalGlobalIndex, LocalMemoryIndex, LocalTableIndex, MemoryIndex,
11 ModuleInfo, TableIndex, entity::EntityRef,
12};
13use more_asserts::assert_lt;
14use std::convert::TryFrom;
15
16#[derive(Copy, Clone, Debug)]
18pub struct VMBuiltinFunctionIndex(u32);
19
20impl VMBuiltinFunctionIndex {
21 pub const fn get_memory32_grow_index() -> Self {
23 Self(0)
24 }
25 pub const fn get_imported_memory32_grow_index() -> Self {
27 Self(1)
28 }
29 pub const fn get_memory32_size_index() -> Self {
31 Self(2)
32 }
33 pub const fn get_imported_memory32_size_index() -> Self {
35 Self(3)
36 }
37 pub const fn get_table_copy_index() -> Self {
40 Self(4)
41 }
42 pub const fn get_table_init_index() -> Self {
44 Self(5)
45 }
46 pub const fn get_elem_drop_index() -> Self {
48 Self(6)
49 }
50 pub const fn get_memory_copy_index() -> Self {
52 Self(7)
53 }
54 pub const fn get_memory_fill_index() -> Self {
56 Self(8)
57 }
58 pub const fn get_imported_memory_fill_index() -> Self {
60 Self(9)
61 }
62 pub const fn get_memory_init_index() -> Self {
64 Self(10)
65 }
66 pub const fn get_data_drop_index() -> Self {
68 Self(11)
69 }
70 pub const fn get_raise_trap_index() -> Self {
72 Self(12)
73 }
74 pub const fn get_table_size_index() -> Self {
76 Self(13)
77 }
78 pub const fn get_imported_table_size_index() -> Self {
80 Self(14)
81 }
82 pub const fn get_table_grow_index() -> Self {
84 Self(15)
85 }
86 pub const fn get_imported_table_grow_index() -> Self {
88 Self(16)
89 }
90 pub const fn get_table_get_index() -> Self {
92 Self(17)
93 }
94 pub const fn get_imported_table_get_index() -> Self {
96 Self(18)
97 }
98 pub const fn get_table_set_index() -> Self {
100 Self(19)
101 }
102 pub const fn get_imported_table_set_index() -> Self {
104 Self(20)
105 }
106 pub const fn get_func_ref_index() -> Self {
108 Self(21)
109 }
110 pub const fn get_table_fill_index() -> Self {
112 Self(22)
113 }
114 pub const fn get_memory_atomic_wait32_index() -> Self {
116 Self(23)
117 }
118 pub const fn get_imported_memory_atomic_wait32_index() -> Self {
120 Self(24)
121 }
122 pub const fn get_memory_atomic_wait64_index() -> Self {
124 Self(25)
125 }
126 pub const fn get_imported_memory_atomic_wait64_index() -> Self {
128 Self(26)
129 }
130 pub const fn get_memory_atomic_notify_index() -> Self {
132 Self(27)
133 }
134
135 pub const fn get_imported_memory_atomic_notify_index() -> Self {
137 Self(28)
138 }
139
140 pub const fn get_imported_debug_usize_index() -> Self {
142 Self(29)
143 }
144
145 pub const fn get_imported_debug_str_index() -> Self {
147 Self(30)
148 }
149
150 pub const fn get_imported_personality2_index() -> Self {
152 Self(31)
153 }
154
155 pub const fn get_imported_alloc_exception_index() -> Self {
157 Self(32)
158 }
159
160 pub const fn get_imported_throw_index() -> Self {
162 Self(33)
163 }
164
165 pub const fn get_imported_read_exnref_index() -> Self {
167 Self(34)
168 }
169
170 pub const fn get_imported_exception_into_exnref_index() -> Self {
172 Self(35)
173 }
174
175 pub const fn builtin_functions_total_number() -> u32 {
177 36
178 }
179
180 pub const fn index(self) -> u32 {
182 self.0
183 }
184}
185
186#[cfg(target_pointer_width = "32")]
187fn cast_to_u32(sz: usize) -> u32 {
188 u32::try_from(sz).unwrap()
189}
190#[cfg(target_pointer_width = "64")]
191fn cast_to_u32(sz: usize) -> u32 {
192 u32::try_from(sz).expect("overflow in cast from usize to u32")
193}
194
195#[inline]
197const fn align(offset: u32, width: u32) -> u32 {
198 offset.div_ceil(width) * width
199}
200
201#[derive(Clone, Debug)]
204pub struct VMOffsets {
205 pointer_size: u8,
207 num_imported_functions: u32,
209 num_imported_tables: u32,
211 num_imported_memories: u32,
213 num_tag_ids: u32,
215 num_imported_globals: u32,
217 num_local_tables: u32,
219 num_local_memories: u32,
221 num_local_globals: u32,
223 local_fixed_funcref_table_offsets: Vec<Option<u32>>,
226 size_of_local_fixed_funcref_tables: u32,
228
229 vmctx_imported_functions_begin: u32,
230 vmctx_imported_tables_begin: u32,
231 vmctx_imported_memories_begin: u32,
232 vmctx_tag_ids_begin: u32,
233 vmctx_imported_globals_begin: u32,
234 vmctx_tables_begin: u32,
235 vmctx_fixed_funcref_tables_begin: u32,
236 vmctx_memories_begin: u32,
237 vmctx_globals_begin: u32,
238 vmctx_builtin_functions_begin: u32,
239 vmctx_trap_handler_begin: u32,
240 vmctx_gas_limiter_pointer: u32,
241 vmctx_stack_limit_begin: u32,
242 vmctx_stack_limit_initial_begin: u32,
243 size_of_vmctx: u32,
244}
245
246impl VMOffsets {
247 pub fn new(pointer_size: u8, module: &ModuleInfo) -> Self {
249 let mut local_fixed_funcref_table_offsets =
250 vec![None; module.tables.len() - module.num_imported_tables];
251 let mut size_of_local_fixed_funcref_tables: u32 = 0;
252 let size_of_vmcaller_checked_anyfunc = 4 * u32::from(pointer_size);
254
255 for (table_index, table) in module.tables.iter() {
256 if let Some(local_table_index) = module.local_table_index(table_index)
257 && table.is_fixed_funcref_table()
258 {
259 local_fixed_funcref_table_offsets[local_table_index.index()] =
260 Some(size_of_local_fixed_funcref_tables);
261 size_of_local_fixed_funcref_tables = size_of_local_fixed_funcref_tables
262 .checked_add(
263 table
264 .minimum
265 .checked_mul(size_of_vmcaller_checked_anyfunc)
266 .unwrap(),
267 )
268 .unwrap();
269 }
270 }
271
272 let mut ret = Self {
273 pointer_size,
274 num_imported_functions: cast_to_u32(module.num_imported_functions),
275 num_imported_tables: cast_to_u32(module.num_imported_tables),
276 num_imported_memories: cast_to_u32(module.num_imported_memories),
277 num_tag_ids: cast_to_u32(module.tags.len()),
278 num_imported_globals: cast_to_u32(module.num_imported_globals),
279 num_local_tables: cast_to_u32(module.tables.len()),
280 num_local_memories: cast_to_u32(module.memories.len()),
281 num_local_globals: cast_to_u32(module.globals.len()),
282 local_fixed_funcref_table_offsets,
283 size_of_local_fixed_funcref_tables,
284 vmctx_imported_functions_begin: 0,
285 vmctx_imported_tables_begin: 0,
286 vmctx_imported_memories_begin: 0,
287 vmctx_tag_ids_begin: 0,
288 vmctx_imported_globals_begin: 0,
289 vmctx_tables_begin: 0,
290 vmctx_fixed_funcref_tables_begin: 0,
291 vmctx_memories_begin: 0,
292 vmctx_globals_begin: 0,
293 vmctx_builtin_functions_begin: 0,
294 vmctx_trap_handler_begin: 0,
295 vmctx_gas_limiter_pointer: 0,
296 vmctx_stack_limit_begin: 0,
297 vmctx_stack_limit_initial_begin: 0,
298 size_of_vmctx: 0,
299 };
300 ret.precompute();
301 ret
302 }
303
304 pub fn new_for_trampolines(pointer_size: u8) -> Self {
309 Self {
310 pointer_size,
311 num_imported_functions: 0,
312 num_imported_tables: 0,
313 num_imported_memories: 0,
314 num_tag_ids: 0,
315 num_imported_globals: 0,
316 num_local_tables: 0,
317 num_local_memories: 0,
318 num_local_globals: 0,
319 local_fixed_funcref_table_offsets: Vec::new(),
320 size_of_local_fixed_funcref_tables: 0,
321 vmctx_imported_functions_begin: 0,
322 vmctx_imported_tables_begin: 0,
323 vmctx_imported_memories_begin: 0,
324 vmctx_tag_ids_begin: 0,
325 vmctx_imported_globals_begin: 0,
326 vmctx_tables_begin: 0,
327 vmctx_fixed_funcref_tables_begin: 0,
328 vmctx_memories_begin: 0,
329 vmctx_globals_begin: 0,
330 vmctx_builtin_functions_begin: 0,
331 vmctx_trap_handler_begin: 0,
332 vmctx_gas_limiter_pointer: 0,
333 vmctx_stack_limit_begin: 0,
334 vmctx_stack_limit_initial_begin: 0,
335 size_of_vmctx: 0,
336 }
337 }
338
339 pub fn num_local_tables(&self) -> u32 {
341 self.num_local_tables
342 }
343
344 pub fn num_local_memories(&self) -> u32 {
346 self.num_local_memories
347 }
348
349 pub fn num_local_globals(&self) -> u32 {
351 self.num_local_globals
352 }
353
354 fn precompute(&mut self) {
355 fn offset_by(base: u32, num_items: u32, item_size: u32) -> u32 {
357 base.checked_add(num_items.checked_mul(item_size).unwrap())
358 .unwrap()
359 }
360 let pointer_size = self.pointer_size as u32;
364 let offset_by_aligned = |base: u32, num_items: u32, item_size: u32| -> u32 {
365 align(
366 base.checked_add(num_items.checked_mul(item_size).unwrap())
367 .unwrap(),
368 pointer_size,
369 )
370 };
371
372 self.vmctx_imported_functions_begin = 0;
373 self.vmctx_imported_tables_begin = offset_by_aligned(
374 self.vmctx_imported_functions_begin,
375 self.num_imported_functions,
376 u32::from(self.size_of_vmfunction_import()),
377 );
378 self.vmctx_imported_memories_begin = offset_by_aligned(
379 self.vmctx_imported_tables_begin,
380 self.num_imported_tables,
381 u32::from(self.size_of_vmtable_import()),
382 );
383
384 self.vmctx_tag_ids_begin = offset_by_aligned(
385 self.vmctx_imported_memories_begin,
386 self.num_imported_memories,
387 u32::from(self.size_of_vmmemory_import()),
388 );
389
390 self.vmctx_imported_globals_begin = offset_by_aligned(
391 self.vmctx_tag_ids_begin,
392 self.num_tag_ids,
393 u32::from(self.size_of_vmshared_tag_index()),
394 );
395
396 self.vmctx_tables_begin = offset_by_aligned(
397 self.vmctx_imported_globals_begin,
398 self.num_imported_globals,
399 u32::from(self.size_of_vmglobal_import()),
400 );
401 self.vmctx_fixed_funcref_tables_begin = offset_by_aligned(
402 self.vmctx_tables_begin,
403 self.num_local_tables,
404 u32::from(self.size_of_vmtable_definition()),
405 );
406 self.vmctx_memories_begin = align(
407 self.vmctx_fixed_funcref_tables_begin
408 .checked_add(self.size_of_local_fixed_funcref_tables)
409 .unwrap(),
410 pointer_size,
411 );
412 self.vmctx_globals_begin = align(
413 offset_by(
414 self.vmctx_memories_begin,
415 self.num_local_memories,
416 u32::from(self.size_of_vmmemory_definition()),
417 ),
418 16,
419 );
420 self.vmctx_builtin_functions_begin = offset_by(
421 self.vmctx_globals_begin,
422 self.num_local_globals,
423 u32::from(self.size_of_vmglobal_local()),
424 );
425 self.vmctx_trap_handler_begin = offset_by(
426 self.vmctx_builtin_functions_begin,
427 VMBuiltinFunctionIndex::builtin_functions_total_number(),
428 u32::from(self.pointer_size),
429 );
430 self.vmctx_gas_limiter_pointer = offset_by(
431 self.vmctx_trap_handler_begin,
432 1,
433 u32::from(self.pointer_size),
434 );
435 self.vmctx_stack_limit_begin = offset_by(
436 self.vmctx_gas_limiter_pointer,
437 1,
438 u32::from(self.pointer_size),
439 );
440 self.vmctx_stack_limit_initial_begin = self.vmctx_stack_limit_begin.checked_add(4).unwrap();
441 self.size_of_vmctx = self.vmctx_stack_limit_begin.checked_add(4).unwrap();
442 }
443}
444
445impl VMOffsets {
447 #[allow(clippy::erasing_op)]
449 pub const fn vmfunction_import_body(&self) -> u8 {
450 0 * self.pointer_size
451 }
452
453 #[allow(clippy::identity_op)]
455 pub const fn vmfunction_import_vmctx(&self) -> u8 {
456 1 * self.pointer_size
457 }
458
459 pub const fn vmfunction_import_handle(&self) -> u8 {
461 2 * self.pointer_size
462 }
463
464 pub const fn vmfunction_import_include_m0_param(&self) -> u8 {
466 3 * self.pointer_size
467 }
468
469 pub const fn size_of_vmfunction_import(&self) -> u8 {
471 4 * self.pointer_size
472 }
473}
474
475impl VMOffsets {
477 #[allow(clippy::erasing_op)]
479 pub const fn vmdynamicfunction_import_context_address(&self) -> u8 {
480 0 * self.pointer_size
481 }
482
483 #[allow(clippy::identity_op)]
485 pub const fn vmdynamicfunction_import_context_ctx(&self) -> u8 {
486 1 * self.pointer_size
487 }
488
489 pub const fn size_of_vmdynamicfunction_import_context(&self) -> u8 {
491 2 * self.pointer_size
492 }
493}
494
495impl VMOffsets {
497 #[allow(clippy::identity_op)]
499 pub const fn size_of_vmfunction_body_ptr(&self) -> u8 {
500 1 * self.pointer_size
501 }
502}
503
504impl VMOffsets {
506 #[allow(clippy::erasing_op)]
508 pub const fn vmtable_import_definition(&self) -> u8 {
509 0 * self.pointer_size
510 }
511
512 #[allow(clippy::identity_op)]
514 pub const fn vmtable_import_handle(&self) -> u8 {
515 1 * self.pointer_size
516 }
517
518 pub const fn size_of_vmtable_import(&self) -> u8 {
520 2 * self.pointer_size
521 }
522}
523
524impl VMOffsets {
526 #[allow(clippy::erasing_op)]
528 pub const fn vmtable_definition_base(&self) -> u8 {
529 0 * self.pointer_size
530 }
531
532 #[allow(clippy::identity_op)]
534 pub const fn vmtable_definition_current_elements(&self) -> u8 {
535 1 * self.pointer_size
536 }
537
538 pub const fn size_of_vmtable_definition_current_elements(&self) -> u8 {
540 4
541 }
542
543 pub const fn size_of_vmtable_definition(&self) -> u8 {
545 2 * self.pointer_size
546 }
547}
548
549impl VMOffsets {
551 #[allow(clippy::erasing_op)]
553 pub const fn vmmemory_import_definition(&self) -> u8 {
554 0 * self.pointer_size
555 }
556
557 #[allow(clippy::identity_op)]
559 pub const fn vmmemory_import_handle(&self) -> u8 {
560 1 * self.pointer_size
561 }
562
563 pub const fn size_of_vmmemory_import(&self) -> u8 {
565 2 * self.pointer_size
566 }
567}
568
569impl VMOffsets {
571 #[allow(clippy::erasing_op)]
573 pub const fn vmmemory_definition_base(&self) -> u8 {
574 0 * self.pointer_size
575 }
576
577 #[allow(clippy::identity_op)]
579 pub const fn vmmemory_definition_current_length(&self) -> u8 {
580 1 * self.pointer_size
581 }
582
583 pub const fn size_of_vmmemory_definition_current_length(&self) -> u8 {
585 4
586 }
587
588 pub const fn size_of_vmmemory_definition(&self) -> u8 {
590 2 * self.pointer_size
591 }
592}
593
594impl VMOffsets {
596 #[allow(clippy::erasing_op)]
598 pub const fn vmglobal_import_definition(&self) -> u8 {
599 0 * self.pointer_size
600 }
601
602 #[allow(clippy::identity_op)]
604 pub const fn vmglobal_import_handle(&self) -> u8 {
605 1 * self.pointer_size
606 }
607
608 #[allow(clippy::identity_op)]
610 pub const fn size_of_vmglobal_import(&self) -> u8 {
611 2 * self.pointer_size
612 }
613}
614
615impl VMOffsets {
617 pub const fn size_of_vmglobal_local(&self) -> u8 {
622 16
623 }
624}
625
626impl VMOffsets {
628 #[allow(clippy::erasing_op)]
630 pub const fn vmcaller_checked_anyfunc_func_ptr(&self) -> u8 {
631 0 * self.pointer_size
632 }
633
634 #[allow(clippy::identity_op)]
636 pub const fn vmcaller_checked_anyfunc_signature_hash(&self) -> u8 {
637 1 * self.pointer_size
638 }
639
640 pub const fn vmcaller_checked_anyfunc_vmctx(&self) -> u8 {
642 2 * self.pointer_size
643 }
644
645 pub const fn vmcaller_checked_anyfunc_call_trampoline(&self) -> u8 {
647 3 * self.pointer_size
648 }
649
650 pub const fn size_of_vmcaller_checked_anyfunc(&self) -> u8 {
652 4 * self.pointer_size
653 }
654}
655
656impl VMOffsets {
658 #[allow(clippy::erasing_op)]
660 pub const fn vm_funcref_anyfunc_ptr(&self) -> u8 {
661 0 * self.pointer_size
662 }
663
664 #[allow(clippy::identity_op)]
666 pub const fn size_of_vm_funcref(&self) -> u8 {
667 1 * self.pointer_size
668 }
669}
670
671impl VMOffsets {
673 pub const fn size_of_vmshared_tag_index(&self) -> u8 {
675 4
676 }
677}
678
679impl VMOffsets {
681 #[allow(clippy::erasing_op)]
683 pub fn vmctx_imported_functions_begin(&self) -> u32 {
684 self.vmctx_imported_functions_begin
685 }
686
687 #[allow(clippy::identity_op)]
689 pub fn vmctx_imported_tables_begin(&self) -> u32 {
690 self.vmctx_imported_tables_begin
691 }
692
693 pub fn vmctx_imported_memories_begin(&self) -> u32 {
695 self.vmctx_imported_memories_begin
696 }
697
698 pub fn vmctx_imported_globals_begin(&self) -> u32 {
700 self.vmctx_imported_globals_begin
701 }
702
703 pub fn vmctx_tag_ids_begin(&self) -> u32 {
705 self.vmctx_tag_ids_begin
706 }
707
708 pub fn vmctx_tables_begin(&self) -> u32 {
710 self.vmctx_tables_begin
711 }
712
713 pub fn vmctx_fixed_funcref_tables_begin(&self) -> u32 {
715 self.vmctx_fixed_funcref_tables_begin
716 }
717
718 pub fn vmctx_memories_begin(&self) -> u32 {
720 self.vmctx_memories_begin
721 }
722
723 pub fn vmctx_globals_begin(&self) -> u32 {
725 self.vmctx_globals_begin
726 }
727
728 pub fn vmctx_builtin_functions_begin(&self) -> u32 {
730 self.vmctx_builtin_functions_begin
731 }
732
733 pub fn size_of_vmctx(&self) -> u32 {
735 self.size_of_vmctx
736 }
737
738 pub fn vmctx_vmfunction_import(&self, index: FunctionIndex) -> u32 {
740 assert_lt!(index.as_u32(), self.num_imported_functions);
741 self.vmctx_imported_functions_begin
742 + index.as_u32() * u32::from(self.size_of_vmfunction_import())
743 }
744
745 pub fn vmctx_vmtable_import(&self, index: TableIndex) -> u32 {
747 assert_lt!(index.as_u32(), self.num_imported_tables);
748 self.vmctx_imported_tables_begin + index.as_u32() * u32::from(self.size_of_vmtable_import())
749 }
750
751 pub fn vmctx_vmmemory_import(&self, index: MemoryIndex) -> u32 {
753 assert_lt!(index.as_u32(), self.num_imported_memories);
754 self.vmctx_imported_memories_begin
755 + index.as_u32() * u32::from(self.size_of_vmmemory_import())
756 }
757
758 pub fn vmctx_vmglobal_import(&self, index: GlobalIndex) -> u32 {
760 assert_lt!(index.as_u32(), self.num_imported_globals);
761 self.vmctx_imported_globals_begin
762 + index.as_u32() * u32::from(self.size_of_vmglobal_import())
763 }
764
765 pub fn vmctx_vmtable_definition(&self, index: LocalTableIndex) -> u32 {
767 assert_lt!(index.as_u32(), self.num_local_tables);
768 self.vmctx_tables_begin + index.as_u32() * u32::from(self.size_of_vmtable_definition())
769 }
770
771 pub fn vmctx_vmmemory_definition(&self, index: LocalMemoryIndex) -> u32 {
773 assert_lt!(index.as_u32(), self.num_local_memories);
774 self.vmctx_memories_begin + index.as_u32() * u32::from(self.size_of_vmmemory_definition())
775 }
776
777 pub fn vmctx_vmglobal_definition(&self, index: LocalGlobalIndex) -> u32 {
779 assert_lt!(index.as_u32(), self.num_local_globals);
780 self.vmctx_globals_begin + index.as_u32() * u32::from(self.size_of_vmglobal_local())
781 }
782
783 pub fn vmctx_vmfunction_import_body(&self, index: FunctionIndex) -> u32 {
786 self.vmctx_vmfunction_import(index) + u32::from(self.vmfunction_import_body())
787 }
788
789 pub fn vmctx_vmfunction_import_vmctx(&self, index: FunctionIndex) -> u32 {
792 self.vmctx_vmfunction_import(index) + u32::from(self.vmfunction_import_vmctx())
793 }
794
795 pub fn vmctx_vmtable_import_definition(&self, index: TableIndex) -> u32 {
798 self.vmctx_vmtable_import(index) + u32::from(self.vmtable_import_definition())
799 }
800
801 pub fn vmctx_vmtable_definition_base(&self, index: LocalTableIndex) -> u32 {
804 self.vmctx_vmtable_definition(index) + u32::from(self.vmtable_definition_base())
805 }
806
807 pub fn vmctx_vmtable_definition_current_elements(&self, index: LocalTableIndex) -> u32 {
810 self.vmctx_vmtable_definition(index) + u32::from(self.vmtable_definition_current_elements())
811 }
812
813 pub fn vmctx_fixed_funcref_table_anyfuncs(&self, index: LocalTableIndex) -> Option<u32> {
816 assert_lt!(index.as_u32(), self.num_local_tables);
817 self.local_fixed_funcref_table_offsets[index.index()]
818 .map(|offset| self.vmctx_fixed_funcref_tables_begin + offset)
819 }
820
821 pub fn vmctx_vmmemory_import_definition(&self, index: MemoryIndex) -> u32 {
824 self.vmctx_vmmemory_import(index) + u32::from(self.vmmemory_import_definition())
825 }
826
827 pub fn vmctx_vmmemory_import_handle(&self, index: MemoryIndex) -> u32 {
830 self.vmctx_vmmemory_import(index) + u32::from(self.vmmemory_import_handle())
831 }
832
833 pub fn vmctx_vmmemory_definition_base(&self, index: LocalMemoryIndex) -> u32 {
836 self.vmctx_vmmemory_definition(index) + u32::from(self.vmmemory_definition_base())
837 }
838
839 pub fn vmctx_vmmemory_definition_current_length(&self, index: LocalMemoryIndex) -> u32 {
842 self.vmctx_vmmemory_definition(index) + u32::from(self.vmmemory_definition_current_length())
843 }
844
845 pub fn vmctx_vmglobal_import_definition(&self, index: GlobalIndex) -> u32 {
848 self.vmctx_vmglobal_import(index) + u32::from(self.vmglobal_import_definition())
849 }
850
851 pub fn vmctx_builtin_function(&self, index: VMBuiltinFunctionIndex) -> u32 {
854 self.vmctx_builtin_functions_begin + index.index() * u32::from(self.pointer_size)
855 }
856}
857
858#[cfg(test)]
859mod tests {
860 use crate::vmoffsets::align;
861
862 #[test]
863 fn alignment() {
864 fn is_aligned(x: u32) -> bool {
865 x.is_multiple_of(16)
866 }
867 assert!(is_aligned(align(0, 16)));
868 assert!(is_aligned(align(32, 16)));
869 assert!(is_aligned(align(33, 16)));
870 assert!(is_aligned(align(31, 16)));
871 }
872}