Any comments on this patch? 



Best
Martin


Am Samstag, dem 18.04.2026 um 12:44 +0200 schrieb Martin Uecker:
> This is how this patch could look like with a single builtin that returns
> an anonymous structure.  I named it __builtin_call_info which might be a bit
> nicer in combination with __builtin_call_with_static_chain.  I also now fold
> this in gimple-fold.cc for the trivial case where there is no static
> chain.
> 
> Bootstrapped and regression tested on x86_64. No AI was used.
> 
> Best,
> Martin
> 
> 
> 
> 
> 
>     c: Built-in to access code pointer and static chain of nested function.
>     
>     This patch adds a new built-in, __builtin_call_info, to extract the code
>     pointer and the static chain pointer from a (nested) function.  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. when trampolines prevent devirtualization (PR49666).
>     
>     gcc/ChangeLog:
>             * builtins.def: Add new built-in.
>             * builtin-types.def: Add new call info type.
>             * tree.h: Add new call info type.
>             * tree-core.h: Add new call info type.
>             * tree.cc (build_common_tree_nodes): Build new call info type 
> node.
>             * builtins.cc (expand_builtin): Mark new case as unreachable.
>             (is_simple_builtin): Add new built-in.
>             * gimple-fold.cc (gimple_fold_builtin_call_info): New function.
>             (gimple_fold_builtin) Expand new built-in for non-nested 
> functions.
>             * tree-nested.cc (convert_tramp_reference_stmt): Ingore new 
> built-in.
>             (convert_gimple_call): Expand built-in for nested functions.
>             * tree-inline.cc (initialize_inlined_parameters): Replace
>             assertion with error.
>     
>     gcc/doc/ChangeLog:
>             * extend.texi: Document new built-in.
>     
>     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/builtin-types.def b/gcc/builtin-types.def
> index ab0cbc65cfc..1ca220d1ce1 100644
> --- a/gcc/builtin-types.def
> +++ b/gcc/builtin-types.def
> @@ -209,6 +209,8 @@ DEF_PRIMITIVE_TYPE (BT_DFLOAT64X, (dfloat64x_type_node
>  DEF_PRIMITIVE_TYPE (BT_VALIST_REF, va_list_ref_type_node)
>  DEF_PRIMITIVE_TYPE (BT_VALIST_ARG, va_list_arg_type_node)
>  
> +DEF_PRIMITIVE_TYPE (BT_CALL_INFO, call_info_type_node)
> +
>  DEF_PRIMITIVE_TYPE (BT_I1, builtin_type_for_size (BITS_PER_UNIT*1, 1))
>  DEF_PRIMITIVE_TYPE (BT_I2, builtin_type_for_size (BITS_PER_UNIT*2, 1))
>  DEF_PRIMITIVE_TYPE (BT_I4, builtin_type_for_size (BITS_PER_UNIT*4, 1))
> @@ -425,6 +427,7 @@ DEF_FUNCTION_TYPE_1 (BT_FN_UINT16_UINT32, BT_UINT16, 
> BT_UINT32)
>  DEF_FUNCTION_TYPE_1 (BT_FN_UINT32_UINT16, BT_UINT32, BT_UINT16)
>  DEF_FUNCTION_TYPE_1 (BT_FN_INT_FENV_T_PTR, BT_INT, BT_FENV_T_PTR)
>  DEF_FUNCTION_TYPE_1 (BT_FN_INT_CONST_FENV_T_PTR, BT_INT, BT_CONST_FENV_T_PTR)
> +DEF_FUNCTION_TYPE_1 (BT_FN_CALL_INFO_PTR, BT_CALL_INFO, BT_PTR)
>  
>  DEF_POINTER_TYPE (BT_PTR_FN_VOID_PTR, BT_FN_VOID_PTR)
>  
> diff --git a/gcc/builtins.cc b/gcc/builtins.cc
> index 692e20088c2..efef8afce69 100644
> --- a/gcc/builtins.cc
> +++ b/gcc/builtins.cc
> @@ -8093,6 +8093,9 @@ 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_INFO:
> +      gcc_unreachable ();
> +
>      case BUILT_IN_SAVEREGS:
>        return expand_builtin_saveregs ();
>  
> @@ -12311,6 +12314,7 @@ is_simple_builtin (tree decl)
>        case BUILT_IN_STACK_SAVE:
>        case BUILT_IN_STACK_RESTORE:
>        case BUILT_IN_DWARF_CFA:
> +      case BUILT_IN_CALL_INFO:
>       /* 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 8ab0599b17f..1d5bff152e3 100644
> --- a/gcc/builtins.def
> +++ b/gcc/builtins.def
> @@ -1164,6 +1164,9 @@ 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_INFO, "call_info", BT_FN_CALL_INFO_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 47b0bdf1340..2ddcd1f30d6 100644
> --- a/gcc/doc/extend.texi
> +++ b/gcc/doc/extend.texi
> @@ -16610,6 +16610,19 @@ This builtin can be used to call Go closures from C.
>  
>  @enddefbuiltin
>  
> +@defbuiltin{@var{type} __builtin_call_info (@var{pointer_exp})}
> +
> +The @var{pointer_exp} expression must designate a function.
> +The result is a structure with two members of pointer type named @code{code}
> +and @code{chain}.  @code{code} holds the static address of the function.
> +For a nested function, the address represents the address of the underlying
> +machine code and not of a trampoline that would otherwise be generated to
> +setup the static chain.  @code{chain} is the chain pointer that that is
> +needed to call the function call in its current context, or a null pointer
> +if none is needed.
> +
> +@enddefbuiltin
> +
>  @node Return Address
>  @section Getting the Return or Frame Address of a Function
>  
> diff --git a/gcc/gimple-fold.cc b/gcc/gimple-fold.cc
> index e9019e2c7bd..d8eceb080be 100644
> --- a/gcc/gimple-fold.cc
> +++ b/gcc/gimple-fold.cc
> @@ -5354,6 +5354,41 @@ gimple_fold_builtin_stdarg (gimple_stmt_iterator *gsi, 
> gcall *call)
>      }
>  }
>  
> +/* Fold __builtin_call_info builtin.  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)
> +{
> +  gcall *stmt = as_a <gcall *>(gsi_stmt (*gsi));
> +  tree arg = gimple_call_arg (stmt, 0);
> +
> +  if (TREE_CODE (arg) != ADDR_EXPR || !DECL_P (TREE_OPERAND (arg, 0))
> +      || FUNCTION_DECL != TREE_CODE (TREE_OPERAND (arg, 0)))
> +    {
> +      error_at (gimple_location (stmt),
> +             "argument to %<__builtin_call_info%> must be a function");
> +      return false;
> +    }
> +
> +  /* The case with static chain is handled in tree-nested.cc.  */
> +  gcc_assert (!DECL_STATIC_CHAIN (TREE_OPERAND (arg, 0)));
> +
> +  tree fields = TYPE_FIELDS (call_info_type_node);
> +  tree ret = create_tmp_var (call_info_type_node);
> +  tree cref2 = build3 (COMPONENT_REF, TREE_TYPE (fields),
> +                    ret, fields, NULL_TREE);
> +  tree cref1 = build3 (COMPONENT_REF, TREE_TYPE (TREE_CHAIN (fields)),
> +                    ret, TREE_CHAIN (fields), NULL_TREE);
> +  gimple *g = gimple_build_assign (cref1, null_pointer_node);
> +  gsi_insert_before (gsi, g, GSI_SAME_STMT);
> +  g = gimple_build_assign (cref2, arg);
> +  gsi_insert_before (gsi, g, GSI_SAME_STMT);
> +
> +  replace_call_with_value (gsi, ret);
> +  return true;
> +}
> +
>  /* Fold the non-target builtin at *GSI and return whether any simplification
>     was made.  */
>  
> @@ -5530,6 +5565,9 @@ gimple_fold_builtin (gimple_stmt_iterator *gsi)
>      case BUILT_IN_CONSTANT_P:
>        return gimple_fold_builtin_constant_p (gsi);
>  
> +    case BUILT_IN_CALL_INFO:
> +      return gimple_fold_builtin_call_info (gsi);
> +
>      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..24ac8b1ccf2
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/builtin-call-info-1.c
> @@ -0,0 +1,26 @@
> +/* { dg-do run } */
> +/* { dg-options "-Wtrampolines" } */
> +
> +typedef typeof(__builtin_call_info(nullptr)) call_info_t;
> +
> +int apply(call_info_t c, int value)
> +{
> +     return __builtin_call_with_static_chain(((int(*)(int))c.code)(value), 
> c.chain);
> +}
> +
> +int foo(int x)
> +{
> +     int add(int y)
> +     {
> +             return x + y;
> +     }
> +
> +     return apply(__builtin_call_info(add), 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..0ec8d865971
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/builtin-call-info-2.c
> @@ -0,0 +1,49 @@
> +/* { dg-do run } */
> +/* { dg-options "-Wtrampolines" } */
> +
> +/* Check that we get the expected pointers in
> +   different context.  */
> +
> +int f(int x)
> +{
> +     static typeof(__builtin_call_info(f)) c;
> +     c = __builtin_call_info(f);
> +
> +     if (f != c.code)
> +             __builtin_abort();
> +
> +     if ((void*)0 != c.chain)
> +             __builtin_abort();
> +
> +     int g(int y)
> +     {
> +             auto c2 = __builtin_call_info(g);
> +
> +             if (c.code != c2.code)
> +                     __builtin_abort();
> +             
> +             if (c.chain != c2.chain)
> +                     __builtin_abort();
> +     
> +             return x + y; 
> +     }
> +
> +     c = __builtin_call_info(g);
> +
> +     return g(x);
> +}
> +
> +int main()
> +{
> +     auto c = __builtin_call_info(f);
> +
> +     if (f != c.code)
> +             __builtin_abort();
> +
> +     if ((void*)0 != c.chain)
> +             __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..5d78892fefb
> --- /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_info(g).code;     // ok
> +}
> +
> +void * bar(int n)
> +{
> +     int g(int x)
> +     {
> +             return n + x;
> +     }
> +
> +     return __builtin_call_info(g).chain;    /* { dg-warning "returns 
> address of local variable" } */
> +}
> +
> diff --git a/gcc/tree-core.h b/gcc/tree-core.h
> index 07e9318f5e8..0a3740b060a 100644
> --- a/gcc/tree-core.h
> +++ b/gcc/tree-core.h
> @@ -822,6 +822,7 @@ enum tree_index : unsigned {
>    TI_FEXCEPT_T_PTR_TYPE,
>    TI_CONST_FEXCEPT_T_PTR_TYPE,
>    TI_POINTER_SIZED_TYPE,
> +  TI_CALL_INFO_TYPE,
>  
>    TI_DFLOAT32_TYPE,
>    TI_DFLOAT64_TYPE,
> diff --git a/gcc/tree-inline.cc b/gcc/tree-inline.cc
> index 087fcc8a8b8..8f5701ab693 100644
> --- a/gcc/tree-inline.cc
> +++ b/gcc/tree-inline.cc
> @@ -3760,8 +3760,8 @@ 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);
> +      if (!static_chain)
> +     error ("called function requires a static chain");
>  
>        setup_one_parameter (id, p, static_chain, fn, bb, &vars);
>      }
> diff --git a/gcc/tree-nested.cc b/gcc/tree-nested.cc
> index cdccc51d33e..0b0affe9a44 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,11 @@ 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)
> +         && BUILT_IN_CALL_INFO == DECL_FUNCTION_CODE (decl))
> +       break;
> +
>       /* Only walk call arguments, lest we generate trampolines for
>          direct calls.  */
>       unsigned long i, nargs = gimple_call_num_args (stmt);
> @@ -2994,11 +3000,52 @@ 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_INFO)
> +     {
> +       tree d = gimple_call_arg (stmt, 0);
> +       tree ret1 = null_pointer_node;
> +       tree ret2 = null_pointer_node;
> +       if (TREE_CODE (d) != ADDR_EXPR || !DECL_P (TREE_OPERAND (d, 0))
> +           || FUNCTION_DECL != TREE_CODE (TREE_OPERAND (d, 0)))
> +         {
> +           error_at (gimple_location (stmt),
> +                     "argument to %<__builtin_call_info%> "
> +                     "must be a function");
> +         }
> +       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));
> +               ret1 = get_static_chain (info, target_context, &wi->gsi);
> +             }
> +           /* Return code pointer.  */
> +           ret2 = build_addr (TREE_OPERAND (d, 0));
> +           TREE_NO_TRAMPOLINE (ret2) = 1;
> +         }
> +       tree fields = TYPE_FIELDS (call_info_type_node);
> +       tree ret = create_tmp_var_for (info, call_info_type_node, NULL);
> +       tree cref2 = build3 (COMPONENT_REF, TREE_TYPE (fields),
> +                            ret, fields, NULL_TREE);
> +       tree cref1 = build3 (COMPONENT_REF, TREE_TYPE (TREE_CHAIN (fields)),
> +                            ret, TREE_CHAIN (fields), NULL_TREE);
> +       gimple *assign = gimple_build_assign (cref1, ret1);
> +       gsi_insert_before (gsi, assign, GSI_SAME_STMT);
> +       assign = gimple_build_assign (cref2, ret2);
> +       gsi_insert_before (gsi, assign, GSI_SAME_STMT);
> +       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))
>       {
> diff --git a/gcc/tree.cc b/gcc/tree.cc
> index d0e745e8d28..38d0590a8c7 100644
> --- a/gcc/tree.cc
> +++ b/gcc/tree.cc
> @@ -9984,6 +9984,18 @@ build_common_tree_nodes (bool signed_char)
>      va_list_type_node = t;
>    }
>  
> +  /* Create the call info type.  */
> +  call_info_type_node = make_node (RECORD_TYPE);
> +  tree code_field = build_decl (BUILTINS_LOCATION, FIELD_DECL,
> +                             get_identifier ("code"), ptr_type_node);
> +  DECL_FIELD_CONTEXT (code_field) = call_info_type_node;
> +  tree data_field = build_decl (BUILTINS_LOCATION, FIELD_DECL,
> +                             get_identifier ("chain"), ptr_type_node);
> +  DECL_FIELD_CONTEXT (data_field) = call_info_type_node;
> +  TREE_CHAIN (code_field) = data_field;
> +  TYPE_FIELDS (call_info_type_node) = code_field;
> +  layout_type (call_info_type_node);
> +
>    /* SCEV analyzer global shared trees.  */
>    chrec_dont_know = make_node (SCEV_NOT_KNOWN);
>    TREE_TYPE (chrec_dont_know) = void_type_node;
> diff --git a/gcc/tree.h b/gcc/tree.h
> index 19bc67718d1..c63898f672c 100644
> --- a/gcc/tree.h
> +++ b/gcc/tree.h
> @@ -4665,6 +4665,7 @@ tree_strip_any_location_wrapper (tree exp)
>  #define fexcept_t_ptr_type_node              
> global_trees[TI_FEXCEPT_T_PTR_TYPE]
>  #define const_fexcept_t_ptr_type_node        
> global_trees[TI_CONST_FEXCEPT_T_PTR_TYPE]
>  #define pointer_sized_int_node               
> global_trees[TI_POINTER_SIZED_TYPE]
> +#define call_info_type_node          global_trees[TI_CALL_INFO_TYPE]
>  
>  #define boolean_type_node            global_trees[TI_BOOLEAN_TYPE]
>  #define boolean_false_node           global_trees[TI_BOOLEAN_FALSE]

Reply via email to