update_test_checks: match IR basic block labels (#88979)

Labels are matched using a regexp of the form '^(pattern):', which
requires the addition of a "suffix" concept to NamelessValue.

Aside from that, the key challenge is that block labels are values, and
we typically capture values including the prefix '%'. However, when
labels appear at the start of a basic block, the prefix '%' is not
included, so we must capture block label values *without* the prefix
'%'.

We don't know ahead of time whether an IR value is a label or not. In
most cases, they are prefixed by the word "label" (their type), but this
isn't the case in phi nodes. We solve this issue by leveraging the
two-phase nature of variable generalization: the first pass finds all
occurences of a variable and determines whether the '%' prefix can be
included or not. The second pass does the actual substitution.

This change also unifies the generalization path for assembly with that
for IR and analysis, in the hope that any future changes avoid diverging
those cases future.

I also considered the alternative of trying to detect the phi node case
using more regular expression special cases but ultimately decided
against that because it seemed more fragile, and perhaps the approach of
keeping a tentative prefix that may later be discarded could also be
eventually applied to some metadata and attribute cases.

Note that an early version of this change was reviewed as
https://reviews.llvm.org/D142452, before version numbers were
introduced. This is a substantially updated version of that change.
This commit is contained in:
Nicolai Hähnle
2024-05-19 01:39:47 +02:00
committed by GitHub
parent c3677e4522
commit 597ac471cc
9 changed files with 538 additions and 520 deletions

View File

@@ -1,15 +1,15 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
; RUN: opt < %s -S | FileCheck %s
define i32 @phi_after_label(i1 %cc) {
; CHECK-LABEL: define i32 @phi_after_label(
; CHECK-SAME: i1 [[CC:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: br i1 [[CC]], label [[THEN:%.*]], label [[END:%.*]]
; CHECK: then:
; CHECK-NEXT: br label [[END]]
; CHECK: end:
; CHECK-NEXT: [[R:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ 1, [[THEN]] ]
; CHECK-NEXT: [[ENTRY:.*]]:
; CHECK-NEXT: br i1 [[CC]], label %[[THEN:.*]], label %[[END:.*]]
; CHECK: [[THEN]]:
; CHECK-NEXT: br label %[[END]]
; CHECK: [[END]]:
; CHECK-NEXT: [[R:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ 1, %[[THEN]] ]
; CHECK-NEXT: ret i32 [[R]]
;
entry:
@@ -26,14 +26,14 @@ end:
define void @phi_before_label(i32 %bound) {
; CHECK-LABEL: define void @phi_before_label(
; CHECK-SAME: i32 [[BOUND:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
; CHECK-NEXT: [[CTR:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[CTR_NEXT:%.*]], [[LOOP]] ]
; CHECK-NEXT: [[ENTRY:.*]]:
; CHECK-NEXT: br label %[[LOOP:.*]]
; CHECK: [[LOOP]]:
; CHECK-NEXT: [[CTR:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[CTR_NEXT:%.*]], %[[LOOP]] ]
; CHECK-NEXT: [[CTR_NEXT]] = add i32 [[CTR]], 1
; CHECK-NEXT: [[CC:%.*]] = icmp ult i32 [[CTR_NEXT]], [[BOUND]]
; CHECK-NEXT: br i1 [[CC]], label [[LOOP]], label [[END:%.*]]
; CHECK: end:
; CHECK-NEXT: br i1 [[CC]], label %[[LOOP]], label %[[END:.*]]
; CHECK: [[END]]:
; CHECK-NEXT: ret void
;
entry:
@@ -52,11 +52,11 @@ end:
define i32 @phi_after_label_unnamed(i1 %cc) {
; CHECK-LABEL: define i32 @phi_after_label_unnamed(
; CHECK-SAME: i1 [[CC:%.*]]) {
; CHECK-NEXT: br i1 [[CC]], label [[TMP1:%.*]], label [[TMP2:%.*]]
; CHECK: 1:
; CHECK-NEXT: br label [[TMP2]]
; CHECK: 2:
; CHECK-NEXT: [[R:%.*]] = phi i32 [ 0, [[TMP0:%.*]] ], [ 1, [[TMP1]] ]
; CHECK-NEXT: br i1 [[CC]], label %[[BB1:.*]], label %[[BB2:.*]]
; CHECK: [[BB1]]:
; CHECK-NEXT: br label %[[BB2]]
; CHECK: [[BB2]]:
; CHECK-NEXT: [[R:%.*]] = phi i32 [ 0, [[TMP0:%.*]] ], [ 1, %[[BB1]] ]
; CHECK-NEXT: ret i32 [[R]]
;
0:

View File

@@ -1,4 +1,4 @@
# RUN: cp -f %S/Inputs/phi-labels.ll %t.ll && %update_test_checks --version 4 %t.ll
# RUN: cp -f %S/Inputs/phi-labels.ll %t.ll && %update_test_checks --version 5 %t.ll
# RUN: diff -u %t.ll %S/Inputs/phi-labels.ll.expected
## Check that running the script again does not change the result:
# RUN: %update_test_checks %t.ll

View File

@@ -605,6 +605,7 @@ def add_checks(
prefix_list,
func_dict,
func_name,
ginfo: common.GeneralizerInfo,
global_vars_seen_dict,
is_filtered,
):
@@ -617,9 +618,7 @@ def add_checks(
func_dict,
func_name,
check_label_format,
True,
False,
1,
ginfo,
global_vars_seen_dict,
is_filtered=is_filtered,
)

File diff suppressed because it is too large Load Diff

View File

@@ -60,6 +60,7 @@ def add_checks(
prefix_list,
func_dict,
func_name,
ginfo: common.GeneralizerInfo,
global_vars_seen_dict,
is_filtered,
):
@@ -72,9 +73,7 @@ def add_checks(
func_dict,
func_name,
check_label_format,
True,
False,
1,
ginfo,
global_vars_seen_dict,
is_filtered=is_filtered,
)

View File

@@ -96,6 +96,7 @@ def main():
# now, we just ignore all but the last.
prefix_list.append((check_prefixes, tool_cmd_args))
ginfo = common.make_analyze_generalizer(version=1)
builder = common.FunctionTestBuilder(
run_list=prefix_list,
flags=type(
@@ -111,6 +112,7 @@ def main():
),
scrubber_args=[],
path=ti.path,
ginfo=ginfo,
)
for prefixes, opt_args in prefix_list:
@@ -131,7 +133,6 @@ def main():
common.scrub_body,
raw_tool_output,
prefixes,
False,
)
elif re.search(r"LV: Checking a loop in ", raw_tool_outputs) is not None:
# Split analysis outputs by "Printing analysis " declarations.
@@ -143,7 +144,6 @@ def main():
common.scrub_body,
raw_tool_output,
prefixes,
False,
)
else:
common.warn("Don't know how to deal with this output")
@@ -179,6 +179,7 @@ def main():
prefix_list,
func_dict,
func_name,
ginfo,
is_filtered=builder.is_filtered(),
)
)

View File

@@ -270,7 +270,7 @@ def get_function_body(builder, args, filename, clang_args, extra_commands, prefi
raw_tool_output = common.invoke_tool(extra_args[0], extra_args[1:], f.name)
if "-emit-llvm" in clang_args:
builder.process_run_line(
common.OPT_FUNCTION_RE, common.scrub_body, raw_tool_output, prefixes, False
common.OPT_FUNCTION_RE, common.scrub_body, raw_tool_output, prefixes
)
builder.processed_prefixes(prefixes)
else:
@@ -360,8 +360,13 @@ def main():
# Store only filechecked runlines.
filecheck_run_list = [i for i in run_list if i[0]]
ginfo = common.make_ir_generalizer(version=ti.args.version)
builder = common.FunctionTestBuilder(
run_list=filecheck_run_list, flags=ti.args, scrubber_args=[], path=ti.path
run_list=filecheck_run_list,
flags=ti.args,
scrubber_args=[],
path=ti.path,
ginfo=ginfo,
)
for prefixes, args, extra_commands, triple_in_cmd in run_list:
@@ -415,29 +420,18 @@ def main():
# Now generate all the checks.
def check_generator(my_output_lines, prefixes, func):
if "-emit-llvm" in clang_args:
return common.add_ir_checks(
my_output_lines,
"//",
prefixes,
func_dict,
func,
False,
ti.args.function_signature,
ti.args.version,
global_vars_seen_dict,
is_filtered=builder.is_filtered(),
)
else:
return asm.add_checks(
my_output_lines,
"//",
prefixes,
func_dict,
func,
global_vars_seen_dict,
is_filtered=builder.is_filtered(),
)
return common.add_ir_checks(
my_output_lines,
"//",
prefixes,
func_dict,
func,
False,
ti.args.function_signature,
ginfo,
global_vars_seen_dict,
is_filtered=builder.is_filtered(),
)
if ti.args.check_globals != 'none':
generated_prefixes.extend(
@@ -446,6 +440,7 @@ def main():
"//",
run_list,
output_lines,
ginfo,
global_vars_seen_dict,
False,
True,
@@ -506,6 +501,7 @@ def main():
"//",
run_list,
output_lines,
ginfo,
global_vars_seen_dict,
False,
True,
@@ -525,7 +521,7 @@ def main():
mangled,
False,
args.function_signature,
args.version,
ginfo,
global_vars_seen_dict,
is_filtered=builder.is_filtered(),
)
@@ -543,6 +539,7 @@ def main():
"//",
run_list,
output_lines,
ginfo,
global_vars_seen_dict,
False,
False,

View File

@@ -133,6 +133,7 @@ def main():
else:
check_indent = ""
ginfo = common.make_asm_generalizer(version=1)
builder = common.FunctionTestBuilder(
run_list=run_list,
flags=type(
@@ -148,6 +149,7 @@ def main():
),
scrubber_args=[ti.args],
path=ti.path,
ginfo=ginfo,
)
for (
@@ -173,9 +175,7 @@ def main():
triple = common.get_triple_from_march(march_in_cmd)
scrubber, function_re = output_type.get_run_handler(triple)
builder.process_run_line(
function_re, scrubber, raw_tool_output, prefixes, True
)
builder.process_run_line(function_re, scrubber, raw_tool_output, prefixes)
builder.processed_prefixes(prefixes)
func_dict = builder.finish_and_get_func_dict()
@@ -218,6 +218,7 @@ def main():
prefixes,
func_dict,
func,
ginfo,
global_vars_seen_dict,
is_filtered=builder.is_filtered(),
),
@@ -243,6 +244,7 @@ def main():
run_list,
func_dict,
func_name,
ginfo,
global_vars_seen_dict,
is_filtered=builder.is_filtered(),
)

View File

@@ -147,9 +147,14 @@ def main():
# now, we just ignore all but the last.
prefix_list.append((check_prefixes, tool_cmd_args, preprocess_cmd))
ginfo = common.make_ir_generalizer(ti.args.version)
global_vars_seen_dict = {}
builder = common.FunctionTestBuilder(
run_list=prefix_list, flags=ti.args, scrubber_args=[], path=ti.path
run_list=prefix_list,
flags=ti.args,
scrubber_args=[],
path=ti.path,
ginfo=ginfo,
)
tool_binary = ti.args.tool_binary
@@ -172,7 +177,6 @@ def main():
common.scrub_body,
raw_tool_output,
prefixes,
False,
)
builder.processed_prefixes(prefixes)
@@ -217,6 +221,7 @@ def main():
";",
prefix_list,
output_lines,
ginfo,
global_vars_seen_dict,
args.preserve_names,
True,
@@ -239,7 +244,7 @@ def main():
func,
False,
args.function_signature,
args.version,
ginfo,
global_vars_seen_dict,
is_filtered=builder.is_filtered(),
original_check_lines=original_check_lines.get(func, {}),
@@ -271,7 +276,7 @@ def main():
func_name,
args.preserve_names,
args.function_signature,
args.version,
ginfo,
global_vars_seen_dict,
is_filtered=builder.is_filtered(),
original_check_lines=original_check_lines.get(
@@ -290,6 +295,7 @@ def main():
";",
prefix_list,
output_lines,
ginfo,
global_vars_seen_dict,
args.preserve_names,
True,
@@ -337,6 +343,7 @@ def main():
";",
prefix_list,
output_lines,
ginfo,
global_vars_seen_dict,
args.preserve_names,
False,