[llvm] [ConstraintElim] Add support for `trunc nsw/nuw` (PR #118745)

via llvm-commits llvm-commits at lists.llvm.org
Wed Dec 4 22:00:57 PST 2024


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-transforms

Author: Yingwei Zheng (dtcxzyw)

<details>
<summary>Changes</summary>

Proof for `trunc nsw nneg X -> trunc nuw X`: https://alive2.llvm.org/ce/z/ooP6Mt


---
Full diff: https://github.com/llvm/llvm-project/pull/118745.diff


2 Files Affected:

- (modified) llvm/lib/Transforms/Scalar/ConstraintElimination.cpp (+13-3) 
- (added) llvm/test/Transforms/ConstraintElimination/trunc.ll (+185) 


``````````diff
diff --git a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
index 8d1e793836c772..e64fc153cf3d26 100644
--- a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
@@ -521,6 +521,9 @@ static Decomposition decompose(Value *V,
     else if (match(V, m_NNegZExt(m_Value(Op0)))) {
       V = Op0;
       IsKnownNonNegative = true;
+    } else if (match(V, m_NSWTrunc(m_Value(Op0)))) {
+      if (Op0->getType()->getScalarSizeInBits() <= 64)
+        V = Op0;
     }
 
     if (match(V, m_NSWAdd(m_Value(Op0), m_Value(Op1))))
@@ -558,12 +561,19 @@ static Decomposition decompose(Value *V,
   if (match(V, m_ZExt(m_Value(Op0)))) {
     IsKnownNonNegative = true;
     V = Op0;
-  }
-
-  if (match(V, m_SExt(m_Value(Op0)))) {
+  } else if (match(V, m_SExt(m_Value(Op0)))) {
     V = Op0;
     Preconditions.emplace_back(CmpInst::ICMP_SGE, Op0,
                                ConstantInt::get(Op0->getType(), 0));
+  } else if (auto *Trunc = dyn_cast<TruncInst>(V)) {
+    if (Trunc->getSrcTy()->getScalarSizeInBits() <= 64) {
+      if (Trunc->hasNoUnsignedWrap() || Trunc->hasNoSignedWrap()) {
+        V = Trunc->getOperand(0);
+        if (!Trunc->hasNoUnsignedWrap())
+          Preconditions.emplace_back(CmpInst::ICMP_SGE, V,
+                                     ConstantInt::get(V->getType(), 0));
+      }
+    }
   }
 
   Value *Op1;
diff --git a/llvm/test/Transforms/ConstraintElimination/trunc.ll b/llvm/test/Transforms/ConstraintElimination/trunc.ll
new file mode 100644
index 00000000000000..f2a708cb6caa85
--- /dev/null
+++ b/llvm/test/Transforms/ConstraintElimination/trunc.ll
@@ -0,0 +1,185 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -passes=constraint-elimination -S %s | FileCheck %s
+
+define i1 @test_icmp_ult_zext_icmp_trunc_nuw(i16 %x, i32 %y) {
+; CHECK-LABEL: define i1 @test_icmp_ult_zext_icmp_trunc_nuw(
+; CHECK-SAME: i16 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT:    [[EXT:%.*]] = zext i16 [[X]] to i32
+; CHECK-NEXT:    [[COND:%.*]] = icmp ult i32 [[Y]], [[EXT]]
+; CHECK-NEXT:    br i1 [[COND]], label %[[IF_THEN:.*]], label %[[IF_ELSE:.*]]
+; CHECK:       [[IF_THEN]]:
+; CHECK-NEXT:    [[CONV:%.*]] = trunc nuw i32 [[Y]] to i16
+; CHECK-NEXT:    ret i1 false
+; CHECK:       [[IF_ELSE]]:
+; CHECK-NEXT:    ret i1 false
+;
+  %ext = zext i16 %x to i32
+  %cond = icmp ult i32 %y, %ext
+  br i1 %cond, label %if.then, label %if.else
+
+if.then:
+  %conv = trunc nuw i32 %y to i16
+  %cmp = icmp eq i16 %x, %conv
+  ret i1 %cmp
+
+if.else:
+  ret i1 false
+}
+
+define i1 @test_icmp_slt_sext_icmp_trunc_nsw(i16 %x, i32 %y) {
+; CHECK-LABEL: define i1 @test_icmp_slt_sext_icmp_trunc_nsw(
+; CHECK-SAME: i16 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT:    [[EXT:%.*]] = sext i16 [[X]] to i32
+; CHECK-NEXT:    [[COND:%.*]] = icmp slt i32 [[Y]], [[EXT]]
+; CHECK-NEXT:    br i1 [[COND]], label %[[IF_THEN:.*]], label %[[IF_ELSE:.*]]
+; CHECK:       [[IF_THEN]]:
+; CHECK-NEXT:    [[CONV:%.*]] = trunc nsw i32 [[Y]] to i16
+; CHECK-NEXT:    ret i1 false
+; CHECK:       [[IF_ELSE]]:
+; CHECK-NEXT:    ret i1 false
+;
+  %ext = sext i16 %x to i32
+  %cond = icmp slt i32 %y, %ext
+  br i1 %cond, label %if.then, label %if.else
+
+if.then:
+  %conv = trunc nsw i32 %y to i16
+  %cmp = icmp slt i16 %x, %conv
+  ret i1 %cmp
+
+if.else:
+  ret i1 false
+}
+
+define i1 @test_icmp_ult_trunc_nsw_nneg_icmp_trunc_nuw(i64 %x, i32 %y) {
+; CHECK-LABEL: define i1 @test_icmp_ult_trunc_nsw_nneg_icmp_trunc_nuw(
+; CHECK-SAME: i64 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT:    [[EXT:%.*]] = trunc nsw i64 [[X]] to i32
+; CHECK-NEXT:    [[NNEG:%.*]] = icmp sgt i64 [[X]], -1
+; CHECK-NEXT:    [[COND:%.*]] = icmp ult i32 [[Y]], [[EXT]]
+; CHECK-NEXT:    [[AND:%.*]] = and i1 [[NNEG]], [[COND]]
+; CHECK-NEXT:    br i1 [[AND]], label %[[IF_THEN:.*]], label %[[IF_ELSE:.*]]
+; CHECK:       [[IF_THEN]]:
+; CHECK-NEXT:    [[CONV:%.*]] = zext i32 [[Y]] to i64
+; CHECK-NEXT:    ret i1 false
+; CHECK:       [[IF_ELSE]]:
+; CHECK-NEXT:    ret i1 false
+;
+  %ext = trunc nsw i64 %x to i32
+  %nneg = icmp sgt i64 %x, -1
+  %cond = icmp ult i32 %y, %ext
+  %and = and i1 %nneg, %cond
+  br i1 %and, label %if.then, label %if.else
+
+if.then:
+  %conv = zext i32 %y to i64
+  %cmp = icmp eq i64 %x, %conv
+  ret i1 %cmp
+
+if.else:
+  ret i1 false
+}
+
+define i1 @test2(i32 %n) {
+; CHECK-LABEL: define i1 @test2(
+; CHECK-SAME: i32 [[N:%.*]]) {
+; CHECK-NEXT:    [[COND:%.*]] = icmp sgt i32 [[N]], 0
+; CHECK-NEXT:    br i1 [[COND]], label %[[IF_THEN:.*]], label %[[IF_ELSE:.*]]
+; CHECK:       [[IF_THEN]]:
+; CHECK-NEXT:    [[EXT:%.*]] = zext nneg i32 [[N]] to i64
+; CHECK-NEXT:    [[END:%.*]] = add nsw i64 [[EXT]], -1
+; CHECK-NEXT:    br label %[[FOR_BODY:.*]]
+; CHECK:       [[FOR_BODY]]:
+; CHECK-NEXT:    [[INDVAR:%.*]] = phi i64 [ 0, %[[IF_THEN]] ], [ [[INDVAR_NEXT:%.*]], %[[FOR_NEXT:.*]] ]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i64 [[INDVAR]], [[END]]
+; CHECK-NEXT:    br i1 [[CMP]], label %[[IF_ELSE]], label %[[FOR_NEXT]]
+; CHECK:       [[FOR_NEXT]]:
+; CHECK-NEXT:    [[INDVAR_NEXT]] = add nuw nsw i64 [[INDVAR]], 1
+; CHECK-NEXT:    [[COND2:%.*]] = call i1 @cond()
+; CHECK-NEXT:    br i1 [[COND2]], label %[[FOR_BODY]], label %[[FOR_END:.*]]
+; CHECK:       [[FOR_END]]:
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc nsw i64 [[INDVAR_NEXT]] to i32
+; CHECK-NEXT:    ret i1 true
+; CHECK:       [[IF_ELSE]]:
+; CHECK-NEXT:    ret i1 false
+;
+  %cond = icmp sgt i32 %n, 0
+  br i1 %cond, label %if.then, label %if.else
+
+if.then:
+  %ext = zext nneg i32 %n to i64
+  %end = add nsw i64 %ext, -1
+  br label %for.body
+
+for.body:
+  %indvar = phi i64 [ 0, %if.then ], [ %indvar.next, %for.next ]
+  %cmp = icmp eq i64 %indvar, %end
+  br i1 %cmp, label %if.else, label %for.next
+
+for.next:
+  %indvar.next = add nuw nsw i64 %indvar, 1
+  %cond2 = call i1 @cond()
+  br i1 %cond2, label %for.body, label %for.end
+
+for.end:
+  %trunc = trunc nsw i64 %indvar.next to i32
+  %res = icmp sgt i32 %n, %trunc
+  ret i1 %res
+
+if.else:
+  ret i1 false
+}
+
+define i1 @test_icmp_ult_zext_icmp_trunc(i16 %x, i32 %y) {
+; CHECK-LABEL: define i1 @test_icmp_ult_zext_icmp_trunc(
+; CHECK-SAME: i16 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT:    [[EXT:%.*]] = zext i16 [[X]] to i32
+; CHECK-NEXT:    [[COND:%.*]] = icmp ult i32 [[Y]], [[EXT]]
+; CHECK-NEXT:    br i1 [[COND]], label %[[IF_THEN:.*]], label %[[IF_ELSE:.*]]
+; CHECK:       [[IF_THEN]]:
+; CHECK-NEXT:    [[CONV:%.*]] = trunc i32 [[Y]] to i16
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i16 [[X]], [[CONV]]
+; CHECK-NEXT:    ret i1 [[CMP]]
+; CHECK:       [[IF_ELSE]]:
+; CHECK-NEXT:    ret i1 false
+;
+  %ext = zext i16 %x to i32
+  %cond = icmp ult i32 %y, %ext
+  br i1 %cond, label %if.then, label %if.else
+
+if.then:
+  %conv = trunc i32 %y to i16
+  %cmp = icmp eq i16 %x, %conv
+  ret i1 %cmp
+
+if.else:
+  ret i1 false
+}
+
+define i1 @test_icmp_ult_zext_icmp_trunc_nuw_i128(i16 %x, i128 %y) {
+; CHECK-LABEL: define i1 @test_icmp_ult_zext_icmp_trunc_nuw_i128(
+; CHECK-SAME: i16 [[X:%.*]], i128 [[Y:%.*]]) {
+; CHECK-NEXT:    [[EXT:%.*]] = zext i16 [[X]] to i128
+; CHECK-NEXT:    [[COND:%.*]] = icmp ult i128 [[Y]], [[EXT]]
+; CHECK-NEXT:    br i1 [[COND]], label %[[IF_THEN:.*]], label %[[IF_ELSE:.*]]
+; CHECK:       [[IF_THEN]]:
+; CHECK-NEXT:    [[CONV:%.*]] = trunc nuw i128 [[Y]] to i16
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i16 [[X]], [[CONV]]
+; CHECK-NEXT:    ret i1 [[CMP]]
+; CHECK:       [[IF_ELSE]]:
+; CHECK-NEXT:    ret i1 false
+;
+  %ext = zext i16 %x to i128
+  %cond = icmp ult i128 %y, %ext
+  br i1 %cond, label %if.then, label %if.else
+
+if.then:
+  %conv = trunc nuw i128 %y to i16
+  %cmp = icmp eq i16 %x, %conv
+  ret i1 %cmp
+
+if.else:
+  ret i1 false
+}
+
+declare void @cond()

``````````

</details>


https://github.com/llvm/llvm-project/pull/118745


More information about the llvm-commits mailing list