/* SSA operand allocation and finalizing.
   Copyright (C) 2005 Free Software Foundation, Inc.

This file is part of GCC.

GCC is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 2, or (at your option) any later
version.

GCC is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
for more details.

You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING.  If not, write to the Free
Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.  */


/* This file contains common code which is used by each of the 5 operand 
   types.  Macros are defined to specify the varying components.

   FINALIZE_FUNC - name of finalize function.
   FINALIZE_ALLOC - name of allocation routine.
   FINALIZE_FREE - name of free list.
   FINALIZE_TYPE - type of node.
   FINALIZE_OPS - Lead element in list.
   FINALIZE_USE_PTR - How to get the use_operand_p, if this is a use operand.
   FINALIZE_INITIALIZE - How to initialize an element.
   FINALIZE_ELEM - How to retrieve an element.
   FINALIZE_BASE - How to retrieve the base variable of an element.
   FINALIZE_BASE_TYPE - Type of the base variable.
   FINALIZE_OPBUILD - Opbuild array for these nodes.
   FINALIZE_OPBUILD_ELEM - How to get an element from the opbuild list.
   FINALIZE_OPBUILD_BASE - How to get an element base from the opbuild list.
   FINALIZE_BASE_ZERO - How to zero an element.  */


/* This routine will either pick up a node from the free list, or allocate a
   new one if need be.  */

static inline FINALIZE_TYPE *
FINALIZE_ALLOC (void)
{
  FINALIZE_TYPE *ret;
  if (FINALIZE_FREE)
    {
      ret = FINALIZE_FREE;
      FINALIZE_FREE = FINALIZE_FREE->next;
    }
  else
    ret = (FINALIZE_TYPE *)ssa_operand_alloc (sizeof (FINALIZE_TYPE));
  return ret;
}



/* This routine will take the new operands from FINALIZE_OPBUILD and turn them
   into the new operands for STMT.  All required linking and deleting is u
   performed here.  */
static inline void
FINALIZE_FUNC (tree stmt)
{
  unsigned new_i;
  FINALIZE_TYPE *old_ops, *ptr, *last;
  FINALIZE_BASE_TYPE old_base;
  FINALIZE_TYPE new_list;

  new_list.next = NULL;
  last = &new_list;

  old_ops = FINALIZE_OPS (stmt);
  if (old_ops)
    old_base = FINALIZE_BASE (FINALIZE_ELEM (old_ops));
  else
    old_base = FINALIZE_BASE_ZERO;

  new_i = 0;
  while (old_ops && new_i < VEC_length (tree, FINALIZE_OPBUILD))
    {
      FINALIZE_BASE_TYPE new_base = FINALIZE_OPBUILD_BASE (new_i);
      if (old_base == new_base)
        {
	  /* if variables are the same, reuse this node.  */
	  last->next = old_ops;
	  last = old_ops;
#ifdef FINALIZE_CORRECT_USE
	  FINALIZE_CORRECT_USE (FINALIZE_USE_PTR (last), stmt);
#endif
	  old_ops = old_ops->next;
	  new_i++;
	}
      else
        if (old_base < new_base)
	  {
	    /* if old is less than new, old goes to the free list.  */
#ifdef FINALIZE_USE_PTR
	    use_operand_p use_p = FINALIZE_USE_PTR (old_ops);
	    delink_imm_use (use_p);
#endif
	    ptr = old_ops;
	    old_ops = old_ops->next;
	    ptr->next = FINALIZE_FREE;
	    FINALIZE_FREE = ptr;
	  }
	else
	  {
	    /* This is a new operand.  */
	    ptr = FINALIZE_ALLOC ();
	    FINALIZE_INITIALIZE (ptr, FINALIZE_OPBUILD_ELEM (new_i), stmt);
	    last->next = ptr;
	    last = ptr;
	    new_i++;
	  }
      if (old_ops)
        old_base = FINALIZE_BASE (FINALIZE_ELEM (old_ops));
    }

  /* If there is anything remaining in the opbuild list, simply emit them.  */
  for ( ; new_i < VEC_length (tree, FINALIZE_OPBUILD); new_i++)
    {
      ptr = FINALIZE_ALLOC ();
      FINALIZE_INITIALIZE (ptr, FINALIZE_OPBUILD_ELEM (new_i), stmt);
      last->next = ptr;
      last = ptr;
    }

  last->next = NULL;

  /* If there is anything in the old list, free them.  */
  if (old_ops)
    {
#ifdef FINALIZE_USE_PTR
      for (ptr = old_ops; ptr; ptr = ptr->next)
	{
	  use_operand_p use_p = FINALIZE_USE_PTR (ptr);
	  delink_imm_use (use_p);
	}
#endif
      old_ops->next = FINALIZE_FREE;
      FINALIZE_FREE = old_ops;
    }

  /* NOw set the stmt's operands.  */
  FINALIZE_OPS (stmt) = new_list.next;

#ifdef ENABLE_CHECKING
  {
    unsigned x = 0;
    for (ptr = FINALIZE_OPS (stmt); ptr; ptr = ptr->next)
      x++;

    gcc_assert (x == VEC_length (tree, FINALIZE_OPBUILD));
  }
#endif
}

#undef FINALIZE_FUNC
#undef FINALIZE_ALLOC
#undef FINALIZE_FREE
#undef FINALIZE_TYPE
#undef FINALIZE_OPS
#undef FINALIZE_USE_PTR
#undef FINALIZE_INITIALIZE
#undef FINALIZE_ELEM
#undef FINALIZE_BASE
#undef FINALIZE_BASE_TYPE
#undef FINALIZE_OPBUILD
#undef FINALIZE_OPBUILD_ELEM
#undef FINALIZE_OPBUILD_BASE
#undef FINALIZE_BASE_ZERO
#undef FINALIZE_CORRECT_USE
