| /* |
| * 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.rop.type.Type; |
| import com.android.dx.rop.type.TypeBearer; |
| import com.android.dx.util.Hex; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| /** |
| * 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<Operand> operands = new ArrayList<Operand>(); |
| |
| /** {@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} */ |
| 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.<p> |
| * |
| * 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; |
| } |
| |
| /** |
| * 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 |
| */ |
| 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<SsaBasicBlock> predBlocksForReg(int reg, SsaMethod ssaMeth) { |
| ArrayList<SsaBasicBlock> ret = new ArrayList<SsaBasicBlock>(); |
| |
| 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); |
| } |
| } |