<div dir="ltr">The number-memchecks.ll test fails for me on Windows because the output ordering is different. The two check groups are printed in opposite order:<div><br></div><div><br></div><div>"""</div><div><div>Printing analysis 'Loop Access Analysis' for function 'testg':</div><div>  for.body:</div><div>    Report: unsafe dependent memory operations in loop</div><div>    Interesting Dependences:</div><div>      Unknown:</div><div>          store i16 %mul1, i16* %arrayidxC, align 2 -></div><div>          store i16 %mul, i16* %arrayidxC1, align 2</div><div><br></div><div>    Run-time memory checks:</div><div>    Check 0:</div><div>      Comparing group 0:</div><div>        %arrayidxB = getelementptr inbounds i16, i16* %b, i64 %ind</div><div>      Against group 2:</div><div>        %arrayidxC1 = getelementptr inbounds i16, i16* %c, i64 %store_ind_inc</div><div>        %arrayidxC = getelementptr inbounds i16, i16* %c, i64 %store_ind</div><div>    Check 1:</div><div>      Comparing group 1:</div><div>        %arrayidxA1 = getelementptr inbounds i16, i16* %a, i64 %add</div><div>        %arrayidxA = getelementptr inbounds i16, i16* %a, i64 %ind</div><div>      Against group 2:</div><div>        %arrayidxC1 = getelementptr inbounds i16, i16* %c, i64 %store_ind_inc</div><div>        %arrayidxC = getelementptr inbounds i16, i16* %c, i64 %store_ind</div></div><div>"""</div><div><br></div><div>Can you fix the code to be deterministic, i.e. not rely on hashtable ordering?</div></div><div class="gmail_extra"><br><div class="gmail_quote">On Wed, Jul 8, 2015 at 2:16 AM, Silviu Baranga <span dir="ltr"><<a href="mailto:silviu.baranga@arm.com" target="_blank">silviu.baranga@arm.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Author: sbaranga<br>
Date: Wed Jul  8 04:16:33 2015<br>
New Revision: 241673<br>
<br>
URL: <a href="https://urldefense.proofpoint.com/v2/url?u=http-3A__llvm.org_viewvc_llvm-2Dproject-3Frev-3D241673-26view-3Drev&d=AwMFaQ&c=8hUWFZcy2Z-Za5rBPlktOQ&r=mQ4LZ2PUj9hpadE3cDHZnIdEwhEBrbAstXeMaFoB9tg&m=uFcf0gLR_MIrQFYhuAb8gVsoNdftcSB1mZMezpDReAU&s=Dxo7K237VlAGYajD5SNgUt5XddeKt_mFDUjKmbGQiqw&e=" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project?rev=241673&view=rev</a><br>
Log:<br>
[LAA] Merge memchecks for accesses separated by a constant offset<br>
<br>
Summary:<br>
Often filter-like loops will do memory accesses that are<br>
separated by constant offsets. In these cases it is<br>
common that we will exceed the threshold for the<br>
allowable number of checks.<br>
<br>
However, it should be possible to merge such checks,<br>
sice a check of any interval againt two other intervals separated<br>
by a constant offset (a,b), (a+c, b+c) will be equivalent with<br>
a check againt (a, b+c), as long as (a,b) and (a+c, b+c) overlap.<br>
Assuming the loop will be executed for a sufficient number of<br>
iterations, this will be true. If not true, checking against<br>
(a, b+c) is still safe (although not equivalent).<br>
<br>
As long as there are no dependencies between two accesses,<br>
we can merge their checks into a single one. We use this<br>
technique to construct groups of accesses, and then check<br>
the intervals associated with the groups instead of<br>
checking the accesses directly.<br>
<br>
Reviewers: anemet<br>
<br>
Subscribers: llvm-commits<br>
<br>
Differential Revision: <a href="https://urldefense.proofpoint.com/v2/url?u=http-3A__reviews.llvm.org_D10386&d=AwMFaQ&c=8hUWFZcy2Z-Za5rBPlktOQ&r=mQ4LZ2PUj9hpadE3cDHZnIdEwhEBrbAstXeMaFoB9tg&m=uFcf0gLR_MIrQFYhuAb8gVsoNdftcSB1mZMezpDReAU&s=WZTW9zyjMHaRKXUhKETIcSFkjBukReMt6Zx5q0F5GLo&e=" rel="noreferrer" target="_blank">http://reviews.llvm.org/D10386</a><br>
<br>
Modified:<br>
    llvm/trunk/include/llvm/Analysis/LoopAccessAnalysis.h<br>
    llvm/trunk/lib/Analysis/LoopAccessAnalysis.cpp<br>
    llvm/trunk/test/Analysis/LoopAccessAnalysis/number-of-memchecks.ll<br>
    llvm/trunk/test/Analysis/LoopAccessAnalysis/resort-to-memchecks-only.ll<br>
    llvm/trunk/test/Analysis/LoopAccessAnalysis/unsafe-and-rt-checks.ll<br>
    llvm/trunk/test/Transforms/LoopDistribute/basic-with-memchecks.ll<br>
<br>
Modified: llvm/trunk/include/llvm/Analysis/LoopAccessAnalysis.h<br>
URL: <a href="https://urldefense.proofpoint.com/v2/url?u=http-3A__llvm.org_viewvc_llvm-2Dproject_llvm_trunk_include_llvm_Analysis_LoopAccessAnalysis.h-3Frev-3D241673-26r1-3D241672-26r2-3D241673-26view-3Ddiff&d=AwMFaQ&c=8hUWFZcy2Z-Za5rBPlktOQ&r=mQ4LZ2PUj9hpadE3cDHZnIdEwhEBrbAstXeMaFoB9tg&m=uFcf0gLR_MIrQFYhuAb8gVsoNdftcSB1mZMezpDReAU&s=ee7qyjv9D3q1usyor7Ut8EOQ4lfsGoBXXa40MufUDrQ&e=" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Analysis/LoopAccessAnalysis.h?rev=241673&r1=241672&r2=241673&view=diff</a><br>
==============================================================================<br>
--- llvm/trunk/include/llvm/Analysis/LoopAccessAnalysis.h (original)<br>
+++ llvm/trunk/include/llvm/Analysis/LoopAccessAnalysis.h Wed Jul  8 04:16:33 2015<br>
@@ -311,7 +311,7 @@ public:<br>
   /// This struct holds information about the memory runtime legality check that<br>
   /// a group of pointers do not overlap.<br>
   struct RuntimePointerCheck {<br>
-    RuntimePointerCheck() : Need(false) {}<br>
+    RuntimePointerCheck(ScalarEvolution *SE) : Need(false), SE(SE) {}<br>
<br>
     /// Reset the state of the pointer runtime information.<br>
     void reset() {<br>
@@ -322,16 +322,55 @@ public:<br>
       IsWritePtr.clear();<br>
       DependencySetId.clear();<br>
       AliasSetId.clear();<br>
+      Exprs.clear();<br>
     }<br>
<br>
     /// Insert a pointer and calculate the start and end SCEVs.<br>
-    void insert(ScalarEvolution *SE, Loop *Lp, Value *Ptr, bool WritePtr,<br>
-                unsigned DepSetId, unsigned ASId,<br>
-                const ValueToValueMap &Strides);<br>
+    void insert(Loop *Lp, Value *Ptr, bool WritePtr, unsigned DepSetId,<br>
+                unsigned ASId, const ValueToValueMap &Strides);<br>
<br>
     /// \brief No run-time memory checking is necessary.<br>
     bool empty() const { return Pointers.empty(); }<br>
<br>
+    /// A grouping of pointers. A single memcheck is required between<br>
+    /// two groups.<br>
+    struct CheckingPtrGroup {<br>
+      /// \brief Create a new pointer checking group containing a single<br>
+      /// pointer, with index \p Index in RtCheck.<br>
+      CheckingPtrGroup(unsigned Index, RuntimePointerCheck &RtCheck)<br>
+          : RtCheck(RtCheck), High(RtCheck.Ends[Index]),<br>
+            Low(RtCheck.Starts[Index]) {<br>
+        Members.push_back(Index);<br>
+      }<br>
+<br>
+      /// \brief Tries to add the pointer recorded in RtCheck at index<br>
+      /// \p Index to this pointer checking group. We can only add a pointer<br>
+      /// to a checking group if we will still be able to get<br>
+      /// the upper and lower bounds of the check. Returns true in case<br>
+      /// of success, false otherwise.<br>
+      bool addPointer(unsigned Index);<br>
+<br>
+      /// Constitutes the context of this pointer checking group. For each<br>
+      /// pointer that is a member of this group we will retain the index<br>
+      /// at which it appears in RtCheck.<br>
+      RuntimePointerCheck &RtCheck;<br>
+      /// The SCEV expression which represents the upper bound of all the<br>
+      /// pointers in this group.<br>
+      const SCEV *High;<br>
+      /// The SCEV expression which represents the lower bound of all the<br>
+      /// pointers in this group.<br>
+      const SCEV *Low;<br>
+      /// Indices of all the pointers that constitute this grouping.<br>
+      SmallVector<unsigned, 2> Members;<br>
+    };<br>
+<br>
+    /// \brief Groups pointers such that a single memcheck is required<br>
+    /// between two different groups. This will clear the CheckingGroups vector<br>
+    /// and re-compute it. We will only group dependecies if \p UseDependencies<br>
+    /// is true, otherwise we will create a separate group for each pointer.<br>
+    void groupChecks(MemoryDepChecker::DepCandidates &DepCands,<br>
+                     bool UseDependencies);<br>
+<br>
     /// \brief Decide whether we need to issue a run-time check for pointer at<br>
     /// index \p I and \p J to prove their independence.<br>
     ///<br>
@@ -341,6 +380,12 @@ public:<br>
     bool needsChecking(unsigned I, unsigned J,<br>
                        const SmallVectorImpl<int> *PtrPartition) const;<br>
<br>
+    /// \brief Decide if we need to add a check between two groups of pointers,<br>
+    /// according to needsChecking.<br>
+    bool needsChecking(const CheckingPtrGroup &M,<br>
+                       const CheckingPtrGroup &N,<br>
+                       const SmallVectorImpl<int> *PtrPartition) const;<br>
+<br>
     /// \brief Return true if any pointer requires run-time checking according<br>
     /// to needsChecking.<br>
     bool needsAnyChecking(const SmallVectorImpl<int> *PtrPartition) const;<br>
@@ -372,6 +417,12 @@ public:<br>
     SmallVector<unsigned, 2> DependencySetId;<br>
     /// Holds the id of the disjoint alias set to which this pointer belongs.<br>
     SmallVector<unsigned, 2> AliasSetId;<br>
+    /// Holds at position i the SCEV for the access i<br>
+    SmallVector<const SCEV *, 2> Exprs;<br>
+    /// Holds a partitioning of pointers into "check groups".<br>
+    SmallVector<CheckingPtrGroup, 2> CheckingGroups;<br>
+    /// Holds a pointer to the ScalarEvolution analysis.<br>
+    ScalarEvolution *SE;<br>
   };<br>
<br>
   LoopAccessInfo(Loop *L, ScalarEvolution *SE, const DataLayout &DL,<br>
<br>
Modified: llvm/trunk/lib/Analysis/LoopAccessAnalysis.cpp<br>
URL: <a href="https://urldefense.proofpoint.com/v2/url?u=http-3A__llvm.org_viewvc_llvm-2Dproject_llvm_trunk_lib_Analysis_LoopAccessAnalysis.cpp-3Frev-3D241673-26r1-3D241672-26r2-3D241673-26view-3Ddiff&d=AwMFaQ&c=8hUWFZcy2Z-Za5rBPlktOQ&r=mQ4LZ2PUj9hpadE3cDHZnIdEwhEBrbAstXeMaFoB9tg&m=uFcf0gLR_MIrQFYhuAb8gVsoNdftcSB1mZMezpDReAU&s=jgDoUSmGrEpNcijw3f5mG6psVDXWQp5Rz1pJjlCtKRQ&e=" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Analysis/LoopAccessAnalysis.cpp?rev=241673&r1=241672&r2=241673&view=diff</a><br>
==============================================================================<br>
--- llvm/trunk/lib/Analysis/LoopAccessAnalysis.cpp (original)<br>
+++ llvm/trunk/lib/Analysis/LoopAccessAnalysis.cpp Wed Jul  8 04:16:33 2015<br>
@@ -48,6 +48,13 @@ static cl::opt<unsigned, true> RuntimeMe<br>
     cl::location(VectorizerParams::RuntimeMemoryCheckThreshold), cl::init(8));<br>
 unsigned VectorizerParams::RuntimeMemoryCheckThreshold;<br>
<br>
+/// \brief The maximum iterations used to merge memory checks<br>
+static cl::opt<unsigned> MemoryCheckMergeThreshold(<br>
+    "memory-check-merge-threshold", cl::Hidden,<br>
+    cl::desc("Maximum number of comparisons done when trying to merge "<br>
+             "runtime memory checks. (default = 100)"),<br>
+    cl::init(100));<br>
+<br>
 /// Maximum SIMD width.<br>
 const unsigned VectorizerParams::MaxVectorWidth = 64;<br>
<br>
@@ -113,8 +120,8 @@ const SCEV *llvm::replaceSymbolicStrideS<br>
 }<br>
<br>
 void LoopAccessInfo::RuntimePointerCheck::insert(<br>
-    ScalarEvolution *SE, Loop *Lp, Value *Ptr, bool WritePtr, unsigned DepSetId,<br>
-    unsigned ASId, const ValueToValueMap &Strides) {<br>
+    Loop *Lp, Value *Ptr, bool WritePtr, unsigned DepSetId, unsigned ASId,<br>
+    const ValueToValueMap &Strides) {<br>
   // Get the stride replaced scev.<br>
   const SCEV *Sc = replaceSymbolicStrideSCEV(SE, Strides, Ptr);<br>
   const SCEVAddRecExpr *AR = dyn_cast<SCEVAddRecExpr>(Sc);<br>
@@ -127,6 +134,136 @@ void LoopAccessInfo::RuntimePointerCheck<br>
   IsWritePtr.push_back(WritePtr);<br>
   DependencySetId.push_back(DepSetId);<br>
   AliasSetId.push_back(ASId);<br>
+  Exprs.push_back(Sc);<br>
+}<br>
+<br>
+bool LoopAccessInfo::RuntimePointerCheck::needsChecking(<br>
+    const CheckingPtrGroup &M, const CheckingPtrGroup &N,<br>
+    const SmallVectorImpl<int> *PtrPartition) const {<br>
+  for (unsigned I = 0, EI = M.Members.size(); EI != I; ++I)<br>
+    for (unsigned J = 0, EJ = N.Members.size(); EJ != J; ++J)<br>
+      if (needsChecking(M.Members[I], N.Members[J], PtrPartition))<br>
+        return true;<br>
+  return false;<br>
+}<br>
+<br>
+/// Compare \p I and \p J and return the minimum.<br>
+/// Return nullptr in case we couldn't find an answer.<br>
+static const SCEV *getMinFromExprs(const SCEV *I, const SCEV *J,<br>
+                                   ScalarEvolution *SE) {<br>
+  const SCEV *Diff = SE->getMinusSCEV(J, I);<br>
+  const SCEVConstant *C = dyn_cast<const SCEVConstant>(Diff);<br>
+<br>
+  if (!C)<br>
+    return nullptr;<br>
+  if (C->getValue()->isNegative())<br>
+    return J;<br>
+  return I;<br>
+}<br>
+<br>
+bool LoopAccessInfo::RuntimePointerCheck::CheckingPtrGroup::addPointer(<br>
+    unsigned Index) {<br>
+  // Compare the starts and ends with the known minimum and maximum<br>
+  // of this set. We need to know how we compare against the min/max<br>
+  // of the set in order to be able to emit memchecks.<br>
+  const SCEV *Min0 = getMinFromExprs(RtCheck.Starts[Index], Low, RtCheck.SE);<br>
+  if (!Min0)<br>
+    return false;<br>
+<br>
+  const SCEV *Min1 = getMinFromExprs(RtCheck.Ends[Index], High, RtCheck.SE);<br>
+  if (!Min1)<br>
+    return false;<br>
+<br>
+  // Update the low bound  expression if we've found a new min value.<br>
+  if (Min0 == RtCheck.Starts[Index])<br>
+    Low = RtCheck.Starts[Index];<br>
+<br>
+  // Update the high bound expression if we've found a new max value.<br>
+  if (Min1 != RtCheck.Ends[Index])<br>
+    High = RtCheck.Ends[Index];<br>
+<br>
+  Members.push_back(Index);<br>
+  return true;<br>
+}<br>
+<br>
+void LoopAccessInfo::RuntimePointerCheck::groupChecks(<br>
+    MemoryDepChecker::DepCandidates &DepCands,<br>
+    bool UseDependencies) {<br>
+  // We build the groups from dependency candidates equivalence classes<br>
+  // because:<br>
+  //    - We know that pointers in the same equivalence class share<br>
+  //      the same underlying object and therefore there is a chance<br>
+  //      that we can compare pointers<br>
+  //    - We wouldn't be able to merge two pointers for which we need<br>
+  //      to emit a memcheck. The classes in DepCands are already<br>
+  //      conveniently built such that no two pointers in the same<br>
+  //      class need checking against each other.<br>
+<br>
+  // We use the following (greedy) algorithm to construct the groups<br>
+  // For every pointer in the equivalence class:<br>
+  //   For each existing group:<br>
+  //   - if the difference between this pointer and the min/max bounds<br>
+  //     of the group is a constant, then make the pointer part of the<br>
+  //     group and update the min/max bounds of that group as required.<br>
+<br>
+  CheckingGroups.clear();<br>
+<br>
+  // If we don't have the dependency partitions, construct a new<br>
+  // checking pointer group for each pointer.<br>
+  if (!UseDependencies) {<br>
+    for (unsigned I = 0; I < Pointers.size(); ++I)<br>
+      CheckingGroups.push_back(CheckingPtrGroup(I, *this));<br>
+    return;<br>
+  }<br>
+<br>
+  unsigned TotalComparisons = 0;<br>
+<br>
+  DenseMap<Value *, unsigned> PositionMap;<br>
+  for (unsigned Pointer = 0; Pointer < Pointers.size(); ++Pointer)<br>
+    PositionMap[Pointers[Pointer]] = Pointer;<br>
+<br>
+  // Go through all equivalence classes, get the the "pointer check groups"<br>
+  // and add them to the overall solution.<br>
+  for (auto DI = DepCands.begin(), DE = DepCands.end(); DI != DE; ++DI) {<br>
+    if (!DI->isLeader())<br>
+      continue;<br>
+<br>
+    SmallVector<CheckingPtrGroup, 2> Groups;<br>
+<br>
+    for (auto MI = DepCands.member_begin(DI), ME = DepCands.member_end();<br>
+         MI != ME; ++MI) {<br>
+      unsigned Pointer = PositionMap[MI->getPointer()];<br>
+      bool Merged = false;<br>
+<br>
+      // Go through all the existing sets and see if we can find one<br>
+      // which can include this pointer.<br>
+      for (CheckingPtrGroup &Group : Groups) {<br>
+        // Don't perform more than a certain amount of comparisons.<br>
+        // This should limit the cost of grouping the pointers to something<br>
+        // reasonable.  If we do end up hitting this threshold, the algorithm<br>
+        // will create separate groups for all remaining pointers.<br>
+        if (TotalComparisons > MemoryCheckMergeThreshold)<br>
+          break;<br>
+<br>
+        TotalComparisons++;<br>
+<br>
+        if (Group.addPointer(Pointer)) {<br>
+          Merged = true;<br>
+          break;<br>
+        }<br>
+      }<br>
+<br>
+      if (!Merged)<br>
+        // We couldn't add this pointer to any existing set or the threshold<br>
+        // for the number of comparisons has been reached. Create a new group<br>
+        // to hold the current pointer.<br>
+        Groups.push_back(CheckingPtrGroup(Pointer, *this));<br>
+    }<br>
+<br>
+    // We've computed the grouped checks for this partition.<br>
+    // Save the results and continue with the next one.<br>
+    std::copy(Groups.begin(), Groups.end(), std::back_inserter(CheckingGroups));<br>
+  }<br>
 }<br>
<br>
 bool LoopAccessInfo::RuntimePointerCheck::needsChecking(<br>
@@ -156,42 +293,71 @@ bool LoopAccessInfo::RuntimePointerCheck<br>
 void LoopAccessInfo::RuntimePointerCheck::print(<br>
     raw_ostream &OS, unsigned Depth,<br>
     const SmallVectorImpl<int> *PtrPartition) const {<br>
-  unsigned NumPointers = Pointers.size();<br>
-  if (NumPointers == 0)<br>
-    return;<br>
<br>
   OS.indent(Depth) << "Run-time memory checks:\n";<br>
+<br>
   unsigned N = 0;<br>
-  for (unsigned I = 0; I < NumPointers; ++I)<br>
-    for (unsigned J = I + 1; J < NumPointers; ++J)<br>
-      if (needsChecking(I, J, PtrPartition)) {<br>
-        OS.indent(Depth) << N++ << ":\n";<br>
-        OS.indent(Depth + 2) << *Pointers[I];<br>
-        if (PtrPartition)<br>
-          OS << " (Partition: " << (*PtrPartition)[I] << ")";<br>
-        OS << "\n";<br>
-        OS.indent(Depth + 2) << *Pointers[J];<br>
-        if (PtrPartition)<br>
-          OS << " (Partition: " << (*PtrPartition)[J] << ")";<br>
-        OS << "\n";<br>
+  for (unsigned I = 0; I < CheckingGroups.size(); ++I)<br>
+    for (unsigned J = I + 1; J < CheckingGroups.size(); ++J)<br>
+      if (needsChecking(CheckingGroups[I], CheckingGroups[J], PtrPartition)) {<br>
+        OS.indent(Depth) << "Check " << N++ << ":\n";<br>
+        OS.indent(Depth + 2) << "Comparing group " << I << ":\n";<br>
+<br>
+        for (unsigned K = 0; K < CheckingGroups[I].Members.size(); ++K) {<br>
+          OS.indent(Depth + 2) << *Pointers[CheckingGroups[I].Members[K]]<br>
+                               << "\n";<br>
+          if (PtrPartition)<br>
+            OS << " (Partition: "<br>
+               << (*PtrPartition)[CheckingGroups[I].Members[K]] << ")"<br>
+               << "\n";<br>
+        }<br>
+<br>
+        OS.indent(Depth + 2) << "Against group " << J << ":\n";<br>
+<br>
+        for (unsigned K = 0; K < CheckingGroups[J].Members.size(); ++K) {<br>
+          OS.indent(Depth + 2) << *Pointers[CheckingGroups[J].Members[K]]<br>
+                               << "\n";<br>
+          if (PtrPartition)<br>
+            OS << " (Partition: "<br>
+               << (*PtrPartition)[CheckingGroups[J].Members[K]] << ")"<br>
+               << "\n";<br>
+        }<br>
       }<br>
+<br>
+  OS.indent(Depth) << "Grouped accesses:\n";<br>
+  for (unsigned I = 0; I < CheckingGroups.size(); ++I) {<br>
+    OS.indent(Depth + 2) << "Group " << I << ":\n";<br>
+    OS.indent(Depth + 4) << "(Low: " << *CheckingGroups[I].Low<br>
+                         << " High: " << *CheckingGroups[I].High << ")\n";<br>
+    for (unsigned J = 0; J < CheckingGroups[I].Members.size(); ++J) {<br>
+      OS.indent(Depth + 6) << "Member: " << *Exprs[CheckingGroups[I].Members[J]]<br>
+                           << "\n";<br>
+    }<br>
+  }<br>
 }<br>
<br>
 unsigned LoopAccessInfo::RuntimePointerCheck::getNumberOfChecks(<br>
     const SmallVectorImpl<int> *PtrPartition) const {<br>
-  unsigned NumPointers = Pointers.size();<br>
+<br>
+  unsigned NumPartitions = CheckingGroups.size();<br>
   unsigned CheckCount = 0;<br>
<br>
-  for (unsigned I = 0; I < NumPointers; ++I)<br>
-    for (unsigned J = I + 1; J < NumPointers; ++J)<br>
-      if (needsChecking(I, J, PtrPartition))<br>
+  for (unsigned I = 0; I < NumPartitions; ++I)<br>
+    for (unsigned J = I + 1; J < NumPartitions; ++J)<br>
+      if (needsChecking(CheckingGroups[I], CheckingGroups[J], PtrPartition))<br>
         CheckCount++;<br>
   return CheckCount;<br>
 }<br>
<br>
 bool LoopAccessInfo::RuntimePointerCheck::needsAnyChecking(<br>
     const SmallVectorImpl<int> *PtrPartition) const {<br>
-  return getNumberOfChecks(PtrPartition) != 0;<br>
+  unsigned NumPointers = Pointers.size();<br>
+<br>
+  for (unsigned I = 0; I < NumPointers; ++I)<br>
+    for (unsigned J = I + 1; J < NumPointers; ++J)<br>
+      if (needsChecking(I, J, PtrPartition))<br>
+        return true;<br>
+  return false;<br>
 }<br>
<br>
 namespace {<br>
@@ -341,7 +507,7 @@ bool AccessAnalysis::canCheckPtrAtRT(<br>
           // Each access has its own dependence set.<br>
           DepId = RunningDepId++;<br>
<br>
-        RtCheck.insert(SE, TheLoop, Ptr, IsWrite, DepId, ASId, StridesMap);<br>
+        RtCheck.insert(TheLoop, Ptr, IsWrite, DepId, ASId, StridesMap);<br>
<br>
         DEBUG(dbgs() << "LAA: Found a runtime check ptr:" << *Ptr << '\n');<br>
       } else {<br>
@@ -387,6 +553,9 @@ bool AccessAnalysis::canCheckPtrAtRT(<br>
     }<br>
   }<br>
<br>
+  if (NeedRTCheck && CanDoRT)<br>
+    RtCheck.groupChecks(DepCands, IsDepCheckNeeded);<br>
+<br>
   return CanDoRT;<br>
 }<br>
<br>
@@ -1360,32 +1529,35 @@ std::pair<Instruction *, Instruction *><br>
   if (!PtrRtCheck.Need)<br>
     return std::make_pair(nullptr, nullptr);<br>
<br>
-  unsigned NumPointers = PtrRtCheck.Pointers.size();<br>
-  SmallVector<TrackingVH<Value> , 2> Starts;<br>
-  SmallVector<TrackingVH<Value> , 2> Ends;<br>
+  SmallVector<TrackingVH<Value>, 2> Starts;<br>
+  SmallVector<TrackingVH<Value>, 2> Ends;<br>
<br>
   LLVMContext &Ctx = Loc->getContext();<br>
   SCEVExpander Exp(*SE, DL, "induction");<br>
   Instruction *FirstInst = nullptr;<br>
<br>
-  for (unsigned i = 0; i < NumPointers; ++i) {<br>
-    Value *Ptr = PtrRtCheck.Pointers[i];<br>
+  for (unsigned i = 0; i < PtrRtCheck.CheckingGroups.size(); ++i) {<br>
+    const RuntimePointerCheck::CheckingPtrGroup &CG =<br>
+        PtrRtCheck.CheckingGroups[i];<br>
+    Value *Ptr = PtrRtCheck.Pointers[CG.Members[0]];<br>
     const SCEV *Sc = SE->getSCEV(Ptr);<br>
<br>
     if (SE->isLoopInvariant(Sc, TheLoop)) {<br>
-      DEBUG(dbgs() << "LAA: Adding RT check for a loop invariant ptr:" <<<br>
-            *Ptr <<"\n");<br>
+      DEBUG(dbgs() << "LAA: Adding RT check for a loop invariant ptr:" << *Ptr<br>
+                   << "\n");<br>
       Starts.push_back(Ptr);<br>
       Ends.push_back(Ptr);<br>
     } else {<br>
-      DEBUG(dbgs() << "LAA: Adding RT check for range:" << *Ptr << '\n');<br>
       unsigned AS = Ptr->getType()->getPointerAddressSpace();<br>
<br>
       // Use this type for pointer arithmetic.<br>
       Type *PtrArithTy = Type::getInt8PtrTy(Ctx, AS);<br>
+      Value *Start = nullptr, *End = nullptr;<br>
<br>
-      Value *Start = Exp.expandCodeFor(PtrRtCheck.Starts[i], PtrArithTy, Loc);<br>
-      Value *End = Exp.expandCodeFor(PtrRtCheck.Ends[i], PtrArithTy, Loc);<br>
+      DEBUG(dbgs() << "LAA: Adding RT check for range:\n");<br>
+      Start = Exp.expandCodeFor(CG.Low, PtrArithTy, Loc);<br>
+      End = Exp.expandCodeFor(CG.High, PtrArithTy, Loc);<br>
+      DEBUG(dbgs() << "Start: " << *CG.Low << " End: " << *CG.High << "\n");<br>
       Starts.push_back(Start);<br>
       Ends.push_back(End);<br>
     }<br>
@@ -1394,9 +1566,14 @@ std::pair<Instruction *, Instruction *><br>
   IRBuilder<> ChkBuilder(Loc);<br>
   // Our instructions might fold to a constant.<br>
   Value *MemoryRuntimeCheck = nullptr;<br>
-  for (unsigned i = 0; i < NumPointers; ++i) {<br>
-    for (unsigned j = i+1; j < NumPointers; ++j) {<br>
-      if (!PtrRtCheck.needsChecking(i, j, PtrPartition))<br>
+  for (unsigned i = 0; i < PtrRtCheck.CheckingGroups.size(); ++i) {<br>
+    for (unsigned j = i + 1; j < PtrRtCheck.CheckingGroups.size(); ++j) {<br>
+      const RuntimePointerCheck::CheckingPtrGroup &CGI =<br>
+          PtrRtCheck.CheckingGroups[i];<br>
+      const RuntimePointerCheck::CheckingPtrGroup &CGJ =<br>
+          PtrRtCheck.CheckingGroups[j];<br>
+<br>
+      if (!PtrRtCheck.needsChecking(CGI, CGJ, PtrPartition))<br>
         continue;<br>
<br>
       unsigned AS0 = Starts[i]->getType()->getPointerAddressSpace();<br>
@@ -1447,8 +1624,8 @@ LoopAccessInfo::LoopAccessInfo(Loop *L,<br>
                                const TargetLibraryInfo *TLI, AliasAnalysis *AA,<br>
                                DominatorTree *DT, LoopInfo *LI,<br>
                                const ValueToValueMap &Strides)<br>
-    : DepChecker(SE, L), TheLoop(L), SE(SE), DL(DL),<br>
-      TLI(TLI), AA(AA), DT(DT), LI(LI), NumLoads(0), NumStores(0),<br>
+    : PtrRtCheck(SE), DepChecker(SE, L), TheLoop(L), SE(SE), DL(DL), TLI(TLI),<br>
+      AA(AA), DT(DT), LI(LI), NumLoads(0), NumStores(0),<br>
       MaxSafeDepDistBytes(-1U), CanVecMem(false),<br>
       StoreToLoopInvariantAddress(false) {<br>
   if (canAnalyzeLoop())<br>
<br>
Modified: llvm/trunk/test/Analysis/LoopAccessAnalysis/number-of-memchecks.ll<br>
URL: <a href="https://urldefense.proofpoint.com/v2/url?u=http-3A__llvm.org_viewvc_llvm-2Dproject_llvm_trunk_test_Analysis_LoopAccessAnalysis_number-2Dof-2Dmemchecks.ll-3Frev-3D241673-26r1-3D241672-26r2-3D241673-26view-3Ddiff&d=AwMFaQ&c=8hUWFZcy2Z-Za5rBPlktOQ&r=mQ4LZ2PUj9hpadE3cDHZnIdEwhEBrbAstXeMaFoB9tg&m=uFcf0gLR_MIrQFYhuAb8gVsoNdftcSB1mZMezpDReAU&s=Y-v3S_4R47_3pPyXhbHdLm4ACVBkEjbE-9GgFMygShY&e=" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Analysis/LoopAccessAnalysis/number-of-memchecks.ll?rev=241673&r1=241672&r2=241673&view=diff</a><br>
==============================================================================<br>
--- llvm/trunk/test/Analysis/LoopAccessAnalysis/number-of-memchecks.ll (original)<br>
+++ llvm/trunk/test/Analysis/LoopAccessAnalysis/number-of-memchecks.ll Wed Jul  8 04:16:33 2015<br>
@@ -1,19 +1,20 @@<br>
 ; RUN: opt -loop-accesses -analyze < %s | FileCheck %s<br>
<br>
-; 3 reads and 3 writes should need 12 memchecks<br>
-<br>
 target datalayout = "e-m:e-i64:64-i128:128-n32:64-S128"<br>
 target triple = "aarch64--linux-gnueabi"<br>
<br>
+; 3 reads and 3 writes should need 12 memchecks<br>
+; CHECK: function 'testf':<br>
 ; CHECK: Memory dependences are safe with run-time checks<br>
-; Memory dependecies have labels starting from 0, so in<br>
+<br>
+; Memory dependencies have labels starting from 0, so in<br>
 ; order to verify that we have n checks, we look for<br>
 ; (n-1): and not n:.<br>
<br>
 ; CHECK: Run-time memory checks:<br>
-; CHECK-NEXT: 0:<br>
-; CHECK: 11:<br>
-; CHECK-NOT: 12:<br>
+; CHECK-NEXT: Check 0:<br>
+; CHECK: Check 11:<br>
+; CHECK-NOT: Check 12:<br>
<br>
 define void @testf(i16* %a,<br>
                i16* %b,<br>
@@ -52,6 +53,165 @@ for.body:<br>
<br>
   %exitcond = icmp eq i64 %add, 20<br>
   br i1 %exitcond, label %for.end, label %for.body<br>
+<br>
+for.end:                                          ; preds = %for.body<br>
+  ret void<br>
+}<br>
+<br>
+; The following (testg and testh) check that we can group<br>
+; memory checks of accesses which differ by a constant value.<br>
+; Both tests are based on the following C code:<br>
+;<br>
+; void testh(short *a, short *b, short *c) {<br>
+;   unsigned long ind = 0;<br>
+;   for (unsigned long ind = 0; ind < 20; ++ind) {<br>
+;     c[2 * ind] = a[ind] * a[ind + 1];<br>
+;     c[2 * ind + 1] = a[ind] * a[ind + 1] * b[ind];<br>
+;   }<br>
+; }<br>
+;<br>
+; It is sufficient to check the intervals<br>
+; [a, a + 21], [b, b + 20] against [c, c + 41].<br>
+<br>
+; 3 reads and 2 writes - two of the reads can be merged,<br>
+; and the writes can be merged as well. This gives us a<br>
+; total of 2 memory checks.<br>
+<br>
+; CHECK: function 'testg':<br>
+<br>
+; CHECK: Run-time memory checks:<br>
+; CHECK-NEXT:   Check 0:<br>
+; CHECK-NEXT:     Comparing group 0:<br>
+; CHECK-NEXT:       %arrayidxA1 = getelementptr inbounds i16, i16* %a, i64 %add<br>
+; CHECK-NEXT:       %arrayidxA = getelementptr inbounds i16, i16* %a, i64 %ind<br>
+; CHECK-NEXT:     Against group 2:<br>
+; CHECK-NEXT:       %arrayidxC1 = getelementptr inbounds i16, i16* %c, i64 %store_ind_inc<br>
+; CHECK-NEXT:       %arrayidxC = getelementptr inbounds i16, i16* %c, i64 %store_ind<br>
+; CHECK-NEXT:   Check 1:<br>
+; CHECK-NEXT:     Comparing group 1:<br>
+; CHECK-NEXT:       %arrayidxB = getelementptr inbounds i16, i16* %b, i64 %ind<br>
+; CHECK-NEXT:     Against group 2:<br>
+; CHECK-NEXT:       %arrayidxC1 = getelementptr inbounds i16, i16* %c, i64 %store_ind_inc<br>
+; CHECK-NEXT:       %arrayidxC = getelementptr inbounds i16, i16* %c, i64 %store_ind<br>
+; CHECK-NEXT:   Grouped accesses:<br>
+; CHECK-NEXT:    Group 0:<br>
+; CHECK-NEXT:       (Low: %a High: (40 + %a))<br>
+; CHECK-NEXT:         Member: {(2 + %a),+,2}<br>
+; CHECK-NEXT:         Member: {%a,+,2}<br>
+; CHECK-NEXT:     Group 1:<br>
+; CHECK-NEXT:       (Low: %b High: (38 + %b))<br>
+; CHECK-NEXT:         Member: {%b,+,2}<br>
+; CHECK-NEXT:     Group 2:<br>
+; CHECK-NEXT:       (Low: %c High: (78 + %c))<br>
+; CHECK-NEXT:         Member: {(2 + %c),+,4}<br>
+; CHECK-NEXT:         Member: {%c,+,4}<br>
+<br>
+define void @testg(i16* %a,<br>
+               i16* %b,<br>
+               i16* %c) {<br>
+entry:<br>
+  br label %for.body<br>
+<br>
+for.body:                                         ; preds = %for.body, %entry<br>
+  %ind = phi i64 [ 0, %entry ], [ %add, %for.body ]<br>
+  %store_ind = phi i64 [ 0, %entry ], [ %store_ind_next, %for.body ]<br>
+<br>
+  %add = add nuw nsw i64 %ind, 1<br>
+  %store_ind_inc = add nuw nsw i64 %store_ind, 1<br>
+  %store_ind_next = add nuw nsw i64 %store_ind_inc, 1<br>
+<br>
+  %arrayidxA = getelementptr inbounds i16, i16* %a, i64 %ind<br>
+  %loadA = load i16, i16* %arrayidxA, align 2<br>
+<br>
+  %arrayidxA1 = getelementptr inbounds i16, i16* %a, i64 %add<br>
+  %loadA1 = load i16, i16* %arrayidxA1, align 2<br>
+<br>
+  %arrayidxB = getelementptr inbounds i16, i16* %b, i64 %ind<br>
+  %loadB = load i16, i16* %arrayidxB, align 2<br>
+<br>
+  %mul = mul i16 %loadA, %loadA1<br>
+  %mul1 = mul i16 %mul, %loadB<br>
+<br>
+  %arrayidxC = getelementptr inbounds i16, i16* %c, i64 %store_ind<br>
+  store i16 %mul1, i16* %arrayidxC, align 2<br>
+<br>
+  %arrayidxC1 = getelementptr inbounds i16, i16* %c, i64 %store_ind_inc<br>
+  store i16 %mul, i16* %arrayidxC1, align 2<br>
+<br>
+  %exitcond = icmp eq i64 %add, 20<br>
+  br i1 %exitcond, label %for.end, label %for.body<br>
+<br>
+for.end:                                          ; preds = %for.body<br>
+  ret void<br>
+}<br>
+<br>
+; 3 reads and 2 writes - the writes can be merged into a single<br>
+; group, but the GEPs used for the reads are not marked as inbounds.<br>
+; We can still merge them because we are using a unit stride for<br>
+; accesses, so we cannot overflow the GEPs.<br>
+<br>
+; CHECK: function 'testh':<br>
+; CHECK: Run-time memory checks:<br>
+; CHECK-NEXT:   Check 0:<br>
+; CHECK-NEXT:     Comparing group 0:<br>
+; CHECK-NEXT:         %arrayidxA1 = getelementptr i16, i16* %a, i64 %add<br>
+; CHECK-NEXT:         %arrayidxA = getelementptr i16, i16* %a, i64 %ind<br>
+; CHECK-NEXT:     Against group 2:<br>
+; CHECK-NEXT:         %arrayidxC1 = getelementptr inbounds i16, i16* %c, i64 %store_ind_inc<br>
+; CHECK-NEXT:         %arrayidxC = getelementptr inbounds i16, i16* %c, i64 %store_ind<br>
+; CHECK-NEXT:   Check 1:<br>
+; CHECK-NEXT:     Comparing group 1:<br>
+; CHECK-NEXT:         %arrayidxB = getelementptr i16, i16* %b, i64 %ind<br>
+; CHECK-NEXT:     Against group 2:<br>
+; CHECK-NEXT:         %arrayidxC1 = getelementptr inbounds i16, i16* %c, i64 %store_ind_inc<br>
+; CHECK-NEXT:         %arrayidxC = getelementptr inbounds i16, i16* %c, i64 %store_ind<br>
+; CHECK-NEXT:   Grouped accesses:<br>
+; CHECK-NEXT:     Group 0:<br>
+; CHECK-NEXT:       (Low: %a High: (40 + %a))<br>
+; CHECK-NEXT:         Member: {(2 + %a),+,2}<br>
+; CHECK-NEXT:         Member: {%a,+,2}<br>
+; CHECK-NEXT:     Group 1:<br>
+; CHECK-NEXT:       (Low: %b High: (38 + %b))<br>
+; CHECK-NEXT:         Member: {%b,+,2}<br>
+; CHECK-NEXT:     Group 2:<br>
+; CHECK-NEXT:       (Low: %c High: (78 + %c))<br>
+; CHECK-NEXT:         Member: {(2 + %c),+,4}<br>
+; CHECK-NEXT:         Member: {%c,+,4}<br>
+<br>
+define void @testh(i16* %a,<br>
+               i16* %b,<br>
+               i16* %c) {<br>
+entry:<br>
+  br label %for.body<br>
+<br>
+for.body:                                         ; preds = %for.body, %entry<br>
+  %ind = phi i64 [ 0, %entry ], [ %add, %for.body ]<br>
+  %store_ind = phi i64 [ 0, %entry ], [ %store_ind_next, %for.body ]<br>
+<br>
+  %add = add nuw nsw i64 %ind, 1<br>
+  %store_ind_inc = add nuw nsw i64 %store_ind, 1<br>
+  %store_ind_next = add nuw nsw i64 %store_ind_inc, 1<br>
+<br>
+  %arrayidxA = getelementptr i16, i16* %a, i64 %ind<br>
+  %loadA = load i16, i16* %arrayidxA, align 2<br>
+<br>
+  %arrayidxA1 = getelementptr i16, i16* %a, i64 %add<br>
+  %loadA1 = load i16, i16* %arrayidxA1, align 2<br>
+<br>
+  %arrayidxB = getelementptr i16, i16* %b, i64 %ind<br>
+  %loadB = load i16, i16* %arrayidxB, align 2<br>
+<br>
+  %mul = mul i16 %loadA, %loadA1<br>
+  %mul1 = mul i16 %mul, %loadB<br>
+<br>
+  %arrayidxC = getelementptr inbounds i16, i16* %c, i64 %store_ind<br>
+  store i16 %mul1, i16* %arrayidxC, align 2<br>
+<br>
+  %arrayidxC1 = getelementptr inbounds i16, i16* %c, i64 %store_ind_inc<br>
+  store i16 %mul, i16* %arrayidxC1, align 2<br>
+<br>
+  %exitcond = icmp eq i64 %add, 20<br>
+  br i1 %exitcond, label %for.end, label %for.body<br>
<br>
 for.end:                                          ; preds = %for.body<br>
   ret void<br>
<br>
Modified: llvm/trunk/test/Analysis/LoopAccessAnalysis/resort-to-memchecks-only.ll<br>
URL: <a href="https://urldefense.proofpoint.com/v2/url?u=http-3A__llvm.org_viewvc_llvm-2Dproject_llvm_trunk_test_Analysis_LoopAccessAnalysis_resort-2Dto-2Dmemchecks-2Donly.ll-3Frev-3D241673-26r1-3D241672-26r2-3D241673-26view-3Ddiff&d=AwMFaQ&c=8hUWFZcy2Z-Za5rBPlktOQ&r=mQ4LZ2PUj9hpadE3cDHZnIdEwhEBrbAstXeMaFoB9tg&m=uFcf0gLR_MIrQFYhuAb8gVsoNdftcSB1mZMezpDReAU&s=JOc5_PXhNth6VnQN3CEEvSTM90SHTXct7enPAEmHz7I&e=" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Analysis/LoopAccessAnalysis/resort-to-memchecks-only.ll?rev=241673&r1=241672&r2=241673&view=diff</a><br>
==============================================================================<br>
--- llvm/trunk/test/Analysis/LoopAccessAnalysis/resort-to-memchecks-only.ll (original)<br>
+++ llvm/trunk/test/Analysis/LoopAccessAnalysis/resort-to-memchecks-only.ll Wed Jul  8 04:16:33 2015<br>
@@ -15,7 +15,9 @@ target triple = "x86_64-apple-macosx10.1<br>
 ; CHECK-NEXT: Interesting Dependences:<br>
 ; CHECK-NEXT: Run-time memory checks:<br>
 ; CHECK-NEXT: 0:<br>
+; CHECK-NEXT: Comparing group<br>
 ; CHECK-NEXT:   %arrayidxA2 = getelementptr inbounds i16, i16* %a, i64 %idx<br>
+; CHECK-NEXT: Against group<br>
 ; CHECK-NEXT:   %arrayidxA = getelementptr inbounds i16, i16* %a, i64 %indvar<br>
<br>
 @B = common global i16* null, align 8<br>
<br>
Modified: llvm/trunk/test/Analysis/LoopAccessAnalysis/unsafe-and-rt-checks.ll<br>
URL: <a href="https://urldefense.proofpoint.com/v2/url?u=http-3A__llvm.org_viewvc_llvm-2Dproject_llvm_trunk_test_Analysis_LoopAccessAnalysis_unsafe-2Dand-2Drt-2Dchecks.ll-3Frev-3D241673-26r1-3D241672-26r2-3D241673-26view-3Ddiff&d=AwMFaQ&c=8hUWFZcy2Z-Za5rBPlktOQ&r=mQ4LZ2PUj9hpadE3cDHZnIdEwhEBrbAstXeMaFoB9tg&m=uFcf0gLR_MIrQFYhuAb8gVsoNdftcSB1mZMezpDReAU&s=YLQkgpXK7eq7uHA9rBwftq4p-U7welp1znOtw8VfhGI&e=" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Analysis/LoopAccessAnalysis/unsafe-and-rt-checks.ll?rev=241673&r1=241672&r2=241673&view=diff</a><br>
==============================================================================<br>
--- llvm/trunk/test/Analysis/LoopAccessAnalysis/unsafe-and-rt-checks.ll (original)<br>
+++ llvm/trunk/test/Analysis/LoopAccessAnalysis/unsafe-and-rt-checks.ll Wed Jul  8 04:16:33 2015<br>
@@ -14,10 +14,16 @@ target triple = "x86_64-apple-macosx10.1<br>
 ; CHECK-NEXT:     store i16 %mul1, i16* %arrayidxA_plus_2, align 2<br>
 ; CHECK: Run-time memory checks:<br>
 ; CHECK-NEXT: 0:<br>
+; CHECK-NEXT: Comparing group<br>
+; CHECK-NEXT:   %arrayidxA = getelementptr inbounds i16, i16* %a, i64 %storemerge3<br>
 ; CHECK-NEXT:   %arrayidxA_plus_2 = getelementptr inbounds i16, i16* %a, i64 %add<br>
+; CHECK-NEXT: Against group<br>
 ; CHECK-NEXT:   %arrayidxB = getelementptr inbounds i16, i16* %b, i64 %storemerge3<br>
 ; CHECK-NEXT: 1:<br>
+; CHECK-NEXT: Comparing group<br>
+; CHECK-NEXT:   %arrayidxA = getelementptr inbounds i16, i16* %a, i64 %storemerge3<br>
 ; CHECK-NEXT:   %arrayidxA_plus_2 = getelementptr inbounds i16, i16* %a, i64 %add<br>
+; CHECK-NEXT: Against group<br>
 ; CHECK-NEXT:   %arrayidxC = getelementptr inbounds i16, i16* %c, i64 %storemerge3<br>
<br>
 @B = common global i16* null, align 8<br>
<br>
Modified: llvm/trunk/test/Transforms/LoopDistribute/basic-with-memchecks.ll<br>
URL: <a href="https://urldefense.proofpoint.com/v2/url?u=http-3A__llvm.org_viewvc_llvm-2Dproject_llvm_trunk_test_Transforms_LoopDistribute_basic-2Dwith-2Dmemchecks.ll-3Frev-3D241673-26r1-3D241672-26r2-3D241673-26view-3Ddiff&d=AwMFaQ&c=8hUWFZcy2Z-Za5rBPlktOQ&r=mQ4LZ2PUj9hpadE3cDHZnIdEwhEBrbAstXeMaFoB9tg&m=uFcf0gLR_MIrQFYhuAb8gVsoNdftcSB1mZMezpDReAU&s=e-P3DrwxY7CSNu_j9MVwxJ2dBrtPl4pmQ_epAx9j5BY&e=" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/LoopDistribute/basic-with-memchecks.ll?rev=241673&r1=241672&r2=241673&view=diff</a><br>
==============================================================================<br>
--- llvm/trunk/test/Transforms/LoopDistribute/basic-with-memchecks.ll (original)<br>
+++ llvm/trunk/test/Transforms/LoopDistribute/basic-with-memchecks.ll Wed Jul  8 04:16:33 2015<br>
@@ -32,16 +32,14 @@ entry:<br>
   %e = load i32*, i32** @E, align 8<br>
   br label %for.body<br>
<br>
-; We have two compares for each array overlap check which is a total of 10<br>
-; compares.<br>
+; We have two compares for each array overlap check.<br>
+; Since the checks to A and A + 4 get merged, this will give us a<br>
+; total of 8 compares.<br>
 ;<br>
 ; CHECK: for.body.lver.memcheck:<br>
 ; CHECK:     = icmp<br>
 ; CHECK:     = icmp<br>
<br>
-; CHECK:     = icmp<br>
-; CHECK:     = icmp<br>
-<br>
 ; CHECK:     = icmp<br>
 ; CHECK:     = icmp<br>
<br>
<br>
<br>
_______________________________________________<br>
llvm-commits mailing list<br>
<a href="mailto:llvm-commits@cs.uiuc.edu">llvm-commits@cs.uiuc.edu</a><br>
<a href="http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits" rel="noreferrer" target="_blank">http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits</a><br>
</blockquote></div><br></div>