Skip to content

Commit 74e3a74

Browse files
committed
[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. Remove extra indentation from MIR check lines conflict processing
1 parent 44f5ae3 commit 74e3a74

File tree

9 files changed

+194
-19
lines changed

9 files changed

+194
-19
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
; RUN: llc -mtriple=x86_64 < %s | FileCheck %s --check-prefix=ASM
2+
; RUN: llc -mtriple=x86_64 -stop-after=finalize-isel < %s | FileCheck %s --check-prefix=MIR
3+
4+
define i64 @test1(i64 %i) nounwind readnone {
5+
%loc = alloca i64
6+
%j = load i64, i64 * %loc
7+
%r = add i64 %i, %j
8+
ret i64 %r
9+
}
10+
11+
define i64 @test2(i32 %i) nounwind readnone {
12+
%loc = alloca i32
13+
%j = load i32, i32 * %loc
14+
%r = add i32 %i, %j
15+
%ext = zext i32 %r to i64
16+
ret i64 %ext
17+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
2+
; RUN: llc -mtriple=x86_64 < %s | FileCheck %s --check-prefix=ASM
3+
; RUN: llc -mtriple=x86_64 -stop-after=finalize-isel < %s | FileCheck %s --check-prefix=MIR
4+
5+
define i64 @test1(i64 %i) nounwind readnone {
6+
; ASM-LABEL: test1:
7+
; ASM: # %bb.0:
8+
; ASM-NEXT: movq %rdi, %rax
9+
; ASM-NEXT: addq -{{[0-9]+}}(%rsp), %rax
10+
; ASM-NEXT: retq
11+
; MIR-LABEL: name: test1
12+
; MIR: bb.0 (%ir-block.0):
13+
; MIR-NEXT: liveins: $rdi
14+
; MIR-NEXT: {{ $}}
15+
; MIR-NEXT: [[COPY:%[0-9]+]]:gr64 = COPY $rdi
16+
; 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)
17+
; MIR-NEXT: $rax = COPY [[ADD64rm]]
18+
; MIR-NEXT: RET 0, $rax
19+
%loc = alloca i64
20+
%j = load i64, i64 * %loc
21+
%r = add i64 %i, %j
22+
ret i64 %r
23+
}
24+
25+
define i64 @test2(i32 %i) nounwind readnone {
26+
; ASM-LABEL: test2:
27+
; ASM: # %bb.0:
28+
; ASM-NEXT: movl %edi, %eax
29+
; ASM-NEXT: addl -{{[0-9]+}}(%rsp), %eax
30+
; ASM-NEXT: retq
31+
; MIR-LABEL: name: test2
32+
; MIR: bb.0 (%ir-block.0):
33+
; MIR-NEXT: liveins: $edi
34+
; MIR-NEXT: {{ $}}
35+
; MIR-NEXT: [[COPY:%[0-9]+]]:gr32 = COPY $edi
36+
; 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)
37+
; MIR-NEXT: [[SUBREG_TO_REG:%[0-9]+]]:gr64 = SUBREG_TO_REG 0, killed [[ADD32rm]], %subreg.sub_32bit
38+
; MIR-NEXT: $rax = COPY [[SUBREG_TO_REG]]
39+
; MIR-NEXT: RET 0, $rax
40+
%loc = alloca i32
41+
%j = load i32, i32 * %loc
42+
%r = add i32 %i, %j
43+
%ext = zext i32 %r to i64
44+
ret i64 %ext
45+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
2+
; RUN: llc -mtriple=x86_64 < %s | FileCheck %s --check-prefix=CHECK
3+
; RUN: llc -mtriple=x86_64 -stop-after=finalize-isel < %s | FileCheck %s --check-prefix=CHECK
4+
5+
define i32 @add(i32 %a, i32 %b) {
6+
%sum = add i32 %a, %b
7+
ret i32 %sum
8+
}
9+
10+
define i32 @sub(i32 %a, i32 %b) {
11+
%diff = sub i32 %a, %b
12+
ret i32 %diff
13+
}
14+
15+
;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
16+
; CHECK: {{.*}}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
2+
; RUN: llc -mtriple=x86_64 < %s | FileCheck %s --check-prefix=CHECK
3+
; RUN: llc -mtriple=x86_64 -stop-after=finalize-isel < %s | FileCheck %s --check-prefix=CHECK
4+
5+
define i32 @add(i32 %a, i32 %b) {
6+
%sum = add i32 %a, %b
7+
ret i32 %sum
8+
}
9+
10+
define i32 @sub(i32 %a, i32 %b) {
11+
%diff = sub i32 %a, %b
12+
ret i32 %diff
13+
}
14+
15+
;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
16+
; CHECK: {{.*}}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# REQUIRES: x86-registered-target
2+
## Test checking that update_llc_test_checks.py can generate both ASM and MIR checks in the same file
3+
4+
# RUN: cp -f %S/Inputs/x86_asm_mir_mixed.ll %t.ll && %update_llc_test_checks %t.ll
5+
# RUN: diff -u %S/Inputs/x86_asm_mir_mixed.ll.expected %t.ll
6+
## Verify that running the script again on an already updated file doesn't add duplicate checks
7+
# RUN: %update_llc_test_checks %t.ll
8+
# RUN: diff -u %S/Inputs/x86_asm_mir_mixed.ll.expected %t.ll
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
## Test that using the same prefix for both ASM and MIR outputs generates a warning
2+
## and only produces ASM checks (not MIR checks).
3+
4+
# RUN: cp -f %S/Inputs/x86_asm_mir_same_prefix.ll %t.ll && %update_llc_test_checks %t.ll 2>&1 | FileCheck %s --check-prefix=WARNING
5+
# RUN: diff -u %S/Inputs/x86_asm_mir_same_prefix.ll.expected %t.ll
6+
7+
# WARNING: WARNING: The following prefixes are used for both ASM and MIR output, which will cause FileCheck failures: CHECK

llvm/utils/UpdateTestChecks/common.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -605,6 +605,7 @@ def invoke_tool(exe, cmd_args, ir, preprocess_cmd=None, verbose=False):
605605
TRIPLE_ARG_RE = re.compile(r"-m?triple[= ]([^ ]+)")
606606
MARCH_ARG_RE = re.compile(r"-march[= ]([^ ]+)")
607607
DEBUG_ONLY_ARG_RE = re.compile(r"-debug-only[= ]([^ ]+)")
608+
STOP_PASS_RE = re.compile(r"-stop-(before|after)=(\w+)")
608609

609610
IS_DEBUG_RECORD_RE = re.compile(r"^(\s+)#dbg_")
610611
IS_SWITCH_CASE_RE = re.compile(r"^\s+i\d+ \d+, label %\w+")

llvm/utils/UpdateTestChecks/mir.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,13 +163,14 @@ def add_mir_checks_for_function(
163163
print_fixed_stack,
164164
first_check_is_next,
165165
at_the_function_name,
166+
check_indent=None,
166167
):
167168
printed_prefixes = set()
168169
for run in run_list:
169170
for prefix in run[0]:
170171
if prefix in printed_prefixes:
171172
break
172-
if not func_dict[prefix][func_name]:
173+
if not func_dict[prefix].get(func_name):
173174
continue
174175
if printed_prefixes:
175176
# Add some space between different check prefixes.
@@ -185,6 +186,7 @@ def add_mir_checks_for_function(
185186
func_dict[prefix][func_name],
186187
print_fixed_stack,
187188
first_check_is_next,
189+
check_indent,
188190
)
189191
break
190192
else:
@@ -204,6 +206,7 @@ def add_mir_check_lines(
204206
func_info,
205207
print_fixed_stack,
206208
first_check_is_next,
209+
check_indent=None,
207210
):
208211
func_body = str(func_info).splitlines()
209212
if single_bb:
@@ -220,7 +223,11 @@ def add_mir_check_lines(
220223
first_line = func_body[0]
221224
indent = len(first_line) - len(first_line.lstrip(" "))
222225
# A check comment, indented the appropriate amount
223-
check = "{:>{}}; {}".format("", indent, prefix)
226+
# If check_indent is provided, use it; otherwise, auto-detect from MIR body
227+
if check_indent is not None:
228+
check = "{}; {}".format(check_indent, prefix)
229+
else:
230+
check = "{:>{}}; {}".format("", indent, prefix)
224231

225232
output_lines.append("{}-LABEL: name: {}".format(check, func_name))
226233

llvm/utils/update_llc_test_checks.py

Lines changed: 75 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
import os # Used to advertise this file's name ("autogenerated_note").
1616
import sys
1717

18-
from UpdateTestChecks import common
18+
from UpdateTestChecks import common, mir
1919

2020
# llc is the only llc-like in the LLVM tree but downstream forks can add
2121
# additional ones here if they have them.
@@ -33,6 +33,7 @@ def update_test(ti: common.TestInfo):
3333
break
3434

3535
run_list = []
36+
mir_run_list = []
3637
for l in ti.run_lines:
3738
if "|" not in l:
3839
common.warn("Skipping unparsable RUN line: " + l)
@@ -57,9 +58,14 @@ def update_test(ti: common.TestInfo):
5758
if m:
5859
march_in_cmd = m.groups()[0]
5960

61+
target_list = run_list
6062
m = common.DEBUG_ONLY_ARG_RE.search(llc_cmd)
6163
if m and m.groups()[0] == "isel":
6264
from UpdateTestChecks import isel as output_type
65+
elif not m and common.STOP_PASS_RE.search(llc_cmd):
66+
# MIR output mode. If -debug-only is present assume
67+
# the debug output is the main point of interest.
68+
target_list = mir_run_list
6369
else:
6470
from UpdateTestChecks import asm as output_type
6571

@@ -84,7 +90,7 @@ def update_test(ti: common.TestInfo):
8490

8591
# FIXME: We should use multiple check prefixes to common check lines. For
8692
# now, we just ignore all but the last.
87-
run_list.append(
93+
target_list.append(
8894
(
8995
check_prefixes,
9096
llc_tool,
@@ -119,14 +125,18 @@ def update_test(ti: common.TestInfo):
119125
ginfo=ginfo,
120126
)
121127

122-
for (
123-
prefixes,
124-
llc_tool,
125-
llc_args,
126-
preprocess_cmd,
127-
triple_in_cmd,
128-
march_in_cmd,
129-
) in run_list:
128+
# Dictionary to store MIR function bodies separately
129+
mir_func_dict = {}
130+
for run_tuple, is_mir in [(run, False) for run in run_list] + [(run, True) for run in mir_run_list]:
131+
(
132+
prefixes,
133+
llc_tool,
134+
llc_args,
135+
preprocess_cmd,
136+
triple_in_cmd,
137+
march_in_cmd,
138+
) = run_tuple
139+
130140
common.debug("Extracted LLC cmd:", llc_tool, llc_args)
131141
common.debug("Extracted FileCheck prefixes:", str(prefixes))
132142

@@ -141,22 +151,54 @@ def update_test(ti: common.TestInfo):
141151
if not triple:
142152
triple = common.get_triple_from_march(march_in_cmd)
143153

144-
scrubber, function_re = output_type.get_run_handler(triple)
145-
if 0 == builder.process_run_line(
146-
function_re, scrubber, raw_tool_output, prefixes
147-
):
148-
common.warn(
149-
"Couldn't match any function. Possibly the wrong target triple has been provided"
154+
if is_mir:
155+
# MIR output mode
156+
common.debug("Detected MIR output mode for prefixes:", str(prefixes))
157+
for prefix in prefixes:
158+
if prefix not in mir_func_dict:
159+
mir_func_dict[prefix] = {}
160+
161+
mir.build_function_info_dictionary(
162+
ti.path,
163+
raw_tool_output,
164+
triple,
165+
prefixes,
166+
mir_func_dict,
167+
ti.args.verbose,
150168
)
151-
builder.processed_prefixes(prefixes)
169+
else:
170+
# ASM output mode
171+
scrubber, function_re = output_type.get_run_handler(triple)
172+
if 0 == builder.process_run_line(
173+
function_re, scrubber, raw_tool_output, prefixes
174+
):
175+
common.warn(
176+
"Couldn't match any function. Possibly the wrong target triple has been provided"
177+
)
178+
builder.processed_prefixes(prefixes)
152179

153180
func_dict = builder.finish_and_get_func_dict()
181+
182+
# Check for conflicts: same prefix used for both ASM and MIR
183+
conflicting_prefixes = set(func_dict.keys()) & set(mir_func_dict.keys())
184+
if conflicting_prefixes:
185+
common.warn(
186+
"The following prefixes are used for both ASM and MIR output, which will cause FileCheck failures: {}".format(
187+
", ".join(sorted(conflicting_prefixes))
188+
),
189+
test_file=ti.path,
190+
)
191+
for prefix in conflicting_prefixes:
192+
mir_func_dict[prefix] = {}
193+
func_dict[prefix] = {}
194+
154195
global_vars_seen_dict = {}
155196

156197
is_in_function = False
157198
is_in_function_start = False
158199
func_name = None
159200
prefix_set = set([prefix for p in run_list for prefix in p[0]])
201+
prefix_set.update([prefix for p in mir_run_list for prefix in p[0]])
160202
common.debug("Rewriting FileCheck prefixes:", str(prefix_set))
161203
output_lines = []
162204

@@ -221,6 +263,22 @@ def update_test(ti: common.TestInfo):
221263
is_filtered=builder.is_filtered(),
222264
)
223265
)
266+
267+
# Also add MIR checks if we have them for this function
268+
if mir_run_list and func_name:
269+
mir.add_mir_checks_for_function(
270+
ti.path,
271+
output_lines,
272+
mir_run_list,
273+
mir_func_dict,
274+
func_name,
275+
single_bb=False, # Don't skip basic block labels.
276+
print_fixed_stack=False, # Don't print fixed stack (ASM tests don't need it).
277+
first_check_is_next=False, # First check is LABEL, not NEXT.
278+
at_the_function_name=False, # Use "name:" not "@name".
279+
check_indent="", # No indentation for IR files (not MIR files).
280+
)
281+
224282
is_in_function_start = False
225283

226284
if is_in_function:

0 commit comments

Comments
 (0)