https://github.com/fhahn updated https://github.com/llvm/llvm-project/pull/219663
>From 79662e61289a68ef9ad52c253836cccf2c45f9af Mon Sep 17 00:00:00 2001 From: Florian Hahn <[email protected]> Date: Sat, 29 Aug 2026 10:18:37 +0100 Subject: [PATCH 1/3] [LoopFlatten] Modernize phase ordering test, add 64 bit IV variant. --- .../PhaseOrdering/AArch64/loopflatten.ll | 155 +++++++++++++----- 1 file changed, 117 insertions(+), 38 deletions(-) diff --git a/llvm/test/Transforms/PhaseOrdering/AArch64/loopflatten.ll b/llvm/test/Transforms/PhaseOrdering/AArch64/loopflatten.ll index 8f106d720f758..9fd480d64a218 100644 --- a/llvm/test/Transforms/PhaseOrdering/AArch64/loopflatten.ll +++ b/llvm/test/Transforms/PhaseOrdering/AArch64/loopflatten.ll @@ -1,64 +1,143 @@ -; 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 +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_PREHEADER]] ] +; CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds nuw [4 x i8], ptr [[A]], i64 [[INDVAR6]] +; 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: 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 +} >From a29091e819ae80074a7d0333e77c0e4c0bdbb9e5 Mon Sep 17 00:00:00 2001 From: Florian Hahn <[email protected]> Date: Thu, 27 Aug 2026 19:11:37 +0100 Subject: [PATCH 2/3] [ConstraintElim] Add NUW for mul/shl with NSW and non-positive ops. 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. We still flatten if the IV is i64 at the outset, i.e. no widening needed (added new variant of test). 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 Compile-time impact is in the noise: https://llvm-compile-time-tracker.com/compare.php?from=eb85a8e8073eb60b773f53b0cc3915b035c37ee9&to=8e7659c55fac9e56391927333630027c0b882e78&stat=instructions:u Alive2 Proof: https://alive2.llvm.org/ce/z/C7QZ4P --- .../Scalar/ConstraintElimination.cpp | 47 +++++++++++-------- .../gep-arithmetic-different-types.ll | 4 +- .../materialize-flags.ll | 4 +- .../ConstraintElimination/mul-nsw.ll | 24 +++++----- .../PhaseOrdering/AArch64/loopflatten.ll | 22 +++++++-- 5 files changed, 61 insertions(+), 40 deletions(-) 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-different-types.ll b/llvm/test/Transforms/ConstraintElimination/gep-arithmetic-different-types.ll index fe67e0a822d9a..47d8ec22dda42 100644 --- a/llvm/test/Transforms/ConstraintElimination/gep-arithmetic-different-types.ll +++ b/llvm/test/Transforms/ConstraintElimination/gep-arithmetic-different-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 9fd480d64a218..76e328329ea74 100644 --- a/llvm/test/Transforms/PhaseOrdering/AArch64/loopflatten.ll +++ b/llvm/test/Transforms/PhaseOrdering/AArch64/loopflatten.ll @@ -4,6 +4,8 @@ target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128" target triple = "aarch64" +; 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 { @@ -15,15 +17,25 @@ define void @foo_32_bit_iv(ptr %A, i32 %N, i32 %M) { ; 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 %[[INNER_HEADER_PREHEADER:.*]] ; CHECK: [[INNER_HEADER_PREHEADER]]: -; CHECK-NEXT: [[INDVAR6:%.*]] = phi i64 [ 0, %[[INNER_HEADER_PREHEADER_LR_PH_SPLIT]] ], [ [[INDVAR_NEXT7:%.*]], %[[INNER_HEADER_PREHEADER]] ] -; CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds nuw [4 x i8], ptr [[A]], i64 [[INDVAR6]] +; 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: [[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 >From efe6cac83955eced772fda33461b0bbf37365753 Mon Sep 17 00:00:00 2001 From: Florian Hahn <[email protected]> Date: Sat, 29 Aug 2026 11:38:54 +0100 Subject: [PATCH 3/3] !fixup update clang tests --- clang/test/CodeGen/attr-counted-by-or-null-for-pointers.c | 3 +-- clang/test/CodeGen/attr-sized-by-for-pointers.c | 4 ++-- clang/test/CodeGen/attr-sized-by-or-null-for-pointers.c | 4 ++-- 3 files changed, 5 insertions(+), 6 deletions(-) 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]] _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
