https://github.com/xlauko updated https://github.com/llvm/llvm-project/pull/223451
>From 1518d0a361033ba7c73dc3121a61e6345ff77f34 Mon Sep 17 00:00:00 2001 From: Henrich Lauko <[email protected]> Date: Tue, 29 Sep 2026 09:56:08 +0000 Subject: [PATCH 1/4] [CIR][CodeGen][NFC] Share isEmptyFieldForLayout and isEmptyRecordForLayout Deduplicates `isEmptyFieldForLayout` and `isEmptyRecordForLayout` between CIR and classic CodeGen into a new `RecordLayoutUtils.h`. The 25 callers now name them as `CodeGenUtils::isEmptyFieldForLayout` and `CodeGenUtils::isEmptyRecordForLayout`, like the other shared helpers. Assisted-by: Claude Code (Claude Fable 5.1). --- .../clang/CodeGenUtils/RecordLayoutUtils.h | 34 ++++++++++++++ clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 3 +- clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp | 6 ++- clang/lib/CIR/CodeGen/CIRGenModule.cpp | 3 +- .../CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp | 7 +-- clang/lib/CIR/CodeGen/TargetInfo.cpp | 34 -------------- clang/lib/CIR/CodeGen/TargetInfo.h | 9 ---- clang/lib/CodeGen/ABIInfoImpl.cpp | 33 -------------- clang/lib/CodeGen/ABIInfoImpl.h | 9 ---- clang/lib/CodeGen/CGClass.cpp | 5 ++- clang/lib/CodeGen/CGExpr.cpp | 3 +- clang/lib/CodeGen/CGExprConstant.cpp | 11 ++--- clang/lib/CodeGen/CGOpenMPRuntime.cpp | 7 +-- clang/lib/CodeGen/CGRecordLayoutBuilder.cpp | 13 +++--- clang/lib/CodeGen/CodeGenTBAA.cpp | 3 +- clang/lib/CodeGenUtils/CMakeLists.txt | 1 + clang/lib/CodeGenUtils/RecordLayoutUtils.cpp | 45 +++++++++++++++++++ 17 files changed, 116 insertions(+), 110 deletions(-) create mode 100644 clang/include/clang/CodeGenUtils/RecordLayoutUtils.h create mode 100644 clang/lib/CodeGenUtils/RecordLayoutUtils.cpp diff --git a/clang/include/clang/CodeGenUtils/RecordLayoutUtils.h b/clang/include/clang/CodeGenUtils/RecordLayoutUtils.h new file mode 100644 index 0000000000000..77f14fee5a3b4 --- /dev/null +++ b/clang/include/clang/CodeGenUtils/RecordLayoutUtils.h @@ -0,0 +1,34 @@ +//===--- RecordLayoutUtils.h - Shared record layout queries -----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file holds the AST queries about record layout that both classic +// CodeGen and CIR CodeGen need while lowering a record to its target type. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_CODEGENUTILS_RECORDLAYOUTUTILS_H +#define LLVM_CLANG_CODEGENUTILS_RECORDLAYOUTUTILS_H + +#include "clang/AST/ASTContext.h" + +namespace clang::CodeGenUtils { + +/// Return true iff the field is "empty", that is, either a zero-width +/// bit-field or an \ref isEmptyRecordForLayout. +bool isEmptyFieldForLayout(const ASTContext &Ctx, const FieldDecl *FD); + +/// Return true iff a structure contains only empty base classes (per \ref +/// isEmptyRecordForLayout) and fields (per \ref isEmptyFieldForLayout). Note, +/// C++ record fields are considered empty if the [[no_unique_address]] +/// attribute would have made them empty, so this is not the same as \ref +/// isEmptyRecord. +bool isEmptyRecordForLayout(const ASTContext &Ctx, QualType T); + +} // namespace clang::CodeGenUtils + +#endif // LLVM_CLANG_CODEGENUTILS_RECORDLAYOUTUTILS_H diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp index 1f738f9737d31..51533f9dc0f9c 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -32,6 +32,7 @@ #include "clang/CIR/MissingFeatures.h" #include "clang/CodeGenUtils/CodeGenUtils.h" #include "clang/CodeGenUtils/ExprUtils.h" +#include "clang/CodeGenUtils/RecordLayoutUtils.h" #include <optional> using namespace clang; @@ -77,7 +78,7 @@ Address CIRGenFunction::emitAddrOfFieldStorage(Address base, bool addressedByFieldIndex = field->isPotentiallyOverlapping() ? layout.hasCIRField(field) - : !isEmptyFieldForLayout(getContext(), field); + : !CodeGenUtils::isEmptyFieldForLayout(getContext(), field); if (!addressedByFieldIndex) return emitAddrOfZeroSizeField(*this, base, field); diff --git a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp index afc6b8058066d..100d11916dcee 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp @@ -29,6 +29,7 @@ #include "clang/CIR/Dialect/IR/CIRAttrs.h" #include "clang/CIR/Dialect/IR/CIRTypes.h" #include "clang/CIR/MissingFeatures.h" +#include "clang/CodeGenUtils/RecordLayoutUtils.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Support/ErrorHandling.h" @@ -1210,7 +1211,8 @@ static mlir::TypedAttr emitNullConstant(CIRGenModule &cgm, const RecordDecl *rd, const auto *baseDecl = base.getType()->castAsCXXRecordDecl(); // Ignore empty bases. - if (isEmptyRecordForLayout(cgm.getASTContext(), base.getType()) || + if (CodeGenUtils::isEmptyRecordForLayout(cgm.getASTContext(), + base.getType()) || cgm.getASTContext() .getASTRecordLayout(baseDecl) .getNonVirtualSize() @@ -1228,7 +1230,7 @@ static mlir::TypedAttr emitNullConstant(CIRGenModule &cgm, const RecordDecl *rd, // Fill in non-bitfields. (Bitfields always use a zero pattern, which we // will fill in later.) if (!field->isBitField() && - !isEmptyFieldForLayout(cgm.getASTContext(), field)) { + !CodeGenUtils::isEmptyFieldForLayout(cgm.getASTContext(), field)) { unsigned fieldIndex = layout.getCIRFieldNo(field); elements[fieldIndex] = cgm.emitNullConstantAttr(field->getType()); } diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index 5063b6a4cc779..92c8af433aace 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -38,6 +38,7 @@ #include "clang/CIR/MissingFeatures.h" #include "clang/CodeGenUtils/CodeGenUtils.h" #include "clang/CodeGenUtils/ModuleUtils.h" +#include "clang/CodeGenUtils/RecordLayoutUtils.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" @@ -2493,7 +2494,7 @@ bool CIRGenModule::findFieldMemberPath(const CXXRecordDecl *currentClass, bool CIRGenModule::isEmptyFieldForMemberPointer(const FieldDecl *field) { if (!field->isPotentiallyOverlapping() || - !isEmptyFieldForLayout(astContext, field)) + !CodeGenUtils::isEmptyFieldForLayout(astContext, field)) return false; // Unions always have a field even if they are empty. diff --git a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp index c2075a245f05c..8617ba5561e41 100644 --- a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp @@ -23,6 +23,7 @@ #include "clang/CIR/Dialect/IR/CIRDataLayout.h" #include "clang/CIR/MissingFeatures.h" #include "clang/CodeGenUtils/CodeGenUtils.h" +#include "clang/CodeGenUtils/RecordLayoutUtils.h" #include "llvm/Support/Casting.h" #include <memory> @@ -632,7 +633,7 @@ CIRRecordLowering::accumulateBitFields(RecordDecl::field_iterator field, // non-reusable tail padding. CharUnits limitOffset; for (auto probe = field; probe != fieldEnd; ++probe) - if (!isEmptyFieldForLayout(astContext, *probe)) { + if (!CodeGenUtils::isEmptyFieldForLayout(astContext, *probe)) { // A member with storage sets the limit. assert((getFieldBitOffset(*probe) % charBits) == 0 && "Next storage is not byte-aligned"); @@ -730,7 +731,7 @@ void CIRRecordLowering::accumulateFields(bool nonVirtualBaseType) { field = accumulateBitFields(field, fieldEnd); assert((field == fieldEnd || !field->isBitField()) && "Failed to accumulate all the bitfields"); - } else if (isEmptyFieldForLayout(astContext, *field) && + } else if (CodeGenUtils::isEmptyFieldForLayout(astContext, *field) && field->isPotentiallyOverlapping()) { // We lay out normal empty fields, as they are required for GEPs/getting // function pointers. However 'no-unique-address' lends some additional @@ -1287,7 +1288,7 @@ void CIRRecordLowering::accumulateBases() { void CIRRecordLowering::accumulateVBases() { for (const auto &base : cxxRecordDecl->vbases()) { const CXXRecordDecl *baseDecl = base.getType()->getAsCXXRecordDecl(); - if (isEmptyRecordForLayout(astContext, base.getType())) + if (CodeGenUtils::isEmptyRecordForLayout(astContext, base.getType())) continue; CharUnits offset = astRecordLayout.getVBaseClassOffset(baseDecl); // If the vbase is a primary virtual base of some base, then it doesn't diff --git a/clang/lib/CIR/CodeGen/TargetInfo.cpp b/clang/lib/CIR/CodeGen/TargetInfo.cpp index 2e86a60030841..b648ca1f2cd74 100644 --- a/clang/lib/CIR/CodeGen/TargetInfo.cpp +++ b/clang/lib/CIR/CodeGen/TargetInfo.cpp @@ -12,40 +12,6 @@ using namespace clang; using namespace clang::CIRGen; -bool clang::CIRGen::isEmptyRecordForLayout(const ASTContext &context, - QualType t) { - const auto *rd = t->getAsRecordDecl(); - if (!rd) - return false; - - // If this is a C++ record, check the bases first. - if (const CXXRecordDecl *cxxrd = dyn_cast<CXXRecordDecl>(rd)) { - if (cxxrd->isDynamicClass()) - return false; - - for (const auto &i : cxxrd->bases()) - if (!isEmptyRecordForLayout(context, i.getType())) - return false; - } - - for (const auto *i : rd->fields()) - if (!isEmptyFieldForLayout(context, i)) - return false; - - return true; -} - -bool clang::CIRGen::isEmptyFieldForLayout(const ASTContext &context, - const FieldDecl *fd) { - if (fd->isZeroLengthBitField()) - return true; - - if (fd->isUnnamedBitField()) - return false; - - return isEmptyRecordForLayout(context, fd->getType()); -} - bool clang::CIRGen::isEmptyRecordForABI(const ASTContext &context, QualType t) { const auto *rd = t->getAsRecordDecl(); if (!rd) diff --git a/clang/lib/CIR/CodeGen/TargetInfo.h b/clang/lib/CIR/CodeGen/TargetInfo.h index 6dad9df44269e..19bb5991fb793 100644 --- a/clang/lib/CIR/CodeGen/TargetInfo.h +++ b/clang/lib/CIR/CodeGen/TargetInfo.h @@ -27,15 +27,6 @@ namespace clang::CIRGen { -/// isEmptyFieldForLayout - Return true if the field is "empty", that is, -/// either a zero-width bit-field or an isEmptyRecordForLayout. -bool isEmptyFieldForLayout(const ASTContext &context, const FieldDecl *fd); - -/// isEmptyRecordForLayout - Return true if a structure contains only empty -/// base classes (per isEmptyRecordForLayout) and fields (per -/// isEmptyFieldForLayout). Note, C++ record fields are considered empty -/// if the [[no_unique_address]] attribute would have made them empty. -bool isEmptyRecordForLayout(const ASTContext &context, QualType t); /// isEmptyFieldForABI - Return true if the field is "empty", that is, it is a /// zero-width bit-field or an (array of) empty record(s). An unnamed diff --git a/clang/lib/CodeGen/ABIInfoImpl.cpp b/clang/lib/CodeGen/ABIInfoImpl.cpp index 887a645a4783a..23b6b7e8200da 100644 --- a/clang/lib/CodeGen/ABIInfoImpl.cpp +++ b/clang/lib/CodeGen/ABIInfoImpl.cpp @@ -302,39 +302,6 @@ bool CodeGen::isEmptyRecord(ASTContext &Context, QualType T, bool AllowArrays, return true; } -bool CodeGen::isEmptyFieldForLayout(const ASTContext &Context, - const FieldDecl *FD) { - if (FD->isZeroLengthBitField()) - return true; - - if (FD->isUnnamedBitField()) - return false; - - return isEmptyRecordForLayout(Context, FD->getType()); -} - -bool CodeGen::isEmptyRecordForLayout(const ASTContext &Context, QualType T) { - const auto *RD = T->getAsRecordDecl(); - if (!RD) - return false; - - // If this is a C++ record, check the bases first. - if (const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(RD)) { - if (CXXRD->isDynamicClass()) - return false; - - for (const auto &I : CXXRD->bases()) - if (!isEmptyRecordForLayout(Context, I.getType())) - return false; - } - - for (const auto *I : RD->fields()) - if (!isEmptyFieldForLayout(Context, I)) - return false; - - return true; -} - const Type *CodeGen::isSingleElementStruct(QualType T, ASTContext &Context) { const auto *RD = T->getAsRecordDecl(); if (!RD) diff --git a/clang/lib/CodeGen/ABIInfoImpl.h b/clang/lib/CodeGen/ABIInfoImpl.h index d9d79c6a55ddb..180a76f4a820e 100644 --- a/clang/lib/CodeGen/ABIInfoImpl.h +++ b/clang/lib/CodeGen/ABIInfoImpl.h @@ -120,15 +120,6 @@ bool isEmptyField(ASTContext &Context, const FieldDecl *FD, bool AllowArrays, bool isEmptyRecord(ASTContext &Context, QualType T, bool AllowArrays, bool AsIfNoUniqueAddr = false); -/// isEmptyFieldForLayout - Return true iff the field is "empty", that is, -/// either a zero-width bit-field or an \ref isEmptyRecordForLayout. -bool isEmptyFieldForLayout(const ASTContext &Context, const FieldDecl *FD); - -/// isEmptyRecordForLayout - Return true iff a structure contains only empty -/// base classes (per \ref isEmptyRecordForLayout) and fields (per -/// \ref isEmptyFieldForLayout). Note, C++ record fields are considered empty -/// if the [[no_unique_address]] attribute would have made them empty. -bool isEmptyRecordForLayout(const ASTContext &Context, QualType T); /// isSingleElementStruct - Determine if a structure is a "single /// element struct", i.e. it has exactly one non-empty field or diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp index 63f5d9ae13dbf..7e832f8155aa7 100644 --- a/clang/lib/CodeGen/CGClass.cpp +++ b/clang/lib/CodeGen/CGClass.cpp @@ -27,6 +27,7 @@ #include "clang/CodeGen/CGFunctionInfo.h" #include "clang/CodeGenUtils/ClassUtils.h" #include "clang/CodeGenUtils/CodeGenUtils.h" +#include "clang/CodeGenUtils/RecordLayoutUtils.h" #include "llvm/IR/Intrinsics.h" #include "llvm/IR/Metadata.h" #include "llvm/Support/SaveAndRestore.h" @@ -882,7 +883,7 @@ class FieldMemcpyizer { } void addMemcpyableField(FieldDecl *F) { - if (isEmptyFieldForLayout(CGF.getContext(), F)) + if (CodeGenUtils::isEmptyFieldForLayout(CGF.getContext(), F)) return; if (!FirstField) addInitialField(F); @@ -1863,7 +1864,7 @@ class SanitizeDtorCleanupBuilder { const CXXDestructorDecl *DD) : Context(Context), EHStack(EHStack), DD(DD), StartIndex(std::nullopt) {} void PushCleanupForField(const FieldDecl *Field) { - if (isEmptyFieldForLayout(Context, Field)) + if (CodeGenUtils::isEmptyFieldForLayout(Context, Field)) return; unsigned FieldIndex = Field->getFieldIndex(); if (CodeGenUtils::fieldHasTrivialDestructorBody(Context, Field)) { diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 76bd781a709b6..8323a170a1dbb 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -41,6 +41,7 @@ #include "clang/Basic/SourceManager.h" #include "clang/CodeGenUtils/CodeGenUtils.h" #include "clang/CodeGenUtils/ExprUtils.h" +#include "clang/CodeGenUtils/RecordLayoutUtils.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/StringExtras.h" @@ -5909,7 +5910,7 @@ static Address emitAddrOfZeroSizeField(CodeGenFunction &CGF, Address Base, static Address emitRawAddrOfFieldStorage(CodeGenFunction &CGF, Address base, const FieldDecl *field, bool IsInBounds) { - if (isEmptyFieldForLayout(CGF.getContext(), field)) + if (CodeGenUtils::isEmptyFieldForLayout(CGF.getContext(), field)) return emitAddrOfZeroSizeField(CGF, base, field, IsInBounds); const RecordDecl *rec = field->getParent(); diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp index cc4793d328166..e2ec07bbae711 100644 --- a/clang/lib/CodeGen/CGExprConstant.cpp +++ b/clang/lib/CodeGen/CGExprConstant.cpp @@ -26,6 +26,7 @@ #include "clang/AST/RecordLayout.h" #include "clang/AST/StmtVisitor.h" #include "clang/Basic/Builtins.h" +#include "clang/CodeGenUtils/RecordLayoutUtils.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/Sequence.h" #include "llvm/Analysis/ConstantFolding.h" @@ -762,7 +763,7 @@ bool ConstStructBuilder::Build(const InitListExpr *ILE, bool AllowOverwrite) { // Zero-sized fields are not emitted, but their initializers may still // prevent emission of this struct as a constant. - if (isEmptyFieldForLayout(CGM.getContext(), Field)) { + if (CodeGenUtils::isEmptyFieldForLayout(CGM.getContext(), Field)) { if (Init && Init->HasSideEffects(CGM.getContext())) return false; continue; @@ -925,7 +926,7 @@ bool ConstStructBuilder::Build(const APValue &Val, const RecordDecl *RD, // Don't emit anonymous bitfields or zero-sized fields. if (Field->isUnnamedBitField() || - isEmptyFieldForLayout(CGM.getContext(), *Field)) + CodeGenUtils::isEmptyFieldForLayout(CGM.getContext(), *Field)) continue; // Emit the value of the initializer. @@ -2842,7 +2843,7 @@ static llvm::Constant *EmitNullConstant(CodeGenModule &CGM, const auto *base = I.getType()->castAsCXXRecordDecl(); // Ignore empty bases. - if (isEmptyRecordForLayout(CGM.getContext(), I.getType()) || + if (CodeGenUtils::isEmptyRecordForLayout(CGM.getContext(), I.getType()) || CGM.getContext() .getASTRecordLayout(base) .getNonVirtualSize() @@ -2860,7 +2861,7 @@ static llvm::Constant *EmitNullConstant(CodeGenModule &CGM, // Fill in non-bitfields. (Bitfields always use a zero pattern, which we // will fill in later.) if (!Field->isBitField() && - !isEmptyFieldForLayout(CGM.getContext(), Field)) { + !CodeGenUtils::isEmptyFieldForLayout(CGM.getContext(), Field)) { unsigned fieldIndex = layout.getLLVMFieldNo(Field); elements[fieldIndex] = CGM.EmitNullConstant(Field->getType()); } @@ -2880,7 +2881,7 @@ static llvm::Constant *EmitNullConstant(CodeGenModule &CGM, for (const auto &I : CXXR->vbases()) { const auto *base = I.getType()->castAsCXXRecordDecl(); // Ignore empty bases. - if (isEmptyRecordForLayout(CGM.getContext(), I.getType())) + if (CodeGenUtils::isEmptyRecordForLayout(CGM.getContext(), I.getType())) continue; unsigned fieldIndex = layout.getVirtualBaseIndex(base); diff --git a/clang/lib/CodeGen/CGOpenMPRuntime.cpp b/clang/lib/CodeGen/CGOpenMPRuntime.cpp index 58bfd48a49d39..9893ecc4ec7cc 100644 --- a/clang/lib/CodeGen/CGOpenMPRuntime.cpp +++ b/clang/lib/CodeGen/CGOpenMPRuntime.cpp @@ -28,6 +28,7 @@ #include "clang/Basic/OpenMPKinds.h" #include "clang/Basic/SourceManager.h" #include "clang/CodeGen/ConstantInitBuilder.h" +#include "clang/CodeGenUtils/RecordLayoutUtils.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/SmallVector.h" @@ -8937,7 +8938,7 @@ class MappableExprsHandler { QualType BaseTy = I.getType(); const auto *Base = BaseTy->getAsCXXRecordDecl(); // Ignore empty bases. - if (isEmptyRecordForLayout(CGF.getContext(), BaseTy) || + if (CodeGenUtils::isEmptyRecordForLayout(CGF.getContext(), BaseTy) || CGF.getContext() .getASTRecordLayout(Base) .getNonVirtualSize() @@ -8951,7 +8952,7 @@ class MappableExprsHandler { for (const auto &I : RD->vbases()) { QualType BaseTy = I.getType(); // Ignore empty bases. - if (isEmptyRecordForLayout(CGF.getContext(), BaseTy)) + if (CodeGenUtils::isEmptyRecordForLayout(CGF.getContext(), BaseTy)) continue; const auto *Base = BaseTy->getAsCXXRecordDecl(); @@ -8966,7 +8967,7 @@ class MappableExprsHandler { // Fill in non-bitfields. (Bitfields always use a zero pattern, which we // will fill in later.) if (!Field->isBitField() && - !isEmptyFieldForLayout(CGF.getContext(), Field)) { + !CodeGenUtils::isEmptyFieldForLayout(CGF.getContext(), Field)) { unsigned FieldIndex = RL.getLLVMFieldNo(Field); RecordLayout[FieldIndex] = Field; } diff --git a/clang/lib/CodeGen/CGRecordLayoutBuilder.cpp b/clang/lib/CodeGen/CGRecordLayoutBuilder.cpp index d98d7c0c96dc3..14c4b969e141c 100644 --- a/clang/lib/CodeGen/CGRecordLayoutBuilder.cpp +++ b/clang/lib/CodeGen/CGRecordLayoutBuilder.cpp @@ -22,6 +22,7 @@ #include "clang/AST/RecordLayout.h" #include "clang/Basic/CodeGenOptions.h" #include "clang/CodeGenUtils/CodeGenUtils.h" +#include "clang/CodeGenUtils/RecordLayoutUtils.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/Type.h" @@ -381,7 +382,7 @@ void CGRecordLowering::accumulateFields(bool isNonVirtualBaseType) { Field = accumulateBitFields(isNonVirtualBaseType, Field, FieldEnd); assert((Field == FieldEnd || !Field->isBitField()) && "Failed to accumulate all the bitfields"); - } else if (isEmptyFieldForLayout(Context, *Field)) { + } else if (CodeGenUtils::isEmptyFieldForLayout(Context, *Field)) { // Empty fields have no storage. ++Field; } else { @@ -630,7 +631,7 @@ CGRecordLowering::accumulateBitFields(bool isNonVirtualBaseType, // non-reusable tail padding. CharUnits LimitOffset; for (auto Probe = Field; Probe != FieldEnd; ++Probe) - if (!isEmptyFieldForLayout(Context, *Probe)) { + if (!CodeGenUtils::isEmptyFieldForLayout(Context, *Probe)) { // A member with storage sets the limit. assert((getFieldBitOffset(*Probe) % CharBits) == 0 && "Next storage is not byte-aligned"); @@ -728,7 +729,7 @@ void CGRecordLowering::accumulateBases() { // Bases can be zero-sized even if not technically empty if they // contain only a trailing array member. const CXXRecordDecl *BaseDecl = Base.getType()->getAsCXXRecordDecl(); - if (!isEmptyRecordForLayout(Context, Base.getType()) && + if (!CodeGenUtils::isEmptyRecordForLayout(Context, Base.getType()) && !Context.getASTRecordLayout(BaseDecl).getNonVirtualSize().isZero()) Members.push_back(MemberInfo(Layout.getBaseClassOffset(BaseDecl), MemberInfo::Base, getStorageType(BaseDecl), BaseDecl)); @@ -877,7 +878,7 @@ CGRecordLowering::calculateTailClippingOffset(bool isNonVirtualBaseType) const { if (!isNonVirtualBaseType && isOverlappingVBaseABI()) for (const auto &Base : RD->vbases()) { const CXXRecordDecl *BaseDecl = Base.getType()->getAsCXXRecordDecl(); - if (isEmptyRecordForLayout(Context, Base.getType())) + if (CodeGenUtils::isEmptyRecordForLayout(Context, Base.getType())) continue; // If the vbase is a primary virtual base of some base, then it doesn't // get its own storage location but instead lives inside of that base. @@ -893,7 +894,7 @@ CGRecordLowering::calculateTailClippingOffset(bool isNonVirtualBaseType) const { void CGRecordLowering::accumulateVBases() { for (const auto &Base : RD->vbases()) { const CXXRecordDecl *BaseDecl = Base.getType()->getAsCXXRecordDecl(); - if (isEmptyRecordForLayout(Context, Base.getType())) + if (CodeGenUtils::isEmptyRecordForLayout(Context, Base.getType())) continue; CharUnits Offset = Layout.getVBaseClassOffset(BaseDecl); // If the vbase is a primary virtual base of some base, then it doesn't @@ -1154,7 +1155,7 @@ CodeGenTypes::ComputeRecordLayout(const RecordDecl *D, llvm::StructType *Ty) { const FieldDecl *FD = *it; // Ignore zero-sized fields. - if (isEmptyFieldForLayout(getContext(), FD)) + if (CodeGenUtils::isEmptyFieldForLayout(getContext(), FD)) continue; // For non-bit-fields, just check that the LLVM struct offset matches the diff --git a/clang/lib/CodeGen/CodeGenTBAA.cpp b/clang/lib/CodeGen/CodeGenTBAA.cpp index 1854df7c7c0f1..d33188db9da15 100644 --- a/clang/lib/CodeGen/CodeGenTBAA.cpp +++ b/clang/lib/CodeGen/CodeGenTBAA.cpp @@ -25,6 +25,7 @@ #include "clang/AST/RecordLayout.h" #include "clang/Basic/CodeGenOptions.h" #include "clang/Basic/TargetInfo.h" +#include "clang/CodeGenUtils/RecordLayoutUtils.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Metadata.h" #include "llvm/IR/Module.h" @@ -455,7 +456,7 @@ CodeGenTBAA::CollectFields(uint64_t BaseOffset, unsigned idx = 0; for (RecordDecl::field_iterator i = RD->field_begin(), e = RD->field_end(); i != e; ++i, ++idx) { - if (isEmptyFieldForLayout(Context, *i)) + if (CodeGenUtils::isEmptyFieldForLayout(Context, *i)) continue; uint64_t Offset = diff --git a/clang/lib/CodeGenUtils/CMakeLists.txt b/clang/lib/CodeGenUtils/CMakeLists.txt index 86d680aaf64b1..7fb94a994bfed 100644 --- a/clang/lib/CodeGenUtils/CMakeLists.txt +++ b/clang/lib/CodeGenUtils/CMakeLists.txt @@ -15,6 +15,7 @@ add_clang_library(clangCodeGenUtils FunctionUtils.cpp ItaniumCXXABIUtils.cpp ModuleUtils.cpp + RecordLayoutUtils.cpp StmtUtils.cpp TargetUtils.cpp diff --git a/clang/lib/CodeGenUtils/RecordLayoutUtils.cpp b/clang/lib/CodeGenUtils/RecordLayoutUtils.cpp new file mode 100644 index 0000000000000..3b8d433dc8759 --- /dev/null +++ b/clang/lib/CodeGenUtils/RecordLayoutUtils.cpp @@ -0,0 +1,45 @@ +//===--- RecordLayoutUtils.cpp - Shared record layout queries -------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/CodeGenUtils/RecordLayoutUtils.h" + +namespace clang::CodeGenUtils { + +bool isEmptyFieldForLayout(const ASTContext &Ctx, const FieldDecl *FD) { + if (FD->isZeroLengthBitField()) + return true; + + if (FD->isUnnamedBitField()) + return false; + + return isEmptyRecordForLayout(Ctx, FD->getType()); +} + +bool isEmptyRecordForLayout(const ASTContext &Ctx, QualType T) { + const auto *RD = T->getAsRecordDecl(); + if (!RD) + return false; + + // If this is a C++ record, check the bases first. + if (const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(RD)) { + if (CXXRD->isDynamicClass()) + return false; + + for (const auto &I : CXXRD->bases()) + if (!isEmptyRecordForLayout(Ctx, I.getType())) + return false; + } + + for (const auto *I : RD->fields()) + if (!isEmptyFieldForLayout(Ctx, I)) + return false; + + return true; +} + +} // namespace clang::CodeGenUtils >From 6d20665a0bfbded82f0b700ee03bc2f7fb029403 Mon Sep 17 00:00:00 2001 From: Henrich Lauko <[email protected]> Date: Tue, 29 Sep 2026 09:56:12 +0000 Subject: [PATCH 2/4] [CIR][CodeGen][NFC] Share the bit-field and vbase layout ABI predicates Deduplicates `isDiscreteBitFieldABI` and `isOverlappingVBaseABI` between CIR and classic CodeGen into `RecordLayoutUtils.h`, as free functions taking the `ASTContext`. Assisted-by: Claude Code (Claude Fable 5.1). --- .../clang/CodeGenUtils/RecordLayoutUtils.h | 12 ++++++++ .../CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp | 22 ++------------ clang/lib/CodeGen/CGRecordLayoutBuilder.cpp | 30 ++++--------------- clang/lib/CodeGenUtils/RecordLayoutUtils.cpp | 9 ++++++ 4 files changed, 30 insertions(+), 43 deletions(-) diff --git a/clang/include/clang/CodeGenUtils/RecordLayoutUtils.h b/clang/include/clang/CodeGenUtils/RecordLayoutUtils.h index 77f14fee5a3b4..2c1ee3c271a3d 100644 --- a/clang/include/clang/CodeGenUtils/RecordLayoutUtils.h +++ b/clang/include/clang/CodeGenUtils/RecordLayoutUtils.h @@ -18,6 +18,12 @@ namespace clang::CodeGenUtils { +/// The Microsoft bitfield layout rule allocates discrete storage units of the +/// field's formal type and only combines adjacent fields of the same formal +/// type. We want to emit a layout with these discrete storage units instead +/// of combining them into a continuous run. +bool isDiscreteBitFieldABI(const ASTContext &Ctx, const RecordDecl *RD); + /// Return true iff the field is "empty", that is, either a zero-width /// bit-field or an \ref isEmptyRecordForLayout. bool isEmptyFieldForLayout(const ASTContext &Ctx, const FieldDecl *FD); @@ -29,6 +35,12 @@ bool isEmptyFieldForLayout(const ASTContext &Ctx, const FieldDecl *FD); /// isEmptyRecord. bool isEmptyRecordForLayout(const ASTContext &Ctx, QualType T); +/// The Itanium base layout rule allows virtual bases to overlap other bases, +/// which complicates layout in specific ways. +/// +/// Note specifically that the ms_struct attribute doesn't change this. +bool isOverlappingVBaseABI(const ASTContext &Ctx); + } // namespace clang::CodeGenUtils #endif // LLVM_CLANG_CODEGENUTILS_RECORDLAYOUTUTILS_H diff --git a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp index 8617ba5561e41..982a0923a5067 100644 --- a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp @@ -134,27 +134,10 @@ struct CIRRecordLowering final { /// Helper function to check if the target machine is BigEndian. bool isBigEndian() const { return astContext.getTargetInfo().isBigEndian(); } - // The Itanium base layout rule allows virtual bases to overlap - // other bases, which complicates layout in specific ways. - // - // Note specifically that the ms_struct attribute doesn't change this. - bool isOverlappingVBaseABI() { - return !astContext.getTargetInfo().getCXXABI().isMicrosoft(); - } // Recursively searches all of the bases to find out if a vbase is // not the primary vbase of some base class. bool hasOwnStorage(const CXXRecordDecl *decl, const CXXRecordDecl *query); - /// The Microsoft bitfield layout rule allocates discrete storage - /// units of the field's formal type and only combines adjacent - /// fields of the same formal type. We want to emit a layout with - /// these discrete storage units instead of combining them into a - /// continuous run. - bool isDiscreteBitFieldABI() { - return astContext.getTargetInfo().getCXXABI().isMicrosoft() || - recordDecl->isMsStruct(astContext); - } - CharUnits bitsToCharUnits(uint64_t bitOffset) { return astContext.toCharUnitsFromBits(bitOffset); } @@ -446,7 +429,7 @@ void CIRRecordLowering::fillOutputFields() { RecordDecl::field_iterator CIRRecordLowering::accumulateBitFields(RecordDecl::field_iterator field, RecordDecl::field_iterator fieldEnd) { - if (isDiscreteBitFieldABI()) { + if (CodeGenUtils::isDiscreteBitFieldABI(astContext, recordDecl)) { // run stores the first element of the current run of bitfields. fieldEnd is // used as a special value to note that we don't have a current run. A // bitfield run is a contiguous collection of bitfields that can be stored @@ -1293,7 +1276,8 @@ void CIRRecordLowering::accumulateVBases() { CharUnits offset = astRecordLayout.getVBaseClassOffset(baseDecl); // If the vbase is a primary virtual base of some base, then it doesn't // get its own storage location but instead lives inside of that base. - if (isOverlappingVBaseABI() && astContext.isNearlyEmpty(baseDecl) && + if (CodeGenUtils::isOverlappingVBaseABI(astContext) && + astContext.isNearlyEmpty(baseDecl) && !hasOwnStorage(cxxRecordDecl, baseDecl)) { members.push_back(MemberInfo(offset, MemberInfo::InfoKind::VBase, nullptr, cir::RecordMemberKind::Data, baseDecl)); diff --git a/clang/lib/CodeGen/CGRecordLayoutBuilder.cpp b/clang/lib/CodeGen/CGRecordLayoutBuilder.cpp index 14c4b969e141c..9b2f6426cfc84 100644 --- a/clang/lib/CodeGen/CGRecordLayoutBuilder.cpp +++ b/clang/lib/CodeGen/CGRecordLayoutBuilder.cpp @@ -101,27 +101,9 @@ struct CGRecordLowering { return MemberInfo(Offset, MemberInfo::Field, Data); } - /// The Microsoft bitfield layout rule allocates discrete storage - /// units of the field's formal type and only combines adjacent - /// fields of the same formal type. We want to emit a layout with - /// these discrete storage units instead of combining them into a - /// continuous run. - bool isDiscreteBitFieldABI() const { - return Context.getTargetInfo().getCXXABI().isMicrosoft() || - D->isMsStruct(Context); - } - /// Helper function to check if the target machine is BigEndian. bool isBE() const { return Context.getTargetInfo().isBigEndian(); } - /// The Itanium base layout rule allows virtual bases to overlap - /// other bases, which complicates layout in specific ways. - /// - /// Note specifically that the ms_struct attribute doesn't change this. - bool isOverlappingVBaseABI() const { - return !Context.getTargetInfo().getCXXABI().isMicrosoft(); - } - /// Wraps llvm::Type::getIntNTy with some implicit arguments. llvm::Type *getIntNType(uint64_t NumBits) const { unsigned AlignedBits = llvm::alignTo(NumBits, Context.getCharWidth()); @@ -144,7 +126,8 @@ struct CGRecordLowering { llvm::Type *getStorageType(const FieldDecl *FD) const { llvm::Type *Type = Types.ConvertTypeForMem(FD->getType()); if (!FD->isBitField()) return Type; - if (isDiscreteBitFieldABI()) return Type; + if (CodeGenUtils::isDiscreteBitFieldABI(Context, D)) + return Type; return getIntNType(std::min(FD->getBitWidthValue(), (unsigned)Context.toBits(getSize(Type)))); } @@ -407,7 +390,7 @@ RecordDecl::field_iterator CGRecordLowering::accumulateBitFields(bool isNonVirtualBaseType, RecordDecl::field_iterator Field, RecordDecl::field_iterator FieldEnd) { - if (isDiscreteBitFieldABI()) { + if (CodeGenUtils::isDiscreteBitFieldABI(Context, D)) { // Run stores the first element of the current run of bitfields. FieldEnd is // used as a special value to note that we don't have a current run. A // bitfield run is a contiguous collection of bitfields that can be stored @@ -875,7 +858,7 @@ CGRecordLowering::calculateTailClippingOffset(bool isNonVirtualBaseType) const { // smaller than the nvsize. Here we check to see if such a base is placed // before the nvsize and set the scissor offset to that, instead of the // nvsize. - if (!isNonVirtualBaseType && isOverlappingVBaseABI()) + if (!isNonVirtualBaseType && CodeGenUtils::isOverlappingVBaseABI(Context)) for (const auto &Base : RD->vbases()) { const CXXRecordDecl *BaseDecl = Base.getType()->getAsCXXRecordDecl(); if (CodeGenUtils::isEmptyRecordForLayout(Context, Base.getType())) @@ -899,9 +882,8 @@ void CGRecordLowering::accumulateVBases() { CharUnits Offset = Layout.getVBaseClassOffset(BaseDecl); // If the vbase is a primary virtual base of some base, then it doesn't // get its own storage location but instead lives inside of that base. - if (isOverlappingVBaseABI() && - Context.isNearlyEmpty(BaseDecl) && - !hasOwnStorage(RD, BaseDecl)) { + if (CodeGenUtils::isOverlappingVBaseABI(Context) && + Context.isNearlyEmpty(BaseDecl) && !hasOwnStorage(RD, BaseDecl)) { Members.push_back(MemberInfo(Offset, MemberInfo::VBase, nullptr, BaseDecl)); continue; diff --git a/clang/lib/CodeGenUtils/RecordLayoutUtils.cpp b/clang/lib/CodeGenUtils/RecordLayoutUtils.cpp index 3b8d433dc8759..b41fb61491377 100644 --- a/clang/lib/CodeGenUtils/RecordLayoutUtils.cpp +++ b/clang/lib/CodeGenUtils/RecordLayoutUtils.cpp @@ -7,9 +7,14 @@ //===----------------------------------------------------------------------===// #include "clang/CodeGenUtils/RecordLayoutUtils.h" +#include "clang/Basic/TargetInfo.h" namespace clang::CodeGenUtils { +bool isDiscreteBitFieldABI(const ASTContext &Ctx, const RecordDecl *RD) { + return Ctx.getTargetInfo().getCXXABI().isMicrosoft() || RD->isMsStruct(Ctx); +} + bool isEmptyFieldForLayout(const ASTContext &Ctx, const FieldDecl *FD) { if (FD->isZeroLengthBitField()) return true; @@ -42,4 +47,8 @@ bool isEmptyRecordForLayout(const ASTContext &Ctx, QualType T) { return true; } +bool isOverlappingVBaseABI(const ASTContext &Ctx) { + return !Ctx.getTargetInfo().getCXXABI().isMicrosoft(); +} + } // namespace clang::CodeGenUtils >From 4d86c772aaf8d2d04bd7ccde3bab9505eadc5c86 Mon Sep 17 00:00:00 2001 From: Henrich Lauko <[email protected]> Date: Tue, 29 Sep 2026 09:56:18 +0000 Subject: [PATCH 3/4] [CIR][CodeGen][NFC] Share hasOwnStorage Deduplicates `hasOwnStorage` between CIR and classic CodeGen into `RecordLayoutUtils.h`. Assisted-by: Claude Code (Claude Fable 5.1). --- .../clang/CodeGenUtils/RecordLayoutUtils.h | 5 +++++ .../CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp | 17 +-------------- clang/lib/CodeGen/CGRecordLayoutBuilder.cpp | 21 ++++--------------- clang/lib/CodeGenUtils/RecordLayoutUtils.cpp | 12 +++++++++++ 4 files changed, 22 insertions(+), 33 deletions(-) diff --git a/clang/include/clang/CodeGenUtils/RecordLayoutUtils.h b/clang/include/clang/CodeGenUtils/RecordLayoutUtils.h index 2c1ee3c271a3d..594411e076277 100644 --- a/clang/include/clang/CodeGenUtils/RecordLayoutUtils.h +++ b/clang/include/clang/CodeGenUtils/RecordLayoutUtils.h @@ -18,6 +18,11 @@ namespace clang::CodeGenUtils { +/// Recursively searches all of the bases of \p Decl to find out whether +/// \p Query is not the primary vbase of some base class. +bool hasOwnStorage(const ASTContext &Ctx, const CXXRecordDecl *Decl, + const CXXRecordDecl *Query); + /// The Microsoft bitfield layout rule allocates discrete storage units of the /// field's formal type and only combines adjacent fields of the same formal /// type. We want to emit a layout with these discrete storage units instead diff --git a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp index 982a0923a5067..4bb483fc57a96 100644 --- a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp @@ -134,10 +134,6 @@ struct CIRRecordLowering final { /// Helper function to check if the target machine is BigEndian. bool isBigEndian() const { return astContext.getTargetInfo().isBigEndian(); } - // Recursively searches all of the bases to find out if a vbase is - // not the primary vbase of some base class. - bool hasOwnStorage(const CXXRecordDecl *decl, const CXXRecordDecl *query); - CharUnits bitsToCharUnits(uint64_t bitOffset) { return astContext.toCharUnitsFromBits(bitOffset); } @@ -1112,17 +1108,6 @@ void CIRRecordLowering::lowerUnion(bool nonVirtualBaseType) { packed = !layoutSize.isMultipleOf(getMemberAlignment(storageType)); } -bool CIRRecordLowering::hasOwnStorage(const CXXRecordDecl *decl, - const CXXRecordDecl *query) { - const ASTRecordLayout &declLayout = astContext.getASTRecordLayout(decl); - if (declLayout.isPrimaryBaseVirtual() && declLayout.getPrimaryBase() == query) - return false; - for (const auto &base : decl->bases()) - if (!hasOwnStorage(base.getType()->getAsCXXRecordDecl(), query)) - return false; - return true; -} - /// The AAPCS that defines that, when possible, bit-fields should /// be accessed using containers of the declared type width: /// When a volatile bit-field is read, and its container does not overlap with @@ -1278,7 +1263,7 @@ void CIRRecordLowering::accumulateVBases() { // get its own storage location but instead lives inside of that base. if (CodeGenUtils::isOverlappingVBaseABI(astContext) && astContext.isNearlyEmpty(baseDecl) && - !hasOwnStorage(cxxRecordDecl, baseDecl)) { + !CodeGenUtils::hasOwnStorage(astContext, cxxRecordDecl, baseDecl)) { members.push_back(MemberInfo(offset, MemberInfo::InfoKind::VBase, nullptr, cir::RecordMemberKind::Data, baseDecl)); continue; diff --git a/clang/lib/CodeGen/CGRecordLayoutBuilder.cpp b/clang/lib/CodeGen/CGRecordLayoutBuilder.cpp index 9b2f6426cfc84..300fd521e36cf 100644 --- a/clang/lib/CodeGen/CGRecordLayoutBuilder.cpp +++ b/clang/lib/CodeGen/CGRecordLayoutBuilder.cpp @@ -172,10 +172,6 @@ struct CGRecordLowering { void accumulateBases(); void accumulateVPtrs(); void accumulateVBases(); - /// Recursively searches all of the bases to find out if a vbase is - /// not the primary vbase of some base class. - bool hasOwnStorage(const CXXRecordDecl *Decl, - const CXXRecordDecl *Query) const; void calculateZeroInit(); CharUnits calculateTailClippingOffset(bool isNonVirtualBaseType) const; void checkBitfieldClipping(bool isNonVirtualBaseType) const; @@ -865,7 +861,8 @@ CGRecordLowering::calculateTailClippingOffset(bool isNonVirtualBaseType) const { continue; // If the vbase is a primary virtual base of some base, then it doesn't // get its own storage location but instead lives inside of that base. - if (Context.isNearlyEmpty(BaseDecl) && !hasOwnStorage(RD, BaseDecl)) + if (Context.isNearlyEmpty(BaseDecl) && + !CodeGenUtils::hasOwnStorage(Context, RD, BaseDecl)) continue; ScissorOffset = std::min(ScissorOffset, Layout.getVBaseClassOffset(BaseDecl)); @@ -883,7 +880,8 @@ void CGRecordLowering::accumulateVBases() { // If the vbase is a primary virtual base of some base, then it doesn't // get its own storage location but instead lives inside of that base. if (CodeGenUtils::isOverlappingVBaseABI(Context) && - Context.isNearlyEmpty(BaseDecl) && !hasOwnStorage(RD, BaseDecl)) { + Context.isNearlyEmpty(BaseDecl) && + !CodeGenUtils::hasOwnStorage(Context, RD, BaseDecl)) { Members.push_back(MemberInfo(Offset, MemberInfo::VBase, nullptr, BaseDecl)); continue; @@ -897,17 +895,6 @@ void CGRecordLowering::accumulateVBases() { } } -bool CGRecordLowering::hasOwnStorage(const CXXRecordDecl *Decl, - const CXXRecordDecl *Query) const { - const ASTRecordLayout &DeclLayout = Context.getASTRecordLayout(Decl); - if (DeclLayout.isPrimaryBaseVirtual() && DeclLayout.getPrimaryBase() == Query) - return false; - for (const auto &Base : Decl->bases()) - if (!hasOwnStorage(Base.getType()->getAsCXXRecordDecl(), Query)) - return false; - return true; -} - void CGRecordLowering::calculateZeroInit() { for (std::vector<MemberInfo>::const_iterator Member = Members.begin(), MemberEnd = Members.end(); diff --git a/clang/lib/CodeGenUtils/RecordLayoutUtils.cpp b/clang/lib/CodeGenUtils/RecordLayoutUtils.cpp index b41fb61491377..31306470b6da6 100644 --- a/clang/lib/CodeGenUtils/RecordLayoutUtils.cpp +++ b/clang/lib/CodeGenUtils/RecordLayoutUtils.cpp @@ -7,10 +7,22 @@ //===----------------------------------------------------------------------===// #include "clang/CodeGenUtils/RecordLayoutUtils.h" +#include "clang/AST/RecordLayout.h" #include "clang/Basic/TargetInfo.h" namespace clang::CodeGenUtils { +bool hasOwnStorage(const ASTContext &Ctx, const CXXRecordDecl *Decl, + const CXXRecordDecl *Query) { + const ASTRecordLayout &DeclLayout = Ctx.getASTRecordLayout(Decl); + if (DeclLayout.isPrimaryBaseVirtual() && DeclLayout.getPrimaryBase() == Query) + return false; + for (const auto &Base : Decl->bases()) + if (!hasOwnStorage(Ctx, Base.getType()->getAsCXXRecordDecl(), Query)) + return false; + return true; +} + bool isDiscreteBitFieldABI(const ASTContext &Ctx, const RecordDecl *RD) { return Ctx.getTargetInfo().getCXXABI().isMicrosoft() || RD->isMsStruct(Ctx); } >From 650834e214c346375877183f615b997cad10535d Mon Sep 17 00:00:00 2001 From: Henrich Lauko <[email protected]> Date: Mon, 14 Sep 2026 16:22:27 +0000 Subject: [PATCH 4/4] [CIR][CodeGen][NFC] Retire the CodeGenUtils.h catch-all header Moves the last helpers out of `CodeGenUtils.h` into `ClassUtils.h`, `ModuleUtils.h`, `TargetUtils.h` and `FunctionUtils.h` (`checkTargetFeatures`, since it came from CodeGenFunction.cpp) and deletes the header. Only moves code already on main, so it can be dropped on its own. Assisted-by: Claude Code (Claude Fable 5.1). --- clang/include/clang/CodeGenUtils/ClassUtils.h | 18 ++ .../include/clang/CodeGenUtils/CodeGenUtils.h | 73 ----- .../clang/CodeGenUtils/FunctionUtils.h | 26 +- .../include/clang/CodeGenUtils/ModuleUtils.h | 15 ++ .../include/clang/CodeGenUtils/TargetUtils.h | 4 + clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp | 2 +- clang/lib/CIR/CodeGen/CIRGenClass.cpp | 1 - clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 2 +- clang/lib/CIR/CodeGen/CIRGenFunction.cpp | 2 +- clang/lib/CIR/CodeGen/CIRGenModule.cpp | 1 - .../CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp | 2 +- clang/lib/CodeGen/CGClass.cpp | 1 - clang/lib/CodeGen/CGExpr.cpp | 2 +- clang/lib/CodeGen/CGRecordLayoutBuilder.cpp | 2 +- clang/lib/CodeGen/CodeGenFunction.cpp | 1 - clang/lib/CodeGen/CodeGenModule.cpp | 1 - clang/lib/CodeGenUtils/CMakeLists.txt | 1 - clang/lib/CodeGenUtils/ClassUtils.cpp | 78 ++++++ clang/lib/CodeGenUtils/CodeGenUtils.cpp | 254 ------------------ clang/lib/CodeGenUtils/FunctionUtils.cpp | 120 +++++++++ clang/lib/CodeGenUtils/ModuleUtils.cpp | 37 +++ clang/lib/CodeGenUtils/TargetUtils.cpp | 5 + 22 files changed, 307 insertions(+), 341 deletions(-) delete mode 100644 clang/include/clang/CodeGenUtils/CodeGenUtils.h delete mode 100644 clang/lib/CodeGenUtils/CodeGenUtils.cpp diff --git a/clang/include/clang/CodeGenUtils/ClassUtils.h b/clang/include/clang/CodeGenUtils/ClassUtils.h index 63664ed737933..570c67110e120 100644 --- a/clang/include/clang/CodeGenUtils/ClassUtils.h +++ b/clang/include/clang/CodeGenUtils/ClassUtils.h @@ -24,6 +24,24 @@ namespace clang::CodeGenUtils { /// properly set. bool baseInitializerUsesThis(ASTContext &Ctx, const Expr *Init); +/// Check whether we need to initialize any vtable pointers before calling this +/// destructor. +bool canSkipVTablePointerInitialization(ASTContext &Ctx, + const CXXDestructorDecl *Dtor); + +/// Check whether destructing \p Field has no observable behaviors, and thus can +/// be skipped when creating a destructor body. So non-record types, anonymous +/// structs/unions, or record types where the destructor doesnt DO anything are +/// considered as this version of 'trivial'. +/// Note: This is a more liberal definition of trivial destruction than the C++ +/// Standard's version, and thus cannot be used as a substitute for C++ Standard +/// requirements. +bool fieldHasTrivialDestructorBody(ASTContext &Ctx, const FieldDecl *Field); + +/// Return whether \p BaseInit initializes a base class that is dynamic, that +/// is, one whose vtable pointer must be set before the initializer runs. +bool isInitializerOfDynamicClass(const CXXCtorInitializer *BaseInit); + } // namespace clang::CodeGenUtils #endif // LLVM_CLANG_CODEGENUTILS_CLASSUTILS_H diff --git a/clang/include/clang/CodeGenUtils/CodeGenUtils.h b/clang/include/clang/CodeGenUtils/CodeGenUtils.h deleted file mode 100644 index b2cc416330eb3..0000000000000 --- a/clang/include/clang/CodeGenUtils/CodeGenUtils.h +++ /dev/null @@ -1,73 +0,0 @@ -//===--- CodeGenUtils.h - Shared Classic CodeGen/CIR CodeGen Utils--C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_CODEGENUTILS_CODEGENUTILS_H -#define LLVM_CLANG_CODEGENUTILS_CODEGENUTILS_H - -#include "clang/AST/ASTContext.h" -#include "clang/Basic/SourceLocation.h" - -namespace clang { -class CallExpr; -class DiagnosticsEngine; -class FunctionDecl; -class LangOptions; -} // namespace clang - -namespace clang::CodeGenUtils { -/// Check whether we need to initialize any vtable pointers before calling this -/// destructor. -bool canSkipVTablePointerInitialization(ASTContext &Ctx, - const CXXDestructorDecl *Dtor); - -/// Check whether destructing \p Field has no observable behaviors, and thus can -/// be skipped when creating a destructor body. So non-record types, anonymous -/// structs/unions, or record types where the destructor doesnt DO anything are -/// considered as this version of 'trivial'. -/// Note: This is a more liberal definition of trivial destruction than the C++ -/// Standard's version, and thus cannot be used as a substitute for C++ Standard -/// requirements. -bool fieldHasTrivialDestructorBody(ASTContext &Context, const FieldDecl *Field); - -/// Determines whether the language options require us to model -/// unwind exceptions. We treat -fexceptions as mandating this -/// except under the fragile ObjC ABI with only ObjC exceptions -/// enabled. This means, for example, that C with -fexceptions -/// enables this. -bool hasUnwindExceptions(const LangOptions &LangOpts); - -/// Helper method to check if the underlying ABI is AAPCS -bool isAAPCS(const TargetInfo &TargetInfo); - -/// Return the AST address space of constant literal, which is used to emit -/// the constant literal as global variable in LLVM IR. -/// Note: This is not necessarily the address space of the constant literal -/// in AST. For address space agnostic language, e.g. C++, constant literal -/// in AST is always in default address space. -LangAS getGlobalConstantAddressSpace(const LangOptions &LangOpts, - const TargetInfo &Target); - -bool isInitializerOfDynamicClass(const CXXCtorInitializer *BaseInit); - -/// Check that a call to a target-specific builtin has the required target -/// features enabled in the caller, emitting an error diagnostic if not. -/// \p caller is the FunctionDecl of the enclosing function (may be null). -void checkTargetFeatures(ASTContext &Ctx, DiagnosticsEngine &Diags, - const LangOptions &LangOpts, const CallExpr *E, - const FunctionDecl *Caller, - const FunctionDecl *TargetDecl); - -/// Overload taking a raw source location instead of a CallExpr. -void checkTargetFeatures(ASTContext &Ctx, DiagnosticsEngine &Diags, - const LangOptions &LangOpts, SourceLocation Loc, - const FunctionDecl *Caller, - const FunctionDecl *TargetDecl); - -} // namespace clang::CodeGenUtils - -#endif // LLVM_CLANG_CODEGENUTILS_CODEGENUTILS_H diff --git a/clang/include/clang/CodeGenUtils/FunctionUtils.h b/clang/include/clang/CodeGenUtils/FunctionUtils.h index f924874d829e0..4e500aa8d3c0b 100644 --- a/clang/include/clang/CodeGenUtils/FunctionUtils.h +++ b/clang/include/clang/CodeGenUtils/FunctionUtils.h @@ -6,8 +6,8 @@ // //===----------------------------------------------------------------------===// // -// This file holds the queries that both classic CodeGen and CIR CodeGen need -// while emitting a function body. +// This file holds the queries and checks that both classic CodeGen and CIR +// CodeGen need while emitting a function body. // //===----------------------------------------------------------------------===// @@ -16,6 +16,14 @@ #include "clang/Basic/CodeGenOptions.h" #include "clang/Basic/LangOptions.h" +#include "clang/Basic/SourceLocation.h" + +namespace clang { +class ASTContext; +class CallExpr; +class DiagnosticsEngine; +class FunctionDecl; +} // namespace clang namespace clang::CodeGenUtils { @@ -23,6 +31,20 @@ namespace clang::CodeGenUtils { bool shouldEmitLifetimeMarkers(const CodeGenOptions &CGOpts, const LangOptions &LangOpts); +/// Check that a call to a target-specific builtin has the required target +/// features enabled in the caller, emitting an error diagnostic if not. +/// \p Caller is the FunctionDecl of the enclosing function (may be null). +void checkTargetFeatures(ASTContext &Ctx, DiagnosticsEngine &Diags, + const LangOptions &LangOpts, const CallExpr *E, + const FunctionDecl *Caller, + const FunctionDecl *TargetDecl); + +/// Overload taking a raw source location instead of a CallExpr. +void checkTargetFeatures(ASTContext &Ctx, DiagnosticsEngine &Diags, + const LangOptions &LangOpts, SourceLocation Loc, + const FunctionDecl *Caller, + const FunctionDecl *TargetDecl); + } // namespace clang::CodeGenUtils #endif // LLVM_CLANG_CODEGENUTILS_FUNCTIONUTILS_H diff --git a/clang/include/clang/CodeGenUtils/ModuleUtils.h b/clang/include/clang/CodeGenUtils/ModuleUtils.h index 5c7423ad4fac5..10b39438567db 100644 --- a/clang/include/clang/CodeGenUtils/ModuleUtils.h +++ b/clang/include/clang/CodeGenUtils/ModuleUtils.h @@ -19,6 +19,21 @@ namespace clang::CodeGenUtils { +/// Determines whether the language options require us to model +/// unwind exceptions. We treat -fexceptions as mandating this +/// except under the fragile ObjC ABI with only ObjC exceptions +/// enabled. This means, for example, that C with -fexceptions +/// enables this. +/// Return the AST address space of constant literal, which is used to emit +/// the constant literal as global variable in LLVM IR. +/// Note: This is not necessarily the address space of the constant literal +/// in AST. For address space agnostic language, e.g. C++, constant literal +/// in AST is always in default address space. +LangAS getGlobalConstantAddressSpace(const LangOptions &LangOpts, + const TargetInfo &Target); + +bool hasUnwindExceptions(const LangOptions &LangOpts); + /// Check whether \p D is a strong definition, and thus must not be given /// common linkage. \p NoCommon reflects -fno-common. bool isVarDeclStrongDefinition(const ASTContext &Ctx, const VarDecl *D, diff --git a/clang/include/clang/CodeGenUtils/TargetUtils.h b/clang/include/clang/CodeGenUtils/TargetUtils.h index a44541c572590..2c812c6a2b617 100644 --- a/clang/include/clang/CodeGenUtils/TargetUtils.h +++ b/clang/include/clang/CodeGenUtils/TargetUtils.h @@ -21,6 +21,7 @@ namespace clang { class Decl; class FunctionDecl; +class TargetInfo; } // namespace clang namespace clang::CodeGenUtils { @@ -53,6 +54,9 @@ ArmSMEInlinability getArmSMEInlinability(const FunctionDecl *Caller, /// TODO: Make this return false for SISD builtins. bool hasExtraNeonArgument(unsigned BuiltinID); +/// Helper method to check if the underlying ABI is AAPCS +bool isAAPCS(const TargetInfo &TargetInfo); + /// Returns whether \p D must be given protected visibility on AMDGPU. /// \p HasHiddenVisibility is whether the emitted global currently has hidden /// visibility. diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp index c47165ec153a7..834f583d1e38a 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp @@ -26,7 +26,7 @@ #include "clang/Basic/OperatorKinds.h" #include "clang/CIR/Dialect/IR/CIRTypes.h" #include "clang/CIR/MissingFeatures.h" -#include "clang/CodeGenUtils/CodeGenUtils.h" +#include "clang/CodeGenUtils/FunctionUtils.h" #include "llvm/ADT/STLExtras.h" #include "llvm/IR/Intrinsics.h" #include "llvm/Support/ErrorHandling.h" diff --git a/clang/lib/CIR/CodeGen/CIRGenClass.cpp b/clang/lib/CIR/CodeGen/CIRGenClass.cpp index 28db1763fd3cb..9633b915bd669 100644 --- a/clang/lib/CIR/CodeGen/CIRGenClass.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenClass.cpp @@ -20,7 +20,6 @@ #include "clang/CIR/Dialect/IR/CIRDialect.h" #include "clang/CIR/MissingFeatures.h" #include "clang/CodeGenUtils/ClassUtils.h" -#include "clang/CodeGenUtils/CodeGenUtils.h" using namespace clang; using namespace clang::CIRGen; diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp index 51533f9dc0f9c..8ff4114040883 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -30,9 +30,9 @@ #include "clang/CIR/Dialect/IR/CIRDialect.h" #include "clang/CIR/Dialect/IR/CIRTypes.h" #include "clang/CIR/MissingFeatures.h" -#include "clang/CodeGenUtils/CodeGenUtils.h" #include "clang/CodeGenUtils/ExprUtils.h" #include "clang/CodeGenUtils/RecordLayoutUtils.h" +#include "clang/CodeGenUtils/TargetUtils.h" #include <optional> using namespace clang; diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp index 382a0008113e2..c6f3fc22a776b 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp @@ -21,7 +21,7 @@ #include "clang/AST/GlobalDecl.h" #include "clang/CIR/Dialect/IR/CIRDialect.h" #include "clang/CIR/MissingFeatures.h" -#include "clang/CodeGenUtils/CodeGenUtils.h" +#include "clang/CodeGenUtils/ClassUtils.h" #include "clang/CodeGenUtils/FunctionUtils.h" #include "llvm/ADT/ScopeExit.h" #include "llvm/IR/FPEnv.h" diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index 92c8af433aace..71fcd4f274e95 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -36,7 +36,6 @@ #include "clang/CIR/Dialect/IR/CIRTypes.h" #include "clang/CIR/Interfaces/CIROpInterfaces.h" #include "clang/CIR/MissingFeatures.h" -#include "clang/CodeGenUtils/CodeGenUtils.h" #include "clang/CodeGenUtils/ModuleUtils.h" #include "clang/CodeGenUtils/RecordLayoutUtils.h" #include "llvm/ADT/STLExtras.h" diff --git a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp index 4bb483fc57a96..7ecb316efecb5 100644 --- a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp @@ -22,8 +22,8 @@ #include "clang/CIR/Dialect/IR/CIRAttrs.h" #include "clang/CIR/Dialect/IR/CIRDataLayout.h" #include "clang/CIR/MissingFeatures.h" -#include "clang/CodeGenUtils/CodeGenUtils.h" #include "clang/CodeGenUtils/RecordLayoutUtils.h" +#include "clang/CodeGenUtils/TargetUtils.h" #include "llvm/Support/Casting.h" #include <memory> diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp index 7e832f8155aa7..2901233638ffe 100644 --- a/clang/lib/CodeGen/CGClass.cpp +++ b/clang/lib/CodeGen/CGClass.cpp @@ -26,7 +26,6 @@ #include "clang/Basic/CodeGenOptions.h" #include "clang/CodeGen/CGFunctionInfo.h" #include "clang/CodeGenUtils/ClassUtils.h" -#include "clang/CodeGenUtils/CodeGenUtils.h" #include "clang/CodeGenUtils/RecordLayoutUtils.h" #include "llvm/IR/Intrinsics.h" #include "llvm/IR/Metadata.h" diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 8323a170a1dbb..7f72da4f0e478 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -39,9 +39,9 @@ #include "clang/Basic/CodeGenOptions.h" #include "clang/Basic/Module.h" #include "clang/Basic/SourceManager.h" -#include "clang/CodeGenUtils/CodeGenUtils.h" #include "clang/CodeGenUtils/ExprUtils.h" #include "clang/CodeGenUtils/RecordLayoutUtils.h" +#include "clang/CodeGenUtils/TargetUtils.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/StringExtras.h" diff --git a/clang/lib/CodeGen/CGRecordLayoutBuilder.cpp b/clang/lib/CodeGen/CGRecordLayoutBuilder.cpp index 300fd521e36cf..6e7df1395350c 100644 --- a/clang/lib/CodeGen/CGRecordLayoutBuilder.cpp +++ b/clang/lib/CodeGen/CGRecordLayoutBuilder.cpp @@ -21,8 +21,8 @@ #include "clang/AST/Expr.h" #include "clang/AST/RecordLayout.h" #include "clang/Basic/CodeGenOptions.h" -#include "clang/CodeGenUtils/CodeGenUtils.h" #include "clang/CodeGenUtils/RecordLayoutUtils.h" +#include "clang/CodeGenUtils/TargetUtils.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/Type.h" diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp index 26d8d73e1eb2f..3eedb12e7d4bd 100644 --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -36,7 +36,6 @@ #include "clang/Basic/TargetBuiltins.h" #include "clang/Basic/TargetInfo.h" #include "clang/CodeGen/CGFunctionInfo.h" -#include "clang/CodeGenUtils/CodeGenUtils.h" #include "clang/CodeGenUtils/FunctionUtils.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/ScopeExit.h" diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index b4ebc6b6abff0..fdac5cc858893 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -48,7 +48,6 @@ #include "clang/Basic/Version.h" #include "clang/CodeGen/BackendUtil.h" #include "clang/CodeGen/ConstantInitBuilder.h" -#include "clang/CodeGenUtils/CodeGenUtils.h" #include "clang/CodeGenUtils/ModuleUtils.h" #include "clang/Lex/Preprocessor.h" #include "llvm/ABI/IRTypeMapper.h" diff --git a/clang/lib/CodeGenUtils/CMakeLists.txt b/clang/lib/CodeGenUtils/CMakeLists.txt index 7fb94a994bfed..dc701f8dbc72f 100644 --- a/clang/lib/CodeGenUtils/CMakeLists.txt +++ b/clang/lib/CodeGenUtils/CMakeLists.txt @@ -9,7 +9,6 @@ add_clang_library(clangCodeGenUtils BackendDiagnosticHandler.cpp CallUtils.cpp ClassUtils.cpp - CodeGenUtils.cpp EHPersonality.cpp ExprUtils.cpp FunctionUtils.cpp diff --git a/clang/lib/CodeGenUtils/ClassUtils.cpp b/clang/lib/CodeGenUtils/ClassUtils.cpp index 90bab38280c30..5a504ca0b5beb 100644 --- a/clang/lib/CodeGenUtils/ClassUtils.cpp +++ b/clang/lib/CodeGenUtils/ClassUtils.cpp @@ -36,4 +36,82 @@ bool baseInitializerUsesThis(ASTContext &Ctx, const Expr *Init) { return Checker.UsesThis; } +static bool +hasTrivialDestructorBody(ASTContext &Ctx, const CXXRecordDecl *BaseClassDecl, + const CXXRecordDecl *MostDerivedClassDecl) { + // If the destructor is trivial we don't have to check anything else. + if (BaseClassDecl->hasTrivialDestructor()) + return true; + + if (!BaseClassDecl->getDestructor()->hasTrivialBody()) + return false; + + // Check fields. + for (const auto *Field : BaseClassDecl->fields()) + if (!fieldHasTrivialDestructorBody(Ctx, Field)) + return false; + + // Check non-virtual bases. + for (const auto &I : BaseClassDecl->bases()) { + if (I.isVirtual()) + continue; + + const auto *NonVirtualBase = I.getType()->castAsCXXRecordDecl(); + if (!hasTrivialDestructorBody(Ctx, NonVirtualBase, MostDerivedClassDecl)) + return false; + } + + if (BaseClassDecl == MostDerivedClassDecl) { + // Check virtual bases. + for (const auto &I : BaseClassDecl->vbases()) { + const auto *VirtualBase = I.getType()->castAsCXXRecordDecl(); + if (!hasTrivialDestructorBody(Ctx, VirtualBase, MostDerivedClassDecl)) + return false; + } + } + + return true; +} + +bool fieldHasTrivialDestructorBody(ASTContext &Ctx, const FieldDecl *Field) { + QualType FieldBaseElementType = Ctx.getBaseElementType(Field->getType()); + + auto *FieldClassDecl = FieldBaseElementType->getAsCXXRecordDecl(); + if (!FieldClassDecl) + return true; + + // The destructor for an implicit anonymous union member is never invoked. + if (FieldClassDecl->isUnion() && FieldClassDecl->isAnonymousStructOrUnion()) + return true; + + return hasTrivialDestructorBody(Ctx, FieldClassDecl, FieldClassDecl); +} + +bool canSkipVTablePointerInitialization(ASTContext &Ctx, + const CXXDestructorDecl *Dtor) { + const CXXRecordDecl *ClassDecl = Dtor->getParent(); + if (!ClassDecl->isDynamicClass()) + return true; + + // For a final class, the vtable pointer is known to already point to the + // class's vtable. + if (ClassDecl->isEffectivelyFinal()) + return true; + + if (!Dtor->hasTrivialBody()) + return false; + + // Check the fields. + for (const auto *Field : ClassDecl->fields()) + if (!fieldHasTrivialDestructorBody(Ctx, Field)) + return false; + + return true; +} + +bool isInitializerOfDynamicClass(const CXXCtorInitializer *BaseInit) { + const Type *BaseType = BaseInit->getBaseClass(); + return BaseType->castAsCXXRecordDecl()->isDynamicClass(); +} + } // namespace clang::CodeGenUtils diff --git a/clang/lib/CodeGenUtils/CodeGenUtils.cpp b/clang/lib/CodeGenUtils/CodeGenUtils.cpp deleted file mode 100644 index 11afe31ae89b0..0000000000000 --- a/clang/lib/CodeGenUtils/CodeGenUtils.cpp +++ /dev/null @@ -1,254 +0,0 @@ -//==--- CodeGenUtils.cpp - Shared Classic CodeGen/CIR CodeGen Utils--C++ -*-==// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "clang/CodeGenUtils/CodeGenUtils.h" -#include "clang/AST/Attr.h" -#include "clang/AST/Expr.h" -#include "clang/Basic/Builtins.h" -#include "clang/Basic/DiagnosticFrontend.h" -#include "clang/Basic/TargetBuiltins.h" -#include "clang/Basic/TargetInfo.h" -#include "llvm/ADT/StringMap.h" - -namespace clang::CodeGenUtils { -static bool -hasTrivialDestructorBody(ASTContext &Context, - const CXXRecordDecl *BaseClassDecl, - const CXXRecordDecl *MostDerivedClassDecl) { - // If the destructor is trivial we don't have to check anything else. - if (BaseClassDecl->hasTrivialDestructor()) - return true; - - if (!BaseClassDecl->getDestructor()->hasTrivialBody()) - return false; - - // Check fields. - for (const auto *Field : BaseClassDecl->fields()) - if (!fieldHasTrivialDestructorBody(Context, Field)) - return false; - - // Check non-virtual bases. - for (const auto &I : BaseClassDecl->bases()) { - if (I.isVirtual()) - continue; - - const auto *NonVirtualBase = I.getType()->castAsCXXRecordDecl(); - if (!hasTrivialDestructorBody(Context, NonVirtualBase, - MostDerivedClassDecl)) - return false; - } - - if (BaseClassDecl == MostDerivedClassDecl) { - // Check virtual bases. - for (const auto &I : BaseClassDecl->vbases()) { - const auto *VirtualBase = I.getType()->castAsCXXRecordDecl(); - if (!hasTrivialDestructorBody(Context, VirtualBase, MostDerivedClassDecl)) - return false; - } - } - - return true; -} - -bool fieldHasTrivialDestructorBody(ASTContext &Context, - const FieldDecl *Field) { - QualType FieldBaseElementType = Context.getBaseElementType(Field->getType()); - - auto *FieldClassDecl = FieldBaseElementType->getAsCXXRecordDecl(); - if (!FieldClassDecl) - return true; - - // The destructor for an implicit anonymous union member is never invoked. - if (FieldClassDecl->isUnion() && FieldClassDecl->isAnonymousStructOrUnion()) - return true; - - return hasTrivialDestructorBody(Context, FieldClassDecl, FieldClassDecl); -} - -/// Check whether we need to initialize any vtable pointers before calling this -/// destructor. -bool canSkipVTablePointerInitialization(ASTContext &Ctx, - const CXXDestructorDecl *Dtor) { - const CXXRecordDecl *ClassDecl = Dtor->getParent(); - if (!ClassDecl->isDynamicClass()) - return true; - - // For a final class, the vtable pointer is known to already point to the - // class's vtable. - if (ClassDecl->isEffectivelyFinal()) - return true; - - if (!Dtor->hasTrivialBody()) - return false; - - // Check the fields. - for (const auto *Field : ClassDecl->fields()) - if (!fieldHasTrivialDestructorBody(Ctx, Field)) - return false; - - return true; -} -bool hasUnwindExceptions(const LangOptions &LangOpts) { - // If exceptions are completely disabled, obviously this is false. - if (!LangOpts.Exceptions) - return false; - - // If C++ exceptions are enabled, this is true. - if (LangOpts.CXXExceptions) - return true; - - // If ObjC exceptions are enabled, this depends on the ABI. - if (LangOpts.ObjCExceptions) { - return LangOpts.ObjCRuntime.hasUnwindExceptions(); - } - - return true; -} - -bool isAAPCS(const TargetInfo &TargetInfo) { - return TargetInfo.getABI().starts_with("aapcs"); -} - -LangAS getGlobalConstantAddressSpace(const LangOptions &LangOpts, - const TargetInfo &Target) { - // OpenCL v1.2 s6.5.3: a string literal is in the constant address space. - if (LangOpts.OpenCL) - return LangAS::opencl_constant; - if (LangOpts.SYCLIsDevice) - return LangAS::sycl_global; - if (LangOpts.HIP && LangOpts.CUDAIsDevice && Target.getTriple().isSPIRV()) - // For HIPSPV map literals to cuda_device (maps to CrossWorkGroup in SPIR-V) - // instead of default AS (maps to Generic in SPIR-V). Otherwise, we end up - // with OpVariable instructions with Generic storage class which is not - // allowed (SPIR-V V1.6 s3.42.8). Also, mapping literals to SPIR-V - // UniformConstant storage class is not viable as pointers to it may not be - // casted to Generic pointers which are used to model HIP's "flat" pointers. - return LangAS::cuda_device; - if (auto AS = Target.getConstantAddressSpace()) - return *AS; - return LangAS::Default; -} - -bool isInitializerOfDynamicClass(const CXXCtorInitializer *BaseInit) { - const Type *BaseType = BaseInit->getBaseClass(); - return BaseType->castAsCXXRecordDecl()->isDynamicClass(); -} - -// Emits an error if we don't have a valid set of target features for the -// called function. -void checkTargetFeatures(ASTContext &Ctx, DiagnosticsEngine &Diags, - const LangOptions &LangOpts, const CallExpr *E, - const FunctionDecl *Caller, - const FunctionDecl *TargetDecl) { - // SemaChecking cannot handle these x86 builtins because they have different - // parameter ranges depending on the caller's TargetAttribute. - if (Ctx.getTargetInfo().getTriple().isX86()) { - unsigned BuiltinID = TargetDecl->getBuiltinID(); - if (BuiltinID == X86::BI__builtin_ia32_cmpps || - BuiltinID == X86::BI__builtin_ia32_cmpss || - BuiltinID == X86::BI__builtin_ia32_cmppd || - BuiltinID == X86::BI__builtin_ia32_cmpsd) { - llvm::StringMap<bool> TargetFeatureMap; - Ctx.getFunctionFeatureMap(TargetFeatureMap, Caller); - llvm::APSInt Result = *(E->getArg(2)->getIntegerConstantExpr(Ctx)); - if (Result.getSExtValue() > 7 && !TargetFeatureMap.lookup("avx")) - Diags.Report(E->getBeginLoc(), diag::err_builtin_needs_feature) - << TargetDecl->getDeclName() << "avx"; - } - } - checkTargetFeatures(Ctx, Diags, LangOpts, E->getBeginLoc(), Caller, - TargetDecl); -} - -// Emits an error if we don't have a valid set of target features for the -// called function. -void checkTargetFeatures(ASTContext &Ctx, DiagnosticsEngine &Diags, - const LangOptions &LangOpts, SourceLocation Loc, - const FunctionDecl *Caller, - const FunctionDecl *TargetDecl) { - if (!TargetDecl || !Caller) - return; - - bool IsAlwaysInline = TargetDecl->hasAttr<AlwaysInlineAttr>(); - bool IsFlatten = Caller->hasAttr<FlattenAttr>(); - - unsigned BuiltinID = TargetDecl->getBuiltinID(); - std::string MissingFeature; - llvm::StringMap<bool> CallerFeatureMap; - Ctx.getFunctionFeatureMap(CallerFeatureMap, Caller); - // When compiling in HipStdPar mode we have to be conservative in rejecting - // target specific features in the FE, and defer the possible error to the - // AcceleratorCodeSelection pass, wherein iff an unsupported target builtin is - // referenced by an accelerator executable function, we emit an error. - bool IsHipStdPar = LangOpts.HIPStdPar && LangOpts.CUDAIsDevice; - if (BuiltinID) { - StringRef FeatureList(Ctx.BuiltinInfo.getRequiredFeatures(BuiltinID)); - if (!Builtin::evaluateRequiredTargetFeatures(FeatureList, - CallerFeatureMap) && - !IsHipStdPar) - Diags.Report(Loc, diag::err_builtin_needs_feature) - << TargetDecl->getDeclName() << FeatureList; - } else if (!TargetDecl->isMultiVersion() && - TargetDecl->hasAttr<TargetAttr>()) { - // Get the required features for the callee. - const TargetAttr *TD = TargetDecl->getAttr<TargetAttr>(); - ParsedTargetAttr ParsedAttr = Ctx.filterFunctionTargetAttrs(TD); - - SmallVector<StringRef, 1> ReqFeatures; - llvm::StringMap<bool> CalleeFeatureMap; - Ctx.getFunctionFeatureMap(CalleeFeatureMap, TargetDecl); - - for (const auto &F : ParsedAttr.Features) { - if (F[0] == '+' && CalleeFeatureMap.lookup(F.substr(1))) - ReqFeatures.push_back(StringRef(F).substr(1)); - } - for (const auto &F : CalleeFeatureMap) { - if (F.getValue()) - ReqFeatures.push_back(F.getKey()); - } - if (!llvm::all_of(ReqFeatures, - [&](StringRef Feature) { - if (!CallerFeatureMap.lookup(Feature)) { - MissingFeature = Feature.str(); - return false; - } - return true; - }) && - !IsHipStdPar) { - if (IsAlwaysInline) - Diags.Report(Loc, diag::err_function_needs_feature) - << Caller->getDeclName() << TargetDecl->getDeclName() - << MissingFeature; - else if (IsFlatten) - Diags.Report(Loc, diag::err_flatten_function_needs_feature) - << Caller->getDeclName() << TargetDecl->getDeclName() - << MissingFeature; - } - } else if (!Caller->isMultiVersion() && Caller->hasAttr<TargetAttr>()) { - llvm::StringMap<bool> CalleeFeatureMap; - Ctx.getFunctionFeatureMap(CalleeFeatureMap, TargetDecl); - - for (const auto &F : CalleeFeatureMap) { - if (F.getValue() && - (!CallerFeatureMap.lookup(F.getKey()) || - !CallerFeatureMap.find(F.getKey())->getValue()) && - !IsHipStdPar) { - if (IsAlwaysInline) - Diags.Report(Loc, diag::err_function_needs_feature) - << Caller->getDeclName() << TargetDecl->getDeclName() - << F.getKey(); - else if (IsFlatten) - Diags.Report(Loc, diag::err_flatten_function_needs_feature) - << Caller->getDeclName() << TargetDecl->getDeclName() - << F.getKey(); - } - } - } -} - -} // namespace clang::CodeGenUtils diff --git a/clang/lib/CodeGenUtils/FunctionUtils.cpp b/clang/lib/CodeGenUtils/FunctionUtils.cpp index 9f01745abbf82..083c04d30bec4 100644 --- a/clang/lib/CodeGenUtils/FunctionUtils.cpp +++ b/clang/lib/CodeGenUtils/FunctionUtils.cpp @@ -7,6 +7,14 @@ //===----------------------------------------------------------------------===// #include "clang/CodeGenUtils/FunctionUtils.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Attr.h" +#include "clang/AST/Expr.h" +#include "clang/Basic/Builtins.h" +#include "clang/Basic/DiagnosticFrontend.h" +#include "clang/Basic/TargetBuiltins.h" +#include "clang/Basic/TargetInfo.h" +#include "llvm/ADT/StringMap.h" namespace clang::CodeGenUtils { @@ -26,4 +34,116 @@ bool shouldEmitLifetimeMarkers(const CodeGenOptions &CGOpts, return CGOpts.OptimizationLevel != 0; } +// Emits an error if we don't have a valid set of target features for the +// called function. +void checkTargetFeatures(ASTContext &Ctx, DiagnosticsEngine &Diags, + const LangOptions &LangOpts, const CallExpr *E, + const FunctionDecl *Caller, + const FunctionDecl *TargetDecl) { + // SemaChecking cannot handle these x86 builtins because they have different + // parameter ranges depending on the caller's TargetAttribute. + if (Ctx.getTargetInfo().getTriple().isX86()) { + unsigned BuiltinID = TargetDecl->getBuiltinID(); + if (BuiltinID == X86::BI__builtin_ia32_cmpps || + BuiltinID == X86::BI__builtin_ia32_cmpss || + BuiltinID == X86::BI__builtin_ia32_cmppd || + BuiltinID == X86::BI__builtin_ia32_cmpsd) { + llvm::StringMap<bool> TargetFeatureMap; + Ctx.getFunctionFeatureMap(TargetFeatureMap, Caller); + llvm::APSInt Result = *(E->getArg(2)->getIntegerConstantExpr(Ctx)); + if (Result.getSExtValue() > 7 && !TargetFeatureMap.lookup("avx")) + Diags.Report(E->getBeginLoc(), diag::err_builtin_needs_feature) + << TargetDecl->getDeclName() << "avx"; + } + } + checkTargetFeatures(Ctx, Diags, LangOpts, E->getBeginLoc(), Caller, + TargetDecl); +} + +// Emits an error if we don't have a valid set of target features for the +// called function. +void checkTargetFeatures(ASTContext &Ctx, DiagnosticsEngine &Diags, + const LangOptions &LangOpts, SourceLocation Loc, + const FunctionDecl *Caller, + const FunctionDecl *TargetDecl) { + if (!TargetDecl || !Caller) + return; + + bool IsAlwaysInline = TargetDecl->hasAttr<AlwaysInlineAttr>(); + bool IsFlatten = Caller->hasAttr<FlattenAttr>(); + + unsigned BuiltinID = TargetDecl->getBuiltinID(); + std::string MissingFeature; + llvm::StringMap<bool> CallerFeatureMap; + Ctx.getFunctionFeatureMap(CallerFeatureMap, Caller); + // When compiling in HipStdPar mode we have to be conservative in rejecting + // target specific features in the FE, and defer the possible error to the + // AcceleratorCodeSelection pass, wherein iff an unsupported target builtin is + // referenced by an accelerator executable function, we emit an error. + bool IsHipStdPar = LangOpts.HIPStdPar && LangOpts.CUDAIsDevice; + if (BuiltinID) { + StringRef FeatureList(Ctx.BuiltinInfo.getRequiredFeatures(BuiltinID)); + if (!Builtin::evaluateRequiredTargetFeatures(FeatureList, + CallerFeatureMap) && + !IsHipStdPar) + Diags.Report(Loc, diag::err_builtin_needs_feature) + << TargetDecl->getDeclName() << FeatureList; + } else if (!TargetDecl->isMultiVersion() && + TargetDecl->hasAttr<TargetAttr>()) { + // Get the required features for the callee. + const TargetAttr *TD = TargetDecl->getAttr<TargetAttr>(); + ParsedTargetAttr ParsedAttr = Ctx.filterFunctionTargetAttrs(TD); + + SmallVector<StringRef, 1> ReqFeatures; + llvm::StringMap<bool> CalleeFeatureMap; + Ctx.getFunctionFeatureMap(CalleeFeatureMap, TargetDecl); + + for (const auto &F : ParsedAttr.Features) { + if (F[0] == '+' && CalleeFeatureMap.lookup(F.substr(1))) + ReqFeatures.push_back(StringRef(F).substr(1)); + } + for (const auto &F : CalleeFeatureMap) { + if (F.getValue()) + ReqFeatures.push_back(F.getKey()); + } + if (!llvm::all_of(ReqFeatures, + [&](StringRef Feature) { + if (!CallerFeatureMap.lookup(Feature)) { + MissingFeature = Feature.str(); + return false; + } + return true; + }) && + !IsHipStdPar) { + if (IsAlwaysInline) + Diags.Report(Loc, diag::err_function_needs_feature) + << Caller->getDeclName() << TargetDecl->getDeclName() + << MissingFeature; + else if (IsFlatten) + Diags.Report(Loc, diag::err_flatten_function_needs_feature) + << Caller->getDeclName() << TargetDecl->getDeclName() + << MissingFeature; + } + } else if (!Caller->isMultiVersion() && Caller->hasAttr<TargetAttr>()) { + llvm::StringMap<bool> CalleeFeatureMap; + Ctx.getFunctionFeatureMap(CalleeFeatureMap, TargetDecl); + + for (const auto &F : CalleeFeatureMap) { + if (F.getValue() && + (!CallerFeatureMap.lookup(F.getKey()) || + !CallerFeatureMap.find(F.getKey())->getValue()) && + !IsHipStdPar) { + if (IsAlwaysInline) + Diags.Report(Loc, diag::err_function_needs_feature) + << Caller->getDeclName() << TargetDecl->getDeclName() + << F.getKey(); + else if (IsFlatten) + Diags.Report(Loc, diag::err_flatten_function_needs_feature) + << Caller->getDeclName() << TargetDecl->getDeclName() + << F.getKey(); + } + } + } +} + } // namespace clang::CodeGenUtils diff --git a/clang/lib/CodeGenUtils/ModuleUtils.cpp b/clang/lib/CodeGenUtils/ModuleUtils.cpp index 0d4882345e303..93209970501c6 100644 --- a/clang/lib/CodeGenUtils/ModuleUtils.cpp +++ b/clang/lib/CodeGenUtils/ModuleUtils.cpp @@ -12,6 +12,43 @@ namespace clang::CodeGenUtils { +LangAS getGlobalConstantAddressSpace(const LangOptions &LangOpts, + const TargetInfo &Target) { + // OpenCL v1.2 s6.5.3: a string literal is in the constant address space. + if (LangOpts.OpenCL) + return LangAS::opencl_constant; + if (LangOpts.SYCLIsDevice) + return LangAS::sycl_global; + if (LangOpts.HIP && LangOpts.CUDAIsDevice && Target.getTriple().isSPIRV()) + // For HIPSPV map literals to cuda_device (maps to CrossWorkGroup in SPIR-V) + // instead of default AS (maps to Generic in SPIR-V). Otherwise, we end up + // with OpVariable instructions with Generic storage class which is not + // allowed (SPIR-V V1.6 s3.42.8). Also, mapping literals to SPIR-V + // UniformConstant storage class is not viable as pointers to it may not be + // casted to Generic pointers which are used to model HIP's "flat" pointers. + return LangAS::cuda_device; + if (auto AS = Target.getConstantAddressSpace()) + return *AS; + return LangAS::Default; +} + +bool hasUnwindExceptions(const LangOptions &LangOpts) { + // If exceptions are completely disabled, obviously this is false. + if (!LangOpts.Exceptions) + return false; + + // If C++ exceptions are enabled, this is true. + if (LangOpts.CXXExceptions) + return true; + + // If ObjC exceptions are enabled, this depends on the ABI. + if (LangOpts.ObjCExceptions) { + return LangOpts.ObjCRuntime.hasUnwindExceptions(); + } + + return true; +} + bool isVarDeclStrongDefinition(const ASTContext &Ctx, const VarDecl *D, bool NoCommon) { // Don't give variables common linkage if -fno-common was specified unless it diff --git a/clang/lib/CodeGenUtils/TargetUtils.cpp b/clang/lib/CodeGenUtils/TargetUtils.cpp index f72218f4dda2d..86ebb29410d10 100644 --- a/clang/lib/CodeGenUtils/TargetUtils.cpp +++ b/clang/lib/CodeGenUtils/TargetUtils.cpp @@ -10,6 +10,7 @@ #include "clang/AST/Attr.h" #include "clang/AST/Decl.h" #include "clang/Basic/TargetBuiltins.h" +#include "clang/Basic/TargetInfo.h" namespace clang::CodeGenUtils { @@ -72,6 +73,10 @@ bool hasExtraNeonArgument(unsigned BuiltinID) { return mask != 0; } +bool isAAPCS(const TargetInfo &TargetInfo) { + return TargetInfo.getABI().starts_with("aapcs"); +} + bool requiresAMDGPUProtectedVisibility(const Decl *D, bool HasHiddenVisibility) { if (!HasHiddenVisibility) _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
