llvm.org GIT mirror llvm / 4d97f61
[rs4gc] Optionally directly relocated vector of pointers This patch teaches rewrite-statepoints-for-gc to relocate vector-of-pointers directly rather than trying to split them. This builds on the recent lowering/IR changes to allow vector typed gc.relocates. The motivation for this is that we recently found a bug in the vector splitting code where depending on visit order, a vector might not be relocated at some safepoint. Specifically, the bug is that the splitting code wasn't updating the side tables (live vector) of other safepoints. As a result, a vector which was live at two safepoints might not be updated at one of them. However, if you happened to visit safepoints in post order over the dominator tree, everything worked correctly. Weirdly, it turns out that post order is actually an incredibly common order to visit instructions in in practice. Frustratingly, I have not managed to write a test case which actually hits this. I can only reproduce it in large IR files produced by actual applications. Rather than continue to make this code more complicated, we can remove all of the complexity by just representing the relocation of the entire vector natively in the IR. At the moment, the new functionality is hidden behind a flag. To use this code, you need to pass "-rs4gc-split-vector-values=0". Once I have a chance to stress test with this option and get feedback from other users, my plan is to flip the default and remove the original splitting code. I would just remove it now, but given the rareness of the bug, I figured it was better to leave it in place until the new approach has been stress tested. Differential Revision: http://reviews.llvm.org/D15982 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@257244 91177308-0d34-0410-b5e6-96231b3b80d8 Philip Reames 3 years ago
4 changed file(s) with 143 addition(s) and 14 deletion(s). Raw diff Collapse all Expand all
13161316 assert(Index < LiveVec.size() && "Bug in std::find?");
13171317 return Index;
13181318 };
1319
1320 // All gc_relocate are set to i8 addrspace(1)* type. We originally generated
1321 // unique declarations for each pointer type, but this proved problematic
1322 // because the intrinsic mangling code is incomplete and fragile. Since
1323 // we're moving towards a single unified pointer type anyways, we can just
1324 // cast everything to an i8* of the right address space. A bitcast is added
1325 // later to convert gc_relocate to the actual value's type.
13261319 Module *M = StatepointToken->getModule();
1327 auto AS = cast(LiveVariables[0]->getType())->getAddressSpace();
1328 Type *Types[] = {Type::getInt8PtrTy(M->getContext(), AS)};
1329 Value *GCRelocateDecl =
1330 Intrinsic::getDeclaration(M, Intrinsic::experimental_gc_relocate, Types);
1320
1321 // All gc_relocate are generated as i8 addrspace(1)* (or a vector type whose
1322 // element type is i8 addrspace(1)*). We originally generated unique
1323 // declarations for each pointer type, but this proved problematic because
1324 // the intrinsic mangling code is incomplete and fragile. Since we're moving
1325 // towards a single unified pointer type anyways, we can just cast everything
1326 // to an i8* of the right address space. A bitcast is added later to convert
1327 // gc_relocate to the actual value's type.
1328 auto getGCRelocateDecl = [&] (Type *Ty) {
1329 assert(isHandledGCPointerType(Ty));
1330 auto AS = Ty->getScalarType()->getPointerAddressSpace();
1331 Type *NewTy = Type::getInt8PtrTy(M->getContext(), AS);
1332 if (auto *VT = dyn_cast(Ty))
1333 NewTy = VectorType::get(NewTy, VT->getNumElements());
1334 return Intrinsic::getDeclaration(M, Intrinsic::experimental_gc_relocate,
1335 {NewTy});
1336 };
1337
1338 // Lazily populated map from input types to the canonicalized form mentioned
1339 // in the comment above. This should probably be cached somewhere more
1340 // broadly.
1341 DenseMap TypeToDeclMap;
13311342
13321343 for (unsigned i = 0; i < LiveVariables.size(); i++) {
13331344 // Generate the gc.relocate call and save the result
13341345 Value *BaseIdx =
13351346 Builder.getInt32(LiveStart + FindIndex(LiveVariables, BasePtrs[i]));
13361347 Value *LiveIdx = Builder.getInt32(LiveStart + i);
1348
1349 Type *Ty = LiveVariables[i]->getType();
1350 if (!TypeToDeclMap.count(Ty))
1351 TypeToDeclMap[Ty] = getGCRelocateDecl(Ty);
1352 Value *GCRelocateDecl = TypeToDeclMap[Ty];
13371353
13381354 // only specify a debug name if we can give a useful one
13391355 CallInst *Reloc = Builder.CreateCall(
24772493 #ifndef NDEBUG
24782494 // sanity check
24792495 for (auto *Ptr : Live)
2480 assert(isGCPointerType(Ptr->getType()) && "must be a gc pointer type");
2496 assert(isHandledGCPointerType(Ptr->getType()) &&
2497 "must be a gc pointer type");
24812498 #endif
24822499
24832500 relocationViaAlloca(F, DT, Live, Records);
0 ; Test that we can correctly handle vectors of pointers in statepoint
1 ; rewriting.
2 ; RUN: opt %s -rewrite-statepoints-for-gc -rs4gc-use-deopt-bundles -rs4gc-split-vector-values=0 -S | FileCheck %s
3
4 ; A non-vector relocation for comparison
5 define i64 addrspace(1)* @test(i64 addrspace(1)* %obj) gc "statepoint-example" {
6 ; CHECK-LABEL: test
7 ; CHECK: gc.statepoint
8 ; CHECK-NEXT: gc.relocate
9 ; CHECK-NEXT: bitcast
10 ; CHECK-NEXT: ret i64 addrspace(1)*
11 ; A base vector from a argument
12 entry:
13 call void @do_safepoint() [ "deopt"() ]
14 ret i64 addrspace(1)* %obj
15 }
16
17 ; A vector argument
18 define <2 x i64 addrspace(1)*> @test2(<2 x i64 addrspace(1)*> %obj) gc "statepoint-example" {
19 ; CHECK-LABEL: test2
20 ; CHECK-NEXT: gc.statepoint
21 ; CHECK-NEXT: gc.relocate
22 ; CHECK-NEXT: bitcast
23 ; CHECK-NEXT: ret <2 x i64 addrspace(1)*>
24 call void @do_safepoint() [ "deopt"() ]
25 ret <2 x i64 addrspace(1)*> %obj
26 }
27
28 ; A load
29 define <2 x i64 addrspace(1)*> @test3(<2 x i64 addrspace(1)*>* %ptr) gc "statepoint-example" {
30 ; CHECK-LABEL: test3
31 ; CHECK: load
32 ; CHECK-NEXT: gc.statepoint
33 ; CHECK-NEXT: gc.relocate
34 ; CHECK-NEXT: bitcast
35 ; CHECK-NEXT: ret <2 x i64 addrspace(1)*>
36 entry:
37 %obj = load <2 x i64 addrspace(1)*>, <2 x i64 addrspace(1)*>* %ptr
38 call void @do_safepoint() [ "deopt"() ]
39 ret <2 x i64 addrspace(1)*> %obj
40 }
41
42 declare i32 @fake_personality_function()
43
44 ; When a statepoint is an invoke rather than a call
45 define <2 x i64 addrspace(1)*> @test4(<2 x i64 addrspace(1)*>* %ptr) gc "statepoint-example" personality i32 ()* @fake_personality_function {
46 ; CHECK-LABEL: test4
47 ; CHECK: load
48 ; CHECK-NEXT: gc.statepoint
49 entry:
50 %obj = load <2 x i64 addrspace(1)*>, <2 x i64 addrspace(1)*>* %ptr
51 invoke void @do_safepoint() [ "deopt"() ]
52 to label %normal_return unwind label %exceptional_return
53
54 normal_return: ; preds = %entry
55 ; CHECK-LABEL: normal_return:
56 ; CHECK: gc.relocate
57 ; CHECK-NEXT: bitcast
58 ; CHECK-NEXT: ret <2 x i64 addrspace(1)*>
59 ret <2 x i64 addrspace(1)*> %obj
60
61 exceptional_return: ; preds = %entry
62 ; CHECK-LABEL: exceptional_return:
63 ; CHECK: gc.relocate
64 ; CHECK-NEXT: bitcast
65 ; CHECK-NEXT: ret <2 x i64 addrspace(1)*>
66 %landing_pad4 = landingpad token
67 cleanup
68 ret <2 x i64 addrspace(1)*> %obj
69 }
70
71 ; A newly created vector
72 define <2 x i64 addrspace(1)*> @test5(i64 addrspace(1)* %p) gc "statepoint-example" {
73 ; CHECK-LABEL: test5
74 ; CHECK: insertelement
75 ; CHECK-NEXT: gc.statepoint
76 ; CHECK-NEXT: gc.relocate
77 ; CHECK-NEXT: bitcast
78 ; CHECK-NEXT: ret <2 x i64 addrspace(1)*> %vec.relocated.casted
79 entry:
80 %vec = insertelement <2 x i64 addrspace(1)*> undef, i64 addrspace(1)* %p, i32 0
81 call void @do_safepoint() [ "deopt"() ]
82 ret <2 x i64 addrspace(1)*> %vec
83 }
84
85 ; A merge point
86 define <2 x i64 addrspace(1)*> @test6(i1 %cnd, <2 x i64 addrspace(1)*>* %ptr) gc "statepoint-example" {
87 ; CHECK-LABEL: test6
88 entry:
89 br i1 %cnd, label %taken, label %untaken
90
91 taken: ; preds = %entry
92 %obja = load <2 x i64 addrspace(1)*>, <2 x i64 addrspace(1)*>* %ptr
93 br label %merge
94
95 untaken: ; preds = %entry
96 %objb = load <2 x i64 addrspace(1)*>, <2 x i64 addrspace(1)*>* %ptr
97 br label %merge
98
99 merge: ; preds = %untaken, %taken
100 ; CHECK-LABEL: merge:
101 ; CHECK-NEXT: = phi
102 ; CHECK-NEXT: gc.statepoint
103 ; CHECK-NEXT: gc.relocate
104 ; CHECK-NEXT: bitcast
105 ; CHECK-NEXT: ret <2 x i64 addrspace(1)*>
106 %obj = phi <2 x i64 addrspace(1)*> [ %obja, %taken ], [ %objb, %untaken ]
107 call void @do_safepoint() [ "deopt"() ]
108 ret <2 x i64 addrspace(1)*> %obj
109 }
110
111 declare void @do_safepoint()
0 ; Test that we can correctly handle vectors of pointers in statepoint
11 ; rewriting. Currently, we scalarize, but that's an implementation detail.
2 ; RUN: opt %s -rewrite-statepoints-for-gc -rs4gc-use-deopt-bundles -S | FileCheck %s
2 ; RUN: opt %s -rewrite-statepoints-for-gc -rs4gc-use-deopt-bundles -rs4gc-split-vector-values -S | FileCheck %s
33
44 ; A non-vector relocation for comparison
55
0 ; Test that we can correctly handle vectors of pointers in statepoint
11 ; rewriting. Currently, we scalarize, but that's an implementation detail.
2 ; RUN: opt %s -rewrite-statepoints-for-gc -S | FileCheck %s
2 ; RUN: opt %s -rewrite-statepoints-for-gc -rs4gc-split-vector-values -S | FileCheck %s
33
44 ; A non-vector relocation for comparison
55 define i64 addrspace(1)* @test(i64 addrspace(1)* %obj) gc "statepoint-example" {