Home | History | Annotate | Download | only in back
      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
     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
     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
     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        */
     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      */
    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
    163     public boolean wantsParamsMovedHigh() {
    164         return true;
    165     }
    166 
    167     /** {@inheritDoc} */
    168     @Override
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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     */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
   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      */
   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      */
   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      */
   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 
   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          */
   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          */
   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          */
   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          */
   1252         public int getSize() {
   1253             return size;
   1254         }
   1255     }
   1256 }
   1257