//===--- 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 "CIRGenOpenMPRuntime.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/StringMap.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<TargetCIRGenInfo> 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 &target;

  std::unique_ptr<CIRGenCXXABI> abi;

  CIRGenTypes genTypes;

  /// Holds information about C++ vtables.
  CIRGenVTables vtables;

  /// Holds the CUDA runtime
  std::unique_ptr<CIRGenCUDARuntime> cudaRuntime;

  /// Holds the OpenMP runtime
  std::unique_ptr<CIRGenOpenMPRuntime> openMPRuntime;

  /// Per-function codegen information. Updated everytime emitCIR is called
  /// for FunctionDecls's.
  CIRGenFunction *curCGF = nullptr;

  llvm::SmallVector<mlir::Attribute> globalScopeAsm;

  /// Accumulated record layout entries, materialized in release().
  llvm::SmallVector<mlir::NamedAttribute> recordLayoutEntries;

  llvm::DenseSet<clang::GlobalDecl> diagnosedConflictingDefinitions;

  /// -------
  /// Annotations
  /// -------

  /// We store each annotation as an attribute of GlobalOp and FuncOp rather
  /// than collecting them into a single module-level list.  The deferred map
  /// lets us attach annotations at the end of codegen so the most up-to-date
  /// ValueDecl (which carries all inherited annotations) is used.

  /// Used for uniquing of annotation arguments.
  llvm::DenseMap<unsigned, mlir::ArrayAttr> annotationArgs;

  /// Store deferred function annotations so they can be emitted at the end
  /// with the most up to date ValueDecl that will have all the inherited
  /// annotations.
  llvm::DenseMap<llvm::StringRef, const clang::ValueDecl *> deferredAnnotations;

  /// A queue of (optional) vtables to consider emitting.
  std::vector<const CXXRecordDecl *> deferredVTables;

  /// A queue of (optional) vtables that may be emitted opportunistically.
  std::vector<const CXXRecordDecl *> opportunisticVTables;

  void createCUDARuntime();
  void createOpenMPRuntime();

  /// 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, const clang::Decl *targetDecl,
      bool isThunk, bool attrOnCallSite,
      llvm::MutableArrayRef<mlir::NamedAttrList> 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<const Expr *, mlir::Operation *>
      materializedGlobalTemporaryMap;

public:
  mlir::ModuleOp getModule() const { return theModule; }
  CIRGenBuilderTy &getBuilder() { return builder; }

  /// Queue a record layout entry for materialization in release().
  void addRecordLayout(mlir::StringAttr name, cir::RecordLayoutAttr attr) {
    recordLayoutEntries.push_back(mlir::NamedAttribute(name, attr));
  }
  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<const clang::FieldDecl *, llvm::StringRef> 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<cir::BlockAddrInfoAttr, cir::LabelOp> blockAddressInfoToLabel;
  cir::LabelOp lookupBlockAddressInfo(cir::BlockAddrInfoAttr blockInfo);
  void mapBlockAddress(cir::BlockAddrInfoAttr blockInfo, cir::LabelOp label);

  /// Add a global value to the llvmUsed list.
  void addUsedGlobal(cir::CIRGlobalValueInterface gv);

  /// Add a global value to the llvmCompilerUsed list.
  void addCompilerUsedGlobal(cir::CIRGlobalValueInterface gv);

  /// Add a global to a list to be added to the llvm.compiler.used metadata.
  void addUsedOrCompilerUsedGlobal(cir::CIRGlobalValueInterface gv);

  /// Emit llvm.used and llvm.compiler.used globals.
  void emitLLVMUsed();

  /// Tell the consumer that this variable has been instantiated.
  void handleCXXStaticMemberVarInstantiation(VarDecl *vd);

  llvm::DenseMap<const Decl *, cir::GlobalOp> staticLocalDeclMap;
  llvm::DenseMap<const VarDecl *, cir::GlobalOp> initializerConstants;

  /// Cache for O(1) symbol lookups by name, replacing the O(N) linear scan
  /// in SymbolTable::lookupSymbolIn that getGlobalValue used previously.
  llvm::StringMap<mlir::Operation *> symbolLookupCache;

  mlir::Operation *getGlobalValue(llvm::StringRef ref);

  /// O(1) lookup of a FuncOp by name in the symbol cache.
  /// Returns nullptr if the name is not found or is not a FuncOp.
  cir::FuncOp lookupFuncOp(llvm::StringRef name) {
    auto *op = getGlobalValue(name);
    return op ? mlir::dyn_cast<cir::FuncOp>(op) : cir::FuncOp{};
  }

  void insertGlobalSymbol(mlir::Operation *op) {
    if (auto sym = mlir::dyn_cast<mlir::SymbolOpInterface>(op))
      symbolLookupCache[sym.getName()] = op;
  }
  void eraseGlobalSymbol(mlir::Operation *op) {
    if (auto sym = mlir::dyn_cast<mlir::SymbolOpInterface>(op)) {
      auto it = symbolLookupCache.find(sym.getName());
      if (it != symbolLookupCache.end() && it->second == op)
        symbolLookupCache.erase(it);
    }
  }

  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);

  cir::GlobalOp
  createGlobalOp(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<int> priority = std::nullopt);
  void addGlobalDtor(cir::FuncOp dtor,
                     std::optional<int> 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<unsigned> 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);

  /// Get the GlobalOp of a template parameter object.
  cir::GlobalOp
  getAddrOfTemplateParamObject(const TemplateParamObjectDecl *tpo);
  // Get the GlobalOp of a source_location object.
  cir::GlobalOp
  getAddrOfUnnamedGlobalConstantDecl(const UnnamedGlobalConstantDecl *gcd);

  CharUnits computeNonVirtualBaseClassOffset(
      const CXXRecordDecl *derivedClass,
      llvm::iterator_range<CastExpr::path_const_iterator> 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<mlir::NamedAttrList> 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!");
  }

  static cir::VisibilityKind getCIRVisibilityKind(Visibility v) {
    switch (v) {
    case DefaultVisibility:
      return cir::VisibilityKind::Default;
    case HiddenVisibility:
      return cir::VisibilityKind::Hidden;
    case ProtectedVisibility:
      return cir::VisibilityKind::Protected;
    }

    llvm_unreachable("unknown visibility!");
  }

  llvm::DenseMap<mlir::Attribute, cir::GlobalOp> constantStringMap;
  llvm::DenseMap<const UnnamedGlobalConstantDecl *, cir::GlobalOp>
      unnamedGlobalConstantDeclMap;
  llvm::DenseMap<const CompoundLiteralExpr *, cir::GlobalOp>
      emittedCompoundLiterals;

  cir::GlobalOp
  getAddrOfConstantCompoundLiteralIfEmitted(const CompoundLiteralExpr *e) {
    return emittedCompoundLiterals.lookup(e);
  }
  void setAddrOfConstantCompoundLiteral(const CompoundLiteralExpr *e,
                                        cir::GlobalOp gv) {
    [[maybe_unused]] bool ok = emittedCompoundLiterals.insert({e, gv}).second;
    assert(ok && "compound literal global already emitted");
  }

  /// 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<cir::FuncType, cir::FuncOp> getAddrAndTypeOfCXXStructor(
      clang::GlobalDecl gd, const CIRGenFunctionInfo *fnInfo = nullptr,
      cir::FuncType fnType = nullptr, bool dontDefer = false,
      ForDefinition_t isForDefinition = NotForDefinition);

  /// List of global values which are required to be present in the object file;
  /// This is used for forcing visibility of symbols which may otherwise be
  /// optimized out.
  std::vector<cir::CIRGlobalValueInterface> llvmUsed;
  std::vector<cir::CIRGlobalValueInterface> llvmCompilerUsed;

  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<llvm::StringRef, clang::GlobalDecl> 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<clang::GlobalDecl> 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);

  /// Check if `fd` ends up calling itself directly through asm label or
  /// builtin-pointer-to-self trickery (e.g., glibc's `extern inline` libc
  /// wrappers that call `__builtin_strrchr`, which the codegen lowers to a
  /// call on the same asm-named symbol).  Emitting an
  /// `available_externally` body for such a function feeds the LLVM
  /// Decide whether to emit the body of `gd` to CIR.  Returns false for
  /// available_externally functions that are trivially recursive (PR9614).
  /// Mirrors classic CodeGen's `CodeGenModule::shouldEmitFunction`.
  bool shouldEmitFunction(clang::GlobalDecl gd);

  /// 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);

  /// Emit a definition for an `__attribute__((alias))` declaration.
  void emitAliasDefinition(GlobalDecl gd);

  mlir::Type convertType(clang::QualType type);

  /// Set the visibility for the given global.
  void setGlobalVisibility(cir::CIRGlobalValueInterface gv,
                           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. If `isExtendingDecl` is true, then the operation is a
  /// temporary whose lifetime is extended by the variable declared by `d`.
  void setTLSMode(mlir::Operation *op, const VarDecl &d,
                  bool isExtendingDecl = false);

  /// 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);

  /// Helper function for the below two that will create the
  /// constructor/destructor in specified regions, rather than in the GlobalOp.
  void emitCXXSpecialVarDeclInit(const VarDecl *varDecl, cir::GlobalOp addr,
                                 bool performInit, mlir::Region &ctorRegion,
                                 mlir::Region &dtorRegion);
  /// Emit the function that initializes the specified static-local variable.
  void emitCXXStaticLocalVarDeclInit(const VarDecl *varDecl, cir::GlobalOp addr,
                                     bool performInit);
  /// Emit the function that initializes the specified global
  void emitCXXGlobalVarDeclInit(const VarDecl *varDecl, cir::GlobalOp addr,
                                bool performInit);

  void setGlobalTlsReferences(const VarDecl &vd, cir::GlobalOp globalOp);
  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 <typename BeforeOpTy, typename DataClauseTy>
  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<const OpenACCClause *> 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);

  /// Build a GEP-style field-index path from \p destClass to \p field.
  /// Returns std::nullopt and emits errorNYI for virtual-base paths.
  std::optional<llvm::SmallVector<int32_t>>
  buildMemberPath(const CXXRecordDecl *destClass, const FieldDecl *field);

  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;

  /// Print the postfix for externalized static variable or kernels for single
  /// source offloading languages CUDA and HIP. The unique postfix is created
  /// using either the CUID argument, or the file's UniqueID and active macros.
  /// The fallback method without a CUID requires that the offloading toolchain
  /// does not define separate macros via the -cc1 options.
  void printPostfixForExternalizedDecl(llvm::raw_ostream &os, const Decl *d);

  /// 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";
  static constexpr const char *builtinCoroFree = "__builtin_coro_free";

  /// 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;
  }

  CIRGenOpenMPRuntime &getOpenMPRuntime() {
    assert(openMPRuntime != nullptr);
    return *openMPRuntime;
  }

  bool isPaddedAtomicType(QualType type);
  bool isPaddedAtomicType(const AtomicType *type);

  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);
  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);

  void addReplacement(llvm::StringRef name, mlir::Operation *op);

  /// Helpers to emit "not yet implemented" error diagnostics
  DiagnosticBuilder errorNYI(SourceLocation, llvm::StringRef);

  template <typename T>
  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 <typename T>
  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();

  /// Add global annotations for a global value (GlobalOp or FuncOp).
  void addGlobalAnnotations(const clang::ValueDecl *d, mlir::Operation *gv);

private:
  /// Search \p currentClass and its non-virtual base subobjects for \p field,
  /// appending CIR field indices along the path from \p currentClass.
  bool findFieldMemberPath(const CXXRecordDecl *currentClass,
                           const FieldDecl *field,
                           llvm::SmallVectorImpl<int32_t> &path);

  // An ordered map of canonical GlobalDecls to their mangled names.
  llvm::MapVector<clang::GlobalDecl, llvm::StringRef> mangledDeclNames;
  llvm::StringMap<clang::GlobalDecl, llvm::BumpPtrAllocator> manglings;

  // FIXME: should we use llvm::TrackingVH<mlir::Operation> here?
  llvm::MapVector<StringRef, mlir::Operation *> replacements;
  /// Call replaceAllUsesWith on all pairs in replacements.
  void applyReplacements();

  bool getCPUAndFeaturesAttributes(GlobalDecl gd,
                                   llvm::StringMap<std::string> &attrs,
                                   bool setTargetFeatures = true);
  void setNonAliasAttributes(GlobalDecl gd, mlir::Operation *op);

  /// Map source language used to a CIR attribute.
  std::optional<cir::SourceLanguage> getCIRSourceLanguage() const;

  /// Emit all the global annotations.
  void emitGlobalAnnotations();

  /// Build (or fetch from the dedup cache) the args ArrayAttr for an
  /// annotation. Returns the empty ArrayAttr when the annotation has none.
  mlir::ArrayAttr getOrCreateAnnotationArgs(const clang::AnnotateAttr *attr);

  /// Create cir::AnnotationAttr for a single AnnotateAttr on a global.
  cir::AnnotationAttr emitAnnotateAttr(const clang::AnnotateAttr *aa);

  /// 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
