-
Notifications
You must be signed in to change notification settings - Fork 13.5k
[GlobalISel] Add a update_givaluetracking_test_checks.py script #140296
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
As with the other update scripts this takes the output of -passes=print<gisel-value-tracking> and inserts the results into an existing mir file. This means that the input is a lot like update_analysis_test_checks.py, and the output needs to insert into a mir file similarly to update_mir_test_checks.py. The code used to do the inserting has been moved to common, to allow it to be reused.
@llvm/pr-subscribers-llvm-globalisel @llvm/pr-subscribers-backend-aarch64 Author: David Green (davemgreen) ChangesAs with the other update scripts this takes the output of -passes=print<gisel-value-tracking> and inserts the results into an existing mir file. This means that the input is a lot like update_analysis_test_checks.py, and the output needs to insert into a mir file similarly to update_mir_test_checks.py. The code used to do the inserting has been moved to common, to allow it to be reused. Otherwise it tries to reuse the existing infrastructure, and update_givaluetracking_test_checks is kept relatively short. Patch is 26.19 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/140296.diff 4 Files Affected:
diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/knownbits-const.mir b/llvm/test/CodeGen/AArch64/GlobalISel/knownbits-const.mir
index 5d88bb08ebe72..00af98cc504c3 100644
--- a/llvm/test/CodeGen/AArch64/GlobalISel/knownbits-const.mir
+++ b/llvm/test/CodeGen/AArch64/GlobalISel/knownbits-const.mir
@@ -1,27 +1,27 @@
-# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
-# RUN: llc -mtriple aarch64 -passes="print<gisel-value-tracking>" %s -o - 2>&1 | FileCheck %s
+# NOTE: Assertions have been autogenerated by utils/update_givaluetracking_test_checks.py UTC_ARGS: --version 5
+# RUN: llc -mtriple aarch64 -passes="print<gisel-value-tracking>" %s -filetype=null 2>&1 | FileCheck %s
---
name: Cst
tracksRegLiveness: true
body: |
bb.1:
- ; CHECK-LABEL: name: @Cst
- ; CHECK-NEXT: %0:_ KnownBits:00000001 SignBits:7
- ; CHECK-NEXT: %1:_ KnownBits:00000001 SignBits:7
+ ; CHECK-LABEL: name: @Cst
+ ; CHECK-NEXT: %0:_ KnownBits:00000001 SignBits:7
+ ; CHECK-NEXT: %1:_ KnownBits:00000001 SignBits:7
%0:_(s8) = G_CONSTANT i8 1
%1:_(s8) = COPY %0
...
---
name: CstWithClass
-# We can't analyze %0 due to the lack of an LLT. We will get a default
-# constructed KnownBits back. %0 will have the correct size but we will
-# not know any further info.
tracksRegLiveness: true
body: |
bb.1:
- ; CHECK-LABEL: name: @CstWithClass
- ; CHECK-NEXT: %1:_ KnownBits:???????????????????????????????? SignBits:1
+ ; We can't analyze %0 due to the lack of an LLT. We will get a default
+ ; constructed KnownBits back. %0 will have the correct size but we will
+ ; not know any further info.
+ ; CHECK-LABEL: name: @CstWithClass
+ ; CHECK-NEXT: %1:_ KnownBits:???????????????????????????????? SignBits:1
%0:gpr32 = MOVi32imm 1
%1:_(s32) = COPY %0
...
diff --git a/llvm/utils/UpdateTestChecks/common.py b/llvm/utils/UpdateTestChecks/common.py
index d150612dd368e..35c80faf95013 100644
--- a/llvm/utils/UpdateTestChecks/common.py
+++ b/llvm/utils/UpdateTestChecks/common.py
@@ -2270,6 +2270,244 @@ def add_analyze_checks(
)
+IR_FUNC_NAME_RE = re.compile(
+ r"^\s*define\s+(?:internal\s+)?[^@]*@(?P<func>[A-Za-z0-9_.]+)\s*\("
+)
+IR_PREFIX_DATA_RE = re.compile(r"^ *(;|$)")
+MIR_FUNC_NAME_RE = re.compile(r" *name: *(?P<func>[A-Za-z0-9_.-]+)")
+MIR_BODY_BEGIN_RE = re.compile(r" *body: *\|")
+MIR_BASIC_BLOCK_RE = re.compile(r" *bb\.[0-9]+.*:$")
+MIR_PREFIX_DATA_RE = re.compile(r"^ *(;|bb.[0-9].*: *$|[a-z]+:( |$)|$)")
+
+
+def find_mir_functions_with_one_bb(lines, verbose=False):
+ result = []
+ cur_func = None
+ bbs = 0
+ for line in lines:
+ m = MIR_FUNC_NAME_RE.match(line)
+ if m:
+ if bbs == 1:
+ result.append(cur_func)
+ cur_func = m.group("func")
+ bbs = 0
+ m = MIR_BASIC_BLOCK_RE.match(line)
+ if m:
+ bbs += 1
+ if bbs == 1:
+ result.append(cur_func)
+ return result
+
+
+def add_mir_checks_for_function(
+ test,
+ output_lines,
+ run_list,
+ func_dict,
+ func_name,
+ single_bb,
+ print_fixed_stack,
+ first_check_is_next,
+ at_the_function_name,
+):
+ printed_prefixes = set()
+ for run in run_list:
+ for prefix in run[0]:
+ if prefix in printed_prefixes:
+ break
+ if not func_dict[prefix][func_name]:
+ continue
+ if printed_prefixes:
+ # Add some space between different check prefixes.
+ indent = len(output_lines[-1]) - len(output_lines[-1].lstrip(" "))
+ output_lines.append(" " * indent + ";")
+ printed_prefixes.add(prefix)
+ add_mir_check_lines(
+ test,
+ output_lines,
+ prefix,
+ ("@" if at_the_function_name else "") + func_name,
+ single_bb,
+ func_dict[prefix][func_name],
+ print_fixed_stack,
+ first_check_is_next,
+ )
+ break
+ else:
+ warn(
+ "Found conflicting asm for function: {}".format(func_name),
+ test_file=test,
+ )
+ return output_lines
+
+
+def add_mir_check_lines(
+ test,
+ output_lines,
+ prefix,
+ func_name,
+ single_bb,
+ func_info,
+ print_fixed_stack,
+ first_check_is_next,
+):
+ func_body = str(func_info).splitlines()
+ if single_bb:
+ # Don't bother checking the basic block label for a single BB
+ func_body.pop(0)
+
+ if not func_body:
+ warn(
+ "Function has no instructions to check: {}".format(func_name),
+ test_file=test,
+ )
+ return
+
+ first_line = func_body[0]
+ indent = len(first_line) - len(first_line.lstrip(" "))
+ # A check comment, indented the appropriate amount
+ check = "{:>{}}; {}".format("", indent, prefix)
+
+ output_lines.append("{}-LABEL: name: {}".format(check, func_name))
+
+ if print_fixed_stack:
+ output_lines.append("{}: fixedStack:".format(check))
+ for stack_line in func_info.extrascrub.splitlines():
+ filecheck_directive = check + "-NEXT"
+ output_lines.append("{}: {}".format(filecheck_directive, stack_line))
+
+ first_check = not first_check_is_next
+ for func_line in func_body:
+ 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 + "$}}")
+ continue
+ filecheck_directive = check if first_check else check + "-NEXT"
+ first_check = False
+ check_line = "{}: {}".format(filecheck_directive, func_line[indent:]).rstrip()
+ output_lines.append(check_line)
+
+
+def should_add_mir_line_to_output(input_line, prefix_set):
+ # Skip any check lines that we're handling as well as comments
+ m = CHECK_RE.match(input_line)
+ if (m and m.group(1) in prefix_set) or re.search("^[ \t]*;$", input_line):
+ return False
+ return True
+
+
+def add_mir_checks(
+ input_lines,
+ prefix_set,
+ autogenerated_note,
+ test,
+ run_list,
+ func_dict,
+ print_fixed_stack,
+ first_check_is_next,
+ at_the_function_name,
+):
+ simple_functions = find_mir_functions_with_one_bb(input_lines)
+
+ output_lines = []
+ output_lines.append(autogenerated_note)
+
+ func_name = None
+ state = "toplevel"
+ for input_line in input_lines:
+ if input_line == autogenerated_note:
+ continue
+
+ if state == "toplevel":
+ m = IR_FUNC_NAME_RE.match(input_line)
+ if m:
+ state = "ir function prefix"
+ func_name = m.group("func")
+ if input_line.rstrip("| \r\n") == "---":
+ state = "document"
+ output_lines.append(input_line)
+ elif state == "document":
+ m = MIR_FUNC_NAME_RE.match(input_line)
+ if m:
+ state = "mir function metadata"
+ func_name = m.group("func")
+ if input_line.strip() == "...":
+ state = "toplevel"
+ func_name = None
+ if should_add_mir_line_to_output(input_line, prefix_set):
+ output_lines.append(input_line)
+ elif state == "mir function metadata":
+ if should_add_mir_line_to_output(input_line, prefix_set):
+ output_lines.append(input_line)
+ m = MIR_BODY_BEGIN_RE.match(input_line)
+ if m:
+ if func_name in simple_functions:
+ # If there's only one block, put the checks inside it
+ state = "mir function prefix"
+ continue
+ state = "mir function body"
+ add_mir_checks_for_function(
+ test,
+ output_lines,
+ run_list,
+ func_dict,
+ func_name,
+ single_bb=False,
+ print_fixed_stack=print_fixed_stack,
+ first_check_is_next=first_check_is_next,
+ at_the_function_name=at_the_function_name,
+ )
+ elif state == "mir function prefix":
+ m = MIR_PREFIX_DATA_RE.match(input_line)
+ if not m:
+ state = "mir function body"
+ add_mir_checks_for_function(
+ test,
+ output_lines,
+ run_list,
+ func_dict,
+ func_name,
+ single_bb=True,
+ print_fixed_stack=print_fixed_stack,
+ first_check_is_next=first_check_is_next,
+ at_the_function_name=at_the_function_name,
+ )
+
+ if should_add_mir_line_to_output(input_line, prefix_set):
+ output_lines.append(input_line)
+ elif state == "mir function body":
+ if input_line.strip() == "...":
+ state = "toplevel"
+ func_name = None
+ if should_add_mir_line_to_output(input_line, prefix_set):
+ output_lines.append(input_line)
+ elif state == "ir function prefix":
+ m = IR_PREFIX_DATA_RE.match(input_line)
+ if not m:
+ state = "ir function body"
+ add_mir_checks_for_function(
+ test,
+ output_lines,
+ run_list,
+ func_dict,
+ func_name,
+ single_bb=False,
+ print_fixed_stack=print_fixed_stack,
+ first_check_is_next=first_check_is_next,
+ at_the_function_name=at_the_function_name,
+ )
+
+ if should_add_mir_line_to_output(input_line, prefix_set):
+ output_lines.append(input_line)
+ elif state == "ir function body":
+ if input_line.strip() == "}":
+ state = "toplevel"
+ func_name = None
+ if should_add_mir_line_to_output(input_line, prefix_set):
+ output_lines.append(input_line)
+ return output_lines
+
+
def build_global_values_dictionary(glob_val_dict, raw_tool_output, prefixes, ginfo):
for nameless_value in ginfo.get_nameless_values():
if nameless_value.global_ir_rhs_regexp is None:
diff --git a/llvm/utils/update_givaluetracking_test_checks.py b/llvm/utils/update_givaluetracking_test_checks.py
new file mode 100755
index 0000000000000..13a0aee26c408
--- /dev/null
+++ b/llvm/utils/update_givaluetracking_test_checks.py
@@ -0,0 +1,138 @@
+#!/usr/bin/env python3
+
+"""Updates FileCheck checks in GlobalISel Known Bits tests.
+
+This script is a utility to update MIR based tests with new FileCheck
+patterns for GlobalISel Known Bits.
+
+The checks added by this script are similar to update_mir_test_checks, using
+the output of KnownBits and SignBits from -passes=print<gisel-value-tracking>.
+"""
+
+from __future__ import print_function
+
+from sys import stderr
+from traceback import print_exc
+import argparse
+import os
+import re
+import sys
+
+from UpdateTestChecks import common
+
+VT_FUNCTION_RE = re.compile(
+ r"\s*name:\s*@(?P<func>[A-Za-z0-9_-]+)"
+ r"(?P<body>(\s*%[0-9]+:_\s*KnownBits:[01?]+\sSignBits:[0-9]+$)+)",
+ flags=(re.X | re.M),
+)
+
+
+def update_test(ti: common.TestInfo):
+ run_list = []
+ for l in ti.run_lines:
+ if "|" not in l:
+ common.warn("Skipping unparsable RUN line: " + l)
+ continue
+
+ (llc_cmd, filecheck_cmd) = tuple([cmd.strip() for cmd in l.split("|", 1)])
+ common.verify_filecheck_prefixes(filecheck_cmd)
+
+ if not llc_cmd.startswith("llc "):
+ common.warn("Skipping non-llc RUN line: " + l)
+ continue
+
+ if not filecheck_cmd.startswith("FileCheck "):
+ common.warn("Skipping non-FileChecked RUN line: " + l)
+ continue
+
+ llc_cmd_args = llc_cmd[4:].strip()
+ llc_cmd_args = llc_cmd_args.replace("< %s", "").replace("%s", "").strip()
+ check_prefixes = common.get_check_prefixes(filecheck_cmd)
+
+ run_list.append((check_prefixes, llc_cmd_args))
+
+ ginfo = common.make_analyze_generalizer(version=1)
+ builder = common.FunctionTestBuilder(
+ run_list=run_list,
+ flags=type(
+ "",
+ (object,),
+ {
+ "verbose": ti.args.verbose,
+ "filters": ti.args.filters,
+ "function_signature": False,
+ "check_attributes": False,
+ "replace_value_regex": [],
+ },
+ ),
+ scrubber_args=[],
+ path=ti.path,
+ ginfo=ginfo,
+ )
+
+ for prefixes, llc_args in run_list:
+ common.debug("Extracted llc cmd:", "llc", llc_args)
+ common.debug("Extracted FileCheck prefixes:", str(prefixes))
+
+ if ti.path.endswith(".mir"):
+ llc_args += " -x mir"
+ raw_tool_output = common.invoke_tool(
+ ti.args.llc_binary, llc_args, ti.path, verbose=ti.args.verbose
+ )
+
+ builder.process_run_line(
+ VT_FUNCTION_RE,
+ common.scrub_body,
+ raw_tool_output,
+ prefixes,
+ )
+
+ builder.processed_prefixes(prefixes)
+
+ func_dict = builder.finish_and_get_func_dict()
+ prefix_set = set([prefix for p in run_list for prefix in p[0]])
+ common.debug("Rewriting FileCheck prefixes:", str(prefix_set))
+ output_lines = common.add_mir_checks(
+ ti.input_lines,
+ prefix_set,
+ ti.test_autogenerated_note,
+ ti.path,
+ run_list,
+ func_dict,
+ print_fixed_stack=False,
+ first_check_is_next=True,
+ at_the_function_name=True,
+ )
+
+ common.debug("Writing %d lines to %s..." % (len(output_lines), ti.path))
+
+ with open(ti.path, "wb") as f:
+ f.writelines(["{}\n".format(l).encode("utf-8") for l in output_lines])
+
+
+def main():
+ parser = argparse.ArgumentParser(
+ description=__doc__, formatter_class=argparse.RawTextHelpFormatter
+ )
+ parser.add_argument(
+ "--llc-binary",
+ default=None,
+ help='The "llc" binary to generate the test case with',
+ )
+ parser.add_argument("tests", nargs="+")
+ args = common.parse_commandline_args(parser)
+
+ script_name = os.path.basename(__file__)
+ returncode = 0
+ for ti in common.itertests(args.tests, parser, script_name="utils/" + script_name):
+ try:
+ update_test(ti)
+ except Exception:
+ stderr.write(f"Error: Failed to update test {ti.path}\n")
+ print_exc()
+ returncode = 1
+ return returncode
+
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/llvm/utils/update_mir_test_checks.py b/llvm/utils/update_mir_test_checks.py
index 86147034d946b..96ac90a79d848 100755
--- a/llvm/utils/update_mir_test_checks.py
+++ b/llvm/utils/update_mir_test_checks.py
@@ -32,9 +32,6 @@
from UpdateTestChecks import common
-MIR_FUNC_NAME_RE = re.compile(r" *name: *(?P<func>[A-Za-z0-9_.-]+)")
-MIR_BODY_BEGIN_RE = re.compile(r" *body: *\|")
-MIR_BASIC_BLOCK_RE = re.compile(r" *bb\.[0-9]+.*:$")
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 "
@@ -47,12 +44,6 @@
VREG_RE.pattern, MI_FLAGS_STR, VREG_DEF_FLAGS_STR
)
)
-MIR_PREFIX_DATA_RE = re.compile(r"^ *(;|bb.[0-9].*: *$|[a-z]+:( |$)|$)")
-
-IR_FUNC_NAME_RE = re.compile(
- r"^\s*define\s+(?:internal\s+)?[^@]*@(?P<func>[A-Za-z0-9_.]+)\s*\("
-)
-IR_PREFIX_DATA_RE = re.compile(r"^ *(;|$)")
MIR_FUNC_RE = re.compile(
r"^---$"
@@ -88,16 +79,6 @@ def __call__(self, args, ir):
return stdout
-class Run:
- def __init__(self, prefixes, cmd_args, triple):
- self.prefixes = prefixes
- self.cmd_args = cmd_args
- self.triple = triple
-
- def __getitem__(self, index):
- return [self.prefixes, self.cmd_args, self.triple][index]
-
-
def log(msg, verbose=True):
if verbose:
print(msg, file=sys.stderr)
@@ -147,46 +128,16 @@ def build_run_list(test, run_lines, verbose=False):
check_prefixes = common.get_check_prefixes(filecheck_cmd)
all_prefixes += check_prefixes
- run_list.append(Run(check_prefixes, cmd_args, triple))
+ run_list.append((check_prefixes, cmd_args, triple))
# Sort prefixes that are shared between run lines before unshared prefixes.
# This causes us to prefer printing shared prefixes.
for run in run_list:
- run.prefixes.sort(key=lambda prefix: -all_prefixes.count(prefix))
+ run[0].sort(key=lambda prefix: -all_prefixes.count(prefix))
return run_list
-def find_functions_with_one_bb(lines, verbose=False):
- result = []
- cur_func = None
- bbs = 0
- for line in lines:
- m = MIR_FUNC_NAME_RE.match(line)
- if m:
- if bbs == 1:
- result.append(cur_func)
- cur_func = m.group("func")
- bbs = 0
- m = MIR_BASIC_BLOCK_RE.match(line)
- if m:
- bbs += 1
- if bbs == 1:
- result.append(cur_func)
- return result
-
-
-class FunctionInfo:
- def __init__(self, body, fixedStack):
- self.body = body
- self.fixedStack = fixedStack
-
- def __eq__(self, other):
- if not isinstance(other, FunctionInfo):
- return False
- return self.body == other.body and self.fixedStack == other.fixedStack
-
-
def build_function_info_dictionary(
test, raw_tool_output, triple, prefixes, func_dict, verbose
):
@@ -222,88 +173,19 @@ def build_function_info_dictionary(
body = "".join(mangled)
for prefix in prefixes:
- info = FunctionInfo(body, fixedStack)
+ info = common.function_body(
+ body, fixedStack, None, None, None, None, ginfo=None
+ )
if func in func_dict[prefix]:
- if func_dict[prefix][func] != info:
+ if (
+ 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 add_checks_for_function(
- test, output_lines, run_list, func_dict, func_name, single_bb, args
-):
- printed_prefixes = set()
- for run in run_list:
- for prefix in run.prefixes:
- if prefix in printed_prefixes:
- break
- if not func_dict[prefix][func_name]:
- continue
- if printed_prefixes:
- # Add some space between different check prefixes.
- indent = len(output_lines[-1]) - len(output_lines[-1].lstrip(" "))
- output_lines.append(" "*indent + ";")
- printed_prefixes.add(prefix)
- log("Adding {} lines for {}".format(prefix, func_name), args.verbose)
- add_check_lines(
- test,
- output_lines,
- prefix,
- func_name,
- single_bb,
- func_dict[prefix][func_name],
- args,
- )
- break
- else:
- common.warn(
- "Found conflicting asm for function: {}".format(func_name),
- test_file=test,
- )
- return output_lines
-
-
-def add_check_lines(
- test, output_lines, prefix, func_name, single_bb, func_info: FunctionInfo, args
-):
- func_body = func_info.body.splitlines()
- if single_bb:
- # Don't bother checking the basic block label for a single BB
- func_body.pop(0)
-
- if not func_body:
- common.warn(
- "Function has no instructions to check: {}".format(func_name),
- test_file=test,
- )
- return
-
- first_line = func_body[0]
- indent = len(first_line) - len(first_line.lstri...
[truncated]
|
@llvm/pr-subscribers-testing-tools Author: David Green (davemgreen) ChangesAs with the other update scripts this takes the output of -passes=print<gisel-value-tracking> and inserts the results into an existing mir file. This means that the input is a lot like update_analysis_test_checks.py, and the output needs to insert into a mir file similarly to update_mir_test_checks.py. The code used to do the inserting has been moved to common, to allow it to be reused. Otherwise it tries to reuse the existing infrastructure, and update_givaluetracking_test_checks is kept relatively short. Patch is 26.19 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/140296.diff 4 Files Affected:
diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/knownbits-const.mir b/llvm/test/CodeGen/AArch64/GlobalISel/knownbits-const.mir
index 5d88bb08ebe72..00af98cc504c3 100644
--- a/llvm/test/CodeGen/AArch64/GlobalISel/knownbits-const.mir
+++ b/llvm/test/CodeGen/AArch64/GlobalISel/knownbits-const.mir
@@ -1,27 +1,27 @@
-# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
-# RUN: llc -mtriple aarch64 -passes="print<gisel-value-tracking>" %s -o - 2>&1 | FileCheck %s
+# NOTE: Assertions have been autogenerated by utils/update_givaluetracking_test_checks.py UTC_ARGS: --version 5
+# RUN: llc -mtriple aarch64 -passes="print<gisel-value-tracking>" %s -filetype=null 2>&1 | FileCheck %s
---
name: Cst
tracksRegLiveness: true
body: |
bb.1:
- ; CHECK-LABEL: name: @Cst
- ; CHECK-NEXT: %0:_ KnownBits:00000001 SignBits:7
- ; CHECK-NEXT: %1:_ KnownBits:00000001 SignBits:7
+ ; CHECK-LABEL: name: @Cst
+ ; CHECK-NEXT: %0:_ KnownBits:00000001 SignBits:7
+ ; CHECK-NEXT: %1:_ KnownBits:00000001 SignBits:7
%0:_(s8) = G_CONSTANT i8 1
%1:_(s8) = COPY %0
...
---
name: CstWithClass
-# We can't analyze %0 due to the lack of an LLT. We will get a default
-# constructed KnownBits back. %0 will have the correct size but we will
-# not know any further info.
tracksRegLiveness: true
body: |
bb.1:
- ; CHECK-LABEL: name: @CstWithClass
- ; CHECK-NEXT: %1:_ KnownBits:???????????????????????????????? SignBits:1
+ ; We can't analyze %0 due to the lack of an LLT. We will get a default
+ ; constructed KnownBits back. %0 will have the correct size but we will
+ ; not know any further info.
+ ; CHECK-LABEL: name: @CstWithClass
+ ; CHECK-NEXT: %1:_ KnownBits:???????????????????????????????? SignBits:1
%0:gpr32 = MOVi32imm 1
%1:_(s32) = COPY %0
...
diff --git a/llvm/utils/UpdateTestChecks/common.py b/llvm/utils/UpdateTestChecks/common.py
index d150612dd368e..35c80faf95013 100644
--- a/llvm/utils/UpdateTestChecks/common.py
+++ b/llvm/utils/UpdateTestChecks/common.py
@@ -2270,6 +2270,244 @@ def add_analyze_checks(
)
+IR_FUNC_NAME_RE = re.compile(
+ r"^\s*define\s+(?:internal\s+)?[^@]*@(?P<func>[A-Za-z0-9_.]+)\s*\("
+)
+IR_PREFIX_DATA_RE = re.compile(r"^ *(;|$)")
+MIR_FUNC_NAME_RE = re.compile(r" *name: *(?P<func>[A-Za-z0-9_.-]+)")
+MIR_BODY_BEGIN_RE = re.compile(r" *body: *\|")
+MIR_BASIC_BLOCK_RE = re.compile(r" *bb\.[0-9]+.*:$")
+MIR_PREFIX_DATA_RE = re.compile(r"^ *(;|bb.[0-9].*: *$|[a-z]+:( |$)|$)")
+
+
+def find_mir_functions_with_one_bb(lines, verbose=False):
+ result = []
+ cur_func = None
+ bbs = 0
+ for line in lines:
+ m = MIR_FUNC_NAME_RE.match(line)
+ if m:
+ if bbs == 1:
+ result.append(cur_func)
+ cur_func = m.group("func")
+ bbs = 0
+ m = MIR_BASIC_BLOCK_RE.match(line)
+ if m:
+ bbs += 1
+ if bbs == 1:
+ result.append(cur_func)
+ return result
+
+
+def add_mir_checks_for_function(
+ test,
+ output_lines,
+ run_list,
+ func_dict,
+ func_name,
+ single_bb,
+ print_fixed_stack,
+ first_check_is_next,
+ at_the_function_name,
+):
+ printed_prefixes = set()
+ for run in run_list:
+ for prefix in run[0]:
+ if prefix in printed_prefixes:
+ break
+ if not func_dict[prefix][func_name]:
+ continue
+ if printed_prefixes:
+ # Add some space between different check prefixes.
+ indent = len(output_lines[-1]) - len(output_lines[-1].lstrip(" "))
+ output_lines.append(" " * indent + ";")
+ printed_prefixes.add(prefix)
+ add_mir_check_lines(
+ test,
+ output_lines,
+ prefix,
+ ("@" if at_the_function_name else "") + func_name,
+ single_bb,
+ func_dict[prefix][func_name],
+ print_fixed_stack,
+ first_check_is_next,
+ )
+ break
+ else:
+ warn(
+ "Found conflicting asm for function: {}".format(func_name),
+ test_file=test,
+ )
+ return output_lines
+
+
+def add_mir_check_lines(
+ test,
+ output_lines,
+ prefix,
+ func_name,
+ single_bb,
+ func_info,
+ print_fixed_stack,
+ first_check_is_next,
+):
+ func_body = str(func_info).splitlines()
+ if single_bb:
+ # Don't bother checking the basic block label for a single BB
+ func_body.pop(0)
+
+ if not func_body:
+ warn(
+ "Function has no instructions to check: {}".format(func_name),
+ test_file=test,
+ )
+ return
+
+ first_line = func_body[0]
+ indent = len(first_line) - len(first_line.lstrip(" "))
+ # A check comment, indented the appropriate amount
+ check = "{:>{}}; {}".format("", indent, prefix)
+
+ output_lines.append("{}-LABEL: name: {}".format(check, func_name))
+
+ if print_fixed_stack:
+ output_lines.append("{}: fixedStack:".format(check))
+ for stack_line in func_info.extrascrub.splitlines():
+ filecheck_directive = check + "-NEXT"
+ output_lines.append("{}: {}".format(filecheck_directive, stack_line))
+
+ first_check = not first_check_is_next
+ for func_line in func_body:
+ 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 + "$}}")
+ continue
+ filecheck_directive = check if first_check else check + "-NEXT"
+ first_check = False
+ check_line = "{}: {}".format(filecheck_directive, func_line[indent:]).rstrip()
+ output_lines.append(check_line)
+
+
+def should_add_mir_line_to_output(input_line, prefix_set):
+ # Skip any check lines that we're handling as well as comments
+ m = CHECK_RE.match(input_line)
+ if (m and m.group(1) in prefix_set) or re.search("^[ \t]*;$", input_line):
+ return False
+ return True
+
+
+def add_mir_checks(
+ input_lines,
+ prefix_set,
+ autogenerated_note,
+ test,
+ run_list,
+ func_dict,
+ print_fixed_stack,
+ first_check_is_next,
+ at_the_function_name,
+):
+ simple_functions = find_mir_functions_with_one_bb(input_lines)
+
+ output_lines = []
+ output_lines.append(autogenerated_note)
+
+ func_name = None
+ state = "toplevel"
+ for input_line in input_lines:
+ if input_line == autogenerated_note:
+ continue
+
+ if state == "toplevel":
+ m = IR_FUNC_NAME_RE.match(input_line)
+ if m:
+ state = "ir function prefix"
+ func_name = m.group("func")
+ if input_line.rstrip("| \r\n") == "---":
+ state = "document"
+ output_lines.append(input_line)
+ elif state == "document":
+ m = MIR_FUNC_NAME_RE.match(input_line)
+ if m:
+ state = "mir function metadata"
+ func_name = m.group("func")
+ if input_line.strip() == "...":
+ state = "toplevel"
+ func_name = None
+ if should_add_mir_line_to_output(input_line, prefix_set):
+ output_lines.append(input_line)
+ elif state == "mir function metadata":
+ if should_add_mir_line_to_output(input_line, prefix_set):
+ output_lines.append(input_line)
+ m = MIR_BODY_BEGIN_RE.match(input_line)
+ if m:
+ if func_name in simple_functions:
+ # If there's only one block, put the checks inside it
+ state = "mir function prefix"
+ continue
+ state = "mir function body"
+ add_mir_checks_for_function(
+ test,
+ output_lines,
+ run_list,
+ func_dict,
+ func_name,
+ single_bb=False,
+ print_fixed_stack=print_fixed_stack,
+ first_check_is_next=first_check_is_next,
+ at_the_function_name=at_the_function_name,
+ )
+ elif state == "mir function prefix":
+ m = MIR_PREFIX_DATA_RE.match(input_line)
+ if not m:
+ state = "mir function body"
+ add_mir_checks_for_function(
+ test,
+ output_lines,
+ run_list,
+ func_dict,
+ func_name,
+ single_bb=True,
+ print_fixed_stack=print_fixed_stack,
+ first_check_is_next=first_check_is_next,
+ at_the_function_name=at_the_function_name,
+ )
+
+ if should_add_mir_line_to_output(input_line, prefix_set):
+ output_lines.append(input_line)
+ elif state == "mir function body":
+ if input_line.strip() == "...":
+ state = "toplevel"
+ func_name = None
+ if should_add_mir_line_to_output(input_line, prefix_set):
+ output_lines.append(input_line)
+ elif state == "ir function prefix":
+ m = IR_PREFIX_DATA_RE.match(input_line)
+ if not m:
+ state = "ir function body"
+ add_mir_checks_for_function(
+ test,
+ output_lines,
+ run_list,
+ func_dict,
+ func_name,
+ single_bb=False,
+ print_fixed_stack=print_fixed_stack,
+ first_check_is_next=first_check_is_next,
+ at_the_function_name=at_the_function_name,
+ )
+
+ if should_add_mir_line_to_output(input_line, prefix_set):
+ output_lines.append(input_line)
+ elif state == "ir function body":
+ if input_line.strip() == "}":
+ state = "toplevel"
+ func_name = None
+ if should_add_mir_line_to_output(input_line, prefix_set):
+ output_lines.append(input_line)
+ return output_lines
+
+
def build_global_values_dictionary(glob_val_dict, raw_tool_output, prefixes, ginfo):
for nameless_value in ginfo.get_nameless_values():
if nameless_value.global_ir_rhs_regexp is None:
diff --git a/llvm/utils/update_givaluetracking_test_checks.py b/llvm/utils/update_givaluetracking_test_checks.py
new file mode 100755
index 0000000000000..13a0aee26c408
--- /dev/null
+++ b/llvm/utils/update_givaluetracking_test_checks.py
@@ -0,0 +1,138 @@
+#!/usr/bin/env python3
+
+"""Updates FileCheck checks in GlobalISel Known Bits tests.
+
+This script is a utility to update MIR based tests with new FileCheck
+patterns for GlobalISel Known Bits.
+
+The checks added by this script are similar to update_mir_test_checks, using
+the output of KnownBits and SignBits from -passes=print<gisel-value-tracking>.
+"""
+
+from __future__ import print_function
+
+from sys import stderr
+from traceback import print_exc
+import argparse
+import os
+import re
+import sys
+
+from UpdateTestChecks import common
+
+VT_FUNCTION_RE = re.compile(
+ r"\s*name:\s*@(?P<func>[A-Za-z0-9_-]+)"
+ r"(?P<body>(\s*%[0-9]+:_\s*KnownBits:[01?]+\sSignBits:[0-9]+$)+)",
+ flags=(re.X | re.M),
+)
+
+
+def update_test(ti: common.TestInfo):
+ run_list = []
+ for l in ti.run_lines:
+ if "|" not in l:
+ common.warn("Skipping unparsable RUN line: " + l)
+ continue
+
+ (llc_cmd, filecheck_cmd) = tuple([cmd.strip() for cmd in l.split("|", 1)])
+ common.verify_filecheck_prefixes(filecheck_cmd)
+
+ if not llc_cmd.startswith("llc "):
+ common.warn("Skipping non-llc RUN line: " + l)
+ continue
+
+ if not filecheck_cmd.startswith("FileCheck "):
+ common.warn("Skipping non-FileChecked RUN line: " + l)
+ continue
+
+ llc_cmd_args = llc_cmd[4:].strip()
+ llc_cmd_args = llc_cmd_args.replace("< %s", "").replace("%s", "").strip()
+ check_prefixes = common.get_check_prefixes(filecheck_cmd)
+
+ run_list.append((check_prefixes, llc_cmd_args))
+
+ ginfo = common.make_analyze_generalizer(version=1)
+ builder = common.FunctionTestBuilder(
+ run_list=run_list,
+ flags=type(
+ "",
+ (object,),
+ {
+ "verbose": ti.args.verbose,
+ "filters": ti.args.filters,
+ "function_signature": False,
+ "check_attributes": False,
+ "replace_value_regex": [],
+ },
+ ),
+ scrubber_args=[],
+ path=ti.path,
+ ginfo=ginfo,
+ )
+
+ for prefixes, llc_args in run_list:
+ common.debug("Extracted llc cmd:", "llc", llc_args)
+ common.debug("Extracted FileCheck prefixes:", str(prefixes))
+
+ if ti.path.endswith(".mir"):
+ llc_args += " -x mir"
+ raw_tool_output = common.invoke_tool(
+ ti.args.llc_binary, llc_args, ti.path, verbose=ti.args.verbose
+ )
+
+ builder.process_run_line(
+ VT_FUNCTION_RE,
+ common.scrub_body,
+ raw_tool_output,
+ prefixes,
+ )
+
+ builder.processed_prefixes(prefixes)
+
+ func_dict = builder.finish_and_get_func_dict()
+ prefix_set = set([prefix for p in run_list for prefix in p[0]])
+ common.debug("Rewriting FileCheck prefixes:", str(prefix_set))
+ output_lines = common.add_mir_checks(
+ ti.input_lines,
+ prefix_set,
+ ti.test_autogenerated_note,
+ ti.path,
+ run_list,
+ func_dict,
+ print_fixed_stack=False,
+ first_check_is_next=True,
+ at_the_function_name=True,
+ )
+
+ common.debug("Writing %d lines to %s..." % (len(output_lines), ti.path))
+
+ with open(ti.path, "wb") as f:
+ f.writelines(["{}\n".format(l).encode("utf-8") for l in output_lines])
+
+
+def main():
+ parser = argparse.ArgumentParser(
+ description=__doc__, formatter_class=argparse.RawTextHelpFormatter
+ )
+ parser.add_argument(
+ "--llc-binary",
+ default=None,
+ help='The "llc" binary to generate the test case with',
+ )
+ parser.add_argument("tests", nargs="+")
+ args = common.parse_commandline_args(parser)
+
+ script_name = os.path.basename(__file__)
+ returncode = 0
+ for ti in common.itertests(args.tests, parser, script_name="utils/" + script_name):
+ try:
+ update_test(ti)
+ except Exception:
+ stderr.write(f"Error: Failed to update test {ti.path}\n")
+ print_exc()
+ returncode = 1
+ return returncode
+
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/llvm/utils/update_mir_test_checks.py b/llvm/utils/update_mir_test_checks.py
index 86147034d946b..96ac90a79d848 100755
--- a/llvm/utils/update_mir_test_checks.py
+++ b/llvm/utils/update_mir_test_checks.py
@@ -32,9 +32,6 @@
from UpdateTestChecks import common
-MIR_FUNC_NAME_RE = re.compile(r" *name: *(?P<func>[A-Za-z0-9_.-]+)")
-MIR_BODY_BEGIN_RE = re.compile(r" *body: *\|")
-MIR_BASIC_BLOCK_RE = re.compile(r" *bb\.[0-9]+.*:$")
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 "
@@ -47,12 +44,6 @@
VREG_RE.pattern, MI_FLAGS_STR, VREG_DEF_FLAGS_STR
)
)
-MIR_PREFIX_DATA_RE = re.compile(r"^ *(;|bb.[0-9].*: *$|[a-z]+:( |$)|$)")
-
-IR_FUNC_NAME_RE = re.compile(
- r"^\s*define\s+(?:internal\s+)?[^@]*@(?P<func>[A-Za-z0-9_.]+)\s*\("
-)
-IR_PREFIX_DATA_RE = re.compile(r"^ *(;|$)")
MIR_FUNC_RE = re.compile(
r"^---$"
@@ -88,16 +79,6 @@ def __call__(self, args, ir):
return stdout
-class Run:
- def __init__(self, prefixes, cmd_args, triple):
- self.prefixes = prefixes
- self.cmd_args = cmd_args
- self.triple = triple
-
- def __getitem__(self, index):
- return [self.prefixes, self.cmd_args, self.triple][index]
-
-
def log(msg, verbose=True):
if verbose:
print(msg, file=sys.stderr)
@@ -147,46 +128,16 @@ def build_run_list(test, run_lines, verbose=False):
check_prefixes = common.get_check_prefixes(filecheck_cmd)
all_prefixes += check_prefixes
- run_list.append(Run(check_prefixes, cmd_args, triple))
+ run_list.append((check_prefixes, cmd_args, triple))
# Sort prefixes that are shared between run lines before unshared prefixes.
# This causes us to prefer printing shared prefixes.
for run in run_list:
- run.prefixes.sort(key=lambda prefix: -all_prefixes.count(prefix))
+ run[0].sort(key=lambda prefix: -all_prefixes.count(prefix))
return run_list
-def find_functions_with_one_bb(lines, verbose=False):
- result = []
- cur_func = None
- bbs = 0
- for line in lines:
- m = MIR_FUNC_NAME_RE.match(line)
- if m:
- if bbs == 1:
- result.append(cur_func)
- cur_func = m.group("func")
- bbs = 0
- m = MIR_BASIC_BLOCK_RE.match(line)
- if m:
- bbs += 1
- if bbs == 1:
- result.append(cur_func)
- return result
-
-
-class FunctionInfo:
- def __init__(self, body, fixedStack):
- self.body = body
- self.fixedStack = fixedStack
-
- def __eq__(self, other):
- if not isinstance(other, FunctionInfo):
- return False
- return self.body == other.body and self.fixedStack == other.fixedStack
-
-
def build_function_info_dictionary(
test, raw_tool_output, triple, prefixes, func_dict, verbose
):
@@ -222,88 +173,19 @@ def build_function_info_dictionary(
body = "".join(mangled)
for prefix in prefixes:
- info = FunctionInfo(body, fixedStack)
+ info = common.function_body(
+ body, fixedStack, None, None, None, None, ginfo=None
+ )
if func in func_dict[prefix]:
- if func_dict[prefix][func] != info:
+ if (
+ 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 add_checks_for_function(
- test, output_lines, run_list, func_dict, func_name, single_bb, args
-):
- printed_prefixes = set()
- for run in run_list:
- for prefix in run.prefixes:
- if prefix in printed_prefixes:
- break
- if not func_dict[prefix][func_name]:
- continue
- if printed_prefixes:
- # Add some space between different check prefixes.
- indent = len(output_lines[-1]) - len(output_lines[-1].lstrip(" "))
- output_lines.append(" "*indent + ";")
- printed_prefixes.add(prefix)
- log("Adding {} lines for {}".format(prefix, func_name), args.verbose)
- add_check_lines(
- test,
- output_lines,
- prefix,
- func_name,
- single_bb,
- func_dict[prefix][func_name],
- args,
- )
- break
- else:
- common.warn(
- "Found conflicting asm for function: {}".format(func_name),
- test_file=test,
- )
- return output_lines
-
-
-def add_check_lines(
- test, output_lines, prefix, func_name, single_bb, func_info: FunctionInfo, args
-):
- func_body = func_info.body.splitlines()
- if single_bb:
- # Don't bother checking the basic block label for a single BB
- func_body.pop(0)
-
- if not func_body:
- common.warn(
- "Function has no instructions to check: {}".format(func_name),
- test_file=test,
- )
- return
-
- first_line = func_body[0]
- indent = len(first_line) - len(first_line.lstri...
[truncated]
|
6812205
to
3a59a42
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could this be integrated into update_mir_test_checks.py
if we see -passes="print<gisel-value-tracking>"
in the RUN line?
…. NFC This served as a test of update_mir_test_checks from #140296, although the test changes are mostly because the tests have not ben regenerated in a long time. We managed to stop it from removing extra comments, only the empty lines are now removed.
As with the other update scripts this takes the output of -passes=print and inserts the results into an existing mir file. This means that the input is a lot like update_analysis_test_checks.py, and the output needs to insert into a mir file similarly to update_mir_test_checks.py. The code used to do the inserting has been moved to common, to allow it to be reused. Otherwise it tries to reuse the existing infrastructure, and update_givaluetracking_test_checks is kept relatively short.