2 * Copyright (C) 2007 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.dx.ssa;
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;
28 import java.util.ArrayList;
29 import java.util.BitSet;
30 import java.util.HashMap;
31 import java.util.HashSet;
34 * Complete transformation to SSA form by renaming all registers accessed.<p>
36 * See Appel algorithm 19.7<p>
38 * Unlike the original algorithm presented in Appel, this renamer converts
39 * to a new flat (versionless) register space. The "version 0" registers,
40 * which represent the initial state of the Rop registers and should never
41 * actually be meaningfully accessed in a legal program, are represented
42 * as the first N registers in the SSA namespace. Subsequent assignments
43 * are assigned new unique names. Note that the incoming Rop representation
44 * has a concept of register widths, where 64-bit values are stored into
45 * two adjoining Rop registers. This adjoining register representation is
46 * ignored in SSA form conversion and while in SSA form, each register can be e
47 * either 32 or 64 bits wide depending on use. The adjoining-register
48 * represention is re-created later when converting back to Rop form. <p>
50 * But, please note, the SSA Renamer's ignoring of the adjoining-register ROP
51 * representation means that unaligned accesses to 64-bit registers are not
52 * supported. For example, you cannot do a 32-bit operation on a portion of
53 * a 64-bit register. This will never be observed to happen when coming
54 * from Java code, of course.<p>
56 * The implementation here, rather than keeping a single register version
57 * stack for the entire method as the dom tree is walked, instead keeps
58 * a mapping table for the current block being processed. Once the
59 * current block has been processed, this mapping table is then copied
60 * and used as the initial state for child blocks.<p>
62 public class SsaRenamer implements Runnable {
64 private static final boolean DEBUG = false;
66 /** method we're processing */
67 private final SsaMethod ssaMeth;
69 /** next available SSA register */
70 private int nextSsaReg;
72 /** the number of original rop registers */
73 private final int ropRegCount;
75 /** work only on registers above this value */
76 private int threshold;
79 * indexed by block index; register version state for each block start.
80 * This list is updated by each dom parent for its children. The only
81 * sub-arrays that exist at any one time are the start states for blocks
82 * yet to be processed by a {@code BlockRenamer} instance.
84 private final RegisterSpec[][] startsForBlocks;
86 /** map of SSA register number to debug (local var names) or null of n/a */
87 private final ArrayList<LocalItem> ssaRegToLocalItems;
90 * maps SSA registers back to the original rop number. Used for
93 private IntList ssaRegToRopReg;
96 * Constructs an instance of the renamer
98 * @param ssaMeth {@code non-null;} un-renamed SSA method that will
101 public SsaRenamer(SsaMethod ssaMeth) {
102 ropRegCount = ssaMeth.getRegCount();
104 this.ssaMeth = ssaMeth;
107 * Reserve the first N registers in the SSA register space for
108 * "version 0" registers.
110 nextSsaReg = ropRegCount;
112 startsForBlocks = new RegisterSpec[ssaMeth.getBlocks().size()][];
114 ssaRegToLocalItems = new ArrayList<LocalItem>();
117 ssaRegToRopReg = new IntList(ropRegCount);
124 * for each variable a // register i
125 * Count[a] <- 0 // nextSsaReg, flattened
126 * Stack[a] <- 0 // versionStack
127 * push 0 onto Stack[a]
131 // top entry for the version stack is version 0
132 RegisterSpec[] initialRegMapping = new RegisterSpec[ropRegCount];
133 for (int i = 0; i < ropRegCount; i++) {
134 // everyone starts with a version 0 register
135 initialRegMapping[i] = RegisterSpec.make(i, Type.VOID);
138 ssaRegToRopReg.add(i);
142 // Initial state for entry block
143 startsForBlocks[ssaMeth.getEntryBlockIndex()] = initialRegMapping;
147 * Constructs an instance of the renamer with threshold set
149 * @param ssaMeth {@code non-null;} un-renamed SSA method that will
151 * @param thresh registers below this number are unchanged
153 public SsaRenamer(SsaMethod ssaMeth, int thresh) {
159 * Performs renaming transformation, modifying the method's instructions
163 // Rename each block in dom-tree DFS order.
164 ssaMeth.forEachBlockDepthFirstDom(new SsaBasicBlock.Visitor() {
165 public void visitBlock (SsaBasicBlock block,
166 SsaBasicBlock unused) {
167 new BlockRenamer(block).process();
171 ssaMeth.setNewRegCount(nextSsaReg);
172 ssaMeth.onInsnsChanged();
175 System.out.println("SSA\tRop");
177 * We're going to compute the version of the rop register
178 * by keeping a running total of how many times the rop
179 * register has been mapped.
181 int[] versions = new int[ropRegCount];
183 int sz = ssaRegToRopReg.size();
184 for (int i = 0; i < sz; i++) {
185 int ropReg = ssaRegToRopReg.get(i);
186 System.out.println(i + "\t" + ropReg + "["
187 + versions[ropReg] + "]");
194 * Duplicates a RegisterSpec array.
196 * @param orig {@code non-null;} array to duplicate
197 * @return {@code non-null;} new instance
199 private static RegisterSpec[] dupArray(RegisterSpec[] orig) {
200 RegisterSpec[] copy = new RegisterSpec[orig.length];
202 System.arraycopy(orig, 0, copy, 0, orig.length);
208 * Gets a local variable item for a specified register.
210 * @param ssaReg register in SSA name space
211 * @return {@code null-ok;} Local variable name or null if none
213 private LocalItem getLocalForNewReg(int ssaReg) {
214 if (ssaReg < ssaRegToLocalItems.size()) {
215 return ssaRegToLocalItems.get(ssaReg);
222 * Records a debug (local variable) name for a specified register.
224 * @param ssaReg non-null named register spec in SSA name space
226 private void setNameForSsaReg(RegisterSpec ssaReg) {
227 int reg = ssaReg.getReg();
228 LocalItem local = ssaReg.getLocalItem();
230 ssaRegToLocalItems.ensureCapacity(reg + 1);
231 while (ssaRegToLocalItems.size() <= reg) {
232 ssaRegToLocalItems.add(null);
235 ssaRegToLocalItems.set(reg, local);
239 * Returns true if this SSA register is below the specified threshold.
240 * Used when most code is already in SSA form, and renaming is needed only
241 * for registers above a certain threshold.
243 * @param ssaReg the SSA register in question
244 * @return {@code true} if its register number is below the threshold
246 private boolean isBelowThresholdRegister(int ssaReg) {
247 return ssaReg < threshold;
251 * Returns true if this SSA register is a "version 0"
252 * register. All version 0 registers are assigned the first N register
253 * numbers, where N is the count of original rop registers.
255 * @param ssaReg the SSA register in question
256 * @return true if it is a version 0 register.
258 private boolean isVersionZeroRegister(int ssaReg) {
259 return ssaReg < ropRegCount;
263 * Returns true if a and b are equal or are both null.
267 * @return Returns true if a and b are equal or are both null
269 private static boolean equalsHandlesNulls(Object a, Object b) {
270 return a == b || (a != null && a.equals(b));
274 * Processes all insns in a block and renames their registers
277 private class BlockRenamer implements SsaInsn.Visitor{
278 /** {@code non-null;} block we're processing. */
279 private final SsaBasicBlock block;
282 * {@code non-null;} indexed by old register name. The current
283 * top of the version stack as seen by this block. It's
284 * initialized from the ending state of its dom parent,
285 * updated as the block's instructions are processed, and then
286 * copied to each one of its dom children.
288 private final RegisterSpec[] currentMapping;
291 * contains the set of moves we need to keep to preserve local
292 * var info. All other moves will be deleted.
294 private final HashSet<SsaInsn> movesToKeep;
297 * maps the set of insns to replace after renaming is finished
300 private final HashMap<SsaInsn, SsaInsn> insnsToReplace;
302 private final RenamingMapper mapper;
305 * Constructs a block renamer instance. Call {@code process}
308 * @param block {@code non-null;} block to process
310 BlockRenamer(final SsaBasicBlock block) {
312 currentMapping = startsForBlocks[block.getIndex()];
313 movesToKeep = new HashSet<SsaInsn>();
314 insnsToReplace = new HashMap<SsaInsn, SsaInsn>();
315 mapper = new RenamingMapper();
317 // We don't need our own start state anymore
318 startsForBlocks[block.getIndex()] = null;
322 * Provides a register mapping between the old register space
323 * and the current renaming mapping. The mapping is updated
324 * as the current block's instructions are processed.
326 private class RenamingMapper extends RegisterMapper {
327 public RenamingMapper() {
328 // This space intentionally left blank.
333 public int getNewRegisterCount() {
339 public RegisterSpec map(RegisterSpec registerSpec) {
340 if (registerSpec == null) return null;
342 int reg = registerSpec.getReg();
344 // For debugging: assert that the mapped types are compatible.
346 RegisterSpec newVersion = currentMapping[reg];
347 if (newVersion.getBasicType() != Type.BT_VOID
348 && registerSpec.getBasicFrameType()
349 != newVersion.getBasicFrameType()) {
351 throw new RuntimeException(
352 "mapping registers of incompatible types! "
354 + " " + currentMapping[reg]);
358 return registerSpec.withReg(currentMapping[reg].getReg());
363 * Renames all the variables in this block and inserts appriopriate
364 * phis in successor blocks.
366 public void process() {
371 * for each statement S in block n // 'statement' in 'block'
374 block.forEachInsn(this);
376 updateSuccessorPhis();
378 // Delete all move insns in this block.
379 ArrayList<SsaInsn> insns = block.getInsns();
380 int szInsns = insns.size();
382 for (int i = szInsns - 1; i >= 0 ; i--) {
383 SsaInsn insn = insns.get(i);
386 replaceInsn = insnsToReplace.get(insn);
388 if (replaceInsn != null) {
389 insns.set(i, replaceInsn);
390 } else if (insn.isNormalMoveInsn()
391 && !movesToKeep.contains(insn)) {
396 // Store the start states for our dom children.
397 boolean first = true;
398 for (SsaBasicBlock child : block.getDomChildren()) {
399 if (child != block) {
400 // Don't bother duplicating the array for the first child.
401 RegisterSpec[] childStart = first ? currentMapping
402 : dupArray(currentMapping);
404 startsForBlocks[child.getIndex()] = childStart;
409 // currentMapping is owned by a child now.
413 * Enforces a few contraints when a register mapping is added.
416 * <li> Ensures that all new SSA registers specs in the mapping
417 * table with the same register number are identical. In effect, once
418 * an SSA register spec has received or lost a local variable name,
419 * then every old-namespace register that maps to it should gain or
420 * lose its local variable name as well.
421 * <li> Records the local name associated with the
422 * register so that a register is never associated with more than one
424 * <li> ensures that only one SSA register
425 * at a time is considered to be associated with a local variable. When
426 * {@code currentMapping} is updated and the newly added element
427 * is named, strip that name from any other SSA registers.
430 * @param ropReg {@code >= 0;} rop register number
431 * @param ssaReg {@code non-null;} an SSA register that has just
432 * been added to {@code currentMapping}
434 private void addMapping(int ropReg, RegisterSpec ssaReg) {
435 int ssaRegNum = ssaReg.getReg();
436 LocalItem ssaRegLocal = ssaReg.getLocalItem();
438 currentMapping[ropReg] = ssaReg;
441 * Ensure all SSA register specs with the same reg are identical.
443 for (int i = currentMapping.length - 1; i >= 0; i--) {
444 RegisterSpec cur = currentMapping[i];
446 if (ssaRegNum == cur.getReg()) {
447 currentMapping[i] = ssaReg;
451 // All further steps are for registers with local information.
452 if (ssaRegLocal == null) {
456 // Record that this SSA reg has been associated with a local.
457 setNameForSsaReg(ssaReg);
459 // Ensure that no other SSA regs are associated with this local.
460 for (int i = currentMapping.length - 1; i >= 0; i--) {
461 RegisterSpec cur = currentMapping[i];
463 if (ssaRegNum != cur.getReg()
464 && ssaRegLocal.equals(cur.getLocalItem())) {
465 currentMapping[i] = cur.withLocalItem(null);
473 * Phi insns have their result registers renamed.
475 public void visitPhiInsn(PhiInsn phi) {
476 /* don't process sources for phi's */
477 processResultReg(phi);
483 * Move insns are treated as a simple mapping operation, and
484 * will later be removed unless they represent a local variable
485 * assignment. If they represent a local variable assignement, they
488 public void visitMoveInsn(NormalSsaInsn insn) {
490 * For moves: copy propogate the move if we can, but don't
491 * if we need to preserve local variable info and the
492 * result has a different name than the source.
495 RegisterSpec ropResult = insn.getResult();
496 int ropResultReg = ropResult.getReg();
497 int ropSourceReg = insn.getSources().get(0).getReg();
499 insn.mapSourceRegisters(mapper);
500 int ssaSourceReg = insn.getSources().get(0).getReg();
502 LocalItem sourceLocal
503 = currentMapping[ropSourceReg].getLocalItem();
504 LocalItem resultLocal = ropResult.getLocalItem();
507 * A move from a register that's currently associated with a local
508 * to one that will not be associated with a local does not need
509 * to be preserved, but the local association should remain.
510 * Hence, we inherit the sourceLocal where the resultLocal is null.
514 = (resultLocal == null) ? sourceLocal : resultLocal;
515 LocalItem associatedLocal = getLocalForNewReg(ssaSourceReg);
518 * If we take the new local, will only one local have ever
519 * been associated with this SSA reg?
521 boolean onlyOneAssociatedLocal
522 = associatedLocal == null || newLocal == null
523 || newLocal.equals(associatedLocal);
526 * If we're going to copy-propogate, then the ssa register
527 * spec that's going to go into the mapping is made up of
528 * the source register number mapped from above, the type
529 * of the result, and the name either from the result (if
530 * specified) or inherited from the existing mapping.
532 * The move source has incomplete type information in null
533 * object cases, so the result type is used.
536 = RegisterSpec.makeLocalOptional(
537 ssaSourceReg, ropResult.getType(), newLocal);
539 if (!Optimizer.getPreserveLocals() || (onlyOneAssociatedLocal
540 && equalsHandlesNulls(newLocal, sourceLocal)) &&
543 * We don't have to keep this move to preserve local
544 * information. Either the name is the same, or the result
545 * register spec is unnamed.
548 addMapping(ropResultReg, ssaReg);
549 } else if (onlyOneAssociatedLocal && sourceLocal == null &&
552 * The register was previously unnamed. This means that a
553 * local starts after it's first assignment in SSA form
556 RegisterSpecList ssaSources = RegisterSpecList.make(
557 RegisterSpec.make(ssaReg.getReg(),
558 ssaReg.getType(), newLocal));
561 = SsaInsn.makeFromRop(
562 new PlainInsn(Rops.opMarkLocal(ssaReg),
563 SourcePosition.NO_INFO, null, ssaSources),block);
565 insnsToReplace.put(insn, newInsn);
567 // Just map as above.
568 addMapping(ropResultReg, ssaReg);
571 * Do not copy-propogate, since the two registers have
572 * two different local-variable names.
574 processResultReg(insn);
576 movesToKeep.add(insn);
583 * All insns that are not move or phi insns have their source registers
584 * mapped ot the current mapping. Their result registers are then
585 * renamed to a new SSA register which is then added to the current
588 public void visitNonMoveInsn(NormalSsaInsn insn) {
589 /* for each use of some variable X in S */
590 insn.mapSourceRegisters(mapper);
592 processResultReg(insn);
596 * Renames the result register of this insn and updates the
597 * current register mapping. Does nothing if this insn has no result.
598 * Applied to all non-move insns.
600 * @param insn insn to process.
602 void processResultReg(SsaInsn insn) {
603 RegisterSpec ropResult = insn.getResult();
605 if (ropResult == null) {
609 int ropReg = ropResult.getReg();
610 if (isBelowThresholdRegister(ropReg)) {
614 insn.changeResultReg(nextSsaReg);
615 addMapping(ropReg, insn.getResult());
618 ssaRegToRopReg.add(ropReg);
625 * Updates the phi insns in successor blocks with operands based
626 * on the current mapping of the rop register the phis represent.
628 private void updateSuccessorPhis() {
629 PhiInsn.Visitor visitor = new PhiInsn.Visitor() {
630 public void visitPhiInsn (PhiInsn insn) {
633 ropReg = insn.getRopResultReg();
634 if (isBelowThresholdRegister(ropReg)) {
639 * Never add a version 0 register as a phi
640 * operand. Version 0 registers represent the
641 * initial register state, and thus are never
642 * significant. Furthermore, the register liveness
643 * algorithm doesn't properly count them as "live
644 * in" at the beginning of the method.
647 RegisterSpec stackTop = currentMapping[ropReg];
648 if (!isVersionZeroRegister(stackTop.getReg())) {
649 insn.addPhiOperand(stackTop, block);
654 BitSet successors = block.getSuccessors();
655 for (int i = successors.nextSetBit(0); i >= 0;
656 i = successors.nextSetBit(i + 1)) {
657 SsaBasicBlock successor = ssaMeth.getBlocks().get(i);
658 successor.forEachPhiInsn(visitor);