Replace func name with regex for update test scripts
authorGiorgis Georgakoudis <georgakoudis1@llnl.gov>
Sat, 20 Feb 2021 04:22:14 +0000 (20:22 -0800)
committerGiorgis Georgakoudis <georgakoudis1@llnl.gov>
Sat, 13 Mar 2021 01:37:09 +0000 (17:37 -0800)
The patch adds an argument to update test scripts, such as update_cc_test_checks, for replacing a function name matching a regex. This functionality is needed to match generated function signatures that include file hashes. Example:

The function signature for the following function:

`__omp_offloading_50_b84c41e__Z9ftemplateIiET_i_l30_worker`

with `--replace-function-regex "__omp_offloading_[0-9]+_[a-z0-9]+_(.*)"` will become:

`CHECK-LABEL: @{{__omp_offloading_[0-9]+_[a-z0-9]+__Z9ftemplateIiET_i_l30_worker}}(`

Reviewed By: jdoerfert

Differential Revision: https://reviews.llvm.org/D97107

clang/test/utils/update_cc_test_checks/Inputs/generated-funcs-regex.c [new file with mode: 0644]
clang/test/utils/update_cc_test_checks/Inputs/generated-funcs-regex.c.expected [new file with mode: 0644]
clang/test/utils/update_cc_test_checks/generated-funcs-regex.test [new file with mode: 0644]
llvm/utils/UpdateTestChecks/common.py
llvm/utils/update_llc_test_checks.py

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 (file)
index 0000000..3d51d48
--- /dev/null
@@ -0,0 +1,12 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -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 (file)
index 0000000..838db8d
--- /dev/null
@@ -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_[a-z0-9]+_[a-z0-9]+_(.*)_l[0-9]+"
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -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_[a-z0-9]+_[a-z0-9]+_foo_l[0-9]+}}(i64 [[TMP1]]) #[[ATTR3:[0-9]+]]
+// CHECK-NEXT:    call void (...) @{{__test_offloading_[a-z0-9]+_[a-z0-9]+_bar_l[0-9]+}}()
+// CHECK-NEXT:    ret void
+//
+//
+// CHECK-LABEL: @{{__omp_offloading_[a-z0-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 [[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 (file)
index 0000000..4051d0a
--- /dev/null
@@ -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_[a-z0-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
index 5cf509013f940d058b25b8e064bc7a072cb71248..b3ca33f07428a95391dbf8aa9b418ec211c8c09c 100644 (file)
@@ -30,6 +30,8 @@ def parse_commandline_args(parser):
                        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
@@ -275,6 +277,8 @@ class FunctionTestBuilder:
     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 = {}
     self._global_var_dict = {}
@@ -348,6 +352,30 @@ class FunctionTestBuilder:
               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)
@@ -794,6 +822,8 @@ def get_autogennote_suffix(parser, args):
       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])
index 7cfe85cc10f8a8941121ee99d9e2718d49ec9a6d..614cd073213ae4283875e79379dd5152d1545e0e 100755 (executable)
@@ -109,7 +109,8 @@ def main():
         flags=type('', (object,), {
             'verbose': ti.args.verbose,
             'function_signature': False,
-            'check_attributes': False}),
+            'check_attributes': False,
+            'replace_function_regex': []}),
         scrubber_args=[ti.args])
 
     for prefixes, llc_args, triple_in_cmd, march_in_cmd in run_list: