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.*;
20import com.android.dx.util.ToHuman;
21
22/**
23 * An instruction in SSA form
24 */
25public abstract class SsaInsn implements ToHuman, Cloneable {
26    /** {@code non-null;} the block that contains this instance */
27    private final SsaBasicBlock block;
28
29    /** {@code null-ok;} result register */
30    private RegisterSpec result;
31
32    /**
33     * Constructs an instance.
34     *
35     * @param result {@code null-ok;} initial result register. May be changed.
36     * @param block {@code non-null;} block containing this insn. Can
37     * never change.
38     */
39    protected SsaInsn(RegisterSpec result, SsaBasicBlock block) {
40        if (block == null) {
41            throw new NullPointerException("block == null");
42        }
43
44        this.block = block;
45        this.result = result;
46    }
47
48    /**
49     * Makes a new SSA insn form a rop insn.
50     *
51     * @param insn {@code non-null;} rop insn
52     * @param block {@code non-null;} owning block
53     * @return {@code non-null;} an appropriately constructed instance
54     */
55    public static SsaInsn makeFromRop(Insn insn, SsaBasicBlock block) {
56        return new NormalSsaInsn(insn, block);
57    }
58
59    /** {@inheritDoc} */
60    @Override
61    public SsaInsn clone() {
62        try {
63            return (SsaInsn)super.clone();
64        } catch (CloneNotSupportedException ex) {
65            throw new RuntimeException ("unexpected", ex);
66        }
67    }
68
69    /**
70     * Like {@link com.android.dx.rop.code.Insn getResult()}.
71     *
72     * @return result register
73     */
74    public RegisterSpec getResult() {
75        return result;
76    }
77
78    /**
79     * Set the result register.
80     *
81     * @param result {@code non-null;} the new result register
82     */
83    protected void setResult(RegisterSpec result) {
84        if (result == null) {
85            throw new NullPointerException("result == null");
86        }
87
88        this.result = result;
89    }
90
91    /**
92     * Like {@link com.android.dx.rop.code.Insn getSources()}.
93     *
94     * @return {@code non-null;} sources list
95     */
96    abstract public RegisterSpecList getSources();
97
98    /**
99     * Gets the block to which this insn instance belongs.
100     *
101     * @return owning block
102     */
103    public SsaBasicBlock getBlock() {
104        return block;
105    }
106
107    /**
108     * Returns whether or not the specified reg is the result reg.
109     *
110     * @param reg register to test
111     * @return true if there is a result and it is stored in the specified
112     * register
113     */
114    public boolean isResultReg(int reg) {
115        return result != null && result.getReg() == reg;
116    }
117
118
119    /**
120     * Changes the result register if this insn has a result. This is used
121     * during renaming.
122     *
123     * @param reg new result register
124     */
125    public void changeResultReg(int reg) {
126        if (result != null) {
127            result = result.withReg(reg);
128        }
129    }
130
131    /**
132     * Sets the local association for the result of this insn. This is
133     * sometimes updated during the SsaRenamer process.
134     *
135     * @param local {@code null-ok;} new debug/local variable info
136     */
137    public final void setResultLocal(LocalItem local) {
138        LocalItem oldItem = result.getLocalItem();
139
140        if (local != oldItem && (local == null
141                || !local.equals(result.getLocalItem()))) {
142            result = RegisterSpec.makeLocalOptional(
143                    result.getReg(), result.getType(), local);
144        }
145    }
146
147    /**
148     * Map registers after register allocation.
149     *
150     * @param mapper {@code non-null;} mapping from old to new registers
151     */
152    public final void mapRegisters(RegisterMapper mapper) {
153        RegisterSpec oldResult = result;
154
155        result = mapper.map(result);
156        block.getParent().updateOneDefinition(this, oldResult);
157        mapSourceRegisters(mapper);
158    }
159
160    /**
161     * Maps only source registers.
162     *
163     * @param mapper new mapping
164     */
165    abstract public void mapSourceRegisters(RegisterMapper mapper);
166
167    /**
168     * Returns the Rop opcode for this insn, or null if this is a phi insn.
169     *
170     * TODO: Move this up into NormalSsaInsn.
171     *
172     * @return {@code null-ok;} Rop opcode if there is one.
173     */
174    abstract public Rop getOpcode();
175
176    /**
177     * Returns the original Rop insn for this insn, or null if this is
178     * a phi insn.
179     *
180     * TODO: Move this up into NormalSsaInsn.
181     *
182     * @return {@code null-ok;} Rop insn if there is one.
183     */
184    abstract public Insn getOriginalRopInsn();
185
186    /**
187     * Gets the spec of a local variable assignment that occurs at this
188     * instruction, or null if no local variable assignment occurs. This
189     * may be the result register, or for {@code mark-local} insns
190     * it may be the source.
191     *
192     * @see com.android.dx.rop.code.Insn#getLocalAssignment()
193     *
194     * @return {@code null-ok;} a local-associated register spec or null
195     */
196    public RegisterSpec getLocalAssignment() {
197        if (result != null && result.getLocalItem() != null) {
198            return result;
199        }
200
201        return null;
202    }
203
204    /**
205     * Indicates whether the specified register is amongst the registers
206     * used as sources for this instruction.
207     *
208     * @param reg the register in question
209     * @return true if the reg is a source
210     */
211    public boolean isRegASource(int reg) {
212        return null != getSources().specForRegister(reg);
213    }
214
215    /**
216     * Transform back to ROP form.
217     *
218     * TODO: Move this up into NormalSsaInsn.
219     *
220     * @return {@code non-null;} a ROP representation of this instruction, with
221     * updated registers.
222     */
223    public abstract Insn toRopInsn();
224
225    /**
226     * @return true if this is a PhiInsn or a normal move insn
227     */
228    public abstract boolean isPhiOrMove();
229
230    /**
231     * Returns true if this insn is considered to have a side effect beyond
232     * that of assigning to the result reg.
233     *
234     * @return true if this insn is considered to have a side effect beyond
235     * that of assigning to the result reg.
236     */
237    public abstract boolean hasSideEffect();
238
239    /**
240     * @return true if this is a move (but not a move-operand or
241     * move-exception) instruction
242     */
243    public boolean isNormalMoveInsn() {
244        return false;
245    }
246
247    /**
248     * @return true if this is a move-exception instruction.
249     * These instructions must immediately follow a preceeding invoke*
250     */
251    public boolean isMoveException() {
252        return false;
253    }
254
255    /**
256     * @return true if this instruction can throw.
257     */
258    abstract public boolean canThrow();
259
260    /**
261     * Accepts a visitor.
262     *
263     * @param v {@code non-null} the visitor
264     */
265    public abstract void accept(Visitor v);
266
267    /**
268     * Visitor interface for this class.
269     */
270    public static interface Visitor {
271        /**
272         * Any non-phi move instruction
273         * @param insn {@code non-null;} the instruction to visit
274         */
275        public void visitMoveInsn(NormalSsaInsn insn);
276
277        /**
278         * Any phi insn
279         * @param insn {@code non-null;} the instruction to visit
280         */
281        public void visitPhiInsn(PhiInsn insn);
282
283        /**
284         * Any insn that isn't a move or a phi (which is also a move).
285         * @param insn {@code non-null;} the instruction to visit
286         */
287        public void visitNonMoveInsn(NormalSsaInsn insn);
288    }
289}
290