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