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.rop.code.BasicBlock;
     20 import com.android.dx.rop.code.BasicBlockList;
     21 import com.android.dx.rop.code.CstInsn;
     22 import com.android.dx.rop.code.Insn;
     23 import com.android.dx.rop.code.InsnList;
     24 import com.android.dx.rop.code.RegOps;
     25 import com.android.dx.rop.code.RopMethod;
     26 import com.android.dx.rop.code.SwitchInsn;
     27 import com.android.dx.util.IntList;
     28 
     29 import java.util.BitSet;
     30 
     31 /**
     32  * Searches for basic blocks that all have the same successor and insns
     33  * but different predecessors. These blocks are then combined into a single
     34  * block and the now-unused blocks are deleted. These identical blocks
     35  * frequently are created when catch blocks are edge-split.
     36  */
     37 public class IdenticalBlockCombiner {
     38     private final RopMethod ropMethod;
     39     private final BasicBlockList blocks;
     40     private final BasicBlockList newBlocks;
     41 
     42     /**
     43      * Constructs instance. Call {@code process()} to run.
     44      *
     45      * @param rm {@code non-null;} instance to process
     46      */
     47     public IdenticalBlockCombiner(RopMethod rm) {
     48         ropMethod = rm;
     49         blocks = ropMethod.getBlocks();
     50         newBlocks = blocks.getMutableCopy();
     51     }
     52 
     53     /**
     54      * Runs algorithm. TODO: This is n^2, and could be made linear-ish with
     55      * a hash. In particular, hash the contents of each block and only
     56      * compare blocks with the same hash.
     57      *
     58      * @return {@code non-null;} new method that has been processed
     59      */
     60     public RopMethod process() {
     61         int szBlocks = blocks.size();
     62         // indexed by label
     63         BitSet toDelete = new BitSet(blocks.getMaxLabel());
     64 
     65         // For each non-deleted block...
     66         for (int bindex = 0; bindex < szBlocks; bindex++) {
     67             BasicBlock b = blocks.get(bindex);
     68 
     69             if (toDelete.get(b.getLabel())) {
     70                 // doomed block
     71                 continue;
     72             }
     73 
     74             IntList preds = ropMethod.labelToPredecessors(b.getLabel());
     75 
     76             // ...look at all of it's predecessors that have only one succ...
     77             int szPreds = preds.size();
     78             for (int i = 0; i < szPreds; i++) {
     79                 int iLabel = preds.get(i);
     80 
     81                 BasicBlock iBlock = blocks.labelToBlock(iLabel);
     82 
     83                 if (toDelete.get(iLabel)
     84                         || iBlock.getSuccessors().size() > 1
     85                         || iBlock.getFirstInsn().getOpcode().getOpcode() ==
     86                             RegOps.MOVE_RESULT) {
     87                     continue;
     88                 }
     89 
     90                 IntList toCombine = new IntList();
     91 
     92                 // ...and see if they can be combined with any other preds...
     93                 for (int j = i + 1; j < szPreds; j++) {
     94                     int jLabel = preds.get(j);
     95                     BasicBlock jBlock = blocks.labelToBlock(jLabel);
     96 
     97                     if (jBlock.getSuccessors().size() == 1
     98                             && compareInsns(iBlock, jBlock)) {
     99 
    100                         toCombine.add(jLabel);
    101                         toDelete.set(jLabel);
    102                     }
    103                 }
    104 
    105                 combineBlocks(iLabel, toCombine);
    106             }
    107         }
    108 
    109         for (int i = szBlocks - 1; i >= 0; i--) {
    110             if (toDelete.get(newBlocks.get(i).getLabel())) {
    111                 newBlocks.set(i, null);
    112             }
    113         }
    114 
    115         newBlocks.shrinkToFit();
    116         newBlocks.setImmutable();
    117 
    118         return new RopMethod(newBlocks, ropMethod.getFirstLabel());
    119     }
    120 
    121     /**
    122      * Helper method to compare the contents of two blocks.
    123      *
    124      * @param a {@code non-null;} a block to compare
    125      * @param b {@code non-null;} another block to compare
    126      * @return {@code true} iff the two blocks' instructions are the same
    127      */
    128     private static boolean compareInsns(BasicBlock a, BasicBlock b) {
    129         return a.getInsns().contentEquals(b.getInsns());
    130     }
    131 
    132     /**
    133      * Combines blocks proven identical into one alpha block, re-writing
    134      * all of the successor links that point to the beta blocks to point
    135      * to the alpha block instead.
    136      *
    137      * @param alphaLabel block that will replace all the beta block
    138      * @param betaLabels label list of blocks to combine
    139      */
    140     private void combineBlocks(int alphaLabel, IntList betaLabels) {
    141         int szBetas = betaLabels.size();
    142 
    143         for (int i = 0; i < szBetas; i++) {
    144             int betaLabel = betaLabels.get(i);
    145             BasicBlock bb = blocks.labelToBlock(betaLabel);
    146             IntList preds = ropMethod.labelToPredecessors(bb.getLabel());
    147             int szPreds = preds.size();
    148 
    149             for (int j = 0; j < szPreds; j++) {
    150                 BasicBlock predBlock = newBlocks.labelToBlock(preds.get(j));
    151                 replaceSucc(predBlock, betaLabel, alphaLabel);
    152             }
    153         }
    154     }
    155 
    156     /**
    157      * Replaces one of a block's successors with a different label. Constructs
    158      * an updated BasicBlock instance and places it in {@code newBlocks}.
    159      *
    160      * @param block block to replace
    161      * @param oldLabel label of successor to replace
    162      * @param newLabel label of new successor
    163      */
    164     private void replaceSucc(BasicBlock block, int oldLabel, int newLabel) {
    165         IntList newSuccessors = block.getSuccessors().mutableCopy();
    166         int newPrimarySuccessor;
    167 
    168         newSuccessors.set(newSuccessors.indexOf(oldLabel), newLabel);
    169         newPrimarySuccessor = block.getPrimarySuccessor();
    170 
    171         if (newPrimarySuccessor == oldLabel) {
    172             newPrimarySuccessor = newLabel;
    173         }
    174 
    175         newSuccessors.setImmutable();
    176 
    177         BasicBlock newBB = new BasicBlock(block.getLabel(),
    178                 block.getInsns(), newSuccessors, newPrimarySuccessor);
    179 
    180         newBlocks.set(newBlocks.indexOfLabel(block.getLabel()), newBB);
    181     }
    182 }
    183