[llvm] [llvm-utils][Tools] fix mir-check filter to handle scattered check-lines (PR #209226)
Gang Chen via llvm-commits
llvm-commits at lists.llvm.org
Fri Jul 17 08:57:37 PDT 2026
https://github.com/cmc-rep updated https://github.com/llvm/llvm-project/pull/209226
>From 759aacc898c294f04f7ef4f36874b65da80f9596 Mon Sep 17 00:00:00 2001
From: Gang Chen <Gang.Chen at amd.com>
Date: Mon, 13 Jul 2026 07:56:40 -0700
Subject: [PATCH 1/2] [llvm-utils][Tools] fix mir-check filter to handle
scattered check-lines
---
.../Inputs/x86-filter.mir.filter-out.expected | 4 +-
.../Inputs/x86-filter.mir.filter.expected | 5 +-
llvm/utils/UpdateTestChecks/mir.py | 65 ++++++++++++++++++-
3 files changed, 69 insertions(+), 5 deletions(-)
diff --git a/llvm/test/tools/UpdateTestChecks/update_mir_test_checks/Inputs/x86-filter.mir.filter-out.expected b/llvm/test/tools/UpdateTestChecks/update_mir_test_checks/Inputs/x86-filter.mir.filter-out.expected
index 87c3670a10421..ea924dd74d398 100644
--- a/llvm/test/tools/UpdateTestChecks/update_mir_test_checks/Inputs/x86-filter.mir.filter-out.expected
+++ b/llvm/test/tools/UpdateTestChecks/update_mir_test_checks/Inputs/x86-filter.mir.filter-out.expected
@@ -32,7 +32,7 @@ body: |
; CHECK-LABEL: name: ptrtoint_s8
; CHECK: liveins: $rdi
; CHECK-NEXT: {{ $}}
- ; CHECK-NEXT: $al = COPY [[COPY1]]
+ ; CHECK: $al = COPY [[COPY1]]
; CHECK-NEXT: RET 0, implicit $al
%0:gpr(p0) = COPY $rdi
%1:gpr(s8) = G_PTRTOINT %0(p0)
@@ -57,7 +57,7 @@ body: |
; CHECK: liveins: $rdi
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: [[COPY:%[0-9]+]]:gr64 = COPY $rdi
- ; CHECK-NEXT: $eax = COPY [[COPY1]]
+ ; CHECK: $eax = COPY [[COPY1]]
; CHECK-NEXT: RET 0, implicit $eax
%0:gpr(p0) = COPY $rdi
%1:gpr(s32) = G_PTRTOINT %0(p0)
diff --git a/llvm/test/tools/UpdateTestChecks/update_mir_test_checks/Inputs/x86-filter.mir.filter.expected b/llvm/test/tools/UpdateTestChecks/update_mir_test_checks/Inputs/x86-filter.mir.filter.expected
index 39064f9a3ae3c..4141015cf9059 100644
--- a/llvm/test/tools/UpdateTestChecks/update_mir_test_checks/Inputs/x86-filter.mir.filter.expected
+++ b/llvm/test/tools/UpdateTestChecks/update_mir_test_checks/Inputs/x86-filter.mir.filter.expected
@@ -30,7 +30,8 @@ body: |
liveins: $rdi
; CHECK-LABEL: name: ptrtoint_s8
- ; CHECK: [[COPY1:%[0-9]+]]:gr8 = COPY [[COPY]].sub_8bit
+ ; CHECK: [[COPY:%[0-9]+]]:gr64_with_sub_8bit = COPY $rdi
+ ; CHECK-NEXT: [[COPY1:%[0-9]+]]:gr8 = COPY [[COPY]].sub_8bit
%0:gpr(p0) = COPY $rdi
%1:gpr(s8) = G_PTRTOINT %0(p0)
$al = COPY %1(s8)
@@ -50,6 +51,8 @@ body: |
bb.1.entry:
liveins: $rdi
+ ; CHECK-LABEL: name: ptrtoint_s32
+ ; CHECK: [[COPY1:%[0-9]+]]:gr32 = COPY [[COPY]].sub_32bit
%0:gpr(p0) = COPY $rdi
%1:gpr(s32) = G_PTRTOINT %0(p0)
$eax = COPY %1(s32)
diff --git a/llvm/utils/UpdateTestChecks/mir.py b/llvm/utils/UpdateTestChecks/mir.py
index e75d3dd2ff368..5b269167b8db8 100644
--- a/llvm/utils/UpdateTestChecks/mir.py
+++ b/llvm/utils/UpdateTestChecks/mir.py
@@ -50,6 +50,52 @@
flags=(re.M | re.S),
)
+MD_PTR_RE = re.compile(r"\<0x[a-f0-9]+\>", re.IGNORECASE)
+
+# A marker inserted into a filtered MIR function body to record that one or more
+# lines were removed by --filter between two retained lines. When emitting
+# checks, the retained line that follows a marker is printed as CHECK (rather
+# than CHECK-NEXT) because it is not guaranteed to immediately follow the
+# previous match in the tool output.
+MIR_FILTER_GAP_MARKER = "; UTC-MIR-FILTER-GAP"
+
+
+def filter_mir_body(body, filters):
+ """Filter a MIR function body while keeping the generated checks correct.
+
+ Behaves like common.do_filter, with two MIR-specific adjustments that make
+ --filter usable for tests whose interesting instructions are scattered
+ through the function:
+
+ * Basic block label lines are always retained. This keeps the check
+ comments at the block-level indentation (so the surrounding "body: |"
+ block scalar stays valid) and documents the control-flow structure.
+ * A marker line is inserted wherever one or more lines were removed
+ between two retained lines. add_mir_check_lines turns the retained line
+ after a marker into a CHECK instead of a CHECK-NEXT, since filtered-out
+ instructions may sit between the two matches in the tool output.
+ """
+ if not filters:
+ return body
+ if common.has_filter_out_after(filters):
+ lines = common.filter_out_after(body, filters)
+ else:
+ lines = body.splitlines()
+ result = []
+ have_gap = False
+ for line in lines:
+ keep = common.apply_filters(line, filters) or bool(
+ MIR_BASIC_BLOCK_RE.match(line)
+ )
+ if not keep:
+ have_gap = True
+ continue
+ if have_gap and result:
+ result.append(MIR_FILTER_GAP_MARKER)
+ result.append(line)
+ have_gap = False
+ return "\n".join(result)
+
def build_function_info_dictionary(
test, raw_tool_output, triple, prefixes, func_dict, verbose, filters=None
@@ -84,7 +130,7 @@ def build_function_info_dictionary(
)
mangled.append(func_line)
body = "".join(mangled)
- filtered_body = common.do_filter(body, filters)
+ filtered_body = filter_mir_body(body, filters)
for prefix in prefixes:
info = common.function_body(
@@ -215,6 +261,12 @@ def add_mir_check_lines(
# Don't bother checking the basic block label for a single BB
func_body.pop(0)
+ # A gap marker carries no information before the first retained line, and
+ # popping the basic block label above may have exposed one. Drop any such
+ # leading markers so indentation is computed from a real instruction line.
+ while func_body and func_body[0] == MIR_FILTER_GAP_MARKER:
+ func_body.pop(0)
+
if not func_body:
warn(
"Function has no instructions to check: {}".format(func_name),
@@ -240,6 +292,12 @@ def add_mir_check_lines(
first_check = not first_check_is_next
for func_line in func_body:
+ if func_line == MIR_FILTER_GAP_MARKER:
+ # One or more lines were filtered out here, so the next retained
+ # line is not necessarily adjacent to the previous match: emit it as
+ # CHECK rather than CHECK-NEXT.
+ first_check = True
+ continue
if not func_line.strip():
# The mir printer prints leading whitespace so we can't use CHECK-EMPTY:
output_lines.append(check + "-NEXT: {{" + func_line + "$}}")
@@ -277,7 +335,10 @@ def add_mir_checks(
func_name = None
state = "toplevel"
for input_line in input_lines:
- if input_line == autogenerated_note:
+ if common.UTC_ADVERT in input_line:
+ # Discard any previous autogenerated note. Matching on the advert
+ # text (rather than the exact note) ensures a stale note is dropped
+ # even when the arguments differ, e.g. when --filter was added.
continue
if state == "toplevel":
>From bae4f8a3cf88d1268304e0fa0819ae9f6dbdbde2 Mon Sep 17 00:00:00 2001
From: Gang Chen <Gang.Chen at amd.com>
Date: Thu, 16 Jul 2026 11:49:54 -0700
Subject: [PATCH 2/2] [LLVM][Utils] Add tests for the fix of mir-check filter
---
.../Inputs/amdgpu-scattered-filter.mir | 64 ++++++++++++++
...mdgpu-scattered-filter.mir.filter.expected | 84 +++++++++++++++++++
.../update_mir_test_checks/amdgpu-filter.test | 13 +++
llvm/utils/UpdateTestChecks/mir.py | 3 -
4 files changed, 161 insertions(+), 3 deletions(-)
create mode 100644 llvm/test/tools/UpdateTestChecks/update_mir_test_checks/Inputs/amdgpu-scattered-filter.mir
create mode 100644 llvm/test/tools/UpdateTestChecks/update_mir_test_checks/Inputs/amdgpu-scattered-filter.mir.filter.expected
create mode 100644 llvm/test/tools/UpdateTestChecks/update_mir_test_checks/amdgpu-filter.test
diff --git a/llvm/test/tools/UpdateTestChecks/update_mir_test_checks/Inputs/amdgpu-scattered-filter.mir b/llvm/test/tools/UpdateTestChecks/update_mir_test_checks/Inputs/amdgpu-scattered-filter.mir
new file mode 100644
index 0000000000000..85380526a3d59
--- /dev/null
+++ b/llvm/test/tools/UpdateTestChecks/update_mir_test_checks/Inputs/amdgpu-scattered-filter.mir
@@ -0,0 +1,64 @@
+# RUN: llc -mtriple=amdgpu10.10 -run-pass=none -verify-machineinstrs -o - %s | FileCheck -check-prefixes=GFX10 %s
+
+---
+name: _amdgpu_hs_main
+alignment: 1
+tracksRegLiveness: true
+machineFunctionInfo:
+ isEntryFunction: true
+body: |
+ bb.0:
+ successors: %bb.1(0x40000000), %bb.2(0x40000000)
+
+ %2:vgpr_32 = IMPLICIT_DEF
+ %3:sreg_32 = S_MOV_B32 8
+ %5:vgpr_32 = V_BFE_U32_e64 %2, 8, 5, implicit $exec
+ %6:sreg_32 = S_MOV_B32 5
+ %7:sreg_32 = V_CMP_NE_U32_e64 %5, killed %6, implicit $exec
+ %8:sreg_32 = S_XOR_B32 %7, -1, implicit-def $scc
+ %9:sreg_32 = S_AND_B32 %8, $exec_lo, implicit-def $scc
+ %10:sreg_32 = S_XOR_B32 $exec_lo, %9, implicit-def $scc
+ $exec_lo = S_MOV_B32_term %9
+ S_CBRANCH_EXECZ %bb.2, implicit $exec
+ S_BRANCH %bb.1
+
+ bb.1:
+ successors: %bb.2(0x80000000)
+
+ %0:sgpr_32 = IMPLICIT_DEF
+ S_BRANCH %bb.2
+
+ bb.2:
+ successors: %bb.3(0x40000000), %bb.4(0x40000000)
+
+ $exec_lo = S_OR_B32 $exec_lo, %10, implicit-def $scc
+ %4:vgpr_32 = V_LSHRREV_B32_e64 %3, %2, implicit $exec
+ %11:sreg_32 = S_MOV_B32 31
+ %12:sreg_32 = V_CMP_NE_U32_e64 %5, killed %11, implicit $exec
+ %13:sreg_32 = S_XOR_B32 %12, -1, implicit-def $scc
+ %14:sreg_32 = S_AND_B32 %13, $exec_lo, implicit-def $scc
+ %15:sreg_32 = S_XOR_B32 $exec_lo, %14, implicit-def $scc
+ $exec_lo = S_MOV_B32_term %14
+ S_CBRANCH_EXECZ %bb.4, implicit $exec
+ S_BRANCH %bb.3
+
+ bb.3:
+ successors: %bb.4(0x80000000)
+
+ S_BRANCH %bb.4
+
+ bb.4:
+ successors: %bb.5(0x80000000)
+
+ $exec_lo = S_OR_B32 $exec_lo, %15, implicit-def $scc
+ %1:sreg_32 = IMPLICIT_DEF
+ %16:sreg_32 = S_MOV_B32 16
+ %17:sreg_32 = S_MOV_B32 4
+ %18:vgpr_32 = nuw nsw V_LSHL_ADD_U32_e64 %4, %17, killed %16, implicit $exec
+ S_BRANCH %bb.5
+
+ bb.5:
+ $exec_lo = S_OR_B32 $exec_lo, %1, implicit-def $scc
+ S_ENDPGM 0
+
+...
diff --git a/llvm/test/tools/UpdateTestChecks/update_mir_test_checks/Inputs/amdgpu-scattered-filter.mir.filter.expected b/llvm/test/tools/UpdateTestChecks/update_mir_test_checks/Inputs/amdgpu-scattered-filter.mir.filter.expected
new file mode 100644
index 0000000000000..05bcd9b82df54
--- /dev/null
+++ b/llvm/test/tools/UpdateTestChecks/update_mir_test_checks/Inputs/amdgpu-scattered-filter.mir.filter.expected
@@ -0,0 +1,84 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --filter "exec_lo|IMPLICIT_DEF"
+# RUN: llc -mtriple=amdgpu10.10 -run-pass=none -verify-machineinstrs -o - %s | FileCheck -check-prefixes=GFX10 %s
+
+---
+name: _amdgpu_hs_main
+alignment: 1
+tracksRegLiveness: true
+machineFunctionInfo:
+ isEntryFunction: true
+body: |
+ ; GFX10-LABEL: name: _amdgpu_hs_main
+ ; GFX10: bb.0:
+ ; GFX10: [[DEF:%[0-9]+]]:vgpr_32 = IMPLICIT_DEF
+ ; GFX10: [[S_AND_B32_:%[0-9]+]]:sreg_32 = S_AND_B32 [[S_XOR_B32_]], $exec_lo, implicit-def $scc
+ ; GFX10-NEXT: [[S_XOR_B32_1:%[0-9]+]]:sreg_32 = S_XOR_B32 $exec_lo, [[S_AND_B32_]], implicit-def $scc
+ ; GFX10-NEXT: $exec_lo = S_MOV_B32_term [[S_AND_B32_]]
+ ; GFX10: bb.1:
+ ; GFX10: [[DEF1:%[0-9]+]]:sgpr_32 = IMPLICIT_DEF
+ ; GFX10: bb.2:
+ ; GFX10: $exec_lo = S_OR_B32 $exec_lo, [[S_XOR_B32_1]], implicit-def $scc
+ ; GFX10: [[S_AND_B32_1:%[0-9]+]]:sreg_32 = S_AND_B32 [[S_XOR_B32_2]], $exec_lo, implicit-def $scc
+ ; GFX10-NEXT: [[S_XOR_B32_3:%[0-9]+]]:sreg_32 = S_XOR_B32 $exec_lo, [[S_AND_B32_1]], implicit-def $scc
+ ; GFX10-NEXT: $exec_lo = S_MOV_B32_term [[S_AND_B32_1]]
+ ; GFX10: bb.3:
+ ; GFX10: bb.4:
+ ; GFX10: $exec_lo = S_OR_B32 $exec_lo, [[S_XOR_B32_3]], implicit-def $scc
+ ; GFX10-NEXT: [[DEF2:%[0-9]+]]:sreg_32 = IMPLICIT_DEF
+ ; GFX10: bb.5:
+ ; GFX10-NEXT: $exec_lo = S_OR_B32 $exec_lo, [[DEF2]], implicit-def $scc
+ bb.0:
+ successors: %bb.1(0x40000000), %bb.2(0x40000000)
+
+ %2:vgpr_32 = IMPLICIT_DEF
+ %3:sreg_32 = S_MOV_B32 8
+ %5:vgpr_32 = V_BFE_U32_e64 %2, 8, 5, implicit $exec
+ %6:sreg_32 = S_MOV_B32 5
+ %7:sreg_32 = V_CMP_NE_U32_e64 %5, killed %6, implicit $exec
+ %8:sreg_32 = S_XOR_B32 %7, -1, implicit-def $scc
+ %9:sreg_32 = S_AND_B32 %8, $exec_lo, implicit-def $scc
+ %10:sreg_32 = S_XOR_B32 $exec_lo, %9, implicit-def $scc
+ $exec_lo = S_MOV_B32_term %9
+ S_CBRANCH_EXECZ %bb.2, implicit $exec
+ S_BRANCH %bb.1
+
+ bb.1:
+ successors: %bb.2(0x80000000)
+
+ %0:sgpr_32 = IMPLICIT_DEF
+ S_BRANCH %bb.2
+
+ bb.2:
+ successors: %bb.3(0x40000000), %bb.4(0x40000000)
+
+ $exec_lo = S_OR_B32 $exec_lo, %10, implicit-def $scc
+ %4:vgpr_32 = V_LSHRREV_B32_e64 %3, %2, implicit $exec
+ %11:sreg_32 = S_MOV_B32 31
+ %12:sreg_32 = V_CMP_NE_U32_e64 %5, killed %11, implicit $exec
+ %13:sreg_32 = S_XOR_B32 %12, -1, implicit-def $scc
+ %14:sreg_32 = S_AND_B32 %13, $exec_lo, implicit-def $scc
+ %15:sreg_32 = S_XOR_B32 $exec_lo, %14, implicit-def $scc
+ $exec_lo = S_MOV_B32_term %14
+ S_CBRANCH_EXECZ %bb.4, implicit $exec
+ S_BRANCH %bb.3
+
+ bb.3:
+ successors: %bb.4(0x80000000)
+
+ S_BRANCH %bb.4
+
+ bb.4:
+ successors: %bb.5(0x80000000)
+
+ $exec_lo = S_OR_B32 $exec_lo, %15, implicit-def $scc
+ %1:sreg_32 = IMPLICIT_DEF
+ %16:sreg_32 = S_MOV_B32 16
+ %17:sreg_32 = S_MOV_B32 4
+ %18:vgpr_32 = nuw nsw V_LSHL_ADD_U32_e64 %4, %17, killed %16, implicit $exec
+ S_BRANCH %bb.5
+
+ bb.5:
+ $exec_lo = S_OR_B32 $exec_lo, %1, implicit-def $scc
+ S_ENDPGM 0
+
+...
diff --git a/llvm/test/tools/UpdateTestChecks/update_mir_test_checks/amdgpu-filter.test b/llvm/test/tools/UpdateTestChecks/update_mir_test_checks/amdgpu-filter.test
new file mode 100644
index 0000000000000..3c9f88c2a1517
--- /dev/null
+++ b/llvm/test/tools/UpdateTestChecks/update_mir_test_checks/amdgpu-filter.test
@@ -0,0 +1,13 @@
+# REQUIRES: amdgpu-registered-target
+
+## Check that --filter works properly.
+# RUN: cp -f %S/Inputs/amdgpu-scattered-filter.mir %t.mir && %update_mir_test_checks --filter="exec_lo|IMPLICIT_DEF" %t.mir
+# RUN: diff -u %t.mir %S/Inputs/amdgpu-scattered-filter.mir.filter.expected
+
+## Check that running the script again does not change the result:
+# RUN: %update_mir_test_checks --filter="exec_lo|IMPLICIT_DEF" %t.mir
+# RUN: diff -u %t.mir %S/Inputs/amdgpu-scattered-filter.mir.filter.expected
+
+# RUN: cp -f %S/Inputs/amdgpu-scattered-filter.mir %t.mir && %update_mir_test_checks %t.mir
+# RUN: %update_mir_test_checks --filter="exec_lo|IMPLICIT_DEF" %t.mir
+# RUN: diff -u %t.mir %S/Inputs/amdgpu-scattered-filter.mir.filter.expected
diff --git a/llvm/utils/UpdateTestChecks/mir.py b/llvm/utils/UpdateTestChecks/mir.py
index 5b269167b8db8..e5f381edcb343 100644
--- a/llvm/utils/UpdateTestChecks/mir.py
+++ b/llvm/utils/UpdateTestChecks/mir.py
@@ -50,8 +50,6 @@
flags=(re.M | re.S),
)
-MD_PTR_RE = re.compile(r"\<0x[a-f0-9]+\>", re.IGNORECASE)
-
# A marker inserted into a filtered MIR function body to record that one or more
# lines were removed by --filter between two retained lines. When emitting
# checks, the retained line that follows a marker is printed as CHECK (rather
@@ -59,7 +57,6 @@
# previous match in the tool output.
MIR_FILTER_GAP_MARKER = "; UTC-MIR-FILTER-GAP"
-
def filter_mir_body(body, filters):
"""Filter a MIR function body while keeping the generated checks correct.
More information about the llvm-commits
mailing list