[Attributor] Use knowledge retained in llvm.assume (operand bundles)

This patch integrates operand bundle llvm.assumes [0] with the
Attributor. Most IRAttributes will now look at uses of the associated
value and if there are llvm.assume operand bundle uses with the right
tag we will check if they are in the must-be-executed-context (around
the context instruction). Droppable users, which is currently only
llvm::assume, are handled special in some places now as well.

[0] http://lists.llvm.org/pipermail/llvm-dev/2019-December/137632.html

Reviewed By: uenoku

Differential Revision: https://reviews.llvm.org/D74888
This commit is contained in:
Johannes Doerfert 2020-02-20 02:06:48 -06:00
parent 7339fca25f
commit 5699d08b79
8 changed files with 292 additions and 54 deletions

View File

@ -22,6 +22,7 @@
#include "llvm/ADT/DenseMap.h"
namespace llvm {
class IntrinsicInst;
/// Build a call to llvm.assume to preserve informations that can be derived
/// from the given instruction.
@ -84,7 +85,14 @@ struct MinMax {
unsigned Max;
};
using RetainedKnowledgeMap = DenseMap<RetainedKnowledgeKey, MinMax>;
/// A mapping from intrinsics (=`llvm.assume` calls) to a value range
/// (=knowledge) that is encoded in them. How the value range is interpreted
/// depends on the RetainedKnowledgeKey that was used to get this out of the
/// RetainedKnowledgeMap.
using Assume2KnowledgeMap = DenseMap<IntrinsicInst *, MinMax>;
using RetainedKnowledgeMap =
DenseMap<RetainedKnowledgeKey, Assume2KnowledgeMap>;
/// Insert into the map all the informations contained in the operand bundles of
/// the llvm.assume. This should be used instead of hasAttributeInAssume when

View File

@ -110,11 +110,13 @@
#include "llvm/Analysis/TargetTransformInfo.h"
#include "llvm/IR/CallSite.h"
#include "llvm/IR/ConstantRange.h"
#include "llvm/IR/KnowledgeRetention.h"
#include "llvm/IR/PassManager.h"
#include "llvm/Transforms/Utils/CallGraphUpdater.h"
namespace llvm {
struct Attributor;
struct AbstractAttribute;
struct InformationCache;
struct AAIsDead;
@ -391,7 +393,8 @@ struct IRPosition {
/// e.g., the function position if this is an
/// argument position, should be ignored.
bool hasAttr(ArrayRef<Attribute::AttrKind> AKs,
bool IgnoreSubsumingPositions = false) const;
bool IgnoreSubsumingPositions = false,
Attributor *A = nullptr) const;
/// Return the attributes of any kind in \p AKs existing in the IR at a
/// position that will affect this one. While each position can only have a
@ -403,7 +406,8 @@ struct IRPosition {
/// argument position, should be ignored.
void getAttrs(ArrayRef<Attribute::AttrKind> AKs,
SmallVectorImpl<Attribute> &Attrs,
bool IgnoreSubsumingPositions = false) const;
bool IgnoreSubsumingPositions = false,
Attributor *A = nullptr) const;
/// Remove the attribute of kind \p AKs existing in the IR at this position.
void removeAttrs(ArrayRef<Attribute::AttrKind> AKs) const {
@ -463,6 +467,12 @@ private:
bool getAttrsFromIRAttr(Attribute::AttrKind AK,
SmallVectorImpl<Attribute> &Attrs) const;
/// Return the attributes of kind \p AK existing in the IR as operand bundles
/// of an llvm.assume.
bool getAttrsFromAssumes(Attribute::AttrKind AK,
SmallVectorImpl<Attribute> &Attrs,
Attributor &A) const;
protected:
/// The value this position is anchored at.
Value *AnchorVal;
@ -607,6 +617,9 @@ struct InformationCache {
/// Return datalayout used in the module.
const DataLayout &getDL() { return DL; }
/// Return the map conaining all the knowledge we have from `llvm.assume`s.
const RetainedKnowledgeMap &getKnowledgeMap() const { return KnowledgeMap; }
private:
/// A map type from functions to opcode to instruction maps.
using FuncInstOpcodeMapTy = DenseMap<const Function *, OpcodeInstMapTy>;
@ -627,6 +640,9 @@ private:
/// MustBeExecutedContextExplorer
MustBeExecutedContextExplorer Explorer;
/// A map with knowledge retained in `llvm.assume` instructions.
RetainedKnowledgeMap KnowledgeMap;
/// Getters for analysis.
AnalysisGetter &AG;
@ -1710,7 +1726,8 @@ struct IRAttribute : public IRPosition, public Base {
/// See AbstractAttribute::initialize(...).
virtual void initialize(Attributor &A) override {
const IRPosition &IRP = this->getIRPosition();
if (isa<UndefValue>(IRP.getAssociatedValue()) || hasAttr(getAttrKind())) {
if (isa<UndefValue>(IRP.getAssociatedValue()) ||
hasAttr(getAttrKind(), /* IgnoreSubsumingPositions */ false, &A)) {
this->getState().indicateOptimisticFixpoint();
return;
}

View File

@ -206,6 +206,8 @@ static Value *getValueFromBundleOpInfo(IntrinsicInst &Assume,
bool llvm::hasAttributeInAssume(CallInst &AssumeCI, Value *IsOn,
StringRef AttrName, uint64_t *ArgVal,
AssumeQuery AQR) {
assert(isa<IntrinsicInst>(AssumeCI) &&
"this function is intended to be used on llvm.assume");
IntrinsicInst &Assume = cast<IntrinsicInst>(AssumeCI);
assert(Assume.getIntrinsicID() == Intrinsic::assume &&
"this function is intended to be used on llvm.assume");
@ -253,19 +255,19 @@ void llvm::fillMapFromAssume(CallInst &AssumeCI, RetainedKnowledgeMap &Result) {
if (Key.first == nullptr && Key.second == Attribute::None)
continue;
if (!BundleHasArguement(Bundles, BOIE_Argument)) {
Result[Key] = {0, 0};
Result[Key][&Assume] = {0, 0};
continue;
}
unsigned Val = cast<ConstantInt>(
getValueFromBundleOpInfo(Assume, Bundles, BOIE_Argument))
->getZExtValue();
auto Lookup = Result.find(Key);
if (Lookup == Result.end()) {
Result[Key] = {Val, Val};
if (Lookup == Result.end() || !Lookup->second.count(&Assume)) {
Result[Key][&Assume] = {Val, Val};
continue;
}
Lookup->second.Min = std::min(Val, Lookup->second.Min);
Lookup->second.Max = std::max(Val, Lookup->second.Max);
Lookup->second[&Assume].Min = std::min(Val, Lookup->second[&Assume].Min);
Lookup->second[&Assume].Max = std::max(Val, Lookup->second[&Assume].Max);
}
}

View File

@ -677,7 +677,7 @@ SubsumingPositionIterator::SubsumingPositionIterator(const IRPosition &IRP) {
}
bool IRPosition::hasAttr(ArrayRef<Attribute::AttrKind> AKs,
bool IgnoreSubsumingPositions) const {
bool IgnoreSubsumingPositions, Attributor *A) const {
SmallVector<Attribute, 4> Attrs;
for (const IRPosition &EquivIRP : SubsumingPositionIterator(*this)) {
for (Attribute::AttrKind AK : AKs)
@ -689,12 +689,16 @@ bool IRPosition::hasAttr(ArrayRef<Attribute::AttrKind> AKs,
if (IgnoreSubsumingPositions)
break;
}
if (A)
for (Attribute::AttrKind AK : AKs)
if (getAttrsFromAssumes(AK, Attrs, *A))
return true;
return false;
}
void IRPosition::getAttrs(ArrayRef<Attribute::AttrKind> AKs,
SmallVectorImpl<Attribute> &Attrs,
bool IgnoreSubsumingPositions) const {
bool IgnoreSubsumingPositions, Attributor *A) const {
for (const IRPosition &EquivIRP : SubsumingPositionIterator(*this)) {
for (Attribute::AttrKind AK : AKs)
EquivIRP.getAttrsFromIRAttr(AK, Attrs);
@ -704,6 +708,9 @@ void IRPosition::getAttrs(ArrayRef<Attribute::AttrKind> AKs,
if (IgnoreSubsumingPositions)
break;
}
if (A)
for (Attribute::AttrKind AK : AKs)
getAttrsFromAssumes(AK, Attrs, *A);
}
bool IRPosition::getAttrsFromIRAttr(Attribute::AttrKind AK,
@ -723,6 +730,31 @@ bool IRPosition::getAttrsFromIRAttr(Attribute::AttrKind AK,
return HasAttr;
}
bool IRPosition::getAttrsFromAssumes(Attribute::AttrKind AK,
SmallVectorImpl<Attribute> &Attrs,
Attributor &A) const {
assert(getPositionKind() != IRP_INVALID && "Did expect a valid position!");
Value &AssociatedValue = getAssociatedValue();
const Assume2KnowledgeMap &A2K =
A.getInfoCache().getKnowledgeMap().lookup({&AssociatedValue, AK});
// Check if we found any potential assume use, if not we don't need to create
// explorer iterators.
if (A2K.empty())
return false;
LLVMContext &Ctx = AssociatedValue.getContext();
unsigned AttrsSize = Attrs.size();
MustBeExecutedContextExplorer &Explorer =
A.getInfoCache().getMustBeExecutedContextExplorer();
auto EIt = Explorer.begin(getCtxI()), EEnd = Explorer.end(getCtxI());
for (auto &It : A2K)
if (Explorer.findInContextOf(It.first, EIt, EEnd))
Attrs.push_back(Attribute::get(Ctx, AK, It.second.Max));
return AttrsSize != Attrs.size();
}
void IRPosition::verify() {
switch (KindOrArgNo) {
default:
@ -2057,7 +2089,8 @@ struct AANonNullImpl : AANonNull {
/// See AbstractAttribute::initialize(...).
void initialize(Attributor &A) override {
if (!NullIsDefined &&
hasAttr({Attribute::NonNull, Attribute::Dereferenceable}))
hasAttr({Attribute::NonNull, Attribute::Dereferenceable},
/* IgnoreSubsumingPositions */ false, &A))
indicateOptimisticFixpoint();
else if (isa<ConstantPointerNull>(getAssociatedValue()))
indicatePessimisticFixpoint();
@ -3643,7 +3676,7 @@ struct AADereferenceableImpl : AADereferenceable {
void initialize(Attributor &A) override {
SmallVector<Attribute, 4> Attrs;
getAttrs({Attribute::Dereferenceable, Attribute::DereferenceableOrNull},
Attrs);
Attrs, /* IgnoreSubsumingPositions */ false, &A);
for (const Attribute &Attr : Attrs)
takeKnownDerefBytesMaximum(Attr.getValueAsInt());
@ -4394,8 +4427,9 @@ struct AACaptureUseTracker final : public CaptureTracker {
/// See CaptureTracker::shouldExplore(...).
bool shouldExplore(const Use *U) override {
// Check liveness.
return !A.isAssumedDead(*U, &NoCaptureAA, &IsDeadAA);
// Check liveness and ignore droppable users.
return !U->getUser()->isDroppable() &&
!A.isAssumedDead(*U, &NoCaptureAA, &IsDeadAA);
}
/// Update the state according to \p CapturedInMem, \p CapturedInInt, and
@ -6157,6 +6191,10 @@ ChangeStatus AAMemoryBehaviorFloating::updateImpl(Attributor &A) {
if (A.isAssumedDead(*U, this, &LivenessAA))
continue;
// Droppable users, e.g., llvm::assume does not actually perform any action.
if (UserI->isDroppable())
continue;
// Check if the users of UserI should also be visited.
if (followUsersOfUseIn(A, U, UserI))
for (const Use &UserIUse : UserI->uses())
@ -7414,6 +7452,10 @@ bool Attributor::checkForAllUses(function_ref<bool(const Use &, bool &)> Pred,
LLVM_DEBUG(dbgs() << "[Attributor] Dead use, skip!\n");
continue;
}
if (U->getUser()->isDroppable()) {
LLVM_DEBUG(dbgs() << "[Attributor] Droppable user, skip!\n");
continue;
}
bool Follow = false;
if (!Pred(*U, Follow))
@ -8308,11 +8350,18 @@ void Attributor::initializeInformationCache(Function &F) {
"New call site/base instruction type needs to be known in the "
"Attributor.");
break;
case Instruction::Call:
// Calls are interesting but for `llvm.assume` calls we also fill the
// KnowledgeMap as we find them.
if (IntrinsicInst *Assume = dyn_cast<IntrinsicInst>(&I)) {
if (Assume->getIntrinsicID() == Intrinsic::assume)
fillMapFromAssume(*Assume, InfoCache.KnowledgeMap);
}
LLVM_FALLTHROUGH;
case Instruction::Load:
// The alignment of a pointer is interesting for loads.
case Instruction::Store:
// The alignment of a pointer is interesting for stores.
case Instruction::Call:
case Instruction::CallBr:
case Instruction::Invoke:
case Instruction::CleanupRet:

View File

@ -455,5 +455,67 @@ if.end8: ; preds = %if.then5, %if.else6
ret void
}
declare void @unknown()
define void @nonnull_assume_pos(i8* %arg1, i8* %arg2, i8* %arg3, i8* %arg4) {
; ATTRIBUTOR-LABEL: define {{[^@]+}}@nonnull_assume_pos
; ATTRIBUTOR-SAME: (i8* nocapture nofree nonnull readnone dereferenceable(101) [[ARG1:%.*]], i8* nocapture nofree readnone dereferenceable_or_null(31) [[ARG2:%.*]], i8* nocapture nofree nonnull readnone [[ARG3:%.*]], i8* nocapture nofree readnone dereferenceable_or_null(42) [[ARG4:%.*]])
; ATTRIBUTOR-NEXT: call void @llvm.assume(i1 true) #6 [ "nonnull"(i8* undef), "dereferenceable"(i8* undef, i64 1), "dereferenceable"(i8* undef, i64 2), "dereferenceable"(i8* undef, i64 101), "dereferenceable_or_null"(i8* undef, i64 31), "dereferenceable_or_null"(i8* undef, i64 42) ]
; ATTRIBUTOR-NEXT: call void @unknown()
; ATTRIBUTOR-NEXT: ret void
;
call void @llvm.assume(i1 true) [ "nonnull"(i8* %arg3), "dereferenceable"(i8* %arg1, i64 1), "dereferenceable"(i8* %arg1, i64 2), "dereferenceable"(i8* %arg1, i64 101), "dereferenceable_or_null"(i8* %arg2, i64 31), "dereferenceable_or_null"(i8* %arg4, i64 42)]
call void @unknown()
ret void
}
define void @nonnull_assume_neg(i8* %arg1, i8* %arg2, i8* %arg3) {
; ATTRIBUTOR-LABEL: define {{[^@]+}}@nonnull_assume_neg
; ATTRIBUTOR-SAME: (i8* nocapture nofree readnone [[ARG1:%.*]], i8* nocapture nofree readnone [[ARG2:%.*]], i8* nocapture nofree readnone [[ARG3:%.*]])
; ATTRIBUTOR-NEXT: call void @unknown()
; ATTRIBUTOR-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i8* undef, i64 101), "dereferenceable"(i8* undef, i64 -2), "dereferenceable_or_null"(i8* undef, i64 31) ]
; ATTRIBUTOR-NEXT: ret void
;
call void @unknown()
call void @llvm.assume(i1 true) ["dereferenceable"(i8* %arg1, i64 101), "dereferenceable"(i8* %arg2, i64 -2), "dereferenceable_or_null"(i8* %arg3, i64 31)]
ret void
}
define void @nonnull_assume_call(i8* %arg1, i8* %arg2, i8* %arg3, i8* %arg4) {
; ATTRIBUTOR-LABEL: define {{[^@]+}}@nonnull_assume_call
; ATTRIBUTOR-SAME: (i8* [[ARG1:%.*]], i8* [[ARG2:%.*]], i8* [[ARG3:%.*]], i8* [[ARG4:%.*]])
; ATTRIBUTOR-NEXT: call void @unknown()
; ATTRIBUTOR-NEXT: [[P:%.*]] = call nonnull dereferenceable(101) i32* @unkown_ptr()
; ATTRIBUTOR-NEXT: call void @unknown_use32(i32* nonnull dereferenceable(101) [[P]])
; ATTRIBUTOR-NEXT: call void @unknown_use8(i8* nonnull dereferenceable(42) [[ARG4]])
; ATTRIBUTOR-NEXT: call void @unknown_use8(i8* nonnull [[ARG3]])
; ATTRIBUTOR-NEXT: call void @unknown_use8(i8* nonnull dereferenceable(31) [[ARG2]])
; ATTRIBUTOR-NEXT: call void @unknown_use8(i8* nonnull dereferenceable(2) [[ARG1]])
; ATTRIBUTOR-NEXT: call void @llvm.assume(i1 true) [ "nonnull"(i8* [[ARG3]]), "dereferenceable"(i8* [[ARG1]], i64 1), "dereferenceable"(i8* [[ARG1]], i64 2), "dereferenceable"(i32* [[P]], i64 101), "dereferenceable_or_null"(i8* [[ARG2]], i64 31), "dereferenceable_or_null"(i8* [[ARG4]], i64 42) ]
; ATTRIBUTOR-NEXT: call void @unknown_use8(i8* nonnull dereferenceable(2) [[ARG1]])
; ATTRIBUTOR-NEXT: call void @unknown_use8(i8* nonnull dereferenceable(31) [[ARG2]])
; ATTRIBUTOR-NEXT: call void @unknown_use8(i8* nonnull [[ARG3]])
; ATTRIBUTOR-NEXT: call void @unknown_use8(i8* nonnull dereferenceable(42) [[ARG4]])
; ATTRIBUTOR-NEXT: call void @unknown_use32(i32* nonnull dereferenceable(101) [[P]])
; ATTRIBUTOR-NEXT: call void @unknown()
; ATTRIBUTOR-NEXT: ret void
;
call void @unknown()
%p = call i32* @unkown_ptr()
call void @unknown_use32(i32* %p)
call void @unknown_use8(i8* %arg4)
call void @unknown_use8(i8* %arg3)
call void @unknown_use8(i8* %arg2)
call void @unknown_use8(i8* %arg1)
call void @llvm.assume(i1 true) [ "nonnull"(i8* %arg3), "dereferenceable"(i8* %arg1, i64 1), "dereferenceable"(i8* %arg1, i64 2), "dereferenceable"(i32* %p, i64 101), "dereferenceable_or_null"(i8* %arg2, i64 31), "dereferenceable_or_null"(i8* %arg4, i64 42)]
call void @unknown_use8(i8* %arg1)
call void @unknown_use8(i8* %arg2)
call void @unknown_use8(i8* %arg3)
call void @unknown_use8(i8* %arg4)
call void @unknown_use32(i32* %p)
call void @unknown()
ret void
}
declare void @unknown_use8(i8*) willreturn nounwind
declare void @unknown_use32(i32*) willreturn nounwind
declare void @llvm.assume(i1)
!0 = !{i64 10, i64 100}

View File

@ -247,6 +247,64 @@ define void @test14(i8* nocapture %0, i8* nocapture %1) {
ret void
}
; UTC_ARGS: --enable
define void @nonnull_assume_pos(i8* %arg1, i8* %arg2, i8* %arg3, i8* %arg4) {
; ATTRIBUTOR-LABEL: define {{[^@]+}}@nonnull_assume_pos
; ATTRIBUTOR-SAME: (i8* nofree [[ARG1:%.*]], i8* [[ARG2:%.*]], i8* nofree [[ARG3:%.*]], i8* [[ARG4:%.*]])
; ATTRIBUTOR-NEXT: call void @llvm.assume(i1 true) #11 [ "nofree"(i8* [[ARG1]]), "nofree"(i8* [[ARG3]]) ]
; ATTRIBUTOR-NEXT: call void @unknown(i8* nofree [[ARG1]], i8* [[ARG2]], i8* nofree [[ARG3]], i8* [[ARG4]])
; ATTRIBUTOR-NEXT: ret void
;
call void @llvm.assume(i1 true) ["nofree"(i8* %arg1), "nofree"(i8* %arg3)]
call void @unknown(i8* %arg1, i8* %arg2, i8* %arg3, i8* %arg4)
ret void
}
define void @nonnull_assume_neg(i8* %arg1, i8* %arg2, i8* %arg3, i8* %arg4) {
; ATTRIBUTOR-LABEL: define {{[^@]+}}@nonnull_assume_neg
; ATTRIBUTOR-SAME: (i8* [[ARG1:%.*]], i8* [[ARG2:%.*]], i8* [[ARG3:%.*]], i8* [[ARG4:%.*]])
; ATTRIBUTOR-NEXT: call void @unknown(i8* [[ARG1]], i8* [[ARG2]], i8* [[ARG3]], i8* [[ARG4]])
; ATTRIBUTOR-NEXT: call void @llvm.assume(i1 true) [ "nofree"(i8* [[ARG1]]), "nofree"(i8* [[ARG3]]) ]
; ATTRIBUTOR-NEXT: ret void
;
call void @unknown(i8* %arg1, i8* %arg2, i8* %arg3, i8* %arg4)
call void @llvm.assume(i1 true) ["nofree"(i8* %arg1), "nofree"(i8* %arg3)]
ret void
}
define void @nonnull_assume_call(i8* %arg1, i8* %arg2, i8* %arg3, i8* %arg4) {
; ATTRIBUTOR-LABEL: define {{[^@]+}}@nonnull_assume_call
; ATTRIBUTOR-SAME: (i8* [[ARG1:%.*]], i8* [[ARG2:%.*]], i8* [[ARG3:%.*]], i8* [[ARG4:%.*]])
; ATTRIBUTOR-NEXT: call void @unknown(i8* [[ARG1]], i8* [[ARG2]], i8* [[ARG3]], i8* [[ARG4]])
; ATTRIBUTOR-NEXT: call void @use_i8_ptr(i8* noalias readnone [[ARG1]])
; ATTRIBUTOR-NEXT: call void @use_i8_ptr(i8* noalias readnone [[ARG2]])
; ATTRIBUTOR-NEXT: call void @llvm.assume(i1 true) [ "nofree"(i8* [[ARG1]]), "nofree"(i8* [[ARG3]]) ]
; ATTRIBUTOR-NEXT: call void @use_i8_ptr(i8* noalias nofree readnone [[ARG3]])
; ATTRIBUTOR-NEXT: call void @use_i8_ptr(i8* noalias readnone [[ARG4]])
; ATTRIBUTOR-NEXT: call void @use_i8_ptr_ret(i8* noalias nofree readnone [[ARG1]])
; ATTRIBUTOR-NEXT: call void @use_i8_ptr_ret(i8* noalias readnone [[ARG2]])
; ATTRIBUTOR-NEXT: call void @llvm.assume(i1 true) [ "nofree"(i8* [[ARG1]]), "nofree"(i8* [[ARG4]]) ]
; ATTRIBUTOR-NEXT: call void @use_i8_ptr_ret(i8* noalias nofree readnone [[ARG3]])
; ATTRIBUTOR-NEXT: call void @use_i8_ptr_ret(i8* noalias nofree readnone [[ARG4]])
; ATTRIBUTOR-NEXT: ret void
;
call void @unknown(i8* %arg1, i8* %arg2, i8* %arg3, i8* %arg4)
call void @use_i8_ptr(i8* %arg1)
call void @use_i8_ptr(i8* %arg2)
call void @llvm.assume(i1 true) ["nofree"(i8* %arg1), "nofree"(i8* %arg3)]
call void @use_i8_ptr(i8* %arg3)
call void @use_i8_ptr(i8* %arg4)
call void @use_i8_ptr_ret(i8* %arg1)
call void @use_i8_ptr_ret(i8* %arg2)
call void @llvm.assume(i1 true) ["nofree"(i8* %arg1), "nofree"(i8* %arg4)]
call void @use_i8_ptr_ret(i8* %arg3)
call void @use_i8_ptr_ret(i8* %arg4)
ret void
}
declare void @llvm.assume(i1)
declare void @unknown(i8*, i8*, i8*, i8*)
declare void @use_i8_ptr(i8* nocapture readnone) nounwind
declare void @use_i8_ptr_ret(i8* nocapture readnone) nounwind willreturn
declare noalias i8* @malloc(i64)
attributes #0 = { nounwind uwtable noinline }

View File

@ -167,7 +167,6 @@ define void @test13_helper() {
tail call void @test13(i8* %nonnullptr, i8* %maybenullptr, i8* %nonnullptr)
ret void
}
declare void @use_i8_ptr(i8* nofree nocapture readnone) nounwind
define internal void @test13(i8* %a, i8* %b, i8* %c) {
; ATTRIBUTOR: define internal void @test13(i8* noalias nocapture nofree nonnull readnone %a, i8* noalias nocapture nofree readnone %b, i8* noalias nocapture nofree readnone %c)
call void @use_i8_ptr(i8* %a)
@ -839,5 +838,44 @@ define i8* @mybasename(i8* nofree readonly %str) {
ret i8* %cond
}
define void @nonnull_assume_pos(i8* %arg) {
; ATTRIBUTOR-LABEL: define {{[^@]+}}@nonnull_assume_pos
; ATTRIBUTOR-SAME: (i8* nocapture nofree nonnull readnone [[ARG:%.*]])
; ATTRIBUTOR-NEXT: call void @llvm.assume(i1 true) #11 [ "nonnull"(i8* [[ARG]]) ]
; ATTRIBUTOR-NEXT: call void @use_i8_ptr(i8* noalias nocapture nofree nonnull readnone [[ARG]])
; ATTRIBUTOR-NEXT: [[TMP1:%.*]] = call i8* @unknown()
; ATTRIBUTOR-NEXT: ret void
;
call void @llvm.assume(i1 true) ["nonnull"(i8* %arg)]
call void @use_i8_ptr(i8* %arg)
call i8* @unknown()
ret void
}
define void @nonnull_assume_neg(i8* %arg) {
; ATTRIBUTOR-LABEL: define {{[^@]+}}@nonnull_assume_neg
; ATTRIBUTOR-SAME: (i8* nocapture nofree readnone [[ARG:%.*]])
; ATTRIBUTOR-NEXT: [[TMP1:%.*]] = call i8* @unknown()
; ATTRIBUTOR-NEXT: call void @use_i8_ptr(i8* noalias nocapture nofree readnone [[ARG]])
; ATTRIBUTOR-NEXT: call void @llvm.assume(i1 true) [ "nonnull"(i8* [[ARG]]) ]
; ATTRIBUTOR-NEXT: call void @use_i8_ptr(i8* noalias nocapture nofree nonnull readnone [[ARG]])
; ATTRIBUTOR-NEXT: [[TMP2:%.*]] = call i8* @unknown()
; ATTRIBUTOR-NEXT: call void @use_i8_ptr_ret(i8* noalias nocapture nofree nonnull readnone [[ARG]])
; ATTRIBUTOR-NEXT: call void @llvm.assume(i1 true) [ "nonnull"(i8* [[ARG]]) ]
; ATTRIBUTOR-NEXT: call void @use_i8_ptr_ret(i8* noalias nocapture nofree nonnull readnone [[ARG]])
; ATTRIBUTOR-NEXT: ret void
;
call i8* @unknown()
call void @use_i8_ptr(i8* %arg)
call void @llvm.assume(i1 true) ["nonnull"(i8* %arg)]
call void @use_i8_ptr(i8* %arg)
call i8* @unknown()
call void @use_i8_ptr_ret(i8* %arg)
call void @llvm.assume(i1 true) ["nonnull"(i8* %arg)]
call void @use_i8_ptr_ret(i8* %arg)
ret void
}
declare void @use_i8_ptr(i8* nofree nocapture readnone) nounwind
declare void @use_i8_ptr_ret(i8* nofree nocapture readnone) nounwind willreturn
attributes #0 = { "null-pointer-is-valid"="true" }
attributes #1 = { nounwind willreturn}

View File

@ -22,6 +22,10 @@ using namespace llvm;
extern cl::opt<bool> ShouldPreserveAllAttributes;
extern cl::opt<bool> EnableKnowledgeRetention;
static IntrinsicInst *buildAssumeFromInst(Instruction *I) {
return cast_or_null<IntrinsicInst>(BuildAssumeFromInst(I));
}
static void RunTest(
StringRef Head, StringRef Tail,
std::vector<std::pair<StringRef, llvm::function_ref<void(Instruction *)>>>
@ -40,7 +44,7 @@ static void RunTest(
}
}
bool hasMatchesExactlyAttributes(CallInst *Assume, Value *WasOn,
bool hasMatchesExactlyAttributes(IntrinsicInst *Assume, Value *WasOn,
StringRef AttrToMatch) {
Regex Reg(AttrToMatch);
SmallVector<StringRef, 1> Matches;
@ -56,7 +60,7 @@ bool hasMatchesExactlyAttributes(CallInst *Assume, Value *WasOn,
return true;
}
bool hasTheRightValue(CallInst *Assume, Value *WasOn,
bool hasTheRightValue(IntrinsicInst *Assume, Value *WasOn,
Attribute::AttrKind Kind, unsigned Value, bool Both,
AssumeQuery AQ = AssumeQuery::Highest) {
if (!Both) {
@ -97,7 +101,7 @@ TEST(AssumeQueryAPI, hasAttributeInAssume) {
"call void @func(i32* nonnull align 4 dereferenceable(16) %P, i32* align "
"8 noalias %P1)\n",
[](Instruction *I) {
CallInst *Assume = BuildAssumeFromInst(I);
IntrinsicInst *Assume = buildAssumeFromInst(I);
Assume->insertBefore(I);
ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, I->getOperand(0),
"(nonnull|align|dereferenceable)"));
@ -117,7 +121,7 @@ TEST(AssumeQueryAPI, hasAttributeInAssume) {
"dereferenceable(4) "
"%P, i32* nonnull align 16 dereferenceable(12) %P)\n",
[](Instruction *I) {
CallInst *Assume = BuildAssumeFromInst(I);
IntrinsicInst *Assume = buildAssumeFromInst(I);
Assume->insertBefore(I);
ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, I->getOperand(0),
"(nonnull|align|dereferenceable)"));
@ -149,7 +153,7 @@ TEST(AssumeQueryAPI, hasAttributeInAssume) {
Tests.push_back(std::make_pair(
"call void @func_many(i32* align 8 %P1) cold\n", [](Instruction *I) {
ShouldPreserveAllAttributes.setValue(true);
CallInst *Assume = BuildAssumeFromInst(I);
IntrinsicInst *Assume = buildAssumeFromInst(I);
Assume->insertBefore(I);
ASSERT_TRUE(hasMatchesExactlyAttributes(
Assume, nullptr,
@ -159,7 +163,7 @@ TEST(AssumeQueryAPI, hasAttributeInAssume) {
}));
Tests.push_back(
std::make_pair("call void @llvm.assume(i1 true)\n", [](Instruction *I) {
CallInst *Assume = cast<CallInst>(I);
IntrinsicInst *Assume = cast<IntrinsicInst>(I);
ASSERT_TRUE(hasMatchesExactlyAttributes(Assume, nullptr, ""));
}));
Tests.push_back(std::make_pair(
@ -169,7 +173,7 @@ TEST(AssumeQueryAPI, hasAttributeInAssume) {
"dereferenceable(4) "
"%P2, i32* nonnull align 16 dereferenceable(12) %P3)\n",
[](Instruction *I) {
CallInst *Assume = BuildAssumeFromInst(I);
IntrinsicInst *Assume = buildAssumeFromInst(I);
Assume->insertBefore(I);
ASSERT_TRUE(hasMatchesExactlyAttributes(
Assume, I->getOperand(0),
@ -205,7 +209,7 @@ TEST(AssumeQueryAPI, hasAttributeInAssume) {
"dereferenceable(4) "
"%P2, i32* nonnull align 16 dereferenceable(12) %P3)\n",
[](Instruction *I) {
CallInst *Assume = BuildAssumeFromInst(I);
IntrinsicInst *Assume = buildAssumeFromInst(I);
Assume->insertBefore(I);
I->getOperand(1)->dropDroppableUses();
I->getOperand(2)->dropDroppableUses();
@ -228,7 +232,7 @@ TEST(AssumeQueryAPI, hasAttributeInAssume) {
"call void @func(i32* nonnull align 4 dereferenceable(16) %P, i32* align "
"8 noalias %P1)\n",
[](Instruction *I) {
CallInst *Assume = BuildAssumeFromInst(I);
IntrinsicInst *Assume = buildAssumeFromInst(I);
Assume->insertBefore(I);
Value *New = I->getFunction()->getArg(3);
Value *Old = I->getOperand(0);
@ -260,11 +264,11 @@ static bool FindExactlyAttributes(RetainedKnowledgeMap &Map, Value *WasOn,
return true;
}
static bool MapHasRightValue(RetainedKnowledgeMap &Map,
RetainedKnowledgeKey Key, MinMax MM) {
static bool MapHasRightValue(RetainedKnowledgeMap &Map, IntrinsicInst *II,
RetainedKnowledgeKey Key, MinMax MM) {
auto LookupIt = Map.find(Key);
return (LookupIt != Map.end()) && (LookupIt->second.Min == MM.Min) &&
(LookupIt->second.Max == MM.Max);
return (LookupIt != Map.end()) && (LookupIt->second[II].Min == MM.Min) &&
(LookupIt->second[II].Max == MM.Max);
}
TEST(AssumeQueryAPI, fillMapFromAssume) {
@ -284,7 +288,7 @@ TEST(AssumeQueryAPI, fillMapFromAssume) {
"call void @func(i32* nonnull align 4 dereferenceable(16) %P, i32* align "
"8 noalias %P1)\n",
[](Instruction *I) {
CallInst *Assume = BuildAssumeFromInst(I);
IntrinsicInst *Assume = buildAssumeFromInst(I);
Assume->insertBefore(I);
RetainedKnowledgeMap Map;
@ -294,10 +298,10 @@ TEST(AssumeQueryAPI, fillMapFromAssume) {
ASSERT_TRUE(FindExactlyAttributes(Map, I->getOperand(1),
"(align)"));
ASSERT_TRUE(MapHasRightValue(
Map, {I->getOperand(0), Attribute::Dereferenceable}, {16, 16}));
ASSERT_TRUE(MapHasRightValue(Map, {I->getOperand(0), Attribute::Alignment},
Map, Assume, {I->getOperand(0), Attribute::Dereferenceable}, {16, 16}));
ASSERT_TRUE(MapHasRightValue(Map, Assume, {I->getOperand(0), Attribute::Alignment},
{4, 4}));
ASSERT_TRUE(MapHasRightValue(Map, {I->getOperand(0), Attribute::Alignment},
ASSERT_TRUE(MapHasRightValue(Map, Assume, {I->getOperand(0), Attribute::Alignment},
{4, 4}));
}));
Tests.push_back(std::make_pair(
@ -307,7 +311,7 @@ TEST(AssumeQueryAPI, fillMapFromAssume) {
"dereferenceable(4) "
"%P, i32* nonnull align 16 dereferenceable(12) %P)\n",
[](Instruction *I) {
CallInst *Assume = BuildAssumeFromInst(I);
IntrinsicInst *Assume = buildAssumeFromInst(I);
Assume->insertBefore(I);
RetainedKnowledgeMap Map;
@ -322,14 +326,14 @@ TEST(AssumeQueryAPI, fillMapFromAssume) {
ASSERT_TRUE(FindExactlyAttributes(Map, I->getOperand(3),
"(nonnull|align|dereferenceable)"));
ASSERT_TRUE(MapHasRightValue(
Map, {I->getOperand(0), Attribute::Dereferenceable}, {4, 48}));
ASSERT_TRUE(MapHasRightValue(Map, {I->getOperand(0), Attribute::Alignment},
Map, Assume, {I->getOperand(0), Attribute::Dereferenceable}, {4, 48}));
ASSERT_TRUE(MapHasRightValue(Map, Assume, {I->getOperand(0), Attribute::Alignment},
{8, 64}));
}));
Tests.push_back(std::make_pair(
"call void @func_many(i32* align 8 %P1) cold\n", [](Instruction *I) {
ShouldPreserveAllAttributes.setValue(true);
CallInst *Assume = BuildAssumeFromInst(I);
IntrinsicInst *Assume = buildAssumeFromInst(I);
Assume->insertBefore(I);
RetainedKnowledgeMap Map;
@ -342,7 +346,7 @@ TEST(AssumeQueryAPI, fillMapFromAssume) {
Tests.push_back(
std::make_pair("call void @llvm.assume(i1 true)\n", [](Instruction *I) {
RetainedKnowledgeMap Map;
fillMapFromAssume(*cast<CallInst>(I), Map);
fillMapFromAssume(*cast<IntrinsicInst>(I), Map);
ASSERT_TRUE(FindExactlyAttributes(Map, nullptr, ""));
ASSERT_TRUE(Map.empty());
@ -354,7 +358,7 @@ TEST(AssumeQueryAPI, fillMapFromAssume) {
"dereferenceable(4) "
"%P2, i32* nonnull align 16 dereferenceable(12) %P3)\n",
[](Instruction *I) {
CallInst *Assume = BuildAssumeFromInst(I);
IntrinsicInst *Assume = buildAssumeFromInst(I);
Assume->insertBefore(I);
RetainedKnowledgeMap Map;
@ -368,22 +372,22 @@ TEST(AssumeQueryAPI, fillMapFromAssume) {
"(align|dereferenceable)"));
ASSERT_TRUE(FindExactlyAttributes(Map, I->getOperand(3),
"(nonnull|align|dereferenceable)"));
ASSERT_TRUE(MapHasRightValue(Map, {I->getOperand(0), Attribute::Alignment},
ASSERT_TRUE(MapHasRightValue(Map, Assume, {I->getOperand(0), Attribute::Alignment},
{32, 32}));
ASSERT_TRUE(MapHasRightValue(
Map, {I->getOperand(0), Attribute::Dereferenceable}, {48, 48}));
Map, Assume, {I->getOperand(0), Attribute::Dereferenceable}, {48, 48}));
ASSERT_TRUE(MapHasRightValue(
Map, {I->getOperand(1), Attribute::Dereferenceable}, {28, 28}));
ASSERT_TRUE(MapHasRightValue(Map, {I->getOperand(1), Attribute::Alignment},
Map, Assume, {I->getOperand(1), Attribute::Dereferenceable}, {28, 28}));
ASSERT_TRUE(MapHasRightValue(Map, Assume, {I->getOperand(1), Attribute::Alignment},
{8, 8}));
ASSERT_TRUE(MapHasRightValue(Map, {I->getOperand(2), Attribute::Alignment},
ASSERT_TRUE(MapHasRightValue(Map, Assume, {I->getOperand(2), Attribute::Alignment},
{64, 64}));
ASSERT_TRUE(MapHasRightValue(
Map, {I->getOperand(2), Attribute::Dereferenceable}, {4, 4}));
ASSERT_TRUE(MapHasRightValue(Map, {I->getOperand(3), Attribute::Alignment},
Map, Assume, {I->getOperand(2), Attribute::Dereferenceable}, {4, 4}));
ASSERT_TRUE(MapHasRightValue(Map, Assume, {I->getOperand(3), Attribute::Alignment},
{16, 16}));
ASSERT_TRUE(MapHasRightValue(
Map, {I->getOperand(3), Attribute::Dereferenceable}, {12, 12}));
Map, Assume, {I->getOperand(3), Attribute::Dereferenceable}, {12, 12}));
}));
/// Keep this test last as it modifies the function.
@ -391,7 +395,7 @@ TEST(AssumeQueryAPI, fillMapFromAssume) {
"call void @func(i32* nonnull align 4 dereferenceable(16) %P, i32* align "
"8 noalias %P1)\n",
[](Instruction *I) {
CallInst *Assume = BuildAssumeFromInst(I);
IntrinsicInst *Assume = buildAssumeFromInst(I);
Assume->insertBefore(I);
RetainedKnowledgeMap Map;
@ -475,12 +479,12 @@ static void RunRandTest(uint64_t Seed, int Size, int MinCount, int MaxCount,
OpBundle.push_back(OperandBundleDef{ss.str().c_str(), std::move(Args)});
}
Instruction *Assume =
CallInst::Create(FnAssume, ArrayRef<Value *>({ConstantInt::getTrue(C)}),
std::move(OpBundle));
auto *Assume = cast<IntrinsicInst>(IntrinsicInst::Create(
FnAssume, ArrayRef<Value *>({ConstantInt::getTrue(C)}),
std::move(OpBundle)));
Assume->insertBefore(&F->begin()->front());
RetainedKnowledgeMap Map;
fillMapFromAssume(*cast<CallInst>(Assume), Map);
fillMapFromAssume(*Assume, Map);
for (int i = 0; i < (Size * 2); i++) {
if (!HasArg[i])
continue;
@ -488,7 +492,7 @@ static void RunRandTest(uint64_t Seed, int Size, int MinCount, int MaxCount,
getKnowledgeFromUseInAssume(&*ShuffledArgs[i]->use_begin());
auto LookupIt = Map.find(RetainedKnowledgeKey{K.WasOn, K.AttrKind});
ASSERT_TRUE(LookupIt != Map.end());
MinMax MM = LookupIt->second;
MinMax MM = LookupIt->second[Assume];
ASSERT_TRUE(MM.Min == MM.Max);
ASSERT_TRUE(MM.Min == K.ArgValue);
}