| ;; 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 $1 (func)) | |
| ;; CHECK: (type $2 (func (result i32))) | |
| ;; CHECK: (global $ctor-eval$global_2 (ref (exact $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_2)) | |
| (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 $2) (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 $1) | |
| ;; CHECK-NEXT: (struct.set $A 0 | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_2) | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_2) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK: (func $test_3 (type $1) | |
| ;; 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 $1 (func)) | |
| ;; CHECK: (type $2 (func (result i32))) | |
| ;; CHECK: (global $ctor-eval$global_2 (ref (exact $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_2)) | |
| (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 $2) (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 $1) | |
| ;; CHECK-NEXT: (struct.set $A 1 | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_2) | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_2) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK: (func $test_3 (type $1) | |
| ;; 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 $1 (func)) | |
| ;; CHECK: (type $2 (func (result i32))) | |
| ;; CHECK: (global $ctor-eval$global_5 (ref (exact $A)) (struct.new $A | |
| ;; CHECK-NEXT: (ref.null none) | |
| ;; CHECK-NEXT: (i32.const 42) | |
| ;; CHECK-NEXT: )) | |
| ;; CHECK: (global $ctor-eval$global_6 (ref (exact $A)) (struct.new $A | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_5) | |
| ;; CHECK-NEXT: (i32.const 1337) | |
| ;; CHECK-NEXT: )) | |
| ;; CHECK: (global $a (mut (ref null $A)) (global.get $ctor-eval$global_5)) | |
| (global $a (mut (ref null $A)) (ref.null $A)) | |
| ;; CHECK: (global $b (mut (ref null $A)) (global.get $ctor-eval$global_6)) | |
| (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 $2) (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 $1) | |
| ;; CHECK-NEXT: (struct.set $A 0 | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_5) | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_6) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK: (func $test_3 (type $1) | |
| ;; 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 $2 (func)) | |
| ;; CHECK: (type $3 (func (result i32))) | |
| ;; CHECK: (global $ctor-eval$global_5 (ref (exact $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_5)) | |
| (global $a (mut (ref null $A)) (ref.null $A)) | |
| ;; CHECK: (global $ctor-eval$global_6 (ref (exact $B)) (struct.new $B | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_5) | |
| ;; CHECK-NEXT: (i32.const 1337) | |
| ;; CHECK-NEXT: )) | |
| ;; CHECK: (global $b (mut (ref null $B)) (global.get $ctor-eval$global_6)) | |
| (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 $3) (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 $2) | |
| ;; CHECK-NEXT: (struct.set $A 0 | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_5) | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_6) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK: (func $test_3 (type $2) | |
| ;; 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 $2 (func)) | |
| ;; CHECK: (type $3 (func (result i32))) | |
| ;; CHECK: (global $ctor-eval$global_5 (ref (exact $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_5)) | |
| ;; CHECK: (global $ctor-eval$global_6 (ref (exact $B)) (struct.new $B | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_5) | |
| ;; CHECK-NEXT: (i32.const 1337) | |
| ;; CHECK-NEXT: )) | |
| ;; CHECK: (global $b (mut (ref null $B)) (global.get $ctor-eval$global_6)) | |
| (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 $3) (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 $2) | |
| ;; CHECK-NEXT: (struct.set $A 0 | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_5) | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_6) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK: (func $test_3 (type $2) | |
| ;; 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 $2 (func)) | |
| ;; CHECK: (type $3 (func (result i32))) | |
| ;; CHECK: (global $ctor-eval$global_5 (ref (exact $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_5)) | |
| (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_6 (ref (exact $B)) (struct.new $B | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_5) | |
| ;; 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 $3) (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 $2) | |
| ;; CHECK-NEXT: (struct.set $A 0 | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_5) | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_6) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK: (func $test_3 (type $2) | |
| ;; 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 $2 (func)) | |
| ;; CHECK: (type $3 (func (result i32))) | |
| ;; CHECK: (global $ctor-eval$global_5 (ref (exact $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_5)) | |
| (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_6 (ref (exact $B)) (struct.new $B | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_5) | |
| ;; 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 $3) (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 $2) | |
| ;; CHECK-NEXT: (struct.set $A 0 | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_5) | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_6) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK: (func $test_3 (type $2) | |
| ;; 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 $1 (func)) | |
| ;; CHECK: (type $2 (func (result i32))) | |
| ;; CHECK: (global $ctor-eval$global_9 (ref (exact $A)) (struct.new $A | |
| ;; CHECK-NEXT: (ref.null none) | |
| ;; CHECK-NEXT: (i32.const 42) | |
| ;; CHECK-NEXT: )) | |
| ;; CHECK: (global $ctor-eval$global_11 (ref (exact $A)) (struct.new $A | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_9) | |
| ;; CHECK-NEXT: (i32.const 1337) | |
| ;; CHECK-NEXT: )) | |
| ;; CHECK: (global $ctor-eval$global_10 (ref (exact $A)) (struct.new $A | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_11) | |
| ;; CHECK-NEXT: (i32.const 99999) | |
| ;; CHECK-NEXT: )) | |
| ;; CHECK: (global $a (mut (ref null $A)) (global.get $ctor-eval$global_9)) | |
| (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: (export "test" (func $test_3)) | |
| ;; CHECK: (export "keepalive" (func $keepalive)) | |
| ;; CHECK: (start $start) | |
| ;; CHECK: (func $keepalive (type $2) (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 $1) | |
| ;; CHECK-NEXT: (struct.set $A 0 | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_9) | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_10) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK: (func $test_3 (type $1) | |
| ;; 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 $3 (func)) | |
| ;; CHECK: (type $4 (func (result i32))) | |
| ;; CHECK: (global $ctor-eval$global_9 (ref (exact $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_9)) | |
| (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 2 | |
| (local.get $b) | |
| (local.get $a) | |
| ) | |
| ) | |
| ) | |
| (struct.set $A 0 | |
| (local.get $a) | |
| (global.get $c) | |
| ) | |
| ) | |
| ;; CHECK: (global $ctor-eval$global_11 (ref (exact $B)) (array.new_fixed $B 10 | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_9) | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_9) | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_9) | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_9) | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_9) | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_9) | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_9) | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_9) | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_9) | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_9) | |
| ;; CHECK-NEXT: )) | |
| ;; CHECK: (global $ctor-eval$global_10 (ref (exact $C)) (array.new_fixed $C 2 | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_11) | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_9) | |
| ;; CHECK-NEXT: )) | |
| ;; CHECK: (export "test" (func $test_3)) | |
| ;; CHECK: (export "keepalive" (func $keepalive)) | |
| ;; CHECK: (start $start) | |
| ;; CHECK: (func $keepalive (type $4) (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 $3) | |
| ;; CHECK-NEXT: (struct.set $A 0 | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_9) | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_10) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK: (func $test_3 (type $3) | |
| ;; 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 $3 (func)) | |
| ;; CHECK: (type $4 (func (result i32))) | |
| ;; CHECK: (global $ctor-eval$global_9 (ref (exact $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_9)) | |
| (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 2 | |
| (local.get $b) | |
| (local.get $a) | |
| ) | |
| ) | |
| ) | |
| (struct.set $A 0 | |
| (local.get $a) | |
| (global.get $c) | |
| ) | |
| ) | |
| ;; CHECK: (global $ctor-eval$global_11 (ref (exact $B)) (array.new_fixed $B 10 | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_9) | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_9) | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_9) | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_9) | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_9) | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_9) | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_9) | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_9) | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_9) | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_9) | |
| ;; CHECK-NEXT: )) | |
| ;; CHECK: (global $ctor-eval$global_10 (ref (exact $C)) (array.new_fixed $C 2 | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_11) | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_9) | |
| ;; CHECK-NEXT: )) | |
| ;; CHECK: (export "test" (func $test_3)) | |
| ;; CHECK: (export "keepalive" (func $keepalive)) | |
| ;; CHECK: (start $start) | |
| ;; CHECK: (func $keepalive (type $4) (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 $3) | |
| ;; CHECK-NEXT: (struct.set $A 0 | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_9) | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_10) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK: (func $test_3 (type $3) | |
| ;; 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 $2 (func)) | |
| ;; CHECK: (type $3 (func (result anyref))) | |
| ;; CHECK: (global $ctor-eval$global_11 (ref (exact $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 (exact $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 (exact $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_11)) | |
| (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 3 | |
| (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_13 (ref (exact $B)) (array.new_fixed $B 3 | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_14) | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_11) | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_15) | |
| ;; CHECK-NEXT: )) | |
| ;; CHECK: (global $ctor-eval$global_12 (ref (exact $B)) (array.new_fixed $B 0)) | |
| ;; CHECK: (global $ctor-eval$global_16 (ref (exact $B)) (array.new_fixed $B 0)) | |
| ;; CHECK: (export "test" (func $test_3)) | |
| ;; CHECK: (export "keepalive" (func $keepalive)) | |
| ;; CHECK: (start $start) | |
| ;; CHECK: (func $keepalive (type $3) (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 $2) | |
| ;; CHECK-NEXT: (struct.set $A 0 | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_11) | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_12) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK-NEXT: (struct.set $A 1 | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_11) | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_13) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK-NEXT: (struct.set $A 2 | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_11) | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_16) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK: (func $test_3 (type $2) | |
| ;; 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 $2 (func)) | |
| ;; CHECK: (type $3 (func (result anyref))) | |
| ;; CHECK: (global $ctor-eval$global_11 (ref (exact $B)) (array.new_fixed $B 3 | |
| ;; CHECK-NEXT: (ref.null none) | |
| ;; CHECK-NEXT: (ref.null none) | |
| ;; CHECK-NEXT: (ref.null none) | |
| ;; CHECK-NEXT: )) | |
| ;; CHECK: (global $ctor-eval$global_14 (ref (exact $B)) (array.new_fixed $B 0)) | |
| ;; CHECK: (global $ctor-eval$global_15 (ref (exact $B)) (array.new_fixed $B 0)) | |
| ;; CHECK: (global $ctor-eval$global_13 (ref (exact $A)) (struct.new $A | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_14) | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_11) | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_15) | |
| ;; CHECK-NEXT: )) | |
| ;; CHECK: (global $a (mut (ref null $A)) (global.get $ctor-eval$global_13)) | |
| (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 3 | |
| (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_12 (ref (exact $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_16 (ref (exact $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 $3) (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 $2) | |
| ;; CHECK-NEXT: (array.set $B | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_11) | |
| ;; CHECK-NEXT: (i32.const 0) | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_12) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK-NEXT: (array.set $B | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_11) | |
| ;; CHECK-NEXT: (i32.const 1) | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_13) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK-NEXT: (array.set $B | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_11) | |
| ;; CHECK-NEXT: (i32.const 2) | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global_16) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK: (func $test_3 (type $2) | |
| ;; CHECK-NEXT: (local $b (ref $B)) | |
| ;; CHECK-NEXT: (nop) | |
| ;; CHECK-NEXT: ) | |
| (module | |
| ;; The start function already exists here. We must *not* prepend to it: it gets | |
| ;; evalled away too (we execute it before the first ctor, and we should not | |
| ;; eval those contents twice). | |
| ;; CHECK: (type $A (struct (field (mut (ref null $A))) (field i32))) | |
| (type $A (struct (field (mut (ref null $A))) (field i32))) | |
| ;; CHECK: (type $1 (func)) | |
| ;; CHECK: (type $2 (func (result i32))) | |
| ;; CHECK: (global $ctor-eval$global_3 (ref (exact $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)) | |
| ;; CHECK: (global $b (mut (ref null $A)) (ref.null none)) | |
| (global $b (mut (ref null $A)) (ref.null $A)) | |
| (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: (export "test" (func $test_4)) | |
| ;; CHECK: (export "keepalive" (func $keepalive)) | |
| ;; CHECK: (start $start_3) | |
| ;; CHECK: (func $keepalive (type $2) (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) | |
| ) | |
| ) | |
| ) | |
| (func $start | |
| (global.set $b | |
| (global.get $a) | |
| ) | |
| ) | |
| ) | |
| ;; CHECK: (func $start_3 (type $1) | |
| ;; 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_4 (type $1) | |
| ;; 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 $1 (func)) | |
| ;; CHECK: (type $2 (func (param anyref))) | |
| ;; CHECK: (import "a" "b" (func $import (type $2) (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 (exact $A)) (struct.new $A | |
| ;; CHECK-NEXT: (ref.null none) | |
| ;; CHECK-NEXT: )) | |
| ;; CHECK: (export "test" (func $test_3)) | |
| ;; CHECK: (start $start) | |
| ;; CHECK: (func $start (type $1) | |
| ;; 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 $1) | |
| ;; CHECK-NEXT: (call $import | |
| ;; CHECK-NEXT: (global.get $ctor-eval$global) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK-NEXT: ) | |
| (module | |
| ;; A circular reference using a non-nullable (but mutable) field. Unlike cases | |
| ;; above, we cannot break up such cycles, and must give up. We should at least | |
| ;; not error. | |
| ;; CHECK: (type $array (array i8)) | |
| (type $array (array i8)) | |
| ;; CHECK: (type $struct (struct (field (mut (ref any))))) | |
| (type $struct (struct (field (mut (ref any))))) | |
| ;; CHECK: (type $2 (func)) | |
| ;; CHECK: (global $global (mut i32) (i32.const 42)) | |
| (global $global (mut i32) (i32.const 42)) | |
| ;; CHECK: (export "test" (func $test)) | |
| ;; CHECK: (func $test (type $2) | |
| ;; CHECK-NEXT: (local $temp (ref $struct)) | |
| ;; CHECK-NEXT: (global.set $global | |
| ;; CHECK-NEXT: (i32.const 1337) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK-NEXT: (local.set $temp | |
| ;; CHECK-NEXT: (struct.new $struct | |
| ;; CHECK-NEXT: (array.new_fixed $array 0) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK-NEXT: (struct.set $struct 0 | |
| ;; CHECK-NEXT: (local.get $temp) | |
| ;; CHECK-NEXT: (local.get $temp) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK-NEXT: ) | |
| (func $test (export "test") | |
| (local $temp (ref $struct)) | |
| ;; Set the global. This will not get written out, as we will cancel all our | |
| ;; work when we hit the cycle below. (TODO: improve that) | |
| (global.set $global | |
| (i32.const 1337) | |
| ) | |
| ;; Start with the struct referring to an array. | |
| (local.set $temp | |
| (struct.new $struct | |
| (array.new_fixed $array 0) | |
| ) | |
| ) | |
| ;; Make the struct refer to itself, circularly. | |
| (struct.set $struct 0 | |
| (local.get $temp) | |
| (local.get $temp) | |
| ) | |
| ) | |
| ) | |