//===--- CIRGenModule.h - Per-Module state for CIR gen ----------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This is the internal per-translation-unit state used for CIR translation. // //===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_LIB_CIR_CODEGEN_CIRGENMODULE_H #define LLVM_CLANG_LIB_CIR_CODEGEN_CIRGENMODULE_H #include "CIRGenBuilder.h" #include "CIRGenCUDARuntime.h" #include "CIRGenCall.h" #include "CIRGenTypeCache.h" #include "CIRGenTypes.h" #include "CIRGenVTables.h" #include "CIRGenValue.h" #include "clang/AST/CharUnits.h" #include "clang/CIR/Dialect/IR/CIRDataLayout.h" #include "clang/CIR/Dialect/IR/CIRDialect.h" #include "TargetInfo.h" #include "mlir/Dialect/Ptr/IR/MemorySpaceInterfaces.h" #include "mlir/IR/Builders.h" #include "mlir/IR/BuiltinOps.h" #include "mlir/IR/MLIRContext.h" #include "clang/AST/Decl.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/TargetInfo.h" #include "clang/CIR/Dialect/IR/CIROpsEnums.h" #include "llvm/ADT/StringRef.h" #include "llvm/TargetParser/Triple.h" namespace clang { class ASTContext; class CodeGenOptions; class Decl; class GlobalDecl; class LangOptions; class TargetInfo; class VarDecl; namespace CIRGen { class CIRGenFunction; class CIRGenCXXABI; enum ForDefinition_t : bool { NotForDefinition = false, ForDefinition = true }; /// This class organizes the cross-function state that is used while generating /// CIR code. class CIRGenModule : public CIRGenTypeCache { CIRGenModule(CIRGenModule &) = delete; CIRGenModule &operator=(CIRGenModule &) = delete; public: CIRGenModule(mlir::MLIRContext &mlirContext, clang::ASTContext &astContext, const clang::CodeGenOptions &cgo, clang::DiagnosticsEngine &diags); ~CIRGenModule(); private: mutable std::unique_ptr theTargetCIRGenInfo; CIRGenBuilderTy builder; /// Hold Clang AST information. clang::ASTContext &astContext; const clang::LangOptions &langOpts; const clang::CodeGenOptions &codeGenOpts; /// A "module" matches a c/cpp source file: containing a list of functions. mlir::ModuleOp theModule; clang::DiagnosticsEngine &diags; const clang::TargetInfo ⌖ std::unique_ptr abi; CIRGenTypes genTypes; /// Holds information about C++ vtables. CIRGenVTables vtables; /// Holds the CUDA runtime std::unique_ptr cudaRuntime; /// Per-function codegen information. Updated everytime emitCIR is called /// for FunctionDecls's. CIRGenFunction *curCGF = nullptr; llvm::SmallVector globalScopeAsm; llvm::DenseSet diagnosedConflictingDefinitions; /// A queue of (optional) vtables to consider emitting. std::vector deferredVTables; /// A queue of (optional) vtables that may be emitted opportunistically. std::vector opportunisticVTables; void createCUDARuntime(); /// A helper for constructAttributeList that handles return attributes. void constructFunctionReturnAttributes(const CIRGenFunctionInfo &info, const Decl *targetDecl, bool isThunk, mlir::NamedAttrList &retAttrs); /// A helper for constructAttributeList that handles argument attributes. void constructFunctionArgumentAttributes( const CIRGenFunctionInfo &info, bool isThunk, llvm::MutableArrayRef argAttrs); /// A helper function for constructAttributeList that determines whether a /// return value might have been discarded. bool mayDropFunctionReturn(const ASTContext &context, QualType retTy); /// A helper function for constructAttributeList that determines whether /// `noundef` on a return is possible. bool hasStrictReturn(QualType retTy, const Decl *targetDecl); llvm::DenseMap materializedGlobalTemporaryMap; public: mlir::ModuleOp getModule() const { return theModule; } CIRGenBuilderTy &getBuilder() { return builder; } clang::ASTContext &getASTContext() const { return astContext; } const clang::TargetInfo &getTarget() const { return target; } const clang::CodeGenOptions &getCodeGenOpts() const { return codeGenOpts; } clang::DiagnosticsEngine &getDiags() const { return diags; } CIRGenTypes &getTypes() { return genTypes; } const clang::LangOptions &getLangOpts() const { return langOpts; } CIRGenCXXABI &getCXXABI() const { return *abi; } mlir::MLIRContext &getMLIRContext() { return *builder.getContext(); } const cir::CIRDataLayout getDataLayout() const { // FIXME(cir): instead of creating a CIRDataLayout every time, set it as an // attribute for the CIRModule class. return cir::CIRDataLayout(theModule); } /// ------- /// Handling globals /// ------- mlir::Operation *lastGlobalOp = nullptr; /// Keep a map between lambda fields and names, this needs to be per module /// since lambdas might get generated later as part of defered work, and since /// the pointers are supposed to be uniqued, should be fine. Revisit this if /// it ends up taking too much memory. llvm::DenseMap lambdaFieldToName; /// Map BlockAddrInfoAttr (function name, label name) to the corresponding CIR /// LabelOp. This provides the main lookup table used to resolve block /// addresses into their label operations. llvm::DenseMap blockAddressInfoToLabel; /// Map CIR BlockAddressOps directly to their resolved LabelOps. /// Used once a block address has been successfully lowered to a label. llvm::MapVector blockAddressToLabel; /// Track CIR BlockAddressOps that cannot be resolved immediately /// because their LabelOp has not yet been emitted. These entries /// are solved later once the corresponding label is available. llvm::DenseSet unresolvedBlockAddressToLabel; cir::LabelOp lookupBlockAddressInfo(cir::BlockAddrInfoAttr blockInfo); void mapBlockAddress(cir::BlockAddrInfoAttr blockInfo, cir::LabelOp label); void mapUnresolvedBlockAddress(cir::BlockAddressOp op); void mapResolvedBlockAddress(cir::BlockAddressOp op, cir::LabelOp); void updateResolvedBlockAddress(cir::BlockAddressOp op, cir::LabelOp newLabel); /// Tell the consumer that this variable has been instantiated. void handleCXXStaticMemberVarInstantiation(VarDecl *vd); llvm::DenseMap staticLocalDeclMap; llvm::DenseMap initializerConstants; mlir::Operation *getGlobalValue(llvm::StringRef ref); cir::GlobalOp getStaticLocalDeclAddress(const VarDecl *d) { return staticLocalDeclMap[d]; } void setStaticLocalDeclAddress(const VarDecl *d, cir::GlobalOp c) { staticLocalDeclMap[d] = c; } cir::GlobalOp getOrCreateStaticVarDecl(const VarDecl &d, cir::GlobalLinkageKind linkage); Address createUnnamedGlobalFrom(const VarDecl &d, mlir::Attribute constAttr, CharUnits align); /// If the specified mangled name is not in the module, create and return an /// mlir::GlobalOp value cir::GlobalOp getOrCreateCIRGlobal(llvm::StringRef mangledName, mlir::Type ty, LangAS langAS, const VarDecl *d, ForDefinition_t isForDefinition); cir::GlobalOp getOrCreateCIRGlobal(const VarDecl *d, mlir::Type ty, ForDefinition_t isForDefinition); static cir::GlobalOp createGlobalOp(CIRGenModule &cgm, mlir::Location loc, llvm::StringRef name, mlir::Type t, bool isConstant = false, mlir::ptr::MemorySpaceAttrInterface addrSpace = {}, mlir::Operation *insertPoint = nullptr); /// Add a global constructor or destructor to the module. /// The priority is optional, if not specified, the default priority is used. void addGlobalCtor(cir::FuncOp ctor, std::optional priority = std::nullopt); void addGlobalDtor(cir::FuncOp dtor, std::optional priority = std::nullopt); bool shouldZeroInitPadding() const { // In C23 (N3096) $6.7.10: // """ // If any object is initialized with an empty initializer, then it is // subject to default initialization: // - if it is an aggregate, every member is initialized (recursively) // according to these rules, and any padding is initialized to zero bits; // - if it is a union, the first named member is initialized (recursively) // according to these rules, and any padding is initialized to zero bits. // // If the aggregate or union contains elements or members that are // aggregates or unions, these rules apply recursively to the subaggregates // or contained unions. // // If there are fewer initializers in a brace-enclosed list than there are // elements or members of an aggregate, or fewer characters in a string // literal used to initialize an array of known size than there are elements // in the array, the remainder of the aggregate is subject to default // initialization. // """ // // The standard seems ambiguous in the following two areas: // 1. For a union type with empty initializer, if the first named member is // not the largest member, then the bytes comes after the first named member // but before padding are left unspecified. An example is: // union U { int a; long long b;}; // union U u = {}; // The first 4 bytes are 0, but 4-8 bytes are left // unspecified. // // 2. It only mentions padding for empty initializer, but doesn't mention // padding for a non empty initialization list. And if the aggregation or // union contains elements or members that are aggregates or unions, and // some are non empty initializers, while others are empty initializers, // the padding initialization is unclear. An example is: // struct S1 { int a; long long b; }; // struct S2 { char c; struct S1 s1; }; // // The values for paddings between s2.c and s2.s1.a, between s2.s1.a // and s2.s1.b are unclear. // struct S2 s2 = { 'c' }; // // Here we choose to zero initiailize left bytes of a union type because // projects like the Linux kernel are relying on this behavior. If we don't // explicitly zero initialize them, the undef values can be optimized to // return garbage data. We also choose to zero initialize paddings for // aggregates and unions, no matter they are initialized by empty // initializers or non empty initializers. This can provide a consistent // behavior. So projects like the Linux kernel can rely on it. return !getLangOpts().CPlusPlus; } llvm::StringMap cgGlobalNames; std::string getUniqueGlobalName(const std::string &baseName); /// 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 getAddrOfGlobalVar(const VarDecl *d, mlir::Type ty = {}, ForDefinition_t isForDefinition = NotForDefinition); /// Get or create a thunk function with the given name and type. cir::FuncOp getAddrOfThunk(StringRef name, mlir::Type fnTy, GlobalDecl gd); /// Return the mlir::GlobalViewAttr for the address of the given global. cir::GlobalViewAttr getAddrOfGlobalVarAttr(const VarDecl *d); CharUnits computeNonVirtualBaseClassOffset( const CXXRecordDecl *derivedClass, llvm::iterator_range path); /// Get the CIR attributes and calling convention to use for a particular /// function type. /// /// \param name - The function name. /// \param info - The function type information. /// \param calleeInfo - The callee information these attributes are being /// constructed for. If valid, the attributes applied to this decl may /// contribute to the function attributes and calling convention. /// \param attrs [out] - On return, the attribute list to use. /// \param callingConv [out] - On return, the calling convention to use. /// \param sideEffect [out] - On return, the side effect type of the /// attributes. /// \param attrOnCallSite - Whether or not the attributes are on a call site. /// \param isThunk - Whether the function is a thunk. void constructAttributeList( llvm::StringRef name, const CIRGenFunctionInfo &info, CIRGenCalleeInfo calleeInfo, mlir::NamedAttrList &attrs, llvm::MutableArrayRef argAttrs, mlir::NamedAttrList &retAttrs, cir::CallingConv &callingConv, cir::SideEffect &sideEffect, bool attrOnCallSite, bool isThunk); /// Helper function for constructAttributeList/others. Builds a set of /// function attributes to add to a function based on language opts, codegen /// opts, and some small properties. void addDefaultFunctionAttributes(StringRef name, bool hasOptNoneAttr, bool attrOnCallSite, mlir::NamedAttrList &attrs); /// Will return a global variable of the given type. If a variable with a /// different type already exists then a new variable with the right type /// will be created and all uses of the old variable will be replaced with a /// bitcast to the new variable. cir::GlobalOp createOrReplaceCXXRuntimeVariable( mlir::Location loc, llvm::StringRef name, mlir::Type ty, cir::GlobalLinkageKind linkage, clang::CharUnits alignment); void emitVTable(const CXXRecordDecl *rd); /// Return the appropriate linkage for the vtable, VTT, and type information /// of the given class. cir::GlobalLinkageKind getVTableLinkage(const CXXRecordDecl *rd); /// Get the address of the RTTI descriptor for the given type. mlir::Attribute getAddrOfRTTIDescriptor(mlir::Location loc, QualType ty, bool forEH = false); static mlir::SymbolTable::Visibility getMLIRVisibility(Visibility v) { switch (v) { case DefaultVisibility: return mlir::SymbolTable::Visibility::Public; case HiddenVisibility: return mlir::SymbolTable::Visibility::Private; case ProtectedVisibility: // The distinction between ProtectedVisibility and DefaultVisibility is // that symbols with ProtectedVisibility, while visible to the dynamic // linker like DefaultVisibility, are guaranteed to always dynamically // resolve to a symbol in the current shared object. There is currently no // equivalent MLIR visibility, so we fall back on the fact that the symbol // is visible. return mlir::SymbolTable::Visibility::Public; } llvm_unreachable("unknown visibility!"); } llvm::DenseMap constantStringMap; /// Return a constant array for the given string. mlir::Attribute getConstantArrayFromStringLiteral(const StringLiteral *e); /// Return a global symbol reference to a constant array for the given string /// literal. cir::GlobalOp getGlobalForStringLiteral(const StringLiteral *s, llvm::StringRef name = ".str"); /// Return a global symbol reference to a constant array for the given string /// literal. cir::GlobalViewAttr getAddrOfConstantStringFromLiteral(const StringLiteral *s, llvm::StringRef name = ".str"); /// Returns the address space for temporary allocations in the language. This /// ensures that the allocated variable's address space matches the /// expectations of the AST, rather than using the target's allocation address /// space, which may lead to type mismatches in other parts of the IR. LangAS getLangTempAllocaAddressSpace() const; /// Set attributes which are common to any form of a global definition (alias, /// Objective-C method, function, global variable). /// /// NOTE: This should only be called for definitions. void setCommonAttributes(GlobalDecl gd, mlir::Operation *op); const TargetCIRGenInfo &getTargetCIRGenInfo(); /// Helpers to convert the presumed location of Clang's SourceLocation to an /// MLIR Location. mlir::Location getLoc(clang::SourceLocation cLoc); mlir::Location getLoc(clang::SourceRange cRange); /// Return the best known alignment for an unknown pointer to a /// particular class. clang::CharUnits getClassPointerAlignment(const clang::CXXRecordDecl *rd); /// FIXME: this could likely be a common helper and not necessarily related /// with codegen. clang::CharUnits getNaturalTypeAlignment(clang::QualType t, LValueBaseInfo *baseInfo = nullptr, bool forPointeeType = false); clang::CharUnits getNaturalPointeeTypeAlignment(clang::QualType t, LValueBaseInfo *baseInfo = nullptr); /// Returns the minimum object size for an object of the given class type /// (or a class derived from it). CharUnits getMinimumClassObjectSize(const CXXRecordDecl *cd); /// Returns the minimum object size for an object of the given type. CharUnits getMinimumObjectSize(QualType ty) { if (CXXRecordDecl *rd = ty->getAsCXXRecordDecl()) return getMinimumClassObjectSize(rd); return getASTContext().getTypeSizeInChars(ty); } /// TODO: Add TBAAAccessInfo CharUnits getDynamicOffsetAlignment(CharUnits actualBaseAlign, const CXXRecordDecl *baseDecl, CharUnits expectedTargetAlign); /// Returns the assumed alignment of a virtual base of a class. CharUnits getVBaseAlignment(CharUnits derivedAlign, const CXXRecordDecl *derived, const CXXRecordDecl *vbase); cir::FuncOp getAddrOfCXXStructor(clang::GlobalDecl gd, const CIRGenFunctionInfo *fnInfo = nullptr, cir::FuncType fnType = nullptr, bool dontDefer = false, ForDefinition_t isForDefinition = NotForDefinition) { return getAddrAndTypeOfCXXStructor(gd, fnInfo, fnType, dontDefer, isForDefinition) .second; } std::pair getAddrAndTypeOfCXXStructor( clang::GlobalDecl gd, const CIRGenFunctionInfo *fnInfo = nullptr, cir::FuncType fnType = nullptr, bool dontDefer = false, ForDefinition_t isForDefinition = NotForDefinition); mlir::Type getVTableComponentType(); CIRGenVTables &getVTables() { return vtables; } ItaniumVTableContext &getItaniumVTableContext() { return vtables.getItaniumVTableContext(); } const ItaniumVTableContext &getItaniumVTableContext() const { return vtables.getItaniumVTableContext(); } /// This contains all the decls which have definitions but which are deferred /// for emission and therefore should only be output if they are actually /// used. If a decl is in this, then it is known to have not been referenced /// yet. std::map deferredDecls; // This is a list of deferred decls which we have seen that *are* actually // referenced. These get code generated when the module is done. std::vector deferredDeclsToEmit; void addDeferredDeclToEmit(clang::GlobalDecl GD) { deferredDeclsToEmit.emplace_back(GD); } void emitTopLevelDecl(clang::Decl *decl); /// Determine whether the definition must be emitted; if this returns \c /// false, the definition can be emitted lazily if it's used. bool mustBeEmitted(const clang::ValueDecl *d); /// Determine whether the definition can be emitted eagerly, or should be /// delayed until the end of the translation unit. This is relevant for /// definitions whose linkage can change, e.g. implicit function /// instantiations which may later be explicitly instantiated. bool mayBeEmittedEagerly(const clang::ValueDecl *d); bool verifyModule() const; /// Return the address of the given function. If funcType is non-null, then /// this function will use the specified type if it has to create it. // TODO: this is a bit weird as `GetAddr` given we give back a FuncOp? cir::FuncOp getAddrOfFunction(clang::GlobalDecl gd, mlir::Type funcType = nullptr, bool forVTable = false, bool dontDefer = false, ForDefinition_t isForDefinition = NotForDefinition); mlir::Operation * getAddrOfGlobal(clang::GlobalDecl gd, ForDefinition_t isForDefinition = NotForDefinition); // Return whether RTTI information should be emitted for this target. bool shouldEmitRTTI(bool forEH = false) { return (forEH || getLangOpts().RTTI) && !getLangOpts().CUDAIsDevice && !(getLangOpts().OpenMP && getLangOpts().OpenMPIsTargetDevice && getTriple().isNVPTX()); } /// Emit type info if type of an expression is a variably modified /// type. Also emit proper debug info for cast types. void emitExplicitCastExprType(const ExplicitCastExpr *e, CIRGenFunction *cgf = nullptr); void addDeferredVTable(const CXXRecordDecl *rd) { deferredVTables.push_back(rd); } /// Emit code for a single global function or variable declaration. Forward /// declarations are emitted lazily. void emitGlobal(clang::GlobalDecl gd); void emitAliasForGlobal(llvm::StringRef mangledName, mlir::Operation *op, GlobalDecl aliasGD, cir::FuncOp aliasee, cir::GlobalLinkageKind linkage); mlir::Type convertType(clang::QualType type); /// Set the visibility for the given global. void setGlobalVisibility(mlir::Operation *op, const NamedDecl *d) const; void setDSOLocal(mlir::Operation *op) const; void setDSOLocal(cir::CIRGlobalValueInterface gv) const; /// Set visibility, dllimport/dllexport and dso_local. /// This must be called after dllimport/dllexport is set. void setGVProperties(mlir::Operation *op, const NamedDecl *d) const; void setGVPropertiesAux(mlir::Operation *op, const NamedDecl *d) const; /// Set TLS mode for the given operation based on the given variable /// declaration. void setTLSMode(mlir::Operation *op, const VarDecl &d); /// Get TLS mode from CodeGenOptions. cir::TLS_Model getDefaultCIRTLSModel() const; /// Set function attributes for a function declaration. void setFunctionAttributes(GlobalDecl gd, cir::FuncOp f, bool isIncompleteFunction, bool isThunk); /// Set the CIR function attributes (Sext, zext, etc). void setCIRFunctionAttributes(GlobalDecl gd, const CIRGenFunctionInfo &info, cir::FuncOp func, bool isThunk); /// Set extra attributes (inline, etc.) for a function. void setCIRFunctionAttributesForDefinition(const clang::FunctionDecl *fd, cir::FuncOp f); void emitGlobalDefinition(clang::GlobalDecl gd, mlir::Operation *op = nullptr); void emitGlobalFunctionDefinition(clang::GlobalDecl gd, mlir::Operation *op); void emitGlobalVarDefinition(const clang::VarDecl *vd, bool isTentative = false); /// Emit the function that initializes the specified global void emitCXXGlobalVarDeclInit(const VarDecl *varDecl, cir::GlobalOp addr, bool performInit); void emitCXXGlobalVarDeclInitFunc(const VarDecl *vd, cir::GlobalOp addr, bool performInit); void emitGlobalOpenACCDecl(const clang::OpenACCConstructDecl *cd); void emitGlobalOpenACCRoutineDecl(const clang::OpenACCRoutineDecl *cd); void emitGlobalOpenACCDeclareDecl(const clang::OpenACCDeclareDecl *cd); template void emitGlobalOpenACCDeclareDataOperands(const Expr *varOperand, DataClauseTy dataClause, OpenACCModifierKind modifiers, bool structured, bool implicit, bool requiresDtor); // Each of the acc.routine operations must have a unique name, so we just use // an integer counter. This is how Flang does it, so it seems reasonable. unsigned routineCounter = 0; void emitOpenACCRoutineDecl(const clang::FunctionDecl *funcDecl, cir::FuncOp func, SourceLocation pragmaLoc, ArrayRef clauses); void emitOMPThreadPrivateDecl(const OMPThreadPrivateDecl *d); void emitOMPGroupPrivateDecl(const OMPGroupPrivateDecl *d); void emitOMPCapturedExpr(const OMPCapturedExprDecl *d); void emitOMPAllocateDecl(const OMPAllocateDecl *d); void emitOMPDeclareReduction(const OMPDeclareReductionDecl *d); void emitOMPDeclareMapper(const OMPDeclareMapperDecl *d); void emitOMPRequiresDecl(const OMPRequiresDecl *d); // C++ related functions. void emitDeclContext(const DeclContext *dc); /// Return the result of value-initializing the given type, i.e. a null /// expression of the given type. mlir::Value emitNullConstant(QualType t, mlir::Location loc); mlir::TypedAttr emitNullConstantAttr(QualType t); /// Return a null constant appropriate for zero-initializing a base class with /// the given type. This is usually, but not always, an LLVM null constant. mlir::TypedAttr emitNullConstantForBase(const CXXRecordDecl *record); mlir::Value emitMemberPointerConstant(const UnaryOperator *e); /// Returns a null attribute to represent either a null method or null data /// member, depending on the type of mpt. mlir::TypedAttr emitNullMemberAttr(QualType t, const MemberPointerType *mpt); llvm::StringRef getMangledName(clang::GlobalDecl gd); // This function is to support the OpenACC 'bind' clause, which names an // alternate name for the function to be called by. This function mangles // `attachedFunction` as-if its name was actually `bindName` (that is, with // the same signature). It has some additional complications, as the 'bind' // target is always going to be a global function, so member functions need an // explicit instead of implicit 'this' parameter, and thus gets mangled // differently. std::string getOpenACCBindMangledName(const IdentifierInfo *bindName, const FunctionDecl *attachedFunction); void emitTentativeDefinition(const VarDecl *d); // Make sure that this type is translated. void updateCompletedType(const clang::TagDecl *td); // Produce code for this constructor/destructor. This method doesn't try to // apply any ABI rules about which other constructors/destructors are needed // or if they are alias to each other. cir::FuncOp codegenCXXStructor(clang::GlobalDecl gd); bool lookupRepresentativeDecl(llvm::StringRef mangledName, clang::GlobalDecl &gd) const; bool supportsCOMDAT() const; void maybeSetTrivialComdat(const clang::Decl &d, mlir::Operation *op); static void setInitializer(cir::GlobalOp &op, mlir::Attribute value); // Whether a global variable should be emitted by CUDA/HIP host/device // related attributes. bool shouldEmitCUDAGlobalVar(const VarDecl *global) const; /// Replace all uses of the old global with the new global, updating types /// and references as needed. Erases the old global when done. void replaceGlobal(cir::GlobalOp oldGV, cir::GlobalOp newGV); void replaceUsesOfNonProtoTypeWithRealFunction(mlir::Operation *old, cir::FuncOp newFn); cir::FuncOp getOrCreateCIRFunction(llvm::StringRef mangledName, mlir::Type funcType, clang::GlobalDecl gd, bool forVTable, bool dontDefer = false, bool isThunk = false, ForDefinition_t isForDefinition = NotForDefinition, mlir::NamedAttrList extraAttrs = {}); cir::FuncOp getOrCreateCIRFunction(llvm::StringRef mangledName, mlir::Type funcType, clang::GlobalDecl gd, bool forVTable, mlir::NamedAttrList extraAttrs) { return getOrCreateCIRFunction(mangledName, funcType, gd, forVTable, /*dontDefer=*/false, /*isThunk=*/false, NotForDefinition, extraAttrs); } cir::FuncOp createCIRFunction(mlir::Location loc, llvm::StringRef name, cir::FuncType funcType, const clang::FunctionDecl *funcDecl); /// Create a CIR function with builtin attribute set. cir::FuncOp createCIRBuiltinFunction(mlir::Location loc, llvm::StringRef name, cir::FuncType ty, const clang::FunctionDecl *fd); /// Mark the function as a special member (e.g. constructor, destructor) void setCXXSpecialMemberAttr(cir::FuncOp funcOp, const clang::FunctionDecl *funcDecl); cir::FuncOp createRuntimeFunction(cir::FuncType ty, llvm::StringRef name, mlir::NamedAttrList extraAttrs = {}, bool isLocal = false, bool assumeConvergent = false); static constexpr const char *builtinCoroId = "__builtin_coro_id"; static constexpr const char *builtinCoroAlloc = "__builtin_coro_alloc"; static constexpr const char *builtinCoroBegin = "__builtin_coro_begin"; static constexpr const char *builtinCoroEnd = "__builtin_coro_end"; /// Given a builtin id for a function like "__builtin_fabsf", return a /// Function* for "fabsf". cir::FuncOp getBuiltinLibFunction(const FunctionDecl *fd, unsigned builtinID); CIRGenCUDARuntime &getCUDARuntime() { assert(cudaRuntime != nullptr); return *cudaRuntime; } mlir::IntegerAttr getSize(CharUnits size) { return builder.getSizeFromCharUnits(size); } /// Emit any needed decls for which code generation was deferred. void emitDeferred(); bool shouldOpportunisticallyEmitVTables(); /// Emit any vtables which we deferred and still have a use for. void emitDeferredVTables(); /// Try to emit external vtables as available_externally if they have emitted /// all inlined virtual functions. It runs after EmitDeferred() and therefore /// is not allowed to create new references to things that need to be emitted /// lazily. void emitVTablesOpportunistically(); /// Helper for `emitDeferred` to apply actual codegen. void emitGlobalDecl(const clang::GlobalDecl &d); const llvm::Triple &getTriple() const { return target.getTriple(); } // Finalize CIR code generation. void release(); /// Returns a pointer to a global variable representing a temporary with /// static or thread storage duration. mlir::Operation *getAddrOfGlobalTemporary(const MaterializeTemporaryExpr *mte, const Expr *init); /// ------- /// Visibility and Linkage /// ------- static mlir::SymbolTable::Visibility getMLIRVisibilityFromCIRLinkage(cir::GlobalLinkageKind GLK); static cir::VisibilityKind getGlobalVisibilityKindFromClangVisibility( clang::VisibilityAttr::VisibilityType visibility); cir::VisibilityAttr getGlobalVisibilityAttrFromDecl(const Decl *decl); cir::GlobalLinkageKind getFunctionLinkage(GlobalDecl gd); static mlir::SymbolTable::Visibility getMLIRVisibility(cir::GlobalOp op); cir::GlobalLinkageKind getCIRLinkageForDeclarator(const DeclaratorDecl *dd, GVALinkage linkage, bool isConstantVariable); void setFunctionLinkage(GlobalDecl gd, cir::FuncOp f) { cir::GlobalLinkageKind l = getFunctionLinkage(gd); f.setLinkageAttr(cir::GlobalLinkageKindAttr::get(&getMLIRContext(), l)); mlir::SymbolTable::setSymbolVisibility(f, getMLIRVisibilityFromCIRLinkage(l)); } cir::GlobalLinkageKind getCIRLinkageVarDefinition(const VarDecl *vd, bool isConstant); void addReplacement(llvm::StringRef name, mlir::Operation *op); /// Helpers to emit "not yet implemented" error diagnostics DiagnosticBuilder errorNYI(SourceLocation, llvm::StringRef); template DiagnosticBuilder errorNYI(SourceLocation loc, llvm::StringRef feature, const T &name) { unsigned diagID = diags.getCustomDiagID(DiagnosticsEngine::Error, "ClangIR code gen Not Yet Implemented: %0: %1"); return diags.Report(loc, diagID) << feature << name; } DiagnosticBuilder errorNYI(mlir::Location loc, llvm::StringRef feature) { // TODO: Convert the location to a SourceLocation unsigned diagID = diags.getCustomDiagID( DiagnosticsEngine::Error, "ClangIR code gen Not Yet Implemented: %0"); return diags.Report(diagID) << feature; } DiagnosticBuilder errorNYI(llvm::StringRef feature) const { // TODO: Make a default location? currSrcLoc? unsigned diagID = diags.getCustomDiagID( DiagnosticsEngine::Error, "ClangIR code gen Not Yet Implemented: %0"); return diags.Report(diagID) << feature; } DiagnosticBuilder errorNYI(SourceRange, llvm::StringRef); template DiagnosticBuilder errorNYI(SourceRange loc, llvm::StringRef feature, const T &name) { return errorNYI(loc.getBegin(), feature, name) << loc; } /// Emit a general error that something can't be done. void error(SourceLocation loc, llvm::StringRef error); /// Print out an error that codegen doesn't support the specified stmt yet. void errorUnsupported(const Stmt *s, llvm::StringRef type); /// Print out an error that codegen doesn't support the specified decl yet. void errorUnsupported(const Decl *d, llvm::StringRef type); /// Emits AMDGPU specific Metadata. void emitAMDGPUMetadata(); private: // An ordered map of canonical GlobalDecls to their mangled names. llvm::MapVector mangledDeclNames; llvm::StringMap manglings; // FIXME: should we use llvm::TrackingVH here? llvm::MapVector replacements; /// Call replaceAllUsesWith on all pairs in replacements. void applyReplacements(); /// A helper function to replace all uses of OldF to NewF that replace /// the type of pointer arguments. This is not needed to tradtional /// pipeline since LLVM has opaque pointers but CIR not. void replacePointerTypeArgs(cir::FuncOp oldF, cir::FuncOp newF); void setNonAliasAttributes(GlobalDecl gd, mlir::Operation *op); /// Map source language used to a CIR attribute. std::optional getCIRSourceLanguage() const; /// Return the AST address space of the underlying global variable for D, as /// determined by its declaration. Normally this is the same as the address /// space of D's type, but in CUDA, address spaces are associated with /// declarations, not types. If D is nullptr, return the default address /// space for global variable. /// /// For languages without explicit address spaces, if D has default address /// space, target-specific global or constant address space may be returned. LangAS getGlobalVarAddressSpace(const VarDecl *decl); }; } // namespace CIRGen } // namespace clang #endif // LLVM_CLANG_LIB_CIR_CODEGEN_CIRGENMODULE_H