From aca49e1a6e1e29b6ffe8430b5fe83eaabc9c9dc7 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 18 Nov 2019 10:12:41 -0600 Subject: [PATCH] Fix the anyref xform working on empty modules (#1861) If there's no need for a transformation then there's no need to inject anything, so make sure that wasm-bindgen with anyref passes enabled works on non-wasm-bindgen blobs as well. Closes bytecodealliance/cargo-wasi#16 --- crates/anyref-xform/src/lib.rs | 126 ++++++++++++++++---------- crates/cli/tests/wasm-bindgen/main.rs | 36 ++++++++ 2 files changed, 114 insertions(+), 48 deletions(-) diff --git a/crates/anyref-xform/src/lib.rs b/crates/anyref-xform/src/lib.rs index e4388024fe9..bd981519868 100644 --- a/crates/anyref-xform/src/lib.rs +++ b/crates/anyref-xform/src/lib.rs @@ -58,9 +58,9 @@ struct Transform<'a> { // Indices of items that we have injected or found. This state is maintained // during the pass execution. table: TableId, - clone_ref: FunctionId, - heap_alloc: FunctionId, - heap_dealloc: FunctionId, + clone_ref: Option, + heap_alloc: Option, + heap_dealloc: Option, stack_pointer: GlobalId, } @@ -185,33 +185,34 @@ impl Context { _ => {} } } - let heap_alloc = heap_alloc.ok_or_else(|| anyhow!("failed to find heap alloc"))?; - let heap_dealloc = heap_dealloc.ok_or_else(|| anyhow!("failed to find heap dealloc"))?; - - // Create a shim function that looks like: - // - // (func __wbindgen_object_clone_ref (param i32) (result i32) - // (local i32) - // (table.set - // (tee_local 1 (call $heap_alloc)) - // (table.get (local.get 0))) - // (local.get 1)) - let mut builder = - walrus::FunctionBuilder::new(&mut module.types, &[ValType::I32], &[ValType::I32]); - let arg = module.locals.add(ValType::I32); - let local = module.locals.add(ValType::I32); - - let mut body = builder.func_body(); - body.call(heap_alloc) - .local_tee(local) - .local_get(arg) - .table_get(table) - .table_set(table) - .local_get(local); - - let clone_ref = builder.finish(vec![arg], &mut module.funcs); - let name = "__wbindgen_object_clone_ref".to_string(); - module.funcs.get_mut(clone_ref).name = Some(name); + let mut clone_ref = None; + if let Some(heap_alloc) = heap_alloc { + // Create a shim function that looks like: + // + // (func __wbindgen_object_clone_ref (param i32) (result i32) + // (local i32) + // (table.set + // (tee_local 1 (call $heap_alloc)) + // (table.get (local.get 0))) + // (local.get 1)) + let mut builder = + walrus::FunctionBuilder::new(&mut module.types, &[ValType::I32], &[ValType::I32]); + let arg = module.locals.add(ValType::I32); + let local = module.locals.add(ValType::I32); + + let mut body = builder.func_body(); + body.call(heap_alloc) + .local_tee(local) + .local_get(arg) + .table_get(table) + .table_set(table) + .local_get(local); + + let func = builder.finish(vec![arg], &mut module.funcs); + let name = "__wbindgen_object_clone_ref".to_string(); + module.funcs.get_mut(func).name = Some(name); + clone_ref = Some(func); + } // And run the transformation! Transform { @@ -236,9 +237,9 @@ impl Transform<'_> { self.find_intrinsics(module)?; // Perform transformations of imports, exports, and function pointers. - self.process_imports(module); + self.process_imports(module)?; assert!(self.cx.imports.is_empty()); - self.process_exports(module); + self.process_exports(module)?; assert!(self.cx.exports.is_empty()); self.process_elements(module)?; assert!(self.cx.elements.is_empty()); @@ -251,7 +252,7 @@ impl Transform<'_> { // Perform all instruction transformations to rewrite calls between // functions and make sure everything is still hooked up right. - self.rewrite_calls(module); + self.rewrite_calls(module)?; Ok(()) } @@ -297,7 +298,22 @@ impl Transform<'_> { Ok(()) } - fn process_imports(&mut self, module: &mut Module) { + fn heap_alloc(&self) -> Result { + self.heap_alloc + .ok_or_else(|| anyhow!("failed to find the `__wbindgen_anyref_table_alloc` function")) + } + + fn clone_ref(&self) -> Result { + self.clone_ref + .ok_or_else(|| anyhow!("failed to find intrinsics to enable `clone_ref` function")) + } + + fn heap_dealloc(&self) -> Result { + self.heap_dealloc + .ok_or_else(|| anyhow!("failed to find the `__wbindgen_anyref_table_dealloc` function")) + } + + fn process_imports(&mut self, module: &mut Module) -> Result<(), Error> { for import in module.imports.iter_mut() { let f = match import.kind { walrus::ImportKind::Function(f) => f, @@ -315,16 +331,17 @@ impl Transform<'_> { &mut module.types, &mut module.funcs, &mut module.locals, - ); + )?; self.import_map.insert(f, shim); match &mut module.funcs.get_mut(f).kind { walrus::FunctionKind::Import(f) => f.ty = anyref_ty, _ => unreachable!(), } } + Ok(()) } - fn process_exports(&mut self, module: &mut Module) { + fn process_exports(&mut self, module: &mut Module) -> Result<(), Error> { // let mut new_exports = Vec::new(); for export in module.exports.iter_mut() { let f = match export.item { @@ -342,9 +359,10 @@ impl Transform<'_> { &mut module.types, &mut module.funcs, &mut module.locals, - ); + )?; export.item = shim.into(); } + Ok(()) } fn process_elements(&mut self, module: &mut Module) -> Result<(), Error> { @@ -372,7 +390,7 @@ impl Transform<'_> { &mut module.types, &mut module.funcs, &mut module.locals, - ); + )?; kind.elements.push(Some(shim)); } @@ -393,7 +411,7 @@ impl Transform<'_> { types: &mut walrus::ModuleTypes, funcs: &mut walrus::ModuleFunctions, locals: &mut walrus::ModuleLocals, - ) -> (FunctionId, TypeId) { + ) -> Result<(FunctionId, TypeId), Error> { let target = funcs.get_mut(shim_target); let (is_export, ty) = match &target.kind { walrus::FunctionKind::Import(f) => (false, f.ty), @@ -508,7 +526,7 @@ impl Transform<'_> { body.local_get(params[i]) .table_get(self.table) .local_get(params[i]) - .call(self.heap_dealloc); + .call(self.heap_dealloc()?); } Convert::Load { owned: false } => { body.local_get(params[i]).table_get(self.table); @@ -516,7 +534,7 @@ impl Transform<'_> { Convert::Store { owned: true } => { // Allocate space for the anyref, store it, and then leave // the index of the allocated anyref on the stack. - body.call(self.heap_alloc) + body.call(self.heap_alloc()?) .local_tee(scratch_i32) .local_get(params[i]) .table_set(self.table) @@ -559,13 +577,13 @@ impl Transform<'_> { body.local_tee(scratch_i32) .table_get(self.table) .local_get(scratch_i32) - .call(self.heap_dealloc); + .call(self.heap_dealloc()?); } else { // Imports are the opposite, we have any anyref on the stack // and convert it to an i32 by allocating space for it and // storing it there. body.local_set(scratch_anyref) - .call(self.heap_alloc) + .call(self.heap_alloc()?) .local_tee(scratch_i32) .local_get(scratch_anyref) .table_set(self.table) @@ -604,20 +622,32 @@ impl Transform<'_> { let name = format!("{}_anyref_shim", name); funcs.get_mut(id).name = Some(name); self.shims.insert(id); - (id, anyref_ty) + Ok((id, anyref_ty)) } - fn rewrite_calls(&mut self, module: &mut Module) { + fn rewrite_calls(&mut self, module: &mut Module) -> Result<(), Error> { for (id, func) in module.funcs.iter_local_mut() { if self.shims.contains(&id) { continue; } let entry = func.entry_block(); - dfs_pre_order_mut(&mut Rewrite { xform: self }, func, entry); + dfs_pre_order_mut( + &mut Rewrite { + clone_ref: self.clone_ref()?, + heap_dealloc: self.heap_dealloc()?, + xform: self, + }, + func, + entry, + ); } + return Ok(()); + struct Rewrite<'a, 'b> { xform: &'a Transform<'b>, + clone_ref: FunctionId, + heap_dealloc: FunctionId, } impl VisitorMut for Rewrite<'_, '_> { @@ -664,8 +694,8 @@ impl Transform<'_> { seq.instrs .insert(i, (RefNull {}.into(), InstrLocId::default())); } - Intrinsic::DropRef => call.func = self.xform.heap_dealloc, - Intrinsic::CloneRef => call.func = self.xform.clone_ref, + Intrinsic::DropRef => call.func = self.heap_dealloc, + Intrinsic::CloneRef => call.func = self.clone_ref, } } } diff --git a/crates/cli/tests/wasm-bindgen/main.rs b/crates/cli/tests/wasm-bindgen/main.rs index 312e5947d96..2f58e2fe7cd 100644 --- a/crates/cli/tests/wasm-bindgen/main.rs +++ b/crates/cli/tests/wasm-bindgen/main.rs @@ -197,3 +197,39 @@ fn bin_crate_works() { .success() .stdout("hello, world\n"); } + +#[test] +fn empty_interface_types() { + let (mut cmd, _out_dir) = Project::new("empty_interface_types") + .file( + "src/lib.rs", + r#" + #[no_mangle] + pub extern fn foo() {} + "# + ) + .file( + "Cargo.toml", + &format!( + " + [package] + name = \"empty_interface_types\" + authors = [] + version = \"1.0.0\" + edition = '2018' + + [dependencies] + wasm-bindgen = {{ path = '{}' }} + + [lib] + crate-type = ['cdylib'] + + [workspace] + ", + repo_root().display(), + ), + ) + .wasm_bindgen(""); + cmd.env("WASM_INTERFACE_TYPES", "1"); + cmd.assert().success(); +}