diff --git a/clang/test/utils/update_cc_test_checks/Inputs/generated-funcs-regex.c b/clang/test/utils/update_cc_test_checks/Inputs/generated-funcs-regex.c new file mode 100644 --- /dev/null +++ b/clang/test/utils/update_cc_test_checks/Inputs/generated-funcs-regex.c @@ -0,0 +1,12 @@ +// RUN: %clang_cc1 -fopenmp %s -emit-llvm -o - | FileCheck %s + +void __test_offloading_42_abcdef_bar_l123(); +void use(int); + +void foo(int a) +{ + #pragma omp target + use(a); + + __test_offloading_42_abcdef_bar_l123(); +} diff --git a/clang/test/utils/update_cc_test_checks/Inputs/generated-funcs-regex.c.expected b/clang/test/utils/update_cc_test_checks/Inputs/generated-funcs-regex.c.expected new file mode 100644 --- /dev/null +++ b/clang/test/utils/update_cc_test_checks/Inputs/generated-funcs-regex.c.expected @@ -0,0 +1,36 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --include-generated-funcs --replace-function-regex "__([a-z]+)_offloading_[0-9]+_[a-z0-9]+_(.*)_l[0-9]+" +// RUN: %clang_cc1 -fopenmp %s -emit-llvm -o - | FileCheck %s + +void __test_offloading_42_abcdef_bar_l123(); +void use(int); + +void foo(int a) +{ + #pragma omp target + use(a); + + __test_offloading_42_abcdef_bar_l123(); +} +// CHECK-LABEL: @foo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[A_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[A_CASTED:%.*]] = alloca i64, align 8 +// CHECK-NEXT: store i32 [[A:%.*]], i32* [[A_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, i32* [[A_ADDR]], align 4 +// CHECK-NEXT: [[CONV:%.*]] = bitcast i64* [[A_CASTED]] to i32* +// CHECK-NEXT: store i32 [[TMP0]], i32* [[CONV]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load i64, i64* [[A_CASTED]], align 8 +// CHECK-NEXT: call void @{{__omp_offloading_[0-9]+_[a-z0-9]+_foo_l[0-9]+}}(i64 [[TMP1]]) [[ATTR3:#.*]] +// CHECK-NEXT: call void bitcast (void (...)* @{{__test_offloading_[0-9]+_[a-z0-9]+_bar_l[0-9]+}} to void ()*)() +// CHECK-NEXT: ret void +// +// +// CHECK-LABEL: @{{__omp_offloading_[0-9]+_[a-z0-9]+_foo_l[0-9]+}}( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[A_ADDR:%.*]] = alloca i64, align 8 +// CHECK-NEXT: store i64 [[A:%.*]], i64* [[A_ADDR]], align 8 +// CHECK-NEXT: [[CONV:%.*]] = bitcast i64* [[A_ADDR]] to i32* +// CHECK-NEXT: [[TMP0:%.*]] = load i32, i32* [[CONV]], align 8 +// CHECK-NEXT: call void @use(i32 signext [[TMP0]]) +// CHECK-NEXT: ret void +// diff --git a/clang/test/utils/update_cc_test_checks/generated-funcs-regex.test b/clang/test/utils/update_cc_test_checks/generated-funcs-regex.test new file mode 100644 --- /dev/null +++ b/clang/test/utils/update_cc_test_checks/generated-funcs-regex.test @@ -0,0 +1,9 @@ +## Test that CHECK lines are generated for clang-generated functions replaced +## by regex + +## RUN: cp %S/Inputs/generated-funcs-regex.c %t-generated-funcs-regex.c && %update_cc_test_checks --include-generated-funcs --replace-function-regex "__([a-z]+)_offloading_[0-9]+_[a-z0-9]+_(.*)_l[0-9]+" -- %t-generated-funcs-regex.c +# RUN: diff -u %S/Inputs/generated-funcs-regex.c.expected %t-generated-funcs-regex.c + +## Check that re-running update_cc_test_checks doesn't change the output +# RUN: %update_cc_test_checks %t-generated-funcs-regex.c +# RUN: diff -u %S/Inputs/generated-funcs-regex.c.expected %t-generated-funcs-regex.c diff --git a/llvm/utils/UpdateTestChecks/common.py b/llvm/utils/UpdateTestChecks/common.py --- a/llvm/utils/UpdateTestChecks/common.py +++ b/llvm/utils/UpdateTestChecks/common.py @@ -30,6 +30,8 @@ help='Activate CHECK line generation from this point forward') parser.add_argument('--disable', action='store_false', dest='enabled', help='Deactivate CHECK line generation from this point forward') + parser.add_argument('--replace-function-regex', nargs='+', default=[], + help='List of regular expressions to replace matching function names') args = parser.parse_args() global _verbose _verbose = args.verbose @@ -264,6 +266,8 @@ self._record_args = flags.function_signature self._check_attributes = flags.check_attributes self._scrubber_args = scrubber_args + # Strip double-quotes if input was read by UTC_ARGS + self._replace_function_regex = list(map(lambda x: x.strip('"'), flags.replace_function_regex)) self._func_dict = {} self._func_order = {} for tuple in run_list: @@ -331,6 +335,30 @@ self._func_dict[prefix][func] = None continue + # Replace function names matching the regex. + for regex in self._replace_function_regex: + # Pattern that matches capture groups in the regex in leftmost order. + group_regex = re.compile('\(.*?\)') + # Replace function name with regex. + match = re.match(regex, func) + if match: + func_repl = regex + # Replace any capture groups with their matched strings. + for g in match.groups(): + func_repl = group_regex.sub(g, func_repl, count=1) + func = '{{' + func_repl + '}}' + + # Replace all calls to regex matching functions. + matches = re.finditer(regex, scrubbed_body) + for match in matches: + func_repl = regex + # Replace any capture groups with their matched strings. + for g in match.groups(): + func_repl = group_regex.sub(g, func_repl, count=1) + # Substitute function call names that match the regex with the same + # capture groups set. + scrubbed_body = re.sub(func_repl, '{{' + func_repl + '}}', scrubbed_body) + self._func_dict[prefix][func] = function_body( scrubbed_body, scrubbed_extra, args_and_sig, attrs) self._func_order[prefix].append(func) @@ -633,6 +661,8 @@ continue # Don't add default values autogenerated_note_args += action.option_strings[0] + ' ' if action.const is None: # action takes a parameter + if action.nargs == '+': + value = ' '.join(map(lambda v: '"' + v.strip('"') + '"', value)) autogenerated_note_args += '%s ' % value if autogenerated_note_args: autogenerated_note_args = ' %s %s' % (UTC_ARGS_KEY, autogenerated_note_args[:-1])