diff --git a/include/llvm/IR/Metadata.h b/include/llvm/IR/Metadata.h index 5ccfc113ac2..3291f31278b 100644 --- a/include/llvm/IR/Metadata.h +++ b/include/llvm/IR/Metadata.h @@ -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 + static typename std::enable_if::value, T *>::type + replaceWithPermanent(std::unique_ptr N) { + return cast(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 static typename std::enable_if::value, T *>::type replaceWithUniqued(std::unique_ptr N) { @@ -810,6 +822,7 @@ public: } private: + MDNode *replaceWithPermanentImpl(); MDNode *replaceWithUniquedImpl(); MDNode *replaceWithDistinctImpl(); diff --git a/lib/IR/Metadata.cpp b/lib/IR/Metadata.cpp index d5be35a53a0..0ad3c5c04ba 100644 --- a/lib/IR/Metadata.cpp +++ b/lib/IR/Metadata.cpp @@ -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 struct MDNode::HasCachedHash { }; MDNode *MDNode::uniquify() { + assert(!hasSelfReference(this) && "Cannot uniquify a self-referencing node"); + // Try to insert into uniquing store. switch (getMetadataID()) { default: diff --git a/unittests/IR/MetadataTest.cpp b/unittests/IR/MetadataTest.cpp index 9bc8164dd48..e8e6d68c5fa 100644 --- a/unittests/IR/MetadataTest.cpp +++ b/unittests/IR/MetadataTest.cpp @@ -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());