[AA] Tracking per-location ModRef info in FunctionModRefBehavior (NFCI)
authorNikita Popov <npopov@redhat.com>
Thu, 28 Jul 2022 13:50:39 +0000 (15:50 +0200)
committerNikita Popov <npopov@redhat.com>
Wed, 14 Sep 2022 14:34:41 +0000 (16:34 +0200)
Currently, FunctionModRefBehavior tracks whether the function reads
or writes memory (ModRefInfo) and which locations it can access
(argmem, inaccessiblemem and other). This patch changes it to track
ModRef information per-location instead.

To give two examples of why this is useful:

* D117095 highlights a weakness of ModRef modelling in the presence
  of operand bundles. For a memcpy call with deopt operand bundle,
  we want to say that it can read any memory, but only write argument
  memory. This would allow them to be treated like any other calls.
  However, we currently can't express this and have to say that it
  can read or write any memory.
* D127383 would ideally be modelled as a separate threadid location,
  where threadid Refs outside pre-split coroutines can be ignored
  (like other accesses to constant memory). The current representation
  does not allow modelling this precisely.

The patch as implemented is intended to be NFC, but there are some
obvious opportunities for improvements and simplification. To fully
capitalize on this we would also want to change the way we represent
memory attributes on functions, but that's a larger change, and I
think it makes sense to separate out the FunctionModRefBehavior
refactoring.

Differential Revision: https://reviews.llvm.org/D130896

16 files changed:
llvm/include/llvm/Analysis/AliasAnalysis.h
llvm/lib/Analysis/AliasAnalysis.cpp
llvm/lib/Analysis/AliasSetTracker.cpp
llvm/lib/Analysis/BasicAliasAnalysis.cpp
llvm/lib/Analysis/GlobalsModRef.cpp
llvm/lib/Analysis/ObjCARCAliasAnalysis.cpp
llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp
llvm/lib/Transforms/IPO/FunctionAttrs.cpp
llvm/lib/Transforms/IPO/ThinLTOBitcodeWriter.cpp
llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp
llvm/lib/Transforms/ObjCARC/DependencyAnalysis.cpp
llvm/lib/Transforms/Scalar/LICM.cpp
llvm/lib/Transforms/Utils/InlineFunction.cpp
llvm/unittests/Analysis/GlobalsModRefTest.cpp
polly/lib/Analysis/ScopBuilder.cpp
polly/lib/Analysis/ScopDetection.cpp

index 47c5d6b..7195c93 100644 (file)
@@ -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,193 @@ intersectModRef(const ModRefInfo MRI1, const ModRefInfo MRI2) {
   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 {
@@ -588,7 +637,7 @@ public:
   ///
   /// 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 +652,7 @@ public:
   ///
   /// 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 +665,7 @@ public:
   ///
   /// 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 +678,7 @@ public:
   ///
   /// 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 +1191,11 @@ public:
   }
 
   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 637c260..85d17d0 100644 (file)
@@ -236,17 +236,15 @@ ModRefInfo AAResults::getModRefInfo(const CallBase *Call,
   // 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 @@ ModRefInfo AAResults::getModRefInfo(const CallBase *Call1,
 
   // 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 @@ ModRefInfo AAResults::getModRefInfo(const CallBase *Call1,
 
   // 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 @@ ModRefInfo AAResults::getModRefInfo(const CallBase *Call1,
 }
 
 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 CallBase *Call) {
 }
 
 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 @@ raw_ostream &llvm::operator<<(raw_ostream &OS, AliasResult AR) {
   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 @@ ModRefInfo AAResults::getModRefInfo(const Instruction *I,
                                     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());
index f53a4c4..232fefe 100644 (file)
@@ -452,7 +452,7 @@ void AliasSetTracker::add(Instruction *I) {
           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 a5bc835..965a1fa 100644 (file)
@@ -751,31 +751,30 @@ static bool isIntrinsicCall(const CallBase *Call, Intrinsic::ID IID) {
 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;
 }
@@ -785,24 +784,22 @@ FunctionModRefBehavior BasicAAResult::getModRefBehavior(const CallBase *Call) {
 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.
@@ -1057,12 +1054,12 @@ ModRefInfo BasicAAResult::getModRefInfo(const CallBase *Call1,
   // 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 54da2ed..5e175aa 100644 (file)
@@ -239,12 +239,8 @@ void GlobalsAAResult::DeletionCallbackHandle::deleted() {
 }
 
 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);
 }
@@ -583,7 +579,7 @@ void GlobalsAAResult::AnalyzeCallGraph(CallGraph &CG, Module &M) {
 
               FunctionModRefBehavior Behaviour =
                   AAResultBase::getModRefBehavior(Callee);
-              FI.addModRefInfo(createModRefInfo(Behaviour));
+              FI.addModRefInfo(Behaviour.getModRef());
             }
           }
           continue;
index 6fe056d..30eb1be 100644 (file)
@@ -98,7 +98,7 @@ FunctionModRefBehavior ObjCARCAAResult::getModRefBehavior(const Function *F) {
 
   switch (GetFunctionClass(F)) {
   case ARCInstKind::NoopCast:
-    return FMRB_DoesNotAccessMemory;
+    return FunctionModRefBehavior::none();
   default:
     break;
   }
index c6fda7e..1751e99 100644 (file)
@@ -414,7 +414,7 @@ TypeBasedAAResult::getModRefBehavior(const CallBase *Call) {
   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);
 }
index 73ebb27..843d663 100644 (file)
@@ -126,16 +126,16 @@ static FunctionModRefBehavior
 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 @@ checkFunctionMemoryAccess(Function &F, bool ThisBody, AAResults &AAR,
           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 @@ checkFunctionMemoryAccess(Function &F, bool ThisBody, AAResults &AAR,
       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;
       }
@@ -195,13 +191,7 @@ checkFunctionMemoryAccess(Function &F, bool ThisBody, AAResults &AAR,
           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)) {
@@ -234,23 +224,17 @@ checkFunctionMemoryAccess(Function &F, bool ThisBody, AAResults &AAR,
     // 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,
@@ -276,12 +260,12 @@ static void addMemoryAttrs(const SCCNodeSet &SCCNodes, AARGetterT &&AARGetter,
     // 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 a5d8d9a..6700970 100644 (file)
@@ -318,8 +318,8 @@ void splitAndWriteThinLTOBitcode(
             return;
         }
         if (!F->isDeclaration() &&
-            computeFunctionBodyMemoryAccess(*F, AARGetter(*F)) ==
-                FMRB_DoesNotAccessMemory)
+            computeFunctionBodyMemoryAccess(*F, AARGetter(*F))
+                .doesNotAccessMemory())
           EligibleVirtualFns.insert(F);
       });
     }
index ff61924..74fb3f0 100644 (file)
@@ -1709,8 +1709,8 @@ bool DevirtModule::tryVirtualConstProp(
   // 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 de0f580..4a96f58 100644 (file)
@@ -49,9 +49,9 @@ bool llvm::objcarc::CanAlterRefCount(const Instruction *Inst, const Value *Ptr,
 
   // 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 37929d0..671c011 100644 (file)
@@ -1197,13 +1197,13 @@ bool llvm::canSinkOrHoistInst(Instruction &I, AAResults *AA, DominatorTree *DT,
 
     // 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 c9b5a1b..5abf7b1 100644 (file)
@@ -1064,10 +1064,10 @@ static void AddAliasScopeMetadata(CallBase &CB, ValueToValueMapTy &VMap,
           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 5066d74..821180c 100644 (file)
@@ -52,7 +52,7 @@ TEST(GlobalsModRef, OptNone) {
 
   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 1817797..0f36d24 100644 (file)
@@ -1634,31 +1634,16 @@ bool ScopBuilder::buildAccessCallInst(MemAccInst Inst, ScopStmt *Stmt) {
   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())
@@ -1679,9 +1664,12 @@ bool ScopBuilder::buildAccessCallInst(MemAccInst Inst, ScopStmt *Stmt) {
     }
     return true;
   }
-  }
 
-  return true;
+  if (FMRB.onlyReadsMemory()) {
+    GlobalReads.emplace_back(Stmt, CI);
+    return true;
+  }
+  return false;
 }
 
 void ScopBuilder::buildAccessSingleDim(MemAccInst Inst, ScopStmt *Stmt) {
index 99e9396..7c80a74 100644 (file)
@@ -708,23 +708,8 @@ bool ScopDetection::isValidCallInst(CallInst &CI,
   }
 
   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 @@ bool ScopDetection::isValidCallInst(CallInst &CI,
       // 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;