[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