Skip to content
This repository was archived by the owner on Mar 28, 2020. It is now read-only.

Commit 8fa25a1

Browse files
committed
IR: Drop uniquing when an MDNode Value operand is deleted
This is a fix for PR28697. An MDNode can indirectly refer to a GlobalValue, through a ConstantAsMetadata. When the GlobalValue is deleted, the MDNode operand is reset to `nullptr`. If the node is uniqued, this can lead to a hard-to-detect cache invalidation in a Metadata map that's shared across an LLVMContext. Consider: 1. A map from Metadata* to `T` called RemappedMDs. 2. A node that references a global variable, `!{i1* @gv}`. 3. Insert `!{i1* @gv} -> SomeT` in the map. 4. Delete `@GV`, leaving behind `!{null} -> SomeT`. Looking up the generic and uninteresting `!{null}` gives you `SomeT`, which is likely related to `@GV`. Worse, `SomeT`'s lifetime may be tied to the deleted `@GV`. This occurs in practice in the shared ValueMap used since r266579 in the IRMover. Other code that handles more than one Module (with different lifetimes) in the same LLVMContext could hit it too. The fix here is a partial revert of r225223: in the rare case that an MDNode operand is a ConstantAsMetadata (i.e., wrapping a node from the Value hierarchy), drop uniquing if it gets replaced with `nullptr`. This changes step #4 above to leave behind `distinct !{null} -> SomeT`, which can't be confused with the generic `!{null}`. In theory, this can cause some churn in the LLVMContext's MDNode uniquing map when Values are being deleted. However: - The number of GlobalValues referenced from uniqued MDNodes is expected to be quite small. E.g., the debug info metadata schema only references GlobalValues from distinct nodes. - Other Constants have the lifetime of the LLVMContext, whose teardown is careful to drop references before deleting the constants. As a result, I don't expect a compile time regression from this change. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@277625 91177308-0d34-0410-b5e6-96231b3b80d8
1 parent 38caf19 commit 8fa25a1

File tree

5 files changed

+81
-5
lines changed

5 files changed

+81
-5
lines changed

lib/IR/Metadata.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -675,8 +675,8 @@ void MDNode::handleChangedOperand(void *Ref, Metadata *New) {
675675
Metadata *Old = getOperand(Op);
676676
setOperand(Op, New);
677677

678-
// Drop uniquing for self-reference cycles.
679-
if (New == this) {
678+
// Drop uniquing for self-reference cycles and deleted constants.
679+
if (New == this || (!New && Old && isa<ConstantAsMetadata>(Old))) {
680680
if (!isResolved())
681681
resolve();
682682
storeDistinctInContext();
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
!named.null = !{!0}
2+
3+
!0 = !{null}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
; RUN: llvm-link -S -o - %s %S/Inputs/metadata-with-global-value-operand.ll | FileCheck %s
2+
; This test confirms that the !{null} from the second module doesn't get mapped
3+
; onto the abandoned !{i1* @var} node from this module.
4+
5+
; CHECK: @var = global
6+
@var = global i1 false
7+
8+
; CHECK: !named.vars = !{!0}
9+
; CHECK: !named.null = !{!1}
10+
!named.vars = !{!0}
11+
12+
; CHECK: !0 = !{i1* @var}
13+
; CHECK: !1 = !{null}
14+
!0 = !{i1* @var}

test/Transforms/GlobalOpt/metadata.ll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,5 @@ declare void @llvm.foo(metadata, metadata) nounwind readnone
2828
; CHECK: !named = !{![[NULL:[0-9]+]]}
2929

3030
!0 = !{i8*** @G}
31-
; CHECK-DAG: ![[NULL]] = !{null}
31+
; CHECK-DAG: ![[NULL]] = distinct !{null}
3232
; CHECK-DAG: ![[EMPTY]] = !{}

unittests/IR/MetadataTest.cpp

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,40 @@ TEST_F(MDNodeTest, DistinctOnUniquingCollision) {
449449
EXPECT_FALSE(Wrapped1->isDistinct());
450450
}
451451

452+
TEST_F(MDNodeTest, UniquedOnDeletedOperand) {
453+
// temp !{}
454+
TempMDTuple T = MDTuple::getTemporary(Context, None);
455+
456+
// !{temp !{}}
457+
Metadata *Ops[] = {T.get()};
458+
MDTuple *N = MDTuple::get(Context, Ops);
459+
460+
// !{temp !{}} => !{null}
461+
T.reset();
462+
ASSERT_TRUE(N->isUniqued());
463+
Metadata *NullOps[] = {nullptr};
464+
ASSERT_EQ(N, MDTuple::get(Context, NullOps));
465+
}
466+
467+
TEST_F(MDNodeTest, DistinctOnDeletedValueOperand) {
468+
// i1* @GV
469+
Type *Ty = Type::getInt1PtrTy(Context);
470+
std::unique_ptr<GlobalVariable> GV(
471+
new GlobalVariable(Ty, false, GlobalValue::ExternalLinkage));
472+
ConstantAsMetadata *Op = ConstantAsMetadata::get(GV.get());
473+
474+
// !{i1* @GV}
475+
Metadata *Ops[] = {Op};
476+
MDTuple *N = MDTuple::get(Context, Ops);
477+
478+
// !{i1* @GV} => !{null}
479+
GV.reset();
480+
ASSERT_TRUE(N->isDistinct());
481+
ASSERT_EQ(nullptr, N->getOperand(0));
482+
Metadata *NullOps[] = {nullptr};
483+
ASSERT_NE(N, MDTuple::get(Context, NullOps));
484+
}
485+
452486
TEST_F(MDNodeTest, getDistinct) {
453487
// !{}
454488
MDNode *Empty = MDNode::get(Context, None);
@@ -669,7 +703,7 @@ TEST_F(MDNodeTest, replaceWithUniquedResolvingOperand) {
669703
EXPECT_TRUE(N->isResolved());
670704
}
671705

672-
TEST_F(MDNodeTest, replaceWithUniquedChangingOperand) {
706+
TEST_F(MDNodeTest, replaceWithUniquedDeletedOperand) {
673707
// i1* @GV
674708
Type *Ty = Type::getInt1PtrTy(Context);
675709
std::unique_ptr<GlobalVariable> GV(
@@ -686,8 +720,33 @@ TEST_F(MDNodeTest, replaceWithUniquedChangingOperand) {
686720

687721
// !{i1* @GV} => !{null}
688722
GV.reset();
689-
ASSERT_TRUE(N->isUniqued());
723+
ASSERT_TRUE(N->isDistinct());
724+
ASSERT_EQ(nullptr, N->getOperand(0));
690725
Metadata *NullOps[] = {nullptr};
726+
ASSERT_NE(N, MDTuple::get(Context, NullOps));
727+
}
728+
729+
TEST_F(MDNodeTest, replaceWithUniquedChangedOperand) {
730+
// i1* @GV
731+
Type *Ty = Type::getInt1PtrTy(Context);
732+
std::unique_ptr<GlobalVariable> GV(
733+
new GlobalVariable(Ty, false, GlobalValue::ExternalLinkage));
734+
ConstantAsMetadata *Op = ConstantAsMetadata::get(GV.get());
735+
736+
// temp !{i1* @GV}
737+
Metadata *Ops[] = {Op};
738+
MDTuple *N = MDTuple::getTemporary(Context, Ops).release();
739+
740+
// temp !{i1* @GV} => !{i1* @GV}
741+
ASSERT_EQ(N, MDNode::replaceWithUniqued(TempMDTuple(N)));
742+
ASSERT_TRUE(N->isUniqued());
743+
744+
// !{i1* @GV} => !{i1* @GV2}
745+
std::unique_ptr<GlobalVariable> GV2(
746+
new GlobalVariable(Ty, false, GlobalValue::ExternalLinkage));
747+
GV->replaceAllUsesWith(GV2.get());
748+
ASSERT_TRUE(N->isUniqued());
749+
Metadata *NullOps[] = {ConstantAsMetadata::get(GV2.get())};
691750
ASSERT_EQ(N, MDTuple::get(Context, NullOps));
692751
}
693752

0 commit comments

Comments
 (0)