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.CstInsn;
     20 import com.android.dx.rop.code.Insn;
     21 import com.android.dx.rop.code.RegOps;
     22 import com.android.dx.rop.code.RegisterSpecList;
     23 import com.android.dx.rop.code.Rop;
     24 import com.android.dx.rop.code.RegisterSpec;
     25 import com.android.dx.rop.cst.Constant;
     26 import com.android.dx.rop.cst.CstInteger;
     27 import com.android.dx.rop.cst.TypedConstant;
     28 import com.android.dx.rop.type.TypeBearer;
     29 import com.android.dx.rop.type.Type;
     30 
     31 import java.util.ArrayList;
     32 import java.util.BitSet;
     33 
     34 /**
     35  * A small variant of Wegman and Zadeck's Sparse Conditional Constant
     36  * Propagation algorithm.
     37  */
     38 public class SCCP {
     39     /** Lattice values  */
     40     private static final int TOP = 0;
     41     private static final int CONSTANT = 1;
     42     private static final int VARYING = 2;
     43     /** method we're processing */
     44     private SsaMethod ssaMeth;
     45     /** ssaMeth.getRegCount() */
     46     private int regCount;
     47     /** Lattice values for each SSA register */
     48     private int[] latticeValues;
     49     /** For those registers that are constant, this is the constant value */
     50     private Constant[] latticeConstants;
     51     /** Worklist of basic blocks to be processed */
     52     private ArrayList<SsaBasicBlock> cfgWorklist;
     53     /** Bitset containing bits for each block that has been found executable */
     54     private BitSet executableBlocks;
     55     /** Worklist for SSA edges.  This is a list of registers to process */
     56     private ArrayList<SsaInsn> ssaWorklist;
     57     /**
     58      * Worklist for SSA edges that represent varying values.  It makes the
     59      * algorithm much faster if you move all values to VARYING as fast as
     60      * possible.
     61      */
     62     private ArrayList<SsaInsn> varyingWorklist;
     63 
     64     private SCCP(SsaMethod ssaMeth) {
     65         this.ssaMeth = ssaMeth;
     66         this.regCount = ssaMeth.getRegCount();
     67         this.latticeValues = new int[this.regCount];
     68         this.latticeConstants = new Constant[this.regCount];
     69         this.cfgWorklist = new ArrayList<SsaBasicBlock>();
     70         this.executableBlocks = new BitSet(ssaMeth.getBlocks().size());
     71         this.ssaWorklist = new ArrayList<SsaInsn>();
     72         this.varyingWorklist = new ArrayList<SsaInsn>();
     73         for (int i = 0; i < this.regCount; i++) {
     74             latticeValues[i] = TOP;
     75             latticeConstants[i] = null;
     76         }
     77     }
     78 
     79     /**
     80      * Performs sparse conditional constant propagation on a method.
     81      * @param ssaMethod Method to process
     82      */
     83     public static void process (SsaMethod ssaMethod) {
     84         new SCCP(ssaMethod).run();
     85     }
     86 
     87     /**
     88      * Add a new SSA basic block to the CFG worklist
     89      * @param ssaBlock Block to add
     90      */
     91     private void addBlockToWorklist(SsaBasicBlock ssaBlock) {
     92         if (!executableBlocks.get(ssaBlock.getIndex())) {
     93             cfgWorklist.add(ssaBlock);
     94             executableBlocks.set(ssaBlock.getIndex());
     95         }
     96     }
     97 
     98     /**
     99      * Adds an SSA register's uses to the SSA worklist.
    100      * @param reg SSA register
    101      * @param latticeValue new lattice value for @param reg.
    102      */
    103     private void addUsersToWorklist(int reg, int latticeValue) {
    104         if (latticeValue == VARYING) {
    105             for (SsaInsn insn : ssaMeth.getUseListForRegister(reg)) {
    106                 varyingWorklist.add(insn);
    107             }
    108         } else {
    109             for (SsaInsn insn : ssaMeth.getUseListForRegister(reg)) {
    110                 ssaWorklist.add(insn);
    111             }
    112         }
    113     }
    114 
    115     /**
    116      * Sets a lattice value for a register to value.
    117      * @param reg SSA register
    118      * @param value Lattice value
    119      * @param cst Constant value (may be null)
    120      * @return true if the lattice value changed.
    121      */
    122     private boolean setLatticeValueTo(int reg, int value, Constant cst) {
    123         if (value != CONSTANT) {
    124             if (latticeValues[reg] != value) {
    125                 latticeValues[reg] = value;
    126                 return true;
    127             }
    128             return false;
    129         } else {
    130             if (latticeValues[reg] != value
    131                     || !latticeConstants[reg].equals(cst)) {
    132                 latticeValues[reg] = value;
    133                 latticeConstants[reg] = cst;
    134                 return true;
    135             }
    136             return false;
    137         }
    138     }
    139 
    140     /**
    141      * Simulates a PHI node and set the lattice for the result
    142      * to the approriate value.
    143      * Meet values:
    144      * TOP x anything = anything
    145      * VARYING x anything = VARYING
    146      * CONSTANT x CONSTANT = CONSTANT if equal constants, VARYING otherwise
    147      * @param insn PHI to simulate.
    148      */
    149     private void simulatePhi(PhiInsn insn) {
    150         int phiResultReg = insn.getResult().getReg();
    151 
    152         if (latticeValues[phiResultReg] == VARYING) {
    153             return;
    154         }
    155 
    156         RegisterSpecList sources = insn.getSources();
    157         int phiResultValue = TOP;
    158         Constant phiConstant = null;
    159         int sourceSize = sources.size();
    160 
    161         for (int i = 0; i < sourceSize; i++) {
    162             int predBlockIndex = insn.predBlockIndexForSourcesIndex(i);
    163             int sourceReg = sources.get(i).getReg();
    164             int sourceRegValue = latticeValues[sourceReg];
    165 
    166             if (!executableBlocks.get(predBlockIndex)
    167                     || sourceRegValue == TOP) {
    168                 continue;
    169             }
    170 
    171             if (sourceRegValue == CONSTANT) {
    172                 if (phiConstant == null) {
    173                     phiConstant = latticeConstants[sourceReg];
    174                     phiResultValue = CONSTANT;
    175                  } else if (!latticeConstants[sourceReg].equals(phiConstant)){
    176                     phiResultValue = VARYING;
    177                     break;
    178                 }
    179 
    180             } else if (sourceRegValue == VARYING) {
    181                 phiResultValue = VARYING;
    182                 break;
    183             }
    184         }
    185         if (setLatticeValueTo(phiResultReg, phiResultValue, phiConstant)) {
    186             addUsersToWorklist(phiResultReg, phiResultValue);
    187         }
    188     }
    189 
    190     /**
    191      * Simulate a block and note the results in the lattice.
    192      * @param block Block to visit
    193      */
    194     private void simulateBlock(SsaBasicBlock block) {
    195         for (SsaInsn insn : block.getInsns()) {
    196             if (insn instanceof PhiInsn) {
    197                 simulatePhi((PhiInsn) insn);
    198             } else {
    199                 simulateStmt(insn);
    200             }
    201         }
    202     }
    203     private static String latticeValName(int latticeVal) {
    204         switch (latticeVal) {
    205             case TOP: return "TOP";
    206             case CONSTANT: return "CONSTANT";
    207             case VARYING: return "VARYING";
    208             default: return "UNKNOWN";
    209         }
    210     }
    211 
    212     /**
    213      * Simplifies a jump statement.
    214      * @param insn jump to simplify
    215      * @return an instruction representing the simplified jump.
    216      */
    217     private Insn simplifyJump (Insn insn) {
    218         return insn;
    219     }
    220 
    221     /**
    222      * Simulates math insns, if possible.
    223      *
    224      * @param insn non-null insn to simulate
    225      * @return constant result or null if not simulatable.
    226      */
    227     private Constant simulateMath(SsaInsn insn) {
    228         Insn ropInsn = insn.getOriginalRopInsn();
    229         int opcode = insn.getOpcode().getOpcode();
    230         RegisterSpecList sources = insn.getSources();
    231         int regA = sources.get(0).getReg();
    232         Constant cA;
    233         Constant cB;
    234 
    235         if (latticeValues[regA] != CONSTANT) {
    236             cA = null;
    237         } else {
    238             cA = latticeConstants[regA];
    239         }
    240 
    241         if (sources.size() == 1) {
    242             CstInsn cstInsn = (CstInsn) ropInsn;
    243             cB = cstInsn.getConstant();
    244         } else { /* sources.size() == 2 */
    245             int regB = sources.get(1).getReg();
    246             if (latticeValues[regB] != CONSTANT) {
    247                 cB = null;
    248             } else {
    249                 cB = latticeConstants[regB];
    250             }
    251         }
    252 
    253         if (cA == null || cB == null) {
    254             //TODO handle a constant of 0 with MUL or AND
    255             return null;
    256         }
    257 
    258         switch (insn.getResult().getBasicType()) {
    259             case Type.BT_INT:
    260                 int vR;
    261                 boolean skip=false;
    262 
    263                 int vA = ((CstInteger) cA).getValue();
    264                 int vB = ((CstInteger) cB).getValue();
    265 
    266                 switch (opcode) {
    267                     case RegOps.ADD:
    268                         vR = vA + vB;
    269                         break;
    270                     case RegOps.SUB:
    271                         vR = vA - vB;
    272                         break;
    273                     case RegOps.MUL:
    274                         vR = vA * vB;
    275                         break;
    276                     case RegOps.DIV:
    277                         if (vB == 0) {
    278                             skip = true;
    279                             vR = 0; // just to hide a warning
    280                         } else {
    281                             vR = vA / vB;
    282                         }
    283                         break;
    284                     case RegOps.AND:
    285                         vR = vA & vB;
    286                         break;
    287                     case RegOps.OR:
    288                         vR = vA | vB;
    289                         break;
    290                     case RegOps.XOR:
    291                         vR = vA ^ vB;
    292                         break;
    293                     case RegOps.SHL:
    294                         vR = vA << vB;
    295                         break;
    296                     case RegOps.SHR:
    297                         vR = vA >> vB;
    298                         break;
    299                     case RegOps.USHR:
    300                         vR = vA >>> vB;
    301                         break;
    302                     case RegOps.REM:
    303                         vR = vA % vB;
    304                         break;
    305                     default:
    306                         throw new RuntimeException("Unexpected op");
    307                 }
    308 
    309                 return skip ? null : CstInteger.make(vR);
    310 
    311             default:
    312                 // not yet supported
    313                 return null;
    314         }
    315     }
    316 
    317     /**
    318      * Simulates a statement and set the result lattice value.
    319      * @param insn instruction to simulate
    320      */
    321     private void simulateStmt(SsaInsn insn) {
    322         Insn ropInsn = insn.getOriginalRopInsn();
    323         if (ropInsn.getOpcode().getBranchingness() != Rop.BRANCH_NONE
    324                 || ropInsn.getOpcode().isCallLike()) {
    325             ropInsn = simplifyJump (ropInsn);
    326             /* TODO: If jump becomes constant, only take true edge. */
    327             SsaBasicBlock block = insn.getBlock();
    328             int successorSize = block.getSuccessorList().size();
    329             for (int i = 0; i < successorSize; i++) {
    330                 int successor = block.getSuccessorList().get(i);
    331                 addBlockToWorklist(ssaMeth.getBlocks().get(successor));
    332             }
    333         }
    334 
    335         if (insn.getResult() == null) {
    336             return;
    337         }
    338 
    339         /* TODO: Simplify statements when possible using the constants. */
    340         int resultReg = insn.getResult().getReg();
    341         int resultValue = VARYING;
    342         Constant resultConstant = null;
    343         int opcode = insn.getOpcode().getOpcode();
    344         switch (opcode) {
    345             case RegOps.CONST: {
    346                 CstInsn cstInsn = (CstInsn)ropInsn;
    347                 resultValue = CONSTANT;
    348                 resultConstant = cstInsn.getConstant();
    349                 break;
    350             }
    351             case RegOps.MOVE: {
    352                 if (insn.getSources().size() == 1) {
    353                     int sourceReg = insn.getSources().get(0).getReg();
    354                     resultValue = latticeValues[sourceReg];
    355                     resultConstant = latticeConstants[sourceReg];
    356                 }
    357                 break;
    358             }
    359 
    360             case RegOps.ADD:
    361             case RegOps.SUB:
    362             case RegOps.MUL:
    363             case RegOps.DIV:
    364             case RegOps.AND:
    365             case RegOps.OR:
    366             case RegOps.XOR:
    367             case RegOps.SHL:
    368             case RegOps.SHR:
    369             case RegOps.USHR:
    370             case RegOps.REM:
    371 
    372                 resultConstant = simulateMath(insn);
    373 
    374                 if (resultConstant == null) {
    375                     resultValue = VARYING;
    376                 } else {
    377                     resultValue = CONSTANT;
    378                 }
    379             break;
    380             /* TODO: Handle non-int arithmetic.
    381                TODO: Eliminate check casts that we can prove the type of. */
    382             default: {}
    383         }
    384         if (setLatticeValueTo(resultReg, resultValue, resultConstant)) {
    385             addUsersToWorklist(resultReg, resultValue);
    386         }
    387     }
    388 
    389     private void run() {
    390         SsaBasicBlock firstBlock = ssaMeth.getEntryBlock();
    391         addBlockToWorklist(firstBlock);
    392 
    393         /* Empty all the worklists by propagating our values */
    394         while (!cfgWorklist.isEmpty()
    395                 || !ssaWorklist.isEmpty()
    396                 || !varyingWorklist.isEmpty()) {
    397             while (!cfgWorklist.isEmpty()) {
    398                 int listSize = cfgWorklist.size() - 1;
    399                 SsaBasicBlock block = cfgWorklist.remove(listSize);
    400                 simulateBlock(block);
    401             }
    402             while (!varyingWorklist.isEmpty()) {
    403                 int listSize = varyingWorklist.size() - 1;
    404                 SsaInsn insn = varyingWorklist.remove(listSize);
    405 
    406                 if (!executableBlocks.get(insn.getBlock().getIndex())) {
    407                     continue;
    408                 }
    409 
    410                 if (insn instanceof PhiInsn) {
    411                     simulatePhi((PhiInsn)insn);
    412                 } else {
    413                     simulateStmt(insn);
    414                 }
    415             }
    416             while (!ssaWorklist.isEmpty()) {
    417                 int listSize = ssaWorklist.size() - 1;
    418                 SsaInsn insn = ssaWorklist.remove(listSize);
    419 
    420                 if (!executableBlocks.get(insn.getBlock().getIndex())) {
    421                     continue;
    422                 }
    423 
    424                 if (insn instanceof PhiInsn) {
    425                     simulatePhi((PhiInsn)insn);
    426                 } else {
    427                     simulateStmt(insn);
    428                 }
    429             }
    430         }
    431 
    432         replaceConstants();
    433     }
    434 
    435     /**
    436      * Replaces TypeBearers in source register specs with constant type
    437      * bearers if possible. These are then referenced in later optimization
    438      * steps.
    439      */
    440     private void replaceConstants() {
    441         for (int reg = 0; reg < regCount; reg++) {
    442             if (latticeValues[reg] != CONSTANT) {
    443                 continue;
    444             }
    445             if (!(latticeConstants[reg] instanceof TypedConstant)) {
    446                 // We can't do much with these
    447                 continue;
    448             }
    449 
    450             SsaInsn defn = ssaMeth.getDefinitionForRegister(reg);
    451             TypeBearer typeBearer = defn.getResult().getTypeBearer();
    452 
    453             if (typeBearer.isConstant()) {
    454                 /*
    455                  * The definition was a constant already.
    456                  * The uses should be as well.
    457                  */
    458                 continue;
    459             }
    460 
    461             /*
    462              * Update the sources RegisterSpec's of all non-move uses.
    463              * These will be used in later steps.
    464              */
    465             for (SsaInsn insn : ssaMeth.getUseListForRegister(reg)) {
    466                 if (insn.isPhiOrMove()) {
    467                     continue;
    468                 }
    469 
    470                 NormalSsaInsn nInsn = (NormalSsaInsn) insn;
    471                 RegisterSpecList sources = insn.getSources();
    472 
    473                 int index = sources.indexOfRegister(reg);
    474 
    475                 RegisterSpec spec = sources.get(index);
    476                 RegisterSpec newSpec
    477                         = spec.withType((TypedConstant)latticeConstants[reg]);
    478 
    479                 nInsn.changeOneSource(index, newSpec);
    480             }
    481         }
    482     }
    483 }
    484