get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/2217908/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 2217908,
    "url": "http://patchwork.ozlabs.org/api/patches/2217908/?format=api",
    "web_url": "http://patchwork.ozlabs.org/project/gcc/patch/c034d08dd62cbb105df9131cec9f72835bbbaa51.camel@tugraz.at/",
    "project": {
        "id": 17,
        "url": "http://patchwork.ozlabs.org/api/projects/17/?format=api",
        "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,
        "list_archive_url": "",
        "list_archive_url_format": "",
        "commit_url_format": ""
    },
    "msgid": "<c034d08dd62cbb105df9131cec9f72835bbbaa51.camel@tugraz.at>",
    "list_archive_url": null,
    "date": "2026-03-30T21:56:29",
    "name": "[RFC,C] Built-ins to access code pointer and static chain of nested function",
    "commit_ref": null,
    "pull_url": null,
    "state": "new",
    "archived": false,
    "hash": "076a3437ecac1c023f08335300cf1bc6edfa4f50",
    "submitter": {
        "id": 85465,
        "url": "http://patchwork.ozlabs.org/api/people/85465/?format=api",
        "name": "Martin Uecker",
        "email": "uecker@tugraz.at"
    },
    "delegate": null,
    "mbox": "http://patchwork.ozlabs.org/project/gcc/patch/c034d08dd62cbb105df9131cec9f72835bbbaa51.camel@tugraz.at/mbox/",
    "series": [
        {
            "id": 498102,
            "url": "http://patchwork.ozlabs.org/api/series/498102/?format=api",
            "web_url": "http://patchwork.ozlabs.org/project/gcc/list/?series=498102",
            "date": "2026-03-30T21:56:29",
            "name": "[RFC,C] Built-ins to access code pointer and static chain of nested function",
            "version": 1,
            "mbox": "http://patchwork.ozlabs.org/series/498102/mbox/"
        }
    ],
    "comments": "http://patchwork.ozlabs.org/api/patches/2217908/comments/",
    "check": "pending",
    "checks": "http://patchwork.ozlabs.org/api/patches/2217908/checks/",
    "tags": {},
    "related": [],
    "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=eROuQEmu;\n\tdkim-atps=neutral",
            "legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org\n (client-ip=2620:52:6:3111::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=eROuQEmu",
            "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\n [IPv6:2620:52:6:3111::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 4fl4q109fkz1yCp\n\tfor <incoming@patchwork.ozlabs.org>; Tue, 31 Mar 2026 08:57:16 +1100 (AEDT)",
            "from vm01.sourceware.org (localhost [127.0.0.1])\n\tby sourceware.org (Postfix) with ESMTP id 63D934B920AD\n\tfor <incoming@patchwork.ozlabs.org>; Mon, 30 Mar 2026 21:57:14 +0000 (GMT)",
            "from mailrelay.tugraz.at (mailrelay.tugraz.at [129.27.2.202])\n by sourceware.org (Postfix) with ESMTPS id 668CA4BA2E36\n for <gcc-patches@gcc.gnu.org>; Mon, 30 Mar 2026 21:56:41 +0000 (GMT)",
            "from vra-171-236.tugraz.at (vra-171-236.tugraz.at [129.27.171.236])\n by mailrelay.tugraz.at (Postfix) with ESMTPSA id 4fl4p61GyRz9rxF;\n Mon, 30 Mar 2026 23:56:30 +0200 (CEST)"
        ],
        "DKIM-Filter": [
            "OpenDKIM Filter v2.11.0 sourceware.org 63D934B920AD",
            "OpenDKIM Filter v2.11.0 sourceware.org 668CA4BA2E36"
        ],
        "DMARC-Filter": "OpenDMARC Filter v1.4.2 sourceware.org 668CA4BA2E36",
        "ARC-Filter": "OpenARC Filter v1.0.0 sourceware.org 668CA4BA2E36",
        "ARC-Seal": "i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1774907801; cv=none;\n b=a9DvqFoIkPSv3pex1EbHmt9amxNjrp1co/m8wm2mwRIS12TvmCpHZ8WLfsLyZlF5+jaogLIQWDwX4WxrZ5zG53PV6h16IzNGzjV8TEkCBO2R3faxBnRnfjh4RSig4yTwSnYdA9pFahBlXTU7SqppPQh1CCWL4ZTeuRxEALQnuAE=",
        "ARC-Message-Signature": "i=1; a=rsa-sha256; d=sourceware.org; s=key;\n t=1774907801; c=relaxed/simple;\n bh=tvxb5t6E+AnB3qULwp4ZEbYTgLL24Uv0iQxPNwkjOBY=;\n h=DKIM-Signature:Message-ID:Subject:From:To:Date:MIME-Version;\n b=lOnwL3TygiCJieBkYb02Tx2D3bvpD0W4HQrTz3BJkh+/TwigiCvkU87/EO9GB5hFE8QXBSLWysgRKx7kpyx3EuPcfdGYZppUDGG7IzL2nFUWHKIZrnolkrUs8UNqglegKR8teIuxTRhVxxBeryghKznrdBK3UWkq0Kperppry0Q=",
        "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=1774907790;\n bh=oaQr7VFpAJ/NNqd/2pF0EE7Lk831KzRfnTU+Dz3Qr6A=;\n h=Subject:From:To:Cc:Date;\n b=eROuQEmuRAP7XgozqnaxhZl6Z7BXKj5ke7H45Jm3Jc4YEmPjvk/UnGPs95D5z+nSU\n zgf0Yz71pT94AFzur8EawqgDHh3QtYbL/M7hiCb5lEdtkEujRwAuwFH064ft7FBoK9\n wRuDO3w4cjOTJGJoIrTeQ42ixtNYIYGpV1LaN/9k=",
        "Message-ID": "<c034d08dd62cbb105df9131cec9f72835bbbaa51.camel@tugraz.at>",
        "Subject": "[RFC C PATCH] Built-ins to access code pointer and static chain of\n 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>",
        "Date": "Mon, 30 Mar 2026 23:56:29 +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": "Here is a patch (stage1) for a feature which I think we have been\nmissing for a long time as we have __builtin_call_with_static_chain\nto call into other languages but no way to use it for nested\nfunctions in C (as a hack, one could extract the address from the\ntrampoline).\n\n\nMy personal motivation is mostly to get around the problem that we\ncurrently cannot \"devirtualize\" nested functions (PR49666) which\nprevents important optimizations for my main use case (higher-order\nloop kernels in numerics). With this patch this works beautifully,\ne.g. the function \"foo\" in  the first test is compiled to\n\nfoo:\n        leal    (%rdi,%rdi), %eax\n        ret\n\nwhich is perfect as all the overhead has completely disappeared.\n\nBut the built-ins also allows one to get get rid of the trampolines\nmore generally.\n\nThe implementation is relatively straightforward. The built-ins\nare expanded in tree-nested.cc and either disable trampoline\ngeneration to return the code address or return the static chain.\n\nBootstrapped and regression tested on x86_64.\n\nMartin\n\n\n\n    c: Built-ins to access code pointer and static chain of nested function.\n    \n    This patch adds two new built-ins, __builtin_nested_code and\n    __builtin_nested_chain, to extract the code pointer and the static\n    chain pointer from a nested function.  Those can then be used to\n    call the nested function using the existing built-in\n    __builtin_call_with_static_chain.  This 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-ins.\n            * builtins.cc (expand_builtin): Trivially expand built-ins.\n            (is_simple_builtin): Add new built-ins.\n            * tree-nested.cc (convert_tramp_reference_stmt): Ingore\n            new built-ins.\n            (convert_gimple_call): Expand built-ins.\n            * tree-inline.cc (initialize_inlined_parameters): Replace\n            assertion with error.\n    \n    gcc/doc/ChangeLog:\n            * extend.texi: Document new built-ins.\n    \n    gcc/testsuite/ChangeLog:\n            * gcc.dg/builtin-nested-1.c: New test.\n            * gcc.dg/builtin-nested-2.c: New test.\n            * gcc.dg/builtin-nested-3.c: New test.",
    "diff": "diff --git a/gcc/builtins.cc b/gcc/builtins.cc\nindex 692e20088c2..e12790ca0b0 100644\n--- a/gcc/builtins.cc\n+++ b/gcc/builtins.cc\n@@ -8093,6 +8093,14 @@ 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_NESTED_CODEPTR:\n+      /* If not expanded in tree-nested.cc.  */\n+      return expand_normal (CALL_EXPR_ARG (exp, 0));\n+\n+    case BUILT_IN_NESTED_CHAIN:\n+      /* If not expanded in tree-nested.cc.  */\n+      return const0_rtx;\n+\n     case BUILT_IN_SAVEREGS:\n       return expand_builtin_saveregs ();\n \n@@ -12311,6 +12319,8 @@ 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_NESTED_CODEPTR:\n+      case BUILT_IN_NESTED_CHAIN:\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..817c39ecd94 100644\n--- a/gcc/builtins.def\n+++ b/gcc/builtins.def\n@@ -1164,6 +1164,10 @@ 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+/* Using nested functions.  */\n+DEF_GCC_BUILTIN (BUILT_IN_NESTED_CHAIN, \"nested_chain\", BT_FN_PTR_PTR, ATTR_NULL)\n+DEF_GCC_BUILTIN (BUILT_IN_NESTED_CODEPTR, \"nested_codeptr\", BT_FN_PTR_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..8fbb088226f 100644\n--- a/gcc/doc/extend.texi\n+++ b/gcc/doc/extend.texi\n@@ -16610,6 +16610,25 @@ This builtin can be used to call Go closures from C.\n \n @enddefbuiltin\n \n+@defbuiltin{@var{type} __builtin_nested_chain (@var{pointer_exp})}\n+\n+The @var{pointer_exp} expression must designate a function.\n+The result is the static chain pointer that that is needed to call\n+the function call in its current context, or a null pointer if none\n+is needed.\n+\n+@enddefbuiltin\n+\n+@defbuiltin{@var{type} __builtin_nested_codeptr (@var{pointer_exp})}\n+\n+The @var{pointer_exp} expression must designate a function.\n+The result is the static address of the function.  For a nested function,\n+the address represents the address of the underlying machine code and\n+not of a trampoline that would otherwise be generated to setup the\n+static chain.\n+\n+@enddefbuiltin\n+\n @node Return Address\n @section Getting the Return or Frame Address of a Function\n \ndiff --git a/gcc/testsuite/gcc.dg/builtin-nested-1.c b/gcc/testsuite/gcc.dg/builtin-nested-1.c\nnew file mode 100644\nindex 00000000000..d1e887357b1\n--- /dev/null\n+++ b/gcc/testsuite/gcc.dg/builtin-nested-1.c\n@@ -0,0 +1,34 @@\n+/* { dg-do run } */\n+/* { dg-options \"-Wtrampolines\" } */\n+\n+struct closure {\n+\tint (*fun)(int);\n+\tvoid *data;\n+};\n+\n+int apply(struct closure c, int value)\n+{\n+\treturn __builtin_call_with_static_chain(c.fun(value), c.data);\n+}\n+\n+int foo(int x)\n+{\n+\tint add(int y)\n+\t{\n+\t\treturn x + y;\n+\t}\n+\n+\tstruct closure c = { \n+\t\t__builtin_nested_codeptr(add),\n+\t\t__builtin_nested_chain(add),\n+\t};\n+\n+\treturn apply(c, 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-nested-2.c b/gcc/testsuite/gcc.dg/builtin-nested-2.c\nnew file mode 100644\nindex 00000000000..af2cd207149\n--- /dev/null\n+++ b/gcc/testsuite/gcc.dg/builtin-nested-2.c\n@@ -0,0 +1,46 @@\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 void *chain, *code;\n+\n+\tif (f != __builtin_nested_codeptr(f))\n+\t\t__builtin_abort();\n+\n+\tif ((void*)0 != __builtin_nested_chain(f))\n+\t\t__builtin_abort();\n+\n+\n+\tint g(int y)\n+\t{\n+\t\tif (code != __builtin_nested_codeptr(g))\n+\t\t\t__builtin_abort();\n+\t\t\n+\t\tif (chain != __builtin_nested_chain(g))\n+\t\t\t__builtin_abort();\n+\t\n+\t\treturn x + y; \n+\t}\n+\n+\tchain = __builtin_nested_chain(g);\n+\tcode = __builtin_nested_codeptr(g);\n+\n+\treturn g(x);\n+}\n+\n+int main()\n+{\n+\tif (f != __builtin_nested_codeptr(f))\n+\t\t__builtin_abort();\n+\n+\tif ((void*)0 != __builtin_nested_chain(f))\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-nested-3.c b/gcc/testsuite/gcc.dg/builtin-nested-3.c\nnew file mode 100644\nindex 00000000000..2423cca737f\n--- /dev/null\n+++ b/gcc/testsuite/gcc.dg/builtin-nested-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_nested_codeptr(g);\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_nested_chain(g);\t/* { dg-warning \"returns address of local variable\" } */\n+}\n+\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..0be2b5840ef 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,12 @@ 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_NESTED_CHAIN == DECL_FUNCTION_CODE (decl)\n+\t\t|| BUILT_IN_NESTED_CODEPTR == 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 +3001,47 @@ 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_NESTED_CODEPTR\n+\t      || DECL_FUNCTION_CODE (decl) == BUILT_IN_NESTED_CHAIN))\n+\t{\n+\t  bool chain_p = DECL_FUNCTION_CODE (decl) == BUILT_IN_NESTED_CHAIN;\n+\t  tree d = gimple_call_arg (stmt, 0);\n+\t  tree ret = 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\tchain_p ? \"argument to %<__builtin_nested_chain%>\"\n+\t\t\t\t  \" must be a function\"\n+\t\t\t\t: \"argument to %<__builtin_nested_codeptr%>\"\n+\t\t\t\t  \" must be a function\");\n+\t    }\n+\t  else if (chain_p)\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 |= (1 << (info->context != target_context));\n+\t\t  ret = get_static_chain (info, target_context, &wi->gsi);\n+\t\t}\n+\t    }\n+\t  else\n+\t    {\n+\t      /* Return code pointer.  */\n+\t      ret = build_addr (TREE_OPERAND (d, 0));\n+\t      TREE_NO_TRAMPOLINE (ret) = 1;\n+\t    }\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{\n",
    "prefixes": [
        "RFC",
        "C"
    ]
}