{"id":2224765,"url":"http://patchwork.ozlabs.org/api/1.1/patches/2224765/?format=json","web_url":"http://patchwork.ozlabs.org/project/gcc/patch/3fe70005e9f2b334bc18674ea5d38349d10b458f.camel@tugraz.at/","project":{"id":17,"url":"http://patchwork.ozlabs.org/api/1.1/projects/17/?format=json","name":"GNU Compiler Collection","link_name":"gcc","list_id":"gcc-patches.gcc.gnu.org","list_email":"gcc-patches@gcc.gnu.org","web_url":null,"scm_url":null,"webscm_url":null},"msgid":"<3fe70005e9f2b334bc18674ea5d38349d10b458f.camel@tugraz.at>","date":"2026-04-18T10:44:12","name":"[RFC,C,v2] Built-in to access code pointer and static chain of nested function","commit_ref":null,"pull_url":null,"state":"new","archived":false,"hash":"fdd3d97bc0b6c6ffd5d812b313eede69b4f28844","submitter":{"id":85465,"url":"http://patchwork.ozlabs.org/api/1.1/people/85465/?format=json","name":"Martin Uecker","email":"uecker@tugraz.at"},"delegate":null,"mbox":"http://patchwork.ozlabs.org/project/gcc/patch/3fe70005e9f2b334bc18674ea5d38349d10b458f.camel@tugraz.at/mbox/","series":[{"id":500436,"url":"http://patchwork.ozlabs.org/api/1.1/series/500436/?format=json","web_url":"http://patchwork.ozlabs.org/project/gcc/list/?series=500436","date":"2026-04-18T10:44:12","name":"[RFC,C,v2] Built-in to access code pointer and static chain of nested function","version":2,"mbox":"http://patchwork.ozlabs.org/series/500436/mbox/"}],"comments":"http://patchwork.ozlabs.org/api/patches/2224765/comments/","check":"pending","checks":"http://patchwork.ozlabs.org/api/patches/2224765/checks/","tags":{},"headers":{"Return-Path":"<gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org>","X-Original-To":["incoming@patchwork.ozlabs.org","gcc-patches@gcc.gnu.org"],"Delivered-To":["patchwork-incoming@legolas.ozlabs.org","gcc-patches@gcc.gnu.org"],"Authentication-Results":["legolas.ozlabs.org;\n\tdkim=pass (1024-bit key;\n unprotected) header.d=tugraz.at header.i=@tugraz.at header.a=rsa-sha256\n header.s=mailrelay header.b=GLB2gmAk;\n\tdkim-atps=neutral","legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org\n (client-ip=38.145.34.32; helo=vm01.sourceware.org;\n envelope-from=gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org;\n receiver=patchwork.ozlabs.org)","sourceware.org;\n\tdkim=pass (1024-bit key,\n unprotected) header.d=tugraz.at header.i=@tugraz.at header.a=rsa-sha256\n header.s=mailrelay header.b=GLB2gmAk","sourceware.org; dmarc=pass (p=quarantine dis=none)\n header.from=tugraz.at","sourceware.org; spf=pass smtp.mailfrom=tugraz.at","server2.sourceware.org;\n arc=none smtp.remote-ip=129.27.2.202"],"Received":["from vm01.sourceware.org (vm01.sourceware.org [38.145.34.32])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4fyT0c3FGfz1yCv\n\tfor <incoming@patchwork.ozlabs.org>; Sat, 18 Apr 2026 20:45:03 +1000 (AEST)","from vm01.sourceware.org (localhost [127.0.0.1])\n\tby sourceware.org (Postfix) with ESMTP id 8CD824AA394F\n\tfor <incoming@patchwork.ozlabs.org>; Sat, 18 Apr 2026 10:44:59 +0000 (GMT)","from mailrelay.tugraz.at (mailrelay.tugraz.at [129.27.2.202])\n by sourceware.org (Postfix) with ESMTPS id 9704E4AA394F\n for <gcc-patches@gcc.gnu.org>; Sat, 18 Apr 2026 10:44:28 +0000 (GMT)","from vra-172-68.tugraz.at (vra-172-68.tugraz.at [129.27.172.68])\n by mailrelay.tugraz.at (Postfix) with ESMTPSA id 4fySzd1l8Vz9rxN;\n Sat, 18 Apr 2026 12:44:13 +0200 (CEST)"],"DKIM-Filter":["OpenDKIM Filter v2.11.0 sourceware.org 8CD824AA394F","OpenDKIM Filter v2.11.0 sourceware.org 9704E4AA394F"],"DMARC-Filter":"OpenDMARC Filter v1.4.2 sourceware.org 9704E4AA394F","ARC-Filter":"OpenARC Filter v1.0.0 sourceware.org 9704E4AA394F","ARC-Seal":"i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1776509070; cv=none;\n b=Ie0DqCBWO/fR4vIHBYOUGiE8V0Os8wSeGMQ6D03wJnG6+ZN9uhVBwT10EQSRyo0TwDG0+WDOvQ6H1Y2zCJf8/VlRx2Y4FwUCP7zEdt4ZSMPLDo3oyZfDbvxJIyHzsmVwu+GdqP5YDqQOhhcr5Ph47p+7/qbwxkkwo39H5HK9Hbo=","ARC-Message-Signature":"i=1; a=rsa-sha256; d=sourceware.org; s=key;\n t=1776509070; c=relaxed/simple;\n bh=ZSOiLznSbHeebJ3l0m+SLrGqNrka3np0urDwXfxw4J0=;\n h=DKIM-Signature:Message-ID:Subject:From:To:Date:MIME-Version;\n b=FMw/FhrfT/F7ARAopnv1omIbA6/6dPk4QJ+NqWIrxlT98U6Cs2I98+5gEChJXj6B6anQGAbmKP0WY0V+xjbdGei1TMOXGiumUahJAv9nTBTMOBqmZmrxYFnsWuork3ZzDaArF463t3cySB/qQBmEdtF848hobG9USkaFmaIcKcg=","ARC-Authentication-Results":"i=1; server2.sourceware.org","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=tugraz.at;\n s=mailrelay; t=1776509053;\n bh=usWo/AFHhXqnHjbH12SRZUbXBQlketi9JgXeIAxp+L0=;\n h=Subject:From:To:Cc:Date;\n b=GLB2gmAk6kcaG+4ZuZW++LyIJNJicVZBM5H5EQTzDw4IuPaOKl56jnHrGXgXEZwgj\n TlLB6S3YzN5ZZLB2jzHeNf4tgI1PmaomCFnvcRE2Mnbm0lEul2SjUUyuZsJ9VGLktG\n EesYWsTb0mzljcFzlwwi7cDgV2195RwBC2ix77/k=","Message-ID":"<3fe70005e9f2b334bc18674ea5d38349d10b458f.camel@tugraz.at>","Subject":"[RFC C PATCH v2] Built-in to access code pointer and static chain\n of nested function","From":"Martin Uecker <uecker@tugraz.at>","To":"gcc-patches@gcc.gnu.org","Cc":"Joseph Myers <josmyers@redhat.com>, Richard Biener <rguenther@suse.de>,\n Andrew Pinski <andrew.pinski@oss.qualcomm.com>, Jakub Jelinek\n <jakub@redhat.com>","Date":"Sat, 18 Apr 2026 12:44:12 +0200","Content-Type":"text/plain; charset=\"UTF-8\"","Content-Transfer-Encoding":"quoted-printable","User-Agent":"Evolution 3.56.2-0+deb13u1 ","MIME-Version":"1.0","X-TUG-Backscatter-control":"G/VXY7/6zeyuAY/PU2/0qw","X-Scanned-By":"MIMEDefang 2.74 on 129.27.10.117","X-BeenThere":"gcc-patches@gcc.gnu.org","X-Mailman-Version":"2.1.30","Precedence":"list","List-Id":"Gcc-patches mailing list <gcc-patches.gcc.gnu.org>","List-Unsubscribe":"<https://gcc.gnu.org/mailman/options/gcc-patches>,\n <mailto:gcc-patches-request@gcc.gnu.org?subject=unsubscribe>","List-Archive":"<https://gcc.gnu.org/pipermail/gcc-patches/>","List-Post":"<mailto:gcc-patches@gcc.gnu.org>","List-Help":"<mailto:gcc-patches-request@gcc.gnu.org?subject=help>","List-Subscribe":"<https://gcc.gnu.org/mailman/listinfo/gcc-patches>,\n <mailto:gcc-patches-request@gcc.gnu.org?subject=subscribe>","Errors-To":"gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org"},"content":"This is how this patch could look like with a single builtin that returns\nan anonymous structure.  I named it __builtin_call_info which might be a bit\nnicer in combination with __builtin_call_with_static_chain.  I also now fold\nthis in gimple-fold.cc for the trivial case where there is no static\nchain.\n\nBootstrapped and regression tested on x86_64. No AI was used.\n\nBest,\nMartin\n\n\n\n\n\n    c: Built-in to access code pointer and static chain of nested function.\n    \n    This patch adds a new built-in, __builtin_call_info, to extract the code\n    pointer and the static chain pointer from a (nested) function.  Those can\n    then be used to call the nested function using the existing built-in\n    __builtin_call_with_static_chain.  This feature can be used to avoid the\n    creation of trampolines and often allows writing more efficient\n    code, e.g. when trampolines prevent devirtualization (PR49666).\n    \n    gcc/ChangeLog:\n            * builtins.def: Add new built-in.\n            * builtin-types.def: Add new call info type.\n            * tree.h: Add new call info type.\n            * tree-core.h: Add new call info type.\n            * tree.cc (build_common_tree_nodes): Build new call info type node.\n            * builtins.cc (expand_builtin): Mark new case as unreachable.\n            (is_simple_builtin): Add new built-in.\n            * gimple-fold.cc (gimple_fold_builtin_call_info): New function.\n            (gimple_fold_builtin) Expand new built-in for non-nested functions.\n            * tree-nested.cc (convert_tramp_reference_stmt): Ingore new built-in.\n            (convert_gimple_call): Expand built-in for nested functions.\n            * tree-inline.cc (initialize_inlined_parameters): Replace\n            assertion with error.\n    \n    gcc/doc/ChangeLog:\n            * extend.texi: Document new built-in.\n    \n    gcc/testsuite/ChangeLog:\n            * gcc.dg/builtin-call-info-1.c: New test.\n            * gcc.dg/builtin-call-info-2.c: New test.\n            * gcc.dg/builtin-call-info-3.c: New test.","diff":"diff --git a/gcc/builtin-types.def b/gcc/builtin-types.def\nindex ab0cbc65cfc..1ca220d1ce1 100644\n--- a/gcc/builtin-types.def\n+++ b/gcc/builtin-types.def\n@@ -209,6 +209,8 @@ DEF_PRIMITIVE_TYPE (BT_DFLOAT64X, (dfloat64x_type_node\n DEF_PRIMITIVE_TYPE (BT_VALIST_REF, va_list_ref_type_node)\n DEF_PRIMITIVE_TYPE (BT_VALIST_ARG, va_list_arg_type_node)\n \n+DEF_PRIMITIVE_TYPE (BT_CALL_INFO, call_info_type_node)\n+\n DEF_PRIMITIVE_TYPE (BT_I1, builtin_type_for_size (BITS_PER_UNIT*1, 1))\n DEF_PRIMITIVE_TYPE (BT_I2, builtin_type_for_size (BITS_PER_UNIT*2, 1))\n DEF_PRIMITIVE_TYPE (BT_I4, builtin_type_for_size (BITS_PER_UNIT*4, 1))\n@@ -425,6 +427,7 @@ DEF_FUNCTION_TYPE_1 (BT_FN_UINT16_UINT32, BT_UINT16, BT_UINT32)\n DEF_FUNCTION_TYPE_1 (BT_FN_UINT32_UINT16, BT_UINT32, BT_UINT16)\n DEF_FUNCTION_TYPE_1 (BT_FN_INT_FENV_T_PTR, BT_INT, BT_FENV_T_PTR)\n DEF_FUNCTION_TYPE_1 (BT_FN_INT_CONST_FENV_T_PTR, BT_INT, BT_CONST_FENV_T_PTR)\n+DEF_FUNCTION_TYPE_1 (BT_FN_CALL_INFO_PTR, BT_CALL_INFO, BT_PTR)\n \n DEF_POINTER_TYPE (BT_PTR_FN_VOID_PTR, BT_FN_VOID_PTR)\n \ndiff --git a/gcc/builtins.cc b/gcc/builtins.cc\nindex 692e20088c2..efef8afce69 100644\n--- a/gcc/builtins.cc\n+++ b/gcc/builtins.cc\n@@ -8093,6 +8093,9 @@ expand_builtin (tree exp, rtx target, rtx subtarget, machine_mode mode,\n \texpand_builtin_return (expand_normal (CALL_EXPR_ARG (exp, 0)));\n       return const0_rtx;\n \n+    case BUILT_IN_CALL_INFO:\n+      gcc_unreachable ();\n+\n     case BUILT_IN_SAVEREGS:\n       return expand_builtin_saveregs ();\n \n@@ -12311,6 +12314,7 @@ is_simple_builtin (tree decl)\n       case BUILT_IN_STACK_SAVE:\n       case BUILT_IN_STACK_RESTORE:\n       case BUILT_IN_DWARF_CFA:\n+      case BUILT_IN_CALL_INFO:\n \t/* Exception state returns or moves registers around.  */\n       case BUILT_IN_EH_FILTER:\n       case BUILT_IN_EH_POINTER:\ndiff --git a/gcc/builtins.def b/gcc/builtins.def\nindex 8ab0599b17f..1d5bff152e3 100644\n--- a/gcc/builtins.def\n+++ b/gcc/builtins.def\n@@ -1164,6 +1164,9 @@ DEF_BUILTIN_STUB (BUILT_IN_NONLOCAL_GOTO, \"__builtin_nonlocal_goto\")\n DEF_EXT_LIB_BUILTIN (BUILT_IN_GCC_NESTED_PTR_CREATED, \"__gcc_nested_func_ptr_created\", BT_FN_VOID_PTR_PTR_PTR, ATTR_NOTHROW_LIST)\n DEF_EXT_LIB_BUILTIN (BUILT_IN_GCC_NESTED_PTR_DELETED, \"__gcc_nested_func_ptr_deleted\", BT_FN_VOID, ATTR_NOTHROW_LIST)\n \n+/* Information needed to call (nested) functions.  */\n+DEF_GCC_BUILTIN (BUILT_IN_CALL_INFO, \"call_info\", BT_FN_CALL_INFO_PTR, ATTR_NULL)\n+\n /* Implementing __builtin_setjmp.  */\n DEF_BUILTIN_STUB (BUILT_IN_SETJMP_SETUP, \"__builtin_setjmp_setup\")\n DEF_BUILTIN_STUB (BUILT_IN_SETJMP_RECEIVER, \"__builtin_setjmp_receiver\")\ndiff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi\nindex 47b0bdf1340..2ddcd1f30d6 100644\n--- a/gcc/doc/extend.texi\n+++ b/gcc/doc/extend.texi\n@@ -16610,6 +16610,19 @@ This builtin can be used to call Go closures from C.\n \n @enddefbuiltin\n \n+@defbuiltin{@var{type} __builtin_call_info (@var{pointer_exp})}\n+\n+The @var{pointer_exp} expression must designate a function.\n+The result is a structure with two members of pointer type named @code{code}\n+and @code{chain}.  @code{code} holds the static address of the function.\n+For a nested function, the address represents the address of the underlying\n+machine code and not of a trampoline that would otherwise be generated to\n+setup the static chain.  @code{chain} is the chain pointer that that is\n+needed to call the function call in its current context, or a null pointer\n+if none is needed.\n+\n+@enddefbuiltin\n+\n @node Return Address\n @section Getting the Return or Frame Address of a Function\n \ndiff --git a/gcc/gimple-fold.cc b/gcc/gimple-fold.cc\nindex e9019e2c7bd..d8eceb080be 100644\n--- a/gcc/gimple-fold.cc\n+++ b/gcc/gimple-fold.cc\n@@ -5354,6 +5354,41 @@ gimple_fold_builtin_stdarg (gimple_stmt_iterator *gsi, gcall *call)\n     }\n }\n \n+/* Fold __builtin_call_info builtin.  This handles only the trivial\n+   left-over cases not processed in tree-nested.cc.  */\n+\n+static bool\n+gimple_fold_builtin_call_info (gimple_stmt_iterator *gsi)\n+{\n+  gcall *stmt = as_a <gcall *>(gsi_stmt (*gsi));\n+  tree arg = gimple_call_arg (stmt, 0);\n+\n+  if (TREE_CODE (arg) != ADDR_EXPR || !DECL_P (TREE_OPERAND (arg, 0))\n+      || FUNCTION_DECL != TREE_CODE (TREE_OPERAND (arg, 0)))\n+    {\n+      error_at (gimple_location (stmt),\n+\t\t\"argument to %<__builtin_call_info%> must be a function\");\n+      return false;\n+    }\n+\n+  /* The case with static chain is handled in tree-nested.cc.  */\n+  gcc_assert (!DECL_STATIC_CHAIN (TREE_OPERAND (arg, 0)));\n+\n+  tree fields = TYPE_FIELDS (call_info_type_node);\n+  tree ret = create_tmp_var (call_info_type_node);\n+  tree cref2 = build3 (COMPONENT_REF, TREE_TYPE (fields),\n+\t\t       ret, fields, NULL_TREE);\n+  tree cref1 = build3 (COMPONENT_REF, TREE_TYPE (TREE_CHAIN (fields)),\n+\t\t       ret, TREE_CHAIN (fields), NULL_TREE);\n+  gimple *g = gimple_build_assign (cref1, null_pointer_node);\n+  gsi_insert_before (gsi, g, GSI_SAME_STMT);\n+  g = gimple_build_assign (cref2, arg);\n+  gsi_insert_before (gsi, g, GSI_SAME_STMT);\n+\n+  replace_call_with_value (gsi, ret);\n+  return true;\n+}\n+\n /* Fold the non-target builtin at *GSI and return whether any simplification\n    was made.  */\n \n@@ -5530,6 +5565,9 @@ gimple_fold_builtin (gimple_stmt_iterator *gsi)\n     case BUILT_IN_CONSTANT_P:\n       return gimple_fold_builtin_constant_p (gsi);\n \n+    case BUILT_IN_CALL_INFO:\n+      return gimple_fold_builtin_call_info (gsi);\n+\n     default:;\n     }\n \ndiff --git a/gcc/testsuite/gcc.dg/builtin-call-info-1.c b/gcc/testsuite/gcc.dg/builtin-call-info-1.c\nnew file mode 100644\nindex 00000000000..24ac8b1ccf2\n--- /dev/null\n+++ b/gcc/testsuite/gcc.dg/builtin-call-info-1.c\n@@ -0,0 +1,26 @@\n+/* { dg-do run } */\n+/* { dg-options \"-Wtrampolines\" } */\n+\n+typedef typeof(__builtin_call_info(nullptr)) call_info_t;\n+\n+int apply(call_info_t c, int value)\n+{\n+\treturn __builtin_call_with_static_chain(((int(*)(int))c.code)(value), c.chain);\n+}\n+\n+int foo(int x)\n+{\n+\tint add(int y)\n+\t{\n+\t\treturn x + y;\n+\t}\n+\n+\treturn apply(__builtin_call_info(add), x);\n+}\n+\n+int main()\n+{\n+\tif (4 != foo(2))\n+\t\t__builtin_abort();\n+}\n+\ndiff --git a/gcc/testsuite/gcc.dg/builtin-call-info-2.c b/gcc/testsuite/gcc.dg/builtin-call-info-2.c\nnew file mode 100644\nindex 00000000000..0ec8d865971\n--- /dev/null\n+++ b/gcc/testsuite/gcc.dg/builtin-call-info-2.c\n@@ -0,0 +1,49 @@\n+/* { dg-do run } */\n+/* { dg-options \"-Wtrampolines\" } */\n+\n+/* Check that we get the expected pointers in\n+   different context.  */\n+\n+int f(int x)\n+{\n+\tstatic typeof(__builtin_call_info(f)) c;\n+\tc = __builtin_call_info(f);\n+\n+\tif (f != c.code)\n+\t\t__builtin_abort();\n+\n+\tif ((void*)0 != c.chain)\n+\t\t__builtin_abort();\n+\n+\tint g(int y)\n+\t{\n+\t\tauto c2 = __builtin_call_info(g);\n+\n+\t\tif (c.code != c2.code)\n+\t\t\t__builtin_abort();\n+\t\t\n+\t\tif (c.chain != c2.chain)\n+\t\t\t__builtin_abort();\n+\t\n+\t\treturn x + y; \n+\t}\n+\n+\tc = __builtin_call_info(g);\n+\n+\treturn g(x);\n+}\n+\n+int main()\n+{\n+\tauto c = __builtin_call_info(f);\n+\n+\tif (f != c.code)\n+\t\t__builtin_abort();\n+\n+\tif ((void*)0 != c.chain)\n+\t\t__builtin_abort();\n+\n+\tif (6 != f(3))\n+\t\t__builtin_abort();\n+}\n+\ndiff --git a/gcc/testsuite/gcc.dg/builtin-call-info-3.c b/gcc/testsuite/gcc.dg/builtin-call-info-3.c\nnew file mode 100644\nindex 00000000000..5d78892fefb\n--- /dev/null\n+++ b/gcc/testsuite/gcc.dg/builtin-call-info-3.c\n@@ -0,0 +1,24 @@\n+/* { dg-do compile } */\n+/* { dg-options \"-O2 -Wtrampolines -Wreturn-local-addr\" } */\n+\n+\n+void * foo(int n)\n+{\n+\tint g(int x)\n+\t{\n+\t\treturn n + x;\n+\t}\n+\n+\treturn __builtin_call_info(g).code;\t// ok\n+}\n+\n+void * bar(int n)\n+{\n+\tint g(int x)\n+\t{\n+\t\treturn n + x;\n+\t}\n+\n+\treturn __builtin_call_info(g).chain;\t/* { dg-warning \"returns address of local variable\" } */\n+}\n+\ndiff --git a/gcc/tree-core.h b/gcc/tree-core.h\nindex 07e9318f5e8..0a3740b060a 100644\n--- a/gcc/tree-core.h\n+++ b/gcc/tree-core.h\n@@ -822,6 +822,7 @@ enum tree_index : unsigned {\n   TI_FEXCEPT_T_PTR_TYPE,\n   TI_CONST_FEXCEPT_T_PTR_TYPE,\n   TI_POINTER_SIZED_TYPE,\n+  TI_CALL_INFO_TYPE,\n \n   TI_DFLOAT32_TYPE,\n   TI_DFLOAT64_TYPE,\ndiff --git a/gcc/tree-inline.cc b/gcc/tree-inline.cc\nindex 087fcc8a8b8..8f5701ab693 100644\n--- a/gcc/tree-inline.cc\n+++ b/gcc/tree-inline.cc\n@@ -3760,8 +3760,8 @@ initialize_inlined_parameters (copy_body_data *id, gimple *stmt,\n   gcc_assert (fn != current_function_decl);\n   if (p)\n     {\n-      /* No static chain?  Seems like a bug in tree-nested.cc.  */\n-      gcc_assert (static_chain);\n+      if (!static_chain)\n+\terror (\"called function requires a static chain\");\n \n       setup_one_parameter (id, p, static_chain, fn, bb, &vars);\n     }\ndiff --git a/gcc/tree-nested.cc b/gcc/tree-nested.cc\nindex cdccc51d33e..0b0affe9a44 100644\n--- a/gcc/tree-nested.cc\n+++ b/gcc/tree-nested.cc\n@@ -36,6 +36,7 @@\n #include \"gimplify.h\"\n #include \"gimple-iterator.h\"\n #include \"gimple-walk.h\"\n+#include \"gimple-fold.h\"\n #include \"tree-cfg.h\"\n #include \"explow.h\"\n #include \"langhooks.h\"\n@@ -2882,6 +2883,11 @@ convert_tramp_reference_stmt (gimple_stmt_iterator *gsi, bool *handled_ops_p,\n     {\n     case GIMPLE_CALL:\n       {\n+\ttree decl = gimple_call_fndecl (stmt);\n+\tif (decl && fndecl_built_in_p (decl, BUILT_IN_NORMAL)\n+\t    && BUILT_IN_CALL_INFO == DECL_FUNCTION_CODE (decl))\n+\t  break;\n+\n \t/* Only walk call arguments, lest we generate trampolines for\n \t   direct calls.  */\n \tunsigned long i, nargs = gimple_call_num_args (stmt);\n@@ -2994,11 +3000,52 @@ convert_gimple_call (gimple_stmt_iterator *gsi, bool *handled_ops_p,\n   switch (gimple_code (stmt))\n     {\n     case GIMPLE_CALL:\n-      if (gimple_call_chain (stmt))\n-\tbreak;\n       decl = gimple_call_fndecl (stmt);\n       if (!decl)\n \tbreak;\n+      if (fndecl_built_in_p (decl, BUILT_IN_NORMAL)\n+\t  && DECL_FUNCTION_CODE (decl) == BUILT_IN_CALL_INFO)\n+\t{\n+\t  tree d = gimple_call_arg (stmt, 0);\n+\t  tree ret1 = null_pointer_node;\n+\t  tree ret2 = null_pointer_node;\n+\t  if (TREE_CODE (d) != ADDR_EXPR || !DECL_P (TREE_OPERAND (d, 0))\n+\t      || FUNCTION_DECL != TREE_CODE (TREE_OPERAND (d, 0)))\n+\t    {\n+\t      error_at (gimple_location (stmt),\n+\t\t\t\"argument to %<__builtin_call_info%> \"\n+\t\t\t\"must be a function\");\n+\t    }\n+\t  else\n+\t    {\n+\t      decl = TREE_OPERAND (d, 0);\n+\t      target_context = decl_function_context (decl);\n+\t      if (target_context && DECL_STATIC_CHAIN (decl))\n+\t\t{\n+\t\t  /* Return static chain.  */\n+\t\t  info->static_chain_added\n+\t\t    |= (1 << (info->context != target_context));\n+\t\t  ret1 = get_static_chain (info, target_context, &wi->gsi);\n+\t\t}\n+\t      /* Return code pointer.  */\n+\t      ret2 = build_addr (TREE_OPERAND (d, 0));\n+\t      TREE_NO_TRAMPOLINE (ret2) = 1;\n+\t    }\n+\t  tree fields = TYPE_FIELDS (call_info_type_node);\n+\t  tree ret = create_tmp_var_for (info, call_info_type_node, NULL);\n+\t  tree cref2 = build3 (COMPONENT_REF, TREE_TYPE (fields),\n+\t\t\t       ret, fields, NULL_TREE);\n+\t  tree cref1 = build3 (COMPONENT_REF, TREE_TYPE (TREE_CHAIN (fields)),\n+\t\t\t       ret, TREE_CHAIN (fields), NULL_TREE);\n+\t  gimple *assign = gimple_build_assign (cref1, ret1);\n+\t  gsi_insert_before (gsi, assign, GSI_SAME_STMT);\n+\t  assign = gimple_build_assign (cref2, ret2);\n+\t  gsi_insert_before (gsi, assign, GSI_SAME_STMT);\n+\t  replace_call_with_value (gsi, ret);\n+\t  break;\n+\t}\n+      if (gimple_call_chain (stmt))\n+\tbreak;\n       target_context = decl_function_context (decl);\n       if (target_context && DECL_STATIC_CHAIN (decl))\n \t{\ndiff --git a/gcc/tree.cc b/gcc/tree.cc\nindex d0e745e8d28..38d0590a8c7 100644\n--- a/gcc/tree.cc\n+++ b/gcc/tree.cc\n@@ -9984,6 +9984,18 @@ build_common_tree_nodes (bool signed_char)\n     va_list_type_node = t;\n   }\n \n+  /* Create the call info type.  */\n+  call_info_type_node = make_node (RECORD_TYPE);\n+  tree code_field = build_decl (BUILTINS_LOCATION, FIELD_DECL,\n+\t\t\t\tget_identifier (\"code\"), ptr_type_node);\n+  DECL_FIELD_CONTEXT (code_field) = call_info_type_node;\n+  tree data_field = build_decl (BUILTINS_LOCATION, FIELD_DECL,\n+\t\t\t\tget_identifier (\"chain\"), ptr_type_node);\n+  DECL_FIELD_CONTEXT (data_field) = call_info_type_node;\n+  TREE_CHAIN (code_field) = data_field;\n+  TYPE_FIELDS (call_info_type_node) = code_field;\n+  layout_type (call_info_type_node);\n+\n   /* SCEV analyzer global shared trees.  */\n   chrec_dont_know = make_node (SCEV_NOT_KNOWN);\n   TREE_TYPE (chrec_dont_know) = void_type_node;\ndiff --git a/gcc/tree.h b/gcc/tree.h\nindex 19bc67718d1..c63898f672c 100644\n--- a/gcc/tree.h\n+++ b/gcc/tree.h\n@@ -4665,6 +4665,7 @@ tree_strip_any_location_wrapper (tree exp)\n #define fexcept_t_ptr_type_node\t\tglobal_trees[TI_FEXCEPT_T_PTR_TYPE]\n #define const_fexcept_t_ptr_type_node\tglobal_trees[TI_CONST_FEXCEPT_T_PTR_TYPE]\n #define pointer_sized_int_node\t\tglobal_trees[TI_POINTER_SIZED_TYPE]\n+#define call_info_type_node\t\tglobal_trees[TI_CALL_INFO_TYPE]\n \n #define boolean_type_node\t\tglobal_trees[TI_BOOLEAN_TYPE]\n #define boolean_false_node\t\tglobal_trees[TI_BOOLEAN_FALSE]\n","prefixes":["RFC","C","v2"]}