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.LocalItem; 20 import com.android.dx.rop.code.PlainInsn; 21 import com.android.dx.rop.code.RegisterSpec; 22 import com.android.dx.rop.code.RegisterSpecList; 23 import com.android.dx.rop.code.Rops; 24 import com.android.dx.rop.code.SourcePosition; 25 import com.android.dx.rop.type.Type; 26 import com.android.dx.util.IntList; 27 import java.util.ArrayList; 28 import java.util.BitSet; 29 import java.util.HashMap; 30 import java.util.HashSet; 31 32 /** 33 * Complete transformation to SSA form by renaming all registers accessed.<p> 34 * 35 * See Appel algorithm 19.7<p> 36 * 37 * Unlike the original algorithm presented in Appel, this renamer converts 38 * to a new flat (versionless) register space. The "version 0" registers, 39 * which represent the initial state of the Rop registers and should never 40 * actually be meaningfully accessed in a legal program, are represented 41 * as the first N registers in the SSA namespace. Subsequent assignments 42 * are assigned new unique names. Note that the incoming Rop representation 43 * has a concept of register widths, where 64-bit values are stored into 44 * two adjoining Rop registers. This adjoining register representation is 45 * ignored in SSA form conversion and while in SSA form, each register can be e 46 * either 32 or 64 bits wide depending on use. The adjoining-register 47 * represention is re-created later when converting back to Rop form. <p> 48 * 49 * But, please note, the SSA Renamer's ignoring of the adjoining-register ROP 50 * representation means that unaligned accesses to 64-bit registers are not 51 * supported. For example, you cannot do a 32-bit operation on a portion of 52 * a 64-bit register. This will never be observed to happen when coming 53 * from Java code, of course.<p> 54 * 55 * The implementation here, rather than keeping a single register version 56 * stack for the entire method as the dom tree is walked, instead keeps 57 * a mapping table for the current block being processed. Once the 58 * current block has been processed, this mapping table is then copied 59 * and used as the initial state for child blocks.<p> 60 */ 61 public class SsaRenamer implements Runnable { 62 /** debug flag */ 63 private static final boolean DEBUG = false; 64 65 /** method we're processing */ 66 private final SsaMethod ssaMeth; 67 68 /** next available SSA register */ 69 private int nextSsaReg; 70 71 /** the number of original rop registers */ 72 private final int ropRegCount; 73 74 /** work only on registers above this value */ 75 private int threshold; 76 77 /** 78 * indexed by block index; register version state for each block start. 79 * This list is updated by each dom parent for its children. The only 80 * sub-arrays that exist at any one time are the start states for blocks 81 * yet to be processed by a {@code BlockRenamer} instance. 82 */ 83 private final RegisterSpec[][] startsForBlocks; 84 85 /** map of SSA register number to debug (local var names) or null of n/a */ 86 private final ArrayList<LocalItem> ssaRegToLocalItems; 87 88 /** 89 * maps SSA registers back to the original rop number. Used for 90 * debug only. 91 */ 92 private IntList ssaRegToRopReg; 93 94 /** 95 * Constructs an instance of the renamer 96 * 97 * @param ssaMeth {@code non-null;} un-renamed SSA method that will 98 * be renamed. 99 */ SsaRenamer(SsaMethod ssaMeth)100 public SsaRenamer(SsaMethod ssaMeth) { 101 ropRegCount = ssaMeth.getRegCount(); 102 103 this.ssaMeth = ssaMeth; 104 105 /* 106 * Reserve the first N registers in the SSA register space for 107 * "version 0" registers. 108 */ 109 nextSsaReg = ropRegCount; 110 threshold = 0; 111 startsForBlocks = new RegisterSpec[ssaMeth.getBlocks().size()][]; 112 113 ssaRegToLocalItems = new ArrayList<LocalItem>(); 114 115 if (DEBUG) { 116 ssaRegToRopReg = new IntList(ropRegCount); 117 } 118 119 /* 120 * Appel 19.7 121 * 122 * Initialization: 123 * for each variable a // register i 124 * Count[a] <- 0 // nextSsaReg, flattened 125 * Stack[a] <- 0 // versionStack 126 * push 0 onto Stack[a] 127 * 128 */ 129 130 // top entry for the version stack is version 0 131 RegisterSpec[] initialRegMapping = new RegisterSpec[ropRegCount]; 132 for (int i = 0; i < ropRegCount; i++) { 133 // everyone starts with a version 0 register 134 initialRegMapping[i] = RegisterSpec.make(i, Type.VOID); 135 136 if (DEBUG) { 137 ssaRegToRopReg.add(i); 138 } 139 } 140 141 // Initial state for entry block 142 startsForBlocks[ssaMeth.getEntryBlockIndex()] = initialRegMapping; 143 } 144 145 /** 146 * Constructs an instance of the renamer with threshold set 147 * 148 * @param ssaMeth {@code non-null;} un-renamed SSA method that will 149 * be renamed. 150 * @param thresh registers below this number are unchanged 151 */ SsaRenamer(SsaMethod ssaMeth, int thresh)152 public SsaRenamer(SsaMethod ssaMeth, int thresh) { 153 this(ssaMeth); 154 threshold = thresh; 155 } 156 157 /** 158 * Performs renaming transformation, modifying the method's instructions 159 * in-place. 160 */ run()161 public void run() { 162 // Rename each block in dom-tree DFS order. 163 ssaMeth.forEachBlockDepthFirstDom(new SsaBasicBlock.Visitor() { 164 public void visitBlock (SsaBasicBlock block, 165 SsaBasicBlock unused) { 166 new BlockRenamer(block).process(); 167 } 168 }); 169 170 ssaMeth.setNewRegCount(nextSsaReg); 171 ssaMeth.onInsnsChanged(); 172 173 if (DEBUG) { 174 System.out.println("SSA\tRop"); 175 /* 176 * We're going to compute the version of the rop register 177 * by keeping a running total of how many times the rop 178 * register has been mapped. 179 */ 180 int[] versions = new int[ropRegCount]; 181 182 int sz = ssaRegToRopReg.size(); 183 for (int i = 0; i < sz; i++) { 184 int ropReg = ssaRegToRopReg.get(i); 185 System.out.println(i + "\t" + ropReg + "[" 186 + versions[ropReg] + "]"); 187 versions[ropReg]++; 188 } 189 } 190 } 191 192 /** 193 * Duplicates a RegisterSpec array. 194 * 195 * @param orig {@code non-null;} array to duplicate 196 * @return {@code non-null;} new instance 197 */ dupArray(RegisterSpec[] orig)198 private static RegisterSpec[] dupArray(RegisterSpec[] orig) { 199 RegisterSpec[] copy = new RegisterSpec[orig.length]; 200 201 System.arraycopy(orig, 0, copy, 0, orig.length); 202 203 return copy; 204 } 205 206 /** 207 * Gets a local variable item for a specified register. 208 * 209 * @param ssaReg register in SSA name space 210 * @return {@code null-ok;} Local variable name or null if none 211 */ getLocalForNewReg(int ssaReg)212 private LocalItem getLocalForNewReg(int ssaReg) { 213 if (ssaReg < ssaRegToLocalItems.size()) { 214 return ssaRegToLocalItems.get(ssaReg); 215 } else { 216 return null; 217 } 218 } 219 220 /** 221 * Records a debug (local variable) name for a specified register. 222 * 223 * @param ssaReg non-null named register spec in SSA name space 224 */ setNameForSsaReg(RegisterSpec ssaReg)225 private void setNameForSsaReg(RegisterSpec ssaReg) { 226 int reg = ssaReg.getReg(); 227 LocalItem local = ssaReg.getLocalItem(); 228 229 ssaRegToLocalItems.ensureCapacity(reg + 1); 230 while (ssaRegToLocalItems.size() <= reg) { 231 ssaRegToLocalItems.add(null); 232 } 233 234 ssaRegToLocalItems.set(reg, local); 235 } 236 237 /** 238 * Returns true if this SSA register is below the specified threshold. 239 * Used when most code is already in SSA form, and renaming is needed only 240 * for registers above a certain threshold. 241 * 242 * @param ssaReg the SSA register in question 243 * @return {@code true} if its register number is below the threshold 244 */ isBelowThresholdRegister(int ssaReg)245 private boolean isBelowThresholdRegister(int ssaReg) { 246 return ssaReg < threshold; 247 } 248 249 /** 250 * Returns true if this SSA register is a "version 0" 251 * register. All version 0 registers are assigned the first N register 252 * numbers, where N is the count of original rop registers. 253 * 254 * @param ssaReg the SSA register in question 255 * @return true if it is a version 0 register. 256 */ isVersionZeroRegister(int ssaReg)257 private boolean isVersionZeroRegister(int ssaReg) { 258 return ssaReg < ropRegCount; 259 } 260 261 /** 262 * Returns true if a and b are equal or are both null. 263 * 264 * @param a null-ok 265 * @param b null-ok 266 * @return Returns true if a and b are equal or are both null 267 */ equalsHandlesNulls(Object a, Object b)268 private static boolean equalsHandlesNulls(Object a, Object b) { 269 return a == b || (a != null && a.equals(b)); 270 } 271 272 /** 273 * Processes all insns in a block and renames their registers 274 * as appropriate. 275 */ 276 private class BlockRenamer implements SsaInsn.Visitor{ 277 /** {@code non-null;} block we're processing. */ 278 private final SsaBasicBlock block; 279 280 /** 281 * {@code non-null;} indexed by old register name. The current 282 * top of the version stack as seen by this block. It's 283 * initialized from the ending state of its dom parent, 284 * updated as the block's instructions are processed, and then 285 * copied to each one of its dom children. 286 */ 287 private final RegisterSpec[] currentMapping; 288 289 /** 290 * contains the set of moves we need to keep to preserve local 291 * var info. All other moves will be deleted. 292 */ 293 private final HashSet<SsaInsn> movesToKeep; 294 295 /** 296 * maps the set of insns to replace after renaming is finished 297 * on the block. 298 */ 299 private final HashMap<SsaInsn, SsaInsn> insnsToReplace; 300 301 private final RenamingMapper mapper; 302 303 /** 304 * Constructs a block renamer instance. Call {@code process} 305 * to process. 306 * 307 * @param block {@code non-null;} block to process 308 */ BlockRenamer(final SsaBasicBlock block)309 BlockRenamer(final SsaBasicBlock block) { 310 this.block = block; 311 currentMapping = startsForBlocks[block.getIndex()]; 312 movesToKeep = new HashSet<SsaInsn>(); 313 insnsToReplace = new HashMap<SsaInsn, SsaInsn>(); 314 mapper = new RenamingMapper(); 315 316 // We don't need our own start state anymore 317 startsForBlocks[block.getIndex()] = null; 318 } 319 320 /** 321 * Provides a register mapping between the old register space 322 * and the current renaming mapping. The mapping is updated 323 * as the current block's instructions are processed. 324 */ 325 private class RenamingMapper extends RegisterMapper { RenamingMapper()326 public RenamingMapper() { 327 // This space intentionally left blank. 328 } 329 330 /** {@inheritDoc} */ 331 @Override getNewRegisterCount()332 public int getNewRegisterCount() { 333 return nextSsaReg; 334 } 335 336 /** {@inheritDoc} */ 337 @Override map(RegisterSpec registerSpec)338 public RegisterSpec map(RegisterSpec registerSpec) { 339 if (registerSpec == null) return null; 340 341 int reg = registerSpec.getReg(); 342 343 // For debugging: assert that the mapped types are compatible. 344 if (DEBUG) { 345 RegisterSpec newVersion = currentMapping[reg]; 346 if (newVersion.getBasicType() != Type.BT_VOID 347 && registerSpec.getBasicFrameType() 348 != newVersion.getBasicFrameType()) { 349 350 throw new RuntimeException( 351 "mapping registers of incompatible types! " 352 + registerSpec 353 + " " + currentMapping[reg]); 354 } 355 } 356 357 return registerSpec.withReg(currentMapping[reg].getReg()); 358 } 359 } 360 361 /** 362 * Renames all the variables in this block and inserts appriopriate 363 * phis in successor blocks. 364 */ process()365 public void process() { 366 /* 367 * From Appel: 368 * 369 * Rename(n) = 370 * for each statement S in block n // 'statement' in 'block' 371 */ 372 373 block.forEachInsn(this); 374 375 updateSuccessorPhis(); 376 377 // Delete all move insns in this block. 378 ArrayList<SsaInsn> insns = block.getInsns(); 379 int szInsns = insns.size(); 380 381 for (int i = szInsns - 1; i >= 0 ; i--) { 382 SsaInsn insn = insns.get(i); 383 SsaInsn replaceInsn; 384 385 replaceInsn = insnsToReplace.get(insn); 386 387 if (replaceInsn != null) { 388 insns.set(i, replaceInsn); 389 } else if (insn.isNormalMoveInsn() 390 && !movesToKeep.contains(insn)) { 391 insns.remove(i); 392 } 393 } 394 395 // Store the start states for our dom children. 396 boolean first = true; 397 for (SsaBasicBlock child : block.getDomChildren()) { 398 if (child != block) { 399 // Don't bother duplicating the array for the first child. 400 RegisterSpec[] childStart = first ? currentMapping 401 : dupArray(currentMapping); 402 403 startsForBlocks[child.getIndex()] = childStart; 404 first = false; 405 } 406 } 407 408 // currentMapping is owned by a child now. 409 } 410 411 /** 412 * Enforces a few contraints when a register mapping is added. 413 * 414 * <ol> 415 * <li> Ensures that all new SSA registers specs in the mapping 416 * table with the same register number are identical. In effect, once 417 * an SSA register spec has received or lost a local variable name, 418 * then every old-namespace register that maps to it should gain or 419 * lose its local variable name as well. 420 * <li> Records the local name associated with the 421 * register so that a register is never associated with more than one 422 * local. 423 * <li> ensures that only one SSA register 424 * at a time is considered to be associated with a local variable. When 425 * {@code currentMapping} is updated and the newly added element 426 * is named, strip that name from any other SSA registers. 427 * </ol> 428 * 429 * @param ropReg {@code >= 0;} rop register number 430 * @param ssaReg {@code non-null;} an SSA register that has just 431 * been added to {@code currentMapping} 432 */ addMapping(int ropReg, RegisterSpec ssaReg)433 private void addMapping(int ropReg, RegisterSpec ssaReg) { 434 int ssaRegNum = ssaReg.getReg(); 435 LocalItem ssaRegLocal = ssaReg.getLocalItem(); 436 437 currentMapping[ropReg] = ssaReg; 438 439 /* 440 * Ensure all SSA register specs with the same reg are identical. 441 */ 442 for (int i = currentMapping.length - 1; i >= 0; i--) { 443 RegisterSpec cur = currentMapping[i]; 444 445 if (ssaRegNum == cur.getReg()) { 446 currentMapping[i] = ssaReg; 447 } 448 } 449 450 // All further steps are for registers with local information. 451 if (ssaRegLocal == null) { 452 return; 453 } 454 455 // Record that this SSA reg has been associated with a local. 456 setNameForSsaReg(ssaReg); 457 458 // Ensure that no other SSA regs are associated with this local. 459 for (int i = currentMapping.length - 1; i >= 0; i--) { 460 RegisterSpec cur = currentMapping[i]; 461 462 if (ssaRegNum != cur.getReg() 463 && ssaRegLocal.equals(cur.getLocalItem())) { 464 currentMapping[i] = cur.withLocalItem(null); 465 } 466 } 467 } 468 469 /** 470 * {@inheritDoc} 471 * 472 * Phi insns have their result registers renamed. 473 */ visitPhiInsn(PhiInsn phi)474 public void visitPhiInsn(PhiInsn phi) { 475 /* don't process sources for phi's */ 476 processResultReg(phi); 477 } 478 479 /** 480 * {@inheritDoc} 481 * 482 * Move insns are treated as a simple mapping operation, and 483 * will later be removed unless they represent a local variable 484 * assignment. If they represent a local variable assignement, they 485 * are preserved. 486 */ visitMoveInsn(NormalSsaInsn insn)487 public void visitMoveInsn(NormalSsaInsn insn) { 488 /* 489 * For moves: copy propogate the move if we can, but don't 490 * if we need to preserve local variable info and the 491 * result has a different name than the source. 492 */ 493 494 RegisterSpec ropResult = insn.getResult(); 495 int ropResultReg = ropResult.getReg(); 496 int ropSourceReg = insn.getSources().get(0).getReg(); 497 498 insn.mapSourceRegisters(mapper); 499 int ssaSourceReg = insn.getSources().get(0).getReg(); 500 501 LocalItem sourceLocal 502 = currentMapping[ropSourceReg].getLocalItem(); 503 LocalItem resultLocal = ropResult.getLocalItem(); 504 505 /* 506 * A move from a register that's currently associated with a local 507 * to one that will not be associated with a local does not need 508 * to be preserved, but the local association should remain. 509 * Hence, we inherit the sourceLocal where the resultLocal is null. 510 */ 511 512 LocalItem newLocal 513 = (resultLocal == null) ? sourceLocal : resultLocal; 514 LocalItem associatedLocal = getLocalForNewReg(ssaSourceReg); 515 516 /* 517 * If we take the new local, will only one local have ever 518 * been associated with this SSA reg? 519 */ 520 boolean onlyOneAssociatedLocal 521 = associatedLocal == null || newLocal == null 522 || newLocal.equals(associatedLocal); 523 524 /* 525 * If we're going to copy-propogate, then the ssa register 526 * spec that's going to go into the mapping is made up of 527 * the source register number mapped from above, the type 528 * of the result, and the name either from the result (if 529 * specified) or inherited from the existing mapping. 530 * 531 * The move source has incomplete type information in null 532 * object cases, so the result type is used. 533 */ 534 RegisterSpec ssaReg 535 = RegisterSpec.makeLocalOptional( 536 ssaSourceReg, ropResult.getType(), newLocal); 537 538 if (!Optimizer.getPreserveLocals() || (onlyOneAssociatedLocal 539 && equalsHandlesNulls(newLocal, sourceLocal)) && 540 threshold == 0) { 541 /* 542 * We don't have to keep this move to preserve local 543 * information. Either the name is the same, or the result 544 * register spec is unnamed. 545 */ 546 547 addMapping(ropResultReg, ssaReg); 548 } else if (onlyOneAssociatedLocal && sourceLocal == null && 549 threshold == 0) { 550 /* 551 * The register was previously unnamed. This means that a 552 * local starts after it's first assignment in SSA form 553 */ 554 555 RegisterSpecList ssaSources = RegisterSpecList.make( 556 RegisterSpec.make(ssaReg.getReg(), 557 ssaReg.getType(), newLocal)); 558 559 SsaInsn newInsn 560 = SsaInsn.makeFromRop( 561 new PlainInsn(Rops.opMarkLocal(ssaReg), 562 SourcePosition.NO_INFO, null, ssaSources),block); 563 564 insnsToReplace.put(insn, newInsn); 565 566 // Just map as above. 567 addMapping(ropResultReg, ssaReg); 568 } else { 569 /* 570 * Do not copy-propogate, since the two registers have 571 * two different local-variable names. 572 */ 573 processResultReg(insn); 574 575 movesToKeep.add(insn); 576 } 577 } 578 579 /** 580 * {@inheritDoc} 581 * 582 * All insns that are not move or phi insns have their source registers 583 * mapped ot the current mapping. Their result registers are then 584 * renamed to a new SSA register which is then added to the current 585 * register mapping. 586 */ visitNonMoveInsn(NormalSsaInsn insn)587 public void visitNonMoveInsn(NormalSsaInsn insn) { 588 /* for each use of some variable X in S */ 589 insn.mapSourceRegisters(mapper); 590 591 processResultReg(insn); 592 } 593 594 /** 595 * Renames the result register of this insn and updates the 596 * current register mapping. Does nothing if this insn has no result. 597 * Applied to all non-move insns. 598 * 599 * @param insn insn to process. 600 */ processResultReg(SsaInsn insn)601 void processResultReg(SsaInsn insn) { 602 RegisterSpec ropResult = insn.getResult(); 603 604 if (ropResult == null) { 605 return; 606 } 607 608 int ropReg = ropResult.getReg(); 609 if (isBelowThresholdRegister(ropReg)) { 610 return; 611 } 612 613 insn.changeResultReg(nextSsaReg); 614 addMapping(ropReg, insn.getResult()); 615 616 if (DEBUG) { 617 ssaRegToRopReg.add(ropReg); 618 } 619 620 nextSsaReg++; 621 } 622 623 /** 624 * Updates the phi insns in successor blocks with operands based 625 * on the current mapping of the rop register the phis represent. 626 */ updateSuccessorPhis()627 private void updateSuccessorPhis() { 628 PhiInsn.Visitor visitor = new PhiInsn.Visitor() { 629 public void visitPhiInsn (PhiInsn insn) { 630 int ropReg; 631 632 ropReg = insn.getRopResultReg(); 633 if (isBelowThresholdRegister(ropReg)) { 634 return; 635 } 636 637 /* 638 * Never add a version 0 register as a phi 639 * operand. Version 0 registers represent the 640 * initial register state, and thus are never 641 * significant. Furthermore, the register liveness 642 * algorithm doesn't properly count them as "live 643 * in" at the beginning of the method. 644 */ 645 646 RegisterSpec stackTop = currentMapping[ropReg]; 647 if (!isVersionZeroRegister(stackTop.getReg())) { 648 insn.addPhiOperand(stackTop, block); 649 } 650 } 651 }; 652 653 BitSet successors = block.getSuccessors(); 654 for (int i = successors.nextSetBit(0); i >= 0; 655 i = successors.nextSetBit(i + 1)) { 656 SsaBasicBlock successor = ssaMeth.getBlocks().get(i); 657 successor.forEachPhiInsn(visitor); 658 } 659 } 660 } 661 } 662