blob: cbec8495f43704ce42339dd5288721d55a66ee7f [file] [edit]
;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited.
;; RUN: wasm-opt %s --constraint-analysis -all -S -o - | filecheck %s
(module
;; CHECK: (import "a" "b" (func $import (type $3) (result i32)))
(import "a" "b" (func $import (result i32)))
;; CHECK: (func $infinite-loop (type $0)
;; CHECK-NEXT: (local $x i32)
;; CHECK-NEXT: (loop $loop
;; CHECK-NEXT: (local.set $x
;; CHECK-NEXT: (i32.add
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (br $loop)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $infinite-loop
(local $x i32)
;; An infinite loop. We should not hang, but nothing can be optimized.
(loop $loop
(local.set $x
(i32.add
(local.get $x)
(i32.const 1)
)
)
(br $loop)
)
)
;; CHECK: (func $almost-infinite-loop (type $0)
;; CHECK-NEXT: (local $x i32)
;; CHECK-NEXT: (loop $loop
;; CHECK-NEXT: (local.set $x
;; CHECK-NEXT: (i32.add
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (br_if $loop
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $almost-infinite-loop
(local $x i32)
;; A loop that continues until an overflow happens. We should not hang, but
;; nothing can be optimized.
(loop $loop
(local.set $x
(i32.add
(local.get $x)
(i32.const 1)
)
)
;; Stop looping after we go all the way back to 0.
(br_if $loop
(local.get $x)
)
)
)
;; CHECK: (func $infinite-loop-with-branch (type $0)
;; CHECK-NEXT: (local $x i32)
;; CHECK-NEXT: (loop $loop
;; CHECK-NEXT: (if
;; CHECK-NEXT: (i32.gt_s
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (then
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $x
;; CHECK-NEXT: (i32.add
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (br $loop)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $infinite-loop-with-branch
(local $x i32)
;; An infinite loop that *looks* like it might not be infinite. We have a
;; branch with a loop-like condition, but it is just a nop.
(loop $loop
(if
(i32.gt_s
(local.get $x)
(i32.const 0)
)
(then
(nop)
)
)
(local.set $x
(i32.add
(local.get $x)
(i32.const 1)
)
)
(br $loop)
)
)
;; CHECK: (func $bound (type $0)
;; CHECK-NEXT: (local $x i32)
;; CHECK-NEXT: (loop $loop
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $x
;; CHECK-NEXT: (call $import)
;; CHECK-NEXT: )
;; CHECK-NEXT: (if
;; CHECK-NEXT: (i32.gt_s
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (then
;; CHECK-NEXT: (if
;; CHECK-NEXT: (i32.le_s
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (i32.const 100)
;; CHECK-NEXT: )
;; CHECK-NEXT: (then
;; CHECK-NEXT: (br $loop)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $bound
(local $x i32)
(loop $loop
;; We arrive at the loop top with {x == 0} || {x > 0 && x <= 100}. Those
;; OR into {x >= 0 && x <= 100} - that is, the x == 0 and x > 0 combine
;; into x >= 0.
(drop
(i32.ge_s
(local.get $x)
(i32.const 0)
)
)
(drop
(i32.le_s
(local.get $x)
(i32.const 100)
)
)
;; Set $x to an unknown value before applying the constraints below on the
;; way back to the loop top.
(local.set $x
(call $import)
)
(if
(i32.gt_s
(local.get $x)
(i32.const 0)
)
(then
(if
(i32.le_s
(local.get $x)
(i32.const 100)
)
(then
(br $loop)
)
)
)
)
)
)
;; CHECK: (func $bound-flipped-ifs (type $0)
;; CHECK-NEXT: (local $x i32)
;; CHECK-NEXT: (loop $loop
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $x
;; CHECK-NEXT: (call $import)
;; CHECK-NEXT: )
;; CHECK-NEXT: (if
;; CHECK-NEXT: (i32.le_s
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (i32.const 100)
;; CHECK-NEXT: )
;; CHECK-NEXT: (then
;; CHECK-NEXT: (if
;; CHECK-NEXT: (i32.gt_s
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (then
;; CHECK-NEXT: (br $loop)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $bound-flipped-ifs
(local $x i32)
;; As above, but with the ifs flipped. We optimize the same way.
(loop $loop
(drop
(i32.ge_s
(local.get $x)
(i32.const 0)
)
)
(drop
(i32.le_s
(local.get $x)
(i32.const 100)
)
)
(local.set $x
(call $import)
)
(if
(i32.le_s
(local.get $x)
(i32.const 100)
)
(then
(if
(i32.gt_s
(local.get $x)
(i32.const 0)
)
(then
(br $loop)
)
)
)
)
)
)
;; CHECK: (func $bound-nonconstant-no (type $1) (param $p i32)
;; CHECK-NEXT: (local $x i32)
;; CHECK-NEXT: (loop $loop
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.ge_s
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (local.get $p)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.le_s
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (local.get $p)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $x
;; CHECK-NEXT: (call $import)
;; CHECK-NEXT: )
;; CHECK-NEXT: (if
;; CHECK-NEXT: (i32.gt_s
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (local.get $p)
;; CHECK-NEXT: )
;; CHECK-NEXT: (then
;; CHECK-NEXT: (if
;; CHECK-NEXT: (i32.le_s
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (i32.const 100)
;; CHECK-NEXT: )
;; CHECK-NEXT: (then
;; CHECK-NEXT: (br $loop)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $bound-nonconstant-no (param $p i32)
(local $x i32)
;; As above, but rather than zero we have an unknown param $p.
(loop $loop
;; We can infer nothing here, as p is unknown, and x might be 0 or <= 100.
(drop
(i32.ge_s
(local.get $x)
(local.get $p)
)
)
(drop
(i32.le_s
(local.get $x)
(local.get $p)
)
)
(local.set $x
(call $import)
)
(if
(i32.gt_s
(local.get $x)
(local.get $p)
)
(then
(if
(i32.le_s
(local.get $x)
(i32.const 100)
)
(then
(br $loop)
)
)
)
)
)
)
;; CHECK: (func $bound-incremented (type $0)
;; CHECK-NEXT: (local $x i32)
;; CHECK-NEXT: (loop $loop
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $x
;; CHECK-NEXT: (i32.add
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (if
;; CHECK-NEXT: (i32.lt_s
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (i32.const 100)
;; CHECK-NEXT: )
;; CHECK-NEXT: (then
;; CHECK-NEXT: (br $loop)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $bound-incremented
(local $x i32)
(loop $loop
;; A realistic do-while loop, with $x++ and a bounds check. We can infer
;; that no overflow happens, and prove these two checks are true.
(drop
(i32.lt_s
(local.get $x)
(i32.const 100)
)
)
(drop
(i32.ge_s
(local.get $x)
(i32.const 0)
)
)
;; This changed compared to previous testcases: now we have x++.
(local.set $x
(i32.add
(local.get $x)
(i32.const 1)
)
)
(if
(i32.lt_s
(local.get $x)
(i32.const 100)
)
(then
(br $loop)
)
)
)
)
;; CHECK: (func $bound-incremented-unsigned (type $0)
;; CHECK-NEXT: (local $x i32)
;; CHECK-NEXT: (loop $loop
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $x
;; CHECK-NEXT: (i32.add
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (if
;; CHECK-NEXT: (i32.lt_u
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (i32.const 100)
;; CHECK-NEXT: )
;; CHECK-NEXT: (then
;; CHECK-NEXT: (br $loop)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $bound-incremented-unsigned
;; As above, but with unsigned operations. Again, we optimize these to 1.
(local $x i32)
(loop $loop
(drop
(i32.lt_u
(local.get $x)
(i32.const 100)
)
)
(drop
(i32.ge_u
(local.get $x)
(i32.const 0)
)
)
(local.set $x
(i32.add
(local.get $x)
(i32.const 1)
)
)
(if
(i32.lt_u
(local.get $x)
(i32.const 100)
)
(then
(br $loop)
)
)
)
)
;; CHECK: (func $bound-incremented-while (type $0)
;; CHECK-NEXT: (local $x i32)
;; CHECK-NEXT: (block $out
;; CHECK-NEXT: (loop $loop
;; CHECK-NEXT: (if
;; CHECK-NEXT: (i32.ge_s
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (i32.const 100)
;; CHECK-NEXT: )
;; CHECK-NEXT: (then
;; CHECK-NEXT: (br $out)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $x
;; CHECK-NEXT: (i32.add
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (br $loop)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $bound-incremented-while
;; Similar to above, but before we had a do-while loop (loop condition at
;; the bottom) and now it is at the top.
(local $x i32)
(block $out
(loop $loop
;; Conditional branch at the top.
(if
(i32.ge_s
(local.get $x)
(i32.const 100)
)
(then
(br $out)
)
)
;; We can infer both of these to be 1.
(drop
(i32.lt_s
(local.get $x)
(i32.const 100)
)
)
(drop
(i32.ge_s
(local.get $x)
(i32.const 0)
)
)
(local.set $x
(i32.add
(local.get $x)
(i32.const 1)
)
)
;; Unconditional branch at the bottom.
(br $loop)
)
)
)
;; CHECK: (func $bound-incremented-inc-first (type $0)
;; CHECK-NEXT: (local $x i32)
;; CHECK-NEXT: (block $out
;; CHECK-NEXT: (loop $loop
;; CHECK-NEXT: (local.set $x
;; CHECK-NEXT: (i32.add
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (if
;; CHECK-NEXT: (i32.ge_s
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (i32.const 100)
;; CHECK-NEXT: )
;; CHECK-NEXT: (then
;; CHECK-NEXT: (br $out)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (br $loop)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $bound-incremented-inc-first
;; Similar to the above "while" loop, but now with the increment before the
;; if.
(local $x i32)
(block $out
(loop $loop
;; Increment at the top.
(local.set $x
(i32.add
(local.get $x)
(i32.const 1)
)
)
;; If after the increment.
(if
(i32.ge_s
(local.get $x)
(i32.const 100)
)
(then
(br $out)
)
)
;; x > 0 && x < 100 here (0 is impossible, compared to before).
(drop
(i32.gt_s
(local.get $x)
(i32.const 0)
)
)
(drop
(i32.lt_s
(local.get $x)
(i32.const 100)
)
)
(br $loop)
)
)
)
;; CHECK: (func $bound-incremented-inc-first-less (type $0)
;; CHECK-NEXT: (local $x i32)
;; CHECK-NEXT: (block $out
;; CHECK-NEXT: (loop $loop
;; CHECK-NEXT: (local.set $x
;; CHECK-NEXT: (i32.add
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (if
;; CHECK-NEXT: (i32.gt_s
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (i32.const 100)
;; CHECK-NEXT: )
;; CHECK-NEXT: (then
;; CHECK-NEXT: (br $out)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (br $loop)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $bound-incremented-inc-first-less
;; As in the last testcase, but the if's condition changed.
(local $x i32)
(block $out
(loop $loop
(local.set $x
(i32.add
(local.get $x)
(i32.const 1)
)
)
;; Before we left the loop when x >= 100. Now we leave when x > 100,
;; so we do actually reach 100 in the code below.
(if
(i32.gt_s
(local.get $x)
(i32.const 100)
)
(then
(br $out)
)
)
;; x > 0 && x <= 100 here.
(drop
(i32.gt_s
(local.get $x)
(i32.const 0)
)
)
(drop
(i32.le_s
(local.get $x)
(i32.const 100)
)
)
(br $loop)
)
)
)
;; CHECK: (func $bound-incremented-inc-first-less-unsigned (type $0)
;; CHECK-NEXT: (local $x i32)
;; CHECK-NEXT: (block $out
;; CHECK-NEXT: (loop $loop
;; CHECK-NEXT: (local.set $x
;; CHECK-NEXT: (i32.add
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (if
;; CHECK-NEXT: (i32.gt_u
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (i32.const 100)
;; CHECK-NEXT: )
;; CHECK-NEXT: (then
;; CHECK-NEXT: (br $out)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (br $loop)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $bound-incremented-inc-first-less-unsigned
;; As in the last testcase, but unsigned.
(local $x i32)
(block $out
(loop $loop
(local.set $x
(i32.add
(local.get $x)
(i32.const 1)
)
)
(if
(i32.gt_u
(local.get $x)
(i32.const 100)
)
(then
(br $out)
)
)
;; x > 0 && x <= 100 here.
(drop
(i32.gt_u
(local.get $x)
(i32.const 0)
)
)
(drop
(i32.le_u
(local.get $x)
(i32.const 100)
)
)
(br $loop)
)
)
)
;; CHECK: (func $extra-constraint (type $0)
;; CHECK-NEXT: (local $x i32)
;; CHECK-NEXT: (block $out
;; CHECK-NEXT: (loop $loop
;; CHECK-NEXT: (local.set $x
;; CHECK-NEXT: (i32.add
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (if
;; CHECK-NEXT: (i32.gt_u
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (i32.const 100)
;; CHECK-NEXT: )
;; CHECK-NEXT: (then
;; CHECK-NEXT: (br $out)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (if
;; CHECK-NEXT: (i32.eq
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: )
;; CHECK-NEXT: (then
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (br $loop)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $extra-constraint
;; As in the last testcase, but with extra code below.
(local $x i32)
(block $out
(loop $loop
(local.set $x
(i32.add
(local.get $x)
(i32.const 1)
)
)
(if
(i32.gt_u
(local.get $x)
(i32.const 100)
)
(then
(br $out)
)
)
;; An extra constraint gets added to the local here. We should still be
;; able to optimize the things below.
(if
(i32.eq
(local.get $x)
(i32.const 42)
)
(then
(unreachable)
)
)
(drop
(i32.gt_u
(local.get $x)
(i32.const 0)
)
)
(drop
(i32.le_u
(local.get $x)
(i32.const 100)
)
)
;; And we also optimize that extra constraint: this is true. Note that
;; this shows we optimize three separate constraints (a range 0-100 plus
;; an additional fact, i.e., we can optimize more than simple numerical
;; spans).
(drop
(i32.ne
(local.get $x)
(i32.const 42)
)
)
(br $loop)
)
)
)
;; CHECK: (func $while-nonconstant (type $1) (param $len i32)
;; CHECK-NEXT: (local $x i32)
;; CHECK-NEXT: (block $out
;; CHECK-NEXT: (loop $loop
;; CHECK-NEXT: (if
;; CHECK-NEXT: (i32.ge_s
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (local.get $len)
;; CHECK-NEXT: )
;; CHECK-NEXT: (then
;; CHECK-NEXT: (br $out)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $x
;; CHECK-NEXT: (i32.add
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (br $loop)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $while-nonconstant (param $len i32)
;; A while loop whose upper bound is a local and not a constant.
(local $x i32)
(block $out
(loop $loop
(if
(i32.ge_s
(local.get $x)
(local.get $len)
)
(then
(br $out)
)
)
;; We can infer both of these to be 1.
(drop
(i32.lt_s
(local.get $x)
(local.get $len)
)
)
(drop
(i32.ge_s
(local.get $x)
(i32.const 0)
)
)
(local.set $x
(i32.add
(local.get $x)
(i32.const 1)
)
)
(br $loop)
)
)
)
;; CHECK: (func $while-nonconstant-unsigned (type $1) (param $len i32)
;; CHECK-NEXT: (local $x i32)
;; CHECK-NEXT: (block $out
;; CHECK-NEXT: (loop $loop
;; CHECK-NEXT: (if
;; CHECK-NEXT: (i32.ge_u
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (local.get $len)
;; CHECK-NEXT: )
;; CHECK-NEXT: (then
;; CHECK-NEXT: (br $out)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $x
;; CHECK-NEXT: (i32.add
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (br $loop)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $while-nonconstant-unsigned (param $len i32)
;; As above, but unsigned.
(local $x i32)
(block $out
(loop $loop
(if
(i32.ge_u
(local.get $x)
(local.get $len)
)
(then
(br $out)
)
)
;; We can infer both of these to be 1.
(drop
(i32.lt_u
(local.get $x)
(local.get $len)
)
)
(drop
(i32.ge_u
(local.get $x)
(i32.const 0)
)
)
(local.set $x
(i32.add
(local.get $x)
(i32.const 1)
)
)
(br $loop)
)
)
)
;; CHECK: (func $do-nonconstant (type $1) (param $len i32)
;; CHECK-NEXT: (local $x i32)
;; CHECK-NEXT: (if
;; CHECK-NEXT: (i32.lt_s
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (local.get $len)
;; CHECK-NEXT: )
;; CHECK-NEXT: (then
;; CHECK-NEXT: (loop $loop
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $x
;; CHECK-NEXT: (i32.add
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (br_if $loop
;; CHECK-NEXT: (i32.lt_s
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (local.get $len)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $do-nonconstant (param $len i32)
;; A do loop whose upper bound is a local and not a constant. The loop must
;; be entered only if at least one iteration would run.
(local $x i32)
(if
(i32.lt_s
(local.get $x)
(local.get $len)
)
(then
(loop $loop
;; We can infer both of these to be 1.
(drop
(i32.lt_s
(local.get $x)
(local.get $len)
)
)
(drop
(i32.ge_s
(local.get $x)
(i32.const 0)
)
)
(local.set $x
(i32.add
(local.get $x)
(i32.const 1)
)
)
(br_if $loop
(i32.lt_s
(local.get $x)
(local.get $len)
)
)
)
)
)
)
;; CHECK: (func $do-nonconstant-unsigned (type $1) (param $len i32)
;; CHECK-NEXT: (local $x i32)
;; CHECK-NEXT: (if
;; CHECK-NEXT: (i32.lt_u
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (local.get $len)
;; CHECK-NEXT: )
;; CHECK-NEXT: (then
;; CHECK-NEXT: (loop $loop
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $x
;; CHECK-NEXT: (i32.add
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (br_if $loop
;; CHECK-NEXT: (i32.lt_u
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (local.get $len)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $do-nonconstant-unsigned (param $len i32)
;; As above, but unsigned.
(local $x i32)
(if
(i32.lt_u
(local.get $x)
(local.get $len)
)
(then
(loop $loop
;; We can infer both of these to be 1.
(drop
(i32.lt_u
(local.get $x)
(local.get $len)
)
)
(drop
(i32.ge_u
(local.get $x)
(i32.const 0)
)
)
(local.set $x
(i32.add
(local.get $x)
(i32.const 1)
)
)
(br_if $loop
(i32.lt_u
(local.get $x)
(local.get $len)
)
)
)
)
)
)
;; CHECK: (func $nested (type $1) (param $len i32)
;; CHECK-NEXT: (local $i i32)
;; CHECK-NEXT: (local $j i32)
;; CHECK-NEXT: (block $out
;; CHECK-NEXT: (loop $outer
;; CHECK-NEXT: (if
;; CHECK-NEXT: (i32.ge_s
;; CHECK-NEXT: (local.get $i)
;; CHECK-NEXT: (local.get $len)
;; CHECK-NEXT: )
;; CHECK-NEXT: (then
;; CHECK-NEXT: (br $out)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $i
;; CHECK-NEXT: (i32.add
;; CHECK-NEXT: (local.get $i)
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (block $in
;; CHECK-NEXT: (local.set $j
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (loop $inner
;; CHECK-NEXT: (if
;; CHECK-NEXT: (i32.ge_s
;; CHECK-NEXT: (local.get $j)
;; CHECK-NEXT: (local.get $i)
;; CHECK-NEXT: )
;; CHECK-NEXT: (then
;; CHECK-NEXT: (br $in)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $j
;; CHECK-NEXT: (i32.add
;; CHECK-NEXT: (local.get $j)
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (br $inner)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (br $outer)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $nested (param $len i32)
(local $i i32)
(local $j i32)
(block $out
;; Outer loop, i goes from 0 to len.
(loop $outer
(if
(i32.ge_s
(local.get $i)
(local.get $len)
)
(then
(br $out)
)
)
;; We can infer both of these to be 1.
(drop
(i32.lt_s
(local.get $i)
(local.get $len)
)
)
(drop
(i32.ge_s
(local.get $i)
(i32.const 0)
)
)
(local.set $i
(i32.add
(local.get $i)
(i32.const 1)
)
)
;; Inner loop, j goes from 0 to i.
(block $in
(local.set $j
(i32.const 0)
)
(loop $inner
(if
(i32.ge_s
(local.get $j)
(local.get $i)
)
(then
(br $in)
)
)
;; We can infer both of these to be 1.
(drop
(i32.lt_s
(local.get $j)
(local.get $i)
)
)
(drop
(i32.ge_s
(local.get $j)
(i32.const 0)
)
)
(local.set $j
(i32.add
(local.get $j)
(i32.const 1)
)
)
(br $inner)
)
)
(br $outer)
)
)
)
;; CHECK: (func $nested-increment-i-later (type $1) (param $len i32)
;; CHECK-NEXT: (local $i i32)
;; CHECK-NEXT: (local $j i32)
;; CHECK-NEXT: (block $out
;; CHECK-NEXT: (loop $outer
;; CHECK-NEXT: (if
;; CHECK-NEXT: (i32.ge_s
;; CHECK-NEXT: (local.get $i)
;; CHECK-NEXT: (local.get $len)
;; CHECK-NEXT: )
;; CHECK-NEXT: (then
;; CHECK-NEXT: (br $out)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (block $in
;; CHECK-NEXT: (local.set $j
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (loop $inner
;; CHECK-NEXT: (if
;; CHECK-NEXT: (i32.ge_s
;; CHECK-NEXT: (local.get $j)
;; CHECK-NEXT: (local.get $i)
;; CHECK-NEXT: )
;; CHECK-NEXT: (then
;; CHECK-NEXT: (br $in)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $j
;; CHECK-NEXT: (i32.add
;; CHECK-NEXT: (local.get $j)
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (br $inner)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $i
;; CHECK-NEXT: (i32.add
;; CHECK-NEXT: (local.get $i)
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (br $outer)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $nested-increment-i-later (param $len i32)
;; As $nested, but i is incremented after the inner loop.
(local $i i32)
(local $j i32)
(block $out
(loop $outer
(if
(i32.ge_s
(local.get $i)
(local.get $len)
)
(then
(br $out)
)
)
;; We can infer both of these to be 1.
(drop
(i32.lt_s
(local.get $i)
(local.get $len)
)
)
(drop
(i32.ge_s
(local.get $i)
(i32.const 0)
)
)
;; Inner loop.
(block $in
(local.set $j
(i32.const 0)
)
(loop $inner
(if
(i32.ge_s
(local.get $j)
(local.get $i)
)
(then
(br $in)
)
)
;; We can infer both of these to be 1.
(drop
(i32.lt_s
(local.get $j)
(local.get $i)
)
)
(drop
(i32.ge_s
(local.get $j)
(i32.const 0)
)
)
(local.set $j
(i32.add
(local.get $j)
(i32.const 1)
)
)
(br $inner)
)
)
;; Outer loop's increment+branch.
(local.set $i
(i32.add
(local.get $i)
(i32.const 1)
)
)
(br $outer)
)
)
)
;; CHECK: (func $nested-from-nonconstant-no (type $1) (param $len i32)
;; CHECK-NEXT: (local $i i32)
;; CHECK-NEXT: (local $j i32)
;; CHECK-NEXT: (block $out
;; CHECK-NEXT: (loop $outer
;; CHECK-NEXT: (if
;; CHECK-NEXT: (i32.ge_s
;; CHECK-NEXT: (local.get $i)
;; CHECK-NEXT: (local.get $len)
;; CHECK-NEXT: )
;; CHECK-NEXT: (then
;; CHECK-NEXT: (br $out)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $i
;; CHECK-NEXT: (i32.add
;; CHECK-NEXT: (local.get $i)
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (block $in
;; CHECK-NEXT: (local.set $j
;; CHECK-NEXT: (local.get $i)
;; CHECK-NEXT: )
;; CHECK-NEXT: (loop $inner
;; CHECK-NEXT: (if
;; CHECK-NEXT: (i32.ge_s
;; CHECK-NEXT: (local.get $j)
;; CHECK-NEXT: (local.get $len)
;; CHECK-NEXT: )
;; CHECK-NEXT: (then
;; CHECK-NEXT: (br $in)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.ge_s
;; CHECK-NEXT: (local.get $j)
;; CHECK-NEXT: (local.get $i)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $j
;; CHECK-NEXT: (i32.add
;; CHECK-NEXT: (local.get $j)
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (br $inner)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (br $outer)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $nested-from-nonconstant-no (param $len i32)
;; As above, but the inner loop is i..len rather than 0..i
(local $i i32)
(local $j i32)
(block $out
(loop $outer
(if
(i32.ge_s
(local.get $i)
(local.get $len)
)
(then
(br $out)
)
)
;; We can infer both of these to be 1.
(drop
(i32.lt_s
(local.get $i)
(local.get $len)
)
)
(drop
(i32.ge_s
(local.get $i)
(i32.const 0)
)
)
(local.set $i
(i32.add
(local.get $i)
(i32.const 1)
)
)
(block $in
(local.set $j
(local.get $i)
)
(loop $inner
(if
(i32.ge_s
(local.get $j)
(local.get $len)
)
(then
(br $in)
)
)
;; We don't infer the second of these, as we don't extend ranges for
;; non-constant initial values yet. TODO
(drop
(i32.lt_s
(local.get $j)
(local.get $len)
)
)
(drop
(i32.ge_s
(local.get $j)
(local.get $i)
)
)
(local.set $j
(i32.add
(local.get $j)
(i32.const 1)
)
)
(br $inner)
)
)
(br $outer)
)
)
)
;; CHECK: (func $impossible-branch (type $0)
;; CHECK-NEXT: (local $x i32)
;; CHECK-NEXT: (local $y i32)
;; CHECK-NEXT: (loop $loop
;; CHECK-NEXT: (br_if $loop
;; CHECK-NEXT: (i32.lt_s
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (local.get $y)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $impossible-branch
(local $x i32)
(local $y i32)
(loop $loop
;; x == y == 0, so x < y leads to a contradiction, and we never branch
;; back up to the loop. We should not error here.
(br_if $loop
(i32.lt_s
(local.get $x)
(local.get $y)
)
)
)
)
;; CHECK: (func $impossible-branch-unsigned (type $0)
;; CHECK-NEXT: (local $x i32)
;; CHECK-NEXT: (local $y i32)
;; CHECK-NEXT: (loop $loop
;; CHECK-NEXT: (br_if $loop
;; CHECK-NEXT: (i32.lt_u
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (local.get $y)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $impossible-branch-unsigned
(local $x i32)
(local $y i32)
;; As above, but unsigned.
(loop $loop
(br_if $loop
(i32.lt_u
(local.get $x)
(local.get $y)
)
)
)
)
;; CHECK: (func $add-overflow (type $1) (param $x i32)
;; CHECK-NEXT: (if
;; CHECK-NEXT: (i32.ge_s
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (then
;; CHECK-NEXT: (local.set $x
;; CHECK-NEXT: (i32.add
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.gt_s
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $add-overflow (param $x i32)
(if
(i32.ge_s
(local.get $x)
(i32.const 0)
)
(then
;; Normally, x >= 0 and x++ lead to x > 0. However, we overflow if
;; x == MAX_INT, so we cannot optimize the dropped value below us.
(local.set $x
(i32.add
(local.get $x)
(i32.const 1)
)
)
(drop
(i32.gt_s
(local.get $x)
(i32.const 0)
)
)
)
)
)
;; CHECK: (func $add-overflow-yes (type $1) (param $x i32)
;; CHECK-NEXT: (if
;; CHECK-NEXT: (i32.ge_s
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (then
;; CHECK-NEXT: (if
;; CHECK-NEXT: (i32.le_s
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (i32.const 1000)
;; CHECK-NEXT: )
;; CHECK-NEXT: (then
;; CHECK-NEXT: (local.set $x
;; CHECK-NEXT: (i32.add
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $add-overflow-yes (param $x i32)
;; As above, but we add an inner if that does x < 1000. Now x cannot be
;; MAX_INT, so we do optimize to 1.
(if
(i32.ge_s
(local.get $x)
(i32.const 0)
)
(then
(if
(i32.le_s
(local.get $x)
(i32.const 1000)
)
(then
(local.set $x
(i32.add
(local.get $x)
(i32.const 1)
)
)
(drop
(i32.gt_s
(local.get $x)
(i32.const 0)
)
)
)
)
)
)
)
;; CHECK: (func $add-overflow-unsigned (type $1) (param $x i32)
;; CHECK-NEXT: (if
;; CHECK-NEXT: (i32.ge_u
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (then
;; CHECK-NEXT: (local.set $x
;; CHECK-NEXT: (i32.add
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.gt_u
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $add-overflow-unsigned (param $x i32)
;; As above, but unsigned. We change the 0 to 1, as x >= 0 is always true
;; for unsigned anyhow.
(if
(i32.ge_u
(local.get $x)
(i32.const 1)
)
(then
(local.set $x
(i32.add
(local.get $x)
(i32.const 1)
)
)
;; We do not optimize, due to the risk of overflow.
(drop
(i32.gt_u
(local.get $x)
(i32.const 1)
)
)
)
)
)
;; CHECK: (func $add-overflow-unsigned-yes (type $1) (param $x i32)
;; CHECK-NEXT: (if
;; CHECK-NEXT: (i32.ge_u
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (then
;; CHECK-NEXT: (if
;; CHECK-NEXT: (i32.le_u
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (i32.const 1000)
;; CHECK-NEXT: )
;; CHECK-NEXT: (then
;; CHECK-NEXT: (local.set $x
;; CHECK-NEXT: (i32.add
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $add-overflow-unsigned-yes (param $x i32)
;; As above, but unsigned.
(if
(i32.ge_u
(local.get $x)
(i32.const 1)
)
(then
(if
(i32.le_u
(local.get $x)
(i32.const 1000)
)
(then
(local.set $x
(i32.add
(local.get $x)
(i32.const 1)
)
)
;; We do optimize to 1.
(drop
(i32.gt_u
(local.get $x)
(i32.const 1)
)
)
)
)
)
)
)
;; CHECK: (func $add-overflow-unsigned-yes-nonconstant (type $2) (param $x i32) (param $y i32)
;; CHECK-NEXT: (if
;; CHECK-NEXT: (i32.ge_u
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (then
;; CHECK-NEXT: (if
;; CHECK-NEXT: (i32.lt_u
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (local.get $y)
;; CHECK-NEXT: )
;; CHECK-NEXT: (then
;; CHECK-NEXT: (local.set $x
;; CHECK-NEXT: (i32.add
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $add-overflow-unsigned-yes-nonconstant (param $x i32) (param $y i32)
;; As above, but the upper bound is not a constant, it is $y, another
;; unknown value. This is still enough: $x < $y means $x++ cannot overflow,
;; as it was not MAX_INT.
(if
(i32.ge_u
(local.get $x)
(i32.const 1)
)
(then
(if
(i32.lt_u
(local.get $x)
(local.get $y)
)
(then
(local.set $x
(i32.add
(local.get $x)
(i32.const 1)
)
)
;; We do optimize to 1.
(drop
(i32.gt_u
(local.get $x)
(i32.const 1)
)
)
)
)
)
)
)
;; CHECK: (func $add-overflow-signed-yes-nonconstant (type $2) (param $x i32) (param $y i32)
;; CHECK-NEXT: (if
;; CHECK-NEXT: (i32.ge_s
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (then
;; CHECK-NEXT: (if
;; CHECK-NEXT: (i32.lt_s
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (local.get $y)
;; CHECK-NEXT: )
;; CHECK-NEXT: (then
;; CHECK-NEXT: (local.set $x
;; CHECK-NEXT: (i32.add
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $add-overflow-signed-yes-nonconstant (param $x i32) (param $y i32)
;; Ditto, signed.
(if
(i32.ge_s
(local.get $x)
(i32.const 1)
)
(then
(if
(i32.lt_s
(local.get $x)
(local.get $y)
)
(then
(local.set $x
(i32.add
(local.get $x)
(i32.const 1)
)
)
;; We do optimize to 1.
(drop
(i32.gt_s
(local.get $x)
(i32.const 1)
)
)
)
)
)
)
)
)