Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/1.1/patches/2229950/?format=api
{ "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" ] }