Skip to content

Commit e86d4fb

Browse files
authored
[CIR][CUDA] Handle local, __device__, __shared__, and __constant__ variables (#184248)
Support local, `__device__`, `__shared__`, and `__constant__` variables. Mark device variables as `externally_initialized`. References: #175871, #179278, llvm/clangir#1368, llvm/clangir#1394 --------- Signed-off-by: ZakyHermawan <zaky.hermawan9615@gmail.com>
1 parent 1b61537 commit e86d4fb

File tree

7 files changed

+233
-37
lines changed

7 files changed

+233
-37
lines changed

clang/include/clang/CIR/Dialect/IR/CIRCUDAAttrs.td

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,5 +36,17 @@ def CIR_CUDAKernelNameAttr : CIR_Attr<"CUDAKernelName", "cu.kernel_name"> {
3636
let assemblyFormat = "`<` $kernel_name `>`";
3737
}
3838

39+
def CUDAExternallyInitializedAttr : CIR_Attr<"CUDAExternallyInitialized",
40+
"cu.externally_initialized"> {
41+
let summary = "The marked variable is externally initialized.";
42+
let description =
43+
[{
44+
CUDA __device__ and __constant__ variables, along with surface and
45+
textures, might be initialized by host, hence "externally initialized".
46+
Therefore they must be emitted even if they are not referenced.
47+
48+
The attribute corresponds to the attribute on LLVM with the same name.
49+
}];
50+
}
3951

40-
#endif // CLANG_CIR_DIALECT_IR_CIRCUDAATTRS_TD
52+
#endif // CLANG_CIR_DIALECT_IR_CIRCUDAATTRS_TD

clang/lib/CIR/CodeGen/CIRGenDecl.cpp

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,13 @@
1414
#include "CIRGenFunction.h"
1515
#include "mlir/IR/Location.h"
1616
#include "clang/AST/Attr.h"
17+
#include "clang/AST/Attrs.inc"
1718
#include "clang/AST/Decl.h"
1819
#include "clang/AST/DeclOpenACC.h"
1920
#include "clang/AST/Expr.h"
2021
#include "clang/AST/ExprCXX.h"
22+
#include "clang/Basic/Cuda.h"
23+
#include "clang/CIR/Dialect/IR/CIRAttrs.h"
2124
#include "clang/CIR/MissingFeatures.h"
2225

2326
using namespace clang;
@@ -38,7 +41,7 @@ CIRGenFunction::emitAutoVarAlloca(const VarDecl &d,
3841
emission.isEscapingByRef = d.isEscapingByref();
3942
if (emission.isEscapingByRef)
4043
cgm.errorNYI(d.getSourceRange(),
41-
"emitAutoVarDecl: decl escaping by reference");
44+
"emitAutoVarAlloca: decl escaping by reference");
4245

4346
CharUnits alignment = getContext().getDeclAlign(&d);
4447

@@ -363,7 +366,7 @@ void CIRGenFunction::emitVarDecl(const VarDecl &d) {
363366
if (d.getType()->isSamplerT()) {
364367
// Nothing needs to be done here, but let's flag it as an error until we
365368
// have a test. It requires OpenCL support.
366-
cgm.errorNYI(d.getSourceRange(), "emitVarDecl static sampler type");
369+
cgm.errorNYI(d.getSourceRange(), "emitVarDecl: static sampler type");
367370
return;
368371
}
369372

@@ -378,7 +381,7 @@ void CIRGenFunction::emitVarDecl(const VarDecl &d) {
378381
}
379382

380383
if (d.getType().getAddressSpace() == LangAS::opencl_local)
381-
cgm.errorNYI(d.getSourceRange(), "emitVarDecl openCL address space");
384+
cgm.errorNYI(d.getSourceRange(), "emitVarDecl: openCL address space");
382385

383386
assert(d.hasLocalStorage());
384387

@@ -399,11 +402,14 @@ static std::string getStaticDeclName(CIRGenModule &cgm, const VarDecl &d) {
399402
if (const auto *fd = dyn_cast<FunctionDecl>(dc))
400403
contextName = std::string(cgm.getMangledName(fd));
401404
else if (isa<BlockDecl>(dc))
402-
cgm.errorNYI(d.getSourceRange(), "block decl context for static var");
405+
cgm.errorNYI(d.getSourceRange(),
406+
"getStaticDeclName: block decl context for static var");
403407
else if (isa<ObjCMethodDecl>(dc))
404-
cgm.errorNYI(d.getSourceRange(), "ObjC decl context for static var");
408+
cgm.errorNYI(d.getSourceRange(),
409+
"getStaticDeclName: ObjC decl context for static var");
405410
else
406-
cgm.errorNYI(d.getSourceRange(), "Unknown context for static var decl");
411+
cgm.errorNYI(d.getSourceRange(),
412+
"getStaticDeclName: Unknown context for static var decl");
407413

408414
contextName += "." + d.getNameAsString();
409415
return contextName;
@@ -433,12 +439,14 @@ CIRGenModule::getOrCreateStaticVarDecl(const VarDecl &d,
433439
mlir::Type lty = getTypes().convertTypeForMem(ty);
434440
assert(!cir::MissingFeatures::addressSpace());
435441

436-
if (d.hasAttr<LoaderUninitializedAttr>() || d.hasAttr<CUDASharedAttr>())
437-
errorNYI(d.getSourceRange(),
438-
"getOrCreateStaticVarDecl: LoaderUninitializedAttr");
439-
assert(!cir::MissingFeatures::addressSpace());
440-
441-
mlir::Attribute init = builder.getZeroInitAttr(convertType(ty));
442+
// OpenCL variables in local address space and CUDA shared
443+
// variables cannot have an initializer.
444+
mlir::Attribute init = nullptr;
445+
if (ty.getAddressSpace() == LangAS::opencl_local ||
446+
d.hasAttr<CUDASharedAttr>() || d.hasAttr<LoaderUninitializedAttr>())
447+
init = cir::UndefAttr::get(lty);
448+
else
449+
init = builder.getZeroInitAttr(convertType(ty));
442450

443451
cir::GlobalOp gv = builder.createVersionedGlobal(
444452
getModule(), getLoc(d.getLocation()), name, lty, false, linkage);
@@ -667,8 +675,24 @@ void CIRGenFunction::emitStaticVarDecl(const VarDecl &d,
667675

668676
// There are a lot of attributes that need to be handled here. Until
669677
// we start to support them, we just report an error if there are any.
670-
if (d.hasAttrs())
671-
cgm.errorNYI(d.getSourceRange(), "static var with attrs");
678+
if (d.hasAttr<AnnotateAttr>())
679+
cgm.errorNYI(d.getSourceRange(), "emitStaticVarDecl: Global annotations");
680+
if (d.getAttr<PragmaClangBSSSectionAttr>())
681+
cgm.errorNYI(d.getSourceRange(),
682+
"emitStaticVarDecl: CIR global BSS section attribute");
683+
if (d.getAttr<PragmaClangDataSectionAttr>())
684+
cgm.errorNYI(d.getSourceRange(),
685+
"emitStaticVarDecl: CIR global Data section attribute");
686+
if (d.getAttr<PragmaClangRodataSectionAttr>())
687+
cgm.errorNYI(d.getSourceRange(),
688+
"emitStaticVarDecl: CIR global Rodata section attribute");
689+
if (d.getAttr<PragmaClangRelroSectionAttr>())
690+
cgm.errorNYI(d.getSourceRange(),
691+
"emitStaticVarDecl: CIR global Relro section attribute");
692+
693+
if (d.getAttr<SectionAttr>())
694+
cgm.errorNYI(d.getSourceRange(),
695+
"emitStaticVarDecl: CIR global object file section attribute");
672696

673697
if (cgm.getCodeGenOpts().KeepPersistentStorageVariables)
674698
cgm.errorNYI(d.getSourceRange(), "static var keep persistent storage");

clang/lib/CIR/CodeGen/CIRGenModule.cpp

Lines changed: 84 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
#include "clang/AST/ASTContext.h"
2020
#include "clang/AST/ASTLambda.h"
21+
#include "clang/AST/Attrs.inc"
2122
#include "clang/AST/DeclBase.h"
2223
#include "clang/AST/DeclOpenACC.h"
2324
#include "clang/AST/GlobalDecl.h"
@@ -58,7 +59,7 @@ static CIRGenCXXABI *createCXXABI(CIRGenModule &cgm) {
5859
case TargetCXXABI::WebAssembly:
5960
case TargetCXXABI::XL:
6061
case TargetCXXABI::Microsoft:
61-
cgm.errorNYI("C++ ABI kind not yet implemented");
62+
cgm.errorNYI("createCXXABI: C++ ABI kind");
6263
return nullptr;
6364
}
6465

@@ -749,7 +750,8 @@ CIRGenModule::getOrCreateCIRGlobal(StringRef mangledName, mlir::Type ty,
749750
cir::GlobalOp entry;
750751
if (mlir::Operation *v = getGlobalValue(mangledName)) {
751752
if (!isa<cir::GlobalOp>(v))
752-
errorNYI(d->getSourceRange(), "global with non-GlobalOp type");
753+
errorNYI(d->getSourceRange(),
754+
"getOrCreateCIRGlobal: global with non-GlobalOp type");
753755
entry = cast<cir::GlobalOp>(v);
754756
}
755757

@@ -770,7 +772,8 @@ CIRGenModule::getOrCreateCIRGlobal(StringRef mangledName, mlir::Type ty,
770772
// recognizing the global as a declaration, for now only check if
771773
// initializer is present.
772774
if (isForDefinition && !entry.isDeclaration()) {
773-
errorNYI(d->getSourceRange(), "global with conflicting type");
775+
errorNYI(d->getSourceRange(),
776+
"getOrCreateCIRGlobal: global with conflicting type");
774777
}
775778

776779
// Address space check removed because it is unnecessary because CIR records
@@ -820,15 +823,16 @@ CIRGenModule::getOrCreateCIRGlobal(StringRef mangledName, mlir::Type ty,
820823
// Handle things which are present even on external declarations.
821824
if (d) {
822825
if (langOpts.OpenMP && !langOpts.OpenMPSimd)
823-
errorNYI(d->getSourceRange(), "OpenMP target global variable");
826+
errorNYI(d->getSourceRange(),
827+
"getOrCreateCIRGlobal: OpenMP target global variable");
824828

825829
gv.setAlignmentAttr(getSize(astContext.getDeclAlign(d)));
826830

827831
setLinkageForGV(gv, d);
828832

829833
if (d->getTLSKind()) {
830834
if (d->getTLSKind() == VarDecl::TLS_Dynamic)
831-
errorNYI(d->getSourceRange(), "TLS dynamic");
835+
errorNYI(d->getSourceRange(), "getOrCreateCIRGlobal: TLS dynamic");
832836
setTLSMode(gv, *d);
833837
}
834838

@@ -837,25 +841,41 @@ CIRGenModule::getOrCreateCIRGlobal(StringRef mangledName, mlir::Type ty,
837841
// If required by the ABI, treat declarations of static data members with
838842
// inline initializers as definitions.
839843
if (astContext.isMSStaticDataMemberInlineDefinition(d))
840-
errorNYI(d->getSourceRange(), "MS static data member inline definition");
844+
errorNYI(d->getSourceRange(),
845+
"getOrCreateCIRGlobal: MS static data member inline definition");
841846

842847
assert(!cir::MissingFeatures::opGlobalSection());
843848
gv.setGlobalVisibilityAttr(getGlobalVisibilityAttrFromDecl(d));
844849

845850
// Handle XCore specific ABI requirements.
846851
if (getTriple().getArch() == llvm::Triple::xcore)
847-
errorNYI(d->getSourceRange(), "XCore specific ABI requirements");
852+
errorNYI(d->getSourceRange(),
853+
"getOrCreateCIRGlobal: XCore specific ABI requirements");
848854

849855
// Check if we a have a const declaration with an initializer, we may be
850856
// able to emit it as available_externally to expose it's value to the
851857
// optimizer.
852858
if (getLangOpts().CPlusPlus && gv.isPublic() &&
853859
d->getType().isConstQualified() && gv.isDeclaration() &&
854860
!d->hasDefinition() && d->hasInit() && !d->hasAttr<DLLImportAttr>())
861+
errorNYI(
862+
d->getSourceRange(),
863+
"getOrCreateCIRGlobal: external const declaration with initializer");
864+
}
865+
866+
if (d &&
867+
d->isThisDeclarationADefinition(astContext) == VarDecl::DeclarationOnly) {
868+
getTargetCIRGenInfo().setTargetAttributes(d, gv, *this);
869+
// TODO(cir): set target attributes
870+
// External HIP managed variables needed to be recorded for transformation
871+
// in both device and host compilations.
872+
if (getLangOpts().CUDA && d && d->hasAttr<HIPManagedAttr>() &&
873+
d->hasExternalStorage())
855874
errorNYI(d->getSourceRange(),
856-
"external const declaration with initializer");
875+
"getOrCreateCIRGlobal: HIP managed attribute");
857876
}
858877

878+
assert(!cir::MissingFeatures::addressSpace());
859879
return gv;
860880
}
861881

@@ -907,7 +927,8 @@ cir::GlobalViewAttr CIRGenModule::getAddrOfGlobalVarAttr(const VarDecl *d) {
907927
void CIRGenModule::emitGlobalVarDefinition(const clang::VarDecl *vd,
908928
bool isTentative) {
909929
if (getLangOpts().OpenCL || getLangOpts().OpenMPIsTargetDevice) {
910-
errorNYI(vd->getSourceRange(), "emit OpenCL/OpenMP global variable");
930+
errorNYI(vd->getSourceRange(),
931+
"emitGlobalVarDefinition: emit OpenCL/OpenMP global variable");
911932
return;
912933
}
913934

@@ -939,9 +960,19 @@ void CIRGenModule::emitGlobalVarDefinition(const clang::VarDecl *vd,
939960

940961
assert(!cir::MissingFeatures::cudaSupport());
941962

942-
if (vd->hasAttr<LoaderUninitializedAttr>()) {
943-
errorNYI(vd->getSourceRange(), "loader uninitialized attribute");
944-
return;
963+
// CUDA E.2.4.1 "__shared__ variables cannot have an initialization
964+
// as part of their declaration." Sema has already checked for
965+
// error cases, so we just need to set Init to UndefValue.
966+
bool isCUDASharedVar =
967+
getLangOpts().CUDAIsDevice && vd->hasAttr<CUDASharedAttr>();
968+
// TODO(cir): implement isCUDAShadowVar and isCUDADeviceShadowVar, reference:
969+
// OGCG
970+
971+
if (getLangOpts().CUDA && isCUDASharedVar) {
972+
init = cir::UndefAttr::get(&getMLIRContext(), convertType(vd->getType()));
973+
} else if (vd->hasAttr<LoaderUninitializedAttr>()) {
974+
errorNYI(vd->getSourceRange(),
975+
"emitGlobalVarDefinition: loader uninitialized attribute");
945976
} else if (!initExpr) {
946977
// This is a tentative definition; tentative definitions are
947978
// implicitly initialized with { 0 }.
@@ -964,12 +995,14 @@ void CIRGenModule::emitGlobalVarDefinition(const clang::VarDecl *vd,
964995

965996
if (getLangOpts().CPlusPlus) {
966997
if (initDecl->hasFlexibleArrayInit(astContext))
967-
errorNYI(vd->getSourceRange(), "flexible array initializer");
998+
errorNYI(vd->getSourceRange(),
999+
"emitGlobalVarDefinition: flexible array initializer");
9681000
init = builder.getZeroInitAttr(convertType(qt));
9691001
if (!isDefinitionAvailableExternally)
9701002
needsGlobalCtor = true;
9711003
} else {
972-
errorNYI(vd->getSourceRange(), "static initializer");
1004+
errorNYI(vd->getSourceRange(),
1005+
"emitGlobalVarDefinition: static initializer");
9731006
}
9741007
} else {
9751008
init = initializer;
@@ -982,7 +1015,9 @@ void CIRGenModule::emitGlobalVarDefinition(const clang::VarDecl *vd,
9821015

9831016
mlir::Type initType;
9841017
if (mlir::isa<mlir::SymbolRefAttr>(init)) {
985-
errorNYI(vd->getSourceRange(), "global initializer is a symbol reference");
1018+
errorNYI(
1019+
vd->getSourceRange(),
1020+
"emitGlobalVarDefinition: global initializer is a symbol reference");
9861021
return;
9871022
} else {
9881023
assert(mlir::isa<mlir::TypedAttr>(init) && "This should have a type");
@@ -996,18 +1031,49 @@ void CIRGenModule::emitGlobalVarDefinition(const clang::VarDecl *vd,
9961031
// TODO(cir): Strip off pointer casts from Entry if we get them?
9971032

9981033
if (!gv || gv.getSymType() != initType) {
999-
errorNYI(vd->getSourceRange(), "global initializer with type mismatch");
1034+
errorNYI(vd->getSourceRange(),
1035+
"emitGlobalVarDefinition: global initializer with type mismatch");
10001036
return;
10011037
}
10021038

10031039
assert(!cir::MissingFeatures::maybeHandleStaticInExternC());
10041040

10051041
if (vd->hasAttr<AnnotateAttr>()) {
1006-
errorNYI(vd->getSourceRange(), "annotate global variable");
1042+
errorNYI(vd->getSourceRange(),
1043+
"emitGlobalVarDefinition: annotate global variable");
10071044
}
10081045

1046+
// Set CIR's linkage type as appropriate.
1047+
cir::GlobalLinkageKind linkage =
1048+
getCIRLinkageVarDefinition(vd, /*IsConstant=*/false);
1049+
1050+
// CUDA B.2.1 "The __device__ qualifier declares a variable that resides on
1051+
// the device. [...]"
1052+
// CUDA B.2.2 "The __constant__ qualifier, optionally used together with
1053+
// __device__, declares a variable that: [...]
1054+
// Is accessible from all the threads within the grid and from the host
1055+
// through the runtime library (cudaGetSymbolAddress() / cudaGetSymbolSize()
1056+
// / cudaMemcpyToSymbol() / cudaMemcpyFromSymbol())."
10091057
if (langOpts.CUDA) {
1010-
errorNYI(vd->getSourceRange(), "CUDA global variable");
1058+
if (langOpts.CUDAIsDevice) {
1059+
// __shared__ variables is not marked as externally initialized,
1060+
// because they must not be initialized.
1061+
if (linkage != cir::GlobalLinkageKind::InternalLinkage &&
1062+
!vd->isConstexpr() && !vd->getType().isConstQualified() &&
1063+
(vd->hasAttr<CUDADeviceAttr>() || vd->hasAttr<CUDAConstantAttr>() ||
1064+
vd->getType()->isCUDADeviceBuiltinSurfaceType() ||
1065+
vd->getType()->isCUDADeviceBuiltinTextureType())) {
1066+
gv->setAttr(cir::CUDAExternallyInitializedAttr::getMnemonic(),
1067+
cir::CUDAExternallyInitializedAttr::get(&getMLIRContext()));
1068+
}
1069+
} else {
1070+
// TODO(cir):
1071+
// Adjust linkage of shadow variables in host compilation
1072+
// getCUDARuntime().internalizeDeviceSideVar(vd, linkage);
1073+
}
1074+
// TODO(cir):
1075+
// Handle variable registration
1076+
// getCUDARuntime().handleVarRegistration(vd, gv);
10111077
}
10121078

10131079
// Set initializer and finalize emission
@@ -1024,10 +1090,6 @@ void CIRGenModule::emitGlobalVarDefinition(const clang::VarDecl *vd,
10241090
/*ExcludeDtor=*/true)));
10251091
assert(!cir::MissingFeatures::opGlobalSection());
10261092

1027-
// Set CIR's linkage type as appropriate.
1028-
cir::GlobalLinkageKind linkage =
1029-
getCIRLinkageVarDefinition(vd, /*IsConstant=*/false);
1030-
10311093
// Set CIR linkage and DLL storage class.
10321094
gv.setLinkage(linkage);
10331095
// FIXME(cir): setLinkage should likely set MLIR's visibility automatically.

clang/lib/CIR/CodeGen/TargetInfo.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,3 +91,12 @@ bool TargetCIRGenInfo::isNoProtoCallVariadic(
9191
// For everything else, we just prefer false unless we opt out.
9292
return false;
9393
}
94+
95+
clang::LangAS
96+
TargetCIRGenInfo::getGlobalVarAddressSpace(CIRGenModule &cgm,
97+
const clang::VarDecl *d) const {
98+
assert(!cgm.getLangOpts().OpenCL &&
99+
!(cgm.getLangOpts().CUDA && cgm.getLangOpts().CUDAIsDevice) &&
100+
"Address space agnostic languages only");
101+
return d ? d->getType().getAddressSpace() : LangAS::Default;
102+
}

clang/lib/CIR/CodeGen/TargetInfo.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,13 @@ class TargetCIRGenInfo {
4949
/// Returns ABI info helper for the target.
5050
const ABIInfo &getABIInfo() const { return *info; }
5151

52+
/// Get target favored AST address space of a global variable for languages
53+
/// other than OpenCL and CUDA.
54+
/// If \p d is nullptr, returns the default target favored address space
55+
/// for global variable.
56+
virtual clang::LangAS getGlobalVarAddressSpace(CIRGenModule &cgm,
57+
const clang::VarDecl *d) const;
58+
5259
/// Get the address space for alloca.
5360
virtual mlir::ptr::MemorySpaceAttrInterface getCIRAllocaAddressSpace() const {
5461
return cir::LangAddressSpaceAttr::get(&info->cgt.getMLIRContext(),
@@ -99,6 +106,15 @@ class TargetCIRGenInfo {
99106
/// right thing when calling a function with no know signature.
100107
virtual bool isNoProtoCallVariadic(const FunctionNoProtoType *fnType) const;
101108

109+
/// Provides a convenient hook to handle extra target-specific attributes
110+
/// for the given global.
111+
/// In OG, the function receives an llvm::GlobalValue. However, functions
112+
/// and global variables are separate types in Clang IR, so we use a general
113+
/// mlir::Operation*.
114+
virtual void setTargetAttributes(const clang::Decl *decl,
115+
mlir::Operation *global,
116+
CIRGenModule &module) const {}
117+
102118
virtual bool isScalarizableAsmOperand(CIRGenFunction &cgf,
103119
mlir::Type ty) const {
104120
return false;

0 commit comments

Comments
 (0)