[llvm] [Verifier] Validate !tbaa.struct metadata (PR #225910)

Abhay Kanhere via llvm-commits llvm-commits at lists.llvm.org
Fri Sep 25 11:53:58 PDT 2026


https://github.com/AbhayKanhere updated https://github.com/llvm/llvm-project/pull/225910

>From ce250b2f4b793b00c44e9bb7188e960e0c20b7f8 Mon Sep 17 00:00:00 2001
From: Abhay Kanhere <a_kanhere at apple.com>
Date: Wed, 23 Sep 2026 12:32:37 -0700
Subject: [PATCH 1/4] [Verifier] Validate !tbaa.struct metadata

Check that !tbaa.struct operands come in (offset, size, tag) triples with
constant offset and size.
---
 llvm/include/llvm/IR/Verifier.h   |  1 +
 llvm/lib/IR/Verifier.cpp          | 23 +++++++++++++++++++++++
 llvm/test/Verifier/tbaa-struct.ll |  8 ++++++--
 3 files changed, 30 insertions(+), 2 deletions(-)

diff --git a/llvm/include/llvm/IR/Verifier.h b/llvm/include/llvm/IR/Verifier.h
index 80e9b9284c9b7..9fa37cfae1974 100644
--- a/llvm/include/llvm/IR/Verifier.h
+++ b/llvm/include/llvm/IR/Verifier.h
@@ -79,6 +79,7 @@ class TBAAVerifier {
   /// Visit an instruction, or a TBAA node itself as part of a metadata, and
   /// return true if it is valid, return false if an invalid TBAA is attached.
   LLVM_ABI bool visitTBAAMetadata(const Instruction *I, const MDNode *MD);
+  LLVM_ABI bool visitTBAAStructMetadata(const Instruction *I, const MDNode *MD);
 };
 
 /// Check a function for errors, useful for use when debugging a
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index 1440b95896474..d9852a34646c7 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -6062,6 +6062,9 @@ void Verifier::visitInstruction(Instruction &I) {
   if (MDNode *TBAA = I.getMetadata(LLVMContext::MD_tbaa))
     TBAAVerifyHelper.visitTBAAMetadata(&I, TBAA);
 
+  if (MDNode *TBAAStruct = I.getMetadata(LLVMContext::MD_tbaa_struct))
+    TBAAVerifyHelper.visitTBAAStructMetadata(&I, TBAAStruct);
+
   if (MDNode *MD = I.getMetadata(LLVMContext::MD_noalias))
     visitAliasScopeListMetadata(MD);
   if (MDNode *MD = I.getMetadata(LLVMContext::MD_alias_scope))
@@ -8348,6 +8351,26 @@ bool TBAAVerifier::visitTBAAMetadata(const Instruction *I, const MDNode *MD) {
   return true;
 }
 
+bool TBAAVerifier::visitTBAAStructMetadata(const Instruction *I,
+                                           const MDNode *MD) {
+  // !tbaa.struct is a list of (offset, size, tag) triples. Offset and size
+  // must be constants; a non-null tag must be a valid access tag.
+  CheckTBAA(MD->getNumOperands() % 3 == 0,
+            "!tbaa.struct operands must come in groups of three", I, MD);
+
+  for (unsigned Idx = 0, E = MD->getNumOperands(); Idx != E; Idx += 3) {
+    CheckTBAA(mdconst::dyn_extract_or_null<ConstantInt>(MD->getOperand(Idx)),
+              "!tbaa.struct field offset must be a constant integer", I, MD);
+    CheckTBAA(
+        mdconst::dyn_extract_or_null<ConstantInt>(MD->getOperand(Idx + 1)),
+        "!tbaa.struct field size must be a constant integer", I, MD);
+    if (auto *Tag = dyn_cast_or_null<MDNode>(MD->getOperand(Idx + 2)))
+      if (!visitTBAAMetadata(I, Tag))
+        return false;
+  }
+  return true;
+}
+
 char VerifierLegacyPass::ID = 0;
 INITIALIZE_PASS(VerifierLegacyPass, "verify", "Module Verifier", false, false)
 
diff --git a/llvm/test/Verifier/tbaa-struct.ll b/llvm/test/Verifier/tbaa-struct.ll
index b8ddc7cee496a..9058daaa398b1 100644
--- a/llvm/test/Verifier/tbaa-struct.ll
+++ b/llvm/test/Verifier/tbaa-struct.ll
@@ -1,6 +1,7 @@
-; RUN: llvm-as < %s 2>&1
+; RUN: not llvm-as -disable-output < %s 2>&1 | FileCheck %s
 
-; FIXME: The verifer should reject the invalid !tbaa.struct nodes below.
+; FIXME: The verifier does not yet reject the overlapping-region (@test_overlapping_regions)
+; or null-tag (@test_tbaa_missing) nodes below.
 
 define void @test_overlapping_regions(ptr %a1) {
   %ld = load i8, ptr %a1, align 1, !tbaa.struct !0
@@ -8,11 +9,13 @@ define void @test_overlapping_regions(ptr %a1) {
 }
 
 define void @test_size_not_integer(ptr %a1) {
+; CHECK-DAG: !tbaa.struct field size must be a constant integer
   store i8 1, ptr %a1, align 1, !tbaa.struct !5
   ret void
 }
 
 define void @test_offset_not_integer(ptr %a1, ptr %a2) {
+; CHECK-DAG: !tbaa.struct field offset must be a constant integer
   tail call void @llvm.memcpy.p0.p0.i64(ptr align 8 %a1, ptr align 8 %a2, i64 16, i1 false), !tbaa.struct !6
   ret void
 }
@@ -23,6 +26,7 @@ define void @test_tbaa_missing(ptr %a1, ptr %a2) {
 }
 
 define void @test_tbaa_invalid(ptr %a1) {
+; CHECK-DAG: Old-style TBAA is no longer allowed
   store i8 1, ptr %a1, align 1, !tbaa.struct !8
   ret void
 }

>From 578b280656c1832d59358c47986ff24a8804d1eb Mon Sep 17 00:00:00 2001
From: Abhay Kanhere <a_kanhere at apple.com>
Date: Wed, 23 Sep 2026 15:15:57 -0700
Subject: [PATCH 2/4] [Verifier] Require ascending !tbaa.struct field offsets

Check that !tbaa.struct field offsets are non-decreasing.
---
 llvm/lib/IR/Verifier.cpp          | 21 +++++++++++++++++----
 llvm/test/Verifier/tbaa-struct.ll |  7 +++++++
 2 files changed, 24 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index d9852a34646c7..d0014082f3b0c 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -8353,20 +8353,33 @@ bool TBAAVerifier::visitTBAAMetadata(const Instruction *I, const MDNode *MD) {
 
 bool TBAAVerifier::visitTBAAStructMetadata(const Instruction *I,
                                            const MDNode *MD) {
-  // !tbaa.struct is a list of (offset, size, tag) triples. Offset and size
-  // must be constants; a non-null tag must be a valid access tag.
+  // !tbaa.struct is a list of (offset, size, tag) triples with ascending
+  // offsets. Offset and size must be constants; a non-null tag must be a
+  // valid access tag.
   CheckTBAA(MD->getNumOperands() % 3 == 0,
             "!tbaa.struct operands must come in groups of three", I, MD);
 
+  std::optional<APInt> PrevOffset;
   for (unsigned Idx = 0, E = MD->getNumOperands(); Idx != E; Idx += 3) {
-    CheckTBAA(mdconst::dyn_extract_or_null<ConstantInt>(MD->getOperand(Idx)),
-              "!tbaa.struct field offset must be a constant integer", I, MD);
+    auto *OffsetCI =
+        mdconst::dyn_extract_or_null<ConstantInt>(MD->getOperand(Idx));
+    CheckTBAA(OffsetCI, "!tbaa.struct field offset must be a constant integer",
+              I, MD);
     CheckTBAA(
         mdconst::dyn_extract_or_null<ConstantInt>(MD->getOperand(Idx + 1)),
         "!tbaa.struct field size must be a constant integer", I, MD);
     if (auto *Tag = dyn_cast_or_null<MDNode>(MD->getOperand(Idx + 2)))
       if (!visitTBAAMetadata(I, Tag))
         return false;
+
+    const APInt &Offset = OffsetCI->getValue();
+    if (PrevOffset) {
+      unsigned Width =
+          std::max(PrevOffset->getBitWidth(), Offset.getBitWidth());
+      CheckTBAA(PrevOffset->zext(Width).ule(Offset.zext(Width)),
+                "!tbaa.struct field offsets must be non-decreasing", I, MD);
+    }
+    PrevOffset = Offset;
   }
   return true;
 }
diff --git a/llvm/test/Verifier/tbaa-struct.ll b/llvm/test/Verifier/tbaa-struct.ll
index 9058daaa398b1..d1bd3ebb1e546 100644
--- a/llvm/test/Verifier/tbaa-struct.ll
+++ b/llvm/test/Verifier/tbaa-struct.ll
@@ -31,6 +31,12 @@ define void @test_tbaa_invalid(ptr %a1) {
   ret void
 }
 
+define void @test_offsets_not_increasing(ptr %a1) {
+; CHECK-DAG: !tbaa.struct field offsets must be non-decreasing
+  store i8 1, ptr %a1, align 1, !tbaa.struct !9
+  ret void
+}
+
 declare void @llvm.memcpy.p0.p0.i64(ptr nocapture, ptr nocapture, i64, i1) nounwind
 
 !0 = !{i64 0, i64 4, !1, i64 1, i64 4, !1}
@@ -42,3 +48,4 @@ declare void @llvm.memcpy.p0.p0.i64(ptr nocapture, ptr nocapture, i64, i1) nounw
 !6 = !{!2, i64 0, !1}
 !7 = !{i64 0, i64 4, null}
 !8 = !{i64 0, i64 4, !2}
+!9 = !{i64 4, i64 4, !1, i64 0, i64 4, !1}

>From 8750e1ae3a9af284f30b987dc3f14b59ad8ba1e7 Mon Sep 17 00:00:00 2001
From: Abhay Kanhere <a_kanhere at apple.com>
Date: Wed, 23 Sep 2026 15:58:29 -0700
Subject: [PATCH 3/4] [Verifier] Make tbaa-struct test robust to !tbaa.struct
 auto-upgrade

Point test_tbaa_invalid at a struct-path-shaped field tag with a
non-constant offset. The TBAA auto-upgrade leaves such a tag unchanged
(operand 0 is already an MDNode), so the verifier still rejects it once
!tbaa.struct field-tag auto-upgrade lands.
---
 llvm/test/Verifier/tbaa-struct.ll | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/llvm/test/Verifier/tbaa-struct.ll b/llvm/test/Verifier/tbaa-struct.ll
index d1bd3ebb1e546..db946c271f830 100644
--- a/llvm/test/Verifier/tbaa-struct.ll
+++ b/llvm/test/Verifier/tbaa-struct.ll
@@ -26,7 +26,7 @@ define void @test_tbaa_missing(ptr %a1, ptr %a2) {
 }
 
 define void @test_tbaa_invalid(ptr %a1) {
-; CHECK-DAG: Old-style TBAA is no longer allowed
+; CHECK-DAG: Offset must be constant integer
   store i8 1, ptr %a1, align 1, !tbaa.struct !8
   ret void
 }
@@ -47,5 +47,8 @@ declare void @llvm.memcpy.p0.p0.i64(ptr nocapture, ptr nocapture, i64, i1) nounw
 !5 = !{i64 0, !2, !1}
 !6 = !{!2, i64 0, !1}
 !7 = !{i64 0, i64 4, null}
-!8 = !{i64 0, i64 4, !2}
+!8 = !{i64 0, i64 4, !10}
 !9 = !{i64 4, i64 4, !1, i64 0, i64 4, !1}
+; A struct-path-shaped access tag with a non-constant offset. Auto-upgrade
+; leaves it unchanged (operand 0 is already an MDNode), so it stays rejected.
+!10 = !{!2, !2, !3}

>From e2a85e049ed6da2d841a74c81b0c2b06335a5848 Mon Sep 17 00:00:00 2001
From: Abhay Kanhere <a_kanhere at apple.com>
Date: Wed, 23 Sep 2026 22:13:52 -0700
Subject: [PATCH 4/4] [AtomicExpand][test] Fix malformed !tbaa.struct in
 expand-atomic-i16

The !tbaa.struct had five operands (not a multiple of three). Make it a
valid two-field node so it satisfies the new !tbaa.struct verifier check.
---
 llvm/test/Transforms/AtomicExpand/AMDGPU/expand-atomic-i16.ll | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/test/Transforms/AtomicExpand/AMDGPU/expand-atomic-i16.ll b/llvm/test/Transforms/AtomicExpand/AMDGPU/expand-atomic-i16.ll
index 8fa9b5419ec79..e77d2294b0cbf 100644
--- a/llvm/test/Transforms/AtomicExpand/AMDGPU/expand-atomic-i16.ll
+++ b/llvm/test/Transforms/AtomicExpand/AMDGPU/expand-atomic-i16.ll
@@ -2074,7 +2074,7 @@ define i16 @test_atomicrmw_usub_sat_i16_flat_agent_align4(ptr %ptr, i16 %value)
 !2 = !{!3}
 !3 = distinct !{!3, !4}
 !4 = distinct !{!4}
-!5 = !{i64 0, i64 4, !1, i64 8, i64 4}
+!5 = !{i64 0, i64 4, !6, i64 4, i64 4, !6}
 !6 = !{!7, !7, i64 0}
 !7 = !{!"omnipotent char", !8, i64 0}
 !8 = !{!"Simple C/C++ TBAA"}



More information about the llvm-commits mailing list