blob: ca7a1a2f1c7543e5dd5c1b2a1e65dcca2a4931d0 [file] [log] [blame]
/*
* 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);
}
}