[clang] [clang][CodeGen] Add TBAA for bit-field accesses (PR #226806)
via cfe-commits
cfe-commits at lists.llvm.org
Sun Sep 27 09:47:23 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang-codegen
Author: Dávid Bolvanský (davidbolvansky)
<details>
<summary>Changes</summary>
Bit-field loads and read-modify-write stores currently have no TBAA metadata. This prevents forwarding across accesses to distinct bit-field storage units, even when enhanced struct-path TBAA preserves enough of the enclosing aggregate path to disambiguate them.
Represent each distinct bit-field storage unit as an omnipotent-char range in enhanced TBAA aggregate descriptors, propagate that path to bit-field lvalues, and attach it to the generated loads and stores. This is sound because the tag covers the complete storage unit touched by the IR instruction rather than the logical bit range; bit-fields sharing storage therefore remain aliasing. Union, `may_alias`, relaxed-aliasing, and AAPCS volatile cases remain conservative.
The regression test checks both the newly optimized distinct-storage case and a same-storage case where the reload must remain.
Assisted-by: OpenAI Codex
---
Full diff: https://github.com/llvm/llvm-project/pull/226806.diff
4 Files Affected:
- (modified) clang/lib/CodeGen/CGExpr.cpp (+29-6)
- (modified) clang/lib/CodeGen/CodeGenTBAA.cpp (+22-7)
- (added) clang/test/CodeGen/tbaa-bitfield-access.c (+47)
- (modified) clang/test/CodeGen/tbaa-struct.cpp (+3-3)
``````````diff
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 4a481c01f6a68..bea3a8096a932 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,10 @@ 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 =
+ 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 +3104,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 +6014,28 @@ 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..8e7e202af52df 100644
--- a/clang/lib/CodeGen/CodeGenTBAA.cpp
+++ b/clang/lib/CodeGen/CodeGenTBAA.cpp
@@ -559,19 +559,34 @@ 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}
``````````
</details>
https://github.com/llvm/llvm-project/pull/226806
More information about the cfe-commits
mailing list