OSDN Git Service

original
[gb-231r1-is01/Gingerbread_2.3.3_r1_IS01.git] / dalvik / dx / src / com / android / dx / dex / code / OutputFinisher.java
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      */
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 }