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