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