OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-stable/] [gcc-4.5.1/] [gcc/] [stmt.c] - Diff between revs 816 and 826

Only display areas with differences | Details | Blame | View Log

Rev 816 Rev 826
/* Expands front end tree to back end RTL for GCC
/* Expands front end tree to back end RTL for GCC
   Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997,
   Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997,
   1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
   1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
   2010 Free Software Foundation, Inc.
   2010 Free Software Foundation, Inc.
 
 
This file is part of GCC.
This file is part of GCC.
 
 
GCC is free software; you can redistribute it and/or modify it under
GCC is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 3, or (at your option) any later
Software Foundation; either version 3, or (at your option) any later
version.
version.
 
 
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
for more details.
for more details.
 
 
You should have received a copy of the GNU General Public License
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3.  If not see
along with GCC; see the file COPYING3.  If not see
<http://www.gnu.org/licenses/>.  */
<http://www.gnu.org/licenses/>.  */
 
 
/* This file handles the generation of rtl code from tree structure
/* This file handles the generation of rtl code from tree structure
   above the level of expressions, using subroutines in exp*.c and emit-rtl.c.
   above the level of expressions, using subroutines in exp*.c and emit-rtl.c.
   The functions whose names start with `expand_' are called by the
   The functions whose names start with `expand_' are called by the
   expander to generate RTL instructions for various kinds of constructs.  */
   expander to generate RTL instructions for various kinds of constructs.  */
 
 
#include "config.h"
#include "config.h"
#include "system.h"
#include "system.h"
#include "coretypes.h"
#include "coretypes.h"
#include "tm.h"
#include "tm.h"
 
 
#include "rtl.h"
#include "rtl.h"
#include "hard-reg-set.h"
#include "hard-reg-set.h"
#include "tree.h"
#include "tree.h"
#include "tm_p.h"
#include "tm_p.h"
#include "flags.h"
#include "flags.h"
#include "except.h"
#include "except.h"
#include "function.h"
#include "function.h"
#include "insn-config.h"
#include "insn-config.h"
#include "expr.h"
#include "expr.h"
#include "libfuncs.h"
#include "libfuncs.h"
#include "recog.h"
#include "recog.h"
#include "machmode.h"
#include "machmode.h"
#include "toplev.h"
#include "toplev.h"
#include "output.h"
#include "output.h"
#include "ggc.h"
#include "ggc.h"
#include "langhooks.h"
#include "langhooks.h"
#include "predict.h"
#include "predict.h"
#include "optabs.h"
#include "optabs.h"
#include "target.h"
#include "target.h"
#include "gimple.h"
#include "gimple.h"
#include "regs.h"
#include "regs.h"
#include "alloc-pool.h"
#include "alloc-pool.h"
#include "pretty-print.h"
#include "pretty-print.h"


/* Functions and data structures for expanding case statements.  */
/* Functions and data structures for expanding case statements.  */
 
 
/* Case label structure, used to hold info on labels within case
/* Case label structure, used to hold info on labels within case
   statements.  We handle "range" labels; for a single-value label
   statements.  We handle "range" labels; for a single-value label
   as in C, the high and low limits are the same.
   as in C, the high and low limits are the same.
 
 
   We start with a vector of case nodes sorted in ascending order, and
   We start with a vector of case nodes sorted in ascending order, and
   the default label as the last element in the vector.  Before expanding
   the default label as the last element in the vector.  Before expanding
   to RTL, we transform this vector into a list linked via the RIGHT
   to RTL, we transform this vector into a list linked via the RIGHT
   fields in the case_node struct.  Nodes with higher case values are
   fields in the case_node struct.  Nodes with higher case values are
   later in the list.
   later in the list.
 
 
   Switch statements can be output in three forms.  A branch table is
   Switch statements can be output in three forms.  A branch table is
   used if there are more than a few labels and the labels are dense
   used if there are more than a few labels and the labels are dense
   within the range between the smallest and largest case value.  If a
   within the range between the smallest and largest case value.  If a
   branch table is used, no further manipulations are done with the case
   branch table is used, no further manipulations are done with the case
   node chain.
   node chain.
 
 
   The alternative to the use of a branch table is to generate a series
   The alternative to the use of a branch table is to generate a series
   of compare and jump insns.  When that is done, we use the LEFT, RIGHT,
   of compare and jump insns.  When that is done, we use the LEFT, RIGHT,
   and PARENT fields to hold a binary tree.  Initially the tree is
   and PARENT fields to hold a binary tree.  Initially the tree is
   totally unbalanced, with everything on the right.  We balance the tree
   totally unbalanced, with everything on the right.  We balance the tree
   with nodes on the left having lower case values than the parent
   with nodes on the left having lower case values than the parent
   and nodes on the right having higher values.  We then output the tree
   and nodes on the right having higher values.  We then output the tree
   in order.
   in order.
 
 
   For very small, suitable switch statements, we can generate a series
   For very small, suitable switch statements, we can generate a series
   of simple bit test and branches instead.  */
   of simple bit test and branches instead.  */
 
 
struct case_node
struct case_node
{
{
  struct case_node      *left;  /* Left son in binary tree */
  struct case_node      *left;  /* Left son in binary tree */
  struct case_node      *right; /* Right son in binary tree; also node chain */
  struct case_node      *right; /* Right son in binary tree; also node chain */
  struct case_node      *parent; /* Parent of node in binary tree */
  struct case_node      *parent; /* Parent of node in binary tree */
  tree                  low;    /* Lowest index value for this label */
  tree                  low;    /* Lowest index value for this label */
  tree                  high;   /* Highest index value for this label */
  tree                  high;   /* Highest index value for this label */
  tree                  code_label; /* Label to jump to when node matches */
  tree                  code_label; /* Label to jump to when node matches */
};
};
 
 
typedef struct case_node case_node;
typedef struct case_node case_node;
typedef struct case_node *case_node_ptr;
typedef struct case_node *case_node_ptr;
 
 
/* These are used by estimate_case_costs and balance_case_nodes.  */
/* These are used by estimate_case_costs and balance_case_nodes.  */
 
 
/* This must be a signed type, and non-ANSI compilers lack signed char.  */
/* This must be a signed type, and non-ANSI compilers lack signed char.  */
static short cost_table_[129];
static short cost_table_[129];
static int use_cost_table;
static int use_cost_table;
static int cost_table_initialized;
static int cost_table_initialized;
 
 
/* Special care is needed because we allow -1, but TREE_INT_CST_LOW
/* Special care is needed because we allow -1, but TREE_INT_CST_LOW
   is unsigned.  */
   is unsigned.  */
#define COST_TABLE(I)  cost_table_[(unsigned HOST_WIDE_INT) ((I) + 1)]
#define COST_TABLE(I)  cost_table_[(unsigned HOST_WIDE_INT) ((I) + 1)]


static int n_occurrences (int, const char *);
static int n_occurrences (int, const char *);
static bool tree_conflicts_with_clobbers_p (tree, HARD_REG_SET *);
static bool tree_conflicts_with_clobbers_p (tree, HARD_REG_SET *);
static void expand_nl_goto_receiver (void);
static void expand_nl_goto_receiver (void);
static bool check_operand_nalternatives (tree, tree);
static bool check_operand_nalternatives (tree, tree);
static bool check_unique_operand_names (tree, tree, tree);
static bool check_unique_operand_names (tree, tree, tree);
static char *resolve_operand_name_1 (char *, tree, tree, tree);
static char *resolve_operand_name_1 (char *, tree, tree, tree);
static void expand_null_return_1 (void);
static void expand_null_return_1 (void);
static void expand_value_return (rtx);
static void expand_value_return (rtx);
static int estimate_case_costs (case_node_ptr);
static int estimate_case_costs (case_node_ptr);
static bool lshift_cheap_p (void);
static bool lshift_cheap_p (void);
static int case_bit_test_cmp (const void *, const void *);
static int case_bit_test_cmp (const void *, const void *);
static void emit_case_bit_tests (tree, tree, tree, tree, case_node_ptr, rtx);
static void emit_case_bit_tests (tree, tree, tree, tree, case_node_ptr, rtx);
static void balance_case_nodes (case_node_ptr *, case_node_ptr);
static void balance_case_nodes (case_node_ptr *, case_node_ptr);
static int node_has_low_bound (case_node_ptr, tree);
static int node_has_low_bound (case_node_ptr, tree);
static int node_has_high_bound (case_node_ptr, tree);
static int node_has_high_bound (case_node_ptr, tree);
static int node_is_bounded (case_node_ptr, tree);
static int node_is_bounded (case_node_ptr, tree);
static void emit_case_nodes (rtx, case_node_ptr, rtx, tree);
static void emit_case_nodes (rtx, case_node_ptr, rtx, tree);
static struct case_node *add_case_node (struct case_node *, tree,
static struct case_node *add_case_node (struct case_node *, tree,
                                        tree, tree, tree, alloc_pool);
                                        tree, tree, tree, alloc_pool);
 
 


/* Return the rtx-label that corresponds to a LABEL_DECL,
/* Return the rtx-label that corresponds to a LABEL_DECL,
   creating it if necessary.  */
   creating it if necessary.  */
 
 
rtx
rtx
label_rtx (tree label)
label_rtx (tree label)
{
{
  gcc_assert (TREE_CODE (label) == LABEL_DECL);
  gcc_assert (TREE_CODE (label) == LABEL_DECL);
 
 
  if (!DECL_RTL_SET_P (label))
  if (!DECL_RTL_SET_P (label))
    {
    {
      rtx r = gen_label_rtx ();
      rtx r = gen_label_rtx ();
      SET_DECL_RTL (label, r);
      SET_DECL_RTL (label, r);
      if (FORCED_LABEL (label) || DECL_NONLOCAL (label))
      if (FORCED_LABEL (label) || DECL_NONLOCAL (label))
        LABEL_PRESERVE_P (r) = 1;
        LABEL_PRESERVE_P (r) = 1;
    }
    }
 
 
  return DECL_RTL (label);
  return DECL_RTL (label);
}
}
 
 
/* As above, but also put it on the forced-reference list of the
/* As above, but also put it on the forced-reference list of the
   function that contains it.  */
   function that contains it.  */
rtx
rtx
force_label_rtx (tree label)
force_label_rtx (tree label)
{
{
  rtx ref = label_rtx (label);
  rtx ref = label_rtx (label);
  tree function = decl_function_context (label);
  tree function = decl_function_context (label);
 
 
  gcc_assert (function);
  gcc_assert (function);
 
 
  forced_labels = gen_rtx_EXPR_LIST (VOIDmode, ref, forced_labels);
  forced_labels = gen_rtx_EXPR_LIST (VOIDmode, ref, forced_labels);
  return ref;
  return ref;
}
}
 
 
/* Add an unconditional jump to LABEL as the next sequential instruction.  */
/* Add an unconditional jump to LABEL as the next sequential instruction.  */
 
 
void
void
emit_jump (rtx label)
emit_jump (rtx label)
{
{
  do_pending_stack_adjust ();
  do_pending_stack_adjust ();
  emit_jump_insn (gen_jump (label));
  emit_jump_insn (gen_jump (label));
  emit_barrier ();
  emit_barrier ();
}
}
 
 
/* Emit code to jump to the address
/* Emit code to jump to the address
   specified by the pointer expression EXP.  */
   specified by the pointer expression EXP.  */
 
 
void
void
expand_computed_goto (tree exp)
expand_computed_goto (tree exp)
{
{
  rtx x = expand_normal (exp);
  rtx x = expand_normal (exp);
 
 
  x = convert_memory_address (Pmode, x);
  x = convert_memory_address (Pmode, x);
 
 
  do_pending_stack_adjust ();
  do_pending_stack_adjust ();
  emit_indirect_jump (x);
  emit_indirect_jump (x);
}
}


/* Handle goto statements and the labels that they can go to.  */
/* Handle goto statements and the labels that they can go to.  */
 
 
/* Specify the location in the RTL code of a label LABEL,
/* Specify the location in the RTL code of a label LABEL,
   which is a LABEL_DECL tree node.
   which is a LABEL_DECL tree node.
 
 
   This is used for the kind of label that the user can jump to with a
   This is used for the kind of label that the user can jump to with a
   goto statement, and for alternatives of a switch or case statement.
   goto statement, and for alternatives of a switch or case statement.
   RTL labels generated for loops and conditionals don't go through here;
   RTL labels generated for loops and conditionals don't go through here;
   they are generated directly at the RTL level, by other functions below.
   they are generated directly at the RTL level, by other functions below.
 
 
   Note that this has nothing to do with defining label *names*.
   Note that this has nothing to do with defining label *names*.
   Languages vary in how they do that and what that even means.  */
   Languages vary in how they do that and what that even means.  */
 
 
void
void
expand_label (tree label)
expand_label (tree label)
{
{
  rtx label_r = label_rtx (label);
  rtx label_r = label_rtx (label);
 
 
  do_pending_stack_adjust ();
  do_pending_stack_adjust ();
  emit_label (label_r);
  emit_label (label_r);
  if (DECL_NAME (label))
  if (DECL_NAME (label))
    LABEL_NAME (DECL_RTL (label)) = IDENTIFIER_POINTER (DECL_NAME (label));
    LABEL_NAME (DECL_RTL (label)) = IDENTIFIER_POINTER (DECL_NAME (label));
 
 
  if (DECL_NONLOCAL (label))
  if (DECL_NONLOCAL (label))
    {
    {
      expand_nl_goto_receiver ();
      expand_nl_goto_receiver ();
      nonlocal_goto_handler_labels
      nonlocal_goto_handler_labels
        = gen_rtx_EXPR_LIST (VOIDmode, label_r,
        = gen_rtx_EXPR_LIST (VOIDmode, label_r,
                             nonlocal_goto_handler_labels);
                             nonlocal_goto_handler_labels);
    }
    }
 
 
  if (FORCED_LABEL (label))
  if (FORCED_LABEL (label))
    forced_labels = gen_rtx_EXPR_LIST (VOIDmode, label_r, forced_labels);
    forced_labels = gen_rtx_EXPR_LIST (VOIDmode, label_r, forced_labels);
 
 
  if (DECL_NONLOCAL (label) || FORCED_LABEL (label))
  if (DECL_NONLOCAL (label) || FORCED_LABEL (label))
    maybe_set_first_label_num (label_r);
    maybe_set_first_label_num (label_r);
}
}
 
 
/* Generate RTL code for a `goto' statement with target label LABEL.
/* Generate RTL code for a `goto' statement with target label LABEL.
   LABEL should be a LABEL_DECL tree node that was or will later be
   LABEL should be a LABEL_DECL tree node that was or will later be
   defined with `expand_label'.  */
   defined with `expand_label'.  */
 
 
void
void
expand_goto (tree label)
expand_goto (tree label)
{
{
#ifdef ENABLE_CHECKING
#ifdef ENABLE_CHECKING
  /* Check for a nonlocal goto to a containing function.  Should have
  /* Check for a nonlocal goto to a containing function.  Should have
     gotten translated to __builtin_nonlocal_goto.  */
     gotten translated to __builtin_nonlocal_goto.  */
  tree context = decl_function_context (label);
  tree context = decl_function_context (label);
  gcc_assert (!context || context == current_function_decl);
  gcc_assert (!context || context == current_function_decl);
#endif
#endif
 
 
  emit_jump (label_rtx (label));
  emit_jump (label_rtx (label));
}
}


/* Return the number of times character C occurs in string S.  */
/* Return the number of times character C occurs in string S.  */
static int
static int
n_occurrences (int c, const char *s)
n_occurrences (int c, const char *s)
{
{
  int n = 0;
  int n = 0;
  while (*s)
  while (*s)
    n += (*s++ == c);
    n += (*s++ == c);
  return n;
  return n;
}
}


/* Generate RTL for an asm statement (explicit assembler code).
/* Generate RTL for an asm statement (explicit assembler code).
   STRING is a STRING_CST node containing the assembler code text,
   STRING is a STRING_CST node containing the assembler code text,
   or an ADDR_EXPR containing a STRING_CST.  VOL nonzero means the
   or an ADDR_EXPR containing a STRING_CST.  VOL nonzero means the
   insn is volatile; don't optimize it.  */
   insn is volatile; don't optimize it.  */
 
 
static void
static void
expand_asm_loc (tree string, int vol, location_t locus)
expand_asm_loc (tree string, int vol, location_t locus)
{
{
  rtx body;
  rtx body;
 
 
  if (TREE_CODE (string) == ADDR_EXPR)
  if (TREE_CODE (string) == ADDR_EXPR)
    string = TREE_OPERAND (string, 0);
    string = TREE_OPERAND (string, 0);
 
 
  body = gen_rtx_ASM_INPUT_loc (VOIDmode,
  body = gen_rtx_ASM_INPUT_loc (VOIDmode,
                                ggc_strdup (TREE_STRING_POINTER (string)),
                                ggc_strdup (TREE_STRING_POINTER (string)),
                                locus);
                                locus);
 
 
  MEM_VOLATILE_P (body) = vol;
  MEM_VOLATILE_P (body) = vol;
 
 
  emit_insn (body);
  emit_insn (body);
}
}
 
 
/* Parse the output constraint pointed to by *CONSTRAINT_P.  It is the
/* Parse the output constraint pointed to by *CONSTRAINT_P.  It is the
   OPERAND_NUMth output operand, indexed from zero.  There are NINPUTS
   OPERAND_NUMth output operand, indexed from zero.  There are NINPUTS
   inputs and NOUTPUTS outputs to this extended-asm.  Upon return,
   inputs and NOUTPUTS outputs to this extended-asm.  Upon return,
   *ALLOWS_MEM will be TRUE iff the constraint allows the use of a
   *ALLOWS_MEM will be TRUE iff the constraint allows the use of a
   memory operand.  Similarly, *ALLOWS_REG will be TRUE iff the
   memory operand.  Similarly, *ALLOWS_REG will be TRUE iff the
   constraint allows the use of a register operand.  And, *IS_INOUT
   constraint allows the use of a register operand.  And, *IS_INOUT
   will be true if the operand is read-write, i.e., if it is used as
   will be true if the operand is read-write, i.e., if it is used as
   an input as well as an output.  If *CONSTRAINT_P is not in
   an input as well as an output.  If *CONSTRAINT_P is not in
   canonical form, it will be made canonical.  (Note that `+' will be
   canonical form, it will be made canonical.  (Note that `+' will be
   replaced with `=' as part of this process.)
   replaced with `=' as part of this process.)
 
 
   Returns TRUE if all went well; FALSE if an error occurred.  */
   Returns TRUE if all went well; FALSE if an error occurred.  */
 
 
bool
bool
parse_output_constraint (const char **constraint_p, int operand_num,
parse_output_constraint (const char **constraint_p, int operand_num,
                         int ninputs, int noutputs, bool *allows_mem,
                         int ninputs, int noutputs, bool *allows_mem,
                         bool *allows_reg, bool *is_inout)
                         bool *allows_reg, bool *is_inout)
{
{
  const char *constraint = *constraint_p;
  const char *constraint = *constraint_p;
  const char *p;
  const char *p;
 
 
  /* Assume the constraint doesn't allow the use of either a register
  /* Assume the constraint doesn't allow the use of either a register
     or memory.  */
     or memory.  */
  *allows_mem = false;
  *allows_mem = false;
  *allows_reg = false;
  *allows_reg = false;
 
 
  /* Allow the `=' or `+' to not be at the beginning of the string,
  /* Allow the `=' or `+' to not be at the beginning of the string,
     since it wasn't explicitly documented that way, and there is a
     since it wasn't explicitly documented that way, and there is a
     large body of code that puts it last.  Swap the character to
     large body of code that puts it last.  Swap the character to
     the front, so as not to uglify any place else.  */
     the front, so as not to uglify any place else.  */
  p = strchr (constraint, '=');
  p = strchr (constraint, '=');
  if (!p)
  if (!p)
    p = strchr (constraint, '+');
    p = strchr (constraint, '+');
 
 
  /* If the string doesn't contain an `=', issue an error
  /* If the string doesn't contain an `=', issue an error
     message.  */
     message.  */
  if (!p)
  if (!p)
    {
    {
      error ("output operand constraint lacks %<=%>");
      error ("output operand constraint lacks %<=%>");
      return false;
      return false;
    }
    }
 
 
  /* If the constraint begins with `+', then the operand is both read
  /* If the constraint begins with `+', then the operand is both read
     from and written to.  */
     from and written to.  */
  *is_inout = (*p == '+');
  *is_inout = (*p == '+');
 
 
  /* Canonicalize the output constraint so that it begins with `='.  */
  /* Canonicalize the output constraint so that it begins with `='.  */
  if (p != constraint || *is_inout)
  if (p != constraint || *is_inout)
    {
    {
      char *buf;
      char *buf;
      size_t c_len = strlen (constraint);
      size_t c_len = strlen (constraint);
 
 
      if (p != constraint)
      if (p != constraint)
        warning (0, "output constraint %qc for operand %d "
        warning (0, "output constraint %qc for operand %d "
                 "is not at the beginning",
                 "is not at the beginning",
                 *p, operand_num);
                 *p, operand_num);
 
 
      /* Make a copy of the constraint.  */
      /* Make a copy of the constraint.  */
      buf = XALLOCAVEC (char, c_len + 1);
      buf = XALLOCAVEC (char, c_len + 1);
      strcpy (buf, constraint);
      strcpy (buf, constraint);
      /* Swap the first character and the `=' or `+'.  */
      /* Swap the first character and the `=' or `+'.  */
      buf[p - constraint] = buf[0];
      buf[p - constraint] = buf[0];
      /* Make sure the first character is an `='.  (Until we do this,
      /* Make sure the first character is an `='.  (Until we do this,
         it might be a `+'.)  */
         it might be a `+'.)  */
      buf[0] = '=';
      buf[0] = '=';
      /* Replace the constraint with the canonicalized string.  */
      /* Replace the constraint with the canonicalized string.  */
      *constraint_p = ggc_alloc_string (buf, c_len);
      *constraint_p = ggc_alloc_string (buf, c_len);
      constraint = *constraint_p;
      constraint = *constraint_p;
    }
    }
 
 
  /* Loop through the constraint string.  */
  /* Loop through the constraint string.  */
  for (p = constraint + 1; *p; p += CONSTRAINT_LEN (*p, p))
  for (p = constraint + 1; *p; p += CONSTRAINT_LEN (*p, p))
    switch (*p)
    switch (*p)
      {
      {
      case '+':
      case '+':
      case '=':
      case '=':
        error ("operand constraint contains incorrectly positioned "
        error ("operand constraint contains incorrectly positioned "
               "%<+%> or %<=%>");
               "%<+%> or %<=%>");
        return false;
        return false;
 
 
      case '%':
      case '%':
        if (operand_num + 1 == ninputs + noutputs)
        if (operand_num + 1 == ninputs + noutputs)
          {
          {
            error ("%<%%%> constraint used with last operand");
            error ("%<%%%> constraint used with last operand");
            return false;
            return false;
          }
          }
        break;
        break;
 
 
      case 'V':  case TARGET_MEM_CONSTRAINT:  case 'o':
      case 'V':  case TARGET_MEM_CONSTRAINT:  case 'o':
        *allows_mem = true;
        *allows_mem = true;
        break;
        break;
 
 
      case '?':  case '!':  case '*':  case '&':  case '#':
      case '?':  case '!':  case '*':  case '&':  case '#':
      case 'E':  case 'F':  case 'G':  case 'H':
      case 'E':  case 'F':  case 'G':  case 'H':
      case 's':  case 'i':  case 'n':
      case 's':  case 'i':  case 'n':
      case 'I':  case 'J':  case 'K':  case 'L':  case 'M':
      case 'I':  case 'J':  case 'K':  case 'L':  case 'M':
      case 'N':  case 'O':  case 'P':  case ',':
      case 'N':  case 'O':  case 'P':  case ',':
        break;
        break;
 
 
      case '0':  case '1':  case '2':  case '3':  case '4':
      case '0':  case '1':  case '2':  case '3':  case '4':
      case '5':  case '6':  case '7':  case '8':  case '9':
      case '5':  case '6':  case '7':  case '8':  case '9':
      case '[':
      case '[':
        error ("matching constraint not valid in output operand");
        error ("matching constraint not valid in output operand");
        return false;
        return false;
 
 
      case '<':  case '>':
      case '<':  case '>':
        /* ??? Before flow, auto inc/dec insns are not supposed to exist,
        /* ??? Before flow, auto inc/dec insns are not supposed to exist,
           excepting those that expand_call created.  So match memory
           excepting those that expand_call created.  So match memory
           and hope.  */
           and hope.  */
        *allows_mem = true;
        *allows_mem = true;
        break;
        break;
 
 
      case 'g':  case 'X':
      case 'g':  case 'X':
        *allows_reg = true;
        *allows_reg = true;
        *allows_mem = true;
        *allows_mem = true;
        break;
        break;
 
 
      case 'p': case 'r':
      case 'p': case 'r':
        *allows_reg = true;
        *allows_reg = true;
        break;
        break;
 
 
      default:
      default:
        if (!ISALPHA (*p))
        if (!ISALPHA (*p))
          break;
          break;
        if (REG_CLASS_FROM_CONSTRAINT (*p, p) != NO_REGS)
        if (REG_CLASS_FROM_CONSTRAINT (*p, p) != NO_REGS)
          *allows_reg = true;
          *allows_reg = true;
#ifdef EXTRA_CONSTRAINT_STR
#ifdef EXTRA_CONSTRAINT_STR
        else if (EXTRA_ADDRESS_CONSTRAINT (*p, p))
        else if (EXTRA_ADDRESS_CONSTRAINT (*p, p))
          *allows_reg = true;
          *allows_reg = true;
        else if (EXTRA_MEMORY_CONSTRAINT (*p, p))
        else if (EXTRA_MEMORY_CONSTRAINT (*p, p))
          *allows_mem = true;
          *allows_mem = true;
        else
        else
          {
          {
            /* Otherwise we can't assume anything about the nature of
            /* Otherwise we can't assume anything about the nature of
               the constraint except that it isn't purely registers.
               the constraint except that it isn't purely registers.
               Treat it like "g" and hope for the best.  */
               Treat it like "g" and hope for the best.  */
            *allows_reg = true;
            *allows_reg = true;
            *allows_mem = true;
            *allows_mem = true;
          }
          }
#endif
#endif
        break;
        break;
      }
      }
 
 
  return true;
  return true;
}
}
 
 
/* Similar, but for input constraints.  */
/* Similar, but for input constraints.  */
 
 
bool
bool
parse_input_constraint (const char **constraint_p, int input_num,
parse_input_constraint (const char **constraint_p, int input_num,
                        int ninputs, int noutputs, int ninout,
                        int ninputs, int noutputs, int ninout,
                        const char * const * constraints,
                        const char * const * constraints,
                        bool *allows_mem, bool *allows_reg)
                        bool *allows_mem, bool *allows_reg)
{
{
  const char *constraint = *constraint_p;
  const char *constraint = *constraint_p;
  const char *orig_constraint = constraint;
  const char *orig_constraint = constraint;
  size_t c_len = strlen (constraint);
  size_t c_len = strlen (constraint);
  size_t j;
  size_t j;
  bool saw_match = false;
  bool saw_match = false;
 
 
  /* Assume the constraint doesn't allow the use of either
  /* Assume the constraint doesn't allow the use of either
     a register or memory.  */
     a register or memory.  */
  *allows_mem = false;
  *allows_mem = false;
  *allows_reg = false;
  *allows_reg = false;
 
 
  /* Make sure constraint has neither `=', `+', nor '&'.  */
  /* Make sure constraint has neither `=', `+', nor '&'.  */
 
 
  for (j = 0; j < c_len; j += CONSTRAINT_LEN (constraint[j], constraint+j))
  for (j = 0; j < c_len; j += CONSTRAINT_LEN (constraint[j], constraint+j))
    switch (constraint[j])
    switch (constraint[j])
      {
      {
      case '+':  case '=':  case '&':
      case '+':  case '=':  case '&':
        if (constraint == orig_constraint)
        if (constraint == orig_constraint)
          {
          {
            error ("input operand constraint contains %qc", constraint[j]);
            error ("input operand constraint contains %qc", constraint[j]);
            return false;
            return false;
          }
          }
        break;
        break;
 
 
      case '%':
      case '%':
        if (constraint == orig_constraint
        if (constraint == orig_constraint
            && input_num + 1 == ninputs - ninout)
            && input_num + 1 == ninputs - ninout)
          {
          {
            error ("%<%%%> constraint used with last operand");
            error ("%<%%%> constraint used with last operand");
            return false;
            return false;
          }
          }
        break;
        break;
 
 
      case 'V':  case TARGET_MEM_CONSTRAINT:  case 'o':
      case 'V':  case TARGET_MEM_CONSTRAINT:  case 'o':
        *allows_mem = true;
        *allows_mem = true;
        break;
        break;
 
 
      case '<':  case '>':
      case '<':  case '>':
      case '?':  case '!':  case '*':  case '#':
      case '?':  case '!':  case '*':  case '#':
      case 'E':  case 'F':  case 'G':  case 'H':
      case 'E':  case 'F':  case 'G':  case 'H':
      case 's':  case 'i':  case 'n':
      case 's':  case 'i':  case 'n':
      case 'I':  case 'J':  case 'K':  case 'L':  case 'M':
      case 'I':  case 'J':  case 'K':  case 'L':  case 'M':
      case 'N':  case 'O':  case 'P':  case ',':
      case 'N':  case 'O':  case 'P':  case ',':
        break;
        break;
 
 
        /* Whether or not a numeric constraint allows a register is
        /* Whether or not a numeric constraint allows a register is
           decided by the matching constraint, and so there is no need
           decided by the matching constraint, and so there is no need
           to do anything special with them.  We must handle them in
           to do anything special with them.  We must handle them in
           the default case, so that we don't unnecessarily force
           the default case, so that we don't unnecessarily force
           operands to memory.  */
           operands to memory.  */
      case '0':  case '1':  case '2':  case '3':  case '4':
      case '0':  case '1':  case '2':  case '3':  case '4':
      case '5':  case '6':  case '7':  case '8':  case '9':
      case '5':  case '6':  case '7':  case '8':  case '9':
        {
        {
          char *end;
          char *end;
          unsigned long match;
          unsigned long match;
 
 
          saw_match = true;
          saw_match = true;
 
 
          match = strtoul (constraint + j, &end, 10);
          match = strtoul (constraint + j, &end, 10);
          if (match >= (unsigned long) noutputs)
          if (match >= (unsigned long) noutputs)
            {
            {
              error ("matching constraint references invalid operand number");
              error ("matching constraint references invalid operand number");
              return false;
              return false;
            }
            }
 
 
          /* Try and find the real constraint for this dup.  Only do this
          /* Try and find the real constraint for this dup.  Only do this
             if the matching constraint is the only alternative.  */
             if the matching constraint is the only alternative.  */
          if (*end == '\0'
          if (*end == '\0'
              && (j == 0 || (j == 1 && constraint[0] == '%')))
              && (j == 0 || (j == 1 && constraint[0] == '%')))
            {
            {
              constraint = constraints[match];
              constraint = constraints[match];
              *constraint_p = constraint;
              *constraint_p = constraint;
              c_len = strlen (constraint);
              c_len = strlen (constraint);
              j = 0;
              j = 0;
              /* ??? At the end of the loop, we will skip the first part of
              /* ??? At the end of the loop, we will skip the first part of
                 the matched constraint.  This assumes not only that the
                 the matched constraint.  This assumes not only that the
                 other constraint is an output constraint, but also that
                 other constraint is an output constraint, but also that
                 the '=' or '+' come first.  */
                 the '=' or '+' come first.  */
              break;
              break;
            }
            }
          else
          else
            j = end - constraint;
            j = end - constraint;
          /* Anticipate increment at end of loop.  */
          /* Anticipate increment at end of loop.  */
          j--;
          j--;
        }
        }
        /* Fall through.  */
        /* Fall through.  */
 
 
      case 'p':  case 'r':
      case 'p':  case 'r':
        *allows_reg = true;
        *allows_reg = true;
        break;
        break;
 
 
      case 'g':  case 'X':
      case 'g':  case 'X':
        *allows_reg = true;
        *allows_reg = true;
        *allows_mem = true;
        *allows_mem = true;
        break;
        break;
 
 
      default:
      default:
        if (! ISALPHA (constraint[j]))
        if (! ISALPHA (constraint[j]))
          {
          {
            error ("invalid punctuation %qc in constraint", constraint[j]);
            error ("invalid punctuation %qc in constraint", constraint[j]);
            return false;
            return false;
          }
          }
        if (REG_CLASS_FROM_CONSTRAINT (constraint[j], constraint + j)
        if (REG_CLASS_FROM_CONSTRAINT (constraint[j], constraint + j)
            != NO_REGS)
            != NO_REGS)
          *allows_reg = true;
          *allows_reg = true;
#ifdef EXTRA_CONSTRAINT_STR
#ifdef EXTRA_CONSTRAINT_STR
        else if (EXTRA_ADDRESS_CONSTRAINT (constraint[j], constraint + j))
        else if (EXTRA_ADDRESS_CONSTRAINT (constraint[j], constraint + j))
          *allows_reg = true;
          *allows_reg = true;
        else if (EXTRA_MEMORY_CONSTRAINT (constraint[j], constraint + j))
        else if (EXTRA_MEMORY_CONSTRAINT (constraint[j], constraint + j))
          *allows_mem = true;
          *allows_mem = true;
        else
        else
          {
          {
            /* Otherwise we can't assume anything about the nature of
            /* Otherwise we can't assume anything about the nature of
               the constraint except that it isn't purely registers.
               the constraint except that it isn't purely registers.
               Treat it like "g" and hope for the best.  */
               Treat it like "g" and hope for the best.  */
            *allows_reg = true;
            *allows_reg = true;
            *allows_mem = true;
            *allows_mem = true;
          }
          }
#endif
#endif
        break;
        break;
      }
      }
 
 
  if (saw_match && !*allows_reg)
  if (saw_match && !*allows_reg)
    warning (0, "matching constraint does not allow a register");
    warning (0, "matching constraint does not allow a register");
 
 
  return true;
  return true;
}
}
 
 
/* Return DECL iff there's an overlap between *REGS and DECL, where DECL
/* Return DECL iff there's an overlap between *REGS and DECL, where DECL
   can be an asm-declared register.  Called via walk_tree.  */
   can be an asm-declared register.  Called via walk_tree.  */
 
 
static tree
static tree
decl_overlaps_hard_reg_set_p (tree *declp, int *walk_subtrees ATTRIBUTE_UNUSED,
decl_overlaps_hard_reg_set_p (tree *declp, int *walk_subtrees ATTRIBUTE_UNUSED,
                              void *data)
                              void *data)
{
{
  tree decl = *declp;
  tree decl = *declp;
  const HARD_REG_SET *const regs = (const HARD_REG_SET *) data;
  const HARD_REG_SET *const regs = (const HARD_REG_SET *) data;
 
 
  if (TREE_CODE (decl) == VAR_DECL)
  if (TREE_CODE (decl) == VAR_DECL)
    {
    {
      if (DECL_HARD_REGISTER (decl)
      if (DECL_HARD_REGISTER (decl)
          && REG_P (DECL_RTL (decl))
          && REG_P (DECL_RTL (decl))
          && REGNO (DECL_RTL (decl)) < FIRST_PSEUDO_REGISTER)
          && REGNO (DECL_RTL (decl)) < FIRST_PSEUDO_REGISTER)
        {
        {
          rtx reg = DECL_RTL (decl);
          rtx reg = DECL_RTL (decl);
 
 
          if (overlaps_hard_reg_set_p (*regs, GET_MODE (reg), REGNO (reg)))
          if (overlaps_hard_reg_set_p (*regs, GET_MODE (reg), REGNO (reg)))
            return decl;
            return decl;
        }
        }
      walk_subtrees = 0;
      walk_subtrees = 0;
    }
    }
  else if (TYPE_P (decl) || TREE_CODE (decl) == PARM_DECL)
  else if (TYPE_P (decl) || TREE_CODE (decl) == PARM_DECL)
    walk_subtrees = 0;
    walk_subtrees = 0;
  return NULL_TREE;
  return NULL_TREE;
}
}
 
 
/* If there is an overlap between *REGS and DECL, return the first overlap
/* If there is an overlap between *REGS and DECL, return the first overlap
   found.  */
   found.  */
tree
tree
tree_overlaps_hard_reg_set (tree decl, HARD_REG_SET *regs)
tree_overlaps_hard_reg_set (tree decl, HARD_REG_SET *regs)
{
{
  return walk_tree (&decl, decl_overlaps_hard_reg_set_p, regs, NULL);
  return walk_tree (&decl, decl_overlaps_hard_reg_set_p, regs, NULL);
}
}
 
 
/* Check for overlap between registers marked in CLOBBERED_REGS and
/* Check for overlap between registers marked in CLOBBERED_REGS and
   anything inappropriate in T.  Emit error and return the register
   anything inappropriate in T.  Emit error and return the register
   variable definition for error, NULL_TREE for ok.  */
   variable definition for error, NULL_TREE for ok.  */
 
 
static bool
static bool
tree_conflicts_with_clobbers_p (tree t, HARD_REG_SET *clobbered_regs)
tree_conflicts_with_clobbers_p (tree t, HARD_REG_SET *clobbered_regs)
{
{
  /* Conflicts between asm-declared register variables and the clobber
  /* Conflicts between asm-declared register variables and the clobber
     list are not allowed.  */
     list are not allowed.  */
  tree overlap = tree_overlaps_hard_reg_set (t, clobbered_regs);
  tree overlap = tree_overlaps_hard_reg_set (t, clobbered_regs);
 
 
  if (overlap)
  if (overlap)
    {
    {
      error ("asm-specifier for variable %qE conflicts with asm clobber list",
      error ("asm-specifier for variable %qE conflicts with asm clobber list",
             DECL_NAME (overlap));
             DECL_NAME (overlap));
 
 
      /* Reset registerness to stop multiple errors emitted for a single
      /* Reset registerness to stop multiple errors emitted for a single
         variable.  */
         variable.  */
      DECL_REGISTER (overlap) = 0;
      DECL_REGISTER (overlap) = 0;
      return true;
      return true;
    }
    }
 
 
  return false;
  return false;
}
}
 
 
/* Generate RTL for an asm statement with arguments.
/* Generate RTL for an asm statement with arguments.
   STRING is the instruction template.
   STRING is the instruction template.
   OUTPUTS is a list of output arguments (lvalues); INPUTS a list of inputs.
   OUTPUTS is a list of output arguments (lvalues); INPUTS a list of inputs.
   Each output or input has an expression in the TREE_VALUE and
   Each output or input has an expression in the TREE_VALUE and
   a tree list in TREE_PURPOSE which in turn contains a constraint
   a tree list in TREE_PURPOSE which in turn contains a constraint
   name in TREE_VALUE (or NULL_TREE) and a constraint string
   name in TREE_VALUE (or NULL_TREE) and a constraint string
   in TREE_PURPOSE.
   in TREE_PURPOSE.
   CLOBBERS is a list of STRING_CST nodes each naming a hard register
   CLOBBERS is a list of STRING_CST nodes each naming a hard register
   that is clobbered by this insn.
   that is clobbered by this insn.
 
 
   Not all kinds of lvalue that may appear in OUTPUTS can be stored directly.
   Not all kinds of lvalue that may appear in OUTPUTS can be stored directly.
   Some elements of OUTPUTS may be replaced with trees representing temporary
   Some elements of OUTPUTS may be replaced with trees representing temporary
   values.  The caller should copy those temporary values to the originally
   values.  The caller should copy those temporary values to the originally
   specified lvalues.
   specified lvalues.
 
 
   VOL nonzero means the insn is volatile; don't optimize it.  */
   VOL nonzero means the insn is volatile; don't optimize it.  */
 
 
static void
static void
expand_asm_operands (tree string, tree outputs, tree inputs,
expand_asm_operands (tree string, tree outputs, tree inputs,
                     tree clobbers, tree labels, int vol, location_t locus)
                     tree clobbers, tree labels, int vol, location_t locus)
{
{
  rtvec argvec, constraintvec, labelvec;
  rtvec argvec, constraintvec, labelvec;
  rtx body;
  rtx body;
  int ninputs = list_length (inputs);
  int ninputs = list_length (inputs);
  int noutputs = list_length (outputs);
  int noutputs = list_length (outputs);
  int nlabels = list_length (labels);
  int nlabels = list_length (labels);
  int ninout;
  int ninout;
  int nclobbers;
  int nclobbers;
  HARD_REG_SET clobbered_regs;
  HARD_REG_SET clobbered_regs;
  int clobber_conflict_found = 0;
  int clobber_conflict_found = 0;
  tree tail;
  tree tail;
  tree t;
  tree t;
  int i;
  int i;
  /* Vector of RTX's of evaluated output operands.  */
  /* Vector of RTX's of evaluated output operands.  */
  rtx *output_rtx = XALLOCAVEC (rtx, noutputs);
  rtx *output_rtx = XALLOCAVEC (rtx, noutputs);
  int *inout_opnum = XALLOCAVEC (int, noutputs);
  int *inout_opnum = XALLOCAVEC (int, noutputs);
  rtx *real_output_rtx = XALLOCAVEC (rtx, noutputs);
  rtx *real_output_rtx = XALLOCAVEC (rtx, noutputs);
  enum machine_mode *inout_mode = XALLOCAVEC (enum machine_mode, noutputs);
  enum machine_mode *inout_mode = XALLOCAVEC (enum machine_mode, noutputs);
  const char **constraints = XALLOCAVEC (const char *, noutputs + ninputs);
  const char **constraints = XALLOCAVEC (const char *, noutputs + ninputs);
  int old_generating_concat_p = generating_concat_p;
  int old_generating_concat_p = generating_concat_p;
 
 
  /* An ASM with no outputs needs to be treated as volatile, for now.  */
  /* An ASM with no outputs needs to be treated as volatile, for now.  */
  if (noutputs == 0)
  if (noutputs == 0)
    vol = 1;
    vol = 1;
 
 
  if (! check_operand_nalternatives (outputs, inputs))
  if (! check_operand_nalternatives (outputs, inputs))
    return;
    return;
 
 
  string = resolve_asm_operand_names (string, outputs, inputs, labels);
  string = resolve_asm_operand_names (string, outputs, inputs, labels);
 
 
  /* Collect constraints.  */
  /* Collect constraints.  */
  i = 0;
  i = 0;
  for (t = outputs; t ; t = TREE_CHAIN (t), i++)
  for (t = outputs; t ; t = TREE_CHAIN (t), i++)
    constraints[i] = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (t)));
    constraints[i] = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (t)));
  for (t = inputs; t ; t = TREE_CHAIN (t), i++)
  for (t = inputs; t ; t = TREE_CHAIN (t), i++)
    constraints[i] = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (t)));
    constraints[i] = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (t)));
 
 
  /* Sometimes we wish to automatically clobber registers across an asm.
  /* Sometimes we wish to automatically clobber registers across an asm.
     Case in point is when the i386 backend moved from cc0 to a hard reg --
     Case in point is when the i386 backend moved from cc0 to a hard reg --
     maintaining source-level compatibility means automatically clobbering
     maintaining source-level compatibility means automatically clobbering
     the flags register.  */
     the flags register.  */
  clobbers = targetm.md_asm_clobbers (outputs, inputs, clobbers);
  clobbers = targetm.md_asm_clobbers (outputs, inputs, clobbers);
 
 
  /* Count the number of meaningful clobbered registers, ignoring what
  /* Count the number of meaningful clobbered registers, ignoring what
     we would ignore later.  */
     we would ignore later.  */
  nclobbers = 0;
  nclobbers = 0;
  CLEAR_HARD_REG_SET (clobbered_regs);
  CLEAR_HARD_REG_SET (clobbered_regs);
  for (tail = clobbers; tail; tail = TREE_CHAIN (tail))
  for (tail = clobbers; tail; tail = TREE_CHAIN (tail))
    {
    {
      const char *regname;
      const char *regname;
 
 
      if (TREE_VALUE (tail) == error_mark_node)
      if (TREE_VALUE (tail) == error_mark_node)
        return;
        return;
      regname = TREE_STRING_POINTER (TREE_VALUE (tail));
      regname = TREE_STRING_POINTER (TREE_VALUE (tail));
 
 
      i = decode_reg_name (regname);
      i = decode_reg_name (regname);
      if (i >= 0 || i == -4)
      if (i >= 0 || i == -4)
        ++nclobbers;
        ++nclobbers;
      else if (i == -2)
      else if (i == -2)
        error ("unknown register name %qs in %<asm%>", regname);
        error ("unknown register name %qs in %<asm%>", regname);
 
 
      /* Mark clobbered registers.  */
      /* Mark clobbered registers.  */
      if (i >= 0)
      if (i >= 0)
        {
        {
          /* Clobbering the PIC register is an error.  */
          /* Clobbering the PIC register is an error.  */
          if (i == (int) PIC_OFFSET_TABLE_REGNUM)
          if (i == (int) PIC_OFFSET_TABLE_REGNUM)
            {
            {
              error ("PIC register %qs clobbered in %<asm%>", regname);
              error ("PIC register %qs clobbered in %<asm%>", regname);
              return;
              return;
            }
            }
 
 
          SET_HARD_REG_BIT (clobbered_regs, i);
          SET_HARD_REG_BIT (clobbered_regs, i);
        }
        }
    }
    }
 
 
  /* First pass over inputs and outputs checks validity and sets
  /* First pass over inputs and outputs checks validity and sets
     mark_addressable if needed.  */
     mark_addressable if needed.  */
 
 
  ninout = 0;
  ninout = 0;
  for (i = 0, tail = outputs; tail; tail = TREE_CHAIN (tail), i++)
  for (i = 0, tail = outputs; tail; tail = TREE_CHAIN (tail), i++)
    {
    {
      tree val = TREE_VALUE (tail);
      tree val = TREE_VALUE (tail);
      tree type = TREE_TYPE (val);
      tree type = TREE_TYPE (val);
      const char *constraint;
      const char *constraint;
      bool is_inout;
      bool is_inout;
      bool allows_reg;
      bool allows_reg;
      bool allows_mem;
      bool allows_mem;
 
 
      /* If there's an erroneous arg, emit no insn.  */
      /* If there's an erroneous arg, emit no insn.  */
      if (type == error_mark_node)
      if (type == error_mark_node)
        return;
        return;
 
 
      /* Try to parse the output constraint.  If that fails, there's
      /* Try to parse the output constraint.  If that fails, there's
         no point in going further.  */
         no point in going further.  */
      constraint = constraints[i];
      constraint = constraints[i];
      if (!parse_output_constraint (&constraint, i, ninputs, noutputs,
      if (!parse_output_constraint (&constraint, i, ninputs, noutputs,
                                    &allows_mem, &allows_reg, &is_inout))
                                    &allows_mem, &allows_reg, &is_inout))
        return;
        return;
 
 
      if (! allows_reg
      if (! allows_reg
          && (allows_mem
          && (allows_mem
              || is_inout
              || is_inout
              || (DECL_P (val)
              || (DECL_P (val)
                  && REG_P (DECL_RTL (val))
                  && REG_P (DECL_RTL (val))
                  && GET_MODE (DECL_RTL (val)) != TYPE_MODE (type))))
                  && GET_MODE (DECL_RTL (val)) != TYPE_MODE (type))))
        mark_addressable (val);
        mark_addressable (val);
 
 
      if (is_inout)
      if (is_inout)
        ninout++;
        ninout++;
    }
    }
 
 
  ninputs += ninout;
  ninputs += ninout;
  if (ninputs + noutputs > MAX_RECOG_OPERANDS)
  if (ninputs + noutputs > MAX_RECOG_OPERANDS)
    {
    {
      error ("more than %d operands in %<asm%>", MAX_RECOG_OPERANDS);
      error ("more than %d operands in %<asm%>", MAX_RECOG_OPERANDS);
      return;
      return;
    }
    }
 
 
  for (i = 0, tail = inputs; tail; i++, tail = TREE_CHAIN (tail))
  for (i = 0, tail = inputs; tail; i++, tail = TREE_CHAIN (tail))
    {
    {
      bool allows_reg, allows_mem;
      bool allows_reg, allows_mem;
      const char *constraint;
      const char *constraint;
 
 
      /* If there's an erroneous arg, emit no insn, because the ASM_INPUT
      /* If there's an erroneous arg, emit no insn, because the ASM_INPUT
         would get VOIDmode and that could cause a crash in reload.  */
         would get VOIDmode and that could cause a crash in reload.  */
      if (TREE_TYPE (TREE_VALUE (tail)) == error_mark_node)
      if (TREE_TYPE (TREE_VALUE (tail)) == error_mark_node)
        return;
        return;
 
 
      constraint = constraints[i + noutputs];
      constraint = constraints[i + noutputs];
      if (! parse_input_constraint (&constraint, i, ninputs, noutputs, ninout,
      if (! parse_input_constraint (&constraint, i, ninputs, noutputs, ninout,
                                    constraints, &allows_mem, &allows_reg))
                                    constraints, &allows_mem, &allows_reg))
        return;
        return;
 
 
      if (! allows_reg && allows_mem)
      if (! allows_reg && allows_mem)
        mark_addressable (TREE_VALUE (tail));
        mark_addressable (TREE_VALUE (tail));
    }
    }
 
 
  /* Second pass evaluates arguments.  */
  /* Second pass evaluates arguments.  */
 
 
  ninout = 0;
  ninout = 0;
  for (i = 0, tail = outputs; tail; tail = TREE_CHAIN (tail), i++)
  for (i = 0, tail = outputs; tail; tail = TREE_CHAIN (tail), i++)
    {
    {
      tree val = TREE_VALUE (tail);
      tree val = TREE_VALUE (tail);
      tree type = TREE_TYPE (val);
      tree type = TREE_TYPE (val);
      bool is_inout;
      bool is_inout;
      bool allows_reg;
      bool allows_reg;
      bool allows_mem;
      bool allows_mem;
      rtx op;
      rtx op;
      bool ok;
      bool ok;
 
 
      ok = parse_output_constraint (&constraints[i], i, ninputs,
      ok = parse_output_constraint (&constraints[i], i, ninputs,
                                    noutputs, &allows_mem, &allows_reg,
                                    noutputs, &allows_mem, &allows_reg,
                                    &is_inout);
                                    &is_inout);
      gcc_assert (ok);
      gcc_assert (ok);
 
 
      /* If an output operand is not a decl or indirect ref and our constraint
      /* If an output operand is not a decl or indirect ref and our constraint
         allows a register, make a temporary to act as an intermediate.
         allows a register, make a temporary to act as an intermediate.
         Make the asm insn write into that, then our caller will copy it to
         Make the asm insn write into that, then our caller will copy it to
         the real output operand.  Likewise for promoted variables.  */
         the real output operand.  Likewise for promoted variables.  */
 
 
      generating_concat_p = 0;
      generating_concat_p = 0;
 
 
      real_output_rtx[i] = NULL_RTX;
      real_output_rtx[i] = NULL_RTX;
      if ((TREE_CODE (val) == INDIRECT_REF
      if ((TREE_CODE (val) == INDIRECT_REF
           && allows_mem)
           && allows_mem)
          || (DECL_P (val)
          || (DECL_P (val)
              && (allows_mem || REG_P (DECL_RTL (val)))
              && (allows_mem || REG_P (DECL_RTL (val)))
              && ! (REG_P (DECL_RTL (val))
              && ! (REG_P (DECL_RTL (val))
                    && GET_MODE (DECL_RTL (val)) != TYPE_MODE (type)))
                    && GET_MODE (DECL_RTL (val)) != TYPE_MODE (type)))
          || ! allows_reg
          || ! allows_reg
          || is_inout)
          || is_inout)
        {
        {
          op = expand_expr (val, NULL_RTX, VOIDmode, EXPAND_WRITE);
          op = expand_expr (val, NULL_RTX, VOIDmode, EXPAND_WRITE);
          if (MEM_P (op))
          if (MEM_P (op))
            op = validize_mem (op);
            op = validize_mem (op);
 
 
          if (! allows_reg && !MEM_P (op))
          if (! allows_reg && !MEM_P (op))
            error ("output number %d not directly addressable", i);
            error ("output number %d not directly addressable", i);
          if ((! allows_mem && MEM_P (op))
          if ((! allows_mem && MEM_P (op))
              || GET_CODE (op) == CONCAT)
              || GET_CODE (op) == CONCAT)
            {
            {
              real_output_rtx[i] = op;
              real_output_rtx[i] = op;
              op = gen_reg_rtx (GET_MODE (op));
              op = gen_reg_rtx (GET_MODE (op));
              if (is_inout)
              if (is_inout)
                emit_move_insn (op, real_output_rtx[i]);
                emit_move_insn (op, real_output_rtx[i]);
            }
            }
        }
        }
      else
      else
        {
        {
          op = assign_temp (type, 0, 0, 1);
          op = assign_temp (type, 0, 0, 1);
          op = validize_mem (op);
          op = validize_mem (op);
          if (!MEM_P (op) && TREE_CODE (TREE_VALUE (tail)) == SSA_NAME)
          if (!MEM_P (op) && TREE_CODE (TREE_VALUE (tail)) == SSA_NAME)
            set_reg_attrs_for_decl_rtl (SSA_NAME_VAR (TREE_VALUE (tail)), op);
            set_reg_attrs_for_decl_rtl (SSA_NAME_VAR (TREE_VALUE (tail)), op);
          TREE_VALUE (tail) = make_tree (type, op);
          TREE_VALUE (tail) = make_tree (type, op);
        }
        }
      output_rtx[i] = op;
      output_rtx[i] = op;
 
 
      generating_concat_p = old_generating_concat_p;
      generating_concat_p = old_generating_concat_p;
 
 
      if (is_inout)
      if (is_inout)
        {
        {
          inout_mode[ninout] = TYPE_MODE (type);
          inout_mode[ninout] = TYPE_MODE (type);
          inout_opnum[ninout++] = i;
          inout_opnum[ninout++] = i;
        }
        }
 
 
      if (tree_conflicts_with_clobbers_p (val, &clobbered_regs))
      if (tree_conflicts_with_clobbers_p (val, &clobbered_regs))
        clobber_conflict_found = 1;
        clobber_conflict_found = 1;
    }
    }
 
 
  /* Make vectors for the expression-rtx, constraint strings,
  /* Make vectors for the expression-rtx, constraint strings,
     and named operands.  */
     and named operands.  */
 
 
  argvec = rtvec_alloc (ninputs);
  argvec = rtvec_alloc (ninputs);
  constraintvec = rtvec_alloc (ninputs);
  constraintvec = rtvec_alloc (ninputs);
  labelvec = rtvec_alloc (nlabels);
  labelvec = rtvec_alloc (nlabels);
 
 
  body = gen_rtx_ASM_OPERANDS ((noutputs == 0 ? VOIDmode
  body = gen_rtx_ASM_OPERANDS ((noutputs == 0 ? VOIDmode
                                : GET_MODE (output_rtx[0])),
                                : GET_MODE (output_rtx[0])),
                               ggc_strdup (TREE_STRING_POINTER (string)),
                               ggc_strdup (TREE_STRING_POINTER (string)),
                               empty_string, 0, argvec, constraintvec,
                               empty_string, 0, argvec, constraintvec,
                               labelvec, locus);
                               labelvec, locus);
 
 
  MEM_VOLATILE_P (body) = vol;
  MEM_VOLATILE_P (body) = vol;
 
 
  /* Eval the inputs and put them into ARGVEC.
  /* Eval the inputs and put them into ARGVEC.
     Put their constraints into ASM_INPUTs and store in CONSTRAINTS.  */
     Put their constraints into ASM_INPUTs and store in CONSTRAINTS.  */
 
 
  for (i = 0, tail = inputs; tail; tail = TREE_CHAIN (tail), ++i)
  for (i = 0, tail = inputs; tail; tail = TREE_CHAIN (tail), ++i)
    {
    {
      bool allows_reg, allows_mem;
      bool allows_reg, allows_mem;
      const char *constraint;
      const char *constraint;
      tree val, type;
      tree val, type;
      rtx op;
      rtx op;
      bool ok;
      bool ok;
 
 
      constraint = constraints[i + noutputs];
      constraint = constraints[i + noutputs];
      ok = parse_input_constraint (&constraint, i, ninputs, noutputs, ninout,
      ok = parse_input_constraint (&constraint, i, ninputs, noutputs, ninout,
                                   constraints, &allows_mem, &allows_reg);
                                   constraints, &allows_mem, &allows_reg);
      gcc_assert (ok);
      gcc_assert (ok);
 
 
      generating_concat_p = 0;
      generating_concat_p = 0;
 
 
      val = TREE_VALUE (tail);
      val = TREE_VALUE (tail);
      type = TREE_TYPE (val);
      type = TREE_TYPE (val);
      /* EXPAND_INITIALIZER will not generate code for valid initializer
      /* EXPAND_INITIALIZER will not generate code for valid initializer
         constants, but will still generate code for other types of operand.
         constants, but will still generate code for other types of operand.
         This is the behavior we want for constant constraints.  */
         This is the behavior we want for constant constraints.  */
      op = expand_expr (val, NULL_RTX, VOIDmode,
      op = expand_expr (val, NULL_RTX, VOIDmode,
                        allows_reg ? EXPAND_NORMAL
                        allows_reg ? EXPAND_NORMAL
                        : allows_mem ? EXPAND_MEMORY
                        : allows_mem ? EXPAND_MEMORY
                        : EXPAND_INITIALIZER);
                        : EXPAND_INITIALIZER);
 
 
      /* Never pass a CONCAT to an ASM.  */
      /* Never pass a CONCAT to an ASM.  */
      if (GET_CODE (op) == CONCAT)
      if (GET_CODE (op) == CONCAT)
        op = force_reg (GET_MODE (op), op);
        op = force_reg (GET_MODE (op), op);
      else if (MEM_P (op))
      else if (MEM_P (op))
        op = validize_mem (op);
        op = validize_mem (op);
 
 
      if (asm_operand_ok (op, constraint, NULL) <= 0)
      if (asm_operand_ok (op, constraint, NULL) <= 0)
        {
        {
          if (allows_reg && TYPE_MODE (type) != BLKmode)
          if (allows_reg && TYPE_MODE (type) != BLKmode)
            op = force_reg (TYPE_MODE (type), op);
            op = force_reg (TYPE_MODE (type), op);
          else if (!allows_mem)
          else if (!allows_mem)
            warning (0, "asm operand %d probably doesn%'t match constraints",
            warning (0, "asm operand %d probably doesn%'t match constraints",
                     i + noutputs);
                     i + noutputs);
          else if (MEM_P (op))
          else if (MEM_P (op))
            {
            {
              /* We won't recognize either volatile memory or memory
              /* We won't recognize either volatile memory or memory
                 with a queued address as available a memory_operand
                 with a queued address as available a memory_operand
                 at this point.  Ignore it: clearly this *is* a memory.  */
                 at this point.  Ignore it: clearly this *is* a memory.  */
            }
            }
          else
          else
            {
            {
              warning (0, "use of memory input without lvalue in "
              warning (0, "use of memory input without lvalue in "
                       "asm operand %d is deprecated", i + noutputs);
                       "asm operand %d is deprecated", i + noutputs);
 
 
              if (CONSTANT_P (op))
              if (CONSTANT_P (op))
                {
                {
                  rtx mem = force_const_mem (TYPE_MODE (type), op);
                  rtx mem = force_const_mem (TYPE_MODE (type), op);
                  if (mem)
                  if (mem)
                    op = validize_mem (mem);
                    op = validize_mem (mem);
                  else
                  else
                    op = force_reg (TYPE_MODE (type), op);
                    op = force_reg (TYPE_MODE (type), op);
                }
                }
              if (REG_P (op)
              if (REG_P (op)
                  || GET_CODE (op) == SUBREG
                  || GET_CODE (op) == SUBREG
                  || GET_CODE (op) == CONCAT)
                  || GET_CODE (op) == CONCAT)
                {
                {
                  tree qual_type = build_qualified_type (type,
                  tree qual_type = build_qualified_type (type,
                                                         (TYPE_QUALS (type)
                                                         (TYPE_QUALS (type)
                                                          | TYPE_QUAL_CONST));
                                                          | TYPE_QUAL_CONST));
                  rtx memloc = assign_temp (qual_type, 1, 1, 1);
                  rtx memloc = assign_temp (qual_type, 1, 1, 1);
                  memloc = validize_mem (memloc);
                  memloc = validize_mem (memloc);
                  emit_move_insn (memloc, op);
                  emit_move_insn (memloc, op);
                  op = memloc;
                  op = memloc;
                }
                }
            }
            }
        }
        }
 
 
      generating_concat_p = old_generating_concat_p;
      generating_concat_p = old_generating_concat_p;
      ASM_OPERANDS_INPUT (body, i) = op;
      ASM_OPERANDS_INPUT (body, i) = op;
 
 
      ASM_OPERANDS_INPUT_CONSTRAINT_EXP (body, i)
      ASM_OPERANDS_INPUT_CONSTRAINT_EXP (body, i)
        = gen_rtx_ASM_INPUT (TYPE_MODE (type),
        = gen_rtx_ASM_INPUT (TYPE_MODE (type),
                             ggc_strdup (constraints[i + noutputs]));
                             ggc_strdup (constraints[i + noutputs]));
 
 
      if (tree_conflicts_with_clobbers_p (val, &clobbered_regs))
      if (tree_conflicts_with_clobbers_p (val, &clobbered_regs))
        clobber_conflict_found = 1;
        clobber_conflict_found = 1;
    }
    }
 
 
  /* Protect all the operands from the queue now that they have all been
  /* Protect all the operands from the queue now that they have all been
     evaluated.  */
     evaluated.  */
 
 
  generating_concat_p = 0;
  generating_concat_p = 0;
 
 
  /* For in-out operands, copy output rtx to input rtx.  */
  /* For in-out operands, copy output rtx to input rtx.  */
  for (i = 0; i < ninout; i++)
  for (i = 0; i < ninout; i++)
    {
    {
      int j = inout_opnum[i];
      int j = inout_opnum[i];
      char buffer[16];
      char buffer[16];
 
 
      ASM_OPERANDS_INPUT (body, ninputs - ninout + i)
      ASM_OPERANDS_INPUT (body, ninputs - ninout + i)
        = output_rtx[j];
        = output_rtx[j];
 
 
      sprintf (buffer, "%d", j);
      sprintf (buffer, "%d", j);
      ASM_OPERANDS_INPUT_CONSTRAINT_EXP (body, ninputs - ninout + i)
      ASM_OPERANDS_INPUT_CONSTRAINT_EXP (body, ninputs - ninout + i)
        = gen_rtx_ASM_INPUT (inout_mode[i], ggc_strdup (buffer));
        = gen_rtx_ASM_INPUT (inout_mode[i], ggc_strdup (buffer));
    }
    }
 
 
  /* Copy labels to the vector.  */
  /* Copy labels to the vector.  */
  for (i = 0, tail = labels; i < nlabels; ++i, tail = TREE_CHAIN (tail))
  for (i = 0, tail = labels; i < nlabels; ++i, tail = TREE_CHAIN (tail))
    ASM_OPERANDS_LABEL (body, i)
    ASM_OPERANDS_LABEL (body, i)
      = gen_rtx_LABEL_REF (Pmode, label_rtx (TREE_VALUE (tail)));
      = gen_rtx_LABEL_REF (Pmode, label_rtx (TREE_VALUE (tail)));
 
 
  generating_concat_p = old_generating_concat_p;
  generating_concat_p = old_generating_concat_p;
 
 
  /* Now, for each output, construct an rtx
  /* Now, for each output, construct an rtx
     (set OUTPUT (asm_operands INSN OUTPUTCONSTRAINT OUTPUTNUMBER
     (set OUTPUT (asm_operands INSN OUTPUTCONSTRAINT OUTPUTNUMBER
                               ARGVEC CONSTRAINTS OPNAMES))
                               ARGVEC CONSTRAINTS OPNAMES))
     If there is more than one, put them inside a PARALLEL.  */
     If there is more than one, put them inside a PARALLEL.  */
 
 
  if (nlabels > 0 && nclobbers == 0)
  if (nlabels > 0 && nclobbers == 0)
    {
    {
      gcc_assert (noutputs == 0);
      gcc_assert (noutputs == 0);
      emit_jump_insn (body);
      emit_jump_insn (body);
    }
    }
  else if (noutputs == 0 && nclobbers == 0)
  else if (noutputs == 0 && nclobbers == 0)
    {
    {
      /* No output operands: put in a raw ASM_OPERANDS rtx.  */
      /* No output operands: put in a raw ASM_OPERANDS rtx.  */
      emit_insn (body);
      emit_insn (body);
    }
    }
  else if (noutputs == 1 && nclobbers == 0)
  else if (noutputs == 1 && nclobbers == 0)
    {
    {
      ASM_OPERANDS_OUTPUT_CONSTRAINT (body) = ggc_strdup (constraints[0]);
      ASM_OPERANDS_OUTPUT_CONSTRAINT (body) = ggc_strdup (constraints[0]);
      emit_insn (gen_rtx_SET (VOIDmode, output_rtx[0], body));
      emit_insn (gen_rtx_SET (VOIDmode, output_rtx[0], body));
    }
    }
  else
  else
    {
    {
      rtx obody = body;
      rtx obody = body;
      int num = noutputs;
      int num = noutputs;
 
 
      if (num == 0)
      if (num == 0)
        num = 1;
        num = 1;
 
 
      body = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (num + nclobbers));
      body = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (num + nclobbers));
 
 
      /* For each output operand, store a SET.  */
      /* For each output operand, store a SET.  */
      for (i = 0, tail = outputs; tail; tail = TREE_CHAIN (tail), i++)
      for (i = 0, tail = outputs; tail; tail = TREE_CHAIN (tail), i++)
        {
        {
          XVECEXP (body, 0, i)
          XVECEXP (body, 0, i)
            = gen_rtx_SET (VOIDmode,
            = gen_rtx_SET (VOIDmode,
                           output_rtx[i],
                           output_rtx[i],
                           gen_rtx_ASM_OPERANDS
                           gen_rtx_ASM_OPERANDS
                           (GET_MODE (output_rtx[i]),
                           (GET_MODE (output_rtx[i]),
                            ggc_strdup (TREE_STRING_POINTER (string)),
                            ggc_strdup (TREE_STRING_POINTER (string)),
                            ggc_strdup (constraints[i]),
                            ggc_strdup (constraints[i]),
                            i, argvec, constraintvec, labelvec, locus));
                            i, argvec, constraintvec, labelvec, locus));
 
 
          MEM_VOLATILE_P (SET_SRC (XVECEXP (body, 0, i))) = vol;
          MEM_VOLATILE_P (SET_SRC (XVECEXP (body, 0, i))) = vol;
        }
        }
 
 
      /* If there are no outputs (but there are some clobbers)
      /* If there are no outputs (but there are some clobbers)
         store the bare ASM_OPERANDS into the PARALLEL.  */
         store the bare ASM_OPERANDS into the PARALLEL.  */
 
 
      if (i == 0)
      if (i == 0)
        XVECEXP (body, 0, i++) = obody;
        XVECEXP (body, 0, i++) = obody;
 
 
      /* Store (clobber REG) for each clobbered register specified.  */
      /* Store (clobber REG) for each clobbered register specified.  */
 
 
      for (tail = clobbers; tail; tail = TREE_CHAIN (tail))
      for (tail = clobbers; tail; tail = TREE_CHAIN (tail))
        {
        {
          const char *regname = TREE_STRING_POINTER (TREE_VALUE (tail));
          const char *regname = TREE_STRING_POINTER (TREE_VALUE (tail));
          int j = decode_reg_name (regname);
          int j = decode_reg_name (regname);
          rtx clobbered_reg;
          rtx clobbered_reg;
 
 
          if (j < 0)
          if (j < 0)
            {
            {
              if (j == -3)      /* `cc', which is not a register */
              if (j == -3)      /* `cc', which is not a register */
                continue;
                continue;
 
 
              if (j == -4)      /* `memory', don't cache memory across asm */
              if (j == -4)      /* `memory', don't cache memory across asm */
                {
                {
                  XVECEXP (body, 0, i++)
                  XVECEXP (body, 0, i++)
                    = gen_rtx_CLOBBER (VOIDmode,
                    = gen_rtx_CLOBBER (VOIDmode,
                                       gen_rtx_MEM
                                       gen_rtx_MEM
                                       (BLKmode,
                                       (BLKmode,
                                        gen_rtx_SCRATCH (VOIDmode)));
                                        gen_rtx_SCRATCH (VOIDmode)));
                  continue;
                  continue;
                }
                }
 
 
              /* Ignore unknown register, error already signaled.  */
              /* Ignore unknown register, error already signaled.  */
              continue;
              continue;
            }
            }
 
 
          /* Use QImode since that's guaranteed to clobber just one reg.  */
          /* Use QImode since that's guaranteed to clobber just one reg.  */
          clobbered_reg = gen_rtx_REG (QImode, j);
          clobbered_reg = gen_rtx_REG (QImode, j);
 
 
          /* Do sanity check for overlap between clobbers and respectively
          /* Do sanity check for overlap between clobbers and respectively
             input and outputs that hasn't been handled.  Such overlap
             input and outputs that hasn't been handled.  Such overlap
             should have been detected and reported above.  */
             should have been detected and reported above.  */
          if (!clobber_conflict_found)
          if (!clobber_conflict_found)
            {
            {
              int opno;
              int opno;
 
 
              /* We test the old body (obody) contents to avoid tripping
              /* We test the old body (obody) contents to avoid tripping
                 over the under-construction body.  */
                 over the under-construction body.  */
              for (opno = 0; opno < noutputs; opno++)
              for (opno = 0; opno < noutputs; opno++)
                if (reg_overlap_mentioned_p (clobbered_reg, output_rtx[opno]))
                if (reg_overlap_mentioned_p (clobbered_reg, output_rtx[opno]))
                  internal_error ("asm clobber conflict with output operand");
                  internal_error ("asm clobber conflict with output operand");
 
 
              for (opno = 0; opno < ninputs - ninout; opno++)
              for (opno = 0; opno < ninputs - ninout; opno++)
                if (reg_overlap_mentioned_p (clobbered_reg,
                if (reg_overlap_mentioned_p (clobbered_reg,
                                             ASM_OPERANDS_INPUT (obody, opno)))
                                             ASM_OPERANDS_INPUT (obody, opno)))
                  internal_error ("asm clobber conflict with input operand");
                  internal_error ("asm clobber conflict with input operand");
            }
            }
 
 
          XVECEXP (body, 0, i++)
          XVECEXP (body, 0, i++)
            = gen_rtx_CLOBBER (VOIDmode, clobbered_reg);
            = gen_rtx_CLOBBER (VOIDmode, clobbered_reg);
        }
        }
 
 
      if (nlabels > 0)
      if (nlabels > 0)
        emit_jump_insn (body);
        emit_jump_insn (body);
      else
      else
        emit_insn (body);
        emit_insn (body);
    }
    }
 
 
  /* For any outputs that needed reloading into registers, spill them
  /* For any outputs that needed reloading into registers, spill them
     back to where they belong.  */
     back to where they belong.  */
  for (i = 0; i < noutputs; ++i)
  for (i = 0; i < noutputs; ++i)
    if (real_output_rtx[i])
    if (real_output_rtx[i])
      emit_move_insn (real_output_rtx[i], output_rtx[i]);
      emit_move_insn (real_output_rtx[i], output_rtx[i]);
 
 
  crtl->has_asm_statement = 1;
  crtl->has_asm_statement = 1;
  free_temp_slots ();
  free_temp_slots ();
}
}
 
 
void
void
expand_asm_stmt (gimple stmt)
expand_asm_stmt (gimple stmt)
{
{
  int noutputs;
  int noutputs;
  tree outputs, tail, t;
  tree outputs, tail, t;
  tree *o;
  tree *o;
  size_t i, n;
  size_t i, n;
  const char *s;
  const char *s;
  tree str, out, in, cl, labels;
  tree str, out, in, cl, labels;
  location_t locus = gimple_location (stmt);
  location_t locus = gimple_location (stmt);
 
 
  /* Meh... convert the gimple asm operands into real tree lists.
  /* Meh... convert the gimple asm operands into real tree lists.
     Eventually we should make all routines work on the vectors instead
     Eventually we should make all routines work on the vectors instead
     of relying on TREE_CHAIN.  */
     of relying on TREE_CHAIN.  */
  out = NULL_TREE;
  out = NULL_TREE;
  n = gimple_asm_noutputs (stmt);
  n = gimple_asm_noutputs (stmt);
  if (n > 0)
  if (n > 0)
    {
    {
      t = out = gimple_asm_output_op (stmt, 0);
      t = out = gimple_asm_output_op (stmt, 0);
      for (i = 1; i < n; i++)
      for (i = 1; i < n; i++)
        t = TREE_CHAIN (t) = gimple_asm_output_op (stmt, i);
        t = TREE_CHAIN (t) = gimple_asm_output_op (stmt, i);
    }
    }
 
 
  in = NULL_TREE;
  in = NULL_TREE;
  n = gimple_asm_ninputs (stmt);
  n = gimple_asm_ninputs (stmt);
  if (n > 0)
  if (n > 0)
    {
    {
      t = in = gimple_asm_input_op (stmt, 0);
      t = in = gimple_asm_input_op (stmt, 0);
      for (i = 1; i < n; i++)
      for (i = 1; i < n; i++)
        t = TREE_CHAIN (t) = gimple_asm_input_op (stmt, i);
        t = TREE_CHAIN (t) = gimple_asm_input_op (stmt, i);
    }
    }
 
 
  cl = NULL_TREE;
  cl = NULL_TREE;
  n = gimple_asm_nclobbers (stmt);
  n = gimple_asm_nclobbers (stmt);
  if (n > 0)
  if (n > 0)
    {
    {
      t = cl = gimple_asm_clobber_op (stmt, 0);
      t = cl = gimple_asm_clobber_op (stmt, 0);
      for (i = 1; i < n; i++)
      for (i = 1; i < n; i++)
        t = TREE_CHAIN (t) = gimple_asm_clobber_op (stmt, i);
        t = TREE_CHAIN (t) = gimple_asm_clobber_op (stmt, i);
    }
    }
 
 
  labels = NULL_TREE;
  labels = NULL_TREE;
  n = gimple_asm_nlabels (stmt);
  n = gimple_asm_nlabels (stmt);
  if (n > 0)
  if (n > 0)
    {
    {
      t = labels = gimple_asm_label_op (stmt, 0);
      t = labels = gimple_asm_label_op (stmt, 0);
      for (i = 1; i < n; i++)
      for (i = 1; i < n; i++)
        t = TREE_CHAIN (t) = gimple_asm_label_op (stmt, i);
        t = TREE_CHAIN (t) = gimple_asm_label_op (stmt, i);
    }
    }
 
 
  s = gimple_asm_string (stmt);
  s = gimple_asm_string (stmt);
  str = build_string (strlen (s), s);
  str = build_string (strlen (s), s);
 
 
  if (gimple_asm_input_p (stmt))
  if (gimple_asm_input_p (stmt))
    {
    {
      expand_asm_loc (str, gimple_asm_volatile_p (stmt), locus);
      expand_asm_loc (str, gimple_asm_volatile_p (stmt), locus);
      return;
      return;
    }
    }
 
 
  outputs = out;
  outputs = out;
  noutputs = gimple_asm_noutputs (stmt);
  noutputs = gimple_asm_noutputs (stmt);
  /* o[I] is the place that output number I should be written.  */
  /* o[I] is the place that output number I should be written.  */
  o = (tree *) alloca (noutputs * sizeof (tree));
  o = (tree *) alloca (noutputs * sizeof (tree));
 
 
  /* Record the contents of OUTPUTS before it is modified.  */
  /* Record the contents of OUTPUTS before it is modified.  */
  for (i = 0, tail = outputs; tail; tail = TREE_CHAIN (tail), i++)
  for (i = 0, tail = outputs; tail; tail = TREE_CHAIN (tail), i++)
    o[i] = TREE_VALUE (tail);
    o[i] = TREE_VALUE (tail);
 
 
  /* Generate the ASM_OPERANDS insn; store into the TREE_VALUEs of
  /* Generate the ASM_OPERANDS insn; store into the TREE_VALUEs of
     OUTPUTS some trees for where the values were actually stored.  */
     OUTPUTS some trees for where the values were actually stored.  */
  expand_asm_operands (str, outputs, in, cl, labels,
  expand_asm_operands (str, outputs, in, cl, labels,
                       gimple_asm_volatile_p (stmt), locus);
                       gimple_asm_volatile_p (stmt), locus);
 
 
  /* Copy all the intermediate outputs into the specified outputs.  */
  /* Copy all the intermediate outputs into the specified outputs.  */
  for (i = 0, tail = outputs; tail; tail = TREE_CHAIN (tail), i++)
  for (i = 0, tail = outputs; tail; tail = TREE_CHAIN (tail), i++)
    {
    {
      if (o[i] != TREE_VALUE (tail))
      if (o[i] != TREE_VALUE (tail))
        {
        {
          expand_assignment (o[i], TREE_VALUE (tail), false);
          expand_assignment (o[i], TREE_VALUE (tail), false);
          free_temp_slots ();
          free_temp_slots ();
 
 
          /* Restore the original value so that it's correct the next
          /* Restore the original value so that it's correct the next
             time we expand this function.  */
             time we expand this function.  */
          TREE_VALUE (tail) = o[i];
          TREE_VALUE (tail) = o[i];
        }
        }
    }
    }
}
}
 
 
/* A subroutine of expand_asm_operands.  Check that all operands have
/* A subroutine of expand_asm_operands.  Check that all operands have
   the same number of alternatives.  Return true if so.  */
   the same number of alternatives.  Return true if so.  */
 
 
static bool
static bool
check_operand_nalternatives (tree outputs, tree inputs)
check_operand_nalternatives (tree outputs, tree inputs)
{
{
  if (outputs || inputs)
  if (outputs || inputs)
    {
    {
      tree tmp = TREE_PURPOSE (outputs ? outputs : inputs);
      tree tmp = TREE_PURPOSE (outputs ? outputs : inputs);
      int nalternatives
      int nalternatives
        = n_occurrences (',', TREE_STRING_POINTER (TREE_VALUE (tmp)));
        = n_occurrences (',', TREE_STRING_POINTER (TREE_VALUE (tmp)));
      tree next = inputs;
      tree next = inputs;
 
 
      if (nalternatives + 1 > MAX_RECOG_ALTERNATIVES)
      if (nalternatives + 1 > MAX_RECOG_ALTERNATIVES)
        {
        {
          error ("too many alternatives in %<asm%>");
          error ("too many alternatives in %<asm%>");
          return false;
          return false;
        }
        }
 
 
      tmp = outputs;
      tmp = outputs;
      while (tmp)
      while (tmp)
        {
        {
          const char *constraint
          const char *constraint
            = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (tmp)));
            = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (tmp)));
 
 
          if (n_occurrences (',', constraint) != nalternatives)
          if (n_occurrences (',', constraint) != nalternatives)
            {
            {
              error ("operand constraints for %<asm%> differ "
              error ("operand constraints for %<asm%> differ "
                     "in number of alternatives");
                     "in number of alternatives");
              return false;
              return false;
            }
            }
 
 
          if (TREE_CHAIN (tmp))
          if (TREE_CHAIN (tmp))
            tmp = TREE_CHAIN (tmp);
            tmp = TREE_CHAIN (tmp);
          else
          else
            tmp = next, next = 0;
            tmp = next, next = 0;
        }
        }
    }
    }
 
 
  return true;
  return true;
}
}
 
 
/* A subroutine of expand_asm_operands.  Check that all operand names
/* A subroutine of expand_asm_operands.  Check that all operand names
   are unique.  Return true if so.  We rely on the fact that these names
   are unique.  Return true if so.  We rely on the fact that these names
   are identifiers, and so have been canonicalized by get_identifier,
   are identifiers, and so have been canonicalized by get_identifier,
   so all we need are pointer comparisons.  */
   so all we need are pointer comparisons.  */
 
 
static bool
static bool
check_unique_operand_names (tree outputs, tree inputs, tree labels)
check_unique_operand_names (tree outputs, tree inputs, tree labels)
{
{
  tree i, j;
  tree i, j;
 
 
  for (i = outputs; i ; i = TREE_CHAIN (i))
  for (i = outputs; i ; i = TREE_CHAIN (i))
    {
    {
      tree i_name = TREE_PURPOSE (TREE_PURPOSE (i));
      tree i_name = TREE_PURPOSE (TREE_PURPOSE (i));
      if (! i_name)
      if (! i_name)
        continue;
        continue;
 
 
      for (j = TREE_CHAIN (i); j ; j = TREE_CHAIN (j))
      for (j = TREE_CHAIN (i); j ; j = TREE_CHAIN (j))
        if (simple_cst_equal (i_name, TREE_PURPOSE (TREE_PURPOSE (j))))
        if (simple_cst_equal (i_name, TREE_PURPOSE (TREE_PURPOSE (j))))
          goto failure;
          goto failure;
    }
    }
 
 
  for (i = inputs; i ; i = TREE_CHAIN (i))
  for (i = inputs; i ; i = TREE_CHAIN (i))
    {
    {
      tree i_name = TREE_PURPOSE (TREE_PURPOSE (i));
      tree i_name = TREE_PURPOSE (TREE_PURPOSE (i));
      if (! i_name)
      if (! i_name)
        continue;
        continue;
 
 
      for (j = TREE_CHAIN (i); j ; j = TREE_CHAIN (j))
      for (j = TREE_CHAIN (i); j ; j = TREE_CHAIN (j))
        if (simple_cst_equal (i_name, TREE_PURPOSE (TREE_PURPOSE (j))))
        if (simple_cst_equal (i_name, TREE_PURPOSE (TREE_PURPOSE (j))))
          goto failure;
          goto failure;
      for (j = outputs; j ; j = TREE_CHAIN (j))
      for (j = outputs; j ; j = TREE_CHAIN (j))
        if (simple_cst_equal (i_name, TREE_PURPOSE (TREE_PURPOSE (j))))
        if (simple_cst_equal (i_name, TREE_PURPOSE (TREE_PURPOSE (j))))
          goto failure;
          goto failure;
    }
    }
 
 
  for (i = labels; i ; i = TREE_CHAIN (i))
  for (i = labels; i ; i = TREE_CHAIN (i))
    {
    {
      tree i_name = TREE_PURPOSE (i);
      tree i_name = TREE_PURPOSE (i);
      if (! i_name)
      if (! i_name)
        continue;
        continue;
 
 
      for (j = TREE_CHAIN (i); j ; j = TREE_CHAIN (j))
      for (j = TREE_CHAIN (i); j ; j = TREE_CHAIN (j))
        if (simple_cst_equal (i_name, TREE_PURPOSE (j)))
        if (simple_cst_equal (i_name, TREE_PURPOSE (j)))
          goto failure;
          goto failure;
      for (j = inputs; j ; j = TREE_CHAIN (j))
      for (j = inputs; j ; j = TREE_CHAIN (j))
        if (simple_cst_equal (i_name, TREE_PURPOSE (TREE_PURPOSE (j))))
        if (simple_cst_equal (i_name, TREE_PURPOSE (TREE_PURPOSE (j))))
          goto failure;
          goto failure;
    }
    }
 
 
  return true;
  return true;
 
 
 failure:
 failure:
  error ("duplicate asm operand name %qs",
  error ("duplicate asm operand name %qs",
         TREE_STRING_POINTER (TREE_PURPOSE (TREE_PURPOSE (i))));
         TREE_STRING_POINTER (TREE_PURPOSE (TREE_PURPOSE (i))));
  return false;
  return false;
}
}
 
 
/* A subroutine of expand_asm_operands.  Resolve the names of the operands
/* A subroutine of expand_asm_operands.  Resolve the names of the operands
   in *POUTPUTS and *PINPUTS to numbers, and replace the name expansions in
   in *POUTPUTS and *PINPUTS to numbers, and replace the name expansions in
   STRING and in the constraints to those numbers.  */
   STRING and in the constraints to those numbers.  */
 
 
tree
tree
resolve_asm_operand_names (tree string, tree outputs, tree inputs, tree labels)
resolve_asm_operand_names (tree string, tree outputs, tree inputs, tree labels)
{
{
  char *buffer;
  char *buffer;
  char *p;
  char *p;
  const char *c;
  const char *c;
  tree t;
  tree t;
 
 
  check_unique_operand_names (outputs, inputs, labels);
  check_unique_operand_names (outputs, inputs, labels);
 
 
  /* Substitute [<name>] in input constraint strings.  There should be no
  /* Substitute [<name>] in input constraint strings.  There should be no
     named operands in output constraints.  */
     named operands in output constraints.  */
  for (t = inputs; t ; t = TREE_CHAIN (t))
  for (t = inputs; t ; t = TREE_CHAIN (t))
    {
    {
      c = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (t)));
      c = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (t)));
      if (strchr (c, '[') != NULL)
      if (strchr (c, '[') != NULL)
        {
        {
          p = buffer = xstrdup (c);
          p = buffer = xstrdup (c);
          while ((p = strchr (p, '[')) != NULL)
          while ((p = strchr (p, '[')) != NULL)
            p = resolve_operand_name_1 (p, outputs, inputs, NULL);
            p = resolve_operand_name_1 (p, outputs, inputs, NULL);
          TREE_VALUE (TREE_PURPOSE (t))
          TREE_VALUE (TREE_PURPOSE (t))
            = build_string (strlen (buffer), buffer);
            = build_string (strlen (buffer), buffer);
          free (buffer);
          free (buffer);
        }
        }
    }
    }
 
 
  /* Now check for any needed substitutions in the template.  */
  /* Now check for any needed substitutions in the template.  */
  c = TREE_STRING_POINTER (string);
  c = TREE_STRING_POINTER (string);
  while ((c = strchr (c, '%')) != NULL)
  while ((c = strchr (c, '%')) != NULL)
    {
    {
      if (c[1] == '[')
      if (c[1] == '[')
        break;
        break;
      else if (ISALPHA (c[1]) && c[2] == '[')
      else if (ISALPHA (c[1]) && c[2] == '[')
        break;
        break;
      else
      else
        {
        {
          c += 1;
          c += 1;
          continue;
          continue;
        }
        }
    }
    }
 
 
  if (c)
  if (c)
    {
    {
      /* OK, we need to make a copy so we can perform the substitutions.
      /* OK, we need to make a copy so we can perform the substitutions.
         Assume that we will not need extra space--we get to remove '['
         Assume that we will not need extra space--we get to remove '['
         and ']', which means we cannot have a problem until we have more
         and ']', which means we cannot have a problem until we have more
         than 999 operands.  */
         than 999 operands.  */
      buffer = xstrdup (TREE_STRING_POINTER (string));
      buffer = xstrdup (TREE_STRING_POINTER (string));
      p = buffer + (c - TREE_STRING_POINTER (string));
      p = buffer + (c - TREE_STRING_POINTER (string));
 
 
      while ((p = strchr (p, '%')) != NULL)
      while ((p = strchr (p, '%')) != NULL)
        {
        {
          if (p[1] == '[')
          if (p[1] == '[')
            p += 1;
            p += 1;
          else if (ISALPHA (p[1]) && p[2] == '[')
          else if (ISALPHA (p[1]) && p[2] == '[')
            p += 2;
            p += 2;
          else
          else
            {
            {
              p += 1;
              p += 1;
              continue;
              continue;
            }
            }
 
 
          p = resolve_operand_name_1 (p, outputs, inputs, labels);
          p = resolve_operand_name_1 (p, outputs, inputs, labels);
        }
        }
 
 
      string = build_string (strlen (buffer), buffer);
      string = build_string (strlen (buffer), buffer);
      free (buffer);
      free (buffer);
    }
    }
 
 
  return string;
  return string;
}
}
 
 
/* A subroutine of resolve_operand_names.  P points to the '[' for a
/* A subroutine of resolve_operand_names.  P points to the '[' for a
   potential named operand of the form [<name>].  In place, replace
   potential named operand of the form [<name>].  In place, replace
   the name and brackets with a number.  Return a pointer to the
   the name and brackets with a number.  Return a pointer to the
   balance of the string after substitution.  */
   balance of the string after substitution.  */
 
 
static char *
static char *
resolve_operand_name_1 (char *p, tree outputs, tree inputs, tree labels)
resolve_operand_name_1 (char *p, tree outputs, tree inputs, tree labels)
{
{
  char *q;
  char *q;
  int op;
  int op;
  tree t;
  tree t;
 
 
  /* Collect the operand name.  */
  /* Collect the operand name.  */
  q = strchr (++p, ']');
  q = strchr (++p, ']');
  if (!q)
  if (!q)
    {
    {
      error ("missing close brace for named operand");
      error ("missing close brace for named operand");
      return strchr (p, '\0');
      return strchr (p, '\0');
    }
    }
  *q = '\0';
  *q = '\0';
 
 
  /* Resolve the name to a number.  */
  /* Resolve the name to a number.  */
  for (op = 0, t = outputs; t ; t = TREE_CHAIN (t), op++)
  for (op = 0, t = outputs; t ; t = TREE_CHAIN (t), op++)
    {
    {
      tree name = TREE_PURPOSE (TREE_PURPOSE (t));
      tree name = TREE_PURPOSE (TREE_PURPOSE (t));
      if (name && strcmp (TREE_STRING_POINTER (name), p) == 0)
      if (name && strcmp (TREE_STRING_POINTER (name), p) == 0)
        goto found;
        goto found;
    }
    }
  for (t = inputs; t ; t = TREE_CHAIN (t), op++)
  for (t = inputs; t ; t = TREE_CHAIN (t), op++)
    {
    {
      tree name = TREE_PURPOSE (TREE_PURPOSE (t));
      tree name = TREE_PURPOSE (TREE_PURPOSE (t));
      if (name && strcmp (TREE_STRING_POINTER (name), p) == 0)
      if (name && strcmp (TREE_STRING_POINTER (name), p) == 0)
        goto found;
        goto found;
    }
    }
  for (t = labels; t ; t = TREE_CHAIN (t), op++)
  for (t = labels; t ; t = TREE_CHAIN (t), op++)
    {
    {
      tree name = TREE_PURPOSE (t);
      tree name = TREE_PURPOSE (t);
      if (name && strcmp (TREE_STRING_POINTER (name), p) == 0)
      if (name && strcmp (TREE_STRING_POINTER (name), p) == 0)
        goto found;
        goto found;
    }
    }
 
 
  error ("undefined named operand %qs", identifier_to_locale (p));
  error ("undefined named operand %qs", identifier_to_locale (p));
  op = 0;
  op = 0;
 
 
 found:
 found:
  /* Replace the name with the number.  Unfortunately, not all libraries
  /* Replace the name with the number.  Unfortunately, not all libraries
     get the return value of sprintf correct, so search for the end of the
     get the return value of sprintf correct, so search for the end of the
     generated string by hand.  */
     generated string by hand.  */
  sprintf (--p, "%d", op);
  sprintf (--p, "%d", op);
  p = strchr (p, '\0');
  p = strchr (p, '\0');
 
 
  /* Verify the no extra buffer space assumption.  */
  /* Verify the no extra buffer space assumption.  */
  gcc_assert (p <= q);
  gcc_assert (p <= q);
 
 
  /* Shift the rest of the buffer down to fill the gap.  */
  /* Shift the rest of the buffer down to fill the gap.  */
  memmove (p, q + 1, strlen (q + 1) + 1);
  memmove (p, q + 1, strlen (q + 1) + 1);
 
 
  return p;
  return p;
}
}


/* Generate RTL to evaluate the expression EXP.  */
/* Generate RTL to evaluate the expression EXP.  */
 
 
void
void
expand_expr_stmt (tree exp)
expand_expr_stmt (tree exp)
{
{
  rtx value;
  rtx value;
  tree type;
  tree type;
 
 
  value = expand_expr (exp, const0_rtx, VOIDmode, EXPAND_NORMAL);
  value = expand_expr (exp, const0_rtx, VOIDmode, EXPAND_NORMAL);
  type = TREE_TYPE (exp);
  type = TREE_TYPE (exp);
 
 
  /* If all we do is reference a volatile value in memory,
  /* If all we do is reference a volatile value in memory,
     copy it to a register to be sure it is actually touched.  */
     copy it to a register to be sure it is actually touched.  */
  if (value && MEM_P (value) && TREE_THIS_VOLATILE (exp))
  if (value && MEM_P (value) && TREE_THIS_VOLATILE (exp))
    {
    {
      if (TYPE_MODE (type) == VOIDmode)
      if (TYPE_MODE (type) == VOIDmode)
        ;
        ;
      else if (TYPE_MODE (type) != BLKmode)
      else if (TYPE_MODE (type) != BLKmode)
        value = copy_to_reg (value);
        value = copy_to_reg (value);
      else
      else
        {
        {
          rtx lab = gen_label_rtx ();
          rtx lab = gen_label_rtx ();
 
 
          /* Compare the value with itself to reference it.  */
          /* Compare the value with itself to reference it.  */
          emit_cmp_and_jump_insns (value, value, EQ,
          emit_cmp_and_jump_insns (value, value, EQ,
                                   expand_normal (TYPE_SIZE (type)),
                                   expand_normal (TYPE_SIZE (type)),
                                   BLKmode, 0, lab);
                                   BLKmode, 0, lab);
          emit_label (lab);
          emit_label (lab);
        }
        }
    }
    }
 
 
  /* Free any temporaries used to evaluate this expression.  */
  /* Free any temporaries used to evaluate this expression.  */
  free_temp_slots ();
  free_temp_slots ();
}
}
 
 
/* Warn if EXP contains any computations whose results are not used.
/* Warn if EXP contains any computations whose results are not used.
   Return 1 if a warning is printed; 0 otherwise.  LOCUS is the
   Return 1 if a warning is printed; 0 otherwise.  LOCUS is the
   (potential) location of the expression.  */
   (potential) location of the expression.  */
 
 
int
int
warn_if_unused_value (const_tree exp, location_t locus)
warn_if_unused_value (const_tree exp, location_t locus)
{
{
 restart:
 restart:
  if (TREE_USED (exp) || TREE_NO_WARNING (exp))
  if (TREE_USED (exp) || TREE_NO_WARNING (exp))
    return 0;
    return 0;
 
 
  /* Don't warn about void constructs.  This includes casting to void,
  /* Don't warn about void constructs.  This includes casting to void,
     void function calls, and statement expressions with a final cast
     void function calls, and statement expressions with a final cast
     to void.  */
     to void.  */
  if (VOID_TYPE_P (TREE_TYPE (exp)))
  if (VOID_TYPE_P (TREE_TYPE (exp)))
    return 0;
    return 0;
 
 
  if (EXPR_HAS_LOCATION (exp))
  if (EXPR_HAS_LOCATION (exp))
    locus = EXPR_LOCATION (exp);
    locus = EXPR_LOCATION (exp);
 
 
  switch (TREE_CODE (exp))
  switch (TREE_CODE (exp))
    {
    {
    case PREINCREMENT_EXPR:
    case PREINCREMENT_EXPR:
    case POSTINCREMENT_EXPR:
    case POSTINCREMENT_EXPR:
    case PREDECREMENT_EXPR:
    case PREDECREMENT_EXPR:
    case POSTDECREMENT_EXPR:
    case POSTDECREMENT_EXPR:
    case MODIFY_EXPR:
    case MODIFY_EXPR:
    case INIT_EXPR:
    case INIT_EXPR:
    case TARGET_EXPR:
    case TARGET_EXPR:
    case CALL_EXPR:
    case CALL_EXPR:
    case TRY_CATCH_EXPR:
    case TRY_CATCH_EXPR:
    case WITH_CLEANUP_EXPR:
    case WITH_CLEANUP_EXPR:
    case EXIT_EXPR:
    case EXIT_EXPR:
    case VA_ARG_EXPR:
    case VA_ARG_EXPR:
      return 0;
      return 0;
 
 
    case BIND_EXPR:
    case BIND_EXPR:
      /* For a binding, warn if no side effect within it.  */
      /* For a binding, warn if no side effect within it.  */
      exp = BIND_EXPR_BODY (exp);
      exp = BIND_EXPR_BODY (exp);
      goto restart;
      goto restart;
 
 
    case SAVE_EXPR:
    case SAVE_EXPR:
    case NON_LVALUE_EXPR:
    case NON_LVALUE_EXPR:
      exp = TREE_OPERAND (exp, 0);
      exp = TREE_OPERAND (exp, 0);
      goto restart;
      goto restart;
 
 
    case TRUTH_ORIF_EXPR:
    case TRUTH_ORIF_EXPR:
    case TRUTH_ANDIF_EXPR:
    case TRUTH_ANDIF_EXPR:
      /* In && or ||, warn if 2nd operand has no side effect.  */
      /* In && or ||, warn if 2nd operand has no side effect.  */
      exp = TREE_OPERAND (exp, 1);
      exp = TREE_OPERAND (exp, 1);
      goto restart;
      goto restart;
 
 
    case COMPOUND_EXPR:
    case COMPOUND_EXPR:
      if (warn_if_unused_value (TREE_OPERAND (exp, 0), locus))
      if (warn_if_unused_value (TREE_OPERAND (exp, 0), locus))
        return 1;
        return 1;
      /* Let people do `(foo (), 0)' without a warning.  */
      /* Let people do `(foo (), 0)' without a warning.  */
      if (TREE_CONSTANT (TREE_OPERAND (exp, 1)))
      if (TREE_CONSTANT (TREE_OPERAND (exp, 1)))
        return 0;
        return 0;
      exp = TREE_OPERAND (exp, 1);
      exp = TREE_OPERAND (exp, 1);
      goto restart;
      goto restart;
 
 
    case COND_EXPR:
    case COND_EXPR:
      /* If this is an expression with side effects, don't warn; this
      /* If this is an expression with side effects, don't warn; this
         case commonly appears in macro expansions.  */
         case commonly appears in macro expansions.  */
      if (TREE_SIDE_EFFECTS (exp))
      if (TREE_SIDE_EFFECTS (exp))
        return 0;
        return 0;
      goto warn;
      goto warn;
 
 
    case INDIRECT_REF:
    case INDIRECT_REF:
      /* Don't warn about automatic dereferencing of references, since
      /* Don't warn about automatic dereferencing of references, since
         the user cannot control it.  */
         the user cannot control it.  */
      if (TREE_CODE (TREE_TYPE (TREE_OPERAND (exp, 0))) == REFERENCE_TYPE)
      if (TREE_CODE (TREE_TYPE (TREE_OPERAND (exp, 0))) == REFERENCE_TYPE)
        {
        {
          exp = TREE_OPERAND (exp, 0);
          exp = TREE_OPERAND (exp, 0);
          goto restart;
          goto restart;
        }
        }
      /* Fall through.  */
      /* Fall through.  */
 
 
    default:
    default:
      /* Referencing a volatile value is a side effect, so don't warn.  */
      /* Referencing a volatile value is a side effect, so don't warn.  */
      if ((DECL_P (exp) || REFERENCE_CLASS_P (exp))
      if ((DECL_P (exp) || REFERENCE_CLASS_P (exp))
          && TREE_THIS_VOLATILE (exp))
          && TREE_THIS_VOLATILE (exp))
        return 0;
        return 0;
 
 
      /* If this is an expression which has no operands, there is no value
      /* If this is an expression which has no operands, there is no value
         to be unused.  There are no such language-independent codes,
         to be unused.  There are no such language-independent codes,
         but front ends may define such.  */
         but front ends may define such.  */
      if (EXPRESSION_CLASS_P (exp) && TREE_OPERAND_LENGTH (exp) == 0)
      if (EXPRESSION_CLASS_P (exp) && TREE_OPERAND_LENGTH (exp) == 0)
        return 0;
        return 0;
 
 
    warn:
    warn:
      warning_at (locus, OPT_Wunused_value, "value computed is not used");
      warning_at (locus, OPT_Wunused_value, "value computed is not used");
      return 1;
      return 1;
    }
    }
}
}
 
 


/* Generate RTL to return from the current function, with no value.
/* Generate RTL to return from the current function, with no value.
   (That is, we do not do anything about returning any value.)  */
   (That is, we do not do anything about returning any value.)  */
 
 
void
void
expand_null_return (void)
expand_null_return (void)
{
{
  /* If this function was declared to return a value, but we
  /* If this function was declared to return a value, but we
     didn't, clobber the return registers so that they are not
     didn't, clobber the return registers so that they are not
     propagated live to the rest of the function.  */
     propagated live to the rest of the function.  */
  clobber_return_register ();
  clobber_return_register ();
 
 
  expand_null_return_1 ();
  expand_null_return_1 ();
}
}
 
 
/* Generate RTL to return directly from the current function.
/* Generate RTL to return directly from the current function.
   (That is, we bypass any return value.)  */
   (That is, we bypass any return value.)  */
 
 
void
void
expand_naked_return (void)
expand_naked_return (void)
{
{
  rtx end_label;
  rtx end_label;
 
 
  clear_pending_stack_adjust ();
  clear_pending_stack_adjust ();
  do_pending_stack_adjust ();
  do_pending_stack_adjust ();
 
 
  end_label = naked_return_label;
  end_label = naked_return_label;
  if (end_label == 0)
  if (end_label == 0)
    end_label = naked_return_label = gen_label_rtx ();
    end_label = naked_return_label = gen_label_rtx ();
 
 
  emit_jump (end_label);
  emit_jump (end_label);
}
}
 
 
/* Generate RTL to return from the current function, with value VAL.  */
/* Generate RTL to return from the current function, with value VAL.  */
 
 
static void
static void
expand_value_return (rtx val)
expand_value_return (rtx val)
{
{
  /* Copy the value to the return location unless it's already there.  */
  /* Copy the value to the return location unless it's already there.  */
 
 
  tree decl = DECL_RESULT (current_function_decl);
  tree decl = DECL_RESULT (current_function_decl);
  rtx return_reg = DECL_RTL (decl);
  rtx return_reg = DECL_RTL (decl);
  if (return_reg != val)
  if (return_reg != val)
    {
    {
      tree funtype = TREE_TYPE (current_function_decl);
      tree funtype = TREE_TYPE (current_function_decl);
      tree type = TREE_TYPE (decl);
      tree type = TREE_TYPE (decl);
      int unsignedp = TYPE_UNSIGNED (type);
      int unsignedp = TYPE_UNSIGNED (type);
      enum machine_mode old_mode = DECL_MODE (decl);
      enum machine_mode old_mode = DECL_MODE (decl);
      enum machine_mode mode = promote_function_mode (type, old_mode,
      enum machine_mode mode = promote_function_mode (type, old_mode,
                                                      &unsignedp, funtype, 1);
                                                      &unsignedp, funtype, 1);
 
 
      if (mode != old_mode)
      if (mode != old_mode)
        val = convert_modes (mode, old_mode, val, unsignedp);
        val = convert_modes (mode, old_mode, val, unsignedp);
 
 
      if (GET_CODE (return_reg) == PARALLEL)
      if (GET_CODE (return_reg) == PARALLEL)
        emit_group_load (return_reg, val, type, int_size_in_bytes (type));
        emit_group_load (return_reg, val, type, int_size_in_bytes (type));
      else
      else
        emit_move_insn (return_reg, val);
        emit_move_insn (return_reg, val);
    }
    }
 
 
  expand_null_return_1 ();
  expand_null_return_1 ();
}
}
 
 
/* Output a return with no value.  */
/* Output a return with no value.  */
 
 
static void
static void
expand_null_return_1 (void)
expand_null_return_1 (void)
{
{
  clear_pending_stack_adjust ();
  clear_pending_stack_adjust ();
  do_pending_stack_adjust ();
  do_pending_stack_adjust ();
  emit_jump (return_label);
  emit_jump (return_label);
}
}


/* Generate RTL to evaluate the expression RETVAL and return it
/* Generate RTL to evaluate the expression RETVAL and return it
   from the current function.  */
   from the current function.  */
 
 
void
void
expand_return (tree retval)
expand_return (tree retval)
{
{
  rtx result_rtl;
  rtx result_rtl;
  rtx val = 0;
  rtx val = 0;
  tree retval_rhs;
  tree retval_rhs;
 
 
  /* If function wants no value, give it none.  */
  /* If function wants no value, give it none.  */
  if (TREE_CODE (TREE_TYPE (TREE_TYPE (current_function_decl))) == VOID_TYPE)
  if (TREE_CODE (TREE_TYPE (TREE_TYPE (current_function_decl))) == VOID_TYPE)
    {
    {
      expand_normal (retval);
      expand_normal (retval);
      expand_null_return ();
      expand_null_return ();
      return;
      return;
    }
    }
 
 
  if (retval == error_mark_node)
  if (retval == error_mark_node)
    {
    {
      /* Treat this like a return of no value from a function that
      /* Treat this like a return of no value from a function that
         returns a value.  */
         returns a value.  */
      expand_null_return ();
      expand_null_return ();
      return;
      return;
    }
    }
  else if ((TREE_CODE (retval) == MODIFY_EXPR
  else if ((TREE_CODE (retval) == MODIFY_EXPR
            || TREE_CODE (retval) == INIT_EXPR)
            || TREE_CODE (retval) == INIT_EXPR)
           && TREE_CODE (TREE_OPERAND (retval, 0)) == RESULT_DECL)
           && TREE_CODE (TREE_OPERAND (retval, 0)) == RESULT_DECL)
    retval_rhs = TREE_OPERAND (retval, 1);
    retval_rhs = TREE_OPERAND (retval, 1);
  else
  else
    retval_rhs = retval;
    retval_rhs = retval;
 
 
  result_rtl = DECL_RTL (DECL_RESULT (current_function_decl));
  result_rtl = DECL_RTL (DECL_RESULT (current_function_decl));
 
 
  /* If we are returning the RESULT_DECL, then the value has already
  /* If we are returning the RESULT_DECL, then the value has already
     been stored into it, so we don't have to do anything special.  */
     been stored into it, so we don't have to do anything special.  */
  if (TREE_CODE (retval_rhs) == RESULT_DECL)
  if (TREE_CODE (retval_rhs) == RESULT_DECL)
    expand_value_return (result_rtl);
    expand_value_return (result_rtl);
 
 
  /* If the result is an aggregate that is being returned in one (or more)
  /* If the result is an aggregate that is being returned in one (or more)
     registers, load the registers here.  The compiler currently can't handle
     registers, load the registers here.  The compiler currently can't handle
     copying a BLKmode value into registers.  We could put this code in a
     copying a BLKmode value into registers.  We could put this code in a
     more general area (for use by everyone instead of just function
     more general area (for use by everyone instead of just function
     call/return), but until this feature is generally usable it is kept here
     call/return), but until this feature is generally usable it is kept here
     (and in expand_call).  */
     (and in expand_call).  */
 
 
  else if (retval_rhs != 0
  else if (retval_rhs != 0
           && TYPE_MODE (TREE_TYPE (retval_rhs)) == BLKmode
           && TYPE_MODE (TREE_TYPE (retval_rhs)) == BLKmode
           && REG_P (result_rtl))
           && REG_P (result_rtl))
    {
    {
      int i;
      int i;
      unsigned HOST_WIDE_INT bitpos, xbitpos;
      unsigned HOST_WIDE_INT bitpos, xbitpos;
      unsigned HOST_WIDE_INT padding_correction = 0;
      unsigned HOST_WIDE_INT padding_correction = 0;
      unsigned HOST_WIDE_INT bytes
      unsigned HOST_WIDE_INT bytes
        = int_size_in_bytes (TREE_TYPE (retval_rhs));
        = int_size_in_bytes (TREE_TYPE (retval_rhs));
      int n_regs = (bytes + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
      int n_regs = (bytes + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
      unsigned int bitsize
      unsigned int bitsize
        = MIN (TYPE_ALIGN (TREE_TYPE (retval_rhs)), BITS_PER_WORD);
        = MIN (TYPE_ALIGN (TREE_TYPE (retval_rhs)), BITS_PER_WORD);
      rtx *result_pseudos = XALLOCAVEC (rtx, n_regs);
      rtx *result_pseudos = XALLOCAVEC (rtx, n_regs);
      rtx result_reg, src = NULL_RTX, dst = NULL_RTX;
      rtx result_reg, src = NULL_RTX, dst = NULL_RTX;
      rtx result_val = expand_normal (retval_rhs);
      rtx result_val = expand_normal (retval_rhs);
      enum machine_mode tmpmode, result_reg_mode;
      enum machine_mode tmpmode, result_reg_mode;
 
 
      if (bytes == 0)
      if (bytes == 0)
        {
        {
          expand_null_return ();
          expand_null_return ();
          return;
          return;
        }
        }
 
 
      /* If the structure doesn't take up a whole number of words, see
      /* If the structure doesn't take up a whole number of words, see
         whether the register value should be padded on the left or on
         whether the register value should be padded on the left or on
         the right.  Set PADDING_CORRECTION to the number of padding
         the right.  Set PADDING_CORRECTION to the number of padding
         bits needed on the left side.
         bits needed on the left side.
 
 
         In most ABIs, the structure will be returned at the least end of
         In most ABIs, the structure will be returned at the least end of
         the register, which translates to right padding on little-endian
         the register, which translates to right padding on little-endian
         targets and left padding on big-endian targets.  The opposite
         targets and left padding on big-endian targets.  The opposite
         holds if the structure is returned at the most significant
         holds if the structure is returned at the most significant
         end of the register.  */
         end of the register.  */
      if (bytes % UNITS_PER_WORD != 0
      if (bytes % UNITS_PER_WORD != 0
          && (targetm.calls.return_in_msb (TREE_TYPE (retval_rhs))
          && (targetm.calls.return_in_msb (TREE_TYPE (retval_rhs))
              ? !BYTES_BIG_ENDIAN
              ? !BYTES_BIG_ENDIAN
              : BYTES_BIG_ENDIAN))
              : BYTES_BIG_ENDIAN))
        padding_correction = (BITS_PER_WORD - ((bytes % UNITS_PER_WORD)
        padding_correction = (BITS_PER_WORD - ((bytes % UNITS_PER_WORD)
                                               * BITS_PER_UNIT));
                                               * BITS_PER_UNIT));
 
 
      /* Copy the structure BITSIZE bits at a time.  */
      /* Copy the structure BITSIZE bits at a time.  */
      for (bitpos = 0, xbitpos = padding_correction;
      for (bitpos = 0, xbitpos = padding_correction;
           bitpos < bytes * BITS_PER_UNIT;
           bitpos < bytes * BITS_PER_UNIT;
           bitpos += bitsize, xbitpos += bitsize)
           bitpos += bitsize, xbitpos += bitsize)
        {
        {
          /* We need a new destination pseudo each time xbitpos is
          /* We need a new destination pseudo each time xbitpos is
             on a word boundary and when xbitpos == padding_correction
             on a word boundary and when xbitpos == padding_correction
             (the first time through).  */
             (the first time through).  */
          if (xbitpos % BITS_PER_WORD == 0
          if (xbitpos % BITS_PER_WORD == 0
              || xbitpos == padding_correction)
              || xbitpos == padding_correction)
            {
            {
              /* Generate an appropriate register.  */
              /* Generate an appropriate register.  */
              dst = gen_reg_rtx (word_mode);
              dst = gen_reg_rtx (word_mode);
              result_pseudos[xbitpos / BITS_PER_WORD] = dst;
              result_pseudos[xbitpos / BITS_PER_WORD] = dst;
 
 
              /* Clear the destination before we move anything into it.  */
              /* Clear the destination before we move anything into it.  */
              emit_move_insn (dst, CONST0_RTX (GET_MODE (dst)));
              emit_move_insn (dst, CONST0_RTX (GET_MODE (dst)));
            }
            }
 
 
          /* We need a new source operand each time bitpos is on a word
          /* We need a new source operand each time bitpos is on a word
             boundary.  */
             boundary.  */
          if (bitpos % BITS_PER_WORD == 0)
          if (bitpos % BITS_PER_WORD == 0)
            src = operand_subword_force (result_val,
            src = operand_subword_force (result_val,
                                         bitpos / BITS_PER_WORD,
                                         bitpos / BITS_PER_WORD,
                                         BLKmode);
                                         BLKmode);
 
 
          /* Use bitpos for the source extraction (left justified) and
          /* Use bitpos for the source extraction (left justified) and
             xbitpos for the destination store (right justified).  */
             xbitpos for the destination store (right justified).  */
          store_bit_field (dst, bitsize, xbitpos % BITS_PER_WORD, word_mode,
          store_bit_field (dst, bitsize, xbitpos % BITS_PER_WORD, word_mode,
                           extract_bit_field (src, bitsize,
                           extract_bit_field (src, bitsize,
                                              bitpos % BITS_PER_WORD, 1,
                                              bitpos % BITS_PER_WORD, 1,
                                              NULL_RTX, word_mode, word_mode));
                                              NULL_RTX, word_mode, word_mode));
        }
        }
 
 
      tmpmode = GET_MODE (result_rtl);
      tmpmode = GET_MODE (result_rtl);
      if (tmpmode == BLKmode)
      if (tmpmode == BLKmode)
        {
        {
          /* Find the smallest integer mode large enough to hold the
          /* Find the smallest integer mode large enough to hold the
             entire structure and use that mode instead of BLKmode
             entire structure and use that mode instead of BLKmode
             on the USE insn for the return register.  */
             on the USE insn for the return register.  */
          for (tmpmode = GET_CLASS_NARROWEST_MODE (MODE_INT);
          for (tmpmode = GET_CLASS_NARROWEST_MODE (MODE_INT);
               tmpmode != VOIDmode;
               tmpmode != VOIDmode;
               tmpmode = GET_MODE_WIDER_MODE (tmpmode))
               tmpmode = GET_MODE_WIDER_MODE (tmpmode))
            /* Have we found a large enough mode?  */
            /* Have we found a large enough mode?  */
            if (GET_MODE_SIZE (tmpmode) >= bytes)
            if (GET_MODE_SIZE (tmpmode) >= bytes)
              break;
              break;
 
 
          /* A suitable mode should have been found.  */
          /* A suitable mode should have been found.  */
          gcc_assert (tmpmode != VOIDmode);
          gcc_assert (tmpmode != VOIDmode);
 
 
          PUT_MODE (result_rtl, tmpmode);
          PUT_MODE (result_rtl, tmpmode);
        }
        }
 
 
      if (GET_MODE_SIZE (tmpmode) < GET_MODE_SIZE (word_mode))
      if (GET_MODE_SIZE (tmpmode) < GET_MODE_SIZE (word_mode))
        result_reg_mode = word_mode;
        result_reg_mode = word_mode;
      else
      else
        result_reg_mode = tmpmode;
        result_reg_mode = tmpmode;
      result_reg = gen_reg_rtx (result_reg_mode);
      result_reg = gen_reg_rtx (result_reg_mode);
 
 
      for (i = 0; i < n_regs; i++)
      for (i = 0; i < n_regs; i++)
        emit_move_insn (operand_subword (result_reg, i, 0, result_reg_mode),
        emit_move_insn (operand_subword (result_reg, i, 0, result_reg_mode),
                        result_pseudos[i]);
                        result_pseudos[i]);
 
 
      if (tmpmode != result_reg_mode)
      if (tmpmode != result_reg_mode)
        result_reg = gen_lowpart (tmpmode, result_reg);
        result_reg = gen_lowpart (tmpmode, result_reg);
 
 
      expand_value_return (result_reg);
      expand_value_return (result_reg);
    }
    }
  else if (retval_rhs != 0
  else if (retval_rhs != 0
           && !VOID_TYPE_P (TREE_TYPE (retval_rhs))
           && !VOID_TYPE_P (TREE_TYPE (retval_rhs))
           && (REG_P (result_rtl)
           && (REG_P (result_rtl)
               || (GET_CODE (result_rtl) == PARALLEL)))
               || (GET_CODE (result_rtl) == PARALLEL)))
    {
    {
      /* Calculate the return value into a temporary (usually a pseudo
      /* Calculate the return value into a temporary (usually a pseudo
         reg).  */
         reg).  */
      tree ot = TREE_TYPE (DECL_RESULT (current_function_decl));
      tree ot = TREE_TYPE (DECL_RESULT (current_function_decl));
      tree nt = build_qualified_type (ot, TYPE_QUALS (ot) | TYPE_QUAL_CONST);
      tree nt = build_qualified_type (ot, TYPE_QUALS (ot) | TYPE_QUAL_CONST);
 
 
      val = assign_temp (nt, 0, 0, 1);
      val = assign_temp (nt, 0, 0, 1);
      val = expand_expr (retval_rhs, val, GET_MODE (val), EXPAND_NORMAL);
      val = expand_expr (retval_rhs, val, GET_MODE (val), EXPAND_NORMAL);
      val = force_not_mem (val);
      val = force_not_mem (val);
      /* Return the calculated value.  */
      /* Return the calculated value.  */
      expand_value_return (val);
      expand_value_return (val);
    }
    }
  else
  else
    {
    {
      /* No hard reg used; calculate value into hard return reg.  */
      /* No hard reg used; calculate value into hard return reg.  */
      expand_expr (retval, const0_rtx, VOIDmode, EXPAND_NORMAL);
      expand_expr (retval, const0_rtx, VOIDmode, EXPAND_NORMAL);
      expand_value_return (result_rtl);
      expand_value_return (result_rtl);
    }
    }
}
}


/* Emit code to restore vital registers at the beginning of a nonlocal goto
/* Emit code to restore vital registers at the beginning of a nonlocal goto
   handler.  */
   handler.  */
static void
static void
expand_nl_goto_receiver (void)
expand_nl_goto_receiver (void)
{
{
  rtx chain;
  rtx chain;
 
 
  /* Clobber the FP when we get here, so we have to make sure it's
  /* Clobber the FP when we get here, so we have to make sure it's
     marked as used by this function.  */
     marked as used by this function.  */
  emit_use (hard_frame_pointer_rtx);
  emit_use (hard_frame_pointer_rtx);
 
 
  /* Mark the static chain as clobbered here so life information
  /* Mark the static chain as clobbered here so life information
     doesn't get messed up for it.  */
     doesn't get messed up for it.  */
  chain = targetm.calls.static_chain (current_function_decl, true);
  chain = targetm.calls.static_chain (current_function_decl, true);
  if (chain && REG_P (chain))
  if (chain && REG_P (chain))
    emit_clobber (chain);
    emit_clobber (chain);
 
 
#ifdef HAVE_nonlocal_goto
#ifdef HAVE_nonlocal_goto
  if (! HAVE_nonlocal_goto)
  if (! HAVE_nonlocal_goto)
#endif
#endif
    /* First adjust our frame pointer to its actual value.  It was
    /* First adjust our frame pointer to its actual value.  It was
       previously set to the start of the virtual area corresponding to
       previously set to the start of the virtual area corresponding to
       the stacked variables when we branched here and now needs to be
       the stacked variables when we branched here and now needs to be
       adjusted to the actual hardware fp value.
       adjusted to the actual hardware fp value.
 
 
       Assignments are to virtual registers are converted by
       Assignments are to virtual registers are converted by
       instantiate_virtual_regs into the corresponding assignment
       instantiate_virtual_regs into the corresponding assignment
       to the underlying register (fp in this case) that makes
       to the underlying register (fp in this case) that makes
       the original assignment true.
       the original assignment true.
       So the following insn will actually be
       So the following insn will actually be
       decrementing fp by STARTING_FRAME_OFFSET.  */
       decrementing fp by STARTING_FRAME_OFFSET.  */
    emit_move_insn (virtual_stack_vars_rtx, hard_frame_pointer_rtx);
    emit_move_insn (virtual_stack_vars_rtx, hard_frame_pointer_rtx);
 
 
#if ARG_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM
#if ARG_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM
  if (fixed_regs[ARG_POINTER_REGNUM])
  if (fixed_regs[ARG_POINTER_REGNUM])
    {
    {
#ifdef ELIMINABLE_REGS
#ifdef ELIMINABLE_REGS
      /* If the argument pointer can be eliminated in favor of the
      /* If the argument pointer can be eliminated in favor of the
         frame pointer, we don't need to restore it.  We assume here
         frame pointer, we don't need to restore it.  We assume here
         that if such an elimination is present, it can always be used.
         that if such an elimination is present, it can always be used.
         This is the case on all known machines; if we don't make this
         This is the case on all known machines; if we don't make this
         assumption, we do unnecessary saving on many machines.  */
         assumption, we do unnecessary saving on many machines.  */
      static const struct elims {const int from, to;} elim_regs[] = ELIMINABLE_REGS;
      static const struct elims {const int from, to;} elim_regs[] = ELIMINABLE_REGS;
      size_t i;
      size_t i;
 
 
      for (i = 0; i < ARRAY_SIZE (elim_regs); i++)
      for (i = 0; i < ARRAY_SIZE (elim_regs); i++)
        if (elim_regs[i].from == ARG_POINTER_REGNUM
        if (elim_regs[i].from == ARG_POINTER_REGNUM
            && elim_regs[i].to == HARD_FRAME_POINTER_REGNUM)
            && elim_regs[i].to == HARD_FRAME_POINTER_REGNUM)
          break;
          break;
 
 
      if (i == ARRAY_SIZE (elim_regs))
      if (i == ARRAY_SIZE (elim_regs))
#endif
#endif
        {
        {
          /* Now restore our arg pointer from the address at which it
          /* Now restore our arg pointer from the address at which it
             was saved in our stack frame.  */
             was saved in our stack frame.  */
          emit_move_insn (crtl->args.internal_arg_pointer,
          emit_move_insn (crtl->args.internal_arg_pointer,
                          copy_to_reg (get_arg_pointer_save_area ()));
                          copy_to_reg (get_arg_pointer_save_area ()));
        }
        }
    }
    }
#endif
#endif
 
 
#ifdef HAVE_nonlocal_goto_receiver
#ifdef HAVE_nonlocal_goto_receiver
  if (HAVE_nonlocal_goto_receiver)
  if (HAVE_nonlocal_goto_receiver)
    emit_insn (gen_nonlocal_goto_receiver ());
    emit_insn (gen_nonlocal_goto_receiver ());
#endif
#endif
 
 
  /* We must not allow the code we just generated to be reordered by
  /* We must not allow the code we just generated to be reordered by
     scheduling.  Specifically, the update of the frame pointer must
     scheduling.  Specifically, the update of the frame pointer must
     happen immediately, not later.  */
     happen immediately, not later.  */
  emit_insn (gen_blockage ());
  emit_insn (gen_blockage ());
}
}


/* Generate RTL for the automatic variable declaration DECL.
/* Generate RTL for the automatic variable declaration DECL.
   (Other kinds of declarations are simply ignored if seen here.)  */
   (Other kinds of declarations are simply ignored if seen here.)  */
 
 
void
void
expand_decl (tree decl)
expand_decl (tree decl)
{
{
  tree type;
  tree type;
 
 
  type = TREE_TYPE (decl);
  type = TREE_TYPE (decl);
 
 
  /* For a CONST_DECL, set mode, alignment, and sizes from those of the
  /* For a CONST_DECL, set mode, alignment, and sizes from those of the
     type in case this node is used in a reference.  */
     type in case this node is used in a reference.  */
  if (TREE_CODE (decl) == CONST_DECL)
  if (TREE_CODE (decl) == CONST_DECL)
    {
    {
      DECL_MODE (decl) = TYPE_MODE (type);
      DECL_MODE (decl) = TYPE_MODE (type);
      DECL_ALIGN (decl) = TYPE_ALIGN (type);
      DECL_ALIGN (decl) = TYPE_ALIGN (type);
      DECL_SIZE (decl) = TYPE_SIZE (type);
      DECL_SIZE (decl) = TYPE_SIZE (type);
      DECL_SIZE_UNIT (decl) = TYPE_SIZE_UNIT (type);
      DECL_SIZE_UNIT (decl) = TYPE_SIZE_UNIT (type);
      return;
      return;
    }
    }
 
 
  /* Otherwise, only automatic variables need any expansion done.  Static and
  /* Otherwise, only automatic variables need any expansion done.  Static and
     external variables, and external functions, will be handled by
     external variables, and external functions, will be handled by
     `assemble_variable' (called from finish_decl).  TYPE_DECL requires
     `assemble_variable' (called from finish_decl).  TYPE_DECL requires
     nothing.  PARM_DECLs are handled in `assign_parms'.  */
     nothing.  PARM_DECLs are handled in `assign_parms'.  */
  if (TREE_CODE (decl) != VAR_DECL)
  if (TREE_CODE (decl) != VAR_DECL)
    return;
    return;
 
 
  if (TREE_STATIC (decl) || DECL_EXTERNAL (decl))
  if (TREE_STATIC (decl) || DECL_EXTERNAL (decl))
    return;
    return;
 
 
  /* Create the RTL representation for the variable.  */
  /* Create the RTL representation for the variable.  */
 
 
  if (type == error_mark_node)
  if (type == error_mark_node)
    SET_DECL_RTL (decl, gen_rtx_MEM (BLKmode, const0_rtx));
    SET_DECL_RTL (decl, gen_rtx_MEM (BLKmode, const0_rtx));
 
 
  else if (DECL_SIZE (decl) == 0)
  else if (DECL_SIZE (decl) == 0)
    {
    {
      /* Variable with incomplete type.  */
      /* Variable with incomplete type.  */
      rtx x;
      rtx x;
      if (DECL_INITIAL (decl) == 0)
      if (DECL_INITIAL (decl) == 0)
        /* Error message was already done; now avoid a crash.  */
        /* Error message was already done; now avoid a crash.  */
        x = gen_rtx_MEM (BLKmode, const0_rtx);
        x = gen_rtx_MEM (BLKmode, const0_rtx);
      else
      else
        /* An initializer is going to decide the size of this array.
        /* An initializer is going to decide the size of this array.
           Until we know the size, represent its address with a reg.  */
           Until we know the size, represent its address with a reg.  */
        x = gen_rtx_MEM (BLKmode, gen_reg_rtx (Pmode));
        x = gen_rtx_MEM (BLKmode, gen_reg_rtx (Pmode));
 
 
      set_mem_attributes (x, decl, 1);
      set_mem_attributes (x, decl, 1);
      SET_DECL_RTL (decl, x);
      SET_DECL_RTL (decl, x);
    }
    }
  else if (use_register_for_decl (decl))
  else if (use_register_for_decl (decl))
    {
    {
      /* Automatic variable that can go in a register.  */
      /* Automatic variable that can go in a register.  */
      enum machine_mode reg_mode = promote_decl_mode (decl, NULL);
      enum machine_mode reg_mode = promote_decl_mode (decl, NULL);
 
 
      SET_DECL_RTL (decl, gen_reg_rtx (reg_mode));
      SET_DECL_RTL (decl, gen_reg_rtx (reg_mode));
 
 
      /* Note if the object is a user variable.  */
      /* Note if the object is a user variable.  */
      if (!DECL_ARTIFICIAL (decl))
      if (!DECL_ARTIFICIAL (decl))
          mark_user_reg (DECL_RTL (decl));
          mark_user_reg (DECL_RTL (decl));
 
 
      if (POINTER_TYPE_P (type))
      if (POINTER_TYPE_P (type))
        mark_reg_pointer (DECL_RTL (decl),
        mark_reg_pointer (DECL_RTL (decl),
                          TYPE_ALIGN (TREE_TYPE (TREE_TYPE (decl))));
                          TYPE_ALIGN (TREE_TYPE (TREE_TYPE (decl))));
    }
    }
 
 
  else
  else
    {
    {
      rtx oldaddr = 0;
      rtx oldaddr = 0;
      rtx addr;
      rtx addr;
      rtx x;
      rtx x;
 
 
      /* Variable-sized decls are dealt with in the gimplifier.  */
      /* Variable-sized decls are dealt with in the gimplifier.  */
      gcc_assert (TREE_CODE (DECL_SIZE_UNIT (decl)) == INTEGER_CST);
      gcc_assert (TREE_CODE (DECL_SIZE_UNIT (decl)) == INTEGER_CST);
 
 
      /* If we previously made RTL for this decl, it must be an array
      /* If we previously made RTL for this decl, it must be an array
         whose size was determined by the initializer.
         whose size was determined by the initializer.
         The old address was a register; set that register now
         The old address was a register; set that register now
         to the proper address.  */
         to the proper address.  */
      if (DECL_RTL_SET_P (decl))
      if (DECL_RTL_SET_P (decl))
        {
        {
          gcc_assert (MEM_P (DECL_RTL (decl)));
          gcc_assert (MEM_P (DECL_RTL (decl)));
          gcc_assert (REG_P (XEXP (DECL_RTL (decl), 0)));
          gcc_assert (REG_P (XEXP (DECL_RTL (decl), 0)));
          oldaddr = XEXP (DECL_RTL (decl), 0);
          oldaddr = XEXP (DECL_RTL (decl), 0);
        }
        }
 
 
      /* Set alignment we actually gave this decl.  */
      /* Set alignment we actually gave this decl.  */
      DECL_ALIGN (decl) = (DECL_MODE (decl) == BLKmode ? BIGGEST_ALIGNMENT
      DECL_ALIGN (decl) = (DECL_MODE (decl) == BLKmode ? BIGGEST_ALIGNMENT
                           : GET_MODE_BITSIZE (DECL_MODE (decl)));
                           : GET_MODE_BITSIZE (DECL_MODE (decl)));
      DECL_USER_ALIGN (decl) = 0;
      DECL_USER_ALIGN (decl) = 0;
 
 
      x = assign_temp (decl, 1, 1, 1);
      x = assign_temp (decl, 1, 1, 1);
      set_mem_attributes (x, decl, 1);
      set_mem_attributes (x, decl, 1);
      SET_DECL_RTL (decl, x);
      SET_DECL_RTL (decl, x);
 
 
      if (oldaddr)
      if (oldaddr)
        {
        {
          addr = force_operand (XEXP (DECL_RTL (decl), 0), oldaddr);
          addr = force_operand (XEXP (DECL_RTL (decl), 0), oldaddr);
          if (addr != oldaddr)
          if (addr != oldaddr)
            emit_move_insn (oldaddr, addr);
            emit_move_insn (oldaddr, addr);
        }
        }
    }
    }
}
}


/* Emit code to save the current value of stack.  */
/* Emit code to save the current value of stack.  */
rtx
rtx
expand_stack_save (void)
expand_stack_save (void)
{
{
  rtx ret = NULL_RTX;
  rtx ret = NULL_RTX;
 
 
  do_pending_stack_adjust ();
  do_pending_stack_adjust ();
  emit_stack_save (SAVE_BLOCK, &ret, NULL_RTX);
  emit_stack_save (SAVE_BLOCK, &ret, NULL_RTX);
  return ret;
  return ret;
}
}
 
 
/* Emit code to restore the current value of stack.  */
/* Emit code to restore the current value of stack.  */
void
void
expand_stack_restore (tree var)
expand_stack_restore (tree var)
{
{
  rtx sa = expand_normal (var);
  rtx sa = expand_normal (var);
 
 
  sa = convert_memory_address (Pmode, sa);
  sa = convert_memory_address (Pmode, sa);
  emit_stack_restore (SAVE_BLOCK, sa, NULL_RTX);
  emit_stack_restore (SAVE_BLOCK, sa, NULL_RTX);
}
}


/* Do the insertion of a case label into case_list.  The labels are
/* Do the insertion of a case label into case_list.  The labels are
   fed to us in descending order from the sorted vector of case labels used
   fed to us in descending order from the sorted vector of case labels used
   in the tree part of the middle end.  So the list we construct is
   in the tree part of the middle end.  So the list we construct is
   sorted in ascending order.  The bounds on the case range, LOW and HIGH,
   sorted in ascending order.  The bounds on the case range, LOW and HIGH,
   are converted to case's index type TYPE.  */
   are converted to case's index type TYPE.  */
 
 
static struct case_node *
static struct case_node *
add_case_node (struct case_node *head, tree type, tree low, tree high,
add_case_node (struct case_node *head, tree type, tree low, tree high,
               tree label, alloc_pool case_node_pool)
               tree label, alloc_pool case_node_pool)
{
{
  tree min_value, max_value;
  tree min_value, max_value;
  struct case_node *r;
  struct case_node *r;
 
 
  gcc_assert (TREE_CODE (low) == INTEGER_CST);
  gcc_assert (TREE_CODE (low) == INTEGER_CST);
  gcc_assert (!high || TREE_CODE (high) == INTEGER_CST);
  gcc_assert (!high || TREE_CODE (high) == INTEGER_CST);
 
 
  min_value = TYPE_MIN_VALUE (type);
  min_value = TYPE_MIN_VALUE (type);
  max_value = TYPE_MAX_VALUE (type);
  max_value = TYPE_MAX_VALUE (type);
 
 
  /* If there's no HIGH value, then this is not a case range; it's
  /* If there's no HIGH value, then this is not a case range; it's
     just a simple case label.  But that's just a degenerate case
     just a simple case label.  But that's just a degenerate case
     range.
     range.
     If the bounds are equal, turn this into the one-value case.  */
     If the bounds are equal, turn this into the one-value case.  */
  if (!high || tree_int_cst_equal (low, high))
  if (!high || tree_int_cst_equal (low, high))
    {
    {
      /* If the simple case value is unreachable, ignore it.  */
      /* If the simple case value is unreachable, ignore it.  */
      if ((TREE_CODE (min_value) == INTEGER_CST
      if ((TREE_CODE (min_value) == INTEGER_CST
            && tree_int_cst_compare (low, min_value) < 0)
            && tree_int_cst_compare (low, min_value) < 0)
          || (TREE_CODE (max_value) == INTEGER_CST
          || (TREE_CODE (max_value) == INTEGER_CST
              && tree_int_cst_compare (low, max_value) > 0))
              && tree_int_cst_compare (low, max_value) > 0))
        return head;
        return head;
      low = fold_convert (type, low);
      low = fold_convert (type, low);
      high = low;
      high = low;
    }
    }
  else
  else
    {
    {
      /* If the entire case range is unreachable, ignore it.  */
      /* If the entire case range is unreachable, ignore it.  */
      if ((TREE_CODE (min_value) == INTEGER_CST
      if ((TREE_CODE (min_value) == INTEGER_CST
            && tree_int_cst_compare (high, min_value) < 0)
            && tree_int_cst_compare (high, min_value) < 0)
          || (TREE_CODE (max_value) == INTEGER_CST
          || (TREE_CODE (max_value) == INTEGER_CST
              && tree_int_cst_compare (low, max_value) > 0))
              && tree_int_cst_compare (low, max_value) > 0))
        return head;
        return head;
 
 
      /* If the lower bound is less than the index type's minimum
      /* If the lower bound is less than the index type's minimum
         value, truncate the range bounds.  */
         value, truncate the range bounds.  */
      if (TREE_CODE (min_value) == INTEGER_CST
      if (TREE_CODE (min_value) == INTEGER_CST
            && tree_int_cst_compare (low, min_value) < 0)
            && tree_int_cst_compare (low, min_value) < 0)
        low = min_value;
        low = min_value;
      low = fold_convert (type, low);
      low = fold_convert (type, low);
 
 
      /* If the upper bound is greater than the index type's maximum
      /* If the upper bound is greater than the index type's maximum
         value, truncate the range bounds.  */
         value, truncate the range bounds.  */
      if (TREE_CODE (max_value) == INTEGER_CST
      if (TREE_CODE (max_value) == INTEGER_CST
          && tree_int_cst_compare (high, max_value) > 0)
          && tree_int_cst_compare (high, max_value) > 0)
        high = max_value;
        high = max_value;
      high = fold_convert (type, high);
      high = fold_convert (type, high);
    }
    }
 
 
 
 
  /* Add this label to the chain.  Make sure to drop overflow flags.  */
  /* Add this label to the chain.  Make sure to drop overflow flags.  */
  r = (struct case_node *) pool_alloc (case_node_pool);
  r = (struct case_node *) pool_alloc (case_node_pool);
  r->low = build_int_cst_wide (TREE_TYPE (low), TREE_INT_CST_LOW (low),
  r->low = build_int_cst_wide (TREE_TYPE (low), TREE_INT_CST_LOW (low),
                               TREE_INT_CST_HIGH (low));
                               TREE_INT_CST_HIGH (low));
  r->high = build_int_cst_wide (TREE_TYPE (high), TREE_INT_CST_LOW (high),
  r->high = build_int_cst_wide (TREE_TYPE (high), TREE_INT_CST_LOW (high),
                                TREE_INT_CST_HIGH (high));
                                TREE_INT_CST_HIGH (high));
  r->code_label = label;
  r->code_label = label;
  r->parent = r->left = NULL;
  r->parent = r->left = NULL;
  r->right = head;
  r->right = head;
  return r;
  return r;
}
}


/* Maximum number of case bit tests.  */
/* Maximum number of case bit tests.  */
#define MAX_CASE_BIT_TESTS  3
#define MAX_CASE_BIT_TESTS  3
 
 
/* By default, enable case bit tests on targets with ashlsi3.  */
/* By default, enable case bit tests on targets with ashlsi3.  */
#ifndef CASE_USE_BIT_TESTS
#ifndef CASE_USE_BIT_TESTS
#define CASE_USE_BIT_TESTS  (optab_handler (ashl_optab, word_mode)->insn_code \
#define CASE_USE_BIT_TESTS  (optab_handler (ashl_optab, word_mode)->insn_code \
                             != CODE_FOR_nothing)
                             != CODE_FOR_nothing)
#endif
#endif
 
 
 
 
/* A case_bit_test represents a set of case nodes that may be
/* A case_bit_test represents a set of case nodes that may be
   selected from using a bit-wise comparison.  HI and LO hold
   selected from using a bit-wise comparison.  HI and LO hold
   the integer to be tested against, LABEL contains the label
   the integer to be tested against, LABEL contains the label
   to jump to upon success and BITS counts the number of case
   to jump to upon success and BITS counts the number of case
   nodes handled by this test, typically the number of bits
   nodes handled by this test, typically the number of bits
   set in HI:LO.  */
   set in HI:LO.  */
 
 
struct case_bit_test
struct case_bit_test
{
{
  HOST_WIDE_INT hi;
  HOST_WIDE_INT hi;
  HOST_WIDE_INT lo;
  HOST_WIDE_INT lo;
  rtx label;
  rtx label;
  int bits;
  int bits;
};
};
 
 
/* Determine whether "1 << x" is relatively cheap in word_mode.  */
/* Determine whether "1 << x" is relatively cheap in word_mode.  */
 
 
static
static
bool lshift_cheap_p (void)
bool lshift_cheap_p (void)
{
{
  static bool init = false;
  static bool init = false;
  static bool cheap = true;
  static bool cheap = true;
 
 
  if (!init)
  if (!init)
    {
    {
      rtx reg = gen_rtx_REG (word_mode, 10000);
      rtx reg = gen_rtx_REG (word_mode, 10000);
      int cost = rtx_cost (gen_rtx_ASHIFT (word_mode, const1_rtx, reg), SET,
      int cost = rtx_cost (gen_rtx_ASHIFT (word_mode, const1_rtx, reg), SET,
                           optimize_insn_for_speed_p ());
                           optimize_insn_for_speed_p ());
      cheap = cost < COSTS_N_INSNS (3);
      cheap = cost < COSTS_N_INSNS (3);
      init = true;
      init = true;
    }
    }
 
 
  return cheap;
  return cheap;
}
}
 
 
/* Comparison function for qsort to order bit tests by decreasing
/* Comparison function for qsort to order bit tests by decreasing
   number of case nodes, i.e. the node with the most cases gets
   number of case nodes, i.e. the node with the most cases gets
   tested first.  */
   tested first.  */
 
 
static int
static int
case_bit_test_cmp (const void *p1, const void *p2)
case_bit_test_cmp (const void *p1, const void *p2)
{
{
  const struct case_bit_test *const d1 = (const struct case_bit_test *) p1;
  const struct case_bit_test *const d1 = (const struct case_bit_test *) p1;
  const struct case_bit_test *const d2 = (const struct case_bit_test *) p2;
  const struct case_bit_test *const d2 = (const struct case_bit_test *) p2;
 
 
  if (d2->bits != d1->bits)
  if (d2->bits != d1->bits)
    return d2->bits - d1->bits;
    return d2->bits - d1->bits;
 
 
  /* Stabilize the sort.  */
  /* Stabilize the sort.  */
  return CODE_LABEL_NUMBER (d2->label) - CODE_LABEL_NUMBER (d1->label);
  return CODE_LABEL_NUMBER (d2->label) - CODE_LABEL_NUMBER (d1->label);
}
}
 
 
/*  Expand a switch statement by a short sequence of bit-wise
/*  Expand a switch statement by a short sequence of bit-wise
    comparisons.  "switch(x)" is effectively converted into
    comparisons.  "switch(x)" is effectively converted into
    "if ((1 << (x-MINVAL)) & CST)" where CST and MINVAL are
    "if ((1 << (x-MINVAL)) & CST)" where CST and MINVAL are
    integer constants.
    integer constants.
 
 
    INDEX_EXPR is the value being switched on, which is of
    INDEX_EXPR is the value being switched on, which is of
    type INDEX_TYPE.  MINVAL is the lowest case value of in
    type INDEX_TYPE.  MINVAL is the lowest case value of in
    the case nodes, of INDEX_TYPE type, and RANGE is highest
    the case nodes, of INDEX_TYPE type, and RANGE is highest
    value minus MINVAL, also of type INDEX_TYPE.  NODES is
    value minus MINVAL, also of type INDEX_TYPE.  NODES is
    the set of case nodes, and DEFAULT_LABEL is the label to
    the set of case nodes, and DEFAULT_LABEL is the label to
    branch to should none of the cases match.
    branch to should none of the cases match.
 
 
    There *MUST* be MAX_CASE_BIT_TESTS or less unique case
    There *MUST* be MAX_CASE_BIT_TESTS or less unique case
    node targets.  */
    node targets.  */
 
 
static void
static void
emit_case_bit_tests (tree index_type, tree index_expr, tree minval,
emit_case_bit_tests (tree index_type, tree index_expr, tree minval,
                     tree range, case_node_ptr nodes, rtx default_label)
                     tree range, case_node_ptr nodes, rtx default_label)
{
{
  struct case_bit_test test[MAX_CASE_BIT_TESTS];
  struct case_bit_test test[MAX_CASE_BIT_TESTS];
  enum machine_mode mode;
  enum machine_mode mode;
  rtx expr, index, label;
  rtx expr, index, label;
  unsigned int i,j,lo,hi;
  unsigned int i,j,lo,hi;
  struct case_node *n;
  struct case_node *n;
  unsigned int count;
  unsigned int count;
 
 
  count = 0;
  count = 0;
  for (n = nodes; n; n = n->right)
  for (n = nodes; n; n = n->right)
    {
    {
      label = label_rtx (n->code_label);
      label = label_rtx (n->code_label);
      for (i = 0; i < count; i++)
      for (i = 0; i < count; i++)
        if (label == test[i].label)
        if (label == test[i].label)
          break;
          break;
 
 
      if (i == count)
      if (i == count)
        {
        {
          gcc_assert (count < MAX_CASE_BIT_TESTS);
          gcc_assert (count < MAX_CASE_BIT_TESTS);
          test[i].hi = 0;
          test[i].hi = 0;
          test[i].lo = 0;
          test[i].lo = 0;
          test[i].label = label;
          test[i].label = label;
          test[i].bits = 1;
          test[i].bits = 1;
          count++;
          count++;
        }
        }
      else
      else
        test[i].bits++;
        test[i].bits++;
 
 
      lo = tree_low_cst (fold_build2 (MINUS_EXPR, index_type,
      lo = tree_low_cst (fold_build2 (MINUS_EXPR, index_type,
                                      n->low, minval), 1);
                                      n->low, minval), 1);
      hi = tree_low_cst (fold_build2 (MINUS_EXPR, index_type,
      hi = tree_low_cst (fold_build2 (MINUS_EXPR, index_type,
                                      n->high, minval), 1);
                                      n->high, minval), 1);
      for (j = lo; j <= hi; j++)
      for (j = lo; j <= hi; j++)
        if (j >= HOST_BITS_PER_WIDE_INT)
        if (j >= HOST_BITS_PER_WIDE_INT)
          test[i].hi |= (HOST_WIDE_INT) 1 << (j - HOST_BITS_PER_INT);
          test[i].hi |= (HOST_WIDE_INT) 1 << (j - HOST_BITS_PER_INT);
        else
        else
          test[i].lo |= (HOST_WIDE_INT) 1 << j;
          test[i].lo |= (HOST_WIDE_INT) 1 << j;
    }
    }
 
 
  qsort (test, count, sizeof(*test), case_bit_test_cmp);
  qsort (test, count, sizeof(*test), case_bit_test_cmp);
 
 
  index_expr = fold_build2 (MINUS_EXPR, index_type,
  index_expr = fold_build2 (MINUS_EXPR, index_type,
                            fold_convert (index_type, index_expr),
                            fold_convert (index_type, index_expr),
                            fold_convert (index_type, minval));
                            fold_convert (index_type, minval));
  index = expand_normal (index_expr);
  index = expand_normal (index_expr);
  do_pending_stack_adjust ();
  do_pending_stack_adjust ();
 
 
  mode = TYPE_MODE (index_type);
  mode = TYPE_MODE (index_type);
  expr = expand_normal (range);
  expr = expand_normal (range);
  if (default_label)
  if (default_label)
    emit_cmp_and_jump_insns (index, expr, GTU, NULL_RTX, mode, 1,
    emit_cmp_and_jump_insns (index, expr, GTU, NULL_RTX, mode, 1,
                             default_label);
                             default_label);
 
 
  index = convert_to_mode (word_mode, index, 0);
  index = convert_to_mode (word_mode, index, 0);
  index = expand_binop (word_mode, ashl_optab, const1_rtx,
  index = expand_binop (word_mode, ashl_optab, const1_rtx,
                        index, NULL_RTX, 1, OPTAB_WIDEN);
                        index, NULL_RTX, 1, OPTAB_WIDEN);
 
 
  for (i = 0; i < count; i++)
  for (i = 0; i < count; i++)
    {
    {
      expr = immed_double_const (test[i].lo, test[i].hi, word_mode);
      expr = immed_double_const (test[i].lo, test[i].hi, word_mode);
      expr = expand_binop (word_mode, and_optab, index, expr,
      expr = expand_binop (word_mode, and_optab, index, expr,
                           NULL_RTX, 1, OPTAB_WIDEN);
                           NULL_RTX, 1, OPTAB_WIDEN);
      emit_cmp_and_jump_insns (expr, const0_rtx, NE, NULL_RTX,
      emit_cmp_and_jump_insns (expr, const0_rtx, NE, NULL_RTX,
                               word_mode, 1, test[i].label);
                               word_mode, 1, test[i].label);
    }
    }
 
 
  if (default_label)
  if (default_label)
    emit_jump (default_label);
    emit_jump (default_label);
}
}
 
 
#ifndef HAVE_casesi
#ifndef HAVE_casesi
#define HAVE_casesi 0
#define HAVE_casesi 0
#endif
#endif
 
 
#ifndef HAVE_tablejump
#ifndef HAVE_tablejump
#define HAVE_tablejump 0
#define HAVE_tablejump 0
#endif
#endif
 
 
/* Terminate a case (Pascal/Ada) or switch (C) statement
/* Terminate a case (Pascal/Ada) or switch (C) statement
   in which ORIG_INDEX is the expression to be tested.
   in which ORIG_INDEX is the expression to be tested.
   If ORIG_TYPE is not NULL, it is the original ORIG_INDEX
   If ORIG_TYPE is not NULL, it is the original ORIG_INDEX
   type as given in the source before any compiler conversions.
   type as given in the source before any compiler conversions.
   Generate the code to test it and jump to the right place.  */
   Generate the code to test it and jump to the right place.  */
 
 
void
void
expand_case (gimple stmt)
expand_case (gimple stmt)
{
{
  tree minval = NULL_TREE, maxval = NULL_TREE, range = NULL_TREE;
  tree minval = NULL_TREE, maxval = NULL_TREE, range = NULL_TREE;
  rtx default_label = 0;
  rtx default_label = 0;
  struct case_node *n;
  struct case_node *n;
  unsigned int count, uniq;
  unsigned int count, uniq;
  rtx index;
  rtx index;
  rtx table_label;
  rtx table_label;
  int ncases;
  int ncases;
  rtx *labelvec;
  rtx *labelvec;
  int i;
  int i;
  rtx before_case, end, lab;
  rtx before_case, end, lab;
 
 
  tree index_expr = gimple_switch_index (stmt);
  tree index_expr = gimple_switch_index (stmt);
  tree index_type = TREE_TYPE (index_expr);
  tree index_type = TREE_TYPE (index_expr);
  int unsignedp = TYPE_UNSIGNED (index_type);
  int unsignedp = TYPE_UNSIGNED (index_type);
 
 
  /* The insn after which the case dispatch should finally
  /* The insn after which the case dispatch should finally
     be emitted.  Zero for a dummy.  */
     be emitted.  Zero for a dummy.  */
  rtx start;
  rtx start;
 
 
  /* A list of case labels; it is first built as a list and it may then
  /* A list of case labels; it is first built as a list and it may then
     be rearranged into a nearly balanced binary tree.  */
     be rearranged into a nearly balanced binary tree.  */
  struct case_node *case_list = 0;
  struct case_node *case_list = 0;
 
 
  /* Label to jump to if no case matches.  */
  /* Label to jump to if no case matches.  */
  tree default_label_decl = NULL_TREE;
  tree default_label_decl = NULL_TREE;
 
 
  alloc_pool case_node_pool = create_alloc_pool ("struct case_node pool",
  alloc_pool case_node_pool = create_alloc_pool ("struct case_node pool",
                                                 sizeof (struct case_node),
                                                 sizeof (struct case_node),
                                                 100);
                                                 100);
 
 
  do_pending_stack_adjust ();
  do_pending_stack_adjust ();
 
 
  /* An ERROR_MARK occurs for various reasons including invalid data type.  */
  /* An ERROR_MARK occurs for various reasons including invalid data type.  */
  if (index_type != error_mark_node)
  if (index_type != error_mark_node)
    {
    {
      tree elt;
      tree elt;
      bitmap label_bitmap;
      bitmap label_bitmap;
      int stopi = 0;
      int stopi = 0;
 
 
      /* cleanup_tree_cfg removes all SWITCH_EXPR with their index
      /* cleanup_tree_cfg removes all SWITCH_EXPR with their index
         expressions being INTEGER_CST.  */
         expressions being INTEGER_CST.  */
      gcc_assert (TREE_CODE (index_expr) != INTEGER_CST);
      gcc_assert (TREE_CODE (index_expr) != INTEGER_CST);
 
 
      /* The default case, if ever taken, is the first element.  */
      /* The default case, if ever taken, is the first element.  */
      elt = gimple_switch_label (stmt, 0);
      elt = gimple_switch_label (stmt, 0);
      if (!CASE_LOW (elt) && !CASE_HIGH (elt))
      if (!CASE_LOW (elt) && !CASE_HIGH (elt))
        {
        {
          default_label_decl = CASE_LABEL (elt);
          default_label_decl = CASE_LABEL (elt);
          stopi = 1;
          stopi = 1;
        }
        }
 
 
      for (i = gimple_switch_num_labels (stmt) - 1; i >= stopi; --i)
      for (i = gimple_switch_num_labels (stmt) - 1; i >= stopi; --i)
        {
        {
          tree low, high;
          tree low, high;
          elt = gimple_switch_label (stmt, i);
          elt = gimple_switch_label (stmt, i);
 
 
          low = CASE_LOW (elt);
          low = CASE_LOW (elt);
          gcc_assert (low);
          gcc_assert (low);
          high = CASE_HIGH (elt);
          high = CASE_HIGH (elt);
 
 
          /* Discard empty ranges.  */
          /* Discard empty ranges.  */
          if (high && tree_int_cst_lt (high, low))
          if (high && tree_int_cst_lt (high, low))
            continue;
            continue;
 
 
          case_list = add_case_node (case_list, index_type, low, high,
          case_list = add_case_node (case_list, index_type, low, high,
                                     CASE_LABEL (elt), case_node_pool);
                                     CASE_LABEL (elt), case_node_pool);
        }
        }
 
 
 
 
      before_case = start = get_last_insn ();
      before_case = start = get_last_insn ();
      if (default_label_decl)
      if (default_label_decl)
        default_label = label_rtx (default_label_decl);
        default_label = label_rtx (default_label_decl);
 
 
      /* Get upper and lower bounds of case values.  */
      /* Get upper and lower bounds of case values.  */
 
 
      uniq = 0;
      uniq = 0;
      count = 0;
      count = 0;
      label_bitmap = BITMAP_ALLOC (NULL);
      label_bitmap = BITMAP_ALLOC (NULL);
      for (n = case_list; n; n = n->right)
      for (n = case_list; n; n = n->right)
        {
        {
          /* Count the elements and track the largest and smallest
          /* Count the elements and track the largest and smallest
             of them (treating them as signed even if they are not).  */
             of them (treating them as signed even if they are not).  */
          if (count++ == 0)
          if (count++ == 0)
            {
            {
              minval = n->low;
              minval = n->low;
              maxval = n->high;
              maxval = n->high;
            }
            }
          else
          else
            {
            {
              if (tree_int_cst_lt (n->low, minval))
              if (tree_int_cst_lt (n->low, minval))
                minval = n->low;
                minval = n->low;
              if (tree_int_cst_lt (maxval, n->high))
              if (tree_int_cst_lt (maxval, n->high))
                maxval = n->high;
                maxval = n->high;
            }
            }
          /* A range counts double, since it requires two compares.  */
          /* A range counts double, since it requires two compares.  */
          if (! tree_int_cst_equal (n->low, n->high))
          if (! tree_int_cst_equal (n->low, n->high))
            count++;
            count++;
 
 
          /* If we have not seen this label yet, then increase the
          /* If we have not seen this label yet, then increase the
             number of unique case node targets seen.  */
             number of unique case node targets seen.  */
          lab = label_rtx (n->code_label);
          lab = label_rtx (n->code_label);
          if (!bitmap_bit_p (label_bitmap, CODE_LABEL_NUMBER (lab)))
          if (!bitmap_bit_p (label_bitmap, CODE_LABEL_NUMBER (lab)))
            {
            {
              bitmap_set_bit (label_bitmap, CODE_LABEL_NUMBER (lab));
              bitmap_set_bit (label_bitmap, CODE_LABEL_NUMBER (lab));
              uniq++;
              uniq++;
            }
            }
        }
        }
 
 
      BITMAP_FREE (label_bitmap);
      BITMAP_FREE (label_bitmap);
 
 
      /* cleanup_tree_cfg removes all SWITCH_EXPR with a single
      /* cleanup_tree_cfg removes all SWITCH_EXPR with a single
         destination, such as one with a default case only.  However,
         destination, such as one with a default case only.  However,
         it doesn't remove cases that are out of range for the switch
         it doesn't remove cases that are out of range for the switch
         type, so we may still get a zero here.  */
         type, so we may still get a zero here.  */
      if (count == 0)
      if (count == 0)
        {
        {
          if (default_label)
          if (default_label)
            emit_jump (default_label);
            emit_jump (default_label);
          free_alloc_pool (case_node_pool);
          free_alloc_pool (case_node_pool);
          return;
          return;
        }
        }
 
 
      /* Compute span of values.  */
      /* Compute span of values.  */
      range = fold_build2 (MINUS_EXPR, index_type, maxval, minval);
      range = fold_build2 (MINUS_EXPR, index_type, maxval, minval);
 
 
      /* Try implementing this switch statement by a short sequence of
      /* Try implementing this switch statement by a short sequence of
         bit-wise comparisons.  However, we let the binary-tree case
         bit-wise comparisons.  However, we let the binary-tree case
         below handle constant index expressions.  */
         below handle constant index expressions.  */
      if (CASE_USE_BIT_TESTS
      if (CASE_USE_BIT_TESTS
          && ! TREE_CONSTANT (index_expr)
          && ! TREE_CONSTANT (index_expr)
          && compare_tree_int (range, GET_MODE_BITSIZE (word_mode)) < 0
          && compare_tree_int (range, GET_MODE_BITSIZE (word_mode)) < 0
          && compare_tree_int (range, 0) > 0
          && compare_tree_int (range, 0) > 0
          && lshift_cheap_p ()
          && lshift_cheap_p ()
          && ((uniq == 1 && count >= 3)
          && ((uniq == 1 && count >= 3)
              || (uniq == 2 && count >= 5)
              || (uniq == 2 && count >= 5)
              || (uniq == 3 && count >= 6)))
              || (uniq == 3 && count >= 6)))
        {
        {
          /* Optimize the case where all the case values fit in a
          /* Optimize the case where all the case values fit in a
             word without having to subtract MINVAL.  In this case,
             word without having to subtract MINVAL.  In this case,
             we can optimize away the subtraction.  */
             we can optimize away the subtraction.  */
          if (compare_tree_int (minval, 0) > 0
          if (compare_tree_int (minval, 0) > 0
              && compare_tree_int (maxval, GET_MODE_BITSIZE (word_mode)) < 0)
              && compare_tree_int (maxval, GET_MODE_BITSIZE (word_mode)) < 0)
            {
            {
              minval = build_int_cst (index_type, 0);
              minval = build_int_cst (index_type, 0);
              range = maxval;
              range = maxval;
            }
            }
          emit_case_bit_tests (index_type, index_expr, minval, range,
          emit_case_bit_tests (index_type, index_expr, minval, range,
                               case_list, default_label);
                               case_list, default_label);
        }
        }
 
 
      /* If range of values is much bigger than number of values,
      /* If range of values is much bigger than number of values,
         make a sequence of conditional branches instead of a dispatch.
         make a sequence of conditional branches instead of a dispatch.
         If the switch-index is a constant, do it this way
         If the switch-index is a constant, do it this way
         because we can optimize it.  */
         because we can optimize it.  */
 
 
      else if (count < targetm.case_values_threshold ()
      else if (count < targetm.case_values_threshold ()
               || compare_tree_int (range,
               || compare_tree_int (range,
                                    (optimize_insn_for_size_p () ? 3 : 10) * count) > 0
                                    (optimize_insn_for_size_p () ? 3 : 10) * count) > 0
               /* RANGE may be signed, and really large ranges will show up
               /* RANGE may be signed, and really large ranges will show up
                  as negative numbers.  */
                  as negative numbers.  */
               || compare_tree_int (range, 0) < 0
               || compare_tree_int (range, 0) < 0
#ifndef ASM_OUTPUT_ADDR_DIFF_ELT
#ifndef ASM_OUTPUT_ADDR_DIFF_ELT
               || flag_pic
               || flag_pic
#endif
#endif
               || !flag_jump_tables
               || !flag_jump_tables
               || TREE_CONSTANT (index_expr)
               || TREE_CONSTANT (index_expr)
               /* If neither casesi or tablejump is available, we can
               /* If neither casesi or tablejump is available, we can
                  only go this way.  */
                  only go this way.  */
               || (!HAVE_casesi && !HAVE_tablejump))
               || (!HAVE_casesi && !HAVE_tablejump))
        {
        {
          index = expand_normal (index_expr);
          index = expand_normal (index_expr);
 
 
          /* If the index is a short or char that we do not have
          /* If the index is a short or char that we do not have
             an insn to handle comparisons directly, convert it to
             an insn to handle comparisons directly, convert it to
             a full integer now, rather than letting each comparison
             a full integer now, rather than letting each comparison
             generate the conversion.  */
             generate the conversion.  */
 
 
          if (GET_MODE_CLASS (GET_MODE (index)) == MODE_INT
          if (GET_MODE_CLASS (GET_MODE (index)) == MODE_INT
              && ! have_insn_for (COMPARE, GET_MODE (index)))
              && ! have_insn_for (COMPARE, GET_MODE (index)))
            {
            {
              enum machine_mode wider_mode;
              enum machine_mode wider_mode;
              for (wider_mode = GET_MODE (index); wider_mode != VOIDmode;
              for (wider_mode = GET_MODE (index); wider_mode != VOIDmode;
                   wider_mode = GET_MODE_WIDER_MODE (wider_mode))
                   wider_mode = GET_MODE_WIDER_MODE (wider_mode))
                if (have_insn_for (COMPARE, wider_mode))
                if (have_insn_for (COMPARE, wider_mode))
                  {
                  {
                    index = convert_to_mode (wider_mode, index, unsignedp);
                    index = convert_to_mode (wider_mode, index, unsignedp);
                    break;
                    break;
                  }
                  }
            }
            }
 
 
          do_pending_stack_adjust ();
          do_pending_stack_adjust ();
 
 
          if (MEM_P (index))
          if (MEM_P (index))
            index = copy_to_reg (index);
            index = copy_to_reg (index);
 
 
          /* We generate a binary decision tree to select the
          /* We generate a binary decision tree to select the
             appropriate target code.  This is done as follows:
             appropriate target code.  This is done as follows:
 
 
             The list of cases is rearranged into a binary tree,
             The list of cases is rearranged into a binary tree,
             nearly optimal assuming equal probability for each case.
             nearly optimal assuming equal probability for each case.
 
 
             The tree is transformed into RTL, eliminating
             The tree is transformed into RTL, eliminating
             redundant test conditions at the same time.
             redundant test conditions at the same time.
 
 
             If program flow could reach the end of the
             If program flow could reach the end of the
             decision tree an unconditional jump to the
             decision tree an unconditional jump to the
             default code is emitted.  */
             default code is emitted.  */
 
 
          use_cost_table = estimate_case_costs (case_list);
          use_cost_table = estimate_case_costs (case_list);
          balance_case_nodes (&case_list, NULL);
          balance_case_nodes (&case_list, NULL);
          emit_case_nodes (index, case_list, default_label, index_type);
          emit_case_nodes (index, case_list, default_label, index_type);
          if (default_label)
          if (default_label)
            emit_jump (default_label);
            emit_jump (default_label);
        }
        }
      else
      else
        {
        {
          rtx fallback_label = label_rtx (case_list->code_label);
          rtx fallback_label = label_rtx (case_list->code_label);
          table_label = gen_label_rtx ();
          table_label = gen_label_rtx ();
          if (! try_casesi (index_type, index_expr, minval, range,
          if (! try_casesi (index_type, index_expr, minval, range,
                            table_label, default_label, fallback_label))
                            table_label, default_label, fallback_label))
            {
            {
              bool ok;
              bool ok;
 
 
              /* Index jumptables from zero for suitable values of
              /* Index jumptables from zero for suitable values of
                 minval to avoid a subtraction.  */
                 minval to avoid a subtraction.  */
              if (optimize_insn_for_speed_p ()
              if (optimize_insn_for_speed_p ()
                  && compare_tree_int (minval, 0) > 0
                  && compare_tree_int (minval, 0) > 0
                  && compare_tree_int (minval, 3) < 0)
                  && compare_tree_int (minval, 3) < 0)
                {
                {
                  minval = build_int_cst (index_type, 0);
                  minval = build_int_cst (index_type, 0);
                  range = maxval;
                  range = maxval;
                }
                }
 
 
              ok = try_tablejump (index_type, index_expr, minval, range,
              ok = try_tablejump (index_type, index_expr, minval, range,
                                  table_label, default_label);
                                  table_label, default_label);
              gcc_assert (ok);
              gcc_assert (ok);
            }
            }
 
 
          /* Get table of labels to jump to, in order of case index.  */
          /* Get table of labels to jump to, in order of case index.  */
 
 
          ncases = tree_low_cst (range, 0) + 1;
          ncases = tree_low_cst (range, 0) + 1;
          labelvec = XALLOCAVEC (rtx, ncases);
          labelvec = XALLOCAVEC (rtx, ncases);
          memset (labelvec, 0, ncases * sizeof (rtx));
          memset (labelvec, 0, ncases * sizeof (rtx));
 
 
          for (n = case_list; n; n = n->right)
          for (n = case_list; n; n = n->right)
            {
            {
              /* Compute the low and high bounds relative to the minimum
              /* Compute the low and high bounds relative to the minimum
                 value since that should fit in a HOST_WIDE_INT while the
                 value since that should fit in a HOST_WIDE_INT while the
                 actual values may not.  */
                 actual values may not.  */
              HOST_WIDE_INT i_low
              HOST_WIDE_INT i_low
                = tree_low_cst (fold_build2 (MINUS_EXPR, index_type,
                = tree_low_cst (fold_build2 (MINUS_EXPR, index_type,
                                             n->low, minval), 1);
                                             n->low, minval), 1);
              HOST_WIDE_INT i_high
              HOST_WIDE_INT i_high
                = tree_low_cst (fold_build2 (MINUS_EXPR, index_type,
                = tree_low_cst (fold_build2 (MINUS_EXPR, index_type,
                                             n->high, minval), 1);
                                             n->high, minval), 1);
              HOST_WIDE_INT i;
              HOST_WIDE_INT i;
 
 
              for (i = i_low; i <= i_high; i ++)
              for (i = i_low; i <= i_high; i ++)
                labelvec[i]
                labelvec[i]
                  = gen_rtx_LABEL_REF (Pmode, label_rtx (n->code_label));
                  = gen_rtx_LABEL_REF (Pmode, label_rtx (n->code_label));
            }
            }
 
 
          /* Fill in the gaps with the default.  We may have gaps at
          /* Fill in the gaps with the default.  We may have gaps at
             the beginning if we tried to avoid the minval subtraction,
             the beginning if we tried to avoid the minval subtraction,
             so substitute some label even if the default label was
             so substitute some label even if the default label was
             deemed unreachable.  */
             deemed unreachable.  */
          if (!default_label)
          if (!default_label)
            default_label = fallback_label;
            default_label = fallback_label;
          for (i = 0; i < ncases; i++)
          for (i = 0; i < ncases; i++)
            if (labelvec[i] == 0)
            if (labelvec[i] == 0)
              labelvec[i] = gen_rtx_LABEL_REF (Pmode, default_label);
              labelvec[i] = gen_rtx_LABEL_REF (Pmode, default_label);
 
 
          /* Output the table.  */
          /* Output the table.  */
          emit_label (table_label);
          emit_label (table_label);
 
 
          if (CASE_VECTOR_PC_RELATIVE || flag_pic)
          if (CASE_VECTOR_PC_RELATIVE || flag_pic)
            emit_jump_insn (gen_rtx_ADDR_DIFF_VEC (CASE_VECTOR_MODE,
            emit_jump_insn (gen_rtx_ADDR_DIFF_VEC (CASE_VECTOR_MODE,
                                                   gen_rtx_LABEL_REF (Pmode, table_label),
                                                   gen_rtx_LABEL_REF (Pmode, table_label),
                                                   gen_rtvec_v (ncases, labelvec),
                                                   gen_rtvec_v (ncases, labelvec),
                                                   const0_rtx, const0_rtx));
                                                   const0_rtx, const0_rtx));
          else
          else
            emit_jump_insn (gen_rtx_ADDR_VEC (CASE_VECTOR_MODE,
            emit_jump_insn (gen_rtx_ADDR_VEC (CASE_VECTOR_MODE,
                                              gen_rtvec_v (ncases, labelvec)));
                                              gen_rtvec_v (ncases, labelvec)));
 
 
          /* Record no drop-through after the table.  */
          /* Record no drop-through after the table.  */
          emit_barrier ();
          emit_barrier ();
        }
        }
 
 
      before_case = NEXT_INSN (before_case);
      before_case = NEXT_INSN (before_case);
      end = get_last_insn ();
      end = get_last_insn ();
      reorder_insns (before_case, end, start);
      reorder_insns (before_case, end, start);
    }
    }
 
 
  free_temp_slots ();
  free_temp_slots ();
  free_alloc_pool (case_node_pool);
  free_alloc_pool (case_node_pool);
}
}
 
 
/* Generate code to jump to LABEL if OP0 and OP1 are equal in mode MODE.  */
/* Generate code to jump to LABEL if OP0 and OP1 are equal in mode MODE.  */
 
 
static void
static void
do_jump_if_equal (enum machine_mode mode, rtx op0, rtx op1, rtx label,
do_jump_if_equal (enum machine_mode mode, rtx op0, rtx op1, rtx label,
                  int unsignedp)
                  int unsignedp)
{
{
  do_compare_rtx_and_jump (op0, op1, EQ, unsignedp, mode,
  do_compare_rtx_and_jump (op0, op1, EQ, unsignedp, mode,
                           NULL_RTX, NULL_RTX, label, -1);
                           NULL_RTX, NULL_RTX, label, -1);
}
}


/* Not all case values are encountered equally.  This function
/* Not all case values are encountered equally.  This function
   uses a heuristic to weight case labels, in cases where that
   uses a heuristic to weight case labels, in cases where that
   looks like a reasonable thing to do.
   looks like a reasonable thing to do.
 
 
   Right now, all we try to guess is text, and we establish the
   Right now, all we try to guess is text, and we establish the
   following weights:
   following weights:
 
 
        chars above space:      16
        chars above space:      16
        digits:                 16
        digits:                 16
        default:                12
        default:                12
        space, punct:           8
        space, punct:           8
        tab:                    4
        tab:                    4
        newline:                2
        newline:                2
        other "\" chars:        1
        other "\" chars:        1
        remaining chars:        0
        remaining chars:        0
 
 
   If we find any cases in the switch that are not either -1 or in the range
   If we find any cases in the switch that are not either -1 or in the range
   of valid ASCII characters, or are control characters other than those
   of valid ASCII characters, or are control characters other than those
   commonly used with "\", don't treat this switch scanning text.
   commonly used with "\", don't treat this switch scanning text.
 
 
   Return 1 if these nodes are suitable for cost estimation, otherwise
   Return 1 if these nodes are suitable for cost estimation, otherwise
   return 0.  */
   return 0.  */
 
 
static int
static int
estimate_case_costs (case_node_ptr node)
estimate_case_costs (case_node_ptr node)
{
{
  tree min_ascii = integer_minus_one_node;
  tree min_ascii = integer_minus_one_node;
  tree max_ascii = build_int_cst (TREE_TYPE (node->high), 127);
  tree max_ascii = build_int_cst (TREE_TYPE (node->high), 127);
  case_node_ptr n;
  case_node_ptr n;
  int i;
  int i;
 
 
  /* If we haven't already made the cost table, make it now.  Note that the
  /* If we haven't already made the cost table, make it now.  Note that the
     lower bound of the table is -1, not zero.  */
     lower bound of the table is -1, not zero.  */
 
 
  if (! cost_table_initialized)
  if (! cost_table_initialized)
    {
    {
      cost_table_initialized = 1;
      cost_table_initialized = 1;
 
 
      for (i = 0; i < 128; i++)
      for (i = 0; i < 128; i++)
        {
        {
          if (ISALNUM (i))
          if (ISALNUM (i))
            COST_TABLE (i) = 16;
            COST_TABLE (i) = 16;
          else if (ISPUNCT (i))
          else if (ISPUNCT (i))
            COST_TABLE (i) = 8;
            COST_TABLE (i) = 8;
          else if (ISCNTRL (i))
          else if (ISCNTRL (i))
            COST_TABLE (i) = -1;
            COST_TABLE (i) = -1;
        }
        }
 
 
      COST_TABLE (' ') = 8;
      COST_TABLE (' ') = 8;
      COST_TABLE ('\t') = 4;
      COST_TABLE ('\t') = 4;
      COST_TABLE ('\0') = 4;
      COST_TABLE ('\0') = 4;
      COST_TABLE ('\n') = 2;
      COST_TABLE ('\n') = 2;
      COST_TABLE ('\f') = 1;
      COST_TABLE ('\f') = 1;
      COST_TABLE ('\v') = 1;
      COST_TABLE ('\v') = 1;
      COST_TABLE ('\b') = 1;
      COST_TABLE ('\b') = 1;
    }
    }
 
 
  /* See if all the case expressions look like text.  It is text if the
  /* See if all the case expressions look like text.  It is text if the
     constant is >= -1 and the highest constant is <= 127.  Do all comparisons
     constant is >= -1 and the highest constant is <= 127.  Do all comparisons
     as signed arithmetic since we don't want to ever access cost_table with a
     as signed arithmetic since we don't want to ever access cost_table with a
     value less than -1.  Also check that none of the constants in a range
     value less than -1.  Also check that none of the constants in a range
     are strange control characters.  */
     are strange control characters.  */
 
 
  for (n = node; n; n = n->right)
  for (n = node; n; n = n->right)
    {
    {
      if (tree_int_cst_lt (n->low, min_ascii)
      if (tree_int_cst_lt (n->low, min_ascii)
          || tree_int_cst_lt (max_ascii, n->high))
          || tree_int_cst_lt (max_ascii, n->high))
        return 0;
        return 0;
 
 
      for (i = (HOST_WIDE_INT) TREE_INT_CST_LOW (n->low);
      for (i = (HOST_WIDE_INT) TREE_INT_CST_LOW (n->low);
           i <= (HOST_WIDE_INT) TREE_INT_CST_LOW (n->high); i++)
           i <= (HOST_WIDE_INT) TREE_INT_CST_LOW (n->high); i++)
        if (COST_TABLE (i) < 0)
        if (COST_TABLE (i) < 0)
          return 0;
          return 0;
    }
    }
 
 
  /* All interesting values are within the range of interesting
  /* All interesting values are within the range of interesting
     ASCII characters.  */
     ASCII characters.  */
  return 1;
  return 1;
}
}
 
 
/* Take an ordered list of case nodes
/* Take an ordered list of case nodes
   and transform them into a near optimal binary tree,
   and transform them into a near optimal binary tree,
   on the assumption that any target code selection value is as
   on the assumption that any target code selection value is as
   likely as any other.
   likely as any other.
 
 
   The transformation is performed by splitting the ordered
   The transformation is performed by splitting the ordered
   list into two equal sections plus a pivot.  The parts are
   list into two equal sections plus a pivot.  The parts are
   then attached to the pivot as left and right branches.  Each
   then attached to the pivot as left and right branches.  Each
   branch is then transformed recursively.  */
   branch is then transformed recursively.  */
 
 
static void
static void
balance_case_nodes (case_node_ptr *head, case_node_ptr parent)
balance_case_nodes (case_node_ptr *head, case_node_ptr parent)
{
{
  case_node_ptr np;
  case_node_ptr np;
 
 
  np = *head;
  np = *head;
  if (np)
  if (np)
    {
    {
      int cost = 0;
      int cost = 0;
      int i = 0;
      int i = 0;
      int ranges = 0;
      int ranges = 0;
      case_node_ptr *npp;
      case_node_ptr *npp;
      case_node_ptr left;
      case_node_ptr left;
 
 
      /* Count the number of entries on branch.  Also count the ranges.  */
      /* Count the number of entries on branch.  Also count the ranges.  */
 
 
      while (np)
      while (np)
        {
        {
          if (!tree_int_cst_equal (np->low, np->high))
          if (!tree_int_cst_equal (np->low, np->high))
            {
            {
              ranges++;
              ranges++;
              if (use_cost_table)
              if (use_cost_table)
                cost += COST_TABLE (TREE_INT_CST_LOW (np->high));
                cost += COST_TABLE (TREE_INT_CST_LOW (np->high));
            }
            }
 
 
          if (use_cost_table)
          if (use_cost_table)
            cost += COST_TABLE (TREE_INT_CST_LOW (np->low));
            cost += COST_TABLE (TREE_INT_CST_LOW (np->low));
 
 
          i++;
          i++;
          np = np->right;
          np = np->right;
        }
        }
 
 
      if (i > 2)
      if (i > 2)
        {
        {
          /* Split this list if it is long enough for that to help.  */
          /* Split this list if it is long enough for that to help.  */
          npp = head;
          npp = head;
          left = *npp;
          left = *npp;
          if (use_cost_table)
          if (use_cost_table)
            {
            {
              /* Find the place in the list that bisects the list's total cost,
              /* Find the place in the list that bisects the list's total cost,
                 Here I gets half the total cost.  */
                 Here I gets half the total cost.  */
              int n_moved = 0;
              int n_moved = 0;
              i = (cost + 1) / 2;
              i = (cost + 1) / 2;
              while (1)
              while (1)
                {
                {
                  /* Skip nodes while their cost does not reach that amount.  */
                  /* Skip nodes while their cost does not reach that amount.  */
                  if (!tree_int_cst_equal ((*npp)->low, (*npp)->high))
                  if (!tree_int_cst_equal ((*npp)->low, (*npp)->high))
                    i -= COST_TABLE (TREE_INT_CST_LOW ((*npp)->high));
                    i -= COST_TABLE (TREE_INT_CST_LOW ((*npp)->high));
                  i -= COST_TABLE (TREE_INT_CST_LOW ((*npp)->low));
                  i -= COST_TABLE (TREE_INT_CST_LOW ((*npp)->low));
                  if (i <= 0)
                  if (i <= 0)
                    break;
                    break;
                  npp = &(*npp)->right;
                  npp = &(*npp)->right;
                  n_moved += 1;
                  n_moved += 1;
                }
                }
              if (n_moved == 0)
              if (n_moved == 0)
                {
                {
                  /* Leave this branch lopsided, but optimize left-hand
                  /* Leave this branch lopsided, but optimize left-hand
                     side and fill in `parent' fields for right-hand side.  */
                     side and fill in `parent' fields for right-hand side.  */
                  np = *head;
                  np = *head;
                  np->parent = parent;
                  np->parent = parent;
                  balance_case_nodes (&np->left, np);
                  balance_case_nodes (&np->left, np);
                  for (; np->right; np = np->right)
                  for (; np->right; np = np->right)
                    np->right->parent = np;
                    np->right->parent = np;
                  return;
                  return;
                }
                }
            }
            }
          /* If there are just three nodes, split at the middle one.  */
          /* If there are just three nodes, split at the middle one.  */
          else if (i == 3)
          else if (i == 3)
            npp = &(*npp)->right;
            npp = &(*npp)->right;
          else
          else
            {
            {
              /* Find the place in the list that bisects the list's total cost,
              /* Find the place in the list that bisects the list's total cost,
                 where ranges count as 2.
                 where ranges count as 2.
                 Here I gets half the total cost.  */
                 Here I gets half the total cost.  */
              i = (i + ranges + 1) / 2;
              i = (i + ranges + 1) / 2;
              while (1)
              while (1)
                {
                {
                  /* Skip nodes while their cost does not reach that amount.  */
                  /* Skip nodes while their cost does not reach that amount.  */
                  if (!tree_int_cst_equal ((*npp)->low, (*npp)->high))
                  if (!tree_int_cst_equal ((*npp)->low, (*npp)->high))
                    i--;
                    i--;
                  i--;
                  i--;
                  if (i <= 0)
                  if (i <= 0)
                    break;
                    break;
                  npp = &(*npp)->right;
                  npp = &(*npp)->right;
                }
                }
            }
            }
          *head = np = *npp;
          *head = np = *npp;
          *npp = 0;
          *npp = 0;
          np->parent = parent;
          np->parent = parent;
          np->left = left;
          np->left = left;
 
 
          /* Optimize each of the two split parts.  */
          /* Optimize each of the two split parts.  */
          balance_case_nodes (&np->left, np);
          balance_case_nodes (&np->left, np);
          balance_case_nodes (&np->right, np);
          balance_case_nodes (&np->right, np);
        }
        }
      else
      else
        {
        {
          /* Else leave this branch as one level,
          /* Else leave this branch as one level,
             but fill in `parent' fields.  */
             but fill in `parent' fields.  */
          np = *head;
          np = *head;
          np->parent = parent;
          np->parent = parent;
          for (; np->right; np = np->right)
          for (; np->right; np = np->right)
            np->right->parent = np;
            np->right->parent = np;
        }
        }
    }
    }
}
}


/* Search the parent sections of the case node tree
/* Search the parent sections of the case node tree
   to see if a test for the lower bound of NODE would be redundant.
   to see if a test for the lower bound of NODE would be redundant.
   INDEX_TYPE is the type of the index expression.
   INDEX_TYPE is the type of the index expression.
 
 
   The instructions to generate the case decision tree are
   The instructions to generate the case decision tree are
   output in the same order as nodes are processed so it is
   output in the same order as nodes are processed so it is
   known that if a parent node checks the range of the current
   known that if a parent node checks the range of the current
   node minus one that the current node is bounded at its lower
   node minus one that the current node is bounded at its lower
   span.  Thus the test would be redundant.  */
   span.  Thus the test would be redundant.  */
 
 
static int
static int
node_has_low_bound (case_node_ptr node, tree index_type)
node_has_low_bound (case_node_ptr node, tree index_type)
{
{
  tree low_minus_one;
  tree low_minus_one;
  case_node_ptr pnode;
  case_node_ptr pnode;
 
 
  /* If the lower bound of this node is the lowest value in the index type,
  /* If the lower bound of this node is the lowest value in the index type,
     we need not test it.  */
     we need not test it.  */
 
 
  if (tree_int_cst_equal (node->low, TYPE_MIN_VALUE (index_type)))
  if (tree_int_cst_equal (node->low, TYPE_MIN_VALUE (index_type)))
    return 1;
    return 1;
 
 
  /* If this node has a left branch, the value at the left must be less
  /* If this node has a left branch, the value at the left must be less
     than that at this node, so it cannot be bounded at the bottom and
     than that at this node, so it cannot be bounded at the bottom and
     we need not bother testing any further.  */
     we need not bother testing any further.  */
 
 
  if (node->left)
  if (node->left)
    return 0;
    return 0;
 
 
  low_minus_one = fold_build2 (MINUS_EXPR, TREE_TYPE (node->low),
  low_minus_one = fold_build2 (MINUS_EXPR, TREE_TYPE (node->low),
                               node->low,
                               node->low,
                               build_int_cst (TREE_TYPE (node->low), 1));
                               build_int_cst (TREE_TYPE (node->low), 1));
 
 
  /* If the subtraction above overflowed, we can't verify anything.
  /* If the subtraction above overflowed, we can't verify anything.
     Otherwise, look for a parent that tests our value - 1.  */
     Otherwise, look for a parent that tests our value - 1.  */
 
 
  if (! tree_int_cst_lt (low_minus_one, node->low))
  if (! tree_int_cst_lt (low_minus_one, node->low))
    return 0;
    return 0;
 
 
  for (pnode = node->parent; pnode; pnode = pnode->parent)
  for (pnode = node->parent; pnode; pnode = pnode->parent)
    if (tree_int_cst_equal (low_minus_one, pnode->high))
    if (tree_int_cst_equal (low_minus_one, pnode->high))
      return 1;
      return 1;
 
 
  return 0;
  return 0;
}
}
 
 
/* Search the parent sections of the case node tree
/* Search the parent sections of the case node tree
   to see if a test for the upper bound of NODE would be redundant.
   to see if a test for the upper bound of NODE would be redundant.
   INDEX_TYPE is the type of the index expression.
   INDEX_TYPE is the type of the index expression.
 
 
   The instructions to generate the case decision tree are
   The instructions to generate the case decision tree are
   output in the same order as nodes are processed so it is
   output in the same order as nodes are processed so it is
   known that if a parent node checks the range of the current
   known that if a parent node checks the range of the current
   node plus one that the current node is bounded at its upper
   node plus one that the current node is bounded at its upper
   span.  Thus the test would be redundant.  */
   span.  Thus the test would be redundant.  */
 
 
static int
static int
node_has_high_bound (case_node_ptr node, tree index_type)
node_has_high_bound (case_node_ptr node, tree index_type)
{
{
  tree high_plus_one;
  tree high_plus_one;
  case_node_ptr pnode;
  case_node_ptr pnode;
 
 
  /* If there is no upper bound, obviously no test is needed.  */
  /* If there is no upper bound, obviously no test is needed.  */
 
 
  if (TYPE_MAX_VALUE (index_type) == NULL)
  if (TYPE_MAX_VALUE (index_type) == NULL)
    return 1;
    return 1;
 
 
  /* If the upper bound of this node is the highest value in the type
  /* If the upper bound of this node is the highest value in the type
     of the index expression, we need not test against it.  */
     of the index expression, we need not test against it.  */
 
 
  if (tree_int_cst_equal (node->high, TYPE_MAX_VALUE (index_type)))
  if (tree_int_cst_equal (node->high, TYPE_MAX_VALUE (index_type)))
    return 1;
    return 1;
 
 
  /* If this node has a right branch, the value at the right must be greater
  /* If this node has a right branch, the value at the right must be greater
     than that at this node, so it cannot be bounded at the top and
     than that at this node, so it cannot be bounded at the top and
     we need not bother testing any further.  */
     we need not bother testing any further.  */
 
 
  if (node->right)
  if (node->right)
    return 0;
    return 0;
 
 
  high_plus_one = fold_build2 (PLUS_EXPR, TREE_TYPE (node->high),
  high_plus_one = fold_build2 (PLUS_EXPR, TREE_TYPE (node->high),
                               node->high,
                               node->high,
                               build_int_cst (TREE_TYPE (node->high), 1));
                               build_int_cst (TREE_TYPE (node->high), 1));
 
 
  /* If the addition above overflowed, we can't verify anything.
  /* If the addition above overflowed, we can't verify anything.
     Otherwise, look for a parent that tests our value + 1.  */
     Otherwise, look for a parent that tests our value + 1.  */
 
 
  if (! tree_int_cst_lt (node->high, high_plus_one))
  if (! tree_int_cst_lt (node->high, high_plus_one))
    return 0;
    return 0;
 
 
  for (pnode = node->parent; pnode; pnode = pnode->parent)
  for (pnode = node->parent; pnode; pnode = pnode->parent)
    if (tree_int_cst_equal (high_plus_one, pnode->low))
    if (tree_int_cst_equal (high_plus_one, pnode->low))
      return 1;
      return 1;
 
 
  return 0;
  return 0;
}
}
 
 
/* Search the parent sections of the
/* Search the parent sections of the
   case node tree to see if both tests for the upper and lower
   case node tree to see if both tests for the upper and lower
   bounds of NODE would be redundant.  */
   bounds of NODE would be redundant.  */
 
 
static int
static int
node_is_bounded (case_node_ptr node, tree index_type)
node_is_bounded (case_node_ptr node, tree index_type)
{
{
  return (node_has_low_bound (node, index_type)
  return (node_has_low_bound (node, index_type)
          && node_has_high_bound (node, index_type));
          && node_has_high_bound (node, index_type));
}
}


/* Emit step-by-step code to select a case for the value of INDEX.
/* Emit step-by-step code to select a case for the value of INDEX.
   The thus generated decision tree follows the form of the
   The thus generated decision tree follows the form of the
   case-node binary tree NODE, whose nodes represent test conditions.
   case-node binary tree NODE, whose nodes represent test conditions.
   INDEX_TYPE is the type of the index of the switch.
   INDEX_TYPE is the type of the index of the switch.
 
 
   Care is taken to prune redundant tests from the decision tree
   Care is taken to prune redundant tests from the decision tree
   by detecting any boundary conditions already checked by
   by detecting any boundary conditions already checked by
   emitted rtx.  (See node_has_high_bound, node_has_low_bound
   emitted rtx.  (See node_has_high_bound, node_has_low_bound
   and node_is_bounded, above.)
   and node_is_bounded, above.)
 
 
   Where the test conditions can be shown to be redundant we emit
   Where the test conditions can be shown to be redundant we emit
   an unconditional jump to the target code.  As a further
   an unconditional jump to the target code.  As a further
   optimization, the subordinates of a tree node are examined to
   optimization, the subordinates of a tree node are examined to
   check for bounded nodes.  In this case conditional and/or
   check for bounded nodes.  In this case conditional and/or
   unconditional jumps as a result of the boundary check for the
   unconditional jumps as a result of the boundary check for the
   current node are arranged to target the subordinates associated
   current node are arranged to target the subordinates associated
   code for out of bound conditions on the current node.
   code for out of bound conditions on the current node.
 
 
   We can assume that when control reaches the code generated here,
   We can assume that when control reaches the code generated here,
   the index value has already been compared with the parents
   the index value has already been compared with the parents
   of this node, and determined to be on the same side of each parent
   of this node, and determined to be on the same side of each parent
   as this node is.  Thus, if this node tests for the value 51,
   as this node is.  Thus, if this node tests for the value 51,
   and a parent tested for 52, we don't need to consider
   and a parent tested for 52, we don't need to consider
   the possibility of a value greater than 51.  If another parent
   the possibility of a value greater than 51.  If another parent
   tests for the value 50, then this node need not test anything.  */
   tests for the value 50, then this node need not test anything.  */
 
 
static void
static void
emit_case_nodes (rtx index, case_node_ptr node, rtx default_label,
emit_case_nodes (rtx index, case_node_ptr node, rtx default_label,
                 tree index_type)
                 tree index_type)
{
{
  /* If INDEX has an unsigned type, we must make unsigned branches.  */
  /* If INDEX has an unsigned type, we must make unsigned branches.  */
  int unsignedp = TYPE_UNSIGNED (index_type);
  int unsignedp = TYPE_UNSIGNED (index_type);
  enum machine_mode mode = GET_MODE (index);
  enum machine_mode mode = GET_MODE (index);
  enum machine_mode imode = TYPE_MODE (index_type);
  enum machine_mode imode = TYPE_MODE (index_type);
 
 
  /* Handle indices detected as constant during RTL expansion.  */
  /* Handle indices detected as constant during RTL expansion.  */
  if (mode == VOIDmode)
  if (mode == VOIDmode)
    mode = imode;
    mode = imode;
 
 
  /* See if our parents have already tested everything for us.
  /* See if our parents have already tested everything for us.
     If they have, emit an unconditional jump for this node.  */
     If they have, emit an unconditional jump for this node.  */
  if (node_is_bounded (node, index_type))
  if (node_is_bounded (node, index_type))
    emit_jump (label_rtx (node->code_label));
    emit_jump (label_rtx (node->code_label));
 
 
  else if (tree_int_cst_equal (node->low, node->high))
  else if (tree_int_cst_equal (node->low, node->high))
    {
    {
      /* Node is single valued.  First see if the index expression matches
      /* Node is single valued.  First see if the index expression matches
         this node and then check our children, if any.  */
         this node and then check our children, if any.  */
 
 
      do_jump_if_equal (mode, index,
      do_jump_if_equal (mode, index,
                        convert_modes (mode, imode,
                        convert_modes (mode, imode,
                                       expand_normal (node->low),
                                       expand_normal (node->low),
                                       unsignedp),
                                       unsignedp),
                        label_rtx (node->code_label), unsignedp);
                        label_rtx (node->code_label), unsignedp);
 
 
      if (node->right != 0 && node->left != 0)
      if (node->right != 0 && node->left != 0)
        {
        {
          /* This node has children on both sides.
          /* This node has children on both sides.
             Dispatch to one side or the other
             Dispatch to one side or the other
             by comparing the index value with this node's value.
             by comparing the index value with this node's value.
             If one subtree is bounded, check that one first,
             If one subtree is bounded, check that one first,
             so we can avoid real branches in the tree.  */
             so we can avoid real branches in the tree.  */
 
 
          if (node_is_bounded (node->right, index_type))
          if (node_is_bounded (node->right, index_type))
            {
            {
              emit_cmp_and_jump_insns (index,
              emit_cmp_and_jump_insns (index,
                                       convert_modes
                                       convert_modes
                                       (mode, imode,
                                       (mode, imode,
                                        expand_normal (node->high),
                                        expand_normal (node->high),
                                        unsignedp),
                                        unsignedp),
                                       GT, NULL_RTX, mode, unsignedp,
                                       GT, NULL_RTX, mode, unsignedp,
                                       label_rtx (node->right->code_label));
                                       label_rtx (node->right->code_label));
              emit_case_nodes (index, node->left, default_label, index_type);
              emit_case_nodes (index, node->left, default_label, index_type);
            }
            }
 
 
          else if (node_is_bounded (node->left, index_type))
          else if (node_is_bounded (node->left, index_type))
            {
            {
              emit_cmp_and_jump_insns (index,
              emit_cmp_and_jump_insns (index,
                                       convert_modes
                                       convert_modes
                                       (mode, imode,
                                       (mode, imode,
                                        expand_normal (node->high),
                                        expand_normal (node->high),
                                        unsignedp),
                                        unsignedp),
                                       LT, NULL_RTX, mode, unsignedp,
                                       LT, NULL_RTX, mode, unsignedp,
                                       label_rtx (node->left->code_label));
                                       label_rtx (node->left->code_label));
              emit_case_nodes (index, node->right, default_label, index_type);
              emit_case_nodes (index, node->right, default_label, index_type);
            }
            }
 
 
          /* If both children are single-valued cases with no
          /* If both children are single-valued cases with no
             children, finish up all the work.  This way, we can save
             children, finish up all the work.  This way, we can save
             one ordered comparison.  */
             one ordered comparison.  */
          else if (tree_int_cst_equal (node->right->low, node->right->high)
          else if (tree_int_cst_equal (node->right->low, node->right->high)
                   && node->right->left == 0
                   && node->right->left == 0
                   && node->right->right == 0
                   && node->right->right == 0
                   && tree_int_cst_equal (node->left->low, node->left->high)
                   && tree_int_cst_equal (node->left->low, node->left->high)
                   && node->left->left == 0
                   && node->left->left == 0
                   && node->left->right == 0)
                   && node->left->right == 0)
            {
            {
              /* Neither node is bounded.  First distinguish the two sides;
              /* Neither node is bounded.  First distinguish the two sides;
                 then emit the code for one side at a time.  */
                 then emit the code for one side at a time.  */
 
 
              /* See if the value matches what the right hand side
              /* See if the value matches what the right hand side
                 wants.  */
                 wants.  */
              do_jump_if_equal (mode, index,
              do_jump_if_equal (mode, index,
                                convert_modes (mode, imode,
                                convert_modes (mode, imode,
                                               expand_normal (node->right->low),
                                               expand_normal (node->right->low),
                                               unsignedp),
                                               unsignedp),
                                label_rtx (node->right->code_label),
                                label_rtx (node->right->code_label),
                                unsignedp);
                                unsignedp);
 
 
              /* See if the value matches what the left hand side
              /* See if the value matches what the left hand side
                 wants.  */
                 wants.  */
              do_jump_if_equal (mode, index,
              do_jump_if_equal (mode, index,
                                convert_modes (mode, imode,
                                convert_modes (mode, imode,
                                               expand_normal (node->left->low),
                                               expand_normal (node->left->low),
                                               unsignedp),
                                               unsignedp),
                                label_rtx (node->left->code_label),
                                label_rtx (node->left->code_label),
                                unsignedp);
                                unsignedp);
            }
            }
 
 
          else
          else
            {
            {
              /* Neither node is bounded.  First distinguish the two sides;
              /* Neither node is bounded.  First distinguish the two sides;
                 then emit the code for one side at a time.  */
                 then emit the code for one side at a time.  */
 
 
              tree test_label
              tree test_label
                = build_decl (CURR_INSN_LOCATION,
                = build_decl (CURR_INSN_LOCATION,
                              LABEL_DECL, NULL_TREE, NULL_TREE);
                              LABEL_DECL, NULL_TREE, NULL_TREE);
 
 
              /* See if the value is on the right.  */
              /* See if the value is on the right.  */
              emit_cmp_and_jump_insns (index,
              emit_cmp_and_jump_insns (index,
                                       convert_modes
                                       convert_modes
                                       (mode, imode,
                                       (mode, imode,
                                        expand_normal (node->high),
                                        expand_normal (node->high),
                                        unsignedp),
                                        unsignedp),
                                       GT, NULL_RTX, mode, unsignedp,
                                       GT, NULL_RTX, mode, unsignedp,
                                       label_rtx (test_label));
                                       label_rtx (test_label));
 
 
              /* Value must be on the left.
              /* Value must be on the left.
                 Handle the left-hand subtree.  */
                 Handle the left-hand subtree.  */
              emit_case_nodes (index, node->left, default_label, index_type);
              emit_case_nodes (index, node->left, default_label, index_type);
              /* If left-hand subtree does nothing,
              /* If left-hand subtree does nothing,
                 go to default.  */
                 go to default.  */
              if (default_label)
              if (default_label)
                emit_jump (default_label);
                emit_jump (default_label);
 
 
              /* Code branches here for the right-hand subtree.  */
              /* Code branches here for the right-hand subtree.  */
              expand_label (test_label);
              expand_label (test_label);
              emit_case_nodes (index, node->right, default_label, index_type);
              emit_case_nodes (index, node->right, default_label, index_type);
            }
            }
        }
        }
 
 
      else if (node->right != 0 && node->left == 0)
      else if (node->right != 0 && node->left == 0)
        {
        {
          /* Here we have a right child but no left so we issue a conditional
          /* Here we have a right child but no left so we issue a conditional
             branch to default and process the right child.
             branch to default and process the right child.
 
 
             Omit the conditional branch to default if the right child
             Omit the conditional branch to default if the right child
             does not have any children and is single valued; it would
             does not have any children and is single valued; it would
             cost too much space to save so little time.  */
             cost too much space to save so little time.  */
 
 
          if (node->right->right || node->right->left
          if (node->right->right || node->right->left
              || !tree_int_cst_equal (node->right->low, node->right->high))
              || !tree_int_cst_equal (node->right->low, node->right->high))
            {
            {
              if (!node_has_low_bound (node, index_type))
              if (!node_has_low_bound (node, index_type))
                {
                {
                  emit_cmp_and_jump_insns (index,
                  emit_cmp_and_jump_insns (index,
                                           convert_modes
                                           convert_modes
                                           (mode, imode,
                                           (mode, imode,
                                            expand_normal (node->high),
                                            expand_normal (node->high),
                                            unsignedp),
                                            unsignedp),
                                           LT, NULL_RTX, mode, unsignedp,
                                           LT, NULL_RTX, mode, unsignedp,
                                           default_label);
                                           default_label);
                }
                }
 
 
              emit_case_nodes (index, node->right, default_label, index_type);
              emit_case_nodes (index, node->right, default_label, index_type);
            }
            }
          else
          else
            /* We cannot process node->right normally
            /* We cannot process node->right normally
               since we haven't ruled out the numbers less than
               since we haven't ruled out the numbers less than
               this node's value.  So handle node->right explicitly.  */
               this node's value.  So handle node->right explicitly.  */
            do_jump_if_equal (mode, index,
            do_jump_if_equal (mode, index,
                              convert_modes
                              convert_modes
                              (mode, imode,
                              (mode, imode,
                               expand_normal (node->right->low),
                               expand_normal (node->right->low),
                               unsignedp),
                               unsignedp),
                              label_rtx (node->right->code_label), unsignedp);
                              label_rtx (node->right->code_label), unsignedp);
        }
        }
 
 
      else if (node->right == 0 && node->left != 0)
      else if (node->right == 0 && node->left != 0)
        {
        {
          /* Just one subtree, on the left.  */
          /* Just one subtree, on the left.  */
          if (node->left->left || node->left->right
          if (node->left->left || node->left->right
              || !tree_int_cst_equal (node->left->low, node->left->high))
              || !tree_int_cst_equal (node->left->low, node->left->high))
            {
            {
              if (!node_has_high_bound (node, index_type))
              if (!node_has_high_bound (node, index_type))
                {
                {
                  emit_cmp_and_jump_insns (index,
                  emit_cmp_and_jump_insns (index,
                                           convert_modes
                                           convert_modes
                                           (mode, imode,
                                           (mode, imode,
                                            expand_normal (node->high),
                                            expand_normal (node->high),
                                            unsignedp),
                                            unsignedp),
                                           GT, NULL_RTX, mode, unsignedp,
                                           GT, NULL_RTX, mode, unsignedp,
                                           default_label);
                                           default_label);
                }
                }
 
 
              emit_case_nodes (index, node->left, default_label, index_type);
              emit_case_nodes (index, node->left, default_label, index_type);
            }
            }
          else
          else
            /* We cannot process node->left normally
            /* We cannot process node->left normally
               since we haven't ruled out the numbers less than
               since we haven't ruled out the numbers less than
               this node's value.  So handle node->left explicitly.  */
               this node's value.  So handle node->left explicitly.  */
            do_jump_if_equal (mode, index,
            do_jump_if_equal (mode, index,
                              convert_modes
                              convert_modes
                              (mode, imode,
                              (mode, imode,
                               expand_normal (node->left->low),
                               expand_normal (node->left->low),
                               unsignedp),
                               unsignedp),
                              label_rtx (node->left->code_label), unsignedp);
                              label_rtx (node->left->code_label), unsignedp);
        }
        }
    }
    }
  else
  else
    {
    {
      /* Node is a range.  These cases are very similar to those for a single
      /* Node is a range.  These cases are very similar to those for a single
         value, except that we do not start by testing whether this node
         value, except that we do not start by testing whether this node
         is the one to branch to.  */
         is the one to branch to.  */
 
 
      if (node->right != 0 && node->left != 0)
      if (node->right != 0 && node->left != 0)
        {
        {
          /* Node has subtrees on both sides.
          /* Node has subtrees on both sides.
             If the right-hand subtree is bounded,
             If the right-hand subtree is bounded,
             test for it first, since we can go straight there.
             test for it first, since we can go straight there.
             Otherwise, we need to make a branch in the control structure,
             Otherwise, we need to make a branch in the control structure,
             then handle the two subtrees.  */
             then handle the two subtrees.  */
          tree test_label = 0;
          tree test_label = 0;
 
 
          if (node_is_bounded (node->right, index_type))
          if (node_is_bounded (node->right, index_type))
            /* Right hand node is fully bounded so we can eliminate any
            /* Right hand node is fully bounded so we can eliminate any
               testing and branch directly to the target code.  */
               testing and branch directly to the target code.  */
            emit_cmp_and_jump_insns (index,
            emit_cmp_and_jump_insns (index,
                                     convert_modes
                                     convert_modes
                                     (mode, imode,
                                     (mode, imode,
                                      expand_normal (node->high),
                                      expand_normal (node->high),
                                      unsignedp),
                                      unsignedp),
                                     GT, NULL_RTX, mode, unsignedp,
                                     GT, NULL_RTX, mode, unsignedp,
                                     label_rtx (node->right->code_label));
                                     label_rtx (node->right->code_label));
          else
          else
            {
            {
              /* Right hand node requires testing.
              /* Right hand node requires testing.
                 Branch to a label where we will handle it later.  */
                 Branch to a label where we will handle it later.  */
 
 
              test_label = build_decl (CURR_INSN_LOCATION,
              test_label = build_decl (CURR_INSN_LOCATION,
                                       LABEL_DECL, NULL_TREE, NULL_TREE);
                                       LABEL_DECL, NULL_TREE, NULL_TREE);
              emit_cmp_and_jump_insns (index,
              emit_cmp_and_jump_insns (index,
                                       convert_modes
                                       convert_modes
                                       (mode, imode,
                                       (mode, imode,
                                        expand_normal (node->high),
                                        expand_normal (node->high),
                                        unsignedp),
                                        unsignedp),
                                       GT, NULL_RTX, mode, unsignedp,
                                       GT, NULL_RTX, mode, unsignedp,
                                       label_rtx (test_label));
                                       label_rtx (test_label));
            }
            }
 
 
          /* Value belongs to this node or to the left-hand subtree.  */
          /* Value belongs to this node or to the left-hand subtree.  */
 
 
          emit_cmp_and_jump_insns (index,
          emit_cmp_and_jump_insns (index,
                                   convert_modes
                                   convert_modes
                                   (mode, imode,
                                   (mode, imode,
                                    expand_normal (node->low),
                                    expand_normal (node->low),
                                    unsignedp),
                                    unsignedp),
                                   GE, NULL_RTX, mode, unsignedp,
                                   GE, NULL_RTX, mode, unsignedp,
                                   label_rtx (node->code_label));
                                   label_rtx (node->code_label));
 
 
          /* Handle the left-hand subtree.  */
          /* Handle the left-hand subtree.  */
          emit_case_nodes (index, node->left, default_label, index_type);
          emit_case_nodes (index, node->left, default_label, index_type);
 
 
          /* If right node had to be handled later, do that now.  */
          /* If right node had to be handled later, do that now.  */
 
 
          if (test_label)
          if (test_label)
            {
            {
              /* If the left-hand subtree fell through,
              /* If the left-hand subtree fell through,
                 don't let it fall into the right-hand subtree.  */
                 don't let it fall into the right-hand subtree.  */
              if (default_label)
              if (default_label)
                emit_jump (default_label);
                emit_jump (default_label);
 
 
              expand_label (test_label);
              expand_label (test_label);
              emit_case_nodes (index, node->right, default_label, index_type);
              emit_case_nodes (index, node->right, default_label, index_type);
            }
            }
        }
        }
 
 
      else if (node->right != 0 && node->left == 0)
      else if (node->right != 0 && node->left == 0)
        {
        {
          /* Deal with values to the left of this node,
          /* Deal with values to the left of this node,
             if they are possible.  */
             if they are possible.  */
          if (!node_has_low_bound (node, index_type))
          if (!node_has_low_bound (node, index_type))
            {
            {
              emit_cmp_and_jump_insns (index,
              emit_cmp_and_jump_insns (index,
                                       convert_modes
                                       convert_modes
                                       (mode, imode,
                                       (mode, imode,
                                        expand_normal (node->low),
                                        expand_normal (node->low),
                                        unsignedp),
                                        unsignedp),
                                       LT, NULL_RTX, mode, unsignedp,
                                       LT, NULL_RTX, mode, unsignedp,
                                       default_label);
                                       default_label);
            }
            }
 
 
          /* Value belongs to this node or to the right-hand subtree.  */
          /* Value belongs to this node or to the right-hand subtree.  */
 
 
          emit_cmp_and_jump_insns (index,
          emit_cmp_and_jump_insns (index,
                                   convert_modes
                                   convert_modes
                                   (mode, imode,
                                   (mode, imode,
                                    expand_normal (node->high),
                                    expand_normal (node->high),
                                    unsignedp),
                                    unsignedp),
                                   LE, NULL_RTX, mode, unsignedp,
                                   LE, NULL_RTX, mode, unsignedp,
                                   label_rtx (node->code_label));
                                   label_rtx (node->code_label));
 
 
          emit_case_nodes (index, node->right, default_label, index_type);
          emit_case_nodes (index, node->right, default_label, index_type);
        }
        }
 
 
      else if (node->right == 0 && node->left != 0)
      else if (node->right == 0 && node->left != 0)
        {
        {
          /* Deal with values to the right of this node,
          /* Deal with values to the right of this node,
             if they are possible.  */
             if they are possible.  */
          if (!node_has_high_bound (node, index_type))
          if (!node_has_high_bound (node, index_type))
            {
            {
              emit_cmp_and_jump_insns (index,
              emit_cmp_and_jump_insns (index,
                                       convert_modes
                                       convert_modes
                                       (mode, imode,
                                       (mode, imode,
                                        expand_normal (node->high),
                                        expand_normal (node->high),
                                        unsignedp),
                                        unsignedp),
                                       GT, NULL_RTX, mode, unsignedp,
                                       GT, NULL_RTX, mode, unsignedp,
                                       default_label);
                                       default_label);
            }
            }
 
 
          /* Value belongs to this node or to the left-hand subtree.  */
          /* Value belongs to this node or to the left-hand subtree.  */
 
 
          emit_cmp_and_jump_insns (index,
          emit_cmp_and_jump_insns (index,
                                   convert_modes
                                   convert_modes
                                   (mode, imode,
                                   (mode, imode,
                                    expand_normal (node->low),
                                    expand_normal (node->low),
                                    unsignedp),
                                    unsignedp),
                                   GE, NULL_RTX, mode, unsignedp,
                                   GE, NULL_RTX, mode, unsignedp,
                                   label_rtx (node->code_label));
                                   label_rtx (node->code_label));
 
 
          emit_case_nodes (index, node->left, default_label, index_type);
          emit_case_nodes (index, node->left, default_label, index_type);
        }
        }
 
 
      else
      else
        {
        {
          /* Node has no children so we check low and high bounds to remove
          /* Node has no children so we check low and high bounds to remove
             redundant tests.  Only one of the bounds can exist,
             redundant tests.  Only one of the bounds can exist,
             since otherwise this node is bounded--a case tested already.  */
             since otherwise this node is bounded--a case tested already.  */
          int high_bound = node_has_high_bound (node, index_type);
          int high_bound = node_has_high_bound (node, index_type);
          int low_bound = node_has_low_bound (node, index_type);
          int low_bound = node_has_low_bound (node, index_type);
 
 
          if (!high_bound && low_bound)
          if (!high_bound && low_bound)
            {
            {
              emit_cmp_and_jump_insns (index,
              emit_cmp_and_jump_insns (index,
                                       convert_modes
                                       convert_modes
                                       (mode, imode,
                                       (mode, imode,
                                        expand_normal (node->high),
                                        expand_normal (node->high),
                                        unsignedp),
                                        unsignedp),
                                       GT, NULL_RTX, mode, unsignedp,
                                       GT, NULL_RTX, mode, unsignedp,
                                       default_label);
                                       default_label);
            }
            }
 
 
          else if (!low_bound && high_bound)
          else if (!low_bound && high_bound)
            {
            {
              emit_cmp_and_jump_insns (index,
              emit_cmp_and_jump_insns (index,
                                       convert_modes
                                       convert_modes
                                       (mode, imode,
                                       (mode, imode,
                                        expand_normal (node->low),
                                        expand_normal (node->low),
                                        unsignedp),
                                        unsignedp),
                                       LT, NULL_RTX, mode, unsignedp,
                                       LT, NULL_RTX, mode, unsignedp,
                                       default_label);
                                       default_label);
            }
            }
          else if (!low_bound && !high_bound)
          else if (!low_bound && !high_bound)
            {
            {
              /* Widen LOW and HIGH to the same width as INDEX.  */
              /* Widen LOW and HIGH to the same width as INDEX.  */
              tree type = lang_hooks.types.type_for_mode (mode, unsignedp);
              tree type = lang_hooks.types.type_for_mode (mode, unsignedp);
              tree low = build1 (CONVERT_EXPR, type, node->low);
              tree low = build1 (CONVERT_EXPR, type, node->low);
              tree high = build1 (CONVERT_EXPR, type, node->high);
              tree high = build1 (CONVERT_EXPR, type, node->high);
              rtx low_rtx, new_index, new_bound;
              rtx low_rtx, new_index, new_bound;
 
 
              /* Instead of doing two branches, emit one unsigned branch for
              /* Instead of doing two branches, emit one unsigned branch for
                 (index-low) > (high-low).  */
                 (index-low) > (high-low).  */
              low_rtx = expand_expr (low, NULL_RTX, mode, EXPAND_NORMAL);
              low_rtx = expand_expr (low, NULL_RTX, mode, EXPAND_NORMAL);
              new_index = expand_simple_binop (mode, MINUS, index, low_rtx,
              new_index = expand_simple_binop (mode, MINUS, index, low_rtx,
                                               NULL_RTX, unsignedp,
                                               NULL_RTX, unsignedp,
                                               OPTAB_WIDEN);
                                               OPTAB_WIDEN);
              new_bound = expand_expr (fold_build2 (MINUS_EXPR, type,
              new_bound = expand_expr (fold_build2 (MINUS_EXPR, type,
                                                    high, low),
                                                    high, low),
                                       NULL_RTX, mode, EXPAND_NORMAL);
                                       NULL_RTX, mode, EXPAND_NORMAL);
 
 
              emit_cmp_and_jump_insns (new_index, new_bound, GT, NULL_RTX,
              emit_cmp_and_jump_insns (new_index, new_bound, GT, NULL_RTX,
                                       mode, 1, default_label);
                                       mode, 1, default_label);
            }
            }
 
 
          emit_jump (label_rtx (node->code_label));
          emit_jump (label_rtx (node->code_label));
        }
        }
    }
    }
}
}
 
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.