update-test-checks: safely handle tests with #if's

There is at least one Clang test (clang/test/CodeGen/arm_acle.c) which
has functions guarded by #if's that cause those functions to be compiled
only for a subset of RUN lines.

This results in a case where one RUN line has a body for the function
and another doesn't. Treat this case as a conflict for any prefixes that
the two RUN lines have in common.

This change exposed a bug where functions with '$' in the name weren't
properly recognized in ARM assembly (despite there being a test case
that was supposed to catch the problem!). This bug is fixed as well.

Differential Revision: https://reviews.llvm.org/D130089
This commit is contained in:
Nicolai Hähnle 2022-07-19 09:43:58 +02:00
parent 8ba794be31
commit 5a4033c367
11 changed files with 83 additions and 24 deletions

View File

@ -0,0 +1,12 @@
// RUN: %clang_cc1 -triple=x86_64-unknown-linux-gnu -emit-llvm -o - %s | FileCheck -check-prefixes=CHECK %s
// RUN: %clang_cc1 -triple=x86_64-unknown-linux-gnu -emit-llvm -o - %s -DFOO | FileCheck -check-prefixes=CHECK,FOO %s
#ifdef FOO
int foo() {
return 1;
}
#endif
int bar() {
return 2;
}

View File

@ -0,0 +1,21 @@
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py
// RUN: %clang_cc1 -triple=x86_64-unknown-linux-gnu -emit-llvm -o - %s | FileCheck -check-prefixes=CHECK %s
// RUN: %clang_cc1 -triple=x86_64-unknown-linux-gnu -emit-llvm -o - %s -DFOO | FileCheck -check-prefixes=CHECK,FOO %s
#ifdef FOO
// FOO-LABEL: @foo(
// FOO-NEXT: entry:
// FOO-NEXT: ret i32 1
//
int foo() {
return 1;
}
#endif
// CHECK-LABEL: @bar(
// CHECK-NEXT: entry:
// CHECK-NEXT: ret i32 2
//
int bar() {
return 2;
}

View File

@ -0,0 +1,8 @@
## Test that functions that are only compiled in a subset of RUN lines are
## handled correctly
# RUN: cp %S/Inputs/ifdef.c %t.c && %update_cc_test_checks %t.c
# RUN: diff -u %S/Inputs/ifdef.c.expected %t.c
## Check that re-running update_cc_test_checks doesn't change the output
# RUN: %update_cc_test_checks %t.c
# RUN: diff -u %S/Inputs/ifdef.c.expected %t.c

View File

@ -1,9 +1,8 @@
; Check that we accept functions with '$' in the name.
; TODO: This is not handled correcly on 32bit ARM and needs to be fixed.
; RUN: llc -mtriple=armv7-unknown-linux < %s | FileCheck --prefix=LINUX %s
; RUN: llc -mtriple=armv7-apple-darwin < %s | FileCheck --prefix=DARWIN %s
; RUN: llc -mtriple=armv7-apple-ios < %s | FileCheck --prefix=IOS %s
; RUN: llc -mtriple=armv7-unknown-linux < %s | FileCheck %s
; RUN: llc -mtriple=armv7-apple-darwin < %s | FileCheck %s
; RUN: llc -mtriple=armv7-apple-ios < %s | FileCheck %s
define hidden i32 @"_Z54bar$ompvariant$bar"() {
entry:

View File

@ -1,10 +1,9 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
; Check that we accept functions with '$' in the name.
; TODO: This is not handled correcly on 32bit ARM and needs to be fixed.
; RUN: llc -mtriple=armv7-unknown-linux < %s | FileCheck --prefix=LINUX %s
; RUN: llc -mtriple=armv7-apple-darwin < %s | FileCheck --prefix=DARWIN %s
; RUN: llc -mtriple=armv7-apple-ios < %s | FileCheck --prefix=IOS %s
; RUN: llc -mtriple=armv7-unknown-linux < %s | FileCheck %s
; RUN: llc -mtriple=armv7-apple-darwin < %s | FileCheck %s
; RUN: llc -mtriple=armv7-apple-ios < %s | FileCheck %s
define hidden i32 @"_Z54bar$ompvariant$bar"() {
; CHECK-LABEL: _Z54bar$ompvariant$bar:

View File

@ -23,10 +23,10 @@ ASM_FUNCTION_X86_RE = re.compile(
flags=(re.M | re.S))
ASM_FUNCTION_ARM_RE = re.compile(
r'^(?P<func>[0-9a-zA-Z_]+):\n' # f: (name of function)
r'^(?P<func>[0-9a-zA-Z_$]+):\n' # f: (name of function)
r'\s+\.fnstart\n' # .fnstart
r'(?P<body>.*?)\n' # (body of the function)
r'.Lfunc_end[0-9]+:', # .Lfunc_end0: or # -- End function
r'(?P<body>.*?)' # (body of the function)
r'^.Lfunc_end[0-9]+:', # .Lfunc_end0: or # -- End function
flags=(re.M | re.S))
ASM_FUNCTION_AARCH64_RE = re.compile(
@ -128,7 +128,8 @@ ASM_FUNCTION_AARCH64_DARWIN_RE = re.compile(
flags=(re.M | re.S))
ASM_FUNCTION_ARM_DARWIN_RE = re.compile(
r'^[ \t]*\.globl[ \t]*_(?P<func>[^ \t])[ \t]*@[ \t]--[ \t]Begin[ \t]function[ \t]"?(?P=func)"?'
r'@[ \t]--[ \t]Begin[ \t]function[ \t](?P<func>[^ \t]+?)\n'
r'^[ \t]*\.globl[ \t]*_(?P=func)[ \t]*'
r'(?P<directives>.*?)'
r'^_(?P=func):\n[ \t]*'
r'(?P<body>.*?)'

View File

@ -496,6 +496,7 @@ class FunctionTestBuilder:
self._func_dict = {}
self._func_order = {}
self._global_var_dict = {}
self._processed_prefixes = set()
for tuple in run_list:
for prefix in tuple[0]:
self._func_dict.update({prefix:dict()})
@ -584,30 +585,43 @@ class FunctionTestBuilder:
scrubbed_body)
if func in self._func_dict[prefix]:
if (self._func_dict[prefix][func] is None or
str(self._func_dict[prefix][func]) != scrubbed_body or
self._func_dict[prefix][func].args_and_sig != args_and_sig or
self._func_dict[prefix][func].attrs != attrs):
if (self._func_dict[prefix][func] is not None and
self._func_dict[prefix][func].is_same_except_arg_names(
(str(self._func_dict[prefix][func]) != scrubbed_body or
self._func_dict[prefix][func].args_and_sig != args_and_sig or
self._func_dict[prefix][func].attrs != attrs)):
if self._func_dict[prefix][func].is_same_except_arg_names(
scrubbed_extra,
args_and_sig,
attrs,
is_backend)):
is_backend):
self._func_dict[prefix][func].scrub = scrubbed_extra
self._func_dict[prefix][func].args_and_sig = args_and_sig
continue
else:
# This means a previous RUN line produced a body for this function
# that is different from the one produced by this current RUN line,
# so the body can't be common accross RUN lines. We use None to
# indicate that.
self._func_dict[prefix][func] = None
continue
else:
if prefix not in self._processed_prefixes:
self._func_dict[prefix][func] = function_body(
scrubbed_body, scrubbed_extra, args_and_sig, attrs, func_name_separator)
scrubbed_body, scrubbed_extra, args_and_sig, attrs,
func_name_separator)
self._func_order[prefix].append(func)
else:
# An earlier RUN line used this check prefixes but didn't produce
# a body for this function. This happens in Clang tests that use
# preprocesser directives to exclude individual functions from some
# RUN lines.
self._func_dict[prefix][func] = None
def processed_prefixes(self, prefixes):
"""
Mark a set of prefixes as having had at least one applicable RUN line fully
processed. This is used to filter out function bodies that don't have
outputs for all RUN lines.
"""
self._processed_prefixes.update(prefixes)
def get_failed_prefixes(self):
# This returns the list of those prefixes that failed to match any function,

View File

@ -124,6 +124,8 @@ def main():
common.warn('Don\'t know how to deal with this output')
continue
builder.processed_prefixes(prefixes)
func_dict = builder.finish_and_get_func_dict()
is_in_function = False
is_in_function_start = False

View File

@ -214,6 +214,7 @@ def get_function_body(builder, args, filename, clang_args, extra_commands,
builder.process_run_line(
common.OPT_FUNCTION_RE, common.scrub_body, raw_tool_output,
prefixes, False)
builder.processed_prefixes(prefixes)
else:
print('The clang command line should include -emit-llvm as asm tests '
'are discouraged in Clang testsuite.', file=sys.stderr)

View File

@ -137,6 +137,7 @@ def main():
scrubber, function_re = output_type.get_run_handler(triple)
builder.process_run_line(function_re, scrubber, raw_tool_output, prefixes, True)
builder.processed_prefixes(prefixes)
func_dict = builder.finish_and_get_func_dict()
global_vars_seen_dict = {}

View File

@ -120,6 +120,7 @@ def main():
verbose=ti.args.verbose)
builder.process_run_line(common.OPT_FUNCTION_RE, common.scrub_body,
raw_tool_output, prefixes, False)
builder.processed_prefixes(prefixes)
func_dict = builder.finish_and_get_func_dict()
is_in_function = False