1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.dexgen.dex.code;
18
19import com.android.dexgen.rop.code.LocalItem;
20import com.android.dexgen.rop.code.RegisterSpec;
21import com.android.dexgen.rop.code.RegisterSpecList;
22import com.android.dexgen.rop.code.RegisterSpecSet;
23import com.android.dexgen.rop.code.SourcePosition;
24import com.android.dexgen.rop.cst.Constant;
25import com.android.dexgen.rop.cst.CstMemberRef;
26import com.android.dexgen.rop.cst.CstType;
27import com.android.dexgen.rop.cst.CstUtf8;
28import com.android.dexgen.rop.type.Type;
29
30import java.util.ArrayList;
31import 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 */
38public 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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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