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

import java.util.Enumeration;
import org.jikesrvm.compilers.opt.OptimizingCompilerException;
import org.jikesrvm.compilers.opt.ir.IR;
import org.jikesrvm.compilers.opt.ir.Operator;
import org.jikesrvm.compilers.opt.ir.Register;

/**
 * This class provides utilities to record defs and uses of physical
 * registers by IR operators.
 */
public abstract class PhysicalDefUse {

  // constants used to encode defs/uses of physical registers
  /** Default empty mask */
  public static final int mask = 0x0000;
  /** AF in the eflags is used/defined */
  public static final int maskAF = 0x0001;
  /** CF in the eflags is used/defined */
  public static final int maskCF = 0x0002;
  /** OF in the eflags is used/defined */
  public static final int maskOF = 0x0004;
  /** PF in the eflags is used/defined */
  public static final int maskPF = 0x0008;
  /** SF in the eflags is used/defined */
  public static final int maskSF = 0x0010;
  /** ZF in the eflags is used/defined */
  public static final int maskZF = 0x0020;
  /** C0 in the x87 FPU is used/defined */
  public static final int maskC0 = 0x0040;
  /** C1 in the x87 FPU is used/defined */
  public static final int maskC1 = 0x0080;
  /** C2 in the x87 FPU is used/defined */
  public static final int maskC2 = 0x0100;
  /** C3 in the x87 FPU is used/defined */
  public static final int maskC3 = 0x0200;
  /** The processor register is used/defined */
  public static final int maskPR = 0x0400;
  /** The ESP register is used/defined */
  public static final int maskESP= 0x0800;
  /* Meta mask for the enumeration. */
  /** First mask bit */
  private static final int maskHIGH = 0x0800;
  /** Mask for all bits */
  private static final int maskALL = 0x0FFF;

  public static final int maskCF_OF = maskCF | maskOF;
  public static final int maskCF_PF_ZF = maskCF | maskPF | maskZF;
  public static final int maskCF_OF_PF_SF_ZF = maskCF | maskOF | maskPF | maskSF | maskZF;
  public static final int maskAF_OF_PF_SF_ZF = maskAF | maskOF | maskPF | maskSF | maskZF;
  public static final int maskAF_CF_OF_PF_SF_ZF = maskAF | maskCF | maskOF | maskPF | maskSF | maskZF;
  public static final int maskC0_C1_C2_C3 = maskC0 | maskC1 | maskC2 | maskC3;
  public static final int maskcallDefs = maskAF_CF_OF_PF_SF_ZF | maskESP;
  public static final int maskcallUses = maskESP;
  public static final int maskIEEEMagicUses = mask;
  /** Uses mask used by dependence graph to show a yield point */
  public static final int maskTSPUses = maskESP;
  /** Definitions mask used by dependence graph to show a yield point */
  public static final int maskTSPDefs = maskAF_CF_OF_PF_SF_ZF | maskPR | maskESP;

  /**
   * @return whether or not an Operator uses the EFLAGS
   */
  public static boolean usesEFLAGS(Operator op) {
    return (op.implicitUses & maskAF_CF_OF_PF_SF_ZF) != 0;
  }

  /**
   * @return whether or not an Operator uses the EFLAGS
   */
  public static boolean definesEFLAGS(Operator op) {
    return (op.implicitDefs & maskAF_CF_OF_PF_SF_ZF) != 0;
  }

  /**
   * @return whether or not an Operator implicitly uses or defines ESP
   */
  public static boolean usesOrDefinesESP(Operator op) {
    return ((op.implicitUses & maskESP) != 0) || ((op.implicitDefs & maskESP) != 0);
  }
  /**
   * @return a string representation of the physical registers encoded by
   * an integer
   */
  public static String getString(int code) {
    if (code == mask) return "";
    if (code == maskAF_CF_OF_PF_SF_ZF) return " AF CF OF PF SF ZF";
    // Not a common case, construct it...
    String s = "";
    if ((code & maskAF) != 0) s += " AF";
    if ((code & maskCF) != 0) s += " CF";
    if ((code & maskOF) != 0) s += " OF";
    if ((code & maskPF) != 0) s += " PF";
    if ((code & maskZF) != 0) s += " ZF";
    if ((code & maskC0) != 0) s += " CO";
    if ((code & maskC1) != 0) s += " C1";
    if ((code & maskC2) != 0) s += " C2";
    if ((code & maskC3) != 0) s += " C3";
    if ((code & maskPR) != 0) s += " PR";
    if ((code & maskESP) != 0) s += " ESP";
    return s;
  }

  /**
   * @param code an integer that encodes a set of physical registers
   * @param ir the governing IR
   * @return an enumeration of the physical registers embodied by a code
   */
  public static PDUEnumeration enumerate(int code, IR ir) {
    return new PDUEnumeration(code, ir);
  }

  /**
   * @param ir the governing IR
   * @return an enumeration of all physical registers that code be
   *         implicitly defed/used
   */
  public static PDUEnumeration enumerateAllImplicitDefUses(IR ir) {
    return new PDUEnumeration(maskALL, ir);
  }

  /**
   * A class to enumerate physical registers based on a code.
   */
  public static final class PDUEnumeration implements Enumeration<Register> {
    private int code;
    private int curMask;
    private PhysicalRegisterSet phys;

    PDUEnumeration(int c, IR ir) {
      phys = ir.regpool.getPhysicalRegisterSet();
      code = c;
      curMask = maskHIGH;
    }

    public boolean hasMoreElements() {
      return code != 0;
    }

    public Register nextElement() {
      while (true) {
        int curBit = code & curMask;
        code -= curBit;
        curMask = curMask >> 1;
        if (curBit != 0) return getReg(curBit, phys);
      }
    }

    // artifically make static to enable scalar replacement of
    // enumeration object without requiring this method to be inlined.
    private static Register getReg(int m, PhysicalRegisterSet phys) {
      switch (m) {
        case maskAF:
          return phys.getAF();
        case maskCF:
          return phys.getCF();
        case maskOF:
          return phys.getOF();
        case maskPF:
          return phys.getPF();
        case maskSF:
          return phys.getSF();
        case maskZF:
          return phys.getZF();
        case maskC0:
          return phys.getC0();
        case maskC1:
          return phys.getC1();
        case maskC2:
          return phys.getC2();
        case maskC3:
          return phys.getC3();
        case maskPR:
          return phys.getPR();
        case maskESP:
          return phys.getESP();
      }
      OptimizingCompilerException.UNREACHABLE();
      return null; // placate jikes.
    }
  }
}
