[llvm-branch-commits] [clang] release/23.x: [Clang][counted_by] Load the count through unions by byte offset (#228309) (PR #232138)
via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Wed Oct 14 16:59:13 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang-codegen
Author: llvmbot
<details>
<summary>Changes</summary>
Backport 46a1c4b
Requested by: @<!-- -->bwendling
---
Patch is 22.20 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/232138.diff
5 Files Affected:
- (modified) clang/lib/CodeGen/CGExpr.cpp (+31-61)
- (modified) clang/test/CodeGen/attr-counted-by-issue200014.c (+11-11)
- (modified) clang/test/CodeGen/attr-counted-by-nested-structs.c (+4-4)
- (added) clang/test/CodeGen/attr-counted-by-union-storage.c (+47)
- (modified) clang/test/CodeGen/builtin-counted-by-ref.c (+16-16)
``````````diff
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 6a0d127fb451b6..d642d46e2b3beb 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -1060,8 +1060,8 @@ static llvm::Value *getArrayIndexingBound(CodeGenFunction &CGF,
/// Returns true if \p Field is reachable from \p RD either as a direct field or
/// through a chain of nested record fields (including anonymous
-/// structs/unions). This mirrors the GEP path that getGEPIndicesToField builds,
-/// and is used to identify the right anchor expression in Base.
+/// structs/unions). This mirrors the path that getFieldOffsetInBits walks, and
+/// is used to identify the right anchor expression in Base.
static bool RecordContainsField(const RecordDecl *RD, const FieldDecl *Field) {
for (const FieldDecl *FD : RD->fields()) {
if (FD == Field)
@@ -1096,7 +1096,7 @@ class StructAccessBase
: public ConstStmtVisitor<StructAccessBase, const Expr *> {
/// The count field we're navigating to. We stop at the innermost expression
/// whose struct type transitively contains this field, so that
- /// getGEPIndicesToField can navigate from that struct down to it.
+ /// getFieldOffsetInBits can navigate from that struct down to it.
const FieldDecl *CountDecl;
/// Returns true if E's record type (or pointee record type) transitively
@@ -1188,34 +1188,28 @@ class StructAccessBase
} // end anonymous namespace
-using RecIndicesTy = SmallVector<llvm::Value *, 8>;
+/// The offset of a field from the beginning of the record.
+static bool getFieldOffsetInBits(CodeGenFunction &CGF, const RecordDecl *RD,
+ const FieldDecl *Field, int64_t &Offset) {
+ ASTContext &Ctx = CGF.getContext();
+ const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+ unsigned FieldNo = 0;
-static bool getGEPIndicesToField(CodeGenFunction &CGF, const RecordDecl *RD,
- const FieldDecl *Field,
- RecIndicesTy &Indices) {
- const CGRecordLayout &Layout = CGF.CGM.getTypes().getCGRecordLayout(RD);
- int64_t FieldNo = -1;
for (const FieldDecl *FD : RD->fields()) {
- if (!Layout.containsFieldDecl(FD))
- // This could happen if the field has a struct type that's empty. I don't
- // know why either.
- continue;
-
- FieldNo = Layout.getLLVMFieldNo(FD);
if (FD == Field) {
- Indices.emplace_back(CGF.Builder.getInt32(FieldNo));
+ Offset += Layout.getFieldOffset(FieldNo);
return true;
}
QualType Ty = FD->getType();
- if (Ty->isRecordType()) {
- if (getGEPIndicesToField(CGF, Ty->getAsRecordDecl(), Field, Indices)) {
- if (RD->isUnion())
- FieldNo = 0;
- Indices.emplace_back(CGF.Builder.getInt32(FieldNo));
+ if (Ty->isRecordType())
+ if (getFieldOffsetInBits(CGF, Ty->getAsRecordDecl(), Field, Offset)) {
+ Offset += Layout.getFieldOffset(FieldNo);
return true;
}
- }
+
+ if (!RD->isUnion())
+ ++FieldNo;
}
return false;
@@ -1224,8 +1218,8 @@ static bool getGEPIndicesToField(CodeGenFunction &CGF, const RecordDecl *RD,
llvm::Value *CodeGenFunction::GetCountedByFieldExprGEP(
const Expr *Base, const FieldDecl *FAMDecl, const FieldDecl *CountDecl) {
// Walk Base to find the deepest sub-expression whose struct type transitively
- // contains CountDecl. This is our GEP anchor — getGEPIndicesToField then
- // builds the field indices from that struct down to CountDecl, handling any
+ // contains CountDecl. This is our GEP anchor — getFieldOffsetInBits then
+ // computes the offset from that struct down to CountDecl, handling any
// intermediate nesting without requiring us to pre-compute a RecordDecl from
// Base's type or from CountDecl's parent chain.
const Expr *StructBase = StructAccessBase(CountDecl).Visit(Base);
@@ -1240,6 +1234,16 @@ llvm::Value *CodeGenFunction::GetCountedByFieldExprGEP(
if (!RD)
return nullptr;
+ // A bit-field count has no byte offset of its own to load from.
+ if (CountDecl->isBitField())
+ return nullptr;
+
+ // Struct GEP indices can't reach a count inside a union, because the union
+ // lowers to its largest member and the indices would walk that one instead.
+ int64_t Offset = 0;
+ if (!getFieldOffsetInBits(*this, RD, CountDecl, Offset))
+ return nullptr;
+
llvm::Value *Res = nullptr;
if (StructBase->getType()->isPointerType()) {
LValueBaseInfo BaseInfo;
@@ -1254,16 +1258,9 @@ llvm::Value *CodeGenFunction::GetCountedByFieldExprGEP(
return nullptr;
}
- RecIndicesTy Indices;
- getGEPIndicesToField(*this, RD, CountDecl, Indices);
- if (Indices.empty())
- return nullptr;
-
- Indices.push_back(Builder.getInt32(0));
- CanQualType T = CGM.getContext().getCanonicalTagType(RD);
- return Builder.CreateInBoundsGEP(ConvertType(T), Res,
- RecIndicesTy(llvm::reverse(Indices)),
- "counted_by.gep");
+ return Builder.CreateInBoundsGEP(
+ Int8Ty, Res, Builder.getSize(getContext().toCharUnitsFromBits(Offset)),
+ "counted_by.gep");
}
/// This method is typically called in contexts where we can't generate
@@ -4886,33 +4883,6 @@ struct StructFieldAccess
} // end anonymous namespace
-/// The offset of a field from the beginning of the record.
-static bool getFieldOffsetInBits(CodeGenFunction &CGF, const RecordDecl *RD,
- const FieldDecl *Field, int64_t &Offset) {
- ASTContext &Ctx = CGF.getContext();
- const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
- unsigned FieldNo = 0;
-
- for (const FieldDecl *FD : RD->fields()) {
- if (FD == Field) {
- Offset += Layout.getFieldOffset(FieldNo);
- return true;
- }
-
- QualType Ty = FD->getType();
- if (Ty->isRecordType())
- if (getFieldOffsetInBits(CGF, Ty->getAsRecordDecl(), Field, Offset)) {
- Offset += Layout.getFieldOffset(FieldNo);
- return true;
- }
-
- if (!RD->isUnion())
- ++FieldNo;
- }
-
- return false;
-}
-
/// Returns the relative offset difference between \p FD1 and \p FD2.
/// \code
/// offsetof(struct foo, FD1) - offsetof(struct foo, FD2)
diff --git a/clang/test/CodeGen/attr-counted-by-issue200014.c b/clang/test/CodeGen/attr-counted-by-issue200014.c
index e514ba18152290..6b672fdee0bf9e 100644
--- a/clang/test/CodeGen/attr-counted-by-issue200014.c
+++ b/clang/test/CodeGen/attr-counted-by-issue200014.c
@@ -84,7 +84,7 @@ extern size_t sink;
// FRONTEND-NEXT: store i64 [[TMP1]], ptr @sink, align 8
// FRONTEND-NEXT: [[FAM1:%.*]] = getelementptr inbounds nuw [[STRUCT_ANNOTATED_FLEX]], ptr [[AF]], i32 0, i32 2
// FRONTEND-NEXT: [[ARRAYDECAY2:%.*]] = getelementptr inbounds [0 x i8], ptr [[FAM1]], i64 0, i64 0
-// FRONTEND-NEXT: [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds [[STRUCT_ANNOTATED_FLEX]], ptr [[AF]], i32 0, i32 0
+// FRONTEND-NEXT: [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds i8, ptr [[AF]], i64 0
// FRONTEND-NEXT: [[COUNTED_BY_LOAD:%.*]] = load i64, ptr [[COUNTED_BY_GEP]], align 4
// FRONTEND-NEXT: [[FLEXIBLE_ARRAY_MEMBER_SIZE:%.*]] = mul nuw i64 [[COUNTED_BY_LOAD]], 1
// FRONTEND-NEXT: [[TMP2:%.*]] = icmp sgt i64 [[FLEXIBLE_ARRAY_MEMBER_SIZE]], -1
@@ -98,7 +98,7 @@ extern size_t sink;
// O2-NEXT: call void @llvm.lifetime.start.p0(ptr nonnull [[AF]]) #[[ATTR6:[0-9]+]]
// O2-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw i8, ptr [[AF]], i64 9
// O2-NEXT: [[TMP1:%.*]] = ptrtoint ptr [[TMP0]] to i64
-// O2-NEXT: store i64 [[TMP1]], ptr @sink, align 8, !tbaa [[LONG_TBAA5:![0-9]+]]
+// O2-NEXT: store i64 [[TMP1]], ptr @sink, align 8, !tbaa [[LONG_TBAA6:![0-9]+]]
// O2-NEXT: [[TMP2:%.*]] = call i64 @llvm.smax.i64(i64 [[N]], i64 0)
// O2-NEXT: call void @llvm.lifetime.end.p0(ptr nonnull [[AF]]) #[[ATTR6]]
// O2-NEXT: ret i64 [[TMP2]]
@@ -129,7 +129,7 @@ size_t local_fam(size_t n) {
// FRONTEND-NEXT: store i64 [[TMP1]], ptr @sink, align 8
// FRONTEND-NEXT: [[FAM1:%.*]] = getelementptr inbounds nuw [[STRUCT_ANNOTATED_FLEX]], ptr [[AF]], i32 0, i32 2
// FRONTEND-NEXT: [[ARRAYIDX2:%.*]] = getelementptr inbounds [0 x i8], ptr [[FAM1]], i64 0, i64 2
-// FRONTEND-NEXT: [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds [[STRUCT_ANNOTATED_FLEX]], ptr [[AF]], i32 0, i32 0
+// FRONTEND-NEXT: [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds i8, ptr [[AF]], i64 0
// FRONTEND-NEXT: [[COUNTED_BY_LOAD:%.*]] = load i64, ptr [[COUNTED_BY_GEP]], align 4
// FRONTEND-NEXT: [[FLEXIBLE_ARRAY_MEMBER_SIZE:%.*]] = mul nuw i64 [[COUNTED_BY_LOAD]], 1
// FRONTEND-NEXT: [[RESULT:%.*]] = sub nuw i64 [[FLEXIBLE_ARRAY_MEMBER_SIZE]], 2
@@ -145,7 +145,7 @@ size_t local_fam(size_t n) {
// O2-NEXT: call void @llvm.lifetime.start.p0(ptr nonnull [[AF]]) #[[ATTR6]]
// O2-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds nuw i8, ptr [[AF]], i64 11
// O2-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[ARRAYIDX]] to i64
-// O2-NEXT: store i64 [[TMP0]], ptr @sink, align 8, !tbaa [[LONG_TBAA5]]
+// O2-NEXT: store i64 [[TMP0]], ptr @sink, align 8, !tbaa [[LONG_TBAA6]]
// O2-NEXT: [[RESULT:%.*]] = add i64 [[N]], -2
// O2-NEXT: [[TMP1:%.*]] = call i64 @llvm.smax.i64(i64 [[RESULT]], i64 0)
// O2-NEXT: call void @llvm.lifetime.end.p0(ptr nonnull [[AF]]) #[[ATTR6]]
@@ -185,7 +185,7 @@ size_t local_subscript(size_t n) {
// O2-NEXT: call void @llvm.lifetime.start.p0(ptr nonnull [[AF]]) #[[ATTR6]]
// O2-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw i8, ptr [[AF]], i64 9
// O2-NEXT: [[TMP1:%.*]] = ptrtoint ptr [[TMP0]] to i64
-// O2-NEXT: store i64 [[TMP1]], ptr @sink, align 8, !tbaa [[LONG_TBAA5]]
+// O2-NEXT: store i64 [[TMP1]], ptr @sink, align 8, !tbaa [[LONG_TBAA6]]
// O2-NEXT: call void @llvm.lifetime.end.p0(ptr nonnull [[AF]]) #[[ATTR6]]
// O2-NEXT: ret i64 7
//
@@ -274,7 +274,7 @@ size_t global_addrof(void) {
// FRONTEND-NEXT: [[ENTRY:.*:]]
// FRONTEND-NEXT: [[FAM:%.*]] = getelementptr inbounds nuw [[STRUCT_ANNOTATED_FLEX:%.*]], ptr [[P]], i32 0, i32 2
// FRONTEND-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [0 x i8], ptr [[FAM]], i64 0, i64 0
-// FRONTEND-NEXT: [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds [[STRUCT_ANNOTATED_FLEX]], ptr [[P]], i32 0, i32 0
+// FRONTEND-NEXT: [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 0
// FRONTEND-NEXT: [[COUNTED_BY_LOAD:%.*]] = load i64, ptr [[COUNTED_BY_GEP]], align 4
// FRONTEND-NEXT: [[FLEXIBLE_ARRAY_MEMBER_SIZE:%.*]] = mul nuw i64 [[COUNTED_BY_LOAD]], 1
// FRONTEND-NEXT: [[TMP0:%.*]] = icmp sgt i64 [[FLEXIBLE_ARRAY_MEMBER_SIZE]], -1
@@ -300,7 +300,7 @@ size_t ptr_fam(struct annotated_flex *p) {
// FRONTEND-NEXT: [[ENTRY:.*:]]
// FRONTEND-NEXT: [[FAM:%.*]] = getelementptr inbounds nuw [[STRUCT_ANNOTATED_FLEX:%.*]], ptr [[P]], i32 0, i32 2
// FRONTEND-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [0 x i8], ptr [[FAM]], i64 0, i64 3
-// FRONTEND-NEXT: [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds [[STRUCT_ANNOTATED_FLEX]], ptr [[P]], i32 0, i32 0
+// FRONTEND-NEXT: [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 0
// FRONTEND-NEXT: [[COUNTED_BY_LOAD:%.*]] = load i64, ptr [[COUNTED_BY_GEP]], align 4
// FRONTEND-NEXT: [[FLEXIBLE_ARRAY_MEMBER_SIZE:%.*]] = mul nuw i64 [[COUNTED_BY_LOAD]], 1
// FRONTEND-NEXT: [[RESULT:%.*]] = sub nuw i64 [[FLEXIBLE_ARRAY_MEMBER_SIZE]], 3
@@ -340,8 +340,8 @@ size_t ptr_addrof(struct annotated_flex *p) {
return __builtin_dynamic_object_size(&p->fam, 1);
}
//.
-// O2: [[META3:![0-9]+]] = !{!"omnipotent char", [[META4:![0-9]+]], i64 0}
-// O2: [[META4]] = !{!"Simple C/C++ TBAA"}
-// O2: [[LONG_TBAA5]] = !{[[META6:![0-9]+]], [[META6]], i64 0}
-// O2: [[META6]] = !{!"long", [[META3]], i64 0}
+// O2: [[META4:![0-9]+]] = !{!"omnipotent char", [[META5:![0-9]+]], i64 0}
+// O2: [[META5]] = !{!"Simple C/C++ TBAA"}
+// O2: [[LONG_TBAA6]] = !{[[META7:![0-9]+]], [[META7]], i64 0}
+// O2: [[META7]] = !{!"long", [[META4]], i64 0}
//.
diff --git a/clang/test/CodeGen/attr-counted-by-nested-structs.c b/clang/test/CodeGen/attr-counted-by-nested-structs.c
index 9f715add35872f..0c9f349e82a8b5 100644
--- a/clang/test/CodeGen/attr-counted-by-nested-structs.c
+++ b/clang/test/CodeGen/attr-counted-by-nested-structs.c
@@ -43,7 +43,7 @@ struct quad_nested {
// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SINGLE_NESTED:%.*]], ptr [[TMP0]], i32 0, i32 1
// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[P_ADDR]], align 8
// CHECK-NEXT: [[BUF1:%.*]] = getelementptr inbounds nuw [[STRUCT_SINGLE_NESTED]], ptr [[TMP1]], i32 0, i32 1
-// CHECK-NEXT: [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds [[STRUCT_FLEX:%.*]], ptr [[BUF1]], i32 0, i32 0
+// CHECK-NEXT: [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds i8, ptr [[BUF1]], i64 0
// CHECK-NEXT: [[COUNTED_BY_LOAD:%.*]] = load i32, ptr [[COUNTED_BY_GEP]], align 4
// CHECK-NEXT: [[COUNT:%.*]] = zext i32 [[COUNTED_BY_LOAD]] to i64
// CHECK-NEXT: [[FLEXIBLE_ARRAY_MEMBER_SIZE:%.*]] = mul nuw i64 [[COUNT]], 1
@@ -66,7 +66,7 @@ unsigned test_size_of_single_nested(struct single_nested *p) {
// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_DOUBLE_NESTED:%.*]], ptr [[TMP0]], i32 0, i32 1
// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[P_ADDR]], align 8
// CHECK-NEXT: [[BUF1:%.*]] = getelementptr inbounds nuw [[STRUCT_DOUBLE_NESTED]], ptr [[TMP1]], i32 0, i32 1
-// CHECK-NEXT: [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds [[STRUCT_SINGLE_NESTED:%.*]], ptr [[BUF1]], i32 0, i32 1, i32 0
+// CHECK-NEXT: [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds i8, ptr [[BUF1]], i64 4
// CHECK-NEXT: [[COUNTED_BY_LOAD:%.*]] = load i32, ptr [[COUNTED_BY_GEP]], align 4
// CHECK-NEXT: [[COUNT:%.*]] = zext i32 [[COUNTED_BY_LOAD]] to i64
// CHECK-NEXT: [[FLEXIBLE_ARRAY_MEMBER_SIZE:%.*]] = mul nuw i64 [[COUNT]], 1
@@ -89,7 +89,7 @@ unsigned test_size_of_double_nested(struct double_nested *p) {
// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_TRIPLE_NESTED:%.*]], ptr [[TMP0]], i32 0, i32 1
// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[P_ADDR]], align 8
// CHECK-NEXT: [[BUF1:%.*]] = getelementptr inbounds nuw [[STRUCT_TRIPLE_NESTED]], ptr [[TMP1]], i32 0, i32 1
-// CHECK-NEXT: [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds [[STRUCT_DOUBLE_NESTED:%.*]], ptr [[BUF1]], i32 0, i32 1, i32 1, i32 0
+// CHECK-NEXT: [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds i8, ptr [[BUF1]], i64 8
// CHECK-NEXT: [[COUNTED_BY_LOAD:%.*]] = load i32, ptr [[COUNTED_BY_GEP]], align 4
// CHECK-NEXT: [[COUNT:%.*]] = zext i32 [[COUNTED_BY_LOAD]] to i64
// CHECK-NEXT: [[FLEXIBLE_ARRAY_MEMBER_SIZE:%.*]] = mul nuw i64 [[COUNT]], 1
@@ -112,7 +112,7 @@ unsigned test_size_of_triple_nested(struct triple_nested *p) {
// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_QUAD_NESTED:%.*]], ptr [[TMP0]], i32 0, i32 1
// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[P_ADDR]], align 8
// CHECK-NEXT: [[BUF1:%.*]] = getelementptr inbounds nuw [[STRUCT_QUAD_NESTED]], ptr [[TMP1]], i32 0, i32 1
-// CHECK-NEXT: [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds [[STRUCT_TRIPLE_NESTED:%.*]], ptr [[BUF1]], i32 0, i32 1, i32 1, i32 1, i32 0
+// CHECK-NEXT: [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds i8, ptr [[BUF1]], i64 12
// CHECK-NEXT: [[COUNTED_BY_LOAD:%.*]] = load i32, ptr [[COUNTED_BY_GEP]], align 4
// CHECK-NEXT: [[COUNT:%.*]] = zext i32 [[COUNTED_BY_LOAD]] to i64
// CHECK-NEXT: [[FLEXIBLE_ARRAY_MEMBER_SIZE:%.*]] = mul nuw i64 [[COUNT]], 1
diff --git a/clang/test/CodeGen/attr-counted-by-union-storage.c b/clang/test/CodeGen/attr-counted-by-union-storage.c
new file mode 100644
index 00000000000000..b05f3863dce6f9
--- /dev/null
+++ b/clang/test/CodeGen/attr-counted-by-union-storage.c
@@ -0,0 +1,47 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -O2 -emit-llvm -o - %s | FileCheck %s
+
+// The union's IR type is struct work, so num_layers has to be reached by byte
+// offset rather than through struct work's field indices.
+
+struct list {
+ struct list *next, *prev;
+};
+
+struct work {
+ long data;
+ struct list entry;
+ void (*func)(void);
+};
+
+struct domain {
+ void *rules[3];
+ void *hierarchy;
+ union {
+ struct work work_free;
+ struct {
+ int usage;
+ unsigned int num_layers;
+ unsigned int masks[] __attribute__((counted_by(num_layers)));
+ };
+ };
+};
+
+// CHECK-LABEL: define {{.*}} @test_bdos(
+// CHECK: [[GEP:%.*]] = getelementptr inbounds nuw i8, ptr %d, i64 36
+// CHECK: load i32, ptr [[GEP]]
+unsigned long test_bdos(struct domain *d) {
+ return __builtin_dynamic_object_size(d->masks, 1);
+}
+
+// A bit-field count can't be loaded by byte offset, so the size is unknown.
+struct bitfield_count {
+ unsigned int pad : 20;
+ unsigned int count : 12;
+ int fam[] __attribute__((counted_by(count)));
+};
+
+// CHECK-LABEL: define {{.*}} @test_bitfield_count(
+// CHECK: ret i64 -1
+unsigned long test_bitfield_count(struct bitfield_count *p) {
+ return __builtin_dynamic_object_size(p->fam, 1);
+}
diff --git a/clang/test/CodeGen/builtin-counted-by-ref.c b/clang/test/CodeGen/builtin-counted-by-ref.c
index 8b1ef0edb8bd9f..b79eb168e88da4 100644
--- a/clang/test/CodeGen/builtin-counted-by-ref.c
+++ b/clang/test/CodeGen/builtin-counted-by-ref.c
@@ -23,8 +23,8 @@ struct a {
// X86_64-NEXT: [[TMP1:%.*]] = load i32, ptr [[SIZE_ADDR]], align 4
// X86_64-NEXT: [[CONV1:%.*]] = trunc i32 [[TMP1]] to i16
// X86_64-NEXT: [[TMP2:%.*]] = load ptr, ptr [[P]], align 8
-// X86_64-NEXT: [[DOT_COUNTED_BY_GEP:%.*]] = getelementptr inbounds [[STRUCT_A:%.*]], ptr [[TMP2]], i32 0, i32 1
-// X86_64-NEXT: store i16 [[CONV1]], ptr [[DOT_COUNTED_BY_GEP]], align 2
+// X86_64-NEXT: [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 2
+// X86_64-NEXT: store i16 [[CONV1]], ptr [[COUNTED_BY_GEP]], align 2
// X86_64-NEXT: [[TMP3:%.*]] = load ptr, ptr [[P]], align 8
// X86_64-NEXT: ret ptr [[TMP3]]
//
@@ -42,8 +42,8 @@ struct a {
// I386-NEXT: [[TMP1:%.*]] = load i32, ptr [[SIZE_ADDR]], align 4
// I386-NEXT: [[CONV:%.*]] = trunc i32 [[TMP1]] to i16
// I386-NEXT: [[TMP2:%.*]] = load ptr, ptr [[P]], align 4
-// I386-NEXT: [[DOT_COUNTED_BY_GEP:%.*]] = getelementptr inbounds [[STRUCT_A:%.*]], ptr [[TMP2]], i32 0, i32 1
-// I386-NEXT: store i16 [[CONV]], ptr [[DOT_COUNTED_BY_GEP]], align 2
+// I386-NEXT: [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i32 2
+// I386-NEXT: store i16 [[CONV]], ptr [[COUNTED_BY_GEP]], align 2
// I386-NEXT: [[TMP3:%.*]] = load ptr, ptr [[P]], align 4
// I386-NEXT: ret ptr [[TMP3]]
//
@@ -91,8 +91,8 @@ struct b {
// X86_64-NEXT: [[TMP1:%.*]] = load i32, ptr [[SIZE_ADDR]], align 4
// X86_64-NEXT: [[CONV1:%.*]] = trunc i32 [[TMP1]] to i8
// X86_64-NEXT: [[TMP2:%.*]] = load ptr, ptr [[P]], align 8
-// X86_64-NEXT: [[DOT_COUNTED_BY_GEP:%.*]] = getelementptr inbounds [[STRUCT_B:%.*]], ptr [[TMP2]], i32 0, i32 1, i32 1, i32 1, i32 0
-// X86_64-NEXT: store i8 [[CONV1]], ptr [[DOT_COUNTED_BY_GEP]], align 1
+// X86_64-NEXT: [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 12
+// X86_64-NEXT: store i8 [[CONV1]], ptr [[COUNTED_BY_GEP]], align 1
// X86_64-NEXT: [[TMP3:%.*]] = load ptr, ptr [[P]], align 8
// X86_64-NEXT: ret ptr [[TMP3]]
//
@@ -110,8 +110,8 @@ struct b {
// I386-NEXT: [[TMP1:%.*]] = load i32, ptr [[SIZE_ADDR]], align 4
// I386-NEXT: [[CONV:%.*]] = trunc i32 [[TMP1]] to i8
// I386-NEXT: [[TMP2:%.*]] = load ptr, ptr [[P]], align 4
-// I386-NEXT: [[DOT_COUNTED_BY_GEP:%.*]] = getelementptr inbounds [[STRUCT_B:%.*]], ptr [[TMP2]], i32 0, i32 1, i32 1, i32 1, i32 0
-// I386-NEXT: store i8 [[CONV]], ptr [[DOT_COUNTED_BY_GEP]], align 1
+// I386-NEXT: [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i32 12
+// I386-NEXT: store i8 [[CONV]], ptr [[COUNTED_BY_GEP]], align 1
// I386-NEXT: [[TMP3:%.*]] = load ptr, ptr [[P]], align 4
// I386-NEXT: ret ptr [[TMP3]]
//
@@ -200,8 +200,8 @@ struct d {
// X86_64-NEXT: [[TMP2:%.*]] = load i32, ptr [[SIZE_ADDR]], align 4
// X86_64-NEXT: [[CONV:%.*]] = trunc i32 [[TMP2]] to i16
// X86_64-NEXT: [[TM...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/232138
More information about the llvm-branch-commits
mailing list