llvm.org GIT mirror llvm / b11315c
[RS4GC] "Constant fold" the rs4gc-split-vector-values flag This flag was part of a migration to a new means of handling vectors-of-points which was described in the llvm-dev thread "FYI: Relocating vector of pointers". The old code path has been off by default for a while without complaints, so time to cleanup. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@261569 91177308-0d34-0410-b5e6-96231b3b80d8 Philip Reames 3 years ago
3 changed file(s) with 1 addition(s) and 324 deletion(s). Raw diff Collapse all Expand all
7373 static cl::opt
7474 AllowStatepointWithNoDeoptInfo("rs4gc-allow-statepoint-with-no-deopt-info",
7575 cl::Hidden, cl::init(true));
76
77 /// Should we split vectors of pointers into their individual elements? This
78 /// is known to be buggy, but the alternate implementation isn't yet ready.
79 /// This is purely to provide a debugging and dianostic hook until the vector
80 /// split is replaced with vector relocations.
81 static cl::opt UseVectorSplit("rs4gc-split-vector-values", cl::Hidden,
82 cl::init(false));
8376
8477 namespace {
8578 struct RewriteStatepointsForGC : public ModulePass {
18181811 }
18191812 }
18201813
1821 /// Remove any vector of pointers from the live set by scalarizing them over the
1822 /// statepoint instruction. Adds the scalarized pieces to the live set. It
1823 /// would be preferable to include the vector in the statepoint itself, but
1824 /// the lowering code currently does not handle that. Extending it would be
1825 /// slightly non-trivial since it requires a format change. Given how rare
1826 /// such cases are (for the moment?) scalarizing is an acceptable compromise.
1827 static void splitVectorValues(Instruction *StatepointInst,
1828 StatepointLiveSetTy &LiveSet,
1829 DenseMap& PointerToBase,
1830 DominatorTree &DT) {
1831 SmallVector ToSplit;
1832 for (Value *V : LiveSet)
1833 if (isa(V->getType()))
1834 ToSplit.push_back(V);
1835
1836 if (ToSplit.empty())
1837 return;
1838
1839 DenseMap> ElementMapping;
1840
1841 Function &F = *(StatepointInst->getParent()->getParent());
1842
1843 DenseMap AllocaMap;
1844 // First is normal return, second is exceptional return (invoke only)
1845 DenseMap> Replacements;
1846 for (Value *V : ToSplit) {
1847 AllocaInst *Alloca =
1848 new AllocaInst(V->getType(), "", F.getEntryBlock().getFirstNonPHI());
1849 AllocaMap[V] = Alloca;
1850
1851 VectorType *VT = cast(V->getType());
1852 IRBuilder<> Builder(StatepointInst);
1853 SmallVector Elements;
1854 for (unsigned i = 0; i < VT->getNumElements(); i++)
1855 Elements.push_back(Builder.CreateExtractElement(V, Builder.getInt32(i)));
1856 ElementMapping[V] = Elements;
1857
1858 auto InsertVectorReform = [&](Instruction *IP) {
1859 Builder.SetInsertPoint(IP);
1860 Builder.SetCurrentDebugLocation(IP->getDebugLoc());
1861 Value *ResultVec = UndefValue::get(VT);
1862 for (unsigned i = 0; i < VT->getNumElements(); i++)
1863 ResultVec = Builder.CreateInsertElement(ResultVec, Elements[i],
1864 Builder.getInt32(i));
1865 return ResultVec;
1866 };
1867
1868 if (isa(StatepointInst)) {
1869 BasicBlock::iterator Next(StatepointInst);
1870 Next++;
1871 Instruction *IP = &*(Next);
1872 Replacements[V].first = InsertVectorReform(IP);
1873 Replacements[V].second = nullptr;
1874 } else {
1875 InvokeInst *Invoke = cast(StatepointInst);
1876 // We've already normalized - check that we don't have shared destination
1877 // blocks
1878 BasicBlock *NormalDest = Invoke->getNormalDest();
1879 assert(!isa(NormalDest->begin()));
1880 BasicBlock *UnwindDest = Invoke->getUnwindDest();
1881 assert(!isa(UnwindDest->begin()));
1882 // Insert insert element sequences in both successors
1883 Instruction *IP = &*(NormalDest->getFirstInsertionPt());
1884 Replacements[V].first = InsertVectorReform(IP);
1885 IP = &*(UnwindDest->getFirstInsertionPt());
1886 Replacements[V].second = InsertVectorReform(IP);
1887 }
1888 }
1889
1890 for (Value *V : ToSplit) {
1891 AllocaInst *Alloca = AllocaMap[V];
1892
1893 // Capture all users before we start mutating use lists
1894 SmallVector Users;
1895 for (User *U : V->users())
1896 Users.push_back(cast(U));
1897
1898 for (Instruction *I : Users) {
1899 if (auto Phi = dyn_cast(I)) {
1900 for (unsigned i = 0; i < Phi->getNumIncomingValues(); i++)
1901 if (V == Phi->getIncomingValue(i)) {
1902 LoadInst *Load = new LoadInst(
1903 Alloca, "", Phi->getIncomingBlock(i)->getTerminator());
1904 Phi->setIncomingValue(i, Load);
1905 }
1906 } else {
1907 LoadInst *Load = new LoadInst(Alloca, "", I);
1908 I->replaceUsesOfWith(V, Load);
1909 }
1910 }
1911
1912 // Store the original value and the replacement value into the alloca
1913 StoreInst *Store = new StoreInst(V, Alloca);
1914 if (auto I = dyn_cast(V))
1915 Store->insertAfter(I);
1916 else
1917 Store->insertAfter(Alloca);
1918
1919 // Normal return for invoke, or call return
1920 Instruction *Replacement = cast(Replacements[V].first);
1921 (new StoreInst(Replacement, Alloca))->insertAfter(Replacement);
1922 // Unwind return for invoke only
1923 Replacement = cast_or_null(Replacements[V].second);
1924 if (Replacement)
1925 (new StoreInst(Replacement, Alloca))->insertAfter(Replacement);
1926 }
1927
1928 // apply mem2reg to promote alloca to SSA
1929 SmallVector Allocas;
1930 for (Value *V : ToSplit)
1931 Allocas.push_back(AllocaMap[V]);
1932 PromoteMemToReg(Allocas, DT);
1933
1934 // Update our tracking of live pointers and base mappings to account for the
1935 // changes we just made.
1936 for (Value *V : ToSplit) {
1937 auto &Elements = ElementMapping[V];
1938
1939 LiveSet.erase(V);
1940 LiveSet.insert(Elements.begin(), Elements.end());
1941 // We need to update the base mapping as well.
1942 assert(PointerToBase.count(V));
1943 Value *OldBase = PointerToBase[V];
1944 auto &BaseElements = ElementMapping[OldBase];
1945 PointerToBase.erase(V);
1946 assert(Elements.size() == BaseElements.size());
1947 for (unsigned i = 0; i < Elements.size(); i++) {
1948 Value *Elem = Elements[i];
1949 PointerToBase[Elem] = BaseElements[i];
1950 }
1951 }
1952 }
1953
19541814 // Helper function for the "rematerializeLiveValues". It walks use chain
19551815 // starting from the "CurrentValue" until it meets "BaseValue". Only "simple"
19561816 // values are visited (currently it is GEP's and casts). Returns true if it
22662126 CI->eraseFromParent();
22672127
22682128 Holders.clear();
2269
2270 // Do a limited scalarization of any live at safepoint vector values which
2271 // contain pointers. This enables this pass to run after vectorization at
2272 // the cost of some possible performance loss. Note: This is known to not
2273 // handle updating of the side tables correctly which can lead to relocation
2274 // bugs when the same vector is live at multiple statepoints. We're in the
2275 // process of implementing the alternate lowering - relocating the
2276 // vector-of-pointers as first class item and updating the backend to
2277 // understand that - but that's not yet complete.
2278 if (UseVectorSplit)
2279 for (size_t i = 0; i < Records.size(); i++) {
2280 PartiallyConstructedSafepointRecord &Info = Records[i];
2281 Instruction *Statepoint = ToUpdate[i].getInstruction();
2282 splitVectorValues(cast(Statepoint), Info.LiveSet,
2283 Info.PointerToBase, DT);
2284 }
22852129
22862130 // In order to reduce live set of statepoint we might choose to rematerialize
22872131 // some values instead of relocating them. This is purely an optimization and
0 ; Test that we can correctly handle vectors of pointers in statepoint
11 ; rewriting.
2 ; RUN: opt < %s -rewrite-statepoints-for-gc -rs4gc-split-vector-values=0 -S | FileCheck %s
2 ; RUN: opt < %s -rewrite-statepoints-for-gc -S | FileCheck %s
33
44 ; A non-vector relocation for comparison
55 define i64 addrspace(1)* @test(i64 addrspace(1)* %obj) gc "statepoint-example" {
+0
-167
test/Transforms/RewriteStatepointsForGC/live-vector.ll less more
None ; Test that we can correctly handle vectors of pointers in statepoint
1 ; rewriting. Currently, we scalarize, but that's an implementation detail.
2 ; RUN: opt < %s -rewrite-statepoints-for-gc -rs4gc-split-vector-values -S | FileCheck %s
3
4 ; A non-vector relocation for comparison
5
6 define i64 addrspace(1)* @test(i64 addrspace(1)* %obj) gc "statepoint-example" {
7 ; CHECK-LABEL: test
8 ; CHECK: gc.statepoint
9 ; CHECK-NEXT: gc.relocate
10 ; CHECK-NEXT: bitcast
11 ; CHECK-NEXT: ret i64 addrspace(1)* %obj.relocated.casted
12 ; A base vector from a argument
13 entry:
14 call void @do_safepoint() [ "deopt"() ]
15 ret i64 addrspace(1)* %obj
16 }
17
18 define <2 x i64 addrspace(1)*> @test2(<2 x i64 addrspace(1)*> %obj) gc "statepoint-example" {
19 ; CHECK-LABEL: test2
20 ; CHECK: extractelement
21 ; CHECK-NEXT: extractelement
22 ; CHECK-NEXT: gc.statepoint
23 ; CHECK-NEXT: gc.relocate
24 ; CHECK-NEXT: bitcast
25 ; CHECK-NEXT: gc.relocate
26 ; CHECK-NEXT: bitcast
27 ; CHECK-NEXT: insertelement
28 ; CHECK-NEXT: insertelement
29 ; CHECK-NEXT: ret <2 x i64 addrspace(1)*> %7
30 ; A base vector from a load
31 entry:
32 call void @do_safepoint() [ "deopt"() ]
33 ret <2 x i64 addrspace(1)*> %obj
34 }
35
36 define <2 x i64 addrspace(1)*> @test3(<2 x i64 addrspace(1)*>* %ptr) gc "statepoint-example" {
37 ; CHECK-LABEL: test3
38 ; CHECK: load
39 ; CHECK-NEXT: extractelement
40 ; CHECK-NEXT: extractelement
41 ; CHECK-NEXT: gc.statepoint
42 ; CHECK-NEXT: gc.relocate
43 ; CHECK-NEXT: bitcast
44 ; CHECK-NEXT: gc.relocate
45 ; CHECK-NEXT: bitcast
46 ; CHECK-NEXT: insertelement
47 ; CHECK-NEXT: insertelement
48 ; CHECK-NEXT: ret <2 x i64 addrspace(1)*> %7
49 ; When a statepoint is an invoke rather than a call
50 entry:
51 %obj = load <2 x i64 addrspace(1)*>, <2 x i64 addrspace(1)*>* %ptr
52 call void @do_safepoint() [ "deopt"() ]
53 ret <2 x i64 addrspace(1)*> %obj
54 }
55
56 declare i32 @fake_personality_function()
57
58 define <2 x i64 addrspace(1)*> @test4(<2 x i64 addrspace(1)*>* %ptr) gc "statepoint-example" personality i32 ()* @fake_personality_function {
59 ; CHECK-LABEL: test4
60 ; CHECK: load
61 ; CHECK-NEXT: extractelement
62 ; CHECK-NEXT: extractelement
63 ; CHECK-NEXT: gc.statepoint
64 entry:
65 %obj = load <2 x i64 addrspace(1)*>, <2 x i64 addrspace(1)*>* %ptr
66 invoke void @do_safepoint() [ "deopt"() ]
67 to label %normal_return unwind label %exceptional_return
68
69 normal_return: ; preds = %entry
70 ; CHECK-LABEL: normal_return:
71 ; CHECK: gc.relocate
72 ; CHECK-NEXT: bitcast
73 ; CHECK-NEXT: gc.relocate
74 ; CHECK-NEXT: bitcast
75 ; CHECK-NEXT: insertelement
76 ; CHECK-NEXT: insertelement
77 ; CHECK-NEXT: ret <2 x i64 addrspace(1)*> %7
78 ret <2 x i64 addrspace(1)*> %obj
79
80 exceptional_return: ; preds = %entry
81 ; CHECK-LABEL: exceptional_return:
82 ; CHECK: gc.relocate
83 ; CHECK-NEXT: bitcast
84 ; CHECK-NEXT: gc.relocate
85 ; CHECK-NEXT: bitcast
86 ; CHECK-NEXT: insertelement
87 ; CHECK-NEXT: insertelement
88 ; CHECK-NEXT: ret <2 x i64 addrspace(1)*> %13
89 ; Can we handle an insert element with a constant offset? This effectively
90 ; tests both the equal and inequal case since we have to relocate both indices
91 ; in the vector.
92 %landing_pad4 = landingpad token
93 cleanup
94 ret <2 x i64 addrspace(1)*> %obj
95 }
96
97 define <2 x i64 addrspace(1)*> @test5(i64 addrspace(1)* %p) gc "statepoint-example" {
98 ; CHECK-LABEL: test5
99 ; CHECK: insertelement
100 ; CHECK-NEXT: insertelement
101 ; CHECK-NEXT: extractelement
102 ; CHECK-NEXT: extractelement
103 ; CHECK-NEXT: extractelement
104 ; CHECK-NEXT: extractelement
105 ; CHECK-NEXT: gc.statepoint
106 ; CHECK-NEXT: gc.relocate
107 ; CHECK-NEXT: bitcast
108 ; CHECK-NEXT: gc.relocate
109 ; CHECK-NEXT: bitcast
110 ; CHECK-NEXT: gc.relocate
111 ; CHECK-NEXT: bitcast
112 ; CHECK-NEXT: gc.relocate
113 ; CHECK-NEXT: bitcast
114 ; CHECK-NEXT: insertelement
115 ; CHECK-NEXT: insertelement
116 ; CHECK-NEXT: insertelement
117 ; CHECK-NEXT: insertelement
118 ; CHECK-NEXT: ret <2 x i64 addrspace(1)*>
119 ; A base vector from a load
120 entry:
121 %vec = insertelement <2 x i64 addrspace(1)*> undef, i64 addrspace(1)* %p, i32 0
122 call void @do_safepoint() [ "deopt"() ]
123 ret <2 x i64 addrspace(1)*> %vec
124 }
125
126 define <2 x i64 addrspace(1)*> @test6(i1 %cnd, <2 x i64 addrspace(1)*>* %ptr) gc "statepoint-example" {
127 ; CHECK-LABEL: test6
128 entry:
129 br i1 %cnd, label %taken, label %untaken
130
131 taken: ; preds = %entry
132 %obja = load <2 x i64 addrspace(1)*>, <2 x i64 addrspace(1)*>* %ptr
133 br label %merge
134
135 untaken: ; preds = %entry
136 %objb = load <2 x i64 addrspace(1)*>, <2 x i64 addrspace(1)*>* %ptr
137 br label %merge
138
139 merge: ; preds = %untaken, %taken
140 ; CHECK-LABEL: merge:
141 ; CHECK-NEXT: = phi
142 ; CHECK-NEXT: = phi
143 ; CHECK-NEXT: extractelement
144 ; CHECK-NEXT: extractelement
145 ; CHECK-NEXT: extractelement
146 ; CHECK-NEXT: extractelement
147 ; CHECK-NEXT: gc.statepoint
148 ; CHECK-NEXT: gc.relocate
149 ; CHECK-NEXT: bitcast
150 ; CHECK-NEXT: gc.relocate
151 ; CHECK-NEXT: bitcast
152 ; CHECK-NEXT: gc.relocate
153 ; CHECK-NEXT: bitcast
154 ; CHECK-NEXT: gc.relocate
155 ; CHECK-NEXT: bitcast
156 ; CHECK-NEXT: insertelement
157 ; CHECK-NEXT: insertelement
158 ; CHECK-NEXT: insertelement
159 ; CHECK-NEXT: insertelement
160 ; CHECK-NEXT: ret <2 x i64 addrspace(1)*>
161 %obj = phi <2 x i64 addrspace(1)*> [ %obja, %taken ], [ %objb, %untaken ]
162 call void @do_safepoint() [ "deopt"() ]
163 ret <2 x i64 addrspace(1)*> %obj
164 }
165
166 declare void @do_safepoint()