1/*
2 * Copyright (C) 2008 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.RegisterSpec;
20import com.android.dx.rop.code.RegOps;
21import com.android.dx.rop.code.CstInsn;
22import com.android.dx.rop.code.LocalItem;
23import com.android.dx.rop.cst.CstInteger;
24
25import java.util.HashSet;
26import java.util.ArrayList;
27import java.util.List;
28
29/**
30 * Combine identical move-param insns, which may result from Ropper's
31 * handling of synchronized methods.
32 */
33public class MoveParamCombiner {
34
35    /** method to process */
36    private final SsaMethod ssaMeth;
37
38    /**
39     * Processes a method with this optimization step.
40     *
41     * @param ssaMethod method to process
42     */
43    public static void process(SsaMethod ssaMethod) {
44        new MoveParamCombiner(ssaMethod).run();
45    }
46
47    private MoveParamCombiner(SsaMethod ssaMeth) {
48        this.ssaMeth = ssaMeth;
49    }
50
51    /**
52     * Runs this optimization step.
53     */
54    private void run() {
55        // This will contain the definition specs for each parameter
56        final RegisterSpec[] paramSpecs
57                = new RegisterSpec[ssaMeth.getParamWidth()];
58
59        // Insns to delete when all done
60        final HashSet<SsaInsn> deletedInsns = new HashSet();
61
62        ssaMeth.forEachInsn(new SsaInsn.Visitor() {
63            public void visitMoveInsn (NormalSsaInsn insn) {
64            }
65            public void visitPhiInsn (PhiInsn phi) {
66            }
67            public void visitNonMoveInsn (NormalSsaInsn insn) {
68                if (insn.getOpcode().getOpcode() != RegOps.MOVE_PARAM) {
69                    return;
70                }
71
72                int param = getParamIndex(insn);
73
74                if (paramSpecs[param] == null) {
75                    paramSpecs[param] = insn.getResult();
76                } else {
77                    final RegisterSpec specA = paramSpecs[param];
78                    final RegisterSpec specB = insn.getResult();
79                    LocalItem localA = specA.getLocalItem();
80                    LocalItem localB = specB.getLocalItem();
81                    LocalItem newLocal;
82
83                    /*
84                     * Is there local information to preserve?
85                     */
86
87                    if (localA == null) {
88                        newLocal = localB;
89                    } else if (localB == null) {
90                        newLocal = localA;
91                    } else if (localA.equals(localB)) {
92                        newLocal = localA;
93                    } else {
94                        /*
95                         * Oddly, these two identical move-params have distinct
96                         * debug info. We'll just keep them distinct.
97                         */
98                        return;
99                    }
100
101                    ssaMeth.getDefinitionForRegister(specA.getReg())
102                            .setResultLocal(newLocal);
103
104                    /*
105                     * Map all uses of specB to specA
106                     */
107
108                    RegisterMapper mapper = new RegisterMapper() {
109                        /** @inheritDoc */
110                        public int getNewRegisterCount() {
111                            return ssaMeth.getRegCount();
112                        }
113
114                        /** @inheritDoc */
115                        public RegisterSpec map(RegisterSpec registerSpec) {
116                            if (registerSpec.getReg() == specB.getReg()) {
117                                return specA;
118                            }
119
120                            return registerSpec;
121                        }
122                    };
123
124                    List<SsaInsn> uses
125                            = ssaMeth.getUseListForRegister(specB.getReg());
126
127                    // Use list is modified by mapSourceRegisters
128                    for (int i = uses.size() - 1; i >= 0; i--) {
129                        SsaInsn use = uses.get(i);
130                        use.mapSourceRegisters(mapper);
131                    }
132
133                    deletedInsns.add(insn);
134                }
135
136            }
137        });
138
139        ssaMeth.deleteInsns(deletedInsns);
140    }
141
142    /**
143     * Returns the parameter index associated with a move-param insn. Does
144     * not verify that the insn is a move-param insn.
145     *
146     * @param insn {@code non-null;} a move-param insn
147     * @return {@code >=0;} parameter index
148     */
149    private int getParamIndex(NormalSsaInsn insn) {
150        CstInsn cstInsn = (CstInsn)(insn.getOriginalRopInsn());
151
152        int param = ((CstInteger)cstInsn.getConstant()).getValue();
153        return param;
154    }
155
156}
157