forked from OSchip/llvm-project
[Attributor] Deduce memory behavior of functions and arguments
Deduce the memory behavior, aka "read-none", "read-only", or "write-only", for functions and arguments. Reviewers: sstefan1, uenoku Subscribers: hiraditya, bollu, jfb, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D67384 llvm-svn: 373965
This commit is contained in:
parent
7cdeac43e5
commit
1097fab1cf
|
@ -408,7 +408,11 @@ struct IRPosition {
|
|||
|
||||
/// Return true if any kind in \p AKs existing in the IR at a position that
|
||||
/// will affect this one. See also getAttrs(...).
|
||||
bool hasAttr(ArrayRef<Attribute::AttrKind> AKs) const;
|
||||
/// \param IgnoreSubsumingPositions Flag to determine if subsuming positions,
|
||||
/// e.g., the function position if this is an
|
||||
/// argument position, should be ignored.
|
||||
bool hasAttr(ArrayRef<Attribute::AttrKind> AKs,
|
||||
bool IgnoreSubsumingPositions = false) 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
|
||||
|
@ -434,6 +438,28 @@ struct IRPosition {
|
|||
return Attribute();
|
||||
}
|
||||
|
||||
/// Remove the attribute of kind \p AKs existing in the IR at this position.
|
||||
void removeAttrs(ArrayRef<Attribute::AttrKind> AKs) {
|
||||
if (getPositionKind() == IRP_INVALID || getPositionKind() == IRP_FLOAT)
|
||||
return;
|
||||
|
||||
AttributeList AttrList;
|
||||
CallSite CS = CallSite(&getAnchorValue());
|
||||
if (CS)
|
||||
AttrList = CS.getAttributes();
|
||||
else
|
||||
AttrList = getAssociatedFunction()->getAttributes();
|
||||
|
||||
LLVMContext &Ctx = getAnchorValue().getContext();
|
||||
for (Attribute::AttrKind AK : AKs)
|
||||
AttrList = AttrList.removeAttribute(Ctx, getAttrIdx(), AK);
|
||||
|
||||
if (CS)
|
||||
CS.setAttributes(AttrList);
|
||||
else
|
||||
getAssociatedFunction()->setAttributes(AttrList);
|
||||
}
|
||||
|
||||
bool isAnyCallSitePosition() const {
|
||||
switch (getPositionKind()) {
|
||||
case IRPosition::IRP_CALL_SITE:
|
||||
|
@ -1824,6 +1850,54 @@ struct AAHeapToStack : public StateWrapper<BooleanState, AbstractAttribute>,
|
|||
static const char ID;
|
||||
};
|
||||
|
||||
/// An abstract interface for all memory related attributes.
|
||||
struct AAMemoryBehavior
|
||||
: public IRAttribute<Attribute::ReadNone,
|
||||
StateWrapper<IntegerState, AbstractAttribute>> {
|
||||
AAMemoryBehavior(const IRPosition &IRP) : IRAttribute(IRP) {}
|
||||
|
||||
/// State encoding bits. A set bit in the state means the property holds.
|
||||
/// BEST_STATE is the best possible state, 0 the worst possible state.
|
||||
enum {
|
||||
NO_READS = 1 << 0,
|
||||
NO_WRITES = 1 << 1,
|
||||
NO_ACCESSES = NO_READS | NO_WRITES,
|
||||
|
||||
BEST_STATE = NO_ACCESSES,
|
||||
};
|
||||
|
||||
/// Return true if we know that the underlying value is not read or accessed
|
||||
/// in its respective scope.
|
||||
bool isKnownReadNone() const { return isKnown(NO_ACCESSES); }
|
||||
|
||||
/// Return true if we assume that the underlying value is not read or accessed
|
||||
/// in its respective scope.
|
||||
bool isAssumedReadNone() const { return isAssumed(NO_ACCESSES); }
|
||||
|
||||
/// Return true if we know that the underlying value is not accessed
|
||||
/// (=written) in its respective scope.
|
||||
bool isKnownReadOnly() const { return isKnown(NO_WRITES); }
|
||||
|
||||
/// Return true if we assume that the underlying value is not accessed
|
||||
/// (=written) in its respective scope.
|
||||
bool isAssumedReadOnly() const { return isAssumed(NO_WRITES); }
|
||||
|
||||
/// Return true if we know that the underlying value is not read in its
|
||||
/// respective scope.
|
||||
bool isKnownWriteOnly() const { return isKnown(NO_READS); }
|
||||
|
||||
/// Return true if we assume that the underlying value is not read in its
|
||||
/// respective scope.
|
||||
bool isAssumedWriteOnly() const { return isAssumed(NO_READS); }
|
||||
|
||||
/// Create an abstract attribute view for the position \p IRP.
|
||||
static AAMemoryBehavior &createForPosition(const IRPosition &IRP,
|
||||
Attributor &A);
|
||||
|
||||
/// Unique ID (due to the unique address)
|
||||
static const char ID;
|
||||
};
|
||||
|
||||
} // end namespace llvm
|
||||
|
||||
#endif // LLVM_TRANSFORMS_IPO_FUNCTIONATTRS_H
|
||||
|
|
|
@ -418,11 +418,18 @@ SubsumingPositionIterator::SubsumingPositionIterator(const IRPosition &IRP) {
|
|||
}
|
||||
}
|
||||
|
||||
bool IRPosition::hasAttr(ArrayRef<Attribute::AttrKind> AKs) const {
|
||||
for (const IRPosition &EquivIRP : SubsumingPositionIterator(*this))
|
||||
bool IRPosition::hasAttr(ArrayRef<Attribute::AttrKind> AKs,
|
||||
bool IgnoreSubsumingPositions) const {
|
||||
for (const IRPosition &EquivIRP : SubsumingPositionIterator(*this)) {
|
||||
for (Attribute::AttrKind AK : AKs)
|
||||
if (EquivIRP.getAttr(AK).getKindAsEnum() == AK)
|
||||
return true;
|
||||
// The first position returned by the SubsumingPositionIterator is
|
||||
// always the position itself. If we ignore subsuming positions we
|
||||
// are done after the first iteration.
|
||||
if (IgnoreSubsumingPositions)
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -3437,6 +3444,448 @@ struct AAHeapToStackFunction final : public AAHeapToStackImpl {
|
|||
};
|
||||
} // namespace
|
||||
|
||||
/// -------------------- Memory Behavior Attributes ----------------------------
|
||||
/// Includes read-none, read-only, and write-only.
|
||||
/// ----------------------------------------------------------------------------
|
||||
struct AAMemoryBehaviorImpl : public AAMemoryBehavior {
|
||||
AAMemoryBehaviorImpl(const IRPosition &IRP) : AAMemoryBehavior(IRP) {}
|
||||
|
||||
/// See AbstractAttribute::initialize(...).
|
||||
void initialize(Attributor &A) override {
|
||||
intersectAssumedBits(BEST_STATE);
|
||||
getKnownStateFromValue(getIRPosition(), getState());
|
||||
IRAttribute::initialize(A);
|
||||
}
|
||||
|
||||
/// Return the memory behavior information encoded in the IR for \p IRP.
|
||||
static void getKnownStateFromValue(const IRPosition &IRP,
|
||||
IntegerState &State) {
|
||||
SmallVector<Attribute, 2> Attrs;
|
||||
IRP.getAttrs(AttrKinds, Attrs);
|
||||
for (const Attribute &Attr : Attrs) {
|
||||
switch (Attr.getKindAsEnum()) {
|
||||
case Attribute::ReadNone:
|
||||
State.addKnownBits(NO_ACCESSES);
|
||||
break;
|
||||
case Attribute::ReadOnly:
|
||||
State.addKnownBits(NO_WRITES);
|
||||
break;
|
||||
case Attribute::WriteOnly:
|
||||
State.addKnownBits(NO_READS);
|
||||
break;
|
||||
default:
|
||||
llvm_unreachable("Unexpcted attribute!");
|
||||
}
|
||||
}
|
||||
|
||||
if (auto *I = dyn_cast<Instruction>(&IRP.getAnchorValue())) {
|
||||
if (!I->mayReadFromMemory())
|
||||
State.addKnownBits(NO_READS);
|
||||
if (!I->mayWriteToMemory())
|
||||
State.addKnownBits(NO_WRITES);
|
||||
}
|
||||
}
|
||||
|
||||
/// See AbstractAttribute::getDeducedAttributes(...).
|
||||
void getDeducedAttributes(LLVMContext &Ctx,
|
||||
SmallVectorImpl<Attribute> &Attrs) const override {
|
||||
assert(Attrs.size() == 0);
|
||||
if (isAssumedReadNone())
|
||||
Attrs.push_back(Attribute::get(Ctx, Attribute::ReadNone));
|
||||
else if (isAssumedReadOnly())
|
||||
Attrs.push_back(Attribute::get(Ctx, Attribute::ReadOnly));
|
||||
else if (isAssumedWriteOnly())
|
||||
Attrs.push_back(Attribute::get(Ctx, Attribute::WriteOnly));
|
||||
assert(Attrs.size() <= 1);
|
||||
}
|
||||
|
||||
/// See AbstractAttribute::manifest(...).
|
||||
ChangeStatus manifest(Attributor &A) override {
|
||||
IRPosition &IRP = getIRPosition();
|
||||
|
||||
// Check if we would improve the existing attributes first.
|
||||
SmallVector<Attribute, 4> DeducedAttrs;
|
||||
getDeducedAttributes(IRP.getAnchorValue().getContext(), DeducedAttrs);
|
||||
if (llvm::all_of(DeducedAttrs, [&](const Attribute &Attr) {
|
||||
return IRP.hasAttr(Attr.getKindAsEnum(),
|
||||
/* IgnoreSubsumingPositions */ true);
|
||||
}))
|
||||
return ChangeStatus::UNCHANGED;
|
||||
|
||||
// Clear existing attributes.
|
||||
IRP.removeAttrs(AttrKinds);
|
||||
|
||||
// Use the generic manifest method.
|
||||
return IRAttribute::manifest(A);
|
||||
}
|
||||
|
||||
/// See AbstractState::getAsStr().
|
||||
const std::string getAsStr() const override {
|
||||
if (isAssumedReadNone())
|
||||
return "readnone";
|
||||
if (isAssumedReadOnly())
|
||||
return "readonly";
|
||||
if (isAssumedWriteOnly())
|
||||
return "writeonly";
|
||||
return "may-read/write";
|
||||
}
|
||||
|
||||
/// The set of IR attributes AAMemoryBehavior deals with.
|
||||
static const Attribute::AttrKind AttrKinds[3];
|
||||
};
|
||||
|
||||
const Attribute::AttrKind AAMemoryBehaviorImpl::AttrKinds[] = {
|
||||
Attribute::ReadNone, Attribute::ReadOnly, Attribute::WriteOnly};
|
||||
|
||||
/// Memory behavior attribute for a floating value.
|
||||
struct AAMemoryBehaviorFloating : AAMemoryBehaviorImpl {
|
||||
AAMemoryBehaviorFloating(const IRPosition &IRP) : AAMemoryBehaviorImpl(IRP) {}
|
||||
|
||||
/// See AbstractAttribute::initialize(...).
|
||||
void initialize(Attributor &A) override {
|
||||
AAMemoryBehaviorImpl::initialize(A);
|
||||
// Initialize the use vector with all direct uses of the associated value.
|
||||
for (const Use &U : getAssociatedValue().uses())
|
||||
Uses.insert(&U);
|
||||
}
|
||||
|
||||
/// See AbstractAttribute::updateImpl(...).
|
||||
ChangeStatus updateImpl(Attributor &A) override;
|
||||
|
||||
/// See AbstractAttribute::trackStatistics()
|
||||
void trackStatistics() const override {
|
||||
if (isAssumedReadNone())
|
||||
STATS_DECLTRACK_FLOATING_ATTR(readnone)
|
||||
else if (isAssumedReadOnly())
|
||||
STATS_DECLTRACK_FLOATING_ATTR(readonly)
|
||||
else if (isAssumedWriteOnly())
|
||||
STATS_DECLTRACK_FLOATING_ATTR(writeonly)
|
||||
}
|
||||
|
||||
private:
|
||||
/// Return true if users of \p UserI might access the underlying
|
||||
/// variable/location described by \p U and should therefore be analyzed.
|
||||
bool followUsersOfUseIn(Attributor &A, const Use *U,
|
||||
const Instruction *UserI);
|
||||
|
||||
/// Update the state according to the effect of use \p U in \p UserI.
|
||||
void analyzeUseIn(Attributor &A, const Use *U, const Instruction *UserI);
|
||||
|
||||
protected:
|
||||
/// Container for (transitive) uses of the associated argument.
|
||||
SetVector<const Use *> Uses;
|
||||
};
|
||||
|
||||
/// Memory behavior attribute for function argument.
|
||||
struct AAMemoryBehaviorArgument : AAMemoryBehaviorFloating {
|
||||
AAMemoryBehaviorArgument(const IRPosition &IRP)
|
||||
: AAMemoryBehaviorFloating(IRP) {}
|
||||
|
||||
/// See AbstractAttribute::initialize(...).
|
||||
void initialize(Attributor &A) override {
|
||||
AAMemoryBehaviorFloating::initialize(A);
|
||||
|
||||
// TODO: From readattrs.ll: "inalloca parameters are always
|
||||
// considered written"
|
||||
if (hasAttr({Attribute::InAlloca}))
|
||||
removeAssumedBits(NO_WRITES);
|
||||
|
||||
// Initialize the use vector with all direct uses of the associated value.
|
||||
Argument *Arg = getAssociatedArgument();
|
||||
if (!Arg || !Arg->getParent()->hasExactDefinition())
|
||||
indicatePessimisticFixpoint();
|
||||
}
|
||||
|
||||
/// See AbstractAttribute::trackStatistics()
|
||||
void trackStatistics() const override {
|
||||
if (isAssumedReadNone())
|
||||
STATS_DECLTRACK_ARG_ATTR(readnone)
|
||||
else if (isAssumedReadOnly())
|
||||
STATS_DECLTRACK_ARG_ATTR(readonly)
|
||||
else if (isAssumedWriteOnly())
|
||||
STATS_DECLTRACK_ARG_ATTR(writeonly)
|
||||
}
|
||||
};
|
||||
|
||||
struct AAMemoryBehaviorCallSiteArgument final : AAMemoryBehaviorArgument {
|
||||
AAMemoryBehaviorCallSiteArgument(const IRPosition &IRP)
|
||||
: AAMemoryBehaviorArgument(IRP) {}
|
||||
|
||||
/// See AbstractAttribute::updateImpl(...).
|
||||
ChangeStatus updateImpl(Attributor &A) override {
|
||||
// TODO: Once we have call site specific value information we can provide
|
||||
// call site specific liveness liveness information and then it makes
|
||||
// sense to specialize attributes for call sites arguments instead of
|
||||
// redirecting requests to the callee argument.
|
||||
Argument *Arg = getAssociatedArgument();
|
||||
const IRPosition &ArgPos = IRPosition::argument(*Arg);
|
||||
auto &ArgAA = A.getAAFor<AAMemoryBehavior>(*this, ArgPos);
|
||||
return clampStateAndIndicateChange(
|
||||
getState(),
|
||||
static_cast<const AANoCapture::StateType &>(ArgAA.getState()));
|
||||
}
|
||||
|
||||
/// See AbstractAttribute::trackStatistics()
|
||||
void trackStatistics() const override {
|
||||
if (isAssumedReadNone())
|
||||
STATS_DECLTRACK_CSARG_ATTR(readnone)
|
||||
else if (isAssumedReadOnly())
|
||||
STATS_DECLTRACK_CSARG_ATTR(readonly)
|
||||
else if (isAssumedWriteOnly())
|
||||
STATS_DECLTRACK_CSARG_ATTR(writeonly)
|
||||
}
|
||||
};
|
||||
|
||||
/// Memory behavior attribute for a call site return position.
|
||||
struct AAMemoryBehaviorCallSiteReturned final : AAMemoryBehaviorFloating {
|
||||
AAMemoryBehaviorCallSiteReturned(const IRPosition &IRP)
|
||||
: AAMemoryBehaviorFloating(IRP) {}
|
||||
|
||||
/// See AbstractAttribute::manifest(...).
|
||||
ChangeStatus manifest(Attributor &A) override {
|
||||
// We do not annotate returned values.
|
||||
return ChangeStatus::UNCHANGED;
|
||||
}
|
||||
|
||||
/// See AbstractAttribute::trackStatistics()
|
||||
void trackStatistics() const override {}
|
||||
};
|
||||
|
||||
/// An AA to represent the memory behavior function attributes.
|
||||
struct AAMemoryBehaviorFunction final : public AAMemoryBehaviorImpl {
|
||||
AAMemoryBehaviorFunction(const IRPosition &IRP) : AAMemoryBehaviorImpl(IRP) {}
|
||||
|
||||
/// See AbstractAttribute::updateImpl(Attributor &A).
|
||||
virtual ChangeStatus updateImpl(Attributor &A) override;
|
||||
|
||||
/// See AbstractAttribute::manifest(...).
|
||||
ChangeStatus manifest(Attributor &A) override {
|
||||
Function &F = cast<Function>(getAnchorValue());
|
||||
if (isAssumedReadNone()) {
|
||||
F.removeFnAttr(Attribute::ArgMemOnly);
|
||||
F.removeFnAttr(Attribute::InaccessibleMemOnly);
|
||||
F.removeFnAttr(Attribute::InaccessibleMemOrArgMemOnly);
|
||||
}
|
||||
return AAMemoryBehaviorImpl::manifest(A);
|
||||
}
|
||||
|
||||
/// See AbstractAttribute::trackStatistics()
|
||||
void trackStatistics() const override {
|
||||
if (isAssumedReadNone())
|
||||
STATS_DECLTRACK_FN_ATTR(readnone)
|
||||
else if (isAssumedReadOnly())
|
||||
STATS_DECLTRACK_FN_ATTR(readonly)
|
||||
else if (isAssumedWriteOnly())
|
||||
STATS_DECLTRACK_FN_ATTR(writeonly)
|
||||
}
|
||||
};
|
||||
|
||||
/// AAMemoryBehavior attribute for call sites.
|
||||
struct AAMemoryBehaviorCallSite final : AAMemoryBehaviorImpl {
|
||||
AAMemoryBehaviorCallSite(const IRPosition &IRP) : AAMemoryBehaviorImpl(IRP) {}
|
||||
|
||||
/// See AbstractAttribute::initialize(...).
|
||||
void initialize(Attributor &A) override {
|
||||
AAMemoryBehaviorImpl::initialize(A);
|
||||
Function *F = getAssociatedFunction();
|
||||
if (!F || !F->hasExactDefinition())
|
||||
indicatePessimisticFixpoint();
|
||||
}
|
||||
|
||||
/// See AbstractAttribute::updateImpl(...).
|
||||
ChangeStatus updateImpl(Attributor &A) override {
|
||||
// TODO: Once we have call site specific value information we can provide
|
||||
// call site specific liveness liveness information and then it makes
|
||||
// sense to specialize attributes for call sites arguments instead of
|
||||
// redirecting requests to the callee argument.
|
||||
Function *F = getAssociatedFunction();
|
||||
const IRPosition &FnPos = IRPosition::function(*F);
|
||||
auto &FnAA = A.getAAFor<AAMemoryBehavior>(*this, FnPos);
|
||||
return clampStateAndIndicateChange(
|
||||
getState(), static_cast<const AAAlign::StateType &>(FnAA.getState()));
|
||||
}
|
||||
|
||||
/// See AbstractAttribute::trackStatistics()
|
||||
void trackStatistics() const override {
|
||||
if (isAssumedReadNone())
|
||||
STATS_DECLTRACK_CS_ATTR(readnone)
|
||||
else if (isAssumedReadOnly())
|
||||
STATS_DECLTRACK_CS_ATTR(readonly)
|
||||
else if (isAssumedWriteOnly())
|
||||
STATS_DECLTRACK_CS_ATTR(writeonly)
|
||||
}
|
||||
};
|
||||
|
||||
ChangeStatus AAMemoryBehaviorFunction::updateImpl(Attributor &A) {
|
||||
|
||||
// The current assumed state used to determine a change.
|
||||
auto AssumedState = getAssumed();
|
||||
|
||||
auto CheckRWInst = [&](Instruction &I) {
|
||||
// If the instruction has an own memory behavior state, use it to restrict
|
||||
// the local state. No further analysis is required as the other memory
|
||||
// state is as optimistic as it gets.
|
||||
if (ImmutableCallSite ICS = ImmutableCallSite(&I)) {
|
||||
const auto &MemBehaviorAA = A.getAAFor<AAMemoryBehavior>(
|
||||
*this, IRPosition::callsite_function(ICS));
|
||||
intersectAssumedBits(MemBehaviorAA.getAssumed());
|
||||
return !isAtFixpoint();
|
||||
}
|
||||
|
||||
// Remove access kind modifiers if necessary.
|
||||
if (I.mayReadFromMemory())
|
||||
removeAssumedBits(NO_READS);
|
||||
if (I.mayWriteToMemory())
|
||||
removeAssumedBits(NO_WRITES);
|
||||
return !isAtFixpoint();
|
||||
};
|
||||
|
||||
if (!A.checkForAllReadWriteInstructions(CheckRWInst, *this))
|
||||
return indicatePessimisticFixpoint();
|
||||
|
||||
return (AssumedState != getAssumed()) ? ChangeStatus::CHANGED
|
||||
: ChangeStatus::UNCHANGED;
|
||||
}
|
||||
|
||||
ChangeStatus AAMemoryBehaviorFloating::updateImpl(Attributor &A) {
|
||||
|
||||
const IRPosition &IRP = getIRPosition();
|
||||
const IRPosition &FnPos = IRPosition::function_scope(IRP);
|
||||
AAMemoryBehavior::StateType &S = getState();
|
||||
|
||||
// First, check the function scope. We take the known information and we avoid
|
||||
// work if the assumed information implies the current assumed information for
|
||||
// this attribute.
|
||||
const auto &FnMemAA = A.getAAFor<AAMemoryBehavior>(*this, FnPos);
|
||||
S.addKnownBits(FnMemAA.getKnown());
|
||||
if ((S.getAssumed() & FnMemAA.getAssumed()) == S.getAssumed())
|
||||
return ChangeStatus::UNCHANGED;
|
||||
|
||||
// Make sure the value is not captured (except through "return"), if
|
||||
// it is, any information derived would be irrelevant anyway as we cannot
|
||||
// check the potential aliases introduced by the capture.
|
||||
const auto &ArgNoCaptureAA = A.getAAFor<AANoCapture>(*this, IRP);
|
||||
if (!ArgNoCaptureAA.isAssumedNoCaptureMaybeReturned())
|
||||
return indicatePessimisticFixpoint();
|
||||
|
||||
// The current assumed state used to determine a change.
|
||||
auto AssumedState = S.getAssumed();
|
||||
|
||||
// Liveness information to exclude dead users.
|
||||
// TODO: Take the FnPos once we have call site specific liveness information.
|
||||
const auto &LivenessAA = A.getAAFor<AAIsDead>(
|
||||
*this, IRPosition::function(*IRP.getAssociatedFunction()));
|
||||
|
||||
// Visit and expand uses until all are analyzed or a fixpoint is reached.
|
||||
for (unsigned i = 0; i < Uses.size() && !isAtFixpoint(); i++) {
|
||||
const Use *U = Uses[i];
|
||||
Instruction *UserI = cast<Instruction>(U->getUser());
|
||||
LLVM_DEBUG(dbgs() << "[AAMemoryBehavior] Use: " << **U << " in " << *UserI
|
||||
<< " [Dead: " << (LivenessAA.isAssumedDead(UserI))
|
||||
<< "]\n");
|
||||
if (LivenessAA.isAssumedDead(UserI))
|
||||
continue;
|
||||
|
||||
// Check if the users of UserI should also be visited.
|
||||
if (followUsersOfUseIn(A, U, UserI))
|
||||
for (const Use &UserIUse : UserI->uses())
|
||||
Uses.insert(&UserIUse);
|
||||
|
||||
// If UserI might touch memory we analyze the use in detail.
|
||||
if (UserI->mayReadOrWriteMemory())
|
||||
analyzeUseIn(A, U, UserI);
|
||||
}
|
||||
|
||||
return (AssumedState != getAssumed()) ? ChangeStatus::CHANGED
|
||||
: ChangeStatus::UNCHANGED;
|
||||
}
|
||||
|
||||
bool AAMemoryBehaviorFloating::followUsersOfUseIn(Attributor &A, const Use *U,
|
||||
const Instruction *UserI) {
|
||||
// The loaded value is unrelated to the pointer argument, no need to
|
||||
// follow the users of the load.
|
||||
if (isa<LoadInst>(UserI))
|
||||
return false;
|
||||
|
||||
// By default we follow all uses assuming UserI might leak information on U,
|
||||
// we have special handling for call sites operands though.
|
||||
ImmutableCallSite ICS(UserI);
|
||||
if (!ICS || !ICS.isArgOperand(U))
|
||||
return true;
|
||||
|
||||
// If the use is a call argument known not to be captured, the users of
|
||||
// the call do not need to be visited because they have to be unrelated to
|
||||
// the input. Note that this check is not trivial even though we disallow
|
||||
// general capturing of the underlying argument. The reason is that the
|
||||
// call might the argument "through return", which we allow and for which we
|
||||
// need to check call users.
|
||||
unsigned ArgNo = ICS.getArgumentNo(U);
|
||||
const auto &ArgNoCaptureAA =
|
||||
A.getAAFor<AANoCapture>(*this, IRPosition::callsite_argument(ICS, ArgNo));
|
||||
return !ArgNoCaptureAA.isAssumedNoCapture();
|
||||
}
|
||||
|
||||
void AAMemoryBehaviorFloating::analyzeUseIn(Attributor &A, const Use *U,
|
||||
const Instruction *UserI) {
|
||||
assert(UserI->mayReadOrWriteMemory());
|
||||
|
||||
switch (UserI->getOpcode()) {
|
||||
default:
|
||||
// TODO: Handle all atomics and other side-effect operations we know of.
|
||||
break;
|
||||
case Instruction::Load:
|
||||
// Loads cause the NO_READS property to disappear.
|
||||
removeAssumedBits(NO_READS);
|
||||
return;
|
||||
|
||||
case Instruction::Store:
|
||||
// Stores cause the NO_WRITES property to disappear if the use is the
|
||||
// pointer operand. Note that we do assume that capturing was taken care of
|
||||
// somewhere else.
|
||||
if (cast<StoreInst>(UserI)->getPointerOperand() == U->get())
|
||||
removeAssumedBits(NO_WRITES);
|
||||
return;
|
||||
|
||||
case Instruction::Call:
|
||||
case Instruction::CallBr:
|
||||
case Instruction::Invoke: {
|
||||
// For call sites we look at the argument memory behavior attribute (this
|
||||
// could be recursive!) in order to restrict our own state.
|
||||
ImmutableCallSite ICS(UserI);
|
||||
|
||||
// Give up on operand bundles.
|
||||
if (ICS.isBundleOperand(U)) {
|
||||
indicatePessimisticFixpoint();
|
||||
return;
|
||||
}
|
||||
|
||||
// Calling a function does read the function pointer, maybe write it if the
|
||||
// function is self-modifying.
|
||||
if (ICS.isCallee(U)) {
|
||||
removeAssumedBits(NO_READS);
|
||||
break;
|
||||
}
|
||||
|
||||
// Adjust the possible access behavior based on the information on the
|
||||
// argument.
|
||||
unsigned ArgNo = ICS.getArgumentNo(U);
|
||||
const IRPosition &ArgPos = IRPosition::callsite_argument(ICS, ArgNo);
|
||||
const auto &MemBehaviorAA = A.getAAFor<AAMemoryBehavior>(*this, ArgPos);
|
||||
// "assumed" has at most the same bits as the MemBehaviorAA assumed
|
||||
// and at least "known".
|
||||
intersectAssumedBits(MemBehaviorAA.getAssumed());
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// Generally, look at the "may-properties" and adjust the assumed state if we
|
||||
// did not trigger special handling before.
|
||||
if (UserI->mayReadFromMemory())
|
||||
removeAssumedBits(NO_READS);
|
||||
if (UserI->mayWriteToMemory())
|
||||
removeAssumedBits(NO_WRITES);
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------------------------------
|
||||
/// Attributor
|
||||
/// ----------------------------------------------------------------------------
|
||||
|
@ -3607,7 +4056,8 @@ bool Attributor::checkForAllInstructions(
|
|||
|
||||
auto &OpcodeInstMap =
|
||||
InfoCache.getOpcodeInstMapForFunction(*AssociatedFunction);
|
||||
if (!checkForAllInstructionsImpl(OpcodeInstMap, Pred, &LivenessAA, AnyDead, Opcodes))
|
||||
if (!checkForAllInstructionsImpl(OpcodeInstMap, Pred, &LivenessAA, AnyDead,
|
||||
Opcodes))
|
||||
return false;
|
||||
|
||||
// If we actually used liveness information so we have to record a dependence.
|
||||
|
@ -3965,6 +4415,9 @@ void Attributor::identifyDefaultAbstractAttributes(Function &F) {
|
|||
// Every function might be "no-recurse".
|
||||
getOrCreateAAFor<AANoRecurse>(FPos);
|
||||
|
||||
// Every function might be "readnone/readonly/writeonly/...".
|
||||
getOrCreateAAFor<AAMemoryBehavior>(FPos);
|
||||
|
||||
// Every function might be applicable for Heap-To-Stack conversion.
|
||||
if (EnableHeapToStack)
|
||||
getOrCreateAAFor<AAHeapToStack>(FPos);
|
||||
|
@ -4019,6 +4472,10 @@ void Attributor::identifyDefaultAbstractAttributes(Function &F) {
|
|||
|
||||
// Every argument with pointer type might be marked nocapture.
|
||||
getOrCreateAAFor<AANoCapture>(ArgPos);
|
||||
|
||||
// Every argument with pointer type might be marked
|
||||
// "readnone/readonly/writeonly/..."
|
||||
getOrCreateAAFor<AAMemoryBehavior>(ArgPos);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4232,6 +4689,7 @@ const char AAAlign::ID = 0;
|
|||
const char AANoCapture::ID = 0;
|
||||
const char AAValueSimplify::ID = 0;
|
||||
const char AAHeapToStack::ID = 0;
|
||||
const char AAMemoryBehavior::ID = 0;
|
||||
|
||||
// Macro magic to create the static generator function for attributes that
|
||||
// follow the naming scheme.
|
||||
|
@ -4310,6 +4768,23 @@ const char AAHeapToStack::ID = 0;
|
|||
return *AA; \
|
||||
}
|
||||
|
||||
#define CREATE_NON_RET_ABSTRACT_ATTRIBUTE_FOR_POSITION(CLASS) \
|
||||
CLASS &CLASS::createForPosition(const IRPosition &IRP, Attributor &A) { \
|
||||
CLASS *AA = nullptr; \
|
||||
switch (IRP.getPositionKind()) { \
|
||||
SWITCH_PK_INV(CLASS, IRP_INVALID, "invalid") \
|
||||
SWITCH_PK_INV(CLASS, IRP_RETURNED, "returned") \
|
||||
SWITCH_PK_CREATE(CLASS, IRP, IRP_FUNCTION, Function) \
|
||||
SWITCH_PK_CREATE(CLASS, IRP, IRP_CALL_SITE, CallSite) \
|
||||
SWITCH_PK_CREATE(CLASS, IRP, IRP_FLOAT, Floating) \
|
||||
SWITCH_PK_CREATE(CLASS, IRP, IRP_ARGUMENT, Argument) \
|
||||
SWITCH_PK_CREATE(CLASS, IRP, IRP_CALL_SITE_RETURNED, CallSiteReturned) \
|
||||
SWITCH_PK_CREATE(CLASS, IRP, IRP_CALL_SITE_ARGUMENT, CallSiteArgument) \
|
||||
} \
|
||||
AA->initialize(A); \
|
||||
return *AA; \
|
||||
}
|
||||
|
||||
CREATE_FUNCTION_ABSTRACT_ATTRIBUTE_FOR_POSITION(AANoUnwind)
|
||||
CREATE_FUNCTION_ABSTRACT_ATTRIBUTE_FOR_POSITION(AANoSync)
|
||||
CREATE_FUNCTION_ABSTRACT_ATTRIBUTE_FOR_POSITION(AANoFree)
|
||||
|
@ -4329,6 +4804,8 @@ CREATE_ALL_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAValueSimplify)
|
|||
|
||||
CREATE_FUNCTION_ONLY_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAHeapToStack)
|
||||
|
||||
CREATE_NON_RET_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAMemoryBehavior)
|
||||
|
||||
#undef CREATE_FUNCTION_ABSTRACT_ATTRIBUTE_FOR_POSITION
|
||||
#undef CREATE_VALUE_ABSTRACT_ATTRIBUTE_FOR_POSITION
|
||||
#undef CREATE_ALL_ABSTRACT_ATTRIBUTE_FOR_POSITION
|
||||
|
|
|
@ -7,26 +7,26 @@ target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
|
|||
|
||||
|
||||
; TEST 1
|
||||
; ATTRIBUTOR: define align 8 i32* @test1(i32* returned align 8 "no-capture-maybe-returned" %0)
|
||||
; ATTRIBUTOR: define align 8 i32* @test1(i32* readnone returned align 8 "no-capture-maybe-returned" %0)
|
||||
define i32* @test1(i32* align 8 %0) #0 {
|
||||
ret i32* %0
|
||||
}
|
||||
|
||||
; TEST 2
|
||||
; ATTRIBUTOR: define i32* @test2(i32* returned "no-capture-maybe-returned" %0)
|
||||
; ATTRIBUTOR: define i32* @test2(i32* readnone returned "no-capture-maybe-returned" %0)
|
||||
define i32* @test2(i32* %0) #0 {
|
||||
ret i32* %0
|
||||
}
|
||||
|
||||
; TEST 3
|
||||
; ATTRIBUTOR: define align 4 i32* @test3(i32* align 8 "no-capture-maybe-returned" %0, i32* align 4 "no-capture-maybe-returned" %1, i1 %2)
|
||||
; ATTRIBUTOR: define align 4 i32* @test3(i32* readnone align 8 "no-capture-maybe-returned" %0, i32* readnone align 4 "no-capture-maybe-returned" %1, i1 %2)
|
||||
define i32* @test3(i32* align 8 %0, i32* align 4 %1, i1 %2) #0 {
|
||||
%ret = select i1 %2, i32* %0, i32* %1
|
||||
ret i32* %ret
|
||||
}
|
||||
|
||||
; TEST 4
|
||||
; ATTRIBUTOR: define align 32 i32* @test4(i32* align 32 "no-capture-maybe-returned" %0, i32* align 32 "no-capture-maybe-returned" %1, i1 %2)
|
||||
; ATTRIBUTOR: define align 32 i32* @test4(i32* readnone align 32 "no-capture-maybe-returned" %0, i32* readnone align 32 "no-capture-maybe-returned" %1, i1 %2)
|
||||
define i32* @test4(i32* align 32 %0, i32* align 32 %1, i1 %2) #0 {
|
||||
%ret = select i1 %2, i32* %0, i32* %1
|
||||
ret i32* %ret
|
||||
|
@ -139,7 +139,7 @@ define internal i8* @f3(i8* readnone %0) local_unnamed_addr #0 {
|
|||
|
||||
; TEST 7
|
||||
; Better than IR information
|
||||
; ATTRIBUTOR: define align 32 i32* @test7(i32* returned align 32 "no-capture-maybe-returned" %p)
|
||||
; ATTRIBUTOR: define align 32 i32* @test7(i32* readnone returned align 32 "no-capture-maybe-returned" %p)
|
||||
define align 4 i32* @test7(i32* align 32 %p) #0 {
|
||||
tail call i8* @f1(i8* align 8 dereferenceable(1) @a1)
|
||||
ret i32* %p
|
||||
|
@ -162,7 +162,7 @@ define void @test8_helper() {
|
|||
}
|
||||
|
||||
define internal void @test8(i32* %a, i32* %b, i32* %c) {
|
||||
; ATTRIBUTOR: define internal void @test8(i32* nocapture align 4 %a, i32* nocapture align 4 %b, i32* nocapture %c)
|
||||
; ATTRIBUTOR: define internal void @test8(i32* nocapture readnone align 4 %a, i32* nocapture readnone align 4 %b, i32* nocapture readnone %c)
|
||||
ret void
|
||||
}
|
||||
|
||||
|
|
|
@ -116,8 +116,7 @@ entry:
|
|||
;
|
||||
; CHECK: define dereferenceable_or_null(8) i64* @scc_B(double* readnone returned dereferenceable_or_null(8) "no-capture-maybe-returned" %a)
|
||||
;
|
||||
; FIXME: readnone missing for %s
|
||||
; CHECK: define dereferenceable_or_null(2) i8* @scc_C(i16* returned dereferenceable_or_null(2) "no-capture-maybe-returned" %a)
|
||||
; CHECK: define dereferenceable_or_null(2) i8* @scc_C(i16* readnone returned dereferenceable_or_null(2) "no-capture-maybe-returned" %a)
|
||||
;
|
||||
; float *scc_A(int *a) {
|
||||
; return (float*)(a ? (int*)scc_A((int*)scc_B((double*)scc_C((short*)a))) : a);
|
||||
|
@ -245,7 +244,7 @@ declare i32 @printf(i8* nocapture, ...)
|
|||
; }
|
||||
;
|
||||
; There should *not* be a no-capture attribute on %a
|
||||
; CHECK: define i64* @not_captured_but_returned_0(i64* returned "no-capture-maybe-returned" %a)
|
||||
; CHECK: define i64* @not_captured_but_returned_0(i64* returned writeonly "no-capture-maybe-returned" %a)
|
||||
define i64* @not_captured_but_returned_0(i64* %a) #0 {
|
||||
entry:
|
||||
store i64 0, i64* %a, align 8
|
||||
|
@ -260,7 +259,7 @@ entry:
|
|||
; }
|
||||
;
|
||||
; There should *not* be a no-capture attribute on %a
|
||||
; CHECK: define nonnull i64* @not_captured_but_returned_1(i64* "no-capture-maybe-returned" %a)
|
||||
; CHECK: define nonnull i64* @not_captured_but_returned_1(i64* writeonly "no-capture-maybe-returned" %a)
|
||||
define i64* @not_captured_but_returned_1(i64* %a) #0 {
|
||||
entry:
|
||||
%add.ptr = getelementptr inbounds i64, i64* %a, i64 1
|
||||
|
@ -275,8 +274,7 @@ entry:
|
|||
; not_captured_but_returned_1(a);
|
||||
; }
|
||||
;
|
||||
; FIXME: no-capture missing for %a
|
||||
; CHECK: define void @test_not_captured_but_returned_calls(i64* nocapture %a)
|
||||
; CHECK: define void @test_not_captured_but_returned_calls(i64* nocapture writeonly %a)
|
||||
define void @test_not_captured_but_returned_calls(i64* %a) #0 {
|
||||
entry:
|
||||
%call = call i64* @not_captured_but_returned_0(i64* %a)
|
||||
|
@ -291,7 +289,7 @@ entry:
|
|||
; }
|
||||
;
|
||||
; There should *not* be a no-capture attribute on %a
|
||||
; CHECK: define i64* @negative_test_not_captured_but_returned_call_0a(i64* returned "no-capture-maybe-returned" %a)
|
||||
; CHECK: define i64* @negative_test_not_captured_but_returned_call_0a(i64* returned writeonly "no-capture-maybe-returned" %a)
|
||||
define i64* @negative_test_not_captured_but_returned_call_0a(i64* %a) #0 {
|
||||
entry:
|
||||
%call = call i64* @not_captured_but_returned_0(i64* %a)
|
||||
|
@ -305,7 +303,7 @@ entry:
|
|||
; }
|
||||
;
|
||||
; There should *not* be a no-capture attribute on %a
|
||||
; CHECK: define void @negative_test_not_captured_but_returned_call_0b(i64* %a)
|
||||
; CHECK: define void @negative_test_not_captured_but_returned_call_0b(i64* writeonly %a)
|
||||
define void @negative_test_not_captured_but_returned_call_0b(i64* %a) #0 {
|
||||
entry:
|
||||
%call = call i64* @not_captured_but_returned_0(i64* %a)
|
||||
|
@ -321,7 +319,7 @@ entry:
|
|||
; }
|
||||
;
|
||||
; There should *not* be a no-capture attribute on %a
|
||||
; CHECK: define nonnull i64* @negative_test_not_captured_but_returned_call_1a(i64* "no-capture-maybe-returned" %a)
|
||||
; CHECK: define nonnull i64* @negative_test_not_captured_but_returned_call_1a(i64* writeonly "no-capture-maybe-returned" %a)
|
||||
define i64* @negative_test_not_captured_but_returned_call_1a(i64* %a) #0 {
|
||||
entry:
|
||||
%call = call i64* @not_captured_but_returned_1(i64* %a)
|
||||
|
@ -335,7 +333,7 @@ entry:
|
|||
; }
|
||||
;
|
||||
; There should *not* be a no-capture attribute on %a
|
||||
; CHECK: define void @negative_test_not_captured_but_returned_call_1b(i64* %a)
|
||||
; CHECK: define void @negative_test_not_captured_but_returned_call_1b(i64* writeonly %a)
|
||||
define void @negative_test_not_captured_but_returned_call_1b(i64* %a) #0 {
|
||||
entry:
|
||||
%call = call i64* @not_captured_but_returned_1(i64* %a)
|
||||
|
@ -391,7 +389,7 @@ r:
|
|||
|
||||
; TEST not captured by readonly external function
|
||||
;
|
||||
; CHECK: define void @not_captured_by_readonly_call(i32* nocapture %b)
|
||||
; CHECK: define void @not_captured_by_readonly_call(i32* nocapture readonly %b)
|
||||
declare i32* @readonly_unknown(i32*, i32*) readonly
|
||||
|
||||
define void @not_captured_by_readonly_call(i32* %b) #0 {
|
||||
|
|
|
@ -159,23 +159,16 @@ return: ; preds = %cond.end, %if.then3
|
|||
|
||||
; TEST SCC test returning a pointer value argument
|
||||
;
|
||||
; BOTH: Function Attrs: nofree noinline norecurse nosync nounwind readnone uwtable
|
||||
; BOTH-NEXT: define double* @ptr_sink_r0(double* readnone returned "no-capture-maybe-returned" %r)
|
||||
; BOTH: Function Attrs: nofree noinline nosync nounwind readnone uwtable
|
||||
; BOTH-NEXT: define double* @ptr_scc_r1(double* %a, double* readnone returned %r, double* nocapture readnone %b)
|
||||
; BOTH: Function Attrs: nofree noinline nosync nounwind readnone uwtable
|
||||
; BOTH-NEXT: define double* @ptr_scc_r2(double* readnone %a, double* readnone %b, double* readnone returned %r)
|
||||
;
|
||||
; FNATTR: define double* @ptr_sink_r0(double* readnone returned %r)
|
||||
; FNATTR: define double* @ptr_scc_r1(double* %a, double* readnone %r, double* nocapture readnone %b)
|
||||
; FNATTR: define double* @ptr_scc_r2(double* readnone %a, double* readnone %b, double* readnone %r)
|
||||
;
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind uwtable
|
||||
; ATTRIBUTOR-NEXT: define double* @ptr_sink_r0(double* returned "no-capture-maybe-returned" %r)
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind uwtable
|
||||
; ATTRIBUTOR-NEXT: define double* @ptr_scc_r1(double* %a, double* returned %r, double* nocapture %b)
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind uwtable
|
||||
; ATTRIBUTOR-NEXT: define double* @ptr_scc_r2(double* %a, double* %b, double* returned %r)
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind readnone uwtable
|
||||
; ATTRIBUTOR-NEXT: define double* @ptr_sink_r0(double* readnone returned "no-capture-maybe-returned" %r)
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind readnone uwtable
|
||||
; ATTRIBUTOR-NEXT: define double* @ptr_scc_r1(double* readnone %a, double* readnone returned %r, double* nocapture readnone %b)
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind readnone uwtable
|
||||
; ATTRIBUTOR-NEXT: define double* @ptr_scc_r2(double* readnone %a, double* readnone %b, double* readnone returned %r)
|
||||
;
|
||||
; double* ptr_scc_r1(double* a, double* b, double* r);
|
||||
; double* ptr_scc_r2(double* a, double* b, double* r);
|
||||
|
@ -293,7 +286,7 @@ entry:
|
|||
;
|
||||
; FNATTR: define i32* @rt2_helper(i32* %a)
|
||||
; FNATTR: define i32* @rt2(i32* readnone %a, i32* readnone %b)
|
||||
; BOTH: define i32* @rt2_helper(i32* returned %a)
|
||||
; BOTH: define i32* @rt2_helper(i32* readnone returned %a)
|
||||
; BOTH: define i32* @rt2(i32* readnone %a, i32* readnone "no-capture-maybe-returned" %b)
|
||||
define i32* @rt2_helper(i32* %a) #0 {
|
||||
entry:
|
||||
|
@ -319,7 +312,7 @@ if.end:
|
|||
;
|
||||
; FNATTR: define i32* @rt3_helper(i32* %a, i32* %b)
|
||||
; FNATTR: define i32* @rt3(i32* readnone %a, i32* readnone %b)
|
||||
; BOTH: define i32* @rt3_helper(i32* %a, i32* returned "no-capture-maybe-returned" %b)
|
||||
; BOTH: define i32* @rt3_helper(i32* readnone %a, i32* readnone returned "no-capture-maybe-returned" %b)
|
||||
; BOTH: define i32* @rt3(i32* readnone %a, i32* readnone returned "no-capture-maybe-returned" %b)
|
||||
define i32* @rt3_helper(i32* %a, i32* %b) #0 {
|
||||
entry:
|
||||
|
@ -355,7 +348,7 @@ if.end:
|
|||
; BOTH: Function Attrs: noinline nounwind uwtable
|
||||
; BOTH-NEXT: define i32* @calls_unknown_fn(i32* readnone returned "no-capture-maybe-returned" %r)
|
||||
; FNATTR: define i32* @calls_unknown_fn(i32* readnone returned %r)
|
||||
; ATTRIBUTOR: define i32* @calls_unknown_fn(i32* returned "no-capture-maybe-returned" %r)
|
||||
; ATTRIBUTOR: define i32* @calls_unknown_fn(i32* readnone returned "no-capture-maybe-returned" %r)
|
||||
declare void @unknown_fn(i32* (i32*)*) #0
|
||||
|
||||
define i32* @calls_unknown_fn(i32* %r) #0 {
|
||||
|
@ -443,7 +436,7 @@ entry:
|
|||
; BOTH-NEXT: define double @select_and_phi(double returned %b)
|
||||
;
|
||||
; FNATTR: define double @select_and_phi(double %b)
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind uwtable
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind readnone uwtable
|
||||
; ATTRIBUTOR-NEXT: define double @select_and_phi(double returned %b)
|
||||
define double @select_and_phi(double %b) #0 {
|
||||
entry:
|
||||
|
@ -475,7 +468,7 @@ if.end: ; preds = %if.then, %entry
|
|||
;
|
||||
; FNATTR: define double @recursion_select_and_phi(i32 %a, double %b)
|
||||
;
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind uwtable
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind readnone uwtable
|
||||
; ATTRIBUTOR-NEXT: define double @recursion_select_and_phi(i32 %a, double returned %b)
|
||||
define double @recursion_select_and_phi(i32 %a, double %b) #0 {
|
||||
entry:
|
||||
|
@ -506,8 +499,8 @@ if.end: ; preds = %if.then, %entry
|
|||
;
|
||||
; FNATTR: define double* @bitcast(i32* readnone %b)
|
||||
;
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind uwtable
|
||||
; ATTRIBUTOR-NEXT: define double* @bitcast(i32* returned "no-capture-maybe-returned" %b)
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind readnone uwtable
|
||||
; ATTRIBUTOR-NEXT: define double* @bitcast(i32* readnone returned "no-capture-maybe-returned" %b)
|
||||
define double* @bitcast(i32* %b) #0 {
|
||||
entry:
|
||||
%bc0 = bitcast i32* %b to double*
|
||||
|
@ -529,8 +522,8 @@ entry:
|
|||
;
|
||||
; FNATTR: define double* @bitcasts_select_and_phi(i32* readnone %b)
|
||||
;
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind uwtable
|
||||
; ATTRIBUTOR-NEXT: define double* @bitcasts_select_and_phi(i32* returned %b)
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind readnone uwtable
|
||||
; ATTRIBUTOR-NEXT: define double* @bitcasts_select_and_phi(i32* readnone returned %b)
|
||||
define double* @bitcasts_select_and_phi(i32* %b) #0 {
|
||||
entry:
|
||||
%bc0 = bitcast i32* %b to double*
|
||||
|
@ -567,8 +560,8 @@ if.end: ; preds = %if.then, %entry
|
|||
;
|
||||
; FNATTR: define double* @ret_arg_arg_undef(i32* readnone %b)
|
||||
;
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind uwtable
|
||||
; ATTRIBUTOR-NEXT: define double* @ret_arg_arg_undef(i32* returned %b)
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind readnone uwtable
|
||||
; ATTRIBUTOR-NEXT: define double* @ret_arg_arg_undef(i32* readnone returned %b)
|
||||
define double* @ret_arg_arg_undef(i32* %b) #0 {
|
||||
entry:
|
||||
%bc0 = bitcast i32* %b to double*
|
||||
|
@ -605,8 +598,8 @@ ret_undef:
|
|||
;
|
||||
; FNATTR: define double* @ret_undef_arg_arg(i32* readnone %b)
|
||||
;
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind uwtable
|
||||
; ATTRIBUTOR-NEXT: define double* @ret_undef_arg_arg(i32* returned %b)
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind readnone uwtable
|
||||
; ATTRIBUTOR-NEXT: define double* @ret_undef_arg_arg(i32* readnone returned %b)
|
||||
define double* @ret_undef_arg_arg(i32* %b) #0 {
|
||||
entry:
|
||||
%bc0 = bitcast i32* %b to double*
|
||||
|
@ -642,7 +635,7 @@ ret_arg1:
|
|||
; BOTH-NEXT: define double* @ret_undef_arg_undef(i32* readnone returned %b)
|
||||
;
|
||||
; FNATTR: define double* @ret_undef_arg_undef(i32* readnone %b)
|
||||
; ATTRIBUTOR: define double* @ret_undef_arg_undef(i32* returned %b)
|
||||
; ATTRIBUTOR: define double* @ret_undef_arg_undef(i32* readnone returned %b)
|
||||
define double* @ret_undef_arg_undef(i32* %b) #0 {
|
||||
entry:
|
||||
%bc0 = bitcast i32* %b to double*
|
||||
|
@ -846,7 +839,8 @@ attributes #0 = { noinline nounwind uwtable }
|
|||
; BOTH-DAG: attributes #{{[0-9]*}} = { nofree noinline noreturn nosync nounwind readonly uwtable }
|
||||
; BOTH-DAG: attributes #{{[0-9]*}} = { noinline nounwind uwtable }
|
||||
; BOTH-DAG: attributes #{{[0-9]*}} = { noreturn }
|
||||
; BOTH-DAG: attributes #{{[0-9]*}} = { nofree nosync willreturn }
|
||||
; BOTH-DAG: attributes #{{[0-9]*}} = { nofree nosync }
|
||||
; BOTH-DAG: attributes #{{[0-9]*}} = { nofree noreturn nosync }
|
||||
; BOTH-DAG: attributes #{{[0-9]*}} = { nofree noinline norecurse nosync nounwind readnone uwtable }
|
||||
; BOTH-DAG: attributes #{{[0-9]*}} = { nofree nosync readnone willreturn }
|
||||
; BOTH-DAG: attributes #{{[0-9]*}} = { nofree nosync readnone }
|
||||
; BOTH-DAG: attributes #{{[0-9]*}} = { nofree noreturn nosync readonly }
|
||||
; BOTH-NOT: attributes #
|
||||
|
|
|
@ -7,7 +7,7 @@ declare void @deref_phi_user(i32* %a);
|
|||
; take mininimum of return values
|
||||
;
|
||||
define i32* @test1(i32* dereferenceable(4) %0, double* dereferenceable(8) %1, i1 zeroext %2) local_unnamed_addr {
|
||||
; ATTRIBUTOR: define nonnull dereferenceable(4) i32* @test1(i32* nonnull dereferenceable(4) "no-capture-maybe-returned" %0, double* nonnull dereferenceable(8) "no-capture-maybe-returned" %1, i1 zeroext %2)
|
||||
; ATTRIBUTOR: define nonnull dereferenceable(4) i32* @test1(i32* nonnull readnone dereferenceable(4) "no-capture-maybe-returned" %0, double* nonnull readnone dereferenceable(8) "no-capture-maybe-returned" %1, i1 zeroext %2)
|
||||
%4 = bitcast double* %1 to i32*
|
||||
%5 = select i1 %2, i32* %0, i32* %4
|
||||
ret i32* %5
|
||||
|
@ -15,7 +15,7 @@ define i32* @test1(i32* dereferenceable(4) %0, double* dereferenceable(8) %1, i1
|
|||
|
||||
; TEST 2
|
||||
define i32* @test2(i32* dereferenceable_or_null(4) %0, double* dereferenceable(8) %1, i1 zeroext %2) local_unnamed_addr {
|
||||
; ATTRIBUTOR: define dereferenceable_or_null(4) i32* @test2(i32* dereferenceable_or_null(4) "no-capture-maybe-returned" %0, double* nonnull dereferenceable(8) "no-capture-maybe-returned" %1, i1 zeroext %2)
|
||||
; ATTRIBUTOR: define dereferenceable_or_null(4) i32* @test2(i32* readnone dereferenceable_or_null(4) "no-capture-maybe-returned" %0, double* nonnull readnone dereferenceable(8) "no-capture-maybe-returned" %1, i1 zeroext %2)
|
||||
%4 = bitcast double* %1 to i32*
|
||||
%5 = select i1 %2, i32* %0, i32* %4
|
||||
ret i32* %5
|
||||
|
@ -24,20 +24,20 @@ define i32* @test2(i32* dereferenceable_or_null(4) %0, double* dereferenceable(8
|
|||
; TEST 3
|
||||
; GEP inbounds
|
||||
define i32* @test3_1(i32* dereferenceable(8) %0) local_unnamed_addr {
|
||||
; ATTRIBUTOR: define nonnull dereferenceable(4) i32* @test3_1(i32* nonnull dereferenceable(8) "no-capture-maybe-returned" %0)
|
||||
; ATTRIBUTOR: define nonnull dereferenceable(4) i32* @test3_1(i32* nonnull readnone dereferenceable(8) "no-capture-maybe-returned" %0)
|
||||
%ret = getelementptr inbounds i32, i32* %0, i64 1
|
||||
ret i32* %ret
|
||||
}
|
||||
|
||||
define i32* @test3_2(i32* dereferenceable_or_null(32) %0) local_unnamed_addr {
|
||||
; FIXME: Argument should be mark dereferenceable because of GEP `inbounds`.
|
||||
; ATTRIBUTOR: define nonnull dereferenceable(16) i32* @test3_2(i32* dereferenceable_or_null(32) "no-capture-maybe-returned" %0)
|
||||
; ATTRIBUTOR: define nonnull dereferenceable(16) i32* @test3_2(i32* readnone dereferenceable_or_null(32) "no-capture-maybe-returned" %0)
|
||||
%ret = getelementptr inbounds i32, i32* %0, i64 4
|
||||
ret i32* %ret
|
||||
}
|
||||
|
||||
define i32* @test3_3(i32* dereferenceable(8) %0, i32* dereferenceable(16) %1, i1 %2) local_unnamed_addr {
|
||||
; ATTRIBUTOR: define nonnull dereferenceable(4) i32* @test3_3(i32* nonnull dereferenceable(8) "no-capture-maybe-returned" %0, i32* nonnull dereferenceable(16) "no-capture-maybe-returned" %1, i1 %2) local_unnamed_addr
|
||||
; ATTRIBUTOR: define nonnull dereferenceable(4) i32* @test3_3(i32* nonnull readnone dereferenceable(8) "no-capture-maybe-returned" %0, i32* nonnull readnone dereferenceable(16) "no-capture-maybe-returned" %1, i1 %2) local_unnamed_addr
|
||||
%ret1 = getelementptr inbounds i32, i32* %0, i64 1
|
||||
%ret2 = getelementptr inbounds i32, i32* %1, i64 2
|
||||
%ret = select i1 %2, i32* %ret1, i32* %ret2
|
||||
|
@ -48,7 +48,7 @@ define i32* @test3_3(i32* dereferenceable(8) %0, i32* dereferenceable(16) %1, i1
|
|||
; Better than known in IR.
|
||||
|
||||
define dereferenceable(4) i32* @test4(i32* dereferenceable(8) %0) local_unnamed_addr {
|
||||
; ATTRIBUTOR: define nonnull dereferenceable(8) i32* @test4(i32* nonnull returned dereferenceable(8) "no-capture-maybe-returned" %0)
|
||||
; ATTRIBUTOR: define nonnull dereferenceable(8) i32* @test4(i32* nonnull readnone returned dereferenceable(8) "no-capture-maybe-returned" %0)
|
||||
ret i32* %0
|
||||
}
|
||||
|
||||
|
|
|
@ -8,9 +8,7 @@ entry:
|
|||
ret i32 %add
|
||||
}
|
||||
|
||||
; FIXME: Should be something like this.
|
||||
; define internal i32 @noalias_args(i32* nocapture readonly %A, i32* noalias nocapture readonly %B)
|
||||
; CHECK: define internal i32 @noalias_args(i32* nocapture %A, i32* noalias nocapture %B)
|
||||
; CHECK: define internal i32 @noalias_args(i32* nocapture readonly %A, i32* noalias nocapture readonly %B)
|
||||
|
||||
define internal i32 @noalias_args(i32* %A, i32* %B) #0 {
|
||||
entry:
|
||||
|
@ -25,7 +23,7 @@ entry:
|
|||
|
||||
; FIXME: Should be something like this.
|
||||
; define internal i32 @noalias_args_argmem(i32* noalias nocapture readonly %A, i32* noalias nocapture readonly %B)
|
||||
; CHECK: define internal i32 @noalias_args_argmem(i32* nocapture %A, i32* nocapture %B)
|
||||
; CHECK: define internal i32 @noalias_args_argmem(i32* nocapture readonly %A, i32* nocapture readonly %B)
|
||||
;
|
||||
define internal i32 @noalias_args_argmem(i32* %A, i32* %B) #1 {
|
||||
entry:
|
||||
|
|
|
@ -39,8 +39,8 @@ define i32 @volatile_load(i32*) norecurse nounwind uwtable {
|
|||
ret i32 %2
|
||||
}
|
||||
|
||||
; CHECK: Function Attrs: nofree norecurse nosync nounwind uwtable willreturn
|
||||
; CHECK-NEXT: define internal i32 @internal_load(i32* nocapture nonnull %0)
|
||||
; CHECK: Function Attrs: nofree norecurse nosync nounwind readonly uwtable willreturn
|
||||
; CHECK-NEXT: define internal i32 @internal_load(i32* nocapture nonnull readonly %0)
|
||||
define internal i32 @internal_load(i32*) norecurse nounwind uwtable {
|
||||
%2 = load i32, i32* %0, align 4
|
||||
ret i32 %2
|
||||
|
@ -48,11 +48,11 @@ define internal i32 @internal_load(i32*) norecurse nounwind uwtable {
|
|||
; TEST 1: Only first block is live.
|
||||
|
||||
; CHECK: Function Attrs: nofree noreturn nosync nounwind
|
||||
; CHECK-NEXT: define i32 @first_block_no_return(i32 %a, i32* nocapture nonnull %ptr1, i32* nocapture %ptr2)
|
||||
; CHECK-NEXT: define i32 @first_block_no_return(i32 %a, i32* nocapture nonnull readonly %ptr1, i32* nocapture readnone %ptr2)
|
||||
define i32 @first_block_no_return(i32 %a, i32* nonnull %ptr1, i32* %ptr2) #0 {
|
||||
entry:
|
||||
call i32 @internal_load(i32* %ptr1)
|
||||
; CHECK: call i32 @internal_load(i32* nocapture nonnull %ptr1)
|
||||
; CHECK: call i32 @internal_load(i32* nocapture nonnull readonly %ptr1)
|
||||
call void @no_return_call()
|
||||
; CHECK: call void @no_return_call()
|
||||
; CHECK-NEXT: unreachable
|
||||
|
@ -84,7 +84,7 @@ cond.end: ; preds = %cond.false, %cond.t
|
|||
; dead block and check if it is deduced.
|
||||
|
||||
; CHECK: Function Attrs: nosync
|
||||
; CHECK-NEXT: define i32 @dead_block_present(i32 %a, i32* nocapture %ptr1)
|
||||
; CHECK-NEXT: define i32 @dead_block_present(i32 %a, i32* nocapture readnone %ptr1)
|
||||
define i32 @dead_block_present(i32 %a, i32* %ptr1) #0 {
|
||||
entry:
|
||||
%cmp = icmp eq i32 %a, 0
|
||||
|
@ -239,7 +239,7 @@ cleanup:
|
|||
; TEST 6: Undefined behvior, taken from LangRef.
|
||||
; FIXME: Should be able to detect undefined behavior.
|
||||
|
||||
; CHECK: define void @ub(i32* nocapture %0)
|
||||
; CHECK: define void @ub(i32* nocapture writeonly %0)
|
||||
define void @ub(i32* %0) {
|
||||
%poison = sub nuw i32 0, 1 ; Results in a poison value.
|
||||
%still_poison = and i32 %poison, 0 ; 0, but also poison.
|
||||
|
@ -660,7 +660,7 @@ define internal void @dead_e2() { ret void }
|
|||
; CHECK: define internal void @non_dead_d13()
|
||||
; CHECK: define internal void @non_dead_d14()
|
||||
; Verify we actually deduce information for these functions.
|
||||
; CHECK: Function Attrs: nofree nosync nounwind willreturn
|
||||
; CHECK: Function Attrs: nofree nosync nounwind readnone willreturn
|
||||
; CHECK-NEXT: define internal void @non_dead_d15()
|
||||
; CHECK-NOT: define internal void @dead_e
|
||||
|
||||
|
|
|
@ -153,7 +153,7 @@ define i8* @test8(i32* %0) nounwind uwtable {
|
|||
; TEST 9
|
||||
; Simple Argument Test
|
||||
define internal void @test9(i8* %a, i8* %b) {
|
||||
; CHECK: define internal void @test9(i8* noalias nocapture %a, i8* nocapture %b)
|
||||
; CHECK: define internal void @test9(i8* noalias nocapture readnone %a, i8* nocapture readnone %b)
|
||||
ret void
|
||||
}
|
||||
define void @test9_helper(i8* %a, i8* %b) {
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
@g = global i32* null ; <i32**> [#uses=1]
|
||||
|
||||
; FNATTR: define i32* @c1(i32* readnone returned %q)
|
||||
; ATTRIBUTOR: define i32* @c1(i32* returned "no-capture-maybe-returned" %q)
|
||||
; ATTRIBUTOR: define i32* @c1(i32* readnone returned "no-capture-maybe-returned" %q)
|
||||
define i32* @c1(i32* %q) {
|
||||
ret i32* %q
|
||||
}
|
||||
|
@ -24,7 +24,8 @@ define void @c3(i32* %q) {
|
|||
ret void
|
||||
}
|
||||
|
||||
; EITHER: define i1 @c4(i32* %q, i32 %bitno)
|
||||
; FNATTR: define i1 @c4(i32* %q, i32 %bitno)
|
||||
; ATTRIBUTOR: define i1 @c4(i32* readnone %q, i32 %bitno)
|
||||
define i1 @c4(i32* %q, i32 %bitno) {
|
||||
%tmp = ptrtoint i32* %q to i32
|
||||
%tmp2 = lshr i32 %tmp, %bitno
|
||||
|
@ -126,8 +127,7 @@ define void @nc3(void ()* %p) {
|
|||
}
|
||||
|
||||
declare void @external(i8*) readonly nounwind
|
||||
; FNATTR: define void @nc4(i8* nocapture readonly %p)
|
||||
; ATTRIBUTOR: define void @nc4(i8* nocapture %p)
|
||||
; EITHER: define void @nc4(i8* nocapture readonly %p)
|
||||
define void @nc4(i8* %p) {
|
||||
call void @external(i8* %p)
|
||||
ret void
|
||||
|
@ -141,7 +141,7 @@ define void @nc5(void (i8*)* %f, i8* %p) {
|
|||
}
|
||||
|
||||
; FNATTR: define void @test1_1(i8* nocapture readnone %x1_1, i8* %y1_1, i1 %c)
|
||||
; ATTRIBUTOR: define void @test1_1(i8* nocapture %x1_1, i8* nocapture %y1_1, i1 %c)
|
||||
; ATTRIBUTOR: define void @test1_1(i8* nocapture readnone %x1_1, i8* nocapture readnone %y1_1, i1 %c)
|
||||
; It would be acceptable to add readnone to %y1_1 and %y1_2.
|
||||
define void @test1_1(i8* %x1_1, i8* %y1_1, i1 %c) {
|
||||
call i8* @test1_2(i8* %x1_1, i8* %y1_1, i1 %c)
|
||||
|
@ -150,7 +150,7 @@ define void @test1_1(i8* %x1_1, i8* %y1_1, i1 %c) {
|
|||
}
|
||||
|
||||
; FNATTR: define i8* @test1_2(i8* nocapture readnone %x1_2, i8* returned %y1_2, i1 %c)
|
||||
; ATTRIBUTOR: define i8* @test1_2(i8* nocapture %x1_2, i8* returned "no-capture-maybe-returned" %y1_2, i1 %c)
|
||||
; ATTRIBUTOR: define i8* @test1_2(i8* nocapture readnone %x1_2, i8* readnone returned "no-capture-maybe-returned" %y1_2, i1 %c)
|
||||
define i8* @test1_2(i8* %x1_2, i8* %y1_2, i1 %c) {
|
||||
br i1 %c, label %t, label %f
|
||||
t:
|
||||
|
@ -161,16 +161,14 @@ f:
|
|||
ret i8* %y1_2
|
||||
}
|
||||
|
||||
; FNATTR: define void @test2(i8* nocapture readnone %x2)
|
||||
; ATTRIBUTOR: define void @test2(i8* nocapture %x2)
|
||||
; EITHER: define void @test2(i8* nocapture readnone %x2)
|
||||
define void @test2(i8* %x2) {
|
||||
call void @test2(i8* %x2)
|
||||
store i32* null, i32** @g
|
||||
ret void
|
||||
}
|
||||
|
||||
; FNATTR: define void @test3(i8* nocapture readnone %x3, i8* nocapture readnone %y3, i8* nocapture readnone %z3)
|
||||
; ATTRIBUTOR: define void @test3(i8* nocapture %x3, i8* nocapture %y3, i8* nocapture %z3)
|
||||
; EITHER: define void @test3(i8* nocapture readnone %x3, i8* nocapture readnone %y3, i8* nocapture readnone %z3)
|
||||
define void @test3(i8* %x3, i8* %y3, i8* %z3) {
|
||||
call void @test3(i8* %z3, i8* %y3, i8* %x3)
|
||||
store i32* null, i32** @g
|
||||
|
@ -178,7 +176,7 @@ define void @test3(i8* %x3, i8* %y3, i8* %z3) {
|
|||
}
|
||||
|
||||
; FNATTR: define void @test4_1(i8* %x4_1, i1 %c)
|
||||
; ATTRIBUTOR: define void @test4_1(i8* nocapture %x4_1, i1 %c)
|
||||
; ATTRIBUTOR: define void @test4_1(i8* nocapture readnone %x4_1, i1 %c)
|
||||
define void @test4_1(i8* %x4_1, i1 %c) {
|
||||
call i8* @test4_2(i8* %x4_1, i8* %x4_1, i8* %x4_1, i1 %c)
|
||||
store i32* null, i32** @g
|
||||
|
@ -186,7 +184,7 @@ define void @test4_1(i8* %x4_1, i1 %c) {
|
|||
}
|
||||
|
||||
; FNATTR: define i8* @test4_2(i8* nocapture readnone %x4_2, i8* readnone returned %y4_2, i8* nocapture readnone %z4_2, i1 %c)
|
||||
; ATTRIBUTOR: define i8* @test4_2(i8* nocapture %x4_2, i8* returned "no-capture-maybe-returned" %y4_2, i8* nocapture %z4_2, i1 %c)
|
||||
; ATTRIBUTOR: define i8* @test4_2(i8* nocapture readnone %x4_2, i8* readnone returned "no-capture-maybe-returned" %y4_2, i8* nocapture readnone %z4_2, i1 %c)
|
||||
define i8* @test4_2(i8* %x4_2, i8* %y4_2, i8* %z4_2, i1 %c) {
|
||||
br i1 %c, label %t, label %f
|
||||
t:
|
||||
|
@ -257,7 +255,8 @@ define void @captureLaunder(i8* %p) {
|
|||
ret void
|
||||
}
|
||||
|
||||
; EITHER: @nocaptureStrip(i8* nocapture %p)
|
||||
; FNATTR: @nocaptureStrip(i8* nocapture %p)
|
||||
; ATTRIBUTOR: @nocaptureStrip(i8* nocapture writeonly %p)
|
||||
define void @nocaptureStrip(i8* %p) {
|
||||
entry:
|
||||
%b = call i8* @llvm.strip.invariant.group.p0i8(i8* %p)
|
||||
|
@ -273,22 +272,19 @@ define void @captureStrip(i8* %p) {
|
|||
ret void
|
||||
}
|
||||
|
||||
; FNATTR: define i1 @captureICmp(i32* readnone %x)
|
||||
; ATTRIBUTOR: define i1 @captureICmp(i32* %x)
|
||||
; EITHER: define i1 @captureICmp(i32* readnone %x)
|
||||
define i1 @captureICmp(i32* %x) {
|
||||
%1 = icmp eq i32* %x, null
|
||||
ret i1 %1
|
||||
}
|
||||
|
||||
; FNATTR: define i1 @captureICmpRev(i32* readnone %x)
|
||||
; ATTRIBUTOR: define i1 @captureICmpRev(i32* %x)
|
||||
; EITHER: define i1 @captureICmpRev(i32* readnone %x)
|
||||
define i1 @captureICmpRev(i32* %x) {
|
||||
%1 = icmp eq i32* null, %x
|
||||
ret i1 %1
|
||||
}
|
||||
|
||||
; FNATTR: define i1 @nocaptureInboundsGEPICmp(i32* nocapture readnone %x)
|
||||
; ATTRIBUTOR: define i1 @nocaptureInboundsGEPICmp(i32* nocapture %x)
|
||||
; EITHER: define i1 @nocaptureInboundsGEPICmp(i32* nocapture readnone %x)
|
||||
define i1 @nocaptureInboundsGEPICmp(i32* %x) {
|
||||
%1 = getelementptr inbounds i32, i32* %x, i32 5
|
||||
%2 = bitcast i32* %1 to i8*
|
||||
|
@ -296,8 +292,7 @@ define i1 @nocaptureInboundsGEPICmp(i32* %x) {
|
|||
ret i1 %3
|
||||
}
|
||||
|
||||
; FNATTR: define i1 @nocaptureInboundsGEPICmpRev(i32* nocapture readnone %x)
|
||||
; ATTRIBUTOR: define i1 @nocaptureInboundsGEPICmpRev(i32* nocapture %x)
|
||||
; EITHER: define i1 @nocaptureInboundsGEPICmpRev(i32* nocapture readnone %x)
|
||||
define i1 @nocaptureInboundsGEPICmpRev(i32* %x) {
|
||||
%1 = getelementptr inbounds i32, i32* %x, i32 5
|
||||
%2 = bitcast i32* %1 to i8*
|
||||
|
@ -305,16 +300,14 @@ define i1 @nocaptureInboundsGEPICmpRev(i32* %x) {
|
|||
ret i1 %3
|
||||
}
|
||||
|
||||
; FNATTR: define i1 @nocaptureDereferenceableOrNullICmp(i32* nocapture readnone dereferenceable_or_null(4) %x)
|
||||
; ATTRIBUTOR: define i1 @nocaptureDereferenceableOrNullICmp(i32* nocapture dereferenceable_or_null(4) %x)
|
||||
; EITHER: define i1 @nocaptureDereferenceableOrNullICmp(i32* nocapture readnone dereferenceable_or_null(4) %x)
|
||||
define i1 @nocaptureDereferenceableOrNullICmp(i32* dereferenceable_or_null(4) %x) {
|
||||
%1 = bitcast i32* %x to i8*
|
||||
%2 = icmp eq i8* %1, null
|
||||
ret i1 %2
|
||||
}
|
||||
|
||||
; FNATTR: define i1 @captureDereferenceableOrNullICmp(i32* readnone dereferenceable_or_null(4) %x)
|
||||
; ATTRIBUTOR: define i1 @captureDereferenceableOrNullICmp(i32* dereferenceable_or_null(4) %x)
|
||||
; EITHER: define i1 @captureDereferenceableOrNullICmp(i32* readnone dereferenceable_or_null(4) %x)
|
||||
define i1 @captureDereferenceableOrNullICmp(i32* dereferenceable_or_null(4) %x) "null-pointer-is-valid"="true" {
|
||||
%1 = bitcast i32* %x to i8*
|
||||
%2 = icmp eq i8* %1, null
|
||||
|
|
|
@ -15,7 +15,7 @@ declare void @_ZdaPv(i8*) local_unnamed_addr #2
|
|||
; TEST 1 (positive case)
|
||||
; FNATTR: Function Attrs: noinline norecurse nounwind readnone uwtable
|
||||
; FNATTR-NEXT: define void @only_return()
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind uwtable
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind readnone uwtable
|
||||
; ATTRIBUTOR-NEXT: define void @only_return()
|
||||
define void @only_return() #0 {
|
||||
ret void
|
||||
|
@ -92,7 +92,7 @@ end:
|
|||
|
||||
; FNATTR: Function Attrs: noinline nounwind readnone uwtable
|
||||
; FNATTR-NEXT: define void @mutual_recursion1()
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline noreturn nosync nounwind uwtable
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline noreturn nosync nounwind readnone uwtable
|
||||
; ATTRIBUTOR-NEXT: define void @mutual_recursion1()
|
||||
define void @mutual_recursion1() #0 {
|
||||
call void @mutual_recursion2()
|
||||
|
@ -101,7 +101,7 @@ define void @mutual_recursion1() #0 {
|
|||
|
||||
; FNATTR: Function Attrs: noinline nounwind readnone uwtable
|
||||
; FNATTR-NEXT: define void @mutual_recursion2()
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline noreturn nosync nounwind uwtable
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline noreturn nosync nounwind readnone uwtable
|
||||
; ATTRIBUTOR-NEXT: define void @mutual_recursion2()
|
||||
define void @mutual_recursion2() #0 {
|
||||
call void @mutual_recursion1()
|
||||
|
@ -158,7 +158,7 @@ declare void @nofree_function() nofree readnone #0
|
|||
|
||||
; FNATTR: Function Attrs: noinline nounwind readnone uwtable
|
||||
; FNATTR-NEXT: define void @call_nofree_function()
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind uwtable
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind readnone uwtable
|
||||
; ATTRIBUTOR-NEXT: define void @call_nofree_function()
|
||||
define void @call_nofree_function() #0 {
|
||||
tail call void @nofree_function()
|
||||
|
@ -211,7 +211,7 @@ declare float @llvm.floor.f32(float)
|
|||
; FNATTRS: Function Attrs: noinline nounwind uwtable
|
||||
; FNATTRS-NEXT: define void @call_floor(float %a)
|
||||
; FIXME: missing nofree
|
||||
; ATTRIBUTOR: Function Attrs: noinline nosync nounwind uwtable
|
||||
; ATTRIBUTOR: Function Attrs: noinline nosync nounwind readnone uwtable
|
||||
; ATTRIBUTOR-NEXT: define void @call_floor(float %a)
|
||||
|
||||
define void @call_floor(float %a) #0 {
|
||||
|
@ -224,7 +224,7 @@ define void @call_floor(float %a) #0 {
|
|||
|
||||
; FNATTRS: Function Attrs: noinline nounwind uwtable
|
||||
; FNATTRS-NEXT: define void @f1()
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind uwtable
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind readnone uwtable
|
||||
; ATTRIBUTOR-NEXT: define void @f1()
|
||||
define void @f1() #0 {
|
||||
tail call void @nofree_function()
|
||||
|
@ -233,7 +233,7 @@ define void @f1() #0 {
|
|||
|
||||
; FNATTRS: Function Attrs: noinline nounwind uwtable
|
||||
; FNATTRS-NEXT: define void @f2()
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind uwtable
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind readnone uwtable
|
||||
; ATTRIBUTOR-NEXT: define void @f2()
|
||||
define void @f2() #0 {
|
||||
tail call void @f1()
|
||||
|
|
|
@ -159,7 +159,7 @@ define void @test13_helper() {
|
|||
ret void
|
||||
}
|
||||
define internal void @test13(i8* %a, i8* %b, i8* %c) {
|
||||
; ATTRIBUTOR: define internal void @test13(i8* nocapture nonnull %a, i8* nocapture %b, i8* nocapture %c)
|
||||
; ATTRIBUTOR: define internal void @test13(i8* nocapture nonnull readnone %a, i8* nocapture readnone %b, i8* nocapture readnone %c)
|
||||
ret void
|
||||
}
|
||||
|
||||
|
@ -178,8 +178,8 @@ declare nonnull i8* @nonnull()
|
|||
|
||||
|
||||
define internal i32* @f1(i32* %arg) {
|
||||
; FIXME: missing nonnull It should be nonnull @f1(i32* nonnull %arg)
|
||||
; ATTRIBUTOR: define internal nonnull i32* @f1(i32* %arg)
|
||||
; FIXME: missing nonnull It should be nonnull @f1(i32* nonnull readonly %arg)
|
||||
; ATTRIBUTOR: define internal nonnull i32* @f1(i32* readonly %arg)
|
||||
|
||||
bb:
|
||||
%tmp = icmp eq i32* %arg, null
|
||||
|
@ -212,18 +212,18 @@ define internal i32* @f2(i32* %arg) {
|
|||
; ATTRIBUTOR: define internal nonnull i32* @f2(i32* %arg)
|
||||
bb:
|
||||
|
||||
; FIXME: missing nonnull. It should be @f1(i32* nonnull %arg)
|
||||
; ATTRIBUTOR: %tmp = tail call nonnull i32* @f1(i32* %arg)
|
||||
; FIXME: missing nonnull. It should be @f1(i32* nonnull readonly %arg)
|
||||
; ATTRIBUTOR: %tmp = tail call nonnull i32* @f1(i32* readonly %arg)
|
||||
%tmp = tail call i32* @f1(i32* %arg)
|
||||
ret i32* %tmp
|
||||
}
|
||||
|
||||
define dso_local noalias i32* @f3(i32* %arg) {
|
||||
; FIXME: missing nonnull. It should be nonnull @f3(i32* nonnull %arg)
|
||||
; ATTRIBUTOR: define dso_local noalias i32* @f3(i32* %arg)
|
||||
; FIXME: missing nonnull. It should be nonnull @f3(i32* nonnull readonly %arg)
|
||||
; ATTRIBUTOR: define dso_local noalias i32* @f3(i32* readonly %arg)
|
||||
bb:
|
||||
; FIXME: missing nonnull. It should be @f1(i32* nonnull %arg)
|
||||
; ATTRIBUTOR: %tmp = call i32* @f1(i32* %arg)
|
||||
; FIXME: missing nonnull. It should be @f1(i32* nonnull readonly %arg)
|
||||
; ATTRIBUTOR: %tmp = call i32* @f1(i32* readonly %arg)
|
||||
%tmp = call i32* @f1(i32* %arg)
|
||||
ret i32* null
|
||||
}
|
||||
|
@ -402,7 +402,7 @@ declare i32 @esfp(...)
|
|||
define i1 @parent8(i8* %a, i8* %bogus1, i8* %b) personality i8* bitcast (i32 (...)* @esfp to i8*){
|
||||
; FNATTR-LABEL: @parent8(i8* nonnull %a, i8* nocapture readnone %bogus1, i8* nonnull %b)
|
||||
; FIXME : missing "nonnull", it should be @parent8(i8* nonnull %a, i8* %bogus1, i8* nonnull %b)
|
||||
; ATTRIBUTOR-LABEL: @parent8(i8* %a, i8* nocapture %bogus1, i8* %b)
|
||||
; ATTRIBUTOR-LABEL: @parent8(i8* %a, i8* nocapture readnone %bogus1, i8* %b)
|
||||
; BOTH-NEXT: entry:
|
||||
; FNATTR-NEXT: invoke void @use2nonnull(i8* %a, i8* %b)
|
||||
; ATTRIBUTOR-NEXT: invoke void @use2nonnull(i8* nonnull %a, i8* nonnull %b)
|
||||
|
@ -458,7 +458,7 @@ define i32* @g1() {
|
|||
ret i32* %c
|
||||
}
|
||||
|
||||
; ATTRIBUTOR: define internal void @called_by_weak(i32* nocapture nonnull %a)
|
||||
; ATTRIBUTOR: define internal void @called_by_weak(i32* nocapture nonnull readnone %a)
|
||||
define internal void @called_by_weak(i32* %a) {
|
||||
ret void
|
||||
}
|
||||
|
|
|
@ -4,14 +4,14 @@
|
|||
|
||||
; CHECK: Function Attrs
|
||||
; CHECK-SAME: norecurse nounwind readnone
|
||||
; ATTRIBUTOR: Function Attrs: nofree norecurse nosync nounwind willreturn
|
||||
; ATTRIBUTOR: Function Attrs: nofree norecurse nosync nounwind readnone willreturn
|
||||
; BOTH-NEXT: define i32 @leaf()
|
||||
define i32 @leaf() {
|
||||
ret i32 1
|
||||
}
|
||||
|
||||
; BOTH: Function Attrs
|
||||
; CHECK-SAME: readnone
|
||||
; BOTH-SAME: readnone
|
||||
; BOTH-NOT: norecurse
|
||||
; BOTH-NEXT: define i32 @self_rec()
|
||||
define i32 @self_rec() {
|
||||
|
@ -20,7 +20,7 @@ define i32 @self_rec() {
|
|||
}
|
||||
|
||||
; BOTH: Function Attrs
|
||||
; CHECK-SAME: readnone
|
||||
; BOTH-SAME: readnone
|
||||
; BOTH-NOT: norecurse
|
||||
; BOTH-NEXT: define i32 @indirect_rec()
|
||||
define i32 @indirect_rec() {
|
||||
|
@ -28,7 +28,7 @@ define i32 @indirect_rec() {
|
|||
ret i32 %a
|
||||
}
|
||||
; BOTH: Function Attrs
|
||||
; CHECK-SAME: readnone
|
||||
; BOTH-SAME: readnone
|
||||
; BOTH-NOT: norecurse
|
||||
; BOTH-NEXT: define i32 @indirect_rec2()
|
||||
define i32 @indirect_rec2() {
|
||||
|
@ -37,7 +37,7 @@ define i32 @indirect_rec2() {
|
|||
}
|
||||
|
||||
; BOTH: Function Attrs
|
||||
; CHECK-SAME: readnone
|
||||
; BOTH-SAME: readnone
|
||||
; BOTH-NOT: norecurse
|
||||
; BOTH-NEXT: define i32 @extern()
|
||||
define i32 @extern() {
|
||||
|
@ -53,7 +53,7 @@ declare i32 @k() readnone
|
|||
; CHECK-SAME: nounwind
|
||||
; BOTH-NOT: norecurse
|
||||
; CHECK-NEXT: define void @intrinsic(i8* nocapture %dest, i8* nocapture readonly %src, i32 %len)
|
||||
; ATTRIBUTOR-NEXT: define void @intrinsic(i8* nocapture %dest, i8* nocapture %src, i32 %len)
|
||||
; ATTRIBUTOR-NEXT: define void @intrinsic(i8* nocapture writeonly %dest, i8* nocapture readonly %src, i32 %len)
|
||||
define void @intrinsic(i8* %dest, i8* %src, i32 %len) {
|
||||
call void @llvm.memcpy.p0i8.p0i8.i32(i8* %dest, i8* %src, i32 %len, i1 false)
|
||||
ret void
|
||||
|
@ -66,7 +66,7 @@ declare void @llvm.memcpy.p0i8.p0i8.i32(i8*, i8*, i32, i1)
|
|||
; BOTH: Function Attrs
|
||||
; CHECK-SAME: norecurse readnone
|
||||
; FIXME: missing "norecurse"
|
||||
; ATTRIBUTOR-SAME: nosync
|
||||
; ATTRIBUTOR-SAME: nosync readnone
|
||||
; CHECK-NEXT: define internal i32 @called_by_norecurse()
|
||||
define internal i32 @called_by_norecurse() {
|
||||
%a = call i32 @k()
|
||||
|
@ -138,7 +138,7 @@ define i32 @eval_func(i32 (i32)* , i32) local_unnamed_addr {
|
|||
|
||||
declare void @unknown()
|
||||
; Call an unknown function in a dead block.
|
||||
; ATTRIBUTOR: Function Attrs: nofree norecurse nosync nounwind willreturn
|
||||
; ATTRIBUTOR: Function Attrs: nofree norecurse nosync nounwind readnone willreturn
|
||||
; ATTRIBUTOR: define i32 @call_unknown_in_dead_block()
|
||||
define i32 @call_unknown_in_dead_block() local_unnamed_addr {
|
||||
ret i32 0
|
||||
|
|
|
@ -28,7 +28,7 @@ target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
|
|||
; FNATTR: Function Attrs: norecurse nounwind optsize readnone ssp uwtable
|
||||
; FNATTR-NEXT: define nonnull i32* @foo(%struct.ST* readnone %s)
|
||||
; ATTRIBUTOR: Function Attrs: nofree nosync nounwind optsize readnone ssp uwtable
|
||||
; ATTRIBUTOR-NEXT: define nonnull i32* @foo(%struct.ST* "no-capture-maybe-returned" %s)
|
||||
; ATTRIBUTOR-NEXT: define nonnull i32* @foo(%struct.ST* readnone "no-capture-maybe-returned" %s)
|
||||
define i32* @foo(%struct.ST* %s) nounwind uwtable readnone optsize ssp {
|
||||
entry:
|
||||
%arrayidx = getelementptr inbounds %struct.ST, %struct.ST* %s, i64 1, i32 2, i32 1, i64 5, i64 13
|
||||
|
@ -61,7 +61,7 @@ define i32 @load_monotonic(i32* nocapture readonly %0) norecurse nounwind uwtabl
|
|||
; FNATTR: Function Attrs: nofree norecurse nounwind uwtable
|
||||
; FNATTR-NEXT: define void @store_monotonic(i32* nocapture %0)
|
||||
; ATTRIBUTOR: Function Attrs: nofree norecurse nosync nounwind uwtable
|
||||
; ATTRIBUTOR-NEXT: define void @store_monotonic(i32* nocapture %0)
|
||||
; ATTRIBUTOR-NEXT: define void @store_monotonic(i32* nocapture writeonly %0)
|
||||
define void @store_monotonic(i32* nocapture %0) norecurse nounwind uwtable {
|
||||
store atomic i32 10, i32* %0 monotonic, align 4
|
||||
ret void
|
||||
|
@ -94,7 +94,7 @@ define i32 @load_acquire(i32* nocapture readonly %0) norecurse nounwind uwtable
|
|||
; FNATTR-NEXT: define void @load_release(i32* nocapture %0)
|
||||
; ATTRIBUTOR: Function Attrs: nofree norecurse nounwind uwtable
|
||||
; ATTRIBUTOR-NOT: nosync
|
||||
; ATTRIBUTOR-NEXT: define void @load_release(i32* nocapture %0)
|
||||
; ATTRIBUTOR-NEXT: define void @load_release(i32* nocapture writeonly %0)
|
||||
define void @load_release(i32* nocapture %0) norecurse nounwind uwtable {
|
||||
store atomic volatile i32 10, i32* %0 release, align 4
|
||||
ret void
|
||||
|
@ -106,7 +106,7 @@ define void @load_release(i32* nocapture %0) norecurse nounwind uwtable {
|
|||
; FNATTR-NEXT: define void @load_volatile_release(i32* nocapture %0)
|
||||
; ATTRIBUTOR: Function Attrs: nofree norecurse nounwind uwtable
|
||||
; ATTRIBUTOR-NOT: nosync
|
||||
; ATTRIBUTOR-NEXT: define void @load_volatile_release(i32* nocapture %0)
|
||||
; ATTRIBUTOR-NEXT: define void @load_volatile_release(i32* nocapture writeonly %0)
|
||||
define void @load_volatile_release(i32* nocapture %0) norecurse nounwind uwtable {
|
||||
store atomic volatile i32 10, i32* %0 release, align 4
|
||||
ret void
|
||||
|
@ -185,8 +185,8 @@ define void @call_might_sync() nounwind uwtable noinline {
|
|||
|
||||
; FNATTR: Function Attrs: nofree noinline nounwind uwtable
|
||||
; FNATTR-NEXT: define i32 @scc1(i32* %0)
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline noreturn nosync nounwind uwtable
|
||||
; ATTRIBUTOR-NEXT: define i32 @scc1(i32* nocapture %0)
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline noreturn nosync nounwind readnone uwtable
|
||||
; ATTRIBUTOR-NEXT: define i32 @scc1(i32* nocapture readnone %0)
|
||||
define i32 @scc1(i32* %0) noinline nounwind uwtable {
|
||||
tail call void @scc2(i32* %0);
|
||||
%val = tail call i32 @volatile_load(i32* %0);
|
||||
|
@ -195,8 +195,8 @@ define i32 @scc1(i32* %0) noinline nounwind uwtable {
|
|||
|
||||
; FNATTR: Function Attrs: nofree noinline nounwind uwtable
|
||||
; FNATTR-NEXT: define void @scc2(i32* %0)
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline noreturn nosync nounwind uwtable
|
||||
; ATTRIBUTOR-NEXT: define void @scc2(i32* nocapture %0)
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline noreturn nosync nounwind readnone uwtable
|
||||
; ATTRIBUTOR-NEXT: define void @scc2(i32* nocapture readnone %0)
|
||||
define void @scc2(i32* %0) noinline nounwind uwtable {
|
||||
tail call i32 @scc1(i32* %0);
|
||||
ret void;
|
||||
|
@ -224,7 +224,7 @@ define void @scc2(i32* %0) noinline nounwind uwtable {
|
|||
; FNATTR: Function Attrs: nofree norecurse nounwind
|
||||
; FNATTR-NEXT: define void @foo1(i32* nocapture %0, %"struct.std::atomic"* nocapture %1)
|
||||
; ATTRIBUTOR-NOT: nosync
|
||||
; ATTRIBUTOR: define void @foo1(i32* nocapture %0, %"struct.std::atomic"* nocapture %1)
|
||||
; ATTRIBUTOR: define void @foo1(i32* nocapture writeonly %0, %"struct.std::atomic"* nocapture writeonly %1)
|
||||
define void @foo1(i32* %0, %"struct.std::atomic"* %1) {
|
||||
store i32 100, i32* %0, align 4
|
||||
fence release
|
||||
|
@ -236,7 +236,7 @@ define void @foo1(i32* %0, %"struct.std::atomic"* %1) {
|
|||
; FNATTR: Function Attrs: nofree norecurse nounwind
|
||||
; FNATTR-NEXT: define void @bar(i32* nocapture readnone %0, %"struct.std::atomic"* nocapture readonly %1)
|
||||
; ATTRIBUTOR-NOT: nosync
|
||||
; ATTRIBUTOR: define void @bar(i32* nocapture %0, %"struct.std::atomic"* nocapture %1)
|
||||
; ATTRIBUTOR: define void @bar(i32* nocapture readnone %0, %"struct.std::atomic"* nocapture readonly %1)
|
||||
define void @bar(i32* %0, %"struct.std::atomic"* %1) {
|
||||
%3 = getelementptr inbounds %"struct.std::atomic", %"struct.std::atomic"* %1, i64 0, i32 0, i32 0
|
||||
br label %4
|
||||
|
@ -256,7 +256,7 @@ define void @bar(i32* %0, %"struct.std::atomic"* %1) {
|
|||
; FNATTR: Function Attrs: nofree norecurse nounwind
|
||||
; FNATTR-NEXT: define void @foo1_singlethread(i32* nocapture %0, %"struct.std::atomic"* nocapture %1)
|
||||
; ATTRIBUTOR: Function Attrs: nofree nosync
|
||||
; ATTRIBUTOR: define void @foo1_singlethread(i32* nocapture %0, %"struct.std::atomic"* nocapture %1)
|
||||
; ATTRIBUTOR: define void @foo1_singlethread(i32* nocapture writeonly %0, %"struct.std::atomic"* nocapture writeonly %1)
|
||||
define void @foo1_singlethread(i32* %0, %"struct.std::atomic"* %1) {
|
||||
store i32 100, i32* %0, align 4
|
||||
fence syncscope("singlethread") release
|
||||
|
@ -268,7 +268,7 @@ define void @foo1_singlethread(i32* %0, %"struct.std::atomic"* %1) {
|
|||
; FNATTR: Function Attrs: nofree norecurse nounwind
|
||||
; FNATTR-NEXT: define void @bar_singlethread(i32* nocapture readnone %0, %"struct.std::atomic"* nocapture readonly %1)
|
||||
; ATTRIBUTOR: Function Attrs: nofree nosync
|
||||
; ATTRIBUTOR: define void @bar_singlethread(i32* nocapture %0, %"struct.std::atomic"* nocapture %1)
|
||||
; ATTRIBUTOR: define void @bar_singlethread(i32* nocapture readnone %0, %"struct.std::atomic"* nocapture readonly %1)
|
||||
define void @bar_singlethread(i32* %0, %"struct.std::atomic"* %1) {
|
||||
%3 = getelementptr inbounds %"struct.std::atomic", %"struct.std::atomic"* %1, i64 0, i32 0, i32 0
|
||||
br label %4
|
||||
|
@ -293,7 +293,7 @@ declare void @llvm.memset(i8* %dest, i8 %val, i32 %len, i1 %isvolatile)
|
|||
;
|
||||
; ATTRIBUTOR: Function Attrs: nounwind
|
||||
; ATTRIBUTOR-NOT: nosync
|
||||
; ATTRIBUTOR-NEXT: define i32 @memcpy_volatile(i8* nocapture %ptr1, i8* nocapture %ptr2)
|
||||
; ATTRIBUTOR-NEXT: define i32 @memcpy_volatile(i8* nocapture writeonly %ptr1, i8* nocapture readonly %ptr2)
|
||||
define i32 @memcpy_volatile(i8* %ptr1, i8* %ptr2) {
|
||||
call void @llvm.memcpy(i8* %ptr1, i8* %ptr2, i32 8, i1 1)
|
||||
ret i32 4
|
||||
|
@ -304,7 +304,7 @@ define i32 @memcpy_volatile(i8* %ptr1, i8* %ptr2) {
|
|||
; It is odd to add nocapture but a result of the llvm.memset nocapture.
|
||||
;
|
||||
; ATTRIBUTOR: Function Attrs: nosync
|
||||
; ATTRIBUTOR-NEXT: define i32 @memset_non_volatile(i8* nocapture %ptr1, i8 %val)
|
||||
; ATTRIBUTOR-NEXT: define i32 @memset_non_volatile(i8* nocapture writeonly %ptr1, i8 %val)
|
||||
define i32 @memset_non_volatile(i8* %ptr1, i8 %val) {
|
||||
call void @llvm.memset(i8* %ptr1, i8 %val, i32 8, i1 0)
|
||||
ret i32 4
|
||||
|
|
|
@ -102,7 +102,7 @@ return: ; preds = %if.end, %if.then
|
|||
}
|
||||
|
||||
; CHECK: Function Attrs: nofree norecurse nosync nounwind
|
||||
; CHECK-NEXT: define i32* @external_sink_ret2_nrw(i32* readnone %n0, i32* nocapture readonly %r0, i32* returned "no-capture-maybe-returned" %w0)
|
||||
; CHECK-NEXT: define i32* @external_sink_ret2_nrw(i32* readnone %n0, i32* nocapture readonly %r0, i32* returned writeonly "no-capture-maybe-returned" %w0)
|
||||
define i32* @external_sink_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) {
|
||||
entry:
|
||||
%tobool = icmp ne i32* %n0, null
|
||||
|
|
|
@ -1,19 +1,24 @@
|
|||
; RUN: opt < %s -functionattrs -S | FileCheck %s
|
||||
; RUN: opt < %s -aa-pipeline=basic-aa -passes='cgscc(function-attrs)' -S | FileCheck %s
|
||||
; RUN: opt < %s -functionattrs -S | FileCheck %s --check-prefixes=CHECK,FNATTR
|
||||
; RUN: opt < %s -aa-pipeline=basic-aa -passes='cgscc(function-attrs)' -S | FileCheck %s --check-prefixes=CHECK,FNATTR
|
||||
; RUN: opt < %s -attributor -attributor-disable=false -S | FileCheck %s --check-prefixes=CHECK,ATTRIBUTOR
|
||||
; RUN: opt < %s -aa-pipeline=basic-aa -passes='attributor' -attributor-disable=false -S | FileCheck %s --check-prefixes=CHECK,ATTRIBUTOR
|
||||
|
||||
@x = global i32 0
|
||||
|
||||
declare void @test1_1(i8* %x1_1, i8* readonly %y1_1, ...)
|
||||
|
||||
; NOTE: readonly for %y1_2 would be OK here but not for the similar situation in test13.
|
||||
;
|
||||
; CHECK: define void @test1_2(i8* %x1_2, i8* readonly %y1_2, i8* %z1_2)
|
||||
; FNATTR: define void @test1_2(i8* %x1_2, i8* readonly %y1_2, i8* %z1_2)
|
||||
; ATTRIBUTOR: define void @test1_2(i8* %x1_2, i8* %y1_2, i8* %z1_2)
|
||||
define void @test1_2(i8* %x1_2, i8* %y1_2, i8* %z1_2) {
|
||||
call void (i8*, i8*, ...) @test1_1(i8* %x1_2, i8* %y1_2, i8* %z1_2)
|
||||
store i32 0, i32* @x
|
||||
ret void
|
||||
}
|
||||
|
||||
; CHECK: define i8* @test2(i8* readnone returned %p)
|
||||
; FNATTR: define i8* @test2(i8* readnone returned %p)
|
||||
; ATTRIBUTOR: define i8* @test2(i8* readnone returned %p)
|
||||
define i8* @test2(i8* %p) {
|
||||
store i32 0, i32* @x
|
||||
ret i8* %p
|
||||
|
@ -33,7 +38,8 @@ define void @test4_2(i8* %p) {
|
|||
ret void
|
||||
}
|
||||
|
||||
; CHECK: define void @test5(i8** nocapture %p, i8* %q)
|
||||
; FNATTR: define void @test5(i8** nocapture %p, i8* %q)
|
||||
; ATTRIBUTOR: define void @test5(i8** nocapture writeonly %p, i8* %q)
|
||||
; Missed optz'n: we could make %q readnone, but don't break test6!
|
||||
define void @test5(i8** %p, i8* %q) {
|
||||
store i8* %q, i8** %p
|
||||
|
@ -41,7 +47,8 @@ define void @test5(i8** %p, i8* %q) {
|
|||
}
|
||||
|
||||
declare void @test6_1()
|
||||
; CHECK: define void @test6_2(i8** nocapture %p, i8* %q)
|
||||
; FNATTR: define void @test6_2(i8** nocapture %p, i8* %q)
|
||||
; ATTRIBUTOR: define void @test6_2(i8** nocapture writeonly %p, i8* %q)
|
||||
; This is not a missed optz'n.
|
||||
define void @test6_2(i8** %p, i8* %q) {
|
||||
store i8* %q, i8** %p
|
||||
|
@ -49,19 +56,22 @@ define void @test6_2(i8** %p, i8* %q) {
|
|||
ret void
|
||||
}
|
||||
|
||||
; CHECK: define void @test7_1(i32* inalloca nocapture %a)
|
||||
; FNATTR: define void @test7_1(i32* inalloca nocapture %a)
|
||||
; ATTRIBUTOR: define void @test7_1(i32* inalloca nocapture writeonly %a)
|
||||
; inalloca parameters are always considered written
|
||||
define void @test7_1(i32* inalloca %a) {
|
||||
ret void
|
||||
}
|
||||
|
||||
; CHECK: define i32* @test8_1(i32* readnone returned %p)
|
||||
; FNATTR: define i32* @test8_1(i32* readnone returned %p)
|
||||
; ATTRIBUTOR: define i32* @test8_1(i32* readnone returned %p)
|
||||
define i32* @test8_1(i32* %p) {
|
||||
entry:
|
||||
ret i32* %p
|
||||
}
|
||||
|
||||
; CHECK: define void @test8_2(i32* %p)
|
||||
; FNATTR: define void @test8_2(i32* %p)
|
||||
; ATTRIBUTOR: define void @test8_2(i32* nocapture writeonly %p)
|
||||
define void @test8_2(i32* %p) {
|
||||
entry:
|
||||
%call = call i32* @test8_1(i32* %p)
|
||||
|
@ -115,18 +125,21 @@ define i32 @volatile_load(i32* %p) {
|
|||
ret i32 %load
|
||||
}
|
||||
|
||||
declare void @escape_readonly_ptr(i8** %addr, i8* readnone %ptr)
|
||||
declare void @escape_readnone_ptr(i8** %addr, i8* readonly %ptr)
|
||||
declare void @escape_readnone_ptr(i8** %addr, i8* readnone %ptr)
|
||||
declare void @escape_readonly_ptr(i8** %addr, i8* readonly %ptr)
|
||||
|
||||
; The argument pointer %escaped_then_written cannot be marked readnone/only even
|
||||
; though the only direct use, in @escape_readnone_ptr/@escape_readonly_ptr,
|
||||
; is marked as readnone/only. However, the functions can write the pointer into
|
||||
; %addr, causing the store to write to %escaped_then_written.
|
||||
;
|
||||
; FIXME: This test currently exposes a bug!
|
||||
; FIXME: This test currently exposes a bug in functionattrs!
|
||||
;
|
||||
; BUG: define void @unsound_readnone(i8* %ignored, i8* readnone %escaped_then_written)
|
||||
; BUG: define void @unsound_readonly(i8* %ignored, i8* readonly %escaped_then_written)
|
||||
; FNATTR: define void @unsound_readnone(i8* nocapture readnone %ignored, i8* readnone %escaped_then_written)
|
||||
; FNATTR: define void @unsound_readonly(i8* nocapture readnone %ignored, i8* readonly %escaped_then_written)
|
||||
;
|
||||
; ATTRIBUTOR: define void @unsound_readnone(i8* nocapture readnone %ignored, i8* %escaped_then_written)
|
||||
; ATTRIBUTOR: define void @unsound_readonly(i8* nocapture readnone %ignored, i8* %escaped_then_written)
|
||||
define void @unsound_readnone(i8* %ignored, i8* %escaped_then_written) {
|
||||
%addr = alloca i8*
|
||||
call void @escape_readnone_ptr(i8** %addr, i8* %escaped_then_written)
|
||||
|
|
|
@ -11,7 +11,7 @@ target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
|
|||
; TEST 1 (positive case)
|
||||
; FNATTR: Function Attrs: noinline norecurse nounwind readnone uwtable
|
||||
; FNATTR-NEXT: define void @only_return()
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline norecurse nosync nounwind uwtable willreturn
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline norecurse nosync nounwind readnone uwtable willreturn
|
||||
; ATTRIBUTOR-NEXT: define void @only_return()
|
||||
define void @only_return() #0 {
|
||||
ret void
|
||||
|
@ -28,7 +28,7 @@ define void @only_return() #0 {
|
|||
; FNATTR: Function Attrs: noinline nounwind readnone uwtable
|
||||
; FNATTR-NEXT: define i32 @fib(i32 %0)
|
||||
; FIXME: missing willreturn
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind uwtable
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind readnone uwtable
|
||||
; ATTRIBUTOR-NEXT: define i32 @fib(i32 %0) local_unnamed_addr
|
||||
define i32 @fib(i32 %0) local_unnamed_addr #0 {
|
||||
%2 = icmp slt i32 %0, 2
|
||||
|
@ -59,7 +59,7 @@ define i32 @fib(i32 %0) local_unnamed_addr #0 {
|
|||
; FNATTR: Function Attrs: noinline norecurse nounwind readnone uwtable
|
||||
; FNATTR-NOT: willreturn
|
||||
; FNATTR-NEXT: define i32 @fact_maybe_not_halt(i32 %0) local_unnamed_addr
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline norecurse nosync nounwind uwtable
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline norecurse nosync nounwind readnone uwtable
|
||||
; ATTRIBUTOR-NOT: willreturn
|
||||
; ATTRIBUTOR-NEXT: define i32 @fact_maybe_not_halt(i32 %0) local_unnamed_addr
|
||||
define i32 @fact_maybe_not_halt(i32 %0) local_unnamed_addr #0 {
|
||||
|
@ -95,7 +95,7 @@ define i32 @fact_maybe_not_halt(i32 %0) local_unnamed_addr #0 {
|
|||
; FIXME: missing willreturn
|
||||
; FNATTR: Function Attrs: noinline norecurse nounwind readnone uwtable
|
||||
; FNATTR-NEXT: define i32 @fact_loop(i32 %0)
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline norecurse nosync nounwind uwtable
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline norecurse nosync nounwind readnone uwtable
|
||||
; ATTRIBUTOR-NEXT: define i32 @fact_loop(i32 %0) local_unnamed_addr
|
||||
define i32 @fact_loop(i32 %0) local_unnamed_addr #0 {
|
||||
%2 = icmp slt i32 %0, 1
|
||||
|
@ -126,7 +126,7 @@ define i32 @fact_loop(i32 %0) local_unnamed_addr #0 {
|
|||
; FNATTR: Function Attrs: noinline nounwind readnone uwtable
|
||||
; FNATTR-NOT: willreturn
|
||||
; FNATTR-NEXT: define void @mutual_recursion1(i1 %c)
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind uwtable
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind readnone uwtable
|
||||
; ATTRIBUTOR-NOT: willreturn
|
||||
; ATTRIBUTOR-NEXT: define void @mutual_recursion1(i1 %c)
|
||||
define void @mutual_recursion1(i1 %c) #0 {
|
||||
|
@ -142,7 +142,7 @@ end:
|
|||
; FNATTR: Function Attrs: noinline nounwind readnone uwtable
|
||||
; FNATTR-NOT: willreturn
|
||||
; FNATTR-NEXT: define void @mutual_recursion2(i1 %c)
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind uwtable
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind readnone uwtable
|
||||
; ATTRIBUTOR-NOT: willreturn
|
||||
; ATTRIBUTOR-NEXT: define void @mutual_recursion2(i1 %c)
|
||||
define void @mutual_recursion2(i1 %c) #0 {
|
||||
|
@ -216,10 +216,10 @@ define void @conditional_exit(i32 %0, i32* nocapture readonly %1) local_unnamed_
|
|||
; ATTRIBUTOR-NEXT: declare float @llvm.floor.f32(float)
|
||||
declare float @llvm.floor.f32(float)
|
||||
|
||||
; FNATTRS: Function Attrs: noinline nounwind uwtable
|
||||
; FNATTRS: Function Attrs: noinline nounwind readnone uwtable
|
||||
; FNATTRS-NEXT: define void @call_floor(float %a)
|
||||
; FIXME: missing willreturn
|
||||
; ATTRIBUTOR: Function Attrs: noinline nosync nounwind uwtable
|
||||
; ATTRIBUTOR: Function Attrs: noinline nosync nounwind readnone uwtable
|
||||
; ATTRIBUTOR-NEXT: define void @call_floor(float %a)
|
||||
define void @call_floor(float %a) #0 {
|
||||
tail call float @llvm.floor.f32(float %a)
|
||||
|
@ -337,7 +337,7 @@ declare i32 @__gxx_personality_v0(...)
|
|||
; FIXME: missing willreturn
|
||||
; FNATTR: Function Attrs: noinline norecurse nounwind readonly uwtable
|
||||
; FNATTR-NEXT: define i32 @loop_constant_trip_count(i32* nocapture readonly %0)
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline norecurse nosync nounwind uwtable
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline norecurse nosync nounwind readonly uwtable
|
||||
; ATTRIBUTOR-NEXT: define i32 @loop_constant_trip_count(i32* nocapture readonly %0)
|
||||
define i32 @loop_constant_trip_count(i32* nocapture readonly %0) #0 {
|
||||
br label %3
|
||||
|
@ -370,7 +370,7 @@ define i32 @loop_constant_trip_count(i32* nocapture readonly %0) #0 {
|
|||
; FNATTR: Function Attrs: noinline norecurse nounwind readonly uwtable
|
||||
; FNATTR-NOT: willreturn
|
||||
; FNATTR-NEXT: define i32 @loop_trip_count_unbound(i32 %0, i32 %1, i32* nocapture readonly %2, i32 %3) local_unnamed_addr
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline norecurse nosync nounwind uwtable
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline norecurse nosync nounwind readonly uwtable
|
||||
; ATTRIBUTOR-NOT: willreturn
|
||||
; ATTRIBUTOR-NEXT: define i32 @loop_trip_count_unbound(i32 %0, i32 %1, i32* nocapture readonly %2, i32 %3) local_unnamed_addr
|
||||
define i32 @loop_trip_count_unbound(i32 %0, i32 %1, i32* nocapture readonly %2, i32 %3) local_unnamed_addr #0 {
|
||||
|
@ -408,7 +408,7 @@ define i32 @loop_trip_count_unbound(i32 %0, i32 %1, i32* nocapture readonly %2,
|
|||
; FIXME: missing willreturn
|
||||
; FNATTR: Function Attrs: noinline norecurse nounwind readonly uwtable
|
||||
; FNATTR-NEXT: define i32 @loop_trip_dec(i32 %0, i32* nocapture readonly %1)
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline norecurse nosync nounwind uwtable
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline norecurse nosync nounwind readonly uwtable
|
||||
; ATTRIBUTOR-NEXT: define i32 @loop_trip_dec(i32 %0, i32* nocapture readonly %1) local_unnamed_addr
|
||||
|
||||
define i32 @loop_trip_dec(i32 %0, i32* nocapture readonly %1) local_unnamed_addr #0 {
|
||||
|
@ -439,7 +439,7 @@ define i32 @loop_trip_dec(i32 %0, i32* nocapture readonly %1) local_unnamed_addr
|
|||
|
||||
; FNATTR: Function Attrs: noinline norecurse nounwind readnone uwtable
|
||||
; FNATTR-NEXT: define i32 @multiple_return(i32 %a)
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline norecurse nosync nounwind uwtable willreturn
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline norecurse nosync nounwind readnone uwtable willreturn
|
||||
; ATTRIBUTOR-NEXT: define i32 @multiple_return(i32 %a)
|
||||
define i32 @multiple_return(i32 %a) #0 {
|
||||
%b = icmp eq i32 %a, 0
|
||||
|
@ -471,7 +471,7 @@ unreachable_label:
|
|||
; FIXME: missing willreturn
|
||||
; FNATTR: Function Attrs: noinline nounwind uwtable
|
||||
; FNATTR-NEXT: define i32 @unreachable_exit_positive2(i32 %0)
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline norecurse nosync nounwind uwtable
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline norecurse nosync nounwind readnone uwtable
|
||||
; ATTRIBUTOR-NEXT: define i32 @unreachable_exit_positive2(i32 %0)
|
||||
define i32 @unreachable_exit_positive2(i32) local_unnamed_addr #0 {
|
||||
%2 = icmp slt i32 %0, 1
|
||||
|
@ -515,7 +515,7 @@ unreachable_label:
|
|||
; FNATTR: Function Attrs: noinline nounwind uwtable
|
||||
; FNATTR-NOT: willreturn
|
||||
; FNATTR-NEXT: define void @unreachable_exit_negative2()
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline norecurse noreturn nosync nounwind uwtable
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline norecurse noreturn nosync nounwind readnone uwtable
|
||||
; ATTRIBUTOR-NOT: willreturn
|
||||
; ATTRIBUTOR-NEXT: define void @unreachable_exit_negative2()
|
||||
define void @unreachable_exit_negative2() #0 {
|
||||
|
|
Loading…
Reference in New Issue