From d086d3119d95e23154679e6c6ca43b0356fd92df Mon Sep 17 00:00:00 2001 From: Richard Guenther Date: Mon, 12 Apr 2010 15:20:48 +0000 Subject: [PATCH] gsstruct.def (GSS_CALL): New. 2010-04-12 Richard Guenther * gsstruct.def (GSS_CALL): New. * gimple.def (GIMPLE_CALL): Change to GSS_CALL. * gimple.h: Include tree-ssa-alias.h. (struct gimple_statement_call): New. (union gimple_statement_struct_d): Add gimple_call member. (gimple_call_reset_alias_info): Declare. (gimple_call_use_set): New function. (gimple_call_clobber_set): Likewise. * Makefile.in (GIMPLE_H): Add tree-ssa-alias.h. * gimple.c (gimple_call_reset_alias_info): New function. (gimple_build_call_1): Call it. * lto-streamer-in.c (input_gimple_stmt): Likewise. * tree-inline.c (remap_gimple_stmt): Likewise. (expand_call_inline): Remove callused handling. * cfgexpand.c (update_alias_info_with_stack_vars): Likewise. * tree-dfa.c (dump_variable): Likewise. * tree-parloops.c (parallelize_loops): Likewise. * tree-ssa.c (init_tree_ssa): Likewise. (delete_tree_ssa): Likewise. * tree-flow-inline.h (is_call_used): Remove. * tree-flow.h (struct gimple_df): Remove callused member. * tree-nrv.c (dest_safe_for_nrv_p): Adjust predicate. * tree-ssa-alias.c (dump_alias_info): Remove callused handling. (ref_maybe_used_by_call_p_1): Simplify. (call_may_clobber_ref_p_1): Likewise. * tree-ssa-structalias.c (compute_points_to_sets): Set the call stmt used and clobbered sets. * tree-tailcall.c (suitable_for_tail_opt_p): Adjust predicate. (find_tail_calls): Verify the tail call. From-SVN: r158226 --- gcc/ChangeLog | 32 ++++++++++++++++++++ gcc/Makefile.in | 3 +- gcc/gimple.c | 16 ++++++++++ gcc/gimple.def | 2 +- gcc/gimple.h | 44 +++++++++++++++++++++++++++ gcc/gsstruct.def | 1 + gcc/lto-streamer-in.c | 4 +++ gcc/tree-dfa.c | 2 -- gcc/tree-flow-inline.h | 9 ------ gcc/tree-flow.h | 3 -- gcc/tree-inline.c | 14 +++++---- gcc/tree-nrv.c | 2 +- gcc/tree-parloops.c | 9 ++---- gcc/tree-ssa-alias.c | 61 ++++++++++---------------------------- gcc/tree-ssa-structalias.c | 46 ++++++++++++++++++++++++++-- gcc/tree-ssa.c | 2 -- gcc/tree-tailcall.c | 29 ++++++++++++++++-- 17 files changed, 199 insertions(+), 80 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 91a18dca295..136ffae6703 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,35 @@ +2010-04-12 Richard Guenther + + * gsstruct.def (GSS_CALL): New. + * gimple.def (GIMPLE_CALL): Change to GSS_CALL. + * gimple.h: Include tree-ssa-alias.h. + (struct gimple_statement_call): New. + (union gimple_statement_struct_d): Add gimple_call member. + (gimple_call_reset_alias_info): Declare. + (gimple_call_use_set): New function. + (gimple_call_clobber_set): Likewise. + * Makefile.in (GIMPLE_H): Add tree-ssa-alias.h. + * gimple.c (gimple_call_reset_alias_info): New function. + (gimple_build_call_1): Call it. + * lto-streamer-in.c (input_gimple_stmt): Likewise. + * tree-inline.c (remap_gimple_stmt): Likewise. + (expand_call_inline): Remove callused handling. + * cfgexpand.c (update_alias_info_with_stack_vars): Likewise. + * tree-dfa.c (dump_variable): Likewise. + * tree-parloops.c (parallelize_loops): Likewise. + * tree-ssa.c (init_tree_ssa): Likewise. + (delete_tree_ssa): Likewise. + * tree-flow-inline.h (is_call_used): Remove. + * tree-flow.h (struct gimple_df): Remove callused member. + * tree-nrv.c (dest_safe_for_nrv_p): Adjust predicate. + * tree-ssa-alias.c (dump_alias_info): Remove callused handling. + (ref_maybe_used_by_call_p_1): Simplify. + (call_may_clobber_ref_p_1): Likewise. + * tree-ssa-structalias.c (compute_points_to_sets): Set + the call stmt used and clobbered sets. + * tree-tailcall.c (suitable_for_tail_opt_p): Adjust predicate. + (find_tail_calls): Verify the tail call. + 2010-04-12 Richard Guenther * ipa.c (cgraph_postorder): Adjust postorder to guarantee diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 99811e78351..c1cec397253 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -876,7 +876,8 @@ BASIC_BLOCK_H = basic-block.h $(BITMAP_H) sbitmap.h varray.h $(PARTITION_H) \ hard-reg-set.h $(PREDICT_H) vec.h $(FUNCTION_H) \ cfghooks.h $(OBSTACK_H) GIMPLE_H = gimple.h gimple.def gsstruct.def pointer-set.h vec.h \ - $(GGC_H) $(BASIC_BLOCK_H) $(TM_H) $(TARGET_H) tree-ssa-operands.h + $(GGC_H) $(BASIC_BLOCK_H) $(TM_H) $(TARGET_H) tree-ssa-operands.h \ + tree-ssa-alias.h GCOV_IO_H = gcov-io.h gcov-iov.h auto-host.h COVERAGE_H = coverage.h $(GCOV_IO_H) DEMANGLE_H = $(srcdir)/../include/demangle.h diff --git a/gcc/gimple.c b/gcc/gimple.c index ae0be4e86bc..ce1f75a884e 100644 --- a/gcc/gimple.c +++ b/gcc/gimple.c @@ -198,6 +198,21 @@ gimple_build_return (tree retval) return s; } +/* Reset alias information on call S. */ + +void +gimple_call_reset_alias_info (gimple s) +{ + if (gimple_call_flags (s) & ECF_CONST) + memset (gimple_call_use_set (s), 0, sizeof (struct pt_solution)); + else + pt_solution_reset (gimple_call_use_set (s)); + if (gimple_call_flags (s) & (ECF_CONST|ECF_PURE|ECF_NOVOPS)) + memset (gimple_call_clobber_set (s), 0, sizeof (struct pt_solution)); + else + pt_solution_reset (gimple_call_clobber_set (s)); +} + /* Helper for gimple_build_call, gimple_build_call_vec and gimple_build_call_from_tree. Build the basic components of a GIMPLE_CALL statement to function FN with NARGS arguments. */ @@ -209,6 +224,7 @@ gimple_build_call_1 (tree fn, unsigned nargs) if (TREE_CODE (fn) == FUNCTION_DECL) fn = build_fold_addr_expr (fn); gimple_set_op (s, 1, fn); + gimple_call_reset_alias_info (s); return s; } diff --git a/gcc/gimple.def b/gcc/gimple.def index 7a1503c9270..49aa88eab33 100644 --- a/gcc/gimple.def +++ b/gcc/gimple.def @@ -122,7 +122,7 @@ DEFGSCODE(GIMPLE_ASM, "gimple_asm", GSS_ASM) is_gimple_operand. CHAIN is the optional static chain link for nested functions. */ -DEFGSCODE(GIMPLE_CALL, "gimple_call", GSS_WITH_MEM_OPS) +DEFGSCODE(GIMPLE_CALL, "gimple_call", GSS_CALL) /* GIMPLE_RETURN represents return statements. diff --git a/gcc/gimple.h b/gcc/gimple.h index b09e8561f10..9df6e92dacf 100644 --- a/gcc/gimple.h +++ b/gcc/gimple.h @@ -29,6 +29,7 @@ along with GCC; see the file COPYING3. If not see #include "hard-reg-set.h" #include "basic-block.h" #include "tree-ssa-operands.h" +#include "tree-ssa-alias.h" DEF_VEC_P(gimple); DEF_VEC_ALLOC_P(gimple,heap); @@ -390,6 +391,25 @@ struct GTY(()) gimple_statement_with_memory_ops }; +/* Call statements that take both memory and register operands. */ + +struct GTY(()) gimple_statement_call +{ + /* [ WORD 1-8 ] */ + struct gimple_statement_with_memory_ops_base membase; + + /* [ WORD 9-12 ] */ + struct pt_solution call_used; + struct pt_solution call_clobbered; + + /* [ WORD 13 ] + Operand vector. NOTE! This must always be the last field + of this structure. In particular, this means that this + structure cannot be embedded inside another one. */ + tree GTY((length ("%h.membase.opbase.gsbase.num_ops"))) op[1]; +}; + + /* OpenMP statements (#pragma omp). */ struct GTY(()) gimple_statement_omp { @@ -739,6 +759,7 @@ union GTY ((desc ("gimple_statement_structure (&%h)"))) gimple_statement_d { struct gimple_statement_with_ops GTY ((tag ("GSS_WITH_OPS"))) gsops; struct gimple_statement_with_memory_ops_base GTY ((tag ("GSS_WITH_MEM_OPS_BASE"))) gsmembase; struct gimple_statement_with_memory_ops GTY ((tag ("GSS_WITH_MEM_OPS"))) gsmem; + struct gimple_statement_call GTY ((tag ("GSS_CALL"))) gimple_call; struct gimple_statement_omp GTY ((tag ("GSS_OMP"))) omp; struct gimple_statement_bind GTY ((tag ("GSS_BIND"))) gimple_bind; struct gimple_statement_catch GTY ((tag ("GSS_CATCH"))) gimple_catch; @@ -836,6 +857,7 @@ void gimple_seq_free (gimple_seq); void gimple_seq_add_seq (gimple_seq *, gimple_seq); gimple_seq gimple_seq_copy (gimple_seq); int gimple_call_flags (const_gimple); +void gimple_call_reset_alias_info (gimple); bool gimple_assign_copy_p (gimple); bool gimple_assign_ssa_name_copy_p (gimple); bool gimple_assign_single_p (gimple); @@ -2200,6 +2222,28 @@ gimple_call_copy_flags (gimple dest_call, gimple orig_call) } +/* Return a pointer to the points-to solution for the set of call-used + variables of the call CALL. */ + +static inline struct pt_solution * +gimple_call_use_set (gimple call) +{ + GIMPLE_CHECK (call, GIMPLE_CALL); + return &call->gimple_call.call_used; +} + + +/* Return a pointer to the points-to solution for the set of call-used + variables of the call CALL. */ + +static inline struct pt_solution * +gimple_call_clobber_set (gimple call) +{ + GIMPLE_CHECK (call, GIMPLE_CALL); + return &call->gimple_call.call_clobbered; +} + + /* Returns true if this is a GIMPLE_ASSIGN or a GIMPLE_CALL with a non-NULL lhs. */ diff --git a/gcc/gsstruct.def b/gcc/gsstruct.def index 7fd9547f650..578d2c19e80 100644 --- a/gcc/gsstruct.def +++ b/gcc/gsstruct.def @@ -29,6 +29,7 @@ DEFGSSTRUCT(GSS_BASE, gimple_statement_base, false) DEFGSSTRUCT(GSS_WITH_OPS, gimple_statement_with_ops, true) DEFGSSTRUCT(GSS_WITH_MEM_OPS_BASE, gimple_statement_with_memory_ops_base, false) DEFGSSTRUCT(GSS_WITH_MEM_OPS, gimple_statement_with_memory_ops, true) +DEFGSSTRUCT(GSS_CALL, gimple_statement_call, true) DEFGSSTRUCT(GSS_ASM, gimple_statement_asm, true) DEFGSSTRUCT(GSS_BIND, gimple_statement_bind, false) DEFGSSTRUCT(GSS_PHI, gimple_statement_phi, false) diff --git a/gcc/lto-streamer-in.c b/gcc/lto-streamer-in.c index 53b1c33b032..6afad5b612c 100644 --- a/gcc/lto-streamer-in.c +++ b/gcc/lto-streamer-in.c @@ -1157,6 +1157,10 @@ input_gimple_stmt (struct lto_input_block *ib, struct data_in *data_in, } } + /* Reset alias information. */ + if (code == GIMPLE_CALL) + gimple_call_reset_alias_info (stmt); + /* Fixup reference tree operands for substituted prevailing decls with mismatched types. */ maybe_fixup_decls (stmt); diff --git a/gcc/tree-dfa.c b/gcc/tree-dfa.c index a918f4ba2bd..bd91e3b60a8 100644 --- a/gcc/tree-dfa.c +++ b/gcc/tree-dfa.c @@ -284,8 +284,6 @@ dump_variable (FILE *file, tree var) if (is_call_clobbered (var)) fprintf (file, ", call clobbered"); - else if (is_call_used (var)) - fprintf (file, ", call used"); if (ann && ann->noalias_state == NO_ALIAS) fprintf (file, ", NO_ALIAS (does not alias other NO_ALIAS symbols)"); diff --git a/gcc/tree-flow-inline.h b/gcc/tree-flow-inline.h index 7faa585642f..2430f0355f7 100644 --- a/gcc/tree-flow-inline.h +++ b/gcc/tree-flow-inline.h @@ -633,15 +633,6 @@ is_call_clobbered (const_tree var) && pt_solution_includes (&cfun->gimple_df->escaped, var))); } -/* Return true if VAR is used by function calls. */ -static inline bool -is_call_used (const_tree var) -{ - return (is_call_clobbered (var) - || (may_be_aliased (var) - && pt_solution_includes (&cfun->gimple_df->callused, var))); -} - /* ----------------------------------------------------------------------- */ /* The following set of routines are used to iterator over various type of diff --git a/gcc/tree-flow.h b/gcc/tree-flow.h index 7bb5088e9e2..82b810999bc 100644 --- a/gcc/tree-flow.h +++ b/gcc/tree-flow.h @@ -56,9 +56,6 @@ struct GTY(()) gimple_df { /* The PTA solution for the ESCAPED artificial variable. */ struct pt_solution escaped; - /* The PTA solution for the CALLUSED artificial variable. */ - struct pt_solution callused; - /* A map of decls to artificial ssa-names that point to the partition of the decl. */ struct pointer_map_t * GTY((skip(""))) decls_to_pointers; diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c index e0928b9fe03..922ce52dd9d 100644 --- a/gcc/tree-inline.c +++ b/gcc/tree-inline.c @@ -1391,6 +1391,13 @@ remap_gimple_stmt (gimple stmt, copy_body_data *id) default: break; } + + /* Reset alias info. + ??? By maintaining DECL_PT_UID this should not + be necessary, but the plan is to only maintain + it when IPA-PTA was run. It's not too easy to + detect this here ... */ + gimple_call_reset_alias_info (copy); } break; @@ -3724,12 +3731,9 @@ expand_call_inline (basic_block bb, gimple stmt, copy_body_data *id) cg_edge->frequency * REG_BR_PROB_BASE / CGRAPH_FREQ_BASE, bb, return_block); - /* Reset the escaped and callused solutions. */ + /* Reset the escaped solution. */ if (cfun->gimple_df) - { - pt_solution_reset (&cfun->gimple_df->escaped); - pt_solution_reset (&cfun->gimple_df->callused); - } + pt_solution_reset (&cfun->gimple_df->escaped); /* Clean up. */ if (id->debug_map) diff --git a/gcc/tree-nrv.c b/gcc/tree-nrv.c index a825a7a0c3f..b85c5a76c67 100644 --- a/gcc/tree-nrv.c +++ b/gcc/tree-nrv.c @@ -307,7 +307,7 @@ dest_safe_for_nrv_p (tree dest) if (TREE_CODE (dest) == SSA_NAME) dest = SSA_NAME_VAR (dest); - if (is_call_used (dest)) + if (is_call_clobbered (dest)) return false; return true; diff --git a/gcc/tree-parloops.c b/gcc/tree-parloops.c index b8a883fb939..c4ac89b001a 100644 --- a/gcc/tree-parloops.c +++ b/gcc/tree-parloops.c @@ -1977,13 +1977,10 @@ parallelize_loops (void) htab_delete (reduction_list); /* Parallelization will cause new function calls to be inserted through - which local variables will escape. Reset the points-to solutions - for ESCAPED and CALLUSED. */ + which local variables will escape. Reset the points-to solution + for ESCAPED. */ if (changed) - { - pt_solution_reset (&cfun->gimple_df->escaped); - pt_solution_reset (&cfun->gimple_df->callused); - } + pt_solution_reset (&cfun->gimple_df->escaped); return changed; } diff --git a/gcc/tree-ssa-alias.c b/gcc/tree-ssa-alias.c index 282148ce5e9..7f09df8d16c 100644 --- a/gcc/tree-ssa-alias.c +++ b/gcc/tree-ssa-alias.c @@ -336,8 +336,6 @@ dump_alias_info (FILE *file) fprintf (file, "\nESCAPED"); dump_points_to_solution (file, &cfun->gimple_df->escaped); - fprintf (file, "\nCALLUSED"); - dump_points_to_solution (file, &cfun->gimple_df->callused); fprintf (file, "\n\nFlow-insensitive points-to information\n\n"); @@ -1070,51 +1068,24 @@ ref_maybe_used_by_call_p_1 (gimple call, ao_ref *ref) goto process_args; } - /* If the base variable is call-used or call-clobbered then - it may be used. */ - if (flags & (ECF_PURE|ECF_CONST|ECF_LOOPING_CONST_OR_PURE|ECF_NOVOPS)) + /* Check if the base variable is call-used. */ + if (DECL_P (base)) { - if (DECL_P (base)) - { - if (is_call_used (base)) - return true; - } - else if (INDIRECT_REF_P (base) - && TREE_CODE (TREE_OPERAND (base, 0)) == SSA_NAME) - { - struct ptr_info_def *pi = SSA_NAME_PTR_INFO (TREE_OPERAND (base, 0)); - if (!pi) - return true; + if (pt_solution_includes (gimple_call_use_set (call), base)) + return true; + } + else if (INDIRECT_REF_P (base) + && TREE_CODE (TREE_OPERAND (base, 0)) == SSA_NAME) + { + struct ptr_info_def *pi = SSA_NAME_PTR_INFO (TREE_OPERAND (base, 0)); + if (!pi) + return true; - if (pt_solution_includes_global (&pi->pt) - || pt_solutions_intersect (&cfun->gimple_df->callused, &pi->pt) - || pt_solutions_intersect (&cfun->gimple_df->escaped, &pi->pt)) - return true; - } - else + if (pt_solutions_intersect (gimple_call_use_set (call), &pi->pt)) return true; } else - { - if (DECL_P (base)) - { - if (is_call_clobbered (base)) - return true; - } - else if (INDIRECT_REF_P (base) - && TREE_CODE (TREE_OPERAND (base, 0)) == SSA_NAME) - { - struct ptr_info_def *pi = SSA_NAME_PTR_INFO (TREE_OPERAND (base, 0)); - if (!pi) - return true; - - if (pt_solution_includes_global (&pi->pt) - || pt_solutions_intersect (&cfun->gimple_df->escaped, &pi->pt)) - return true; - } - else - return true; - } + return true; /* Inspect call arguments for passed-by-value aliases. */ process_args: @@ -1347,8 +1318,9 @@ call_may_clobber_ref_p_1 (gimple call, ao_ref *ref) return false; } + /* Check if the base variable is call-clobbered. */ if (DECL_P (base)) - return is_call_clobbered (base); + return pt_solution_includes (gimple_call_clobber_set (call), base); else if (INDIRECT_REF_P (base) && TREE_CODE (TREE_OPERAND (base, 0)) == SSA_NAME) { @@ -1356,8 +1328,7 @@ call_may_clobber_ref_p_1 (gimple call, ao_ref *ref) if (!pi) return true; - return (pt_solution_includes_global (&pi->pt) - || pt_solutions_intersect (&cfun->gimple_df->escaped, &pi->pt)); + return pt_solutions_intersect (gimple_call_clobber_set (call), &pi->pt); } return true; diff --git a/gcc/tree-ssa-structalias.c b/gcc/tree-ssa-structalias.c index 2384099a242..e14b97a97f5 100644 --- a/gcc/tree-ssa-structalias.c +++ b/gcc/tree-ssa-structalias.c @@ -5480,6 +5480,7 @@ compute_points_to_sets (void) basic_block bb; unsigned i; varinfo_t vi; + struct pt_solution callused; timevar_push (TV_TREE_PTA); @@ -5516,8 +5517,7 @@ compute_points_to_sets (void) call-clobber analysis. */ find_what_var_points_to (get_varinfo (escaped_id), &cfun->gimple_df->escaped); - find_what_var_points_to (get_varinfo (callused_id), - &cfun->gimple_df->callused); + find_what_var_points_to (get_varinfo (callused_id), &callused); /* Make sure the ESCAPED solution (which is used as placeholder in other solutions) does not reference itself. This simplifies @@ -5541,6 +5541,48 @@ compute_points_to_sets (void) find_what_p_points_to (ptr); } + /* Compute the call-used/clobbered sets. */ + FOR_EACH_BB (bb) + { + gimple_stmt_iterator gsi; + + for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi)) + { + gimple stmt = gsi_stmt (gsi); + struct pt_solution *pt; + if (!is_gimple_call (stmt)) + continue; + + pt = gimple_call_use_set (stmt); + if (gimple_call_flags (stmt) & ECF_CONST) + memset (pt, 0, sizeof (struct pt_solution)); + else if (gimple_call_flags (stmt) & ECF_PURE) + { + /* For const calls we should now be able to compute the + call-used set per function. */ + *pt = callused; + /* ??? ESCAPED can be empty even though NONLOCAL + always escaped. */ + pt->nonlocal = 1; + pt->escaped = 1; + } + else + { + *pt = cfun->gimple_df->escaped; + pt->nonlocal = 1; + } + + pt = gimple_call_clobber_set (stmt); + if (gimple_call_flags (stmt) & (ECF_CONST|ECF_PURE|ECF_NOVOPS)) + memset (pt, 0, sizeof (struct pt_solution)); + else + { + *pt = cfun->gimple_df->escaped; + pt->nonlocal = 1; + } + } + } + timevar_pop (TV_TREE_PTA); } diff --git a/gcc/tree-ssa.c b/gcc/tree-ssa.c index 9947e57fa9f..7915bb88b22 100644 --- a/gcc/tree-ssa.c +++ b/gcc/tree-ssa.c @@ -1123,7 +1123,6 @@ init_tree_ssa (struct function *fn) fn->gimple_df->default_defs = htab_create_ggc (20, uid_ssaname_map_hash, uid_ssaname_map_eq, NULL); pt_solution_reset (&fn->gimple_df->escaped); - pt_solution_reset (&fn->gimple_df->callused); init_ssanames (fn, 0); init_phinodes (); } @@ -1163,7 +1162,6 @@ delete_tree_ssa (void) htab_delete (cfun->gimple_df->default_defs); cfun->gimple_df->default_defs = NULL; pt_solution_reset (&cfun->gimple_df->escaped); - pt_solution_reset (&cfun->gimple_df->callused); if (cfun->gimple_df->decls_to_pointers != NULL) pointer_map_destroy (cfun->gimple_df->decls_to_pointers); cfun->gimple_df->decls_to_pointers = NULL; diff --git a/gcc/tree-tailcall.c b/gcc/tree-tailcall.c index 2ecca4aaae7..e0d3f4844e9 100644 --- a/gcc/tree-tailcall.c +++ b/gcc/tree-tailcall.c @@ -136,11 +136,23 @@ suitable_for_tail_opt_p (void) if (cfun->stdarg) return false; - /* No local variable nor structure field should be call-used. */ + /* No local variable nor structure field should escape to callees. */ FOR_EACH_REFERENCED_VAR (var, rvi) { if (!is_global_var (var) - && is_call_used (var)) + /* ??? We do not have a suitable predicate for escaping to + callees. With IPA-PTA the following might be incorrect. + We want to catch + foo { + int i; + bar (&i); + foo (); + } + where bar might store &i somewhere and in the next + recursion should not be able to tell if it got the + same (with tail-recursion applied) or a different + address. */ + && is_call_clobbered (var)) return false; } @@ -430,7 +442,9 @@ find_tail_calls (basic_block bb, struct tailcall **ret) func = gimple_call_fndecl (call); if (func == current_function_decl) { - tree arg; + tree arg, var; + referenced_var_iterator rvi; + for (param = DECL_ARGUMENTS (func), idx = 0; param && idx < gimple_call_num_args (call); param = TREE_CHAIN (param), idx ++) @@ -460,6 +474,15 @@ find_tail_calls (basic_block bb, struct tailcall **ret) } if (idx == gimple_call_num_args (call) && !param) tail_recursion = true; + + /* Make sure the tail invocation of this function does not refer + to local variables. */ + FOR_EACH_REFERENCED_VAR (var, rvi) + { + if (!is_global_var (var) + && ref_maybe_used_by_stmt_p (call, var)) + return; + } } /* Now check the statements after the call. None of them has virtual