Skip to content

Instantly share code, notes, and snippets.

@kripken
Created October 27, 2023 21:53
Show Gist options
  • Save kripken/56a0f4f1d70e9575273c154d6eb981f8 to your computer and use it in GitHub Desktop.
Save kripken/56a0f4f1d70e9575273c154d6eb981f8 to your computer and use it in GitHub Desktop.
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