Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
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

      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