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

import org.jikesrvm.ArchitectureSpecificOpt.PhysicalRegisterSet;
import org.jikesrvm.compilers.opt.ir.operand.RegisterOperand;

/**
 * Represents a symbolic or physical register.
 * Registers are shared among all Operands -- for a given register
 * pool, there is only one instance of an Register with each number.
 *
 * @see RegisterOperand
 * @see org.jikesrvm.ArchitectureSpecificOpt.RegisterPool
 */
public final class Register {

  /**
   * Index number relative to register pool.
   */
  public final int number;

  /**
   * Encoding of register properties & scratch bits
   */
  private int flags;

  private static final int LOCAL = 0x00001;  /* local variable */
  private static final int SPAN_BASIC_BLOCK = 0x00002;  /* live on a basic block boundary */
  private static final int SSA = 0x00004;  /* only one assignment to this register */
  private static final int SEEN_USE = 0x00008;  /* seen use */
  private static final int PHYSICAL = 0x00010;  /* physical (real) register - not symbolic */

  /*  register type  for both physical and symbolic */
  private static final int TYPE_SHIFT = 6;        /* # bits to shift */
  private static final int ADDRESS = 0x00040;  /* address */
  private static final int INTEGER = 0x00080;  /* integer */
  private static final int FLOAT = 0x00100;  /* floating-point single precision */
  private static final int DOUBLE = 0x00200;  /* floating-point double precision */
  private static final int CONDITION = 0x00400;  /* condition: PPC,x86*/
  private static final int LONG = 0x00800;  /* long (two ints)*/
  private static final int VALIDATION = 0x01000;  /* validation pseudo-register */

  /* this for physical register only */
  private static final int VOLATILE = 0x02000;
  private static final int NON_VOLATILE = 0x04000;

  /* used with live analysis */
  private static final int EXCLUDE_LIVEANAL = 0x08000; /* reg is excluded from live analysis */

  /* used by the register allocator */
  private static final int SPILLED = 0x10000; /* spilled into a memory location */
  private static final int TOUCHED = 0x20000; /* register touched */
  private static final int ALLOCATED = 0x40000; /* allocated to some register */
  private static final int PINNED = 0x80000; /* pinned, unavailable for allocation */

  /* derived constants to be exported */
  private static final int TYPE_MASK = (ADDRESS | INTEGER | FLOAT | DOUBLE | CONDITION | LONG | VALIDATION);
  public static final int ADDRESS_TYPE = ADDRESS >>> TYPE_SHIFT;
  public static final int INTEGER_TYPE = INTEGER >>> TYPE_SHIFT;
  public static final int FLOAT_TYPE = FLOAT >>> TYPE_SHIFT;
  public static final int DOUBLE_TYPE = DOUBLE >>> TYPE_SHIFT;
  public static final int CONDITION_TYPE = CONDITION >>> TYPE_SHIFT;
  public static final int LONG_TYPE = LONG >>> TYPE_SHIFT;
  public static final int VALIDATION_TYPE = VALIDATION >>> TYPE_SHIFT;

  public boolean isTemp() { return (flags & LOCAL) == 0; }

  public boolean isLocal() { return (flags & LOCAL) != 0; }

  public boolean spansBasicBlock() { return (flags & SPAN_BASIC_BLOCK) != 0; }

  public boolean isSSA() { return (flags & SSA) != 0; }

  public boolean seenUse() { return (flags & SEEN_USE) != 0; }

  public boolean isPhysical() { return (flags & PHYSICAL) != 0; }

  public boolean isSymbolic() { return (flags & PHYSICAL) == 0; }

  public boolean isAddress() { return (flags & ADDRESS) != 0; }

  public boolean isInteger() { return (flags & INTEGER) != 0; }

  public boolean isLong() { return (flags & LONG) != 0; }

  public boolean isNatural() { return (flags & (INTEGER | LONG | ADDRESS)) != 0; }

  public boolean isFloat() { return (flags & FLOAT) != 0; }

  public boolean isDouble() { return (flags & DOUBLE) != 0; }

  public boolean isFloatingPoint() { return (flags & (FLOAT | DOUBLE)) != 0; }

  public boolean isCondition() { return (flags & CONDITION) != 0; }

  public boolean isValidation() { return (flags & VALIDATION) != 0; }

  public boolean isExcludedLiveA() { return (flags & EXCLUDE_LIVEANAL) != 0; }

  public int getType() { return (flags & TYPE_MASK) >>> TYPE_SHIFT; }

  public boolean isVolatile() { return (flags & VOLATILE) != 0; }

  public boolean isNonVolatile() { return (flags & NON_VOLATILE) != 0; }

  public void setLocal() { flags |= LOCAL; }

  public void setSpansBasicBlock() { flags |= SPAN_BASIC_BLOCK; }

  public void setSSA() { flags |= SSA; }

  public void setSeenUse() { flags |= SEEN_USE; }

  public void setPhysical() { flags |= PHYSICAL; }

  public void setAddress() { flags |= ADDRESS; }

  public void setInteger() { flags |= INTEGER; }

  public void setFloat() { flags |= FLOAT; }

  public void setDouble() { flags |= DOUBLE; }

  public void setLong() { flags |= LONG; }

  public void setCondition() { flags = (flags & ~TYPE_MASK) | CONDITION; }

  public void setValidation() { flags |= VALIDATION; }

  public void setExcludedLiveA() { flags |= EXCLUDE_LIVEANAL; }

  public void setVolatile() { flags |= VOLATILE; }

  public void setNonVolatile() { flags |= NON_VOLATILE; }

  public void putSSA(boolean a) {
    if (a) {
      setSSA();
    } else {
      clearSSA();
    }
  }

  public void putSpansBasicBlock(boolean a) {
    if (a) {
      setSpansBasicBlock();
    } else {
      clearSpansBasicBlock();
    }
  }

  public void clearLocal() { flags &= ~LOCAL; }

  public void clearSpansBasicBlock() { flags &= ~SPAN_BASIC_BLOCK; }

  public void clearSSA() { flags &= ~SSA; }

  public void clearSeenUse() { flags &= ~SEEN_USE; }

  public void clearPhysical() { flags &= ~PHYSICAL; }

  public void clearAddress() { flags &= ~ADDRESS; }

  public void clearInteger() { flags &= ~INTEGER; }

  public void clearFloat() { flags &= ~FLOAT; }

  public void clearDouble() { flags &= ~DOUBLE; }

  public void clearLong() { flags &= ~LONG; }

  public void clearCondition() { flags &= ~CONDITION; }

  public void clearType() { flags &= ~TYPE_MASK; }

  public void clearValidation() { flags &= ~VALIDATION; }

  public Object scratchObject;

  /**
   * Used in dependence graph construction.
   */
  public void setdNode(org.jikesrvm.compilers.opt.depgraph.DepGraphNode a) {
    scratchObject = a;
  }

  public org.jikesrvm.compilers.opt.depgraph.DepGraphNode dNode() {
    return (org.jikesrvm.compilers.opt.depgraph.DepGraphNode) scratchObject;
  }

  /**
   * Used to store register lists.
   * Computed on demand by IR.computeDU().
   */
  public RegisterOperand defList, useList;

  /**
   * This accessor is only valid when register lists are valid
   */
  public Instruction getFirstDef() {
    if (defList == null) {
      return null;
    } else {
      return defList.instruction;
    }
  }

  /**
   * The number of uses; used by flow-insensitive optimizations
   */
  public int useCount;

  /**
   * A field optimizations can use as they choose
   */
  public int scratch;

  public Register(int Number) {
    number = Number;
  }

  public int getNumber() {
    int start = PhysicalRegisterSet.getSize();
    return number - start;
  }

  /**
   * Returns the string representation of this register.
   */
  public String toString() {
    if (isPhysical()) {
      return PhysicalRegisterSet.getName(number);
    }

    // Set s to descriptive letter for register type
    String s = isLocal() ? "l" : "t";
    s = s + getNumber() + (spansBasicBlock() ? "p" : "") + (isSSA() ? "s" : "") + typeName();
    return s;
  }

  public String typeName() {
    String s = "";
    if (isCondition()) s += "c";
    if (isAddress()) s += "a";
    if (isInteger()) s += "i";
    if (isDouble()) s += "d";
    if (isFloat()) s += "f";
    if (isLong()) s += "l";
    if (isValidation()) s += "v";
    if (s == null) s = "_";
    return s;
  }

  /* used by the register allocator */
  public Register mapsToRegister;

  public void clearAllocationFlags() {
    flags &= ~(PINNED | TOUCHED | ALLOCATED | SPILLED);
  }

  public void pinRegister() {
    flags |= PINNED | TOUCHED;
  }

  public void reserveRegister() {
    flags |= PINNED;
  }

  public void touchRegister() {
    flags |= TOUCHED;
  }

  public void allocateRegister() {
    flags = (flags & ~SPILLED) | (ALLOCATED | TOUCHED);
  }

  public void allocateRegister(Register reg) {
    flags = (flags & ~SPILLED) | (ALLOCATED | TOUCHED);
    mapsToRegister = reg;
  }

  public void allocateToRegister(Register reg) {
    this.allocateRegister(reg);
    reg.allocateRegister(this);
  }

  public void deallocateRegister() {
    flags &= ~ALLOCATED;
    mapsToRegister = null;
  }

  public void freeRegister() {
    deallocateRegister();
    Register symbReg = mapsToRegister;
    if (symbReg != null) {
      symbReg.clearSpill();
    }
  }

  public void spillRegister() {
    flags = (flags & ~ALLOCATED) | SPILLED;
  }

  public void clearSpill() {
    flags &= ~SPILLED;
  }

  public void unpinRegister() {
    flags &= ~PINNED;
  }

  public boolean isTouched() {
    return (flags & TOUCHED) != 0;
  }

  public boolean isAllocated() {
    return (flags & ALLOCATED) != 0;
  }

  public boolean isSpilled() {
    return (flags & SPILLED) != 0;
  }

  public boolean isPinned() {
    return (flags & PINNED) != 0;
  }

  public boolean isAvailable() {
    return (flags & (ALLOCATED | PINNED)) == 0;
  }

  public Register getRegisterAllocated() {
    return mapsToRegister;
  }

  public int getSpillAllocated() {
    return scratch;
  }

  public int hashCode() {
    return number;
  }

  /* inlined behavior of DoublyLinkedListElement */ Register next, prev;

  public Register getNext() { return next; }

  void setNext(Register e) { next = e; }

  public Register getPrev() { return prev; }

  public void linkWithNext(Register Next) {
    next = Next;
    Next.prev = this;
  }

  void append(Register l) {
    next = l;
    l.prev = this;
  }

  Register remove() {
    Register Prev = prev, Next = next;
    if (Prev != null) Prev.next = Next;
    if (Next != null) Next.prev = Prev;
    return Next;
  }
  /* end of inlined behavior */
}
