IR: Add test for handleChangedOperand() recursion
authorDuncan P. N. Exon Smith <dexonsmith@apple.com>
Mon, 12 Jan 2015 19:22:04 +0000 (19:22 +0000)
committerDuncan P. N. Exon Smith <dexonsmith@apple.com>
Mon, 12 Jan 2015 19:22:04 +0000 (19:22 +0000)
Turns out this can happen.  Remove the `FIXME` and add a testcase that
crashes without the extra logic.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@225657 91177308-0d34-0410-b5e6-96231b3b80d8

lib/IR/Metadata.cpp
unittests/IR/MetadataTest.cpp

index 1ae9a96fe46b4376ebc375fb42f5adf8a7f8dcbf..baa0b78762c2b930b1251657a74faf98878f9561 100644 (file)
@@ -537,8 +537,6 @@ void GenericMDNode::handleChangedOperand(void *Ref, Metadata *New) {
   if (InRAUW) {
     // We just hit a recursion due to RAUW.  Set the operand and move on, since
     // we're about to be deleted.
-    //
-    // FIXME: Can this cycle really happen?
     setOperand(Op, New);
     return;
   }
index a09cc9cfd7d81430e2b23b69f1d328d7c3fcb40c..ecda5e8820e7eed57d65bb4a7862acbae953159f 100644 (file)
@@ -293,6 +293,69 @@ TEST_F(MDNodeTest, getDistinctWithUnresolvedOperands) {
   EXPECT_EQ(Empty, Distinct->getOperand(0));
 }
 
+TEST_F(MDNodeTest, handleChangedOperandRecursion) {
+  // !0 = !{}
+  MDNode *N0 = MDNode::get(Context, None);
+
+  // !1 = !{!3, null}
+  MDNodeFwdDecl *Temp3 = MDNode::getTemporary(Context, None);
+  Metadata *Ops1[] = {Temp3, nullptr};
+  MDNode *N1 = MDNode::get(Context, Ops1);
+
+  // !2 = !{!3, !0}
+  Metadata *Ops2[] = {Temp3, N0};
+  MDNode *N2 = MDNode::get(Context, Ops2);
+
+  // !3 = !{!2}
+  Metadata *Ops3[] = {N2};
+  MDNode *N3 = MDNode::get(Context, Ops3);
+  Temp3->replaceAllUsesWith(N3);
+
+  // !4 = !{!1}
+  Metadata *Ops4[] = {N1};
+  MDNode *N4 = MDNode::get(Context, Ops4);
+
+  // Confirm that the cycle prevented RAUW from getting dropped.
+  EXPECT_TRUE(N0->isResolved());
+  EXPECT_FALSE(N1->isResolved());
+  EXPECT_FALSE(N2->isResolved());
+  EXPECT_FALSE(N3->isResolved());
+  EXPECT_FALSE(N4->isResolved());
+
+  // Create a couple of distinct nodes to observe what's going on.
+  //
+  // !5 = distinct !{!2}
+  // !6 = distinct !{!3}
+  Metadata *Ops5[] = {N2};
+  MDNode *N5 = MDNode::getDistinct(Context, Ops5);
+  Metadata *Ops6[] = {N3};
+  MDNode *N6 = MDNode::getDistinct(Context, Ops6);
+
+  // Mutate !2 to look like !1, causing a uniquing collision (and an RAUW).
+  // This will ripple up, with !3 colliding with !4, and RAUWing.  Since !2
+  // references !3, this can cause a re-entry of handleChangedOperand() when !3
+  // is not ready for it.
+  //
+  // !2->replaceOperandWith(1, nullptr)
+  // !2: !{!3, !0} => !{!3, null}
+  // !2->replaceAllUsesWith(!1)
+  // !3: !{!2] => !{!1}
+  // !3->replaceAllUsesWith(!4)
+  N2->replaceOperandWith(1, nullptr);
+
+  // If all has gone well, N2 and N3 will have been RAUW'ed and deleted from
+  // under us.  Just check that the other nodes are sane.
+  //
+  // !1 = !{!4, null}
+  // !4 = !{!1}
+  // !5 = distinct !{!1}
+  // !6 = distinct !{!4}
+  EXPECT_EQ(N4, N1->getOperand(0));
+  EXPECT_EQ(N1, N4->getOperand(0));
+  EXPECT_EQ(N1, N5->getOperand(0));
+  EXPECT_EQ(N4, N6->getOperand(0));
+}
+
 typedef MetadataTest MetadataAsValueTest;
 
 TEST_F(MetadataAsValueTest, MDNode) {