/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Netscape Public
 * License Version 1.1 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a copy of
 * the License at http://www.mozilla.org/NPL/
 *
 * Software distributed under the License is distributed on an "AS
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * rights and limitations under the License.
 *
 * The Original Code is mozilla.org code.
 *
 * The Initial Developer of the Original Code is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation. All
 * Rights Reserved.
 *
 * Contributor(s): 
 */

#include "Coloring.h"
#include "VirtualRegister.h"
#include "FastBitSet.h"
#include "FastBitMatrix.h"
#include "CpuInfo.h"

bool Coloring::
assignRegisters(FastBitMatrix& interferenceMatrix)
{
  PRUint32 *stackPtr = new(pool) PRUint32[vRegManager.count()];

  return select(interferenceMatrix, stackPtr, simplify(interferenceMatrix, stackPtr));
}

PRInt32 Coloring::
getLowestSpillCostRegister(FastBitSet& bitset)
{
  PRInt32 lowest = bitset.firstOne();
  if (lowest != -1)
	{
	  Flt32 cost = vRegManager.getVirtualRegister(lowest).spillInfo.spillCost;
	  for (PRInt32 r = bitset.nextOne(lowest); r != -1; r = bitset.nextOne(r))
		{
		  VirtualRegister& vReg = vRegManager.getVirtualRegister(r);
		  if (!vReg.spillInfo.infiniteSpillCost && (vReg.spillInfo.spillCost < cost))
			{
			  cost = vReg.spillInfo.spillCost;
			  lowest = r;
			}
		}
	}
  return lowest;
}

PRUint32* Coloring::
simplify(FastBitMatrix interferenceMatrix, PRUint32* stackPtr)
{
  // first we construct the sets low and high. low contains all nodes of degree
  // inferior to the number of register available on the processor. All the
  // nodes with an high degree and a finite spill cost are placed in high.
  // Nodes of high degree and infinite spill cost are not included in either sets.

  PRUint32 nRegisters = vRegManager.count();
  FastBitSet low(pool, nRegisters);
  FastBitSet high(pool, nRegisters);
  FastBitSet stack(pool, nRegisters);
  bool all_low = true;

  for (VirtualRegisterManager::iterator i = vRegManager.begin(); !vRegManager.done(i); i = vRegManager.advance(i))
	{
	  VirtualRegister& vReg = vRegManager.getVirtualRegister(i);
	  
	  if (vReg.getClass() == vrcStackSlot)
		{
		  stack.set(i);
		  vReg.colorRegister(nRegisters);
		}
	  else
		{
		  if (vReg.colorInfo.interferenceDegree < NUMBER_OF_REGISTERS) {
			low.set(i);
		  } else {
			high.set(i);
			all_low = false;
		  }

		  // Set coloring info.
		  vReg.spillInfo.willSpill = false;

		  switch(vReg.getClass())
			{
			case vrcInteger:
			  vReg.colorRegister(LAST_GREGISTER + 1);
			  break;
			case vrcFloatingPoint:
			case vrcFixedPoint:
			  vReg.colorRegister(LAST_FPREGISTER + 1);
			  break;
			default:
			  PR_ASSERT(false); // Cannot happen.
			}
		}
	}

  // push the stack registers
  PRInt32 j;
  for (j = stack.firstOne(); j != -1; j = stack.nextOne(j))
	  *stackPtr++ = j;
  
  /* Simplify handling when every node in the interference graph has degree less
   * than K and is therefore trivially K-colorable.  In this case, no spilling
   * code is necessary.
   */
  if (all_low)
  {
	  for (j = low.firstOne(); j != -1; j = low.nextOne(j))
		  *stackPtr++ = j;
	  return stackPtr;
  }


  // simplify
  PRInt32 r = low.firstOne();
  while (true)
	{
	  /*
	   * Remove any node of degree less than K from the graph, since that node is 
	   * K-colorable. That deletion may, in turn, cause the degree of other nodes
	   * in the graph to fall below K and these nodes are then removed.
	   */
	  while (r != -1)
		{
		  VirtualRegister& vReg = vRegManager.getVirtualRegister(r);

		  /* update low and high */
		  FastBitSet inter(interferenceMatrix.getRow(r), nRegisters);
		  for (j = inter.firstOne(); j != -1; j = inter.nextOne(j))
			{
			  VirtualRegister& neighbor = vRegManager.getVirtualRegister(j);
			  // if the new interference degree of one of his neighbor becomes
			  // NUMBER_OF_REGISTERS - 1 then it is added to the set 'low'.
			  
			  PRUint32 maxInterference = 0;
			  switch (neighbor.getClass())
				{
				case vrcInteger:
				  maxInterference = NUMBER_OF_GREGISTERS;
				  break;
				case vrcFloatingPoint:
				case vrcFixedPoint:
				  maxInterference = NUMBER_OF_FPREGISTERS;
				  break;
				default:
				  PR_ASSERT(false);
				}
			  if ((vRegManager.getVirtualRegister(j).colorInfo.interferenceDegree-- == maxInterference))
				{
				  high.clear(j);
				  low.set(j);
				}
			  vReg.colorInfo.interferenceDegree--;
			  interferenceMatrix.clear(r, j);
			  interferenceMatrix.clear(j, r);
			}
		  low.clear(r);

		  // Push this register.
		  *stackPtr++ = r;

		  r = low.firstOne();
		}

	  /*
	   * The absence of any node with degree less than K means that a spill might be
	   * necessary.  Use spill cost heurestics to choose the register node to be
	   * removed from the graph.
	   */
	  if ((r = getLowestSpillCostRegister(high)) != -1)
		{
		  high.clear(r);
		}
	  else
		break;
	}

  return stackPtr;
}

bool Coloring::
select(FastBitMatrix& interferenceMatrix, PRUint32* stackBase, PRUint32* stackPtr)
{
  PRUint32 nRegisters = vRegManager.count();
  FastBitSet usedRegisters(NUMBER_OF_REGISTERS + 1); // usedRegisters if used for both GR & FPR.
  FastBitSet preColoredRegisters(NUMBER_OF_REGISTERS + 1);
  FastBitSet usedStack(nRegisters + 1);
  bool success = true;
  Int32 lastUsedSSR = -1;

  // select
  while (stackPtr != stackBase)
	{
	  // Pop one register.
	  PRUint32 r = *--stackPtr;
	  VirtualRegister& vReg = vRegManager.getVirtualRegister(r);

	  FastBitSet neighbors(interferenceMatrix.getRow(r), nRegisters);
		  
	  if (vReg.getClass() == vrcStackSlot)
		// Stack slots coloring.
		{
		  usedStack.clear();

		  for (PRInt32 i = neighbors.firstOne(); i != -1; i = neighbors.nextOne(i))
			usedStack.set(vRegManager.getVirtualRegister(i).getColor());

		  Int32 color = usedStack.firstZero();
		  vReg.colorRegister(color);
		  if (color > lastUsedSSR)
			  lastUsedSSR = color;
		}
	  else
		// Integer & Floating point register coloring.
		{
		  usedRegisters.clear();
		  preColoredRegisters.clear();
		  
		  for (PRInt32 i = neighbors.firstOne(); i != -1; i = neighbors.nextOne(i))
			{
			  VirtualRegister& nvReg = vRegManager.getVirtualRegister(i);
			  usedRegisters.set(nvReg.getColor());
			  if (nvReg.isPreColored())
				preColoredRegisters.set(nvReg.getPreColor());
			}
		  if (vReg.hasSpecialInterference)
			usedRegisters |= vReg.specialInterference;

		  PRInt8 c = -1;
		  PRInt8 maxColor = 0;
		  PRInt8 firstColor = 0;
		  switch (vReg.getClass())
			{
			case vrcInteger:
			  firstColor = FIRST_GREGISTER;
			  maxColor = LAST_GREGISTER;
			  break;
			case vrcFloatingPoint:
			case vrcFixedPoint:
			  firstColor = FIRST_FPREGISTER;
			  maxColor = LAST_FPREGISTER;
			  break;
			default:
			  PR_ASSERT(false);
			}

		  if (vReg.isPreColored())
			{
			  c = vReg.getPreColor();
			  if (usedRegisters.test(c))
				c = -1;
			}
		  else
			{
			  for (c = usedRegisters.nextZero(firstColor - 1); (c >= 0) && (c <= maxColor) && (preColoredRegisters.test(c)); 
				   c = usedRegisters.nextZero(c)) {}
			}

		  if ((c >= 0) && (c <= maxColor))
			{
			  vReg.colorRegister(c);
			}
		  else
			{
			  VirtualRegister& stackRegister = vRegManager.newVirtualRegister(vrcStackSlot);
			  vReg.equivalentRegister[vrcStackSlot] = &stackRegister;	  
			  vReg.spillInfo.willSpill = true;
			  success = false;
			}
		}
	}
  
#ifdef DEBUG
  if (success)
	{
	  for (VirtualRegisterManager::iterator i = vRegManager.begin(); !vRegManager.done(i); i = vRegManager.advance(i))
		{
		  VirtualRegister& vReg = vRegManager.getVirtualRegister(i);
		  switch (vReg.getClass())
			{
			case vrcInteger:
			  if (vReg.getColor() > LAST_GREGISTER)
				PR_ASSERT(false);
			  break;
			case vrcFloatingPoint:
			case vrcFixedPoint:
#if NUMBER_OF_FPREGISTERS != 0
			  if (vReg.getColor() > LAST_FPREGISTER)
				PR_ASSERT(false);
#endif
			  break;
			default:
			  break;
			}
		}
	}
#endif

  vRegManager.nUsedStackSlots = lastUsedSSR + 1;
  return success;
}
