From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id KQ3GLJ6Qs2oBsDYAWB0awg (envelope-from ) for ; Wed, 23 Sep 2026 04:41:02 -0400 Authentication-Results: simark.ca; dkim=pass (2048-bit key; unprotected) header.d=intel.com header.i=@intel.com header.a=rsa-sha256 header.s=Intel header.b=OGjVjYO3; dkim-atps=neutral Received: by simark.ca (Postfix, from userid 112) id B1D961E06B; Wed, 23 Sep 2026 04:41:02 -0400 (EDT) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-3.4 required=5.0 tests=ARC_SIGNED,ARC_VALID,BAYES_00, DKIMWL_WL_HIGH,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED,RCVD_IN_VALIDITY_CERTIFIED_BLOCKED, RCVD_IN_VALIDITY_RPBL_BLOCKED,RCVD_IN_VALIDITY_SAFE_BLOCKED autolearn=ham autolearn_force=no version=4.0.1 Received: from vm01.sourceware.org (vm01.sourceware.org [38.145.34.32]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by simark.ca (Postfix) with ESMTPS id 2DE3F1E033 for ; Wed, 23 Sep 2026 04:41:00 -0400 (EDT) Received: from vm01.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 9195B4BB24EF for ; Wed, 23 Sep 2026 08:40:59 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 9195B4BB24EF Authentication-Results: sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=intel.com header.i=@intel.com header.a=rsa-sha256 header.s=Intel header.b=OGjVjYO3 Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.6]) by sourceware.org (Postfix) with ESMTPS id E23F94BB24DA for ; Wed, 23 Sep 2026 08:40:21 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org E23F94BB24DA Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=intel.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org E23F94BB24DA Authentication-Results: sourceware.org; arc=none smtp.remote-ip=192.198.163.6 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1790152822; cv=none; b=OR45HPaVBWgy4CWKWzMOslP4WXo8KFGhgqb6uYyI4qXMkXtq0aNSSXh14TdT9KUvQqVhn1sloLSVUy8alEVq/NAHtf2oI/plruAIadb4eds38Ca3XFA7wWQ+yvvSaKXnvnviOALOBhg3cDIaJS0/+Jyaf1srRUprBt9NCABsgXA= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1790152822; c=relaxed/simple; bh=fY6GWXZpJF35EDkuOl7GAR6TQ059Jz+13tmH6sKRYA4=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=xwW9yAWmPqJrme96/yeAQEd8n4CfHKpfr/lm0NI9vSlgYT8S2mbdZsnFRkRSJ1dp4VyVZFEW6IyLNOlLkOxVAilSrZwDPhiu7l9ADs31Ne01xa1A6LnmCJZpxV7iRXXajd5UmeF/XYz2aPkyGq1RC2uleDz7gEnmYd5eaui13O8= ARC-Authentication-Results: i=1; sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=intel.com header.i=@intel.com header.a=rsa-sha256 header.s=Intel header.b=OGjVjYO3 DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org E23F94BB24DA DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1790152822; x=1821688822; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=fY6GWXZpJF35EDkuOl7GAR6TQ059Jz+13tmH6sKRYA4=; b=OGjVjYO3i7vITjtniQcTX+EyiipfYrMg9qXdhKK7Zs1TQQTaYLlmmTBO 8tjUdEYBy8lcrOKw5QSa4iAE6wPHBlx6kF0zUZFe35y+ILnQSSfq59Htk vtPci+tRCLXoTlAez4UTVM+uKLBkmE2y0bXKQOhyZ1f8Y00ZFfSzJqHXi Pi7MXDLxksi1dK5xEn10Uv8h6rIRXwcrkvldXH97IkyRYV8a0OkyJRjJo ZxkHtxDKBM0dEmwLfZrwumi92EGagjkwyd0zh12FfMnbnTWWDL8lwGKXJ 6y8GJjbFiAJW+0QAOTfuprEAIO57ESbWRknpN08nRZlBgfyPqZU7gdlU5 A==; X-CSE-ConnectionGUID: FMJT2nGRSIeSpDULhM8j4Q== X-CSE-MsgGUID: teILT7hFRJOCrIvFZ1DWoA== X-IronPort-AV: E=McAfee;i="6800,10657,11913"; a="1317817" X-IronPort-AV: E=Sophos;i="6.27,118,1787036400"; d="scan'208";a="1317817" Received: from fmviesa005.fm.intel.com ([10.60.135.145]) by fmvoesa116.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 23 Sep 2026 01:40:21 -0700 X-CSE-ConnectionGUID: bk3jJ7lWTGGb4B0OyiF47A== X-CSE-MsgGUID: +trO+rgMS6WfbyEo3LMEYA== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.27,118,1787036400"; d="scan'208";a="281592688" Received: from gkdg2frd2754.igk.intel.com (HELO localhost) ([10.211.179.243]) by fmviesa005-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 23 Sep 2026 01:40:19 -0700 From: Klaus Gerlicher To: gdb-patches@sourceware.org Cc: christina.joos@intel.com, luis.machado.foss@gmail.com Subject: [PATCH v4 1/1] gdb/linespec: relax block filter to preserve distinct code paths Date: Wed, 23 Sep 2026 08:40:13 +0000 Message-Id: <20260923084013.419968-2-klaus.gerlicher@intel.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260923084013.419968-1-klaus.gerlicher@intel.com> References: <20260923084013.419968-1-klaus.gerlicher@intel.com> MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gdb-patches-bounces~public-inbox=simark.ca@sourceware.org From: "Gerlicher, Klaus" The block-based duplicate filter in create_sals_line_offset keeps only the first is_stmt line table entry per block. This heuristic reduces multiple for-loop header entries (init/test/increment on same line) to single breakpoint locations. However, it is too aggressive for vectorized loops, where the compiler emits separate code regions for the vectorized body and scalar remainder, both on the same source line. The filter discards the scalar remainder entry, causing breakpoints to be silently missed when only the scalar path executes. The old heuristic collapses all same-block SALs for a given line down to a single breakpoint location, while the new heuristic preserves additional SALs when the instruction range between them ends with an unconditional jump or return, indicating they lie on genuinely distinct, non-fall-through code paths. The fix introduces range_has_unconditional_flow_control, which analyzes the instruction stream for two conditions indicating distinct code paths: 1. Unconditional exit: The last instruction before the second PC is an unconditional jmp or ret (execution cannot fall through). 2. Backward branch: Any instruction in the range is a relative branch targeting before the first PC (loop back-edge indicating separate loop bodies). A new gdbarch method gdbarch_insn_branch_target decodes relative branch targets. Implemented for amd64 and aarch64; other architectures default to conservative behavior, preserving backward compatibility. Existing gdbarch_insn_is_jump and gdbarch_insn_is_ret are now also implemented for aarch64, enabling the heuristic on that platform too. Predicate functions are added to allow for chosing viability of this extended heuristic. Implemented on x86_64 and aarch64. Testing covers: - for_loop_empty_body (-O0): 1 location - for_loop_large_body (-O0): 1 location - for_loop_vectorized (-O2): >= 2 locations with runtime verification - nested_vectorized_loops (-O2): >= 2 locations - loop_with_early_break (-O2): >= 2 or 1 location - ifelse_separate_lines (-O0): 2 locations on x86_64/aarch64, unsupported elsewhere - set/show linespec-extended-block-filter reports on/off and architecture support correctly - add unit test for amd64_branch_target_from_details Tested with GCC 11 and ICX 2025.2 on x86_64, and on aarch64 (raspi5). --- gdb/NEWS | 11 + gdb/aarch64-tdep.c | 91 ++++++ gdb/amd64-tdep.c | 121 ++++++++ gdb/arch-utils.c | 10 + gdb/arch-utils.h | 1 + gdb/arch/aarch64-insn.c | 23 ++ gdb/arch/aarch64-insn.h | 2 + gdb/gdbarch-gen.c | 59 +++- gdb/gdbarch-gen.h | 16 + gdb/gdbarch_components.py | 18 ++ gdb/linespec.c | 148 ++++++++- .../gdb.linespec/block-filter-cases.c | 126 ++++++++ .../gdb.linespec/block-filter-cases.exp | 290 ++++++++++++++++++ 13 files changed, 911 insertions(+), 5 deletions(-) create mode 100644 gdb/testsuite/gdb.linespec/block-filter-cases.c create mode 100644 gdb/testsuite/gdb.linespec/block-filter-cases.exp diff --git a/gdb/NEWS b/gdb/NEWS index 77ad2d3fc22..3ed7f2b1883 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -122,6 +122,17 @@ info locals This fixes an issue where GDB could fail to find a type when relying on the index. Any existing indexes should be regenerated. +* Breakpoint location resolution has been improved for vectorized loops. + Previously, when setting a breakpoint on a source line with vectorized + code (at -O2 with -ftree-vectorize), GDB's block filter heuristic would + discard the scalar remainder location, causing breakpoints to be missed + when only the scalar path executed. GDB now correctly preserves multiple + locations for vectorized and scalar loop paths, improving debuggability + of optimized code. This is supported currently on x86 and aarch64. + + Use "set linespec-extended-block-filter off" to disable the new + heuristic on supported platforms and revert to the original behavior if needed. + * Support for Floating Point Mode Register (FPMR) in AArch64. * When running on the Windows Terminal console, GDB now supports diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c index 950ad4f6aae..3c5be6af3d6 100644 --- a/gdb/aarch64-tdep.c +++ b/gdb/aarch64-tdep.c @@ -4437,6 +4437,92 @@ aarch64_initialize_sme_pseudo_names (struct gdbarch *gdbarch, } } +/* Return the statically-known target of a relative branch instruction. + Handles B , B.cond , CBZ/CBNZ , TBZ/TBNZ . + Returns std::nullopt for indirect branches (BR) or unreadable memory. */ + +static std::optional +aarch64_insn_branch_target (struct gdbarch *gdbarch, CORE_ADDR addr) +{ + gdb_byte buf[4]; + + if (target_read_memory (addr, buf, 4) != 0) + return std::nullopt; + + enum bfd_endian byte_order = gdbarch_byte_order_for_code (gdbarch); + uint32_t insn = extract_unsigned_integer (buf, 4, byte_order); + + int is_bl; + int32_t offset; + + /* B (but not BL, which is a call). */ + if (aarch64_decode_b (addr, insn, &is_bl, &offset) && !is_bl) + return addr + offset; + + /* B.cond . */ + unsigned cond; + if (aarch64_decode_bcond (addr, insn, &cond, &offset)) + return addr + offset; + + /* CBZ/CBNZ . */ + int is64, is_cbnz; + unsigned rn; + if (aarch64_decode_cb (addr, insn, &is64, &is_cbnz, &rn, &offset)) + return addr + offset; + + /* TBZ/TBNZ . */ + int is_tbnz; + unsigned bit, rt; + if (aarch64_decode_tb (addr, insn, &is_tbnz, &bit, &rt, &offset)) + return addr + offset; + + return std::nullopt; +} + +/* Return true if the instruction at ADDR is an unconditional branch. + Handles direct B and indirect BR . */ + +static bool +aarch64_insn_is_jump (struct gdbarch *gdbarch, CORE_ADDR addr) +{ + gdb_byte buf[4]; + + if (target_read_memory (addr, buf, 4) != 0) + return false; + + enum bfd_endian byte_order = gdbarch_byte_order_for_code (gdbarch); + uint32_t insn = extract_unsigned_integer (buf, 4, byte_order); + + /* B (but not BL, which is a call). */ + int is_bl; + int32_t offset; + if (aarch64_decode_b (addr, insn, &is_bl, &offset) && !is_bl) + return true; + + /* BR : bits[31:10] fixed, bits[9:5] = Xn, bits[4:0] = 0. + No shared decoder exists for indirect branches. */ + if ((insn & 0xFFFFFC1F) == 0xD61F0000) + return true; + + return false; +} + +/* Return true if the instruction at ADDR is a return. */ + +static bool +aarch64_insn_is_ret (struct gdbarch *gdbarch, CORE_ADDR addr) +{ + gdb_byte buf[4]; + + if (target_read_memory (addr, buf, 4) != 0) + return false; + + enum bfd_endian byte_order = gdbarch_byte_order_for_code (gdbarch); + uint32_t insn = extract_unsigned_integer (buf, 4, byte_order); + + return aarch64_decode_ret (addr, insn); +} + /* Initialize the current architecture based on INFO. If possible, reuse an architecture from ARCHES, which is a list of architectures already created during this debugging session. @@ -4865,6 +4951,11 @@ aarch64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) set_gdbarch_program_breakpoint_here_p (gdbarch, aarch64_program_breakpoint_here_p); + /* Instruction stream analysis. */ + set_gdbarch_insn_is_jump (gdbarch, aarch64_insn_is_jump); + set_gdbarch_insn_is_ret (gdbarch, aarch64_insn_is_ret); + set_gdbarch_insn_branch_target (gdbarch, aarch64_insn_branch_target); + /* Add some default predicates. */ frame_unwind_append_unwinder (gdbarch, &aarch64_stub_unwind); dwarf2_append_unwinders (gdbarch); diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c index a982e610642..5b76469e40a 100644 --- a/gdb/amd64-tdep.c +++ b/gdb/amd64-tdep.c @@ -1822,6 +1822,69 @@ amd64_insn_is_jump (struct gdbarch *gdbarch, CORE_ADDR addr) return amd64_classify_insn_at (gdbarch, addr, amd64_jmp_p); } +/* Return the statically-known target of a relative branch instruction + (conditional Jcc, JMP, LOOP, JCXZ) whose already-decoded DETAILS starts + at ADDR. Return std::nullopt for non-branch or indirect branches. */ + +static std::optional +amd64_branch_target_from_details (const struct amd64_insn &details, + CORE_ADDR addr) +{ + const gdb_byte *insn = &details.raw_insn[details.opcode_offset]; + CORE_ADDR opcode_addr = addr + details.opcode_offset; + const gdb_byte opc = insn[0]; + + /* Short conditional Jcc: 7x cc (2 bytes, signed 8-bit displacement). */ + if ((opc & 0xf0) == 0x70) + return opcode_addr + 2 + (LONGEST) (signed char) insn[1]; + + /* Short unconditional JMP: EB cc (2 bytes). */ + if (opc == 0xeb) + return opcode_addr + 2 + (LONGEST) (signed char) insn[1]; + + /* LOOP/LOOPE/LOOPNE: E2/E1/E0 cc (2 bytes, signed 8-bit displacement). */ + if ((opc & 0xfd) == 0xe0) + return opcode_addr + 2 + (LONGEST) (signed char) insn[1]; + + /* JCXZ/JECXZ/JRCXZ: E3 cc (2 bytes, signed 8-bit displacement). */ + if (opc == 0xe3) + return opcode_addr + 2 + (LONGEST) (signed char) insn[1]; + + /* Near conditional Jcc: 0F 8x cc cc cc cc (6 bytes, signed 32-bit). */ + if (opc == 0x0f && (insn[1] & 0xf0) == 0x80) + { + int32_t disp; + memcpy (&disp, insn + 2, sizeof (disp)); + return opcode_addr + 6 + (LONGEST) disp; + } + + /* Near unconditional JMP: E9 cc cc cc cc (5 bytes, signed 32-bit). */ + if (opc == 0xe9) + { + int32_t disp; + memcpy (&disp, insn + 1, sizeof (disp)); + return opcode_addr + 5 + (LONGEST) disp; + } + + /* Indirect or far branches: target cannot be determined statically. */ + return std::nullopt; +} + +/* The gdbarch insn_branch_target method. Throws if memory cannot be + read. */ + +static std::optional +amd64_insn_branch_target (struct gdbarch *gdbarch, CORE_ADDR addr) +{ + struct amd64_insn details; + + gdb::byte_vector buf (gdbarch_max_insn_length (gdbarch)); + read_code (addr, buf.data (), buf.size ()); + amd64_get_insn_details (buf.data (), &details); + + return amd64_branch_target_from_details (details, addr); +} + /* Fix up the state of registers and memory after having single-stepped a displaced instruction. */ @@ -3643,6 +3706,7 @@ amd64_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch, set_gdbarch_insn_is_call (gdbarch, amd64_insn_is_call); set_gdbarch_insn_is_ret (gdbarch, amd64_insn_is_ret); set_gdbarch_insn_is_jump (gdbarch, amd64_insn_is_jump); + set_gdbarch_insn_branch_target (gdbarch, amd64_insn_branch_target); set_gdbarch_in_indirect_branch_thunk (gdbarch, amd64_in_indirect_branch_thunk); @@ -3943,11 +4007,68 @@ test_amd64_get_insn_details (void) SELF_CHECK (insn == updated_insn); } +/* Test amd64_branch_target_from_details. */ + +static void +test_amd64_branch_target_from_details (void) +{ + struct amd64_insn details; + + /* INSN: je +5, short conditional Jcc. */ + gdb::byte_vector insn = { 0x74, 0x05 }; + amd64_get_insn_details (insn.data (), &details); + SELF_CHECK (amd64_branch_target_from_details (details, 0x1000) + == 0x1007); + + /* INSN: jmp -2, short unconditional JMP (backward, targets itself). */ + insn = { 0xeb, 0xfe }; + amd64_get_insn_details (insn.data (), &details); + SELF_CHECK (amd64_branch_target_from_details (details, 0x2000) + == 0x2000); + + /* INSN: loop -16. */ + insn = { 0xe2, 0xf0 }; + amd64_get_insn_details (insn.data (), &details); + SELF_CHECK (amd64_branch_target_from_details (details, 0x3000) + == 0x2ff2); + + /* INSN: jrcxz +16. */ + insn = { 0xe3, 0x10 }; + amd64_get_insn_details (insn.data (), &details); + SELF_CHECK (amd64_branch_target_from_details (details, 0x4000) + == 0x4012); + + /* INSN: je +100, near conditional Jcc. */ + insn = { 0x0f, 0x84, 0x64, 0x00, 0x00, 0x00 }; + amd64_get_insn_details (insn.data (), &details); + SELF_CHECK (amd64_branch_target_from_details (details, 0x5000) + == 0x506a); + + /* INSN: jmp -20, near unconditional JMP. */ + insn = { 0xe9, 0xec, 0xff, 0xff, 0xff }; + amd64_get_insn_details (insn.data (), &details); + SELF_CHECK (amd64_branch_target_from_details (details, 0x6000) + == 0x5ff1); + + /* INSN: jmp *%rax, indirect JMP. Target cannot be known statically. */ + insn = { 0xff, 0xe0 }; + amd64_get_insn_details (insn.data (), &details); + SELF_CHECK (!amd64_branch_target_from_details (details, 0x7000) + .has_value ()); + + /* INSN: nop, not a branch at all. */ + insn = { 0x90 }; + amd64_get_insn_details (insn.data (), &details); + SELF_CHECK (!amd64_branch_target_from_details (details, 0x8000) + .has_value ()); +} + static void amd64_insn_decode (void) { test_vex2_to_vex3 (); test_amd64_get_insn_details (); + test_amd64_branch_target_from_details (); } } // namespace selftests diff --git a/gdb/arch-utils.c b/gdb/arch-utils.c index 473a352e781..c6368a547e0 100644 --- a/gdb/arch-utils.c +++ b/gdb/arch-utils.c @@ -45,6 +45,8 @@ #include "dis-asm.h" +#include + bool default_displaced_step_hw_singlestep (struct gdbarch *gdbarch) { @@ -941,6 +943,14 @@ default_insn_is_jump (struct gdbarch *gdbarch, CORE_ADDR addr) /* See arch-utils.h. */ +std::optional +default_insn_branch_target (struct gdbarch *gdbarch, CORE_ADDR addr) +{ + return std::nullopt; +} + +/* See arch-utils.h. */ + bool default_program_breakpoint_here_p (struct gdbarch *gdbarch, CORE_ADDR address) diff --git a/gdb/arch-utils.h b/gdb/arch-utils.h index 06902050043..46d9d89e668 100644 --- a/gdb/arch-utils.h +++ b/gdb/arch-utils.h @@ -327,6 +327,7 @@ extern bool default_return_in_first_hidden_param_p (struct gdbarch *, extern bool default_insn_is_call (struct gdbarch *, CORE_ADDR); extern bool default_insn_is_ret (struct gdbarch *, CORE_ADDR); extern bool default_insn_is_jump (struct gdbarch *, CORE_ADDR); +extern std::optional default_insn_branch_target (struct gdbarch *, CORE_ADDR); /* Default implementation of gdbarch_program_breakpoint_here_p. */ extern bool default_program_breakpoint_here_p (struct gdbarch *gdbarch, diff --git a/gdb/arch/aarch64-insn.c b/gdb/arch/aarch64-insn.c index f6089092eca..3ab28e6dd4b 100644 --- a/gdb/arch/aarch64-insn.c +++ b/gdb/arch/aarch64-insn.c @@ -201,6 +201,29 @@ aarch64_decode_tb (CORE_ADDR addr, uint32_t insn, int *is_tbnz, return 0; } +/* Decode an opcode if it represents a RET instruction. + + ADDR specifies the address of the opcode. + INSN specifies the opcode to test. + + Return 1 if the opcodes matches and is decoded, otherwise 0. */ + +int +aarch64_decode_ret (CORE_ADDR addr, uint32_t insn) +{ + /* ret b1101 0110 0100 nnnn n000 0000 0000 0000 */ + if (decode_masked_match (insn, 0xFFE0FFFF, 0xD6400000)) + { + unsigned int rn = (insn >> 15) & 0x1f; + + aarch64_debug_printf ("decode: 0x%s 0x%x x%u", + core_addr_to_string_nz (addr), insn, + rn); + return 1; + } + return 0; +} + /* Decode an opcode if it represents an LDR or LDRSW instruction taking a literal offset from the current PC. diff --git a/gdb/arch/aarch64-insn.h b/gdb/arch/aarch64-insn.h index 428e53c3858..aafbe3c0d6b 100644 --- a/gdb/arch/aarch64-insn.h +++ b/gdb/arch/aarch64-insn.h @@ -200,6 +200,8 @@ int aarch64_decode_tb (CORE_ADDR addr, uint32_t insn, int *is_tbnz, int aarch64_decode_ldr_literal (CORE_ADDR addr, uint32_t insn, int *is_w, int *is64, unsigned *rt, int32_t *offset); +int aarch64_decode_ret (CORE_ADDR addr, uint32_t insn); + /* Data passed to each method of aarch64_insn_visitor. */ struct aarch64_insn_data diff --git a/gdb/gdbarch-gen.c b/gdb/gdbarch-gen.c index 6008003466c..a0982b12993 100644 --- a/gdb/gdbarch-gen.c +++ b/gdb/gdbarch-gen.c @@ -234,6 +234,7 @@ struct gdbarch gdbarch_insn_is_call_ftype *insn_is_call = default_insn_is_call; gdbarch_insn_is_ret_ftype *insn_is_ret = default_insn_is_ret; gdbarch_insn_is_jump_ftype *insn_is_jump = default_insn_is_jump; + gdbarch_insn_branch_target_ftype *insn_branch_target = default_insn_branch_target; gdbarch_program_breakpoint_here_p_ftype *program_breakpoint_here_p = default_program_breakpoint_here_p; gdbarch_auxv_parse_ftype *auxv_parse = nullptr; gdbarch_print_auxv_entry_ftype *print_auxv_entry = default_print_auxv_entry; @@ -492,8 +493,9 @@ verify_gdbarch (struct gdbarch *gdbarch) /* Skip verify of core_info_proc, has predicate. */ /* Skip verify of ravenscar_ops, invalid_p == 0. */ /* Skip verify of insn_is_call, invalid_p == 0. */ - /* Skip verify of insn_is_ret, invalid_p == 0. */ - /* Skip verify of insn_is_jump, invalid_p == 0. */ + /* Skip verify of insn_is_ret, has predicate. */ + /* Skip verify of insn_is_jump, has predicate. */ + /* Skip verify of insn_branch_target, has predicate. */ /* Skip verify of program_breakpoint_here_p, invalid_p == 0. */ /* Skip verify of auxv_parse, has predicate. */ /* Skip verify of print_auxv_entry, invalid_p == 0. */ @@ -1270,12 +1272,24 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file) gdb_printf (file, "gdbarch_dump: insn_is_call = <%s>\n", host_address_to_string (gdbarch->insn_is_call)); + gdb_printf (file, + "gdbarch_dump: gdbarch_insn_is_ret_p() = %d\n", + gdbarch_insn_is_ret_p (gdbarch)); gdb_printf (file, "gdbarch_dump: insn_is_ret = <%s>\n", host_address_to_string (gdbarch->insn_is_ret)); + gdb_printf (file, + "gdbarch_dump: gdbarch_insn_is_jump_p() = %d\n", + gdbarch_insn_is_jump_p (gdbarch)); gdb_printf (file, "gdbarch_dump: insn_is_jump = <%s>\n", host_address_to_string (gdbarch->insn_is_jump)); + gdb_printf (file, + "gdbarch_dump: gdbarch_insn_branch_target_p() = %d\n", + gdbarch_insn_branch_target_p (gdbarch)); + gdb_printf (file, + "gdbarch_dump: insn_branch_target = <%s>\n", + host_address_to_string (gdbarch->insn_branch_target)); gdb_printf (file, "gdbarch_dump: program_breakpoint_here_p = <%s>\n", host_address_to_string (gdbarch->program_breakpoint_here_p)); @@ -4916,11 +4930,19 @@ set_gdbarch_insn_is_call (struct gdbarch *gdbarch, gdbarch->insn_is_call = insn_is_call; } +bool +gdbarch_insn_is_ret_p (struct gdbarch *gdbarch) +{ + gdb_assert (gdbarch != nullptr); + return gdbarch->insn_is_ret != default_insn_is_ret; +} + bool gdbarch_insn_is_ret (struct gdbarch *gdbarch, CORE_ADDR addr) { gdb_assert (gdbarch != nullptr); gdb_assert (gdbarch->insn_is_ret != nullptr); + /* Do not check predicate: gdbarch->insn_is_ret != default_insn_is_ret, allow call. */ if (gdbarch_debug >= 2) gdb_printf (gdb_stdlog, "gdbarch_insn_is_ret called\n"); return gdbarch->insn_is_ret (gdbarch, addr); @@ -4933,11 +4955,19 @@ set_gdbarch_insn_is_ret (struct gdbarch *gdbarch, gdbarch->insn_is_ret = insn_is_ret; } +bool +gdbarch_insn_is_jump_p (struct gdbarch *gdbarch) +{ + gdb_assert (gdbarch != nullptr); + return gdbarch->insn_is_jump != default_insn_is_jump; +} + bool gdbarch_insn_is_jump (struct gdbarch *gdbarch, CORE_ADDR addr) { gdb_assert (gdbarch != nullptr); gdb_assert (gdbarch->insn_is_jump != nullptr); + /* Do not check predicate: gdbarch->insn_is_jump != default_insn_is_jump, allow call. */ if (gdbarch_debug >= 2) gdb_printf (gdb_stdlog, "gdbarch_insn_is_jump called\n"); return gdbarch->insn_is_jump (gdbarch, addr); @@ -4950,6 +4980,31 @@ set_gdbarch_insn_is_jump (struct gdbarch *gdbarch, gdbarch->insn_is_jump = insn_is_jump; } +bool +gdbarch_insn_branch_target_p (struct gdbarch *gdbarch) +{ + gdb_assert (gdbarch != nullptr); + return gdbarch->insn_branch_target != default_insn_branch_target; +} + +std::optional +gdbarch_insn_branch_target (struct gdbarch *gdbarch, CORE_ADDR addr) +{ + gdb_assert (gdbarch != nullptr); + gdb_assert (gdbarch->insn_branch_target != nullptr); + /* Do not check predicate: gdbarch->insn_branch_target != default_insn_branch_target, allow call. */ + if (gdbarch_debug >= 2) + gdb_printf (gdb_stdlog, "gdbarch_insn_branch_target called\n"); + return gdbarch->insn_branch_target (gdbarch, addr); +} + +void +set_gdbarch_insn_branch_target (struct gdbarch *gdbarch, + gdbarch_insn_branch_target_ftype insn_branch_target) +{ + gdbarch->insn_branch_target = insn_branch_target; +} + bool gdbarch_program_breakpoint_here_p (struct gdbarch *gdbarch, CORE_ADDR address) { diff --git a/gdb/gdbarch-gen.h b/gdb/gdbarch-gen.h index 6eda8693d58..f35706bd3b2 100644 --- a/gdb/gdbarch-gen.h +++ b/gdb/gdbarch-gen.h @@ -1588,16 +1588,32 @@ void set_gdbarch_insn_is_call (struct gdbarch *gdbarch, gdbarch_insn_is_call_fty /* Return true if the instruction at ADDR is a return; false otherwise. */ +bool gdbarch_insn_is_ret_p (struct gdbarch *gdbarch); + using gdbarch_insn_is_ret_ftype = bool (struct gdbarch *gdbarch, CORE_ADDR addr); bool gdbarch_insn_is_ret (struct gdbarch *gdbarch, CORE_ADDR addr); void set_gdbarch_insn_is_ret (struct gdbarch *gdbarch, gdbarch_insn_is_ret_ftype *insn_is_ret); /* Return true if the instruction at ADDR is a jump; false otherwise. */ +bool gdbarch_insn_is_jump_p (struct gdbarch *gdbarch); + using gdbarch_insn_is_jump_ftype = bool (struct gdbarch *gdbarch, CORE_ADDR addr); bool gdbarch_insn_is_jump (struct gdbarch *gdbarch, CORE_ADDR addr); void set_gdbarch_insn_is_jump (struct gdbarch *gdbarch, gdbarch_insn_is_jump_ftype *insn_is_jump); +/* For a relative branch instruction at ADDR (conditional or + unconditional), return the statically-known branch target address. + Return std::nullopt for non-branch instructions, indirect branches, + or when reading memory fails. This is used to detect backward + loop back-edges during DWARF line-table analysis. */ + +bool gdbarch_insn_branch_target_p (struct gdbarch *gdbarch); + +using gdbarch_insn_branch_target_ftype = std::optional (struct gdbarch *gdbarch, CORE_ADDR addr); +std::optional gdbarch_insn_branch_target (struct gdbarch *gdbarch, CORE_ADDR addr); +void set_gdbarch_insn_branch_target (struct gdbarch *gdbarch, gdbarch_insn_branch_target_ftype *insn_branch_target); + /* Return true if there's a program/permanent breakpoint planted in memory at ADDRESS, return false otherwise. */ diff --git a/gdb/gdbarch_components.py b/gdb/gdbarch_components.py index d8b2d114909..4791111088f 100644 --- a/gdb/gdbarch_components.py +++ b/gdb/gdbarch_components.py @@ -2517,6 +2517,7 @@ Return true if the instruction at ADDR is a return; false otherwise. type="bool", name="insn_is_ret", params=[("CORE_ADDR", "addr")], + predicate=True, predefault="default_insn_is_ret", invalid=False, ) @@ -2528,10 +2529,27 @@ Return true if the instruction at ADDR is a jump; false otherwise. type="bool", name="insn_is_jump", params=[("CORE_ADDR", "addr")], + predicate=True, predefault="default_insn_is_jump", invalid=False, ) +Method( + comment=""" +For a relative branch instruction at ADDR (conditional or +unconditional), return the statically-known branch target address. +Return std::nullopt for non-branch instructions, indirect branches, +or when reading memory fails. This is used to detect backward +loop back-edges during DWARF line-table analysis. +""", + type="std::optional", + name="insn_branch_target", + params=[("CORE_ADDR", "addr")], + predicate=True, + predefault="default_insn_branch_target", + invalid=False, +) + Method( comment=""" Return true if there's a program/permanent breakpoint planted in diff --git a/gdb/linespec.c b/gdb/linespec.c index 95ad0b048d2..c550f2c2c05 100644 --- a/gdb/linespec.c +++ b/gdb/linespec.c @@ -44,9 +44,12 @@ #include "gdbsupport/function-view.h" #include "gdbsupport/def-vector.h" #include +#include "disasm.h" +#include "gdbarch.h" #include "inferior.h" #include "gdbsupport/unordered_set.h" #include "cli/cli-style.h" +#include "cli/cli-cmds.h" /* An enumeration of the various things a user might attempt to complete for a linespec location. */ @@ -432,6 +435,9 @@ static bool compare_msymbols (const bound_minimal_symbol &a, previous parser. */ static const char linespec_quote_characters[] = "\"\'"; +/* Control whether the extended block-filter heuristic is enabled. */ +static bool debug_linespec_extended_block_filter = true; + /* Lexer functions. */ /* Lex a number from the input in PARSER. This only supports @@ -1982,6 +1988,52 @@ canonicalize_linespec (struct linespec_state *state, const linespec *ls) explicit_loc->set_string (explicit_loc->to_linespec ()); } +/* Return true if PC_TO is unreachable by fall-through from PC_FROM. + Two conditions are sufficient: (1) any ret in [PC_FROM, PC_TO), or + (2) unconditional jump immediately before PC_TO. Only the last + instruction before PC_TO matters; mid-range branches are ignored to + preserve for-loop deduplication. */ + +static bool +range_has_unconditional_flow_control (struct gdbarch *gdbarch, + CORE_ADDR pc_from, CORE_ADDR pc_to) +{ + /* Return true if: last instruction before PC_TO is unconditional + jmp/ret, or any instruction in range is a backward branch + (indicating separate loop bodies). */ + + CORE_ADDR pc = pc_from; + + while (pc < pc_to) + { + try + { + int len = gdb_insn_length (gdbarch, pc); + if (len <= 0) + return false; + + /* Check for backward branch (loop back-edge). */ + std::optional target + = gdbarch_insn_branch_target (gdbarch, pc); + if (target.has_value () && target.value () < pc_from) + return true; + + /* Is this the last instruction before PC_TO? */ + if (pc + len >= pc_to) + return gdbarch_insn_is_jump (gdbarch, pc) + || gdbarch_insn_is_ret (gdbarch, pc); + + pc += len; + } + catch (const gdb_exception_error &) + { + return false; + } + } + + return false; +} + /* Given a line offset in LS, construct the relevant SALs. */ static std::vector @@ -2080,13 +2132,33 @@ create_sals_line_offset (struct linespec_state *self, for (i = 0; i < intermediate_results.size (); ++i) { - if (blocks[i] != NULL) + if (blocks[i] != NULL && filter[i]) for (j = i + 1; j < intermediate_results.size (); ++j) { - if (blocks[j] == blocks[i]) + if (blocks[j] == blocks[i] && filter[j]) { + const symtab_and_line &sal_i = intermediate_results[i]; + const symtab_and_line &sal_j = intermediate_results[j]; + + auto [pc_lo, pc_hi] = std::minmax (sal_i.pc, sal_j.pc); + + /* Without symtab info, don't filter - we can't reliably + determine if these are distinct code paths. */ + if (sal_i.symtab == nullptr || sal_j.symtab != sal_i.symtab) + continue; + + /* Check for unconditional flow control in the range + (extended heuristic; can be disabled). Memory read + failures are handled gracefully inside the helper. */ + objfile *ofile = sal_i.symtab->compunit ().objfile (); + gdbarch *gdbarch = ofile->arch (); + + if (debug_linespec_extended_block_filter + && range_has_unconditional_flow_control (gdbarch, + pc_lo, pc_hi)) + continue; + filter[j] = 0; - break; } } } @@ -4339,3 +4411,73 @@ get_gdb_linespec_parser_quote_characters (void) { return linespec_quote_characters; } + +/* Return true if architecture implements the insn_is_jump/insn_branch_target + methods for the extended block-filter heuristic. */ + +static bool +extended_block_filter_supported (struct gdbarch *gdbarch) +{ + return (gdbarch_insn_is_jump_p (gdbarch) + && gdbarch_insn_is_ret_p(gdbarch) + && gdbarch_insn_branch_target_p (gdbarch)); +} + +/* Callback for "show linespec-extended-block-filter". Distinguishes + the user's requested setting from whether it actually has any + effect on the current architecture. */ + +static void +show_debug_linespec_extended_block_filter (struct ui_file *file, + int from_tty, + struct cmd_list_element *c, + const char *value) +{ + struct gdbarch *gdbarch = current_inferior ()->arch (); + + gdb_printf (file, + _("The extended block-filter heuristic is set to %s " + "(architecture support for %s: %s).\n"), + value, + gdbarch_bfd_arch_info (gdbarch)->printable_name, + extended_block_filter_supported (gdbarch) ? "yes" : "no"); +} + +/* Callback for "set linespec-extended-block-filter". Warn when + turning the setting on has no effect because the current + architecture doesn't implement the heuristic. */ + +static void +set_debug_linespec_extended_block_filter (const char *args, int from_tty, + struct cmd_list_element *c) +{ + struct gdbarch *gdbarch = current_inferior ()->arch (); + + if (debug_linespec_extended_block_filter + && !extended_block_filter_supported (gdbarch)) + warning (_("the extended block-filter heuristic is not implemented " + "for %s; breakpoint location resolution will keep using " + "the original heuristic"), + gdbarch_bfd_arch_info (gdbarch)->printable_name); +} + +/* Register linespec debugging commands. */ + +INIT_GDB_FILE (linespec) +{ + add_setshow_boolean_cmd ("linespec-extended-block-filter", + class_support, + &debug_linespec_extended_block_filter, _("\ +Set whether the extended block-filter heuristic is enabled."), _("\ +Show whether the extended block-filter heuristic is enabled."), _("\ +The extended heuristic improves breakpoint location resolution for\n\ +vectorized loops at -O2. When off, breakpoints use only the original\n\ +heuristic, which may miss locations in vectorized code. Only\n\ +supported on certain architectures; use \"show\n\ +linespec-extended-block-filter\" to check whether the current\n\ +architecture supports it. On others this setting has no effect.\n\ +Default is on."), + set_debug_linespec_extended_block_filter, + show_debug_linespec_extended_block_filter, + &setlist, &showlist); +} diff --git a/gdb/testsuite/gdb.linespec/block-filter-cases.c b/gdb/testsuite/gdb.linespec/block-filter-cases.c new file mode 100644 index 00000000000..8cf834e95f4 --- /dev/null +++ b/gdb/testsuite/gdb.linespec/block-filter-cases.c @@ -0,0 +1,126 @@ +/* Test cases for the block-filter heuristic in create_sals_line_offset. + + Copyright (C) 2026 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + One function per tested pattern. The accompanying .exp file verifies + the breakpoint location count for each pattern and, where vectorisation + is present, confirms that both the vectorized and scalar-remainder paths + are reachable at runtime. + + argc > 1 -> for_loop_vectorized called with n=1024 (vectorized path) + argc == 1 -> for_loop_vectorized called with n=3 (scalar path only) */ + +volatile int sink; + +/* for-loop with empty body (-O0). Expected: 1 location. */ + +void __attribute__ ((noinline)) +for_loop_empty_body (int n) +{ + for (int i = 0; i < n; i++) {} /* empty-for-line */ +} + +/* for-loop with large body (-O0). Expected: 1 location. */ + +void __attribute__ ((noinline)) +for_loop_large_body (volatile int *a, volatile int *b, volatile int *c, + int n) +{ + for (int i = 0; i < n; i++) { /* large-body-for-line */ + sink = a[i] * 3 + b[i]; + sink = sink ^ (sink >> 5); + sink += c[i] * 7 - a[i]; + sink = (sink << 3) | (sink >> 29); + sink += a[i] + b[i] + c[i]; + sink = sink * sink + a[i]; + sink ^= b[i] * c[i]; + sink += (a[i] | b[i]) & c[i]; + sink = sink - a[i] * b[i]; + sink += c[i] ^ (a[i] + b[i]); + sink += a[i] * 5 - b[i] * 3; + sink ^= (sink << 7) | (sink >> 25); + sink += c[i] * c[i] + a[i]; + sink = sink / (a[i] + 1); + sink += b[i] * b[i] - c[i]; + sink ^= a[i] ^ b[i] ^ c[i]; + sink += (a[i] + b[i]) * (b[i] + c[i]); + sink = (sink >> 3) + a[i]; + sink += c[i] * (a[i] - b[i]); + sink ^= sink >> 1; + } +} + +/* Vectorized for-loop (-O2). Expected: >= 2 locations. */ + +void __attribute__ ((noinline)) +for_loop_vectorized (int *arr, int n) +{ + for (int i = 0; i < n; i++) + arr[i] = i * 3; /* vec-loop-line */ +} + +/* Nested vectorized for-loops (-O2). Expected: >= 2 locations. */ + +void __attribute__ ((noinline)) +nested_vectorized_loops (int *arr, int n) +{ + for (int i = 0; i < n; i++) + for (int j = 0; j < n; j++) + arr[i * n + j] = i * j; /* nested-vec-line */ +} + +/* For-loop with early break (-O2). Expected: >= 2 or 1 location. */ + +void __attribute__ ((noinline)) +loop_with_early_break (int *arr, int n, int target) +{ + for (int i = 0; i < n; i++) { + arr[i] = i * 2; /* break-loop-line */ + if (arr[i] == target) + break; + } +} + +/* If-else with both branches on same line (-O0). Expected: 2 locations. */ + +void __attribute__ ((noinline)) +ifelse_separate_lines (int x, int *result) +{ + if (x > 0) + *result = x * 2; else *result = -x; /* ifelse-line */ +} + + +int +main (int argc, char *argv[]) +{ + int arr[1024]; + volatile int a[4] = {1, 2, 3, 4}; + volatile int b[4] = {4, 3, 2, 1}; + volatile int c[4] = {1, 1, 1, 1}; + int result = 0; + + for_loop_empty_body (4); + for_loop_large_body (a, b, c, 4); + for_loop_vectorized (arr, argc > 1 ? 1024 : 3); + nested_vectorized_loops (arr, argc > 1 ? 32 : 3); + loop_with_early_break (arr, argc > 1 ? 1024 : 3, 42); + ifelse_separate_lines (argc > 1 ? 10 : -5, &result); + + return 0; +} diff --git a/gdb/testsuite/gdb.linespec/block-filter-cases.exp b/gdb/testsuite/gdb.linespec/block-filter-cases.exp new file mode 100644 index 00000000000..7e4bdcab17f --- /dev/null +++ b/gdb/testsuite/gdb.linespec/block-filter-cases.exp @@ -0,0 +1,290 @@ +# Copyright (C) 2026 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# Verify the block-filter heuristic against known patterns. The compiler is +# selected by the board file; this test uses consistent expectations across +# compilers (GCC, ICX, etc.). + +standard_testfile + +if { [build_executable "failed to prepare (O0)" \ + "${testfile}-O0" ${srcfile} { debug optimize=-O0 }] } { + return +} + +if { [build_executable "failed to prepare (O2)" \ + "${testfile}-O2" ${srcfile} \ + { debug optimize=-O2 additional_flags=-ftree-vectorize }] } { + return +} + +# Retrieve all target line numbers from the single shared source file. +set line_empty_for [gdb_get_line_number "empty-for-line" ${srcfile}] +set line_large_body_for [gdb_get_line_number "large-body-for-line" ${srcfile}] +set line_vec_loop [gdb_get_line_number "vec-loop-line" ${srcfile}] +set line_nested_vec [gdb_get_line_number "nested-vec-line" ${srcfile}] +set line_break_loop [gdb_get_line_number "break-loop-line" ${srcfile}] +set line_ifelse [gdb_get_line_number "ifelse-line" ${srcfile}] + +# Return true if the extended block-filter heuristic is actually +# implemented for the current architecture. +proc block_filter_arch_supported { } { + set supported 0 + gdb_test_multiple "show linespec-extended-block-filter" \ + "query extended block-filter architecture support" { + -re -wrap "architecture support for \[^\r\n\]+: yes\\)\\." { + set supported 1 + pass $gdb_test_name + } + -re -wrap "architecture support for \[^\r\n\]+: no\\)\\." { + pass $gdb_test_name + } + } + return $supported +} + +# Turn the extended block-filter heuristic on. +proc enable_extended_block_filter { desc } { + gdb_test "set linespec-extended-block-filter on" \ + "(warning: the extended block-filter heuristic is not implemented\ + for \[^\r\n\]+; breakpoint location resolution will keep using\ + the original heuristic)?" \ + $desc +} + +# Set breakpoint and return how many locations it resolves to. +proc bp_nlocs { srcfile line desc } { + set nlocs -1 + gdb_test_multiple "break ${srcfile}:${line}" $desc { + -re -wrap "Breakpoint \[0-9\]+ at .*\\((\[0-9\]+) locations\\).*" { + set nlocs $expect_out(1,string) + pass $desc + } + -re -wrap "Breakpoint \[0-9\]+ at .*" { + set nlocs 1 + pass $desc + } + } + return $nlocs +} + +clean_restart "${testfile}-O0" + +# Verify the linespec-extended-block-filter setting exists and works. +gdb_test "show linespec-extended-block-filter" \ + "The extended block-filter heuristic is set to on\ + \\(architecture support for \[^\r\n\]+: (yes|no)\\)\\." \ + "show default linespec-extended-block-filter (on)" + +gdb_test_no_output "set linespec-extended-block-filter off" \ + "set linespec-extended-block-filter off" + +gdb_test "show linespec-extended-block-filter" \ + "The extended block-filter heuristic is set to off\ + \\(architecture support for \[^\r\n\]+: (yes|no)\\)\\." \ + "show linespec-extended-block-filter (off)" + +# Re-enable: on a supported arch this must be silent; on an unsupported +# arch prints an informational warning. +if { [block_filter_arch_supported] } { + gdb_test_no_output "set linespec-extended-block-filter on" \ + "set on produces no warning on supported arch" +} else { + enable_extended_block_filter "set linespec-extended-block-filter on" +} + +gdb_test "show linespec-extended-block-filter" \ + "The extended block-filter heuristic is set to on\ + \\(architecture support for \[^\r\n\]+: (yes|no)\\)\\." \ + "show linespec-extended-block-filter re-enabled" + +with_test_prefix "for-loop empty body (-O0)" { + set nlocs [bp_nlocs ${srcfile} ${line_empty_for} "set breakpoint"] + + if { $nlocs == 1 } { + pass "1 breakpoint location (correct)" + } else { + fail "expected 1 location, got $nlocs" + } + + delete_breakpoints +} + +with_test_prefix "for-loop exceeding scan limit (-O0)" { + set nlocs [bp_nlocs ${srcfile} ${line_large_body_for} "set breakpoint"] + + if { $nlocs == 1 } { + pass "1 breakpoint location (correct)" + } else { + fail "expected 1 location, got $nlocs" + } + + delete_breakpoints +} + +with_test_prefix "if-else separate lines (-O0)" { + set nlocs [bp_nlocs ${srcfile} ${line_ifelse} "set breakpoint"] + + if { $nlocs < 2 } { + if { [block_filter_arch_supported] } { + fail "expected 2 locations, got $nlocs" + } else { + unsupported "current arch lacks insn_is_jump support\ + for the extended block filter" + } + } else { + pass "$nlocs locations (if-else branches on same line, different blocks)" + } + + delete_breakpoints +} + +clean_restart "${testfile}-O2" + +with_test_prefix "for-loop vectorized (-O2)" { + set nlocs [bp_nlocs ${srcfile} ${line_vec_loop} "set breakpoint"] + + if { $nlocs < 2 } { + unsupported "compiler did not produce multiple locations;\ + vectorization may not have fired" + } else { + pass "$nlocs locations preserved (vectorized + scalar paths)" + } + + delete_breakpoints +} + +with_test_prefix "nested vectorized loops (-O2)" { + set nlocs [bp_nlocs ${srcfile} ${line_nested_vec} "set breakpoint"] + + if { $nlocs < 2 } { + unsupported "compiler did not produce multiple locations;\ + nested vectorization may not have fired" + } else { + pass "$nlocs locations preserved (nested paths)" + } + + delete_breakpoints +} + +with_test_prefix "loop with early break (-O2)" { + set nlocs [bp_nlocs ${srcfile} ${line_break_loop} "set breakpoint"] + + if { $nlocs >= 2 } { + pass "$nlocs locations preserved (normal and break paths)" + } else { + pass "$nlocs location (break path not separately compiled)" + } + + delete_breakpoints +} + +with_test_prefix "for-loop vectorized runtime (-O2)" { + clean_restart "${testfile}-O2" + + set nlocs [bp_nlocs ${srcfile} ${line_vec_loop} "set breakpoint (probe)"] + delete_breakpoints + + if { $nlocs < 2 } { + unsupported "only 1 location; skip reachability test" + } else { + with_test_prefix "scalar (n=3)" { + if { ![runto_main] } { + return + } + + gdb_test "break ${srcfile}:${line_vec_loop}" \ + "Breakpoint .* at .*" \ + "set breakpoint" + + gdb_test "continue" \ + "Breakpoint .*, for_loop_vectorized .*" \ + "hit breakpoint on scalar path" + + delete_breakpoints + gdb_test "continue" ".*exited normally.*" "program exits cleanly" + } + + clean_restart "${testfile}-O2" + gdb_test_no_output "set args foo" "set args for vectorized run" + + if { ![runto_main] } { + return + } + + with_test_prefix "vectorized (n=1024)" { + gdb_test "break ${srcfile}:${line_vec_loop}" \ + "Breakpoint .* at .*" \ + "set breakpoint" + + gdb_test "continue" \ + "Breakpoint .*, for_loop_vectorized .*" \ + "hit breakpoint on vectorized path" + + delete_breakpoints + gdb_test "continue" ".*exited normally.*" "program exits cleanly" + } + } +} + +with_test_prefix "setting toggle affects resolution" { + clean_restart "${testfile}-O0" + + # Test with extended filter ON (default). + enable_extended_block_filter "enable extended block-filter" + + set nlocs_on [bp_nlocs ${srcfile} ${line_ifelse} \ + "set breakpoint on if-else line with filter ON"] + delete_breakpoints + + if { $nlocs_on < 2 } { + if { [block_filter_arch_supported] } { + fail "expected 2 locations with extended filter ON, got $nlocs_on" + } else { + unsupported "current arch lacks support\ + for the extended block filter; skipping toggle checks" + } + } else { + pass "if-else with extended filter ON: $nlocs_on locations" + + # Test with extended filter OFF. + gdb_test_no_output "set linespec-extended-block-filter off" \ + "disable extended block-filter" + + set nlocs_off [bp_nlocs ${srcfile} ${line_ifelse} \ + "set breakpoint on if-else line with filter OFF"] + delete_breakpoints + + if { $nlocs_off == 1 } { + pass "if-else with extended filter OFF: 1 location" + } else { + fail "expected 1 location with extended filter OFF, got $nlocs_off" + } + + # Verify toggling it back ON restores the two locations. + enable_extended_block_filter "re-enable extended block-filter" + + set nlocs_on_again [bp_nlocs ${srcfile} ${line_ifelse} \ + "set breakpoint on if-else line with filter re-enabled"] + delete_breakpoints + + if { $nlocs_on_again == $nlocs_on } { + pass "if-else with extended filter re-enabled:\ + $nlocs_on_again locations (verified)" + } else { + fail "expected $nlocs_on locations after re-enabling, got $nlocs_on_again" + } + } +} -- 2.34.1 ________________________________________ Intel Deutschland GmbH Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany Tel: +49 (89) 99143-0 www.intel.de Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman Chairperson of the Supervisory Board: Sonja Pierer Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928 This e-mail and any attachments may contain confidential material for the sole use of the intended recipient(s). Any review or distribution by others is strictly prohibited. If you are not the intended recipient, please contact the sender and delete all copies.