[llvm-branch-commits] [clang] [CIR][CodeGen][NFC] Retire the CodeGenUtils.h catch-all header (PR #223451)

Henrich Lauko via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Tue Oct 6 05:50:51 PDT 2026


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 <hlauko at nvidia.com>
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 <hlauko at nvidia.com>
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 <hlauko at nvidia.com>
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 <hlauko at nvidia.com>
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)



More information about the llvm-branch-commits mailing list