Home | History | Annotate | Download | only in ssa
      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;
     18 
     19 import com.android.dx.rop.code.RegisterSpec;
     20 import com.android.dx.rop.code.RopMethod;
     21 import com.android.dx.util.IntIterator;
     22 import java.util.ArrayList;
     23 import java.util.BitSet;
     24 
     25 /**
     26  * Converts ROP methods to SSA Methods
     27  */
     28 public class SsaConverter {
     29     public static final boolean DEBUG = false;
     30 
     31     /**
     32      * Returns an SSA representation, edge-split and with phi
     33      * functions placed.
     34      *
     35      * @param rmeth input
     36      * @param paramWidth the total width, in register-units, of the method's
     37      * parameters
     38      * @param isStatic {@code true} if this method has no {@code this}
     39      * pointer argument
     40      * @return output in SSA form
     41      */
     42     public static SsaMethod convertToSsaMethod(RopMethod rmeth,
     43             int paramWidth, boolean isStatic) {
     44         SsaMethod result
     45             = SsaMethod.newFromRopMethod(rmeth, paramWidth, isStatic);
     46 
     47         edgeSplit(result);
     48 
     49         LocalVariableInfo localInfo = LocalVariableExtractor.extract(result);
     50 
     51         placePhiFunctions(result, localInfo, 0);
     52         new SsaRenamer(result).run();
     53 
     54         /*
     55          * The exit block, added here, is not considered for edge splitting
     56          * or phi placement since no actual control flows to it.
     57          */
     58         result.makeExitBlock();
     59 
     60         return result;
     61     }
     62 
     63     /**
     64      * Updates an SSA representation, placing phi functions and renaming all
     65      * registers above a certain threshold number.
     66      *
     67      * @param ssaMeth input
     68      * @param threshold registers below this number are unchanged
     69      */
     70     public static void updateSsaMethod(SsaMethod ssaMeth, int threshold) {
     71         LocalVariableInfo localInfo = LocalVariableExtractor.extract(ssaMeth);
     72         placePhiFunctions(ssaMeth, localInfo, threshold);
     73         new SsaRenamer(ssaMeth, threshold).run();
     74     }
     75 
     76     /**
     77      * Returns an SSA represention with only the edge-splitter run.
     78      *
     79      * @param rmeth method to process
     80      * @param paramWidth width of all arguments in the method
     81      * @param isStatic {@code true} if this method has no {@code this}
     82      * pointer argument
     83      * @return an SSA represention with only the edge-splitter run
     84      */
     85     public static SsaMethod testEdgeSplit (RopMethod rmeth, int paramWidth,
     86             boolean isStatic) {
     87         SsaMethod result;
     88 
     89         result = SsaMethod.newFromRopMethod(rmeth, paramWidth, isStatic);
     90 
     91         edgeSplit(result);
     92         return result;
     93     }
     94 
     95     /**
     96      * Returns an SSA represention with only the steps through the
     97      * phi placement run.
     98      *
     99      * @param rmeth method to process
    100      * @param paramWidth width of all arguments in the method
    101      * @param isStatic {@code true} if this method has no {@code this}
    102      * pointer argument
    103      * @return an SSA represention with only the edge-splitter run
    104      */
    105     public static SsaMethod testPhiPlacement (RopMethod rmeth, int paramWidth,
    106             boolean isStatic) {
    107         SsaMethod result;
    108 
    109         result = SsaMethod.newFromRopMethod(rmeth, paramWidth, isStatic);
    110 
    111         edgeSplit(result);
    112 
    113         LocalVariableInfo localInfo = LocalVariableExtractor.extract(result);
    114 
    115         placePhiFunctions(result, localInfo, 0);
    116         return result;
    117     }
    118 
    119     /**
    120      * See Appel section 19.1:
    121      *
    122      * Converts CFG into "edge-split" form, such that each node either a
    123      * unique successor or unique predecessor.<p>
    124      *
    125      * In addition, the SSA form we use enforces a further constraint,
    126      * requiring each block with a final instruction that returns a
    127      * value to have a primary successor that has no other
    128      * predecessor. This ensures move statements can always be
    129      * inserted correctly when phi statements are removed.
    130      *
    131      * @param result method to process
    132      */
    133     private static void edgeSplit(SsaMethod result) {
    134         edgeSplitPredecessors(result);
    135         edgeSplitMoveExceptionsAndResults(result);
    136         edgeSplitSuccessors(result);
    137     }
    138 
    139     /**
    140      * Inserts Z nodes as new predecessors for every node that has multiple
    141      * successors and multiple predecessors.
    142      *
    143      * @param result {@code non-null;} method to process
    144      */
    145     private static void edgeSplitPredecessors(SsaMethod result) {
    146         ArrayList<SsaBasicBlock> blocks = result.getBlocks();
    147 
    148         /*
    149          * New blocks are added to the end of the block list during
    150          * this iteration.
    151          */
    152         for (int i = blocks.size() - 1; i >= 0; i-- ) {
    153             SsaBasicBlock block = blocks.get(i);
    154             if (nodeNeedsUniquePredecessor(block)) {
    155                 block.insertNewPredecessor();
    156             }
    157         }
    158     }
    159 
    160     /**
    161      * @param block {@code non-null;} block in question
    162      * @return {@code true} if this node needs to have a unique
    163      * predecessor created for it
    164      */
    165     private static boolean nodeNeedsUniquePredecessor(SsaBasicBlock block) {
    166         /*
    167          * Any block with that has both multiple successors and multiple
    168          * predecessors needs a new predecessor node.
    169          */
    170 
    171         int countPredecessors = block.getPredecessors().cardinality();
    172         int countSuccessors = block.getSuccessors().cardinality();
    173 
    174         return  (countPredecessors > 1 && countSuccessors > 1);
    175     }
    176 
    177     /**
    178      * In ROP form, move-exception must occur as the first insn in a block
    179      * immediately succeeding the insn that could thrown an exception.
    180      * We may need room to insert move insns later, so make sure to split
    181      * any block that starts with a move-exception such that there is a
    182      * unique move-exception block for each predecessor.
    183      *
    184      * @param ssaMeth method to process
    185      */
    186     private static void edgeSplitMoveExceptionsAndResults(SsaMethod ssaMeth) {
    187         ArrayList<SsaBasicBlock> blocks = ssaMeth.getBlocks();
    188 
    189         /*
    190          * New blocks are added to the end of the block list during
    191          * this iteration.
    192          */
    193         for (int i = blocks.size() - 1; i >= 0; i-- ) {
    194             SsaBasicBlock block = blocks.get(i);
    195 
    196             /*
    197              * Any block that starts with a move-exception and has more than
    198              * one predecessor...
    199              */
    200             if (!block.isExitBlock()
    201                     && block.getPredecessors().cardinality() > 1
    202                     && block.getInsns().get(0).isMoveException()) {
    203 
    204                 // block.getPredecessors() is changed in the loop below.
    205                 BitSet preds = (BitSet)block.getPredecessors().clone();
    206                 for (int j = preds.nextSetBit(0); j >= 0;
    207                      j = preds.nextSetBit(j + 1)) {
    208                     SsaBasicBlock predecessor = blocks.get(j);
    209                     SsaBasicBlock zNode
    210                         = predecessor.insertNewSuccessor(block);
    211 
    212                     /*
    213                      * Make sure to place the move-exception as the
    214                      * first insn.
    215                      */
    216                     zNode.getInsns().add(0, block.getInsns().get(0).clone());
    217                 }
    218 
    219                 // Remove the move-exception from the original block.
    220                 block.getInsns().remove(0);
    221             }
    222         }
    223     }
    224 
    225     /**
    226      * Inserts Z nodes for every node that needs a new
    227      * successor.
    228      *
    229      * @param result {@code non-null;} method to process
    230      */
    231     private static void edgeSplitSuccessors(SsaMethod result) {
    232         ArrayList<SsaBasicBlock> blocks = result.getBlocks();
    233 
    234         /*
    235          * New blocks are added to the end of the block list during
    236          * this iteration.
    237          */
    238         for (int i = blocks.size() - 1; i >= 0; i-- ) {
    239             SsaBasicBlock block = blocks.get(i);
    240 
    241             // Successors list is modified in loop below.
    242             BitSet successors = (BitSet)block.getSuccessors().clone();
    243             for (int j = successors.nextSetBit(0);
    244                  j >= 0; j = successors.nextSetBit(j+1)) {
    245 
    246                 SsaBasicBlock succ = blocks.get(j);
    247 
    248                 if (needsNewSuccessor(block, succ)) {
    249                     block.insertNewSuccessor(succ);
    250                 }
    251             }
    252         }
    253     }
    254 
    255     /**
    256      * Returns {@code true} if block and successor need a Z-node
    257      * between them. Presently, this is {@code true} if either:
    258      * <p><ul>
    259      * <li> there is a critical edge between block and successor.
    260      * <li> the final instruction has any sources or results and the current
    261      * successor block has more than one predecessor.
    262      * </ul></p>
    263      *
    264      * @param block predecessor node
    265      * @param succ successor node
    266      * @return {@code true} if a Z node is needed
    267      */
    268     private static boolean needsNewSuccessor(SsaBasicBlock block,
    269             SsaBasicBlock succ) {
    270         ArrayList<SsaInsn> insns = block.getInsns();
    271         SsaInsn lastInsn = insns.get(insns.size() - 1);
    272 
    273         // This is to address b/69128828 where the moves associated
    274         // with a phi were being propagated along a critical
    275         // edge. Consequently, the move instruction inserted was
    276         // positioned before the first instruction in the predecessor
    277         // block. The generated bytecode was rejected by the ART
    278         // verifier.
    279         if (block.getSuccessors().cardinality() > 1 && succ.getPredecessors().cardinality() > 1) {
    280             return true;
    281         }
    282 
    283         return ((lastInsn.getResult() != null)
    284                     || (lastInsn.getSources().size() > 0))
    285                 && succ.getPredecessors().cardinality() > 1;
    286     }
    287 
    288     /**
    289      * See Appel algorithm 19.6:
    290      *
    291      * Place Phi functions in appropriate locations.
    292      *
    293      * @param ssaMeth {@code non-null;} method to process.
    294      * Modifications are made in-place.
    295      * @param localInfo {@code non-null;} local variable info, used
    296      * when placing phis
    297      * @param threshold registers below this number are ignored
    298      */
    299     private static void placePhiFunctions (SsaMethod ssaMeth,
    300             LocalVariableInfo localInfo, int threshold) {
    301         ArrayList<SsaBasicBlock> ssaBlocks;
    302         int regCount;
    303         int blockCount;
    304 
    305         ssaBlocks = ssaMeth.getBlocks();
    306         blockCount = ssaBlocks.size();
    307         regCount = ssaMeth.getRegCount() - threshold;
    308 
    309         DomFront df = new DomFront(ssaMeth);
    310         DomFront.DomInfo[] domInfos = df.run();
    311 
    312         // Bit set of registers vs block index "definition sites"
    313         BitSet[] defsites = new BitSet[regCount];
    314 
    315         // Bit set of registers vs block index "phi placement sites"
    316         BitSet[] phisites = new BitSet[regCount];
    317 
    318         for (int i = 0; i < regCount; i++) {
    319             defsites[i] = new BitSet(blockCount);
    320             phisites[i] = new BitSet(blockCount);
    321         }
    322 
    323         /*
    324          * For each register, build a set of all basic blocks where
    325          * containing an assignment to that register.
    326          */
    327         for (int bi = 0, s = ssaBlocks.size(); bi < s; bi++) {
    328             SsaBasicBlock b = ssaBlocks.get(bi);
    329 
    330             for (SsaInsn insn : b.getInsns()) {
    331                 RegisterSpec rs = insn.getResult();
    332 
    333                 if (rs != null && rs.getReg() - threshold >= 0) {
    334                     defsites[rs.getReg() - threshold].set(bi);
    335                 }
    336             }
    337         }
    338 
    339         if (DEBUG) {
    340             System.out.println("defsites");
    341 
    342             for (int i = 0; i < regCount; i++) {
    343                 StringBuilder sb = new StringBuilder();
    344                 sb.append('v').append(i).append(": ");
    345                 sb.append(defsites[i].toString());
    346                 System.out.println(sb);
    347             }
    348         }
    349 
    350         BitSet worklist;
    351 
    352         /*
    353          * For each register, compute all locations for phi placement
    354          * based on dominance-frontier algorithm.
    355          */
    356         for (int reg = 0, s = regCount; reg < s; reg++) {
    357             int workBlockIndex;
    358 
    359             /* Worklist set starts out with each node where reg is assigned. */
    360 
    361             worklist = (BitSet) (defsites[reg].clone());
    362 
    363             while (0 <= (workBlockIndex = worklist.nextSetBit(0))) {
    364                 worklist.clear(workBlockIndex);
    365                 IntIterator dfIterator
    366                     = domInfos[workBlockIndex].dominanceFrontiers.iterator();
    367 
    368                 while (dfIterator.hasNext()) {
    369                     int dfBlockIndex = dfIterator.next();
    370 
    371                     if (!phisites[reg].get(dfBlockIndex)) {
    372                         phisites[reg].set(dfBlockIndex);
    373 
    374                         int tReg = reg + threshold;
    375                         RegisterSpec rs
    376                             = localInfo.getStarts(dfBlockIndex).get(tReg);
    377 
    378                         if (rs == null) {
    379                             ssaBlocks.get(dfBlockIndex).addPhiInsnForReg(tReg);
    380                         } else {
    381                             ssaBlocks.get(dfBlockIndex).addPhiInsnForReg(rs);
    382                         }
    383 
    384                         if (!defsites[reg].get(dfBlockIndex)) {
    385                             worklist.set(dfBlockIndex);
    386                         }
    387                     }
    388                 }
    389             }
    390         }
    391 
    392         if (DEBUG) {
    393             System.out.println("phisites");
    394 
    395             for (int i = 0; i < regCount; i++) {
    396                 StringBuilder sb = new StringBuilder();
    397                 sb.append('v').append(i).append(": ");
    398                 sb.append(phisites[i].toString());
    399                 System.out.println(sb);
    400             }
    401         }
    402     }
    403 }
    404