| //===- 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/DeclOpenACC.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()); |
| |
| PointerAlignInBytes = |
| astContext |
| .toCharUnitsFromBits( |
| astContext.getTargetInfo().getPointerAlign(LangAS::Default)) |
| .getQuantity(); |
| |
| // TODO(CIR): Should be updated once TypeSizeInfoAttr is upstreamed |
| const unsigned sizeTypeSize = |
| astContext.getTypeSize(astContext.getSignedSizeType()); |
| PtrDiffTy = |
| cir::IntType::get(&getMLIRContext(), sizeTypeSize, /*isSigned=*/true); |
| |
| theModule->setAttr(cir::CIRDialect::getTripleAttrName(), |
| builder.getStringAttr(getTriple().str())); |
| } |
| |
| CharUnits CIRGenModule::getNaturalTypeAlignment(QualType t, |
| LValueBaseInfo *baseInfo) { |
| assert(!cir::MissingFeatures::opTBAA()); |
| |
| // 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 (const auto *tt = t->getAs<TypedefType>()) { |
| if (unsigned align = tt->getDecl()->getMaxAlignment()) { |
| if (baseInfo) |
| *baseInfo = LValueBaseInfo(AlignmentSource::AttributedType); |
| return astContext.toCharUnitsFromBits(align); |
| } |
| } |
| |
| // 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; |
| if (t.getQualifiers().hasUnaligned()) { |
| alignment = CharUnits::One(); |
| } else { |
| assert(!cir::MissingFeatures::alignCXXRecordDecl()); |
| 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; |
| } |
| |
| 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) { |
| if (const auto *cd = dyn_cast<clang::OpenACCConstructDecl>(gd.getDecl())) { |
| emitGlobalOpenACCDecl(cd); |
| return; |
| } |
| |
| const auto *global = cast<ValueDecl>(gd.getDecl()); |
| |
| 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>()) |
| errorNYI(fd->getSourceRange(), "deferredAnnotations"); |
| if (!fd->doesThisDeclarationHaveABody()) { |
| if (!fd->doesDeclarationForceExternallyVisibleDefinition()) |
| return; |
| |
| errorNYI(fd->getSourceRange(), |
| "function declaration that forces code gen"); |
| return; |
| } |
| } else { |
| assert(cast<VarDecl>(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<FunctionDecl>(gd.getDecl()); |
| if (funcDecl->getIdentifier() == nullptr) { |
| errorNYI(funcDecl->getSourceRange().getBegin(), |
| "function definition with a non-identifier for a name"); |
| return; |
| } |
| cir::FuncType funcType = |
| cast<cir::FuncType>(convertType(funcDecl->getType())); |
| |
| cir::FuncOp funcOp = dyn_cast_if_present<cir::FuncOp>(op); |
| if (!funcOp || funcOp.getFunctionType() != funcType) { |
| funcOp = getAddrOfFunction(gd, funcType, /*ForVTable=*/false, |
| /*DontDefer=*/true, ForDefinition); |
| } |
| |
| CIRGenFunction cgf(*this, builder); |
| curCGF = &cgf; |
| { |
| mlir::OpBuilder::InsertionGuard guard(builder); |
| cgf.generateCode(gd, funcOp, funcType); |
| } |
| curCGF = nullptr; |
| } |
| |
| 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<cir::GlobalOp>(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); |
| mlir::Attribute initializer; |
| if (initExpr) { |
| if (APValue *value = initDecl->evaluateValue()) { |
| ConstantEmitter emitter(*this); |
| initializer = emitter.tryEmitPrivateForMemory(*value, astTy); |
| } else { |
| errorNYI(initExpr->getSourceRange(), "non-constant initializer"); |
| } |
| } else { |
| initializer = builder.getZeroInitAttr(convertType(astTy)); |
| } |
| |
| 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<ValueDecl>(gd.getDecl()); |
| if (const auto *fd = dyn_cast<FunctionDecl>(decl)) { |
| // TODO(CIR): Skip generation of CIR for functions with available_externally |
| // linkage at -O0. |
| |
| if (const auto *method = dyn_cast<CXXMethodDecl>(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<VarDecl>(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<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 *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<NoCommonAttr>()) && !vd->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 (vd->getInit() || vd->hasExternalStorage()) |
| return true; |
| |
| // A variable cannot be both common and exist in a section. |
| if (vd->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 (vd->hasAttr<PragmaClangBSSSectionAttr>() || |
| vd->hasAttr<PragmaClangDataSectionAttr>() || |
| vd->hasAttr<PragmaClangRelroSectionAttr>() || |
| vd->hasAttr<PragmaClangRodataSectionAttr>()) |
| 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<WeakImportAttr>()) |
| 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<AlignedAttr>()) |
| return true; |
| QualType varType = vd->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(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<WeakAttr>()) { |
| 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<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>(dd) && |
| !isVarDeclStrongDefinition(astContext, *this, cast<VarDecl>(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<SelectAnyAttr>()) |
| 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<FunctionDecl>(decl); |
| // Consteval functions shouldn't be emitted. |
| if (!fd->isConsteval()) |
| emitGlobal(fd); |
| break; |
| } |
| |
| case Decl::Var: { |
| auto *vd = cast<VarDecl>(decl); |
| emitGlobal(vd); |
| break; |
| } |
| case Decl::OpenACCRoutine: |
| emitGlobalOpenACCDecl(cast<OpenACCRoutineDecl>(decl)); |
| break; |
| case Decl::OpenACCDeclare: |
| emitGlobalOpenACCDecl(cast<OpenACCDeclareDecl>(decl)); |
| break; |
| } |
| } |
| |
| cir::FuncOp CIRGenModule::getAddrOfFunction(clang::GlobalDecl gd, |
| mlir::Type funcType, bool forVTable, |
| bool dontDefer, |
| ForDefinition_t isForDefinition) { |
| assert(!cast<FunctionDecl>(gd.getDecl())->isConsteval() && |
| "consteval function should never be emitted"); |
| |
| if (!funcType) { |
| const auto *fd = cast<FunctionDecl>(gd.getDecl()); |
| funcType = convertType(fd->getType()); |
| } |
| |
| cir::FuncOp func = getOrCreateCIRFunction( |
| cast<NamedDecl>(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<FunctionDecl>(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<cir::FuncType>(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); |
| |
| // 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. |
| CIRGenFunction *cgf = this->curCGF; |
| if (cgf) |
| builder.setInsertionPoint(cgf->curFn); |
| |
| func = builder.create<cir::FuncOp>(loc, name, funcType); |
| |
| if (!cgf) |
| 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; |
| } |