/*
 *  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.operand.ppc;

import org.jikesrvm.compilers.opt.ir.operand.ConditionOperand;
import org.jikesrvm.compilers.opt.ir.operand.Operand;

/**
 * Encodes the BO & BI condition fields for PowerPC
 *
 * @see Operand
 */
public final class PowerPCConditionOperand extends Operand {
  public static final int ALWAYS = (20 << 5);
  public static final int EQUAL = (12 << 5) | 2;
  public static final int NOT_EQUAL = (4 << 5) | 2;
  public static final int LESS = (12 << 5) | 0;
  public static final int GREATER_EQUAL = (4 << 5) | 0;
  public static final int GREATER = (12 << 5) | 1;
  public static final int LESS_EQUAL = (4 << 5) | 1;
  public static final int OVERFLOW = (12 << 5) | 3;
  public static final int NOT_OVERFLOW = (4 << 5) | 3;

  /* interpretation for floating-point values */
  public static final int UNORDERED = (12 << 5) | 3;
  public static final int NOT_UNORDERED = (4 << 5) | 3;

  // --CTR == 0
  public static final int CTRZ = (17 << 5) | 0;
  // --CTR != 0
  public static final int CTRNZ = (16 << 5) | 0;
  // (--CTR == 0) & condition
  public static final int CTRZ_EQUAL = (10 << 5) | 2;
  public static final int CTRZ_NOT_EQUAL = (2 << 5) | 2;
  public static final int CTRZ_LESS = (10 << 5) | 0;
  public static final int CTRZ_GREATER_EQUAL = (2 << 5) | 0;
  public static final int CTRZ_GREATER = (10 << 5) | 1;
  public static final int CTRZ_LESS_EQUAL = (2 << 5) | 1;
  public static final int CTRZ_OVERFLOW = (10 << 5) | 3;
  public static final int CTRZ_NOT_OVERFLOW = (2 << 5) | 3;
  // (--CTR != 0) & condition
  public static final int CTRNZ_EQUAL = (8 << 5) | 2;
  public static final int CTRNZ_NOT_EQUAL = (0 << 5) | 2;
  public static final int CTRNZ_LESS = (8 << 5) | 0;
  public static final int CTRNZ_GREATER_EQUAL = (0 << 5) | 0;
  public static final int CTRNZ_GREATER = (8 << 5) | 1;
  public static final int CTRNZ_LESS_EQUAL = (0 << 5) | 1;
  public static final int CTRNZ_OVERFLOW = (8 << 5) | 3;
  public static final int CTRNZ_NOT_OVERFLOW = (0 << 5) | 3;

  /**
   * Value of this operand.
   */
  public int value;

  // TODO: add the things with the CTR register also.
  public PowerPCConditionOperand(int Code) {
    value = Code;
  }

  public static PowerPCConditionOperand EQUAL() {
    return new PowerPCConditionOperand(EQUAL);
  }

  public static PowerPCConditionOperand NOT_EQUAL() {
    return new PowerPCConditionOperand(NOT_EQUAL);
  }

  public static PowerPCConditionOperand LESS() {
    return new PowerPCConditionOperand(LESS);
  }

  public static PowerPCConditionOperand LESS_EQUAL() {
    return new PowerPCConditionOperand(LESS_EQUAL);
  }

  public static PowerPCConditionOperand GREATER() {
    return new PowerPCConditionOperand(GREATER);
  }

  public static PowerPCConditionOperand GREATER_EQUAL() {
    return new PowerPCConditionOperand(GREATER_EQUAL);
  }

  public static PowerPCConditionOperand UNORDERED() {
    return new PowerPCConditionOperand(UNORDERED);
  }

  public static PowerPCConditionOperand get(ConditionOperand cond) {
    return new PowerPCConditionOperand(cond);
  }

  public Operand copy() {
    return new PowerPCConditionOperand(value);
  }

  public boolean similar(Operand op) {
    return (op instanceof PowerPCConditionOperand) && (((PowerPCConditionOperand) op).value == value);
  }

  /**
   * flips the direction of the condition
   */
  public PowerPCConditionOperand flipCode() {
    switch (value) {
      case EQUAL:
        value = NOT_EQUAL;
        break;
      case NOT_EQUAL:
        value = EQUAL;
        break;
      case LESS:
        value = GREATER_EQUAL;
        break;
      case LESS_EQUAL:
        value = GREATER;
        break;
      case GREATER:
        value = LESS_EQUAL;
        break;
      case GREATER_EQUAL:
        value = LESS;
        break;
      case OVERFLOW:
        value = NOT_OVERFLOW;
        break;
      case NOT_OVERFLOW:
        value = OVERFLOW;
        break;
      case CTRZ:
        value = CTRNZ;
        break;
      case CTRNZ:
        value = CTRZ;
        break;
      default:
        throw new org.jikesrvm.compilers.opt.OptimizingCompilerException("Unhandled case in flipCode");
    }
    return this;
  }

  /**
   * this could be used if you want to flip the order of the operands
   * you will notice that there are some differences
   */
  public PowerPCConditionOperand flipOperands() {
    switch (value) {
      case EQUAL:
        value = NOT_EQUAL;
        break;
      case NOT_EQUAL:
        value = EQUAL;
        break;
      case LESS:
        value = GREATER;
        break;
      case LESS_EQUAL:
        value = GREATER_EQUAL;
        break;
      case GREATER:
        value = LESS;
        break;
      case GREATER_EQUAL:
        value = LESS_EQUAL;
        break;
      case OVERFLOW:
        value = NOT_OVERFLOW;
        break;
      case NOT_OVERFLOW:
        value = OVERFLOW;
        break;
        // TODO remaining
    }
    return this;
  }

  public PowerPCConditionOperand(ConditionOperand c) {
    translate(c);
  }

  /**
   * translate from ConditionOperand: used by BURS
   */
  public void translate(ConditionOperand c) {
    switch (c.value) {
      case ConditionOperand.EQUAL:
      case ConditionOperand.SAME:
      case ConditionOperand.CMPL_EQUAL:
        value = EQUAL;
        break;
      case ConditionOperand.NOT_EQUAL:
      case ConditionOperand.NOT_SAME:
      case ConditionOperand.CMPL_NOT_EQUAL: // Extra unordered test required
        value = NOT_EQUAL;
        break;
      case ConditionOperand.LESS:
      case ConditionOperand.LOWER:
      case ConditionOperand.CMPG_LESS:
      case ConditionOperand.CMPL_LESS: // Extra unordered test required
        value = LESS;
        break;
      case ConditionOperand.LESS_EQUAL:
      case ConditionOperand.LOWER_EQUAL:
      case ConditionOperand.CMPG_LESS_EQUAL:
      case ConditionOperand.CMPL_LESS_EQUAL: // Extra unordered test required
        value = LESS_EQUAL;
        break;
      case ConditionOperand.GREATER:
      case ConditionOperand.HIGHER:
      case ConditionOperand.CMPL_GREATER:
      case ConditionOperand.CMPG_GREATER: // Extra unordered test required
        value = GREATER;
        break;
      case ConditionOperand.GREATER_EQUAL:
      case ConditionOperand.HIGHER_EQUAL:
      case ConditionOperand.CMPL_GREATER_EQUAL:
      case ConditionOperand.CMPG_GREATER_EQUAL: // Extra unordered test required
        value = GREATER_EQUAL;
        break;
      default:
        org.jikesrvm.compilers.opt.OptimizingCompilerException.UNREACHABLE();
    }
  }

  /**
   * Returns the string representation of this operand.
   */
  public String toString() {
    String result = "ppc ";
    if ((value & 0x1C0) == 0) {
      result = result + "--ctr!=0 && ";
    }
    if ((value & 0x1C0) == 0x40) {
      result = result + "--ctr==0 && ";
    }
    String temp = null;
    if ((value & 0x300) == 0x100) {             // true
      switch (value & 0x3) {
        case 0:
          temp = "<";
          break;
        case 1:
          temp = ">";
          break;
        case 2:
          temp = "==";
          break;
        case 3:
          temp = "overflow";
          break;
      }
    }
    if ((value & 0x300) == 0x000) {             // false
      switch (value & 0x3) {
        case 0:
          temp = ">=";
          break;
        case 1:
          temp = "<=";
          break;
        case 2:
          temp = "!=";
          break;
        case 3:
          temp = "not_overflow";
          break;
      }
    }
    return result + temp;
  }
}



