Skip to content

Commit 2b5c44d

Browse files
authored
fix signed 8- and 16-bit loads in wit-dylib bindgen (#2455)
* fix signed 8- and 16-bit loads in wit-dylib bindgen * add tests for signed 8- and 16-bit loads Ideally, we'd model this as a runtime test which uses WAT to avoid compiler-optimization-induced unpredictability, but that would be a lot of work, so we settle for checking that the `wit-dylib` output does in fact use signed loads when the WIT world requires them. I've verified that these tests fail without my earlier fix.
1 parent c463da0 commit 2b5c44d

3 files changed

Lines changed: 90 additions & 23 deletions

File tree

crates/wit-dylib/src/bindgen.rs

Lines changed: 31 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1344,24 +1344,24 @@ impl<'a> FunctionCompiler<'a> {
13441344
fn lift(&mut self, src: &AbiLoc<'_>, ty: Type) {
13451345
let i = self.adapter.intrinsics();
13461346
match ty {
1347-
Type::Bool => self.push_scalar(src, ValType::I32, i.push_bool, 0),
1348-
Type::Char => self.push_scalar(src, ValType::I32, i.push_char, 2),
1349-
Type::U8 => self.push_scalar(src, ValType::I32, i.push_u8, 0),
1350-
Type::S8 => self.push_scalar(src, ValType::I32, i.push_s8, 0),
1351-
Type::U16 => self.push_scalar(src, ValType::I32, i.push_u16, 1),
1352-
Type::S16 => self.push_scalar(src, ValType::I32, i.push_s16, 1),
1353-
Type::U32 => self.push_scalar(src, ValType::I32, i.push_u32, 2),
1354-
Type::S32 => self.push_scalar(src, ValType::I32, i.push_s32, 2),
1355-
Type::U64 => self.push_scalar(src, ValType::I64, i.push_u64, 3),
1356-
Type::S64 => self.push_scalar(src, ValType::I64, i.push_s64, 3),
1357-
Type::F32 => self.push_scalar(src, ValType::F32, i.push_f32, 2),
1358-
Type::F64 => self.push_scalar(src, ValType::F64, i.push_f64, 3),
1347+
Type::Bool => self.push_scalar(src, ValType::I32, i.push_bool, 0, false),
1348+
Type::Char => self.push_scalar(src, ValType::I32, i.push_char, 2, false),
1349+
Type::U8 => self.push_scalar(src, ValType::I32, i.push_u8, 0, false),
1350+
Type::S8 => self.push_scalar(src, ValType::I32, i.push_s8, 0, true),
1351+
Type::U16 => self.push_scalar(src, ValType::I32, i.push_u16, 1, false),
1352+
Type::S16 => self.push_scalar(src, ValType::I32, i.push_s16, 1, true),
1353+
Type::U32 => self.push_scalar(src, ValType::I32, i.push_u32, 2, false),
1354+
Type::S32 => self.push_scalar(src, ValType::I32, i.push_s32, 2, true),
1355+
Type::U64 => self.push_scalar(src, ValType::I64, i.push_u64, 3, false),
1356+
Type::S64 => self.push_scalar(src, ValType::I64, i.push_s64, 3, true),
1357+
Type::F32 => self.push_scalar(src, ValType::F32, i.push_f32, 2, false),
1358+
Type::F64 => self.push_scalar(src, ValType::F64, i.push_f64, 3, false),
13591359
Type::String => {
13601360
let push_string = i.push_string;
13611361
let (src_ptr, src_len) = src.split_ptr_len();
13621362
self.local_get_ctx();
1363-
self.load_abi_loc(&src_ptr, ValType::I32, 2);
1364-
self.load_abi_loc(&src_len, ValType::I32, 2);
1363+
self.load_abi_loc(&src_ptr, ValType::I32, 2, false);
1364+
self.load_abi_loc(&src_len, ValType::I32, 2, false);
13651365
self.ins().call(push_string);
13661366
}
13671367
Type::ErrorContext => todo!("error-context"),
@@ -1502,9 +1502,9 @@ impl<'a> FunctionCompiler<'a> {
15021502
// ownership of the allocation.
15031503
self.local_get_ctx();
15041504
self.ins().i32_const(list_index);
1505-
self.load_abi_loc(&src_ptr, ValType::I32, 2);
1505+
self.load_abi_loc(&src_ptr, ValType::I32, 2, false);
15061506
let l_ptr = self.local_tee_new_tmp(ValType::I32);
1507-
self.load_abi_loc(&src_len, ValType::I32, 2);
1507+
self.load_abi_loc(&src_len, ValType::I32, 2, false);
15081508
let l_len = self.local_tee_new_tmp(ValType::I32);
15091509
self.ins().call(push_list);
15101510

@@ -1643,7 +1643,7 @@ impl<'a> FunctionCompiler<'a> {
16431643
let payload0 = iter.next().unwrap();
16441644
let payload1 = iter.next().unwrap();
16451645

1646-
self.load_abi_loc(&discr_src, ValType::I32, tag_size_log2);
1646+
self.load_abi_loc(&discr_src, ValType::I32, tag_size_log2, false);
16471647
self.ins().if_(BlockType::Empty);
16481648
{
16491649
lift_payload(self, payload1);
@@ -1676,7 +1676,7 @@ impl<'a> FunctionCompiler<'a> {
16761676

16771677
// Block to jump out of
16781678
self.ins().block(BlockType::Empty);
1679-
self.load_abi_loc(&discr_src, ValType::I32, tag_size_log2);
1679+
self.load_abi_loc(&discr_src, ValType::I32, tag_size_log2, false);
16801680
self.ins().br_table(1..(n + 1) as u32, 0);
16811681
self.ins().end();
16821682

@@ -1711,17 +1711,24 @@ impl<'a> FunctionCompiler<'a> {
17111711
) {
17121712
self.local_get_ctx();
17131713
self.ins().i32_const(type_index.try_into().unwrap());
1714-
self.load_abi_loc(src, ValType::I32, size_log2);
1714+
self.load_abi_loc(src, ValType::I32, size_log2, false);
17151715
self.ins().call(push_intrinsic);
17161716
}
17171717

1718-
fn push_scalar(&mut self, src: &AbiLoc<'_>, ty: ValType, push_intrinsic: u32, size_log2: u32) {
1718+
fn push_scalar(
1719+
&mut self,
1720+
src: &AbiLoc<'_>,
1721+
ty: ValType,
1722+
push_intrinsic: u32,
1723+
size_log2: u32,
1724+
signed: bool,
1725+
) {
17191726
self.local_get_ctx();
1720-
self.load_abi_loc(src, ty, size_log2);
1727+
self.load_abi_loc(src, ty, size_log2, signed);
17211728
self.ins().call(push_intrinsic);
17221729
}
17231730

1724-
fn load_abi_loc(&mut self, src: &AbiLoc<'_>, ty: ValType, size_log2: u32) {
1731+
fn load_abi_loc(&mut self, src: &AbiLoc<'_>, ty: ValType, size_log2: u32, signed: bool) {
17251732
match src {
17261733
AbiLoc::Stack([local]) => {
17271734
self.ins().local_get(local.idx);
@@ -1770,7 +1777,9 @@ impl<'a> FunctionCompiler<'a> {
17701777
self.ins().local_get(mem.addr.idx);
17711778
let memarg = mem.memarg(size_log2);
17721779
match (ty, size_log2) {
1780+
(ValType::I32, 0) if signed => self.ins().i32_load8_s(memarg),
17731781
(ValType::I32, 0) => self.ins().i32_load8_u(memarg),
1782+
(ValType::I32, 1) if signed => self.ins().i32_load16_s(memarg),
17741783
(ValType::I32, 1) => self.ins().i32_load16_u(memarg),
17751784
(ValType::I32, 2) => self.ins().i32_load(memarg),
17761785
(ValType::I64, 3) => self.ins().i64_load(memarg),

crates/wit-dylib/src/lib.rs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1404,3 +1404,62 @@ fn dealias(resolve: &Resolve, mut id: TypeId) -> TypeId {
14041404
}
14051405
}
14061406
}
1407+
1408+
#[cfg(test)]
1409+
mod tests {
1410+
use wasmparser::{Operator, Parser, Payload};
1411+
use wit_parser::Resolve;
1412+
1413+
#[derive(Copy, Clone)]
1414+
enum Which {
1415+
S8,
1416+
S16,
1417+
}
1418+
1419+
#[test]
1420+
fn s8_uses_signed_load() {
1421+
assert!(find_signed_load(Which::S8));
1422+
}
1423+
1424+
#[test]
1425+
fn s16_uses_signed_load() {
1426+
assert!(find_signed_load(Which::S16));
1427+
}
1428+
1429+
fn find_signed_load(which: Which) -> bool {
1430+
let mut resolve = Resolve::default();
1431+
let ty = match which {
1432+
Which::S8 => "s8",
1433+
Which::S16 => "s16",
1434+
};
1435+
let package = resolve
1436+
.push_str(
1437+
"wit",
1438+
&format!(
1439+
"package test:test;
1440+
world w {{
1441+
export foo: func(v: list<{ty}>);
1442+
}}
1443+
"
1444+
),
1445+
)
1446+
.unwrap();
1447+
let world = resolve.select_world(&[package], None).unwrap();
1448+
let adapter = super::create(&resolve, world, None);
1449+
for payload in Parser::new(0).parse_all(&adapter) {
1450+
match payload.unwrap() {
1451+
Payload::CodeSectionEntry(body) => {
1452+
for operator in body.get_operators_reader().unwrap() {
1453+
match (operator.unwrap(), which) {
1454+
(Operator::I32Load16S { .. }, Which::S16)
1455+
| (Operator::I32Load8S { .. }, Which::S8) => return true,
1456+
_ => {}
1457+
}
1458+
}
1459+
}
1460+
_ => {}
1461+
}
1462+
}
1463+
false
1464+
}
1465+
}

crates/wit-dylib/tests/roundtrip.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,6 @@ fn run_one(u: &mut Unstructured<'_>) -> Result<()> {
6767
config.streams = false; // TODO
6868
config.async_ = false;
6969
let wasm = wit_smith::smith(&config, u)?;
70-
std::fs::write("./hello.wasm", &wasm).unwrap();
7170
let (mut resolve, _pkg) = match wit_parser::decoding::decode(&wasm).unwrap() {
7271
DecodedWasm::WitPackage(resolve, pkg) => (resolve, pkg),
7372
DecodedWasm::Component(..) => unreachable!(),

0 commit comments

Comments
 (0)