[llvm] [AutoUpgrade] Upgrade old-style !tbaa.struct field tags (PR #225967)
Abhay Kanhere via llvm-commits
llvm-commits at lists.llvm.org
Wed Sep 23 15:51:20 PDT 2026
https://github.com/AbhayKanhere created https://github.com/llvm/llvm-project/pull/225967
Extend the TBAA auto-upgrade to rewrite old-style scalar field tags inside !tbaa.struct
to the struct-path aware format, on both the textual (LLParser) and bitcode (MetadataLoader)
paths, matching the existing top-level !tbaa upgrade.
>From 50aa2432be1c2e510df0b4614555963337e384f6 Mon Sep 17 00:00:00 2001
From: Abhay Kanhere <a_kanhere at apple.com>
Date: Wed, 23 Sep 2026 15:29:01 -0700
Subject: [PATCH] [AutoUpgrade] Upgrade old-style !tbaa.struct field tags
Extend the TBAA auto-upgrade to rewrite old-style scalar field tags inside
!tbaa.struct to the struct-path aware format, on both the textual (LLParser)
and bitcode (MetadataLoader) paths, matching the existing top-level !tbaa
upgrade.
---
llvm/include/llvm/AsmParser/LLParser.h | 1 +
llvm/include/llvm/IR/AutoUpgrade.h | 5 +++
llvm/lib/AsmParser/LLParser.cpp | 10 +++++
llvm/lib/Bitcode/Reader/MetadataLoader.cpp | 3 ++
llvm/lib/IR/AutoUpgrade.cpp | 22 +++++++++++
llvm/test/Bitcode/upgrade-tbaa-struct.ll | 46 ++++++++++++++++++++++
6 files changed, 87 insertions(+)
create mode 100644 llvm/test/Bitcode/upgrade-tbaa-struct.ll
diff --git a/llvm/include/llvm/AsmParser/LLParser.h b/llvm/include/llvm/AsmParser/LLParser.h
index 7cc8e9877bbd0d..ccd8b61b119e34 100644
--- a/llvm/include/llvm/AsmParser/LLParser.h
+++ b/llvm/include/llvm/AsmParser/LLParser.h
@@ -120,6 +120,7 @@ namespace llvm {
SlotMapping *Slots;
SmallVector<Instruction*, 64> InstsWithTBAATag;
+ SmallVector<Instruction *, 64> InstsWithTBAAStructTag;
/// DIAssignID metadata does not support temporary RAUW so we cannot use
/// the normal metadata forward reference resolution method. Instead,
diff --git a/llvm/include/llvm/IR/AutoUpgrade.h b/llvm/include/llvm/IR/AutoUpgrade.h
index 53da20d63a7f1f..b129994885b2d8 100644
--- a/llvm/include/llvm/IR/AutoUpgrade.h
+++ b/llvm/include/llvm/IR/AutoUpgrade.h
@@ -85,6 +85,11 @@ namespace llvm {
/// Otherwise return the \p TBAANode itself.
LLVM_ABI MDNode *UpgradeTBAANode(MDNode &TBAANode);
+ /// If the given !tbaa.struct node has old-style scalar field tags, return an
+ /// equivalent node with each field tag upgraded to the struct-path aware
+ /// format. Otherwise return the node itself.
+ LLVM_ABI MDNode *UpgradeTBAAStructNode(MDNode &TBAAStructNode);
+
/// This is an auto-upgrade for bitcast between pointers with different
/// address spaces: the instruction is replaced by a pair ptrtoint+inttoptr.
LLVM_ABI Instruction *UpgradeBitCastInst(unsigned Opc, Value *V, Type *DestTy,
diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp
index cc7633894fb74b..30da28a7f01931 100644
--- a/llvm/lib/AsmParser/LLParser.cpp
+++ b/llvm/lib/AsmParser/LLParser.cpp
@@ -550,6 +550,14 @@ bool LLParser::validateEndOfModule(bool UpgradeDebugInfo) {
}
}
+ for (auto *Inst : InstsWithTBAAStructTag) {
+ MDNode *MD = Inst->getMetadata(LLVMContext::MD_tbaa_struct);
+ if (!MD)
+ continue;
+ if (MDNode *Upgraded = UpgradeTBAAStructNode(*MD); Upgraded != MD)
+ Inst->setMetadata(LLVMContext::MD_tbaa_struct, Upgraded);
+ }
+
// Look for intrinsic functions and CallInst that need to be upgraded. We use
// make_early_inc_range here because we may remove some functions.
for (Function &F : llvm::make_early_inc_range(*M))
@@ -2554,6 +2562,8 @@ bool LLParser::parseInstructionMetadata(Instruction &Inst) {
if (MDK == LLVMContext::MD_tbaa)
InstsWithTBAATag.push_back(&Inst);
+ else if (MDK == LLVMContext::MD_tbaa_struct)
+ InstsWithTBAAStructTag.push_back(&Inst);
// If this is the end of the list, we're done.
} while (EatIfPresent(lltok::comma));
diff --git a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
index 6f1fbd627edbcf..80e44634d6f957 100644
--- a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
+++ b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
@@ -2647,6 +2647,9 @@ Error MetadataLoader::MetadataLoaderImpl::parseMetadataAttachment(
if (I->second == LLVMContext::MD_tbaa) {
assert(!MD->isTemporary() && "should load MDs before attachments");
MD = UpgradeTBAANode(*MD);
+ } else if (I->second == LLVMContext::MD_tbaa_struct) {
+ assert(!MD->isTemporary() && "should load MDs before attachments");
+ MD = UpgradeTBAAStructNode(*MD);
}
Inst->setMetadata(I->second, MD);
}
diff --git a/llvm/lib/IR/AutoUpgrade.cpp b/llvm/lib/IR/AutoUpgrade.cpp
index 16751a4954844f..5e50ca23be37c6 100644
--- a/llvm/lib/IR/AutoUpgrade.cpp
+++ b/llvm/lib/IR/AutoUpgrade.cpp
@@ -6655,6 +6655,28 @@ MDNode *llvm::UpgradeTBAANode(MDNode &MD) {
return MDNode::get(Context, Elts);
}
+MDNode *llvm::UpgradeTBAAStructNode(MDNode &MD) {
+ // !tbaa.struct is a list of (offset, size, tag) triples. Upgrade any
+ // old-style scalar field tag to struct-path form via UpgradeTBAANode.
+ unsigned NumOperands = MD.getNumOperands();
+ if (NumOperands == 0 || NumOperands % 3 != 0)
+ return &MD; // Malformed; leave it for the verifier to reject.
+
+ SmallVector<Metadata *, 12> Elts(MD.op_begin(), MD.op_end());
+ bool Changed = false;
+ for (unsigned I = 2; I < NumOperands; I += 3) {
+ auto *Tag = dyn_cast_or_null<MDNode>(Elts[I]);
+ if (!Tag)
+ continue;
+ MDNode *Upgraded = UpgradeTBAANode(*Tag);
+ if (Upgraded == Tag)
+ continue;
+ Elts[I] = Upgraded;
+ Changed = true;
+ }
+ return Changed ? MDNode::get(MD.getContext(), Elts) : &MD;
+}
+
Instruction *llvm::UpgradeBitCastInst(unsigned Opc, Value *V, Type *DestTy,
Instruction *&Temp) {
if (Opc != Instruction::BitCast)
diff --git a/llvm/test/Bitcode/upgrade-tbaa-struct.ll b/llvm/test/Bitcode/upgrade-tbaa-struct.ll
new file mode 100644
index 00000000000000..6e4cf0cced95e7
--- /dev/null
+++ b/llvm/test/Bitcode/upgrade-tbaa-struct.ll
@@ -0,0 +1,46 @@
+; RUN: llvm-as < %s | llvm-dis | FileCheck %s
+; RUN: verify-uselistorder < %s
+
+; Old-style scalar tags used as !tbaa.struct field tags are auto-upgraded to
+; the struct-path aware format, like top-level !tbaa. Null and already
+; struct-path field tags are left unchanged; an immutability flag is kept.
+
+define void @copy(ptr %a, ptr %b) {
+ call void @llvm.memcpy.p0.p0.i64(ptr %a, ptr %b, i64 12, i1 false), !tbaa.struct !0
+; CHECK: !tbaa.struct [[TS:![0-9]+]]
+ ret void
+}
+
+define void @copy_flag_and_structpath(ptr %a, ptr %b) {
+ call void @llvm.memcpy.p0.p0.i64(ptr %a, ptr %b, i64 8, i1 false), !tbaa.struct !5
+; CHECK: !tbaa.struct [[TS2:![0-9]+]]
+ ret void
+}
+
+declare void @llvm.memcpy.p0.p0.i64(ptr, ptr, i64, i1)
+
+; Old-style 2-operand scalar field tags and a null field tag.
+!0 = !{i64 0, i64 4, !1, i64 4, i64 4, !3, i64 8, i64 4, null}
+!1 = !{!"int", !2}
+!2 = !{!"omnipotent char", !4}
+!3 = !{!"float", !2}
+!4 = !{!"Simple C/C++ TBAA"}
+
+; Old-style 3-operand (immutable) field tag and an already struct-path field tag.
+!5 = !{i64 0, i64 4, !6, i64 4, i64 4, !7}
+!6 = !{!"const int", !2, i64 1}
+!7 = !{!8, !8, i64 0}
+!8 = !{!"double", !2}
+
+; CHECK: [[TS]] = !{i64 0, i64 4, [[TAG_INT:![0-9]+]], i64 4, i64 4, [[TAG_FLOAT:![0-9]+]], i64 8, i64 4, null}
+; CHECK: [[TAG_INT]] = !{[[TYPE_INT:![0-9]+]], [[TYPE_INT]], i64 0}
+; CHECK: [[TYPE_INT]] = !{!"int", [[TYPE_CHAR:![0-9]+]]}
+; CHECK: [[TYPE_CHAR]] = !{!"omnipotent char", !{{[0-9]+}}}
+; CHECK: [[TAG_FLOAT]] = !{[[TYPE_FLOAT:![0-9]+]], [[TYPE_FLOAT]], i64 0}
+; CHECK: [[TYPE_FLOAT]] = !{!"float", [[TYPE_CHAR]]}
+; The immutability flag (i64 1) is preserved; the already struct-path tag is unchanged.
+; CHECK: [[TS2]] = !{i64 0, i64 4, [[TAG_CONST:![0-9]+]], i64 4, i64 4, [[TAG_DOUBLE:![0-9]+]]}
+; CHECK: [[TAG_CONST]] = !{[[TYPE_CONST:![0-9]+]], [[TYPE_CONST]], i64 0, i64 1}
+; CHECK: [[TYPE_CONST]] = !{!"const int", [[TYPE_CHAR]]}
+; CHECK: [[TAG_DOUBLE]] = !{[[TYPE_DOUBLE:![0-9]+]], [[TYPE_DOUBLE]], i64 0}
+; CHECK: [[TYPE_DOUBLE]] = !{!"double", [[TYPE_CHAR]]}
More information about the llvm-commits
mailing list