<div dir="ltr"><div class="gmail_quote"><div dir="ltr">On Mon, Jul 10, 2017 at 1:28 PM David Blaikie via llvm-commits <<a href="mailto:llvm-commits@lists.llvm.org">llvm-commits@lists.llvm.org</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div class="gmail_quote"><div dir="ltr">On Sun, Jul 9, 2017 at 6:45 AM Chandler Carruth via llvm-commits <<a href="mailto:llvm-commits@lists.llvm.org" target="_blank">llvm-commits@lists.llvm.org</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Author: chandlerc<br>
Date: Sun Jul  9 06:45:11 2017<br>
New Revision: 307498<br>
<br>
URL: <a href="http://llvm.org/viewvc/llvm-project?rev=307498&view=rev" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project?rev=307498&view=rev</a><br>
Log:<br>
[PM] Fix a nasty bug in the new PM where we failed to properly<br>
invalidation of analyses when merging SCCs.<br>
<br>
While I've added a bunch of testing of this, it takes something much<br>
more like the inliner to really trigger this as you need to have<br>
partially-analyzed SCCs with updates at just the right time.</blockquote></div></div><div dir="ltr"><div class="gmail_quote"><div><br>Why is that ^ not reproducible via a unit test with a dummy/stub analysis/transform/etc? Too much boilerplate to write those?<br></div></div></div></blockquote><div><br></div><div>Yeah, it's definitely technically possible, just incredibly complicated and requires huge amounts of boilerplate so it didn't seem like it would remain an effective test going forward.</div><div><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div class="gmail_quote"><div> </div></div></div><div dir="ltr"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"> So I've<br>
added a direct test for this using the inliner and verifying the<br>
domtree. Without the changes here, this test ends up finding a stale<br>
dominator tree.<br>
<br>
However, to handle this properly, we need to invalidate analyses<br>
*before* merging the SCCs. After talking to Philip and Sanjoy about this<br>
they convinced me this was the right approach. To do this, we need<br>
a callback mechanism when merging SCCs so we can observe the cycle that<br>
will be merged before the merge happens. This API update ended up being<br>
surprisingly easy.<br>
<br>
With this commit, the new PM passes the test-suite again. It hadn't<br>
since MemorySSA was enabled for EarlyCSE as that also will find this bug<br>
very quickly.<br>
<br>
Modified:<br>
    llvm/trunk/include/llvm/Analysis/LazyCallGraph.h<br>
    llvm/trunk/lib/Analysis/CGSCCPassManager.cpp<br>
    llvm/trunk/lib/Analysis/LazyCallGraph.cpp<br>
    llvm/trunk/test/Transforms/Inline/cgscc-incremental-invalidate.ll<br>
    llvm/trunk/unittests/Analysis/LazyCallGraphTest.cpp<br>
<br>
Modified: llvm/trunk/include/llvm/Analysis/LazyCallGraph.h<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Analysis/LazyCallGraph.h?rev=307498&r1=307497&r2=307498&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Analysis/LazyCallGraph.h?rev=307498&r1=307497&r2=307498&view=diff</a><br>
==============================================================================<br>
--- llvm/trunk/include/llvm/Analysis/LazyCallGraph.h (original)<br>
+++ llvm/trunk/include/llvm/Analysis/LazyCallGraph.h Sun Jul  9 06:45:11 2017<br>
@@ -652,17 +652,23 @@ public:<br>
     /// Make an existing internal ref edge into a call edge.<br>
     ///<br>
     /// This may form a larger cycle and thus collapse SCCs into TargetN's SCC.<br>
-    /// If that happens, the deleted SCC pointers are returned. These SCCs are<br>
-    /// not in a valid state any longer but the pointers will remain valid<br>
-    /// until destruction of the parent graph instance for the purpose of<br>
-    /// clearing cached information.<br>
+    /// If that happens, the optional callback \p MergedCB will be invoked (if<br>
+    /// provided) on the SCCs being merged away prior to actually performing<br>
+    /// the merge. Note that this will never include the target SCC as that<br>
+    /// will be the SCC functions are merged into to resolve the cycle. Once<br>
+    /// this function returns, these merged SCCs are not in a valid state but<br>
+    /// the pointers will remain valid until destruction of the parent graph<br>
+    /// instance for the purpose of clearing cached information. This function<br>
+    /// also returns 'true' if a cycle was formed and some SCCs merged away as<br>
+    /// a convenience.<br>
     ///<br>
     /// After this operation, both SourceN's SCC and TargetN's SCC may move<br>
     /// position within this RefSCC's postorder list. Any SCCs merged are<br>
     /// merged into the TargetN's SCC in order to preserve reachability analyses<br>
     /// which took place on that SCC.<br>
-    SmallVector<SCC *, 1> switchInternalEdgeToCall(Node &SourceN,<br>
-                                                   Node &TargetN);<br>
+    bool switchInternalEdgeToCall(<br>
+        Node &SourceN, Node &TargetN,<br>
+        function_ref<void(ArrayRef<SCC *> MergedSCCs)> MergeCB = {});<br>
<br>
     /// Make an existing internal call edge between separate SCCs into a ref<br>
     /// edge.<br>
<br>
Modified: llvm/trunk/lib/Analysis/CGSCCPassManager.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Analysis/CGSCCPassManager.cpp?rev=307498&r1=307497&r2=307498&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Analysis/CGSCCPassManager.cpp?rev=307498&r1=307497&r2=307498&view=diff</a><br>
==============================================================================<br>
--- llvm/trunk/lib/Analysis/CGSCCPassManager.cpp (original)<br>
+++ llvm/trunk/lib/Analysis/CGSCCPassManager.cpp Sun Jul  9 06:45:11 2017<br>
@@ -570,25 +570,48 @@ LazyCallGraph::SCC &llvm::updateCGAndAna<br>
     // Otherwise we are switching an internal ref edge to a call edge. This<br>
     // may merge away some SCCs, and we add those to the UpdateResult. We also<br>
     // need to make sure to update the worklist in the event SCCs have moved<br>
-    // before the current one in the post-order sequence.<br>
+    // before the current one in the post-order sequence<br>
+    bool HasFunctionAnalysisProxy = false;<br>
     auto InitialSCCIndex = RC->find(*C) - RC->begin();<br>
-    auto InvalidatedSCCs = RC->switchInternalEdgeToCall(N, *CallTarget);<br>
-    if (!InvalidatedSCCs.empty()) {<br>
+    bool FormedCycle = RC->switchInternalEdgeToCall(<br>
+        N, *CallTarget, [&](ArrayRef<SCC *> MergedSCCs) {<br>
+          for (SCC *MergedC : MergedSCCs) {<br>
+            assert(MergedC != &TargetC && "Cannot merge away the target SCC!");<br>
+<br>
+            HasFunctionAnalysisProxy |=<br>
+                AM.getCachedResult<FunctionAnalysisManagerCGSCCProxy>(<br>
+                    *MergedC) != nullptr;<br>
+<br>
+            // Mark that this SCC will no longer be valid.<br>
+            UR.InvalidatedSCCs.insert(MergedC);<br>
+<br>
+            // FIXME: We should really do a 'clear' here to forcibly release<br>
+            // memory, but we don't have a good way of doing that and<br>
+            // preserving the function analyses.<br>
+            auto PA = PreservedAnalyses::allInSet<AllAnalysesOn<Function>>();<br>
+            PA.preserve<FunctionAnalysisManagerCGSCCProxy>();<br>
+            AM.invalidate(*MergedC, PA);<br>
+          }<br>
+        });<br>
+<br>
+    // If we formed a cycle by creating this call, we need to update more data<br>
+    // structures.<br>
+    if (FormedCycle) {<br>
       C = &TargetC;<br>
       assert(G.lookupSCC(N) == C && "Failed to update current SCC!");<br>
<br>
-      // Any analyses cached for this SCC are no longer precise as the shape<br>
-      // has changed by introducing this cycle.<br>
-      AM.invalidate(*C, PreservedAnalyses::none());<br>
+      // If one of the invalidated SCCs had a cached proxy to a function<br>
+      // analysis manager, we need to create a proxy in the new current SCC as<br>
+      // the invaliadted SCCs had their functions moved.<br>
+      if (HasFunctionAnalysisProxy)<br>
+        AM.getResult<FunctionAnalysisManagerCGSCCProxy>(*C, G);<br>
<br>
-      for (SCC *InvalidatedC : InvalidatedSCCs) {<br>
-        assert(InvalidatedC != C && "Cannot invalidate the current SCC!");<br>
-        UR.InvalidatedSCCs.insert(InvalidatedC);<br>
-<br>
-        // Also clear any cached analyses for the SCCs that are dead. This<br>
-        // isn't really necessary for correctness but can release memory.<br>
-        AM.clear(*InvalidatedC);<br>
-      }<br>
+      // Any analyses cached for this SCC are no longer precise as the shape<br>
+      // has changed by introducing this cycle. However, we have taken care to<br>
+      // update the proxies so it remains valide.<br>
+      auto PA = PreservedAnalyses::allInSet<AllAnalysesOn<Function>>();<br>
+      PA.preserve<FunctionAnalysisManagerCGSCCProxy>();<br>
+      AM.invalidate(*C, PA);<br>
     }<br>
     auto NewSCCIndex = RC->find(*C) - RC->begin();<br>
     if (InitialSCCIndex < NewSCCIndex) {<br>
<br>
Modified: llvm/trunk/lib/Analysis/LazyCallGraph.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Analysis/LazyCallGraph.cpp?rev=307498&r1=307497&r2=307498&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Analysis/LazyCallGraph.cpp?rev=307498&r1=307497&r2=307498&view=diff</a><br>
==============================================================================<br>
--- llvm/trunk/lib/Analysis/LazyCallGraph.cpp (original)<br>
+++ llvm/trunk/lib/Analysis/LazyCallGraph.cpp Sun Jul  9 06:45:11 2017<br>
@@ -456,8 +456,10 @@ updatePostorderSequenceForEdgeInsertion(<br>
   return make_range(SCCs.begin() + SourceIdx, SCCs.begin() + TargetIdx);<br>
 }<br>
<br>
-SmallVector<LazyCallGraph::SCC *, 1><br>
-LazyCallGraph::RefSCC::switchInternalEdgeToCall(Node &SourceN, Node &TargetN) {<br>
+bool<br>
+LazyCallGraph::RefSCC::switchInternalEdgeToCall(<br>
+    Node &SourceN, Node &TargetN,<br>
+    function_ref<void(ArrayRef<SCC *> MergeSCCs)> MergeCB) {<br>
   assert(!(*SourceN)[TargetN].isCall() && "Must start with a ref edge!");<br>
   SmallVector<SCC *, 1> DeletedSCCs;<br>
<br>
@@ -475,7 +477,7 @@ LazyCallGraph::RefSCC::switchInternalEdg<br>
   // we've just added more connectivity.<br>
   if (&SourceSCC == &TargetSCC) {<br>
     SourceN->setEdgeKind(TargetN, Edge::Call);<br>
-    return DeletedSCCs;<br>
+    return false; // No new cycle.<br>
   }<br>
<br>
   // At this point we leverage the postorder list of SCCs to detect when the<br>
@@ -488,7 +490,7 @@ LazyCallGraph::RefSCC::switchInternalEdg<br>
   int TargetIdx = SCCIndices[&TargetSCC];<br>
   if (TargetIdx < SourceIdx) {<br>
     SourceN->setEdgeKind(TargetN, Edge::Call);<br>
-    return DeletedSCCs;<br>
+    return false; // No new cycle.<br>
   }<br>
<br>
   // Compute the SCCs which (transitively) reach the source.<br>
@@ -555,12 +557,16 @@ LazyCallGraph::RefSCC::switchInternalEdg<br>
       SourceSCC, TargetSCC, SCCs, SCCIndices, ComputeSourceConnectedSet,<br>
       ComputeTargetConnectedSet);<br>
<br>
+  // Run the user's callback on the merged SCCs before we actually merge them.<br>
+  if (MergeCB)<br>
+    MergeCB(makeArrayRef(MergeRange.begin(), MergeRange.end()));<br>
+<br>
   // If the merge range is empty, then adding the edge didn't actually form any<br>
   // new cycles. We're done.<br>
   if (MergeRange.begin() == MergeRange.end()) {<br>
     // Now that the SCC structure is finalized, flip the kind to call.<br>
     SourceN->setEdgeKind(TargetN, Edge::Call);<br>
-    return DeletedSCCs;<br>
+    return false; // No new cycle.<br>
   }<br>
<br>
 #ifndef NDEBUG<br>
@@ -596,8 +602,8 @@ LazyCallGraph::RefSCC::switchInternalEdg<br>
   // Now that the SCC structure is finalized, flip the kind to call.<br>
   SourceN->setEdgeKind(TargetN, Edge::Call);<br>
<br>
-  // And we're done!<br>
-  return DeletedSCCs;<br>
+  // And we're done, but we did form a new cycle.<br>
+  return true;<br>
 }<br>
<br>
 void LazyCallGraph::RefSCC::switchTrivialInternalEdgeToRef(Node &SourceN,<br>
<br>
Modified: llvm/trunk/test/Transforms/Inline/cgscc-incremental-invalidate.ll<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/Inline/cgscc-incremental-invalidate.ll?rev=307498&r1=307497&r2=307498&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/Inline/cgscc-incremental-invalidate.ll?rev=307498&r1=307497&r2=307498&view=diff</a><br>
==============================================================================<br>
--- llvm/trunk/test/Transforms/Inline/cgscc-incremental-invalidate.ll (original)<br>
+++ llvm/trunk/test/Transforms/Inline/cgscc-incremental-invalidate.ll Sun Jul  9 06:45:11 2017<br>
@@ -127,3 +127,80 @@ entry:<br>
   ret void<br>
 ; CHECK: ret void<br>
 }<br>
+<br>
+; The 'test2_' prefixed code works to carefully trigger forming an SCC with<br>
+; a dominator tree for one of the functions but not the other and without even<br>
+; a function analysis manager proxy for the SCC that things get merged into.<br>
+; Without proper handling when updating the call graph this will find a stale<br>
+; dominator tree.<br>
+<br>
+@test2_global = external global i32, align 4<br>
+<br>
+define void @test2_hoge(i1 (i32*)* %arg) {<br>
+; CHECK-LABEL: define void @test2_hoge(<br>
+bb:<br>
+  %tmp2 = call zeroext i1 %arg(i32* @test2_global)<br>
+; CHECK: call zeroext i1 %arg(<br>
+  br label %bb3<br>
+<br>
+bb3:<br>
+  %tmp5 = call zeroext i1 %arg(i32* @test2_global)<br>
+; CHECK: call zeroext i1 %arg(<br>
+  br i1 %tmp5, label %bb3, label %bb6<br>
+<br>
+bb6:<br>
+  ret void<br>
+}<br>
+<br>
+define zeroext i1 @test2_widget(i32* %arg) {<br>
+; CHECK-LABEL: define zeroext i1 @test2_widget(<br>
+bb:<br>
+  %tmp1 = alloca i8, align 1<br>
+  %tmp2 = alloca i32, align 4<br>
+  call void @test2_quux()<br>
+; CHECK-NOT:     call<br>
+;<br>
+; CHECK:         call zeroext i1 @test2_widget(i32* @test2_global)<br>
+; CHECK-NEXT:    br label %[[NEW_BB:.*]]<br>
+;<br>
+; CHECK:       [[NEW_BB]]:<br>
+; CHECK-NEXT:    call zeroext i1 @test2_widget(i32* @test2_global)<br>
+;<br>
+; CHECK:       {{.*}}:<br>
+<br>
+  call void @test2_hoge.1(i32* %arg)<br>
+; CHECK-NEXT:    call void @test2_hoge.1(<br>
+<br>
+  %tmp4 = call zeroext i1 @test2_barney(i32* %tmp2)<br>
+  %tmp5 = zext i1 %tmp4 to i32<br>
+  store i32 %tmp5, i32* %tmp2, align 4<br>
+  %tmp6 = call zeroext i1 @test2_barney(i32* null)<br>
+  call void @test2_ham(i8* %tmp1)<br>
+; CHECK:         call void @test2_ham(<br>
+<br>
+  call void @test2_quux()<br>
+; CHECK-NOT:     call<br>
+;<br>
+; CHECK:         call zeroext i1 @test2_widget(i32* @test2_global)<br>
+; CHECK-NEXT:    br label %[[NEW_BB:.*]]<br>
+;<br>
+; CHECK:       [[NEW_BB]]:<br>
+; CHECK-NEXT:    call zeroext i1 @test2_widget(i32* @test2_global)<br>
+;<br>
+; CHECK:       {{.*}}:<br>
+  ret i1 true<br>
+; CHECK-NEXT:    ret i1 true<br>
+}<br>
+<br>
+define internal void @test2_quux() {<br>
+; CHECK-NOT: @test2_quux<br>
+bb:<br>
+  call void @test2_hoge(i1 (i32*)* @test2_widget)<br>
+  ret void<br>
+}<br>
+<br>
+declare void @test2_hoge.1(i32*)<br>
+<br>
+declare zeroext i1 @test2_barney(i32*)<br>
+<br>
+declare void @test2_ham(i8*)<br>
<br>
Modified: llvm/trunk/unittests/Analysis/LazyCallGraphTest.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/Analysis/LazyCallGraphTest.cpp?rev=307498&r1=307497&r2=307498&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/Analysis/LazyCallGraphTest.cpp?rev=307498&r1=307497&r2=307498&view=diff</a><br>
==============================================================================<br>
--- llvm/trunk/unittests/Analysis/LazyCallGraphTest.cpp (original)<br>
+++ llvm/trunk/unittests/Analysis/LazyCallGraphTest.cpp Sun Jul  9 06:45:11 2017<br>
@@ -1277,9 +1277,10 @@ TEST(LazyCallGraphTest, InternalEdgeMuta<br>
   // be invalidated.<br>
   LazyCallGraph::SCC &AC = *CG.lookupSCC(A);<br>
   LazyCallGraph::SCC &CC = *CG.lookupSCC(C);<br>
-  auto InvalidatedSCCs = RC.switchInternalEdgeToCall(A, C);<br>
-  ASSERT_EQ(1u, InvalidatedSCCs.size());<br>
-  EXPECT_EQ(&AC, InvalidatedSCCs[0]);<br>
+  EXPECT_TRUE(RC.switchInternalEdgeToCall(A, C, [&](ArrayRef<LazyCallGraph::SCC *> MergedCs) {<br>
+    ASSERT_EQ(1u, MergedCs.size());<br>
+    EXPECT_EQ(&AC, MergedCs[0]);<br>
+  }));<br>
   EXPECT_EQ(2, CC.size());<br>
   EXPECT_EQ(&CC, CG.lookupSCC(A));<br>
   EXPECT_EQ(&CC, CG.lookupSCC(C));<br>
@@ -1586,8 +1587,7 @@ TEST(LazyCallGraphTest, InternalRefEdgeT<br>
<br>
   // Switch the ref edge from A -> D to a call edge. This should have no<br>
   // effect as it is already in postorder and no new cycles are formed.<br>
-  auto MergedCs = RC.switchInternalEdgeToCall(A, D);<br>
-  EXPECT_EQ(0u, MergedCs.size());<br>
+  EXPECT_FALSE(RC.switchInternalEdgeToCall(A, D));<br>
   ASSERT_EQ(4, RC.size());<br>
   EXPECT_EQ(&DC, &RC[0]);<br>
   EXPECT_EQ(&BC, &RC[1]);<br>
@@ -1596,8 +1596,7 @@ TEST(LazyCallGraphTest, InternalRefEdgeT<br>
<br>
   // Switch B -> C to a call edge. This doesn't form any new cycles but does<br>
   // require reordering the SCCs.<br>
-  MergedCs = RC.switchInternalEdgeToCall(B, C);<br>
-  EXPECT_EQ(0u, MergedCs.size());<br>
+  EXPECT_FALSE(RC.switchInternalEdgeToCall(B, C));<br>
   ASSERT_EQ(4, RC.size());<br>
   EXPECT_EQ(&DC, &RC[0]);<br>
   EXPECT_EQ(&CC, &RC[1]);<br>
@@ -1605,9 +1604,10 @@ TEST(LazyCallGraphTest, InternalRefEdgeT<br>
   EXPECT_EQ(&AC, &RC[3]);<br>
<br>
   // Switch C -> B to a call edge. This forms a cycle and forces merging SCCs.<br>
-  MergedCs = RC.switchInternalEdgeToCall(C, B);<br>
-  ASSERT_EQ(1u, MergedCs.size());<br>
-  EXPECT_EQ(&CC, MergedCs[0]);<br>
+  EXPECT_TRUE(RC.switchInternalEdgeToCall(C, B, [&](ArrayRef<LazyCallGraph::SCC *> MergedCs) {<br>
+    ASSERT_EQ(1u, MergedCs.size());<br>
+    EXPECT_EQ(&CC, MergedCs[0]);<br>
+  }));<br>
   ASSERT_EQ(3, RC.size());<br>
   EXPECT_EQ(&DC, &RC[0]);<br>
   EXPECT_EQ(&BC, &RC[1]);<br>
@@ -1720,8 +1720,7 @@ TEST(LazyCallGraphTest, InternalRefEdgeT<br>
   // Switch C3 -> B1 to a call edge. This doesn't form any new cycles but does<br>
   // require reordering the SCCs in the face of tricky internal node<br>
   // structures.<br>
-  auto MergedCs = RC.switchInternalEdgeToCall(C3, B1);<br>
-  EXPECT_EQ(0u, MergedCs.size());<br>
+  EXPECT_FALSE(RC.switchInternalEdgeToCall(C3, B1));<br>
   ASSERT_EQ(8, RC.size());<br>
   EXPECT_EQ(&DC, &RC[0]);<br>
   EXPECT_EQ(&B3C, &RC[1]);<br>
@@ -1852,10 +1851,12 @@ TEST(LazyCallGraphTest, InternalRefEdgeT<br>
   // C   F      C   |  |<br>
   //  \ /        \ /   |<br>
   //   G          G    |<br>
-  auto MergedCs = RC.switchInternalEdgeToCall(F, B);<br>
-  ASSERT_EQ(2u, MergedCs.size());<br>
-  EXPECT_EQ(&FC, MergedCs[0]);<br>
-  EXPECT_EQ(&DC, MergedCs[1]);<br>
+  EXPECT_TRUE(RC.switchInternalEdgeToCall(<br>
+      F, B, [&](ArrayRef<LazyCallGraph::SCC *> MergedCs) {<br>
+        ASSERT_EQ(2u, MergedCs.size());<br>
+        EXPECT_EQ(&FC, MergedCs[0]);<br>
+        EXPECT_EQ(&DC, MergedCs[1]);<br>
+      }));<br>
   EXPECT_EQ(3, BC.size());<br>
<br>
   // And make sure the postorder was updated.<br>
<br>
<br>
_______________________________________________<br>
llvm-commits mailing list<br>
<a href="mailto:llvm-commits@lists.llvm.org" target="_blank">llvm-commits@lists.llvm.org</a><br>
<a href="http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits" rel="noreferrer" target="_blank">http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits</a><br>
</blockquote></div></div>
_______________________________________________<br>
llvm-commits mailing list<br>
<a href="mailto:llvm-commits@lists.llvm.org" target="_blank">llvm-commits@lists.llvm.org</a><br>
<a href="http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits" rel="noreferrer" target="_blank">http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits</a><br>
</blockquote></div></div>