//===- CIRGenModule.cpp - Per-Module state for CIR generation -------------===// // // 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 is the internal per-translation-unit state used for CIR translation. // //===----------------------------------------------------------------------===// #include "CIRGenCUDARuntime.h" #include "CIRGenCXXABI.h" #include "CIRGenCstEmitter.h" #include "CIRGenFunction.h" #include "CIRGenOpenMPRuntime.h" #include "CIRGenTBAA.h" #include "CIRGenTypes.h" #include "CIRGenValue.h" #include "TargetInfo.h" #include "mlir/Dialect/Func/IR/FuncOps.h" #include "mlir/Dialect/MemRef/IR/MemRef.h" #include "mlir/IR/Attributes.h" #include "mlir/IR/Builders.h" #include "mlir/IR/BuiltinAttributeInterfaces.h" #include "mlir/IR/BuiltinAttributes.h" #include "mlir/IR/BuiltinOps.h" #include "mlir/IR/BuiltinTypes.h" #include "mlir/IR/MLIRContext.h" #include "mlir/IR/OperationSupport.h" #include "mlir/IR/SymbolTable.h" #include "mlir/IR/Verifier.h" #include "clang/AST/Expr.h" #include "clang/Basic/Cuda.h" #include "clang/CIR/Dialect/IR/CIRTypes.h" #include "clang/CIR/MissingFeatures.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclGroup.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/EvaluatedExprVisitor.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" #include "clang/AST/GlobalDecl.h" #include "clang/AST/ParentMap.h" #include "clang/AST/RecordLayout.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/AST/StmtCXX.h" #include "clang/AST/StmtObjC.h" #include "clang/AST/Type.h" #include "clang/Basic/Diagnostic.h" #include "clang/Basic/LangStandard.h" #include "clang/Basic/NoSanitizeList.h" #include "clang/Basic/SourceLocation.h" #include "clang/CIR/CIRGenerator.h" #include "clang/CIR/Dialect/IR/CIRAttrs.h" #include "clang/CIR/Dialect/IR/CIRDialect.h" #include "clang/CIR/Dialect/IR/CIROpsEnums.h" #include "clang/CIR/LowerToLLVM.h" #include "clang/Frontend/FrontendDiagnostic.h" #include "clang/Lex/Preprocessor.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/BitVector.h" #include "llvm/ADT/MapVector.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/ScopedHashTable.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Casting.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/TimeProfiler.h" #include "llvm/Support/raw_ostream.h" #include <iterator> #include <numeric> using namespace cir; using namespace clang; using namespace clang::CIRGen; using llvm::cast; using llvm::dyn_cast; using llvm::isa; using llvm::SmallVector; using llvm::StringRef; static CIRGenCXXABI *createCXXABI(CIRGenModule &CGM) { switch (CGM.getASTContext().getCXXABIKind()) { case TargetCXXABI::GenericItanium: case TargetCXXABI::GenericAArch64: case TargetCXXABI::AppleARM64: return CreateCIRGenItaniumCXXABI(CGM); default: llvm_unreachable("invalid C++ ABI kind"); } } CIRGenModule::CIRGenModule(mlir::MLIRContext &mlirContext, clang::ASTContext &astContext, const clang::CodeGenOptions &CGO, DiagnosticsEngine &Diags) : builder(mlirContext, *this), astContext(astContext), langOpts(astContext.getLangOpts()), codeGenOpts(CGO), theModule{mlir::ModuleOp::create(builder.getUnknownLoc())}, Diags(Diags), target(astContext.getTargetInfo()), ABI(createCXXABI(*this)), genTypes{*this}, VTables{*this}, openMPRuntime(new CIRGenOpenMPRuntime(*this)), cudaRuntime(new CIRGenCUDARuntime(*this)) { // Initialize CIR signed integer types cache. SInt8Ty = cir::IntType::get(&getMLIRContext(), 8, /*isSigned=*/true); SInt16Ty = cir::IntType::get(&getMLIRContext(), 16, /*isSigned=*/true); SInt32Ty = cir::IntType::get(&getMLIRContext(), 32, /*isSigned=*/true); SInt64Ty = cir::IntType::get(&getMLIRContext(), 64, /*isSigned=*/true); SInt128Ty = cir::IntType::get(&getMLIRContext(), 128, /*isSigned=*/true); // Initialize CIR unsigned integer types cache. UInt8Ty = cir::IntType::get(&getMLIRContext(), 8, /*isSigned=*/false); UInt16Ty = cir::IntType::get(&getMLIRContext(), 16, /*isSigned=*/false); UInt32Ty = cir::IntType::get(&getMLIRContext(), 32, /*isSigned=*/false); UInt64Ty = cir::IntType::get(&getMLIRContext(), 64, /*isSigned=*/false); UInt128Ty = cir::IntType::get(&getMLIRContext(), 128, /*isSigned=*/false); VoidTy = cir::VoidType::get(&getMLIRContext()); // Initialize CIR pointer types cache. VoidPtrTy = cir::PointerType::get(&getMLIRContext(), VoidTy); VoidPtrPtrTy = cir::PointerType::get(&getMLIRContext(), VoidPtrTy); FP16Ty = cir::FP16Type::get(&getMLIRContext()); BFloat16Ty = cir::BF16Type::get(&getMLIRContext()); FloatTy = cir::SingleType::get(&getMLIRContext()); DoubleTy = cir::DoubleType::get(&getMLIRContext()); FP80Ty = cir::FP80Type::get(&getMLIRContext()); FP128Ty = cir::FP128Type::get(&getMLIRContext()); // TODO: PointerWidthInBits PointerAlignInBytes = astContext .toCharUnitsFromBits( astContext.getTargetInfo().getPointerAlign(LangAS::Default)) .getQuantity(); SizeSizeInBytes = astContext .toCharUnitsFromBits(astContext.getTargetInfo().getMaxPointerWidth()) .getQuantity(); // TODO: IntAlignInBytes UCharTy = cir::IntType::get(&getMLIRContext(), astContext.getTargetInfo().getCharWidth(), /*isSigned=*/false); UIntTy = cir::IntType::get(&getMLIRContext(), astContext.getTargetInfo().getIntWidth(), /*isSigned=*/false); UIntPtrTy = cir::IntType::get(&getMLIRContext(), astContext.getTargetInfo().getMaxPointerWidth(), /*isSigned=*/false); UInt8PtrTy = builder.getPointerTo(UInt8Ty); UInt8PtrPtrTy = builder.getPointerTo(UInt8PtrTy); AllocaInt8PtrTy = UInt8PtrTy; AllocaVoidPtrTy = VoidPtrTy; // TODO: GlobalsInt8PtrTy // TODO: ConstGlobalsPtrTy CIRAllocaAddressSpace = getTargetCIRGenInfo().getCIRAllocaAddressSpace(); PtrDiffTy = cir::IntType::get(&getMLIRContext(), astContext.getTargetInfo().getMaxPointerWidth(), /*isSigned=*/true); if (langOpts.OpenCL) { createOpenCLRuntime(); } cir::sob::SignedOverflowBehavior sob; switch (langOpts.getSignedOverflowBehavior()) { case clang::LangOptions::SignedOverflowBehaviorTy::SOB_Defined: sob = sob::SignedOverflowBehavior::defined; break; case clang::LangOptions::SignedOverflowBehaviorTy::SOB_Undefined: sob = sob::SignedOverflowBehavior::undefined; break; case clang::LangOptions::SignedOverflowBehaviorTy::SOB_Trapping: sob = sob::SignedOverflowBehavior::trapping; break; } // FIXME(cir): Implement a custom CIR Module Op and attributes to leverage // MLIR features. theModule->setAttr(cir::CIRDialect::getSOBAttrName(), cir::SignedOverflowBehaviorAttr::get(&mlirContext, sob)); auto lang = SourceLanguageAttr::get(&mlirContext, getCIRSourceLanguage()); theModule->setAttr(cir::CIRDialect::getLangAttrName(), cir::LangAttr::get(&mlirContext, lang)); theModule->setAttr(cir::CIRDialect::getTripleAttrName(), builder.getStringAttr(getTriple().str())); if (CGO.OptimizationLevel > 0 || CGO.OptimizeSize > 0) theModule->setAttr(cir::CIRDialect::getOptInfoAttrName(), cir::OptInfoAttr::get(&mlirContext, CGO.OptimizationLevel, CGO.OptimizeSize)); // Set the module name to be the name of the main file. TranslationUnitDecl // often contains invalid source locations and isn't a reliable source for the // module location. auto MainFileID = astContext.getSourceManager().getMainFileID(); const FileEntry &MainFile = *astContext.getSourceManager().getFileEntryForID(MainFileID); auto Path = MainFile.tryGetRealPathName(); if (!Path.empty()) { theModule.setSymName(Path); theModule->setLoc(mlir::FileLineColLoc::get(&mlirContext, Path, /*line=*/0, /*col=*/0)); } if (langOpts.Sanitize.has(SanitizerKind::Thread) || (!codeGenOpts.RelaxedAliasing && codeGenOpts.OptimizationLevel > 0)) { tbaa.reset(new CIRGenTBAA(&mlirContext, astContext, genTypes, theModule, codeGenOpts, langOpts)); } } CIRGenModule::~CIRGenModule() {} bool CIRGenModule::isTypeConstant(QualType Ty, bool ExcludeCtor, bool ExcludeDtor) { if (!Ty.isConstant(astContext) && !Ty->isReferenceType()) return false; if (astContext.getLangOpts().CPlusPlus) { if (const CXXRecordDecl *Record = astContext.getBaseElementType(Ty)->getAsCXXRecordDecl()) return ExcludeCtor && !Record->hasMutableFields() && (Record->hasTrivialDestructor() || ExcludeDtor); } return true; } /// FIXME: this could likely be a common helper and not necessarily related /// with codegen. /// Return the best known alignment for an unknown pointer to a /// particular class. CharUnits CIRGenModule::getClassPointerAlignment(const CXXRecordDecl *RD) { if (!RD->hasDefinition()) return CharUnits::One(); // Hopefully won't be used anywhere. auto &layout = astContext.getASTRecordLayout(RD); // If the class is final, then we know that the pointer points to an // object of that type and can use the full alignment. if (RD->isEffectivelyFinal()) return layout.getAlignment(); // Otherwise, we have to assume it could be a subclass. return layout.getNonVirtualAlignment(); } /// FIXME: this could likely be a common helper and not necessarily related /// with codegen. CharUnits CIRGenModule::getNaturalPointeeTypeAlignment( QualType ty, LValueBaseInfo *baseInfo, TBAAAccessInfo *tbaaInfo) { return getNaturalTypeAlignment(ty->getPointeeType(), baseInfo, tbaaInfo, /* forPointeeType= */ true); } /// FIXME: this could likely be a common helper and not necessarily related /// with codegen. /// TODO: Add TBAAAccessInfo CharUnits CIRGenModule::getNaturalTypeAlignment(QualType T, LValueBaseInfo *BaseInfo, TBAAAccessInfo *tbaaInfo, bool forPointeeType) { if (tbaaInfo) { *tbaaInfo = getTBAAAccessInfo(T); } // FIXME: This duplicates logic in ASTContext::getTypeAlignIfKnown. But // that doesn't return the information we need to compute BaseInfo. // Honor alignment typedef attributes even on incomplete types. // We also honor them straight for C++ class types, even as pointees; // there's an expressivity gap here. if (auto TT = T->getAs<TypedefType>()) { if (auto Align = TT->getDecl()->getMaxAlignment()) { if (BaseInfo) *BaseInfo = LValueBaseInfo(AlignmentSource::AttributedType); return astContext.toCharUnitsFromBits(Align); } } bool AlignForArray = T->isArrayType(); // Analyze the base element type, so we don't get confused by incomplete // array types. T = astContext.getBaseElementType(T); if (T->isIncompleteType()) { // We could try to replicate the logic from // ASTContext::getTypeAlignIfKnown, but nothing uses the alignment if the // type is incomplete, so it's impossible to test. We could try to reuse // getTypeAlignIfKnown, but that doesn't return the information we need // to set BaseInfo. So just ignore the possibility that the alignment is // greater than one. if (BaseInfo) *BaseInfo = LValueBaseInfo(AlignmentSource::Type); return CharUnits::One(); } if (BaseInfo) *BaseInfo = LValueBaseInfo(AlignmentSource::Type); CharUnits Alignment; const CXXRecordDecl *RD; if (T.getQualifiers().hasUnaligned()) { Alignment = CharUnits::One(); } else if (forPointeeType && !AlignForArray && (RD = T->getAsCXXRecordDecl())) { // For C++ class pointees, we don't know whether we're pointing at a // base or a complete object, so we generally need to use the // non-virtual alignment. Alignment = getClassPointerAlignment(RD); } else { Alignment = astContext.getTypeAlignInChars(T); } // Cap to the global maximum type alignment unless the alignment // was somehow explicit on the type. if (unsigned MaxAlign = astContext.getLangOpts().MaxTypeAlign) { if (Alignment.getQuantity() > MaxAlign && !astContext.isAlignmentRequired(T)) Alignment = CharUnits::fromQuantity(MaxAlign); } return Alignment; } bool CIRGenModule::MustBeEmitted(const ValueDecl *Global) { // Never defer when EmitAllDecls is specified. assert(!langOpts.EmitAllDecls && "EmitAllDecls NYI"); assert(!codeGenOpts.KeepStaticConsts && "KeepStaticConsts NYI"); return getASTContext().DeclMustBeEmitted(Global); } bool CIRGenModule::MayBeEmittedEagerly(const ValueDecl *Global) { // In OpenMP 5.0 variables and function may be marked as // device_type(host/nohost) and we should not emit them eagerly unless we sure // that they must be emitted on the host/device. To be sure we need to have // seen a declare target with an explicit mentioning of the function, we know // we have if the level of the declare target attribute is -1. Note that we // check somewhere else if we should emit this at all. if (langOpts.OpenMP >= 50 && !langOpts.OpenMPSimd) { std::optional<OMPDeclareTargetDeclAttr *> ActiveAttr = OMPDeclareTargetDeclAttr::getActiveAttr(Global); if (!ActiveAttr || (*ActiveAttr)->getLevel() != (unsigned)-1) return false; } const auto *FD = dyn_cast<FunctionDecl>(Global); if (FD) { // Implicit template instantiations may change linkage if they are later // explicitly instantiated, so they should not be emitted eagerly. // TODO(cir): do we care? assert(FD->getTemplateSpecializationKind() != TSK_ImplicitInstantiation && "not implemented"); assert(!FD->isTemplated() && "Templates NYI"); } const auto *VD = dyn_cast<VarDecl>(Global); if (VD) // A definition of an inline constexpr static data member may change // linkage later if it's redeclared outside the class. // TODO(cir): do we care? assert(astContext.getInlineVariableDefinitionKind(VD) != ASTContext::InlineVariableDefinitionKind::WeakUnknown && "not implemented"); // If OpenMP is enabled and threadprivates must be generated like TLS, delay // codegen for global variables, because they may be marked as threadprivate. if (langOpts.OpenMP && langOpts.OpenMPUseTLS && getASTContext().getTargetInfo().isTLSSupported() && isa<VarDecl>(Global) && !Global->getType().isConstantStorage(getASTContext(), false, false) && !OMPDeclareTargetDeclAttr::isDeclareTargetDeclaration(Global)) return false; assert((FD || VD) && "Only FunctionDecl and VarDecl should hit this path so far."); return true; } static bool shouldAssumeDSOLocal(const CIRGenModule &CGM, CIRGlobalValueInterface GV) { if (GV.hasLocalLinkage()) return true; if (!GV.hasDefaultVisibility() && !GV.hasExternalWeakLinkage()) { return true; } // DLLImport explicitly marks the GV as external. // so it shouldn't be dso_local // But we don't have the info set now assert(!cir::MissingFeatures::setDLLImportDLLExport()); const llvm::Triple &TT = CGM.getTriple(); const auto &CGOpts = CGM.getCodeGenOpts(); if (TT.isWindowsGNUEnvironment()) { // In MinGW, variables without DLLImport can still be automatically // imported from a DLL by the linker; don't mark variables that // potentially could come from another DLL as DSO local. // With EmulatedTLS, TLS variables can be autoimported from other DLLs // (and this actually happens in the public interface of libstdc++), so // such variables can't be marked as DSO local. (Native TLS variables // can't be dllimported at all, though.) llvm_unreachable("MinGW not supported here"); } // On COFF, don't mark 'extern_weak' symbols as DSO local. If these symbols // remain unresolved in the link, they can be resolved to zero, which is // outside the current DSO. if (TT.isOSBinFormatCOFF() && GV.hasExternalWeakLinkage()) return false; // Every other GV is local on COFF. // Make an exception for windows OS in the triple: Some firmware builds use // *-win32-macho triples. This (accidentally?) produced windows relocations // without GOT tables in older clang versions; Keep this behaviour. // FIXME: even thread local variables? if (TT.isOSBinFormatCOFF() || (TT.isOSWindows() && TT.isOSBinFormatMachO())) return true; // Only handle COFF and ELF for now. if (!TT.isOSBinFormatELF()) return false; llvm::Reloc::Model RM = CGOpts.RelocationModel; const auto &LOpts = CGM.getLangOpts(); if (RM != llvm::Reloc::Static && !LOpts.PIE) { // On ELF, if -fno-semantic-interposition is specified and the target // supports local aliases, there will be neither CC1 // -fsemantic-interposition nor -fhalf-no-semantic-interposition. Set // dso_local on the function if using a local alias is preferable (can avoid // PLT indirection). if (!(isa<cir::FuncOp>(GV) && GV.canBenefitFromLocalAlias())) { return false; } return !(CGM.getLangOpts().SemanticInterposition || CGM.getLangOpts().HalfNoSemanticInterposition); } // A definition cannot be preempted from an executable. if (!GV.isDeclarationForLinker()) return true; // Most PIC code sequences that assume that a symbol is local cannot produce a // 0 if it turns out the symbol is undefined. While this is ABI and relocation // depended, it seems worth it to handle it here. if (RM == llvm::Reloc::PIC_ && GV.hasExternalWeakLinkage()) return false; // PowerPC64 prefers TOC indirection to avoid copy relocations. if (TT.isPPC64()) return false; if (CGOpts.DirectAccessExternalData) { // If -fdirect-access-external-data (default for -fno-pic), set dso_local // for non-thread-local variables. If the symbol is not defined in the // executable, a copy relocation will be needed at link time. dso_local is // excluded for thread-local variables because they generally don't support // copy relocations. if (auto gv = dyn_cast<cir::GlobalOp>(GV.getOperation())) if (!gv.getTlsModelAttr()) return true; // -fno-pic sets dso_local on a function declaration to allow direct // accesses when taking its address (similar to a data symbol). If the // function is not defined in the executable, a canonical PLT entry will be // needed at link time. -fno-direct-access-external-data can avoid the // canonical PLT entry. We don't generalize this condition to -fpie/-fpic as // it could just cause trouble without providing perceptible benefits. if (isa<cir::FuncOp>(GV) && !CGOpts.NoPLT && RM == llvm::Reloc::Static) return true; } // If we can use copy relocations we can assume it is local. // Otherwise don't assume it is local. return false; } void CIRGenModule::setDSOLocal(CIRGlobalValueInterface GV) const { GV.setDSOLocal(shouldAssumeDSOLocal(*this, GV)); } const ABIInfo &CIRGenModule::getABIInfo() { return getTargetCIRGenInfo().getABIInfo(); } void CIRGenModule::emitGlobal(GlobalDecl GD) { llvm::TimeTraceScope scope("build CIR Global", [&]() -> std::string { auto *ND = dyn_cast<NamedDecl>(GD.getDecl()); if (!ND) // TODO: How to print decls which is not named decl? return "Unnamed decl"; std::string Name; llvm::raw_string_ostream OS(Name); ND->getNameForDiagnostic(OS, getASTContext().getPrintingPolicy(), /*Qualified=*/true); return Name; }); const auto *Global = cast<ValueDecl>(GD.getDecl()); assert(!Global->hasAttr<IFuncAttr>() && "NYI"); assert(!Global->hasAttr<CPUDispatchAttr>() && "NYI"); if (langOpts.CUDA || langOpts.HIP) { // clang uses the same flag when building HIP code if (langOpts.CUDAIsDevice) { // This will implicitly mark templates and their // specializations as __host__ __device__. if (langOpts.OffloadImplicitHostDeviceTemplates) llvm_unreachable("NYI"); // This maps some parallel standard libraries implicitly // to GPU, even when they are not marked __device__. if (langOpts.HIPStdPar) llvm_unreachable("NYI"); // Global functions reside on device, so it shouldn't be skipped. if (!Global->hasAttr<CUDAGlobalAttr>() && !Global->hasAttr<CUDADeviceAttr>()) return; } else { // We must skip __device__ functions when compiling for host. if (!Global->hasAttr<CUDAHostAttr>() && Global->hasAttr<CUDADeviceAttr>()) { return; } } if (dyn_cast<VarDecl>(Global)) llvm_unreachable("NYI"); } if (langOpts.OpenMP) { // If this is OpenMP, check if it is legal to emit this global normally. if (openMPRuntime && openMPRuntime->emitTargetGlobal(GD)) { assert(!cir::MissingFeatures::openMPRuntime()); return; } if (auto *DRD = dyn_cast<OMPDeclareReductionDecl>(Global)) { assert(!cir::MissingFeatures::openMP()); return; } if (auto *DMD = dyn_cast<OMPDeclareMapperDecl>(Global)) { assert(!cir::MissingFeatures::openMP()); return; } } // Ignore declarations, they will be emitted on their first use. if (const auto *FD = dyn_cast<FunctionDecl>(Global)) { // Update deferred annotations with the latest declaration if the function // was already used or defined. if (FD->hasAttr<AnnotateAttr>()) { StringRef MangledName = getMangledName(GD); if (getGlobalValue(MangledName)) deferredAnnotations[MangledName] = FD; } // Forward declarations are emitted lazily on first use. if (!FD->doesThisDeclarationHaveABody()) { if (!FD->doesDeclarationForceExternallyVisibleDefinition()) return; llvm::StringRef MangledName = getMangledName(GD); // Compute the function info and CIR type. const auto &FI = getTypes().arrangeGlobalDeclaration(GD); mlir::Type Ty = getTypes().GetFunctionType(FI); GetOrCreateCIRFunction(MangledName, Ty, GD, /*ForVTable=*/false, /*DontDefer=*/false); return; } } else { assert(!langOpts.CUDA && "NYI"); const auto *VD = cast<VarDecl>(Global); assert(VD->isFileVarDecl() && "Cannot emit local var decl as global."); if (VD->isThisDeclarationADefinition() != VarDecl::Definition && !astContext.isMSStaticDataMemberInlineDefinition(VD)) { if (langOpts.OpenMP) { // Emit declaration of the must-be-emitted declare target variable. if (std::optional<OMPDeclareTargetDeclAttr::MapTypeTy> Res = OMPDeclareTargetDeclAttr::isDeclareTargetDeclaration(VD)) { assert(0 && "OMPDeclareTargetDeclAttr NYI"); } } // If this declaration may have caused an inline variable definition to // change linkage, make sure that it's emitted. if (astContext.getInlineVariableDefinitionKind(VD) == ASTContext::InlineVariableDefinitionKind::Strong) getAddrOfGlobalVar(VD); return; } } // Defer code generation to first use when possible, e.g. if this is an inline // function. If the global mjust always be emitted, do it eagerly if possible // to benefit from cache locality. if (MustBeEmitted(Global) && MayBeEmittedEagerly(Global)) { // Emit the definition if it can't be deferred. emitGlobalDefinition(GD); return; } // If we're deferring emission of a C++ variable with an initializer, remember // the order in which it appeared on the file. if (getLangOpts().CPlusPlus && isa<VarDecl>(Global) && cast<VarDecl>(Global)->hasInit()) { DelayedCXXInitPosition[Global] = CXXGlobalInits.size(); CXXGlobalInits.push_back(nullptr); } llvm::StringRef MangledName = getMangledName(GD); if (getGlobalValue(MangledName) != nullptr) { // The value has already been used and should therefore be emitted. addDeferredDeclToEmit(GD); } else if (MustBeEmitted(Global)) { // The value must be emitted, but cannot be emitted eagerly. assert(!MayBeEmittedEagerly(Global)); addDeferredDeclToEmit(GD); } else { // Otherwise, remember that we saw a deferred decl with this name. The first // use of the mangled name will cause it to move into DeferredDeclsToEmit. DeferredDecls[MangledName] = GD; } } void CIRGenModule::emitGlobalFunctionDefinition(GlobalDecl GD, mlir::Operation *Op) { auto const *D = cast<FunctionDecl>(GD.getDecl()); // Compute the function info and CIR type. const CIRGenFunctionInfo &FI = getTypes().arrangeGlobalDeclaration(GD); auto Ty = getTypes().GetFunctionType(FI); // Get or create the prototype for the function. auto Fn = dyn_cast_if_present<cir::FuncOp>(Op); if (!Fn || Fn.getFunctionType() != Ty) { Fn = GetAddrOfFunction(GD, Ty, /*ForVTable=*/false, /*DontDefer=*/true, ForDefinition); } // Already emitted. if (!Fn.isDeclaration()) return; setFunctionLinkage(GD, Fn); setGVProperties(Fn, D); // TODO(cir): MaubeHandleStaticInExternC // TODO(cir): maybeSetTrivialComdat // TODO(cir): setLLVMFunctionFEnvAttributes CIRGenFunction CGF{*this, builder}; CurCGF = &CGF; { mlir::OpBuilder::InsertionGuard guard(builder); CGF.generateCode(GD, Fn, FI); } CurCGF = nullptr; setNonAliasAttributes(GD, Fn); setCIRFunctionAttributesForDefinition(D, Fn); if (const ConstructorAttr *CA = D->getAttr<ConstructorAttr>()) AddGlobalCtor(Fn, CA->getPriority()); if (const DestructorAttr *DA = D->getAttr<DestructorAttr>()) AddGlobalDtor(Fn, DA->getPriority(), true); if (D->getAttr<AnnotateAttr>()) deferredAnnotations[getMangledName(GD)] = cast<ValueDecl>(D); } /// Track functions to be called before main() runs. void CIRGenModule::AddGlobalCtor(cir::FuncOp Ctor, int Priority) { // FIXME(cir): handle LexOrder and Associated data upon testcases. // // Traditional LLVM codegen directly adds the function to the list of global // ctors. In CIR we just add a global_ctor attribute to the function. The // global list is created in LoweringPrepare. // // FIXME(from traditional LLVM): Type coercion of void()* types. Ctor->setAttr( Ctor.getGlobalCtorAttrName(), cir::GlobalCtorAttr::get(&getMLIRContext(), Ctor.getName(), Priority)); } /// Add a function to the list that will be called when the module is unloaded. void CIRGenModule::AddGlobalDtor(cir::FuncOp Dtor, int Priority, bool IsDtorAttrFunc) { assert(IsDtorAttrFunc && "NYI"); if (codeGenOpts.RegisterGlobalDtorsWithAtExit && (!getASTContext().getTargetInfo().getTriple().isOSAIX() || IsDtorAttrFunc)) { llvm_unreachable("NYI"); } // FIXME(from traditional LLVM): Type coercion of void()* types. Dtor->setAttr( Dtor.getGlobalDtorAttrName(), cir::GlobalDtorAttr::get(&getMLIRContext(), Dtor.getName(), Priority)); } mlir::Operation *CIRGenModule::getGlobalValue(StringRef Name) { auto global = mlir::SymbolTable::lookupSymbolIn(theModule, Name); if (!global) return {}; return global; } mlir::Value CIRGenModule::getGlobalValue(const Decl *D) { assert(CurCGF); return CurCGF->symbolTable.lookup(D); } cir::GlobalOp CIRGenModule::createGlobalOp(CIRGenModule &cgm, mlir::Location loc, StringRef name, mlir::Type t, bool isConstant, cir::AddressSpaceAttr addrSpace, mlir::Operation *insertPoint, cir::GlobalLinkageKind linkage) { cir::GlobalOp g; auto &builder = cgm.getBuilder(); { mlir::OpBuilder::InsertionGuard guard(builder); // Some global emissions are triggered while emitting a function, e.g. // void s() { const char *s = "yolo"; ... } // // Be sure to insert global before the current function auto *curCGF = cgm.getCurrCIRGenFun(); if (curCGF) builder.setInsertionPoint(curCGF->CurFn); g = builder.create<cir::GlobalOp>(loc, name, t, isConstant, linkage, addrSpace); if (!curCGF) { if (insertPoint) cgm.getModule().insert(insertPoint, g); else cgm.getModule().push_back(g); } // Default to private until we can judge based on the initializer, // since MLIR doesn't allow public declarations. mlir::SymbolTable::setSymbolVisibility( g, mlir::SymbolTable::Visibility::Private); } return g; } void CIRGenModule::setCommonAttributes(GlobalDecl GD, mlir::Operation *GV) { const Decl *D = GD.getDecl(); if (isa_and_nonnull<NamedDecl>(D)) setGVProperties(GV, dyn_cast<NamedDecl>(D)); else assert(!cir::MissingFeatures::setDefaultVisibility()); if (D && D->hasAttr<UsedAttr>()) assert(!cir::MissingFeatures::addUsedOrCompilerUsedGlobal()); if (const auto *VD = dyn_cast_if_present<VarDecl>(D); VD && ((codeGenOpts.KeepPersistentStorageVariables && (VD->getStorageDuration() == SD_Static || VD->getStorageDuration() == SD_Thread)) || (codeGenOpts.KeepStaticConsts && VD->getStorageDuration() == SD_Static && VD->getType().isConstQualified()))) assert(!cir::MissingFeatures::addUsedOrCompilerUsedGlobal()); } void CIRGenModule::setNonAliasAttributes(GlobalDecl GD, mlir::Operation *GO) { const Decl *D = GD.getDecl(); setCommonAttributes(GD, GO); if (D) { auto GV = llvm::dyn_cast_or_null<cir::GlobalOp>(GO); if (GV) { if (D->hasAttr<RetainAttr>()) assert(!cir::MissingFeatures::addUsedGlobal()); if (auto *SA = D->getAttr<PragmaClangBSSSectionAttr>()) assert(!cir::MissingFeatures::addSectionAttributes()); if (auto *SA = D->getAttr<PragmaClangDataSectionAttr>()) assert(!cir::MissingFeatures::addSectionAttributes()); if (auto *SA = D->getAttr<PragmaClangRodataSectionAttr>()) assert(!cir::MissingFeatures::addSectionAttributes()); if (auto *SA = D->getAttr<PragmaClangRelroSectionAttr>()) assert(!cir::MissingFeatures::addSectionAttributes()); } auto F = llvm::dyn_cast_or_null<cir::FuncOp>(GO); if (F) { if (D->hasAttr<RetainAttr>()) assert(!cir::MissingFeatures::addUsedGlobal()); if (auto *SA = D->getAttr<PragmaClangTextSectionAttr>()) if (!D->getAttr<SectionAttr>()) assert(!cir::MissingFeatures::setSectionForFuncOp()); assert(!cir::MissingFeatures::updateCPUAndFeaturesAttributes()); } if (const auto *CSA = D->getAttr<CodeSegAttr>()) { assert(!cir::MissingFeatures::setSectionForFuncOp()); if (GV) GV.setSection(CSA->getName()); if (F) assert(!cir::MissingFeatures::setSectionForFuncOp()); } else if (const auto *SA = D->getAttr<SectionAttr>()) if (GV) GV.setSection(SA->getName()); if (F) assert(!cir::MissingFeatures::setSectionForFuncOp()); } assert(!cir::MissingFeatures::setTargetAttributes()); } static llvm::SmallVector<int64_t> indexesOfArrayAttr(mlir::ArrayAttr indexes) { llvm::SmallVector<int64_t> inds; for (mlir::Attribute i : indexes) { auto ind = dyn_cast<mlir::IntegerAttr>(i); assert(ind && "expect MLIR integer attribute"); inds.push_back(ind.getValue().getSExtValue()); } return inds; } static bool isViewOnGlobal(GlobalOp glob, GlobalViewAttr view) { return view.getSymbol().getValue() == glob.getSymName(); } static GlobalViewAttr createNewGlobalView(CIRGenModule &CGM, GlobalOp newGlob, GlobalViewAttr attr, mlir::Type oldTy) { if (!attr.getIndices() || !isViewOnGlobal(newGlob, attr)) return attr; llvm::SmallVector<int64_t> oldInds = indexesOfArrayAttr(attr.getIndices()); llvm::SmallVector<int64_t> newInds; CIRGenBuilderTy &bld = CGM.getBuilder(); const CIRDataLayout &layout = CGM.getDataLayout(); mlir::MLIRContext *ctxt = bld.getContext(); auto newTy = newGlob.getSymType(); auto offset = bld.computeOffsetFromGlobalViewIndices(layout, oldTy, oldInds); bld.computeGlobalViewIndicesFromFlatOffset(offset, newTy, layout, newInds); cir::PointerType newPtrTy; if (isa<cir::StructType>(oldTy)) newPtrTy = cir::PointerType::get(ctxt, newTy); else if (cir::ArrayType oldArTy = dyn_cast<cir::ArrayType>(oldTy)) newPtrTy = dyn_cast<cir::PointerType>(attr.getType()); if (newPtrTy) return bld.getGlobalViewAttr(newPtrTy, newGlob, newInds); llvm_unreachable("NYI"); } static mlir::Attribute getNewInitValue(CIRGenModule &CGM, GlobalOp newGlob, mlir::Type oldTy, GlobalOp user, mlir::Attribute oldInit) { if (auto oldView = mlir::dyn_cast<cir::GlobalViewAttr>(oldInit)) { return createNewGlobalView(CGM, newGlob, oldView, oldTy); } else if (auto oldArray = mlir::dyn_cast<ConstArrayAttr>(oldInit)) { llvm::SmallVector<mlir::Attribute> newArray; auto eltsAttr = dyn_cast<mlir::ArrayAttr>(oldArray.getElts()); for (auto elt : eltsAttr) { if (auto view = dyn_cast<GlobalViewAttr>(elt)) newArray.push_back(createNewGlobalView(CGM, newGlob, view, oldTy)); else if (auto view = dyn_cast<ConstArrayAttr>(elt)) newArray.push_back(getNewInitValue(CGM, newGlob, oldTy, user, elt)); } auto &builder = CGM.getBuilder(); mlir::Attribute ar = mlir::ArrayAttr::get(builder.getContext(), newArray); return builder.getConstArray(ar, cast<cir::ArrayType>(oldArray.getType())); } else { llvm_unreachable("NYI"); } } void CIRGenModule::replaceGlobal(cir::GlobalOp Old, cir::GlobalOp New) { assert(Old.getSymName() == New.getSymName() && "symbol names must match"); // If the types does not match, update all references to Old to the new type. auto OldTy = Old.getSymType(); auto NewTy = New.getSymType(); cir::AddressSpaceAttr oldAS = Old.getAddrSpaceAttr(); cir::AddressSpaceAttr newAS = New.getAddrSpaceAttr(); // TODO(cir): If the AS differs, we should also update all references. if (oldAS != newAS) { llvm_unreachable("NYI"); } if (OldTy != NewTy) { auto OldSymUses = Old.getSymbolUses(theModule.getOperation()); if (OldSymUses.has_value()) { for (auto Use : *OldSymUses) { auto *UserOp = Use.getUser(); assert((isa<cir::GetGlobalOp>(UserOp) || isa<cir::GlobalOp>(UserOp)) && "GlobalOp symbol user is neither a GetGlobalOp nor a GlobalOp"); if (auto GGO = dyn_cast<cir::GetGlobalOp>(Use.getUser())) { auto UseOpResultValue = GGO.getAddr(); UseOpResultValue.setType( cir::PointerType::get(&getMLIRContext(), NewTy)); mlir::OpBuilder::InsertionGuard guard(builder); builder.setInsertionPointAfter(GGO); mlir::Type ptrTy = builder.getPointerTo(OldTy); mlir::Value cast = builder.createBitcast(GGO->getLoc(), UseOpResultValue, ptrTy); UseOpResultValue.replaceAllUsesExcept(cast, cast.getDefiningOp()); } else if (auto glob = dyn_cast<cir::GlobalOp>(UserOp)) { if (auto init = glob.getInitialValue()) { auto nw = getNewInitValue(*this, New, OldTy, glob, init.value()); glob.setInitialValueAttr(nw); } } } } } // Remove old global from the module. Old.erase(); } cir::TLS_Model CIRGenModule::GetDefaultCIRTLSModel() const { switch (getCodeGenOpts().getDefaultTLSModel()) { case CodeGenOptions::GeneralDynamicTLSModel: return cir::TLS_Model::GeneralDynamic; case CodeGenOptions::LocalDynamicTLSModel: return cir::TLS_Model::LocalDynamic; case CodeGenOptions::InitialExecTLSModel: return cir::TLS_Model::InitialExec; case CodeGenOptions::LocalExecTLSModel: return cir::TLS_Model::LocalExec; } llvm_unreachable("Invalid TLS model!"); } void CIRGenModule::setTLSMode(mlir::Operation *Op, const VarDecl &D) const { assert(D.getTLSKind() && "setting TLS mode on non-TLS var!"); auto TLM = GetDefaultCIRTLSModel(); // Override the TLS model if it is explicitly specified. if (const TLSModelAttr *Attr = D.getAttr<TLSModelAttr>()) { llvm_unreachable("NYI"); } auto global = dyn_cast<cir::GlobalOp>(Op); assert(global && "NYI for other operations"); global.setTlsModel(TLM); } /// If the specified mangled name is not in the module, /// create and return an mlir GlobalOp with the specified type (TODO(cir): /// address space). /// /// TODO(cir): /// 1. If there is something in the module with the specified name, return /// it potentially bitcasted to the right type. /// /// 2. If D is non-null, it specifies a decl that correspond to this. This is /// used to set the attributes on the global when it is first created. /// /// 3. If IsForDefinition is true, it is guaranteed that an actual global with /// type Ty will be returned, not conversion of a variable with the same /// mangled name but some other type. cir::GlobalOp CIRGenModule::getOrCreateCIRGlobal(StringRef MangledName, mlir::Type Ty, LangAS langAS, const VarDecl *D, ForDefinition_t IsForDefinition) { // Lookup the entry, lazily creating it if necessary. cir::GlobalOp Entry; if (auto *V = getGlobalValue(MangledName)) { assert(isa<cir::GlobalOp>(V) && "only supports GlobalOp for now"); Entry = dyn_cast_or_null<cir::GlobalOp>(V); } cir::AddressSpaceAttr cirAS = builder.getAddrSpaceAttr(langAS); if (Entry) { auto entryCIRAS = Entry.getAddrSpaceAttr(); if (WeakRefReferences.erase(Entry)) { if (D && !D->hasAttr<WeakAttr>()) { auto LT = cir::GlobalLinkageKind::ExternalLinkage; Entry.setLinkageAttr( cir::GlobalLinkageKindAttr::get(&getMLIRContext(), LT)); mlir::SymbolTable::setSymbolVisibility(Entry, getMLIRVisibility(Entry)); } } // Handle dropped DLL attributes. if (D && !D->hasAttr<clang::DLLImportAttr>() && !D->hasAttr<clang::DLLExportAttr>()) assert(!cir::MissingFeatures::setDLLStorageClass() && "NYI"); if (langOpts.OpenMP && !langOpts.OpenMPSimd && D) getOpenMPRuntime().registerTargetGlobalVariable(D, Entry); if (Entry.getSymType() == Ty && entryCIRAS == cirAS) return Entry; // If there are two attempts to define the same mangled name, issue an // error. // // TODO(cir): look at mlir::GlobalValue::isDeclaration for all aspects of // recognizing the global as a declaration, for now only check if // initializer is present. if (IsForDefinition && !Entry.isDeclaration()) { GlobalDecl OtherGD; const VarDecl *OtherD; // Check that D is not yet in DiagnosedConflictingDefinitions is required // to make sure that we issue an error only once. if (D && lookupRepresentativeDecl(MangledName, OtherGD) && (D->getCanonicalDecl() != OtherGD.getCanonicalDecl().getDecl()) && (OtherD = dyn_cast<VarDecl>(OtherGD.getDecl())) && OtherD->hasInit() && DiagnosedConflictingDefinitions.insert(D).second) { getDiags().Report(D->getLocation(), diag::err_duplicate_mangled_name) << MangledName; getDiags().Report(OtherGD.getDecl()->getLocation(), diag::note_previous_definition); } } // TODO(cir): LLVM codegen makes sure the result is of the correct type // by issuing a address space cast. if (entryCIRAS != cirAS) llvm_unreachable("NYI"); // (If global is requested for a definition, we always need to create a new // global, not just return a bitcast.) if (!IsForDefinition) return Entry; } auto declCIRAS = builder.getAddrSpaceAttr(getGlobalVarAddressSpace(D)); // TODO(cir): do we need to strip pointer casts for Entry? auto loc = getLoc(D->getSourceRange()); // mlir::SymbolTable::Visibility::Public is the default, no need to explicitly // mark it as such. auto GV = CIRGenModule::createGlobalOp(*this, loc, MangledName, Ty, /*isConstant=*/false, /*addrSpace=*/declCIRAS, /*insertPoint=*/Entry.getOperation()); // If we already created a global with the same mangled name (but different // type) before, replace it with the new global. if (Entry) { replaceGlobal(Entry, GV); } // This is the first use or definition of a mangled name. If there is a // deferred decl with this name, remember that we need to emit it at the end // of the file. auto DDI = DeferredDecls.find(MangledName); if (DDI != DeferredDecls.end()) { // Move the potentially referenced deferred decl to the DeferredDeclsToEmit // list, and remove it from DeferredDecls (since we don't need it anymore). addDeferredDeclToEmit(DDI->second); DeferredDecls.erase(DDI); } // Handle things which are present even on external declarations. if (D) { if (langOpts.OpenMP && !langOpts.OpenMPSimd && D) getOpenMPRuntime().registerTargetGlobalVariable(D, Entry); // FIXME: This code is overly simple and should be merged with other global // handling. GV.setAlignmentAttr(getSize(astContext.getDeclAlign(D))); GV.setConstant(isTypeConstant(D->getType(), false, false)); // TODO(cir): setLinkageForGV(GV, D); if (D->getTLSKind()) { if (D->getTLSKind() == VarDecl::TLS_Dynamic) llvm_unreachable("NYI"); setTLSMode(GV, *D); } setGVProperties(GV, D); // If required by the ABI, treat declarations of static data members with // inline initializers as definitions. if (astContext.isMSStaticDataMemberInlineDefinition(D)) { assert(0 && "not implemented"); } // Emit section information for extern variables. if (D->hasExternalStorage()) { if (const SectionAttr *SA = D->getAttr<SectionAttr>()) GV.setSectionAttr(builder.getStringAttr(SA->getName())); } GV.setGlobalVisibilityAttr(getGlobalVisibilityAttrFromDecl(D)); // Handle XCore specific ABI requirements. if (getTriple().getArch() == llvm::Triple::xcore) assert(0 && "not implemented"); // Check if we a have a const declaration with an initializer, we maybe // able to emit it as available_externally to expose it's value to the // optimizer. if (getLangOpts().CPlusPlus && GV.isPublic() && D->getType().isConstQualified() && GV.isDeclaration() && !D->hasDefinition() && D->hasInit() && !D->hasAttr<DLLImportAttr>()) { assert(0 && "not implemented"); } } // TODO(cir): if this method is used to handle functions we must have // something closer to GlobalValue::isDeclaration instead of checking for // initializer. if (GV.isDeclaration()) { // TODO(cir): set target attributes // External HIP managed variables needed to be recorded for transformation // in both device and host compilations. if (getLangOpts().CUDA) assert(0 && "not implemented"); } // TODO(cir): address space cast when needed for DAddrSpace. return GV; } cir::GlobalOp CIRGenModule::getOrCreateCIRGlobal(const VarDecl *D, mlir::Type Ty, ForDefinition_t IsForDefinition) { assert(D->hasGlobalStorage() && "Not a global variable"); QualType ASTTy = D->getType(); if (!Ty) Ty = getTypes().convertTypeForMem(ASTTy); StringRef MangledName = getMangledName(D); return getOrCreateCIRGlobal(MangledName, Ty, ASTTy.getAddressSpace(), D, IsForDefinition); } /// Return the mlir::Value for the address of the given global variable. If Ty /// is non-null and if the global doesn't exist, then it will be created with /// the specified type instead of whatever the normal requested type would be. /// If IsForDefinition is true, it is guaranteed that an actual global with type /// Ty will be returned, not conversion of a variable with the same mangled name /// but some other type. mlir::Value CIRGenModule::getAddrOfGlobalVar(const VarDecl *D, mlir::Type Ty, ForDefinition_t IsForDefinition) { assert(D->hasGlobalStorage() && "Not a global variable"); QualType ASTTy = D->getType(); if (!Ty) Ty = getTypes().convertTypeForMem(ASTTy); bool tlsAccess = D->getTLSKind() != VarDecl::TLS_None; auto g = getOrCreateCIRGlobal(D, Ty, IsForDefinition); auto ptrTy = builder.getPointerTo(g.getSymType(), g.getAddrSpaceAttr()); return builder.create<cir::GetGlobalOp>(getLoc(D->getSourceRange()), ptrTy, g.getSymName(), tlsAccess); } cir::GlobalViewAttr CIRGenModule::getAddrOfGlobalVarAttr(const VarDecl *D, mlir::Type Ty, ForDefinition_t IsForDefinition) { assert(D->hasGlobalStorage() && "Not a global variable"); QualType ASTTy = D->getType(); if (!Ty) Ty = getTypes().convertTypeForMem(ASTTy); auto globalOp = getOrCreateCIRGlobal(D, Ty, IsForDefinition); auto ptrTy = builder.getPointerTo(globalOp.getSymType()); return builder.getGlobalViewAttr(ptrTy, globalOp); } mlir::Operation *CIRGenModule::getWeakRefReference(const ValueDecl *VD) { const AliasAttr *AA = VD->getAttr<AliasAttr>(); assert(AA && "No alias?"); // See if there is already something with the target's name in the module. mlir::Operation *Entry = getGlobalValue(AA->getAliasee()); if (Entry) { assert((isa<cir::GlobalOp>(Entry) || isa<cir::FuncOp>(Entry)) && "weak ref should be against a global variable or function"); return Entry; } mlir::Type DeclTy = getTypes().convertTypeForMem(VD->getType()); if (mlir::isa<cir::FuncType>(DeclTy)) { auto F = GetOrCreateCIRFunction(AA->getAliasee(), DeclTy, GlobalDecl(cast<FunctionDecl>(VD)), /*ForVtable=*/false); F.setLinkage(cir::GlobalLinkageKind::ExternalWeakLinkage); WeakRefReferences.insert(F); return F; } llvm_unreachable("GlobalOp NYI"); } /// TODO(cir): looks like part of this code can be part of a common AST /// helper betweem CIR and LLVM codegen. template <typename SomeDecl> void CIRGenModule::maybeHandleStaticInExternC(const SomeDecl *D, cir::GlobalOp GV) { if (!getLangOpts().CPlusPlus) return; // Must have 'used' attribute, or else inline assembly can't rely on // the name existing. if (!D->template hasAttr<UsedAttr>()) return; // Must have internal linkage and an ordinary name. if (!D->getIdentifier() || D->getFormalLinkage() != Linkage::Internal) return; // Must be in an extern "C" context. Entities declared directly within // a record are not extern "C" even if the record is in such a context. const SomeDecl *First = D->getFirstDecl(); if (First->getDeclContext()->isRecord() || !First->isInExternCContext()) return; // TODO(cir): // OK, this is an internal linkage entity inside an extern "C" linkage // specification. Make a note of that so we can give it the "expected" // mangled name if nothing else is using that name. // // If we have multiple internal linkage entities with the same name // in extern "C" regions, none of them gets that name. assert(0 && "not implemented"); } void CIRGenModule::emitGlobalVarDefinition(const clang::VarDecl *D, bool IsTentative) { // TODO(cir): // OpenCL global variables of sampler type are translated to function calls, // therefore no need to be translated. // If this is OpenMP device, check if it is legal to emit this global // normally. QualType ASTTy = D->getType(); if ((getLangOpts().OpenCL && ASTTy->isSamplerT()) || getLangOpts().OpenMPIsTargetDevice) llvm_unreachable("not implemented"); // TODO(cir): LLVM's codegen uses a llvm::TrackingVH here. Is that // necessary here for CIR gen? mlir::Attribute Init; bool NeedsGlobalCtor = false; // Whether the definition of the variable is available externally. // If yes, we shouldn't emit the GloablCtor and GlobalDtor for the variable // since this is the job for its original source. bool IsDefinitionAvailableExternally = astContext.GetGVALinkageForVariable(D) == GVA_AvailableExternally; bool NeedsGlobalDtor = !IsDefinitionAvailableExternally && D->needsDestruction(astContext) == QualType::DK_cxx_destructor; // It is helpless to emit the definition for an available_externally variable // which can't be marked as const. // We don't need to check if it needs global ctor or dtor. See the above // comment for ideas. if (IsDefinitionAvailableExternally && (!D->hasConstantInitialization() || // TODO: Update this when we have interface to check constexpr // destructor. D->needsDestruction(getASTContext()) || !D->getType().isConstantStorage(getASTContext(), true, true))) return; const VarDecl *InitDecl; const Expr *InitExpr = D->getAnyInitializer(InitDecl); std::optional<ConstantEmitter> emitter; // CUDA E.2.4.1 "__shared__ variables cannot have an initialization // as part of their declaration." Sema has already checked for // error cases, so we just need to set Init to UndefValue. bool IsCUDASharedVar = getLangOpts().CUDAIsDevice && D->hasAttr<CUDASharedAttr>(); // Shadows of initialized device-side global variables are also left // undefined. // Managed Variables should be initialized on both host side and device side. bool IsCUDAShadowVar = !getLangOpts().CUDAIsDevice && !D->hasAttr<HIPManagedAttr>() && (D->hasAttr<CUDAConstantAttr>() || D->hasAttr<CUDADeviceAttr>() || D->hasAttr<CUDASharedAttr>()); bool IsCUDADeviceShadowVar = getLangOpts().CUDAIsDevice && !D->hasAttr<HIPManagedAttr>() && (D->getType()->isCUDADeviceBuiltinSurfaceType() || D->getType()->isCUDADeviceBuiltinTextureType()); if (getLangOpts().CUDA && (IsCUDASharedVar || IsCUDAShadowVar || IsCUDADeviceShadowVar)) assert(0 && "not implemented"); else if (D->hasAttr<LoaderUninitializedAttr>()) assert(0 && "not implemented"); else if (!InitExpr) { // This is a tentative definition; tentative definitions are // implicitly initialized with { 0 }. // // Note that tentative definitions are only emitted at the end of // a translation unit, so they should never have incomplete // type. In addition, EmitTentativeDefinition makes sure that we // never attempt to emit a tentative definition if a real one // exists. A use may still exists, however, so we still may need // to do a RAUW. assert(!ASTTy->isIncompleteType() && "Unexpected incomplete type"); Init = builder.getZeroInitAttr(convertType(D->getType())); } else { initializedGlobalDecl = GlobalDecl(D); emitter.emplace(*this); auto Initializer = emitter->tryEmitForInitializer(*InitDecl); if (!Initializer) { QualType T = InitExpr->getType(); if (D->getType()->isReferenceType()) T = D->getType(); if (getLangOpts().CPlusPlus) { if (InitDecl->hasFlexibleArrayInit(astContext)) ErrorUnsupported(D, "flexible array initializer"); Init = builder.getZeroInitAttr(convertType(T)); if (!IsDefinitionAvailableExternally) NeedsGlobalCtor = true; } else { ErrorUnsupported(D, "static initializer"); } } else { Init = Initializer; // We don't need an initializer, so remove the entry for the delayed // initializer position (just in case this entry was delayed) if we // also don't need to register a destructor. if (getLangOpts().CPlusPlus && !NeedsGlobalDtor) DelayedCXXInitPosition.erase(D); } } mlir::Type InitType; // If the initializer attribute is a SymbolRefAttr it means we are // initializing the global based on a global constant. // // TODO(cir): create another attribute to contain the final type and abstract // away SymbolRefAttr. if (auto symAttr = mlir::dyn_cast<mlir::SymbolRefAttr>(Init)) { auto cstGlobal = mlir::SymbolTable::lookupSymbolIn(theModule, symAttr); assert(isa<cir::GlobalOp>(cstGlobal) && "unaware of other symbol providers"); auto g = cast<cir::GlobalOp>(cstGlobal); auto arrayTy = mlir::dyn_cast<cir::ArrayType>(g.getSymType()); // TODO(cir): pointer to array decay. Should this be modeled explicitly in // CIR? if (arrayTy) InitType = cir::PointerType::get(&getMLIRContext(), arrayTy.getEltType()); } else { assert(mlir::isa<mlir::TypedAttr>(Init) && "This should have a type"); auto TypedInitAttr = mlir::cast<mlir::TypedAttr>(Init); InitType = TypedInitAttr.getType(); } assert(!mlir::isa<mlir::NoneType>(InitType) && "Should have a type by now"); auto Entry = getOrCreateCIRGlobal(D, InitType, ForDefinition_t(!IsTentative)); // TODO(cir): Strip off pointer casts from Entry if we get them? // TODO(cir): use GlobalValue interface assert(dyn_cast<GlobalOp>(&Entry) && "FuncOp not supported here"); auto GV = Entry; // We have a definition after a declaration with the wrong type. // We must make a new GlobalVariable* and update everything that used OldGV // (a declaration or tentative definition) with the new GlobalVariable* // (which will be a definition). // // This happens if there is a prototype for a global (e.g. // "extern int x[];") and then a definition of a different type (e.g. // "int x[10];"). This also happens when an initializer has a different type // from the type of the global (this happens with unions). if (!GV || GV.getSymType() != InitType) { // TODO(cir): this should include an address space check as well. assert(0 && "not implemented"); } maybeHandleStaticInExternC(D, GV); if (D->hasAttr<AnnotateAttr>()) addGlobalAnnotations(D, GV); // Set CIR's linkage type as appropriate. cir::GlobalLinkageKind Linkage = getCIRLinkageVarDefinition(D, /*IsConstant=*/false); // TODO(cir): // CUDA B.2.1 "The __device__ qualifier declares a variable that resides on // the device. [...]" // CUDA B.2.2 "The __constant__ qualifier, optionally used together with // __device__, declares a variable that: [...] if (GV && getLangOpts().CUDA) { assert(0 && "not implemented"); } // Set initializer and finalize emission CIRGenModule::setInitializer(GV, Init); if (emitter) emitter->finalize(GV); // TODO(cir): If it is safe to mark the global 'constant', do so now. GV.setConstant(!NeedsGlobalCtor && !NeedsGlobalDtor && isTypeConstant(D->getType(), true, true)); // If it is in a read-only section, mark it 'constant'. if (const SectionAttr *SA = D->getAttr<SectionAttr>()) GV.setSectionAttr(builder.getStringAttr(SA->getName())); GV.setGlobalVisibilityAttr(getGlobalVisibilityAttrFromDecl(D)); // TODO(cir): // GV->setAlignment(getContext().getDeclAlign(D).getAsAlign()); // On Darwin, unlike other Itanium C++ ABI platforms, the thread-wrapper // function is only defined alongside the variable, not also alongside // callers. Normally, all accesses to a thread_local go through the // thread-wrapper in order to ensure initialization has occurred, underlying // variable will never be used other than the thread-wrapper, so it can be // converted to internal linkage. // // However, if the variable has the 'constinit' attribute, it _can_ be // referenced directly, without calling the thread-wrapper, so the linkage // must not be changed. // // Additionally, if the variable isn't plain external linkage, e.g. if it's // weak or linkonce, the de-duplication semantics are important to preserve, // so we don't change the linkage. if (D->getTLSKind() == VarDecl::TLS_Dynamic && GV.isPublic() && astContext.getTargetInfo().getTriple().isOSDarwin() && !D->hasAttr<ConstInitAttr>()) { // TODO(cir): set to mlir::SymbolTable::Visibility::Private once we have // testcases. assert(0 && "not implemented"); } // Set CIR linkage and DLL storage class. GV.setLinkage(Linkage); // FIXME(cir): setLinkage should likely set MLIR's visibility automatically. GV.setVisibility(getMLIRVisibilityFromCIRLinkage(Linkage)); // TODO(cir): handle DLL storage classes in CIR? if (D->hasAttr<DLLImportAttr>()) assert(!cir::MissingFeatures::setDLLStorageClass()); else if (D->hasAttr<DLLExportAttr>()) assert(!cir::MissingFeatures::setDLLStorageClass()); else assert(!cir::MissingFeatures::setDLLStorageClass()); if (Linkage == cir::GlobalLinkageKind::CommonLinkage) { // common vars aren't constant even if declared const. GV.setConstant(false); // Tentative definition of global variables may be initialized with // non-zero null pointers. In this case they should have weak linkage // since common linkage must have zero initializer and must not have // explicit section therefore cannot have non-zero initial value. auto Initializer = GV.getInitialValue(); if (Initializer && !getBuilder().isNullValue(*Initializer)) GV.setLinkage(cir::GlobalLinkageKind::WeakAnyLinkage); } setNonAliasAttributes(D, GV); if (D->getTLSKind() && !GV.getTlsModelAttr()) { if (D->getTLSKind() == VarDecl::TLS_Dynamic) llvm_unreachable("NYI"); setTLSMode(GV, *D); } maybeSetTrivialComdat(*D, GV); // TODO(cir): // Emit the initializer function if necessary. if (NeedsGlobalCtor || NeedsGlobalDtor) { globalOpContext = GV; emitCXXGlobalVarDeclInitFunc(D, GV, NeedsGlobalCtor); globalOpContext = nullptr; } // TODO(cir): sanitizers (reportGlobalToASan) and global variable debug // information. assert(!cir::MissingFeatures::sanitizeOther()); assert(!cir::MissingFeatures::generateDebugInfo()); } void CIRGenModule::emitGlobalDefinition(GlobalDecl GD, mlir::Operation *Op) { const auto *D = cast<ValueDecl>(GD.getDecl()); if (const auto *FD = dyn_cast<FunctionDecl>(D)) { // At -O0, don't generate CIR for functions with available_externally // linkage. if (!shouldEmitFunction(GD)) return; if (const auto *Method = dyn_cast<CXXMethodDecl>(D)) { // Make sure to emit the definition(s) before we emit the thunks. This is // necessary for the generation of certain thunks. if (isa<CXXConstructorDecl>(Method) || isa<CXXDestructorDecl>(Method)) ABI->emitCXXStructor(GD); else if (FD->isMultiVersion()) llvm_unreachable("NYI"); else emitGlobalFunctionDefinition(GD, Op); if (Method->isVirtual()) getVTables().emitThunks(GD); return; } if (FD->isMultiVersion()) llvm_unreachable("NYI"); emitGlobalFunctionDefinition(GD, Op); return; } if (const auto *VD = dyn_cast<VarDecl>(D)) { return emitGlobalVarDefinition(VD, !VD->hasDefinition()); } llvm_unreachable("Invalid argument to emitGlobalDefinition()"); } mlir::Attribute CIRGenModule::getConstantArrayFromStringLiteral(const StringLiteral *E) { assert(!E->getType()->isPointerType() && "Strings are always arrays"); // Don't emit it as the address of the string, emit the string data itself // as an inline array. if (E->getCharByteWidth() == 1) { SmallString<64> Str(E->getString()); // Resize the string to the right size, which is indicated by its type. const ConstantArrayType *CAT = astContext.getAsConstantArrayType(E->getType()); auto finalSize = CAT->getSize().getZExtValue(); Str.resize(finalSize); auto eltTy = convertType(CAT->getElementType()); return builder.getString(Str, eltTy, finalSize); } auto arrayTy = mlir::dyn_cast<cir::ArrayType>(convertType(E->getType())); assert(arrayTy && "string literals must be emitted as an array type"); auto arrayEltTy = mlir::dyn_cast<cir::IntType>(arrayTy.getEltType()); assert(arrayEltTy && "string literal elements must be emitted as integral type"); auto arraySize = arrayTy.getSize(); auto literalSize = E->getLength(); // Collect the code units. SmallVector<uint32_t, 32> elementValues; elementValues.reserve(arraySize); for (unsigned i = 0; i < literalSize; ++i) elementValues.push_back(E->getCodeUnit(i)); elementValues.resize(arraySize); // If the string is full of null bytes, emit a #cir.zero instead. if (std::all_of(elementValues.begin(), elementValues.end(), [](uint32_t x) { return x == 0; })) return builder.getZeroAttr(arrayTy); // Otherwise emit a constant array holding the characters. SmallVector<mlir::Attribute, 32> elements; elements.reserve(arraySize); for (uint64_t i = 0; i < arraySize; ++i) elements.push_back(cir::IntAttr::get(arrayEltTy, elementValues[i])); auto elementsAttr = mlir::ArrayAttr::get(&getMLIRContext(), elements); return builder.getConstArray(elementsAttr, arrayTy); } // TODO(cir): this could be a common AST helper for both CIR and LLVM codegen. LangAS CIRGenModule::getGlobalConstantAddressSpace() const { // OpenCL v1.2 s6.5.3: a string literal is in the constant address space. if (getLangOpts().OpenCL) return LangAS::opencl_constant; if (getLangOpts().SYCLIsDevice) return LangAS::sycl_global; if (auto AS = getTarget().getConstantAddressSpace()) return AS.value(); return LangAS::Default; } // TODO(cir): this could be a common AST helper for both CIR and LLVM codegen. LangAS CIRGenModule::getLangTempAllocaAddressSpace() const { if (getLangOpts().OpenCL) return LangAS::opencl_private; if (getLangOpts().SYCLIsDevice || getLangOpts().CUDAIsDevice || (getLangOpts().OpenMP && getLangOpts().OpenMPIsTargetDevice)) llvm_unreachable("NYI"); return LangAS::Default; } static cir::GlobalOp generateStringLiteral(mlir::Location loc, mlir::TypedAttr C, cir::GlobalLinkageKind LT, CIRGenModule &CGM, StringRef GlobalName, CharUnits Alignment) { cir::AddressSpaceAttr addrSpaceAttr = CGM.getBuilder().getAddrSpaceAttr(CGM.getGlobalConstantAddressSpace()); // Create a global variable for this string // FIXME(cir): check for insertion point in module level. auto GV = CIRGenModule::createGlobalOp(CGM, loc, GlobalName, C.getType(), !CGM.getLangOpts().WritableStrings, addrSpaceAttr); // Set up extra information and add to the module GV.setAlignmentAttr(CGM.getSize(Alignment)); GV.setLinkageAttr( cir::GlobalLinkageKindAttr::get(CGM.getBuilder().getContext(), LT)); CIRGenModule::setInitializer(GV, C); // TODO(cir) assert(!cir::MissingFeatures::threadLocal() && "NYI"); assert(!cir::MissingFeatures::unnamedAddr() && "NYI"); if (GV.isWeakForLinker()) { assert(CGM.supportsCOMDAT() && "Only COFF uses weak string literals"); GV.setComdat(true); } CGM.setDSOLocal(static_cast<mlir::Operation *>(GV)); return GV; } /// Return a pointer to a constant array for the given string literal. cir::GlobalViewAttr CIRGenModule::getAddrOfConstantStringFromLiteral(const StringLiteral *S, StringRef Name) { CharUnits Alignment = astContext.getAlignOfGlobalVarInChars(S->getType(), /*VD=*/nullptr); mlir::Attribute C = getConstantArrayFromStringLiteral(S); cir::GlobalOp GV; if (!getLangOpts().WritableStrings && ConstantStringMap.count(C)) { GV = ConstantStringMap[C]; // The bigger alignment always wins. if (!GV.getAlignment() || uint64_t(Alignment.getQuantity()) > *GV.getAlignment()) GV.setAlignmentAttr(getSize(Alignment)); } else { SmallString<256> StringNameBuffer = Name; llvm::raw_svector_ostream Out(StringNameBuffer); if (StringLiteralCnt) Out << '.' << StringLiteralCnt; Name = Out.str(); StringLiteralCnt++; SmallString<256> MangledNameBuffer; StringRef GlobalVariableName; auto LT = cir::GlobalLinkageKind::ExternalLinkage; // Mangle the string literal if that's how the ABI merges duplicate strings. // Don't do it if they are writable, since we don't want writes in one TU to // affect strings in another. if (getCXXABI().getMangleContext().shouldMangleStringLiteral(S) && !getLangOpts().WritableStrings) { assert(0 && "not implemented"); } else { LT = cir::GlobalLinkageKind::PrivateLinkage; GlobalVariableName = Name; } auto loc = getLoc(S->getSourceRange()); auto typedC = llvm::dyn_cast<mlir::TypedAttr>(C); if (!typedC) llvm_unreachable("this should never be untyped at this point"); GV = generateStringLiteral(loc, typedC, LT, *this, GlobalVariableName, Alignment); setDSOLocal(static_cast<mlir::Operation *>(GV)); ConstantStringMap[C] = GV; assert(!cir::MissingFeatures::reportGlobalToASan() && "NYI"); } auto ArrayTy = mlir::dyn_cast<cir::ArrayType>(GV.getSymType()); assert(ArrayTy && "String literal must be array"); auto PtrTy = getBuilder().getPointerTo(ArrayTy.getEltType(), GV.getAddrSpaceAttr()); return builder.getGlobalViewAttr(PtrTy, GV); } void CIRGenModule::emitDeclContext(const DeclContext *DC) { for (auto *I : DC->decls()) { // Unlike other DeclContexts, the contents of an ObjCImplDecl at TU scope // are themselves considered "top-level", so EmitTopLevelDecl on an // ObjCImplDecl does not recursively visit them. We need to do that in // case they're nested inside another construct (LinkageSpecDecl / // ExportDecl) that does stop them from being considered "top-level". if (auto *OID = dyn_cast<ObjCImplDecl>(I)) llvm_unreachable("NYI"); emitTopLevelDecl(I); } } void CIRGenModule::emitLinkageSpec(const LinkageSpecDecl *LSD) { if (LSD->getLanguage() != LinkageSpecLanguageIDs::C && LSD->getLanguage() != LinkageSpecLanguageIDs::CXX) { llvm_unreachable("unsupported linkage spec"); return; } emitDeclContext(LSD); } mlir::Operation * CIRGenModule::getAddrOfGlobalTemporary(const MaterializeTemporaryExpr *expr, const Expr *init) { assert((expr->getStorageDuration() == SD_Static || expr->getStorageDuration() == SD_Thread) && "not a global temporary"); const auto *varDecl = cast<VarDecl>(expr->getExtendingDecl()); // If we're not materializing a subobject of the temporay, keep the // cv-qualifiers from the type of the MaterializeTemporaryExpr. QualType materializedType = init->getType(); if (init == expr->getSubExpr()) materializedType = expr->getType(); [[maybe_unused]] CharUnits align = getASTContext().getTypeAlignInChars(materializedType); auto insertResult = materializedGlobalTemporaryMap.insert({expr, nullptr}); if (!insertResult.second) { llvm_unreachable("NYI"); } // FIXME: If an externally-visible declaration extends multiple temporaries, // we need to give each temporary the same name in every translation unit (and // we also need to make the temporaries externally-visible). llvm::SmallString<256> name; llvm::raw_svector_ostream out(name); getCXXABI().getMangleContext().mangleReferenceTemporary( varDecl, expr->getManglingNumber(), out); APValue *value = nullptr; if (expr->getStorageDuration() == SD_Static && varDecl->evaluateValue()) { // If the initializer of the extending declaration is a constant // initializer, we should have a cached constant initializer for this // temporay. Note taht this m ight have a different value from the value // computed by evaluating the initializer if the surrounding constant // expression modifies the temporary. value = expr->getOrCreateValue(false); } // Try evaluating it now, it might have a constant initializer Expr::EvalResult evalResult; if (!value && init->EvaluateAsRValue(evalResult, getASTContext()) && !evalResult.hasSideEffects()) value = &evalResult.Val; LangAS addrSpace = getGlobalVarAddressSpace(varDecl); std::optional<ConstantEmitter> emitter; mlir::Attribute initialValue = nullptr; bool isConstant = false; mlir::Type type; if (value) { emitter.emplace(*this); initialValue = emitter->emitForInitializer(*value, addrSpace, materializedType); isConstant = materializedType.isConstantStorage( getASTContext(), /*ExcludeCtor*/ value, /*ExcludeDtor*/ false); type = mlir::cast<mlir::TypedAttr>(initialValue).getType(); } else { // No initializer, the initialization will be provided when we initialize // the declaration which performed lifetime extension. type = getTypes().convertTypeForMem(materializedType); } // Create a global variable for this lifetime-extended temporary. cir::GlobalLinkageKind linkage = getCIRLinkageVarDefinition(varDecl, false); if (linkage == cir::GlobalLinkageKind::ExternalLinkage) { const VarDecl *initVD; if (varDecl->isStaticDataMember() && varDecl->getAnyInitializer(initVD) && isa<CXXRecordDecl>(initVD->getLexicalDeclContext())) { // Temporaries defined inside a class get linkonce_odr linkage because the // calss can be defined in multiple translation units. llvm_unreachable("staticdatamember NYI"); } else { // There is no need for this temporary to have external linkage if the // VarDecl has external linkage. linkage = cir::GlobalLinkageKind::InternalLinkage; } } auto targetAS = builder.getAddrSpaceAttr(addrSpace); auto loc = getLoc(expr->getSourceRange()); auto gv = createGlobalOp(*this, loc, name, type, isConstant, targetAS, nullptr, linkage); gv.setInitialValueAttr(initialValue); if (emitter) emitter->finalize(gv); // Don't assign dllimport or dllexport to lcoal linkage globals if (!gv.hasLocalLinkage()) { llvm_unreachable("NYI"); } gv.setAlignment(align.getAsAlign().value()); if (supportsCOMDAT() && gv.isWeakForLinker()) llvm_unreachable("NYI"); if (varDecl->getTLSKind()) llvm_unreachable("NYI"); mlir::Operation *cv = gv; if (addrSpace != LangAS::Default) llvm_unreachable("NYI"); // Update the map with the new temporay. If we created a placeholder above, // replace it with the new global now. mlir::Operation *&entry = materializedGlobalTemporaryMap[expr]; if (entry) { entry->replaceAllUsesWith(cv); entry->erase(); } entry = cv; return cv; } // Emit code for a single top level declaration. void CIRGenModule::emitTopLevelDecl(Decl *decl) { // Ignore dependent declarations if (decl->isTemplated()) return; // Consteval function shouldn't be emitted. if (auto *FD = dyn_cast<FunctionDecl>(decl)) if (FD->isConsteval()) return; switch (decl->getKind()) { default: llvm::errs() << "emitTopLevelDecl codegen for decl kind '" << decl->getDeclKindName() << "' not implemented\n"; assert(false && "Not yet implemented"); case Decl::TranslationUnit: { // This path is CIR only - CIRGen handles TUDecls because // of clang-tidy checks, that operate on TU granularity. TranslationUnitDecl *TU = cast<TranslationUnitDecl>(decl); for (DeclContext::decl_iterator D = TU->decls_begin(), DEnd = TU->decls_end(); D != DEnd; ++D) emitTopLevelDecl(*D); return; } case Decl::Var: case Decl::Decomposition: case Decl::VarTemplateSpecialization: emitGlobal(cast<VarDecl>(decl)); assert(!isa<DecompositionDecl>(decl) && "not implemented"); // if (auto *DD = dyn_cast<DecompositionDecl>(decl)) // for (auto *B : DD->bindings()) // if (auto *HD = B->getHoldingVar()) // EmitGlobal(HD); break; case Decl::CXXConversion: case Decl::CXXMethod: case Decl::Function: emitGlobal(cast<FunctionDecl>(decl)); assert(!codeGenOpts.CoverageMapping && "Coverage Mapping NYI"); break; // C++ Decls case Decl::Namespace: emitDeclContext(cast<NamespaceDecl>(decl)); break; case Decl::ClassTemplateSpecialization: { // const auto *Spec = cast<ClassTemplateSpecializationDecl>(decl); assert(!cir::MissingFeatures::generateDebugInfo() && "NYI"); } [[fallthrough]]; case Decl::CXXRecord: { CXXRecordDecl *crd = cast<CXXRecordDecl>(decl); // TODO: Handle debug info as CodeGenModule.cpp does for (auto *childDecl : crd->decls()) if (isa<VarDecl>(childDecl) || isa<CXXRecordDecl>(childDecl)) emitTopLevelDecl(childDecl); break; } // No code generation needed. case Decl::UsingShadow: case Decl::ClassTemplate: case Decl::VarTemplate: case Decl::Concept: case Decl::VarTemplatePartialSpecialization: case Decl::FunctionTemplate: case Decl::TypeAliasTemplate: case Decl::Block: case Decl::Empty: case Decl::Binding: break; case Decl::Using: // using X; [C++] case Decl::UsingEnum: // using enum X; [C++] case Decl::NamespaceAlias: case Decl::UsingDirective: // using namespace X; [C++] assert(!cir::MissingFeatures::generateDebugInfo() && "NYI"); break; case Decl::CXXConstructor: getCXXABI().emitCXXConstructors(cast<CXXConstructorDecl>(decl)); break; case Decl::CXXDestructor: getCXXABI().emitCXXDestructors(cast<CXXDestructorDecl>(decl)); break; case Decl::StaticAssert: // Nothing to do. break; case Decl::LinkageSpec: emitLinkageSpec(cast<LinkageSpecDecl>(decl)); break; case Decl::Typedef: case Decl::TypeAlias: // using foo = bar; [C++11] case Decl::Record: case Decl::Enum: assert(!cir::MissingFeatures::generateDebugInfo() && "NYI"); break; } } static bool shouldBeInCOMDAT(CIRGenModule &CGM, const Decl &D) { if (!CGM.supportsCOMDAT()) return false; if (D.hasAttr<SelectAnyAttr>()) return true; GVALinkage Linkage; if (auto *VD = dyn_cast<VarDecl>(&D)) Linkage = CGM.getASTContext().GetGVALinkageForVariable(VD); else Linkage = CGM.getASTContext().GetGVALinkageForFunction(cast<FunctionDecl>(&D)); switch (Linkage) { case clang::GVA_Internal: case clang::GVA_AvailableExternally: case clang::GVA_StrongExternal: return false; case clang::GVA_DiscardableODR: case clang::GVA_StrongODR: return true; } llvm_unreachable("No such linkage"); } // TODO(cir): this could be a common method between LLVM codegen. static bool isVarDeclStrongDefinition(const ASTContext &astContext, CIRGenModule &CGM, const VarDecl *D, bool NoCommon) { // Don't give variables common linkage if -fno-common was specified unless it // was overridden by a NoCommon attribute. if ((NoCommon || D->hasAttr<NoCommonAttr>()) && !D->hasAttr<CommonAttr>()) return true; // C11 6.9.2/2: // A declaration of an identifier for an object that has file scope without // an initializer, and without a storage-class specifier or with the // storage-class specifier static, constitutes a tentative definition. if (D->getInit() || D->hasExternalStorage()) return true; // A variable cannot be both common and exist in a section. if (D->hasAttr<SectionAttr>()) return true; // A variable cannot be both common and exist in a section. // We don't try to determine which is the right section in the front-end. // If no specialized section name is applicable, it will resort to default. if (D->hasAttr<PragmaClangBSSSectionAttr>() || D->hasAttr<PragmaClangDataSectionAttr>() || D->hasAttr<PragmaClangRelroSectionAttr>() || D->hasAttr<PragmaClangRodataSectionAttr>()) return true; // Thread local vars aren't considered common linkage. if (D->getTLSKind()) return true; // Tentative definitions marked with WeakImportAttr are true definitions. if (D->hasAttr<WeakImportAttr>()) return true; // A variable cannot be both common and exist in a comdat. if (shouldBeInCOMDAT(CGM, *D)) return true; // Declarations with a required alignment do not have common linkage in MSVC // mode. if (astContext.getTargetInfo().getCXXABI().isMicrosoft()) { if (D->hasAttr<AlignedAttr>()) return true; QualType VarType = D->getType(); if (astContext.isAlignmentRequired(VarType)) return true; if (const auto *RT = VarType->getAs<RecordType>()) { const RecordDecl *RD = RT->getDecl(); for (const FieldDecl *FD : RD->fields()) { if (FD->isBitField()) continue; if (FD->hasAttr<AlignedAttr>()) return true; if (astContext.isAlignmentRequired(FD->getType())) return true; } } } // Microsoft's link.exe doesn't support alignments greater than 32 bytes for // common symbols, so symbols with greater alignment requirements cannot be // common. // Other COFF linkers (ld.bfd and LLD) support arbitrary power-of-two // alignments for common symbols via the aligncomm directive, so this // restriction only applies to MSVC environments. if (astContext.getTargetInfo().getTriple().isKnownWindowsMSVCEnvironment() && astContext.getTypeAlignIfKnown(D->getType()) > astContext.toBits(CharUnits::fromQuantity(32))) return true; return false; } void CIRGenModule::setInitializer(cir::GlobalOp &global, mlir::Attribute value) { // Recompute visibility when updating initializer. global.setInitialValueAttr(value); mlir::SymbolTable::setSymbolVisibility( global, CIRGenModule::getMLIRVisibility(global)); } mlir::SymbolTable::Visibility CIRGenModule::getMLIRVisibility(cir::GlobalOp op) { // MLIR doesn't accept public symbols declarations (only // definitions). if (op.isDeclaration()) return mlir::SymbolTable::Visibility::Private; return getMLIRVisibilityFromCIRLinkage(op.getLinkage()); } mlir::SymbolTable::Visibility CIRGenModule::getMLIRVisibilityFromCIRLinkage(cir::GlobalLinkageKind GLK) { switch (GLK) { case cir::GlobalLinkageKind::InternalLinkage: case cir::GlobalLinkageKind::PrivateLinkage: return mlir::SymbolTable::Visibility::Private; case cir::GlobalLinkageKind::ExternalLinkage: case cir::GlobalLinkageKind::ExternalWeakLinkage: case cir::GlobalLinkageKind::LinkOnceODRLinkage: case cir::GlobalLinkageKind::AvailableExternallyLinkage: case cir::GlobalLinkageKind::CommonLinkage: case cir::GlobalLinkageKind::WeakAnyLinkage: case cir::GlobalLinkageKind::WeakODRLinkage: return mlir::SymbolTable::Visibility::Public; default: { llvm::errs() << "visibility not implemented for '" << stringifyGlobalLinkageKind(GLK) << "'\n"; assert(0 && "not implemented"); } } llvm_unreachable("linkage should be handled above!"); } cir::VisibilityKind CIRGenModule::getGlobalVisibilityKindFromClangVisibility( clang::VisibilityAttr::VisibilityType visibility) { switch (visibility) { case clang::VisibilityAttr::VisibilityType::Default: return VisibilityKind::Default; case clang::VisibilityAttr::VisibilityType::Hidden: return VisibilityKind::Hidden; case clang::VisibilityAttr::VisibilityType::Protected: return VisibilityKind::Protected; } llvm_unreachable("unexpected visibility value"); } cir::VisibilityAttr CIRGenModule::getGlobalVisibilityAttrFromDecl(const Decl *decl) { const clang::VisibilityAttr *VA = decl->getAttr<clang::VisibilityAttr>(); cir::VisibilityAttr cirVisibility = cir::VisibilityAttr::get(&getMLIRContext()); if (VA) { cirVisibility = cir::VisibilityAttr::get( &getMLIRContext(), getGlobalVisibilityKindFromClangVisibility(VA->getVisibility())); } return cirVisibility; } cir::GlobalLinkageKind CIRGenModule::getCIRLinkageForDeclarator( const DeclaratorDecl *D, GVALinkage Linkage, bool IsConstantVariable) { if (Linkage == GVA_Internal) return cir::GlobalLinkageKind::InternalLinkage; if (D->hasAttr<WeakAttr>()) { if (IsConstantVariable) return cir::GlobalLinkageKind::WeakODRLinkage; else return cir::GlobalLinkageKind::WeakAnyLinkage; } if (const auto *FD = D->getAsFunction()) if (FD->isMultiVersion() && Linkage == GVA_AvailableExternally) return cir::GlobalLinkageKind::LinkOnceAnyLinkage; // We are guaranteed to have a strong definition somewhere else, // so we can use available_externally linkage. if (Linkage == GVA_AvailableExternally) return cir::GlobalLinkageKind::AvailableExternallyLinkage; // Note that Apple's kernel linker doesn't support symbol // coalescing, so we need to avoid linkonce and weak linkages there. // Normally, this means we just map to internal, but for explicit // instantiations we'll map to external. // In C++, the compiler has to emit a definition in every translation unit // that references the function. We should use linkonce_odr because // a) if all references in this translation unit are optimized away, we // don't need to codegen it. b) if the function persists, it needs to be // merged with other definitions. c) C++ has the ODR, so we know the // definition is dependable. if (Linkage == GVA_DiscardableODR) return !astContext.getLangOpts().AppleKext ? cir::GlobalLinkageKind::LinkOnceODRLinkage : cir::GlobalLinkageKind::InternalLinkage; // An explicit instantiation of a template has weak linkage, since // explicit instantiations can occur in multiple translation units // and must all be equivalent. However, we are not allowed to // throw away these explicit instantiations. // // CUDA/HIP: For -fno-gpu-rdc case, device code is limited to one TU, // so say that CUDA templates are either external (for kernels) or internal. // This lets llvm perform aggressive inter-procedural optimizations. For // -fgpu-rdc case, device function calls across multiple TU's are allowed, // therefore we need to follow the normal linkage paradigm. if (Linkage == GVA_StrongODR) { if (getLangOpts().AppleKext) return cir::GlobalLinkageKind::ExternalLinkage; if (getLangOpts().CUDA && getLangOpts().CUDAIsDevice && !getLangOpts().GPURelocatableDeviceCode) return D->hasAttr<CUDAGlobalAttr>() ? cir::GlobalLinkageKind::ExternalLinkage : cir::GlobalLinkageKind::InternalLinkage; return cir::GlobalLinkageKind::WeakODRLinkage; } // C++ doesn't have tentative definitions and thus cannot have common // linkage. if (!getLangOpts().CPlusPlus && isa<VarDecl>(D) && !isVarDeclStrongDefinition(astContext, *this, cast<VarDecl>(D), getCodeGenOpts().NoCommon)) return cir::GlobalLinkageKind::CommonLinkage; // selectany symbols are externally visible, so use weak instead of // linkonce. MSVC optimizes away references to const selectany globals, so // all definitions should be the same and ODR linkage should be used. // http://msdn.microsoft.com/en-us/library/5tkz6s71.aspx if (D->hasAttr<SelectAnyAttr>()) return cir::GlobalLinkageKind::WeakODRLinkage; // Otherwise, we have strong external linkage. assert(Linkage == GVA_StrongExternal); return cir::GlobalLinkageKind::ExternalLinkage; } /// This function is called when we implement a function with no prototype, e.g. /// "int foo() {}". If there are existing call uses of the old function in the /// module, this adjusts them to call the new function directly. /// /// This is not just a cleanup: the always_inline pass requires direct calls to /// functions to be able to inline them. If there is a bitcast in the way, it /// won't inline them. Instcombine normally deletes these calls, but it isn't /// run at -O0. void CIRGenModule::ReplaceUsesOfNonProtoTypeWithRealFunction( mlir::Operation *Old, cir::FuncOp NewFn) { // If we're redefining a global as a function, don't transform it. auto OldFn = dyn_cast<cir::FuncOp>(Old); if (!OldFn) return; // TODO(cir): this RAUW ignores the features below. assert(!cir::MissingFeatures::exceptions() && "Call vs Invoke NYI"); assert(!cir::MissingFeatures::parameterAttributes()); assert(!cir::MissingFeatures::operandBundles()); assert(OldFn->getAttrs().size() > 1 && "Attribute forwarding NYI"); // Mark new function as originated from a no-proto declaration. NewFn.setNoProtoAttr(OldFn.getNoProtoAttr()); // Iterate through all calls of the no-proto function. auto SymUses = OldFn.getSymbolUses(OldFn->getParentOp()); for (auto Use : SymUses.value()) { mlir::OpBuilder::InsertionGuard guard(builder); if (auto noProtoCallOp = dyn_cast<cir::CallOp>(Use.getUser())) { builder.setInsertionPoint(noProtoCallOp); // Patch call type with the real function type. auto realCallOp = builder.createCallOp(noProtoCallOp.getLoc(), NewFn, noProtoCallOp.getOperands()); // Replace old no proto call with fixed call. noProtoCallOp.replaceAllUsesWith(realCallOp); noProtoCallOp.erase(); } else if (auto getGlobalOp = dyn_cast<cir::GetGlobalOp>(Use.getUser())) { // Replace type getGlobalOp.getAddr().setType( cir::PointerType::get(&getMLIRContext(), NewFn.getFunctionType())); } else { llvm_unreachable("NIY"); } } } cir::GlobalLinkageKind CIRGenModule::getCIRLinkageVarDefinition(const VarDecl *VD, bool IsConstant) { assert(!IsConstant && "constant variables NYI"); GVALinkage Linkage = astContext.GetGVALinkageForVariable(VD); return getCIRLinkageForDeclarator(VD, Linkage, IsConstant); } cir::GlobalLinkageKind CIRGenModule::getFunctionLinkage(GlobalDecl GD) { const auto *D = cast<FunctionDecl>(GD.getDecl()); GVALinkage Linkage = astContext.GetGVALinkageForFunction(D); if (const auto *Dtor = dyn_cast<CXXDestructorDecl>(D)) return getCXXABI().getCXXDestructorLinkage(Linkage, Dtor, GD.getDtorType()); return getCIRLinkageForDeclarator(D, Linkage, /*IsConstantVariable=*/false); } void CIRGenModule::emitAliasForGlobal(StringRef mangledName, mlir::Operation *op, GlobalDecl aliasGD, cir::FuncOp aliasee, cir::GlobalLinkageKind linkage) { auto *aliasFD = dyn_cast<FunctionDecl>(aliasGD.getDecl()); assert(aliasFD && "expected FunctionDecl"); // The aliasee function type is different from the alias one, this difference // is specific to CIR because in LLVM the ptr types are already erased at this // point. auto &fnInfo = getTypes().arrangeCXXStructorDeclaration(aliasGD); auto fnType = getTypes().GetFunctionType(fnInfo); auto alias = createCIRFunction(getLoc(aliasGD.getDecl()->getSourceRange()), mangledName, fnType, aliasFD); alias.setAliasee(aliasee.getName()); alias.setLinkage(linkage); // Declarations cannot have public MLIR visibility, just mark them private // but this really should have no meaning since CIR should not be using // this information to derive linkage information. mlir::SymbolTable::setSymbolVisibility( alias, mlir::SymbolTable::Visibility::Private); // Alias constructors and destructors are always unnamed_addr. assert(!cir::MissingFeatures::unnamedAddr()); // Switch any previous uses to the alias. if (op) { llvm_unreachable("NYI"); } else { // Name already set by createCIRFunction } // Finally, set up the alias with its proper name and attributes. setCommonAttributes(aliasGD, alias); } mlir::Type CIRGenModule::convertType(QualType type) { return genTypes.convertType(type); } bool CIRGenModule::verifyModule() { // Verify the module after we have finished constructing it, this will // check the structural properties of the IR and invoke any specific // verifiers we have on the CIR operations. return mlir::verify(theModule).succeeded(); } std::pair<cir::FuncType, cir::FuncOp> CIRGenModule::getAddrAndTypeOfCXXStructor( GlobalDecl GD, const CIRGenFunctionInfo *FnInfo, cir::FuncType FnType, bool Dontdefer, ForDefinition_t IsForDefinition) { auto *MD = cast<CXXMethodDecl>(GD.getDecl()); if (isa<CXXDestructorDecl>(MD)) { // Always alias equivalent complete destructors to base destructors in the // MS ABI. if (getTarget().getCXXABI().isMicrosoft() && GD.getDtorType() == Dtor_Complete && MD->getParent()->getNumVBases() == 0) llvm_unreachable("NYI"); } if (!FnType) { if (!FnInfo) FnInfo = &getTypes().arrangeCXXStructorDeclaration(GD); FnType = getTypes().GetFunctionType(*FnInfo); } auto Fn = GetOrCreateCIRFunction(getMangledName(GD), FnType, GD, /*ForVtable=*/false, Dontdefer, /*IsThunk=*/false, IsForDefinition); return {FnType, Fn}; } cir::FuncOp CIRGenModule::GetAddrOfFunction(clang::GlobalDecl GD, mlir::Type Ty, bool ForVTable, bool DontDefer, ForDefinition_t IsForDefinition) { assert(!cast<FunctionDecl>(GD.getDecl())->isConsteval() && "consteval function should never be emitted"); if (!Ty) { const auto *FD = cast<FunctionDecl>(GD.getDecl()); Ty = convertType(FD->getType()); } // Devirtualized destructor calls may come through here instead of via // getAddrOfCXXStructor. Make sure we use the MS ABI base destructor instead // of the complete destructor when necessary. if (const auto *DD = dyn_cast<CXXDestructorDecl>(GD.getDecl())) { if (getTarget().getCXXABI().isMicrosoft() && GD.getDtorType() == Dtor_Complete && DD->getParent()->getNumVBases() == 0) llvm_unreachable("NYI"); } StringRef MangledName = getMangledName(GD); auto F = GetOrCreateCIRFunction(MangledName, Ty, GD, ForVTable, DontDefer, /*IsThunk=*/false, IsForDefinition); // As __global__ functions (kernels) always reside on device, // when we access them from host, we must refer to the kernel handle. // For HIP, we should never directly access the host device addr, but // instead the Global Variable of that stub. For CUDA, it's just the device // stub. For HIP, it's something different. if ((langOpts.HIP || langOpts.CUDA) && !langOpts.CUDAIsDevice && cast<FunctionDecl>(GD.getDecl())->hasAttr<CUDAGlobalAttr>()) { auto *stubHandle = getCUDARuntime().getKernelHandle(F, GD); if (IsForDefinition) return F; if (langOpts.HIP) llvm_unreachable("NYI"); } return F; } // Returns true if GD is a function decl with internal linkage and needs a // unique suffix after the mangled name. static bool isUniqueInternalLinkageDecl(GlobalDecl GD, CIRGenModule &CGM) { assert(CGM.getModuleNameHash().empty() && "Unique internal linkage names NYI"); return false; } static std::string getMangledNameImpl(CIRGenModule &CGM, GlobalDecl GD, const NamedDecl *ND, bool OmitMultiVersionMangling = false) { assert(!OmitMultiVersionMangling && "NYI"); SmallString<256> Buffer; llvm::raw_svector_ostream Out(Buffer); MangleContext &MC = CGM.getCXXABI().getMangleContext(); assert(CGM.getModuleNameHash().empty() && "NYI"); auto ShouldMangle = MC.shouldMangleDeclName(ND); if (ShouldMangle) { MC.mangleName(GD.getWithDecl(ND), Out); } else { auto *II = ND->getIdentifier(); assert(II && "Attempt to mangle unnamed decl."); const auto *FD = dyn_cast<FunctionDecl>(ND); if (FD && FD->getType()->castAs<FunctionType>()->getCallConv() == CC_X86RegCall) { assert(0 && "NYI"); } else if (FD && FD->hasAttr<CUDAGlobalAttr>() && GD.getKernelReferenceKind() == KernelReferenceKind::Stub) { Out << "__device_stub__"; } else { Out << II->getName(); } } // Check if the module name hash should be appended for internal linkage // symbols. This should come before multi-version target suffixes are // appendded. This is to keep the name and module hash suffix of the internal // linkage function together. The unique suffix should only be added when name // mangling is done to make sure that the final name can be properly // demangled. For example, for C functions without prototypes, name mangling // is not done and the unique suffix should not be appended then. assert(!isUniqueInternalLinkageDecl(GD, CGM) && "NYI"); if (const auto *FD = dyn_cast<FunctionDecl>(ND)) { assert(!FD->isMultiVersion() && "NYI"); } assert(!CGM.getLangOpts().GPURelocatableDeviceCode && "NYI"); return std::string(Out.str()); } StringRef CIRGenModule::getMangledName(GlobalDecl GD) { auto CanonicalGD = GD.getCanonicalDecl(); // Some ABIs don't have constructor variants. Make sure that base and complete // constructors get mangled the same. if (const auto *CD = dyn_cast<CXXConstructorDecl>(CanonicalGD.getDecl())) { if (!getTarget().getCXXABI().hasConstructorVariants()) { assert(false && "NYI"); } } // Keep the first result in the case of a mangling collision. const auto *ND = cast<NamedDecl>(GD.getDecl()); std::string MangledName = getMangledNameImpl(*this, GD, ND); auto Result = Manglings.insert(std::make_pair(MangledName, GD)); return MangledDeclNames[CanonicalGD] = Result.first->first(); } void CIRGenModule::emitTentativeDefinition(const VarDecl *D) { assert(!D->getInit() && "Cannot emit definite definitions here!"); StringRef MangledName = getMangledName(D); auto *GV = getGlobalValue(MangledName); // TODO(cir): can a tentative definition come from something other than a // global op? If not, the assertion below is wrong and should be removed. If // so, getGlobalValue might be better of returining a global value interface // that alows use to manage different globals value types transparently. if (GV) assert(isa<cir::GlobalOp>(GV) && "tentative definition can only be built from a cir.global_op"); // We already have a definition, not declaration, with the same mangled name. // Emitting of declaration is not required (and actually overwrites emitted // definition). if (GV && !dyn_cast<cir::GlobalOp>(GV).isDeclaration()) return; // If we have not seen a reference to this variable yet, place it into the // deferred declarations table to be emitted if needed later. if (!MustBeEmitted(D) && !GV) { DeferredDecls[MangledName] = D; return; } // The tentative definition is the only definition. emitGlobalVarDefinition(D); } void CIRGenModule::setGlobalVisibility(mlir::Operation *GV, const NamedDecl *D) const { assert(!cir::MissingFeatures::setGlobalVisibility()); } void CIRGenModule::setDSOLocal(mlir::Operation *Op) const { assert(!cir::MissingFeatures::setDSOLocal()); if (auto globalValue = dyn_cast<cir::CIRGlobalValueInterface>(Op)) { setDSOLocal(globalValue); } } void CIRGenModule::setGVProperties(mlir::Operation *Op, const NamedDecl *D) const { assert(!cir::MissingFeatures::setDLLImportDLLExport()); setGVPropertiesAux(Op, D); } void CIRGenModule::setGVPropertiesAux(mlir::Operation *Op, const NamedDecl *D) const { setGlobalVisibility(Op, D); setDSOLocal(Op); assert(!cir::MissingFeatures::setPartition()); } bool CIRGenModule::lookupRepresentativeDecl(StringRef MangledName, GlobalDecl &Result) const { auto Res = Manglings.find(MangledName); if (Res == Manglings.end()) return false; Result = Res->getValue(); return true; } cir::FuncOp CIRGenModule::createCIRFunction(mlir::Location loc, StringRef name, cir::FuncType Ty, const clang::FunctionDecl *FD) { // At the point we need to create the function, the insertion point // could be anywhere (e.g. callsite). Do not rely on whatever it might // be, properly save, find the appropriate place and restore. FuncOp f; { mlir::OpBuilder::InsertionGuard guard(builder); // Some global emissions are triggered while emitting a function, e.g. // void s() { x.method() } // // Be sure to insert a new function before a current one. auto *curCGF = getCurrCIRGenFun(); if (curCGF) builder.setInsertionPoint(curCGF->CurFn); f = builder.create<cir::FuncOp>(loc, name, Ty); if (FD) f.setAstAttr(makeFuncDeclAttr(FD, &getMLIRContext())); if (FD && !FD->hasPrototype()) f.setNoProtoAttr(builder.getUnitAttr()); assert(f.isDeclaration() && "expected empty body"); // A declaration gets private visibility by default, but external linkage // as the default linkage. f.setLinkageAttr(cir::GlobalLinkageKindAttr::get( &getMLIRContext(), cir::GlobalLinkageKind::ExternalLinkage)); mlir::SymbolTable::setSymbolVisibility( f, mlir::SymbolTable::Visibility::Private); // Initialize with empty dict of extra attributes. f.setExtraAttrsAttr(cir::ExtraFuncAttributesAttr::get( &getMLIRContext(), builder.getDictionaryAttr({}))); if (!curCGF) theModule.push_back(f); } return f; } cir::FuncOp CIRGenModule::createRuntimeFunction(cir::FuncType Ty, StringRef Name, mlir::ArrayAttr, [[maybe_unused]] bool Local, bool AssumeConvergent) { if (AssumeConvergent) { llvm_unreachable("NYI"); } if (Local) llvm_unreachable("NYI"); auto entry = GetOrCreateCIRFunction(Name, Ty, GlobalDecl(), /*ForVtable=*/false); // Traditional codegen checks for a valid dyn_cast llvm::Function for `entry`, // no testcase that cover this path just yet though. if (!entry) { // Setup runtime CC, DLL support for windows and set dso local. llvm_unreachable("NYI"); } return entry; } bool isDefaultedMethod(const clang::FunctionDecl *FD) { if (FD->isDefaulted() && isa<CXXMethodDecl>(FD) && (cast<CXXMethodDecl>(FD)->isCopyAssignmentOperator() || cast<CXXMethodDecl>(FD)->isMoveAssignmentOperator())) return true; return false; } mlir::Location CIRGenModule::getLocForFunction(const clang::FunctionDecl *FD) { bool invalidLoc = !FD || (FD->getSourceRange().getBegin().isInvalid() || FD->getSourceRange().getEnd().isInvalid()); if (!invalidLoc) return getLoc(FD->getSourceRange()); // Use the module location return theModule->getLoc(); } /// 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. /// TODO(cir): can be shared with traditional LLVM codegen. static bool hasUnwindExceptions(const LangOptions &LangOpts) { // If exceptions are completely disabled, obviously this is false. if (!LangOpts.Exceptions) return false; // If C++ exceptions are enabled, this is true. if (LangOpts.CXXExceptions) return true; // If ObjC exceptions are enabled, this depends on the ABI. if (LangOpts.ObjCExceptions) { return LangOpts.ObjCRuntime.hasUnwindExceptions(); } return true; } void CIRGenModule::setCIRFunctionAttributesForDefinition(const Decl *decl, FuncOp f) { mlir::NamedAttrList attrs{f.getExtraAttrs().getElements().getValue()}; if ((!decl || !decl->hasAttr<NoUwtableAttr>()) && codeGenOpts.UnwindTables) { auto attr = cir::UWTableAttr::get( &getMLIRContext(), cir::UWTableKind(codeGenOpts.UnwindTables)); attrs.set(attr.getMnemonic(), attr); } if (codeGenOpts.StackClashProtector) llvm_unreachable("NYI"); if (codeGenOpts.StackProbeSize && codeGenOpts.StackProbeSize != 4096) llvm_unreachable("NYI"); if (!hasUnwindExceptions(getLangOpts())) { auto attr = cir::NoThrowAttr::get(&getMLIRContext()); attrs.set(attr.getMnemonic(), attr); } assert(!MissingFeatures::stackProtector()); auto existingInlineAttr = dyn_cast_if_present<cir::InlineAttr>( attrs.get(cir::InlineAttr::getMnemonic())); bool isNoInline = existingInlineAttr && existingInlineAttr.isNoInline(); bool isAlwaysInline = existingInlineAttr && existingInlineAttr.isAlwaysInline(); if (!decl) { // Non-entry HLSL functions must always be inlined. if (getLangOpts().HLSL && !isNoInline) { auto attr = cir::InlineAttr::get(&getMLIRContext(), cir::InlineKind::AlwaysInline); attrs.set(attr.getMnemonic(), attr); } else if (!isAlwaysInline && codeGenOpts.getInlining() == CodeGenOptions::OnlyAlwaysInlining) { // If we don't have a declaration to control inlining, the function isn't // explicitly marked as alwaysinline for semantic reasons, and inlining is // disabled, mark the function as noinline. auto attr = cir::InlineAttr::get(&getMLIRContext(), cir::InlineKind::NoInline); attrs.set(attr.getMnemonic(), attr); } f.setExtraAttrsAttr(cir::ExtraFuncAttributesAttr::get( &getMLIRContext(), attrs.getDictionary(&getMLIRContext()))); return; } // Handle SME attributes that apply to function definitions, // rather than to function prototypes. if (decl->hasAttr<ArmLocallyStreamingAttr>()) llvm_unreachable("NYI"); if (auto *attr = decl->getAttr<ArmNewAttr>()) { if (attr->isNewZA()) llvm_unreachable("NYI"); if (attr->isNewZT0()) llvm_unreachable("NYI"); } // Track whether we need to add the optnone attribute, // starting with the default for this optimization level. bool shouldAddOptNone = !codeGenOpts.DisableO0ImplyOptNone && codeGenOpts.OptimizationLevel == 0; // We can't add optnone in the following cases, it won't pass the verifier. shouldAddOptNone &= !decl->hasAttr<MinSizeAttr>(); shouldAddOptNone &= !decl->hasAttr<AlwaysInlineAttr>(); // Non-entry HLSL functions must always be inlined. if (getLangOpts().HLSL && !isNoInline && !decl->hasAttr<NoInlineAttr>()) { auto attr = cir::InlineAttr::get(&getMLIRContext(), cir::InlineKind::AlwaysInline); attrs.set(attr.getMnemonic(), attr); } else if ((shouldAddOptNone || decl->hasAttr<OptimizeNoneAttr>()) && !isAlwaysInline) { // Add optnone, but do so only if the function isn't always_inline. auto optNoneAttr = cir::OptNoneAttr::get(&getMLIRContext()); attrs.set(optNoneAttr.getMnemonic(), optNoneAttr); // OptimizeNone implies noinline; we should not be inlining such functions. auto noInlineAttr = cir::InlineAttr::get(&getMLIRContext(), cir::InlineKind::NoInline); attrs.set(noInlineAttr.getMnemonic(), noInlineAttr); // We still need to handle naked functions even though optnone subsumes // much of their semantics. if (decl->hasAttr<NakedAttr>()) llvm_unreachable("NYI"); // OptimizeNone wins over OptimizeForSize and MinSize. assert(!MissingFeatures::optimizeForSize()); assert(!MissingFeatures::minSize()); } else if (decl->hasAttr<NakedAttr>()) { // Naked implies noinline: we should not be inlining such functions. llvm_unreachable("NYI"); } else if (decl->hasAttr<NoDuplicateAttr>()) { llvm_unreachable("NYI"); } else if (decl->hasAttr<NoInlineAttr>() && !isAlwaysInline) { // Add noinline if the function isn't always_inline. auto attr = cir::InlineAttr::get(&getMLIRContext(), cir::InlineKind::NoInline); attrs.set(attr.getMnemonic(), attr); } else if (decl->hasAttr<AlwaysInlineAttr>() && !isNoInline) { // (noinline wins over always_inline, and we can't specify both in IR) auto attr = cir::InlineAttr::get(&getMLIRContext(), cir::InlineKind::AlwaysInline); attrs.set(attr.getMnemonic(), attr); } else if (codeGenOpts.getInlining() == CodeGenOptions::OnlyAlwaysInlining) { // If we're not inlining, then force everything that isn't always_inline // to carry an explicit noinline attribute. if (!isAlwaysInline) { auto attr = cir::InlineAttr::get(&getMLIRContext(), cir::InlineKind::NoInline); attrs.set(attr.getMnemonic(), attr); } } else { // Otherwise, propagate the inline hint attribute and potentially use its // absence to mark things as noinline. // Search function and template pattern redeclarations for inline. if (auto *fd = dyn_cast<FunctionDecl>(decl)) { auto checkForInline = [](const FunctionDecl *decl) { auto checkRedeclForInline = [](const FunctionDecl *redecl) { return redecl->isInlineSpecified(); }; if (any_of(decl->redecls(), checkRedeclForInline)) return true; const FunctionDecl *pattern = decl->getTemplateInstantiationPattern(); if (!pattern) return false; return any_of(pattern->redecls(), checkRedeclForInline); }; if (checkForInline(fd)) { auto attr = cir::InlineAttr::get(&getMLIRContext(), cir::InlineKind::InlineHint); attrs.set(attr.getMnemonic(), attr); } else if (codeGenOpts.getInlining() == CodeGenOptions::OnlyHintInlining && !fd->isInlined() && !isAlwaysInline) { auto attr = cir::InlineAttr::get(&getMLIRContext(), cir::InlineKind::NoInline); attrs.set(attr.getMnemonic(), attr); } } } // Add other optimization related attributes if we are optimizing this // function. if (!decl->hasAttr<OptimizeNoneAttr>()) { if (decl->hasAttr<ColdAttr>()) { llvm_unreachable("NYI"); } if (decl->hasAttr<HotAttr>()) llvm_unreachable("NYI"); if (decl->hasAttr<MinSizeAttr>()) assert(!MissingFeatures::minSize()); } f.setExtraAttrsAttr(cir::ExtraFuncAttributesAttr::get( &getMLIRContext(), attrs.getDictionary(&getMLIRContext()))); assert(!MissingFeatures::setFunctionAlignment()); // In the cross-dso CFI mode with canonical jump tables, we want !type // attributes on definitions only. if (codeGenOpts.SanitizeCfiCrossDso && codeGenOpts.SanitizeCfiCanonicalJumpTables) { llvm_unreachable("NYI"); } assert(!MissingFeatures::memberFunctionPointerTypeMetadata()); } void CIRGenModule::setCIRFunctionAttributes(GlobalDecl GD, const CIRGenFunctionInfo &info, cir::FuncOp func, bool isThunk) { // TODO(cir): More logic of constructAttributeList is needed. cir::CallingConv callingConv; cir::SideEffect sideEffect; // Initialize PAL with existing attributes to merge attributes. mlir::NamedAttrList PAL{func.getExtraAttrs().getElements().getValue()}; constructAttributeList(func.getName(), info, GD, PAL, callingConv, sideEffect, /*AttrOnCallSite=*/false, isThunk); func.setExtraAttrsAttr(cir::ExtraFuncAttributesAttr::get( &getMLIRContext(), PAL.getDictionary(&getMLIRContext()))); // TODO(cir): Check X86_VectorCall incompatibility with WinARM64EC func.setCallingConv(callingConv); } void CIRGenModule::setFunctionAttributes(GlobalDecl globalDecl, cir::FuncOp func, bool isIncompleteFunction, bool isThunk) { // NOTE(cir): Original CodeGen checks if this is an intrinsic. In CIR we // represent them in dedicated ops. The correct attributes are ensured during // translation to LLVM. Thus, we don't need to check for them here. if (!isIncompleteFunction) { setCIRFunctionAttributes(globalDecl, getTypes().arrangeGlobalDeclaration(globalDecl), func, isThunk); } // TODO(cir): Complete the remaining part of the function. assert(!cir::MissingFeatures::setFunctionAttributes()); // TODO(cir): This needs a lot of work to better match CodeGen. That // ultimately ends up in setGlobalVisibility, which already has the linkage of // the LLVM GV (corresponding to our FuncOp) computed, so it doesn't have to // recompute it here. This is a minimal fix for now. if (!isLocalLinkage(getFunctionLinkage(globalDecl))) { auto decl = globalDecl.getDecl(); func.setGlobalVisibilityAttr(getGlobalVisibilityAttrFromDecl(decl)); } } /// If the specified mangled name is not in the module, /// create and return a CIR Function with the specified type. If there is /// something in the module with the specified name, return it potentially /// bitcasted to the right type. /// /// If D is non-null, it specifies a decl that corresponded to this. This is /// used to set the attributes on the function when it is first created. cir::FuncOp CIRGenModule::GetOrCreateCIRFunction( StringRef MangledName, mlir::Type Ty, GlobalDecl GD, bool ForVTable, bool DontDefer, bool IsThunk, ForDefinition_t IsForDefinition, mlir::ArrayAttr ExtraAttrs) { assert(!IsThunk && "NYI"); const auto *D = GD.getDecl(); // Any attempts to use a MultiVersion function should result in retrieving the // iFunc instead. Name mangling will handle the rest of the changes. if (const auto *FD = cast_or_null<FunctionDecl>(D)) { // For the device mark the function as one that should be emitted. if (getLangOpts().OpenMPIsTargetDevice && FD->isDefined() && !DontDefer && !IsForDefinition) { assert(0 && "OpenMP target functions NYI"); } if (FD->isMultiVersion()) llvm_unreachable("NYI"); } // Lookup the entry, lazily creating it if necessary. mlir::Operation *Entry = getGlobalValue(MangledName); if (Entry) { assert(isa<cir::FuncOp>(Entry) && "not implemented, only supports FuncOp for now"); if (WeakRefReferences.erase(Entry)) { llvm_unreachable("NYI"); } // Handle dropped DLL attributes. if (D && !D->hasAttr<DLLImportAttr>() && !D->hasAttr<DLLExportAttr>()) { // TODO(CIR): Entry->setDLLStorageClass setDSOLocal(Entry); } // If there are two attempts to define the same mangled name, issue an // error. auto Fn = cast<cir::FuncOp>(Entry); if (IsForDefinition && Fn && !Fn.isDeclaration()) { GlobalDecl OtherGD; // CHeck that GD is not yet in DiagnosedConflictingDefinitions is required // to make sure that we issue and error only once. if (lookupRepresentativeDecl(MangledName, OtherGD) && (GD.getCanonicalDecl().getDecl() != OtherGD.getCanonicalDecl().getDecl()) && DiagnosedConflictingDefinitions.insert(GD).second) { getDiags().Report(D->getLocation(), diag::err_duplicate_mangled_name) << MangledName; getDiags().Report(OtherGD.getDecl()->getLocation(), diag::note_previous_definition); } } if (Fn && Fn.getFunctionType() == Ty) { return Fn; } if (!IsForDefinition) { return Fn; } // TODO: clang checks here if this is a llvm::GlobalAlias... how will we // support this? } // This function doesn't have a complete type (for example, the return type is // an incomplete struct). Use a fake type instead, and make sure not to try to // set attributes. bool IsIncompleteFunction = false; cir::FuncType FTy; if (mlir::isa<cir::FuncType>(Ty)) { FTy = mlir::cast<cir::FuncType>(Ty); } else { assert(false && "NYI"); // FTy = mlir::FunctionType::get(VoidTy, false); IsIncompleteFunction = true; } auto *FD = llvm::cast_or_null<FunctionDecl>(D); // TODO: CodeGen includeds the linkage (ExternalLinkage) and only passes the // mangledname if Entry is nullptr auto F = createCIRFunction(getLocForFunction(FD), MangledName, FTy, FD); // If we already created a function with the same mangled name (but different // type) before, take its name and add it to the list of functions to be // replaced with F at the end of CodeGen. // // This happens if there is a prototype for a function (e.g. "int f()") and // then a definition of a different type (e.g. "int f(int x)"). if (Entry) { // Fetch a generic symbol-defining operation and its uses. auto SymbolOp = dyn_cast<mlir::SymbolOpInterface>(Entry); assert(SymbolOp && "Expected a symbol-defining operation"); // TODO(cir): When can this symbol be something other than a function? assert(isa<cir::FuncOp>(Entry) && "NYI"); // This might be an implementation of a function without a prototype, in // which case, try to do special replacement of calls which match the new // prototype. The really key thing here is that we also potentially drop // arguments from the call site so as to make a direct call, which makes the // inliner happier and suppresses a number of optimizer warnings (!) about // dropping arguments. if (SymbolOp.getSymbolUses(SymbolOp->getParentOp())) { ReplaceUsesOfNonProtoTypeWithRealFunction(Entry, F); } // Obliterate no-proto declaration. Entry->erase(); } if (D) setFunctionAttributes(GD, F, IsIncompleteFunction, IsThunk); if (ExtraAttrs) { llvm_unreachable("NYI"); } if (!DontDefer) { // All MSVC dtors other than the base dtor are linkonce_odr and delegate to // each other bottoming out wiht the base dtor. Therefore we emit non-base // dtors on usage, even if there is no dtor definition in the TU. if (isa_and_nonnull<CXXDestructorDecl>(D) && getCXXABI().useThunkForDtorVariant(cast<CXXDestructorDecl>(D), GD.getDtorType())) { llvm_unreachable("NYI"); // addDeferredDeclToEmit(GD); } // This is the first use or definition of a mangled name. If there is a // deferred decl with this name, remember that we need to emit it at the end // of the file. auto DDI = DeferredDecls.find(MangledName); if (DDI != DeferredDecls.end()) { // Move the potentially referenced deferred decl to the // DeferredDeclsToEmit list, and remove it from DeferredDecls (since we // don't need it anymore). addDeferredDeclToEmit(DDI->second); DeferredDecls.erase(DDI); // Otherwise, there are cases we have to worry about where we're using a // declaration for which we must emit a definition but where we might not // find a top-level definition. // - member functions defined inline in their classes // - friend functions defined inline in some class // - special member functions with implicit definitions // If we ever change our AST traversal to walk into class methods, this // will be unnecessary. // // We also don't emit a definition for a function if it's going to be an // entry in a vtable, unless it's already marked as used. } else if (getLangOpts().CPlusPlus && D) { // Look for a declaration that's lexically in a record. for (const auto *FD = cast<FunctionDecl>(D)->getMostRecentDecl(); FD; FD = FD->getPreviousDecl()) { if (isa<CXXRecordDecl>(FD->getLexicalDeclContext())) { if (FD->doesThisDeclarationHaveABody()) { addDeferredDeclToEmit(GD.getWithDecl(FD)); break; } } } } } if (!IsIncompleteFunction) { assert(F.getFunctionType() == Ty); return F; } // TODO(cir): Might need bitcast to different address space. assert(!cir::MissingFeatures::addressSpace()); return F; } mlir::Location CIRGenModule::getLoc(SourceLocation SLoc) { assert(SLoc.isValid() && "expected valid source location"); const SourceManager &SM = astContext.getSourceManager(); PresumedLoc PLoc = SM.getPresumedLoc(SLoc); StringRef Filename = PLoc.getFilename(); return mlir::FileLineColLoc::get(builder.getStringAttr(Filename), PLoc.getLine(), PLoc.getColumn()); } mlir::Location CIRGenModule::getLoc(SourceRange SLoc) { assert(SLoc.isValid() && "expected valid source location"); mlir::Location B = getLoc(SLoc.getBegin()); mlir::Location E = getLoc(SLoc.getEnd()); SmallVector<mlir::Location, 2> locs = {B, E}; mlir::Attribute metadata; return mlir::FusedLoc::get(locs, metadata, &getMLIRContext()); } mlir::Location CIRGenModule::getLoc(mlir::Location lhs, mlir::Location rhs) { SmallVector<mlir::Location, 2> locs = {lhs, rhs}; mlir::Attribute metadata; return mlir::FusedLoc::get(locs, metadata, &getMLIRContext()); } void CIRGenModule::emitGlobalDecl(clang::GlobalDecl &D) { // We should call GetAddrOfGlobal with IsForDefinition set to true in order // to get a Value with exactly the type we need, not something that might // have been created for another decl with the same mangled name but // different type. auto *Op = GetAddrOfGlobal(D, ForDefinition); // In case of different address spaces, we may still get a cast, even with // IsForDefinition equal to true. Query mangled names table to get // GlobalValue. if (!Op) { Op = getGlobalValue(getMangledName(D)); } // In case of different address spaces, we may still get a cast, even with // IsForDefinition equal to true. Query mangled names table to get // GlobalValue. if (!Op) llvm_unreachable("Address spaces NYI"); // Make sure getGlobalValue returned non-null. assert(Op); // Check to see if we've already emitted this. This is necessary for a // couple of reasons: first, decls can end up in deferred-decls queue // multiple times, and second, decls can end up with definitions in unusual // ways (e.g. by an extern inline function acquiring a strong function // redefinition). Just ignore those cases. // TODO: Not sure what to map this to for MLIR auto globalValueOp = Op; if (auto Gv = dyn_cast<cir::GetGlobalOp>(Op)) { auto *result = mlir::SymbolTable::lookupSymbolIn(getModule(), Gv.getNameAttr()); globalValueOp = result; } if (auto cirGlobalValue = dyn_cast<cir::CIRGlobalValueInterface>(globalValueOp)) { if (!cirGlobalValue.isDeclaration()) return; } // If this is OpenMP, check if it is legal to emit this global normally. if (getLangOpts().OpenMP && openMPRuntime && openMPRuntime->emitTargetGlobal(D)) return; // Otherwise, emit the definition and move on to the next one. emitGlobalDefinition(D, Op); } void CIRGenModule::emitDeferred(unsigned recursionLimit) { // Emit deferred declare target declarations if (getLangOpts().OpenMP && !getLangOpts().OpenMPSimd) getOpenMPRuntime().emitDeferredTargetDecls(); // Emit code for any potentially referenced deferred decls. Since a previously // unused static decl may become used during the generation of code for a // static function, iterate until no changes are made. if (!DeferredVTables.empty()) { emitDeferredVTables(); // Emitting a vtable doesn't directly cause more vtables to // become deferred, although it can cause functions to be // emitted that then need those vtables. assert(DeferredVTables.empty()); } // Emit CUDA/HIP static device variables referenced by host code only. Note we // should not clear CUDADeviceVarODRUsedByHost since it is still needed for // further handling. if ((getLangOpts().CUDA || getLangOpts().HIP) && getLangOpts().CUDAIsDevice && !getASTContext().CUDADeviceVarODRUsedByHost.empty()) { llvm_unreachable("NYI"); } // Stop if we're out of both deferred vtables and deferred declarations. if (DeferredDeclsToEmit.empty()) return; // Grab the list of decls to emit. If emitGlobalDefinition schedules more // work, it will not interfere with this. std::vector<GlobalDecl> CurDeclsToEmit; CurDeclsToEmit.swap(DeferredDeclsToEmit); if (recursionLimit == 0) return; recursionLimit--; for (auto &D : CurDeclsToEmit) { if (getCodeGenOpts().ClangIRSkipFunctionsFromSystemHeaders) { auto *decl = D.getDecl(); assert(decl && "expected decl"); if (astContext.getSourceManager().isInSystemHeader(decl->getLocation())) continue; } emitGlobalDecl(D); // If we found out that we need to emit more decls, do that recursively. // This has the advantage that the decls are emitted in a DFS and related // ones are close together, which is convenient for testing. if (!DeferredVTables.empty() || !DeferredDeclsToEmit.empty()) { emitDeferred(recursionLimit); assert(DeferredVTables.empty() && DeferredDeclsToEmit.empty()); } } } mlir::IntegerAttr CIRGenModule::getSize(CharUnits size) { return builder.getSizeFromCharUnits(&getMLIRContext(), size); } mlir::Operation * CIRGenModule::GetAddrOfGlobal(GlobalDecl GD, ForDefinition_t IsForDefinition) { const Decl *D = GD.getDecl(); if (isa<CXXConstructorDecl>(D) || isa<CXXDestructorDecl>(D)) return getAddrOfCXXStructor(GD, /*FnInfo=*/nullptr, /*FnType=*/nullptr, /*DontDefer=*/false, IsForDefinition); if (isa<CXXMethodDecl>(D)) { auto FInfo = &getTypes().arrangeCXXMethodDeclaration(cast<CXXMethodDecl>(D)); auto Ty = getTypes().GetFunctionType(*FInfo); return GetAddrOfFunction(GD, Ty, /*ForVTable=*/false, /*DontDefer=*/false, IsForDefinition); } if (isa<FunctionDecl>(D)) { const CIRGenFunctionInfo &FI = getTypes().arrangeGlobalDeclaration(GD); auto Ty = getTypes().GetFunctionType(FI); return GetAddrOfFunction(GD, Ty, /*ForVTable=*/false, /*DontDefer=*/false, IsForDefinition); } return getAddrOfGlobalVar(cast<VarDecl>(D), /*Ty=*/nullptr, IsForDefinition) .getDefiningOp(); } void CIRGenModule::Release() { assert(!MissingFeatures::emitModuleInitializers()); emitDeferred(getCodeGenOpts().ClangIRBuildDeferredThreshold); assert(!MissingFeatures::emittedDeferredDecls()); assert(!MissingFeatures::emitVTablesOpportunistically()); assert(!MissingFeatures::applyGlobalValReplacements()); applyReplacements(); assert(!MissingFeatures::emitMultiVersionFunctions()); assert(!MissingFeatures::incrementalExtensions()); assert(!MissingFeatures::emitCXXModuleInitFunc()); emitCXXGlobalInitFunc(); assert(!MissingFeatures::emitCXXGlobalCleanUpFunc()); assert(!MissingFeatures::registerGlobalDtorsWithAtExit()); assert(!MissingFeatures::emitCXXThreadLocalInitFunc()); assert(!MissingFeatures::objCRuntime()); assert(!MissingFeatures::openMPRuntime()); assert(!MissingFeatures::pgoReader()); assert(!MissingFeatures::emitCtorList()); // GlobalCtors, GlobalDtors emitGlobalAnnotations(); assert(!MissingFeatures::emitStaticExternCAliases()); assert(!MissingFeatures::checkAliases()); assert(!MissingFeatures::emitDeferredUnusedCoverageMappings()); assert(!MissingFeatures::cirGenPGO()); // setValueProfilingFlag, // setProfileVersion assert(!MissingFeatures::coverageMapping()); if (getCodeGenOpts().SanitizeCfiCrossDso) { llvm_unreachable("NYI"); } if (langOpts.Sanitize.has(SanitizerKind::KCFI)) llvm_unreachable("NYI"); assert(!MissingFeatures::emitAtAvailableLinkGuard()); if (astContext.getTargetInfo().getTriple().isWasm()) llvm_unreachable("NYI"); if (getTriple().isSPIRV() && getTriple().getVendor() == llvm::Triple::AMD) { llvm_unreachable("NYI"); } // Emit a global array containing all external kernels or device variables // used by host functions and mark it as used for CUDA/HIP. This is necessary // to get kernels or device variables in archives linked in even if these // kernels or device variables are only used in host functions. if (!astContext.CUDAExternalDeviceDeclODRUsedByHost.empty()) { llvm_unreachable("NYI"); } assert(!MissingFeatures::emitLLVMUsed()); assert(!MissingFeatures::sanStats()); if (codeGenOpts.Autolink && (astContext.getLangOpts().Modules || !MissingFeatures::linkerOptionsMetadata())) { assert(!MissingFeatures::emitModuleLinkOptions()); } // On ELF we pass the dependent library specifiers directly to the linker // without manipulating them. This is in contrast to other platforms where // they are mapped to a specific linker option by the compiler. This // difference is a result of the greater variety of ELF linkers and the fact // that ELF linkers tend to handle libraries in a more complicated fashion // than on other platforms. This forces us to defer handling the dependent // libs to the linker. // // CUDA/HIP device and host libraries are different. Currently there is no // way to differentiate dependent libraries for host or device. Existing // usage of #pragma comment(lib, *) is intended for host libraries on // Windows. Therefore emit llvm.dependent-libraries only for host. assert(!MissingFeatures::elfDependentLibraries()); assert(!MissingFeatures::dwarfVersion()); if (codeGenOpts.Dwarf64) llvm_unreachable("NYI"); if (astContext.getLangOpts().SemanticInterposition) // Require various optimization to respect semantic interposition. llvm_unreachable("NYI"); if (codeGenOpts.EmitCodeView) { // Indicate that we want CodeView in the metadata. llvm_unreachable("NYI"); } if (codeGenOpts.CodeViewGHash) { llvm_unreachable("NYI"); } if (codeGenOpts.ControlFlowGuard) { // Function ID tables and checks for Control Flow Guard (cfguard=2). llvm_unreachable("NYI"); } else if (codeGenOpts.ControlFlowGuardNoChecks) { // Function ID tables for Control Flow Guard (cfguard=1). llvm_unreachable("NYI"); } if (codeGenOpts.EHContGuard) { // Function ID tables for EH Continuation Guard. llvm_unreachable("NYI"); } if (astContext.getLangOpts().Kernel) { // Note if we are compiling with /kernel. llvm_unreachable("NYI"); } if (codeGenOpts.OptimizationLevel > 0 && codeGenOpts.StrictVTablePointers) { // We don't support LTO with 2 with different StrictVTablePointers // FIXME: we could support it by stripping all the information introduced // by StrictVTablePointers. llvm_unreachable("NYI"); } if (getModuleDebugInfo()) // We support a single version in the linked module. The LLVM // parser will drop debug info with a different version number // (and warn about it, too). llvm_unreachable("NYI"); // We need to record the widths of enums and wchar_t, so that we can generate // the correct build attributes in the ARM backend. wchar_size is also used by // TargetLibraryInfo. assert(!MissingFeatures::wcharWidth()); if (getTriple().isOSzOS()) { llvm_unreachable("NYI"); } llvm::Triple t = astContext.getTargetInfo().getTriple(); if (t.isARM() || t.isThumb()) { // The minimum width of an enum in bytes assert(!MissingFeatures::enumWidth()); } if (t.isRISCV()) { llvm_unreachable("NYI"); } if (codeGenOpts.SanitizeCfiCrossDso) { // Indicate that we want cross-DSO control flow integrity checks. llvm_unreachable("NYI"); } if (codeGenOpts.WholeProgramVTables) { // Indicate whether VFE was enabled for this module, so that the // vcall_visibility metadata added under whole program vtables is handled // appropriately in the optimizer. llvm_unreachable("NYI"); } if (langOpts.Sanitize.has(SanitizerKind::CFIICall)) { llvm_unreachable("NYI"); } if (codeGenOpts.SanitizeCfiICallNormalizeIntegers) { llvm_unreachable("NYI"); } if (langOpts.Sanitize.has(SanitizerKind::KCFI)) { llvm_unreachable("NYI"); } if (codeGenOpts.CFProtectionReturn && target.checkCFProtectionReturnSupported(getDiags())) { // Indicate that we want to instrument return control flow protection. llvm_unreachable("NYI"); } if (codeGenOpts.CFProtectionBranch && target.checkCFProtectionBranchSupported(getDiags())) { // Indicate that we want to instrument branch control flow protection. llvm_unreachable("NYI"); } if (codeGenOpts.FunctionReturnThunks) llvm_unreachable("NYI"); if (codeGenOpts.IndirectBranchCSPrefix) llvm_unreachable("NYI"); // Add module metadata for return address signing (ignoring // non-leaf/all) and stack tagging. These are actually turned on by function // attributes, but we use module metadata to emit build attributes. This is // needed for LTO, where the function attributes are inside bitcode // serialised into a global variable by the time build attributes are // emitted, so we can't access them. LTO objects could be compiled with // different flags therefore module flags are set to "Min" behavior to achieve // the same end result of the normal build where e.g BTI is off if any object // doesn't support it. if (astContext.getTargetInfo().hasFeature("ptrauth") && langOpts.getSignReturnAddressScope() != LangOptions::SignReturnAddressScopeKind::None) llvm_unreachable("NYI"); if (langOpts.Sanitize.has(SanitizerKind::MemtagStack)) llvm_unreachable("NYI"); if (t.isARM() || t.isThumb() || t.isAArch64()) { if (langOpts.BranchTargetEnforcement) llvm_unreachable("NYI"); if (langOpts.BranchProtectionPAuthLR) llvm_unreachable("NYI"); if (langOpts.GuardedControlStack) llvm_unreachable("NYI"); if (langOpts.hasSignReturnAddress()) llvm_unreachable("NYI"); if (langOpts.isSignReturnAddressScopeAll()) llvm_unreachable("NYI"); if (!langOpts.isSignReturnAddressWithAKey()) llvm_unreachable("NYI"); if (langOpts.PointerAuthELFGOT) llvm_unreachable("NYI"); if (getTriple().isOSLinux()) { assert(getTriple().isOSBinFormatELF()); assert(!MissingFeatures::ptrAuth()); } } if (codeGenOpts.StackClashProtector) llvm_unreachable("NYI"); if (codeGenOpts.StackProbeSize && codeGenOpts.StackProbeSize != 4096) llvm_unreachable("NYI"); if (!codeGenOpts.MemoryProfileOutput.empty()) { llvm_unreachable("NYI"); } if (langOpts.EHAsynch) llvm_unreachable("NYI"); // Indicate whether this Module was compiled with -fopenmp assert(!MissingFeatures::openMP()); // Emit OpenCL specific module metadata: OpenCL/SPIR version. if (langOpts.CUDAIsDevice && getTriple().isSPIRV()) llvm_unreachable("CUDA SPIR-V NYI"); if (langOpts.OpenCL) { emitOpenCLMetadata(); // Emit SPIR version. if (getTriple().isSPIR()) llvm_unreachable("SPIR target NYI"); } // HLSL related end of code gen work items. if (langOpts.HLSL) llvm_unreachable("NYI"); if (uint32_t picLevel = astContext.getLangOpts().PICLevel) { assert(picLevel < 3 && "Invalid PIC Level"); assert(!MissingFeatures::setPICLevel()); if (astContext.getLangOpts().PIE) assert(!MissingFeatures::setPIELevel()); } if (getCodeGenOpts().CodeModel.size() > 0) { unsigned cm = llvm::StringSwitch<unsigned>(getCodeGenOpts().CodeModel) .Case("tiny", llvm::CodeModel::Tiny) .Case("small", llvm::CodeModel::Small) .Case("kernel", llvm::CodeModel::Kernel) .Case("medium", llvm::CodeModel::Medium) .Case("large", llvm::CodeModel::Large) .Default(~0u); if (cm != ~0u) { llvm::CodeModel::Model codeModel = static_cast<llvm::CodeModel::Model>(cm); (void)codeModel; assert(!MissingFeatures::codeModel()); if ((cm == llvm::CodeModel::Medium || cm == llvm::CodeModel::Large) && astContext.getTargetInfo().getTriple().getArch() == llvm::Triple::x86_64) { assert(!MissingFeatures::largeDataThreshold()); } } } if (codeGenOpts.NoPLT) llvm_unreachable("NYI"); assert(!MissingFeatures::directAccessExternalData()); if (codeGenOpts.UnwindTables) theModule->setAttr( cir::CIRDialect::getUWTableAttrName(), cir::UWTableAttr::get(&getMLIRContext(), cir::UWTableKind(codeGenOpts.UnwindTables))); switch (codeGenOpts.getFramePointer()) { case CodeGenOptions::FramePointerKind::None: // 0 ("none") is the default. break; case CodeGenOptions::FramePointerKind::Reserved: assert(!MissingFeatures::setFramePointer()); break; case CodeGenOptions::FramePointerKind::NonLeaf: assert(!MissingFeatures::setFramePointer()); break; case CodeGenOptions::FramePointerKind::All: assert(!MissingFeatures::setFramePointer()); break; } assert(!MissingFeatures::simplifyPersonality()); if (getCodeGenOpts().EmitDeclMetadata) llvm_unreachable("NYI"); if (getCodeGenOpts().CoverageNotesFile.size() || getCodeGenOpts().CoverageDataFile.size()) llvm_unreachable("NYI"); if (getModuleDebugInfo()) llvm_unreachable("NYI"); assert(!MissingFeatures::emitVersionIdentMetadata()); if (!getCodeGenOpts().RecordCommandLine.empty()) llvm_unreachable("NYI"); if (!getCodeGenOpts().StackProtectorGuard.empty()) llvm_unreachable("NYI"); if (!getCodeGenOpts().StackProtectorGuardReg.empty()) llvm_unreachable("NYI"); if (!getCodeGenOpts().StackProtectorGuardSymbol.empty()) llvm_unreachable("NYI"); if (getCodeGenOpts().StackProtectorGuardOffset != INT_MAX) llvm_unreachable("NYI"); if (getCodeGenOpts().StackAlignment) llvm_unreachable("NYI"); if (getCodeGenOpts().SkipRaxSetup) llvm_unreachable("NYI"); if (getLangOpts().RegCall4) llvm_unreachable("NYI"); if (getASTContext().getTargetInfo().getMaxTLSAlign()) llvm_unreachable("NYI"); assert(!MissingFeatures::emitTargetGlobals()); assert(!MissingFeatures::emitTargetMetadata()); assert(!MissingFeatures::emitBackendOptionsMetadata()); // If there is device offloading code embed it in the host now. assert(!MissingFeatures::embedObject()); // Set visibility from DLL storage class // We do this at the end of LLVM IR generation; after any operation // that might affect the DLL storage class or the visibility, and // before anything that might act on these. assert(!MissingFeatures::setVisibilityFromDLLStorageClass()); // Check the tail call symbols are truly undefined. if (getTriple().isPPC() && !MissingFeatures::mustTailCallUndefinedGlobals()) { llvm_unreachable("NYI"); } } namespace { // TODO(cir): This should be a common helper shared with CodeGen. struct FunctionIsDirectlyRecursive : public ConstStmtVisitor<FunctionIsDirectlyRecursive, bool> { const StringRef name; const Builtin::Context &builtinCtx; FunctionIsDirectlyRecursive(StringRef name, const Builtin::Context &builtinCtx) : name(name), builtinCtx(builtinCtx) {} bool VisitCallExpr(const CallExpr *expr) { const FunctionDecl *func = expr->getDirectCallee(); if (!func) return false; AsmLabelAttr *attr = func->getAttr<AsmLabelAttr>(); if (attr && name == attr->getLabel()) return true; unsigned builtinId = func->getBuiltinID(); if (!builtinId || !builtinCtx.isLibFunction(builtinId)) return false; StringRef builtinName = builtinCtx.getName(builtinId); if (builtinName.starts_with("__builtin_") && name == builtinName.slice(strlen("__builtin_"), StringRef::npos)) { return true; } return false; } bool VisitStmt(const Stmt *stmt) { for (const Stmt *child : stmt->children()) if (child && this->Visit(child)) return true; return false; } }; } // namespace // isTriviallyRecursive - Check if this function calls another // decl that, because of the asm attribute or the other decl being a builtin, // ends up pointing to itself. // TODO(cir): This should be a common helper shared with CodeGen. bool CIRGenModule::isTriviallyRecursive(const FunctionDecl *func) { StringRef name; if (getCXXABI().getMangleContext().shouldMangleDeclName(func)) { // asm labels are a special kind of mangling we have to support. AsmLabelAttr *attr = func->getAttr<AsmLabelAttr>(); if (!attr) return false; name = attr->getLabel(); } else { name = func->getName(); } FunctionIsDirectlyRecursive walker(name, astContext.BuiltinInfo); const Stmt *body = func->getBody(); return body ? walker.Visit(body) : false; } // TODO(cir): This should be a common helper shared with CodeGen. bool CIRGenModule::shouldEmitFunction(GlobalDecl globalDecl) { if (getFunctionLinkage(globalDecl) != GlobalLinkageKind::AvailableExternallyLinkage) return true; const auto *func = cast<FunctionDecl>(globalDecl.getDecl()); // Inline builtins declaration must be emitted. They often are fortified // functions. if (func->isInlineBuiltinDeclaration()) return true; if (codeGenOpts.OptimizationLevel == 0 && !func->hasAttr<AlwaysInlineAttr>()) return false; // We don't import function bodies from other named module units since that // behavior may break ABI compatibility of the current unit. if (const Module *mod = func->getOwningModule(); mod && mod->getTopLevelModule()->isNamedModule() && astContext.getCurrentNamedModule() != mod->getTopLevelModule()) { // There are practices to mark template member function as always-inline // and mark the template as extern explicit instantiation but not give // the definition for member function. So we have to emit the function // from explicitly instantiation with always-inline. // // See https://github.com/llvm/llvm-project/issues/86893 for details. // // TODO: Maybe it is better to give it a warning if we call a non-inline // function from other module units which is marked as always-inline. if (!func->isTemplateInstantiation() || !func->hasAttr<AlwaysInlineAttr>()) return false; } if (func->hasAttr<NoInlineAttr>()) return false; if (func->hasAttr<DLLImportAttr>() && !func->hasAttr<AlwaysInlineAttr>()) assert(!cir::MissingFeatures::setDLLImportDLLExport() && "shouldEmitFunction for dllimport is NYI"); // PR9614. Avoid cases where the source code is lying to us. An available // externally function should have an equivalent function somewhere else, // but a function that calls itself through asm label/`__builtin_` trickery is // clearly not equivalent to the real implementation. // This happens in glibc's btowc and in some configure checks. return !isTriviallyRecursive(func); } bool CIRGenModule::supportsCOMDAT() const { return getTriple().supportsCOMDAT(); } void CIRGenModule::maybeSetTrivialComdat(const Decl &d, mlir::Operation *op) { if (!shouldBeInCOMDAT(*this, d)) return; auto globalOp = dyn_cast_or_null<cir::GlobalOp>(op); if (globalOp) globalOp.setComdat(true); // Keep it as missing feature as we need to implement comdat for FuncOp. // in the future. assert(!cir::MissingFeatures::setComdat() && "NYI"); } bool CIRGenModule::isInNoSanitizeList(SanitizerMask Kind, cir::FuncOp Fn, SourceLocation Loc) const { const auto &NoSanitizeL = getASTContext().getNoSanitizeList(); // NoSanitize by function name. if (NoSanitizeL.containsFunction(Kind, Fn.getName())) llvm_unreachable("NYI"); // NoSanitize by location. if (Loc.isValid()) return NoSanitizeL.containsLocation(Kind, Loc); // If location is unknown, this may be a compiler-generated function. Assume // it's located in the main file. auto &SM = getASTContext().getSourceManager(); FileEntryRef MainFile = *SM.getFileEntryRefForID(SM.getMainFileID()); if (NoSanitizeL.containsFile(Kind, MainFile.getName())) return true; // Check "src" prefix. if (Loc.isValid()) return NoSanitizeL.containsLocation(Kind, Loc); // If location is unknown, this may be a compiler-generated function. Assume // it's located in the main file. return NoSanitizeL.containsFile(Kind, MainFile.getName()); } void CIRGenModule::AddDeferredUnusedCoverageMapping(Decl *D) { // Do we need to generate coverage mapping? if (!codeGenOpts.CoverageMapping) return; llvm_unreachable("NYI"); } void CIRGenModule::UpdateCompletedType(const TagDecl *TD) { // Make sure that this type is translated. genTypes.UpdateCompletedType(TD); } void CIRGenModule::addReplacement(StringRef Name, mlir::Operation *Op) { Replacements[Name] = Op; } void CIRGenModule::replacePointerTypeArgs(cir::FuncOp OldF, cir::FuncOp NewF) { auto optionalUseRange = OldF.getSymbolUses(theModule); if (!optionalUseRange) return; for (auto U : *optionalUseRange) { // CallTryOp only shows up after FlattenCFG. auto Call = mlir::dyn_cast<cir::CallOp>(U.getUser()); if (!Call) continue; auto ArgOps = Call.getArgOps(); auto FuncArgTypes = NewF.getFunctionType().getInputs(); for (unsigned I = 0; I < FuncArgTypes.size(); I++) { if (ArgOps[I].getType() == FuncArgTypes[I]) continue; auto argPointerTy = mlir::dyn_cast<cir::PointerType>(ArgOps[I].getType()); auto funcArgPointerTy = mlir::dyn_cast<cir::PointerType>(FuncArgTypes[I]); // If we can't solve it, leave it for the verifier to bail out. if (!argPointerTy || !funcArgPointerTy) continue; mlir::OpBuilder::InsertionGuard guard(builder); builder.setInsertionPoint(Call); auto castedArg = builder.createBitcast(Call.getLoc(), ArgOps[I], funcArgPointerTy); Call.setArg(I, castedArg); } } } void CIRGenModule::applyReplacements() { for (auto &I : Replacements) { StringRef MangledName = I.first(); mlir::Operation *Replacement = I.second; auto *Entry = getGlobalValue(MangledName); if (!Entry) continue; assert(isa<cir::FuncOp>(Entry) && "expected function"); auto OldF = cast<cir::FuncOp>(Entry); auto NewF = dyn_cast<cir::FuncOp>(Replacement); assert(NewF && "not implemented"); // LLVM has opaque pointer but CIR not. So we may have to handle these // different pointer types when performing replacement. replacePointerTypeArgs(OldF, NewF); // Replace old with new, but keep the old order. if (OldF.replaceAllSymbolUses(NewF.getSymNameAttr(), theModule).failed()) llvm_unreachable("internal error, cannot RAUW symbol"); if (NewF) { NewF->moveBefore(OldF); OldF->erase(); } } } void CIRGenModule::emitExplicitCastExprType(const ExplicitCastExpr *E, CIRGenFunction *CGF) { // Bind VLAs in the cast type. if (CGF && E->getType()->isVariablyModifiedType()) llvm_unreachable("NYI"); assert(!cir::MissingFeatures::generateDebugInfo() && "NYI"); } void CIRGenModule::HandleCXXStaticMemberVarInstantiation(VarDecl *VD) { auto DK = VD->isThisDeclarationADefinition(); if (DK == VarDecl::Definition && VD->hasAttr<DLLImportAttr>()) return; TemplateSpecializationKind TSK = VD->getTemplateSpecializationKind(); // If we have a definition, this might be a deferred decl. If the // instantiation is explicit, make sure we emit it at the end. if (VD->getDefinition() && TSK == TSK_ExplicitInstantiationDefinition) { llvm_unreachable("NYI"); } emitTopLevelDecl(VD); } cir::GlobalOp CIRGenModule::createOrReplaceCXXRuntimeVariable( mlir::Location loc, StringRef Name, mlir::Type Ty, cir::GlobalLinkageKind Linkage, clang::CharUnits Alignment) { cir::GlobalOp OldGV{}; auto GV = dyn_cast_or_null<cir::GlobalOp>( mlir::SymbolTable::lookupSymbolIn(getModule(), Name)); if (GV) { // Check if the variable has the right type. if (GV.getSymType() == Ty) return GV; // Because C++ name mangling, the only way we can end up with an already // existing global with the same name is if it has been declared extern // "C". assert(GV.isDeclaration() && "Declaration has wrong type!"); OldGV = GV; } // Create a new variable. GV = CIRGenModule::createGlobalOp(*this, loc, Name, Ty); // Set up extra information and add to the module GV.setLinkageAttr( cir::GlobalLinkageKindAttr::get(&getMLIRContext(), Linkage)); mlir::SymbolTable::setSymbolVisibility(GV, CIRGenModule::getMLIRVisibility(GV)); if (OldGV) { // Replace occurrences of the old variable if needed. GV.setName(OldGV.getName()); if (!OldGV->use_empty()) { // TODO(cir): remove erase call above and use replaceGlobal here. llvm_unreachable("NYI"); } OldGV->erase(); } if (supportsCOMDAT() && cir::isWeakForLinker(Linkage) && !GV.hasAvailableExternallyLinkage()) { GV.setComdat(true); } GV.setAlignmentAttr(getSize(Alignment)); setDSOLocal(static_cast<mlir::Operation *>(GV)); return GV; } bool CIRGenModule::shouldOpportunisticallyEmitVTables() { if (codeGenOpts.OptimizationLevel != 0) llvm_unreachable("NYI"); return codeGenOpts.OptimizationLevel > 0; } void CIRGenModule::emitVTableTypeMetadata(const CXXRecordDecl *RD, cir::GlobalOp VTable, const VTableLayout &VTLayout) { if (!getCodeGenOpts().LTOUnit) return; llvm_unreachable("NYI"); } mlir::Attribute CIRGenModule::getAddrOfRTTIDescriptor(mlir::Location loc, QualType Ty, bool ForEH) { // Return a bogus pointer if RTTI is disabled, unless it's for EH. // FIXME: should we even be calling this method if RTTI is disabled // and it's not for EH? if (!shouldEmitRTTI(ForEH)) return getBuilder().getConstNullPtrAttr(builder.getUInt8PtrTy()); if (ForEH && Ty->isObjCObjectPointerType() && getLangOpts().ObjCRuntime.isGNUFamily()) { llvm_unreachable("NYI"); } return getCXXABI().getAddrOfRTTIDescriptor(loc, Ty); } /// TODO(cir): once we have cir.module, add this as a convenience method there. /// /// Look up the specified global in the module symbol table. /// 1. If it does not exist, add a declaration of the global and return it. /// 2. Else, the global exists but has the wrong type: return the function /// with a constantexpr cast to the right type. /// 3. Finally, if the existing global is the correct declaration, return the /// existing global. cir::GlobalOp CIRGenModule::getOrInsertGlobal( mlir::Location loc, StringRef Name, mlir::Type Ty, llvm::function_ref<cir::GlobalOp()> CreateGlobalCallback) { // See if we have a definition for the specified global already. auto GV = dyn_cast_or_null<cir::GlobalOp>(getGlobalValue(Name)); if (!GV) { GV = CreateGlobalCallback(); } assert(GV && "The CreateGlobalCallback is expected to create a global"); // If the variable exists but has the wrong type, return a bitcast to the // right type. auto GVTy = GV.getSymType(); assert(!cir::MissingFeatures::addressSpace()); auto PTy = builder.getPointerTo(Ty); if (GVTy != PTy) llvm_unreachable("NYI"); // Otherwise, we just found the existing function or a prototype. return GV; } // Overload to construct a global variable using its constructor's defaults. cir::GlobalOp CIRGenModule::getOrInsertGlobal(mlir::Location loc, StringRef Name, mlir::Type Ty) { return getOrInsertGlobal(loc, Name, Ty, [&] { return CIRGenModule::createGlobalOp(*this, loc, Name, builder.getPointerTo(Ty)); }); } // TODO(cir): this can be shared with LLVM codegen. CharUnits CIRGenModule::computeNonVirtualBaseClassOffset( const CXXRecordDecl *DerivedClass, CastExpr::path_const_iterator Start, CastExpr::path_const_iterator End) { CharUnits Offset = CharUnits::Zero(); const ASTContext &astContext = getASTContext(); const CXXRecordDecl *RD = DerivedClass; for (CastExpr::path_const_iterator I = Start; I != End; ++I) { const CXXBaseSpecifier *Base = *I; assert(!Base->isVirtual() && "Should not see virtual bases here!"); // Get the layout. const ASTRecordLayout &Layout = astContext.getASTRecordLayout(RD); const auto *BaseDecl = cast<CXXRecordDecl>(Base->getType()->castAs<RecordType>()->getDecl()); // Add the offset. Offset += Layout.getBaseClassOffset(BaseDecl); RD = BaseDecl; } return Offset; } void CIRGenModule::Error(SourceLocation loc, StringRef message) { unsigned diagID = getDiags().getCustomDiagID(DiagnosticsEngine::Error, "%0"); getDiags().Report(astContext.getFullLoc(loc), diagID) << message; } /// Print out an error that codegen doesn't support the specified stmt yet. void CIRGenModule::ErrorUnsupported(const Stmt *S, const char *Type) { unsigned DiagID = getDiags().getCustomDiagID(DiagnosticsEngine::Error, "cannot compile this %0 yet"); std::string Msg = Type; getDiags().Report(astContext.getFullLoc(S->getBeginLoc()), DiagID) << Msg << S->getSourceRange(); } /// Print out an error that codegen doesn't support the specified decl yet. void CIRGenModule::ErrorUnsupported(const Decl *D, const char *Type) { unsigned DiagID = getDiags().getCustomDiagID(DiagnosticsEngine::Error, "cannot compile this %0 yet"); std::string Msg = Type; getDiags().Report(astContext.getFullLoc(D->getLocation()), DiagID) << Msg; } cir::SourceLanguage CIRGenModule::getCIRSourceLanguage() { using ClangStd = clang::LangStandard; using CIRLang = cir::SourceLanguage; auto opts = getLangOpts(); if (opts.OpenCL && !opts.OpenCLCPlusPlus) return CIRLang::OpenCLC; if (opts.CPlusPlus || opts.CPlusPlus11 || opts.CPlusPlus14 || opts.CPlusPlus17 || opts.CPlusPlus20 || opts.CPlusPlus23 || opts.CPlusPlus26) return CIRLang::CXX; if (opts.C99 || opts.C11 || opts.C17 || opts.C23 || opts.LangStd == ClangStd::lang_c89 || opts.LangStd == ClangStd::lang_gnu89) return CIRLang::C; // TODO(cir): support remaining source languages. llvm_unreachable("CIR does not yet support the given source language"); } LangAS CIRGenModule::getGlobalVarAddressSpace(const VarDecl *D) { if (langOpts.OpenCL) { LangAS AS = D ? D->getType().getAddressSpace() : LangAS::opencl_global; assert(AS == LangAS::opencl_global || AS == LangAS::opencl_global_device || AS == LangAS::opencl_global_host || AS == LangAS::opencl_constant || AS == LangAS::opencl_local || AS >= LangAS::FirstTargetAddressSpace); return AS; } if (langOpts.SYCLIsDevice && (!D || D->getType().getAddressSpace() == LangAS::Default)) llvm_unreachable("NYI"); if (langOpts.CUDA && langOpts.CUDAIsDevice) llvm_unreachable("NYI"); if (langOpts.OpenMP) llvm_unreachable("NYI"); return getTargetCIRGenInfo().getGlobalVarAddressSpace(*this, D); } mlir::ArrayAttr CIRGenModule::emitAnnotationArgs(const AnnotateAttr *attr) { ArrayRef<Expr *> exprs = {attr->args_begin(), attr->args_size()}; if (exprs.empty()) { return mlir::ArrayAttr::get(&getMLIRContext(), {}); } llvm::FoldingSetNodeID id; for (Expr *e : exprs) { id.Add(cast<clang::ConstantExpr>(e)->getAPValueResult()); } mlir::ArrayAttr &lookup = annotationArgs[id.ComputeHash()]; if (lookup) return lookup; llvm::SmallVector<mlir::Attribute, 4> args; args.reserve(exprs.size()); for (Expr *e : exprs) { auto &ce = *cast<clang::ConstantExpr>(e); if (auto *const strE = clang::dyn_cast<clang::StringLiteral>(ce.IgnoreParenCasts())) { // Add trailing null character as StringLiteral->getString() does not args.push_back(builder.getStringAttr(strE->getString())); } else if (ce.hasAPValueResult()) { // Handle case which can be evaluated to some numbers, not only literals const auto &ap = ce.getAPValueResult(); if (ap.isInt()) { args.push_back(mlir::IntegerAttr::get( mlir::IntegerType::get(&getMLIRContext(), ap.getInt().getBitWidth()), ap.getInt())); } else { llvm_unreachable("NYI like float, fixed-point, array..."); } } else { llvm_unreachable("NYI"); } } lookup = builder.getArrayAttr(args); return lookup; } cir::AnnotationAttr CIRGenModule::emitAnnotateAttr(const clang::AnnotateAttr *aa) { mlir::StringAttr annoGV = builder.getStringAttr(aa->getAnnotation()); mlir::ArrayAttr args = emitAnnotationArgs(aa); return cir::AnnotationAttr::get(&getMLIRContext(), annoGV, args); } void CIRGenModule::addGlobalAnnotations(const ValueDecl *d, mlir::Operation *gv) { assert(d->hasAttr<AnnotateAttr>() && "no annotate attribute"); assert((isa<GlobalOp>(gv) || isa<FuncOp>(gv)) && "annotation only on globals"); llvm::SmallVector<mlir::Attribute, 4> annotations; for (auto *i : d->specific_attrs<AnnotateAttr>()) annotations.push_back(emitAnnotateAttr(i)); if (auto global = dyn_cast<cir::GlobalOp>(gv)) global.setAnnotationsAttr(builder.getArrayAttr(annotations)); else if (auto func = dyn_cast<cir::FuncOp>(gv)) func.setAnnotationsAttr(builder.getArrayAttr(annotations)); } void CIRGenModule::emitGlobalAnnotations() { for (const auto &[mangledName, vd] : deferredAnnotations) { mlir::Operation *gv = getGlobalValue(mangledName); if (gv) addGlobalAnnotations(vd, gv); } deferredAnnotations.clear(); } cir::TBAAAttr CIRGenModule::getTBAATypeInfo(QualType QTy) { if (!tbaa) { return nullptr; } return tbaa->getTypeInfo(QTy); } TBAAAccessInfo CIRGenModule::getTBAAAccessInfo(QualType accessType) { if (!tbaa) { return TBAAAccessInfo(); } if (getLangOpts().CUDAIsDevice) { llvm_unreachable("NYI"); } return tbaa->getAccessInfo(accessType); } TBAAAccessInfo CIRGenModule::getTBAAVTablePtrAccessInfo(mlir::Type VTablePtrType) { if (!tbaa) return TBAAAccessInfo(); return tbaa->getVTablePtrAccessInfo(VTablePtrType); } mlir::ArrayAttr CIRGenModule::getTBAAStructInfo(QualType QTy) { if (!tbaa) return nullptr; return tbaa->getTBAAStructInfo(QTy); } cir::TBAAAttr CIRGenModule::getTBAABaseTypeInfo(QualType QTy) { if (!tbaa) { return nullptr; } return tbaa->getBaseTypeInfo(QTy); } cir::TBAAAttr CIRGenModule::getTBAAAccessTagInfo(TBAAAccessInfo tbaaInfo) { if (!tbaa) { return nullptr; } return tbaa->getAccessTagInfo(tbaaInfo); } TBAAAccessInfo CIRGenModule::mergeTBAAInfoForCast(TBAAAccessInfo SourceInfo, TBAAAccessInfo TargetInfo) { if (!tbaa) return TBAAAccessInfo(); return tbaa->mergeTBAAInfoForCast(SourceInfo, TargetInfo); } TBAAAccessInfo CIRGenModule::mergeTBAAInfoForConditionalOperator(TBAAAccessInfo InfoA, TBAAAccessInfo InfoB) { if (!tbaa) return TBAAAccessInfo(); return tbaa->mergeTBAAInfoForConditionalOperator(InfoA, InfoB); } TBAAAccessInfo CIRGenModule::mergeTBAAInfoForMemoryTransfer(TBAAAccessInfo DestInfo, TBAAAccessInfo SrcInfo) { if (!tbaa) return TBAAAccessInfo(); return tbaa->mergeTBAAInfoForConditionalOperator(DestInfo, SrcInfo); }