content: /*
   * 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.taobao.android.dx.ssa;
  
  import com.taobao.android.dx.rop.code.Insn;
  import com.taobao.android.dx.rop.code.LocalItem;
  import com.taobao.android.dx.rop.code.RegisterSpec;
  import com.taobao.android.dx.rop.code.RegisterSpecList;
  import com.taobao.android.dx.rop.code.Rop;
  import com.taobao.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.taobao.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.taobao.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.taobao.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);
      }
  }
  