-
Notifications
You must be signed in to change notification settings - Fork 175
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix internal alignment in
frunk
heterogeneous lists (#929)
* Make some `GuestPointer` methods `const` Allow them to be used in the `const` context that calculates the size of a heterogeneous list. * Add comment explaining how padding is calculated Link to the Wikipedia source. * Test `GuestPointer::align_at` Ensure that the non-trivial padding calculation is correct. * Fix alignment between heterogeneous list elements Because heterogeneous lists are recursive types, some special handling is needed. Alignment between internal elements must be calculated separately, because otherwise the alignment for the whole list (the tail) is used, which is not the same as the alignment as the next element. The same is true when reading and writing to memory, because the alignment between each element must be considered, and not the alignment for all the remaining elements. * Fix documentation for test type There's only one place where padding is needed. * Add roundtrip tests for some `hlist` types Make sure that they can be stored in memory and read from memory, and that they can be lowered into its flat layout and lifted back from it. Ensure that their internal element alignment is properly respected.
- Loading branch information
Showing
6 changed files
with
216 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
// Copyright (c) Zefchain Labs, Inc. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
//! Unit tests for guest Wasm module memory manipulation. | ||
use super::GuestPointer; | ||
|
||
/// Test aligning memory addresses. | ||
/// | ||
/// Check that the resulting address is aligned and that it never advances more than the alignment | ||
/// amount. | ||
#[test] | ||
fn align_guest_pointer() { | ||
for alignment_bits in 0..3 { | ||
let alignment = 1 << alignment_bits; | ||
let alignment_mask = alignment - 1; | ||
|
||
for start_offset in 0..32 { | ||
let address = GuestPointer(start_offset).aligned_at(alignment); | ||
|
||
assert_eq!(address.0 & alignment_mask, 0); | ||
assert!(address.0 - start_offset < alignment); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,3 +6,5 @@ | |
mod custom_types; | ||
mod frunk; | ||
mod std; | ||
#[cfg(test)] | ||
mod tests; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
// Copyright (c) Zefchain Labs, Inc. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
//! Unit tests for implementations of the custom traits for existing types. | ||
use crate::{FakeInstance, InstanceWithMemory, Layout, WitLoad, WitStore}; | ||
use frunk::hlist; | ||
use std::fmt::Debug; | ||
|
||
/// Test roundtrip of a heterogeneous list that doesn't need any internal padding. | ||
#[test] | ||
fn hlist_without_padding() { | ||
let input = hlist![ | ||
0x1011_1213_1415_1617_1819_1a1b_1c1d_1e1f_u128, | ||
0x2021_2223_2425_2627_i64, | ||
0x3031_3233_u32, | ||
0x4041_i16, | ||
true, | ||
]; | ||
|
||
test_memory_roundtrip( | ||
input, | ||
&[ | ||
0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, | ||
0x11, 0x10, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, 0x33, 0x32, 0x31, 0x30, | ||
0x41, 0x40, 0x01, | ||
], | ||
); | ||
test_flattening_roundtrip( | ||
input, | ||
hlist![ | ||
0x1819_1a1b_1c1d_1e1f_i64, | ||
0x1011_1213_1415_1617_i64, | ||
0x2021_2223_2425_2627_i64, | ||
0x3031_3233_i32, | ||
0x0000_4041_i32, | ||
0x0000_0001_i32, | ||
], | ||
); | ||
} | ||
|
||
/// Test roundtrip of a heterogeneous list that needs internal padding between some of its elements. | ||
#[test] | ||
fn hlist_with_padding() { | ||
let input = hlist![ | ||
true, | ||
0x1011_i16, | ||
0x2021_u16, | ||
0x3031_3233_u32, | ||
0x4041_4243_4445_4647_i64, | ||
]; | ||
|
||
test_memory_roundtrip( | ||
input, | ||
&[ | ||
0x01, 0, 0x11, 0x10, 0x21, 0x20, 0, 0, 0x33, 0x32, 0x31, 0x30, 0, 0, 0, 0, 0x47, 0x46, | ||
0x45, 0x44, 0x43, 0x42, 0x41, 0x40, | ||
], | ||
); | ||
test_flattening_roundtrip( | ||
input, | ||
hlist![ | ||
0x0000_0001_i32, | ||
0x0000_1011_i32, | ||
0x0000_2021_i32, | ||
0x3031_3233_i32, | ||
0x4041_4243_4445_4647_i64, | ||
], | ||
); | ||
} | ||
|
||
/// Test storing an instance of `T` to memory, checking that the `memory_data` bytes are correctly | ||
/// written, and check that the instance can be loaded from those bytes. | ||
fn test_memory_roundtrip<T>(input: T, memory_data: &[u8]) | ||
where | ||
T: Debug + Eq + WitLoad + WitStore, | ||
{ | ||
let mut instance = FakeInstance::default(); | ||
let mut memory = instance.memory().unwrap(); | ||
let length = memory_data.len() as u32; | ||
|
||
assert_eq!(length, T::SIZE); | ||
|
||
let address = memory.allocate(length).unwrap(); | ||
|
||
input.store(&mut memory, address).unwrap(); | ||
|
||
assert_eq!(memory.read(address, length).unwrap(), memory_data); | ||
assert_eq!(T::load(&memory, address).unwrap(), input); | ||
} | ||
|
||
/// Test lowering an instance of `T`, checking that the resulting flat layout matches the expected | ||
/// `flat_layout`, and check that the instance can be lifted from that flat layout. | ||
fn test_flattening_roundtrip<T>(input: T, flat_layout: <T::Layout as Layout>::Flat) | ||
where | ||
T: Debug + Eq + WitLoad + WitStore, | ||
<T::Layout as Layout>::Flat: Debug + Eq, | ||
{ | ||
let mut instance = FakeInstance::default(); | ||
let mut memory = instance.memory().unwrap(); | ||
|
||
let lowered_layout = input.lower(&mut memory).unwrap(); | ||
|
||
assert_eq!(lowered_layout, flat_layout); | ||
assert_eq!(T::lift_from(lowered_layout, &memory).unwrap(), input); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters