blob: cfef40083f6952af1afaa16a7f3f7421ceec632c [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.*;
/**
* A "normal" (non-phi) instruction in SSA form. Always wraps a rop insn.
*/
public final class NormalSsaInsn extends SsaInsn implements Cloneable {
/** {@code non-null;} rop insn that we're wrapping */
private Insn insn;
/**
* Creates an instance.
*
* @param insn Rop insn to wrap
* @param block block that contains this insn
*/
NormalSsaInsn(final Insn insn, final SsaBasicBlock block) {
super(insn.getResult(), block);
this.insn = insn;
}
/** {@inheritDoc} */
@Override
public final void mapSourceRegisters(RegisterMapper mapper) {
RegisterSpecList oldSources = insn.getSources();
RegisterSpecList newSources = mapper.map(oldSources);
if (newSources != oldSources) {
insn = insn.withNewRegisters(getResult(), newSources);
getBlock().getParent().onSourcesChanged(this, oldSources);
}
}
/**
* Changes one of the insn's sources. New source should be of same type
* and category.
*
* @param index {@code >=0;} index of source to change
* @param newSpec spec for new source
*/
public final void changeOneSource(int index, RegisterSpec newSpec) {
RegisterSpecList origSources = insn.getSources();
int sz = origSources.size();
RegisterSpecList newSources = new RegisterSpecList(sz);
for (int i = 0; i < sz; i++) {
newSources.set(i, i == index ? newSpec : origSources.get(i));
}
newSources.setImmutable();
RegisterSpec origSpec = origSources.get(index);
if (origSpec.getReg() != newSpec.getReg()) {
/*
* If the register remains unchanged, we're only changing
* the type or local var name so don't update use list
*/
getBlock().getParent().onSourceChanged(this, origSpec, newSpec);
}
insn = insn.withNewRegisters(getResult(), newSources);
}
/**
* Changes the source list of the insn. New source list should be the
* same size and consist of sources of identical types.
*
* @param newSources non-null new sources list.
*/
public final void setNewSources (RegisterSpecList newSources) {
RegisterSpecList origSources = insn.getSources();
if (origSources.size() != newSources.size()) {
throw new RuntimeException("Sources counts don't match");
}
insn = insn.withNewRegisters(getResult(), newSources);
}
/** {@inheritDoc} */
@Override
public NormalSsaInsn clone() {
return (NormalSsaInsn) super.clone();
}
/**
* Like rop.Insn.getSources().
*
* @return {@code null-ok;} sources list
*/
@Override
public RegisterSpecList getSources() {
return insn.getSources();
}
/** {@inheritDoc} */
public String toHuman() {
return toRopInsn().toHuman();
}
/** {@inheritDoc} */
@Override
public Insn toRopInsn() {
return insn.withNewRegisters(getResult(), insn.getSources());
}
/**
* @return the Rop opcode for this insn
*/
@Override
public Rop getOpcode() {
return insn.getOpcode();
}
/** {@inheritDoc} */
@Override
public Insn getOriginalRopInsn() {
return insn;
}
/** {@inheritDoc} */
@Override
public RegisterSpec getLocalAssignment() {
RegisterSpec assignment;
if (insn.getOpcode().getOpcode() == RegOps.MARK_LOCAL) {
assignment = insn.getSources().get(0);
} else {
assignment = getResult();
}
if (assignment == null) {
return null;
}
LocalItem local = assignment.getLocalItem();
if (local == null) {
return null;
}
return assignment;
}
/**
* Upgrades this insn to a version that represents the constant source
* literally. If the upgrade is not possible, this does nothing.
*
* @see Insn#withSourceLiteral
*/
public void upgradeToLiteral() {
RegisterSpecList oldSources = insn.getSources();
insn = insn.withSourceLiteral();
getBlock().getParent().onSourcesChanged(this, oldSources);
}
/**
* @return true if this is a move (but not a move-operand) instruction
*/
@Override
public boolean isNormalMoveInsn() {
return insn.getOpcode().getOpcode() == RegOps.MOVE;
}
/** {@inheritDoc} */
@Override
public boolean isMoveException() {
return insn.getOpcode().getOpcode() == RegOps.MOVE_EXCEPTION;
}
/** {@inheritDoc} */
@Override
public boolean canThrow() {
return insn.canThrow();
}
/** {@inheritDoc} */
@Override
public void accept(Visitor v) {
if (isNormalMoveInsn()) {
v.visitMoveInsn(this);
} else {
v.visitNonMoveInsn(this);
}
}
/** {@inheritDoc} */
@Override
public boolean isPhiOrMove() {
return isNormalMoveInsn();
}
/**
* {@inheritDoc}
*
* TODO: Increase the scope of this.
*/
@Override
public boolean hasSideEffect() {
Rop opcode = getOpcode();
if (opcode.getBranchingness() != Rop.BRANCH_NONE) {
return true;
}
boolean hasLocalSideEffect
= Optimizer.getPreserveLocals() && getLocalAssignment() != null;
switch (opcode.getOpcode()) {
case RegOps.MOVE_RESULT:
case RegOps.MOVE:
case RegOps.CONST:
return hasLocalSideEffect;
default:
return true;
}
}
}