/*
 *  This file is part of the Jikes RVM project (http://jikesrvm.org).
 *
 *  This file is licensed to You under the Common Public License (CPL);
 *  You may not use this file except in compliance with the License. You
 *  may obtain a copy of the License at
 *
 *      http://www.opensource.org/licenses/cpl1.0.php
 *
 *  See the COPYRIGHT.txt file distributed with this work for information
 *  regarding copyright ownership.
 */
package org.jikesrvm.compilers.opt.ir.ppc;

import org.jikesrvm.compilers.opt.OptimizingCompilerException;
import org.jikesrvm.compilers.opt.ir.MIR_CondBranch;
import org.jikesrvm.compilers.opt.ir.MIR_CondBranch2;
import org.jikesrvm.compilers.opt.ir.MIR_Load;
import org.jikesrvm.compilers.opt.ir.Instruction;
import org.jikesrvm.compilers.opt.ir.MachineSpecificIR;
import static org.jikesrvm.compilers.opt.ir.Operators.DCBST_opcode;
import static org.jikesrvm.compilers.opt.ir.Operators.DCBTST_opcode;
import static org.jikesrvm.compilers.opt.ir.Operators.DCBT_opcode;
import static org.jikesrvm.compilers.opt.ir.Operators.DCBZL_opcode;
import static org.jikesrvm.compilers.opt.ir.Operators.DCBZ_opcode;
import static org.jikesrvm.compilers.opt.ir.Operators.ICBI_opcode;
import static org.jikesrvm.compilers.opt.ir.Operators.LONG_2ADDR_opcode;
import static org.jikesrvm.compilers.opt.ir.Operators.LONG_ADD_opcode;
import static org.jikesrvm.compilers.opt.ir.Operators.LONG_AND_opcode;
import static org.jikesrvm.compilers.opt.ir.Operators.LONG_MOVE_opcode;
import static org.jikesrvm.compilers.opt.ir.Operators.LONG_NEG_opcode;
import static org.jikesrvm.compilers.opt.ir.Operators.LONG_OR_opcode;
import static org.jikesrvm.compilers.opt.ir.Operators.LONG_SHL_opcode;
import static org.jikesrvm.compilers.opt.ir.Operators.LONG_SHR_opcode;
import static org.jikesrvm.compilers.opt.ir.Operators.LONG_SUB_opcode;
import static org.jikesrvm.compilers.opt.ir.Operators.LONG_USHR_opcode;
import static org.jikesrvm.compilers.opt.ir.Operators.LONG_XOR_opcode;
import static org.jikesrvm.compilers.opt.ir.Operators.PPC_BCOND;
import org.jikesrvm.compilers.opt.ir.operand.IntConstantOperand;
import org.jikesrvm.compilers.opt.ir.operand.Operand;
import org.jikesrvm.compilers.opt.ir.operand.ppc.PowerPCConditionOperand;

/**
 * Wrappers around PowerPC-specific IR common to both 32 & 64 bit
 */
public abstract class MachineSpecificIRPowerPC extends MachineSpecificIR {
  /**
   * Wrappers around 32-bit PowerPC-specific IR
   */
  public static final class PPC32 extends MachineSpecificIRPowerPC {
    public static final PPC32 singleton = new PPC32();

    @Override
    public boolean mayEscapeThread(Instruction instruction) {
      switch (instruction.getOpcode()) {
        case DCBST_opcode:
        case DCBT_opcode:
        case DCBTST_opcode:
        case DCBZ_opcode:
        case DCBZL_opcode:
        case ICBI_opcode:
          return false;
        default:
          throw new OptimizingCompilerException("SimpleEscapge: Unexpected " + instruction);
      }
    }

    @Override
    public boolean mayEscapeMethod(Instruction instruction) {
      return mayEscapeThread(instruction); // at this stage we're no more specific
    }
  }

  /**
   * Wrappers around 64-bit PowerPC-specific IR
   */
  public static final class PPC64 extends MachineSpecificIRPowerPC {
    public static final PPC64 singleton = new PPC64();

    @Override
    public boolean mayEscapeThread(Instruction instruction) {
      switch (instruction.getOpcode()) {
        case DCBST_opcode:
        case DCBT_opcode:
        case DCBTST_opcode:
        case DCBZ_opcode:
        case DCBZL_opcode:
        case ICBI_opcode:
          return false;
        case LONG_OR_opcode:
        case LONG_AND_opcode:
        case LONG_XOR_opcode:
        case LONG_SUB_opcode:
        case LONG_SHL_opcode:
        case LONG_ADD_opcode:
        case LONG_SHR_opcode:
        case LONG_USHR_opcode:
        case LONG_NEG_opcode:
        case LONG_MOVE_opcode:
        case LONG_2ADDR_opcode:
          return true;
        default:
          throw new OptimizingCompilerException("SimpleEscapge: Unexpected " + instruction);
      }
    }

    @Override
    public boolean mayEscapeMethod(Instruction instruction) {
      return mayEscapeThread(instruction); // at this stage we're no more specific
    }
  }

  /*
  * Generic (32/64 neutral) PowerPC support
  */

  /* common to all ISAs */

  @Override
  public final boolean isConditionOperand(Operand operand) {
    return operand instanceof PowerPCConditionOperand;
  }

  @Override
  public final void mutateMIRCondBranch(Instruction cb) {
    MIR_CondBranch.mutate(cb,
                          PPC_BCOND,
                          MIR_CondBranch2.getValue(cb),
                          MIR_CondBranch2.getCond1(cb),
                          MIR_CondBranch2.getTarget1(cb),
                          MIR_CondBranch2.getBranchProfile1(cb));
  }

  @Override
  public final boolean isHandledByRegisterUnknown(char opcode) {
    switch (opcode) {
      case DCBST_opcode:
      case DCBT_opcode:
      case DCBTST_opcode:
      case DCBZ_opcode:
      case DCBZL_opcode:
      case ICBI_opcode:
        return true;
      default:
        return false;
    }
  }

  /* unique to PowerPC */
  @Override
  public final boolean isPowerPCTrapOperand(Operand operand) {
    return operand instanceof PowerPCConditionOperand;
  }

  @Override
  public final boolean canFoldNullCheckAndLoad(Instruction s) {
    if (MIR_Load.conforms(s)) {
      Operand offset = MIR_Load.getOffset(s);
      if (offset instanceof IntConstantOperand) {
        return ((IntConstantOperand) offset).value < 0;
      }
    }
    return false;
  }
}
