/* storag.c -- Implementation File (module.c template V1.0)
   Copyright (C) 1995 Free Software Foundation, Inc.
   Contributed by James Craig Burley (burley@gnu.ai.mit.edu).

This file is part of GNU Fortran.

GNU Fortran 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.

GNU Fortran 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 GNU Fortran; see the file COPYING.  If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.

   Related Modules:
      None

   Description:
      Maintains information on storage (memory) relationships between
      COMMON, dummy, and local variables, plus their equivalences (dummies
      don't have equivalences, however).

   Modifications:
*/

/* Include files. */

#include "proj.h"
#include "storag.h"
#include "data.h"
#include "malloc.h"
#include "symbol.h"
#include "target.h"

/* Externals defined here. */

ffestoragList_ ffestorag_list_;

/* Simple definitions and enumerations. */


/* Internal typedefs. */


/* Private include files. */


/* Internal structure definitions. */


/* Static objects accessed by functions in this module. */

static ffetargetOffset ffestorag_local_size_;	/* #units allocated so far. */
static bool ffestorag_reported_;/* Reports happen only once. */

/* Static functions (internal). */


/* Internal macros. */

#define ffestorag_next_(s) ((s)->next)
#define ffestorag_previous_(s) ((s)->previous)

/* ffestorag_drive -- Drive fn from list of storage objects

   ffestoragList sl;
   void (*fn)(ffestorag mst,ffestorag st);
   ffestorag mst;  // the master ffestorag object (or whatever)
   ffestorag_drive(sl,fn,mst);

   Calls (*fn)(mst,st) for every st in the list sl.  */

void
ffestorag_drive (ffestoragList sl, void (*fn) (ffestorag mst, ffestorag st),
		 ffestorag mst)
{
  ffestorag st;

  for (st = sl->first;
       st != (ffestorag) &sl->first;
       st = st->next)
    (*fn) (mst, st);
}

/* ffestorag_dump -- Dump information on storage object

   ffestorag s;	 // the ffestorag object
   ffestorag_dump(s);

   Dumps information in the storage object.  */

void
ffestorag_dump (ffestorag s)
{
  if (s == NULL)
    {
      fprintf (stdout, "(no storage object)");
      return;
    }

  switch (s->type)
    {
    case FFESTORAG_typeCBLOCK:
      fprintf (stdout, "CBLOCK ");
      break;

    case FFESTORAG_typeCOMMON:
      fprintf (stdout, "COMMON ");
      break;

    case FFESTORAG_typeLOCAL:
      fprintf (stdout, "LOCAL ");
      break;

    case FFESTORAG_typeDUMMY:
      fprintf (stdout, "DUMMY ");
      break;

    case FFESTORAG_typeEQUIV:
      fprintf (stdout, "EQUIV ");
      break;

    default:
      fprintf (stdout, "?%d? ", s->type);
      break;
    }

  if (s->symbol != NULL)
    fprintf (stdout, "\"%s\" ", ffesymbol_text (s->symbol));

  fprintf (stdout, "at %" ffetargetOffset_f "u size %" ffetargetOffset_f
	   "u, align loc%%%"
	   ffetargetAlign_f "u=%" ffetargetAlign_f "u, bt=%s, kt=%s",
	   s->offset,
	   s->size, (unsigned int) s->alignment, (unsigned int) s->modulo,
	   ffeinfo_basictype_string (s->basic_type),
	   ffeinfo_kindtype_string (s->kind_type));

  if (s->equivs_.first != (ffestorag) &s->equivs_.first)
    {
      ffestorag sq;

      fprintf (stdout, " with equivs");
      for (sq = s->equivs_.first;
	   sq != (ffestorag) &s->equivs_.first;
	   sq = ffestorag_next_ (sq))
	{
	  if (ffestorag_previous_ (sq) == (ffestorag) &s->equivs_.first)
	    fputc (' ', stdout);
	  else
	    fputc (',', stdout);
	  fprintf (stdout, "%s", ffesymbol_text (ffestorag_symbol (sq)));
	}
    }
}

/* ffestorag_init_2 -- Initialize for new program unit

   ffestorag_init_2();	*/

void
ffestorag_init_2 ()
{
  ffestorag_list_.first = ffestorag_list_.last
  = (ffestorag) &ffestorag_list_.first;
  ffestorag_local_size_ = 0;
  ffestorag_reported_ = FALSE;
}

/* ffestorag_end_layout -- Do final layout for symbol

   ffesymbol s;
   ffestorag_end_layout(s);  */

void
ffestorag_end_layout (ffesymbol s)
{
  if (ffesymbol_storage (s) != NULL)
    return;			/* Already laid out. */

  ffestorag_exec_layout (s);	/* Do what we have in common. */
#if 0
  assert (ffesymbol_storage (s) == NULL);	/* I'd like to know what
						   cases miss going through
						   ffecom_sym_learned, and
						   why; I don't think we
						   should have to do the
						   exec_layout thing at all
						   here. */
  /* Now I think I know: we have to do exec_layout here, because equivalence
     handling could encounter an error that takes a variable off of its
     equivalence object (and vice versa), and we should then layout the var
     as a local entity. */
#endif
}

/* ffestorag_exec_layout -- Do initial layout for symbol

   ffesymbol s;
   ffestorag_exec_layout(s);  */

void
ffestorag_exec_layout (ffesymbol s)
{
  ffetargetAlign alignment;
  ffetargetAlign modulo;
  ffetargetOffset size;
  ffetargetOffset num_elements;
  ffetargetAlign pad;
  ffestorag st;
  ffestorag stv;
  ffebld list;
  ffebld item;
  ffesymbol var;
  bool init;

  if (ffesymbol_storage (s) != NULL)
    return;			/* Already laid out. */

  switch (ffesymbol_kind (s))
    {
    default:
      return;			/* Do nothing. */

    case FFEINFO_kindENTITY:
      switch (ffesymbol_where (s))
	{
	case FFEINFO_whereLOCAL:
	  if (ffesymbol_equiv (s) != NULL)
	    return;		/* Let ffeequiv handle this guy. */
	  if (ffesymbol_rank (s) == 0)
	    num_elements = 1;
	  else
	    num_elements = ffebld_constant_integerdefault (ffebld_conter
						 (ffesymbol_arraysize (s)));
	  ffetarget_layout (ffesymbol_text (s), &alignment, &modulo,
			    &size, ffesymbol_basictype (s),
			    ffesymbol_kindtype (s), ffesymbol_size (s),
			    num_elements);
	  st = ffestorag_new (ffestorag_list_master ());
	  st->parent = NULL;	/* Initializations happen at sym level. */
	  st->init = NULL;
	  st->accretion = NULL;
	  st->symbol = s;
	  st->size = size;
	  st->offset = 0;
	  st->alignment = alignment;
	  st->modulo = modulo;
	  st->type = FFESTORAG_typeLOCAL;
	  st->basic_type = ffesymbol_basictype (s);
	  st->kind_type = ffesymbol_kindtype (s);
	  st->type_symbol = s;
	  st->save = ffesymbol_save (s);
	  ffesymbol_set_storage (s, st);
	  if (ffesymbol_init (s) != NULL)
	    ffecom_notify_init_symbol (s);	/* Init completed before, but
						   we didn't have a storage
						   object for it; maybe back
						   end wants to see the sym
						   again now. */
	  ffesymbol_signal_unreported (s);
	  return;

	case FFEINFO_whereCOMMON:
	  return;		/* Allocate storage for entire common block
				   at once. */

	case FFEINFO_whereDUMMY:
	  return;		/* Don't do anything about dummies for now. */

	case FFEINFO_whereRESULT:
	case FFEINFO_whereIMMEDIATE:
	case FFEINFO_whereCONSTANT:
	case FFEINFO_whereNONE:
	  return;		/* These don't get storage (esp. NONE, which
				   is UNCERTAIN). */

	default:
	  assert ("bad ENTITY where" == NULL);
	  return;
	}
      break;

    case FFEINFO_kindCOMMON:
      assert (ffesymbol_where (s) == FFEINFO_whereLOCAL);
      st = ffestorag_new (ffestorag_list_master ());
      st->parent = NULL;	/* Initializations happen here. */
      st->init = NULL;
      st->accretion = NULL;
      st->symbol = s;
      st->size = 0;
      st->offset = 0;
      st->alignment = 1;
      st->modulo = 0;
      st->type = FFESTORAG_typeCBLOCK;
      if (ffesymbol_commonlist (s) != NULL)
	{
	  var = ffebld_symter (ffebld_head (ffesymbol_commonlist (s)));
	  st->basic_type = ffesymbol_basictype (var);
	  st->kind_type = ffesymbol_kindtype (var);
	  st->type_symbol = var;
	}
      else
	{			/* Special case for empty common area:
				   NONE/NONE means nothing. */
	  st->basic_type = FFEINFO_basictypeNONE;
	  st->kind_type = FFEINFO_kindtypeNONE;
	  st->type_symbol = NULL;
	}
      st->save = ffesymbol_save (s);
      if (!ffe_is_mainprog ())
	ffeglobal_save_common (s,
			       st->save || ffe_is_saveall (),
			       ffesymbol_where_line (s),
			       ffesymbol_where_column (s));
      ffesymbol_set_storage (s, st);

      init = FALSE;
      for (list = ffesymbol_commonlist (s);
	   list != NULL;
	   list = ffebld_trail (list))
	{
	  item = ffebld_head (list);
	  assert (ffebld_op (item) == FFEBLD_opSYMTER);
	  var = ffebld_symter (item);
	  if (ffesymbol_basictype (var) == FFEINFO_basictypeANY)
	    continue;		/* Ignore any symbols that have errors. */
	  if (ffesymbol_rank (var) == 0)
	    num_elements = 1;
	  else
	    num_elements = ffebld_constant_integerdefault (ffebld_conter
					       (ffesymbol_arraysize (var)));
	  ffetarget_layout (ffesymbol_text (var), &alignment, &modulo,
			    &size, ffesymbol_basictype (var),
			    ffesymbol_kindtype (var), ffesymbol_size (var),
			    num_elements);
	  pad = ffetarget_align (&st->alignment, &st->modulo, st->size,
				 alignment, modulo);
	  if (pad != 0)
	    {			/* Warn about padding in the midst of a
				   common area. */
	      char padding[20];

	      sprintf (&padding[0], "%" ffetargetAlign_f "u", pad);
	      ffebad_start (FFEBAD_COMMON_PAD);
	      ffebad_string (padding);
	      ffebad_string (ffesymbol_text (var));
	      ffebad_string (ffesymbol_text (s));
	      ffebad_string ((pad == 1)
			     ? FFECOM_SIZE_UNIT : FFECOM_SIZE_UNITS);
	      ffebad_here (0, ffesymbol_where_line (s), ffesymbol_where_column (s));
	      ffebad_finish ();
	    }
	  stv = ffestorag_new (ffestorag_list_master ());
	  stv->parent = st;	/* Initializations happen in COMMON block. */
	  stv->init = NULL;
	  stv->accretion = NULL;
	  stv->symbol = var;
	  stv->size = size;
	  if (!ffetarget_offset_add (&stv->offset, st->size, pad))
	    {			/* Common block size plus pad, complain if
				   overflow. */
	      ffetarget_offset_overflow (ffesymbol_text (s));
	    }
	  if (!ffetarget_offset_add (&st->size, stv->offset, stv->size))
	    {			/* Adjust size of common block, complain if
				   overflow. */
	      ffetarget_offset_overflow (ffesymbol_text (s));
	    }
	  stv->alignment = alignment;
	  stv->modulo = modulo;
	  stv->type = FFESTORAG_typeCOMMON;
	  stv->basic_type = ffesymbol_basictype (var);
	  stv->kind_type = ffesymbol_kindtype (var);
	  stv->type_symbol = var;
	  stv->save = st->save;
	  ffesymbol_set_storage (var, stv);
	  ffesymbol_signal_unreported (var);
	  ffestorag_update (st, var, ffesymbol_basictype (var),
			    ffesymbol_kindtype (var));
	  if ((ffesymbol_init (var) != NULL)
	      || (ffesymbol_accretion (var) != NULL))
	    init = TRUE;	/* Must move inits over to COMMON's
				   ffestorag. */
	}
      if (ffeequiv_layout_cblock (st))
	init = TRUE;
      ffeglobal_pad_common (s, st->modulo, ffesymbol_where_line (s),
			    ffesymbol_where_column (s));
      if (init)
	ffedata_gather (st);	/* Gather subordinate inits into one init. */
      ffesymbol_signal_unreported (s);
      return;
    }
}

/* ffestorag_new -- Create new ffestorag object, append to list

   ffestorag s;
   ffestoragList sl;
   s = ffestorag_new(sl);  */

ffestorag
ffestorag_new (ffestoragList sl)
{
  ffestorag s;

  s = (ffestorag) malloc_new_kp (ffe_pool_program_unit (), "ffestorag",
				 sizeof (*s));
  s->next = (ffestorag) &sl->first;
  s->previous = sl->last;
#ifdef FFECOM_storageHOOK
  s->hook = FFECOM_storageNULL;
#endif
  s->previous->next = s;
  sl->last = s;
  s->equivs_.first = s->equivs_.last = (ffestorag) &s->equivs_.first;

  return s;
}

/* Report info on LOCAL non-sym-assoc'ed entities if needed.  */

void
ffestorag_report ()
{
  ffestorag s;

  if (ffestorag_reported_)
    return;

  for (s = ffestorag_list_.first;
       s != (ffestorag) &ffestorag_list_.first;
       s = s->next)
    {
      if (s->symbol == NULL)
	{
	  ffestorag_reported_ = TRUE;
	  fputs ("Storage area: ", stdout);
	  ffestorag_dump (s);
	  fputc ('\n', stdout);
	}
    }
}

/* ffestorag_update -- Update type info for ffestorag object

   ffestorag s;	 // existing object
   ffeinfoBasictype bt;	 // basic type for newly added member of object
   ffeinfoKindtype kt;	// kind type for it
   ffestorag_update(s,bt,kt);

   If the existing type for the storage object agrees with the new type
   info, just returns.	If the basic types agree but not the kind types,
   sets the kind type for the object to NONE.  If the basic types
   disagree, sets the kind type to NONE, and the basic type to NONE if the
   basic types both are not CHARACTER, otherwise to ANY.  If the basic
   type for the object already is NONE, it is set to ANY if the new basic
   type is CHARACTER.  Any time a transition is made to ANY and pedantic
   mode is on, a message is issued that mixing CHARACTER and non-CHARACTER
   stuff in the same COMMON/EQUIVALENCE is invalid.  */

void
ffestorag_update (ffestorag s, ffesymbol sym, ffeinfoBasictype bt,
		  ffeinfoKindtype kt)
{
  if (s->basic_type == bt)
    {
      if (s->kind_type == kt)
	return;
      s->kind_type = FFEINFO_kindtypeNONE;
      return;
    }

  switch (s->basic_type)
    {
    case FFEINFO_basictypeANY:
      return;			/* No need to do anything further. */

    case FFEINFO_basictypeCHARACTER:
    any:			/* :::::::::::::::::::: */
      s->basic_type = FFEINFO_basictypeANY;
      s->kind_type = FFEINFO_kindtypeANY;
      if (ffe_is_pedantic ())
	{
	  ffebad_start (FFEBAD_MIXED_TYPES);
	  ffebad_string (ffesymbol_text (s->type_symbol));
	  ffebad_string (ffesymbol_text (sym));
	  ffebad_finish ();
	}
      return;

    default:
      if (bt == FFEINFO_basictypeCHARACTER)
	goto any;		/* :::::::::::::::::::: */
      s->basic_type = FFEINFO_basictypeNONE;
      s->kind_type = FFEINFO_kindtypeNONE;
      return;
    }
}

/* Update SAVE flag for storage object.

   If the SAVE flag for the <s> object is already TRUE, return.	 Else,
   set it to TRUE and call ffe*_update_save for all contained objects.	*/

void
ffestorag_update_save (ffestorag s)
{
  ffestorag sq;

  if (s->save)
    return;

  s->save = TRUE;

  if (s->symbol != NULL)
    ffesymbol_update_save (s->symbol);

  for (sq = s->equivs_.first;
       sq != (ffestorag) &s->equivs_.first;
       sq = ffestorag_next_ (sq))
    ffestorag_update_save (sq);
}
