https://github.com/isanbard updated https://github.com/llvm/llvm-project/pull/205903
>From 57b81573b38b1140c93f0294cd9a1bd217d2c9ca Mon Sep 17 00:00:00 2001 From: Bill Wendling <[email protected]> Date: Wed, 24 Jun 2026 23:22:32 +0000 Subject: [PATCH 1/3] [Clang][counted_by] Use the expr's RecordDecl if available --- clang/lib/CodeGen/CGExpr.cpp | 21 ++- .../CodeGen/attr-counted-by-nested-structs.c | 127 ++++++++++++++++++ 2 files changed, 142 insertions(+), 6 deletions(-) create mode 100644 clang/test/CodeGen/attr-counted-by-nested-structs.c diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 465a020e38e74..bfe27bebbea9f 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -1204,12 +1204,21 @@ llvm::Value *CodeGenFunction::GetCountedByFieldExprGEP( // 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; + // + // The Base expression may itself be a pointer to a field of type 'struct'. If + // so, then we want to use that as the "base" RecordDecl. + QualType Ty = Base->IgnoreParenNoopCasts(getContext())->getType(); + if (Ty->isPointerType()) + Ty = Ty->getPointeeType(); + const RecordDecl *RD = Ty->getAsRecordDecl(); + if (!RD) { + 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). diff --git a/clang/test/CodeGen/attr-counted-by-nested-structs.c b/clang/test/CodeGen/attr-counted-by-nested-structs.c new file mode 100644 index 0000000000000..954764fde2c79 --- /dev/null +++ b/clang/test/CodeGen/attr-counted-by-nested-structs.c @@ -0,0 +1,127 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 6 +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wall -fstrict-flex-arrays=3 -emit-llvm -o - %s | FileCheck %s + +#if !__has_attribute(counted_by) +#error "has attribute broken" +#endif + +#define __counted_by(member) __attribute__((__counted_by__(member))) +#define __bdos(P) __builtin_dynamic_object_size(P, 0) + +struct flex { + unsigned count; + char a; /* force tail padding */ + char fam[] __counted_by(count); +}; + +struct single_nested { + int header; + struct flex buf; +}; + +struct double_nested { + int header; + struct single_nested buf; +}; + +struct triple_nested { + int header; + struct double_nested buf; +}; + +struct quad_nested { + int header; + struct triple_nested buf; +}; + +// CHECK-LABEL: define dso_local i32 @test_size_of_single_nested( +// CHECK-SAME: ptr noundef [[P:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// 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, i32 1, i32 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 +// CHECK-NEXT: [[RESULT:%.*]] = add i64 [[FLEXIBLE_ARRAY_MEMBER_SIZE]], 12 +// CHECK-NEXT: [[TMP2:%.*]] = icmp sgt i64 [[RESULT]], -1 +// CHECK-NEXT: [[TMP3:%.*]] = select i1 [[TMP2]], i64 [[RESULT]], i64 0 +// CHECK-NEXT: [[CONV:%.*]] = trunc i64 [[TMP3]] to i32 +// CHECK-NEXT: ret i32 [[CONV]] +// +unsigned test_size_of_single_nested(struct single_nested *p) { + return __bdos(&p->buf); +} + +// CHECK-LABEL: define dso_local i32 @test_size_of_double_nested( +// CHECK-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// 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, i32 1, i32 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 +// CHECK-NEXT: [[RESULT:%.*]] = add i64 [[FLEXIBLE_ARRAY_MEMBER_SIZE]], 16 +// CHECK-NEXT: [[TMP2:%.*]] = icmp sgt i64 [[RESULT]], -1 +// CHECK-NEXT: [[TMP3:%.*]] = select i1 [[TMP2]], i64 [[RESULT]], i64 0 +// CHECK-NEXT: [[CONV:%.*]] = trunc i64 [[TMP3]] to i32 +// CHECK-NEXT: ret i32 [[CONV]] +// +unsigned test_size_of_double_nested(struct double_nested *p) { + return __bdos(&p->buf); +} + +// CHECK-LABEL: define dso_local i32 @test_size_of_triple_nested( +// CHECK-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// 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, i32 1, i32 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 +// CHECK-NEXT: [[RESULT:%.*]] = add i64 [[FLEXIBLE_ARRAY_MEMBER_SIZE]], 20 +// CHECK-NEXT: [[TMP2:%.*]] = icmp sgt i64 [[RESULT]], -1 +// CHECK-NEXT: [[TMP3:%.*]] = select i1 [[TMP2]], i64 [[RESULT]], i64 0 +// CHECK-NEXT: [[CONV:%.*]] = trunc i64 [[TMP3]] to i32 +// CHECK-NEXT: ret i32 [[CONV]] +// +unsigned test_size_of_triple_nested(struct triple_nested *p) { + return __bdos(&p->buf); +} + +// CHECK-LABEL: define dso_local i32 @test_size_of_quad_nested( +// CHECK-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// 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, i32 1, i32 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 +// CHECK-NEXT: [[RESULT:%.*]] = add i64 [[FLEXIBLE_ARRAY_MEMBER_SIZE]], 24 +// CHECK-NEXT: [[TMP2:%.*]] = icmp sgt i64 [[RESULT]], -1 +// CHECK-NEXT: [[TMP3:%.*]] = select i1 [[TMP2]], i64 [[RESULT]], i64 0 +// CHECK-NEXT: [[CONV:%.*]] = trunc i64 [[TMP3]] to i32 +// CHECK-NEXT: ret i32 [[CONV]] +// +unsigned test_size_of_quad_nested(struct quad_nested *p) { + return __bdos(&p->buf); +} >From c6cb53434d035d427231f20bae6ccadc7794d256 Mon Sep 17 00:00:00 2001 From: Bill Wendling <[email protected]> Date: Thu, 25 Jun 2026 20:42:33 +0000 Subject: [PATCH 2/3] Re-update the testcase. --- .../CodeGen/attr-counted-by-nested-structs.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/clang/test/CodeGen/attr-counted-by-nested-structs.c b/clang/test/CodeGen/attr-counted-by-nested-structs.c index 954764fde2c79..9f715add35872 100644 --- a/clang/test/CodeGen/attr-counted-by-nested-structs.c +++ b/clang/test/CodeGen/attr-counted-by-nested-structs.c @@ -43,11 +43,11 @@ 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, i32 1, i32 0 +// CHECK-NEXT: [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds [[STRUCT_FLEX:%.*]], ptr [[BUF1]], i32 0, i32 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 -// CHECK-NEXT: [[RESULT:%.*]] = add i64 [[FLEXIBLE_ARRAY_MEMBER_SIZE]], 12 +// CHECK-NEXT: [[RESULT:%.*]] = add i64 [[FLEXIBLE_ARRAY_MEMBER_SIZE]], 8 // CHECK-NEXT: [[TMP2:%.*]] = icmp sgt i64 [[RESULT]], -1 // CHECK-NEXT: [[TMP3:%.*]] = select i1 [[TMP2]], i64 [[RESULT]], i64 0 // CHECK-NEXT: [[CONV:%.*]] = trunc i64 [[TMP3]] to i32 @@ -66,11 +66,11 @@ 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, i32 1, i32 0 +// CHECK-NEXT: [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds [[STRUCT_SINGLE_NESTED:%.*]], ptr [[BUF1]], i32 0, i32 1, i32 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 -// CHECK-NEXT: [[RESULT:%.*]] = add i64 [[FLEXIBLE_ARRAY_MEMBER_SIZE]], 16 +// CHECK-NEXT: [[RESULT:%.*]] = add i64 [[FLEXIBLE_ARRAY_MEMBER_SIZE]], 12 // CHECK-NEXT: [[TMP2:%.*]] = icmp sgt i64 [[RESULT]], -1 // CHECK-NEXT: [[TMP3:%.*]] = select i1 [[TMP2]], i64 [[RESULT]], i64 0 // CHECK-NEXT: [[CONV:%.*]] = trunc i64 [[TMP3]] to i32 @@ -89,11 +89,11 @@ 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, i32 1, i32 0 +// CHECK-NEXT: [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds [[STRUCT_DOUBLE_NESTED:%.*]], ptr [[BUF1]], i32 0, i32 1, i32 1, i32 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 -// CHECK-NEXT: [[RESULT:%.*]] = add i64 [[FLEXIBLE_ARRAY_MEMBER_SIZE]], 20 +// CHECK-NEXT: [[RESULT:%.*]] = add i64 [[FLEXIBLE_ARRAY_MEMBER_SIZE]], 16 // CHECK-NEXT: [[TMP2:%.*]] = icmp sgt i64 [[RESULT]], -1 // CHECK-NEXT: [[TMP3:%.*]] = select i1 [[TMP2]], i64 [[RESULT]], i64 0 // CHECK-NEXT: [[CONV:%.*]] = trunc i64 [[TMP3]] to i32 @@ -112,11 +112,11 @@ 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, i32 1, i32 0 +// 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_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 -// CHECK-NEXT: [[RESULT:%.*]] = add i64 [[FLEXIBLE_ARRAY_MEMBER_SIZE]], 24 +// CHECK-NEXT: [[RESULT:%.*]] = add i64 [[FLEXIBLE_ARRAY_MEMBER_SIZE]], 20 // CHECK-NEXT: [[TMP2:%.*]] = icmp sgt i64 [[RESULT]], -1 // CHECK-NEXT: [[TMP3:%.*]] = select i1 [[TMP2]], i64 [[RESULT]], i64 0 // CHECK-NEXT: [[CONV:%.*]] = trunc i64 [[TMP3]] to i32 >From e28546cb948c0cb909f03759997ea6f970c92a3c Mon Sep 17 00:00:00 2001 From: Bill Wendling <[email protected]> Date: Sun, 5 Jul 2026 20:33:17 -0700 Subject: [PATCH 3/3] Rework how we find the base Expr to build the GEP off of MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. recordContainsField — new static helper that checks if CountDecl is reachable from RD through any level of nesting (mirrors what getGEPIndicesToField traverses). 2. StructAccessBase — now takes const FieldDecl *CountDecl instead of const RecordDecl *ExpectedRD. IsExpectedRecordDecl uses recordContainsField so it matches any struct whose type transitively contains CountDecl, not just the one that directly holds it. 3. GetCountedByFieldExprGEP — removed the two-path RD pre-computation (extract from Base's type, fall back to CountDecl's parent + anonymous struct walk). Now just calls StructAccessBase(CountDecl).Visit(Base) and derives RD from whatever expression the visitor found. --- clang/lib/CodeGen/CGExpr.cpp | 70 +++++++++++++++++++++--------------- 1 file changed, 42 insertions(+), 28 deletions(-) diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index efc47c5bbfb1f..e53b4be55926a 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -1060,6 +1060,23 @@ static llvm::Value *getArrayIndexingBound(CodeGenFunction &CGF, namespace { +/// 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. +static bool recordContainsField(const RecordDecl *RD, + const FieldDecl *Field) { + for (const FieldDecl *FD : RD->fields()) { + if (FD == Field) + return true; + QualType Ty = FD->getType(); + if (Ty->isRecordType()) + if (recordContainsField(Ty->getAsRecordDecl(), Field)) + return true; + } + return false; +} + /// \p StructAccessBase returns the base \p Expr of a field access. It returns /// either a \p DeclRefExpr, representing the base pointer to the struct, i.e.: /// @@ -1078,17 +1095,24 @@ namespace { /// \p MemberExpr for \p p->ptr instead of \p p. class StructAccessBase : public ConstStmtVisitor<StructAccessBase, const Expr *> { - const RecordDecl *ExpectedRD; - + /// 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. + const FieldDecl *CountDecl; + + /// Returns true if E's record type (or pointee record type) transitively + /// contains CountDecl. Handles both direct containment and nested structs, + /// so we don't need a pre-computed RD from the caller. bool IsExpectedRecordDecl(const Expr *E) const { QualType Ty = E->getType(); if (Ty->isPointerType()) Ty = Ty->getPointeeType(); - return ExpectedRD == Ty->getAsRecordDecl(); + const RecordDecl *RD = Ty->getAsRecordDecl(); + return RD && recordContainsField(RD, CountDecl); } public: - StructAccessBase(const RecordDecl *ExpectedRD) : ExpectedRD(ExpectedRD) {} + StructAccessBase(const FieldDecl *CountDecl) : CountDecl(CountDecl) {} //===--------------------------------------------------------------------===// // Visitor Methods @@ -1200,33 +1224,23 @@ static bool getGEPIndicesToField(CodeGenFunction &CGF, const RecordDecl *RD, llvm::Value *CodeGenFunction::GetCountedByFieldExprGEP( const Expr *Base, const FieldDecl *FAMDecl, const FieldDecl *CountDecl) { - // 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. - // - // The Base expression may itself be a pointer to a field of type 'struct'. If - // so, then we want to use that as the "base" RecordDecl. - QualType Ty = Base->IgnoreParenNoopCasts(getContext())->getType(); - if (Ty->isPointerType()) - Ty = Ty->getPointeeType(); - const RecordDecl *RD = Ty->getAsRecordDecl(); - if (!RD) { - 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); + // 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 + // 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); if (!StructBase || StructBase->HasSideEffects(getContext())) return nullptr; + // Derive the record type from the anchor expression itself. + QualType StructTy = StructBase->getType(); + if (StructTy->isPointerType()) + StructTy = StructTy->getPointeeType(); + const RecordDecl *RD = StructTy->getAsRecordDecl(); + if (!RD) + return nullptr; + llvm::Value *Res = nullptr; if (StructBase->getType()->isPointerType()) { LValueBaseInfo BaseInfo; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
