| ;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited. | |
| ;; RUN: wasm-opt %s --remove-unused-names --precompute-propagate --fuzz-exec -all -S -o - \ | |
| ;; RUN: | filecheck %s | |
| (module | |
| ;; CHECK: (global $g (mut i32) (i32.const 10)) | |
| (global $g (mut i32) (i32.const 10)) | |
| ;; CHECK: (func $loop (type $1) | |
| ;; CHECK-NEXT: (local $temp i32) | |
| ;; CHECK-NEXT: (local.set $temp | |
| ;; CHECK-NEXT: (i32.const 10) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK-NEXT: ) | |
| (func $loop | |
| (local $temp i32) | |
| ;; We should not try to precompute this loop. If we attempted to replace it | |
| ;; with its children, we'd need to handle the effects of children properly, | |
| ;; which we do not do in this pass. | |
| (loop | |
| (local.set $temp | |
| (i32.const 10) | |
| ) | |
| ) | |
| ) | |
| ;; CHECK: (func $local.set (type $1) | |
| ;; CHECK-NEXT: (local $temp i32) | |
| ;; CHECK-NEXT: (local.set $temp | |
| ;; CHECK-NEXT: (i32.const 10) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK-NEXT: (drop | |
| ;; CHECK-NEXT: (local.tee $temp | |
| ;; CHECK-NEXT: (i32.const 20) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK-NEXT: ) | |
| (func $local.set | |
| (local $temp i32) | |
| ;; We should not try to precompute a set or tee. | |
| (local.set $temp | |
| (i32.const 10) | |
| ) | |
| (drop | |
| (local.tee $temp | |
| (i32.const 20) | |
| ) | |
| ) | |
| ) | |
| ;; CHECK: (func $global.set (type $1) | |
| ;; CHECK-NEXT: (global.set $g | |
| ;; CHECK-NEXT: (i32.const 20) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK-NEXT: ) | |
| (func $global.set | |
| ;; We should not try to precompute a global set. | |
| (global.set $g | |
| (i32.const 20) | |
| ) | |
| ) | |
| ;; CHECK: (func $binary-tee (type $0) (result i32) | |
| ;; CHECK-NEXT: (local $temp i32) | |
| ;; CHECK-NEXT: (drop | |
| ;; CHECK-NEXT: (local.tee $temp | |
| ;; CHECK-NEXT: (i32.const 10) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK-NEXT: (i32.const 20) | |
| ;; CHECK-NEXT: ) | |
| (func $binary-tee (result i32) | |
| (local $temp i32) | |
| ;; We can precompute this and remove the add, but must keep the tee. | |
| (i32.add | |
| (local.tee $temp | |
| (i32.const 10) | |
| ) | |
| (local.get $temp) | |
| ) | |
| ) | |
| ;; CHECK: (func $binary-tee-2 (type $0) (result i32) | |
| ;; CHECK-NEXT: (local $temp i32) | |
| ;; CHECK-NEXT: (drop | |
| ;; CHECK-NEXT: (local.tee $temp | |
| ;; CHECK-NEXT: (i32.const 10) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK-NEXT: (i32.const 10) | |
| ;; CHECK-NEXT: ) | |
| (func $binary-tee-2 (result i32) | |
| (local $temp i32) | |
| ;; A tee on the other side. | |
| (i32.add | |
| (local.get $temp) | |
| (local.tee $temp | |
| (i32.const 10) | |
| ) | |
| ) | |
| ) | |
| ;; CHECK: (func $binary-both (type $0) (result i32) | |
| ;; CHECK-NEXT: (local $temp i32) | |
| ;; CHECK-NEXT: (drop | |
| ;; CHECK-NEXT: (local.tee $temp | |
| ;; CHECK-NEXT: (i32.const 10) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK-NEXT: (drop | |
| ;; CHECK-NEXT: (local.tee $temp | |
| ;; CHECK-NEXT: (i32.const 20) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK-NEXT: (i32.const 30) | |
| ;; CHECK-NEXT: ) | |
| (func $binary-both (result i32) | |
| (local $temp i32) | |
| ;; Now we must keep both tees. | |
| (i32.add | |
| (local.tee $temp | |
| (i32.const 10) | |
| ) | |
| (local.tee $temp | |
| (i32.const 20) | |
| ) | |
| ) | |
| ) | |
| ;; CHECK: (func $nested-global (type $0) (result i32) | |
| ;; CHECK-NEXT: (local $temp i32) | |
| ;; CHECK-NEXT: (drop | |
| ;; CHECK-NEXT: (block (result i32) | |
| ;; CHECK-NEXT: (local.set $temp | |
| ;; CHECK-NEXT: (i32.const 10) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK-NEXT: (i32.const 20) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK-NEXT: (drop | |
| ;; CHECK-NEXT: (block (result i32) | |
| ;; CHECK-NEXT: (global.set $g | |
| ;; CHECK-NEXT: (i32.const 30) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK-NEXT: (i32.const 40) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK-NEXT: (i32.const 60) | |
| ;; CHECK-NEXT: ) | |
| (func $nested-global (result i32) | |
| (local $temp i32) | |
| ;; Nested effects inside arms, and one is a global effect. | |
| (i32.add | |
| (block (result i32) | |
| (local.set $temp | |
| (i32.const 10) | |
| ) | |
| (i32.const 20) | |
| ) | |
| (block (result i32) | |
| (global.set $g | |
| (i32.const 30) | |
| ) | |
| (i32.const 40) | |
| ) | |
| ) | |
| ) | |
| ;; CHECK: (func $if (type $0) (result i32) | |
| ;; CHECK-NEXT: (i32.const 2) | |
| ;; CHECK-NEXT: ) | |
| (func $if (result i32) | |
| ;; We precompute simple ifs. | |
| (if (result i32) | |
| (i32.const 1) | |
| (then | |
| (i32.const 2) | |
| ) | |
| (else | |
| (i32.const 3) | |
| ) | |
| ) | |
| ) | |
| ;; CHECK: (func $if-no (type $0) (result i32) | |
| ;; CHECK-NEXT: (if (result i32) | |
| ;; CHECK-NEXT: (i32.const 1) | |
| ;; CHECK-NEXT: (then | |
| ;; CHECK-NEXT: (global.set $g | |
| ;; CHECK-NEXT: (i32.const 20) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK-NEXT: (i32.const 2) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK-NEXT: (else | |
| ;; CHECK-NEXT: (i32.const 3) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK-NEXT: ) | |
| (func $if-no (result i32) | |
| ;; We do not precompute ifs with effects. | |
| (if (result i32) | |
| (i32.const 1) | |
| (then | |
| (block (result i32) | |
| (global.set $g | |
| (i32.const 20) | |
| ) | |
| (i32.const 2) | |
| ) | |
| ) | |
| (else | |
| (i32.const 3) | |
| ) | |
| ) | |
| ) | |
| ;; CHECK: (func $try (type $0) (result i32) | |
| ;; CHECK-NEXT: (try (result i32) | |
| ;; CHECK-NEXT: (do | |
| ;; CHECK-NEXT: (i32.const 1) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK-NEXT: (catch_all | |
| ;; CHECK-NEXT: (i32.const 2) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK-NEXT: ) | |
| (func $try (result i32) | |
| ;; We don't precompute trys. | |
| (try (result i32) | |
| (do | |
| (i32.const 1) | |
| ) | |
| (catch_all | |
| (i32.const 2) | |
| ) | |
| ) | |
| ) | |
| ;; CHECK: (func $ordering (type $0) (result i32) | |
| ;; CHECK-NEXT: (local $temp i32) | |
| ;; CHECK-NEXT: (block $out (result i32) | |
| ;; CHECK-NEXT: (select | |
| ;; CHECK-NEXT: (block (result i32) | |
| ;; CHECK-NEXT: (local.set $temp | |
| ;; CHECK-NEXT: (i32.const 0) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK-NEXT: (i32.const 20) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK-NEXT: (br $out | |
| ;; CHECK-NEXT: (i32.const 10) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK-NEXT: (block (result i32) | |
| ;; CHECK-NEXT: (global.set $g | |
| ;; CHECK-NEXT: (i32.const 30) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK-NEXT: (i32.const 40) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK-NEXT: ) | |
| ;; CHECK-NEXT: ) | |
| (func $ordering (result i32) | |
| (local $temp i32) | |
| ;; Nested effects inside arms. The br in the middle arm will execute, so we | |
| ;; want to precompute the entire select into a br, but we must keep alive the | |
| ;; children before and after. While doing so, we must not *reorder* the middle | |
| ;; child against them: if we just remove the middle child (and add a br at the | |
| ;; end) then we are changing the order of execution, as the global.set would | |
| ;; happen, when before it did not. For simplicity, we do not optimize here. | |
| (block $out (result i32) | |
| (select | |
| (block (result i32) | |
| (local.set $temp | |
| (i32.const 0) | |
| ) | |
| (i32.const 20) | |
| ) | |
| (block (result i32) | |
| (br $out | |
| (i32.const 10) | |
| ) | |
| (i32.const 20) | |
| ) | |
| (block (result i32) | |
| (global.set $g | |
| (i32.const 30) | |
| ) | |
| (i32.const 40) | |
| ) | |
| ) | |
| ) | |
| ) | |
| ) | |