Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
From: Luis <luis.machado.foss@gmail.com>
To: Klaus Gerlicher <klaus.gerlicher@intel.com>,
	gdb-patches@sourceware.org, christina.joos@intel.com
Subject: Re: [PATCH v3 1/1] gdb/linespec: relax block filter to preserve distinct code paths
Date: Sat, 25 Jul 2026 08:54:35 +0100	[thread overview]
Message-ID: <db7238d2-1628-44ff-949e-0c176ff6257b@gmail.com> (raw)
In-Reply-To: <20260722093654.30044-2-klaus.gerlicher@intel.com>

Hi,

Thanks for the patch.

Skimming through it, it looks fine to me for the aarch64 parts.

On 22/07/2026 10:36, Klaus Gerlicher wrote:
> From: "Gerlicher, Klaus" <klaus.gerlicher@intel.com>
> 
> 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
> 
> 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                              |  57 ++++
>   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       | 291 ++++++++++++++++++
>   13 files changed, 848 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 8f40ca5cb11..b587ac87763 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -68,6 +68,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.  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 a84da1fd59e..5b45efbf8b9 100644
> --- a/gdb/aarch64-tdep.c
> +++ b/gdb/aarch64-tdep.c
> @@ -4433,6 +4433,92 @@ aarch64_initialize_sme_pseudo_names (struct gdbarch *gdbarch,
>       }
>   }
>   
> +/* Return the statically-known target of a relative branch instruction.
> +   Handles B <imm26>, B.cond <imm19>, CBZ/CBNZ <imm19>, TBZ/TBNZ <imm14>.
> +   Returns std::nullopt for indirect branches (BR) or unreadable memory.  */
> +
> +static std::optional<CORE_ADDR>
> +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 <imm26> (but not BL, which is a call).  */
> +  if (aarch64_decode_b (addr, insn, &is_bl, &offset) && !is_bl)
> +    return addr + offset;
> +
> +  /* B.cond <imm19>.  */
> +  unsigned cond;
> +  if (aarch64_decode_bcond (addr, insn, &cond, &offset))
> +    return addr + offset;
> +
> +  /* CBZ/CBNZ <imm19>.  */
> +  int is64, is_cbnz;
> +  unsigned rn;
> +  if (aarch64_decode_cb (addr, insn, &is64, &is_cbnz, &rn, &offset))
> +    return addr + offset;
> +
> +  /* TBZ/TBNZ <imm14>.  */
> +  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 <imm26> and indirect BR <Xn>.  */
> +
> +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 <imm26> (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 <Xn>: 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.
> @@ -4861,6 +4947,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..726bc475256 100644
> --- 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 e959788bd3b..f73d1581f09 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 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<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/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 f424fa2a86e..1cbb6459f87 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<CORE_ADDR>
> +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 678b308fba5..6a6e9ed40f1 100644
> --- a/gdb/gdbarch-gen.h
> +++ b/gdb/gdbarch-gen.h
> @@ -1582,16 +1582,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<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 b9304d3036d..df5ddefffcb 100644
> --- a/gdb/gdbarch_components.py
> +++ b/gdb/gdbarch_components.py
> @@ -2509,6 +2509,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,
>   )
> @@ -2520,10 +2521,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<CORE_ADDR>",
> +    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 b6505ba283d..1992fc62e4a 100644
> --- a/gdb/linespec.c
> +++ b/gdb/linespec.c
> @@ -44,9 +44,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.  */
> @@ -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
> @@ -1986,6 +1992,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<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;
> +	}
> +      catch (const gdb_exception_error &)
> +	{
> +	  return false;
> +	}
> +    }
> +
> +  return false;
> +}
> +
>   /* Given a line offset in LS, construct the relevant SALs.  */
>   
>   static std::vector<symtab_and_line>
> @@ -2084,13 +2136,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;
>   		  }
>   	      }
>   	}
> @@ -4343,3 +4415,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\
> +architectures that implement gdbarch_insn_is_jump or\n\
> +gdbarch_insn_branch_target actually apply the extended heuristic;\n\
> +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 <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..39f36ad4a27
> --- /dev/null
> +++ b/gdb/testsuite/gdb.linespec/block-filter-cases.exp
> @@ -0,0 +1,291 @@
> +# 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 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}]
> +
> +# Return true if the extended block-filter heuristic is actually
> +# implemented for the current architecture.
> +proc block_filter_arch_supported { } {
> +    global gdb_prompt
> +    set supported -1
> +    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\\)\\." {
> +	    set supported 0
> +	    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 -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"
> +	}
> +    }
> +}
> +
> +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"
> +	}
> +    }
> +}
> 
> 
> 
> Intel Deutschland GmbH
> 
> Registered Address: Dornacher Straße 1, 85622 Feldkirchen, Germany
> Tel: +49 89 991 430, www.intel.de <//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 München HRB 186928
> 

Reviewed-By: Luis Machado <luis.machado.foss@gmai.com>

      reply	other threads:[~2026-07-25  7:55 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-22  9:36 [PATCH v3 0/1] Fix block filter heuristic in linespec " Klaus Gerlicher
2026-07-22  9:36 ` [PATCH v3 1/1] gdb/linespec: relax block filter " Klaus Gerlicher
2026-07-25  7:54   ` Luis [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=db7238d2-1628-44ff-949e-0c176ff6257b@gmail.com \
    --to=luis.machado.foss@gmail.com \
    --cc=christina.joos@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