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.ssa.back;
18 
19 import com.android.dx.dex.DexOptions;
20 import com.android.dx.rop.code.CstInsn;
21 import com.android.dx.rop.code.LocalItem;
22 import com.android.dx.rop.code.RegOps;
23 import com.android.dx.rop.code.RegisterSpec;
24 import com.android.dx.rop.code.RegisterSpecList;
25 import com.android.dx.rop.code.Rop;
26 import com.android.dx.rop.cst.CstInteger;
27 import com.android.dx.ssa.InterferenceRegisterMapper;
28 import com.android.dx.ssa.NormalSsaInsn;
29 import com.android.dx.ssa.Optimizer;
30 import com.android.dx.ssa.PhiInsn;
31 import com.android.dx.ssa.RegisterMapper;
32 import com.android.dx.ssa.SsaBasicBlock;
33 import com.android.dx.ssa.SsaInsn;
34 import com.android.dx.ssa.SsaMethod;
35 import com.android.dx.util.IntIterator;
36 import com.android.dx.util.IntSet;
37 import java.util.ArrayList;
38 import java.util.BitSet;
39 import java.util.Map;
40 import java.util.TreeMap;
41 
42 /**
43  * Allocates registers in a first-fit fashion, with the bottom reserved for
44  * method parameters and all SSAregisters representing the same local variable
45  * kept together if possible.
46  */
47 public class FirstFitLocalCombiningAllocator extends RegisterAllocator {
48 
49     /**
50      * Alignment constraint that can be used during search of free registers.
51      */
52     private enum Alignment {
53       EVEN {
54         @Override
nextClearBit(BitSet bitSet, int startIdx)55         int nextClearBit(BitSet bitSet, int startIdx) {
56           int bitNumber = bitSet.nextClearBit(startIdx);
57           while (!isEven(bitNumber)) {
58             bitNumber = bitSet.nextClearBit(bitNumber + 1);
59           }
60           return bitNumber;
61         }
62       },
63       ODD {
64         @Override
nextClearBit(BitSet bitSet, int startIdx)65         int nextClearBit(BitSet bitSet, int startIdx) {
66           int bitNumber = bitSet.nextClearBit(startIdx);
67           while (isEven(bitNumber)) {
68             bitNumber = bitSet.nextClearBit(bitNumber + 1);
69           }
70           return bitNumber;
71         }
72       },
73       UNSPECIFIED {
74         @Override
nextClearBit(BitSet bitSet, int startIdx)75         int nextClearBit(BitSet bitSet, int startIdx) {
76           return bitSet.nextClearBit(startIdx);
77         }
78       };
79 
80       /**
81        * Returns the index of the first bit that is set to {@code false} that occurs on or after the
82        * specified starting index and that respect {@link Alignment}.
83        *
84        * @param bitSet bitSet working on.
85        * @param startIdx {@code >= 0;} the index to start checking from (inclusive).
86        * @return the index of the next clear bit respecting alignment.
87        */
nextClearBit(BitSet bitSet, int startIdx)88       abstract int nextClearBit(BitSet bitSet, int startIdx);
89     }
90 
91     /** local debug flag */
92     private static final boolean DEBUG = false;
93 
94     /** maps local variable to a list of associated SSA registers */
95     private final Map<LocalItem, ArrayList<RegisterSpec>> localVariables;
96 
97     /** list of move-result-pesudo instructions seen in this method */
98     private final ArrayList<NormalSsaInsn> moveResultPseudoInsns;
99 
100     /** list of invoke-range instructions seen in this method */
101     private final ArrayList<NormalSsaInsn> invokeRangeInsns;
102 
103     /** list of phi instructions seen in this method */
104     private final ArrayList<PhiInsn> phiInsns;
105 
106     /** indexed by SSA reg; the set of SSA regs we've mapped */
107     private final BitSet ssaRegsMapped;
108 
109     /** Register mapper which will be our result */
110     private final InterferenceRegisterMapper mapper;
111 
112     /** end of rop registers range (starting at 0) reserved for parameters */
113     private final int paramRangeEnd;
114 
115     /** set of rop registers reserved for parameters or local variables */
116     private final BitSet reservedRopRegs;
117 
118     /** set of rop registers that have been used by anything */
119     private final BitSet usedRopRegs;
120 
121     /** true if converter should take steps to minimize rop-form registers */
122     private final boolean minimizeRegisters;
123 
124     /**
125      * Constructs instance.
126      *
127      * @param ssaMeth {@code non-null;} method to process
128      * @param interference non-null interference graph for SSA registers
129      * @param minimizeRegisters true if converter should take steps to
130      * minimize rop-form registers
131      */
FirstFitLocalCombiningAllocator( SsaMethod ssaMeth, InterferenceGraph interference, boolean minimizeRegisters)132     public FirstFitLocalCombiningAllocator(
133             SsaMethod ssaMeth, InterferenceGraph interference,
134             boolean minimizeRegisters) {
135         super(ssaMeth, interference);
136 
137         ssaRegsMapped = new BitSet(ssaMeth.getRegCount());
138 
139         mapper = new InterferenceRegisterMapper(
140                 interference, ssaMeth.getRegCount());
141 
142         this.minimizeRegisters = minimizeRegisters;
143 
144         /*
145          * Reserve space for the params at the bottom of the register
146          * space. Later, we'll flip the params to the end of the register
147          * space.
148          */
149 
150         paramRangeEnd = ssaMeth.getParamWidth();
151 
152         reservedRopRegs = new BitSet(paramRangeEnd * 2);
153         reservedRopRegs.set(0, paramRangeEnd);
154         usedRopRegs = new BitSet(paramRangeEnd * 2);
155         localVariables = new TreeMap<LocalItem, ArrayList<RegisterSpec>>();
156         moveResultPseudoInsns = new ArrayList<NormalSsaInsn>();
157         invokeRangeInsns = new ArrayList<NormalSsaInsn>();
158         phiInsns = new ArrayList<PhiInsn>();
159     }
160 
161     /** {@inheritDoc} */
162     @Override
wantsParamsMovedHigh()163     public boolean wantsParamsMovedHigh() {
164         return true;
165     }
166 
167     /** {@inheritDoc} */
168     @Override
allocateRegisters()169     public RegisterMapper allocateRegisters() {
170 
171         analyzeInstructions();
172 
173         if (DEBUG) {
174             printLocalVars();
175         }
176 
177         if (DEBUG) System.out.println("--->Mapping local-associated params");
178         handleLocalAssociatedParams();
179 
180         if (DEBUG) System.out.println("--->Mapping other params");
181         handleUnassociatedParameters();
182 
183         if (DEBUG) System.out.println("--->Mapping invoke-range");
184         handleInvokeRangeInsns();
185 
186         if (DEBUG) {
187             System.out.println("--->Mapping local-associated non-params");
188         }
189         handleLocalAssociatedOther();
190 
191         if (DEBUG) System.out.println("--->Mapping check-cast results");
192         handleCheckCastResults();
193 
194         if (DEBUG) System.out.println("--->Mapping phis");
195         handlePhiInsns();
196 
197         if (DEBUG) System.out.println("--->Mapping others");
198         handleNormalUnassociated();
199 
200         return mapper;
201     }
202 
203     /**
204      * Dumps local variable table to stdout for debugging.
205      */
printLocalVars()206     private void printLocalVars() {
207         System.out.println("Printing local vars");
208         for (Map.Entry<LocalItem, ArrayList<RegisterSpec>> e :
209                 localVariables.entrySet()) {
210             StringBuilder regs = new StringBuilder();
211 
212             regs.append('{');
213             regs.append(' ');
214             for (RegisterSpec reg : e.getValue()) {
215                 regs.append('v');
216                 regs.append(reg.getReg());
217                 regs.append(' ');
218             }
219             regs.append('}');
220             System.out.printf("Local: %s Registers: %s\n", e.getKey(), regs);
221         }
222     }
223 
224     /**
225      * Maps all local-associated parameters to rop registers.
226      */
handleLocalAssociatedParams()227     private void handleLocalAssociatedParams() {
228         for (ArrayList<RegisterSpec> ssaRegs : localVariables.values()) {
229             int sz = ssaRegs.size();
230             int paramIndex = -1;
231             int paramCategory = 0;
232 
233             // First, find out if this local variable is a parameter.
234             for (int i = 0; i < sz; i++) {
235                 RegisterSpec ssaSpec = ssaRegs.get(i);
236                 int ssaReg = ssaSpec.getReg();
237 
238                 paramIndex = getParameterIndexForReg(ssaReg);
239 
240                 if (paramIndex >= 0) {
241                     paramCategory = ssaSpec.getCategory();
242                     addMapping(ssaSpec, paramIndex);
243                     break;
244                 }
245             }
246 
247             if (paramIndex < 0) {
248                 // This local wasn't a parameter.
249                 continue;
250             }
251 
252             // Any remaining local-associated registers will be mapped later.
253             tryMapRegs(ssaRegs, paramIndex, paramCategory, true);
254         }
255     }
256 
257     /**
258      * Gets the parameter index for SSA registers that are method parameters.
259      * {@code -1} is returned for non-parameter registers.
260      *
261      * @param ssaReg {@code >=0;} SSA register to look up
262      * @return parameter index or {@code -1} if not a parameter
263      */
getParameterIndexForReg(int ssaReg)264     private int getParameterIndexForReg(int ssaReg) {
265         SsaInsn defInsn = ssaMeth.getDefinitionForRegister(ssaReg);
266         if (defInsn == null) {
267             return -1;
268         }
269 
270         Rop opcode = defInsn.getOpcode();
271 
272         // opcode == null for phi insns.
273         if (opcode != null && opcode.getOpcode() == RegOps.MOVE_PARAM) {
274             CstInsn origInsn = (CstInsn) defInsn.getOriginalRopInsn();
275             return  ((CstInteger) origInsn.getConstant()).getValue();
276         }
277 
278         return -1;
279     }
280 
281     /**
282      * Maps all local-associated registers that are not parameters.
283      * Tries to find an unreserved range that's wide enough for all of
284      * the SSA registers, and then tries to map them all to that
285      * range. If not all fit, a new range is tried until all registers
286      * have been fit.
287      */
handleLocalAssociatedOther()288     private void handleLocalAssociatedOther() {
289         for (ArrayList<RegisterSpec> specs : localVariables.values()) {
290             int ropReg = paramRangeEnd;
291 
292             boolean done = false;
293             do {
294                 int maxCategory = 1;
295 
296                 // Compute max category for remaining unmapped registers.
297                 int sz = specs.size();
298                 for (int i = 0; i < sz; i++) {
299                     RegisterSpec ssaSpec = specs.get(i);
300                     int category = ssaSpec.getCategory();
301                     if (!ssaRegsMapped.get(ssaSpec.getReg())
302                             && category > maxCategory) {
303                         maxCategory = category;
304                     }
305                 }
306 
307                 ropReg = findRopRegForLocal(ropReg, maxCategory);
308                 if (canMapRegs(specs, ropReg)) {
309                     done = tryMapRegs(specs, ropReg, maxCategory, true);
310                 }
311 
312                 // Increment for next call to findRopRegForLocal.
313                 ropReg++;
314             } while (!done);
315         }
316     }
317 
318     /**
319      * Tries to map a list of SSA registers into the a rop reg, marking
320      * used rop space as reserved. SSA registers that don't fit are left
321      * unmapped.
322      *
323      * @param specs {@code non-null;} SSA registers to attempt to map
324      * @param ropReg {@code >=0;} rop register to map to
325      * @param maxAllowedCategory {@code 1..2;} maximum category
326      * allowed in mapping.
327      * @param markReserved do so if {@code true}
328      * @return {@code true} if all registers were mapped, {@code false}
329      * if some remain unmapped
330      */
tryMapRegs( ArrayList<RegisterSpec> specs, int ropReg, int maxAllowedCategory, boolean markReserved)331     private boolean tryMapRegs(
332             ArrayList<RegisterSpec> specs, int ropReg,
333             int maxAllowedCategory, boolean markReserved) {
334         boolean remaining = false;
335         for (RegisterSpec spec : specs) {
336             if (ssaRegsMapped.get(spec.getReg())) {
337                 continue;
338             }
339 
340             boolean succeeded;
341             succeeded = tryMapReg(spec, ropReg, maxAllowedCategory);
342             remaining = !succeeded || remaining;
343             if (succeeded && markReserved) {
344                 // This only needs to be called once really with
345                 // the widest category used, but <shrug>
346                 markReserved(ropReg, spec.getCategory());
347             }
348         }
349         return !remaining;
350     }
351 
352     /**
353      * Tries to map an SSA register to a rop register.
354      *
355      * @param ssaSpec {@code non-null;} SSA register
356      * @param ropReg {@code >=0;} rop register
357      * @param maxAllowedCategory {@code 1..2;} the maximum category
358      * that the SSA register is allowed to be
359      * @return {@code true} if map succeeded, {@code false} if not
360      */
tryMapReg(RegisterSpec ssaSpec, int ropReg, int maxAllowedCategory)361     private boolean tryMapReg(RegisterSpec ssaSpec, int ropReg,
362             int maxAllowedCategory) {
363         if (ssaSpec.getCategory() <= maxAllowedCategory
364                 && !ssaRegsMapped.get(ssaSpec.getReg())
365                 && canMapReg(ssaSpec, ropReg)) {
366             addMapping(ssaSpec, ropReg);
367             return true;
368         }
369 
370         return false;
371     }
372 
373     /**
374      * Marks a range of rop registers as "reserved for a local variable."
375      *
376      * @param ropReg {@code >= 0;} rop register to reserve
377      * @param category {@code > 0;} width to reserve
378      */
markReserved(int ropReg, int category)379     private void markReserved(int ropReg, int category) {
380         reservedRopRegs.set(ropReg, ropReg + category, true);
381     }
382 
383     /**
384      * Checks to see if any rop registers in the specified range are reserved
385      * for local variables or parameters.
386      *
387      * @param ropRangeStart {@code >= 0;} lowest rop register
388      * @param width {@code > 0;} number of rop registers in range.
389      * @return {@code true} if any register in range is marked reserved
390      */
rangeContainsReserved(int ropRangeStart, int width)391     private boolean rangeContainsReserved(int ropRangeStart, int width) {
392         for (int i = ropRangeStart; i < (ropRangeStart + width); i++) {
393             if (reservedRopRegs.get(i)) {
394                 return true;
395             }
396         }
397         return false;
398     }
399 
400     /**
401      * Returns true if given rop register represents the {@code this} pointer
402      * for a non-static method.
403      *
404      * @param startReg rop register
405      * @return true if the "this" pointer is located here.
406      */
isThisPointerReg(int startReg)407     private boolean isThisPointerReg(int startReg) {
408         // "this" is always the first parameter.
409         return startReg == 0 && !ssaMeth.isStatic();
410     }
411 
412     /**
413      * Return the register alignment constraint to have 64-bits registers that will be align on even
414      * dalvik registers after that parameter registers are move up to the top of the frame to match
415      * the calling convention.
416      *
417      * @param regCategory category of the register that will be aligned.
418      * @return the register alignment constraint.
419      */
getAlignment(int regCategory)420     private Alignment getAlignment(int regCategory) {
421       Alignment alignment = Alignment.UNSPECIFIED;
422 
423       if (DexOptions.ALIGN_64BIT_REGS_SUPPORT && regCategory == 2) {
424         if (isEven(paramRangeEnd)) {
425           alignment = Alignment.EVEN;
426         } else {
427           alignment = Alignment.ODD;
428         }
429       }
430 
431       return alignment;
432     }
433 
434     /**
435      * Finds unreserved rop registers with a specific category.
436      *
437      * @param startReg {@code >= 0;} a rop register to start the search at
438      * @param regCategory {@code > 0;} category of the searched registers.
439      * @return {@code >= 0;} start of available registers.
440      */
findNextUnreservedRopReg(int startReg, int regCategory)441     private int findNextUnreservedRopReg(int startReg, int regCategory) {
442       return findNextUnreservedRopReg(startReg, regCategory, getAlignment(regCategory));
443     }
444 
445     /**
446      * Finds a range of unreserved rop registers.
447      *
448      * @param startReg {@code >= 0;} a rop register to start the search at
449      * @param width {@code > 0;} the width, in registers, required.
450      * @param alignment the alignment constraint.
451      * @return {@code >= 0;} start of available register range.
452      */
findNextUnreservedRopReg(int startReg, int width, Alignment alignment)453     private int findNextUnreservedRopReg(int startReg, int width, Alignment alignment) {
454       int reg = alignment.nextClearBit(reservedRopRegs, startReg);
455 
456       while (true) {
457         int i = 1;
458 
459         while (i < width && !reservedRopRegs.get(reg + i)) {
460           i++;
461         }
462 
463         if (i == width) {
464           return reg;
465         }
466 
467         reg = alignment.nextClearBit(reservedRopRegs, reg + i);
468       }
469     }
470 
471     /**
472      * Finds rop registers that can be used for local variables.
473      * If {@code MIX_LOCALS_AND_OTHER} is {@code false}, this means any
474      * rop register that has not yet been used.
475      *
476      * @param startReg {@code >= 0;} a rop register to start the search at
477      * @param category {@code > 0;} the register category required.
478      * @return {@code >= 0;} start of available registers.
479      */
findRopRegForLocal(int startReg, int category)480     private int findRopRegForLocal(int startReg, int category) {
481       Alignment alignment = getAlignment(category);
482       int reg = alignment.nextClearBit(usedRopRegs, startReg);
483 
484       while (true) {
485         int i = 1;
486 
487         while (i < category && !usedRopRegs.get(reg + i)) {
488           i++;
489         }
490 
491         if (i == category) {
492           return reg;
493         }
494 
495         reg = alignment.nextClearBit(usedRopRegs, reg + i);
496       }
497     }
498 
499     /**
500      * Maps any parameter that isn't local-associated, which can happen
501      * in the case where there is no java debug info.
502      */
handleUnassociatedParameters()503     private void handleUnassociatedParameters() {
504         int szSsaRegs = ssaMeth.getRegCount();
505 
506         for (int ssaReg = 0; ssaReg < szSsaRegs; ssaReg++) {
507             if (ssaRegsMapped.get(ssaReg)) {
508                 // We already did this one above
509                 continue;
510             }
511 
512             int paramIndex = getParameterIndexForReg(ssaReg);
513 
514             RegisterSpec ssaSpec = getDefinitionSpecForSsaReg(ssaReg);
515             if (paramIndex >= 0) {
516                 addMapping(ssaSpec, paramIndex);
517             }
518         }
519     }
520 
521     /**
522      * Handles all insns that want a register range for their sources.
523      */
handleInvokeRangeInsns()524     private void handleInvokeRangeInsns() {
525         for (NormalSsaInsn insn : invokeRangeInsns) {
526             adjustAndMapSourceRangeRange(insn);
527         }
528     }
529 
530     /**
531      * Handles check cast results to reuse the same source register.
532      * Inserts a move if it can't map the same register to both and the
533      * check cast is not caught.
534      */
handleCheckCastResults()535     private void handleCheckCastResults() {
536         for (NormalSsaInsn insn : moveResultPseudoInsns) {
537             RegisterSpec moveRegSpec = insn.getResult();
538             int moveReg = moveRegSpec.getReg();
539             BitSet predBlocks = insn.getBlock().getPredecessors();
540 
541             // Expect one predecessor block only
542             if (predBlocks.cardinality() != 1) {
543                 continue;
544             }
545 
546             SsaBasicBlock predBlock =
547                     ssaMeth.getBlocks().get(predBlocks.nextSetBit(0));
548             ArrayList<SsaInsn> insnList = predBlock.getInsns();
549 
550             /**
551              * If the predecessor block has a check-cast, it will be the last
552              * instruction
553              */
554             SsaInsn checkCastInsn = insnList.get(insnList.size() - 1);
555             if (checkCastInsn.getOpcode().getOpcode() != RegOps.CHECK_CAST) {
556                 continue;
557             }
558 
559             RegisterSpec checkRegSpec = checkCastInsn.getSources().get(0);
560             int checkReg = checkRegSpec.getReg();
561 
562             /**
563              * See if either register is already mapped. Most likely the move
564              * result will be mapped already since the cast result is stored
565              * in a local variable.
566              */
567             int category = checkRegSpec.getCategory();
568             boolean moveMapped = ssaRegsMapped.get(moveReg);
569             boolean checkMapped = ssaRegsMapped.get(checkReg);
570             if (moveMapped & !checkMapped) {
571                 int moveRopReg = mapper.oldToNew(moveReg);
572                 checkMapped = tryMapReg(checkRegSpec, moveRopReg, category);
573             }
574             if (checkMapped & !moveMapped) {
575                 int checkRopReg = mapper.oldToNew(checkReg);
576                 moveMapped = tryMapReg(moveRegSpec, checkRopReg, category);
577             }
578 
579             // Map any unmapped registers to anything available
580             if (!moveMapped || !checkMapped) {
581                 int ropReg = findNextUnreservedRopReg(paramRangeEnd, category);
582                 ArrayList<RegisterSpec> ssaRegs =
583                     new ArrayList<RegisterSpec>(2);
584                 ssaRegs.add(moveRegSpec);
585                 ssaRegs.add(checkRegSpec);
586 
587                 while (!tryMapRegs(ssaRegs, ropReg, category, false)) {
588                     ropReg = findNextUnreservedRopReg(ropReg + 1, category);
589                 }
590             }
591 
592             /*
593              * If source and result have a different mapping, insert a move so
594              * they can have the same mapping. Don't do this if the check cast
595              * is caught, since it will overwrite a potentially live value.
596              */
597             boolean hasExceptionHandlers =
598                 checkCastInsn.getOriginalRopInsn().getCatches().size() != 0;
599             int moveRopReg = mapper.oldToNew(moveReg);
600             int checkRopReg = mapper.oldToNew(checkReg);
601             if (moveRopReg != checkRopReg && !hasExceptionHandlers) {
602                 ((NormalSsaInsn) checkCastInsn).changeOneSource(0,
603                         insertMoveBefore(checkCastInsn, checkRegSpec));
604                 addMapping(checkCastInsn.getSources().get(0), moveRopReg);
605             }
606         }
607     }
608 
609     /**
610     * Handles all phi instructions, trying to map them to a common register.
611     */
handlePhiInsns()612     private void handlePhiInsns() {
613         for (PhiInsn insn : phiInsns) {
614             processPhiInsn(insn);
615         }
616     }
617 
618     /**
619      * Maps all non-parameter, non-local variable registers.
620      */
handleNormalUnassociated()621     private void handleNormalUnassociated() {
622         int szSsaRegs = ssaMeth.getRegCount();
623 
624         for (int ssaReg = 0; ssaReg < szSsaRegs; ssaReg++) {
625             if (ssaRegsMapped.get(ssaReg)) {
626                 // We already did this one
627                 continue;
628             }
629 
630             RegisterSpec ssaSpec = getDefinitionSpecForSsaReg(ssaReg);
631 
632             if (ssaSpec == null) continue;
633 
634             int category = ssaSpec.getCategory();
635             // Find a rop reg that does not interfere
636             int ropReg = findNextUnreservedRopReg(paramRangeEnd, category);
637             while (!canMapReg(ssaSpec, ropReg)) {
638                 ropReg = findNextUnreservedRopReg(ropReg + 1, category);
639             }
640 
641             addMapping(ssaSpec, ropReg);
642         }
643     }
644 
645     /**
646      * Checks to see if a list of SSA registers can all be mapped into
647      * the same rop reg. Ignores registers that have already been mapped,
648      * and checks the interference graph and ensures the range does not
649      * cross the parameter range.
650      *
651      * @param specs {@code non-null;} SSA registers to check
652      * @param ropReg {@code >=0;} rop register to check mapping to
653      * @return {@code true} if all unmapped registers can be mapped
654      */
canMapRegs(ArrayList<RegisterSpec> specs, int ropReg)655     private boolean canMapRegs(ArrayList<RegisterSpec> specs, int ropReg) {
656         for (RegisterSpec spec : specs) {
657             if (ssaRegsMapped.get(spec.getReg())) continue;
658             if (!canMapReg(spec, ropReg)) return false;
659         }
660         return true;
661     }
662 
663     /**
664      * Checks to see if {@code ssaSpec} can be mapped to
665      * {@code ropReg}. Checks interference graph and ensures
666      * the range does not cross the parameter range.
667      *
668      * @param ssaSpec {@code non-null;} SSA spec
669      * @param ropReg prosepctive new-namespace reg
670      * @return {@code true} if mapping is possible
671      */
canMapReg(RegisterSpec ssaSpec, int ropReg)672     private boolean canMapReg(RegisterSpec ssaSpec, int ropReg) {
673         int category = ssaSpec.getCategory();
674         return !(spansParamRange(ropReg, category)
675                 || mapper.interferes(ssaSpec, ropReg));
676     }
677 
678     /**
679      * Returns true if the specified rop register + category
680      * will cross the boundry between the lower {@code paramWidth}
681      * registers reserved for method params and the upper registers. We cannot
682      * allocate a register that spans the param block and the normal block,
683      * because we will be moving the param block to high registers later.
684      *
685      * @param ssaReg register in new namespace
686      * @param category width that the register will have
687      * @return {@code true} in the case noted above
688      */
spansParamRange(int ssaReg, int category)689     private boolean spansParamRange(int ssaReg, int category) {
690         return ((ssaReg < paramRangeEnd)
691                 && ((ssaReg + category) > paramRangeEnd));
692     }
693 
694     /**
695      * Analyze each instruction and find out all the local variable assignments
696      * and move-result-pseudo/invoke-range instrucitons.
697      */
analyzeInstructions()698     private void analyzeInstructions() {
699         ssaMeth.forEachInsn(new SsaInsn.Visitor() {
700             /** {@inheritDoc} */
701             public void visitMoveInsn(NormalSsaInsn insn) {
702                 processInsn(insn);
703             }
704 
705             /** {@inheritDoc} */
706             public void visitPhiInsn(PhiInsn insn) {
707                 processInsn(insn);
708             }
709 
710             /** {@inheritDoc} */
711             public void visitNonMoveInsn(NormalSsaInsn insn) {
712                 processInsn(insn);
713             }
714 
715             /**
716              * This method collects three types of instructions:
717              *
718              * 1) Adds a local variable assignment to the
719              *    {@code localVariables} map.
720              * 2) Add move-result-pseudo to the
721              *    {@code moveResultPseudoInsns} list.
722              * 3) Add invoke-range to the
723              *    {@code invokeRangeInsns} list.
724              *
725              * @param insn {@code non-null;} insn that may represent a
726              * local variable assignment
727              */
728             private void processInsn(SsaInsn insn) {
729                 RegisterSpec assignment;
730                 assignment = insn.getLocalAssignment();
731 
732                 if (assignment != null) {
733                     LocalItem local = assignment.getLocalItem();
734 
735                     ArrayList<RegisterSpec> regList
736                         = localVariables.get(local);
737 
738                     if (regList == null) {
739                         regList = new ArrayList<RegisterSpec>();
740                         localVariables.put(local, regList);
741                     }
742 
743                     regList.add(assignment);
744                 }
745 
746                 if (insn instanceof NormalSsaInsn) {
747                     if (insn.getOpcode().getOpcode() ==
748                             RegOps.MOVE_RESULT_PSEUDO) {
749                         moveResultPseudoInsns.add((NormalSsaInsn) insn);
750                     } else if (Optimizer.getAdvice().requiresSourcesInOrder(
751                             insn.getOriginalRopInsn().getOpcode(),
752                             insn.getSources())) {
753                         invokeRangeInsns.add((NormalSsaInsn) insn);
754                     }
755                 } else if (insn instanceof PhiInsn) {
756                     phiInsns.add((PhiInsn) insn);
757                 }
758 
759             }
760         });
761     }
762 
763     /**
764      * Adds a mapping from an SSA register to a rop register.
765      * {@link #canMapReg} should have already been called.
766      *
767      * @param ssaSpec {@code non-null;} SSA register to map from
768      * @param ropReg {@code >=0;} rop register to map to
769      */
addMapping(RegisterSpec ssaSpec, int ropReg)770     private void addMapping(RegisterSpec ssaSpec, int ropReg) {
771         int ssaReg = ssaSpec.getReg();
772 
773         // An assertion.
774         if (ssaRegsMapped.get(ssaReg) || !canMapReg(ssaSpec, ropReg)) {
775             throw new RuntimeException(
776                     "attempt to add invalid register mapping");
777         }
778 
779         if (DEBUG) {
780             System.out.printf("Add mapping s%d -> v%d c:%d\n",
781                     ssaSpec.getReg(), ropReg, ssaSpec.getCategory());
782         }
783 
784         int category = ssaSpec.getCategory();
785         mapper.addMapping(ssaSpec.getReg(), ropReg, category);
786         ssaRegsMapped.set(ssaReg);
787         usedRopRegs.set(ropReg, ropReg + category);
788     }
789 
790 
791     /**
792      * Maps the source registers of the specified instruction such that they
793      * will fall in a contiguous range in rop form. Moves are inserted as
794      * necessary to allow the range to be allocated.
795      *
796      * @param insn {@code non-null;} insn whos sources to process
797      */
adjustAndMapSourceRangeRange(NormalSsaInsn insn)798     private void adjustAndMapSourceRangeRange(NormalSsaInsn insn) {
799         int newRegStart = findRangeAndAdjust(insn);
800 
801         RegisterSpecList sources = insn.getSources();
802         int szSources = sources.size();
803         int nextRopReg = newRegStart;
804 
805         for (int i = 0; i < szSources; i++) {
806             RegisterSpec source = sources.get(i);
807             int sourceReg = source.getReg();
808             int category = source.getCategory();
809             int curRopReg = nextRopReg;
810             nextRopReg += category;
811 
812             if (ssaRegsMapped.get(sourceReg)) {
813                 continue;
814             }
815 
816             LocalItem localItem = getLocalItemForReg(sourceReg);
817             addMapping(source, curRopReg);
818 
819             if (localItem != null) {
820                 markReserved(curRopReg, category);
821                 ArrayList<RegisterSpec> similarRegisters
822                         = localVariables.get(localItem);
823 
824                 int szSimilar = similarRegisters.size();
825 
826                 /*
827                  * Try to map all SSA registers also associated with
828                  * this local.
829                  */
830                 for (int j = 0; j < szSimilar; j++) {
831                     RegisterSpec similarSpec = similarRegisters.get(j);
832                     int similarReg = similarSpec.getReg();
833 
834                     // Don't map anything that's also a source.
835                     if (-1 != sources.indexOfRegister(similarReg)) {
836                         continue;
837                     }
838 
839                     // Registers left unmapped will get handled later.
840                     tryMapReg(similarSpec, curRopReg, category);
841                 }
842             }
843         }
844     }
845 
846     /**
847      * Find a contiguous rop register range that fits the specified
848      * instruction's sources. First, try to center the range around
849      * sources that have already been mapped to rop registers. If that fails,
850      * just find a new contiguous range that doesn't interfere.
851      *
852      * @param insn {@code non-null;} the insn whose sources need to
853      * fit. Must be last insn in basic block.
854      * @return {@code >= 0;} rop register of start of range
855      */
findRangeAndAdjust(NormalSsaInsn insn)856     private int findRangeAndAdjust(NormalSsaInsn insn) {
857         RegisterSpecList sources = insn.getSources();
858         int szSources = sources.size();
859         // the category for each source index
860         int categoriesForIndex[] = new int[szSources];
861         int rangeLength = 0;
862 
863         // Compute rangeLength and categoriesForIndex
864         for (int i = 0; i < szSources; i++) {
865             int category = sources.get(i).getCategory();
866             categoriesForIndex[i] = category;
867             rangeLength += categoriesForIndex[i];
868         }
869 
870         // the highest score of fits tried so far
871         int maxScore = Integer.MIN_VALUE;
872         // the high scoring range's start
873         int resultRangeStart = -1;
874         // by source index: set of sources needing moves in high scoring plan
875         BitSet resultMovesRequired = null;
876 
877         /*
878          * First, go through each source that's already been mapped. Try
879          * to center the range around the rop register this source is mapped
880          * to.
881          */
882         int rangeStartOffset = 0;
883         for (int i = 0; i < szSources; i++) {
884             int ssaCenterReg = sources.get(i).getReg();
885 
886             if (i != 0) {
887                 rangeStartOffset -= categoriesForIndex[i - 1];
888             }
889             if (!ssaRegsMapped.get(ssaCenterReg)) {
890                 continue;
891             }
892 
893             int rangeStart = mapper.oldToNew(ssaCenterReg) + rangeStartOffset;
894 
895             if (rangeStart < 0 || spansParamRange(rangeStart, rangeLength)) {
896                 continue;
897             }
898 
899             BitSet curMovesRequired = new BitSet(szSources);
900 
901             int fitWidth
902                     = fitPlanForRange(rangeStart, insn, categoriesForIndex,
903                     curMovesRequired);
904 
905             if (fitWidth < 0) {
906                 continue;
907             }
908 
909             int score = fitWidth - curMovesRequired.cardinality();
910 
911             if (score > maxScore) {
912                 maxScore = score;
913                 resultRangeStart = rangeStart;
914                 resultMovesRequired = curMovesRequired;
915             }
916 
917             if (fitWidth == rangeLength) {
918                 // We can't do any better than this, so stop here
919                 break;
920             }
921         }
922 
923         /*
924          * If we were unable to find a plan for a fit centered around
925          * an already-mapped source, just try to find a range of
926          * registers we can move the range into.
927          */
928 
929         if (resultRangeStart == -1) {
930             resultMovesRequired = new BitSet(szSources);
931 
932             resultRangeStart = findAnyFittingRange(insn, rangeLength,
933                     categoriesForIndex, resultMovesRequired);
934         }
935 
936         /*
937          * Now, insert any moves required.
938          */
939 
940         for (int i = resultMovesRequired.nextSetBit(0); i >= 0;
941              i = resultMovesRequired.nextSetBit(i+1)) {
942             insn.changeOneSource(i, insertMoveBefore(insn, sources.get(i)));
943         }
944 
945         return resultRangeStart;
946     }
947 
948     /**
949      * Finds an unreserved range that will fit the sources of the
950      * specified instruction. Does not bother trying to center the range
951      * around an already-mapped source register;
952      *
953      * @param insn {@code non-null;} insn to build range for
954      * @param rangeLength {@code >=0;} length required in register units
955      * @param categoriesForIndex {@code non-null;} indexed by source index;
956      * the category for each source
957      * @param outMovesRequired {@code non-null;} an output parameter indexed by
958      * source index that will contain the set of sources which need
959      * moves inserted
960      * @return the rop register that starts the fitting range
961      */
findAnyFittingRange(NormalSsaInsn insn, int rangeLength, int[] categoriesForIndex, BitSet outMovesRequired)962     private int findAnyFittingRange(NormalSsaInsn insn, int rangeLength,
963             int[] categoriesForIndex, BitSet outMovesRequired) {
964         Alignment alignment = Alignment.UNSPECIFIED;
965 
966         if (DexOptions.ALIGN_64BIT_REGS_SUPPORT) {
967           int regNumber = 0;
968           int p64bitsAligned = 0;
969           int p64bitsNotAligned = 0;
970           for (int category : categoriesForIndex) {
971             if (category == 2) {
972               if (isEven(regNumber)) {
973                 p64bitsAligned++;
974               } else {
975                 p64bitsNotAligned++;
976               }
977               regNumber += 2;
978             } else {
979               regNumber += 1;
980             }
981           }
982 
983           if (p64bitsNotAligned > p64bitsAligned) {
984             if (isEven(paramRangeEnd)) {
985               alignment = Alignment.ODD;
986             } else {
987               alignment = Alignment.EVEN;
988             }
989           } else if (p64bitsAligned > 0) {
990             if (isEven(paramRangeEnd)) {
991               alignment = Alignment.EVEN;
992             } else {
993               alignment = Alignment.ODD;
994             }
995           }
996         }
997 
998         int rangeStart = paramRangeEnd;
999         while (true) {
1000           rangeStart = findNextUnreservedRopReg(rangeStart, rangeLength, alignment);
1001 
1002           int fitWidth = fitPlanForRange(rangeStart, insn, categoriesForIndex, outMovesRequired);
1003 
1004           if (fitWidth >= 0) {
1005             break;
1006           }
1007           rangeStart++;
1008           outMovesRequired.clear();
1009         }
1010 
1011         return rangeStart;
1012     }
1013 
1014     /**
1015      * Attempts to build a plan for fitting a range of sources into rop
1016      * registers.
1017      *
1018      * @param ropReg {@code >= 0;} rop reg that begins range
1019      * @param insn {@code non-null;} insn to plan range for
1020      * @param categoriesForIndex {@code non-null;} indexed by source index;
1021      * the category for each source
1022      * @param outMovesRequired {@code non-null;} an output parameter indexed by
1023      * source index that will contain the set of sources which need
1024      * moves inserted
1025      * @return the width of the fit that that does not involve added moves or
1026      * {@code -1} if "no fit possible"
1027      */
fitPlanForRange(int ropReg, NormalSsaInsn insn, int[] categoriesForIndex, BitSet outMovesRequired)1028     private int fitPlanForRange(int ropReg, NormalSsaInsn insn,
1029             int[] categoriesForIndex, BitSet outMovesRequired) {
1030         RegisterSpecList sources = insn.getSources();
1031         int szSources = sources.size();
1032         int fitWidth = 0;
1033         IntSet liveOut = insn.getBlock().getLiveOutRegs();
1034         RegisterSpecList liveOutSpecs = ssaSetToSpecs(liveOut);
1035 
1036         // An SSA reg may only be mapped into a range once.
1037         BitSet seen = new BitSet(ssaMeth.getRegCount());
1038 
1039         for (int i = 0; i < szSources ; i++) {
1040             RegisterSpec ssaSpec = sources.get(i);
1041             int ssaReg = ssaSpec.getReg();
1042             int category = categoriesForIndex[i];
1043 
1044             if (i != 0) {
1045                 ropReg += categoriesForIndex[i-1];
1046             }
1047 
1048             if (ssaRegsMapped.get(ssaReg)
1049                     && mapper.oldToNew(ssaReg) == ropReg) {
1050                 // This is a register that is already mapped appropriately.
1051                 fitWidth += category;
1052             } else if (rangeContainsReserved(ropReg, category)) {
1053                 fitWidth = -1;
1054                 break;
1055             } else if (!ssaRegsMapped.get(ssaReg)
1056                     && canMapReg(ssaSpec, ropReg)
1057                     && !seen.get(ssaReg)) {
1058                 // This is a register that can be mapped appropriately.
1059                 fitWidth += category;
1060             } else if (!mapper.areAnyPinned(liveOutSpecs, ropReg, category)
1061                     && !mapper.areAnyPinned(sources, ropReg, category)) {
1062                 /*
1063                  * This is a source that can be moved. We can insert a
1064                  * move as long as:
1065                  *
1066                  *   * no SSA register pinned to the desired rop reg
1067                  *     is live out on the block
1068                  *
1069                  *   * no SSA register pinned to desired rop reg is
1070                  *     a source of this insn (since this may require
1071                  *     overlapping moves, which we can't presently handle)
1072                  */
1073 
1074                 outMovesRequired.set(i);
1075             } else {
1076                 fitWidth = -1;
1077                 break;
1078             }
1079 
1080             seen.set(ssaReg);
1081         }
1082         return fitWidth;
1083     }
1084 
1085     /**
1086      * Converts a bit set of SSA registers into a RegisterSpecList containing
1087      * the definition specs of all the registers.
1088      *
1089      * @param ssaSet {@code non-null;} set of SSA registers
1090      * @return list of RegisterSpecs as noted above
1091      */
ssaSetToSpecs(IntSet ssaSet)1092     RegisterSpecList ssaSetToSpecs(IntSet ssaSet) {
1093         RegisterSpecList result = new RegisterSpecList(ssaSet.elements());
1094 
1095         IntIterator iter = ssaSet.iterator();
1096 
1097         int i = 0;
1098         while (iter.hasNext()) {
1099             result.set(i++, getDefinitionSpecForSsaReg(iter.next()));
1100         }
1101 
1102         return result;
1103     }
1104 
1105     /**
1106      * Gets a local item associated with an ssa register, if one exists.
1107      *
1108      * @param ssaReg {@code >= 0;} SSA register
1109      * @return {@code null-ok;} associated local item or null
1110      */
getLocalItemForReg(int ssaReg)1111     private LocalItem getLocalItemForReg(int ssaReg) {
1112         for (Map.Entry<LocalItem, ArrayList<RegisterSpec>> entry :
1113                  localVariables.entrySet()) {
1114             for (RegisterSpec spec : entry.getValue()) {
1115                 if (spec.getReg() == ssaReg) {
1116                     return entry.getKey();
1117                 }
1118             }
1119         }
1120 
1121         return null;
1122     }
1123 
1124     /**
1125      * Attempts to map the sources and result of a phi to a common register.
1126      * Will try existing mappings first, from most to least common. If none
1127      * of the registers have mappings yet, a new mapping is created.
1128      */
processPhiInsn(PhiInsn insn)1129     private void processPhiInsn(PhiInsn insn) {
1130         RegisterSpec result = insn.getResult();
1131         int resultReg = result.getReg();
1132         int category = result.getCategory();
1133 
1134         RegisterSpecList sources = insn.getSources();
1135         int sourcesSize = sources.size();
1136 
1137         // List of phi sources / result that need mapping
1138         ArrayList<RegisterSpec> ssaRegs = new ArrayList<RegisterSpec>();
1139 
1140         // Track how many times a particular mapping is found
1141         Multiset mapSet = new Multiset(sourcesSize + 1);
1142 
1143         /*
1144          * If the result of the phi has an existing mapping, get it.
1145          * Otherwise, add it to the list of regs that need mapping.
1146          */
1147         if (ssaRegsMapped.get(resultReg)) {
1148             mapSet.add(mapper.oldToNew(resultReg));
1149         } else {
1150             ssaRegs.add(result);
1151         }
1152 
1153         for (int i = 0; i < sourcesSize; i++) {
1154             RegisterSpec source = sources.get(i);
1155             SsaInsn def = ssaMeth.getDefinitionForRegister(source.getReg());
1156             RegisterSpec sourceDef = def.getResult();
1157             int sourceReg = sourceDef.getReg();
1158 
1159             /*
1160              * If a source of the phi has an existing mapping, get it.
1161              * Otherwise, add it to the list of regs that need mapping.
1162              */
1163             if (ssaRegsMapped.get(sourceReg)) {
1164                 mapSet.add(mapper.oldToNew(sourceReg));
1165             } else {
1166                 ssaRegs.add(sourceDef);
1167             }
1168         }
1169 
1170         // Try all existing mappings, with the most common ones first
1171         for (int i = 0; i < mapSet.getSize(); i++) {
1172             int maxReg = mapSet.getAndRemoveHighestCount();
1173             tryMapRegs(ssaRegs, maxReg, category, false);
1174         }
1175 
1176         // Map any remaining unmapped regs with whatever fits
1177         int mapReg = findNextUnreservedRopReg(paramRangeEnd, category);
1178         while (!tryMapRegs(ssaRegs, mapReg, category, false)) {
1179             mapReg = findNextUnreservedRopReg(mapReg + 1, category);
1180         }
1181     }
1182 
isEven(int regNumger)1183     private static boolean isEven(int regNumger) {
1184       return ((regNumger & 1) == 0);
1185     }
1186 
1187     // A set that tracks how often elements are added to it.
1188     private static class Multiset {
1189         private final int[] reg;
1190         private final int[] count;
1191         private int size;
1192 
1193         /**
1194          * Constructs an instance.
1195          *
1196          * @param maxSize the maximum distinct elements the set may have
1197          */
Multiset(int maxSize)1198         public Multiset(int maxSize) {
1199             reg = new int[maxSize];
1200             count = new int[maxSize];
1201             size = 0;
1202         }
1203 
1204         /**
1205          * Adds an element to the set.
1206          *
1207          * @param element element to add
1208          */
add(int element)1209         public void add(int element) {
1210             for (int i = 0; i < size; i++) {
1211                 if (reg[i] == element) {
1212                     count[i]++;
1213                     return;
1214                 }
1215             }
1216 
1217             reg[size] = element;
1218             count[size] = 1;
1219             size++;
1220         }
1221 
1222         /**
1223          * Searches the set for the element that has been added the most.
1224          * In the case of a tie, the element that was added first is returned.
1225          * Then, it clears the count on that element. The size of the set
1226          * remains unchanged.
1227          *
1228          * @return element with the highest count
1229          */
getAndRemoveHighestCount()1230         public int getAndRemoveHighestCount() {
1231             int maxIndex = -1;
1232             int maxReg = -1;
1233             int maxCount = 0;
1234 
1235             for (int i = 0; i < size; i++) {
1236                 if (maxCount < count[i]) {
1237                     maxIndex = i;
1238                     maxReg = reg[i];
1239                     maxCount = count[i];
1240                 }
1241             }
1242 
1243             count[maxIndex] = 0;
1244             return maxReg;
1245         }
1246 
1247         /**
1248          * Gets the number of distinct elements in the set.
1249          *
1250          * @return size of the set
1251          */
getSize()1252         public int getSize() {
1253             return size;
1254         }
1255     }
1256 }
1257