From: "Schimpe, Christina" <christina.schimpe@intel.com>
To: "Gerlicher, Klaus" <klaus.gerlicher@intel.com>,
"gdb-patches@sourceware.org" <gdb-patches@sourceware.org>
Subject: RE: [PATCH 1/1] gdb/linespec: relax block filter to preserve distinct code paths
Date: Thu, 30 Apr 2026 16:06:58 +0000 [thread overview]
Message-ID: <SN7PR11MB7638C19F7F36BB38AF2D6CECF9352@SN7PR11MB7638.namprd11.prod.outlook.com> (raw)
In-Reply-To: <20260421133154.585917-2-klaus.gerlicher@intel.com>
> -----Original Message-----
> From: Klaus Gerlicher <klaus.gerlicher@intel.com>
> Sent: Dienstag, 21. April 2026 15:32
> To: gdb-patches@sourceware.org
> Subject: [PATCH 1/1] gdb/linespec: relax block filter to preserve distinct code
> paths
>
> 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 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; other architectures default to conservative
> behavior, preserving backward compatibility.
>
> Testing covers:
> - for_loop_empty_body (-O0): 1 location
> - for_loop_large_body (-O0): 1 location (exceeds former scan limits)
> - 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 (sanity check)
>
> Tested with GCC 11 and ICX 2025.2 on x86_64.
> ---
> gdb/NEWS | 11 +
> gdb/amd64-tdep.c | 57 ++++
> gdb/arch-utils.c | 10 +
> gdb/arch-utils.h | 1 +
> gdb/gdbarch-gen.c | 22 ++
> gdb/gdbarch-gen.h | 10 +
> gdb/gdbarch_components.py | 15 ++
> gdb/linespec.c | 95 ++++++-
> .../gdb.linespec/block-filter-cases.c | 126 +++++++++
> .../gdb.linespec/block-filter-cases.exp | 248 ++++++++++++++++++
> 10 files changed, 592 insertions(+), 3 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 6b5c2f6a46a..9b85bd79944 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -61,6 +61,17 @@
> 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.
> +
> + Use "set linespec-extended-block-filter off" to disable the new
> + heuristic 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/amd64-tdep.c b/gdb/amd64-tdep.c index
> 0d23abb3164..76450775d45 100755
> --- a/gdb/amd64-tdep.c
> +++ b/gdb/amd64-tdep.c
> @@ -1822,6 +1822,62 @@ 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). Return std::nullopt for non-branch,
> + indirect branches, or when memory cannot be read. */
> +
> +static std::optional<CORE_ADDR>
> +amd64_insn_branch_target (struct gdbarch *gdbarch, CORE_ADDR addr) {
> + gdb_byte buf[16];
> +
> + /* Read enough bytes for the longest relative-branch encoding. */
> + if (target_read_memory (addr, buf, sizeof (buf)) != 0)
> + return std::nullopt;
> +
> + /* Skip a single REX prefix byte (0x40-0x4F). */ int off = 0; if
> + ((buf[0] & 0xf0) == 0x40)
> + off = 1;
> +
> + const gdb_byte opc = buf[off];
> +
> + /* Short conditional Jcc: 7x cc (2 bytes, signed 8-bit
> + displacement). */ if ((opc & 0xf0) == 0x70)
> + return addr + off + 2 + (LONGEST) (signed char) buf[off + 1];
> +
> + /* Short unconditional JMP: EB cc (2 bytes). */ if (opc == 0xeb)
> + return addr + off + 2 + (LONGEST) (signed char) buf[off + 1];
> +
> + /* LOOP/LOOPE/LOOPNE: E2/E1/E0 cc (2 bytes, signed 8-bit
> + displacement). */ if ((opc & 0xfd) == 0xe0)
> + return addr + off + 2 + (LONGEST) (signed char) buf[off + 1];
> +
> + /* JCXZ/JECXZ/JRCXZ: E3 cc (2 bytes, signed 8-bit displacement). */
> + if (opc == 0xe3)
> + return addr + off + 2 + (LONGEST) (signed char) buf[off + 1];
> +
> + /* Near conditional Jcc: 0F 8x cc cc cc cc (6 bytes, signed 32-bit).
> + */ if (opc == 0x0f && (buf[off + 1] & 0xf0) == 0x80)
> + {
> + int32_t disp;
> + memcpy (&disp, buf + off + 2, sizeof (disp));
> + return addr + off + 6 + (LONGEST) disp;
> + }
> +
> + /* Near unconditional JMP: E9 cc cc cc cc (5 bytes, signed 32-bit).
> + */ if (opc == 0xe9)
> + {
> + int32_t disp;
> + memcpy (&disp, buf + off + 1, sizeof (disp));
> + return addr + off + 5 + (LONGEST) disp;
> + }
> +
> + /* Indirect or far branches: target cannot be determined statically.
> +*/
> + return std::nullopt;
> +}
> +
> /* Fix up the state of registers and memory after having single-stepped
> a displaced instruction. */
>
> @@ -3643,6 +3699,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);
> diff --git a/gdb/arch-utils.c b/gdb/arch-utils.c index
> ea0b3c111f9..28a8d33d139 100644
> --- a/gdb/arch-utils.c
> +++ b/gdb/arch-utils.c
> @@ -45,6 +45,8 @@
>
> #include "dis-asm.h"
>
> +#include <optional>
> +
> bool
> default_displaced_step_hw_singlestep (struct gdbarch *gdbarch) { @@ -
> 923,6 +925,14 @@ default_insn_is_jump (struct gdbarch *gdbarch,
> CORE_ADDR addr)
>
> /* See arch-utils.h. */
>
> +std::optional<CORE_ADDR>
> +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
> d5772575c7a..2ec7e5900a7 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<CORE_ADDR> 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/gdbarch-gen.c b/gdb/gdbarch-gen.c index
> 1a8f21091b2..d31ad84280c 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; @@ -494,6 +495,7 @@ verify_gdbarch (struct
> gdbarch *gdbarch)
> /* 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_branch_target, invalid_p == 0. */
> /* 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. */ @@ -1276,6 +1278,9
> @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
> gdb_printf (file,
> "gdbarch_dump: insn_is_jump = <%s>\n",
> host_address_to_string (gdbarch->insn_is_jump));
> + 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));
> @@ -4950,6 +4955,23 @@ set_gdbarch_insn_is_jump (struct gdbarch
> *gdbarch,
> gdbarch->insn_is_jump = insn_is_jump; }
>
> +std::optional<CORE_ADDR>
> +gdbarch_insn_branch_target (struct gdbarch *gdbarch, CORE_ADDR addr) {
> + gdb_assert (gdbarch != nullptr);
> + gdb_assert (gdbarch->insn_branch_target != nullptr);
> + 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 c1394978568..e31ddc6b741 100644
> --- a/gdb/gdbarch-gen.h
> +++ b/gdb/gdbarch-gen.h
> @@ -1592,6 +1592,16 @@ 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. */
> +
> +using gdbarch_insn_branch_target_ftype = std::optional<CORE_ADDR>
> +(struct gdbarch *gdbarch, CORE_ADDR addr); std::optional<CORE_ADDR>
> +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 7a329d92166..4dcf595f229 100644
> --- a/gdb/gdbarch_components.py
> +++ b/gdb/gdbarch_components.py
> @@ -2524,6 +2524,21 @@ Return true if the instruction at ADDR is a jump;
> false otherwise.
> 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<CORE_ADDR>",
> + name="insn_branch_target",
> + params=[("CORE_ADDR", "addr")],
> + 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 45e12e09272..e6305ddeabb
> 100644
> --- a/gdb/linespec.c
> +++ b/gdb/linespec.c
> @@ -43,9 +43,12 @@
> #include "gdbsupport/function-view.h"
> #include "gdbsupport/def-vector.h"
> #include <algorithm>
> +#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. */ @@ -431,6 +434,11 @@ 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.
> + This heuristic improves breakpoint location resolution for vectorized
> + loops but can be disabled if it causes unexpected behavior. */
> +static bool debug_linespec_extended_block_filter = true;
> +
> /* Lexer functions. */
>
> /* Lex a number from the input in PARSER. This only supports @@ -1979,6
> +1987,48 @@ 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/ret 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)
> {
> + if (pc_from >= pc_to)
> + return false;
> +
> + /* 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)
> + {
> + int len = gdb_insn_length (gdbarch, pc);
> + if (len <= 0)
> + return false;
> +
> + /* Check for backward branch (loop back-edge). */
> + std::optional<CORE_ADDR> 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;
> + }
> +
> + return false;
> +}
> +
> /* Given a line offset in LS, construct the relevant SALs. */
>
> static std::vector<symtab_and_line>
> @@ -2077,13 +2127,35 @@ 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 == nullptr)
> + continue;
> +
> + if (sal_i.symtab != sal_j.symtab)
> + continue;
> +
> + /* Check for unconditional flow control in the range
> + (extended heuristic; can be disabled). */
> + 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;
> }
> }
> }
> @@ -4330,3 +4402,20 @@ get_gdb_linespec_parser_quote_characters
> (void) {
> return linespec_quote_characters;
> }
> +
> +/* 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.\n\ Default is on."),
> + nullptr, nullptr,
> + &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 <http://www.gnu.org/licenses/>.
> +
> + 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..fa4ce5315c6
> --- /dev/null
> +++ b/gdb/testsuite/gdb.linespec/block-filter-cases.exp
> @@ -0,0 +1,248 @@
> +# 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 <http://www.gnu.org/licenses/>.
> +
> +# Verify the block-filter heuristic in create_sals_line_offset 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 -1
> +}
> +
> +if { [build_executable "failed to prepare (O2)" \
> + "${testfile}-O2" ${srcfile} \
> + { debug optimize=-O2 additional_flags=-ftree-vectorize }] } {
> + return -1
> +}
> +
> +# 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}]
> +
> +# Set breakpoint and return how many locations it resolves to.
> +proc bp_nlocs { srcfile line desc } {
> + global gdb_prompt
> + set nlocs -1
> + gdb_test_multiple "break ${srcfile}:${line}" $desc {
> + -re "Breakpoint \[0-9\]+ at .*\\((\[0-9\]+) locations\\).*$gdb_prompt
> $" {
> + set nlocs $expect_out(1,string)
> + pass $desc
> + }
> + -re "Breakpoint \[0-9\]+ at .*$gdb_prompt $" {
> + 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" \
> + "Whether the extended block-filter heuristic is enabled is on\\." \
> + "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" \
> + "Whether the extended block-filter heuristic is enabled is off\\." \
> + "show linespec-extended-block-filter (off)"
> +
> +gdb_test_no_output "set linespec-extended-block-filter on" \
> + "set linespec-extended-block-filter on"
> +
> +gdb_test "show linespec-extended-block-filter" \
> + "Whether the extended block-filter heuristic is enabled is on\\." \
> + "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 } {
> + pass "2 locations (if-else branches on same line, different blocks)"
> + } else {
> + fail "expected 2 locations, got $nlocs"
> + }
> +
> + 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 -1
> + }
> +
> + 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 -1
> + }
> +
> + 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"
> + }
> + }
> +}
> +
> +# Behavioral test: verify setting actually affects breakpoint resolution.
> +with_test_prefix "behavioral: setting toggle affects resolution" {
> + clean_restart "${testfile}-O0"
> +
> + # Test with extended filter ON (default).
> + gdb_test_no_output "set linespec-extended-block-filter on" \
> + "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 } {
> + pass "if-else with extended filter ON: 2 locations"
> + } else {
> + fail "expected 2 locations with extended filter ON, got $nlocs_on"
> + }
> +
> + # 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.
> + gdb_test_no_output "set linespec-extended-block-filter on" \
> + "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 == 2 } {
> + pass "if-else with extended filter re-enabled: 2 locations (verified)"
> + } else {
> + fail "expected 2 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 991 430, www.intel.de
> Managing Directors: Harry Demas, Jeffrey Schneiderman, Yin Chong Sorrell
> Chairperson of the Supervisory Board: Nicole Lau
> Registered Seat: Munich
> Commercial Register: Amtsgericht Muenchen HRB 186928
Hi Klaus,
Thank you for working on this.
For your patch I see new errors for gcc and clang in the following tests:
- gdb.threads/detach-step-over.exp
- gdb.ada/type_coercion.exp
- gdb.ada/overload_menu_crash.exp
They all show a similar error message:
~~~
(gdb) break main.adb:20^M
Cannot access memory at address 0x267a^M
(gdb) FAIL: gdb.ada/overload_menu_crash.exp:
~~~
Compiler versions:
~~~
$ gcc --version
gcc (Ubuntu 11.4.0-1ubuntu1~22.04.3) 11.4.0
$ clang --version
Ubuntu clang version 14.0.0-1ubuntu1.1
~~~
Besides that, I did not have the time to review this in more detail but wanted to share some of my findings already.
Christina
Intel Deutschland GmbH
Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 89 991 430, www.intel.de
Managing Directors: Harry Demas, Jeffrey Schneiderman, Yin Chong Sorrell
Chairperson of the Supervisory Board: Nicole Lau
Registered Seat: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928
prev parent reply other threads:[~2026-04-30 16:07 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-04-21 13:31 [PATCH 0/1] Fix block filter heuristic in linespec " Klaus Gerlicher
2026-04-21 13:31 ` [PATCH 1/1] gdb/linespec: relax block filter " Klaus Gerlicher
2026-04-30 16:06 ` Schimpe, Christina [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=SN7PR11MB7638C19F7F36BB38AF2D6CECF9352@SN7PR11MB7638.namprd11.prod.outlook.com \
--to=christina.schimpe@intel.com \
--cc=gdb-patches@sourceware.org \
--cc=klaus.gerlicher@intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox