From: David Malcolm <dmalcolm@redhat.com>
To: "Basile Starynkevitch" <basile@starynkevitch.net>,
"Marc Nieper-Wißkirchen" <marc@nieper-wisskirchen.de>,
jit@gcc.gnu.org
Cc: David Malcolm <dmalcolm@redhat.com>
Subject: [PATCH 2/6] FIXME: WIP on non-jit part of must-tail-call
Date: Fri, 01 Jan 2016 00:00:00 -0000 [thread overview]
Message-ID: <1463170721-13825-2-git-send-email-dmalcolm@redhat.com> (raw)
In-Reply-To: <1463170721-13825-1-git-send-email-dmalcolm@redhat.com>
This patch implements support for marking CALL_EXPRs
as being mandatory for tail-call-optimization.
Currently this flag isn't accessible from any frontend,
so the patch uses a plugin for testing the functionality.
TODO:
- lots of FIXMEs
- lots more test coverage
- needs better blurb
- needs ChangeLog
- needs bootstrap®rtest
---
gcc/calls.c | 97 ++++++++++++++++++----
gcc/cfgexpand.c | 1 +
gcc/gimple-pretty-print.c | 2 +
gcc/gimple.c | 1 +
gcc/gimple.h | 19 +++++
gcc/print-tree.c | 2 +-
gcc/testsuite/gcc.dg/plugin/must-tail-call-1.c | 22 +++++
gcc/testsuite/gcc.dg/plugin/must-tail-call-2.c | 15 ++++
.../gcc.dg/plugin/must_tail_call_plugin.c | 76 +++++++++++++++++
gcc/testsuite/gcc.dg/plugin/plugin.exp | 3 +
gcc/tree-core.h | 3 +
gcc/tree.h | 4 +
12 files changed, 229 insertions(+), 16 deletions(-)
create mode 100644 gcc/testsuite/gcc.dg/plugin/must-tail-call-1.c
create mode 100644 gcc/testsuite/gcc.dg/plugin/must-tail-call-2.c
create mode 100644 gcc/testsuite/gcc.dg/plugin/must_tail_call_plugin.c
diff --git a/gcc/calls.c b/gcc/calls.c
index ac8092c..c7c3260 100644
--- a/gcc/calls.c
+++ b/gcc/calls.c
@@ -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
+cannot_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
CALL_EXPR EXP.
@@ -1343,7 +1356,10 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
/* We can't use sibcalls if a callee-copied argument is
stored in the current function's frame. */
if (!call_from_thunk_p && DECL_P (base) && !TREE_STATIC (base))
- *may_tailcall = false;
+ {
+ *may_tailcall = false;
+ cannot_tail_call (exp, "FIXME 1");
+ }
args[i].tree_value = build_fold_addr_expr_loc (loc,
args[i].tree_value);
@@ -1406,6 +1422,7 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
= build_fold_addr_expr_loc (loc, make_tree (type, copy));
type = TREE_TYPE (args[i].tree_value);
*may_tailcall = false;
+ cannot_tail_call (exp, "FIXME 2");
}
}
@@ -2358,48 +2375,74 @@ can_implement_as_sibling_call_p (tree exp,
const args_size &args_size)
{
if (!targetm.have_sibcall_epilogue ())
- return false;
+ {
+ cannot_tail_call (exp, "FIXME: targetm.have_sibcall_epilogue");
+ return false;
+ }
/* Doing sibling call optimization needs some work, since
structure_value_addr can be allocated on the stack.
It does not seem worth the effort since few optimizable
sibling calls will return a structure. */
if (structure_value_addr != NULL_RTX)
- return false;
+ {
+ cannot_tail_call (exp, "FIXME: structure_value_addr");
+ return false;
+ }
#ifdef REG_PARM_STACK_SPACE
/* If outgoing reg parm stack space changes, we can not do sibcall. */
if (OUTGOING_REG_PARM_STACK_SPACE (funtype)
!= OUTGOING_REG_PARM_STACK_SPACE (TREE_TYPE (current_function_decl))
|| (reg_parm_stack_space != REG_PARM_STACK_SPACE (current_function_decl)))
- return false;
+ {
+ cannot_tail_call (exp, "FIXME: outgoing reg parm stack space changes");
+ return false;
+ }
#endif
/* Check whether the target is able to optimize the call
into a sibcall. */
if (!targetm.function_ok_for_sibcall (fndecl, exp))
- return false;
+ {
+ cannot_tail_call (exp, "FIXME: targetm.function_ok_for_sibcall");
+ return false;
+ }
/* Functions that do not return exactly once may not be sibcall
optimized. */
if (flags & (ECF_RETURNS_TWICE | ECF_NORETURN))
- return false;
+ {
+ cannot_tail_call (exp, "FIXME: ECF_RETURNS_TWICE | ECF_NORETURN");
+ return false;
+ }
if (TYPE_VOLATILE (TREE_TYPE (TREE_TYPE (addr))))
- return false;
+ {
+ cannot_tail_call (exp,
+ "FIXME: TYPE_VOLATILE (TREE_TYPE (TREE_TYPE (addr))))");
+ return false;
+ }
/* 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
the argument areas are shared. */
if (fndecl && decl_function_context (fndecl) == current_function_decl)
- return false;
+ {
+ cannot_tail_call (exp, "FIXME");
+ return false;
+ }
/* If this function requires more stack slots than the current
function, we cannot change it into a sibling call.
crtl->args.pretend_args_size is not part of the
stack allocated by our caller. */
if (args_size.constant > (crtl->args.size - crtl->args.pretend_args_size))
- return false;
+ {
+ cannot_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
the same number of arguments as the current function. */
@@ -2407,10 +2450,16 @@ can_implement_as_sibling_call_p (tree exp,
!= targetm.calls.return_pops_args (current_function_decl,
TREE_TYPE (current_function_decl),
crtl->args.size))
- return false;
+ {
+ cannot_tail_call (exp, "FIXME");
+ return false;
+ }
if (!lang_hooks.decls.ok_for_sibcall (fndecl))
- return false;
+ {
+ cannot_tail_call (exp, "lang_hooks.decls.ok_for_sibcall");
+ return false;
+ }
/* All checks passed. */
return true;
@@ -2444,6 +2493,7 @@ expand_call (tree exp, rtx target, int ignore)
/* The type of the function being called. */
tree fntype;
bool try_tail_call = CALL_EXPR_TAILCALL (exp);
+ bool must_tail_call = CALL_EXPR_MUST_TAIL_CALL (exp);
int pass;
/* Register in which non-BLKmode value will be returned,
@@ -2811,10 +2861,18 @@ expand_call (tree exp, rtx target, int ignore)
|| dbg_cnt (tail_call) == false)
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. */
if (try_tail_call)
- try_tail_call = can_implement_as_sibling_call_p (exp, structure_value_addr, funtype,
- reg_parm_stack_space, fndecl,
+ try_tail_call = can_implement_as_sibling_call_p (exp,
+ structure_value_addr,
+ funtype,
+ reg_parm_stack_space,
+ fndecl,
flags, addr, args_size);
/* Check if caller and callee disagree in promotion of function
@@ -2845,7 +2903,10 @@ expand_call (tree exp, rtx target, int ignore)
&& (caller_unsignedp != callee_unsignedp
|| GET_MODE_BITSIZE (caller_mode)
< GET_MODE_BITSIZE (callee_mode)))))
- try_tail_call = 0;
+ {
+ try_tail_call = 0;
+ cannot_tail_call (exp, "FIXME 3");
+ }
}
/* Ensure current function's preferred stack boundary is at least
@@ -3743,7 +3804,13 @@ expand_call (tree exp, rtx target, int ignore)
crtl->tail_call_emit = true;
}
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. */
+ cannot_tail_call (exp, "FIXME");
+ }
currently_expanding_call--;
diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
index 77a1964..2a27691 100644
--- a/gcc/cfgexpand.c
+++ b/gcc/cfgexpand.c
@@ -2626,6 +2626,7 @@ expand_call_stmt (gcall *stmt)
TREE_NOTHROW (exp) = 1;
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);
if (decl
&& DECL_BUILT_IN_CLASS (decl) == BUILT_IN_NORMAL
diff --git a/gcc/gimple-pretty-print.c b/gcc/gimple-pretty-print.c
index 4b0dc7c..1806a8c 100644
--- a/gcc/gimple-pretty-print.c
+++ b/gcc/gimple-pretty-print.c
@@ -742,6 +742,8 @@ dump_gimple_call (pretty_printer *buffer, gcall *gs, int spc, int flags)
pp_string (buffer, " [return slot optimization]");
if (gimple_call_tail_p (gs))
pp_string (buffer, " [tail call]");
+ if (gimple_call_must_tail_p (gs))
+ pp_string (buffer, " [must tail call]");
if (fn == NULL)
return;
diff --git a/gcc/gimple.c b/gcc/gimple.c
index 1a22e82..92f873b 100644
--- a/gcc/gimple.c
+++ b/gcc/gimple.c
@@ -359,6 +359,7 @@ gimple_build_call_from_tree (tree t)
/* Carry all the CALL_EXPR flags to the new GIMPLE_CALL. */
gimple_call_set_chain (call, CALL_EXPR_STATIC_CHAIN (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));
if (fndecl
&& DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
diff --git a/gcc/gimple.h b/gcc/gimple.h
index 6d15dab..9855db1 100644
--- a/gcc/gimple.h
+++ b/gcc/gimple.h
@@ -145,6 +145,7 @@ enum gf_mask {
GF_CALL_INTERNAL = 1 << 6,
GF_CALL_CTRL_ALTERING = 1 << 7,
GF_CALL_WITH_BOUNDS = 1 << 8,
+ GF_CALL_MUST_TAIL_CALL = 1 << 9,
GF_OMP_PARALLEL_COMBINED = 1 << 0,
GF_OMP_PARALLEL_GRID_PHONY = 1 << 1,
GF_OMP_TASK_TASKLOOP = 1 << 0,
@@ -3209,6 +3210,24 @@ gimple_call_tail_p (gcall *s)
return (s->subcode & GF_CALL_TAILCALL) != 0;
}
+/* FIXME. */
+
+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;
+}
+
+/* FIXME. */
+
+static inline bool
+gimple_call_must_tail_p (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
slot optimization. This transformation uses the target of the call
diff --git a/gcc/print-tree.c b/gcc/print-tree.c
index aa6593f..58b6118 100644
--- a/gcc/print-tree.c
+++ b/gcc/print-tree.c
@@ -324,7 +324,7 @@ print_node (FILE *file, const char *prefix, tree node, int indent)
if (TREE_PROTECTED (node))
fputs (" protected", file);
if (TREE_STATIC (node))
- fputs (" static", file);
+ fputs (code == CALL_EXPR ? " must-tail-call" : " static", file);
if (TREE_DEPRECATED (node))
fputs (" deprecated", file);
if (TREE_VISITED (node))
diff --git a/gcc/testsuite/gcc.dg/plugin/must-tail-call-1.c b/gcc/testsuite/gcc.dg/plugin/must-tail-call-1.c
new file mode 100644
index 0000000..6d74955
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/plugin/must-tail-call-1.c
@@ -0,0 +1,22 @@
+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;
+}
diff --git a/gcc/testsuite/gcc.dg/plugin/must-tail-call-2.c b/gcc/testsuite/gcc.dg/plugin/must-tail-call-2.c
new file mode 100644
index 0000000..264a792
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/plugin/must-tail-call-2.c
@@ -0,0 +1,15 @@
+struct box { char dummy[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: FIXME: structure_value_addr" } */
+}
diff --git a/gcc/testsuite/gcc.dg/plugin/must_tail_call_plugin.c b/gcc/testsuite/gcc.dg/plugin/must_tail_call_plugin.c
new file mode 100644
index 0000000..5294f28
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/plugin/must_tail_call_plugin.c
@@ -0,0 +1,76 @@
+/* { 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;
+}
diff --git a/gcc/testsuite/gcc.dg/plugin/plugin.exp b/gcc/testsuite/gcc.dg/plugin/plugin.exp
index fd1e98e..62f6797 100644
--- a/gcc/testsuite/gcc.dg/plugin/plugin.exp
+++ b/gcc/testsuite/gcc.dg/plugin/plugin.exp
@@ -74,6 +74,9 @@ set plugin_test_list [list \
{ location_overflow_plugin.c \
location-overflow-test-1.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 {
diff --git a/gcc/tree-core.h b/gcc/tree-core.h
index ea832fa..078e006 100644
--- a/gcc/tree-core.h
+++ b/gcc/tree-core.h
@@ -998,6 +998,9 @@ struct GTY(()) tree_base {
SSA_NAME_ANTI_RANGE_P in
SSA_NAME
+ MUST_TAIL_CALL in
+ CALL_EXPR
+
public_flag:
TREE_OVERFLOW in
diff --git a/gcc/tree.h b/gcc/tree.h
index 37324bf..bfbb646 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -661,6 +661,10 @@ extern void omp_clause_range_check_failed (const_tree, const char *, int,
#define CALL_EXPR_TAILCALL(NODE) \
(CALL_EXPR_CHECK (NODE)->base.addressable_flag)
+/* FIXME. */
+#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
CASE_LOW operand has been processed. */
#define CASE_LOW_SEEN(NODE) \
--
1.8.5.3
next prev parent reply other threads:[~2016-05-13 19:53 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2016-01-01 0:00 [PATCH 0/6] jit: Work-in-progress implementation " David Malcolm
2016-01-01 0:00 ` Marc Nieper-Wißkirchen
2016-01-01 0:00 ` [PATCH 1/6] FIXME: introduce can_implement_as_sibling_call_p David Malcolm
2016-01-01 0:00 ` David Malcolm [this message]
2016-01-01 0:00 ` [PATCH 6/6] FIXME: jit: add test of failing must-TCO David Malcolm
2016-01-01 0:00 ` [PATCH 4/6] FIXME: non-working scanasm test David Malcolm
2016-01-01 0:00 ` [PATCH 3/6] FIXME: WIP on jit part of must-tail-call David Malcolm
2016-01-01 0:00 ` [PATCH 5/6] FIXME: initial support for gcc diagnostics as jit errors David Malcolm
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1463170721-13825-2-git-send-email-dmalcolm@redhat.com \
--to=dmalcolm@redhat.com \
--cc=basile@starynkevitch.net \
--cc=jit@gcc.gnu.org \
--cc=marc@nieper-wisskirchen.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).