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