Index: llvm/include/llvm/Analysis/AliasAnalysis.h =================================================================== --- llvm/include/llvm/Analysis/AliasAnalysis.h +++ llvm/include/llvm/Analysis/AliasAnalysis.h @@ -39,6 +39,7 @@ #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" @@ -196,145 +197,196 @@ 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(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(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(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(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(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(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(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(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(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(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(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(ModRefInfo::Mod), - - /// This indicates that the function could not be classified into one of the - /// behaviors above. - FMRB_UnknownModRefBehavior = - FMRL_Anywhere | static_cast(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 = 0) : Data(Data) {} + + void setModRef(Location Loc, ModRefInfo MR) { + Data &= ~(LocMask << getLocationPos(Loc)); + Data |= static_cast(MR) << getLocationPos(Loc); + } + + friend raw_ostream &operator<<(raw_ostream &OS, FunctionModRefBehavior RMRB); + +public: + FunctionModRefBehavior(Location Loc, ModRefInfo MR) { setModRef(Loc, MR); } + + /// Create FunctionModRefBehavior that can access any location with the + /// given ModRefInfo. + static FunctionModRefBehavior anyLoc(ModRefInfo MR) { + FunctionModRefBehavior FRMB; + for (Location Loc : locations()) + FRMB.setModRef(Loc, MR); + return FRMB; + } + + /// Create FunctionModRefBehavior that can read and write any memory. + static FunctionModRefBehavior unknown() { + return anyLoc(ModRefInfo::ModRef); + } + + /// Create FunctionModRefBehavior that cannot read or write any memory. + static FunctionModRefBehavior none() { + return FunctionModRefBehavior(); + } + + /// Create FunctionModRefBehavior that can read any memory. + static FunctionModRefBehavior readOnly() { + return anyLoc(ModRefInfo::Ref); + } + + /// Create FunctionModRefBehavior that can write any memory. + static FunctionModRefBehavior writeOnly() { + return anyLoc(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; + 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 withModRef(Location Loc, ModRefInfo MR) const { + FunctionModRefBehavior FMRB = *this; + FMRB.setModRef(Loc, MR); + return FMRB; + } + + /// Get new FunctionModRefBehavior with NoModRef on the given Loc. + FunctionModRefBehavior withoutLoc(Location Loc) const { + return withModRef(Loc, ModRefInfo::NoModRef); + } + + /// 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 withoutLoc(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 withoutLoc(InaccessibleMem).doesNotAccessMemory(); + } + + /// Whether this function may access inaccessible memory. + bool doesAccessInaccessibleMem() const { + return isModOrRefSet(getModRef(InaccessibleMem)); + } + + /// Whether this function only (at most) accesses argument and inaccessible + /// memory. + bool onlyAccessesInaccessibleOrArgMem() const { + return withoutLoc(ArgMem).withoutLoc(InaccessibleMem).doesNotAccessMemory(); + } + + /// Intersect (in-place) with another FunctionModRefBehavior. + FunctionModRefBehavior operator&(FunctionModRefBehavior Other) const { + return FunctionModRefBehavior(Data & Other.Data); + } + + /// Intersect with another FunctionModRefBehavior. + FunctionModRefBehavior &operator&=(FunctionModRefBehavior Other) { + Data &= Other.Data; + return *this; + } + + /// Union (in-place) with another FunctionModRefBehavior. + FunctionModRefBehavior operator|(FunctionModRefBehavior Other) const { + return FunctionModRefBehavior(Data | Other.Data); + } + + /// Union 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(ModRefInfo::ModRef)); -} +/// Debug print FunctionModRefBehavior. +raw_ostream &operator<<(raw_ostream &OS, FunctionModRefBehavior RMRB); /// Virtual base class for providers of capture information. struct CaptureInfo { @@ -588,7 +640,7 @@ /// /// 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. @@ -603,7 +655,7 @@ /// /// 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 @@ -616,7 +668,7 @@ /// /// 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 @@ -629,55 +681,7 @@ /// /// 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 @@ -1190,11 +1194,11 @@ } 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, Index: llvm/lib/Analysis/AliasAnalysis.cpp =================================================================== --- llvm/lib/Analysis/AliasAnalysis.cpp +++ llvm/lib/Analysis/AliasAnalysis.cpp @@ -236,17 +236,15 @@ // 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()) @@ -297,29 +295,29 @@ // 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) { @@ -356,8 +354,8 @@ // 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) { @@ -388,13 +386,13 @@ } 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; } @@ -402,13 +400,13 @@ } 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; } @@ -435,6 +433,43 @@ 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 //===----------------------------------------------------------------------===// @@ -624,9 +659,8 @@ const Optional &OptLoc, AAQueryInfo &AAQIP) { if (OptLoc == None) { - if (const auto *Call = dyn_cast(I)) { - return createModRefInfo(getModRefBehavior(Call)); - } + if (const auto *Call = dyn_cast(I)) + return getModRefBehavior(Call).getModRef(); } const MemoryLocation &Loc = OptLoc.value_or(MemoryLocation()); Index: llvm/lib/Analysis/AliasSetTracker.cpp =================================================================== --- llvm/lib/Analysis/AliasSetTracker.cpp +++ llvm/lib/Analysis/AliasSetTracker.cpp @@ -451,7 +451,7 @@ 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 Index: llvm/lib/Analysis/BasicAliasAnalysis.cpp =================================================================== --- llvm/lib/Analysis/BasicAliasAnalysis.cpp +++ llvm/lib/Analysis/BasicAliasAnalysis.cpp @@ -746,31 +746,30 @@ 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 = FunctionModRefBehavior::anyLoc(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; } @@ -780,24 +779,22 @@ 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::anyLoc(MR); } /// Returns true if this is a writeonly (i.e Mod only) parameter. @@ -1052,12 +1049,12 @@ // 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; Index: llvm/lib/Analysis/GlobalsModRef.cpp =================================================================== --- llvm/lib/Analysis/GlobalsModRef.cpp +++ llvm/lib/Analysis/GlobalsModRef.cpp @@ -239,16 +239,10 @@ } FunctionModRefBehavior GlobalsAAResult::getModRefBehavior(const Function *F) { - FunctionModRefBehavior Min = FMRB_UnknownModRefBehavior; + if (FunctionInfo *FI = getFunctionInfo(F)) + return FunctionModRefBehavior::anyLoc(FI->getModRefInfo()); - if (FunctionInfo *FI = getFunctionInfo(F)) { - if (!isModOrRefSet(FI->getModRefInfo())) - Min = FMRB_DoesNotAccessMemory; - else if (!isModSet(FI->getModRefInfo())) - Min = FMRB_OnlyReadsMemory; - } - - return FunctionModRefBehavior(AAResultBase::getModRefBehavior(F) & Min); + return AAResultBase::getModRefBehavior(F); } /// Returns the function info for the function, or null if we don't have @@ -585,7 +579,7 @@ FunctionModRefBehavior Behaviour = AAResultBase::getModRefBehavior(Callee); - FI.addModRefInfo(createModRefInfo(Behaviour)); + FI.addModRefInfo(Behaviour.getModRef()); } } continue; Index: llvm/lib/Analysis/ObjCARCAliasAnalysis.cpp =================================================================== --- llvm/lib/Analysis/ObjCARCAliasAnalysis.cpp +++ llvm/lib/Analysis/ObjCARCAliasAnalysis.cpp @@ -98,7 +98,7 @@ switch (GetFunctionClass(F)) { case ARCInstKind::NoopCast: - return FMRB_DoesNotAccessMemory; + return FunctionModRefBehavior::none(); default: break; } Index: llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp =================================================================== --- llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp +++ llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp @@ -409,16 +409,14 @@ if (!EnableTBAA) return AAResultBase::getModRefBehavior(Call); - FunctionModRefBehavior Min = FMRB_UnknownModRefBehavior; - // If this is an "immutable" type, we can assume the call doesn't write // to memory. if (const MDNode *M = Call->getMetadata(LLVMContext::MD_tbaa)) if ((!isStructPathTBAA(M) && TBAANode(M).isTypeImmutable()) || (isStructPathTBAA(M) && TBAAStructTagNode(M).isTypeImmutable())) - Min = FMRB_OnlyReadsMemory; + return FunctionModRefBehavior::readOnly(); - return FunctionModRefBehavior(AAResultBase::getModRefBehavior(Call) & Min); + return AAResultBase::getModRefBehavior(Call); } FunctionModRefBehavior TypeBasedAAResult::getModRefBehavior(const Function *F) { Index: llvm/lib/Transforms/IPO/FunctionAttrs.cpp =================================================================== --- llvm/lib/Transforms/IPO/FunctionAttrs.cpp +++ llvm/lib/Transforms/IPO/FunctionAttrs.cpp @@ -126,16 +126,16 @@ 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; @@ -156,7 +156,7 @@ 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)) @@ -169,13 +169,9 @@ if (isa(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; } @@ -195,13 +191,7 @@ 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(&I)) { @@ -234,23 +224,17 @@ // 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(ModRefInfo::Mod)); - if (ReadsMemory) - Result = FunctionModRefBehavior(Result | static_cast(ModRefInfo::Ref)); - return Result; + return FunctionModRefBehavior::argMemOnly(MR); + return FunctionModRefBehavior::anyLoc(MR); } FunctionModRefBehavior llvm::computeFunctionBodyMemoryAccess(Function &F, @@ -276,12 +260,12 @@ // 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) Index: llvm/lib/Transforms/IPO/ThinLTOBitcodeWriter.cpp =================================================================== --- llvm/lib/Transforms/IPO/ThinLTOBitcodeWriter.cpp +++ llvm/lib/Transforms/IPO/ThinLTOBitcodeWriter.cpp @@ -318,8 +318,8 @@ return; } if (!F->isDeclaration() && - computeFunctionBodyMemoryAccess(*F, AARGetter(*F)) == - FMRB_DoesNotAccessMemory) + computeFunctionBodyMemoryAccess(*F, AARGetter(*F)) + .doesNotAccessMemory()) EligibleVirtualFns.insert(F); }); } Index: llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp =================================================================== --- llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp +++ llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp @@ -1709,8 +1709,8 @@ // 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; Index: llvm/lib/Transforms/ObjCARC/DependencyAnalysis.cpp =================================================================== --- llvm/lib/Transforms/ObjCARC/DependencyAnalysis.cpp +++ llvm/lib/Transforms/ObjCARC/DependencyAnalysis.cpp @@ -49,9 +49,9 @@ // 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; Index: llvm/lib/Transforms/Scalar/LICM.cpp =================================================================== --- llvm/lib/Transforms/Scalar/LICM.cpp +++ llvm/lib/Transforms/Scalar/LICM.cpp @@ -1197,13 +1197,13 @@ // 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() && Index: llvm/lib/Transforms/Utils/InlineFunction.cpp =================================================================== --- llvm/lib/Transforms/Utils/InlineFunction.cpp +++ llvm/lib/Transforms/Utils/InlineFunction.cpp @@ -1064,10 +1064,10 @@ 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; } Index: llvm/unittests/Analysis/GlobalsModRefTest.cpp =================================================================== --- llvm/unittests/Analysis/GlobalsModRefTest.cpp +++ llvm/unittests/Analysis/GlobalsModRefTest.cpp @@ -52,7 +52,7 @@ 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)); } Index: polly/lib/Analysis/ScopBuilder.cpp =================================================================== --- polly/lib/Analysis/ScopBuilder.cpp +++ polly/lib/Analysis/ScopBuilder.cpp @@ -1634,31 +1634,14 @@ 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: - 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; + FunctionModRefBehavior FMRB = AA.getModRefBehavior(CalledFunction); + + 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()) @@ -1679,9 +1662,12 @@ } return true; } - } - return true; + if (FMRB.onlyReadsMemory()) { + GlobalReads.emplace_back(Stmt, CI); + return true; + } + return false; } void ScopBuilder::buildAccessSingleDim(MemAccInst Inst, ScopStmt *Stmt) { Index: polly/lib/Analysis/ScopDetection.cpp =================================================================== --- polly/lib/Analysis/ScopDetection.cpp +++ polly/lib/Analysis/ScopDetection.cpp @@ -708,23 +708,8 @@ } 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; @@ -748,13 +733,18 @@ // 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;