[llvm] [utils][UpdateLLCTestChecks] Add MIR support to update_llc_test_checks.py. (PR #164965)

Valery Pykhtin via llvm-commits llvm-commits at lists.llvm.org
Tue Oct 28 00:32:16 PDT 2025


https://github.com/vpykhtin updated https://github.com/llvm/llvm-project/pull/164965

>From 5f596b62cc77bde17360b4834d279d94db09f0f5 Mon Sep 17 00:00:00 2001
From: Valery Pykhtin <valery.pykhtin at amd.com>
Date: Fri, 24 Oct 2025 11:41:06 +0000
Subject: [PATCH 1/4] [utils][UpdateLLCTestChecks] Add MIR support to
 update_llc_test_checks.py

This change enables update_llc_test_checks.py to automatically generate
MIR checks for RUN lines that use -stop-before or -stop-after flags.

This allows tests to verify intermediate compilation stages (e.g., after
instruction selection but before peephole optimizations) alongside the
final assembly output.
---
 .../Inputs/x86_asm_mir_mixed.ll               | 17 ++++++
 .../Inputs/x86_asm_mir_mixed.ll.expected      | 47 ++++++++++++++++
 .../x86-asm-mir-mixed.test                    |  5 ++
 llvm/utils/UpdateTestChecks/common.py         |  1 +
 llvm/utils/update_llc_test_checks.py          | 55 +++++++++++++++++--
 5 files changed, 119 insertions(+), 6 deletions(-)
 create mode 100644 llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/x86_asm_mir_mixed.ll
 create mode 100644 llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/x86_asm_mir_mixed.ll.expected
 create mode 100644 llvm/test/tools/UpdateTestChecks/update_llc_test_checks/x86-asm-mir-mixed.test

diff --git a/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/x86_asm_mir_mixed.ll b/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/x86_asm_mir_mixed.ll
new file mode 100644
index 0000000000000..3c3dd01e4239d
--- /dev/null
+++ b/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/x86_asm_mir_mixed.ll
@@ -0,0 +1,17 @@
+; RUN: llc -mtriple=x86_64 < %s | FileCheck %s --check-prefix=ASM
+; RUN: llc -mtriple=x86_64 -stop-after=finalize-isel < %s | FileCheck %s --check-prefix=MIR
+
+define i64 @test1(i64 %i) nounwind readnone {
+  %loc = alloca i64
+  %j = load i64, i64 * %loc
+  %r = add i64 %i, %j
+  ret i64 %r
+}
+
+define i64 @test2(i32 %i) nounwind readnone {
+  %loc = alloca i32
+  %j = load i32, i32 * %loc
+  %r = add i32 %i, %j
+  %ext = zext i32 %r to i64
+  ret i64 %ext
+}
diff --git a/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/x86_asm_mir_mixed.ll.expected b/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/x86_asm_mir_mixed.ll.expected
new file mode 100644
index 0000000000000..03a79408c7e47
--- /dev/null
+++ b/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/x86_asm_mir_mixed.ll.expected
@@ -0,0 +1,47 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=x86_64 < %s | FileCheck %s --check-prefix=ASM
+; RUN: llc -mtriple=x86_64 -stop-after=finalize-isel < %s | FileCheck %s --check-prefix=MIR
+
+define i64 @test1(i64 %i) nounwind readnone {
+; ASM-LABEL: test1:
+; ASM:       # %bb.0:
+; ASM-NEXT:    movq %rdi, %rax
+; ASM-NEXT:    addq -{{[0-9]+}}(%rsp), %rax
+; ASM-NEXT:    retq
+  ; MIR-LABEL: name: test1
+  ; MIR: bb.0 (%ir-block.0):
+  ; MIR-NEXT:   liveins: $rdi
+  ; MIR-NEXT: {{  $}}
+  ; MIR-NEXT:   [[COPY:%[0-9]+]]:gr64 = COPY $rdi
+  ; MIR-NEXT:   [[ADD64rm:%[0-9]+]]:gr64 = ADD64rm [[COPY]], %stack.0.loc, 1, $noreg, 0, $noreg, implicit-def dead $eflags :: (dereferenceable load (s64) from %ir.loc)
+  ; MIR-NEXT:   $rax = COPY [[ADD64rm]]
+  ; MIR-NEXT:   RET 0, $rax
+  %loc = alloca i64
+  %j = load i64, i64 * %loc
+  %r = add i64 %i, %j
+  ret i64 %r
+}
+
+define i64 @test2(i32 %i) nounwind readnone {
+; ASM-LABEL: test2:
+; ASM:       # %bb.0:
+; ASM-NEXT:    movl %edi, %eax
+; ASM-NEXT:    addl -{{[0-9]+}}(%rsp), %eax
+; ASM-NEXT:    retq
+  ; MIR-LABEL: name: test2
+  ; MIR: bb.0 (%ir-block.0):
+  ; MIR-NEXT:   liveins: $edi
+  ; MIR-NEXT: {{  $}}
+  ; MIR-NEXT:   [[COPY:%[0-9]+]]:gr32 = COPY $edi
+  ; MIR-NEXT:   [[ADD32rm:%[0-9]+]]:gr32 = ADD32rm [[COPY]], %stack.0.loc, 1, $noreg, 0, $noreg, implicit-def dead $eflags :: (dereferenceable load (s32) from %ir.loc)
+  ; MIR-NEXT:   [[SUBREG_TO_REG:%[0-9]+]]:gr64 = SUBREG_TO_REG 0, killed [[ADD32rm]], %subreg.sub_32bit
+  ; MIR-NEXT:   $rax = COPY [[SUBREG_TO_REG]]
+  ; MIR-NEXT:   RET 0, $rax
+  %loc = alloca i32
+  %j = load i32, i32 * %loc
+  %r = add i32 %i, %j
+  %ext = zext i32 %r to i64
+  ret i64 %ext
+}
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+; MIR: {{.*}}
diff --git a/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/x86-asm-mir-mixed.test b/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/x86-asm-mir-mixed.test
new file mode 100644
index 0000000000000..57b6d48147949
--- /dev/null
+++ b/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/x86-asm-mir-mixed.test
@@ -0,0 +1,5 @@
+# REQUIRES: x86-registered-target
+## Test checking that update_llc_test_checks.py can generate both ASM and MIR checks in the same file
+
+# RUN: cp -f %S/Inputs/x86_asm_mir_mixed.ll %t.ll && %update_llc_test_checks %t.ll
+# RUN: diff -u %S/Inputs/x86_asm_mir_mixed.ll.expected %t.ll
diff --git a/llvm/utils/UpdateTestChecks/common.py b/llvm/utils/UpdateTestChecks/common.py
index a5e3c39bfdecd..eab0977ed7200 100644
--- a/llvm/utils/UpdateTestChecks/common.py
+++ b/llvm/utils/UpdateTestChecks/common.py
@@ -604,6 +604,7 @@ def invoke_tool(exe, cmd_args, ir, preprocess_cmd=None, verbose=False):
 TRIPLE_ARG_RE = re.compile(r"-m?triple[= ]([^ ]+)")
 MARCH_ARG_RE = re.compile(r"-march[= ]([^ ]+)")
 DEBUG_ONLY_ARG_RE = re.compile(r"-debug-only[= ]([^ ]+)")
+STOP_PASS_RE = re.compile(r"-stop-(before|after)=(\w+)")
 
 IS_DEBUG_RECORD_RE = re.compile(r"^(\s+)#dbg_")
 
diff --git a/llvm/utils/update_llc_test_checks.py b/llvm/utils/update_llc_test_checks.py
index 8c57e75f34f75..2a160cb82a273 100755
--- a/llvm/utils/update_llc_test_checks.py
+++ b/llvm/utils/update_llc_test_checks.py
@@ -16,6 +16,7 @@
 import sys
 
 from UpdateTestChecks import common
+import update_mir_test_checks  # Reuse MIR parsing code.
 
 # llc is the only llc-like in the LLVM tree but downstream forks can add
 # additional ones here if they have them.
@@ -119,6 +120,10 @@ def update_test(ti: common.TestInfo):
         ginfo=ginfo,
     )
 
+    # Dictionary to store MIR function bodies separately
+    mir_func_dict = {}
+    mir_run_list = []
+
     for (
         prefixes,
         llc_tool,
@@ -141,14 +146,37 @@ def update_test(ti: common.TestInfo):
         if not triple:
             triple = common.get_triple_from_march(march_in_cmd)
 
-        scrubber, function_re = output_type.get_run_handler(triple)
-        if 0 == builder.process_run_line(
-            function_re, scrubber, raw_tool_output, prefixes
+        # Check if we are in MIR output mode. If -debug-only is present assume
+        # the debug output is the main point of interest.
+        if common.STOP_PASS_RE.search(llc_args) and not common.DEBUG_ONLY_ARG_RE.search(
+            llc_args
         ):
-            common.warn(
-                "Couldn't match any function. Possibly the wrong target triple has been provided"
+            common.debug("Detected MIR output mode for prefixes:", str(prefixes))
+            for prefix in prefixes:
+                if prefix not in mir_func_dict:
+                    mir_func_dict[prefix] = {}
+
+            update_mir_test_checks.build_function_info_dictionary(
+                ti.path,
+                raw_tool_output,
+                triple,
+                prefixes,
+                mir_func_dict,
+                ti.args.verbose,
+            )
+
+            mir_run_list.append(
+                (prefixes, llc_tool, llc_args, triple_in_cmd, march_in_cmd)
             )
-        builder.processed_prefixes(prefixes)
+        else:
+            scrubber, function_re = output_type.get_run_handler(triple)
+            if 0 == builder.process_run_line(
+                function_re, scrubber, raw_tool_output, prefixes
+            ):
+                common.warn(
+                    "Couldn't match any function. Possibly the wrong target triple has been provided"
+                )
+            builder.processed_prefixes(prefixes)
 
     func_dict = builder.finish_and_get_func_dict()
     global_vars_seen_dict = {}
@@ -221,6 +249,21 @@ def update_test(ti: common.TestInfo):
                         is_filtered=builder.is_filtered(),
                     )
                 )
+
+                # Also add MIR checks if we have them for this function
+                if mir_run_list and func_name:
+                    common.add_mir_checks_for_function(
+                        ti.path,
+                        output_lines,
+                        mir_run_list,
+                        mir_func_dict,
+                        func_name,
+                        single_bb=False,  # Don't skip basic block labels.
+                        print_fixed_stack=False,  # Don't print fixed stack (ASM tests don't need it).
+                        first_check_is_next=False,  # First check is LABEL, not NEXT.
+                        at_the_function_name=False,  # Use "name:" not "@name".
+                    )
+
                 is_in_function_start = False
 
             if is_in_function:

>From d9e9519adbfdb7a53679e64c9a7220c695b7aff4 Mon Sep 17 00:00:00 2001
From: Valery Pykhtin <valery.pykhtin at amd.com>
Date: Sun, 26 Oct 2025 14:01:41 +0000
Subject: [PATCH 2/4] move MIR parsing code to the common

---
 llvm/utils/UpdateTestChecks/common.py | 115 +++++++++++++++++++++++++
 llvm/utils/update_llc_test_checks.py  |   3 +-
 llvm/utils/update_mir_test_checks.py  | 118 +-------------------------
 3 files changed, 117 insertions(+), 119 deletions(-)

diff --git a/llvm/utils/UpdateTestChecks/common.py b/llvm/utils/UpdateTestChecks/common.py
index eab0977ed7200..11449a2693018 100644
--- a/llvm/utils/UpdateTestChecks/common.py
+++ b/llvm/utils/UpdateTestChecks/common.py
@@ -2404,6 +2404,121 @@ def add_analyze_checks(
 MIR_BASIC_BLOCK_RE = re.compile(r" *bb\.[0-9]+.*:$")
 MIR_PREFIX_DATA_RE = re.compile(r"^ *(;|bb.[0-9].*: *$|[a-z]+:( |$)|$)")
 
+# Patterns for MIR function parsing
+VREG_RE = re.compile(r"(%[0-9]+)(?:\.[a-z0-9_]+)?(?::[a-z0-9_]+)?(?:\([<>a-z0-9 ]+\))?")
+MI_FLAGS_STR = (
+    r"(frame-setup |frame-destroy |nnan |ninf |nsz |arcp |contract |afn "
+    r"|reassoc |nuw |nsw |exact |nofpexcept |nomerge |unpredictable "
+    r"|noconvergent |nneg |disjoint |nusw |samesign |inbounds )*"
+)
+VREG_DEF_FLAGS_STR = r"(?:dead |undef )*"
+
+# Pattern to match the defined vregs and the opcode of an instruction that
+# defines vregs. Opcodes starting with a lower-case 't' are allowed to match
+# ARM's thumb instructions, like tADDi8 and t2ADDri.
+VREG_DEF_RE = re.compile(
+    r"^ *(?P<vregs>{2}{0}(?:, {2}{0})*) = "
+    r"{1}(?P<opcode>[A-Zt][A-Za-z0-9_]+)".format(
+        VREG_RE.pattern, MI_FLAGS_STR, VREG_DEF_FLAGS_STR
+    )
+)
+
+MIR_FUNC_RE = re.compile(
+    r"^---$"
+    r"\n"
+    r"^ *name: *(?P<func>[A-Za-z0-9_.-]+)$"
+    r".*?"
+    r"(?:^ *fixedStack: *(\[\])? *\n"
+    r"(?P<fixedStack>.*?)\n?"
+    r"^ *stack:"
+    r".*?)?"
+    r"^ *body: *\|\n"
+    r"(?P<body>.*?)\n"
+    r"^\.\.\.$",
+    flags=(re.M | re.S),
+)
+
+
+def mangle_vreg(opcode, current_names):
+    base = opcode
+    # Simplify some common prefixes and suffixes
+    if opcode.startswith("G_"):
+        base = base[len("G_") :]
+    if opcode.endswith("_PSEUDO"):
+        base = base[: -len("_PSEUDO")]
+    # Shorten some common opcodes with long-ish names
+    base = dict(
+        IMPLICIT_DEF="DEF",
+        GLOBAL_VALUE="GV",
+        CONSTANT="C",
+        FCONSTANT="C",
+        MERGE_VALUES="MV",
+        UNMERGE_VALUES="UV",
+        INTRINSIC="INT",
+        INTRINSIC_W_SIDE_EFFECTS="INT",
+        INSERT_VECTOR_ELT="IVEC",
+        EXTRACT_VECTOR_ELT="EVEC",
+        SHUFFLE_VECTOR="SHUF",
+    ).get(base, base)
+    # Avoid ambiguity when opcodes end in numbers
+    if len(base.rstrip("0123456789")) < len(base):
+        base += "_"
+
+    i = 0
+    for name in current_names:
+        if name.rstrip("0123456789") == base:
+            i += 1
+    if i:
+        return "{}{}".format(base, i)
+    return base
+
+
+def build_function_info_dictionary(
+    test, raw_tool_output, triple, prefixes, func_dict, verbose
+):
+    for m in MIR_FUNC_RE.finditer(raw_tool_output):
+        func = m.group("func")
+        fixedStack = m.group("fixedStack")
+        body = m.group("body")
+        if verbose:
+            debug("Processing function: {}".format(func))
+            for l in body.splitlines():
+                debug("  {}".format(l))
+
+        # Vreg mangling
+        mangled = []
+        vreg_map = {}
+        for func_line in body.splitlines(keepends=True):
+            m = VREG_DEF_RE.match(func_line)
+            if m:
+                for vreg in VREG_RE.finditer(m.group("vregs")):
+                    if vreg.group(1) in vreg_map:
+                        name = vreg_map[vreg.group(1)]
+                    else:
+                        name = mangle_vreg(m.group("opcode"), vreg_map.values())
+                        vreg_map[vreg.group(1)] = name
+                    func_line = func_line.replace(
+                        vreg.group(1), "[[{}:%[0-9]+]]".format(name), 1
+                    )
+            for number, name in vreg_map.items():
+                func_line = re.sub(
+                    r"{}\b".format(number), "[[{}]]".format(name), func_line
+                )
+            mangled.append(func_line)
+        body = "".join(mangled)
+
+        for prefix in prefixes:
+            info = function_body(body, fixedStack, None, None, None, None, ginfo=None)
+            if func in func_dict[prefix]:
+                if (
+                    not func_dict[prefix][func]
+                    or func_dict[prefix][func].scrub != info.scrub
+                    or func_dict[prefix][func].extrascrub != info.extrascrub
+                ):
+                    func_dict[prefix][func] = None
+            else:
+                func_dict[prefix][func] = info
+
 
 def find_mir_functions_with_one_bb(lines, verbose=False):
     result = []
diff --git a/llvm/utils/update_llc_test_checks.py b/llvm/utils/update_llc_test_checks.py
index 2a160cb82a273..079d05352238b 100755
--- a/llvm/utils/update_llc_test_checks.py
+++ b/llvm/utils/update_llc_test_checks.py
@@ -16,7 +16,6 @@
 import sys
 
 from UpdateTestChecks import common
-import update_mir_test_checks  # Reuse MIR parsing code.
 
 # llc is the only llc-like in the LLVM tree but downstream forks can add
 # additional ones here if they have them.
@@ -156,7 +155,7 @@ def update_test(ti: common.TestInfo):
                 if prefix not in mir_func_dict:
                     mir_func_dict[prefix] = {}
 
-            update_mir_test_checks.build_function_info_dictionary(
+            common.build_function_info_dictionary(
                 ti.path,
                 raw_tool_output,
                 triple,
diff --git a/llvm/utils/update_mir_test_checks.py b/llvm/utils/update_mir_test_checks.py
index c4ee0523a6469..dd2763917efa6 100755
--- a/llvm/utils/update_mir_test_checks.py
+++ b/llvm/utils/update_mir_test_checks.py
@@ -32,39 +32,6 @@
 
 from UpdateTestChecks import common
 
-VREG_RE = re.compile(r"(%[0-9]+)(?:\.[a-z0-9_]+)?(?::[a-z0-9_]+)?(?:\([<>a-z0-9 ]+\))?")
-MI_FLAGS_STR = (
-    r"(frame-setup |frame-destroy |nnan |ninf |nsz |arcp |contract |afn "
-    r"|reassoc |nuw |nsw |exact |nofpexcept |nomerge |unpredictable "
-    r"|noconvergent |nneg |disjoint |nusw |samesign |inbounds )*"
-)
-VREG_DEF_FLAGS_STR = r"(?:dead |undef )*"
-
-# Pattern to match the defined vregs and the opcode of an instruction that
-# defines vregs. Opcodes starting with a lower-case 't' are allowed to match
-# ARM's thumb instructions, like tADDi8 and t2ADDri.
-VREG_DEF_RE = re.compile(
-    r"^ *(?P<vregs>{2}{0}(?:, {2}{0})*) = "
-    r"{1}(?P<opcode>[A-Zt][A-Za-z0-9_]+)".format(
-        VREG_RE.pattern, MI_FLAGS_STR, VREG_DEF_FLAGS_STR
-    )
-)
-
-MIR_FUNC_RE = re.compile(
-    r"^---$"
-    r"\n"
-    r"^ *name: *(?P<func>[A-Za-z0-9_.-]+)$"
-    r".*?"
-    r"(?:^ *fixedStack: *(\[\])? *\n"
-    r"(?P<fixedStack>.*?)\n?"
-    r"^ *stack:"
-    r".*?)?"
-    r"^ *body: *\|\n"
-    r"(?P<body>.*?)\n"
-    r"^\.\.\.$",
-    flags=(re.M | re.S),
-)
-
 
 class LLC:
     def __init__(self, bin):
@@ -143,89 +110,6 @@ def build_run_list(test, run_lines, verbose=False):
     return run_list
 
 
-def build_function_info_dictionary(
-    test, raw_tool_output, triple, prefixes, func_dict, verbose
-):
-    for m in MIR_FUNC_RE.finditer(raw_tool_output):
-        func = m.group("func")
-        fixedStack = m.group("fixedStack")
-        body = m.group("body")
-        if verbose:
-            log("Processing function: {}".format(func))
-            for l in body.splitlines():
-                log("  {}".format(l))
-
-        # Vreg mangling
-        mangled = []
-        vreg_map = {}
-        for func_line in body.splitlines(keepends=True):
-            m = VREG_DEF_RE.match(func_line)
-            if m:
-                for vreg in VREG_RE.finditer(m.group("vregs")):
-                    if vreg.group(1) in vreg_map:
-                        name = vreg_map[vreg.group(1)]
-                    else:
-                        name = mangle_vreg(m.group("opcode"), vreg_map.values())
-                        vreg_map[vreg.group(1)] = name
-                    func_line = func_line.replace(
-                        vreg.group(1), "[[{}:%[0-9]+]]".format(name), 1
-                    )
-            for number, name in vreg_map.items():
-                func_line = re.sub(
-                    r"{}\b".format(number), "[[{}]]".format(name), func_line
-                )
-            mangled.append(func_line)
-        body = "".join(mangled)
-
-        for prefix in prefixes:
-            info = common.function_body(
-                body, fixedStack, None, None, None, None, ginfo=None
-            )
-            if func in func_dict[prefix]:
-                if (
-                    not func_dict[prefix][func]
-                    or func_dict[prefix][func].scrub != info.scrub
-                    or func_dict[prefix][func].extrascrub != info.extrascrub
-                ):
-                    func_dict[prefix][func] = None
-            else:
-                func_dict[prefix][func] = info
-
-
-def mangle_vreg(opcode, current_names):
-    base = opcode
-    # Simplify some common prefixes and suffixes
-    if opcode.startswith("G_"):
-        base = base[len("G_") :]
-    if opcode.endswith("_PSEUDO"):
-        base = base[: len("_PSEUDO")]
-    # Shorten some common opcodes with long-ish names
-    base = dict(
-        IMPLICIT_DEF="DEF",
-        GLOBAL_VALUE="GV",
-        CONSTANT="C",
-        FCONSTANT="C",
-        MERGE_VALUES="MV",
-        UNMERGE_VALUES="UV",
-        INTRINSIC="INT",
-        INTRINSIC_W_SIDE_EFFECTS="INT",
-        INSERT_VECTOR_ELT="IVEC",
-        EXTRACT_VECTOR_ELT="EVEC",
-        SHUFFLE_VECTOR="SHUF",
-    ).get(base, base)
-    # Avoid ambiguity when opcodes end in numbers
-    if len(base.rstrip("0123456789")) < len(base):
-        base += "_"
-
-    i = 0
-    for name in current_names:
-        if name.rstrip("0123456789") == base:
-            i += 1
-    if i:
-        return "{}{}".format(base, i)
-    return base
-
-
 def update_test_file(args, test, autogenerated_note):
     with open(test) as fd:
         input_lines = [l.rstrip() for l in fd]
@@ -247,7 +131,7 @@ def update_test_file(args, test, autogenerated_note):
             common.warn("No triple found: skipping file", test_file=test)
             return
 
-        build_function_info_dictionary(
+        common.build_function_info_dictionary(
             test,
             raw_tool_output,
             triple_in_cmd or triple_in_ir,

>From 1e97c19eee43f9bacb82e69ffae060a0d81ce517 Mon Sep 17 00:00:00 2001
From: Valery Pykhtin <valery.pykhtin at amd.com>
Date: Tue, 28 Oct 2025 07:20:20 +0000
Subject: [PATCH 3/4] Rename build_function_info_dictionary to
 build_mir_function_info_dictionary.

---
 llvm/utils/UpdateTestChecks/common.py | 2 +-
 llvm/utils/update_llc_test_checks.py  | 2 +-
 llvm/utils/update_mir_test_checks.py  | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/llvm/utils/UpdateTestChecks/common.py b/llvm/utils/UpdateTestChecks/common.py
index 11449a2693018..dc8f4eb493055 100644
--- a/llvm/utils/UpdateTestChecks/common.py
+++ b/llvm/utils/UpdateTestChecks/common.py
@@ -2473,7 +2473,7 @@ def mangle_vreg(opcode, current_names):
     return base
 
 
-def build_function_info_dictionary(
+def build_mir_function_info_dictionary(
     test, raw_tool_output, triple, prefixes, func_dict, verbose
 ):
     for m in MIR_FUNC_RE.finditer(raw_tool_output):
diff --git a/llvm/utils/update_llc_test_checks.py b/llvm/utils/update_llc_test_checks.py
index 079d05352238b..63ec03a3d3076 100755
--- a/llvm/utils/update_llc_test_checks.py
+++ b/llvm/utils/update_llc_test_checks.py
@@ -155,7 +155,7 @@ def update_test(ti: common.TestInfo):
                 if prefix not in mir_func_dict:
                     mir_func_dict[prefix] = {}
 
-            common.build_function_info_dictionary(
+            common.build_mir_function_info_dictionary(
                 ti.path,
                 raw_tool_output,
                 triple,
diff --git a/llvm/utils/update_mir_test_checks.py b/llvm/utils/update_mir_test_checks.py
index dd2763917efa6..866694e13f522 100755
--- a/llvm/utils/update_mir_test_checks.py
+++ b/llvm/utils/update_mir_test_checks.py
@@ -131,7 +131,7 @@ def update_test_file(args, test, autogenerated_note):
             common.warn("No triple found: skipping file", test_file=test)
             return
 
-        common.build_function_info_dictionary(
+        common.build_mir_function_info_dictionary(
             test,
             raw_tool_output,
             triple_in_cmd or triple_in_ir,

>From 2d34e0477dca23da6bf74fa2bcee6279bd70e6e7 Mon Sep 17 00:00:00 2001
From: Valery Pykhtin <valery.pykhtin at amd.com>
Date: Tue, 28 Oct 2025 07:30:35 +0000
Subject: [PATCH 4/4] Remove extra indentation from MIR check lines

---
 .../Inputs/x86_asm_mir_mixed.ll.expected      | 34 +++++++++----------
 llvm/utils/UpdateTestChecks/common.py         |  9 ++++-
 llvm/utils/update_llc_test_checks.py          |  1 +
 3 files changed, 26 insertions(+), 18 deletions(-)

diff --git a/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/x86_asm_mir_mixed.ll.expected b/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/x86_asm_mir_mixed.ll.expected
index 03a79408c7e47..e7acd2e36a4d6 100644
--- a/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/x86_asm_mir_mixed.ll.expected
+++ b/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/x86_asm_mir_mixed.ll.expected
@@ -8,14 +8,14 @@ define i64 @test1(i64 %i) nounwind readnone {
 ; ASM-NEXT:    movq %rdi, %rax
 ; ASM-NEXT:    addq -{{[0-9]+}}(%rsp), %rax
 ; ASM-NEXT:    retq
-  ; MIR-LABEL: name: test1
-  ; MIR: bb.0 (%ir-block.0):
-  ; MIR-NEXT:   liveins: $rdi
-  ; MIR-NEXT: {{  $}}
-  ; MIR-NEXT:   [[COPY:%[0-9]+]]:gr64 = COPY $rdi
-  ; MIR-NEXT:   [[ADD64rm:%[0-9]+]]:gr64 = ADD64rm [[COPY]], %stack.0.loc, 1, $noreg, 0, $noreg, implicit-def dead $eflags :: (dereferenceable load (s64) from %ir.loc)
-  ; MIR-NEXT:   $rax = COPY [[ADD64rm]]
-  ; MIR-NEXT:   RET 0, $rax
+; MIR-LABEL: name: test1
+; MIR: bb.0 (%ir-block.0):
+; MIR-NEXT:   liveins: $rdi
+; MIR-NEXT: {{  $}}
+; MIR-NEXT:   [[COPY:%[0-9]+]]:gr64 = COPY $rdi
+; MIR-NEXT:   [[ADD64rm:%[0-9]+]]:gr64 = ADD64rm [[COPY]], %stack.0.loc, 1, $noreg, 0, $noreg, implicit-def dead $eflags :: (dereferenceable load (s64) from %ir.loc)
+; MIR-NEXT:   $rax = COPY [[ADD64rm]]
+; MIR-NEXT:   RET 0, $rax
   %loc = alloca i64
   %j = load i64, i64 * %loc
   %r = add i64 %i, %j
@@ -28,15 +28,15 @@ define i64 @test2(i32 %i) nounwind readnone {
 ; ASM-NEXT:    movl %edi, %eax
 ; ASM-NEXT:    addl -{{[0-9]+}}(%rsp), %eax
 ; ASM-NEXT:    retq
-  ; MIR-LABEL: name: test2
-  ; MIR: bb.0 (%ir-block.0):
-  ; MIR-NEXT:   liveins: $edi
-  ; MIR-NEXT: {{  $}}
-  ; MIR-NEXT:   [[COPY:%[0-9]+]]:gr32 = COPY $edi
-  ; MIR-NEXT:   [[ADD32rm:%[0-9]+]]:gr32 = ADD32rm [[COPY]], %stack.0.loc, 1, $noreg, 0, $noreg, implicit-def dead $eflags :: (dereferenceable load (s32) from %ir.loc)
-  ; MIR-NEXT:   [[SUBREG_TO_REG:%[0-9]+]]:gr64 = SUBREG_TO_REG 0, killed [[ADD32rm]], %subreg.sub_32bit
-  ; MIR-NEXT:   $rax = COPY [[SUBREG_TO_REG]]
-  ; MIR-NEXT:   RET 0, $rax
+; MIR-LABEL: name: test2
+; MIR: bb.0 (%ir-block.0):
+; MIR-NEXT:   liveins: $edi
+; MIR-NEXT: {{  $}}
+; MIR-NEXT:   [[COPY:%[0-9]+]]:gr32 = COPY $edi
+; MIR-NEXT:   [[ADD32rm:%[0-9]+]]:gr32 = ADD32rm [[COPY]], %stack.0.loc, 1, $noreg, 0, $noreg, implicit-def dead $eflags :: (dereferenceable load (s32) from %ir.loc)
+; MIR-NEXT:   [[SUBREG_TO_REG:%[0-9]+]]:gr64 = SUBREG_TO_REG 0, killed [[ADD32rm]], %subreg.sub_32bit
+; MIR-NEXT:   $rax = COPY [[SUBREG_TO_REG]]
+; MIR-NEXT:   RET 0, $rax
   %loc = alloca i32
   %j = load i32, i32 * %loc
   %r = add i32 %i, %j
diff --git a/llvm/utils/UpdateTestChecks/common.py b/llvm/utils/UpdateTestChecks/common.py
index dc8f4eb493055..202ec22ecb62c 100644
--- a/llvm/utils/UpdateTestChecks/common.py
+++ b/llvm/utils/UpdateTestChecks/common.py
@@ -2549,6 +2549,7 @@ def add_mir_checks_for_function(
     print_fixed_stack,
     first_check_is_next,
     at_the_function_name,
+    check_indent=None,
 ):
     printed_prefixes = set()
     for run in run_list:
@@ -2571,6 +2572,7 @@ def add_mir_checks_for_function(
                 func_dict[prefix][func_name],
                 print_fixed_stack,
                 first_check_is_next,
+                check_indent,
             )
             break
         else:
@@ -2590,6 +2592,7 @@ def add_mir_check_lines(
     func_info,
     print_fixed_stack,
     first_check_is_next,
+    check_indent=None,
 ):
     func_body = str(func_info).splitlines()
     if single_bb:
@@ -2606,7 +2609,11 @@ def add_mir_check_lines(
     first_line = func_body[0]
     indent = len(first_line) - len(first_line.lstrip(" "))
     # A check comment, indented the appropriate amount
-    check = "{:>{}}; {}".format("", indent, prefix)
+    # If check_indent is provided, use it; otherwise, auto-detect from MIR body
+    if check_indent is not None:
+        check = "{}; {}".format(check_indent, prefix)
+    else:
+        check = "{:>{}}; {}".format("", indent, prefix)
 
     output_lines.append("{}-LABEL: name: {}".format(check, func_name))
 
diff --git a/llvm/utils/update_llc_test_checks.py b/llvm/utils/update_llc_test_checks.py
index 63ec03a3d3076..56b7860c8618d 100755
--- a/llvm/utils/update_llc_test_checks.py
+++ b/llvm/utils/update_llc_test_checks.py
@@ -261,6 +261,7 @@ def update_test(ti: common.TestInfo):
                         print_fixed_stack=False,  # Don't print fixed stack (ASM tests don't need it).
                         first_check_is_next=False,  # First check is LABEL, not NEXT.
                         at_the_function_name=False,  # Use "name:" not "@name".
+                        check_indent="",  # No indentation for IR files (not MIR files).
                     )
 
                 is_in_function_start = False



More information about the llvm-commits mailing list