From 3d6a7bf32dfd2ffee9796a36e1a79f6e238179ee Mon Sep 17 00:00:00 2001 From: Nikolas Klauser Date: Thu, 7 May 2026 09:03:59 +0200 Subject: [PATCH] [InstCombine] Drop alignment assumptions more aggressively (#196060) `computeKnownBits` gets a context instruction now, which allows dropping assumes based on previous assumes. --- llvm/include/llvm/Analysis/SimplifyQuery.h | 6 +++++ llvm/include/llvm/Analysis/ValueTracking.h | 5 ++++ llvm/lib/Analysis/InstructionSimplify.cpp | 2 +- llvm/lib/Analysis/ScalarEvolution.cpp | 4 ++- llvm/lib/Analysis/ValueTracking.cpp | 26 ++++++++----------- .../InstCombine/InstCombineCalls.cpp | 11 +++----- llvm/test/Transforms/InstCombine/assume.ll | 13 ++++++++++ 7 files changed, 43 insertions(+), 24 deletions(-) diff --git a/llvm/include/llvm/Analysis/SimplifyQuery.h b/llvm/include/llvm/Analysis/SimplifyQuery.h index d1d34f22a2fc..b81b1dae2747 100644 --- a/llvm/include/llvm/Analysis/SimplifyQuery.h +++ b/llvm/include/llvm/Analysis/SimplifyQuery.h @@ -86,6 +86,7 @@ struct SimplifyQuery { /// possible values for uses of undef. If it is false, simplifications are not /// allowed to assume a particular value for a use of undef for example. bool CanUseUndef = true; + bool AllowEphemerals = false; SimplifyQuery(const DataLayout &DL, const Instruction *CXTI = nullptr) : DL(DL), CxtI(CXTI) {} @@ -115,6 +116,11 @@ struct SimplifyQuery { Copy.CanUseUndef = false; return Copy; } + SimplifyQuery allowEphemerals(bool AllowEphemerals) const { + SimplifyQuery Copy(*this); + Copy.AllowEphemerals = AllowEphemerals; + return Copy; + } /// If CanUseUndef is true, returns whether \p V is undef. /// Otherwise always return false. diff --git a/llvm/include/llvm/Analysis/ValueTracking.h b/llvm/include/llvm/Analysis/ValueTracking.h index ff2712efe1ef..00469421d55b 100644 --- a/llvm/include/llvm/Analysis/ValueTracking.h +++ b/llvm/include/llvm/Analysis/ValueTracking.h @@ -629,6 +629,11 @@ LLVM_ABI bool isValidAssumeForContext(const Instruction *I, const DominatorTree *DT = nullptr, bool AllowEphemerals = false); +inline bool isValidAssumeForContext(const Instruction *I, + const SimplifyQuery &Q) { + return isValidAssumeForContext(I, Q.CxtI, Q.DT, Q.AllowEphemerals); +} + /// Returns true, if no instruction between \p Assume and \p CtxI may free /// memory and the function is marked as NoSync. The latter ensures the current /// function cannot arrange for another thread to free on its behalf. diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp index af21e46563f9..2bd0eb269510 100644 --- a/llvm/lib/Analysis/InstructionSimplify.cpp +++ b/llvm/lib/Analysis/InstructionSimplify.cpp @@ -3784,7 +3784,7 @@ static Value *simplifyICmpWithDominatingAssume(CmpPredicate Predicate, CallInst *Assume = cast(AssumeVH); if (std::optional Imp = isImpliedCondition( Assume->getArgOperand(0), Predicate, LHS, RHS, Q.DL)) - if (isValidAssumeForContext(Assume, Q.CxtI, Q.DT)) + if (isValidAssumeForContext(Assume, Q)) return ConstantInt::get(getCompareTy(LHS), *Imp); } } diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp index 74037b727433..e15ed1591ed6 100644 --- a/llvm/lib/Analysis/ScalarEvolution.cpp +++ b/llvm/lib/Analysis/ScalarEvolution.cpp @@ -6558,7 +6558,9 @@ APInt ScalarEvolution::getConstantMultipleImpl(const SCEV *S, CtxI = I; } unsigned Known = - computeKnownBits(U->getValue(), getDataLayout(), &AC, CtxI, &DT) + computeKnownBits(U->getValue(), + SimplifyQuery(getDataLayout(), &DT, &AC, CtxI) + .allowEphemerals(true)) .countMinTrailingZeros(); return GetShiftedByZeros(Known); } diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp index 8f10bbae3d46..5635c435417d 100644 --- a/llvm/lib/Analysis/ValueTracking.cpp +++ b/llvm/lib/Analysis/ValueTracking.cpp @@ -853,7 +853,7 @@ static bool isKnownNonZeroFromAssume(const Value *V, const SimplifyQuery &Q) { return false; }(); - if (AssumeImpliesNonNull && isValidAssumeForContext(I, Q.CxtI, Q.DT)) + if (AssumeImpliesNonNull && isValidAssumeForContext(I, Q)) return true; } continue; @@ -869,7 +869,7 @@ static bool isKnownNonZeroFromAssume(const Value *V, const SimplifyQuery &Q) { if (!match(I->getArgOperand(0), m_c_ICmp(Pred, m_V, m_Value(RHS)))) continue; - if (cmpExcludesZero(Pred, RHS) && isValidAssumeForContext(I, Q.CxtI, Q.DT)) + if (cmpExcludesZero(Pred, RHS) && isValidAssumeForContext(I, Q)) return true; } @@ -1095,12 +1095,8 @@ void llvm::computeKnownBitsFromContext(const Value *V, KnownBits &Known, continue; if (RetainedKnowledge RK = getKnowledgeFromBundle( *I, I->bundle_op_info_begin()[Elem.Index])) { - // Allow AllowEphemerals in isValidAssumeForContext, as the CxtI might - // be the producer of the pointer in the bundle. At the moment, align - // assumptions aren't optimized away. if (RK.WasOn == V && RK.AttrKind == Attribute::Alignment && - isPowerOf2_64(RK.ArgValue) && - isValidAssumeForContext(I, Q.CxtI, Q.DT, /*AllowEphemerals*/ true)) + isPowerOf2_64(RK.ArgValue) && isValidAssumeForContext(I, Q)) Known.Zero.setLowBits(Log2_64(RK.ArgValue)); } continue; @@ -1112,14 +1108,14 @@ void llvm::computeKnownBitsFromContext(const Value *V, KnownBits &Known, Value *Arg = I->getArgOperand(0); - if (Arg == V && isValidAssumeForContext(I, Q.CxtI, Q.DT)) { + if (Arg == V && isValidAssumeForContext(I, Q)) { assert(BitWidth == 1 && "assume operand is not i1?"); (void)BitWidth; Known.setAllOnes(); return; } if (match(Arg, m_Not(m_Specific(V))) && - isValidAssumeForContext(I, Q.CxtI, Q.DT)) { + isValidAssumeForContext(I, Q)) { assert(BitWidth == 1 && "assume operand is not i1?"); (void)BitWidth; Known.setAllZero(); @@ -1127,7 +1123,7 @@ void llvm::computeKnownBitsFromContext(const Value *V, KnownBits &Known, } auto *Trunc = dyn_cast(Arg); if (Trunc && Trunc->getOperand(0) == V && - isValidAssumeForContext(I, Q.CxtI, Q.DT)) { + isValidAssumeForContext(I, Q)) { if (Trunc->hasNoUnsignedWrap()) { Known = KnownBits::makeConstant(APInt(BitWidth, 1)); return; @@ -1144,7 +1140,7 @@ void llvm::computeKnownBitsFromContext(const Value *V, KnownBits &Known, if (!Cmp) continue; - if (!isValidAssumeForContext(I, Q.CxtI, Q.DT)) + if (!isValidAssumeForContext(I, Q)) continue; computeKnownBitsFromICmpCond(V, Cmp, Known, Q, /*Invert=*/false); @@ -2670,7 +2666,7 @@ bool llvm::isKnownToBeAPowerOfTwo(const Value *V, bool OrZero, CallInst *I = cast(AssumeVH); if (isImpliedToBeAPowerOfTwoFromCond(V, OrZero, I->getArgOperand(0), /*CondIsTrue=*/true) && - isValidAssumeForContext(I, Q.CxtI, Q.DT)) + isValidAssumeForContext(I, Q)) return true; } } @@ -4118,7 +4114,7 @@ static bool isKnownNonEqualFromContext(const Value *V1, const Value *V2, if (isImpliedCondition(I->getArgOperand(0), ICmpInst::ICMP_NE, V1, V2, Q.DL, /*LHSIsTrue=*/true, Depth) .value_or(false) && - isValidAssumeForContext(I, Q.CxtI, Q.DT)) + isValidAssumeForContext(I, Q)) return true; } @@ -4891,7 +4887,7 @@ static KnownFPClass computeKnownFPClassFromContext(const Value *V, assert(I->getIntrinsicID() == Intrinsic::assume && "must be an assume intrinsic"); - if (!isValidAssumeForContext(I, Q.CxtI, Q.DT)) + if (!isValidAssumeForContext(I, Q)) continue; computeKnownFPClassFromCond(V, I->getArgOperand(0), @@ -10470,7 +10466,7 @@ ConstantRange llvm::computeConstantRange(const Value *V, bool ForSigned, assert(I->getIntrinsicID() == Intrinsic::assume && "must be an assume intrinsic"); - if (!isValidAssumeForContext(I, SQ.CxtI, SQ.DT)) + if (!isValidAssumeForContext(I, SQ)) continue; Value *Arg = I->getArgOperand(0); ICmpInst *Cmp = dyn_cast(Arg); diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp index da258b3b34e2..b4e2ebea4d19 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp @@ -3674,13 +3674,10 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) { if (!UO || isa(UO)) continue; - // Compute known bits for the pointer, passing nullptr as context to - // avoid computeKnownBits using the assumption we are about to remove - // for reasoning. - KnownBits Known = computeKnownBits(RK.WasOn, /*CtxI=*/nullptr); - unsigned TZ = std::min(Known.countMinTrailingZeros(), - Value::MaxAlignmentExponent); - if ((1ULL << TZ) < RK.ArgValue) + // Compute known bits for the pointer and drop the assume if the + // known alignment isn't increased by it. + if ((1ULL << computeKnownBits(RK.WasOn, II).countMinTrailingZeros()) < + RK.ArgValue) continue; return CallBase::removeOperandBundle(II, OBU.getTagID()); } diff --git a/llvm/test/Transforms/InstCombine/assume.ll b/llvm/test/Transforms/InstCombine/assume.ll index 6b4343022938..40fe2f5f46f1 100644 --- a/llvm/test/Transforms/InstCombine/assume.ll +++ b/llvm/test/Transforms/InstCombine/assume.ll @@ -7,6 +7,7 @@ target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-unknown-linux-gnu" declare void @llvm.assume(i1) #1 +declare ptr @get_ptr() ; Check that the assume has not been removed: @@ -80,6 +81,18 @@ entry: ret void } +define void @redundant_align() { +; CHECK-LABEL: @redundant_align( +; CHECK-NEXT: [[PTR:%.*]] = call ptr @get_ptr() +; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[PTR]], i64 8) ] +; CHECK-NEXT: ret void +; + %ptr = call ptr @get_ptr() + call void @llvm.assume(i1 true) [ "align"(ptr %ptr, i64 8) ] + call void @llvm.assume(i1 true) [ "align"(ptr %ptr, i64 8) ] + ret void +} + ; Same check as in @foo1, but make sure it works if the assume is first too. define i32 @foo2(ptr %a) #0 {