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