[clang] [clang][CodeGen] Add TBAA for bit-field accesses (PR #226806)
Dávid Bolvanský via cfe-commits
cfe-commits at lists.llvm.org
Sun Sep 27 09:56:02 PDT 2026
https://github.com/davidbolvansky updated https://github.com/llvm/llvm-project/pull/226806
>From 5d463c919547f89b0ef0fb07f1c5e89e6c8cc2ac Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?D=C3=A1vid=20Bolvansk=C3=BD?= <david.bolvansky at gmail.com>
Date: Sun, 27 Sep 2026 18:46:07 +0200
Subject: [PATCH] [clang][CodeGen] Add TBAA for bit-field accesses
---
clang/lib/CodeGen/CGExpr.cpp | 35 +++++++++++++----
clang/lib/CodeGen/CodeGenTBAA.cpp | 28 ++++++++++----
clang/test/CodeGen/tbaa-bitfield-access.c | 47 +++++++++++++++++++++++
clang/test/CodeGen/tbaa-struct.cpp | 6 +--
4 files changed, 99 insertions(+), 17 deletions(-)
create mode 100644 clang/test/CodeGen/tbaa-bitfield-access.c
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 4a481c01f6a68..f51ca0af010f6 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -2658,8 +2658,9 @@ RValue CodeGenFunction::EmitLoadOfBitfieldLValue(LValue LV,
llvm::Type *ResLTy = ConvertType(LV.getType());
Address Ptr = LV.getBitFieldAddress();
- llvm::Value *Val =
- Builder.CreateLoad(Ptr, LV.isVolatileQualified(), "bf.load");
+ auto *Load = Builder.CreateLoad(Ptr, LV.isVolatileQualified(), "bf.load");
+ CGM.DecorateInstructionWithTBAA(Load, LV.getTBAAInfo());
+ llvm::Value *Val = Load;
bool UseVolatile = LV.isVolatileQualified() &&
Info.VolatileStorageSize != 0 &&
@@ -3073,8 +3074,9 @@ void CodeGenFunction::EmitStoreThroughBitfieldLValue(RValue Src, LValue Dst,
// and mask together with source before storing.
if (StorageSize != Info.Size) {
assert(StorageSize > Info.Size && "Invalid bitfield size.");
- llvm::Value *Val =
- Builder.CreateLoad(Ptr, Dst.isVolatileQualified(), "bf.load");
+ auto *Load = Builder.CreateLoad(Ptr, Dst.isVolatileQualified(), "bf.load");
+ CGM.DecorateInstructionWithTBAA(Load, Dst.getTBAAInfo());
+ llvm::Value *Val = Load;
// Mask the source value as needed.
if (!Dst.getType()->hasBooleanRepresentation())
@@ -3101,12 +3103,14 @@ void CodeGenFunction::EmitStoreThroughBitfieldLValue(RValue Src, LValue Dst,
// of the container. The two accesses are not atomic.
if (Dst.isVolatileQualified() && CodeGenUtils::isAAPCS(CGM.getTarget()) &&
CGM.getCodeGenOpts().ForceAAPCSBitfieldLoad)
- Builder.CreateLoad(Ptr, true, "bf.load");
+ CGM.DecorateInstructionWithTBAA(Builder.CreateLoad(Ptr, true, "bf.load"),
+ Dst.getTBAAInfo());
}
// Write the new value back out.
auto *I = Builder.CreateStore(SrcVal, Ptr, Dst.isVolatileQualified());
addInstToCurrentSourceAtom(I, SrcVal);
+ CGM.DecorateInstructionWithTBAA(I, Dst.getTBAAInfo());
// Return the new value of the bit-field, if requested.
if (Result) {
@@ -6009,10 +6013,27 @@ LValue CodeGenFunction::EmitLValueForField(LValue base, const FieldDecl *field,
QualType fieldType =
field->getType().withCVRQualifiers(base.getVRQualifiers());
- // TODO: Support TBAA for bit fields.
+ TBAAAccessInfo FieldTBAAInfo;
+ if (CGM.getCodeGenOpts().NewStructPathTBAA && !UseVolatile &&
+ !base.getTBAAInfo().isMayAlias() && !rec->hasAttr<MayAliasAttr>() &&
+ !rec->isUnion()) {
+ FieldTBAAInfo = base.getTBAAInfo();
+ if (!FieldTBAAInfo.BaseType) {
+ FieldTBAAInfo.BaseType = CGM.getTBAABaseTypeInfo(base.getType());
+ assert(!FieldTBAAInfo.Offset &&
+ "Nonzero offset for an access with no base type!");
+ }
+
+ // A bit-field access reads or writes its complete storage unit. Use
+ // the character type for that unit while retaining its struct path.
+ if (FieldTBAAInfo.BaseType)
+ FieldTBAAInfo.Offset += Info.StorageOffset.getQuantity();
+ FieldTBAAInfo.AccessType = CGM.getTBAATypeInfo(getContext().CharTy);
+ FieldTBAAInfo.Size = getContext().toCharUnitsFromBits(SS).getQuantity();
+ }
LValueBaseInfo FieldBaseInfo(BaseInfo.getAlignmentSource());
return LValue::MakeBitfield(Addr, Info, fieldType, FieldBaseInfo,
- TBAAAccessInfo());
+ FieldTBAAInfo);
}
// Fields of may-alias structures are may-alias themselves.
diff --git a/clang/lib/CodeGen/CodeGenTBAA.cpp b/clang/lib/CodeGen/CodeGenTBAA.cpp
index 1854df7c7c0f1..813a23550e179 100644
--- a/clang/lib/CodeGen/CodeGenTBAA.cpp
+++ b/clang/lib/CodeGen/CodeGenTBAA.cpp
@@ -559,19 +559,33 @@ llvm::MDNode *CodeGenTBAA::getBaseTypeInfoHelper(const Type *Ty) {
return A.Offset < B.Offset;
});
}
+ SmallVector<std::pair<uint64_t, uint64_t>, 4> BitFieldStorageUnits;
for (FieldDecl *Field : RD->fields()) {
if (Field->isZeroSize(Context) || Field->isUnnamedBitField())
continue;
QualType FieldQTy = Field->getType();
- llvm::MDNode *TypeNode = isValidBaseType(FieldQTy)
- ? getValidBaseTypeInfo(FieldQTy)
- : getTypeInfo(FieldQTy);
+ llvm::MDNode *TypeNode;
+ uint64_t Offset;
+ uint64_t Size;
+ if (CodeGenOpts.NewStructPathTBAA && Field->isBitField()) {
+ const CGBitFieldInfo &Info =
+ CGTypes.getCGRecordLayout(RD).getBitFieldInfo(Field);
+ TypeNode = getChar();
+ Offset = Info.StorageOffset.getQuantity();
+ Size = llvm::divideCeil(Info.StorageSize, Context.getCharWidth());
+ if (llvm::is_contained(BitFieldStorageUnits,
+ std::make_pair(Offset, Size)))
+ continue;
+ BitFieldStorageUnits.emplace_back(Offset, Size);
+ } else {
+ TypeNode = isValidBaseType(FieldQTy) ? getValidBaseTypeInfo(FieldQTy)
+ : getTypeInfo(FieldQTy);
+ uint64_t BitOffset = Layout.getFieldOffset(Field->getFieldIndex());
+ Offset = Context.toCharUnitsFromBits(BitOffset).getQuantity();
+ Size = Context.getTypeSizeInChars(FieldQTy).getQuantity();
+ }
if (!TypeNode)
return nullptr;
-
- uint64_t BitOffset = Layout.getFieldOffset(Field->getFieldIndex());
- uint64_t Offset = Context.toCharUnitsFromBits(BitOffset).getQuantity();
- uint64_t Size = Context.getTypeSizeInChars(FieldQTy).getQuantity();
Fields.push_back(llvm::MDBuilder::TBAAStructField(Offset, Size,
TypeNode));
}
diff --git a/clang/test/CodeGen/tbaa-bitfield-access.c b/clang/test/CodeGen/tbaa-bitfield-access.c
new file mode 100644
index 0000000000000..eaee5536bde43
--- /dev/null
+++ b/clang/test/CodeGen/tbaa-bitfield-access.c
@@ -0,0 +1,47 @@
+// RUN: %clang_cc1 -triple x86_64-linux -O1 -emit-llvm %s -o - | \
+// RUN: FileCheck %s --check-prefix=OLD
+// RUN: %clang_cc1 -triple x86_64-linux -O1 -new-struct-path-tbaa \
+// RUN: -relaxed-aliasing -emit-llvm %s -o - | FileCheck %s --check-prefix=OLD
+// RUN: %clang_cc1 -triple x86_64-linux -O1 -new-struct-path-tbaa \
+// RUN: -emit-llvm %s -o - | FileCheck %s --check-prefix=NEW
+
+struct A {
+ int a : 3;
+ int b : 3;
+};
+struct B {
+ struct A a1, a2;
+};
+struct C {
+ struct B b[10];
+} *c;
+struct D {
+ struct C c;
+} *d;
+
+// The two bit-fields occupy different storage units. Their TBAA tags retain
+// enough of the enclosing struct path to prove that the store cannot clobber
+// the value written by the first store.
+// OLD-LABEL: define{{.*}} i32 @different_storage(
+// OLD: load i8, ptr
+// OLD: ret i32
+// NEW-LABEL: define{{.*}} i32 @different_storage(
+// NEW-COUNT-2: load i8, ptr
+// NEW: ret i32 0
+int different_storage(int i, int j) {
+ c->b[i].a1.a = 0;
+ d->c.b[j].a2.b = 1;
+ return c->b[i].a1.a;
+}
+
+// These accesses may designate the same storage unit, so the reload must be
+// retained.
+// NEW-LABEL: define{{.*}} i32 @same_storage(
+// NEW: store i8
+// NEW: load i8, ptr
+// NEW: ret i32
+int same_storage(int i, int j) {
+ c->b[i].a1.a = 1;
+ d->c.b[j].a1.a = 0;
+ return c->b[i].a1.a;
+}
diff --git a/clang/test/CodeGen/tbaa-struct.cpp b/clang/test/CodeGen/tbaa-struct.cpp
index 2776ea2e4e861..ec564277efcd0 100644
--- a/clang/test/CodeGen/tbaa-struct.cpp
+++ b/clang/test/CodeGen/tbaa-struct.cpp
@@ -224,12 +224,12 @@ void copy12(UnionMember2 *a1, UnionMember2 *a2) {
// CHECK-NEW: [[META21]] = !{[[META4]], i64 6, !"_ZTS1D", [[META4]], i64 0, i64 1, [[META4]], i64 4, i64 1, [[META4]], i64 5, i64 1}
// CHECK-NEW: [[TBAA23]] = !{[[META4]], [[META4]], i64 0, i64 0}
// CHECK-NEW: [[TBAA24]] = !{[[META25:![0-9]+]], [[META25]], i64 0, i64 16}
-// CHECK-NEW: [[META25]] = !{[[META4]], i64 16, !"_ZTS14NamedBitfields", [[META3]], i64 0, i64 4, [[META3]], i64 1, i64 4, [[META4]], i64 2, i64 1, [[META26:![0-9]+]], i64 8, i64 8}
+// CHECK-NEW: [[META25]] = !{[[META4]], i64 16, !"_ZTS14NamedBitfields", [[META4]], i64 0, i64 2, [[META4]], i64 2, i64 1, [[META26:![0-9]+]], i64 8, i64 8}
// CHECK-NEW: [[META26]] = !{[[META4]], i64 8, !"double"}
// CHECK-NEW: [[TBAA30]] = !{[[META31:![0-9]+]], [[META31]], i64 0, i64 24}
-// CHECK-NEW: [[META31]] = !{[[META4]], i64 24, !"_ZTS15NamedBitfields2", [[META4]], i64 0, i64 1, [[META4]], i64 1, i64 1, [[META4]], i64 2, i64 1, [[META3]], i64 3, i64 4, [[META3]], i64 3, i64 4, [[META4]], i64 4, i64 1, [[META26]], i64 8, i64 8, [[META3]], i64 16, i64 4}
+// CHECK-NEW: [[META31]] = !{[[META4]], i64 24, !"_ZTS15NamedBitfields2", [[META4]], i64 0, i64 1, [[META4]], i64 1, i64 1, [[META4]], i64 2, i64 1, [[META4]], i64 3, i64 2, [[META26]], i64 8, i64 8, [[META4]], i64 16, i64 1}
// CHECK-NEW: [[TBAA33]] = !{[[META34:![0-9]+]], [[META34]], i64 0, i64 16}
-// CHECK-NEW: [[META34]] = !{[[META4]], i64 16, !"_ZTS15NamedBitfields3", [[META3]], i64 1, i64 4, [[META3]], i64 2, i64 4, [[META26]], i64 8, i64 8}
+// CHECK-NEW: [[META34]] = !{[[META4]], i64 16, !"_ZTS15NamedBitfields3", [[META4]], i64 0, i64 4, [[META26]], i64 8, i64 8}
// CHECK-NEW: [[TBAA37]] = !{[[META38:![0-9]+]], [[META38]], i64 0, i64 16}
// CHECK-NEW: [[META38]] = !{[[META4]], i64 16, !"_ZTS12UnionMember1", [[META4]], i64 0, i64 8, [[META3]], i64 8, i64 4}
// CHECK-NEW: [[TBAA41]] = !{[[META42:![0-9]+]], [[META42]], i64 0, i64 16}
More information about the cfe-commits
mailing list