Skip to content

Commit fc269c1

Browse files
committed
Added attributor for identifying !invariant.loads.
1 parent 617cfed commit fc269c1

File tree

5 files changed

+509
-4
lines changed

5 files changed

+509
-4
lines changed

llvm/include/llvm/Transforms/IPO/Attributor.h

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6289,6 +6289,44 @@ struct AAUnderlyingObjects : AbstractAttribute {
62896289
AA::ValueScope Scope = AA::Interprocedural) const = 0;
62906290
};
62916291

6292+
/// An abstract interface for identifying pointers from which loads can be
6293+
/// marked invariant.
6294+
struct AAInvariantLoadPointer : public AbstractAttribute {
6295+
AAInvariantLoadPointer(const IRPosition &IRP) : AbstractAttribute(IRP) {}
6296+
6297+
/// See AbstractAttribute::isValidIRPositionForInit
6298+
static bool isValidIRPositionForInit(Attributor &A, const IRPosition &IRP) {
6299+
if (!IRP.getAssociatedType()->isPointerTy())
6300+
return false;
6301+
return AbstractAttribute::isValidIRPositionForInit(A, IRP);
6302+
}
6303+
6304+
/// Create an abstract attribute view for the position \p IRP.
6305+
static AAInvariantLoadPointer &createForPosition(const IRPosition &IRP,
6306+
Attributor &A);
6307+
6308+
/// Return true if the pointer's contents are known to remain invariant.
6309+
virtual bool isKnownInvariant() const = 0;
6310+
6311+
/// Return true if the pointer's contents are assumed to remain invariant.
6312+
virtual bool isAssumedInvariant() const = 0;
6313+
6314+
/// See AbstractAttribute::getName().
6315+
StringRef getName() const override { return "AAInvariantLoadPointer"; }
6316+
6317+
/// See AbstractAttribute::getIdAddr().
6318+
const char *getIdAddr() const override { return &ID; }
6319+
6320+
/// This function should return true if the type of the \p AA is
6321+
/// AAInvariantLoadPointer
6322+
static bool classof(const AbstractAttribute *AA) {
6323+
return (AA->getIdAddr() == &ID);
6324+
}
6325+
6326+
/// Unique ID (due to the unique address).
6327+
static const char ID;
6328+
};
6329+
62926330
/// An abstract interface for address space information.
62936331
struct AAAddressSpace : public StateWrapper<BooleanState, AbstractAttribute> {
62946332
AAAddressSpace(const IRPosition &IRP, Attributor &A)

llvm/lib/Transforms/IPO/Attributor.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3620,6 +3620,8 @@ void Attributor::identifyDefaultAbstractAttributes(Function &F) {
36203620
if (SimplifyAllLoads)
36213621
getAssumedSimplified(IRPosition::value(I), nullptr,
36223622
UsedAssumedInformation, AA::Intraprocedural);
3623+
getOrCreateAAFor<AAInvariantLoadPointer>(
3624+
IRPosition::value(*LI->getPointerOperand()));
36233625
getOrCreateAAFor<AAAddressSpace>(
36243626
IRPosition::value(*LI->getPointerOperand()));
36253627
} else {

llvm/lib/Transforms/IPO/AttributorAttributes.cpp

Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ PIPE_OPERATOR(AAInterFnReachability)
191191
PIPE_OPERATOR(AAPointerInfo)
192192
PIPE_OPERATOR(AAAssumptionInfo)
193193
PIPE_OPERATOR(AAUnderlyingObjects)
194+
PIPE_OPERATOR(AAInvariantLoadPointer)
194195
PIPE_OPERATOR(AAAddressSpace)
195196
PIPE_OPERATOR(AAAllocationInfo)
196197
PIPE_OPERATOR(AAIndirectCallInfo)
@@ -12534,6 +12535,248 @@ struct AAIndirectCallInfoCallSite : public AAIndirectCallInfo {
1253412535
};
1253512536
} // namespace
1253612537

12538+
/// --------------------- Invariant Load Pointer -------------------------------
12539+
namespace {
12540+
12541+
struct AAInvariantLoadPointerImpl
12542+
: public StateWrapper<BitIntegerState<uint8_t, 7>, AAInvariantLoadPointer,
12543+
uint8_t> {
12544+
// load invariance is implied by, but not equivalent to IS_NOALIAS |
12545+
// IS_READONLY, as load invariance is also implied by all underlying objects
12546+
// being load invariant.
12547+
//
12548+
// IS_INVARIANT is set to indicate that the contents of the pointer are
12549+
// *known* to be invariant.
12550+
enum {
12551+
IS_INVARIANT = 1 << 0,
12552+
IS_NOALIAS = 1 << 1,
12553+
IS_READONLY = 1 << 2,
12554+
};
12555+
static_assert(getBestState() == (IS_INVARIANT | IS_NOALIAS | IS_READONLY),
12556+
"Unexpected best state!");
12557+
12558+
using Base = StateWrapper<BitIntegerState<uint8_t, 7>, AAInvariantLoadPointer,
12559+
uint8_t>;
12560+
12561+
// the BitIntegerState is optimistic about noalias and readonly, but
12562+
// pessimistic about invariance
12563+
AAInvariantLoadPointerImpl(const IRPosition &IRP, Attributor &A)
12564+
: Base(IRP, IS_NOALIAS | IS_READONLY) {}
12565+
12566+
void initialize(Attributor &A) final {
12567+
// conservatively assume that the pointer's contents are not invariant,
12568+
// until proven otherwise.
12569+
removeAssumedBits(IS_INVARIANT);
12570+
}
12571+
12572+
bool isKnownInvariant() const final {
12573+
return isKnown(IS_INVARIANT) || isKnown(IS_NOALIAS | IS_READONLY);
12574+
}
12575+
12576+
bool isAssumedInvariant() const final {
12577+
return isAssumed(IS_INVARIANT) || isAssumed(IS_NOALIAS | IS_READONLY);
12578+
}
12579+
12580+
ChangeStatus updateImpl(Attributor &A) override {
12581+
if (isKnownInvariant())
12582+
return ChangeStatus::UNCHANGED;
12583+
12584+
ChangeStatus Changed = ChangeStatus::UNCHANGED;
12585+
12586+
Changed |= updateNoAlias(A);
12587+
Changed |= updateReadOnly(A);
12588+
12589+
bool UsedAssumedInformation = false;
12590+
const auto IsInvariantLoadIfPointer = [&](const Value &V) {
12591+
if (!V.getType()->isPointerTy())
12592+
return true;
12593+
const auto *IsInvariantLoadPointer =
12594+
A.getOrCreateAAFor<AAInvariantLoadPointer>(IRPosition::value(V), this,
12595+
DepClassTy::REQUIRED);
12596+
if (IsInvariantLoadPointer->isKnownInvariant())
12597+
return true;
12598+
if (!IsInvariantLoadPointer->isAssumedInvariant())
12599+
return false;
12600+
12601+
UsedAssumedInformation = true;
12602+
return true;
12603+
};
12604+
12605+
const auto *AUO = A.getOrCreateAAFor<AAUnderlyingObjects>(
12606+
getIRPosition(), this, DepClassTy::REQUIRED);
12607+
12608+
if (!AUO->forallUnderlyingObjects(IsInvariantLoadIfPointer)) {
12609+
removeAssumedBits(IS_INVARIANT);
12610+
return ChangeStatus::CHANGED;
12611+
}
12612+
12613+
if (!UsedAssumedInformation) {
12614+
// pointer is known (not assumed) to be invariant
12615+
addKnownBits(IS_INVARIANT);
12616+
return ChangeStatus::CHANGED;
12617+
}
12618+
12619+
return Changed;
12620+
}
12621+
12622+
ChangeStatus manifest(Attributor &A) override {
12623+
if (!isKnownInvariant())
12624+
return ChangeStatus::UNCHANGED;
12625+
12626+
ChangeStatus Changed = ChangeStatus::UNCHANGED;
12627+
Value *Ptr = &getAssociatedValue();
12628+
const auto TagInvariantLoads = [&](const Use &U, bool &) {
12629+
if (U.get() != Ptr)
12630+
return true;
12631+
auto *I = dyn_cast<Instruction>(U.getUser());
12632+
if (!I)
12633+
return true;
12634+
12635+
// Ensure that we are only changing uses from the corresponding callgraph
12636+
// SSC in the case that the AA isn't run on the entire module
12637+
if (!A.isRunOn(I->getFunction()))
12638+
return true;
12639+
12640+
if (I->hasMetadata(LLVMContext::MD_invariant_load))
12641+
return true;
12642+
12643+
if (auto *LI = dyn_cast<LoadInst>(I)) {
12644+
if (LI->isVolatile() || LI->isAtomic())
12645+
return true;
12646+
12647+
LI->setMetadata(LLVMContext::MD_invariant_load,
12648+
MDNode::get(LI->getContext(), {}));
12649+
Changed = ChangeStatus::CHANGED;
12650+
}
12651+
return true;
12652+
};
12653+
12654+
(void)A.checkForAllUses(TagInvariantLoads, *this, *Ptr);
12655+
return Changed;
12656+
}
12657+
12658+
/// See AbstractAttribute::getAsStr().
12659+
const std::string getAsStr(Attributor *) const override {
12660+
std::string Str;
12661+
raw_string_ostream OS(Str);
12662+
OS << "load invariant pointer: " << isKnown() << '\n';
12663+
return Str;
12664+
}
12665+
12666+
/// See AbstractAttribute::trackStatistics().
12667+
void trackStatistics() const override {}
12668+
12669+
protected:
12670+
ChangeStatus updateNoAlias(Attributor &A) {
12671+
if (isKnown(IS_NOALIAS) || !isAssumed(IS_NOALIAS))
12672+
return ChangeStatus::UNCHANGED;
12673+
12674+
const auto *ANoAlias = A.getOrCreateAAFor<AANoAlias>(getIRPosition(), this,
12675+
DepClassTy::REQUIRED);
12676+
if (!ANoAlias)
12677+
return tryInferNoAlias(A);
12678+
12679+
if (!ANoAlias->isAssumedNoAlias()) {
12680+
removeAssumedBits(IS_NOALIAS);
12681+
return ChangeStatus::CHANGED;
12682+
}
12683+
if (ANoAlias->isKnownNoAlias())
12684+
addKnownBits(IS_NOALIAS);
12685+
12686+
return ChangeStatus::UNCHANGED;
12687+
}
12688+
12689+
/// Fallback method if updateNoAlias fails to infer noalias information from
12690+
/// AANoAlias.
12691+
virtual ChangeStatus tryInferNoAlias(Attributor &A) {
12692+
return ChangeStatus::UNCHANGED;
12693+
}
12694+
12695+
ChangeStatus updateReadOnly(Attributor &A) {
12696+
if (isKnown(IS_READONLY) || !isAssumed(IS_READONLY))
12697+
return ChangeStatus::UNCHANGED;
12698+
12699+
// AAMemoryBehavior may crash if value is global
12700+
if (!getAssociatedFunction())
12701+
return tryInferReadOnly(A);
12702+
12703+
const auto *AMemoryBehavior = A.getOrCreateAAFor<AAMemoryBehavior>(
12704+
getIRPosition(), this, DepClassTy::REQUIRED);
12705+
if (!AMemoryBehavior)
12706+
return tryInferReadOnly(A);
12707+
12708+
if (!AMemoryBehavior->isAssumedReadOnly()) {
12709+
removeAssumedBits(IS_READONLY);
12710+
return ChangeStatus::CHANGED;
12711+
}
12712+
if (AMemoryBehavior->isKnownReadOnly())
12713+
addKnownBits(IS_READONLY);
12714+
12715+
return ChangeStatus::UNCHANGED;
12716+
}
12717+
12718+
/// Fallback method if updateReadOnly fails to infer readonly information from
12719+
/// AAMemoryBehavior.
12720+
virtual ChangeStatus tryInferReadOnly(Attributor &A) {
12721+
return ChangeStatus::UNCHANGED;
12722+
}
12723+
};
12724+
12725+
struct AAInvariantLoadPointerFloating final : AAInvariantLoadPointerImpl {
12726+
AAInvariantLoadPointerFloating(const IRPosition &IRP, Attributor &A)
12727+
: AAInvariantLoadPointerImpl(IRP, A) {}
12728+
};
12729+
12730+
struct AAInvariantLoadPointerReturned final : AAInvariantLoadPointerImpl {
12731+
AAInvariantLoadPointerReturned(const IRPosition &IRP, Attributor &A)
12732+
: AAInvariantLoadPointerImpl(IRP, A) {}
12733+
};
12734+
12735+
struct AAInvariantLoadPointerCallSiteReturned final
12736+
: AAInvariantLoadPointerImpl {
12737+
AAInvariantLoadPointerCallSiteReturned(const IRPosition &IRP, Attributor &A)
12738+
: AAInvariantLoadPointerImpl(IRP, A) {}
12739+
};
12740+
12741+
struct AAInvariantLoadPointerArgument final : AAInvariantLoadPointerImpl {
12742+
AAInvariantLoadPointerArgument(const IRPosition &IRP, Attributor &A)
12743+
: AAInvariantLoadPointerImpl(IRP, A) {}
12744+
12745+
protected:
12746+
ChangeStatus tryInferNoAlias(Attributor &A) override {
12747+
const auto *Arg = getAssociatedArgument();
12748+
if (Arg->hasNoAliasAttr()) {
12749+
addKnownBits(IS_NOALIAS);
12750+
return ChangeStatus::UNCHANGED;
12751+
}
12752+
12753+
// noalias information is not provided, and cannot be inferred from
12754+
// AANoAlias
12755+
removeAssumedBits(IS_NOALIAS);
12756+
return ChangeStatus::CHANGED;
12757+
}
12758+
12759+
ChangeStatus tryInferReadOnly(Attributor &A) override {
12760+
const auto *Arg = getAssociatedArgument();
12761+
if (Arg->onlyReadsMemory()) {
12762+
addKnownBits(IS_READONLY);
12763+
return ChangeStatus::UNCHANGED;
12764+
}
12765+
12766+
// readonly information is not provided, and cannot be inferred from
12767+
// AAMemoryBehavior
12768+
removeAssumedBits(IS_READONLY);
12769+
return ChangeStatus::CHANGED;
12770+
}
12771+
};
12772+
12773+
struct AAInvariantLoadPointerCallSiteArgument final
12774+
: AAInvariantLoadPointerImpl {
12775+
AAInvariantLoadPointerCallSiteArgument(const IRPosition &IRP, Attributor &A)
12776+
: AAInvariantLoadPointerImpl(IRP, A) {}
12777+
};
12778+
} // namespace
12779+
1253712780
/// ------------------------ Address Space ------------------------------------
1253812781
namespace {
1253912782

@@ -13031,6 +13274,7 @@ const char AAInterFnReachability::ID = 0;
1303113274
const char AAPointerInfo::ID = 0;
1303213275
const char AAAssumptionInfo::ID = 0;
1303313276
const char AAUnderlyingObjects::ID = 0;
13277+
const char AAInvariantLoadPointer::ID = 0;
1303413278
const char AAAddressSpace::ID = 0;
1303513279
const char AAAllocationInfo::ID = 0;
1303613280
const char AAIndirectCallInfo::ID = 0;
@@ -13165,6 +13409,7 @@ CREATE_VALUE_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAPotentialValues)
1316513409
CREATE_VALUE_ABSTRACT_ATTRIBUTE_FOR_POSITION(AANoUndef)
1316613410
CREATE_VALUE_ABSTRACT_ATTRIBUTE_FOR_POSITION(AANoFPClass)
1316713411
CREATE_VALUE_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAPointerInfo)
13412+
CREATE_VALUE_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAInvariantLoadPointer)
1316813413
CREATE_VALUE_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAAddressSpace)
1316913414
CREATE_VALUE_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAAllocationInfo)
1317013415

llvm/test/Transforms/Attributor/multiple-offsets-pointer-info.ll

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ define i8 @select_offsets_simplifiable_1(i1 %cnd1, i1 %cnd2) {
1010
; CHECK-LABEL: define {{[^@]+}}@select_offsets_simplifiable_1
1111
; CHECK-SAME: (i1 [[CND1:%.*]], i1 [[CND2:%.*]]) {
1212
; CHECK-NEXT: entry:
13-
; CHECK-NEXT: [[BYTES:%.*]] = call ptr @calloc(i64 noundef 1024, i64 noundef 1)
13+
; CHECK-NEXT: [[BYTES:%.*]] = call noalias ptr @calloc(i64 noundef 1024, i64 noundef 1)
1414
; CHECK-NEXT: [[GEP23:%.*]] = getelementptr inbounds [1024 x i8], ptr [[BYTES]], i64 0, i64 23
1515
; CHECK-NEXT: store i8 23, ptr [[GEP23]], align 4
1616
; CHECK-NEXT: [[GEP29:%.*]] = getelementptr inbounds [1024 x i8], ptr [[BYTES]], i64 0, i64 29
@@ -190,7 +190,7 @@ define i8 @select_offsets_not_simplifiable_3(i1 %cnd1, i1 %cnd2) {
190190
; CHECK-LABEL: define {{[^@]+}}@select_offsets_not_simplifiable_3
191191
; CHECK-SAME: (i1 [[CND1:%.*]], i1 [[CND2:%.*]]) {
192192
; CHECK-NEXT: entry:
193-
; CHECK-NEXT: [[BYTES:%.*]] = call ptr @calloc(i64 noundef 1024, i64 noundef 1)
193+
; CHECK-NEXT: [[BYTES:%.*]] = call noalias ptr @calloc(i64 noundef 1024, i64 noundef 1)
194194
; CHECK-NEXT: [[SEL0:%.*]] = select i1 [[CND1]], i64 23, i64 29
195195
; CHECK-NEXT: [[SEL1:%.*]] = select i1 [[CND2]], i64 [[SEL0]], i64 7
196196
; CHECK-NEXT: [[GEP_SEL:%.*]] = getelementptr inbounds [1024 x i8], ptr [[BYTES]], i64 0, i64 [[SEL1]]
@@ -214,7 +214,7 @@ define i8 @select_offsets_not_simplifiable_4(i1 %cnd1, i1 %cnd2) {
214214
; CHECK-LABEL: define {{[^@]+}}@select_offsets_not_simplifiable_4
215215
; CHECK-SAME: (i1 [[CND1:%.*]], i1 [[CND2:%.*]]) {
216216
; CHECK-NEXT: entry:
217-
; CHECK-NEXT: [[BYTES:%.*]] = call ptr @calloc(i64 noundef 1024, i64 noundef 1)
217+
; CHECK-NEXT: [[BYTES:%.*]] = call noalias ptr @calloc(i64 noundef 1024, i64 noundef 1)
218218
; CHECK-NEXT: [[SEL0:%.*]] = select i1 [[CND1]], i64 23, i64 29
219219
; CHECK-NEXT: [[SEL1:%.*]] = select i1 [[CND2]], i64 [[SEL0]], i64 7
220220
; CHECK-NEXT: [[GEP_SEL:%.*]] = getelementptr inbounds [1024 x i8], ptr [[BYTES]], i64 0, i64 [[SEL1]]
@@ -445,7 +445,7 @@ define i8 @phi_gep_not_simplifiable_2(i1 %cnd1, i1 %cnd2) {
445445
; CHECK-LABEL: define {{[^@]+}}@phi_gep_not_simplifiable_2
446446
; CHECK-SAME: (i1 [[CND1:%.*]], i1 [[CND2:%.*]]) {
447447
; CHECK-NEXT: entry:
448-
; CHECK-NEXT: [[BYTES:%.*]] = call ptr @calloc(i64 noundef 1024, i64 noundef 1)
448+
; CHECK-NEXT: [[BYTES:%.*]] = call noalias ptr @calloc(i64 noundef 1024, i64 noundef 1)
449449
; CHECK-NEXT: [[GEP23:%.*]] = getelementptr inbounds [1024 x i8], ptr [[BYTES]], i64 0, i64 23
450450
; CHECK-NEXT: br i1 [[CND1]], label [[THEN:%.*]], label [[ELSE:%.*]]
451451
; CHECK: then:

0 commit comments

Comments
 (0)