[llvm-branch-commits] [clang] release/22.x: [Clang][CodeGen] Fix __builtin_counted_by_ref for nested struct FAMs (#182575) (#182590) (PR #182606)

via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Fri Feb 20 14:29:32 PST 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: None (llvmbot)

<details>
<summary>Changes</summary>

Backport 09a3d830a888f15abca0ca51f68786e5517cd61f

Requested by: @<!-- -->efriedma-quic

---
Full diff: https://github.com/llvm/llvm-project/pull/182606.diff


3 Files Affected:

- (modified) clang/lib/CodeGen/CGExpr.cpp (+12-1) 
- (modified) clang/test/CodeGen/attr-counted-by-pr88931.c (+6-1) 
- (modified) clang/test/CodeGen/builtin-counted-by-ref.c (+27) 


``````````diff
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 999726340aaed..4749caceb5a2c 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -1187,7 +1187,18 @@ static bool getGEPIndicesToField(CodeGenFunction &CGF, const RecordDecl *RD,
 
 llvm::Value *CodeGenFunction::GetCountedByFieldExprGEP(
     const Expr *Base, const FieldDecl *FAMDecl, const FieldDecl *CountDecl) {
-  const RecordDecl *RD = CountDecl->getParent()->getOuterLexicalRecordContext();
+  // Find the record containing the count field. Walk up through anonymous
+  // structs/unions (which are transparent in C) but stop at named records.
+  // Using getOuterLexicalRecordContext() here would be wrong because it walks
+  // past named nested structs to the outermost record, causing a crash when a
+  // struct with a counted_by FAM is defined nested inside another struct.
+  const RecordDecl *RD = CountDecl->getParent();
+  while (RD->isAnonymousStructOrUnion()) {
+    const auto *Parent = dyn_cast<RecordDecl>(RD->getLexicalParent());
+    if (!Parent)
+      break;
+    RD = Parent;
+  }
 
   // Find the base struct expr (i.e. p in p->a.b.c.d).
   const Expr *StructBase = StructAccessBase(RD).Visit(Base);
diff --git a/clang/test/CodeGen/attr-counted-by-pr88931.c b/clang/test/CodeGen/attr-counted-by-pr88931.c
index 1bd6f24461422..ded301f521f71 100644
--- a/clang/test/CodeGen/attr-counted-by-pr88931.c
+++ b/clang/test/CodeGen/attr-counted-by-pr88931.c
@@ -15,7 +15,12 @@ void init(void * __attribute__((pass_dynamic_object_size(0))));
 // CHECK-SAME: ptr noundef [[P:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] {
 // CHECK-NEXT:  entry:
 // CHECK-NEXT:    [[ARRAY:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 4
-// CHECK-NEXT:    tail call void @init(ptr noundef nonnull [[ARRAY]], i64 noundef -1) #[[ATTR2:[0-9]+]]
+// CHECK-NEXT:    [[COUNTED_BY_LOAD:%.*]] = load i32, ptr [[P]], align 4
+// CHECK-NEXT:    [[COUNT:%.*]] = sext i32 [[COUNTED_BY_LOAD]] to i64
+// CHECK-NEXT:    [[FLEXIBLE_ARRAY_MEMBER_SIZE:%.*]] = shl nsw i64 [[COUNT]], 2
+// CHECK-NEXT:    [[TMP0:%.*]] = icmp sgt i32 [[COUNTED_BY_LOAD]], -1
+// CHECK-NEXT:    [[TMP1:%.*]] = select i1 [[TMP0]], i64 [[FLEXIBLE_ARRAY_MEMBER_SIZE]], i64 0
+// CHECK-NEXT:    tail call void @init(ptr noundef nonnull [[ARRAY]], i64 noundef [[TMP1]]) #[[ATTR2:[0-9]+]]
 // CHECK-NEXT:    ret void
 //
 void test1(struct bar *p) {
diff --git a/clang/test/CodeGen/builtin-counted-by-ref.c b/clang/test/CodeGen/builtin-counted-by-ref.c
index d44dbf5d0c1a2..8b1ef0edb8bd9 100644
--- a/clang/test/CodeGen/builtin-counted-by-ref.c
+++ b/clang/test/CodeGen/builtin-counted-by-ref.c
@@ -233,3 +233,30 @@ struct d *test4(int size, int *data) {
   *__builtin_counted_by_ref(p->ptr) = size;
   return p;
 }
+
+// Test for FAM struct defined nested inside another struct (issue #182575).
+// The nested struct is named (not anonymous), so getOuterLexicalRecordContext()
+// would incorrectly resolve to 'struct outer' instead of 'struct inner'.
+struct outer {
+  struct inner {
+    int counter;
+    int ent[] __attribute__((counted_by(counter)));
+  } *entries;
+};
+
+// X86_64-LABEL: define dso_local ptr @test5(
+// X86_64-SAME: i32 noundef [[COUNT:%.*]]) #[[ATTR0]] {
+// X86_64:    [[DOT_COUNTED_BY_GEP:%.*]] = getelementptr inbounds [[STRUCT_INNER:%.*]], ptr {{%.*}}, i32 0, i32 0
+// X86_64-NEXT:    store i32 [[TMP1:%.*]], ptr [[DOT_COUNTED_BY_GEP]], align 4
+//
+// I386-LABEL: define dso_local ptr @test5(
+// I386-SAME: i32 noundef [[COUNT:%.*]]) #[[ATTR0]] {
+// I386:    [[DOT_COUNTED_BY_GEP:%.*]] = getelementptr inbounds [[STRUCT_INNER:%.*]], ptr {{%.*}}, i32 0, i32 0
+// I386-NEXT:    store i32 [[TMP1:%.*]], ptr [[DOT_COUNTED_BY_GEP]], align 4
+//
+struct inner *test5(int count) {
+  struct inner *entries = __builtin_malloc(sizeof(*entries) + count * sizeof(*entries->ent));
+  if (entries)
+    *__builtin_counted_by_ref(entries->ent) = count;
+  return entries;
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/182606


More information about the llvm-branch-commits mailing list