OutputFinisher.java revision eba7057935e122d8a9234154ea97e3fa32452a4f
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.LocalItem; 22import com.android.dx.rop.code.RegisterSpec; 23import com.android.dx.rop.code.RegisterSpecList; 24import com.android.dx.rop.code.RegisterSpecSet; 25import com.android.dx.rop.code.SourcePosition; 26import com.android.dx.rop.cst.Constant; 27import com.android.dx.rop.cst.CstMemberRef; 28import com.android.dx.rop.cst.CstType; 29import com.android.dx.rop.cst.CstUtf8; 30import com.android.dx.rop.type.Type; 31 32import java.util.ArrayList; 33import java.util.BitSet; 34import java.util.HashSet; 35 36/** 37 * Processor for instruction lists, which takes a "first cut" of 38 * instruction selection as a basis and produces a "final cut" in the 39 * form of a {@link DalvInsnList} instance. 40 */ 41public final class OutputFinisher { 42 /** {@code non-null;} options for dex output */ 43 private final DexOptions dexOptions; 44 45 /** 46 * {@code >= 0;} register count for the method, not including any extra 47 * "reserved" registers needed to translate "difficult" instructions 48 */ 49 private final int unreservedRegCount; 50 51 /** {@code non-null;} the list of instructions, per se */ 52 private ArrayList<DalvInsn> insns; 53 54 /** whether any instruction has position info */ 55 private boolean hasAnyPositionInfo; 56 57 /** whether any instruction has local variable info */ 58 private boolean hasAnyLocalInfo; 59 60 /** 61 * {@code >= 0;} the count of reserved registers (low-numbered 62 * registers used when expanding instructions that can't be 63 * represented simply); becomes valid after a call to {@link 64 * #massageInstructions} 65 */ 66 private int reservedCount; 67 68 /** 69 * Constructs an instance. It initially contains no instructions. 70 * 71 * @param dexOptions {@code non-null;} options for dex output 72 * @param regCount {@code >= 0;} register count for the method 73 * @param initialCapacity {@code >= 0;} initial capacity of the 74 * instructions list 75 */ 76 public OutputFinisher(DexOptions dexOptions, int initialCapacity, int regCount) { 77 this.dexOptions = dexOptions; 78 this.unreservedRegCount = regCount; 79 this.insns = new ArrayList<DalvInsn>(initialCapacity); 80 this.reservedCount = -1; 81 this.hasAnyPositionInfo = false; 82 this.hasAnyLocalInfo = false; 83 } 84 85 /** 86 * Returns whether any of the instructions added to this instance 87 * come with position info. 88 * 89 * @return whether any of the instructions added to this instance 90 * come with position info 91 */ 92 public boolean hasAnyPositionInfo() { 93 return hasAnyPositionInfo; 94 } 95 96 /** 97 * Returns whether this instance has any local variable information. 98 * 99 * @return whether this instance has any local variable information 100 */ 101 public boolean hasAnyLocalInfo() { 102 return hasAnyLocalInfo; 103 } 104 105 /** 106 * Helper for {@link #add} which scrutinizes a single 107 * instruction for local variable information. 108 * 109 * @param insn {@code non-null;} instruction to scrutinize 110 * @return {@code true} iff the instruction refers to any 111 * named locals 112 */ 113 private static boolean hasLocalInfo(DalvInsn insn) { 114 if (insn instanceof LocalSnapshot) { 115 RegisterSpecSet specs = ((LocalSnapshot) insn).getLocals(); 116 int size = specs.size(); 117 for (int i = 0; i < size; i++) { 118 if (hasLocalInfo(specs.get(i))) { 119 return true; 120 } 121 } 122 } else if (insn instanceof LocalStart) { 123 RegisterSpec spec = ((LocalStart) insn).getLocal(); 124 if (hasLocalInfo(spec)) { 125 return true; 126 } 127 } 128 129 return false; 130 } 131 132 /** 133 * Helper for {@link #hasAnyLocalInfo} which scrutinizes a single 134 * register spec. 135 * 136 * @param spec {@code non-null;} spec to scrutinize 137 * @return {@code true} iff the spec refers to any 138 * named locals 139 */ 140 private static boolean hasLocalInfo(RegisterSpec spec) { 141 return (spec != null) 142 && (spec.getLocalItem().getName() != null); 143 } 144 145 /** 146 * Returns the set of all constants referred to by instructions added 147 * to this instance. 148 * 149 * @return {@code non-null;} the set of constants 150 */ 151 public HashSet<Constant> getAllConstants() { 152 HashSet<Constant> result = new HashSet<Constant>(20); 153 154 for (DalvInsn insn : insns) { 155 addConstants(result, insn); 156 } 157 158 return result; 159 } 160 161 /** 162 * Helper for {@link #getAllConstants} which adds all the info for 163 * a single instruction. 164 * 165 * @param result {@code non-null;} result set to add to 166 * @param insn {@code non-null;} instruction to scrutinize 167 */ 168 private static void addConstants(HashSet<Constant> result, 169 DalvInsn insn) { 170 if (insn instanceof CstInsn) { 171 Constant cst = ((CstInsn) insn).getConstant(); 172 result.add(cst); 173 } else if (insn instanceof LocalSnapshot) { 174 RegisterSpecSet specs = ((LocalSnapshot) insn).getLocals(); 175 int size = specs.size(); 176 for (int i = 0; i < size; i++) { 177 addConstants(result, specs.get(i)); 178 } 179 } else if (insn instanceof LocalStart) { 180 RegisterSpec spec = ((LocalStart) insn).getLocal(); 181 addConstants(result, spec); 182 } 183 } 184 185 /** 186 * Helper for {@link #getAllConstants} which adds all the info for 187 * a single {@code RegisterSpec}. 188 * 189 * @param result {@code non-null;} result set to add to 190 * @param spec {@code null-ok;} register spec to add 191 */ 192 private static void addConstants(HashSet<Constant> result, 193 RegisterSpec spec) { 194 if (spec == null) { 195 return; 196 } 197 198 LocalItem local = spec.getLocalItem(); 199 CstUtf8 name = local.getName(); 200 CstUtf8 signature = local.getSignature(); 201 Type type = spec.getType(); 202 203 if (type != Type.KNOWN_NULL) { 204 result.add(CstType.intern(type)); 205 } 206 207 if (name != null) { 208 result.add(name); 209 } 210 211 if (signature != null) { 212 result.add(signature); 213 } 214 } 215 216 /** 217 * Adds an instruction to the output. 218 * 219 * @param insn {@code non-null;} the instruction to add 220 */ 221 public void add(DalvInsn insn) { 222 insns.add(insn); 223 updateInfo(insn); 224 } 225 226 /** 227 * Inserts an instruction in the output at the given offset. 228 * 229 * @param at {@code >= 0;} what index to insert at 230 * @param insn {@code non-null;} the instruction to insert 231 */ 232 public void insert(int at, DalvInsn insn) { 233 insns.add(at, insn); 234 updateInfo(insn); 235 } 236 237 /** 238 * Helper for {@link #add} and {@link #insert}, 239 * which updates the position and local info flags. 240 * 241 * @param insn {@code non-null;} an instruction that was just introduced 242 */ 243 private void updateInfo(DalvInsn insn) { 244 if (! hasAnyPositionInfo) { 245 SourcePosition pos = insn.getPosition(); 246 if (pos.getLine() >= 0) { 247 hasAnyPositionInfo = true; 248 } 249 } 250 251 if (! hasAnyLocalInfo) { 252 if (hasLocalInfo(insn)) { 253 hasAnyLocalInfo = true; 254 } 255 } 256 } 257 258 /** 259 * Reverses a branch which is buried a given number of instructions 260 * backward in the output. It is illegal to call this unless the 261 * indicated instruction really is a reversible branch. 262 * 263 * @param which how many instructions back to find the branch; 264 * {@code 0} is the most recently added instruction, 265 * {@code 1} is the instruction before that, etc. 266 * @param newTarget {@code non-null;} the new target for the 267 * reversed branch 268 */ 269 public void reverseBranch(int which, CodeAddress newTarget) { 270 int size = insns.size(); 271 int index = size - which - 1; 272 TargetInsn targetInsn; 273 274 try { 275 targetInsn = (TargetInsn) insns.get(index); 276 } catch (IndexOutOfBoundsException ex) { 277 // Translate the exception. 278 throw new IllegalArgumentException("too few instructions"); 279 } catch (ClassCastException ex) { 280 // Translate the exception. 281 throw new IllegalArgumentException("non-reversible instruction"); 282 } 283 284 /* 285 * No need to call this.set(), since the format and other info 286 * are the same. 287 */ 288 insns.set(index, targetInsn.withNewTargetAndReversed(newTarget)); 289 } 290 291 /** 292 * Assigns indices in all instructions that need them, using the 293 * given callback to perform lookups. This should be called before 294 * calling {@link #finishProcessingAndGetList}. 295 * 296 * @param callback {@code non-null;} callback object 297 */ 298 public void assignIndices(DalvCode.AssignIndicesCallback callback) { 299 for (DalvInsn insn : insns) { 300 if (insn instanceof CstInsn) { 301 assignIndices((CstInsn) insn, callback); 302 } 303 } 304 } 305 306 /** 307 * Helper for {@link #assignIndices} which does assignment for one 308 * instruction. 309 * 310 * @param insn {@code non-null;} the instruction 311 * @param callback {@code non-null;} the callback 312 */ 313 private static void assignIndices(CstInsn insn, 314 DalvCode.AssignIndicesCallback callback) { 315 Constant cst = insn.getConstant(); 316 int index = callback.getIndex(cst); 317 318 if (index >= 0) { 319 insn.setIndex(index); 320 } 321 322 if (cst instanceof CstMemberRef) { 323 CstMemberRef member = (CstMemberRef) cst; 324 CstType definer = member.getDefiningClass(); 325 index = callback.getIndex(definer); 326 if (index >= 0) { 327 insn.setClassIndex(index); 328 } 329 } 330 } 331 332 /** 333 * Does final processing on this instance and gets the output as 334 * a {@link DalvInsnList}. Final processing consists of: 335 * 336 * <ul> 337 * <li>optionally renumbering registers (to make room as needed for 338 * expanded instructions)</li> 339 * <li>picking a final opcode for each instruction</li> 340 * <li>rewriting instructions, because of register number, 341 * constant pool index, or branch target size issues</li> 342 * <li>assigning final addresses</li> 343 * </ul> 344 * 345 * <p><b>Note:</b> This method may only be called once per instance 346 * of this class.</p> 347 * 348 * @return {@code non-null;} the output list 349 * @throws UnsupportedOperationException if this method has 350 * already been called 351 */ 352 public DalvInsnList finishProcessingAndGetList() { 353 if (reservedCount >= 0) { 354 throw new UnsupportedOperationException("already processed"); 355 } 356 357 Dop[] opcodes = makeOpcodesArray(); 358 reserveRegisters(opcodes); 359 massageInstructions(opcodes); 360 assignAddressesAndFixBranches(); 361 362 return DalvInsnList.makeImmutable(insns, 363 reservedCount + unreservedRegCount); 364 } 365 366 /** 367 * Helper for {@link #finishProcessingAndGetList}, which extracts 368 * the opcode out of each instruction into a separate array, to be 369 * further manipulated as things progress. 370 * 371 * @return {@code non-null;} the array of opcodes 372 */ 373 private Dop[] makeOpcodesArray() { 374 int size = insns.size(); 375 Dop[] result = new Dop[size]; 376 377 for (int i = 0; i < size; i++) { 378 result[i] = insns.get(i).getOpcode(); 379 } 380 381 return result; 382 } 383 384 /** 385 * Helper for {@link #finishProcessingAndGetList}, which figures 386 * out how many reserved registers are required and then reserving 387 * them. It also updates the given {@code opcodes} array so 388 * as to avoid extra work when constructing the massaged 389 * instruction list. 390 * 391 * @param opcodes {@code non-null;} array of per-instruction 392 * opcode selections 393 */ 394 private void reserveRegisters(Dop[] opcodes) { 395 int oldReservedCount = (reservedCount < 0) ? 0 : reservedCount; 396 397 /* 398 * Call calculateReservedCount() and then perform register 399 * reservation, repeatedly until no new reservations happen. 400 */ 401 for (;;) { 402 int newReservedCount = calculateReservedCount(opcodes); 403 if (oldReservedCount >= newReservedCount) { 404 break; 405 } 406 407 int reservedDifference = newReservedCount - oldReservedCount; 408 int size = insns.size(); 409 410 for (int i = 0; i < size; i++) { 411 /* 412 * CodeAddress instance identity is used to link 413 * TargetInsns to their targets, so it is 414 * inappropriate to make replacements, and they don't 415 * have registers in any case. Hence, the instanceof 416 * test below. 417 */ 418 DalvInsn insn = insns.get(i); 419 if (!(insn instanceof CodeAddress)) { 420 /* 421 * No need to call this.set() since the format and 422 * other info are the same. 423 */ 424 insns.set(i, insn.withRegisterOffset(reservedDifference)); 425 } 426 } 427 428 oldReservedCount = newReservedCount; 429 } 430 431 reservedCount = oldReservedCount; 432 } 433 434 /** 435 * Helper for {@link #reserveRegisters}, which does one 436 * pass over the instructions, calculating the number of 437 * registers that need to be reserved. It also updates the 438 * {@code opcodes} list to help avoid extra work in future 439 * register reservation passes. 440 * 441 * @param opcodes {@code non-null;} array of per-instruction 442 * opcode selections 443 * @return {@code >= 0;} the count of reserved registers 444 */ 445 private int calculateReservedCount(Dop[] opcodes) { 446 int size = insns.size(); 447 448 /* 449 * Potential new value of reservedCount, which gets updated in the 450 * following loop. It starts out with the existing reservedCount 451 * and gets increased if it turns out that additional registers 452 * need to be reserved. 453 */ 454 int newReservedCount = reservedCount; 455 456 for (int i = 0; i < size; i++) { 457 DalvInsn insn = insns.get(i); 458 Dop originalOpcode = opcodes[i]; 459 Dop newOpcode = findOpcodeForInsn(insn, originalOpcode); 460 461 if (newOpcode == null) { 462 /* 463 * The instruction will need to be expanded, so find the 464 * expanded opcode and reserve registers for it. 465 */ 466 Dop expandedOp = findExpandedOpcodeForInsn(insn); 467 BitSet compatRegs = expandedOp.getFormat().compatibleRegs(insn); 468 int reserve = insn.getMinimumRegisterRequirement(compatRegs); 469 if (reserve > newReservedCount) { 470 newReservedCount = reserve; 471 } 472 } else if (originalOpcode == newOpcode) { 473 continue; 474 } 475 476 opcodes[i] = newOpcode; 477 } 478 479 return newReservedCount; 480 } 481 482 /** 483 * Attempts to fit the given instruction into a specific opcode, 484 * returning the opcode whose format that the instruction fits 485 * into or {@code null} to indicate that the instruction will need 486 * to be expanded. This fitting process starts with the given 487 * opcode as a first "best guess" and then pessimizes from there 488 * if necessary. 489 * 490 * @param insn {@code non-null;} the instruction in question 491 * @param guess {@code null-ok;} the current guess as to the best 492 * opcode; {@code null} means that no simple opcode fits 493 * @return {@code null-ok;} a possibly-different opcode; either a 494 * {@code non-null} good fit or {@code null} to indicate that no 495 * simple opcode fits 496 */ 497 private Dop findOpcodeForInsn(DalvInsn insn, Dop guess) { 498 /* 499 * Note: The initial guess might be null, meaning that an 500 * earlier call to this method already determined that there 501 * was no possible simple opcode fit. 502 */ 503 504 while (guess != null) { 505 if (guess.getFormat().isCompatible(insn)) { 506 break; 507 } 508 509 guess = Dops.getNextOrNull(guess, dexOptions); 510 } 511 512 return guess; 513 } 514 515 /** 516 * Finds the proper opcode for the given instruction, ignoring 517 * register constraints. 518 * 519 * @param insn {@code non-null;} the instruction in question 520 * @return {@code non-null;} the opcode that fits 521 */ 522 private Dop findExpandedOpcodeForInsn(DalvInsn insn) { 523 Dop result = findOpcodeForInsn(insn.getLowRegVersion(), insn.getOpcode()); 524 if (result == null) { 525 throw new AssertionError(); 526 } 527 return result; 528 } 529 530 /** 531 * Helper for {@link #finishProcessingAndGetList}, which goes 532 * through each instruction in the output, making sure its opcode 533 * can accomodate its arguments. In cases where the opcode is 534 * unable to do so, this replaces the instruction with a larger 535 * instruction with identical semantics that <i>will</i> work. 536 * 537 * <p>This method may also reserve a number of low-numbered 538 * registers, renumbering the instructions' original registers, in 539 * order to have register space available in which to move 540 * very-high registers when expanding instructions into 541 * multi-instruction sequences. This expansion is done when no 542 * simple instruction format can be found for a given instruction that 543 * is able to accomodate that instruction's registers.</p> 544 * 545 * <p>This method ignores issues of branch target size, since 546 * final addresses aren't known at the point that this method is 547 * called.</p> 548 * 549 * @param opcodes {@code non-null;} array of per-instruction 550 * opcode selections 551 */ 552 private void massageInstructions(Dop[] opcodes) { 553 if (reservedCount == 0) { 554 /* 555 * The easy common case: No registers were reserved, so we 556 * merely need to replace any instructions whose format 557 * (and hence whose opcode) changed during the reservation 558 * pass, but all instructions will stay at their original 559 * indices, and the instruction list doesn't grow. 560 */ 561 int size = insns.size(); 562 563 for (int i = 0; i < size; i++) { 564 DalvInsn insn = insns.get(i); 565 Dop originalOpcode = insn.getOpcode(); 566 Dop currentOpcode = opcodes[i]; 567 568 if (originalOpcode != currentOpcode) { 569 insns.set(i, insn.withOpcode(currentOpcode)); 570 } 571 } 572 } else { 573 /* 574 * The difficult uncommon case: Some instructions have to be 575 * expanded to deal with high registers. 576 */ 577 insns = performExpansion(opcodes); 578 } 579 } 580 581 /** 582 * Helper for {@link #massageInstructions}, which constructs a 583 * replacement list, where each {link DalvInsn} instance that 584 * couldn't be represented simply (due to register representation 585 * problems) is expanded into a series of instances that together 586 * perform the proper function. 587 * 588 * @param opcodes {@code non-null;} array of per-instruction 589 * opcode selections 590 * @return {@code non-null;} the replacement list 591 */ 592 private ArrayList<DalvInsn> performExpansion(Dop[] opcodes) { 593 int size = insns.size(); 594 ArrayList<DalvInsn> result = new ArrayList<DalvInsn>(size * 2); 595 596 for (int i = 0; i < size; i++) { 597 DalvInsn insn = insns.get(i); 598 Dop originalOpcode = insn.getOpcode(); 599 Dop currentOpcode = opcodes[i]; 600 DalvInsn prefix; 601 DalvInsn suffix; 602 603 if (currentOpcode != null) { 604 // No expansion is necessary. 605 prefix = null; 606 suffix = null; 607 } else { 608 // Expansion is required. 609 currentOpcode = findExpandedOpcodeForInsn(insn); 610 BitSet compatRegs = 611 currentOpcode.getFormat().compatibleRegs(insn); 612 prefix = insn.expandedPrefix(compatRegs); 613 suffix = insn.expandedSuffix(compatRegs); 614 615 // Expand necessary registers to fit the new format 616 insn = insn.expandedVersion(compatRegs); 617 } 618 619 if (prefix != null) { 620 result.add(prefix); 621 } 622 623 if (currentOpcode != originalOpcode) { 624 insn = insn.withOpcode(currentOpcode); 625 } 626 result.add(insn); 627 628 if (suffix != null) { 629 result.add(suffix); 630 } 631 } 632 633 return result; 634 } 635 636 /** 637 * Helper for {@link #finishProcessingAndGetList}, which assigns 638 * addresses to each instruction, possibly rewriting branches to 639 * fix ones that wouldn't otherwise be able to reach their 640 * targets. 641 */ 642 private void assignAddressesAndFixBranches() { 643 for (;;) { 644 assignAddresses(); 645 if (!fixBranches()) { 646 break; 647 } 648 } 649 } 650 651 /** 652 * Helper for {@link #assignAddressesAndFixBranches}, which 653 * assigns an address to each instruction, in order. 654 */ 655 private void assignAddresses() { 656 int address = 0; 657 int size = insns.size(); 658 659 for (int i = 0; i < size; i++) { 660 DalvInsn insn = insns.get(i); 661 insn.setAddress(address); 662 address += insn.codeSize(); 663 } 664 } 665 666 /** 667 * Helper for {@link #assignAddressesAndFixBranches}, which checks 668 * the branch target size requirement of each branch instruction 669 * to make sure it fits. For instructions that don't fit, this 670 * rewrites them to use a {@code goto} of some sort. In the 671 * case of a conditional branch that doesn't fit, the sense of the 672 * test is reversed in order to branch around a {@code goto} 673 * to the original target. 674 * 675 * @return whether any branches had to be fixed 676 */ 677 private boolean fixBranches() { 678 int size = insns.size(); 679 boolean anyFixed = false; 680 681 for (int i = 0; i < size; i++) { 682 DalvInsn insn = insns.get(i); 683 if (!(insn instanceof TargetInsn)) { 684 // This loop only needs to inspect TargetInsns. 685 continue; 686 } 687 688 Dop opcode = insn.getOpcode(); 689 TargetInsn target = (TargetInsn) insn; 690 691 if (opcode.getFormat().branchFits(target)) { 692 continue; 693 } 694 695 if (opcode.getFamily() == Opcodes.GOTO) { 696 // It is a goto; widen it if possible. 697 opcode = findOpcodeForInsn(insn, opcode); 698 if (opcode == null) { 699 /* 700 * The branch is already maximally large. This should 701 * only be possible if a method somehow manages to have 702 * more than 2^31 code units. 703 */ 704 throw new UnsupportedOperationException("method too long"); 705 } 706 insns.set(i, insn.withOpcode(opcode)); 707 } else { 708 /* 709 * It is a conditional: Reverse its sense, and arrange for 710 * it to branch around an absolute goto to the original 711 * branch target. 712 * 713 * Note: An invariant of the list being processed is 714 * that every TargetInsn is followed by a CodeAddress. 715 * Hence, it is always safe to get the next element 716 * after a TargetInsn and cast it to CodeAddress, as 717 * is happening a few lines down. 718 * 719 * Also note: Size gets incremented by one here, as we 720 * have -- in the net -- added one additional element 721 * to the list, so we increment i to match. The added 722 * and changed elements will be inspected by a repeat 723 * call to this method after this invocation returns. 724 */ 725 CodeAddress newTarget; 726 try { 727 newTarget = (CodeAddress) insns.get(i + 1); 728 } catch (IndexOutOfBoundsException ex) { 729 // The TargetInsn / CodeAddress invariant was violated. 730 throw new IllegalStateException( 731 "unpaired TargetInsn (dangling)"); 732 } catch (ClassCastException ex) { 733 // The TargetInsn / CodeAddress invariant was violated. 734 throw new IllegalStateException("unpaired TargetInsn"); 735 } 736 TargetInsn gotoInsn = 737 new TargetInsn(Dops.GOTO, target.getPosition(), 738 RegisterSpecList.EMPTY, target.getTarget()); 739 insns.set(i, gotoInsn); 740 insns.add(i, target.withNewTargetAndReversed(newTarget)); 741 size++; 742 i++; 743 } 744 745 anyFixed = true; 746 } 747 748 return anyFixed; 749 } 750} 751