#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/Sequence.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Analysis/MemoryLocation.h"
#include "llvm/IR/PassManager.h"
return MRI1 & MRI2;
}
-/// The locations at which a function might access memory.
-///
-/// These are primarily used in conjunction with the \c AccessKind bits to
-/// describe both the nature of access and the locations of access for a
-/// function call.
-enum FunctionModRefLocation {
- /// Base case is no access to memory.
- FMRL_Nowhere = 0,
- /// Access to memory via argument pointers.
- FMRL_ArgumentPointees = 8,
- /// Memory that is inaccessible via LLVM IR.
- FMRL_InaccessibleMem = 16,
- /// Access to any memory.
- FMRL_Anywhere = 32 | FMRL_InaccessibleMem | FMRL_ArgumentPointees
-};
+/// Debug print ModRefInfo.
+raw_ostream &operator<<(raw_ostream &OS, ModRefInfo MR);
/// Summary of how a function affects memory in the program.
///
/// Loads from constant globals are not considered memory accesses for this
/// interface. Also, functions may freely modify stack space local to their
/// invocation without having to report it through these interfaces.
-enum FunctionModRefBehavior {
- /// This function does not perform any non-local loads or stores to memory.
- ///
- /// This property corresponds to the GCC 'const' attribute.
- /// This property corresponds to the LLVM IR 'readnone' attribute.
- /// This property corresponds to the IntrNoMem LLVM intrinsic flag.
- FMRB_DoesNotAccessMemory =
- FMRL_Nowhere | static_cast<int>(ModRefInfo::NoModRef),
-
- /// The only memory references in this function (if it has any) are
- /// non-volatile loads from objects pointed to by its pointer-typed
- /// arguments, with arbitrary offsets.
- ///
- /// This property corresponds to the combination of the IntrReadMem
- /// and IntrArgMemOnly LLVM intrinsic flags.
- FMRB_OnlyReadsArgumentPointees =
- FMRL_ArgumentPointees | static_cast<int>(ModRefInfo::Ref),
-
- /// The only memory references in this function (if it has any) are
- /// non-volatile stores from objects pointed to by its pointer-typed
- /// arguments, with arbitrary offsets.
- ///
- /// This property corresponds to the combination of the IntrWriteMem
- /// and IntrArgMemOnly LLVM intrinsic flags.
- FMRB_OnlyWritesArgumentPointees =
- FMRL_ArgumentPointees | static_cast<int>(ModRefInfo::Mod),
-
- /// The only memory references in this function (if it has any) are
- /// non-volatile loads and stores from objects pointed to by its
- /// pointer-typed arguments, with arbitrary offsets.
- ///
- /// This property corresponds to the IntrArgMemOnly LLVM intrinsic flag.
- FMRB_OnlyAccessesArgumentPointees =
- FMRL_ArgumentPointees | static_cast<int>(ModRefInfo::ModRef),
+class FunctionModRefBehavior {
+public:
+ /// The locations at which a function might access memory.
+ enum Location {
+ /// Access to memory via argument pointers.
+ ArgMem = 0,
+ /// Memory that is inaccessible via LLVM IR.
+ InaccessibleMem = 1,
+ /// Any other memory.
+ Other = 2,
+ };
- /// The only memory references in this function (if it has any) are
- /// reads of memory that is otherwise inaccessible via LLVM IR.
- ///
- /// This property corresponds to the LLVM IR inaccessiblememonly attribute.
- FMRB_OnlyReadsInaccessibleMem =
- FMRL_InaccessibleMem | static_cast<int>(ModRefInfo::Ref),
+private:
+ uint32_t Data = 0;
- /// The only memory references in this function (if it has any) are
- /// writes to memory that is otherwise inaccessible via LLVM IR.
- ///
- /// This property corresponds to the LLVM IR inaccessiblememonly attribute.
- FMRB_OnlyWritesInaccessibleMem =
- FMRL_InaccessibleMem | static_cast<int>(ModRefInfo::Mod),
+ static constexpr uint32_t BitsPerLoc = 2;
+ static constexpr uint32_t LocMask = (1 << BitsPerLoc) - 1;
- /// The only memory references in this function (if it has any) are
- /// references of memory that is otherwise inaccessible via LLVM IR.
- ///
- /// This property corresponds to the LLVM IR inaccessiblememonly attribute.
- FMRB_OnlyAccessesInaccessibleMem =
- FMRL_InaccessibleMem | static_cast<int>(ModRefInfo::ModRef),
-
- /// The function may perform non-volatile loads from objects pointed
- /// to by its pointer-typed arguments, with arbitrary offsets, and
- /// it may also perform loads of memory that is otherwise
- /// inaccessible via LLVM IR.
- ///
- /// This property corresponds to the LLVM IR
- /// inaccessiblemem_or_argmemonly attribute.
- FMRB_OnlyReadsInaccessibleOrArgMem = FMRL_InaccessibleMem |
- FMRL_ArgumentPointees |
- static_cast<int>(ModRefInfo::Ref),
-
- /// The function may perform non-volatile stores to objects pointed
- /// to by its pointer-typed arguments, with arbitrary offsets, and
- /// it may also perform stores of memory that is otherwise
- /// inaccessible via LLVM IR.
- ///
- /// This property corresponds to the LLVM IR
- /// inaccessiblemem_or_argmemonly attribute.
- FMRB_OnlyWritesInaccessibleOrArgMem = FMRL_InaccessibleMem |
- FMRL_ArgumentPointees |
- static_cast<int>(ModRefInfo::Mod),
-
- /// The function may perform non-volatile loads and stores of objects
- /// pointed to by its pointer-typed arguments, with arbitrary offsets, and
- /// it may also perform loads and stores of memory that is otherwise
- /// inaccessible via LLVM IR.
- ///
- /// This property corresponds to the LLVM IR
- /// inaccessiblemem_or_argmemonly attribute.
- FMRB_OnlyAccessesInaccessibleOrArgMem = FMRL_InaccessibleMem |
- FMRL_ArgumentPointees |
- static_cast<int>(ModRefInfo::ModRef),
-
- /// This function does not perform any non-local stores or volatile loads,
- /// but may read from any memory location.
- ///
- /// This property corresponds to the GCC 'pure' attribute.
- /// This property corresponds to the LLVM IR 'readonly' attribute.
- /// This property corresponds to the IntrReadMem LLVM intrinsic flag.
- FMRB_OnlyReadsMemory = FMRL_Anywhere | static_cast<int>(ModRefInfo::Ref),
-
- // This function does not read from memory anywhere, but may write to any
- // memory location.
- //
- // This property corresponds to the LLVM IR 'writeonly' attribute.
- // This property corresponds to the IntrWriteMem LLVM intrinsic flag.
- FMRB_OnlyWritesMemory = FMRL_Anywhere | static_cast<int>(ModRefInfo::Mod),
-
- /// This indicates that the function could not be classified into one of the
- /// behaviors above.
- FMRB_UnknownModRefBehavior =
- FMRL_Anywhere | static_cast<int>(ModRefInfo::ModRef)
+ static uint32_t getLocationPos(Location Loc) {
+ return (uint32_t)Loc * BitsPerLoc;
+ }
+
+ static auto locations() {
+ return enum_seq_inclusive(Location::ArgMem, Location::Other,
+ force_iteration_on_noniterable_enum);
+ }
+
+ FunctionModRefBehavior(uint32_t Data) : Data(Data) {}
+
+ void setModRef(Location Loc, ModRefInfo MR) {
+ Data &= ~(LocMask << getLocationPos(Loc));
+ Data |= static_cast<uint32_t>(MR) << getLocationPos(Loc);
+ }
+
+ friend raw_ostream &operator<<(raw_ostream &OS, FunctionModRefBehavior RMRB);
+
+public:
+ /// Create FunctionModRefBehavior that can access only the given location
+ /// with the given ModRefInfo.
+ FunctionModRefBehavior(Location Loc, ModRefInfo MR) { setModRef(Loc, MR); }
+
+ /// Create FunctionModRefBehavior that can access any location with the
+ /// given ModRefInfo.
+ explicit FunctionModRefBehavior(ModRefInfo MR) {
+ for (Location Loc : locations())
+ setModRef(Loc, MR);
+ }
+
+ /// Create FunctionModRefBehavior that can read and write any memory.
+ static FunctionModRefBehavior unknown() {
+ return FunctionModRefBehavior(ModRefInfo::ModRef);
+ }
+
+ /// Create FunctionModRefBehavior that cannot read or write any memory.
+ static FunctionModRefBehavior none() {
+ return FunctionModRefBehavior(ModRefInfo::NoModRef);
+ }
+
+ /// Create FunctionModRefBehavior that can read any memory.
+ static FunctionModRefBehavior readOnly() {
+ return FunctionModRefBehavior(ModRefInfo::Ref);
+ }
+
+ /// Create FunctionModRefBehavior that can write any memory.
+ static FunctionModRefBehavior writeOnly() {
+ return FunctionModRefBehavior(ModRefInfo::Mod);
+ }
+
+ /// Create FunctionModRefBehavior that can only access argument memory.
+ static FunctionModRefBehavior argMemOnly(ModRefInfo MR) {
+ return FunctionModRefBehavior(ArgMem, MR);
+ }
+
+ /// Create FunctionModRefBehavior that can only access inaccessible memory.
+ static FunctionModRefBehavior inaccessibleMemOnly(ModRefInfo MR) {
+ return FunctionModRefBehavior(InaccessibleMem, MR);
+ }
+
+ /// Create FunctionModRefBehavior that can only access inaccessible or
+ /// argument memory.
+ static FunctionModRefBehavior inaccessibleOrArgMemOnly(ModRefInfo MR) {
+ FunctionModRefBehavior FRMB = none();
+ FRMB.setModRef(ArgMem, MR);
+ FRMB.setModRef(InaccessibleMem, MR);
+ return FRMB;
+ }
+
+ /// Get ModRefInfo for the given Location.
+ ModRefInfo getModRef(Location Loc) const {
+ return ModRefInfo((Data >> getLocationPos(Loc)) & LocMask);
+ }
+
+ /// Get new FunctionModRefBehavior with modified ModRefInfo for Loc.
+ FunctionModRefBehavior getWithModRef(Location Loc, ModRefInfo MR) const {
+ FunctionModRefBehavior FMRB = *this;
+ FMRB.setModRef(Loc, MR);
+ return FMRB;
+ }
+
+ /// Get new FunctionModRefBehavior with NoModRef on the given Loc.
+ FunctionModRefBehavior getWithoutLoc(Location Loc) const {
+ FunctionModRefBehavior FMRB = *this;
+ FMRB.setModRef(Loc, ModRefInfo::NoModRef);
+ return FMRB;
+ }
+
+ /// Get ModRefInfo for any location.
+ ModRefInfo getModRef() const {
+ ModRefInfo MR = ModRefInfo::NoModRef;
+ for (Location Loc : locations())
+ MR |= getModRef(Loc);
+ return MR;
+ }
+
+ /// Whether this function accesses no memory.
+ bool doesNotAccessMemory() const { return Data == 0; }
+
+ /// Whether this function only (at most) reads memory.
+ bool onlyReadsMemory() const { return !isModSet(getModRef()); }
+
+ /// Whether this function only (at most) writes memory.
+ bool onlyWritesMemory() const { return !isRefSet(getModRef()); }
+
+ /// Whether this function only (at most) accesses argument memory.
+ bool onlyAccessesArgPointees() const {
+ return getWithoutLoc(ArgMem).doesNotAccessMemory();
+ }
+
+ /// Whether this function may access argument memory.
+ bool doesAccessArgPointees() const {
+ return isModOrRefSet(getModRef(ArgMem));
+ }
+
+ /// Whether this function only (at most) accesses inaccessible memory.
+ bool onlyAccessesInaccessibleMem() const {
+ return getWithoutLoc(InaccessibleMem).doesNotAccessMemory();
+ }
+
+ /// Whether this function only (at most) accesses argument and inaccessible
+ /// memory.
+ bool onlyAccessesInaccessibleOrArgMem() const {
+ return isNoModRef(getModRef(Other));
+ }
+
+ /// Intersect with another FunctionModRefBehavior.
+ FunctionModRefBehavior operator&(FunctionModRefBehavior Other) const {
+ return FunctionModRefBehavior(Data & Other.Data);
+ }
+
+ /// Intersect (in-place) with another FunctionModRefBehavior.
+ FunctionModRefBehavior &operator&=(FunctionModRefBehavior Other) {
+ Data &= Other.Data;
+ return *this;
+ }
+
+ /// Union with another FunctionModRefBehavior.
+ FunctionModRefBehavior operator|(FunctionModRefBehavior Other) const {
+ return FunctionModRefBehavior(Data | Other.Data);
+ }
+
+ /// Union (in-place) with another FunctionModRefBehavior.
+ FunctionModRefBehavior &operator|=(FunctionModRefBehavior Other) {
+ Data |= Other.Data;
+ return *this;
+ }
+
+ /// Check whether this is the same as another FunctionModRefBehavior.
+ bool operator==(FunctionModRefBehavior Other) const {
+ return Data == Other.Data;
+ }
+
+ /// Check whether this is different from another FunctionModRefBehavior.
+ bool operator!=(FunctionModRefBehavior Other) const {
+ return !operator==(Other);
+ }
};
-// Wrapper method strips bits significant only in FunctionModRefBehavior,
-// to obtain a valid ModRefInfo. The benefit of using the wrapper is that if
-// ModRefInfo enum changes, the wrapper can be updated to & with the new enum
-// entry with all bits set to 1.
-[[nodiscard]] inline ModRefInfo
-createModRefInfo(const FunctionModRefBehavior FMRB) {
- return ModRefInfo(FMRB & static_cast<int>(ModRefInfo::ModRef));
-}
+/// Debug print FunctionModRefBehavior.
+raw_ostream &operator<<(raw_ostream &OS, FunctionModRefBehavior RMRB);
/// Virtual base class for providers of capture information.
struct CaptureInfo {
///
/// This property corresponds to the GCC 'const' attribute.
bool doesNotAccessMemory(const CallBase *Call) {
- return getModRefBehavior(Call) == FMRB_DoesNotAccessMemory;
+ return getModRefBehavior(Call).doesNotAccessMemory();
}
/// Checks if the specified function is known to never read or write memory.
///
/// This property corresponds to the GCC 'const' attribute.
bool doesNotAccessMemory(const Function *F) {
- return getModRefBehavior(F) == FMRB_DoesNotAccessMemory;
+ return getModRefBehavior(F).doesNotAccessMemory();
}
/// Checks if the specified call is known to only read from non-volatile
///
/// This property corresponds to the GCC 'pure' attribute.
bool onlyReadsMemory(const CallBase *Call) {
- return onlyReadsMemory(getModRefBehavior(Call));
+ return getModRefBehavior(Call).onlyReadsMemory();
}
/// Checks if the specified function is known to only read from non-volatile
///
/// This property corresponds to the GCC 'pure' attribute.
bool onlyReadsMemory(const Function *F) {
- return onlyReadsMemory(getModRefBehavior(F));
- }
-
- /// Checks if functions with the specified behavior are known to only read
- /// from non-volatile memory (or not access memory at all).
- static bool onlyReadsMemory(FunctionModRefBehavior MRB) {
- return !isModSet(createModRefInfo(MRB));
- }
-
- /// Checks if functions with the specified behavior are known to only write
- /// memory (or not access memory at all).
- static bool onlyWritesMemory(FunctionModRefBehavior MRB) {
- return !isRefSet(createModRefInfo(MRB));
- }
-
- /// Checks if functions with the specified behavior are known to read and
- /// write at most from objects pointed to by their pointer-typed arguments
- /// (with arbitrary offsets).
- static bool onlyAccessesArgPointees(FunctionModRefBehavior MRB) {
- return !((unsigned)MRB & FMRL_Anywhere & ~FMRL_ArgumentPointees);
- }
-
- /// Checks if functions with the specified behavior are known to potentially
- /// read or write from objects pointed to be their pointer-typed arguments
- /// (with arbitrary offsets).
- static bool doesAccessArgPointees(FunctionModRefBehavior MRB) {
- return isModOrRefSet(createModRefInfo(MRB)) &&
- ((unsigned)MRB & FMRL_ArgumentPointees);
- }
-
- /// Checks if functions with the specified behavior are known to read and
- /// write at most from memory that is inaccessible from LLVM IR.
- static bool onlyAccessesInaccessibleMem(FunctionModRefBehavior MRB) {
- return !((unsigned)MRB & FMRL_Anywhere & ~FMRL_InaccessibleMem);
- }
-
- /// Checks if functions with the specified behavior are known to potentially
- /// read or write from memory that is inaccessible from LLVM IR.
- static bool doesAccessInaccessibleMem(FunctionModRefBehavior MRB) {
- return isModOrRefSet(createModRefInfo(MRB)) &&
- ((unsigned)MRB & FMRL_InaccessibleMem);
- }
-
- /// Checks if functions with the specified behavior are known to read and
- /// write at most from memory that is inaccessible from LLVM IR or objects
- /// pointed to by their pointer-typed arguments (with arbitrary offsets).
- static bool onlyAccessesInaccessibleOrArgMem(FunctionModRefBehavior MRB) {
- return !((unsigned)MRB & FMRL_Anywhere &
- ~(FMRL_InaccessibleMem | FMRL_ArgumentPointees));
+ return getModRefBehavior(F).onlyReadsMemory();
}
/// getModRefInfo (for call sites) - Return information about whether
}
FunctionModRefBehavior getModRefBehavior(const CallBase *Call) {
- return FMRB_UnknownModRefBehavior;
+ return FunctionModRefBehavior::unknown();
}
FunctionModRefBehavior getModRefBehavior(const Function *F) {
- return FMRB_UnknownModRefBehavior;
+ return FunctionModRefBehavior::unknown();
}
ModRefInfo getModRefInfo(const CallBase *Call, const MemoryLocation &Loc,
// Try to refine the mod-ref info further using other API entry points to the
// aggregate set of AA results.
auto MRB = getModRefBehavior(Call);
- if (onlyAccessesInaccessibleMem(MRB))
+ if (MRB.onlyAccessesInaccessibleMem())
return ModRefInfo::NoModRef;
- if (onlyReadsMemory(MRB))
- Result &= ModRefInfo::Ref;
- else if (onlyWritesMemory(MRB))
- Result &= ModRefInfo::Mod;
+ // TODO: Exclude inaccessible memory location here.
+ Result &= MRB.getModRef();
- if (onlyAccessesArgPointees(MRB) || onlyAccessesInaccessibleOrArgMem(MRB)) {
+ if (MRB.onlyAccessesArgPointees() || MRB.onlyAccessesInaccessibleOrArgMem()) {
ModRefInfo AllArgsMask = ModRefInfo::NoModRef;
- if (doesAccessArgPointees(MRB)) {
+ if (MRB.doesAccessArgPointees()) {
for (const auto &I : llvm::enumerate(Call->args())) {
const Value *Arg = I.value();
if (!Arg->getType()->isPointerTy())
// If Call1 or Call2 are readnone, they don't interact.
auto Call1B = getModRefBehavior(Call1);
- if (Call1B == FMRB_DoesNotAccessMemory)
+ if (Call1B.doesNotAccessMemory())
return ModRefInfo::NoModRef;
auto Call2B = getModRefBehavior(Call2);
- if (Call2B == FMRB_DoesNotAccessMemory)
+ if (Call2B.doesNotAccessMemory())
return ModRefInfo::NoModRef;
// If they both only read from memory, there is no dependence.
- if (onlyReadsMemory(Call1B) && onlyReadsMemory(Call2B))
+ if (Call1B.onlyReadsMemory() && Call2B.onlyReadsMemory())
return ModRefInfo::NoModRef;
// If Call1 only reads memory, the only dependence on Call2 can be
// from Call1 reading memory written by Call2.
- if (onlyReadsMemory(Call1B))
+ if (Call1B.onlyReadsMemory())
Result &= ModRefInfo::Ref;
- else if (onlyWritesMemory(Call1B))
+ else if (Call1B.onlyWritesMemory())
Result &= ModRefInfo::Mod;
// If Call2 only access memory through arguments, accumulate the mod/ref
// information from Call1's references to the memory referenced by
// Call2's arguments.
- if (onlyAccessesArgPointees(Call2B)) {
- if (!doesAccessArgPointees(Call2B))
+ if (Call2B.onlyAccessesArgPointees()) {
+ if (!Call2B.doesAccessArgPointees())
return ModRefInfo::NoModRef;
ModRefInfo R = ModRefInfo::NoModRef;
for (auto I = Call2->arg_begin(), E = Call2->arg_end(); I != E; ++I) {
// If Call1 only accesses memory through arguments, check if Call2 references
// any of the memory referenced by Call1's arguments. If not, return NoModRef.
- if (onlyAccessesArgPointees(Call1B)) {
- if (!doesAccessArgPointees(Call1B))
+ if (Call1B.onlyAccessesArgPointees()) {
+ if (!Call1B.doesAccessArgPointees())
return ModRefInfo::NoModRef;
ModRefInfo R = ModRefInfo::NoModRef;
for (auto I = Call1->arg_begin(), E = Call1->arg_end(); I != E; ++I) {
}
FunctionModRefBehavior AAResults::getModRefBehavior(const CallBase *Call) {
- FunctionModRefBehavior Result = FMRB_UnknownModRefBehavior;
+ FunctionModRefBehavior Result = FunctionModRefBehavior::unknown();
for (const auto &AA : AAs) {
- Result = FunctionModRefBehavior(Result & AA->getModRefBehavior(Call));
+ Result &= AA->getModRefBehavior(Call);
// Early-exit the moment we reach the bottom of the lattice.
- if (Result == FMRB_DoesNotAccessMemory)
+ if (Result.doesNotAccessMemory())
return Result;
}
}
FunctionModRefBehavior AAResults::getModRefBehavior(const Function *F) {
- FunctionModRefBehavior Result = FMRB_UnknownModRefBehavior;
+ FunctionModRefBehavior Result = FunctionModRefBehavior::unknown();
for (const auto &AA : AAs) {
- Result = FunctionModRefBehavior(Result & AA->getModRefBehavior(F));
+ Result &= AA->getModRefBehavior(F);
// Early-exit the moment we reach the bottom of the lattice.
- if (Result == FMRB_DoesNotAccessMemory)
+ if (Result.doesNotAccessMemory())
return Result;
}
return OS;
}
+raw_ostream &llvm::operator<<(raw_ostream &OS, ModRefInfo MR) {
+ switch (MR) {
+ case ModRefInfo::NoModRef:
+ OS << "NoModRef";
+ break;
+ case ModRefInfo::Ref:
+ OS << "Ref";
+ break;
+ case ModRefInfo::Mod:
+ OS << "Mod";
+ break;
+ case ModRefInfo::ModRef:
+ OS << "ModRef";
+ break;
+ }
+ return OS;
+}
+
+raw_ostream &llvm::operator<<(raw_ostream &OS, FunctionModRefBehavior FMRB) {
+ for (FunctionModRefBehavior::Location Loc :
+ FunctionModRefBehavior::locations()) {
+ switch (Loc) {
+ case FunctionModRefBehavior::ArgMem:
+ OS << "ArgMem: ";
+ break;
+ case FunctionModRefBehavior::InaccessibleMem:
+ OS << "InaccessibleMem: ";
+ break;
+ case FunctionModRefBehavior::Other:
+ OS << "Other: ";
+ break;
+ }
+ OS << FMRB.getModRef(Loc) << ", ";
+ }
+ return OS;
+}
+
//===----------------------------------------------------------------------===//
// Helper method implementation
//===----------------------------------------------------------------------===//
const Optional<MemoryLocation> &OptLoc,
AAQueryInfo &AAQIP) {
if (OptLoc == None) {
- if (const auto *Call = dyn_cast<CallBase>(I)) {
- return createModRefInfo(getModRefBehavior(Call));
- }
+ if (const auto *Call = dyn_cast<CallBase>(I))
+ return getModRefBehavior(Call).getModRef();
}
const MemoryLocation &Loc = OptLoc.value_or(MemoryLocation());
return AliasSet::NoAccess;
};
- ModRefInfo CallMask = createModRefInfo(AA.getModRefBehavior(Call));
+ ModRefInfo CallMask = AA.getModRefBehavior(Call).getModRef();
// Some intrinsics are marked as modifying memory for control flow
// modelling purposes, but don't actually modify any specific memory
FunctionModRefBehavior BasicAAResult::getModRefBehavior(const CallBase *Call) {
if (Call->doesNotAccessMemory())
// Can't do better than this.
- return FMRB_DoesNotAccessMemory;
-
- FunctionModRefBehavior Min = FMRB_UnknownModRefBehavior;
+ return FunctionModRefBehavior::none();
// If the callsite knows it only reads memory, don't return worse
// than that.
+ ModRefInfo MR = ModRefInfo::ModRef;
if (Call->onlyReadsMemory())
- Min = FMRB_OnlyReadsMemory;
+ MR = ModRefInfo::Ref;
else if (Call->onlyWritesMemory())
- Min = FMRB_OnlyWritesMemory;
+ MR = ModRefInfo::Mod;
+ FunctionModRefBehavior Min(MR);
if (Call->onlyAccessesArgMemory())
- Min = FunctionModRefBehavior(Min & FMRB_OnlyAccessesArgumentPointees);
+ Min = FunctionModRefBehavior::argMemOnly(MR);
else if (Call->onlyAccessesInaccessibleMemory())
- Min = FunctionModRefBehavior(Min & FMRB_OnlyAccessesInaccessibleMem);
+ Min = FunctionModRefBehavior::inaccessibleMemOnly(MR);
else if (Call->onlyAccessesInaccessibleMemOrArgMem())
- Min = FunctionModRefBehavior(Min & FMRB_OnlyAccessesInaccessibleOrArgMem);
+ Min = FunctionModRefBehavior::inaccessibleOrArgMemOnly(MR);
// If the call has operand bundles then aliasing attributes from the function
// it calls do not directly apply to the call. This can be made more precise
// in the future.
if (!Call->hasOperandBundles())
if (const Function *F = Call->getCalledFunction())
- Min =
- FunctionModRefBehavior(Min & getBestAAResults().getModRefBehavior(F));
+ Min &= getBestAAResults().getModRefBehavior(F);
return Min;
}
FunctionModRefBehavior BasicAAResult::getModRefBehavior(const Function *F) {
// If the function declares it doesn't access memory, we can't do better.
if (F->doesNotAccessMemory())
- return FMRB_DoesNotAccessMemory;
-
- FunctionModRefBehavior Min = FMRB_UnknownModRefBehavior;
+ return FunctionModRefBehavior::none();
// If the function declares it only reads memory, go with that.
+ ModRefInfo MR = ModRefInfo::ModRef;
if (F->onlyReadsMemory())
- Min = FMRB_OnlyReadsMemory;
+ MR = ModRefInfo::Ref;
else if (F->onlyWritesMemory())
- Min = FMRB_OnlyWritesMemory;
+ MR = ModRefInfo::Mod;
if (F->onlyAccessesArgMemory())
- Min = FunctionModRefBehavior(Min & FMRB_OnlyAccessesArgumentPointees);
- else if (F->onlyAccessesInaccessibleMemory())
- Min = FunctionModRefBehavior(Min & FMRB_OnlyAccessesInaccessibleMem);
- else if (F->onlyAccessesInaccessibleMemOrArgMem())
- Min = FunctionModRefBehavior(Min & FMRB_OnlyAccessesInaccessibleOrArgMem);
-
- return Min;
+ return FunctionModRefBehavior::argMemOnly(MR);
+ if (F->onlyAccessesInaccessibleMemory())
+ return FunctionModRefBehavior::inaccessibleMemOnly(MR);
+ if (F->onlyAccessesInaccessibleMemOrArgMem())
+ return FunctionModRefBehavior::inaccessibleOrArgMemOnly(MR);
+ return FunctionModRefBehavior(MR);
}
/// Returns true if this is a writeonly (i.e Mod only) parameter.
// possibilities for guard intrinsics.
if (isIntrinsicCall(Call1, Intrinsic::experimental_guard))
- return isModSet(createModRefInfo(getModRefBehavior(Call2)))
+ return isModSet(getModRefBehavior(Call2).getModRef())
? ModRefInfo::Ref
: ModRefInfo::NoModRef;
if (isIntrinsicCall(Call2, Intrinsic::experimental_guard))
- return isModSet(createModRefInfo(getModRefBehavior(Call1)))
+ return isModSet(getModRefBehavior(Call1).getModRef())
? ModRefInfo::Mod
: ModRefInfo::NoModRef;
}
FunctionModRefBehavior GlobalsAAResult::getModRefBehavior(const Function *F) {
- if (FunctionInfo *FI = getFunctionInfo(F)) {
- if (!isModOrRefSet(FI->getModRefInfo()))
- return FMRB_DoesNotAccessMemory;
- else if (!isModSet(FI->getModRefInfo()))
- return FMRB_OnlyReadsMemory;
- }
+ if (FunctionInfo *FI = getFunctionInfo(F))
+ return FunctionModRefBehavior(FI->getModRefInfo());
return AAResultBase::getModRefBehavior(F);
}
FunctionModRefBehavior Behaviour =
AAResultBase::getModRefBehavior(Callee);
- FI.addModRefInfo(createModRefInfo(Behaviour));
+ FI.addModRefInfo(Behaviour.getModRef());
}
}
continue;
switch (GetFunctionClass(F)) {
case ARCInstKind::NoopCast:
- return FMRB_DoesNotAccessMemory;
+ return FunctionModRefBehavior::none();
default:
break;
}
if (const MDNode *M = Call->getMetadata(LLVMContext::MD_tbaa))
if ((!isStructPathTBAA(M) && TBAANode(M).isTypeImmutable()) ||
(isStructPathTBAA(M) && TBAAStructTagNode(M).isTypeImmutable()))
- return FMRB_OnlyReadsMemory;
+ return FunctionModRefBehavior::readOnly();
return AAResultBase::getModRefBehavior(Call);
}
checkFunctionMemoryAccess(Function &F, bool ThisBody, AAResults &AAR,
const SCCNodeSet &SCCNodes) {
FunctionModRefBehavior MRB = AAR.getModRefBehavior(&F);
- if (MRB == FMRB_DoesNotAccessMemory)
+ if (MRB.doesNotAccessMemory())
// Already perfect!
return MRB;
if (!ThisBody)
return MRB;
+ // TODO: We should directly populate a FunctionModRefBehavior here.
// Scan the function body for instructions that may read or write memory.
- bool ReadsMemory = false;
- bool WritesMemory = false;
+ ModRefInfo MR = ModRefInfo::NoModRef;
// Track if the function accesses memory not based on pointer arguments or
// allocas.
bool AccessesNonArgsOrAlloca = false;
SCCNodes.count(Call->getCalledFunction()))
continue;
FunctionModRefBehavior MRB = AAR.getModRefBehavior(Call);
- ModRefInfo MRI = createModRefInfo(MRB);
+ ModRefInfo MRI = MRB.getModRef();
// If the call doesn't access memory, we're done.
if (isNoModRef(MRI))
if (isa<PseudoProbeInst>(I))
continue;
- if (!AliasAnalysis::onlyAccessesArgPointees(MRB)) {
- // The call could access any memory. If that includes writes, note it.
- if (isModSet(MRI))
- WritesMemory = true;
- // If it reads, note it.
- if (isRefSet(MRI))
- ReadsMemory = true;
+ if (!MRB.onlyAccessesArgPointees()) {
+ // The call could access any memory.
+ MR |= MRI;
AccessesNonArgsOrAlloca = true;
continue;
}
continue;
AccessesNonArgsOrAlloca |= !IsArgumentOrAlloca(Loc.Ptr);
-
- if (isModSet(MRI))
- // Writes non-local memory.
- WritesMemory = true;
- if (isRefSet(MRI))
- // Ok, it reads non-local memory.
- ReadsMemory = true;
+ MR |= MRI;
}
continue;
} else if (LoadInst *LI = dyn_cast<LoadInst>(&I)) {
// read or write memory.
//
// Writes memory, remember that.
- WritesMemory |= I.mayWriteToMemory();
+ if (I.mayWriteToMemory())
+ MR |= ModRefInfo::Mod;
// If this instruction may read memory, remember that.
- ReadsMemory |= I.mayReadFromMemory();
+ if (I.mayReadFromMemory())
+ MR |= ModRefInfo::Ref;
}
- if (!WritesMemory && !ReadsMemory)
- return FMRB_DoesNotAccessMemory;
-
- FunctionModRefBehavior Result = FunctionModRefBehavior(FMRL_Anywhere);
if (!AccessesNonArgsOrAlloca)
- Result = FunctionModRefBehavior(FMRL_ArgumentPointees);
- if (WritesMemory)
- Result = FunctionModRefBehavior(Result | static_cast<int>(ModRefInfo::Mod));
- if (ReadsMemory)
- Result = FunctionModRefBehavior(Result | static_cast<int>(ModRefInfo::Ref));
- return Result;
+ return FunctionModRefBehavior::argMemOnly(MR);
+ return FunctionModRefBehavior(MR);
}
FunctionModRefBehavior llvm::computeFunctionBodyMemoryAccess(Function &F,
// comment on GlobalValue::isDefinitionExact for more details.
FunctionModRefBehavior FMRB =
checkFunctionMemoryAccess(*F, F->hasExactDefinition(), AAR, SCCNodes);
- if (FMRB == FMRB_DoesNotAccessMemory)
+ if (FMRB.doesNotAccessMemory())
continue;
- ModRefInfo MR = createModRefInfo(FMRB);
+ ModRefInfo MR = FMRB.getModRef();
ReadsMemory |= isRefSet(MR);
WritesMemory |= isModSet(MR);
- ArgMemOnly &= AliasAnalysis::onlyAccessesArgPointees(FMRB);
+ ArgMemOnly &= FMRB.onlyAccessesArgPointees();
// Reached neither readnone, readonly, writeonly nor argmemonly can be
// inferred. Exit.
if (ReadsMemory && WritesMemory && !ArgMemOnly)
return;
}
if (!F->isDeclaration() &&
- computeFunctionBodyMemoryAccess(*F, AARGetter(*F)) ==
- FMRB_DoesNotAccessMemory)
+ computeFunctionBodyMemoryAccess(*F, AARGetter(*F))
+ .doesNotAccessMemory())
EligibleVirtualFns.insert(F);
});
}
// rather than using function attributes to perform local optimization.
for (VirtualCallTarget &Target : TargetsForSlot) {
if (Target.Fn->isDeclaration() ||
- computeFunctionBodyMemoryAccess(*Target.Fn, AARGetter(*Target.Fn)) !=
- FMRB_DoesNotAccessMemory ||
+ !computeFunctionBodyMemoryAccess(*Target.Fn, AARGetter(*Target.Fn))
+ .doesNotAccessMemory() ||
Target.Fn->arg_empty() || !Target.Fn->arg_begin()->use_empty() ||
Target.Fn->getReturnType() != RetType)
return false;
// See if AliasAnalysis can help us with the call.
FunctionModRefBehavior MRB = PA.getAA()->getModRefBehavior(Call);
- if (AliasAnalysis::onlyReadsMemory(MRB))
+ if (MRB.onlyReadsMemory())
return false;
- if (AliasAnalysis::onlyAccessesArgPointees(MRB)) {
+ if (MRB.onlyAccessesArgPointees()) {
for (const Value *Op : Call->args()) {
if (IsPotentialRetainableObjPtr(Op, *PA.getAA()) && PA.related(Ptr, Op))
return true;
// Handle simple cases by querying alias analysis.
FunctionModRefBehavior Behavior = AA->getModRefBehavior(CI);
- if (Behavior == FMRB_DoesNotAccessMemory)
+ if (Behavior.doesNotAccessMemory())
return true;
- if (AAResults::onlyReadsMemory(Behavior)) {
+ if (Behavior.onlyReadsMemory()) {
// A readonly argmemonly function only reads from memory pointed to by
// it's arguments with arbitrary offsets. If we can prove there are no
// writes to this memory in the loop, we can hoist or sink.
- if (AAResults::onlyAccessesArgPointees(Behavior)) {
+ if (Behavior.onlyAccessesArgPointees()) {
// TODO: expand to writeable arguments
for (Value *Op : CI->args())
if (Op->getType()->isPointerTy() &&
FunctionModRefBehavior MRB = CalleeAAR->getModRefBehavior(Call);
// We'll retain this knowledge without additional metadata.
- if (AAResults::onlyAccessesInaccessibleMem(MRB))
+ if (MRB.onlyAccessesInaccessibleMem())
continue;
- if (AAResults::onlyAccessesArgPointees(MRB))
+ if (MRB.onlyAccessesArgPointees())
IsArgMemOnlyCall = true;
}
auto AAR = GlobalsAAResult::analyzeModule(*M, GetTLI, CG);
- EXPECT_EQ(FMRB_UnknownModRefBehavior, AAR.getModRefBehavior(&F1));
- EXPECT_EQ(FMRB_DoesNotAccessMemory, AAR.getModRefBehavior(&F2));
- EXPECT_EQ(FMRB_OnlyReadsMemory, AAR.getModRefBehavior(&F3));
+ EXPECT_EQ(FunctionModRefBehavior::unknown(), AAR.getModRefBehavior(&F1));
+ EXPECT_EQ(FunctionModRefBehavior::none(), AAR.getModRefBehavior(&F2));
+ EXPECT_EQ(FunctionModRefBehavior::readOnly(), AAR.getModRefBehavior(&F3));
}
if (CI->doesNotAccessMemory() || isIgnoredIntrinsic(CI) || isDebugCall(CI))
return true;
- bool ReadOnly = false;
auto *AF = SE.getConstant(IntegerType::getInt64Ty(CI->getContext()), 0);
auto *CalledFunction = CI->getCalledFunction();
- switch (AA.getModRefBehavior(CalledFunction)) {
- case FMRB_UnknownModRefBehavior:
- llvm_unreachable("Unknown mod ref behaviour cannot be represented.");
- case FMRB_DoesNotAccessMemory:
+ FunctionModRefBehavior FMRB = AA.getModRefBehavior(CalledFunction);
+ if (FMRB.doesNotAccessMemory())
return true;
- case FMRB_OnlyWritesMemory:
- case FMRB_OnlyWritesInaccessibleMem:
- case FMRB_OnlyWritesInaccessibleOrArgMem:
- case FMRB_OnlyAccessesInaccessibleMem:
- case FMRB_OnlyAccessesInaccessibleOrArgMem:
- return false;
- case FMRB_OnlyReadsMemory:
- case FMRB_OnlyReadsInaccessibleMem:
- case FMRB_OnlyReadsInaccessibleOrArgMem:
- GlobalReads.emplace_back(Stmt, CI);
- return true;
- case FMRB_OnlyReadsArgumentPointees:
- ReadOnly = true;
- [[fallthrough]];
- case FMRB_OnlyWritesArgumentPointees:
- case FMRB_OnlyAccessesArgumentPointees: {
- auto AccType = ReadOnly ? MemoryAccess::READ : MemoryAccess::MAY_WRITE;
+
+ if (FMRB.onlyAccessesArgPointees()) {
+ ModRefInfo ArgMR = FMRB.getModRef(FunctionModRefBehavior::ArgMem);
+ auto AccType =
+ !isModSet(ArgMR) ? MemoryAccess::READ : MemoryAccess::MAY_WRITE;
Loop *L = LI.getLoopFor(Inst->getParent());
for (const auto &Arg : CI->args()) {
if (!Arg->getType()->isPointerTy())
}
return true;
}
- }
- return true;
+ if (FMRB.onlyReadsMemory()) {
+ GlobalReads.emplace_back(Stmt, CI);
+ return true;
+ }
+ return false;
}
void ScopBuilder::buildAccessSingleDim(MemAccInst Inst, ScopStmt *Stmt) {
}
if (AllowModrefCall) {
- switch (AA.getModRefBehavior(CalledFunction)) {
- case FMRB_UnknownModRefBehavior:
- return false;
- case FMRB_DoesNotAccessMemory:
- case FMRB_OnlyReadsMemory:
- case FMRB_OnlyReadsInaccessibleMem:
- case FMRB_OnlyReadsInaccessibleOrArgMem:
- // Implicitly disable delinearization since we have an unknown
- // accesses with an unknown access function.
- Context.HasUnknownAccess = true;
- // Explicitly use addUnknown so we don't put a loop-variant
- // pointer into the alias set.
- Context.AST.addUnknown(&CI);
- return true;
- case FMRB_OnlyReadsArgumentPointees:
- case FMRB_OnlyAccessesArgumentPointees:
- case FMRB_OnlyWritesArgumentPointees:
+ FunctionModRefBehavior FMRB = AA.getModRefBehavior(CalledFunction);
+ if (FMRB.onlyAccessesArgPointees()) {
for (const auto &Arg : CI.args()) {
if (!Arg->getType()->isPointerTy())
continue;
// pointer into the alias set.
Context.AST.addUnknown(&CI);
return true;
- case FMRB_OnlyWritesMemory:
- case FMRB_OnlyWritesInaccessibleMem:
- case FMRB_OnlyWritesInaccessibleOrArgMem:
- case FMRB_OnlyAccessesInaccessibleMem:
- case FMRB_OnlyAccessesInaccessibleOrArgMem:
- return false;
}
+
+ if (FMRB.onlyReadsMemory()) {
+ // Implicitly disable delinearization since we have an unknown
+ // accesses with an unknown access function.
+ Context.HasUnknownAccess = true;
+ // Explicitly use addUnknown so we don't put a loop-variant
+ // pointer into the alias set.
+ Context.AST.addUnknown(&CI);
+ return true;
+ }
+ return false;
}
return false;