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 */ convertToSsaMethod(RopMethod rmeth, int paramWidth, boolean isStatic)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 */ updateSsaMethod(SsaMethod ssaMeth, int threshold)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 */ testEdgeSplit(RopMethod rmeth, int paramWidth, boolean isStatic)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 */ testPhiPlacement(RopMethod rmeth, int paramWidth, boolean isStatic)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 */ edgeSplit(SsaMethod result)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 */ edgeSplitPredecessors(SsaMethod result)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 */ nodeNeedsUniquePredecessor(SsaBasicBlock block)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 */ edgeSplitMoveExceptionsAndResults(SsaMethod ssaMeth)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 */ edgeSplitSuccessors(SsaMethod result)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 */ needsNewSuccessor(SsaBasicBlock block, SsaBasicBlock succ)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 */ placePhiFunctions(SsaMethod ssaMeth, LocalVariableInfo localInfo, int threshold)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