Author: Florian Hahn
Date: 2026-08-30T16:11:51+01:00
New Revision: c053daa736f6ad30c638725985f64d28280f47ae

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

LOG: [ConstraintElim] Add NUW for mul/shl with NSW and non-positive ops. 
(#219663)

If a mul/shl has NSW and both operands are known-non-negative, add NUW.

Note the changes in loopflatten.ll. The IV is now widened by
LoopFlatten, converting it into a form the pass cannot flatten. This is
an underlying issue in the pass

This improves flags in a number of cases, leading to a number of
additional folds:
https://github.com/dtcxzyw/llvm-opt-benchmark-nightly/pull/1098

Alive2 Proof: https://alive2.llvm.org/ce/z/C7QZ4P

PR: https://github.com/llvm/llvm-project/pull/219663

Added: 
    

Modified: 
    clang/test/CodeGen/attr-counted-by-or-null-for-pointers.c
    clang/test/CodeGen/attr-sized-by-for-pointers.c
    clang/test/CodeGen/attr-sized-by-or-null-for-pointers.c
    llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
    llvm/test/Transforms/ConstraintElimination/gep-arithmetic-different-types.ll
    llvm/test/Transforms/ConstraintElimination/materialize-flags.ll
    llvm/test/Transforms/ConstraintElimination/mul-nsw.ll
    llvm/test/Transforms/PhaseOrdering/AArch64/loopflatten.ll

Removed: 
    


################################################################################
diff  --git a/clang/test/CodeGen/attr-counted-by-or-null-for-pointers.c 
b/clang/test/CodeGen/attr-counted-by-or-null-for-pointers.c
index fcb70bd371337..56b52fae804d2 100644
--- a/clang/test/CodeGen/attr-counted-by-or-null-for-pointers.c
+++ b/clang/test/CodeGen/attr-counted-by-or-null-for-pointers.c
@@ -136,7 +136,7 @@ size_t test_bdos_of_pointer_through_cast(struct 
annotated_ptr *p, int index) {
 // SANITIZE-WITH-ATTR-NEXT:    [[TMP5:%.*]] = icmp eq ptr [[TMP4]], null
 // SANITIZE-WITH-ATTR-NEXT:    [[TMP6:%.*]] = shl nuw nsw i64 [[COUNT]], 3
 // SANITIZE-WITH-ATTR-NEXT:    [[ARRAY_SIZE:%.*]] = select i1 [[TMP5]], i64 0, 
i64 [[TMP6]]
-// SANITIZE-WITH-ATTR-NEXT:    [[INDEX_SIZE:%.*]] = shl nsw i64 [[IDXPROM]], 3
+// SANITIZE-WITH-ATTR-NEXT:    [[INDEX_SIZE:%.*]] = shl nuw nsw i64 
[[IDXPROM]], 3
 // SANITIZE-WITH-ATTR-NEXT:    [[RESULT:%.*]] = sub nsw i64 [[ARRAY_SIZE]], 
[[INDEX_SIZE]]
 // SANITIZE-WITH-ATTR-NEXT:    [[TMP7:%.*]] = tail call i64 @llvm.smax.i64(i64 
[[RESULT]], i64 0)
 // SANITIZE-WITH-ATTR-NEXT:    ret i64 [[TMP7]]
@@ -391,7 +391,6 @@ void test_pass_dynamic_object_size_of_pointer(struct 
annotated_volatile_ptr *p)
 // The '&((cast) ptr->array)[idx]' form via pass_dynamic_object_size.
 // FIXME: for the _or_null variants the object-size null-check re-emits the
 // `ptr->array` load. Instead it should reuse the value.
-//
 // SANITIZE-WITH-ATTR-LABEL: define dso_local void 
@test_pass_dynamic_object_size_of_element_address(
 // SANITIZE-WITH-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) 
local_unnamed_addr #[[ATTR0]] {
 // SANITIZE-WITH-ATTR-NEXT:  [[ENTRY:.*:]]

diff  --git a/clang/test/CodeGen/attr-sized-by-for-pointers.c 
b/clang/test/CodeGen/attr-sized-by-for-pointers.c
index 709e2ac2af2af..8bf77bcf71ca5 100644
--- a/clang/test/CodeGen/attr-sized-by-for-pointers.c
+++ b/clang/test/CodeGen/attr-sized-by-for-pointers.c
@@ -198,7 +198,7 @@ size_t test_bdos_of_pointer_multibyte_pointee(struct 
annotated_sized_ptr_non_byt
 // SANITIZE-WITH-ATTR:       [[CONT8]]:
 // SANITIZE-WITH-ATTR-NEXT:    [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
 // SANITIZE-WITH-ATTR-NEXT:    [[COUNT:%.*]] = zext nneg i32 
[[DOTCOUNTED_BY_LOAD]] to i64
-// SANITIZE-WITH-ATTR-NEXT:    [[INDEX_SIZE:%.*]] = shl nsw i64 [[IDXPROM]], 3
+// SANITIZE-WITH-ATTR-NEXT:    [[INDEX_SIZE:%.*]] = shl nuw nsw i64 
[[IDXPROM]], 3
 // SANITIZE-WITH-ATTR-NEXT:    [[RESULT:%.*]] = sub nsw i64 [[COUNT]], 
[[INDEX_SIZE]]
 // SANITIZE-WITH-ATTR-NEXT:    [[TMP4:%.*]] = tail call i64 @llvm.smax.i64(i64 
[[RESULT]], i64 0)
 // SANITIZE-WITH-ATTR-NEXT:    ret i64 [[TMP4]]
@@ -487,7 +487,7 @@ void 
test_pass_dynamic_object_size_of_element_address(struct annotated_volatile_
 // SANITIZE-WITH-ATTR:       [[CONT8]]:
 // SANITIZE-WITH-ATTR-NEXT:    [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
 // SANITIZE-WITH-ATTR-NEXT:    [[COUNT:%.*]] = zext nneg i32 
[[DOTCOUNTED_BY_LOAD]] to i64
-// SANITIZE-WITH-ATTR-NEXT:    [[INDEX_SIZE:%.*]] = shl nsw i64 [[IDXPROM]], 2
+// SANITIZE-WITH-ATTR-NEXT:    [[INDEX_SIZE:%.*]] = shl nuw nsw i64 
[[IDXPROM]], 2
 // SANITIZE-WITH-ATTR-NEXT:    [[RESULT:%.*]] = sub nsw i64 [[COUNT]], 
[[INDEX_SIZE]]
 // SANITIZE-WITH-ATTR-NEXT:    [[TMP4:%.*]] = tail call i64 @llvm.smax.i64(i64 
[[RESULT]], i64 0)
 // SANITIZE-WITH-ATTR-NEXT:    ret i64 [[TMP4]]

diff  --git a/clang/test/CodeGen/attr-sized-by-or-null-for-pointers.c 
b/clang/test/CodeGen/attr-sized-by-or-null-for-pointers.c
index 5f673e44a8ec3..61ca39b37d5f2 100644
--- a/clang/test/CodeGen/attr-sized-by-or-null-for-pointers.c
+++ b/clang/test/CodeGen/attr-sized-by-or-null-for-pointers.c
@@ -234,7 +234,7 @@ size_t test_bdos_of_pointer_multibyte_pointee(struct 
annotated_sized_ptr_non_byt
 // SANITIZE-WITH-ATTR-NEXT:    [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
 // SANITIZE-WITH-ATTR-NEXT:    [[TMP5:%.*]] = icmp eq ptr [[TMP4]], null
 // SANITIZE-WITH-ATTR-NEXT:    [[COUNT_OR_NULL:%.*]] = select i1 [[TMP5]], i64 
0, i64 [[COUNT]]
-// SANITIZE-WITH-ATTR-NEXT:    [[INDEX_SIZE:%.*]] = shl nsw i64 [[IDXPROM]], 3
+// SANITIZE-WITH-ATTR-NEXT:    [[INDEX_SIZE:%.*]] = shl nuw nsw i64 
[[IDXPROM]], 3
 // SANITIZE-WITH-ATTR-NEXT:    [[RESULT:%.*]] = sub nsw i64 [[COUNT_OR_NULL]], 
[[INDEX_SIZE]]
 // SANITIZE-WITH-ATTR-NEXT:    [[TMP6:%.*]] = tail call i64 @llvm.smax.i64(i64 
[[RESULT]], i64 0)
 // SANITIZE-WITH-ATTR-NEXT:    ret i64 [[TMP6]]
@@ -563,7 +563,7 @@ void 
test_pass_dynamic_object_size_of_element_address(struct annotated_volatile_
 // SANITIZE-WITH-ATTR-NEXT:    [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
 // SANITIZE-WITH-ATTR-NEXT:    [[TMP5:%.*]] = icmp eq ptr [[TMP4]], null
 // SANITIZE-WITH-ATTR-NEXT:    [[COUNT_OR_NULL:%.*]] = select i1 [[TMP5]], i64 
0, i64 [[COUNT]]
-// SANITIZE-WITH-ATTR-NEXT:    [[INDEX_SIZE:%.*]] = shl nsw i64 [[IDXPROM]], 2
+// SANITIZE-WITH-ATTR-NEXT:    [[INDEX_SIZE:%.*]] = shl nuw nsw i64 
[[IDXPROM]], 2
 // SANITIZE-WITH-ATTR-NEXT:    [[RESULT:%.*]] = sub nsw i64 [[COUNT_OR_NULL]], 
[[INDEX_SIZE]]
 // SANITIZE-WITH-ATTR-NEXT:    [[TMP6:%.*]] = tail call i64 @llvm.smax.i64(i64 
[[RESULT]], i64 0)
 // SANITIZE-WITH-ATTR-NEXT:    ret i64 [[TMP6]]

diff  --git a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp 
b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
index 65cfcd78205f6..ef324c40e44a6 100644
--- a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
@@ -1307,9 +1307,10 @@ static bool canStrengthenFlags(Instruction *I) {
   case Instruction::Shl:
     if (BO->hasNoUnsignedWrap() && BO->hasNoSignedWrap())
       return false;
-    // A constant second operand can be used to bound the first operand to
-    // refine no-wrap flags.
-    return isa<ConstantInt>(BO->getOperand(1));
+    // With a constant second operand, we can use bounds on the first operand 
to
+    // refine no-wrap flags. Independently, nuw can be added for nsw if the
+    // operands are non-negative.
+    return isa<ConstantInt>(BO->getOperand(1)) || BO->hasNoSignedWrap();
   default:
     return false;
   }
@@ -1367,26 +1368,34 @@ static bool tryToStrengthenFlags(Instruction *I, 
ConstraintInfo &Info,
     bool Changed = false;
     // For a constant Op1, the ranges of Op0 for which the operation does not
     // wrap are known exactly; check if the systems imply one of them.
-    auto *C = cast<ConstantInt>(Op1);
-    ConstantRange Other(C->getValue());
-    if (!I->hasNoUnsignedWrap() &&
-        doesHoldInRange(Info, Op0,
-                        ConstantRange::makeGuaranteedNoWrapRegion(
-                            Opcode, Other, OBO::NoUnsignedWrap),
-                        /*Signed=*/false)) {
+    if (auto *C = dyn_cast<ConstantInt>(Op1)) {
+      ConstantRange Other(C->getValue());
+      if (!I->hasNoUnsignedWrap() &&
+          doesHoldInRange(Info, Op0,
+                          ConstantRange::makeGuaranteedNoWrapRegion(
+                              Opcode, Other, OBO::NoUnsignedWrap),
+                          /*Signed=*/false)) {
+        LLVM_DEBUG(dbgs() << "Adding nuw to " << *I << "\n");
+        I->setHasNoUnsignedWrap();
+        Changed = true;
+      }
+      if (!I->hasNoSignedWrap() &&
+          doesHoldInRange(Info, Op0,
+                          ConstantRange::makeGuaranteedNoWrapRegion(
+                              Opcode, Other, OBO::NoSignedWrap),
+                          /*Signed=*/true)) {
+        LLVM_DEBUG(dbgs() << "Adding nsw to " << *I << "\n");
+        I->setHasNoSignedWrap();
+        Changed = true;
+      }
+    }
+    if (!I->hasNoUnsignedWrap() && I->hasNoSignedWrap() &&
+        Info.isKnownNonNegative(Op0) &&
+        (Opcode == Instruction::Shl || Info.isKnownNonNegative(Op1))) {
       LLVM_DEBUG(dbgs() << "Adding nuw to " << *I << "\n");
       I->setHasNoUnsignedWrap();
       Changed = true;
     }
-    if (!I->hasNoSignedWrap() &&
-        doesHoldInRange(Info, Op0,
-                        ConstantRange::makeGuaranteedNoWrapRegion(
-                            Opcode, Other, OBO::NoSignedWrap),
-                        /*Signed=*/true)) {
-      LLVM_DEBUG(dbgs() << "Adding nsw to " << *I << "\n");
-      I->setHasNoSignedWrap();
-      Changed = true;
-    }
     return Changed;
   }
   default:

diff  --git a/llvm/test/Transforms/ConstraintElimination/gep-arithmetic-
diff erent-types.ll b/llvm/test/Transforms/ConstraintElimination/gep-arithmetic-
diff erent-types.ll
index fe67e0a822d9a..47d8ec22dda42 100644
--- a/llvm/test/Transforms/ConstraintElimination/gep-arithmetic-
diff erent-types.ll
+++ b/llvm/test/Transforms/ConstraintElimination/gep-arithmetic-
diff erent-types.ll
@@ -123,11 +123,11 @@ define i1 @gep_shl_nsw_positive_index(ptr %A, ptr %upper, 
i8 %idx) {
 ; CHECK-LABEL: @gep_shl_nsw_positive_index(
 ; CHECK-NEXT:    [[IDX_POS:%.*]] = icmp sge i8 [[IDX:%.*]], 0
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[IDX_POS]])
-; CHECK-NEXT:    [[IDX_2:%.*]] = shl nsw i8 [[IDX]], 2
+; CHECK-NEXT:    [[IDX_2:%.*]] = shl nuw nsw i8 [[IDX]], 2
 ; CHECK-NEXT:    [[ADD_I8_IDX_2:%.*]] = getelementptr inbounds i8, ptr 
[[A:%.*]], i8 [[IDX_2]]
 ; CHECK-NEXT:    [[C_0:%.*]] = icmp ult ptr [[ADD_I8_IDX_2]], [[UPPER:%.*]]
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[C_0]])
-; CHECK-NEXT:    [[IDX_1:%.*]] = shl nsw i8 [[IDX]], 1
+; CHECK-NEXT:    [[IDX_1:%.*]] = shl nuw nsw i8 [[IDX]], 1
 ; CHECK-NEXT:    [[ADD_I32_IDX_1:%.*]] = getelementptr inbounds i32, ptr 
[[A]], i8 [[IDX_1]]
 ; CHECK-NEXT:    [[C_1:%.*]] = icmp ult ptr [[ADD_I32_IDX_1]], [[UPPER]]
 ; CHECK-NEXT:    [[ADD_I8_IDX_1:%.*]] = getelementptr inbounds i8, ptr [[A]], 
i8 [[IDX_1]]

diff  --git a/llvm/test/Transforms/ConstraintElimination/materialize-flags.ll 
b/llvm/test/Transforms/ConstraintElimination/materialize-flags.ll
index 2ec78db674b04..ef858b2252639 100644
--- a/llvm/test/Transforms/ConstraintElimination/materialize-flags.ll
+++ b/llvm/test/Transforms/ConstraintElimination/materialize-flags.ll
@@ -541,7 +541,7 @@ define i64 @mul_nuw_from_nsw_and_non_negative(i64 %x, i64 
%y, i64 %z) {
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[C_1]])
 ; CHECK-NEXT:    [[C_2:%.*]] = icmp sge i64 [[Y]], 0
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[C_2]])
-; CHECK-NEXT:    [[MUL:%.*]] = mul nsw i64 [[X]], [[Y]]
+; CHECK-NEXT:    [[MUL:%.*]] = mul nuw nsw i64 [[X]], [[Y]]
 ; CHECK-NEXT:    ret i64 [[MUL]]
 ;
 entry:
@@ -585,7 +585,7 @@ define i64 @shl_nuw_from_nsw_and_non_negative(i64 %x, i64 
%s, i64 %z) {
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[C_0]])
 ; CHECK-NEXT:    [[C_1:%.*]] = icmp sge i64 [[Z]], 0
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[C_1]])
-; CHECK-NEXT:    [[SHL:%.*]] = shl nsw i64 [[X]], [[S]]
+; CHECK-NEXT:    [[SHL:%.*]] = shl nuw nsw i64 [[X]], [[S]]
 ; CHECK-NEXT:    ret i64 [[SHL]]
 ;
 entry:

diff  --git a/llvm/test/Transforms/ConstraintElimination/mul-nsw.ll 
b/llvm/test/Transforms/ConstraintElimination/mul-nsw.ll
index 4bae51d2fcb04..3bb135c957574 100644
--- a/llvm/test/Transforms/ConstraintElimination/mul-nsw.ll
+++ b/llvm/test/Transforms/ConstraintElimination/mul-nsw.ll
@@ -16,11 +16,11 @@ define void @slt_mul_nsw_3_known_positive_1(i8 %start, i8 
%high) {
 ; CHECK-NEXT:    call void @use(i1 true)
 ; CHECK-NEXT:    [[START_1:%.*]] = mul nuw nsw i8 [[START]], 1
 ; CHECK-NEXT:    call void @use(i1 true)
-; CHECK-NEXT:    [[START_2:%.*]] = mul nsw i8 [[START]], 2
+; CHECK-NEXT:    [[START_2:%.*]] = mul nuw nsw i8 [[START]], 2
 ; CHECK-NEXT:    call void @use(i1 true)
-; CHECK-NEXT:    [[START_3:%.*]] = mul nsw i8 [[START]], 3
+; CHECK-NEXT:    [[START_3:%.*]] = mul nuw nsw i8 [[START]], 3
 ; CHECK-NEXT:    call void @use(i1 true)
-; CHECK-NEXT:    [[START_4:%.*]] = mul nsw i8 [[START]], 4
+; CHECK-NEXT:    [[START_4:%.*]] = mul nuw nsw i8 [[START]], 4
 ; CHECK-NEXT:    [[C_3:%.*]] = icmp slt i8 [[START_4]], [[HIGH]]
 ; CHECK-NEXT:    call void @use(i1 [[C_3]])
 ; CHECK-NEXT:    ret void
@@ -124,13 +124,13 @@ define void @slt_mul_no_nsw_3_known_positive_1(i8 %start, 
i8 %high) {
 ; CHECK-NEXT:    [[START_1:%.*]] = mul nuw nsw i8 [[START]], 1
 ; CHECK-NEXT:    [[C_4:%.*]] = icmp slt i8 [[START_1]], [[HIGH]]
 ; CHECK-NEXT:    call void @use(i1 [[C_4]])
-; CHECK-NEXT:    [[START_2:%.*]] = mul nsw i8 [[START]], 2
+; CHECK-NEXT:    [[START_2:%.*]] = mul nuw nsw i8 [[START]], 2
 ; CHECK-NEXT:    [[C_5:%.*]] = icmp slt i8 [[START_2]], [[HIGH]]
 ; CHECK-NEXT:    call void @use(i1 [[C_5]])
-; CHECK-NEXT:    [[START_3:%.*]] = mul nsw i8 [[START]], 3
+; CHECK-NEXT:    [[START_3:%.*]] = mul nuw nsw i8 [[START]], 3
 ; CHECK-NEXT:    [[C_6:%.*]] = icmp slt i8 [[START_3]], [[HIGH]]
 ; CHECK-NEXT:    call void @use(i1 [[C_6]])
-; CHECK-NEXT:    [[START_4:%.*]] = mul nsw i8 [[START]], 4
+; CHECK-NEXT:    [[START_4:%.*]] = mul nuw nsw i8 [[START]], 4
 ; CHECK-NEXT:    [[C_7:%.*]] = icmp slt i8 [[START_4]], [[HIGH]]
 ; CHECK-NEXT:    call void @use(i1 [[C_7]])
 ; CHECK-NEXT:    ret void
@@ -656,11 +656,11 @@ define void @slt_mul_nsw_3_known_nonnegative_1(i8 %start, 
i8 %high) {
 ; CHECK-NEXT:    call void @use(i1 true)
 ; CHECK-NEXT:    [[START_1:%.*]] = mul nuw nsw i8 [[START]], 1
 ; CHECK-NEXT:    call void @use(i1 true)
-; CHECK-NEXT:    [[START_2:%.*]] = mul nsw i8 [[START]], 2
+; CHECK-NEXT:    [[START_2:%.*]] = mul nuw nsw i8 [[START]], 2
 ; CHECK-NEXT:    call void @use(i1 true)
-; CHECK-NEXT:    [[START_3:%.*]] = mul nsw i8 [[START]], 3
+; CHECK-NEXT:    [[START_3:%.*]] = mul nuw nsw i8 [[START]], 3
 ; CHECK-NEXT:    call void @use(i1 true)
-; CHECK-NEXT:    [[START_4:%.*]] = mul nsw i8 [[START]], 4
+; CHECK-NEXT:    [[START_4:%.*]] = mul nuw nsw i8 [[START]], 4
 ; CHECK-NEXT:    [[C_3:%.*]] = icmp slt i8 [[START_4]], [[HIGH]]
 ; CHECK-NEXT:    call void @use(i1 [[C_3]])
 ; CHECK-NEXT:    ret void
@@ -768,13 +768,13 @@ define void @slt_mul_nsw_both_var_non_negative_1(i8 
%start, i8 %scale, i8 %high)
 ; CHECK-NEXT:    [[START_1:%.*]] = mul nuw nsw i8 [[START]], 1
 ; CHECK-NEXT:    [[T_1:%.*]] = icmp slt i8 [[START_1]], [[HIGH]]
 ; CHECK-NEXT:    call void @use(i1 [[T_1]])
-; CHECK-NEXT:    [[START_2:%.*]] = mul nsw i8 [[START]], 2
+; CHECK-NEXT:    [[START_2:%.*]] = mul nuw nsw i8 [[START]], 2
 ; CHECK-NEXT:    [[T_2:%.*]] = icmp slt i8 [[START_2]], [[HIGH]]
 ; CHECK-NEXT:    call void @use(i1 [[T_2]])
-; CHECK-NEXT:    [[START_3:%.*]] = mul nsw i8 [[START]], 3
+; CHECK-NEXT:    [[START_3:%.*]] = mul nuw nsw i8 [[START]], 3
 ; CHECK-NEXT:    [[T_3:%.*]] = icmp slt i8 [[START_3]], [[HIGH]]
 ; CHECK-NEXT:    call void @use(i1 [[T_3]])
-; CHECK-NEXT:    [[START_4:%.*]] = mul nsw i8 [[START]], 4
+; CHECK-NEXT:    [[START_4:%.*]] = mul nuw nsw i8 [[START]], 4
 ; CHECK-NEXT:    [[C_3:%.*]] = icmp slt i8 [[START_4]], [[HIGH]]
 ; CHECK-NEXT:    call void @use(i1 [[C_3]])
 ; CHECK-NEXT:    ret void

diff  --git a/llvm/test/Transforms/PhaseOrdering/AArch64/loopflatten.ll 
b/llvm/test/Transforms/PhaseOrdering/AArch64/loopflatten.ll
index 8f106d720f758..76e328329ea74 100644
--- a/llvm/test/Transforms/PhaseOrdering/AArch64/loopflatten.ll
+++ b/llvm/test/Transforms/PhaseOrdering/AArch64/loopflatten.ll
@@ -1,64 +1,155 @@
-; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py 
UTC_ARGS: --version 6
 ; RUN: opt -passes="default<O3>" -enable-loop-flatten 
-loop-flatten-cost-threshold=3 -S %s | FileCheck %s
 
 target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
 target triple = "aarch64"
 
-define dso_local void @_Z3fooPiii(ptr %A, i32 %N, i32 %M) #0 {
-; CHECK-LABEL: @_Z3fooPiii(
-; CHECK-NEXT:  entry:
-; CHECK-NEXT:    [[CMP3:%.*]] = icmp sgt i32 [[N:%.*]], 0
-; CHECK-NEXT:    [[CMP21:%.*]] = icmp sgt i32 [[M:%.*]], 0
+; TODO: @foo_32_bit_iv is no longer flattened, because its i32 bit IV gets
+; widened and LoopFlatten does not recognize the wide form.
+define void @foo_32_bit_iv(ptr %A, i32 %N, i32 %M) {
+; CHECK-LABEL: define void @foo_32_bit_iv(
+; CHECK-SAME: ptr nofree readonly captures(none) [[A:%.*]], i32 [[N:%.*]], i32 
[[M:%.*]]) local_unnamed_addr {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[CMP3:%.*]] = icmp sgt i32 [[N]], 0
+; CHECK-NEXT:    [[CMP21:%.*]] = icmp sgt i32 [[M]], 0
 ; CHECK-NEXT:    [[OR_COND:%.*]] = select i1 [[CMP3]], i1 [[CMP21]], i1 false
-; CHECK-NEXT:    br i1 [[OR_COND]], label 
[[FOR_COND1_PREHEADER_LR_PH_SPLIT_US:%.*]], label [[FOR_COND_CLEANUP:%.*]]
-; CHECK:       for.cond1.preheader.lr.ph.split:
+; CHECK-NEXT:    br i1 [[OR_COND]], label 
%[[INNER_HEADER_PREHEADER_LR_PH_SPLIT:.*]], label %[[EXIT:.*]]
+; CHECK:       [[INNER_HEADER_PREHEADER_LR_PH_SPLIT]]:
 ; CHECK-NEXT:    [[TMP0:%.*]] = zext nneg i32 [[M]] to i64
 ; CHECK-NEXT:    [[TMP1:%.*]] = zext nneg i32 [[N]] to i64
-; CHECK-NEXT:    [[FLATTEN_TRIPCOUNT:%.*]] = mul nuw nsw i64 [[TMP0]], [[TMP1]]
-; CHECK-NEXT:    br label [[FOR_COND1_PREHEADER_US:%.*]]
-; CHECK:       for.cond1.preheader:
-; CHECK-NEXT:    [[INDVAR6:%.*]] = phi i64 [ 0, 
[[FOR_COND1_PREHEADER_LR_PH_SPLIT_US]] ], [ [[INDVAR_NEXT7:%.*]], 
[[FOR_COND1_PREHEADER_US]] ]
-; CHECK-NEXT:    [[ARRAYIDX_US:%.*]] = getelementptr inbounds nuw [4 x i8], 
ptr [[A:%.*]], i64 [[INDVAR6]]
-; CHECK-NEXT:    [[TMP2:%.*]] = load i32, ptr [[ARRAYIDX_US]], align 4
+; CHECK-NEXT:    br label %[[INNER_HEADER_PREHEADER:.*]]
+; CHECK:       [[INNER_HEADER_PREHEADER]]:
+; CHECK-NEXT:    [[INDVAR6:%.*]] = phi i64 [ 0, 
%[[INNER_HEADER_PREHEADER_LR_PH_SPLIT]] ], [ [[INDVAR_NEXT7:%.*]], 
%[[INNER_HEADER_OUTER_LATCH_CRIT_EDGE:.*]] ]
+; CHECK-NEXT:    [[TMP5:%.*]] = trunc i64 [[INDVAR6]] to i32
+; CHECK-NEXT:    [[TMP3:%.*]] = mul i32 [[M]], [[TMP5]]
+; CHECK-NEXT:    [[TMP4:%.*]] = sext i32 [[TMP3]] to i64
+; CHECK-NEXT:    [[INVARIANT_GEP:%.*]] = getelementptr [4 x i8], ptr [[A]], 
i64 [[TMP4]]
+; CHECK-NEXT:    br label %[[INNER_LATCH:.*]]
+; CHECK:       [[INNER_LATCH]]:
+; CHECK-NEXT:    [[INDVAR:%.*]] = phi i64 [ 0, %[[INNER_HEADER_PREHEADER]] ], 
[ [[INDVAR_NEXT:%.*]], %[[INNER_LATCH]] ]
+; CHECK-NEXT:    [[ARRAYIDX:%.*]] = getelementptr [4 x i8], ptr 
[[INVARIANT_GEP]], i64 [[INDVAR]]
+; CHECK-NEXT:    [[TMP2:%.*]] = load i32, ptr [[ARRAYIDX]], align 4
 ; CHECK-NEXT:    tail call void @_Z1fi(i32 [[TMP2]])
-; CHECK-NEXT:    [[INDVAR_NEXT7]] = add nuw i64 [[INDVAR6]], 1
-; CHECK-NEXT:    [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVAR_NEXT7]], 
[[FLATTEN_TRIPCOUNT]]
-; CHECK-NEXT:    br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP]], label 
[[FOR_COND1_PREHEADER_US]]
-; CHECK:       for.cond.cleanup:
+; CHECK-NEXT:    [[INDVAR_NEXT]] = add nuw nsw i64 [[INDVAR]], 1
+; CHECK-NEXT:    [[EXITCOND_NOT1:%.*]] = icmp eq i64 [[INDVAR_NEXT]], [[TMP0]]
+; CHECK-NEXT:    br i1 [[EXITCOND_NOT1]], label 
%[[INNER_HEADER_OUTER_LATCH_CRIT_EDGE]], label %[[INNER_LATCH]]
+; CHECK:       [[INNER_HEADER_OUTER_LATCH_CRIT_EDGE]]:
+; CHECK-NEXT:    [[INDVAR_NEXT7]] = add nuw nsw i64 [[INDVAR6]], 1
+; CHECK-NEXT:    [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVAR_NEXT7]], [[TMP1]]
+; CHECK-NEXT:    br i1 [[EXITCOND_NOT]], label %[[EXIT]], label 
%[[INNER_HEADER_PREHEADER]]
+; CHECK:       [[EXIT]]:
 ; CHECK-NEXT:    ret void
 ;
 entry:
-  br label %for.cond
+  br label %outer.header
 
-for.cond:
-  %i.0 = phi i32 [ 0, %entry ], [ %inc6, %for.cond.cleanup3 ]
-  %cmp = icmp slt i32 %i.0, %N
-  br i1 %cmp, label %for.body, label %for.cond.cleanup
+outer.header:
+  %outer.iv = phi i32 [ 0, %entry ], [ %outer.iv.next, %outer.latch ]
+  %cmp = icmp slt i32 %outer.iv, %N
+  br i1 %cmp, label %outer.body, label %exit
 
-for.cond.cleanup:
-  ret void
-
-for.body:
-  br label %for.cond1
-
-for.cond1:
-  %j.0 = phi i32 [ 0, %for.body ], [ %inc, %for.body4 ]
-  %cmp2 = icmp slt i32 %j.0, %M
-  br i1 %cmp2, label %for.body4, label %for.cond.cleanup3
+outer.body:
+  br label %inner.header
 
-for.cond.cleanup3:
-  %inc6 = add nsw i32 %i.0, 1
-  br label %for.cond
+inner.header:
+  %inner.iv = phi i32 [ 0, %outer.body ], [ %inner.iv.next, %inner.latch ]
+  %cmp2 = icmp slt i32 %inner.iv, %M
+  br i1 %cmp2, label %inner.latch, label %outer.latch
 
-for.body4:
-  %mul = mul nsw i32 %i.0, %M
-  %add = add nsw i32 %mul, %j.0
+inner.latch:
+  %mul = mul nsw i32 %outer.iv, %M
+  %add = add nsw i32 %mul, %inner.iv
   %idxprom = sext i32 %add to i64
   %arrayidx = getelementptr inbounds i32, ptr %A, i64 %idxprom
   %0 = load i32, ptr %arrayidx, align 4
   call void @_Z1fi(i32 %0)
-  %inc = add nsw i32 %j.0, 1
-  br label %for.cond1
+  %inner.iv.next = add nsw i32 %inner.iv, 1
+  br label %inner.header
+
+outer.latch:
+  %outer.iv.next = add nsw i32 %outer.iv, 1
+  br label %outer.header
+
+exit:
+  ret void
 }
 
 declare dso_local void @_Z1fi(i32) #2
+
+; Same nest, but with i64 induction variables.
+define dso_local void @foo_64_bit_iv(ptr %A, i64 %N, i64 %M) {
+; CHECK-LABEL: define dso_local void @foo_64_bit_iv(
+; CHECK-SAME: ptr nofree readonly captures(none) [[A:%.*]], i64 [[N:%.*]], i64 
[[M:%.*]]) local_unnamed_addr {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[CMP3:%.*]] = icmp sgt i64 [[N]], 0
+; CHECK-NEXT:    [[CMP21:%.*]] = icmp sgt i64 [[M]], 0
+; CHECK-NEXT:    [[OR_COND:%.*]] = select i1 [[CMP3]], i1 [[CMP21]], i1 false
+; CHECK-NEXT:    br i1 [[OR_COND]], label 
%[[INNER_HEADER_PREHEADER_LVER_CHECK:.*]], label %[[EXIT:.*]]
+; CHECK:       [[INNER_HEADER_PREHEADER_LVER_CHECK]]:
+; CHECK-NEXT:    [[FLATTEN_MUL:%.*]] = tail call { i64, i1 } 
@llvm.umul.with.overflow.i64(i64 [[N]], i64 [[M]])
+; CHECK-NEXT:    [[FLATTEN_OVERFLOW:%.*]] = extractvalue { i64, i1 } 
[[FLATTEN_MUL]], 1
+; CHECK-NEXT:    br i1 [[FLATTEN_OVERFLOW]], label 
%[[INNER_HEADER_PREHEADER_LVER_ORIG:.*]], label 
%[[INNER_HEADER_PREHEADER_PREHEADER:.*]]
+; CHECK:       [[INNER_HEADER_PREHEADER_PREHEADER]]:
+; CHECK-NEXT:    [[FLATTEN_TRIPCOUNT:%.*]] = extractvalue { i64, i1 } 
[[FLATTEN_MUL]], 0
+; CHECK-NEXT:    [[SMAX:%.*]] = tail call i64 @llvm.smax.i64(i64 
[[FLATTEN_TRIPCOUNT]], i64 1)
+; CHECK-NEXT:    br label %[[INNER_HEADER_PREHEADER:.*]]
+; CHECK:       [[INNER_HEADER_PREHEADER_LVER_ORIG]]:
+; CHECK-NEXT:    [[OUTER_IV4_LVER_ORIG:%.*]] = phi i64 [ 
[[OUTER_IV_NEXT_LVER_ORIG:%.*]], 
%[[INNER_HEADER_OUTER_LATCH_CRIT_EDGE_LVER_ORIG:.*]] ], [ 0, 
%[[INNER_HEADER_PREHEADER_LVER_CHECK]] ]
+; CHECK-NEXT:    [[MUL_LVER_ORIG:%.*]] = mul nuw nsw i64 
[[OUTER_IV4_LVER_ORIG]], [[M]]
+; CHECK-NEXT:    [[TMP0:%.*]] = getelementptr [4 x i8], ptr [[A]], i64 
[[MUL_LVER_ORIG]]
+; CHECK-NEXT:    br label %[[INNER_LATCH_LVER_ORIG:.*]]
+; CHECK:       [[INNER_LATCH_LVER_ORIG]]:
+; CHECK-NEXT:    [[INNER_IV2_LVER_ORIG:%.*]] = phi i64 [ 0, 
%[[INNER_HEADER_PREHEADER_LVER_ORIG]] ], [ [[INNER_IV_NEXT_LVER_ORIG:%.*]], 
%[[INNER_LATCH_LVER_ORIG]] ]
+; CHECK-NEXT:    [[ARRAYIDX_LVER_ORIG:%.*]] = getelementptr [4 x i8], ptr 
[[TMP0]], i64 [[INNER_IV2_LVER_ORIG]]
+; CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[ARRAYIDX_LVER_ORIG]], align 4
+; CHECK-NEXT:    tail call void @_Z1fi(i32 [[TMP1]])
+; CHECK-NEXT:    [[INNER_IV_NEXT_LVER_ORIG]] = add nuw nsw i64 
[[INNER_IV2_LVER_ORIG]], 1
+; CHECK-NEXT:    [[EXITCOND7_NOT:%.*]] = icmp eq i64 
[[INNER_IV_NEXT_LVER_ORIG]], [[M]]
+; CHECK-NEXT:    br i1 [[EXITCOND7_NOT]], label 
%[[INNER_HEADER_OUTER_LATCH_CRIT_EDGE_LVER_ORIG]], label 
%[[INNER_LATCH_LVER_ORIG]]
+; CHECK:       [[INNER_HEADER_OUTER_LATCH_CRIT_EDGE_LVER_ORIG]]:
+; CHECK-NEXT:    [[OUTER_IV_NEXT_LVER_ORIG]] = add nuw nsw i64 
[[OUTER_IV4_LVER_ORIG]], 1
+; CHECK-NEXT:    [[EXITCOND8_NOT:%.*]] = icmp eq i64 
[[OUTER_IV_NEXT_LVER_ORIG]], [[N]]
+; CHECK-NEXT:    br i1 [[EXITCOND8_NOT]], label %[[EXIT]], label 
%[[INNER_HEADER_PREHEADER_LVER_ORIG]]
+; CHECK:       [[INNER_HEADER_PREHEADER]]:
+; CHECK-NEXT:    [[OUTER_IV4:%.*]] = phi i64 [ [[OUTER_IV_NEXT:%.*]], 
%[[INNER_HEADER_PREHEADER]] ], [ 0, %[[INNER_HEADER_PREHEADER_PREHEADER]] ]
+; CHECK-NEXT:    [[FLATTEN_ARRAYIDX:%.*]] = getelementptr [4 x i8], ptr [[A]], 
i64 [[OUTER_IV4]]
+; CHECK-NEXT:    [[TMP2:%.*]] = load i32, ptr [[FLATTEN_ARRAYIDX]], align 4
+; CHECK-NEXT:    tail call void @_Z1fi(i32 [[TMP2]])
+; CHECK-NEXT:    [[OUTER_IV_NEXT]] = add nuw nsw i64 [[OUTER_IV4]], 1
+; CHECK-NEXT:    [[EXITCOND_NOT:%.*]] = icmp eq i64 [[OUTER_IV_NEXT]], [[SMAX]]
+; CHECK-NEXT:    br i1 [[EXITCOND_NOT]], label %[[EXIT]], label 
%[[INNER_HEADER_PREHEADER]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %outer.header
+
+outer.header:
+  %outer.iv = phi i64 [ 0, %entry ], [ %outer.iv.next, %outer.latch ]
+  %cmp = icmp slt i64 %outer.iv, %N
+  br i1 %cmp, label %outer.body, label %exit
+
+outer.body:
+  br label %inner.header
+
+inner.header:
+  %inner.iv = phi i64 [ 0, %outer.body ], [ %inner.iv.next, %inner.latch ]
+  %cmp2 = icmp slt i64 %inner.iv, %M
+  br i1 %cmp2, label %inner.latch, label %outer.latch
+
+inner.latch:
+  %mul = mul nsw i64 %outer.iv, %M
+  %add = add nsw i64 %mul, %inner.iv
+  %arrayidx = getelementptr inbounds i32, ptr %A, i64 %add
+  %0 = load i32, ptr %arrayidx, align 4
+  call void @_Z1fi(i32 %0)
+  %inner.iv.next = add nsw i64 %inner.iv, 1
+  br label %inner.header
+
+outer.latch:
+  %outer.iv.next = add nsw i64 %outer.iv, 1
+  br label %outer.header
+
+exit:
+  ret void
+}


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to