[clang] [NFCI] Extract out some functions into CodeGenUtils. (PR #221001)

Erich Keane via cfe-commits cfe-commits at lists.llvm.org
Thu Sep 3 14:09:56 PDT 2026


https://github.com/erichkeane updated https://github.com/llvm/llvm-project/pull/221001

>From fe6f4b59a4d872a34965a8fefb9925e8fb450702 Mon Sep 17 00:00:00 2001
From: erichkeane <ekeane at nvidia.com>
Date: Thu, 3 Sep 2026 09:50:02 -0700
Subject: [PATCH 1/4] [NFCI] Extract out some functions into CodeGenUtils.

I need to use these for CIR (see #220946). Also, its been a long-time
coming that we had a way to share stuff between both CodeGens.

This patch does (hopefully) the infrastructure work to get this running
right.

I asked Claude to help me with the CMake files, and best I can tell it
looks right?  But if someone knows better, please let me know. I suspect
we might need to add dependencies along the way as we add new things,
but this seems to work at the moment?

Otherwise, this just moves a handful of trivial utilities that seemed
like a good starting point, plus one I need for CIR right away.
---
 .../include/clang/CodeGenUtils/CodeGenUtils.h |  33 +++++
 clang/lib/CIR/CodeGen/CIRGenClass.cpp         |   9 +-
 clang/lib/CIR/CodeGen/CIRGenExpr.cpp          |  15 +--
 clang/lib/CIR/CodeGen/CIRGenModule.cpp        |  21 +---
 .../CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp |   7 +-
 clang/lib/CIR/CodeGen/CMakeLists.txt          |   1 +
 clang/lib/CMakeLists.txt                      |   1 +
 clang/lib/CodeGen/CGClass.cpp                 |  94 +-------------
 clang/lib/CodeGen/CGExpr.cpp                  |  15 +--
 clang/lib/CodeGen/CGRecordLayoutBuilder.cpp   |   9 +-
 clang/lib/CodeGen/CMakeLists.txt              |   1 +
 clang/lib/CodeGen/CodeGenModule.cpp           |  23 +---
 clang/lib/CodeGenUtils/CMakeLists.txt         |  11 ++
 clang/lib/CodeGenUtils/CodeGenUtils.cpp       | 115 ++++++++++++++++++
 14 files changed, 189 insertions(+), 166 deletions(-)
 create mode 100644 clang/include/clang/CodeGenUtils/CodeGenUtils.h
 create mode 100644 clang/lib/CodeGenUtils/CMakeLists.txt
 create mode 100644 clang/lib/CodeGenUtils/CodeGenUtils.cpp

diff --git a/clang/include/clang/CodeGenUtils/CodeGenUtils.h b/clang/include/clang/CodeGenUtils/CodeGenUtils.h
new file mode 100644
index 0000000000000..9a93281c3689d
--- /dev/null
+++ b/clang/include/clang/CodeGenUtils/CodeGenUtils.h
@@ -0,0 +1,33 @@
+//===--- 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"
+
+namespace clang::CodeGenUtils {
+/// Check whether we need to initialize any vtable pointers before calling this
+/// destructor.
+bool CanSkipVTablePointerInitialization(ASTContext &Ctx,
+                                        const CXXDestructorDecl *Dtor);
+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);
+
+bool isInitializerOfDynamicClass(const CXXCtorInitializer *BaseInit);
+}
+
+#endif // LLVM_CLANG_CODEGENUTILS_CODEGENUTILS_H
diff --git a/clang/lib/CIR/CodeGen/CIRGenClass.cpp b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
index 5427ca8bf30a6..b81f26839a579 100644
--- a/clang/lib/CIR/CodeGen/CIRGenClass.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
@@ -20,6 +20,7 @@
 #include "clang/AST/Type.h"
 #include "clang/CIR/Dialect/IR/CIRDialect.h"
 #include "clang/CIR/MissingFeatures.h"
+#include "clang/CodeGenUtils/CodeGenUtils.h"
 
 using namespace clang;
 using namespace clang::CIRGen;
@@ -154,12 +155,6 @@ static void emitMemberInitializer(CIRGenFunction &cgf,
   cgf.emitInitializerForField(field, lhs, memberInit->getInit());
 }
 
-static bool isInitializerOfDynamicClass(const CXXCtorInitializer *baseInit) {
-  const Type *baseType = baseInit->getBaseClass();
-  const auto *baseClassDecl = baseType->castAsCXXRecordDecl();
-  return baseClassDecl->isDynamicClass();
-}
-
 namespace {
 /// Call the destructor for a direct base class.
 struct CallBaseDtor final : EHScopeStack::Cleanup {
@@ -344,7 +339,7 @@ void CIRGenFunction::emitCtorPrologue(const CXXConstructorDecl *cd,
   auto emitInitializer = [&](CXXCtorInitializer *baseInit) {
     if (cgm.getCodeGenOpts().StrictVTablePointers &&
         cgm.getCodeGenOpts().OptimizationLevel > 0 &&
-        isInitializerOfDynamicClass(baseInit)) {
+        CodeGenUtils::isInitializerOfDynamicClass(baseInit)) {
       // It's OK to continue after emitting the error here. The missing code
       // just "launders" the 'this' pointer.
       cgm.errorNYI(cd->getSourceRange(),
diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index ac7d4693c6000..28b00321fa39b 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -30,6 +30,7 @@
 #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 <optional>
 
 using namespace clang;
@@ -507,12 +508,6 @@ void CIRGenFunction::emitStoreOfScalar(mlir::Value value, Address addr,
   assert(!cir::MissingFeatures::opTBAA());
 }
 
-// TODO: Replace this with a proper TargetInfo function call.
-/// Helper method to check if the underlying ABI is AAPCS
-static bool isAAPCS(const TargetInfo &targetInfo) {
-  return targetInfo.getABI().starts_with("aapcs");
-}
-
 mlir::Value CIRGenFunction::emitStoreThroughBitfieldLValue(RValue src,
                                                            LValue dst) {
 
@@ -520,9 +515,9 @@ mlir::Value CIRGenFunction::emitStoreThroughBitfieldLValue(RValue src,
   mlir::Type resLTy = convertTypeForMem(dst.getType());
   Address ptr = dst.getBitFieldAddress();
 
-  bool useVoaltile = cgm.getCodeGenOpts().AAPCSBitfieldWidth &&
-                     dst.isVolatileQualified() &&
-                     info.volatileStorageSize != 0 && isAAPCS(cgm.getTarget());
+  bool useVoaltile =
+      cgm.getCodeGenOpts().AAPCSBitfieldWidth && dst.isVolatileQualified() &&
+      info.volatileStorageSize != 0 && CodeGenUtils::isAAPCS(cgm.getTarget());
 
   assert(currSrcLoc && "must pass in source location");
 
@@ -539,7 +534,7 @@ RValue CIRGenFunction::emitLoadOfBitfieldLValue(LValue lv, SourceLocation loc) {
   Address ptr = lv.getBitFieldAddress();
 
   bool useVoaltile = lv.isVolatileQualified() && info.volatileOffset != 0 &&
-                     isAAPCS(cgm.getTarget());
+                     CodeGenUtils::isAAPCS(cgm.getTarget());
 
   mlir::Value field =
       builder.createGetBitfield(getLoc(loc), resLTy, ptr, ptr.getElementType(),
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index 0cb2164f4c0ef..2abd167634614 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -36,6 +36,7 @@
 #include "clang/CIR/Dialect/IR/CIRTypes.h"
 #include "clang/CIR/Interfaces/CIROpInterfaces.h"
 #include "clang/CIR/MissingFeatures.h"
+#include "clang/CodeGenUtils/CodeGenUtils.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/ADT/StringRef.h"
@@ -3268,30 +3269,12 @@ void CIRGenModule::setFunctionAttributes(GlobalDecl globalDecl,
   }
 }
 
-/// 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.
-static 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;
-}
-
 void CIRGenModule::setCIRFunctionAttributesForDefinition(
     const clang::FunctionDecl *decl, cir::FuncOp f) {
   assert(!cir::MissingFeatures::opFuncUnwindTablesAttr());
   assert(!cir::MissingFeatures::stackProtector());
 
-  if (!hasUnwindExceptions(langOpts))
+  if (!CodeGenUtils::hasUnwindExceptions(langOpts))
     f->setAttr(cir::CIRDialect::getNoThrowAttrName(),
                mlir::UnitAttr::get(&getMLIRContext()));
 
diff --git a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp
index c00e514c48cd0..fe31b92af6166 100644
--- a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp
@@ -22,6 +22,7 @@
 #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 "llvm/Support/Casting.h"
 
 #include <memory>
@@ -112,10 +113,6 @@ struct CIRRecordLowering final {
 
   mlir::Type getVFPtrType();
 
-  bool isAAPCS() const {
-    return astContext.getTargetInfo().getABI().starts_with("aapcs");
-  }
-
   /// Helper function to check if the target machine is BigEndian.
   bool isBigEndian() const { return astContext.getTargetInfo().isBigEndian(); }
 
@@ -1091,7 +1088,7 @@ bool CIRRecordLowering::hasOwnStorage(const CXXRecordDecl *decl,
 /// Enforcing the width restriction can be disabled using
 /// -fno-aapcs-bitfield-width.
 void CIRRecordLowering::computeVolatileBitfields() {
-  if (!isAAPCS() ||
+  if (!CodeGenUtils::isAAPCS(astContext.getTargetInfo()) ||
       !cirGenTypes.getCGModule().getCodeGenOpts().AAPCSBitfieldWidth)
     return;
 
diff --git a/clang/lib/CIR/CodeGen/CMakeLists.txt b/clang/lib/CIR/CodeGen/CMakeLists.txt
index 3ea96a8ea05e9..0fd5cdfb02540 100644
--- a/clang/lib/CIR/CodeGen/CMakeLists.txt
+++ b/clang/lib/CIR/CodeGen/CMakeLists.txt
@@ -69,6 +69,7 @@ add_clang_library(clangCIR
   LINK_LIBS
   clangAST
   clangBasic
+  clangCodeGenUtils
   clangLex
   ${dialect_libs}
   CIRRegisterAllDialects
diff --git a/clang/lib/CMakeLists.txt b/clang/lib/CMakeLists.txt
index dee31cb3f166e..22d260e55b271 100644
--- a/clang/lib/CMakeLists.txt
+++ b/clang/lib/CMakeLists.txt
@@ -7,6 +7,7 @@ add_subdirectory(AST)
 add_subdirectory(ASTMatchers)
 add_subdirectory(CrossTU)
 add_subdirectory(Sema)
+add_subdirectory(CodeGenUtils)
 add_subdirectory(CodeGen)
 add_subdirectory(Analysis)
 add_subdirectory(Edit)
diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp
index b1c4b0db65c2b..8db5e0393b73d 100644
--- a/clang/lib/CodeGen/CGClass.cpp
+++ b/clang/lib/CodeGen/CGClass.cpp
@@ -26,6 +26,7 @@
 #include "clang/AST/StmtCXX.h"
 #include "clang/Basic/CodeGenOptions.h"
 #include "clang/CodeGen/CGFunctionInfo.h"
+#include "clang/CodeGenUtils/CodeGenUtils.h"
 #include "llvm/IR/Intrinsics.h"
 #include "llvm/IR/Metadata.h"
 #include "llvm/Support/SaveAndRestore.h"
@@ -1228,11 +1229,6 @@ class AssignmentMemcpyizer : public FieldMemcpyizer {
 
 } // end anonymous namespace
 
-static bool isInitializerOfDynamicClass(const CXXCtorInitializer *BaseInit) {
-  const Type *BaseType = BaseInit->getBaseClass();
-  return BaseType->castAsCXXRecordDecl()->isDynamicClass();
-}
-
 /// EmitCtorPrologue - This routine generates necessary code to initialize
 /// base classes and non-static data members belonging to this constructor.
 void CodeGenFunction::EmitCtorPrologue(const CXXConstructorDecl *CD,
@@ -1291,7 +1287,7 @@ void CodeGenFunction::EmitCtorPrologue(const CXXConstructorDecl *CD,
       SaveAndRestore ThisRAII(CXXThisValue);
       if (CGM.getCodeGenOpts().StrictVTablePointers &&
           CGM.getCodeGenOpts().OptimizationLevel > 0 &&
-          isInitializerOfDynamicClass(Initializer))
+          CodeGenUtils::isInitializerOfDynamicClass(Initializer))
         CXXThisValue = Builder.CreateLaunderInvariantGroup(LoadCXXThis());
       EmitBaseInitializer(*this, ClassDecl, Initializer);
     }
@@ -1309,7 +1305,7 @@ void CodeGenFunction::EmitCtorPrologue(const CXXConstructorDecl *CD,
     SaveAndRestore ThisRAII(CXXThisValue);
     if (CGM.getCodeGenOpts().StrictVTablePointers &&
         CGM.getCodeGenOpts().OptimizationLevel > 0 &&
-        isInitializerOfDynamicClass(Initializer))
+        CodeGenUtils::isInitializerOfDynamicClass(Initializer))
       CXXThisValue = Builder.CreateLaunderInvariantGroup(LoadCXXThis());
     EmitBaseInitializer(*this, ClassDecl, Initializer);
   }
@@ -1329,86 +1325,6 @@ void CodeGenFunction::EmitCtorPrologue(const CXXConstructorDecl *CD,
   CM.finish();
 }
 
-static bool FieldHasTrivialDestructorBody(ASTContext &Context,
-                                          const FieldDecl *Field);
-
-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;
-}
-
-static 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);
-}
-
-/// CanSkipVTablePointerInitialization - Check whether we need to initialize
-/// any vtable pointers before calling this destructor.
-static bool CanSkipVTablePointerInitialization(CodeGenFunction &CGF,
-                                               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(CGF.getContext(), Field))
-      return false;
-
-  return true;
-}
 
 static void EmitConditionalArrayDtorCall(const CXXDestructorDecl *DD,
                                          CodeGenFunction &CGF,
@@ -1611,7 +1527,7 @@ void CodeGenFunction::EmitDestructorBody(FunctionArgList &Args) {
     EnterDtorCleanups(Dtor, Dtor_Base);
 
     // Initialize the vtable pointers before entering the body.
-    if (!CanSkipVTablePointerInitialization(*this, Dtor)) {
+    if (!CodeGenUtils::CanSkipVTablePointerInitialization(getContext(), Dtor)) {
       // Insert the llvm.launder.invariant.group intrinsic before initializing
       // the vptrs to cancel any previous assumptions we might have made.
       if (CGM.getCodeGenOpts().StrictVTablePointers &&
@@ -1981,7 +1897,7 @@ class SanitizeDtorCleanupBuilder {
     if (isEmptyFieldForLayout(Context, Field))
       return;
     unsigned FieldIndex = Field->getFieldIndex();
-    if (FieldHasTrivialDestructorBody(Context, Field)) {
+    if (CodeGenUtils::FieldHasTrivialDestructorBody(Context, Field)) {
       if (!StartIndex)
         StartIndex = FieldIndex;
     } else if (StartIndex) {
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index cba81de9d10dd..6e78ed6b7cabb 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -39,6 +39,7 @@
 #include "clang/Basic/CodeGenOptions.h"
 #include "clang/Basic/Module.h"
 #include "clang/Basic/SourceManager.h"
+#include "clang/CodeGenUtils/CodeGenUtils.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/ScopeExit.h"
 #include "llvm/ADT/StringExtras.h"
@@ -514,11 +515,6 @@ static RawAddress createReferenceTemporary(CodeGenFunction &CGF,
   llvm_unreachable("unknown storage duration");
 }
 
-/// Helper method to check if the underlying ABI is AAPCS
-static bool isAAPCS(const TargetInfo &TargetInfo) {
-  return TargetInfo.getABI().starts_with("aapcs");
-}
-
 LValue CodeGenFunction::
 EmitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *M) {
   const Expr *E = M->getSubExpr();
@@ -2665,7 +2661,8 @@ RValue CodeGenFunction::EmitLoadOfBitfieldLValue(LValue LV,
       Builder.CreateLoad(Ptr, LV.isVolatileQualified(), "bf.load");
 
   bool UseVolatile = LV.isVolatileQualified() &&
-                     Info.VolatileStorageSize != 0 && isAAPCS(CGM.getTarget());
+                     Info.VolatileStorageSize != 0 &&
+                     CodeGenUtils::isAAPCS(CGM.getTarget());
   const unsigned Offset = UseVolatile ? Info.VolatileOffset : Info.Offset;
   const unsigned StorageSize =
       UseVolatile ? Info.VolatileStorageSize : Info.StorageSize;
@@ -3067,7 +3064,7 @@ void CodeGenFunction::EmitStoreThroughBitfieldLValue(RValue Src, LValue Dst,
 
   const bool UseVolatile =
       CGM.getCodeGenOpts().AAPCSBitfieldWidth && Dst.isVolatileQualified() &&
-      Info.VolatileStorageSize != 0 && isAAPCS(CGM.getTarget());
+      Info.VolatileStorageSize != 0 && CodeGenUtils::isAAPCS(CGM.getTarget());
   const unsigned StorageSize =
       UseVolatile ? Info.VolatileStorageSize : Info.StorageSize;
   const unsigned Offset = UseVolatile ? Info.VolatileOffset : Info.Offset;
@@ -3101,7 +3098,7 @@ void CodeGenFunction::EmitStoreThroughBitfieldLValue(RValue Src, LValue Dst,
     // with any non-bit-field member, its container must be read exactly once
     // and written exactly once using the access width appropriate to the type
     // of the container. The two accesses are not atomic.
-    if (Dst.isVolatileQualified() && isAAPCS(CGM.getTarget()) &&
+    if (Dst.isVolatileQualified() && CodeGenUtils::isAAPCS(CGM.getTarget()) &&
         CGM.getCodeGenOpts().ForceAAPCSBitfieldLoad)
       Builder.CreateLoad(Ptr, true, "bf.load");
   }
@@ -5801,7 +5798,7 @@ LValue CodeGenFunction::EmitLValueForField(LValue base, const FieldDecl *field,
     const CGRecordLayout &RL =
         CGM.getTypes().getCGRecordLayout(field->getParent());
     const CGBitFieldInfo &Info = RL.getBitFieldInfo(field);
-    const bool UseVolatile = isAAPCS(CGM.getTarget()) &&
+    const bool UseVolatile = CodeGenUtils::isAAPCS(CGM.getTarget()) &&
                              CGM.getCodeGenOpts().AAPCSBitfieldWidth &&
                              Info.VolatileStorageSize != 0 &&
                              field->getType()
diff --git a/clang/lib/CodeGen/CGRecordLayoutBuilder.cpp b/clang/lib/CodeGen/CGRecordLayoutBuilder.cpp
index e9205c68c2812..d98d7c0c96dc3 100644
--- a/clang/lib/CodeGen/CGRecordLayoutBuilder.cpp
+++ b/clang/lib/CodeGen/CGRecordLayoutBuilder.cpp
@@ -21,6 +21,7 @@
 #include "clang/AST/Expr.h"
 #include "clang/AST/RecordLayout.h"
 #include "clang/Basic/CodeGenOptions.h"
+#include "clang/CodeGenUtils/CodeGenUtils.h"
 #include "llvm/IR/DataLayout.h"
 #include "llvm/IR/DerivedTypes.h"
 #include "llvm/IR/Type.h"
@@ -109,11 +110,6 @@ struct CGRecordLowering {
            D->isMsStruct(Context);
   }
 
-  /// Helper function to check if we are targeting AAPCS.
-  bool isAAPCS() const {
-    return Context.getTargetInfo().getABI().starts_with("aapcs");
-  }
-
   /// Helper function to check if the target machine is BigEndian.
   bool isBE() const { return Context.getTargetInfo().isBigEndian(); }
 
@@ -753,7 +749,8 @@ void CGRecordLowering::accumulateBases() {
 /// Enforcing the width restriction can be disabled using
 /// -fno-aapcs-bitfield-width.
 void CGRecordLowering::computeVolatileBitfields() {
-  if (!isAAPCS() || !Types.getCodeGenOpts().AAPCSBitfieldWidth)
+  if (!CodeGenUtils::isAAPCS(Context.getTargetInfo()) ||
+      !Types.getCodeGenOpts().AAPCSBitfieldWidth)
     return;
 
   for (auto &I : BitFields) {
diff --git a/clang/lib/CodeGen/CMakeLists.txt b/clang/lib/CodeGen/CMakeLists.txt
index 853f038c4186b..d3986ddecfe12 100644
--- a/clang/lib/CodeGen/CMakeLists.txt
+++ b/clang/lib/CodeGen/CMakeLists.txt
@@ -178,6 +178,7 @@ add_clang_library(clangCodeGen
   clangAST
   clangAnalysis
   clangBasic
+  clangCodeGenUtils
   clangFrontend
   clangLex
   clangSerialization
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 17b5f0fe4133d..622423893bfa6 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -48,6 +48,7 @@
 #include "clang/Basic/Version.h"
 #include "clang/CodeGen/BackendUtil.h"
 #include "clang/CodeGen/ConstantInitBuilder.h"
+#include "clang/CodeGenUtils/CodeGenUtils.h"
 #include "clang/Lex/Preprocessor.h"
 #include "llvm/ABI/IRTypeMapper.h"
 #include "llvm/ABI/TargetInfo.h"
@@ -3073,26 +3074,6 @@ void CodeGenModule::GenKernelArgMetadata(llvm::Function *Fn,
                     llvm::MDNode::get(VMContext, argNames));
 }
 
-/// 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.
-static 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;
-}
-
 static bool requiresMemberFunctionPointerTypeMetadata(CodeGenModule &CGM,
                                                       const CXXMethodDecl *MD) {
   // Check that the type metadata can ever actually be used by a call.
@@ -3135,7 +3116,7 @@ void CodeGenModule::SetLLVMFunctionAttributesForDefinition(const Decl *D,
     B.addAttribute("stack-probe-size",
                    std::to_string(CodeGenOpts.StackProbeSize));
 
-  if (!hasUnwindExceptions(LangOpts))
+  if (!CodeGenUtils::hasUnwindExceptions(LangOpts))
     B.addAttribute(llvm::Attribute::NoUnwind);
 
   if (std::optional<llvm::Attribute::AttrKind> Attr =
diff --git a/clang/lib/CodeGenUtils/CMakeLists.txt b/clang/lib/CodeGenUtils/CMakeLists.txt
new file mode 100644
index 0000000000000..b87c83b42dd9b
--- /dev/null
+++ b/clang/lib/CodeGenUtils/CMakeLists.txt
@@ -0,0 +1,11 @@
+set(LLVM_LINK_COMPONENTS
+  Support
+  )
+
+add_clang_library(clangCodeGenUtils
+  CodeGenUtils.cpp
+
+  LINK_LIBS
+  clangAST
+  clangBasic
+  )
diff --git a/clang/lib/CodeGenUtils/CodeGenUtils.cpp b/clang/lib/CodeGenUtils/CodeGenUtils.cpp
new file mode 100644
index 0000000000000..e052410dfc3c5
--- /dev/null
+++ b/clang/lib/CodeGenUtils/CodeGenUtils.cpp
@@ -0,0 +1,115 @@
+//==--- 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/Basic/TargetInfo.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");
+}
+bool isInitializerOfDynamicClass(const CXXCtorInitializer *BaseInit) {
+  const Type *BaseType = BaseInit->getBaseClass();
+  return BaseType->castAsCXXRecordDecl()->isDynamicClass();
+}
+
+} // namespace clang::CodeGenUtils

>From 18d9d305ae14839706c2219338d883815f639238 Mon Sep 17 00:00:00 2001
From: erichkeane <ekeane at nvidia.com>
Date: Thu, 3 Sep 2026 10:22:22 -0700
Subject: [PATCH 2/4] Clang-format

---
 clang/include/clang/CodeGenUtils/CodeGenUtils.h | 5 ++---
 clang/lib/CodeGen/CGClass.cpp                   | 1 -
 2 files changed, 2 insertions(+), 4 deletions(-)

diff --git a/clang/include/clang/CodeGenUtils/CodeGenUtils.h b/clang/include/clang/CodeGenUtils/CodeGenUtils.h
index 9a93281c3689d..19ba6732d4b30 100644
--- a/clang/include/clang/CodeGenUtils/CodeGenUtils.h
+++ b/clang/include/clang/CodeGenUtils/CodeGenUtils.h
@@ -16,8 +16,7 @@ namespace clang::CodeGenUtils {
 /// destructor.
 bool CanSkipVTablePointerInitialization(ASTContext &Ctx,
                                         const CXXDestructorDecl *Dtor);
-bool FieldHasTrivialDestructorBody(ASTContext &Context,
-                                          const FieldDecl *Field);
+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
@@ -28,6 +27,6 @@ bool hasUnwindExceptions(const LangOptions &LangOpts);
 bool isAAPCS(const TargetInfo &TargetInfo);
 
 bool isInitializerOfDynamicClass(const CXXCtorInitializer *BaseInit);
-}
+} // namespace clang::CodeGenUtils
 
 #endif // LLVM_CLANG_CODEGENUTILS_CODEGENUTILS_H
diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp
index 8db5e0393b73d..030c7328edcb3 100644
--- a/clang/lib/CodeGen/CGClass.cpp
+++ b/clang/lib/CodeGen/CGClass.cpp
@@ -1325,7 +1325,6 @@ void CodeGenFunction::EmitCtorPrologue(const CXXConstructorDecl *CD,
   CM.finish();
 }
 
-
 static void EmitConditionalArrayDtorCall(const CXXDestructorDecl *DD,
                                          CodeGenFunction &CGF,
                                          llvm::Value *ShouldDeleteCondition) {

>From 105aa8af71bfe8ed0af4c1ed92e264a630c0d8c7 Mon Sep 17 00:00:00 2001
From: erichkeane <ekeane at nvidia.com>
Date: Thu, 3 Sep 2026 11:15:12 -0700
Subject: [PATCH 3/4] Add newlines inside header file

---
 clang/include/clang/CodeGenUtils/CodeGenUtils.h | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/clang/include/clang/CodeGenUtils/CodeGenUtils.h b/clang/include/clang/CodeGenUtils/CodeGenUtils.h
index 19ba6732d4b30..9f8cde5835eaa 100644
--- a/clang/include/clang/CodeGenUtils/CodeGenUtils.h
+++ b/clang/include/clang/CodeGenUtils/CodeGenUtils.h
@@ -16,13 +16,16 @@ namespace clang::CodeGenUtils {
 /// destructor.
 bool CanSkipVTablePointerInitialization(ASTContext &Ctx,
                                         const CXXDestructorDecl *Dtor);
+
 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);
 

>From b751d719bd2494d9dec6d2bc97ecee086dd3d363 Mon Sep 17 00:00:00 2001
From: erichkeane <ekeane at nvidia.com>
Date: Thu, 3 Sep 2026 14:07:42 -0700
Subject: [PATCH 4/4] Switch to camel case

---
 clang/include/clang/CodeGenUtils/CodeGenUtils.h |  4 ++--
 clang/lib/CodeGen/CGClass.cpp                   |  4 ++--
 clang/lib/CodeGenUtils/CodeGenUtils.cpp         | 16 ++++++++--------
 3 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/clang/include/clang/CodeGenUtils/CodeGenUtils.h b/clang/include/clang/CodeGenUtils/CodeGenUtils.h
index 9f8cde5835eaa..faca3b151edc2 100644
--- a/clang/include/clang/CodeGenUtils/CodeGenUtils.h
+++ b/clang/include/clang/CodeGenUtils/CodeGenUtils.h
@@ -14,10 +14,10 @@
 namespace clang::CodeGenUtils {
 /// Check whether we need to initialize any vtable pointers before calling this
 /// destructor.
-bool CanSkipVTablePointerInitialization(ASTContext &Ctx,
+bool canSkipVTablePointerInitialization(ASTContext &Ctx,
                                         const CXXDestructorDecl *Dtor);
 
-bool FieldHasTrivialDestructorBody(ASTContext &Context, const FieldDecl *Field);
+bool fieldHasTrivialDestructorBody(ASTContext &Context, const FieldDecl *Field);
 
 /// Determines whether the language options require us to model
 /// unwind exceptions.  We treat -fexceptions as mandating this
diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp
index 030c7328edcb3..e51f3f9ac9cb4 100644
--- a/clang/lib/CodeGen/CGClass.cpp
+++ b/clang/lib/CodeGen/CGClass.cpp
@@ -1526,7 +1526,7 @@ void CodeGenFunction::EmitDestructorBody(FunctionArgList &Args) {
     EnterDtorCleanups(Dtor, Dtor_Base);
 
     // Initialize the vtable pointers before entering the body.
-    if (!CodeGenUtils::CanSkipVTablePointerInitialization(getContext(), Dtor)) {
+    if (!CodeGenUtils::canSkipVTablePointerInitialization(getContext(), Dtor)) {
       // Insert the llvm.launder.invariant.group intrinsic before initializing
       // the vptrs to cancel any previous assumptions we might have made.
       if (CGM.getCodeGenOpts().StrictVTablePointers &&
@@ -1896,7 +1896,7 @@ class SanitizeDtorCleanupBuilder {
     if (isEmptyFieldForLayout(Context, Field))
       return;
     unsigned FieldIndex = Field->getFieldIndex();
-    if (CodeGenUtils::FieldHasTrivialDestructorBody(Context, Field)) {
+    if (CodeGenUtils::fieldHasTrivialDestructorBody(Context, Field)) {
       if (!StartIndex)
         StartIndex = FieldIndex;
     } else if (StartIndex) {
diff --git a/clang/lib/CodeGenUtils/CodeGenUtils.cpp b/clang/lib/CodeGenUtils/CodeGenUtils.cpp
index e052410dfc3c5..482896e1944c1 100644
--- a/clang/lib/CodeGenUtils/CodeGenUtils.cpp
+++ b/clang/lib/CodeGenUtils/CodeGenUtils.cpp
@@ -11,7 +11,7 @@
 
 namespace clang::CodeGenUtils {
 static bool
-HasTrivialDestructorBody(ASTContext &Context,
+hasTrivialDestructorBody(ASTContext &Context,
                          const CXXRecordDecl *BaseClassDecl,
                          const CXXRecordDecl *MostDerivedClassDecl) {
   // If the destructor is trivial we don't have to check anything else.
@@ -23,7 +23,7 @@ HasTrivialDestructorBody(ASTContext &Context,
 
   // Check fields.
   for (const auto *Field : BaseClassDecl->fields())
-    if (!FieldHasTrivialDestructorBody(Context, Field))
+    if (!fieldHasTrivialDestructorBody(Context, Field))
       return false;
 
   // Check non-virtual bases.
@@ -32,7 +32,7 @@ HasTrivialDestructorBody(ASTContext &Context,
       continue;
 
     const auto *NonVirtualBase = I.getType()->castAsCXXRecordDecl();
-    if (!HasTrivialDestructorBody(Context, NonVirtualBase,
+    if (!hasTrivialDestructorBody(Context, NonVirtualBase,
                                   MostDerivedClassDecl))
       return false;
   }
@@ -41,7 +41,7 @@ HasTrivialDestructorBody(ASTContext &Context,
     // Check virtual bases.
     for (const auto &I : BaseClassDecl->vbases()) {
       const auto *VirtualBase = I.getType()->castAsCXXRecordDecl();
-      if (!HasTrivialDestructorBody(Context, VirtualBase, MostDerivedClassDecl))
+      if (!hasTrivialDestructorBody(Context, VirtualBase, MostDerivedClassDecl))
         return false;
     }
   }
@@ -49,7 +49,7 @@ HasTrivialDestructorBody(ASTContext &Context,
   return true;
 }
 
-bool FieldHasTrivialDestructorBody(ASTContext &Context,
+bool fieldHasTrivialDestructorBody(ASTContext &Context,
                                    const FieldDecl *Field) {
   QualType FieldBaseElementType = Context.getBaseElementType(Field->getType());
 
@@ -61,12 +61,12 @@ bool FieldHasTrivialDestructorBody(ASTContext &Context,
   if (FieldClassDecl->isUnion() && FieldClassDecl->isAnonymousStructOrUnion())
     return true;
 
-  return HasTrivialDestructorBody(Context, FieldClassDecl, FieldClassDecl);
+  return hasTrivialDestructorBody(Context, FieldClassDecl, FieldClassDecl);
 }
 
 /// Check whether we need to initialize any vtable pointers before calling this
 /// destructor.
-bool CanSkipVTablePointerInitialization(ASTContext &Ctx,
+bool canSkipVTablePointerInitialization(ASTContext &Ctx,
                                         const CXXDestructorDecl *Dtor) {
   const CXXRecordDecl *ClassDecl = Dtor->getParent();
   if (!ClassDecl->isDynamicClass())
@@ -82,7 +82,7 @@ bool CanSkipVTablePointerInitialization(ASTContext &Ctx,
 
   // Check the fields.
   for (const auto *Field : ClassDecl->fields())
-    if (!FieldHasTrivialDestructorBody(Ctx, Field))
+    if (fieldHasTrivialDestructorBody(Ctx, Field))
       return false;
 
   return true;



More information about the cfe-commits mailing list