Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions llvm/include/llvm/Transforms/IPO/Attributor.h
Original file line number Diff line number Diff line change
Expand Up @@ -6289,6 +6289,47 @@ struct AAUnderlyingObjects : AbstractAttribute {
AA::ValueScope Scope = AA::Interprocedural) const = 0;
};

/// An abstract interface for identifying pointers from which loads can be
/// marked invariant.
struct AAInvariantLoadPointer : public AbstractAttribute {
AAInvariantLoadPointer(const IRPosition &IRP) : AbstractAttribute(IRP) {}

/// See AbstractAttribute::isValidIRPositionForInit
static bool isValidIRPositionForInit(Attributor &A, const IRPosition &IRP) {
if (!IRP.getAssociatedType()->isPointerTy())
return false;

return AbstractAttribute::isValidIRPositionForInit(A, IRP);
}

/// Create an abstract attribute view for the position \p IRP.
static AAInvariantLoadPointer &createForPosition(const IRPosition &IRP,
Attributor &A);

/// Return true if the pointer's contents are known to remain invariant.
virtual bool isKnownInvariant() const = 0;
virtual bool isKnownLocallyInvariant() const = 0;

/// Return true if the pointer's contents are assumed to remain invariant.
virtual bool isAssumedInvariant() const = 0;
virtual bool isAssumedLocallyInvariant() const = 0;

/// See AbstractAttribute::getName().
StringRef getName() const override { return "AAInvariantLoadPointer"; }

/// See AbstractAttribute::getIdAddr().
const char *getIdAddr() const override { return &ID; }

/// This function should return true if the type of the \p AA is
/// AAInvariantLoadPointer
static bool classof(const AbstractAttribute *AA) {
return (AA->getIdAddr() == &ID);
}

/// Unique ID (due to the unique address).
static const char ID;
};

/// An abstract interface for address space information.
struct AAAddressSpace : public StateWrapper<BooleanState, AbstractAttribute> {
AAAddressSpace(const IRPosition &IRP, Attributor &A)
Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/Transforms/IPO/Attributor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3620,6 +3620,8 @@ void Attributor::identifyDefaultAbstractAttributes(Function &F) {
if (SimplifyAllLoads)
getAssumedSimplified(IRPosition::value(I), nullptr,
UsedAssumedInformation, AA::Intraprocedural);
getOrCreateAAFor<AAInvariantLoadPointer>(
IRPosition::value(*LI->getPointerOperand()));
getOrCreateAAFor<AAAddressSpace>(
IRPosition::value(*LI->getPointerOperand()));
} else {
Expand Down
314 changes: 314 additions & 0 deletions llvm/lib/Transforms/IPO/AttributorAttributes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ PIPE_OPERATOR(AAInterFnReachability)
PIPE_OPERATOR(AAPointerInfo)
PIPE_OPERATOR(AAAssumptionInfo)
PIPE_OPERATOR(AAUnderlyingObjects)
PIPE_OPERATOR(AAInvariantLoadPointer)
PIPE_OPERATOR(AAAddressSpace)
PIPE_OPERATOR(AAAllocationInfo)
PIPE_OPERATOR(AAIndirectCallInfo)
Expand Down Expand Up @@ -12534,6 +12535,317 @@ struct AAIndirectCallInfoCallSite : public AAIndirectCallInfo {
};
} // namespace

/// --------------------- Invariant Load Pointer -------------------------------
namespace {

struct AAInvariantLoadPointerImpl
: public StateWrapper<BitIntegerState<uint8_t, 15>,
AAInvariantLoadPointer> {

enum {
// pointer does not alias within the bounds of the function
IS_NOALIAS = 1 << 0,
// pointer is not involved in any effectful instructions within the bounds
// of the function
IS_NOEFFECT = 1 << 1,
// loads are invariant within the bounds of the function
IS_LOCALLY_INVARIANT = 1 << 2,
// memory lifetime is constrained within the bounds of the function
IS_LOCALLY_CONSTRAINED = 1 << 3,

IS_BEST_STATE = IS_NOALIAS | IS_NOEFFECT | IS_LOCALLY_INVARIANT |
IS_LOCALLY_CONSTRAINED,
};
static_assert(getBestState() == IS_BEST_STATE, "Unexpected best state");

using Base =
StateWrapper<BitIntegerState<uint8_t, 15>, AAInvariantLoadPointer>;

// the BitIntegerState is optimistic about IS_NOALIAS and IS_NOEFFECT, but
// pessimistic about IS_KNOWN_INVARIANT
AAInvariantLoadPointerImpl(const IRPosition &IRP, Attributor &A)
: Base(IRP) {}

bool isKnownInvariant() const final {
return isKnownLocallyInvariant() && isKnown(IS_LOCALLY_CONSTRAINED);
}

bool isKnownLocallyInvariant() const final {
if (isKnown(IS_LOCALLY_INVARIANT))
return true;
return isKnown(IS_NOALIAS | IS_NOEFFECT);
}

bool isAssumedInvariant() const final {
return isAssumedLocallyInvariant() && isAssumed(IS_LOCALLY_CONSTRAINED);
}

bool isAssumedLocallyInvariant() const final {
if (isAssumed(IS_LOCALLY_INVARIANT))
return true;
return isAssumed(IS_NOALIAS | IS_NOEFFECT);
}

ChangeStatus updateImpl(Attributor &A) override {
ChangeStatus Changed = ChangeStatus::UNCHANGED;

Changed |= updateNoAlias(A);
if (requiresNoAlias() && !isAssumed(IS_NOALIAS))
return indicatePessimisticFixpoint();

Changed |= updateNoEffect(A);

Changed |= updateLocalInvariance(A);

return Changed;
}

ChangeStatus manifest(Attributor &A) override {
if (!isKnownInvariant())
return ChangeStatus::UNCHANGED;

ChangeStatus Changed = ChangeStatus::UNCHANGED;
const Value *Ptr = &getAssociatedValue();
const auto TagInvariantLoads = [&](const Use &U, bool &) {
if (U.get() != Ptr)
return true;
auto *I = dyn_cast<Instruction>(U.getUser());
if (!I)
return true;

// Ensure that we are only changing uses from the corresponding callgraph
// SSC in the case that the AA isn't run on the entire module
if (!A.isRunOn(I->getFunction()))
return true;

if (I->hasMetadata(LLVMContext::MD_invariant_load))
return true;

if (auto *LI = dyn_cast<LoadInst>(I)) {
LI->setMetadata(LLVMContext::MD_invariant_load,
MDNode::get(LI->getContext(), {}));
Changed = ChangeStatus::CHANGED;
}
return true;
};

(void)A.checkForAllUses(TagInvariantLoads, *this, *Ptr);
return Changed;
}

/// See AbstractAttribute::getAsStr().
const std::string getAsStr(Attributor *) const override {
if (isKnownInvariant())
return "load-invariant pointer";
return "non-invariant pointer";
}

/// See AbstractAttribute::trackStatistics().
void trackStatistics() const override {}

protected:
/// Indicate that invariance necessarily requires the pointer to be noalias.
virtual bool requiresNoAlias() const { return false; }

private:
bool isExternal() const {
const Function *F = getAssociatedFunction();
if (!F)
return true;
return isCallableCC(F->getCallingConv()) &&
getPositionKind() != IRP_CALL_SITE_RETURNED;
}

ChangeStatus updateNoAlias(Attributor &A) {
if (isKnown(IS_NOALIAS) || !isAssumed(IS_NOALIAS))
return ChangeStatus::UNCHANGED;

// try to use AANoAlias
if (const auto *ANoAlias = A.getOrCreateAAFor<AANoAlias>(
getIRPosition(), this, DepClassTy::REQUIRED)) {
if (ANoAlias->isKnownNoAlias()) {
addKnownBits(IS_NOALIAS);
return ChangeStatus::CHANGED;
}

if (!ANoAlias->isAssumedNoAlias()) {
removeAssumedBits(IS_NOALIAS);
return ChangeStatus::CHANGED;
}

return ChangeStatus::UNCHANGED;
}

// try to infer noalias from argument attribute, since it is applicable for
// the duration of the function
if (const Argument *Arg = getAssociatedArgument()) {
if (Arg->hasNoAliasAttr()) {
addKnownBits(IS_NOALIAS);
return ChangeStatus::UNCHANGED;
}

// noalias information is not provided, and cannot be inferred,
// so we conservatively assume the pointer aliases.
removeAssumedBits(IS_NOALIAS);
return ChangeStatus::CHANGED;
}

return ChangeStatus::UNCHANGED;
}

ChangeStatus updateNoEffect(Attributor &A) {
if (isKnown(IS_NOEFFECT) || !isAssumed(IS_NOEFFECT))
return ChangeStatus::UNCHANGED;

if (!getAssociatedFunction())
return indicatePessimisticFixpoint();

const auto HasNoEffectLoads = [&](const Use &U, bool &) {
const auto *LI = dyn_cast<LoadInst>(U.getUser());
return !LI || !LI->mayHaveSideEffects();
};
if (!A.checkForAllUses(HasNoEffectLoads, *this, getAssociatedValue()))
return indicatePessimisticFixpoint();

// try to use AAMemoryBehavior to infer readonly attribute
if (const auto *AMemoryBehavior = A.getOrCreateAAFor<AAMemoryBehavior>(
getIRPosition(), this, DepClassTy::REQUIRED)) {
if (!AMemoryBehavior->isAssumedReadOnly())
return indicatePessimisticFixpoint();

if (AMemoryBehavior->isKnownReadOnly()) {
addKnownBits(IS_NOEFFECT);
return ChangeStatus::UNCHANGED;
}

return ChangeStatus::UNCHANGED;
}

if (const Argument *Arg = getAssociatedArgument()) {
if (Arg->onlyReadsMemory()) {
addKnownBits(IS_NOEFFECT);
return ChangeStatus::UNCHANGED;
}

// readonly information is not provided, and cannot be inferred from
// AAMemoryBehavior
return indicatePessimisticFixpoint();
}

return ChangeStatus::UNCHANGED;
}

ChangeStatus updateLocalInvariance(Attributor &A) {
if (isKnown(IS_LOCALLY_INVARIANT) || !isAssumed(IS_LOCALLY_INVARIANT))
return ChangeStatus::UNCHANGED;

// try to infer invariance from underlying objects
const auto *AUO = A.getOrCreateAAFor<AAUnderlyingObjects>(
getIRPosition(), this, DepClassTy::REQUIRED);
if (!AUO)
return ChangeStatus::UNCHANGED;

bool UsedAssumedInformation = false;
const auto IsLocallyInvariantLoadIfPointer = [&](const Value &V) {
if (!V.getType()->isPointerTy())
return true;
const auto *IsInvariantLoadPointer =
A.getOrCreateAAFor<AAInvariantLoadPointer>(IRPosition::value(V), this,
DepClassTy::REQUIRED);
// conservatively fail if invariance cannot be inferred
if (!IsInvariantLoadPointer)
return false;

if (IsInvariantLoadPointer->isKnownLocallyInvariant())
return true;
if (!IsInvariantLoadPointer->isAssumedLocallyInvariant())
return false;

UsedAssumedInformation = true;
return true;
};
if (!AUO->forallUnderlyingObjects(IsLocallyInvariantLoadIfPointer))
return indicatePessimisticFixpoint();

if (!UsedAssumedInformation) {
// pointer is known (not assumed) to be locally invariant
addKnownBits(IS_LOCALLY_INVARIANT);
return ChangeStatus::CHANGED;
}

return ChangeStatus::UNCHANGED;
}
};

struct AAInvariantLoadPointerFloating final : AAInvariantLoadPointerImpl {
AAInvariantLoadPointerFloating(const IRPosition &IRP, Attributor &A)
: AAInvariantLoadPointerImpl(IRP, A) {}
};

struct AAInvariantLoadPointerReturned final : AAInvariantLoadPointerImpl {
AAInvariantLoadPointerReturned(const IRPosition &IRP, Attributor &A)
: AAInvariantLoadPointerImpl(IRP, A) {}

void initialize(Attributor &) override {
removeAssumedBits(IS_LOCALLY_CONSTRAINED);
}
};

struct AAInvariantLoadPointerCallSiteReturned final
: AAInvariantLoadPointerImpl {
AAInvariantLoadPointerCallSiteReturned(const IRPosition &IRP, Attributor &A)
: AAInvariantLoadPointerImpl(IRP, A) {}

void initialize(Attributor &A) override {
const Function *F = getAssociatedFunction();
assert(F && "no associated function for return from call");

// not much we can say about opaque functions
if (F->isDeclaration() || F->isIntrinsic()) {
if (!F->onlyReadsMemory() || !F->hasNoSync()) {
indicatePessimisticFixpoint();
return;
}
}
AAInvariantLoadPointerImpl::initialize(A);
}

protected:
virtual bool requiresNoAlias() const override { return true; }
};

struct AAInvariantLoadPointerArgument final : AAInvariantLoadPointerImpl {
AAInvariantLoadPointerArgument(const IRPosition &IRP, Attributor &A)
: AAInvariantLoadPointerImpl(IRP, A) {}

void initialize(Attributor &) override {
const Function *F = getAssociatedFunction();
assert(F && "no associated function to argument");

if (!isCallableCC(F->getCallingConv())) {
addKnownBits(IS_LOCALLY_CONSTRAINED);
return;
}

if (!F->hasLocalLinkage())
removeAssumedBits(IS_LOCALLY_CONSTRAINED);
}

protected:
virtual bool requiresNoAlias() const override {
const Function *F = getAssociatedFunction();
assert(F && "no associated function to argument");
return !isCallableCC(F->getCallingConv());
}
};

struct AAInvariantLoadPointerCallSiteArgument final
: AAInvariantLoadPointerImpl {
AAInvariantLoadPointerCallSiteArgument(const IRPosition &IRP, Attributor &A)
: AAInvariantLoadPointerImpl(IRP, A) {}
};
} // namespace

/// ------------------------ Address Space ------------------------------------
namespace {

Expand Down Expand Up @@ -13031,6 +13343,7 @@ const char AAInterFnReachability::ID = 0;
const char AAPointerInfo::ID = 0;
const char AAAssumptionInfo::ID = 0;
const char AAUnderlyingObjects::ID = 0;
const char AAInvariantLoadPointer::ID = 0;
const char AAAddressSpace::ID = 0;
const char AAAllocationInfo::ID = 0;
const char AAIndirectCallInfo::ID = 0;
Expand Down Expand Up @@ -13165,6 +13478,7 @@ CREATE_VALUE_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAPotentialValues)
CREATE_VALUE_ABSTRACT_ATTRIBUTE_FOR_POSITION(AANoUndef)
CREATE_VALUE_ABSTRACT_ATTRIBUTE_FOR_POSITION(AANoFPClass)
CREATE_VALUE_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAPointerInfo)
CREATE_VALUE_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAInvariantLoadPointer)
CREATE_VALUE_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAAddressSpace)
CREATE_VALUE_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAAllocationInfo)

Expand Down
Loading