| ;; 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) |
| ) |
| ) |
| |