[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