SimplifyLocals: Check effects of set value rather than LocalSet in SinkableInfo LinearExecutionWalker: Add connectAdjacentCalls and enable in SimplifyLocals
diff --git a/src/ir/linear-execution.h b/src/ir/linear-execution.h index 9e69405..430271f 100644 --- a/src/ir/linear-execution.h +++ b/src/ir/linear-execution.h
@@ -76,6 +76,7 @@ // cases (calls, if without an else) and it has very low overhead (we still // only do a simple postorder walk on the IR, no CFG is constructed, etc.). bool connectAdjacentBlocks = false; + bool connectAdjacentCalls = false; static void scan(SubType* self, Expression** currp) { Expression* curr = *currp; @@ -89,7 +90,7 @@ // Control is nonlinear if we return or throw. Traps don't need to be // taken into account since they don't break control flow in a way // that's observable. - if (mayThrow || isReturn) { + if (isReturn || (!self->connectAdjacentCalls && mayThrow)) { self->pushTask(SubType::doNoteNonLinear, currp); } }
diff --git a/src/passes/SimplifyLocals.cpp b/src/passes/SimplifyLocals.cpp index 0879138..b60ed10 100644 --- a/src/passes/SimplifyLocals.cpp +++ b/src/passes/SimplifyLocals.cpp
@@ -76,13 +76,16 @@ SimplifyLocals<allowTee, allowStructure, allowNesting>>(); } + bool connectAdjacentCalls = true; + // information for a local.set we can sink struct SinkableInfo { Expression** item; EffectAnalyzer effects; SinkableInfo(Expression** item, PassOptions& passOptions, Module& module) - : item(item), effects(passOptions, module, *item) {} + : item(item), + effects(passOptions, module, (*item)->cast<LocalSet>()->value) {} }; // a list of sinkables in a linear execution trace
diff --git a/test/lit/ctor-eval/multivalue-local.wast b/test/lit/ctor-eval/multivalue-local.wast index 306e26a..aa9e80e 100644 --- a/test/lit/ctor-eval/multivalue-local.wast +++ b/test/lit/ctor-eval/multivalue-local.wast
@@ -44,13 +44,6 @@ ;; CHECK: (export "multivalue-local" (func $multivalue-local_2)) ;; CHECK: (func $multivalue-local_2 (type $1) (result i32) -;; CHECK-NEXT: (local $0 i32) -;; CHECK-NEXT: (local.set $0 -;; CHECK-NEXT: (i32.const 42) -;; CHECK-NEXT: ) ;; CHECK-NEXT: (call $import) -;; CHECK-NEXT: (i32.add -;; CHECK-NEXT: (local.get $0) -;; CHECK-NEXT: (local.get $0) -;; CHECK-NEXT: ) +;; CHECK-NEXT: (i32.const 84) ;; CHECK-NEXT: )
diff --git a/test/lit/passes/flatten_simplify-locals-nonesting_souperify-single-use_enable-threads.wast b/test/lit/passes/flatten_simplify-locals-nonesting_souperify-single-use_enable-threads.wast index f719700..0ab4ed4 100644 --- a/test/lit/passes/flatten_simplify-locals-nonesting_souperify-single-use_enable-threads.wast +++ b/test/lit/passes/flatten_simplify-locals-nonesting_souperify-single-use_enable-threads.wast
@@ -3133,9 +3133,7 @@ ;; CHECK-NEXT: (local.set $y ;; CHECK-NEXT: (i32.const 2) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.set $z - ;; CHECK-NEXT: (i32.const 3) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: (loop $loopy ;; CHECK-NEXT: (local.set $x ;; CHECK-NEXT: (i32.const 4) @@ -3151,18 +3149,18 @@ ;; CHECK-NEXT: (br_if $loopy ;; CHECK-NEXT: (local.get $t) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.set $z - ;; CHECK-NEXT: (i32.const 6) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: (nop) - ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: (local.set $8 + ;; CHECK-NEXT: (i32.const 6) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: (local.set $9 ;; CHECK-NEXT: (select ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: (local.get $y) - ;; CHECK-NEXT: (local.get $z) + ;; CHECK-NEXT: (local.get $8) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (return @@ -3534,13 +3532,13 @@ ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (nop) - ;; CHECK-NEXT: (local.set $10 - ;; CHECK-NEXT: (f64.const 0) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: (local.set $11 + ;; CHECK-NEXT: (f64.const 0) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.get $10) + ;; CHECK-NEXT: (local.get $11) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: )
diff --git a/test/lit/passes/flatten_simplify-locals-nonesting_souperify_enable-threads.wast b/test/lit/passes/flatten_simplify-locals-nonesting_souperify_enable-threads.wast index d57df65..08a4bf6 100644 --- a/test/lit/passes/flatten_simplify-locals-nonesting_souperify_enable-threads.wast +++ b/test/lit/passes/flatten_simplify-locals-nonesting_souperify_enable-threads.wast
@@ -3200,9 +3200,7 @@ ;; CHECK-NEXT: (local.set $y ;; CHECK-NEXT: (i32.const 2) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.set $z - ;; CHECK-NEXT: (i32.const 3) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: (loop $loopy ;; CHECK-NEXT: (local.set $x ;; CHECK-NEXT: (i32.const 4) @@ -3218,18 +3216,18 @@ ;; CHECK-NEXT: (br_if $loopy ;; CHECK-NEXT: (local.get $t) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.set $z - ;; CHECK-NEXT: (i32.const 6) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: (nop) - ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: (local.set $8 + ;; CHECK-NEXT: (i32.const 6) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: (local.set $9 ;; CHECK-NEXT: (select ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: (local.get $y) - ;; CHECK-NEXT: (local.get $z) + ;; CHECK-NEXT: (local.get $8) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (return @@ -3601,13 +3599,13 @@ ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (nop) - ;; CHECK-NEXT: (local.set $10 - ;; CHECK-NEXT: (f64.const 0) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: (local.set $11 + ;; CHECK-NEXT: (f64.const 0) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.get $10) + ;; CHECK-NEXT: (local.get $11) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: )
diff --git a/test/lit/passes/simplify-locals-eh.wast b/test/lit/passes/simplify-locals-eh.wast index 5a25d02..a5b32fb 100644 --- a/test/lit/passes/simplify-locals-eh.wast +++ b/test/lit/passes/simplify-locals-eh.wast
@@ -2,15 +2,15 @@ ;; RUN: wasm-opt %s --simplify-locals -all -S -o - | filecheck %s (module - ;; CHECK: (tag $e-i32 (type $2) (param i32)) + ;; CHECK: (tag $e-i32 (type $1) (param i32)) (tag $e-i32 (param i32)) - ;; CHECK: (func $bar (type $0) (result i32) + ;; CHECK: (func $bar (type $2) (result i32) ;; CHECK-NEXT: (i32.const 3) ;; CHECK-NEXT: ) (func $bar (result i32) (i32.const 3)) - ;; CHECK: (func $call-cannot-be-sinked-into-try_table (type $1) + ;; CHECK: (func $call-cannot-be-sinked-into-try_table (type $0) ;; CHECK-NEXT: (local $0 i32) ;; CHECK-NEXT: (local.set $0 ;; CHECK-NEXT: (call $bar) @@ -46,7 +46,7 @@ ) ) - ;; CHECK: (func $non-call-can-be-sinked-into-try_table (type $1) + ;; CHECK: (func $non-call-can-be-sinked-into-try_table (type $0) ;; CHECK-NEXT: (local $0 i32) ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: (block $tryend @@ -80,7 +80,7 @@ ) ) - ;; CHECK: (func $return-call-can-be-sinked-into-try_table (type $0) (result i32) + ;; CHECK: (func $return-call-can-be-sinked-into-try_table (type $2) (result i32) ;; CHECK-NEXT: (local $0 i32) ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: (block $tryend (result i32) @@ -125,7 +125,7 @@ ) ) - ;; CHECK: (func $equivalent-set-removal-call (type $2) (param $0 i32) + ;; CHECK: (func $equivalent-set-removal-call (type $1) (param $0 i32) ;; CHECK-NEXT: (local $1 i32) ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: (drop @@ -138,9 +138,7 @@ ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (local.get $0) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.get $0) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: ) (func $equivalent-set-removal-call (param $0 i32) (local $1 i32) @@ -224,17 +222,15 @@ (drop (local.get $1)) ) - ;; CHECK: (func $nop (type $1) + ;; CHECK: (func $nop (type $0) ;; CHECK-NEXT: ) (func $nop) ;; CHECK: (func $sink-across-control-flow (type $4) (param $bool i32) (result i32) ;; CHECK-NEXT: (local $l i32) - ;; CHECK-NEXT: (local.set $l - ;; CHECK-NEXT: (i32.const 2) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: (call $nop) - ;; CHECK-NEXT: (local.get $l) + ;; CHECK-NEXT: (i32.const 2) ;; CHECK-NEXT: ) (func $sink-across-control-flow (param $bool i32) (result i32) (local $l i32)
diff --git a/test/lit/passes/simplify-locals-global-effects-eh.wast b/test/lit/passes/simplify-locals-global-effects-eh.wast index 2fed91f..0e7badf 100644 --- a/test/lit/passes/simplify-locals-global-effects-eh.wast +++ b/test/lit/passes/simplify-locals-global-effects-eh.wast
@@ -38,17 +38,16 @@ ;; CHECK: (func $read-g-with-throw-in-between (type $1) (result i32) ;; CHECK-NEXT: (local $x i32) - ;; CHECK-NEXT: (local.set $x - ;; CHECK-NEXT: (global.get $g) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: (call $throws) - ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (global.get $g) ;; CHECK-NEXT: ) (func $read-g-with-throw-in-between (result i32) (local $x i32) (local.set $x (global.get $g)) - ;; A potential throw halts our optimizations. + ;; Sinking across a throw is valid here because reading the global has no + ;; side effects. (call $throws) (local.get $x) @@ -132,22 +131,19 @@ ;; CHECK: (func $read-g-with-effectful-call-ref (type $3) (param $ref (ref $throw-type)) (result i32) ;; CHECK-NEXT: (local $x i32) - ;; CHECK-NEXT: (local.set $x - ;; CHECK-NEXT: (global.get $g) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (call_ref $throw-type ;; CHECK-NEXT: (local.get $ref) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (global.get $g) ;; CHECK-NEXT: ) (func $read-g-with-effectful-call-ref (param $ref (ref $throw-type)) (result i32) (local $x i32) (local.set $x (global.get $g)) - ;; Similar to above, except here we can tell that the indirect call may - ;; throw so optimization is halted. + ;; Sinking across a potentially throwing call is valid here. (drop (call_ref $throw-type (local.get $ref))) (local.get $x) @@ -155,22 +151,19 @@ ;; CHECK: (func $read-g-with-effectful-call-indirect (type $3) (param $ref (ref $throw-type)) (result i32) ;; CHECK-NEXT: (local $x i32) - ;; CHECK-NEXT: (local.set $x - ;; CHECK-NEXT: (global.get $g) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (call_indirect $t (type $throw-type) ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (global.get $g) ;; CHECK-NEXT: ) (func $read-g-with-effectful-call-indirect (param $ref (ref $throw-type)) (result i32) (local $x i32) (local.set $x (global.get $g)) - ;; Similar to above, except here we can tell that the indirect call may - ;; throw so optimization is halted. + ;; Sinking across a potentially throwing call is valid here. (drop (call_indirect (type $throw-type) (i32.const 0))) (local.get $x)