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.LocalVariableInfo; 26import com.android.dx.rop.code.PlainCstInsn; 27import com.android.dx.rop.code.PlainInsn; 28import com.android.dx.rop.code.RegOps; 29import com.android.dx.rop.code.RegisterSpec; 30import com.android.dx.rop.code.RegisterSpecList; 31import com.android.dx.rop.code.RegisterSpecSet; 32import com.android.dx.rop.code.Rop; 33import com.android.dx.rop.code.RopMethod; 34import com.android.dx.rop.code.SourcePosition; 35import com.android.dx.rop.code.SwitchInsn; 36import com.android.dx.rop.code.ThrowingCstInsn; 37import com.android.dx.rop.code.ThrowingInsn; 38import com.android.dx.rop.cst.Constant; 39import com.android.dx.rop.cst.CstInteger; 40import com.android.dx.util.Bits; 41import com.android.dx.util.IntList; 42 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 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); 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 public void visitPlainInsn(PlainInsn insn) { 531 Rop rop = insn.getOpcode(); 532 if (rop.getOpcode() == RegOps.MARK_LOCAL) { 533 /* 534 * Ignore these. They're dealt with by 535 * the LocalVariableAwareTranslationVisitor 536 */ 537 return; 538 } 539 if (rop.getOpcode() == RegOps.MOVE_RESULT_PSEUDO) { 540 // These get skipped 541 return; 542 } 543 544 SourcePosition pos = insn.getPosition(); 545 Dop opcode = RopToDop.dopFor(insn); 546 DalvInsn di; 547 548 switch (rop.getBranchingness()) { 549 case Rop.BRANCH_NONE: 550 case Rop.BRANCH_RETURN: 551 case Rop.BRANCH_THROW: { 552 di = new SimpleInsn(opcode, pos, getRegs(insn)); 553 break; 554 } 555 case Rop.BRANCH_GOTO: { 556 /* 557 * Code in the main translation loop will emit a 558 * goto if necessary (if the branch isn't to the 559 * immediately subsequent block). 560 */ 561 return; 562 } 563 case Rop.BRANCH_IF: { 564 int target = block.getSuccessors().get(1); 565 di = new TargetInsn(opcode, pos, getRegs(insn), 566 addresses.getStart(target)); 567 break; 568 } 569 default: { 570 throw new RuntimeException("shouldn't happen"); 571 } 572 } 573 574 addOutput(di); 575 } 576 577 /** {@inheritDoc} */ 578 public void visitPlainCstInsn(PlainCstInsn insn) { 579 SourcePosition pos = insn.getPosition(); 580 Dop opcode = RopToDop.dopFor(insn); 581 Rop rop = insn.getOpcode(); 582 int ropOpcode = rop.getOpcode(); 583 DalvInsn di; 584 585 if (rop.getBranchingness() != Rop.BRANCH_NONE) { 586 throw new RuntimeException("shouldn't happen"); 587 } 588 589 if (ropOpcode == RegOps.MOVE_PARAM) { 590 if (!paramsAreInOrder) { 591 /* 592 * Parameters are not in order at the top of the reg space. 593 * We need to add moves. 594 */ 595 596 RegisterSpec dest = insn.getResult(); 597 int param = 598 ((CstInteger) insn.getConstant()).getValue(); 599 RegisterSpec source = 600 RegisterSpec.make(regCount - paramSize + param, 601 dest.getType()); 602 di = new SimpleInsn(opcode, pos, 603 RegisterSpecList.make(dest, source)); 604 addOutput(di); 605 } 606 } else { 607 // No moves required for the parameters 608 RegisterSpecList regs = getRegs(insn); 609 di = new CstInsn(opcode, pos, regs, insn.getConstant()); 610 addOutput(di); 611 } 612 } 613 614 /** {@inheritDoc} */ 615 public void visitSwitchInsn(SwitchInsn insn) { 616 SourcePosition pos = insn.getPosition(); 617 IntList cases = insn.getCases(); 618 IntList successors = block.getSuccessors(); 619 int casesSz = cases.size(); 620 int succSz = successors.size(); 621 int primarySuccessor = block.getPrimarySuccessor(); 622 623 /* 624 * Check the assumptions that the number of cases is one 625 * less than the number of successors and that the last 626 * successor in the list is the primary (in this case, the 627 * default). This test is here to guard against forgetting 628 * to change this code if the way switch instructions are 629 * constructed also gets changed. 630 */ 631 if ((casesSz != (succSz - 1)) || 632 (primarySuccessor != successors.get(casesSz))) { 633 throw new RuntimeException("shouldn't happen"); 634 } 635 636 CodeAddress[] switchTargets = new CodeAddress[casesSz]; 637 638 for (int i = 0; i < casesSz; i++) { 639 int label = successors.get(i); 640 switchTargets[i] = addresses.getStart(label); 641 } 642 643 CodeAddress dataAddress = new CodeAddress(pos); 644 SwitchData dataInsn = 645 new SwitchData(pos, lastAddress, cases, switchTargets); 646 Dop opcode = dataInsn.isPacked() ? 647 Dops.PACKED_SWITCH : Dops.SPARSE_SWITCH; 648 TargetInsn switchInsn = 649 new TargetInsn(opcode, pos, getRegs(insn), dataAddress); 650 651 addOutput(lastAddress); 652 addOutput(switchInsn); 653 654 addOutputSuffix(new OddSpacer(pos)); 655 addOutputSuffix(dataAddress); 656 addOutputSuffix(dataInsn); 657 } 658 659 /** 660 * Looks forward to the current block's primary successor, returning 661 * the RegisterSpec of the result of the move-result-pseudo at the 662 * top of that block or null if none. 663 * 664 * @return {@code null-ok;} result of move-result-pseudo at the beginning of 665 * primary successor 666 */ 667 private RegisterSpec getNextMoveResultPseudo() 668 { 669 int label = block.getPrimarySuccessor(); 670 671 if (label < 0) { 672 return null; 673 } 674 675 Insn insn 676 = method.getBlocks().labelToBlock(label).getInsns().get(0); 677 678 if (insn.getOpcode().getOpcode() != RegOps.MOVE_RESULT_PSEUDO) { 679 return null; 680 } else { 681 return insn.getResult(); 682 } 683 } 684 685 /** {@inheritDoc} */ 686 public void visitThrowingCstInsn(ThrowingCstInsn insn) { 687 SourcePosition pos = insn.getPosition(); 688 Dop opcode = RopToDop.dopFor(insn); 689 Rop rop = insn.getOpcode(); 690 Constant cst = insn.getConstant(); 691 692 if (rop.getBranchingness() != Rop.BRANCH_THROW) { 693 throw new RuntimeException("shouldn't happen"); 694 } 695 696 addOutput(lastAddress); 697 698 if (rop.isCallLike()) { 699 RegisterSpecList regs = insn.getSources(); 700 DalvInsn di = new CstInsn(opcode, pos, regs, cst); 701 702 addOutput(di); 703 } else { 704 RegisterSpec realResult = getNextMoveResultPseudo(); 705 706 RegisterSpecList regs = getRegs(insn, realResult); 707 DalvInsn di; 708 709 boolean hasResult = opcode.hasResult() 710 || (rop.getOpcode() == RegOps.CHECK_CAST); 711 712 if (hasResult != (realResult != null)) { 713 throw new RuntimeException( 714 "Insn with result/move-result-pseudo mismatch " + 715 insn); 716 } 717 718 if ((rop.getOpcode() == RegOps.NEW_ARRAY) && 719 (opcode.getOpcode() != Opcodes.NEW_ARRAY)) { 720 /* 721 * It's a type-specific new-array-<primitive>, and 722 * so it should be turned into a SimpleInsn (no 723 * constant ref as it's implicit). 724 */ 725 di = new SimpleInsn(opcode, pos, regs); 726 } else { 727 /* 728 * This is the general case for constant-bearing 729 * instructions. 730 */ 731 di = new CstInsn(opcode, pos, regs, cst); 732 } 733 734 addOutput(di); 735 } 736 } 737 738 /** {@inheritDoc} */ 739 public void visitThrowingInsn(ThrowingInsn insn) { 740 SourcePosition pos = insn.getPosition(); 741 Dop opcode = RopToDop.dopFor(insn); 742 Rop rop = insn.getOpcode(); 743 RegisterSpec realResult; 744 745 if (rop.getBranchingness() != Rop.BRANCH_THROW) { 746 throw new RuntimeException("shouldn't happen"); 747 } 748 749 realResult = getNextMoveResultPseudo(); 750 751 if (opcode.hasResult() != (realResult != null)) { 752 throw new RuntimeException( 753 "Insn with result/move-result-pseudo mismatch" + insn); 754 } 755 756 addOutput(lastAddress); 757 758 DalvInsn di = new SimpleInsn(opcode, pos, 759 getRegs(insn, realResult)); 760 761 addOutput(di); 762 } 763 764 /** {@inheritDoc} */ 765 public void visitFillArrayDataInsn(FillArrayDataInsn insn) { 766 SourcePosition pos = insn.getPosition(); 767 Constant cst = insn.getConstant(); 768 ArrayList<Constant> values = insn.getInitValues(); 769 Rop rop = insn.getOpcode(); 770 771 if (rop.getBranchingness() != Rop.BRANCH_NONE) { 772 throw new RuntimeException("shouldn't happen"); 773 } 774 CodeAddress dataAddress = new CodeAddress(pos); 775 ArrayData dataInsn = 776 new ArrayData(pos, lastAddress, values, cst); 777 778 TargetInsn fillArrayDataInsn = 779 new TargetInsn(Dops.FILL_ARRAY_DATA, pos, getRegs(insn), 780 dataAddress); 781 782 addOutput(lastAddress); 783 addOutput(fillArrayDataInsn); 784 785 addOutputSuffix(new OddSpacer(pos)); 786 addOutputSuffix(dataAddress); 787 addOutputSuffix(dataInsn); 788 } 789 790 /** 791 * Adds to the output. 792 * 793 * @param insn {@code non-null;} instruction to add 794 */ 795 protected void addOutput(DalvInsn insn) { 796 output.add(insn); 797 } 798 799 /** 800 * Adds to the output suffix. 801 * 802 * @param insn {@code non-null;} instruction to add 803 */ 804 protected void addOutputSuffix(DalvInsn insn) { 805 output.addSuffix(insn); 806 } 807 } 808 809 /** 810 * Instruction visitor class for doing instruction translation with 811 * local variable tracking 812 */ 813 private class LocalVariableAwareTranslationVisitor 814 extends TranslationVisitor { 815 /** {@code non-null;} local variable info */ 816 private LocalVariableInfo locals; 817 818 /** 819 * Constructs an instance. 820 * 821 * @param output {@code non-null;} destination for instruction output 822 * @param locals {@code non-null;} the local variable info 823 */ 824 public LocalVariableAwareTranslationVisitor(OutputCollector output, 825 LocalVariableInfo locals) { 826 super(output); 827 this.locals = locals; 828 } 829 830 /** {@inheritDoc} */ 831 @Override 832 public void visitPlainInsn(PlainInsn insn) { 833 super.visitPlainInsn(insn); 834 addIntroductionIfNecessary(insn); 835 } 836 837 /** {@inheritDoc} */ 838 @Override 839 public void visitPlainCstInsn(PlainCstInsn insn) { 840 super.visitPlainCstInsn(insn); 841 addIntroductionIfNecessary(insn); 842 } 843 844 /** {@inheritDoc} */ 845 @Override 846 public void visitSwitchInsn(SwitchInsn insn) { 847 super.visitSwitchInsn(insn); 848 addIntroductionIfNecessary(insn); 849 } 850 851 /** {@inheritDoc} */ 852 @Override 853 public void visitThrowingCstInsn(ThrowingCstInsn insn) { 854 super.visitThrowingCstInsn(insn); 855 addIntroductionIfNecessary(insn); 856 } 857 858 /** {@inheritDoc} */ 859 @Override 860 public void visitThrowingInsn(ThrowingInsn insn) { 861 super.visitThrowingInsn(insn); 862 addIntroductionIfNecessary(insn); 863 } 864 865 /** 866 * Adds a {@link LocalStart} to the output if the given 867 * instruction in fact introduces a local variable. 868 * 869 * @param insn {@code non-null;} instruction in question 870 */ 871 public void addIntroductionIfNecessary(Insn insn) { 872 RegisterSpec spec = locals.getAssignment(insn); 873 874 if (spec != null) { 875 addOutput(new LocalStart(insn.getPosition(), spec)); 876 } 877 } 878 } 879} 880