Skip to content

[UTC] Record TBAA semantics when autogenerating check lines #147670

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

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

antoniofrighetto
Copy link
Contributor

UpdateTestChecks have been updated to take into account TBAA semantics as well, when emitting checks. This is achieved by parsing TBAA metadata for each tool invocation – whose tool is identified by their prefixes –, and maintaining a global dict of prefixes, TBAA nodes.

UpdateTestChecks have been updated to take into account TBAA
semantics as well, when emitting checks. This is achieved by
parsing TBAA metadata for each tool invocation – whose tool
is identified by their prefixes –, and maintaining a global
dict of prefixes, TBAA nodes.
@llvmbot
Copy link
Member

llvmbot commented Jul 9, 2025

@llvm/pr-subscribers-testing-tools

Author: Antonio Frighetto (antoniofrighetto)

Changes

UpdateTestChecks have been updated to take into account TBAA semantics as well, when emitting checks. This is achieved by parsing TBAA metadata for each tool invocation – whose tool is identified by their prefixes –, and maintaining a global dict of prefixes, TBAA nodes.


Patch is 21.94 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/147670.diff

6 Files Affected:

  • (added) llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/tbaa-semantics-checks.ll (+84)
  • (added) llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/tbaa-semantics-checks.ll.expected (+156)
  • (added) llvm/test/tools/UpdateTestChecks/update_test_checks/tbaa-semantics-checks.test (+7)
  • (modified) llvm/utils/UpdateTestChecks/common.py (+88-3)
  • (modified) llvm/utils/update_cc_test_checks.py (+10)
  • (modified) llvm/utils/update_test_checks.py (+10)
diff --git a/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/tbaa-semantics-checks.ll b/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/tbaa-semantics-checks.ll
new file mode 100644
index 0000000000000..9d6059602bdb3
--- /dev/null
+++ b/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/tbaa-semantics-checks.ll
@@ -0,0 +1,84 @@
+; RUN: opt < %s -S | FileCheck %s
+
+define void @store_unsignedptr(ptr %ptr) {
+entry:
+  store ptr null, ptr %ptr, align 8, !tbaa !0
+  ret void
+}
+
+define void @store_char(ptr %ptr) {
+entry:
+  store i8 0, ptr %ptr, align 1, !tbaa !5
+  ret void
+}
+
+define float @ptr_to_float(ptr %ptr) {
+entry:
+  store float 0.000000e+00, ptr %ptr, align 4, !tbaa !6
+  call void @opaque(ptr %ptr)
+  %val = load float, ptr %ptr, align 4, !tbaa !6
+  ret float %val
+}
+
+define i64 @ptr_to_longlong(ptr %ptr) {
+entry:
+  %val = load i64, ptr %ptr, align 8, !tbaa !8
+  store i64 0, ptr %ptr, align 8, !tbaa !8
+  ret i64 %val
+}
+
+; struct STRUCT1 {
+;   int x;
+;   int y;
+; };
+
+define void @store_struct1ptr(ptr %ptr) {
+entry:
+  ; *(struct STRUCT1 **)ptr = 0;
+  store ptr null, ptr %ptr, align 8, !tbaa !10
+  ret void
+}
+
+; struct STRUCT2 {
+;   struct STRUCT1 *s;
+; };
+
+define void @store_struct2(ptr %ptr) {
+entry:
+  ; ptr->s = 0;
+  store ptr null, ptr %ptr, align 8, !tbaa !12
+  ret void
+}
+
+define double @access_matrix(ptr %ptr) {
+entry:
+  %alloca.ptr = alloca ptr, align 8
+  store ptr %ptr, ptr %alloca.ptr, align 8, !tbaa !14
+  %ptr.idx = load ptr, ptr %alloca.ptr, align 8, !tbaa !14
+  %add.ptr = getelementptr inbounds ptr, ptr %ptr.idx, i64 4
+  %ptr.idx.1 = load ptr, ptr %add.ptr, align 8, !tbaa !16
+  %add.ptr1 = getelementptr inbounds [6 x double], ptr %ptr.idx.1, i64 6
+  %ptr.idx.2 = load <6 x double>, ptr %add.ptr1, align 8, !tbaa !5
+  %matrixext = extractelement <6 x double> %ptr.idx.2, i64 5
+  ret double %matrixext
+}
+
+declare void @opaque(ptr)
+
+!0 = !{!1, !1, i64 0}
+!1 = !{!"p1 int", !2, i64 0}
+!2 = !{!"any pointer", !3, i64 0}
+!3 = !{!"omnipotent char", !4, i64 0}
+!4 = !{!"Simple C/C++ TBAA"}
+!5 = !{!3, !3, i64 0}
+!6 = !{!7, !7, i64 0}
+!7 = !{!"float", !3, i64 0}
+!8 = !{!9, !9, i64 0}
+!9 = !{!"long long", !3, i64 0}
+!10 = !{!11, !11, i64 0}
+!11 = !{!"p1 _ZTS7STRUCT1", !2, i64 0}
+!12 = !{!13, !11, i64 0}
+!13 = !{!"STRUCT2", !11, i64 0}
+!14 = !{!15, !15, i64 0}
+!15 = !{!"any p2 pointer", !2, i64 0}
+!16 = !{!2, !2, i64 0}
diff --git a/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/tbaa-semantics-checks.ll.expected b/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/tbaa-semantics-checks.ll.expected
new file mode 100644
index 0000000000000..39d573208e7b6
--- /dev/null
+++ b/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/tbaa-semantics-checks.ll.expected
@@ -0,0 +1,156 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt < %s -S | FileCheck %s
+
+define void @store_unsignedptr(ptr %ptr) {
+; CHECK-LABEL: define void @store_unsignedptr(
+; CHECK-SAME: ptr [[PTR:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    store ptr null, ptr [[PTR]], align 8, !tbaa [[INTPTR_TBAA0:![0-9]+]]
+; CHECK-NEXT:    ret void
+;
+entry:
+  store ptr null, ptr %ptr, align 8, !tbaa !0
+  ret void
+}
+
+define void @store_char(ptr %ptr) {
+; CHECK-LABEL: define void @store_char(
+; CHECK-SAME: ptr [[PTR:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    store i8 0, ptr [[PTR]], align 1, !tbaa [[CHAR_TBAA5:![0-9]+]]
+; CHECK-NEXT:    ret void
+;
+entry:
+  store i8 0, ptr %ptr, align 1, !tbaa !5
+  ret void
+}
+
+define float @ptr_to_float(ptr %ptr) {
+; CHECK-LABEL: define float @ptr_to_float(
+; CHECK-SAME: ptr [[PTR:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    store float 0.000000e+00, ptr [[PTR]], align 4, !tbaa [[FLOAT_TBAA6:![0-9]+]]
+; CHECK-NEXT:    call void @opaque(ptr [[PTR]])
+; CHECK-NEXT:    [[VAL:%.*]] = load float, ptr [[PTR]], align 4, !tbaa [[FLOAT_TBAA6]]
+; CHECK-NEXT:    ret float [[VAL]]
+;
+entry:
+  store float 0.000000e+00, ptr %ptr, align 4, !tbaa !6
+  call void @opaque(ptr %ptr)
+  %val = load float, ptr %ptr, align 4, !tbaa !6
+  ret float %val
+}
+
+define i64 @ptr_to_longlong(ptr %ptr) {
+; CHECK-LABEL: define i64 @ptr_to_longlong(
+; CHECK-SAME: ptr [[PTR:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[VAL:%.*]] = load i64, ptr [[PTR]], align 8, !tbaa [[LONG_LONG_TBAA8:![0-9]+]]
+; CHECK-NEXT:    store i64 0, ptr [[PTR]], align 8, !tbaa [[LONG_LONG_TBAA8]]
+; CHECK-NEXT:    ret i64 [[VAL]]
+;
+entry:
+  %val = load i64, ptr %ptr, align 8, !tbaa !8
+  store i64 0, ptr %ptr, align 8, !tbaa !8
+  ret i64 %val
+}
+
+; struct STRUCT1 {
+;   int x;
+;   int y;
+; };
+
+define void @store_struct1ptr(ptr %ptr) {
+; CHECK-LABEL: define void @store_struct1ptr(
+; CHECK-SAME: ptr [[PTR:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    store ptr null, ptr [[PTR]], align 8, !tbaa [[_ZTS7STRUCT1PTR_TBAA10:![0-9]+]]
+; CHECK-NEXT:    ret void
+;
+entry:
+  ; *(struct STRUCT1 **)ptr = 0;
+  store ptr null, ptr %ptr, align 8, !tbaa !10
+  ret void
+}
+
+; struct STRUCT2 {
+;   struct STRUCT1 *s;
+; };
+
+define void @store_struct2(ptr %ptr) {
+; CHECK-LABEL: define void @store_struct2(
+; CHECK-SAME: ptr [[PTR:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    store ptr null, ptr [[PTR]], align 8, !tbaa [[_ZTS7STRUCT1PTR_TBAA12:![0-9]+]]
+; CHECK-NEXT:    ret void
+;
+entry:
+  ; ptr->s = 0;
+  store ptr null, ptr %ptr, align 8, !tbaa !12
+  ret void
+}
+
+define double @access_matrix(ptr %ptr) {
+; CHECK-LABEL: define double @access_matrix(
+; CHECK-SAME: ptr [[PTR:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[ALLOCA_PTR:%.*]] = alloca ptr, align 8
+; CHECK-NEXT:    store ptr [[PTR]], ptr [[ALLOCA_PTR]], align 8, !tbaa [[ANYPTR_TBAA14:![0-9]+]]
+; CHECK-NEXT:    [[PTR_IDX:%.*]] = load ptr, ptr [[ALLOCA_PTR]], align 8, !tbaa [[ANYPTR_TBAA14]]
+; CHECK-NEXT:    [[ADD_PTR:%.*]] = getelementptr inbounds ptr, ptr [[PTR_IDX]], i64 4
+; CHECK-NEXT:    [[PTR_IDX_1:%.*]] = load ptr, ptr [[ADD_PTR]], align 8, !tbaa [[ANYPTR_TBAA16:![0-9]+]]
+; CHECK-NEXT:    [[ADD_PTR1:%.*]] = getelementptr inbounds [6 x double], ptr [[PTR_IDX_1]], i64 6
+; CHECK-NEXT:    [[PTR_IDX_2:%.*]] = load <6 x double>, ptr [[ADD_PTR1]], align 8, !tbaa [[CHAR_TBAA5]]
+; CHECK-NEXT:    [[MATRIXEXT:%.*]] = extractelement <6 x double> [[PTR_IDX_2]], i64 5
+; CHECK-NEXT:    ret double [[MATRIXEXT]]
+;
+entry:
+  %alloca.ptr = alloca ptr, align 8
+  store ptr %ptr, ptr %alloca.ptr, align 8, !tbaa !14
+  %ptr.idx = load ptr, ptr %alloca.ptr, align 8, !tbaa !14
+  %add.ptr = getelementptr inbounds ptr, ptr %ptr.idx, i64 4
+  %ptr.idx.1 = load ptr, ptr %add.ptr, align 8, !tbaa !16
+  %add.ptr1 = getelementptr inbounds [6 x double], ptr %ptr.idx.1, i64 6
+  %ptr.idx.2 = load <6 x double>, ptr %add.ptr1, align 8, !tbaa !5
+  %matrixext = extractelement <6 x double> %ptr.idx.2, i64 5
+  ret double %matrixext
+}
+
+declare void @opaque(ptr)
+
+!0 = !{!1, !1, i64 0}
+!1 = !{!"p1 int", !2, i64 0}
+!2 = !{!"any pointer", !3, i64 0}
+!3 = !{!"omnipotent char", !4, i64 0}
+!4 = !{!"Simple C/C++ TBAA"}
+!5 = !{!3, !3, i64 0}
+!6 = !{!7, !7, i64 0}
+!7 = !{!"float", !3, i64 0}
+!8 = !{!9, !9, i64 0}
+!9 = !{!"long long", !3, i64 0}
+!10 = !{!11, !11, i64 0}
+!11 = !{!"p1 _ZTS7STRUCT1", !2, i64 0}
+!12 = !{!13, !11, i64 0}
+!13 = !{!"STRUCT2", !11, i64 0}
+!14 = !{!15, !15, i64 0}
+!15 = !{!"any p2 pointer", !2, i64 0}
+!16 = !{!2, !2, i64 0}
+;.
+; CHECK: [[INTPTR_TBAA0]] = !{[[META1:![0-9]+]], [[META1]], i64 0}
+; CHECK: [[META1]] = !{!"p1 int", [[META2:![0-9]+]], i64 0}
+; CHECK: [[META2]] = !{!"any pointer", [[META3:![0-9]+]], i64 0}
+; CHECK: [[META3]] = !{!"omnipotent char", [[META4:![0-9]+]], i64 0}
+; CHECK: [[META4]] = !{!"Simple C/C++ TBAA"}
+; CHECK: [[CHAR_TBAA5]] = !{[[META3]], [[META3]], i64 0}
+; CHECK: [[FLOAT_TBAA6]] = !{[[META7:![0-9]+]], [[META7]], i64 0}
+; CHECK: [[META7]] = !{!"float", [[META3]], i64 0}
+; CHECK: [[LONG_LONG_TBAA8]] = !{[[META9:![0-9]+]], [[META9]], i64 0}
+; CHECK: [[META9]] = !{!"long long", [[META3]], i64 0}
+; CHECK: [[_ZTS7STRUCT1PTR_TBAA10]] = !{[[META11:![0-9]+]], [[META11]], i64 0}
+; CHECK: [[META11]] = !{!"p1 _ZTS7STRUCT1", [[META2]], i64 0}
+; CHECK: [[_ZTS7STRUCT1PTR_TBAA12]] = !{[[META13:![0-9]+]], [[META11]], i64 0}
+; CHECK: [[META13]] = !{!"STRUCT2", [[META11]], i64 0}
+; CHECK: [[ANYPTR_TBAA14]] = !{[[META15:![0-9]+]], [[META15]], i64 0}
+; CHECK: [[META15]] = !{!"any p2 pointer", [[META2]], i64 0}
+; CHECK: [[ANYPTR_TBAA16]] = !{[[META2]], [[META2]], i64 0}
+;.
diff --git a/llvm/test/tools/UpdateTestChecks/update_test_checks/tbaa-semantics-checks.test b/llvm/test/tools/UpdateTestChecks/update_test_checks/tbaa-semantics-checks.test
new file mode 100644
index 0000000000000..0849e8940084c
--- /dev/null
+++ b/llvm/test/tools/UpdateTestChecks/update_test_checks/tbaa-semantics-checks.test
@@ -0,0 +1,7 @@
+## Test correct application of TBAA semantics in TBAA check lines.
+# RUN: cp -f %S/Inputs/tbaa-semantics-checks.ll %t.ll && %update_test_checks --version 6 %t.ll
+# RUN: diff -u %t.ll %S/Inputs/tbaa-semantics-checks.ll.expected
+
+## Check that running the script again does not change the result.
+# RUN: %update_test_checks %t.ll
+# RUN: diff -u %t.ll %S/Inputs/tbaa-semantics-checks.ll.expected
diff --git a/llvm/utils/UpdateTestChecks/common.py b/llvm/utils/UpdateTestChecks/common.py
index 178c623e33e0e..4e662c35dd8ff 100644
--- a/llvm/utils/UpdateTestChecks/common.py
+++ b/llvm/utils/UpdateTestChecks/common.py
@@ -28,8 +28,9 @@
 4: --check-globals now has a third option ('smart'). The others are now called
    'none' and 'all'. 'smart' is the default.
 5: Basic block labels are matched by FileCheck expressions
+6: The semantics of TBAA checks has been incorporated in the check lines.
 """
-DEFAULT_VERSION = 5
+DEFAULT_VERSION = 6
 
 
 SUPPORTED_ANALYSES = {
@@ -620,6 +621,9 @@ def invoke_tool(exe, cmd_args, ir, preprocess_cmd=None, verbose=False):
 
 SEPARATOR = "."
 
+METADATA_NODES_RE = re.compile(r'^\s*!(\d+)\s*=\s*!{(.*)}', re.M)
+TBAA_TAGS_RE = re.compile(r'!tbaa\s*!([0-9]+)')
+
 
 def error(msg, test_file=None):
     if test_file:
@@ -685,6 +689,59 @@ def get_globals_name_prefix(raw_tool_output):
     return "_" if ch == "o" or ch == "x" else None
 
 
+def get_tbaa_records(version, raw_output_tools):
+    if version < 6:
+        return {}
+
+    # Retrieve all unique tbaa tags for the given IR.
+    unique_tbaa_tags = {f'!{n}' for n in TBAA_TAGS_RE.findall(raw_output_tools)}
+    if not unique_tbaa_tags:
+        return {}
+
+    # Small dict of metadata ID and its node content as value.
+    md_nodes = {
+        f'!{m.group(1)}': m.group(2)
+        for m in METADATA_NODES_RE.finditer(raw_output_tools)
+    }
+    assert md_nodes, "Shouldn't have TBAA tags without their type descriptors."
+
+    result = {}
+    for tag in unique_tbaa_tags:
+        type_desc = md_nodes.get(tag)
+        assert type_desc, f"Expected type descriptor for node {tag}."
+
+        # We deal with a tag of kind `(BaseTy, AccessTy, Offset)`.
+        access_ty = type_desc.split(',')[1].strip()
+
+        parent_ty = md_nodes.get(access_ty)
+        assert parent_ty, f"Couldn't find metadata for access type {access_ty}."
+
+        ty_name_field = parent_ty.split(',')[0]
+        assert ty_name_field.startswith('!"') and ty_name_field.endswith('"'), \
+            "First operand should be a MDString."
+        ty_name = ty_name_field[2:-1]
+
+        if ty_name.startswith("p"):
+            # Dealing with a pointer here.
+            pointee_ty_name = ty_name.split(maxsplit=1)[1]
+            if pointee_ty_name.startswith("omnipotent"):
+                pointee_ty_name = "char"
+            # TODO: If pointee_ty_name is a C++ name, should it be demangled?
+            tbaa_prefix = f'{pointee_ty_name.replace(" ", "_")}ptr'
+        elif ty_name.startswith("any"):
+            tbaa_prefix = "anyptr"
+        elif ty_name.startswith("omnipotent"):
+            tbaa_prefix = "char"
+        else:
+            tbaa_prefix = ty_name.replace(" ", "_")
+
+        # Record tag node and its semantics (e.g., INT_TBAA, INTPTR_TBAA).
+        tbaa_sema = f'{tbaa_prefix.upper()}_TBAA'
+        result[tag] = tbaa_sema
+
+    return result
+
+
 def apply_filters(line, filters):
     has_filter = False
     for f in filters:
@@ -1775,6 +1832,7 @@ def generalize_check_lines(
     ginfo: GeneralizerInfo,
     vars_seen,
     global_vars_seen,
+    global_tbaa_records = {},
     preserve_names=False,
     original_check_lines=None,
     *,
@@ -1935,14 +1993,25 @@ def escape_braces(match_obj):
                 else:
                     vars_dict = global_vars_seen
 
+                mapped_name = mapping[value.name]
+
+                # We have computed the name mapping. Now, if possible,
+                # substitute the TBAA value name with its semantics.
+                if ginfo.get_version() >= 6:
+                    if value.key == "!" and global_tbaa_records \
+                        and mapped_name.startswith("TBAA") and mapped_name[4:].isdigit():
+                        tbaa_sema = global_tbaa_records.get(value.text)
+                        assert tbaa_sema, f"Shouldn't miss TBAA name for {value.text}?"
+                        mapped_name = f"{tbaa_sema}{mapped_name[4:]}"
+
                 if key in defs:
                     line += vars_dict[key].get_def(
-                        mapping[value.name], value.prefix, value.suffix
+                        mapped_name, value.prefix, value.suffix
                     )
                     defs.remove(key)
                 else:
                     line += vars_dict[key].get_use(
-                        mapping[value.name], value.prefix, value.suffix
+                        mapped_name, value.prefix, value.suffix
                     )
 
             line += line_template
@@ -1968,6 +2037,7 @@ def add_checks(
     ginfo,
     global_vars_seen_dict,
     is_filtered,
+    global_tbaa_records_for_prefixes = {},
     preserve_names=False,
     original_check_lines: Mapping[str, List[str]] = {},
 ):
@@ -2018,6 +2088,10 @@ def add_checks(
                 global_vars_seen_dict[checkprefix] = {}
 
             global_vars_seen_before = [key for key in global_vars_seen.keys()]
+            global_tbaa_records = next(
+                (val for key, val in global_tbaa_records_for_prefixes.items() if checkprefix in key),
+                None
+            )
 
             vars_seen = {}
             printed_prefixes.append(checkprefix)
@@ -2041,6 +2115,7 @@ def add_checks(
                     ginfo,
                     vars_seen,
                     global_vars_seen,
+                    global_tbaa_records,
                     preserve_names,
                     original_check_lines=[],
                     no_meta_details=ginfo.no_meta_details(),
@@ -2155,6 +2230,7 @@ def add_checks(
                     ginfo,
                     vars_seen,
                     global_vars_seen,
+                    global_tbaa_records,
                     preserve_names,
                     original_check_lines=original_check_lines.get(checkprefix),
                 )
@@ -2217,6 +2293,7 @@ def add_ir_checks(
     function_sig,
     ginfo: GeneralizerInfo,
     global_vars_seen_dict,
+    global_tbaa_records_for_prefixes,
     is_filtered,
     original_check_lines={},
 ):
@@ -2241,6 +2318,7 @@ def add_ir_checks(
         ginfo,
         global_vars_seen_dict,
         is_filtered,
+        global_tbaa_records_for_prefixes,
         preserve_names,
         original_check_lines=original_check_lines,
     )
@@ -2627,6 +2705,7 @@ def add_global_checks(
     output_lines,
     ginfo: GeneralizerInfo,
     global_vars_seen_dict,
+    global_tbaa_records_for_prefixes,
     preserve_names,
     is_before_functions,
     global_check_setting,
@@ -2659,6 +2738,11 @@ def add_global_checks(
 
                 check_lines = []
                 global_vars_seen_before = [key for key in global_vars_seen.keys()]
+                global_tbaa_records = next(
+                    (val for key, val in global_tbaa_records_for_prefixes.items() if checkprefix in key),
+                    None
+                )
+
                 lines_w_index = glob_val_dict[checkprefix][nameless_value.check_prefix]
                 lines_w_index = filter_globals_according_to_preference(
                     lines_w_index,
@@ -2682,6 +2766,7 @@ def add_global_checks(
                         ginfo,
                         {},
                         global_vars_seen,
+                        global_tbaa_records,
                         preserve_names,
                         unstable_globals_only=True,
                     )
diff --git a/llvm/utils/update_cc_test_checks.py b/llvm/utils/update_cc_test_checks.py
index 4102ee4ecbd22..97b446d565973 100755
--- a/llvm/utils/update_cc_test_checks.py
+++ b/llvm/utils/update_cc_test_checks.py
@@ -365,6 +365,7 @@ def update_test(ti: common.TestInfo):
         ginfo=ginfo,
     )
 
+    global_tbaa_records_for_prefixes = {}
     for prefixes, args, extra_commands, triple_in_cmd in run_list:
         # Execute non-filechecked runline.
         if not prefixes:
@@ -391,6 +392,10 @@ def update_test(ti: common.TestInfo):
             raw_tool_output,
         )
 
+        # Extract TBAA metadata for later usage in check lines.
+        tbaa_map = common.get_tbaa_records(ti.args.version, raw_tool_output)
+        global_tbaa_records_for_prefixes[tuple(prefixes)] = tbaa_map
+
         # Invoke clang -Xclang -ast-dump=json to get mapping from start lines to
         # mangled names. Forward all clang args for now.
         for k, v in get_line2func_list(
@@ -436,6 +441,7 @@ def check_generator(my_output_lines, prefixes, func):
                 ti.args.function_signature,
                 ginfo,
                 global_vars_seen_dict,
+                global_tbaa_records_for_prefixes,
                 is_filtered=builder.is_filtered(),
             )
 
@@ -448,6 +454,7 @@ def check_generator(my_output_lines, prefixes, func):
                     output_lines,
                     ginfo,
                     global_vars_seen_dict,
+                    global_tbaa_records_for_prefixes,
                     False,
                     True,
                     ti.args.check_globals,
@@ -509,6 +516,7 @@ def check_generator(my_output_lines, prefixes, func):
                                     output_lines,
                                     ginfo,
                                     global_vars_seen_dict,
+                                    global_tbaa_records_for_prefixes,
                                     False,
                                     True,
                                     ti.args.check_globals,
@@ -529,6 +537,7 @@ def check_generator(my_output_lines, prefixes, func):
                                 args.function_signature,
                                 ginfo,
                                 global_vars_seen_dict,
+                                global_tbaa_records_for_prefixes,
                                 is_filtered=builder.is_filtered(),
                             )
                         )
@@ -547,6 +556,7 @@ def check_generator(my_output_lines, prefixes, func):
                 output_lines,
                 ginfo,
                 global_vars_seen_dict,
+                global_tbaa_records_for_prefixes,
                 False,
                 False,
                 ti.args.check_globals,
diff --git a/llvm/utils/update_test_checks.py b/llvm/utils/update_test_checks.py
index ff5bb7d782346..3b562fbc54f78 100755
--- a/llvm/utils/update_test_checks.py
+++ b/llvm/utils/update_test_checks.py
@@ -93,6 +93,7 @@ def update_test(ti: common.TestInfo):
 
     ginfo = common.make_ir_generalizer(ti.args.version, ti.args.check_globals == "none")
     global_vars_seen_dict = {}
+    global_tbaa_records_for_prefixes = {}
     builder = common.FunctionTestBuilder(
         run_list=prefix_list,
         flags=ti.args,
@@ -124,6 +125,10 @@ def update_test(ti: common.TestInfo):
         )
         builder.processed_prefixes(prefixes)
 
+        # Extract TBAA metadata for later usage in check lines.
+        tbaa_map = common.get_tbaa_records(ti.args.version, raw_tool_output)
+        global_tbaa_records_for_prefix...
[truncated]

Copy link

github-actions bot commented Jul 9, 2025

✅ With the latest revision this PR passed the Python code formatter.

Copy link
Contributor

@nikic nikic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like a reasonable feature, but someone with more Python skills should probably review this...

I'd suggest running this over all tests that have tbaa metadata to check whether any of the assertions fire.

@@ -620,6 +621,9 @@ def invoke_tool(exe, cmd_args, ir, preprocess_cmd=None, verbose=False):

SEPARATOR = "."

METADATA_NODES_RE = re.compile(r"^\s*!(\d+)\s*=\s*!{(.*)}", re.M)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
METADATA_NODES_RE = re.compile(r"^\s*!(\d+)\s*=\s*!{(.*)}", re.M)
METADATA_NODES_RE = re.compile(r"^\s*!(\d+)\s*=\s*!\{(.*)\}", re.M)

Kinda surprised this works. Does Pythons regex dialect interpret { literally if not followed by a digit?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It indeed looks like that is allowed to be unescaped since it doesn't form a valid quantifier. Escaping them as best practice.

@antoniofrighetto
Copy link
Contributor Author

I'd suggest running this over all tests that have tbaa metadata to check whether any of the assertions fire.

Assuming this is to be done locally, or should we take the opportunity to update all UTC (possibly just version 5) tests that have TBAA metadata, here or separately?

@nikic
Copy link
Contributor

nikic commented Jul 9, 2025

I'd suggest running this over all tests that have tbaa metadata to check whether any of the assertions fire.

Assuming this is to be done locally, or should we take the opportunity to update all UTC (possibly just version 5) tests that have TBAA metadata, here or separately?

Just locally for now. But updating tests in a followup may also make sense.

@antoniofrighetto
Copy link
Contributor Author

Hit an assert in a few tests that are using the new-struct-path-tbaa option, while testing locally. Relaxed the assert and left a TODO for now, as I couldn't find description of the new TBAA format in the LangRef, and I'm not sure if adoption to the new format is still an open work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants