| ;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. | |
| ;; As in monomorphize-types.wast, test in both "always" mode, which always | |
| ;; monomorphizes, and in "careful" mode which does it only when it appears to | |
| ;; actually help, and use a minimum benefit of 0 to make it easy to write | |
| ;; small testcases. | |
| ;; RUN: foreach %s %t wasm-opt --monomorphize-always -all -S -o - | filecheck %s --check-prefix ALWAYS | |
| ;; RUN: foreach %s %t wasm-opt --monomorphize --pass-arg=monomorphize-min-benefit@0 -all -S -o - | filecheck %s --check-prefix CAREFUL | |
| (module | |
| ;; ALWAYS: (type $0 (func (param i32) (result i32))) | |
| ;; ALWAYS: (type $1 (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 anyref funcref i32 f64 i32 anyref anyref))) | |
| ;; ALWAYS: (type $2 (func (param i32 i32 i32 i32 i32 i32))) | |
| ;; ALWAYS: (type $struct (struct)) | |
| (type $struct (struct)) | |
| (memory 10 20) | |
| ;; ALWAYS: (global $imm i32 (i32.const 10)) | |
| ;; CAREFUL: (type $0 (func (param i32) (result i32))) | |
| ;; CAREFUL: (type $1 (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 anyref funcref i32 f64 i32 anyref anyref))) | |
| ;; CAREFUL: (type $2 (func (param i32 i32 i32 i32 i32 i32))) | |
| ;; CAREFUL: (global $imm i32 (i32.const 10)) | |
| (global $imm i32 (i32.const 10)) | |
| ;; ALWAYS: (global $mut (mut i32) (i32.const 20)) | |
| ;; CAREFUL: (global $mut (mut i32) (i32.const 20)) | |
| (global $mut (mut i32) (i32.const 20)) | |
| ;; ALWAYS: (memory $0 10 20) | |
| ;; ALWAYS: (elem declare func $target) | |
| ;; ALWAYS: (func $caller (type $0) (param $x i32) (result i32) | |
| ;; ALWAYS-NEXT: (block $out (result i32) | |
| ;; ALWAYS-NEXT: (call $target_2 | |
| ;; ALWAYS-NEXT: (br_if $out | |
| ;; ALWAYS-NEXT: (i32.const 12) | |
| ;; ALWAYS-NEXT: (i32.const 13) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (block (result i32) | |
| ;; ALWAYS-NEXT: (i32.const 0) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (if (result i32) | |
| ;; ALWAYS-NEXT: (i32.const 1) | |
| ;; ALWAYS-NEXT: (then | |
| ;; ALWAYS-NEXT: (i32.const 2) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (else | |
| ;; ALWAYS-NEXT: (i32.const 3) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (call $caller | |
| ;; ALWAYS-NEXT: (i32.const 4) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (local.get $x) | |
| ;; ALWAYS-NEXT: (local.tee $x | |
| ;; ALWAYS-NEXT: (i32.const 5) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (i32.const 14) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; CAREFUL: (memory $0 10 20) | |
| ;; CAREFUL: (func $caller (type $0) (param $x i32) (result i32) | |
| ;; CAREFUL-NEXT: (block $out (result i32) | |
| ;; CAREFUL-NEXT: (call $target_2 | |
| ;; CAREFUL-NEXT: (br_if $out | |
| ;; CAREFUL-NEXT: (i32.const 12) | |
| ;; CAREFUL-NEXT: (i32.const 13) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: (block (result i32) | |
| ;; CAREFUL-NEXT: (i32.const 0) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: (if (result i32) | |
| ;; CAREFUL-NEXT: (i32.const 1) | |
| ;; CAREFUL-NEXT: (then | |
| ;; CAREFUL-NEXT: (i32.const 2) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: (else | |
| ;; CAREFUL-NEXT: (i32.const 3) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: (call $caller | |
| ;; CAREFUL-NEXT: (i32.const 4) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: (local.get $x) | |
| ;; CAREFUL-NEXT: (local.tee $x | |
| ;; CAREFUL-NEXT: (i32.const 5) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: (i32.const 14) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: ) | |
| (func $caller (param $x i32) (result i32) | |
| ;; Show the variety of things we can and cannot move into the call context. | |
| ;; | |
| ;; Note that in CAREFUL mode we only optimize here if we properly take into | |
| ;; account the call context in the cost. The function we are calling has | |
| ;; an empty body, so the monomorphized function will contain basically just | |
| ;; the moved code from the call context. If we didn't measure that in the | |
| ;; cost before monomorphization then it would seem like we went from cost 0 | |
| ;; (empty body) to the cost of the operations that remain after we | |
| ;; optimize (which is the i32.load, which might trap so it remains). But if | |
| ;; we take into account the context, then monomorphization definitely helps | |
| ;; as it removes a bunch of constants. | |
| (block $out (result i32) | |
| (call $target | |
| ;; We can't move control flow. | |
| (br_if $out | |
| (i32.const 12) | |
| (i32.const 13) | |
| ) | |
| ;; We can't move control flow structures. | |
| (block (result i32) | |
| (i32.const 0) | |
| ) | |
| (if (result i32) | |
| (i32.const 1) | |
| (then | |
| (i32.const 2) | |
| ) | |
| (else | |
| (i32.const 3) | |
| ) | |
| ) | |
| ;; We don't move calls. | |
| (call $caller | |
| (i32.const 4) | |
| ) | |
| ;; We can't move local operations. | |
| (local.get $x) | |
| (local.tee $x | |
| (i32.const 5) | |
| ) | |
| ;; We can move globals, even mutable. | |
| (global.get $imm) | |
| (global.get $mut) | |
| ;; We can move loads and other options that might trap. | |
| (i32.load | |
| (i32.const 6) | |
| ) | |
| ;; We can move constants. | |
| (i32.const 7) | |
| (ref.null any) | |
| (ref.func $target) | |
| ;; We can move math operations. | |
| (i32.eqz | |
| (i32.const 8) | |
| ) | |
| (f64.add | |
| (f64.const 2.71828) | |
| (f64.const 3.14159) | |
| ) | |
| ;; We can move selects. | |
| (select | |
| (i32.const 9) | |
| (i32.const 10) | |
| (i32.const 11) | |
| ) | |
| ;; We can move GC operations. | |
| (ref.cast (ref null none) | |
| (ref.null none) | |
| ) | |
| (struct.new $struct) | |
| ) | |
| (i32.const 14) | |
| ) | |
| ) | |
| ;; ALWAYS: (func $target (type $1) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (param $4 i32) (param $5 i32) (param $6 i32) (param $7 i32) (param $8 i32) (param $9 i32) (param $10 anyref) (param $11 funcref) (param $12 i32) (param $13 f64) (param $14 i32) (param $15 anyref) (param $16 anyref) | |
| ;; ALWAYS-NEXT: ) | |
| ;; CAREFUL: (func $target (type $1) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (param $4 i32) (param $5 i32) (param $6 i32) (param $7 i32) (param $8 i32) (param $9 i32) (param $10 anyref) (param $11 funcref) (param $12 i32) (param $13 f64) (param $14 i32) (param $15 anyref) (param $16 anyref) | |
| ;; CAREFUL-NEXT: (nop) | |
| ;; CAREFUL-NEXT: ) | |
| (func $target | |
| (param i32) | |
| (param i32) | |
| (param i32) | |
| (param i32) | |
| (param i32) | |
| (param i32) | |
| (param i32) | |
| (param i32) | |
| (param i32) | |
| (param i32) | |
| (param anyref) | |
| (param funcref) | |
| (param i32) | |
| (param f64) | |
| (param i32) | |
| (param anyref) | |
| (param anyref) | |
| ) | |
| ) | |
| ;; ALWAYS: (func $target_2 (type $2) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (param $4 i32) (param $5 i32) | |
| ;; ALWAYS-NEXT: (local $6 i32) | |
| ;; ALWAYS-NEXT: (local $7 i32) | |
| ;; ALWAYS-NEXT: (local $8 i32) | |
| ;; ALWAYS-NEXT: (local $9 i32) | |
| ;; ALWAYS-NEXT: (local $10 i32) | |
| ;; ALWAYS-NEXT: (local $11 i32) | |
| ;; ALWAYS-NEXT: (local $12 i32) | |
| ;; ALWAYS-NEXT: (local $13 i32) | |
| ;; ALWAYS-NEXT: (local $14 i32) | |
| ;; ALWAYS-NEXT: (local $15 i32) | |
| ;; ALWAYS-NEXT: (local $16 anyref) | |
| ;; ALWAYS-NEXT: (local $17 funcref) | |
| ;; ALWAYS-NEXT: (local $18 i32) | |
| ;; ALWAYS-NEXT: (local $19 f64) | |
| ;; ALWAYS-NEXT: (local $20 i32) | |
| ;; ALWAYS-NEXT: (local $21 anyref) | |
| ;; ALWAYS-NEXT: (local $22 anyref) | |
| ;; ALWAYS-NEXT: (local.set $6 | |
| ;; ALWAYS-NEXT: (local.get $0) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (local.set $7 | |
| ;; ALWAYS-NEXT: (local.get $1) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (local.set $8 | |
| ;; ALWAYS-NEXT: (local.get $2) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (local.set $9 | |
| ;; ALWAYS-NEXT: (local.get $3) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (local.set $10 | |
| ;; ALWAYS-NEXT: (local.get $4) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (local.set $11 | |
| ;; ALWAYS-NEXT: (local.get $5) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (local.set $12 | |
| ;; ALWAYS-NEXT: (global.get $imm) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (local.set $13 | |
| ;; ALWAYS-NEXT: (global.get $mut) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (local.set $14 | |
| ;; ALWAYS-NEXT: (i32.load | |
| ;; ALWAYS-NEXT: (i32.const 6) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (local.set $15 | |
| ;; ALWAYS-NEXT: (i32.const 7) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (local.set $16 | |
| ;; ALWAYS-NEXT: (ref.null none) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (local.set $17 | |
| ;; ALWAYS-NEXT: (ref.func $target) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (local.set $18 | |
| ;; ALWAYS-NEXT: (i32.eqz | |
| ;; ALWAYS-NEXT: (i32.const 8) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (local.set $19 | |
| ;; ALWAYS-NEXT: (f64.add | |
| ;; ALWAYS-NEXT: (f64.const 2.71828) | |
| ;; ALWAYS-NEXT: (f64.const 3.14159) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (local.set $20 | |
| ;; ALWAYS-NEXT: (select | |
| ;; ALWAYS-NEXT: (i32.const 9) | |
| ;; ALWAYS-NEXT: (i32.const 10) | |
| ;; ALWAYS-NEXT: (i32.const 11) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (local.set $21 | |
| ;; ALWAYS-NEXT: (ref.cast nullref | |
| ;; ALWAYS-NEXT: (ref.null none) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (local.set $22 | |
| ;; ALWAYS-NEXT: (struct.new_default $struct) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (block | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; CAREFUL: (func $target_2 (type $2) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (param $4 i32) (param $5 i32) | |
| ;; CAREFUL-NEXT: (drop | |
| ;; CAREFUL-NEXT: (i32.load | |
| ;; CAREFUL-NEXT: (i32.const 6) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: ) | |
| (module | |
| ;; ALWAYS: (type $0 (func (param i32) (result i32))) | |
| ;; ALWAYS: (type $1 (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 anyref funcref i32 f64 i32 anyref anyref) (result i32))) | |
| ;; ALWAYS: (type $2 (func (param i32 i32 i32 i32 i32 i32))) | |
| ;; ALWAYS: (type $struct (struct)) | |
| (type $struct (struct)) | |
| (memory 10 20) | |
| ;; ALWAYS: (global $imm i32 (i32.const 10)) | |
| ;; CAREFUL: (type $0 (func (param i32) (result i32))) | |
| ;; CAREFUL: (type $1 (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 anyref funcref i32 f64 i32 anyref anyref) (result i32))) | |
| ;; CAREFUL: (type $2 (func (param i32 i32 i32 i32 i32 i32))) | |
| ;; CAREFUL: (global $imm i32 (i32.const 10)) | |
| (global $imm i32 (i32.const 10)) | |
| ;; ALWAYS: (global $mut (mut i32) (i32.const 20)) | |
| ;; CAREFUL: (global $mut (mut i32) (i32.const 20)) | |
| (global $mut (mut i32) (i32.const 20)) | |
| ;; ALWAYS: (memory $0 10 20) | |
| ;; ALWAYS: (elem declare func $target) | |
| ;; ALWAYS: (func $caller (type $0) (param $x i32) (result i32) | |
| ;; ALWAYS-NEXT: (block $out (result i32) | |
| ;; ALWAYS-NEXT: (call $target_2 | |
| ;; ALWAYS-NEXT: (br_if $out | |
| ;; ALWAYS-NEXT: (i32.const 12) | |
| ;; ALWAYS-NEXT: (i32.const 13) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (block (result i32) | |
| ;; ALWAYS-NEXT: (i32.const 0) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (if (result i32) | |
| ;; ALWAYS-NEXT: (i32.const 1) | |
| ;; ALWAYS-NEXT: (then | |
| ;; ALWAYS-NEXT: (i32.const 2) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (else | |
| ;; ALWAYS-NEXT: (i32.const 3) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (call $caller | |
| ;; ALWAYS-NEXT: (i32.const 4) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (local.get $x) | |
| ;; ALWAYS-NEXT: (local.tee $x | |
| ;; ALWAYS-NEXT: (i32.const 5) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (i32.const 14) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; CAREFUL: (memory $0 10 20) | |
| ;; CAREFUL: (func $caller (type $0) (param $x i32) (result i32) | |
| ;; CAREFUL-NEXT: (block $out (result i32) | |
| ;; CAREFUL-NEXT: (call $target_2 | |
| ;; CAREFUL-NEXT: (br_if $out | |
| ;; CAREFUL-NEXT: (i32.const 12) | |
| ;; CAREFUL-NEXT: (i32.const 13) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: (block (result i32) | |
| ;; CAREFUL-NEXT: (i32.const 0) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: (if (result i32) | |
| ;; CAREFUL-NEXT: (i32.const 1) | |
| ;; CAREFUL-NEXT: (then | |
| ;; CAREFUL-NEXT: (i32.const 2) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: (else | |
| ;; CAREFUL-NEXT: (i32.const 3) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: (call $caller | |
| ;; CAREFUL-NEXT: (i32.const 4) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: (local.get $x) | |
| ;; CAREFUL-NEXT: (local.tee $x | |
| ;; CAREFUL-NEXT: (i32.const 5) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: (i32.const 14) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: ) | |
| (func $caller (param $x i32) (result i32) | |
| ;; As above, but now the call is dropped, so the context can include the | |
| ;; drop. | |
| (block $out (result i32) | |
| (drop | |
| (call $target | |
| (br_if $out | |
| (i32.const 12) | |
| (i32.const 13) | |
| ) | |
| (block (result i32) | |
| (i32.const 0) | |
| ) | |
| (if (result i32) | |
| (i32.const 1) | |
| (then | |
| (i32.const 2) | |
| ) | |
| (else | |
| (i32.const 3) | |
| ) | |
| ) | |
| (call $caller | |
| (i32.const 4) | |
| ) | |
| (local.get $x) | |
| (local.tee $x | |
| (i32.const 5) | |
| ) | |
| (global.get $imm) | |
| (global.get $mut) | |
| (i32.load | |
| (i32.const 6) | |
| ) | |
| (i32.const 7) | |
| (ref.null any) | |
| (ref.func $target) | |
| (i32.eqz | |
| (i32.const 8) | |
| ) | |
| (f64.add | |
| (f64.const 2.71828) | |
| (f64.const 3.14159) | |
| ) | |
| (select | |
| (i32.const 9) | |
| (i32.const 10) | |
| (i32.const 11) | |
| ) | |
| (ref.cast (ref null none) | |
| (ref.null none) | |
| ) | |
| (struct.new $struct) | |
| ) | |
| ) | |
| (i32.const 14) | |
| ) | |
| ) | |
| ;; ALWAYS: (func $target (type $1) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (param $4 i32) (param $5 i32) (param $6 i32) (param $7 i32) (param $8 i32) (param $9 i32) (param $10 anyref) (param $11 funcref) (param $12 i32) (param $13 f64) (param $14 i32) (param $15 anyref) (param $16 anyref) (result i32) | |
| ;; ALWAYS-NEXT: (local.get $7) | |
| ;; ALWAYS-NEXT: ) | |
| ;; CAREFUL: (func $target (type $1) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (param $4 i32) (param $5 i32) (param $6 i32) (param $7 i32) (param $8 i32) (param $9 i32) (param $10 anyref) (param $11 funcref) (param $12 i32) (param $13 f64) (param $14 i32) (param $15 anyref) (param $16 anyref) (result i32) | |
| ;; CAREFUL-NEXT: (local.get $7) | |
| ;; CAREFUL-NEXT: ) | |
| (func $target | |
| (param i32) | |
| (param i32) | |
| (param i32) | |
| (param i32) | |
| (param i32) | |
| (param i32) | |
| (param i32) | |
| (param i32) | |
| (param i32) | |
| (param i32) | |
| (param anyref) | |
| (param funcref) | |
| (param i32) | |
| (param f64) | |
| (param i32) | |
| (param anyref) | |
| (param anyref) | |
| (result i32) | |
| (local.get 7) | |
| ) | |
| ) | |
| ;; ALWAYS: (func $target_2 (type $2) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (param $4 i32) (param $5 i32) | |
| ;; ALWAYS-NEXT: (local $6 i32) | |
| ;; ALWAYS-NEXT: (local $7 i32) | |
| ;; ALWAYS-NEXT: (local $8 i32) | |
| ;; ALWAYS-NEXT: (local $9 i32) | |
| ;; ALWAYS-NEXT: (local $10 i32) | |
| ;; ALWAYS-NEXT: (local $11 i32) | |
| ;; ALWAYS-NEXT: (local $12 i32) | |
| ;; ALWAYS-NEXT: (local $13 i32) | |
| ;; ALWAYS-NEXT: (local $14 i32) | |
| ;; ALWAYS-NEXT: (local $15 i32) | |
| ;; ALWAYS-NEXT: (local $16 anyref) | |
| ;; ALWAYS-NEXT: (local $17 funcref) | |
| ;; ALWAYS-NEXT: (local $18 i32) | |
| ;; ALWAYS-NEXT: (local $19 f64) | |
| ;; ALWAYS-NEXT: (local $20 i32) | |
| ;; ALWAYS-NEXT: (local $21 anyref) | |
| ;; ALWAYS-NEXT: (local $22 anyref) | |
| ;; ALWAYS-NEXT: (drop | |
| ;; ALWAYS-NEXT: (block (result i32) | |
| ;; ALWAYS-NEXT: (local.set $6 | |
| ;; ALWAYS-NEXT: (local.get $0) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (local.set $7 | |
| ;; ALWAYS-NEXT: (local.get $1) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (local.set $8 | |
| ;; ALWAYS-NEXT: (local.get $2) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (local.set $9 | |
| ;; ALWAYS-NEXT: (local.get $3) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (local.set $10 | |
| ;; ALWAYS-NEXT: (local.get $4) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (local.set $11 | |
| ;; ALWAYS-NEXT: (local.get $5) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (local.set $12 | |
| ;; ALWAYS-NEXT: (global.get $imm) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (local.set $13 | |
| ;; ALWAYS-NEXT: (global.get $mut) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (local.set $14 | |
| ;; ALWAYS-NEXT: (i32.load | |
| ;; ALWAYS-NEXT: (i32.const 6) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (local.set $15 | |
| ;; ALWAYS-NEXT: (i32.const 7) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (local.set $16 | |
| ;; ALWAYS-NEXT: (ref.null none) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (local.set $17 | |
| ;; ALWAYS-NEXT: (ref.func $target) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (local.set $18 | |
| ;; ALWAYS-NEXT: (i32.eqz | |
| ;; ALWAYS-NEXT: (i32.const 8) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (local.set $19 | |
| ;; ALWAYS-NEXT: (f64.add | |
| ;; ALWAYS-NEXT: (f64.const 2.71828) | |
| ;; ALWAYS-NEXT: (f64.const 3.14159) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (local.set $20 | |
| ;; ALWAYS-NEXT: (select | |
| ;; ALWAYS-NEXT: (i32.const 9) | |
| ;; ALWAYS-NEXT: (i32.const 10) | |
| ;; ALWAYS-NEXT: (i32.const 11) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (local.set $21 | |
| ;; ALWAYS-NEXT: (ref.cast nullref | |
| ;; ALWAYS-NEXT: (ref.null none) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (local.set $22 | |
| ;; ALWAYS-NEXT: (struct.new_default $struct) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (local.get $13) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; CAREFUL: (func $target_2 (type $2) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (param $4 i32) (param $5 i32) | |
| ;; CAREFUL-NEXT: (drop | |
| ;; CAREFUL-NEXT: (i32.load | |
| ;; CAREFUL-NEXT: (i32.const 6) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: ) | |
| (module | |
| (memory 10 20) | |
| ;; ALWAYS: (type $0 (func)) | |
| ;; ALWAYS: (type $1 (func (param f32))) | |
| ;; ALWAYS: (type $2 (func (param f64))) | |
| ;; ALWAYS: (memory $0 10 20) | |
| ;; ALWAYS: (func $caller (type $0) | |
| ;; ALWAYS-NEXT: (call $target_2 | |
| ;; ALWAYS-NEXT: (block $label$1 (result f64) | |
| ;; ALWAYS-NEXT: (f64.const 0) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (call $target_3) | |
| ;; ALWAYS-NEXT: ) | |
| ;; CAREFUL: (type $0 (func)) | |
| ;; CAREFUL: (type $1 (func (param f32))) | |
| ;; CAREFUL: (memory $0 10 20) | |
| ;; CAREFUL: (func $caller (type $0) | |
| ;; CAREFUL-NEXT: (call $target | |
| ;; CAREFUL-NEXT: (f32.demote_f64 | |
| ;; CAREFUL-NEXT: (block $label$1 (result f64) | |
| ;; CAREFUL-NEXT: (f64.const 0) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: (call $target_2) | |
| ;; CAREFUL-NEXT: ) | |
| (func $caller | |
| ;; Nesting: the f32.demote_f64 operation can be moved into the context, but | |
| ;; its child cannot, so we stop there. (In CAREFUL mode, we end up doing | |
| ;; nothing here, as the benefit of monomorphization is not worth it.) | |
| (call $target | |
| (f32.demote_f64 | |
| (block $label$1 (result f64) | |
| (f64.const 0) | |
| ) | |
| ) | |
| ) | |
| ;; Now the child is an f64.abs, which can be moved into the context, so it | |
| ;; all is moved. This ends up worthwhile in CAREFUL mode (since we can | |
| ;; optimize all the math here). | |
| (call $target | |
| (f32.demote_f64 | |
| (f64.abs ;; this changed | |
| (f64.const 0) | |
| ) | |
| ) | |
| ) | |
| ) | |
| ;; ALWAYS: (func $target (type $1) (param $f32 f32) | |
| ;; ALWAYS-NEXT: (f32.store | |
| ;; ALWAYS-NEXT: (i32.const 42) | |
| ;; ALWAYS-NEXT: (local.get $f32) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; CAREFUL: (func $target (type $1) (param $0 f32) | |
| ;; CAREFUL-NEXT: (f32.store | |
| ;; CAREFUL-NEXT: (i32.const 42) | |
| ;; CAREFUL-NEXT: (local.get $0) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: ) | |
| (func $target (param $f32 f32) | |
| ;; When monomorphized the first time, the param here will be f64 and not | |
| ;; i32, showing we handle a type change. | |
| ;; | |
| ;; When monomorphized the second time, the param will go away entirely. | |
| (f32.store | |
| (i32.const 42) | |
| (local.get $f32) | |
| ) | |
| ) | |
| ) | |
| ;; ALWAYS: (func $target_2 (type $2) (param $0 f64) | |
| ;; ALWAYS-NEXT: (local $f32 f32) | |
| ;; ALWAYS-NEXT: (local.set $f32 | |
| ;; ALWAYS-NEXT: (f32.demote_f64 | |
| ;; ALWAYS-NEXT: (local.get $0) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (f32.store | |
| ;; ALWAYS-NEXT: (i32.const 42) | |
| ;; ALWAYS-NEXT: (local.get $f32) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS: (func $target_3 (type $0) | |
| ;; ALWAYS-NEXT: (local $f32 f32) | |
| ;; ALWAYS-NEXT: (local.set $f32 | |
| ;; ALWAYS-NEXT: (f32.demote_f64 | |
| ;; ALWAYS-NEXT: (f64.abs | |
| ;; ALWAYS-NEXT: (f64.const 0) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (f32.store | |
| ;; ALWAYS-NEXT: (i32.const 42) | |
| ;; ALWAYS-NEXT: (local.get $f32) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; CAREFUL: (func $target_2 (type $0) | |
| ;; CAREFUL-NEXT: (f32.store | |
| ;; CAREFUL-NEXT: (i32.const 42) | |
| ;; CAREFUL-NEXT: (f32.const 0) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: ) | |
| (module | |
| ;; ALWAYS: (type $struct (struct (field (mut f32)) (field (mut f64)))) | |
| ;; CAREFUL: (type $struct (struct (field (mut f32)) (field (mut f64)))) | |
| (type $struct (struct (field (mut f32)) (field (mut f64)))) | |
| ;; ALWAYS: (type $1 (func (param f32) (result anyref))) | |
| ;; ALWAYS: (type $2 (func (param f64) (result anyref))) | |
| ;; ALWAYS: (type $3 (func (param f64))) | |
| ;; ALWAYS: (type $4 (func (param (ref $struct)) (result anyref))) | |
| ;; ALWAYS: (func $caller (type $1) (param $x f32) (result anyref) | |
| ;; ALWAYS-NEXT: (call $target_4 | |
| ;; ALWAYS-NEXT: (local.get $x) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; CAREFUL: (type $1 (func (param f64))) | |
| ;; CAREFUL: (type $2 (func (param f32) (result anyref))) | |
| ;; CAREFUL: (type $3 (func (param f64) (result anyref))) | |
| ;; CAREFUL: (type $4 (func (param (ref $struct)) (result anyref))) | |
| ;; CAREFUL: (func $caller (type $2) (param $x f32) (result anyref) | |
| ;; CAREFUL-NEXT: (call $target | |
| ;; CAREFUL-NEXT: (struct.new $struct | |
| ;; CAREFUL-NEXT: (local.get $x) | |
| ;; CAREFUL-NEXT: (f64.const 4.2) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: ) | |
| (func $caller (param $x f32) (result anyref) | |
| ;; We can reverse-inline the struct.new and the nested constant, leaving | |
| ;; only the local.get as a remaining param. (In CAREFUL mode, however, this | |
| ;; does not look promising enough to optimize.) | |
| (call $target | |
| (struct.new $struct | |
| (local.get $x) | |
| (f64.const 4.2) | |
| ) | |
| ) | |
| ) | |
| ;; ALWAYS: (func $caller-flip (type $2) (param $x f64) (result anyref) | |
| ;; ALWAYS-NEXT: (call $target_5 | |
| ;; ALWAYS-NEXT: (local.get $x) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; CAREFUL: (func $caller-flip (type $3) (param $x f64) (result anyref) | |
| ;; CAREFUL-NEXT: (call $target | |
| ;; CAREFUL-NEXT: (struct.new $struct | |
| ;; CAREFUL-NEXT: (f32.const 13.369999885559082) | |
| ;; CAREFUL-NEXT: (local.get $x) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: ) | |
| (func $caller-flip (param $x f64) (result anyref) | |
| ;; As above, but with struct.new's children flipped (which does not change | |
| ;; anything). | |
| (call $target | |
| (struct.new $struct | |
| (f32.const 13.37) | |
| (local.get $x) | |
| ) | |
| ) | |
| ) | |
| ;; ALWAYS: (func $dropped-caller (type $3) (param $x f64) | |
| ;; ALWAYS-NEXT: (call $target_6 | |
| ;; ALWAYS-NEXT: (local.get $x) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; CAREFUL: (func $dropped-caller (type $1) (param $x f64) | |
| ;; CAREFUL-NEXT: (call $target_4 | |
| ;; CAREFUL-NEXT: (local.get $x) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: ) | |
| (func $dropped-caller (param $x f64) | |
| ;; As above, but the outcome is dropped. As the target function only does | |
| ;; some struct.sets, escape analysis after monomorphization can prove that | |
| ;; we can remove all the code, so this is optimized in CAREFUL mode. | |
| (drop | |
| (call $target | |
| (struct.new $struct | |
| (f32.const 13.37) | |
| (local.get $x) | |
| ) | |
| ) | |
| ) | |
| ) | |
| ;; ALWAYS: (func $target (type $4) (param $ref (ref $struct)) (result anyref) | |
| ;; ALWAYS-NEXT: (struct.set $struct 0 | |
| ;; ALWAYS-NEXT: (local.get $ref) | |
| ;; ALWAYS-NEXT: (f32.add | |
| ;; ALWAYS-NEXT: (struct.get $struct 0 | |
| ;; ALWAYS-NEXT: (local.get $ref) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (f32.const 1.100000023841858) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (struct.set $struct 1 | |
| ;; ALWAYS-NEXT: (local.get $ref) | |
| ;; ALWAYS-NEXT: (f64.max | |
| ;; ALWAYS-NEXT: (struct.get $struct 1 | |
| ;; ALWAYS-NEXT: (local.get $ref) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (f64.const -1.2) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (local.get $ref) | |
| ;; ALWAYS-NEXT: ) | |
| ;; CAREFUL: (func $target (type $4) (param $0 (ref $struct)) (result anyref) | |
| ;; CAREFUL-NEXT: (struct.set $struct 0 | |
| ;; CAREFUL-NEXT: (local.get $0) | |
| ;; CAREFUL-NEXT: (f32.add | |
| ;; CAREFUL-NEXT: (struct.get $struct 0 | |
| ;; CAREFUL-NEXT: (local.get $0) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: (f32.const 1.100000023841858) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: (struct.set $struct 1 | |
| ;; CAREFUL-NEXT: (local.get $0) | |
| ;; CAREFUL-NEXT: (f64.max | |
| ;; CAREFUL-NEXT: (struct.get $struct 1 | |
| ;; CAREFUL-NEXT: (local.get $0) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: (f64.const -1.2) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: (local.get $0) | |
| ;; CAREFUL-NEXT: ) | |
| (func $target (param $ref (ref $struct)) (result anyref) | |
| ;; Do some operations on the reference, but do not escape it. | |
| (struct.set $struct 0 | |
| (local.get $ref) | |
| (f32.add | |
| (struct.get $struct 0 | |
| (local.get $ref) | |
| ) | |
| (f32.const 1.1) | |
| ) | |
| ) | |
| (struct.set $struct 1 | |
| (local.get $ref) | |
| (f64.max | |
| (struct.get $struct 1 | |
| (local.get $ref) | |
| ) | |
| (f64.const -1.2) | |
| ) | |
| ) | |
| (local.get $ref) | |
| ) | |
| ) | |
| ;; ALWAYS: (func $target_4 (type $1) (param $0 f32) (result anyref) | |
| ;; ALWAYS-NEXT: (local $ref (ref $struct)) | |
| ;; ALWAYS-NEXT: (local.set $ref | |
| ;; ALWAYS-NEXT: (struct.new $struct | |
| ;; ALWAYS-NEXT: (local.get $0) | |
| ;; ALWAYS-NEXT: (f64.const 4.2) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (block (result anyref) | |
| ;; ALWAYS-NEXT: (struct.set $struct 0 | |
| ;; ALWAYS-NEXT: (local.get $ref) | |
| ;; ALWAYS-NEXT: (f32.add | |
| ;; ALWAYS-NEXT: (struct.get $struct 0 | |
| ;; ALWAYS-NEXT: (local.get $ref) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (f32.const 1.100000023841858) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (struct.set $struct 1 | |
| ;; ALWAYS-NEXT: (local.get $ref) | |
| ;; ALWAYS-NEXT: (f64.max | |
| ;; ALWAYS-NEXT: (struct.get $struct 1 | |
| ;; ALWAYS-NEXT: (local.get $ref) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (f64.const -1.2) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (local.get $ref) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS: (func $target_5 (type $2) (param $0 f64) (result anyref) | |
| ;; ALWAYS-NEXT: (local $ref (ref $struct)) | |
| ;; ALWAYS-NEXT: (local.set $ref | |
| ;; ALWAYS-NEXT: (struct.new $struct | |
| ;; ALWAYS-NEXT: (f32.const 13.369999885559082) | |
| ;; ALWAYS-NEXT: (local.get $0) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (block (result anyref) | |
| ;; ALWAYS-NEXT: (struct.set $struct 0 | |
| ;; ALWAYS-NEXT: (local.get $ref) | |
| ;; ALWAYS-NEXT: (f32.add | |
| ;; ALWAYS-NEXT: (struct.get $struct 0 | |
| ;; ALWAYS-NEXT: (local.get $ref) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (f32.const 1.100000023841858) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (struct.set $struct 1 | |
| ;; ALWAYS-NEXT: (local.get $ref) | |
| ;; ALWAYS-NEXT: (f64.max | |
| ;; ALWAYS-NEXT: (struct.get $struct 1 | |
| ;; ALWAYS-NEXT: (local.get $ref) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (f64.const -1.2) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (local.get $ref) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS: (func $target_6 (type $3) (param $0 f64) | |
| ;; ALWAYS-NEXT: (local $ref (ref $struct)) | |
| ;; ALWAYS-NEXT: (drop | |
| ;; ALWAYS-NEXT: (block (result anyref) | |
| ;; ALWAYS-NEXT: (local.set $ref | |
| ;; ALWAYS-NEXT: (struct.new $struct | |
| ;; ALWAYS-NEXT: (f32.const 13.369999885559082) | |
| ;; ALWAYS-NEXT: (local.get $0) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (block (result anyref) | |
| ;; ALWAYS-NEXT: (struct.set $struct 0 | |
| ;; ALWAYS-NEXT: (local.get $ref) | |
| ;; ALWAYS-NEXT: (f32.add | |
| ;; ALWAYS-NEXT: (struct.get $struct 0 | |
| ;; ALWAYS-NEXT: (local.get $ref) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (f32.const 1.100000023841858) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (struct.set $struct 1 | |
| ;; ALWAYS-NEXT: (local.get $ref) | |
| ;; ALWAYS-NEXT: (f64.max | |
| ;; ALWAYS-NEXT: (struct.get $struct 1 | |
| ;; ALWAYS-NEXT: (local.get $ref) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (f64.const -1.2) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (local.get $ref) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; CAREFUL: (func $target_4 (type $1) (param $0 f64) | |
| ;; CAREFUL-NEXT: (nop) | |
| ;; CAREFUL-NEXT: ) | |
| (module | |
| ;; ALWAYS: (type $struct (struct (field i16) (field (mut i8)) (field (mut f64)))) | |
| ;; CAREFUL: (type $struct (struct (field i16) (field (mut i8)) (field (mut f64)))) | |
| (type $struct (struct (field i16) (field (mut i8)) (field (mut f64)))) | |
| ;; ALWAYS: (type $1 (func)) | |
| ;; ALWAYS: (type $2 (func (param (ref $struct)))) | |
| ;; ALWAYS: (type $3 (func (param i32 f64))) | |
| ;; ALWAYS: (func $caller (type $1) | |
| ;; ALWAYS-NEXT: (local $i32 i32) | |
| ;; ALWAYS-NEXT: (local $f64 f64) | |
| ;; ALWAYS-NEXT: (call $target_2 | |
| ;; ALWAYS-NEXT: (local.get $i32) | |
| ;; ALWAYS-NEXT: (local.get $f64) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; CAREFUL: (type $1 (func)) | |
| ;; CAREFUL: (type $2 (func (param (ref $struct)))) | |
| ;; CAREFUL: (type $3 (func (param i32 f64))) | |
| ;; CAREFUL: (func $caller (type $1) | |
| ;; CAREFUL-NEXT: (local $i32 i32) | |
| ;; CAREFUL-NEXT: (local $f64 f64) | |
| ;; CAREFUL-NEXT: (call $target_2 | |
| ;; CAREFUL-NEXT: (local.get $i32) | |
| ;; CAREFUL-NEXT: (local.get $f64) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: ) | |
| (func $caller | |
| (local $i32 i32) | |
| (local $f64 f64) | |
| ;; The first operand can be moved to the context, but not the other two. Of | |
| ;; those two, the order of iteration matters, as they have different types | |
| ;; (if we got mixed up and reordered them, we'd error). | |
| (call $target | |
| (struct.new $struct | |
| (i32.const 0) | |
| (local.get $i32) | |
| (local.get $f64) | |
| ) | |
| ) | |
| ) | |
| ;; ALWAYS: (func $target (type $2) (param $0 (ref $struct)) | |
| ;; ALWAYS-NEXT: (nop) | |
| ;; ALWAYS-NEXT: ) | |
| ;; CAREFUL: (func $target (type $2) (param $0 (ref $struct)) | |
| ;; CAREFUL-NEXT: (nop) | |
| ;; CAREFUL-NEXT: ) | |
| (func $target (param (ref $struct)) | |
| (nop) | |
| ) | |
| ) | |
| ;; ALWAYS: (func $target_2 (type $3) (param $0 i32) (param $1 f64) | |
| ;; ALWAYS-NEXT: (local $2 (ref $struct)) | |
| ;; ALWAYS-NEXT: (local.set $2 | |
| ;; ALWAYS-NEXT: (struct.new $struct | |
| ;; ALWAYS-NEXT: (i32.const 0) | |
| ;; ALWAYS-NEXT: (local.get $0) | |
| ;; ALWAYS-NEXT: (local.get $1) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (nop) | |
| ;; ALWAYS-NEXT: ) | |
| ;; CAREFUL: (func $target_2 (type $3) (param $0 i32) (param $1 f64) | |
| ;; CAREFUL-NEXT: (nop) | |
| ;; CAREFUL-NEXT: ) | |
| (module | |
| ;; ALWAYS: (type $array (sub (array (mut i8)))) | |
| (type $array (sub (array (mut i8)))) | |
| ;; ALWAYS: (type $1 (func)) | |
| ;; ALWAYS: (type $2 (func (param i32))) | |
| ;; ALWAYS: (type $3 (func (param anyref anyref) (result i32))) | |
| ;; ALWAYS: (type $4 (func (param i32 i32 i32))) | |
| ;; ALWAYS: (func $caller (type $1) | |
| ;; ALWAYS-NEXT: (call $target_3) | |
| ;; ALWAYS-NEXT: ) | |
| ;; CAREFUL: (type $0 (func)) | |
| ;; CAREFUL: (type $1 (func (param i32))) | |
| ;; CAREFUL: (type $2 (func (param anyref anyref) (result i32))) | |
| ;; CAREFUL: (type $3 (func (param i32 i32 i32))) | |
| ;; CAREFUL: (func $caller (type $0) | |
| ;; CAREFUL-NEXT: (call $target_3) | |
| ;; CAREFUL-NEXT: ) | |
| (func $caller | |
| ;; Call the target with array.new which has an optional child, the initial | |
| ;; value. Set it in one and leave it as nullptr in the other to see we | |
| ;; handle both properly when we move the array.new + children into the | |
| ;; monomorphized function. | |
| (drop | |
| (call $target | |
| (array.new_default $array | |
| (i32.const 1) | |
| ) | |
| (array.new $array | |
| (i32.const 2) | |
| (i32.const 3) | |
| ) | |
| ) | |
| ) | |
| ) | |
| ;; ALWAYS: (func $caller-unknown (type $2) (param $x i32) | |
| ;; ALWAYS-NEXT: (call $target_4 | |
| ;; ALWAYS-NEXT: (local.get $x) | |
| ;; ALWAYS-NEXT: (local.get $x) | |
| ;; ALWAYS-NEXT: (local.get $x) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; CAREFUL: (func $caller-unknown (type $1) (param $x i32) | |
| ;; CAREFUL-NEXT: (call $target_4 | |
| ;; CAREFUL-NEXT: (local.get $x) | |
| ;; CAREFUL-NEXT: (local.get $x) | |
| ;; CAREFUL-NEXT: (local.get $x) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: ) | |
| (func $caller-unknown (param $x i32) | |
| ;; As above, but now there are unknown children, which are not moved. | |
| (drop | |
| (call $target | |
| (array.new_default $array | |
| (local.get $x) | |
| ) | |
| (array.new $array | |
| (local.get $x) | |
| (local.get $x) | |
| ) | |
| ) | |
| ) | |
| ) | |
| ;; ALWAYS: (func $target (type $3) (param $0 anyref) (param $1 anyref) (result i32) | |
| ;; ALWAYS-NEXT: (unreachable) | |
| ;; ALWAYS-NEXT: ) | |
| ;; CAREFUL: (func $target (type $2) (param $0 anyref) (param $1 anyref) (result i32) | |
| ;; CAREFUL-NEXT: (unreachable) | |
| ;; CAREFUL-NEXT: ) | |
| (func $target (param anyref) (param anyref) (result i32) | |
| (unreachable) | |
| ) | |
| ) | |
| ;; ALWAYS: (func $target_3 (type $1) | |
| ;; ALWAYS-NEXT: (local $0 anyref) | |
| ;; ALWAYS-NEXT: (local $1 anyref) | |
| ;; ALWAYS-NEXT: (local.set $0 | |
| ;; ALWAYS-NEXT: (array.new_default $array | |
| ;; ALWAYS-NEXT: (i32.const 1) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (local.set $1 | |
| ;; ALWAYS-NEXT: (array.new $array | |
| ;; ALWAYS-NEXT: (i32.const 2) | |
| ;; ALWAYS-NEXT: (i32.const 3) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (unreachable) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS: (func $target_4 (type $4) (param $0 i32) (param $1 i32) (param $2 i32) | |
| ;; ALWAYS-NEXT: (local $3 anyref) | |
| ;; ALWAYS-NEXT: (local $4 anyref) | |
| ;; ALWAYS-NEXT: (local.set $3 | |
| ;; ALWAYS-NEXT: (array.new_default $array | |
| ;; ALWAYS-NEXT: (local.get $0) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (local.set $4 | |
| ;; ALWAYS-NEXT: (array.new $array | |
| ;; ALWAYS-NEXT: (local.get $1) | |
| ;; ALWAYS-NEXT: (local.get $2) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (unreachable) | |
| ;; ALWAYS-NEXT: ) | |
| ;; CAREFUL: (func $target_3 (type $0) | |
| ;; CAREFUL-NEXT: (unreachable) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL: (func $target_4 (type $3) (param $0 i32) (param $1 i32) (param $2 i32) | |
| ;; CAREFUL-NEXT: (unreachable) | |
| ;; CAREFUL-NEXT: ) | |
| (module | |
| ;; ALWAYS: (type $struct (struct (field anyref))) | |
| (type $struct (struct (field anyref))) | |
| ;; ALWAYS: (type $1 (func (param anyref) (result anyref))) | |
| ;; ALWAYS: (func $caller (type $1) (param $x anyref) (result anyref) | |
| ;; ALWAYS-NEXT: (call $target_2 | |
| ;; ALWAYS-NEXT: (local.get $x) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; CAREFUL: (type $0 (func (param anyref) (result anyref))) | |
| ;; CAREFUL: (func $caller (type $0) (param $x anyref) (result anyref) | |
| ;; CAREFUL-NEXT: (call $target_2 | |
| ;; CAREFUL-NEXT: (local.get $x) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: ) | |
| (func $caller (param $x anyref) (result anyref) | |
| ;; A call with a deeply nested param. We can move all of it but the | |
| ;; local.get into the monomorphized function. | |
| (call $target | |
| (struct.new $struct | |
| (struct.new $struct | |
| (struct.new $struct | |
| (struct.new $struct | |
| (struct.new $struct | |
| (struct.new $struct | |
| (local.get $x) | |
| ) | |
| ) | |
| ) | |
| ) | |
| ) | |
| ) | |
| ) | |
| ) | |
| ;; ALWAYS: (func $target (type $1) (param $0 anyref) (result anyref) | |
| ;; ALWAYS-NEXT: (unreachable) | |
| ;; ALWAYS-NEXT: ) | |
| ;; CAREFUL: (func $target (type $0) (param $0 anyref) (result anyref) | |
| ;; CAREFUL-NEXT: (unreachable) | |
| ;; CAREFUL-NEXT: ) | |
| (func $target (param anyref) (result anyref) | |
| (unreachable) | |
| ) | |
| ) | |
| ;; ALWAYS: (func $target_2 (type $1) (param $0 anyref) (result anyref) | |
| ;; ALWAYS-NEXT: (local $1 anyref) | |
| ;; ALWAYS-NEXT: (local.set $1 | |
| ;; ALWAYS-NEXT: (struct.new $struct | |
| ;; ALWAYS-NEXT: (struct.new $struct | |
| ;; ALWAYS-NEXT: (struct.new $struct | |
| ;; ALWAYS-NEXT: (struct.new $struct | |
| ;; ALWAYS-NEXT: (struct.new $struct | |
| ;; ALWAYS-NEXT: (struct.new $struct | |
| ;; ALWAYS-NEXT: (local.get $0) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (unreachable) | |
| ;; ALWAYS-NEXT: ) | |
| ;; CAREFUL: (func $target_2 (type $0) (param $0 anyref) (result anyref) | |
| ;; CAREFUL-NEXT: (unreachable) | |
| ;; CAREFUL-NEXT: ) | |
| (module | |
| (memory 10 20) | |
| ;; ALWAYS: (type $0 (func)) | |
| ;; ALWAYS: (type $1 (func (param i32 i32))) | |
| ;; ALWAYS: (type $2 (func (param i32))) | |
| ;; ALWAYS: (memory $0 10 20) | |
| ;; ALWAYS: (func $caller (type $0) | |
| ;; ALWAYS-NEXT: (call $target | |
| ;; ALWAYS-NEXT: (i32.load | |
| ;; ALWAYS-NEXT: (i32.const 0) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (block (result i32) | |
| ;; ALWAYS-NEXT: (i32.store | |
| ;; ALWAYS-NEXT: (i32.const 0) | |
| ;; ALWAYS-NEXT: (i32.const -1) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (i32.const 11) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; CAREFUL: (type $0 (func)) | |
| ;; CAREFUL: (type $1 (func (param i32 i32))) | |
| ;; CAREFUL: (memory $0 10 20) | |
| ;; CAREFUL: (func $caller (type $0) | |
| ;; CAREFUL-NEXT: (call $target | |
| ;; CAREFUL-NEXT: (i32.load | |
| ;; CAREFUL-NEXT: (i32.const 0) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: (block (result i32) | |
| ;; CAREFUL-NEXT: (i32.store | |
| ;; CAREFUL-NEXT: (i32.const 0) | |
| ;; CAREFUL-NEXT: (i32.const -1) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: (i32.const 11) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: ) | |
| (func $caller | |
| ;; The two operands here cannot be reordered: the first loads, and the | |
| ;; second stores. If we move the first into the context but not the second | |
| ;; (which is what we'd normally do: the first can be copied, while the | |
| ;; second is a control flow structure) then we'd execute the second before | |
| ;; the first, as we'd execute the first only after doing the call, which | |
| ;; would be wrong. | |
| (call $target | |
| (i32.load | |
| (i32.const 0) | |
| ) | |
| (block (result i32) | |
| (i32.store | |
| (i32.const 0) | |
| (i32.const 0xffffffff) | |
| ) | |
| (i32.const 11) | |
| ) | |
| ) | |
| ) | |
| ;; ALWAYS: (func $caller-flip (type $0) | |
| ;; ALWAYS-NEXT: (call $target_3 | |
| ;; ALWAYS-NEXT: (block (result i32) | |
| ;; ALWAYS-NEXT: (i32.store | |
| ;; ALWAYS-NEXT: (i32.const 0) | |
| ;; ALWAYS-NEXT: (i32.const -1) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (i32.const 11) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; CAREFUL: (func $caller-flip (type $0) | |
| ;; CAREFUL-NEXT: (call $target | |
| ;; CAREFUL-NEXT: (block (result i32) | |
| ;; CAREFUL-NEXT: (i32.store | |
| ;; CAREFUL-NEXT: (i32.const 0) | |
| ;; CAREFUL-NEXT: (i32.const -1) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: (i32.const 11) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: (i32.load | |
| ;; CAREFUL-NEXT: (i32.const 0) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: ) | |
| (func $caller-flip | |
| ;; With the order reversed, there is no problem: the load can be moved into | |
| ;; the context (in ALWAYS, at least). | |
| (call $target | |
| (block (result i32) | |
| (i32.store | |
| (i32.const 0) | |
| (i32.const 0xffffffff) | |
| ) | |
| (i32.const 11) | |
| ) | |
| (i32.load | |
| (i32.const 0) | |
| ) | |
| ) | |
| ) | |
| ;; ALWAYS: (func $target (type $1) (param $0 i32) (param $1 i32) | |
| ;; ALWAYS-NEXT: ) | |
| ;; CAREFUL: (func $target (type $1) (param $0 i32) (param $1 i32) | |
| ;; CAREFUL-NEXT: (nop) | |
| ;; CAREFUL-NEXT: ) | |
| (func $target (param i32) (param i32) | |
| ) | |
| ) | |
| ;; ALWAYS: (func $target_3 (type $2) (param $0 i32) | |
| ;; ALWAYS-NEXT: (local $1 i32) | |
| ;; ALWAYS-NEXT: (local $2 i32) | |
| ;; ALWAYS-NEXT: (local.set $1 | |
| ;; ALWAYS-NEXT: (local.get $0) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (local.set $2 | |
| ;; ALWAYS-NEXT: (i32.load | |
| ;; ALWAYS-NEXT: (i32.const 0) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (block | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| (module | |
| (memory 10 20) | |
| ;; ALWAYS: (type $0 (func (param i32))) | |
| ;; ALWAYS: (type $1 (func)) | |
| ;; ALWAYS: (memory $0 10 20) | |
| ;; ALWAYS: (func $caller (type $1) | |
| ;; ALWAYS-NEXT: (call $target_2 | |
| ;; ALWAYS-NEXT: (block (result i32) | |
| ;; ALWAYS-NEXT: (i32.load | |
| ;; ALWAYS-NEXT: (i32.const 0) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; CAREFUL: (type $0 (func)) | |
| ;; CAREFUL: (type $1 (func (param i32))) | |
| ;; CAREFUL: (memory $0 10 20) | |
| ;; CAREFUL: (func $caller (type $0) | |
| ;; CAREFUL-NEXT: (call $target | |
| ;; CAREFUL-NEXT: (i32.atomic.rmw8.cmpxchg_u | |
| ;; CAREFUL-NEXT: (i32.const 0) | |
| ;; CAREFUL-NEXT: (block (result i32) | |
| ;; CAREFUL-NEXT: (i32.load | |
| ;; CAREFUL-NEXT: (i32.const 0) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: (i32.const 1) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: ) | |
| (func $caller | |
| ;; Effect interaction between a parent and child: the child reads while the | |
| ;; parent writes (and also reads), so they cannot be reordered. But the | |
| ;; parent executes later anyhow, so it is fine to optimize the atomic | |
| ;; operation into the context. | |
| (call $target | |
| (i32.atomic.rmw8.cmpxchg_u | |
| (i32.const 0) | |
| (block (result i32) ;; Use a block to prevent the child from being | |
| ;; moved into the context. | |
| (i32.load | |
| (i32.const 0) | |
| ) | |
| ) | |
| (i32.const 1) | |
| ) | |
| ) | |
| ;; Note that we cannot test the reverse, since if the block were on the | |
| ;; outside then anything inside it would not be moved. | |
| ) | |
| ;; ALWAYS: (func $target (type $0) (param $0 i32) | |
| ;; ALWAYS-NEXT: ) | |
| ;; CAREFUL: (func $target (type $1) (param $0 i32) | |
| ;; CAREFUL-NEXT: (nop) | |
| ;; CAREFUL-NEXT: ) | |
| (func $target (param i32) | |
| ) | |
| ) | |
| ;; ALWAYS: (func $target_2 (type $0) (param $0 i32) | |
| ;; ALWAYS-NEXT: (local $1 i32) | |
| ;; ALWAYS-NEXT: (local.set $1 | |
| ;; ALWAYS-NEXT: (i32.atomic.rmw8.cmpxchg_u | |
| ;; ALWAYS-NEXT: (i32.const 0) | |
| ;; ALWAYS-NEXT: (local.get $0) | |
| ;; ALWAYS-NEXT: (i32.const 1) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (block | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| (module | |
| ;; ALWAYS: (type $0 (func)) | |
| ;; ALWAYS: (type $struct (struct (field i32) (field i32))) | |
| (type $struct (struct (field i32) (field i32))) | |
| (memory 10 20) | |
| ;; ALWAYS: (type $2 (func (param anyref))) | |
| ;; ALWAYS: (type $3 (func (param i32 i32))) | |
| ;; ALWAYS: (type $4 (func (param i32))) | |
| ;; ALWAYS: (memory $0 10 20) | |
| ;; ALWAYS: (func $caller (type $0) | |
| ;; ALWAYS-NEXT: (call $target_3 | |
| ;; ALWAYS-NEXT: (i32.load | |
| ;; ALWAYS-NEXT: (i32.const 0) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (block (result i32) | |
| ;; ALWAYS-NEXT: (i32.store | |
| ;; ALWAYS-NEXT: (i32.const 0) | |
| ;; ALWAYS-NEXT: (i32.const 1) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (i32.const 2) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; CAREFUL: (type $0 (func)) | |
| ;; CAREFUL: (type $1 (func (param anyref))) | |
| ;; CAREFUL: (type $2 (func (param i32 i32))) | |
| ;; CAREFUL: (type $3 (func (param i32))) | |
| ;; CAREFUL: (memory $0 10 20) | |
| ;; CAREFUL: (func $caller (type $0) | |
| ;; CAREFUL-NEXT: (call $target_3 | |
| ;; CAREFUL-NEXT: (i32.load | |
| ;; CAREFUL-NEXT: (i32.const 0) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: (block (result i32) | |
| ;; CAREFUL-NEXT: (i32.store | |
| ;; CAREFUL-NEXT: (i32.const 0) | |
| ;; CAREFUL-NEXT: (i32.const 1) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: (i32.const 2) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: ) | |
| (func $caller | |
| ;; Effect interaction between children of a call operand. The read and write | |
| ;; cannot be reordered, so all we can move into the call context is the | |
| ;; struct.new. | |
| (call $target | |
| (struct.new $struct | |
| (i32.load | |
| (i32.const 0) | |
| ) | |
| (block (result i32) ;; Use a block to prevent the child from being | |
| ;; moved into the context. | |
| (i32.store | |
| (i32.const 0) | |
| (i32.const 1) | |
| ) | |
| (i32.const 2) | |
| ) | |
| ) | |
| ) | |
| ) | |
| ;; ALWAYS: (func $caller-flip (type $0) | |
| ;; ALWAYS-NEXT: (call $target_4 | |
| ;; ALWAYS-NEXT: (block (result i32) | |
| ;; ALWAYS-NEXT: (i32.store | |
| ;; ALWAYS-NEXT: (i32.const 0) | |
| ;; ALWAYS-NEXT: (i32.const 1) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (i32.const 2) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; CAREFUL: (func $caller-flip (type $0) | |
| ;; CAREFUL-NEXT: (call $target_4 | |
| ;; CAREFUL-NEXT: (block (result i32) | |
| ;; CAREFUL-NEXT: (i32.store | |
| ;; CAREFUL-NEXT: (i32.const 0) | |
| ;; CAREFUL-NEXT: (i32.const 1) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: (i32.const 2) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: ) | |
| (func $caller-flip | |
| ;; With the order reversed, there is no problem, and the load can also be | |
| ;; optimized into the context. | |
| (call $target | |
| (struct.new $struct | |
| (block (result i32) | |
| (i32.store | |
| (i32.const 0) | |
| (i32.const 1) | |
| ) | |
| (i32.const 2) | |
| ) | |
| (i32.load | |
| (i32.const 0) | |
| ) | |
| ) | |
| ) | |
| ) | |
| ;; ALWAYS: (func $target (type $2) (param $0 anyref) | |
| ;; ALWAYS-NEXT: ) | |
| ;; CAREFUL: (func $target (type $1) (param $0 anyref) | |
| ;; CAREFUL-NEXT: (nop) | |
| ;; CAREFUL-NEXT: ) | |
| (func $target (param anyref) | |
| ) | |
| ) | |
| ;; ALWAYS: (func $target_3 (type $3) (param $0 i32) (param $1 i32) | |
| ;; ALWAYS-NEXT: (local $2 anyref) | |
| ;; ALWAYS-NEXT: (local.set $2 | |
| ;; ALWAYS-NEXT: (struct.new $struct | |
| ;; ALWAYS-NEXT: (local.get $0) | |
| ;; ALWAYS-NEXT: (local.get $1) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (block | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS: (func $target_4 (type $4) (param $0 i32) | |
| ;; ALWAYS-NEXT: (local $1 anyref) | |
| ;; ALWAYS-NEXT: (local.set $1 | |
| ;; ALWAYS-NEXT: (struct.new $struct | |
| ;; ALWAYS-NEXT: (local.get $0) | |
| ;; ALWAYS-NEXT: (i32.load | |
| ;; ALWAYS-NEXT: (i32.const 0) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (block | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; CAREFUL: (func $target_3 (type $2) (param $0 i32) (param $1 i32) | |
| ;; CAREFUL-NEXT: (nop) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL: (func $target_4 (type $3) (param $0 i32) | |
| ;; CAREFUL-NEXT: (drop | |
| ;; CAREFUL-NEXT: (i32.load | |
| ;; CAREFUL-NEXT: (i32.const 0) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: ) | |
| (module | |
| (memory 10 20) | |
| ;; ALWAYS: (type $0 (func)) | |
| ;; ALWAYS: (type $1 (func (param i32 i32 i32 i32 i32 i32))) | |
| ;; ALWAYS: (type $2 (func (param i32 i32 i32 i32 i32))) | |
| ;; ALWAYS: (memory $0 10 20) | |
| ;; ALWAYS: (func $caller (type $0) | |
| ;; ALWAYS-NEXT: (call $target_2 | |
| ;; ALWAYS-NEXT: (block (result i32) | |
| ;; ALWAYS-NEXT: (i32.store | |
| ;; ALWAYS-NEXT: (i32.const 0) | |
| ;; ALWAYS-NEXT: (i32.const -1) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (i32.const 11) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (i32.load | |
| ;; ALWAYS-NEXT: (i32.const 0) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (block (result i32) | |
| ;; ALWAYS-NEXT: (i32.store | |
| ;; ALWAYS-NEXT: (i32.const 0) | |
| ;; ALWAYS-NEXT: (i32.const -1) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (i32.const 11) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (i32.load | |
| ;; ALWAYS-NEXT: (i32.const 0) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (block (result i32) | |
| ;; ALWAYS-NEXT: (i32.store | |
| ;; ALWAYS-NEXT: (i32.const 0) | |
| ;; ALWAYS-NEXT: (i32.const -1) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (i32.const 11) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; CAREFUL: (type $0 (func)) | |
| ;; CAREFUL: (type $1 (func (param i32 i32 i32 i32 i32 i32))) | |
| ;; CAREFUL: (memory $0 10 20) | |
| ;; CAREFUL: (func $caller (type $0) | |
| ;; CAREFUL-NEXT: (call $target | |
| ;; CAREFUL-NEXT: (block (result i32) | |
| ;; CAREFUL-NEXT: (i32.store | |
| ;; CAREFUL-NEXT: (i32.const 0) | |
| ;; CAREFUL-NEXT: (i32.const -1) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: (i32.const 11) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: (i32.load | |
| ;; CAREFUL-NEXT: (i32.const 0) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: (block (result i32) | |
| ;; CAREFUL-NEXT: (i32.store | |
| ;; CAREFUL-NEXT: (i32.const 0) | |
| ;; CAREFUL-NEXT: (i32.const -1) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: (i32.const 11) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: (i32.load | |
| ;; CAREFUL-NEXT: (i32.const 0) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: (block (result i32) | |
| ;; CAREFUL-NEXT: (i32.store | |
| ;; CAREFUL-NEXT: (i32.const 0) | |
| ;; CAREFUL-NEXT: (i32.const -1) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: (i32.const 11) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: (i32.load | |
| ;; CAREFUL-NEXT: (i32.const 0) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: ) | |
| (func $caller | |
| ;; Similar to before, but with a sequence of interleaved things with | |
| ;; interactions. The same two items repeat three times. Any load cannot be | |
| ;; moved into the context if there is a store after it, which means that the | |
| ;; last load can be optimized and nothing else (in CAREFUL mode, however, | |
| ;; that ends up not worthwhile). | |
| (call $target | |
| (block (result i32) | |
| (i32.store | |
| (i32.const 0) | |
| (i32.const 0xffffffff) | |
| ) | |
| (i32.const 11) | |
| ) | |
| (i32.load | |
| (i32.const 0) | |
| ) | |
| (block (result i32) | |
| (i32.store | |
| (i32.const 0) | |
| (i32.const 0xffffffff) | |
| ) | |
| (i32.const 11) | |
| ) | |
| (i32.load | |
| (i32.const 0) | |
| ) | |
| (block (result i32) | |
| (i32.store | |
| (i32.const 0) | |
| (i32.const 0xffffffff) | |
| ) | |
| (i32.const 11) | |
| ) | |
| (i32.load | |
| (i32.const 0) | |
| ) | |
| ) | |
| ) | |
| ;; ALWAYS: (func $target (type $1) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (param $4 i32) (param $5 i32) | |
| ;; ALWAYS-NEXT: ) | |
| ;; CAREFUL: (func $target (type $1) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (param $4 i32) (param $5 i32) | |
| ;; CAREFUL-NEXT: (nop) | |
| ;; CAREFUL-NEXT: ) | |
| (func $target (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) | |
| ) | |
| ) | |
| ;; ALWAYS: (func $target_2 (type $2) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (param $4 i32) | |
| ;; ALWAYS-NEXT: (local $5 i32) | |
| ;; ALWAYS-NEXT: (local $6 i32) | |
| ;; ALWAYS-NEXT: (local $7 i32) | |
| ;; ALWAYS-NEXT: (local $8 i32) | |
| ;; ALWAYS-NEXT: (local $9 i32) | |
| ;; ALWAYS-NEXT: (local $10 i32) | |
| ;; ALWAYS-NEXT: (local.set $5 | |
| ;; ALWAYS-NEXT: (local.get $0) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (local.set $6 | |
| ;; ALWAYS-NEXT: (local.get $1) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (local.set $7 | |
| ;; ALWAYS-NEXT: (local.get $2) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (local.set $8 | |
| ;; ALWAYS-NEXT: (local.get $3) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (local.set $9 | |
| ;; ALWAYS-NEXT: (local.get $4) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (local.set $10 | |
| ;; ALWAYS-NEXT: (i32.load | |
| ;; ALWAYS-NEXT: (i32.const 0) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: (block | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| (module | |
| ;; ALWAYS: (type $0 (func)) | |
| ;; ALWAYS: (type $1 (func (param f32))) | |
| ;; ALWAYS: (func $caller (type $0) | |
| ;; ALWAYS-NEXT: (local $tuple (tuple i32 f32)) | |
| ;; ALWAYS-NEXT: (call $target | |
| ;; ALWAYS-NEXT: (tuple.extract 2 1 | |
| ;; ALWAYS-NEXT: (local.get $tuple) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; ALWAYS-NEXT: ) | |
| ;; CAREFUL: (type $0 (func)) | |
| ;; CAREFUL: (type $1 (func (param f32))) | |
| ;; CAREFUL: (func $caller (type $0) | |
| ;; CAREFUL-NEXT: (local $tuple (tuple i32 f32)) | |
| ;; CAREFUL-NEXT: (call $target | |
| ;; CAREFUL-NEXT: (tuple.extract 2 1 | |
| ;; CAREFUL-NEXT: (local.get $tuple) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: ) | |
| ;; CAREFUL-NEXT: ) | |
| (func $caller | |
| (local $tuple (tuple i32 f32)) | |
| ;; We cannot move the tuple.extract into the context, as that would mean the | |
| ;; new call has a tuple param. Rather than handle that somehow, ignore it. | |
| (call $target | |
| (tuple.extract 2 1 | |
| (local.get $tuple) | |
| ) | |
| ) | |
| ) | |
| ;; ALWAYS: (func $target (type $1) (param $0 f32) | |
| ;; ALWAYS-NEXT: ) | |
| ;; CAREFUL: (func $target (type $1) (param $0 f32) | |
| ;; CAREFUL-NEXT: ) | |
| (func $target (param $0 f32) | |
| ) | |
| ) | |