From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id BiAACORrZGqZiiwAWB0awg (envelope-from ) for ; Sat, 25 Jul 2026 03:55:16 -0400 Authentication-Results: simark.ca; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20251104 header.b=O7r9F8Xv; dkim-atps=neutral Received: by simark.ca (Postfix, from userid 112) id 0F6991E099; Sat, 25 Jul 2026 03:55:16 -0400 (EDT) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-5.4 required=5.0 tests=ARC_SIGNED,ARC_VALID,BAYES_00, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FROM,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED autolearn=ham autolearn_force=no version=4.0.1 Received: from vm01.sourceware.org (vm01.sourceware.org [38.145.34.32]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by simark.ca (Postfix) with ESMTPS id 568451E099 for ; Sat, 25 Jul 2026 03:55:13 -0400 (EDT) Received: from vm01.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 7DBDE4BA798B for ; Sat, 25 Jul 2026 07:55:12 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 7DBDE4BA798B Authentication-Results: sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20251104 header.b=O7r9F8Xv Received: from mail-wm1-x331.google.com (mail-wm1-x331.google.com [IPv6:2a00:1450:4864:20::331]) by sourceware.org (Postfix) with ESMTPS id 93CB84BA2E2F for ; Sat, 25 Jul 2026 07:54:38 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 93CB84BA2E2F Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gmail.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 93CB84BA2E2F Authentication-Results: sourceware.org; arc=none smtp.remote-ip=2a00:1450:4864:20::331 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1784966078; cv=none; b=E0HnC9c60tMLu6FCFxhIHCVHEXvLsLl7v8/dyq6jfvuN8K0rGbyam18F1u98nhLk6xHUV8nz3ljg7gMiqfALpwyV4iLsby/4Sm02XAmRSAYOsbRKzGEQlVlXE7I3RdR7dHpmzQ21/VDEml8kbVgVbJDPHtDdWCuHL1l0xAqZJ2o= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1784966078; c=relaxed/simple; bh=AlvSmyHF8JpmDgd6/6Fwq+rz+CGlH3+jmTL2DCZN2Pc=; h=DKIM-Signature:Message-ID:Date:MIME-Version:Subject:To:From; b=it4WVBmSBzRf1U27bxmexcaL3wt6JQH2UwsMHwDnLrTK6roYr+GSSmzBUq2SvGzIfnffJ8aaSlLrHR/WTR/wa64Rj+OWBpYglqBf2rEsNuvcbnLhtCNknJCm4prBPqcysousY7/eC8uIa9G7KPHiPDyJ/bWh8SKqCclL6LAjpAc= ARC-Authentication-Results: i=1; sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20251104 header.b=O7r9F8Xv DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 93CB84BA2E2F Received: by mail-wm1-x331.google.com with SMTP id 5b1f17b1804b1-4956869750eso8391535e9.2 for ; Sat, 25 Jul 2026 00:54:38 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1784966077; x=1785570877; darn=sourceware.org; h=content-transfer-encoding:content-type:in-reply-to:from:references :to:content-language:subject:user-agent:mime-version:date:message-id :from:to:cc:subject:date:message-id:reply-to:content-type; bh=aLLRwcHtCr57vigOh/YF1cAoFcoWvv8JOA4IYTSJ8NI=; b=O7r9F8XvyYOpCo8qOEXiLRSLE1qo6rw22ma2B6yesyDKbrfpdGeUVFbsgtUFP4SdYg XGwcV15R/XRR3wG+LV2SZ1KTjBdGgErqLYzjgl+G+jDARQjtCXagVSe1gb72S65BtsmO s/wfNSZJUPxZIJGS7cphf7ORiCPA5nwyEx2er7EaTbYXlMIEa32TQC375y9Un2mqXhUl 2rzunnQgT22PbK2EoJWwvPHjCWTWTi9flwxL7GTQeo+U91nJCHCxBhrjtCeRvtwS6Aqu Hf+enCXlc+H7HUkYdQoqP66u0H+UtmV3FIszULFwZZf6tGvVDvGS5u0ZoKt9O6IJWegt UwWg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784966077; x=1785570877; h=content-transfer-encoding:content-type:in-reply-to:from:references :to:content-language:subject:user-agent:mime-version:date:message-id :x-gm-gg:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to:content-type; bh=aLLRwcHtCr57vigOh/YF1cAoFcoWvv8JOA4IYTSJ8NI=; b=J+legbDUE43wjn1ks6B451ixUbMit2clNmvRkoAEl3dqzNiRjMiAg9JBvJgHbwar5e 6gRf0phRKKuwm4421SmCrtrHxjqO2dD4y6Fm1Y8j2e2oUZKwrkNQ8PA3Ttg+TdFle+zT kPzdlipNr9hUTiQ0SZRNHtJBs+CU9rWKM90tevqggQ3b9QvjV7vUFIB/x8RQ3E3p58f8 0N0Tjusjz1zyE8KboaD01i7Y27n1ZGOb++6B/Eq/1ssBzdC5dlpdh7JPi2Hn3EEmhHdK H0yQqx9MfaYUpg5V5QE2bJz2tz+e0lWi06Qcjz0ysPw8y0fVvfy1Kc78dX01cqcalVrO ke1w== X-Forwarded-Encrypted: i=1; AHgh+RrX/zqcHNYRToXf+CZnqJ/l4Qu07E/wY0SLtBonPckbZhIHovCjDR06FcHNuZqs2Ff9rE0KkO76LLjw8Q==@sourceware.org X-Gm-Message-State: AOJu0Yzng0VOjDQlMa+8qLAs1uljh3+JJnl3k2zy8MAUBg0t3svz2TL/ ARkme5dt/08/BzdW8xHamaLlOX7P6+rNwrmTQ0++kg84H2Xn87v6IPeN X-Gm-Gg: AR+sD12Vf0RXmUz29SAOwbOFapLOSkl/IB1JtvfXjZigfYsSbH0RiqaM28Ay4k5l+CU thQgNf0xh/zr/BqbqrhWHhpD93qfw4qqrI4Sems7ni/myyE8BlSBvOiWw2+4zoecKBsGAIWbdSH yu539DQYTo0n/t1BrdubRgyahgrWhjjWcaUxa5ydoeZXU688GVMx7xxKdc7WldOM6LwtcDVeVbA OyJVLHj3XU8/ioE3vIGe46vvh69TxJl3L7U+ENXZkTBhY/o8Btr57M9Z49lsfBP2QlS2+Q71CZL 8vyRDNoCP8g4JcYNH5Z/O5z1POzF/HSSawkeeOqpuuFuYBITZRDQaytMVSDP5kkTdRsxOcbk8EU vvHH5vzYgjF1UEoDKIrnsC7XlB9//bccMkxTnFrSvnmFINb74Y07cJrL6nBzG7c5vV/BODkKZ4w iTZoY3XYpmNKyFURw= X-Received: by 2002:a05:600c:1f88:b0:492:3da4:81ef with SMTP id 5b1f17b1804b1-496b5716086mr14933955e9.18.1784966076855; Sat, 25 Jul 2026 00:54:36 -0700 (PDT) Received: from [192.168.0.38] ([86.12.216.189]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-496b4ed5f93sm74285225e9.1.2026.07.25.00.54.35 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Sat, 25 Jul 2026 00:54:36 -0700 (PDT) Message-ID: Date: Sat, 25 Jul 2026 08:54:35 +0100 MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Subject: Re: [PATCH v3 1/1] gdb/linespec: relax block filter to preserve distinct code paths Content-Language: en-US To: Klaus Gerlicher , gdb-patches@sourceware.org, christina.joos@intel.com References: <20260722093654.30044-1-klaus.gerlicher@intel.com> <20260722093654.30044-2-klaus.gerlicher@intel.com> From: Luis In-Reply-To: <20260722093654.30044-2-klaus.gerlicher@intel.com> Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 8bit X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gdb-patches-bounces~public-inbox=simark.ca@sourceware.org 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" > > 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 , B.cond , CBZ/CBNZ , TBZ/TBNZ . > + Returns std::nullopt for indirect branches (BR) or unreadable memory. */ > + > +static std::optional > +aarch64_insn_branch_target (struct gdbarch *gdbarch, CORE_ADDR addr) > +{ > + gdb_byte buf[4]; > + > + if (target_read_memory (addr, buf, 4) != 0) > + return std::nullopt; > + > + enum bfd_endian byte_order = gdbarch_byte_order_for_code (gdbarch); > + uint32_t insn = extract_unsigned_integer (buf, 4, byte_order); > + > + int is_bl; > + int32_t offset; > + > + /* B (but not BL, which is a call). */ > + if (aarch64_decode_b (addr, insn, &is_bl, &offset) && !is_bl) > + return addr + offset; > + > + /* B.cond . */ > + unsigned cond; > + if (aarch64_decode_bcond (addr, insn, &cond, &offset)) > + return addr + offset; > + > + /* CBZ/CBNZ . */ > + int is64, is_cbnz; > + unsigned rn; > + if (aarch64_decode_cb (addr, insn, &is64, &is_cbnz, &rn, &offset)) > + return addr + offset; > + > + /* TBZ/TBNZ . */ > + int is_tbnz; > + unsigned bit, rt; > + if (aarch64_decode_tb (addr, insn, &is_tbnz, &bit, &rt, &offset)) > + return addr + offset; > + > + return std::nullopt; > +} > + > +/* Return true if the instruction at ADDR is an unconditional branch. > + Handles direct B and indirect BR . */ > + > +static bool > +aarch64_insn_is_jump (struct gdbarch *gdbarch, CORE_ADDR addr) > +{ > + gdb_byte buf[4]; > + > + if (target_read_memory (addr, buf, 4) != 0) > + return false; > + > + enum bfd_endian byte_order = gdbarch_byte_order_for_code (gdbarch); > + uint32_t insn = extract_unsigned_integer (buf, 4, byte_order); > + > + /* B (but not BL, which is a call). */ > + int is_bl; > + int32_t offset; > + if (aarch64_decode_b (addr, insn, &is_bl, &offset) && !is_bl) > + return true; > + > + /* BR : bits[31:10] fixed, bits[9:5] = Xn, bits[4:0] = 0. > + No shared decoder exists for indirect branches. */ > + if ((insn & 0xFFFFFC1F) == 0xD61F0000) > + return true; > + > + return false; > +} > + > +/* Return true if the instruction at ADDR is a return. */ > + > +static bool > +aarch64_insn_is_ret (struct gdbarch *gdbarch, CORE_ADDR addr) > +{ > + gdb_byte buf[4]; > + > + if (target_read_memory (addr, buf, 4) != 0) > + return false; > + > + enum bfd_endian byte_order = gdbarch_byte_order_for_code (gdbarch); > + uint32_t insn = extract_unsigned_integer (buf, 4, byte_order); > + > + return aarch64_decode_ret (addr, insn); > +} > + > /* Initialize the current architecture based on INFO. If possible, > reuse an architecture from ARCHES, which is a list of > architectures already created during this debugging session. > @@ -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 > +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 > + > 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 > +default_insn_branch_target (struct gdbarch *gdbarch, CORE_ADDR addr) > +{ > + return std::nullopt; > +} > + > +/* See arch-utils.h. */ > + > bool > default_program_breakpoint_here_p (struct gdbarch *gdbarch, > CORE_ADDR address) > diff --git a/gdb/arch-utils.h b/gdb/arch-utils.h > index 06902050043..46d9d89e668 100644 > --- a/gdb/arch-utils.h > +++ b/gdb/arch-utils.h > @@ -327,6 +327,7 @@ extern bool default_return_in_first_hidden_param_p (struct gdbarch *, > extern bool default_insn_is_call (struct gdbarch *, CORE_ADDR); > extern bool default_insn_is_ret (struct gdbarch *, CORE_ADDR); > extern bool default_insn_is_jump (struct gdbarch *, CORE_ADDR); > +extern std::optional default_insn_branch_target (struct gdbarch *, CORE_ADDR); > > /* Default implementation of gdbarch_program_breakpoint_here_p. */ > extern bool default_program_breakpoint_here_p (struct gdbarch *gdbarch, > diff --git a/gdb/arch/aarch64-insn.c b/gdb/arch/aarch64-insn.c > index f6089092eca..3ab28e6dd4b 100644 > --- a/gdb/arch/aarch64-insn.c > +++ b/gdb/arch/aarch64-insn.c > @@ -201,6 +201,29 @@ aarch64_decode_tb (CORE_ADDR addr, uint32_t insn, int *is_tbnz, > return 0; > } > > +/* Decode an opcode if it represents a RET instruction. > + > + ADDR specifies the address of the opcode. > + INSN specifies the opcode to test. > + > + Return 1 if the opcodes matches and is decoded, otherwise 0. */ > + > +int > +aarch64_decode_ret (CORE_ADDR addr, uint32_t insn) > +{ > + /* ret b1101 0110 0100 nnnn n000 0000 0000 0000 */ > + if (decode_masked_match (insn, 0xFFE0FFFF, 0xD6400000)) > + { > + unsigned int rn = (insn >> 15) & 0x1f; > + > + aarch64_debug_printf ("decode: 0x%s 0x%x x%u", > + core_addr_to_string_nz (addr), insn, > + rn); > + return 1; > + } > + return 0; > +} > + > /* Decode an opcode if it represents an LDR or LDRSW instruction taking a > literal offset from the current PC. > > diff --git a/gdb/arch/aarch64-insn.h b/gdb/arch/aarch64-insn.h > index 428e53c3858..aafbe3c0d6b 100644 > --- a/gdb/arch/aarch64-insn.h > +++ b/gdb/arch/aarch64-insn.h > @@ -200,6 +200,8 @@ int aarch64_decode_tb (CORE_ADDR addr, uint32_t insn, int *is_tbnz, > int aarch64_decode_ldr_literal (CORE_ADDR addr, uint32_t insn, int *is_w, > int *is64, unsigned *rt, int32_t *offset); > > +int aarch64_decode_ret (CORE_ADDR addr, uint32_t insn); > + > /* Data passed to each method of aarch64_insn_visitor. */ > > struct aarch64_insn_data > diff --git a/gdb/gdbarch-gen.c b/gdb/gdbarch-gen.c > index 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 > +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 (struct gdbarch *gdbarch, CORE_ADDR addr); > +std::optional gdbarch_insn_branch_target (struct gdbarch *gdbarch, CORE_ADDR addr); > +void set_gdbarch_insn_branch_target (struct gdbarch *gdbarch, gdbarch_insn_branch_target_ftype *insn_branch_target); > + > /* Return true if there's a program/permanent breakpoint planted in > memory at ADDRESS, return false otherwise. */ > > diff --git a/gdb/gdbarch_components.py b/gdb/gdbarch_components.py > index 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", > + 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 > +#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 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 > @@ -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 . > + > + 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 . > +# > +# 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 > 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