[Attributor][FIX] Pipe UsedAssumedInformation through more interfaces

`UsedAssumedInformation` is a return argument utilized to determine what
information is known. Most APIs used it already but
`genericValueTraversal` did not. This adds it to `genericValueTraversal`
and replaces `AllCallSitesKnown` of `checkForAllCallSites` with the
commonly used `UsedAssumedInformation`.

This was supposed to be a NFC commit, then the test change appeared.
Turns out, we had one user of `AllCallSitesKnown` (AANoReturn) and the
way we set `AllCallSitesKnown` was wrong as we ignored the fact some
call sites were optimistically assumed dead. Included a dedicated test
for this as well now.

Fixes https://github.com/llvm/llvm-project/issues/53884
This commit is contained in:
Johannes Doerfert 2022-02-16 11:05:09 -06:00
parent 67ab4c010b
commit 6ed1ef0643
5 changed files with 143 additions and 84 deletions
llvm
include/llvm/Transforms/IPO
lib/Transforms/IPO
test/Transforms

View File

@ -192,6 +192,7 @@ bool getAssumedUnderlyingObjects(Attributor &A, const Value &Ptr,
SmallVectorImpl<Value *> &Objects,
const AbstractAttribute &QueryingAA,
const Instruction *CtxI,
bool &UsedAssumedInformation,
bool Intraprocedural = false);
/// Collect all potential values of the one stored by \p SI into
@ -1824,23 +1825,24 @@ public:
/// This method will evaluate \p Pred on call sites and return
/// true if \p Pred holds in every call sites. However, this is only possible
/// all call sites are known, hence the function has internal linkage.
/// If true is returned, \p AllCallSitesKnown is set if all possible call
/// sites of the function have been visited.
/// If true is returned, \p UsedAssumedInformation is set if assumed
/// information was used to skip or simplify potential call sites.
bool checkForAllCallSites(function_ref<bool(AbstractCallSite)> Pred,
const AbstractAttribute &QueryingAA,
bool RequireAllCallSites, bool &AllCallSitesKnown);
bool RequireAllCallSites,
bool &UsedAssumedInformation);
/// Check \p Pred on all call sites of \p Fn.
///
/// This method will evaluate \p Pred on call sites and return
/// true if \p Pred holds in every call sites. However, this is only possible
/// all call sites are known, hence the function has internal linkage.
/// If true is returned, \p AllCallSitesKnown is set if all possible call
/// sites of the function have been visited.
/// If true is returned, \p UsedAssumedInformation is set if assumed
/// information was used to skip or simplify potential call sites.
bool checkForAllCallSites(function_ref<bool(AbstractCallSite)> Pred,
const Function &Fn, bool RequireAllCallSites,
const AbstractAttribute *QueryingAA,
bool &AllCallSitesKnown);
bool &UsedAssumedInformation);
/// Check \p Pred on all values potentially returned by \p F.
///

View File

@ -320,7 +320,8 @@ bool AA::getPotentialCopiesOfStoredValue(
Value &Ptr = *SI.getPointerOperand();
SmallVector<Value *, 8> Objects;
if (!AA::getAssumedUnderlyingObjects(A, Ptr, Objects, QueryingAA, &SI)) {
if (!AA::getAssumedUnderlyingObjects(A, Ptr, Objects, QueryingAA, &SI,
UsedAssumedInformation)) {
LLVM_DEBUG(
dbgs() << "Underlying objects stored into could not be determined\n";);
return false;
@ -514,10 +515,10 @@ isPotentiallyReachable(Attributor &A, const Instruction &FromI,
return true;
};
bool AllCallSitesKnown;
bool UsedAssumedInformation = false;
Result = !A.checkForAllCallSites(CheckCallSite, *FromFn,
/* RequireAllCallSites */ true,
&QueryingAA, AllCallSitesKnown);
&QueryingAA, UsedAssumedInformation);
if (Result) {
LLVM_DEBUG(dbgs() << "[AA] stepping back to call sites from " << *CurFromI
<< " in @" << FromFn->getName()
@ -1277,7 +1278,7 @@ bool Attributor::checkForAllUses(
bool Attributor::checkForAllCallSites(function_ref<bool(AbstractCallSite)> Pred,
const AbstractAttribute &QueryingAA,
bool RequireAllCallSites,
bool &AllCallSitesKnown) {
bool &UsedAssumedInformation) {
// We can try to determine information from
// the call sites. However, this is only possible all call sites are known,
// hence the function has internal linkage.
@ -1286,31 +1287,26 @@ bool Attributor::checkForAllCallSites(function_ref<bool(AbstractCallSite)> Pred,
if (!AssociatedFunction) {
LLVM_DEBUG(dbgs() << "[Attributor] No function associated with " << IRP
<< "\n");
AllCallSitesKnown = false;
return false;
}
return checkForAllCallSites(Pred, *AssociatedFunction, RequireAllCallSites,
&QueryingAA, AllCallSitesKnown);
&QueryingAA, UsedAssumedInformation);
}
bool Attributor::checkForAllCallSites(function_ref<bool(AbstractCallSite)> Pred,
const Function &Fn,
bool RequireAllCallSites,
const AbstractAttribute *QueryingAA,
bool &AllCallSitesKnown) {
bool &UsedAssumedInformation) {
if (RequireAllCallSites && !Fn.hasLocalLinkage()) {
LLVM_DEBUG(
dbgs()
<< "[Attributor] Function " << Fn.getName()
<< " has no internal linkage, hence not all call sites are known\n");
AllCallSitesKnown = false;
return false;
}
// If we do not require all call sites we might not see all.
AllCallSitesKnown = RequireAllCallSites;
SmallVector<const Use *, 8> Uses(make_pointer_range(Fn.uses()));
for (unsigned u = 0; u < Uses.size(); ++u) {
const Use &U = *Uses[u];
@ -1322,7 +1318,6 @@ bool Attributor::checkForAllCallSites(function_ref<bool(AbstractCallSite)> Pred,
dbgs() << "[Attributor] Check use: " << *U << " in " << *U.getUser()
<< "\n";
});
bool UsedAssumedInformation = false;
if (isAssumedDead(U, QueryingAA, nullptr, UsedAssumedInformation,
/* CheckBBLivenessOnly */ true)) {
LLVM_DEBUG(dbgs() << "[Attributor] Dead use, skip!\n");
@ -1795,7 +1790,7 @@ void Attributor::identifyDeadInternalFunctions() {
if (!F)
continue;
bool AllCallSitesKnown;
bool UsedAssumedInformation = false;
if (checkForAllCallSites(
[&](AbstractCallSite ACS) {
Function *Callee = ACS.getInstruction()->getFunction();
@ -1803,7 +1798,7 @@ void Attributor::identifyDeadInternalFunctions() {
(Functions.count(Callee) && Callee->hasLocalLinkage() &&
!LiveInternalFns.count(Callee));
},
*F, true, nullptr, AllCallSitesKnown)) {
*F, true, nullptr, UsedAssumedInformation)) {
continue;
}
@ -2290,9 +2285,9 @@ bool Attributor::isValidFunctionSignatureRewrite(
}
// Avoid callbacks for now.
bool AllCallSitesKnown;
bool UsedAssumedInformation = false;
if (!checkForAllCallSites(CallSiteCanBeChanged, *Fn, true, nullptr,
AllCallSitesKnown)) {
UsedAssumedInformation)) {
LLVM_DEBUG(dbgs() << "[Attributor] Cannot rewrite all call sites\n");
return false;
}
@ -2305,7 +2300,6 @@ bool Attributor::isValidFunctionSignatureRewrite(
// Forbid must-tail calls for now.
// TODO:
bool UsedAssumedInformation = false;
auto &OpcodeInstMap = InfoCache.getOpcodeInstMapForFunction(*Fn);
if (!checkForAllInstructionsImpl(nullptr, OpcodeInstMap, InstPred, nullptr,
nullptr, {Instruction::Call},
@ -2514,9 +2508,9 @@ ChangeStatus Attributor::rewriteFunctionSignatures(
};
// Use the CallSiteReplacementCreator to create replacement call sites.
bool AllCallSitesKnown;
bool UsedAssumedInformation = false;
bool Success = checkForAllCallSites(CallSiteReplacementCreator, *OldFn,
true, nullptr, AllCallSitesKnown);
true, nullptr, UsedAssumedInformation);
(void)Success;
assert(Success && "Assumed call site replacement to succeed!");

View File

@ -260,7 +260,8 @@ static bool genericValueTraversal(
StateTy &State,
function_ref<bool(Value &, const Instruction *, StateTy &, bool)>
VisitValueCB,
const Instruction *CtxI, bool UseValueSimplify = true, int MaxValues = 16,
const Instruction *CtxI, bool &UsedAssumedInformation,
bool UseValueSimplify = true, int MaxValues = 16,
function_ref<Value *(Value *)> StripCB = nullptr,
bool Intraprocedural = false) {
@ -320,7 +321,6 @@ static bool genericValueTraversal(
// Look through select instructions, visit assumed potential values.
if (auto *SI = dyn_cast<SelectInst>(V)) {
bool UsedAssumedInformation = false;
Optional<Constant *> C = A.getAssumedConstant(
*SI->getCondition(), QueryingAA, UsedAssumedInformation);
bool NoValueYet = !C.hasValue();
@ -347,6 +347,7 @@ static bool genericValueTraversal(
BasicBlock *IncomingBB = PHI->getIncomingBlock(u);
if (LivenessAA->isEdgeDead(IncomingBB, PHI->getParent())) {
AnyDead = true;
UsedAssumedInformation |= !LivenessAA->isAtFixpoint();
continue;
}
Worklist.push_back(
@ -358,7 +359,7 @@ static bool genericValueTraversal(
if (auto *Arg = dyn_cast<Argument>(V)) {
if (!Intraprocedural && !Arg->hasPassPointeeByValueCopyAttr()) {
SmallVector<Item> CallSiteValues;
bool AllCallSitesKnown = true;
bool UsedAssumedInformation = false;
if (A.checkForAllCallSites(
[&](AbstractCallSite ACS) {
// Callbacks might not have a corresponding call site operand,
@ -369,7 +370,7 @@ static bool genericValueTraversal(
CallSiteValues.push_back({CSOp, ACS.getInstruction()});
return true;
},
*Arg->getParent(), true, &QueryingAA, AllCallSitesKnown)) {
*Arg->getParent(), true, &QueryingAA, UsedAssumedInformation)) {
Worklist.append(CallSiteValues);
continue;
}
@ -377,7 +378,6 @@ static bool genericValueTraversal(
}
if (UseValueSimplify && !isa<Constant>(V)) {
bool UsedAssumedInformation = false;
Optional<Value *> SimpleV =
A.getAssumedSimplified(*V, QueryingAA, UsedAssumedInformation);
if (!SimpleV.hasValue())
@ -412,6 +412,7 @@ bool AA::getAssumedUnderlyingObjects(Attributor &A, const Value &Ptr,
SmallVectorImpl<Value *> &Objects,
const AbstractAttribute &QueryingAA,
const Instruction *CtxI,
bool &UsedAssumedInformation,
bool Intraprocedural) {
auto StripCB = [&](Value *V) { return getUnderlyingObject(V); };
SmallPtrSet<Value *, 8> SeenObjects;
@ -424,7 +425,7 @@ bool AA::getAssumedUnderlyingObjects(Attributor &A, const Value &Ptr,
};
if (!genericValueTraversal<decltype(Objects)>(
A, IRPosition::value(Ptr), QueryingAA, Objects, VisitValueCB, CtxI,
true, 32, StripCB, Intraprocedural))
UsedAssumedInformation, true, 32, StripCB, Intraprocedural))
return false;
return true;
}
@ -570,9 +571,9 @@ static void clampCallSiteArgumentStates(Attributor &A, const AAType &QueryingAA,
return T->isValidState();
};
bool AllCallSitesKnown;
bool UsedAssumedInformation = false;
if (!A.checkForAllCallSites(CallSiteCheck, QueryingAA, true,
AllCallSitesKnown))
UsedAssumedInformation))
S.indicatePessimisticFixpoint();
else if (T.hasValue())
S ^= *T;
@ -1877,17 +1878,18 @@ ChangeStatus AAReturnedValuesImpl::updateImpl(Attributor &A) {
return true;
};
bool UsedAssumedInformation = false;
auto ReturnInstCB = [&](Instruction &I) {
ReturnInst &Ret = cast<ReturnInst>(I);
return genericValueTraversal<ReturnInst>(
A, IRPosition::value(*Ret.getReturnValue()), *this, Ret, ReturnValueCB,
&I, /* UseValueSimplify */ true, /* MaxValues */ 16,
&I, UsedAssumedInformation, /* UseValueSimplify */ true,
/* MaxValues */ 16,
/* StripCB */ nullptr, /* Intraprocedural */ true);
};
// Discover returned values from all live returned instructions in the
// associated function.
bool UsedAssumedInformation = false;
if (!A.checkForAllInstructions(ReturnInstCB, *this, {Instruction::Ret},
UsedAssumedInformation))
return indicatePessimisticFixpoint();
@ -2403,8 +2405,10 @@ struct AANonNullFloating : public AANonNullImpl {
};
StateType T;
bool UsedAssumedInformation = false;
if (!genericValueTraversal<StateType>(A, getIRPosition(), *this, T,
VisitValueCB, getCtxI()))
VisitValueCB, getCtxI(),
UsedAssumedInformation))
return indicatePessimisticFixpoint();
return clampStateAndIndicateChange(getState(), T);
@ -2482,14 +2486,15 @@ struct AANoRecurseFunction final : AANoRecurseImpl {
DepClassTy::NONE);
return NoRecurseAA.isKnownNoRecurse();
};
bool AllCallSitesKnown;
if (A.checkForAllCallSites(CallSitePred, *this, true, AllCallSitesKnown)) {
bool UsedAssumedInformation = false;
if (A.checkForAllCallSites(CallSitePred, *this, true,
UsedAssumedInformation)) {
// If we know all call sites and all are known no-recurse, we are done.
// If all known call sites, which might not be all that exist, are known
// to be no-recurse, we are not done but we can continue to assume
// no-recurse. If one of the call sites we have not visited will become
// live, another update is triggered.
if (AllCallSitesKnown)
if (!UsedAssumedInformation)
indicateOptimisticFixpoint();
return ChangeStatus::UNCHANGED;
}
@ -3129,10 +3134,10 @@ struct AANoAliasArgument final
// If the argument is never passed through callbacks, no-alias cannot break
// synchronization.
bool AllCallSitesKnown;
bool UsedAssumedInformation = false;
if (A.checkForAllCallSites(
[](AbstractCallSite ACS) { return !ACS.isCallbackCall(); }, *this,
true, AllCallSitesKnown))
true, UsedAssumedInformation))
return Base::updateImpl(A);
// TODO: add no-alias but make sure it doesn't break synchronization by
@ -3710,9 +3715,8 @@ struct AAIsDeadReturned : public AAIsDeadValueImpl {
return areAllUsesAssumedDead(A, *ACS.getInstruction());
};
bool AllCallSitesKnown;
if (!A.checkForAllCallSites(PredForCallSite, *this, true,
AllCallSitesKnown))
UsedAssumedInformation))
return indicatePessimisticFixpoint();
return ChangeStatus::UNCHANGED;
@ -4295,8 +4299,10 @@ struct AADereferenceableFloating : AADereferenceableImpl {
};
DerefState T;
bool UsedAssumedInformation = false;
if (!genericValueTraversal<DerefState>(A, getIRPosition(), *this, T,
VisitValueCB, getCtxI()))
VisitValueCB, getCtxI(),
UsedAssumedInformation))
return indicatePessimisticFixpoint();
return clampStateAndIndicateChange(getState(), T);
@ -4561,8 +4567,10 @@ struct AAAlignFloating : AAAlignImpl {
};
StateType T;
bool UsedAssumedInformation = false;
if (!genericValueTraversal<StateType>(A, getIRPosition(), *this, T,
VisitValueCB, getCtxI()))
VisitValueCB, getCtxI(),
UsedAssumedInformation))
return indicatePessimisticFixpoint();
// TODO: If we know we visited all incoming values, thus no are assumed
@ -5342,7 +5350,9 @@ struct AAValueSimplifyImpl : AAValueSimplify {
Value &Ptr = *L.getPointerOperand();
SmallVector<Value *, 8> Objects;
if (!AA::getAssumedUnderlyingObjects(A, Ptr, Objects, AA, &L))
bool UsedAssumedInformation = false;
if (!AA::getAssumedUnderlyingObjects(A, Ptr, Objects, AA, &L,
UsedAssumedInformation))
return false;
const auto *TLI =
@ -5354,7 +5364,6 @@ struct AAValueSimplifyImpl : AAValueSimplify {
if (isa<ConstantPointerNull>(Obj)) {
// A null pointer access can be undefined but any offset from null may
// be OK. We do not try to optimize the latter.
bool UsedAssumedInformation = false;
if (!NullPointerIsDefined(L.getFunction(),
Ptr.getType()->getPointerAddressSpace()) &&
A.getAssumedSimplified(Ptr, AA, UsedAssumedInformation) == Obj)
@ -5460,14 +5469,14 @@ struct AAValueSimplifyArgument final : AAValueSimplifyImpl {
// Generate a answer specific to a call site context.
bool Success;
bool AllCallSitesKnown;
bool UsedAssumedInformation = false;
if (hasCallBaseContext() &&
getCallBaseContext()->getCalledFunction() == Arg->getParent())
Success = PredForCallSite(
AbstractCallSite(&getCallBaseContext()->getCalledOperandUse()));
else
Success = A.checkForAllCallSites(PredForCallSite, *this, true,
AllCallSitesKnown);
UsedAssumedInformation);
if (!Success)
if (!askSimplifiedValueForOtherAAs(A))
@ -5737,8 +5746,10 @@ struct AAValueSimplifyFloating : AAValueSimplifyImpl {
};
bool Dummy = false;
bool UsedAssumedInformation = false;
if (!genericValueTraversal<bool>(A, getIRPosition(), *this, Dummy,
VisitValueCB, getCtxI(),
UsedAssumedInformation,
/* UseValueSimplify */ false))
if (!askSimplifiedValueForOtherAAs(A))
return indicatePessimisticFixpoint();
@ -6150,7 +6161,8 @@ ChangeStatus AAHeapToStackFunction::updateImpl(Attributor &A) {
// branches etc.
SmallVector<Value *, 8> Objects;
if (!AA::getAssumedUnderlyingObjects(A, *DI.CB->getArgOperand(0), Objects,
*this, DI.CB)) {
*this, DI.CB,
UsedAssumedInformation)) {
LLVM_DEBUG(
dbgs()
<< "[H2S] Unexpected failure in getAssumedUnderlyingObjects!\n");
@ -6428,10 +6440,10 @@ struct AAPrivatizablePtrArgument final : public AAPrivatizablePtrImpl {
Optional<Type *> identifyPrivatizableType(Attributor &A) override {
// If this is a byval argument and we know all the call sites (so we can
// rewrite them), there is no need to check them explicitly.
bool AllCallSitesKnown;
bool UsedAssumedInformation = false;
if (getIRPosition().hasAttr(Attribute::ByVal) &&
A.checkForAllCallSites([](AbstractCallSite ACS) { return true; }, *this,
true, AllCallSitesKnown))
true, UsedAssumedInformation))
return getAssociatedValue().getType()->getPointerElementType();
Optional<Type *> Ty;
@ -6481,7 +6493,8 @@ struct AAPrivatizablePtrArgument final : public AAPrivatizablePtrImpl {
return !Ty.hasValue() || Ty.getValue();
};
if (!A.checkForAllCallSites(CallSiteCheck, *this, true, AllCallSitesKnown))
if (!A.checkForAllCallSites(CallSiteCheck, *this, true,
UsedAssumedInformation))
return nullptr;
return Ty;
}
@ -6528,9 +6541,9 @@ struct AAPrivatizablePtrArgument final : public AAPrivatizablePtrImpl {
return TTI->areTypesABICompatible(
CB->getCaller(), CB->getCalledFunction(), ReplacementTypes);
};
bool AllCallSitesKnown;
bool UsedAssumedInformation = false;
if (!A.checkForAllCallSites(CallSiteCheck, *this, true,
AllCallSitesKnown)) {
UsedAssumedInformation)) {
LLVM_DEBUG(
dbgs() << "[AAPrivatizablePtr] ABI incompatibility detected for "
<< Fn.getName() << "\n");
@ -6657,7 +6670,7 @@ struct AAPrivatizablePtrArgument final : public AAPrivatizablePtrImpl {
};
if (!A.checkForAllCallSites(IsCompatiblePrivArgOfOtherCallSite, *this, true,
AllCallSitesKnown))
UsedAssumedInformation))
return indicatePessimisticFixpoint();
return ChangeStatus::UNCHANGED;
@ -7757,7 +7770,9 @@ void AAMemoryLocationImpl::categorizePtrValue(
<< getMemoryLocationsAsStr(State.getAssumed()) << "]\n");
SmallVector<Value *, 8> Objects;
bool UsedAssumedInformation = false;
if (!AA::getAssumedUnderlyingObjects(A, Ptr, Objects, *this, &I,
UsedAssumedInformation,
/* Intraprocedural */ true)) {
LLVM_DEBUG(
dbgs() << "[AAMemoryLocation] Pointer locations not categorized\n");
@ -8573,8 +8588,10 @@ struct AAValueConstantRangeFloating : AAValueConstantRangeImpl {
IntegerRangeState T(getBitWidth());
bool UsedAssumedInformation = false;
if (!genericValueTraversal<IntegerRangeState>(A, getIRPosition(), *this, T,
VisitValueCB, getCtxI(),
UsedAssumedInformation,
/* UseValueSimplify */ false))
return indicatePessimisticFixpoint();
@ -9385,8 +9402,10 @@ struct AANoUndefFloating : public AANoUndefImpl {
};
StateType T;
bool UsedAssumedInformation = false;
if (!genericValueTraversal<StateType>(A, getIRPosition(), *this, T,
VisitValueCB, getCtxI()))
VisitValueCB, getCtxI(),
UsedAssumedInformation))
return indicatePessimisticFixpoint();
return clampStateAndIndicateChange(getState(), T);
@ -9503,9 +9522,10 @@ struct AACallEdgesCallSite : public AACallEdgesImpl {
// Process any value that we might call.
auto ProcessCalledOperand = [&](Value *V) {
bool DummyValue = false;
bool UsedAssumedInformation = false;
if (!genericValueTraversal<bool>(A, IRPosition::value(*V), *this,
DummyValue, VisitValue, nullptr,
false)) {
UsedAssumedInformation, false)) {
// If we haven't gone through all values, assume that there are unknown
// callees.
setHasUnknownCallee(true, Change);
@ -9924,12 +9944,13 @@ struct AAAssumptionInfoFunction final : AAAssumptionInfoImpl {
return !getAssumed().empty() || !getKnown().empty();
};
bool AllCallSitesKnown;
bool UsedAssumedInformation = false;
// Get the intersection of all assumptions held by this node's predecessors.
// If we don't know all the call sites then this is either an entry into the
// call graph or an empty node. This node is known to only contain its own
// assumptions and can be propagated to its successors.
if (!A.checkForAllCallSites(CallSitePred, *this, true, AllCallSitesKnown))
if (!A.checkForAllCallSites(CallSitePred, *this, true,
UsedAssumedInformation))
return indicatePessimisticFixpoint();
return Changed ? ChangeStatus::CHANGED : ChangeStatus::UNCHANGED;

View File

@ -1,6 +1,6 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes --check-globals
; RUN: opt -attributor -enable-new-pm=0 -attributor-manifest-internal -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=4 -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_CGSCC_NPM,NOT_CGSCC_OPM,NOT_TUNIT_NPM,IS__TUNIT____,IS________OPM,IS__TUNIT_OPM
; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=4 -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_CGSCC_OPM,NOT_CGSCC_NPM,NOT_TUNIT_OPM,IS__TUNIT____,IS________NPM,IS__TUNIT_NPM
; RUN: opt -attributor -enable-new-pm=0 -attributor-manifest-internal -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=7 -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_CGSCC_NPM,NOT_CGSCC_OPM,NOT_TUNIT_NPM,IS__TUNIT____,IS________OPM,IS__TUNIT_OPM
; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=7 -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_CGSCC_OPM,NOT_CGSCC_NPM,NOT_TUNIT_OPM,IS__TUNIT____,IS________NPM,IS__TUNIT_NPM
; RUN: opt -attributor-cgscc -enable-new-pm=0 -attributor-manifest-internal -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_TUNIT_NPM,NOT_TUNIT_OPM,NOT_CGSCC_NPM,IS__CGSCC____,IS________OPM,IS__CGSCC_OPM
; RUN: opt -aa-pipeline=basic-aa -passes=attributor-cgscc -attributor-manifest-internal -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_TUNIT_NPM,NOT_TUNIT_OPM,NOT_CGSCC_OPM,IS__CGSCC____,IS________NPM,IS__CGSCC_NPM
@ -71,7 +71,7 @@ define void @intrinsic(i8* %dest, i8* %src, i32 %len) {
; CHECK: Function Attrs: argmemonly nofree nosync nounwind willreturn
; CHECK-LABEL: define {{[^@]+}}@intrinsic
; CHECK-SAME: (i8* nocapture nofree writeonly [[DEST:%.*]], i8* nocapture nofree readonly [[SRC:%.*]], i32 [[LEN:%.*]]) #[[ATTR4:[0-9]+]] {
; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i32(i8* noalias nocapture nofree writeonly [[DEST]], i8* noalias nocapture nofree readonly [[SRC]], i32 [[LEN]], i1 noundef false) #[[ATTR8:[0-9]+]]
; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i32(i8* noalias nocapture nofree writeonly [[DEST]], i8* noalias nocapture nofree readonly [[SRC]], i32 [[LEN]], i1 noundef false) #[[ATTR9:[0-9]+]]
; CHECK-NEXT: ret void
;
call void @llvm.memcpy.p0i8.p0i8.i32(i8* %dest, i8* %src, i32 %len, i1 false)
@ -250,6 +250,47 @@ Dead:
ret i32 1
}
define i1 @test_rec_neg(i1 %c) norecurse {
; CHECK: Function Attrs: norecurse
; CHECK-LABEL: define {{[^@]+}}@test_rec_neg
; CHECK-SAME: (i1 [[C:%.*]]) #[[ATTR8:[0-9]+]] {
; CHECK-NEXT: [[RC1:%.*]] = call noundef i1 @rec(i1 noundef true)
; CHECK-NEXT: br i1 [[RC1]], label [[T:%.*]], label [[F:%.*]]
; CHECK: t:
; CHECK-NEXT: [[RC2:%.*]] = call noundef i1 @rec(i1 [[C]])
; CHECK-NEXT: ret i1 [[RC2]]
; CHECK: f:
; CHECK-NEXT: ret i1 [[RC1]]
;
%rc1 = call i1 @rec(i1 true)
br i1 %rc1, label %t, label %f
t:
%rc2 = call i1 @rec(i1 %c)
ret i1 %rc2
f:
ret i1 %rc1
}
define internal i1 @rec(i1 %c1) {
; CHECK-LABEL: define {{[^@]+}}@rec
; CHECK-SAME: (i1 [[C1:%.*]]) {
; CHECK-NEXT: br i1 [[C1]], label [[T:%.*]], label [[F:%.*]]
; CHECK: t:
; CHECK-NEXT: ret i1 true
; CHECK: f:
; CHECK-NEXT: [[R:%.*]] = call i1 @rec(i1 noundef true)
; CHECK-NEXT: call void @unknown()
; CHECK-NEXT: ret i1 false
;
br i1 %c1, label %t, label %f
t:
ret i1 true
f:
%r = call i1 @rec(i1 true)
call void @unknown()
ret i1 false
}
;.
; CHECK: attributes #[[ATTR0]] = { nofree norecurse nosync nounwind readnone willreturn }
; CHECK: attributes #[[ATTR1]] = { nofree nosync nounwind readnone willreturn }
@ -259,5 +300,6 @@ Dead:
; CHECK: attributes #[[ATTR5:[0-9]+]] = { argmemonly nofree nounwind willreturn }
; CHECK: attributes #[[ATTR6]] = { norecurse nosync readnone }
; CHECK: attributes #[[ATTR7]] = { null_pointer_is_valid }
; CHECK: attributes #[[ATTR8]] = { willreturn }
; CHECK: attributes #[[ATTR8]] = { norecurse }
; CHECK: attributes #[[ATTR9]] = { willreturn }
;.

View File

@ -1620,9 +1620,9 @@ attributes #9 = { convergent nounwind readonly willreturn }
; AMDGPU-NEXT: ret void
;
;
; AMDGPU: Function Attrs: convergent noinline norecurse nounwind
; AMDGPU: Function Attrs: convergent noinline nounwind
; AMDGPU-LABEL: define {{[^@]+}}@simple_state_machine_interprocedural_nested_recursive_after.internalized
; AMDGPU-SAME: (i32 [[A:%.*]]) #[[ATTR0]] {
; AMDGPU-SAME: (i32 [[A:%.*]]) #[[ATTR1]] {
; AMDGPU-NEXT: entry:
; AMDGPU-NEXT: [[A_ADDR:%.*]] = alloca i32, align 4
; AMDGPU-NEXT: store i32 [[A]], i32* [[A_ADDR]], align 4
@ -1632,7 +1632,7 @@ attributes #9 = { convergent nounwind readonly willreturn }
; AMDGPU-NEXT: br label [[RETURN:%.*]]
; AMDGPU: if.end:
; AMDGPU-NEXT: [[SUB:%.*]] = sub nsw i32 [[A]], 1
; AMDGPU-NEXT: call void @simple_state_machine_interprocedural_nested_recursive_after.internalized(i32 [[SUB]]) #[[ATTR11:[0-9]+]]
; AMDGPU-NEXT: call void @simple_state_machine_interprocedural_nested_recursive_after.internalized(i32 [[SUB]]) #[[ATTR8]]
; AMDGPU-NEXT: call void @simple_state_machine_interprocedural_nested_recursive_after_after.internalized() #[[ATTR8]]
; AMDGPU-NEXT: br label [[RETURN]]
; AMDGPU: return:
@ -1772,9 +1772,9 @@ attributes #9 = { convergent nounwind readonly willreturn }
; AMDGPU-NEXT: ret void
;
;
; AMDGPU: Function Attrs: convergent noinline norecurse nounwind
; AMDGPU: Function Attrs: convergent noinline nounwind
; AMDGPU-LABEL: define {{[^@]+}}@simple_state_machine_interprocedural_nested_recursive_after_after.internalized
; AMDGPU-SAME: () #[[ATTR0]] {
; AMDGPU-SAME: () #[[ATTR1]] {
; AMDGPU-NEXT: entry:
; AMDGPU-NEXT: [[CAPTURED_VARS_ADDRS:%.*]] = alloca [0 x i8*], align 8
; AMDGPU-NEXT: [[TMP0:%.*]] = call i32 @__kmpc_global_thread_num(%struct.ident_t* noundef @[[GLOB2]]) #[[ATTR3]]
@ -2590,9 +2590,9 @@ attributes #9 = { convergent nounwind readonly willreturn }
; NVPTX-NEXT: ret void
;
;
; NVPTX: Function Attrs: convergent noinline norecurse nounwind
; NVPTX: Function Attrs: convergent noinline nounwind
; NVPTX-LABEL: define {{[^@]+}}@simple_state_machine_interprocedural_nested_recursive_after.internalized
; NVPTX-SAME: (i32 [[A:%.*]]) #[[ATTR0]] {
; NVPTX-SAME: (i32 [[A:%.*]]) #[[ATTR1]] {
; NVPTX-NEXT: entry:
; NVPTX-NEXT: [[A_ADDR:%.*]] = alloca i32, align 4
; NVPTX-NEXT: store i32 [[A]], i32* [[A_ADDR]], align 4
@ -2602,7 +2602,7 @@ attributes #9 = { convergent nounwind readonly willreturn }
; NVPTX-NEXT: br label [[RETURN:%.*]]
; NVPTX: if.end:
; NVPTX-NEXT: [[SUB:%.*]] = sub nsw i32 [[A]], 1
; NVPTX-NEXT: call void @simple_state_machine_interprocedural_nested_recursive_after.internalized(i32 [[SUB]]) #[[ATTR11:[0-9]+]]
; NVPTX-NEXT: call void @simple_state_machine_interprocedural_nested_recursive_after.internalized(i32 [[SUB]]) #[[ATTR8]]
; NVPTX-NEXT: call void @simple_state_machine_interprocedural_nested_recursive_after_after.internalized() #[[ATTR8]]
; NVPTX-NEXT: br label [[RETURN]]
; NVPTX: return:
@ -2741,9 +2741,9 @@ attributes #9 = { convergent nounwind readonly willreturn }
; NVPTX-NEXT: ret void
;
;
; NVPTX: Function Attrs: convergent noinline norecurse nounwind
; NVPTX: Function Attrs: convergent noinline nounwind
; NVPTX-LABEL: define {{[^@]+}}@simple_state_machine_interprocedural_nested_recursive_after_after.internalized
; NVPTX-SAME: () #[[ATTR0]] {
; NVPTX-SAME: () #[[ATTR1]] {
; NVPTX-NEXT: entry:
; NVPTX-NEXT: [[CAPTURED_VARS_ADDRS:%.*]] = alloca [0 x i8*], align 8
; NVPTX-NEXT: [[TMP0:%.*]] = call i32 @__kmpc_global_thread_num(%struct.ident_t* noundef @[[GLOB2]]) #[[ATTR3]]
@ -3315,9 +3315,9 @@ attributes #9 = { convergent nounwind readonly willreturn }
; AMDGPU-DISABLED-NEXT: ret void
;
;
; AMDGPU-DISABLED: Function Attrs: convergent noinline norecurse nounwind
; AMDGPU-DISABLED: Function Attrs: convergent noinline nounwind
; AMDGPU-DISABLED-LABEL: define {{[^@]+}}@simple_state_machine_interprocedural_nested_recursive_after.internalized
; AMDGPU-DISABLED-SAME: (i32 [[A:%.*]]) #[[ATTR0]] {
; AMDGPU-DISABLED-SAME: (i32 [[A:%.*]]) #[[ATTR1]] {
; AMDGPU-DISABLED-NEXT: entry:
; AMDGPU-DISABLED-NEXT: [[A_ADDR:%.*]] = alloca i32, align 4
; AMDGPU-DISABLED-NEXT: store i32 [[A]], i32* [[A_ADDR]], align 4
@ -3327,7 +3327,7 @@ attributes #9 = { convergent nounwind readonly willreturn }
; AMDGPU-DISABLED-NEXT: br label [[RETURN:%.*]]
; AMDGPU-DISABLED: if.end:
; AMDGPU-DISABLED-NEXT: [[SUB:%.*]] = sub nsw i32 [[A]], 1
; AMDGPU-DISABLED-NEXT: call void @simple_state_machine_interprocedural_nested_recursive_after.internalized(i32 [[SUB]]) #[[ATTR11:[0-9]+]]
; AMDGPU-DISABLED-NEXT: call void @simple_state_machine_interprocedural_nested_recursive_after.internalized(i32 [[SUB]]) #[[ATTR8]]
; AMDGPU-DISABLED-NEXT: call void @simple_state_machine_interprocedural_nested_recursive_after_after.internalized() #[[ATTR8]]
; AMDGPU-DISABLED-NEXT: br label [[RETURN]]
; AMDGPU-DISABLED: return:
@ -3436,9 +3436,9 @@ attributes #9 = { convergent nounwind readonly willreturn }
; AMDGPU-DISABLED-NEXT: ret void
;
;
; AMDGPU-DISABLED: Function Attrs: convergent noinline norecurse nounwind
; AMDGPU-DISABLED: Function Attrs: convergent noinline nounwind
; AMDGPU-DISABLED-LABEL: define {{[^@]+}}@simple_state_machine_interprocedural_nested_recursive_after_after.internalized
; AMDGPU-DISABLED-SAME: () #[[ATTR0]] {
; AMDGPU-DISABLED-SAME: () #[[ATTR1]] {
; AMDGPU-DISABLED-NEXT: entry:
; AMDGPU-DISABLED-NEXT: [[CAPTURED_VARS_ADDRS:%.*]] = alloca [0 x i8*], align 8
; AMDGPU-DISABLED-NEXT: [[TMP0:%.*]] = call i32 @__kmpc_global_thread_num(%struct.ident_t* noundef @[[GLOB2]]) #[[ATTR3]]
@ -4010,9 +4010,9 @@ attributes #9 = { convergent nounwind readonly willreturn }
; NVPTX-DISABLED-NEXT: ret void
;
;
; NVPTX-DISABLED: Function Attrs: convergent noinline norecurse nounwind
; NVPTX-DISABLED: Function Attrs: convergent noinline nounwind
; NVPTX-DISABLED-LABEL: define {{[^@]+}}@simple_state_machine_interprocedural_nested_recursive_after.internalized
; NVPTX-DISABLED-SAME: (i32 [[A:%.*]]) #[[ATTR0]] {
; NVPTX-DISABLED-SAME: (i32 [[A:%.*]]) #[[ATTR1]] {
; NVPTX-DISABLED-NEXT: entry:
; NVPTX-DISABLED-NEXT: [[A_ADDR:%.*]] = alloca i32, align 4
; NVPTX-DISABLED-NEXT: store i32 [[A]], i32* [[A_ADDR]], align 4
@ -4022,7 +4022,7 @@ attributes #9 = { convergent nounwind readonly willreturn }
; NVPTX-DISABLED-NEXT: br label [[RETURN:%.*]]
; NVPTX-DISABLED: if.end:
; NVPTX-DISABLED-NEXT: [[SUB:%.*]] = sub nsw i32 [[A]], 1
; NVPTX-DISABLED-NEXT: call void @simple_state_machine_interprocedural_nested_recursive_after.internalized(i32 [[SUB]]) #[[ATTR11:[0-9]+]]
; NVPTX-DISABLED-NEXT: call void @simple_state_machine_interprocedural_nested_recursive_after.internalized(i32 [[SUB]]) #[[ATTR8]]
; NVPTX-DISABLED-NEXT: call void @simple_state_machine_interprocedural_nested_recursive_after_after.internalized() #[[ATTR8]]
; NVPTX-DISABLED-NEXT: br label [[RETURN]]
; NVPTX-DISABLED: return:
@ -4131,9 +4131,9 @@ attributes #9 = { convergent nounwind readonly willreturn }
; NVPTX-DISABLED-NEXT: ret void
;
;
; NVPTX-DISABLED: Function Attrs: convergent noinline norecurse nounwind
; NVPTX-DISABLED: Function Attrs: convergent noinline nounwind
; NVPTX-DISABLED-LABEL: define {{[^@]+}}@simple_state_machine_interprocedural_nested_recursive_after_after.internalized
; NVPTX-DISABLED-SAME: () #[[ATTR0]] {
; NVPTX-DISABLED-SAME: () #[[ATTR1]] {
; NVPTX-DISABLED-NEXT: entry:
; NVPTX-DISABLED-NEXT: [[CAPTURED_VARS_ADDRS:%.*]] = alloca [0 x i8*], align 8
; NVPTX-DISABLED-NEXT: [[TMP0:%.*]] = call i32 @__kmpc_global_thread_num(%struct.ident_t* noundef @[[GLOB2]]) #[[ATTR3]]