Skip to content

Commit f15c602

Browse files
committed
[UpdateTestChecks] Auto-generate stub bodies for unused prefixes
This is scoped to autogenerated tests. The goal is to support having each RUN line specify a list of check-prefixes where one can specify potentially redundant prefixes. For example, for X86, if one specified prefixes for both AVX1 and AVX2, and the codegen happened to match today, one of the prefixes would be used and the onther one not. If the unused prefix were dropped, and later, codegen differences were introduced, one would have to go figure out where to add what prefix (paraphrasing https://lists.llvm.org/pipermail/llvm-dev/2021-February/148326.html) To avoid getting errors due to unused prefixes, whole directories can be opted out (as discussed on that thread), but that means that tests that aren't autogenerated in such directories could have undetected unused prefix bugs. This patch proposes an alternative that both avoids the above, dir-level optout, and supports the main autogen scenario discussed first. The autogen tool appends at the end of the test file the list of unused prefixes, together with a note explaining that is the case. Each prefix is set up to always pass. This way, unexpected unused prefixes are easily discoverable, and expected cases "just work". Differential Revision: https://reviews.llvm.org/D124306
1 parent 6af5f56 commit f15c602

14 files changed

+169
-48
lines changed

llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/amdgpu_no_merge_comments.ll.expected

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,5 @@ define hidden i32 @main(i32 %a) {
2828
%sub = sub i32 %mul, %add
2929
ret i32 %sub
3030
}
31+
;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
32+
; GCN: {{.*}}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
; RUN: llc < %s -mtriple=i686-unknown-linux-gnu -mattr=+sse2 | FileCheck %s --check-prefixes=A,B
2+
; RUN: llc < %s -mtriple=x86_64-unknown-linux-gnu | FileCheck %s --allow-unused-prefixes=true --check-prefixes=C,A,UNUSED
3+
; RUN: llc < %s -mtriple=x86_64-unknown-linux-gnu | FileCheck %s --allow-unused-prefixes=true --check-prefixe=A
4+
5+
declare <2 x i64> @llvm.bswap.v2i64(<2 x i64>)
6+
7+
define <2 x i64> @fold_v2i64() {
8+
; B-LABEL: fold_v2i64:
9+
; B: # %bb.0: # %entry
10+
; B-NEXT: movaps {{.*#+}} xmm0 = [0,4278190080,4294967295,4294967295]
11+
; B-NEXT: retl
12+
;
13+
; C-LABEL: fold_v2i64:
14+
; C: # %bb.0: # %entry
15+
; C-NEXT: movaps {{.*#+}} xmm0 = [18374686479671623680,18446744073709551615]
16+
; C-NEXT: retq
17+
entry:
18+
%r = call <2 x i64> @llvm.bswap.v2i64(<2 x i64> <i64 255, i64 -1>)
19+
ret <2 x i64> %r
20+
}
21+
22+
declare <4 x i32> @llvm.bswap.v4i32(<4 x i32>)
23+
24+
define <4 x i32> @test2(<4 x i32> %v) {
25+
%r = call <4 x i32> @llvm.bswap.v4i32(<4 x i32> %v)
26+
ret <4 x i32> %r
27+
}
28+
;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
29+
; A: {{.*}}
30+
; UNUSED: {{.*}}

llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/common-label-different-bodies-1.ll

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
; RUN: llc < %s -mtriple=x86_64-unknown-linux-gnu | FileCheck %s --allow-unused-prefixes=true --check-prefixes=C,A,UNUSED
33

44
declare <2 x i64> @llvm.bswap.v2i64(<2 x i64>)
5-
; A: declare <2 x i64> @llvm.bswap.v2i64(<2 x i64>)
65

76
define <2 x i64> @fold_v2i64() {
87
entry:

llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/common-label-different-bodies-2.ll

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
; RUN: llc < %s -mtriple=x86_64-unknown-linux-gnu | FileCheck %s --check-prefixes=C,A
33

44
declare <2 x i64> @llvm.bswap.v2i64(<2 x i64>)
5-
; A: declare <2 x i64> @llvm.bswap.v2i64(<2 x i64>)
65

76
define <2 x i64> @fold_v2i64() {
87
entry:

llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/common-label-different-bodies-3.ll

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
; RUN: llc < %s -mtriple=x86_64-unknown-linux-gnu | FileCheck %s --check-prefixes=A,C
33

44
declare <2 x i64> @llvm.bswap.v2i64(<2 x i64>)
5-
; A: declare <2 x i64> @llvm.bswap.v2i64(<2 x i64>)
65

76
define <2 x i64> @fold_v2i64() {
87
entry:
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
; RUN: llc < %s -mtriple=i686-unknown-linux-gnu -mattr=+sse2 | FileCheck %s --check-prefixes=A,B,REDUNDANT
2+
; RUN: llc < %s -mtriple=x86_64-unknown-linux-gnu | FileCheck %s --check-prefixes=C,A,UNUSED
3+
4+
; prefix 'A' has conflicting outputs, while the REDUNDANT and UNUSED ones are
5+
; unused
6+
declare <2 x i64> @llvm.bswap.v2i64(<2 x i64>)
7+
8+
define <2 x i64> @function_1() {
9+
entry:
10+
%r = call <2 x i64> @llvm.bswap.v2i64(<2 x i64> <i64 255, i64 -1>)
11+
ret <2 x i64> %r
12+
}
13+
14+
define <2 x i64> @function_2() {
15+
entry:
16+
%r = call <2 x i64> @llvm.bswap.v2i64(<2 x i64> <i64 16, i64 -1>)
17+
ret <2 x i64> %r
18+
}

llvm/test/tools/UpdateTestChecks/update_llc_test_checks/common-label-different-bodies.test

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,29 @@
44
# RUN: cp -f %S/Inputs/common-label-different-bodies-2.ll %t-2.ll
55
# RUN: cp -f %S/Inputs/common-label-different-bodies-3.ll %t-3.ll
66
# RUN: %update_llc_test_checks %t-1.ll
7+
8+
# re-running update_llc_test_checks leaves the file as-is.
9+
# RUN: cp -f %t-1.ll %t-1-copy.ll
10+
# RUN: %update_llc_test_checks %t-1.ll
11+
# RUN: diff %t-1.ll %t-1-copy.ll
712
# RUN: %update_llc_test_checks %t-2.ll
813
# RUN: %update_llc_test_checks %t-3.ll
9-
# RUN: FileCheck --input-file=%t-1.ll %s
14+
# RUN: FileCheck --input-file=%t-1.ll %s --check-prefixes=CHECK,CHECK-UNUSED
1015
# RUN: FileCheck --input-file=%t-2.ll %s
1116
# RUN: FileCheck --input-file=%t-3.ll %s
1217

1318
# CHECK: B-LABEL: fold_v2i64
14-
# CHECK-NOT: A-LABEL: fold_v2i64
19+
# CHECK-NOT: A-LABEL: fold_v2i64
20+
# CHECK: NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
21+
# CHECK-NEXT: A:
22+
# CHECK-UNUSED-NEXT: UNUSED:
23+
24+
# adding a test removes that label from "unused" prefixes list
25+
# the input file is like "1" after the tool was run, and then we added a new test
26+
# RUN: cp -f %S/Inputs/common-label-different-bodies-1-next.ll %t-1-next.ll
27+
# RUN: %update_llc_test_checks %t-1-next.ll
28+
# RUN: FileCheck --input-file=%t-1-next.ll %s --check-prefixes=AFTER-CHANGE
29+
30+
# AFTER-CHANGE: NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
31+
# AFTER-CHANGE-NEXT: UNUSED:
32+
# AFTER-CHANGE-NOT: A:
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# REQUIRES: x86-registered-target
22

33
# RUN: cp -f %S/Inputs/prefix-never-matches.ll %t.ll
4-
# RUN: %update_llc_test_checks %t.ll 2>&1 | FileCheck %s
4+
# RUN: %update_llc_test_checks --no-generate-body-for-unused-prefixes %t.ll 2>&1 | FileCheck %s
55
# RUN: FileCheck --input-file=%t.ll %s --check-prefix=OUTPUT
66

77
# CHECK: WARNING: Prefix A had conflicting output
8-
# OUTPUT-NOT: A:
8+
# OUTPUT-NOT: A:
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# REQUIRES: x86-registered-target
2+
3+
# RUN: cp -f %S/Inputs/redundant-and-unmatching-prefixes.ll %t-1.ll
4+
# RUN: %update_llc_test_checks %t-1.ll
5+
# RUN: FileCheck --input-file=%t-1.ll %s
6+
7+
# CHECK: B-LABEL: function_1:
8+
# CHECK-NOT: A-LABEL: function_1
9+
# CHECK-NOT: REDUNDANT-LABEL: function_1:
10+
11+
# CHECK: B-LABEL: function_2:
12+
# CHECK-NOT: A-LABEL: function_2:
13+
# CHECK-NOT: UNUSED-LABEL: function_2:
14+
15+
# CHECK: NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
16+
# CHECK-NEXT: A:
17+
# CHECK-NEXT: REDUNDANT:
18+
# CHECK-NEXT: UNUSED:

llvm/utils/UpdateTestChecks/asm.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,6 @@ def add_checks(output_lines, comment_marker, prefix_list, func_dict,
484484
func_name, global_vars_seen_dict, is_filtered):
485485
# Label format is based on ASM string.
486486
check_label_format = '{} %s-LABEL: %s%s%s'.format(comment_marker)
487-
common.add_checks(output_lines, comment_marker, prefix_list, func_dict,
488-
func_name, check_label_format, True, False,
489-
global_vars_seen_dict, is_filtered=is_filtered)
487+
return common.add_checks(output_lines, comment_marker, prefix_list, func_dict,
488+
func_name, check_label_format, True, False,
489+
global_vars_seen_dict, is_filtered=is_filtered)

llvm/utils/UpdateTestChecks/common.py

Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
import sys
1111
import shlex
1212

13+
from typing import List
14+
1315
##### Common utilities for update_*test_checks.py
1416

1517

@@ -129,6 +131,11 @@ def __call__(self, parser, namespace, values, option_string=None):
129131
help='List of regular expressions that a global value declaration must match to generate a check (has no effect if checking globals is not enabled)')
130132
parser.add_argument('--global-hex-value-regex', nargs='+', default=[],
131133
help='List of regular expressions such that, for matching global value declarations, literal integer values should be encoded in hex in the associated FileCheck directives')
134+
parser.add_argument('--generate-body-for-unused-prefixes',
135+
action=argparse.BooleanOptionalAction,
136+
dest='gen_unused_prefix_body',
137+
default=True,
138+
help='Generate a function body that always matches for unused prefixes. This is useful when unused prefixes are desired, and it avoids needing to annotate each FileCheck as allowing them.')
132139
args = parser.parse_args()
133140
global _verbose, _global_value_regex, _global_hex_value_regex
134141
_verbose = args.verbose
@@ -167,6 +174,7 @@ def __init__(self, test, parser, script_name, input_lines, args, argv,
167174
self.autogenerated_note_prefix = self.comment_prefix + ' ' + UTC_ADVERT
168175
self.test_autogenerated_note = self.autogenerated_note_prefix + script_name
169176
self.test_autogenerated_note += get_autogennote_suffix(parser, self.args)
177+
self.test_unused_note = self.comment_prefix + self.comment_prefix + ' ' + UNUSED_NOTE
170178

171179
def ro_iterlines(self):
172180
for line_num, input_line in enumerate(self.input_lines):
@@ -188,6 +196,22 @@ def iterlines(self, output_lines):
188196
continue
189197
yield line_info
190198

199+
def get_checks_for_unused_prefixes(self, run_list, used_prefixes: List[str]) -> List[str]:
200+
unused_prefixes = set(
201+
[prefix for sublist in run_list for prefix in sublist[0]]).difference(set(used_prefixes))
202+
203+
ret = []
204+
if not unused_prefixes:
205+
return ret
206+
ret.append(self.test_unused_note)
207+
for unused in sorted(unused_prefixes):
208+
ret.append('{comment} {prefix}: {match_everything}'.format(
209+
comment=self.comment_prefix,
210+
prefix=unused,
211+
match_everything=r"""{{.*}}"""
212+
))
213+
return ret
214+
191215
def itertests(test_patterns, parser, script_name, comment_prefix=None, argparse_callback=None):
192216
for pattern in test_patterns:
193217
# On Windows we must expand the patterns ourselves.
@@ -212,7 +236,12 @@ def itertests(test_patterns, parser, script_name, comment_prefix=None, argparse_
212236
assert UTC_ADVERT not in first_line
213237
warn("Skipping test which isn't autogenerated: " + test)
214238
continue
215-
yield TestInfo(test, parser, script_name, input_lines, args, argv,
239+
final_input_lines = []
240+
for l in input_lines:
241+
if UNUSED_NOTE in l:
242+
break
243+
final_input_lines.append(l)
244+
yield TestInfo(test, parser, script_name, final_input_lines, args, argv,
216245
comment_prefix, argparse_callback)
217246

218247

@@ -293,6 +322,7 @@ def invoke_tool(exe, cmd_args, ir, preprocess_cmd=None, verbose=False):
293322
UTC_ARGS_KEY = 'UTC_ARGS:'
294323
UTC_ARGS_CMD = re.compile(r'.*' + UTC_ARGS_KEY + '\s*(?P<cmd>.*)\s*$')
295324
UTC_ADVERT = 'NOTE: Assertions have been autogenerated by '
325+
UNUSED_NOTE = 'NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:'
296326

297327
OPT_FUNCTION_RE = re.compile(
298328
r'^(\s*;\s*Function\sAttrs:\s(?P<attrs>[\w\s]+?))?\s*define\s+(?:internal\s+)?[^@]*@(?P<func>[\w.$-]+?)\s*'
@@ -471,7 +501,7 @@ def __init__(self, run_list, flags, scrubber_args, path):
471501
self._global_var_dict.update({prefix:dict()})
472502

473503
def finish_and_get_func_dict(self):
474-
for prefix in self._get_failed_prefixes():
504+
for prefix in self.get_failed_prefixes():
475505
warn('Prefix %s had conflicting output from different RUN lines for all functions in test %s' % (prefix,self._path,))
476506
return self._func_dict
477507

@@ -577,11 +607,10 @@ def process_run_line(self, function_re, scrubber, raw_tool_output, prefixes, is_
577607
scrubbed_body, scrubbed_extra, args_and_sig, attrs, func_name_separator)
578608
self._func_order[prefix].append(func)
579609

580-
def _get_failed_prefixes(self):
610+
def get_failed_prefixes(self):
581611
# This returns the list of those prefixes that failed to match any function,
582612
# because there were conflicting bodies produced by different RUN lines, in
583-
# all instances of the prefix. Effectively, this prefix is unused and should
584-
# be removed.
613+
# all instances of the prefix.
585614
for prefix in self._func_dict:
586615
if (self._func_dict[prefix] and
587616
(not [fct for fct in self._func_dict[prefix]
@@ -974,23 +1003,24 @@ def add_checks(output_lines, comment_marker, prefix_list, func_dict, func_name,
9741003
if key not in global_vars_seen_before:
9751004
global_vars_seen_dict[checkprefix][key] = global_vars_seen[key]
9761005
break
1006+
return printed_prefixes
9771007

9781008
def add_ir_checks(output_lines, comment_marker, prefix_list, func_dict,
9791009
func_name, preserve_names, function_sig,
9801010
global_vars_seen_dict, is_filtered):
9811011
# Label format is based on IR string.
9821012
function_def_regex = 'define {{[^@]+}}' if function_sig else ''
9831013
check_label_format = '{} %s-LABEL: {}@%s%s%s'.format(comment_marker, function_def_regex)
984-
add_checks(output_lines, comment_marker, prefix_list, func_dict, func_name,
985-
check_label_format, False, preserve_names, global_vars_seen_dict,
986-
is_filtered)
1014+
return add_checks(output_lines, comment_marker, prefix_list, func_dict, func_name,
1015+
check_label_format, False, preserve_names, global_vars_seen_dict,
1016+
is_filtered)
9871017

9881018
def add_analyze_checks(output_lines, comment_marker, prefix_list, func_dict, func_name, is_filtered):
9891019
check_label_format = '{} %s-LABEL: \'%s%s%s\''.format(comment_marker)
9901020
global_vars_seen_dict = {}
991-
add_checks(output_lines, comment_marker, prefix_list, func_dict, func_name,
992-
check_label_format, False, True, global_vars_seen_dict,
993-
is_filtered)
1021+
return add_checks(output_lines, comment_marker, prefix_list, func_dict, func_name,
1022+
check_label_format, False, True, global_vars_seen_dict,
1023+
is_filtered)
9941024

9951025
def build_global_values_dictionary(glob_val_dict, raw_tool_output, prefixes):
9961026
for nameless_value in itertools.chain(ir_nameless_values, asm_nameless_values):
@@ -1189,6 +1219,7 @@ def dump_input_lines(output_lines, test_info, prefix_set, comment_string):
11891219
def add_checks_at_end(output_lines, prefix_list, func_order,
11901220
comment_string, check_generator):
11911221
added = set()
1222+
generated_prefixes = []
11921223
for prefix in prefix_list:
11931224
prefixes = prefix[0]
11941225
tool_args = prefix[1]
@@ -1212,6 +1243,7 @@ def add_checks_at_end(output_lines, prefix_list, func_order,
12121243
# single prefix before moving on to the next prefix. So checks
12131244
# are ordered by prefix instead of by function as in "normal"
12141245
# mode.
1215-
check_generator(output_lines,
1246+
generated_prefixes.extend(check_generator(output_lines,
12161247
[([prefix], tool_args)],
1217-
func)
1248+
func))
1249+
return generated_prefixes

llvm/utils/UpdateTestChecks/isel.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,6 @@ def add_checks(output_lines, comment_marker, prefix_list, func_dict, func_name,
5252
global_vars_seen_dict, is_filtered):
5353
# Label format is based on iSel string.
5454
check_label_format = '{} %s-LABEL: %s%s%s'.format(comment_marker)
55-
common.add_checks(output_lines, comment_marker, prefix_list, func_dict,
56-
func_name, check_label_format, True, False,
57-
global_vars_seen_dict, is_filtered = is_filtered)
55+
return common.add_checks(output_lines, comment_marker, prefix_list, func_dict,
56+
func_name, check_label_format, True, False,
57+
global_vars_seen_dict, is_filtered=is_filtered)

llvm/utils/update_cc_test_checks.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -340,17 +340,17 @@ def main():
340340
# Now generate all the checks.
341341
def check_generator(my_output_lines, prefixes, func):
342342
if '-emit-llvm' in clang_args:
343-
common.add_ir_checks(my_output_lines, '//',
344-
prefixes,
345-
func_dict, func, False,
346-
ti.args.function_signature,
347-
global_vars_seen_dict,
348-
is_filtered=builder.is_filtered())
343+
return common.add_ir_checks(my_output_lines, '//',
344+
prefixes,
345+
func_dict, func, False,
346+
ti.args.function_signature,
347+
global_vars_seen_dict,
348+
is_filtered=builder.is_filtered())
349349
else:
350-
asm.add_checks(my_output_lines, '//',
351-
prefixes,
352-
func_dict, func, global_vars_seen_dict,
353-
is_filtered=builder.is_filtered())
350+
return asm.add_checks(my_output_lines, '//',
351+
prefixes,
352+
func_dict, func, global_vars_seen_dict,
353+
is_filtered=builder.is_filtered())
354354

355355
if ti.args.check_globals:
356356
common.add_global_checks(builder.global_var_dict(), '//', run_list,

llvm/utils/update_llc_test_checks.py

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ def main():
153153
'--include-generated-funcs',
154154
True)
155155

156+
generated_prefixes = []
156157
if include_generated_funcs:
157158
# Generate the appropriate checks for each function. We need to emit
158159
# these in the order according to the generated output so that CHECK-LABEL
@@ -164,14 +165,15 @@ def main():
164165
common.dump_input_lines(output_lines, ti, prefix_set, ';')
165166

166167
# Now generate all the checks.
167-
common.add_checks_at_end(output_lines, run_list, builder.func_order(),
168-
check_indent + ';',
169-
lambda my_output_lines, prefixes, func:
170-
output_type.add_checks(my_output_lines,
171-
check_indent + ';',
172-
prefixes, func_dict, func,
173-
global_vars_seen_dict,
174-
is_filtered=builder.is_filtered()))
168+
generated_prefixes = common.add_checks_at_end(
169+
output_lines, run_list, builder.func_order(),
170+
check_indent + ';',
171+
lambda my_output_lines, prefixes, func:
172+
output_type.add_checks(my_output_lines,
173+
check_indent + ';',
174+
prefixes, func_dict, func,
175+
global_vars_seen_dict,
176+
is_filtered=builder.is_filtered()))
175177
else:
176178
for input_info in ti.iterlines(output_lines):
177179
input_line = input_info.line
@@ -186,9 +188,10 @@ def main():
186188
continue
187189

188190
# Print out the various check lines here.
189-
output_type.add_checks(output_lines, check_indent + ';', run_list,
190-
func_dict, func_name, global_vars_seen_dict,
191-
is_filtered=builder.is_filtered())
191+
generated_prefixes.extend(
192+
output_type.add_checks(output_lines, check_indent + ';', run_list,
193+
func_dict, func_name, global_vars_seen_dict,
194+
is_filtered=builder.is_filtered()))
192195
is_in_function_start = False
193196

194197
if is_in_function:
@@ -213,8 +216,11 @@ def main():
213216
continue
214217
is_in_function = is_in_function_start = True
215218

219+
if ti.args.gen_unused_prefix_body:
220+
output_lines.extend(ti.get_checks_for_unused_prefixes(
221+
run_list, generated_prefixes))
222+
216223
common.debug('Writing %d lines to %s...' % (len(output_lines), ti.path))
217-
218224
with open(ti.path, 'wb') as f:
219225
f.writelines(['{}\n'.format(l).encode('utf-8') for l in output_lines])
220226

0 commit comments

Comments
 (0)