From d07d15355c64d040d8fb397993467312e0886951 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 26 Apr 2022 13:57:57 -0700 Subject: [PATCH 1/2] Add support for params passed indirectly This commit adds support for the `MAX_FLAT_PARAMS` variable in the current canonical ABI draft. Argument lists which exceed a static number of parameters are now always passed indirectly through memory. In export this is dynamically allocated/freed and in imports this is statically passed. --- crates/gen-c/src/lib.rs | 21 +- crates/gen-js/src/lib.rs | 17 +- crates/gen-rust-wasm/src/lib.rs | 28 +-- crates/gen-spidermonkey/src/lib.rs | 83 +++---- crates/gen-wasmtime-py/src/lib.rs | 18 +- crates/gen-wasmtime/src/lib.rs | 18 +- crates/parser/src/abi.rs | 203 +++++++++++------- crates/parser/src/sizealign.rs | 24 ++- crates/rust-wasm/src/lib.rs | 2 +- crates/test-rust-wasm/Cargo.toml | 4 + .../test-rust-wasm/src/bin/many_arguments.rs | 3 + tests/codegen/many-arguments.wit | 47 ++++ tests/runtime/many_arguments/exports.wit | 22 ++ tests/runtime/many_arguments/host.py | 68 ++++++ tests/runtime/many_arguments/host.rs | 73 +++++++ tests/runtime/many_arguments/host.ts | 95 ++++++++ tests/runtime/many_arguments/imports.wit | 22 ++ tests/runtime/many_arguments/wasm.c | 72 +++++++ tests/runtime/many_arguments/wasm.rs | 56 +++++ 19 files changed, 713 insertions(+), 163 deletions(-) create mode 100644 crates/test-rust-wasm/src/bin/many_arguments.rs create mode 100644 tests/codegen/many-arguments.wit create mode 100644 tests/runtime/many_arguments/exports.wit create mode 100644 tests/runtime/many_arguments/host.py create mode 100644 tests/runtime/many_arguments/host.rs create mode 100644 tests/runtime/many_arguments/host.ts create mode 100644 tests/runtime/many_arguments/imports.wit create mode 100644 tests/runtime/many_arguments/wasm.c create mode 100644 tests/runtime/many_arguments/wasm.rs diff --git a/crates/gen-c/src/lib.rs b/crates/gen-c/src/lib.rs index bdb785c5d..067badd3b 100644 --- a/crates/gen-c/src/lib.rs +++ b/crates/gen-c/src/lib.rs @@ -656,18 +656,6 @@ impl Generator for C { let variant = Self::abi_variant(dir); self.sizes.fill(iface); self.in_import = variant == AbiVariant::GuestImport; - - for func in iface.functions.iter() { - let sig = iface.wasm_signature(variant, func); - if sig.retptr { - self.return_pointer_area_size = self - .return_pointer_area_size - .max(self.sizes.size(&func.result)); - self.return_pointer_area_align = self - .return_pointer_area_align - .max(self.sizes.align(&func.result)); - } - } } fn type_record( @@ -1262,7 +1250,9 @@ impl Bindgen for FunctionBindgen<'_> { self.blocks.push((src.into(), mem::take(operands))); } - fn return_pointer(&mut self, _iface: &Interface, _ty: &Type) -> String { + fn return_pointer(&mut self, size: usize, align: usize) -> String { + self.gen.return_pointer_area_size = self.gen.return_pointer_area_size.max(size); + self.gen.return_pointer_area_align = self.gen.return_pointer_area_align.max(align); let ptr = self.locals.tmp("ptr"); self.src .push_str(&format!("int32_t {} = (int32_t) &RET_AREA;\n", ptr)); @@ -1784,6 +1774,11 @@ impl Bindgen for FunctionBindgen<'_> { self.load_ext("int16_t", *offset, operands, results) } + Instruction::Free { .. } => { + self.src + .push_str(&format!("free((void*) ({}));\n", operands[0])); + } + i => unimplemented!("{:?}", i), } } diff --git a/crates/gen-js/src/lib.rs b/crates/gen-js/src/lib.rs index 55776bfde..fe813fd3b 100644 --- a/crates/gen-js/src/lib.rs +++ b/crates/gen-js/src/lib.rs @@ -1158,7 +1158,7 @@ impl Bindgen for FunctionBindgen<'_> { self.blocks.push((src.into(), mem::take(operands))); } - fn return_pointer(&mut self, _iface: &Interface, _ty: &Type) -> String { + fn return_pointer(&mut self, _size: usize, _align: usize) -> String { unimplemented!() } @@ -1944,6 +1944,21 @@ impl Bindgen for FunctionBindgen<'_> { Instruction::I32Store8 { offset } => self.store("setInt8", *offset, operands), Instruction::I32Store16 { offset } => self.store("setInt16", *offset, operands), + Instruction::Malloc { + realloc, + size, + align, + } => { + self.needs_realloc = Some(realloc.to_string()); + let tmp = self.tmp(); + let ptr = format!("ptr{}", tmp); + self.src.js(&format!( + "const {} = realloc(0, 0, {}, {});\n", + ptr, align, size + )); + results.push(ptr); + } + i => unimplemented!("{:?}", i), } } diff --git a/crates/gen-rust-wasm/src/lib.rs b/crates/gen-rust-wasm/src/lib.rs index 0b7ce8bde..4b5caf4f6 100644 --- a/crates/gen-rust-wasm/src/lib.rs +++ b/crates/gen-rust-wasm/src/lib.rs @@ -159,18 +159,6 @@ impl Generator for RustWasm { } self.sizes.fill(iface); - - for func in iface.functions.iter() { - let sig = iface.wasm_signature(variant, func); - if sig.retptr { - self.return_pointer_area_size = self - .return_pointer_area_size - .max(self.sizes.size(&func.result)); - self.return_pointer_area_align = self - .return_pointer_area_align - .max(self.sizes.align(&func.result)); - } - } } fn type_record( @@ -791,7 +779,9 @@ impl Bindgen for FunctionBindgen<'_> { } } - fn return_pointer(&mut self, _iface: &Interface, _ty: &Type) -> String { + fn return_pointer(&mut self, size: usize, align: usize) -> String { + self.gen.return_pointer_area_size = self.gen.return_pointer_area_size.max(size); + self.gen.return_pointer_area_align = self.gen.return_pointer_area_align.max(align); let tmp = self.tmp(); self.push_str(&format!( "let ptr{} = RET_AREA.0.as_mut_ptr() as i32;\n", @@ -1422,6 +1412,18 @@ impl Bindgen for FunctionBindgen<'_> { operands[1], offset, operands[0] )); } + + Instruction::Malloc { .. } => unimplemented!(), + Instruction::Free { + free: _, + size, + align, + } => { + self.push_str(&format!( + "wit_bindgen_rust::rt::canonical_abi_free({} as *mut u8, {}, {});\n", + operands[0], size, align + )); + } } } } diff --git a/crates/gen-spidermonkey/src/lib.rs b/crates/gen-spidermonkey/src/lib.rs index 13b1acf31..fd49e07ab 100644 --- a/crates/gen-spidermonkey/src/lib.rs +++ b/crates/gen-spidermonkey/src/lib.rs @@ -48,7 +48,7 @@ lazy_static! { WasmSignature { params: vec![], results: vec![], - retptr: false, + retptr: false,indirect_params:false, }, ), ( @@ -56,7 +56,7 @@ lazy_static! { WasmSignature { params: vec![WasmType::I32, WasmType::I32, WasmType::I32], results: vec![], - retptr: false, + retptr: false,indirect_params:false, }, ), ( @@ -64,7 +64,7 @@ lazy_static! { WasmSignature { params: vec![WasmType::I32, WasmType::I32, WasmType::I32, WasmType::I32], results: vec![WasmType::I32], - retptr: false, + retptr: false,indirect_params:false, }, ), ( @@ -72,7 +72,7 @@ lazy_static! { WasmSignature { params: vec![], results: vec![], - retptr: false, + retptr: false,indirect_params:false, }, ), ( @@ -80,7 +80,7 @@ lazy_static! { WasmSignature { params: vec![WasmType::I32, WasmType::I32], results: vec![WasmType::I32], - retptr: false, + retptr: false,indirect_params:false, }, ), ( @@ -88,7 +88,7 @@ lazy_static! { WasmSignature { params: vec![WasmType::I32, WasmType::I32, WasmType::I32, WasmType::I32, WasmType::I32], results: vec![], - retptr: false, + retptr: false,indirect_params:false, }, ), ( @@ -96,7 +96,7 @@ lazy_static! { WasmSignature { params: vec![WasmType::I32], results: vec![], - retptr: false, + retptr: false,indirect_params:false, }, ), ( @@ -104,7 +104,7 @@ lazy_static! { WasmSignature { params: vec![WasmType::I32, WasmType::I32, WasmType::I32], results: vec![], - retptr: false, + retptr: false,indirect_params:false, }, ), ( @@ -112,7 +112,7 @@ lazy_static! { WasmSignature { params: vec![WasmType::I32], results: vec![WasmType::I32], - retptr: false, + retptr: false,indirect_params:false, }, ), ( @@ -120,7 +120,7 @@ lazy_static! { WasmSignature { params: vec![WasmType::I32, WasmType::I32], results: vec![], - retptr: false, + retptr: false,indirect_params:false, } ), ( @@ -128,7 +128,7 @@ lazy_static! { WasmSignature { params: vec![], results: vec![], - retptr: false, + retptr: false,indirect_params:false, }, ), ( @@ -136,7 +136,7 @@ lazy_static! { WasmSignature { params: vec![WasmType::I32], results: vec![], - retptr: false, + retptr: false,indirect_params:false, }, ), ( @@ -144,7 +144,7 @@ lazy_static! { WasmSignature { params: vec![WasmType::I32, WasmType::I32, WasmType::I32, WasmType::I32], results: vec![], - retptr: false, + retptr: false,indirect_params:false, }, ), ( @@ -152,7 +152,7 @@ lazy_static! { WasmSignature { params: vec![WasmType::I32], results: vec![], - retptr: false, + retptr: false,indirect_params:false, }, ), ( @@ -160,7 +160,7 @@ lazy_static! { WasmSignature { params: vec![WasmType::I32, WasmType::I32], results: vec![], - retptr: false, + retptr: false,indirect_params:false, }, ), ( @@ -168,7 +168,7 @@ lazy_static! { WasmSignature { params: vec![WasmType::I32], results: vec![WasmType::I32], - retptr: false, + retptr: false,indirect_params:false, }, ), ( @@ -176,7 +176,7 @@ lazy_static! { WasmSignature { params: vec![WasmType::I32, WasmType::I32], results: vec![], - retptr: false, + retptr: false,indirect_params:false, }, ), ( @@ -184,7 +184,7 @@ lazy_static! { WasmSignature { params: vec![WasmType::I32, WasmType::I32], results: vec![], - retptr: false, + retptr: false,indirect_params:false, }, ), ( @@ -192,7 +192,7 @@ lazy_static! { WasmSignature { params: vec![WasmType::I32, WasmType::I32, WasmType::I32], results: vec![], - retptr: false, + retptr: false,indirect_params:false, }, ), ( @@ -200,7 +200,7 @@ lazy_static! { WasmSignature { params: vec![WasmType::I32], results: vec![WasmType::I32], - retptr: false, + retptr: false,indirect_params:false, }, ), ( @@ -208,7 +208,7 @@ lazy_static! { WasmSignature { params: vec![WasmType::I32, WasmType::I32, WasmType::I32], results: vec![], - retptr: false, + retptr: false,indirect_params:false, }, ), ( @@ -216,7 +216,7 @@ lazy_static! { WasmSignature { params: vec![WasmType::I32, WasmType::I32], results: vec![], - retptr: false, + retptr: false,indirect_params:false, }, ), ( @@ -224,7 +224,7 @@ lazy_static! { WasmSignature { params: vec![WasmType::I32], results: vec![], - retptr: false, + retptr: false,indirect_params:false, }, ), ( @@ -232,7 +232,7 @@ lazy_static! { WasmSignature { params: vec![WasmType::I32], results: vec![WasmType::I32], - retptr: false, + retptr: false,indirect_params:false, }, ), ]; @@ -585,6 +585,7 @@ impl<'a> SpiderMonkeyWasm<'a> { params: vec![], results: vec![], retptr: false, + indirect_params: false, }); funcs.function(ty_index); @@ -908,30 +909,6 @@ impl Generator for SpiderMonkeyWasm<'_> { Some(u32::try_from(imports.iter().map(|i| i.functions.len()).sum::()).unwrap()); self.num_export_functions = Some(u32::try_from(exports.iter().map(|i| i.functions.len()).sum::()).unwrap()); - - // Figure out what the maximum return pointer area we will need is. - for (iface, variant) in imports - .iter() - .zip(std::iter::repeat(AbiVariant::GuestImport)) - .chain( - exports - .iter() - .zip(std::iter::repeat(AbiVariant::GuestExport)), - ) - { - self.sizes.fill(iface); - for func in iface.functions.iter() { - let sig = iface.wasm_signature(variant, func); - if sig.retptr { - self.return_pointer_area_size = self - .return_pointer_area_size - .max(self.sizes.size(&func.result)); - self.return_pointer_area_align = self - .return_pointer_area_align - .max(self.sizes.align(&func.result)); - } - } - } } fn preprocess_one(&mut self, iface: &Interface, _dir: Direction) { @@ -1101,6 +1078,7 @@ impl Generator for SpiderMonkeyWasm<'_> { WasmType::I32, ], retptr: false, + indirect_params: false, }); for f in &self.import_glue_fns { funcs.function(js_native_type_index); @@ -2118,10 +2096,15 @@ impl abi::Bindgen for Bindgen<'_, '_> { abi::Instruction::ReturnAsyncExport { .. } => todo!(), abi::Instruction::ReturnAsyncImport { .. } => todo!(), + + abi::Instruction::Malloc { .. } => todo!(), + abi::Instruction::Free { .. } => todo!(), } } - fn return_pointer(&mut self, _iface: &Interface, _ty: &Type) -> Self::Operand { + fn return_pointer(&mut self, size: usize, align: usize) -> Self::Operand { + self.gen.return_pointer_area_size = self.gen.return_pointer_area_size.max(size); + self.gen.return_pointer_area_align = self.gen.return_pointer_area_align.max(align); let local = self.new_local(wasm_encoder::ValType::I32); // [] @@ -2142,7 +2125,7 @@ impl abi::Bindgen for Bindgen<'_, '_> { } fn sizes(&self) -> &SizeAlign { - todo!() + &self.gen.sizes } fn is_list_canonical(&self, _iface: &Interface, _ty: &Type) -> bool { diff --git a/crates/gen-wasmtime-py/src/lib.rs b/crates/gen-wasmtime-py/src/lib.rs index f8c1879b8..be7cc5bc5 100644 --- a/crates/gen-wasmtime-py/src/lib.rs +++ b/crates/gen-wasmtime-py/src/lib.rs @@ -1303,7 +1303,7 @@ impl Bindgen for FunctionBindgen<'_> { self.blocks.push((src.into(), mem::take(operands))); } - fn return_pointer(&mut self, _iface: &Interface, _ty: &Type) -> String { + fn return_pointer(&mut self, _size: usize, _align: usize) -> String { unimplemented!() } @@ -2036,6 +2036,22 @@ impl Bindgen for FunctionBindgen<'_> { Instruction::I32Store8 { offset } => self.store("c_uint8", *offset, operands), Instruction::I32Store16 { offset } => self.store("c_uint16", *offset, operands), + Instruction::Malloc { + realloc, + size, + align, + } => { + self.needs_realloc = Some(realloc.to_string()); + let ptr = self.locals.tmp("ptr"); + self.src.push_str(&format!( + " + {ptr} = realloc(caller, 0, 0, {align}, {size}) + assert(isinstance({ptr}, int)) + ", + )); + results.push(ptr); + } + i => unimplemented!("{:?}", i), } } diff --git a/crates/gen-wasmtime/src/lib.rs b/crates/gen-wasmtime/src/lib.rs index 22036fa16..5f0d26e48 100644 --- a/crates/gen-wasmtime/src/lib.rs +++ b/crates/gen-wasmtime/src/lib.rs @@ -1374,7 +1374,7 @@ impl Bindgen for FunctionBindgen<'_> { self.caller_memory_available = false; } - fn return_pointer(&mut self, _iface: &Interface, _ty: &Type) -> String { + fn return_pointer(&mut self, _size: usize, _align: usize) -> String { unimplemented!() } @@ -2009,6 +2009,22 @@ impl Bindgen for FunctionBindgen<'_> { Instruction::I32Store16 { offset } => { self.store(*offset, "as_i32", " as u16", operands) } + + Instruction::Malloc { + realloc, + size, + align, + } => { + self.needs_functions + .insert(realloc.to_string(), NeededFunction::Realloc); + let tmp = self.tmp(); + let ptr = format!("ptr{}", tmp); + self.push_str(&format!("let {} = ", ptr)); + self.call_intrinsic(realloc, format!("(0, 0, {}, {})", align, size)); + results.push(ptr); + } + + Instruction::Free { .. } => unimplemented!(), } } } diff --git a/crates/parser/src/abi.rs b/crates/parser/src/abi.rs index 991cbefa7..516ff59cd 100644 --- a/crates/parser/src/abi.rs +++ b/crates/parser/src/abi.rs @@ -1,3 +1,4 @@ +use crate::sizealign::align_to; use crate::{ Function, Int, Interface, Record, RecordKind, ResourceId, Type, TypeDefKind, TypeId, Variant, }; @@ -8,8 +9,18 @@ use std::mem; pub struct WasmSignature { /// The WebAssembly parameters of this function. pub params: Vec, + /// The WebAssembly results of this function. pub results: Vec, + + /// Whether or not this signature is passing all of its parameters + /// indirectly through a pointer within `params`. + /// + /// Note that `params` still reflects the true wasm paramters of this + /// function, this is auxiliary information for code generators if + /// necessary. + pub indirect_params: bool, + /// Whether or not this signature is using a return pointer to store the /// result of the function, which is reflected either in `params` or /// `results` depending on the context this function is used (e.g. an import @@ -654,6 +665,19 @@ def_instruction! { params: usize, } : [*params + 2] => [0], + /// TODO + Malloc { + realloc: &'static str, + size: usize, + align: usize, + } : [0] => [1], + + /// TODO + Free { + free: &'static str, + size: usize, + align: usize, + } : [1] => [0], } } @@ -748,7 +772,7 @@ pub trait Bindgen { ); /// TODO - fn return_pointer(&mut self, iface: &Interface, ty: &Type) -> Self::Operand; + fn return_pointer(&mut self, size: usize, align: usize) -> Self::Operand; /// Enters a new block of code to generate code for. /// @@ -791,12 +815,22 @@ impl Interface { /// The first entry returned is the list of parameters and the second entry /// is the list of results for the wasm function signature. pub fn wasm_signature(&self, variant: AbiVariant, func: &Function) -> WasmSignature { + const MAX_FLAT_PARAMS: usize = 16; + const MAX_FLAT_RESULTS: usize = 1; + let mut params = Vec::new(); - let mut results = Vec::new(); + let mut indirect_params = false; for (_, param) in func.params.iter() { self.push_wasm(variant, param, &mut params); } + if params.len() > MAX_FLAT_PARAMS { + params.truncate(0); + params.push(WasmType::I32); + indirect_params = true; + } + + let mut results = Vec::new(); self.push_wasm(variant, &func.result, &mut results); let mut retptr = false; @@ -828,7 +862,7 @@ impl Interface { // would have multiple results then instead truncate it. Imports take a // return pointer to write into and exports return a pointer they wrote // into. - if results.len() > 1 { + if results.len() > MAX_FLAT_RESULTS { retptr = true; results.truncate(0); match variant { @@ -844,6 +878,7 @@ impl Interface { WasmSignature { params, + indirect_params, results, retptr, } @@ -997,12 +1032,47 @@ impl<'a, B: Bindgen> Generator<'a, B> { match self.lift_lower { LiftLower::LowerArgsLiftResults => { - // Push all parameters for this function onto the stack, and - // then batch-lower everything all at once. - for nth in 0..func.params.len() { - self.emit(&Instruction::GetArg { nth }); + if !sig.indirect_params { + // If the parameters for this function aren't indirect + // (there aren't too many) then we simply do a normal lower + // operation for them all. + for (nth, (_, ty)) in func.params.iter().enumerate() { + self.emit(&Instruction::GetArg { nth }); + self.lower(ty); + } + } else { + // ... otherwise if parameters are indirect space is + // allocated from them and each argument is lowered + // individually into memory. + let (size, align) = self + .bindgen + .sizes() + .record(func.params.iter().map(|t| &t.1)); + let ptr = match self.variant { + // When a wasm module calls an import it will provide + // static space that isn't dynamically allocated. + AbiVariant::GuestImport => self.bindgen.return_pointer(size, align), + // When calling a wasm module from the outside, though, + // malloc needs to be called. + AbiVariant::GuestExport => { + self.emit(&Instruction::Malloc { + realloc: "canonical_abi_realloc", + size, + align, + }); + self.stack.pop().unwrap() + } + }; + let mut offset = 0usize; + for (nth, (_, ty)) in func.params.iter().enumerate() { + self.emit(&Instruction::GetArg { nth }); + offset = align_to(offset, self.bindgen.sizes().align(ty)); + self.write_to_memory(ty, ptr.clone(), offset as i32); + offset += self.bindgen.sizes().size(ty); + } + + self.stack.push(ptr); } - self.lower_all(&func.params); if func.is_async { // We emit custom instructions for async calls since they @@ -1040,7 +1110,9 @@ impl<'a, B: Bindgen> Generator<'a, B> { // If necessary we may need to prepare a return pointer for // this ABI. if self.variant == AbiVariant::GuestImport && sig.retptr { - let ptr = self.bindgen.return_pointer(self.iface, &func.result); + let size = self.bindgen.sizes().size(&func.result); + let align = self.bindgen.sizes().align(&func.result); + let ptr = self.bindgen.return_pointer(size, align); self.return_pointer = Some(ptr.clone()); self.stack.push(ptr); } @@ -1084,36 +1156,58 @@ impl<'a, B: Bindgen> Generator<'a, B> { self.emit(&Instruction::Return { func, amt: 1 }); } LiftLower::LiftArgsLowerResults => { - // Use `GetArg` to push all relevant arguments onto the stack. - // Note that we can't use the signature of this function - // directly due to various conversions and return pointers, so - // we need to somewhat manually calculate all the arguments - // which are converted as interface types arguments below. - let nargs = { - let skip_cnt = if func.is_async { - match self.variant { - AbiVariant::GuestExport => 1, - AbiVariant::GuestImport => 2, + if !sig.indirect_params { + // If parameters are not passed indirectly then we lift each + // argument in succession from the component wasm types that + // make-up the type. + let mut offset = 0; + let mut temp = Vec::new(); + for (_, ty) in func.params.iter() { + temp.truncate(0); + self.iface.push_wasm(self.variant, ty, &mut temp); + for _ in 0..temp.len() { + self.emit(&Instruction::GetArg { nth: offset }); + offset += 1; } - } else { - (sig.retptr && self.variant == AbiVariant::GuestImport) as usize - }; - sig.params.len() - skip_cnt - }; - for nth in 0..nargs { - self.emit(&Instruction::GetArg { nth }); + self.lift(ty); + } + } else { + // ... otherwise argument is read in succession from memory + // where the pointer to the arguments is the first argument + // to the function. + let mut offset = 0usize; + self.emit(&Instruction::GetArg { nth: 0 }); + let ptr = self.stack.pop().unwrap(); + for (_, ty) in func.params.iter() { + offset = align_to(offset, self.bindgen.sizes().align(ty)); + self.read_from_memory(ty, ptr.clone(), offset as i32); + offset += self.bindgen.sizes().size(ty); + } } - // Once everything is on the stack we can lift all arguments - // one-by-one into their interface-types equivalent. - self.lift_all(&func.params); - // ... and that allows us to call the interface types function self.emit(&Instruction::CallInterface { module: &self.iface.name, func, }); + // This was dynamically allocated by the caller so after + // it's been read by the guest we need to deallocate it. + if let AbiVariant::GuestExport = self.variant { + if sig.indirect_params { + let (size, align) = self + .bindgen + .sizes() + .record(func.params.iter().map(|t| &t.1)); + self.emit(&Instruction::GetArg { nth: 0 }); + self.emit(&Instruction::Free { + free: "canonical_abi_free", + size, + align, + }); + } + } + if func.is_async { match self.variant { // Returning from a guest import means that the @@ -1146,7 +1240,9 @@ impl<'a, B: Bindgen> Generator<'a, B> { // invoke the completion intrinsics with where the // result is stored in linear memory. AbiVariant::GuestExport => { - let ptr = self.bindgen.return_pointer(self.iface, &func.result); + let size = self.bindgen.sizes().size(&func.result); + let align = self.bindgen.sizes().align(&func.result); + let ptr = self.bindgen.return_pointer(size, align); self.write_to_memory(&func.result, ptr.clone(), 0); // Get the caller's context index. @@ -1187,7 +1283,9 @@ impl<'a, B: Bindgen> Generator<'a, B> { // (statically) and then write the result into that // memory, returning the pointer at the end. AbiVariant::GuestExport => { - let ptr = self.bindgen.return_pointer(self.iface, &func.result); + let size = self.bindgen.sizes().size(&func.result); + let align = self.bindgen.sizes().align(&func.result); + let ptr = self.bindgen.return_pointer(size, align); self.write_to_memory(&func.result, ptr.clone(), 0); self.stack.push(ptr); } @@ -1209,47 +1307,6 @@ impl<'a, B: Bindgen> Generator<'a, B> { ); } - /// Assumes that the wasm values to create `tys` are all located on the - /// stack. - /// - /// Inserts instructions necesesary to lift those types into their - /// interface types equivalent. - fn lift_all(&mut self, tys: &[(String, Type)]) { - let mut temp = Vec::new(); - let operands = tys - .iter() - .rev() - .map(|(_, ty)| { - let ntys = { - temp.truncate(0); - self.iface.push_wasm(self.variant, ty, &mut temp); - temp.len() - }; - self.stack - .drain(self.stack.len() - ntys..) - .collect::>() - }) - .collect::>(); - for (operands, (_, ty)) in operands.into_iter().rev().zip(tys) { - self.stack.extend(operands); - self.lift(ty); - } - } - - /// Assumes that the value for `tys` is already on the stack, and then - /// converts all of those values into their wasm types by lowering each - /// argument in-order. - fn lower_all(&mut self, tys: &[(String, Type)]) { - let operands = self - .stack - .drain(self.stack.len() - tys.len()..) - .collect::>(); - for (operand, (_, ty)) in operands.into_iter().zip(tys) { - self.stack.push(operand); - self.lower(ty); - } - } - fn emit(&mut self, inst: &Instruction<'_>) { self.operands.clear(); self.results.clear(); diff --git a/crates/parser/src/sizealign.rs b/crates/parser/src/sizealign.rs index 491bc7ebc..780610f22 100644 --- a/crates/parser/src/sizealign.rs +++ b/crates/parser/src/sizealign.rs @@ -29,15 +29,7 @@ impl SizeAlign { None => (r.num_i32s() * 4, 4), }; } - let mut size = 0; - let mut align = 1; - for f in r.fields.iter() { - let field_size = self.size(&f.ty); - let field_align = self.align(&f.ty); - size = align_to(size, field_align) + field_size; - align = align.max(field_align); - } - (align_to(size, align), align) + self.record(r.fields.iter().map(|f| &f.ty)) } TypeDefKind::Variant(v) => { let (discrim_size, discrim_align) = int_size_align(v.tag); @@ -102,6 +94,18 @@ impl SizeAlign { let tag_size = int_size_align(variant.tag).0; align_to(tag_size, max_align) } + + pub fn record<'a>(&self, types: impl Iterator) -> (usize, usize) { + let mut size = 0; + let mut align = 1; + for ty in types { + let field_size = self.size(ty); + let field_align = self.align(ty); + size = align_to(size, field_align) + field_size; + align = align.max(field_align); + } + (align_to(size, align), align) + } } fn int_size_align(i: Int) -> (usize, usize) { @@ -113,6 +117,6 @@ fn int_size_align(i: Int) -> (usize, usize) { } } -fn align_to(val: usize, align: usize) -> usize { +pub(crate) fn align_to(val: usize, align: usize) -> usize { (val + align - 1) & !(align - 1) } diff --git a/crates/rust-wasm/src/lib.rs b/crates/rust-wasm/src/lib.rs index 865a33702..d7c4133a8 100644 --- a/crates/rust-wasm/src/lib.rs +++ b/crates/rust-wasm/src/lib.rs @@ -158,7 +158,7 @@ pub mod rt { } #[no_mangle] - unsafe extern "C" fn canonical_abi_free(ptr: *mut u8, len: usize, align: usize) { + pub unsafe extern "C" fn canonical_abi_free(ptr: *mut u8, len: usize, align: usize) { if len == 0 { return; } diff --git a/crates/test-rust-wasm/Cargo.toml b/crates/test-rust-wasm/Cargo.toml index 3da333da6..d5e937ee0 100644 --- a/crates/test-rust-wasm/Cargo.toml +++ b/crates/test-rust-wasm/Cargo.toml @@ -47,3 +47,7 @@ test = false [[bin]] name = "async_functions" test = false + +[[bin]] +name = "many_arguments" +test = false diff --git a/crates/test-rust-wasm/src/bin/many_arguments.rs b/crates/test-rust-wasm/src/bin/many_arguments.rs new file mode 100644 index 000000000..6033fcce9 --- /dev/null +++ b/crates/test-rust-wasm/src/bin/many_arguments.rs @@ -0,0 +1,3 @@ +include!("../../../../tests/runtime/many_arguments/wasm.rs"); + +fn main() {} diff --git a/tests/codegen/many-arguments.wit b/tests/codegen/many-arguments.wit new file mode 100644 index 000000000..475c94525 --- /dev/null +++ b/tests/codegen/many-arguments.wit @@ -0,0 +1,47 @@ +many-args: function( + a1: u64, + a2: u64, + a3: u64, + a4: u64, + a5: u64, + a6: u64, + a7: u64, + a8: u64, + a9: u64, + a10: u64, + a11: u64, + a12: u64, + a13: u64, + a14: u64, + a15: u64, + a16: u64, + a17: u64, + a18: u64, + a19: u64, + a20: u64, +) + +record big-struct { + a1: string, + a2: string, + a3: string, + a4: string, + a5: string, + a6: string, + a7: string, + a8: string, + a9: string, + a10: string, + a11: string, + a12: string, + a13: string, + a14: string, + a15: string, + a16: string, + a17: string, + a18: string, + a19: string, + a20: string, +} + +big-argument: function(x: big-struct) diff --git a/tests/runtime/many_arguments/exports.wit b/tests/runtime/many_arguments/exports.wit new file mode 100644 index 000000000..be1b18287 --- /dev/null +++ b/tests/runtime/many_arguments/exports.wit @@ -0,0 +1,22 @@ +many-arguments: function( + a1: u64, + a2: u64, + a3: u64, + a4: u64, + a5: u64, + a6: u64, + a7: u64, + a8: u64, + a9: u64, + a10: u64, + a11: u64, + a12: u64, + a13: u64, + a14: u64, + a15: u64, + a16: u64, + a17: u64, + a18: u64, + a19: u64, + a20: u64, +) diff --git a/tests/runtime/many_arguments/host.py b/tests/runtime/many_arguments/host.py new file mode 100644 index 000000000..5f5c2c089 --- /dev/null +++ b/tests/runtime/many_arguments/host.py @@ -0,0 +1,68 @@ +from exports.bindings import Exports +from imports.bindings import add_imports_to_linker, Imports +import math; +import sys +import wasmtime + +class MyImports: + def many_arguments(self, + a1: int, + a2: int, + a3: int, + a4: int, + a5: int, + a6: int, + a7: int, + a8: int, + a9: int, + a10: int, + a11: int, + a12: int, + a13: int, + a14: int, + a15: int, + a16: int, + a17: int, + a18: int, + a19: int, + a20: int) -> None: + assert(a1 == 1) + assert(a2 == 2) + assert(a3 == 3) + assert(a4 == 4) + assert(a5 == 5) + assert(a6 == 6) + assert(a7 == 7) + assert(a8 == 8) + assert(a9 == 9) + assert(a10 == 10) + assert(a11 == 11) + assert(a12 == 12) + assert(a13 == 13) + assert(a14 == 14) + assert(a15 == 15) + assert(a16 == 16) + assert(a17 == 17) + assert(a18 == 18) + assert(a19 == 19) + assert(a20 == 20) + + +def run(wasm_file: str) -> None: + store = wasmtime.Store() + module = wasmtime.Module.from_file(store.engine, wasm_file) + linker = wasmtime.Linker(store.engine) + linker.define_wasi() + wasi = wasmtime.WasiConfig() + wasi.inherit_stdout() + wasi.inherit_stderr() + store.set_wasi(wasi) + + imports = MyImports() + add_imports_to_linker(linker, store, imports) + wasm = Exports(store, linker, module) + + wasm.many_arguments(store, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,14, 15, 16, 17, 18, 19, 20) + +if __name__ == '__main__': + run(sys.argv[1]) diff --git a/tests/runtime/many_arguments/host.rs b/tests/runtime/many_arguments/host.rs new file mode 100644 index 000000000..61f962580 --- /dev/null +++ b/tests/runtime/many_arguments/host.rs @@ -0,0 +1,73 @@ +use anyhow::Result; + +wit_bindgen_wasmtime::export!("../../tests/runtime/many_arguments/imports.wit"); + +#[derive(Default)] +pub struct MyImports { + scalar: u32, +} + +impl imports::Imports for MyImports { + fn many_arguments( + &mut self, + a1: u64, + a2: u64, + a3: u64, + a4: u64, + a5: u64, + a6: u64, + a7: u64, + a8: u64, + a9: u64, + a10: u64, + a11: u64, + a12: u64, + a13: u64, + a14: u64, + a15: u64, + a16: u64, + a17: u64, + a18: u64, + a19: u64, + a20: u64, + ) { + assert_eq!(a1, 1); + assert_eq!(a2, 2); + assert_eq!(a3, 3); + assert_eq!(a4, 4); + assert_eq!(a5, 5); + assert_eq!(a6, 6); + assert_eq!(a7, 7); + assert_eq!(a8, 8); + assert_eq!(a9, 9); + assert_eq!(a10, 10); + assert_eq!(a11, 11); + assert_eq!(a12, 12); + assert_eq!(a13, 13); + assert_eq!(a14, 14); + assert_eq!(a15, 15); + assert_eq!(a16, 16); + assert_eq!(a17, 17); + assert_eq!(a18, 18); + assert_eq!(a19, 19); + assert_eq!(a20, 20); + } +} + +wit_bindgen_wasmtime::import!("../../tests/runtime/many_arguments/exports.wit"); + +fn run(wasm: &str) -> Result<()> { + let (exports, mut store) = crate::instantiate( + wasm, + |linker| imports::add_to_linker(linker, |cx| -> &mut MyImports { &mut cx.imports }), + |store, module, linker| { + exports::Exports::instantiate(store, module, linker, |cx| &mut cx.exports) + }, + )?; + + exports.many_arguments( + &mut store, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + )?; + + Ok(()) +} diff --git a/tests/runtime/many_arguments/host.ts b/tests/runtime/many_arguments/host.ts new file mode 100644 index 000000000..6d382eec8 --- /dev/null +++ b/tests/runtime/many_arguments/host.ts @@ -0,0 +1,95 @@ +import { addImportsToImports, Imports } from "./imports.js"; +import { Exports } from "./exports.js"; +import { getWasm, addWasiToImports } from "./helpers.js"; + +function assertEq(x: any, y: any) { + if (x !== y) + throw new Error(`${x} != ${y}`); +} + +function assert(x: boolean) { + if (!x) + throw new Error("assert failed"); +} + +async function run() { + const importObj = {}; + const imports: Imports = { + manyArguments( + a1, + a2, + a3, + a4, + a5, + a6, + a7, + a8, + a9, + a10, + a11, + a12, + a13, + a14, + a15, + a16, + a17, + a18, + a19, + a20, + ) { + assertEq(a1, 1n); + assertEq(a2, 2n); + assertEq(a3, 3n); + assertEq(a4, 4n); + assertEq(a5, 5n); + assertEq(a6, 6n); + assertEq(a7, 7n); + assertEq(a8, 8n); + assertEq(a9, 9n); + assertEq(a10, 10n); + assertEq(a11, 11n); + assertEq(a12, 12n); + assertEq(a13, 13n); + assertEq(a14, 14n); + assertEq(a15, 15n); + assertEq(a16, 16n); + assertEq(a17, 17n); + assertEq(a18, 18n); + assertEq(a19, 19n); + assertEq(a20, 20n); + }, + }; + let instance: WebAssembly.Instance; + addImportsToImports(importObj, imports, name => instance.exports[name]); + const wasi = addWasiToImports(importObj); + + const wasm = new Exports(); + await wasm.instantiate(getWasm(), importObj); + wasi.start(wasm.instance); + instance = wasm.instance; + + wasm.manyArguments( + 1n, + 2n, + 3n, + 4n, + 5n, + 6n, + 7n, + 8n, + 9n, + 10n, + 11n, + 12n, + 13n, + 14n, + 15n, + 16n, + 17n, + 18n, + 19n, + 20n, + ); +} + +await run() diff --git a/tests/runtime/many_arguments/imports.wit b/tests/runtime/many_arguments/imports.wit new file mode 100644 index 000000000..be1b18287 --- /dev/null +++ b/tests/runtime/many_arguments/imports.wit @@ -0,0 +1,22 @@ +many-arguments: function( + a1: u64, + a2: u64, + a3: u64, + a4: u64, + a5: u64, + a6: u64, + a7: u64, + a8: u64, + a9: u64, + a10: u64, + a11: u64, + a12: u64, + a13: u64, + a14: u64, + a15: u64, + a16: u64, + a17: u64, + a18: u64, + a19: u64, + a20: u64, +) diff --git a/tests/runtime/many_arguments/wasm.c b/tests/runtime/many_arguments/wasm.c new file mode 100644 index 000000000..4a4d03a85 --- /dev/null +++ b/tests/runtime/many_arguments/wasm.c @@ -0,0 +1,72 @@ +#include +#include +#include +#include +#include + +void exports_many_arguments( + uint64_t a1, + uint64_t a2, + uint64_t a3, + uint64_t a4, + uint64_t a5, + uint64_t a6, + uint64_t a7, + uint64_t a8, + uint64_t a9, + uint64_t a10, + uint64_t a11, + uint64_t a12, + uint64_t a13, + uint64_t a14, + uint64_t a15, + uint64_t a16, + uint64_t a17, + uint64_t a18, + uint64_t a19, + uint64_t a20 + ) { + assert(a1 == 1); + assert(a2 == 2); + assert(a3 == 3); + assert(a4 == 4); + assert(a5 == 5); + assert(a6 == 6); + assert(a7 == 7); + assert(a8 == 8); + assert(a9 == 9); + assert(a10 == 10); + assert(a11 == 11); + assert(a12 == 12); + assert(a13 == 13); + assert(a14 == 14); + assert(a15 == 15); + assert(a16 == 16); + assert(a17 == 17); + assert(a18 == 18); + assert(a19 == 19); + assert(a20 == 20); + + imports_many_arguments( + a1, + a2, + a3, + a4, + a5, + a6, + a7, + a8, + a9, + a10, + a11, + a12, + a13, + a14, + a15, + a16, + a17, + a18, + a19, + a20 + ); +} diff --git a/tests/runtime/many_arguments/wasm.rs b/tests/runtime/many_arguments/wasm.rs new file mode 100644 index 000000000..0acbcf933 --- /dev/null +++ b/tests/runtime/many_arguments/wasm.rs @@ -0,0 +1,56 @@ +wit_bindgen_rust::import!("../../tests/runtime/many_arguments/imports.wit"); +wit_bindgen_rust::export!("../../tests/runtime/many_arguments/exports.wit"); + +use imports::*; + +struct Exports; + +impl exports::Exports for Exports { + fn many_arguments( + a1: u64, + a2: u64, + a3: u64, + a4: u64, + a5: u64, + a6: u64, + a7: u64, + a8: u64, + a9: u64, + a10: u64, + a11: u64, + a12: u64, + a13: u64, + a14: u64, + a15: u64, + a16: u64, + a17: u64, + a18: u64, + a19: u64, + a20: u64, + ) { + assert_eq!(a1, 1); + assert_eq!(a2, 2); + assert_eq!(a3, 3); + assert_eq!(a4, 4); + assert_eq!(a5, 5); + assert_eq!(a6, 6); + assert_eq!(a7, 7); + assert_eq!(a8, 8); + assert_eq!(a9, 9); + assert_eq!(a10, 10); + assert_eq!(a11, 11); + assert_eq!(a12, 12); + assert_eq!(a13, 13); + assert_eq!(a14, 14); + assert_eq!(a15, 15); + assert_eq!(a16, 16); + assert_eq!(a17, 17); + assert_eq!(a18, 18); + assert_eq!(a19, 19); + assert_eq!(a20, 20); + many_arguments( + a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, + a20, + ); + } +} From 8ced4418061de77604d5fce6225df741ec05ad18 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 28 Apr 2022 12:28:24 -0700 Subject: [PATCH 2/2] Review comments --- crates/gen-spidermonkey/src/lib.rs | 74 ++++++++++++++++++++---------- crates/parser/src/abi.rs | 8 +++- 2 files changed, 55 insertions(+), 27 deletions(-) diff --git a/crates/gen-spidermonkey/src/lib.rs b/crates/gen-spidermonkey/src/lib.rs index fd49e07ab..0cce7d878 100644 --- a/crates/gen-spidermonkey/src/lib.rs +++ b/crates/gen-spidermonkey/src/lib.rs @@ -48,7 +48,8 @@ lazy_static! { WasmSignature { params: vec![], results: vec![], - retptr: false,indirect_params:false, + retptr: false, + indirect_params: false, }, ), ( @@ -56,7 +57,8 @@ lazy_static! { WasmSignature { params: vec![WasmType::I32, WasmType::I32, WasmType::I32], results: vec![], - retptr: false,indirect_params:false, + retptr: false, + indirect_params: false, }, ), ( @@ -64,7 +66,8 @@ lazy_static! { WasmSignature { params: vec![WasmType::I32, WasmType::I32, WasmType::I32, WasmType::I32], results: vec![WasmType::I32], - retptr: false,indirect_params:false, + retptr: false, + indirect_params: false, }, ), ( @@ -72,7 +75,8 @@ lazy_static! { WasmSignature { params: vec![], results: vec![], - retptr: false,indirect_params:false, + retptr: false, + indirect_params: false, }, ), ( @@ -80,7 +84,8 @@ lazy_static! { WasmSignature { params: vec![WasmType::I32, WasmType::I32], results: vec![WasmType::I32], - retptr: false,indirect_params:false, + retptr: false, + indirect_params: false, }, ), ( @@ -88,7 +93,8 @@ lazy_static! { WasmSignature { params: vec![WasmType::I32, WasmType::I32, WasmType::I32, WasmType::I32, WasmType::I32], results: vec![], - retptr: false,indirect_params:false, + retptr: false, + indirect_params: false, }, ), ( @@ -96,7 +102,8 @@ lazy_static! { WasmSignature { params: vec![WasmType::I32], results: vec![], - retptr: false,indirect_params:false, + retptr: false, + indirect_params: false, }, ), ( @@ -104,7 +111,8 @@ lazy_static! { WasmSignature { params: vec![WasmType::I32, WasmType::I32, WasmType::I32], results: vec![], - retptr: false,indirect_params:false, + retptr: false, + indirect_params: false, }, ), ( @@ -112,7 +120,8 @@ lazy_static! { WasmSignature { params: vec![WasmType::I32], results: vec![WasmType::I32], - retptr: false,indirect_params:false, + retptr: false, + indirect_params: false, }, ), ( @@ -120,15 +129,17 @@ lazy_static! { WasmSignature { params: vec![WasmType::I32, WasmType::I32], results: vec![], - retptr: false,indirect_params:false, - } + retptr: false, + indirect_params: false, + } ), ( "SMW_clear_operands", WasmSignature { params: vec![], results: vec![], - retptr: false,indirect_params:false, + retptr: false, + indirect_params: false, }, ), ( @@ -136,7 +147,8 @@ lazy_static! { WasmSignature { params: vec![WasmType::I32], results: vec![], - retptr: false,indirect_params:false, + retptr: false, + indirect_params: false, }, ), ( @@ -144,7 +156,8 @@ lazy_static! { WasmSignature { params: vec![WasmType::I32, WasmType::I32, WasmType::I32, WasmType::I32], results: vec![], - retptr: false,indirect_params:false, + retptr: false, + indirect_params: false, }, ), ( @@ -152,7 +165,8 @@ lazy_static! { WasmSignature { params: vec![WasmType::I32], results: vec![], - retptr: false,indirect_params:false, + retptr: false, + indirect_params: false, }, ), ( @@ -160,7 +174,8 @@ lazy_static! { WasmSignature { params: vec![WasmType::I32, WasmType::I32], results: vec![], - retptr: false,indirect_params:false, + retptr: false, + indirect_params: false, }, ), ( @@ -168,7 +183,8 @@ lazy_static! { WasmSignature { params: vec![WasmType::I32], results: vec![WasmType::I32], - retptr: false,indirect_params:false, + retptr: false, + indirect_params: false, }, ), ( @@ -176,7 +192,8 @@ lazy_static! { WasmSignature { params: vec![WasmType::I32, WasmType::I32], results: vec![], - retptr: false,indirect_params:false, + retptr: false, + indirect_params: false, }, ), ( @@ -184,7 +201,8 @@ lazy_static! { WasmSignature { params: vec![WasmType::I32, WasmType::I32], results: vec![], - retptr: false,indirect_params:false, + retptr: false, + indirect_params: false, }, ), ( @@ -192,7 +210,8 @@ lazy_static! { WasmSignature { params: vec![WasmType::I32, WasmType::I32, WasmType::I32], results: vec![], - retptr: false,indirect_params:false, + retptr: false, + indirect_params: false, }, ), ( @@ -200,7 +219,8 @@ lazy_static! { WasmSignature { params: vec![WasmType::I32], results: vec![WasmType::I32], - retptr: false,indirect_params:false, + retptr: false, + indirect_params: false, }, ), ( @@ -208,7 +228,8 @@ lazy_static! { WasmSignature { params: vec![WasmType::I32, WasmType::I32, WasmType::I32], results: vec![], - retptr: false,indirect_params:false, + retptr: false, + indirect_params: false, }, ), ( @@ -216,7 +237,8 @@ lazy_static! { WasmSignature { params: vec![WasmType::I32, WasmType::I32], results: vec![], - retptr: false,indirect_params:false, + retptr: false, + indirect_params: false, }, ), ( @@ -224,7 +246,8 @@ lazy_static! { WasmSignature { params: vec![WasmType::I32], results: vec![], - retptr: false,indirect_params:false, + retptr: false, + indirect_params: false, }, ), ( @@ -232,7 +255,8 @@ lazy_static! { WasmSignature { params: vec![WasmType::I32], results: vec![WasmType::I32], - retptr: false,indirect_params:false, + retptr: false, + indirect_params: false, }, ), ]; diff --git a/crates/parser/src/abi.rs b/crates/parser/src/abi.rs index 516ff59cd..b79ef55e6 100644 --- a/crates/parser/src/abi.rs +++ b/crates/parser/src/abi.rs @@ -665,14 +665,18 @@ def_instruction! { params: usize, } : [*params + 2] => [0], - /// TODO + /// Calls the `realloc` function specified in a malloc-like fashion + /// allocating `size` bytes with alignment `align`. + /// + /// Pushes the returned pointer onto the stack. Malloc { realloc: &'static str, size: usize, align: usize, } : [0] => [1], - /// TODO + /// Calls the `free` function specified to deallocate the pointer on the + /// stack which has `size` bytes with alignment `align`. Free { free: &'static str, size: usize,