Skip to content

Commit

Permalink
Fix the codegen of the TableGrow intrinsic
Browse files Browse the repository at this point in the history
It's not safe for us to arbitrarily modify the instruction stream since
wasm isn't guaranteed to be an AST! Instead we resort to a few extra
instructions with locals to achieve what we want here.

Closes rustwasm#2446
  • Loading branch information
alexcrichton committed Feb 9, 2021
1 parent 5442f26 commit 0768860
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 11 deletions.
27 changes: 21 additions & 6 deletions crates/externref-xform/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -681,11 +681,13 @@ impl Transform<'_> {
continue;
}
let entry = func.entry_block();
let scratch_i32 = module.locals.add(ValType::I32);
dfs_pre_order_mut(
&mut Rewrite {
clone_ref: self.clone_ref()?,
heap_dealloc: self.heap_dealloc()?,
xform: self,
scratch_i32,
},
func,
entry,
Expand All @@ -698,6 +700,7 @@ impl Transform<'_> {
xform: &'a Transform<'b>,
clone_ref: FunctionId,
heap_dealloc: FunctionId,
scratch_i32: LocalId,
}

impl VisitorMut for Rewrite<'_, '_> {
Expand All @@ -723,16 +726,28 @@ impl Transform<'_> {
let ty = ValType::Externref;
match intrinsic {
Intrinsic::TableGrow => {
// Switch this to a `table.grow` instruction...
// Change something that looks like:
//
// call $table_grow
//
// into:
//
// local.set $scratch
// ref.null extern
// local.get $scratch
// table.grow $table
//
// Note that things happen backwards here due to the
// order of insertion.
seq.instrs[i].0 = TableGrow {
table: self.xform.table,
}
.into();
// ... and then insert a `ref.null` before the
// preceding instruction as the value to grow the
// table with.
seq.instrs
.insert(i - 1, (RefNull { ty }.into(), InstrLocId::default()));
let loc = seq.instrs[i].1;
let local = self.scratch_i32;
seq.instrs.insert(i, (LocalGet { local }.into(), loc));
seq.instrs.insert(i, (RefNull { ty }.into(), loc));
seq.instrs.insert(i, (LocalSet { local }.into(), loc));
}
Intrinsic::TableSetNull => {
// Switch this to a `table.set` instruction...
Expand Down
13 changes: 8 additions & 5 deletions crates/externref-xform/tests/table-grow-intrinsic.wat
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@
(type (;0;) (func (result i32)))
(type (;1;) (func (param i32)))
(type (;2;) (func (param externref)))
(func $foo (type 1) (param i32)
(local i32)
i32.const 0
local.set 1
ref.null extern
local.get 1
table.grow 0
drop)
(func $foo_externref_shim (@name "foo externref shim") (type 2) (param externref)
(local i32)
call $alloc
Expand All @@ -25,11 +33,6 @@
table.set 0
local.get 1
call $foo)
(func $foo (type 1) (param i32)
ref.null extern
i32.const 0
table.grow 0
drop)
(func $alloc (type 0) (result i32)
i32.const 0)
(table (;0;) 32 externref)
Expand Down
43 changes: 43 additions & 0 deletions crates/externref-xform/tests/tee-before-grow.wat
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
;; @xform export "foo" (externref_owned)

(module
(import "__wbindgen_externref_xform__" "__wbindgen_externref_table_grow"
(func $grow (param i32) (result i32)))
(func $foo (export "foo") (param i32)
(local i32)
i32.const 0
local.tee 0
call $grow
drop)
(func $alloc (export "__externref_table_alloc") (result i32)
i32.const 0)
(func $dealloc (export "__externref_table_dealloc") (param i32))
)

(; CHECK-ALL:
(module
(type (;0;) (func (result i32)))
(type (;1;) (func (param i32)))
(type (;2;) (func (param externref)))
(func $foo (type 1) (param i32)
(local i32)
i32.const 0
local.tee 0
local.set 1
ref.null extern
local.get 1
table.grow 0
drop)
(func $foo_externref_shim (@name "foo externref shim") (type 2) (param externref)
(local i32)
call $alloc
local.tee 1
local.get 0
table.set 0
local.get 1
call $foo)
(func $alloc (type 0) (result i32)
i32.const 0)
(table (;0;) 32 externref)
(export "foo" (func $foo_externref_shim)))
;)

0 comments on commit 0768860

Please sign in to comment.