;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited. ;; RUN: wasm-opt %s --simplify-locals -all -S -o - | filecheck %s (module ;; CHECK: (tag $e-i32 (param i32)) (tag $e-i32 (param i32)) ;; CHECK: (func $bar (type $1) (result i32) ;; CHECK-NEXT: (i32.const 3) ;; CHECK-NEXT: ) (func $bar (result i32) (i32.const 3)) ;; CHECK: (func $call-cannot-be-sinked-into-try_table (type $2) ;; CHECK-NEXT: (local $0 i32) ;; CHECK-NEXT: (local.set $0 ;; CHECK-NEXT: (call $bar) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (block $tryend ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (block $catch (result i32) ;; CHECK-NEXT: (try_table (catch $e-i32 $catch) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (local.get $0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (br $tryend) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $call-cannot-be-sinked-into-try_table (local $0 i32) (drop ;; This local.tee should NOT be sinked into 'try_table' below, because it ;; may throw (local.tee $0 (call $bar)) ) (block $tryend (drop (block $catch (result i32) (try_table (catch $e-i32 $catch) (drop (local.get $0)) ) (br $tryend) ) ) ) ) ;; CHECK: (func $non-call-can-be-sinked-into-try_table (type $2) ;; CHECK-NEXT: (local $0 i32) ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: (block $tryend ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (block $catch (result i32) ;; CHECK-NEXT: (try_table (catch $e-i32 $catch) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (i32.const 3) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (br $tryend) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $non-call-can-be-sinked-into-try_table (local $0 i32) (drop ;; This local.tee can be sinked into 'try_table' below, because it cannot ;; throw (local.tee $0 (i32.const 3)) ) (block $tryend (drop (block $catch (result i32) (try_table (catch $e-i32 $catch) (drop (local.get $0)) ) (br $tryend) ) ) ) ) ;; CHECK: (func $return-call-can-be-sinked-into-try_table (type $1) (result i32) ;; CHECK-NEXT: (local $0 i32) ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: (block $tryend (result i32) ;; CHECK-NEXT: (try_table (result i32) (catch $e-i32 $tryend) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (if (result i32) ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: (then ;; CHECK-NEXT: (return_call $return-call-can-be-sinked-into-try_table) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (else ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $return-call-can-be-sinked-into-try_table (result i32) (local $0 i32) (drop ;; This cannot throw either, so it can be sunk. Wrap the return_call in an ;; if so the whole expression does not return unconditionally. (local.tee $0 (if (result i32) (i32.const 0) (then (return_call $return-call-can-be-sinked-into-try_table) ) (else (i32.const 1) ) ) ) ) (block $tryend (result i32) (try_table (result i32) (catch $e-i32 $tryend) (drop (local.get $0)) (i32.const 0) ) ) ) ;; CHECK: (func $equivalent-set-removal-call (type $0) (param $0 i32) ;; CHECK-NEXT: (local $1 i32) ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (local.get $0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: (call $equivalent-set-removal-call ;; CHECK-NEXT: (i32.const 2) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (local.get $0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (local.get $0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $equivalent-set-removal-call (param $0 i32) (local $1 i32) (local.set $1 (local.get $0)) (drop (local.get $0)) (drop (local.get $1)) ;; Even with EH enabled we can look past the call and optimize the final 1 ;; to a 0, since they contain the same (and while the call might branch, ;; such a branch does not cause a problem here, as if we branch we just ;; don't reach the change later down). (call $equivalent-set-removal-call (i32.const 2) ) (drop (local.get $0)) (drop (local.get $1)) ) ;; CHECK: (func $equivalent-set-removal-if (type $3) (param $p i32) (param $0 i32) ;; CHECK-NEXT: (local $1 i32) ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (local.get $0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (local.set $1 ;; CHECK-NEXT: (local.get $0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (if ;; CHECK-NEXT: (local.get $p) ;; CHECK-NEXT: (then ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (local.get $0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (local.get $0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (else ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (local.get $0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (local.get $1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (local.get $0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (local.get $1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $equivalent-set-removal-if (param $p i32) (param $0 i32) (local $1 i32) (local.set $1 (local.get $0)) (drop (local.get $0)) ;; This local.get of 1 can be of 0. (drop (local.get $1)) (if (local.get $p) (then (block ;; We also optimize in this block, which is adjacent to the code before ;; us. It is valid to optimize the 1 to a 0 here, as it is dominated by ;; the code earlier. (drop (local.get $0)) (drop (local.get $1)) ) ) (else (block ;; We could also optimize here, but atm just look at code adjacent to ;; its dominator. TODO (drop (local.get $0)) (drop (local.get $1)) ) ) ) ;; As in the else, this could be optimized. TODO (drop (local.get $0)) (drop (local.get $1)) ) )