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