Skip to content

Commit ff6b4c4

Browse files
committed
Reimplement anyref processing and passes
This commit reimplements the `anyref` transformation pass tasked with taking raw rustc output and enhancing the module to use `anyref`. This was disabled in the previous commits during refactoring, and now the pass is re-enabled in the manner originally intended. Instead of being tangled up in the `js/mod.rs` pass, the anyref transformation now happens locally within one module, `cli-support/src/anyref.rs`, which exclusively uses the output of the `webidl` module which produces a WebIDL bindings section as well as an auxiliary wasm-bindgen specific section. This makes the anyref transform much more straightforward and local, ensuring that it doesn't propagate elsewhere and can be a largely local concern during the transformation. The main addition needed to support this pass was detailed knowledge of the ABI of a `Descriptor`. This knowledge is already implicitly hardcoded in `js2rust.rs` and `rust2js.rs` through the ABI shims generated. This was previously used for the anyref transformation to piggy-back what was already there, but as a separate pass we are unable to reuse the knowledge in the binding generator. Instead `Descriptor` now has two dedicated methods describing the various ABI properties of a type. This is then asserted to be correct (all the time) when processing bindings, ensuring that the two are kept in sync.
1 parent 0bd7c39 commit ff6b4c4

File tree

7 files changed

+265
-194
lines changed

7 files changed

+265
-194
lines changed

crates/anyref-xform/src/lib.rs

Lines changed: 36 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use std::cmp;
2020
use std::collections::{BTreeMap, HashMap, HashSet};
2121
use std::mem;
2222
use walrus::ir::*;
23+
use walrus::{ExportId, ImportId};
2324
use walrus::{FunctionId, GlobalId, InitExpr, Module, TableId, ValType};
2425

2526
// must be kept in sync with src/lib.rs and ANYREF_HEAP_START
@@ -32,8 +33,8 @@ pub struct Context {
3233
// Functions within the module that we're gonna be wrapping, organized by
3334
// type. The `Function` contains information about what arguments/return
3435
// values in the function signature should turn into anyref.
35-
imports: HashMap<String, HashMap<String, Function>>,
36-
exports: HashMap<String, Function>,
36+
imports: HashMap<ImportId, Function>,
37+
exports: HashMap<ExportId, Function>,
3738
elements: BTreeMap<u32, (u32, Function)>,
3839

3940
// When wrapping closures with new shims, this is the index of the next
@@ -42,9 +43,6 @@ pub struct Context {
4243

4344
// The anyref table we'll be using, injected after construction
4445
table: Option<TableId>,
45-
46-
// Whether or not the transformation will actually be run at the end
47-
pub enabled: bool,
4846
}
4947

5048
struct Transform<'a> {
@@ -68,7 +66,6 @@ struct Transform<'a> {
6866
}
6967

7068
struct Function {
71-
name: String,
7269
// A map of argument index to whether it's an owned or borrowed anyref
7370
// (owned = true)
7471
args: HashMap<usize, bool>,
@@ -87,10 +84,6 @@ impl Context {
8784
/// large the function table is so we know what indexes to hand out when
8885
/// we're appending entries.
8986
pub fn prepare(&mut self, module: &mut Module) -> Result<(), Error> {
90-
if !self.enabled {
91-
return Ok(());
92-
}
93-
9487
// Figure out what the maximum index of functions pointers are. We'll
9588
// be adding new entries to the function table later (maybe) so
9689
// precalculate this ahead of time.
@@ -118,35 +111,27 @@ impl Context {
118111
/// transformed. The actual transformation happens later during `run`.
119112
pub fn import_xform(
120113
&mut self,
121-
module: &str,
122-
name: &str,
114+
id: ImportId,
123115
anyref: &[(usize, bool)],
124116
ret_anyref: bool,
125117
) -> &mut Self {
126-
if !self.enabled {
127-
return self;
118+
if let Some(f) = self.function(anyref, ret_anyref) {
119+
self.imports.insert(id, f);
128120
}
129-
let f = self.function(name, anyref, ret_anyref);
130-
self.imports
131-
.entry(module.to_string())
132-
.or_insert_with(Default::default)
133-
.insert(name.to_string(), f);
134121
self
135122
}
136123

137124
/// Store information about an exported function that needs to be
138125
/// transformed. The actual transformation happens later during `run`.
139126
pub fn export_xform(
140127
&mut self,
141-
name: &str,
128+
id: ExportId,
142129
anyref: &[(usize, bool)],
143130
ret_anyref: bool,
144131
) -> &mut Self {
145-
if !self.enabled {
146-
return self;
132+
if let Some(f) = self.function(anyref, ret_anyref) {
133+
self.exports.insert(id, f);
147134
}
148-
let f = self.function(name, anyref, ret_anyref);
149-
self.exports.insert(name.to_string(), f);
150135
self
151136
}
152137

@@ -158,34 +143,26 @@ impl Context {
158143
idx: u32,
159144
anyref: &[(usize, bool)],
160145
ret_anyref: bool,
161-
) -> u32 {
162-
if !self.enabled {
163-
return idx;
164-
}
165-
let name = format!("closure{}", idx);
166-
let f = self.function(&name, anyref, ret_anyref);
167-
let ret = self.next_element;
168-
self.next_element += 1;
169-
self.elements.insert(ret, (idx, f));
170-
ret
146+
) -> Option<u32> {
147+
self.function(anyref, ret_anyref).map(|f| {
148+
let ret = self.next_element;
149+
self.next_element += 1;
150+
self.elements.insert(ret, (idx, f));
151+
ret
152+
})
171153
}
172154

173-
fn function(&self, name: &str, anyref: &[(usize, bool)], ret_anyref: bool) -> Function {
174-
Function {
175-
name: name.to_string(),
155+
fn function(&self, anyref: &[(usize, bool)], ret_anyref: bool) -> Option<Function> {
156+
if !ret_anyref && anyref.len() == 0 {
157+
return None;
158+
}
159+
Some(Function {
176160
args: anyref.iter().cloned().collect(),
177161
ret_anyref,
178-
}
179-
}
180-
181-
pub fn anyref_table_id(&self) -> TableId {
182-
self.table.unwrap()
162+
})
183163
}
184164

185165
pub fn run(&mut self, module: &mut Module) -> Result<(), Error> {
186-
if !self.enabled {
187-
return Ok(());
188-
}
189166
let table = self.table.unwrap();
190167

191168
// Inject a stack pointer global which will be used for managing the
@@ -261,9 +238,7 @@ impl Transform<'_> {
261238

262239
// Perform transformations of imports, exports, and function pointers.
263240
self.process_imports(module);
264-
for m in self.cx.imports.values() {
265-
assert!(m.is_empty());
266-
}
241+
assert!(self.cx.imports.is_empty());
267242
self.process_exports(module);
268243
assert!(self.cx.exports.is_empty());
269244
self.process_elements(module)?;
@@ -333,20 +308,15 @@ impl Transform<'_> {
333308
walrus::ImportKind::Function(f) => f,
334309
_ => continue,
335310
};
336-
let import = {
337-
let entries = match self.cx.imports.get_mut(&import.module) {
338-
Some(s) => s,
339-
None => continue,
340-
};
341-
match entries.remove(&import.name) {
342-
Some(s) => s,
343-
None => continue,
344-
}
311+
let func = match self.cx.imports.remove(&import.id()) {
312+
Some(s) => s,
313+
None => continue,
345314
};
346315

347316
let shim = self.append_shim(
348317
f,
349-
import,
318+
&import.name,
319+
func,
350320
&mut module.types,
351321
&mut module.funcs,
352322
&mut module.locals,
@@ -356,29 +326,25 @@ impl Transform<'_> {
356326
}
357327

358328
fn process_exports(&mut self, module: &mut Module) {
359-
let mut new_exports = Vec::new();
360-
for export in module.exports.iter() {
329+
// let mut new_exports = Vec::new();
330+
for export in module.exports.iter_mut() {
361331
let f = match export.item {
362332
walrus::ExportItem::Function(f) => f,
363333
_ => continue,
364334
};
365-
let function = match self.cx.exports.remove(&export.name) {
335+
let function = match self.cx.exports.remove(&export.id()) {
366336
Some(s) => s,
367337
None => continue,
368338
};
369339
let shim = self.append_shim(
370340
f,
341+
&export.name,
371342
function,
372343
&mut module.types,
373344
&mut module.funcs,
374345
&mut module.locals,
375346
);
376-
new_exports.push((export.name.to_string(), shim, export.id()));
377-
}
378-
379-
for (name, shim, old_id) in new_exports {
380-
module.exports.add(&name, shim);
381-
module.exports.delete(old_id);
347+
export.item = shim.into();
382348
}
383349
}
384350

@@ -402,6 +368,7 @@ impl Transform<'_> {
402368
let target = kind.elements[idx as usize].unwrap();
403369
let shim = self.append_shim(
404370
target,
371+
&format!("closure{}", idx),
405372
function,
406373
&mut module.types,
407374
&mut module.funcs,
@@ -422,6 +389,7 @@ impl Transform<'_> {
422389
fn append_shim(
423390
&mut self,
424391
shim_target: FunctionId,
392+
name: &str,
425393
mut func: Function,
426394
types: &mut walrus::ModuleTypes,
427395
funcs: &mut walrus::ModuleFunctions,
@@ -625,7 +593,7 @@ impl Transform<'_> {
625593
// nice name for debugging and then we're good to go!
626594
let expr = builder.with_side_effects(before, result, after);
627595
let id = builder.finish_parts(shim_ty, params, vec![expr], types, funcs);
628-
let name = format!("{}_anyref_shim", func.name);
596+
let name = format!("{}_anyref_shim", name);
629597
funcs.get_mut(id).name = Some(name);
630598
self.shims.insert(id);
631599
return id;

crates/cli-support/src/anyref.rs

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
use crate::descriptor::Function;
2+
use crate::webidl::{ImportBinding, WasmBindgenAux, WebidlCustomSection, AuxImport};
3+
use failure::Error;
4+
use std::collections::HashSet;
5+
use walrus::Module;
6+
7+
pub fn process(module: &mut Module) -> Result<(), Error> {
8+
let mut cfg = wasm_bindgen_anyref_xform::Context::default();
9+
cfg.prepare(module)?;
10+
let bindings = module
11+
.customs
12+
.get_typed::<WebidlCustomSection>()
13+
.expect("webidl custom section should exist");
14+
15+
for (export, binding) in bindings.exports.iter() {
16+
let (args, ret) = extract_anyrefs(binding);
17+
cfg.export_xform(*export, &args, ret);
18+
}
19+
20+
for (import, kind) in bindings.imports.iter() {
21+
let binding = match kind {
22+
ImportBinding::Function(f) => f,
23+
ImportBinding::Constructor(f) => f,
24+
ImportBinding::Method(f) => f,
25+
};
26+
let (args, ret) = extract_anyrefs(binding);
27+
cfg.import_xform(*import, &args, ret);
28+
}
29+
30+
let aux = module
31+
.customs
32+
.get_typed_mut::<WasmBindgenAux>()
33+
.expect("webidl custom section should exist");
34+
for import in aux.import_map.values_mut() {
35+
let closure = match import {
36+
AuxImport::Closure(f) => f,
37+
_ => continue,
38+
};
39+
let (args, ret) = extract_anyrefs(&closure.function);
40+
if let Some(new) = cfg.table_element_xform(closure.shim_idx, &args, ret) {
41+
closure.shim_idx = new;
42+
}
43+
}
44+
45+
cfg.run(module)?;
46+
walrus::passes::gc::run(module);
47+
48+
// The GC pass above may end up removing some imported intrinsics. For
49+
// example `__wbindgen_object_clone_ref` is no longer needed after the
50+
// anyref pass. Make sure to delete the associated metadata for those
51+
// intrinsics so we don't try to access stale intrinsics later on.
52+
let remaining_imports = module
53+
.imports
54+
.iter()
55+
.map(|i| i.id())
56+
.collect::<HashSet<_>>();
57+
module
58+
.customs
59+
.get_typed_mut::<WebidlCustomSection>()
60+
.expect("webidl custom section should exist")
61+
.imports
62+
.retain(|id, _| remaining_imports.contains(id));
63+
module
64+
.customs
65+
.get_typed_mut::<WasmBindgenAux>()
66+
.expect("wasm-bindgen aux section should exist")
67+
.import_map
68+
.retain(|id, _| remaining_imports.contains(id));
69+
Ok(())
70+
}
71+
72+
fn extract_anyrefs(f: &Function) -> (Vec<(usize, bool)>, bool) {
73+
let mut args = Vec::new();
74+
let mut cur = 0;
75+
if f.ret.abi_returned_through_pointer() {
76+
cur += 1;
77+
}
78+
for arg in f.arguments.iter() {
79+
if arg.is_anyref() {
80+
args.push((cur, true));
81+
} else if arg.is_ref_anyref() {
82+
args.push((cur, false));
83+
}
84+
cur += arg.abi_arg_count();
85+
}
86+
(args, f.ret.is_anyref())
87+
}

0 commit comments

Comments
 (0)