llvm.org GIT mirror llvm / 2880b72
[RS4GC] Strip off invariant.start because memory locations arent invariant Summary: Invariant.start on memory locations has the property that the memory location is unchanging. However, this is not true in the face of rewriting statepoints for GC. Teach RS4GC about removing invariant.start so that optimizations after RS4GC does not incorrect sink a load from the memory location past a statepoint. Added test showcasing the issue. Reviewers: reames, apilipenko, dneilson Subscribers: llvm-commits Differential Revision: https://reviews.llvm.org/D39388 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@317215 91177308-0d34-0410-b5e6-96231b3b80d8 Anna Thomas 1 year, 10 months ago
2 changed file(s) with 93 addition(s) and 10 deletion(s). Raw diff Collapse all Expand all
124124 Changed |= runOnFunction(F);
125125
126126 if (Changed) {
127 // stripNonValidAttributesAndMetadata asserts that shouldRewriteStatepointsIn
127 // stripNonValidData asserts that shouldRewriteStatepointsIn
128128 // returns true for at least one function in the module. Since at least
129129 // one function changed, we know that the precondition is satisfied.
130 stripNonValidAttributesAndMetadata(M);
130 stripNonValidData(M);
131131 }
132132
133133 return Changed;
145145 /// metadata implying dereferenceability that are no longer valid/correct after
146146 /// RewriteStatepointsForGC has run. This is because semantically, after
147147 /// RewriteStatepointsForGC runs, all calls to gc.statepoint "free" the entire
148 /// heap. stripNonValidAttributesAndMetadata (conservatively) restores
148 /// heap. stripNonValidData (conservatively) restores
149149 /// correctness by erasing all attributes in the module that externally imply
150150 /// dereferenceability. Similar reasoning also applies to the noalias
151151 /// attributes and metadata. gc.statepoint can touch the entire heap including
152152 /// noalias objects.
153 void stripNonValidAttributesAndMetadata(Module &M);
154
155 // Helpers for stripNonValidAttributesAndMetadata
156 void stripNonValidAttributesAndMetadataFromBody(Function &F);
153 /// Apart from attributes and metadata, we also remove instructions that imply
154 /// constant physical memory: llvm.invariant.start.
155 void stripNonValidData(Module &M);
156
157 // Helpers for stripNonValidData
158 void stripNonValidDataFromBody(Function &F);
157159 void stripNonValidAttributesFromPrototype(Function &F);
158160
159161 // Certain metadata on instructions are invalid after running RS4GC.
23842386 I.dropUnknownNonDebugMetadata(ValidMetadataAfterRS4GC);
23852387 }
23862388
2387 void RewriteStatepointsForGC::stripNonValidAttributesAndMetadataFromBody(Function &F) {
2389 void RewriteStatepointsForGC::stripNonValidDataFromBody(Function &F) {
23882390 if (F.empty())
23892391 return;
23902392
23912393 LLVMContext &Ctx = F.getContext();
23922394 MDBuilder Builder(Ctx);
23932395
2396 // Set of invariantstart instructions that we need to remove.
2397 // Use this to avoid invalidating the instruction iterator.
2398 SmallVector InvariantStartInstructions;
2399
23942400 for (Instruction &I : instructions(F)) {
2401 // invariant.start on memory location implies that the referenced memory
2402 // location is constant and unchanging. This is no longer true after
2403 // RewriteStatepointsForGC runs because there can be calls to gc.statepoint
2404 // which frees the entire heap and the presence of invariant.start allows
2405 // the optimizer to sink the load of a memory location past a statepoint,
2406 // which is incorrect.
2407 if (auto *II = dyn_cast(&I))
2408 if (II->getIntrinsicID() == Intrinsic::invariant_start) {
2409 InvariantStartInstructions.push_back(II);
2410 continue;
2411 }
2412
23952413 if (const MDNode *MD = I.getMetadata(LLVMContext::MD_tbaa)) {
23962414 assert(MD->getNumOperands() < 5 && "unrecognized metadata shape!");
23972415 bool IsImmutableTBAA =
24202438 if (isa(CS.getType()))
24212439 RemoveNonValidAttrAtIndex(Ctx, CS, AttributeList::ReturnIndex);
24222440 }
2441 }
2442
2443 // Delete the invariant.start instructions and any corresponding uses that
2444 // don't have further uses, for example invariant.end.
2445 for (auto *II : InvariantStartInstructions) {
2446 for (auto *U : II->users())
2447 if (auto *I = dyn_cast(U))
2448 if (U->hasNUses(0))
2449 I->eraseFromParent();
2450 // We cannot just delete the remaining uses of II, so we RAUW undef.
2451 II->replaceAllUsesWith(UndefValue::get(II->getType()));
2452 II->eraseFromParent();
24232453 }
24242454 }
24252455
24372467 return false;
24382468 }
24392469
2440 void RewriteStatepointsForGC::stripNonValidAttributesAndMetadata(Module &M) {
2470 void RewriteStatepointsForGC::stripNonValidData(Module &M) {
24412471 #ifndef NDEBUG
24422472 assert(llvm::any_of(M, shouldRewriteStatepointsIn) && "precondition!");
24432473 #endif
24462476 stripNonValidAttributesFromPrototype(F);
24472477
24482478 for (Function &F : M)
2449 stripNonValidAttributesAndMetadataFromBody(F);
2479 stripNonValidDataFromBody(F);
24502480 }
24512481
24522482 bool RewriteStatepointsForGC::runOnFunction(Function &F) {
7474 ret void
7575 }
7676
77 ; invariant.start allows us to sink the load past the baz statepoint call into taken block, which is
78 ; incorrect. remove the invariant.start and RAUW undef.
79 define void @test_inv_start(i1 %cond, i32 addrspace(1)* addrspace(1)* %p, i32 %x, i32 addrspace(1)* %q) gc "statepoint-example" {
80 ; CHECK-LABEL: test_inv_start
81 ; CHECK-NOT: invariant.start
82 ; CHECK: gc.statepoint
83 %v1 = load i32 addrspace(1)*, i32 addrspace(1)* addrspace(1)* %p
84 %invst = call {}* @llvm.invariant.start.p1i32(i64 1, i32 addrspace(1)* %v1)
85 %v2 = load i32, i32 addrspace(1)* %v1
86 call void @baz(i32 %x)
87 br i1 %cond, label %taken, label %untaken
88
89 ; CHECK-LABEL: taken:
90 ; CHECK-NOT: llvm.invariant.end
91 taken:
92 store i32 %v2, i32 addrspace(1)* %q, align 16
93 call void @llvm.invariant.end.p1i32({}* %invst, i64 4, i32 addrspace(1)* %v1)
94 ret void
95
96 ; CHECK-LABEL: untaken:
97 ; CHECK: gc.statepoint
98 untaken:
99 %foo = call i32 @escaping.invariant.start({}* %invst)
100 call void @dummy(i32 %foo)
101 ret void
102 }
103
104 ; invariant.start and end is removed. No other uses.
105 define void @test_inv_start2(i1 %cond, i32 addrspace(1)* addrspace(1)* %p, i32 %x, i32 addrspace(1)* %q) gc "statepoint-example" {
106 ; CHECK-LABEL: test_inv_start2
107 ; CHECK-NOT: invariant.start
108 ; CHECK: gc.statepoint
109 %v1 = load i32 addrspace(1)*, i32 addrspace(1)* addrspace(1)* %p
110 %invst = call {}* @llvm.invariant.start.p1i32(i64 1, i32 addrspace(1)* %v1)
111 %v2 = load i32, i32 addrspace(1)* %v1
112 call void @baz(i32 %x)
113 br i1 %cond, label %taken, label %untaken
114
115 ; CHECK-LABEL: taken:
116 ; CHECK-NOT: llvm.invariant.end
117 taken:
118 store i32 %v2, i32 addrspace(1)* %q, align 16
119 call void @llvm.invariant.end.p1i32({}* %invst, i64 4, i32 addrspace(1)* %v1)
120 ret void
121
122 ; CHECK-LABEL: untaken:
123 untaken:
124 ret void
125 }
126 declare {}* @llvm.invariant.start.p1i32(i64, i32 addrspace(1)* nocapture) nounwind readonly
127 declare void @llvm.invariant.end.p1i32({}*, i64, i32 addrspace(1)* nocapture) nounwind
128 declare i32 @escaping.invariant.start({}*) nounwind
129 declare void @dummy(i32)
77130 declare token @llvm.experimental.gc.statepoint.p0f_isVoidi32f(i64, i32, void (i32)*, i32, i32, ...)
78131
79132 ; Function Attrs: nounwind readonly