[llvm] ab1b42a - [InstCombine] Add simplifications for div/rem with `i1` operands; PR62607

Noah Goldstein via llvm-commits llvm-commits at lists.llvm.org
Sat May 13 12:36:33 PDT 2023


Author: Noah Goldstein
Date: 2023-05-13T14:35:57-05:00
New Revision: ab1b42ac5cb82cd7d541caa06e7929825b4fed84

URL: https://github.com/llvm/llvm-project/commit/ab1b42ac5cb82cd7d541caa06e7929825b4fed84
DIFF: https://github.com/llvm/llvm-project/commit/ab1b42ac5cb82cd7d541caa06e7929825b4fed84.diff

LOG: [InstCombine] Add simplifications for div/rem with `i1` operands; PR62607

This is generally handled already in early CSE.

If a specialized pipeline is used, however, its possible for `i1`
operand with known-zero denominator to slip through. Generally the
known-zero denominator is caught and poison is returned, but if it is
indirect enough (known zero through a phi node) we can miss this case
in `InstructionSimplify` and then miss handling `i1`. This is because
`i1` is current handled with the following check:
    `if(Known.countMinLeadingZeros() == Known.getBitWidth() - 1)`

which only works on the assumption we don't know the denominator to be
zero. If we know the denominator to be zero, this check fails:
https://github.com/llvm/llvm-project/issues/62607

This patch simply adds an explicit `if(Known.isZero) return poison;`
which fixes the issue.

Alive2 Link for tests:
    https://alive2.llvm.org/ce/z/VTw54n

Reviewed By: nikic

Differential Revision: https://reviews.llvm.org/D150142

Added: 
    llvm/test/Transforms/InstCombine/div-i1.ll

Modified: 
    llvm/lib/Analysis/InstructionSimplify.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index 75fa94fb02165..fd0ba395b2937 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -1130,12 +1130,21 @@ static Value *simplifyDivRem(Instruction::BinaryOps Opcode, Value *Op0,
   if (Op0 == Op1)
     return IsDiv ? ConstantInt::get(Ty, 1) : Constant::getNullValue(Ty);
 
+
+  KnownBits Known = computeKnownBits(Op1, Q.DL, 0, Q.AC, Q.CxtI, Q.DT);
+  // X / 0 -> poison
+  // X % 0 -> poison
+  // If the divisor is known to be zero, just return poison. This can happen in
+  // some cases where its provable indirectly the denominator is zero but it's
+  // not trivially simplifiable (i.e known zero through a phi node).
+  if (Known.isZero())
+    return PoisonValue::get(Ty);
+
   // X / 1 -> X
   // X % 1 -> 0
   // If the divisor can only be zero or one, we can't have division-by-zero
   // or remainder-by-zero, so assume the divisor is 1.
   //   e.g. 1, zext (i8 X), sdiv X (Y and 1)
-  KnownBits Known = computeKnownBits(Op1, Q.DL, 0, Q.AC, Q.CxtI, Q.DT);
   if (Known.countMinLeadingZeros() == Known.getBitWidth() - 1)
     return IsDiv ? Op0 : Constant::getNullValue(Ty);
 

diff  --git a/llvm/test/Transforms/InstCombine/div-i1.ll b/llvm/test/Transforms/InstCombine/div-i1.ll
new file mode 100644
index 0000000000000..a54a2b8874f38
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/div-i1.ll
@@ -0,0 +1,153 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt -S -passes=instcombine < %s | FileCheck %s
+
+define i1 @sdiv_by_zero_indirect_is_poison(i1 %c, i1 %x, i1 %y) {
+; CHECK-LABEL: @sdiv_by_zero_indirect_is_poison(
+; CHECK-NEXT:    br i1 [[C:%.*]], label [[TRUE:%.*]], label [[FALSE:%.*]]
+; CHECK:       true:
+; CHECK-NEXT:    br label [[DONE:%.*]]
+; CHECK:       false:
+; CHECK-NEXT:    br label [[DONE]]
+; CHECK:       done:
+; CHECK-NEXT:    ret i1 poison
+;
+  br i1 %c, label %true, label %false
+true:
+  %y_true = and i1 %y, 0
+  br label %done
+false:
+  %y_false = and i1 %y, 0
+  br label %done
+done:
+  %yy = phi i1 [ %y_false, %false ], [ %y_true, %true ]
+  %r = sdiv i1 %x, %yy
+  ret i1 %r
+}
+
+define i1 @udiv_by_zero_indirect_is_poison(i1 %c, i1 %x, i1 %y) {
+; CHECK-LABEL: @udiv_by_zero_indirect_is_poison(
+; CHECK-NEXT:    br i1 [[C:%.*]], label [[TRUE:%.*]], label [[FALSE:%.*]]
+; CHECK:       true:
+; CHECK-NEXT:    br label [[DONE:%.*]]
+; CHECK:       false:
+; CHECK-NEXT:    br label [[DONE]]
+; CHECK:       done:
+; CHECK-NEXT:    ret i1 poison
+;
+  br i1 %c, label %true, label %false
+true:
+  %y_true = and i1 %y, 0
+  br label %done
+false:
+  %y_false = and i1 %y, 0
+  br label %done
+done:
+  %yy = phi i1 [ %y_false, %false ], [ %y_true, %true ]
+  %r = udiv i1 %x, %yy
+  ret i1 %r
+}
+
+define i1 @srem_by_zero_indirect_is_poison(i1 %c, i1 %x, i1 %y) {
+; CHECK-LABEL: @srem_by_zero_indirect_is_poison(
+; CHECK-NEXT:    br i1 [[C:%.*]], label [[TRUE:%.*]], label [[FALSE:%.*]]
+; CHECK:       true:
+; CHECK-NEXT:    br label [[DONE:%.*]]
+; CHECK:       false:
+; CHECK-NEXT:    br label [[DONE]]
+; CHECK:       done:
+; CHECK-NEXT:    ret i1 poison
+;
+  br i1 %c, label %true, label %false
+true:
+  %y_true = and i1 %y, 0
+  br label %done
+false:
+  %y_false = and i1 %y, 0
+  br label %done
+done:
+  %yy = phi i1 [ %y_false, %false ], [ %y_true, %true ]
+  %r = srem i1 %x, %yy
+  ret i1 %r
+}
+
+define i1 @urem_by_zero_indirect_is_poison(i1 %c, i1 %x, i1 %y) {
+; CHECK-LABEL: @urem_by_zero_indirect_is_poison(
+; CHECK-NEXT:    br i1 [[C:%.*]], label [[TRUE:%.*]], label [[FALSE:%.*]]
+; CHECK:       true:
+; CHECK-NEXT:    br label [[DONE:%.*]]
+; CHECK:       false:
+; CHECK-NEXT:    br label [[DONE]]
+; CHECK:       done:
+; CHECK-NEXT:    ret i1 poison
+;
+  br i1 %c, label %true, label %false
+true:
+  %y_true = and i1 %y, 0
+  br label %done
+false:
+  %y_false = and i1 %y, 0
+  br label %done
+done:
+  %yy = phi i1 [ %y_false, %false ], [ %y_true, %true ]
+  %r = urem i1 %x, %yy
+  ret i1 %r
+}
+
+define i1 @sdiv_i1_is_op0(i1 %x, i1 %y) {
+; CHECK-LABEL: @sdiv_i1_is_op0(
+; CHECK-NEXT:    ret i1 [[X:%.*]]
+;
+  %r = sdiv i1 %x, %y
+  ret i1 %r
+}
+
+define i1 @udiv_i1_is_op0(i1 %x, i1 %y) {
+; CHECK-LABEL: @udiv_i1_is_op0(
+; CHECK-NEXT:    ret i1 [[X:%.*]]
+;
+  %r = udiv i1 %x, %y
+  ret i1 %r
+}
+
+define i1 @srem_i1_is_zero(i1 %x, i1 %y) {
+; CHECK-LABEL: @srem_i1_is_zero(
+; CHECK-NEXT:    ret i1 false
+;
+  %r = srem i1 %x, %y
+  ret i1 %r
+}
+
+define i1 @urem_i1_is_zero(i1 %x, i1 %y) {
+; CHECK-LABEL: @urem_i1_is_zero(
+; CHECK-NEXT:    ret i1 false
+;
+  %r = urem i1 %x, %y
+  ret i1 %r
+}
+
+declare void @llvm.assume(i1 noundef)
+
+define i1 @pt62607() {
+; CHECK-LABEL: @pt62607(
+; CHECK-NEXT:  entry_1:
+; CHECK-NEXT:    br label [[LOOP_5:%.*]]
+; CHECK:       loop_5:
+; CHECK-NEXT:    [[LOOP_CNT_I1_26_0:%.*]] = phi i1 [ false, [[ENTRY_1:%.*]] ], [ [[VAL_I1_55:%.*]], [[LOOP_5]] ]
+; CHECK-NEXT:    [[VAL_I1_55]] = xor i1 [[LOOP_CNT_I1_26_0]], true
+; CHECK-NEXT:    call void @llvm.assume(i1 [[VAL_I1_55]])
+; CHECK-NEXT:    br i1 poison, label [[LOOP_5]], label [[LOOP_EXIT_8:%.*]]
+; CHECK:       loop_exit_8:
+; CHECK-NEXT:    ret i1 false
+;
+entry_1:
+  %val_i1_38 = trunc i8 109 to i1
+  br label %loop_5
+loop_5:  ; preds = %loop_5, %entry_1
+  %loop_cnt_i1_26.0 = phi i1 [ false, %entry_1 ], [ %val_i1_55, %loop_5 ]
+  %val_i1_55 = add i1 %loop_cnt_i1_26.0, true
+  call void @llvm.assume(i1 %val_i1_55)
+  %val_i1_67 = udiv i1 %val_i1_38, %loop_cnt_i1_26.0
+  br i1 %val_i1_67, label %loop_5, label %loop_exit_8
+loop_exit_8:  ; preds = %loop_5
+  ret i1 false
+}


        


More information about the llvm-commits mailing list