Skip to content
Prev Previous commit
Next Next commit
host_env: read call scalar returns as a full register, use add_i32
`call` read `Code` returns through `low::ffi_arg`. The `libffi_sys`
binding types `ffi_arg` as `c_ulong`, which is 4 bytes under LLP64
(Windows x64), so `low::call` truncated 8-byte returns (`q`/`Q`/`d`) to
the low 4 bytes. `calls_f64_scalar` and `passes_large_struct_by_value`
failed on windows-2025. Read a full register (`u64`) instead;
`decode_type_code` still slices the leading bytes each type code needs.
No change on LP64, where `ffi_arg` is already 8 bytes.

Add `typed_two_scalar_args`, which calls the previously-unused `add_i32`
ABI helper (two-argument scalar path); the helper tripped `-D warnings`
dead-code on clippy.

Assisted-by: Claude
  • Loading branch information
youknowone committed Jul 7, 2026
commit 88eb78403e5f50c6e09747615effdbaa33365c7e
40 changes: 36 additions & 4 deletions crates/host_env/src/ctypes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2044,8 +2044,8 @@ pub struct CallOptions {
pub enum CallValue {
/// Void return.
Void,
/// Raw return-register image, native endian (`low::ffi_arg`-sized). Decode
/// with [`decode_type_code`].
/// Raw return-register image, native endian (register-sized: 8 bytes on
/// 64-bit). Decode with [`decode_type_code`].
Scalar(Vec<u8>),
/// Pointer-valued return.
Pointer(usize),
Expand Down Expand Up @@ -2168,7 +2168,7 @@ pub fn call(
enum RawResult {
Void,
Pointer(usize),
Scalar(low::ffi_arg),
Scalar(u64),
Aggregate,
}

Expand All @@ -2182,7 +2182,12 @@ pub fn call(
RawResult::Pointer(unsafe { cif.call::<usize>(code_ptr, &ffi_args) })
}
CallRet::Code(_) => {
RawResult::Scalar(unsafe { cif.call::<low::ffi_arg>(code_ptr, &ffi_args) })
// Capture a full register (`u64`), not `low::ffi_arg`: the
// `libffi_sys` binding types `ffi_arg` as `c_ulong`, which is
// 4 bytes under LLP64 (Windows x64) and would truncate 8-byte
// returns (`q`/`Q`/`d`). `decode_type_code` reads the leading
// bytes the type code needs.
RawResult::Scalar(unsafe { cif.call::<u64>(code_ptr, &ffi_args) })
}
CallRet::Pointer => {
RawResult::Pointer(unsafe { cif.call::<usize>(code_ptr, &ffi_args) })
Expand Down Expand Up @@ -3091,6 +3096,33 @@ mod tests {
));
}

#[test]
fn typed_two_scalar_args() {
let addr = add_i32 as *const () as usize;
let a = 2i32.to_ne_bytes();
let b = 3i32.to_ne_bytes();
let result = call(
addr,
&[
CallArg::Typed {
code: "i",
buffer: &a,
},
CallArg::Typed {
code: "i",
buffer: &b,
},
],
CallRet::Code("i"),
CallOptions::default(),
)
.unwrap();
assert!(matches!(
decode_type_code("i", scalar_bytes(&result)),
DecodedValue::Signed(5)
));
}

#[test]
fn void_return_is_void() {
let result = call(addr_of(noop), &[], CallRet::Void, CallOptions::default()).unwrap();
Expand Down