2 * Copyright (C) 2007 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.dx.dex.code;
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;
30 import java.util.ArrayList;
31 import java.util.HashSet;
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.
38 public final class OutputFinisher {
40 * {@code >= 0;} register count for the method, not including any extra
41 * "reserved" registers needed to translate "difficult" instructions
43 private final int unreservedRegCount;
45 /** {@code non-null;} the list of instructions, per se */
46 private ArrayList<DalvInsn> insns;
48 /** whether any instruction has position info */
49 private boolean hasAnyPositionInfo;
51 /** whether any instruction has local variable info */
52 private boolean hasAnyLocalInfo;
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}
60 private int reservedCount;
63 * Constructs an instance. It initially contains no instructions.
65 * @param regCount {@code >= 0;} register count for the method
66 * @param initialCapacity {@code >= 0;} initial capacity of the instructions
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;
78 * Returns whether any of the instructions added to this instance
79 * come with position info.
81 * @return whether any of the instructions added to this instance
82 * come with position info
84 public boolean hasAnyPositionInfo() {
85 return hasAnyPositionInfo;
89 * Returns whether this instance has any local variable information.
91 * @return whether this instance has any local variable information
93 public boolean hasAnyLocalInfo() {
94 return hasAnyLocalInfo;
98 * Helper for {@link #add} which scrutinizes a single
99 * instruction for local variable information.
101 * @param insn {@code non-null;} instruction to scrutinize
102 * @return {@code true} iff the instruction refers to any
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))) {
114 } else if (insn instanceof LocalStart) {
115 RegisterSpec spec = ((LocalStart) insn).getLocal();
116 if (hasLocalInfo(spec)) {
125 * Helper for {@link #hasAnyLocalInfo} which scrutinizes a single
128 * @param spec {@code non-null;} spec to scrutinize
129 * @return {@code true} iff the spec refers to any
132 private static boolean hasLocalInfo(RegisterSpec spec) {
133 return (spec != null)
134 && (spec.getLocalItem().getName() != null);
138 * Returns the set of all constants referred to by instructions added
141 * @return {@code non-null;} the set of constants
143 public HashSet<Constant> getAllConstants() {
144 HashSet<Constant> result = new HashSet<Constant>(20);
146 for (DalvInsn insn : insns) {
147 addConstants(result, insn);
154 * Helper for {@link #getAllConstants} which adds all the info for
155 * a single instruction.
157 * @param result {@code non-null;} result set to add to
158 * @param insn {@code non-null;} instruction to scrutinize
160 private static void addConstants(HashSet<Constant> result,
162 if (insn instanceof CstInsn) {
163 Constant cst = ((CstInsn) insn).getConstant();
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));
171 } else if (insn instanceof LocalStart) {
172 RegisterSpec spec = ((LocalStart) insn).getLocal();
173 addConstants(result, spec);
178 * Helper for {@link #getAllConstants} which adds all the info for
179 * a single {@code RegisterSpec}.
181 * @param result {@code non-null;} result set to add to
182 * @param spec {@code null-ok;} register spec to add
184 private static void addConstants(HashSet<Constant> result,
190 LocalItem local = spec.getLocalItem();
191 CstUtf8 name = local.getName();
192 CstUtf8 signature = local.getSignature();
193 Type type = spec.getType();
195 if (type != Type.KNOWN_NULL) {
196 result.add(CstType.intern(type));
203 if (signature != null) {
204 result.add(signature);
209 * Adds an instruction to the output.
211 * @param insn {@code non-null;} the instruction to add
213 public void add(DalvInsn insn) {
219 * Inserts an instruction in the output at the given offset.
221 * @param at {@code >= 0;} what index to insert at
222 * @param insn {@code non-null;} the instruction to insert
224 public void insert(int at, DalvInsn insn) {
230 * Helper for {@link #add} and {@link #insert},
231 * which updates the position and local info flags.
233 * @param insn {@code non-null;} an instruction that was just introduced
235 private void updateInfo(DalvInsn insn) {
236 if (! hasAnyPositionInfo) {
237 SourcePosition pos = insn.getPosition();
238 if (pos.getLine() >= 0) {
239 hasAnyPositionInfo = true;
243 if (! hasAnyLocalInfo) {
244 if (hasLocalInfo(insn)) {
245 hasAnyLocalInfo = true;
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.
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
260 public void reverseBranch(int which, CodeAddress newTarget) {
261 int size = insns.size();
262 int index = size - which - 1;
263 TargetInsn targetInsn;
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");
276 * No need to call this.set(), since the format and other info
279 insns.set(index, targetInsn.withNewTargetAndReversed(newTarget));
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}.
287 * @param callback {@code non-null;} callback object
289 public void assignIndices(DalvCode.AssignIndicesCallback callback) {
290 for (DalvInsn insn : insns) {
291 if (insn instanceof CstInsn) {
292 assignIndices((CstInsn) insn, callback);
298 * Helper for {@link #assignIndices} which does assignment for one
301 * @param insn {@code non-null;} the instruction
302 * @param callback {@code non-null;} the callback
304 private static void assignIndices(CstInsn insn,
305 DalvCode.AssignIndicesCallback callback) {
306 Constant cst = insn.getConstant();
307 int index = callback.getIndex(cst);
310 insn.setIndex(index);
313 if (cst instanceof CstMemberRef) {
314 CstMemberRef member = (CstMemberRef) cst;
315 CstType definer = member.getDefiningClass();
316 index = callback.getIndex(definer);
318 insn.setClassIndex(index);
324 * Does final processing on this instance and gets the output as
325 * a {@link DalvInsnList}. Final processing consists of:
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>
336 * <p><b>Note:</b> This method may only be called once per instance
339 * @return {@code non-null;} the output list
340 * @throws UnsupportedOperationException if this method has
341 * already been called
343 public DalvInsnList finishProcessingAndGetList() {
344 if (reservedCount >= 0) {
345 throw new UnsupportedOperationException("already processed");
348 InsnFormat[] formats = makeFormatsArray();
349 reserveRegisters(formats);
350 massageInstructions(formats);
351 assignAddressesAndFixBranches();
353 return DalvInsnList.makeImmutable(insns,
354 reservedCount + unreservedRegCount);
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.
362 * @return {@code non-null;} the array of formats
364 private InsnFormat[] makeFormatsArray() {
365 int size = insns.size();
366 InsnFormat[] result = new InsnFormat[size];
368 for (int i = 0; i < size; i++) {
369 result[i] = insns.get(i).getOpcode().getFormat();
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
382 * @param formats {@code non-null;} array of per-instruction format selections
384 private void reserveRegisters(InsnFormat[] formats) {
385 int oldReservedCount = (reservedCount < 0) ? 0 : reservedCount;
388 * Call calculateReservedCount() and then perform register
389 * reservation, repeatedly until no new reservations happen.
392 int newReservedCount = calculateReservedCount(formats);
393 if (oldReservedCount >= newReservedCount) {
397 int reservedDifference = newReservedCount - oldReservedCount;
398 int size = insns.size();
400 for (int i = 0; i < size; i++) {
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
408 DalvInsn insn = insns.get(i);
409 if (!(insn instanceof CodeAddress)) {
411 * No need to call this.set() since the format and
412 * other info are the same.
414 insns.set(i, insn.withRegisterOffset(reservedDifference));
418 oldReservedCount = newReservedCount;
421 reservedCount = oldReservedCount;
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.
431 * @param formats {@code non-null;} array of per-instruction format selections
432 * @return {@code >= 0;} the count of reserved registers
434 private int calculateReservedCount(InsnFormat[] formats) {
435 int size = insns.size();
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.
443 int newReservedCount = reservedCount;
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);
450 if (originalFormat == newFormat) {
454 if (newFormat == null) {
456 * The instruction will need to be expanded, so reserve
459 int reserve = insn.getMinimumRegisterRequirement();
460 if (reserve > newReservedCount) {
461 newReservedCount = reserve;
465 formats[i] = newFormat;
468 return newReservedCount;
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.
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
485 private InsnFormat findFormatForInsn(DalvInsn insn, InsnFormat format) {
486 if (format == null) {
487 // The instruction is already known not to fit any simple format.
491 if (format.isCompatible(insn)) {
492 // The instruction already fits in the current best-known format.
496 Dop dop = insn.getOpcode();
497 int family = dop.getFamily();
500 format = format.nextUp();
501 if ((format == null) ||
502 (format.isCompatible(insn) &&
503 (Dops.getOrNull(family, format) != null))) {
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.
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>
526 * <p>This method ignores issues of branch target size, since
527 * final addresses aren't known at the point that this method is
530 * @param formats {@code non-null;} array of per-instruction format selections
532 private void massageInstructions(InsnFormat[] formats) {
533 if (reservedCount == 0) {
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
541 int size = insns.size();
543 for (int i = 0; i < size; i++) {
544 DalvInsn insn = insns.get(i);
545 Dop dop = insn.getOpcode();
546 InsnFormat format = formats[i];
548 if (format != dop.getFormat()) {
549 dop = Dops.getOrNull(dop.getFamily(), format);
550 insns.set(i, insn.withOpcode(dop));
555 * The difficult uncommon case: Some instructions have to be
556 * expanded to deal with high registers.
558 insns = performExpansion(formats);
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.
569 * @param formats {@code non-null;} array of per-instruction format selections
570 * @return {@code non-null;} the replacement list
572 private ArrayList<DalvInsn> performExpansion(InsnFormat[] formats) {
573 int size = insns.size();
574 ArrayList<DalvInsn> result = new ArrayList<DalvInsn>(size * 2);
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];
584 if (currentFormat != null) {
585 // No expansion is necessary.
589 // Expansion is required.
590 prefix = insn.hrPrefix();
591 suffix = insn.hrSuffix();
594 * Get the initial guess as to the hr version, but then
595 * let findFormatForInsn() pick a better format, if any.
597 insn = insn.hrVersion();
598 originalFormat = insn.getOpcode().getFormat();
599 currentFormat = findFormatForInsn(insn, originalFormat);
602 if (prefix != null) {
606 if (currentFormat != originalFormat) {
607 dop = Dops.getOrNull(dop.getFamily(), currentFormat);
608 insn = insn.withOpcode(dop);
612 if (suffix != null) {
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
626 private void assignAddressesAndFixBranches() {
629 if (!fixBranches()) {
636 * Helper for {@link #assignAddressesAndFixBranches}, which
637 * assigns an address to each instruction, in order.
639 private void assignAddresses() {
641 int size = insns.size();
643 for (int i = 0; i < size; i++) {
644 DalvInsn insn = insns.get(i);
645 insn.setAddress(address);
646 address += insn.codeSize();
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.
659 * @return whether any branches had to be fixed
661 private boolean fixBranches() {
662 int size = insns.size();
663 boolean anyFixed = false;
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.
672 Dop dop = insn.getOpcode();
673 InsnFormat format = dop.getFormat();
674 TargetInsn target = (TargetInsn) insn;
676 if (format.branchFits(target)) {
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) {
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.
689 throw new UnsupportedOperationException("method too long");
691 dop = Dops.getOrNull(dop.getFamily(), newFormat);
692 insn = insn.withOpcode(dop);
696 * It is a conditional: Reverse its sense, and arrange for
697 * it to branch around an absolute goto to the original
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.
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.
712 CodeAddress newTarget;
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");
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));