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