/* Convert language-specific tree expression to rtl instructions,
   for GNU CHILL compiler.
   Copyright (C) 1992, 93, 1994, 1998, 1999 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 "rtl.h"
#include "tree.h"
#include "flags.h"
#include "expr.h"
#include "ch-tree.h"
#include "assert.h"
#include "lex.h"
#include "convert.h"
#include "toplev.h"

extern char **boolean_code_name;
extern int  flag_old_strings;
extern tree long_unsigned_type_node;
extern int  ignore_case;
extern int  special_UC;

/* definitions for duration built-ins */
#define MILLISECS_MULTIPLIER                                 1
#define SECS_MULTIPLIER            MILLISECS_MULTIPLIER * 1000
#define MINUTES_MULTIPLIER                SECS_MULTIPLIER * 60
#define HOURS_MULTIPLIER               MINUTES_MULTIPLIER * 60
#define DAYS_MULTIPLIER                  HOURS_MULTIPLIER * 24

/* the maximum value for each of the calls */
#define MILLISECS_MAX                               0xffffffff
#define SECS_MAX                                       4294967
#define MINUTES_MAX                                      71582
#define HOURS_MAX                                         1193
#define DAYS_MAX                                            49

/* forward declaration */
rtx chill_expand_expr PROTO((tree, rtx, enum machine_mode, 
			     enum expand_modifier));

/* variable to hold the type the DESCR built-in returns */
static tree descr_type = NULL_TREE;


/* called from ch-lex.l */
void
init_chill_expand ()
{
  lang_expand_expr = chill_expand_expr;
}

/* Take the address of something that needs to be passed by reference. */
tree
force_addr_of (value)
     tree value;
{
  /* FIXME.  Move to memory, if needed. */
  if (TREE_CODE (value) == INDIRECT_REF)
    return convert_to_pointer (ptr_type_node, TREE_OPERAND (value, 0));
  mark_addressable (value);
  return build1 (ADDR_EXPR, ptr_type_node, value);
}

/* Check that EXP has a known type. */

tree
check_have_mode (exp, context)
     tree exp;
     char *context;
{
  if (TREE_CODE (exp) != ERROR_MARK && TREE_TYPE (exp) == NULL_TREE)
    {
      if (TREE_CODE (exp) == CONSTRUCTOR)
	error ("tuple without specified mode not allowed in %s", context);
      else if (TREE_CODE (exp) == COND_EXPR || TREE_CODE (exp) == CASE_EXPR)
	error ("conditional expression not allowed in %s", context);
      else
	error ("internal error:  unknown expression mode in %s", context);

      return error_mark_node;
    }
  return exp;
}

/* Check that EXP is discrete.  Handle conversion if flag_old_strings. */

tree
check_case_selector (exp)
     tree exp;
{
  if (exp != NULL_TREE && TREE_TYPE (exp) != NULL_TREE)
    exp = convert_to_discrete (exp);
  if (exp)
    return exp;
  error ("CASE selector is not a discrete expression");
  return error_mark_node;
}

tree
check_case_selector_list (list)
     tree list;
{
  tree selector, exp, return_list = NULL_TREE;

  for (selector = list; selector != NULL_TREE; selector = TREE_CHAIN (selector))
    {
      exp = check_case_selector (TREE_VALUE (selector));
      if (exp == error_mark_node)
	{
	  return_list = error_mark_node;
	  break;
	}
      return_list = tree_cons (TREE_PURPOSE (selector), exp, return_list);
    }

  return nreverse(return_list);
}

tree
chill_expand_case_expr (expr)
     tree expr;
{
  tree selector_list = TREE_OPERAND (expr, 0), selector;
  tree alternatives  = TREE_OPERAND (expr, 1);
  tree type = TREE_TYPE (expr);
  int  else_seen = 0;
  tree result;

  if (TREE_CODE (selector_list) != TREE_LIST
    || TREE_CODE (alternatives) != TREE_LIST)
    abort();
  if (TREE_CHAIN (selector_list) != NULL_TREE)
    abort ();

  /* make a temp for the case result */
  result = decl_temp1 (get_unique_identifier ("CASE_EXPR"),
		       type, 0, NULL_TREE, 0, 0);

  selector = check_case_selector (TREE_VALUE (selector_list));

  expand_start_case (1, selector, TREE_TYPE (selector), "CASE expression");

  alternatives = nreverse (alternatives);
  for ( ; alternatives != NULL_TREE; alternatives = TREE_CHAIN (alternatives))
    { 
      tree labels = TREE_PURPOSE (alternatives), t;
      
      if (labels == NULL_TREE)
	{
	  chill_handle_case_default ();
	  else_seen++;
        }
      else
	{
	  tree label;
	  if (labels != NULL_TREE)
	    {
	      for (label = TREE_VALUE (labels);
		   label != NULL_TREE; label = TREE_CHAIN (label))
		chill_handle_case_label (TREE_VALUE (label), selector);
	      labels = TREE_CHAIN (labels);
	      if (labels != NULL_TREE)
		error ("The number of CASE selectors does not match the number "
                       "of CASE label lists");
		
	    }
        }

      t = build (MODIFY_EXPR, type, result,
		 convert (type, TREE_VALUE (alternatives)));
      TREE_SIDE_EFFECTS (t) = 1;
      expand_expr_stmt (t);
      expand_exit_something ();
    }

  if (!else_seen)
    {
      chill_handle_case_default ();
      expand_exit_something ();
#if 0
      expand_raise ();
#endif

      check_missing_cases (TREE_TYPE (selector));
    }

  expand_end_case (selector);
  return result;
}

/* Hook used by expand_expr to expand CHILL-specific tree codes.  */

rtx
chill_expand_expr (exp, target, tmode, modifier)
     tree exp;
     rtx target;
     enum machine_mode tmode;
     enum expand_modifier modifier;
{
  tree type = TREE_TYPE (exp);
  register enum machine_mode mode = TYPE_MODE (type);
  register enum tree_code code = TREE_CODE (exp);
  rtx original_target = target;
  rtx op0, op1;
  int ignore = target == const0_rtx;
  char *lib_func;                   /* name of library routine */

  if (ignore)
    target = 0, original_target = 0;

  /* No sense saving up arithmetic to be done
     if it's all in the wrong mode to form part of an address.
     And force_operand won't know whether to sign-extend or zero-extend.  */

  if (mode != Pmode && modifier == EXPAND_SUM)
    modifier = EXPAND_NORMAL;

  switch (code)
    {
    case STRING_EQ_EXPR:
    case STRING_LT_EXPR:
      {
	rtx func = gen_rtx (SYMBOL_REF, Pmode,
			    code == STRING_EQ_EXPR ? "__eqstring"
			    : "__ltstring");
	tree exp0 = TREE_OPERAND (exp, 0);
	tree exp1 = TREE_OPERAND (exp, 1);
	tree size0, size1;
	rtx op0, op1, siz0, siz1;
	if (chill_varying_type_p (TREE_TYPE (exp0)))
	  {
	    exp0 = save_if_needed (exp0);
	    size0 = convert (integer_type_node,
			     build_component_ref (exp0, var_length_id));
	    exp0 = build_component_ref (exp0, var_data_id);
	  }
	else
	  size0 = size_in_bytes (TREE_TYPE (exp0));
	if (chill_varying_type_p (TREE_TYPE (exp1)))
	  {
	    exp1 = save_if_needed (exp1);
	    size1 = convert (integer_type_node,
			     build_component_ref (exp1, var_length_id));
	    exp1 = build_component_ref (exp1, var_data_id);
	  }
	else
	  size1 = size_in_bytes (TREE_TYPE (exp1));

	op0 = expand_expr (force_addr_of (exp0),
			   NULL_RTX, MEM, EXPAND_CONST_ADDRESS);
	op1 = expand_expr (force_addr_of (exp1),
			   NULL_RTX, MEM, EXPAND_CONST_ADDRESS);
	siz0 = expand_expr (size0, NULL_RTX, VOIDmode, 0);
	siz1 = expand_expr (size1, NULL_RTX, VOIDmode, 0);
	return emit_library_call_value (func, target,
					0, QImode, 4,
					op0, GET_MODE (op0),
					siz0, TYPE_MODE (sizetype),
					op1, GET_MODE (op1),
					siz1, TYPE_MODE (sizetype));
      }

    case CASE_EXPR:
      return expand_expr (chill_expand_case_expr (exp),
			  NULL_RTX, VOIDmode, 0);
      break;

    case SLICE_EXPR:
      {
	tree func_call;
	tree array = TREE_OPERAND (exp, 0);
	tree min_value = TREE_OPERAND (exp, 1);
	tree length = TREE_OPERAND (exp, 2);
	tree new_type = TREE_TYPE (exp);
	tree temp = decl_temp1 (get_unique_identifier ("BITSTRING"),
				new_type, 0, NULL_TREE, 0, 0);
	if (! CH_REFERABLE (array) && TYPE_MODE (TREE_TYPE (array)) != BLKmode)
	  array = decl_temp1 (get_unique_identifier ("BSTRINGVAL"),
				TREE_TYPE (array), 0, array, 0, 0);
	func_call = build_chill_function_call (
		    lookup_name (get_identifier ("__psslice")),
			   tree_cons (NULL_TREE, 
                             build_chill_addr_expr (temp, (char *)0),
			       tree_cons (NULL_TREE, length,
		                 tree_cons (NULL_TREE,
                                    force_addr_of (array),
			             tree_cons (NULL_TREE, powersetlen (array),
			               tree_cons (NULL_TREE, convert (integer_type_node, min_value),
				         tree_cons (NULL_TREE, length, NULL_TREE)))))));
	expand_expr (func_call, const0_rtx, VOIDmode, 0);
	emit_queue ();
	return expand_expr (temp, ignore ? const0_rtx : target,
			    VOIDmode, 0);
      }
      
    /* void __concatstring (char *out, char *left, unsigned left_len,
                            char *right, unsigned right_len) */
    case CONCAT_EXPR:
      {
	tree exp0 = TREE_OPERAND (exp, 0);
	tree exp1 = TREE_OPERAND (exp, 1);
	rtx size0 = NULL_RTX, size1 = NULL_RTX;
	rtx targetx;

	if (TREE_CODE (exp1) == UNDEFINED_EXPR)
	  {
	    if (TYPE_MODE (TREE_TYPE (exp0)) == BLKmode
		&& TYPE_MODE (TREE_TYPE (exp)) == BLKmode)
	      {
		rtx temp = expand_expr (exp0, target, tmode, modifier);
		if (temp == target || target == NULL_RTX)
		  return temp;
		emit_block_move (target, temp, expr_size (exp0),
				 TYPE_ALIGN (TREE_TYPE(exp0)) / BITS_PER_UNIT);
		return target;
	      }
	    else
	      {
		exp0 = force_addr_of (exp0);
		exp0 = convert (build_pointer_type (TREE_TYPE (exp)), exp0);
		exp0 = build1 (INDIRECT_REF, TREE_TYPE (exp), exp0);
		return expand_expr (exp0,
				    NULL_RTX, Pmode, EXPAND_CONST_ADDRESS);
	      }
	  }

	if (TREE_CODE (type) == ARRAY_TYPE)
	  {
	    /* No need to handle scalars or varying strings here, since that
	       was done in convert or build_concat_expr. */
	    size0 = expand_expr (size_in_bytes (TREE_TYPE (exp0)),
				 NULL_RTX, Pmode, EXPAND_CONST_ADDRESS);

	    size1 = expand_expr (size_in_bytes (TREE_TYPE (exp1)),
				   NULL_RTX, Pmode, EXPAND_CONST_ADDRESS);

	    /* build a temp for the result, target is its address */
	    if (target == NULL_RTX)
	      {
		tree type0 = TREE_TYPE (exp0);
		tree type1 = TREE_TYPE (exp1);
		int	len0 = int_size_in_bytes (type0);
		int	len1 = int_size_in_bytes (type1);

		if (len0 < 0 && TYPE_ARRAY_MAX_SIZE (type0)
		    && TREE_CODE (TYPE_ARRAY_MAX_SIZE (type0)) == INTEGER_CST)
		  len0 = TREE_INT_CST_LOW (TYPE_ARRAY_MAX_SIZE (type0));

		if (len1 < 0 && TYPE_ARRAY_MAX_SIZE (type1)
		    && TREE_CODE (TYPE_ARRAY_MAX_SIZE (type1)) == INTEGER_CST)
		  len1 = TREE_INT_CST_LOW (TYPE_ARRAY_MAX_SIZE (type1));

		if (len0 < 0 || len1 < 0)
		  fatal ("internal error - don't know how much space is needed for concatenation");
		target = assign_stack_temp (mode, len0 + len1, 0);
		preserve_temp_slots (target);
	      }
	  }
	else if (TREE_CODE (type) == SET_TYPE)
	  {
	    if (target == NULL_RTX)
	      {
		target = assign_stack_temp (mode, int_size_in_bytes (type), 0);
		preserve_temp_slots (target);
	      }
	  }
	else
	  abort ();

	if (GET_CODE (target) == MEM)
	  targetx = target;
	else
	  targetx = assign_stack_temp (mode, GET_MODE_SIZE (mode), 0);

	/* expand 1st operand to a pointer to the array */
	op0 = expand_expr (force_addr_of (exp0),
			   NULL_RTX, MEM, EXPAND_CONST_ADDRESS);

	/* expand 2nd operand to a pointer to the array */
	op1 = expand_expr (force_addr_of (exp1),
			   NULL_RTX, MEM, EXPAND_CONST_ADDRESS);

	if (TREE_CODE (type) == SET_TYPE)
	  {
	    size0 = expand_expr (powersetlen (exp0),
				 NULL_RTX, VOIDmode, 0);
	    size1 = expand_expr (powersetlen (exp1),
				 NULL_RTX, VOIDmode, 0);

	    emit_library_call (gen_rtx(SYMBOL_REF, Pmode, "__concatps"),
			       0, Pmode, 5, XEXP (targetx, 0), Pmode,
			       op0, GET_MODE (op0),
			       convert_to_mode (TYPE_MODE (sizetype),
						size0, TREE_UNSIGNED (sizetype)),
			       TYPE_MODE (sizetype),
			       op1, GET_MODE (op1),
			       convert_to_mode (TYPE_MODE (sizetype),
						size1, TREE_UNSIGNED (sizetype)),
			       TYPE_MODE (sizetype));
	  }
	else
	  {
	    /* copy left, then right array to target */
	    emit_library_call (gen_rtx(SYMBOL_REF, Pmode, "__concatstring"),
			       0, Pmode, 5, XEXP (targetx, 0), Pmode,
			       op0, GET_MODE (op0),
			       convert_to_mode (TYPE_MODE (sizetype),
						size0, TREE_UNSIGNED (sizetype)),
			       TYPE_MODE (sizetype),
			       op1, GET_MODE (op1),
			       convert_to_mode (TYPE_MODE (sizetype),
						size1, TREE_UNSIGNED (sizetype)),
			       TYPE_MODE (sizetype));
	  }
	if (targetx != target)
	  emit_move_insn (target, targetx);
	return target;
      }

      /* FIXME: the set_length computed below is a compile-time constant;
	 you'll need to re-write that part for VARYING bit arrays, and
	 possibly the set pointer will need to be adjusted to point past
	 the word containing its dynamic length. */

    /* void __notpowerset (char *out, char *src,
       unsigned long bitlength) */
    case SET_NOT_EXPR:
      {
	
	tree expr = TREE_OPERAND (exp, 0);
	tree tsize = powersetlen (expr);
	rtx targetx;

	if (TREE_CODE (TREE_TYPE (expr)) != SET_TYPE)
	  tsize = fold (build (MULT_EXPR, sizetype, tsize, 
			       size_int (BITS_PER_UNIT)));

	/* expand 1st operand to a pointer to the set */
	op0 = expand_expr (force_addr_of (expr),
			   NULL_RTX, MEM, EXPAND_CONST_ADDRESS);

	/* build a temp for the result, target is its address */
	if (target == NULL_RTX)
	  {
	    target = assign_stack_temp (TYPE_MODE (TREE_TYPE (exp)), 
					int_size_in_bytes (TREE_TYPE (exp)),
					0);
	    preserve_temp_slots (target);
	  }
	if (GET_CODE (target) == MEM)
	  targetx = target;
	else
	  targetx = assign_stack_temp (GET_MODE (target),
				       GET_MODE_SIZE (GET_MODE (target)),
				       0);
	emit_library_call (gen_rtx(SYMBOL_REF, Pmode, "__notpowerset"), 
			   0, VOIDmode, 3, XEXP (targetx, 0), Pmode,
			   op0, GET_MODE (op0),
			   expand_expr (tsize, NULL_RTX, MEM, 
					EXPAND_CONST_ADDRESS),
			   TYPE_MODE (long_unsigned_type_node));
	if (targetx != target)
	  emit_move_insn (target, targetx);
	return target;
      }

    case SET_DIFF_EXPR:
      lib_func = "__diffpowerset";
      goto format_2;

    case SET_IOR_EXPR:
      lib_func = "__orpowerset";
      goto format_2;

    case SET_XOR_EXPR:
      lib_func = "__xorpowerset";
      goto format_2;

    /* void __diffpowerset (char *out, char *left, char *right,
                            unsigned bitlength) */
    case SET_AND_EXPR:
      lib_func = "__andpowerset";
    format_2:
      {
	tree expr = TREE_OPERAND (exp, 0);
	tree tsize = powersetlen (expr);
	rtx targetx;

	if (TREE_CODE (TREE_TYPE (expr)) != SET_TYPE)
	  tsize = fold (build (MULT_EXPR, long_unsigned_type_node,
			       tsize, 
			       size_int (BITS_PER_UNIT)));

	/* expand 1st operand to a pointer to the set */
        op0 = expand_expr (force_addr_of (expr),
			   NULL_RTX, MEM, EXPAND_CONST_ADDRESS);

	/* expand 2nd operand to a pointer to the set */
        op1 = expand_expr (force_addr_of (TREE_OPERAND (exp, 1)),
			   NULL_RTX, MEM,
			   EXPAND_CONST_ADDRESS);

/* FIXME: re-examine this code - the unary operator code above has recently
   (93/03/12) been changed a lot.  Should this code also change? */
	/* build a temp for the result, target is its address */
	if (target == NULL_RTX)
	  {
	    target = assign_stack_temp (TYPE_MODE (TREE_TYPE (exp)), 
					int_size_in_bytes (TREE_TYPE (exp)),
					0);
	    preserve_temp_slots (target);
	  }
	if (GET_CODE (target) == MEM)
	  targetx = target;
	else
	  targetx = assign_stack_temp (GET_MODE (target),
				       GET_MODE_SIZE (GET_MODE (target)), 0);
	emit_library_call (gen_rtx(SYMBOL_REF, Pmode, lib_func),
			   0, VOIDmode, 4, XEXP (targetx, 0), Pmode,
			   op0, GET_MODE (op0), op1, GET_MODE (op1),
			   expand_expr (tsize, NULL_RTX, MEM, 
					EXPAND_CONST_ADDRESS),
			   TYPE_MODE (long_unsigned_type_node));
	if (target != targetx)
	  emit_move_insn (target, targetx);
	return target;
      }

    case SET_IN_EXPR:
      {
	tree set = TREE_OPERAND (exp, 1);
	tree pos = convert (long_unsigned_type_node, TREE_OPERAND (exp, 0));
	tree set_type = TREE_TYPE (set);
	tree set_length = discrete_count (TYPE_DOMAIN (set_type));
	tree min_val = convert (long_integer_type_node,
				TYPE_MIN_VALUE (TYPE_DOMAIN (set_type)));
	tree fcall;
	
	/* FIXME: Function-call not needed if pos and width are constant! */
	if (! mark_addressable (set))
	  {
	    error ("powerset is not addressable");
	    return const0_rtx;
	  }
	/* we use different functions for bitstrings and powersets */
	if (CH_BOOLS_TYPE_P (set_type))
	  fcall =
             build_chill_function_call (
               lookup_name (get_identifier ("__inbitstring")),
	         tree_cons (NULL_TREE, 
	           convert (long_unsigned_type_node, pos), 
		     tree_cons (NULL_TREE,
		       build1 (ADDR_EXPR, build_pointer_type (set_type), set),
		         tree_cons (NULL_TREE, 
		           convert (long_unsigned_type_node, set_length),
		             tree_cons (NULL_TREE, min_val,
                               tree_cons (NULL_TREE, force_addr_of (get_chill_filename ()),
                                 build_tree_list (NULL_TREE, get_chill_linenumber ())))))));
	else
	  fcall =
             build_chill_function_call (
               lookup_name (get_identifier ("__inpowerset")),
	         tree_cons (NULL_TREE, 
	           convert (long_unsigned_type_node, pos), 
		     tree_cons (NULL_TREE,
		       build1 (ADDR_EXPR, build_pointer_type (set_type), set),
		         tree_cons (NULL_TREE, 
		           convert (long_unsigned_type_node, set_length),
		             build_tree_list (NULL_TREE, min_val)))));
	return expand_expr (fcall, NULL_RTX, VOIDmode, 0);
      }

    case PACKED_ARRAY_REF:
      {
	tree array = TREE_OPERAND (exp, 0);
	tree pos = save_expr (TREE_OPERAND (exp, 1));
	tree array_type = TREE_TYPE (array);
	tree array_length = discrete_count (TYPE_DOMAIN (array_type));
	tree min_val = convert (long_integer_type_node,
				TYPE_MIN_VALUE (TYPE_DOMAIN (array_type)));
	tree fcall;
	
	/* FIXME: Function-call not needed if pos and width are constant! */
	/* TODO: make sure this makes sense. */
	if (! mark_addressable (array))
	  {
	    error ("array is not addressable");
	    return const0_rtx;
	  }
	fcall =
	  build_chill_function_call (
               lookup_name (get_identifier ("__inpowerset")),
	         tree_cons (NULL_TREE, 
	           convert (long_unsigned_type_node, pos), 
		     tree_cons (NULL_TREE,
		       build1 (ADDR_EXPR, build_pointer_type (array_type), array),
		         tree_cons (NULL_TREE, 
		           convert (long_unsigned_type_node, array_length),
		             build_tree_list (NULL_TREE, min_val)))));
	return expand_expr (fcall, NULL_RTX, VOIDmode, 0);
      }

    case UNDEFINED_EXPR:
      if (target == 0)
	{
	  target = assign_stack_temp (TYPE_MODE (TREE_TYPE (exp)), 
				      int_size_in_bytes (TREE_TYPE (exp)), 0);
	  preserve_temp_slots (target);
	}
      /* We don't actually need to *do* anything ... */
      return target;

    default:
      break;
    }

  /* NOTREACHED */
  return NULL;
}

/* Check that the argument list has a length in [min_length .. max_length].
   (max_length == -1 means "infinite".)
   If so return the actual length.
   Otherwise, return an error message and return -1. */

static int
check_arglist_length (args, min_length, max_length, name)
     tree args;
     int min_length;
     int max_length;
     tree name;
{
  int length = list_length (args);
  if (length < min_length)
    error ("Too few arguments in call to `%s'", IDENTIFIER_POINTER (name));
  else if (max_length != -1 && length > max_length)
    error ("Too many arguments in call to `%s'", IDENTIFIER_POINTER (name));
  else
    return length;
  return -1;
}

/*
 * This is the code from c-typeck.c, with the C-specific cruft
 * removed (possibly I just didn't understand it, but it was
 * apparently simply discarding part of my LIST).
 */
static tree
internal_build_compound_expr (list, first_p)
     tree list;
     int first_p ATTRIBUTE_UNUSED;
{
  register tree rest;

  if (TREE_CHAIN (list) == 0)
    return TREE_VALUE (list);

  rest = internal_build_compound_expr (TREE_CHAIN (list), FALSE);

  if (! TREE_SIDE_EFFECTS (TREE_VALUE (list)))
    return rest;

  return build (COMPOUND_EXPR, TREE_TYPE (rest), TREE_VALUE (list), rest);
}


/* Given a list of expressions, return a compound expression
   that performs them all and returns the value of the last of them.  */
/* FIXME: this should be merged with the C version */
tree
build_chill_compound_expr (list)
     tree list;
{
  return internal_build_compound_expr (list, TRUE);
}

/* Given an expression PTR for a pointer, return an expression
   for the value pointed to.
   do_empty_check is 0, don't perform a NULL pointer check,
   else do it. */

tree
build_chill_indirect_ref (ptr, mode, do_empty_check)
     tree ptr;
     tree mode;
     int do_empty_check;
{
  register tree type;

  if (ptr == NULL_TREE || TREE_CODE (ptr) == ERROR_MARK)
    return ptr;
  if (mode != NULL_TREE && TREE_CODE (mode) == ERROR_MARK)
    return error_mark_node;

  type = TREE_TYPE (ptr);

  if (TREE_CODE (type) == REFERENCE_TYPE)
    {
      type = TREE_TYPE (type);
      ptr = convert (type, ptr);
    }

  /* check for ptr is really a POINTER */
  if (TREE_CODE (type) != POINTER_TYPE)
    {
      error ("cannot dereference, not a pointer.");
      return error_mark_node;
    }
  
  if (mode && TREE_CODE (mode) == IDENTIFIER_NODE)
    {
      tree decl = lookup_name (mode);
      if (decl == NULL_TREE || TREE_CODE (decl) != TYPE_DECL)
	{
	  if (pass == 2)
	    error ("missing '.' operator or undefined mode name `%s'.",
		   IDENTIFIER_POINTER (mode));
#if 0
	  error ("You have forgotten the '.' operator which must");
	  error (" precede a STRUCT field reference, or `%s' is an undefined mode", 
		 IDENTIFIER_POINTER (mode));
#endif
	  return error_mark_node;
	}
    }

  if (mode)
    {
      mode = get_type_of (mode);
      ptr = convert (build_pointer_type (mode), ptr);
    }
  else if (type == ptr_type_node)
    {
      error ("Can't dereference PTR value using unary `->'.");
      return error_mark_node;
    }

  if (do_empty_check)
    ptr = check_non_null (ptr);

  type = TREE_TYPE (ptr);

  if (TREE_CODE (type) == POINTER_TYPE)
    {
      if (TREE_CODE (ptr) == ADDR_EXPR
	  && !flag_volatile
	  && (TREE_TYPE (TREE_OPERAND (ptr, 0))
	      == TREE_TYPE (type)))
	return TREE_OPERAND (ptr, 0);
      else
	{
	  tree t = TREE_TYPE (type);
	  register tree ref = build1 (INDIRECT_REF,
				      TYPE_MAIN_VARIANT (t), ptr);

	  if (TYPE_SIZE (t) == 0 && TREE_CODE (t) != ARRAY_TYPE)
	    {
	      error ("dereferencing pointer to incomplete type");
	      return error_mark_node;
	    }
	  if (TREE_CODE (t) == VOID_TYPE)
	    warning ("dereferencing `void *' pointer");

	  /* We *must* set TREE_READONLY when dereferencing a pointer to const,
	     so that we get the proper error message if the result is used
	     to assign to.  Also, &* is supposed to be a no-op.
	     And ANSI C seems to specify that the type of the result
	     should be the const type.  */
	  /* A de-reference of a pointer to const is not a const.  It is valid
	     to change it via some other pointer.  */
	  TREE_READONLY (ref) = TYPE_READONLY (t);
	  TREE_SIDE_EFFECTS (ref)
	    = TYPE_VOLATILE (t) || TREE_SIDE_EFFECTS (ptr) || flag_volatile;
	  TREE_THIS_VOLATILE (ref) = TYPE_VOLATILE (t) || flag_volatile;
	  return ref;
	}
    }
  else if (TREE_CODE (ptr) != ERROR_MARK)
    error ("invalid type argument of `->'");
  return error_mark_node;
}

/* NODE is a COMPONENT_REF whose mode is an IDENTIFIER,
   which is replaced by the proper FIELD_DECL.
   Also do the right thing for variant records. */

tree
resolve_component_ref (node)
     tree node;
{
  tree datum = TREE_OPERAND (node, 0);
  tree field_name = TREE_OPERAND (node, 1);
  tree type = TREE_TYPE (datum);
  tree field;
  if (TREE_CODE (datum) == ERROR_MARK)
    return error_mark_node;
  if (TREE_CODE (type) == REFERENCE_TYPE)
    {
      type = TREE_TYPE (type);
      TREE_OPERAND (node, 0) = datum = convert (type, datum);
    }
  if (TREE_CODE (type) != RECORD_TYPE)
    {
      error ("operand of '.' is not a STRUCT");
      return error_mark_node;
    }

  TREE_READONLY (node) = TREE_READONLY (datum);
  TREE_SIDE_EFFECTS (node) = TREE_SIDE_EFFECTS (datum);

  for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
    {
      if (TREE_CODE (TREE_TYPE (field)) == UNION_TYPE)
	{
	  tree variant;
	  for (variant = TYPE_FIELDS (TREE_TYPE (field));
	       variant;  variant = TREE_CHAIN (variant))
	    {
	      tree vfield;
	      for (vfield = TYPE_FIELDS (TREE_TYPE (variant));
		   vfield; vfield = TREE_CHAIN (vfield))
		{
		  if (DECL_NAME (vfield) == field_name)
		    { /* Found a variant field */
		      datum = build (COMPONENT_REF, TREE_TYPE (field),
				     datum, field);
		      datum = build (COMPONENT_REF, TREE_TYPE (variant),
				     datum, variant);
		      TREE_OPERAND (node, 0) = datum;
		      TREE_OPERAND (node, 1) = vfield;
		      TREE_TYPE (node) = TREE_TYPE (vfield);
		      TREE_READONLY (node) |= TYPE_READONLY (TREE_TYPE (node));
#if 0
		      if (flag_testing_tags)
			{
			  tree tagtest = NOT IMPLEMENTED;
			  tree tagf = ridpointers[(int) RID_RANGEFAIL];
			  node = check_expression (node, tagtest,
						   tagf);
			}
#endif
		      return node;
		    }
		}
	    }
	}

      if (DECL_NAME (field) == field_name)
	{ /* Found a fixed field */
	  TREE_OPERAND (node, 1) = field;
	  TREE_TYPE (node) = TREE_TYPE (field);
	  TREE_READONLY (node) |= TYPE_READONLY (TREE_TYPE (node));
	  return fold (node);
	}
    }

  error ("No field named `%s'", IDENTIFIER_POINTER (field_name));
  return error_mark_node;
}

tree
build_component_ref (datum, field_name)
  tree datum, field_name;
{
  tree node = build_nt (COMPONENT_REF, datum, field_name);
  if (pass != 1)
    node = resolve_component_ref (node);
  return node;
}

/*
 function checks (for build_chill_component_ref) if a given
 type is really an instance type. CH_IS_INSTANCE_MODE is not
 strict enough in this case, i.e. SYNMODE foo = STRUCT (a, b UINT)
 is compatible to INSTANCE. */

static int
is_really_instance (type)
     tree type;
{
  tree decl = TYPE_NAME (type);

  if (decl == NULL_TREE)
    /* this is not an instance */
    return 0;

  if (DECL_NAME (decl) == ridpointers[(int)RID_INSTANCE])
    /* this is an instance */
    return 1;

  if (TYPE_FIELDS (type) == TYPE_FIELDS (instance_type_node))
    /* we have a NEWMODE'd instance */
    return 1;

  return 0;
}

/* This function is called by the parse.
   Here we check if the user tries to access a field in a type which is
   layouted as a structure but isn't like INSTANCE, BUFFER, EVENT, ASSOCIATION,
   ACCESS, TEXT, or VARYING array or character string.
   We don't do this in build_component_ref cause this function gets
   called from the compiler to access fields in one of the above mentioned
   modes. */
tree
build_chill_component_ref (datum, field_name)
     tree datum, field_name;
{
  tree type = TREE_TYPE (datum);
  if ((type != NULL_TREE && TREE_CODE (type) == RECORD_TYPE) &&
      ((CH_IS_INSTANCE_MODE (type) && is_really_instance (type)) ||
	CH_IS_BUFFER_MODE (type) ||
       CH_IS_EVENT_MODE (type) || CH_IS_ASSOCIATION_MODE (type) ||
       CH_IS_ACCESS_MODE (type) || CH_IS_TEXT_MODE (type) ||
       chill_varying_type_p (type)))
    {
      error ("operand of '.' is not a STRUCT");
      return error_mark_node;
    }
  return build_component_ref (datum, field_name);
}

/*
 * Check for invalid binary operands & unary operands
 * RIGHT is 1 if checking right operand or unary operand;
 * it is 0 if checking left operand.
 *
 * return 1 if the given operand is NOT compatible as the
 * operand of the given operator
 *
 * return 0 if they might be compatible
 */
static int
invalid_operand (code, type, right)
     enum chill_tree_code code;
     tree type;
     int right; /* 1 if right operand */
{
  switch ((int)code)
    {
    case ADDR_EXPR:
      break;
    case BIT_AND_EXPR:
    case BIT_IOR_EXPR:
    case BIT_NOT_EXPR:
    case BIT_XOR_EXPR:
      goto relationals;
    case CASE_EXPR:
      break;
    case CEIL_MOD_EXPR:
      goto numerics;
    case CONCAT_EXPR:           /* must be static or varying char array */
      if (TREE_CODE (type) == CHAR_TYPE)
	return 0;
      if (TREE_CODE (type) == ARRAY_TYPE 
	   && TREE_CODE (TREE_TYPE (type)) == CHAR_TYPE)
	return 0;
      if (!chill_varying_type_p (type))
	  return 1;
      if (TREE_CODE (TREE_TYPE (CH_VARYING_ARRAY_TYPE (type)))
            == CHAR_TYPE)
        return 0;
      else
        return 1;
    /* note: CHILL conditional expressions (COND_EXPR) won't come
     *  through here; they're routed straight to C-specific code */
    case EQ_EXPR:
      return 0;                  /* ANYTHING can be compared equal */
    case FLOOR_MOD_EXPR:
      if (TREE_CODE (type) == REAL_TYPE)
	return 1;
      goto numerics;
    case GE_EXPR:
    case GT_EXPR:
      goto relatables;
    case SET_IN_EXPR:
      if (TREE_CODE (type) == SET_TYPE)
        return 0;
      else
        return 1;
    case PACKED_ARRAY_REF:
      if (TREE_CODE (type) == ARRAY_TYPE)
        return 0;
      else
        return 1;
    case LE_EXPR:
    case LT_EXPR:
    relatables:
      switch ((int)TREE_CODE(type))   /* right operand must be set/bitarray type */
	{
	case ARRAY_TYPE:
	  if (TREE_CODE (TREE_TYPE (type)) == CHAR_TYPE)
	    return 0;
	  else
	    return 1;
	case BOOLEAN_TYPE:
	case CHAR_TYPE:
	case COMPLEX_TYPE:
	case ENUMERAL_TYPE:
	case INTEGER_TYPE:
	case OFFSET_TYPE:
	case POINTER_TYPE:
	case REAL_TYPE:
	case SET_TYPE:
	  return 0;
	case FILE_TYPE:
	case FUNCTION_TYPE:
	case GRANT_TYPE:
	case LANG_TYPE:
	case METHOD_TYPE:
	  return 1;
	case RECORD_TYPE:
	  if (chill_varying_type_p (type)
	      && TREE_CODE (TREE_TYPE (CH_VARYING_ARRAY_TYPE (type))) == CHAR_TYPE)
	    return 0;
	  else
	    return 1;
	case REFERENCE_TYPE:
	case SEIZE_TYPE:
	case UNION_TYPE:
	case VOID_TYPE:
	  return 1;
	}
      break;
    case MINUS_EXPR:
    case MULT_EXPR:
      goto numerics;
    case NEGATE_EXPR:
      if (TREE_CODE (type) == BOOLEAN_TYPE)
        return 0;
      else
	goto numerics;
    case NE_EXPR:
      return 0;                  /* ANYTHING can be compared unequal */
    case NOP_EXPR:
      return 0;                  /* ANYTHING can be converted */
    case PLUS_EXPR:
    numerics:
      switch ((int)TREE_CODE(type))   /* left operand must be discrete type */
	{
	case ARRAY_TYPE:
	  if (right || TREE_CODE (TREE_TYPE (type)) != BOOLEAN_TYPE)
	    return 1;
	  else
	    return 0;
	case CHAR_TYPE:
	  return right;
	case BOOLEAN_TYPE:
	case COMPLEX_TYPE:
	case FILE_TYPE:
	case FUNCTION_TYPE:
	case GRANT_TYPE:
	case LANG_TYPE:
	case METHOD_TYPE:
	case RECORD_TYPE:
	case REFERENCE_TYPE:
	case SEIZE_TYPE:
	case UNION_TYPE:
	case VOID_TYPE:
	  return 1;
	case ENUMERAL_TYPE:
	case INTEGER_TYPE:
	case OFFSET_TYPE:
	case POINTER_TYPE:
	case REAL_TYPE:
	case SET_TYPE:
	  return 0;
	}
      break;
    case RANGE_EXPR:
      break;

    case REPLICATE_EXPR:
      switch ((int)TREE_CODE(type))   /* right operand must be set/bitarray type */
	{
	case COMPLEX_TYPE:
	case FILE_TYPE:
	case FUNCTION_TYPE:
	case GRANT_TYPE:
	case LANG_TYPE:
	case METHOD_TYPE:
	case OFFSET_TYPE:
	case POINTER_TYPE:
	case RECORD_TYPE:
	case REAL_TYPE:
	case SEIZE_TYPE:
	case UNION_TYPE:
	case VOID_TYPE:
	  return 1;
	case ARRAY_TYPE:
	case BOOLEAN_TYPE:
	case CHAR_TYPE:
	case ENUMERAL_TYPE:
	case INTEGER_TYPE:
	case REFERENCE_TYPE:
	case SET_TYPE:
	  return 0;
	}
      
    case TRUNC_DIV_EXPR:
      goto numerics;
    case TRUNC_MOD_EXPR:
      if (TREE_CODE (type) == REAL_TYPE)
	return 1;
      goto numerics;
    case TRUTH_ANDIF_EXPR:
    case TRUTH_AND_EXPR:
    case TRUTH_NOT_EXPR:
    case TRUTH_ORIF_EXPR:
    case TRUTH_OR_EXPR:
    relationals:
      switch ((int)TREE_CODE(type))   /* left operand must be discrete type */
	{
	case ARRAY_TYPE:
	case CHAR_TYPE:
	case COMPLEX_TYPE:
	case ENUMERAL_TYPE:
	case FILE_TYPE:
	case FUNCTION_TYPE:
	case GRANT_TYPE:
	case INTEGER_TYPE:
	case LANG_TYPE:
	case METHOD_TYPE:
	case OFFSET_TYPE:
	case POINTER_TYPE:
	case REAL_TYPE:
	case RECORD_TYPE:
	case REFERENCE_TYPE:
	case SEIZE_TYPE:
	case UNION_TYPE:
	case VOID_TYPE:
	  return 1;
	case BOOLEAN_TYPE:
	case SET_TYPE:
	  return 0;
	}
      break;

    default:
      return 1;       /* perhaps you forgot to add a new DEFTREECODE? */
    }
  return 1;
}


static int
invalid_right_operand (code, type)
     enum chill_tree_code code;
     tree type;
{
  return invalid_operand (code, type, 1);
}

tree
build_chill_abs (expr)
     tree expr;
{
  tree temp;

  if (TREE_CODE (TREE_TYPE (expr)) == REAL_TYPE
      || discrete_type_p (TREE_TYPE (expr)))
    temp = fold (build1 (ABS_EXPR, TREE_TYPE (expr), expr));
  else 
    {
      error("ABS argument must be discrete or real mode");
      return error_mark_node;
    }
  /* FIXME: should call
   * cond_type_range_exception (temp);
   */
  return temp;
}

tree
build_chill_abstime (exprlist)
     tree exprlist;
{
  int  mask = 0, i, numargs;
  tree args = NULL_TREE;
  tree filename, lineno;
  int  had_errors = 0;
  tree tmp;

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

  /* check for integer expressions */
  i = 1;
  tmp = exprlist;
  while (tmp != NULL_TREE)
    {
      tree exp = TREE_VALUE (tmp);

      if (exp == NULL_TREE || TREE_CODE (exp) == ERROR_MARK)
	had_errors = 1;
      else if (TREE_CODE (TREE_TYPE (exp)) != INTEGER_TYPE)
	{
	  error ("argument %d to ABSTIME must be of integer type.", i);
	  had_errors = 1;
	}
      tmp = TREE_CHAIN (tmp);
      i++;
    }
  if (had_errors)
    return error_mark_node;

  numargs = list_length (exprlist);
  for (i = 0; i < numargs; i++)
    mask |= (1 << i);

  /* make it all arguments */
  for (i = numargs; i < 6; i++)
    exprlist = tree_cons (NULL_TREE, integer_zero_node, exprlist);

  args = tree_cons (NULL_TREE, build_int_2 (mask, 0), exprlist);

  filename = force_addr_of (get_chill_filename ());
  lineno = get_chill_linenumber ();
  args = chainon (args, tree_cons (NULL_TREE, filename,
			  tree_cons (NULL_TREE, lineno, NULL_TREE)));

  return build_chill_function_call (
    lookup_name (get_identifier ("_abstime")), args);
}


tree
build_allocate_memory_call (ptr, size)
  tree ptr, size;
{
  int err = 0;
    
  /* check for ptr is referable */
  if (! CH_REFERABLE (ptr))
    {
      error ("parameter 1 must be referable.");
      err++;
    }
   /* check for pointer */
  else if (TREE_CODE (TREE_TYPE (ptr)) != POINTER_TYPE)
    {
      error ("mode mismatch in parameter 1.");
      err++;
    }

  /* check for size > 0 if it is a constant */
  if (TREE_CODE (size) == INTEGER_CST && TREE_INT_CST_LOW (size) <= 0)
    {
      error ("parameter 2 must be a positive integer.");
      err++;
    }
  if (err)
    return error_mark_node;

  if (TREE_TYPE (ptr) != ptr_type_node)
    ptr = build_chill_cast (ptr_type_node, ptr);

  return build_chill_function_call (
    lookup_name (get_identifier ("_allocate_memory")),
           tree_cons (NULL_TREE, ptr,
	     tree_cons (NULL_TREE, size,
	       tree_cons (NULL_TREE, force_addr_of (get_chill_filename ()),
		 tree_cons (NULL_TREE, get_chill_linenumber (), 
			    NULL_TREE)))));
}


tree
build_allocate_global_memory_call (ptr, size)
  tree ptr, size;
{
  int err = 0;
    
  /* check for ptr is referable */
  if (! CH_REFERABLE (ptr))
    {
      error ("parameter 1 must be referable.");
      err++;
    }
  /* check for pointer */
  else if (TREE_CODE (TREE_TYPE (ptr)) != POINTER_TYPE)
    {
      error ("mode mismatch in parameter 1.");
      err++;
    }

  /* check for size > 0 if it is a constant */
  if (TREE_CODE (size) == INTEGER_CST && TREE_INT_CST_LOW (size) <= 0)
    {
      error ("parameter 2 must be a positive integer.");
      err++;
    }
  if (err)
    return error_mark_node;
    
  if (TREE_TYPE (ptr) != ptr_type_node)
    ptr = build_chill_cast (ptr_type_node, ptr);

  return build_chill_function_call (
    lookup_name (get_identifier ("_allocate_global_memory")),
           tree_cons (NULL_TREE, ptr,
	     tree_cons (NULL_TREE, size,
	       tree_cons (NULL_TREE, force_addr_of (get_chill_filename ()),
		 tree_cons (NULL_TREE, get_chill_linenumber (), 
			    NULL_TREE)))));
}


tree
build_return_memory (ptr)
  tree ptr;
{
  /* check input */
  if (ptr == NULL_TREE || TREE_CODE (ptr) == ERROR_MARK)
      return error_mark_node;
  
  /* check for pointer */
  if (TREE_CODE (TREE_TYPE (ptr)) != POINTER_TYPE)
    {
      error ("mode mismatch in parameter 1.");
      return error_mark_node;
    }

  if (TREE_TYPE (ptr) != ptr_type_node)
    ptr = build_chill_cast (ptr_type_node, ptr);

  return build_chill_function_call (
    lookup_name (get_identifier ("_return_memory")),
      tree_cons (NULL_TREE, ptr,
	tree_cons (NULL_TREE, force_addr_of (get_chill_filename ()),
	  tree_cons (NULL_TREE, get_chill_linenumber (), 
		     NULL_TREE))));
}


/* Compute the number of runtime members of the
 * given powerset.
 */
tree
build_chill_card (powerset)
     tree powerset;
{
  if (pass == 2)
    {
      tree temp;
      tree card_func = lookup_name (get_identifier ("__cardpowerset"));
      
      if (powerset == NULL_TREE || TREE_CODE (powerset) == ERROR_MARK)
	return error_mark_node;
      
      if (TREE_CODE (powerset) == IDENTIFIER_NODE)
	powerset = lookup_name (powerset);

      if (TREE_CODE (TREE_TYPE(powerset)) == SET_TYPE)
	{ int size;

	  /* Do constant folding, if possible. */
	  if (TREE_CODE (powerset) == CONSTRUCTOR
	      && TREE_CONSTANT (powerset)
	      && (size = int_size_in_bytes (TREE_TYPE (powerset))) >= 0)
	    {
	      int bit_size = size * BITS_PER_UNIT;
	      char* buffer = (char*) alloca (bit_size);
	      temp = get_set_constructor_bits (powerset, buffer, bit_size);
	      if (!temp)
		{ int i;
		  int count = 0;
		  for (i = 0; i < bit_size; i++)
		    if (buffer[i])
		      count++;
		  temp = build_int_2 (count, 0);
		  TREE_TYPE (temp) = TREE_TYPE (TREE_TYPE (card_func));
		  return temp;
		}
	    }
	  temp = build_chill_function_call (card_func,
		     tree_cons (NULL_TREE, force_addr_of (powerset),
		       tree_cons (NULL_TREE, powersetlen (powerset), NULL_TREE)));
	  /* FIXME: should call
	   * cond_type_range_exception (op0);
	   */
	  return temp;
	}
      error("CARD argument must be powerset mode");
      return error_mark_node;
    }
  return NULL_TREE;
}

/* function to build the type needed for the DESCR-built-in
 */

void build_chill_descr_type ()
{
  tree decl1, decl2;
  
  if (descr_type != NULL_TREE)
    /* already done */
    return;
  
  decl1 = build_decl (FIELD_DECL, get_identifier ("datap"), ptr_type_node);
  decl2 = build_decl (FIELD_DECL, get_identifier ("len"),
		      TREE_TYPE (lookup_name (
					      get_identifier ((ignore_case || ! special_UC) ? "ulong" : "ULONG"))));
  TREE_CHAIN (decl1) = decl2;
  TREE_CHAIN (decl2) = NULL_TREE;
  decl2 = build_chill_struct_type (decl1);
  descr_type = build_decl (TYPE_DECL, get_identifier ("__tmp_DESCR_type"), decl2);
  pushdecl (descr_type);
  DECL_SOURCE_LINE (descr_type) = 0;
  satisfy_decl (descr_type, 0);
}

/* build a pointer to a descriptor.
 * descriptor = STRUCT (datap PTR,
 *			len ULONG);
 * This descriptor is build in variable descr_type.
 */

tree
build_chill_descr (expr)
    tree expr;
{
  if (pass == 2)
    {
      tree tuple, decl, descr_var, datap, len, tmp;
      int is_static;

      if (expr == NULL_TREE || TREE_CODE (expr) == ERROR_MARK)
	return error_mark_node;
      
      /* check for expression is referable */
      if (! CH_REFERABLE (expr))
	{
	  error ("expression for DESCR-builtin must be referable.");
	  return error_mark_node;
	}
      
      mark_addressable (expr);
#if 0
      datap = build1 (ADDR_EXPR, build_chill_pointer_type (descr_type), expr);
#else
      datap = build_chill_arrow_expr (expr, 1);
#endif
      len = size_in_bytes (TREE_TYPE (expr));
      
      descr_var = get_unique_identifier ("DESCR");
      tuple = build_nt (CONSTRUCTOR, NULL_TREE,
			tree_cons (NULL_TREE, datap,
				   tree_cons (NULL_TREE, len, NULL_TREE)));

      is_static = (current_function_decl == global_function_decl) && TREE_STATIC (expr);
      decl = decl_temp1 (descr_var, TREE_TYPE (descr_type), is_static,
			 tuple, 0, 0);
#if 0
      tmp = force_addr_of (decl);
#else
      tmp = build_chill_arrow_expr (decl, 1);
#endif
      return tmp;
    }
  return NULL_TREE;
}

/* this function process the builtin's
   MILLISECS, SECS, MINUTES, HOURS and DAYS.
   The built duration value is in milliseconds. */

tree
build_chill_duration (expr, multiplier, fnname, maxvalue)
     tree           expr;
     unsigned long  multiplier;
     tree           fnname;
     unsigned long  maxvalue;
{
  tree temp;

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

  if (TREE_CODE (TREE_TYPE (expr)) != INTEGER_TYPE)
    {
      error ("argument to `%s' must be of integer type.", IDENTIFIER_POINTER (fnname));
      return error_mark_node;
    }

  temp = convert (duration_timing_type_node, expr);
  temp = fold (build (MULT_EXPR, duration_timing_type_node,
		      temp, build_int_2 (multiplier, 0)));

  if (range_checking)
    temp = check_range (temp, expr, integer_zero_node, build_int_2 (maxvalue, 0));

  return temp;
}

/* build function call to one of the floating point functions */
static tree
build_chill_floatcall (expr, chillname, funcname)
     tree expr;
     char *chillname;
     char *funcname;
{
  tree result;
  tree type;

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

  /* look if expr is a REAL_TYPE */
  type = TREE_TYPE (expr);
  if (type == NULL_TREE || TREE_CODE (type) == ERROR_MARK)
    return error_mark_node;
  if (TREE_CODE (type) != REAL_TYPE)
    {
      error ("argument 1 to `%s' must be of floating point mode", chillname);
      return error_mark_node;
    }
  result = build_chill_function_call (
             lookup_name (get_identifier (funcname)),
               tree_cons (NULL_TREE, expr, NULL_TREE));
  return result;
}

/* common function for ALLOCATE and GETSTACK */
static tree
build_allocate_getstack (mode, value, chill_name, fnname, filename, linenumber)
     tree mode;
     tree value;
     char *chill_name;
     char *fnname;
     tree filename;
     tree linenumber;
{
  tree type, result;
  tree expr = NULL_TREE;
  tree args, tmpvar, fncall, ptr, outlist = NULL_TREE;

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

  if (TREE_CODE (mode) == TYPE_DECL)
    type = TREE_TYPE (mode);
  else
    type = mode;

  /* check if we have a mode */
  if (TREE_CODE_CLASS (TREE_CODE (type)) != 't')
    {
      error ("First argument to `%s' must be a mode", chill_name);
      return error_mark_node;
    }

  /* check if we have a value if type is READonly */
  if (TYPE_READONLY_PROPERTY (type) && value == NULL_TREE)
    {
      error ("READonly modes for %s must have a value", chill_name);
      return error_mark_node;
    }

  if (value != NULL_TREE)
    {
      if (TREE_CODE (value) == ERROR_MARK)
	return error_mark_node;
      expr = chill_convert_for_assignment (type, value, "assignment");
    }

  /* build function arguments */
  if (filename == NULL_TREE)
    args = tree_cons (NULL_TREE, size_in_bytes (type), NULL_TREE);
  else
    args = tree_cons (NULL_TREE, size_in_bytes (type),
             tree_cons (NULL_TREE, force_addr_of (filename),
               tree_cons (NULL_TREE, linenumber, NULL_TREE)));

  ptr = build_chill_pointer_type (type);
  tmpvar = decl_temp1 (get_unique_identifier (chill_name),
		       ptr, 0, NULL_TREE, 0, 0);
  fncall = build_chill_function_call (
             lookup_name (get_identifier (fnname)), args);
  outlist = tree_cons (NULL_TREE,
               build_chill_modify_expr (tmpvar, fncall), outlist);
  if (expr == NULL_TREE)
    {
      /* set allocated memory to 0 */
      fncall = build_chill_function_call (
                 lookup_name (get_identifier ("memset")),
                   tree_cons (NULL_TREE, convert (ptr_type_node, tmpvar),
                     tree_cons (NULL_TREE, integer_zero_node,
                       tree_cons (NULL_TREE, size_in_bytes (type), NULL_TREE))));
      outlist = tree_cons (NULL_TREE, fncall, outlist);
    }
  else
    {
      /* write the init value to allocated memory */
      outlist = tree_cons (NULL_TREE,
                  build_chill_modify_expr (build_chill_indirect_ref (tmpvar, NULL_TREE, 0),
					   expr), 
			   outlist);
    }
  outlist = tree_cons (NULL_TREE, tmpvar, outlist);
  result = build_chill_compound_expr (nreverse (outlist));
  return result;
}

/* process the ALLOCATE built-in */
tree
build_chill_allocate (mode, value)
     tree mode;
     tree value;
{
  return build_allocate_getstack (mode, value, "ALLOCATE", "__allocate",
				  get_chill_filename (), get_chill_linenumber ());
}

/* process the GETSTACK built-in */
tree
build_chill_getstack (mode, value)
     tree mode;
     tree value;
{
  return build_allocate_getstack (mode, value, "GETSTACK", "__builtin_alloca",
				  NULL_TREE, NULL_TREE);
}

/* process the TERMINATE built-in */
tree
build_chill_terminate (ptr)
     tree ptr;
{
  tree result;
  tree type;

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

  type = TREE_TYPE (ptr);
  if (type == NULL_TREE || TREE_CODE (type) != POINTER_TYPE)
    {
      error ("argument to TERMINATE must be a reference primitive value");
      return error_mark_node;
    }
  result = build_chill_function_call (
	     lookup_name (get_identifier ("__terminate")),
	       tree_cons (NULL_TREE, convert (ptr_type_node, ptr),
                 tree_cons (NULL_TREE, force_addr_of (get_chill_filename ()),
                   tree_cons (NULL_TREE, get_chill_linenumber (), NULL_TREE))));
  return result;
}

/* build the type passed to _inttime function */
void
build_chill_inttime_type ()
{
  tree idxlist;
  tree arrtype;
  tree decl;

  idxlist = build_tree_list (NULL_TREE,
               build_chill_range_type (NULL_TREE,
				       integer_zero_node,
				       build_int_2 (5, 0)));
  arrtype = build_chill_array_type (ptr_type_node, idxlist, 0, NULL_TREE);

  decl = build_decl (TYPE_DECL, get_identifier ("__tmp_INTTIME_type"), arrtype);
  pushdecl (decl);
  DECL_SOURCE_LINE (decl) = 0;
  satisfy_decl (decl, 0);
}

tree
build_chill_inttime (t, loclist)
     tree t, loclist;
{
  int  had_errors = 0, cnt;
  tree tmp;
  tree init = NULL_TREE;
  int  numargs;
  tree tuple, var;

  if (t == NULL_TREE || TREE_CODE (t) == ERROR_MARK)
    return error_mark_node;
  if (loclist == NULL_TREE || TREE_CODE (loclist) == ERROR_MARK)
    return error_mark_node;

  /* check first argument to be NEWMODE TIME */
  if (TREE_TYPE (t) != abs_timing_type_node)
    {
      error ("argument 1 to INTTIME must be of mode TIME.");
      had_errors = 1;
    }

  cnt = 2;
  tmp = loclist;
  while (tmp != NULL_TREE)
    {
      tree loc = TREE_VALUE (tmp);
      char errmsg[200];
      char *p, *p1;
      int  write_error = 0;

      sprintf (errmsg, "argument %d to INTTIME must be ", cnt);
      p = errmsg + strlen (errmsg);
      p1 = p;
      
      if (loc == NULL_TREE || TREE_CODE (loc) == ERROR_MARK)
	had_errors = 1;
      else
	{
	  if (! CH_REFERABLE (loc))
	    {
	      strcpy (p, "referable");
	      p += strlen (p);
	      write_error = 1;
	      had_errors = 1;
	    }
	  if (TREE_CODE (TREE_TYPE (loc)) != INTEGER_TYPE)
	    {
	      if (p != p1)
		{
		  strcpy (p, " and ");
		  p += strlen (p);
		}
	      strcpy (p, "of integer type");
	      write_error = 1;
	      had_errors = 1;
	    }
	  /* FIXME: what's about ranges can't hold the result ?? */
	  if (write_error)
	    error ("%s.", errmsg);
	}
      /* next location */
      tmp = TREE_CHAIN (tmp);
      cnt++;
    }

  if (had_errors)
    return error_mark_node;

  /* make it always 6 arguments */
  numargs = list_length (loclist);
  for (cnt = numargs; cnt < 6; cnt++)
    init = tree_cons (NULL_TREE, null_pointer_node, init);

  /* append the given one's */
  tmp = loclist;
  while (tmp != NULL_TREE)
    {
      init = chainon (init,
		      build_tree_list (NULL_TREE,
				       build_chill_descr (TREE_VALUE (tmp))));
      tmp = TREE_CHAIN (tmp);
    }

  tuple = build_nt (CONSTRUCTOR, NULL_TREE, init);
  var = decl_temp1 (get_unique_identifier ("INTTIME"),
		    TREE_TYPE (lookup_name (get_identifier ("__tmp_INTTIME_type"))),
		    0, tuple, 0, 0);

  return build_chill_function_call (
    lookup_name (get_identifier ("_inttime")),
       tree_cons (NULL_TREE, t,
          tree_cons (NULL_TREE, force_addr_of (var),
		     NULL_TREE)));
}


/* Compute the runtime length of the given string variable
 * or expression.
 */
tree
build_chill_length (expr)
     tree expr;
{
  if (pass == 2)
    {
      tree type;
      
      if (expr == NULL_TREE || TREE_CODE (expr) == ERROR_MARK)
	return error_mark_node;
      
      if (TREE_CODE (expr) == IDENTIFIER_NODE)
	expr = lookup_name (expr);

      type = TREE_TYPE (expr);
      
      if (TREE_CODE(type) == ERROR_MARK)
	return type;
      if (chill_varying_type_p (type))
	{ 
	  tree temp = convert (integer_type_node,
			  build_component_ref (expr, var_length_id));
	  /* FIXME: should call
	   * cond_type_range_exception (temp);
	   */
	  return temp;
	}
      
      if ((TREE_CODE (type) == ARRAY_TYPE ||
	   /* should work for a bitstring too */
	   (TREE_CODE (type) == SET_TYPE && TREE_CODE (TREE_TYPE (type)) == BOOLEAN_TYPE)) &&
	  integer_zerop (TYPE_MIN_VALUE (TYPE_DOMAIN (type))))
	{
	  tree temp =  fold (build (PLUS_EXPR, chill_integer_type_node,
				    integer_one_node,
				    TYPE_MAX_VALUE (TYPE_DOMAIN (type))));
	  return convert (chill_integer_type_node, temp);
	}
      
      if (CH_IS_BUFFER_MODE (type) || CH_IS_EVENT_MODE (type))
        {
          tree len = max_queue_size (type);
          
          if (len == NULL_TREE)
            len = integer_minus_one_node;
          return len;
        }

      if (CH_IS_TEXT_MODE (type))
	{
	  if (TREE_CODE (expr) == TYPE_DECL)
	    {
	      /* text mode name */
	      return text_length (type);
	    }
	  else
	    {
	      /* text location */
	      tree temp = build_component_ref (
			    build_component_ref (expr, get_identifier ("tloc")),
                                var_length_id);
	      return convert (integer_type_node, temp);
	    }
	}
 
      error("LENGTH argument must be string, buffer, event mode, text location or mode");
      return error_mark_node;
    }
  return NULL_TREE;
}

/* Compute the declared minimum/maximum value of the variable,
 * expression or declared type
 */
static tree
build_chill_lower_or_upper (what, is_upper)
     tree what;
     int is_upper;  /* o -> LOWER; 1 -> UPPER */
{
  if (pass == 2)
    {
      tree type;
      struct ch_class class;

      if (what == NULL_TREE || TREE_CODE (what) == ERROR_MARK)
	return error_mark_node;
      
      if (TREE_CODE_CLASS (TREE_CODE (what)) == 't')
	type = what;
      else
	type = TREE_TYPE (what);
      if (type == NULL_TREE)
	{
	  if (is_upper)
	    error ("UPPER argument must have a mode, or be a mode");
	  else
	    error ("LOWER argument must have a mode, or be a mode");
	  return error_mark_node;
	}
      while (TREE_CODE (type) == REFERENCE_TYPE)
	type = TREE_TYPE (type);
      if (chill_varying_type_p (type))
	type = CH_VARYING_ARRAY_TYPE (type);
     
      if (discrete_type_p (type))
	{
	  tree val = is_upper ? TYPE_MAX_VALUE (type) : TYPE_MIN_VALUE (type);
	  class.kind = CH_VALUE_CLASS;
	  class.mode = type;
	  return convert_to_class (class, val);
	}
      else if (TREE_CODE (type) == ARRAY_TYPE || TREE_CODE (type) == SET_TYPE)
	{
	  if (TYPE_STRING_FLAG (type))
	    {
	      class.kind = CH_DERIVED_CLASS;
	      class.mode = integer_type_node;
	    }
	  else
	    {
	      class.kind = CH_VALUE_CLASS;
	      class.mode = TYPE_DOMAIN (type);
	    }
	  type = TYPE_DOMAIN (type);
	  return convert_to_class (class,
				   is_upper
				   ? TYPE_MAX_VALUE (type)
				   : TYPE_MIN_VALUE (type));
	}
      if (is_upper)
	error("UPPER argument must be string, array, mode or integer");
      else
	error("LOWER argument must be string, array, mode or integer");
      return error_mark_node;
    }
  return NULL_TREE;
}

tree
build_chill_lower (what)
     tree what;
{
  return build_chill_lower_or_upper (what, 0);
}

static tree
build_max_min (expr, max_min)
     tree expr;
     int max_min; /* 0: calculate MIN; 1: calculate MAX */
{
  if (pass == 2)
    {
      tree type, temp, setminval;
      tree set_base_type;
      int size_in_bytes;
      
      if (expr == NULL_TREE || TREE_CODE (expr) == ERROR_MARK)
	return error_mark_node;
      
      if (TREE_CODE (expr) == IDENTIFIER_NODE)
	expr = lookup_name (expr);

      type = TREE_TYPE (expr);
      set_base_type = TYPE_DOMAIN (type);
      setminval = TYPE_MIN_VALUE (set_base_type);
      
      if (TREE_CODE (type) != SET_TYPE)
	{
	  error("%s argument must be POWERSET mode",
		max_min ? "MAX" : "MIN");
	  return error_mark_node;
	}

      /* find max/min of constant powerset at compile time */
      if (TREE_CODE (expr) == CONSTRUCTOR && TREE_CONSTANT (expr)
	  && (size_in_bytes = int_size_in_bytes (type)) >= 0)
	{
	  HOST_WIDE_INT min_val = -1, max_val = -1;
	  HOST_WIDE_INT i, i_hi = 0;
	  HOST_WIDE_INT size_in_bits = size_in_bytes * BITS_PER_UNIT;
	  char *buffer = (char*) alloca (size_in_bits);
	  if (buffer == NULL
	      || get_set_constructor_bits (expr, buffer, size_in_bits))
	    abort ();
	  for (i = 0; i < size_in_bits; i++)
	    {
	      if (buffer[i])
		{
		  if (min_val < 0)
		    min_val = i;
		  max_val = i;
		}
	    }
	  if (min_val < 0)
	    error ("%s called for empty POWERSET", max_min ? "MAX" : "MIN");
	  i = max_min ? max_val : min_val;
	  temp = TYPE_MIN_VALUE (TYPE_DOMAIN (TREE_TYPE (expr)));
	  add_double (i, i_hi,
		      TREE_INT_CST_LOW (temp), TREE_INT_CST_HIGH (temp),
		      &i, &i_hi);
	  temp = build_int_2 (i, i_hi);
	  TREE_TYPE (temp) = set_base_type;
	  return temp;
	}
      else
	{
	  tree parmlist, filename, lineno;
	  char *funcname;
	  
	  /* set up to call appropriate runtime function */
	  if (max_min)
	    funcname = "__flsetpowerset";
	  else
	    funcname = "__ffsetpowerset";
	  
	  setminval = convert (long_integer_type_node, setminval);
	  filename = force_addr_of (get_chill_filename());
	  lineno = get_chill_linenumber();
	  parmlist = tree_cons (NULL_TREE, force_addr_of (expr),
		       tree_cons (NULL_TREE, powersetlen (expr),
			 tree_cons (NULL_TREE, setminval,
			   tree_cons (NULL_TREE, filename,
			     build_tree_list (NULL_TREE, lineno)))));
	  temp = lookup_name (get_identifier (funcname));
	  temp = build_chill_function_call (temp, parmlist);
	  TREE_TYPE (temp) = set_base_type;
	  return temp;
	}
    }
  return NULL_TREE;
}


/* Compute the current runtime maximum value of the powerset
 */
tree
build_chill_max (expr)
     tree expr;
{
  return build_max_min (expr, 1);
}


/* Compute the current runtime minimum value of the powerset
 */
tree
build_chill_min (expr)
     tree expr;
{
  return build_max_min (expr, 0);
}


/* Build a conversion from the given expression to an INT,
 * but only when the expression's type is the same size as
 * an INT.
 */
tree
build_chill_num (expr)
     tree expr;
{
  if (pass == 2)
    {
      tree temp;
      int need_unsigned;

      if (expr == NULL_TREE || TREE_CODE(expr) == ERROR_MARK)
	return error_mark_node;
      
      if (TREE_CODE (expr) == IDENTIFIER_NODE)
	expr = lookup_name (expr);

      expr = convert_to_discrete (expr);
      if (expr == NULL_TREE)
	{
	  error ("argument to NUM is not discrete");
	  return error_mark_node;
	}

      /* enumeral types and string slices of length 1 must be kept unsigned */
      need_unsigned = (TREE_CODE (TREE_TYPE (expr)) == ENUMERAL_TYPE)
	|| TREE_UNSIGNED (TREE_TYPE (expr));

      temp = type_for_size (TYPE_PRECISION (TREE_TYPE (expr)), 
			    need_unsigned);
      if (temp == NULL_TREE)
	{
	  error ("No integer mode which matches expression's mode");
	  return integer_zero_node;
	}
      temp = convert (temp, expr);

      if (TREE_CONSTANT (temp))
	{
	  if (tree_int_cst_lt (temp, 
			       TYPE_MIN_VALUE (TREE_TYPE (temp))))
	    error ("NUM's parameter is below its mode range");
	  if (tree_int_cst_lt (TYPE_MAX_VALUE (TREE_TYPE (temp)),
	                       temp))
	    error ("NUM's parameter is above its mode range");
	}
#if 0
      else
	{
	  if (range_checking)
	    cond_overflow_exception (temp, 
	      TYPE_MIN_VALUE (TREE_TYPE (temp)),
	      TYPE_MAX_VALUE (TREE_TYPE (temp)));
	}
#endif

      /* NUM delivers the INT derived class */
      CH_DERIVED_FLAG (temp) = 1;
      
      return temp;
    }
  return NULL_TREE;
}


static tree
build_chill_pred_or_succ (expr, op)
     tree expr;
     enum tree_code op; /* PLUS_EXPR for SUCC; MINUS_EXPR for PRED. */
{
  struct ch_class class;
  tree etype, cond;

  if (pass == 1)
    return NULL_TREE;

  if (expr == NULL_TREE || TREE_CODE (expr) == ERROR_MARK)
    return error_mark_node;
  
  /* disallow numbered SETs */
  if (TREE_CODE (TREE_TYPE (expr)) == ENUMERAL_TYPE
      && CH_ENUM_IS_NUMBERED (TREE_TYPE (expr)))
    {
      error ("Cannot take SUCC or PRED of a numbered SET");
      return error_mark_node;
    }
  
  if (TREE_CODE (TREE_TYPE (expr)) == POINTER_TYPE)
    {
      if (TREE_TYPE (TREE_TYPE (expr)) == void_type_node)
	{
	  error ("SUCC or PRED must not be done on a PTR.");
	  return error_mark_node;
	}
      pedwarn ("SUCC or PRED for a reference type is not standard.");
      return fold (build (op, TREE_TYPE (expr),
			  expr,
			  size_in_bytes (TREE_TYPE (TREE_TYPE (expr)))));
    }

  expr = convert_to_discrete (expr);

  if (expr == NULL_TREE)
    {
      error ("SUCC or PRED argument must be a discrete mode");
      return error_mark_node;
    }

  class = chill_expr_class (expr);
  if (class.mode)
    class.mode = CH_ROOT_MODE (class.mode);
  etype = class.mode;
  expr = convert (etype, expr);

  /* Exception if expression is already at the
     min (PRED)/max(SUCC) valid value for its type. */
  cond = fold (build (op == PLUS_EXPR ? GE_EXPR : LE_EXPR,
		      boolean_type_node,
		      expr,
		      convert (etype,
			       op == PLUS_EXPR ? TYPE_MAX_VALUE (etype)
			       : TYPE_MIN_VALUE (etype))));
  if (TREE_CODE (cond) == INTEGER_CST
      && tree_int_cst_equal (cond, integer_one_node))
    {
      error ("Taking the %s of a value already at its %s value",
	     op == PLUS_EXPR ? "SUCC" : "PRED",
	     op == PLUS_EXPR ? "maximum" : "minimum");
      return error_mark_node;
    }

  if (range_checking)
    expr = check_expression (expr, cond,
			     ridpointers[(int) RID_OVERFLOW]);

  expr = fold (build (op, etype, expr, 
	   convert (etype, integer_one_node)));
  return convert_to_class (class, expr);
}

/* Compute the value of the CHILL `size' operator just
 * like the C 'sizeof' operator (code stolen from c-typeck.c)
 * TYPE may be a location or mode tree.  In pass 1, we build
 * a function-call syntax tree;  in pass 2, we evaluate it.
 */
tree
build_chill_sizeof (type)
     tree type;
{
  if (pass == 2)
    {
      tree temp;
      struct ch_class class;
      enum tree_code code;
      tree signame = NULL_TREE;

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

      if (TREE_CODE (type) == IDENTIFIER_NODE)
	type = lookup_name (type);

      code = TREE_CODE (type);
      if (code == ERROR_MARK)
	return error_mark_node;
      
      if (TREE_CODE_CLASS (TREE_CODE (type)) != 't')
	{
	  if (TREE_CODE (type) == TYPE_DECL && CH_DECL_SIGNAL (type))
	    signame = DECL_NAME (type);
	type = TREE_TYPE (type);
	}

      if (code == FUNCTION_TYPE)
	{
	  if (pedantic || warn_pointer_arith)
	    pedwarn ("size applied to a function mode");
	  return error_mark_node;
	}
      if (code == VOID_TYPE)
	{
	  if (pedantic || warn_pointer_arith)
	    pedwarn ("sizeof applied to a void mode");
	  return error_mark_node;
	}
      if (TYPE_SIZE (type) == 0)
	{
	  error ("sizeof applied to an incomplete mode");
	  return error_mark_node;
	}
      
      temp = size_binop (CEIL_DIV_EXPR, TYPE_SIZE (type),
			 size_int (TYPE_PRECISION (char_type_node)));
      if (signame != NULL_TREE)
        {
          /* we have a signal definition. This signal may have no
             data items specified. The definition however says that
             there are data, cause we cannot build a structure without
             fields. In this case return 0. */
          if (IDENTIFIER_SIGNAL_DATA (signame) == 0)
            temp = integer_zero_node;
        }
      
      /* FIXME: should call
       * cond_type_range_exception (temp);
       */
      class.kind = CH_DERIVED_CLASS;
      class.mode = integer_type_node;
      return convert_to_class (class, temp);
    }
  return NULL_TREE;
}

/* Compute the declared maximum value of the variable,
 * expression or declared type
 */
tree
build_chill_upper (what)
     tree what;
{
  return build_chill_lower_or_upper (what, 1);
}

/*
 * Here at the site of a function/procedure call..  We need to build
 * temps for the INOUT and OUT parameters, and copy the actual parameters
 * into the temps.  After the call, we 'copy back' the values from the
 * temps to the actual parameter variables.  This somewhat verbose pol-
 * icy meets the requirement that the actual parameters are undisturbed
 * if the function/procedure causes an exception.  They are updated only
 * upon a normal return from the function.
 *
 * Note: the expr_list, which collects all of the above assignments, etc,
 * is built in REVERSE execution order.  The list is corrected by nreverse
 * inside the build_chill_compound_expr call.
 */
tree
build_chill_function_call (function, expr)
     tree function, expr;
{
  register tree typetail, valtail, typelist;
  register tree temp, actual_args = NULL_TREE;
  tree name = NULL_TREE;
  tree function_call;
  tree fntype;
  int parmno = 1;            /* parameter number for error message */
  int callee_raise_exception = 0;

  /* list of assignments to run after the actual call,
     copying from the temps back to the user's variables. */
  tree copy_back = NULL_TREE;

  /* list of expressions to run before the call, copying from
     the user's variable to the temps that are passed to the function */
  tree expr_list = NULL_TREE;
 
  if (function == NULL_TREE || TREE_CODE (function) == ERROR_MARK)
    return error_mark_node;

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

  if (pass < 2)
    return error_mark_node;

  fntype = TREE_TYPE (function);
  if (TREE_CODE (function) == FUNCTION_DECL)
    {
      callee_raise_exception = TYPE_RAISES_EXCEPTIONS (fntype) != NULL_TREE;

      /* Differs from default_conversion by not setting TREE_ADDRESSABLE
	 (because calling an inline function does not mean the function
	 needs to be separately compiled).  */
      fntype = build_type_variant (fntype,
				   TREE_READONLY (function),
				   TREE_THIS_VOLATILE (function));
      name = DECL_NAME (function);

      /* check that function is not a PROCESS */
      if (CH_DECL_PROCESS (function))
	{
	  error ("cannot call a PROCESS, you START a PROCESS");
	  return error_mark_node;
	}

      function = build1 (ADDR_EXPR, build_pointer_type (fntype), function);
    }
  else if (TREE_CODE (fntype) == POINTER_TYPE)
    {
      fntype = TREE_TYPE (fntype);
      callee_raise_exception = TYPE_RAISES_EXCEPTIONS (fntype) != NULL_TREE;

      /* Z.200 6.7 Call Action:
	 "A procedure call causes the EMPTY exception if the
	 procedure primitive value delivers NULL. */
      if (TREE_CODE (function) != ADDR_EXPR
	  || TREE_CODE (TREE_OPERAND (function, 0)) != FUNCTION_DECL)
	function = check_non_null (function);
    }

  typelist = TYPE_ARG_TYPES (fntype);
  if (callee_raise_exception)
    {
      /* remove last two arguments from list for subsequent checking.
	  They will get added automatically after checking */
      int len = list_length (typelist);
      int i;
      tree newtypelist = NULL_TREE;
      tree wrk = typelist;
      
      for (i = 0; i < len - 3; i++)
	{
	    newtypelist = tree_cons (TREE_PURPOSE (wrk), TREE_VALUE (wrk), newtypelist);
	      wrk = TREE_CHAIN (wrk);
	  }
      /* add the void_type_node */
      newtypelist = tree_cons (NULL_TREE, void_type_node, newtypelist);
      typelist = nreverse (newtypelist);
    }

  /* Scan the given expressions and types, producing individual
     converted arguments and pushing them on ACTUAL_ARGS in 
     reverse order.  */
  for (valtail = expr, typetail = typelist;
       valtail != NULL_TREE && typetail != NULL_TREE;  parmno++,
       valtail = TREE_CHAIN (valtail), typetail = TREE_CHAIN (typetail))
    {
      register tree actual = TREE_VALUE (valtail);
      register tree attr   = TREE_PURPOSE (typetail)
	? TREE_PURPOSE (typetail) : ridpointers[(int) RID_IN];
      register tree type   = TREE_VALUE (typetail);
      char place[30];
      sprintf (place, "parameter %d", parmno);
 	  
      /* if we have reached void_type_node in typelist we are at the
	  end of formal parameters and then we have too many actual
	   parameters */
      if (type == void_type_node)
	 break;

      /* check if actual is a TYPE_DECL. FIXME: what else ? */
      if (TREE_CODE (actual) == TYPE_DECL)
	{
	  error ("invalid %s", place);
	  actual = error_mark_node;
	}
      /* INOUT or OUT param to handle? */
      else if (attr == ridpointers[(int) RID_OUT]
	  || attr == ridpointers[(int)RID_INOUT])
	{
	  char temp_name[20]; 
	  tree parmtmp;
	  tree in_actual = NULL_TREE, out_actual;

	  /* actual parameter must be a location so we can
	     build a reference to it */
	  if (!CH_LOCATION_P (actual))
	    {
	      error ("%s parameter %d must be a location", 
		     (attr == ridpointers[(int) RID_OUT]) ?
		     "OUT" : "INOUT", parmno);
	      continue;
	    }
	  if (TYPE_READONLY_PROPERTY (TREE_TYPE (actual))
	      || TREE_READONLY (actual))
	    {
	      error ("%s parameter %d is READ-only", 
		     (attr == ridpointers[(int) RID_OUT]) ?
		     "OUT" : "INOUT", parmno);
	      continue;
	    }

	  sprintf (temp_name, "PARM_%d_%s",  parmno,
		   (attr == ridpointers[(int)RID_OUT]) ?
		   "OUT" : "INOUT");
	  parmtmp = decl_temp1 (get_unique_identifier (temp_name),
				TREE_TYPE (type), 0, NULL_TREE, 0, 0);
	  /* this temp *must not* be optimized into a register */
	  mark_addressable (parmtmp);

	  if (attr == ridpointers[(int)RID_INOUT])
	    {
	      tree in_actual = chill_convert_for_assignment (TREE_TYPE (type),
							     actual, place);
	      tree tmp = build_chill_modify_expr (parmtmp, in_actual);
	      expr_list = tree_cons (NULL_TREE, tmp, expr_list);
	    }
	  if (in_actual != error_mark_node)
	    {
	      /* list of copy back assignments to perform, from the temp
		 back to the actual parameter */
	      out_actual = chill_convert_for_assignment (TREE_TYPE (actual),
							 parmtmp, place);
	      copy_back = tree_cons (NULL_TREE,
				     build_chill_modify_expr (actual,
							      out_actual),
				     copy_back);
	    }
	  /* we can do this because build_chill_function_type
	     turned these parameters into REFERENCE_TYPEs. */
	  actual = build1 (ADDR_EXPR, type, parmtmp);
	}
      else if (attr == ridpointers[(int) RID_LOC])
	{
	  int is_location = chill_location (actual);
	  if (is_location)
	    {
	      if (is_location == 1)
		{
		  error ("LOC actual parameter %d is a non-referable location",
			 parmno);
		  actual = error_mark_node;
		}
	      else if (! CH_READ_COMPATIBLE (type, TREE_TYPE (actual)))
		{
		  error ("mode mismatch in parameter %d", parmno);
		  actual = error_mark_node;
		}
	      else
		actual = convert (type, actual);
	    }
	  else
	    {
	      sprintf (place, "parameter_%d", parmno);
	      actual = decl_temp1 (get_identifier (place),
				   TREE_TYPE (type), 0, actual, 0, 0);
	      actual = convert (type, actual);
	    }
	  mark_addressable (actual);
	}
      else
	actual = chill_convert_for_assignment (type, actual, place);

      actual_args = tree_cons (NULL_TREE, actual, actual_args);
    }
 
  if (valtail != 0 && TREE_VALUE (valtail) != void_type_node)
    {
      char *errstr = "too many arguments to procedure";
      if (name)
	error ("%s `%s'", errstr, IDENTIFIER_POINTER (name));
      else
	error (errstr);
      return error_mark_node;
    }
  else if (typetail != 0 && TREE_VALUE (typetail) != void_type_node)
    {
      char *errstr = "too few arguments to procedure";
      if (name)
	error ("%s `%s'", errstr, IDENTIFIER_POINTER (name));
      else
	error (errstr);
      return error_mark_node;
    }
  
  if (callee_raise_exception)
    {
      /* add linenumber and filename of the caller as arguments */
      actual_args = tree_cons (NULL_TREE, force_addr_of (get_chill_filename ()),
			              actual_args);
      actual_args = tree_cons (NULL_TREE, get_chill_linenumber (), actual_args);
    }
  
  function_call = build (CALL_EXPR, TREE_TYPE (fntype),
			  function, nreverse (actual_args), NULL_TREE);
  TREE_SIDE_EFFECTS (function_call) = 1;

  if (copy_back == NULL_TREE && expr_list == NULL_TREE)
    return function_call;        /* no copying to do, either way */
  else
    {
      tree result_type = TREE_TYPE (fntype);
      tree result_tmp = NULL_TREE;

      /* no result wanted from procedure call */
      if (result_type == NULL_TREE || result_type == void_type_node)
	expr_list = tree_cons (NULL_TREE, function_call, expr_list);
      else
	{
	  /* create a temp for the function's result. this is so that we can
	     evaluate this temp as the last expression in the list, which will
	     make the function's return value the value of the whole list of
	     expressions (by the C rules for compound expressions) */
	  result_tmp = decl_temp1 (get_unique_identifier ("FUNC_RESULT"),
				   result_type, 0, NULL_TREE, 0, 0);
	  expr_list = tree_cons (NULL_TREE, 
	         	build_chill_modify_expr (result_tmp, function_call),
				 expr_list);
	}

      expr_list = chainon (copy_back, expr_list);

      /* last, but not least, the function's result */
      if (result_tmp != NULL_TREE)
	expr_list = tree_cons (NULL_TREE, result_tmp, expr_list);
      temp = build_chill_compound_expr (nreverse (expr_list));
      return temp;
    }
}

/* We saw something that looks like a function call,
   but if it's pass 1, we're not sure. */

tree
build_generalized_call (func, args)
     tree func, args;
{
  tree type = TREE_TYPE (func);

  if (pass == 1)
    return build (CALL_EXPR, NULL_TREE, func, args, NULL_TREE);

  /* Handle string repetition */
  if (TREE_CODE (func) == INTEGER_CST)
    {
      if (args == NULL_TREE || TREE_CHAIN (args) != NULL_TREE)
	{
	  error ("syntax error (integer used as function)");
	  return error_mark_node;
	}
      if (TREE_CODE (args) == TREE_LIST)
	args = TREE_VALUE (args);
      return build_chill_repetition_op (func, args);
    }

  if (args != NULL_TREE)
    {
      if (TREE_CODE (args) == RANGE_EXPR)
	{
	  tree lo = TREE_OPERAND (args, 0), hi = TREE_OPERAND (args, 1);
	  if (TREE_CODE_CLASS (TREE_CODE (func)) == 't')
	    return build_chill_range_type (func, lo, hi);
	  else
	    return build_chill_slice_with_range (func, lo, hi);
	}
      else if (TREE_CODE (args) != TREE_LIST)
	{
	  error ("syntax error - missing operator, comma, or '('?");
	  return error_mark_node;
	}
    }

  if (TREE_CODE (func) == TYPE_DECL)
    {
      if (CH_DECL_SIGNAL (func))
	return build_signal_descriptor (func, args);
      func = TREE_TYPE (func);
    }

  if (TREE_CODE_CLASS (TREE_CODE (func)) == 't'
      && args != NULL_TREE && TREE_CHAIN (args) == NULL_TREE)
    return build_chill_cast (func, TREE_VALUE (args));

  if (TREE_CODE (type) == FUNCTION_TYPE
      || (TREE_CODE (type) == POINTER_TYPE
	  && TREE_TYPE (type) != NULL_TREE
	  && TREE_CODE (TREE_TYPE (type)) == FUNCTION_TYPE))
    {
      /* Check for a built-in Chill function.  */
      if (TREE_CODE (func) == FUNCTION_DECL
	  && DECL_BUILT_IN (func)
	  && DECL_FUNCTION_CODE (func) > END_BUILTINS)
	{
	  tree fnname = DECL_NAME (func);
	  switch ((enum chill_built_in_function)DECL_FUNCTION_CODE (func))
	    {
	    case BUILT_IN_CH_ABS:
	      if (check_arglist_length (args, 1, 1, fnname) < 0)
		return error_mark_node;
	      return build_chill_abs (TREE_VALUE (args));
	    case BUILT_IN_ABSTIME:
	      if (check_arglist_length (args, 0, 6, fnname) < 0)
		return error_mark_node;
	      return build_chill_abstime (args);
	    case BUILT_IN_ADDR:
	      if (check_arglist_length (args, 1, 1, fnname) < 0)
		return error_mark_node;
#if 0
	      return build_chill_addr_expr (TREE_VALUE (args), (char *)0);
#else
	      return build_chill_arrow_expr (TREE_VALUE (args), 0);
#endif
	    case BUILT_IN_ALLOCATE_GLOBAL_MEMORY:
	      if (check_arglist_length (args, 2, 2, fnname) < 0)
		return error_mark_node;
	      return build_allocate_global_memory_call
		(TREE_VALUE (args),
		 TREE_VALUE (TREE_CHAIN (args)));
	    case BUILT_IN_ALLOCATE:
	      if (check_arglist_length (args, 1, 2, fnname) < 0)
		return error_mark_node;
	      return build_chill_allocate (TREE_VALUE (args),
                       TREE_CHAIN (args) == NULL_TREE ? NULL_TREE : TREE_VALUE (TREE_CHAIN (args)));
	    case BUILT_IN_ALLOCATE_MEMORY:
	      if (check_arglist_length (args, 2, 2, fnname) < 0)
		return error_mark_node;
	      return build_allocate_memory_call
		(TREE_VALUE (args),
		 TREE_VALUE (TREE_CHAIN (args)));
	    case BUILT_IN_ASSOCIATE:
	      if (check_arglist_length (args, 2, 3, fnname) < 0)
		return error_mark_node;
	      return build_chill_associate
		(TREE_VALUE (args),
		 TREE_VALUE (TREE_CHAIN (args)),
		 TREE_CHAIN (TREE_CHAIN (args)));
	    case BUILT_IN_ARCCOS:
	      if (check_arglist_length (args, 1, 1, fnname) < 0)
		return error_mark_node;
	      return build_chill_floatcall (TREE_VALUE (args),
					    IDENTIFIER_POINTER (fnname),
					    "__acos");
	    case BUILT_IN_ARCSIN:
	      if (check_arglist_length (args, 1, 1, fnname) < 0)
		return error_mark_node;
	      return build_chill_floatcall (TREE_VALUE (args),
					    IDENTIFIER_POINTER (fnname),
					    "__asin");
	    case BUILT_IN_ARCTAN:
	      if (check_arglist_length (args, 1, 1, fnname) < 0)
		return error_mark_node;
	      return build_chill_floatcall (TREE_VALUE (args),
					    IDENTIFIER_POINTER (fnname),
					    "__atan");
	    case BUILT_IN_CARD:
	      if (check_arglist_length (args, 1, 1, fnname) < 0)
		return error_mark_node;
	      return build_chill_card (TREE_VALUE (args));
	    case BUILT_IN_CONNECT:
	      if (check_arglist_length (args, 3, 5, fnname) < 0)
		return error_mark_node;
	      return build_chill_connect 
		(TREE_VALUE (args),
		 TREE_VALUE (TREE_CHAIN (args)),
		 TREE_VALUE (TREE_CHAIN (TREE_CHAIN (args))),
		 TREE_CHAIN (TREE_CHAIN (TREE_CHAIN (args))));
	    case BUILT_IN_COPY_NUMBER:
	      if (check_arglist_length (args, 1, 1, fnname) < 0)
		return error_mark_node;
	      return build_copy_number (TREE_VALUE (args));
	    case BUILT_IN_CH_COS:
	      if (check_arglist_length (args, 1, 1, fnname) < 0)
		return error_mark_node;
	      return build_chill_floatcall (TREE_VALUE (args),
					    IDENTIFIER_POINTER (fnname),
					    "__cos");
	    case BUILT_IN_CREATE:
	      if (check_arglist_length (args, 1, 1, fnname) < 0)
		return error_mark_node;
	      return build_chill_create (TREE_VALUE (args));
	    case BUILT_IN_DAYS:
	      if (check_arglist_length (args, 1, 1, fnname) < 0)
		return error_mark_node;
	      return build_chill_duration (TREE_VALUE (args), DAYS_MULTIPLIER,
					   fnname, DAYS_MAX);
	    case BUILT_IN_CH_DELETE:
	      if (check_arglist_length (args, 1, 1, fnname) < 0)
		return error_mark_node;
	      return build_chill_delete (TREE_VALUE (args));
	    case BUILT_IN_DESCR:
	      if (check_arglist_length (args, 1, 1, fnname) < 0)
		return error_mark_node;
	      return build_chill_descr (TREE_VALUE (args));
	    case BUILT_IN_DISCONNECT:
	      if (check_arglist_length (args, 1, 1, fnname) < 0)
		return error_mark_node;
	      return build_chill_disconnect (TREE_VALUE (args));
	    case BUILT_IN_DISSOCIATE:
	      if (check_arglist_length (args, 1, 1, fnname) < 0)
		return error_mark_node;
	      return build_chill_dissociate (TREE_VALUE (args));
	    case BUILT_IN_EOLN:
	      if (check_arglist_length (args, 1, 1, fnname) < 0)
		return error_mark_node;
	      return build_chill_eoln (TREE_VALUE (args));
	    case BUILT_IN_EXISTING:
	      if (check_arglist_length (args, 1, 1, fnname) < 0)
		return error_mark_node;
	      return build_chill_existing (TREE_VALUE (args));
	    case BUILT_IN_EXP:
	      if (check_arglist_length (args, 1, 1, fnname) < 0)
		return error_mark_node;
	      return build_chill_floatcall (TREE_VALUE (args),
					    IDENTIFIER_POINTER (fnname),
					    "__exp");
	    case BUILT_IN_GEN_CODE:
	      if (check_arglist_length (args, 1, 1, fnname) < 0)
		return error_mark_node;
	      return build_gen_code (TREE_VALUE (args));
	    case BUILT_IN_GEN_INST:
	      if (check_arglist_length (args, 2, 2, fnname) < 0)
		return error_mark_node;
	      return build_gen_inst (TREE_VALUE (args),
		 TREE_VALUE (TREE_CHAIN (args)));
	    case BUILT_IN_GEN_PTYPE:
	      if (check_arglist_length (args, 1, 1, fnname) < 0)
		return error_mark_node;
	      return build_gen_ptype (TREE_VALUE (args));
	    case BUILT_IN_GETASSOCIATION:
	      if (check_arglist_length (args, 1, 1, fnname) < 0)
		return error_mark_node;
	      return build_chill_getassociation (TREE_VALUE (args));
	    case BUILT_IN_GETSTACK:
	      if (check_arglist_length (args, 1, 2, fnname) < 0)
		return error_mark_node;
	      return build_chill_getstack (TREE_VALUE (args),
		       TREE_CHAIN (args) == NULL_TREE ? NULL_TREE : TREE_VALUE (TREE_CHAIN (args)));
	    case BUILT_IN_GETTEXTACCESS:
	      if (check_arglist_length (args, 1, 1, fnname) < 0)
		return error_mark_node;
	      return build_chill_gettextaccess (TREE_VALUE (args));
	    case BUILT_IN_GETTEXTINDEX:
	      if (check_arglist_length (args, 1, 1, fnname) < 0)
		return error_mark_node;
	      return build_chill_gettextindex (TREE_VALUE (args));
	    case BUILT_IN_GETTEXTRECORD:
	      if (check_arglist_length (args, 1, 1, fnname) < 0)
		return error_mark_node;
	      return build_chill_gettextrecord (TREE_VALUE (args));
	    case BUILT_IN_GETUSAGE:
	      if (check_arglist_length (args, 1, 1, fnname) < 0)
		return error_mark_node;
	      return build_chill_getusage (TREE_VALUE (args));
	    case BUILT_IN_HOURS:
	      if (check_arglist_length (args, 1, 1, fnname) < 0)
		return error_mark_node;
	      return build_chill_duration (TREE_VALUE (args), HOURS_MULTIPLIER,
					   fnname, HOURS_MAX);
	    case BUILT_IN_INDEXABLE:
	      if (check_arglist_length (args, 1, 1, fnname) < 0)
		return error_mark_node;
	      return build_chill_indexable (TREE_VALUE (args));
	    case BUILT_IN_INTTIME:
	      if (check_arglist_length (args, 2, 7, fnname) < 0)
		return error_mark_node;
	      return build_chill_inttime (TREE_VALUE (args),
		 TREE_CHAIN (args));
	    case BUILT_IN_ISASSOCIATED:
	      if (check_arglist_length (args, 1, 1, fnname) < 0)
		return error_mark_node;
	      return build_chill_isassociated (TREE_VALUE (args));
	    case BUILT_IN_LENGTH:
	      if (check_arglist_length (args, 1, 1, fnname) < 0)
		return error_mark_node;
	      return build_chill_length (TREE_VALUE (args));
	    case BUILT_IN_LN:
	      if (check_arglist_length (args, 1, 1, fnname) < 0)
		return error_mark_node;
	      return build_chill_floatcall (TREE_VALUE (args),
					    IDENTIFIER_POINTER (fnname),
					    "__log");
	    case BUILT_IN_LOG:
	      if (check_arglist_length (args, 1, 1, fnname) < 0)
		return error_mark_node;
	      return build_chill_floatcall (TREE_VALUE (args),
					    IDENTIFIER_POINTER (fnname),
					    "__log10");
	    case BUILT_IN_LOWER:
	      if (check_arglist_length (args, 1, 1, fnname) < 0)
		return error_mark_node;
	      return build_chill_lower (TREE_VALUE (args));
	    case BUILT_IN_MAX:
	      if (check_arglist_length (args, 1, 1, fnname) < 0)
		return error_mark_node;
	      return build_chill_max (TREE_VALUE (args));
	    case BUILT_IN_MILLISECS:
	      if (check_arglist_length (args, 1, 1, fnname) < 0)
		return error_mark_node;
	      return build_chill_duration (TREE_VALUE (args), MILLISECS_MULTIPLIER,
					   fnname, MILLISECS_MAX);
	    case BUILT_IN_MIN:
	      if (check_arglist_length (args, 1, 1, fnname) < 0)
		return error_mark_node;
	      return build_chill_min (TREE_VALUE (args));
	    case BUILT_IN_MINUTES:
	      if (check_arglist_length (args, 1, 1, fnname) < 0)
		return error_mark_node;
	      return build_chill_duration (TREE_VALUE (args), MINUTES_MULTIPLIER,
					   fnname, MINUTES_MAX);
	    case BUILT_IN_MODIFY:
	      if (check_arglist_length (args, 1, -1, fnname) < 0)
		return error_mark_node;
	      return build_chill_modify (TREE_VALUE (args), TREE_CHAIN (args));
	    case BUILT_IN_NUM:
	      if (check_arglist_length (args, 1, 1, fnname) < 0)
		return error_mark_node;
	      return build_chill_num (TREE_VALUE (args));
	    case BUILT_IN_OUTOFFILE:
	      if (check_arglist_length (args, 1, 1, fnname) < 0)
		return error_mark_node;
	      return build_chill_outoffile (TREE_VALUE (args));
	    case BUILT_IN_PRED:
	      if (check_arglist_length (args, 1, 1, fnname) < 0)
		return error_mark_node;
	      return build_chill_pred_or_succ (TREE_VALUE (args), MINUS_EXPR);
	    case BUILT_IN_PROC_TYPE:
	      if (check_arglist_length (args, 1, 1, fnname) < 0)
		return error_mark_node;
	      return build_proc_type (TREE_VALUE (args));
	    case BUILT_IN_QUEUE_LENGTH:
	      if (check_arglist_length (args, 1, 1, fnname) < 0)
		return error_mark_node;
	      return build_queue_length (TREE_VALUE (args));
	    case BUILT_IN_READABLE:
	      if (check_arglist_length (args, 1, 1, fnname) < 0)
		return error_mark_node;
	      return build_chill_readable (TREE_VALUE (args));
	    case BUILT_IN_READRECORD:
	      if (check_arglist_length (args, 1, 3, fnname) < 0)
		return error_mark_node;
	      return build_chill_readrecord (TREE_VALUE (args), TREE_CHAIN (args));
	    case BUILT_IN_READTEXT:
	      if (check_arglist_length (args, 2, -1, fnname) < 0)
		return error_mark_node;
	      return build_chill_readtext (TREE_VALUE (args),
					   TREE_CHAIN (args));
	    case BUILT_IN_RETURN_MEMORY:
	      if (check_arglist_length (args, 1, 1, fnname) < 0)
		return error_mark_node;
	      return build_return_memory (TREE_VALUE (args));
	    case BUILT_IN_SECS:
	      if (check_arglist_length (args, 1, 1, fnname) < 0)
		return error_mark_node;
	      return build_chill_duration (TREE_VALUE (args), SECS_MULTIPLIER,
					   fnname, SECS_MAX);
	    case BUILT_IN_SEQUENCIBLE:
	      if (check_arglist_length (args, 1, 1, fnname) < 0)
		return error_mark_node;
	      return build_chill_sequencible (TREE_VALUE (args));
	    case BUILT_IN_SETTEXTACCESS:
	      if (check_arglist_length (args, 2, 2, fnname) < 0)
		return error_mark_node;
	      return build_chill_settextaccess (TREE_VALUE (args),
						TREE_VALUE (TREE_CHAIN (args)));
	    case BUILT_IN_SETTEXTINDEX:
	      if (check_arglist_length (args, 2, 2, fnname) < 0)
		return error_mark_node;
	      return build_chill_settextindex (TREE_VALUE (args),
					       TREE_VALUE (TREE_CHAIN (args)));
	    case BUILT_IN_SETTEXTRECORD:
	      if (check_arglist_length (args, 2, 2, fnname) < 0)
		return error_mark_node;
	      return build_chill_settextrecord (TREE_VALUE (args),
						TREE_VALUE (TREE_CHAIN (args)));
	    case BUILT_IN_CH_SIN:
	      if (check_arglist_length (args, 1, 1, fnname) < 0)
		return error_mark_node;
	      return build_chill_floatcall (TREE_VALUE (args),
					    IDENTIFIER_POINTER (fnname),
					    "__sin");
	    case BUILT_IN_SIZE:
	      if (check_arglist_length (args, 1, 1, fnname) < 0)
		return error_mark_node;
	      return build_chill_sizeof (TREE_VALUE (args));
	    case BUILT_IN_SQRT:
	      if (check_arglist_length (args, 1, 1, fnname) < 0)
		return error_mark_node;
	      return build_chill_floatcall (TREE_VALUE (args),
					    IDENTIFIER_POINTER (fnname),
					    "__sqrt");
	    case BUILT_IN_SUCC:
	      if (check_arglist_length (args, 1, 1, fnname) < 0)
		return error_mark_node;
	      return build_chill_pred_or_succ (TREE_VALUE (args), PLUS_EXPR);
	    case BUILT_IN_TAN:
	      if (check_arglist_length (args, 1, 1, fnname) < 0)
		return error_mark_node;
	      return build_chill_floatcall (TREE_VALUE (args),
					    IDENTIFIER_POINTER (fnname),
					    "__tan");
	    case BUILT_IN_TERMINATE:
	      if (check_arglist_length (args, 1, 1, fnname) < 0)
		return error_mark_node;
	      return build_chill_terminate (TREE_VALUE (args));
	    case BUILT_IN_UPPER:
	      if (check_arglist_length (args, 1, 1, fnname) < 0)
		return error_mark_node;
	      return build_chill_upper (TREE_VALUE (args));
	    case BUILT_IN_VARIABLE:
	      if (check_arglist_length (args, 1, 1, fnname) < 0)
		return error_mark_node;
	      return build_chill_variable (TREE_VALUE (args));
	    case BUILT_IN_WRITEABLE:
	      if (check_arglist_length (args, 1, 1, fnname) < 0)
		return error_mark_node;
	      return build_chill_writeable (TREE_VALUE (args));
	    case BUILT_IN_WRITERECORD:
	      if (check_arglist_length (args, 2, 3, fnname) < 0)
		return error_mark_node;
	      return build_chill_writerecord (TREE_VALUE (args), TREE_CHAIN (args));
	    case BUILT_IN_WRITETEXT:
	      if (check_arglist_length (args, 2, -1, fnname) < 0)
		return error_mark_node;
	      return build_chill_writetext (TREE_VALUE (args),
					    TREE_CHAIN (args));

	    case BUILT_IN_EXPIRED:
	    case BUILT_IN_WAIT:
	      sorry ("unimplemented builtin function `%s'",
		     IDENTIFIER_POINTER (fnname));
	      break;
	    default:
	      error ("internal error - bad builtin function `%s'",
		     IDENTIFIER_POINTER (fnname));
	    }
	}
      return build_chill_function_call (func, args);
    }

  if (chill_varying_type_p (TREE_TYPE (func)))
    type = TREE_TYPE (TREE_CHAIN (TYPE_FIELDS (type)));

  if (CH_STRING_TYPE_P (type))
    {
      if (args == NULL_TREE)
	{
	  error ("empty expression in string index");
	  return error_mark_node;
	}
      if (TREE_CHAIN (args) != NULL)
	{
	  error ("only one expression allowed in string index");
	  return error_mark_node;
	}
      if (flag_old_strings)
	return build_chill_slice_with_length (func,
					      TREE_VALUE (args),
					      integer_one_node);
      else if (CH_BOOLS_TYPE_P (type))
	return build_chill_bitref (func, args);
      else
	return build_chill_array_ref (func, args);
    }

  else if (TREE_CODE (type) == ARRAY_TYPE)
    return build_chill_array_ref (func, args);

  if (TREE_CODE (func) != ERROR_MARK)
    error ("invalid: primval ( untyped_exprlist )");
  return error_mark_node;
}

/* Given a set stored as one bit per char (in BUFFER[0 .. BIT_SIZE-1]),
   return a CONTRUCTOR, of type TYPE (a SET_TYPE). */
tree
expand_packed_set (buffer, bit_size, type)
     char *buffer;
     int   bit_size;
     tree type;
{
  /* The ordinal number corresponding to the first stored bit. */
  HOST_WIDE_INT first_bit_no =
    TREE_INT_CST_LOW (TYPE_MIN_VALUE (TYPE_DOMAIN (type)));
  tree list = NULL_TREE;
  int i;

  for (i = 0; i < bit_size; i++)
    if (buffer[i])
      {
	int next_0;
	for (next_0 = i + 1; 
	     next_0 < bit_size && buffer[next_0]; next_0++)
	  ;
	if (next_0 == i + 1)
	  list = tree_cons (NULL_TREE, 
		   build_int_2 (i + first_bit_no, 0), list);
	else
	  {
	    list = tree_cons (build_int_2 (i + first_bit_no, 0),
			      build_int_2 (next_0 - 1 + first_bit_no, 0), list);
	    /* advance i past the range of 1-bits */
	    i = next_0;
	  }
      }
  list = build (CONSTRUCTOR, type, NULL_TREE, nreverse (list));
  TREE_CONSTANT (list) = 1;
  return list;
}

/*
 * fold a set represented as a CONSTRUCTOR list.
 * An empty set has a NULL_TREE in its TREE_OPERAND (set, 1) slot.
 */
static tree
fold_set_expr (code, op0, op1)
     enum chill_tree_code code;
     tree op0, op1;
{
  tree temp;
  char *buffer0, *buffer1 = NULL, *bufferr;
  int i, size0, size1, first_unused_bit;

  if (! TREE_CONSTANT (op0) || TREE_CODE (op0) != CONSTRUCTOR)
      return NULL_TREE;

  if (op1 
      && (! TREE_CONSTANT (op1) || TREE_CODE (op1) != CONSTRUCTOR))
    return NULL_TREE;

  size0 = int_size_in_bytes (TREE_TYPE (op0)) * BITS_PER_UNIT;
  if (size0 < 0)
    {
      error ("operand is variable-size bitstring/power-set");
      return error_mark_node;
    }
  buffer0 = (char*) alloca (size0);

  temp = get_set_constructor_bits (op0, buffer0, size0);
  if (temp)
    return NULL_TREE;
  
  if (op0 && op1)
    {
      size1 = int_size_in_bytes (TREE_TYPE (op1)) * BITS_PER_UNIT;
      if (size1 < 0)
	{
	  error ("operand is variable-size bitstring/power-set");
	  return error_mark_node;
	}
      if (size0 != size1)
	return NULL_TREE;
      buffer1 = (char*) alloca (size1);
      temp = get_set_constructor_bits (op1, buffer1, size1);
      if (temp)
	return NULL_TREE;
    }

  bufferr = (char*) alloca (size0); /* result buffer */

  switch ((int)code)
    {
    case SET_NOT_EXPR:
    case BIT_NOT_EXPR:
      for (i = 0; i < size0; i++) 
	bufferr[i] = 1 & ~buffer0[i];
      goto build_result;
    case SET_AND_EXPR:
    case BIT_AND_EXPR:
      for (i = 0; i < size0; i++)
	bufferr[i] = buffer0[i] & buffer1[i];
      goto build_result;
    case SET_IOR_EXPR:
    case BIT_IOR_EXPR:
      for (i = 0; i < size0; i++)
	bufferr[i] = buffer0[i] | buffer1[i];
      goto build_result;
    case SET_XOR_EXPR:
    case BIT_XOR_EXPR:
      for (i = 0; i < size0; i++) 
	bufferr[i] = (buffer0[i] ^ buffer1[i]) & 1;      
      goto build_result;
    case SET_DIFF_EXPR:
    case MINUS_EXPR:
      for (i = 0; i < size0; i++)
	bufferr[i] = buffer0[i] & ~buffer1[i];
      goto build_result;
    build_result:
      /* mask out unused bits. Same as runtime library does. */
      first_unused_bit = TREE_INT_CST_LOW (TYPE_MAX_VALUE (TYPE_DOMAIN (TREE_TYPE (op0))))
	- TREE_INT_CST_LOW (TYPE_MIN_VALUE (TYPE_DOMAIN (TREE_TYPE (op0)))) + 1;
      for (i = first_unused_bit; i < size0 ; i++)
	bufferr[i] = 0;
      return expand_packed_set (bufferr, size0, TREE_TYPE (op0));
    case EQ_EXPR:
      for (i = 0; i < size0; i++)
	if (buffer0[i] != buffer1[i])
	  return boolean_false_node;
      return boolean_true_node;
      
    case NE_EXPR:
      for (i = 0; i < size0; i++)
	if (buffer0[i] != buffer1[i])
	  return boolean_true_node;
      return boolean_false_node;

    default:
      return NULL_TREE;
    }
}

/*
 * build a set or bit-array expression.  Type-checking is
 * done elsewhere.
 */
static tree
build_compare_set_expr (code, op0, op1)
     enum tree_code code;
     tree op0, op1;
{
  tree result_type = NULL_TREE;
  char *fnname;
  tree x;

  /* These conversions are needed if -fold-strings. */
  if (TREE_CODE (TREE_TYPE (op0)) == BOOLEAN_TYPE)
    {
      if (CH_BOOLS_ONE_P (TREE_TYPE (op1)))
	return build_compare_discrete_expr (code,
					    op0,
					    convert (boolean_type_node, op1));
      else
	op0 = convert (bitstring_one_type_node, op0);
    }
  if (TREE_CODE (TREE_TYPE (op1)) == BOOLEAN_TYPE)
    {
      if (CH_BOOLS_ONE_P (TREE_TYPE (op0)))
	return build_compare_discrete_expr (code,
					    convert (boolean_type_node, op0),
					    op1);
      else
	op1 = convert (bitstring_one_type_node, op1);
    }

  switch ((int)code)
    {
    case EQ_EXPR:
      {
	tree temp = fold_set_expr (EQ_EXPR, op0, op1);
	if (temp) 
	  return temp;
	fnname = "__eqpowerset";
	goto compare_powerset;
      }
      break;

    case GE_EXPR:
      /* switch operands and fall thru */
      x = op0;
      op0 = op1;
      op1 = x;

    case LE_EXPR:
      fnname = "__lepowerset";
      goto compare_powerset;

    case GT_EXPR:
      /* switch operands and fall thru */
      x = op0;
      op0 = op1;
      op1 = x;

    case LT_EXPR:
      fnname = "__ltpowerset";
      goto compare_powerset;

    case NE_EXPR:
      return invert_truthvalue (build_compare_set_expr (EQ_EXPR, op0, op1));

    compare_powerset:
      {
	tree tsize = powersetlen (op0);
	
	if (TREE_CODE (TREE_TYPE (op0)) != SET_TYPE)
	  tsize = fold (build (MULT_EXPR, sizetype, tsize,
			       size_int (BITS_PER_UNIT)));

	return build_chill_function_call (lookup_name (get_identifier (fnname)),
	       tree_cons (NULL_TREE, force_addr_of (op0),
	         tree_cons (NULL_TREE, force_addr_of (op1),
		   tree_cons (NULL_TREE, tsize, NULL_TREE))));
      }
      break;

    default:
      if ((int) code >= (int)LAST_AND_UNUSED_TREE_CODE)
	{
	  error ("tree code `%s' unhandled in build_compare_set_expr",
		 tree_code_name[(int)code]);
	  return error_mark_node;
	}
      break;
    }

  return build ((enum tree_code)code, result_type, 
		op0, op1);
}

/* Convert a varying string (or array) to dynamic non-varying string:
   EXP becomes EXP.var_data(0 UP EXP.var_length). */

tree
varying_to_slice (exp)
     tree exp;
{
  if (!chill_varying_type_p (TREE_TYPE (exp)))
    return exp;
  else
    { tree size, data, data_domain, min;
      tree novelty = CH_NOVELTY (TREE_TYPE (exp));
      exp = save_if_needed (exp);
      size = build_component_ref (exp, var_length_id);
      data = build_component_ref (exp, var_data_id);
      TREE_TYPE (data) = copy_novelty (novelty, TREE_TYPE (data));
      data_domain = TYPE_DOMAIN (TREE_TYPE (data));
      if (data_domain != NULL_TREE
	  && TYPE_MIN_VALUE (data_domain) != NULL_TREE)
	min = TYPE_MIN_VALUE (data_domain);
      else
	min = integer_zero_node;
      return build_chill_slice (data, min, size);
    }
}

/* Convert a scalar argument to a string or array type.  This is a subroutine
   of `build_concat_expr'.  */

static tree
scalar_to_string (exp)
     tree exp;
{
  tree type = TREE_TYPE (exp);

  if (SCALAR_P (type))
    {
      int was_const = TREE_CONSTANT (exp);
      if (TREE_TYPE (exp) == char_type_node)
	exp = convert (string_one_type_node, exp);
      else if (TREE_TYPE (exp) == boolean_type_node)
	exp = convert (bitstring_one_type_node, exp);
      else
	exp = convert (build_array_type_for_scalar (type), exp);
      TREE_CONSTANT (exp) = was_const;
      return exp;
    }
  return varying_to_slice (exp);
}

/* FIXME:  Generalize this to general arrays (not just strings),
   at least for the compiler-generated case of padding fixed-length arrays. */

static tree
build_concat_expr (op0, op1)
     tree op0, op1;
{
  tree orig_op0 = op0, orig_op1 = op1;
  tree type0, type1, size0, size1, res;

  op0 = scalar_to_string (op0);
  type0 = TREE_TYPE (op0);
  op1 = scalar_to_string (op1);
  type1 = TREE_TYPE (op1);
  size1 = size_in_bytes (type1);

  /* try to fold constant string literals */
  if (TREE_CODE (op0) == STRING_CST
      && (TREE_CODE (op1) == STRING_CST 
	  || TREE_CODE (op1) == UNDEFINED_EXPR)
      && TREE_CODE (size1) == INTEGER_CST)
    {
      int len0 = TREE_STRING_LENGTH (op0);
      int len1 = TREE_INT_CST_LOW (size1);
      char *result = xmalloc (len0 + len1 + 1);
      memcpy (result, TREE_STRING_POINTER (op0), len0);
      if (TREE_CODE (op1) == UNDEFINED_EXPR)
	memset (&result[len0], '\0', len1);
      else
	memcpy (&result[len0], TREE_STRING_POINTER (op1), len1);
      return build_chill_string (len0 + len1, result);
    }
  else if (TREE_CODE (type0) == TREE_CODE (type1))
    {
      tree result_size;
      struct ch_class result_class;
      struct ch_class class0;
      struct ch_class class1;

      class0 = chill_expr_class (orig_op0);
      class1 = chill_expr_class (orig_op1);

      if (TREE_CODE (type0) == SET_TYPE)
	{
	  result_size = size_binop (PLUS_EXPR,
				    discrete_count (TYPE_DOMAIN (type0)),
				    discrete_count (TYPE_DOMAIN (type1)));
	  result_class.mode = build_bitstring_type (result_size);
	}
      else
	{
	  tree max0 = TYPE_MAX_VALUE (type0);
	  tree max1 = TYPE_MAX_VALUE (type1);

	  /* new array's dynamic size (in bytes). */
	  size0     = size_in_bytes (type0);
	  /* size1 was computed above.  */

	  result_size = size_binop (PLUS_EXPR, size0, size1);
	  /* new array's type. */
	  result_class.mode = build_string_type (char_type_node, result_size);

	  if (max0 || max1)
	    {
	      max0 = max0 == 0 ? size0 : convert (sizetype, max0);
	      max1 = max1 == 0 ? size1 : convert (sizetype, max1);
	      TYPE_MAX_VALUE (result_class.mode)
		= size_binop (PLUS_EXPR, max0, max1);
	    }
	}

      if (class0.kind == CH_VALUE_CLASS || class1.kind == CH_VALUE_CLASS)
	{
	  tree novelty0 = CH_NOVELTY (TREE_TYPE (orig_op0));
	  result_class.kind = CH_VALUE_CLASS;
	  if (class0.kind == CH_VALUE_CLASS && novelty0 != NULL_TREE)
	    SET_CH_NOVELTY_NONNIL (result_class.mode, novelty0);
	  else if (class1.kind == CH_VALUE_CLASS)
	    SET_CH_NOVELTY (result_class.mode,
			    CH_NOVELTY (TREE_TYPE (orig_op1)));
	}
      else
	result_class.kind = CH_DERIVED_CLASS;

      if (TREE_CODE (result_class.mode) == SET_TYPE
	  && TREE_CONSTANT (op0) && TREE_CONSTANT (op1)
	  && TREE_CODE (op0) == CONSTRUCTOR && TREE_CODE (op1) == CONSTRUCTOR)
	{
	  HOST_WIDE_INT size0, size1;  char *buffer;
	  size0 = TREE_INT_CST_LOW (TYPE_MAX_VALUE (TYPE_DOMAIN (type0))) + 1;
	  size1 = TREE_INT_CST_LOW (TYPE_MAX_VALUE (TYPE_DOMAIN (type1))) + 1;
	  buffer = (char*) alloca (size0 + size1);
	  if (size0 < 0 || size1 < 0
	      || get_set_constructor_bits (op0, buffer, size0)
	      || get_set_constructor_bits (op1, buffer + size0, size1))
	    abort ();
	  res = expand_packed_set (buffer, size0 + size1, result_class.mode);
	}
      else
	res = build (CONCAT_EXPR, result_class.mode, op0, op1);
      return convert_to_class (result_class, res);
    }
  else
    {
      error ("incompatible modes in concat expression");
      return error_mark_node;
    }
}

/*
 * handle varying and fixed array compare operations
 */
static tree
build_compare_string_expr (code, op0, op1)
     enum tree_code code;
     tree op0, op1;
{
  if (op0 == NULL_TREE || TREE_CODE (op0) == ERROR_MARK)
    return error_mark_node;
  if (op1 == NULL_TREE || TREE_CODE (op1) == ERROR_MARK)
    return error_mark_node;

  if (tree_int_cst_equal (TYPE_SIZE (TREE_TYPE (op0)),
			  TYPE_SIZE (TREE_TYPE (op1)))
      && ! chill_varying_type_p (TREE_TYPE (op0))
      && ! chill_varying_type_p (TREE_TYPE (op1)))
    {
      tree size = size_in_bytes (TREE_TYPE (op0));
      tree temp = lookup_name (get_identifier ("memcmp"));
      temp = build_chill_function_call (temp,
		 tree_cons (NULL_TREE, force_addr_of (op0),
		     tree_cons (NULL_TREE, force_addr_of (op1),
		       tree_cons (NULL_TREE, size, NULL_TREE))));
      return build_compare_discrete_expr (code, temp, integer_zero_node);
    }

  switch ((int)code)
    {
    case EQ_EXPR:
      code = STRING_EQ_EXPR;
      break;
    case GE_EXPR:
      return invert_truthvalue (build_compare_string_expr (LT_EXPR, op0, op1));
    case LE_EXPR:
      return invert_truthvalue (build_compare_string_expr (LT_EXPR, op1, op0));
    case GT_EXPR:
      return build_compare_string_expr (LT_EXPR, op1, op0);
    case LT_EXPR:
      code = STRING_LT_EXPR;
      break;
    case NE_EXPR:
      return invert_truthvalue (build_compare_string_expr (EQ_EXPR, op0, op1));
    default:
      error ("Invalid operation on array of chars");
      return error_mark_node;
    }

  return build (code, boolean_type_node, op0, op1);
}

tree
compare_records (exp0, exp1)
     tree exp0, exp1;
{
  tree type = TREE_TYPE (exp0);
  tree field;
  int have_variants = 0;

  tree result = boolean_true_node;
  extern int maximum_field_alignment;

  if (TREE_CODE (type) != RECORD_TYPE)
    abort ();

  exp0 = save_if_needed (exp0);
  exp1 = save_if_needed (exp1);

  for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
    {
      if (DECL_NAME (field) == NULL_TREE)
	{
	  have_variants = 1;
	  break;
	}
    }

  /* in case of -fpack we always do a memcmp */
  if (maximum_field_alignment != 0)
    {
      tree memcmp_func = lookup_name (get_identifier ("memcmp"));
      tree arg1 = force_addr_of (exp0);
      tree arg2 = force_addr_of (exp1);
      tree arg3 = size_in_bytes (type);
      tree fcall = build_chill_function_call (memcmp_func,
                     tree_cons (NULL_TREE, arg1,
                       tree_cons (NULL_TREE, arg2,
                         tree_cons (NULL_TREE, arg3, NULL_TREE))));

      if (have_variants)
	warning ("comparison of variant structures is unsafe");
      result = build_chill_binary_op (EQ_EXPR, fcall, integer_zero_node);
      return result;
    }

  if (have_variants)
    {
      sorry ("compare with variant records");
      return error_mark_node;
    }

  for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
    {
      tree exp0fld = build_component_ref (exp0, DECL_NAME (field));
      tree exp1fld = build_component_ref (exp1, DECL_NAME (field));
      tree eq_flds = build_chill_binary_op (EQ_EXPR, exp0fld, exp1fld);
      result = build_chill_binary_op (TRUTH_AND_EXPR, result, eq_flds);
    }
  return result;
}

int
compare_int_csts (op, val1, val2)
     enum tree_code op;
     tree val1, val2;
{
  int result;
  tree tmp;
  tree type1 = TREE_TYPE (val1);
  tree type2 = TREE_TYPE (val2);
  switch (op)
    {
    case GT_EXPR:
    case GE_EXPR:
      tmp = val1;  val1 = val2;  val2 = tmp;
      tmp = type1;  type1 = type2; type2 = tmp;
      op = (op == GT_EXPR) ? LT_EXPR : LE_EXPR;
      /* ... fall through ... */
    case LT_EXPR:
    case LE_EXPR:
      if (!TREE_UNSIGNED (type1))
	{
	  if (!TREE_UNSIGNED (type2))
	    result = INT_CST_LT (val1, val2);
	  else if (TREE_INT_CST_HIGH (val1) < 0)
	    result = 1;
	  else
	    result = INT_CST_LT_UNSIGNED (val1, val2);
	}
      else
	{
	  if (!TREE_UNSIGNED (type2) && TREE_INT_CST_HIGH (val2) < 0)
	    result = 0;
	  else
	    result = INT_CST_LT_UNSIGNED (val1, val2);
	}
      if (op == LT_EXPR || result == 1)
	break;
      /* else fall through ... */
    case NE_EXPR:
    case EQ_EXPR:
      if (TREE_INT_CST_LOW (val1) == TREE_INT_CST_LOW (val2)
	  && TREE_INT_CST_HIGH (val1) == TREE_INT_CST_HIGH (val2)
	  /* They're bitwise equal.
	     Check for one being negative and the other unsigned. */
	  && (TREE_INT_CST_HIGH (val2) >= 0
	      || TREE_UNSIGNED (TREE_TYPE (val1))
	      == TREE_UNSIGNED (TREE_TYPE (val2))))
	result = 1;
      else
	result = 0;
      if (op == NE_EXPR)
	result = !result;
      break;
    default:
      abort();
    }
  return result;
}

/* Build an expression to compare discrete values VAL1 and VAL2.
   This does not check that they are discrete, nor that they are
   compatible;  if you need such checks use build_compare_expr. */

tree
build_compare_discrete_expr (op, val1, val2)
     enum tree_code op;
     tree val1, val2;
{
  tree type1 = TREE_TYPE (val1);
  tree type2 = TREE_TYPE (val2);
  tree tmp;

  if (TREE_CODE (val1) == INTEGER_CST && TREE_CODE (val2) == INTEGER_CST)
    {
      if (compare_int_csts (op, val1, val2))
	return boolean_true_node;
      else	
	return boolean_false_node;
    }

  if (TREE_UNSIGNED (type1) != TREE_UNSIGNED (type2))
    {
      switch (op)
	{
	case GT_EXPR:
	case GE_EXPR:
	  tmp = val1;  val1 = val2;  val2 = tmp;
	  tmp = type1;  type1 = type2; type2 = tmp;
	  op = (op == GT_EXPR) ? LT_EXPR : LE_EXPR;
	  /* ... fall through ... */
	case LT_EXPR:
	case LE_EXPR:
	  if (TREE_UNSIGNED (type2))
	    {
	      tmp = build_int_2_wide (0, 0);
	      TREE_TYPE (tmp) = type1;
	      val1 = save_expr (val1);
	      tmp = fold (build (LT_EXPR, boolean_type_node, val1, tmp));
	      if (TYPE_PRECISION (type2) < TYPE_PRECISION (type1))	
		{
		  type2 = unsigned_type (type1);
		  val2 = convert_to_integer (type2, val2);
		}
	      val1 = convert_to_integer (type2, val1);
	      return fold (build (TRUTH_OR_EXPR, boolean_type_node,
				  tmp,
				  fold (build (op, boolean_type_node,
					       val1, val2))));
	    }
	unsigned_vs_signed: /* val1 is unsigned, val2 is signed */
	  tmp = build_int_2_wide (0, 0);
	  TREE_TYPE (tmp) = type2;
	  val2 = save_expr (val2);
	  tmp = fold (build (GE_EXPR, boolean_type_node, val2, tmp));
	  if (TYPE_PRECISION (type1) < TYPE_PRECISION (type2))	
	    {
	      type1 = unsigned_type (type2);
	      val1 = convert_to_integer (type1, val1);
	    }
	  val2 = convert_to_integer (type1, val2);
	  return fold (build (TRUTH_AND_EXPR, boolean_type_node, tmp,
			      fold (build (op, boolean_type_node,
					   val1, val2))));
	case EQ_EXPR:
	  if (TREE_UNSIGNED (val2))
	    {
	      tmp = val1;  val1 = val2;  val2 = tmp;
	      tmp = type1;  type1 = type2; type2 = tmp;
	    }
	  goto unsigned_vs_signed;
	case NE_EXPR:
	  tmp = build_compare_expr (EQ_EXPR, val1, val2);
	  return build_chill_unary_op (TRUTH_NOT_EXPR, tmp);
	default:
	  abort();
	}
    }
  if (TYPE_PRECISION (type1) > TYPE_PRECISION (type2))
    val2 = convert (type1, val2);
  else if (TYPE_PRECISION (type1) < TYPE_PRECISION (type2))
    val1 = convert (type2, val1);
  return fold (build (op, boolean_type_node, val1, val2));
}

tree
build_compare_expr (op, val1, val2)
     enum tree_code op;
     tree val1, val2;
{
  tree tmp;
  tree type1, type2;
  val1 = check_have_mode (val1, "relational expression");
  val2 = check_have_mode (val2, "relational expression");
  if (val1 == NULL_TREE || TREE_CODE (val1) == ERROR_MARK)
    return error_mark_node;
  if (val2 == NULL_TREE || TREE_CODE (val2) == ERROR_MARK)
    return error_mark_node;

  if (pass == 1)
    return build (op, NULL_TREE, val1, val2);

  if (!CH_COMPATIBLE_CLASSES (val1, val2))
    {
      error ("incompatible operands to %s", boolean_code_name [op]);
      return error_mark_node;
    }

  tmp = CH_ROOT_MODE (TREE_TYPE (val1));
  if (tmp != TREE_TYPE (val1))
    val1 = convert (tmp, val1);
  tmp = CH_ROOT_MODE (TREE_TYPE (val2));
  if (tmp != TREE_TYPE (val2))
    val2 = convert (tmp, val2);

  type1 = TREE_TYPE (val1);
  type2 = TREE_TYPE (val2);

  if (TREE_CODE (type1) == SET_TYPE)
    tmp =  build_compare_set_expr (op, val1, val2);

  else if (discrete_type_p (type1))
    tmp = build_compare_discrete_expr (op, val1, val2);

  else if (chill_varying_type_p (type1) || chill_varying_type_p (type2)
      || (TREE_CODE (type1) == ARRAY_TYPE
	  && TREE_CODE (TREE_TYPE (type1)) == CHAR_TYPE)
      || (TREE_CODE (type2) == ARRAY_TYPE
	  && TREE_CODE (TREE_TYPE (type2)) == CHAR_TYPE) )
    tmp =  build_compare_string_expr (op, val1, val2);

  else if ((TREE_CODE (type1) == RECORD_TYPE
	    || TREE_CODE (type2) == RECORD_TYPE)
	   && (op == EQ_EXPR || op == NE_EXPR))
    {
      /* This is for handling INSTANCEs being compared against NULL. */
      if (val1 == null_pointer_node)
	val1 = convert (type2, val1);
      if (val2 == null_pointer_node)
	val2 = convert (type1, val2);

      tmp = compare_records (val1, val2);
      if (op == NE_EXPR)
	tmp = build_chill_unary_op (TRUTH_NOT_EXPR, tmp);
    }

  else if (TREE_CODE (type1) == REAL_TYPE || TREE_CODE (type2) == REAL_TYPE
	   || (op == EQ_EXPR || op == NE_EXPR))
    {
      tmp = build (op, boolean_type_node, val1, val2);
      CH_DERIVED_FLAG (tmp) = 1; /* Optimization to avoid copy_node. */
      tmp = fold (tmp);
    }

  else
    {
      error ("relational operator not allowed for this mode");
      return error_mark_node;
    }

  if (!CH_DERIVED_FLAG (tmp))
    {
      tmp = copy_node (tmp);
      CH_DERIVED_FLAG (tmp) = 1;
    }
  return tmp;
}

tree
finish_chill_binary_op (node)
     tree node;
{
  tree op0 = check_have_mode (TREE_OPERAND (node, 0), "binary expression");
  tree op1 = check_have_mode (TREE_OPERAND (node, 1), "binary expression");
  tree type0 = TREE_TYPE (op0);
  tree type1 = TREE_TYPE (op1);
  tree folded;

  if (TREE_CODE (op0) == ERROR_MARK || TREE_CODE (op1) == ERROR_MARK)
    return error_mark_node;

  if (UNSATISFIED (op0) || UNSATISFIED (op1))
    {
      UNSATISFIED_FLAG (node) = 1;
      return node;
    }
#if 0
  /* assure that both operands have a type */
  if (! type0 && type1)
    {
      op0 = convert (type1, op0);
      type0 = TREE_TYPE (op0);
    }
  if (! type1 && type0)
    {
      op1 = convert (type0, op1);
      type1 = TREE_TYPE (op1);
    }
#endif
  UNSATISFIED_FLAG (node) = 0;
#if 0

  { int op0f = TREE_CODE (op0) == FUNCTION_DECL;
    int op1f = TREE_CODE (op1) == FUNCTION_DECL;
    if (op0f)
      op0 = convert (build_pointer_type (TREE_TYPE (op0)), op0);
    if (op1f)
      op1 = convert (build_pointer_type (TREE_TYPE (op1)), op1);
    if ((op0f || op1f)
	&& code != EQ_EXPR && code != NE_EXPR)
      error ("Cannot use %s operator on PROC mode variable",
	     tree_code_name[(int)code]);
  }

  if (invalid_left_operand (type0, code))
    {
      error ("invalid left operand of %s", tree_code_name[(int)code]);
      return error_mark_node;
    }
  if (invalid_right_operand (code, type1))
    {
      error ("invalid right operand of %s", tree_code_name[(int)code]);
      return error_mark_node;
    }
#endif

  switch (TREE_CODE (node))
    {
    case CONCAT_EXPR:
      return build_concat_expr (op0, op1);

    case REPLICATE_EXPR:
      op0 = fold (op0);
      if (!TREE_CONSTANT (op0) || !TREE_CONSTANT (op1))
	{
	  error ("repetition expression must be constant");
	  return error_mark_node;
	}
      else
	return build_chill_repetition_op (op0, op1);

    case FLOOR_MOD_EXPR:
    case TRUNC_MOD_EXPR:
      if (TREE_CODE (type0) != INTEGER_TYPE)
	{
	  error ("left argument to MOD/REM operator must be integral");
	  return error_mark_node;
	}
      if (TREE_CODE (type1) != INTEGER_TYPE)
	{
	  error ("right argument to MOD/REM operator must be integral");
	  return error_mark_node;
	}
      break;

    case MINUS_EXPR:
      if (TREE_CODE (type1) == SET_TYPE)
	{
	  tree temp = fold_set_expr (MINUS_EXPR, op0, op1);

	  if (temp)
	    return temp;
	  if (TYPE_MODE (type1) == BLKmode)
	    TREE_SET_CODE (node, SET_DIFF_EXPR);
	  else
	    {
	      op1 = build_chill_unary_op (BIT_NOT_EXPR, op1);
	      TREE_OPERAND (node, 1) = op1;
	      TREE_SET_CODE (node, BIT_AND_EXPR);
	    }
	}
      break;

    case TRUNC_DIV_EXPR:
      if (TREE_CODE (type0) == REAL_TYPE || TREE_CODE (type1) == REAL_TYPE)
	TREE_SET_CODE (node, RDIV_EXPR);
      break;

    case BIT_AND_EXPR:
      if (TYPE_MODE (type1) == BLKmode)
	TREE_SET_CODE (node, SET_AND_EXPR);
      goto fold_set_binop;
    case BIT_IOR_EXPR:
      if (TYPE_MODE (type1) == BLKmode)
	TREE_SET_CODE (node, SET_IOR_EXPR);
      goto fold_set_binop;
    case BIT_XOR_EXPR:
      if (TYPE_MODE (type1) == BLKmode)
	TREE_SET_CODE (node, SET_XOR_EXPR);
      goto fold_set_binop;
    case SET_AND_EXPR:
    case SET_IOR_EXPR:
    case SET_XOR_EXPR:
    case SET_DIFF_EXPR:
    fold_set_binop:
      if (TREE_CODE (type0) == SET_TYPE)
	{
	  tree temp = fold_set_expr (TREE_CODE (node), op0, op1);

	  if (temp)
	    return temp;
	}
      break;

    case SET_IN_EXPR:
      if (TREE_CODE (type1) != SET_TYPE || CH_BOOLS_TYPE_P (type1))
	{
	  error ("right operand of IN is not a powerset");
	  return error_mark_node;
	}
      if (!CH_COMPATIBLE (op0, TYPE_DOMAIN (type1)))
	{
	  error ("left operand of IN incompatible with right operand");
	  return error_mark_node;
	}
      type0 = CH_ROOT_MODE (type0);
      if (type0 != TREE_TYPE (op0))
	TREE_OPERAND (node, 0) = op0 = convert (type0, op0);
      TREE_TYPE (node) = boolean_type_node;
      CH_DERIVED_FLAG (node) = 1;
      node = fold (node);
      if (!CH_DERIVED_FLAG (node))
	{
	  node = copy_node (node);
	  CH_DERIVED_FLAG (node) = 1;
	}
      return node;
    case NE_EXPR:
    case EQ_EXPR:
    case GE_EXPR:
    case GT_EXPR:
    case LE_EXPR:
    case LT_EXPR:
      return build_compare_expr (TREE_CODE (node), op0, op1);
    default:
      ;
    }

  if (!CH_COMPATIBLE_CLASSES (op0, op1))
    {
      error ("incompatible operands to %s", tree_code_name[(int) TREE_CODE (node)]);
      return error_mark_node;
    }

  if (TREE_TYPE (node) == NULL_TREE)
    {
      struct ch_class class;
      class = CH_ROOT_RESULTING_CLASS (op0, op1);
      TREE_OPERAND (node, 0) = op0 = convert_to_class (class, op0);
      type0 = TREE_TYPE (op0);
      TREE_OPERAND (node, 1) = op1 = convert_to_class (class, op1);
      type1 = TREE_TYPE (op1);
      TREE_TYPE (node) = class.mode;
      folded = convert_to_class (class, fold (node));
    }
  else
    folded = fold (node);
#if 0
  if (folded == node)
    TREE_CONSTANT (folded) = TREE_CONSTANT (op0) & TREE_CONSTANT (op1);
#endif
  if (TREE_CODE (node) == TRUNC_DIV_EXPR)
    {
      if (TREE_CONSTANT (op1))
	{
	  if (tree_int_cst_equal (op1, integer_zero_node))
	    {
	      error ("division by zero");
	      return integer_zero_node;
	    }
	}
      else if (range_checking)
	{
#if 0
	  tree test =
	    build (EQ_EXPR, boolean_type_node, op1, integer_zero_node);
	  /* Should this be overflow? */
	  folded = check_expression (folded, test,
				     ridpointers[(int) RID_RANGEFAIL]);
#endif
	}
    }
  return folded;
}

/*
 * This implements the '->' operator, which, like the '&' in C,
 * returns a pointer to an object, which has the type of
 * pointer-to-that-object.
 *
 * FORCE is 0 when we're evaluating a user-level syntactic construct,
 * and 1 when we're calling from inside the compiler.
 */
tree
build_chill_arrow_expr (ref, force)
     tree ref;
     int force;
{
  tree addr_type;
  tree result;

  if (pass == 1)
    {
      error ("-> operator not allow in constant expression");
      return error_mark_node;
    }

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

  while (TREE_CODE (TREE_TYPE (ref)) == REFERENCE_TYPE)
    ref = convert (TREE_TYPE (TREE_TYPE (ref)), ref);

  if (!force && ! CH_LOCATION_P (ref))
    {
      if (TREE_CODE (ref) == STRING_CST)
	pedwarn ("taking the address of a string literal is non-standard");
      else if (TREE_CODE (TREE_TYPE (ref)) == FUNCTION_TYPE)
	pedwarn ("taking the address of a function is non-standard");
      else
	{
	  error ("ADDR requires a LOCATION argument");
	  return error_mark_node;
	}
      /* FIXME: Should we be sure that ref isn't a
	 function if we're being pedantic? */
    }

  addr_type = build_pointer_type (TREE_TYPE (ref));

#if 0
  /* This transformation makes chill_expr_class return CH_VALUE_CLASS
     when it should return CH_REFERENCE_CLASS.  That could be fixed,
     but we probably don't want this transformation anyway. */
  if (TREE_CODE (ref) == NOP_EXPR) /* RETYPE_EXPR */
    {
      tree addr;
      while (TREE_CODE (ref) == NOP_EXPR) /* RETYPE_EXPR */
	ref = TREE_OPERAND (ref, 0);
      mark_addressable (ref);
      addr = build1 (ADDR_EXPR, 
		     build_pointer_type (TREE_TYPE (ref)), ref);
      return build1 (NOP_EXPR, /* RETYPE_EXPR */
		      addr_type,
		      addr);
    } 
  else
#endif
    {
      if (! mark_addressable (ref))
	{
	  error ("-> expression is not addressable");
	  return error_mark_node;
	}
      result = build1 (ADDR_EXPR, addr_type, ref);
      if (staticp (ref)
	  && ! (TREE_CODE (ref) == FUNCTION_DECL
		&& DECL_CONTEXT (ref) != 0))
	TREE_CONSTANT (result) = 1;
      return result;
    }
}

/*
 * This implements the ADDR builtin function, which returns a 
 * free reference, analogous to the C 'void *'.
 */
tree
build_chill_addr_expr (ref, errormsg)
     tree ref;
     char *errormsg;
{
  if (ref == error_mark_node)
    return ref;

  if (! CH_LOCATION_P (ref)
      && TREE_CODE (TREE_TYPE (ref)) != FUNCTION_TYPE)
    {
      error ("ADDR parameter must be a LOCATION");
      return error_mark_node;
    }
  ref = build_chill_arrow_expr (ref, 1);

  if (ref != NULL_TREE && TREE_CODE (ref) != ERROR_MARK)
    TREE_TYPE (ref) = ptr_type_node;
  else if (errormsg == NULL)
    {
      error ("possible internal error in build_chill_arrow_expr");
      return error_mark_node;
    }
  else
    {
      error ("%s is not addressable", errormsg);
      return error_mark_node;
    }
  return ref;
}

tree
build_chill_binary_op (code, op0, op1)
     enum chill_tree_code code;
     tree op0, op1;
{
  register tree result;

  if (op0 == NULL_TREE || TREE_CODE (op0) == ERROR_MARK)
    return error_mark_node;
  if (op1 == NULL_TREE || TREE_CODE (op1) == ERROR_MARK)
    return error_mark_node;

  result = build (code, NULL_TREE, op0, op1);

  if (pass != 1)
    result = finish_chill_binary_op (result);
  return result;
}

/*
 * process a string repetition phrase '(' COUNT ')' STRING
 */
tree
string_char_rep (count, string)
     int count;
     tree string;
{
  int slen, charindx, repcnt;
  char ch;
  char *temp;
  char *inp;
  char *outp;
  tree type;

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

  type = TREE_TYPE (string);
  slen = int_size_in_bytes (type);
  temp = xmalloc (slen * count);
  inp = &ch;
  outp = temp;
  if (TREE_CODE (string) == STRING_CST)  
    inp = TREE_STRING_POINTER (string);
  else                           /* single character */
    ch = (char)TREE_INT_CST_LOW (string);

  /* copy the string/char COUNT times into the output buffer */
  for (outp = temp, repcnt = 0; repcnt < count; repcnt++)
    for (charindx = 0; charindx < slen; charindx++)
      *outp++ = inp[charindx];
  return build_chill_string (slen * count, temp);
}

/* Build a bit-string constant containing with the given LENGTH
   containing all ones (if VALUE is true), or all zeros (if VALUE is false). */

tree
build_boring_bitstring (length, value)
     long length;
     int value;
{
  tree result;
  tree list;  /* Value of CONSTRUCTOR_ELTS in the result. */
  if (value && length > 0)
    list = tree_cons (integer_zero_node, size_int (length - 1), NULL_TREE);
  else
    list = NULL_TREE;
		
  result = build (CONSTRUCTOR,
		  build_bitstring_type (size_int (length)),
		  NULL_TREE,
		  list);
  TREE_CONSTANT (result) = 1;
  CH_DERIVED_FLAG (result) = 1;
  return result;
}

/*
 * handle a string repetition, with the syntax:
 *        ( COUNT ) 'STRING'
 * COUNT is required to be constant, positive and folded.
 */
tree
build_chill_repetition_op (count_op, string)
     tree count_op;
     tree string;
{
  int count;
  tree type = TREE_TYPE (string);

  if (TREE_CODE (count_op) != INTEGER_CST)
    {
      error ("repetition count is not an integer constant");
      return error_mark_node;
    }

  count = TREE_INT_CST_LOW (count_op);

  if (count < 0)
    {
      error ("repetition count < 0");
      return error_mark_node;
    }
  if (! TREE_CONSTANT (string))
    {
      error ("repetition value not constant");
      return error_mark_node;
    }

  if (TREE_CODE (string) == STRING_CST)
    return string_char_rep (count, string);

  switch ((int)TREE_CODE (type))
    {
    case BOOLEAN_TYPE:
      if (TREE_CODE (string) == INTEGER_CST)
	return build_boring_bitstring (count, TREE_INT_CST_LOW (string));
      error ("bitstring repetition of non-constant boolean");
      return error_mark_node;

    case CHAR_TYPE:
      return string_char_rep (count, string);

    case SET_TYPE:
      { int i, tree_const = 1;
	tree new_list = NULL_TREE;
	tree vallist;
	tree result;
	tree domain = TYPE_DOMAIN (type);
	tree orig_length;
	HOST_WIDE_INT orig_len;

	if (!CH_BOOLS_TYPE_P (type)) /* cannot replicate a powerset */
	  break;

	orig_length = discrete_count (domain);

	if (TREE_CODE (string) != CONSTRUCTOR || !TREE_CONSTANT (string)
	    || TREE_CODE (orig_length) != INTEGER_CST)
	  {
	    error ("string repetition operand is non-constant bitstring");
	    return error_mark_node;
	  }

			       
	orig_len = TREE_INT_CST_LOW (orig_length);

	/* if the set is empty, this is NULL */
	vallist = TREE_OPERAND (string, 1);

	if (vallist == NULL_TREE) /* No bits are set. */
	  return build_boring_bitstring (count * orig_len, 0);
	else if (TREE_CHAIN (vallist) == NULL_TREE
		 && (TREE_PURPOSE (vallist) == NULL_TREE
		     ? (orig_len == 1
			&& tree_int_cst_equal (TYPE_MIN_VALUE (domain),
					       TREE_VALUE (vallist)))
		     : (tree_int_cst_equal (TYPE_MIN_VALUE (domain),
					    TREE_PURPOSE (vallist))
			&& tree_int_cst_equal (TYPE_MAX_VALUE (domain),
					       TREE_VALUE (vallist)))))
	  return build_boring_bitstring (count * orig_len, 1);

	for (i = 0; i < count; i++)
	  {
	    tree origin = build_int_2 (i * orig_len, 0);
	    tree temp;

	    /* scan down the given value list, building
	       new bit-positions */
	    for (temp = vallist; temp; temp = TREE_CHAIN (temp))
	      {
		tree new_value
		  = fold (size_binop (PLUS_EXPR, origin, TREE_VALUE (temp)));
		tree new_purpose = NULL_TREE;
		if (! TREE_CONSTANT (TREE_VALUE (temp)))
		  tree_const = 0;
		if (TREE_PURPOSE (temp))
		  {
		    new_purpose = fold (size_binop (PLUS_EXPR,
						    origin,
						    TREE_PURPOSE (temp)));
		    if (! TREE_CONSTANT (TREE_PURPOSE (temp)))
		      tree_const = 0;
		  }

		new_list = tree_cons (new_purpose,
					  new_value, new_list);
	      }
	  }
	result = build (CONSTRUCTOR,
			build_bitstring_type (size_int (count * orig_len)),
			NULL_TREE, nreverse (new_list));
	TREE_CONSTANT (result) = tree_const;
	CH_DERIVED_FLAG (result) = CH_DERIVED_FLAG (string);
	return result;
      }

    default:
      error ("non-char, non-bit string repetition");
      return error_mark_node;
  }
  return error_mark_node;
}

tree
finish_chill_unary_op (node)
     tree node;
{
  enum chill_tree_code code = TREE_CODE (node);
  tree op0 = check_have_mode (TREE_OPERAND (node, 0), "unary expression");
  tree type0 = TREE_TYPE (op0);
  struct ch_class class;

  if (TREE_CODE (op0) == ERROR_MARK)
    return error_mark_node;
  /* The expression codes of the data types of the arguments tell us
     whether the arguments are integers, floating, pointers, etc.  */

  if (TREE_CODE (type0) == REFERENCE_TYPE)
    {
      op0 = convert (TREE_TYPE (type0), op0);
      type0 = TREE_TYPE (op0);
    }

  if (invalid_right_operand (code, type0))
    {
      error ("invalid operand of %s", 
	     tree_code_name[(int)code]);
      return error_mark_node;
    }
  switch ((int)TREE_CODE (type0))
    {
    case ARRAY_TYPE:
      if (TREE_CODE ( TREE_TYPE (type0)) == BOOLEAN_TYPE)
	code = SET_NOT_EXPR;
      else
	{
	  error ("right operand of %s is not array of boolean",
		 tree_code_name[(int)code]);
	  return error_mark_node;
	}
      break;
    case BOOLEAN_TYPE:
      switch ((int)code)
	{
	case BIT_NOT_EXPR:
	case TRUTH_NOT_EXPR:
	  return invert_truthvalue (truthvalue_conversion (op0));

	default:
	  error ("%s operator applied to boolean variable",
		 tree_code_name[(int)code]);
	  return error_mark_node;
	}
      break;

    case SET_TYPE:
      switch ((int)code)
	{
	case BIT_NOT_EXPR:
	case NEGATE_EXPR:
	  {
	    tree temp = fold_set_expr (BIT_NOT_EXPR, op0, NULL_TREE);

	    if (temp) 
	      return temp;

	    code = SET_NOT_EXPR;
	  }
	  break;

	default:
	  error ("invalid right operand of %s", tree_code_name[(int)code]);
	  return error_mark_node;
	}

    }

  class = chill_expr_class (op0);
  if (class.mode)
    class.mode = CH_ROOT_MODE (class.mode);
  TREE_SET_CODE (node, code);
  TREE_OPERAND (node, 0) = op0 = convert_to_class (class, op0);
  TREE_TYPE (node) = TREE_TYPE (op0);

  node = convert_to_class (class, fold (node));

  /* FIXME: should call
   * cond_type_range_exception (op0);
   */
  return node;
}

/* op is TRUTH_NOT_EXPR, BIT_NOT_EXPR, or NEGATE_EXPR */

tree
build_chill_unary_op (code, op0)
     enum chill_tree_code code;
     tree op0;
{
  register tree result = NULL_TREE;

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

  result = build1 (code, NULL_TREE, op0);

  if (pass != 1)
    result = finish_chill_unary_op (result);
  return result;
}

tree
truthvalue_conversion (expr)
     tree expr;
{
  if (expr == NULL_TREE || TREE_CODE (expr) == ERROR_MARK)
    return error_mark_node;

#if 0        /* what about a LE_EXPR (integer_type, integer_type ) */
  if (TREE_CODE (TREE_TYPE (expr)) != BOOLEAN_TYPE)
    error ("non-boolean mode in conditional expression");
#endif

  switch ((int)TREE_CODE (expr))
    {
      /* It is simpler and generates better code to have only TRUTH_*_EXPR
	 or comparison expressions as truth values at this level.  */
#if 0
    case COMPONENT_REF:
      /* A one-bit unsigned bit-field is already acceptable.  */
      if (1 == TREE_INT_CST_LOW (DECL_SIZE (TREE_OPERAND (expr, 1)))
	  && TREE_UNSIGNED (TREE_OPERAND (expr, 1)))
	return expr;
      break;
#endif

    case EQ_EXPR:
      /* It is simpler and generates better code to have only TRUTH_*_EXPR
	 or comparison expressions as truth values at this level.  */
    case NE_EXPR: case LE_EXPR: case GE_EXPR: case LT_EXPR: case GT_EXPR:
    case TRUTH_ANDIF_EXPR:
    case TRUTH_ORIF_EXPR:
    case TRUTH_AND_EXPR:
    case TRUTH_OR_EXPR:
    case ERROR_MARK:
      return expr;

    case INTEGER_CST:
      return integer_zerop (expr) ? boolean_false_node : boolean_true_node;

    case REAL_CST:
      return real_zerop (expr) ? boolean_false_node : boolean_true_node;

    case ADDR_EXPR:
      if (TREE_SIDE_EFFECTS (TREE_OPERAND (expr, 0)))
	return build (COMPOUND_EXPR, boolean_type_node,
		      TREE_OPERAND (expr, 0), boolean_true_node);
      else
	return boolean_true_node;

    case NEGATE_EXPR:
    case ABS_EXPR:
    case FLOAT_EXPR:
    case FFS_EXPR:
      /* These don't change whether an object is non-zero or zero.  */
      return truthvalue_conversion (TREE_OPERAND (expr, 0));

    case LROTATE_EXPR:
    case RROTATE_EXPR:
      /* These don't change whether an object is zero or non-zero, but
	 we can't ignore them if their second arg has side-effects.  */
      if (TREE_SIDE_EFFECTS (TREE_OPERAND (expr, 1)))
	return build (COMPOUND_EXPR, boolean_type_node, TREE_OPERAND (expr, 1),
		      truthvalue_conversion (TREE_OPERAND (expr, 0)));
      else
	return truthvalue_conversion (TREE_OPERAND (expr, 0));
      
    case COND_EXPR:
      /* Distribute the conversion into the arms of a COND_EXPR.  */
      return fold (build (COND_EXPR, boolean_type_node, TREE_OPERAND (expr, 0),
			  truthvalue_conversion (TREE_OPERAND (expr, 1)),
			  truthvalue_conversion (TREE_OPERAND (expr, 2))));

    case CONVERT_EXPR:
      /* Don't cancel the effect of a CONVERT_EXPR from a REFERENCE_TYPE,
	 since that affects how `default_conversion' will behave.  */
      if (TREE_CODE (TREE_TYPE (expr)) == REFERENCE_TYPE
	  || TREE_CODE (TREE_TYPE (TREE_OPERAND (expr, 0))) == REFERENCE_TYPE)
	break;
      /* fall through... */
    case NOP_EXPR:
      /* If this is widening the argument, we can ignore it.  */
      if (TYPE_PRECISION (TREE_TYPE (expr))
	  >= TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (expr, 0))))
	return truthvalue_conversion (TREE_OPERAND (expr, 0));
      break;

    case BIT_XOR_EXPR:
    case MINUS_EXPR:
      /* These can be changed into a comparison of the two objects.  */
      if (TREE_TYPE (TREE_OPERAND (expr, 0))
	  == TREE_TYPE (TREE_OPERAND (expr, 1)))
	return build_chill_binary_op (NE_EXPR, TREE_OPERAND (expr, 0),
				      TREE_OPERAND (expr, 1));
      return build_chill_binary_op (NE_EXPR, TREE_OPERAND (expr, 0),
				    fold (build1 (NOP_EXPR,
					    TREE_TYPE (TREE_OPERAND (expr, 0)),
					    TREE_OPERAND (expr, 1))));
    }

  return build_chill_binary_op (NE_EXPR, expr, boolean_false_node);
}


/*
 * return a folded tree for the powerset's length in bits.  If a
 * non-set is passed, we assume it's an array or boolean bytes.
 */
tree
powersetlen (powerset)
     tree powerset;
{
  if (powerset == NULL_TREE || TREE_CODE (powerset) == ERROR_MARK)
    return error_mark_node;

  return discrete_count (TYPE_DOMAIN (TREE_TYPE (powerset)));
}
