From 1f4590e7a633c6335512b012578bddba7602b3c9 Mon Sep 17 00:00:00 2001
From: Yury Gribov <tetra2005@gmail.com>
Date: Sun, 28 May 2017 21:02:20 +0100
Subject: [PATCH] Added no_tail_call attribute.

gcc/
2017-05-29  Yury Gribov  <tetra2005@gmail.com>

	* cgraphunit.c (cgraph_node::expand_thunk): Prevent
	tailcalling functions marked with no_tail_call.
	* gcc/doc/extend.texi: Document no_tail_call.
	* tree-tailcall.c (find_tail_calls): Ditto.
	* tree.c (comp_type_attributes): Treat no_tail_call
	mismatch as error.

gcc/c-family/
2017-05-29  Yury Gribov  <tetra2005@gmail.com>

	* c-attribs.c: New attribute.

gcc/testsuite/
2017-05-29  Yury Gribov  <tetra2005@gmail.com>

	* gcc.dg/pr66826-1.c: New test.
	* gcc.dg/pr66826-2.c: New test.
---
 gcc/c-family/c-attribs.c         |  1 +
 gcc/cgraphunit.c                 |  6 ++++--
 gcc/doc/extend.texi              |  7 +++++++
 gcc/testsuite/gcc.dg/pr66826-1.c | 14 ++++++++++++++
 gcc/testsuite/gcc.dg/pr66826-2.c |  6 ++++++
 gcc/tree-tailcall.c              |  4 ++++
 gcc/tree.c                       |  7 ++++---
 7 files changed, 40 insertions(+), 5 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/pr66826-1.c
 create mode 100644 gcc/testsuite/gcc.dg/pr66826-2.c

diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c
index 695c58c..482db00 100644
--- a/gcc/c-family/c-attribs.c
+++ b/gcc/c-family/c-attribs.c
@@ -345,6 +345,7 @@ const struct attribute_spec c_common_attribute_table[] =
 			      handle_bnd_instrument, false },
   { "fallthrough",	      0, 0, false, false, false,
 			      handle_fallthrough_attribute, false },
+  { "no_tail_call",           0, 0, false, true, true, NULL, true },
   { NULL,                     0, 0, false, false, false, NULL, false }
 };
 
diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c
index 4a949ca..e23fd21 100644
--- a/gcc/cgraphunit.c
+++ b/gcc/cgraphunit.c
@@ -1715,6 +1715,7 @@ cgraph_node::expand_thunk (bool output_asm_thunks, bool force_gimple_thunk)
       tree resdecl;
       tree restmp = NULL;
       tree resbnd = NULL;
+      bool no_tail_call_p;
 
       gcall *call;
       greturn *ret;
@@ -1823,6 +1824,7 @@ cgraph_node::expand_thunk (bool output_asm_thunks, bool force_gimple_thunk)
       callees->call_stmt = call;
       gimple_call_set_from_thunk (call, true);
       gimple_call_set_with_bounds (call, instrumentation_clone);
+      no_tail_call_p = lookup_attribute ("no_tail_call", TYPE_ATTRIBUTES (gimple_call_fntype (call)));
 
       /* Return slot optimization is always possible and in fact requred to
          return values with DECL_BY_REFERENCE.  */
@@ -1912,7 +1914,7 @@ cgraph_node::expand_thunk (bool output_asm_thunks, bool force_gimple_thunk)
 		  bsi = gsi_last_bb (return_bb);
 		}
 	    }
-	  else
+	  else if (!no_tail_call_p)
 	    gimple_call_set_tail (call, true);
 
 	  /* Build return value.  */
@@ -1924,7 +1926,7 @@ cgraph_node::expand_thunk (bool output_asm_thunks, bool force_gimple_thunk)
 
 	  gsi_insert_after (&bsi, ret, GSI_NEW_STMT);
 	}
-      else
+      else if (!no_tail_call_p)
 	{
 	  gimple_call_set_tail (call, true);
 	  remove_edge (single_succ_edge (bb));
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index c737634..6fa9c66 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -3067,6 +3067,13 @@ the standard C library can be guaranteed not to throw an exception
 with the notable exceptions of @code{qsort} and @code{bsearch} that
 take function pointer arguments.
 
+@item no_tail_call
+@cindex @code{no_tail_call} function attribute
+The @code{no_tail_call} attribute is used to prohibit tail-call
+optimization of a function.  When taking address of function
+marked with @code{no_tail_call}, resulting function pointer type must
+have this attribute set too.
+
 @item optimize
 @cindex @code{optimize} function attribute
 The @code{optimize} attribute is used to specify that a function is to
diff --git a/gcc/testsuite/gcc.dg/pr66826-1.c b/gcc/testsuite/gcc.dg/pr66826-1.c
new file mode 100644
index 0000000..eac8ba9
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr66826-1.c
@@ -0,0 +1,14 @@
+/* { dg-do compile { target x86_64-*-* } } */
+/* { dg-options "-O2" } */
+
+int tail();
+
+__attribute__((no_tail_call)) int notail();
+
+int foo1() {
+  return tail();  /* { dg-final { scan-assembler "jmp.*\\ytail" } } */
+}
+
+int foo2() {
+  return notail();  /* { dg-final { scan-assembler "call.*\\ynotail" } } */
+}
diff --git a/gcc/testsuite/gcc.dg/pr66826-2.c b/gcc/testsuite/gcc.dg/pr66826-2.c
new file mode 100644
index 0000000..a22df8b
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr66826-2.c
@@ -0,0 +1,6 @@
+__attribute__((no_tail_call)) int foo();
+
+int bar() {
+  int (*p)() = foo; /* { dg-error "initialization from incompatible pointer type" } */
+  return p();
+}
diff --git a/gcc/tree-tailcall.c b/gcc/tree-tailcall.c
index f586edc..30a6fad 100644
--- a/gcc/tree-tailcall.c
+++ b/gcc/tree-tailcall.c
@@ -601,6 +601,10 @@ find_tail_calls (basic_block bb, struct tailcall **ret)
   if (m && POINTER_TYPE_P (TREE_TYPE (DECL_RESULT (current_function_decl))))
     return;
 
+  /* See if function does not want to be tailcalled.  */
+  if (lookup_attribute ("no_tail_call", TYPE_ATTRIBUTES (gimple_call_fntype (call))))
+    return;
+
   nw = XNEW (struct tailcall);
 
   nw->call_gsi = gsi;
diff --git a/gcc/tree.c b/gcc/tree.c
index b76b521..47e1ea4 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -5034,7 +5034,7 @@ comp_type_attributes (const_tree type1, const_tree type2)
       const_tree attr;
 
       as = lookup_attribute_spec (get_attribute_name (a));
-      if (!as || as->affects_type_identity == false)
+      if (!as || !as->affects_type_identity)
         continue;
 
       attr = lookup_attribute (as->name, CONST_CAST_TREE (a2));
@@ -5048,7 +5048,7 @@ comp_type_attributes (const_tree type1, const_tree type2)
 	  const struct attribute_spec *as;
 
 	  as = lookup_attribute_spec (get_attribute_name (a));
-	  if (!as || as->affects_type_identity == false)
+	  if (!as || !as->affects_type_identity)
 	    continue;
 
 	  if (!lookup_attribute (as->name, CONST_CAST_TREE (a1)))
@@ -5061,7 +5061,8 @@ comp_type_attributes (const_tree type1, const_tree type2)
       if (!a)
         return 1;
     }
-  if (lookup_attribute ("transaction_safe", CONST_CAST_TREE (a)))
+  if (lookup_attribute ("transaction_safe", CONST_CAST_TREE (a))
+      || lookup_attribute ("no_tail_call", CONST_CAST_TREE (a)))
     return 0;
   /* As some type combinations - like default calling-convention - might
      be compatible, we have to call the target hook to get the final result.  */
-- 
2.7.4

