[llvm] [IR] Do not track metadata uses of ConstantData (PR #223303)
via llvm-commits
llvm-commits at lists.llvm.org
Sun Sep 13 21:48:37 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-ir
Author: Fangrui Song (MaskRay)
<details>
<summary>Changes</summary>
- **[IR] Simplify ValueAsMetadata::handleRAUW**
- **[IR] Do not track metadata uses of ConstantData**
---
Full diff: https://github.com/llvm/llvm-project/pull/223303.diff
3 Files Affected:
- (modified) llvm/lib/IR/Metadata.cpp (+31-36)
- (modified) llvm/unittests/IR/DebugInfoTest.cpp (+6)
- (modified) llvm/unittests/IR/MetadataTest.cpp (+17-6)
``````````diff
diff --git a/llvm/lib/IR/Metadata.cpp b/llvm/lib/IR/Metadata.cpp
index fc03bf4a87eb1..9cfb0cd00a73d 100644
--- a/llvm/lib/IR/Metadata.cpp
+++ b/llvm/lib/IR/Metadata.cpp
@@ -454,6 +454,13 @@ void ReplaceableUses::resolveAllUses(bool ResolveUsers) {
}
}
+// A ConstantData outlives every use of it and Value::doRAUW rejects it, so
+// a use recorded for one could never fire.
+static bool isTrackedValue(const Metadata &MD) {
+ auto *VAM = dyn_cast<ValueAsMetadata>(&MD);
+ return VAM && !isa<ConstantData>(VAM->getValue());
+}
+
// Special handing of DIArgList is required in the RemoveDIs project, see
// commentry in DIArgList::handleChangedOperand for details. Hidden behind
// conditional compilation to avoid a compile time regression.
@@ -465,7 +472,7 @@ ReplaceableUses *ReplaceableUses::getOrCreate(Metadata &MD) {
}
if (auto ArgList = dyn_cast<DIArgList>(&MD))
return ArgList;
- return dyn_cast<ValueAsMetadata>(&MD);
+ return isTrackedValue(MD) ? cast<ValueAsMetadata>(&MD) : nullptr;
}
ReplaceableUses *ReplaceableUses::getIfExists(Metadata &MD) {
@@ -476,13 +483,13 @@ ReplaceableUses *ReplaceableUses::getIfExists(Metadata &MD) {
}
if (auto ArgList = dyn_cast<DIArgList>(&MD))
return ArgList;
- return dyn_cast<ValueAsMetadata>(&MD);
+ return isTrackedValue(MD) ? cast<ValueAsMetadata>(&MD) : nullptr;
}
bool ReplaceableUses::isReplaceable(const Metadata &MD) {
if (auto *N = dyn_cast<MDNode>(&MD))
return !N->isResolved() || N->isAlwaysReplaceable();
- return isa<ValueAsMetadata>(&MD) || isa<DIArgList>(&MD);
+ return isTrackedValue(MD) || isa<DIArgList>(&MD);
}
static DISubprogram *getLocalFunctionMetadata(Value *V) {
@@ -551,15 +558,13 @@ void ValueAsMetadata::handleRAUW(Value *From, Value *To) {
assert(From != To && "Expected changed value");
assert(&From->getContext() == &To->getContext() && "Expected same context");
- LLVMContext &Context = From->getType()->getContext();
- auto &Store = Context.pImpl->ValuesAsMetadata;
+ auto &Store = From->getContext().pImpl->ValuesAsMetadata;
auto I = Store.find(From);
if (I == Store.end()) {
assert(!From->IsUsedByMD && "Expected From not to be used by metadata");
return;
}
- // Remove old entry from the map.
assert(From->IsUsedByMD && "Expected From to be used by metadata");
From->IsUsedByMD = false;
ValueAsMetadata *MD = I->second;
@@ -567,40 +572,30 @@ void ValueAsMetadata::handleRAUW(Value *From, Value *To) {
assert(MD->getValue() == From && "Expected valid mapping");
Store.erase(I);
- if (isa<LocalAsMetadata>(MD)) {
- if (auto *C = dyn_cast<Constant>(To)) {
- // Local became a constant.
- MD->replaceAllUsesWith(ConstantAsMetadata::get(C));
- delete MD;
- return;
- }
- if (getLocalFunctionMetadata(From) && getLocalFunctionMetadata(To) &&
- getLocalFunctionMetadata(From) != getLocalFunctionMetadata(To)) {
- // DISubprogram changed.
- MD->replaceAllUsesWith(nullptr);
- delete MD;
- return;
- }
- } else if (!isa<Constant>(To)) {
- // Changed to function-local value.
- MD->replaceAllUsesWith(nullptr);
- delete MD;
+ // The uses of a ConstantData node are not tracked, so the node can only be
+ // retyped in place.
+ if (isa<ConstantData>(From)) {
+ assert(isa<ConstantData>(To) && !Store.contains(To) &&
+ "Cannot merge or drop a ConstantData node");
+ To->IsUsedByMD = true;
+ MD->V = To;
+ Store[To] = MD;
return;
}
- auto *&Entry = Store[To];
- if (Entry) {
- // The target already exists.
- MD->replaceAllUsesWith(Entry);
- delete MD;
- return;
+ // Move the uses to To's node. Uses of a function-local value are dropped if
+ // it becomes a local of another function or replaces a constant.
+ Metadata *New = nullptr;
+ if (isa<Constant>(To)) {
+ New = ValueAsMetadata::get(To);
+ } else if (isa<LocalAsMetadata>(MD)) {
+ DISubprogram *FromSP = getLocalFunctionMetadata(From);
+ DISubprogram *ToSP = FromSP ? getLocalFunctionMetadata(To) : nullptr;
+ if (!FromSP || !ToSP || FromSP == ToSP)
+ New = ValueAsMetadata::get(To);
}
-
- // Update MD in place (and update the map entry).
- assert(!To->IsUsedByMD && "Expected this to be the only metadata use");
- To->IsUsedByMD = true;
- MD->V = To;
- Entry = MD;
+ MD->replaceAllUsesWith(New);
+ delete MD;
}
//===----------------------------------------------------------------------===//
diff --git a/llvm/unittests/IR/DebugInfoTest.cpp b/llvm/unittests/IR/DebugInfoTest.cpp
index 4004ab4b080b2..2de3906db0ee8 100644
--- a/llvm/unittests/IR/DebugInfoTest.cpp
+++ b/llvm/unittests/IR/DebugInfoTest.cpp
@@ -234,6 +234,12 @@ TEST(MetadataTest, GlobalConstantMetadataUsedByDbgRecord) {
EXPECT_EQ(DVRs[0]->getNumVariableLocationOps(), 1u);
EXPECT_TRUE(DVRVs.size() == 1);
EXPECT_FALSE(isa<UndefValue>(DVRs[0]->getValue(0)));
+
+ // Uses of the poison replacing @x are not tracked.
+ Value *Poison = PoisonValue::get(V->getType());
+ V->replaceAllUsesWith(Poison);
+ EXPECT_EQ(DVRVs[0]->getValue(0), Poison);
+ EXPECT_TRUE(findDVRValues(Poison).empty());
}
TEST(DbgVariableIntrinsic, EmptyMDIsKillLocation) {
diff --git a/llvm/unittests/IR/MetadataTest.cpp b/llvm/unittests/IR/MetadataTest.cpp
index cb0096d8f4a2c..1de5c836f9c31 100644
--- a/llvm/unittests/IR/MetadataTest.cpp
+++ b/llvm/unittests/IR/MetadataTest.cpp
@@ -122,6 +122,13 @@ class MetadataTest : public testing::Test {
ConstantAsMetadata *getConstantAsMetadata() {
return ConstantAsMetadata::get(getConstant());
}
+ /// Uses of \a ConstantData are not tracked, so tests that replace or delete
+ /// a constant need one with a use list.
+ ConstantAsMetadata *getGlobalAsMetadata() {
+ return ConstantAsMetadata::get(new GlobalVariable(
+ M, Type::getInt8Ty(Context), false, GlobalValue::ExternalLinkage,
+ nullptr, "g" + Twine(Counter++)));
+ }
DIType *getCompositeType() {
return DICompositeType::getDistinct(Context, dwarf::DW_TAG_structure_type,
"", nullptr, 0, nullptr, nullptr, 32,
@@ -5209,9 +5216,12 @@ TEST_F(ValueAsMetadataTest, UpdatesOnRAUW) {
EXPECT_TRUE(MD->getValue() == GV0.get());
ASSERT_TRUE(GV0->use_empty());
+ TrackingMDRef Ref(MD);
std::unique_ptr<GlobalVariable> GV1(
new GlobalVariable(Ty, false, GlobalValue::ExternalLinkage));
+ // RAUW updates `Ref`.
GV0->replaceAllUsesWith(GV1.get());
+ MD = cast<ValueAsMetadata>(Ref.get());
EXPECT_TRUE(MD->getValue() == GV1.get());
}
@@ -5224,11 +5234,13 @@ TEST_F(ValueAsMetadataTest, handleRAUWWithTypeChange) {
auto *I32Poison = PoisonValue::get(Type::getInt32Ty(Context));
auto *I64Poison = PoisonValue::get(Type::getInt64Ty(Context));
auto *MD = ConstantAsMetadata::get(I32Poison);
+ TrackingMDRef Ref(MD);
EXPECT_EQ(MD->getValue(), I32Poison);
EXPECT_NE(MD->getValue(), I64Poison);
ValueAsMetadata::handleRAUW(I32Poison, I64Poison);
+ MD = cast<ConstantAsMetadata>(Ref.get());
EXPECT_NE(MD->getValue(), I32Poison);
EXPECT_EQ(MD->getValue(), I64Poison);
@@ -5253,8 +5265,7 @@ TEST_F(ValueAsMetadataTest, TempTempReplacement) {
TEST_F(ValueAsMetadataTest, CollidingDoubleUpdates) {
// Create a constant.
- ConstantAsMetadata *CI =
- ConstantAsMetadata::get(ConstantInt::get(Context, APInt(8, 0)));
+ ConstantAsMetadata *CI = getGlobalAsMetadata();
// Create a temporary to prevent nodes from resolving.
auto Temp = MDTuple::getTemporary(Context, {});
@@ -5615,7 +5626,7 @@ typedef MetadataTest MDTupleAllocationTest;
TEST_F(MDTupleAllocationTest, Tracking) {
// Make sure that the move constructor and move assignment op
// for MDOperand correctly adjust tracking information.
- auto *Value1 = getConstantAsMetadata();
+ auto *Value1 = getGlobalAsMetadata();
MDTuple *A = MDTuple::getDistinct(Context, {Value1, Value1});
EXPECT_EQ(A->getOperand(0), Value1);
EXPECT_EQ(A->getOperand(1), Value1);
@@ -5631,7 +5642,7 @@ TEST_F(MDTupleAllocationTest, Tracking) {
EXPECT_EQ(NewOps1.get(), static_cast<Metadata *>(Value1));
EXPECT_EQ(NewOps2.get(), static_cast<Metadata *>(Value1));
- auto *Value2 = getConstantAsMetadata();
+ auto *Value2 = getGlobalAsMetadata();
Value *V1 = Value1->getValue();
Value *V2 = Value2->getValue();
ValueAsMetadata::handleRAUW(V1, V2);
@@ -5724,7 +5735,7 @@ TEST_F(MDTupleAllocationTest, Resize) {
TEST_F(MDTupleAllocationTest, Tracking2) {
// Resize a tuple and check that we can still RAUW one of its operands.
- auto *Value1 = getConstantAsMetadata();
+ auto *Value1 = getGlobalAsMetadata();
MDTuple *A = getTuple();
A->push_back(Value1);
A->push_back(Value1);
@@ -5733,7 +5744,7 @@ TEST_F(MDTupleAllocationTest, Tracking2) {
EXPECT_EQ(A->getOperand(1), Value1);
EXPECT_EQ(A->getOperand(2), Value1);
- auto *Value2 = getConstantAsMetadata();
+ auto *Value2 = getGlobalAsMetadata();
Value *V1 = Value1->getValue();
Value *V2 = Value2->getValue();
ValueAsMetadata::handleRAUW(V1, V2);
``````````
</details>
https://github.com/llvm/llvm-project/pull/223303
More information about the llvm-commits
mailing list