• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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