/* Pass computing data for optimizing stdarg functions.
- Copyright (C) 2004, 2005 Free Software Foundation, Inc.
+ Copyright (C) 2004-2020 Free Software Foundation, Inc.
Contributed by Jakub Jelinek <jakub@redhat.com>
This file is part of GCC.
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 Software Foundation; either version 2, or (at your option)
+the Free Software Foundation; either version 3, or (at your option)
any later version.
GCC is distributed in the hope that it will be useful,
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
-along with GCC; see the file COPYING. If not, write to
-the Free Software Foundation, 51 Franklin Street, Fifth Floor,
-Boston, MA 02110-1301, USA. */
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
#include "config.h"
#include "system.h"
#include "coretypes.h"
-#include "tm.h"
-#include "tree.h"
-#include "function.h"
-#include "langhooks.h"
-#include "diagnostic.h"
+#include "backend.h"
#include "target.h"
-#include "tree-flow.h"
+#include "tree.h"
+#include "gimple.h"
#include "tree-pass.h"
+#include "ssa.h"
+#include "gimple-pretty-print.h"
+#include "fold-const.h"
+#include "langhooks.h"
+#include "gimple-iterator.h"
+#include "gimple-walk.h"
+#include "gimplify.h"
+#include "tree-into-ssa.h"
+#include "tree-cfg.h"
#include "tree-stdarg.h"
/* A simple pass that attempts to optimize stdarg functions on architectures
static bool
reachable_at_most_once (basic_block va_arg_bb, basic_block va_start_bb)
{
- edge *stack, e;
+ auto_vec<edge, 10> stack;
+ edge e;
edge_iterator ei;
- int sp;
- sbitmap visited;
bool ret;
if (va_arg_bb == va_start_bb)
if (! dominated_by_p (CDI_DOMINATORS, va_arg_bb, va_start_bb))
return false;
- stack = XNEWVEC (edge, n_basic_blocks + 1);
- sp = 0;
-
- visited = sbitmap_alloc (last_basic_block);
- sbitmap_zero (visited);
+ auto_sbitmap visited (last_basic_block_for_fn (cfun));
+ bitmap_clear (visited);
ret = true;
FOR_EACH_EDGE (e, ei, va_arg_bb->preds)
- stack[sp++] = e;
+ stack.safe_push (e);
- while (sp)
+ while (! stack.is_empty ())
{
basic_block src;
- --sp;
- e = stack[sp];
+ e = stack.pop ();
src = e->src;
if (e->flags & EDGE_COMPLEX)
break;
}
- gcc_assert (src != ENTRY_BLOCK_PTR);
+ gcc_assert (src != ENTRY_BLOCK_PTR_FOR_FN (cfun));
- if (! TEST_BIT (visited, src->index))
+ if (! bitmap_bit_p (visited, src->index))
{
- SET_BIT (visited, src->index);
+ bitmap_set_bit (visited, src->index);
FOR_EACH_EDGE (e, ei, src->preds)
- stack[sp++] = e;
+ stack.safe_push (e);
}
}
- free (stack);
- sbitmap_free (visited);
return ret;
}
/* For statement COUNTER = RHS, if RHS is COUNTER + constant,
- return constant, otherwise return (unsigned HOST_WIDE_INT) -1.
+ return constant, otherwise return HOST_WIDE_INT_M1U.
GPR_P is true if this is GPR counter. */
static unsigned HOST_WIDE_INT
va_list_counter_bump (struct stdarg_info *si, tree counter, tree rhs,
bool gpr_p)
{
- tree stmt, lhs, orig_lhs;
+ tree lhs, orig_lhs;
+ gimple *stmt;
unsigned HOST_WIDE_INT ret = 0, val, counter_val;
unsigned int max_size;
orig_lhs = lhs = rhs;
while (lhs)
{
+ enum tree_code rhs_code;
+ tree rhs1;
+
if (si->offsets[SSA_NAME_VERSION (lhs)] != -1)
{
if (counter_val >= max_size)
stmt = SSA_NAME_DEF_STMT (lhs);
- if (TREE_CODE (stmt) != GIMPLE_MODIFY_STMT
- || GIMPLE_STMT_OPERAND (stmt, 0) != lhs)
- return (unsigned HOST_WIDE_INT) -1;
-
- rhs = GIMPLE_STMT_OPERAND (stmt, 1);
- if (TREE_CODE (rhs) == WITH_SIZE_EXPR)
- rhs = TREE_OPERAND (rhs, 0);
+ if (!is_gimple_assign (stmt) || gimple_assign_lhs (stmt) != lhs)
+ return HOST_WIDE_INT_M1U;
- if (TREE_CODE (rhs) == SSA_NAME)
+ rhs_code = gimple_assign_rhs_code (stmt);
+ rhs1 = gimple_assign_rhs1 (stmt);
+ if ((get_gimple_rhs_class (rhs_code) == GIMPLE_SINGLE_RHS
+ || gimple_assign_cast_p (stmt))
+ && TREE_CODE (rhs1) == SSA_NAME)
{
- lhs = rhs;
+ lhs = rhs1;
continue;
}
- if ((TREE_CODE (rhs) == NOP_EXPR
- || TREE_CODE (rhs) == CONVERT_EXPR)
- && TREE_CODE (TREE_OPERAND (rhs, 0)) == SSA_NAME)
+ if ((rhs_code == POINTER_PLUS_EXPR
+ || rhs_code == PLUS_EXPR)
+ && TREE_CODE (rhs1) == SSA_NAME
+ && tree_fits_uhwi_p (gimple_assign_rhs2 (stmt)))
{
- lhs = TREE_OPERAND (rhs, 0);
+ ret += tree_to_uhwi (gimple_assign_rhs2 (stmt));
+ lhs = rhs1;
continue;
}
- if (TREE_CODE (rhs) == PLUS_EXPR
- && TREE_CODE (TREE_OPERAND (rhs, 0)) == SSA_NAME
- && TREE_CODE (TREE_OPERAND (rhs, 1)) == INTEGER_CST
- && host_integerp (TREE_OPERAND (rhs, 1), 1))
+ if (rhs_code == ADDR_EXPR
+ && TREE_CODE (TREE_OPERAND (rhs1, 0)) == MEM_REF
+ && TREE_CODE (TREE_OPERAND (TREE_OPERAND (rhs1, 0), 0)) == SSA_NAME
+ && tree_fits_uhwi_p (TREE_OPERAND (TREE_OPERAND (rhs1, 0), 1)))
{
- ret += tree_low_cst (TREE_OPERAND (rhs, 1), 1);
- lhs = TREE_OPERAND (rhs, 0);
+ ret += tree_to_uhwi (TREE_OPERAND (TREE_OPERAND (rhs1, 0), 1));
+ lhs = TREE_OPERAND (TREE_OPERAND (rhs1, 0), 0);
continue;
}
+ if (get_gimple_rhs_class (rhs_code) != GIMPLE_SINGLE_RHS)
+ return HOST_WIDE_INT_M1U;
+
+ rhs = gimple_assign_rhs1 (stmt);
if (TREE_CODE (counter) != TREE_CODE (rhs))
- return (unsigned HOST_WIDE_INT) -1;
+ return HOST_WIDE_INT_M1U;
if (TREE_CODE (counter) == COMPONENT_REF)
{
if (get_base_address (counter) != get_base_address (rhs)
|| TREE_CODE (TREE_OPERAND (rhs, 1)) != FIELD_DECL
|| TREE_OPERAND (counter, 1) != TREE_OPERAND (rhs, 1))
- return (unsigned HOST_WIDE_INT) -1;
+ return HOST_WIDE_INT_M1U;
}
else if (counter != rhs)
- return (unsigned HOST_WIDE_INT) -1;
+ return HOST_WIDE_INT_M1U;
lhs = NULL;
}
val = ret + counter_val;
while (lhs)
{
+ enum tree_code rhs_code;
+ tree rhs1;
+
if (si->offsets[SSA_NAME_VERSION (lhs)] != -1)
break;
stmt = SSA_NAME_DEF_STMT (lhs);
- rhs = GIMPLE_STMT_OPERAND (stmt, 1);
- if (TREE_CODE (rhs) == WITH_SIZE_EXPR)
- rhs = TREE_OPERAND (rhs, 0);
-
- if (TREE_CODE (rhs) == SSA_NAME)
+ rhs_code = gimple_assign_rhs_code (stmt);
+ rhs1 = gimple_assign_rhs1 (stmt);
+ if ((get_gimple_rhs_class (rhs_code) == GIMPLE_SINGLE_RHS
+ || gimple_assign_cast_p (stmt))
+ && TREE_CODE (rhs1) == SSA_NAME)
{
- lhs = rhs;
+ lhs = rhs1;
continue;
}
- if ((TREE_CODE (rhs) == NOP_EXPR
- || TREE_CODE (rhs) == CONVERT_EXPR)
- && TREE_CODE (TREE_OPERAND (rhs, 0)) == SSA_NAME)
+ if ((rhs_code == POINTER_PLUS_EXPR
+ || rhs_code == PLUS_EXPR)
+ && TREE_CODE (rhs1) == SSA_NAME
+ && tree_fits_uhwi_p (gimple_assign_rhs2 (stmt)))
{
- lhs = TREE_OPERAND (rhs, 0);
+ val -= tree_to_uhwi (gimple_assign_rhs2 (stmt));
+ lhs = rhs1;
continue;
}
- if (TREE_CODE (rhs) == PLUS_EXPR
- && TREE_CODE (TREE_OPERAND (rhs, 0)) == SSA_NAME
- && TREE_CODE (TREE_OPERAND (rhs, 1)) == INTEGER_CST
- && host_integerp (TREE_OPERAND (rhs, 1), 1))
+ if (rhs_code == ADDR_EXPR
+ && TREE_CODE (TREE_OPERAND (rhs1, 0)) == MEM_REF
+ && TREE_CODE (TREE_OPERAND (TREE_OPERAND (rhs1, 0), 0)) == SSA_NAME
+ && tree_fits_uhwi_p (TREE_OPERAND (TREE_OPERAND (rhs1, 0), 1)))
{
- val -= tree_low_cst (TREE_OPERAND (rhs, 1), 1);
- lhs = TREE_OPERAND (rhs, 0);
+ val -= tree_to_uhwi (TREE_OPERAND (TREE_OPERAND (rhs1, 0), 1));
+ lhs = TREE_OPERAND (TREE_OPERAND (rhs1, 0), 0);
continue;
}
find_va_list_reference (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED,
void *data)
{
- bitmap va_list_vars = (bitmap) data;
+ bitmap va_list_vars = (bitmap) ((struct walk_stmt_info *) data)->info;
tree var = *tp;
if (TREE_CODE (var) == SSA_NAME)
- var = SSA_NAME_VAR (var);
-
- if (TREE_CODE (var) == VAR_DECL
- && bitmap_bit_p (va_list_vars, DECL_UID (var)))
- return var;
+ {
+ if (bitmap_bit_p (va_list_vars, SSA_NAME_VERSION (var)))
+ return var;
+ }
+ else if (VAR_P (var))
+ {
+ if (bitmap_bit_p (va_list_vars, DECL_UID (var) + num_ssa_names))
+ return var;
+ }
return NULL_TREE;
}
return false;
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, SSA_NAME_VERSION (var)))
return false;
base = get_base_address (ap);
- if (TREE_CODE (base) != VAR_DECL
- || !bitmap_bit_p (si->va_list_vars, DECL_UID (base)))
+ if (!VAR_P (base)
+ || !bitmap_bit_p (si->va_list_vars, DECL_UID (base) + num_ssa_names))
return false;
if (TREE_OPERAND (ap, 1) == va_list_gpr_counter_field)
static bool
va_list_ptr_read (struct stdarg_info *si, tree ap, tree tem)
{
- if (TREE_CODE (ap) != VAR_DECL
- || !bitmap_bit_p (si->va_list_vars, DECL_UID (ap)))
+ if (!VAR_P (ap)
+ || !bitmap_bit_p (si->va_list_vars, DECL_UID (ap) + num_ssa_names))
return false;
if (TREE_CODE (tem) != SSA_NAME
- || bitmap_bit_p (si->va_list_vars,
- DECL_UID (SSA_NAME_VAR (tem)))
- || is_global_var (SSA_NAME_VAR (tem)))
+ || bitmap_bit_p (si->va_list_vars, SSA_NAME_VERSION (tem)))
return false;
if (si->compute_sizes < 0)
if (! si->compute_sizes)
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) == HOST_WIDE_INT_M1U)
return false;
/* Note the temporary, as we need to track whether it doesn't escape
the current function. */
- bitmap_set_bit (si->va_list_escape_vars,
- DECL_UID (SSA_NAME_VAR (tem)));
+ bitmap_set_bit (si->va_list_escape_vars, SSA_NAME_VERSION (tem));
+
return true;
}
{
unsigned HOST_WIDE_INT increment;
- if (TREE_CODE (ap) != VAR_DECL
- || !bitmap_bit_p (si->va_list_vars, DECL_UID (ap)))
+ if (!VAR_P (ap)
+ || !bitmap_bit_p (si->va_list_vars, DECL_UID (ap) + num_ssa_names))
return false;
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, SSA_NAME_VERSION (tem2)))
return false;
if (si->compute_sizes <= 0)
if (! POINTER_TYPE_P (TREE_TYPE (rhs)))
return;
- if ((TREE_CODE (rhs) == PLUS_EXPR
- && TREE_CODE (TREE_OPERAND (rhs, 1)) == INTEGER_CST)
- || TREE_CODE (rhs) == NOP_EXPR
- || TREE_CODE (rhs) == CONVERT_EXPR)
- rhs = TREE_OPERAND (rhs, 0);
-
- if (TREE_CODE (rhs) != SSA_NAME
- || ! bitmap_bit_p (si->va_list_escape_vars,
- DECL_UID (SSA_NAME_VAR (rhs))))
+ if (TREE_CODE (rhs) == SSA_NAME)
+ {
+ if (! bitmap_bit_p (si->va_list_escape_vars, SSA_NAME_VERSION (rhs)))
+ return;
+ }
+ else if (TREE_CODE (rhs) == ADDR_EXPR
+ && TREE_CODE (TREE_OPERAND (rhs, 0)) == MEM_REF
+ && TREE_CODE (TREE_OPERAND (TREE_OPERAND (rhs, 0), 0)) == SSA_NAME)
+ {
+ tree ptr = TREE_OPERAND (TREE_OPERAND (rhs, 0), 0);
+ if (! bitmap_bit_p (si->va_list_escape_vars, SSA_NAME_VERSION (ptr)))
+ return;
+ }
+ else
return;
- if (TREE_CODE (lhs) != SSA_NAME || is_global_var (SSA_NAME_VAR (lhs)))
+ if (TREE_CODE (lhs) != SSA_NAME)
{
si->va_list_escapes = true;
return;
}
if (va_list_counter_bump (si, si->va_start_ap, lhs, true)
- == (unsigned HOST_WIDE_INT) -1)
+ == HOST_WIDE_INT_M1U)
{
si->va_list_escapes = true;
return;
}
- bitmap_set_bit (si->va_list_escape_vars,
- DECL_UID (SSA_NAME_VAR (lhs)));
+ bitmap_set_bit (si->va_list_escape_vars, SSA_NAME_VERSION (lhs));
}
{
basic_block bb;
- FOR_EACH_BB (bb)
+ FOR_EACH_BB_FN (bb, cfun)
{
- block_stmt_iterator i;
+ for (gphi_iterator i = gsi_start_phis (bb); !gsi_end_p (i);
+ gsi_next (&i))
+ {
+ tree lhs;
+ use_operand_p uop;
+ ssa_op_iter soi;
+ gphi *phi = i.phi ();
+
+ lhs = PHI_RESULT (phi);
+ if (virtual_operand_p (lhs)
+ || bitmap_bit_p (si->va_list_escape_vars,
+ SSA_NAME_VERSION (lhs)))
+ continue;
+
+ FOR_EACH_PHI_ARG (uop, phi, soi, SSA_OP_USE)
+ {
+ tree rhs = USE_FROM_PTR (uop);
+ if (TREE_CODE (rhs) == SSA_NAME
+ && bitmap_bit_p (si->va_list_escape_vars,
+ SSA_NAME_VERSION (rhs)))
+ {
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ {
+ fputs ("va_list escapes in ", dump_file);
+ print_gimple_stmt (dump_file, phi, 0, dump_flags);
+ fputc ('\n', dump_file);
+ }
+ return true;
+ }
+ }
+ }
- for (i = bsi_start (bb); !bsi_end_p (i); bsi_next (&i))
+ for (gimple_stmt_iterator i = gsi_start_bb (bb); !gsi_end_p (i);
+ gsi_next (&i))
{
- tree stmt = bsi_stmt (i), use;
+ gimple *stmt = gsi_stmt (i);
+ tree use;
ssa_op_iter iter;
+ if (is_gimple_debug (stmt))
+ continue;
+
FOR_EACH_SSA_TREE_OPERAND (use, stmt, iter, SSA_OP_ALL_USES)
{
if (! bitmap_bit_p (si->va_list_escape_vars,
- DECL_UID (SSA_NAME_VAR (use))))
+ SSA_NAME_VERSION (use)))
continue;
- if (TREE_CODE (stmt) == GIMPLE_MODIFY_STMT)
+ if (is_gimple_assign (stmt))
{
- tree lhs = GIMPLE_STMT_OPERAND (stmt, 0);
- tree rhs = GIMPLE_STMT_OPERAND (stmt, 1);
-
- if (TREE_CODE (rhs) == WITH_SIZE_EXPR)
- rhs = TREE_OPERAND (rhs, 0);
+ tree rhs = gimple_assign_rhs1 (stmt);
+ enum tree_code rhs_code = gimple_assign_rhs_code (stmt);
/* x = *ap_temp; */
- if (TREE_CODE (rhs) == INDIRECT_REF
+ if (rhs_code == MEM_REF
&& TREE_OPERAND (rhs, 0) == use
&& TYPE_SIZE_UNIT (TREE_TYPE (rhs))
- && host_integerp (TYPE_SIZE_UNIT (TREE_TYPE (rhs)), 1)
+ && tree_fits_uhwi_p (TYPE_SIZE_UNIT (TREE_TYPE (rhs)))
&& si->offsets[SSA_NAME_VERSION (use)] != -1)
{
unsigned HOST_WIDE_INT gpr_size;
tree access_size = TYPE_SIZE_UNIT (TREE_TYPE (rhs));
gpr_size = si->offsets[SSA_NAME_VERSION (use)]
- + tree_low_cst (access_size, 1);
+ + tree_to_shwi (TREE_OPERAND (rhs, 1))
+ + tree_to_uhwi (access_size);
if (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)
other_ap_temp = (some_type *) ap_temp;
ap = ap_temp;
statements. */
- if ((TREE_CODE (rhs) == PLUS_EXPR
- && TREE_CODE (TREE_OPERAND (rhs, 1)) == INTEGER_CST)
- || TREE_CODE (rhs) == NOP_EXPR
- || TREE_CODE (rhs) == CONVERT_EXPR)
- rhs = TREE_OPERAND (rhs, 0);
-
- if (rhs == use)
+ if (rhs == use
+ && ((rhs_code == POINTER_PLUS_EXPR
+ && (TREE_CODE (gimple_assign_rhs2 (stmt))
+ == INTEGER_CST))
+ || gimple_assign_cast_p (stmt)
+ || (get_gimple_rhs_class (rhs_code)
+ == GIMPLE_SINGLE_RHS)))
{
+ tree lhs = gimple_assign_lhs (stmt);
+
if (TREE_CODE (lhs) == SSA_NAME
&& bitmap_bit_p (si->va_list_escape_vars,
- DECL_UID (SSA_NAME_VAR (lhs))))
+ SSA_NAME_VERSION (lhs)))
continue;
- if (TREE_CODE (lhs) == VAR_DECL
+ if (VAR_P (lhs)
&& bitmap_bit_p (si->va_list_vars,
- DECL_UID (lhs)))
+ DECL_UID (lhs) + num_ssa_names))
+ continue;
+ }
+ else if (rhs_code == ADDR_EXPR
+ && TREE_CODE (TREE_OPERAND (rhs, 0)) == MEM_REF
+ && TREE_OPERAND (TREE_OPERAND (rhs, 0), 0) == use)
+ {
+ tree lhs = gimple_assign_lhs (stmt);
+
+ if (bitmap_bit_p (si->va_list_escape_vars,
+ SSA_NAME_VERSION (lhs)))
continue;
}
}
if (dump_file && (dump_flags & TDF_DETAILS))
{
fputs ("va_list escapes in ", dump_file);
- print_generic_expr (dump_file, stmt, dump_flags);
+ print_gimple_stmt (dump_file, stmt, 0, dump_flags);
fputc ('\n', dump_file);
}
return true;
return false;
}
+/* Optimize FUN->va_list_gpr_size and FUN->va_list_fpr_size. */
-/* Return true if this optimization pass should be done.
- It makes only sense for stdarg functions. */
-
-static bool
-gate_optimize_stdarg (void)
-{
- /* This optimization is only for stdarg functions. */
- return current_function_stdarg != 0;
-}
-
-
-/* Entry point to the stdarg optimization pass. */
-
-static unsigned int
-execute_optimize_stdarg (void)
+static void
+optimize_va_list_gpr_fpr_size (function *fun)
{
basic_block bb;
bool va_list_escapes = false;
bool va_list_simple_ptr;
struct stdarg_info si;
+ struct walk_stmt_info wi;
const char *funcname = NULL;
+ tree cfun_va_list;
- cfun->va_list_gpr_size = 0;
- cfun->va_list_fpr_size = 0;
+ fun->va_list_gpr_size = 0;
+ fun->va_list_fpr_size = 0;
memset (&si, 0, sizeof (si));
si.va_list_vars = BITMAP_ALLOC (NULL);
si.va_list_escape_vars = BITMAP_ALLOC (NULL);
if (dump_file)
funcname = lang_hooks.decl_printable_name (current_function_decl, 2);
- 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) == char_type_node);
- gcc_assert (is_gimple_reg_type (va_list_type_node) == va_list_simple_ptr);
+ cfun_va_list = targetm.fn_abi_va_list (fun->decl);
+ va_list_simple_ptr = POINTER_TYPE_P (cfun_va_list)
+ && (TREE_TYPE (cfun_va_list) == void_type_node
+ || TREE_TYPE (cfun_va_list) == char_type_node);
+ gcc_assert (is_gimple_reg_type (cfun_va_list) == va_list_simple_ptr);
- FOR_EACH_BB (bb)
+ FOR_EACH_BB_FN (bb, fun)
{
- block_stmt_iterator i;
+ gimple_stmt_iterator i;
- for (i = bsi_start (bb); !bsi_end_p (i); bsi_next (&i))
+ for (i = gsi_start_bb (bb); !gsi_end_p (i); gsi_next (&i))
{
- tree stmt = bsi_stmt (i);
- tree call = get_call_expr_in (stmt), callee;
- tree ap;
+ gimple *stmt = gsi_stmt (i);
+ tree callee, ap;
- if (!call)
+ if (!is_gimple_call (stmt))
continue;
- callee = get_callee_fndecl (call);
+ callee = gimple_call_fndecl (stmt);
if (!callee
- || DECL_BUILT_IN_CLASS (callee) != BUILT_IN_NORMAL)
+ || !fndecl_built_in_p (callee, BUILT_IN_NORMAL))
continue;
switch (DECL_FUNCTION_CODE (callee))
break;
/* If old style builtins are used, don't optimize anything. */
case BUILT_IN_SAVEREGS:
- case BUILT_IN_STDARG_START:
- case BUILT_IN_ARGS_INFO:
case BUILT_IN_NEXT_ARG:
va_list_escapes = true;
continue;
}
si.va_start_count++;
- ap = TREE_VALUE (TREE_OPERAND (call, 1));
+ ap = gimple_call_arg (stmt, 0);
if (TREE_CODE (ap) != ADDR_EXPR)
{
ap = TREE_OPERAND (ap, 0);
}
if (TYPE_MAIN_VARIANT (TREE_TYPE (ap))
- != TYPE_MAIN_VARIANT (va_list_type_node)
- || TREE_CODE (ap) != VAR_DECL)
+ != TYPE_MAIN_VARIANT (targetm.fn_abi_va_list (fun->decl))
+ || !VAR_P (ap))
{
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) + num_ssa_names);
/* VA_START_BB and VA_START_AP will be only used if there is just
one va_start in the function. */
/* For void * or char * va_list there is just one counter
(va_list itself). Use VA_LIST_GPR_SIZE for it. */
if (va_list_simple_ptr)
- cfun->va_list_fpr_size = VA_LIST_MAX_FPR_SIZE;
+ fun->va_list_fpr_size = VA_LIST_MAX_FPR_SIZE;
calculate_dominance_info (CDI_DOMINATORS);
+ memset (&wi, 0, sizeof (wi));
+ wi.info = si.va_list_vars;
- FOR_EACH_BB (bb)
+ FOR_EACH_BB_FN (bb, fun)
{
- block_stmt_iterator i;
-
si.compute_sizes = -1;
si.bb = bb;
/* 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
not needed for non-simple va_list because virtual phis don't perform
- any real data movement. */
- if (va_list_simple_ptr)
+ any real data movement. Also, check PHI nodes for taking address of
+ the va_list vars. */
+ tree lhs, rhs;
+ use_operand_p uop;
+ ssa_op_iter soi;
+
+ for (gphi_iterator i = gsi_start_phis (bb); !gsi_end_p (i);
+ gsi_next (&i))
{
- tree phi, lhs, rhs;
- use_operand_p uop;
- ssa_op_iter soi;
+ gphi *phi = i.phi ();
+ lhs = PHI_RESULT (phi);
- for (phi = phi_nodes (bb); phi; phi = PHI_CHAIN (phi))
- {
- lhs = PHI_RESULT (phi);
-
- if (!is_gimple_reg (lhs))
- continue;
+ if (virtual_operand_p (lhs))
+ continue;
+ if (va_list_simple_ptr)
+ {
FOR_EACH_PHI_ARG (uop, phi, soi, SSA_OP_USE)
{
rhs = USE_FROM_PTR (uop);
else
check_va_list_escapes (&si, lhs, rhs);
- if (si.va_list_escapes
- || walk_tree (&phi, find_va_list_reference,
- si.va_list_vars, NULL))
+ if (si.va_list_escapes)
{
if (dump_file && (dump_flags & TDF_DETAILS))
{
fputs ("va_list escapes in ", dump_file);
- print_generic_expr (dump_file, phi, dump_flags);
+ print_gimple_stmt (dump_file, phi, 0, dump_flags);
fputc ('\n', dump_file);
}
va_list_escapes = true;
}
}
}
+
+ for (unsigned j = 0; !va_list_escapes
+ && j < gimple_phi_num_args (phi); ++j)
+ if ((!va_list_simple_ptr
+ || TREE_CODE (gimple_phi_arg_def (phi, j)) != SSA_NAME)
+ && walk_tree (gimple_phi_arg_def_ptr (phi, j),
+ find_va_list_reference, &wi, NULL))
+ {
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ {
+ fputs ("va_list escapes in ", dump_file);
+ print_gimple_stmt (dump_file, phi, 0, dump_flags);
+ fputc ('\n', dump_file);
+ }
+ va_list_escapes = true;
+ }
}
- for (i = bsi_start (bb);
- !bsi_end_p (i) && !va_list_escapes;
- bsi_next (&i))
+ for (gimple_stmt_iterator i = gsi_start_bb (bb);
+ !gsi_end_p (i) && !va_list_escapes;
+ gsi_next (&i))
{
- tree stmt = bsi_stmt (i);
- tree call;
+ gimple *stmt = gsi_stmt (i);
/* Don't look at __builtin_va_{start,end}, they are ok. */
- call = get_call_expr_in (stmt);
- if (call)
+ if (is_gimple_call (stmt))
{
- tree callee = get_callee_fndecl (call);
+ tree callee = gimple_call_fndecl (stmt);
if (callee
- && DECL_BUILT_IN_CLASS (callee) == BUILT_IN_NORMAL
- && (DECL_FUNCTION_CODE (callee) == BUILT_IN_VA_START
- || DECL_FUNCTION_CODE (callee) == BUILT_IN_VA_END))
+ && (fndecl_built_in_p (callee, BUILT_IN_VA_START)
+ || fndecl_built_in_p (callee, BUILT_IN_VA_END)))
continue;
}
- if (TREE_CODE (stmt) == GIMPLE_MODIFY_STMT)
+ if (is_gimple_assign (stmt))
{
- tree lhs = GIMPLE_STMT_OPERAND (stmt, 0);
- tree rhs = GIMPLE_STMT_OPERAND (stmt, 1);
-
- if (TREE_CODE (rhs) == WITH_SIZE_EXPR)
- rhs = TREE_OPERAND (rhs, 0);
+ lhs = gimple_assign_lhs (stmt);
+ rhs = gimple_assign_rhs1 (stmt);
if (va_list_simple_ptr)
{
- /* Check for tem = ap. */
- if (va_list_ptr_read (&si, rhs, lhs))
- continue;
+ if (get_gimple_rhs_class (gimple_assign_rhs_code (stmt))
+ == GIMPLE_SINGLE_RHS)
+ {
+ /* Check for ap ={v} {}. */
+ if (TREE_CLOBBER_P (rhs))
+ continue;
- /* Check for the last insn in:
- tem1 = ap;
- tem2 = tem1 + CST;
- ap = tem2;
- sequence. */
- else if (va_list_ptr_write (&si, lhs, rhs))
- continue;
+ /* Check for tem = ap. */
+ else if (va_list_ptr_read (&si, rhs, lhs))
+ continue;
- else
+ /* Check for the last insn in:
+ tem1 = ap;
+ tem2 = tem1 + CST;
+ ap = tem2;
+ sequence. */
+ else if (va_list_ptr_write (&si, lhs, rhs))
+ continue;
+ }
+
+ if ((gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR
+ && TREE_CODE (gimple_assign_rhs2 (stmt)) == INTEGER_CST)
+ || CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (stmt))
+ || (get_gimple_rhs_class (gimple_assign_rhs_code (stmt))
+ == GIMPLE_SINGLE_RHS))
check_va_list_escapes (&si, lhs, rhs);
}
else
{
- /* Check for ap[0].field = temp. */
- if (va_list_counter_struct_op (&si, lhs, rhs, true))
- continue;
+ if (get_gimple_rhs_class (gimple_assign_rhs_code (stmt))
+ == GIMPLE_SINGLE_RHS)
+ {
+ /* Check for ap ={v} {}. */
+ if (TREE_CLOBBER_P (rhs))
+ continue;
- /* Check for temp = ap[0].field. */
- else if (va_list_counter_struct_op (&si, rhs, lhs, false))
- continue;
+ /* Check for ap[0].field = temp. */
+ else if (va_list_counter_struct_op (&si, lhs, rhs, true))
+ continue;
+
+ /* Check for temp = ap[0].field. */
+ else if (va_list_counter_struct_op (&si, rhs, lhs,
+ false))
+ continue;
+ }
/* Do any architecture specific checking. */
- else if (targetm.stdarg_optimize_hook
- && targetm.stdarg_optimize_hook (&si, lhs, rhs))
+ if (targetm.stdarg_optimize_hook
+ && targetm.stdarg_optimize_hook (&si, stmt))
continue;
}
}
+ else if (is_gimple_debug (stmt))
+ continue;
/* All other uses of va_list are either va_copy (that is not handled
in this optimization), taking address of va_list variable or
fully), or some unexpected use of va_list. None of these should
happen in a gimplified VA_ARG_EXPR. */
if (si.va_list_escapes
- || walk_tree (&stmt, find_va_list_reference,
- si.va_list_vars, NULL))
+ || walk_gimple_op (stmt, find_va_list_reference, &wi))
{
if (dump_file && (dump_flags & TDF_DETAILS))
{
fputs ("va_list escapes in ", dump_file);
- print_generic_expr (dump_file, stmt, dump_flags);
+ print_gimple_stmt (dump_file, stmt, 0, dump_flags);
fputc ('\n', dump_file);
}
va_list_escapes = true;
finish:
if (va_list_escapes)
{
- cfun->va_list_gpr_size = VA_LIST_MAX_GPR_SIZE;
- cfun->va_list_fpr_size = VA_LIST_MAX_FPR_SIZE;
+ fun->va_list_gpr_size = VA_LIST_MAX_GPR_SIZE;
+ fun->va_list_fpr_size = VA_LIST_MAX_FPR_SIZE;
}
BITMAP_FREE (si.va_list_vars);
BITMAP_FREE (si.va_list_escape_vars);
{
fprintf (dump_file, "%s: va_list escapes %d, needs to save ",
funcname, (int) va_list_escapes);
- if (cfun->va_list_gpr_size >= VA_LIST_MAX_GPR_SIZE)
+ if (fun->va_list_gpr_size >= VA_LIST_MAX_GPR_SIZE)
fputs ("all", dump_file);
else
fprintf (dump_file, "%d", cfun->va_list_gpr_size);
fputs (" GPR units and ", dump_file);
- if (cfun->va_list_fpr_size >= VA_LIST_MAX_FPR_SIZE)
+ if (fun->va_list_fpr_size >= VA_LIST_MAX_FPR_SIZE)
fputs ("all", dump_file);
else
fprintf (dump_file, "%d", cfun->va_list_fpr_size);
fputs (" FPR units.\n", dump_file);
}
+}
+
+/* Expand IFN_VA_ARGs in FUN. */
+
+static void
+expand_ifn_va_arg_1 (function *fun)
+{
+ bool modified = false;
+ basic_block bb;
+ gimple_stmt_iterator i;
+ location_t saved_location;
+
+ FOR_EACH_BB_FN (bb, fun)
+ for (i = gsi_start_bb (bb); !gsi_end_p (i); gsi_next (&i))
+ {
+ gimple *stmt = gsi_stmt (i);
+ tree ap, aptype, expr, lhs, type;
+ gimple_seq pre = NULL, post = NULL;
+
+ if (!gimple_call_internal_p (stmt, IFN_VA_ARG))
+ continue;
+
+ modified = true;
+
+ type = TREE_TYPE (TREE_TYPE (gimple_call_arg (stmt, 1)));
+ ap = gimple_call_arg (stmt, 0);
+ aptype = TREE_TYPE (gimple_call_arg (stmt, 2));
+ gcc_assert (POINTER_TYPE_P (aptype));
+
+ /* Balanced out the &ap, usually added by build_va_arg. */
+ ap = build2 (MEM_REF, TREE_TYPE (aptype), ap,
+ build_int_cst (aptype, 0));
+
+ push_gimplify_context (false);
+ saved_location = input_location;
+ input_location = gimple_location (stmt);
+
+ /* Make it easier for the backends by protecting the valist argument
+ from multiple evaluations. */
+ gimplify_expr (&ap, &pre, &post, is_gimple_min_lval, fb_lvalue);
+
+ expr = targetm.gimplify_va_arg_expr (ap, type, &pre, &post);
+
+ lhs = gimple_call_lhs (stmt);
+ if (lhs != NULL_TREE)
+ {
+ unsigned int nargs = gimple_call_num_args (stmt);
+ gcc_assert (useless_type_conversion_p (TREE_TYPE (lhs), type));
+
+ if (nargs == 4)
+ {
+ /* We've transported the size of with WITH_SIZE_EXPR here as
+ the last argument of the internal fn call. Now reinstate
+ it. */
+ tree size = gimple_call_arg (stmt, nargs - 1);
+ expr = build2 (WITH_SIZE_EXPR, TREE_TYPE (expr), expr, size);
+ }
+
+ /* We use gimplify_assign here, rather than gimple_build_assign,
+ because gimple_assign knows how to deal with variable-sized
+ types. */
+ gimplify_assign (lhs, expr, &pre);
+ }
+ else
+ gimplify_and_add (expr, &pre);
+
+ input_location = saved_location;
+ pop_gimplify_context (NULL);
+
+ gimple_seq_add_seq (&pre, post);
+ update_modified_stmts (pre);
+
+ /* Add the sequence after IFN_VA_ARG. This splits the bb right
+ after IFN_VA_ARG, and adds the sequence in one or more new bbs
+ inbetween. */
+ gimple_find_sub_bbs (pre, &i);
+
+ /* Remove the IFN_VA_ARG gimple_call. It's the last stmt in the
+ bb. */
+ unlink_stmt_vdef (stmt);
+ release_ssa_name_fn (fun, gimple_vdef (stmt));
+ gsi_remove (&i, true);
+ gcc_assert (gsi_end_p (i));
+
+ /* We're walking here into the bbs which contain the expansion of
+ IFN_VA_ARG, and will not contain another IFN_VA_ARG that needs
+ expanding. We could try to skip walking these bbs, perhaps by
+ walking backwards over gimples and bbs. */
+ break;
+ }
+
+ if (!modified)
+ return;
+
+ free_dominance_info (CDI_DOMINATORS);
+ update_ssa (TODO_update_ssa);
+}
+
+/* Expand IFN_VA_ARGs in FUN, if necessary. */
+
+static void
+expand_ifn_va_arg (function *fun)
+{
+ if ((fun->curr_properties & PROP_gimple_lva) == 0)
+ expand_ifn_va_arg_1 (fun);
+
+ if (flag_checking)
+ {
+ basic_block bb;
+ gimple_stmt_iterator i;
+ FOR_EACH_BB_FN (bb, fun)
+ for (i = gsi_start_bb (bb); !gsi_end_p (i); gsi_next (&i))
+ gcc_assert (!gimple_call_internal_p (gsi_stmt (i), IFN_VA_ARG));
+ }
+}
+
+namespace {
+
+const pass_data pass_data_stdarg =
+{
+ GIMPLE_PASS, /* type */
+ "stdarg", /* name */
+ OPTGROUP_NONE, /* optinfo_flags */
+ TV_NONE, /* tv_id */
+ ( PROP_cfg | PROP_ssa ), /* properties_required */
+ PROP_gimple_lva, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ 0, /* todo_flags_finish */
+};
+
+class pass_stdarg : public gimple_opt_pass
+{
+public:
+ pass_stdarg (gcc::context *ctxt)
+ : gimple_opt_pass (pass_data_stdarg, ctxt)
+ {}
+
+ /* opt_pass methods: */
+ virtual bool gate (function *)
+ {
+ /* Always run this pass, in order to expand va_arg internal_fns. We
+ also need to do that if fun->stdarg == 0, because a va_arg may also
+ occur in a function without varargs, f.i. if when passing a va_list to
+ another function. */
+ return true;
+ }
+
+ virtual unsigned int execute (function *);
+
+}; // class pass_stdarg
+
+unsigned int
+pass_stdarg::execute (function *fun)
+{
+ /* TODO: Postpone expand_ifn_va_arg till after
+ optimize_va_list_gpr_fpr_size. */
+ expand_ifn_va_arg (fun);
+
+ if (flag_stdarg_opt
+ /* This optimization is only for stdarg functions. */
+ && fun->stdarg != 0)
+ optimize_va_list_gpr_fpr_size (fun);
+
return 0;
}
+} // anon namespace
+
+gimple_opt_pass *
+make_pass_stdarg (gcc::context *ctxt)
+{
+ return new pass_stdarg (ctxt);
+}
+
+namespace {
-struct tree_opt_pass pass_stdarg =
+const pass_data pass_data_lower_vaarg =
{
- "stdarg", /* name */
- gate_optimize_stdarg, /* gate */
- execute_optimize_stdarg, /* execute */
- NULL, /* sub */
- NULL, /* next */
- 0, /* static_pass_number */
- 0, /* tv_id */
- PROP_cfg | PROP_ssa | PROP_alias, /* properties_required */
- 0, /* properties_provided */
- 0, /* properties_destroyed */
- 0, /* todo_flags_start */
- TODO_dump_func, /* todo_flags_finish */
- 0 /* letter */
+ GIMPLE_PASS, /* type */
+ "lower_vaarg", /* name */
+ OPTGROUP_NONE, /* optinfo_flags */
+ TV_NONE, /* tv_id */
+ ( PROP_cfg | PROP_ssa ), /* properties_required */
+ PROP_gimple_lva, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ 0, /* todo_flags_finish */
};
+
+class pass_lower_vaarg : public gimple_opt_pass
+{
+public:
+ pass_lower_vaarg (gcc::context *ctxt)
+ : gimple_opt_pass (pass_data_lower_vaarg, ctxt)
+ {}
+
+ /* opt_pass methods: */
+ virtual bool gate (function *)
+ {
+ return (cfun->curr_properties & PROP_gimple_lva) == 0;
+ }
+
+ virtual unsigned int execute (function *);
+
+}; // class pass_lower_vaarg
+
+unsigned int
+pass_lower_vaarg::execute (function *fun)
+{
+ expand_ifn_va_arg (fun);
+ return 0;
+}
+
+} // anon namespace
+
+gimple_opt_pass *
+make_pass_lower_vaarg (gcc::context *ctxt)
+{
+ return new pass_lower_vaarg (ctxt);
+}