diff --git a/llvm/docs/TestingGuide.rst b/llvm/docs/TestingGuide.rst index 6ab33383e929..f2c953a6221f 100644 --- a/llvm/docs/TestingGuide.rst +++ b/llvm/docs/TestingGuide.rst @@ -328,6 +328,9 @@ assertions: update_test_checks.py opt + update_llubi_test_checks.py + llubi + Precommit workflow for tests ---------------------------- diff --git a/llvm/test/tools/llubi/main.ll b/llvm/test/tools/llubi/main.ll index c10824621018..d1e91312314e 100644 --- a/llvm/test/tools/llubi/main.ll +++ b/llvm/test/tools/llubi/main.ll @@ -1,3 +1,4 @@ +; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6 ; RUN: llubi --verbose < %s 2>&1 | FileCheck %s define i32 @main(i32 %argc, ptr %argv) { @@ -5,7 +6,7 @@ define i32 @main(i32 %argc, ptr %argv) { } ; CHECK: Entering function: main -; CHECK: i32 %argc = i32 1 -; CHECK: ptr %argv = ptr 0x8 [argv] -; CHECK: ret i32 0 -; CHECK: Exiting function: main +; CHECK-NEXT: i32 %argc = i32 1 +; CHECK-NEXT: ptr %argv = ptr 0x8 [argv] +; CHECK-NEXT: ret i32 0 +; CHECK-NEXT: Exiting function: main diff --git a/llvm/test/tools/llubi/main2.ll b/llvm/test/tools/llubi/main2.ll index 58c5744bb090..0752f6335418 100644 --- a/llvm/test/tools/llubi/main2.ll +++ b/llvm/test/tools/llubi/main2.ll @@ -1,3 +1,4 @@ +; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6 ; RUN: llubi --verbose < %s 2>&1 | FileCheck %s define i32 @main() { @@ -5,5 +6,5 @@ define i32 @main() { } ; CHECK: Entering function: main -; CHECK: ret i32 0 -; CHECK: Exiting function: main +; CHECK-NEXT: ret i32 0 +; CHECK-NEXT: Exiting function: main diff --git a/llvm/test/tools/llubi/poison.ll b/llvm/test/tools/llubi/poison.ll index cf3b69d1aeb7..9d98b7f75e86 100644 --- a/llvm/test/tools/llubi/poison.ll +++ b/llvm/test/tools/llubi/poison.ll @@ -1,11 +1,12 @@ +; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6 ; RUN: not llubi --verbose < %s 2>&1 | FileCheck %s define i32 @main(i32 %argc, ptr %argv) { ret i32 poison } ; CHECK: Entering function: main -; CHECK: i32 %argc = i32 1 -; CHECK: ptr %argv = ptr 0x8 [argv] -; CHECK: ret i32 poison -; CHECK: Exiting function: main -; CHECK: error: Execution of function 'main' resulted in poison return value. +; CHECK-NEXT: i32 %argc = i32 1 +; CHECK-NEXT: ptr %argv = ptr 0x8 [argv] +; CHECK-NEXT: ret i32 poison +; CHECK-NEXT: Exiting function: main +; CHECK-NEXT: error: Execution of function 'main' resulted in poison return value. diff --git a/llvm/utils/UpdateTestChecks/common.py b/llvm/utils/UpdateTestChecks/common.py index 9cca0a7ad5d3..60ee18f5e4fc 100644 --- a/llvm/utils/UpdateTestChecks/common.py +++ b/llvm/utils/UpdateTestChecks/common.py @@ -2723,6 +2723,7 @@ def get_autogennote_suffix(parser, args): "tool_binary", "opt_binary", "llc_binary", + "llubi_binary", "clang", "opt", "llvm_bin", diff --git a/llvm/utils/update_llubi_test_checks.py b/llvm/utils/update_llubi_test_checks.py new file mode 100755 index 000000000000..6b2600069835 --- /dev/null +++ b/llvm/utils/update_llubi_test_checks.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python3 +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +"""A test case update script. + +This script is a utility to update LLVM 'llubi' based test cases with new +FileCheck patterns. +""" + +from __future__ import print_function + +from sys import stderr +from traceback import print_exc +import argparse +import os +import subprocess +import sys + +from UpdateTestChecks import common + + +# Invoke the tool that is being tested. +def invoke_tool(exe, cmd_args, ir, check_rc): + with open(ir) as ir_file: + substitutions = common.getSubstitutions(ir) + stdout = subprocess.run( + exe + " " + common.applySubstitutions(cmd_args, substitutions), + shell=True, + stdin=ir_file, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + check=check_rc, + ).stdout.decode() + # Fix line endings to unix CR style. + return stdout.replace("\r\n", "\n") + + +def update_test(ti: common.TestInfo): + if len(ti.run_lines) == 0: + common.warn("No RUN lines found in test: " + ti.path) + return + if len(ti.run_lines) > 1: + common.warn("Multiple RUN lines found in test: " + ti.path) + common.warn("Only the first RUN line will be processed.") + + l = ti.run_lines[0] + if "|" not in l: + common.warn("Skipping unparsable RUN line: " + l) + return + + commands = [cmd.strip() for cmd in l.split("|")] + assert len(commands) == 2 + llubi_cmd = commands[-2] + filecheck_cmd = commands[-1] + args = llubi_cmd.split(" ") + llubi_tool = args[0] + check_rc = True + if len(args) > 1 and args[0] == "not": + llubi_tool = args[1] + check_rc = False + + common.verify_filecheck_prefixes(filecheck_cmd) + + if llubi_tool != "llubi": + common.warn("Skipping non-llubi RUN line: " + l) + return + + if not filecheck_cmd.startswith("FileCheck "): + common.warn("Skipping non-FileChecked RUN line: " + l) + return + + llubi_args = llubi_cmd[llubi_cmd.index(llubi_tool) + len(llubi_tool) :].strip() + llubi_args = llubi_args.replace("< %s", "").replace("%s", "").strip() + prefixes = common.get_check_prefixes(filecheck_cmd) + + common.debug("Extracted llubi cmd:", llubi_tool, llubi_args) + common.debug("Extracted FileCheck prefixes:", str(prefixes)) + prefix_set = set([prefix for prefix in prefixes]) + + raw_tool_output = invoke_tool( + ti.args.llubi_binary or llubi_tool, + llubi_args, + ti.path, + check_rc=check_rc, + ) + if ti.args.llubi_binary: + raw_tool_output = raw_tool_output.replace(ti.args.llubi_binary, llubi_tool) + + output_lines = [] + common.dump_input_lines(output_lines, ti, prefix_set, ";") + tool_output_lines = raw_tool_output.splitlines() + if len(tool_output_lines) == 0: + common.warn("No output from llubi.") + else: + output_lines.append("; CHECK: " + tool_output_lines[0]) + output_lines.extend(["; CHECK-NEXT: " + line for line in tool_output_lines[1:]]) + + 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__) + parser.add_argument( + "--llubi-binary", + default=None, + help='The "llubi" binary to use to generate the test case', + ) + parser.add_argument( + "--tool", + default=None, + help="Treat the given tool name as an llubi-like tool for which check lines should be generated", + ) + parser.add_argument("tests", nargs="+") + initial_args = common.parse_commandline_args(parser) + + script_name = os.path.basename(__file__) + + returncode = 0 + for ti in common.itertests( + initial_args.tests, parser, script_name="utils/" + script_name + ): + try: + update_test(ti) + except Exception as e: + stderr.write(f"Error: Failed to update test {ti.path}\n") + print_exc() + returncode = 1 + return returncode + + +if __name__ == "__main__": + sys.exit(main())