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

import org.jikesrvm.compilers.common.assembler.ia32.AssemblerConstants;
import org.jikesrvm.compilers.opt.OptimizingCompilerException;
import org.jikesrvm.compilers.opt.ir.operand.ConditionOperand;
import org.jikesrvm.compilers.opt.ir.operand.Operand;

/**
 * An IA32 condition operand
 */
public final class IA32ConditionOperand extends Operand implements AssemblerConstants {

  /**
   * Value of this operand (one of the ConditionCode constants operands
   * defined in AssemblerConstants)
   */
  public byte value;

  /**
   * Returns a copy of the current operand.
   */
  public Operand copy() {
    return new IA32ConditionOperand(value);
  }

  /**
   * Returns if this operand is the 'same' as another operand.
   *
   * @param op other operand
   */
  public boolean similar(Operand op) {
    return (op instanceof IA32ConditionOperand) && ((IA32ConditionOperand) op).value == value;
  }

  /**
   * flip the direction of the condition (return this, mutated to flip value)
   */
  public IA32ConditionOperand flipCode() {
    switch (value) {
      case O:
        value = NO;
        break;
      case NO:
        value = O;
        break;
      case LLT:
        value = LGE;
        break;
      case LGE:
        value = LLT;
        break;
      case EQ:
        value = NE;
        break;
      case NE:
        value = EQ;
        break;
      case LLE:
        value = LGT;
        break;
      case LGT:
        value = LLE;
        break;
      case S:
        value = NS;
        break;
      case NS:
        value = S;
        break;
      case PE:
        value = PO;
        break;
      case PO:
        value = PE;
        break;
      case LT:
        value = GE;
        break;
      case GE:
        value = LT;
        break;
      case LE:
        value = GT;
        break;
      case GT:
        value = LE;
        break;
      default:
        OptimizingCompilerException.UNREACHABLE();
    }
    return this;
  }

  /**
   * change the condition when operands are flipped
   * (return this mutated to change value)
   */
  public IA32ConditionOperand flipOperands() {
    switch (value) {
      case LLT:
        value = LGT;
        break;
      case LGE:
        value = LLE;
        break;
      case LLE:
        value = LGE;
        break;
      case LGT:
        value = LLT;
        break;
      case LT:
        value = GT;
        break;
      case GE:
        value = LE;
        break;
      case LE:
        value = GE;
        break;
      case GT:
        value = LT;
        break;
      default:
        OptimizingCompilerException.TODO();
    }
    return this;
  }

  /**
   * Construct the IA32 Condition Operand that corresponds to the
   * argument ConditionOperand
   */
  public IA32ConditionOperand(ConditionOperand c) {
    translate(c);
  }

  public static IA32ConditionOperand EQ() {
    return new IA32ConditionOperand(EQ);
  }

  public static IA32ConditionOperand NE() {
    return new IA32ConditionOperand(NE);
  }

  public static IA32ConditionOperand LT() {
    return new IA32ConditionOperand(LT);
  }

  public static IA32ConditionOperand LE() {
    return new IA32ConditionOperand(LE);
  }

  public static IA32ConditionOperand GT() {
    return new IA32ConditionOperand(GT);
  }

  public static IA32ConditionOperand GE() {
    return new IA32ConditionOperand(GE);
  }

  public static IA32ConditionOperand O() {
    return new IA32ConditionOperand(O);
  }

  public static IA32ConditionOperand NO() {
    return new IA32ConditionOperand(NO);
  }

  public static IA32ConditionOperand LGT() {
    return new IA32ConditionOperand(LGT);
  }

  public static IA32ConditionOperand LLT() {
    return new IA32ConditionOperand(LLT);
  }

  public static IA32ConditionOperand LGE() {
    return new IA32ConditionOperand(LGE);
  }

  public static IA32ConditionOperand LLE() {
    return new IA32ConditionOperand(LLE);
  }

  public static IA32ConditionOperand PE() {
    return new IA32ConditionOperand(PE);
  }

  public static IA32ConditionOperand PO() {
    return new IA32ConditionOperand(PO);
  }

  private IA32ConditionOperand(byte c) {
    value = c;
  }

  // translate from ConditionOperand: used during LIR => MIR translation
  private void translate(ConditionOperand c) {
    switch (c.value) {
      case ConditionOperand.EQUAL:
      case ConditionOperand.SAME:
        value = EQ;
        break;
      case ConditionOperand.NOT_EQUAL:
      case ConditionOperand.NOT_SAME:
        value = NE;
        break;
      case ConditionOperand.LESS:
        value = LT;
        break;
      case ConditionOperand.LESS_EQUAL:
        value = LE;
        break;
      case ConditionOperand.GREATER:
        value = GT;
        break;
      case ConditionOperand.GREATER_EQUAL:
        value = GE;
        break;
      case ConditionOperand.HIGHER:
        value = LGT;
        break;
      case ConditionOperand.LOWER:
        value = LLT;
        break;
      case ConditionOperand.HIGHER_EQUAL:
        value = LGE;
        break;
      case ConditionOperand.LOWER_EQUAL:
        value = LLE;
        break;
      case ConditionOperand.CMPL_EQUAL:
      case ConditionOperand.CMPL_GREATER:
      case ConditionOperand.CMPG_LESS:
      case ConditionOperand.CMPL_GREATER_EQUAL:
      case ConditionOperand.CMPG_LESS_EQUAL:
      case ConditionOperand.CMPL_NOT_EQUAL:
      case ConditionOperand.CMPL_LESS:
      case ConditionOperand.CMPG_GREATER_EQUAL:
      case ConditionOperand.CMPG_GREATER:
      case ConditionOperand.CMPL_LESS_EQUAL:
        throw new Error("IA32ConditionOperand.translate: Complex operand can't be directly translated " + c);
      default:
        OptimizingCompilerException.UNREACHABLE();
    }
  }

  // Returns the string representation of this operand.
  public String toString() {
    return CONDITION[value];
  }

}
