/* Miscellaneous functions for bounds checking.
   Written by Richard W.M. Jones <rwmj@doc.ic.ac.uk>.
   Copyright (C) 1995 Richard W.M. Jones.

This file is part of the bounds checking patches for GNU CC. The main
body of code used at run time can be found in the `bounds/' subdirectory.

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, 675 Mass Ave, Cambridge, MA 02139, USA.  */

/* [RWMJ] <- this identifier ensures this file gets packed by my script. */

#include "config.h"
#include "tree.h"
#include "rtl.h"
#include "flags.h"
#include "c-tree.h"
#include "c-lex.h"
#include "c-bounds.h"
#include <stdio.h>

extern int warn_missing_prototypes;

#define DEBUG 0

extern tree get_file_function_name PROTO((int));

static tree build_current_lineno PROTO((void));
static tree build_current_filename PROTO((void));
static tree build_decl_lineno PROTO((tree));
static tree build_decl_filename PROTO((tree));
static tree my_build_function_call PROTO((char *, tree *, int));
static tree my_build_string PROTO((char *));
static tree my_pointer_from_array PROTO((tree));
static tree my_pointer_from_array_type PROTO((tree));
static tree declare_private_statics PROTO((void));

/* Build a file-scope function that gets called before main(). This function
 * calls the bounds checking library for all global variables in this file.
 * The function will look like this:
 *	__GLOBAL$I$main ()
 *	{
 *	  __bounds_initialize_library ();
 *	  __bounds_note_constructed_object (&p, 4, 4, ...);
 *	  ...
 *	}
 */
void
bounds_build_static_constructors ()
{
  tree fnname, decl, void_list_node, function_call, params[3], table;
  int i, len, old_warn_strict_prototypes, old_warn_missing_prototypes;

#if DEBUG
  fprintf (stderr, "Building bounds-checking private static table.\n");
#endif

  /* Get list of top-level named static declarations. The two other type of
   * static data, namely static variables in functions and unnamed static
   * data, are found and dealt with in `varasm.c'.
   */
  decl = copy_list (getdecls ());
  len = list_length (decl);

  bounds_external_declaration( decl , len );

  /* Build the table of private static data. This code is in `varasm.c'. This
   * table is declared:
   *	static unsigned *__bounds_private_statics;
   */
  assemble_private_statics_table ();
  table = declare_private_statics ();

#if DEBUG
  fprintf (stderr, "Building bounds-checking constructor functions.\n");
#endif

  /* Create a unique function name for this file. This code is in tree.c,
   * and is also used by cc1plus, but that should be OK.
   */
  fnname = get_file_function_name ('I');

#if DEBUG
  fprintf (stderr, "  Concatenated lists of decls. List length = %d.\n", len);
#endif

  /* Build the top-level constructor function for this file.
   */
  void_list_node = build_tree_list (NULL_TREE, void_type_node);
  old_warn_strict_prototypes = warn_strict_prototypes;
  old_warn_missing_prototypes = warn_missing_prototypes;
  warn_strict_prototypes = 0;
  warn_missing_prototypes = 0;
  start_function (void_list_node,
		  build_parse_node (CALL_EXPR, fnname, void_list_node, NULL_TREE),
		  NULL_TREE, NULL_TREE, 0);
  fnname = DECL_ASSEMBLER_NAME (current_function_decl);
  store_parm_decls ();
  warn_strict_prototypes = old_warn_strict_prototypes;
  warn_missing_prototypes = old_warn_missing_prototypes;

  /* Enter a new function context, as if this function had appeared in the
   * source code.
   */
  pushlevel (0);
  clear_last_expr ();
  push_momentary ();
  expand_start_bindings (0);

  /* Build a call to initialize the library. This initialization function may
   * be called multiple times as a result. This doesn't matter.
   */
  function_call = my_build_function_call ("__bounds_initialize_library", NULL, 0);
  iterator_expand (function_call);
  clear_momentary ();

  /* Build a call to '__bounds_note_constructed_private_table'.
   */
  params[0] = default_conversion (table);
  params[1] = build_current_filename ();
  params[2] = build_current_lineno ();
  function_call = my_build_function_call ("__bounds_note_constructed_private_table", params, 3);
  iterator_expand (function_call);
  clear_momentary ();

  /* For each top-level declaration, build a constructor call if that is
   * what is required.
   */
  for (i = 0; i < len; ++i, decl = TREE_CHAIN (decl))
    {
      if (TREE_CODE (decl) == VAR_DECL
	  && !DECL_EXTERNAL (decl)
	  /* Don't generate calls for __FUNCTION__ and __PRETTY_FUNCTION__. */
	  && !DECL_IN_SYSTEM_HEADER (decl))
	{
	tree type, ptr, size_exp, params[6], function_call, decl_name,
	  alignment;

#if DEBUG
	fprintf (stderr, "  %d: Generating code to initialize %s.\n",
		 i+1, IDENTIFIER_POINTER (DECL_NAME (decl)));
#endif

	/* Obtain a pointer to the object, its size and its alignment. For
	 * arrays, the alignment is the size of each element, and the size
	 * is the size of the whole array. For structs, unions, the alignment
	 * is always 1. Other data types in C are not aggregates, so the
	 * alignment is the same as the size.
	 */
	type = TREE_TYPE (decl);
/*	size_exp = c_size_in_bytes (type); */
	/* RWMJ: Just ignore static variables if the size hasn't been
	 * decided yet. Declarations like `char buff[];' at the top level
	 * cause this to happen.
	 */
	if (DECL_SIZE (decl) != 0)
	  size_exp = size_binop (CEIL_DIV_EXPR,
				 DECL_SIZE (decl),
				 size_int (BITS_PER_UNIT));
	else
	  {
	    warning ("variable with no size will not be bounds checked");
	    continue;
	  }
	switch (TREE_CODE (type)) {
	case ARRAY_TYPE: {
	  enum tree_code code;
	  ptr = my_pointer_from_array (decl);
	  code = TREE_CODE (TREE_TYPE (TREE_TYPE (ptr)));
	  if (code != RECORD_TYPE && code != UNION_TYPE &&
	      code != QUAL_UNION_TYPE)
	    alignment = c_size_in_bytes (TREE_TYPE (TREE_TYPE (ptr)));
	  else
	    alignment = integer_one_node;
	  break;
	}
	case RECORD_TYPE:
	case UNION_TYPE:
	case QUAL_UNION_TYPE:
	  ptr = build1 (ADDR_EXPR, build_pointer_type (type), decl);
	  alignment = integer_one_node;
	  break;
	default:
	  ptr = build1 (ADDR_EXPR, build_pointer_type (type), decl);
	  alignment = size_exp;
	}

	decl_name = my_build_string (IDENTIFIER_POINTER (DECL_NAME (decl)));

	params[0] = ptr;			/* pointer */
	params[1] = size_exp;			/* size of object */
	params[2] = alignment;			/* size of elements */
	params[3] = build_decl_filename (decl);	/* where object declared */
	params[4] = build_decl_lineno (decl);
	params[5] = decl_name;			/* name of object */

	function_call = my_build_function_call ("__bounds_note_constructed_object", params, 6);

	/* Generate the code for this function call within this function.
	 */
#if DEBUG && 0
	fprintf (stderr, "About to expand code for %s\n",
		 IDENTIFIER_POINTER (DECL_NAME (decl)));
#endif
	iterator_expand (function_call);
	clear_momentary ();
      }
    }

  /* Leave the constructor function, and pop back to the global context.
   */
  expand_end_bindings (getdecls (), 1, 0);
  poplevel (1, 0, 0);
  pop_momentary ();
  finish_function (0);

  /* Assemble a call to the constructor function that will be executed when
   * the program is initialized.
   */
  assemble_constructor (IDENTIFIER_POINTER (fnname));
}

/* Declare `static unsigned __bounds_private_statics[];' which will list
 * the private readonly objects in the file. This is an incomplete type.
 * The actual assembler for it is built directly in `varasm.c'.
 * Returns the VAR_DECL node.
 */
static tree
declare_private_statics ()
{
  tree decl, type;

  type = build_array_type (char_type_node,
			   build_index_type (integer_zero_node));

  push_obstacks_nochange ();
  decl = build_decl (VAR_DECL, get_identifier ("__bounds_private_statics"),
		     type);
  TREE_STATIC (decl) = 1;
  TREE_READONLY (decl) = 1;
  TREE_ASM_WRITTEN (decl) = 1;		/* (fake) */
  DECL_SOURCE_LINE (decl) = 0;
  DECL_IN_SYSTEM_HEADER (decl) = 1;
  DECL_IGNORED_P (decl) = 1;
  DECL_INITIAL (decl) = NULL_TREE;
  finish_decl (pushdecl (decl), NULL_TREE, NULL_TREE);

  return decl;
}



/* DECL is either a static or automatic local variable. Build a constructor
 * function for it so that when it comes into scope, it is added to our
 * list of valid objects. The constructor takes the form of a variable
 * initializer, and there may already be an initializer for this variable,
 * so we must alter the old initializer, if present, so it has the side
 * effect of calling the constructor. So,
 * if there is an initializer already, form the expression:
 *	__bounds_add_stack_object (...), old_initializer
 * else if there isn't, for the expression:
 *	__bounds_add_stack_object (...), 0
 * This has the unfortunate side effect of initializing non-initialized
 * variables to zero, but I can't see how to avoid that.
 */
void
bounds_frig_decl_initial (decl)
  tree decl;
{
  tree type, decl_name, size_exp, ptr, alignment, params[6], fn_call;
  enum tree_code code;
  int already_initialized;

#if DEBUG && 0
  fprintf (stderr, "bounds_frig_decl_initial on %s\n",
	   IDENTIFIER_POINTER (DECL_NAME (decl)));
#endif

  /* Is there a previous initializer? */
  /* FIXME: Will this cause problems with error expressions??? */
  if (DECL_INITIAL (decl) && DECL_INITIAL (decl) != error_mark_node)
    already_initialized = 1;
  else
    already_initialized = 0;

#if DEBUG && 0
  fprintf (stderr, "*** for %s (%sinitialized), DECL_INITIAL is:\n",
	   IDENTIFIER_POINTER (DECL_NAME (decl)),
	   already_initialized ? "" : "not ");
  debug_tree (DECL_INITIAL (decl));
#endif

  /* A static variable declared in a function can't have its DECL_INITIAL
   * modified with a non-constant function call. We catch such variables
   * in varasm.c:make_decl_rtl instead, so forget about them for now.
   */
  if (TREE_STATIC (decl))
    return;

  /* Reach this point only for `auto' variables (ie. variables really on
   * the stack). We have to take the address of every auto variable to
   * bounds check it. This means that `register' assignments will generate
   * unnecessary warnings unless we suppress them here.
   */
  DECL_REGISTER (decl) = 0;
  /* Note (8/6/95): We don't want to mark these addressable yet, since we
   * may be able to delete these calls later if they are never really
   * addressed, unless it's an array decl. */
  if (TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE)
    mark_addressable (decl);
  else
    put_var_into_stack (decl);

  type = TREE_TYPE (decl);
  code = TREE_CODE (type);

  /* Form the parameters for the call to __bounds_add_stack_object ().
   */
  decl_name = my_build_string (IDENTIFIER_POINTER (DECL_NAME (decl)));
  /* RWMJ - 8/8/95: This is wrong. The size is sometimes reported as 0.
   * The suggested replacement code is below, but this doesn't work well
   * either. Why? Normal `sizeof' in C gets this right. How?	(FIXME)
   */
  size_exp = c_size_in_bytes (type);
/*  size_exp = c_sizeof_nowarn (type);
  if (integer_zerop (size_exp))
    {
      size_exp = c_sizeof_nowarn (TREE_TYPE (DECL_INITIAL (decl)));
      if (integer_zerop (size_exp))
	xyz!!!_______abort ();
    } */
  switch (TREE_CODE (type)) {
  case ARRAY_TYPE: {
    enum tree_code type_code;
    ptr = my_pointer_from_array (decl);
    type_code = TREE_CODE (TREE_TYPE (TREE_TYPE (ptr)));
    if (type_code != RECORD_TYPE && type_code != UNION_TYPE &&
	type_code != QUAL_UNION_TYPE)
      alignment = c_size_in_bytes (TREE_TYPE (TREE_TYPE (ptr)));
    else
      alignment = integer_one_node;
    break;
  }
  case RECORD_TYPE:
  case UNION_TYPE:
  case QUAL_UNION_TYPE:
    ptr = build1 (ADDR_EXPR, build_pointer_type (type), decl);
    alignment = integer_one_node;
    break;
  default:
    ptr = build1 (ADDR_EXPR, build_pointer_type (type), decl);
    alignment = size_exp;
  }
    
  params[0] = ptr;
  params[1] = size_exp;
  params[2] = alignment;
  params[3] = build_decl_filename (decl);
  params[4] = build_decl_lineno (decl);
  params[5] = decl_name;
  fn_call = my_build_function_call ("__bounds_add_stack_object",
				    params, 6);

  if (!AGGREGATE_TYPE_P (type)) {

    /* Scalar types are handled here. We have fn_call and DECL_INITIAL.
     * We form the expression 'fn_call, DECL_INITIAL' or 'fn_call, 0'
     * and use that as new DECL_INITIAL.
     */
    tree new_init_expr, old_init_expr;

    if (already_initialized)
      old_init_expr = DECL_INITIAL (decl);
    else
      old_init_expr = integer_zero_node;
    new_init_expr = build (COMPOUND_EXPR, type,
			   fn_call,			       	/* lhs */
			   build_c_cast (type, old_init_expr));	/* rhs */

    TREE_CONSTANT (new_init_expr) = TREE_STATIC (new_init_expr) = 0;

    DECL_INITIAL (decl) = new_init_expr;

#if DEBUG && 0
    fprintf (stderr, "*** scalar declaration; replacing:\n");
    debug_tree (old_init_expr);
    fprintf (stderr, "*** with new DECL_INITIAL:\n");
    debug_tree (new_init_expr);
#endif

  } else {

    /* Array and record types are handled here. The initializers for array
     * and record types are nested 'CONSTRUCTOR' nodes. Operand 1 of a
     * CONSTRUCTOR node points to a list of TREE_LIST nodes (chained through
     * TREE_CHAIN). At each TREE_LIST node, the TREE_VALUE field points to
     * the initializer expression for that element and the TREE_PURPOSE field
     * says which element is being initialized. For example, with the
     * declaration:
     *	struct {
     *	  int a, b;
     *  } rec = {0, 1};
     * GCC might build the following initializer:
     *	VAR_DECL rec
     *	DECL_INITIAL --> CONSTRUCTOR node
     *			 op1
     *			  |
     *			  v
     *			 TREE_LIST node		TREE_LIST node
     *			 TREE_CHAIN ---------->	TREE_CHAIN --------> (0)
     *			 TREE_PURPOSE, VALUE	TREE_PURPOSE, VALUE
     *			  |		|	 |	       |
     *			  v		v        v             v
     *			 FIELD_DECL a	0	FIELD_DECL b   1
     * CONSTRUCTOR nodes are nested for nested aggregate types (eg. arrays
     * of records). The strategy here, then, depends upon whether the array
     * or record has been initialized. If not, we set out to build the
     * tree structure above, searching for the first non-aggregate element
     * and using its initializer function as for scalar types. If there
     * is already an initializer, we walk over the tree which GCC has built,
     * firstly looking for static data, and secondly to insert our constructor
     * function at the first non-aggregate node we reach.
     */

#if DEBUG
    fprintf (stderr, "*** about to frig an array or record type; old DECL_INITIAL is:\n");
    debug_tree (DECL_INITIAL (decl));
#endif

    if (!already_initialized) {
      /* BUILD the constructor tree here.
       */
      tree prev = 0, top = 0, new_init_expr, new_type, field_decl = 0;

      while (type
	     && AGGREGATE_TYPE_P (type)) {
	tree temp = build (CONSTRUCTOR, type, NULL_TREE, NULL_TREE);
	if (prev == 0)
	  top = temp;
	else
	  TREE_OPERAND (prev, 1) = build_tree_list (field_decl, temp);
	prev = temp;
	TREE_CONSTANT (prev) = TREE_STATIC (prev) = 0;
	TREE_SIDE_EFFECTS (prev) = 1;
	if (TREE_CODE (type) == ARRAY_TYPE) {
	  type = TREE_TYPE (type);
	  field_decl = integer_zero_node;
	} else if (TYPE_FIELDS (type)) {
	  field_decl = TYPE_FIELDS (type);
	  type = TREE_TYPE (field_decl);
	} else {
	  error ("this record type is empty, so cannot be bounds checked");
	  return;
	}
      } /* while */

      if (!type) {
	error ("strangeness in bounds_frig_init_decl");
	return;
      }

      /* Construct the expression 'fn_call, 0', which will be used to
       * initialize this non-aggregate element we have found.
       */
      new_init_expr = build (COMPOUND_EXPR, type,
			     fn_call,
			     build_c_cast (type, integer_zero_node));
      /* Create the lowest-level node to initialize this element. Make sure
       * GCC knows it's not constant or static.
       */
      TREE_OPERAND (prev, 1)
	= tree_cons (field_decl, new_init_expr, NULL_TREE);
      TREE_CONSTANT (new_init_expr) = TREE_STATIC (new_init_expr) = 0;

#if DEBUG
      fprintf (stderr, "*** new DECL_INITIAL is:\n");
      debug_tree (top);
#endif

      DECL_INITIAL (decl) = top;

    } else {

#if 0
      /* We WALK over the constructor tree here. We stop when we reach
       * a non-aggregate type or a string.
       */
      tree prev = 0;
      tree this_tree_list, old_init_expr, this_field_decl, rest_of_this_init,
	new_init_expr;

      /* Look for a simple string constructor first.
       */
      if (TREE_CODE (DECL_INITIAL (decl)) == STRING_CST)
	{
	  /* Simple string. */
	  DECL_INITIAL (decl) = build (COMPOUND_EXPR,
				       TREE_TYPE (DECL_INITIAL (decl)),
				       fn_call, DECL_INITIAL (decl));
	  return;
	}

      while (type
	     && AGGREGATE_TYPE_P (type)
	     && (prev == 0
		 ? TREE_CODE (DECL_INITIAL (decl)) != STRING_CST
		 : TREE_CODE (TREE_OPERAND (prev, 1)) != STRING_CST)) {
	if (prev == 0)
	  prev = DECL_INITIAL (decl);
	else
	  {
	    tree temp_prev = prev;

	    prev = TREE_OPERAND (prev, 1);
	    if (!prev)
	      {
		error ("strangeness in bounds_frig_init_decl (#1)");
		return;
	      }
	    while (prev && TREE_CODE (prev) == TREE_LIST)
	      prev = TREE_VALUE (prev);
	    if (!prev ||
		(TREE_CODE (prev) != CONSTRUCTOR &&
		 TREE_CODE (prev) != STRING_CST))
	      {
		error ("strangeness in bounds_frig_init_decl (#2)");
		return;
	      }
	    if (TREE_CODE (prev) == STRING_CST)
	      {
		prev = temp_prev;
		break;
	      }
	  }
	TREE_CONSTANT (prev) = TREE_STATIC (prev) = 0;
	TREE_SIDE_EFFECTS (prev) = 1;
	if (TREE_CODE (type) == ARRAY_TYPE)
	  type = TREE_TYPE (type);
	else if (TYPE_FIELDS (type))
	  type = TREE_TYPE (TYPE_FIELDS (type));
	else {
	  error ("this record type is empty, so cannot be bounds checked");
	  return;
	}
      } /* while */

      if (!type) {
	error ("strangeness in bounds_frig_init_decl (type == 0)");
	return;
      }
#endif

      tree prev;
      tree this_tree_list, old_init_expr, this_field_decl, rest_of_this_init,
	new_init_expr;

      /* Strings and indirect refs (eg. struct s a = *b;) are handled
       * specially here.
       */
      if (TREE_CODE (DECL_INITIAL (decl)) == STRING_CST
	  || TREE_CODE (DECL_INITIAL (decl)) == INDIRECT_REF)
	{
	  DECL_INITIAL (decl) = build (COMPOUND_EXPR,
				       TREE_TYPE (DECL_INITIAL (decl)),
				       fn_call, DECL_INITIAL (decl));
	  return;
	}

      if (TREE_CODE (DECL_INITIAL (decl)) != CONSTRUCTOR)
	{
	  error ("strangeness in bounds_frig_decl_initial (#1)");
	  return;
	}

      /* Now walk down the constructor tree. `prev' records the last
       * CONSTRUCTOR we saw. `type' is the type we are currently
       * looking at.
       */
      prev = DECL_INITIAL (decl);
      while (type
	     && AGGREGATE_TYPE_P (type))
	{
	  tree temp_prev = prev;

	  prev = TREE_OPERAND (prev, 1);
	  while (prev && TREE_CODE (prev) == TREE_LIST)
	    prev = TREE_VALUE (prev);
	  if (!prev)
	    {
	      error ("strangeness in bounds_frig_decl_initial (#2)");
	      return;
	    }
	  if (TREE_CODE (prev) != CONSTRUCTOR)
	    {
	      prev = temp_prev;
	      break;
	    }
	  TREE_CONSTANT (prev) = TREE_STATIC (prev) = 0;
	  TREE_SIDE_EFFECTS (prev) = 1;
	  if (TREE_CODE (type) == ARRAY_TYPE)
	    type = TREE_TYPE (type);
	  else if (TYPE_FIELDS (type))
	    type = TREE_TYPE (TYPE_FIELDS (type));
	  else {
	    error ("this record type is empty, so cannot be bounds checked");
	    return;
	  }
	}

      if (!type)
	{
	  error ("strangeness in bounds_frig_init_decl (type == 0)");
	  return;
	}

      /* At this point, `prev' should be the CONSTRUCTOR node of the inner-
       * most level of the initializer. We assume this constructor contains
       * at least one field, and this is the non-aggregate node that
       * we fix up.
       */
      this_tree_list = TREE_OPERAND (prev, 1);
      if (this_tree_list == NULL_TREE
	  || TREE_VALUE (this_tree_list) == NULL_TREE) {
	error ("empty initializer cannot be bounds checked");
	return;
      }
      old_init_expr = TREE_VALUE (this_tree_list);
      this_field_decl = TREE_PURPOSE (this_tree_list);
      rest_of_this_init = TREE_CHAIN (this_tree_list);

      /* Take the old initializer and add the side-effect of calling our
       * constructor function.
       */
      new_init_expr = build (COMPOUND_EXPR, TREE_TYPE (old_init_expr),
			     fn_call, old_init_expr);
      TREE_OPERAND (prev, 1)
	= tree_cons (this_field_decl, new_init_expr, rest_of_this_init);
      TREE_CONSTANT (new_init_expr) = TREE_STATIC (new_init_expr) = 0;
      TREE_CONSTANT (DECL_INITIAL (decl)) = TREE_STATIC (DECL_INITIAL (decl))
	= 0;

#if DEBUG && 0
      fprintf (stderr, "*** new DECL_INITIAL is:\n");
      debug_tree (DECL_INITIAL (decl));
#endif

    }
  }
}

/* DECL is an automatic local variable. Build a destructor function for it
 * so that when it goes out of scope, we delete it from our list of valid
 * objects.
 */
void
bounds_expand_decl_cleanup (decl)
  tree decl;
{
  tree cleanup_expr, params[1], ptr, type;

#if DEBUG && 0
  fprintf (stderr, "bounds_expand_decl_cleanup on %s\n",
	   IDENTIFIER_POINTER (DECL_NAME (decl)));
#endif

  type = TREE_TYPE (decl);
  if (TREE_CODE (type) == ARRAY_TYPE)
    ptr = my_pointer_from_array (decl);
  else
    ptr = build1 (ADDR_EXPR, build_pointer_type (type), decl);

  params[0] = ptr;
  cleanup_expr = my_build_function_call ("__bounds_delete_stack_object",
					 params, 1);
  expand_decl_cleanup (decl, cleanup_expr);
}

/*----------------------------------------------------------------------*/

/* When building a unary '&' operator, we normally perform the identities
 * &x[y] == x + y and so on. We cannot do this with bounds checking, since
 * we may already have expanded the array reference into a call to the
 * bounds checking library. So we need to extend the patterns recognized
 * here.
 *
 * #	FORM	PATTERN						RETURN
 * 1	&*p	*(type *)__bounds_check_reference (p, ...)	p
 * 2	&x[y]	x[__bounds_check_array_ref (x, y, ...)]		x+y
 * 3	&p->e	(*(type *)__bounds_check_comp_ref (p, ...)).e	p+offset(e)
 */
tree
bounds_cancel_address_expr (exp)
  tree exp;
{
  if (exp
      && TREE_CODE (exp) == INDIRECT_REF
      && (TREE_CODE (TREE_OPERAND (exp, 0)) == CONVERT_EXPR
	  || TREE_CODE (TREE_OPERAND (exp, 0)) == NOP_EXPR)
      && TREE_CODE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0)) == CALL_EXPR)
    {
      /* Could be pattern #1. Find the name of the function being
       * called here.
       */
      tree  call_expr = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
      char *fn_name   = IDENTIFIER_POINTER (DECL_NAME (TREE_OPERAND
						       (TREE_OPERAND
							(call_expr, 0), 0)));
      tree  arg0      = TREE_VALUE (TREE_OPERAND (call_expr, 1));
      tree  arg1      = TREE_VALUE (TREE_CHAIN (TREE_OPERAND (call_expr, 1)));

      if (strcmp (fn_name, "__bounds_check_reference") == 0)
	{
	  /* This is pattern #1.
	   */
	  return arg0;
	}
    }
  else if (exp
	   && TREE_CODE (exp) == ARRAY_REF
	   && TREE_CODE (TREE_OPERAND (exp, 1)) == CALL_EXPR)
    {
      /* Could be pattern #2. Find the name of the function being
       * called here.
       */
      tree call_expr = TREE_OPERAND (exp, 1);
      char *fn_name  = IDENTIFIER_POINTER (DECL_NAME (TREE_OPERAND
						      (TREE_OPERAND
						       (call_expr, 0), 0)));
      tree  arg0      = TREE_VALUE (TREE_OPERAND (call_expr, 1));
      tree  arg1      = TREE_VALUE (TREE_CHAIN (TREE_OPERAND (call_expr, 1)));

      if (strcmp (fn_name, "__bounds_check_array_reference") == 0)
	{
	  /* This is pattern #2. Return expression for `arg0 + arg1'. We
	   * still want this to be checked ...
	   */
	  tree r_expr;

	  r_expr = build_binary_op (PLUS_EXPR, arg0, arg1, 1);
	  return r_expr;
	}
    }
  else if (exp
	   && TREE_CODE (exp) == COMPONENT_REF
	   && TREE_CODE (TREE_OPERAND (exp, 0)) == INDIRECT_REF
	   && (TREE_CODE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0))
	                                        == CONVERT_EXPR
	       || TREE_CODE (TREE_OPERAND (TREE_OPERAND (exp, 0), 0))
	                                        == NOP_EXPR)
	   && TREE_CODE (TREE_OPERAND (TREE_OPERAND (TREE_OPERAND
						     (exp, 0), 0), 0))
	                                        == CALL_EXPR)
    {
      /* This is pattern #3. Return expression for `arg0 + offset (field)'.
       * Note that `arg0' is a pointer and `field' is a FIELD_DECL.
       * FIXME: This ought to be checked ...
       */
      tree call_expr   = TREE_OPERAND (TREE_OPERAND (TREE_OPERAND
						     (exp, 0), 0), 0);
      char *fn_name    = IDENTIFIER_POINTER (DECL_NAME (TREE_OPERAND
							(TREE_OPERAND
							 (call_expr, 0), 0)));
      tree arg0        = TREE_VALUE (TREE_OPERAND (call_expr, 1));
      tree field       = TREE_OPERAND (exp, 1);
      tree field_name  = DECL_NAME (field);
      tree r_expr;

      if (strcmp (fn_name, "__bounds_check_component_reference") == 0)
	{
	  bounds_checking_enabled = 0;
	  r_expr = build_unary_op (ADDR_EXPR,
				   build_component_ref (
				     build_indirect_ref (arg0,
							 "->"),
				   field_name),
				   0);
	  bounds_checking_enabled = 1;
	  return r_expr;
	}
    }

  /* No matching pattern. Return 0. */
  return NULL_TREE;
}

/*----------------------------------------------------------------------*/

tree
bounds_build_reference (ptr)
  tree ptr;
{
  tree pointer = default_conversion (ptr);
  tree type = TREE_TYPE (pointer), type_of_type = TREE_TYPE (type);
  tree params [4], function_call;
  tree size_exp = c_size_in_bytes (TREE_TYPE (type));

  /* Build a call to __bounds_check_reference. This returns the
   * pointer passed, which we then reference.
   */
  params[0] = ptr;				/* pointer */
  params[1] = size_exp;				/* reference size */
  params[2] = build_current_filename ();	/* filename */
  params[3] = build_current_lineno ();		/* line number */

  function_call = my_build_function_call ("__bounds_check_reference",
					  params, 4);

  /* Build cast to correct pointer type, and reference it. */
  return build1 (INDIRECT_REF,
		 TYPE_MAIN_VARIANT (type_of_type),
		 build_c_cast (type, function_call));
}

tree
bounds_build_component_indirect_ref (pointer, component)
  tree pointer, component;
{
  /* Generate correct code for 'p->component' expressions. The expression
   * built looks like this:
   *	*(type *)
   *	  (__bounds_check_component_reference (p, offsetof (component), ...))
   *	    . component
   */
  tree params[5], function_call;
  tree type;
  tree ref_exp, offset_exp, size_exp;

  /* Find the size and offset of the FIELD_DECL for component. Note that
   *	ref_exp		p->e
   *	size_exp	sizeof (p->e)
   *
   *#define Offset(p_type,field) ((int)&(((p_type)NULL)->field))
   *    offset_exp = Offset(type(ref_exp),component)
   */
  bounds_checking_enabled = 0;
  ref_exp = build_component_ref (build_indirect_ref (pointer, "->"),
				 component);
  if (ref_exp == error_mark_node) {
    bounds_checking_enabled = 1;
    return error_mark_node;
  }
  if (TREE_CODE (ref_exp) == COMPONENT_REF
      && DECL_BIT_FIELD (TREE_OPERAND (ref_exp, 1))) {
        bounds_checking_enabled = 1;
	return ref_exp;
  }

  /* Handle size_of(bitfields) special because the function
   * c_size_in_bytes can not handle them. (HtB)
   */
  if (TREE_CODE (ref_exp) == COMPONENT_REF
      && TREE_CODE(TREE_OPERAND (ref_exp, 1)) == FIELD_DECL) {
    int size = DECL_FIELD_SIZE(TREE_OPERAND (ref_exp, 1));

    if (size == 0) {
      size_exp = c_size_in_bytes (TREE_TYPE (ref_exp));
    }
    else {
      size_exp = build_int_2 (size,0);
      size_exp = size_binop (CEIL_DIV_EXPR, size_exp, 
			     size_int (BITS_PER_UNIT));
      force_fit_type (size_exp, 0);
    }
  }
  else {
    size_exp = c_size_in_bytes (TREE_TYPE (ref_exp));
  }

  /* (RWMJ) If `type' points to an array, turn it into a pointer to the
   * first element.
   */
  type = default_conversion (TREE_TYPE (pointer));

  if (TREE_CODE (type) == ARRAY_TYPE)
    type = my_pointer_from_array_type (type);

  offset_exp = build_c_cast(integer_type_node,
		build_unary_op (ADDR_EXPR,
		 build_component_ref (
		  build_indirect_ref (
		   build_c_cast(groktypename (type), integer_zero_node),
			 "->"), component), 0));
  bounds_checking_enabled = 1;

  params[0] = pointer;				/* pointer */
  params[1] = offset_exp;			/* offset of component */
  params[2] = size_exp;				/* size of component */
  params[3] = build_current_filename ();	/* filename */
  params[4] = build_current_lineno ();		/* line number */

  function_call = my_build_function_call ("__bounds_check_component_reference",
					  params, 5);
  return build_component_ref (build1 (INDIRECT_REF,
				      TYPE_MAIN_VARIANT (TREE_TYPE (type)),
				      build_c_cast (type, function_call)),
			      component);
}

tree
bounds_build_array_reference (array, index)
  tree array, index;
{
  /* In bounds checking modes, generate a call to __bounds_check_
   * array_reference and an indirect ref. to that.
   */
  tree params [6], function_call;
  tree array_type = TREE_TYPE (array);
  tree array_element_type = TREE_TYPE (array_type);
  tree size_exp = c_size_in_bytes (array_element_type);
  tree size_array = TYPE_SIZE(array_type)==0 ? integer_zero_node
					     : c_size_in_bytes (array_type);
  tree ptr = default_conversion (array);

  params[0] = ptr;			    	/* pointer */
  params[1] = index;				/* index in array */
  params[2] = size_exp;				/* size of array elements */
  params[3] = size_array;                      /* number of array elements */
  params[4] = build_current_filename ();	/* filename */
  params[5] = build_current_lineno ();		/* line number */

  function_call = my_build_function_call ("__bounds_check_array_reference",
					  params, 6);

  /* Build cast to correct pointer type, and reference it. */
  return build  (ARRAY_REF,
		 TYPE_MAIN_VARIANT (array_element_type),
/*		 array,build_c_cast (TREE_TYPE (ptr), function_call)); */
		 array, function_call);
}

int
bounds_can_test_array_reference_now (array, index)
  tree array, index;
{
  /* This is called before 'bounds_build_array_reference' to see if we can
   * test the array reference right now. If so, and it is outside bounds,
   * we give an error. We return 1 if we could do the test, 0 if the test
   * has to be done at run time.
   */
  tree min, max, min_is_ok, max_is_ok;
  int min_test, max_test;

  if (!TREE_CONSTANT (index)
      /* Make sure this is an array type, with known domain. */
      || TREE_CODE (TREE_TYPE (array)) != ARRAY_TYPE
      || !TYPE_DOMAIN (TREE_TYPE (array))
      || !TYPE_MIN_VALUE (TYPE_DOMAIN (TREE_TYPE (array)))
      || !TYPE_MAX_VALUE (TYPE_DOMAIN (TREE_TYPE (array))))
    return 0;

  /* See if we can get the array bounds now.
   */
  min = TYPE_MIN_VALUE (TYPE_DOMAIN (TREE_TYPE (array)));
  max = TYPE_MAX_VALUE (TYPE_DOMAIN (TREE_TYPE (array)));

  /* See if we can evaluate the test now.
   * MIN_IS_OK := 'index' >= 'min'
   * MAX_IS_OK := 'index' <= 'max'+1   (it is valid for index == max+1)
   */
  min_is_ok = build_binary_op (GE_EXPR, index, min, 0);
  max_is_ok = build_binary_op (LE_EXPR, index,
			       build_binary_op (PLUS_EXPR, max,
						integer_one_node, 0),
			       0);

  /* If we could do the comparison, and both expressions folded to constant
   * non-zero, then we have done the test, and we return 1. If either
   * expression was zero, there was a mistake. If the things couldn't be
   * folded, we return 0.
   */
  if (!min_is_ok || !max_is_ok
      || TREE_CODE (min_is_ok) != INTEGER_CST
      || TREE_CODE (max_is_ok) != INTEGER_CST)
    return 0;
  min_test = !integer_zerop (min_is_ok);
  max_test = !integer_zerop (max_is_ok);
  if (!min_test || !max_test)
    error ("array expression is out of bounds");
  return 1;
}

tree
bounds_build_ptr_plus_int (resultcode, ptrop, intop, size_exp)
  enum tree_code resultcode;
  tree ptrop, intop, size_exp;
{
  tree result_type = TREE_TYPE (ptrop);
  tree params [6];

  params[0] = ptrop;				/* pointer */
  params[1] = intop;				/* offset */
  params[2] = size_exp;				/* element size */
  params[3] = resultcode == PLUS_EXPR ?		/* is_plus? */
    integer_one_node : integer_zero_node;
  params[4] = build_current_filename ();      	/* filename */
  params[5] = build_current_lineno ();		/* line number */

  /* Return (<type>*) function_call (...); */
  return build_c_cast (result_type,
		       my_build_function_call ("__bounds_check_ptr_plus_int",
					       params, 6));
}

tree
bounds_build_ptr_diff (op0, op1, target_type)
  tree op0, op1, target_type;
{
  tree size_exp = c_size_in_bytes (target_type);
  tree params[5];

  params[0] = op0;				/* pointer #1 */
  params[1] = op1;	      			/* pointer #2 */
  params[2] = size_exp;				/* size of target type */
  params[3] = build_current_filename ();	/* filename */
  params[4] = build_current_lineno ();		/* line number */
  return my_build_function_call ("__bounds_check_ptr_diff", params, 5);
}

/* Build a pointer comparison operation. CODE will be one of LT_EXPR,
 * LE_EXPR, GE_EXPR, GT_EXPR, EQ_EXPR or NE_EXPR. RESULT_TYPE is the
 * type of the resulting expression. OP0 and OP1 are guaranteed to have
 * been promoted to pointers by this stage.
 */
tree
bounds_build_comparison (code, result_type, op0, op1)
  enum tree_code code;
  tree result_type, op0, op1;
{
  tree params[4], bool_type_node;
  char *name;

  /* RESULT_TYPE must always be integer_type_node.
   */
  if (result_type != integer_type_node) abort ();

  /* Decide which function we'll be calling here.
   */
  switch (code) {
  case LT_EXPR:		name = "__bounds_check_ptr_lt_ptr"; break;
  case LE_EXPR:		name = "__bounds_check_ptr_le_ptr"; break;
  case GE_EXPR:		name = "__bounds_check_ptr_ge_ptr"; break;
  case GT_EXPR:		name = "__bounds_check_ptr_gt_ptr"; break;
  case EQ_EXPR:		name = "__bounds_check_ptr_eq_ptr"; break;
  case NE_EXPR:		name = "__bounds_check_ptr_ne_ptr"; break;
  }
  params[0] = op0;				/* pointer #1 */
  params[1] = op1;				/* pointer #2 */
  params[2] = build_current_filename ();       	/* filename */
  params[3] = build_current_lineno ();		/* line number */

  return my_build_function_call (name, params, 4);
}

/* Build a pre- or post-, inc- or decrement expression. TYPE is the pointer
 * type. ARG is the pointer itself. INC is the actual increment amount.
 * For 'p++' we build:
 *	(type*) __bounds_check_ptr_postinc (&p, inc, ...);
 *	(type*) __bounds_check_ptr_preinc (&p, inc, ...);
 */
tree
bounds_build_inc_or_dec (code, type, arg, inc)
  enum tree_code code;
  tree type, arg, inc;
{
  tree params[4];
  char *name;

  mark_addressable (arg);

  switch (code) {
  case POSTINCREMENT_EXPR:	name = "__bounds_check_ptr_postinc"; break;
  case PREINCREMENT_EXPR:	name = "__bounds_check_ptr_preinc";  break;
  case POSTDECREMENT_EXPR:	name = "__bounds_check_ptr_postdec"; break;
  case PREDECREMENT_EXPR:	name = "__bounds_check_ptr_predec";  break;
  }

  params[0] = build1 (ADDR_EXPR, build_pointer_type (type), arg);
  params[1] = inc;
  params[2] = build_current_filename ();
  params[3] = build_current_lineno ();

  return build_c_cast (type,
		       my_build_function_call (name, params, 4));
}

/* Build the truthvalue of a pointer expression, eg. `if (ptr)'. We build
 * a call to __bounds_check_ptr_true (ptr, filename, line); The return type
 * is int.
 */
tree
bounds_build_truthvalue_conversion (arg)
  tree arg;
{
  tree params[3];

  params[0] = default_conversion (arg);
  params[1] = build_current_filename ();
  params[2] = build_current_lineno ();

  return my_build_function_call ("__bounds_check_ptr_true", params, 3);
}

/* Build the invert truthvalue of a pointer expression, ie. `!ptr'. We build
 * a call to __bounds_check_ptr_false (ptr, filename, line); The return type
 * is int.
 */
tree
bounds_build_invert_truthvalue (arg)
  tree arg;
{
  tree params[3];

  params[0] = default_conversion (arg);
  params[1] = build_current_filename ();
  params[2] = build_current_lineno ();

  return my_build_function_call ("__bounds_check_ptr_false", params, 3);
}

/* This function is called just after the function's arguments have been
 * processed. We here build extra code at the start and end of the function
 * to find the function's arguments, and if the function is main, to find
 * the program's argument list. A side effect of this call is that an
 * extra block level will be built around the function. As an example:
 *	f (int a, char b) { ... }
 * becomes:
 *	f (int a, char b)
 *	{
 *	  __bounds_push_function ("f", 2, 0, ...);
 *	  __bounds_add_param_object (&a, 4, 4, file, line, "a");
 *	  __bounds_add_param_object (&b, 1, 1, file, line, "b");
 *	  {
 *	    previous function code here ...
 *	  }
 *	  __bounds_pop_function ("f");
 *	}
 */
void
bounds_build_args ()
{
  tree params[6], function_call, arg, cleanup_expr, cleanup_params[1];
  int temp;

  /* This is the current function declaration. */
  tree fndecl = current_function_decl;

  /* This is the list of arguments of the current function. */
  tree args = DECL_ARGUMENTS (fndecl);

  /* This is the name of the function. */
  char *function_name;

  /* True if this is main (). */
  int in_main = 0;

  /* Records the number of arguments to this function. Only `real' arguments
   * are checked. Varargs are treated as unchecked stack objects.
   */
  int nr_args;

  /* Some checks here ... */
  if (!DECL_NAME (fndecl)) {
    error ("can't have unnamed functions in bounds checked code");
    return;
  }

  /* Get the function name. Determine if it's `main'.
   */
  function_name = IDENTIFIER_POINTER (DECL_NAME (fndecl));
  in_main = strcmp (function_name, "main") == 0
    && DECL_CONTEXT (fndecl) == NULL_TREE;

  /* Enter a new function context. This is the 'outer block' of this function.
   * This is matched by a matching poplevel in finish_function.
   */
  pushlevel (0);
  clear_last_expr ();
  push_momentary ();
  expand_start_bindings (0);
  bounds_poplevel = 1;			/* Remember to do the poplevel. */

  /* Make sure that '__bounds_pop_function' gets called if we leave this
   * function early.
   */
  temp = suspend_momentary ();
  cleanup_params[0] = my_build_string (function_name);
  cleanup_expr = my_build_function_call ("__bounds_pop_function",
					 cleanup_params, 1);
  expand_decl_cleanup (NULL_TREE, cleanup_expr);
  resume_momentary (temp);

  /* Count the number of arguments passed. We tell the library how many
   * arguments we're going to pass, as a slight simplification there.
   */
  for (nr_args = 0, arg = args; arg; arg = TREE_CHAIN (arg), ++nr_args)
    ;

  /* Build a call to __bounds_push_function with the function name and the
   * source file/line it appears at. The matching '__bounds_pop_function' will
   * delete all unmatched stack objects when the function exits.
   */
  params[0] = my_build_string (function_name);
  params[1] = build_int_2 (nr_args, 0);
  params[2] = build_int_2 (in_main, 0);
  params[3] = build_current_filename ();
  params[4] = build_current_lineno ();
  function_call = my_build_function_call ("__bounds_push_function",
					  params, 5);
  iterator_expand (function_call);
  clear_momentary ();

  /* Note the locations of the function parameters now.
   */
  for (arg = args; arg; arg = TREE_CHAIN (arg))
    {
      tree size_exp, ptr, alignment, type;
      enum tree_code code;

      /* Make sure that the parameter gets a stack slot and doesn't stay in
       * a register. Setting DECL_REGISTER to 0 avoids warning messages if
       * the parameter is actually declared `register'.
       */
      DECL_REGISTER (arg) = 0;
      /* Note (8/6/95): We don't want to do this, since we might be able
       * to delete the call to __bounds_add_param_object if the parameter
       * is never really addressed, unless it's an array decl. */
      if (TREE_CODE (TREE_TYPE (arg)) == ARRAY_TYPE)
	mark_addressable (arg);
      else
	put_var_into_stack (arg);

      /* Build a call to __bounds_add_param_object (...) which is just a
       * wrapper around __bounds_add_stack_object and takes the same args.
       */
      type = TREE_TYPE (arg);
      code = TREE_CODE (type);

      size_exp = c_size_in_bytes (type);
      switch (code) {
      case ARRAY_TYPE: {
	enum tree_code type_code;
	ptr = my_pointer_from_array (arg);
	type_code = TREE_CODE (TREE_TYPE (TREE_TYPE (ptr)));
	if (type_code != RECORD_TYPE && type_code != UNION_TYPE &&
	    type_code != QUAL_UNION_TYPE)
	  alignment = c_size_in_bytes (TREE_TYPE (TREE_TYPE (ptr)));
	else
	  alignment = integer_one_node;
	break;
      }
      case RECORD_TYPE:
      case UNION_TYPE:
      case QUAL_UNION_TYPE:
	ptr = build1 (ADDR_EXPR, build_pointer_type (type), arg);
	alignment = integer_one_node;
	break;
      default:
	ptr = build1 (ADDR_EXPR, build_pointer_type (type), arg);
	alignment = size_exp;
      }

      params[0] = ptr;
      params[1] = size_exp;
      params[2] = alignment;
      params[3] = build_decl_filename (arg);
      params[4] = build_decl_lineno (arg);
      params[5] = my_build_string (IDENTIFIER_POINTER (DECL_NAME (arg)));

      function_call = my_build_function_call ("__bounds_add_param_object",
					      params, 6);
      iterator_expand (function_call);
      clear_momentary ();
    }
}


/*----------------------------------------------------------------------*/

/* This is the section where we attempt to optimize calls to
 *	__bounds_push_function
 *	__bounds_pop_function
 *	__bounds_add_param_object
 *	__bounds_add_stack_object
 *	__bounds_delete_stack_object
 * We cannily observe that if the address of a local variable or local
 * parameter is not taken (with '&') then there is no need recording
 * it in the tree. But we can't determine this until the whole function has
 * been parsed, so we have to delete these calls later on to optimize them.
 * We fiddle `calls.c' so that whenever one of these calls is expanded
 * into RTL, we record the first and last RTX in the generated sequence,
 * and the VAR_DECL corresponding to the first parameter. `calls.c' will
 * call
 *	bounds_is_deletable_fn_p
 *	bounds_note_call_for_deletion
 * to do this.
 * At the end of the function, in `finish_function',
 *	bounds_delete_redundant_calls
 * is called which goes over these saved calls in two passes to determine which
 * can be deleted.
 * In the first pass, we delete calls to `__bounds_add_stack_object',
 * `__bounds_delete_stack_object' and `__bounds_add_param_object' if they
 * are redundant (if the VAR_DECL is not addressed).
 * In the second pass, we delete calls to `__bounds_push_function' and
 * `__bounds_pop_function' is no calls to add parameters remain in the
 * function.
 */
/*--*/
/* `BOUNDS_IS_DELETABLE_FN_P' is called from expand_call to determine if
 * this function call may later be deleted by bounds_delete_redundant_calls.
 */
int
bounds_is_deletable_fn_p (name)
  char *name;
{
  if (name != NULL &&
      name[0] == '_' && name[1] == '_' && name[2] == 'b' &&
      name[3] == 'o' && name[4] == 'u' && name[5] == 'n' &&
      name[6] == 'd' && name[7] == 's' && name[8] == '_' &&
      (strcmp (&name[9], "add_stack_object") == 0 ||
       strcmp (&name[9], "delete_stack_object") == 0 ||
       strcmp (&name[9], "add_param_object") == 0 ||
       strcmp (&name[9], "push_function") == 0 ||
       strcmp (&name[9], "pop_function") == 0))
    return 1;
  return 0;
}

/* This is a list of possibly deletable function calls in the current
 * function. We reset the list after using it in `bounds_delete_redundant_
 * calls'.
 */
static struct deletable_fn_call {
  struct deletable_fn_call *next;
  tree vardecl;
  int fntype;
  rtx first_insn, last_insn;
} *first = NULL;

/* `BOUNDS_NOTE_CALL_FOR_DELETION' is called at the end of expand_call to
 * record the beginning and end of the current sequence of insns making
 * up a function call that may be deleted. In this function, we also
 * record the corresponding VAR_DECL if appropriate.
 */
void
bounds_note_call_for_deletion (first_insn, last_insn, fnname, callexpr)
  rtx first_insn, last_insn;
  char *fnname;
  tree callexpr;
{
  tree vardecl;
  int fntype;
  struct deletable_fn_call *ptr;

  /* Check this is a deletable function call: if not we have made a
   * mistake.
   */
  if (fnname == NULL ||
      fnname[0] != '_' || fnname[1] != '_' || fnname[2] != 'b' ||
      fnname[3] != 'o' || fnname[4] != 'u' || fnname[5] != 'n' ||
      fnname[6] != 'd' || fnname[7] != 's' || fnname[8] != '_')
    abort ();
  /* Which function call is this?
   */
  if (strcmp (&fnname[9], "add_stack_object") == 0)
    fntype = 1;
  else if (strcmp (&fnname[9], "delete_stack_object") == 0)
    fntype = 1;
  else if (strcmp (&fnname[9], "add_param_object") == 0)
    fntype = 2;
  else if (strcmp (&fnname[9], "push_function") == 0)
    fntype = 3;
  else if (strcmp (&fnname[9], "pop_function") == 0)
    fntype = 3;
  else
    abort ();

  /* If function type is 1 or 2, look at the first argument to find the
   * corresponding VAR_DECL.
   */
  if (fntype == 1 || fntype == 2)
    {
      /* The first argument has the form '&v' where v is the VAR_DECL we
       * want.
       */
      tree arg0 = TREE_VALUE (TREE_OPERAND (callexpr, 1)); /* First argument */

      if (arg0 == NULL				/* Check this is '&v' */
	  || TREE_CODE (arg0) != ADDR_EXPR)
	abort ();
      vardecl = TREE_OPERAND (arg0, 0);
      if (vardecl == NULL			/* Must be parmdecl/vardecl. */
	  || (TREE_CODE (vardecl) != PARM_DECL
	      && TREE_CODE (vardecl) != VAR_DECL))
	abort ();
    }

  /* Add this to the current list.
   */
  ptr = (struct deletable_fn_call *) xmalloc (sizeof (struct deletable_fn_call));
  ptr->next = first;
  first = ptr;
  ptr->first_insn = first_insn;
  ptr->last_insn = last_insn;
  ptr->fntype = fntype;
  ptr->vardecl = vardecl;
}

/* `BOUNDS_DELETE_REDUNDANT_CALLS' deletes redundant calls in two passes. Look
 * at the comments above to find out more about this function.
 */
static void delete_call PROTO((struct deletable_fn_call *));

void
bounds_delete_redundant_calls (fndecl)
  tree fndecl;
{
  struct deletable_fn_call *ptr, *ptr_next;

  /* If a call to `__bounds_add_param_object' is not deleted, this is set to
   * 1, and pass 2 is omitted.
   */
  int param_undeleted = 0;

  /* If we are in main(), then this variable will be nonzero, and we _don't_
   * delete calls to add_param_object and push/pop_function. This is
   * because we use these calls to get the position of argc/argv.
   */
  int in_main =
    strcmp (IDENTIFIER_POINTER (DECL_NAME (fndecl)), "main") == 0
    && DECL_CONTEXT (fndecl) == NULL_TREE;

  /* First pass. Look for calls to `__bounds_add_stack_object',
   * `__bounds_delete_stack_object', `__bounds_add_param_object' and if the
   * first argument was not marked addressable, delete it.
   */
/*  fprintf (stderr, "******\n"); */
  for (ptr = first; ptr; ptr = ptr->next)
    {
#if 0
      fprintf (stderr,
	       "CALL: insns from %d to %d, fntype %d, vardecl %p (%s) %c\n",
	       INSN_UID (ptr->first_insn), INSN_UID (ptr->last_insn),
	       ptr->fntype, (void *) (ptr->vardecl),
	       IDENTIFIER_POINTER (DECL_NAME (ptr->vardecl)),
	       ptr->vardecl && TREE_ADDRESSABLE (ptr->vardecl) ? '*' : ' ');
#endif

      /* If this is `main' and this is a PARM_DECL associated with a call
       * to a `__bounds_add_param_object' then mark it addressable so it
       * doesn't get deleted.
       */
      if (in_main && ptr->fntype == 2)
	mark_addressable (ptr->vardecl);

      if (ptr->fntype == 1 || ptr->fntype == 2)
	{
	  if (!TREE_ADDRESSABLE (ptr->vardecl))
	    delete_call (ptr);
	  else
	    if (ptr->fntype == 2)
	      param_undeleted = 1;
	}
    }

  /* Second pass. If all calls to `__bounds_add_param_object' were deleted,
   * then we can delete all the calls to `__bounds_push_function' and
   * `__bounds_pop_function' unconditionally.
   */
  if (!param_undeleted)
    for (ptr = first; ptr; ptr = ptr->next)
      if (ptr->fntype == 3)
	delete_call (ptr);

  /* Free up the deletable calls list.
   */
  for (ptr = first; ptr; ptr = ptr_next)
    {
      ptr_next = ptr->next;
      free (ptr);
    }
  first = NULL;
}

/* Delete a function call directly from the RTL using the INSN rtx's
 * recorded when the function call was set down. Notice that `delete_insn'
 * doesn't actually delete instructions unless optimize > 0, so we have
 * to have to do that ourselves here.
 */
static void
delete_call (ptr)
  struct deletable_fn_call *ptr;
{
#if 0
  fprintf (stderr, "Deleting insns %d to %d.\n",
	   INSN_UID (ptr->first_insn),
	   INSN_UID (ptr->last_insn));
#endif

  /* This is OK since there are no jumps or labels in the code we are
   * deleting, and since code to delete never occurs right at the
   * beginning or end of the function.
   */
  if (PREV_INSN (ptr->first_insn) == NULL_RTX ||
      NEXT_INSN (ptr->last_insn)  == NULL_RTX)
    abort ();
  else
    {
      NEXT_INSN (PREV_INSN (ptr->first_insn)) = NEXT_INSN (ptr->last_insn);
      PREV_INSN (NEXT_INSN (ptr->last_insn))  = PREV_INSN (ptr->first_insn);
    }
}


/*----------------------------------------------------------------------*/

/* Build a node containing the current line number and current input file-
 * name.
 */
static tree
build_current_lineno ()
{
  return build_int_2 (lineno, 0);
}

static tree
build_current_filename ()
{
  return my_build_string (input_filename);
}

/* `build_string' wrapper that sets TREE_TYPE and other things correctly. We
 * can build strings like the input_filename over and over again, and GCC will
 * optimize them to a single instance in the output file.
 */
static tree
my_build_string (str)
  char *str;
{
  int len;
  tree t;

  len = strlen (str)+1;
  t = build_string (len, str);
  TREE_TYPE (t) = char_array_type_node;	/* string_type_node doesn't work */
  TREE_CONSTANT (t) = 1;	    	/* put it in the text segment */
  TREE_STATIC (t) = 1;			/* don't share it */

  return t;
}

/* Build a node containing the decl's line number and source file.
 */
static tree
build_decl_lineno (decl)
  tree decl;
{
  return build_int_2 (DECL_SOURCE_LINE (decl), 0);
}

static tree
build_decl_filename (decl)
  tree decl;
{
  return my_build_string (DECL_SOURCE_FILE (decl));
}

/* Build function call with parameters. n >= 0.
 */
static tree
my_build_function_call (name, params, n)
  char *name;			/* Name of the function (as string) */
  tree *params;			/* Array of parameters */
  int n;		    	/* Number of parameters (n >= 0) */
{
  tree paramlist, function_name, function_decl;
  int i;

  if (n > 0) {
    /* Chain the parameters together in reverse order. */
    paramlist = build_tree_list (NULL_TREE, params[n-1]);
    for (i = n-2; i >= 0; --i)
      paramlist = chainon (build_tree_list (NULL_TREE, params[i]), paramlist);
  } else
    paramlist = NULL_TREE;

  /* Look up function name and get the declaration of it. */
  function_name = get_identifier (name);
  function_decl = lookup_name (function_name);

  /* Return function_call (...); */
  return build_function_call (function_decl, paramlist);
}

/* From a decl which has type array, get a pointer to the first element that
 * has type the primary array element type.
 * eg.	int a[10][5]; would return a pointer to an int.
 * For 1D arrays, this is identical to `default_conversion'.
 */
static tree
my_pointer_from_array (decl)
  tree decl;
{
  tree ptr, type;

  if (TREE_CODE (TREE_TYPE (decl)) != ARRAY_TYPE)
    abort ();

  /* Get the correct pointer, but we will change its type in a minute.
   */
  ptr = default_conversion (decl);

  type = TREE_TYPE (decl);
  while (TREE_CODE (type) == ARRAY_TYPE)
    type = TREE_TYPE (type);

  /* Type should now be the type of the fundamental elements (eg. int). Build
   * a pointer to that.
   */
  TREE_TYPE (ptr) = build_pointer_type (type);

  return ptr;
}

/* Similar to the above function, but build the pointer type from the array
 * type directly.
 */
static tree
my_pointer_from_array_type (type)
  tree type;
{
  if (TREE_CODE (type) != ARRAY_TYPE)
    abort ();

  while (TREE_CODE (type) == ARRAY_TYPE)
    type = TREE_TYPE (type);

  /* Type should now be the type of the fundamental elements (eg. int). Build
   * a pointer to that.
   */
  return build_pointer_type (type);
}
