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