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