Mercurial > hg > graal-jvmci-8
comparison graal/Compiler/src/com/sun/c1x/opt/NullCheckEliminator.java @ 2507:9ec15d6914ca
Pull over of compiler from maxine repository.
author | Thomas Wuerthinger <thomas@wuerthinger.net> |
---|---|
date | Wed, 27 Apr 2011 11:43:22 +0200 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
2506:4a3bf8a5bf41 | 2507:9ec15d6914ca |
---|---|
1 /* | |
2 * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved. | |
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
4 * | |
5 * This code is free software; you can redistribute it and/or modify it | |
6 * under the terms of the GNU General Public License version 2 only, as | |
7 * published by the Free Software Foundation. | |
8 * | |
9 * This code is distributed in the hope that it will be useful, but WITHOUT | |
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
12 * version 2 for more details (a copy is included in the LICENSE file that | |
13 * accompanied this code). | |
14 * | |
15 * You should have received a copy of the GNU General Public License version | |
16 * 2 along with this work; if not, write to the Free Software Foundation, | |
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
18 * | |
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
20 * or visit www.oracle.com if you need additional information or have any | |
21 * questions. | |
22 */ | |
23 package com.sun.c1x.opt; | |
24 | |
25 import java.util.*; | |
26 | |
27 import com.sun.c1x.*; | |
28 import com.sun.c1x.graph.*; | |
29 import com.sun.c1x.ir.*; | |
30 import com.sun.c1x.util.*; | |
31 import com.sun.c1x.value.FrameState.*; | |
32 import com.sun.cri.ci.*; | |
33 | |
34 /** | |
35 * This class implements a data-flow analysis to remove redundant null checks | |
36 * and deoptimization info for instructions that cannot ever produce {@code NullPointerException}s. | |
37 * | |
38 * This implementation uses an optimistic dataflow analysis by attempting to visit all predecessors | |
39 * of a block before visiting the block itself. For this purpose it uses the block numbers computed by | |
40 * the {@link BlockMap} during graph construction, which may not actually be | |
41 * a valid reverse post-order number (due to inlining and any previous optimizations). | |
42 * | |
43 * When loops are encountered, or if the blocks are not visited in the optimal order, this implementation | |
44 * will fall back to performing an iterative data flow analysis where it maintains a set | |
45 * of incoming non-null instructions and a set of locally produced outgoing non-null instructions | |
46 * and iterates the dataflow equations to a fixed point. Basically, for block b, | |
47 * out(b) = in(b) U local_out(b) and in(b) = intersect(out(pred)). After a fixed point is | |
48 * reached, the resulting incoming sets are used to visit instructions with uneliminated null checks | |
49 * a second time. | |
50 * | |
51 * Note that the iterative phase is actually optional, because the first pass is conservative. | |
52 * Iteration can be disabled by setting {@link C1XOptions#OptIterativeNCE} to | |
53 * {@code false}. Iteration is rarely necessary for acyclic graphs. | |
54 * | |
55 * @author Ben L. Titzer | |
56 */ | |
57 public class NullCheckEliminator extends DefaultValueVisitor { | |
58 | |
59 private static class IfEdge { | |
60 final BlockBegin ifBlock; | |
61 final BlockBegin succ; | |
62 final Value checked; | |
63 | |
64 IfEdge(BlockBegin i, BlockBegin s, Value c) { | |
65 this.ifBlock = i; | |
66 this.succ = s; | |
67 this.checked = c; | |
68 } | |
69 } | |
70 | |
71 private static class BlockInfo { | |
72 // used in first pass | |
73 final BlockBegin block; | |
74 boolean marked; | |
75 CiBitMap localOut; | |
76 CiBitMap localExcept; | |
77 List<Value> localUses; | |
78 // used in iteration and flow sensitivity | |
79 IfEdge ifEdge; | |
80 CiBitMap localIn; | |
81 | |
82 BlockInfo(BlockBegin b) { | |
83 this.block = b; | |
84 } | |
85 } | |
86 | |
87 private static class ValueInfo { | |
88 final Value value; | |
89 final int globalIndex; | |
90 | |
91 ValueInfo(Value value, int index) { | |
92 this.value = value; | |
93 globalIndex = index; | |
94 } | |
95 } | |
96 | |
97 final IR ir; | |
98 final BlockWorkList workList = new BlockWorkList(); | |
99 | |
100 final ArrayList<BlockInfo> blockInfos = new ArrayList<BlockInfo>(5); | |
101 final ArrayList<ValueInfo> valueInfos = new ArrayList<ValueInfo>(5); | |
102 final ArrayList<BlockInfo> remainingUses = new ArrayList<BlockInfo>(5); | |
103 | |
104 // maps used only in iteration | |
105 boolean requiresIteration; | |
106 int maximumIndex; | |
107 | |
108 CiBitMap currentBitMap; | |
109 List<Value> currentUses; | |
110 | |
111 /** | |
112 * Creates a new null check eliminator for the specified IR and performs the optimization. | |
113 * @param ir the IR | |
114 */ | |
115 public NullCheckEliminator(IR ir) { | |
116 this.ir = ir; | |
117 optimize(); | |
118 } | |
119 | |
120 private void optimize() { | |
121 if (C1XOptions.PrintTimers) { | |
122 C1XTimers.NCE.start(); | |
123 } | |
124 BlockInfo start = getBlockInfo(ir.startBlock); | |
125 mark(start); | |
126 processBlock(start); | |
127 while (!workList.isEmpty()) { | |
128 processBlock(getBlockInfo(workList.removeFromWorkList())); | |
129 } | |
130 if (requiresIteration && C1XOptions.OptIterativeNCE) { | |
131 // there was a loop, or blocks were not visited in reverse post-order; | |
132 // iteration is required to compute the in sets for a second pass | |
133 iterate(); | |
134 } | |
135 clearInfo(); | |
136 if (C1XOptions.PrintTimers) { | |
137 C1XTimers.NCE.stop(); | |
138 } | |
139 } | |
140 | |
141 private void processBlock(BlockInfo info) { | |
142 BlockBegin block = info.block; | |
143 // first pass on a block | |
144 computeLocalInSet(info); | |
145 // process any phis in the block | |
146 block.stateBefore().forEachPhi(block, new PhiProcedure() { | |
147 public boolean doPhi(Phi phi) { | |
148 visitPhi(phi); | |
149 return true; | |
150 } | |
151 }); | |
152 | |
153 // now visit the instructions in order | |
154 for (Instruction i = block.next(); i != null; i = i.next()) { | |
155 i.accept(this); | |
156 } | |
157 if (!currentUses.isEmpty()) { | |
158 // remember any localUses in this block for later iterative processing | |
159 info.localUses = currentUses; | |
160 remainingUses.add(info); | |
161 } | |
162 queueSuccessors(block.end().successors()); | |
163 queueSuccessors(block.exceptionHandlerBlocks()); | |
164 } | |
165 | |
166 private void queueSuccessors(List<BlockBegin> successorList) { | |
167 for (BlockBegin succ : successorList) { | |
168 BlockInfo info = getBlockInfo(succ); | |
169 if (!isMarked(info)) { | |
170 workList.addSorted(succ, succ.depthFirstNumber()); | |
171 mark(info); | |
172 } | |
173 } | |
174 } | |
175 | |
176 private void computeLocalInSet(BlockInfo info) { | |
177 BlockBegin block = info.block; | |
178 // compute the initial {in} set based on the {localOut} sets of predecessors, if possible | |
179 currentBitMap = null; | |
180 currentUses = new ArrayList<Value>(); | |
181 if (block.numberOfPreds() == 0) { | |
182 // no predecessors => start block | |
183 assert block == ir.startBlock : "block without predecessors should be start block"; | |
184 currentBitMap = newBitMap(); | |
185 } else { | |
186 // block has at least one predecessor | |
187 for (BlockBegin pred : block.predecessors()) { | |
188 if (getPredecessorMap(pred, block.isExceptionEntry()) == null) { | |
189 // one of the predecessors of this block has not been visited, | |
190 // we have to be conservative and start with nothing known | |
191 currentBitMap = newBitMap(); | |
192 requiresIteration = true; | |
193 } | |
194 } | |
195 if (currentBitMap == null) { | |
196 // all the predecessors have been visited, compute the intersection of their {localOut} sets | |
197 for (BlockBegin pred : block.predecessors()) { | |
198 BlockInfo predInfo = getBlockInfo(pred); | |
199 CiBitMap predMap = getPredecessorMap(predInfo, block.isExceptionEntry()); | |
200 currentBitMap = intersectLocalOut(predInfo, currentBitMap, predMap, block); | |
201 } | |
202 } | |
203 } | |
204 assert currentBitMap != null : "current bitmap should be computed one or the other way"; | |
205 // if there are exception handlers for this block, then clone {in} and put it in {localExcept} | |
206 if (block.numberOfExceptionHandlers() > 0) { | |
207 info.localExcept = currentBitMap.copy(); | |
208 } | |
209 info.localOut = currentBitMap; | |
210 } | |
211 | |
212 private CiBitMap intersectLocalOut(BlockInfo pred, CiBitMap current, CiBitMap predMap, BlockBegin succ) { | |
213 predMap = intersectFlowSensitive(pred, predMap, succ); | |
214 if (current == null) { | |
215 current = predMap.copy(); | |
216 } else { | |
217 current.setIntersect(predMap); | |
218 } | |
219 return current; | |
220 } | |
221 | |
222 private CiBitMap intersectFlowSensitive(BlockInfo pred, CiBitMap n, BlockBegin succ) { | |
223 if (C1XOptions.OptFlowSensitiveNCE) { | |
224 // check to see if there is an if edge between these two blocks | |
225 if (pred.ifEdge != null && pred.ifEdge.succ == succ) { | |
226 // if there is a special edge between pred and block, add the checked instruction | |
227 n = n.copy(); | |
228 setValue(pred.ifEdge.checked, n); | |
229 } | |
230 } | |
231 return n; | |
232 } | |
233 | |
234 private void iterate() { | |
235 // the previous phase calculated all the {localOut} sets; use iteration to | |
236 // calculate the {in} sets | |
237 if (remainingUses.size() > 0) { | |
238 // only perform iterative flow analysis if there are checks remaining to eliminate | |
239 C1XMetrics.NullCheckIterations++; | |
240 clearMarked(); | |
241 // start off by propagating a new set to the start block | |
242 propagate(getBlockInfo(ir.startBlock), newBitMap(), ir.startBlock); | |
243 while (!workList.isEmpty()) { | |
244 BlockInfo block = getBlockInfo(workList.removeFromWorkList()); | |
245 unmark(block); | |
246 iterateBlock(block); | |
247 } | |
248 // now that the fixed point is reached, reprocess any remaining localUses | |
249 currentUses = null; // the list won't be needed this time | |
250 for (BlockInfo info : remainingUses) { | |
251 reprocessUses(info.localIn, info.localUses); | |
252 } | |
253 } | |
254 } | |
255 | |
256 private void iterateBlock(BlockInfo info) { | |
257 CiBitMap prevMap = info.localIn; | |
258 assert prevMap != null : "how did the block get on the worklist without an initial in map?"; | |
259 CiBitMap localOut = info.localOut; | |
260 CiBitMap out; | |
261 // copy larger and do union with smaller | |
262 if (localOut.size() > prevMap.size()) { | |
263 out = localOut.copy(); | |
264 out.setUnion(prevMap); | |
265 } else { | |
266 out = prevMap.copy(); | |
267 out.setUnion(localOut); | |
268 } | |
269 propagateSuccessors(info, out, info.block.end().successors()); // propagate {in} U {localOut} to successors | |
270 propagateSuccessors(info, prevMap, info.block.exceptionHandlerBlocks()); // propagate {in} to exception handlers | |
271 } | |
272 | |
273 private void propagateSuccessors(BlockInfo block, CiBitMap out, List<BlockBegin> successorList) { | |
274 for (BlockBegin succ : successorList) { | |
275 propagate(block, out, succ); | |
276 } | |
277 } | |
278 | |
279 private void propagate(BlockInfo pred, CiBitMap bitMap, BlockBegin succ) { | |
280 boolean changed; | |
281 BlockInfo succInfo = getBlockInfo(succ); | |
282 if (succInfo.localIn == null) { | |
283 // this is the first time this block is being iterated | |
284 succInfo.localIn = bitMap.copy(); | |
285 propagateFlowSensitive(pred, succInfo.localIn, succ, false); | |
286 changed = true; | |
287 } else { | |
288 // perform intersection with previous map | |
289 bitMap = propagateFlowSensitive(pred, bitMap, succ, true); | |
290 changed = succInfo.localIn.setIntersect(bitMap); | |
291 } | |
292 if (changed && !isMarked(succInfo)) { | |
293 mark(succInfo); | |
294 workList.addSorted(succ, succ.depthFirstNumber()); | |
295 } | |
296 } | |
297 | |
298 private CiBitMap propagateFlowSensitive(BlockInfo pred, CiBitMap bitMap, BlockBegin succ, boolean copy) { | |
299 if (C1XOptions.OptFlowSensitiveNCE) { | |
300 if (pred.ifEdge != null && pred.ifEdge.succ == succ) { | |
301 if (copy) { | |
302 bitMap = bitMap.copy(); | |
303 } | |
304 // there is a special if edge between these blocks, add the checked instruction | |
305 setValue(pred.ifEdge.checked, bitMap); | |
306 } | |
307 } | |
308 return bitMap; | |
309 } | |
310 | |
311 private void reprocessUses(CiBitMap in, List<Value> uses) { | |
312 // iterate over each of the use instructions again, using the input bitmap | |
313 // and the hash sets | |
314 assert in != null; | |
315 currentBitMap = in; | |
316 for (Value i : uses) { | |
317 i.accept(this); | |
318 } | |
319 } | |
320 | |
321 private boolean processUse(Value use, Value object, boolean implicitCheck) { | |
322 if (object.isNonNull()) { | |
323 // the object itself is known for sure to be non-null, so clear the flag. | |
324 // the flag is usually cleared in the constructor of the using instruction, but | |
325 // later optimizations may more reveal more non-null objects | |
326 use.eliminateNullCheck(); | |
327 return true; | |
328 } else { | |
329 // check if the object is non-null in the bitmap or hashset | |
330 if (checkValue(object, currentBitMap)) { | |
331 // the object is non-null at this site | |
332 use.eliminateNullCheck(); | |
333 return true; | |
334 } else { | |
335 if (implicitCheck) { | |
336 // the object will be non-null after executing this instruction | |
337 setValue(object, currentBitMap); | |
338 } | |
339 if (currentUses != null) { | |
340 currentUses.add(use); // record a use for potential later iteration | |
341 } | |
342 } | |
343 } | |
344 return false; | |
345 } | |
346 | |
347 private boolean isNonNullOnEdge(BlockBegin pred, BlockBegin succ, Value i) { | |
348 if (C1XOptions.OptFlowSensitiveNCE) { | |
349 IfEdge e = getBlockInfo(pred).ifEdge; | |
350 if (e != null && e.succ == succ && e.checked == i) { | |
351 return true; | |
352 } | |
353 } | |
354 return false; | |
355 } | |
356 | |
357 @Override | |
358 public void visitPhi(Phi phi) { | |
359 for (int j = 0; j < phi.inputCount(); j++) { | |
360 Value operand = phi.inputAt(j); | |
361 if (processUse(phi, operand, false)) { | |
362 continue; | |
363 } | |
364 if (C1XOptions.OptFlowSensitiveNCE) { | |
365 BlockBegin phiBlock = phi.block(); | |
366 if (!phiBlock.isExceptionEntry() && isNonNullOnEdge(phiBlock.predecessors().get(j), phiBlock, operand)) { | |
367 continue; | |
368 } | |
369 } | |
370 return; | |
371 } | |
372 // all inputs are non-null | |
373 phi.setFlag(Value.Flag.NonNull); | |
374 } | |
375 | |
376 @Override | |
377 public void visitLoadPointer(LoadPointer i) { | |
378 Value pointer = i.pointer(); | |
379 if (pointer != null) { | |
380 processUse(i, pointer, true); | |
381 } | |
382 } | |
383 | |
384 @Override | |
385 public void visitUnsafeCast(UnsafeCast i) { | |
386 if (processUse(i, i.value(), false)) { | |
387 // if the object is non null, the result of the cast is as well | |
388 i.setFlag(Value.Flag.NonNull); | |
389 } | |
390 } | |
391 | |
392 @Override | |
393 public void visitLoadField(LoadField i) { | |
394 Value object = i.object(); | |
395 if (object != null) { | |
396 processUse(i, object, true); | |
397 } | |
398 } | |
399 | |
400 @Override | |
401 public void visitStorePointer(StorePointer i) { | |
402 Value pointer = i.pointer(); | |
403 if (pointer != null) { | |
404 processUse(i, pointer, true); | |
405 } | |
406 } | |
407 | |
408 @Override | |
409 public void visitStoreField(StoreField i) { | |
410 Value object = i.object(); | |
411 if (object != null) { | |
412 processUse(i, object, true); | |
413 } | |
414 } | |
415 | |
416 @Override | |
417 public void visitArrayLength(ArrayLength i) { | |
418 processUse(i, i.array(), true); | |
419 } | |
420 | |
421 @Override | |
422 public void visitLoadIndexed(LoadIndexed i) { | |
423 processUse(i, i.array(), true); | |
424 } | |
425 | |
426 @Override | |
427 public void visitStoreIndexed(StoreIndexed i) { | |
428 processUse(i, i.array(), true); | |
429 } | |
430 | |
431 @Override | |
432 public void visitNullCheck(NullCheck i) { | |
433 processUse(i, i.object(), true); | |
434 } | |
435 | |
436 @Override | |
437 public void visitInvoke(Invoke i) { | |
438 if (!i.isStatic()) { | |
439 processUse(i, i.receiver(), true); | |
440 } | |
441 } | |
442 | |
443 @Override | |
444 public void visitCheckCast(CheckCast i) { | |
445 if (processUse(i, i.object(), false)) { | |
446 // if the object is non null, the result of the cast is as well | |
447 i.setFlag(Value.Flag.NonNull); | |
448 } | |
449 } | |
450 | |
451 @Override | |
452 public void visitInstanceOf(InstanceOf i) { | |
453 processUse(i, i.object(), false); // instanceof can check faster if object is non-null | |
454 } | |
455 | |
456 @Override | |
457 public void visitMonitorEnter(MonitorEnter i) { | |
458 processUse(i, i.object(), true); | |
459 } | |
460 | |
461 @Override | |
462 public void visitIntrinsic(Intrinsic i) { | |
463 if (!i.isStatic()) { | |
464 processUse(i, i.receiver(), true); | |
465 } | |
466 } | |
467 | |
468 @Override | |
469 public void visitIf(If i) { | |
470 if (C1XOptions.OptFlowSensitiveNCE) { | |
471 if (i.trueSuccessor() != i.falseSuccessor()) { | |
472 // if the two successors are different, then we may learn something on one branch | |
473 Value x = i.x(); | |
474 if (x.kind == CiKind.Object) { | |
475 // this is a comparison of object references | |
476 Value y = i.y(); | |
477 if (processUse(i, x, false)) { | |
478 // x is known to be non-null | |
479 compareAgainstNonNull(i, y); | |
480 } else if (processUse(i, y, false)) { | |
481 // y is known to be non-null | |
482 compareAgainstNonNull(i, x); | |
483 } else if (x.isNullConstant()) { | |
484 // x is the null constant | |
485 compareAgainstNull(i, y); | |
486 } else if (y.isNullConstant()) { | |
487 // y is the null constaint | |
488 compareAgainstNull(i, x); | |
489 } | |
490 } | |
491 // XXX: also check (x instanceof T) tests | |
492 } | |
493 } | |
494 } | |
495 | |
496 private void compareAgainstNonNull(If i, Value use) { | |
497 if (i.condition() == Condition.EQ) { | |
498 propagateNonNull(i, use, i.trueSuccessor()); | |
499 } | |
500 } | |
501 | |
502 private void compareAgainstNull(If i, Value use) { | |
503 if (i.condition() == Condition.EQ) { | |
504 propagateNonNull(i, use, i.falseSuccessor()); | |
505 } else if (i.condition() == Condition.NE) { | |
506 propagateNonNull(i, use, i.trueSuccessor()); | |
507 } | |
508 } | |
509 | |
510 private void propagateNonNull(If i, Value use, BlockBegin succ) { | |
511 BlockInfo info = getBlockInfo(i.begin()); | |
512 if (info.ifEdge == null) { | |
513 // Only use one if edge. | |
514 info.ifEdge = new IfEdge(i.begin(), succ, use); | |
515 } | |
516 } | |
517 | |
518 private ValueInfo getValueInfo(Value value) { | |
519 Object info = value.optInfo; | |
520 if (info instanceof ValueInfo) { | |
521 return (ValueInfo) info; | |
522 } | |
523 C1XMetrics.NullCheckIdsAssigned++; | |
524 ValueInfo ninfo = new ValueInfo(value, maximumIndex++); | |
525 value.optInfo = ninfo; | |
526 valueInfos.add(ninfo); | |
527 return ninfo; | |
528 } | |
529 | |
530 private BlockInfo getBlockInfo(BlockBegin block) { | |
531 Object info = block.optInfo; | |
532 if (info instanceof BlockInfo) { | |
533 return (BlockInfo) info; | |
534 } | |
535 BlockInfo ninfo = new BlockInfo(block); | |
536 block.optInfo = ninfo; | |
537 blockInfos.add(ninfo); | |
538 return ninfo; | |
539 } | |
540 | |
541 private void setValue(Value value, CiBitMap bitmap) { | |
542 int index = getValueInfo(value).globalIndex; | |
543 bitmap.grow(index + 1); | |
544 bitmap.set(index); | |
545 if (value instanceof UnsafeCast) { | |
546 // An unsafe cast is just an alias | |
547 setValue(((UnsafeCast) value).value(), bitmap); | |
548 } | |
549 } | |
550 | |
551 private boolean checkValue(Value value, CiBitMap bitmap) { | |
552 Object info = value.optInfo; | |
553 return info instanceof ValueInfo && bitmap.getDefault(((ValueInfo) info).globalIndex); | |
554 } | |
555 | |
556 private boolean isMarked(BlockInfo block) { | |
557 return block.marked; | |
558 } | |
559 | |
560 private void mark(BlockInfo block) { | |
561 block.marked = true; | |
562 } | |
563 | |
564 private void unmark(BlockInfo block) { | |
565 block.marked = false; | |
566 } | |
567 | |
568 private void clearInfo() { | |
569 // be a good citizen and clear all the information added to nodes | |
570 // (also avoids confusing a later application of this same optimization) | |
571 for (BlockInfo info : blockInfos) { | |
572 assert info.block.optInfo instanceof BlockInfo : "inconsistent block information"; | |
573 info.block.optInfo = null; | |
574 } | |
575 for (ValueInfo info : valueInfos) { | |
576 assert info.value.optInfo instanceof ValueInfo : "inconsistent value information"; | |
577 info.value.optInfo = null; | |
578 } | |
579 } | |
580 | |
581 private void clearMarked() { | |
582 for (BlockInfo info : blockInfos) { | |
583 info.marked = false; | |
584 } | |
585 } | |
586 | |
587 private CiBitMap newBitMap() { | |
588 return new CiBitMap(maximumIndex > 32 ? maximumIndex : 32); | |
589 } | |
590 | |
591 private CiBitMap getPredecessorMap(BlockBegin pred, boolean exceptionBlock) { | |
592 BlockInfo predInfo = getBlockInfo(pred); | |
593 return exceptionBlock ? predInfo.localExcept : predInfo.localOut; | |
594 } | |
595 | |
596 private CiBitMap getPredecessorMap(BlockInfo predInfo, boolean exceptionBlock) { | |
597 return exceptionBlock ? predInfo.localExcept : predInfo.localOut; | |
598 } | |
599 } |