1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.dx.ssa;
18
19import com.android.dx.rop.code.LocalItem;
20import com.android.dx.rop.code.PlainInsn;
21import com.android.dx.rop.code.RegisterSpec;
22import com.android.dx.rop.code.RegisterSpecList;
23import com.android.dx.rop.code.Rops;
24import com.android.dx.rop.code.SourcePosition;
25import com.android.dx.rop.type.Type;
26import com.android.dx.util.IntList;
27import java.util.ArrayList;
28import java.util.BitSet;
29import java.util.HashMap;
30import 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 */
61public 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     */
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    */
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    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     */
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     */
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     */
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     */
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     */
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     */
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         */
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 {
326            public RenamingMapper() {
327                // This space intentionally left blank.
328            }
329
330            /** {@inheritDoc} */
331            @Override
332            public int getNewRegisterCount() {
333                return nextSsaReg;
334            }
335
336            /** {@inheritDoc} */
337            @Override
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         */
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         */
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         */
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         */
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         */
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         */
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         */
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