diff options
-rw-r--r-- | src/passes/SimplifyGlobals.cpp | 26 | ||||
-rw-r--r-- | test/lit/passes/simplify-globals-read_only_to_write.wast | 269 |
2 files changed, 290 insertions, 5 deletions
diff --git a/src/passes/SimplifyGlobals.cpp b/src/passes/SimplifyGlobals.cpp index 9730400d6..daaa82447 100644 --- a/src/passes/SimplifyGlobals.cpp +++ b/src/passes/SimplifyGlobals.cpp @@ -193,12 +193,17 @@ struct GlobalUseScanner : public WalkerPass<PostWalker<GlobalUseScanner>> { struct FlowScanner : public ExpressionStackWalker<FlowScanner, UnifiedExpressionVisitor<FlowScanner>> { + GlobalUseScanner& globalUseScanner; Name writtenGlobal; PassOptions& passOptions; Module& wasm; - FlowScanner(Name writtenGlobal, PassOptions& passOptions, Module& wasm) - : writtenGlobal(writtenGlobal), passOptions(passOptions), wasm(wasm) {} + FlowScanner(GlobalUseScanner& globalUseScanner, + Name writtenGlobal, + PassOptions& passOptions, + Module& wasm) + : globalUseScanner(globalUseScanner), writtenGlobal(writtenGlobal), + passOptions(passOptions), wasm(wasm) {} bool ok = true; @@ -208,7 +213,7 @@ struct GlobalUseScanner : public WalkerPass<PostWalker<GlobalUseScanner>> { // We found the get of the global. Check where its value flows to, // and how it is used there. assert(expressionStack.back() == get); - for (Index i = 0; i < expressionStack.size() - 1; i++) { + for (int i = int(expressionStack.size()) - 2; i >= 0; i--) { // Consider one pair of parent->child, and check if the parent // causes any problems when the child's value reaches it. auto* parent = expressionStack[i]; @@ -225,7 +230,18 @@ struct GlobalUseScanner : public WalkerPass<PostWalker<GlobalUseScanner>> { if (auto* iff = parent->dynCast<If>()) { if (iff->condition == child) { // The child is used to decide what code to run, which is - // dangerous. + // dangerous: check what effects it causes. If it is a nested + // appearance of the pattern, that is one case that we know is + // actually safe. + if (!iff->ifFalse && + globalUseScanner.readsGlobalOnlyToWriteIt( + iff->condition, iff->ifTrue) == writtenGlobal) { + // This is safe, and we can stop here: the value does not + // flow any further. + break; + } + + // Otherwise, we found a problem, and can stop. ok = false; break; } @@ -236,7 +252,7 @@ struct GlobalUseScanner : public WalkerPass<PostWalker<GlobalUseScanner>> { } }; - FlowScanner scanner(writtenGlobal, getPassOptions(), *getModule()); + FlowScanner scanner(*this, writtenGlobal, getPassOptions(), *getModule()); scanner.walk(condition); return scanner.ok ? writtenGlobal : Name(); } diff --git a/test/lit/passes/simplify-globals-read_only_to_write.wast b/test/lit/passes/simplify-globals-read_only_to_write.wast index 2bce9050c..149dd2dfb 100644 --- a/test/lit/passes/simplify-globals-read_only_to_write.wast +++ b/test/lit/passes/simplify-globals-read_only_to_write.wast @@ -536,3 +536,272 @@ ) ) ) + +(module + (memory 1 1) + + ;; CHECK: (type $none_=>_none (func)) + + ;; CHECK: (global $once i32 (i32.const 0)) + (global $once (mut i32) (i32.const 0)) + + ;; CHECK: (memory $0 1 1) + + ;; CHECK: (func $nested-pattern + ;; CHECK-NEXT: (local $x i32) + ;; CHECK-NEXT: (local $y i32) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (block $block (result i32) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.eq + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $nested-pattern + (local $x i32) + (local $y i32) + (if + (block (result i32) + ;; Another appearance of the pattern nested inside this one. This should + ;; not prevent us from optimizing. + (if + (i32.eqz + (global.get $once) + ) + (global.set $once + (i32.const 1) + ) + ) + (i32.eq + (global.get $once) + (i32.const 0) + ) + ) + (global.set $once + (i32.const 1) + ) + ) + ) +) + +(module + (memory 1 1) + + ;; CHECK: (type $none_=>_none (func)) + + ;; CHECK: (global $once (mut i32) (i32.const 0)) + (global $once (mut i32) (i32.const 0)) + + ;; CHECK: (memory $0 1 1) + + ;; CHECK: (func $almost-nested-pattern + ;; CHECK-NEXT: (local $x i32) + ;; CHECK-NEXT: (local $y i32) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (block $block (result i32) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (global.get $once) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (global.set $once + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.eq + ;; CHECK-NEXT: (global.get $once) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (global.set $once + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $almost-nested-pattern + (local $x i32) + (local $y i32) + (if + (block (result i32) + ;; This is almost the nested pattern, but not quite, as it has an + ;; "else" arm. + (if + (i32.eqz + (global.get $once) + ) + (global.set $once + (i32.const 1) + ) + (nop) ;; This breaks the pattern we are looking for. + ) + (i32.eq + (global.get $once) + (i32.const 0) + ) + ) + (global.set $once + (i32.const 1) + ) + ) + ) +) + +(module + (memory 1 1) + + ;; CHECK: (type $none_=>_none (func)) + + ;; CHECK: (global $once i32 (i32.const 0)) + (global $once (mut i32) (i32.const 0)) + + ;; CHECK: (memory $0 1 1) + + ;; CHECK: (func $nested-pattern-thrice + ;; CHECK-NEXT: (local $x i32) + ;; CHECK-NEXT: (local $y i32) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (block $block (result i32) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (block $block1 (result i32) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.eq + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $nested-pattern-thrice + (local $x i32) + (local $y i32) + (if + (block (result i32) + (if + (i32.eqz + (block (result i32) + ;; A third nested appearance. + (if + (global.get $once) + (global.set $once + (i32.const 1) + ) + ) + (global.get $once) + ) + ) + (global.set $once + (i32.const 1) + ) + ) + (i32.eq + (global.get $once) + (i32.const 0) + ) + ) + (global.set $once + (i32.const 1) + ) + ) + ) +) + +(module + (memory 1 1) + + ;; CHECK: (type $none_=>_none (func)) + + ;; CHECK: (global $once (mut i32) (i32.const 0)) + (global $once (mut i32) (i32.const 0)) + + ;; CHECK: (memory $0 1 1) + + ;; CHECK: (func $nested-pattern + ;; CHECK-NEXT: (local $x i32) + ;; CHECK-NEXT: (local $y i32) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (block $block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $once) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (global.get $once) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (global.set $once + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.eq + ;; CHECK-NEXT: (global.get $once) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (global.set $once + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $nested-pattern + (local $x i32) + (local $y i32) + (if + (block (result i32) + + ;; As above, but adding a drop of another get. This is *not* the + ;; pattern we are looking for, and it will prevent us from + ;; optimizing as we will no longer see that the number of gets + ;; matches the number of read-only-to-write patterns. In the + ;; future we could do a more complex counting operation to handle + ;; this too. + (drop + (global.get $once) + ) + + (if + (i32.eqz + (global.get $once) + ) + (global.set $once + (i32.const 1) + ) + ) + (i32.eq + (global.get $once) + (i32.const 0) + ) + ) + (global.set $once + (i32.const 1) + ) + ) + ) +) |