This versions has the following changes:

- error handling is moved to builtins.cc
- the logic for the two builtins is unified inĀ 
  gimple-fold.cc and tree-nested.cc
- the documentation is extended and includes an
  example, cross references and notes about UB
- if we try to inline a function which need a static
  chain but none is given, we set it to NULL


Bootstrapped and regression-tested on x86_64.


    Built-ins to access code pointer and static chain of nested function.
    
    This patch adds two new built-ins, __builtin_call_code_address and
    __builtin_call_static chain, to extract the code address and the static
    chain pointer from a (nested) function, respectively.  Those can then be
    used to call the nested function using the existing built-in
    __builtin_call_with_static_chain.  This feature can be used to avoid the
    creation of trampolines and often allows writing more efficient
    code, e.g. where trampolines prevent devirtualization (PR49666).
    
    gcc/ChangeLog:
            * builtins.def (BUILT_IN_CALL_CODE_ADDRESS,
            BUILT_IN_CALL_STATIC_CHAIN): New.
            * builtins.cc (expand_builtin): Emit errors for invalid uses.
            (is_simple_builtin): Add new built-in functions.
            * gimple-fold.cc (gimple_fold_builtin_call_info): New function.
            (gimple_fold_builtin): Expand new built-ins for non-nested 
functions.
            * tree-nested.cc (convert_tramp_reference_stmt): Ingore new 
built-ins.
            (convert_gimple_call): Expand built-ins for nested functions.
            * tree-inline.cc (initialize_inlined_parameters): Set a missing
            static chain to NULL.
    
    gcc/doc/ChangeLog:
            * extend.texi (Constructing Calls): Document
            __builtin_call_static_chain and __builtin_call_code_address
            and update __builtin_call_with_static_chain.
            (Nested Functions): Update.
    
    gcc/testsuite/ChangeLog:
            * gcc.dg/builtin-call-info-1.c: New test.
            * gcc.dg/builtin-call-info-2.c: New test.
            * gcc.dg/builtin-call-info-3.c: New test.

diff --git a/gcc/builtins.cc b/gcc/builtins.cc
index 52f3ee029d0..59ac630e30c 100644
--- a/gcc/builtins.cc
+++ b/gcc/builtins.cc
@@ -8092,6 +8092,18 @@ expand_builtin (tree exp, rtx target, rtx subtarget, 
machine_mode mode,
        expand_builtin_return (expand_normal (CALL_EXPR_ARG (exp, 0)));
       return const0_rtx;
 
+    case BUILT_IN_CALL_CODE_ADDRESS:
+      /* All valid uses of __builtin_call_code_address () are removed in
+        in tree-nested.cc or gimple-fold.cc.  */
+      error ("argument to %<__builtin_call_code_address%> must be a function");
+      return const0_rtx;
+
+    case BUILT_IN_CALL_STATIC_CHAIN:
+       /* All valid uses of __builtin_call_static_chain () are removed in
+         in tree-nested.cc or gimple-fold.cc.  */
+      error ("argument to %<__builtin_call_static_chain%> must be a function");
+      return const0_rtx;
+
     case BUILT_IN_SAVEREGS:
       return expand_builtin_saveregs ();
 
@@ -12368,6 +12380,8 @@ is_simple_builtin (tree decl)
       case BUILT_IN_STACK_SAVE:
       case BUILT_IN_STACK_RESTORE:
       case BUILT_IN_DWARF_CFA:
+      case BUILT_IN_CALL_CODE_ADDRESS:
+      case BUILT_IN_CALL_STATIC_CHAIN:
        /* Exception state returns or moves registers around.  */
       case BUILT_IN_EH_FILTER:
       case BUILT_IN_EH_POINTER:
diff --git a/gcc/builtins.def b/gcc/builtins.def
index e39c3427d77..4adeb440ed9 100644
--- a/gcc/builtins.def
+++ b/gcc/builtins.def
@@ -1170,6 +1170,10 @@ DEF_BUILTIN_STUB (BUILT_IN_NONLOCAL_GOTO, 
"__builtin_nonlocal_goto")
 DEF_EXT_LIB_BUILTIN (BUILT_IN_GCC_NESTED_PTR_CREATED, 
"__gcc_nested_func_ptr_created", BT_FN_VOID_PTR_PTR_PTR, ATTR_NOTHROW_LIST)
 DEF_EXT_LIB_BUILTIN (BUILT_IN_GCC_NESTED_PTR_DELETED, 
"__gcc_nested_func_ptr_deleted", BT_FN_VOID, ATTR_NOTHROW_LIST)
 
+/* Information needed to call (nested) functions.  */
+DEF_GCC_BUILTIN (BUILT_IN_CALL_CODE_ADDRESS, "call_code_address", 
BT_FN_PTR_PTR, ATTR_NULL)
+DEF_GCC_BUILTIN (BUILT_IN_CALL_STATIC_CHAIN, "call_static_chain", 
BT_FN_PTR_PTR, ATTR_NULL)
+
 /* Implementing __builtin_setjmp.  */
 DEF_BUILTIN_STUB (BUILT_IN_SETJMP_SETUP, "__builtin_setjmp_setup")
 DEF_BUILTIN_STUB (BUILT_IN_SETJMP_RECEIVER, "__builtin_setjmp_receiver")
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 9f821f04853..f6a536747be 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -13978,7 +13978,10 @@ safe.
 GCC implements taking the address of a nested function using a technique
 called @dfn{trampolines}.  This technique was described in
 @cite{Lexical Closures for C++} (Thomas M. Breuel, USENIX
-C++ Conference Proceedings, October 17-21, 1988).
+C++ Conference Proceedings, October 17-21, 1988).  It is also
+possible to use the built-in @code{__builtin_call_with_static_chain}
+(@pxref{Constructing Calls}) to invoke a nested function indirectly
+without the use of a trampoline.
 
 A nested function can jump to a label inherited from a containing
 function, provided the label is explicitly declared in the containing
@@ -16674,11 +16677,73 @@ myopen (const char *path, int oflag, ...)
 The @var{call_exp} expression must be a function call, and the
 @var{pointer_exp} expression must be a pointer.  The @var{pointer_exp}
 is passed to the function call in the target's static chain location.
-The result of builtin is the result of the function call.
+The result of the built-in is the result of the function call.
+This built-in can be used to call Go closures from C, and it can be
+used to call @xref{Nested Functions} using the code address and
+static chain obtained with @code{__builtin_call_code_address} and
+@code{__builtin_call_static_chain}, respectively.
 
-@emph{Note:} This builtin is only available for C@.
-This builtin can be used to call Go closures from C.
+@smallexample
+int process(int (*callback)(int, void *), void *data)
+@{
+  int value = 5;
+  return (*callback)(value, data);
+@}
+
+struct call_info @{
+  int (*code)(int);
+  void *chain;
+@};
+
+int callback(int arg, void *data)
+@{
+   struct call_info *ci = data;
+   return __builtin_call_with_static_chain(ci->code(arg), ci->chain);
+@}
+
+int doit(int x)
+@{
+  int worker(int y)
+  @{
+    return x + y;
+  @}
+
+  struct call_info ci = @{
+    ci.code = __builtin_call_code_address(worker),
+    ci.chain = __builtin_call_static_chain(worker),
+  @};
+
+  return process(callback, &ci);
+@}
+@end smallexample
+
+@emph{Note:} This built-in is only available for C@.
+@enddefbuiltin
+
+@defbuiltin{@var{type} __builtin_call_static_chain (@var{pointer_exp})}
+
+The @var{pointer_exp} expression must a function identifier.
+The result is the static chain pointer that can be used to call
+the function in its current context using the built-in
+@code{__builtin_call_with_static_chain}, or a null pointer if
+no static chain is needed.
+
+@emph{Note:} The lifetime of a nested function that accesses the
+environment of an enclosing function is limited.  Any use of the
+static chain after this lifetime has ended has undefined behavior.
+@enddefbuiltin
+
+@defbuiltin{@var{type} __builtin_call_code_address (@var{pointer_exp})}
+
+The @var{pointer_exp} expression must be a function identifier.
+The result is the static address of the function.  For a nested function,
+it represents the address of the underlying machine code and not of a
+trampoline that would otherwise be generated to setup the static chain.
+The code address can be used together with a static chain pointer to
+call the function with the built-in @code{__builtin_call_with_static_chain}.
 
+@emph{Note:} Calling a function that requires a static chain using the
+code address directly in a function call expression has undefined behavior.
 @enddefbuiltin
 
 @node Return Address
diff --git a/gcc/gimple-fold.cc b/gcc/gimple-fold.cc
index fab4886b0f2..4422a48383d 100644
--- a/gcc/gimple-fold.cc
+++ b/gcc/gimple-fold.cc
@@ -5409,6 +5409,33 @@ gimple_fold_builtin_stdarg (gimple_stmt_iterator *gsi, 
gcall *call)
     }
 }
 
+/* Fold __builtin_call_{code_address,static_chain} builtins.  This handles
+   only the trivial left-over cases not processed in tree-nested.cc.  */
+
+static bool
+gimple_fold_builtin_call_info (gimple_stmt_iterator *gsi,
+                              enum built_in_function fcode)
+{
+  gcall *stmt = as_a <gcall *>(gsi_stmt (*gsi));
+  tree arg = gimple_call_arg (stmt, 0);
+
+  /* The error will be emitted in builtins.cc.  */
+  if (TREE_CODE (arg) != ADDR_EXPR
+      || FUNCTION_DECL != TREE_CODE (TREE_OPERAND (arg, 0)))
+    return false;
+
+  /* The case with static chain is handled in tree-nested.cc.  */
+  gcc_assert (!DECL_STATIC_CHAIN (TREE_OPERAND (arg, 0)));
+
+  if (fcode == BUILT_IN_CALL_STATIC_CHAIN)
+    replace_call_with_value (gsi, null_pointer_node);
+  else
+    replace_call_with_value (gsi, arg);
+
+  return true;
+}
+
+
 /* Fold the non-target builtin at *GSI and return whether any simplification
    was made.  */
 
@@ -5589,6 +5616,10 @@ gimple_fold_builtin (gimple_stmt_iterator *gsi)
     case BUILT_IN_CONSTANT_P:
       return gimple_fold_builtin_constant_p (gsi);
 
+    case BUILT_IN_CALL_CODE_ADDRESS:
+    case BUILT_IN_CALL_STATIC_CHAIN:
+      return gimple_fold_builtin_call_info (gsi, fcode);
+
     default:;
     }
 
diff --git a/gcc/testsuite/gcc.dg/builtin-call-info-1.c 
b/gcc/testsuite/gcc.dg/builtin-call-info-1.c
new file mode 100644
index 00000000000..ecfe3a62c9a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-call-info-1.c
@@ -0,0 +1,34 @@
+/* { dg-do run } */
+/* { dg-options "-Wtrampolines" } */
+
+typedef struct {
+       int (*code)(int);
+       void *chain;
+} call_info_t;
+
+int apply(call_info_t c, int value)
+{
+       return __builtin_call_with_static_chain(c.code(value), c.chain);
+}
+
+int foo(int x)
+{
+       int add(int y)
+       {
+               return x + y;
+       }
+
+       call_info_t info = {
+               __builtin_call_code_address(add),
+               __builtin_call_static_chain(add)
+       };
+
+       return apply(info, x);
+}
+
+int main()
+{
+       if (4 != foo(2))
+               __builtin_abort();
+}
+
diff --git a/gcc/testsuite/gcc.dg/builtin-call-info-2.c 
b/gcc/testsuite/gcc.dg/builtin-call-info-2.c
new file mode 100644
index 00000000000..acb6ad98382
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-call-info-2.c
@@ -0,0 +1,47 @@
+/* { dg-do run } */
+/* { dg-options "-Wtrampolines" } */
+
+/* Check that we get the expected pointers in
+   different context.  */
+
+int f(int x)
+{
+
+       if (f != __builtin_call_code_address(f))
+               __builtin_abort();
+
+       if ((void*)0 != __builtin_call_static_chain(f))
+               __builtin_abort();
+
+       static int (*code)(int);
+       static void *chain;
+
+       int g(int y)
+       {
+               if (code != __builtin_call_code_address(g))
+                       __builtin_abort();
+               
+               if (chain != __builtin_call_static_chain(g))
+                       __builtin_abort();
+       
+               return x + y; 
+       }
+
+       code = __builtin_call_code_address(g);
+       chain = __builtin_call_static_chain(g);
+
+       return g(x);
+}
+
+int main()
+{
+       if (f != __builtin_call_code_address(f))
+               __builtin_abort();
+
+       if ((void*)0 != __builtin_call_static_chain(f))
+               __builtin_abort();
+
+       if (6 != f(3))
+               __builtin_abort();
+}
+
diff --git a/gcc/testsuite/gcc.dg/builtin-call-info-3.c 
b/gcc/testsuite/gcc.dg/builtin-call-info-3.c
new file mode 100644
index 00000000000..70101c5fdee
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-call-info-3.c
@@ -0,0 +1,24 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -Wtrampolines -Wreturn-local-addr" } */
+
+
+void * foo(int n)
+{
+       int g(int x)
+       {
+               return n + x;
+       }
+
+       return __builtin_call_code_address(g);  // ok
+}
+
+void * bar(int n)
+{
+       int g(int x)
+       {
+               return n + x;
+       }
+
+       return __builtin_call_static_chain(g);  /* { dg-warning "returns 
address of local variable" } */
+}
+
diff --git a/gcc/tree-inline.cc b/gcc/tree-inline.cc
index 302ab8d6b7c..a3b2c71fb22 100644
--- a/gcc/tree-inline.cc
+++ b/gcc/tree-inline.cc
@@ -3760,9 +3760,11 @@ initialize_inlined_parameters (copy_body_data *id, 
gimple *stmt,
   gcc_assert (fn != current_function_decl);
   if (p)
     {
-      /* No static chain?  Seems like a bug in tree-nested.cc.  */
-      gcc_assert (static_chain);
-
+      /* Calling a function that requires a static chain directly
+        without setting up the static chain has undefined behavior
+        at run-time.  */
+      if (!static_chain)
+       static_chain = null_pointer_node;
       setup_one_parameter (id, p, static_chain, fn, bb, &vars);
     }
 
diff --git a/gcc/tree-nested.cc b/gcc/tree-nested.cc
index cdccc51d33e..4019afb5e2c 100644
--- a/gcc/tree-nested.cc
+++ b/gcc/tree-nested.cc
@@ -36,6 +36,7 @@
 #include "gimplify.h"
 #include "gimple-iterator.h"
 #include "gimple-walk.h"
+#include "gimple-fold.h"
 #include "tree-cfg.h"
 #include "explow.h"
 #include "langhooks.h"
@@ -2882,6 +2883,12 @@ convert_tramp_reference_stmt (gimple_stmt_iterator *gsi, 
bool *handled_ops_p,
     {
     case GIMPLE_CALL:
       {
+       tree decl = gimple_call_fndecl (stmt);
+       if (decl && fndecl_built_in_p (decl, BUILT_IN_NORMAL)
+           && (DECL_FUNCTION_CODE (decl) == BUILT_IN_CALL_CODE_ADDRESS
+               || DECL_FUNCTION_CODE (decl) == BUILT_IN_CALL_STATIC_CHAIN))
+         break;
+
        /* Only walk call arguments, lest we generate trampolines for
           direct calls.  */
        unsigned long i, nargs = gimple_call_num_args (stmt);
@@ -2994,11 +3001,41 @@ convert_gimple_call (gimple_stmt_iterator *gsi, bool 
*handled_ops_p,
   switch (gimple_code (stmt))
     {
     case GIMPLE_CALL:
-      if (gimple_call_chain (stmt))
-       break;
       decl = gimple_call_fndecl (stmt);
       if (!decl)
        break;
+      if (fndecl_built_in_p (decl, BUILT_IN_NORMAL)
+         && (DECL_FUNCTION_CODE (decl) == BUILT_IN_CALL_CODE_ADDRESS
+             || DECL_FUNCTION_CODE (decl) == BUILT_IN_CALL_STATIC_CHAIN))
+       {
+         tree d = gimple_call_arg (stmt, 0);
+         if (TREE_CODE (d) != ADDR_EXPR
+             || FUNCTION_DECL != TREE_CODE (TREE_OPERAND (d, 0)))
+           break;
+         tree ret = null_pointer_node;
+         if (DECL_FUNCTION_CODE (decl) == BUILT_IN_CALL_CODE_ADDRESS)
+           {
+             /* Return code pointer.  */
+             ret = build_addr (TREE_OPERAND (d, 0));
+             TREE_NO_TRAMPOLINE (ret) = 1;
+           }
+         else
+           {
+             decl = TREE_OPERAND (d, 0);
+             target_context = decl_function_context (decl);
+             if (target_context && DECL_STATIC_CHAIN (decl))
+               {
+                 /* Return static chain.  */
+                 info->static_chain_added
+                   |= (1 << (info->context != target_context));
+                 ret = get_static_chain (info, target_context, &wi->gsi);
+               }
+           }
+         replace_call_with_value (gsi, ret);
+         break;
+       }
+      if (gimple_call_chain (stmt))
+       break;
       target_context = decl_function_context (decl);
       if (target_context && DECL_STATIC_CHAIN (decl))
        {

Reply via email to