IR: Add MDNode::replaceWithPermanent()
authorDuncan P. N. Exon Smith <dexonsmith@apple.com>
Tue, 10 Feb 2015 19:13:46 +0000 (19:13 +0000)
committerDuncan P. N. Exon Smith <dexonsmith@apple.com>
Tue, 10 Feb 2015 19:13:46 +0000 (19:13 +0000)
Add new API for converting temporaries that may self-reference.
Self-referencing nodes are not allowed to be uniqued, so sending them
into `replaceWithUniqued()` is dangerous (and this commit adds
assertions that prevent it).

`replaceWithPermanent()` has similar semantics to `get()` followed by
calls to `replaceOperandWith()`.  In particular, if there's a
self-reference, it returns a distinct node; otherwise, it returns a
uniqued one.  Like `replaceWithUniqued()` and `replaceWithDistinct()`
(well, it calls out to them) it mutates the temporary node in place if
possible, only calling `replaceAllUsesWith()` on a uniquing collision.

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

include/llvm/IR/Metadata.h
lib/IR/Metadata.cpp
unittests/IR/MetadataTest.cpp

index 5ccfc113ac290d3cecc08af7ee66d333c9d561f1..3291f31278b00d78274f9daa41f577b19506824f 100644 (file)
@@ -789,10 +789,22 @@ public:
   /// \pre No operands (or operands' operands, etc.) have \a isTemporary().
   void resolveCycles();
 
+  /// \brief Replace a temporary node with a permanent one.
+  ///
+  /// Try to create a uniqued version of \c N -- in place, if possible -- and
+  /// return it.  If \c N cannot be uniqued, return a distinct node instead.
+  template <class T>
+  static typename std::enable_if<std::is_base_of<MDNode, T>::value, T *>::type
+  replaceWithPermanent(std::unique_ptr<T, TempMDNodeDeleter> N) {
+    return cast<T>(N.release()->replaceWithPermanentImpl());
+  }
+
   /// \brief Replace a temporary node with a uniqued one.
   ///
   /// Create a uniqued version of \c N -- in place, if possible -- and return
   /// it.  Takes ownership of the temporary node.
+  ///
+  /// \pre N does not self-reference.
   template <class T>
   static typename std::enable_if<std::is_base_of<MDNode, T>::value, T *>::type
   replaceWithUniqued(std::unique_ptr<T, TempMDNodeDeleter> N) {
@@ -810,6 +822,7 @@ public:
   }
 
 private:
+  MDNode *replaceWithPermanentImpl();
   MDNode *replaceWithUniquedImpl();
   MDNode *replaceWithDistinctImpl();
 
index d5be35a53a0b143275819a4730f7c1eb3538b702..0ad3c5c04bae97a886c8167341a0273aa5b85dd9 100644 (file)
@@ -518,9 +518,23 @@ void MDNode::resolveCycles() {
   }
 }
 
+static bool hasSelfReference(MDNode *N) {
+  for (Metadata *MD : N->operands())
+    if (MD == N)
+      return true;
+  return false;
+}
+
+MDNode *MDNode::replaceWithPermanentImpl() {
+  if (hasSelfReference(this))
+    return replaceWithDistinctImpl();
+  return replaceWithUniquedImpl();
+}
+
 MDNode *MDNode::replaceWithUniquedImpl() {
   // Try to uniquify in place.
   MDNode *UniquedNode = uniquify();
+
   if (UniquedNode == this) {
     makeUniqued();
     return this;
@@ -633,6 +647,8 @@ template <class NodeTy> struct MDNode::HasCachedHash {
 };
 
 MDNode *MDNode::uniquify() {
+  assert(!hasSelfReference(this) && "Cannot uniquify a self-referencing node");
+
   // Try to insert into uniquing store.
   switch (getMetadataID()) {
   default:
index 9bc8164dd48bd11fd81dacb504373186db5284f9..e8e6d68c5fa36e049000b9d3626082d46c8b9337 100644 (file)
@@ -518,6 +518,44 @@ TEST_F(MDNodeTest, replaceWithDistinct) {
   }
 }
 
+TEST_F(MDNodeTest, replaceWithPermanent) {
+  Metadata *Ops[] = {nullptr};
+  auto Temp = MDTuple::getTemporary(Context, Ops);
+  auto *T = Temp.get();
+
+  // U is a normal, uniqued node that references T.
+  auto *U = MDTuple::get(Context, T);
+  EXPECT_TRUE(U->isUniqued());
+
+  // Make Temp self-referencing.
+  Temp->replaceOperandWith(0, T);
+
+  // Try to uniquify Temp.  This should, despite the name in the API, give a
+  // 'distinct' node, since self-references aren't allowed to be uniqued.
+  //
+  // Since it's distinct, N should have the same address as when it was a
+  // temporary (i.e., be equal to T not U).
+  auto *N = MDNode::replaceWithPermanent(std::move(Temp));
+  EXPECT_EQ(N, T);
+  EXPECT_TRUE(N->isDistinct());
+
+  // U should be the canonical unique node with N as the argument.
+  EXPECT_EQ(U, MDTuple::get(Context, N));
+  EXPECT_TRUE(U->isUniqued());
+
+  // This temporary should collide with U when replaced, but it should still be
+  // uniqued.
+  EXPECT_EQ(U, MDNode::replaceWithPermanent(MDTuple::getTemporary(Context, N)));
+  EXPECT_TRUE(U->isUniqued());
+
+  // This temporary should become a new uniqued node.
+  auto Temp2 = MDTuple::getTemporary(Context, U);
+  auto *V = Temp2.get();
+  EXPECT_EQ(V, MDNode::replaceWithPermanent(std::move(Temp2)));
+  EXPECT_TRUE(V->isUniqued());
+  EXPECT_EQ(U, V->getOperand(0));
+}
+
 TEST_F(MDNodeTest, deleteTemporaryWithTrackingRef) {
   TrackingMDRef Ref;
   EXPECT_EQ(nullptr, Ref.get());