[clang] d3ad7fe - [clang][CIR][CUDA][HIP] Unify CUDA decl emission check (NFC) (#231153)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Oct 15 00:32:28 PDT 2026
Author: Steffen Larsen
Date: 2026-10-15T07:32:22Z
New Revision: d3ad7fe9f82ea7aa48755320c03233acd7db7938
URL: https://github.com/llvm/llvm-project/commit/d3ad7fe9f82ea7aa48755320c03233acd7db7938
DIFF: https://github.com/llvm/llvm-project/commit/d3ad7fe9f82ea7aa48755320c03233acd7db7938.diff
LOG: [clang][CIR][CUDA][HIP] Unify CUDA decl emission check (NFC) (#231153)
CodeGen and CIRGen both decide which global variables and functions to
emit on the host or device side of a CUDA/HIP compilation with copies of
the same check. Move the check to ASTContext::shouldEmitCUDADecl to
reduce replicated surface.
Assisted-by: Claude Opus 5.5
Signed-off-by: Steffen Holst Larsen <sholstla at amd.com>
Added:
Modified:
clang/include/clang/AST/ASTContext.h
clang/lib/AST/ASTContext.cpp
clang/lib/CIR/CodeGen/CIRGenModule.cpp
clang/lib/CIR/CodeGen/CIRGenModule.h
clang/lib/CodeGen/CodeGenModule.cpp
clang/lib/CodeGen/CodeGenModule.h
Removed:
################################################################################
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index 1349637273d1e..04cc2da25cecb 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -3664,6 +3664,13 @@ class ASTContext : public RefCountedBase<ASTContext> {
/// it is not used.
bool DeclMustBeEmitted(const Decl *D);
+ /// Determines if the global variable or function \p D is emitted on the side
+ /// of the CUDA/HIP compilation, i.e. host or device, that is being compiled,
+ /// based on its host/device related attributes.
+ ///
+ /// \returns false if CodeGen never emits \p D in this compilation.
+ bool shouldEmitCUDADecl(const ValueDecl *D) const;
+
/// Visits all versions of a multiversioned function with the passed
/// predicate.
void forEachMultiversionedFunctionVersion(
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 664688b852956..19737642428ab 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -15,6 +15,7 @@
#include "CXXABI.h"
#include "clang/AST/APValue.h"
#include "clang/AST/ASTConcept.h"
+#include "clang/AST/ASTLambda.h"
#include "clang/AST/ASTMutationListener.h"
#include "clang/AST/ASTStructuralEquivalence.h"
#include "clang/AST/ASTTypeTraits.h"
@@ -13509,6 +13510,44 @@ bool ASTContext::DeclMustBeEmitted(const Decl *D) {
return false;
}
+template <typename AttrT> static bool hasImplicitAttr(const ValueDecl *D) {
+ if (auto *A = D->getAttr<AttrT>())
+ return A->isImplicit();
+ return D->isImplicit();
+}
+
+bool ASTContext::shouldEmitCUDADecl(const ValueDecl *D) const {
+ assert(LangOpts.CUDA && "Should not be called by non-CUDA languages");
+ assert((isa<FunctionDecl>(D) || isa<VarDecl>(D)) &&
+ "Expected Variable or Function");
+
+ if (const auto *VD = dyn_cast<VarDecl>(D)) {
+ // We need to emit host-side 'shadows' for all global device-side variables
+ // because the CUDA runtime needs their size and host-side address in order
+ // to provide access to their device-side incarnations.
+ return !LangOpts.CUDAIsDevice || VD->hasAttr<CUDADeviceAttr>() ||
+ VD->hasAttr<CUDAConstantAttr>() || VD->hasAttr<CUDASharedAttr>() ||
+ VD->getType()->isCUDADeviceBuiltinSurfaceType() ||
+ VD->getType()->isCUDADeviceBuiltinTextureType();
+ }
+
+ const auto *FD = cast<FunctionDecl>(D);
+ // Device-only functions are the only things we skip on the host.
+ if (!LangOpts.CUDAIsDevice)
+ return FD->hasAttr<CUDAHostAttr>() || !FD->hasAttr<CUDADeviceAttr>();
+
+ // Non-constexpr non-lambda implicit host device functions are not emitted
+ // unless they are used on device side.
+ return (FD->hasAttr<CUDADeviceAttr>() &&
+ !(LangOpts.OffloadImplicitHostDeviceTemplates &&
+ hasImplicitAttr<CUDAHostAttr>(FD) &&
+ hasImplicitAttr<CUDADeviceAttr>(FD) && !FD->isConstexpr() &&
+ !isLambdaCallOperator(FD) &&
+ !CUDAImplicitHostDeviceFunUsedByDevice.count(FD))) ||
+ FD->hasAttr<CUDAGlobalAttr>() ||
+ (LangOpts.HIPStdPar && !FD->hasAttr<CUDAHostAttr>());
+}
+
void ASTContext::forEachMultiversionedFunctionVersion(
const FunctionDecl *FD,
llvm::function_ref<void(FunctionDecl *)> Pred) const {
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index 245af3dbdd98b..cf3b4200115cb 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -524,28 +524,6 @@ void CIRGenModule::emitDeferred() {
}
}
-template <typename AttrT> static bool hasImplicitAttr(const ValueDecl *decl) {
- if (!decl)
- return false;
- if (auto *attr = decl->getAttr<AttrT>())
- return attr->isImplicit();
- return decl->isImplicit();
-}
-
-// TODO(cir): This should be shared with OG Codegen.
-bool CIRGenModule::shouldEmitCUDAGlobalVar(const VarDecl *global) const {
- assert(langOpts.CUDA && "Should not be called by non-CUDA languages");
- // We need to emit host-side 'shadows' for all global
- // device-side variables because the CUDA runtime needs their
- // size and host-side address in order to provide access to
- // their device-side incarnations.
- return !langOpts.CUDAIsDevice || global->hasAttr<CUDADeviceAttr>() ||
- global->hasAttr<CUDAConstantAttr>() ||
- global->hasAttr<CUDASharedAttr>() ||
- global->getType()->isCUDADeviceBuiltinSurfaceType() ||
- global->getType()->isCUDADeviceBuiltinTextureType();
-}
-
void CIRGenModule::printPostfixForExternalizedDecl(llvm::raw_ostream &os,
const Decl *d) {
// ptxas does not allow '.' in symbol names. On the other hand, HIP prefers
@@ -589,34 +567,8 @@ void CIRGenModule::emitGlobal(clang::GlobalDecl gd) {
}
// If this is CUDA, be selective about which declarations we emit.
- // Non-constexpr non-lambda implicit host device functions are not emitted
- // unless they are used on device side.
- if (langOpts.CUDA) {
- assert((isa<FunctionDecl>(global) || isa<VarDecl>(global)) &&
- "Expected Variable or Function");
- if (const auto *varDecl = dyn_cast<VarDecl>(global)) {
- if (!shouldEmitCUDAGlobalVar(varDecl))
- return;
- // TODO(cir): This should be shared with OG Codegen.
- } else if (langOpts.CUDAIsDevice) {
- const auto *functionDecl = dyn_cast<FunctionDecl>(global);
- if ((!global->hasAttr<CUDADeviceAttr>() ||
- (langOpts.OffloadImplicitHostDeviceTemplates &&
- hasImplicitAttr<CUDAHostAttr>(functionDecl) &&
- hasImplicitAttr<CUDADeviceAttr>(functionDecl) &&
- !functionDecl->isConstexpr() &&
- !isLambdaCallOperator(functionDecl) &&
- !getASTContext().CUDAImplicitHostDeviceFunUsedByDevice.count(
- functionDecl))) &&
- !global->hasAttr<CUDAGlobalAttr>() &&
- !(langOpts.HIPStdPar && isa<FunctionDecl>(global) &&
- !global->hasAttr<CUDAHostAttr>()))
- return;
- // Device-only functions are the only things we skip.
- } else if (!global->hasAttr<CUDAHostAttr>() &&
- global->hasAttr<CUDADeviceAttr>())
- return;
- }
+ if (langOpts.CUDA && !getASTContext().shouldEmitCUDADecl(global))
+ return;
if (langOpts.OpenMP) {
// If this is OpenMP, check if it is legal to emit this global normally.
@@ -783,7 +735,7 @@ void CIRGenModule::addGlobalDtor(cir::FuncOp dtor,
void CIRGenModule::handleCXXStaticMemberVarInstantiation(VarDecl *vd) {
VarDecl::DefinitionKind dk = vd->isThisDeclarationADefinition();
if ((dk == VarDecl::Definition && vd->hasAttr<DLLImportAttr>()) ||
- (langOpts.CUDA && !shouldEmitCUDAGlobalVar(vd)))
+ (langOpts.CUDA && !getASTContext().shouldEmitCUDADecl(vd)))
return;
TemplateSpecializationKind tsk = vd->getTemplateSpecializationKind();
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h
index 1ea0acba27b05..b37bc9f5f901c 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.h
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.h
@@ -804,10 +804,6 @@ class CIRGenModule : public CIRGenTypeCache {
static void setInitializer(cir::GlobalOp &op, mlir::Attribute value);
- // Whether a global variable should be emitted by CUDA/HIP host/device
- // related attributes.
- bool shouldEmitCUDAGlobalVar(const VarDecl *global) const;
-
/// Print the postfix for externalized static variable or kernels for single
/// source offloading languages CUDA and HIP. The unique postfix is created
/// using either the CUID argument, or the file's UniqueID and active macros.
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index bf03e10f9fb42..f5fd8b7b90435 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -4763,14 +4763,6 @@ ConstantAddress CodeGenModule::GetWeakRefReference(const ValueDecl *VD) {
return ConstantAddress(Aliasee, DeclTy, Alignment);
}
-template <typename AttrT> static bool hasImplicitAttr(const ValueDecl *D) {
- if (!D)
- return false;
- if (auto *A = D->getAttr<AttrT>())
- return A->isImplicit();
- return D->isImplicit();
-}
-
static bool shouldSkipAliasEmission(const CodeGenModule &CGM,
const ValueDecl *Global) {
const LangOptions &LangOpts = CGM.getLangOpts();
@@ -4803,19 +4795,6 @@ static bool shouldSkipAliasEmission(const CodeGenModule &CGM,
return false;
}
-bool CodeGenModule::shouldEmitCUDAGlobalVar(const VarDecl *Global) const {
- assert(LangOpts.CUDA && "Should not be called by non-CUDA languages");
- // We need to emit host-side 'shadows' for all global
- // device-side variables because the CUDA runtime needs their
- // size and host-side address in order to provide access to
- // their device-side incarnations.
- return !LangOpts.CUDAIsDevice || Global->hasAttr<CUDADeviceAttr>() ||
- Global->hasAttr<CUDAConstantAttr>() ||
- Global->hasAttr<CUDASharedAttr>() ||
- Global->getType()->isCUDADeviceBuiltinSurfaceType() ||
- Global->getType()->isCUDADeviceBuiltinTextureType();
-}
-
void CodeGenModule::EmitGlobal(GlobalDecl GD) {
const auto *Global = cast<ValueDecl>(GD.getDecl());
@@ -4840,31 +4819,8 @@ void CodeGenModule::EmitGlobal(GlobalDecl GD) {
return emitCPUDispatchDefinition(GD);
// If this is CUDA, be selective about which declarations we emit.
- // Non-constexpr non-lambda implicit host device functions are not emitted
- // unless they are used on device side.
- if (LangOpts.CUDA) {
- assert((isa<FunctionDecl>(Global) || isa<VarDecl>(Global)) &&
- "Expected Variable or Function");
- if (const auto *VD = dyn_cast<VarDecl>(Global)) {
- if (!shouldEmitCUDAGlobalVar(VD))
- return;
- } else if (LangOpts.CUDAIsDevice) {
- const auto *FD = dyn_cast<FunctionDecl>(Global);
- if ((!Global->hasAttr<CUDADeviceAttr>() ||
- (LangOpts.OffloadImplicitHostDeviceTemplates &&
- hasImplicitAttr<CUDAHostAttr>(FD) &&
- hasImplicitAttr<CUDADeviceAttr>(FD) && !FD->isConstexpr() &&
- !isLambdaCallOperator(FD) &&
- !getContext().CUDAImplicitHostDeviceFunUsedByDevice.count(FD))) &&
- !Global->hasAttr<CUDAGlobalAttr>() &&
- !(LangOpts.HIPStdPar && isa<FunctionDecl>(Global) &&
- !Global->hasAttr<CUDAHostAttr>()))
- return;
- // Device-only functions are the only things we skip.
- } else if (!Global->hasAttr<CUDAHostAttr>() &&
- Global->hasAttr<CUDADeviceAttr>())
- return;
- }
+ if (LangOpts.CUDA && !getContext().shouldEmitCUDADecl(Global))
+ return;
if (LangOpts.OpenMP) {
// If this is OpenMP, check if it is legal to emit this global normally.
@@ -7056,7 +7012,7 @@ static void ReplaceUsesOfNonProtoTypeWithRealFunction(llvm::GlobalValue *Old,
void CodeGenModule::HandleCXXStaticMemberVarInstantiation(VarDecl *VD) {
auto DK = VD->isThisDeclarationADefinition();
if ((DK == VarDecl::Definition && VD->hasAttr<DLLImportAttr>()) ||
- (LangOpts.CUDA && !shouldEmitCUDAGlobalVar(VD)))
+ (LangOpts.CUDA && !getContext().shouldEmitCUDADecl(VD)))
return;
TemplateSpecializationKind TSK = VD->getTemplateSpecializationKind();
diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h
index 77ed7abc48a9e..d9f3901c0c67e 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -654,9 +654,6 @@ class CodeGenModule : public CodeGenTypeCache {
void createHLSLRuntime();
bool shouldEmitFunction(GlobalDecl GD);
- // Whether a global variable should be emitted by CUDA/HIP host/device
- // related attributes.
- bool shouldEmitCUDAGlobalVar(const VarDecl *VD) const;
bool shouldOpportunisticallyEmitVTables();
/// Map used to be sure we don't emit the same CompoundLiteral twice.
llvm::DenseMap<const CompoundLiteralExpr *, llvm::GlobalVariable *>
More information about the cfe-commits
mailing list