//===- 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 "CIRGenModule.h" #include "CIRGenConstantEmitter.h" #include "CIRGenFunction.h" #include "clang/AST/ASTContext.h" #include "clang/AST/DeclBase.h" #include "clang/AST/GlobalDecl.h" #include "clang/Basic/SourceManager.h" #include "clang/CIR/Dialect/IR/CIRDialect.h" #include "clang/CIR/MissingFeatures.h" #include "mlir/IR/BuiltinOps.h" #include "mlir/IR/Location.h" #include "mlir/IR/MLIRContext.h" #include "mlir/IR/Verifier.h" using namespace clang; using namespace clang::CIRGen; 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(mlir::UnknownLoc::get(&mlirContext))}, diags(diags), target(astContext.getTargetInfo()), genTypes(*this) { // Initialize cached types VoidTy = cir::VoidType::get(&getMLIRContext()); 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); 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); 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()); theModule->setAttr(cir::CIRDialect::getTripleAttrName(), builder.getStringAttr(getTriple().str())); } mlir::Location CIRGenModule::getLoc(SourceLocation cLoc) { assert(cLoc.isValid() && "expected valid source location"); const SourceManager &sm = astContext.getSourceManager(); PresumedLoc pLoc = sm.getPresumedLoc(cLoc); StringRef filename = pLoc.getFilename(); return mlir::FileLineColLoc::get(builder.getStringAttr(filename), pLoc.getLine(), pLoc.getColumn()); } mlir::Location CIRGenModule::getLoc(SourceRange cRange) { assert(cRange.isValid() && "expected a valid source range"); mlir::Location begin = getLoc(cRange.getBegin()); mlir::Location end = getLoc(cRange.getEnd()); mlir::Attribute metadata; return mlir::FusedLoc::get({begin, end}, metadata, builder.getContext()); } void CIRGenModule::emitGlobal(clang::GlobalDecl gd) { const auto *global = cast(gd.getDecl()); if (const auto *fd = dyn_cast(global)) { // Update deferred annotations with the latest declaration if the function // was already used or defined. if (fd->hasAttr()) errorNYI(fd->getSourceRange(), "deferredAnnotations"); if (!fd->doesThisDeclarationHaveABody()) { if (!fd->doesDeclarationForceExternallyVisibleDefinition()) return; errorNYI(fd->getSourceRange(), "function declaration that forces code gen"); return; } } else { assert(cast(global)->isFileVarDecl() && "Cannot emit local var decl as global"); } // TODO(CIR): Defer emitting some global definitions until later emitGlobalDefinition(gd); } void CIRGenModule::emitGlobalFunctionDefinition(clang::GlobalDecl gd, mlir::Operation *op) { auto const *funcDecl = cast(gd.getDecl()); if (funcDecl->getIdentifier() == nullptr) { errorNYI(funcDecl->getSourceRange().getBegin(), "function definition with a non-identifier for a name"); return; } cir::FuncType funcType = cast(convertType(funcDecl->getType())); cir::FuncOp funcOp = dyn_cast_if_present(op); if (!funcOp || funcOp.getFunctionType() != funcType) { funcOp = getAddrOfFunction(gd, funcType, /*ForVTable=*/false, /*DontDefer=*/true, ForDefinition); } CIRGenFunction cgf(*this, builder); { mlir::OpBuilder::InsertionGuard guard(builder); cgf.generateCode(gd, funcOp, funcType); } } void CIRGenModule::emitGlobalVarDefinition(const clang::VarDecl *vd, bool isTentative) { const QualType astTy = vd->getType(); const mlir::Type type = convertType(vd->getType()); if (clang::IdentifierInfo *identifier = vd->getIdentifier()) { auto varOp = builder.create(getLoc(vd->getSourceRange()), identifier->getName(), type); // TODO(CIR): This code for processing initial values is a placeholder // until class ConstantEmitter is upstreamed and the code for processing // constant expressions is filled out. Only the most basic handling of // certain constant expressions is implemented for now. const VarDecl *initDecl; const Expr *initExpr = vd->getAnyInitializer(initDecl); if (initExpr) { mlir::Attribute initializer; if (APValue *value = initDecl->evaluateValue()) { ConstantEmitter emitter(*this); initializer = emitter.tryEmitPrivateForMemory(*value, astTy); } else { errorNYI(initExpr->getSourceRange(), "non-constant initializer"); } varOp.setInitialValueAttr(initializer); } // Set CIR's linkage type as appropriate. cir::GlobalLinkageKind linkage = getCIRLinkageVarDefinition(vd, /*IsConstant=*/false); // Set CIR linkage and DLL storage class. varOp.setLinkage(linkage); if (linkage == cir::GlobalLinkageKind::CommonLinkage) errorNYI(initExpr->getSourceRange(), "common linkage"); theModule.push_back(varOp); } else { errorNYI(vd->getSourceRange().getBegin(), "variable definition with a non-identifier for a name"); } } void CIRGenModule::emitGlobalDefinition(clang::GlobalDecl gd, mlir::Operation *op) { const auto *decl = cast(gd.getDecl()); if (const auto *fd = dyn_cast(decl)) { // TODO(CIR): Skip generation of CIR for functions with available_externally // linkage at -O0. if (const auto *method = dyn_cast(decl)) { // Make sure to emit the definition(s) before we emit the thunks. This is // necessary for the generation of certain thunks. (void)method; errorNYI(method->getSourceRange(), "member function"); return; } if (fd->isMultiVersion()) errorNYI(fd->getSourceRange(), "multiversion functions"); emitGlobalFunctionDefinition(gd, op); return; } if (const auto *vd = dyn_cast(decl)) return emitGlobalVarDefinition(vd, !vd->hasDefinition()); llvm_unreachable("Invalid argument to CIRGenModule::emitGlobalDefinition"); } static bool shouldBeInCOMDAT(CIRGenModule &cgm, const Decl &d) { assert(!cir::MissingFeatures::supportComdat()); if (d.hasAttr()) return true; GVALinkage linkage; if (auto *vd = dyn_cast(&d)) linkage = cgm.getASTContext().GetGVALinkageForVariable(vd); else linkage = cgm.getASTContext().GetGVALinkageForFunction(cast(&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 *vd, bool noCommon) { // Don't give variables common linkage if -fno-common was specified unless it // was overridden by a NoCommon attribute. if ((noCommon || vd->hasAttr()) && !vd->hasAttr()) 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 (vd->getInit() || vd->hasExternalStorage()) return true; // A variable cannot be both common and exist in a section. if (vd->hasAttr()) 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 (vd->hasAttr() || vd->hasAttr() || vd->hasAttr() || vd->hasAttr()) return true; // Thread local vars aren't considered common linkage. if (vd->getTLSKind()) return true; // Tentative definitions marked with WeakImportAttr are true definitions. if (vd->hasAttr()) return true; // A variable cannot be both common and exist in a comdat. if (shouldBeInCOMDAT(cgm, *vd)) return true; // Declarations with a required alignment do not have common linkage in MSVC // mode. if (astContext.getTargetInfo().getCXXABI().isMicrosoft()) { if (vd->hasAttr()) return true; QualType varType = vd->getType(); if (astContext.isAlignmentRequired(varType)) return true; if (const auto *rt = varType->getAs()) { const RecordDecl *rd = rt->getDecl(); for (const FieldDecl *fd : rd->fields()) { if (fd->isBitField()) continue; if (fd->hasAttr()) 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(vd->getType()) > astContext.toBits(CharUnits::fromQuantity(32))) return true; return false; } cir::GlobalLinkageKind CIRGenModule::getCIRLinkageForDeclarator( const DeclaratorDecl *dd, GVALinkage linkage, bool isConstantVariable) { if (linkage == GVA_Internal) return cir::GlobalLinkageKind::InternalLinkage; if (dd->hasAttr()) { if (isConstantVariable) return cir::GlobalLinkageKind::WeakODRLinkage; return cir::GlobalLinkageKind::WeakAnyLinkage; } if (const auto *fd = dd->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 dd->hasAttr() ? 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(dd) && !isVarDeclStrongDefinition(astContext, *this, cast(dd), getCodeGenOpts().NoCommon)) { errorNYI(dd->getBeginLoc(), "common linkage", dd->getDeclKindName()); 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 (dd->hasAttr()) return cir::GlobalLinkageKind::WeakODRLinkage; // Otherwise, we have strong external linkage. assert(linkage == GVA_StrongExternal); return cir::GlobalLinkageKind::ExternalLinkage; } cir::GlobalLinkageKind CIRGenModule::getCIRLinkageVarDefinition(const VarDecl *vd, bool isConstant) { assert(!isConstant && "constant variables NYI"); GVALinkage linkage = astContext.GetGVALinkageForVariable(vd); return getCIRLinkageForDeclarator(vd, linkage, isConstant); } // Emit code for a single top level declaration. void CIRGenModule::emitTopLevelDecl(Decl *decl) { // Ignore dependent declarations. if (decl->isTemplated()) return; switch (decl->getKind()) { default: errorNYI(decl->getBeginLoc(), "declaration of kind", decl->getDeclKindName()); break; case Decl::Function: { auto *fd = cast(decl); // Consteval functions shouldn't be emitted. if (!fd->isConsteval()) emitGlobal(fd); break; } case Decl::Var: { auto *vd = cast(decl); emitGlobal(vd); break; } } } cir::FuncOp CIRGenModule::getAddrOfFunction(clang::GlobalDecl gd, mlir::Type funcType, bool forVTable, bool dontDefer, ForDefinition_t isForDefinition) { assert(!cast(gd.getDecl())->isConsteval() && "consteval function should never be emitted"); if (!funcType) { const auto *fd = cast(gd.getDecl()); funcType = convertType(fd->getType()); } cir::FuncOp func = getOrCreateCIRFunction( cast(gd.getDecl())->getIdentifier()->getName(), funcType, gd, forVTable, dontDefer, /*isThunk=*/false, isForDefinition); return func; } cir::FuncOp CIRGenModule::getOrCreateCIRFunction( StringRef mangledName, mlir::Type funcType, GlobalDecl gd, bool forVTable, bool dontDefer, bool isThunk, ForDefinition_t isForDefinition, mlir::ArrayAttr extraAttrs) { auto *funcDecl = llvm::cast_or_null(gd.getDecl()); bool invalidLoc = !funcDecl || funcDecl->getSourceRange().getBegin().isInvalid() || funcDecl->getSourceRange().getEnd().isInvalid(); cir::FuncOp funcOp = createCIRFunction( invalidLoc ? theModule->getLoc() : getLoc(funcDecl->getSourceRange()), mangledName, mlir::cast(funcType), funcDecl); return funcOp; } cir::FuncOp CIRGenModule::createCIRFunction(mlir::Location loc, StringRef name, cir::FuncType funcType, const clang::FunctionDecl *funcDecl) { cir::FuncOp func; { mlir::OpBuilder::InsertionGuard guard(builder); func = builder.create(loc, name, funcType); theModule.push_back(func); } return func; } mlir::Type CIRGenModule::convertType(QualType type) { return genTypes.convertType(type); } bool CIRGenModule::verifyModule() const { // 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(); } DiagnosticBuilder CIRGenModule::errorNYI(SourceLocation loc, llvm::StringRef feature) { unsigned diagID = diags.getCustomDiagID( DiagnosticsEngine::Error, "ClangIR code gen Not Yet Implemented: %0"); return diags.Report(loc, diagID) << feature; } DiagnosticBuilder CIRGenModule::errorNYI(SourceRange loc, llvm::StringRef feature) { return errorNYI(loc.getBegin(), feature) << loc; }