diff options
Diffstat (limited to 'test/lit/passes/precompute-partial.wast')
-rw-r--r-- | test/lit/passes/precompute-partial.wast | 788 |
1 files changed, 788 insertions, 0 deletions
diff --git a/test/lit/passes/precompute-partial.wast b/test/lit/passes/precompute-partial.wast new file mode 100644 index 000000000..86a55f56a --- /dev/null +++ b/test/lit/passes/precompute-partial.wast @@ -0,0 +1,788 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited. + +;; RUN: foreach %s %t wasm-opt --precompute --optimize-level=2 -all -S -o - | filecheck %s + +(module + ;; CHECK: (func $simple-1 (type $0) (param $param i32) (result i32) + ;; CHECK-NEXT: (select + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (local.get $param) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $simple-1 (param $param i32) (result i32) + ;; The eqz can be applied to the select arms. + (i32.eqz + (select + (i32.const 42) + (i32.const 1337) + (local.get $param) + ) + ) + ) + + ;; CHECK: (func $simple-2 (type $0) (param $param i32) (result i32) + ;; CHECK-NEXT: (select + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (local.get $param) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $simple-2 (param $param i32) (result i32) + (i32.eqz + (select + (i32.const 0) + (i32.const 10) + (local.get $param) + ) + ) + ) + + ;; CHECK: (func $simple-3 (type $0) (param $param i32) (result i32) + ;; CHECK-NEXT: (select + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: (local.get $param) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $simple-3 (param $param i32) (result i32) + (i32.eqz + (select + (i32.const 20) + (i32.const 0) + (local.get $param) + ) + ) + ) + + ;; CHECK: (func $simple-4 (type $0) (param $param i32) (result i32) + ;; CHECK-NEXT: (select + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: (local.get $param) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $simple-4 (param $param i32) (result i32) + (i32.eqz + (select + (i32.const 0) + (i32.const 0) + (local.get $param) + ) + ) + ) + + ;; CHECK: (func $not-left (type $0) (param $param i32) (result i32) + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (select + ;; CHECK-NEXT: (local.get $param) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (local.get $param) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $not-left (param $param i32) (result i32) + (i32.eqz + (select + (local.get $param) ;; this cannot be precomputed, so we do nothing + (i32.const 0) + (local.get $param) + ) + ) + ) + + ;; CHECK: (func $not-right (type $0) (param $param i32) (result i32) + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (select + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (local.get $param) + ;; CHECK-NEXT: (local.get $param) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $not-right (param $param i32) (result i32) + (i32.eqz + (select + (i32.const 0) + (local.get $param) ;; this cannot be precomputed, so we do nothing + (local.get $param) + ) + ) + ) + + ;; CHECK: (func $not-neither (type $0) (param $param i32) (result i32) + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (select + ;; CHECK-NEXT: (local.get $param) + ;; CHECK-NEXT: (local.get $param) + ;; CHECK-NEXT: (local.get $param) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $not-neither (param $param i32) (result i32) + (i32.eqz + (select + (local.get $param) ;; these cannot be precomputed, + (local.get $param) ;; so we do nothing + (local.get $param) + ) + ) + ) + + ;; CHECK: (func $binary (type $0) (param $param i32) (result i32) + ;; CHECK-NEXT: (select + ;; CHECK-NEXT: (i32.const 11) + ;; CHECK-NEXT: (i32.const 21) + ;; CHECK-NEXT: (local.get $param) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $binary (param $param i32) (result i32) + (i32.add + (select + (i32.const 10) + (i32.const 20) + (local.get $param) + ) + (i32.const 1) + ) + ) + + ;; CHECK: (func $binary-2 (type $0) (param $param i32) (result i32) + ;; CHECK-NEXT: (select + ;; CHECK-NEXT: (i32.const 11) + ;; CHECK-NEXT: (i32.const 21) + ;; CHECK-NEXT: (local.get $param) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $binary-2 (param $param i32) (result i32) + ;; As above but with the select in the other arm. + (i32.add + (i32.const 1) + (select + (i32.const 10) + (i32.const 20) + (local.get $param) + ) + ) + ) + + ;; CHECK: (func $binary-3 (type $0) (param $param i32) (result i32) + ;; CHECK-NEXT: (i32.add + ;; CHECK-NEXT: (select + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: (i32.const 20) + ;; CHECK-NEXT: (local.get $param) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (select + ;; CHECK-NEXT: (i32.const 30) + ;; CHECK-NEXT: (i32.const 40) + ;; CHECK-NEXT: (local.get $param) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $binary-3 (param $param i32) (result i32) + ;; Two selects. We do not optimize here (but in theory could, as the + ;; condition is identical - other passes should merge that). + (i32.add + (select + (i32.const 10) + (i32.const 20) + (local.get $param) + ) + (select + (i32.const 30) + (i32.const 40) + (local.get $param) + ) + ) + ) + + ;; CHECK: (func $unreachable (type $0) (param $param i32) (result i32) + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (select + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $unreachable (param $param i32) (result i32) + ;; We should ignore unreachable code like this. We would need to make sure + ;; to emit the proper type on the outside, and it's simpler to just defer + ;; this to DCE. + (i32.eqz + (select + (i32.const 0) + (i32.const 0) + (unreachable) + ) + ) + ) + + ;; CHECK: (func $tuple (type $1) (param $param i32) (result i32 i32) + ;; CHECK-NEXT: (tuple.make 2 + ;; CHECK-NEXT: (select + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: (local.get $param) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $tuple (param $param i32) (result i32 i32) + ;; We should ignore tuples, as select outputs cannot be tuples. + (tuple.make 2 + (select + (i32.const 0) + (i32.const 1) + (local.get $param) + ) + (i32.const 2) + ) + ) + + ;; CHECK: (func $control-flow (type $0) (param $param i32) (result i32) + ;; CHECK-NEXT: (block $target (result i32) + ;; CHECK-NEXT: (select + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: (br_if $target + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: (local.get $param) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $control-flow (param $param i32) (result i32) + ;; We ignore control flow structures to avoid issues with removing them (as + ;; the condition might refer to them, as in this testcase). + (block $target (result i32) + (select + (i32.const 0) + (i32.const 1) + ;; If we precomputed the block into the select arms, this br_if + ;; would have nowhere to go. + (br_if $target + (i32.const 1) + (local.get $param) + ) + ) + ) + ) + + ;; CHECK: (func $break (type $0) (param $x i32) (result i32) + ;; CHECK-NEXT: (block $label (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (br_if $label + ;; CHECK-NEXT: (select + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 3) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $break (param $x i32) (result i32) + ;; We should change nothing here: we do not partially precompute breaks yet + ;; TODO + (block $label (result i32) + (drop + (br_if $label + (select + (i32.const 0) + (i32.const 1) + (local.get $x) + ) + (i32.const 2) + ) + ) + (i32.const 3) + ) + ) + + ;; CHECK: (func $toplevel (type $0) (param $param i32) (result i32) + ;; CHECK-NEXT: (select + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: (local.get $param) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $toplevel (param $param i32) (result i32) + ;; There is nothing to do for a select with no parent, but do not error at + ;; least. + (select + (i32.const 0) + (i32.const 10) + (local.get $param) + ) + ) + + ;; CHECK: (func $toplevel-nested (type $0) (param $param i32) (result i32) + ;; CHECK-NEXT: (select + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (local.get $param) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $toplevel-nested (param $param i32) (result i32) + ;; The inner select can be optimized: here we apply the outer select onto + ;; the inner one (the same as any other thing we apply into the select arms, + ;; but it happens to be a select itself). + (select + (i32.const 0) + (i32.const 10) + (select + (i32.const 0) + (i32.const 20) + (local.get $param) + ) + ) + ) + + ;; CHECK: (func $toplevel-nested-flip (type $0) (param $param i32) (result i32) + ;; CHECK-NEXT: (select + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: (local.get $param) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $toplevel-nested-flip (param $param i32) (result i32) + ;; As above but with inner select arms flipped. That flips the result. + (select + (i32.const 0) + (i32.const 10) + (select + (i32.const 20) + (i32.const 0) + (local.get $param) + ) + ) + ) + + ;; CHECK: (func $toplevel-nested-indirect (type $0) (param $param i32) (result i32) + ;; CHECK-NEXT: (select + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: (local.get $param) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $toplevel-nested-indirect (param $param i32) (result i32) + ;; As above, with an instruction in the middle. Again, we can optimize. + (select + (i32.const 0) + (i32.const 10) + (i32.eqz + (select + (i32.const 0) + (i32.const 20) + (local.get $param) + ) + ) + ) + ) + + ;; CHECK: (func $nested (type $0) (param $param i32) (result i32) + ;; CHECK-NEXT: (select + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (local.get $param) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $nested (param $param i32) (result i32) + ;; As above, with an outer eqz as well. Now both the outer and inner selects + ;; can be optimized, and after the inner one is, it can be optimized with + ;; the outer one as well, leaving a single select and no eqz. + (i32.eqz + (select + (i32.const 0) + (i32.const 10) + (i32.eqz + (select + (i32.const 0) + (i32.const 20) + (local.get $param) + ) + ) + ) + ) + ) + + ;; CHECK: (func $nested-arms (type $0) (param $param i32) (result i32) + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (select + ;; CHECK-NEXT: (select + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: (i32.const 20) + ;; CHECK-NEXT: (local.get $param) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (select + ;; CHECK-NEXT: (i32.const 30) + ;; CHECK-NEXT: (i32.const 40) + ;; CHECK-NEXT: (local.get $param) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (select + ;; CHECK-NEXT: (i32.const 50) + ;; CHECK-NEXT: (i32.const 60) + ;; CHECK-NEXT: (local.get $param) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $nested-arms (param $param i32) (result i32) + ;; We do nothing for selects nested directly in select arms, but do not + ;; error at least. Note that we could apply the eqz two levels deep TODO + (i32.eqz + (select + (select + (i32.const 10) + (i32.const 20) + (local.get $param) + ) + (select + (i32.const 30) + (i32.const 40) + (local.get $param) + ) + (select + (i32.const 50) + (i32.const 60) + (local.get $param) + ) + ) + ) + ) + + ;; CHECK: (func $nested-indirect-arms (type $0) (param $param i32) (result i32) + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (select + ;; CHECK-NEXT: (select + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (local.get $param) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (select + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: (local.get $param) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (select + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (local.get $param) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $nested-indirect-arms (param $param i32) (result i32) + ;; As above, but now there is something in the middle, that can be optimized + ;; into those selects. + (i32.eqz + (select + (i32.eqz + (select + (i32.const 0) + (i32.const 10) + (local.get $param) + ) + ) + (i32.eqz + (select + (i32.const 20) + (i32.const 0) + (local.get $param) + ) + ) + (i32.eqz + (select + (i32.const 0) + (i32.const 30) + (local.get $param) + ) + ) + ) + ) + ) +) + +;; References. +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $vtable (sub (struct (field funcref)))) + (type $vtable (sub (struct funcref))) + + ;; CHECK: (type $specific-func (sub (func (result i32)))) + (type $specific-func (sub (func (result i32)))) + + ;; CHECK: (type $specific-func.sub (sub $specific-func (func (result i32)))) + (type $specific-func.sub (sub $specific-func (func (result i32)))) + + ;; CHECK: (type $vtable.sub (sub $vtable (struct (field (ref $specific-func))))) + (type $vtable.sub (sub $vtable (struct (field (ref $specific-func))))) + ) + + ;; CHECK: (global $A$vtable (ref $vtable) (struct.new $vtable + ;; CHECK-NEXT: (ref.func $A$func) + ;; CHECK-NEXT: )) + (global $A$vtable (ref $vtable) (struct.new $vtable + (ref.func $A$func) + )) + + ;; CHECK: (global $B$vtable (ref $vtable) (struct.new $vtable + ;; CHECK-NEXT: (ref.func $B$func) + ;; CHECK-NEXT: )) + (global $B$vtable (ref $vtable) (struct.new $vtable + (ref.func $B$func) + )) + + ;; CHECK: (func $test-expanded (type $0) (param $x i32) (result funcref) + ;; CHECK-NEXT: (select (result (ref $specific-func)) + ;; CHECK-NEXT: (ref.func $A$func) + ;; CHECK-NEXT: (ref.func $B$func) + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $test-expanded (param $x i32) (result funcref) + ;; We can apply the struct.get to the select arms: As the globals are all + ;; immutable, we can read the function references from them, and emit a + ;; select on those values, saving the struct.get operation entirely. + ;; + ;; Note that this test also checks updating the type of the select, which + ;; initially returned a struct, and afterwards returns a func. + (struct.get $vtable 0 + (select + (global.get $A$vtable) + (global.get $B$vtable) + (local.get $x) + ) + ) + ) + + ;; CHECK: (func $test-subtyping (type $0) (param $x i32) (result funcref) + ;; CHECK-NEXT: (select (result (ref $specific-func)) + ;; CHECK-NEXT: (ref.func $A$func) + ;; CHECK-NEXT: (ref.func $B$func) + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $test-subtyping (param $x i32) (result funcref) + ;; As above, but now we have struct.news directly in the arms, and one is + ;; of a subtype of the final result (which should not prevent optimization). + (struct.get $vtable.sub 0 + (select + (struct.new $vtable.sub + (ref.func $A$func) + ) + (struct.new $vtable.sub + (ref.func $B$func) ;; this function is of a subtype of the field type + ) + (local.get $x) + ) + ) + ) + + ;; CHECK: (func $test-expanded-twice (type $5) (param $x i32) (result i32) + ;; CHECK-NEXT: (select + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $test-expanded-twice (param $x i32) (result i32) + ;; As $test-expanded, but we have two operations that can be applied. Both + ;; references are non-null, so the select arms will become 0. + (ref.is_null + (struct.get $vtable 0 + (select + (global.get $A$vtable) + (global.get $B$vtable) + (local.get $x) + ) + ) + ) + ) + + ;; CHECK: (func $test-expanded-twice-stop (type $6) (param $x i32) + ;; CHECK-NEXT: (call $send-i32 + ;; CHECK-NEXT: (select + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $test-expanded-twice-stop (param $x i32) + ;; As $test-expanded-twice, but we stop after two expansions when we fail on + ;; the call. + (call $send-i32 + (ref.is_null + (struct.get $vtable 0 + (select + (global.get $A$vtable) + (global.get $B$vtable) + (local.get $x) + ) + ) + ) + ) + ) + + ;; CHECK: (func $send-i32 (type $6) (param $x i32) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + (func $send-i32 (param $x i32) + ;; Helper for above. + ) + + ;; CHECK: (func $binary (type $5) (param $param i32) (result i32) + ;; CHECK-NEXT: (select + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (local.get $param) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $binary (param $param i32) (result i32) + ;; This is a common pattern in J2Wasm output. Note that this is optimized + ;; because immutable globals can be compared at compile time. + (ref.eq + (select + (global.get $A$vtable) + (global.get $B$vtable) + (local.get $param) + ) + (global.get $A$vtable) + ) + ) + + ;; CHECK: (func $test-trap (type $0) (param $x i32) (result funcref) + ;; CHECK-NEXT: (struct.get $vtable 0 + ;; CHECK-NEXT: (select (result (ref null $vtable)) + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: (global.get $B$vtable) + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $test-trap (param $x i32) (result funcref) + ;; One arm has a null, which makes the struct.get trap, so we ignore this + ;; for now. TODO: handle traps + (struct.get $vtable 0 + (select + (ref.null $vtable) + (global.get $B$vtable) + (local.get $x) + ) + ) + ) + + ;; CHECK: (func $A$func (type $specific-func) (result i32) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + (func $A$func (type $specific-func) (result i32) + ;; Helper for above. + (i32.const 1) + ) + + ;; CHECK: (func $B$func (type $specific-func.sub) (result i32) + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: ) + (func $B$func (type $specific-func.sub) (result i32) + ;; Helper for above. + (i32.const 2) + ) +) + +;; References with nested globals. +(module + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $vtable (sub (struct (field funcref)))) + (type $vtable (sub (struct funcref))) + + ;; CHECK: (type $itable (sub (struct (field (ref $vtable))))) + (type $itable (sub (struct (ref $vtable)))) + ) + + ;; CHECK: (global $A$itable (ref $itable) (struct.new $itable + ;; CHECK-NEXT: (struct.new $vtable + ;; CHECK-NEXT: (ref.func $A$func) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: )) + (global $A$itable (ref $itable) (struct.new $itable + (struct.new $vtable + (ref.func $A$func) + ) + )) + + ;; CHECK: (global $B$itable (ref $itable) (struct.new $itable + ;; CHECK-NEXT: (struct.new $vtable + ;; CHECK-NEXT: (ref.func $B$func) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: )) + (global $B$itable (ref $itable) (struct.new $itable + (struct.new $vtable + (ref.func $B$func) + ) + )) + + ;; CHECK: (func $test-expanded (type $3) (param $x i32) (result funcref) + ;; CHECK-NEXT: (select (result (ref $2)) + ;; CHECK-NEXT: (ref.func $A$func) + ;; CHECK-NEXT: (ref.func $B$func) + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $test-expanded (param $x i32) (result funcref) + ;; Nesting in the global declarations means we cannot precompute the inner + ;; struct.get by itself (as that would return the inner struct.new), but + ;; when we do the outer struct.get, it all comes together. This is a common + ;; pattern in J2Wasm output. + (struct.get $vtable 0 + (struct.get $itable 0 + (select + (global.get $A$itable) + (global.get $B$itable) + (local.get $x) + ) + ) + ) + ) + + ;; CHECK: (func $test-expanded-almost (type $4) (param $x i32) (result anyref) + ;; CHECK-NEXT: (struct.get $itable 0 + ;; CHECK-NEXT: (select (result (ref $itable)) + ;; CHECK-NEXT: (global.get $A$itable) + ;; CHECK-NEXT: (global.get $B$itable) + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $test-expanded-almost (param $x i32) (result anyref) + ;; As above, but without the outer struct.get. We get "stuck" with the + ;; inner part of the global, as explained there, and fail to optimize here. + (struct.get $itable 0 + (select + (global.get $A$itable) + (global.get $B$itable) + (local.get $x) + ) + ) + ) + + ;; CHECK: (func $A$func (type $2) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + (func $A$func + ;; Helper for above. + ) + + ;; CHECK: (func $B$func (type $2) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + (func $B$func + ;; Helper for above. + ) +) |