[llvm] [TBAA] Recover !tbaa for a memcpy of struct with same type fields (PR #214116)
Abhay Kanhere via llvm-commits
llvm-commits at lists.llvm.org
Mon Sep 21 16:47:18 PDT 2026
https://github.com/AbhayKanhere updated https://github.com/llvm/llvm-project/pull/214116
>From 983c632b059abd49dce79784aa40c3c34927bf73 Mon Sep 17 00:00:00 2001
From: Abhay Kanhere <a_kanhere at apple.com>
Date: Tue, 4 Aug 2026 18:31:48 -0700
Subject: [PATCH 1/8] [TBAA] Recover !tbaa for a memcpy of struct
When InstCombine widens a small same-typed aggregate copy into an integer
load/store, it derives no !tbaa, and !tbaa.struct is nulled out, so
the load/store are left untyped. An untyped access may-alias every typed
access and blocks optimization:
struct Coord { int x, y; };
void copy_if_inbounds(Coord *dst, const Coord *src, const long *bound, int n) {
for (int i = 0; i < n; ++i)
if (i < *bound)
dst[i] = *src; // {int,int} copy -> memcpy + !tbaa.struct
}
compare this to field-by-field copy, which has typed field access
void copy_fields_if_inbounds(Coord *dst, const Coord *src, const long *bound, int n) {
for (int i = 0; i < n; ++i)
if (i < *bound) { dst[i].x = src->x; dst[i].y = src->y; } // per-field, typed
}
---
llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp | 24 +++++++++++++
.../InstCombine/struct-assign-tbaa.ll | 36 +++++++++++++++++--
2 files changed, 58 insertions(+), 2 deletions(-)
diff --git a/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp b/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp
index dbe4ccac7801d..2c52153677e26 100644
--- a/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp
+++ b/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp
@@ -819,6 +819,30 @@ AAMDNodes AAMDNodes::adjustForAccess(unsigned AccessSize) {
M->getOperand(2) && isa<MDNode>(M->getOperand(2)))
New.TBAA = cast<MDNode>(M->getOperand(2));
+ // The access may cover several !tbaa.struct fields (e.g. a {int, int} copy
+ // widened to an i64 load/store). If those fields share a single tag and tile
+ // [0, AccessSize) with no gaps, that tag still describes the whole access.
+ if (!New.TBAA && M) {
+ MDNode *CommonTag = nullptr;
+ uint64_t Offset = 0;
+ for (size_t I = 0, E = M->getNumOperands(); I < E; I += 3) {
+ ConstantInt *FieldOffset =
+ mdconst::extract<ConstantInt>(M->getOperand(I));
+ ConstantInt *FieldSize =
+ mdconst::extract<ConstantInt>(M->getOperand(I + 1));
+ MDNode *FieldTag = dyn_cast_or_null<MDNode>(M->getOperand(I + 2));
+ if (!FieldTag || FieldOffset->getZExtValue() != Offset ||
+ (CommonTag && FieldTag != CommonTag))
+ break;
+ CommonTag = FieldTag;
+ Offset += FieldSize->getZExtValue();
+ if (Offset >= AccessSize)
+ break;
+ }
+ if (Offset == AccessSize)
+ New.TBAA = CommonTag;
+ }
+
New.TBAAStruct = nullptr;
return New;
}
diff --git a/llvm/test/Transforms/InstCombine/struct-assign-tbaa.ll b/llvm/test/Transforms/InstCombine/struct-assign-tbaa.ll
index fd4389ab0f8f2..a33c07171514e 100644
--- a/llvm/test/Transforms/InstCombine/struct-assign-tbaa.ll
+++ b/llvm/test/Transforms/InstCombine/struct-assign-tbaa.ll
@@ -40,8 +40,8 @@ define void @test3_multiple_fields(ptr nocapture %a, ptr nocapture %b) {
; CHECK-LABEL: define void @test3_multiple_fields(
; CHECK-SAME: ptr captures(none) [[A:%.*]], ptr captures(none) [[B:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*:]]
-; CHECK-NEXT: [[TMP0:%.*]] = load i64, ptr [[B]], align 4
-; CHECK-NEXT: store i64 [[TMP0]], ptr [[A]], align 4
+; CHECK-NEXT: [[TMP0:%.*]] = load i64, ptr [[B]], align 4, !tbaa [[FLOAT_TBAA0]]
+; CHECK-NEXT: store i64 [[TMP0]], ptr [[A]], align 4, !tbaa [[FLOAT_TBAA0]]
; CHECK-NEXT: ret void
;
entry:
@@ -75,6 +75,32 @@ entry:
ret void
}
+define void @test6_int_int(ptr nocapture %a, ptr nocapture %b) {
+; CHECK-LABEL: define void @test6_int_int(
+; CHECK-SAME: ptr captures(none) [[A:%.*]], ptr captures(none) [[B:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[TMP0:%.*]] = load i64, ptr [[B]], align 4, !tbaa [[INT_TBAA3:![0-9]+]]
+; CHECK-NEXT: store i64 [[TMP0]], ptr [[A]], align 4, !tbaa [[INT_TBAA3]]
+; CHECK-NEXT: ret void
+;
+entry:
+ tail call void @llvm.memcpy.p0.p0.i64(ptr align 4 %a, ptr align 4 %b, i64 8, i1 false), !tbaa.struct !8
+ ret void
+}
+
+define void @test7_mixed_type(ptr nocapture %a, ptr nocapture %b) {
+; CHECK-LABEL: define void @test7_mixed_type(
+; CHECK-SAME: ptr captures(none) [[A:%.*]], ptr captures(none) [[B:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[TMP0:%.*]] = load i64, ptr [[B]], align 4
+; CHECK-NEXT: store i64 [[TMP0]], ptr [[A]], align 4
+; CHECK-NEXT: ret void
+;
+entry:
+ tail call void @llvm.memcpy.p0.p0.i64(ptr align 4 %a, ptr align 4 %b, i64 8, i1 false), !tbaa.struct !11
+ ret void
+}
+
!0 = !{!"Simple C/C++ TBAA"}
!1 = !{!"omnipotent char", !0}
!2 = !{!5, !5, i64 0}
@@ -83,6 +109,10 @@ entry:
!5 = !{!"float", !0}
!6 = !{i64 0, i64 4, !2, i64 4, i64 4, !2}
!7 = !{i64 0, i64 2, !2, i64 4, i64 6, !2}
+!8 = !{i64 0, i64 4, !9, i64 4, i64 4, !9}
+!9 = !{!10, !10, i64 0}
+!10 = !{!"int", !0}
+!11 = !{i64 0, i64 4, !9, i64 4, i64 4, !2}
;.
; CHECK: attributes #[[ATTR0:[0-9]+]] = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
@@ -90,4 +120,6 @@ entry:
; CHECK: [[FLOAT_TBAA0]] = !{[[META1:![0-9]+]], [[META1]], i64 0}
; CHECK: [[META1]] = !{!"float", [[META2:![0-9]+]]}
; CHECK: [[META2]] = !{!"Simple C/C++ TBAA"}
+; CHECK: [[INT_TBAA3]] = !{[[META4:![0-9]+]], [[META4]], i64 0}
+; CHECK: [[META4]] = !{!"int", [[META2]]}
;.
>From 411e1c87c5aae23c712777c46f3a67b56e8b9444 Mon Sep 17 00:00:00 2001
From: Abhay Kanhere <a_kanhere at apple.com>
Date: Wed, 5 Aug 2026 09:40:23 -0700
Subject: [PATCH 2/8] [Analysis] updated code formatting.
update to code formatting.
---
llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp | 40 ++++++++++----------
1 file changed, 19 insertions(+), 21 deletions(-)
diff --git a/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp b/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp
index 2c52153677e26..90117cf78ec05 100644
--- a/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp
+++ b/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp
@@ -822,28 +822,26 @@ AAMDNodes AAMDNodes::adjustForAccess(unsigned AccessSize) {
// The access may cover several !tbaa.struct fields (e.g. a {int, int} copy
// widened to an i64 load/store). If those fields share a single tag and tile
// [0, AccessSize) with no gaps, that tag still describes the whole access.
- if (!New.TBAA && M) {
- MDNode *CommonTag = nullptr;
- uint64_t Offset = 0;
- for (size_t I = 0, E = M->getNumOperands(); I < E; I += 3) {
- ConstantInt *FieldOffset =
- mdconst::extract<ConstantInt>(M->getOperand(I));
- ConstantInt *FieldSize =
- mdconst::extract<ConstantInt>(M->getOperand(I + 1));
- MDNode *FieldTag = dyn_cast_or_null<MDNode>(M->getOperand(I + 2));
- if (!FieldTag || FieldOffset->getZExtValue() != Offset ||
- (CommonTag && FieldTag != CommonTag))
- break;
- CommonTag = FieldTag;
- Offset += FieldSize->getZExtValue();
- if (Offset >= AccessSize)
- break;
- }
- if (Offset == AccessSize)
- New.TBAA = CommonTag;
- }
-
New.TBAAStruct = nullptr;
+ if (New.TBAA || !M)
+ return New;
+ MDNode *CommonTag = nullptr;
+ uint64_t Offset = 0;
+ for (size_t I = 0, E = M->getNumOperands(); I < E; I += 3) {
+ ConstantInt *FieldOffset = mdconst::extract<ConstantInt>(M->getOperand(I));
+ ConstantInt *FieldSize =
+ mdconst::extract<ConstantInt>(M->getOperand(I + 1));
+ MDNode *FieldTag = dyn_cast_or_null<MDNode>(M->getOperand(I + 2));
+ if (!FieldTag || FieldOffset->getZExtValue() != Offset ||
+ (CommonTag && FieldTag != CommonTag))
+ break;
+ CommonTag = FieldTag;
+ Offset += FieldSize->getZExtValue();
+ if (Offset >= AccessSize)
+ break;
+ }
+ if (Offset == AccessSize)
+ New.TBAA = CommonTag;
return New;
}
>From d9024d693e20e625203ad5cc877f0305f868f4fa Mon Sep 17 00:00:00 2001
From: Abhay Kanhere <a_kanhere at apple.com>
Date: Wed, 26 Aug 2026 10:42:52 -0700
Subject: [PATCH 3/8] [TBAA] robustness checks for looping over AA records
robust looping over AA records.
---
llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp b/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp
index 90117cf78ec05..b0b732f844596 100644
--- a/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp
+++ b/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp
@@ -827,12 +827,14 @@ AAMDNodes AAMDNodes::adjustForAccess(unsigned AccessSize) {
return New;
MDNode *CommonTag = nullptr;
uint64_t Offset = 0;
- for (size_t I = 0, E = M->getNumOperands(); I < E; I += 3) {
- ConstantInt *FieldOffset = mdconst::extract<ConstantInt>(M->getOperand(I));
+ for (size_t I = 0, E = M->getNumOperands(); I + 2 < E; I += 3) {
+ ConstantInt *FieldOffset =
+ mdconst::dyn_extract_or_null<ConstantInt>(M->getOperand(I));
ConstantInt *FieldSize =
- mdconst::extract<ConstantInt>(M->getOperand(I + 1));
+ mdconst::dyn_extract_or_null<ConstantInt>(M->getOperand(I + 1));
MDNode *FieldTag = dyn_cast_or_null<MDNode>(M->getOperand(I + 2));
- if (!FieldTag || FieldOffset->getZExtValue() != Offset ||
+ if (!FieldOffset || !FieldSize || !FieldTag ||
+ FieldOffset->getZExtValue() != Offset ||
(CommonTag && FieldTag != CommonTag))
break;
CommonTag = FieldTag;
>From 01cda67c9826b76b6b70ad02a1fbc3ee82ff3d62 Mon Sep 17 00:00:00 2001
From: Abhay Kanhere <a_kanhere at apple.com>
Date: Wed, 2 Sep 2026 15:01:37 -0700
Subject: [PATCH 4/8] [TBAA] remove redundant fast path
---
llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp | 8 --------
1 file changed, 8 deletions(-)
diff --git a/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp b/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp
index b0b732f844596..19e14ddd7e306 100644
--- a/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp
+++ b/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp
@@ -810,14 +810,6 @@ MDNode *AAMDNodes::extendToTBAA(MDNode *MD, ssize_t Len) {
AAMDNodes AAMDNodes::adjustForAccess(unsigned AccessSize) {
AAMDNodes New = *this;
MDNode *M = New.TBAAStruct;
- if (!New.TBAA && M && M->getNumOperands() >= 3 && M->getOperand(0) &&
- mdconst::hasa<ConstantInt>(M->getOperand(0)) &&
- mdconst::extract<ConstantInt>(M->getOperand(0))->isZero() &&
- M->getOperand(1) && mdconst::hasa<ConstantInt>(M->getOperand(1)) &&
- mdconst::extract<ConstantInt>(M->getOperand(1))->getValue() ==
- AccessSize &&
- M->getOperand(2) && isa<MDNode>(M->getOperand(2)))
- New.TBAA = cast<MDNode>(M->getOperand(2));
// The access may cover several !tbaa.struct fields (e.g. a {int, int} copy
// widened to an i64 load/store). If those fields share a single tag and tile
>From fd810a6354e2c28e0886a149250c34cc51c672d3 Mon Sep 17 00:00:00 2001
From: Abhay Kanhere <a_kanhere at apple.com>
Date: Wed, 2 Sep 2026 15:03:22 -0700
Subject: [PATCH 5/8] [TBAA] guard oversized !tbaa.struct fields via
getTBAAStructFieldAsInt64
---
llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp | 22 +++++++++++++------
.../InstCombine/struct-assign-tbaa.ll | 16 ++++++++++++++
2 files changed, 31 insertions(+), 7 deletions(-)
diff --git a/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp b/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp
index 19e14ddd7e306..d0c0d5691882d 100644
--- a/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp
+++ b/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp
@@ -748,6 +748,16 @@ MDNode *AAMDNodes::shiftTBAA(MDNode *MD, size_t Offset) {
return MD;
}
+// Read a !tbaa.struct field entry (an offset or a size) into Out as a 64-bit
+// value. Returns false if it is not a constant integer that fits in 64 bits.
+static bool getTBAAStructFieldAsInt64(const MDOperand &Op, uint64_t &Out) {
+ auto *CI = mdconst::dyn_extract_or_null<ConstantInt>(Op);
+ if (!CI || !CI->getValue().isIntN(64))
+ return false;
+ Out = CI->getZExtValue();
+ return true;
+}
+
MDNode *AAMDNodes::shiftTBAAStruct(MDNode *MD, size_t Offset) {
// Fast path if there's no offset
if (Offset == 0)
@@ -820,17 +830,15 @@ AAMDNodes AAMDNodes::adjustForAccess(unsigned AccessSize) {
MDNode *CommonTag = nullptr;
uint64_t Offset = 0;
for (size_t I = 0, E = M->getNumOperands(); I + 2 < E; I += 3) {
- ConstantInt *FieldOffset =
- mdconst::dyn_extract_or_null<ConstantInt>(M->getOperand(I));
- ConstantInt *FieldSize =
- mdconst::dyn_extract_or_null<ConstantInt>(M->getOperand(I + 1));
+ uint64_t FieldOffset, FieldSize;
MDNode *FieldTag = dyn_cast_or_null<MDNode>(M->getOperand(I + 2));
- if (!FieldOffset || !FieldSize || !FieldTag ||
- FieldOffset->getZExtValue() != Offset ||
+ if (!getTBAAStructFieldAsInt64(M->getOperand(I), FieldOffset) ||
+ !getTBAAStructFieldAsInt64(M->getOperand(I + 1), FieldSize) ||
+ !FieldTag || FieldOffset != Offset ||
(CommonTag && FieldTag != CommonTag))
break;
CommonTag = FieldTag;
- Offset += FieldSize->getZExtValue();
+ Offset += FieldSize;
if (Offset >= AccessSize)
break;
}
diff --git a/llvm/test/Transforms/InstCombine/struct-assign-tbaa.ll b/llvm/test/Transforms/InstCombine/struct-assign-tbaa.ll
index a33c07171514e..66ff443e772d6 100644
--- a/llvm/test/Transforms/InstCombine/struct-assign-tbaa.ll
+++ b/llvm/test/Transforms/InstCombine/struct-assign-tbaa.ll
@@ -101,6 +101,21 @@ entry:
ret void
}
+; A !tbaa.struct field offset that does not fit in i64 must not assert in
+; getZExtValue(); the walk bails and no tag is recovered (untyped load/store).
+define void @test8_i128_offset(ptr nocapture %a, ptr nocapture %b) {
+; CHECK-LABEL: define void @test8_i128_offset(
+; CHECK-SAME: ptr captures(none) [[A:%.*]], ptr captures(none) [[B:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[TMP0:%.*]] = load i64, ptr [[B]], align 4
+; CHECK-NEXT: store i64 [[TMP0]], ptr [[A]], align 4
+; CHECK-NEXT: ret void
+;
+entry:
+ tail call void @llvm.memcpy.p0.p0.i64(ptr align 4 %a, ptr align 4 %b, i64 8, i1 false), !tbaa.struct !12
+ ret void
+}
+
!0 = !{!"Simple C/C++ TBAA"}
!1 = !{!"omnipotent char", !0}
!2 = !{!5, !5, i64 0}
@@ -113,6 +128,7 @@ entry:
!9 = !{!10, !10, i64 0}
!10 = !{!"int", !0}
!11 = !{i64 0, i64 4, !9, i64 4, i64 4, !2}
+!12 = !{i128 0, i128 4, !2, i128 18446744073709551616, i128 4, !2}
;.
; CHECK: attributes #[[ATTR0:[0-9]+]] = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
>From 59bc9e25c18591220d8b3055c363d24cfc6800ca Mon Sep 17 00:00:00 2001
From: Abhay Kanhere <a_kanhere at apple.com>
Date: Tue, 8 Sep 2026 12:24:13 -0700
Subject: [PATCH 6/8] [TBAA] review comment addressed
use tryZExtValue in getTBAAStructFieldAsInt64.
---
llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp b/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp
index d0c0d5691882d..f49f0df5dfbaf 100644
--- a/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp
+++ b/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp
@@ -752,9 +752,12 @@ MDNode *AAMDNodes::shiftTBAA(MDNode *MD, size_t Offset) {
// value. Returns false if it is not a constant integer that fits in 64 bits.
static bool getTBAAStructFieldAsInt64(const MDOperand &Op, uint64_t &Out) {
auto *CI = mdconst::dyn_extract_or_null<ConstantInt>(Op);
- if (!CI || !CI->getValue().isIntN(64))
+ if (!CI)
return false;
- Out = CI->getZExtValue();
+ std::optional<uint64_t> Val = CI->getValue().tryZExtValue();
+ if (!Val)
+ return false;
+ Out = *Val;
return true;
}
>From e6cc661cdb11223ee386c06cbdc95804808caa30 Mon Sep 17 00:00:00 2001
From: Abhay Kanhere <a_kanhere at apple.com>
Date: Thu, 10 Sep 2026 12:25:28 -0700
Subject: [PATCH 7/8] [TBAA] Reject invalid !tbaa.struct field tags in
adjustForAccess
Promote a !tbaa.struct field's tag operand to !tbaa only after
checking the tag is a valid access tag; otherwise the module
verifier rejects it.
Resolves crash identified in
https://github.com/cuhk-s3/Archer/issues/21
---
llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp | 18 +++++++++++++++++-
.../InstCombine/struct-assign-tbaa.ll | 17 +++++++++++++++++
2 files changed, 34 insertions(+), 1 deletion(-)
diff --git a/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp b/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp
index f49f0df5dfbaf..a056b4d2aaeb6 100644
--- a/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp
+++ b/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp
@@ -761,6 +761,22 @@ static bool getTBAAStructFieldAsInt64(const MDOperand &Op, uint64_t &Out) {
return true;
}
+// Return true if Tag is a struct-path TBAA access tag: two type nodes (base
+// and access) followed by constant integer fields (offset, and an optional
+// size and/or immutability flag). A !tbaa.struct field operand need not be
+// such a tag, so validate before reusing it as !tbaa.
+static bool isValidTBAAAccessTag(const MDNode *Tag) {
+ unsigned NumOps = Tag->getNumOperands();
+ if (NumOps < 3 || NumOps > 5)
+ return false;
+ if (!isa_and_nonnull<MDNode>(Tag->getOperand(0)) ||
+ !isa_and_nonnull<MDNode>(Tag->getOperand(1)))
+ return false;
+ return all_of(drop_begin(Tag->operands(), 2), [](const MDOperand &Op) {
+ return mdconst::dyn_extract_or_null<ConstantInt>(Op) != nullptr;
+ });
+}
+
MDNode *AAMDNodes::shiftTBAAStruct(MDNode *MD, size_t Offset) {
// Fast path if there's no offset
if (Offset == 0)
@@ -837,7 +853,7 @@ AAMDNodes AAMDNodes::adjustForAccess(unsigned AccessSize) {
MDNode *FieldTag = dyn_cast_or_null<MDNode>(M->getOperand(I + 2));
if (!getTBAAStructFieldAsInt64(M->getOperand(I), FieldOffset) ||
!getTBAAStructFieldAsInt64(M->getOperand(I + 1), FieldSize) ||
- !FieldTag || FieldOffset != Offset ||
+ !FieldTag || !isValidTBAAAccessTag(FieldTag) || FieldOffset != Offset ||
(CommonTag && FieldTag != CommonTag))
break;
CommonTag = FieldTag;
diff --git a/llvm/test/Transforms/InstCombine/struct-assign-tbaa.ll b/llvm/test/Transforms/InstCombine/struct-assign-tbaa.ll
index 66ff443e772d6..fa8ba0ee968c2 100644
--- a/llvm/test/Transforms/InstCombine/struct-assign-tbaa.ll
+++ b/llvm/test/Transforms/InstCombine/struct-assign-tbaa.ll
@@ -116,6 +116,21 @@ entry:
ret void
}
+; A !tbaa.struct field tag that is not a valid access tag (here it is itself
+; !tbaa.struct-shaped) must not be promoted to !tbaa.
+define void @test9_invalid_field_tag(ptr nocapture %a, ptr nocapture %b) {
+; CHECK-LABEL: define void @test9_invalid_field_tag(
+; CHECK-SAME: ptr captures(none) [[A:%.*]], ptr captures(none) [[B:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[B]], align 4
+; CHECK-NEXT: store i32 [[TMP0]], ptr [[A]], align 4
+; CHECK-NEXT: ret void
+;
+entry:
+ tail call void @llvm.memcpy.p0.p0.i64(ptr align 4 %a, ptr align 4 %b, i64 4, i1 false), !tbaa.struct !13
+ ret void
+}
+
!0 = !{!"Simple C/C++ TBAA"}
!1 = !{!"omnipotent char", !0}
!2 = !{!5, !5, i64 0}
@@ -129,6 +144,8 @@ entry:
!10 = !{!"int", !0}
!11 = !{i64 0, i64 4, !9, i64 4, i64 4, !2}
!12 = !{i128 0, i128 4, !2, i128 18446744073709551616, i128 4, !2}
+!13 = !{i64 0, i64 2, !14, i64 2, i64 2, !14}
+!14 = !{i64 0, i64 4, null}
;.
; CHECK: attributes #[[ATTR0:[0-9]+]] = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
>From 8651eaee3a8ef2597577e19a66eaeede13657be9 Mon Sep 17 00:00:00 2001
From: Abhay Kanhere <a_kanhere at apple.com>
Date: Mon, 14 Sep 2026 08:52:52 -0700
Subject: [PATCH 8/8] [TBAA] Tighten struct-path access-tag shape check in
adjustForAccess
Reuse the file-local isNewFormatTypeNode so the !tbaa.struct field-tag
shape check expects the correct operand count per TBAA format, and
return std::optional<uint64_t> from getTBAAStructFieldAsInt64.
---
llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp | 58 ++++++++++----------
1 file changed, 30 insertions(+), 28 deletions(-)
diff --git a/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp b/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp
index a056b4d2aaeb6..5a21668c42b59 100644
--- a/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp
+++ b/llvm/lib/Analysis/TypeBasedAliasAnalysis.cpp
@@ -748,30 +748,31 @@ MDNode *AAMDNodes::shiftTBAA(MDNode *MD, size_t Offset) {
return MD;
}
-// Read a !tbaa.struct field entry (an offset or a size) into Out as a 64-bit
-// value. Returns false if it is not a constant integer that fits in 64 bits.
-static bool getTBAAStructFieldAsInt64(const MDOperand &Op, uint64_t &Out) {
+// Read a !tbaa.struct field entry (an offset or a size) as a 64-bit value.
+// Returns std::nullopt if it is not a constant integer that fits in 64 bits.
+static std::optional<uint64_t> getTBAAStructFieldAsInt64(const MDOperand &Op) {
auto *CI = mdconst::dyn_extract_or_null<ConstantInt>(Op);
- if (!CI)
+ return CI ? CI->getValue().tryZExtValue() : std::nullopt;
+}
+
+// Return true if Tag is a well-formed struct-path TBAA access tag shape: base
+// and access type nodes followed by constant-integer fields (offset, a size
+// field for the new format, and an optional immutability flag). Structural
+// check only.
+static bool isWellFormedTBAAAccessTagShape(const MDNode *Tag) {
+ const MDNode *AccessType = nullptr;
+ if (Tag->getNumOperands() < 3 ||
+ !dyn_cast_or_null<MDNode>(Tag->getOperand(0)) ||
+ !(AccessType = dyn_cast_or_null<MDNode>(Tag->getOperand(1))))
return false;
- std::optional<uint64_t> Val = CI->getValue().tryZExtValue();
- if (!Val)
- return false;
- Out = *Val;
- return true;
-}
-// Return true if Tag is a struct-path TBAA access tag: two type nodes (base
-// and access) followed by constant integer fields (offset, and an optional
-// size and/or immutability flag). A !tbaa.struct field operand need not be
-// such a tag, so validate before reusing it as !tbaa.
-static bool isValidTBAAAccessTag(const MDNode *Tag) {
- unsigned NumOps = Tag->getNumOperands();
- if (NumOps < 3 || NumOps > 5)
- return false;
- if (!isa_and_nonnull<MDNode>(Tag->getOperand(0)) ||
- !isa_and_nonnull<MDNode>(Tag->getOperand(1)))
+ // Operand count is format-dependent: the new format carries a size field the
+ // old one lacks; both allow a trailing immutability flag.
+ unsigned MinOps = isNewFormatTypeNode(AccessType) ? 4 : 3;
+ if (Tag->getNumOperands() < MinOps || Tag->getNumOperands() > MinOps + 1)
return false;
+
+ // Offset, size, and the immutability flag are constant integers.
return all_of(drop_begin(Tag->operands(), 2), [](const MDOperand &Op) {
return mdconst::dyn_extract_or_null<ConstantInt>(Op) != nullptr;
});
@@ -848,18 +849,19 @@ AAMDNodes AAMDNodes::adjustForAccess(unsigned AccessSize) {
return New;
MDNode *CommonTag = nullptr;
uint64_t Offset = 0;
- for (size_t I = 0, E = M->getNumOperands(); I + 2 < E; I += 3) {
- uint64_t FieldOffset, FieldSize;
+ for (size_t I = 0, E = M->getNumOperands(); I + 2 < E && Offset < AccessSize;
+ I += 3) {
+ std::optional<uint64_t> FieldOffset =
+ getTBAAStructFieldAsInt64(M->getOperand(I));
+ std::optional<uint64_t> FieldSize =
+ getTBAAStructFieldAsInt64(M->getOperand(I + 1));
MDNode *FieldTag = dyn_cast_or_null<MDNode>(M->getOperand(I + 2));
- if (!getTBAAStructFieldAsInt64(M->getOperand(I), FieldOffset) ||
- !getTBAAStructFieldAsInt64(M->getOperand(I + 1), FieldSize) ||
- !FieldTag || !isValidTBAAAccessTag(FieldTag) || FieldOffset != Offset ||
+ if (!FieldOffset || !FieldSize || !FieldTag ||
+ !isWellFormedTBAAAccessTagShape(FieldTag) || *FieldOffset != Offset ||
(CommonTag && FieldTag != CommonTag))
break;
CommonTag = FieldTag;
- Offset += FieldSize;
- if (Offset >= AccessSize)
- break;
+ Offset += *FieldSize;
}
if (Offset == AccessSize)
New.TBAA = CommonTag;
More information about the llvm-commits
mailing list