https://github.com/bwendling created https://github.com/llvm/llvm-project/pull/205903
The counted field could be in nested structs. This can happen when the Expr is pointing to a field that has a struct type. In that case, we want to use that struct type for the StructAccessBase visitor. Otherwise, the visitor would not find the correct struct base Expr. Fixes: 205652 >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] [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); +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
