[InstCombine] Drop alignment assumptions more aggressively (#196060)

`computeKnownBits` gets a context instruction now, which allows dropping
assumes based on previous assumes.
This commit is contained in:
Nikolas Klauser 2026-05-07 09:03:59 +02:00 committed by GitHub
parent b68f6c0e8b
commit 3d6a7bf32d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 43 additions and 24 deletions

View file

@ -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.

View file

@ -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.

View file

@ -3784,7 +3784,7 @@ static Value *simplifyICmpWithDominatingAssume(CmpPredicate Predicate,
CallInst *Assume = cast<CallInst>(AssumeVH);
if (std::optional<bool> 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);
}
}

View file

@ -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);
}

View file

@ -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<TruncInst>(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<CallInst>(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<ICmpInst>(Arg);

View file

@ -3674,13 +3674,10 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) {
if (!UO || isa<Argument>(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());
}

View file

@ -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 {