get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

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

{
    "id": 2229950,
    "url": "http://patchwork.ozlabs.org/api/1.1/patches/2229950/?format=api",
    "web_url": "http://patchwork.ozlabs.org/project/gcc/patch/20260428232816.1953994-1-dmalcolm@redhat.com/",
    "project": {
        "id": 17,
        "url": "http://patchwork.ozlabs.org/api/1.1/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
    },
    "msgid": "<20260428232816.1953994-1-dmalcolm@redhat.com>",
    "date": "2026-04-28T23:28:16",
    "name": "[pushed:,r17-184] analyzer: split setjmp/longjmp implementation to their own file",
    "commit_ref": null,
    "pull_url": null,
    "state": "new",
    "archived": false,
    "hash": "4a40d3316fab69f47712b934ede4e48f225a130d",
    "submitter": {
        "id": 24465,
        "url": "http://patchwork.ozlabs.org/api/1.1/people/24465/?format=api",
        "name": "David Malcolm",
        "email": "dmalcolm@redhat.com"
    },
    "delegate": null,
    "mbox": "http://patchwork.ozlabs.org/project/gcc/patch/20260428232816.1953994-1-dmalcolm@redhat.com/mbox/",
    "series": [
        {
            "id": 501959,
            "url": "http://patchwork.ozlabs.org/api/1.1/series/501959/?format=api",
            "web_url": "http://patchwork.ozlabs.org/project/gcc/list/?series=501959",
            "date": "2026-04-28T23:28:16",
            "name": "[pushed:,r17-184] analyzer: split setjmp/longjmp implementation to their own file",
            "version": 1,
            "mbox": "http://patchwork.ozlabs.org/series/501959/mbox/"
        }
    ],
    "comments": "http://patchwork.ozlabs.org/api/patches/2229950/comments/",
    "check": "pending",
    "checks": "http://patchwork.ozlabs.org/api/patches/2229950/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=redhat.com header.i=@redhat.com header.a=rsa-sha256\n header.s=mimecast20190719 header.b=Dlgy8Mk6;\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=redhat.com header.i=@redhat.com header.a=rsa-sha256\n header.s=mimecast20190719 header.b=Dlgy8Mk6",
            "sourceware.org; dmarc=pass (p=quarantine dis=none)\n header.from=redhat.com",
            "sourceware.org; spf=pass smtp.mailfrom=redhat.com",
            "server2.sourceware.org;\n arc=none smtp.remote-ip=170.10.129.124"
        ],
        "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 4g4xWj68sxz1yHX\n\tfor <incoming@patchwork.ozlabs.org>; Wed, 29 Apr 2026 09:30:57 +1000 (AEST)",
            "from vm01.sourceware.org (localhost [127.0.0.1])\n\tby sourceware.org (Postfix) with ESMTP id D69424BBC0FD\n\tfor <incoming@patchwork.ozlabs.org>; Tue, 28 Apr 2026 23:30:55 +0000 (GMT)",
            "from us-smtp-delivery-124.mimecast.com\n (us-smtp-delivery-124.mimecast.com [170.10.129.124])\n by sourceware.org (Postfix) with ESMTP id 26FD44BBC0F4\n for <gcc-patches@gcc.gnu.org>; Tue, 28 Apr 2026 23:28:21 +0000 (GMT)",
            "from mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com\n (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by\n relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3,\n cipher=TLS_AES_256_GCM_SHA384) id us-mta-611-4sn4y4sBNTWLsGjGusp57Q-1; Tue,\n 28 Apr 2026 19:28:19 -0400",
            "from mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com\n (mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.111])\n (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest\n SHA256)\n (No client certificate requested)\n by mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS\n id 7B5BD1956050\n for <gcc-patches@gcc.gnu.org>; Tue, 28 Apr 2026 23:28:18 +0000 (UTC)",
            "from t14s.localdomain.com (unknown [10.22.65.168])\n by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP\n id BE28C180045E; Tue, 28 Apr 2026 23:28:17 +0000 (UTC)"
        ],
        "DKIM-Filter": [
            "OpenDKIM Filter v2.11.0 sourceware.org D69424BBC0FD",
            "OpenDKIM Filter v2.11.0 sourceware.org 26FD44BBC0F4"
        ],
        "DMARC-Filter": "OpenDMARC Filter v1.4.2 sourceware.org 26FD44BBC0F4",
        "ARC-Filter": "OpenARC Filter v1.0.0 sourceware.org 26FD44BBC0F4",
        "ARC-Seal": "i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1777418901; cv=none;\n b=pPdtjgLQ03CfXF5/JcMSSTlZM2bvIJe3Vsk8tOx/bDm6r02AzNP32Vr+IBmyjBTrqx0XsBvfPXxNnBaJyLvFw6wiQRJBMEVq8hWMvn/k7q1tohHRdCHNHId62nnOylUNCXhpSwE5nrL9KtxBbyGxGizLC3ygHkaDjShPR9gOZpM=",
        "ARC-Message-Signature": "i=1; a=rsa-sha256; d=sourceware.org; s=key;\n t=1777418901; c=relaxed/simple;\n bh=WRuSDY9HNuAvLDg9PZFnSbxm0+jUj1fuqiNlTEx/MpQ=;\n h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version;\n b=eSUc8Ci/hpT7GbDhW9EioUMk8NCn7MQYIqOR0YTn64TUQwXmV0QYcG3FroPgpZUsrqwV22eKXYTUgED1UcsudZfV/a74F2DXVYUfth603ZfGhxYNAeGE1rsDmRZpTMAHXN38za8TWAcFhzTibsG1MTSg0CksQ3Bq/w80KygLmgM=",
        "ARC-Authentication-Results": "i=1; server2.sourceware.org",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com;\n s=mimecast20190719; t=1777418900;\n h=from:from:reply-to:subject:subject:date:date:message-id:message-id:\n to:to:cc:cc:mime-version:mime-version:content-type:content-type:\n content-transfer-encoding:content-transfer-encoding;\n bh=D+ERazVK8LcBl106xZNcEcilazFDnq5v2tA0um+5Y7g=;\n b=Dlgy8Mk6dLDLeXV7T4UV/PrGa/ETkh1fKluWuF6oCN8Z4eAYWVha408f9CA7Oxv86SGSFl\n HNL/EXn6kHji6tyo/nEiF/wICQHYZl2lKdijOYMZg8LIlTybkSPg0BjJtsagBEPtQDxKMP\n xddwY1vNw8e6YZOJUnhH0orXyfEbvBw=",
        "X-MC-Unique": "4sn4y4sBNTWLsGjGusp57Q-1",
        "X-Mimecast-MFC-AGG-ID": "4sn4y4sBNTWLsGjGusp57Q_1777418898",
        "From": "David Malcolm <dmalcolm@redhat.com>",
        "To": "gcc-patches@gcc.gnu.org",
        "Cc": "David Malcolm <dmalcolm@redhat.com>",
        "Subject": "[pushed: r17-184] analyzer: split setjmp/longjmp implementation to\n their own file",
        "Date": "Tue, 28 Apr 2026 19:28:16 -0400",
        "Message-ID": "<20260428232816.1953994-1-dmalcolm@redhat.com>",
        "MIME-Version": "1.0",
        "X-Scanned-By": "MIMEDefang 3.4.1 on 10.30.177.111",
        "X-Mimecast-Spam-Score": "0",
        "X-Mimecast-MFC-PROC-ID": "SXa_2wmjNJJHAxoB2zWHMjcqoLBnPlqtVueYz31FLkM_1777418898",
        "X-Mimecast-Originator": "redhat.com",
        "Content-Transfer-Encoding": "8bit",
        "content-type": "text/plain; charset=\"US-ASCII\"; x-default=true",
        "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": "No functional change intended.\n\nSuccessfully bootstrapped & regrtested on powerpc64le-unknown-linux-gnu\n(albeit without ada,cobol,d, and possibly missing some optional test dependencies)\nPushed to trunk as r17-184-g7a1365277122aa.\n\ngcc/ChangeLog:\n\t* Makefile.in (ANALYZER_OBJS): Add analyzer/setjmp-longjmp.o.\n\ngcc/analyzer/ChangeLog:\n\t* analyzer.cc (is_setjmp_call_p): Move to setjmp-longjmp.cc.\n\t(is_longjmp_call_p): Likewise.\n\t* engine.cc (setjmp_record::cmp): Likewise.\n\t(setjmp_svalue::accept): Likewise.\n\t(setjmp_svalue::dump_to_pp): Likewise.\n\t(setjmp_svalue::print_dump_widget_label): Likewise.\n\t(setjmp_svalue::add_dump_widget_children): Likewise.\n\t(setjmp_svalue::get_enode_index): Likewise.\n\t(valid_longjmp_stack_p): Likewise.\n\t(class stale_jmp_buf): Likewise.\n\t(exploded_node::on_longjmp): Likewise.\n\t(rewind_info_t::update_model): Likewise.\n\t(rewind_info_t::add_events_to_path): Likewise.\n\t* region-model.cc (region_model::on_setjmp): Likewise.\n\t(region_model::on_longjmp): Likewise.\n\t* setjmp-longjmp.cc: New file, made from the above material.\n\t* svalue.cc: Update comment.\n\nSigned-off-by: David Malcolm <dmalcolm@redhat.com>\n\n---\n gcc/Makefile.in                |   1 +\n gcc/analyzer/analyzer.cc       |  29 --\n gcc/analyzer/engine.cc         | 346 -----------------------\n gcc/analyzer/region-model.cc   |  89 ------\n gcc/analyzer/setjmp-longjmp.cc | 499 +++++++++++++++++++++++++++++++++\n gcc/analyzer/svalue.cc         |   3 +-\n 6 files changed, 501 insertions(+), 466 deletions(-)\n create mode 100644 gcc/analyzer/setjmp-longjmp.cc",
    "diff": "diff --git a/gcc/Makefile.in b/gcc/Makefile.in\nindex 0bccfffedc0..c9888c38cef 100644\n--- a/gcc/Makefile.in\n+++ b/gcc/Makefile.in\n@@ -1373,6 +1373,7 @@ ANALYZER_OBJS = \\\n \tanalyzer/region-model-asm.o \\\n \tanalyzer/region-model-manager.o \\\n \tanalyzer/region-model-reachability.o \\\n+\tanalyzer/setjmp-longjmp.o \\\n \tanalyzer/shift-diagnostics.o \\\n \tanalyzer/sm.o \\\n \tanalyzer/sm-file.o \\\ndiff --git a/gcc/analyzer/analyzer.cc b/gcc/analyzer/analyzer.cc\nindex 1a067c76764..cf9d822629d 100644\n--- a/gcc/analyzer/analyzer.cc\n+++ b/gcc/analyzer/analyzer.cc\n@@ -420,35 +420,6 @@ is_std_named_call_p (const_tree fndecl, const char *funcname,\n   return true;\n }\n \n-/* Return true if stmt is a setjmp or sigsetjmp call.  */\n-\n-bool\n-is_setjmp_call_p (const gcall &call)\n-{\n-  if (is_special_named_call_p (call, \"setjmp\", 1)\n-      || is_special_named_call_p (call, \"sigsetjmp\", 2))\n-    /* region_model::on_setjmp requires a pointer.  */\n-    if (POINTER_TYPE_P (TREE_TYPE (gimple_call_arg (&call, 0))))\n-      return true;\n-\n-  return false;\n-}\n-\n-/* Return true if stmt is a longjmp or siglongjmp call.  */\n-\n-bool\n-is_longjmp_call_p (const gcall &call)\n-{\n-  if (is_special_named_call_p (call, \"longjmp\", 2)\n-      || is_special_named_call_p (call, \"siglongjmp\", 2))\n-    /* exploded_node::on_longjmp requires a pointer for the initial\n-       argument.  */\n-    if (POINTER_TYPE_P (TREE_TYPE (gimple_call_arg (&call, 0))))\n-      return true;\n-\n-  return false;\n-}\n-\n bool\n is_cxa_throw_p (const gcall &call)\n {\ndiff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc\nindex a5ab173ca16..b506cdbe848 100644\n--- a/gcc/analyzer/engine.cc\n+++ b/gcc/analyzer/engine.cc\n@@ -209,66 +209,6 @@ impl_region_model_context::terminate_path ()\n     return m_path_ctxt->terminate_path ();\n }\n \n-/* struct setjmp_record.  */\n-\n-int\n-setjmp_record::cmp (const setjmp_record &rec1, const setjmp_record &rec2)\n-{\n-  if (int cmp_enode = rec1.m_enode->m_index - rec2.m_enode->m_index)\n-    return cmp_enode;\n-  gcc_assert (&rec1 == &rec2);\n-  return 0;\n-}\n-\n-/* class setjmp_svalue : public svalue.  */\n-\n-/* Implementation of svalue::accept vfunc for setjmp_svalue.  */\n-\n-void\n-setjmp_svalue::accept (visitor *v) const\n-{\n-  v->visit_setjmp_svalue (this);\n-}\n-\n-/* Implementation of svalue::dump_to_pp vfunc for setjmp_svalue.  */\n-\n-void\n-setjmp_svalue::dump_to_pp (pretty_printer *pp, bool simple) const\n-{\n-  if (simple)\n-    pp_printf (pp, \"SETJMP(EN: %i)\", get_enode_index ());\n-  else\n-    pp_printf (pp, \"setjmp_svalue(EN%i)\", get_enode_index ());\n-}\n-\n-/* Implementation of svalue::print_dump_widget_label vfunc for\n-   setjmp_svalue.  */\n-\n-void\n-setjmp_svalue::print_dump_widget_label (pretty_printer *pp) const\n-{\n-  pp_printf (pp, \"setjmp_svalue(EN: %i)\", get_enode_index ());\n-}\n-\n-/* Implementation of svalue::add_dump_widget_children vfunc for\n-   setjmp_svalue.  */\n-\n-void\n-setjmp_svalue::\n-add_dump_widget_children (text_art::tree_widget &,\n-\t\t\t  const text_art::dump_widget_info &) const\n-{\n-  /* No children.  */\n-}\n-\n-/* Get the index of the stored exploded_node.  */\n-\n-int\n-setjmp_svalue::get_enode_index () const\n-{\n-  return m_setjmp_record.m_enode->m_index;\n-}\n-\n bool\n impl_region_model_context::\n get_state_map_by_name (const char *name,\n@@ -1306,234 +1246,6 @@ fndecl_has_gimple_body_p (tree fndecl)\n \n namespace ana {\n \n-/* Verify that the stack at LONGJMP_POINT is still valid, given a call\n-   to \"setjmp\" at SETJMP_POINT - the stack frame that \"setjmp\" was\n-   called in must still be valid.\n-\n-   Caveat: this merely checks the call_strings in the points; it doesn't\n-   detect the case where a frame returns and is then called again.  */\n-\n-static bool\n-valid_longjmp_stack_p (const program_point &longjmp_point,\n-\t\t       const program_point &setjmp_point)\n-{\n-  const call_string &cs_at_longjmp = longjmp_point.get_call_string ();\n-  const call_string &cs_at_setjmp = setjmp_point.get_call_string ();\n-\n-  if (cs_at_longjmp.length () < cs_at_setjmp.length ())\n-    return false;\n-\n-  /* Check that the call strings match, up to the depth of the\n-     setjmp point.  */\n-  for (unsigned depth = 0; depth < cs_at_setjmp.length (); depth++)\n-    if (cs_at_longjmp[depth] != cs_at_setjmp[depth])\n-      return false;\n-\n-  return true;\n-}\n-\n-/* A pending_diagnostic subclass for complaining about bad longjmps,\n-   where the enclosing function of the \"setjmp\" has returned (and thus\n-   the stack frame no longer exists).  */\n-\n-class stale_jmp_buf : public pending_diagnostic_subclass<stale_jmp_buf>\n-{\n-public:\n-  stale_jmp_buf (const gcall &setjmp_call, const gcall &longjmp_call,\n-\t\t const program_point &setjmp_point)\n-  : m_setjmp_call (setjmp_call), m_longjmp_call (longjmp_call),\n-    m_setjmp_point (setjmp_point), m_stack_pop_event (nullptr)\n-  {}\n-\n-  int get_controlling_option () const final override\n-  {\n-    return OPT_Wanalyzer_stale_setjmp_buffer;\n-  }\n-\n-  bool emit (diagnostic_emission_context &ctxt) final override\n-  {\n-    return ctxt.warn (\"%qs called after enclosing function of %qs has returned\",\n-\t\t      get_user_facing_name (m_longjmp_call),\n-\t\t      get_user_facing_name (m_setjmp_call));\n-  }\n-\n-  const char *get_kind () const final override\n-  { return \"stale_jmp_buf\"; }\n-\n-  bool operator== (const stale_jmp_buf &other) const\n-  {\n-    return (&m_setjmp_call == &other.m_setjmp_call\n-\t    && &m_longjmp_call == &other.m_longjmp_call);\n-  }\n-\n-  bool\n-  maybe_add_custom_events_for_eedge (const exploded_edge &eedge,\n-\t\t\t\t     checker_path *emission_path)\n-    final override\n-  {\n-    /* Detect exactly when the stack first becomes invalid,\n-       and issue an event then.  */\n-    if (m_stack_pop_event)\n-      return false;\n-    const exploded_node *src_node = eedge.m_src;\n-    const program_point &src_point = src_node->get_point ();\n-    const exploded_node *dst_node = eedge.m_dest;\n-    const program_point &dst_point = dst_node->get_point ();\n-    if (valid_longjmp_stack_p (src_point, m_setjmp_point)\n-\t&& !valid_longjmp_stack_p (dst_point, m_setjmp_point))\n-      {\n-\t/* Compare with diagnostic_manager::add_events_for_superedge.  */\n-\tconst int src_stack_depth = src_point.get_stack_depth ();\n-\tm_stack_pop_event = new precanned_custom_event\n-\t  (event_loc_info (src_point.get_location (),\n-\t\t\t   src_point.get_fndecl (),\n-\t\t\t   src_stack_depth),\n-\t   \"stack frame is popped here, invalidating saved environment\");\n-\temission_path->add_event\n-\t  (std::unique_ptr<custom_event> (m_stack_pop_event));\n-\treturn false;\n-      }\n-    return false;\n-  }\n-\n-  bool\n-  describe_final_event (pretty_printer &pp,\n-\t\t\tconst evdesc::final_event &) final override\n-  {\n-    if (m_stack_pop_event)\n-      pp_printf (&pp,\n-\t\t \"%qs called after enclosing function of %qs returned at %@\",\n-\t\t get_user_facing_name (m_longjmp_call),\n-\t\t get_user_facing_name (m_setjmp_call),\n-\t\t m_stack_pop_event->get_id_ptr ());\n-    else\n-      pp_printf (&pp,\n-\t\t \"%qs called after enclosing function of %qs has returned\",\n-\t\t get_user_facing_name (m_longjmp_call),\n-\t\t get_user_facing_name (m_setjmp_call));\n-    return true;\n-  }\n-\n-\n-private:\n-  const gcall &m_setjmp_call;\n-  const gcall &m_longjmp_call;\n-  program_point m_setjmp_point;\n-  custom_event *m_stack_pop_event;\n-};\n-\n-/* Handle LONGJMP_CALL, a call to longjmp or siglongjmp.\n-\n-   Attempt to locate where setjmp/sigsetjmp was called on the jmp_buf and build\n-   an exploded_node and exploded_edge to it representing a rewind to that frame,\n-   handling the various kinds of failure that can occur.  */\n-\n-void\n-exploded_node::on_longjmp (exploded_graph &eg,\n-\t\t\t   const gcall &longjmp_call,\n-\t\t\t   program_state *new_state,\n-\t\t\t   region_model_context *ctxt)\n-{\n-  tree buf_ptr = gimple_call_arg (&longjmp_call, 0);\n-  gcc_assert (POINTER_TYPE_P (TREE_TYPE (buf_ptr)));\n-\n-  region_model *new_region_model = new_state->m_region_model;\n-  const svalue *buf_ptr_sval = new_region_model->get_rvalue (buf_ptr, ctxt);\n-  const region *buf = new_region_model->deref_rvalue (buf_ptr_sval, buf_ptr,\n-\t\t\t\t\t\t       ctxt);\n-\n-  const svalue *buf_content_sval\n-    = new_region_model->get_store_value (buf, ctxt);\n-  const setjmp_svalue *setjmp_sval\n-    = buf_content_sval->dyn_cast_setjmp_svalue ();\n-  if (!setjmp_sval)\n-    return;\n-\n-  const setjmp_record tmp_setjmp_record = setjmp_sval->get_setjmp_record ();\n-\n-  /* Build a custom enode and eedge for rewinding from the longjmp/siglongjmp\n-     call back to the setjmp/sigsetjmp.  */\n-  rewind_info_t rewind_info (tmp_setjmp_record, longjmp_call);\n-\n-  const gcall &setjmp_call = rewind_info.get_setjmp_call ();\n-  const program_point point_before_setjmp = rewind_info.get_point_before_setjmp ();\n-  const program_point point_after_setjmp = rewind_info.get_point_after_setjmp ();\n-\n-  const program_point &longjmp_point = get_point ();\n-\n-  /* Verify that the setjmp's call_stack hasn't been popped.  */\n-  if (!valid_longjmp_stack_p (longjmp_point, point_after_setjmp))\n-    {\n-      ctxt->warn (std::make_unique<stale_jmp_buf> (setjmp_call,\n-\t\t\t\t\t\t   longjmp_call,\n-\t\t\t\t\t\t   point_before_setjmp));\n-      return;\n-    }\n-\n-  gcc_assert (longjmp_point.get_stack_depth ()\n-\t      >= point_after_setjmp.get_stack_depth ());\n-\n-  /* Update the state for use by the destination node.  */\n-\n-  /* Stash the current number of diagnostics so that we can update\n-     any that this adds to show where the longjmp is rewinding to.  */\n-\n-  diagnostic_manager *dm = &eg.get_diagnostic_manager ();\n-  unsigned prev_num_diagnostics = dm->get_num_diagnostics ();\n-\n-  new_region_model->on_longjmp (longjmp_call, setjmp_call,\n-\t\t\t\tpoint_after_setjmp.get_stack_depth (), ctxt);\n-\n-  /* Detect leaks in the new state relative to the old state.  */\n-  program_state::detect_leaks (get_state (), *new_state, nullptr,\n-\t\t\t\teg.get_ext_state (), ctxt);\n-  exploded_node *next\n-    = eg.get_or_create_node (point_after_setjmp, *new_state, this);\n-\n-  /* Create custom exploded_edge for a longjmp.  */\n-  if (next)\n-    {\n-      exploded_edge *eedge\n-\t= eg.add_edge (const_cast<exploded_node *> (this), next, nullptr, true,\n-\t\t       std::make_unique<rewind_info_t> (tmp_setjmp_record,\n-\t\t\t\t\t\t\tlongjmp_call));\n-\n-      /* For any diagnostics that were queued here (such as leaks) we want\n-\t the checker_path to show the rewinding events after the \"final event\"\n-\t so that the user sees where the longjmp is rewinding to (otherwise the\n-\t path is meaningless).\n-\n-\t For example, we want to emit something like:\n-                        |   NN | {\n-                        |   NN |   longjmp (env, 1);\n-                        |      |   ~~~~~~~~~~~~~~~~\n-                        |      |   |\n-                        |      |   (10) 'ptr' leaks here; was allocated at (7)\n-                        |      |   (11) rewinding from 'longjmp' in 'inner'...\n-                        |\n-          <-------------+\n-          |\n-        'outer': event 12\n-          |\n-          |   NN |   i = setjmp(env);\n-          |      |       ^~~~~~\n-          |      |       |\n-          |      |       (12) ...to 'setjmp' in 'outer' (saved at (2))\n-\n-\t where the \"final\" event above is event (10), but we want to append\n-\t events (11) and (12) afterwards.\n-\n-\t Do this by setting m_trailing_eedge on any diagnostics that were\n-\t just saved.  */\n-      unsigned num_diagnostics = dm->get_num_diagnostics ();\n-      for (unsigned i = prev_num_diagnostics; i < num_diagnostics; i++)\n-\t{\n-\t  saved_diagnostic *sd = dm->get_saved_diagnostic (i);\n-\t  sd->m_trailing_eedge = eedge;\n-\t}\n-    }\n-}\n-\n /* Subclass of call_info for exploded edges that express\n    a throw or rethrow of an exception (actually a call\n    to __cxa_throw or __cxa_rethrow).  */\n@@ -2013,64 +1725,6 @@ interprocedural_return::add_events_to_path (checker_path *emission_path,\n \t\t\tdst_point.get_stack_depth ())));\n }\n \n-/* class rewind_info_t : public custom_edge_info.  */\n-\n-/* Implementation of custom_edge_info::update_model vfunc\n-   for rewind_info_t.\n-\n-   Update state for the special-case of a rewind of a longjmp\n-   to a setjmp (which doesn't have a superedge, but does affect\n-   state).  */\n-\n-bool\n-rewind_info_t::update_model (region_model *model,\n-\t\t\t     const exploded_edge *eedge,\n-\t\t\t     region_model_context *) const\n-{\n-  gcc_assert (eedge);\n-  const program_point &longjmp_point = eedge->m_src->get_point ();\n-  const program_point &setjmp_point = eedge->m_dest->get_point ();\n-\n-  gcc_assert (longjmp_point.get_stack_depth ()\n-\t      >= setjmp_point.get_stack_depth ());\n-\n-  model->on_longjmp (get_longjmp_call (),\n-\t\t     get_setjmp_call (),\n-\t\t     setjmp_point.get_stack_depth (), nullptr);\n-  return true;\n-}\n-\n-/* Implementation of custom_edge_info::add_events_to_path vfunc\n-   for rewind_info_t.  */\n-\n-void\n-rewind_info_t::add_events_to_path (checker_path *emission_path,\n-\t\t\t\t   const exploded_edge &eedge,\n-\t\t\t\t   pending_diagnostic &) const\n-{\n-  const exploded_node *src_node = eedge.m_src;\n-  const program_point &src_point = src_node->get_point ();\n-  const int src_stack_depth = src_point.get_stack_depth ();\n-  const exploded_node *dst_node = eedge.m_dest;\n-  const program_point &dst_point = dst_node->get_point ();\n-  const int dst_stack_depth = dst_point.get_stack_depth ();\n-\n-  emission_path->add_event\n-    (std::make_unique<rewind_from_longjmp_event>\n-       (&eedge,\n-\tevent_loc_info (get_longjmp_call ().location,\n-\t\t\tsrc_point.get_fndecl (),\n-\t\t\tsrc_stack_depth),\n-\tthis));\n-  emission_path->add_event\n-    (std::make_unique<rewind_to_setjmp_event>\n-       (&eedge,\n-\tevent_loc_info (get_setjmp_call ().location,\n-\t\t\tdst_point.get_fndecl (),\n-\t\t\tdst_stack_depth),\n-\tthis));\n-}\n-\n /* class exploded_edge : public dedge<eg_traits>.  */\n \n /* exploded_edge's ctor.  */\ndiff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc\nindex 15210f5aa9c..02b00bc03c9 100644\n--- a/gcc/analyzer/region-model.cc\n+++ b/gcc/analyzer/region-model.cc\n@@ -2603,95 +2603,6 @@ region_model::on_return (const greturn *return_stmt, region_model_context *ctxt)\n     }\n }\n \n-/* Update this model for a call and return of setjmp/sigsetjmp at CALL within\n-   ENODE, using CTXT to report any diagnostics.\n-\n-   This is for the initial direct invocation of setjmp/sigsetjmp (which returns\n-   0), as opposed to any second return due to longjmp/sigsetjmp.  */\n-\n-void\n-region_model::on_setjmp (const gcall &call,\n-\t\t\t const exploded_node &enode,\n-\t\t\t const superedge &sedge,\n-\t\t\t region_model_context *ctxt)\n-{\n-  const svalue *buf_ptr = get_rvalue (gimple_call_arg (&call, 0), ctxt);\n-  const region *buf_reg = deref_rvalue (buf_ptr, gimple_call_arg (&call, 0),\n-\t\t\t\t\t ctxt);\n-\n-  /* Create a setjmp_svalue for this call and store it in BUF_REG's\n-     region.  */\n-  if (buf_reg)\n-    {\n-      setjmp_record r (&enode, &sedge, call);\n-      const svalue *sval\n-\t= m_mgr->get_or_create_setjmp_svalue (r, buf_reg->get_type ());\n-      set_value (buf_reg, sval, ctxt);\n-    }\n-\n-  /* Direct calls to setjmp return 0.  */\n-  if (tree lhs = gimple_call_lhs (&call))\n-    {\n-      const svalue *new_sval\n-\t= m_mgr->get_or_create_int_cst (TREE_TYPE (lhs), 0);\n-      const region *lhs_reg = get_lvalue (lhs, ctxt);\n-      set_value (lhs_reg, new_sval, ctxt);\n-    }\n-}\n-\n-/* Update this region_model for rewinding from a \"longjmp\" at LONGJMP_CALL\n-   to a \"setjmp\" at SETJMP_CALL where the final stack depth should be\n-   SETJMP_STACK_DEPTH.  Pop any stack frames.  Leak detection is *not*\n-   done, and should be done by the caller.  */\n-\n-void\n-region_model::on_longjmp (const gcall &longjmp_call, const gcall &setjmp_call,\n-\t\t\t   int setjmp_stack_depth, region_model_context *ctxt)\n-{\n-  /* Evaluate the val, using the frame of the \"longjmp\".  */\n-  tree fake_retval = gimple_call_arg (&longjmp_call, 1);\n-  const svalue *fake_retval_sval = get_rvalue (fake_retval, ctxt);\n-\n-  /* Pop any frames until we reach the stack depth of the function where\n-     setjmp was called.  */\n-  gcc_assert (get_stack_depth () >= setjmp_stack_depth);\n-  while (get_stack_depth () > setjmp_stack_depth)\n-    pop_frame (nullptr, nullptr, ctxt, nullptr, false);\n-\n-  gcc_assert (get_stack_depth () == setjmp_stack_depth);\n-\n-  /* Assign to LHS of \"setjmp\" in new_state.  */\n-  if (tree lhs = gimple_call_lhs (&setjmp_call))\n-    {\n-      /* Passing 0 as the val to longjmp leads to setjmp returning 1.  */\n-      const svalue *zero_sval\n-\t= m_mgr->get_or_create_int_cst (TREE_TYPE (fake_retval), 0);\n-      tristate eq_zero = eval_condition (fake_retval_sval, EQ_EXPR, zero_sval);\n-      /* If we have 0, use 1.  */\n-      if (eq_zero.is_true ())\n-\t{\n-\t  const svalue *one_sval\n-\t    = m_mgr->get_or_create_int_cst (TREE_TYPE (fake_retval), 1);\n-\t  fake_retval_sval = one_sval;\n-\t}\n-      else\n-\t{\n-\t  /* Otherwise note that the value is nonzero.  */\n-\t  m_constraints->add_constraint (fake_retval_sval, NE_EXPR, zero_sval);\n-\t}\n-\n-      /* Decorate the return value from setjmp as being unmergeable,\n-\t so that we don't attempt to merge states with it as zero\n-\t with states in which it's nonzero, leading to a clean distinction\n-\t in the exploded_graph betweeen the first return and the second\n-\t return.  */\n-      fake_retval_sval = m_mgr->get_or_create_unmergeable (fake_retval_sval);\n-\n-      const region *lhs_reg = get_lvalue (lhs, ctxt);\n-      set_value (lhs_reg, fake_retval_sval, ctxt);\n-    }\n-}\n-\n /* Implementation of region_model::get_lvalue; the latter adds type-checking.\n \n    Get the id of the region for PV within this region_model,\ndiff --git a/gcc/analyzer/setjmp-longjmp.cc b/gcc/analyzer/setjmp-longjmp.cc\nnew file mode 100644\nindex 00000000000..711dfb3e8d0\n--- /dev/null\n+++ b/gcc/analyzer/setjmp-longjmp.cc\n@@ -0,0 +1,499 @@\n+/* setjmp/longjmp handling\n+   Copyright (C) 2019-2026 Free Software Foundation, Inc.\n+   Contributed by David Malcolm <dmalcolm@redhat.com>.\n+\n+This file is part of GCC.\n+\n+GCC is free software; you can redistribute it and/or modify it\n+under the terms of the GNU General Public License as published by\n+the Free Software Foundation; either version 3, or (at your option)\n+any later version.\n+\n+GCC is distributed in the hope that it will be useful, but\n+WITHOUT ANY WARRANTY; without even the implied warranty of\n+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n+General Public License for more details.\n+\n+You should have received a copy of the GNU General Public License\n+along with GCC; see the file COPYING3.  If not see\n+<http://www.gnu.org/licenses/>.  */\n+\n+#include \"analyzer/common.h\"\n+\n+#include \"analyzer/region-model.h\"\n+#include \"analyzer/checker-path.h\"\n+#include \"analyzer/checker-event.h\"\n+#include \"analyzer/exploded-graph.h\"\n+#include \"analyzer/constraint-manager.h\"\n+\n+#if ENABLE_ANALYZER\n+\n+/* Return true if stmt is a setjmp or sigsetjmp call.  */\n+\n+bool\n+is_setjmp_call_p (const gcall &call)\n+{\n+  if (is_special_named_call_p (call, \"setjmp\", 1)\n+      || is_special_named_call_p (call, \"sigsetjmp\", 2))\n+    /* region_model::on_setjmp requires a pointer.  */\n+    if (POINTER_TYPE_P (TREE_TYPE (gimple_call_arg (&call, 0))))\n+      return true;\n+\n+  return false;\n+}\n+\n+/* Return true if stmt is a longjmp or siglongjmp call.  */\n+\n+bool\n+is_longjmp_call_p (const gcall &call)\n+{\n+  if (is_special_named_call_p (call, \"longjmp\", 2)\n+      || is_special_named_call_p (call, \"siglongjmp\", 2))\n+    /* exploded_node::on_longjmp requires a pointer for the initial\n+       argument.  */\n+    if (POINTER_TYPE_P (TREE_TYPE (gimple_call_arg (&call, 0))))\n+      return true;\n+\n+  return false;\n+}\n+\n+namespace ana {\n+\n+/* struct setjmp_record.  */\n+\n+int\n+setjmp_record::cmp (const setjmp_record &rec1, const setjmp_record &rec2)\n+{\n+  if (int cmp_enode = rec1.m_enode->m_index - rec2.m_enode->m_index)\n+    return cmp_enode;\n+  gcc_assert (&rec1 == &rec2);\n+  return 0;\n+}\n+\n+/* class setjmp_svalue : public svalue.  */\n+\n+/* Implementation of svalue::accept vfunc for setjmp_svalue.  */\n+\n+void\n+setjmp_svalue::accept (visitor *v) const\n+{\n+  v->visit_setjmp_svalue (this);\n+}\n+\n+/* Implementation of svalue::dump_to_pp vfunc for setjmp_svalue.  */\n+\n+void\n+setjmp_svalue::dump_to_pp (pretty_printer *pp, bool simple) const\n+{\n+  if (simple)\n+    pp_printf (pp, \"SETJMP(EN: %i)\", get_enode_index ());\n+  else\n+    pp_printf (pp, \"setjmp_svalue(EN%i)\", get_enode_index ());\n+}\n+\n+/* Implementation of svalue::print_dump_widget_label vfunc for\n+   setjmp_svalue.  */\n+\n+void\n+setjmp_svalue::print_dump_widget_label (pretty_printer *pp) const\n+{\n+  pp_printf (pp, \"setjmp_svalue(EN: %i)\", get_enode_index ());\n+}\n+\n+/* Implementation of svalue::add_dump_widget_children vfunc for\n+   setjmp_svalue.  */\n+\n+void\n+setjmp_svalue::\n+add_dump_widget_children (text_art::tree_widget &,\n+\t\t\t  const text_art::dump_widget_info &) const\n+{\n+  /* No children.  */\n+}\n+\n+/* Get the index of the stored exploded_node.  */\n+\n+int\n+setjmp_svalue::get_enode_index () const\n+{\n+  return m_setjmp_record.m_enode->m_index;\n+}\n+\n+/* Verify that the stack at LONGJMP_POINT is still valid, given a call\n+   to \"setjmp\" at SETJMP_POINT - the stack frame that \"setjmp\" was\n+   called in must still be valid.\n+\n+   Caveat: this merely checks the call_strings in the points; it doesn't\n+   detect the case where a frame returns and is then called again.  */\n+\n+static bool\n+valid_longjmp_stack_p (const program_point &longjmp_point,\n+\t\t       const program_point &setjmp_point)\n+{\n+  const call_string &cs_at_longjmp = longjmp_point.get_call_string ();\n+  const call_string &cs_at_setjmp = setjmp_point.get_call_string ();\n+\n+  if (cs_at_longjmp.length () < cs_at_setjmp.length ())\n+    return false;\n+\n+  /* Check that the call strings match, up to the depth of the\n+     setjmp point.  */\n+  for (unsigned depth = 0; depth < cs_at_setjmp.length (); depth++)\n+    if (cs_at_longjmp[depth] != cs_at_setjmp[depth])\n+      return false;\n+\n+  return true;\n+}\n+\n+/* A pending_diagnostic subclass for complaining about bad longjmps,\n+   where the enclosing function of the \"setjmp\" has returned (and thus\n+   the stack frame no longer exists).  */\n+\n+class stale_jmp_buf : public pending_diagnostic_subclass<stale_jmp_buf>\n+{\n+public:\n+  stale_jmp_buf (const gcall &setjmp_call, const gcall &longjmp_call,\n+\t\t const program_point &setjmp_point)\n+  : m_setjmp_call (setjmp_call), m_longjmp_call (longjmp_call),\n+    m_setjmp_point (setjmp_point), m_stack_pop_event (nullptr)\n+  {}\n+\n+  int get_controlling_option () const final override\n+  {\n+    return OPT_Wanalyzer_stale_setjmp_buffer;\n+  }\n+\n+  bool emit (diagnostic_emission_context &ctxt) final override\n+  {\n+    return ctxt.warn (\"%qs called after enclosing function of %qs has returned\",\n+\t\t      get_user_facing_name (m_longjmp_call),\n+\t\t      get_user_facing_name (m_setjmp_call));\n+  }\n+\n+  const char *get_kind () const final override\n+  { return \"stale_jmp_buf\"; }\n+\n+  bool operator== (const stale_jmp_buf &other) const\n+  {\n+    return (&m_setjmp_call == &other.m_setjmp_call\n+\t    && &m_longjmp_call == &other.m_longjmp_call);\n+  }\n+\n+  bool\n+  maybe_add_custom_events_for_eedge (const exploded_edge &eedge,\n+\t\t\t\t     checker_path *emission_path)\n+    final override\n+  {\n+    /* Detect exactly when the stack first becomes invalid,\n+       and issue an event then.  */\n+    if (m_stack_pop_event)\n+      return false;\n+    const exploded_node *src_node = eedge.m_src;\n+    const program_point &src_point = src_node->get_point ();\n+    const exploded_node *dst_node = eedge.m_dest;\n+    const program_point &dst_point = dst_node->get_point ();\n+    if (valid_longjmp_stack_p (src_point, m_setjmp_point)\n+\t&& !valid_longjmp_stack_p (dst_point, m_setjmp_point))\n+      {\n+\t/* Compare with diagnostic_manager::add_events_for_superedge.  */\n+\tconst int src_stack_depth = src_point.get_stack_depth ();\n+\tm_stack_pop_event = new precanned_custom_event\n+\t  (event_loc_info (src_point.get_location (),\n+\t\t\t   src_point.get_fndecl (),\n+\t\t\t   src_stack_depth),\n+\t   \"stack frame is popped here, invalidating saved environment\");\n+\temission_path->add_event\n+\t  (std::unique_ptr<custom_event> (m_stack_pop_event));\n+\treturn false;\n+      }\n+    return false;\n+  }\n+\n+  bool\n+  describe_final_event (pretty_printer &pp,\n+\t\t\tconst evdesc::final_event &) final override\n+  {\n+    if (m_stack_pop_event)\n+      pp_printf (&pp,\n+\t\t \"%qs called after enclosing function of %qs returned at %@\",\n+\t\t get_user_facing_name (m_longjmp_call),\n+\t\t get_user_facing_name (m_setjmp_call),\n+\t\t m_stack_pop_event->get_id_ptr ());\n+    else\n+      pp_printf (&pp,\n+\t\t \"%qs called after enclosing function of %qs has returned\",\n+\t\t get_user_facing_name (m_longjmp_call),\n+\t\t get_user_facing_name (m_setjmp_call));\n+    return true;\n+  }\n+\n+\n+private:\n+  const gcall &m_setjmp_call;\n+  const gcall &m_longjmp_call;\n+  program_point m_setjmp_point;\n+  custom_event *m_stack_pop_event;\n+};\n+\n+/* Update this model for a call and return of setjmp/sigsetjmp at CALL within\n+   ENODE, using CTXT to report any diagnostics.\n+\n+   This is for the initial direct invocation of setjmp/sigsetjmp (which returns\n+   0), as opposed to any second return due to longjmp/sigsetjmp.  */\n+\n+void\n+region_model::on_setjmp (const gcall &call,\n+\t\t\t const exploded_node &enode,\n+\t\t\t const superedge &sedge,\n+\t\t\t region_model_context *ctxt)\n+{\n+  const svalue *buf_ptr = get_rvalue (gimple_call_arg (&call, 0), ctxt);\n+  const region *buf_reg = deref_rvalue (buf_ptr, gimple_call_arg (&call, 0),\n+\t\t\t\t\t ctxt);\n+\n+  /* Create a setjmp_svalue for this call and store it in BUF_REG's\n+     region.  */\n+  if (buf_reg)\n+    {\n+      setjmp_record r (&enode, &sedge, call);\n+      const svalue *sval\n+\t= m_mgr->get_or_create_setjmp_svalue (r, buf_reg->get_type ());\n+      set_value (buf_reg, sval, ctxt);\n+    }\n+\n+  /* Direct calls to setjmp return 0.  */\n+  if (tree lhs = gimple_call_lhs (&call))\n+    {\n+      const svalue *new_sval\n+\t= m_mgr->get_or_create_int_cst (TREE_TYPE (lhs), 0);\n+      const region *lhs_reg = get_lvalue (lhs, ctxt);\n+      set_value (lhs_reg, new_sval, ctxt);\n+    }\n+}\n+\n+/* Update this region_model for rewinding from a \"longjmp\" at LONGJMP_CALL\n+   to a \"setjmp\" at SETJMP_CALL where the final stack depth should be\n+   SETJMP_STACK_DEPTH.  Pop any stack frames.  Leak detection is *not*\n+   done, and should be done by the caller.  */\n+\n+void\n+region_model::on_longjmp (const gcall &longjmp_call, const gcall &setjmp_call,\n+\t\t\t   int setjmp_stack_depth, region_model_context *ctxt)\n+{\n+  /* Evaluate the val, using the frame of the \"longjmp\".  */\n+  tree fake_retval = gimple_call_arg (&longjmp_call, 1);\n+  const svalue *fake_retval_sval = get_rvalue (fake_retval, ctxt);\n+\n+  /* Pop any frames until we reach the stack depth of the function where\n+     setjmp was called.  */\n+  gcc_assert (get_stack_depth () >= setjmp_stack_depth);\n+  while (get_stack_depth () > setjmp_stack_depth)\n+    pop_frame (nullptr, nullptr, ctxt, nullptr, false);\n+\n+  gcc_assert (get_stack_depth () == setjmp_stack_depth);\n+\n+  /* Assign to LHS of \"setjmp\" in new_state.  */\n+  if (tree lhs = gimple_call_lhs (&setjmp_call))\n+    {\n+      /* Passing 0 as the val to longjmp leads to setjmp returning 1.  */\n+      const svalue *zero_sval\n+\t= m_mgr->get_or_create_int_cst (TREE_TYPE (fake_retval), 0);\n+      tristate eq_zero = eval_condition (fake_retval_sval, EQ_EXPR, zero_sval);\n+      /* If we have 0, use 1.  */\n+      if (eq_zero.is_true ())\n+\t{\n+\t  const svalue *one_sval\n+\t    = m_mgr->get_or_create_int_cst (TREE_TYPE (fake_retval), 1);\n+\t  fake_retval_sval = one_sval;\n+\t}\n+      else\n+\t{\n+\t  /* Otherwise note that the value is nonzero.  */\n+\t  m_constraints->add_constraint (fake_retval_sval, NE_EXPR, zero_sval);\n+\t}\n+\n+      /* Decorate the return value from setjmp as being unmergeable,\n+\t so that we don't attempt to merge states with it as zero\n+\t with states in which it's nonzero, leading to a clean distinction\n+\t in the exploded_graph betweeen the first return and the second\n+\t return.  */\n+      fake_retval_sval = m_mgr->get_or_create_unmergeable (fake_retval_sval);\n+\n+      const region *lhs_reg = get_lvalue (lhs, ctxt);\n+      set_value (lhs_reg, fake_retval_sval, ctxt);\n+    }\n+}\n+\n+/* Handle LONGJMP_CALL, a call to longjmp or siglongjmp.\n+\n+   Attempt to locate where setjmp/sigsetjmp was called on the jmp_buf and build\n+   an exploded_node and exploded_edge to it representing a rewind to that frame,\n+   handling the various kinds of failure that can occur.  */\n+\n+void\n+exploded_node::on_longjmp (exploded_graph &eg,\n+\t\t\t   const gcall &longjmp_call,\n+\t\t\t   program_state *new_state,\n+\t\t\t   region_model_context *ctxt)\n+{\n+  tree buf_ptr = gimple_call_arg (&longjmp_call, 0);\n+  gcc_assert (POINTER_TYPE_P (TREE_TYPE (buf_ptr)));\n+\n+  region_model *new_region_model = new_state->m_region_model;\n+  const svalue *buf_ptr_sval = new_region_model->get_rvalue (buf_ptr, ctxt);\n+  const region *buf = new_region_model->deref_rvalue (buf_ptr_sval, buf_ptr,\n+\t\t\t\t\t\t       ctxt);\n+\n+  const svalue *buf_content_sval\n+    = new_region_model->get_store_value (buf, ctxt);\n+  const setjmp_svalue *setjmp_sval\n+    = buf_content_sval->dyn_cast_setjmp_svalue ();\n+  if (!setjmp_sval)\n+    return;\n+\n+  const setjmp_record tmp_setjmp_record = setjmp_sval->get_setjmp_record ();\n+\n+  /* Build a custom enode and eedge for rewinding from the longjmp/siglongjmp\n+     call back to the setjmp/sigsetjmp.  */\n+  rewind_info_t rewind_info (tmp_setjmp_record, longjmp_call);\n+\n+  const gcall &setjmp_call = rewind_info.get_setjmp_call ();\n+  const program_point point_before_setjmp = rewind_info.get_point_before_setjmp ();\n+  const program_point point_after_setjmp = rewind_info.get_point_after_setjmp ();\n+\n+  const program_point &longjmp_point = get_point ();\n+\n+  /* Verify that the setjmp's call_stack hasn't been popped.  */\n+  if (!valid_longjmp_stack_p (longjmp_point, point_after_setjmp))\n+    {\n+      ctxt->warn (std::make_unique<stale_jmp_buf> (setjmp_call,\n+\t\t\t\t\t\t   longjmp_call,\n+\t\t\t\t\t\t   point_before_setjmp));\n+      return;\n+    }\n+\n+  gcc_assert (longjmp_point.get_stack_depth ()\n+\t      >= point_after_setjmp.get_stack_depth ());\n+\n+  /* Update the state for use by the destination node.  */\n+\n+  /* Stash the current number of diagnostics so that we can update\n+     any that this adds to show where the longjmp is rewinding to.  */\n+\n+  diagnostic_manager *dm = &eg.get_diagnostic_manager ();\n+  unsigned prev_num_diagnostics = dm->get_num_diagnostics ();\n+\n+  new_region_model->on_longjmp (longjmp_call, setjmp_call,\n+\t\t\t\tpoint_after_setjmp.get_stack_depth (), ctxt);\n+\n+  /* Detect leaks in the new state relative to the old state.  */\n+  program_state::detect_leaks (get_state (), *new_state, nullptr,\n+\t\t\t\teg.get_ext_state (), ctxt);\n+  exploded_node *next\n+    = eg.get_or_create_node (point_after_setjmp, *new_state, this);\n+\n+  /* Create custom exploded_edge for a longjmp.  */\n+  if (next)\n+    {\n+      exploded_edge *eedge\n+\t= eg.add_edge (const_cast<exploded_node *> (this), next, nullptr, true,\n+\t\t       std::make_unique<rewind_info_t> (tmp_setjmp_record,\n+\t\t\t\t\t\t\tlongjmp_call));\n+\n+      /* For any diagnostics that were queued here (such as leaks) we want\n+\t the checker_path to show the rewinding events after the \"final event\"\n+\t so that the user sees where the longjmp is rewinding to (otherwise the\n+\t path is meaningless).\n+\n+\t For example, we want to emit something like:\n+                        |   NN | {\n+                        |   NN |   longjmp (env, 1);\n+                        |      |   ~~~~~~~~~~~~~~~~\n+                        |      |   |\n+                        |      |   (10) 'ptr' leaks here; was allocated at (7)\n+                        |      |   (11) rewinding from 'longjmp' in 'inner'...\n+                        |\n+          <-------------+\n+          |\n+        'outer': event 12\n+          |\n+          |   NN |   i = setjmp(env);\n+          |      |       ^~~~~~\n+          |      |       |\n+          |      |       (12) ...to 'setjmp' in 'outer' (saved at (2))\n+\n+\t where the \"final\" event above is event (10), but we want to append\n+\t events (11) and (12) afterwards.\n+\n+\t Do this by setting m_trailing_eedge on any diagnostics that were\n+\t just saved.  */\n+      unsigned num_diagnostics = dm->get_num_diagnostics ();\n+      for (unsigned i = prev_num_diagnostics; i < num_diagnostics; i++)\n+\t{\n+\t  saved_diagnostic *sd = dm->get_saved_diagnostic (i);\n+\t  sd->m_trailing_eedge = eedge;\n+\t}\n+    }\n+}\n+\n+/* class rewind_info_t : public custom_edge_info.  */\n+\n+/* Implementation of custom_edge_info::update_model vfunc\n+   for rewind_info_t.\n+\n+   Update state for the special-case of a rewind of a longjmp\n+   to a setjmp (which doesn't have a superedge, but does affect\n+   state).  */\n+\n+bool\n+rewind_info_t::update_model (region_model *model,\n+\t\t\t     const exploded_edge *eedge,\n+\t\t\t     region_model_context *) const\n+{\n+  gcc_assert (eedge);\n+  const program_point &longjmp_point = eedge->m_src->get_point ();\n+  const program_point &setjmp_point = eedge->m_dest->get_point ();\n+\n+  gcc_assert (longjmp_point.get_stack_depth ()\n+\t      >= setjmp_point.get_stack_depth ());\n+\n+  model->on_longjmp (get_longjmp_call (),\n+\t\t     get_setjmp_call (),\n+\t\t     setjmp_point.get_stack_depth (), nullptr);\n+  return true;\n+}\n+\n+/* Implementation of custom_edge_info::add_events_to_path vfunc\n+   for rewind_info_t.  */\n+\n+void\n+rewind_info_t::add_events_to_path (checker_path *emission_path,\n+\t\t\t\t   const exploded_edge &eedge,\n+\t\t\t\t   pending_diagnostic &) const\n+{\n+  const exploded_node *src_node = eedge.m_src;\n+  const program_point &src_point = src_node->get_point ();\n+  const int src_stack_depth = src_point.get_stack_depth ();\n+  const exploded_node *dst_node = eedge.m_dest;\n+  const program_point &dst_point = dst_node->get_point ();\n+  const int dst_stack_depth = dst_point.get_stack_depth ();\n+\n+  emission_path->add_event\n+    (std::make_unique<rewind_from_longjmp_event>\n+       (&eedge,\n+\tevent_loc_info (get_longjmp_call ().location,\n+\t\t\tsrc_point.get_fndecl (),\n+\t\t\tsrc_stack_depth),\n+\tthis));\n+  emission_path->add_event\n+    (std::make_unique<rewind_to_setjmp_event>\n+       (&eedge,\n+\tevent_loc_info (get_setjmp_call ().location,\n+\t\t\tdst_point.get_fndecl (),\n+\t\t\tdst_stack_depth),\n+\tthis));\n+}\n+\n+} // namespace ana\n+\n+#endif /* #if ENABLE_ANALYZER */\ndiff --git a/gcc/analyzer/svalue.cc b/gcc/analyzer/svalue.cc\nindex 52e853fdc8a..daf2f3ccd8f 100644\n--- a/gcc/analyzer/svalue.cc\n+++ b/gcc/analyzer/svalue.cc\n@@ -1364,8 +1364,7 @@ poisoned_svalue::maybe_fold_bits_within (tree type,\n   return mgr->get_or_create_poisoned_svalue (m_kind, type);\n }\n \n-/* class setjmp_svalue's implementation is in engine.cc, so that it can use\n-   the declaration of exploded_node.  */\n+/* class setjmp_svalue's implementation is in setjmp-longjmp.cc.  */\n \n /* class initial_svalue : public svalue.  */\n \n",
    "prefixes": [
        "pushed:",
        "r17-184"
    ]
}