summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWilliam Maddox <16312695+wmaddox@users.noreply.github.com>2019-07-28 11:02:14 -0700
committerAlon Zakai <azakai@google.com>2019-07-28 11:02:14 -0700
commitbe6f7c4795ab3bc135e10162b0d7a1063e5d65ba (patch)
tree081fcb0744be156ad05b374227d8777cc896ef65
parent6ac5fa713b421588eede2e791235e4b60b22b172 (diff)
downloadbinaryen-be6f7c4795ab3bc135e10162b0d7a1063e5d65ba.tar.gz
binaryen-be6f7c4795ab3bc135e10162b0d7a1063e5d65ba.tar.bz2
binaryen-be6f7c4795ab3bc135e10162b0d7a1063e5d65ba.zip
Fix stack pointer identification for wasm::ABI::getStackSpace(). (#2243)
* Fix stack pointer identification for wasm::ABI::getStackSpace(). Recent stack pointer simplification in Emscripten broke the --spill-pointers pass. This fix for #2229 restores this functionality by recognizing an alternative coding idiom in Emscripten-generated WASM code.
-rw-r--r--src/abi/stack.h34
-rw-r--r--test/passes/spill-pointers.txt647
-rw-r--r--test/passes/spill-pointers.wast170
3 files changed, 851 insertions, 0 deletions
diff --git a/src/abi/stack.h b/src/abi/stack.h
index 72220645c..22cd8ba3c 100644
--- a/src/abi/stack.h
+++ b/src/abi/stack.h
@@ -38,10 +38,44 @@ inline Index stackAlign(Index size) {
// Allocate some space on the stack, and assign it to a local.
// The local will have the same constant value in all the function, so you can
// just local.get it anywhere there.
+//
+// FIXME: This function assumes that the stack grows upward, per the convention
+// used by fastcomp. The stack grows downward when using the WASM backend.
+
inline void
getStackSpace(Index local, Function* func, Index size, Module& wasm) {
+ // Attempt to locate the stack pointer by recognizing code idioms
+ // used by Emscripten. First, look for a global initialized to an
+ // imported variable named "STACKTOP" in environment "env".
auto* stackPointer =
GlobalUtils::getGlobalInitializedToImport(wasm, ENV, "STACKTOP");
+ // Starting with Emscripten 1.38.24, the stack pointer variable is
+ // initialized with a literal constant, eliminating the import that
+ // we used to locate the stack pointer by name. We must match a more
+ // complicated idiom, expecting to see the module structured as follows:
+ //
+ //(module
+ // ...
+ // (export "stackSave" (func $stackSave))
+ // ...
+ // (func $stackSave (; 410 ;) (; has Stack IR ;) (result i32)
+ // (global.get $STACKTOP)
+ // )
+ // ...
+ //)
+ if (!stackPointer) {
+ auto* stackSaveFunctionExport = wasm.getExportOrNull("stackSave");
+ if (stackSaveFunctionExport &&
+ stackSaveFunctionExport->kind == ExternalKind::Function) {
+ auto* stackSaveFunction =
+ wasm.getFunction(stackSaveFunctionExport->value);
+ assert(!stackSaveFunction->imported());
+ auto* globalGet = stackSaveFunction->body->dynCast<GlobalGet>();
+ if (globalGet) {
+ stackPointer = wasm.getGlobal(globalGet->name);
+ }
+ }
+ }
if (!stackPointer) {
Fatal() << "getStackSpace: failed to find the stack pointer";
}
diff --git a/test/passes/spill-pointers.txt b/test/passes/spill-pointers.txt
index debd87eac..8ad136ee3 100644
--- a/test/passes/spill-pointers.txt
+++ b/test/passes/spill-pointers.txt
@@ -642,3 +642,650 @@
)
)
)
+(module
+ (type $ii (func (param i32 i32)))
+ (type $FUNCSIG$vi (func (param i32)))
+ (type $FUNCSIG$i (func (result i32)))
+ (type $FUNCSIG$v (func))
+ (type $FUNCSIG$ii (func (param i32) (result i32)))
+ (type $FUNCSIG$vd (func (param f64)))
+ (import "env" "segfault" (func $segfault (param i32)))
+ (memory $0 10)
+ (table $0 1 1 funcref)
+ (global $stack_ptr (mut i32) (i32.const 1716592))
+ (export "stackSave" (func $stack_save))
+ (func $stack_save (; 1 ;) (type $FUNCSIG$i) (result i32)
+ (global.get $stack_ptr)
+ )
+ (func $nothing (; 2 ;) (type $FUNCSIG$v)
+ (nop)
+ )
+ (func $not-alive (; 3 ;) (type $FUNCSIG$v)
+ (local $x i32)
+ (local.set $x
+ (i32.const 1)
+ )
+ (call $nothing)
+ )
+ (func $spill (; 4 ;) (type $FUNCSIG$v)
+ (local $x i32)
+ (local $1 i32)
+ (local.set $1
+ (global.get $stack_ptr)
+ )
+ (global.set $stack_ptr
+ (i32.add
+ (local.get $1)
+ (i32.const 16)
+ )
+ )
+ (block
+ (block
+ (i32.store
+ (local.get $1)
+ (local.get $x)
+ )
+ (call $nothing)
+ )
+ (drop
+ (local.get $x)
+ )
+ )
+ (global.set $stack_ptr
+ (local.get $1)
+ )
+ )
+ (func $ignore-non-pointers (; 5 ;) (type $FUNCSIG$v)
+ (local $x i32)
+ (local $y i64)
+ (local $z f32)
+ (local $w f64)
+ (local $4 i32)
+ (local.set $4
+ (global.get $stack_ptr)
+ )
+ (global.set $stack_ptr
+ (i32.add
+ (local.get $4)
+ (i32.const 16)
+ )
+ )
+ (block
+ (local.set $x
+ (i32.const 1)
+ )
+ (local.set $y
+ (i64.const 1)
+ )
+ (local.set $z
+ (f32.const 1)
+ )
+ (local.set $w
+ (f64.const 1)
+ )
+ (block
+ (i32.store
+ (local.get $4)
+ (local.get $x)
+ )
+ (call $nothing)
+ )
+ (drop
+ (local.get $x)
+ )
+ (drop
+ (local.get $y)
+ )
+ (drop
+ (local.get $z)
+ )
+ (drop
+ (local.get $w)
+ )
+ )
+ (global.set $stack_ptr
+ (local.get $4)
+ )
+ )
+ (func $spill4 (; 6 ;) (type $FUNCSIG$v)
+ (local $x i32)
+ (local $y i32)
+ (local $z i32)
+ (local $w i32)
+ (local $4 i32)
+ (local.set $4
+ (global.get $stack_ptr)
+ )
+ (global.set $stack_ptr
+ (i32.add
+ (local.get $4)
+ (i32.const 16)
+ )
+ )
+ (block
+ (local.set $x
+ (i32.const 1)
+ )
+ (local.set $y
+ (i32.const 1)
+ )
+ (local.set $z
+ (i32.const 1)
+ )
+ (local.set $w
+ (i32.const 1)
+ )
+ (block
+ (i32.store
+ (local.get $4)
+ (local.get $x)
+ )
+ (i32.store offset=4
+ (local.get $4)
+ (local.get $y)
+ )
+ (i32.store offset=8
+ (local.get $4)
+ (local.get $z)
+ )
+ (i32.store offset=12
+ (local.get $4)
+ (local.get $w)
+ )
+ (call $nothing)
+ )
+ (drop
+ (local.get $x)
+ )
+ (drop
+ (local.get $y)
+ )
+ (drop
+ (local.get $z)
+ )
+ (drop
+ (local.get $w)
+ )
+ )
+ (global.set $stack_ptr
+ (local.get $4)
+ )
+ )
+ (func $spill5 (; 7 ;) (type $FUNCSIG$v)
+ (local $x i32)
+ (local $y i32)
+ (local $z i32)
+ (local $w i32)
+ (local $a i32)
+ (local $5 i32)
+ (local.set $5
+ (global.get $stack_ptr)
+ )
+ (global.set $stack_ptr
+ (i32.add
+ (local.get $5)
+ (i32.const 32)
+ )
+ )
+ (block
+ (local.set $x
+ (i32.const 1)
+ )
+ (local.set $y
+ (i32.const 1)
+ )
+ (local.set $z
+ (i32.const 1)
+ )
+ (local.set $w
+ (i32.const 1)
+ )
+ (local.set $a
+ (i32.const 1)
+ )
+ (block
+ (i32.store
+ (local.get $5)
+ (local.get $x)
+ )
+ (i32.store offset=4
+ (local.get $5)
+ (local.get $y)
+ )
+ (i32.store offset=8
+ (local.get $5)
+ (local.get $z)
+ )
+ (i32.store offset=12
+ (local.get $5)
+ (local.get $w)
+ )
+ (i32.store offset=16
+ (local.get $5)
+ (local.get $a)
+ )
+ (call $nothing)
+ )
+ (drop
+ (local.get $x)
+ )
+ (drop
+ (local.get $y)
+ )
+ (drop
+ (local.get $z)
+ )
+ (drop
+ (local.get $w)
+ )
+ (drop
+ (local.get $a)
+ )
+ )
+ (global.set $stack_ptr
+ (local.get $5)
+ )
+ )
+ (func $some-alive (; 8 ;) (type $FUNCSIG$v)
+ (local $x i32)
+ (local $y i32)
+ (local $2 i32)
+ (local.set $2
+ (global.get $stack_ptr)
+ )
+ (global.set $stack_ptr
+ (i32.add
+ (local.get $2)
+ (i32.const 16)
+ )
+ )
+ (block
+ (block
+ (i32.store
+ (local.get $2)
+ (local.get $x)
+ )
+ (call $nothing)
+ )
+ (drop
+ (local.get $x)
+ )
+ )
+ (global.set $stack_ptr
+ (local.get $2)
+ )
+ )
+ (func $spill-args (; 9 ;) (type $ii) (param $p i32) (param $q i32)
+ (local $x i32)
+ (local $3 i32)
+ (local $4 i32)
+ (local $5 i32)
+ (local.set $3
+ (global.get $stack_ptr)
+ )
+ (global.set $stack_ptr
+ (i32.add
+ (local.get $3)
+ (i32.const 16)
+ )
+ )
+ (block
+ (block
+ (local.set $4
+ (i32.const 1)
+ )
+ (local.set $5
+ (i32.const 2)
+ )
+ (i32.store offset=8
+ (local.get $3)
+ (local.get $x)
+ )
+ (call $spill-args
+ (local.get $4)
+ (local.get $5)
+ )
+ )
+ (drop
+ (local.get $x)
+ )
+ )
+ (global.set $stack_ptr
+ (local.get $3)
+ )
+ )
+ (func $spill-ret (; 10 ;) (type $FUNCSIG$i) (result i32)
+ (local $x i32)
+ (local $1 i32)
+ (local $2 i32)
+ (local $3 i32)
+ (local $4 i32)
+ (local.set $1
+ (global.get $stack_ptr)
+ )
+ (global.set $stack_ptr
+ (i32.add
+ (local.get $1)
+ (i32.const 16)
+ )
+ )
+ (local.set $4
+ (block (result i32)
+ (block
+ (i32.store
+ (local.get $1)
+ (local.get $x)
+ )
+ (call $nothing)
+ )
+ (drop
+ (local.get $x)
+ )
+ (if
+ (i32.const 1)
+ (block
+ (local.set $2
+ (i32.const 2)
+ )
+ (global.set $stack_ptr
+ (local.get $1)
+ )
+ (return
+ (local.get $2)
+ )
+ )
+ (block
+ (local.set $3
+ (i32.const 3)
+ )
+ (global.set $stack_ptr
+ (local.get $1)
+ )
+ (return
+ (local.get $3)
+ )
+ )
+ )
+ (i32.const 4)
+ )
+ )
+ (global.set $stack_ptr
+ (local.get $1)
+ )
+ (local.get $4)
+ )
+ (func $spill-unreachable (; 11 ;) (type $FUNCSIG$i) (result i32)
+ (local $x i32)
+ (local $1 i32)
+ (local $2 i32)
+ (local.set $1
+ (global.get $stack_ptr)
+ )
+ (global.set $stack_ptr
+ (i32.add
+ (local.get $1)
+ (i32.const 16)
+ )
+ )
+ (local.set $2
+ (block (result i32)
+ (block
+ (i32.store
+ (local.get $1)
+ (local.get $x)
+ )
+ (call $nothing)
+ )
+ (drop
+ (local.get $x)
+ )
+ (unreachable)
+ )
+ )
+ (global.set $stack_ptr
+ (local.get $1)
+ )
+ (local.get $2)
+ )
+ (func $spill-call-call0 (; 12 ;) (type $FUNCSIG$ii) (param $p i32) (result i32)
+ (unreachable)
+ )
+ (func $spill-call-call1 (; 13 ;) (type $FUNCSIG$ii) (param $p i32) (result i32)
+ (local $x i32)
+ (local $2 i32)
+ (local $3 i32)
+ (local $4 i32)
+ (local $5 i32)
+ (local.set $2
+ (global.get $stack_ptr)
+ )
+ (global.set $stack_ptr
+ (i32.add
+ (local.get $2)
+ (i32.const 16)
+ )
+ )
+ (local.set $5
+ (block (result i32)
+ (drop
+ (block (result i32)
+ (local.set $3
+ (block (result i32)
+ (local.set $4
+ (i32.const 1)
+ )
+ (i32.store offset=4
+ (local.get $2)
+ (local.get $x)
+ )
+ (call $spill-call-call1
+ (local.get $4)
+ )
+ )
+ )
+ (i32.store offset=4
+ (local.get $2)
+ (local.get $x)
+ )
+ (call $spill-call-call0
+ (local.get $3)
+ )
+ )
+ )
+ (local.get $x)
+ )
+ )
+ (global.set $stack_ptr
+ (local.get $2)
+ )
+ (local.get $5)
+ )
+ (func $spill-call-ret (; 14 ;) (type $FUNCSIG$ii) (param $p i32) (result i32)
+ (local $x i32)
+ (drop
+ (call $spill-call-call0
+ (return
+ (i32.const 1)
+ )
+ )
+ )
+ (i32.const 0)
+ )
+ (func $spill-ret-call (; 15 ;) (type $FUNCSIG$ii) (param $p i32) (result i32)
+ (local $x i32)
+ (drop
+ (return
+ (call $spill-call-call0
+ (i32.const 1)
+ )
+ )
+ )
+ (i32.const 0)
+ )
+ (func $spill-ret-ret (; 16 ;) (type $FUNCSIG$i) (result i32)
+ (local $x i32)
+ (local $1 i32)
+ (local $2 i32)
+ (local $3 i32)
+ (local.set $1
+ (global.get $stack_ptr)
+ )
+ (global.set $stack_ptr
+ (i32.add
+ (local.get $1)
+ (i32.const 16)
+ )
+ )
+ (local.set $3
+ (block (result i32)
+ (block
+ (i32.store
+ (local.get $1)
+ (local.get $x)
+ )
+ (call $nothing)
+ )
+ (drop
+ (local.get $x)
+ )
+ (drop
+ (block
+ (global.set $stack_ptr
+ (local.get $1)
+ )
+ (return
+ (block
+ (local.set $2
+ (i32.const 1)
+ )
+ (global.set $stack_ptr
+ (local.get $1)
+ )
+ (return
+ (local.get $2)
+ )
+ )
+ )
+ )
+ )
+ (i32.const 0)
+ )
+ )
+ (global.set $stack_ptr
+ (local.get $1)
+ )
+ (local.get $3)
+ )
+ (func $spill-call-othertype (; 17 ;) (type $FUNCSIG$vd) (param $y f64)
+ (local $x i32)
+ (local $2 i32)
+ (local $3 f64)
+ (local.set $2
+ (global.get $stack_ptr)
+ )
+ (global.set $stack_ptr
+ (i32.add
+ (local.get $2)
+ (i32.const 16)
+ )
+ )
+ (block
+ (block
+ (local.set $3
+ (f64.const 1)
+ )
+ (i32.store
+ (local.get $2)
+ (local.get $x)
+ )
+ (call $spill-call-othertype
+ (local.get $3)
+ )
+ )
+ (drop
+ (local.get $x)
+ )
+ )
+ (global.set $stack_ptr
+ (local.get $2)
+ )
+ )
+ (func $spill-call_indirect (; 18 ;) (type $FUNCSIG$v)
+ (local $x i32)
+ (local $1 i32)
+ (local $2 i32)
+ (local $3 i32)
+ (local $4 i32)
+ (local.set $1
+ (global.get $stack_ptr)
+ )
+ (global.set $stack_ptr
+ (i32.add
+ (local.get $1)
+ (i32.const 16)
+ )
+ )
+ (block
+ (block
+ (local.set $2
+ (i32.const 123)
+ )
+ (local.set $3
+ (i32.const 456)
+ )
+ (local.set $4
+ (i32.const 789)
+ )
+ (i32.store
+ (local.get $1)
+ (local.get $x)
+ )
+ (call_indirect (type $ii)
+ (local.get $2)
+ (local.get $3)
+ (local.get $4)
+ )
+ )
+ (drop
+ (local.get $x)
+ )
+ )
+ (global.set $stack_ptr
+ (local.get $1)
+ )
+ )
+ (func $spill-call_import (; 19 ;) (type $FUNCSIG$v)
+ (local $x i32)
+ (local $1 i32)
+ (local $2 i32)
+ (local.set $1
+ (global.get $stack_ptr)
+ )
+ (global.set $stack_ptr
+ (i32.add
+ (local.get $1)
+ (i32.const 16)
+ )
+ )
+ (block
+ (block
+ (local.set $2
+ (i32.const 200)
+ )
+ (i32.store
+ (local.get $1)
+ (local.get $x)
+ )
+ (call $segfault
+ (local.get $2)
+ )
+ )
+ (drop
+ (local.get $x)
+ )
+ )
+ (global.set $stack_ptr
+ (local.get $1)
+ )
+ )
+)
diff --git a/test/passes/spill-pointers.wast b/test/passes/spill-pointers.wast
index ce486ea73..4eb05a721 100644
--- a/test/passes/spill-pointers.wast
+++ b/test/passes/spill-pointers.wast
@@ -166,3 +166,173 @@
)
)
+(module
+ (memory 10)
+ (type $ii (func (param i32 i32)))
+ (table 1 1 funcref)
+ (elem (i32.const 0))
+ (global $stack_ptr (mut i32) (i32.const 1716592))
+ (export "stackSave" (func $stack_save))
+ (import "env" "segfault" (func $segfault (param i32)))
+ (func $stack_save (result i32)
+ (global.get $stack_ptr)
+ )
+
+ (func $nothing
+ )
+ (func $not-alive
+ (local $x i32)
+ (local.set $x (i32.const 1))
+ (call $nothing)
+ )
+ (func $spill
+ (local $x i32)
+ (call $nothing)
+ (drop (local.get $x))
+ )
+ (func $ignore-non-pointers
+ (local $x i32)
+ (local $y i64)
+ (local $z f32)
+ (local $w f64)
+ (local.set $x (i32.const 1))
+ (local.set $y (i64.const 1))
+ (local.set $z (f32.const 1))
+ (local.set $w (f64.const 1))
+ (call $nothing)
+ (drop (local.get $x))
+ (drop (local.get $y))
+ (drop (local.get $z))
+ (drop (local.get $w))
+ )
+ (func $spill4
+ (local $x i32)
+ (local $y i32)
+ (local $z i32)
+ (local $w i32)
+ (local.set $x (i32.const 1))
+ (local.set $y (i32.const 1))
+ (local.set $z (i32.const 1))
+ (local.set $w (i32.const 1))
+ (call $nothing)
+ (drop (local.get $x))
+ (drop (local.get $y))
+ (drop (local.get $z))
+ (drop (local.get $w))
+ )
+ (func $spill5
+ (local $x i32)
+ (local $y i32)
+ (local $z i32)
+ (local $w i32)
+ (local $a i32)
+ (local.set $x (i32.const 1))
+ (local.set $y (i32.const 1))
+ (local.set $z (i32.const 1))
+ (local.set $w (i32.const 1))
+ (local.set $a (i32.const 1))
+ (call $nothing)
+ (drop (local.get $x))
+ (drop (local.get $y))
+ (drop (local.get $z))
+ (drop (local.get $w))
+ (drop (local.get $a))
+ )
+ (func $some-alive
+ (local $x i32)
+ (local $y i32)
+ (call $nothing)
+ (drop (local.get $x))
+ )
+ (func $spill-args (param $p i32) (param $q i32)
+ (local $x i32)
+ (call $spill-args (i32.const 1) (i32.const 2))
+ (drop (local.get $x))
+ )
+ (func $spill-ret (result i32)
+ (local $x i32)
+ (call $nothing)
+ (drop (local.get $x))
+ (if (i32.const 1)
+ (return (i32.const 2))
+ (return (i32.const 3))
+ )
+ (i32.const 4)
+ )
+ (func $spill-unreachable (result i32)
+ (local $x i32)
+ (call $nothing)
+ (drop (local.get $x))
+ (unreachable)
+ )
+ (func $spill-call-call0 (param $p i32) (result i32)
+ (unreachable)
+ )
+ (func $spill-call-call1 (param $p i32) (result i32)
+ (local $x i32)
+ (drop
+ (call $spill-call-call0
+ (call $spill-call-call1
+ (i32.const 1)
+ )
+ )
+ )
+ (local.get $x)
+ )
+ (func $spill-call-ret (param $p i32) (result i32)
+ (local $x i32)
+ (drop
+ (call $spill-call-call0
+ (return
+ (i32.const 1)
+ )
+ )
+ )
+ (local.get $x)
+ )
+ (func $spill-ret-call (param $p i32) (result i32)
+ (local $x i32)
+ (drop
+ (return
+ (call $spill-call-call0
+ (i32.const 1)
+ )
+ )
+ )
+ (local.get $x)
+ )
+ (func $spill-ret-ret (result i32)
+ (local $x i32)
+ (call $nothing)
+ (drop (local.get $x))
+ (drop
+ (return
+ (return
+ (i32.const 1)
+ )
+ )
+ )
+ (local.get $x)
+ )
+ (func $spill-call-othertype (param $y f64)
+ (local $x i32)
+ (call $spill-call-othertype (f64.const 1))
+ (drop (local.get $x))
+ )
+ (func $spill-call_indirect
+ (local $x i32)
+ (call_indirect (type $ii)
+ (i32.const 123)
+ (i32.const 456)
+ (i32.const 789)
+ )
+ (drop (local.get $x))
+ )
+ (func $spill-call_import
+ (local $x i32)
+ (call $segfault
+ (i32.const 200)
+ )
+ (drop (local.get $x))
+ )
+)