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.dex.code; 18 19import com.android.dx.dex.DexOptions; 20import com.android.dx.io.Opcodes; 21import com.android.dx.rop.code.BasicBlock; 22import com.android.dx.rop.code.BasicBlockList; 23import com.android.dx.rop.code.FillArrayDataInsn; 24import com.android.dx.rop.code.Insn; 25import com.android.dx.rop.code.InvokePolymorphicInsn; 26import com.android.dx.rop.code.LocalVariableInfo; 27import com.android.dx.rop.code.PlainCstInsn; 28import com.android.dx.rop.code.PlainInsn; 29import com.android.dx.rop.code.RegOps; 30import com.android.dx.rop.code.RegisterSpec; 31import com.android.dx.rop.code.RegisterSpecList; 32import com.android.dx.rop.code.RegisterSpecSet; 33import com.android.dx.rop.code.Rop; 34import com.android.dx.rop.code.RopMethod; 35import com.android.dx.rop.code.SourcePosition; 36import com.android.dx.rop.code.SwitchInsn; 37import com.android.dx.rop.code.ThrowingCstInsn; 38import com.android.dx.rop.code.ThrowingInsn; 39import com.android.dx.rop.cst.Constant; 40import com.android.dx.rop.cst.CstInteger; 41import com.android.dx.util.Bits; 42import com.android.dx.util.IntList; 43import java.util.ArrayList; 44 45/** 46 * Translator from {@link RopMethod} to {@link DalvCode}. The {@link 47 * #translate} method is the thing to call on this class. 48 */ 49public final class RopTranslator { 50 /** {@code non-null;} options for dex output */ 51 private final DexOptions dexOptions; 52 53 /** {@code non-null;} method to translate */ 54 private final RopMethod method; 55 56 /** 57 * how much position info to preserve; one of the static 58 * constants in {@link PositionList} 59 */ 60 private final int positionInfo; 61 62 /** {@code null-ok;} local variable info to use */ 63 private final LocalVariableInfo locals; 64 65 /** {@code non-null;} container for all the address objects for the method */ 66 private final BlockAddresses addresses; 67 68 /** {@code non-null;} list of output instructions in-progress */ 69 private final OutputCollector output; 70 71 /** {@code non-null;} visitor to use during translation */ 72 private final TranslationVisitor translationVisitor; 73 74 /** {@code >= 0;} register count for the method */ 75 private final int regCount; 76 77 /** {@code null-ok;} block output order; becomes non-null in {@link #pickOrder} */ 78 private int[] order; 79 80 /** size, in register units, of all the parameters to this method */ 81 private final int paramSize; 82 83 /** 84 * true if the parameters to this method happen to be in proper order 85 * at the end of the frame (as the optimizer emits them) 86 */ 87 private final boolean paramsAreInOrder; 88 89 /** 90 * Translates a {@link RopMethod}. This may modify the given 91 * input. 92 * 93 * @param method {@code non-null;} the original method 94 * @param positionInfo how much position info to preserve; one of the 95 * static constants in {@link PositionList} 96 * @param locals {@code null-ok;} local variable information to use 97 * @param paramSize size, in register units, of all the parameters to 98 * this method 99 * @param dexOptions {@code non-null;} options for dex output 100 * @return {@code non-null;} the translated version 101 */ 102 public static DalvCode translate(RopMethod method, int positionInfo, 103 LocalVariableInfo locals, int paramSize, DexOptions dexOptions) { 104 RopTranslator translator = 105 new RopTranslator(method, positionInfo, locals, paramSize, dexOptions); 106 return translator.translateAndGetResult(); 107 } 108 109 /** 110 * Constructs an instance. This method is private. Use {@link #translate}. 111 * 112 * @param method {@code non-null;} the original method 113 * @param positionInfo how much position info to preserve; one of the 114 * static constants in {@link PositionList} 115 * @param locals {@code null-ok;} local variable information to use 116 * @param paramSize size, in register units, of all the parameters to 117 * this method 118 * @param dexOptions {@code non-null;} options for dex output 119 */ 120 private RopTranslator(RopMethod method, int positionInfo, LocalVariableInfo locals, 121 int paramSize, DexOptions dexOptions) { 122 this.dexOptions = dexOptions; 123 this.method = method; 124 this.positionInfo = positionInfo; 125 this.locals = locals; 126 this.addresses = new BlockAddresses(method); 127 this.paramSize = paramSize; 128 this.order = null; 129 this.paramsAreInOrder = calculateParamsAreInOrder(method, paramSize); 130 131 BasicBlockList blocks = method.getBlocks(); 132 int bsz = blocks.size(); 133 134 /* 135 * Max possible instructions includes three code address 136 * objects per basic block (to the first and last instruction, 137 * and just past the end of the block), and the possibility of 138 * an extra goto at the end of each basic block. 139 */ 140 int maxInsns = (bsz * 3) + blocks.getInstructionCount(); 141 142 if (locals != null) { 143 /* 144 * If we're tracking locals, then there's could be another 145 * extra instruction per block (for the locals state at the 146 * start of the block) as well as one for each interblock 147 * local introduction. 148 */ 149 maxInsns += bsz + locals.getAssignmentCount(); 150 } 151 152 /* 153 * If params are not in order, we will need register space 154 * for them before this is all over... 155 */ 156 this.regCount = blocks.getRegCount() 157 + (paramsAreInOrder ? 0 : this.paramSize); 158 159 this.output = new OutputCollector(dexOptions, maxInsns, bsz * 3, regCount, paramSize); 160 161 if (locals != null) { 162 this.translationVisitor = 163 new LocalVariableAwareTranslationVisitor(output, locals); 164 } else { 165 this.translationVisitor = new TranslationVisitor(output); 166 } 167 } 168 169 /** 170 * Checks to see if the move-param instructions that occur in this 171 * method happen to slot the params in an order at the top of the 172 * stack frame that matches dalvik's calling conventions. This will 173 * alway result in "true" for methods that have run through the 174 * SSA optimizer. 175 * 176 * @param paramSize size, in register units, of all the parameters 177 * to this method 178 */ 179 private static boolean calculateParamsAreInOrder(RopMethod method, 180 final int paramSize) { 181 final boolean[] paramsAreInOrder = { true }; 182 final int initialRegCount = method.getBlocks().getRegCount(); 183 184 /* 185 * We almost could just check the first block here, but the 186 * {@code cf} layer will put in a second move-param in a 187 * subsequent block in the case of synchronized methods. 188 */ 189 method.getBlocks().forEachInsn(new Insn.BaseVisitor() { 190 @Override 191 public void visitPlainCstInsn(PlainCstInsn insn) { 192 if (insn.getOpcode().getOpcode()== RegOps.MOVE_PARAM) { 193 int param = 194 ((CstInteger) insn.getConstant()).getValue(); 195 196 paramsAreInOrder[0] = paramsAreInOrder[0] 197 && ((initialRegCount - paramSize + param) 198 == insn.getResult().getReg()); 199 } 200 } 201 }); 202 203 return paramsAreInOrder[0]; 204 } 205 206 /** 207 * Does the translation and returns the result. 208 * 209 * @return {@code non-null;} the result 210 */ 211 private DalvCode translateAndGetResult() { 212 pickOrder(); 213 outputInstructions(); 214 215 StdCatchBuilder catches = 216 new StdCatchBuilder(method, order, addresses); 217 218 return new DalvCode(positionInfo, output.getFinisher(), catches); 219 } 220 221 /** 222 * Performs initial creation of output instructions based on the 223 * original blocks. 224 */ 225 private void outputInstructions() { 226 BasicBlockList blocks = method.getBlocks(); 227 int[] order = this.order; 228 int len = order.length; 229 230 // Process the blocks in output order. 231 for (int i = 0; i < len; i++) { 232 int nextI = i + 1; 233 int nextLabel = (nextI == order.length) ? -1 : order[nextI]; 234 outputBlock(blocks.labelToBlock(order[i]), nextLabel); 235 } 236 } 237 238 /** 239 * Helper for {@link #outputInstructions}, which does the processing 240 * and output of one block. 241 * 242 * @param block {@code non-null;} the block to process and output 243 * @param nextLabel {@code >= -1;} the next block that will be processed, or 244 * {@code -1} if there is no next block 245 */ 246 private void outputBlock(BasicBlock block, int nextLabel) { 247 // Append the code address for this block. 248 CodeAddress startAddress = addresses.getStart(block); 249 output.add(startAddress); 250 251 // Append the local variable state for the block. 252 if (locals != null) { 253 RegisterSpecSet starts = locals.getStarts(block); 254 output.add(new LocalSnapshot(startAddress.getPosition(), 255 starts)); 256 } 257 258 /* 259 * Choose and append an output instruction for each original 260 * instruction. 261 */ 262 translationVisitor.setBlock(block, addresses.getLast(block)); 263 block.getInsns().forEach(translationVisitor); 264 265 // Insert the block end code address. 266 output.add(addresses.getEnd(block)); 267 268 // Set up for end-of-block activities. 269 270 int succ = block.getPrimarySuccessor(); 271 Insn lastInsn = block.getLastInsn(); 272 273 /* 274 * Check for (and possibly correct for) a non-optimal choice of 275 * which block will get output next. 276 */ 277 278 if ((succ >= 0) && (succ != nextLabel)) { 279 /* 280 * The block has a "primary successor" and that primary 281 * successor isn't the next block to be output. 282 */ 283 Rop lastRop = lastInsn.getOpcode(); 284 if ((lastRop.getBranchingness() == Rop.BRANCH_IF) && 285 (block.getSecondarySuccessor() == nextLabel)) { 286 /* 287 * The block ends with an "if" of some sort, and its 288 * secondary successor (the "then") is in fact the 289 * next block to output. So, reverse the sense of 290 * the test, so that we can just emit the next block 291 * without an interstitial goto. 292 */ 293 output.reverseBranch(1, addresses.getStart(succ)); 294 } else { 295 /* 296 * Our only recourse is to add a goto here to get the 297 * flow to be correct. 298 */ 299 TargetInsn insn = 300 new TargetInsn(Dops.GOTO, lastInsn.getPosition(), 301 RegisterSpecList.EMPTY, 302 addresses.getStart(succ)); 303 output.add(insn); 304 } 305 } 306 } 307 308 /** 309 * Picks an order for the blocks by doing "trace" analysis. 310 */ 311 private void pickOrder() { 312 BasicBlockList blocks = method.getBlocks(); 313 int sz = blocks.size(); 314 int maxLabel = blocks.getMaxLabel(); 315 int[] workSet = Bits.makeBitSet(maxLabel); 316 int[] tracebackSet = Bits.makeBitSet(maxLabel); 317 318 for (int i = 0; i < sz; i++) { 319 BasicBlock one = blocks.get(i); 320 Bits.set(workSet, one.getLabel()); 321 } 322 323 int[] order = new int[sz]; 324 int at = 0; 325 326 /* 327 * Starting with the designated "first label" (that is, the 328 * first block of the method), add that label to the order, 329 * and then pick its first as-yet unordered successor to 330 * immediately follow it, giving top priority to the primary 331 * (aka default) successor (if any). Keep following successors 332 * until the trace runs out of possibilities. Then, continue 333 * by finding an unordered chain containing the first as-yet 334 * unordered block, and adding it to the order, and so on. 335 */ 336 for (int label = method.getFirstLabel(); 337 label != -1; 338 label = Bits.findFirst(workSet, 0)) { 339 340 /* 341 * Attempt to trace backward from the chosen block to an 342 * as-yet unordered predecessor which lists the chosen 343 * block as its primary successor, and so on, until we 344 * fail to find such an unordered predecessor. Start the 345 * trace with that block. Note that the first block in the 346 * method has no predecessors, so in that case this loop 347 * will simply terminate with zero iterations and without 348 * picking a new starter block. 349 */ 350 traceBack: 351 for (;;) { 352 IntList preds = method.labelToPredecessors(label); 353 int psz = preds.size(); 354 355 for (int i = 0; i < psz; i++) { 356 int predLabel = preds.get(i); 357 358 if (Bits.get(tracebackSet, predLabel)) { 359 /* 360 * We found a predecessor loop; stop tracing back 361 * from here. 362 */ 363 break; 364 } 365 366 if (!Bits.get(workSet, predLabel)) { 367 // This one's already ordered. 368 continue; 369 } 370 371 BasicBlock pred = blocks.labelToBlock(predLabel); 372 if (pred.getPrimarySuccessor() == label) { 373 // Found one! 374 label = predLabel; 375 Bits.set(tracebackSet, label); 376 continue traceBack; 377 } 378 } 379 380 // Failed to find a better block to start the trace. 381 break; 382 } 383 384 /* 385 * Trace a path from the chosen block to one of its 386 * unordered successors (hopefully the primary), and so 387 * on, until we run out of unordered successors. 388 */ 389 while (label != -1) { 390 Bits.clear(workSet, label); 391 Bits.clear(tracebackSet, label); 392 order[at] = label; 393 at++; 394 395 BasicBlock one = blocks.labelToBlock(label); 396 BasicBlock preferredBlock = blocks.preferredSuccessorOf(one); 397 398 if (preferredBlock == null) { 399 break; 400 } 401 402 int preferred = preferredBlock.getLabel(); 403 int primary = one.getPrimarySuccessor(); 404 405 if (Bits.get(workSet, preferred)) { 406 /* 407 * Order the current block's preferred successor 408 * next, as it has yet to be scheduled. 409 */ 410 label = preferred; 411 } else if ((primary != preferred) && (primary >= 0) 412 && Bits.get(workSet, primary)) { 413 /* 414 * The primary is available, so use that. 415 */ 416 label = primary; 417 } else { 418 /* 419 * There's no obvious candidate, so pick the first 420 * one that's available, if any. 421 */ 422 IntList successors = one.getSuccessors(); 423 int ssz = successors.size(); 424 label = -1; 425 for (int i = 0; i < ssz; i++) { 426 int candidate = successors.get(i); 427 if (Bits.get(workSet, candidate)) { 428 label = candidate; 429 break; 430 } 431 } 432 } 433 } 434 } 435 436 if (at != sz) { 437 // There was a duplicate block label. 438 throw new RuntimeException("shouldn't happen"); 439 } 440 441 this.order = order; 442 } 443 444 /** 445 * Gets the complete register list (result and sources) out of a 446 * given rop instruction. For insns that are commutative, have 447 * two register sources, and have a source equal to the result, 448 * place that source first. 449 * 450 * @param insn {@code non-null;} instruction in question 451 * @return {@code non-null;} the instruction's complete register list 452 */ 453 private static RegisterSpecList getRegs(Insn insn) { 454 return getRegs(insn, insn.getResult()); 455 } 456 457 /** 458 * Gets the complete register list (result and sources) out of a 459 * given rop instruction. For insns that are commutative, have 460 * two register sources, and have a source equal to the result, 461 * place that source first. 462 * 463 * @param insn {@code non-null;} instruction in question 464 * @param resultReg {@code null-ok;} the real result to use (ignore the insn's) 465 * @return {@code non-null;} the instruction's complete register list 466 */ 467 private static RegisterSpecList getRegs(Insn insn, 468 RegisterSpec resultReg) { 469 RegisterSpecList regs = insn.getSources(); 470 471 if (insn.getOpcode().isCommutative() 472 && (regs.size() == 2) 473 && (resultReg.getReg() == regs.get(1).getReg())) { 474 475 /* 476 * For commutative ops which have two register sources, 477 * if the second source is the same register as the result, 478 * swap the sources so that an opcode of form 12x can be selected 479 * instead of one of form 23x 480 */ 481 482 regs = RegisterSpecList.make(regs.get(1), regs.get(0)); 483 } 484 485 if (resultReg == null) { 486 return regs; 487 } 488 489 return regs.withFirst(resultReg); 490 } 491 492 /** 493 * Instruction visitor class for doing the instruction translation per se. 494 */ 495 private class TranslationVisitor implements Insn.Visitor { 496 /** {@code non-null;} list of output instructions in-progress */ 497 private final OutputCollector output; 498 499 /** {@code non-null;} basic block being worked on */ 500 private BasicBlock block; 501 502 /** 503 * {@code null-ok;} code address for the salient last instruction of the 504 * block (used before switches and throwing instructions) 505 */ 506 private CodeAddress lastAddress; 507 508 /** 509 * Constructs an instance. 510 * 511 * @param output {@code non-null;} destination for instruction output 512 */ 513 public TranslationVisitor(OutputCollector output) { 514 this.output = output; 515 } 516 517 /** 518 * Sets the block currently being worked on. 519 * 520 * @param block {@code non-null;} the block 521 * @param lastAddress {@code non-null;} code address for the salient 522 * last instruction of the block 523 */ 524 public void setBlock(BasicBlock block, CodeAddress lastAddress) { 525 this.block = block; 526 this.lastAddress = lastAddress; 527 } 528 529 /** {@inheritDoc} */ 530 @Override 531 public void visitPlainInsn(PlainInsn insn) { 532 Rop rop = insn.getOpcode(); 533 if (rop.getOpcode() == RegOps.MARK_LOCAL) { 534 /* 535 * Ignore these. They're dealt with by 536 * the LocalVariableAwareTranslationVisitor 537 */ 538 return; 539 } 540 if (rop.getOpcode() == RegOps.MOVE_RESULT_PSEUDO) { 541 // These get skipped 542 return; 543 } 544 545 SourcePosition pos = insn.getPosition(); 546 Dop opcode = RopToDop.dopFor(insn); 547 DalvInsn di; 548 549 switch (rop.getBranchingness()) { 550 case Rop.BRANCH_NONE: 551 case Rop.BRANCH_RETURN: 552 case Rop.BRANCH_THROW: { 553 di = new SimpleInsn(opcode, pos, getRegs(insn)); 554 break; 555 } 556 case Rop.BRANCH_GOTO: { 557 /* 558 * Code in the main translation loop will emit a 559 * goto if necessary (if the branch isn't to the 560 * immediately subsequent block). 561 */ 562 return; 563 } 564 case Rop.BRANCH_IF: { 565 int target = block.getSuccessors().get(1); 566 di = new TargetInsn(opcode, pos, getRegs(insn), 567 addresses.getStart(target)); 568 break; 569 } 570 default: { 571 throw new RuntimeException("shouldn't happen"); 572 } 573 } 574 575 addOutput(di); 576 } 577 578 /** {@inheritDoc} */ 579 @Override 580 public void visitPlainCstInsn(PlainCstInsn insn) { 581 SourcePosition pos = insn.getPosition(); 582 Dop opcode = RopToDop.dopFor(insn); 583 Rop rop = insn.getOpcode(); 584 int ropOpcode = rop.getOpcode(); 585 DalvInsn di; 586 587 if (rop.getBranchingness() != Rop.BRANCH_NONE) { 588 throw new RuntimeException("shouldn't happen"); 589 } 590 591 if (ropOpcode == RegOps.MOVE_PARAM) { 592 if (!paramsAreInOrder) { 593 /* 594 * Parameters are not in order at the top of the reg space. 595 * We need to add moves. 596 */ 597 598 RegisterSpec dest = insn.getResult(); 599 int param = 600 ((CstInteger) insn.getConstant()).getValue(); 601 RegisterSpec source = 602 RegisterSpec.make(regCount - paramSize + param, 603 dest.getType()); 604 di = new SimpleInsn(opcode, pos, 605 RegisterSpecList.make(dest, source)); 606 addOutput(di); 607 } 608 } else { 609 // No moves required for the parameters 610 RegisterSpecList regs = getRegs(insn); 611 di = new CstInsn(opcode, pos, regs, insn.getConstant()); 612 addOutput(di); 613 } 614 } 615 616 /** {@inheritDoc} */ 617 @Override 618 public void visitSwitchInsn(SwitchInsn insn) { 619 SourcePosition pos = insn.getPosition(); 620 IntList cases = insn.getCases(); 621 IntList successors = block.getSuccessors(); 622 int casesSz = cases.size(); 623 int succSz = successors.size(); 624 int primarySuccessor = block.getPrimarySuccessor(); 625 626 /* 627 * Check the assumptions that the number of cases is one 628 * less than the number of successors and that the last 629 * successor in the list is the primary (in this case, the 630 * default). This test is here to guard against forgetting 631 * to change this code if the way switch instructions are 632 * constructed also gets changed. 633 */ 634 if ((casesSz != (succSz - 1)) || 635 (primarySuccessor != successors.get(casesSz))) { 636 throw new RuntimeException("shouldn't happen"); 637 } 638 639 CodeAddress[] switchTargets = new CodeAddress[casesSz]; 640 641 for (int i = 0; i < casesSz; i++) { 642 int label = successors.get(i); 643 switchTargets[i] = addresses.getStart(label); 644 } 645 646 CodeAddress dataAddress = new CodeAddress(pos); 647 // make a new address that binds closely to the switch instruction 648 CodeAddress switchAddress = 649 new CodeAddress(lastAddress.getPosition(), true); 650 SwitchData dataInsn = 651 new SwitchData(pos, switchAddress, cases, switchTargets); 652 Dop opcode = dataInsn.isPacked() ? 653 Dops.PACKED_SWITCH : Dops.SPARSE_SWITCH; 654 TargetInsn switchInsn = 655 new TargetInsn(opcode, pos, getRegs(insn), dataAddress); 656 657 addOutput(switchAddress); 658 addOutput(switchInsn); 659 660 addOutputSuffix(new OddSpacer(pos)); 661 addOutputSuffix(dataAddress); 662 addOutputSuffix(dataInsn); 663 } 664 665 /** 666 * Looks forward to the current block's primary successor, returning 667 * the RegisterSpec of the result of the move-result-pseudo at the 668 * top of that block or null if none. 669 * 670 * @return {@code null-ok;} result of move-result-pseudo at the beginning of 671 * primary successor 672 */ 673 private RegisterSpec getNextMoveResultPseudo() 674 { 675 int label = block.getPrimarySuccessor(); 676 677 if (label < 0) { 678 return null; 679 } 680 681 Insn insn 682 = method.getBlocks().labelToBlock(label).getInsns().get(0); 683 684 if (insn.getOpcode().getOpcode() != RegOps.MOVE_RESULT_PSEUDO) { 685 return null; 686 } else { 687 return insn.getResult(); 688 } 689 } 690 691 /** {@inheritDoc} */ 692 @Override 693 public void visitInvokePolymorphicInsn(InvokePolymorphicInsn insn) { 694 SourcePosition pos = insn.getPosition(); 695 Dop opcode = RopToDop.dopFor(insn); 696 Rop rop = insn.getOpcode(); 697 698 if (rop.getBranchingness() != Rop.BRANCH_THROW) { 699 throw new RuntimeException("Expected BRANCH_THROW got " + rop.getBranchingness()); 700 } else if (!rop.isCallLike()) { 701 throw new RuntimeException("Expected call-like operation"); 702 } 703 704 addOutput(lastAddress); 705 706 RegisterSpecList regs = insn.getSources(); 707 Constant[] constants = new Constant[] { 708 insn.getPolymorphicMethod(), 709 insn.getCallSiteProto() 710 }; 711 DalvInsn di = new MultiCstInsn(opcode, pos, regs, constants); 712 713 addOutput(di); 714 } 715 716 /** {@inheritDoc} */ 717 @Override 718 public void visitThrowingCstInsn(ThrowingCstInsn insn) { 719 SourcePosition pos = insn.getPosition(); 720 Dop opcode = RopToDop.dopFor(insn); 721 Rop rop = insn.getOpcode(); 722 Constant cst = insn.getConstant(); 723 724 if (rop.getBranchingness() != Rop.BRANCH_THROW) { 725 throw new RuntimeException("Expected BRANCH_THROW got " + rop.getBranchingness()); 726 } 727 728 addOutput(lastAddress); 729 730 if (rop.isCallLike()) { 731 RegisterSpecList regs = insn.getSources(); 732 DalvInsn di = new CstInsn(opcode, pos, regs, cst); 733 734 addOutput(di); 735 } else { 736 RegisterSpec realResult = getNextMoveResultPseudo(); 737 738 RegisterSpecList regs = getRegs(insn, realResult); 739 DalvInsn di; 740 741 boolean hasResult = opcode.hasResult() 742 || (rop.getOpcode() == RegOps.CHECK_CAST); 743 744 if (hasResult != (realResult != null)) { 745 throw new RuntimeException( 746 "Insn with result/move-result-pseudo mismatch " + 747 insn); 748 } 749 750 if ((rop.getOpcode() == RegOps.NEW_ARRAY) && 751 (opcode.getOpcode() != Opcodes.NEW_ARRAY)) { 752 /* 753 * It's a type-specific new-array-<primitive>, and 754 * so it should be turned into a SimpleInsn (no 755 * constant ref as it's implicit). 756 */ 757 di = new SimpleInsn(opcode, pos, regs); 758 } else { 759 /* 760 * This is the general case for constant-bearing 761 * instructions. 762 */ 763 di = new CstInsn(opcode, pos, regs, cst); 764 } 765 766 addOutput(di); 767 } 768 } 769 770 /** {@inheritDoc} */ 771 @Override 772 public void visitThrowingInsn(ThrowingInsn insn) { 773 SourcePosition pos = insn.getPosition(); 774 Dop opcode = RopToDop.dopFor(insn); 775 Rop rop = insn.getOpcode(); 776 RegisterSpec realResult; 777 778 if (rop.getBranchingness() != Rop.BRANCH_THROW) { 779 throw new RuntimeException("shouldn't happen"); 780 } 781 782 realResult = getNextMoveResultPseudo(); 783 784 if (opcode.hasResult() != (realResult != null)) { 785 throw new RuntimeException( 786 "Insn with result/move-result-pseudo mismatch" + insn); 787 } 788 789 addOutput(lastAddress); 790 791 DalvInsn di = new SimpleInsn(opcode, pos, 792 getRegs(insn, realResult)); 793 794 addOutput(di); 795 } 796 797 /** {@inheritDoc} */ 798 @Override 799 public void visitFillArrayDataInsn(FillArrayDataInsn insn) { 800 SourcePosition pos = insn.getPosition(); 801 Constant cst = insn.getConstant(); 802 ArrayList<Constant> values = insn.getInitValues(); 803 Rop rop = insn.getOpcode(); 804 805 if (rop.getBranchingness() != Rop.BRANCH_NONE) { 806 throw new RuntimeException("shouldn't happen"); 807 } 808 CodeAddress dataAddress = new CodeAddress(pos); 809 ArrayData dataInsn = 810 new ArrayData(pos, lastAddress, values, cst); 811 812 TargetInsn fillArrayDataInsn = 813 new TargetInsn(Dops.FILL_ARRAY_DATA, pos, getRegs(insn), 814 dataAddress); 815 816 addOutput(lastAddress); 817 addOutput(fillArrayDataInsn); 818 819 addOutputSuffix(new OddSpacer(pos)); 820 addOutputSuffix(dataAddress); 821 addOutputSuffix(dataInsn); 822 } 823 824 /** 825 * Adds to the output. 826 * 827 * @param insn {@code non-null;} instruction to add 828 */ 829 protected void addOutput(DalvInsn insn) { 830 output.add(insn); 831 } 832 833 /** 834 * Adds to the output suffix. 835 * 836 * @param insn {@code non-null;} instruction to add 837 */ 838 protected void addOutputSuffix(DalvInsn insn) { 839 output.addSuffix(insn); 840 } 841 } 842 843 /** 844 * Instruction visitor class for doing instruction translation with 845 * local variable tracking 846 */ 847 private class LocalVariableAwareTranslationVisitor 848 extends TranslationVisitor { 849 /** {@code non-null;} local variable info */ 850 private final LocalVariableInfo locals; 851 852 /** 853 * Constructs an instance. 854 * 855 * @param output {@code non-null;} destination for instruction output 856 * @param locals {@code non-null;} the local variable info 857 */ 858 public LocalVariableAwareTranslationVisitor(OutputCollector output, 859 LocalVariableInfo locals) { 860 super(output); 861 this.locals = locals; 862 } 863 864 /** {@inheritDoc} */ 865 @Override 866 public void visitPlainInsn(PlainInsn insn) { 867 super.visitPlainInsn(insn); 868 addIntroductionIfNecessary(insn); 869 } 870 871 /** {@inheritDoc} */ 872 @Override 873 public void visitPlainCstInsn(PlainCstInsn insn) { 874 super.visitPlainCstInsn(insn); 875 addIntroductionIfNecessary(insn); 876 } 877 878 /** {@inheritDoc} */ 879 @Override 880 public void visitSwitchInsn(SwitchInsn insn) { 881 super.visitSwitchInsn(insn); 882 addIntroductionIfNecessary(insn); 883 } 884 885 /** {@inheritDoc} */ 886 @Override 887 public void visitThrowingCstInsn(ThrowingCstInsn insn) { 888 super.visitThrowingCstInsn(insn); 889 addIntroductionIfNecessary(insn); 890 } 891 892 /** {@inheritDoc} */ 893 @Override 894 public void visitThrowingInsn(ThrowingInsn insn) { 895 super.visitThrowingInsn(insn); 896 addIntroductionIfNecessary(insn); 897 } 898 899 /** 900 * Adds a {@link LocalStart} to the output if the given 901 * instruction in fact introduces a local variable. 902 * 903 * @param insn {@code non-null;} instruction in question 904 */ 905 public void addIntroductionIfNecessary(Insn insn) { 906 RegisterSpec spec = locals.getAssignment(insn); 907 908 if (spec != null) { 909 addOutput(new LocalStart(insn.getPosition(), spec)); 910 } 911 } 912 } 913} 914