/* Implement tasking-related actions for CHILL.
   Copyright (C) 1992, 1993, 1994, 1998, 1999, 2000
   Free Software Foundation, Inc.

This file is part of GNU CC.

GNU CC 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 CC 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 CC; see the file COPYING.  If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.  */

#include "config.h"
#include "system.h"
#include "tree.h"
#include "rtl.h"
#include "ch-tree.h"
#include "flags.h"
#include "input.h"
#include "obstack.h"
#include "assert.h"
#include "tasking.h"
#include "lex.h"
#include "toplev.h"

/* from ch-lex.l, from compiler directives */
extern tree process_type;
extern tree send_signal_prio;
extern tree send_buffer_prio;

tree tasking_message_type;
tree instance_type_node;
tree generic_signal_type_node;

/* the type a tasking code variable has */
tree chill_taskingcode_type_node;

/* forward declarations */
#if 0
static void validate_process_parameters		PARAMS ((tree));
static tree get_struct_variable_name		PARAMS ((tree));
static tree decl_tasking_code_variable		PARAMS ((tree, tree *, int));
#endif
static tree get_struct_debug_type_name		PARAMS ((tree));
static tree get_process_wrapper_name		PARAMS ((tree));
static tree build_tasking_enum			PARAMS ((void));
static void build_tasking_message_type		PARAMS ((void));
static tree build_receive_signal_case_label	PARAMS ((tree, tree));
static tree build_receive_buffer_case_label	PARAMS ((tree, tree));
static void build_receive_buffer_case_end	PARAMS ((tree, tree));
static void build_receive_signal_case_end	PARAMS ((tree, tree));

/* list of this module's process, buffer, etc. decls.
 This is a list of TREE_VECs, chain by their TREE_CHAINs. */
tree tasking_list = NULL_TREE;
/* The parts of a tasking_list element. */
#define TASK_INFO_PDECL(NODE) TREE_VEC_ELT(NODE,0)
#define TASK_INFO_ENTRY(NODE) TREE_VEC_ELT(NODE,1)
#define TASK_INFO_CODE_DECL(NODE) TREE_VEC_ELT(NODE,2)
#define TASK_INFO_STUFF_NUM(NODE) TREE_VEC_ELT(NODE,3)
#define TASK_INFO_STUFF_TYPE(NODE) TREE_VEC_ELT(NODE,4)

/* name template for process argument type */
#define STRUCT_NAME "__tmp_%s_arg_type"

/* name template for process arguments for debugging type */
#define STRUCT_DEBUG_NAME "__tmp_%s_debug_type"

/* name template for process argument variable */
#define DATA_NAME  "__tmp_%s_arg_variable"

/* name template for process wrapper */
#define WRAPPER_NAME "__tmp_%s_wrapper"

/* name template for process code */
#define SKELNAME "__tmp_%s_code"

extern int ignoring;
static tree void_ftype_void;
static tree pointer_to_instance;
static tree infinite_buffer_event_length_node;

tree
get_struct_type_name (name)
     tree name;
{
  const char *idp = IDENTIFIER_POINTER (name);        /* process name */
  char *tmpname = xmalloc (strlen (idp) + sizeof (STRUCT_NAME));

  sprintf (tmpname, STRUCT_NAME, idp);
  return get_identifier (tmpname);
}

static tree
get_struct_debug_type_name (name)
     tree name;
{
  const char *idp = IDENTIFIER_POINTER (name);        /* process name */
  char *tmpname = xmalloc (strlen (idp) + sizeof (STRUCT_DEBUG_NAME));

  sprintf (tmpname, STRUCT_DEBUG_NAME, idp);
  return get_identifier (tmpname);
}


tree
get_tasking_code_name (name)
     tree name;
{
  const char *name_str = IDENTIFIER_POINTER (name);
  char *tmpname  = (char *) alloca (IDENTIFIER_LENGTH (name) +
				    sizeof (SKELNAME));
  
  sprintf (tmpname, SKELNAME, name_str);
  return get_identifier (tmpname);
}

#if 0
static tree
get_struct_variable_name (name)
     tree name;
{
  const char *idp = IDENTIFIER_POINTER (name);        /* process name */
  char *tmpname = xmalloc (strlen (idp) + sizeof (DATA_NAME));

  sprintf (tmpname, DATA_NAME, idp);
  return get_identifier (tmpname);
}
#endif

static tree
get_process_wrapper_name (name)
    tree name;
{
  const char *idp = IDENTIFIER_POINTER (name);
  char *tmpname = xmalloc (strlen (idp) + sizeof (WRAPPER_NAME));
    
  sprintf (tmpname, WRAPPER_NAME, idp);
  return get_identifier (tmpname);
}

/*
 * If this is a quasi declaration - parsed within a SPEC MODULE,
 * QUASI_FLAG is TRUE, to indicate that the variable should not
 * be initialized.  The other module will do that.
 */
tree
generate_tasking_code_variable (name, tasking_code_ptr, quasi_flag)
     tree name, *tasking_code_ptr;
     int  quasi_flag;
{

  tree decl;
  tree tasking_code_name = get_tasking_code_name (name);
  
  if (pass == 2 && ! quasi_flag && *tasking_code_ptr != NULL_TREE)
    {
      /* check for value should be assigned is out of range */
      if (TREE_INT_CST_LOW (*tasking_code_ptr) >
	  TREE_INT_CST_LOW (TYPE_MAX_VALUE (chill_taskingcode_type_node)))
	  error ("tasking code %ld out of range for `%s'",
		 (long) TREE_INT_CST_LOW (*tasking_code_ptr),
		 IDENTIFIER_POINTER (name));
    }

  decl = do_decl (tasking_code_name, 
		  chill_taskingcode_type_node, 1, 1,
		  quasi_flag ? NULL_TREE : *tasking_code_ptr, 
		  0);

  /* prevent granting of this type */
  DECL_SOURCE_LINE (decl) = 0;

  if (pass == 2 && ! quasi_flag && *tasking_code_ptr != NULL_TREE)
    *tasking_code_ptr = fold (build (PLUS_EXPR, chill_taskingcode_type_node,
				     integer_one_node,
				     *tasking_code_ptr));
  return decl;
}


/*
 * If this is a quasi declaration - parsed within a SPEC MODULE,
 * QUASI_FLAG is TRUE, to indicate that the variable should not
 * be initialized.  The other module will do that.  This is just 
 * for BUFFERs and EVENTs.
 */
#if 0
static tree
decl_tasking_code_variable (name, tasking_code_ptr, quasi_flag)
     tree name, *tasking_code_ptr;
     int  quasi_flag;
{
  extern struct obstack permanent_obstack;
  tree tasking_code_name = get_tasking_code_name (name);
  tree decl;

  /* guarantee that RTL for the code_variable resides in
     the permanent obstack.  The BUFFER or EVENT may be
     declared in a PROC, not at global scope... */
  push_obstacks (&permanent_obstack, &permanent_obstack);
  push_obstacks_nochange ();

  if (pass == 2 && ! quasi_flag && *tasking_code_ptr != NULL_TREE)
    {
      /* check for value should be assigned is out of range */
      if (TREE_INT_CST_LOW (*tasking_code_ptr) >
	  TREE_INT_CST_LOW (TYPE_MAX_VALUE (chill_taskingcode_type_node)))
	  error ("tasking code %ld out of range for `%s'",
		 (long) TREE_INT_CST_LOW (*tasking_code_ptr),
		 IDENTIFIER_POINTER (name));
    }

  decl = decl_temp1 (tasking_code_name, 
		     chill_taskingcode_type_node, 1,
		     quasi_flag ? NULL_TREE : *tasking_code_ptr, 
		     0, 0);
  /* prevent granting of this type */
  DECL_SOURCE_LINE (decl) = 0;

  /* Return to the ambient context.  */
  pop_obstacks ();

  if (pass == 2 && ! quasi_flag && *tasking_code_ptr != NULL_TREE)
    *tasking_code_ptr = fold (build (PLUS_EXPR, chill_taskingcode_type_node,
				     integer_one_node,
				     *tasking_code_ptr));
  return decl;
}
#endif

/*
 * Transmute a process parameter list into an argument structure 
 * TYPE_DECL for the start_process call to reference.  Create a 
 * proc_type variable for later.  Returns the new struct type.
 */
tree
make_process_struct (name, processparlist)
     tree name, processparlist;
{
  tree temp;
  tree a_parm;
  tree field_decls = NULL_TREE;

  if (name == NULL_TREE || TREE_CODE (name) == ERROR_MARK)
    return error_mark_node;

  if (processparlist == NULL_TREE)
    return tree_cons (NULL_TREE, NULL_TREE, void_list_node);

  if (TREE_CODE (processparlist) == ERROR_MARK)
    return error_mark_node;

  /* build list of field decls for build_chill_struct_type */
  for (a_parm = processparlist; a_parm != NULL_TREE; 
       a_parm = TREE_CHAIN (a_parm))
    {
      tree parnamelist = TREE_VALUE (a_parm);
      tree purpose     = TREE_PURPOSE (a_parm);
      tree mode        = TREE_VALUE (purpose);
      tree parm_attr   = TREE_PURPOSE (purpose);
      tree field;

      /* build a FIELD_DECL node */
      if (parm_attr != NULL_TREE)
	{
	  if (parm_attr == ridpointers[(int)RID_LOC])
	    mode = build_chill_reference_type (mode);
	  else if (parm_attr == ridpointers[(int)RID_IN])
	    ;
	  else if (pass == 1)
	    {
	      for (field = parnamelist; field != NULL_TREE;
		   field = TREE_CHAIN (field))
	        error ("invalid attribute for argument `%s' (only IN or LOC allowed)",
		       IDENTIFIER_POINTER (TREE_VALUE (field)));
	    }
	}

      field = grok_chill_fixedfields (parnamelist, mode, NULL_TREE);

      /* chain the fields in reverse */
      if (field_decls == NULL_TREE)
	field_decls = field;
      else
	chainon (field_decls, field);
    }

  temp = build_chill_struct_type (field_decls);
  return temp;
}

/* Build a function for a PROCESS  and define some
   types for the process arguments.
   After the PROCESS a wrapper function will be 
   generated which gets the PROCESS arguments via a pointer
   to a structure having the same layout as the arguments.
   This wrapper function then will call the PROCESS.
   The advantage in doing it this way is, that PROCESS
   arguments may be displayed by gdb without any change
   to gdb.
*/
tree
build_process_header (plabel, paramlist)
     tree plabel, paramlist;
{
  tree struct_ptr_type = NULL_TREE;
  tree new_param_list = NULL_TREE;
  tree struct_decl = NULL_TREE;
  tree process_struct = NULL_TREE;
  tree struct_debug_type = NULL_TREE;
  tree code_decl;
    
  if (! global_bindings_p ())
    {
      error ("PROCESS may only be declared at module level");
      return error_mark_node;
    }

  if (paramlist)
    {
      /* must make the structure OUTSIDE the parameter scope */
      if (pass == 1)
	{
	  process_struct = make_process_struct (plabel, paramlist);
	  struct_ptr_type = build_chill_pointer_type (process_struct);
	}
      else
	{
	  process_struct = NULL_TREE;
	  struct_ptr_type = NULL_TREE;
	}
		          
      struct_decl = push_modedef (get_struct_type_name (plabel),
				  struct_ptr_type, -1);
      DECL_SOURCE_LINE (struct_decl) = 0;
      struct_debug_type = push_modedef (get_struct_debug_type_name (plabel),
					process_struct, -1);
      DECL_SOURCE_LINE (struct_debug_type) = 0;

      if (pass == 2)
        {
          /* build a list of PARM_DECL's */
          tree  wrk = paramlist;
          tree  tmp, list = NULL_TREE;
          
          while (wrk != NULL_TREE)
            {
              tree wrk1 = TREE_VALUE (wrk);
                
              while (wrk1 != NULL_TREE)
                {
                  tmp = make_node (PARM_DECL);
                  DECL_ASSEMBLER_NAME (tmp) = DECL_NAME (tmp) = TREE_VALUE (wrk1);
                  if (list == NULL_TREE)
                    new_param_list = list = tmp;
                  else
                    {
                      TREE_CHAIN (list) = tmp;
                      list = tmp;
                    }
                  wrk1 = TREE_CHAIN (wrk1);
                }
              wrk = TREE_CHAIN (wrk);
            }
        }
      else
        {
          /* build a list of modes */
          tree  wrk = paramlist;
          
          while (wrk != NULL_TREE)
            {
              tree wrk1 = TREE_VALUE (wrk);
              
              while (wrk1 != NULL_TREE)
                {
                  new_param_list = tree_cons (TREE_PURPOSE (TREE_PURPOSE (wrk)),
                                              TREE_VALUE (TREE_PURPOSE (wrk)),
                                              new_param_list);
                  wrk1 = TREE_CHAIN (wrk1);
                }
              wrk = TREE_CHAIN (wrk);
            }
          new_param_list = nreverse (new_param_list);
        }
    }

  /* declare the code variable outside the process */
  code_decl = generate_tasking_code_variable (plabel, 
					      &process_type, 0);

  /* start the parameter scope */
  push_chill_function_context ();

  if (! start_chill_function (plabel, void_type_node, 
			      new_param_list, NULL_TREE, NULL_TREE))
    return error_mark_node;

  current_module->procedure_seen = 1; 
  CH_DECL_PROCESS (current_function_decl) = 1;
  /* remember the code variable in the function decl */
  DECL_TASKING_CODE_DECL (current_function_decl) = 
    (struct lang_decl *)code_decl;
  if (paramlist == NULL_TREE)
      /* do it here, cause we don't have a wrapper */
    add_taskstuff_to_list (code_decl, "_TT_Process", process_type,
			   current_function_decl, NULL_TREE);

  return perm_tree_cons (code_decl, struct_decl, NULL_TREE);
}

/* Generate a function which gets a pointer
   to an argument block and call the corresponding
   PROCESS
*/
void
build_process_wrapper (plabel, processdata)
    tree        plabel;
    tree        processdata;
{
  tree  args = NULL_TREE;
  tree  wrapper = NULL_TREE;
  tree  parammode = TREE_VALUE (processdata);
  tree  code_decl = TREE_PURPOSE (processdata);
  tree  func = lookup_name (plabel);
    
  /* check the mode. If it is an ERROR_MARK there was an error
     in build_process_header, if it is a NULL_TREE the process
     don't have parameters, so we must not generate a wrapper */
  if (parammode == NULL_TREE ||
      TREE_CODE (parammode) == ERROR_MARK)
    return;
    
  /* get the function name */
  wrapper = get_process_wrapper_name (plabel);
    
  /* build the argument */
  if (pass == 2)
    {
      /* build a PARM_DECL */
      args = make_node (PARM_DECL);
      DECL_ASSEMBLER_NAME (args) = DECL_NAME (args) = get_identifier ("x");
    }
  else
    {
      /* build a tree list with the mode */
      args = tree_cons (NULL_TREE,
                        TREE_TYPE (parammode),
                        NULL_TREE);
    }
    
  /* start the function */
  push_chill_function_context ();
    
  if (! start_chill_function (wrapper, void_type_node,
                              args, NULL_TREE, NULL_TREE))
    return;

  /* to avoid granting */
  DECL_SOURCE_LINE (current_function_decl) = 0;

  if (! ignoring)
    {
      /* make the call to the PROCESS */
      tree      wrk;
      tree      x = lookup_name (get_identifier ("x"));
      /* no need to check this pointer to be NULL */
      tree      indref = build_chill_indirect_ref (x, NULL_TREE, 0);
        
      args = NULL_TREE;
      wrk = TYPE_FIELDS (TREE_TYPE (TREE_TYPE (x)));
      while (wrk != NULL_TREE)
        {
          args = tree_cons (NULL_TREE,
                            build_component_ref (indref, DECL_NAME (wrk)),
                            args);
          wrk = TREE_CHAIN (wrk);
        }
      CH_DECL_PROCESS (func) = 0;
      expand_expr_stmt (
        build_chill_function_call (func, nreverse (args)));
      CH_DECL_PROCESS (func) = 1;
    }

  add_taskstuff_to_list (code_decl, "_TT_Process", process_type,
                         func, current_function_decl);
    
  /* finish the function */
  finish_chill_function ();
  pop_chill_function_context (); 
}

/* Generate errors for INOUT, OUT parameters.

   "Only if LOC is specified may the mode have the non-value
    property"
 */

#if 0
static void
validate_process_parameters (parms)
     tree parms ATTRIBUTE_UNUSED;
{
}
#endif

/*
 * build the tree for a start process action.  Loop through the
 * actual parameters, making a constructor list, which we use to
 * initialize the argument structure.  NAME is the process' name.
 * COPYNUM is its copy number, whatever that is.  EXPRLIST is the
 * list of actual parameters passed by the start call.  They must
 * match. EXPRLIST must still be in reverse order;  we'll reverse it here.
 *
 * Note: the OPTSET name is not now used - it's here for 
 * possible future support for the optional 'SET instance-var'
 * clause.
 */
void
build_start_process (process_name, copynum,
		     exprlist, optset)
     tree process_name, copynum, exprlist, optset;
{
  tree process_decl = NULL_TREE, struct_type_node = NULL_TREE;
  tree result;
  tree valtail, typetail;
  tree tuple = NULL_TREE, actuallist = NULL_TREE;
  tree typelist;
  int  parmno = 2;
  tree args;
  tree filename, linenumber;
  
  if (exprlist != NULL_TREE && TREE_CODE (exprlist) == ERROR_MARK)
    process_decl = NULL_TREE;
  else if (! ignoring)
    {
      process_decl = lookup_name (process_name);
      if (process_decl == NULL_TREE)
	error ("process name %s never declared",
	       IDENTIFIER_POINTER (process_name));
      else if (TREE_CODE (process_decl) != FUNCTION_DECL
	  || ! CH_DECL_PROCESS (process_decl))
	{
	  error ("you may only START a process, not a proc");
	  process_decl = NULL_TREE;
	}
      else if (DECL_EXTERNAL (process_decl))
        {
          args = TYPE_ARG_TYPES (TREE_TYPE (process_decl));
          if (TREE_VALUE (args) != void_type_node)
              struct_type_node = TREE_TYPE (TREE_VALUE (args));
          else
              struct_type_node = NULL_TREE;
        }
      else
        {
          tree  debug_type = lookup_name (
                               get_struct_debug_type_name (DECL_NAME (process_decl)));

          if (debug_type == NULL_TREE)
              /* no debug type, no arguments */
              struct_type_node = NULL_TREE;
          else
              struct_type_node = TREE_TYPE (debug_type);
        }
    }

  /* begin a new name scope */
  pushlevel (1);
  clear_last_expr ();
  push_momentary ();
  if (pass == 2)
    expand_start_bindings (0);

  if (! ignoring && process_decl != NULL_TREE)
    {
      if (optset == NULL_TREE) ;
      else if (!CH_REFERABLE (optset))
	{
	  error ("SET expression not a location");
	  optset = NULL_TREE;
	}
      else if (!CH_IS_INSTANCE_MODE (TREE_TYPE (optset)))
	{
	  error ("SET location must be INSTANCE mode");
	  optset = NULL_TREE;
	}
      if (optset)
	optset = force_addr_of (optset);
      else
	optset = convert (ptr_type_node, integer_zero_node);

      if (struct_type_node != NULL_TREE)
	{
	  typelist = TYPE_FIELDS (struct_type_node);

	  for (valtail = nreverse (exprlist), typetail = typelist;
	       valtail != NULL_TREE && typetail != NULL_TREE;  parmno++,
	       valtail = TREE_CHAIN (valtail), typetail = TREE_CHAIN (typetail))
	    {
	      register tree actual  = valtail  ? TREE_VALUE (valtail)  : 0;
	      register tree type    = typetail ? TREE_TYPE (typetail) : 0;
	      char place[30];
	      sprintf (place, "signal field %d", parmno);
	      actual = chill_convert_for_assignment (type, actual, place);
	      actuallist = tree_cons (NULL_TREE, actual, 
				      actuallist);
	    }

	  tuple = build_nt (CONSTRUCTOR, NULL_TREE, 
			    nreverse (actuallist));
	}
      else
	{
	  valtail = NULL_TREE;
	  typetail = NULL_TREE;
	}
  
      if (valtail != 0 && TREE_VALUE (valtail) != void_type_node)
	{
	  if (process_name)
	    error ("too many arguments to process `%s'",
		   IDENTIFIER_POINTER (process_name));
	  else
	    error ("too many arguments to process");
	}
      else if (typetail != 0 && TREE_VALUE (typetail) != void_type_node)
	{
	  if (process_name)
	    error ("too few arguments to process `%s'",
		   IDENTIFIER_POINTER (process_name));
	  else
	    error ("too few arguments to process");
	}
      else
      {
	tree process_decl = lookup_name (process_name);
	tree process_type = (tree)DECL_TASKING_CODE_DECL (process_decl);
	tree struct_size, struct_pointer;
	
	if (struct_type_node != NULL_TREE)
	  {
	    result = 
	      decl_temp1 (get_unique_identifier ("START_ARG"),
			  struct_type_node, 0, tuple, 0, 0);
	    /* prevent granting of this type */
	    DECL_SOURCE_LINE (result) = 0;

	    mark_addressable (result);
	    struct_pointer
	      = build1 (ADDR_EXPR,
			build_chill_pointer_type (struct_type_node),
			result);
	    struct_size = size_in_bytes (struct_type_node);
	  }
	else
	  {
	    struct_size = integer_zero_node;
	    struct_pointer = null_pointer_node;
	  }

	filename = force_addr_of (get_chill_filename ());
	linenumber = get_chill_linenumber ();
	
	expand_expr_stmt (
          build_chill_function_call (lookup_name (get_identifier ("__start_process")),
	    tree_cons (NULL_TREE, process_type,
              tree_cons (NULL_TREE, convert (integer_type_node, copynum),
		tree_cons (NULL_TREE, struct_size,
		  tree_cons (NULL_TREE, struct_pointer,
		    tree_cons (NULL_TREE, optset,
		      tree_cons (NULL_TREE, filename,
		        build_tree_list (NULL_TREE, linenumber)))))))));
      }
    }
  /* end of scope */

  if (pass == 2)
    expand_end_bindings (getdecls (), kept_level_p (), 0);
  poplevel (kept_level_p (), 0, 0);
  pop_momentary ();
}

/*
 * A CHILL SET which represents all of the possible tasking
 * elements.
 */
static tree
build_tasking_enum ()
{
  tree result, decl1;
  tree enum1;
  tree list = NULL_TREE;
  tree value = integer_zero_node;

  enum1  = start_enum (NULL_TREE);
  result = build_enumerator (get_identifier ("_TT_UNUSED"),
			     value);
  list = chainon (result, list);
  value = fold (build (PLUS_EXPR, integer_type_node,
		       value, integer_one_node));
		      
  result = build_enumerator (get_identifier ("_TT_Process"),
			     value);
  list = chainon (result, list);
  value = fold (build (PLUS_EXPR, integer_type_node,
		       value, integer_one_node));
		      
  result = build_enumerator (get_identifier ("_TT_Signal"),
			     value);
  list = chainon (result, list);
  value = fold (build (PLUS_EXPR, integer_type_node,
		       value, integer_one_node));

  result = build_enumerator (get_identifier ("_TT_Buffer"),
			     value);
  list = chainon (result, list);
  value = fold (build (PLUS_EXPR, integer_type_node,
		       value, integer_one_node));
  
  result = build_enumerator (get_identifier ("_TT_Event"),
			     value);
  list = chainon (result, list);
  value = fold (build (PLUS_EXPR, integer_type_node,
		       value, integer_one_node));

  result = build_enumerator (get_identifier ("_TT_Synonym"),
			     value);
  list = chainon (result, list);
  value = fold (build (PLUS_EXPR, integer_type_node,
		       value, integer_one_node));
  
  result = build_enumerator (get_identifier ("_TT_Exception"),
			     value);
  list = chainon (result, list);  
  value = fold (build (PLUS_EXPR, integer_type_node,
		       value, integer_one_node));

  result = finish_enum (enum1, list); 

  decl1 = build_decl (TYPE_DECL, 
		      get_identifier ("__tmp_TaskingEnum"),
		      result);
  pushdecl (decl1);
  satisfy_decl (decl1, 0);
  return decl1;
}

tree
build_tasking_struct ()
{  
  tree listbase, decl1, decl2, result;
  tree enum_type = TREE_TYPE (build_tasking_enum ());
  /* We temporarily reset the maximum_field_alignment to zero so the
     compiler's init data structures can be compatible with the
     run-time system, even when we're compiling with -fpack. */
  unsigned int save_maximum_field_alignment = maximum_field_alignment;
  maximum_field_alignment = 0;

  decl1 = build_decl (FIELD_DECL, get_identifier ("TaskName"),
		      build_chill_pointer_type (char_type_node));
  DECL_INITIAL (decl1) = NULL_TREE;
  listbase = decl1;

  decl2 = build_decl (FIELD_DECL, get_identifier ("TaskValue"),
		      build_chill_pointer_type (chill_taskingcode_type_node));
  TREE_CHAIN (decl1) = decl2;
  DECL_INITIAL (decl2) = NULL_TREE;
  decl1 = decl2;

  decl2 = build_decl (FIELD_DECL, get_identifier ("TaskValueDefined"),
                      integer_type_node);
  TREE_CHAIN (decl1) = decl2;
  DECL_INITIAL (decl2) = NULL_TREE;
  decl1 = decl2;

  decl2 = build_decl (FIELD_DECL, get_identifier ("TaskEntry"),
                      build_chill_pointer_type (void_ftype_void));
  TREE_CHAIN (decl1) = decl2;
  DECL_INITIAL (decl2) = NULL_TREE;
  decl1 = decl2;

  decl2 = build_decl (FIELD_DECL, get_identifier ("TaskType"),
		      enum_type);
  TREE_CHAIN (decl1) = decl2;
  DECL_INITIAL (decl2) = NULL_TREE;
  decl1 = decl2;

  TREE_CHAIN (decl2) = NULL_TREE;
  result = build_chill_struct_type (listbase);
  satisfy_decl (result, 0);
  maximum_field_alignment = save_maximum_field_alignment;
  return result;
}

/*
 * build data structures describing each task/signal, etc.
 * in current module.
 */
void
tasking_setup ()
{
  tree tasknode;
  tree struct_type;

  if (pass == 1)
    return;

  struct_type = TREE_TYPE (lookup_name (
		  get_identifier ("__tmp_TaskingStruct")));

  for (tasknode = tasking_list; tasknode != NULL_TREE; 
       tasknode = TREE_CHAIN (tasknode))
    {
      /* This is the tasking_code_variable's decl */
      tree stuffnumber = TASK_INFO_STUFF_NUM (tasknode);
      tree code_decl   = TASK_INFO_CODE_DECL (tasknode);
      tree proc_decl   = TASK_INFO_PDECL (tasknode);
      tree entry       = TASK_INFO_ENTRY (tasknode);
      tree name = DECL_NAME (proc_decl);
      char *init_struct = (char *) alloca (IDENTIFIER_LENGTH(name) + 20);
      /* take care of zero termination */
      tree task_name;
      /* these are the fields of the struct, in declaration order */
      tree init_flag = (stuffnumber == NULL_TREE) ? 
	integer_zero_node : integer_one_node;
      tree type = DECL_INITIAL (TASK_INFO_STUFF_TYPE (tasknode));
      tree int_addr;
      tree entry_point;
      tree name_ptr;
      tree decl;
      tree struct_id;
      tree initializer;
      
      if (TREE_CODE (proc_decl) == FUNCTION_DECL
	  && CH_DECL_PROCESS (proc_decl) 
	  && ! DECL_EXTERNAL (proc_decl))
        {
          if (entry == NULL_TREE)
	    entry = proc_decl;
	  mark_addressable (entry);
	  entry_point = build1 (ADDR_EXPR, 
				build_chill_pointer_type (void_ftype_void),
				entry);
        }
      else
	entry_point = build1 (NOP_EXPR, 
                        build_chill_pointer_type (void_ftype_void), 
                          null_pointer_node);

      /* take care of zero termination */
      task_name = 
	build_chill_string (IDENTIFIER_LENGTH (name) + 1,
			    IDENTIFIER_POINTER (name));

      mark_addressable (code_decl);
      int_addr = build1 (ADDR_EXPR,
			 build_chill_pointer_type (chill_integer_type_node),
			 code_decl);

      mark_addressable (task_name);
      name_ptr = build1 (ADDR_EXPR,
		   build_chill_pointer_type (char_type_node), 
		     task_name);

      sprintf (init_struct, "__tmp_%s_struct", 
	       IDENTIFIER_POINTER (name));

      struct_id = get_identifier (init_struct);
      initializer = build (CONSTRUCTOR, struct_type, NULL_TREE,
		      tree_cons (NULL_TREE, name_ptr,
                        tree_cons (NULL_TREE, int_addr,
                          tree_cons (NULL_TREE, init_flag,
                            tree_cons (NULL_TREE, entry_point,
                              tree_cons (NULL_TREE, type, NULL_TREE))))));
      TREE_CONSTANT (initializer) = 1;
      decl = decl_temp1 (struct_id, struct_type, 1, initializer, 0, 0);
      /* prevent granting of this type */
      DECL_SOURCE_LINE (decl) = 0;

      /* pass the decl to tasking_registry() in the symbol table */
      IDENTIFIER_LOCAL_VALUE (struct_id) = decl;
    }
}


/*
 * Generate code to register the tasking-related stuff
 * with the runtime.  Only in pass 2.
 */
void
tasking_registry ()
{
  tree tasknode, fn_decl;

  if (pass == 1)
    return;

  fn_decl = lookup_name (get_identifier ("__register_tasking"));

  for (tasknode = tasking_list; tasknode != NULL_TREE; 
       tasknode = TREE_CHAIN (tasknode))
    {
      tree proc_decl = TASK_INFO_PDECL (tasknode);
      tree name = DECL_NAME (proc_decl);
      tree arg_decl;
      char *init_struct = (char *) alloca (IDENTIFIER_LENGTH (name) + 20);

      sprintf (init_struct, "__tmp_%s_struct", 
	       IDENTIFIER_POINTER (name));
      arg_decl = lookup_name (get_identifier (init_struct));

      expand_expr_stmt (
        build_chill_function_call (fn_decl,
	  build_tree_list (NULL_TREE, force_addr_of (arg_decl))));
    }
}

/*
 * Put a tasking entity (a PROCESS, or SIGNAL) onto
 * the list for tasking_setup (). CODE_DECL is the integer code
 * variable's DECL, which describes the shadow integer which 
 * accompanies each tasking entity.  STUFFTYPE is a string
 * representing the sort of tasking entity we have here (i.e. 
 * process, signal, etc.).  STUFFNUMBER is an enumeration
 * value saying the same thing.  PROC_DECL is the declaration of
 * the entity.  It's a FUNCTION_DECL if the entity is a PROCESS, it's
 * a TYPE_DECL if the entity is a SIGNAL.
 */
void
add_taskstuff_to_list (code_decl, stufftype, stuffnumber,
		       proc_decl, entry)
     tree code_decl;
     const char *stufftype;
     tree stuffnumber, proc_decl, entry;
{
  if (pass == 1)
    /* tell chill_finish_compile that there's
       task-level code to be processed. */
    tasking_list = integer_one_node;

  /* do only in pass 2 so we know in chill_finish_compile whether
     to generate a constructor function, and to avoid double the
     correct number of entries. */
  else /* pass == 2 */
    {
      tree task_node = make_tree_vec (5);
      TASK_INFO_PDECL (task_node) = proc_decl;
      TASK_INFO_ENTRY (task_node) = entry;
      TASK_INFO_CODE_DECL (task_node) = code_decl;
      TASK_INFO_STUFF_NUM (task_node) = stuffnumber;
      TASK_INFO_STUFF_TYPE (task_node)
	= lookup_name (get_identifier (stufftype));
      TREE_CHAIN (task_node) = tasking_list;
      tasking_list = task_node;
    }
}

/*
 * These next routines are called out of build_generalized_call
 */
tree
build_copy_number (instance_expr)
     tree instance_expr;
{
  tree result;

  if (instance_expr == NULL_TREE 
      || TREE_CODE (instance_expr) == ERROR_MARK)
    return error_mark_node;
  if (! CH_IS_INSTANCE_MODE (TREE_TYPE (instance_expr)))
    {
      error ("COPY_NUMBER argument must be INSTANCE expression");
      return error_mark_node;
    }
  result = build_component_ref (instance_expr,
				get_identifier (INS_COPY));
  CH_DERIVED_FLAG (result) = 1;
  return result;
}


tree
build_gen_code (decl)
     tree decl;
{
  tree result;

  if (decl == NULL_TREE || TREE_CODE (decl) == ERROR_MARK)
    return error_mark_node;

  if ((TREE_CODE (decl) == FUNCTION_DECL && CH_DECL_PROCESS (decl))
      || (TREE_CODE (decl) == TYPE_DECL && CH_DECL_SIGNAL (decl)))
    result = (tree)(DECL_TASKING_CODE_DECL (decl));
  else
    {
      error ("GEN_CODE argument must be a process or signal name");
      return error_mark_node;
    }
  CH_DERIVED_FLAG (result) = 1;
  return (result);
}


tree
build_gen_inst (process, copyn)
     tree process, copyn;
{
  tree ptype;
  tree result;

  if (copyn == NULL_TREE || TREE_CODE (copyn) == ERROR_MARK)
    return error_mark_node;
  if (process == NULL_TREE || TREE_CODE (process) == ERROR_MARK)
    return error_mark_node;

  if (TREE_CODE (TREE_TYPE (copyn)) != INTEGER_TYPE)
    {
      error ("GEN_INST parameter 2 must be an integer mode");
      copyn = integer_zero_node;
    }

  copyn = check_range (copyn, copyn, 
		       TYPE_MIN_VALUE (chill_taskingcode_type_node),
		       TYPE_MAX_VALUE (chill_taskingcode_type_node));

  if (TREE_CODE (process) == FUNCTION_DECL
      && CH_DECL_PROCESS (process))
    ptype = (tree)DECL_TASKING_CODE_DECL (process);
  else if (TREE_TYPE (process) != NULL_TREE
	   && TREE_CODE (TREE_TYPE (process)) == INTEGER_TYPE)
    {
      process = check_range (process, process, 
			     TYPE_MIN_VALUE (chill_taskingcode_type_node),
			     TYPE_MAX_VALUE (chill_taskingcode_type_node));
      ptype = convert (chill_taskingcode_type_node, process);
    }
  else
    {
      error ("GEN_INST parameter 1 must be a PROCESS or an integer expression");
      return (error_mark_node);
    }
  
  result = convert (instance_type_node,
	     build_nt (CONSTRUCTOR, NULL_TREE,
	       tree_cons (NULL_TREE, ptype,
	         tree_cons (NULL_TREE, 
	           convert (chill_taskingcode_type_node, copyn), NULL_TREE))));
  CH_DERIVED_FLAG (result) = 1;
  return result;
}


tree
build_gen_ptype (process_decl)
     tree process_decl;
{
  tree result;

  if (process_decl == NULL_TREE || TREE_CODE (process_decl) == ERROR_MARK)
    return error_mark_node;

  if (TREE_CODE (process_decl) != FUNCTION_DECL
      || ! CH_DECL_PROCESS (process_decl))
    {
      error_with_decl (process_decl, "%s is not a declared process");
      return error_mark_node;
    }

  result = (tree)DECL_TASKING_CODE_DECL (process_decl);
  CH_DERIVED_FLAG (result) = 1;
  return result;
}


tree
build_proc_type (instance_expr)
     tree instance_expr;
{
  tree result;

  if (instance_expr == NULL_TREE || TREE_CODE (instance_expr) == ERROR_MARK)
    return error_mark_node;

  if (! CH_IS_INSTANCE_MODE (TREE_TYPE (instance_expr)))
    {
      error ("PROC_TYPE argument must be INSTANCE expression");
      return error_mark_node;
    }
  result = build_component_ref (instance_expr,
				get_identifier (INS_PTYPE));
  CH_DERIVED_FLAG (result) = 1;
  return result;
}

tree
build_queue_length (buf_ev)
     tree buf_ev;
{
  if (buf_ev == NULL_TREE || TREE_CODE (buf_ev) == ERROR_MARK)
    return error_mark_node;
  if (TREE_TYPE (buf_ev) == NULL_TREE ||
      TREE_CODE (TREE_TYPE (buf_ev)) == ERROR_MARK)
    return error_mark_node;

  if (CH_IS_BUFFER_MODE (TREE_TYPE (buf_ev)) ||
      CH_IS_EVENT_MODE (TREE_TYPE (buf_ev)))
    {
      const char *field_name;
      tree  arg1, arg2;

      if (CH_IS_EVENT_MODE (TREE_TYPE (buf_ev)))
	{
	  field_name = "__event_data";
	  arg2 = integer_one_node;
	}
      else
	{
	  field_name = "__buffer_data";
	  arg2 = integer_zero_node;
	}
      arg1 = build_component_ref (buf_ev, get_identifier (field_name));
      return build_chill_function_call (
                lookup_name (get_identifier ("__queue_length")),
                   tree_cons (NULL_TREE, arg1,
                      tree_cons (NULL_TREE, arg2, NULL_TREE)));
    }

  error ("QUEUE_LENGTH argument must be a BUFFER/EVENT location");
  return error_mark_node;
}

tree
build_signal_struct_type (signame, sigmodelist, optsigdest)
     tree signame, sigmodelist, optsigdest;
{
  tree decl, temp;

  if (pass == 1)
    {
      int  fldcnt = 0;
      tree mode, field_decls = NULL_TREE;

      for (mode = sigmodelist; mode != NULL_TREE; mode = TREE_CHAIN (mode))
	{ 
	  tree field;
	  char fldname[20];
      
	  if (TREE_VALUE (mode) == NULL_TREE)
	    continue;
	  sprintf (fldname, "fld%03d", fldcnt++);
	  field = build_decl (FIELD_DECL,
			      get_identifier (fldname),
			      TREE_VALUE (mode));
	  if (field_decls == NULL_TREE)
	    field_decls = field;
	  else
	    chainon (field_decls, field);
	}
      if (field_decls == NULL_TREE)
	field_decls = build_decl (FIELD_DECL,
				  get_identifier ("__tmp_empty"),
				  boolean_type_node); 
      temp = build_chill_struct_type (field_decls);

      /* save the destination process name of the signal */
      IDENTIFIER_SIGNAL_DEST (signame) = optsigdest;
      IDENTIFIER_SIGNAL_DATA (signame) = fldcnt;
    }
  else
    {
      /* optsigset is only valid in pass 2, so we have to save it now */
      IDENTIFIER_SIGNAL_DEST (signame) = optsigdest;
      temp = NULL_TREE; /* Actually, don't care. */
    }
  
  decl = push_modedef (signame, temp, -1);
  if (decl != NULL_TREE)
    CH_DECL_SIGNAL (decl) = 1;
  return decl;
}

/*
 * An instance type is a unique process identifier in the CHILL
 * tasking arena.  It consists of a process type and a copy number.
 */
void
build_instance_type ()
{
  tree decl1, decl2, tdecl;

  decl1 = build_decl (FIELD_DECL, get_identifier (INS_PTYPE), 
		      chill_taskingcode_type_node);

  TREE_CHAIN (decl1) = decl2 =
    build_decl (FIELD_DECL, get_identifier (INS_COPY), 
		chill_taskingcode_type_node);
  TREE_CHAIN (decl2) = NULL_TREE;

  instance_type_node = build_chill_struct_type (decl1);
  tdecl = build_decl (TYPE_DECL, ridpointers[(int) RID_INSTANCE],
		      instance_type_node);
  TYPE_NAME (instance_type_node) = tdecl;
  CH_NOVELTY (instance_type_node) = tdecl;
  DECL_SOURCE_LINE (tdecl) = 0;
  pushdecl (tdecl);

  pointer_to_instance = build_chill_pointer_type (instance_type_node);
}

/*
 *
 * The tasking message descriptor looks like this C structure:
 *
 * typedef struct
 *   {
 *     short *sc;                 // ptr to code integer
 *     int    data_len;           // length of signal/buffer data msg
 *     void  *data;               // ptr to signal/buffer data
 *   } SignalDescr;
 *
 *
 */

static void
build_tasking_message_type ()
{
  tree type_name;
  tree temp;
  /* We temporarily reset maximum_field_alignment to deal with
     the runtime system. */
  unsigned int save_maximum_field_alignment = maximum_field_alignment;
  tree field1, field2, field3;

  maximum_field_alignment = 0;
  field1 = build_decl (FIELD_DECL, 
		       get_identifier ("_SD_code_ptr"),
		       build_pointer_type (chill_integer_type_node));
  field2 = build_decl (FIELD_DECL,
		       get_identifier ("_SD_data_len"),
		       integer_type_node);
  field3 = build_decl (FIELD_DECL,
		       get_identifier ("_SD_data_ptr"),
		       ptr_type_node);
  TREE_CHAIN (field1) = field2;
  TREE_CHAIN (field2) = field3;
  temp = build_chill_struct_type (field1);
  
  type_name = get_identifier ("__tmp_SD_struct");
  tasking_message_type = build_decl (TYPE_DECL, type_name, temp);

  /* This won't get seen in pass 2, so lay it out now.  */
  layout_chill_struct_type (temp);
  pushdecl (tasking_message_type);
  maximum_field_alignment = save_maximum_field_alignment;
}

tree
build_signal_descriptor (sigdef, exprlist)
     tree sigdef, exprlist;
{
  tree fieldlist, typetail, valtail;
  tree actuallist = NULL_TREE;
  tree signame = DECL_NAME (sigdef);
  tree dataptr, datalen;
  int  parmno = 1;

  if (sigdef == NULL_TREE
      || TREE_CODE (sigdef) == ERROR_MARK)
    return error_mark_node;

  if (exprlist != NULL_TREE
      && TREE_CODE (exprlist) == ERROR_MARK)
    return error_mark_node;

  if (TREE_CODE (sigdef) != TYPE_DECL
      || ! CH_DECL_SIGNAL (sigdef))
    {
      error ("SEND requires a SIGNAL; %s is not a SIGNAL name", 
	     IDENTIFIER_POINTER (signame));
      return error_mark_node;
    }
  if (CH_TYPE_NONVALUE_P (TREE_TYPE (sigdef)))
    return error_mark_node;

  fieldlist = TYPE_FIELDS (TREE_TYPE (sigdef));
  if (IDENTIFIER_SIGNAL_DATA (signame) == 0)
    fieldlist = TREE_CHAIN (fieldlist);

  for (valtail = exprlist, typetail = fieldlist;
       valtail != NULL_TREE && typetail != NULL_TREE;  
       parmno++, valtail = TREE_CHAIN (valtail),
       typetail = TREE_CHAIN (typetail))
    {
      register tree actual  = valtail  ? TREE_VALUE (valtail) : 0;
      register tree type    = typetail ? TREE_TYPE (typetail) : 0;
      char place[30];
      sprintf (place, "signal field %d", parmno);
      actual = chill_convert_for_assignment (type, actual, place);
      actuallist = tree_cons (NULL_TREE,  actual, actuallist);
    }
  if (valtail != 0 && TREE_VALUE (valtail) != void_type_node)
    {
      error ("too many values for SIGNAL `%s'",
	     IDENTIFIER_POINTER (signame));
      return error_mark_node;
    }
  else if (typetail != 0 && TREE_VALUE (typetail) != void_type_node)
    {
      error ("too few values for SIGNAL `%s'",
	   IDENTIFIER_POINTER (signame));
      return error_mark_node;
    }

  {
    /* build signal data structure */
    tree sigdataname = get_unique_identifier (
                         IDENTIFIER_POINTER (signame));
    if (exprlist == NULL_TREE)
      {
	dataptr = null_pointer_node;
	datalen = integer_zero_node;
      }
    else
      {
	tree tuple = build_nt (CONSTRUCTOR,
		       NULL_TREE, nreverse (actuallist));
	tree decl = decl_temp1 (sigdataname, TREE_TYPE (sigdef), 
			   0, tuple, 0, 0);
	/* prevent granting of this type */
	DECL_SOURCE_LINE (decl) = 0;

	dataptr = force_addr_of (decl);
	datalen = size_in_bytes (TREE_TYPE (decl));
      }
    
    /* build descriptor pointing to signal data */
    {
      tree decl, tuple;
      tree tasking_message_var = get_unique_identifier (
                                   IDENTIFIER_POINTER (signame));

      tree tasking_code = 
	(tree)DECL_TASKING_CODE_DECL (lookup_name (signame));

      mark_addressable (tasking_code);
      tuple = build_nt (CONSTRUCTOR, NULL_TREE,
		tree_cons (NULL_TREE, 
		  build1 (ADDR_EXPR, 
		    build_chill_pointer_type (chill_integer_type_node), 
			  tasking_code),
		      tree_cons (NULL_TREE, datalen,
		        tree_cons (NULL_TREE, dataptr, NULL_TREE))));
			      
      decl = decl_temp1 (tasking_message_var,
			 TREE_TYPE (tasking_message_type), 0,
			 tuple, 0, 0);
      /* prevent granting of this type */
      DECL_SOURCE_LINE (decl) = 0;

      tuple = force_addr_of (decl);
      return tuple;
    }
  }
}

void
expand_send_signal (sigmsgbuffer, optroutinginfo, optsendto,
		   optpriority, signame)
     tree sigmsgbuffer;
     tree optroutinginfo;
     tree optsendto;
     tree optpriority;
     tree signame;
{
  tree routing_size, routing_addr;
  tree filename, linenumber;
  tree sigdest = IDENTIFIER_SIGNAL_DEST (signame);

  /* check the presence of priority */
  if (optpriority == NULL_TREE)
    {
      if (send_signal_prio == NULL_TREE)
	{
	  /* issue a warning in case of -Wall */
	  if (extra_warnings)
	    {
	      warning ("signal sent without priority");
	      warning (" and no default priority was set.");
	      warning (" PRIORITY defaulted to 0");
	    }
	  optpriority = integer_zero_node;
	}
      else
	optpriority = send_signal_prio;
    }

  /* check the presence of a destination.
     optdest either may be an instance location
     or a process declaration */
  if (optsendto == NULL_TREE)
    {
      if (sigdest == NULL_TREE)
        {
	  error ("SEND without a destination instance");
	  error (" and no destination process specified");
	  error (" for the signal");
	  optsendto = convert (instance_type_node,
			       null_pointer_node);
        }
      else
        {
	  /* build an instance [sigdest; -1] */
	  tree process_name = DECL_NAME (sigdest);
	  tree copy_number = fold (build (MINUS_EXPR, integer_type_node,
					  integer_zero_node,
					  integer_one_node));
	  tree tasking_code = (tree)DECL_TASKING_CODE_DECL (
                                lookup_name (process_name));

	  optsendto = build (CONSTRUCTOR, instance_type_node, NULL_TREE,
                        tree_cons (NULL_TREE, tasking_code,
                          tree_cons (NULL_TREE, copy_number, NULL_TREE)));
	  /* as our system doesn't allow that and Z.200 specifies it,
	     we issue a warning */
	  warning ("SEND to ANY copy of process `%s'", IDENTIFIER_POINTER (process_name));
        }
    }
  else if (! CH_IS_INSTANCE_MODE (TREE_TYPE (optsendto)))
    {
      error ("SEND TO must be an INSTANCE mode");
      optsendto = convert (instance_type_node, null_pointer_node);
    }
  else
    optsendto = check_non_null (convert (instance_type_node, optsendto));

  /* check the routing stuff */
  if (optroutinginfo != NULL_TREE)
    {
      tree routing_name;
      tree decl;

      if (TREE_TYPE (optroutinginfo) == NULL_TREE)
	{
	  error ("SEND WITH must have a mode");
	  optroutinginfo = integer_zero_node;
	}
      routing_name = get_unique_identifier ("RI");
      decl = decl_temp1 (routing_name,
			 TREE_TYPE (optroutinginfo), 0,
			 optroutinginfo, 0, 0);
      /* prevent granting of this type */
      DECL_SOURCE_LINE (decl) = 0;

      routing_addr = force_addr_of (decl);
      routing_size = size_in_bytes (TREE_TYPE (decl));
    }
  else
    {
      routing_size = integer_zero_node;
      routing_addr = null_pointer_node;
    }
  /* get filename and linenumber */
  filename = force_addr_of (get_chill_filename ());
  linenumber = get_chill_linenumber ();
  
  /* Now (at last!) we can call the runtime */
  expand_expr_stmt (
    build_chill_function_call (lookup_name (get_identifier ("__send_signal")),
      tree_cons (NULL_TREE, sigmsgbuffer,
        tree_cons (NULL_TREE, optsendto,
	  tree_cons (NULL_TREE, optpriority,
	    tree_cons (NULL_TREE, routing_size,
	      tree_cons (NULL_TREE, routing_addr,
                tree_cons (NULL_TREE, filename,
		  tree_cons (NULL_TREE, linenumber, NULL_TREE)))))))));
}

/*
 * The following code builds a RECEIVE CASE action, which actually
 * has 2 different functionalities:
 *
 * 1) RECEIVE signal CASE action
 *   which looks like this:
 *
 *    SIGNAL advance;
 *    SIGNAL terminate = (CHAR);
 *    SIGNAL sig1 = (CHAR);
 *
 *    DCL user, system INSTANCE;
 *    DCL count INT, char_code CHAR;
 *    DCL instance_loc INSTANCE;
 *
 *    workloop:
 *      RECEIVE CASE SET instance_loc;
 *        (advance): 
 *           count + := 1;
 *        (terminate IN char_code):
 *           SEND sig1(char_code) TO system;
 *           EXIT workloop; 
 *      ELSE 
 *        STOP;
 *      ESAC;
 *
 * Because we don't know until we get to the ESAC how
 * many signals need processing, we generate the following
 * C-equivalent code:
 *
 * // define the codes for the signals
 * static short __tmp_advance_code;
 * static short __tmp_terminate_code;
 * static short __tmp_sig1_code;
 *
 * // define the types of the signals
 * typedef struct
 *  {
 *     char fld0;
 *  } __tmp_terminate_struct;
 *
 * typedef struct
 *  {
 *     char fld0;
 *  } __tmp_sig1_struct;
 *
 * static INSTANCE user, system, instance_loc;
 * static short count;
 * static char char_code;
 *
 * {               // start a new symbol context
 *   int    number_of_sigs;
 *   short *sig_code [];
 *   void  *sigdatabuf;
 *   int    sigdatalen;
 *   short  sigcode;
 *
 *   goto __rcsetup;
 *
 *  __rcdoit: ;
 *   int timedout = __wait_signal (&sigcode
 *                                 number_of_sigs,
 *                                 sig_code,
 *                                 sigdatabuf,
 *                                 sigdatalen,
 *                                 &instance_loc);
 *   if (sigcode == __tmp_advance_code)
 *     {
 *       // code for advance alternative's action_statement_list
 *       count++;
 *     }
 *   else if (sigcode == __tmp_terminate_code)
 *     {
 *        // copy signal's data to where they belong,
 *           with range-check, if enabled
 *        char_code = ((__tmp_terminate_struct *)sigdatabuf)->fld0;
 *
 *       // code for terminate alternative's action_statement_list
 *        __send_signal (sig1 ..... );
 *        goto __workloop_end;
 *     }
 *   else
 *     {
 *        // code here for the ELSE action_statement_list
 *        __stop_process ();
 *     }
 *   goto __rc_done;
 *
 * __rcsetup:
 *   union { __tmp_terminate_struct terminate; 
 *	     __tmp_sig1_struct } databuf;
 *   short *sig_code_ptr [2] = { &__tmp_advance_code,
 *                               &__tmp_terminate_code };
 *   sigdatabuf = &databuf;
 *   sigdatalen = sizeof (databuf);
 *   sig_code = &sig_code_ptr[0];
 *   number_of_sigs = 2;
 *   goto __rcdoit;
 *
 * __rc_done: ;
 * }               // end the new symbol context
 * __workloop_end: ;
 *
 *
 * 2) RECEIVE buffer CASE action:
 *   which looks like this:
 *
 *    NEWMODE m_s = STRUCT (mini INT, maxi INT);
 *    DCL b1 BUFFER INT;
 *    DCL b2 BUFFER (30) s;
 *
 *    DCL i INT, s m_s, ins INSTANCE;
 *    DCL count INT;
 *
 *    workloop:
 *      RECEIVE CASE SET ins;
 *        (b1 IN i):
 *          count +:= i;
 *        (b2 in s):
 *          IF count < s.mini OR count > s.maxi THEN
 *            EXIT workloop;
 *          FI;
 *        ELSE
 *          STOP;
 *      ESAC;
 *
 * Because we don't know until we get to the ESAC how
 * many buffers need processing, we generate the following
 * C-equivalent code:
 *
 * typedef struct
 * {
 *    short mini;
 *    short maxi;
 * } m_s;
 *
 * static void *b1;
 * static void *b2;
 * static short i;
 * static m_s s;
 * static INSTANCE ins;
 * static short count;
 *
 * workloop:
 * {                     // start a new symbol context
 *   int     number_of_sigs;
 *   void   *sig_code [];
 *   void   *sigdatabuf;
 *   int     sigdatalen;
 *   void   *buflocation;
 *   int     timedout;
 *
 *   goto __rcsetup;
 *
 *  __rcdoit:
 *   timedout = __wait_buffer (&buflocation,
 *                             number_of_sigs,
 *                             sig_code,
 *                             sigdatabuf,
 *                             sigdatalen,
 *                             &ins, ...);
 *   if (buflocation == &b1)
 *     {
 *       i = ((short *)sigdatabuf)->fld0;
 *       count += i;
 *     }
 *   else if (buflocation == &b2)
 *     {
 *       s = ((m_s)*sigdatabuf)->fld1;
 *       if (count < s.mini || count > s.maxi)
 *         goto __workloop_end;
 *     }
 *   else
 *       __stop_process ();
 *   goto __rc_done;
 *
 *  __rcsetup:
 *   typedef struct
 *   {
 *      void      *p;
 *      unsigned   maxqueuesize;
 *   } Buffer_Descr;
 *   union { short    b1,
 *           m_s      b2 } databuf;
 *   Buffer_Descr bufptr [2] =
 *       {
 *         { &b1, -1 },
 *         { &b2, 30 },
 *       };
 *   void * bufarray[2] = { &bufptr[0],
 *                          &bufptr[1] };
 *   sigdatabuf = &databuf;
 *   sigdatalen = sizeof (databuf);
 *   sig_code = &bufarray[0];
 *   number_of_sigs = 2;
 *   goto __rcdoit;
 *
 *  __rc_done;
 * }          // end of symbol context
 * __workloop_end:
 *
 */

struct rc_state_type
{
  struct rc_state_type *enclosing;
  rtx  rcdoit;
  rtx  rcsetup;
  tree n_sigs;
  tree sig_code;
  tree databufp;
  tree datalen;
  tree else_clause;
  tree received_signal;
  tree received_buffer;
  tree to_loc;
  int  sigseen;
  int  bufseen;
  tree actuallist;
  int  call_generated;
  int  if_generated;
  int  bufcnt;
};

struct rc_state_type *current_rc_state = NULL;

/* 
 * this function tells if there is an if to terminate
 * or not
 */
int
build_receive_case_if_generated()
{
  if (!current_rc_state)
    {
      error ("internal error: RECEIVE CASE stack invalid");
      abort ();
    }
  return current_rc_state->if_generated;
}

/* build_receive_case_start returns an INTEGER_CST node
   containing the case-label number to be used by
   build_receive_case_end to generate correct labels */
tree
build_receive_case_start (optset)
     tree optset;
{
  /* counter to generate unique receive_case labels */
  static int rc_lbl_count = 0;
  tree current_label_value = 
    build_int_2 ((HOST_WIDE_INT)rc_lbl_count, 0);
  tree sigcodename, filename, linenumber;
  
  struct rc_state_type *rc_state
    = (struct rc_state_type*) xmalloc (sizeof (struct rc_state_type));
  rc_state->rcdoit = gen_label_rtx ();
  rc_state->rcsetup = gen_label_rtx ();
  rc_state->enclosing = current_rc_state;
  current_rc_state = rc_state;
  rc_state->sigseen = 0;
  rc_state->bufseen = 0;
  rc_state->call_generated = 0;
  rc_state->if_generated = 0;
  rc_state->bufcnt = 0;

  rc_lbl_count++;
  if (optset == NULL_TREE || TREE_CODE (optset) == ERROR_MARK)
    optset = null_pointer_node;
  else
    {
      if (CH_IS_INSTANCE_MODE (TREE_TYPE (optset)) && CH_LOCATION_P (optset))
	optset = force_addr_of (optset);
      else
	{
	  error ("SET requires INSTANCE location");
	  optset = null_pointer_node;
	}			 
    }

  rc_state->to_loc = build_timeout_preface ();
  
  rc_state->n_sigs =
    decl_temp1 (get_identifier ("number_of_sigs"),
		integer_type_node, 0, integer_zero_node, 0, 0);

  rc_state->sig_code =
    decl_temp1 (get_identifier ("sig_codep"),
		ptr_type_node, 0, null_pointer_node, 0, 0);

  rc_state->databufp =
    decl_temp1 (get_identifier ("databufp"),
		ptr_type_node, 0, null_pointer_node, 0, 0);

  rc_state->datalen =
    decl_temp1 (get_identifier ("datalen"),
		integer_type_node, 0, integer_zero_node, 0, 0);

  rc_state->else_clause =
    decl_temp1 (get_identifier ("else_clause"),
		integer_type_node, 0, integer_zero_node, 0, 0);

  /* wait_signal will store the signal number in here */
  sigcodename = get_identifier ("received_signal");
  rc_state->received_signal = 
    decl_temp1 (sigcodename, chill_integer_type_node, 0, 
		NULL_TREE, 0, 0);

  /* wait_buffer will store the buffer address in here */
  sigcodename = get_unique_identifier ("received_buffer");
  rc_state->received_buffer =
    decl_temp1 (sigcodename, ptr_type_node, 0,
		NULL_TREE, 0, 0);

  /* now jump to the end of RECEIVE CASE actions, to
     set up variables for them. */
  emit_jump (rc_state->rcsetup);

  /* define the __rcdoit label. We come here after
     initialization of all variables, to execute the
     actions. */
  emit_label (rc_state->rcdoit);

  filename = force_addr_of (get_chill_filename ());
  linenumber = get_chill_linenumber ();
  
  /* Argument list for calling the runtime routine.  We'll call it
     the first time we call build_receive_case_label, when we know
     whether to call wait_signal or wait_buffer. NOTE: at this time
     the first argument will be set. */
  rc_state->actuallist = 
    tree_cons (NULL_TREE, NULL_TREE,
      tree_cons (NULL_TREE, rc_state->n_sigs,
        tree_cons (NULL_TREE, rc_state->sig_code,
          tree_cons (NULL_TREE, rc_state->databufp,
            tree_cons (NULL_TREE, rc_state->datalen,
              tree_cons (NULL_TREE, optset, 
	        tree_cons (NULL_TREE, rc_state->else_clause,
	          tree_cons (NULL_TREE, rc_state->to_loc,
		    tree_cons (NULL_TREE, filename,
		      tree_cons (NULL_TREE, linenumber, NULL_TREE))))))))));
  return current_label_value;
}

static tree
build_receive_signal_case_label (sigdecl, loclist)
     tree sigdecl, loclist;
{
  struct rc_state_type *rc_state = current_rc_state;
  tree signame = DECL_NAME (sigdecl);
  tree expr;

  if (rc_state->bufseen != 0)
    {
      error ("SIGNAL in RECEIVE CASE alternative follows");
      error (" a BUFFER name on line %d", rc_state->bufseen);
      return error_mark_node;
    }
  rc_state->sigseen = lineno;
  rc_state->bufseen = 0;

  if (!IDENTIFIER_SIGNAL_DATA (signame) && loclist != NULL_TREE)
    {
      error ("SIGNAL `%s' has no data fields", IDENTIFIER_POINTER (signame));
      return error_mark_node;
    }
  if (IDENTIFIER_SIGNAL_DATA (signame) && loclist == NULL_TREE)
    {
      error ("SIGNAL `%s' requires data fields", IDENTIFIER_POINTER (signame));
      return error_mark_node;
    }

  if (!rc_state->call_generated)
    {
      tree wait_call;

      TREE_VALUE (rc_state->actuallist) = force_addr_of (rc_state->received_signal);
      wait_call = build_chill_function_call (lookup_name
		    (get_identifier ("__wait_signal_timed")),
		       rc_state->actuallist);
#if 0
      chill_expand_assignment (rc_state->received_signal,
			       NOP_EXPR, wait_call);
#endif
      build_timesupervised_call (wait_call, rc_state->to_loc);
      
      rc_state->call_generated = 1;
    }

  /* build the conditional expression */
  expr = build (EQ_EXPR, boolean_type_node,
		rc_state->received_signal,
		(tree)DECL_TASKING_CODE_DECL (sigdecl));

  if (!rc_state->if_generated)
    {
      expand_start_cond (expr, 0);
      rc_state->if_generated = 1;
    }
  else
    expand_start_elseif (expr);

  if (IDENTIFIER_SIGNAL_DATA (signame))
    {
      /* copy data from signal buffer to user's variables */
      tree typelist = TYPE_FIELDS (TREE_TYPE (sigdecl));
      tree valtail, typetail;
      int  parmno = 1;
      tree pointer_type = build_chill_pointer_type (TREE_TYPE (sigdecl));
      tree pointer = convert (pointer_type, rc_state->databufp);
	  
      for (valtail = nreverse (loclist), typetail = typelist;
	   valtail != NULL_TREE && typetail != NULL_TREE;  
	   parmno++, valtail = TREE_CHAIN (valtail),
	   typetail = TREE_CHAIN (typetail))
	{
	  register tree actual  = valtail  ? TREE_VALUE (valtail)  : 0;
	  register tree type    = typetail ? TREE_TYPE (typetail) : 0;
	  register tree assgn;
	  char place[30];
	  sprintf (place, "signal field %d", parmno);

	  assgn = build_component_ref (build1 (INDIRECT_REF,
					       TREE_TYPE (sigdecl),
					       pointer),
				       DECL_NAME (typetail));
	  if (!CH_TYPE_NONVALUE_P (type))
	    /* don't assign to non-value type. Error printed at signal definition */
	    chill_expand_assignment (actual, NOP_EXPR, assgn);
	}

      if (valtail == NULL_TREE && typetail != NULL_TREE)
	error ("too few data fields provided for `%s'",
	       IDENTIFIER_POINTER (signame));
      if (valtail != NULL_TREE && typetail == NULL_TREE)
	error ("too many data fields provided for `%s'",
	       IDENTIFIER_POINTER (signame));
    }

  /* last action here */
  emit_line_note (input_filename, lineno);

  return build_tree_list (loclist, signame);
}

static tree
build_receive_buffer_case_label (buffer, loclist)
     tree buffer, loclist;
{
  struct rc_state_type *rc_state = current_rc_state;
  tree buftype = buffer_element_mode (TREE_TYPE (buffer));
  tree expr, var;
  tree pointer_type, pointer, assgn;
  int  had_errors = 0;
  tree x, y, z, bufaddr;

  if (rc_state->sigseen != 0)
    {
      error ("BUFFER in RECEIVE CASE alternative follows");
      error (" a SIGNAL name on line %d", rc_state->sigseen);
      return error_mark_node;
    }
  rc_state->bufseen = lineno;
  rc_state->sigseen = 0;

  if (! CH_REFERABLE (buffer))
    {
      error ("BUFFER in RECEIVE CASE alternative must be a location");
      return error_mark_node;
    }

  if (TREE_CHAIN (loclist) != NULL_TREE)
    {
      error ("buffer receive alternative requires only 1 defining occurrence");
      return error_mark_node;
    }

  if (!rc_state->call_generated)
    {
      tree wait_call;

      /* here we change the mode of rc_state->sig_code to
	 REF ARRAY (0:65535) REF __tmp_DESCR_type.
	 This is necessary, cause we cannot evaluate the buffer twice
	 (once here where we compare against the address of the buffer
	 and second in build_receive_buffer_case_end, where we use the
	 address build the descriptor, which gets passed to __wait_buffer).
	 So we change the comparison from
	 if (rc_state->received_buffer == &buffer)
	 to
	 if (rc_state->received_buffer ==
	 rc_state->sig_codep->[rc_state->bufcnt]->datap).
	 
	 This will evaluate the buffer location only once
	 (in build_receive_buffer_case_end) and therefore doesn't confuse
	 our machinery. */
      
      tree reftmpdescr = build_chill_pointer_type (
		            TREE_TYPE (lookup_name (
				get_identifier ("__tmp_DESCR_type"))));
      tree idxtype = build_chill_range_type (NULL_TREE,
			integer_zero_node,
			   build_int_2 (65535, 0)); /* should be enough, probably use ULONG */
      tree arrtype = build_chill_array_type (reftmpdescr,
			tree_cons (NULL_TREE, idxtype, NULL_TREE),
			   0, NULL_TREE);
      tree refarrtype = build_chill_pointer_type (arrtype);

      TREE_VALUE (rc_state->actuallist) = force_addr_of (rc_state->received_buffer);
      wait_call = build_chill_function_call (
		    lookup_name (get_identifier ("__wait_buffer")),
		      rc_state->actuallist);
#if 0
      chill_expand_assignment (rc_state->received_buffer,
				 NOP_EXPR, wait_call);
#endif
      build_timesupervised_call (wait_call, rc_state->to_loc);
      
      /* do this after the call, otherwise there will be a mode mismatch */
      TREE_TYPE (rc_state->sig_code) = refarrtype;
      
      /* now we are ready to generate the call */
      rc_state->call_generated = 1;
    }

  x = build_chill_indirect_ref (rc_state->sig_code, NULL_TREE, 0);
  y = build_chill_array_ref (x,
        tree_cons (NULL_TREE, build_int_2 (rc_state->bufcnt, 0), NULL_TREE));
  z = build_chill_indirect_ref (y, NULL_TREE, 0);
  bufaddr = build_chill_component_ref (z, get_identifier ("datap"));

  /* build the conditional expression */
  expr = build (EQ_EXPR, boolean_type_node,
		rc_state->received_buffer,
		bufaddr);

  /* next buffer in list */
  rc_state->bufcnt++;

  if (!rc_state->if_generated)
    {
      expand_start_cond (expr, 0);
      rc_state->if_generated = 1;
    }
  else
    expand_start_elseif (expr);

  /* copy buffer's data to destination */
  var = TREE_VALUE (loclist);

  if (buftype != NULL_TREE && TREE_CODE (buftype) == ERROR_MARK)
    had_errors = 1;
  else if (! CH_COMPATIBLE (var, buftype))
    {
      error ("incompatible modes in receive buffer alternative");
      had_errors = 1;
    }

  if (! CH_LOCATION_P (var))
    {
      error ("defining occurrence in receive buffer alternative must be a location");
      had_errors = 1;
    }

  if (! had_errors)
    {
      pointer_type = build_chill_pointer_type (TREE_TYPE (var));
      pointer = convert (pointer_type,
			 rc_state->databufp);
      /* no need to check this pointer being NULL */
      assgn = build_chill_indirect_ref (pointer, NULL_TREE, 0);
      
      chill_expand_assignment (var, NOP_EXPR, assgn);
    }

  /* last action here */
  emit_line_note (input_filename, lineno);

  return build_tree_list (loclist, buffer);
}
/*
 *  SIGNAME is the signal name or buffer location,
 *  LOCLIST is a list of possible locations to store data in
 */
tree
build_receive_case_label (signame, loclist)
     tree signame, loclist;
{
  /* now see what we have got and do some checks */
  if (TREE_CODE (signame) == TYPE_DECL && CH_DECL_SIGNAL (signame))
    return build_receive_signal_case_label (signame, loclist);

  if (TREE_TYPE (signame) != NULL_TREE
      && CH_IS_BUFFER_MODE (TREE_TYPE (signame)))
    {
      if (loclist == NULL_TREE)
	{
	  error ("buffer receive alternative without `IN location'");
	  return error_mark_node;
	}
      return build_receive_buffer_case_label (signame, loclist);
    }

  error ("RECEIVE CASE alternative must specify a SIGNAL name or BUFFER location");
  return error_mark_node;
}

/*
 * LABEL_CNT is the case-label counter passed from build_receive_case_start.
 * ELSE_CLAUSE defines if the RECEIVE CASE action had an ELSE(1) or not(0).
 * BUF_LIST is a tree-list of tree-lists, where TREE_VALUE defines the 
 * BUFFER location and TREE_PURPOSE defines the defining occurrence.
 */
static void
build_receive_buffer_case_end (buf_list, else_clause)
     tree buf_list, else_clause;
{
  struct rc_state_type *rc_state = current_rc_state;
  tree alist;
  tree field_decls = NULL_TREE; /* list of all buffer types, for the union */
  int  buffer_cnt = 0;
  tree descr_type = lookup_name (get_identifier ("__tmp_DESCR_type"));
  tree tuple = NULL_TREE;       /* constructors for array of ptrs */
  tree union_type_node = NULL_TREE;

  /* walk thru all the buffers */
  for (alist = buf_list; alist != NULL_TREE;
       buffer_cnt++, alist = TREE_CHAIN (alist))
    {
      tree value      = TREE_VALUE (alist);
      tree buffer     = TREE_VALUE (value);                 /* this is the buffer */
      tree data       = TREE_VALUE (TREE_PURPOSE (value));  /* the location to receive in */
      tree buffer_descr;
      tree buffer_descr_init;
      tree buffer_length;
      tree field;
      char fldname[20];

      /* build descriptor for buffer */
      buffer_length = max_queue_size (TREE_TYPE (buffer));
      if (buffer_length == NULL_TREE)
	buffer_length = infinite_buffer_event_length_node;
      buffer_descr_init = build_nt (CONSTRUCTOR, NULL_TREE,
                            tree_cons (NULL_TREE, force_addr_of (buffer),
                              tree_cons (NULL_TREE, buffer_length, NULL_TREE)));
      buffer_descr = decl_temp1 (get_unique_identifier ("RCbuffer"),
				 TREE_TYPE (descr_type), 0,
				 buffer_descr_init, 0, 0);
      tuple = tree_cons (NULL_TREE,
			 force_addr_of (buffer_descr),
			 tuple);

      /* make a field for the union */
      sprintf (fldname, "fld%03d", buffer_cnt);
      field = grok_chill_fixedfields (
                 tree_cons (NULL_TREE, get_identifier (fldname), NULL_TREE),
                   TREE_TYPE (data), NULL_TREE);
      if (field_decls == NULL_TREE)
	field_decls = field;
      else
	chainon (field_decls, field);
    }

  /* generate the union */
  if (field_decls != NULL_TREE)
    {
      tree data_id = get_identifier ("databuffer");
      tree data_decl;

      union_type_node = finish_struct (
			  start_struct (UNION_TYPE, NULL_TREE),
			    field_decls);
      data_decl = decl_temp1 (data_id, union_type_node, 0, NULL_TREE, 0, 0);

      chill_expand_assignment (rc_state->databufp, NOP_EXPR,
			       force_addr_of (data_decl));

      chill_expand_assignment (rc_state->datalen, NOP_EXPR,
			       size_in_bytes (TREE_TYPE (data_decl)));
    }

  /* tell runtime system if we had an else or not */
  chill_expand_assignment (rc_state->else_clause, NOP_EXPR, else_clause);

  /* generate the array of pointers to all buffers */
  {
    tree array_id = get_identifier ("buf_ptr_array");
    tree array_type_node =
           build_chill_array_type (ptr_type_node,
             tree_cons (NULL_TREE,
               build_chill_range_type (NULL_TREE,
				       integer_one_node,
				       build_int_2 (buffer_cnt, 0)),
			NULL_TREE),
			  0, NULL_TREE);
    tree constr = build_nt (CONSTRUCTOR, NULL_TREE, nreverse (tuple));
    tree array_decl = decl_temp1 (array_id, array_type_node, 0,
				  constr, 0, 0);
    
    chill_expand_assignment (build_chill_cast (ptr_type_node, rc_state->sig_code),
			     NOP_EXPR,
			     force_addr_of (array_decl));
    chill_expand_assignment (rc_state->n_sigs, NOP_EXPR,
			     build_int_2 (buffer_cnt, 0));
  }
}

/*
 * SIG_LIST is a tree list.  The TREE_VALUEs are VAR_DECLs of 
 * __tmp_%s_code variables, and the TREE_PURPOSEs are the
 * TYPE_DECLs of the __tmp_%s_struct types.  LABEL_CNT is the
 * case-label counter passed from build_receive_case_start.
 */
static void
build_receive_signal_case_end (sig_list, else_clause)
     tree sig_list, else_clause;
{
  struct rc_state_type *rc_state = current_rc_state;
  tree alist, temp1;
  tree union_type_node = NULL_TREE;
  tree field_decls = NULL_TREE;  /* list of signal
				   structure, for the union */
  tree tuple = NULL_TREE;    /* constructor for array of ptrs */
  int  signal_cnt = 0;
  int  fldcnt = 0;

  /* for each list of locations, validate it against the
     corresponding signal's list of fields. */
  {
    for (alist = sig_list; alist != NULL_TREE;
	 signal_cnt++, alist = TREE_CHAIN (alist))
      {
	tree value    = TREE_VALUE (alist);
	tree signame  = TREE_VALUE (value);  /* signal's ID node */
	tree sigdecl  = lookup_name (signame);
	tree sigtype  = TREE_TYPE (sigdecl);
	tree field;
	char fldname[20];

	if (IDENTIFIER_SIGNAL_DATA (signame))
	  {
	    sprintf (fldname, "fld%03d", fldcnt++);
	    field = grok_chill_fixedfields (
		      tree_cons (NULL_TREE, 
				 get_identifier (fldname),
				 NULL_TREE),
			sigtype, NULL_TREE); 
	    if (field_decls == NULL_TREE)
	      field_decls = field;
	    else
	      chainon (field_decls, field);

	  }

	temp1 = (tree)DECL_TASKING_CODE_DECL (sigdecl);
	mark_addressable (temp1);
	tuple = tree_cons (NULL_TREE,
		  build1 (ADDR_EXPR, 
		    build_chill_pointer_type (chill_integer_type_node),
			  temp1),
		    tuple);
      }
  }

  /* generate the union of all of the signal data types */
  if (field_decls != NULL_TREE)
    {
      tree data_id = get_identifier ("databuffer");
      tree data_decl;
      union_type_node = finish_struct (start_struct (UNION_TYPE, 
					       NULL_TREE),
				 field_decls); 
      data_decl =
	decl_temp1 (data_id, union_type_node, 0, NULL_TREE, 0, 0);

      chill_expand_assignment (rc_state->databufp, NOP_EXPR,
			       force_addr_of (data_decl));

      chill_expand_assignment (rc_state->datalen, NOP_EXPR, 
			       size_in_bytes (TREE_TYPE (data_decl)));
    }

  /* tell runtime system if we had an else or not */
  chill_expand_assignment (rc_state->else_clause, NOP_EXPR, else_clause);

  /* generate the array of all signal codes */
  {
    tree array_id = get_identifier ("sig_code_array");
    tree array_type_node
      = build_chill_array_type (
          build_chill_pointer_type (chill_integer_type_node),
	    tree_cons (NULL_TREE,
	      build_chill_range_type (NULL_TREE,
				      integer_one_node,
				      build_int_2 (signal_cnt, 0)),
		       NULL_TREE),
	 0, NULL_TREE);
    tree constr = build_nt (CONSTRUCTOR, NULL_TREE,
			    nreverse (tuple));
    tree array_decl = 
      decl_temp1 (array_id, array_type_node, 0, constr, 0, 0);

    chill_expand_assignment (rc_state->sig_code, NOP_EXPR, 
			     force_addr_of (array_decl));

    /* give number of signals to runtime system */
    chill_expand_assignment (rc_state->n_sigs, NOP_EXPR, 
			     build_int_2 (signal_cnt, 0));
  }
}

/* General function for the end of a RECEIVE CASE action */

void
build_receive_case_end (alist, else_clause)
     tree alist, else_clause;
{
  rtx rcdone = gen_label_rtx ();
  struct rc_state_type *rc_state = current_rc_state;
  tree tmp;
  int had_errors = 0;

  /* finish the if's, if generated */
  if (rc_state->if_generated)
    expand_end_cond ();

  /* check alist for errors */
  for (tmp = alist; tmp != NULL_TREE; tmp = TREE_CHAIN (tmp))
    {
      if (TREE_CODE (TREE_VALUE (tmp)) == ERROR_MARK)
	had_errors++;
    }

  /* jump to the end of RECEIVE CASE processing */
  emit_jump (rcdone);

  /* define the __rcsetup label. We come here to initialize
     all variables */
  emit_label (rc_state->rcsetup);

  if (alist == NULL_TREE && !had_errors)
    {
      error ("RECEIVE CASE without alternatives");
      goto gen_rcdoit;
    }

  if (TREE_CODE (alist) == ERROR_MARK || had_errors)
    goto gen_rcdoit;

  /* now call the actual end function */
  if (rc_state->bufseen)
    build_receive_buffer_case_end (alist, else_clause);
  else
    build_receive_signal_case_end (alist, else_clause);

  /* now jump to the beginning of RECEIVE CASE processing */
gen_rcdoit: ;
  emit_jump (rc_state->rcdoit);

  /* define the __rcdone label. We come here when the whole
     receive case is done. */
  emit_label (rcdone);

  current_rc_state = rc_state->enclosing;
  free(rc_state);
}

/* build a CONTINUE action */

void expand_continue_event (evloc)
     tree evloc;
{
  tree filename, linenumber, evaddr;

  /* do some checks */
  if (evloc == NULL_TREE || TREE_CODE (evloc) == ERROR_MARK)
    return;

  if (! CH_REFERABLE (evloc) || ! CH_IS_EVENT_MODE (TREE_TYPE (evloc)))
    {
      error ("CONTINUE requires an event location");
      return;
    }

  evaddr = force_addr_of (evloc);
  filename = force_addr_of (get_chill_filename ());
  linenumber = get_chill_linenumber ();

  expand_expr_stmt (
    build_chill_function_call (lookup_name (get_identifier ("__continue")),
      tree_cons (NULL_TREE, evaddr,
        tree_cons (NULL_TREE, filename,
          tree_cons (NULL_TREE, linenumber, NULL_TREE)))));
}

/*
 * The following code builds a DELAY CASE statement,
 * which looks like this in CHILL:
 *
 *    DCL ev1, ev2 EVENT, ins INSTANCE;
 *    DCL ev3 EVENT (10);
 *    DCL count1 INT := 0, count2 INT := 0;
 *
 *    DELAY CASE SET ins;
 *      (ev1): count1 +:= 1;
 *      (ev2, ev3): count2 +:= 1;
 *    ESAC; 
 *
 * Because we don't know until we get to the ESAC how
 * many events need processing, we generate the following
 * C-equivalent code:
 *
 *
 * {               // start a new symbol context
 *   typedef struct
 *   {
 *      void           *p;
 *      unsigned long  len;
 *   } Descr;
 *   int     number_of_events;
 *   Descr  *event_codes;
 *
 *   goto __dlsetup;
 *
 *  __dldoit: 
 *   void *whatevent = __delay_event (number_of_events,
 *                                    event_codes,
 *                                    priority,
 *                                    &instance_loc,
 *                                    filename,
 *                                    linenumber);
 *   if (whatevent == &ev1)
 *     {
 *       // code for ev1 alternative's action_statement_list
 *       count1 += 1;
 *     }
 *   else if (whatevent == &ev2 || whatevent == &ev3)
 *     {
 *       // code for ev2 and ev3 alternative's action_statement_list
 *       count2 += 1;
 *     }
 *   goto __dl_done;
 *
 * __dlsetup:
 *   Descr event_code_ptr [3] = {
 *              { &ev1, -1 },
 *              { &ev2, -1 },
 *              { &ev3, 10 } };
 *   event_codes = &event_code_ptr[0];
 *   number_of_events = 3;
 *   goto __dldoit;
 *
 * __dl_done: 
 *   ;
 * }               // end the new symbol context
 *
 */

struct dl_state_type
{
  struct dl_state_type *enclosing;
  rtx  dldoit;
  rtx  dlsetup;
  tree n_events;
  tree event_codes;
  tree received_event;
};

struct dl_state_type *current_dl_state = NULL;

/* build_receive_case_start returns an INTEGER_CST node
   containing the case-label number to be used by
   build_receive_case_end to generate correct labels */
tree
build_delay_case_start (optset, optpriority)
     tree optset, optpriority;
{
  /* counter to generate unique delay case labels */
  static int dl_lbl_count = 0;
  tree current_label_value = 
    build_int_2 ((HOST_WIDE_INT)dl_lbl_count, 0);
  tree wait_call;
  tree actuallist = NULL_TREE;
  tree filename, linenumber;
  tree to_loc;
  
  struct dl_state_type *dl_state
    = (struct dl_state_type*) xmalloc (sizeof (struct dl_state_type));
  dl_state->enclosing = current_dl_state;
  current_dl_state = dl_state;
  dl_state->dldoit = gen_label_rtx ();
  dl_state->dlsetup = gen_label_rtx ();

  dl_lbl_count++;

  /* check the optional SET location */
  if (optset == NULL_TREE
      || TREE_CODE (optset) == ERROR_MARK)
    optset = null_pointer_node;
  else if (CH_IS_INSTANCE_MODE (TREE_TYPE (optset)) && CH_LOCATION_P (optset))
    optset = force_addr_of (optset);
  else
    {
      error ("SET requires INSTANCE location");
      optset = null_pointer_node;
    }			 

  /* check the presence of the PRIORITY expression */
  if (optpriority == NULL_TREE)
    optpriority = integer_zero_node;
  else if (TREE_CODE (optpriority) == ERROR_MARK)
    optpriority = integer_zero_node;
  else if (TREE_CODE (TREE_TYPE (optpriority)) != INTEGER_TYPE)
    {
      error ("PRIORITY must be of integer type");
      optpriority = integer_zero_node;
    }

  /* check for time supervised */
  to_loc = build_timeout_preface ();
  
  dl_state->n_events =
    decl_temp1 (get_identifier ("number_of_events"),
		integer_type_node, 0, integer_zero_node, 0, 0);

  dl_state->event_codes =
    decl_temp1 (get_identifier ("event_codes"),
		ptr_type_node, 0, null_pointer_node, 0, 0);

  /* wait_event will store the signal number in here */
  dl_state->received_event =
    decl_temp1 (get_identifier ("received_event"),
		ptr_type_node, 0, NULL_TREE, 0, 0);

  /* now jump to the end of RECEIVE CASE actions, to
     set up variables for them. */
  emit_jump (dl_state->dlsetup);

  /* define the __rcdoit label. We come here after
     initialization of all variables, to execute the
     actions. */
  emit_label (dl_state->dldoit);

  filename = force_addr_of (get_chill_filename ());
  linenumber = get_chill_linenumber ();
  
  /* here we go, call the runtime routine */
  actuallist = tree_cons (NULL_TREE, force_addr_of (dl_state->received_event),
                 tree_cons (NULL_TREE, dl_state->n_events,
                   tree_cons (NULL_TREE, dl_state->event_codes,
                     tree_cons (NULL_TREE, optpriority, 
                       tree_cons (NULL_TREE, to_loc,
                         tree_cons (NULL_TREE, optset, 
		           tree_cons (NULL_TREE, filename,
		             tree_cons (NULL_TREE, linenumber, NULL_TREE))))))));

  wait_call = build_chill_function_call (
                lookup_name (get_identifier ("__delay_event")),
					 actuallist);

#if 0
  chill_expand_assignment (dl_state->received_event, NOP_EXPR, wait_call);
#endif
  build_timesupervised_call (wait_call, to_loc);
  return current_label_value;
}

/*
   EVENTLIST is the list of this alternative's events
   and IF_OR_ELSEIF indicates what action (1 for if and 
   0 for else if) should be generated.
*/
void
build_delay_case_label (eventlist, if_or_elseif)
     tree eventlist;
     int  if_or_elseif;
{
  tree eventp, expr = NULL_TREE;

  if (eventlist == NULL_TREE || TREE_CODE (eventlist) == ERROR_MARK)
    return;

  for (eventp = eventlist; eventp != NULL_TREE; 
       eventp = TREE_CHAIN (eventp))
    {
      tree event = TREE_VALUE (eventp);
      tree temp1;

      if (event == NULL_TREE || TREE_CODE (event) == ERROR_MARK)
	temp1 = null_pointer_node;
      else if (! CH_IS_EVENT_MODE (TREE_TYPE (event)) || ! CH_REFERABLE (event))
	{
	  error ("delay alternative must be an EVENT location");
	  temp1 = null_pointer_node;
	}
      else
	temp1 = force_addr_of (event);
      
      /* build the conditional expression */
      if (expr == NULL_TREE)
	expr = build (EQ_EXPR, boolean_type_node,
		      current_dl_state->received_event, temp1);
      else
	expr = 
	  build (TRUTH_ORIF_EXPR, boolean_type_node, expr,
		 build (EQ_EXPR, boolean_type_node,
			current_dl_state->received_event, temp1));
    }
  if (if_or_elseif)
    expand_start_cond (expr, 0);
  else
    expand_start_elseif (expr);

  /* last action here */
  emit_line_note (input_filename, lineno);
}

/*
 * EVENT_LIST is a tree list.  The TREE_VALUEs are VAR_DECLs of 
 * EVENT variables.  LABEL_CNT is the case-label counter
 * passed from build_delay_case_start.
 */
void
build_delay_case_end (event_list)
     tree event_list;
{
  struct dl_state_type *dl_state = current_dl_state;
  rtx    dldone          = gen_label_rtx ();
  tree tuple = NULL_TREE;    /* constructor for array of descrs */
  tree acode;
  int  event_cnt = 0;

  /* if we have an empty event_list, there was no alternatives and we
     havn't started an if therefor don't run expand_end_cond */
  if (event_list != NULL_TREE)
    /* finish the if's */
    expand_end_cond ();

  /* jump to the end of RECEIVE CASE processing */
  emit_jump (dldone);

  /* define the __dlsetup label. We come here to initialize
     all variables */
  emit_label (dl_state->dlsetup);

  if (event_list == NULL_TREE)
    {
      error ("DELAY CASE without alternatives");
      goto gen_dldoit;
    }

  if (event_list == NULL_TREE 
      || TREE_CODE (event_list) == ERROR_MARK)
    goto gen_dldoit;

  /* make a list of pointers (in reverse order)
     to the event code variables */
  for (acode = event_list; acode != NULL_TREE; 
       acode = TREE_CHAIN (acode))
    {
      tree event = TREE_VALUE (acode);
      tree event_length;
      tree descr_init;

      if (event == NULL_TREE || TREE_CODE (event) == ERROR_MARK)
	{
	  descr_init = 
	    tree_cons (NULL_TREE, null_pointer_node,
	      tree_cons (NULL_TREE, integer_zero_node, NULL_TREE));
	}
      else
	{
	  event_length = max_queue_size (TREE_TYPE (event));
	  if (event_length == NULL_TREE)
	    event_length = infinite_buffer_event_length_node;
	  descr_init =
	    tree_cons (NULL_TREE, force_addr_of (event),
              tree_cons (NULL_TREE, event_length, NULL_TREE));
	}
      tuple = tree_cons (NULL_TREE,
		build_nt (CONSTRUCTOR, NULL_TREE, descr_init),
		  tuple);
      event_cnt++;
    }
    
  /* generate the array of all event code pointers */
  {
    tree descr_type = TREE_TYPE (lookup_name (get_identifier ("__tmp_DESCR_type")));
    tree array_id = get_identifier ("event_code_array");
    tree array_type_node
      = build_chill_array_type (descr_type,
	 tree_cons (NULL_TREE,
           build_chill_range_type (NULL_TREE,
				   integer_one_node,
				   build_int_2 (event_cnt, 0)),
		    NULL_TREE),
	 0, NULL_TREE);
    tree constr = build_nt (CONSTRUCTOR, NULL_TREE,
			    nreverse (tuple));
    tree array_decl = 
      decl_temp1 (array_id, array_type_node, 0, constr, 0, 0);

    chill_expand_assignment (dl_state->event_codes, NOP_EXPR, 
			     force_addr_of (array_decl));

    /* give number of signals to runtime system */
    chill_expand_assignment (dl_state->n_events, NOP_EXPR, 
			     build_int_2 (event_cnt, 0));
  }

  /* now jump to the beginning of DELAY CASE processing */
gen_dldoit: 
  emit_jump (dl_state->dldoit);

  /* define the __dldone label. We come here when the whole
     DELAY CASE is done. */
  emit_label (dldone);

  current_dl_state = dl_state->enclosing;
  free(dl_state);
}

/*
 * The following code builds a simple delay statement,
 * which looks like this in CHILL:
 *
 *    DCL ev1 EVENT(5), ins INSTANCE;
 *
 *    DELAY ev1 PRIORITY 7;
 *
 * This statement unconditionally delays the current 
 * PROCESS, until some other process CONTINUEs it.
 *
 * Here is the generated C code:
 *
 * typedef struct
 * {
 *   void          *p;
 *   unsigned long len;
 * } Descr;
 *
 * static short __tmp_ev1_code;
 * 
 * {  // start a new symbol context
 *
 *   Descr __delay_array[1] = { { ev1, 5 } };
 *
 *   __delay_event (1, &__delay_array, 7, NULL,
 *		    filename, linenumber);
 *
 * } // end of symbol scope
 */
void
build_delay_action (event, optpriority)
	tree event, optpriority;
{
  int had_errors = 0;
  tree to_loc = NULL_TREE;
  /* we discard the return value of __delay_event, cause in
     a normal DELAY action no selections have to be made */
  tree ev_got = null_pointer_node;
  
  /* check the event */
  if (event == NULL_TREE || TREE_CODE (event) == ERROR_MARK)
    had_errors = 1;
  else if (! CH_IS_EVENT_MODE (TREE_TYPE (event)) || ! CH_REFERABLE (event))
    {
      error ("DELAY action requires an event location");
      had_errors = 1;
    }

  /* check the presence of priority */
  if (optpriority != NULL_TREE)
    {
      if (TREE_CODE (optpriority) == ERROR_MARK)
	return;
      if (TREE_CODE (TREE_TYPE (optpriority)) != INTEGER_TYPE)
	{
	  error ("PRIORITY in DELAY action must be of integer type");
	  return;
	}
    }
  else
    {
      /* issue a warning in case of -Wall */
      if (extra_warnings)
	{
	  warning ("DELAY action without priority.");
	  warning (" PRIORITY defaulted to 0");
	}
      optpriority = integer_zero_node;
    }
  if (had_errors)
    return;

  {
    tree descr_type;
    tree array_type_node;
    tree array_decl;
    tree descr_init;
    tree array_init;
    tree event_length = max_queue_size (TREE_TYPE (event));
    tree event_codes;
    tree filename = force_addr_of (get_chill_filename ());
    tree linenumber = get_chill_linenumber ();
    tree actuallist;

    to_loc = build_timeout_preface ();
    
    descr_type = TREE_TYPE (lookup_name (get_identifier ("__tmp_DESCR_type")));

    array_type_node =
        build_chill_array_type (descr_type,
	  tree_cons (NULL_TREE,
	    build_chill_range_type (NULL_TREE, integer_one_node,
				    integer_one_node),
		     NULL_TREE),
		       0, NULL_TREE);
    if (event_length == NULL_TREE)
      event_length = infinite_buffer_event_length_node;

    descr_init = 
      tree_cons (NULL_TREE, force_addr_of (event),
        tree_cons (NULL_TREE, event_length, NULL_TREE));
    array_init = 
      tree_cons (NULL_TREE,
		 build_nt (CONSTRUCTOR, NULL_TREE, descr_init),
		 NULL_TREE);
    array_decl = 
      decl_temp1 (get_unique_identifier ("event_codes_array"),
		  array_type_node, 0, 
		  build_nt (CONSTRUCTOR, NULL_TREE, array_init),
		  0, 0);

    event_codes =
      decl_temp1 (get_unique_identifier ("event_ptr"), 
		  ptr_type_node, 0, 
		  force_addr_of (array_decl),
		  0, 0);

    actuallist = 
      tree_cons (NULL_TREE, ev_got,
        tree_cons (NULL_TREE, integer_one_node,
	  tree_cons (NULL_TREE, event_codes,
            tree_cons (NULL_TREE, optpriority,
              tree_cons (NULL_TREE, to_loc,
	        tree_cons (NULL_TREE, null_pointer_node,
	          tree_cons (NULL_TREE, filename,
		    tree_cons (NULL_TREE, linenumber, NULL_TREE))))))));

		   
    build_timesupervised_call (
      build_chill_function_call (
	lookup_name (get_identifier ("__delay_event")),
	  actuallist), to_loc);
  }
}

void
expand_send_buffer (buffer, value, optpriority, optwith, optto)
     tree buffer, value, optpriority, optwith, optto;
{
  tree filename, linenumber;
  tree buffer_mode_decl = NULL_TREE;
  tree buffer_ptr, value_ptr;
  int  had_errors = 0;
  tree timeout_value, fcall;
  
  /* check buffer location */
  if (buffer == NULL_TREE || TREE_CODE (buffer) == ERROR_MARK)
    {
      buffer = NULL_TREE;
      had_errors = 1;
    }
  if (buffer != NULL_TREE)
    {
      if (! CH_IS_BUFFER_MODE (TREE_TYPE (buffer)) || ! CH_REFERABLE (buffer))
	{
	  error ("send buffer action requires a BUFFER location");
	  had_errors = 1;
	}
      else
	buffer_mode_decl = TREE_CHAIN (TYPE_FIELDS (TREE_TYPE (buffer)));
    }

  /* check value and type */
  if (value == NULL_TREE || TREE_CODE (value) == ERROR_MARK)
    {
      had_errors = 1;
      value = NULL_TREE;
    }
  if (value != NULL_TREE)
    {
      if (TREE_CHAIN (value) != NULL_TREE)
	{
	  error ("there must be only 1 value for send buffer action");
	  had_errors = 1;
	}
      else
	{
	  value = TREE_VALUE (value);
	  if (value == NULL_TREE || TREE_CODE (value) == ERROR_MARK)
	    {
	      had_errors = 1;
	      value = NULL_TREE;
	    }
	  if (value != NULL_TREE && buffer_mode_decl != NULL_TREE)
	    {
	      if (TREE_TYPE (buffer_mode_decl) != NULL_TREE &&
		  TREE_CODE (TREE_TYPE (buffer_mode_decl)) == ERROR_MARK)
		had_errors = 1;
	      else if (CH_COMPATIBLE (value, TREE_TYPE (buffer_mode_decl)))
		{
		  value = convert (TREE_TYPE (buffer_mode_decl), value);
		  if (value == NULL_TREE || TREE_CODE (value) == ERROR_MARK)
		    {
		      error ("convert failed for send buffer action");
		      had_errors = 1;
		    }
		}
	      else
		{
		  error ("incompatible modes in send buffer action");
		  had_errors = 1;
		}
	    }
	}
    }

  /* check the presence of priority */
  if (optpriority == NULL_TREE)
    {
      if (send_buffer_prio == NULL_TREE)
	{
	  /* issue a warning in case of -Wall */
	  if (extra_warnings)
	    {
	      warning ("buffer sent without priority");
	      warning (" and no default priority was set.");
	      warning (" PRIORITY defaulted to 0");
	    }
	  optpriority = integer_zero_node;
	}
      else
	optpriority = send_buffer_prio;
    }
  else if (TREE_CODE (optpriority) == ERROR_MARK)
    had_errors = 1;
  else if (TREE_CODE (TREE_TYPE (optpriority)) != INTEGER_TYPE)
    {
      error ("PRIORITY must be of integer type");
      had_errors = 1;
    }

  if (optwith != NULL_TREE)
    {
      error ("WITH not allowed for send buffer action");
      had_errors = 1;
    }
  if (optto != NULL_TREE)
    {
      error ("TO not allowed for send buffer action");
      had_errors = 1;
    }
  if (had_errors)
    return;

  {
    tree descr_type;
    tree buffer_descr, buffer_init, buffer_length;
    tree val;

    /* process timeout */
    timeout_value = build_timeout_preface ();

    descr_type = lookup_name (get_identifier ("__tmp_DESCR_type"));

    /* build descr for buffer */
    buffer_length = max_queue_size (TREE_TYPE (buffer));
    if (buffer_length == NULL_TREE)
      buffer_length = infinite_buffer_event_length_node;
    buffer_init = build_nt (CONSTRUCTOR, NULL_TREE,
		    tree_cons (NULL_TREE, force_addr_of (buffer),
                      tree_cons (NULL_TREE, buffer_length, NULL_TREE)));
    buffer_descr = decl_temp1 (get_unique_identifier ("buffer_descr"),
			       TREE_TYPE (descr_type), 0, buffer_init,
			       0, 0);
    buffer_ptr = decl_temp1 (get_unique_identifier ("buffer_ptr"),
			     ptr_type_node, 0,
			     force_addr_of (buffer_descr),
			     0, 0);

    /* build descr for value */
    if (! CH_REFERABLE (value))
      val = decl_temp1 (get_identifier ("buffer_value"),
			TREE_TYPE (value), 0,
			value, 0, 0);
    else
      val = value;

    value_ptr = build_chill_descr (val);

  }

  /* get filename and linenumber */
  filename = force_addr_of (get_chill_filename ());
  linenumber = get_chill_linenumber ();
  
  /* Now, we can call the runtime */
  fcall = build_chill_function_call (
    lookup_name (get_identifier ("__send_buffer")),
      tree_cons (NULL_TREE, buffer_ptr,
	tree_cons (NULL_TREE, value_ptr,
	  tree_cons (NULL_TREE, optpriority,
            tree_cons (NULL_TREE, timeout_value,
              tree_cons (NULL_TREE, filename,
                tree_cons (NULL_TREE, linenumber, NULL_TREE)))))));
  build_timesupervised_call (fcall, timeout_value);
}
# if 0

void
process_buffer_decls (namelist, mode, optstatic)
  tree namelist, mode;
  int  optstatic;
{
  tree names;
  int quasi_flag = current_module->is_spec_module;

  if (pass < 2)
    return;

  for (names = namelist; names != NULL_TREE; names = TREE_CHAIN (names))
    { 
      tree name = TREE_VALUE (names);
      tree bufdecl = lookup_name (name);
      tree code_decl = 
	decl_tasking_code_variable (name, &buffer_code, quasi_flag);

      /* remember the code variable in the buffer decl */
      DECL_TASKING_CODE_DECL (bufdecl) = (struct lang_decl *)code_decl;

      add_taskstuff_to_list (code_decl, "_TT_Buffer", 
			     quasi_flag ? NULL_TREE : buffer_code,
			     bufdecl);
    }
}
#endif

/*
 * if no queue size was specified, QUEUESIZE is integer_zero_node.
 */
tree
build_buffer_type (element_type, queuesize)
     tree element_type, queuesize;
{
  tree type, field;
  if (element_type == NULL_TREE || TREE_CODE (element_type) == ERROR_MARK)
    return error_mark_node;
  if (queuesize != NULL_TREE && TREE_CODE (queuesize) == ERROR_MARK)
    return error_mark_node;

  type = make_node (RECORD_TYPE);
  field = build_decl (FIELD_DECL, get_identifier("__buffer_data"),
		      ptr_type_node);
  TYPE_FIELDS (type) = field;
  TREE_CHAIN (field)
    = build_lang_decl (TYPE_DECL, get_identifier ("__element_mode"),
		       element_type);
  field = TREE_CHAIN (field);
  if (queuesize)
    {
      tree size_field = build_decl (CONST_DECL, get_identifier("__queue_max"),
				    integer_type_node);
      DECL_INITIAL (size_field) = queuesize;
      TREE_CHAIN (field) = size_field;
    }
  CH_IS_BUFFER_MODE (type) = 1;
  CH_TYPE_NONVALUE_P (type) = 1;
  if (pass == 2)
    type = layout_chill_struct_type (type);
  return type;
}

#if 0
tree
build_buffer_descriptor (bufname, expr, optpriority)
     tree bufname, expr, optpriority;
{
  tree bufdecl;

  if (bufname == NULL_TREE
      || TREE_CODE (bufname) == ERROR_MARK)
    return error_mark_node;

  if (expr != NULL_TREE
      && TREE_CODE (expr) == ERROR_MARK)
    return error_mark_node;
#if 0
/* FIXME: is this what we really want to test? */
  bufdecl = lookup_name (bufname);
  if (TREE_CODE (bufdecl) != TYPE_DECL
      || ! CH_IS_BUFFER_MODE (TREE_TYPE (bufdecl)))
    {
      error ("SEND requires a BUFFER; `%s' is not a BUFFER name", 
	     bufname);
      return error_mark_node;
    }
#endif
  {
    /* build buffer/signal data structure */
    tree bufdataname = get_unique_identifier (IDENTIFIER_POINTER (bufname));
    tree dataptr;

    if (expr == NULL_TREE)
      dataptr = null_pointer_node;
    else
      {
	tree decl = 
	  decl_temp1 (bufdataname, TREE_TYPE (bufdecl), 0, 
		      expr, 0, 0);
	/* prevent granting of this variable */
	DECL_SOURCE_LINE (decl) = 0;

	dataptr = force_addr_of (decl);
      }
    
    /* build descriptor pointing to buffer data */
    {
      tree tasking_message_var = get_unique_identifier (IDENTIFIER_POINTER (bufname));
      tree data_len = (expr == NULL_TREE) ? integer_zero_node :
	                     size_in_bytes (TREE_TYPE (bufdecl));
      tree tasking_code = (tree)DECL_TASKING_CODE_DECL (bufdecl);
      tree tuple = build_nt (CONSTRUCTOR, NULL_TREE,
		     tree_cons (NULL_TREE, 
		       build1 (ADDR_EXPR, 
			       build_chill_pointer_type (chill_integer_type_node), 
			       tasking_code),
			   tree_cons (NULL_TREE, data_len,
			     tree_cons (NULL_TREE, dataptr, NULL_TREE))));
			      
      tree decl = decl_temp1 (tasking_message_var,
			      TREE_TYPE (tasking_message_type), 0,
			      tuple, 0, 0);
      mark_addressable (tasking_code);
      /* prevent granting of this variable */
      DECL_SOURCE_LINE (decl) = 0;

      tuple = force_addr_of (decl);
      return tuple;
    }
  }
}
#endif

#if 0
void
process_event_decls (namelist, mode, optstatic)
  tree namelist, mode;
  int  optstatic;
{
  tree names;
  int quasi_flag = current_module->is_spec_module;

  if (pass < 2)
    return;

  for (names = namelist; names != NULL_TREE; names = TREE_CHAIN (names))
    { 
      tree name = TREE_VALUE (names);
      tree eventdecl = lookup_name (name);
      tree code_decl = 
	decl_tasking_code_variable (name, &event_code, quasi_flag);

      /* remember the code variable in the event decl */
      DECL_TASKING_CODE_DECL (eventdecl) = (struct lang_decl *)code_decl;

      add_taskstuff_to_list (code_decl, "_TT_Event", 
			     quasi_flag ? NULL_TREE : event_code,
			     eventdecl);
    }
}
#endif

/* Return the buffer or event length of a buffer or event mode.
   (NULL_TREE means unlimited.) */

tree
max_queue_size (mode)
     tree mode;
{
  tree field = TYPE_FIELDS (mode);
  for ( ; field != NULL_TREE ; field = TREE_CHAIN (field))
    {
      if (TREE_CODE (field) == CONST_DECL)
	return DECL_INITIAL (field);
    }
  return NULL_TREE;
}

/* Return the buffer element mode of a buffer mode. */

tree
buffer_element_mode (bufmode)
     tree bufmode;
{
  tree field = TYPE_FIELDS (bufmode);
  for ( ; field != NULL_TREE; field = TREE_CHAIN (field))
    {
      if (TREE_CODE (field) == TYPE_DECL)
	return TREE_TYPE (field);
    }
  return NULL_TREE;
}

/* invalidate buffer element mode in case we detect, that the
   elelment mode has the non-value property */

void
invalidate_buffer_element_mode (bufmode)
     tree bufmode;
{
  tree field = TYPE_FIELDS (bufmode);
  for ( ; field != NULL_TREE; field = TREE_CHAIN (field))
    {
      if (TREE_CODE (field) == TYPE_DECL)
	{
	  TREE_TYPE (field) = error_mark_node;
	  return;
	}
    }
}

/* For an EVENT or BUFFER mode TYPE, with a give maximum queue size QSIZE,
   perform various error checks.  Return a new queue size. */

tree
check_queue_size (qsize)
     tree qsize;
{
  if (qsize == NULL_TREE || TREE_CODE (qsize) == ERROR_MARK)
    return qsize;
  if (TREE_TYPE (qsize) == NULL_TREE
      || !CH_SIMILAR (TREE_TYPE (qsize), integer_type_node))
    {
      error ("non-integral max queue size for EVENT/BUFFER mode");
      return integer_one_node;
    }
  if (TREE_CODE (qsize) != INTEGER_CST)
    {
      error ("non-constant max queue size for EVENT/BUFFER mode");
      return integer_one_node;
    }
  if (compare_int_csts (pedantic ? LE_EXPR : LT_EXPR,
			qsize,
			integer_zero_node))
    {
      error ("max queue_size for EVENT/BUFFER is not positive");
      return integer_one_node;
    }
  return qsize;
}

/*
 * An EVENT type is modelled as a boolean type, which should
 * allocate the minimum amount of space.
 */
tree
build_event_type (queuesize)
     tree queuesize;
{
  tree type = make_node (RECORD_TYPE);
  tree field = build_decl (FIELD_DECL, get_identifier("__event_data"),
		      ptr_type_node);
  TYPE_FIELDS (type) = field;
  if (queuesize)
    {
      tree size_field = build_decl (CONST_DECL, get_identifier("__queue_max"),
				    integer_type_node);
      DECL_INITIAL (size_field) = queuesize;
      TREE_CHAIN (field) = size_field;
    }
  CH_IS_EVENT_MODE (type) = 1;
  CH_TYPE_NONVALUE_P (type) = 1;
  if (pass == 2)
    type = layout_chill_struct_type (type);
  return type;
}

/*
 * Initialize the various types of tasking data.
 */
void
tasking_init ()
{
  extern int  ignore_case;
  extern int  special_UC;
  extern tree chill_predefined_function_type;
  tree temp, ins_ftype_void;
  tree endlink = void_list_node;
  tree int_ftype_ptr_int_ptr_ptr_int_ptr_int_ptr_ptr_int;
  tree void_ftype_ptr;
  tree void_ftype_ptr_ins_int_int_ptr_ptr_int;
  tree int_ftype_ptr_ptr_int_ptr_ptr_int;
  tree void_ftype_int_int_int_ptr_ptr_ptr_int;
  tree int_ftype_ptr_int_ptr_int_ptr_ptr_ptr_int;
  tree int_ftype_ptr_int;

  /* type of tasking code variables */
  chill_taskingcode_type_node = short_unsigned_type_node;

  void_ftype_void =
       build_function_type (void_type_node,
	 tree_cons (NULL_TREE, void_type_node, NULL_TREE));

  build_instance_type ();
  ins_ftype_void
    = build_function_type (instance_type_node,
        tree_cons (NULL_TREE, void_type_node,
	  build_tree_list (NULL_TREE, void_type_node)));

  builtin_function ("__whoami", ins_ftype_void,
		    0, NOT_BUILT_IN, NULL_PTR);

  build_tasking_message_type ();
   
  temp = build_decl (TYPE_DECL,
	   get_identifier ("__tmp_TaskingStruct"),
	     build_tasking_struct ());
  pushdecl (temp);
  DECL_SOURCE_LINE (temp) = 0;

  /* any SIGNAL will be compatible with this one */
  generic_signal_type_node = copy_node (boolean_type_node);

  builtin_function ((ignore_case || ! special_UC) ? "copy_number" : "COPY_NUMBER",
		    chill_predefined_function_type,
		    BUILT_IN_COPY_NUMBER, BUILT_IN_NORMAL, NULL_PTR);
  builtin_function ((ignore_case || ! special_UC) ? "gen_code" : "GEN_CODE",
		    chill_predefined_function_type,
		    BUILT_IN_GEN_CODE, BUILT_IN_NORMAL, NULL_PTR);
  builtin_function ((ignore_case || ! special_UC) ? "gen_inst" : "GEN_INST",
		    chill_predefined_function_type,
		    BUILT_IN_GEN_INST, BUILT_IN_NORMAL, NULL_PTR);
  builtin_function ((ignore_case || ! special_UC) ? "gen_ptype" : "GEN_PTYPE",
		    chill_predefined_function_type,
		    BUILT_IN_GEN_PTYPE, BUILT_IN_NORMAL, NULL_PTR);
  builtin_function ((ignore_case || ! special_UC) ? "proc_type" : "PROC_TYPE",
		    chill_predefined_function_type,
		    BUILT_IN_PROC_TYPE, BUILT_IN_NORMAL, NULL_PTR);
  builtin_function ((ignore_case || ! special_UC) ? "queue_length" : "QUEUE_LENGTH",
		    chill_predefined_function_type,
		    BUILT_IN_QUEUE_LENGTH, BUILT_IN_NORMAL, NULL_PTR);

  int_ftype_ptr_int_ptr_ptr_int_ptr_int_ptr_ptr_int
     = build_function_type (integer_type_node,
         tree_cons (NULL_TREE, ptr_type_node,
	   tree_cons (NULL_TREE, integer_type_node,
	     tree_cons (NULL_TREE, ptr_type_node,
	       tree_cons (NULL_TREE, ptr_type_node,
		 tree_cons (NULL_TREE, integer_type_node,
		   tree_cons (NULL_TREE, ptr_type_node,
		     tree_cons (NULL_TREE, integer_type_node,
                       tree_cons (NULL_TREE, ptr_type_node,
                         tree_cons (NULL_TREE, ptr_type_node,
                           tree_cons (NULL_TREE, integer_type_node,
			     endlink)))))))))));
  void_ftype_ptr
     = build_function_type (void_type_node,
           tree_cons (NULL_TREE, ptr_type_node, endlink));

  int_ftype_ptr_int_ptr_int_ptr_ptr_ptr_int
     = build_function_type (integer_type_node,
         tree_cons (NULL_TREE, ptr_type_node,
	   tree_cons (NULL_TREE, integer_type_node,
	     tree_cons (NULL_TREE, ptr_type_node,
               tree_cons (NULL_TREE, integer_type_node,
		 tree_cons (NULL_TREE, ptr_type_node,
		   tree_cons (NULL_TREE, ptr_type_node,
		     tree_cons (NULL_TREE, ptr_type_node,
		       tree_cons (NULL_TREE, integer_type_node,
		         endlink)))))))));

  void_ftype_ptr_ins_int_int_ptr_ptr_int
    = build_function_type (void_type_node,
	  tree_cons (NULL_TREE, ptr_type_node,
	      tree_cons (NULL_TREE, instance_type_node,
		  tree_cons (NULL_TREE, integer_type_node,
		      tree_cons (NULL_TREE, integer_type_node,
		        tree_cons (NULL_TREE, ptr_type_node,
			    tree_cons (NULL_TREE, ptr_type_node,
				tree_cons (NULL_TREE, integer_type_node,
		                    endlink))))))));
  int_ftype_ptr_ptr_int_ptr_ptr_int
    = build_function_type (integer_type_node,
	  tree_cons (NULL_TREE, ptr_type_node,
	    tree_cons (NULL_TREE, ptr_type_node,
	        tree_cons (NULL_TREE, integer_type_node,
		    tree_cons (NULL_TREE, ptr_type_node,
		        tree_cons (NULL_TREE, ptr_type_node,
		            tree_cons (NULL_TREE, integer_type_node,
		                endlink)))))));

  void_ftype_int_int_int_ptr_ptr_ptr_int
     = build_function_type (void_type_node,
	   tree_cons (NULL_TREE, integer_type_node,
	       tree_cons (NULL_TREE, integer_type_node,
		   tree_cons (NULL_TREE, integer_type_node,
		       tree_cons (NULL_TREE, ptr_type_node,
			   tree_cons (NULL_TREE, ptr_type_node,
		               tree_cons (NULL_TREE, ptr_type_node,
				   tree_cons (NULL_TREE, integer_type_node,
			               endlink))))))));

  int_ftype_ptr_int
     = build_function_type (integer_type_node,
	   tree_cons (NULL_TREE, ptr_type_node,
               tree_cons (NULL_TREE, integer_type_node,
                   endlink)));

  builtin_function ("__delay_event", int_ftype_ptr_int_ptr_int_ptr_ptr_ptr_int,
		    0, NOT_BUILT_IN, NULL_PTR);
  builtin_function ("__queue_length", int_ftype_ptr_int,
		    0, NOT_BUILT_IN, NULL_PTR);
  builtin_function ("__register_tasking", void_ftype_ptr,
		    0, NOT_BUILT_IN, NULL_PTR);
  builtin_function ("__send_signal", void_ftype_ptr_ins_int_int_ptr_ptr_int,
		    0, NOT_BUILT_IN, NULL_PTR);
  builtin_function ("__send_buffer", int_ftype_ptr_ptr_int_ptr_ptr_int,
		    0, NOT_BUILT_IN, NULL_PTR);
  builtin_function ("__start_process", void_ftype_int_int_int_ptr_ptr_ptr_int,
		    0, NOT_BUILT_IN, NULL_PTR);
  builtin_function ("__stop_process", void_ftype_void, 0, NOT_BUILT_IN,
		    NULL_PTR);
  builtin_function ("__wait_buffer", int_ftype_ptr_int_ptr_ptr_int_ptr_int_ptr_ptr_int,
		    0, NOT_BUILT_IN, NULL_PTR);
  builtin_function ("__wait_signal_timed", int_ftype_ptr_int_ptr_ptr_int_ptr_int_ptr_ptr_int,
		    0, NOT_BUILT_IN, NULL_PTR);

  infinite_buffer_event_length_node = build_int_2 (-1, 0);
  TREE_TYPE (infinite_buffer_event_length_node) = long_integer_type_node;
  TREE_UNSIGNED (infinite_buffer_event_length_node) = 1;
}
