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.Insn;
20import com.android.dx.rop.code.LocalItem;
21import com.android.dx.rop.code.RegOps;
22import com.android.dx.rop.code.RegisterSpec;
23import com.android.dx.rop.code.RegisterSpecList;
24import com.android.dx.rop.code.Rop;
25
26/**
27 * A "normal" (non-phi) instruction in SSA form. Always wraps a rop insn.
28 */
29public final class NormalSsaInsn extends SsaInsn implements Cloneable {
30    /** {@code non-null;} rop insn that we're wrapping */
31    private Insn insn;
32
33    /**
34     * Creates an instance.
35     *
36     * @param insn Rop insn to wrap
37     * @param block block that contains this insn
38     */
39    NormalSsaInsn(final Insn insn, final SsaBasicBlock block) {
40        super(insn.getResult(), block);
41        this.insn = insn;
42    }
43
44    /** {@inheritDoc} */
45    @Override
46    public final void mapSourceRegisters(RegisterMapper mapper) {
47        RegisterSpecList oldSources = insn.getSources();
48        RegisterSpecList newSources = mapper.map(oldSources);
49
50        if (newSources != oldSources) {
51            insn = insn.withNewRegisters(getResult(), newSources);
52            getBlock().getParent().onSourcesChanged(this, oldSources);
53        }
54    }
55
56    /**
57     * Changes one of the insn's sources. New source should be of same type
58     * and category.
59     *
60     * @param index {@code >=0;} index of source to change
61     * @param newSpec spec for new source
62     */
63    public final void changeOneSource(int index, RegisterSpec newSpec) {
64        RegisterSpecList origSources = insn.getSources();
65        int sz = origSources.size();
66        RegisterSpecList newSources = new RegisterSpecList(sz);
67
68        for (int i = 0; i < sz; i++) {
69            newSources.set(i, i == index ? newSpec : origSources.get(i));
70        }
71
72        newSources.setImmutable();
73
74        RegisterSpec origSpec = origSources.get(index);
75        if (origSpec.getReg() != newSpec.getReg()) {
76            /*
77             * If the register remains unchanged, we're only changing
78             * the type or local var name so don't update use list
79             */
80            getBlock().getParent().onSourceChanged(this, origSpec, newSpec);
81        }
82
83        insn = insn.withNewRegisters(getResult(), newSources);
84    }
85
86    /**
87     * Changes the source list of the insn. New source list should be the
88     * same size and consist of sources of identical types.
89     *
90     * @param newSources non-null new sources list.
91     */
92    public final void setNewSources (RegisterSpecList newSources) {
93        RegisterSpecList origSources = insn.getSources();
94
95        if (origSources.size() != newSources.size()) {
96            throw new RuntimeException("Sources counts don't match");
97        }
98
99        insn = insn.withNewRegisters(getResult(), newSources);
100    }
101
102    /** {@inheritDoc} */
103    @Override
104    public NormalSsaInsn clone() {
105        return (NormalSsaInsn) super.clone();
106    }
107
108    /**
109     * Like rop.Insn.getSources().
110     *
111     * @return {@code null-ok;} sources list
112     */
113    @Override
114    public RegisterSpecList getSources() {
115        return insn.getSources();
116    }
117
118    /** {@inheritDoc} */
119    public String toHuman() {
120        return toRopInsn().toHuman();
121    }
122
123    /** {@inheritDoc} */
124    @Override
125    public Insn toRopInsn() {
126        return insn.withNewRegisters(getResult(), insn.getSources());
127    }
128
129    /**
130     * @return the Rop opcode for this insn
131     */
132    @Override
133    public Rop getOpcode() {
134        return insn.getOpcode();
135    }
136
137    /** {@inheritDoc} */
138    @Override
139    public Insn getOriginalRopInsn() {
140        return insn;
141    }
142
143    /** {@inheritDoc} */
144    @Override
145    public RegisterSpec getLocalAssignment() {
146        RegisterSpec assignment;
147
148        if (insn.getOpcode().getOpcode() == RegOps.MARK_LOCAL) {
149            assignment = insn.getSources().get(0);
150        } else {
151            assignment = getResult();
152        }
153
154        if (assignment == null) {
155            return null;
156        }
157
158        LocalItem local = assignment.getLocalItem();
159
160        if (local == null) {
161            return null;
162        }
163
164        return assignment;
165    }
166
167    /**
168     * Upgrades this insn to a version that represents the constant source
169     * literally. If the upgrade is not possible, this does nothing.
170     *
171     * @see Insn#withSourceLiteral
172     */
173    public void upgradeToLiteral() {
174        RegisterSpecList oldSources = insn.getSources();
175
176        insn = insn.withSourceLiteral();
177        getBlock().getParent().onSourcesChanged(this, oldSources);
178    }
179
180    /**
181     * @return true if this is a move (but not a move-operand) instruction
182     */
183    @Override
184    public boolean isNormalMoveInsn() {
185        return insn.getOpcode().getOpcode() == RegOps.MOVE;
186    }
187
188    /** {@inheritDoc} */
189    @Override
190    public boolean isMoveException() {
191        return insn.getOpcode().getOpcode() == RegOps.MOVE_EXCEPTION;
192    }
193
194    /** {@inheritDoc} */
195    @Override
196    public boolean canThrow() {
197        return insn.canThrow();
198    }
199
200    /** {@inheritDoc} */
201    @Override
202    public void accept(Visitor v) {
203        if (isNormalMoveInsn()) {
204            v.visitMoveInsn(this);
205        } else {
206            v.visitNonMoveInsn(this);
207        }
208    }
209
210    /** {@inheritDoc} */
211    @Override
212    public  boolean isPhiOrMove() {
213        return isNormalMoveInsn();
214    }
215
216    /**
217     * {@inheritDoc}
218     *
219     * TODO: Increase the scope of this.
220     */
221    @Override
222    public boolean hasSideEffect() {
223        Rop opcode = getOpcode();
224
225        if (opcode.getBranchingness() != Rop.BRANCH_NONE) {
226            return true;
227        }
228
229        boolean hasLocalSideEffect
230            = Optimizer.getPreserveLocals() && getLocalAssignment() != null;
231
232        switch (opcode.getOpcode()) {
233            case RegOps.MOVE_RESULT:
234            case RegOps.MOVE:
235            case RegOps.CONST:
236                return hasLocalSideEffect;
237            default:
238                return true;
239        }
240    }
241}
242