Created
October 27, 2023 21:53
-
-
Save kripken/56a0f4f1d70e9575273c154d6eb981f8 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/test/lit/passes/once-reduction.wast b/test/lit/passes/once-reduction.wast | |
index dd15da5da..7feae8857 100644 | |
--- a/test/lit/passes/once-reduction.wast | |
+++ b/test/lit/passes/once-reduction.wast | |
@@ -1349,311 +1349,311 @@ | |
) | |
;; Corner case: Exported mutable global. We cannot optimize it, since the | |
;; outside may read and write it. | |
(module | |
;; CHECK: (type $0 (func)) | |
;; CHECK: (global $once (mut i32) (i32.const 0)) | |
(global $once (mut i32) (i32.const 0)) | |
;; CHECK: (export "once-global" (global $once)) | |
(export "once-global" (global $once)) | |
;; CHECK: (func $once (type $0) | |
;; CHECK-NEXT: (if | |
;; CHECK-NEXT: (global.get $once) | |
;; CHECK-NEXT: (return) | |
;; CHECK-NEXT: ) | |
;; CHECK-NEXT: (global.set $once | |
;; CHECK-NEXT: (i32.const 1) | |
;; CHECK-NEXT: ) | |
;; CHECK-NEXT: ) | |
(func $once | |
(if | |
(global.get $once) | |
(return) | |
) | |
(global.set $once (i32.const 1)) | |
) | |
;; CHECK: (func $caller (type $0) | |
;; CHECK-NEXT: (call $once) | |
;; CHECK-NEXT: (call $once) | |
;; CHECK-NEXT: ) | |
(func $caller | |
(call $once) | |
(call $once) | |
) | |
) | |
;; Test calls of other "once" functions in "once" functions. | |
(module | |
;; CHECK: (type $0 (func)) | |
;; CHECK: (global $once (mut i32) (i32.const 0)) | |
(global $once (mut i32) (i32.const 0)) | |
;; CHECK: (global $once.1 (mut i32) (i32.const 0)) | |
(global $once.1 (mut i32) (i32.const 0)) | |
;; CHECK: (func $once (type $0) | |
;; CHECK-NEXT: (nop) | |
;; CHECK-NEXT: (nop) | |
;; CHECK-NEXT: (call $once.1) | |
;; CHECK-NEXT: ) | |
(func $once | |
;; A minimal "once" function, which calls another. We can remove the first | |
;; two lines here (the early-exit logic). | |
(if | |
(global.get $once) | |
(return) | |
) | |
(global.set $once (i32.const 1)) | |
(call $once.1) | |
) | |
;; CHECK: (func $once.1 (type $0) | |
;; CHECK-NEXT: (nop) | |
;; CHECK-NEXT: ) | |
(func $once.1 | |
;; Another minimal "once" function. | |
(if | |
(global.get $once.1) | |
(return) | |
) | |
(global.set $once.1 (i32.const 1)) | |
) | |
;; CHECK: (func $caller (type $0) | |
;; CHECK-NEXT: (call $once) | |
;; CHECK-NEXT: (nop) | |
;; CHECK-NEXT: ) | |
(func $caller | |
;; Call a once function more than once. The second call will become a nop. | |
(call $once) | |
(call $once) | |
) | |
;; CHECK: (func $caller$1 (type $0) | |
;; CHECK-NEXT: (call $once.1) | |
;; CHECK-NEXT: (nop) | |
;; CHECK-NEXT: ) | |
(func $caller$1 | |
;; Two calls to the second function. Again, the second becomes a nop. | |
(call $once.1) | |
(call $once.1) | |
) | |
;; CHECK: (func $caller$2 (type $0) | |
;; CHECK-NEXT: (call $once) | |
- ;; CHECK-NEXT: (nop) | |
+ ;; CHECK-NEXT: (call $once.1) | |
;; CHECK-NEXT: ) | |
(func $caller$2 | |
;; A mix of calls. We can still remove the second, because we know the first | |
;; will call it. | |
(call $once) | |
(call $once.1) | |
) | |
;; CHECK: (func $caller$3 (type $0) | |
;; CHECK-NEXT: (call $once.1) | |
;; CHECK-NEXT: (call $once) | |
;; CHECK-NEXT: ) | |
(func $caller$3 | |
;; Reverse of the above; now we cannot optimize, as $once.1 does not call | |
;; $once. | |
(call $once.1) | |
(call $once) | |
) | |
;; CHECK: (func $caller$4 (type $0) | |
;; CHECK-NEXT: (call $once.1) | |
;; CHECK-NEXT: (call $caller$4) | |
;; CHECK-NEXT: ) | |
(func $caller$4 | |
;; Here we cannot optimize, since the second function is not a "once" | |
;; function. | |
(call $once.1) | |
(call $caller$4) | |
) | |
) | |
;; Test loops between "once" functions. | |
(module | |
;; CHECK: (type $0 (func)) | |
;; CHECK: (global $once (mut i32) (i32.const 0)) | |
(global $once (mut i32) (i32.const 0)) | |
;; CHECK: (global $once.1 (mut i32) (i32.const 0)) | |
(global $once.1 (mut i32) (i32.const 0)) | |
;; CHECK: (func $once (type $0) | |
;; CHECK-NEXT: (nop) | |
;; CHECK-NEXT: (nop) | |
;; CHECK-NEXT: (call $once.1) | |
;; CHECK-NEXT: ) | |
(func $once | |
(if | |
(global.get $once) | |
(return) | |
) | |
(global.set $once (i32.const 1)) | |
(call $once.1) | |
) | |
;; CHECK: (func $once.1 (type $0) | |
;; CHECK-NEXT: (if | |
;; CHECK-NEXT: (global.get $once.1) | |
;; CHECK-NEXT: (return) | |
;; CHECK-NEXT: ) | |
;; CHECK-NEXT: (global.set $once.1 | |
;; CHECK-NEXT: (i32.const 1) | |
;; CHECK-NEXT: ) | |
;; CHECK-NEXT: (call $once) | |
;; CHECK-NEXT: ) | |
(func $once.1 | |
;; This early-exit logic looks removable, since we call another "once" | |
;; function. However, we remove that function's early-exit logic, so we | |
;; cannot do so here (it would risk an infinite loop). | |
(if | |
(global.get $once.1) | |
(return) | |
) | |
(global.set $once.1 (i32.const 1)) | |
(call $once) ;; This call was added. | |
) | |
;; CHECK: (func $caller (type $0) | |
;; CHECK-NEXT: (call $once) | |
;; CHECK-NEXT: (nop) | |
;; CHECK-NEXT: ) | |
(func $caller | |
;; The second call will become a nop. | |
(call $once) | |
(call $once) | |
) | |
;; CHECK: (func $caller$1 (type $0) | |
;; CHECK-NEXT: (call $once.1) | |
;; CHECK-NEXT: (nop) | |
;; CHECK-NEXT: ) | |
(func $caller$1 | |
;; Again, the second becomes a nop. | |
(call $once.1) | |
(call $once.1) | |
) | |
;; CHECK: (func $caller$2 (type $0) | |
;; CHECK-NEXT: (call $once) | |
- ;; CHECK-NEXT: (nop) | |
+ ;; CHECK-NEXT: (call $once.1) | |
;; CHECK-NEXT: ) | |
(func $caller$2 | |
;; Again, the second becomes a nop. | |
(call $once) | |
(call $once.1) | |
) | |
;; CHECK: (func $caller$3 (type $0) | |
;; CHECK-NEXT: (call $once.1) | |
- ;; CHECK-NEXT: (nop) | |
+ ;; CHECK-NEXT: (call $once) | |
;; CHECK-NEXT: ) | |
(func $caller$3 | |
;; An improvement compared to the previous module, now we can optimize the | |
;; second one. | |
(call $once.1) | |
(call $once) | |
) | |
) | |
;; Test a dangerous triple loop. | |
(module | |
;; CHECK: (type $0 (func)) | |
;; CHECK: (type $1 (func (param i32))) | |
;; CHECK: (import "env" "foo" (func $import (type $1) (param i32))) | |
(import "env" "foo" (func $import (param i32))) | |
;; CHECK: (global $once (mut i32) (i32.const 0)) | |
(global $once (mut i32) (i32.const 0)) | |
;; CHECK: (global $once.1 (mut i32) (i32.const 0)) | |
(global $once.1 (mut i32) (i32.const 0)) | |
;; CHECK: (global $once.2 (mut i32) (i32.const 0)) | |
(global $once.2 (mut i32) (i32.const 0)) | |
;; CHECK: (func $once (type $0) | |
;; CHECK-NEXT: (if | |
;; CHECK-NEXT: (global.get $once) | |
;; CHECK-NEXT: (return) | |
;; CHECK-NEXT: ) | |
;; CHECK-NEXT: (global.set $once | |
;; CHECK-NEXT: (i32.const 1) | |
;; CHECK-NEXT: ) | |
;; CHECK-NEXT: (call $once.1) | |
;; CHECK-NEXT: (call $once.2) | |
;; CHECK-NEXT: (call $import | |
;; CHECK-NEXT: (i32.const 0) | |
;; CHECK-NEXT: ) | |
;; CHECK-NEXT: ) | |
(func $once | |
(if | |
(global.get $once) | |
(return) | |
) | |
(global.set $once (i32.const 1)) | |
(call $once.1) | |
;; We cannot remove this second call. While $once.1 calls $once.2, we may | |
;; be in this situation: a call started at $once.1, which calls $once | |
;; (here) which then calls $once.1 which immediately exits (as the global | |
;; has been set for it), and then we call $once.2 from here, which calls | |
;; the other two that immediately exit as well, and then we call the import | |
;; from there with value 2. Then we call it from here with value 0, and | |
;; then we return to the caller, $once.1, which calls with 1, so we have | |
;; 2, 0, 1. If we remove the call here to $once.2 then the order would be | |
;; 0, 2, 1. | |
;; | |
;; The problem is that the setting of the global happens at the very start | |
;; of the once function, but that does not mean we have executed the body | |
;; yet, and without executing it, we cannot infer anything about other | |
;; globals. | |
(call $once.2) | |
(call $import | |
(i32.const 0) | |
) | |
) | |
;; CHECK: (func $once.1 (type $0) | |
;; CHECK-NEXT: (if | |
;; CHECK-NEXT: (global.get $once.1) | |
;; CHECK-NEXT: (return) | |
;; CHECK-NEXT: ) | |
;; CHECK-NEXT: (global.set $once.1 | |
;; CHECK-NEXT: (i32.const 1) | |
;; CHECK-NEXT: ) | |
;; CHECK-NEXT: (call $once) | |
;; CHECK-NEXT: (call $once.2) | |
;; CHECK-NEXT: (call $import | |
;; CHECK-NEXT: (i32.const 1) | |
;; CHECK-NEXT: ) | |
;; CHECK-NEXT: ) | |
(func $once.1 | |
(if | |
(global.get $once.1) | |
(return) | |
) | |
(global.set $once.1 (i32.const 1)) | |
(call $once) | |
;; As above, by symmetry, we cannot remove this second call. | |
(call $once.2) | |
(call $import | |
(i32.const 1) | |
) | |
) | |
;; CHECK: (func $once.2 (type $0) | |
;; CHECK-NEXT: (if | |
;; CHECK-NEXT: (global.get $once.2) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment