| /* |
| * Copyright (C) 2007 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.dx.ssa; |
| |
| import com.android.dx.rop.code.*; |
| import com.android.dx.util.ToHuman; |
| |
| /** |
| * An instruction in SSA form |
| */ |
| public abstract class SsaInsn implements ToHuman, Cloneable { |
| /** {@code non-null;} the block that contains this instance */ |
| private final SsaBasicBlock block; |
| |
| /** {@code null-ok;} result register */ |
| private RegisterSpec result; |
| |
| /** |
| * Constructs an instance. |
| * |
| * @param result {@code null-ok;} initial result register. May be changed. |
| * @param block {@code non-null;} block containing this insn. Can |
| * never change. |
| */ |
| protected SsaInsn(RegisterSpec result, SsaBasicBlock block) { |
| if (block == null) { |
| throw new NullPointerException("block == null"); |
| } |
| |
| this.block = block; |
| this.result = result; |
| } |
| |
| /** |
| * Makes a new SSA insn form a rop insn. |
| * |
| * @param insn {@code non-null;} rop insn |
| * @param block {@code non-null;} owning block |
| * @return {@code non-null;} an appropriately constructed instance |
| */ |
| public static SsaInsn makeFromRop(Insn insn, SsaBasicBlock block) { |
| return new NormalSsaInsn(insn, block); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public SsaInsn clone() { |
| try { |
| return (SsaInsn)super.clone(); |
| } catch (CloneNotSupportedException ex) { |
| throw new RuntimeException ("unexpected", ex); |
| } |
| } |
| |
| /** |
| * Like {@link com.android.dx.rop.code.Insn getResult()}. |
| * |
| * @return result register |
| */ |
| public RegisterSpec getResult() { |
| return result; |
| } |
| |
| /** |
| * Set the result register. |
| * |
| * @param result {@code non-null;} the new result register |
| */ |
| protected void setResult(RegisterSpec result) { |
| if (result == null) { |
| throw new NullPointerException("result == null"); |
| } |
| |
| this.result = result; |
| } |
| |
| /** |
| * Like {@link com.android.dx.rop.code.Insn getSources()}. |
| * |
| * @return {@code non-null;} sources list |
| */ |
| abstract public RegisterSpecList getSources(); |
| |
| /** |
| * Gets the block to which this insn instance belongs. |
| * |
| * @return owning block |
| */ |
| public SsaBasicBlock getBlock() { |
| return block; |
| } |
| |
| /** |
| * Returns whether or not the specified reg is the result reg. |
| * |
| * @param reg register to test |
| * @return true if there is a result and it is stored in the specified |
| * register |
| */ |
| public boolean isResultReg(int reg) { |
| return result != null && result.getReg() == reg; |
| } |
| |
| |
| /** |
| * Changes the result register if this insn has a result. This is used |
| * during renaming. |
| * |
| * @param reg new result register |
| */ |
| public void changeResultReg(int reg) { |
| if (result != null) { |
| result = result.withReg(reg); |
| } |
| } |
| |
| /** |
| * Sets the local association for the result of this insn. This is |
| * sometimes updated during the SsaRenamer process. |
| * |
| * @param local {@code null-ok;} new debug/local variable info |
| */ |
| public final void setResultLocal(LocalItem local) { |
| LocalItem oldItem = result.getLocalItem(); |
| |
| if (local != oldItem && (local == null |
| || !local.equals(result.getLocalItem()))) { |
| result = RegisterSpec.makeLocalOptional( |
| result.getReg(), result.getType(), local); |
| } |
| } |
| |
| /** |
| * Map registers after register allocation. |
| * |
| * @param mapper {@code non-null;} mapping from old to new registers |
| */ |
| public final void mapRegisters(RegisterMapper mapper) { |
| RegisterSpec oldResult = result; |
| |
| result = mapper.map(result); |
| block.getParent().updateOneDefinition(this, oldResult); |
| mapSourceRegisters(mapper); |
| } |
| |
| /** |
| * Maps only source registers. |
| * |
| * @param mapper new mapping |
| */ |
| abstract public void mapSourceRegisters(RegisterMapper mapper); |
| |
| /** |
| * Returns the Rop opcode for this insn, or null if this is a phi insn. |
| * |
| * TODO: Move this up into NormalSsaInsn. |
| * |
| * @return {@code null-ok;} Rop opcode if there is one. |
| */ |
| abstract public Rop getOpcode(); |
| |
| /** |
| * Returns the original Rop insn for this insn, or null if this is |
| * a phi insn. |
| * |
| * TODO: Move this up into NormalSsaInsn. |
| * |
| * @return {@code null-ok;} Rop insn if there is one. |
| */ |
| abstract public Insn getOriginalRopInsn(); |
| |
| /** |
| * Gets the spec of a local variable assignment that occurs at this |
| * instruction, or null if no local variable assignment occurs. This |
| * may be the result register, or for {@code mark-local} insns |
| * it may be the source. |
| * |
| * @see com.android.dx.rop.code.Insn#getLocalAssignment() |
| * |
| * @return {@code null-ok;} a local-associated register spec or null |
| */ |
| public RegisterSpec getLocalAssignment() { |
| if (result != null && result.getLocalItem() != null) { |
| return result; |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Indicates whether the specified register is amongst the registers |
| * used as sources for this instruction. |
| * |
| * @param reg the register in question |
| * @return true if the reg is a source |
| */ |
| public boolean isRegASource(int reg) { |
| return null != getSources().specForRegister(reg); |
| } |
| |
| /** |
| * Transform back to ROP form. |
| * |
| * TODO: Move this up into NormalSsaInsn. |
| * |
| * @return {@code non-null;} a ROP representation of this instruction, with |
| * updated registers. |
| */ |
| public abstract Insn toRopInsn(); |
| |
| /** |
| * @return true if this is a PhiInsn or a normal move insn |
| */ |
| public abstract boolean isPhiOrMove(); |
| |
| /** |
| * Returns true if this insn is considered to have a side effect beyond |
| * that of assigning to the result reg. |
| * |
| * @return true if this insn is considered to have a side effect beyond |
| * that of assigning to the result reg. |
| */ |
| public abstract boolean hasSideEffect(); |
| |
| /** |
| * @return true if this is a move (but not a move-operand or |
| * move-exception) instruction |
| */ |
| public boolean isNormalMoveInsn() { |
| return false; |
| } |
| |
| /** |
| * @return true if this is a move-exception instruction. |
| * These instructions must immediately follow a preceeding invoke* |
| */ |
| public boolean isMoveException() { |
| return false; |
| } |
| |
| /** |
| * @return true if this instruction can throw. |
| */ |
| abstract public boolean canThrow(); |
| |
| /** |
| * Accepts a visitor. |
| * |
| * @param v {@code non-null} the visitor |
| */ |
| public abstract void accept(Visitor v); |
| |
| /** |
| * Visitor interface for this class. |
| */ |
| public static interface Visitor { |
| /** |
| * Any non-phi move instruction |
| * @param insn {@code non-null;} the instruction to visit |
| */ |
| public void visitMoveInsn(NormalSsaInsn insn); |
| |
| /** |
| * Any phi insn |
| * @param insn {@code non-null;} the instruction to visit |
| */ |
| public void visitPhiInsn(PhiInsn insn); |
| |
| /** |
| * Any insn that isn't a move or a phi (which is also a move). |
| * @param insn {@code non-null;} the instruction to visit |
| */ |
| public void visitNonMoveInsn(NormalSsaInsn insn); |
| } |
| } |