1 /* 2 * Copyright (c) 2009, 2015, 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 org.graalvm.compiler.java; 24 25 import static org.graalvm.compiler.bytecode.Bytecodes.AALOAD; 26 import static org.graalvm.compiler.bytecode.Bytecodes.AASTORE; 27 import static org.graalvm.compiler.bytecode.Bytecodes.ARETURN; 28 import static org.graalvm.compiler.bytecode.Bytecodes.ARRAYLENGTH; 29 import static org.graalvm.compiler.bytecode.Bytecodes.ATHROW; 30 import static org.graalvm.compiler.bytecode.Bytecodes.BALOAD; 31 import static org.graalvm.compiler.bytecode.Bytecodes.BASTORE; 32 import static org.graalvm.compiler.bytecode.Bytecodes.CALOAD; 33 import static org.graalvm.compiler.bytecode.Bytecodes.CASTORE; 34 import static org.graalvm.compiler.bytecode.Bytecodes.DALOAD; 35 import static org.graalvm.compiler.bytecode.Bytecodes.DASTORE; 36 import static org.graalvm.compiler.bytecode.Bytecodes.DRETURN; 37 import static org.graalvm.compiler.bytecode.Bytecodes.FALOAD; 38 import static org.graalvm.compiler.bytecode.Bytecodes.FASTORE; 39 import static org.graalvm.compiler.bytecode.Bytecodes.FRETURN; 40 import static org.graalvm.compiler.bytecode.Bytecodes.GETFIELD; 41 import static org.graalvm.compiler.bytecode.Bytecodes.GOTO; 42 import static org.graalvm.compiler.bytecode.Bytecodes.GOTO_W; 43 import static org.graalvm.compiler.bytecode.Bytecodes.IALOAD; 44 import static org.graalvm.compiler.bytecode.Bytecodes.IASTORE; 45 import static org.graalvm.compiler.bytecode.Bytecodes.IFEQ; 46 import static org.graalvm.compiler.bytecode.Bytecodes.IFGE; 47 import static org.graalvm.compiler.bytecode.Bytecodes.IFGT; 48 import static org.graalvm.compiler.bytecode.Bytecodes.IFLE; 49 import static org.graalvm.compiler.bytecode.Bytecodes.IFLT; 50 import static org.graalvm.compiler.bytecode.Bytecodes.IFNE; 51 import static org.graalvm.compiler.bytecode.Bytecodes.IFNONNULL; 52 import static org.graalvm.compiler.bytecode.Bytecodes.IFNULL; 53 import static org.graalvm.compiler.bytecode.Bytecodes.IF_ACMPEQ; 54 import static org.graalvm.compiler.bytecode.Bytecodes.IF_ACMPNE; 55 import static org.graalvm.compiler.bytecode.Bytecodes.IF_ICMPEQ; 56 import static org.graalvm.compiler.bytecode.Bytecodes.IF_ICMPGE; 57 import static org.graalvm.compiler.bytecode.Bytecodes.IF_ICMPGT; 58 import static org.graalvm.compiler.bytecode.Bytecodes.IF_ICMPLE; 59 import static org.graalvm.compiler.bytecode.Bytecodes.IF_ICMPLT; 60 import static org.graalvm.compiler.bytecode.Bytecodes.IF_ICMPNE; 61 import static org.graalvm.compiler.bytecode.Bytecodes.INVOKEDYNAMIC; 62 import static org.graalvm.compiler.bytecode.Bytecodes.INVOKEINTERFACE; 63 import static org.graalvm.compiler.bytecode.Bytecodes.INVOKESPECIAL; 64 import static org.graalvm.compiler.bytecode.Bytecodes.INVOKESTATIC; 65 import static org.graalvm.compiler.bytecode.Bytecodes.INVOKEVIRTUAL; 66 import static org.graalvm.compiler.bytecode.Bytecodes.IRETURN; 67 import static org.graalvm.compiler.bytecode.Bytecodes.JSR; 68 import static org.graalvm.compiler.bytecode.Bytecodes.JSR_W; 69 import static org.graalvm.compiler.bytecode.Bytecodes.LALOAD; 70 import static org.graalvm.compiler.bytecode.Bytecodes.LASTORE; 71 import static org.graalvm.compiler.bytecode.Bytecodes.LOOKUPSWITCH; 72 import static org.graalvm.compiler.bytecode.Bytecodes.LRETURN; 73 import static org.graalvm.compiler.bytecode.Bytecodes.PUTFIELD; 74 import static org.graalvm.compiler.bytecode.Bytecodes.RET; 75 import static org.graalvm.compiler.bytecode.Bytecodes.RETURN; 76 import static org.graalvm.compiler.bytecode.Bytecodes.SALOAD; 77 import static org.graalvm.compiler.bytecode.Bytecodes.SASTORE; 78 import static org.graalvm.compiler.bytecode.Bytecodes.TABLESWITCH; 79 import static org.graalvm.compiler.core.common.GraalOptions.SupportJsrBytecodes; 80 81 import java.util.ArrayList; 82 import java.util.Arrays; 83 import java.util.Collection; 84 import java.util.HashSet; 85 import java.util.Iterator; 86 import java.util.List; 87 import java.util.TreeSet; 88 89 import org.graalvm.compiler.bytecode.Bytecode; 90 import org.graalvm.compiler.bytecode.BytecodeLookupSwitch; 91 import org.graalvm.compiler.bytecode.BytecodeStream; 92 import org.graalvm.compiler.bytecode.BytecodeSwitch; 93 import org.graalvm.compiler.bytecode.BytecodeTableSwitch; 94 import org.graalvm.compiler.bytecode.Bytecodes; 95 import org.graalvm.compiler.core.common.PermanentBailoutException; 96 import org.graalvm.compiler.debug.DebugContext; 97 import org.graalvm.compiler.options.OptionValues; 98 import org.graalvm.util.EconomicMap; 99 import org.graalvm.util.Equivalence; 100 101 import jdk.vm.ci.code.BytecodeFrame; 102 import jdk.vm.ci.meta.ExceptionHandler; 103 104 /** 105 * Builds a mapping between bytecodes and basic blocks and builds a conservative control flow graph 106 * (CFG). It makes one linear passes over the bytecodes to build the CFG where it detects block 107 * headers and connects them. 108 * <p> 109 * It also creates exception dispatch blocks for exception handling. These blocks are between a 110 * bytecode that might throw an exception, and the actual exception handler entries, and are later 111 * used to create the type checks with the exception handler catch types. If a bytecode is covered 112 * by an exception handler, this bytecode ends the basic block. This guarantees that a) control flow 113 * cannot be transferred to an exception dispatch block in the middle of a block, and b) that every 114 * block has at most one exception dispatch block (which is always the last entry in the successor 115 * list). 116 * <p> 117 * If a bytecode is covered by multiple exception handlers, a chain of exception dispatch blocks is 118 * created so that multiple exception handler types can be checked. The chains are re-used if 119 * multiple bytecodes are covered by the same exception handlers. 120 * <p> 121 * Note that exception unwinds, i.e., bytecodes that can throw an exception but the exception is not 122 * handled in this method, do not end a basic block. Not modeling the exception unwind block reduces 123 * the complexity of the CFG, and there is no algorithm yet where the exception unwind block would 124 * matter. 125 * <p> 126 * The class also handles subroutines (jsr and ret bytecodes): subroutines are inlined by 127 * duplicating the subroutine blocks. This is limited to simple, structured subroutines with a 128 * maximum subroutine nesting of 4. Otherwise, a bailout is thrown. 129 * <p> 130 * Loops in the methods are detected. If a method contains an irreducible loop (a loop with more 131 * than one entry), a bailout is thrown. This simplifies the compiler later on since only structured 132 * loops need to be supported. 133 * <p> 134 * A data flow analysis computes the live local variables from the point of view of the interpreter. 135 * The result is used later to prune frame states, i.e., remove local variable entries that are 136 * guaranteed to be never used again (even in the case of deoptimization). 137 * <p> 138 * The algorithms and analysis in this class are conservative and do not use any assumptions or 139 * profiling information. 140 */ 141 public final class BciBlockMapping { 142 143 public static class BciBlock implements Cloneable { 144 145 protected int id; 146 public int startBci; 147 public int endBci; 148 public boolean isExceptionEntry; 149 public boolean isLoopHeader; 150 public int loopId; 151 public int loopEnd; 152 protected List<BciBlock> successors; 153 private int predecessorCount; 154 155 private boolean visited; 156 private boolean active; 157 public long loops; 158 public JSRData jsrData; 159 160 public static class JSRData implements Cloneable { 161 public EconomicMap<JsrScope, BciBlock> jsrAlternatives; 162 public JsrScope jsrScope = JsrScope.EMPTY_SCOPE; 163 public BciBlock jsrSuccessor; 164 public int jsrReturnBci; 165 public BciBlock retSuccessor; 166 public boolean endsWithRet = false; 167 168 public JSRData copy() { 169 try { 170 return (JSRData) this.clone(); 171 } catch (CloneNotSupportedException e) { 172 return null; 173 } 174 } 175 } 176 177 public BciBlock() { 178 this.successors = new ArrayList<>(4); 179 } 180 181 public BciBlock exceptionDispatchBlock() { 182 if (successors.size() > 0 && successors.get(successors.size() - 1) instanceof ExceptionDispatchBlock) { 183 return successors.get(successors.size() - 1); 184 } 185 return null; 186 } 187 188 public int getId() { 189 return id; 190 } 191 192 public int getPredecessorCount() { 193 return this.predecessorCount; 194 } 195 196 public int numNormalSuccessors() { 197 if (exceptionDispatchBlock() != null) { 198 return successors.size() - 1; 199 } 200 return successors.size(); 201 } 202 203 public BciBlock copy() { 204 try { 205 BciBlock block = (BciBlock) super.clone(); 206 if (block.jsrData != null) { 207 block.jsrData = block.jsrData.copy(); 208 } 209 block.successors = new ArrayList<>(successors); 210 return block; 211 } catch (CloneNotSupportedException e) { 212 throw new RuntimeException(e); 213 } 214 } 215 216 @Override 217 public String toString() { 218 StringBuilder sb = new StringBuilder("B").append(getId()); 219 sb.append('[').append(startBci).append("->").append(endBci); 220 if (isLoopHeader || isExceptionEntry) { 221 sb.append(' '); 222 if (isLoopHeader) { 223 sb.append('L'); 224 } 225 if (isExceptionEntry) { 226 sb.append('!'); 227 } 228 } 229 sb.append(']'); 230 return sb.toString(); 231 } 232 233 public int getLoopDepth() { 234 return Long.bitCount(loops); 235 } 236 237 public boolean isLoopHeader() { 238 return isLoopHeader; 239 } 240 241 public boolean isExceptionEntry() { 242 return isExceptionEntry; 243 } 244 245 public BciBlock getSuccessor(int index) { 246 return successors.get(index); 247 } 248 249 /** 250 * Get the loop id of the inner most loop. 251 * 252 * @return the loop id of the most inner loop or -1 if not part of any loop 253 */ 254 public int getLoopId() { 255 long l = loops; 256 if (l == 0) { 257 return -1; 258 } 259 int pos = 0; 260 for (int lMask = 1; (l & lMask) == 0; lMask = lMask << 1) { 261 pos++; 262 } 263 return pos; 264 } 265 266 /** 267 * Iterate over loop ids. 268 */ 269 public Iterable<Integer> loopIdIterable() { 270 return new Iterable<Integer>() { 271 @Override 272 public Iterator<Integer> iterator() { 273 return idIterator(loops); 274 } 275 }; 276 } 277 278 private static Iterator<Integer> idIterator(long field) { 279 return new Iterator<Integer>() { 280 281 long l = field; 282 int pos = 0; 283 int lMask = 1; 284 285 @Override 286 public Integer next() { 287 for (; (l & lMask) == 0; lMask = lMask << 1) { 288 pos++; 289 } 290 l &= ~lMask; 291 return pos; 292 } 293 294 @Override 295 public boolean hasNext() { 296 return l != 0; 297 } 298 }; 299 300 } 301 302 public double probability() { 303 return 1D; 304 } 305 306 public BciBlock getPostdominator() { 307 return null; 308 } 309 310 private JSRData getOrCreateJSRData() { 311 if (jsrData == null) { 312 jsrData = new JSRData(); 313 } 314 return jsrData; 315 } 316 317 void setEndsWithRet() { 318 getOrCreateJSRData().endsWithRet = true; 319 } 320 321 public JsrScope getJsrScope() { 322 if (this.jsrData == null) { 323 return JsrScope.EMPTY_SCOPE; 324 } else { 325 return jsrData.jsrScope; 326 } 327 } 328 329 public boolean endsWithRet() { 330 if (this.jsrData == null) { 331 return false; 332 } else { 333 return jsrData.endsWithRet; 334 } 335 } 336 337 void setRetSuccessor(BciBlock bciBlock) { 338 this.getOrCreateJSRData().retSuccessor = bciBlock; 339 } 340 341 public BciBlock getRetSuccessor() { 342 if (this.jsrData == null) { 343 return null; 344 } else { 345 return jsrData.retSuccessor; 346 } 347 } 348 349 public BciBlock getJsrSuccessor() { 350 if (this.jsrData == null) { 351 return null; 352 } else { 353 return jsrData.jsrSuccessor; 354 } 355 } 356 357 public int getJsrReturnBci() { 358 if (this.jsrData == null) { 359 return -1; 360 } else { 361 return jsrData.jsrReturnBci; 362 } 363 } 364 365 public EconomicMap<JsrScope, BciBlock> getJsrAlternatives() { 366 if (this.jsrData == null) { 367 return null; 368 } else { 369 return jsrData.jsrAlternatives; 370 } 371 } 372 373 public void initJsrAlternatives() { 374 JSRData data = this.getOrCreateJSRData(); 375 if (data.jsrAlternatives == null) { 376 data.jsrAlternatives = EconomicMap.create(Equivalence.DEFAULT); 377 } 378 } 379 380 void setJsrScope(JsrScope nextScope) { 381 this.getOrCreateJSRData().jsrScope = nextScope; 382 } 383 384 void setJsrSuccessor(BciBlock clone) { 385 this.getOrCreateJSRData().jsrSuccessor = clone; 386 } 387 388 void setJsrReturnBci(int bci) { 389 this.getOrCreateJSRData().jsrReturnBci = bci; 390 } 391 392 public int getSuccessorCount() { 393 return successors.size(); 394 } 395 396 public List<BciBlock> getSuccessors() { 397 return successors; 398 } 399 400 void setId(int i) { 401 this.id = i; 402 } 403 404 public void addSuccessor(BciBlock sux) { 405 successors.add(sux); 406 sux.predecessorCount++; 407 } 408 409 public void clearSucccessors() { 410 for (BciBlock sux : successors) { 411 sux.predecessorCount--; 412 } 413 successors.clear(); 414 } 415 } 416 417 public static class ExceptionDispatchBlock extends BciBlock { 418 419 private EconomicMap<ExceptionHandler, ExceptionDispatchBlock> exceptionDispatch = EconomicMap.create(Equivalence.DEFAULT); 420 421 public ExceptionHandler handler; 422 public int deoptBci; 423 } 424 425 /** 426 * The blocks found in this method, in reverse postorder. 427 */ 428 private BciBlock[] blocks; 429 public final Bytecode code; 430 public boolean hasJsrBytecodes; 431 432 private final ExceptionHandler[] exceptionHandlers; 433 private BciBlock startBlock; 434 private BciBlock[] loopHeaders; 435 436 private static final int LOOP_HEADER_MAX_CAPACITY = Long.SIZE; 437 private static final int LOOP_HEADER_INITIAL_CAPACITY = 4; 438 439 private int blocksNotYetAssignedId; 440 private final DebugContext debug; 441 442 /** 443 * Creates a new BlockMap instance from {@code code}. 444 */ 445 private BciBlockMapping(Bytecode code, DebugContext debug) { 446 this.code = code; 447 this.debug = debug; 448 this.exceptionHandlers = code.getExceptionHandlers(); 449 } 450 451 public BciBlock[] getBlocks() { 452 return this.blocks; 453 } 454 455 /** 456 * Builds the block map and conservative CFG and numbers blocks. 457 */ 458 public void build(BytecodeStream stream, OptionValues options) { 459 int codeSize = code.getCodeSize(); 460 BciBlock[] blockMap = new BciBlock[codeSize]; 461 makeExceptionEntries(blockMap); 462 iterateOverBytecodes(blockMap, stream); 463 if (hasJsrBytecodes) { 464 if (!SupportJsrBytecodes.getValue(options)) { 465 throw new JsrNotSupportedBailout("jsr/ret parsing disabled"); 466 } 467 createJsrAlternatives(blockMap, blockMap[0]); 468 } 469 if (debug.isLogEnabled()) { 470 this.log(blockMap, "Before BlockOrder"); 471 } 472 computeBlockOrder(blockMap); 473 fixLoopBits(blockMap); 474 475 assert verify(); 476 477 startBlock = blockMap[0]; 478 if (debug.isLogEnabled()) { 479 this.log(blockMap, "Before LivenessAnalysis"); 480 } 481 } 482 483 private boolean verify() { 484 for (BciBlock block : blocks) { 485 assert blocks[block.getId()] == block; 486 487 for (int i = 0; i < block.getSuccessorCount(); i++) { 488 BciBlock sux = block.getSuccessor(i); 489 if (sux instanceof ExceptionDispatchBlock) { 490 assert i == block.getSuccessorCount() - 1 : "Only one exception handler allowed, and it must be last in successors list"; 491 } 492 } 493 } 494 495 return true; 496 } 497 498 private void makeExceptionEntries(BciBlock[] blockMap) { 499 // start basic blocks at all exception handler blocks and mark them as exception entries 500 for (ExceptionHandler h : this.exceptionHandlers) { 501 BciBlock xhandler = makeBlock(blockMap, h.getHandlerBCI()); 502 xhandler.isExceptionEntry = true; 503 } 504 } 505 506 private void iterateOverBytecodes(BciBlock[] blockMap, BytecodeStream stream) { 507 // iterate over the bytecodes top to bottom. 508 // mark the entrypoints of basic blocks and build lists of successors for 509 // all bytecodes that end basic blocks (i.e. goto, ifs, switches, throw, jsr, returns, ret) 510 BciBlock current = null; 511 stream.setBCI(0); 512 while (stream.currentBC() != Bytecodes.END) { 513 int bci = stream.currentBCI(); 514 515 if (current == null || blockMap[bci] != null) { 516 BciBlock b = makeBlock(blockMap, bci); 517 if (current != null) { 518 addSuccessor(blockMap, current.endBci, b); 519 } 520 current = b; 521 } 522 blockMap[bci] = current; 523 current.endBci = bci; 524 525 switch (stream.currentBC()) { 526 case IRETURN: // fall through 527 case LRETURN: // fall through 528 case FRETURN: // fall through 529 case DRETURN: // fall through 530 case ARETURN: // fall through 531 case RETURN: { 532 current = null; 533 break; 534 } 535 case ATHROW: { 536 current = null; 537 ExceptionDispatchBlock handler = handleExceptions(blockMap, bci); 538 if (handler != null) { 539 addSuccessor(blockMap, bci, handler); 540 } 541 break; 542 } 543 case IFEQ: // fall through 544 case IFNE: // fall through 545 case IFLT: // fall through 546 case IFGE: // fall through 547 case IFGT: // fall through 548 case IFLE: // fall through 549 case IF_ICMPEQ: // fall through 550 case IF_ICMPNE: // fall through 551 case IF_ICMPLT: // fall through 552 case IF_ICMPGE: // fall through 553 case IF_ICMPGT: // fall through 554 case IF_ICMPLE: // fall through 555 case IF_ACMPEQ: // fall through 556 case IF_ACMPNE: // fall through 557 case IFNULL: // fall through 558 case IFNONNULL: { 559 current = null; 560 addSuccessor(blockMap, bci, makeBlock(blockMap, stream.readBranchDest())); 561 addSuccessor(blockMap, bci, makeBlock(blockMap, stream.nextBCI())); 562 break; 563 } 564 case GOTO: 565 case GOTO_W: { 566 current = null; 567 addSuccessor(blockMap, bci, makeBlock(blockMap, stream.readBranchDest())); 568 break; 569 } 570 case TABLESWITCH: { 571 current = null; 572 addSwitchSuccessors(blockMap, bci, new BytecodeTableSwitch(stream, bci)); 573 break; 574 } 575 case LOOKUPSWITCH: { 576 current = null; 577 addSwitchSuccessors(blockMap, bci, new BytecodeLookupSwitch(stream, bci)); 578 break; 579 } 580 case JSR: 581 case JSR_W: { 582 hasJsrBytecodes = true; 583 int target = stream.readBranchDest(); 584 if (target == 0) { 585 throw new JsrNotSupportedBailout("jsr target bci 0 not allowed"); 586 } 587 BciBlock b1 = makeBlock(blockMap, target); 588 current.setJsrSuccessor(b1); 589 current.setJsrReturnBci(stream.nextBCI()); 590 current = null; 591 addSuccessor(blockMap, bci, b1); 592 break; 593 } 594 case RET: { 595 current.setEndsWithRet(); 596 current = null; 597 break; 598 } 599 case INVOKEINTERFACE: 600 case INVOKESPECIAL: 601 case INVOKESTATIC: 602 case INVOKEVIRTUAL: 603 case INVOKEDYNAMIC: { 604 current = null; 605 addSuccessor(blockMap, bci, makeBlock(blockMap, stream.nextBCI())); 606 ExceptionDispatchBlock handler = handleExceptions(blockMap, bci); 607 if (handler != null) { 608 addSuccessor(blockMap, bci, handler); 609 } 610 break; 611 } 612 case IASTORE: 613 case LASTORE: 614 case FASTORE: 615 case DASTORE: 616 case AASTORE: 617 case BASTORE: 618 case CASTORE: 619 case SASTORE: 620 case IALOAD: 621 case LALOAD: 622 case FALOAD: 623 case DALOAD: 624 case AALOAD: 625 case BALOAD: 626 case CALOAD: 627 case SALOAD: 628 case ARRAYLENGTH: 629 case PUTFIELD: 630 case GETFIELD: { 631 ExceptionDispatchBlock handler = handleExceptions(blockMap, bci); 632 if (handler != null) { 633 current = null; 634 addSuccessor(blockMap, bci, makeBlock(blockMap, stream.nextBCI())); 635 addSuccessor(blockMap, bci, handler); 636 } 637 } 638 } 639 stream.next(); 640 } 641 } 642 643 private BciBlock makeBlock(BciBlock[] blockMap, int startBci) { 644 BciBlock oldBlock = blockMap[startBci]; 645 if (oldBlock == null) { 646 BciBlock newBlock = new BciBlock(); 647 blocksNotYetAssignedId++; 648 newBlock.startBci = startBci; 649 blockMap[startBci] = newBlock; 650 return newBlock; 651 652 } else if (oldBlock.startBci != startBci) { 653 // Backward branch into the middle of an already processed block. 654 // Add the correct fall-through successor. 655 BciBlock newBlock = new BciBlock(); 656 blocksNotYetAssignedId++; 657 newBlock.startBci = startBci; 658 newBlock.endBci = oldBlock.endBci; 659 for (BciBlock oldSuccessor : oldBlock.getSuccessors()) { 660 newBlock.addSuccessor(oldSuccessor); 661 } 662 663 oldBlock.endBci = startBci - 1; 664 oldBlock.clearSucccessors(); 665 oldBlock.addSuccessor(newBlock); 666 667 for (int i = startBci; i <= newBlock.endBci; i++) { 668 blockMap[i] = newBlock; 669 } 670 return newBlock; 671 672 } else { 673 return oldBlock; 674 } 675 } 676 677 private void addSwitchSuccessors(BciBlock[] blockMap, int predBci, BytecodeSwitch bswitch) { 678 // adds distinct targets to the successor list 679 Collection<Integer> targets = new TreeSet<>(); 680 for (int i = 0; i < bswitch.numberOfCases(); i++) { 681 targets.add(bswitch.targetAt(i)); 682 } 683 targets.add(bswitch.defaultTarget()); 684 for (int targetBci : targets) { 685 addSuccessor(blockMap, predBci, makeBlock(blockMap, targetBci)); 686 } 687 } 688 689 private static void addSuccessor(BciBlock[] blockMap, int predBci, BciBlock sux) { 690 BciBlock predecessor = blockMap[predBci]; 691 if (sux.isExceptionEntry) { 692 throw new PermanentBailoutException("Exception handler can be reached by both normal and exceptional control flow"); 693 } 694 predecessor.addSuccessor(sux); 695 } 696 697 private final ArrayList<BciBlock> jsrVisited = new ArrayList<>(); 698 699 private void createJsrAlternatives(BciBlock[] blockMap, BciBlock block) { 700 jsrVisited.add(block); 701 JsrScope scope = block.getJsrScope(); 702 703 if (block.endsWithRet()) { 704 block.setRetSuccessor(blockMap[scope.nextReturnAddress()]); 705 block.addSuccessor(block.getRetSuccessor()); 706 assert block.getRetSuccessor() != block.getJsrSuccessor(); 707 } 708 debug.log("JSR alternatives block %s sux %s jsrSux %s retSux %s jsrScope %s", block, block.getSuccessors(), block.getJsrSuccessor(), block.getRetSuccessor(), block.getJsrScope()); 709 710 if (block.getJsrSuccessor() != null || !scope.isEmpty()) { 711 for (int i = 0; i < block.getSuccessorCount(); i++) { 712 BciBlock successor = block.getSuccessor(i); 713 JsrScope nextScope = scope; 714 if (successor == block.getJsrSuccessor()) { 715 nextScope = scope.push(block.getJsrReturnBci()); 716 } 717 if (successor == block.getRetSuccessor()) { 718 nextScope = scope.pop(); 719 } 720 if (!successor.getJsrScope().isPrefixOf(nextScope)) { 721 throw new JsrNotSupportedBailout("unstructured control flow (" + successor.getJsrScope() + " " + nextScope + ")"); 722 } 723 if (!nextScope.isEmpty()) { 724 BciBlock clone; 725 if (successor.getJsrAlternatives() != null && successor.getJsrAlternatives().containsKey(nextScope)) { 726 clone = successor.getJsrAlternatives().get(nextScope); 727 } else { 728 successor.initJsrAlternatives(); 729 clone = successor.copy(); 730 blocksNotYetAssignedId++; 731 clone.setJsrScope(nextScope); 732 successor.getJsrAlternatives().put(nextScope, clone); 733 } 734 block.getSuccessors().set(i, clone); 735 if (successor == block.getJsrSuccessor()) { 736 block.setJsrSuccessor(clone); 737 } 738 if (successor == block.getRetSuccessor()) { 739 block.setRetSuccessor(clone); 740 } 741 } 742 } 743 } 744 for (BciBlock successor : block.getSuccessors()) { 745 if (!jsrVisited.contains(successor)) { 746 createJsrAlternatives(blockMap, successor); 747 } 748 } 749 } 750 751 private EconomicMap<ExceptionHandler, ExceptionDispatchBlock> initialExceptionDispatch; 752 753 private EconomicMap<ExceptionHandler, ExceptionDispatchBlock> getInitialExceptionDispatch() { 754 if (initialExceptionDispatch == null) { 755 initialExceptionDispatch = EconomicMap.create(Equivalence.DEFAULT); 756 } 757 return initialExceptionDispatch; 758 } 759 760 private ExceptionDispatchBlock handleExceptions(BciBlock[] blockMap, int bci) { 761 ExceptionDispatchBlock lastHandler = null; 762 763 for (int i = exceptionHandlers.length - 1; i >= 0; i--) { 764 ExceptionHandler h = exceptionHandlers[i]; 765 if (h.getStartBCI() <= bci && bci < h.getEndBCI()) { 766 if (h.isCatchAll()) { 767 // Discard all information about succeeding exception handlers, since they can 768 // never be reached. 769 lastHandler = null; 770 } 771 772 EconomicMap<ExceptionHandler, ExceptionDispatchBlock> exceptionDispatch = lastHandler != null ? lastHandler.exceptionDispatch : getInitialExceptionDispatch(); 773 ExceptionDispatchBlock curHandler = exceptionDispatch.get(h); 774 if (curHandler == null) { 775 curHandler = new ExceptionDispatchBlock(); 776 blocksNotYetAssignedId++; 777 curHandler.startBci = -1; 778 curHandler.endBci = -1; 779 curHandler.deoptBci = bci; 780 curHandler.handler = h; 781 curHandler.addSuccessor(blockMap[h.getHandlerBCI()]); 782 if (lastHandler != null) { 783 curHandler.addSuccessor(lastHandler); 784 } 785 exceptionDispatch.put(h, curHandler); 786 } 787 lastHandler = curHandler; 788 } 789 } 790 return lastHandler; 791 } 792 793 private boolean loopChanges; 794 795 private void fixLoopBits(BciBlock[] blockMap) { 796 do { 797 loopChanges = false; 798 for (BciBlock b : blocks) { 799 b.visited = false; 800 } 801 802 long loop = fixLoopBits(blockMap, blockMap[0]); 803 804 if (loop != 0) { 805 // There is a path from a loop end to the method entry that does not pass the loop 806 // header. 807 // Therefore, the loop is non reducible (has more than one entry). 808 // We don't want to compile such methods because the IR only supports structured 809 // loops. 810 throw new PermanentBailoutException("Non-reducible loop: %016x", loop); 811 } 812 } while (loopChanges); 813 } 814 815 private void computeBlockOrder(BciBlock[] blockMap) { 816 int maxBlocks = blocksNotYetAssignedId; 817 this.blocks = new BciBlock[blocksNotYetAssignedId]; 818 long loop = computeBlockOrder(blockMap[0]); 819 820 if (loop != 0) { 821 // There is a path from a loop end to the method entry that does not pass the loop 822 // header. Therefore, the loop is non reducible (has more than one entry). 823 // We don't want to compile such methods because the IR only supports structured loops. 824 throw new PermanentBailoutException("Non-reducible loop"); 825 } 826 827 // Purge null entries for unreached blocks and sort blocks such that loop bodies are always 828 // consecutively in the array. 829 int blockCount = maxBlocks - blocksNotYetAssignedId + 1; 830 BciBlock[] newBlocks = new BciBlock[blockCount]; 831 int next = 0; 832 for (int i = 0; i < blocks.length; ++i) { 833 BciBlock b = blocks[i]; 834 if (b != null) { 835 b.setId(next); 836 newBlocks[next++] = b; 837 if (b.isLoopHeader) { 838 next = handleLoopHeader(newBlocks, next, i, b); 839 } 840 } 841 } 842 assert next == newBlocks.length - 1; 843 844 // Add unwind block. 845 ExceptionDispatchBlock unwindBlock = new ExceptionDispatchBlock(); 846 unwindBlock.startBci = -1; 847 unwindBlock.endBci = -1; 848 unwindBlock.deoptBci = code.getMethod().isSynchronized() ? BytecodeFrame.UNWIND_BCI : BytecodeFrame.AFTER_EXCEPTION_BCI; 849 unwindBlock.setId(newBlocks.length - 1); 850 newBlocks[newBlocks.length - 1] = unwindBlock; 851 852 blocks = newBlocks; 853 } 854 855 private int handleLoopHeader(BciBlock[] newBlocks, int nextStart, int i, BciBlock loopHeader) { 856 int next = nextStart; 857 int endOfLoop = nextStart - 1; 858 for (int j = i + 1; j < blocks.length; ++j) { 859 BciBlock other = blocks[j]; 860 if (other != null && (other.loops & (1L << loopHeader.loopId)) != 0) { 861 other.setId(next); 862 endOfLoop = next; 863 newBlocks[next++] = other; 864 blocks[j] = null; 865 if (other.isLoopHeader) { 866 next = handleLoopHeader(newBlocks, next, j, other); 867 } 868 } 869 } 870 loopHeader.loopEnd = endOfLoop; 871 return next; 872 } 873 874 public void log(BciBlock[] blockMap, String name) { 875 if (debug.isLogEnabled()) { 876 String n = System.lineSeparator(); 877 StringBuilder sb = new StringBuilder(debug.getCurrentScopeName()).append("BlockMap ").append(name).append(" :"); 878 sb.append(n); 879 Iterable<BciBlock> it; 880 if (blocks == null) { 881 it = new HashSet<>(Arrays.asList(blockMap)); 882 } else { 883 it = Arrays.asList(blocks); 884 } 885 for (BciBlock b : it) { 886 if (b == null) { 887 continue; 888 } 889 sb.append("B").append(b.getId()).append(" (").append(b.startBci).append(" -> ").append(b.endBci).append(")"); 890 if (b.isLoopHeader) { 891 sb.append(" LoopHeader"); 892 } 893 if (b.isExceptionEntry) { 894 sb.append(" ExceptionEntry"); 895 } 896 sb.append(n).append(" Sux : "); 897 for (BciBlock s : b.getSuccessors()) { 898 sb.append("B").append(s.getId()).append(" (").append(s.startBci).append(" -> ").append(s.endBci).append(")"); 899 if (s.isExceptionEntry) { 900 sb.append("!"); 901 } 902 sb.append(" "); 903 } 904 sb.append(n).append(" Loop : "); 905 for (int pos : b.loopIdIterable()) { 906 sb.append("B").append(loopHeaders[pos].getId()).append(" "); 907 } 908 sb.append(n); 909 } 910 debug.log("%s", sb); 911 } 912 } 913 914 /** 915 * Get the header block for a loop index. 916 */ 917 public BciBlock getLoopHeader(int index) { 918 return loopHeaders[index]; 919 } 920 921 /** 922 * The next available loop number. 923 */ 924 private int nextLoop; 925 926 /** 927 * Mark the block as a loop header, using the next available loop number. Also checks for corner 928 * cases that we don't want to compile. 929 */ 930 private void makeLoopHeader(BciBlock block) { 931 if (!block.isLoopHeader) { 932 block.isLoopHeader = true; 933 934 if (block.isExceptionEntry) { 935 // Loops that are implicitly formed by an exception handler lead to all sorts of 936 // corner cases. 937 // Don't compile such methods for now, until we see a concrete case that allows 938 // checking for correctness. 939 throw new PermanentBailoutException("Loop formed by an exception handler"); 940 } 941 if (nextLoop >= LOOP_HEADER_MAX_CAPACITY) { 942 // This restriction can be removed by using a fall-back to a BitSet in case we have 943 // more than 64 loops 944 // Don't compile such methods for now, until we see a concrete case that allows 945 // checking for correctness. 946 throw new PermanentBailoutException("Too many loops in method"); 947 } 948 949 assert block.loops == 0; 950 block.loops = 1L << nextLoop; 951 debug.log("makeLoopHeader(%s) -> %x", block, block.loops); 952 if (loopHeaders == null) { 953 loopHeaders = new BciBlock[LOOP_HEADER_INITIAL_CAPACITY]; 954 } else if (nextLoop >= loopHeaders.length) { 955 loopHeaders = Arrays.copyOf(loopHeaders, LOOP_HEADER_MAX_CAPACITY); 956 } 957 loopHeaders[nextLoop] = block; 958 block.loopId = nextLoop; 959 nextLoop++; 960 } 961 assert Long.bitCount(block.loops) == 1; 962 } 963 964 /** 965 * Depth-first traversal of the control flow graph. The flag {@linkplain BciBlock#visited} is 966 * used to visit every block only once. The flag {@linkplain BciBlock#active} is used to detect 967 * cycles (backward edges). 968 */ 969 private long computeBlockOrder(BciBlock block) { 970 if (block.visited) { 971 if (block.active) { 972 // Reached block via backward branch. 973 makeLoopHeader(block); 974 // Return cached loop information for this block. 975 return block.loops; 976 } else if (block.isLoopHeader) { 977 return block.loops & ~(1L << block.loopId); 978 } else { 979 return block.loops; 980 } 981 } 982 983 block.visited = true; 984 block.active = true; 985 986 long loops = 0; 987 for (BciBlock successor : block.getSuccessors()) { 988 // Recursively process successors. 989 loops |= computeBlockOrder(successor); 990 if (successor.active) { 991 // Reached block via backward branch. 992 loops |= (1L << successor.loopId); 993 } 994 } 995 996 block.loops = loops; 997 debug.log("computeBlockOrder(%s) -> %x", block, block.loops); 998 999 if (block.isLoopHeader) { 1000 loops &= ~(1L << block.loopId); 1001 } 1002 1003 block.active = false; 1004 blocksNotYetAssignedId--; 1005 blocks[blocksNotYetAssignedId] = block; 1006 1007 return loops; 1008 } 1009 1010 private long fixLoopBits(BciBlock[] blockMap, BciBlock block) { 1011 if (block.visited) { 1012 // Return cached loop information for this block. 1013 if (block.isLoopHeader) { 1014 return block.loops & ~(1L << block.loopId); 1015 } else { 1016 return block.loops; 1017 } 1018 } 1019 1020 block.visited = true; 1021 long loops = block.loops; 1022 for (BciBlock successor : block.getSuccessors()) { 1023 // Recursively process successors. 1024 loops |= fixLoopBits(blockMap, successor); 1025 } 1026 if (block.loops != loops) { 1027 loopChanges = true; 1028 block.loops = loops; 1029 debug.log("fixLoopBits0(%s) -> %x", block, block.loops); 1030 } 1031 1032 if (block.isLoopHeader) { 1033 loops &= ~(1L << block.loopId); 1034 } 1035 1036 return loops; 1037 } 1038 1039 public static BciBlockMapping create(BytecodeStream stream, Bytecode code, OptionValues options, DebugContext debug) { 1040 BciBlockMapping map = new BciBlockMapping(code, debug); 1041 map.build(stream, options); 1042 if (debug.isDumpEnabled(DebugContext.INFO_LEVEL)) { 1043 debug.dump(DebugContext.INFO_LEVEL, map, code.getMethod().format("After block building %f %R %H.%n(%P)")); 1044 } 1045 1046 return map; 1047 } 1048 1049 public BciBlock[] getLoopHeaders() { 1050 return loopHeaders; 1051 } 1052 1053 public BciBlock getStartBlock() { 1054 return startBlock; 1055 } 1056 1057 public ExceptionDispatchBlock getUnwindBlock() { 1058 return (ExceptionDispatchBlock) blocks[blocks.length - 1]; 1059 } 1060 1061 public int getLoopCount() { 1062 return nextLoop; 1063 } 1064 1065 public int getBlockCount() { 1066 return blocks.length; 1067 } 1068 }