| ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6 | |
| ; RUN: opt < %s -passes=instsimplify -S | FileCheck %s | |
| declare i8 @llvm.umax.i8(i8, i8) | |
| ; Structural X u>= Y proofs. | |
| ; X != X urem Y is true if X is an add nuw of A and Y. | |
| define i1 @nuw_add_ne(i8 %a, i8 %y) { | |
| ; CHECK-LABEL: define i1 @nuw_add_ne( | |
| ; CHECK-SAME: i8 [[A:%.*]], i8 [[Y:%.*]]) { | |
| ; CHECK-NEXT: ret i1 true | |
| ; | |
| %x = add nuw i8 %a, %y | |
| %rem = urem i8 %x, %y | |
| %cmp = icmp ne i8 %x, %rem | |
| ret i1 %cmp | |
| } | |
| ; X urem Y != X is true with commuted add and compare operands. | |
| define i1 @nuw_add_ne_commuted(i8 %a, i8 %y) { | |
| ; CHECK-LABEL: define i1 @nuw_add_ne_commuted( | |
| ; CHECK-SAME: i8 [[A:%.*]], i8 [[Y:%.*]]) { | |
| ; CHECK-NEXT: ret i1 true | |
| ; | |
| %x = add nuw i8 %y, %a | |
| %rem = urem i8 %x, %y | |
| %cmp = icmp ne i8 %rem, %x | |
| ret i1 %cmp | |
| } | |
| ; umax(X - (X urem Y), 1) is X - (X urem Y) if X is an add nuw | |
| ; of A and Y. | |
| define i8 @nuw_add_umax(i8 %a, i8 %y) { | |
| ; CHECK-LABEL: define i8 @nuw_add_umax( | |
| ; CHECK-SAME: i8 [[A:%.*]], i8 [[Y:%.*]]) { | |
| ; CHECK-NEXT: [[X:%.*]] = add nuw i8 [[A]], [[Y]] | |
| ; CHECK-NEXT: [[REM:%.*]] = urem i8 [[X]], [[Y]] | |
| ; CHECK-NEXT: [[SUB:%.*]] = sub nuw i8 [[X]], [[REM]] | |
| ; CHECK-NEXT: ret i8 [[SUB]] | |
| ; | |
| %x = add nuw i8 %a, %y | |
| %rem = urem i8 %x, %y | |
| %sub = sub nuw i8 %x, %rem | |
| %max = call i8 @llvm.umax.i8(i8 %sub, i8 1) | |
| ret i8 %max | |
| } | |
| ; X != X urem Y is true element-wise if X is an add nuw of A and Y. | |
| define <2 x i1> @nuw_add_ne_vector(<2 x i4> %a, <2 x i4> %y) { | |
| ; CHECK-LABEL: define <2 x i1> @nuw_add_ne_vector( | |
| ; CHECK-SAME: <2 x i4> [[A:%.*]], <2 x i4> [[Y:%.*]]) { | |
| ; CHECK-NEXT: ret <2 x i1> splat (i1 true) | |
| ; | |
| %x = add nuw <2 x i4> %a, %y | |
| %rem = urem <2 x i4> %x, %y | |
| %cmp = icmp ne <2 x i4> %x, %rem | |
| ret <2 x i1> %cmp | |
| } | |
| ; Dominating X u>= Y proofs. | |
| ; X != X urem Y is true if X u>= Y. | |
| define i1 @dom_ne(i8 %x, i8 %y) { | |
| ; CHECK-LABEL: define i1 @dom_ne( | |
| ; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) { | |
| ; CHECK-NEXT: [[ENTRY:.*:]] | |
| ; CHECK-NEXT: [[COND:%.*]] = icmp uge i8 [[X]], [[Y]] | |
| ; CHECK-NEXT: br i1 [[COND]], label %[[BODY:.*]], label %[[EXIT:.*]] | |
| ; CHECK: [[BODY]]: | |
| ; CHECK-NEXT: ret i1 true | |
| ; CHECK: [[EXIT]]: | |
| ; CHECK-NEXT: ret i1 false | |
| ; | |
| entry: | |
| %cond = icmp uge i8 %x, %y | |
| br i1 %cond, label %body, label %exit | |
| body: | |
| %rem = urem i8 %x, %y | |
| %cmp = icmp ne i8 %x, %rem | |
| ret i1 %cmp | |
| exit: | |
| ret i1 false | |
| } | |
| ; X urem Y == X is false if X u>= Y. | |
| define i1 @dom_eq_commuted(i8 %x, i8 %y) { | |
| ; CHECK-LABEL: define i1 @dom_eq_commuted( | |
| ; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) { | |
| ; CHECK-NEXT: [[ENTRY:.*:]] | |
| ; CHECK-NEXT: [[COND:%.*]] = icmp uge i8 [[X]], [[Y]] | |
| ; CHECK-NEXT: br i1 [[COND]], label %[[BODY:.*]], label %[[EXIT:.*]] | |
| ; CHECK: [[BODY]]: | |
| ; CHECK-NEXT: ret i1 false | |
| ; CHECK: [[EXIT]]: | |
| ; CHECK-NEXT: ret i1 true | |
| ; | |
| entry: | |
| %cond = icmp uge i8 %x, %y | |
| br i1 %cond, label %body, label %exit | |
| body: | |
| %rem = urem i8 %x, %y | |
| %cmp = icmp eq i8 %rem, %x | |
| ret i1 %cmp | |
| exit: | |
| ret i1 true | |
| } | |
| ; Equivalent comparisons against one. | |
| ; (X - (X urem Y)) u>= 1 is true if X u>= Y. | |
| define i1 @dom_uge_one(i8 %x, i8 %y) { | |
| ; CHECK-LABEL: define i1 @dom_uge_one( | |
| ; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) { | |
| ; CHECK-NEXT: [[ENTRY:.*:]] | |
| ; CHECK-NEXT: [[COND:%.*]] = icmp uge i8 [[X]], [[Y]] | |
| ; CHECK-NEXT: br i1 [[COND]], label %[[BODY:.*]], label %[[EXIT:.*]] | |
| ; CHECK: [[BODY]]: | |
| ; CHECK-NEXT: ret i1 true | |
| ; CHECK: [[EXIT]]: | |
| ; CHECK-NEXT: ret i1 false | |
| ; | |
| entry: | |
| %cond = icmp uge i8 %x, %y | |
| br i1 %cond, label %body, label %exit | |
| body: | |
| %rem = urem i8 %x, %y | |
| %sub = sub i8 %x, %rem | |
| %cmp = icmp uge i8 %sub, 1 | |
| ret i1 %cmp | |
| exit: | |
| ret i1 false | |
| } | |
| ; (X - (X urem Y)) u< 1 is false if X u>= Y. | |
| define i1 @dom_ult_one(i8 %x, i8 %y) { | |
| ; CHECK-LABEL: define i1 @dom_ult_one( | |
| ; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) { | |
| ; CHECK-NEXT: [[ENTRY:.*:]] | |
| ; CHECK-NEXT: [[COND:%.*]] = icmp uge i8 [[X]], [[Y]] | |
| ; CHECK-NEXT: br i1 [[COND]], label %[[BODY:.*]], label %[[EXIT:.*]] | |
| ; CHECK: [[BODY]]: | |
| ; CHECK-NEXT: ret i1 false | |
| ; CHECK: [[EXIT]]: | |
| ; CHECK-NEXT: ret i1 true | |
| ; | |
| entry: | |
| %cond = icmp uge i8 %x, %y | |
| br i1 %cond, label %body, label %exit | |
| body: | |
| %rem = urem i8 %x, %y | |
| %sub = sub i8 %x, %rem | |
| %cmp = icmp ult i8 %sub, 1 | |
| ret i1 %cmp | |
| exit: | |
| ret i1 true | |
| } | |
| ; Equivalent unsigned comparisons against zero. | |
| ; (X - (X urem Y)) u> 0 is true on the false edge of X u< Y. | |
| define i1 @dom_ugt_zero_false_edge(i8 %x, i8 %y) { | |
| ; CHECK-LABEL: define i1 @dom_ugt_zero_false_edge( | |
| ; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) { | |
| ; CHECK-NEXT: [[ENTRY:.*:]] | |
| ; CHECK-NEXT: [[COND:%.*]] = icmp ult i8 [[X]], [[Y]] | |
| ; CHECK-NEXT: br i1 [[COND]], label %[[EXIT:.*]], label %[[BODY:.*]] | |
| ; CHECK: [[BODY]]: | |
| ; CHECK-NEXT: ret i1 true | |
| ; CHECK: [[EXIT]]: | |
| ; CHECK-NEXT: ret i1 false | |
| ; | |
| entry: | |
| %cond = icmp ult i8 %x, %y | |
| br i1 %cond, label %exit, label %body | |
| body: | |
| %rem = urem i8 %x, %y | |
| %sub = sub i8 %x, %rem | |
| %cmp = icmp ugt i8 %sub, 0 | |
| ret i1 %cmp | |
| exit: | |
| ret i1 false | |
| } | |
| ; 0 u>= (X - (X urem Y)) is false if X u>= Y. | |
| define i1 @dom_ule_zero_commuted(i8 %x, i8 %y) { | |
| ; CHECK-LABEL: define i1 @dom_ule_zero_commuted( | |
| ; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) { | |
| ; CHECK-NEXT: [[ENTRY:.*:]] | |
| ; CHECK-NEXT: [[COND:%.*]] = icmp uge i8 [[X]], [[Y]] | |
| ; CHECK-NEXT: br i1 [[COND]], label %[[BODY:.*]], label %[[EXIT:.*]] | |
| ; CHECK: [[BODY]]: | |
| ; CHECK-NEXT: ret i1 false | |
| ; CHECK: [[EXIT]]: | |
| ; CHECK-NEXT: ret i1 true | |
| ; | |
| entry: | |
| %cond = icmp uge i8 %x, %y | |
| br i1 %cond, label %body, label %exit | |
| body: | |
| %rem = urem i8 %x, %y | |
| %sub = sub i8 %x, %rem | |
| %cmp = icmp uge i8 0, %sub | |
| ret i1 %cmp | |
| exit: | |
| ret i1 true | |
| } | |
| ; Equality comparisons against zero. | |
| ; (X - (X urem Y)) != 0 is true if X u>= Y. | |
| define i1 @dom_ne_zero(i8 %x, i8 %y) { | |
| ; CHECK-LABEL: define i1 @dom_ne_zero( | |
| ; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) { | |
| ; CHECK-NEXT: [[ENTRY:.*:]] | |
| ; CHECK-NEXT: [[COND:%.*]] = icmp uge i8 [[X]], [[Y]] | |
| ; CHECK-NEXT: br i1 [[COND]], label %[[BODY:.*]], label %[[EXIT:.*]] | |
| ; CHECK: [[BODY]]: | |
| ; CHECK-NEXT: ret i1 true | |
| ; CHECK: [[EXIT]]: | |
| ; CHECK-NEXT: ret i1 false | |
| ; | |
| entry: | |
| %cond = icmp uge i8 %x, %y | |
| br i1 %cond, label %body, label %exit | |
| body: | |
| %rem = urem i8 %x, %y | |
| %sub = sub i8 %x, %rem | |
| %cmp = icmp ne i8 %sub, 0 | |
| ret i1 %cmp | |
| exit: | |
| ret i1 false | |
| } | |
| ; 0 == (X - (X urem Y)) is false if X u>= Y. | |
| define i1 @dom_eq_zero_commuted(i8 %x, i8 %y) { | |
| ; CHECK-LABEL: define i1 @dom_eq_zero_commuted( | |
| ; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) { | |
| ; CHECK-NEXT: [[ENTRY:.*:]] | |
| ; CHECK-NEXT: [[COND:%.*]] = icmp uge i8 [[X]], [[Y]] | |
| ; CHECK-NEXT: br i1 [[COND]], label %[[BODY:.*]], label %[[EXIT:.*]] | |
| ; CHECK: [[BODY]]: | |
| ; CHECK-NEXT: ret i1 false | |
| ; CHECK: [[EXIT]]: | |
| ; CHECK-NEXT: ret i1 true | |
| ; | |
| entry: | |
| %cond = icmp uge i8 %x, %y | |
| br i1 %cond, label %body, label %exit | |
| body: | |
| %rem = urem i8 %x, %y | |
| %sub = sub i8 %x, %rem | |
| %cmp = icmp eq i8 0, %sub | |
| ret i1 %cmp | |
| exit: | |
| ret i1 true | |
| } | |
| ; Intrinsic user. | |
| ; umax(X - (X urem Y), 1) is X - (X urem Y) if X u>= Y. | |
| define i8 @dom_umax_false_edge(i8 %x, i8 %y) { | |
| ; CHECK-LABEL: define i8 @dom_umax_false_edge( | |
| ; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) { | |
| ; CHECK-NEXT: [[ENTRY:.*:]] | |
| ; CHECK-NEXT: [[COND:%.*]] = icmp ult i8 [[X]], [[Y]] | |
| ; CHECK-NEXT: br i1 [[COND]], label %[[EXIT:.*]], label %[[BODY:.*]] | |
| ; CHECK: [[BODY]]: | |
| ; CHECK-NEXT: [[REM:%.*]] = urem i8 [[X]], [[Y]] | |
| ; CHECK-NEXT: [[SUB:%.*]] = sub i8 [[X]], [[REM]] | |
| ; CHECK-NEXT: ret i8 [[SUB]] | |
| ; CHECK: [[EXIT]]: | |
| ; CHECK-NEXT: ret i8 0 | |
| ; | |
| entry: | |
| %cond = icmp ult i8 %x, %y | |
| br i1 %cond, label %exit, label %body | |
| body: | |
| %rem = urem i8 %x, %y | |
| %sub = sub i8 %x, %rem | |
| %max = call i8 @llvm.umax.i8(i8 %sub, i8 1) | |
| ret i8 %max | |
| exit: | |
| ret i8 0 | |
| } | |
| ; Negative tests. | |
| ; Do not simplify if the add may wrap. | |
| define i8 @add_may_wrap(i8 %a, i8 %y) { | |
| ; CHECK-LABEL: define i8 @add_may_wrap( | |
| ; CHECK-SAME: i8 [[A:%.*]], i8 [[Y:%.*]]) { | |
| ; CHECK-NEXT: [[X:%.*]] = add i8 [[A]], [[Y]] | |
| ; CHECK-NEXT: [[REM:%.*]] = urem i8 [[X]], [[Y]] | |
| ; CHECK-NEXT: [[SUB:%.*]] = sub i8 [[X]], [[REM]] | |
| ; CHECK-NEXT: [[MAX:%.*]] = call i8 @llvm.umax.i8(i8 [[SUB]], i8 1) | |
| ; CHECK-NEXT: ret i8 [[MAX]] | |
| ; | |
| %x = add i8 %a, %y | |
| %rem = urem i8 %x, %y | |
| %sub = sub i8 %x, %rem | |
| %max = call i8 @llvm.umax.i8(i8 %sub, i8 1) | |
| ret i8 %max | |
| } | |
| ; Do not simplify (X - (X urem Y)) != 0 without proof of X u>= Y. | |
| define i1 @no_known_relation(i8 %x, i8 %y) { | |
| ; CHECK-LABEL: define i1 @no_known_relation( | |
| ; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) { | |
| ; CHECK-NEXT: [[REM:%.*]] = urem i8 [[X]], [[Y]] | |
| ; CHECK-NEXT: [[SUB:%.*]] = sub i8 [[X]], [[REM]] | |
| ; CHECK-NEXT: [[CMP:%.*]] = icmp ne i8 [[SUB]], 0 | |
| ; CHECK-NEXT: ret i1 [[CMP]] | |
| ; | |
| %rem = urem i8 %x, %y | |
| %sub = sub i8 %x, %rem | |
| %cmp = icmp ne i8 %sub, 0 | |
| ret i1 %cmp | |
| } | |
| ; Do not simplify using X u>= Z when the remainder divisor is Y. | |
| define i1 @dom_wrong_divisor_ne_zero(i8 %x, i8 %y, i8 %z) { | |
| ; CHECK-LABEL: define i1 @dom_wrong_divisor_ne_zero( | |
| ; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) { | |
| ; CHECK-NEXT: [[ENTRY:.*:]] | |
| ; CHECK-NEXT: [[COND:%.*]] = icmp uge i8 [[X]], [[Z]] | |
| ; CHECK-NEXT: br i1 [[COND]], label %[[BODY:.*]], label %[[EXIT:.*]] | |
| ; CHECK: [[BODY]]: | |
| ; CHECK-NEXT: [[REM:%.*]] = urem i8 [[X]], [[Y]] | |
| ; CHECK-NEXT: [[SUB:%.*]] = sub i8 [[X]], [[REM]] | |
| ; CHECK-NEXT: [[CMP:%.*]] = icmp ne i8 [[SUB]], 0 | |
| ; CHECK-NEXT: ret i1 [[CMP]] | |
| ; CHECK: [[EXIT]]: | |
| ; CHECK-NEXT: ret i1 false | |
| ; | |
| entry: | |
| %cond = icmp uge i8 %x, %z | |
| br i1 %cond, label %body, label %exit | |
| body: | |
| %rem = urem i8 %x, %y | |
| %sub = sub i8 %x, %rem | |
| %cmp = icmp ne i8 %sub, 0 | |
| ret i1 %cmp | |
| exit: | |
| ret i1 false | |
| } | |
| ; Do not simplify umax using X u>= Y when the remainder divisor is Z. | |
| define i8 @dom_wrong_divisor(i8 %x, i8 %y, i8 %z) { | |
| ; CHECK-LABEL: define i8 @dom_wrong_divisor( | |
| ; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[Z:%.*]]) { | |
| ; CHECK-NEXT: [[ENTRY:.*:]] | |
| ; CHECK-NEXT: [[COND:%.*]] = icmp uge i8 [[X]], [[Y]] | |
| ; CHECK-NEXT: br i1 [[COND]], label %[[BODY:.*]], label %[[EXIT:.*]] | |
| ; CHECK: [[BODY]]: | |
| ; CHECK-NEXT: [[REM:%.*]] = urem i8 [[X]], [[Z]] | |
| ; CHECK-NEXT: [[SUB:%.*]] = sub i8 [[X]], [[REM]] | |
| ; CHECK-NEXT: [[MAX:%.*]] = call i8 @llvm.umax.i8(i8 [[SUB]], i8 1) | |
| ; CHECK-NEXT: ret i8 [[MAX]] | |
| ; CHECK: [[EXIT]]: | |
| ; CHECK-NEXT: ret i8 0 | |
| ; | |
| entry: | |
| %cond = icmp uge i8 %x, %y | |
| br i1 %cond, label %body, label %exit | |
| body: | |
| %rem = urem i8 %x, %z | |
| %sub = sub i8 %x, %rem | |
| %max = call i8 @llvm.umax.i8(i8 %sub, i8 1) | |
| ret i8 %max | |
| exit: | |
| ret i8 0 | |
| } |