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

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-old/] [gcc-4.2.2/] [gcc/] [tree-stdarg.c] - Diff between revs 154 and 816

Go to most recent revision | Only display areas with differences | Details | Blame | View Log

Rev 154 Rev 816
/* Pass computing data for optimizing stdarg functions.
/* Pass computing data for optimizing stdarg functions.
   Copyright (C) 2004, 2005, 2007 Free Software Foundation, Inc.
   Copyright (C) 2004, 2005, 2007 Free Software Foundation, Inc.
   Contributed by Jakub Jelinek <jakub@redhat.com>
   Contributed by Jakub Jelinek <jakub@redhat.com>
 
 
This file is part of GCC.
This file is part of GCC.
 
 
GCC is free software; you can redistribute it and/or modify
GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
the Free Software Foundation; either version 3, or (at your option)
any later version.
any later version.
 
 
GCC is distributed in the hope that it will be useful,
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
GNU General Public License 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/>.  */
 
 
#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 "tree.h"
#include "tree.h"
#include "function.h"
#include "function.h"
#include "langhooks.h"
#include "langhooks.h"
#include "diagnostic.h"
#include "diagnostic.h"
#include "target.h"
#include "target.h"
#include "tree-flow.h"
#include "tree-flow.h"
#include "tree-pass.h"
#include "tree-pass.h"
#include "tree-stdarg.h"
#include "tree-stdarg.h"
 
 
/* A simple pass that attempts to optimize stdarg functions on architectures
/* A simple pass that attempts to optimize stdarg functions on architectures
   that need to save register arguments to stack on entry to stdarg functions.
   that need to save register arguments to stack on entry to stdarg functions.
   If the function doesn't use any va_start macros, no registers need to
   If the function doesn't use any va_start macros, no registers need to
   be saved.  If va_start macros are used, the va_list variables don't escape
   be saved.  If va_start macros are used, the va_list variables don't escape
   the function, it is only necessary to save registers that will be used
   the function, it is only necessary to save registers that will be used
   in va_arg macros.  E.g. if va_arg is only used with integral types
   in va_arg macros.  E.g. if va_arg is only used with integral types
   in the function, floating point registers don't need to be saved, etc.  */
   in the function, floating point registers don't need to be saved, etc.  */
 
 
 
 
/* Return true if basic block VA_ARG_BB is dominated by VA_START_BB and
/* Return true if basic block VA_ARG_BB is dominated by VA_START_BB and
   is executed at most as many times as VA_START_BB.  */
   is executed at most as many times as VA_START_BB.  */
 
 
static bool
static bool
reachable_at_most_once (basic_block va_arg_bb, basic_block va_start_bb)
reachable_at_most_once (basic_block va_arg_bb, basic_block va_start_bb)
{
{
  edge *stack, e;
  edge *stack, e;
  edge_iterator ei;
  edge_iterator ei;
  int sp;
  int sp;
  sbitmap visited;
  sbitmap visited;
  bool ret;
  bool ret;
 
 
  if (va_arg_bb == va_start_bb)
  if (va_arg_bb == va_start_bb)
    return true;
    return true;
 
 
  if (! dominated_by_p (CDI_DOMINATORS, va_arg_bb, va_start_bb))
  if (! dominated_by_p (CDI_DOMINATORS, va_arg_bb, va_start_bb))
    return false;
    return false;
 
 
  stack = XNEWVEC (edge, n_basic_blocks + 1);
  stack = XNEWVEC (edge, n_basic_blocks + 1);
  sp = 0;
  sp = 0;
 
 
  visited = sbitmap_alloc (last_basic_block);
  visited = sbitmap_alloc (last_basic_block);
  sbitmap_zero (visited);
  sbitmap_zero (visited);
  ret = true;
  ret = true;
 
 
  FOR_EACH_EDGE (e, ei, va_arg_bb->preds)
  FOR_EACH_EDGE (e, ei, va_arg_bb->preds)
    stack[sp++] = e;
    stack[sp++] = e;
 
 
  while (sp)
  while (sp)
    {
    {
      basic_block src;
      basic_block src;
 
 
      --sp;
      --sp;
      e = stack[sp];
      e = stack[sp];
      src = e->src;
      src = e->src;
 
 
      if (e->flags & EDGE_COMPLEX)
      if (e->flags & EDGE_COMPLEX)
        {
        {
          ret = false;
          ret = false;
          break;
          break;
        }
        }
 
 
      if (src == va_start_bb)
      if (src == va_start_bb)
        continue;
        continue;
 
 
      /* va_arg_bb can be executed more times than va_start_bb.  */
      /* va_arg_bb can be executed more times than va_start_bb.  */
      if (src == va_arg_bb)
      if (src == va_arg_bb)
        {
        {
          ret = false;
          ret = false;
          break;
          break;
        }
        }
 
 
      gcc_assert (src != ENTRY_BLOCK_PTR);
      gcc_assert (src != ENTRY_BLOCK_PTR);
 
 
      if (! TEST_BIT (visited, src->index))
      if (! TEST_BIT (visited, src->index))
        {
        {
          SET_BIT (visited, src->index);
          SET_BIT (visited, src->index);
          FOR_EACH_EDGE (e, ei, src->preds)
          FOR_EACH_EDGE (e, ei, src->preds)
            stack[sp++] = e;
            stack[sp++] = e;
        }
        }
    }
    }
 
 
  free (stack);
  free (stack);
  sbitmap_free (visited);
  sbitmap_free (visited);
  return ret;
  return ret;
}
}
 
 
 
 
/* For statement COUNTER = RHS, if RHS is COUNTER + constant,
/* For statement COUNTER = RHS, if RHS is COUNTER + constant,
   return constant, otherwise return (unsigned HOST_WIDE_INT) -1.
   return constant, otherwise return (unsigned HOST_WIDE_INT) -1.
   GPR_P is true if this is GPR counter.  */
   GPR_P is true if this is GPR counter.  */
 
 
static unsigned HOST_WIDE_INT
static unsigned HOST_WIDE_INT
va_list_counter_bump (struct stdarg_info *si, tree counter, tree rhs,
va_list_counter_bump (struct stdarg_info *si, tree counter, tree rhs,
                      bool gpr_p)
                      bool gpr_p)
{
{
  tree stmt, lhs, orig_lhs;
  tree stmt, lhs, orig_lhs;
  unsigned HOST_WIDE_INT ret = 0, val, counter_val;
  unsigned HOST_WIDE_INT ret = 0, val, counter_val;
  unsigned int max_size;
  unsigned int max_size;
 
 
  if (si->offsets == NULL)
  if (si->offsets == NULL)
    {
    {
      unsigned int i;
      unsigned int i;
 
 
      si->offsets = XNEWVEC (int, num_ssa_names);
      si->offsets = XNEWVEC (int, num_ssa_names);
      for (i = 0; i < num_ssa_names; ++i)
      for (i = 0; i < num_ssa_names; ++i)
        si->offsets[i] = -1;
        si->offsets[i] = -1;
    }
    }
 
 
  counter_val = gpr_p ? cfun->va_list_gpr_size : cfun->va_list_fpr_size;
  counter_val = gpr_p ? cfun->va_list_gpr_size : cfun->va_list_fpr_size;
  max_size = gpr_p ? VA_LIST_MAX_GPR_SIZE : VA_LIST_MAX_FPR_SIZE;
  max_size = gpr_p ? VA_LIST_MAX_GPR_SIZE : VA_LIST_MAX_FPR_SIZE;
  orig_lhs = lhs = rhs;
  orig_lhs = lhs = rhs;
  while (lhs)
  while (lhs)
    {
    {
      if (si->offsets[SSA_NAME_VERSION (lhs)] != -1)
      if (si->offsets[SSA_NAME_VERSION (lhs)] != -1)
        {
        {
          if (counter_val >= max_size)
          if (counter_val >= max_size)
            {
            {
              ret = max_size;
              ret = max_size;
              break;
              break;
            }
            }
 
 
          ret -= counter_val - si->offsets[SSA_NAME_VERSION (lhs)];
          ret -= counter_val - si->offsets[SSA_NAME_VERSION (lhs)];
          break;
          break;
        }
        }
 
 
      stmt = SSA_NAME_DEF_STMT (lhs);
      stmt = SSA_NAME_DEF_STMT (lhs);
 
 
      if (TREE_CODE (stmt) != MODIFY_EXPR
      if (TREE_CODE (stmt) != MODIFY_EXPR
          || TREE_OPERAND (stmt, 0) != lhs)
          || TREE_OPERAND (stmt, 0) != lhs)
        return (unsigned HOST_WIDE_INT) -1;
        return (unsigned HOST_WIDE_INT) -1;
 
 
      rhs = TREE_OPERAND (stmt, 1);
      rhs = TREE_OPERAND (stmt, 1);
      if (TREE_CODE (rhs) == WITH_SIZE_EXPR)
      if (TREE_CODE (rhs) == WITH_SIZE_EXPR)
        rhs = TREE_OPERAND (rhs, 0);
        rhs = TREE_OPERAND (rhs, 0);
 
 
      if (TREE_CODE (rhs) == SSA_NAME)
      if (TREE_CODE (rhs) == SSA_NAME)
        {
        {
          lhs = rhs;
          lhs = rhs;
          continue;
          continue;
        }
        }
 
 
      if ((TREE_CODE (rhs) == NOP_EXPR
      if ((TREE_CODE (rhs) == NOP_EXPR
           || TREE_CODE (rhs) == CONVERT_EXPR)
           || TREE_CODE (rhs) == CONVERT_EXPR)
          && TREE_CODE (TREE_OPERAND (rhs, 0)) == SSA_NAME)
          && TREE_CODE (TREE_OPERAND (rhs, 0)) == SSA_NAME)
        {
        {
          lhs = TREE_OPERAND (rhs, 0);
          lhs = TREE_OPERAND (rhs, 0);
          continue;
          continue;
        }
        }
 
 
      if (TREE_CODE (rhs) == PLUS_EXPR
      if (TREE_CODE (rhs) == PLUS_EXPR
          && TREE_CODE (TREE_OPERAND (rhs, 0)) == SSA_NAME
          && TREE_CODE (TREE_OPERAND (rhs, 0)) == SSA_NAME
          && TREE_CODE (TREE_OPERAND (rhs, 1)) == INTEGER_CST
          && TREE_CODE (TREE_OPERAND (rhs, 1)) == INTEGER_CST
          && host_integerp (TREE_OPERAND (rhs, 1), 1))
          && host_integerp (TREE_OPERAND (rhs, 1), 1))
        {
        {
          ret += tree_low_cst (TREE_OPERAND (rhs, 1), 1);
          ret += tree_low_cst (TREE_OPERAND (rhs, 1), 1);
          lhs = TREE_OPERAND (rhs, 0);
          lhs = TREE_OPERAND (rhs, 0);
          continue;
          continue;
        }
        }
 
 
      if (TREE_CODE (counter) != TREE_CODE (rhs))
      if (TREE_CODE (counter) != TREE_CODE (rhs))
        return (unsigned HOST_WIDE_INT) -1;
        return (unsigned HOST_WIDE_INT) -1;
 
 
      if (TREE_CODE (counter) == COMPONENT_REF)
      if (TREE_CODE (counter) == COMPONENT_REF)
        {
        {
          if (get_base_address (counter) != get_base_address (rhs)
          if (get_base_address (counter) != get_base_address (rhs)
              || TREE_CODE (TREE_OPERAND (rhs, 1)) != FIELD_DECL
              || TREE_CODE (TREE_OPERAND (rhs, 1)) != FIELD_DECL
              || TREE_OPERAND (counter, 1) != TREE_OPERAND (rhs, 1))
              || TREE_OPERAND (counter, 1) != TREE_OPERAND (rhs, 1))
            return (unsigned HOST_WIDE_INT) -1;
            return (unsigned HOST_WIDE_INT) -1;
        }
        }
      else if (counter != rhs)
      else if (counter != rhs)
        return (unsigned HOST_WIDE_INT) -1;
        return (unsigned HOST_WIDE_INT) -1;
 
 
      lhs = NULL;
      lhs = NULL;
    }
    }
 
 
  lhs = orig_lhs;
  lhs = orig_lhs;
  val = ret + counter_val;
  val = ret + counter_val;
  while (lhs)
  while (lhs)
    {
    {
      if (si->offsets[SSA_NAME_VERSION (lhs)] != -1)
      if (si->offsets[SSA_NAME_VERSION (lhs)] != -1)
        break;
        break;
 
 
      if (val >= max_size)
      if (val >= max_size)
        si->offsets[SSA_NAME_VERSION (lhs)] = max_size;
        si->offsets[SSA_NAME_VERSION (lhs)] = max_size;
      else
      else
        si->offsets[SSA_NAME_VERSION (lhs)] = val;
        si->offsets[SSA_NAME_VERSION (lhs)] = val;
 
 
      stmt = SSA_NAME_DEF_STMT (lhs);
      stmt = SSA_NAME_DEF_STMT (lhs);
 
 
      rhs = TREE_OPERAND (stmt, 1);
      rhs = TREE_OPERAND (stmt, 1);
      if (TREE_CODE (rhs) == WITH_SIZE_EXPR)
      if (TREE_CODE (rhs) == WITH_SIZE_EXPR)
        rhs = TREE_OPERAND (rhs, 0);
        rhs = TREE_OPERAND (rhs, 0);
 
 
      if (TREE_CODE (rhs) == SSA_NAME)
      if (TREE_CODE (rhs) == SSA_NAME)
        {
        {
          lhs = rhs;
          lhs = rhs;
          continue;
          continue;
        }
        }
 
 
      if ((TREE_CODE (rhs) == NOP_EXPR
      if ((TREE_CODE (rhs) == NOP_EXPR
           || TREE_CODE (rhs) == CONVERT_EXPR)
           || TREE_CODE (rhs) == CONVERT_EXPR)
          && TREE_CODE (TREE_OPERAND (rhs, 0)) == SSA_NAME)
          && TREE_CODE (TREE_OPERAND (rhs, 0)) == SSA_NAME)
        {
        {
          lhs = TREE_OPERAND (rhs, 0);
          lhs = TREE_OPERAND (rhs, 0);
          continue;
          continue;
        }
        }
 
 
      if (TREE_CODE (rhs) == PLUS_EXPR
      if (TREE_CODE (rhs) == PLUS_EXPR
          && TREE_CODE (TREE_OPERAND (rhs, 0)) == SSA_NAME
          && TREE_CODE (TREE_OPERAND (rhs, 0)) == SSA_NAME
          && TREE_CODE (TREE_OPERAND (rhs, 1)) == INTEGER_CST
          && TREE_CODE (TREE_OPERAND (rhs, 1)) == INTEGER_CST
          && host_integerp (TREE_OPERAND (rhs, 1), 1))
          && host_integerp (TREE_OPERAND (rhs, 1), 1))
        {
        {
          val -= tree_low_cst (TREE_OPERAND (rhs, 1), 1);
          val -= tree_low_cst (TREE_OPERAND (rhs, 1), 1);
          lhs = TREE_OPERAND (rhs, 0);
          lhs = TREE_OPERAND (rhs, 0);
          continue;
          continue;
        }
        }
 
 
      lhs = NULL;
      lhs = NULL;
    }
    }
 
 
  return ret;
  return ret;
}
}
 
 
 
 
/* Called by walk_tree to look for references to va_list variables.  */
/* Called by walk_tree to look for references to va_list variables.  */
 
 
static tree
static tree
find_va_list_reference (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED,
find_va_list_reference (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED,
                        void *data)
                        void *data)
{
{
  bitmap va_list_vars = (bitmap) data;
  bitmap va_list_vars = (bitmap) data;
  tree var = *tp;
  tree var = *tp;
 
 
  if (TREE_CODE (var) == SSA_NAME)
  if (TREE_CODE (var) == SSA_NAME)
    var = SSA_NAME_VAR (var);
    var = SSA_NAME_VAR (var);
 
 
  if (TREE_CODE (var) == VAR_DECL
  if (TREE_CODE (var) == VAR_DECL
      && bitmap_bit_p (va_list_vars, DECL_UID (var)))
      && bitmap_bit_p (va_list_vars, DECL_UID (var)))
    return var;
    return var;
 
 
  return NULL_TREE;
  return NULL_TREE;
}
}
 
 
 
 
/* Helper function of va_list_counter_struct_op.  Compute
/* Helper function of va_list_counter_struct_op.  Compute
   cfun->va_list_{g,f}pr_size.  AP is a va_list GPR/FPR counter,
   cfun->va_list_{g,f}pr_size.  AP is a va_list GPR/FPR counter,
   if WRITE_P is true, seen in AP = VAR, otherwise seen in VAR = AP
   if WRITE_P is true, seen in AP = VAR, otherwise seen in VAR = AP
   statement.  GPR_P is true if AP is a GPR counter, false if it is
   statement.  GPR_P is true if AP is a GPR counter, false if it is
   a FPR counter.  */
   a FPR counter.  */
 
 
static void
static void
va_list_counter_op (struct stdarg_info *si, tree ap, tree var, bool gpr_p,
va_list_counter_op (struct stdarg_info *si, tree ap, tree var, bool gpr_p,
                    bool write_p)
                    bool write_p)
{
{
  unsigned HOST_WIDE_INT increment;
  unsigned HOST_WIDE_INT increment;
 
 
  if (si->compute_sizes < 0)
  if (si->compute_sizes < 0)
    {
    {
      si->compute_sizes = 0;
      si->compute_sizes = 0;
      if (si->va_start_count == 1
      if (si->va_start_count == 1
          && reachable_at_most_once (si->bb, si->va_start_bb))
          && reachable_at_most_once (si->bb, si->va_start_bb))
        si->compute_sizes = 1;
        si->compute_sizes = 1;
 
 
      if (dump_file && (dump_flags & TDF_DETAILS))
      if (dump_file && (dump_flags & TDF_DETAILS))
        fprintf (dump_file,
        fprintf (dump_file,
                 "bb%d will %sbe executed at most once for each va_start "
                 "bb%d will %sbe executed at most once for each va_start "
                 "in bb%d\n", si->bb->index, si->compute_sizes ? "" : "not ",
                 "in bb%d\n", si->bb->index, si->compute_sizes ? "" : "not ",
                 si->va_start_bb->index);
                 si->va_start_bb->index);
    }
    }
 
 
  if (write_p
  if (write_p
      && si->compute_sizes
      && si->compute_sizes
      && (increment = va_list_counter_bump (si, ap, var, gpr_p)) + 1 > 1)
      && (increment = va_list_counter_bump (si, ap, var, gpr_p)) + 1 > 1)
    {
    {
      if (gpr_p && cfun->va_list_gpr_size + increment < VA_LIST_MAX_GPR_SIZE)
      if (gpr_p && cfun->va_list_gpr_size + increment < VA_LIST_MAX_GPR_SIZE)
        {
        {
          cfun->va_list_gpr_size += increment;
          cfun->va_list_gpr_size += increment;
          return;
          return;
        }
        }
 
 
      if (!gpr_p && cfun->va_list_fpr_size + increment < VA_LIST_MAX_FPR_SIZE)
      if (!gpr_p && cfun->va_list_fpr_size + increment < VA_LIST_MAX_FPR_SIZE)
        {
        {
          cfun->va_list_fpr_size += increment;
          cfun->va_list_fpr_size += increment;
          return;
          return;
        }
        }
    }
    }
 
 
  if (write_p || !si->compute_sizes)
  if (write_p || !si->compute_sizes)
    {
    {
      if (gpr_p)
      if (gpr_p)
        cfun->va_list_gpr_size = VA_LIST_MAX_GPR_SIZE;
        cfun->va_list_gpr_size = VA_LIST_MAX_GPR_SIZE;
      else
      else
        cfun->va_list_fpr_size = VA_LIST_MAX_FPR_SIZE;
        cfun->va_list_fpr_size = VA_LIST_MAX_FPR_SIZE;
    }
    }
}
}
 
 
 
 
/* If AP is a va_list GPR/FPR counter, compute cfun->va_list_{g,f}pr_size.
/* If AP is a va_list GPR/FPR counter, compute cfun->va_list_{g,f}pr_size.
   If WRITE_P is true, AP has been seen in AP = VAR assignment, if WRITE_P
   If WRITE_P is true, AP has been seen in AP = VAR assignment, if WRITE_P
   is false, AP has been seen in VAR = AP assignment.
   is false, AP has been seen in VAR = AP assignment.
   Return true if the AP = VAR (resp. VAR = AP) statement is a recognized
   Return true if the AP = VAR (resp. VAR = AP) statement is a recognized
   va_arg operation that doesn't cause the va_list variable to escape
   va_arg operation that doesn't cause the va_list variable to escape
   current function.  */
   current function.  */
 
 
static bool
static bool
va_list_counter_struct_op (struct stdarg_info *si, tree ap, tree var,
va_list_counter_struct_op (struct stdarg_info *si, tree ap, tree var,
                           bool write_p)
                           bool write_p)
{
{
  tree base;
  tree base;
 
 
  if (TREE_CODE (ap) != COMPONENT_REF
  if (TREE_CODE (ap) != COMPONENT_REF
      || TREE_CODE (TREE_OPERAND (ap, 1)) != FIELD_DECL)
      || TREE_CODE (TREE_OPERAND (ap, 1)) != FIELD_DECL)
    return false;
    return false;
 
 
  if (TREE_CODE (var) != SSA_NAME
  if (TREE_CODE (var) != SSA_NAME
      || bitmap_bit_p (si->va_list_vars, DECL_UID (SSA_NAME_VAR (var))))
      || bitmap_bit_p (si->va_list_vars, DECL_UID (SSA_NAME_VAR (var))))
    return false;
    return false;
 
 
  base = get_base_address (ap);
  base = get_base_address (ap);
  if (TREE_CODE (base) != VAR_DECL
  if (TREE_CODE (base) != VAR_DECL
      || !bitmap_bit_p (si->va_list_vars, DECL_UID (base)))
      || !bitmap_bit_p (si->va_list_vars, DECL_UID (base)))
    return false;
    return false;
 
 
  if (TREE_OPERAND (ap, 1) == va_list_gpr_counter_field)
  if (TREE_OPERAND (ap, 1) == va_list_gpr_counter_field)
    va_list_counter_op (si, ap, var, true, write_p);
    va_list_counter_op (si, ap, var, true, write_p);
  else if (TREE_OPERAND (ap, 1) == va_list_fpr_counter_field)
  else if (TREE_OPERAND (ap, 1) == va_list_fpr_counter_field)
    va_list_counter_op (si, ap, var, false, write_p);
    va_list_counter_op (si, ap, var, false, write_p);
 
 
  return true;
  return true;
}
}
 
 
 
 
/* Check for TEM = AP.  Return true if found and the caller shouldn't
/* Check for TEM = AP.  Return true if found and the caller shouldn't
   search for va_list references in the statement.  */
   search for va_list references in the statement.  */
 
 
static bool
static bool
va_list_ptr_read (struct stdarg_info *si, tree ap, tree tem)
va_list_ptr_read (struct stdarg_info *si, tree ap, tree tem)
{
{
  if (TREE_CODE (ap) != VAR_DECL
  if (TREE_CODE (ap) != VAR_DECL
      || !bitmap_bit_p (si->va_list_vars, DECL_UID (ap)))
      || !bitmap_bit_p (si->va_list_vars, DECL_UID (ap)))
    return false;
    return false;
 
 
  if (TREE_CODE (tem) != SSA_NAME
  if (TREE_CODE (tem) != SSA_NAME
      || bitmap_bit_p (si->va_list_vars,
      || bitmap_bit_p (si->va_list_vars,
                       DECL_UID (SSA_NAME_VAR (tem)))
                       DECL_UID (SSA_NAME_VAR (tem)))
      || is_global_var (SSA_NAME_VAR (tem)))
      || is_global_var (SSA_NAME_VAR (tem)))
    return false;
    return false;
 
 
  if (si->compute_sizes < 0)
  if (si->compute_sizes < 0)
    {
    {
      si->compute_sizes = 0;
      si->compute_sizes = 0;
      if (si->va_start_count == 1
      if (si->va_start_count == 1
          && reachable_at_most_once (si->bb, si->va_start_bb))
          && reachable_at_most_once (si->bb, si->va_start_bb))
        si->compute_sizes = 1;
        si->compute_sizes = 1;
 
 
      if (dump_file && (dump_flags & TDF_DETAILS))
      if (dump_file && (dump_flags & TDF_DETAILS))
        fprintf (dump_file,
        fprintf (dump_file,
                 "bb%d will %sbe executed at most once for each va_start "
                 "bb%d will %sbe executed at most once for each va_start "
                 "in bb%d\n", si->bb->index, si->compute_sizes ? "" : "not ",
                 "in bb%d\n", si->bb->index, si->compute_sizes ? "" : "not ",
                 si->va_start_bb->index);
                 si->va_start_bb->index);
    }
    }
 
 
  /* For void * or char * va_list types, there is just one counter.
  /* For void * or char * va_list types, there is just one counter.
     If va_arg is used in a loop, we don't know how many registers need
     If va_arg is used in a loop, we don't know how many registers need
     saving.  */
     saving.  */
  if (! si->compute_sizes)
  if (! si->compute_sizes)
    return false;
    return false;
 
 
  if (va_list_counter_bump (si, ap, tem, true) == (unsigned HOST_WIDE_INT) -1)
  if (va_list_counter_bump (si, ap, tem, true) == (unsigned HOST_WIDE_INT) -1)
    return false;
    return false;
 
 
  /* Note the temporary, as we need to track whether it doesn't escape
  /* Note the temporary, as we need to track whether it doesn't escape
     the current function.  */
     the current function.  */
  bitmap_set_bit (si->va_list_escape_vars,
  bitmap_set_bit (si->va_list_escape_vars,
                  DECL_UID (SSA_NAME_VAR (tem)));
                  DECL_UID (SSA_NAME_VAR (tem)));
  return true;
  return true;
}
}
 
 
 
 
/* Check for:
/* Check for:
     tem1 = AP;
     tem1 = AP;
     TEM2 = tem1 + CST;
     TEM2 = tem1 + CST;
     AP = TEM2;
     AP = TEM2;
   sequence and update cfun->va_list_gpr_size.  Return true if found.  */
   sequence and update cfun->va_list_gpr_size.  Return true if found.  */
 
 
static bool
static bool
va_list_ptr_write (struct stdarg_info *si, tree ap, tree tem2)
va_list_ptr_write (struct stdarg_info *si, tree ap, tree tem2)
{
{
  unsigned HOST_WIDE_INT increment;
  unsigned HOST_WIDE_INT increment;
 
 
  if (TREE_CODE (ap) != VAR_DECL
  if (TREE_CODE (ap) != VAR_DECL
      || !bitmap_bit_p (si->va_list_vars, DECL_UID (ap)))
      || !bitmap_bit_p (si->va_list_vars, DECL_UID (ap)))
    return false;
    return false;
 
 
  if (TREE_CODE (tem2) != SSA_NAME
  if (TREE_CODE (tem2) != SSA_NAME
      || bitmap_bit_p (si->va_list_vars, DECL_UID (SSA_NAME_VAR (tem2))))
      || bitmap_bit_p (si->va_list_vars, DECL_UID (SSA_NAME_VAR (tem2))))
    return false;
    return false;
 
 
  if (si->compute_sizes <= 0)
  if (si->compute_sizes <= 0)
    return false;
    return false;
 
 
  increment = va_list_counter_bump (si, ap, tem2, true);
  increment = va_list_counter_bump (si, ap, tem2, true);
  if (increment + 1 <= 1)
  if (increment + 1 <= 1)
    return false;
    return false;
 
 
  if (cfun->va_list_gpr_size + increment < VA_LIST_MAX_GPR_SIZE)
  if (cfun->va_list_gpr_size + increment < VA_LIST_MAX_GPR_SIZE)
    cfun->va_list_gpr_size += increment;
    cfun->va_list_gpr_size += increment;
  else
  else
    cfun->va_list_gpr_size = VA_LIST_MAX_GPR_SIZE;
    cfun->va_list_gpr_size = VA_LIST_MAX_GPR_SIZE;
 
 
  return true;
  return true;
}
}
 
 
 
 
/* If RHS is X, (some type *) X or X + CST for X a temporary variable
/* If RHS is X, (some type *) X or X + CST for X a temporary variable
   containing value of some va_list variable plus optionally some constant,
   containing value of some va_list variable plus optionally some constant,
   either set si->va_list_escapes or add LHS to si->va_list_escape_vars,
   either set si->va_list_escapes or add LHS to si->va_list_escape_vars,
   depending whether LHS is a function local temporary.  */
   depending whether LHS is a function local temporary.  */
 
 
static void
static void
check_va_list_escapes (struct stdarg_info *si, tree lhs, tree rhs)
check_va_list_escapes (struct stdarg_info *si, tree lhs, tree rhs)
{
{
  if (! POINTER_TYPE_P (TREE_TYPE (rhs)))
  if (! POINTER_TYPE_P (TREE_TYPE (rhs)))
    return;
    return;
 
 
  if ((TREE_CODE (rhs) == PLUS_EXPR
  if ((TREE_CODE (rhs) == PLUS_EXPR
       && TREE_CODE (TREE_OPERAND (rhs, 1)) == INTEGER_CST)
       && TREE_CODE (TREE_OPERAND (rhs, 1)) == INTEGER_CST)
      || TREE_CODE (rhs) == NOP_EXPR
      || TREE_CODE (rhs) == NOP_EXPR
      || TREE_CODE (rhs) == CONVERT_EXPR)
      || TREE_CODE (rhs) == CONVERT_EXPR)
    rhs = TREE_OPERAND (rhs, 0);
    rhs = TREE_OPERAND (rhs, 0);
 
 
  if (TREE_CODE (rhs) != SSA_NAME
  if (TREE_CODE (rhs) != SSA_NAME
      || ! bitmap_bit_p (si->va_list_escape_vars,
      || ! bitmap_bit_p (si->va_list_escape_vars,
                         DECL_UID (SSA_NAME_VAR (rhs))))
                         DECL_UID (SSA_NAME_VAR (rhs))))
    return;
    return;
 
 
  if (TREE_CODE (lhs) != SSA_NAME || is_global_var (SSA_NAME_VAR (lhs)))
  if (TREE_CODE (lhs) != SSA_NAME || is_global_var (SSA_NAME_VAR (lhs)))
    {
    {
      si->va_list_escapes = true;
      si->va_list_escapes = true;
      return;
      return;
    }
    }
 
 
  if (si->compute_sizes < 0)
  if (si->compute_sizes < 0)
    {
    {
      si->compute_sizes = 0;
      si->compute_sizes = 0;
      if (si->va_start_count == 1
      if (si->va_start_count == 1
          && reachable_at_most_once (si->bb, si->va_start_bb))
          && reachable_at_most_once (si->bb, si->va_start_bb))
        si->compute_sizes = 1;
        si->compute_sizes = 1;
 
 
      if (dump_file && (dump_flags & TDF_DETAILS))
      if (dump_file && (dump_flags & TDF_DETAILS))
        fprintf (dump_file,
        fprintf (dump_file,
                 "bb%d will %sbe executed at most once for each va_start "
                 "bb%d will %sbe executed at most once for each va_start "
                 "in bb%d\n", si->bb->index, si->compute_sizes ? "" : "not ",
                 "in bb%d\n", si->bb->index, si->compute_sizes ? "" : "not ",
                 si->va_start_bb->index);
                 si->va_start_bb->index);
    }
    }
 
 
  /* For void * or char * va_list types, there is just one counter.
  /* For void * or char * va_list types, there is just one counter.
     If va_arg is used in a loop, we don't know how many registers need
     If va_arg is used in a loop, we don't know how many registers need
     saving.  */
     saving.  */
  if (! si->compute_sizes)
  if (! si->compute_sizes)
    {
    {
      si->va_list_escapes = true;
      si->va_list_escapes = true;
      return;
      return;
    }
    }
 
 
  if (va_list_counter_bump (si, si->va_start_ap, lhs, true)
  if (va_list_counter_bump (si, si->va_start_ap, lhs, true)
      == (unsigned HOST_WIDE_INT) -1)
      == (unsigned HOST_WIDE_INT) -1)
    {
    {
      si->va_list_escapes = true;
      si->va_list_escapes = true;
      return;
      return;
    }
    }
 
 
  bitmap_set_bit (si->va_list_escape_vars,
  bitmap_set_bit (si->va_list_escape_vars,
                  DECL_UID (SSA_NAME_VAR (lhs)));
                  DECL_UID (SSA_NAME_VAR (lhs)));
}
}
 
 
 
 
/* Check all uses of temporaries from si->va_list_escape_vars bitmap.
/* Check all uses of temporaries from si->va_list_escape_vars bitmap.
   Return true if va_list might be escaping.  */
   Return true if va_list might be escaping.  */
 
 
static bool
static bool
check_all_va_list_escapes (struct stdarg_info *si)
check_all_va_list_escapes (struct stdarg_info *si)
{
{
  basic_block bb;
  basic_block bb;
 
 
  FOR_EACH_BB (bb)
  FOR_EACH_BB (bb)
    {
    {
      block_stmt_iterator i;
      block_stmt_iterator i;
 
 
      for (i = bsi_start (bb); !bsi_end_p (i); bsi_next (&i))
      for (i = bsi_start (bb); !bsi_end_p (i); bsi_next (&i))
        {
        {
          tree stmt = bsi_stmt (i), use;
          tree stmt = bsi_stmt (i), use;
          ssa_op_iter iter;
          ssa_op_iter iter;
 
 
          FOR_EACH_SSA_TREE_OPERAND (use, stmt, iter, SSA_OP_ALL_USES)
          FOR_EACH_SSA_TREE_OPERAND (use, stmt, iter, SSA_OP_ALL_USES)
            {
            {
              if (! bitmap_bit_p (si->va_list_escape_vars,
              if (! bitmap_bit_p (si->va_list_escape_vars,
                                  DECL_UID (SSA_NAME_VAR (use))))
                                  DECL_UID (SSA_NAME_VAR (use))))
                continue;
                continue;
 
 
              if (TREE_CODE (stmt) == MODIFY_EXPR)
              if (TREE_CODE (stmt) == MODIFY_EXPR)
                {
                {
                  tree lhs = TREE_OPERAND (stmt, 0);
                  tree lhs = TREE_OPERAND (stmt, 0);
                  tree rhs = TREE_OPERAND (stmt, 1);
                  tree rhs = TREE_OPERAND (stmt, 1);
 
 
                  if (TREE_CODE (rhs) == WITH_SIZE_EXPR)
                  if (TREE_CODE (rhs) == WITH_SIZE_EXPR)
                    rhs = TREE_OPERAND (rhs, 0);
                    rhs = TREE_OPERAND (rhs, 0);
 
 
                  /* x = *ap_temp;  */
                  /* x = *ap_temp;  */
                  if (TREE_CODE (rhs) == INDIRECT_REF
                  if (TREE_CODE (rhs) == INDIRECT_REF
                      && TREE_OPERAND (rhs, 0) == use
                      && TREE_OPERAND (rhs, 0) == use
                      && TYPE_SIZE_UNIT (TREE_TYPE (rhs))
                      && TYPE_SIZE_UNIT (TREE_TYPE (rhs))
                      && host_integerp (TYPE_SIZE_UNIT (TREE_TYPE (rhs)), 1)
                      && host_integerp (TYPE_SIZE_UNIT (TREE_TYPE (rhs)), 1)
                      && si->offsets[SSA_NAME_VERSION (use)] != -1)
                      && si->offsets[SSA_NAME_VERSION (use)] != -1)
                    {
                    {
                      unsigned HOST_WIDE_INT gpr_size;
                      unsigned HOST_WIDE_INT gpr_size;
                      tree access_size = TYPE_SIZE_UNIT (TREE_TYPE (rhs));
                      tree access_size = TYPE_SIZE_UNIT (TREE_TYPE (rhs));
 
 
                      gpr_size = si->offsets[SSA_NAME_VERSION (use)]
                      gpr_size = si->offsets[SSA_NAME_VERSION (use)]
                                 + tree_low_cst (access_size, 1);
                                 + tree_low_cst (access_size, 1);
                      if (gpr_size >= VA_LIST_MAX_GPR_SIZE)
                      if (gpr_size >= VA_LIST_MAX_GPR_SIZE)
                        cfun->va_list_gpr_size = VA_LIST_MAX_GPR_SIZE;
                        cfun->va_list_gpr_size = VA_LIST_MAX_GPR_SIZE;
                      else if (gpr_size > cfun->va_list_gpr_size)
                      else if (gpr_size > cfun->va_list_gpr_size)
                        cfun->va_list_gpr_size = gpr_size;
                        cfun->va_list_gpr_size = gpr_size;
                      continue;
                      continue;
                    }
                    }
 
 
                  /* va_arg sequences may contain
                  /* va_arg sequences may contain
                     other_ap_temp = ap_temp;
                     other_ap_temp = ap_temp;
                     other_ap_temp = ap_temp + constant;
                     other_ap_temp = ap_temp + constant;
                     other_ap_temp = (some_type *) ap_temp;
                     other_ap_temp = (some_type *) ap_temp;
                     ap = ap_temp;
                     ap = ap_temp;
                     statements.  */
                     statements.  */
                  if ((TREE_CODE (rhs) == PLUS_EXPR
                  if ((TREE_CODE (rhs) == PLUS_EXPR
                       && TREE_CODE (TREE_OPERAND (rhs, 1)) == INTEGER_CST)
                       && TREE_CODE (TREE_OPERAND (rhs, 1)) == INTEGER_CST)
                      || TREE_CODE (rhs) == NOP_EXPR
                      || TREE_CODE (rhs) == NOP_EXPR
                      || TREE_CODE (rhs) == CONVERT_EXPR)
                      || TREE_CODE (rhs) == CONVERT_EXPR)
                    rhs = TREE_OPERAND (rhs, 0);
                    rhs = TREE_OPERAND (rhs, 0);
 
 
                  if (rhs == use)
                  if (rhs == use)
                    {
                    {
                      if (TREE_CODE (lhs) == SSA_NAME
                      if (TREE_CODE (lhs) == SSA_NAME
                          && bitmap_bit_p (si->va_list_escape_vars,
                          && bitmap_bit_p (si->va_list_escape_vars,
                                           DECL_UID (SSA_NAME_VAR (lhs))))
                                           DECL_UID (SSA_NAME_VAR (lhs))))
                        continue;
                        continue;
 
 
                      if (TREE_CODE (lhs) == VAR_DECL
                      if (TREE_CODE (lhs) == VAR_DECL
                          && bitmap_bit_p (si->va_list_vars,
                          && bitmap_bit_p (si->va_list_vars,
                                           DECL_UID (lhs)))
                                           DECL_UID (lhs)))
                        continue;
                        continue;
                    }
                    }
                }
                }
 
 
              if (dump_file && (dump_flags & TDF_DETAILS))
              if (dump_file && (dump_flags & TDF_DETAILS))
                {
                {
                  fputs ("va_list escapes in ", dump_file);
                  fputs ("va_list escapes in ", dump_file);
                  print_generic_expr (dump_file, stmt, dump_flags);
                  print_generic_expr (dump_file, stmt, dump_flags);
                  fputc ('\n', dump_file);
                  fputc ('\n', dump_file);
                }
                }
              return true;
              return true;
            }
            }
        }
        }
    }
    }
 
 
  return false;
  return false;
}
}
 
 
 
 
/* Return true if this optimization pass should be done.
/* Return true if this optimization pass should be done.
   It makes only sense for stdarg functions.  */
   It makes only sense for stdarg functions.  */
 
 
static bool
static bool
gate_optimize_stdarg (void)
gate_optimize_stdarg (void)
{
{
  /* This optimization is only for stdarg functions.  */
  /* This optimization is only for stdarg functions.  */
  return current_function_stdarg != 0;
  return current_function_stdarg != 0;
}
}
 
 
 
 
/* Entry point to the stdarg optimization pass.  */
/* Entry point to the stdarg optimization pass.  */
 
 
static unsigned int
static unsigned int
execute_optimize_stdarg (void)
execute_optimize_stdarg (void)
{
{
  basic_block bb;
  basic_block bb;
  bool va_list_escapes = false;
  bool va_list_escapes = false;
  bool va_list_simple_ptr;
  bool va_list_simple_ptr;
  struct stdarg_info si;
  struct stdarg_info si;
  const char *funcname = NULL;
  const char *funcname = NULL;
 
 
  cfun->va_list_gpr_size = 0;
  cfun->va_list_gpr_size = 0;
  cfun->va_list_fpr_size = 0;
  cfun->va_list_fpr_size = 0;
  memset (&si, 0, sizeof (si));
  memset (&si, 0, sizeof (si));
  si.va_list_vars = BITMAP_ALLOC (NULL);
  si.va_list_vars = BITMAP_ALLOC (NULL);
  si.va_list_escape_vars = BITMAP_ALLOC (NULL);
  si.va_list_escape_vars = BITMAP_ALLOC (NULL);
 
 
  if (dump_file)
  if (dump_file)
    funcname = lang_hooks.decl_printable_name (current_function_decl, 2);
    funcname = lang_hooks.decl_printable_name (current_function_decl, 2);
 
 
  va_list_simple_ptr = POINTER_TYPE_P (va_list_type_node)
  va_list_simple_ptr = POINTER_TYPE_P (va_list_type_node)
                       && (TREE_TYPE (va_list_type_node) == void_type_node
                       && (TREE_TYPE (va_list_type_node) == void_type_node
                           || TREE_TYPE (va_list_type_node) == char_type_node);
                           || TREE_TYPE (va_list_type_node) == char_type_node);
  gcc_assert (is_gimple_reg_type (va_list_type_node) == va_list_simple_ptr);
  gcc_assert (is_gimple_reg_type (va_list_type_node) == va_list_simple_ptr);
 
 
  FOR_EACH_BB (bb)
  FOR_EACH_BB (bb)
    {
    {
      block_stmt_iterator i;
      block_stmt_iterator i;
 
 
      for (i = bsi_start (bb); !bsi_end_p (i); bsi_next (&i))
      for (i = bsi_start (bb); !bsi_end_p (i); bsi_next (&i))
        {
        {
          tree stmt = bsi_stmt (i);
          tree stmt = bsi_stmt (i);
          tree call = get_call_expr_in (stmt), callee;
          tree call = get_call_expr_in (stmt), callee;
          tree ap;
          tree ap;
 
 
          if (!call)
          if (!call)
            continue;
            continue;
 
 
          callee = get_callee_fndecl (call);
          callee = get_callee_fndecl (call);
          if (!callee
          if (!callee
              || DECL_BUILT_IN_CLASS (callee) != BUILT_IN_NORMAL)
              || DECL_BUILT_IN_CLASS (callee) != BUILT_IN_NORMAL)
            continue;
            continue;
 
 
          switch (DECL_FUNCTION_CODE (callee))
          switch (DECL_FUNCTION_CODE (callee))
            {
            {
            case BUILT_IN_VA_START:
            case BUILT_IN_VA_START:
              break;
              break;
              /* If old style builtins are used, don't optimize anything.  */
              /* If old style builtins are used, don't optimize anything.  */
            case BUILT_IN_SAVEREGS:
            case BUILT_IN_SAVEREGS:
            case BUILT_IN_STDARG_START:
            case BUILT_IN_STDARG_START:
            case BUILT_IN_ARGS_INFO:
            case BUILT_IN_ARGS_INFO:
            case BUILT_IN_NEXT_ARG:
            case BUILT_IN_NEXT_ARG:
              va_list_escapes = true;
              va_list_escapes = true;
              continue;
              continue;
            default:
            default:
              continue;
              continue;
            }
            }
 
 
          si.va_start_count++;
          si.va_start_count++;
          ap = TREE_VALUE (TREE_OPERAND (call, 1));
          ap = TREE_VALUE (TREE_OPERAND (call, 1));
 
 
          if (TREE_CODE (ap) != ADDR_EXPR)
          if (TREE_CODE (ap) != ADDR_EXPR)
            {
            {
              va_list_escapes = true;
              va_list_escapes = true;
              break;
              break;
            }
            }
          ap = TREE_OPERAND (ap, 0);
          ap = TREE_OPERAND (ap, 0);
          if (TREE_CODE (ap) == ARRAY_REF)
          if (TREE_CODE (ap) == ARRAY_REF)
            {
            {
              if (! integer_zerop (TREE_OPERAND (ap, 1)))
              if (! integer_zerop (TREE_OPERAND (ap, 1)))
                {
                {
                  va_list_escapes = true;
                  va_list_escapes = true;
                  break;
                  break;
                }
                }
              ap = TREE_OPERAND (ap, 0);
              ap = TREE_OPERAND (ap, 0);
            }
            }
          if (TYPE_MAIN_VARIANT (TREE_TYPE (ap))
          if (TYPE_MAIN_VARIANT (TREE_TYPE (ap))
              != TYPE_MAIN_VARIANT (va_list_type_node)
              != TYPE_MAIN_VARIANT (va_list_type_node)
              || TREE_CODE (ap) != VAR_DECL)
              || TREE_CODE (ap) != VAR_DECL)
            {
            {
              va_list_escapes = true;
              va_list_escapes = true;
              break;
              break;
            }
            }
 
 
          if (is_global_var (ap))
          if (is_global_var (ap))
            {
            {
              va_list_escapes = true;
              va_list_escapes = true;
              break;
              break;
            }
            }
 
 
          bitmap_set_bit (si.va_list_vars, DECL_UID (ap));
          bitmap_set_bit (si.va_list_vars, DECL_UID (ap));
 
 
          /* VA_START_BB and VA_START_AP will be only used if there is just
          /* VA_START_BB and VA_START_AP will be only used if there is just
             one va_start in the function.  */
             one va_start in the function.  */
          si.va_start_bb = bb;
          si.va_start_bb = bb;
          si.va_start_ap = ap;
          si.va_start_ap = ap;
        }
        }
 
 
      if (va_list_escapes)
      if (va_list_escapes)
        break;
        break;
    }
    }
 
 
  /* If there were no va_start uses in the function, there is no need to
  /* If there were no va_start uses in the function, there is no need to
     save anything.  */
     save anything.  */
  if (si.va_start_count == 0)
  if (si.va_start_count == 0)
    goto finish;
    goto finish;
 
 
  /* If some va_list arguments weren't local, we can't optimize.  */
  /* If some va_list arguments weren't local, we can't optimize.  */
  if (va_list_escapes)
  if (va_list_escapes)
    goto finish;
    goto finish;
 
 
  /* For void * or char * va_list, something useful can be done only
  /* For void * or char * va_list, something useful can be done only
     if there is just one va_start.  */
     if there is just one va_start.  */
  if (va_list_simple_ptr && si.va_start_count > 1)
  if (va_list_simple_ptr && si.va_start_count > 1)
    {
    {
      va_list_escapes = true;
      va_list_escapes = true;
      goto finish;
      goto finish;
    }
    }
 
 
  /* For struct * va_list, if the backend didn't tell us what the counter fields
  /* For struct * va_list, if the backend didn't tell us what the counter fields
     are, there is nothing more we can do.  */
     are, there is nothing more we can do.  */
  if (!va_list_simple_ptr
  if (!va_list_simple_ptr
      && va_list_gpr_counter_field == NULL_TREE
      && va_list_gpr_counter_field == NULL_TREE
      && va_list_fpr_counter_field == NULL_TREE)
      && va_list_fpr_counter_field == NULL_TREE)
    {
    {
      va_list_escapes = true;
      va_list_escapes = true;
      goto finish;
      goto finish;
    }
    }
 
 
  /* For void * or char * va_list there is just one counter
  /* For void * or char * va_list there is just one counter
     (va_list itself).  Use VA_LIST_GPR_SIZE for it.  */
     (va_list itself).  Use VA_LIST_GPR_SIZE for it.  */
  if (va_list_simple_ptr)
  if (va_list_simple_ptr)
    cfun->va_list_fpr_size = VA_LIST_MAX_FPR_SIZE;
    cfun->va_list_fpr_size = VA_LIST_MAX_FPR_SIZE;
 
 
  calculate_dominance_info (CDI_DOMINATORS);
  calculate_dominance_info (CDI_DOMINATORS);
 
 
  FOR_EACH_BB (bb)
  FOR_EACH_BB (bb)
    {
    {
      block_stmt_iterator i;
      block_stmt_iterator i;
 
 
      si.compute_sizes = -1;
      si.compute_sizes = -1;
      si.bb = bb;
      si.bb = bb;
 
 
      /* For va_list_simple_ptr, we have to check PHI nodes too.  We treat
      /* For va_list_simple_ptr, we have to check PHI nodes too.  We treat
         them as assignments for the purpose of escape analysis.  This is
         them as assignments for the purpose of escape analysis.  This is
         not needed for non-simple va_list because virtual phis don't perform
         not needed for non-simple va_list because virtual phis don't perform
         any real data movement.  */
         any real data movement.  */
      if (va_list_simple_ptr)
      if (va_list_simple_ptr)
        {
        {
          tree phi, lhs, rhs;
          tree phi, lhs, rhs;
          use_operand_p uop;
          use_operand_p uop;
          ssa_op_iter soi;
          ssa_op_iter soi;
 
 
          for (phi = phi_nodes (bb); phi; phi = PHI_CHAIN (phi))
          for (phi = phi_nodes (bb); phi; phi = PHI_CHAIN (phi))
            {
            {
              lhs = PHI_RESULT (phi);
              lhs = PHI_RESULT (phi);
 
 
              if (!is_gimple_reg (lhs))
              if (!is_gimple_reg (lhs))
                continue;
                continue;
 
 
              FOR_EACH_PHI_ARG (uop, phi, soi, SSA_OP_USE)
              FOR_EACH_PHI_ARG (uop, phi, soi, SSA_OP_USE)
                {
                {
                  rhs = USE_FROM_PTR (uop);
                  rhs = USE_FROM_PTR (uop);
                  if (va_list_ptr_read (&si, rhs, lhs))
                  if (va_list_ptr_read (&si, rhs, lhs))
                    continue;
                    continue;
                  else if (va_list_ptr_write (&si, lhs, rhs))
                  else if (va_list_ptr_write (&si, lhs, rhs))
                    continue;
                    continue;
                  else
                  else
                    check_va_list_escapes (&si, lhs, rhs);
                    check_va_list_escapes (&si, lhs, rhs);
 
 
                  if (si.va_list_escapes
                  if (si.va_list_escapes
                      || walk_tree (&phi, find_va_list_reference,
                      || walk_tree (&phi, find_va_list_reference,
                                    si.va_list_vars, NULL))
                                    si.va_list_vars, NULL))
                    {
                    {
                      if (dump_file && (dump_flags & TDF_DETAILS))
                      if (dump_file && (dump_flags & TDF_DETAILS))
                        {
                        {
                          fputs ("va_list escapes in ", dump_file);
                          fputs ("va_list escapes in ", dump_file);
                          print_generic_expr (dump_file, phi, dump_flags);
                          print_generic_expr (dump_file, phi, dump_flags);
                          fputc ('\n', dump_file);
                          fputc ('\n', dump_file);
                        }
                        }
                      va_list_escapes = true;
                      va_list_escapes = true;
                    }
                    }
                }
                }
            }
            }
        }
        }
 
 
      for (i = bsi_start (bb);
      for (i = bsi_start (bb);
           !bsi_end_p (i) && !va_list_escapes;
           !bsi_end_p (i) && !va_list_escapes;
           bsi_next (&i))
           bsi_next (&i))
        {
        {
          tree stmt = bsi_stmt (i);
          tree stmt = bsi_stmt (i);
          tree call;
          tree call;
 
 
          /* Don't look at __builtin_va_{start,end}, they are ok.  */
          /* Don't look at __builtin_va_{start,end}, they are ok.  */
          call = get_call_expr_in (stmt);
          call = get_call_expr_in (stmt);
          if (call)
          if (call)
            {
            {
              tree callee = get_callee_fndecl (call);
              tree callee = get_callee_fndecl (call);
 
 
              if (callee
              if (callee
                  && DECL_BUILT_IN_CLASS (callee) == BUILT_IN_NORMAL
                  && DECL_BUILT_IN_CLASS (callee) == BUILT_IN_NORMAL
                  && (DECL_FUNCTION_CODE (callee) == BUILT_IN_VA_START
                  && (DECL_FUNCTION_CODE (callee) == BUILT_IN_VA_START
                      || DECL_FUNCTION_CODE (callee) == BUILT_IN_VA_END))
                      || DECL_FUNCTION_CODE (callee) == BUILT_IN_VA_END))
                continue;
                continue;
            }
            }
 
 
          if (TREE_CODE (stmt) == MODIFY_EXPR)
          if (TREE_CODE (stmt) == MODIFY_EXPR)
            {
            {
              tree lhs = TREE_OPERAND (stmt, 0);
              tree lhs = TREE_OPERAND (stmt, 0);
              tree rhs = TREE_OPERAND (stmt, 1);
              tree rhs = TREE_OPERAND (stmt, 1);
 
 
              if (TREE_CODE (rhs) == WITH_SIZE_EXPR)
              if (TREE_CODE (rhs) == WITH_SIZE_EXPR)
                rhs = TREE_OPERAND (rhs, 0);
                rhs = TREE_OPERAND (rhs, 0);
 
 
              if (va_list_simple_ptr)
              if (va_list_simple_ptr)
                {
                {
                  /* Check for tem = ap.  */
                  /* Check for tem = ap.  */
                  if (va_list_ptr_read (&si, rhs, lhs))
                  if (va_list_ptr_read (&si, rhs, lhs))
                    continue;
                    continue;
 
 
                  /* Check for the last insn in:
                  /* Check for the last insn in:
                     tem1 = ap;
                     tem1 = ap;
                     tem2 = tem1 + CST;
                     tem2 = tem1 + CST;
                     ap = tem2;
                     ap = tem2;
                     sequence.  */
                     sequence.  */
                  else if (va_list_ptr_write (&si, lhs, rhs))
                  else if (va_list_ptr_write (&si, lhs, rhs))
                    continue;
                    continue;
 
 
                  else
                  else
                    check_va_list_escapes (&si, lhs, rhs);
                    check_va_list_escapes (&si, lhs, rhs);
                }
                }
              else
              else
                {
                {
                  /* Check for ap[0].field = temp.  */
                  /* Check for ap[0].field = temp.  */
                  if (va_list_counter_struct_op (&si, lhs, rhs, true))
                  if (va_list_counter_struct_op (&si, lhs, rhs, true))
                    continue;
                    continue;
 
 
                  /* Check for temp = ap[0].field.  */
                  /* Check for temp = ap[0].field.  */
                  else if (va_list_counter_struct_op (&si, rhs, lhs, false))
                  else if (va_list_counter_struct_op (&si, rhs, lhs, false))
                    continue;
                    continue;
 
 
                  /* Do any architecture specific checking.  */
                  /* Do any architecture specific checking.  */
                  else if (targetm.stdarg_optimize_hook
                  else if (targetm.stdarg_optimize_hook
                           && targetm.stdarg_optimize_hook (&si, lhs, rhs))
                           && targetm.stdarg_optimize_hook (&si, lhs, rhs))
                    continue;
                    continue;
                }
                }
            }
            }
 
 
          /* All other uses of va_list are either va_copy (that is not handled
          /* All other uses of va_list are either va_copy (that is not handled
             in this optimization), taking address of va_list variable or
             in this optimization), taking address of va_list variable or
             passing va_list to other functions (in that case va_list might
             passing va_list to other functions (in that case va_list might
             escape the function and therefore va_start needs to set it up
             escape the function and therefore va_start needs to set it up
             fully), or some unexpected use of va_list.  None of these should
             fully), or some unexpected use of va_list.  None of these should
             happen in a gimplified VA_ARG_EXPR.  */
             happen in a gimplified VA_ARG_EXPR.  */
          if (si.va_list_escapes
          if (si.va_list_escapes
              || walk_tree (&stmt, find_va_list_reference,
              || walk_tree (&stmt, find_va_list_reference,
                            si.va_list_vars, NULL))
                            si.va_list_vars, NULL))
            {
            {
              if (dump_file && (dump_flags & TDF_DETAILS))
              if (dump_file && (dump_flags & TDF_DETAILS))
                {
                {
                  fputs ("va_list escapes in ", dump_file);
                  fputs ("va_list escapes in ", dump_file);
                  print_generic_expr (dump_file, stmt, dump_flags);
                  print_generic_expr (dump_file, stmt, dump_flags);
                  fputc ('\n', dump_file);
                  fputc ('\n', dump_file);
                }
                }
              va_list_escapes = true;
              va_list_escapes = true;
            }
            }
        }
        }
 
 
      if (va_list_escapes)
      if (va_list_escapes)
        break;
        break;
    }
    }
 
 
  if (! va_list_escapes
  if (! va_list_escapes
      && va_list_simple_ptr
      && va_list_simple_ptr
      && ! bitmap_empty_p (si.va_list_escape_vars)
      && ! bitmap_empty_p (si.va_list_escape_vars)
      && check_all_va_list_escapes (&si))
      && check_all_va_list_escapes (&si))
    va_list_escapes = true;
    va_list_escapes = true;
 
 
finish:
finish:
  if (va_list_escapes)
  if (va_list_escapes)
    {
    {
      cfun->va_list_gpr_size = VA_LIST_MAX_GPR_SIZE;
      cfun->va_list_gpr_size = VA_LIST_MAX_GPR_SIZE;
      cfun->va_list_fpr_size = VA_LIST_MAX_FPR_SIZE;
      cfun->va_list_fpr_size = VA_LIST_MAX_FPR_SIZE;
    }
    }
  BITMAP_FREE (si.va_list_vars);
  BITMAP_FREE (si.va_list_vars);
  BITMAP_FREE (si.va_list_escape_vars);
  BITMAP_FREE (si.va_list_escape_vars);
  free (si.offsets);
  free (si.offsets);
  if (dump_file)
  if (dump_file)
    {
    {
      fprintf (dump_file, "%s: va_list escapes %d, needs to save ",
      fprintf (dump_file, "%s: va_list escapes %d, needs to save ",
               funcname, (int) va_list_escapes);
               funcname, (int) va_list_escapes);
      if (cfun->va_list_gpr_size >= VA_LIST_MAX_GPR_SIZE)
      if (cfun->va_list_gpr_size >= VA_LIST_MAX_GPR_SIZE)
        fputs ("all", dump_file);
        fputs ("all", dump_file);
      else
      else
        fprintf (dump_file, "%d", cfun->va_list_gpr_size);
        fprintf (dump_file, "%d", cfun->va_list_gpr_size);
      fputs (" GPR units and ", dump_file);
      fputs (" GPR units and ", dump_file);
      if (cfun->va_list_fpr_size >= VA_LIST_MAX_FPR_SIZE)
      if (cfun->va_list_fpr_size >= VA_LIST_MAX_FPR_SIZE)
        fputs ("all", dump_file);
        fputs ("all", dump_file);
      else
      else
        fprintf (dump_file, "%d", cfun->va_list_fpr_size);
        fprintf (dump_file, "%d", cfun->va_list_fpr_size);
      fputs (" FPR units.\n", dump_file);
      fputs (" FPR units.\n", dump_file);
    }
    }
  return 0;
  return 0;
}
}
 
 
 
 
struct tree_opt_pass pass_stdarg =
struct tree_opt_pass pass_stdarg =
{
{
  "stdarg",                             /* name */
  "stdarg",                             /* name */
  gate_optimize_stdarg,                 /* gate */
  gate_optimize_stdarg,                 /* gate */
  execute_optimize_stdarg,              /* execute */
  execute_optimize_stdarg,              /* execute */
  NULL,                                 /* sub */
  NULL,                                 /* sub */
  NULL,                                 /* next */
  NULL,                                 /* next */
  0,                                     /* static_pass_number */
  0,                                     /* static_pass_number */
  0,                                     /* tv_id */
  0,                                     /* tv_id */
  PROP_cfg | PROP_ssa | PROP_alias,     /* properties_required */
  PROP_cfg | PROP_ssa | PROP_alias,     /* properties_required */
  0,                                     /* properties_provided */
  0,                                     /* properties_provided */
  0,                                     /* properties_destroyed */
  0,                                     /* properties_destroyed */
  0,                                     /* todo_flags_start */
  0,                                     /* todo_flags_start */
  TODO_dump_func,                       /* todo_flags_finish */
  TODO_dump_func,                       /* todo_flags_finish */
  0                                      /* letter */
  0                                      /* letter */
};
};
 
 

powered by: WebSVN 2.1.0

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