1/* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.dx.cf.code; 18 19import com.android.dx.rop.code.*; 20import com.android.dx.rop.cst.CstInteger; 21import com.android.dx.rop.cst.CstType; 22import com.android.dx.rop.type.Prototype; 23import com.android.dx.rop.type.StdTypeList; 24import com.android.dx.rop.type.Type; 25import com.android.dx.rop.type.TypeList; 26import com.android.dx.util.Bits; 27import com.android.dx.util.Hex; 28import com.android.dx.util.IntList; 29 30import java.util.ArrayList; 31import java.util.BitSet; 32import java.util.HashMap; 33 34/** 35 * Utility that converts a basic block list into a list of register-oriented 36 * blocks. 37 */ 38public final class Ropper { 39 /** label offset for the parameter assignment block */ 40 private static final int PARAM_ASSIGNMENT = -1; 41 42 /** label offset for the return block */ 43 private static final int RETURN = -2; 44 45 /** label offset for the synchronized method final return block */ 46 private static final int SYNCH_RETURN = -3; 47 48 /** label offset for the first synchronized method setup block */ 49 private static final int SYNCH_SETUP_1 = -4; 50 51 /** label offset for the second synchronized method setup block */ 52 private static final int SYNCH_SETUP_2 = -5; 53 54 /** 55 * label offset for the first synchronized method exception 56 * handler block 57 */ 58 private static final int SYNCH_CATCH_1 = -6; 59 60 /** 61 * label offset for the second synchronized method exception 62 * handler block 63 */ 64 private static final int SYNCH_CATCH_2 = -7; 65 66 /** number of special label offsets */ 67 private static final int SPECIAL_LABEL_COUNT = 7; 68 69 /** {@code non-null;} method being converted */ 70 private final ConcreteMethod method; 71 72 /** {@code non-null;} original block list */ 73 private final ByteBlockList blocks; 74 75 /** max locals of the method */ 76 private final int maxLocals; 77 78 /** max label (exclusive) of any original bytecode block */ 79 private final int maxLabel; 80 81 /** {@code non-null;} simulation machine to use */ 82 private final RopperMachine machine; 83 84 /** {@code non-null;} simulator to use */ 85 private final Simulator sim; 86 87 /** 88 * {@code non-null;} sparse array mapping block labels to initial frame 89 * contents, if known 90 */ 91 private final Frame[] startFrames; 92 93 /** {@code non-null;} output block list in-progress */ 94 private final ArrayList<BasicBlock> result; 95 96 /** 97 * {@code non-null;} list of subroutine-nest labels 98 * (See {@link Frame#getSubroutines} associated with each result block. 99 * Parallel to {@link Ropper#result}. 100 */ 101 private final ArrayList<IntList> resultSubroutines; 102 103 /** 104 * {@code non-null;} for each block (by label) that is used as an exception 105 * handler, the type of exception it catches 106 */ 107 private final Type[] catchTypes; 108 109 /** 110 * whether an exception-handler block for a synchronized method was 111 * ever required 112 */ 113 private boolean synchNeedsExceptionHandler; 114 115 /** 116 * {@code non-null;} list of subroutines indexed by label of start 117 * address */ 118 private final Subroutine[] subroutines; 119 120 /** true if {@code subroutines} is non-empty */ 121 private boolean hasSubroutines; 122 123 /** 124 * Keeps track of subroutines that exist in java form and are inlined in 125 * Rop form. 126 */ 127 private class Subroutine { 128 /** list of all blocks that jsr to this subroutine */ 129 private BitSet callerBlocks; 130 /** List of all blocks that return from this subroutine */ 131 private BitSet retBlocks; 132 /** first block in this subroutine */ 133 private int startBlock; 134 135 /** 136 * Constructs instance. 137 * 138 * @param startBlock First block of the subroutine. 139 */ 140 Subroutine(int startBlock) { 141 this.startBlock = startBlock; 142 retBlocks = new BitSet(maxLabel); 143 callerBlocks = new BitSet(maxLabel); 144 hasSubroutines = true; 145 } 146 147 /** 148 * Constructs instance. 149 * 150 * @param startBlock First block of the subroutine. 151 * @param retBlock one of the ret blocks (final blocks) of this 152 * subroutine. 153 */ 154 Subroutine(int startBlock, int retBlock) { 155 this(startBlock); 156 addRetBlock(retBlock); 157 } 158 159 /** 160 * @return {@code >= 0;} the label of the subroutine's start block. 161 */ 162 int getStartBlock() { 163 return startBlock; 164 } 165 166 /** 167 * Adds a label to the list of ret blocks (final blocks) for this 168 * subroutine. 169 * 170 * @param retBlock ret block label 171 */ 172 void addRetBlock(int retBlock) { 173 retBlocks.set(retBlock); 174 } 175 176 /** 177 * Adds a label to the list of caller blocks for this subroutine. 178 * 179 * @param label a block that invokes this subroutine. 180 */ 181 void addCallerBlock(int label) { 182 callerBlocks.set(label); 183 } 184 185 /** 186 * Generates a list of subroutine successors. Note: successor blocks 187 * could be listed more than once. This is ok, because this successor 188 * list (and the block it's associated with) will be copied and inlined 189 * before we leave the ropper. Redundent successors will result in 190 * redundent (no-op) merges. 191 * 192 * @return all currently known successors 193 * (return destinations) for that subroutine 194 */ 195 IntList getSuccessors() { 196 IntList successors = new IntList(callerBlocks.size()); 197 198 /* 199 * For each subroutine caller, get it's target. If the 200 * target is us, add the ret target (subroutine successor) 201 * to our list 202 */ 203 204 for (int label = callerBlocks.nextSetBit(0); label >= 0; 205 label = callerBlocks.nextSetBit(label+1)) { 206 BasicBlock subCaller = labelToBlock(label); 207 successors.add(subCaller.getSuccessors().get(0)); 208 } 209 210 successors.setImmutable(); 211 212 return successors; 213 } 214 215 /** 216 * Merges the specified frame into this subroutine's successors, 217 * setting {@code workSet} as appropriate. To be called with 218 * the frame of a subroutine ret block. 219 * 220 * @param frame {@code non-null;} frame from ret block to merge 221 * @param workSet {@code non-null;} workset to update 222 */ 223 void mergeToSuccessors(Frame frame, int[] workSet) { 224 for (int label = callerBlocks.nextSetBit(0); label >= 0; 225 label = callerBlocks.nextSetBit(label+1)) { 226 BasicBlock subCaller = labelToBlock(label); 227 int succLabel = subCaller.getSuccessors().get(0); 228 229 Frame subFrame = frame.subFrameForLabel(startBlock, label); 230 231 if (subFrame != null) { 232 mergeAndWorkAsNecessary(succLabel, -1, null, 233 subFrame, workSet); 234 } else { 235 Bits.set(workSet, label); 236 } 237 } 238 } 239 } 240 241 /** 242 * Converts a {@link ConcreteMethod} to a {@link RopMethod}. 243 * 244 * @param method {@code non-null;} method to convert 245 * @param advice {@code non-null;} translation advice to use 246 * @return {@code non-null;} the converted instance 247 */ 248 public static RopMethod convert(ConcreteMethod method, 249 TranslationAdvice advice) { 250 try { 251 Ropper r = new Ropper(method, advice); 252 r.doit(); 253 return r.getRopMethod(); 254 } catch (SimException ex) { 255 ex.addContext("...while working on method " + 256 method.getNat().toHuman()); 257 throw ex; 258 } 259 } 260 261 /** 262 * Constructs an instance. This class is not publicly instantiable; use 263 * {@link #convert}. 264 * 265 * @param method {@code non-null;} method to convert 266 * @param advice {@code non-null;} translation advice to use 267 */ 268 private Ropper(ConcreteMethod method, TranslationAdvice advice) { 269 if (method == null) { 270 throw new NullPointerException("method == null"); 271 } 272 273 if (advice == null) { 274 throw new NullPointerException("advice == null"); 275 } 276 277 this.method = method; 278 this.blocks = BasicBlocker.identifyBlocks(method); 279 this.maxLabel = blocks.getMaxLabel(); 280 this.maxLocals = method.getMaxLocals(); 281 this.machine = new RopperMachine(this, method, advice); 282 this.sim = new Simulator(machine, method); 283 this.startFrames = new Frame[maxLabel]; 284 this.subroutines = new Subroutine[maxLabel]; 285 286 /* 287 * The "* 2 + 10" below is to conservatively believe that every 288 * block is an exception handler target and should also 289 * take care of enough other possible extra overhead such that 290 * the underlying array is unlikely to need resizing. 291 */ 292 this.result = new ArrayList<BasicBlock>(blocks.size() * 2 + 10); 293 this.resultSubroutines = 294 new ArrayList<IntList>(blocks.size() * 2 + 10); 295 296 this.catchTypes = new Type[maxLabel]; 297 this.synchNeedsExceptionHandler = false; 298 299 /* 300 * Set up the first stack frame with the right limits, but leave it 301 * empty here (to be filled in outside of the constructor). 302 */ 303 startFrames[0] = new Frame(maxLocals, method.getMaxStack()); 304 } 305 306 /** 307 * Gets the first (lowest) register number to use as the temporary 308 * area when unwinding stack manipulation ops. 309 * 310 * @return {@code >= 0;} the first register to use 311 */ 312 /*package*/ int getFirstTempStackReg() { 313 /* 314 * We use the register that is just past the deepest possible 315 * stack element, plus one if the method is synchronized to 316 * avoid overlapping with the synch register. We don't need to 317 * do anything else special at this level, since later passes 318 * will merely notice the highest register used by explicit 319 * inspection. 320 */ 321 int regCount = getNormalRegCount(); 322 return isSynchronized() ? regCount + 1 : regCount; 323 } 324 325 /** 326 * Gets the label for the exception handler setup block corresponding 327 * to the given label. 328 * 329 * @param label {@code >= 0;} the original label 330 * @return {@code >= 0;} the corresponding exception handler setup label 331 */ 332 private int getExceptionSetupLabel(int label) { 333 return maxLabel + label; 334 } 335 336 /** 337 * Gets the label for the given special-purpose block. The given label 338 * should be one of the static constants defined by this class. 339 * 340 * @param label {@code < 0;} the special label constant 341 * @return {@code >= 0;} the actual label value to use 342 */ 343 private int getSpecialLabel(int label) { 344 /* 345 * The label is bitwise-complemented so that mistakes where 346 * LABEL is used instead of getSpecialLabel(LABEL) cause a 347 * failure at block construction time, since negative labels 348 * are illegal. We multiply maxLabel by 2 since 0..maxLabel 349 * (exclusive) are the original blocks and 350 * maxLabel..(maxLabel*2) are reserved for exception handler 351 * setup blocks (see getExceptionSetupLabel(), above). 352 */ 353 return (maxLabel * 2) + ~label; 354 } 355 356 /** 357 * Gets the minimum label for unreserved use. 358 * 359 * @return {@code >= 0;} the minimum label 360 */ 361 private int getMinimumUnreservedLabel() { 362 /* 363 * The labels below ((maxLabel * 2) + SPECIAL_LABEL_COUNT) are 364 * reserved for particular uses. 365 */ 366 367 return (maxLabel * 2) + SPECIAL_LABEL_COUNT; 368 } 369 370 /** 371 * Gets an arbitrary unreserved and available label. 372 * 373 * @return {@code >= 0;} the label 374 */ 375 private int getAvailableLabel() { 376 int candidate = getMinimumUnreservedLabel(); 377 378 for (BasicBlock bb : result) { 379 int label = bb.getLabel(); 380 if (label >= candidate) { 381 candidate = label + 1; 382 } 383 } 384 385 return candidate; 386 } 387 388 /** 389 * Gets whether the method being translated is synchronized. 390 * 391 * @return whether the method being translated is synchronized 392 */ 393 private boolean isSynchronized() { 394 int accessFlags = method.getAccessFlags(); 395 return (accessFlags & AccessFlags.ACC_SYNCHRONIZED) != 0; 396 } 397 398 /** 399 * Gets whether the method being translated is static. 400 * 401 * @return whether the method being translated is static 402 */ 403 private boolean isStatic() { 404 int accessFlags = method.getAccessFlags(); 405 return (accessFlags & AccessFlags.ACC_STATIC) != 0; 406 } 407 408 /** 409 * Gets the total number of registers used for "normal" purposes (i.e., 410 * for the straightforward translation from the original Java). 411 * 412 * @return {@code >= 0;} the total number of registers used 413 */ 414 private int getNormalRegCount() { 415 return maxLocals + method.getMaxStack(); 416 } 417 418 /** 419 * Gets the register spec to use to hold the object to synchronize on, 420 * for a synchronized method. 421 * 422 * @return {@code non-null;} the register spec 423 */ 424 private RegisterSpec getSynchReg() { 425 /* 426 * We use the register that is just past the deepest possible 427 * stack element, with a minimum of v1 since v0 is what's 428 * always used to hold the caught exception when unwinding. We 429 * don't need to do anything else special at this level, since 430 * later passes will merely notice the highest register used 431 * by explicit inspection. 432 */ 433 int reg = getNormalRegCount(); 434 return RegisterSpec.make((reg < 1) ? 1 : reg, Type.OBJECT); 435 } 436 437 /** 438 * Searches {@link #result} for a block with the given label. Returns its 439 * index if found, or returns {@code -1} if there is no such block. 440 * 441 * @param label the label to look for 442 * @return {@code >= -1;} the index for the block with the given label or 443 * {@code -1} if there is no such block 444 */ 445 private int labelToResultIndex(int label) { 446 int sz = result.size(); 447 for (int i = 0; i < sz; i++) { 448 BasicBlock one = result.get(i); 449 if (one.getLabel() == label) { 450 return i; 451 } 452 } 453 454 return -1; 455 } 456 457 /** 458 * Searches {@link #result} for a block with the given label. Returns it if 459 * found, or throws an exception if there is no such block. 460 * 461 * @param label the label to look for 462 * @return {@code non-null;} the block with the given label 463 */ 464 private BasicBlock labelToBlock(int label) { 465 int idx = labelToResultIndex(label); 466 467 if (idx < 0) { 468 throw new IllegalArgumentException("no such label " + 469 Hex.u2(label)); 470 } 471 472 return result.get(idx); 473 } 474 475 /** 476 * Adds a block to the output result. 477 * 478 * @param block {@code non-null;} the block to add 479 * @param subroutines {@code non-null;} subroutine label list 480 * as described in {@link Frame#getSubroutines} 481 */ 482 private void addBlock(BasicBlock block, IntList subroutines) { 483 if (block == null) { 484 throw new NullPointerException("block == null"); 485 } 486 487 result.add(block); 488 subroutines.throwIfMutable(); 489 resultSubroutines.add(subroutines); 490 } 491 492 /** 493 * Adds or replace a block in the output result. If this is a 494 * replacement, then any extra blocks that got added with the 495 * original get removed as a result of calling this method. 496 * 497 * @param block {@code non-null;} the block to add or replace 498 * @param subroutines {@code non-null;} subroutine label list 499 * as described in {@link Frame#getSubroutines} 500 * @return {@code true} if the block was replaced or 501 * {@code false} if it was added for the first time 502 */ 503 private boolean addOrReplaceBlock(BasicBlock block, IntList subroutines) { 504 if (block == null) { 505 throw new NullPointerException("block == null"); 506 } 507 508 int idx = labelToResultIndex(block.getLabel()); 509 boolean ret; 510 511 if (idx < 0) { 512 ret = false; 513 } else { 514 /* 515 * We are replacing a pre-existing block, so find any 516 * blocks that got added as part of the original and 517 * remove those too. Such blocks are (possibly indirect) 518 * successors of this block which are out of the range of 519 * normally-translated blocks. 520 */ 521 removeBlockAndSpecialSuccessors(idx); 522 ret = true; 523 } 524 525 result.add(block); 526 subroutines.throwIfMutable(); 527 resultSubroutines.add(subroutines); 528 return ret; 529 } 530 531 /** 532 * Adds or replaces a block in the output result. Do not delete 533 * any successors. 534 * 535 * @param block {@code non-null;} the block to add or replace 536 * @param subroutines {@code non-null;} subroutine label list 537 * as described in {@link Frame#getSubroutines} 538 * @return {@code true} if the block was replaced or 539 * {@code false} if it was added for the first time 540 */ 541 private boolean addOrReplaceBlockNoDelete(BasicBlock block, 542 IntList subroutines) { 543 if (block == null) { 544 throw new NullPointerException("block == null"); 545 } 546 547 int idx = labelToResultIndex(block.getLabel()); 548 boolean ret; 549 550 if (idx < 0) { 551 ret = false; 552 } else { 553 result.remove(idx); 554 resultSubroutines.remove(idx); 555 ret = true; 556 } 557 558 result.add(block); 559 subroutines.throwIfMutable(); 560 resultSubroutines.add(subroutines); 561 return ret; 562 } 563 564 /** 565 * Helper for {@link #addOrReplaceBlock} which recursively removes 566 * the given block and all blocks that are (direct and indirect) 567 * successors of it whose labels indicate that they are not in the 568 * normally-translated range. 569 * 570 * @param idx {@code non-null;} block to remove (etc.) 571 */ 572 private void removeBlockAndSpecialSuccessors(int idx) { 573 int minLabel = getMinimumUnreservedLabel(); 574 BasicBlock block = result.get(idx); 575 IntList successors = block.getSuccessors(); 576 int sz = successors.size(); 577 578 result.remove(idx); 579 resultSubroutines.remove(idx); 580 581 for (int i = 0; i < sz; i++) { 582 int label = successors.get(i); 583 if (label >= minLabel) { 584 idx = labelToResultIndex(label); 585 if (idx < 0) { 586 throw new RuntimeException("Invalid label " 587 + Hex.u2(label)); 588 } 589 removeBlockAndSpecialSuccessors(idx); 590 } 591 } 592 } 593 594 /** 595 * Extracts the resulting {@link RopMethod} from the instance. 596 * 597 * @return {@code non-null;} the method object 598 */ 599 private RopMethod getRopMethod() { 600 601 // Construct the final list of blocks. 602 603 int sz = result.size(); 604 BasicBlockList bbl = new BasicBlockList(sz); 605 for (int i = 0; i < sz; i++) { 606 bbl.set(i, result.get(i)); 607 } 608 bbl.setImmutable(); 609 610 // Construct the method object to wrap it all up. 611 612 /* 613 * Note: The parameter assignment block is always the first 614 * that should be executed, hence the second argument to the 615 * constructor. 616 */ 617 return new RopMethod(bbl, getSpecialLabel(PARAM_ASSIGNMENT)); 618 } 619 620 /** 621 * Does the conversion. 622 */ 623 private void doit() { 624 int[] workSet = Bits.makeBitSet(maxLabel); 625 626 Bits.set(workSet, 0); 627 addSetupBlocks(); 628 setFirstFrame(); 629 630 for (;;) { 631 int offset = Bits.findFirst(workSet, 0); 632 if (offset < 0) { 633 break; 634 } 635 Bits.clear(workSet, offset); 636 ByteBlock block = blocks.labelToBlock(offset); 637 Frame frame = startFrames[offset]; 638 try { 639 processBlock(block, frame, workSet); 640 } catch (SimException ex) { 641 ex.addContext("...while working on block " + Hex.u2(offset)); 642 throw ex; 643 } 644 } 645 646 addReturnBlock(); 647 addSynchExceptionHandlerBlock(); 648 addExceptionSetupBlocks(); 649 650 if (hasSubroutines) { 651 // Subroutines are very rare, so skip this step if it's n/a 652 inlineSubroutines(); 653 } 654 } 655 656 /** 657 * Sets up the first frame to contain all the incoming parameters in 658 * locals. 659 */ 660 private void setFirstFrame() { 661 Prototype desc = method.getEffectiveDescriptor(); 662 startFrames[0].initializeWithParameters(desc.getParameterTypes()); 663 startFrames[0].setImmutable(); 664 } 665 666 /** 667 * Processes the given block. 668 * 669 * @param block {@code non-null;} block to process 670 * @param frame {@code non-null;} start frame for the block 671 * @param workSet {@code non-null;} bits representing work to do, 672 * which this method may add to 673 */ 674 private void processBlock(ByteBlock block, Frame frame, int[] workSet) { 675 // Prepare the list of caught exceptions for this block. 676 ByteCatchList catches = block.getCatches(); 677 machine.startBlock(catches.toRopCatchList()); 678 679 /* 680 * Using a copy of the given frame, simulate each instruction, 681 * calling into machine for each. 682 */ 683 frame = frame.copy(); 684 sim.simulate(block, frame); 685 frame.setImmutable(); 686 687 int extraBlockCount = machine.getExtraBlockCount(); 688 ArrayList<Insn> insns = machine.getInsns(); 689 int insnSz = insns.size(); 690 691 /* 692 * Merge the frame into each possible non-exceptional 693 * successor. 694 */ 695 696 int catchSz = catches.size(); 697 IntList successors = block.getSuccessors(); 698 699 int startSuccessorIndex; 700 701 Subroutine calledSubroutine = null; 702 if (machine.hasJsr()) { 703 /* 704 * If this frame ends in a JSR, only merge our frame with 705 * the subroutine start, not the subroutine's return target. 706 */ 707 startSuccessorIndex = 1; 708 709 int subroutineLabel = successors.get(1); 710 711 if (subroutines[subroutineLabel] == null) { 712 subroutines[subroutineLabel] = 713 new Subroutine (subroutineLabel); 714 } 715 716 subroutines[subroutineLabel].addCallerBlock(block.getLabel()); 717 718 calledSubroutine = subroutines[subroutineLabel]; 719 } else if (machine.hasRet()) { 720 /* 721 * This block ends in a ret, which means it's the final block 722 * in some subroutine. Ultimately, this block will be copied 723 * and inlined for each call and then disposed of. 724 */ 725 726 ReturnAddress ra = machine.getReturnAddress(); 727 int subroutineLabel = ra.getSubroutineAddress(); 728 729 if (subroutines[subroutineLabel] == null) { 730 subroutines[subroutineLabel] 731 = new Subroutine (subroutineLabel, block.getLabel()); 732 } else { 733 subroutines[subroutineLabel].addRetBlock(block.getLabel()); 734 } 735 736 successors = subroutines[subroutineLabel].getSuccessors(); 737 subroutines[subroutineLabel] 738 .mergeToSuccessors(frame, workSet); 739 // Skip processing below since we just did it. 740 startSuccessorIndex = successors.size(); 741 } else if (machine.wereCatchesUsed()) { 742 /* 743 * If there are catches, then the first successors 744 * (which will either be all of them or all but the last one) 745 * are catch targets. 746 */ 747 startSuccessorIndex = catchSz; 748 } else { 749 startSuccessorIndex = 0; 750 } 751 752 int succSz = successors.size(); 753 for (int i = startSuccessorIndex; i < succSz; 754 i++) { 755 int succ = successors.get(i); 756 try { 757 mergeAndWorkAsNecessary(succ, block.getLabel(), 758 calledSubroutine, frame, workSet); 759 } catch (SimException ex) { 760 ex.addContext("...while merging to block " + Hex.u2(succ)); 761 throw ex; 762 } 763 } 764 765 if ((succSz == 0) && machine.returns()) { 766 /* 767 * The block originally contained a return, but it has 768 * been made to instead end with a goto, and we need to 769 * tell it at this point that its sole successor is the 770 * return block. This has to happen after the merge loop 771 * above, since, at this point, the return block doesn't 772 * actually exist; it gets synthesized at the end of 773 * processing the original blocks. 774 */ 775 successors = IntList.makeImmutable(getSpecialLabel(RETURN)); 776 succSz = 1; 777 } 778 779 int primarySucc; 780 781 if (succSz == 0) { 782 primarySucc = -1; 783 } else { 784 primarySucc = machine.getPrimarySuccessorIndex(); 785 if (primarySucc >= 0) { 786 primarySucc = successors.get(primarySucc); 787 } 788 } 789 790 /* 791 * This variable is true only when the method is synchronized and 792 * the block being processed can possibly throw an exception. 793 */ 794 boolean synch = isSynchronized() && machine.canThrow(); 795 796 if (synch || (catchSz != 0)) { 797 /* 798 * Deal with exception handlers: Merge an exception-catch 799 * frame into each possible exception handler, and 800 * construct a new set of successors to point at the 801 * exception handler setup blocks (which get synthesized 802 * at the very end of processing). 803 */ 804 boolean catchesAny = false; 805 IntList newSucc = new IntList(succSz); 806 for (int i = 0; i < catchSz; i++) { 807 ByteCatchList.Item one = catches.get(i); 808 CstType exceptionClass = one.getExceptionClass(); 809 int targ = one.getHandlerPc(); 810 811 catchesAny |= (exceptionClass == CstType.OBJECT); 812 813 Frame f = frame.makeExceptionHandlerStartFrame(exceptionClass); 814 815 try { 816 mergeAndWorkAsNecessary(targ, block.getLabel(), 817 null, f, workSet); 818 } catch (SimException ex) { 819 ex.addContext("...while merging exception to block " + 820 Hex.u2(targ)); 821 throw ex; 822 } 823 824 /* 825 * Set up the exception handler type, by setting it if 826 * the given handler has yet to be encountered, or by 827 * conservatively unioning if it has. 828 */ 829 Type already = catchTypes[targ]; 830 if (already == null) { 831 catchTypes[targ] = exceptionClass.getClassType(); 832 } else if (already != exceptionClass.getClassType()) { 833 catchTypes[targ] = Type.OBJECT; 834 } 835 836 /* 837 * The synthesized exception setup block will have the 838 * label getExceptionSetupLabel(targ). 839 */ 840 newSucc.add(getExceptionSetupLabel(targ)); 841 } 842 843 if (synch && !catchesAny) { 844 /* 845 * The method is synchronized and this block doesn't 846 * already have a catch-all handler, so add one to the 847 * end, both in the successors and in the throwing 848 * instruction(s) at the end of the block (which is where 849 * the caught classes live). 850 */ 851 newSucc.add(getSpecialLabel(SYNCH_CATCH_1)); 852 synchNeedsExceptionHandler = true; 853 854 for (int i = insnSz - extraBlockCount - 1; i < insnSz; i++) { 855 Insn insn = insns.get(i); 856 if (insn.canThrow()) { 857 insn = insn.withAddedCatch(Type.OBJECT); 858 insns.set(i, insn); 859 } 860 } 861 } 862 863 if (primarySucc >= 0) { 864 newSucc.add(primarySucc); 865 } 866 867 newSucc.setImmutable(); 868 successors = newSucc; 869 } 870 871 // Construct the final resulting block(s), and store it (them). 872 873 int primarySuccListIndex = successors.indexOf(primarySucc); 874 875 /* 876 * If there are any extra blocks, work backwards through the 877 * list of instructions, adding single-instruction blocks, and 878 * resetting the successors variables as appropriate. 879 */ 880 for (/*extraBlockCount*/; extraBlockCount > 0; extraBlockCount--) { 881 /* 882 * Some of the blocks that the RopperMachine wants added 883 * are for move-result insns, and these need goto insns as well. 884 */ 885 Insn extraInsn = insns.get(--insnSz); 886 boolean needsGoto 887 = extraInsn.getOpcode().getBranchingness() 888 == Rop.BRANCH_NONE; 889 InsnList il = new InsnList(needsGoto ? 2 : 1); 890 IntList extraBlockSuccessors = successors; 891 892 il.set(0, extraInsn); 893 894 if (needsGoto) { 895 il.set(1, new PlainInsn(Rops.GOTO, 896 extraInsn.getPosition(), null, 897 RegisterSpecList.EMPTY)); 898 /* 899 * Obviously, this block won't be throwing an exception 900 * so it should only have one successor. 901 */ 902 extraBlockSuccessors = IntList.makeImmutable(primarySucc); 903 } 904 il.setImmutable(); 905 906 int label = getAvailableLabel(); 907 BasicBlock bb = new BasicBlock(label, il, extraBlockSuccessors, 908 primarySucc); 909 // All of these extra blocks will be in the same subroutine 910 addBlock(bb, frame.getSubroutines()); 911 912 successors = successors.mutableCopy(); 913 successors.set(primarySuccListIndex, label); 914 successors.setImmutable(); 915 primarySucc = label; 916 } 917 918 Insn lastInsn = (insnSz == 0) ? null : insns.get(insnSz - 1); 919 920 /* 921 * Add a goto to the end of the block if it doesn't already 922 * end with a branch, to maintain the invariant that all 923 * blocks end with a branch of some sort or other. Note that 924 * it is possible for there to be blocks for which no 925 * instructions were ever output (e.g., only consist of pop* 926 * in the original Java bytecode). 927 */ 928 if ((lastInsn == null) || 929 (lastInsn.getOpcode().getBranchingness() == Rop.BRANCH_NONE)) { 930 SourcePosition pos = (lastInsn == null) ? SourcePosition.NO_INFO : 931 lastInsn.getPosition(); 932 insns.add(new PlainInsn(Rops.GOTO, pos, null, 933 RegisterSpecList.EMPTY)); 934 insnSz++; 935 } 936 937 /* 938 * Construct a block for the remaining instructions (which in 939 * the usual case is all of them). 940 */ 941 942 InsnList il = new InsnList(insnSz); 943 for (int i = 0; i < insnSz; i++) { 944 il.set(i, insns.get(i)); 945 } 946 il.setImmutable(); 947 948 BasicBlock bb = 949 new BasicBlock(block.getLabel(), il, successors, primarySucc); 950 addOrReplaceBlock(bb, frame.getSubroutines()); 951 } 952 953 /** 954 * Helper for {@link #processBlock}, which merges frames and 955 * adds to the work set, as necessary. 956 * 957 * @param label {@code >= 0;} label to work on 958 * @param pred predecessor label; must be {@code >= 0} when 959 * {@code label} is a subroutine start block and calledSubroutine 960 * is non-null. Otherwise, may be -1. 961 * @param calledSubroutine {@code null-ok;} a Subroutine instance if 962 * {@code label} is the first block in a subroutine. 963 * @param frame {@code non-null;} new frame for the labelled block 964 * @param workSet {@code non-null;} bits representing work to do, 965 * which this method may add to 966 */ 967 private void mergeAndWorkAsNecessary(int label, int pred, 968 Subroutine calledSubroutine, Frame frame, int[] workSet) { 969 Frame existing = startFrames[label]; 970 Frame merged; 971 972 if (existing != null) { 973 /* 974 * Some other block also continues at this label. Merge 975 * the frames, and re-set the bit in the work set if there 976 * was a change. 977 */ 978 if (calledSubroutine != null) { 979 merged = existing.mergeWithSubroutineCaller(frame, 980 calledSubroutine.getStartBlock(), pred); 981 } else { 982 merged = existing.mergeWith(frame); 983 } 984 if (merged != existing) { 985 startFrames[label] = merged; 986 Bits.set(workSet, label); 987 } 988 } else { 989 // This is the first time this label has been encountered. 990 if (calledSubroutine != null) { 991 startFrames[label] 992 = frame.makeNewSubroutineStartFrame(label, pred); 993 } else { 994 startFrames[label] = frame; 995 } 996 Bits.set(workSet, label); 997 } 998 } 999 1000 /** 1001 * Constructs and adds the blocks that perform setup for the rest of 1002 * the method. This includes a first block which merely contains 1003 * assignments from parameters to the same-numbered registers and 1004 * a possible second block which deals with synchronization. 1005 */ 1006 private void addSetupBlocks() { 1007 LocalVariableList localVariables = method.getLocalVariables(); 1008 SourcePosition pos = method.makeSourcePosistion(0); 1009 Prototype desc = method.getEffectiveDescriptor(); 1010 StdTypeList params = desc.getParameterTypes(); 1011 int sz = params.size(); 1012 InsnList insns = new InsnList(sz + 1); 1013 int at = 0; 1014 1015 for (int i = 0; i < sz; i++) { 1016 Type one = params.get(i); 1017 LocalVariableList.Item local = 1018 localVariables.pcAndIndexToLocal(0, at); 1019 RegisterSpec result = (local == null) ? 1020 RegisterSpec.make(at, one) : 1021 RegisterSpec.makeLocalOptional(at, one, local.getLocalItem()); 1022 1023 Insn insn = new PlainCstInsn(Rops.opMoveParam(one), pos, result, 1024 RegisterSpecList.EMPTY, 1025 CstInteger.make(at)); 1026 insns.set(i, insn); 1027 at += one.getCategory(); 1028 } 1029 1030 insns.set(sz, new PlainInsn(Rops.GOTO, pos, null, 1031 RegisterSpecList.EMPTY)); 1032 insns.setImmutable(); 1033 1034 boolean synch = isSynchronized(); 1035 int label = synch ? getSpecialLabel(SYNCH_SETUP_1) : 0; 1036 BasicBlock bb = 1037 new BasicBlock(getSpecialLabel(PARAM_ASSIGNMENT), insns, 1038 IntList.makeImmutable(label), label); 1039 addBlock(bb, IntList.EMPTY); 1040 1041 if (synch) { 1042 RegisterSpec synchReg = getSynchReg(); 1043 Insn insn; 1044 if (isStatic()) { 1045 insn = new ThrowingCstInsn(Rops.CONST_OBJECT, pos, 1046 RegisterSpecList.EMPTY, 1047 StdTypeList.EMPTY, 1048 method.getDefiningClass()); 1049 insns = new InsnList(1); 1050 insns.set(0, insn); 1051 } else { 1052 insns = new InsnList(2); 1053 insn = new PlainCstInsn(Rops.MOVE_PARAM_OBJECT, pos, 1054 synchReg, RegisterSpecList.EMPTY, 1055 CstInteger.VALUE_0); 1056 insns.set(0, insn); 1057 insns.set(1, new PlainInsn(Rops.GOTO, pos, null, 1058 RegisterSpecList.EMPTY)); 1059 } 1060 1061 int label2 = getSpecialLabel(SYNCH_SETUP_2); 1062 insns.setImmutable(); 1063 bb = new BasicBlock(label, insns, 1064 IntList.makeImmutable(label2), label2); 1065 addBlock(bb, IntList.EMPTY); 1066 1067 insns = new InsnList(isStatic() ? 2 : 1); 1068 1069 if (isStatic()) { 1070 insns.set(0, new PlainInsn(Rops.opMoveResultPseudo(synchReg), 1071 pos, synchReg, RegisterSpecList.EMPTY)); 1072 } 1073 1074 insn = new ThrowingInsn(Rops.MONITOR_ENTER, pos, 1075 RegisterSpecList.make(synchReg), 1076 StdTypeList.EMPTY); 1077 insns.set(isStatic() ? 1 :0, insn); 1078 insns.setImmutable(); 1079 bb = new BasicBlock(label2, insns, IntList.makeImmutable(0), 0); 1080 addBlock(bb, IntList.EMPTY); 1081 } 1082 } 1083 1084 /** 1085 * Constructs and adds the return block, if necessary. The return 1086 * block merely contains an appropriate {@code return} 1087 * instruction. 1088 */ 1089 private void addReturnBlock() { 1090 Rop returnOp = machine.getReturnOp(); 1091 1092 if (returnOp == null) { 1093 /* 1094 * The method being converted never returns normally, so there's 1095 * no need for a return block. 1096 */ 1097 return; 1098 } 1099 1100 SourcePosition returnPos = machine.getReturnPosition(); 1101 int label = getSpecialLabel(RETURN); 1102 1103 if (isSynchronized()) { 1104 InsnList insns = new InsnList(1); 1105 Insn insn = new ThrowingInsn(Rops.MONITOR_EXIT, returnPos, 1106 RegisterSpecList.make(getSynchReg()), 1107 StdTypeList.EMPTY); 1108 insns.set(0, insn); 1109 insns.setImmutable(); 1110 1111 int nextLabel = getSpecialLabel(SYNCH_RETURN); 1112 BasicBlock bb = 1113 new BasicBlock(label, insns, 1114 IntList.makeImmutable(nextLabel), nextLabel); 1115 addBlock(bb, IntList.EMPTY); 1116 1117 label = nextLabel; 1118 } 1119 1120 InsnList insns = new InsnList(1); 1121 TypeList sourceTypes = returnOp.getSources(); 1122 RegisterSpecList sources; 1123 1124 if (sourceTypes.size() == 0) { 1125 sources = RegisterSpecList.EMPTY; 1126 } else { 1127 RegisterSpec source = RegisterSpec.make(0, sourceTypes.getType(0)); 1128 sources = RegisterSpecList.make(source); 1129 } 1130 1131 Insn insn = new PlainInsn(returnOp, returnPos, null, sources); 1132 insns.set(0, insn); 1133 insns.setImmutable(); 1134 1135 BasicBlock bb = new BasicBlock(label, insns, IntList.EMPTY, -1); 1136 addBlock(bb, IntList.EMPTY); 1137 } 1138 1139 /** 1140 * Constructs and adds, if necessary, the catch-all exception handler 1141 * block to deal with unwinding the lock taken on entry to a synchronized 1142 * method. 1143 */ 1144 private void addSynchExceptionHandlerBlock() { 1145 if (!synchNeedsExceptionHandler) { 1146 /* 1147 * The method being converted either isn't synchronized or 1148 * can't possibly throw exceptions in its main body, so 1149 * there's no need for a synchronized method exception 1150 * handler. 1151 */ 1152 return; 1153 } 1154 1155 SourcePosition pos = method.makeSourcePosistion(0); 1156 RegisterSpec exReg = RegisterSpec.make(0, Type.THROWABLE); 1157 BasicBlock bb; 1158 Insn insn; 1159 1160 InsnList insns = new InsnList(2); 1161 insn = new PlainInsn(Rops.opMoveException(Type.THROWABLE), pos, 1162 exReg, RegisterSpecList.EMPTY); 1163 insns.set(0, insn); 1164 insn = new ThrowingInsn(Rops.MONITOR_EXIT, pos, 1165 RegisterSpecList.make(getSynchReg()), 1166 StdTypeList.EMPTY); 1167 insns.set(1, insn); 1168 insns.setImmutable(); 1169 1170 int label2 = getSpecialLabel(SYNCH_CATCH_2); 1171 bb = new BasicBlock(getSpecialLabel(SYNCH_CATCH_1), insns, 1172 IntList.makeImmutable(label2), label2); 1173 addBlock(bb, IntList.EMPTY); 1174 1175 insns = new InsnList(1); 1176 insn = new ThrowingInsn(Rops.THROW, pos, 1177 RegisterSpecList.make(exReg), 1178 StdTypeList.EMPTY); 1179 insns.set(0, insn); 1180 insns.setImmutable(); 1181 1182 bb = new BasicBlock(label2, insns, IntList.EMPTY, -1); 1183 addBlock(bb, IntList.EMPTY); 1184 } 1185 1186 /** 1187 * Creates the exception handler setup blocks. "maxLocals" 1188 * below is because that's the register number corresponding 1189 * to the sole element on a one-deep stack (which is the 1190 * situation at the start of an exception handler block). 1191 */ 1192 private void addExceptionSetupBlocks() { 1193 1194 int len = catchTypes.length; 1195 for (int i = 0; i < len; i++) { 1196 Type one = catchTypes[i]; 1197 if (one != null) { 1198 Insn proto = labelToBlock(i).getFirstInsn(); 1199 SourcePosition pos = proto.getPosition(); 1200 InsnList il = new InsnList(2); 1201 1202 Insn insn = new PlainInsn(Rops.opMoveException(one), 1203 pos, 1204 RegisterSpec.make(maxLocals, one), 1205 RegisterSpecList.EMPTY); 1206 il.set(0, insn); 1207 1208 insn = new PlainInsn(Rops.GOTO, pos, null, 1209 RegisterSpecList.EMPTY); 1210 il.set(1, insn); 1211 il.setImmutable(); 1212 1213 BasicBlock bb = new BasicBlock(getExceptionSetupLabel(i), 1214 il, 1215 IntList.makeImmutable(i), 1216 i); 1217 addBlock(bb, startFrames[i].getSubroutines()); 1218 } 1219 } 1220 } 1221 1222 /** 1223 * Checks to see if the basic block is a subroutine caller block. 1224 * 1225 * @param bb {@code non-null;} the basic block in question 1226 * @return true if this block calls a subroutine 1227 */ 1228 private boolean isSubroutineCaller(BasicBlock bb) { 1229 IntList successors = bb.getSuccessors(); 1230 if (successors.size() < 2) return false; 1231 1232 int subLabel = successors.get(1); 1233 1234 return (subLabel < subroutines.length) 1235 && (subroutines[subLabel] != null); 1236 } 1237 1238 /** 1239 * Inlines any subroutine calls. 1240 */ 1241 private void inlineSubroutines() { 1242 final IntList reachableSubroutineCallerLabels = new IntList(4); 1243 1244 /* 1245 * Compile a list of all subroutine calls reachable 1246 * through the normal (non-subroutine) flow. We do this first, since 1247 * we'll be affecting the call flow as we go. 1248 * 1249 * Start at label 0 -- the param assignment block has nothing for us 1250 */ 1251 forEachNonSubBlockDepthFirst(0, new BasicBlock.Visitor() { 1252 public void visitBlock(BasicBlock b) { 1253 if (isSubroutineCaller(b)) { 1254 reachableSubroutineCallerLabels.add(b.getLabel()); 1255 } 1256 } 1257 }); 1258 1259 /* 1260 * Convert the resultSubroutines list, indexed by block index, 1261 * to a label-to-subroutines mapping used by the inliner. 1262 */ 1263 int largestAllocedLabel = getAvailableLabel(); 1264 ArrayList<IntList> labelToSubroutines 1265 = new ArrayList<IntList>(largestAllocedLabel); 1266 for (int i = 0; i < largestAllocedLabel; i++) { 1267 labelToSubroutines.add(null); 1268 } 1269 1270 for (int i = 0; i < result.size(); i++) { 1271 BasicBlock b = result.get(i); 1272 if (b == null) { 1273 continue; 1274 } 1275 IntList subroutineList = resultSubroutines.get(i); 1276 labelToSubroutines.set(b.getLabel(), subroutineList); 1277 } 1278 1279 /* 1280 * Inline all reachable subroutines. 1281 * Inner subroutines will be inlined as they are encountered. 1282 */ 1283 int sz = reachableSubroutineCallerLabels.size(); 1284 for (int i = 0 ; i < sz ; i++) { 1285 int label = reachableSubroutineCallerLabels.get(i); 1286 new SubroutineInliner( 1287 new LabelAllocator(getAvailableLabel()), 1288 labelToSubroutines) 1289 .inlineSubroutineCalledFrom(labelToBlock(label)); 1290 } 1291 1292 // Now find the blocks that aren't reachable and remove them 1293 deleteUnreachableBlocks(); 1294 } 1295 1296 /** 1297 * Deletes all blocks that cannot be reached. This is run to delete 1298 * original subroutine blocks after subroutine inlining. 1299 */ 1300 private void deleteUnreachableBlocks() { 1301 final IntList reachableLabels = new IntList(result.size()); 1302 1303 // subroutine inlining is done now and we won't update this list here 1304 resultSubroutines.clear(); 1305 1306 forEachNonSubBlockDepthFirst(getSpecialLabel(PARAM_ASSIGNMENT), 1307 new BasicBlock.Visitor() { 1308 1309 public void visitBlock(BasicBlock b) { 1310 reachableLabels.add(b.getLabel()); 1311 } 1312 }); 1313 1314 reachableLabels.sort(); 1315 1316 for (int i = result.size() - 1 ; i >= 0 ; i--) { 1317 if (reachableLabels.indexOf(result.get(i).getLabel()) < 0) { 1318 result.remove(i); 1319 // unnecessary here really, since subroutine inlining is done 1320 //resultSubroutines.remove(i); 1321 } 1322 } 1323 } 1324 1325 /** 1326 * Allocates labels, without requiring previously allocated labels 1327 * to have been added to the blocks list. 1328 */ 1329 private static class LabelAllocator { 1330 int nextAvailableLabel; 1331 1332 /** 1333 * @param startLabel available label to start allocating from 1334 */ 1335 LabelAllocator(int startLabel) { 1336 nextAvailableLabel = startLabel; 1337 } 1338 1339 /** 1340 * @return next available label 1341 */ 1342 int getNextLabel() { 1343 return nextAvailableLabel++; 1344 } 1345 } 1346 1347 /** 1348 * Inlines a subroutine. Start by calling 1349 * {@link #inlineSubroutineCalledFrom}. 1350 */ 1351 private class SubroutineInliner { 1352 /** 1353 * maps original label to the label that will be used by the 1354 * inlined version 1355 */ 1356 private final HashMap<Integer, Integer> origLabelToCopiedLabel; 1357 1358 /** set of original labels that need to be copied */ 1359 private final BitSet workList; 1360 1361 /** the label of the original start block for this subroutine */ 1362 private int subroutineStart; 1363 1364 /** the label of the ultimate return block */ 1365 private int subroutineSuccessor; 1366 1367 /** used for generating new labels for copied blocks */ 1368 private final LabelAllocator labelAllocator; 1369 1370 /** 1371 * A mapping, indexed by label, to subroutine nesting list. 1372 * The subroutine nest list is as returned by 1373 * {@link Frame#getSubroutines}. 1374 */ 1375 private final ArrayList<IntList> labelToSubroutines; 1376 1377 SubroutineInliner(final LabelAllocator labelAllocator, 1378 ArrayList<IntList> labelToSubroutines) { 1379 origLabelToCopiedLabel = new HashMap<Integer, Integer>(); 1380 1381 workList = new BitSet(maxLabel); 1382 1383 this.labelAllocator = labelAllocator; 1384 this.labelToSubroutines = labelToSubroutines; 1385 } 1386 1387 /** 1388 * Inlines a subroutine. 1389 * 1390 * @param b block where {@code jsr} occurred in the original bytecode 1391 */ 1392 void inlineSubroutineCalledFrom(final BasicBlock b) { 1393 /* 1394 * The 0th successor of a subroutine caller block is where 1395 * the subroutine should return to. The 1st successor is 1396 * the start block of the subroutine. 1397 */ 1398 subroutineSuccessor = b.getSuccessors().get(0); 1399 subroutineStart = b.getSuccessors().get(1); 1400 1401 /* 1402 * This allocates an initial label and adds the first 1403 * block to the worklist. 1404 */ 1405 int newSubStartLabel = mapOrAllocateLabel(subroutineStart); 1406 1407 for (int label = workList.nextSetBit(0); label >= 0; 1408 label = workList.nextSetBit(0)) { 1409 workList.clear(label); 1410 int newLabel = origLabelToCopiedLabel.get(label); 1411 1412 copyBlock(label, newLabel); 1413 1414 if (isSubroutineCaller(labelToBlock(label))) { 1415 new SubroutineInliner(labelAllocator, labelToSubroutines) 1416 .inlineSubroutineCalledFrom(labelToBlock(newLabel)); 1417 } 1418 } 1419 1420 /* 1421 * Replace the original caller block, since we now have a 1422 * new successor 1423 */ 1424 1425 addOrReplaceBlockNoDelete( 1426 new BasicBlock(b.getLabel(), b.getInsns(), 1427 IntList.makeImmutable (newSubStartLabel), 1428 newSubStartLabel), 1429 labelToSubroutines.get(b.getLabel())); 1430 } 1431 1432 /** 1433 * Copies a basic block, mapping its successors along the way. 1434 * 1435 * @param origLabel original block label 1436 * @param newLabel label that the new block should have 1437 */ 1438 private void copyBlock(int origLabel, int newLabel) { 1439 1440 BasicBlock origBlock = labelToBlock(origLabel); 1441 1442 final IntList origSuccessors = origBlock.getSuccessors(); 1443 IntList successors; 1444 int primarySuccessor = -1; 1445 Subroutine subroutine; 1446 1447 if (isSubroutineCaller(origBlock)) { 1448 /* 1449 * A subroutine call inside a subroutine call. 1450 * Set up so we can recurse. The caller block should have 1451 * it's first successor be a copied block that will be 1452 * the subroutine's return point. It's second successor will 1453 * be copied when we recurse, and remains as the original 1454 * label of the start of the inner subroutine. 1455 */ 1456 1457 successors = IntList.makeImmutable( 1458 mapOrAllocateLabel(origSuccessors.get(0)), 1459 origSuccessors.get(1)); 1460 // primary successor will be set when this block is replaced 1461 } else if (null 1462 != (subroutine = subroutineFromRetBlock(origLabel))) { 1463 /* 1464 * this is a ret block -- its successor 1465 * should be subroutineSuccessor 1466 */ 1467 1468 // Sanity check 1469 if (subroutine.startBlock != subroutineStart) { 1470 throw new RuntimeException ( 1471 "ret instruction returns to label " 1472 + Hex.u2 (subroutine.startBlock) 1473 + " expected: " + Hex.u2(subroutineStart)); 1474 } 1475 1476 successors = IntList.makeImmutable(subroutineSuccessor); 1477 primarySuccessor = subroutineSuccessor; 1478 } else { 1479 // Map all the successor labels 1480 1481 int origPrimary = origBlock.getPrimarySuccessor(); 1482 int sz = origSuccessors.size(); 1483 1484 successors = new IntList(sz); 1485 1486 for (int i = 0 ; i < sz ; i++) { 1487 int origSuccLabel = origSuccessors.get(i); 1488 int newSuccLabel = mapOrAllocateLabel(origSuccLabel); 1489 1490 successors.add(newSuccLabel); 1491 1492 if (origPrimary == origSuccLabel) { 1493 primarySuccessor = newSuccLabel; 1494 } 1495 } 1496 1497 successors.setImmutable(); 1498 } 1499 1500 addBlock ( 1501 new BasicBlock(newLabel, 1502 filterMoveReturnAddressInsns(origBlock.getInsns()), 1503 successors, primarySuccessor), 1504 labelToSubroutines.get(newLabel)); 1505 } 1506 1507 /** 1508 * Checks to see if a specified label is involved in a specified 1509 * subroutine. 1510 * 1511 * @param label {@code >= 0;} a basic block label 1512 * @param subroutineStart {@code >= 0;} a subroutine as identified 1513 * by the label of its start block 1514 * @return true if the block is dominated by the subroutine call 1515 */ 1516 private boolean involvedInSubroutine(int label, int subroutineStart) { 1517 IntList subroutinesList = labelToSubroutines.get(label); 1518 return (subroutinesList.size() > 0 1519 && subroutinesList.top() == subroutineStart); 1520 } 1521 1522 /** 1523 * Maps the label of a pre-copied block to the label of the inlined 1524 * block, allocating a new label and adding it to the worklist 1525 * if necessary. If the origLabel is a "special" label, it 1526 * is returned exactly and not scheduled for duplication: copying 1527 * never proceeds past a special label, which likely is the function 1528 * return block or an immediate predecessor. 1529 * 1530 * @param origLabel label of original, pre-copied block 1531 * @return label for new, inlined block 1532 */ 1533 private int mapOrAllocateLabel(int origLabel) { 1534 int resultLabel; 1535 Integer mappedLabel = origLabelToCopiedLabel.get(origLabel); 1536 1537 if (mappedLabel != null) { 1538 resultLabel = mappedLabel; 1539 } else if (!involvedInSubroutine(origLabel,subroutineStart)) { 1540 /* 1541 * A subroutine has ended by some means other than a "ret" 1542 * (which really means a throw caught later). 1543 */ 1544 resultLabel = origLabel; 1545 } else { 1546 resultLabel = labelAllocator.getNextLabel(); 1547 workList.set(origLabel); 1548 origLabelToCopiedLabel.put(origLabel, resultLabel); 1549 1550 // The new label has the same frame as the original label 1551 while (labelToSubroutines.size() <= resultLabel) { 1552 labelToSubroutines.add(null); 1553 } 1554 labelToSubroutines.set(resultLabel, 1555 labelToSubroutines.get(origLabel)); 1556 } 1557 1558 return resultLabel; 1559 } 1560 } 1561 1562 /** 1563 * Finds a {@code Subroutine} that is returned from by a {@code ret} in 1564 * a given block. 1565 * 1566 * @param label A block that originally contained a {@code ret} instruction 1567 * @return {@code null-ok;} found subroutine or {@code null} if none 1568 * was found 1569 */ 1570 private Subroutine subroutineFromRetBlock(int label) { 1571 for (int i = subroutines.length - 1 ; i >= 0 ; i--) { 1572 if (subroutines[i] != null) { 1573 Subroutine subroutine = subroutines[i]; 1574 1575 if (subroutine.retBlocks.get(label)) { 1576 return subroutine; 1577 } 1578 } 1579 } 1580 1581 return null; 1582 } 1583 1584 1585 /** 1586 * Removes all {@code move-return-address} instructions, returning a new 1587 * {@code InsnList} if necessary. The {@code move-return-address} 1588 * insns are dead code after subroutines have been inlined. 1589 * 1590 * @param insns {@code InsnList} that may contain 1591 * {@code move-return-address} insns 1592 * @return {@code InsnList} with {@code move-return-address} removed 1593 */ 1594 private InsnList filterMoveReturnAddressInsns(InsnList insns) { 1595 int sz; 1596 int newSz = 0; 1597 1598 // First see if we need to filter, and if so what the new size will be 1599 sz = insns.size(); 1600 for (int i = 0; i < sz; i++) { 1601 if (insns.get(i).getOpcode() != Rops.MOVE_RETURN_ADDRESS) { 1602 newSz++; 1603 } 1604 } 1605 1606 if (newSz == sz) { 1607 return insns; 1608 } 1609 1610 // Make a new list without the MOVE_RETURN_ADDRESS insns 1611 InsnList newInsns = new InsnList(newSz); 1612 1613 int newIndex = 0; 1614 for (int i = 0; i < sz; i++) { 1615 Insn insn = insns.get(i); 1616 if (insn.getOpcode() != Rops.MOVE_RETURN_ADDRESS) { 1617 newInsns.set(newIndex++, insn); 1618 } 1619 } 1620 1621 newInsns.setImmutable(); 1622 return newInsns; 1623 } 1624 1625 /** 1626 * Visits each non-subroutine block once in depth-first successor order. 1627 * 1628 * @param firstLabel label of start block 1629 * @param v callback interface 1630 */ 1631 private void forEachNonSubBlockDepthFirst(int firstLabel, 1632 BasicBlock.Visitor v) { 1633 forEachNonSubBlockDepthFirst0(labelToBlock(firstLabel), 1634 v, new BitSet(maxLabel)); 1635 } 1636 1637 /** 1638 * Visits each block once in depth-first successor order, ignoring 1639 * {@code jsr} targets. Worker for {@link #forEachNonSubBlockDepthFirst}. 1640 * 1641 * @param next next block to visit 1642 * @param v callback interface 1643 * @param visited set of blocks already visited 1644 */ 1645 private void forEachNonSubBlockDepthFirst0( 1646 BasicBlock next, BasicBlock.Visitor v, BitSet visited) { 1647 v.visitBlock(next); 1648 visited.set(next.getLabel()); 1649 1650 IntList successors = next.getSuccessors(); 1651 int sz = successors.size(); 1652 1653 for (int i = 0; i < sz; i++) { 1654 int succ = successors.get(i); 1655 1656 if (visited.get(succ)) { 1657 continue; 1658 } 1659 1660 if (isSubroutineCaller(next) && i > 0) { 1661 // ignore jsr targets 1662 continue; 1663 } 1664 1665 /* 1666 * Ignore missing labels: they're successors of 1667 * subroutines that never invoke a ret. 1668 */ 1669 int idx = labelToResultIndex(succ); 1670 if (idx >= 0) { 1671 forEachNonSubBlockDepthFirst0(result.get(idx), v, visited); 1672 } 1673 } 1674 } 1675} 1676