• 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      */
161     @Override
run()162     public void run() {
163         // Rename each block in dom-tree DFS order.
164         ssaMeth.forEachBlockDepthFirstDom(new SsaBasicBlock.Visitor() {
165             @Override
166             public void visitBlock (SsaBasicBlock block,
167                     SsaBasicBlock unused) {
168                 new BlockRenamer(block).process();
169             }
170         });
171 
172         ssaMeth.setNewRegCount(nextSsaReg);
173         ssaMeth.onInsnsChanged();
174 
175         if (DEBUG) {
176             System.out.println("SSA\tRop");
177             /*
178              * We're going to compute the version of the rop register
179              * by keeping a running total of how many times the rop
180              * register has been mapped.
181              */
182             int[] versions = new int[ropRegCount];
183 
184             int sz = ssaRegToRopReg.size();
185             for (int i = 0; i < sz; i++) {
186                 int ropReg = ssaRegToRopReg.get(i);
187                 System.out.println(i + "\t" + ropReg + "["
188                         + versions[ropReg] + "]");
189                 versions[ropReg]++;
190             }
191         }
192     }
193 
194     /**
195      * Duplicates a RegisterSpec array.
196      *
197      * @param orig {@code non-null;} array to duplicate
198      * @return {@code non-null;} new instance
199      */
dupArray(RegisterSpec[] orig)200     private static  RegisterSpec[] dupArray(RegisterSpec[] orig) {
201         RegisterSpec[] copy = new RegisterSpec[orig.length];
202 
203         System.arraycopy(orig, 0, copy, 0, orig.length);
204 
205         return copy;
206     }
207 
208     /**
209      * Gets a local variable item for a specified register.
210      *
211      * @param ssaReg register in SSA name space
212      * @return {@code null-ok;} Local variable name or null if none
213      */
getLocalForNewReg(int ssaReg)214     private LocalItem getLocalForNewReg(int ssaReg) {
215         if (ssaReg < ssaRegToLocalItems.size()) {
216             return ssaRegToLocalItems.get(ssaReg);
217         } else {
218             return null;
219         }
220     }
221 
222     /**
223      * Records a debug (local variable) name for a specified register.
224      *
225      * @param ssaReg non-null named register spec in SSA name space
226      */
setNameForSsaReg(RegisterSpec ssaReg)227     private void setNameForSsaReg(RegisterSpec ssaReg) {
228         int reg = ssaReg.getReg();
229         LocalItem local = ssaReg.getLocalItem();
230 
231         ssaRegToLocalItems.ensureCapacity(reg + 1);
232         while (ssaRegToLocalItems.size() <= reg) {
233             ssaRegToLocalItems.add(null);
234         }
235 
236         ssaRegToLocalItems.set(reg, local);
237     }
238 
239     /**
240      * Returns true if this SSA register is below the specified threshold.
241      * Used when most code is already in SSA form, and renaming is needed only
242      * for registers above a certain threshold.
243      *
244      * @param ssaReg the SSA register in question
245      * @return {@code true} if its register number is below the threshold
246      */
isBelowThresholdRegister(int ssaReg)247     private boolean isBelowThresholdRegister(int ssaReg) {
248         return ssaReg < threshold;
249     }
250 
251     /**
252      * Returns true if this SSA register is a "version 0"
253      * register. All version 0 registers are assigned the first N register
254      * numbers, where N is the count of original rop registers.
255      *
256      * @param ssaReg the SSA register in question
257      * @return true if it is a version 0 register.
258      */
isVersionZeroRegister(int ssaReg)259     private boolean isVersionZeroRegister(int ssaReg) {
260         return ssaReg < ropRegCount;
261     }
262 
263     /**
264      * Returns true if a and b are equal or are both null.
265      *
266      * @param a null-ok
267      * @param b null-ok
268      * @return Returns true if a and b are equal or are both null
269      */
equalsHandlesNulls(Object a, Object b)270     private static boolean equalsHandlesNulls(Object a, Object b) {
271         return a == b ||  (a != null && a.equals(b));
272     }
273 
274     /**
275      * Processes all insns in a block and renames their registers
276      * as appropriate.
277      */
278     private class BlockRenamer implements SsaInsn.Visitor{
279         /** {@code non-null;} block we're processing. */
280         private final SsaBasicBlock block;
281 
282         /**
283          * {@code non-null;} indexed by old register name. The current
284          * top of the version stack as seen by this block. It's
285          * initialized from the ending state of its dom parent,
286          * updated as the block's instructions are processed, and then
287          * copied to each one of its dom children.
288          */
289         private final RegisterSpec[] currentMapping;
290 
291         /**
292          * contains the set of moves we need to keep to preserve local
293          * var info. All other moves will be deleted.
294          */
295         private final HashSet<SsaInsn> movesToKeep;
296 
297         /**
298          * maps the set of insns to replace after renaming is finished
299          * on the block.
300          */
301         private final HashMap<SsaInsn, SsaInsn> insnsToReplace;
302 
303         private final RenamingMapper mapper;
304 
305         /**
306          * Constructs a block renamer instance. Call {@code process}
307          * to process.
308          *
309          * @param block {@code non-null;} block to process
310          */
BlockRenamer(final SsaBasicBlock block)311         BlockRenamer(final SsaBasicBlock block) {
312             this.block = block;
313             currentMapping = startsForBlocks[block.getIndex()];
314             movesToKeep = new HashSet<SsaInsn>();
315             insnsToReplace = new HashMap<SsaInsn, SsaInsn>();
316             mapper =  new RenamingMapper();
317 
318             // We don't need our own start state anymore
319             startsForBlocks[block.getIndex()] = null;
320         }
321 
322         /**
323          * Provides a register mapping between the old register space
324          * and the current renaming mapping. The mapping is updated
325          * as the current block's instructions are processed.
326          */
327         private class RenamingMapper extends RegisterMapper {
RenamingMapper()328             public RenamingMapper() {
329                 // This space intentionally left blank.
330             }
331 
332             /** {@inheritDoc} */
333             @Override
getNewRegisterCount()334             public int getNewRegisterCount() {
335                 return nextSsaReg;
336             }
337 
338             /** {@inheritDoc} */
339             @Override
map(RegisterSpec registerSpec)340             public RegisterSpec map(RegisterSpec registerSpec) {
341                 if (registerSpec == null) return null;
342 
343                 int reg = registerSpec.getReg();
344 
345                 // For debugging: assert that the mapped types are compatible.
346                 if (DEBUG) {
347                     RegisterSpec newVersion = currentMapping[reg];
348                     if (newVersion.getBasicType() != Type.BT_VOID
349                             && registerSpec.getBasicFrameType()
350                                 != newVersion.getBasicFrameType()) {
351 
352                         throw new RuntimeException(
353                                 "mapping registers of incompatible types! "
354                                 + registerSpec
355                                 + " " + currentMapping[reg]);
356                     }
357                 }
358 
359                 return registerSpec.withReg(currentMapping[reg].getReg());
360             }
361         }
362 
363         /**
364          * Renames all the variables in this block and inserts appriopriate
365          * phis in successor blocks.
366          */
process()367         public void process() {
368             /*
369              * From Appel:
370              *
371              * Rename(n) =
372              *   for each statement S in block n   // 'statement' in 'block'
373              */
374 
375             block.forEachInsn(this);
376 
377             updateSuccessorPhis();
378 
379             // Delete all move insns in this block.
380             ArrayList<SsaInsn> insns = block.getInsns();
381             int szInsns = insns.size();
382 
383             for (int i = szInsns - 1; i >= 0 ; i--) {
384                 SsaInsn insn = insns.get(i);
385                 SsaInsn replaceInsn;
386 
387                 replaceInsn = insnsToReplace.get(insn);
388 
389                 if (replaceInsn != null) {
390                     insns.set(i, replaceInsn);
391                 } else if (insn.isNormalMoveInsn()
392                         && !movesToKeep.contains(insn)) {
393                     insns.remove(i);
394                 }
395             }
396 
397             // Store the start states for our dom children.
398             boolean first = true;
399             for (SsaBasicBlock child : block.getDomChildren()) {
400                 if (child != block) {
401                     // Don't bother duplicating the array for the first child.
402                     RegisterSpec[] childStart = first ? currentMapping
403                         : dupArray(currentMapping);
404 
405                     startsForBlocks[child.getIndex()] = childStart;
406                     first = false;
407                 }
408             }
409 
410             // currentMapping is owned by a child now.
411         }
412 
413         /**
414          * Enforces a few contraints when a register mapping is added.
415          *
416          * <ol>
417          * <li> Ensures that all new SSA registers specs in the mapping
418          * table with the same register number are identical. In effect, once
419          * an SSA register spec has received or lost a local variable name,
420          * then every old-namespace register that maps to it should gain or
421          * lose its local variable name as well.
422          * <li> Records the local name associated with the
423          * register so that a register is never associated with more than one
424          * local.
425          * <li> ensures that only one SSA register
426          * at a time is considered to be associated with a local variable. When
427          * {@code currentMapping} is updated and the newly added element
428          * is named, strip that name from any other SSA registers.
429          * </ol>
430          *
431          * @param ropReg {@code >= 0;} rop register number
432          * @param ssaReg {@code non-null;} an SSA register that has just
433          * been added to {@code currentMapping}
434          */
addMapping(int ropReg, RegisterSpec ssaReg)435         private void addMapping(int ropReg, RegisterSpec ssaReg) {
436             int ssaRegNum = ssaReg.getReg();
437             LocalItem ssaRegLocal = ssaReg.getLocalItem();
438 
439             currentMapping[ropReg] = ssaReg;
440 
441             /*
442              * Ensure all SSA register specs with the same reg are identical.
443              */
444             for (int i = currentMapping.length - 1; i >= 0; i--) {
445                 RegisterSpec cur = currentMapping[i];
446 
447                 if (ssaRegNum == cur.getReg()) {
448                     currentMapping[i] = ssaReg;
449                 }
450             }
451 
452             // All further steps are for registers with local information.
453             if (ssaRegLocal == null) {
454                 return;
455             }
456 
457             // Record that this SSA reg has been associated with a local.
458             setNameForSsaReg(ssaReg);
459 
460             // Ensure that no other SSA regs are associated with this local.
461             for (int i = currentMapping.length - 1; i >= 0; i--) {
462                 RegisterSpec cur = currentMapping[i];
463 
464                 if (ssaRegNum != cur.getReg()
465                         && ssaRegLocal.equals(cur.getLocalItem())) {
466                     currentMapping[i] = cur.withLocalItem(null);
467                 }
468             }
469         }
470 
471         /**
472          * {@inheritDoc}
473          *
474          * Phi insns have their result registers renamed.
475          */
476         @Override
visitPhiInsn(PhiInsn phi)477         public void visitPhiInsn(PhiInsn phi) {
478             /* don't process sources for phi's */
479             processResultReg(phi);
480         }
481 
482         /**
483          * {@inheritDoc}
484          *
485          * Move insns are treated as a simple mapping operation, and
486          * will later be removed unless they represent a local variable
487          * assignment. If they represent a local variable assignement, they
488          * are preserved.
489          */
490         @Override
visitMoveInsn(NormalSsaInsn insn)491         public void visitMoveInsn(NormalSsaInsn insn) {
492             /*
493              * For moves: copy propogate the move if we can, but don't
494              * if we need to preserve local variable info and the
495              * result has a different name than the source.
496              */
497 
498             RegisterSpec ropResult = insn.getResult();
499             int ropResultReg = ropResult.getReg();
500             int ropSourceReg = insn.getSources().get(0).getReg();
501 
502             insn.mapSourceRegisters(mapper);
503             int ssaSourceReg = insn.getSources().get(0).getReg();
504 
505             LocalItem sourceLocal
506                 = currentMapping[ropSourceReg].getLocalItem();
507             LocalItem resultLocal = ropResult.getLocalItem();
508 
509             /*
510              * A move from a register that's currently associated with a local
511              * to one that will not be associated with a local does not need
512              * to be preserved, but the local association should remain.
513              * Hence, we inherit the sourceLocal where the resultLocal is null.
514              */
515 
516             LocalItem newLocal
517                 = (resultLocal == null) ? sourceLocal : resultLocal;
518             LocalItem associatedLocal = getLocalForNewReg(ssaSourceReg);
519 
520             /*
521              * If we take the new local, will only one local have ever
522              * been associated with this SSA reg?
523              */
524             boolean onlyOneAssociatedLocal
525                     = associatedLocal == null || newLocal == null
526                     || newLocal.equals(associatedLocal);
527 
528             /*
529              * If we're going to copy-propogate, then the ssa register
530              * spec that's going to go into the mapping is made up of
531              * the source register number mapped from above, the type
532              * of the result, and the name either from the result (if
533              * specified) or inherited from the existing mapping.
534              *
535              * The move source has incomplete type information in null
536              * object cases, so the result type is used.
537              */
538             RegisterSpec ssaReg
539                     = RegisterSpec.makeLocalOptional(
540                         ssaSourceReg, ropResult.getType(), newLocal);
541 
542             if (!Optimizer.getPreserveLocals() || (onlyOneAssociatedLocal
543                     && equalsHandlesNulls(newLocal, sourceLocal)) &&
544                     threshold == 0) {
545                 /*
546                  * We don't have to keep this move to preserve local
547                  * information. Either the name is the same, or the result
548                  * register spec is unnamed.
549                  */
550 
551                 addMapping(ropResultReg, ssaReg);
552             } else if (onlyOneAssociatedLocal && sourceLocal == null &&
553                     threshold == 0) {
554                 /*
555                  * The register was previously unnamed. This means that a
556                  * local starts after it's first assignment in SSA form
557                  */
558 
559                 RegisterSpecList ssaSources = RegisterSpecList.make(
560                         RegisterSpec.make(ssaReg.getReg(),
561                                 ssaReg.getType(), newLocal));
562 
563                 SsaInsn newInsn
564                         = SsaInsn.makeFromRop(
565                             new PlainInsn(Rops.opMarkLocal(ssaReg),
566                             SourcePosition.NO_INFO, null, ssaSources),block);
567 
568                 insnsToReplace.put(insn, newInsn);
569 
570                 // Just map as above.
571                 addMapping(ropResultReg, ssaReg);
572             } else {
573                 /*
574                  * Do not copy-propogate, since the two registers have
575                  * two different local-variable names.
576                  */
577                 processResultReg(insn);
578 
579                 movesToKeep.add(insn);
580             }
581         }
582 
583         /**
584          * {@inheritDoc}
585          *
586          * All insns that are not move or phi insns have their source registers
587          * mapped ot the current mapping. Their result registers are then
588          * renamed to a new SSA register which is then added to the current
589          * register mapping.
590          */
591         @Override
visitNonMoveInsn(NormalSsaInsn insn)592         public void visitNonMoveInsn(NormalSsaInsn insn) {
593             /* for each use of some variable X in S */
594             insn.mapSourceRegisters(mapper);
595 
596             processResultReg(insn);
597         }
598 
599         /**
600          * Renames the result register of this insn and updates the
601          * current register mapping. Does nothing if this insn has no result.
602          * Applied to all non-move insns.
603          *
604          * @param insn insn to process.
605          */
processResultReg(SsaInsn insn)606         void processResultReg(SsaInsn insn) {
607             RegisterSpec ropResult = insn.getResult();
608 
609             if (ropResult == null) {
610                 return;
611             }
612 
613             int ropReg = ropResult.getReg();
614             if (isBelowThresholdRegister(ropReg)) {
615                 return;
616             }
617 
618             insn.changeResultReg(nextSsaReg);
619             addMapping(ropReg, insn.getResult());
620 
621             if (DEBUG) {
622                 ssaRegToRopReg.add(ropReg);
623             }
624 
625             nextSsaReg++;
626         }
627 
628         /**
629          * Updates the phi insns in successor blocks with operands based
630          * on the current mapping of the rop register the phis represent.
631          */
updateSuccessorPhis()632         private void updateSuccessorPhis() {
633             PhiInsn.Visitor visitor = new PhiInsn.Visitor() {
634                 @Override
635                 public void visitPhiInsn (PhiInsn insn) {
636                     int ropReg;
637 
638                     ropReg = insn.getRopResultReg();
639                     if (isBelowThresholdRegister(ropReg)) {
640                         return;
641                     }
642 
643                     /*
644                      * Never add a version 0 register as a phi
645                      * operand. Version 0 registers represent the
646                      * initial register state, and thus are never
647                      * significant. Furthermore, the register liveness
648                      * algorithm doesn't properly count them as "live
649                      * in" at the beginning of the method.
650                      */
651 
652                     RegisterSpec stackTop = currentMapping[ropReg];
653                     if (!isVersionZeroRegister(stackTop.getReg())) {
654                         insn.addPhiOperand(stackTop, block);
655                     }
656                 }
657             };
658 
659             BitSet successors = block.getSuccessors();
660             for (int i = successors.nextSetBit(0); i >= 0;
661                     i = successors.nextSetBit(i + 1)) {
662                 SsaBasicBlock successor = ssaMeth.getBlocks().get(i);
663                 successor.forEachPhiInsn(visitor);
664             }
665         }
666     }
667 }
668