Commit 9a385c2d by David Malcolm Committed by David Malcolm

Implement CALL_EXPR_MUST_TAIL_CALL

This patch implements support for marking CALL_EXPRs
as being mandatory for tail-call-optimization. expand_call
tries harder to perform the optimization on such CALL_EXPRs,
and issues an error if it fails.

Currently this flag isn't accessible from any frontend,
so the patch uses a plugin for testing the functionality.

gcc/ChangeLog:
	* calls.c (maybe_complain_about_tail_call): New function.
	(initialize_argument_information): Call
	maybe_complain_about_tail_call when clearing *may_tailcall.
	(can_implement_as_sibling_call_p): Call
	maybe_complain_about_tail_call when returning false.
	(expand_call): Read CALL_EXPR_MUST_TAIL_CALL and, if set,
	ensure try_tail_call is set.  Call maybe_complain_about_tail_call
	if tail-call optimization fails.
	* cfgexpand.c (expand_call_stmt): Initialize
	CALL_EXPR_MUST_TAIL_CALL from gimple_call_must_tail_p.
	* gimple-pretty-print.c (dump_gimple_call): Dump
	gimple_call_must_tail_p.
	* gimple.c (gimple_build_call_from_tree): Call
	gimple_call_set_must_tail with the value of
	CALL_EXPR_MUST_TAIL_CALL.
	* gimple.h (enum gf_mask): Add GF_CALL_MUST_TAIL_CALL.
	(gimple_call_set_must_tail): New function.
	(gimple_call_must_tail_p): New function.
	* print-tree.c (print_node): Update printing of TREE_STATIC
	to reflect its use for CALL_EXPR_MUST_TAIL_CALL.
	* tree-core.h (struct tree_base): Add MUST_TAIL_CALL to the
	trailing comment listing applicable flags.
	* tree.h (CALL_EXPR_MUST_TAIL_CALL): New macro.

gcc/testsuite/ChangeLog:
	* gcc.dg/plugin/must-tail-call-1.c: New test case.
	* gcc.dg/plugin/must-tail-call-2.c: New test case.
	* gcc.dg/plugin/must_tail_call_plugin.c: New file.
	* gcc.dg/plugin/plugin.exp (plugin_test_list): Add the above.

From-SVN: r236514
parent b40d90e6
2016-05-20 David Malcolm <dmalcolm@redhat.com> 2016-05-20 David Malcolm <dmalcolm@redhat.com>
* calls.c (maybe_complain_about_tail_call): New function.
(initialize_argument_information): Call
maybe_complain_about_tail_call when clearing *may_tailcall.
(can_implement_as_sibling_call_p): Call
maybe_complain_about_tail_call when returning false.
(expand_call): Read CALL_EXPR_MUST_TAIL_CALL and, if set,
ensure try_tail_call is set. Call maybe_complain_about_tail_call
if tail-call optimization fails.
* cfgexpand.c (expand_call_stmt): Initialize
CALL_EXPR_MUST_TAIL_CALL from gimple_call_must_tail_p.
* gimple-pretty-print.c (dump_gimple_call): Dump
gimple_call_must_tail_p.
* gimple.c (gimple_build_call_from_tree): Call
gimple_call_set_must_tail with the value of
CALL_EXPR_MUST_TAIL_CALL.
* gimple.h (enum gf_mask): Add GF_CALL_MUST_TAIL_CALL.
(gimple_call_set_must_tail): New function.
(gimple_call_must_tail_p): New function.
* print-tree.c (print_node): Update printing of TREE_STATIC
to reflect its use for CALL_EXPR_MUST_TAIL_CALL.
* tree-core.h (struct tree_base): Add MUST_TAIL_CALL to the
trailing comment listing applicable flags.
* tree.h (CALL_EXPR_MUST_TAIL_CALL): New macro.
2016-05-20 David Malcolm <dmalcolm@redhat.com>
* calls.c (expand_call): Move "Rest of purposes for tail call * calls.c (expand_call): Move "Rest of purposes for tail call
optimizations to fail" to... optimizations to fail" to...
(can_implement_as_sibling_call_p): ...this new function, and (can_implement_as_sibling_call_p): ...this new function, and
......
...@@ -1102,6 +1102,19 @@ store_unaligned_arguments_into_pseudos (struct arg_data *args, int num_actuals) ...@@ -1102,6 +1102,19 @@ store_unaligned_arguments_into_pseudos (struct arg_data *args, int num_actuals)
} }
} }
/* Issue an error if CALL_EXPR was flagged as requiring
tall-call optimization. */
static void
maybe_complain_about_tail_call (tree call_expr, const char *reason)
{
gcc_assert (TREE_CODE (call_expr) == CALL_EXPR);
if (!CALL_EXPR_MUST_TAIL_CALL (call_expr))
return;
error_at (EXPR_LOCATION (call_expr), "cannot tail-call: %s", reason);
}
/* Fill in ARGS_SIZE and ARGS array based on the parameters found in /* Fill in ARGS_SIZE and ARGS array based on the parameters found in
CALL_EXPR EXP. CALL_EXPR EXP.
...@@ -1343,7 +1356,13 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED, ...@@ -1343,7 +1356,13 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
/* We can't use sibcalls if a callee-copied argument is /* We can't use sibcalls if a callee-copied argument is
stored in the current function's frame. */ stored in the current function's frame. */
if (!call_from_thunk_p && DECL_P (base) && !TREE_STATIC (base)) if (!call_from_thunk_p && DECL_P (base) && !TREE_STATIC (base))
*may_tailcall = false; {
*may_tailcall = false;
maybe_complain_about_tail_call (exp,
"a callee-copied argument is"
" stored in the current "
" function's frame");
}
args[i].tree_value = build_fold_addr_expr_loc (loc, args[i].tree_value = build_fold_addr_expr_loc (loc,
args[i].tree_value); args[i].tree_value);
...@@ -1406,6 +1425,9 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED, ...@@ -1406,6 +1425,9 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
= build_fold_addr_expr_loc (loc, make_tree (type, copy)); = build_fold_addr_expr_loc (loc, make_tree (type, copy));
type = TREE_TYPE (args[i].tree_value); type = TREE_TYPE (args[i].tree_value);
*may_tailcall = false; *may_tailcall = false;
maybe_complain_about_tail_call (exp,
"argument must be passed"
" by copying");
} }
} }
...@@ -2358,48 +2380,87 @@ can_implement_as_sibling_call_p (tree exp, ...@@ -2358,48 +2380,87 @@ can_implement_as_sibling_call_p (tree exp,
const args_size &args_size) const args_size &args_size)
{ {
if (!targetm.have_sibcall_epilogue ()) if (!targetm.have_sibcall_epilogue ())
return false; {
maybe_complain_about_tail_call
(exp,
"machine description does not have"
" a sibcall_epilogue instruction pattern");
return false;
}
/* Doing sibling call optimization needs some work, since /* Doing sibling call optimization needs some work, since
structure_value_addr can be allocated on the stack. structure_value_addr can be allocated on the stack.
It does not seem worth the effort since few optimizable It does not seem worth the effort since few optimizable
sibling calls will return a structure. */ sibling calls will return a structure. */
if (structure_value_addr != NULL_RTX) if (structure_value_addr != NULL_RTX)
return false; {
maybe_complain_about_tail_call (exp, "callee returns a structure");
return false;
}
#ifdef REG_PARM_STACK_SPACE #ifdef REG_PARM_STACK_SPACE
/* If outgoing reg parm stack space changes, we can not do sibcall. */ /* If outgoing reg parm stack space changes, we can not do sibcall. */
if (OUTGOING_REG_PARM_STACK_SPACE (funtype) if (OUTGOING_REG_PARM_STACK_SPACE (funtype)
!= OUTGOING_REG_PARM_STACK_SPACE (TREE_TYPE (current_function_decl)) != OUTGOING_REG_PARM_STACK_SPACE (TREE_TYPE (current_function_decl))
|| (reg_parm_stack_space != REG_PARM_STACK_SPACE (current_function_decl))) || (reg_parm_stack_space != REG_PARM_STACK_SPACE (current_function_decl)))
return false; {
maybe_complain_about_tail_call (exp,
"inconsistent size of stack space"
" allocated for arguments which are"
" passed in registers");
return false;
}
#endif #endif
/* Check whether the target is able to optimize the call /* Check whether the target is able to optimize the call
into a sibcall. */ into a sibcall. */
if (!targetm.function_ok_for_sibcall (fndecl, exp)) if (!targetm.function_ok_for_sibcall (fndecl, exp))
return false; {
maybe_complain_about_tail_call (exp,
"target is not able to optimize the"
" call into a sibling call");
return false;
}
/* Functions that do not return exactly once may not be sibcall /* Functions that do not return exactly once may not be sibcall
optimized. */ optimized. */
if (flags & (ECF_RETURNS_TWICE | ECF_NORETURN)) if (flags & ECF_RETURNS_TWICE)
return false; {
maybe_complain_about_tail_call (exp, "callee returns twice");
return false;
}
if (flags & ECF_NORETURN)
{
maybe_complain_about_tail_call (exp, "callee does not return");
return false;
}
if (TYPE_VOLATILE (TREE_TYPE (TREE_TYPE (addr)))) if (TYPE_VOLATILE (TREE_TYPE (TREE_TYPE (addr))))
return false; {
maybe_complain_about_tail_call (exp, "volatile function type");
return false;
}
/* If the called function is nested in the current one, it might access /* If the called function is nested in the current one, it might access
some of the caller's arguments, but could clobber them beforehand if some of the caller's arguments, but could clobber them beforehand if
the argument areas are shared. */ the argument areas are shared. */
if (fndecl && decl_function_context (fndecl) == current_function_decl) if (fndecl && decl_function_context (fndecl) == current_function_decl)
return false; {
maybe_complain_about_tail_call (exp, "nested function");
return false;
}
/* If this function requires more stack slots than the current /* If this function requires more stack slots than the current
function, we cannot change it into a sibling call. function, we cannot change it into a sibling call.
crtl->args.pretend_args_size is not part of the crtl->args.pretend_args_size is not part of the
stack allocated by our caller. */ stack allocated by our caller. */
if (args_size.constant > (crtl->args.size - crtl->args.pretend_args_size)) if (args_size.constant > (crtl->args.size - crtl->args.pretend_args_size))
return false; {
maybe_complain_about_tail_call (exp,
"callee required more stack slots"
" than the caller");
return false;
}
/* If the callee pops its own arguments, then it must pop exactly /* If the callee pops its own arguments, then it must pop exactly
the same number of arguments as the current function. */ the same number of arguments as the current function. */
...@@ -2407,10 +2468,19 @@ can_implement_as_sibling_call_p (tree exp, ...@@ -2407,10 +2468,19 @@ can_implement_as_sibling_call_p (tree exp,
!= targetm.calls.return_pops_args (current_function_decl, != targetm.calls.return_pops_args (current_function_decl,
TREE_TYPE (current_function_decl), TREE_TYPE (current_function_decl),
crtl->args.size)) crtl->args.size))
return false; {
maybe_complain_about_tail_call (exp,
"inconsistent number of"
" popped arguments");
return false;
}
if (!lang_hooks.decls.ok_for_sibcall (fndecl)) if (!lang_hooks.decls.ok_for_sibcall (fndecl))
return false; {
maybe_complain_about_tail_call (exp, "frontend does not support"
" sibling call");
return false;
}
/* All checks passed. */ /* All checks passed. */
return true; return true;
...@@ -2444,6 +2514,7 @@ expand_call (tree exp, rtx target, int ignore) ...@@ -2444,6 +2514,7 @@ expand_call (tree exp, rtx target, int ignore)
/* The type of the function being called. */ /* The type of the function being called. */
tree fntype; tree fntype;
bool try_tail_call = CALL_EXPR_TAILCALL (exp); bool try_tail_call = CALL_EXPR_TAILCALL (exp);
bool must_tail_call = CALL_EXPR_MUST_TAIL_CALL (exp);
int pass; int pass;
/* Register in which non-BLKmode value will be returned, /* Register in which non-BLKmode value will be returned,
...@@ -2811,10 +2882,18 @@ expand_call (tree exp, rtx target, int ignore) ...@@ -2811,10 +2882,18 @@ expand_call (tree exp, rtx target, int ignore)
|| dbg_cnt (tail_call) == false) || dbg_cnt (tail_call) == false)
try_tail_call = 0; try_tail_call = 0;
/* If the user has marked the function as requiring tail-call
optimization, attempt it. */
if (must_tail_call)
try_tail_call = 1;
/* Rest of purposes for tail call optimizations to fail. */ /* Rest of purposes for tail call optimizations to fail. */
if (try_tail_call) if (try_tail_call)
try_tail_call = can_implement_as_sibling_call_p (exp, structure_value_addr, funtype, try_tail_call = can_implement_as_sibling_call_p (exp,
reg_parm_stack_space, fndecl, structure_value_addr,
funtype,
reg_parm_stack_space,
fndecl,
flags, addr, args_size); flags, addr, args_size);
/* Check if caller and callee disagree in promotion of function /* Check if caller and callee disagree in promotion of function
...@@ -2845,7 +2924,13 @@ expand_call (tree exp, rtx target, int ignore) ...@@ -2845,7 +2924,13 @@ expand_call (tree exp, rtx target, int ignore)
&& (caller_unsignedp != callee_unsignedp && (caller_unsignedp != callee_unsignedp
|| GET_MODE_BITSIZE (caller_mode) || GET_MODE_BITSIZE (caller_mode)
< GET_MODE_BITSIZE (callee_mode))))) < GET_MODE_BITSIZE (callee_mode)))))
try_tail_call = 0; {
try_tail_call = 0;
maybe_complain_about_tail_call (exp,
"caller and callee disagree in"
" promotion of function"
" return value");
}
} }
/* Ensure current function's preferred stack boundary is at least /* Ensure current function's preferred stack boundary is at least
...@@ -3743,7 +3828,13 @@ expand_call (tree exp, rtx target, int ignore) ...@@ -3743,7 +3828,13 @@ expand_call (tree exp, rtx target, int ignore)
crtl->tail_call_emit = true; crtl->tail_call_emit = true;
} }
else else
emit_insn (normal_call_insns); {
emit_insn (normal_call_insns);
if (try_tail_call)
/* Ideally we'd emit a message for all of the ways that it could
have failed. */
maybe_complain_about_tail_call (exp, "tail call production failed");
}
currently_expanding_call--; currently_expanding_call--;
......
...@@ -2626,6 +2626,7 @@ expand_call_stmt (gcall *stmt) ...@@ -2626,6 +2626,7 @@ expand_call_stmt (gcall *stmt)
TREE_NOTHROW (exp) = 1; TREE_NOTHROW (exp) = 1;
CALL_EXPR_TAILCALL (exp) = gimple_call_tail_p (stmt); CALL_EXPR_TAILCALL (exp) = gimple_call_tail_p (stmt);
CALL_EXPR_MUST_TAIL_CALL (exp) = gimple_call_must_tail_p (stmt);
CALL_EXPR_RETURN_SLOT_OPT (exp) = gimple_call_return_slot_opt_p (stmt); CALL_EXPR_RETURN_SLOT_OPT (exp) = gimple_call_return_slot_opt_p (stmt);
if (decl if (decl
&& DECL_BUILT_IN_CLASS (decl) == BUILT_IN_NORMAL && DECL_BUILT_IN_CLASS (decl) == BUILT_IN_NORMAL
......
...@@ -760,6 +760,8 @@ dump_gimple_call (pretty_printer *buffer, gcall *gs, int spc, int flags) ...@@ -760,6 +760,8 @@ dump_gimple_call (pretty_printer *buffer, gcall *gs, int spc, int flags)
pp_string (buffer, " [return slot optimization]"); pp_string (buffer, " [return slot optimization]");
if (gimple_call_tail_p (gs)) if (gimple_call_tail_p (gs))
pp_string (buffer, " [tail call]"); pp_string (buffer, " [tail call]");
if (gimple_call_must_tail_p (gs))
pp_string (buffer, " [must tail call]");
if (fn == NULL) if (fn == NULL)
return; return;
......
...@@ -359,6 +359,7 @@ gimple_build_call_from_tree (tree t) ...@@ -359,6 +359,7 @@ gimple_build_call_from_tree (tree t)
/* Carry all the CALL_EXPR flags to the new GIMPLE_CALL. */ /* Carry all the CALL_EXPR flags to the new GIMPLE_CALL. */
gimple_call_set_chain (call, CALL_EXPR_STATIC_CHAIN (t)); gimple_call_set_chain (call, CALL_EXPR_STATIC_CHAIN (t));
gimple_call_set_tail (call, CALL_EXPR_TAILCALL (t)); gimple_call_set_tail (call, CALL_EXPR_TAILCALL (t));
gimple_call_set_must_tail (call, CALL_EXPR_MUST_TAIL_CALL (t));
gimple_call_set_return_slot_opt (call, CALL_EXPR_RETURN_SLOT_OPT (t)); gimple_call_set_return_slot_opt (call, CALL_EXPR_RETURN_SLOT_OPT (t));
if (fndecl if (fndecl
&& DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
......
...@@ -145,6 +145,7 @@ enum gf_mask { ...@@ -145,6 +145,7 @@ enum gf_mask {
GF_CALL_INTERNAL = 1 << 6, GF_CALL_INTERNAL = 1 << 6,
GF_CALL_CTRL_ALTERING = 1 << 7, GF_CALL_CTRL_ALTERING = 1 << 7,
GF_CALL_WITH_BOUNDS = 1 << 8, GF_CALL_WITH_BOUNDS = 1 << 8,
GF_CALL_MUST_TAIL_CALL = 1 << 9,
GF_OMP_PARALLEL_COMBINED = 1 << 0, GF_OMP_PARALLEL_COMBINED = 1 << 0,
GF_OMP_PARALLEL_GRID_PHONY = 1 << 1, GF_OMP_PARALLEL_GRID_PHONY = 1 << 1,
GF_OMP_TASK_TASKLOOP = 1 << 0, GF_OMP_TASK_TASKLOOP = 1 << 0,
...@@ -3209,6 +3210,25 @@ gimple_call_tail_p (gcall *s) ...@@ -3209,6 +3210,25 @@ gimple_call_tail_p (gcall *s)
return (s->subcode & GF_CALL_TAILCALL) != 0; return (s->subcode & GF_CALL_TAILCALL) != 0;
} }
/* Mark (or clear) call statement S as requiring tail call optimization. */
static inline void
gimple_call_set_must_tail (gcall *s, bool must_tail_p)
{
if (must_tail_p)
s->subcode |= GF_CALL_MUST_TAIL_CALL;
else
s->subcode &= ~GF_CALL_MUST_TAIL_CALL;
}
/* Return true if call statement has been marked as requiring
tail call optimization. */
static inline bool
gimple_call_must_tail_p (const gcall *s)
{
return (s->subcode & GF_CALL_MUST_TAIL_CALL) != 0;
}
/* If RETURN_SLOT_OPT_P is true mark GIMPLE_CALL S as valid for return /* If RETURN_SLOT_OPT_P is true mark GIMPLE_CALL S as valid for return
slot optimization. This transformation uses the target of the call slot optimization. This transformation uses the target of the call
......
...@@ -324,7 +324,7 @@ print_node (FILE *file, const char *prefix, tree node, int indent) ...@@ -324,7 +324,7 @@ print_node (FILE *file, const char *prefix, tree node, int indent)
if (TREE_PROTECTED (node)) if (TREE_PROTECTED (node))
fputs (" protected", file); fputs (" protected", file);
if (TREE_STATIC (node)) if (TREE_STATIC (node))
fputs (" static", file); fputs (code == CALL_EXPR ? " must-tail-call" : " static", file);
if (TREE_DEPRECATED (node)) if (TREE_DEPRECATED (node))
fputs (" deprecated", file); fputs (" deprecated", file);
if (TREE_VISITED (node)) if (TREE_VISITED (node))
......
2016-05-20 David Malcolm <dmalcolm@redhat.com>
* gcc.dg/plugin/must-tail-call-1.c: New test case.
* gcc.dg/plugin/must-tail-call-2.c: New test case.
* gcc.dg/plugin/must_tail_call_plugin.c: New file.
* gcc.dg/plugin/plugin.exp (plugin_test_list): Add the above.
2016-05-20 Jan Hubicka <hubicka@ucw.cz> 2016-05-20 Jan Hubicka <hubicka@ucw.cz>
* gcc.dg/tree-ssa/prefetch-5.c: xfail. * gcc.dg/tree-ssa/prefetch-5.c: xfail.
......
extern void abort (void);
int __attribute__((noinline,noclone))
callee (int i)
{
return i * i;
}
int __attribute__((noinline,noclone))
caller (int i)
{
return callee (i + 1);
}
int
main (int argc, const char **argv)
{
int result = caller (5);
if (result != 36)
abort ();
return 0;
}
/* Allow nested functions. */
/* { dg-options "-Wno-pedantic" } */
struct box { char field[64]; int i; };
struct box __attribute__((noinline,noclone))
returns_struct (int i)
{
struct box b;
b.i = i * i;
return b;
}
int __attribute__((noinline,noclone))
test_1 (int i)
{
return returns_struct (i * 5).i; /* { dg-error "cannot tail-call: callee returns a structure" } */
}
int __attribute__((noinline,noclone))
test_2_callee (int i, struct box b)
{
if (b.field[0])
return 5;
return i * i;
}
int __attribute__((noinline,noclone))
test_2_caller (int i)
{
struct box b;
return test_2_callee (i + 1, b); /* { dg-error "cannot tail-call: callee required more stack slots than the caller" } */
}
extern void setjmp (void);
void
test_3 (void)
{
setjmp (); /* { dg-error "cannot tail-call: callee returns twice" } */
}
void
test_4 (void)
{
void nested (void)
{
}
nested (); /* { dg-error "cannot tail-call: nested function" } */
}
typedef void (fn_ptr_t) (void);
volatile fn_ptr_t fn_ptr;
void
test_5 (void)
{
fn_ptr (); /* { dg-error "cannot tail-call: callee does not return" } */
}
/* { dg-options "-O" } */
/* Mark all CALL_EXPRs not within "main" as requiring tail-call. */
#include "gcc-plugin.h"
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "tree.h"
#include "stringpool.h"
#include "toplev.h"
#include "basic-block.h"
#include "hash-table.h"
#include "vec.h"
#include "ggc.h"
#include "basic-block.h"
#include "tree-ssa-alias.h"
#include "internal-fn.h"
#include "gimple-fold.h"
#include "tree-eh.h"
#include "gimple-expr.h"
#include "is-a.h"
#include "tree.h"
#include "tree-pass.h"
#include "intl.h"
#include "plugin-version.h"
int plugin_is_GPL_compatible;
tree
cb_walk_tree_fn (tree * tp, int * walk_subtrees,
void * data ATTRIBUTE_UNUSED)
{
if (TREE_CODE (*tp) != CALL_EXPR)
return NULL_TREE;
tree call_expr = *tp;
/* Forcibly mark the CALL_EXPR as requiring tail-call optimization. */
CALL_EXPR_MUST_TAIL_CALL (call_expr) = 1;
return NULL_TREE;
}
static void
callback (void *gcc_data, void *user_data)
{
tree fndecl = (tree)gcc_data;
gcc_assert (TREE_CODE (fndecl) == FUNCTION_DECL);
/* Don't mark calls inside "main". */
tree decl_name = DECL_NAME (fndecl);
if (decl_name)
if (0 == strcmp (IDENTIFIER_POINTER (decl_name), "main"))
return;
walk_tree (&DECL_SAVED_TREE (fndecl), cb_walk_tree_fn, NULL, NULL);
}
int
plugin_init (struct plugin_name_args *plugin_info,
struct plugin_gcc_version *version)
{
const char *plugin_name = plugin_info->base_name;
if (!plugin_default_version_check (version, &gcc_version))
return 1;
register_callback (plugin_name,
PLUGIN_PRE_GENERICIZE,
callback,
NULL);
return 0;
}
...@@ -74,6 +74,9 @@ set plugin_test_list [list \ ...@@ -74,6 +74,9 @@ set plugin_test_list [list \
{ location_overflow_plugin.c \ { location_overflow_plugin.c \
location-overflow-test-1.c \ location-overflow-test-1.c \
location-overflow-test-2.c } \ location-overflow-test-2.c } \
{ must_tail_call_plugin.c \
must-tail-call-1.c \
must-tail-call-2.c } \
] ]
foreach plugin_test $plugin_test_list { foreach plugin_test $plugin_test_list {
......
...@@ -1001,6 +1001,9 @@ struct GTY(()) tree_base { ...@@ -1001,6 +1001,9 @@ struct GTY(()) tree_base {
SSA_NAME_ANTI_RANGE_P in SSA_NAME_ANTI_RANGE_P in
SSA_NAME SSA_NAME
MUST_TAIL_CALL in
CALL_EXPR
public_flag: public_flag:
TREE_OVERFLOW in TREE_OVERFLOW in
......
...@@ -661,6 +661,11 @@ extern void omp_clause_range_check_failed (const_tree, const char *, int, ...@@ -661,6 +661,11 @@ extern void omp_clause_range_check_failed (const_tree, const char *, int,
#define CALL_EXPR_TAILCALL(NODE) \ #define CALL_EXPR_TAILCALL(NODE) \
(CALL_EXPR_CHECK (NODE)->base.addressable_flag) (CALL_EXPR_CHECK (NODE)->base.addressable_flag)
/* Set on a CALL_EXPR if the call has been marked as requiring tail call
optimization for correctness. */
#define CALL_EXPR_MUST_TAIL_CALL(NODE) \
(CALL_EXPR_CHECK (NODE)->base.static_flag)
/* Used as a temporary field on a CASE_LABEL_EXPR to indicate that the /* Used as a temporary field on a CASE_LABEL_EXPR to indicate that the
CASE_LOW operand has been processed. */ CASE_LOW operand has been processed. */
#define CASE_LOW_SEEN(NODE) \ #define CASE_LOW_SEEN(NODE) \
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment