summaryrefslogtreecommitdiff
path: root/test/lit/ctor-eval/gc-cycle.wast
diff options
context:
space:
mode:
Diffstat (limited to 'test/lit/ctor-eval/gc-cycle.wast')
-rw-r--r--test/lit/ctor-eval/gc-cycle.wast1297
1 files changed, 1297 insertions, 0 deletions
diff --git a/test/lit/ctor-eval/gc-cycle.wast b/test/lit/ctor-eval/gc-cycle.wast
new file mode 100644
index 000000000..a6edc28aa
--- /dev/null
+++ b/test/lit/ctor-eval/gc-cycle.wast
@@ -0,0 +1,1297 @@
+;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited.
+;; RUN: foreach %s %t wasm-ctor-eval --ctors=test --kept-exports=test --quiet -all -S -o - | filecheck %s
+
+(module
+ ;; CHECK: (type $A (struct (field (mut (ref null $A))) (field i32)))
+ (type $A (struct (field (mut (ref null $A))) (field i32)))
+
+ ;; CHECK: (type $none_=>_none (func))
+
+ ;; CHECK: (type $none_=>_i32 (func (result i32)))
+
+ ;; CHECK: (global $ctor-eval$global_3 (ref $A) (struct.new $A
+ ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: (i32.const 42)
+ ;; CHECK-NEXT: ))
+
+ ;; CHECK: (global $a (mut (ref null $A)) (global.get $ctor-eval$global_3))
+ (global $a (mut (ref null $A)) (ref.null $A))
+
+ (func $test (export "test")
+ (local $a (ref $A))
+ ;; This generates a self-cycle where the global $a's ref field points to
+ ;; itself. To handle this, wasm-ctor-eval will emit a new global with a null
+ ;; in the ref field, and add a start function that adds the self-reference.
+ (global.set $a
+ (local.tee $a
+ (struct.new $A
+ (ref.null $A)
+ (i32.const 42)
+ )
+ )
+ )
+ (struct.set $A 0
+ (local.get $a)
+ (local.get $a)
+ )
+ )
+
+ ;; CHECK: (export "test" (func $test_3))
+
+ ;; CHECK: (export "keepalive" (func $keepalive))
+
+ ;; CHECK: (start $start)
+
+ ;; CHECK: (func $keepalive (type $none_=>_i32) (result i32)
+ ;; CHECK-NEXT: (struct.get $A 1
+ ;; CHECK-NEXT: (struct.get $A 0
+ ;; CHECK-NEXT: (global.get $a)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $keepalive (export "keepalive") (result i32)
+ ;; Getting $A.0.1 (reading from the reference in the global's first field)
+ ;; checks that we have a proper reference there. If we could do --fuzz-exec
+ ;; here we could validate that (but atm we can't use --output=fuzz-exec at the
+ ;; same time as --all-items in the update_lit_checks.py note).
+ (struct.get $A 1
+ (struct.get $A 0
+ (global.get $a)
+ )
+ )
+ )
+)
+
+;; CHECK: (func $start (type $none_=>_none)
+;; CHECK-NEXT: (struct.set $A 0
+;; CHECK-NEXT: (global.get $ctor-eval$global_3)
+;; CHECK-NEXT: (global.get $ctor-eval$global_3)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+
+;; CHECK: (func $test_3 (type $none_=>_none)
+;; CHECK-NEXT: (local $a (ref $A))
+;; CHECK-NEXT: (nop)
+;; CHECK-NEXT: )
+(module
+ ;; As above, but with $A's fields reversed. This verifies we use the right
+ ;; field index in the start function.
+
+ ;; CHECK: (type $A (struct (field i32) (field (mut (ref null $A)))))
+ (type $A (struct (field i32) (field (mut (ref null $A)))))
+
+ ;; CHECK: (type $none_=>_none (func))
+
+ ;; CHECK: (type $none_=>_i32 (func (result i32)))
+
+ ;; CHECK: (global $ctor-eval$global_3 (ref $A) (struct.new $A
+ ;; CHECK-NEXT: (i32.const 42)
+ ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: ))
+
+ ;; CHECK: (global $a (mut (ref null $A)) (global.get $ctor-eval$global_3))
+ (global $a (mut (ref null $A)) (ref.null $A))
+
+ (func $test (export "test")
+ (local $a (ref $A))
+ (global.set $a
+ (local.tee $a
+ (struct.new $A
+ (i32.const 42)
+ (ref.null $A)
+ )
+ )
+ )
+ (struct.set $A 1
+ (local.get $a)
+ (local.get $a)
+ )
+ )
+
+ ;; CHECK: (export "test" (func $test_3))
+
+ ;; CHECK: (export "keepalive" (func $keepalive))
+
+ ;; CHECK: (start $start)
+
+ ;; CHECK: (func $keepalive (type $none_=>_i32) (result i32)
+ ;; CHECK-NEXT: (struct.get $A 0
+ ;; CHECK-NEXT: (struct.get $A 1
+ ;; CHECK-NEXT: (global.get $a)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $keepalive (export "keepalive") (result i32)
+ (struct.get $A 0
+ (struct.get $A 1
+ (global.get $a)
+ )
+ )
+ )
+)
+
+;; CHECK: (func $start (type $none_=>_none)
+;; CHECK-NEXT: (struct.set $A 1
+;; CHECK-NEXT: (global.get $ctor-eval$global_3)
+;; CHECK-NEXT: (global.get $ctor-eval$global_3)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+
+;; CHECK: (func $test_3 (type $none_=>_none)
+;; CHECK-NEXT: (local $a (ref $A))
+;; CHECK-NEXT: (nop)
+;; CHECK-NEXT: )
+(module
+ ;; A cycle between two globals.
+
+ ;; CHECK: (type $A (struct (field (mut (ref null $A))) (field i32)))
+ (type $A (struct (field (mut (ref null $A))) (field i32)))
+
+ ;; CHECK: (type $none_=>_none (func))
+
+ ;; CHECK: (type $none_=>_i32 (func (result i32)))
+
+ ;; CHECK: (global $ctor-eval$global_8 (ref $A) (struct.new $A
+ ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: (i32.const 42)
+ ;; CHECK-NEXT: ))
+
+ ;; CHECK: (global $a (mut (ref null $A)) (global.get $ctor-eval$global_8))
+ (global $a (mut (ref null $A)) (ref.null $A))
+
+ ;; CHECK: (global $ctor-eval$global_7 (ref $A) (struct.new $A
+ ;; CHECK-NEXT: (global.get $ctor-eval$global_8)
+ ;; CHECK-NEXT: (i32.const 1337)
+ ;; CHECK-NEXT: ))
+
+ ;; CHECK: (global $b (mut (ref null $A)) (global.get $ctor-eval$global_7))
+ (global $b (mut (ref null $A)) (ref.null $A))
+
+ (func $test (export "test")
+ (local $a (ref $A))
+ (local $b (ref $A))
+ (global.set $a
+ (local.tee $a
+ (struct.new $A
+ (ref.null $A)
+ (i32.const 42)
+ )
+ )
+ )
+ (global.set $b
+ (local.tee $b
+ (struct.new $A
+ (global.get $a) ;; $b can refer to $a since we've created $a already.
+ (i32.const 1337)
+ )
+ )
+ )
+ ;; $a needs a set to allow us to create the cycle.
+ (struct.set $A 0
+ (local.get $a)
+ (local.get $b)
+ )
+ )
+
+ ;; CHECK: (export "test" (func $test_3))
+
+ ;; CHECK: (export "keepalive" (func $keepalive))
+
+ ;; CHECK: (start $start)
+
+ ;; CHECK: (func $keepalive (type $none_=>_i32) (result i32)
+ ;; CHECK-NEXT: (i32.add
+ ;; CHECK-NEXT: (struct.get $A 1
+ ;; CHECK-NEXT: (global.get $a)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (struct.get $A 1
+ ;; CHECK-NEXT: (global.get $b)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $keepalive (export "keepalive") (result i32)
+ (i32.add
+ (struct.get $A 1
+ (global.get $a)
+ )
+ (struct.get $A 1
+ (global.get $b)
+ )
+ )
+ )
+)
+
+;; CHECK: (func $start (type $none_=>_none)
+;; CHECK-NEXT: (struct.set $A 0
+;; CHECK-NEXT: (global.get $ctor-eval$global_8)
+;; CHECK-NEXT: (global.get $ctor-eval$global_7)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+
+;; CHECK: (func $test_3 (type $none_=>_none)
+;; CHECK-NEXT: (local $a (ref $A))
+;; CHECK-NEXT: (local $b (ref $A))
+;; CHECK-NEXT: (nop)
+;; CHECK-NEXT: )
+(module
+ ;; A cycle between two globals of different types. One of them has an
+ ;; immutable field in the cycle.
+
+ (rec
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $A (struct (field (mut (ref null $B))) (field i32)))
+ (type $A (struct (field (mut (ref null $B))) (field i32)))
+
+ ;; CHECK: (type $B (struct (field (ref null $A)) (field i32)))
+ (type $B (struct (field (ref null $A)) (field i32)))
+ )
+
+ ;; CHECK: (type $none_=>_none (func))
+
+ ;; CHECK: (type $none_=>_i32 (func (result i32)))
+
+ ;; CHECK: (global $ctor-eval$global_8 (ref $A) (struct.new $A
+ ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: (i32.const 42)
+ ;; CHECK-NEXT: ))
+
+ ;; CHECK: (global $a (mut (ref null $A)) (global.get $ctor-eval$global_8))
+ (global $a (mut (ref null $A)) (ref.null $A))
+
+ ;; CHECK: (global $ctor-eval$global_7 (ref $B) (struct.new $B
+ ;; CHECK-NEXT: (global.get $ctor-eval$global_8)
+ ;; CHECK-NEXT: (i32.const 1337)
+ ;; CHECK-NEXT: ))
+
+ ;; CHECK: (global $b (mut (ref null $B)) (global.get $ctor-eval$global_7))
+ (global $b (mut (ref null $B)) (ref.null $B))
+
+ (func $test (export "test")
+ (local $a (ref $A))
+ (local $b (ref $B))
+ (global.set $a
+ (local.tee $a
+ (struct.new $A
+ (ref.null $A)
+ (i32.const 42)
+ )
+ )
+ )
+ (global.set $b
+ (local.tee $b
+ (struct.new $B
+ (global.get $a)
+ (i32.const 1337)
+ )
+ )
+ )
+ (struct.set $A 0
+ (local.get $a)
+ (local.get $b)
+ )
+ )
+
+ ;; CHECK: (export "test" (func $test_3))
+
+ ;; CHECK: (export "keepalive" (func $keepalive))
+
+ ;; CHECK: (start $start)
+
+ ;; CHECK: (func $keepalive (type $none_=>_i32) (result i32)
+ ;; CHECK-NEXT: (i32.add
+ ;; CHECK-NEXT: (struct.get $A 1
+ ;; CHECK-NEXT: (global.get $a)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (struct.get $B 1
+ ;; CHECK-NEXT: (global.get $b)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $keepalive (export "keepalive") (result i32)
+ (i32.add
+ (struct.get $A 1
+ (global.get $a)
+ )
+ (struct.get $B 1
+ (global.get $b)
+ )
+ )
+ )
+)
+
+;; CHECK: (func $start (type $none_=>_none)
+;; CHECK-NEXT: (struct.set $A 0
+;; CHECK-NEXT: (global.get $ctor-eval$global_8)
+;; CHECK-NEXT: (global.get $ctor-eval$global_7)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+
+;; CHECK: (func $test_3 (type $none_=>_none)
+;; CHECK-NEXT: (local $a (ref $A))
+;; CHECK-NEXT: (local $b (ref $B))
+;; CHECK-NEXT: (nop)
+;; CHECK-NEXT: )
+(module
+ ;; As above, but with the order of globals reversed.
+
+ (rec
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $A (struct (field (mut (ref null $B))) (field i32)))
+ (type $A (struct (field (mut (ref null $B))) (field i32)))
+
+ ;; CHECK: (type $B (struct (field (ref null $A)) (field i32)))
+ (type $B (struct (field (ref null $A)) (field i32)))
+ )
+
+
+ ;; CHECK: (type $none_=>_none (func))
+
+ ;; CHECK: (type $none_=>_i32 (func (result i32)))
+
+ ;; CHECK: (global $ctor-eval$global_8 (ref $A) (struct.new $A
+ ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: (i32.const 42)
+ ;; CHECK-NEXT: ))
+
+ ;; CHECK: (global $a (mut (ref null $A)) (global.get $ctor-eval$global_8))
+
+ ;; CHECK: (global $ctor-eval$global_7 (ref $B) (struct.new $B
+ ;; CHECK-NEXT: (global.get $ctor-eval$global_8)
+ ;; CHECK-NEXT: (i32.const 1337)
+ ;; CHECK-NEXT: ))
+
+ ;; CHECK: (global $b (mut (ref null $B)) (global.get $ctor-eval$global_7))
+ (global $b (mut (ref null $B)) (ref.null $B))
+
+ (global $a (mut (ref null $A)) (ref.null $A))
+
+ (func $test (export "test")
+ (local $a (ref $A))
+ (local $b (ref $B))
+ (global.set $a
+ (local.tee $a
+ (struct.new $A
+ (ref.null $A)
+ (i32.const 42)
+ )
+ )
+ )
+ (global.set $b
+ (local.tee $b
+ (struct.new $B
+ (global.get $a)
+ (i32.const 1337)
+ )
+ )
+ )
+ (struct.set $A 0
+ (local.get $a)
+ (local.get $b)
+ )
+ )
+
+ ;; CHECK: (export "test" (func $test_3))
+
+ ;; CHECK: (export "keepalive" (func $keepalive))
+
+ ;; CHECK: (start $start)
+
+ ;; CHECK: (func $keepalive (type $none_=>_i32) (result i32)
+ ;; CHECK-NEXT: (i32.add
+ ;; CHECK-NEXT: (struct.get $A 1
+ ;; CHECK-NEXT: (global.get $a)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (struct.get $B 1
+ ;; CHECK-NEXT: (global.get $b)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $keepalive (export "keepalive") (result i32)
+ (i32.add
+ (struct.get $A 1
+ (global.get $a)
+ )
+ (struct.get $B 1
+ (global.get $b)
+ )
+ )
+ )
+)
+
+;; CHECK: (func $start (type $none_=>_none)
+;; CHECK-NEXT: (struct.set $A 0
+;; CHECK-NEXT: (global.get $ctor-eval$global_8)
+;; CHECK-NEXT: (global.get $ctor-eval$global_7)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+
+;; CHECK: (func $test_3 (type $none_=>_none)
+;; CHECK-NEXT: (local $a (ref $A))
+;; CHECK-NEXT: (local $b (ref $B))
+;; CHECK-NEXT: (nop)
+;; CHECK-NEXT: )
+(module
+ ;; A cycle as above, but with non-nullability rather than immutability.
+
+ (rec
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $A (struct (field (mut (ref null $B))) (field i32)))
+ (type $A (struct (field (mut (ref null $B))) (field i32)))
+
+ ;; CHECK: (type $B (struct (field (mut (ref $A))) (field i32)))
+ (type $B (struct (field (mut (ref $A))) (field i32)))
+ )
+
+ ;; CHECK: (type $none_=>_none (func))
+
+ ;; CHECK: (type $none_=>_i32 (func (result i32)))
+
+ ;; CHECK: (global $ctor-eval$global_8 (ref $A) (struct.new $A
+ ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: (i32.const 42)
+ ;; CHECK-NEXT: ))
+
+ ;; CHECK: (global $a (mut (ref null $A)) (global.get $ctor-eval$global_8))
+ (global $a (mut (ref null $A)) (ref.null $A))
+
+ (global $b (mut (ref null $B)) (ref.null $B))
+
+ (func $test (export "test")
+ (local $a (ref $A))
+ (local $b (ref $B))
+ (global.set $a
+ (local.tee $a
+ (struct.new $A
+ (ref.null $A)
+ (i32.const 42)
+ )
+ )
+ )
+ (global.set $b
+ (local.tee $b
+ (struct.new $B
+ (local.get $a)
+ (i32.const 1337)
+ )
+ )
+ )
+ (struct.set $A 0
+ (local.get $a)
+ (local.get $b)
+ )
+ )
+
+ ;; CHECK: (global $ctor-eval$global_7 (ref $B) (struct.new $B
+ ;; CHECK-NEXT: (global.get $ctor-eval$global_8)
+ ;; CHECK-NEXT: (i32.const 1337)
+ ;; CHECK-NEXT: ))
+
+ ;; CHECK: (export "test" (func $test_3))
+
+ ;; CHECK: (export "keepalive" (func $keepalive))
+
+ ;; CHECK: (start $start)
+
+ ;; CHECK: (func $keepalive (type $none_=>_i32) (result i32)
+ ;; CHECK-NEXT: (struct.get $A 1
+ ;; CHECK-NEXT: (global.get $a)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $keepalive (export "keepalive") (result i32)
+ (struct.get $A 1
+ (global.get $a)
+ )
+ )
+)
+
+;; CHECK: (func $start (type $none_=>_none)
+;; CHECK-NEXT: (struct.set $A 0
+;; CHECK-NEXT: (global.get $ctor-eval$global_8)
+;; CHECK-NEXT: (global.get $ctor-eval$global_7)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+
+;; CHECK: (func $test_3 (type $none_=>_none)
+;; CHECK-NEXT: (local $a (ref $A))
+;; CHECK-NEXT: (local $b (ref $B))
+;; CHECK-NEXT: (nop)
+;; CHECK-NEXT: )
+(module
+ ;; A cycle as above, but with globals in reverse order and with both non-
+ ;; nullability and immutability.
+
+ (rec
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $A (struct (field (mut (ref null $B))) (field i32)))
+ (type $A (struct (field (mut (ref null $B))) (field i32)))
+
+ ;; CHECK: (type $B (struct (field (ref $A)) (field i32)))
+ (type $B (struct (field (ref $A)) (field i32)))
+ )
+
+
+ (global $b (mut (ref null $B)) (ref.null $B))
+
+ ;; CHECK: (type $none_=>_none (func))
+
+ ;; CHECK: (type $none_=>_i32 (func (result i32)))
+
+ ;; CHECK: (global $ctor-eval$global_8 (ref $A) (struct.new $A
+ ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: (i32.const 42)
+ ;; CHECK-NEXT: ))
+
+ ;; CHECK: (global $a (mut (ref null $A)) (global.get $ctor-eval$global_8))
+ (global $a (mut (ref null $A)) (ref.null $A))
+
+ (func $test (export "test")
+ (local $a (ref $A))
+ (local $b (ref $B))
+ (global.set $a
+ (local.tee $a
+ (struct.new $A
+ (ref.null $A)
+ (i32.const 42)
+ )
+ )
+ )
+ (global.set $b
+ (local.tee $b
+ (struct.new $B
+ (local.get $a)
+ (i32.const 1337)
+ )
+ )
+ )
+ (struct.set $A 0
+ (local.get $a)
+ (local.get $b)
+ )
+ )
+
+ ;; CHECK: (global $ctor-eval$global_7 (ref $B) (struct.new $B
+ ;; CHECK-NEXT: (global.get $ctor-eval$global_8)
+ ;; CHECK-NEXT: (i32.const 1337)
+ ;; CHECK-NEXT: ))
+
+ ;; CHECK: (export "test" (func $test_3))
+
+ ;; CHECK: (export "keepalive" (func $keepalive))
+
+ ;; CHECK: (start $start)
+
+ ;; CHECK: (func $keepalive (type $none_=>_i32) (result i32)
+ ;; CHECK-NEXT: (struct.get $A 1
+ ;; CHECK-NEXT: (global.get $a)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $keepalive (export "keepalive") (result i32)
+ (struct.get $A 1
+ (global.get $a)
+ )
+ )
+)
+
+;; CHECK: (func $start (type $none_=>_none)
+;; CHECK-NEXT: (struct.set $A 0
+;; CHECK-NEXT: (global.get $ctor-eval$global_8)
+;; CHECK-NEXT: (global.get $ctor-eval$global_7)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+
+;; CHECK: (func $test_3 (type $none_=>_none)
+;; CHECK-NEXT: (local $a (ref $A))
+;; CHECK-NEXT: (local $b (ref $B))
+;; CHECK-NEXT: (nop)
+;; CHECK-NEXT: )
+(module
+ ;; A cycle between three globals.
+
+ ;; CHECK: (type $A (struct (field (mut (ref null $A))) (field i32)))
+ (type $A (struct (field (mut (ref null $A))) (field i32)))
+
+ ;; CHECK: (type $none_=>_none (func))
+
+ ;; CHECK: (type $none_=>_i32 (func (result i32)))
+
+ ;; CHECK: (global $ctor-eval$global_13 (ref $A) (struct.new $A
+ ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: (i32.const 1337)
+ ;; CHECK-NEXT: ))
+
+ ;; CHECK: (global $ctor-eval$global_14 (ref $A) (struct.new $A
+ ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: (i32.const 42)
+ ;; CHECK-NEXT: ))
+
+ ;; CHECK: (global $a (mut (ref null $A)) (global.get $ctor-eval$global_14))
+ (global $a (mut (ref null $A)) (ref.null $A))
+
+ (global $b (mut (ref null $A)) (ref.null $A))
+
+ (global $c (mut (ref null $A)) (ref.null $A))
+
+ (func $test (export "test")
+ (local $a (ref $A))
+ (local $b (ref $A))
+ (local $c (ref $A))
+ (global.set $a
+ (local.tee $a
+ (struct.new $A
+ (ref.null $A)
+ (i32.const 42)
+ )
+ )
+ )
+ (global.set $b
+ (local.tee $b
+ (struct.new $A
+ (global.get $a)
+ (i32.const 1337)
+ )
+ )
+ )
+ (global.set $c
+ (local.tee $c
+ (struct.new $A
+ (global.get $b)
+ (i32.const 99999)
+ )
+ )
+ )
+ (struct.set $A 0
+ (local.get $a)
+ (local.get $c)
+ )
+ )
+
+ ;; CHECK: (global $ctor-eval$global_12 (ref $A) (struct.new $A
+ ;; CHECK-NEXT: (global.get $ctor-eval$global_13)
+ ;; CHECK-NEXT: (i32.const 99999)
+ ;; CHECK-NEXT: ))
+
+ ;; CHECK: (export "test" (func $test_3))
+
+ ;; CHECK: (export "keepalive" (func $keepalive))
+
+ ;; CHECK: (start $start)
+
+ ;; CHECK: (func $keepalive (type $none_=>_i32) (result i32)
+ ;; CHECK-NEXT: (struct.get $A 1
+ ;; CHECK-NEXT: (global.get $a)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $keepalive (export "keepalive") (result i32)
+ (struct.get $A 1
+ (global.get $a)
+ )
+ )
+)
+
+;; CHECK: (func $start (type $none_=>_none)
+;; CHECK-NEXT: (struct.set $A 0
+;; CHECK-NEXT: (global.get $ctor-eval$global_13)
+;; CHECK-NEXT: (global.get $ctor-eval$global_14)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: (struct.set $A 0
+;; CHECK-NEXT: (global.get $ctor-eval$global_14)
+;; CHECK-NEXT: (global.get $ctor-eval$global_12)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+
+;; CHECK: (func $test_3 (type $none_=>_none)
+;; CHECK-NEXT: (local $a (ref $A))
+;; CHECK-NEXT: (local $b (ref $A))
+;; CHECK-NEXT: (local $c (ref $A))
+;; CHECK-NEXT: (nop)
+;; CHECK-NEXT: )
+(module
+ ;; A cycle between three globals as above, but now using different types and
+ ;; also both structs and arrays. Also reverse the order of globals, make
+ ;; one array immutable and one non-nullable, and make one array refer to the
+ ;; other two.
+
+ (rec
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $A (struct (field (mut (ref null $C))) (field i32)))
+ (type $A (struct (field (mut (ref null $C))) (field i32)))
+ ;; CHECK: (type $B (array (ref null $A)))
+ (type $B (array (ref null $A)))
+ ;; CHECK: (type $C (array (mut (ref any))))
+ (type $C (array (mut (ref any))))
+ )
+
+ (global $c (mut (ref null $C)) (ref.null $C))
+
+ (global $b (mut (ref null $B)) (ref.null $B))
+
+ ;; CHECK: (type $none_=>_none (func))
+
+ ;; CHECK: (type $none_=>_i32 (func (result i32)))
+
+ ;; CHECK: (global $ctor-eval$global_14 (ref $A) (struct.new $A
+ ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: (i32.const 42)
+ ;; CHECK-NEXT: ))
+
+ ;; CHECK: (global $ctor-eval$global_13 (ref $B) (array.new_fixed $B
+ ;; CHECK-NEXT: (global.get $ctor-eval$global_14)
+ ;; CHECK-NEXT: (global.get $ctor-eval$global_14)
+ ;; CHECK-NEXT: (global.get $ctor-eval$global_14)
+ ;; CHECK-NEXT: (global.get $ctor-eval$global_14)
+ ;; CHECK-NEXT: (global.get $ctor-eval$global_14)
+ ;; CHECK-NEXT: (global.get $ctor-eval$global_14)
+ ;; CHECK-NEXT: (global.get $ctor-eval$global_14)
+ ;; CHECK-NEXT: (global.get $ctor-eval$global_14)
+ ;; CHECK-NEXT: (global.get $ctor-eval$global_14)
+ ;; CHECK-NEXT: (global.get $ctor-eval$global_14)
+ ;; CHECK-NEXT: ))
+
+ ;; CHECK: (global $a (mut (ref null $A)) (global.get $ctor-eval$global_14))
+ (global $a (mut (ref null $A)) (ref.null $A))
+
+ (func $test (export "test")
+ (local $a (ref $A))
+ (local $b (ref $B))
+ (local $c (ref $C))
+ (global.set $a
+ (local.tee $a
+ (struct.new $A
+ (ref.null $C)
+ (i32.const 42)
+ )
+ )
+ )
+ (global.set $b
+ (local.tee $b
+ (array.new $B
+ (global.get $a)
+ (i32.const 10)
+ )
+ )
+ )
+ (global.set $c
+ (local.tee $c
+ (array.new_fixed $C
+ (local.get $b)
+ (local.get $a)
+ )
+ )
+ )
+ (struct.set $A 0
+ (local.get $a)
+ (global.get $c)
+ )
+ )
+
+ ;; CHECK: (global $ctor-eval$global_12 (ref $C) (array.new_fixed $C
+ ;; CHECK-NEXT: (global.get $ctor-eval$global_13)
+ ;; CHECK-NEXT: (global.get $ctor-eval$global_14)
+ ;; CHECK-NEXT: ))
+
+ ;; CHECK: (export "test" (func $test_3))
+
+ ;; CHECK: (export "keepalive" (func $keepalive))
+
+ ;; CHECK: (start $start)
+
+ ;; CHECK: (func $keepalive (type $none_=>_i32) (result i32)
+ ;; CHECK-NEXT: (struct.get $A 1
+ ;; CHECK-NEXT: (global.get $a)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $keepalive (export "keepalive") (result i32)
+ (struct.get $A 1
+ (global.get $a)
+ )
+ )
+)
+
+;; CHECK: (func $start (type $none_=>_none)
+;; CHECK-NEXT: (struct.set $A 0
+;; CHECK-NEXT: (global.get $ctor-eval$global_14)
+;; CHECK-NEXT: (global.get $ctor-eval$global_12)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+
+;; CHECK: (func $test_3 (type $none_=>_none)
+;; CHECK-NEXT: (local $a (ref $A))
+;; CHECK-NEXT: (local $b (ref $B))
+;; CHECK-NEXT: (local $c (ref $C))
+;; CHECK-NEXT: (nop)
+;; CHECK-NEXT: )
+(module
+ ;; As above but with the order of globals reversed once more.
+
+ (rec
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $A (struct (field (mut (ref null $C))) (field i32)))
+ (type $A (struct (field (mut (ref null $C))) (field i32)))
+ ;; CHECK: (type $B (array (ref null $A)))
+ (type $B (array (ref null $A)))
+ ;; CHECK: (type $C (array (mut (ref any))))
+ (type $C (array (mut (ref any))))
+ )
+
+ ;; CHECK: (type $none_=>_none (func))
+
+ ;; CHECK: (type $none_=>_i32 (func (result i32)))
+
+ ;; CHECK: (global $ctor-eval$global_14 (ref $A) (struct.new $A
+ ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: (i32.const 42)
+ ;; CHECK-NEXT: ))
+
+ ;; CHECK: (global $ctor-eval$global_13 (ref $B) (array.new_fixed $B
+ ;; CHECK-NEXT: (global.get $ctor-eval$global_14)
+ ;; CHECK-NEXT: (global.get $ctor-eval$global_14)
+ ;; CHECK-NEXT: (global.get $ctor-eval$global_14)
+ ;; CHECK-NEXT: (global.get $ctor-eval$global_14)
+ ;; CHECK-NEXT: (global.get $ctor-eval$global_14)
+ ;; CHECK-NEXT: (global.get $ctor-eval$global_14)
+ ;; CHECK-NEXT: (global.get $ctor-eval$global_14)
+ ;; CHECK-NEXT: (global.get $ctor-eval$global_14)
+ ;; CHECK-NEXT: (global.get $ctor-eval$global_14)
+ ;; CHECK-NEXT: (global.get $ctor-eval$global_14)
+ ;; CHECK-NEXT: ))
+
+ ;; CHECK: (global $a (mut (ref null $A)) (global.get $ctor-eval$global_14))
+ (global $a (mut (ref null $A)) (ref.null $A))
+
+ (global $b (mut (ref null $B)) (ref.null $B))
+
+ (global $c (mut (ref null $C)) (ref.null $C))
+
+ (func $test (export "test")
+ (local $a (ref $A))
+ (local $b (ref $B))
+ (local $c (ref $C))
+ (global.set $a
+ (local.tee $a
+ (struct.new $A
+ (ref.null $C)
+ (i32.const 42)
+ )
+ )
+ )
+ (global.set $b
+ (local.tee $b
+ (array.new $B
+ (global.get $a)
+ (i32.const 10)
+ )
+ )
+ )
+ (global.set $c
+ (local.tee $c
+ (array.new_fixed $C
+ (local.get $b)
+ (local.get $a)
+ )
+ )
+ )
+ (struct.set $A 0
+ (local.get $a)
+ (global.get $c)
+ )
+ )
+
+ ;; CHECK: (global $ctor-eval$global_12 (ref $C) (array.new_fixed $C
+ ;; CHECK-NEXT: (global.get $ctor-eval$global_13)
+ ;; CHECK-NEXT: (global.get $ctor-eval$global_14)
+ ;; CHECK-NEXT: ))
+
+ ;; CHECK: (export "test" (func $test_3))
+
+ ;; CHECK: (export "keepalive" (func $keepalive))
+
+ ;; CHECK: (start $start)
+
+ ;; CHECK: (func $keepalive (type $none_=>_i32) (result i32)
+ ;; CHECK-NEXT: (struct.get $A 1
+ ;; CHECK-NEXT: (global.get $a)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $keepalive (export "keepalive") (result i32)
+ (struct.get $A 1
+ (global.get $a)
+ )
+ )
+)
+
+;; CHECK: (func $start (type $none_=>_none)
+;; CHECK-NEXT: (struct.set $A 0
+;; CHECK-NEXT: (global.get $ctor-eval$global_14)
+;; CHECK-NEXT: (global.get $ctor-eval$global_12)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+
+;; CHECK: (func $test_3 (type $none_=>_none)
+;; CHECK-NEXT: (local $a (ref $A))
+;; CHECK-NEXT: (local $b (ref $B))
+;; CHECK-NEXT: (local $c (ref $C))
+;; CHECK-NEXT: (nop)
+;; CHECK-NEXT: )
+(module
+ ;; A cycle between two globals, where some of the fields participate in the
+ ;; cycle and some do not.
+
+ (rec
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $A (struct (field (mut (ref null $B))) (field (mut (ref null $B))) (field (mut (ref null $B)))))
+ (type $A (struct (field (mut (ref null $B))) (field (mut (ref null $B))) (field (mut (ref null $B)))))
+ ;; CHECK: (type $B (array (ref null $A)))
+ (type $B (array (ref null $A)))
+ )
+
+ ;; CHECK: (type $none_=>_none (func))
+
+ ;; CHECK: (type $none_=>_anyref (func (result anyref)))
+
+ ;; CHECK: (global $ctor-eval$global_16 (ref $A) (struct.new $A
+ ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: ))
+
+ ;; CHECK: (global $a (mut (ref null $A)) (global.get $ctor-eval$global_16))
+ (global $a (mut (ref null $A)) (ref.null $A))
+ (global $b (mut (ref null $B)) (ref.null $B))
+
+ (func $test (export "test")
+ (local $a (ref $A))
+ (global.set $a
+ (local.tee $a
+ (struct.new $A
+ (array.new_default $B
+ (i32.const 0)
+ )
+ (ref.null $B)
+ (array.new_default $B
+ (i32.const 0)
+ )
+ )
+ )
+ )
+ (global.set $b
+ (array.new_fixed $B
+ (struct.new_default $A)
+ (global.get $a)
+ (struct.new_default $A)
+ )
+ )
+ (struct.set $A 1
+ (local.get $a)
+ (global.get $b)
+ )
+ )
+
+ ;; CHECK: (global $ctor-eval$global_19 (ref $A) (struct.new $A
+ ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: ))
+
+ ;; CHECK: (global $ctor-eval$global_15 (ref $A) (struct.new $A
+ ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: ))
+
+ ;; CHECK: (global $ctor-eval$global_14 (ref $B) (array.new_fixed $B
+ ;; CHECK-NEXT: (global.get $ctor-eval$global_15)
+ ;; CHECK-NEXT: (global.get $ctor-eval$global_16)
+ ;; CHECK-NEXT: (global.get $ctor-eval$global_19)
+ ;; CHECK-NEXT: ))
+
+ ;; CHECK: (global $ctor-eval$global_17 (ref $B) (array.new_fixed $B))
+
+ ;; CHECK: (global $ctor-eval$global_18 (ref $B) (array.new_fixed $B))
+
+ ;; CHECK: (export "test" (func $test_3))
+
+ ;; CHECK: (export "keepalive" (func $keepalive))
+
+ ;; CHECK: (start $start)
+
+ ;; CHECK: (func $keepalive (type $none_=>_anyref) (result anyref)
+ ;; CHECK-NEXT: (struct.get $A 1
+ ;; CHECK-NEXT: (global.get $a)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $keepalive (export "keepalive") (result (ref null any))
+ (struct.get $A 1
+ (global.get $a)
+ )
+ )
+)
+
+;; CHECK: (func $start (type $none_=>_none)
+;; CHECK-NEXT: (struct.set $A 0
+;; CHECK-NEXT: (global.get $ctor-eval$global_16)
+;; CHECK-NEXT: (global.get $ctor-eval$global_17)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: (struct.set $A 1
+;; CHECK-NEXT: (global.get $ctor-eval$global_16)
+;; CHECK-NEXT: (global.get $ctor-eval$global_14)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: (struct.set $A 2
+;; CHECK-NEXT: (global.get $ctor-eval$global_16)
+;; CHECK-NEXT: (global.get $ctor-eval$global_18)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+
+;; CHECK: (func $test_3 (type $none_=>_none)
+;; CHECK-NEXT: (local $a (ref $A))
+;; CHECK-NEXT: (nop)
+;; CHECK-NEXT: )
+(module
+ ;; As above, with the cycle creation logic reversed.
+
+ (rec
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $A (struct (field (ref null $B)) (field (ref null $B)) (field (ref null $B))))
+ (type $A (struct (field (ref null $B)) (field (ref null $B)) (field (ref null $B))))
+ ;; CHECK: (type $B (array (mut (ref null $A))))
+ (type $B (array (mut (ref null $A))))
+ )
+
+ ;; CHECK: (type $none_=>_none (func))
+
+ ;; CHECK: (type $none_=>_anyref (func (result anyref)))
+
+ ;; CHECK: (global $ctor-eval$global_16 (ref $B) (array.new_fixed $B
+ ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: ))
+
+ ;; CHECK: (global $ctor-eval$global_19 (ref $B) (array.new_fixed $B))
+
+ ;; CHECK: (global $ctor-eval$global_15 (ref $B) (array.new_fixed $B))
+
+ ;; CHECK: (global $ctor-eval$global_14 (ref $A) (struct.new $A
+ ;; CHECK-NEXT: (global.get $ctor-eval$global_15)
+ ;; CHECK-NEXT: (global.get $ctor-eval$global_16)
+ ;; CHECK-NEXT: (global.get $ctor-eval$global_19)
+ ;; CHECK-NEXT: ))
+
+ ;; CHECK: (global $a (mut (ref null $A)) (global.get $ctor-eval$global_14))
+ (global $a (mut (ref null $A)) (ref.null $A))
+ (global $b (mut (ref null $B)) (ref.null $B))
+
+ (func $test (export "test")
+ (local $b (ref $B))
+ (global.set $b
+ (local.tee $b
+ (array.new_fixed $B
+ (struct.new_default $A)
+ (ref.null $A)
+ (struct.new_default $A)
+ )
+ )
+ )
+ (global.set $a
+ (struct.new $A
+ (array.new_default $B
+ (i32.const 0)
+ )
+ (global.get $b)
+ (array.new_default $B
+ (i32.const 0)
+ )
+ )
+ )
+ (array.set $B
+ (local.get $b)
+ (i32.const 1)
+ (global.get $a)
+ )
+ )
+
+ ;; CHECK: (global $ctor-eval$global_17 (ref $A) (struct.new $A
+ ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: ))
+
+ ;; CHECK: (global $ctor-eval$global_18 (ref $A) (struct.new $A
+ ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: ))
+
+ ;; CHECK: (export "test" (func $test_3))
+
+ ;; CHECK: (export "keepalive" (func $keepalive))
+
+ ;; CHECK: (start $start)
+
+ ;; CHECK: (func $keepalive (type $none_=>_anyref) (result anyref)
+ ;; CHECK-NEXT: (struct.get $A 1
+ ;; CHECK-NEXT: (global.get $a)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $keepalive (export "keepalive") (result (ref null any))
+ (struct.get $A 1
+ (global.get $a)
+ )
+ )
+)
+
+;; CHECK: (func $start (type $none_=>_none)
+;; CHECK-NEXT: (array.set $B
+;; CHECK-NEXT: (global.get $ctor-eval$global_16)
+;; CHECK-NEXT: (i32.const 0)
+;; CHECK-NEXT: (global.get $ctor-eval$global_17)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: (array.set $B
+;; CHECK-NEXT: (global.get $ctor-eval$global_16)
+;; CHECK-NEXT: (i32.const 1)
+;; CHECK-NEXT: (global.get $ctor-eval$global_14)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: (array.set $B
+;; CHECK-NEXT: (global.get $ctor-eval$global_16)
+;; CHECK-NEXT: (i32.const 2)
+;; CHECK-NEXT: (global.get $ctor-eval$global_18)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+
+;; CHECK: (func $test_3 (type $none_=>_none)
+;; CHECK-NEXT: (local $b (ref $B))
+;; CHECK-NEXT: (nop)
+;; CHECK-NEXT: )
+(module
+ ;; The start function already exists here. We must prepend to it.
+
+ ;; CHECK: (type $A (struct (field (mut (ref null $A))) (field i32)))
+ (type $A (struct (field (mut (ref null $A))) (field i32)))
+
+ ;; CHECK: (type $none_=>_none (func))
+
+ ;; CHECK: (type $none_=>_i32 (func (result i32)))
+
+ ;; CHECK: (global $ctor-eval$global_4 (ref $A) (struct.new $A
+ ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: (i32.const 42)
+ ;; CHECK-NEXT: ))
+
+ ;; CHECK: (global $a (mut (ref null $A)) (global.get $ctor-eval$global_4))
+ (global $a (mut (ref null $A)) (ref.null $A))
+
+ ;; CHECK: (global $b (mut (ref null $A)) (ref.null none))
+ (global $b (mut (ref null $A)) (ref.null $A))
+
+ ;; CHECK: (export "test" (func $test_3))
+
+ ;; CHECK: (export "keepalive" (func $keepalive))
+
+ ;; CHECK: (start $start)
+ (start $start)
+
+ (func $test (export "test")
+ (local $a (ref $A))
+ (global.set $a
+ (local.tee $a
+ (struct.new $A
+ (ref.null $A)
+ (i32.const 42)
+ )
+ )
+ )
+ (struct.set $A 0
+ (local.get $a)
+ (local.get $a)
+ )
+ )
+
+ ;; CHECK: (func $keepalive (type $none_=>_i32) (result i32)
+ ;; CHECK-NEXT: (i32.add
+ ;; CHECK-NEXT: (struct.get $A 1
+ ;; CHECK-NEXT: (global.get $a)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (struct.get $A 1
+ ;; CHECK-NEXT: (global.get $b)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $keepalive (export "keepalive") (result i32)
+ (i32.add
+ (struct.get $A 1
+ (global.get $a)
+ )
+ (struct.get $A 1
+ (global.get $b)
+ )
+ )
+ )
+
+ ;; CHECK: (func $start (type $none_=>_none)
+ ;; CHECK-NEXT: (struct.set $A 0
+ ;; CHECK-NEXT: (global.get $ctor-eval$global_4)
+ ;; CHECK-NEXT: (global.get $ctor-eval$global_4)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (global.set $b
+ ;; CHECK-NEXT: (global.get $a)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $start
+ (global.set $b
+ (global.get $a)
+ )
+ )
+)
+
+;; CHECK: (func $test_3 (type $none_=>_none)
+;; CHECK-NEXT: (local $a (ref $A))
+;; CHECK-NEXT: (nop)
+;; CHECK-NEXT: )
+(module
+ ;; CHECK: (type $A (struct (field (mut (ref null $A)))))
+ (type $A (struct (field (mut (ref null $A)))))
+
+ ;; CHECK: (type $none_=>_none (func))
+
+ ;; CHECK: (type $anyref_=>_none (func (param anyref)))
+
+ ;; CHECK: (import "a" "b" (func $import (param anyref)))
+ (import "a" "b" (func $import (param anyref)))
+
+ (func $test (export "test")
+ (local $a (ref $A))
+ (struct.set $A 0
+ (local.tee $a
+ (struct.new_default $A)
+ )
+ (local.get $a)
+ )
+ ;; The previous instructions created a cycle, which we now send to an import.
+ ;; The import will block us from evalling the entire function, and we will
+ ;; only partially eval it, removing the statements before the call. Note that
+ ;; the cycle only exists in local state - there is no global it is copied to -
+ ;; and so this test verifies that we handle cycles in local state.
+ (call $import
+ (local.get $a)
+ )
+ )
+)
+;; CHECK: (global $ctor-eval$global (ref $A) (struct.new $A
+;; CHECK-NEXT: (ref.null none)
+;; CHECK-NEXT: ))
+
+;; CHECK: (export "test" (func $test_3))
+
+;; CHECK: (start $start)
+
+;; CHECK: (func $start (type $none_=>_none)
+;; CHECK-NEXT: (struct.set $A 0
+;; CHECK-NEXT: (global.get $ctor-eval$global)
+;; CHECK-NEXT: (global.get $ctor-eval$global)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+
+;; CHECK: (func $test_3 (type $none_=>_none)
+;; CHECK-NEXT: (call $import
+;; CHECK-NEXT: (global.get $ctor-eval$global)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )