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_imported_memory_copy_index() -> Self {
56 Self(8)
57 }
58 pub const fn get_memory_fill_index() -> Self {
60 Self(9)
61 }
62 pub const fn get_imported_memory_fill_index() -> Self {
64 Self(10)
65 }
66 pub const fn get_memory_init_index() -> Self {
68 Self(11)
69 }
70 pub const fn get_data_drop_index() -> Self {
72 Self(12)
73 }
74 pub const fn get_raise_trap_index() -> Self {
76 Self(13)
77 }
78 pub const fn get_table_size_index() -> Self {
80 Self(14)
81 }
82 pub const fn get_imported_table_size_index() -> Self {
84 Self(15)
85 }
86 pub const fn get_table_grow_index() -> Self {
88 Self(16)
89 }
90 pub const fn get_imported_table_grow_index() -> Self {
92 Self(17)
93 }
94 pub const fn get_table_get_index() -> Self {
96 Self(18)
97 }
98 pub const fn get_imported_table_get_index() -> Self {
100 Self(19)
101 }
102 pub const fn get_table_set_index() -> Self {
104 Self(20)
105 }
106 pub const fn get_imported_table_set_index() -> Self {
108 Self(21)
109 }
110 pub const fn get_func_ref_index() -> Self {
112 Self(22)
113 }
114 pub const fn get_table_fill_index() -> Self {
116 Self(23)
117 }
118 pub const fn get_memory_atomic_wait32_index() -> Self {
120 Self(24)
121 }
122 pub const fn get_imported_memory_atomic_wait32_index() -> Self {
124 Self(25)
125 }
126 pub const fn get_memory_atomic_wait64_index() -> Self {
128 Self(26)
129 }
130 pub const fn get_imported_memory_atomic_wait64_index() -> Self {
132 Self(27)
133 }
134 pub const fn get_memory_atomic_notify_index() -> Self {
136 Self(28)
137 }
138
139 pub const fn get_imported_memory_atomic_notify_index() -> Self {
141 Self(29)
142 }
143
144 pub const fn get_imported_debug_usize_index() -> Self {
146 Self(30)
147 }
148
149 pub const fn get_imported_debug_str_index() -> Self {
151 Self(31)
152 }
153
154 pub const fn get_imported_personality2_index() -> Self {
156 Self(32)
157 }
158
159 pub const fn get_imported_alloc_exception_index() -> Self {
161 Self(33)
162 }
163
164 pub const fn get_imported_throw_index() -> Self {
166 Self(34)
167 }
168
169 pub const fn get_imported_read_exnref_index() -> Self {
171 Self(35)
172 }
173
174 pub const fn get_imported_exception_into_exnref_index() -> Self {
176 Self(36)
177 }
178
179 pub const fn builtin_functions_total_number() -> u32 {
181 37
182 }
183
184 pub const fn index(self) -> u32 {
186 self.0
187 }
188}
189
190#[cfg(target_pointer_width = "32")]
191fn cast_to_u32(sz: usize) -> u32 {
192 u32::try_from(sz).unwrap()
193}
194#[cfg(target_pointer_width = "64")]
195fn cast_to_u32(sz: usize) -> u32 {
196 u32::try_from(sz).expect("overflow in cast from usize to u32")
197}
198
199#[inline]
201const fn align(offset: u32, width: u32) -> u32 {
202 offset.div_ceil(width) * width
203}
204
205#[derive(Clone, Debug)]
208pub struct VMOffsets {
209 pointer_size: u8,
211 num_imported_functions: u32,
213 num_imported_tables: u32,
215 num_imported_memories: u32,
217 num_tag_ids: u32,
219 num_imported_globals: u32,
221 num_local_tables: u32,
223 num_local_memories: u32,
225 num_local_globals: u32,
227 local_fixed_funcref_table_offsets: Vec<Option<u32>>,
230 size_of_local_fixed_funcref_tables: u32,
232
233 vmctx_imported_functions_begin: u32,
234 vmctx_imported_tables_begin: u32,
235 vmctx_imported_memories_begin: u32,
236 vmctx_tag_ids_begin: u32,
237 vmctx_imported_globals_begin: u32,
238 vmctx_tables_begin: u32,
239 vmctx_fixed_funcref_tables_begin: u32,
240 vmctx_memories_begin: u32,
241 vmctx_globals_begin: u32,
242 vmctx_builtin_functions_begin: u32,
243 vmctx_trap_handler_begin: u32,
244 vmctx_gas_limiter_pointer: u32,
245 vmctx_stack_limit_begin: u32,
246 vmctx_stack_limit_initial_begin: u32,
247 size_of_vmctx: u32,
248}
249
250impl VMOffsets {
251 pub fn new(pointer_size: u8, module: &ModuleInfo) -> Self {
253 let mut local_fixed_funcref_table_offsets =
254 vec![None; module.tables.len() - module.num_imported_tables];
255 let mut size_of_local_fixed_funcref_tables: u32 = 0;
256 let size_of_vmcaller_checked_anyfunc = 4 * u32::from(pointer_size);
258
259 for (table_index, table) in module.tables.iter() {
260 if let Some(local_table_index) = module.local_table_index(table_index)
261 && table.is_fixed_funcref_table()
262 {
263 local_fixed_funcref_table_offsets[local_table_index.index()] =
264 Some(size_of_local_fixed_funcref_tables);
265 size_of_local_fixed_funcref_tables = size_of_local_fixed_funcref_tables
266 .checked_add(
267 table
268 .minimum
269 .checked_mul(size_of_vmcaller_checked_anyfunc)
270 .unwrap(),
271 )
272 .unwrap();
273 }
274 }
275
276 let mut ret = Self {
277 pointer_size,
278 num_imported_functions: cast_to_u32(module.num_imported_functions),
279 num_imported_tables: cast_to_u32(module.num_imported_tables),
280 num_imported_memories: cast_to_u32(module.num_imported_memories),
281 num_tag_ids: cast_to_u32(module.tags.len()),
282 num_imported_globals: cast_to_u32(module.num_imported_globals),
283 num_local_tables: cast_to_u32(module.tables.len()),
284 num_local_memories: cast_to_u32(module.memories.len()),
285 num_local_globals: cast_to_u32(module.globals.len()),
286 local_fixed_funcref_table_offsets,
287 size_of_local_fixed_funcref_tables,
288 vmctx_imported_functions_begin: 0,
289 vmctx_imported_tables_begin: 0,
290 vmctx_imported_memories_begin: 0,
291 vmctx_tag_ids_begin: 0,
292 vmctx_imported_globals_begin: 0,
293 vmctx_tables_begin: 0,
294 vmctx_fixed_funcref_tables_begin: 0,
295 vmctx_memories_begin: 0,
296 vmctx_globals_begin: 0,
297 vmctx_builtin_functions_begin: 0,
298 vmctx_trap_handler_begin: 0,
299 vmctx_gas_limiter_pointer: 0,
300 vmctx_stack_limit_begin: 0,
301 vmctx_stack_limit_initial_begin: 0,
302 size_of_vmctx: 0,
303 };
304 ret.precompute();
305 ret
306 }
307
308 pub fn new_for_trampolines(pointer_size: u8) -> Self {
313 Self {
314 pointer_size,
315 num_imported_functions: 0,
316 num_imported_tables: 0,
317 num_imported_memories: 0,
318 num_tag_ids: 0,
319 num_imported_globals: 0,
320 num_local_tables: 0,
321 num_local_memories: 0,
322 num_local_globals: 0,
323 local_fixed_funcref_table_offsets: Vec::new(),
324 size_of_local_fixed_funcref_tables: 0,
325 vmctx_imported_functions_begin: 0,
326 vmctx_imported_tables_begin: 0,
327 vmctx_imported_memories_begin: 0,
328 vmctx_tag_ids_begin: 0,
329 vmctx_imported_globals_begin: 0,
330 vmctx_tables_begin: 0,
331 vmctx_fixed_funcref_tables_begin: 0,
332 vmctx_memories_begin: 0,
333 vmctx_globals_begin: 0,
334 vmctx_builtin_functions_begin: 0,
335 vmctx_trap_handler_begin: 0,
336 vmctx_gas_limiter_pointer: 0,
337 vmctx_stack_limit_begin: 0,
338 vmctx_stack_limit_initial_begin: 0,
339 size_of_vmctx: 0,
340 }
341 }
342
343 pub fn num_local_tables(&self) -> u32 {
345 self.num_local_tables
346 }
347
348 pub fn num_local_memories(&self) -> u32 {
350 self.num_local_memories
351 }
352
353 pub fn num_local_globals(&self) -> u32 {
355 self.num_local_globals
356 }
357
358 fn precompute(&mut self) {
359 fn offset_by(base: u32, num_items: u32, item_size: u32) -> u32 {
361 base.checked_add(num_items.checked_mul(item_size).unwrap())
362 .unwrap()
363 }
364 let pointer_size = self.pointer_size as u32;
368 let offset_by_aligned = |base: u32, num_items: u32, item_size: u32| -> u32 {
369 align(
370 base.checked_add(num_items.checked_mul(item_size).unwrap())
371 .unwrap(),
372 pointer_size,
373 )
374 };
375
376 self.vmctx_imported_functions_begin = 0;
377 self.vmctx_imported_tables_begin = offset_by_aligned(
378 self.vmctx_imported_functions_begin,
379 self.num_imported_functions,
380 u32::from(self.size_of_vmfunction_import()),
381 );
382 self.vmctx_imported_memories_begin = offset_by_aligned(
383 self.vmctx_imported_tables_begin,
384 self.num_imported_tables,
385 u32::from(self.size_of_vmtable_import()),
386 );
387
388 self.vmctx_tag_ids_begin = offset_by_aligned(
389 self.vmctx_imported_memories_begin,
390 self.num_imported_memories,
391 u32::from(self.size_of_vmmemory_import()),
392 );
393
394 self.vmctx_imported_globals_begin = offset_by_aligned(
395 self.vmctx_tag_ids_begin,
396 self.num_tag_ids,
397 u32::from(self.size_of_vmshared_tag_index()),
398 );
399
400 self.vmctx_tables_begin = offset_by_aligned(
401 self.vmctx_imported_globals_begin,
402 self.num_imported_globals,
403 u32::from(self.size_of_vmglobal_import()),
404 );
405 self.vmctx_fixed_funcref_tables_begin = offset_by_aligned(
406 self.vmctx_tables_begin,
407 self.num_local_tables,
408 u32::from(self.size_of_vmtable_definition()),
409 );
410 self.vmctx_memories_begin = align(
411 self.vmctx_fixed_funcref_tables_begin
412 .checked_add(self.size_of_local_fixed_funcref_tables)
413 .unwrap(),
414 pointer_size,
415 );
416 self.vmctx_globals_begin = align(
417 offset_by(
418 self.vmctx_memories_begin,
419 self.num_local_memories,
420 u32::from(self.size_of_vmmemory_definition()),
421 ),
422 16,
423 );
424 self.vmctx_builtin_functions_begin = offset_by(
425 self.vmctx_globals_begin,
426 self.num_local_globals,
427 u32::from(self.size_of_vmglobal_local()),
428 );
429 self.vmctx_trap_handler_begin = offset_by(
430 self.vmctx_builtin_functions_begin,
431 VMBuiltinFunctionIndex::builtin_functions_total_number(),
432 u32::from(self.pointer_size),
433 );
434 self.vmctx_gas_limiter_pointer = offset_by(
435 self.vmctx_trap_handler_begin,
436 1,
437 u32::from(self.pointer_size),
438 );
439 self.vmctx_stack_limit_begin = offset_by(
440 self.vmctx_gas_limiter_pointer,
441 1,
442 u32::from(self.pointer_size),
443 );
444 self.vmctx_stack_limit_initial_begin = self.vmctx_stack_limit_begin.checked_add(4).unwrap();
445 self.size_of_vmctx = self.vmctx_stack_limit_begin.checked_add(4).unwrap();
446 }
447}
448
449impl VMOffsets {
451 #[allow(clippy::erasing_op)]
453 pub const fn vmfunction_import_body(&self) -> u8 {
454 0 * self.pointer_size
455 }
456
457 #[allow(clippy::identity_op)]
459 pub const fn vmfunction_import_vmctx(&self) -> u8 {
460 1 * self.pointer_size
461 }
462
463 pub const fn vmfunction_import_handle(&self) -> u8 {
465 2 * self.pointer_size
466 }
467
468 pub const fn vmfunction_import_include_m0_param(&self) -> u8 {
470 3 * self.pointer_size
471 }
472
473 pub const fn size_of_vmfunction_import(&self) -> u8 {
475 4 * self.pointer_size
476 }
477}
478
479impl VMOffsets {
481 #[allow(clippy::erasing_op)]
483 pub const fn vmdynamicfunction_import_context_address(&self) -> u8 {
484 0 * self.pointer_size
485 }
486
487 #[allow(clippy::identity_op)]
489 pub const fn vmdynamicfunction_import_context_ctx(&self) -> u8 {
490 1 * self.pointer_size
491 }
492
493 pub const fn size_of_vmdynamicfunction_import_context(&self) -> u8 {
495 2 * self.pointer_size
496 }
497}
498
499impl VMOffsets {
501 #[allow(clippy::identity_op)]
503 pub const fn size_of_vmfunction_body_ptr(&self) -> u8 {
504 1 * self.pointer_size
505 }
506}
507
508impl VMOffsets {
510 #[allow(clippy::erasing_op)]
512 pub const fn vmtable_import_definition(&self) -> u8 {
513 0 * self.pointer_size
514 }
515
516 #[allow(clippy::identity_op)]
518 pub const fn vmtable_import_handle(&self) -> u8 {
519 1 * self.pointer_size
520 }
521
522 pub const fn size_of_vmtable_import(&self) -> u8 {
524 2 * self.pointer_size
525 }
526}
527
528impl VMOffsets {
530 #[allow(clippy::erasing_op)]
532 pub const fn vmtable_definition_base(&self) -> u8 {
533 0 * self.pointer_size
534 }
535
536 #[allow(clippy::identity_op)]
538 pub const fn vmtable_definition_current_elements(&self) -> u8 {
539 1 * self.pointer_size
540 }
541
542 pub const fn size_of_vmtable_definition_current_elements(&self) -> u8 {
544 4
545 }
546
547 pub const fn size_of_vmtable_definition(&self) -> u8 {
549 2 * self.pointer_size
550 }
551}
552
553impl VMOffsets {
555 #[allow(clippy::erasing_op)]
557 pub const fn vmmemory_import_definition(&self) -> u8 {
558 0 * self.pointer_size
559 }
560
561 #[allow(clippy::identity_op)]
563 pub const fn vmmemory_import_handle(&self) -> u8 {
564 1 * self.pointer_size
565 }
566
567 pub const fn size_of_vmmemory_import(&self) -> u8 {
569 2 * self.pointer_size
570 }
571}
572
573impl VMOffsets {
575 #[allow(clippy::erasing_op)]
577 pub const fn vmmemory_definition_base(&self) -> u8 {
578 0 * self.pointer_size
579 }
580
581 #[allow(clippy::identity_op)]
583 pub const fn vmmemory_definition_current_length(&self) -> u8 {
584 1 * self.pointer_size
585 }
586
587 pub const fn size_of_vmmemory_definition_current_length(&self) -> u8 {
589 4
590 }
591
592 pub const fn size_of_vmmemory_definition(&self) -> u8 {
594 2 * self.pointer_size
595 }
596}
597
598impl VMOffsets {
600 #[allow(clippy::erasing_op)]
602 pub const fn vmglobal_import_definition(&self) -> u8 {
603 0 * self.pointer_size
604 }
605
606 #[allow(clippy::identity_op)]
608 pub const fn vmglobal_import_handle(&self) -> u8 {
609 1 * self.pointer_size
610 }
611
612 #[allow(clippy::identity_op)]
614 pub const fn size_of_vmglobal_import(&self) -> u8 {
615 2 * self.pointer_size
616 }
617}
618
619impl VMOffsets {
621 pub const fn size_of_vmglobal_local(&self) -> u8 {
626 16
627 }
628}
629
630impl VMOffsets {
632 #[allow(clippy::erasing_op)]
634 pub const fn vmcaller_checked_anyfunc_func_ptr(&self) -> u8 {
635 0 * self.pointer_size
636 }
637
638 #[allow(clippy::identity_op)]
640 pub const fn vmcaller_checked_anyfunc_signature_hash(&self) -> u8 {
641 1 * self.pointer_size
642 }
643
644 pub const fn vmcaller_checked_anyfunc_vmctx(&self) -> u8 {
646 2 * self.pointer_size
647 }
648
649 pub const fn vmcaller_checked_anyfunc_call_trampoline(&self) -> u8 {
651 3 * self.pointer_size
652 }
653
654 pub const fn size_of_vmcaller_checked_anyfunc(&self) -> u8 {
656 4 * self.pointer_size
657 }
658}
659
660impl VMOffsets {
662 #[allow(clippy::erasing_op)]
664 pub const fn vm_funcref_anyfunc_ptr(&self) -> u8 {
665 0 * self.pointer_size
666 }
667
668 #[allow(clippy::identity_op)]
670 pub const fn size_of_vm_funcref(&self) -> u8 {
671 1 * self.pointer_size
672 }
673}
674
675impl VMOffsets {
677 pub const fn size_of_vmshared_tag_index(&self) -> u8 {
679 4
680 }
681}
682
683impl VMOffsets {
685 #[allow(clippy::erasing_op)]
687 pub fn vmctx_imported_functions_begin(&self) -> u32 {
688 self.vmctx_imported_functions_begin
689 }
690
691 #[allow(clippy::identity_op)]
693 pub fn vmctx_imported_tables_begin(&self) -> u32 {
694 self.vmctx_imported_tables_begin
695 }
696
697 pub fn vmctx_imported_memories_begin(&self) -> u32 {
699 self.vmctx_imported_memories_begin
700 }
701
702 pub fn vmctx_imported_globals_begin(&self) -> u32 {
704 self.vmctx_imported_globals_begin
705 }
706
707 pub fn vmctx_tag_ids_begin(&self) -> u32 {
709 self.vmctx_tag_ids_begin
710 }
711
712 pub fn vmctx_tables_begin(&self) -> u32 {
714 self.vmctx_tables_begin
715 }
716
717 pub fn vmctx_fixed_funcref_tables_begin(&self) -> u32 {
719 self.vmctx_fixed_funcref_tables_begin
720 }
721
722 pub fn vmctx_memories_begin(&self) -> u32 {
724 self.vmctx_memories_begin
725 }
726
727 pub fn vmctx_globals_begin(&self) -> u32 {
729 self.vmctx_globals_begin
730 }
731
732 pub fn vmctx_builtin_functions_begin(&self) -> u32 {
734 self.vmctx_builtin_functions_begin
735 }
736
737 pub fn size_of_vmctx(&self) -> u32 {
739 self.size_of_vmctx
740 }
741
742 pub fn vmctx_vmfunction_import(&self, index: FunctionIndex) -> u32 {
744 assert_lt!(index.as_u32(), self.num_imported_functions);
745 self.vmctx_imported_functions_begin
746 + index.as_u32() * u32::from(self.size_of_vmfunction_import())
747 }
748
749 pub fn vmctx_vmtable_import(&self, index: TableIndex) -> u32 {
751 assert_lt!(index.as_u32(), self.num_imported_tables);
752 self.vmctx_imported_tables_begin + index.as_u32() * u32::from(self.size_of_vmtable_import())
753 }
754
755 pub fn vmctx_vmmemory_import(&self, index: MemoryIndex) -> u32 {
757 assert_lt!(index.as_u32(), self.num_imported_memories);
758 self.vmctx_imported_memories_begin
759 + index.as_u32() * u32::from(self.size_of_vmmemory_import())
760 }
761
762 pub fn vmctx_vmglobal_import(&self, index: GlobalIndex) -> u32 {
764 assert_lt!(index.as_u32(), self.num_imported_globals);
765 self.vmctx_imported_globals_begin
766 + index.as_u32() * u32::from(self.size_of_vmglobal_import())
767 }
768
769 pub fn vmctx_vmtable_definition(&self, index: LocalTableIndex) -> u32 {
771 assert_lt!(index.as_u32(), self.num_local_tables);
772 self.vmctx_tables_begin + index.as_u32() * u32::from(self.size_of_vmtable_definition())
773 }
774
775 pub fn vmctx_vmmemory_definition(&self, index: LocalMemoryIndex) -> u32 {
777 assert_lt!(index.as_u32(), self.num_local_memories);
778 self.vmctx_memories_begin + index.as_u32() * u32::from(self.size_of_vmmemory_definition())
779 }
780
781 pub fn vmctx_vmglobal_definition(&self, index: LocalGlobalIndex) -> u32 {
783 assert_lt!(index.as_u32(), self.num_local_globals);
784 self.vmctx_globals_begin + index.as_u32() * u32::from(self.size_of_vmglobal_local())
785 }
786
787 pub fn vmctx_vmfunction_import_body(&self, index: FunctionIndex) -> u32 {
790 self.vmctx_vmfunction_import(index) + u32::from(self.vmfunction_import_body())
791 }
792
793 pub fn vmctx_vmfunction_import_vmctx(&self, index: FunctionIndex) -> u32 {
796 self.vmctx_vmfunction_import(index) + u32::from(self.vmfunction_import_vmctx())
797 }
798
799 pub fn vmctx_vmtable_import_definition(&self, index: TableIndex) -> u32 {
802 self.vmctx_vmtable_import(index) + u32::from(self.vmtable_import_definition())
803 }
804
805 pub fn vmctx_vmtable_definition_base(&self, index: LocalTableIndex) -> u32 {
808 self.vmctx_vmtable_definition(index) + u32::from(self.vmtable_definition_base())
809 }
810
811 pub fn vmctx_vmtable_definition_current_elements(&self, index: LocalTableIndex) -> u32 {
814 self.vmctx_vmtable_definition(index) + u32::from(self.vmtable_definition_current_elements())
815 }
816
817 pub fn vmctx_fixed_funcref_table_anyfuncs(&self, index: LocalTableIndex) -> Option<u32> {
820 assert_lt!(index.as_u32(), self.num_local_tables);
821 self.local_fixed_funcref_table_offsets[index.index()]
822 .map(|offset| self.vmctx_fixed_funcref_tables_begin + offset)
823 }
824
825 pub fn vmctx_vmmemory_import_definition(&self, index: MemoryIndex) -> u32 {
828 self.vmctx_vmmemory_import(index) + u32::from(self.vmmemory_import_definition())
829 }
830
831 pub fn vmctx_vmmemory_import_handle(&self, index: MemoryIndex) -> u32 {
834 self.vmctx_vmmemory_import(index) + u32::from(self.vmmemory_import_handle())
835 }
836
837 pub fn vmctx_vmmemory_definition_base(&self, index: LocalMemoryIndex) -> u32 {
840 self.vmctx_vmmemory_definition(index) + u32::from(self.vmmemory_definition_base())
841 }
842
843 pub fn vmctx_vmmemory_definition_current_length(&self, index: LocalMemoryIndex) -> u32 {
846 self.vmctx_vmmemory_definition(index) + u32::from(self.vmmemory_definition_current_length())
847 }
848
849 pub fn vmctx_vmglobal_import_definition(&self, index: GlobalIndex) -> u32 {
852 self.vmctx_vmglobal_import(index) + u32::from(self.vmglobal_import_definition())
853 }
854
855 pub fn vmctx_builtin_function(&self, index: VMBuiltinFunctionIndex) -> u32 {
858 self.vmctx_builtin_functions_begin + index.index() * u32::from(self.pointer_size)
859 }
860}
861
862#[cfg(test)]
863mod tests {
864 use crate::vmoffsets::align;
865
866 #[test]
867 fn alignment() {
868 fn is_aligned(x: u32) -> bool {
869 x.is_multiple_of(16)
870 }
871 assert!(is_aligned(align(0, 16)));
872 assert!(is_aligned(align(32, 16)));
873 assert!(is_aligned(align(33, 16)));
874 assert!(is_aligned(align(31, 16)));
875 }
876}