org.snapscript.dx.ssa.PhiInsn Maven / Gradle / Ivy
/*
* 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 org.snapscript.dx.ssa;
import java.util.ArrayList;
import java.util.List;
import org.snapscript.dx.rop.code.Insn;
import org.snapscript.dx.rop.code.LocalItem;
import org.snapscript.dx.rop.code.RegisterSpec;
import org.snapscript.dx.rop.code.RegisterSpecList;
import org.snapscript.dx.rop.code.Rop;
import org.snapscript.dx.rop.code.SourcePosition;
import org.snapscript.dx.rop.type.Type;
import org.snapscript.dx.rop.type.TypeBearer;
import org.snapscript.dx.util.Hex;
/**
* A Phi instruction (magical post-control-flow-merge) instruction
* in SSA form. Will be converted to moves in predecessor blocks before
* conversion back to ROP form.
*/
public final class PhiInsn extends SsaInsn {
/**
* result register. The original result register of the phi insn
* is needed during the renaming process after the new result
* register has already been chosen.
*/
private final int ropResultReg;
/**
* {@code non-null;} operands of the instruction; built up by
* {@link #addPhiOperand}
*/
private final ArrayList operands = new ArrayList();
/** {@code null-ok;} source registers; constructed lazily */
private RegisterSpecList sources;
/**
* Constructs a new phi insn with no operands.
*
* @param resultReg the result reg for this phi insn
* @param block block containing this insn.
*/
public PhiInsn(RegisterSpec resultReg, SsaBasicBlock block) {
super(resultReg, block);
ropResultReg = resultReg.getReg();
}
/**
* Makes a phi insn with a void result type.
*
* @param resultReg the result register for this phi insn.
* @param block block containing this insn.
*/
public PhiInsn(final int resultReg, final SsaBasicBlock block) {
/*
* The result type here is bogus: The type depends on the
* operand and will be derived later.
*/
super(RegisterSpec.make(resultReg, Type.VOID), block);
ropResultReg = resultReg;
}
/** {@inheritDoc} */
@Override
public PhiInsn clone() {
throw new UnsupportedOperationException("can't clone phi");
}
/**
* Updates the TypeBearers of all the sources (phi operands) to be
* the current TypeBearer of the register-defining instruction's result.
* This is used during phi-type resolution.
*
* Note that local association of operands are preserved in this step.
*
* @param ssaMeth method that contains this insn
*/
public void updateSourcesToDefinitions(SsaMethod ssaMeth) {
for (Operand o : operands) {
RegisterSpec def
= ssaMeth.getDefinitionForRegister(
o.regSpec.getReg()).getResult();
o.regSpec = o.regSpec.withType(def.getType());
}
sources = null;
}
/**
* Changes the result type. Used during phi type resolution
*
* @param type {@code non-null;} new TypeBearer
* @param local {@code null-ok;} new local info, if available
*/
public void changeResultType(TypeBearer type, LocalItem local) {
setResult(RegisterSpec.makeLocalOptional(
getResult().getReg(), type, local));
}
/**
* Gets the original rop-form result reg. This is useful during renaming.
*
* @return the original rop-form result reg
*/
public int getRopResultReg() {
return ropResultReg;
}
/**
* Adds an operand to this phi instruction.
*
* @param registerSpec register spec, including type and reg of operand
* @param predBlock predecessor block to be associated with this operand
*/
public void addPhiOperand(RegisterSpec registerSpec,
SsaBasicBlock predBlock) {
operands.add(new Operand(registerSpec, predBlock.getIndex(),
predBlock.getRopLabel()));
// Un-cache sources, in case someone has already called getSources().
sources = null;
}
/**
* Removes all operand uses of a register from this phi instruction.
*
* @param registerSpec register spec, including type and reg of operand
*/
public void removePhiRegister(RegisterSpec registerSpec) {
ArrayList operandsToRemove = new ArrayList();
for (Operand o : operands) {
if (o.regSpec.getReg() == registerSpec.getReg()) {
operandsToRemove.add(o);
}
}
operands.removeAll(operandsToRemove);
// Un-cache sources, in case someone has already called getSources().
sources = null;
}
/**
* Gets the index of the pred block associated with the RegisterSpec
* at the particular getSources() index.
*
* @param sourcesIndex index of source in getSources()
* @return block index
*/
public int predBlockIndexForSourcesIndex(int sourcesIndex) {
return operands.get(sourcesIndex).blockIndex;
}
/**
* {@inheritDoc}
*
* Always returns null for {@code PhiInsn}s.
*/
@Override
public Rop getOpcode() {
return null;
}
/**
* {@inheritDoc}
*
* Always returns null for {@code PhiInsn}s.
*/
@Override
public Insn getOriginalRopInsn() {
return null;
}
/**
* {@inheritDoc}
*
* Always returns false for {@code PhiInsn}s.
*/
@Override
public boolean canThrow() {
return false;
}
/**
* Gets sources. Constructed lazily from phi operand data structures and
* then cached.
*
* @return {@code non-null;} sources list
*/
@Override
public RegisterSpecList getSources() {
if (sources != null) {
return sources;
}
if (operands.size() == 0) {
// How'd this happen? A phi insn with no operand?
return RegisterSpecList.EMPTY;
}
int szSources = operands.size();
sources = new RegisterSpecList(szSources);
for (int i = 0; i < szSources; i++) {
Operand o = operands.get(i);
sources.set(i, o.regSpec);
}
sources.setImmutable();
return sources;
}
/** {@inheritDoc} */
@Override
public boolean isRegASource(int reg) {
/*
* Avoid creating a sources list in case it has not already been
* created.
*/
for (Operand o : operands) {
if (o.regSpec.getReg() == reg) {
return true;
}
}
return false;
}
/**
* @return true if all operands use the same register
*/
public boolean areAllOperandsEqual() {
if (operands.size() == 0 ) {
// This should never happen.
return true;
}
int firstReg = operands.get(0).regSpec.getReg();
for (Operand o : operands) {
if (firstReg != o.regSpec.getReg()) {
return false;
}
}
return true;
}
/** {@inheritDoc} */
@Override
public final void mapSourceRegisters(RegisterMapper mapper) {
for (Operand o : operands) {
RegisterSpec old = o.regSpec;
o.regSpec = mapper.map(old);
if (old != o.regSpec) {
getBlock().getParent().onSourceChanged(this, old, o.regSpec);
}
}
sources = null;
}
/**
* Always throws an exeption, since a phi insn may not be
* converted back to rop form.
*
* @return always throws exception
*/
@Override
public Insn toRopInsn() {
throw new IllegalArgumentException(
"Cannot convert phi insns to rop form");
}
/**
* Returns the list of predecessor blocks associated with all operands
* that have {@code reg} as an operand register.
*
* @param reg register to look up
* @param ssaMeth method we're operating on
* @return list of predecessor blocks, empty if none
*/
public List predBlocksForReg(int reg, SsaMethod ssaMeth) {
ArrayList ret = new ArrayList();
for (Operand o : operands) {
if (o.regSpec.getReg() == reg) {
ret.add(ssaMeth.getBlocks().get(o.blockIndex));
}
}
return ret;
}
/** {@inheritDoc} */
@Override
public boolean isPhiOrMove() {
return true;
}
/** {@inheritDoc} */
@Override
public boolean hasSideEffect() {
return Optimizer.getPreserveLocals() && getLocalAssignment() != null;
}
/** {@inheritDoc} */
@Override
public void accept(SsaInsn.Visitor v) {
v.visitPhiInsn(this);
}
/** {@inheritDoc} */
public String toHuman() {
return toHumanWithInline(null);
}
/**
* Returns human-readable string for listing dumps. This method
* allows sub-classes to specify extra text.
*
* @param extra {@code null-ok;} the argument to print after the opcode
* @return human-readable string for listing dumps
*/
protected final String toHumanWithInline(String extra) {
StringBuffer sb = new StringBuffer(80);
sb.append(SourcePosition.NO_INFO);
sb.append(": phi");
if (extra != null) {
sb.append("(");
sb.append(extra);
sb.append(")");
}
RegisterSpec result = getResult();
if (result == null) {
sb.append(" .");
} else {
sb.append(" ");
sb.append(result.toHuman());
}
sb.append(" <-");
int sz = getSources().size();
if (sz == 0) {
sb.append(" .");
} else {
for (int i = 0; i < sz; i++) {
sb.append(" ");
sb.append(sources.get(i).toHuman()
+ "[b="
+ Hex.u2(operands.get(i).ropLabel) + "]");
}
}
return sb.toString();
}
/**
* A single phi operand, consiting of source register and block index
* for move.
*/
private static class Operand {
public RegisterSpec regSpec;
public final int blockIndex;
public final int ropLabel; // only used for debugging
public Operand(RegisterSpec regSpec, int blockIndex, int ropLabel) {
this.regSpec = regSpec;
this.blockIndex = blockIndex;
this.ropLabel = ropLabel;
}
}
/**
* Visitor interface for instances of this (outer) class.
*/
public static interface Visitor {
public void visitPhiInsn(PhiInsn insn);
}
}