Skip to content

Commit 5f90951

Browse files
committed
Use the multi-value xform when targeting interface types
1 parent fc2c502 commit 5f90951

File tree

2 files changed

+174
-3
lines changed

2 files changed

+174
-3
lines changed

crates/cli-support/src/lib.rs

+29-1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ pub struct Bindgen {
3636
// "ready to be instantiated on any thread"
3737
threads: wasm_bindgen_threads_xform::Config,
3838
anyref: bool,
39+
multi_value: bool,
3940
wasm_interface_types: bool,
4041
encode_into: EncodeInto,
4142
}
@@ -77,6 +78,7 @@ impl Bindgen {
7778
pub fn new() -> Bindgen {
7879
let anyref = env::var("WASM_BINDGEN_ANYREF").is_ok();
7980
let wasm_interface_types = env::var("WASM_INTERFACE_TYPES").is_ok();
81+
let multi_value = env::var("WASM_BINDGEN_MULTI_VALUE").is_ok();
8082
Bindgen {
8183
input: Input::None,
8284
out_name: None,
@@ -93,6 +95,7 @@ impl Bindgen {
9395
weak_refs: env::var("WASM_BINDGEN_WEAKREF").is_ok(),
9496
threads: threads_config(),
9597
anyref: anyref || wasm_interface_types,
98+
multi_value,
9699
wasm_interface_types,
97100
encode_into: EncodeInto::Test,
98101
}
@@ -275,6 +278,20 @@ impl Bindgen {
275278
}
276279
};
277280

281+
// Our multi-value xform relies on the presence of the stack pointer, so
282+
// temporarily export it so that our many GC's don't remove it before
283+
// the xform runs.
284+
if self.multi_value {
285+
// Assume that the first global is the shadow stack pointer, since that is
286+
// what LLVM codegens.
287+
match module.globals.iter().next() {
288+
Some(g) if g.ty == walrus::ValType::I32 => {
289+
module.exports.add("__shadow_stack_pointer", g.id());
290+
}
291+
_ => {}
292+
}
293+
}
294+
278295
// This isn't the hardest thing in the world too support but we
279296
// basically don't know how to rationalize #[wasm_bindgen(start)] and
280297
// the actual `start` function if present. Figure this out later if it
@@ -335,7 +352,7 @@ impl Bindgen {
335352
.customs
336353
.delete_typed::<webidl::WasmBindgenAux>()
337354
.expect("aux section should be present");
338-
let bindings = module
355+
let mut bindings = module
339356
.customs
340357
.delete_typed::<webidl::NonstandardWebidlSection>()
341358
.unwrap();
@@ -350,8 +367,19 @@ impl Bindgen {
350367
};
351368

352369
if self.wasm_interface_types {
370+
if self.multi_value {
371+
webidl::standard::add_multi_value(&mut module, &mut bindings)
372+
.context("failed to transform return pointers into multi-value Wasm")?;
373+
}
353374
webidl::standard::add_section(&mut module, &aux, &bindings)
354375
.with_context(|_| "failed to generate a standard wasm bindings custom section")?;
376+
} else {
377+
if self.multi_value {
378+
failure::bail!(
379+
"Wasm multi-value is currently only available when \
380+
Wasm interface types is also enabled"
381+
);
382+
}
355383
}
356384

357385
Ok(Output {

crates/cli-support/src/webidl/standard.rs

+145-2
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,153 @@ use crate::descriptor::VectorKind;
5353
use crate::webidl::{AuxExportKind, AuxImport, AuxValue, JsImport, JsImportName};
5454
use crate::webidl::{NonstandardIncoming, NonstandardOutgoing};
5555
use crate::webidl::{NonstandardWebidlSection, WasmBindgenAux};
56-
use failure::{bail, Error, ResultExt};
57-
use walrus::Module;
56+
use failure::{bail, format_err, Error, ResultExt};
57+
use walrus::{GlobalId, MemoryId, Module};
58+
use wasm_bindgen_multi_value_xform as multi_value_xform;
5859
use wasm_webidl_bindings::ast;
5960

61+
pub fn add_multi_value(
62+
module: &mut Module,
63+
bindings: &mut NonstandardWebidlSection,
64+
) -> Result<(), Error> {
65+
let mut to_xform = vec![];
66+
for (id, binding) in &bindings.exports {
67+
if let Some(ref results) = binding.return_via_outptr {
68+
// LLVM currently always uses the first parameter for the return
69+
// pointer. We hard code that here, since we have no better option.
70+
let return_pointer_index = 0;
71+
to_xform.push((*id, return_pointer_index, &results[..]));
72+
}
73+
}
74+
75+
if to_xform.is_empty() {
76+
// Early exit to avoid failing if we don't have a memory or shadow stack
77+
// pointer because this is a minimal module that doesn't use linear
78+
// memory.
79+
return Ok(());
80+
}
81+
82+
let memory = get_memory(module)?;
83+
let shadow_stack_pointer = get_shadow_stack_pointer(module)?;
84+
multi_value_xform::run(module, memory, shadow_stack_pointer, &to_xform)?;
85+
86+
// Finally, unset `return_via_outptr`, fix up its incoming bindings'
87+
// argument numberings, and update its function type.
88+
for (id, binding) in &mut bindings.exports {
89+
if binding.return_via_outptr.take().is_some() {
90+
if binding.incoming.is_empty() {
91+
bail!("missing incoming binding expression for return pointer parameter");
92+
}
93+
if !is_ret_ptr_bindings(binding.incoming.remove(0)) {
94+
bail!("unexpected incoming binding expression for return pointer parameter");
95+
}
96+
97+
fixup_binding_argument_gets(&mut binding.incoming)?;
98+
99+
let func = match module.exports.get(*id).item {
100+
walrus::ExportItem::Function(f) => f,
101+
_ => unreachable!(),
102+
};
103+
binding.wasm_ty = module.funcs.get(func).ty();
104+
}
105+
}
106+
107+
Ok(())
108+
}
109+
110+
fn is_ret_ptr_bindings(b: NonstandardIncoming) -> bool {
111+
match b {
112+
NonstandardIncoming::Standard(ast::IncomingBindingExpression::As(
113+
ast::IncomingBindingExpressionAs {
114+
ty: walrus::ValType::I32,
115+
expr,
116+
},
117+
)) => match *expr {
118+
ast::IncomingBindingExpression::Get(ast::IncomingBindingExpressionGet { idx: 0 }) => {
119+
true
120+
}
121+
_ => false,
122+
},
123+
_ => false,
124+
}
125+
}
126+
127+
// Since we removed the first parameter (which was the return pointer) now all
128+
// of the `Get` binding expression's are off by one. This function fixes these
129+
// `Get`s.
130+
fn fixup_binding_argument_gets(incoming: &mut [NonstandardIncoming]) -> Result<(), Error> {
131+
for inc in incoming {
132+
fixup_nonstandard_incoming(inc)?;
133+
}
134+
return Ok(());
135+
136+
fn fixup_nonstandard_incoming(inc: &mut NonstandardIncoming) -> Result<(), Error> {
137+
match inc {
138+
NonstandardIncoming::Standard(s) => fixup_standard_incoming(s),
139+
_ => bail!("found usage of non-standard bindings when in standard-bindings-only mode"),
140+
}
141+
}
142+
143+
fn fixup_standard_incoming(s: &mut ast::IncomingBindingExpression) -> Result<(), Error> {
144+
match s {
145+
ast::IncomingBindingExpression::Get(e) => {
146+
if e.idx == 0 {
147+
bail!(
148+
"found usage of removed return pointer parameter in \
149+
non-return pointer bindings"
150+
);
151+
} else {
152+
e.idx -= 1;
153+
Ok(())
154+
}
155+
}
156+
ast::IncomingBindingExpression::As(e) => fixup_standard_incoming(&mut e.expr),
157+
ast::IncomingBindingExpression::AllocUtf8Str(e) => fixup_standard_incoming(&mut e.expr),
158+
ast::IncomingBindingExpression::AllocCopy(e) => fixup_standard_incoming(&mut e.expr),
159+
ast::IncomingBindingExpression::EnumToI32(e) => fixup_standard_incoming(&mut e.expr),
160+
ast::IncomingBindingExpression::Field(e) => fixup_standard_incoming(&mut e.expr),
161+
ast::IncomingBindingExpression::BindImport(e) => fixup_standard_incoming(&mut e.expr),
162+
}
163+
}
164+
}
165+
166+
fn get_memory(module: &Module) -> Result<MemoryId, Error> {
167+
let mut memories = module.memories.iter().map(|m| m.id());
168+
let memory = memories.next();
169+
if memories.next().is_some() {
170+
bail!(
171+
"expected a single memory, found multiple; multiple memories \
172+
currently not supported"
173+
);
174+
}
175+
memory.ok_or_else(|| {
176+
format_err!(
177+
"module does not have a memory; must have a memory \
178+
to transform return pointers into Wasm multi-value"
179+
)
180+
})
181+
}
182+
183+
// Get the `__shadow_stack_pointer` global that we stashed in an export early on
184+
// in the pipeline.
185+
fn get_shadow_stack_pointer(module: &mut Module) -> Result<GlobalId, Error> {
186+
let (g, e) = module
187+
.exports
188+
.iter()
189+
.find(|e| e.name == "__shadow_stack_pointer")
190+
.map(|e| {
191+
let g = match e.item {
192+
walrus::ExportItem::Global(g) => g,
193+
_ => unreachable!(),
194+
};
195+
(g, e.id())
196+
})
197+
.ok_or_else(|| format_err!("module does not have a shadow stack pointer"))?;
198+
199+
module.exports.delete(e);
200+
Ok(g)
201+
}
202+
60203
pub fn add_section(
61204
module: &mut Module,
62205
aux: &WasmBindgenAux,

0 commit comments

Comments
 (0)