1 | /* |
2 | * Copyright (C) 2016-2019 Apple Inc. All rights reserved. |
3 | * |
4 | * Redistribution and use in source and binary forms, with or without |
5 | * modification, are permitted provided that the following conditions |
6 | * are met: |
7 | * 1. Redistributions of source code must retain the above copyright |
8 | * notice, this list of conditions and the following disclaimer. |
9 | * 2. Redistributions in binary form must reproduce the above copyright |
10 | * notice, this list of conditions and the following disclaimer in the |
11 | * documentation and/or other materials provided with the distribution. |
12 | * |
13 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
14 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
16 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
17 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
18 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
19 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
20 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
21 | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
23 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
24 | */ |
25 | |
26 | #include "config.h" |
27 | #include "B3FixSSA.h" |
28 | |
29 | #if ENABLE(B3_JIT) |
30 | |
31 | #include "B3BasicBlockInlines.h" |
32 | #include "B3BreakCriticalEdges.h" |
33 | #include "B3Dominators.h" |
34 | #include "B3InsertionSetInlines.h" |
35 | #include "B3PhaseScope.h" |
36 | #include "B3ProcedureInlines.h" |
37 | #include "B3SSACalculator.h" |
38 | #include "B3UpsilonValue.h" |
39 | #include "B3ValueInlines.h" |
40 | #include "B3Variable.h" |
41 | #include "B3VariableLiveness.h" |
42 | #include "B3VariableValue.h" |
43 | #include <wtf/CommaPrinter.h> |
44 | #include <wtf/IndexSet.h> |
45 | #include <wtf/IndexSparseSet.h> |
46 | |
47 | namespace JSC { namespace B3 { |
48 | |
49 | namespace { |
50 | |
51 | namespace B3FixSSAInternal { |
52 | static constexpr bool verbose = false; |
53 | } |
54 | |
55 | void killDeadVariables(Procedure& proc) |
56 | { |
57 | IndexSet<Variable*> liveVariables; |
58 | for (Value* value : proc.values()) { |
59 | if (value->opcode() == Get) |
60 | liveVariables.add(value->as<VariableValue>()->variable()); |
61 | } |
62 | |
63 | for (Value* value : proc.values()) { |
64 | if (value->opcode() == Set && !liveVariables.contains(value->as<VariableValue>()->variable())) |
65 | value->replaceWithNop(); |
66 | } |
67 | |
68 | for (Variable* variable : proc.variables()) { |
69 | if (!liveVariables.contains(variable)) |
70 | proc.deleteVariable(variable); |
71 | } |
72 | } |
73 | |
74 | void fixSSALocally(Procedure& proc) |
75 | { |
76 | IndexSparseSet<KeyValuePair<unsigned, Value*>> mapping(proc.variables().size()); |
77 | for (BasicBlock* block : proc.blocksInPreOrder()) { |
78 | mapping.clear(); |
79 | |
80 | for (unsigned valueIndex = 0; valueIndex < block->size(); ++valueIndex) { |
81 | Value* value = block->at(valueIndex); |
82 | value->performSubstitution(); |
83 | |
84 | switch (value->opcode()) { |
85 | case Get: { |
86 | VariableValue* variableValue = value->as<VariableValue>(); |
87 | Variable* variable = variableValue->variable(); |
88 | |
89 | if (KeyValuePair<unsigned, Value*>* replacement = mapping.get(variable->index())) |
90 | value->replaceWithIdentity(replacement->value); |
91 | break; |
92 | } |
93 | |
94 | case Set: { |
95 | VariableValue* variableValue = value->as<VariableValue>(); |
96 | Variable* variable = variableValue->variable(); |
97 | |
98 | mapping.set(variable->index(), value->child(0)); |
99 | break; |
100 | } |
101 | |
102 | default: |
103 | break; |
104 | } |
105 | } |
106 | } |
107 | } |
108 | |
109 | void fixSSAGlobally(Procedure& proc) |
110 | { |
111 | VariableLiveness liveness(proc); |
112 | |
113 | // Kill any dead Set's. Each Set creates work for us, so this is profitable. |
114 | for (BasicBlock* block : proc) { |
115 | VariableLiveness::LocalCalc localCalc(liveness, block); |
116 | for (unsigned valueIndex = block->size(); valueIndex--;) { |
117 | Value* value = block->at(valueIndex); |
118 | if (value->opcode() == Set && !localCalc.isLive(value->as<VariableValue>()->variable())) |
119 | value->replaceWithNop(); |
120 | localCalc.execute(valueIndex); |
121 | } |
122 | } |
123 | |
124 | VariableLiveness::LiveAtHead liveAtHead = liveness.liveAtHead(); |
125 | |
126 | SSACalculator ssa(proc); |
127 | |
128 | // Create a SSACalculator::Variable ("calcVar") for every variable. |
129 | Vector<Variable*> calcVarToVariable; |
130 | IndexMap<Variable*, SSACalculator::Variable*> variableToCalcVar(proc.variables().size()); |
131 | |
132 | for (Variable* variable : proc.variables()) { |
133 | SSACalculator::Variable* calcVar = ssa.newVariable(); |
134 | RELEASE_ASSERT(calcVar->index() == calcVarToVariable.size()); |
135 | calcVarToVariable.append(variable); |
136 | variableToCalcVar[variable] = calcVar; |
137 | } |
138 | |
139 | // Create Defs for all of the stores to the stack variable. |
140 | for (BasicBlock* block : proc) { |
141 | for (Value* value : *block) { |
142 | if (value->opcode() != Set) |
143 | continue; |
144 | |
145 | Variable* variable = value->as<VariableValue>()->variable(); |
146 | |
147 | if (SSACalculator::Variable* calcVar = variableToCalcVar[variable]) |
148 | ssa.newDef(calcVar, block, value->child(0)); |
149 | } |
150 | } |
151 | |
152 | // Decide where Phis are to be inserted. This creates them but does not insert them. |
153 | { |
154 | TimingScope timingScope("fixSSA: computePhis" ); |
155 | ssa.computePhis( |
156 | [&] (SSACalculator::Variable* calcVar, BasicBlock* block) -> Value* { |
157 | Variable* variable = calcVarToVariable[calcVar->index()]; |
158 | if (!liveAtHead.isLiveAtHead(block, variable)) |
159 | return nullptr; |
160 | |
161 | Value* phi = proc.add<Value>(Phi, variable->type(), block->at(0)->origin()); |
162 | if (B3FixSSAInternal::verbose) { |
163 | dataLog( |
164 | "Adding Phi for " , pointerDump(variable), " at " , *block, ": " , |
165 | deepDump(proc, phi), "\n" ); |
166 | } |
167 | return phi; |
168 | }); |
169 | } |
170 | |
171 | // Now perform the conversion. |
172 | TimingScope timingScope("fixSSA: convert" ); |
173 | InsertionSet insertionSet(proc); |
174 | IndexSparseSet<KeyValuePair<unsigned, Value*>> mapping(proc.variables().size()); |
175 | IndexSet<Value*> valuesToDelete; |
176 | for (BasicBlock* block : proc.blocksInPreOrder()) { |
177 | mapping.clear(); |
178 | |
179 | auto ensureMapping = [&] (Variable* variable, unsigned valueIndex, Origin origin) -> Value* { |
180 | KeyValuePair<unsigned, Value*>* found = mapping.get(variable->index()); |
181 | if (found) |
182 | return found->value; |
183 | |
184 | SSACalculator::Variable* calcVar = variableToCalcVar[variable]; |
185 | SSACalculator::Def* def = ssa.reachingDefAtHead(block, calcVar); |
186 | if (def) { |
187 | mapping.set(variable->index(), def->value()); |
188 | return def->value(); |
189 | } |
190 | |
191 | return insertionSet.insertBottom(valueIndex, origin, variable->type()); |
192 | }; |
193 | |
194 | for (SSACalculator::Def* phiDef : ssa.phisForBlock(block)) { |
195 | Variable* variable = calcVarToVariable[phiDef->variable()->index()]; |
196 | |
197 | insertionSet.insertValue(0, phiDef->value()); |
198 | mapping.set(variable->index(), phiDef->value()); |
199 | } |
200 | |
201 | for (unsigned valueIndex = 0; valueIndex < block->size(); ++valueIndex) { |
202 | Value* value = block->at(valueIndex); |
203 | value->performSubstitution(); |
204 | |
205 | switch (value->opcode()) { |
206 | case Get: { |
207 | VariableValue* variableValue = value->as<VariableValue>(); |
208 | Variable* variable = variableValue->variable(); |
209 | |
210 | value->replaceWithIdentity(ensureMapping(variable, valueIndex, value->origin())); |
211 | valuesToDelete.add(value); |
212 | break; |
213 | } |
214 | |
215 | case Set: { |
216 | VariableValue* variableValue = value->as<VariableValue>(); |
217 | Variable* variable = variableValue->variable(); |
218 | |
219 | mapping.set(variable->index(), value->child(0)); |
220 | value->replaceWithNop(); |
221 | break; |
222 | } |
223 | |
224 | default: |
225 | break; |
226 | } |
227 | } |
228 | |
229 | unsigned upsilonInsertionPoint = block->size() - 1; |
230 | Origin upsilonOrigin = block->last()->origin(); |
231 | for (BasicBlock* successorBlock : block->successorBlocks()) { |
232 | for (SSACalculator::Def* phiDef : ssa.phisForBlock(successorBlock)) { |
233 | Value* phi = phiDef->value(); |
234 | SSACalculator::Variable* calcVar = phiDef->variable(); |
235 | Variable* variable = calcVarToVariable[calcVar->index()]; |
236 | |
237 | Value* mappedValue = ensureMapping(variable, upsilonInsertionPoint, upsilonOrigin); |
238 | if (B3FixSSAInternal::verbose) { |
239 | dataLog( |
240 | "Mapped value for " , *variable, " with successor Phi " , *phi, |
241 | " at end of " , *block, ": " , pointerDump(mappedValue), "\n" ); |
242 | } |
243 | |
244 | insertionSet.insert<UpsilonValue>( |
245 | upsilonInsertionPoint, upsilonOrigin, mappedValue->foldIdentity(), phi); |
246 | } |
247 | } |
248 | |
249 | insertionSet.execute(block); |
250 | } |
251 | |
252 | // This is isn't strictly necessary, but it leaves the IR nice and tidy, which is particularly |
253 | // useful for phases that do size estimates. |
254 | for (BasicBlock* block : proc) { |
255 | block->values().removeAllMatching( |
256 | [&] (Value* value) -> bool { |
257 | if (!valuesToDelete.contains(value) && value->opcode() != Nop) |
258 | return false; |
259 | |
260 | proc.deleteValue(value); |
261 | return true; |
262 | }); |
263 | } |
264 | |
265 | if (B3FixSSAInternal::verbose) { |
266 | dataLog("B3 after SSA conversion:\n" ); |
267 | dataLog(proc); |
268 | } |
269 | } |
270 | |
271 | } // anonymous namespace |
272 | |
273 | void demoteValues(Procedure& proc, const IndexSet<Value*>& values) |
274 | { |
275 | HashMap<Value*, Variable*> map; |
276 | HashMap<Value*, Variable*> phiMap; |
277 | |
278 | // Create stack slots. |
279 | for (Value* value : values.values(proc.values())) { |
280 | map.add(value, proc.addVariable(value->type())); |
281 | |
282 | if (value->opcode() == Phi) |
283 | phiMap.add(value, proc.addVariable(value->type())); |
284 | } |
285 | |
286 | if (B3FixSSAInternal::verbose) { |
287 | dataLog("Demoting values as follows:\n" ); |
288 | dataLog(" map = " ); |
289 | CommaPrinter comma; |
290 | for (auto& entry : map) |
291 | dataLog(comma, *entry.key, "=>" , *entry.value); |
292 | dataLog("\n" ); |
293 | dataLog(" phiMap = " ); |
294 | comma = CommaPrinter(); |
295 | for (auto& entry : phiMap) |
296 | dataLog(comma, *entry.key, "=>" , *entry.value); |
297 | dataLog("\n" ); |
298 | } |
299 | |
300 | // Change accesses to the values to accesses to the stack slots. |
301 | InsertionSet insertionSet(proc); |
302 | for (BasicBlock* block : proc) { |
303 | if (block->numPredecessors()) { |
304 | // Deal with terminals that produce values (i.e. patchpoint terminals, like the ones we |
305 | // generate for the allocation fast path). |
306 | Value* value = block->predecessor(0)->last(); |
307 | Variable* variable = map.get(value); |
308 | if (variable) { |
309 | RELEASE_ASSERT(block->numPredecessors() == 1); // Critical edges better be broken. |
310 | insertionSet.insert<VariableValue>(0, Set, value->origin(), variable, value); |
311 | } |
312 | } |
313 | |
314 | for (unsigned valueIndex = 0; valueIndex < block->size(); ++valueIndex) { |
315 | Value* value = block->at(valueIndex); |
316 | |
317 | if (value->opcode() == Phi) { |
318 | if (Variable* variable = phiMap.get(value)) { |
319 | value->replaceWithIdentity( |
320 | insertionSet.insert<VariableValue>( |
321 | valueIndex, Get, value->origin(), variable)); |
322 | } |
323 | } else { |
324 | for (Value*& child : value->children()) { |
325 | if (Variable* variable = map.get(child)) { |
326 | child = insertionSet.insert<VariableValue>( |
327 | valueIndex, Get, value->origin(), variable); |
328 | } |
329 | } |
330 | |
331 | if (UpsilonValue* upsilon = value->as<UpsilonValue>()) { |
332 | if (Variable* variable = phiMap.get(upsilon->phi())) { |
333 | insertionSet.insert<VariableValue>( |
334 | valueIndex, Set, upsilon->origin(), variable, upsilon->child(0)); |
335 | value->replaceWithNop(); |
336 | } |
337 | } |
338 | } |
339 | |
340 | if (Variable* variable = map.get(value)) { |
341 | if (valueIndex + 1 < block->size()) { |
342 | insertionSet.insert<VariableValue>( |
343 | valueIndex + 1, Set, value->origin(), variable, value); |
344 | } |
345 | } |
346 | } |
347 | insertionSet.execute(block); |
348 | } |
349 | } |
350 | |
351 | bool fixSSA(Procedure& proc) |
352 | { |
353 | PhaseScope phaseScope(proc, "fixSSA" ); |
354 | |
355 | if (proc.variables().isEmpty()) |
356 | return false; |
357 | |
358 | // Lots of variables have trivial local liveness. We can allocate those without any |
359 | // trouble. |
360 | fixSSALocally(proc); |
361 | |
362 | // Just for sanity, remove any unused variables first. It's unlikely that this code has any |
363 | // bugs having to do with dead variables, but it would be silly to have to fix such a bug if |
364 | // it did arise. |
365 | killDeadVariables(proc); |
366 | |
367 | if (proc.variables().isEmpty()) |
368 | return false; |
369 | |
370 | breakCriticalEdges(proc); |
371 | |
372 | fixSSAGlobally(proc); |
373 | |
374 | return true; |
375 | } |
376 | |
377 | } } // namespace JSC::B3 |
378 | |
379 | #endif // ENABLE(B3_JIT) |
380 | |
381 | |