[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-31 Thread Bill Wendling via cfe-commits

bwendling wrote:

I'll look into it.


https://github.com/llvm/llvm-project/pull/122198
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-31 Thread Nathan Chancellor via cfe-commits

nathanchance wrote:

This breaks `ARCH=arm64 allmodconfig` for me.

```
$ echo CONFIG_WERROR=n >kernel/configs/no-werror.config

$ make -skj"$(nproc)" ARCH=arm64 LLVM=1 mrproper {allmod,no-werror.}config 
drivers/net/ethernet/sfc/falcon/falcon.o
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and 
include the crash backtrace, preprocessed source, and associated run script.
Stack dump:
0.  Program arguments: 
/mnt/nvme/tmp/build/llvm-project-testing/final/bin/clang ...
1.   parser at end of file
2.  Per-file LLVM IR generation
3.  drivers/net/ethernet/sfc/falcon/falcon.c:1099:13: Generating code for 
declaration 'falcon_reconfigure_xmac_core'
4.  drivers/net/ethernet/sfc/falcon/falcon.c:1149:2 
: LLVM IR generation of 
compound statement ('{}')
 #0 0x55636ce677c8 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) 
(/mnt/nvme/tmp/build/llvm-project-testing/final/bin/clang+0x436f7c8)
 #1 0x55636ce652ee llvm::sys::RunSignalHandlers() 
(/mnt/nvme/tmp/build/llvm-project-testing/final/bin/clang+0x436d2ee)
 #2 0x55636cdea976 CrashRecoverySignalHandler(int) 
CrashRecoveryContext.cpp:0:0
 #3 0x7fe53564c1d0 (/usr/lib/libc.so.6+0x3d1d0)
 #4 0x55636d415238 
clang::CodeGen::CodeGenFunction::emitCountedByMemberSize(clang::Expr const*, 
llvm::Value*, unsigned int, llvm::IntegerType*) 
(/mnt/nvme/tmp/build/llvm-project-testing/final/bin/clang+0x491d238)
 #5 0x55636d41489f 
clang::CodeGen::CodeGenFunction::emitBuiltinObjectSize(clang::Expr const*, 
unsigned int, llvm::IntegerType*, llvm::Value*, bool) 
(/mnt/nvme/tmp/build/llvm-project-testing/final/bin/clang+0x491c89f)
 #6 0x55636d41de14 
clang::CodeGen::CodeGenFunction::EmitBuiltinExpr(clang::GlobalDecl, unsigned 
int, clang::CallExpr const*, clang::CodeGen::ReturnValueSlot) 
(/mnt/nvme/tmp/build/llvm-project-testing/final/bin/clang+0x4925e14)
 #8 0x55636d27dd2a (anonymous 
namespace)::ScalarExprEmitter::VisitCallExpr(clang::CallExpr const*) 
CGExprScalar.cpp:0:0
 #9 0x55636d26a64b (anonymous 
namespace)::ScalarExprEmitter::Visit(clang::Expr*) CGExprScalar.cpp:0:0
#10 0x55636d26a53d 
clang::CodeGen::CodeGenFunction::EmitScalarExpr(clang::Expr const*, bool) 
(/mnt/nvme/tmp/build/llvm-project-testing/final/bin/clang+0x477253d)
#11 0x55636d2b42d0 
clang::CodeGen::CodeGenFunction::EmitScalarInit(clang::Expr const*, 
clang::ValueDecl const*, clang::CodeGen::LValue, bool) 
(/mnt/nvme/tmp/build/llvm-project-testing/final/bin/clang+0x47bc2d0)
#12 0x55636d2b78dc 
clang::CodeGen::CodeGenFunction::EmitAutoVarInit(clang::CodeGen::CodeGenFunction::AutoVarEmission
 const&) (/mnt/nvme/tmp/build/llvm-project-testing/final/bin/clang+0x47bf8dc)
#13 0x55636d2b1c69 
clang::CodeGen::CodeGenFunction::EmitVarDecl(clang::VarDecl const&) 
(/mnt/nvme/tmp/build/llvm-project-testing/final/bin/clang+0x47b9c69)
#14 0x55636d2b14ef clang::CodeGen::CodeGenFunction::EmitDecl(clang::Decl 
const&) (/mnt/nvme/tmp/build/llvm-project-testing/final/bin/clang+0x47b94ef)
#15 0x55636d1e146b 
clang::CodeGen::CodeGenFunction::EmitDeclStmt(clang::DeclStmt const&) 
(/mnt/nvme/tmp/build/llvm-project-testing/final/bin/clang+0x46e946b)
#16 0x55636d1d4d53 
clang::CodeGen::CodeGenFunction::EmitSimpleStmt(clang::Stmt const*, 
llvm::ArrayRef) 
(/mnt/nvme/tmp/build/llvm-project-testing/final/bin/clang+0x46dcd53)
#17 0x55636d1d42f4 clang::CodeGen::CodeGenFunction::EmitStmt(clang::Stmt 
const*, llvm::ArrayRef) 
(/mnt/nvme/tmp/build/llvm-project-testing/final/bin/clang+0x46dc2f4)
#18 0x55636d1e2652 
clang::CodeGen::CodeGenFunction::EmitCompoundStmtWithoutScope(clang::CompoundStmt
 const&, bool, clang::CodeGen::AggValueSlot) 
(/mnt/nvme/tmp/build/llvm-project-testing/final/bin/clang+0x46ea652)
#19 0x55636d1e1399 
clang::CodeGen::CodeGenFunction::EmitCompoundStmt(clang::CompoundStmt const&, 
bool, clang::CodeGen::AggValueSlot) 
(/mnt/nvme/tmp/build/llvm-project-testing/final/bin/clang+0x46e9399)
#20 0x55636d2797e7 (anonymous 
namespace)::ScalarExprEmitter::VisitStmtExpr(clang::StmtExpr const*) 
CGExprScalar.cpp:0:0
#21 0x55636d26a53d 
clang::CodeGen::CodeGenFunction::EmitScalarExpr(clang::Expr const*, bool) 
(/mnt/nvme/tmp/build/llvm-project-testing/final/bin/clang+0x477253d)
#22 0x55636d23cb84 
clang::CodeGen::CodeGenFunction::EmitIgnoredExpr(clang::Expr const*) 
(/mnt/nvme/tmp/build/llvm-project-testing/final/bin/clang+0x4744b84)
#23 0x55636d1d43e7 clang::CodeGen::CodeGenFunction::EmitStmt(clang::Stmt 
const*, llvm::ArrayRef) 
(/mnt/nvme/tmp/build/llvm-project-testing/final/bin/clang+0x46dc3e7)
#24 0x55636d1e2830 
clang::CodeGen::CodeGenFunction::EmitCompoundStmtWithoutScope(clang::CompoundStmt
 const&, bool, clang::CodeGen::AggValueSlot) 
(/mnt/nvme/tmp/build/llvm-project-testing/final/bin/clang+0x46ea830)
#25 0x55636d1c0676 
clang::CodeGen::CodeGenFunction::GenerateCode(clang::GlobalDecl, 
llvm::Function*, clang::CodeGen::CGFunctionInfo const&) 
(/mnt/nvme/tmp/build/llvm-project-t

[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-30 Thread Bill Wendling via cfe-commits

https://github.com/bwendling closed 
https://github.com/llvm/llvm-project/pull/122198
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-29 Thread Bill Wendling via cfe-commits

bwendling wrote:

Friendly ping for any further comments.

https://github.com/llvm/llvm-project/pull/122198
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-27 Thread Bill Wendling via cfe-commits

bwendling wrote:

> I still think we may need to triple check how this extension behaves with 
> count fields that do not get extended AND are negative at runtime (if we 
> don't already have tests for that), but perhaps is orthogonal to this initial 
> refactoring.

I'll look into test cases with negative values. Thanks!

https://github.com/llvm/llvm-project/pull/122198
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-27 Thread Nick Desaulniers via cfe-commits

https://github.com/nickdesaulniers approved this pull request.

Thanks, I appreciate the comments about common usage patterns; they help make 
it easier to understand (subjectively) the 4 cases you handle in code.  I still 
think we may need to triple check how this extension behaves with count fields 
that do not get extended AND are negative at runtime (if we don't already have 
tests for that), but perhaps is orthogonal to this initial refactoring.  You 
may want to get another +1 before landing (perhaps @efriedma-quic or @rjmccall 
).

https://github.com/llvm/llvm-project/pull/122198
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-27 Thread Bill Wendling via cfe-commits

bwendling wrote:

@nickdesaulniers I understand what you're saying, and it was addressed in the 
original patch. I do my best to carry the signed-ness of the variables through 
to the end so that the checks work out (i.e. the checks for negatives aren't 
removed after optimizations, which they would be if the results were unsigned).

https://github.com/llvm/llvm-project/pull/122198
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-27 Thread Bill Wendling via cfe-commits


@@ -1049,236 +1050,362 @@ 
CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type,
   return ConstantInt::get(ResType, ObjectSize, /*isSigned=*/true);
 }
 
-const FieldDecl *CodeGenFunction::FindFlexibleArrayMemberFieldAndOffset(
-ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FAMDecl,
-uint64_t &Offset) {
+namespace {
+
+/// StructFieldAccess is a simple visitor class to grab the first MemberExpr
+/// from an Expr. It records any ArraySubscriptExpr we meet along the way.
+class StructFieldAccess
+: public ConstStmtVisitor {
+  bool AddrOfSeen = false;
+
+public:
+  const ArraySubscriptExpr *ASE = nullptr;
+
+  const MemberExpr *VisitMemberExpr(const MemberExpr *E) {
+if (AddrOfSeen && E->getType()->isArrayType())
+  // Avoid forms like '&ptr->array'.
+  return nullptr;
+return E;
+  }
+
+  const MemberExpr *VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
+if (ASE)
+  // We don't support multiple subscripts.
+  return nullptr;
+
+AddrOfSeen = false; // '&ptr->array[idx]' is okay.
+ASE = E;
+return Visit(E->getBase());
+  }
+  const MemberExpr *VisitCastExpr(const CastExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitParenExpr(const ParenExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryAddrOf(const clang::UnaryOperator *E) {
+AddrOfSeen = true;
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryDeref(const clang::UnaryOperator *E) {
+AddrOfSeen = false;
+return Visit(E->getSubExpr());
+  }
+};
+
+} // end anonymous namespace
+
+/// Find a struct's flexible array member. It may be embedded inside multiple
+/// sub-structs, but must still be the last field.
+static const FieldDecl *FindFlexibleArrayMemberField(CodeGenFunction &CGF,
+ ASTContext &Ctx,
+ const RecordDecl *RD) {
   const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
-  getLangOpts().getStrictFlexArraysLevel();
-  uint32_t FieldNo = 0;
+  CGF.getLangOpts().getStrictFlexArraysLevel();
 
   if (RD->isImplicit())
 return nullptr;
 
   for (const FieldDecl *FD : RD->fields()) {
-if ((!FAMDecl || FD == FAMDecl) &&
-Decl::isFlexibleArrayMemberLike(
+if (Decl::isFlexibleArrayMemberLike(
 Ctx, FD, FD->getType(), StrictFlexArraysLevel,
-/*IgnoreTemplateOrMacroSubstitution=*/true)) {
-  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
-  Offset += Layout.getFieldOffset(FieldNo);
+/*IgnoreTemplateOrMacroSubstitution=*/true))
   return FD;
+
+if (auto RT = FD->getType()->getAs())
+  if (const FieldDecl *FD =
+  FindFlexibleArrayMemberField(CGF, Ctx, RT->getAsRecordDecl()))
+return FD;
+  }
+
+  return nullptr;
+}
+
+/// Calculate the offset of a struct field. It may be embedded inside multiple
+/// sub-structs.
+static bool GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD,
+   const FieldDecl *FD, int64_t &Offset) {
+  if (RD->isImplicit())
+return false;
+
+  // Keep track of the field number ourselves, because the other methods
+  // (CGRecordLayout::getLLVMFieldNo) aren't always equivalent to how the AST
+  // is laid out.
+  uint32_t FieldNo = 0;
+  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+
+  for (const FieldDecl *Field : RD->fields()) {
+if (Field == FD) {
+  Offset += Layout.getFieldOffset(FieldNo);
+  return true;
 }
 
-QualType Ty = FD->getType();
-if (Ty->isRecordType()) {
-  if (const FieldDecl *Field = FindFlexibleArrayMemberFieldAndOffset(
-  Ctx, Ty->getAsRecordDecl(), FAMDecl, Offset)) {
-const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+if (auto RT = Field->getType()->getAs()) {
+  if (GetFieldOffset(Ctx, RT->getAsRecordDecl(), FD, Offset)) {
 Offset += Layout.getFieldOffset(FieldNo);
-return Field;
+return true;
   }
 }
 
 if (!RD->isUnion())
   ++FieldNo;
   }
 
-  return nullptr;
+  return false;
 }
 
-static unsigned CountCountedByAttrs(const RecordDecl *RD) {
-  unsigned Num = 0;
+static std::optional
+GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FD) {
+  int64_t Offset = 0;
 
-  for (const FieldDecl *FD : RD->fields()) {
-if (FD->getType()->isCountAttributedType())
-  return ++Num;
-
-QualType Ty = FD->getType();
-if (Ty->isRecordType())
-  Num += CountCountedByAttrs(Ty->getAsRecordDecl());
-  }
+  if (GetFieldOffset(Ctx, RD, FD, Offset))
+return std::optional(Offset);
 
-  return Num;
+  return std::nullopt;
 }
 
 llvm::Value *
-CodeGenFunction::emitFlexibleArrayMemberSize(const Expr *E, unsigned Type,
- llvm::IntegerType *ResType) {
-  // The code generat

[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-27 Thread Nick Desaulniers via cfe-commits

https://github.com/nickdesaulniers edited 
https://github.com/llvm/llvm-project/pull/122198
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-27 Thread Bill Wendling via cfe-commits

https://github.com/bwendling edited 
https://github.com/llvm/llvm-project/pull/122198
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-27 Thread Nick Desaulniers via cfe-commits


@@ -1060,236 +1061,362 @@ 
CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type,
   return ConstantInt::get(ResType, ObjectSize, /*isSigned=*/true);
 }
 
-const FieldDecl *CodeGenFunction::FindFlexibleArrayMemberFieldAndOffset(
-ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FAMDecl,
-uint64_t &Offset) {
+namespace {
+
+/// StructFieldAccess is a simple visitor class to grab the first MemberExpr
+/// from an Expr. It records any ArraySubscriptExpr we meet along the way.
+class StructFieldAccess
+: public ConstStmtVisitor {
+  bool AddrOfSeen = false;
+
+public:
+  const ArraySubscriptExpr *ASE = nullptr;
+
+  const MemberExpr *VisitMemberExpr(const MemberExpr *E) {
+if (AddrOfSeen && E->getType()->isArrayType())
+  // Avoid forms like '&ptr->array'.
+  return nullptr;
+return E;
+  }
+
+  const MemberExpr *VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
+if (ASE)
+  // We don't support multiple subscripts.
+  return nullptr;
+
+AddrOfSeen = false; // '&ptr->array[idx]' is okay.
+ASE = E;
+return Visit(E->getBase());
+  }
+  const MemberExpr *VisitCastExpr(const CastExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitParenExpr(const ParenExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryAddrOf(const clang::UnaryOperator *E) {
+AddrOfSeen = true;
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryDeref(const clang::UnaryOperator *E) {
+AddrOfSeen = false;
+return Visit(E->getSubExpr());
+  }
+};
+
+} // end anonymous namespace
+
+/// Find a struct's flexible array member. It may be embedded inside multiple
+/// sub-structs, but must still be the last field.
+static const FieldDecl *FindFlexibleArrayMemberField(CodeGenFunction &CGF,
+ ASTContext &Ctx,
+ const RecordDecl *RD) {
   const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
-  getLangOpts().getStrictFlexArraysLevel();
-  uint32_t FieldNo = 0;
+  CGF.getLangOpts().getStrictFlexArraysLevel();
 
   if (RD->isImplicit())
 return nullptr;
 
   for (const FieldDecl *FD : RD->fields()) {
-if ((!FAMDecl || FD == FAMDecl) &&
-Decl::isFlexibleArrayMemberLike(
+if (Decl::isFlexibleArrayMemberLike(
 Ctx, FD, FD->getType(), StrictFlexArraysLevel,
-/*IgnoreTemplateOrMacroSubstitution=*/true)) {
-  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
-  Offset += Layout.getFieldOffset(FieldNo);
+/*IgnoreTemplateOrMacroSubstitution=*/true))
   return FD;
+
+if (auto RT = FD->getType()->getAs())
+  if (const FieldDecl *FD =
+  FindFlexibleArrayMemberField(CGF, Ctx, RT->getAsRecordDecl()))
+return FD;
+  }
+
+  return nullptr;
+}
+
+/// Calculate the offset of a struct field. It may be embedded inside multiple
+/// sub-structs.
+static bool GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD,
+   const FieldDecl *FD, int64_t &Offset) {
+  if (RD->isImplicit())
+return false;
+
+  // Keep track of the field number ourselves, because the other methods
+  // (CGRecordLayout::getLLVMFieldNo) aren't always equivalent to how the AST
+  // is laid out.
+  uint32_t FieldNo = 0;
+  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+
+  for (const FieldDecl *Field : RD->fields()) {
+if (Field == FD) {
+  Offset += Layout.getFieldOffset(FieldNo);
+  return true;
 }
 
-QualType Ty = FD->getType();
-if (Ty->isRecordType()) {
-  if (const FieldDecl *Field = FindFlexibleArrayMemberFieldAndOffset(
-  Ctx, Ty->getAsRecordDecl(), FAMDecl, Offset)) {
-const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+if (auto RT = Field->getType()->getAs()) {
+  if (GetFieldOffset(Ctx, RT->getAsRecordDecl(), FD, Offset)) {
 Offset += Layout.getFieldOffset(FieldNo);
-return Field;
+return true;
   }
 }
 
 if (!RD->isUnion())
   ++FieldNo;
   }
 
-  return nullptr;
+  return false;
 }
 
-static unsigned CountCountedByAttrs(const RecordDecl *RD) {
-  unsigned Num = 0;
+static std::optional
+GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FD) {
+  int64_t Offset = 0;
 
-  for (const FieldDecl *FD : RD->fields()) {
-if (FD->getType()->isCountAttributedType())
-  return ++Num;
-
-QualType Ty = FD->getType();
-if (Ty->isRecordType())
-  Num += CountCountedByAttrs(Ty->getAsRecordDecl());
-  }
+  if (GetFieldOffset(Ctx, RD, FD, Offset))
+return std::optional(Offset);
 
-  return Num;
+  return std::nullopt;
 }
 
 llvm::Value *
-CodeGenFunction::emitFlexibleArrayMemberSize(const Expr *E, unsigned Type,
- llvm::IntegerType *ResType) {
-  // The code generat

[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-27 Thread Bill Wendling via cfe-commits

https://github.com/bwendling edited 
https://github.com/llvm/llvm-project/pull/122198
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-27 Thread Nick Desaulniers via cfe-commits


@@ -1049,236 +1050,362 @@ 
CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type,
   return ConstantInt::get(ResType, ObjectSize, /*isSigned=*/true);
 }
 
-const FieldDecl *CodeGenFunction::FindFlexibleArrayMemberFieldAndOffset(
-ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FAMDecl,
-uint64_t &Offset) {
+namespace {
+
+/// StructFieldAccess is a simple visitor class to grab the first MemberExpr
+/// from an Expr. It records any ArraySubscriptExpr we meet along the way.
+class StructFieldAccess
+: public ConstStmtVisitor {
+  bool AddrOfSeen = false;
+
+public:
+  const ArraySubscriptExpr *ASE = nullptr;
+
+  const MemberExpr *VisitMemberExpr(const MemberExpr *E) {
+if (AddrOfSeen && E->getType()->isArrayType())
+  // Avoid forms like '&ptr->array'.
+  return nullptr;
+return E;
+  }
+
+  const MemberExpr *VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
+if (ASE)
+  // We don't support multiple subscripts.
+  return nullptr;
+
+AddrOfSeen = false; // '&ptr->array[idx]' is okay.
+ASE = E;
+return Visit(E->getBase());
+  }
+  const MemberExpr *VisitCastExpr(const CastExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitParenExpr(const ParenExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryAddrOf(const clang::UnaryOperator *E) {
+AddrOfSeen = true;
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryDeref(const clang::UnaryOperator *E) {
+AddrOfSeen = false;
+return Visit(E->getSubExpr());
+  }
+};
+
+} // end anonymous namespace
+
+/// Find a struct's flexible array member. It may be embedded inside multiple
+/// sub-structs, but must still be the last field.
+static const FieldDecl *FindFlexibleArrayMemberField(CodeGenFunction &CGF,
+ ASTContext &Ctx,
+ const RecordDecl *RD) {
   const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
-  getLangOpts().getStrictFlexArraysLevel();
-  uint32_t FieldNo = 0;
+  CGF.getLangOpts().getStrictFlexArraysLevel();
 
   if (RD->isImplicit())
 return nullptr;
 
   for (const FieldDecl *FD : RD->fields()) {
-if ((!FAMDecl || FD == FAMDecl) &&
-Decl::isFlexibleArrayMemberLike(
+if (Decl::isFlexibleArrayMemberLike(
 Ctx, FD, FD->getType(), StrictFlexArraysLevel,
-/*IgnoreTemplateOrMacroSubstitution=*/true)) {
-  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
-  Offset += Layout.getFieldOffset(FieldNo);
+/*IgnoreTemplateOrMacroSubstitution=*/true))
   return FD;
+
+if (auto RT = FD->getType()->getAs())
+  if (const FieldDecl *FD =
+  FindFlexibleArrayMemberField(CGF, Ctx, RT->getAsRecordDecl()))
+return FD;
+  }
+
+  return nullptr;
+}
+
+/// Calculate the offset of a struct field. It may be embedded inside multiple
+/// sub-structs.
+static bool GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD,
+   const FieldDecl *FD, int64_t &Offset) {
+  if (RD->isImplicit())
+return false;
+
+  // Keep track of the field number ourselves, because the other methods
+  // (CGRecordLayout::getLLVMFieldNo) aren't always equivalent to how the AST
+  // is laid out.
+  uint32_t FieldNo = 0;
+  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+
+  for (const FieldDecl *Field : RD->fields()) {
+if (Field == FD) {
+  Offset += Layout.getFieldOffset(FieldNo);
+  return true;
 }
 
-QualType Ty = FD->getType();
-if (Ty->isRecordType()) {
-  if (const FieldDecl *Field = FindFlexibleArrayMemberFieldAndOffset(
-  Ctx, Ty->getAsRecordDecl(), FAMDecl, Offset)) {
-const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+if (auto RT = Field->getType()->getAs()) {
+  if (GetFieldOffset(Ctx, RT->getAsRecordDecl(), FD, Offset)) {
 Offset += Layout.getFieldOffset(FieldNo);
-return Field;
+return true;
   }
 }
 
 if (!RD->isUnion())
   ++FieldNo;
   }
 
-  return nullptr;
+  return false;
 }
 
-static unsigned CountCountedByAttrs(const RecordDecl *RD) {
-  unsigned Num = 0;
+static std::optional
+GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FD) {
+  int64_t Offset = 0;
 
-  for (const FieldDecl *FD : RD->fields()) {
-if (FD->getType()->isCountAttributedType())
-  return ++Num;
-
-QualType Ty = FD->getType();
-if (Ty->isRecordType())
-  Num += CountCountedByAttrs(Ty->getAsRecordDecl());
-  }
+  if (GetFieldOffset(Ctx, RD, FD, Offset))
+return std::optional(Offset);
 
-  return Num;
+  return std::nullopt;
 }
 
 llvm::Value *
-CodeGenFunction::emitFlexibleArrayMemberSize(const Expr *E, unsigned Type,
- llvm::IntegerType *ResType) {
-  // The code generat

[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-27 Thread Bill Wendling via cfe-commits


@@ -1060,236 +1061,362 @@ 
CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type,
   return ConstantInt::get(ResType, ObjectSize, /*isSigned=*/true);
 }
 
-const FieldDecl *CodeGenFunction::FindFlexibleArrayMemberFieldAndOffset(
-ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FAMDecl,
-uint64_t &Offset) {
+namespace {
+
+/// StructFieldAccess is a simple visitor class to grab the first MemberExpr
+/// from an Expr. It records any ArraySubscriptExpr we meet along the way.
+class StructFieldAccess
+: public ConstStmtVisitor {
+  bool AddrOfSeen = false;
+
+public:
+  const ArraySubscriptExpr *ASE = nullptr;
+
+  const MemberExpr *VisitMemberExpr(const MemberExpr *E) {
+if (AddrOfSeen && E->getType()->isArrayType())
+  // Avoid forms like '&ptr->array'.
+  return nullptr;
+return E;
+  }
+
+  const MemberExpr *VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
+if (ASE)
+  // We don't support multiple subscripts.
+  return nullptr;
+
+AddrOfSeen = false; // '&ptr->array[idx]' is okay.
+ASE = E;
+return Visit(E->getBase());
+  }
+  const MemberExpr *VisitCastExpr(const CastExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitParenExpr(const ParenExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryAddrOf(const clang::UnaryOperator *E) {
+AddrOfSeen = true;
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryDeref(const clang::UnaryOperator *E) {
+AddrOfSeen = false;
+return Visit(E->getSubExpr());
+  }
+};
+
+} // end anonymous namespace
+
+/// Find a struct's flexible array member. It may be embedded inside multiple
+/// sub-structs, but must still be the last field.
+static const FieldDecl *FindFlexibleArrayMemberField(CodeGenFunction &CGF,
+ ASTContext &Ctx,
+ const RecordDecl *RD) {
   const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
-  getLangOpts().getStrictFlexArraysLevel();
-  uint32_t FieldNo = 0;
+  CGF.getLangOpts().getStrictFlexArraysLevel();
 
   if (RD->isImplicit())
 return nullptr;
 
   for (const FieldDecl *FD : RD->fields()) {
-if ((!FAMDecl || FD == FAMDecl) &&
-Decl::isFlexibleArrayMemberLike(
+if (Decl::isFlexibleArrayMemberLike(
 Ctx, FD, FD->getType(), StrictFlexArraysLevel,
-/*IgnoreTemplateOrMacroSubstitution=*/true)) {
-  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
-  Offset += Layout.getFieldOffset(FieldNo);
+/*IgnoreTemplateOrMacroSubstitution=*/true))
   return FD;
+
+if (auto RT = FD->getType()->getAs())
+  if (const FieldDecl *FD =
+  FindFlexibleArrayMemberField(CGF, Ctx, RT->getAsRecordDecl()))
+return FD;
+  }
+
+  return nullptr;
+}
+
+/// Calculate the offset of a struct field. It may be embedded inside multiple
+/// sub-structs.
+static bool GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD,
+   const FieldDecl *FD, int64_t &Offset) {
+  if (RD->isImplicit())
+return false;
+
+  // Keep track of the field number ourselves, because the other methods
+  // (CGRecordLayout::getLLVMFieldNo) aren't always equivalent to how the AST
+  // is laid out.
+  uint32_t FieldNo = 0;
+  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+
+  for (const FieldDecl *Field : RD->fields()) {
+if (Field == FD) {
+  Offset += Layout.getFieldOffset(FieldNo);
+  return true;
 }
 
-QualType Ty = FD->getType();
-if (Ty->isRecordType()) {
-  if (const FieldDecl *Field = FindFlexibleArrayMemberFieldAndOffset(
-  Ctx, Ty->getAsRecordDecl(), FAMDecl, Offset)) {
-const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+if (auto RT = Field->getType()->getAs()) {
+  if (GetFieldOffset(Ctx, RT->getAsRecordDecl(), FD, Offset)) {
 Offset += Layout.getFieldOffset(FieldNo);
-return Field;
+return true;
   }
 }
 
 if (!RD->isUnion())
   ++FieldNo;
   }
 
-  return nullptr;
+  return false;
 }
 
-static unsigned CountCountedByAttrs(const RecordDecl *RD) {
-  unsigned Num = 0;
+static std::optional
+GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FD) {
+  int64_t Offset = 0;
 
-  for (const FieldDecl *FD : RD->fields()) {
-if (FD->getType()->isCountAttributedType())
-  return ++Num;
-
-QualType Ty = FD->getType();
-if (Ty->isRecordType())
-  Num += CountCountedByAttrs(Ty->getAsRecordDecl());
-  }
+  if (GetFieldOffset(Ctx, RD, FD, Offset))
+return std::optional(Offset);
 
-  return Num;
+  return std::nullopt;
 }
 
 llvm::Value *
-CodeGenFunction::emitFlexibleArrayMemberSize(const Expr *E, unsigned Type,
- llvm::IntegerType *ResType) {
-  // The code generat

[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-27 Thread Nick Desaulniers via cfe-commits


@@ -1060,236 +1061,362 @@ 
CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type,
   return ConstantInt::get(ResType, ObjectSize, /*isSigned=*/true);
 }
 
-const FieldDecl *CodeGenFunction::FindFlexibleArrayMemberFieldAndOffset(
-ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FAMDecl,
-uint64_t &Offset) {
+namespace {
+
+/// StructFieldAccess is a simple visitor class to grab the first MemberExpr
+/// from an Expr. It records any ArraySubscriptExpr we meet along the way.
+class StructFieldAccess
+: public ConstStmtVisitor {
+  bool AddrOfSeen = false;
+
+public:
+  const ArraySubscriptExpr *ASE = nullptr;
+
+  const MemberExpr *VisitMemberExpr(const MemberExpr *E) {
+if (AddrOfSeen && E->getType()->isArrayType())
+  // Avoid forms like '&ptr->array'.
+  return nullptr;
+return E;
+  }
+
+  const MemberExpr *VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
+if (ASE)
+  // We don't support multiple subscripts.
+  return nullptr;
+
+AddrOfSeen = false; // '&ptr->array[idx]' is okay.
+ASE = E;
+return Visit(E->getBase());
+  }
+  const MemberExpr *VisitCastExpr(const CastExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitParenExpr(const ParenExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryAddrOf(const clang::UnaryOperator *E) {
+AddrOfSeen = true;
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryDeref(const clang::UnaryOperator *E) {
+AddrOfSeen = false;
+return Visit(E->getSubExpr());
+  }
+};
+
+} // end anonymous namespace
+
+/// Find a struct's flexible array member. It may be embedded inside multiple
+/// sub-structs, but must still be the last field.
+static const FieldDecl *FindFlexibleArrayMemberField(CodeGenFunction &CGF,
+ ASTContext &Ctx,
+ const RecordDecl *RD) {
   const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
-  getLangOpts().getStrictFlexArraysLevel();
-  uint32_t FieldNo = 0;
+  CGF.getLangOpts().getStrictFlexArraysLevel();
 
   if (RD->isImplicit())
 return nullptr;
 
   for (const FieldDecl *FD : RD->fields()) {
-if ((!FAMDecl || FD == FAMDecl) &&
-Decl::isFlexibleArrayMemberLike(
+if (Decl::isFlexibleArrayMemberLike(
 Ctx, FD, FD->getType(), StrictFlexArraysLevel,
-/*IgnoreTemplateOrMacroSubstitution=*/true)) {
-  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
-  Offset += Layout.getFieldOffset(FieldNo);
+/*IgnoreTemplateOrMacroSubstitution=*/true))
   return FD;
+
+if (auto RT = FD->getType()->getAs())
+  if (const FieldDecl *FD =
+  FindFlexibleArrayMemberField(CGF, Ctx, RT->getAsRecordDecl()))
+return FD;
+  }
+
+  return nullptr;
+}
+
+/// Calculate the offset of a struct field. It may be embedded inside multiple
+/// sub-structs.
+static bool GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD,
+   const FieldDecl *FD, int64_t &Offset) {
+  if (RD->isImplicit())
+return false;
+
+  // Keep track of the field number ourselves, because the other methods
+  // (CGRecordLayout::getLLVMFieldNo) aren't always equivalent to how the AST
+  // is laid out.
+  uint32_t FieldNo = 0;
+  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+
+  for (const FieldDecl *Field : RD->fields()) {
+if (Field == FD) {
+  Offset += Layout.getFieldOffset(FieldNo);
+  return true;
 }
 
-QualType Ty = FD->getType();
-if (Ty->isRecordType()) {
-  if (const FieldDecl *Field = FindFlexibleArrayMemberFieldAndOffset(
-  Ctx, Ty->getAsRecordDecl(), FAMDecl, Offset)) {
-const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+if (auto RT = Field->getType()->getAs()) {
+  if (GetFieldOffset(Ctx, RT->getAsRecordDecl(), FD, Offset)) {
 Offset += Layout.getFieldOffset(FieldNo);
-return Field;
+return true;
   }
 }
 
 if (!RD->isUnion())
   ++FieldNo;
   }
 
-  return nullptr;
+  return false;
 }
 
-static unsigned CountCountedByAttrs(const RecordDecl *RD) {
-  unsigned Num = 0;
+static std::optional
+GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FD) {
+  int64_t Offset = 0;
 
-  for (const FieldDecl *FD : RD->fields()) {
-if (FD->getType()->isCountAttributedType())
-  return ++Num;
-
-QualType Ty = FD->getType();
-if (Ty->isRecordType())
-  Num += CountCountedByAttrs(Ty->getAsRecordDecl());
-  }
+  if (GetFieldOffset(Ctx, RD, FD, Offset))
+return std::optional(Offset);
 
-  return Num;
+  return std::nullopt;
 }
 
 llvm::Value *
-CodeGenFunction::emitFlexibleArrayMemberSize(const Expr *E, unsigned Type,
- llvm::IntegerType *ResType) {
-  // The code generat

[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-27 Thread Bill Wendling via cfe-commits

bwendling wrote:

Friendly Ping.

https://github.com/llvm/llvm-project/pull/122198
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-22 Thread Bill Wendling via cfe-commits

bwendling wrote:

Are there anymore comments?

https://github.com/llvm/llvm-project/pull/122198
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-15 Thread Bill Wendling via cfe-commits

https://github.com/bwendling edited 
https://github.com/llvm/llvm-project/pull/122198
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-15 Thread Bill Wendling via cfe-commits


@@ -1060,238 +1061,348 @@ 
CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type,
   return ConstantInt::get(ResType, ObjectSize, /*isSigned=*/true);
 }
 
-const FieldDecl *CodeGenFunction::FindFlexibleArrayMemberFieldAndOffset(
-ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FAMDecl,
-uint64_t &Offset) {
+namespace {
+
+/// StructFieldAccess is a simple visitor class to grab the first MemberExpr
+/// from an Expr. It records any ArraySubscriptExpr we meet along the way.
+struct StructFieldAccess
+: public ConstStmtVisitor {
+  const ArraySubscriptExpr *ASE = nullptr;
+  bool AddrOfSeen = false;
+
+  const MemberExpr *VisitMemberExpr(const MemberExpr *E) {
+if (AddrOfSeen && E->getType()->isArrayType())
+  // Avoid forms like '&ptr->array'.
+  return nullptr;
+return E;
+  }
+
+  const MemberExpr *VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
+AddrOfSeen = false; // '&ptr->array[idx]' is okay.
+ASE = E;
+return Visit(E->getBase());
+  }
+  const MemberExpr *VisitCastExpr(const CastExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitParenExpr(const ParenExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryAddrOf(const clang::UnaryOperator *E) {
+AddrOfSeen = true;
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryDeref(const clang::UnaryOperator *E) {
+AddrOfSeen = false;
+return Visit(E->getSubExpr());
+  }
+};
+
+} // end anonymous namespace
+
+/// Find a struct's flexible array member. It may be embedded inside multiple
+/// sub-structs, but must still be the last field.
+static const FieldDecl *FindFlexibleArrayMemberField(CodeGenFunction &CGF,
+ ASTContext &Ctx,
+ const RecordDecl *RD) {
   const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
-  getLangOpts().getStrictFlexArraysLevel();
-  uint32_t FieldNo = 0;
+  CGF.getLangOpts().getStrictFlexArraysLevel();
 
   if (RD->isImplicit())
 return nullptr;
 
   for (const FieldDecl *FD : RD->fields()) {
-if ((!FAMDecl || FD == FAMDecl) &&
-Decl::isFlexibleArrayMemberLike(
+if (Decl::isFlexibleArrayMemberLike(
 Ctx, FD, FD->getType(), StrictFlexArraysLevel,
-/*IgnoreTemplateOrMacroSubstitution=*/true)) {
-  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
-  Offset += Layout.getFieldOffset(FieldNo);
+/*IgnoreTemplateOrMacroSubstitution=*/true))
   return FD;
+
+if (auto RT = FD->getType()->getAs())
+  if (const FieldDecl *FD =
+  FindFlexibleArrayMemberField(CGF, Ctx, RT->getAsRecordDecl()))
+return FD;
+  }
+
+  return nullptr;
+}
+
+/// Calculate the offset of a struct field. It may be embedded inside multiple
+/// sub-structs.
+static bool GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD,
+   const FieldDecl *FD, int64_t &Offset) {
+  if (RD->isImplicit())
+return false;
+
+  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+  uint32_t FieldNo = 0;
+
+  for (const FieldDecl *Field : RD->fields()) {
+if (Field == FD) {
+  Offset += Layout.getFieldOffset(FieldNo);
+  return true;
 }
 
-QualType Ty = FD->getType();
-if (Ty->isRecordType()) {
-  if (const FieldDecl *Field = FindFlexibleArrayMemberFieldAndOffset(
-  Ctx, Ty->getAsRecordDecl(), FAMDecl, Offset)) {
-const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+if (auto RT = Field->getType()->getAs()) {
+  if (GetFieldOffset(Ctx, RT->getAsRecordDecl(), FD, Offset)) {
 Offset += Layout.getFieldOffset(FieldNo);
-return Field;
+return true;
   }
 }
 
 if (!RD->isUnion())
   ++FieldNo;
   }
 
-  return nullptr;
+  return false;
 }
 
-static unsigned CountCountedByAttrs(const RecordDecl *RD) {
-  unsigned Num = 0;
+llvm::Value *
+CodeGenFunction::emitCountedByMemberSize(const Expr *E, llvm::Value *EmittedE,
+ unsigned Type,
+ llvm::IntegerType *ResType) {
+  ASTContext &Ctx = getContext();
 
-  for (const FieldDecl *FD : RD->fields()) {
-if (FD->getType()->isCountAttributedType())
-  return ++Num;
+  // Note: If the whole struct is specificed in the __bdos (i.e. Visitor
+  // returns a DeclRefExpr). The calculation of the whole size of the structure
+  // with a flexible array member can be done in two ways:
+  //
+  // 1) sizeof(struct S) + count * sizeof(typeof(fam))
+  // 2) offsetof(struct S, fam) + count * sizeof(typeof(fam))
+  //
+  // The first will add additional padding after the end of the array
+  // allocation while the second method is more precise, but not quite expected
+  // from programmers. See
+  // https://lore.kernel.org/lkml/ZvV6X5

[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-15 Thread Bill Wendling via cfe-commits


@@ -1060,236 +1061,358 @@ 
CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type,
   return ConstantInt::get(ResType, ObjectSize, /*isSigned=*/true);
 }
 
-const FieldDecl *CodeGenFunction::FindFlexibleArrayMemberFieldAndOffset(
-ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FAMDecl,
-uint64_t &Offset) {
+namespace {
+
+/// StructFieldAccess is a simple visitor class to grab the first MemberExpr
+/// from an Expr. It records any ArraySubscriptExpr we meet along the way.
+class StructFieldAccess
+: public ConstStmtVisitor {
+  bool AddrOfSeen = false;
+
+public:
+  const ArraySubscriptExpr *ASE = nullptr;
+
+  const MemberExpr *VisitMemberExpr(const MemberExpr *E) {
+if (AddrOfSeen && E->getType()->isArrayType())
+  // Avoid forms like '&ptr->array'.
+  return nullptr;
+return E;
+  }
+
+  const MemberExpr *VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
+AddrOfSeen = false; // '&ptr->array[idx]' is okay.
+ASE = E;
+return Visit(E->getBase());
+  }
+  const MemberExpr *VisitCastExpr(const CastExpr *E) {
+return Visit(E->getSubExpr());

bwendling wrote:

Okay, so what do I do with an lvalue-to-rvalue cast when I encounter it?

As I explained in the comment, I'm using the already emitted `Expr` to 
calculate the offset to the 'count' object. To get that, all I need is the 
`MemberExpr` of the flexible array member.

https://github.com/llvm/llvm-project/pull/122198
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-15 Thread Bill Wendling via cfe-commits


@@ -1060,236 +1061,358 @@ 
CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type,
   return ConstantInt::get(ResType, ObjectSize, /*isSigned=*/true);
 }
 
-const FieldDecl *CodeGenFunction::FindFlexibleArrayMemberFieldAndOffset(
-ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FAMDecl,
-uint64_t &Offset) {
+namespace {
+
+/// StructFieldAccess is a simple visitor class to grab the first MemberExpr
+/// from an Expr. It records any ArraySubscriptExpr we meet along the way.
+class StructFieldAccess
+: public ConstStmtVisitor {
+  bool AddrOfSeen = false;
+
+public:
+  const ArraySubscriptExpr *ASE = nullptr;
+
+  const MemberExpr *VisitMemberExpr(const MemberExpr *E) {
+if (AddrOfSeen && E->getType()->isArrayType())
+  // Avoid forms like '&ptr->array'.
+  return nullptr;
+return E;
+  }
+
+  const MemberExpr *VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
+AddrOfSeen = false; // '&ptr->array[idx]' is okay.
+ASE = E;

bwendling wrote:

It looks like GCC returns -1 in this situation. I'll have us do the same.

https://github.com/llvm/llvm-project/pull/122198
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-15 Thread Jannik Glückert via cfe-commits


@@ -1060,238 +1061,348 @@ 
CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type,
   return ConstantInt::get(ResType, ObjectSize, /*isSigned=*/true);
 }
 
-const FieldDecl *CodeGenFunction::FindFlexibleArrayMemberFieldAndOffset(
-ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FAMDecl,
-uint64_t &Offset) {
+namespace {
+
+/// StructFieldAccess is a simple visitor class to grab the first MemberExpr
+/// from an Expr. It records any ArraySubscriptExpr we meet along the way.
+struct StructFieldAccess
+: public ConstStmtVisitor {
+  const ArraySubscriptExpr *ASE = nullptr;
+  bool AddrOfSeen = false;
+
+  const MemberExpr *VisitMemberExpr(const MemberExpr *E) {
+if (AddrOfSeen && E->getType()->isArrayType())
+  // Avoid forms like '&ptr->array'.
+  return nullptr;
+return E;
+  }
+
+  const MemberExpr *VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
+AddrOfSeen = false; // '&ptr->array[idx]' is okay.
+ASE = E;
+return Visit(E->getBase());
+  }
+  const MemberExpr *VisitCastExpr(const CastExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitParenExpr(const ParenExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryAddrOf(const clang::UnaryOperator *E) {
+AddrOfSeen = true;
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryDeref(const clang::UnaryOperator *E) {
+AddrOfSeen = false;
+return Visit(E->getSubExpr());
+  }
+};
+
+} // end anonymous namespace
+
+/// Find a struct's flexible array member. It may be embedded inside multiple
+/// sub-structs, but must still be the last field.
+static const FieldDecl *FindFlexibleArrayMemberField(CodeGenFunction &CGF,
+ ASTContext &Ctx,
+ const RecordDecl *RD) {
   const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
-  getLangOpts().getStrictFlexArraysLevel();
-  uint32_t FieldNo = 0;
+  CGF.getLangOpts().getStrictFlexArraysLevel();
 
   if (RD->isImplicit())
 return nullptr;
 
   for (const FieldDecl *FD : RD->fields()) {
-if ((!FAMDecl || FD == FAMDecl) &&
-Decl::isFlexibleArrayMemberLike(
+if (Decl::isFlexibleArrayMemberLike(
 Ctx, FD, FD->getType(), StrictFlexArraysLevel,
-/*IgnoreTemplateOrMacroSubstitution=*/true)) {
-  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
-  Offset += Layout.getFieldOffset(FieldNo);
+/*IgnoreTemplateOrMacroSubstitution=*/true))
   return FD;
+
+if (auto RT = FD->getType()->getAs())
+  if (const FieldDecl *FD =
+  FindFlexibleArrayMemberField(CGF, Ctx, RT->getAsRecordDecl()))
+return FD;
+  }
+
+  return nullptr;
+}
+
+/// Calculate the offset of a struct field. It may be embedded inside multiple
+/// sub-structs.
+static bool GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD,
+   const FieldDecl *FD, int64_t &Offset) {
+  if (RD->isImplicit())
+return false;
+
+  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+  uint32_t FieldNo = 0;
+
+  for (const FieldDecl *Field : RD->fields()) {
+if (Field == FD) {
+  Offset += Layout.getFieldOffset(FieldNo);
+  return true;
 }
 
-QualType Ty = FD->getType();
-if (Ty->isRecordType()) {
-  if (const FieldDecl *Field = FindFlexibleArrayMemberFieldAndOffset(
-  Ctx, Ty->getAsRecordDecl(), FAMDecl, Offset)) {
-const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+if (auto RT = Field->getType()->getAs()) {
+  if (GetFieldOffset(Ctx, RT->getAsRecordDecl(), FD, Offset)) {
 Offset += Layout.getFieldOffset(FieldNo);
-return Field;
+return true;
   }
 }
 
 if (!RD->isUnion())
   ++FieldNo;
   }
 
-  return nullptr;
+  return false;
 }
 
-static unsigned CountCountedByAttrs(const RecordDecl *RD) {
-  unsigned Num = 0;
+llvm::Value *
+CodeGenFunction::emitCountedByMemberSize(const Expr *E, llvm::Value *EmittedE,
+ unsigned Type,
+ llvm::IntegerType *ResType) {
+  ASTContext &Ctx = getContext();
 
-  for (const FieldDecl *FD : RD->fields()) {
-if (FD->getType()->isCountAttributedType())
-  return ++Num;
+  // Note: If the whole struct is specificed in the __bdos (i.e. Visitor
+  // returns a DeclRefExpr). The calculation of the whole size of the structure
+  // with a flexible array member can be done in two ways:
+  //
+  // 1) sizeof(struct S) + count * sizeof(typeof(fam))
+  // 2) offsetof(struct S, fam) + count * sizeof(typeof(fam))
+  //
+  // The first will add additional padding after the end of the array
+  // allocation while the second method is more precise, but not quite expected
+  // from programmers. See
+  // https://lore.kernel.org/lkml/ZvV6X5

[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-15 Thread Eli Friedman via cfe-commits


@@ -1060,236 +1061,358 @@ 
CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type,
   return ConstantInt::get(ResType, ObjectSize, /*isSigned=*/true);
 }
 
-const FieldDecl *CodeGenFunction::FindFlexibleArrayMemberFieldAndOffset(
-ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FAMDecl,
-uint64_t &Offset) {
+namespace {
+
+/// StructFieldAccess is a simple visitor class to grab the first MemberExpr
+/// from an Expr. It records any ArraySubscriptExpr we meet along the way.
+class StructFieldAccess
+: public ConstStmtVisitor {
+  bool AddrOfSeen = false;
+
+public:
+  const ArraySubscriptExpr *ASE = nullptr;
+
+  const MemberExpr *VisitMemberExpr(const MemberExpr *E) {
+if (AddrOfSeen && E->getType()->isArrayType())
+  // Avoid forms like '&ptr->array'.
+  return nullptr;
+return E;
+  }
+
+  const MemberExpr *VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
+AddrOfSeen = false; // '&ptr->array[idx]' is okay.
+ASE = E;
+return Visit(E->getBase());
+  }
+  const MemberExpr *VisitCastExpr(const CastExpr *E) {
+return Visit(E->getSubExpr());

efriedma-quic wrote:

Like I've mentioned before, I generally prefer explicitly classifying casts by 
CastKind, instead of ignoring the casts, then trying to infer what casts 
happened later. In particular, you do not want to look through lvalue-to-rvalue 
conversions in this context.

https://github.com/llvm/llvm-project/pull/122198
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-15 Thread Eli Friedman via cfe-commits


@@ -1060,236 +1061,358 @@ 
CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type,
   return ConstantInt::get(ResType, ObjectSize, /*isSigned=*/true);
 }
 
-const FieldDecl *CodeGenFunction::FindFlexibleArrayMemberFieldAndOffset(
-ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FAMDecl,
-uint64_t &Offset) {
+namespace {
+
+/// StructFieldAccess is a simple visitor class to grab the first MemberExpr
+/// from an Expr. It records any ArraySubscriptExpr we meet along the way.
+class StructFieldAccess
+: public ConstStmtVisitor {
+  bool AddrOfSeen = false;
+
+public:
+  const ArraySubscriptExpr *ASE = nullptr;
+
+  const MemberExpr *VisitMemberExpr(const MemberExpr *E) {
+if (AddrOfSeen && E->getType()->isArrayType())
+  // Avoid forms like '&ptr->array'.
+  return nullptr;
+return E;
+  }
+
+  const MemberExpr *VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
+AddrOfSeen = false; // '&ptr->array[idx]' is okay.
+ASE = E;

efriedma-quic wrote:

What happens if you have `&ptr->array2d[idx1][idx2]`?

https://github.com/llvm/llvm-project/pull/122198
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-15 Thread Bill Wendling via cfe-commits


@@ -1060,236 +1061,355 @@ 
CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type,
   return ConstantInt::get(ResType, ObjectSize, /*isSigned=*/true);
 }
 
-const FieldDecl *CodeGenFunction::FindFlexibleArrayMemberFieldAndOffset(
-ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FAMDecl,
-uint64_t &Offset) {
+namespace {
+
+/// StructFieldAccess is a simple visitor class to grab the first MemberExpr
+/// from an Expr. It records any ArraySubscriptExpr we meet along the way.
+class StructFieldAccess
+: public ConstStmtVisitor {
+  bool AddrOfSeen = false;
+
+public:
+  const ArraySubscriptExpr *ASE = nullptr;
+
+  const MemberExpr *VisitMemberExpr(const MemberExpr *E) {
+if (AddrOfSeen && E->getType()->isArrayType())
+  // Avoid forms like '&ptr->array'.
+  return nullptr;
+return E;
+  }
+
+  const MemberExpr *VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
+AddrOfSeen = false; // '&ptr->array[idx]' is okay.
+ASE = E;
+return Visit(E->getBase());
+  }
+  const MemberExpr *VisitCastExpr(const CastExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitParenExpr(const ParenExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryAddrOf(const clang::UnaryOperator *E) {
+AddrOfSeen = true;
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryDeref(const clang::UnaryOperator *E) {
+AddrOfSeen = false;
+return Visit(E->getSubExpr());
+  }
+};
+
+} // end anonymous namespace
+
+/// Find a struct's flexible array member. It may be embedded inside multiple
+/// sub-structs, but must still be the last field.
+static const FieldDecl *FindFlexibleArrayMemberField(CodeGenFunction &CGF,
+ ASTContext &Ctx,
+ const RecordDecl *RD) {
   const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
-  getLangOpts().getStrictFlexArraysLevel();
-  uint32_t FieldNo = 0;
+  CGF.getLangOpts().getStrictFlexArraysLevel();
 
   if (RD->isImplicit())
 return nullptr;
 
   for (const FieldDecl *FD : RD->fields()) {
-if ((!FAMDecl || FD == FAMDecl) &&
-Decl::isFlexibleArrayMemberLike(
+if (Decl::isFlexibleArrayMemberLike(
 Ctx, FD, FD->getType(), StrictFlexArraysLevel,
-/*IgnoreTemplateOrMacroSubstitution=*/true)) {
-  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
-  Offset += Layout.getFieldOffset(FieldNo);
+/*IgnoreTemplateOrMacroSubstitution=*/true))
   return FD;
+
+if (auto RT = FD->getType()->getAs())
+  if (const FieldDecl *FD =
+  FindFlexibleArrayMemberField(CGF, Ctx, RT->getAsRecordDecl()))
+return FD;
+  }
+
+  return nullptr;
+}
+
+/// Calculate the offset of a struct field. It may be embedded inside multiple
+/// sub-structs.
+static bool GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD,
+   const FieldDecl *FD, int64_t &Offset) {
+  if (RD->isImplicit())
+return false;
+
+  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+  uint32_t FieldNo = 0;
+
+  for (const FieldDecl *Field : RD->fields()) {
+if (Field == FD) {
+  Offset += Layout.getFieldOffset(FieldNo);
+  return true;
 }
 
-QualType Ty = FD->getType();
-if (Ty->isRecordType()) {
-  if (const FieldDecl *Field = FindFlexibleArrayMemberFieldAndOffset(
-  Ctx, Ty->getAsRecordDecl(), FAMDecl, Offset)) {
-const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+if (auto RT = Field->getType()->getAs()) {
+  if (GetFieldOffset(Ctx, RT->getAsRecordDecl(), FD, Offset)) {
 Offset += Layout.getFieldOffset(FieldNo);
-return Field;
+return true;
   }
 }
 
 if (!RD->isUnion())
   ++FieldNo;
   }
 
-  return nullptr;
+  return false;
 }
 
-static unsigned CountCountedByAttrs(const RecordDecl *RD) {
-  unsigned Num = 0;
-
-  for (const FieldDecl *FD : RD->fields()) {
-if (FD->getType()->isCountAttributedType())
-  return ++Num;
+static std::optional
+GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FD) {
+  int64_t Offset = 0;
 
-QualType Ty = FD->getType();
-if (Ty->isRecordType())
-  Num += CountCountedByAttrs(Ty->getAsRecordDecl());
-  }
+  if (GetFieldOffset(Ctx, RD, FD, Offset))
+return std::optional(Offset);
 
-  return Num;
+  return std::optional();
 }
 
 llvm::Value *
-CodeGenFunction::emitFlexibleArrayMemberSize(const Expr *E, unsigned Type,
- llvm::IntegerType *ResType) {
-  // The code generated here calculates the size of a struct with a flexible
-  // array member that uses the counted_by attribute. There are two instances
-  // we handle:
-  //
-  //   struct s {
-  // unsigned long flags;
-  // int count;
-  // i

[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-15 Thread Bill Wendling via cfe-commits


@@ -1060,238 +1061,348 @@ 
CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type,
   return ConstantInt::get(ResType, ObjectSize, /*isSigned=*/true);
 }
 
-const FieldDecl *CodeGenFunction::FindFlexibleArrayMemberFieldAndOffset(
-ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FAMDecl,
-uint64_t &Offset) {
+namespace {
+
+/// StructFieldAccess is a simple visitor class to grab the first MemberExpr
+/// from an Expr. It records any ArraySubscriptExpr we meet along the way.
+class StructFieldAccess
+: public ConstStmtVisitor {
+  bool AddrOfSeen = false;
+
+public:
+  const ArraySubscriptExpr *ASE = nullptr;
+
+  const MemberExpr *VisitMemberExpr(const MemberExpr *E) {
+if (AddrOfSeen && E->getType()->isArrayType())
+  // Avoid forms like '&ptr->array'.
+  return nullptr;
+return E;
+  }
+
+  const MemberExpr *VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
+AddrOfSeen = false; // '&ptr->array[idx]' is okay.
+ASE = E;
+return Visit(E->getBase());
+  }
+  const MemberExpr *VisitCastExpr(const CastExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitParenExpr(const ParenExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryAddrOf(const clang::UnaryOperator *E) {
+AddrOfSeen = true;
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryDeref(const clang::UnaryOperator *E) {
+AddrOfSeen = false;
+return Visit(E->getSubExpr());
+  }
+};
+
+} // end anonymous namespace
+
+/// Find a struct's flexible array member. It may be embedded inside multiple
+/// sub-structs, but must still be the last field.
+static const FieldDecl *FindFlexibleArrayMemberField(CodeGenFunction &CGF,
+ ASTContext &Ctx,
+ const RecordDecl *RD) {
   const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
-  getLangOpts().getStrictFlexArraysLevel();
-  uint32_t FieldNo = 0;
+  CGF.getLangOpts().getStrictFlexArraysLevel();
 
   if (RD->isImplicit())
 return nullptr;
 
   for (const FieldDecl *FD : RD->fields()) {
-if ((!FAMDecl || FD == FAMDecl) &&
-Decl::isFlexibleArrayMemberLike(
+if (Decl::isFlexibleArrayMemberLike(
 Ctx, FD, FD->getType(), StrictFlexArraysLevel,
-/*IgnoreTemplateOrMacroSubstitution=*/true)) {
-  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
-  Offset += Layout.getFieldOffset(FieldNo);
+/*IgnoreTemplateOrMacroSubstitution=*/true))
   return FD;
+
+if (auto RT = FD->getType()->getAs())
+  if (const FieldDecl *FD =
+  FindFlexibleArrayMemberField(CGF, Ctx, RT->getAsRecordDecl()))
+return FD;
+  }
+
+  return nullptr;
+}
+
+/// Calculate the offset of a struct field. It may be embedded inside multiple
+/// sub-structs.
+static bool GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD,
+   const FieldDecl *FD, int64_t &Offset) {
+  if (RD->isImplicit())
+return false;
+
+  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+  uint32_t FieldNo = 0;

bwendling wrote:

Okay.

https://github.com/llvm/llvm-project/pull/122198
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-15 Thread Bill Wendling via cfe-commits


@@ -1060,236 +1061,355 @@ 
CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type,
   return ConstantInt::get(ResType, ObjectSize, /*isSigned=*/true);
 }
 
-const FieldDecl *CodeGenFunction::FindFlexibleArrayMemberFieldAndOffset(
-ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FAMDecl,
-uint64_t &Offset) {
+namespace {
+
+/// StructFieldAccess is a simple visitor class to grab the first MemberExpr
+/// from an Expr. It records any ArraySubscriptExpr we meet along the way.
+class StructFieldAccess
+: public ConstStmtVisitor {
+  bool AddrOfSeen = false;
+
+public:
+  const ArraySubscriptExpr *ASE = nullptr;
+
+  const MemberExpr *VisitMemberExpr(const MemberExpr *E) {
+if (AddrOfSeen && E->getType()->isArrayType())
+  // Avoid forms like '&ptr->array'.
+  return nullptr;
+return E;
+  }
+
+  const MemberExpr *VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
+AddrOfSeen = false; // '&ptr->array[idx]' is okay.
+ASE = E;
+return Visit(E->getBase());
+  }
+  const MemberExpr *VisitCastExpr(const CastExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitParenExpr(const ParenExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryAddrOf(const clang::UnaryOperator *E) {
+AddrOfSeen = true;
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryDeref(const clang::UnaryOperator *E) {
+AddrOfSeen = false;
+return Visit(E->getSubExpr());
+  }
+};
+
+} // end anonymous namespace
+
+/// Find a struct's flexible array member. It may be embedded inside multiple
+/// sub-structs, but must still be the last field.
+static const FieldDecl *FindFlexibleArrayMemberField(CodeGenFunction &CGF,
+ ASTContext &Ctx,
+ const RecordDecl *RD) {
   const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
-  getLangOpts().getStrictFlexArraysLevel();
-  uint32_t FieldNo = 0;
+  CGF.getLangOpts().getStrictFlexArraysLevel();
 
   if (RD->isImplicit())
 return nullptr;
 
   for (const FieldDecl *FD : RD->fields()) {
-if ((!FAMDecl || FD == FAMDecl) &&
-Decl::isFlexibleArrayMemberLike(
+if (Decl::isFlexibleArrayMemberLike(
 Ctx, FD, FD->getType(), StrictFlexArraysLevel,
-/*IgnoreTemplateOrMacroSubstitution=*/true)) {
-  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
-  Offset += Layout.getFieldOffset(FieldNo);
+/*IgnoreTemplateOrMacroSubstitution=*/true))
   return FD;
+
+if (auto RT = FD->getType()->getAs())
+  if (const FieldDecl *FD =
+  FindFlexibleArrayMemberField(CGF, Ctx, RT->getAsRecordDecl()))
+return FD;
+  }
+
+  return nullptr;
+}
+
+/// Calculate the offset of a struct field. It may be embedded inside multiple
+/// sub-structs.
+static bool GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD,
+   const FieldDecl *FD, int64_t &Offset) {
+  if (RD->isImplicit())
+return false;
+
+  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+  uint32_t FieldNo = 0;
+
+  for (const FieldDecl *Field : RD->fields()) {
+if (Field == FD) {
+  Offset += Layout.getFieldOffset(FieldNo);
+  return true;
 }
 
-QualType Ty = FD->getType();
-if (Ty->isRecordType()) {
-  if (const FieldDecl *Field = FindFlexibleArrayMemberFieldAndOffset(
-  Ctx, Ty->getAsRecordDecl(), FAMDecl, Offset)) {
-const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+if (auto RT = Field->getType()->getAs()) {
+  if (GetFieldOffset(Ctx, RT->getAsRecordDecl(), FD, Offset)) {
 Offset += Layout.getFieldOffset(FieldNo);
-return Field;
+return true;
   }
 }
 
 if (!RD->isUnion())
   ++FieldNo;
   }
 
-  return nullptr;
+  return false;
 }
 
-static unsigned CountCountedByAttrs(const RecordDecl *RD) {
-  unsigned Num = 0;
-
-  for (const FieldDecl *FD : RD->fields()) {
-if (FD->getType()->isCountAttributedType())
-  return ++Num;
+static std::optional
+GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FD) {
+  int64_t Offset = 0;
 
-QualType Ty = FD->getType();
-if (Ty->isRecordType())
-  Num += CountCountedByAttrs(Ty->getAsRecordDecl());
-  }
+  if (GetFieldOffset(Ctx, RD, FD, Offset))
+return std::optional(Offset);
 
-  return Num;
+  return std::optional();
 }
 
 llvm::Value *
-CodeGenFunction::emitFlexibleArrayMemberSize(const Expr *E, unsigned Type,
- llvm::IntegerType *ResType) {
-  // The code generated here calculates the size of a struct with a flexible
-  // array member that uses the counted_by attribute. There are two instances
-  // we handle:
-  //
-  //   struct s {
-  // unsigned long flags;
-  // int count;
-  // i

[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-15 Thread Bill Wendling via cfe-commits


@@ -1060,236 +1061,355 @@ 
CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type,
   return ConstantInt::get(ResType, ObjectSize, /*isSigned=*/true);
 }
 
-const FieldDecl *CodeGenFunction::FindFlexibleArrayMemberFieldAndOffset(
-ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FAMDecl,
-uint64_t &Offset) {
+namespace {
+
+/// StructFieldAccess is a simple visitor class to grab the first MemberExpr
+/// from an Expr. It records any ArraySubscriptExpr we meet along the way.
+class StructFieldAccess
+: public ConstStmtVisitor {
+  bool AddrOfSeen = false;
+
+public:
+  const ArraySubscriptExpr *ASE = nullptr;
+
+  const MemberExpr *VisitMemberExpr(const MemberExpr *E) {
+if (AddrOfSeen && E->getType()->isArrayType())
+  // Avoid forms like '&ptr->array'.
+  return nullptr;
+return E;
+  }
+
+  const MemberExpr *VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
+AddrOfSeen = false; // '&ptr->array[idx]' is okay.
+ASE = E;
+return Visit(E->getBase());
+  }
+  const MemberExpr *VisitCastExpr(const CastExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitParenExpr(const ParenExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryAddrOf(const clang::UnaryOperator *E) {
+AddrOfSeen = true;
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryDeref(const clang::UnaryOperator *E) {
+AddrOfSeen = false;
+return Visit(E->getSubExpr());
+  }
+};
+
+} // end anonymous namespace
+
+/// Find a struct's flexible array member. It may be embedded inside multiple
+/// sub-structs, but must still be the last field.
+static const FieldDecl *FindFlexibleArrayMemberField(CodeGenFunction &CGF,
+ ASTContext &Ctx,
+ const RecordDecl *RD) {
   const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
-  getLangOpts().getStrictFlexArraysLevel();
-  uint32_t FieldNo = 0;
+  CGF.getLangOpts().getStrictFlexArraysLevel();
 
   if (RD->isImplicit())
 return nullptr;
 
   for (const FieldDecl *FD : RD->fields()) {
-if ((!FAMDecl || FD == FAMDecl) &&
-Decl::isFlexibleArrayMemberLike(
+if (Decl::isFlexibleArrayMemberLike(
 Ctx, FD, FD->getType(), StrictFlexArraysLevel,
-/*IgnoreTemplateOrMacroSubstitution=*/true)) {
-  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
-  Offset += Layout.getFieldOffset(FieldNo);
+/*IgnoreTemplateOrMacroSubstitution=*/true))
   return FD;
+
+if (auto RT = FD->getType()->getAs())
+  if (const FieldDecl *FD =
+  FindFlexibleArrayMemberField(CGF, Ctx, RT->getAsRecordDecl()))
+return FD;
+  }
+
+  return nullptr;
+}
+
+/// Calculate the offset of a struct field. It may be embedded inside multiple
+/// sub-structs.
+static bool GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD,
+   const FieldDecl *FD, int64_t &Offset) {
+  if (RD->isImplicit())
+return false;
+
+  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+  uint32_t FieldNo = 0;
+
+  for (const FieldDecl *Field : RD->fields()) {
+if (Field == FD) {
+  Offset += Layout.getFieldOffset(FieldNo);
+  return true;
 }
 
-QualType Ty = FD->getType();
-if (Ty->isRecordType()) {
-  if (const FieldDecl *Field = FindFlexibleArrayMemberFieldAndOffset(
-  Ctx, Ty->getAsRecordDecl(), FAMDecl, Offset)) {
-const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+if (auto RT = Field->getType()->getAs()) {
+  if (GetFieldOffset(Ctx, RT->getAsRecordDecl(), FD, Offset)) {
 Offset += Layout.getFieldOffset(FieldNo);
-return Field;
+return true;
   }
 }
 
 if (!RD->isUnion())
   ++FieldNo;
   }
 
-  return nullptr;
+  return false;
 }
 
-static unsigned CountCountedByAttrs(const RecordDecl *RD) {
-  unsigned Num = 0;
-
-  for (const FieldDecl *FD : RD->fields()) {
-if (FD->getType()->isCountAttributedType())
-  return ++Num;
+static std::optional
+GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FD) {
+  int64_t Offset = 0;
 
-QualType Ty = FD->getType();
-if (Ty->isRecordType())
-  Num += CountCountedByAttrs(Ty->getAsRecordDecl());
-  }
+  if (GetFieldOffset(Ctx, RD, FD, Offset))
+return std::optional(Offset);
 
-  return Num;
+  return std::optional();
 }
 
 llvm::Value *
-CodeGenFunction::emitFlexibleArrayMemberSize(const Expr *E, unsigned Type,
- llvm::IntegerType *ResType) {
-  // The code generated here calculates the size of a struct with a flexible
-  // array member that uses the counted_by attribute. There are two instances
-  // we handle:
-  //
-  //   struct s {
-  // unsigned long flags;
-  // int count;
-  // i

[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-15 Thread Bill Wendling via cfe-commits


@@ -1060,236 +1061,355 @@ 
CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type,
   return ConstantInt::get(ResType, ObjectSize, /*isSigned=*/true);
 }
 
-const FieldDecl *CodeGenFunction::FindFlexibleArrayMemberFieldAndOffset(
-ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FAMDecl,
-uint64_t &Offset) {
+namespace {
+
+/// StructFieldAccess is a simple visitor class to grab the first MemberExpr
+/// from an Expr. It records any ArraySubscriptExpr we meet along the way.
+class StructFieldAccess
+: public ConstStmtVisitor {
+  bool AddrOfSeen = false;
+
+public:
+  const ArraySubscriptExpr *ASE = nullptr;
+
+  const MemberExpr *VisitMemberExpr(const MemberExpr *E) {
+if (AddrOfSeen && E->getType()->isArrayType())
+  // Avoid forms like '&ptr->array'.
+  return nullptr;
+return E;
+  }
+
+  const MemberExpr *VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
+AddrOfSeen = false; // '&ptr->array[idx]' is okay.
+ASE = E;
+return Visit(E->getBase());
+  }
+  const MemberExpr *VisitCastExpr(const CastExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitParenExpr(const ParenExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryAddrOf(const clang::UnaryOperator *E) {
+AddrOfSeen = true;
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryDeref(const clang::UnaryOperator *E) {
+AddrOfSeen = false;
+return Visit(E->getSubExpr());
+  }
+};
+
+} // end anonymous namespace
+
+/// Find a struct's flexible array member. It may be embedded inside multiple
+/// sub-structs, but must still be the last field.
+static const FieldDecl *FindFlexibleArrayMemberField(CodeGenFunction &CGF,
+ ASTContext &Ctx,
+ const RecordDecl *RD) {
   const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
-  getLangOpts().getStrictFlexArraysLevel();
-  uint32_t FieldNo = 0;
+  CGF.getLangOpts().getStrictFlexArraysLevel();
 
   if (RD->isImplicit())
 return nullptr;
 
   for (const FieldDecl *FD : RD->fields()) {
-if ((!FAMDecl || FD == FAMDecl) &&
-Decl::isFlexibleArrayMemberLike(
+if (Decl::isFlexibleArrayMemberLike(
 Ctx, FD, FD->getType(), StrictFlexArraysLevel,
-/*IgnoreTemplateOrMacroSubstitution=*/true)) {
-  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
-  Offset += Layout.getFieldOffset(FieldNo);
+/*IgnoreTemplateOrMacroSubstitution=*/true))
   return FD;
+
+if (auto RT = FD->getType()->getAs())
+  if (const FieldDecl *FD =
+  FindFlexibleArrayMemberField(CGF, Ctx, RT->getAsRecordDecl()))
+return FD;
+  }
+
+  return nullptr;
+}
+
+/// Calculate the offset of a struct field. It may be embedded inside multiple
+/// sub-structs.
+static bool GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD,
+   const FieldDecl *FD, int64_t &Offset) {
+  if (RD->isImplicit())
+return false;
+
+  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+  uint32_t FieldNo = 0;
+
+  for (const FieldDecl *Field : RD->fields()) {
+if (Field == FD) {
+  Offset += Layout.getFieldOffset(FieldNo);
+  return true;
 }
 
-QualType Ty = FD->getType();
-if (Ty->isRecordType()) {
-  if (const FieldDecl *Field = FindFlexibleArrayMemberFieldAndOffset(
-  Ctx, Ty->getAsRecordDecl(), FAMDecl, Offset)) {
-const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+if (auto RT = Field->getType()->getAs()) {
+  if (GetFieldOffset(Ctx, RT->getAsRecordDecl(), FD, Offset)) {
 Offset += Layout.getFieldOffset(FieldNo);
-return Field;
+return true;
   }
 }
 
 if (!RD->isUnion())
   ++FieldNo;
   }
 
-  return nullptr;
+  return false;
 }
 
-static unsigned CountCountedByAttrs(const RecordDecl *RD) {
-  unsigned Num = 0;
-
-  for (const FieldDecl *FD : RD->fields()) {
-if (FD->getType()->isCountAttributedType())
-  return ++Num;
+static std::optional
+GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FD) {
+  int64_t Offset = 0;
 
-QualType Ty = FD->getType();
-if (Ty->isRecordType())
-  Num += CountCountedByAttrs(Ty->getAsRecordDecl());
-  }
+  if (GetFieldOffset(Ctx, RD, FD, Offset))
+return std::optional(Offset);
 
-  return Num;
+  return std::optional();

bwendling wrote:

Done.

https://github.com/llvm/llvm-project/pull/122198
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-15 Thread Bill Wendling via cfe-commits


@@ -1060,236 +1061,355 @@ 
CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type,
   return ConstantInt::get(ResType, ObjectSize, /*isSigned=*/true);
 }
 
-const FieldDecl *CodeGenFunction::FindFlexibleArrayMemberFieldAndOffset(
-ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FAMDecl,
-uint64_t &Offset) {
+namespace {
+
+/// StructFieldAccess is a simple visitor class to grab the first MemberExpr
+/// from an Expr. It records any ArraySubscriptExpr we meet along the way.
+class StructFieldAccess
+: public ConstStmtVisitor {
+  bool AddrOfSeen = false;
+
+public:
+  const ArraySubscriptExpr *ASE = nullptr;
+
+  const MemberExpr *VisitMemberExpr(const MemberExpr *E) {
+if (AddrOfSeen && E->getType()->isArrayType())
+  // Avoid forms like '&ptr->array'.
+  return nullptr;
+return E;
+  }
+
+  const MemberExpr *VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
+AddrOfSeen = false; // '&ptr->array[idx]' is okay.
+ASE = E;
+return Visit(E->getBase());
+  }
+  const MemberExpr *VisitCastExpr(const CastExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitParenExpr(const ParenExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryAddrOf(const clang::UnaryOperator *E) {
+AddrOfSeen = true;
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryDeref(const clang::UnaryOperator *E) {
+AddrOfSeen = false;
+return Visit(E->getSubExpr());
+  }
+};
+
+} // end anonymous namespace
+
+/// Find a struct's flexible array member. It may be embedded inside multiple
+/// sub-structs, but must still be the last field.
+static const FieldDecl *FindFlexibleArrayMemberField(CodeGenFunction &CGF,
+ ASTContext &Ctx,
+ const RecordDecl *RD) {
   const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
-  getLangOpts().getStrictFlexArraysLevel();
-  uint32_t FieldNo = 0;
+  CGF.getLangOpts().getStrictFlexArraysLevel();
 
   if (RD->isImplicit())
 return nullptr;
 
   for (const FieldDecl *FD : RD->fields()) {
-if ((!FAMDecl || FD == FAMDecl) &&
-Decl::isFlexibleArrayMemberLike(
+if (Decl::isFlexibleArrayMemberLike(
 Ctx, FD, FD->getType(), StrictFlexArraysLevel,
-/*IgnoreTemplateOrMacroSubstitution=*/true)) {
-  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
-  Offset += Layout.getFieldOffset(FieldNo);
+/*IgnoreTemplateOrMacroSubstitution=*/true))
   return FD;
+
+if (auto RT = FD->getType()->getAs())
+  if (const FieldDecl *FD =
+  FindFlexibleArrayMemberField(CGF, Ctx, RT->getAsRecordDecl()))
+return FD;
+  }
+
+  return nullptr;
+}
+
+/// Calculate the offset of a struct field. It may be embedded inside multiple
+/// sub-structs.
+static bool GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD,
+   const FieldDecl *FD, int64_t &Offset) {
+  if (RD->isImplicit())
+return false;
+
+  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+  uint32_t FieldNo = 0;
+
+  for (const FieldDecl *Field : RD->fields()) {
+if (Field == FD) {
+  Offset += Layout.getFieldOffset(FieldNo);
+  return true;
 }
 
-QualType Ty = FD->getType();
-if (Ty->isRecordType()) {
-  if (const FieldDecl *Field = FindFlexibleArrayMemberFieldAndOffset(
-  Ctx, Ty->getAsRecordDecl(), FAMDecl, Offset)) {
-const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+if (auto RT = Field->getType()->getAs()) {
+  if (GetFieldOffset(Ctx, RT->getAsRecordDecl(), FD, Offset)) {
 Offset += Layout.getFieldOffset(FieldNo);
-return Field;
+return true;
   }
 }
 
 if (!RD->isUnion())
   ++FieldNo;
   }
 
-  return nullptr;
+  return false;
 }
 
-static unsigned CountCountedByAttrs(const RecordDecl *RD) {
-  unsigned Num = 0;
-
-  for (const FieldDecl *FD : RD->fields()) {
-if (FD->getType()->isCountAttributedType())
-  return ++Num;
+static std::optional
+GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FD) {
+  int64_t Offset = 0;
 
-QualType Ty = FD->getType();
-if (Ty->isRecordType())
-  Num += CountCountedByAttrs(Ty->getAsRecordDecl());
-  }
+  if (GetFieldOffset(Ctx, RD, FD, Offset))
+return std::optional(Offset);

bwendling wrote:

Let's keep the original way. It's clearer, at least to me. :-)

https://github.com/llvm/llvm-project/pull/122198
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-15 Thread Bill Wendling via cfe-commits


@@ -1060,236 +1061,355 @@ 
CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type,
   return ConstantInt::get(ResType, ObjectSize, /*isSigned=*/true);
 }
 
-const FieldDecl *CodeGenFunction::FindFlexibleArrayMemberFieldAndOffset(
-ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FAMDecl,
-uint64_t &Offset) {
+namespace {
+
+/// StructFieldAccess is a simple visitor class to grab the first MemberExpr
+/// from an Expr. It records any ArraySubscriptExpr we meet along the way.
+class StructFieldAccess
+: public ConstStmtVisitor {
+  bool AddrOfSeen = false;
+
+public:
+  const ArraySubscriptExpr *ASE = nullptr;
+
+  const MemberExpr *VisitMemberExpr(const MemberExpr *E) {
+if (AddrOfSeen && E->getType()->isArrayType())
+  // Avoid forms like '&ptr->array'.
+  return nullptr;
+return E;
+  }
+
+  const MemberExpr *VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
+AddrOfSeen = false; // '&ptr->array[idx]' is okay.
+ASE = E;
+return Visit(E->getBase());
+  }
+  const MemberExpr *VisitCastExpr(const CastExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitParenExpr(const ParenExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryAddrOf(const clang::UnaryOperator *E) {
+AddrOfSeen = true;
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryDeref(const clang::UnaryOperator *E) {
+AddrOfSeen = false;
+return Visit(E->getSubExpr());
+  }
+};
+
+} // end anonymous namespace
+
+/// Find a struct's flexible array member. It may be embedded inside multiple
+/// sub-structs, but must still be the last field.
+static const FieldDecl *FindFlexibleArrayMemberField(CodeGenFunction &CGF,
+ ASTContext &Ctx,
+ const RecordDecl *RD) {
   const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
-  getLangOpts().getStrictFlexArraysLevel();
-  uint32_t FieldNo = 0;
+  CGF.getLangOpts().getStrictFlexArraysLevel();
 
   if (RD->isImplicit())
 return nullptr;
 
   for (const FieldDecl *FD : RD->fields()) {
-if ((!FAMDecl || FD == FAMDecl) &&
-Decl::isFlexibleArrayMemberLike(
+if (Decl::isFlexibleArrayMemberLike(
 Ctx, FD, FD->getType(), StrictFlexArraysLevel,
-/*IgnoreTemplateOrMacroSubstitution=*/true)) {
-  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
-  Offset += Layout.getFieldOffset(FieldNo);
+/*IgnoreTemplateOrMacroSubstitution=*/true))
   return FD;
+
+if (auto RT = FD->getType()->getAs())
+  if (const FieldDecl *FD =
+  FindFlexibleArrayMemberField(CGF, Ctx, RT->getAsRecordDecl()))
+return FD;
+  }
+
+  return nullptr;
+}
+
+/// Calculate the offset of a struct field. It may be embedded inside multiple
+/// sub-structs.
+static bool GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD,
+   const FieldDecl *FD, int64_t &Offset) {
+  if (RD->isImplicit())
+return false;
+
+  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+  uint32_t FieldNo = 0;
+
+  for (const FieldDecl *Field : RD->fields()) {
+if (Field == FD) {
+  Offset += Layout.getFieldOffset(FieldNo);
+  return true;
 }
 
-QualType Ty = FD->getType();
-if (Ty->isRecordType()) {
-  if (const FieldDecl *Field = FindFlexibleArrayMemberFieldAndOffset(
-  Ctx, Ty->getAsRecordDecl(), FAMDecl, Offset)) {
-const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+if (auto RT = Field->getType()->getAs()) {
+  if (GetFieldOffset(Ctx, RT->getAsRecordDecl(), FD, Offset)) {
 Offset += Layout.getFieldOffset(FieldNo);
-return Field;
+return true;
   }
 }
 
 if (!RD->isUnion())
   ++FieldNo;
   }
 
-  return nullptr;
+  return false;
 }
 
-static unsigned CountCountedByAttrs(const RecordDecl *RD) {
-  unsigned Num = 0;
-
-  for (const FieldDecl *FD : RD->fields()) {
-if (FD->getType()->isCountAttributedType())
-  return ++Num;
+static std::optional
+GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FD) {
+  int64_t Offset = 0;
 
-QualType Ty = FD->getType();
-if (Ty->isRecordType())
-  Num += CountCountedByAttrs(Ty->getAsRecordDecl());
-  }
+  if (GetFieldOffset(Ctx, RD, FD, Offset))
+return std::optional(Offset);
 
-  return Num;
+  return std::optional();
 }
 
 llvm::Value *
-CodeGenFunction::emitFlexibleArrayMemberSize(const Expr *E, unsigned Type,
- llvm::IntegerType *ResType) {
-  // The code generated here calculates the size of a struct with a flexible
-  // array member that uses the counted_by attribute. There are two instances
-  // we handle:
-  //
-  //   struct s {
-  // unsigned long flags;
-  // int count;
-  // i

[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-15 Thread Nick Desaulniers via cfe-commits


@@ -1060,236 +1061,355 @@ 
CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type,
   return ConstantInt::get(ResType, ObjectSize, /*isSigned=*/true);
 }
 
-const FieldDecl *CodeGenFunction::FindFlexibleArrayMemberFieldAndOffset(
-ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FAMDecl,
-uint64_t &Offset) {
+namespace {
+
+/// StructFieldAccess is a simple visitor class to grab the first MemberExpr
+/// from an Expr. It records any ArraySubscriptExpr we meet along the way.
+class StructFieldAccess
+: public ConstStmtVisitor {
+  bool AddrOfSeen = false;
+
+public:
+  const ArraySubscriptExpr *ASE = nullptr;
+
+  const MemberExpr *VisitMemberExpr(const MemberExpr *E) {
+if (AddrOfSeen && E->getType()->isArrayType())
+  // Avoid forms like '&ptr->array'.
+  return nullptr;
+return E;
+  }
+
+  const MemberExpr *VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
+AddrOfSeen = false; // '&ptr->array[idx]' is okay.
+ASE = E;
+return Visit(E->getBase());
+  }
+  const MemberExpr *VisitCastExpr(const CastExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitParenExpr(const ParenExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryAddrOf(const clang::UnaryOperator *E) {
+AddrOfSeen = true;
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryDeref(const clang::UnaryOperator *E) {
+AddrOfSeen = false;
+return Visit(E->getSubExpr());
+  }
+};
+
+} // end anonymous namespace
+
+/// Find a struct's flexible array member. It may be embedded inside multiple
+/// sub-structs, but must still be the last field.
+static const FieldDecl *FindFlexibleArrayMemberField(CodeGenFunction &CGF,
+ ASTContext &Ctx,
+ const RecordDecl *RD) {
   const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
-  getLangOpts().getStrictFlexArraysLevel();
-  uint32_t FieldNo = 0;
+  CGF.getLangOpts().getStrictFlexArraysLevel();
 
   if (RD->isImplicit())
 return nullptr;
 
   for (const FieldDecl *FD : RD->fields()) {
-if ((!FAMDecl || FD == FAMDecl) &&
-Decl::isFlexibleArrayMemberLike(
+if (Decl::isFlexibleArrayMemberLike(
 Ctx, FD, FD->getType(), StrictFlexArraysLevel,
-/*IgnoreTemplateOrMacroSubstitution=*/true)) {
-  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
-  Offset += Layout.getFieldOffset(FieldNo);
+/*IgnoreTemplateOrMacroSubstitution=*/true))
   return FD;
+
+if (auto RT = FD->getType()->getAs())
+  if (const FieldDecl *FD =
+  FindFlexibleArrayMemberField(CGF, Ctx, RT->getAsRecordDecl()))
+return FD;
+  }
+
+  return nullptr;
+}
+
+/// Calculate the offset of a struct field. It may be embedded inside multiple
+/// sub-structs.
+static bool GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD,
+   const FieldDecl *FD, int64_t &Offset) {
+  if (RD->isImplicit())
+return false;
+
+  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+  uint32_t FieldNo = 0;
+
+  for (const FieldDecl *Field : RD->fields()) {
+if (Field == FD) {
+  Offset += Layout.getFieldOffset(FieldNo);
+  return true;
 }
 
-QualType Ty = FD->getType();
-if (Ty->isRecordType()) {
-  if (const FieldDecl *Field = FindFlexibleArrayMemberFieldAndOffset(
-  Ctx, Ty->getAsRecordDecl(), FAMDecl, Offset)) {
-const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+if (auto RT = Field->getType()->getAs()) {
+  if (GetFieldOffset(Ctx, RT->getAsRecordDecl(), FD, Offset)) {
 Offset += Layout.getFieldOffset(FieldNo);
-return Field;
+return true;
   }
 }
 
 if (!RD->isUnion())
   ++FieldNo;
   }
 
-  return nullptr;
+  return false;
 }
 
-static unsigned CountCountedByAttrs(const RecordDecl *RD) {
-  unsigned Num = 0;
-
-  for (const FieldDecl *FD : RD->fields()) {
-if (FD->getType()->isCountAttributedType())
-  return ++Num;
+static std::optional
+GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FD) {
+  int64_t Offset = 0;
 
-QualType Ty = FD->getType();
-if (Ty->isRecordType())
-  Num += CountCountedByAttrs(Ty->getAsRecordDecl());
-  }
+  if (GetFieldOffset(Ctx, RD, FD, Offset))
+return std::optional(Offset);
 
-  return Num;
+  return std::optional();
 }
 
 llvm::Value *
-CodeGenFunction::emitFlexibleArrayMemberSize(const Expr *E, unsigned Type,
- llvm::IntegerType *ResType) {
-  // The code generated here calculates the size of a struct with a flexible
-  // array member that uses the counted_by attribute. There are two instances
-  // we handle:
-  //
-  //   struct s {
-  // unsigned long flags;
-  // int count;
-  // i

[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-15 Thread Nick Desaulniers via cfe-commits

https://github.com/nickdesaulniers edited 
https://github.com/llvm/llvm-project/pull/122198
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-15 Thread Nick Desaulniers via cfe-commits


@@ -1060,236 +1061,355 @@ 
CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type,
   return ConstantInt::get(ResType, ObjectSize, /*isSigned=*/true);
 }
 
-const FieldDecl *CodeGenFunction::FindFlexibleArrayMemberFieldAndOffset(
-ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FAMDecl,
-uint64_t &Offset) {
+namespace {
+
+/// StructFieldAccess is a simple visitor class to grab the first MemberExpr
+/// from an Expr. It records any ArraySubscriptExpr we meet along the way.
+class StructFieldAccess
+: public ConstStmtVisitor {
+  bool AddrOfSeen = false;
+
+public:
+  const ArraySubscriptExpr *ASE = nullptr;
+
+  const MemberExpr *VisitMemberExpr(const MemberExpr *E) {
+if (AddrOfSeen && E->getType()->isArrayType())
+  // Avoid forms like '&ptr->array'.
+  return nullptr;
+return E;
+  }
+
+  const MemberExpr *VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
+AddrOfSeen = false; // '&ptr->array[idx]' is okay.
+ASE = E;
+return Visit(E->getBase());
+  }
+  const MemberExpr *VisitCastExpr(const CastExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitParenExpr(const ParenExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryAddrOf(const clang::UnaryOperator *E) {
+AddrOfSeen = true;
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryDeref(const clang::UnaryOperator *E) {
+AddrOfSeen = false;
+return Visit(E->getSubExpr());
+  }
+};
+
+} // end anonymous namespace
+
+/// Find a struct's flexible array member. It may be embedded inside multiple
+/// sub-structs, but must still be the last field.
+static const FieldDecl *FindFlexibleArrayMemberField(CodeGenFunction &CGF,
+ ASTContext &Ctx,
+ const RecordDecl *RD) {
   const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
-  getLangOpts().getStrictFlexArraysLevel();
-  uint32_t FieldNo = 0;
+  CGF.getLangOpts().getStrictFlexArraysLevel();
 
   if (RD->isImplicit())
 return nullptr;
 
   for (const FieldDecl *FD : RD->fields()) {
-if ((!FAMDecl || FD == FAMDecl) &&
-Decl::isFlexibleArrayMemberLike(
+if (Decl::isFlexibleArrayMemberLike(
 Ctx, FD, FD->getType(), StrictFlexArraysLevel,
-/*IgnoreTemplateOrMacroSubstitution=*/true)) {
-  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
-  Offset += Layout.getFieldOffset(FieldNo);
+/*IgnoreTemplateOrMacroSubstitution=*/true))
   return FD;
+
+if (auto RT = FD->getType()->getAs())
+  if (const FieldDecl *FD =
+  FindFlexibleArrayMemberField(CGF, Ctx, RT->getAsRecordDecl()))
+return FD;
+  }
+
+  return nullptr;
+}
+
+/// Calculate the offset of a struct field. It may be embedded inside multiple
+/// sub-structs.
+static bool GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD,
+   const FieldDecl *FD, int64_t &Offset) {
+  if (RD->isImplicit())
+return false;
+
+  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+  uint32_t FieldNo = 0;
+
+  for (const FieldDecl *Field : RD->fields()) {
+if (Field == FD) {
+  Offset += Layout.getFieldOffset(FieldNo);
+  return true;
 }
 
-QualType Ty = FD->getType();
-if (Ty->isRecordType()) {
-  if (const FieldDecl *Field = FindFlexibleArrayMemberFieldAndOffset(
-  Ctx, Ty->getAsRecordDecl(), FAMDecl, Offset)) {
-const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+if (auto RT = Field->getType()->getAs()) {
+  if (GetFieldOffset(Ctx, RT->getAsRecordDecl(), FD, Offset)) {
 Offset += Layout.getFieldOffset(FieldNo);
-return Field;
+return true;
   }
 }
 
 if (!RD->isUnion())
   ++FieldNo;
   }
 
-  return nullptr;
+  return false;
 }
 
-static unsigned CountCountedByAttrs(const RecordDecl *RD) {
-  unsigned Num = 0;
-
-  for (const FieldDecl *FD : RD->fields()) {
-if (FD->getType()->isCountAttributedType())
-  return ++Num;
+static std::optional
+GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FD) {
+  int64_t Offset = 0;
 
-QualType Ty = FD->getType();
-if (Ty->isRecordType())
-  Num += CountCountedByAttrs(Ty->getAsRecordDecl());
-  }
+  if (GetFieldOffset(Ctx, RD, FD, Offset))
+return std::optional(Offset);
 
-  return Num;
+  return std::optional();

nickdesaulniers wrote:

Ah, no, I'm wrong about the default constructor. But I think `std::nullopt` is 
still clearer/more cannonical.

https://github.com/llvm/llvm-project/pull/122198
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-15 Thread Nick Desaulniers via cfe-commits


@@ -1060,236 +1061,355 @@ 
CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type,
   return ConstantInt::get(ResType, ObjectSize, /*isSigned=*/true);
 }
 
-const FieldDecl *CodeGenFunction::FindFlexibleArrayMemberFieldAndOffset(
-ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FAMDecl,
-uint64_t &Offset) {
+namespace {
+
+/// StructFieldAccess is a simple visitor class to grab the first MemberExpr
+/// from an Expr. It records any ArraySubscriptExpr we meet along the way.
+class StructFieldAccess
+: public ConstStmtVisitor {
+  bool AddrOfSeen = false;
+
+public:
+  const ArraySubscriptExpr *ASE = nullptr;
+
+  const MemberExpr *VisitMemberExpr(const MemberExpr *E) {
+if (AddrOfSeen && E->getType()->isArrayType())
+  // Avoid forms like '&ptr->array'.
+  return nullptr;
+return E;
+  }
+
+  const MemberExpr *VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
+AddrOfSeen = false; // '&ptr->array[idx]' is okay.
+ASE = E;
+return Visit(E->getBase());
+  }
+  const MemberExpr *VisitCastExpr(const CastExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitParenExpr(const ParenExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryAddrOf(const clang::UnaryOperator *E) {
+AddrOfSeen = true;
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryDeref(const clang::UnaryOperator *E) {
+AddrOfSeen = false;
+return Visit(E->getSubExpr());
+  }
+};
+
+} // end anonymous namespace
+
+/// Find a struct's flexible array member. It may be embedded inside multiple
+/// sub-structs, but must still be the last field.
+static const FieldDecl *FindFlexibleArrayMemberField(CodeGenFunction &CGF,
+ ASTContext &Ctx,
+ const RecordDecl *RD) {
   const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
-  getLangOpts().getStrictFlexArraysLevel();
-  uint32_t FieldNo = 0;
+  CGF.getLangOpts().getStrictFlexArraysLevel();
 
   if (RD->isImplicit())
 return nullptr;
 
   for (const FieldDecl *FD : RD->fields()) {
-if ((!FAMDecl || FD == FAMDecl) &&
-Decl::isFlexibleArrayMemberLike(
+if (Decl::isFlexibleArrayMemberLike(
 Ctx, FD, FD->getType(), StrictFlexArraysLevel,
-/*IgnoreTemplateOrMacroSubstitution=*/true)) {
-  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
-  Offset += Layout.getFieldOffset(FieldNo);
+/*IgnoreTemplateOrMacroSubstitution=*/true))
   return FD;
+
+if (auto RT = FD->getType()->getAs())
+  if (const FieldDecl *FD =
+  FindFlexibleArrayMemberField(CGF, Ctx, RT->getAsRecordDecl()))
+return FD;
+  }
+
+  return nullptr;
+}
+
+/// Calculate the offset of a struct field. It may be embedded inside multiple
+/// sub-structs.
+static bool GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD,
+   const FieldDecl *FD, int64_t &Offset) {
+  if (RD->isImplicit())
+return false;
+
+  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+  uint32_t FieldNo = 0;
+
+  for (const FieldDecl *Field : RD->fields()) {
+if (Field == FD) {
+  Offset += Layout.getFieldOffset(FieldNo);
+  return true;
 }
 
-QualType Ty = FD->getType();
-if (Ty->isRecordType()) {
-  if (const FieldDecl *Field = FindFlexibleArrayMemberFieldAndOffset(
-  Ctx, Ty->getAsRecordDecl(), FAMDecl, Offset)) {
-const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+if (auto RT = Field->getType()->getAs()) {
+  if (GetFieldOffset(Ctx, RT->getAsRecordDecl(), FD, Offset)) {
 Offset += Layout.getFieldOffset(FieldNo);
-return Field;
+return true;
   }
 }
 
 if (!RD->isUnion())
   ++FieldNo;
   }
 
-  return nullptr;
+  return false;
 }
 
-static unsigned CountCountedByAttrs(const RecordDecl *RD) {
-  unsigned Num = 0;
-
-  for (const FieldDecl *FD : RD->fields()) {
-if (FD->getType()->isCountAttributedType())
-  return ++Num;
+static std::optional
+GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FD) {
+  int64_t Offset = 0;
 
-QualType Ty = FD->getType();
-if (Ty->isRecordType())
-  Num += CountCountedByAttrs(Ty->getAsRecordDecl());
-  }
+  if (GetFieldOffset(Ctx, RD, FD, Offset))
+return std::optional(Offset);
 
-  return Num;
+  return std::optional();
 }
 
 llvm::Value *
-CodeGenFunction::emitFlexibleArrayMemberSize(const Expr *E, unsigned Type,
- llvm::IntegerType *ResType) {
-  // The code generated here calculates the size of a struct with a flexible
-  // array member that uses the counted_by attribute. There are two instances
-  // we handle:
-  //
-  //   struct s {
-  // unsigned long flags;
-  // int count;
-  // i

[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-15 Thread Nick Desaulniers via cfe-commits


@@ -1060,236 +1061,355 @@ 
CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type,
   return ConstantInt::get(ResType, ObjectSize, /*isSigned=*/true);
 }
 
-const FieldDecl *CodeGenFunction::FindFlexibleArrayMemberFieldAndOffset(
-ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FAMDecl,
-uint64_t &Offset) {
+namespace {
+
+/// StructFieldAccess is a simple visitor class to grab the first MemberExpr
+/// from an Expr. It records any ArraySubscriptExpr we meet along the way.
+class StructFieldAccess
+: public ConstStmtVisitor {
+  bool AddrOfSeen = false;
+
+public:
+  const ArraySubscriptExpr *ASE = nullptr;
+
+  const MemberExpr *VisitMemberExpr(const MemberExpr *E) {
+if (AddrOfSeen && E->getType()->isArrayType())
+  // Avoid forms like '&ptr->array'.
+  return nullptr;
+return E;
+  }
+
+  const MemberExpr *VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
+AddrOfSeen = false; // '&ptr->array[idx]' is okay.
+ASE = E;
+return Visit(E->getBase());
+  }
+  const MemberExpr *VisitCastExpr(const CastExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitParenExpr(const ParenExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryAddrOf(const clang::UnaryOperator *E) {
+AddrOfSeen = true;
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryDeref(const clang::UnaryOperator *E) {
+AddrOfSeen = false;
+return Visit(E->getSubExpr());
+  }
+};
+
+} // end anonymous namespace
+
+/// Find a struct's flexible array member. It may be embedded inside multiple
+/// sub-structs, but must still be the last field.
+static const FieldDecl *FindFlexibleArrayMemberField(CodeGenFunction &CGF,
+ ASTContext &Ctx,
+ const RecordDecl *RD) {
   const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
-  getLangOpts().getStrictFlexArraysLevel();
-  uint32_t FieldNo = 0;
+  CGF.getLangOpts().getStrictFlexArraysLevel();
 
   if (RD->isImplicit())
 return nullptr;
 
   for (const FieldDecl *FD : RD->fields()) {
-if ((!FAMDecl || FD == FAMDecl) &&
-Decl::isFlexibleArrayMemberLike(
+if (Decl::isFlexibleArrayMemberLike(
 Ctx, FD, FD->getType(), StrictFlexArraysLevel,
-/*IgnoreTemplateOrMacroSubstitution=*/true)) {
-  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
-  Offset += Layout.getFieldOffset(FieldNo);
+/*IgnoreTemplateOrMacroSubstitution=*/true))
   return FD;
+
+if (auto RT = FD->getType()->getAs())
+  if (const FieldDecl *FD =
+  FindFlexibleArrayMemberField(CGF, Ctx, RT->getAsRecordDecl()))
+return FD;
+  }
+
+  return nullptr;
+}
+
+/// Calculate the offset of a struct field. It may be embedded inside multiple
+/// sub-structs.
+static bool GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD,
+   const FieldDecl *FD, int64_t &Offset) {
+  if (RD->isImplicit())
+return false;
+
+  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+  uint32_t FieldNo = 0;
+
+  for (const FieldDecl *Field : RD->fields()) {
+if (Field == FD) {
+  Offset += Layout.getFieldOffset(FieldNo);
+  return true;
 }
 
-QualType Ty = FD->getType();
-if (Ty->isRecordType()) {
-  if (const FieldDecl *Field = FindFlexibleArrayMemberFieldAndOffset(
-  Ctx, Ty->getAsRecordDecl(), FAMDecl, Offset)) {
-const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+if (auto RT = Field->getType()->getAs()) {
+  if (GetFieldOffset(Ctx, RT->getAsRecordDecl(), FD, Offset)) {
 Offset += Layout.getFieldOffset(FieldNo);
-return Field;
+return true;
   }
 }
 
 if (!RD->isUnion())
   ++FieldNo;
   }
 
-  return nullptr;
+  return false;
 }
 
-static unsigned CountCountedByAttrs(const RecordDecl *RD) {
-  unsigned Num = 0;
-
-  for (const FieldDecl *FD : RD->fields()) {
-if (FD->getType()->isCountAttributedType())
-  return ++Num;
+static std::optional
+GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FD) {
+  int64_t Offset = 0;
 
-QualType Ty = FD->getType();
-if (Ty->isRecordType())
-  Num += CountCountedByAttrs(Ty->getAsRecordDecl());
-  }
+  if (GetFieldOffset(Ctx, RD, FD, Offset))
+return std::optional(Offset);

nickdesaulniers wrote:

I think you can do:
```suggestion
return {Offset};
```

https://github.com/llvm/llvm-project/pull/122198
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-15 Thread Nick Desaulniers via cfe-commits


@@ -1060,236 +1061,355 @@ 
CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type,
   return ConstantInt::get(ResType, ObjectSize, /*isSigned=*/true);
 }
 
-const FieldDecl *CodeGenFunction::FindFlexibleArrayMemberFieldAndOffset(
-ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FAMDecl,
-uint64_t &Offset) {
+namespace {
+
+/// StructFieldAccess is a simple visitor class to grab the first MemberExpr
+/// from an Expr. It records any ArraySubscriptExpr we meet along the way.
+class StructFieldAccess
+: public ConstStmtVisitor {
+  bool AddrOfSeen = false;
+
+public:
+  const ArraySubscriptExpr *ASE = nullptr;
+
+  const MemberExpr *VisitMemberExpr(const MemberExpr *E) {
+if (AddrOfSeen && E->getType()->isArrayType())
+  // Avoid forms like '&ptr->array'.
+  return nullptr;
+return E;
+  }
+
+  const MemberExpr *VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
+AddrOfSeen = false; // '&ptr->array[idx]' is okay.
+ASE = E;
+return Visit(E->getBase());
+  }
+  const MemberExpr *VisitCastExpr(const CastExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitParenExpr(const ParenExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryAddrOf(const clang::UnaryOperator *E) {
+AddrOfSeen = true;
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryDeref(const clang::UnaryOperator *E) {
+AddrOfSeen = false;
+return Visit(E->getSubExpr());
+  }
+};
+
+} // end anonymous namespace
+
+/// Find a struct's flexible array member. It may be embedded inside multiple
+/// sub-structs, but must still be the last field.
+static const FieldDecl *FindFlexibleArrayMemberField(CodeGenFunction &CGF,
+ ASTContext &Ctx,
+ const RecordDecl *RD) {
   const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
-  getLangOpts().getStrictFlexArraysLevel();
-  uint32_t FieldNo = 0;
+  CGF.getLangOpts().getStrictFlexArraysLevel();
 
   if (RD->isImplicit())
 return nullptr;
 
   for (const FieldDecl *FD : RD->fields()) {
-if ((!FAMDecl || FD == FAMDecl) &&
-Decl::isFlexibleArrayMemberLike(
+if (Decl::isFlexibleArrayMemberLike(
 Ctx, FD, FD->getType(), StrictFlexArraysLevel,
-/*IgnoreTemplateOrMacroSubstitution=*/true)) {
-  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
-  Offset += Layout.getFieldOffset(FieldNo);
+/*IgnoreTemplateOrMacroSubstitution=*/true))
   return FD;
+
+if (auto RT = FD->getType()->getAs())
+  if (const FieldDecl *FD =
+  FindFlexibleArrayMemberField(CGF, Ctx, RT->getAsRecordDecl()))
+return FD;
+  }
+
+  return nullptr;
+}
+
+/// Calculate the offset of a struct field. It may be embedded inside multiple
+/// sub-structs.
+static bool GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD,
+   const FieldDecl *FD, int64_t &Offset) {
+  if (RD->isImplicit())
+return false;
+
+  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+  uint32_t FieldNo = 0;
+
+  for (const FieldDecl *Field : RD->fields()) {
+if (Field == FD) {
+  Offset += Layout.getFieldOffset(FieldNo);
+  return true;
 }
 
-QualType Ty = FD->getType();
-if (Ty->isRecordType()) {
-  if (const FieldDecl *Field = FindFlexibleArrayMemberFieldAndOffset(
-  Ctx, Ty->getAsRecordDecl(), FAMDecl, Offset)) {
-const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+if (auto RT = Field->getType()->getAs()) {
+  if (GetFieldOffset(Ctx, RT->getAsRecordDecl(), FD, Offset)) {
 Offset += Layout.getFieldOffset(FieldNo);
-return Field;
+return true;
   }
 }
 
 if (!RD->isUnion())
   ++FieldNo;
   }
 
-  return nullptr;
+  return false;
 }
 
-static unsigned CountCountedByAttrs(const RecordDecl *RD) {
-  unsigned Num = 0;
-
-  for (const FieldDecl *FD : RD->fields()) {
-if (FD->getType()->isCountAttributedType())
-  return ++Num;
+static std::optional
+GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FD) {
+  int64_t Offset = 0;
 
-QualType Ty = FD->getType();
-if (Ty->isRecordType())
-  Num += CountCountedByAttrs(Ty->getAsRecordDecl());
-  }
+  if (GetFieldOffset(Ctx, RD, FD, Offset))
+return std::optional(Offset);
 
-  return Num;
+  return std::optional();
 }
 
 llvm::Value *
-CodeGenFunction::emitFlexibleArrayMemberSize(const Expr *E, unsigned Type,
- llvm::IntegerType *ResType) {
-  // The code generated here calculates the size of a struct with a flexible
-  // array member that uses the counted_by attribute. There are two instances
-  // we handle:
-  //
-  //   struct s {
-  // unsigned long flags;
-  // int count;
-  // i

[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-15 Thread Nick Desaulniers via cfe-commits


@@ -1060,236 +1061,355 @@ 
CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type,
   return ConstantInt::get(ResType, ObjectSize, /*isSigned=*/true);
 }
 
-const FieldDecl *CodeGenFunction::FindFlexibleArrayMemberFieldAndOffset(
-ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FAMDecl,
-uint64_t &Offset) {
+namespace {
+
+/// StructFieldAccess is a simple visitor class to grab the first MemberExpr
+/// from an Expr. It records any ArraySubscriptExpr we meet along the way.
+class StructFieldAccess
+: public ConstStmtVisitor {
+  bool AddrOfSeen = false;
+
+public:
+  const ArraySubscriptExpr *ASE = nullptr;
+
+  const MemberExpr *VisitMemberExpr(const MemberExpr *E) {
+if (AddrOfSeen && E->getType()->isArrayType())
+  // Avoid forms like '&ptr->array'.
+  return nullptr;
+return E;
+  }
+
+  const MemberExpr *VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
+AddrOfSeen = false; // '&ptr->array[idx]' is okay.
+ASE = E;
+return Visit(E->getBase());
+  }
+  const MemberExpr *VisitCastExpr(const CastExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitParenExpr(const ParenExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryAddrOf(const clang::UnaryOperator *E) {
+AddrOfSeen = true;
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryDeref(const clang::UnaryOperator *E) {
+AddrOfSeen = false;
+return Visit(E->getSubExpr());
+  }
+};
+
+} // end anonymous namespace
+
+/// Find a struct's flexible array member. It may be embedded inside multiple
+/// sub-structs, but must still be the last field.
+static const FieldDecl *FindFlexibleArrayMemberField(CodeGenFunction &CGF,
+ ASTContext &Ctx,
+ const RecordDecl *RD) {
   const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
-  getLangOpts().getStrictFlexArraysLevel();
-  uint32_t FieldNo = 0;
+  CGF.getLangOpts().getStrictFlexArraysLevel();
 
   if (RD->isImplicit())
 return nullptr;
 
   for (const FieldDecl *FD : RD->fields()) {
-if ((!FAMDecl || FD == FAMDecl) &&
-Decl::isFlexibleArrayMemberLike(
+if (Decl::isFlexibleArrayMemberLike(
 Ctx, FD, FD->getType(), StrictFlexArraysLevel,
-/*IgnoreTemplateOrMacroSubstitution=*/true)) {
-  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
-  Offset += Layout.getFieldOffset(FieldNo);
+/*IgnoreTemplateOrMacroSubstitution=*/true))
   return FD;
+
+if (auto RT = FD->getType()->getAs())
+  if (const FieldDecl *FD =
+  FindFlexibleArrayMemberField(CGF, Ctx, RT->getAsRecordDecl()))
+return FD;
+  }
+
+  return nullptr;
+}
+
+/// Calculate the offset of a struct field. It may be embedded inside multiple
+/// sub-structs.
+static bool GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD,
+   const FieldDecl *FD, int64_t &Offset) {
+  if (RD->isImplicit())
+return false;
+
+  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+  uint32_t FieldNo = 0;
+
+  for (const FieldDecl *Field : RD->fields()) {
+if (Field == FD) {
+  Offset += Layout.getFieldOffset(FieldNo);
+  return true;
 }
 
-QualType Ty = FD->getType();
-if (Ty->isRecordType()) {
-  if (const FieldDecl *Field = FindFlexibleArrayMemberFieldAndOffset(
-  Ctx, Ty->getAsRecordDecl(), FAMDecl, Offset)) {
-const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+if (auto RT = Field->getType()->getAs()) {
+  if (GetFieldOffset(Ctx, RT->getAsRecordDecl(), FD, Offset)) {
 Offset += Layout.getFieldOffset(FieldNo);
-return Field;
+return true;
   }
 }
 
 if (!RD->isUnion())
   ++FieldNo;
   }
 
-  return nullptr;
+  return false;
 }
 
-static unsigned CountCountedByAttrs(const RecordDecl *RD) {
-  unsigned Num = 0;
-
-  for (const FieldDecl *FD : RD->fields()) {
-if (FD->getType()->isCountAttributedType())
-  return ++Num;
+static std::optional
+GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FD) {
+  int64_t Offset = 0;
 
-QualType Ty = FD->getType();
-if (Ty->isRecordType())
-  Num += CountCountedByAttrs(Ty->getAsRecordDecl());
-  }
+  if (GetFieldOffset(Ctx, RD, FD, Offset))
+return std::optional(Offset);
 
-  return Num;
+  return std::optional();

nickdesaulniers wrote:

I think what you have will construct a truthy optional with a default 
initialized `int64_t` of `0`; I suspect what you want is to signal that the 
value does not exist.
```suggestion
  return std::nullopt;
```

https://github.com/llvm/llvm-project/pull/122198
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/

[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-15 Thread Nick Desaulniers via cfe-commits


@@ -1060,238 +1061,348 @@ 
CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type,
   return ConstantInt::get(ResType, ObjectSize, /*isSigned=*/true);
 }
 
-const FieldDecl *CodeGenFunction::FindFlexibleArrayMemberFieldAndOffset(
-ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FAMDecl,
-uint64_t &Offset) {
+namespace {
+
+/// StructFieldAccess is a simple visitor class to grab the first MemberExpr
+/// from an Expr. It records any ArraySubscriptExpr we meet along the way.
+class StructFieldAccess
+: public ConstStmtVisitor {
+  bool AddrOfSeen = false;
+
+public:
+  const ArraySubscriptExpr *ASE = nullptr;
+
+  const MemberExpr *VisitMemberExpr(const MemberExpr *E) {
+if (AddrOfSeen && E->getType()->isArrayType())
+  // Avoid forms like '&ptr->array'.
+  return nullptr;
+return E;
+  }
+
+  const MemberExpr *VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
+AddrOfSeen = false; // '&ptr->array[idx]' is okay.
+ASE = E;
+return Visit(E->getBase());
+  }
+  const MemberExpr *VisitCastExpr(const CastExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitParenExpr(const ParenExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryAddrOf(const clang::UnaryOperator *E) {
+AddrOfSeen = true;
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryDeref(const clang::UnaryOperator *E) {
+AddrOfSeen = false;
+return Visit(E->getSubExpr());
+  }
+};
+
+} // end anonymous namespace
+
+/// Find a struct's flexible array member. It may be embedded inside multiple
+/// sub-structs, but must still be the last field.
+static const FieldDecl *FindFlexibleArrayMemberField(CodeGenFunction &CGF,
+ ASTContext &Ctx,
+ const RecordDecl *RD) {
   const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
-  getLangOpts().getStrictFlexArraysLevel();
-  uint32_t FieldNo = 0;
+  CGF.getLangOpts().getStrictFlexArraysLevel();
 
   if (RD->isImplicit())
 return nullptr;
 
   for (const FieldDecl *FD : RD->fields()) {
-if ((!FAMDecl || FD == FAMDecl) &&
-Decl::isFlexibleArrayMemberLike(
+if (Decl::isFlexibleArrayMemberLike(
 Ctx, FD, FD->getType(), StrictFlexArraysLevel,
-/*IgnoreTemplateOrMacroSubstitution=*/true)) {
-  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
-  Offset += Layout.getFieldOffset(FieldNo);
+/*IgnoreTemplateOrMacroSubstitution=*/true))
   return FD;
+
+if (auto RT = FD->getType()->getAs())
+  if (const FieldDecl *FD =
+  FindFlexibleArrayMemberField(CGF, Ctx, RT->getAsRecordDecl()))
+return FD;
+  }
+
+  return nullptr;
+}
+
+/// Calculate the offset of a struct field. It may be embedded inside multiple
+/// sub-structs.
+static bool GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD,
+   const FieldDecl *FD, int64_t &Offset) {
+  if (RD->isImplicit())
+return false;
+
+  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+  uint32_t FieldNo = 0;

nickdesaulniers wrote:

sure, no problem.  Perhaps a comment saying why `FieldDecl::getFieldIndex` is 
inappropriate to use here might help future travelers.

https://github.com/llvm/llvm-project/pull/122198
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-15 Thread Bill Wendling via cfe-commits

https://github.com/bwendling edited 
https://github.com/llvm/llvm-project/pull/122198
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-15 Thread Bill Wendling via cfe-commits


@@ -1060,238 +1061,348 @@ 
CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type,
   return ConstantInt::get(ResType, ObjectSize, /*isSigned=*/true);
 }
 
-const FieldDecl *CodeGenFunction::FindFlexibleArrayMemberFieldAndOffset(
-ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FAMDecl,
-uint64_t &Offset) {
+namespace {
+
+/// StructFieldAccess is a simple visitor class to grab the first MemberExpr
+/// from an Expr. It records any ArraySubscriptExpr we meet along the way.
+class StructFieldAccess
+: public ConstStmtVisitor {
+  bool AddrOfSeen = false;
+
+public:
+  const ArraySubscriptExpr *ASE = nullptr;
+
+  const MemberExpr *VisitMemberExpr(const MemberExpr *E) {
+if (AddrOfSeen && E->getType()->isArrayType())
+  // Avoid forms like '&ptr->array'.
+  return nullptr;
+return E;
+  }
+
+  const MemberExpr *VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
+AddrOfSeen = false; // '&ptr->array[idx]' is okay.
+ASE = E;
+return Visit(E->getBase());
+  }
+  const MemberExpr *VisitCastExpr(const CastExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitParenExpr(const ParenExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryAddrOf(const clang::UnaryOperator *E) {
+AddrOfSeen = true;
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryDeref(const clang::UnaryOperator *E) {
+AddrOfSeen = false;
+return Visit(E->getSubExpr());
+  }
+};
+
+} // end anonymous namespace
+
+/// Find a struct's flexible array member. It may be embedded inside multiple
+/// sub-structs, but must still be the last field.
+static const FieldDecl *FindFlexibleArrayMemberField(CodeGenFunction &CGF,
+ ASTContext &Ctx,
+ const RecordDecl *RD) {
   const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
-  getLangOpts().getStrictFlexArraysLevel();
-  uint32_t FieldNo = 0;
+  CGF.getLangOpts().getStrictFlexArraysLevel();
 
   if (RD->isImplicit())
 return nullptr;
 
   for (const FieldDecl *FD : RD->fields()) {
-if ((!FAMDecl || FD == FAMDecl) &&
-Decl::isFlexibleArrayMemberLike(
+if (Decl::isFlexibleArrayMemberLike(
 Ctx, FD, FD->getType(), StrictFlexArraysLevel,
-/*IgnoreTemplateOrMacroSubstitution=*/true)) {
-  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
-  Offset += Layout.getFieldOffset(FieldNo);
+/*IgnoreTemplateOrMacroSubstitution=*/true))
   return FD;
+
+if (auto RT = FD->getType()->getAs())
+  if (const FieldDecl *FD =
+  FindFlexibleArrayMemberField(CGF, Ctx, RT->getAsRecordDecl()))
+return FD;
+  }
+
+  return nullptr;
+}
+
+/// Calculate the offset of a struct field. It may be embedded inside multiple
+/// sub-structs.
+static bool GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD,
+   const FieldDecl *FD, int64_t &Offset) {
+  if (RD->isImplicit())
+return false;
+
+  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+  uint32_t FieldNo = 0;

bwendling wrote:

No, it doesn't handle the case well. I've tried various different methods of 
using the layout and context methods and none of them did what I want to use 
here. They either have assertions that require the FD to be in the record or 
don't generate the actual offset within the full 

Let's just leave this as is. :-)

https://github.com/llvm/llvm-project/pull/122198
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-15 Thread Bill Wendling via cfe-commits


@@ -1060,238 +1061,348 @@ 
CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type,
   return ConstantInt::get(ResType, ObjectSize, /*isSigned=*/true);
 }
 
-const FieldDecl *CodeGenFunction::FindFlexibleArrayMemberFieldAndOffset(
-ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FAMDecl,
-uint64_t &Offset) {
+namespace {
+
+/// StructFieldAccess is a simple visitor class to grab the first MemberExpr
+/// from an Expr. It records any ArraySubscriptExpr we meet along the way.
+class StructFieldAccess
+: public ConstStmtVisitor {
+  bool AddrOfSeen = false;
+
+public:
+  const ArraySubscriptExpr *ASE = nullptr;
+
+  const MemberExpr *VisitMemberExpr(const MemberExpr *E) {
+if (AddrOfSeen && E->getType()->isArrayType())
+  // Avoid forms like '&ptr->array'.
+  return nullptr;
+return E;
+  }
+
+  const MemberExpr *VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
+AddrOfSeen = false; // '&ptr->array[idx]' is okay.
+ASE = E;
+return Visit(E->getBase());
+  }
+  const MemberExpr *VisitCastExpr(const CastExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitParenExpr(const ParenExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryAddrOf(const clang::UnaryOperator *E) {
+AddrOfSeen = true;
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryDeref(const clang::UnaryOperator *E) {
+AddrOfSeen = false;
+return Visit(E->getSubExpr());
+  }
+};
+
+} // end anonymous namespace
+
+/// Find a struct's flexible array member. It may be embedded inside multiple
+/// sub-structs, but must still be the last field.
+static const FieldDecl *FindFlexibleArrayMemberField(CodeGenFunction &CGF,
+ ASTContext &Ctx,
+ const RecordDecl *RD) {
   const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
-  getLangOpts().getStrictFlexArraysLevel();
-  uint32_t FieldNo = 0;
+  CGF.getLangOpts().getStrictFlexArraysLevel();
 
   if (RD->isImplicit())
 return nullptr;
 
   for (const FieldDecl *FD : RD->fields()) {
-if ((!FAMDecl || FD == FAMDecl) &&
-Decl::isFlexibleArrayMemberLike(
+if (Decl::isFlexibleArrayMemberLike(
 Ctx, FD, FD->getType(), StrictFlexArraysLevel,
-/*IgnoreTemplateOrMacroSubstitution=*/true)) {
-  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
-  Offset += Layout.getFieldOffset(FieldNo);
+/*IgnoreTemplateOrMacroSubstitution=*/true))
   return FD;
+
+if (auto RT = FD->getType()->getAs())
+  if (const FieldDecl *FD =
+  FindFlexibleArrayMemberField(CGF, Ctx, RT->getAsRecordDecl()))
+return FD;
+  }
+
+  return nullptr;
+}
+
+/// Calculate the offset of a struct field. It may be embedded inside multiple
+/// sub-structs.

bwendling wrote:

?? That's what the comment says...

https://github.com/llvm/llvm-project/pull/122198
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-15 Thread Bill Wendling via cfe-commits


@@ -1060,238 +1061,348 @@ 
CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type,
   return ConstantInt::get(ResType, ObjectSize, /*isSigned=*/true);
 }
 
-const FieldDecl *CodeGenFunction::FindFlexibleArrayMemberFieldAndOffset(
-ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FAMDecl,
-uint64_t &Offset) {
+namespace {
+
+/// StructFieldAccess is a simple visitor class to grab the first MemberExpr
+/// from an Expr. It records any ArraySubscriptExpr we meet along the way.
+class StructFieldAccess
+: public ConstStmtVisitor {
+  bool AddrOfSeen = false;
+
+public:
+  const ArraySubscriptExpr *ASE = nullptr;
+
+  const MemberExpr *VisitMemberExpr(const MemberExpr *E) {
+if (AddrOfSeen && E->getType()->isArrayType())
+  // Avoid forms like '&ptr->array'.
+  return nullptr;
+return E;
+  }
+
+  const MemberExpr *VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
+AddrOfSeen = false; // '&ptr->array[idx]' is okay.
+ASE = E;
+return Visit(E->getBase());
+  }
+  const MemberExpr *VisitCastExpr(const CastExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitParenExpr(const ParenExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryAddrOf(const clang::UnaryOperator *E) {
+AddrOfSeen = true;
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryDeref(const clang::UnaryOperator *E) {
+AddrOfSeen = false;
+return Visit(E->getSubExpr());
+  }
+};
+
+} // end anonymous namespace
+
+/// Find a struct's flexible array member. It may be embedded inside multiple
+/// sub-structs, but must still be the last field.
+static const FieldDecl *FindFlexibleArrayMemberField(CodeGenFunction &CGF,
+ ASTContext &Ctx,
+ const RecordDecl *RD) {
   const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
-  getLangOpts().getStrictFlexArraysLevel();
-  uint32_t FieldNo = 0;
+  CGF.getLangOpts().getStrictFlexArraysLevel();
 
   if (RD->isImplicit())
 return nullptr;
 
   for (const FieldDecl *FD : RD->fields()) {
-if ((!FAMDecl || FD == FAMDecl) &&
-Decl::isFlexibleArrayMemberLike(
+if (Decl::isFlexibleArrayMemberLike(
 Ctx, FD, FD->getType(), StrictFlexArraysLevel,
-/*IgnoreTemplateOrMacroSubstitution=*/true)) {
-  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
-  Offset += Layout.getFieldOffset(FieldNo);
+/*IgnoreTemplateOrMacroSubstitution=*/true))
   return FD;
+
+if (auto RT = FD->getType()->getAs())
+  if (const FieldDecl *FD =
+  FindFlexibleArrayMemberField(CGF, Ctx, RT->getAsRecordDecl()))
+return FD;
+  }
+
+  return nullptr;
+}
+
+/// Calculate the offset of a struct field. It may be embedded inside multiple
+/// sub-structs.
+static bool GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD,
+   const FieldDecl *FD, int64_t &Offset) {
+  if (RD->isImplicit())
+return false;
+
+  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+  uint32_t FieldNo = 0;
+
+  for (const FieldDecl *Field : RD->fields()) {
+if (Field == FD) {
+  Offset += Layout.getFieldOffset(FieldNo);
+  return true;
 }
 
-QualType Ty = FD->getType();
-if (Ty->isRecordType()) {
-  if (const FieldDecl *Field = FindFlexibleArrayMemberFieldAndOffset(
-  Ctx, Ty->getAsRecordDecl(), FAMDecl, Offset)) {
-const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+if (auto RT = Field->getType()->getAs()) {
+  if (GetFieldOffset(Ctx, RT->getAsRecordDecl(), FD, Offset)) {
 Offset += Layout.getFieldOffset(FieldNo);
-return Field;
+return true;
   }
 }
 
 if (!RD->isUnion())
   ++FieldNo;
   }
 
-  return nullptr;
+  return false;
 }
 
-static unsigned CountCountedByAttrs(const RecordDecl *RD) {
-  unsigned Num = 0;
+llvm::Value *
+CodeGenFunction::emitCountedByMemberSize(const Expr *E, llvm::Value *EmittedE,
+ unsigned Type,
+ llvm::IntegerType *ResType) {
+  ASTContext &Ctx = getContext();
 
-  for (const FieldDecl *FD : RD->fields()) {
-if (FD->getType()->isCountAttributedType())
-  return ++Num;
+  // Note: If the whole struct is specificed in the __bdos (i.e. Visitor
+  // returns a DeclRefExpr). The calculation of the whole size of the structure
+  // with a flexible array member can be done in two ways:
+  //
+  // 1) sizeof(struct S) + count * sizeof(typeof(fam))
+  // 2) offsetof(struct S, fam) + count * sizeof(typeof(fam))
+  //
+  // The first will add additional padding after the end of the array
+  // allocation while the second method is more precise, but not quite expected
+  // from programmers. See
+  // https://lore.kernel.org/l

[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-15 Thread Bill Wendling via cfe-commits


@@ -1060,238 +1061,348 @@ 
CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type,
   return ConstantInt::get(ResType, ObjectSize, /*isSigned=*/true);
 }
 
-const FieldDecl *CodeGenFunction::FindFlexibleArrayMemberFieldAndOffset(
-ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FAMDecl,
-uint64_t &Offset) {
+namespace {
+
+/// StructFieldAccess is a simple visitor class to grab the first MemberExpr
+/// from an Expr. It records any ArraySubscriptExpr we meet along the way.
+class StructFieldAccess
+: public ConstStmtVisitor {
+  bool AddrOfSeen = false;
+
+public:
+  const ArraySubscriptExpr *ASE = nullptr;
+
+  const MemberExpr *VisitMemberExpr(const MemberExpr *E) {
+if (AddrOfSeen && E->getType()->isArrayType())
+  // Avoid forms like '&ptr->array'.
+  return nullptr;
+return E;
+  }
+
+  const MemberExpr *VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
+AddrOfSeen = false; // '&ptr->array[idx]' is okay.
+ASE = E;
+return Visit(E->getBase());
+  }
+  const MemberExpr *VisitCastExpr(const CastExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitParenExpr(const ParenExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryAddrOf(const clang::UnaryOperator *E) {
+AddrOfSeen = true;
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryDeref(const clang::UnaryOperator *E) {
+AddrOfSeen = false;
+return Visit(E->getSubExpr());
+  }
+};
+
+} // end anonymous namespace
+
+/// Find a struct's flexible array member. It may be embedded inside multiple
+/// sub-structs, but must still be the last field.
+static const FieldDecl *FindFlexibleArrayMemberField(CodeGenFunction &CGF,
+ ASTContext &Ctx,
+ const RecordDecl *RD) {
   const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
-  getLangOpts().getStrictFlexArraysLevel();
-  uint32_t FieldNo = 0;
+  CGF.getLangOpts().getStrictFlexArraysLevel();
 
   if (RD->isImplicit())
 return nullptr;
 
   for (const FieldDecl *FD : RD->fields()) {
-if ((!FAMDecl || FD == FAMDecl) &&
-Decl::isFlexibleArrayMemberLike(
+if (Decl::isFlexibleArrayMemberLike(
 Ctx, FD, FD->getType(), StrictFlexArraysLevel,
-/*IgnoreTemplateOrMacroSubstitution=*/true)) {
-  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
-  Offset += Layout.getFieldOffset(FieldNo);
+/*IgnoreTemplateOrMacroSubstitution=*/true))
   return FD;
+
+if (auto RT = FD->getType()->getAs())
+  if (const FieldDecl *FD =
+  FindFlexibleArrayMemberField(CGF, Ctx, RT->getAsRecordDecl()))
+return FD;
+  }
+
+  return nullptr;
+}
+
+/// Calculate the offset of a struct field. It may be embedded inside multiple
+/// sub-structs.
+static bool GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD,
+   const FieldDecl *FD, int64_t &Offset) {
+  if (RD->isImplicit())
+return false;
+
+  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+  uint32_t FieldNo = 0;
+
+  for (const FieldDecl *Field : RD->fields()) {
+if (Field == FD) {
+  Offset += Layout.getFieldOffset(FieldNo);
+  return true;
 }
 
-QualType Ty = FD->getType();
-if (Ty->isRecordType()) {
-  if (const FieldDecl *Field = FindFlexibleArrayMemberFieldAndOffset(
-  Ctx, Ty->getAsRecordDecl(), FAMDecl, Offset)) {
-const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+if (auto RT = Field->getType()->getAs()) {
+  if (GetFieldOffset(Ctx, RT->getAsRecordDecl(), FD, Offset)) {
 Offset += Layout.getFieldOffset(FieldNo);
-return Field;
+return true;
   }
 }
 
 if (!RD->isUnion())
   ++FieldNo;
   }
 
-  return nullptr;
+  return false;
 }
 
-static unsigned CountCountedByAttrs(const RecordDecl *RD) {
-  unsigned Num = 0;
+llvm::Value *
+CodeGenFunction::emitCountedByMemberSize(const Expr *E, llvm::Value *EmittedE,
+ unsigned Type,
+ llvm::IntegerType *ResType) {
+  ASTContext &Ctx = getContext();
 
-  for (const FieldDecl *FD : RD->fields()) {
-if (FD->getType()->isCountAttributedType())
-  return ++Num;
+  // Note: If the whole struct is specificed in the __bdos (i.e. Visitor
+  // returns a DeclRefExpr). The calculation of the whole size of the structure
+  // with a flexible array member can be done in two ways:
+  //
+  // 1) sizeof(struct S) + count * sizeof(typeof(fam))
+  // 2) offsetof(struct S, fam) + count * sizeof(typeof(fam))
+  //
+  // The first will add additional padding after the end of the array
+  // allocation while the second method is more precise, but not quite expected
+  // from programmers. See
+  // https://lore.kernel.org/l

[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-15 Thread Nick Desaulniers via cfe-commits


@@ -1060,238 +1061,348 @@ 
CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type,
   return ConstantInt::get(ResType, ObjectSize, /*isSigned=*/true);
 }
 
-const FieldDecl *CodeGenFunction::FindFlexibleArrayMemberFieldAndOffset(
-ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FAMDecl,
-uint64_t &Offset) {
+namespace {
+
+/// StructFieldAccess is a simple visitor class to grab the first MemberExpr
+/// from an Expr. It records any ArraySubscriptExpr we meet along the way.
+class StructFieldAccess
+: public ConstStmtVisitor {
+  bool AddrOfSeen = false;
+
+public:
+  const ArraySubscriptExpr *ASE = nullptr;
+
+  const MemberExpr *VisitMemberExpr(const MemberExpr *E) {
+if (AddrOfSeen && E->getType()->isArrayType())
+  // Avoid forms like '&ptr->array'.
+  return nullptr;
+return E;
+  }
+
+  const MemberExpr *VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
+AddrOfSeen = false; // '&ptr->array[idx]' is okay.
+ASE = E;
+return Visit(E->getBase());
+  }
+  const MemberExpr *VisitCastExpr(const CastExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitParenExpr(const ParenExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryAddrOf(const clang::UnaryOperator *E) {
+AddrOfSeen = true;
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryDeref(const clang::UnaryOperator *E) {
+AddrOfSeen = false;
+return Visit(E->getSubExpr());
+  }
+};
+
+} // end anonymous namespace
+
+/// Find a struct's flexible array member. It may be embedded inside multiple
+/// sub-structs, but must still be the last field.
+static const FieldDecl *FindFlexibleArrayMemberField(CodeGenFunction &CGF,
+ ASTContext &Ctx,
+ const RecordDecl *RD) {
   const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
-  getLangOpts().getStrictFlexArraysLevel();
-  uint32_t FieldNo = 0;
+  CGF.getLangOpts().getStrictFlexArraysLevel();
 
   if (RD->isImplicit())
 return nullptr;
 
   for (const FieldDecl *FD : RD->fields()) {
-if ((!FAMDecl || FD == FAMDecl) &&
-Decl::isFlexibleArrayMemberLike(
+if (Decl::isFlexibleArrayMemberLike(
 Ctx, FD, FD->getType(), StrictFlexArraysLevel,
-/*IgnoreTemplateOrMacroSubstitution=*/true)) {
-  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
-  Offset += Layout.getFieldOffset(FieldNo);
+/*IgnoreTemplateOrMacroSubstitution=*/true))
   return FD;
+
+if (auto RT = FD->getType()->getAs())
+  if (const FieldDecl *FD =
+  FindFlexibleArrayMemberField(CGF, Ctx, RT->getAsRecordDecl()))
+return FD;
+  }
+
+  return nullptr;
+}
+
+/// Calculate the offset of a struct field. It may be embedded inside multiple
+/// sub-structs.
+static bool GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD,
+   const FieldDecl *FD, int64_t &Offset) {
+  if (RD->isImplicit())
+return false;
+
+  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+  uint32_t FieldNo = 0;
+
+  for (const FieldDecl *Field : RD->fields()) {
+if (Field == FD) {
+  Offset += Layout.getFieldOffset(FieldNo);
+  return true;
 }
 
-QualType Ty = FD->getType();
-if (Ty->isRecordType()) {
-  if (const FieldDecl *Field = FindFlexibleArrayMemberFieldAndOffset(
-  Ctx, Ty->getAsRecordDecl(), FAMDecl, Offset)) {
-const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+if (auto RT = Field->getType()->getAs()) {
+  if (GetFieldOffset(Ctx, RT->getAsRecordDecl(), FD, Offset)) {
 Offset += Layout.getFieldOffset(FieldNo);
-return Field;
+return true;
   }
 }
 
 if (!RD->isUnion())
   ++FieldNo;
   }
 
-  return nullptr;
+  return false;
 }
 
-static unsigned CountCountedByAttrs(const RecordDecl *RD) {
-  unsigned Num = 0;
+llvm::Value *
+CodeGenFunction::emitCountedByMemberSize(const Expr *E, llvm::Value *EmittedE,
+ unsigned Type,
+ llvm::IntegerType *ResType) {
+  ASTContext &Ctx = getContext();
 
-  for (const FieldDecl *FD : RD->fields()) {
-if (FD->getType()->isCountAttributedType())
-  return ++Num;
+  // Note: If the whole struct is specificed in the __bdos (i.e. Visitor
+  // returns a DeclRefExpr). The calculation of the whole size of the structure
+  // with a flexible array member can be done in two ways:
+  //
+  // 1) sizeof(struct S) + count * sizeof(typeof(fam))
+  // 2) offsetof(struct S, fam) + count * sizeof(typeof(fam))
+  //
+  // The first will add additional padding after the end of the array
+  // allocation while the second method is more precise, but not quite expected
+  // from programmers. See
+  // https://lore.kernel.org/l

[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-15 Thread Nick Desaulniers via cfe-commits


@@ -1060,238 +1061,348 @@ 
CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type,
   return ConstantInt::get(ResType, ObjectSize, /*isSigned=*/true);
 }
 
-const FieldDecl *CodeGenFunction::FindFlexibleArrayMemberFieldAndOffset(
-ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FAMDecl,
-uint64_t &Offset) {
+namespace {
+
+/// StructFieldAccess is a simple visitor class to grab the first MemberExpr
+/// from an Expr. It records any ArraySubscriptExpr we meet along the way.
+class StructFieldAccess
+: public ConstStmtVisitor {
+  bool AddrOfSeen = false;
+
+public:
+  const ArraySubscriptExpr *ASE = nullptr;
+
+  const MemberExpr *VisitMemberExpr(const MemberExpr *E) {
+if (AddrOfSeen && E->getType()->isArrayType())
+  // Avoid forms like '&ptr->array'.
+  return nullptr;
+return E;
+  }
+
+  const MemberExpr *VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
+AddrOfSeen = false; // '&ptr->array[idx]' is okay.
+ASE = E;
+return Visit(E->getBase());
+  }
+  const MemberExpr *VisitCastExpr(const CastExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitParenExpr(const ParenExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryAddrOf(const clang::UnaryOperator *E) {
+AddrOfSeen = true;
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryDeref(const clang::UnaryOperator *E) {
+AddrOfSeen = false;
+return Visit(E->getSubExpr());
+  }
+};
+
+} // end anonymous namespace
+
+/// Find a struct's flexible array member. It may be embedded inside multiple
+/// sub-structs, but must still be the last field.
+static const FieldDecl *FindFlexibleArrayMemberField(CodeGenFunction &CGF,
+ ASTContext &Ctx,
+ const RecordDecl *RD) {
   const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
-  getLangOpts().getStrictFlexArraysLevel();
-  uint32_t FieldNo = 0;
+  CGF.getLangOpts().getStrictFlexArraysLevel();
 
   if (RD->isImplicit())
 return nullptr;
 
   for (const FieldDecl *FD : RD->fields()) {
-if ((!FAMDecl || FD == FAMDecl) &&
-Decl::isFlexibleArrayMemberLike(
+if (Decl::isFlexibleArrayMemberLike(
 Ctx, FD, FD->getType(), StrictFlexArraysLevel,
-/*IgnoreTemplateOrMacroSubstitution=*/true)) {
-  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
-  Offset += Layout.getFieldOffset(FieldNo);
+/*IgnoreTemplateOrMacroSubstitution=*/true))
   return FD;
+
+if (auto RT = FD->getType()->getAs())
+  if (const FieldDecl *FD =
+  FindFlexibleArrayMemberField(CGF, Ctx, RT->getAsRecordDecl()))
+return FD;
+  }
+
+  return nullptr;
+}
+
+/// Calculate the offset of a struct field. It may be embedded inside multiple
+/// sub-structs.
+static bool GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD,
+   const FieldDecl *FD, int64_t &Offset) {
+  if (RD->isImplicit())
+return false;
+
+  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+  uint32_t FieldNo = 0;
+
+  for (const FieldDecl *Field : RD->fields()) {
+if (Field == FD) {
+  Offset += Layout.getFieldOffset(FieldNo);
+  return true;
 }
 
-QualType Ty = FD->getType();
-if (Ty->isRecordType()) {
-  if (const FieldDecl *Field = FindFlexibleArrayMemberFieldAndOffset(
-  Ctx, Ty->getAsRecordDecl(), FAMDecl, Offset)) {
-const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+if (auto RT = Field->getType()->getAs()) {
+  if (GetFieldOffset(Ctx, RT->getAsRecordDecl(), FD, Offset)) {
 Offset += Layout.getFieldOffset(FieldNo);
-return Field;
+return true;
   }
 }
 
 if (!RD->isUnion())
   ++FieldNo;
   }
 
-  return nullptr;
+  return false;
 }
 
-static unsigned CountCountedByAttrs(const RecordDecl *RD) {
-  unsigned Num = 0;
+llvm::Value *
+CodeGenFunction::emitCountedByMemberSize(const Expr *E, llvm::Value *EmittedE,
+ unsigned Type,
+ llvm::IntegerType *ResType) {
+  ASTContext &Ctx = getContext();
 
-  for (const FieldDecl *FD : RD->fields()) {
-if (FD->getType()->isCountAttributedType())
-  return ++Num;
+  // Note: If the whole struct is specificed in the __bdos (i.e. Visitor
+  // returns a DeclRefExpr). The calculation of the whole size of the structure
+  // with a flexible array member can be done in two ways:
+  //
+  // 1) sizeof(struct S) + count * sizeof(typeof(fam))
+  // 2) offsetof(struct S, fam) + count * sizeof(typeof(fam))
+  //
+  // The first will add additional padding after the end of the array
+  // allocation while the second method is more precise, but not quite expected
+  // from programmers. See
+  // https://lore.kernel.org/l

[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-15 Thread Nick Desaulniers via cfe-commits


@@ -1060,238 +1061,348 @@ 
CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type,
   return ConstantInt::get(ResType, ObjectSize, /*isSigned=*/true);
 }
 
-const FieldDecl *CodeGenFunction::FindFlexibleArrayMemberFieldAndOffset(
-ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FAMDecl,
-uint64_t &Offset) {
+namespace {
+
+/// StructFieldAccess is a simple visitor class to grab the first MemberExpr
+/// from an Expr. It records any ArraySubscriptExpr we meet along the way.
+struct StructFieldAccess
+: public ConstStmtVisitor {
+  const ArraySubscriptExpr *ASE = nullptr;
+  bool AddrOfSeen = false;
+
+  const MemberExpr *VisitMemberExpr(const MemberExpr *E) {
+if (AddrOfSeen && E->getType()->isArrayType())
+  // Avoid forms like '&ptr->array'.
+  return nullptr;
+return E;
+  }
+
+  const MemberExpr *VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
+AddrOfSeen = false; // '&ptr->array[idx]' is okay.
+ASE = E;
+return Visit(E->getBase());
+  }
+  const MemberExpr *VisitCastExpr(const CastExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitParenExpr(const ParenExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryAddrOf(const clang::UnaryOperator *E) {
+AddrOfSeen = true;
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryDeref(const clang::UnaryOperator *E) {
+AddrOfSeen = false;
+return Visit(E->getSubExpr());
+  }
+};
+
+} // end anonymous namespace
+
+/// Find a struct's flexible array member. It may be embedded inside multiple
+/// sub-structs, but must still be the last field.
+static const FieldDecl *FindFlexibleArrayMemberField(CodeGenFunction &CGF,
+ ASTContext &Ctx,
+ const RecordDecl *RD) {
   const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
-  getLangOpts().getStrictFlexArraysLevel();
-  uint32_t FieldNo = 0;
+  CGF.getLangOpts().getStrictFlexArraysLevel();
 
   if (RD->isImplicit())
 return nullptr;
 
   for (const FieldDecl *FD : RD->fields()) {
-if ((!FAMDecl || FD == FAMDecl) &&
-Decl::isFlexibleArrayMemberLike(
+if (Decl::isFlexibleArrayMemberLike(
 Ctx, FD, FD->getType(), StrictFlexArraysLevel,
-/*IgnoreTemplateOrMacroSubstitution=*/true)) {
-  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
-  Offset += Layout.getFieldOffset(FieldNo);
+/*IgnoreTemplateOrMacroSubstitution=*/true))
   return FD;
+
+if (auto RT = FD->getType()->getAs())
+  if (const FieldDecl *FD =
+  FindFlexibleArrayMemberField(CGF, Ctx, RT->getAsRecordDecl()))
+return FD;
+  }
+
+  return nullptr;
+}
+
+/// Calculate the offset of a struct field. It may be embedded inside multiple
+/// sub-structs.
+static bool GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD,
+   const FieldDecl *FD, int64_t &Offset) {
+  if (RD->isImplicit())
+return false;
+
+  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+  uint32_t FieldNo = 0;
+
+  for (const FieldDecl *Field : RD->fields()) {
+if (Field == FD) {
+  Offset += Layout.getFieldOffset(FieldNo);
+  return true;
 }
 
-QualType Ty = FD->getType();
-if (Ty->isRecordType()) {
-  if (const FieldDecl *Field = FindFlexibleArrayMemberFieldAndOffset(
-  Ctx, Ty->getAsRecordDecl(), FAMDecl, Offset)) {
-const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+if (auto RT = Field->getType()->getAs()) {
+  if (GetFieldOffset(Ctx, RT->getAsRecordDecl(), FD, Offset)) {
 Offset += Layout.getFieldOffset(FieldNo);
-return Field;
+return true;
   }
 }
 
 if (!RD->isUnion())
   ++FieldNo;
   }
 
-  return nullptr;
+  return false;
 }
 
-static unsigned CountCountedByAttrs(const RecordDecl *RD) {
-  unsigned Num = 0;
+llvm::Value *
+CodeGenFunction::emitCountedByMemberSize(const Expr *E, llvm::Value *EmittedE,
+ unsigned Type,
+ llvm::IntegerType *ResType) {
+  ASTContext &Ctx = getContext();
 
-  for (const FieldDecl *FD : RD->fields()) {
-if (FD->getType()->isCountAttributedType())
-  return ++Num;
+  // Note: If the whole struct is specificed in the __bdos (i.e. Visitor
+  // returns a DeclRefExpr). The calculation of the whole size of the structure
+  // with a flexible array member can be done in two ways:
+  //
+  // 1) sizeof(struct S) + count * sizeof(typeof(fam))
+  // 2) offsetof(struct S, fam) + count * sizeof(typeof(fam))
+  //
+  // The first will add additional padding after the end of the array
+  // allocation while the second method is more precise, but not quite expected
+  // from programmers. See
+  // https://lore.kernel.org/lkml/ZvV6X5

[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-15 Thread Nick Desaulniers via cfe-commits


@@ -1060,238 +1061,348 @@ 
CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type,
   return ConstantInt::get(ResType, ObjectSize, /*isSigned=*/true);
 }
 
-const FieldDecl *CodeGenFunction::FindFlexibleArrayMemberFieldAndOffset(
-ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FAMDecl,
-uint64_t &Offset) {
+namespace {
+
+/// StructFieldAccess is a simple visitor class to grab the first MemberExpr
+/// from an Expr. It records any ArraySubscriptExpr we meet along the way.
+class StructFieldAccess
+: public ConstStmtVisitor {
+  bool AddrOfSeen = false;
+
+public:
+  const ArraySubscriptExpr *ASE = nullptr;
+
+  const MemberExpr *VisitMemberExpr(const MemberExpr *E) {
+if (AddrOfSeen && E->getType()->isArrayType())
+  // Avoid forms like '&ptr->array'.
+  return nullptr;
+return E;
+  }
+
+  const MemberExpr *VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
+AddrOfSeen = false; // '&ptr->array[idx]' is okay.
+ASE = E;
+return Visit(E->getBase());
+  }
+  const MemberExpr *VisitCastExpr(const CastExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitParenExpr(const ParenExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryAddrOf(const clang::UnaryOperator *E) {
+AddrOfSeen = true;
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryDeref(const clang::UnaryOperator *E) {
+AddrOfSeen = false;
+return Visit(E->getSubExpr());
+  }
+};
+
+} // end anonymous namespace
+
+/// Find a struct's flexible array member. It may be embedded inside multiple
+/// sub-structs, but must still be the last field.
+static const FieldDecl *FindFlexibleArrayMemberField(CodeGenFunction &CGF,
+ ASTContext &Ctx,
+ const RecordDecl *RD) {
   const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
-  getLangOpts().getStrictFlexArraysLevel();
-  uint32_t FieldNo = 0;
+  CGF.getLangOpts().getStrictFlexArraysLevel();
 
   if (RD->isImplicit())
 return nullptr;
 
   for (const FieldDecl *FD : RD->fields()) {
-if ((!FAMDecl || FD == FAMDecl) &&
-Decl::isFlexibleArrayMemberLike(
+if (Decl::isFlexibleArrayMemberLike(
 Ctx, FD, FD->getType(), StrictFlexArraysLevel,
-/*IgnoreTemplateOrMacroSubstitution=*/true)) {
-  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
-  Offset += Layout.getFieldOffset(FieldNo);
+/*IgnoreTemplateOrMacroSubstitution=*/true))
   return FD;
+
+if (auto RT = FD->getType()->getAs())
+  if (const FieldDecl *FD =
+  FindFlexibleArrayMemberField(CGF, Ctx, RT->getAsRecordDecl()))
+return FD;
+  }
+
+  return nullptr;
+}
+
+/// Calculate the offset of a struct field. It may be embedded inside multiple
+/// sub-structs.

nickdesaulniers wrote:

Perhaps worth noting in the comment that you're searching for the offset of 
`FD` in `RD` (IIUC)?

https://github.com/llvm/llvm-project/pull/122198
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-15 Thread Nick Desaulniers via cfe-commits


@@ -1060,238 +1061,348 @@ 
CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type,
   return ConstantInt::get(ResType, ObjectSize, /*isSigned=*/true);
 }
 
-const FieldDecl *CodeGenFunction::FindFlexibleArrayMemberFieldAndOffset(
-ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FAMDecl,
-uint64_t &Offset) {
+namespace {
+
+/// StructFieldAccess is a simple visitor class to grab the first MemberExpr
+/// from an Expr. It records any ArraySubscriptExpr we meet along the way.
+class StructFieldAccess
+: public ConstStmtVisitor {
+  bool AddrOfSeen = false;
+
+public:
+  const ArraySubscriptExpr *ASE = nullptr;
+
+  const MemberExpr *VisitMemberExpr(const MemberExpr *E) {
+if (AddrOfSeen && E->getType()->isArrayType())
+  // Avoid forms like '&ptr->array'.
+  return nullptr;
+return E;
+  }
+
+  const MemberExpr *VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
+AddrOfSeen = false; // '&ptr->array[idx]' is okay.
+ASE = E;
+return Visit(E->getBase());
+  }
+  const MemberExpr *VisitCastExpr(const CastExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitParenExpr(const ParenExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryAddrOf(const clang::UnaryOperator *E) {
+AddrOfSeen = true;
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryDeref(const clang::UnaryOperator *E) {
+AddrOfSeen = false;
+return Visit(E->getSubExpr());
+  }
+};
+
+} // end anonymous namespace
+
+/// Find a struct's flexible array member. It may be embedded inside multiple
+/// sub-structs, but must still be the last field.
+static const FieldDecl *FindFlexibleArrayMemberField(CodeGenFunction &CGF,
+ ASTContext &Ctx,
+ const RecordDecl *RD) {
   const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
-  getLangOpts().getStrictFlexArraysLevel();
-  uint32_t FieldNo = 0;
+  CGF.getLangOpts().getStrictFlexArraysLevel();
 
   if (RD->isImplicit())
 return nullptr;
 
   for (const FieldDecl *FD : RD->fields()) {
-if ((!FAMDecl || FD == FAMDecl) &&
-Decl::isFlexibleArrayMemberLike(
+if (Decl::isFlexibleArrayMemberLike(
 Ctx, FD, FD->getType(), StrictFlexArraysLevel,
-/*IgnoreTemplateOrMacroSubstitution=*/true)) {
-  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
-  Offset += Layout.getFieldOffset(FieldNo);
+/*IgnoreTemplateOrMacroSubstitution=*/true))
   return FD;
+
+if (auto RT = FD->getType()->getAs())
+  if (const FieldDecl *FD =
+  FindFlexibleArrayMemberField(CGF, Ctx, RT->getAsRecordDecl()))
+return FD;
+  }
+
+  return nullptr;
+}
+
+/// Calculate the offset of a struct field. It may be embedded inside multiple
+/// sub-structs.
+static bool GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD,
+   const FieldDecl *FD, int64_t &Offset) {
+  if (RD->isImplicit())
+return false;
+
+  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+  uint32_t FieldNo = 0;
+
+  for (const FieldDecl *Field : RD->fields()) {
+if (Field == FD) {
+  Offset += Layout.getFieldOffset(FieldNo);
+  return true;
 }
 
-QualType Ty = FD->getType();
-if (Ty->isRecordType()) {
-  if (const FieldDecl *Field = FindFlexibleArrayMemberFieldAndOffset(
-  Ctx, Ty->getAsRecordDecl(), FAMDecl, Offset)) {
-const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+if (auto RT = Field->getType()->getAs()) {
+  if (GetFieldOffset(Ctx, RT->getAsRecordDecl(), FD, Offset)) {
 Offset += Layout.getFieldOffset(FieldNo);
-return Field;
+return true;
   }
 }
 
 if (!RD->isUnion())
   ++FieldNo;
   }
 
-  return nullptr;
+  return false;
 }
 
-static unsigned CountCountedByAttrs(const RecordDecl *RD) {
-  unsigned Num = 0;
+llvm::Value *
+CodeGenFunction::emitCountedByMemberSize(const Expr *E, llvm::Value *EmittedE,
+ unsigned Type,
+ llvm::IntegerType *ResType) {
+  ASTContext &Ctx = getContext();
 
-  for (const FieldDecl *FD : RD->fields()) {
-if (FD->getType()->isCountAttributedType())
-  return ++Num;
+  // Note: If the whole struct is specificed in the __bdos (i.e. Visitor
+  // returns a DeclRefExpr). The calculation of the whole size of the structure
+  // with a flexible array member can be done in two ways:
+  //
+  // 1) sizeof(struct S) + count * sizeof(typeof(fam))
+  // 2) offsetof(struct S, fam) + count * sizeof(typeof(fam))
+  //
+  // The first will add additional padding after the end of the array
+  // allocation while the second method is more precise, but not quite expected
+  // from programmers. See
+  // https://lore.kernel.org/l

[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-15 Thread Nick Desaulniers via cfe-commits


@@ -1060,238 +1061,348 @@ 
CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type,
   return ConstantInt::get(ResType, ObjectSize, /*isSigned=*/true);
 }
 
-const FieldDecl *CodeGenFunction::FindFlexibleArrayMemberFieldAndOffset(
-ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FAMDecl,
-uint64_t &Offset) {
+namespace {
+
+/// StructFieldAccess is a simple visitor class to grab the first MemberExpr
+/// from an Expr. It records any ArraySubscriptExpr we meet along the way.
+class StructFieldAccess
+: public ConstStmtVisitor {
+  bool AddrOfSeen = false;
+
+public:
+  const ArraySubscriptExpr *ASE = nullptr;
+
+  const MemberExpr *VisitMemberExpr(const MemberExpr *E) {
+if (AddrOfSeen && E->getType()->isArrayType())
+  // Avoid forms like '&ptr->array'.
+  return nullptr;
+return E;
+  }
+
+  const MemberExpr *VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
+AddrOfSeen = false; // '&ptr->array[idx]' is okay.
+ASE = E;
+return Visit(E->getBase());
+  }
+  const MemberExpr *VisitCastExpr(const CastExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitParenExpr(const ParenExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryAddrOf(const clang::UnaryOperator *E) {
+AddrOfSeen = true;
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryDeref(const clang::UnaryOperator *E) {
+AddrOfSeen = false;
+return Visit(E->getSubExpr());
+  }
+};
+
+} // end anonymous namespace
+
+/// Find a struct's flexible array member. It may be embedded inside multiple
+/// sub-structs, but must still be the last field.
+static const FieldDecl *FindFlexibleArrayMemberField(CodeGenFunction &CGF,
+ ASTContext &Ctx,
+ const RecordDecl *RD) {
   const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
-  getLangOpts().getStrictFlexArraysLevel();
-  uint32_t FieldNo = 0;
+  CGF.getLangOpts().getStrictFlexArraysLevel();
 
   if (RD->isImplicit())
 return nullptr;
 
   for (const FieldDecl *FD : RD->fields()) {
-if ((!FAMDecl || FD == FAMDecl) &&
-Decl::isFlexibleArrayMemberLike(
+if (Decl::isFlexibleArrayMemberLike(
 Ctx, FD, FD->getType(), StrictFlexArraysLevel,
-/*IgnoreTemplateOrMacroSubstitution=*/true)) {
-  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
-  Offset += Layout.getFieldOffset(FieldNo);
+/*IgnoreTemplateOrMacroSubstitution=*/true))
   return FD;
+
+if (auto RT = FD->getType()->getAs())
+  if (const FieldDecl *FD =
+  FindFlexibleArrayMemberField(CGF, Ctx, RT->getAsRecordDecl()))
+return FD;
+  }
+
+  return nullptr;
+}
+
+/// Calculate the offset of a struct field. It may be embedded inside multiple
+/// sub-structs.
+static bool GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD,
+   const FieldDecl *FD, int64_t &Offset) {
+  if (RD->isImplicit())
+return false;
+
+  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+  uint32_t FieldNo = 0;

nickdesaulniers wrote:

Rather than keeping a count, it looks like `FieldDecl` has a method 
`FieldDecl::getFieldIndex` that can do what you need here (it even has a 
comment saying it can be used to then pass info to 
`ASTRecordLayout::getFieldOffset`. It's definitely heavier weight than a simple 
counter, but can you use it here?  I guess one issue is that you skip unions, 
but maybe `FieldDecl::getFieldIndex` handles that case for you? Or no?

The special case for unions is curious; perhaps unit tests for unions with the 
order of members flipped might expose an edge case.

https://github.com/llvm/llvm-project/pull/122198
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-14 Thread Bill Wendling via cfe-commits


@@ -1060,238 +1061,348 @@ 
CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type,
   return ConstantInt::get(ResType, ObjectSize, /*isSigned=*/true);
 }
 
-const FieldDecl *CodeGenFunction::FindFlexibleArrayMemberFieldAndOffset(
-ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FAMDecl,
-uint64_t &Offset) {
+namespace {
+
+/// StructFieldAccess is a simple visitor class to grab the first MemberExpr
+/// from an Expr. It records any ArraySubscriptExpr we meet along the way.
+struct StructFieldAccess
+: public ConstStmtVisitor {
+  const ArraySubscriptExpr *ASE = nullptr;
+  bool AddrOfSeen = false;
+
+  const MemberExpr *VisitMemberExpr(const MemberExpr *E) {
+if (AddrOfSeen && E->getType()->isArrayType())
+  // Avoid forms like '&ptr->array'.
+  return nullptr;
+return E;
+  }
+
+  const MemberExpr *VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
+AddrOfSeen = false; // '&ptr->array[idx]' is okay.
+ASE = E;
+return Visit(E->getBase());
+  }
+  const MemberExpr *VisitCastExpr(const CastExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitParenExpr(const ParenExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryAddrOf(const clang::UnaryOperator *E) {
+AddrOfSeen = true;
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryDeref(const clang::UnaryOperator *E) {
+AddrOfSeen = false;
+return Visit(E->getSubExpr());
+  }
+};
+
+} // end anonymous namespace
+
+/// Find a struct's flexible array member. It may be embedded inside multiple
+/// sub-structs, but must still be the last field.
+static const FieldDecl *FindFlexibleArrayMemberField(CodeGenFunction &CGF,
+ ASTContext &Ctx,
+ const RecordDecl *RD) {
   const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
-  getLangOpts().getStrictFlexArraysLevel();
-  uint32_t FieldNo = 0;
+  CGF.getLangOpts().getStrictFlexArraysLevel();
 
   if (RD->isImplicit())
 return nullptr;
 
   for (const FieldDecl *FD : RD->fields()) {
-if ((!FAMDecl || FD == FAMDecl) &&
-Decl::isFlexibleArrayMemberLike(
+if (Decl::isFlexibleArrayMemberLike(
 Ctx, FD, FD->getType(), StrictFlexArraysLevel,
-/*IgnoreTemplateOrMacroSubstitution=*/true)) {
-  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
-  Offset += Layout.getFieldOffset(FieldNo);
+/*IgnoreTemplateOrMacroSubstitution=*/true))
   return FD;
+
+if (auto RT = FD->getType()->getAs())
+  if (const FieldDecl *FD =
+  FindFlexibleArrayMemberField(CGF, Ctx, RT->getAsRecordDecl()))
+return FD;
+  }
+
+  return nullptr;
+}
+
+/// Calculate the offset of a struct field. It may be embedded inside multiple
+/// sub-structs.
+static bool GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD,
+   const FieldDecl *FD, int64_t &Offset) {
+  if (RD->isImplicit())
+return false;
+
+  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+  uint32_t FieldNo = 0;
+
+  for (const FieldDecl *Field : RD->fields()) {
+if (Field == FD) {
+  Offset += Layout.getFieldOffset(FieldNo);
+  return true;
 }
 
-QualType Ty = FD->getType();
-if (Ty->isRecordType()) {
-  if (const FieldDecl *Field = FindFlexibleArrayMemberFieldAndOffset(
-  Ctx, Ty->getAsRecordDecl(), FAMDecl, Offset)) {
-const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+if (auto RT = Field->getType()->getAs()) {
+  if (GetFieldOffset(Ctx, RT->getAsRecordDecl(), FD, Offset)) {
 Offset += Layout.getFieldOffset(FieldNo);
-return Field;
+return true;
   }
 }
 
 if (!RD->isUnion())
   ++FieldNo;
   }
 
-  return nullptr;
+  return false;
 }
 
-static unsigned CountCountedByAttrs(const RecordDecl *RD) {
-  unsigned Num = 0;
+llvm::Value *
+CodeGenFunction::emitCountedByMemberSize(const Expr *E, llvm::Value *EmittedE,
+ unsigned Type,
+ llvm::IntegerType *ResType) {
+  ASTContext &Ctx = getContext();
 
-  for (const FieldDecl *FD : RD->fields()) {
-if (FD->getType()->isCountAttributedType())
-  return ++Num;
+  // Note: If the whole struct is specificed in the __bdos (i.e. Visitor
+  // returns a DeclRefExpr). The calculation of the whole size of the structure
+  // with a flexible array member can be done in two ways:
+  //
+  // 1) sizeof(struct S) + count * sizeof(typeof(fam))
+  // 2) offsetof(struct S, fam) + count * sizeof(typeof(fam))
+  //
+  // The first will add additional padding after the end of the array
+  // allocation while the second method is more precise, but not quite expected
+  // from programmers. See
+  // https://lore.kernel.org/lkml/ZvV6X5

[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-14 Thread Nick Desaulniers via cfe-commits


@@ -1060,238 +1061,348 @@ 
CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type,
   return ConstantInt::get(ResType, ObjectSize, /*isSigned=*/true);
 }
 
-const FieldDecl *CodeGenFunction::FindFlexibleArrayMemberFieldAndOffset(
-ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FAMDecl,
-uint64_t &Offset) {
+namespace {
+
+/// StructFieldAccess is a simple visitor class to grab the first MemberExpr
+/// from an Expr. It records any ArraySubscriptExpr we meet along the way.
+struct StructFieldAccess
+: public ConstStmtVisitor {
+  const ArraySubscriptExpr *ASE = nullptr;
+  bool AddrOfSeen = false;
+
+  const MemberExpr *VisitMemberExpr(const MemberExpr *E) {
+if (AddrOfSeen && E->getType()->isArrayType())
+  // Avoid forms like '&ptr->array'.
+  return nullptr;
+return E;
+  }
+
+  const MemberExpr *VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
+AddrOfSeen = false; // '&ptr->array[idx]' is okay.
+ASE = E;
+return Visit(E->getBase());
+  }
+  const MemberExpr *VisitCastExpr(const CastExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitParenExpr(const ParenExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryAddrOf(const clang::UnaryOperator *E) {
+AddrOfSeen = true;
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryDeref(const clang::UnaryOperator *E) {
+AddrOfSeen = false;
+return Visit(E->getSubExpr());
+  }
+};
+
+} // end anonymous namespace
+
+/// Find a struct's flexible array member. It may be embedded inside multiple
+/// sub-structs, but must still be the last field.
+static const FieldDecl *FindFlexibleArrayMemberField(CodeGenFunction &CGF,
+ ASTContext &Ctx,
+ const RecordDecl *RD) {
   const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
-  getLangOpts().getStrictFlexArraysLevel();
-  uint32_t FieldNo = 0;
+  CGF.getLangOpts().getStrictFlexArraysLevel();
 
   if (RD->isImplicit())
 return nullptr;
 
   for (const FieldDecl *FD : RD->fields()) {
-if ((!FAMDecl || FD == FAMDecl) &&
-Decl::isFlexibleArrayMemberLike(
+if (Decl::isFlexibleArrayMemberLike(
 Ctx, FD, FD->getType(), StrictFlexArraysLevel,
-/*IgnoreTemplateOrMacroSubstitution=*/true)) {
-  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
-  Offset += Layout.getFieldOffset(FieldNo);
+/*IgnoreTemplateOrMacroSubstitution=*/true))
   return FD;
+
+if (auto RT = FD->getType()->getAs())
+  if (const FieldDecl *FD =
+  FindFlexibleArrayMemberField(CGF, Ctx, RT->getAsRecordDecl()))
+return FD;
+  }
+
+  return nullptr;
+}
+
+/// Calculate the offset of a struct field. It may be embedded inside multiple
+/// sub-structs.
+static bool GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD,
+   const FieldDecl *FD, int64_t &Offset) {
+  if (RD->isImplicit())
+return false;
+
+  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+  uint32_t FieldNo = 0;
+
+  for (const FieldDecl *Field : RD->fields()) {
+if (Field == FD) {
+  Offset += Layout.getFieldOffset(FieldNo);
+  return true;
 }
 
-QualType Ty = FD->getType();
-if (Ty->isRecordType()) {
-  if (const FieldDecl *Field = FindFlexibleArrayMemberFieldAndOffset(
-  Ctx, Ty->getAsRecordDecl(), FAMDecl, Offset)) {
-const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+if (auto RT = Field->getType()->getAs()) {
+  if (GetFieldOffset(Ctx, RT->getAsRecordDecl(), FD, Offset)) {
 Offset += Layout.getFieldOffset(FieldNo);
-return Field;
+return true;
   }
 }
 
 if (!RD->isUnion())
   ++FieldNo;
   }
 
-  return nullptr;
+  return false;
 }
 
-static unsigned CountCountedByAttrs(const RecordDecl *RD) {
-  unsigned Num = 0;
+llvm::Value *
+CodeGenFunction::emitCountedByMemberSize(const Expr *E, llvm::Value *EmittedE,
+ unsigned Type,
+ llvm::IntegerType *ResType) {
+  ASTContext &Ctx = getContext();
 
-  for (const FieldDecl *FD : RD->fields()) {
-if (FD->getType()->isCountAttributedType())
-  return ++Num;
+  // Note: If the whole struct is specificed in the __bdos (i.e. Visitor
+  // returns a DeclRefExpr). The calculation of the whole size of the structure
+  // with a flexible array member can be done in two ways:
+  //
+  // 1) sizeof(struct S) + count * sizeof(typeof(fam))
+  // 2) offsetof(struct S, fam) + count * sizeof(typeof(fam))
+  //
+  // The first will add additional padding after the end of the array
+  // allocation while the second method is more precise, but not quite expected
+  // from programmers. See
+  // https://lore.kernel.org/lkml/ZvV6X5

[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-14 Thread Nick Desaulniers via cfe-commits


@@ -1060,238 +1061,348 @@ 
CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type,
   return ConstantInt::get(ResType, ObjectSize, /*isSigned=*/true);
 }
 
-const FieldDecl *CodeGenFunction::FindFlexibleArrayMemberFieldAndOffset(
-ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FAMDecl,
-uint64_t &Offset) {
+namespace {
+
+/// StructFieldAccess is a simple visitor class to grab the first MemberExpr
+/// from an Expr. It records any ArraySubscriptExpr we meet along the way.
+struct StructFieldAccess
+: public ConstStmtVisitor {
+  const ArraySubscriptExpr *ASE = nullptr;
+  bool AddrOfSeen = false;
+
+  const MemberExpr *VisitMemberExpr(const MemberExpr *E) {
+if (AddrOfSeen && E->getType()->isArrayType())
+  // Avoid forms like '&ptr->array'.
+  return nullptr;
+return E;
+  }
+
+  const MemberExpr *VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
+AddrOfSeen = false; // '&ptr->array[idx]' is okay.
+ASE = E;
+return Visit(E->getBase());
+  }
+  const MemberExpr *VisitCastExpr(const CastExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitParenExpr(const ParenExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryAddrOf(const clang::UnaryOperator *E) {
+AddrOfSeen = true;
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryDeref(const clang::UnaryOperator *E) {
+AddrOfSeen = false;
+return Visit(E->getSubExpr());
+  }
+};
+
+} // end anonymous namespace
+
+/// Find a struct's flexible array member. It may be embedded inside multiple
+/// sub-structs, but must still be the last field.
+static const FieldDecl *FindFlexibleArrayMemberField(CodeGenFunction &CGF,
+ ASTContext &Ctx,
+ const RecordDecl *RD) {
   const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
-  getLangOpts().getStrictFlexArraysLevel();
-  uint32_t FieldNo = 0;
+  CGF.getLangOpts().getStrictFlexArraysLevel();
 
   if (RD->isImplicit())
 return nullptr;
 
   for (const FieldDecl *FD : RD->fields()) {
-if ((!FAMDecl || FD == FAMDecl) &&
-Decl::isFlexibleArrayMemberLike(
+if (Decl::isFlexibleArrayMemberLike(
 Ctx, FD, FD->getType(), StrictFlexArraysLevel,
-/*IgnoreTemplateOrMacroSubstitution=*/true)) {
-  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
-  Offset += Layout.getFieldOffset(FieldNo);
+/*IgnoreTemplateOrMacroSubstitution=*/true))
   return FD;
+
+if (auto RT = FD->getType()->getAs())
+  if (const FieldDecl *FD =
+  FindFlexibleArrayMemberField(CGF, Ctx, RT->getAsRecordDecl()))
+return FD;
+  }
+
+  return nullptr;
+}
+
+/// Calculate the offset of a struct field. It may be embedded inside multiple
+/// sub-structs.
+static bool GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD,
+   const FieldDecl *FD, int64_t &Offset) {
+  if (RD->isImplicit())
+return false;
+
+  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+  uint32_t FieldNo = 0;
+
+  for (const FieldDecl *Field : RD->fields()) {
+if (Field == FD) {
+  Offset += Layout.getFieldOffset(FieldNo);
+  return true;
 }
 
-QualType Ty = FD->getType();
-if (Ty->isRecordType()) {
-  if (const FieldDecl *Field = FindFlexibleArrayMemberFieldAndOffset(
-  Ctx, Ty->getAsRecordDecl(), FAMDecl, Offset)) {
-const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+if (auto RT = Field->getType()->getAs()) {
+  if (GetFieldOffset(Ctx, RT->getAsRecordDecl(), FD, Offset)) {
 Offset += Layout.getFieldOffset(FieldNo);
-return Field;
+return true;
   }
 }
 
 if (!RD->isUnion())
   ++FieldNo;
   }
 
-  return nullptr;
+  return false;
 }
 
-static unsigned CountCountedByAttrs(const RecordDecl *RD) {
-  unsigned Num = 0;
+llvm::Value *
+CodeGenFunction::emitCountedByMemberSize(const Expr *E, llvm::Value *EmittedE,
+ unsigned Type,
+ llvm::IntegerType *ResType) {
+  ASTContext &Ctx = getContext();
 
-  for (const FieldDecl *FD : RD->fields()) {
-if (FD->getType()->isCountAttributedType())
-  return ++Num;
+  // Note: If the whole struct is specificed in the __bdos (i.e. Visitor
+  // returns a DeclRefExpr). The calculation of the whole size of the structure
+  // with a flexible array member can be done in two ways:
+  //
+  // 1) sizeof(struct S) + count * sizeof(typeof(fam))
+  // 2) offsetof(struct S, fam) + count * sizeof(typeof(fam))
+  //
+  // The first will add additional padding after the end of the array
+  // allocation while the second method is more precise, but not quite expected
+  // from programmers. See
+  // https://lore.kernel.org/lkml/ZvV6X5

[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-14 Thread Nick Desaulniers via cfe-commits


@@ -1060,238 +1061,348 @@ 
CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type,
   return ConstantInt::get(ResType, ObjectSize, /*isSigned=*/true);
 }
 
-const FieldDecl *CodeGenFunction::FindFlexibleArrayMemberFieldAndOffset(
-ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FAMDecl,
-uint64_t &Offset) {
+namespace {
+
+/// StructFieldAccess is a simple visitor class to grab the first MemberExpr
+/// from an Expr. It records any ArraySubscriptExpr we meet along the way.
+struct StructFieldAccess
+: public ConstStmtVisitor {
+  const ArraySubscriptExpr *ASE = nullptr;
+  bool AddrOfSeen = false;

nickdesaulniers wrote:

`AddrOfSeen` is not referenced outside of the methods. Consider making it 
`private`.

https://github.com/llvm/llvm-project/pull/122198
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-14 Thread Nick Desaulniers via cfe-commits


@@ -1060,238 +1061,348 @@ 
CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type,
   return ConstantInt::get(ResType, ObjectSize, /*isSigned=*/true);
 }
 
-const FieldDecl *CodeGenFunction::FindFlexibleArrayMemberFieldAndOffset(
-ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FAMDecl,
-uint64_t &Offset) {
+namespace {
+
+/// StructFieldAccess is a simple visitor class to grab the first MemberExpr
+/// from an Expr. It records any ArraySubscriptExpr we meet along the way.
+struct StructFieldAccess
+: public ConstStmtVisitor {
+  const ArraySubscriptExpr *ASE = nullptr;
+  bool AddrOfSeen = false;
+
+  const MemberExpr *VisitMemberExpr(const MemberExpr *E) {
+if (AddrOfSeen && E->getType()->isArrayType())
+  // Avoid forms like '&ptr->array'.
+  return nullptr;
+return E;
+  }
+
+  const MemberExpr *VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
+AddrOfSeen = false; // '&ptr->array[idx]' is okay.
+ASE = E;
+return Visit(E->getBase());
+  }
+  const MemberExpr *VisitCastExpr(const CastExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitParenExpr(const ParenExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryAddrOf(const clang::UnaryOperator *E) {
+AddrOfSeen = true;
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryDeref(const clang::UnaryOperator *E) {
+AddrOfSeen = false;
+return Visit(E->getSubExpr());
+  }
+};
+
+} // end anonymous namespace
+
+/// Find a struct's flexible array member. It may be embedded inside multiple
+/// sub-structs, but must still be the last field.
+static const FieldDecl *FindFlexibleArrayMemberField(CodeGenFunction &CGF,
+ ASTContext &Ctx,
+ const RecordDecl *RD) {
   const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
-  getLangOpts().getStrictFlexArraysLevel();
-  uint32_t FieldNo = 0;
+  CGF.getLangOpts().getStrictFlexArraysLevel();
 
   if (RD->isImplicit())
 return nullptr;
 
   for (const FieldDecl *FD : RD->fields()) {
-if ((!FAMDecl || FD == FAMDecl) &&
-Decl::isFlexibleArrayMemberLike(
+if (Decl::isFlexibleArrayMemberLike(
 Ctx, FD, FD->getType(), StrictFlexArraysLevel,
-/*IgnoreTemplateOrMacroSubstitution=*/true)) {
-  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
-  Offset += Layout.getFieldOffset(FieldNo);
+/*IgnoreTemplateOrMacroSubstitution=*/true))
   return FD;
+
+if (auto RT = FD->getType()->getAs())
+  if (const FieldDecl *FD =
+  FindFlexibleArrayMemberField(CGF, Ctx, RT->getAsRecordDecl()))
+return FD;
+  }
+
+  return nullptr;
+}
+
+/// Calculate the offset of a struct field. It may be embedded inside multiple
+/// sub-structs.
+static bool GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD,
+   const FieldDecl *FD, int64_t &Offset) {
+  if (RD->isImplicit())
+return false;
+
+  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+  uint32_t FieldNo = 0;
+
+  for (const FieldDecl *Field : RD->fields()) {
+if (Field == FD) {
+  Offset += Layout.getFieldOffset(FieldNo);
+  return true;
 }
 
-QualType Ty = FD->getType();
-if (Ty->isRecordType()) {
-  if (const FieldDecl *Field = FindFlexibleArrayMemberFieldAndOffset(
-  Ctx, Ty->getAsRecordDecl(), FAMDecl, Offset)) {
-const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+if (auto RT = Field->getType()->getAs()) {
+  if (GetFieldOffset(Ctx, RT->getAsRecordDecl(), FD, Offset)) {
 Offset += Layout.getFieldOffset(FieldNo);
-return Field;
+return true;
   }
 }
 
 if (!RD->isUnion())
   ++FieldNo;
   }
 
-  return nullptr;
+  return false;
 }
 
-static unsigned CountCountedByAttrs(const RecordDecl *RD) {
-  unsigned Num = 0;
+llvm::Value *
+CodeGenFunction::emitCountedByMemberSize(const Expr *E, llvm::Value *EmittedE,
+ unsigned Type,
+ llvm::IntegerType *ResType) {
+  ASTContext &Ctx = getContext();
 
-  for (const FieldDecl *FD : RD->fields()) {
-if (FD->getType()->isCountAttributedType())
-  return ++Num;
+  // Note: If the whole struct is specificed in the __bdos (i.e. Visitor
+  // returns a DeclRefExpr). The calculation of the whole size of the structure
+  // with a flexible array member can be done in two ways:
+  //
+  // 1) sizeof(struct S) + count * sizeof(typeof(fam))
+  // 2) offsetof(struct S, fam) + count * sizeof(typeof(fam))
+  //
+  // The first will add additional padding after the end of the array
+  // allocation while the second method is more precise, but not quite expected
+  // from programmers. See
+  // https://lore.kernel.org/lkml/ZvV6X5

[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-13 Thread Yeoul Na via cfe-commits

https://github.com/rapidsna commented:

Thanks for addressing comments! LGTM. You may still need to wait on Clang code 
owners review though.

https://github.com/llvm/llvm-project/pull/122198
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-13 Thread Yeoul Na via cfe-commits


@@ -1060,238 +1061,331 @@ 
CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type,
   return ConstantInt::get(ResType, ObjectSize, /*isSigned=*/true);
 }
 
-const FieldDecl *CodeGenFunction::FindFlexibleArrayMemberFieldAndOffset(
-ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FAMDecl,
-uint64_t &Offset) {
+namespace {
+
+/// StructFieldAccess is a simple visitor class to grab the first MemberExpr
+/// from an Expr. It records any ArraySubscriptExpr we meet along the way.
+struct StructFieldAccess
+: public ConstStmtVisitor {
+  const ArraySubscriptExpr *ASE = nullptr;
+
+  const MemberExpr *VisitMemberExpr(const MemberExpr *E) { return E; }
+
+  const MemberExpr *VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
+ASE = E;
+return Visit(E->getBase());
+  }
+  const MemberExpr *VisitCastExpr(const CastExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitParenExpr(const ParenExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryAddrOf(const clang::UnaryOperator *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryDeref(const clang::UnaryOperator *E) {
+return Visit(E->getSubExpr());
+  }
+};
+
+} // end anonymous namespace
+
+/// Find a struct's flexible array member. It may be embedded inside multiple
+/// sub-structs, but must still be the last field.
+static const FieldDecl *FindFlexibleArrayMemberField(CodeGenFunction &CGF,
+ ASTContext &Ctx,
+ const RecordDecl *RD) {
   const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
-  getLangOpts().getStrictFlexArraysLevel();
-  uint32_t FieldNo = 0;
+  CGF.getLangOpts().getStrictFlexArraysLevel();
 
   if (RD->isImplicit())
 return nullptr;
 
   for (const FieldDecl *FD : RD->fields()) {
-if ((!FAMDecl || FD == FAMDecl) &&
-Decl::isFlexibleArrayMemberLike(
+if (Decl::isFlexibleArrayMemberLike(
 Ctx, FD, FD->getType(), StrictFlexArraysLevel,
-/*IgnoreTemplateOrMacroSubstitution=*/true)) {
-  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
-  Offset += Layout.getFieldOffset(FieldNo);
+/*IgnoreTemplateOrMacroSubstitution=*/true))
   return FD;
+
+if (auto RT = FD->getType()->getAs())
+  if (const FieldDecl *FD =
+  FindFlexibleArrayMemberField(CGF, Ctx, RT->getAsRecordDecl()))
+return FD;
+  }
+
+  return nullptr;
+}
+
+/// Calculate the offset of a struct field. It may be embedded inside multiple
+/// sub-structs.
+static bool GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD,
+   const FieldDecl *FD, int64_t &Offset) {
+  if (RD->isImplicit())
+return false;
+
+  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+  uint32_t FieldNo = 0;
+
+  for (const FieldDecl *Field : RD->fields()) {
+if (Field == FD) {
+  Offset += Layout.getFieldOffset(FieldNo);
+  return true;
 }
 
-QualType Ty = FD->getType();
-if (Ty->isRecordType()) {
-  if (const FieldDecl *Field = FindFlexibleArrayMemberFieldAndOffset(
-  Ctx, Ty->getAsRecordDecl(), FAMDecl, Offset)) {
-const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+if (auto RT = Field->getType()->getAs()) {
+  if (GetFieldOffset(Ctx, RT->getAsRecordDecl(), FD, Offset)) {
 Offset += Layout.getFieldOffset(FieldNo);
-return Field;
+return true;
   }
 }
 
 if (!RD->isUnion())
   ++FieldNo;
   }
 
-  return nullptr;
+  return false;
 }
 
-static unsigned CountCountedByAttrs(const RecordDecl *RD) {
-  unsigned Num = 0;
+llvm::Value *
+CodeGenFunction::emitCountedByMemberSize(const Expr *E, llvm::Value *EmittedE,
+ unsigned Type,
+ llvm::IntegerType *ResType) {
+  ASTContext &Ctx = getContext();
 
-  for (const FieldDecl *FD : RD->fields()) {
-if (FD->getType()->isCountAttributedType())
-  return ++Num;
+  // Note: If the whole struct is specificed in the __bdos (i.e. Visitor
+  // returns a DeclRefExpr). The calculation of the whole size of the structure
+  // with a flexible array member can be done in two ways:
+  //
+  // 1) sizeof(struct S) + count * sizeof(typeof(fam))
+  // 2) offsetof(struct S, fam) + count * sizeof(typeof(fam))
+  //
+  // The first will add additional padding after the end of the array
+  // allocation while the second method is more precise, but not quite expected
+  // from programmers. See
+  // https://lore.kernel.org/lkml/ZvV6X5FPBBW7CO1f@archlinux/ for a discussion
+  // of the topic.
+  //
+  // GCC isn't (currently) able to calculate __bdos on a pointer to the whole
+  // structure. Therefore, because of the above issue, we choose to match what
+  // GCC does for consistency's s

[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-13 Thread Yeoul Na via cfe-commits


@@ -1060,238 +1061,331 @@ 
CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type,
   return ConstantInt::get(ResType, ObjectSize, /*isSigned=*/true);
 }
 
-const FieldDecl *CodeGenFunction::FindFlexibleArrayMemberFieldAndOffset(
-ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FAMDecl,
-uint64_t &Offset) {
+namespace {
+
+/// StructFieldAccess is a simple visitor class to grab the first MemberExpr
+/// from an Expr. It records any ArraySubscriptExpr we meet along the way.
+struct StructFieldAccess

rapidsna wrote:

I see. I'm fine with leaving it this way since this is not a regression 
introduced by this. Could you please create an issue, though?

https://github.com/llvm/llvm-project/pull/122198
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-09 Thread Bill Wendling via cfe-commits


@@ -1060,238 +1061,331 @@ 
CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type,
   return ConstantInt::get(ResType, ObjectSize, /*isSigned=*/true);
 }
 
-const FieldDecl *CodeGenFunction::FindFlexibleArrayMemberFieldAndOffset(
-ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FAMDecl,
-uint64_t &Offset) {
+namespace {
+
+/// StructFieldAccess is a simple visitor class to grab the first MemberExpr
+/// from an Expr. It records any ArraySubscriptExpr we meet along the way.
+struct StructFieldAccess
+: public ConstStmtVisitor {
+  const ArraySubscriptExpr *ASE = nullptr;
+
+  const MemberExpr *VisitMemberExpr(const MemberExpr *E) { return E; }
+
+  const MemberExpr *VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
+ASE = E;
+return Visit(E->getBase());
+  }
+  const MemberExpr *VisitCastExpr(const CastExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitParenExpr(const ParenExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryAddrOf(const clang::UnaryOperator *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryDeref(const clang::UnaryOperator *E) {
+return Visit(E->getSubExpr());
+  }
+};
+
+} // end anonymous namespace
+
+/// Find a struct's flexible array member. It may be embedded inside multiple
+/// sub-structs, but must still be the last field.
+static const FieldDecl *FindFlexibleArrayMemberField(CodeGenFunction &CGF,
+ ASTContext &Ctx,
+ const RecordDecl *RD) {
   const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
-  getLangOpts().getStrictFlexArraysLevel();
-  uint32_t FieldNo = 0;
+  CGF.getLangOpts().getStrictFlexArraysLevel();
 
   if (RD->isImplicit())
 return nullptr;
 
   for (const FieldDecl *FD : RD->fields()) {
-if ((!FAMDecl || FD == FAMDecl) &&
-Decl::isFlexibleArrayMemberLike(
+if (Decl::isFlexibleArrayMemberLike(
 Ctx, FD, FD->getType(), StrictFlexArraysLevel,
-/*IgnoreTemplateOrMacroSubstitution=*/true)) {
-  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
-  Offset += Layout.getFieldOffset(FieldNo);
+/*IgnoreTemplateOrMacroSubstitution=*/true))
   return FD;
+
+if (auto RT = FD->getType()->getAs())
+  if (const FieldDecl *FD =
+  FindFlexibleArrayMemberField(CGF, Ctx, RT->getAsRecordDecl()))
+return FD;
+  }
+
+  return nullptr;
+}
+
+/// Calculate the offset of a struct field. It may be embedded inside multiple
+/// sub-structs.
+static bool GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD,
+   const FieldDecl *FD, int64_t &Offset) {
+  if (RD->isImplicit())
+return false;
+
+  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+  uint32_t FieldNo = 0;
+
+  for (const FieldDecl *Field : RD->fields()) {
+if (Field == FD) {
+  Offset += Layout.getFieldOffset(FieldNo);
+  return true;
 }
 
-QualType Ty = FD->getType();
-if (Ty->isRecordType()) {
-  if (const FieldDecl *Field = FindFlexibleArrayMemberFieldAndOffset(
-  Ctx, Ty->getAsRecordDecl(), FAMDecl, Offset)) {
-const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+if (auto RT = Field->getType()->getAs()) {
+  if (GetFieldOffset(Ctx, RT->getAsRecordDecl(), FD, Offset)) {
 Offset += Layout.getFieldOffset(FieldNo);
-return Field;
+return true;
   }
 }
 
 if (!RD->isUnion())
   ++FieldNo;
   }
 
-  return nullptr;
+  return false;
 }
 
-static unsigned CountCountedByAttrs(const RecordDecl *RD) {
-  unsigned Num = 0;
+llvm::Value *
+CodeGenFunction::emitCountedByMemberSize(const Expr *E, llvm::Value *EmittedE,
+ unsigned Type,
+ llvm::IntegerType *ResType) {
+  ASTContext &Ctx = getContext();
 
-  for (const FieldDecl *FD : RD->fields()) {
-if (FD->getType()->isCountAttributedType())
-  return ++Num;
+  // Note: If the whole struct is specificed in the __bdos (i.e. Visitor
+  // returns a DeclRefExpr). The calculation of the whole size of the structure
+  // with a flexible array member can be done in two ways:
+  //
+  // 1) sizeof(struct S) + count * sizeof(typeof(fam))
+  // 2) offsetof(struct S, fam) + count * sizeof(typeof(fam))
+  //
+  // The first will add additional padding after the end of the array
+  // allocation while the second method is more precise, but not quite expected
+  // from programmers. See
+  // https://lore.kernel.org/lkml/ZvV6X5FPBBW7CO1f@archlinux/ for a discussion
+  // of the topic.
+  //
+  // GCC isn't (currently) able to calculate __bdos on a pointer to the whole
+  // structure. Therefore, because of the above issue, we choose to match what
+  // GCC does for consistency's s

[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-09 Thread Bill Wendling via cfe-commits


@@ -1060,238 +1061,331 @@ 
CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type,
   return ConstantInt::get(ResType, ObjectSize, /*isSigned=*/true);
 }
 
-const FieldDecl *CodeGenFunction::FindFlexibleArrayMemberFieldAndOffset(
-ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FAMDecl,
-uint64_t &Offset) {
+namespace {
+
+/// StructFieldAccess is a simple visitor class to grab the first MemberExpr
+/// from an Expr. It records any ArraySubscriptExpr we meet along the way.
+struct StructFieldAccess
+: public ConstStmtVisitor {
+  const ArraySubscriptExpr *ASE = nullptr;
+
+  const MemberExpr *VisitMemberExpr(const MemberExpr *E) { return E; }
+
+  const MemberExpr *VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
+ASE = E;
+return Visit(E->getBase());
+  }
+  const MemberExpr *VisitCastExpr(const CastExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitParenExpr(const ParenExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryAddrOf(const clang::UnaryOperator *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryDeref(const clang::UnaryOperator *E) {
+return Visit(E->getSubExpr());
+  }
+};
+
+} // end anonymous namespace
+
+/// Find a struct's flexible array member. It may be embedded inside multiple
+/// sub-structs, but must still be the last field.
+static const FieldDecl *FindFlexibleArrayMemberField(CodeGenFunction &CGF,
+ ASTContext &Ctx,
+ const RecordDecl *RD) {
   const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
-  getLangOpts().getStrictFlexArraysLevel();
-  uint32_t FieldNo = 0;
+  CGF.getLangOpts().getStrictFlexArraysLevel();
 
   if (RD->isImplicit())
 return nullptr;
 
   for (const FieldDecl *FD : RD->fields()) {
-if ((!FAMDecl || FD == FAMDecl) &&
-Decl::isFlexibleArrayMemberLike(
+if (Decl::isFlexibleArrayMemberLike(
 Ctx, FD, FD->getType(), StrictFlexArraysLevel,
-/*IgnoreTemplateOrMacroSubstitution=*/true)) {
-  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
-  Offset += Layout.getFieldOffset(FieldNo);
+/*IgnoreTemplateOrMacroSubstitution=*/true))
   return FD;
+
+if (auto RT = FD->getType()->getAs())
+  if (const FieldDecl *FD =
+  FindFlexibleArrayMemberField(CGF, Ctx, RT->getAsRecordDecl()))
+return FD;
+  }
+
+  return nullptr;
+}
+
+/// Calculate the offset of a struct field. It may be embedded inside multiple
+/// sub-structs.
+static bool GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD,
+   const FieldDecl *FD, int64_t &Offset) {
+  if (RD->isImplicit())
+return false;
+
+  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+  uint32_t FieldNo = 0;
+
+  for (const FieldDecl *Field : RD->fields()) {
+if (Field == FD) {
+  Offset += Layout.getFieldOffset(FieldNo);
+  return true;
 }
 
-QualType Ty = FD->getType();
-if (Ty->isRecordType()) {
-  if (const FieldDecl *Field = FindFlexibleArrayMemberFieldAndOffset(
-  Ctx, Ty->getAsRecordDecl(), FAMDecl, Offset)) {
-const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+if (auto RT = Field->getType()->getAs()) {
+  if (GetFieldOffset(Ctx, RT->getAsRecordDecl(), FD, Offset)) {
 Offset += Layout.getFieldOffset(FieldNo);
-return Field;
+return true;
   }
 }
 
 if (!RD->isUnion())
   ++FieldNo;
   }
 
-  return nullptr;
+  return false;
 }
 
-static unsigned CountCountedByAttrs(const RecordDecl *RD) {
-  unsigned Num = 0;
+llvm::Value *
+CodeGenFunction::emitCountedByMemberSize(const Expr *E, llvm::Value *EmittedE,
+ unsigned Type,
+ llvm::IntegerType *ResType) {
+  ASTContext &Ctx = getContext();
 
-  for (const FieldDecl *FD : RD->fields()) {
-if (FD->getType()->isCountAttributedType())
-  return ++Num;
+  // Note: If the whole struct is specificed in the __bdos (i.e. Visitor
+  // returns a DeclRefExpr). The calculation of the whole size of the structure
+  // with a flexible array member can be done in two ways:
+  //
+  // 1) sizeof(struct S) + count * sizeof(typeof(fam))
+  // 2) offsetof(struct S, fam) + count * sizeof(typeof(fam))
+  //
+  // The first will add additional padding after the end of the array
+  // allocation while the second method is more precise, but not quite expected
+  // from programmers. See
+  // https://lore.kernel.org/lkml/ZvV6X5FPBBW7CO1f@archlinux/ for a discussion
+  // of the topic.
+  //
+  // GCC isn't (currently) able to calculate __bdos on a pointer to the whole
+  // structure. Therefore, because of the above issue, we choose to match what
+  // GCC does for consistency's s

[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-09 Thread Bill Wendling via cfe-commits


@@ -1060,238 +1061,331 @@ 
CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type,
   return ConstantInt::get(ResType, ObjectSize, /*isSigned=*/true);
 }
 
-const FieldDecl *CodeGenFunction::FindFlexibleArrayMemberFieldAndOffset(
-ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FAMDecl,
-uint64_t &Offset) {
+namespace {
+
+/// StructFieldAccess is a simple visitor class to grab the first MemberExpr
+/// from an Expr. It records any ArraySubscriptExpr we meet along the way.
+struct StructFieldAccess
+: public ConstStmtVisitor {
+  const ArraySubscriptExpr *ASE = nullptr;
+
+  const MemberExpr *VisitMemberExpr(const MemberExpr *E) { return E; }
+
+  const MemberExpr *VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
+ASE = E;
+return Visit(E->getBase());
+  }
+  const MemberExpr *VisitCastExpr(const CastExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitParenExpr(const ParenExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryAddrOf(const clang::UnaryOperator *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryDeref(const clang::UnaryOperator *E) {
+return Visit(E->getSubExpr());
+  }
+};
+
+} // end anonymous namespace
+
+/// Find a struct's flexible array member. It may be embedded inside multiple
+/// sub-structs, but must still be the last field.
+static const FieldDecl *FindFlexibleArrayMemberField(CodeGenFunction &CGF,
+ ASTContext &Ctx,
+ const RecordDecl *RD) {
   const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
-  getLangOpts().getStrictFlexArraysLevel();
-  uint32_t FieldNo = 0;
+  CGF.getLangOpts().getStrictFlexArraysLevel();
 
   if (RD->isImplicit())
 return nullptr;
 
   for (const FieldDecl *FD : RD->fields()) {
-if ((!FAMDecl || FD == FAMDecl) &&
-Decl::isFlexibleArrayMemberLike(
+if (Decl::isFlexibleArrayMemberLike(
 Ctx, FD, FD->getType(), StrictFlexArraysLevel,
-/*IgnoreTemplateOrMacroSubstitution=*/true)) {
-  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
-  Offset += Layout.getFieldOffset(FieldNo);
+/*IgnoreTemplateOrMacroSubstitution=*/true))
   return FD;
+
+if (auto RT = FD->getType()->getAs())
+  if (const FieldDecl *FD =
+  FindFlexibleArrayMemberField(CGF, Ctx, RT->getAsRecordDecl()))
+return FD;
+  }
+
+  return nullptr;
+}
+
+/// Calculate the offset of a struct field. It may be embedded inside multiple
+/// sub-structs.
+static bool GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD,
+   const FieldDecl *FD, int64_t &Offset) {
+  if (RD->isImplicit())
+return false;
+
+  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+  uint32_t FieldNo = 0;
+
+  for (const FieldDecl *Field : RD->fields()) {
+if (Field == FD) {
+  Offset += Layout.getFieldOffset(FieldNo);
+  return true;
 }
 
-QualType Ty = FD->getType();
-if (Ty->isRecordType()) {
-  if (const FieldDecl *Field = FindFlexibleArrayMemberFieldAndOffset(
-  Ctx, Ty->getAsRecordDecl(), FAMDecl, Offset)) {
-const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+if (auto RT = Field->getType()->getAs()) {
+  if (GetFieldOffset(Ctx, RT->getAsRecordDecl(), FD, Offset)) {
 Offset += Layout.getFieldOffset(FieldNo);
-return Field;
+return true;
   }
 }
 
 if (!RD->isUnion())
   ++FieldNo;
   }
 
-  return nullptr;
+  return false;
 }
 
-static unsigned CountCountedByAttrs(const RecordDecl *RD) {
-  unsigned Num = 0;
+llvm::Value *
+CodeGenFunction::emitCountedByMemberSize(const Expr *E, llvm::Value *EmittedE,
+ unsigned Type,
+ llvm::IntegerType *ResType) {
+  ASTContext &Ctx = getContext();
 
-  for (const FieldDecl *FD : RD->fields()) {
-if (FD->getType()->isCountAttributedType())
-  return ++Num;
+  // Note: If the whole struct is specificed in the __bdos (i.e. Visitor
+  // returns a DeclRefExpr). The calculation of the whole size of the structure
+  // with a flexible array member can be done in two ways:
+  //
+  // 1) sizeof(struct S) + count * sizeof(typeof(fam))
+  // 2) offsetof(struct S, fam) + count * sizeof(typeof(fam))
+  //
+  // The first will add additional padding after the end of the array
+  // allocation while the second method is more precise, but not quite expected
+  // from programmers. See
+  // https://lore.kernel.org/lkml/ZvV6X5FPBBW7CO1f@archlinux/ for a discussion
+  // of the topic.
+  //
+  // GCC isn't (currently) able to calculate __bdos on a pointer to the whole
+  // structure. Therefore, because of the above issue, we choose to match what
+  // GCC does for consistency's s

[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-09 Thread Bill Wendling via cfe-commits


@@ -1060,238 +1061,331 @@ 
CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type,
   return ConstantInt::get(ResType, ObjectSize, /*isSigned=*/true);
 }
 
-const FieldDecl *CodeGenFunction::FindFlexibleArrayMemberFieldAndOffset(
-ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FAMDecl,
-uint64_t &Offset) {
+namespace {
+
+/// StructFieldAccess is a simple visitor class to grab the first MemberExpr
+/// from an Expr. It records any ArraySubscriptExpr we meet along the way.
+struct StructFieldAccess
+: public ConstStmtVisitor {
+  const ArraySubscriptExpr *ASE = nullptr;
+
+  const MemberExpr *VisitMemberExpr(const MemberExpr *E) { return E; }
+
+  const MemberExpr *VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
+ASE = E;
+return Visit(E->getBase());
+  }
+  const MemberExpr *VisitCastExpr(const CastExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitParenExpr(const ParenExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryAddrOf(const clang::UnaryOperator *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryDeref(const clang::UnaryOperator *E) {
+return Visit(E->getSubExpr());
+  }
+};
+
+} // end anonymous namespace
+
+/// Find a struct's flexible array member. It may be embedded inside multiple
+/// sub-structs, but must still be the last field.
+static const FieldDecl *FindFlexibleArrayMemberField(CodeGenFunction &CGF,
+ ASTContext &Ctx,
+ const RecordDecl *RD) {
   const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
-  getLangOpts().getStrictFlexArraysLevel();
-  uint32_t FieldNo = 0;
+  CGF.getLangOpts().getStrictFlexArraysLevel();
 
   if (RD->isImplicit())
 return nullptr;
 
   for (const FieldDecl *FD : RD->fields()) {
-if ((!FAMDecl || FD == FAMDecl) &&
-Decl::isFlexibleArrayMemberLike(
+if (Decl::isFlexibleArrayMemberLike(
 Ctx, FD, FD->getType(), StrictFlexArraysLevel,
-/*IgnoreTemplateOrMacroSubstitution=*/true)) {
-  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
-  Offset += Layout.getFieldOffset(FieldNo);
+/*IgnoreTemplateOrMacroSubstitution=*/true))
   return FD;
+
+if (auto RT = FD->getType()->getAs())
+  if (const FieldDecl *FD =
+  FindFlexibleArrayMemberField(CGF, Ctx, RT->getAsRecordDecl()))
+return FD;
+  }
+
+  return nullptr;
+}
+
+/// Calculate the offset of a struct field. It may be embedded inside multiple
+/// sub-structs.
+static bool GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD,
+   const FieldDecl *FD, int64_t &Offset) {
+  if (RD->isImplicit())
+return false;
+
+  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+  uint32_t FieldNo = 0;
+
+  for (const FieldDecl *Field : RD->fields()) {
+if (Field == FD) {
+  Offset += Layout.getFieldOffset(FieldNo);
+  return true;
 }
 
-QualType Ty = FD->getType();
-if (Ty->isRecordType()) {
-  if (const FieldDecl *Field = FindFlexibleArrayMemberFieldAndOffset(
-  Ctx, Ty->getAsRecordDecl(), FAMDecl, Offset)) {
-const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+if (auto RT = Field->getType()->getAs()) {
+  if (GetFieldOffset(Ctx, RT->getAsRecordDecl(), FD, Offset)) {
 Offset += Layout.getFieldOffset(FieldNo);
-return Field;
+return true;
   }
 }
 
 if (!RD->isUnion())
   ++FieldNo;
   }
 
-  return nullptr;
+  return false;
 }
 
-static unsigned CountCountedByAttrs(const RecordDecl *RD) {
-  unsigned Num = 0;
+llvm::Value *
+CodeGenFunction::emitCountedByMemberSize(const Expr *E, llvm::Value *EmittedE,
+ unsigned Type,
+ llvm::IntegerType *ResType) {
+  ASTContext &Ctx = getContext();
 
-  for (const FieldDecl *FD : RD->fields()) {
-if (FD->getType()->isCountAttributedType())
-  return ++Num;
+  // Note: If the whole struct is specificed in the __bdos (i.e. Visitor
+  // returns a DeclRefExpr). The calculation of the whole size of the structure
+  // with a flexible array member can be done in two ways:
+  //
+  // 1) sizeof(struct S) + count * sizeof(typeof(fam))
+  // 2) offsetof(struct S, fam) + count * sizeof(typeof(fam))
+  //
+  // The first will add additional padding after the end of the array
+  // allocation while the second method is more precise, but not quite expected
+  // from programmers. See
+  // https://lore.kernel.org/lkml/ZvV6X5FPBBW7CO1f@archlinux/ for a discussion
+  // of the topic.
+  //
+  // GCC isn't (currently) able to calculate __bdos on a pointer to the whole
+  // structure. Therefore, because of the above issue, we choose to match what
+  // GCC does for consistency's s

[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-09 Thread Bill Wendling via cfe-commits


@@ -1343,6 +1430,12 @@ CodeGenFunction::emitBuiltinObjectSize(const Expr *E, 
unsigned Type,
   assert(Ptr->getType()->isPointerTy() &&
  "Non-pointer passed to __builtin_object_size?");
 
+  if (IsDynamic)

bwendling wrote:

At this point, we know that `EmittedE` has a value. See line 1429.

https://github.com/llvm/llvm-project/pull/122198
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-09 Thread Bill Wendling via cfe-commits


@@ -1060,238 +1061,331 @@ 
CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type,
   return ConstantInt::get(ResType, ObjectSize, /*isSigned=*/true);
 }
 
-const FieldDecl *CodeGenFunction::FindFlexibleArrayMemberFieldAndOffset(
-ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FAMDecl,
-uint64_t &Offset) {
+namespace {
+
+/// StructFieldAccess is a simple visitor class to grab the first MemberExpr
+/// from an Expr. It records any ArraySubscriptExpr we meet along the way.
+struct StructFieldAccess

bwendling wrote:

Both GCC and Clang return the same value for `__bdos(*ptr->array)` with my new 
change. For `__bdos(&ptr->array)` GCC returns -1 and Clang returns the 
calculated value (it does that even without this patch). So it's behavior that 
existed before the advent of `__counted_by` and is probably wrong, but maybe 
not? People have different expectations for `__bdos` in various situations it 
seems...

I'm tempted to leave it as is since it isn't a regression, unless you have 
strong feelings about it.

https://github.com/llvm/llvm-project/pull/122198
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-08 Thread Yeoul Na via cfe-commits


@@ -1060,238 +1061,331 @@ 
CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type,
   return ConstantInt::get(ResType, ObjectSize, /*isSigned=*/true);
 }
 
-const FieldDecl *CodeGenFunction::FindFlexibleArrayMemberFieldAndOffset(
-ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FAMDecl,
-uint64_t &Offset) {
+namespace {
+
+/// StructFieldAccess is a simple visitor class to grab the first MemberExpr
+/// from an Expr. It records any ArraySubscriptExpr we meet along the way.
+struct StructFieldAccess

rapidsna wrote:

I suspect implementing this way will have `__bdos(&ptr->array)` and 
`__bdos(*ptr->array)` treated as Option (1). Was it intended?

https://github.com/llvm/llvm-project/pull/122198
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-08 Thread Yeoul Na via cfe-commits


@@ -1060,238 +1061,331 @@ 
CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type,
   return ConstantInt::get(ResType, ObjectSize, /*isSigned=*/true);
 }
 
-const FieldDecl *CodeGenFunction::FindFlexibleArrayMemberFieldAndOffset(
-ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FAMDecl,
-uint64_t &Offset) {
+namespace {
+
+/// StructFieldAccess is a simple visitor class to grab the first MemberExpr
+/// from an Expr. It records any ArraySubscriptExpr we meet along the way.
+struct StructFieldAccess
+: public ConstStmtVisitor {
+  const ArraySubscriptExpr *ASE = nullptr;
+
+  const MemberExpr *VisitMemberExpr(const MemberExpr *E) { return E; }
+
+  const MemberExpr *VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
+ASE = E;
+return Visit(E->getBase());
+  }
+  const MemberExpr *VisitCastExpr(const CastExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitParenExpr(const ParenExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryAddrOf(const clang::UnaryOperator *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryDeref(const clang::UnaryOperator *E) {
+return Visit(E->getSubExpr());
+  }
+};
+
+} // end anonymous namespace
+
+/// Find a struct's flexible array member. It may be embedded inside multiple
+/// sub-structs, but must still be the last field.
+static const FieldDecl *FindFlexibleArrayMemberField(CodeGenFunction &CGF,
+ ASTContext &Ctx,
+ const RecordDecl *RD) {
   const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
-  getLangOpts().getStrictFlexArraysLevel();
-  uint32_t FieldNo = 0;
+  CGF.getLangOpts().getStrictFlexArraysLevel();
 
   if (RD->isImplicit())
 return nullptr;
 
   for (const FieldDecl *FD : RD->fields()) {
-if ((!FAMDecl || FD == FAMDecl) &&
-Decl::isFlexibleArrayMemberLike(
+if (Decl::isFlexibleArrayMemberLike(
 Ctx, FD, FD->getType(), StrictFlexArraysLevel,
-/*IgnoreTemplateOrMacroSubstitution=*/true)) {
-  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
-  Offset += Layout.getFieldOffset(FieldNo);
+/*IgnoreTemplateOrMacroSubstitution=*/true))
   return FD;
+
+if (auto RT = FD->getType()->getAs())
+  if (const FieldDecl *FD =
+  FindFlexibleArrayMemberField(CGF, Ctx, RT->getAsRecordDecl()))
+return FD;
+  }
+
+  return nullptr;
+}
+
+/// Calculate the offset of a struct field. It may be embedded inside multiple
+/// sub-structs.
+static bool GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD,
+   const FieldDecl *FD, int64_t &Offset) {
+  if (RD->isImplicit())
+return false;
+
+  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+  uint32_t FieldNo = 0;
+
+  for (const FieldDecl *Field : RD->fields()) {
+if (Field == FD) {
+  Offset += Layout.getFieldOffset(FieldNo);
+  return true;
 }
 
-QualType Ty = FD->getType();
-if (Ty->isRecordType()) {
-  if (const FieldDecl *Field = FindFlexibleArrayMemberFieldAndOffset(
-  Ctx, Ty->getAsRecordDecl(), FAMDecl, Offset)) {
-const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+if (auto RT = Field->getType()->getAs()) {
+  if (GetFieldOffset(Ctx, RT->getAsRecordDecl(), FD, Offset)) {
 Offset += Layout.getFieldOffset(FieldNo);
-return Field;
+return true;
   }
 }
 
 if (!RD->isUnion())
   ++FieldNo;
   }
 
-  return nullptr;
+  return false;
 }
 
-static unsigned CountCountedByAttrs(const RecordDecl *RD) {
-  unsigned Num = 0;
+llvm::Value *
+CodeGenFunction::emitCountedByMemberSize(const Expr *E, llvm::Value *EmittedE,
+ unsigned Type,
+ llvm::IntegerType *ResType) {
+  ASTContext &Ctx = getContext();
 
-  for (const FieldDecl *FD : RD->fields()) {
-if (FD->getType()->isCountAttributedType())
-  return ++Num;
+  // Note: If the whole struct is specificed in the __bdos (i.e. Visitor
+  // returns a DeclRefExpr). The calculation of the whole size of the structure
+  // with a flexible array member can be done in two ways:
+  //
+  // 1) sizeof(struct S) + count * sizeof(typeof(fam))
+  // 2) offsetof(struct S, fam) + count * sizeof(typeof(fam))
+  //
+  // The first will add additional padding after the end of the array
+  // allocation while the second method is more precise, but not quite expected
+  // from programmers. See
+  // https://lore.kernel.org/lkml/ZvV6X5FPBBW7CO1f@archlinux/ for a discussion
+  // of the topic.
+  //
+  // GCC isn't (currently) able to calculate __bdos on a pointer to the whole
+  // structure. Therefore, because of the above issue, we choose to match what
+  // GCC does for consistency's s

[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-08 Thread Yeoul Na via cfe-commits


@@ -1060,238 +1061,331 @@ 
CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type,
   return ConstantInt::get(ResType, ObjectSize, /*isSigned=*/true);
 }
 
-const FieldDecl *CodeGenFunction::FindFlexibleArrayMemberFieldAndOffset(
-ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FAMDecl,
-uint64_t &Offset) {
+namespace {
+
+/// StructFieldAccess is a simple visitor class to grab the first MemberExpr
+/// from an Expr. It records any ArraySubscriptExpr we meet along the way.
+struct StructFieldAccess
+: public ConstStmtVisitor {
+  const ArraySubscriptExpr *ASE = nullptr;
+
+  const MemberExpr *VisitMemberExpr(const MemberExpr *E) { return E; }
+
+  const MemberExpr *VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
+ASE = E;
+return Visit(E->getBase());
+  }
+  const MemberExpr *VisitCastExpr(const CastExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitParenExpr(const ParenExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryAddrOf(const clang::UnaryOperator *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryDeref(const clang::UnaryOperator *E) {
+return Visit(E->getSubExpr());
+  }
+};
+
+} // end anonymous namespace
+
+/// Find a struct's flexible array member. It may be embedded inside multiple
+/// sub-structs, but must still be the last field.
+static const FieldDecl *FindFlexibleArrayMemberField(CodeGenFunction &CGF,
+ ASTContext &Ctx,
+ const RecordDecl *RD) {
   const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
-  getLangOpts().getStrictFlexArraysLevel();
-  uint32_t FieldNo = 0;
+  CGF.getLangOpts().getStrictFlexArraysLevel();
 
   if (RD->isImplicit())
 return nullptr;
 
   for (const FieldDecl *FD : RD->fields()) {
-if ((!FAMDecl || FD == FAMDecl) &&
-Decl::isFlexibleArrayMemberLike(
+if (Decl::isFlexibleArrayMemberLike(
 Ctx, FD, FD->getType(), StrictFlexArraysLevel,
-/*IgnoreTemplateOrMacroSubstitution=*/true)) {
-  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
-  Offset += Layout.getFieldOffset(FieldNo);
+/*IgnoreTemplateOrMacroSubstitution=*/true))
   return FD;
+
+if (auto RT = FD->getType()->getAs())
+  if (const FieldDecl *FD =
+  FindFlexibleArrayMemberField(CGF, Ctx, RT->getAsRecordDecl()))
+return FD;
+  }
+
+  return nullptr;
+}
+
+/// Calculate the offset of a struct field. It may be embedded inside multiple
+/// sub-structs.
+static bool GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD,
+   const FieldDecl *FD, int64_t &Offset) {
+  if (RD->isImplicit())
+return false;
+
+  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+  uint32_t FieldNo = 0;
+
+  for (const FieldDecl *Field : RD->fields()) {
+if (Field == FD) {
+  Offset += Layout.getFieldOffset(FieldNo);
+  return true;
 }
 
-QualType Ty = FD->getType();
-if (Ty->isRecordType()) {
-  if (const FieldDecl *Field = FindFlexibleArrayMemberFieldAndOffset(
-  Ctx, Ty->getAsRecordDecl(), FAMDecl, Offset)) {
-const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+if (auto RT = Field->getType()->getAs()) {
+  if (GetFieldOffset(Ctx, RT->getAsRecordDecl(), FD, Offset)) {
 Offset += Layout.getFieldOffset(FieldNo);
-return Field;
+return true;
   }
 }
 
 if (!RD->isUnion())
   ++FieldNo;
   }
 
-  return nullptr;
+  return false;
 }
 
-static unsigned CountCountedByAttrs(const RecordDecl *RD) {
-  unsigned Num = 0;
+llvm::Value *
+CodeGenFunction::emitCountedByMemberSize(const Expr *E, llvm::Value *EmittedE,
+ unsigned Type,
+ llvm::IntegerType *ResType) {
+  ASTContext &Ctx = getContext();
 
-  for (const FieldDecl *FD : RD->fields()) {
-if (FD->getType()->isCountAttributedType())
-  return ++Num;
+  // Note: If the whole struct is specificed in the __bdos (i.e. Visitor
+  // returns a DeclRefExpr). The calculation of the whole size of the structure
+  // with a flexible array member can be done in two ways:
+  //
+  // 1) sizeof(struct S) + count * sizeof(typeof(fam))
+  // 2) offsetof(struct S, fam) + count * sizeof(typeof(fam))
+  //
+  // The first will add additional padding after the end of the array
+  // allocation while the second method is more precise, but not quite expected
+  // from programmers. See
+  // https://lore.kernel.org/lkml/ZvV6X5FPBBW7CO1f@archlinux/ for a discussion
+  // of the topic.
+  //
+  // GCC isn't (currently) able to calculate __bdos on a pointer to the whole
+  // structure. Therefore, because of the above issue, we choose to match what
+  // GCC does for consistency's s

[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-08 Thread Yeoul Na via cfe-commits


@@ -1343,6 +1430,12 @@ CodeGenFunction::emitBuiltinObjectSize(const Expr *E, 
unsigned Type,
   assert(Ptr->getType()->isPointerTy() &&
  "Non-pointer passed to __builtin_object_size?");
 
+  if (IsDynamic)

rapidsna wrote:

Why did this code block move to here from up there?

https://github.com/llvm/llvm-project/pull/122198
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-08 Thread Yeoul Na via cfe-commits


@@ -1060,238 +1061,331 @@ 
CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type,
   return ConstantInt::get(ResType, ObjectSize, /*isSigned=*/true);
 }
 
-const FieldDecl *CodeGenFunction::FindFlexibleArrayMemberFieldAndOffset(
-ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FAMDecl,
-uint64_t &Offset) {
+namespace {
+
+/// StructFieldAccess is a simple visitor class to grab the first MemberExpr
+/// from an Expr. It records any ArraySubscriptExpr we meet along the way.
+struct StructFieldAccess
+: public ConstStmtVisitor {
+  const ArraySubscriptExpr *ASE = nullptr;
+
+  const MemberExpr *VisitMemberExpr(const MemberExpr *E) { return E; }
+
+  const MemberExpr *VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
+ASE = E;
+return Visit(E->getBase());
+  }
+  const MemberExpr *VisitCastExpr(const CastExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitParenExpr(const ParenExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryAddrOf(const clang::UnaryOperator *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryDeref(const clang::UnaryOperator *E) {
+return Visit(E->getSubExpr());
+  }
+};
+
+} // end anonymous namespace
+
+/// Find a struct's flexible array member. It may be embedded inside multiple
+/// sub-structs, but must still be the last field.
+static const FieldDecl *FindFlexibleArrayMemberField(CodeGenFunction &CGF,
+ ASTContext &Ctx,
+ const RecordDecl *RD) {
   const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
-  getLangOpts().getStrictFlexArraysLevel();
-  uint32_t FieldNo = 0;
+  CGF.getLangOpts().getStrictFlexArraysLevel();
 
   if (RD->isImplicit())
 return nullptr;
 
   for (const FieldDecl *FD : RD->fields()) {
-if ((!FAMDecl || FD == FAMDecl) &&
-Decl::isFlexibleArrayMemberLike(
+if (Decl::isFlexibleArrayMemberLike(
 Ctx, FD, FD->getType(), StrictFlexArraysLevel,
-/*IgnoreTemplateOrMacroSubstitution=*/true)) {
-  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
-  Offset += Layout.getFieldOffset(FieldNo);
+/*IgnoreTemplateOrMacroSubstitution=*/true))
   return FD;
+
+if (auto RT = FD->getType()->getAs())
+  if (const FieldDecl *FD =
+  FindFlexibleArrayMemberField(CGF, Ctx, RT->getAsRecordDecl()))
+return FD;
+  }
+
+  return nullptr;
+}
+
+/// Calculate the offset of a struct field. It may be embedded inside multiple
+/// sub-structs.
+static bool GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD,
+   const FieldDecl *FD, int64_t &Offset) {
+  if (RD->isImplicit())
+return false;
+
+  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+  uint32_t FieldNo = 0;
+
+  for (const FieldDecl *Field : RD->fields()) {
+if (Field == FD) {
+  Offset += Layout.getFieldOffset(FieldNo);
+  return true;
 }
 
-QualType Ty = FD->getType();
-if (Ty->isRecordType()) {
-  if (const FieldDecl *Field = FindFlexibleArrayMemberFieldAndOffset(
-  Ctx, Ty->getAsRecordDecl(), FAMDecl, Offset)) {
-const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+if (auto RT = Field->getType()->getAs()) {
+  if (GetFieldOffset(Ctx, RT->getAsRecordDecl(), FD, Offset)) {
 Offset += Layout.getFieldOffset(FieldNo);
-return Field;
+return true;
   }
 }
 
 if (!RD->isUnion())
   ++FieldNo;
   }
 
-  return nullptr;
+  return false;
 }
 
-static unsigned CountCountedByAttrs(const RecordDecl *RD) {
-  unsigned Num = 0;
+llvm::Value *
+CodeGenFunction::emitCountedByMemberSize(const Expr *E, llvm::Value *EmittedE,
+ unsigned Type,
+ llvm::IntegerType *ResType) {
+  ASTContext &Ctx = getContext();
 
-  for (const FieldDecl *FD : RD->fields()) {
-if (FD->getType()->isCountAttributedType())
-  return ++Num;
+  // Note: If the whole struct is specificed in the __bdos (i.e. Visitor
+  // returns a DeclRefExpr). The calculation of the whole size of the structure
+  // with a flexible array member can be done in two ways:
+  //
+  // 1) sizeof(struct S) + count * sizeof(typeof(fam))
+  // 2) offsetof(struct S, fam) + count * sizeof(typeof(fam))
+  //
+  // The first will add additional padding after the end of the array
+  // allocation while the second method is more precise, but not quite expected
+  // from programmers. See
+  // https://lore.kernel.org/lkml/ZvV6X5FPBBW7CO1f@archlinux/ for a discussion
+  // of the topic.
+  //
+  // GCC isn't (currently) able to calculate __bdos on a pointer to the whole
+  // structure. Therefore, because of the above issue, we choose to match what
+  // GCC does for consistency's s

[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-08 Thread Yeoul Na via cfe-commits


@@ -1060,238 +1061,331 @@ 
CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type,
   return ConstantInt::get(ResType, ObjectSize, /*isSigned=*/true);
 }
 
-const FieldDecl *CodeGenFunction::FindFlexibleArrayMemberFieldAndOffset(
-ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FAMDecl,
-uint64_t &Offset) {
+namespace {
+
+/// StructFieldAccess is a simple visitor class to grab the first MemberExpr
+/// from an Expr. It records any ArraySubscriptExpr we meet along the way.
+struct StructFieldAccess
+: public ConstStmtVisitor {
+  const ArraySubscriptExpr *ASE = nullptr;
+
+  const MemberExpr *VisitMemberExpr(const MemberExpr *E) { return E; }
+
+  const MemberExpr *VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
+ASE = E;
+return Visit(E->getBase());
+  }
+  const MemberExpr *VisitCastExpr(const CastExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitParenExpr(const ParenExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryAddrOf(const clang::UnaryOperator *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryDeref(const clang::UnaryOperator *E) {
+return Visit(E->getSubExpr());
+  }
+};
+
+} // end anonymous namespace
+
+/// Find a struct's flexible array member. It may be embedded inside multiple
+/// sub-structs, but must still be the last field.
+static const FieldDecl *FindFlexibleArrayMemberField(CodeGenFunction &CGF,
+ ASTContext &Ctx,
+ const RecordDecl *RD) {
   const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
-  getLangOpts().getStrictFlexArraysLevel();
-  uint32_t FieldNo = 0;
+  CGF.getLangOpts().getStrictFlexArraysLevel();
 
   if (RD->isImplicit())
 return nullptr;
 
   for (const FieldDecl *FD : RD->fields()) {
-if ((!FAMDecl || FD == FAMDecl) &&
-Decl::isFlexibleArrayMemberLike(
+if (Decl::isFlexibleArrayMemberLike(
 Ctx, FD, FD->getType(), StrictFlexArraysLevel,
-/*IgnoreTemplateOrMacroSubstitution=*/true)) {
-  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
-  Offset += Layout.getFieldOffset(FieldNo);
+/*IgnoreTemplateOrMacroSubstitution=*/true))
   return FD;
+
+if (auto RT = FD->getType()->getAs())
+  if (const FieldDecl *FD =
+  FindFlexibleArrayMemberField(CGF, Ctx, RT->getAsRecordDecl()))
+return FD;
+  }
+
+  return nullptr;
+}
+
+/// Calculate the offset of a struct field. It may be embedded inside multiple
+/// sub-structs.
+static bool GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD,
+   const FieldDecl *FD, int64_t &Offset) {
+  if (RD->isImplicit())
+return false;
+
+  const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+  uint32_t FieldNo = 0;
+
+  for (const FieldDecl *Field : RD->fields()) {
+if (Field == FD) {
+  Offset += Layout.getFieldOffset(FieldNo);
+  return true;
 }
 
-QualType Ty = FD->getType();
-if (Ty->isRecordType()) {
-  if (const FieldDecl *Field = FindFlexibleArrayMemberFieldAndOffset(
-  Ctx, Ty->getAsRecordDecl(), FAMDecl, Offset)) {
-const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(RD);
+if (auto RT = Field->getType()->getAs()) {
+  if (GetFieldOffset(Ctx, RT->getAsRecordDecl(), FD, Offset)) {
 Offset += Layout.getFieldOffset(FieldNo);
-return Field;
+return true;
   }
 }
 
 if (!RD->isUnion())
   ++FieldNo;
   }
 
-  return nullptr;
+  return false;
 }
 
-static unsigned CountCountedByAttrs(const RecordDecl *RD) {
-  unsigned Num = 0;
+llvm::Value *
+CodeGenFunction::emitCountedByMemberSize(const Expr *E, llvm::Value *EmittedE,
+ unsigned Type,
+ llvm::IntegerType *ResType) {
+  ASTContext &Ctx = getContext();
 
-  for (const FieldDecl *FD : RD->fields()) {
-if (FD->getType()->isCountAttributedType())
-  return ++Num;
+  // Note: If the whole struct is specificed in the __bdos (i.e. Visitor
+  // returns a DeclRefExpr). The calculation of the whole size of the structure
+  // with a flexible array member can be done in two ways:
+  //
+  // 1) sizeof(struct S) + count * sizeof(typeof(fam))
+  // 2) offsetof(struct S, fam) + count * sizeof(typeof(fam))
+  //
+  // The first will add additional padding after the end of the array
+  // allocation while the second method is more precise, but not quite expected
+  // from programmers. See
+  // https://lore.kernel.org/lkml/ZvV6X5FPBBW7CO1f@archlinux/ for a discussion
+  // of the topic.
+  //
+  // GCC isn't (currently) able to calculate __bdos on a pointer to the whole
+  // structure. Therefore, because of the above issue, we choose to match what
+  // GCC does for consistency's s

[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-08 Thread Bill Wendling via cfe-commits

bwendling wrote:

Howdy!

This is essentially a complete rewrite of 
00b6d032a22196bc14e4e30e413c040eb1b65da4. The original was overly complex and 
fragile. The main difference with this PR is basing the calculations off of the 
already emitted `Expr` instead of doing it within this function. It allows the 
Visitor to skip past pretty much everything to find the first `MemberExpr`. 
Note, we don't perform the calculation on the whole struct (i.e. `__bdos(ptr)`) 
due to issues with GCC compatibility, so the Visitor isn't concerned about any 
other `Expr` type but a `MemberExpr`.

The testcase changes are pretty large, but I visually reviewed the changes and 
ran the resulting code and it worked produced the expected results. (It even 
uncovered a bug! :-D)

Please take a look and let me know any issues you have. It's mostly an NFC 
patch, but it's so large and invasive that I can't really mark it as such.

https://github.com/llvm/llvm-project/pull/122198
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang][counted_by] Refactor __builtin_dynamic_object_size on FAMs (PR #122198)

2025-01-08 Thread via cfe-commits

llvmbot wrote:




@llvm/pr-subscribers-clang

Author: Bill Wendling (bwendling)


Changes

Refactoring of how __builtin_dynamic_object_size() is calculated for flexible 
array members (in preparation for adding support for the 'counted_by' attribute 
on pointers in structs).

The only functionality change is that we use the already emitted Expr code to 
build our calculations off of rather than re-emitting the Expr. That allows the 
'StructFieldAccess' visitor to sift through all casts and ArraySubscriptExprs 
to find the first MemberExpr. We build our GEPs and calculate offsets based off 
of relative distances from that MemberExpr.

The testcase passes execution tests.

There are four categories to support:

  struct p;
  struct s {
  /* ... */
  int count;
  struct p *array[] __attribute__((counted_by(count)));
  };

1) Object size of full flexible array member's size 'ptr->array':

   size_t count = (size_t) ptr->count;
   size_t flexible_array_member_base_size = sizeof (*ptr->array);
   size_t flexible_array_member_size =
   count * flexible_array_member_base_size;

   return flexible_array_member_size;

2) Object size from partial flexible array member '&ptr->array[idx]':

   size_t count = (size_t) ptr->count;
   size_t index = (size_t) idx;
   size_t flexible_array_member_base_size = sizeof (*ptr->array);
   size_t flexible_array_member_size =
   count * flexible_array_member_base_size;
   size_t index_size = index * flexible_array_member_base_size;

   return flexible_array_member_size - index_size;

3) Object size from '&ptr->field':

   size_t count = (size_t) ptr->count;
   size_t sizeof_struct = sizeof (struct s);
   size_t flexible_array_member_base_size = sizeof (*ptr->array);
   size_t flexible_array_member_size =
   count * flexible_array_member_base_size;
   size_t field_offset = offsetof (struct s, field);
   size_t offset_diff = sizeof_struct - field_offset;

   return flexible_array_member_size + offset_diff;

4) Object size from '&ptr->field[idx]':

   size_t count = (size_t) ptr->count;
   size_t index = (size_t) idx;
   size_t sizeof_struct = sizeof (struct s);
   size_t field_base_size = sizeof (*ptr->field);
   size_t flexible_array_member_base_size = sizeof (*ptr->array);
   size_t flexible_array_member_size =
   count * flexible_array_member_base_size;
   size_t field_offset =
   offsetof (struct s, field) + index * field_base_size;
   size_t offset_diff = sizeof_struct - field_offset;

   return offset_diff + flexible_array_member_size;

---

Patch is 127.44 KiB, truncated to 20.00 KiB below, full version: 
https://github.com/llvm/llvm-project/pull/122198.diff


3 Files Affected:

- (modified) clang/lib/CodeGen/CGBuiltin.cpp (+290-197) 
- (modified) clang/lib/CodeGen/CodeGenFunction.h (+5-13) 
- (modified) clang/test/CodeGen/attr-counted-by.c (+514-316) 


``diff
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index dcea32969fb990..61450bcb5f9af8 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -29,6 +29,7 @@
 #include "clang/AST/Expr.h"
 #include "clang/AST/OSLog.h"
 #include "clang/AST/OperationKinds.h"
+#include "clang/AST/StmtVisitor.h"
 #include "clang/AST/Type.h"
 #include "clang/Basic/TargetBuiltins.h"
 #include "clang/Basic/TargetInfo.h"
@@ -1060,33 +1061,82 @@ CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const 
Expr *E, unsigned Type,
   return ConstantInt::get(ResType, ObjectSize, /*isSigned=*/true);
 }
 
-const FieldDecl *CodeGenFunction::FindFlexibleArrayMemberFieldAndOffset(
-ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FAMDecl,
-uint64_t &Offset) {
+namespace {
+
+/// StructFieldAccess is a simple visitor class to grab the first MemberExpr
+/// from an Expr. It records any ArraySubscriptExpr we meet along the way.
+struct StructFieldAccess
+: public ConstStmtVisitor {
+  const ArraySubscriptExpr *ASE = nullptr;
+
+  const MemberExpr *VisitMemberExpr(const MemberExpr *E) { return E; }
+
+  const MemberExpr *VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
+ASE = E;
+return Visit(E->getBase());
+  }
+  const MemberExpr *VisitCastExpr(const CastExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitParenExpr(const ParenExpr *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryAddrOf(const clang::UnaryOperator *E) {
+return Visit(E->getSubExpr());
+  }
+  const MemberExpr *VisitUnaryDeref(const clang::UnaryOperator *E) {
+return Visit(E->getSubExpr());
+  }
+};
+
+} // end anonymous namespace
+
+/// Find a struct's flexible array member. It may be embedded inside multiple
+/// sub-structs, but must still be the last field.
+static const FieldDecl *FindFlexibleArrayMemberField(CodeGenFunction &CGF,
+ ASTContext &Ctx,
+ co