| //===--- CIRGenAction.cpp - LLVM Code generation Frontend Action ---------===// |
| // |
| // 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 |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "clang/CIR/FrontendAction/CIRGenAction.h" |
| #include "mlir/IR/MLIRContext.h" |
| #include "mlir/IR/OwningOpRef.h" |
| #include "clang/Basic/DiagnosticFrontend.h" |
| #include "clang/CIR/CIRGenerator.h" |
| #include "clang/CIR/CIRToCIRPasses.h" |
| #include "clang/CIR/LowerToLLVM.h" |
| #include "clang/CodeGen/BackendUtil.h" |
| #include "clang/CodeGen/ModuleLinker.h" |
| #include "clang/Frontend/CompilerInstance.h" |
| #include "llvm/ADT/ScopeExit.h" |
| #include "llvm/ADT/SmallString.h" |
| #include "llvm/ADT/StringSet.h" |
| #include "llvm/IR/DiagnosticHandler.h" |
| #include "llvm/IR/Function.h" |
| #include "llvm/IR/GlobalValue.h" |
| #include "llvm/IR/LLVMContext.h" |
| #include "llvm/IR/Module.h" |
| #include "llvm/Linker/Linker.h" |
| #include "llvm/Support/Path.h" |
| #include "llvm/Support/raw_ostream.h" |
| #include "llvm/Transforms/IPO/Internalize.h" |
| |
| using namespace cir; |
| using namespace clang; |
| |
| namespace cir { |
| |
| static BackendAction |
| getBackendActionFromOutputType(CIRGenAction::OutputType Action) { |
| switch (Action) { |
| case CIRGenAction::OutputType::EmitCIR: |
| assert(false && |
| "Unsupported output type for getBackendActionFromOutputType!"); |
| break; // Unreachable, but fall through to report that |
| case CIRGenAction::OutputType::EmitAssembly: |
| return BackendAction::Backend_EmitAssembly; |
| case CIRGenAction::OutputType::EmitBC: |
| return BackendAction::Backend_EmitBC; |
| case CIRGenAction::OutputType::EmitLLVM: |
| return BackendAction::Backend_EmitLL; |
| case CIRGenAction::OutputType::EmitObj: |
| return BackendAction::Backend_EmitObj; |
| } |
| // We should only get here if a non-enum value is passed in or we went through |
| // the assert(false) case above |
| llvm_unreachable("Unsupported output type!"); |
| } |
| |
| static std::unique_ptr<llvm::Module> |
| lowerFromCIRToLLVMIR(mlir::ModuleOp MLIRModule, llvm::LLVMContext &LLVMCtx, |
| bool EnableOpenMP, |
| llvm::StringRef mlirSaveTempsOutFile = {}, |
| llvm::vfs::FileSystem *fs = nullptr) { |
| return direct::lowerDirectlyFromCIRToLLVMIR(MLIRModule, LLVMCtx, EnableOpenMP, |
| mlirSaveTempsOutFile, fs); |
| } |
| |
| class CIRGenConsumer : public clang::ASTConsumer { |
| |
| virtual void anchor(); |
| |
| CIRGenAction::OutputType Action; |
| |
| CompilerInstance &CI; |
| |
| std::unique_ptr<raw_pwrite_stream> OutputStream; |
| |
| ASTContext *Context{nullptr}; |
| IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS; |
| std::unique_ptr<CIRGenerator> Gen; |
| const FrontendOptions &FEOptions; |
| CodeGenOptions &CGO; |
| |
| llvm::LLVMContext &LLVMCtx; |
| SmallVectorImpl<::clang::LinkModule> &LinkModules; |
| |
| public: |
| CIRGenConsumer(CIRGenAction::OutputType Action, CompilerInstance &CI, |
| CodeGenOptions &CGO, std::unique_ptr<raw_pwrite_stream> OS, |
| llvm::LLVMContext &LLVMCtx, |
| SmallVectorImpl<::clang::LinkModule> &LinkModules) |
| : Action(Action), CI(CI), OutputStream(std::move(OS)), |
| FS(&CI.getVirtualFileSystem()), |
| Gen(std::make_unique<CIRGenerator>(CI.getDiagnostics(), std::move(FS), |
| CI.getCodeGenOpts())), |
| FEOptions(CI.getFrontendOpts()), CGO(CGO), LLVMCtx(LLVMCtx), |
| LinkModules(LinkModules) {} |
| |
| void Initialize(ASTContext &Ctx) override { |
| assert(!Context && "initialized multiple times"); |
| Context = &Ctx; |
| Gen->Initialize(Ctx); |
| } |
| |
| bool HandleTopLevelDecl(DeclGroupRef D) override { |
| Gen->HandleTopLevelDecl(D); |
| return true; |
| } |
| |
| void HandleCXXStaticMemberVarInstantiation(clang::VarDecl *VD) override { |
| Gen->HandleCXXStaticMemberVarInstantiation(VD); |
| } |
| |
| void HandleOpenACCRoutineReference(const FunctionDecl *FD, |
| const OpenACCRoutineDecl *RD) override { |
| Gen->HandleOpenACCRoutineReference(FD, RD); |
| } |
| |
| void HandleInlineFunctionDefinition(FunctionDecl *D) override { |
| Gen->HandleInlineFunctionDefinition(D); |
| } |
| |
| void HandleTranslationUnit(ASTContext &C) override { |
| Gen->HandleTranslationUnit(C); |
| |
| if (!FEOptions.ClangIRDisableCIRVerifier) { |
| if (!Gen->verifyModule()) { |
| CI.getDiagnostics().Report( |
| diag::err_cir_verification_failed_pre_passes); |
| llvm::report_fatal_error( |
| "CIR codegen: module verification error before running CIR passes"); |
| return; |
| } |
| } |
| |
| mlir::ModuleOp MlirModule = Gen->getModule(); |
| mlir::MLIRContext &MlirCtx = Gen->getMLIRContext(); |
| |
| if (!FEOptions.ClangIRDisablePasses) { |
| std::string LibOptOptions = FEOptions.ClangIRLibOptOptions; |
| |
| // Setup and run CIR pipeline. |
| const bool EnableLibOpt = |
| FEOptions.ClangIRLibOptEnabled && (CGO.OptimizationLevel > 0); |
| if (runCIRToCIRPasses( |
| MlirModule, MlirCtx, C, !FEOptions.ClangIRDisableCIRVerifier, |
| FEOptions.ClangIREnableIdiomRecognizer, CGO.OptimizationLevel > 0, |
| EnableLibOpt, LibOptOptions, |
| FEOptions.ClangIREnableCallConvLowering) |
| .failed()) { |
| CI.getDiagnostics().Report(diag::err_cir_to_cir_transform_failed); |
| return; |
| } |
| } |
| |
| switch (Action) { |
| case CIRGenAction::OutputType::EmitCIR: |
| if (OutputStream && MlirModule) { |
| mlir::OpPrintingFlags Flags; |
| Flags.enableDebugInfo(/*enable=*/true, /*prettyForm=*/false); |
| MlirModule->print(*OutputStream, Flags); |
| } |
| break; |
| case CIRGenAction::OutputType::EmitLLVM: |
| case CIRGenAction::OutputType::EmitBC: |
| case CIRGenAction::OutputType::EmitObj: |
| case CIRGenAction::OutputType::EmitAssembly: { |
| StringRef saveTempsPrefix = CGO.SaveTempsFilePrefix; |
| std::string cirSaveTempsOutFile, mlirSaveTempsOutFile; |
| if (!saveTempsPrefix.empty()) { |
| SmallString<128> stem(saveTempsPrefix); |
| llvm::sys::path::replace_extension(stem, "cir"); |
| cirSaveTempsOutFile = std::string(stem); |
| llvm::sys::path::replace_extension(stem, "mlir"); |
| mlirSaveTempsOutFile = std::string(stem); |
| } |
| |
| if (!cirSaveTempsOutFile.empty()) { |
| std::error_code ec; |
| llvm::raw_fd_ostream out(cirSaveTempsOutFile, ec); |
| if (!ec) |
| MlirModule->print(out); |
| } |
| |
| std::unique_ptr<llvm::Module> LLVMModule = lowerFromCIRToLLVMIR( |
| MlirModule, LLVMCtx, C.getLangOpts().OpenMP, mlirSaveTempsOutFile, |
| &CI.getVirtualFileSystem()); |
| |
| if (linkInModules(*LLVMModule)) |
| return; |
| |
| BackendAction BEAction = getBackendActionFromOutputType(Action); |
| emitBackendOutput( |
| CI, CI.getCodeGenOpts(), C.getTargetInfo().getDataLayoutString(), |
| LLVMModule.get(), BEAction, FS, std::move(OutputStream)); |
| break; |
| } |
| } |
| } |
| |
| // TODO: share with BackendConsumer::LinkInModules once OG's CurLinkModule |
| // diagnostic-handler indirection is abstracted behind a callback for CIR. |
| bool linkInModules(llvm::Module &M) { |
| for (auto &LM : LinkModules) { |
| assert(LM.Module && "LinkModule does not actually have a module"); |
| |
| if (LM.PropagateAttrs) |
| for (llvm::Function &F : *LM.Module) { |
| if (F.isIntrinsic()) |
| continue; |
| clang::CodeGen::mergeDefaultFunctionDefinitionAttributes( |
| F, CGO, CI.getLangOpts(), CI.getTargetOpts(), LM.Internalize); |
| } |
| |
| bool Err; |
| if (LM.Internalize) { |
| Err = llvm::Linker::linkModules( |
| M, std::move(LM.Module), LM.LinkFlags, |
| [](llvm::Module &M, const llvm::StringSet<> &GVS) { |
| llvm::internalizeModule(M, [&GVS](const llvm::GlobalValue &GV) { |
| return !GV.hasName() || (GVS.count(GV.getName()) == 0); |
| }); |
| }); |
| } else { |
| Err = llvm::Linker::linkModules(M, std::move(LM.Module), LM.LinkFlags); |
| } |
| |
| if (Err) |
| return true; |
| } |
| |
| LinkModules.clear(); |
| return false; |
| } |
| |
| void HandleTagDeclDefinition(TagDecl *D) override { |
| PrettyStackTraceDecl CrashInfo(D, SourceLocation(), |
| Context->getSourceManager(), |
| "CIR generation of declaration"); |
| Gen->HandleTagDeclDefinition(D); |
| } |
| |
| void HandleTagDeclRequiredDefinition(const TagDecl *D) override { |
| Gen->HandleTagDeclRequiredDefinition(D); |
| } |
| |
| void CompleteTentativeDefinition(VarDecl *D) override { |
| Gen->CompleteTentativeDefinition(D); |
| } |
| |
| void HandleVTable(CXXRecordDecl *RD) override { Gen->HandleVTable(RD); } |
| }; |
| } // namespace cir |
| |
| void CIRGenConsumer::anchor() {} |
| |
| CIRGenAction::CIRGenAction(OutputType Act, mlir::MLIRContext *MLIRCtx) |
| : MLIRCtx(MLIRCtx ? MLIRCtx : new mlir::MLIRContext), |
| Ctx(std::make_unique<llvm::LLVMContext>()), Action(Act) {} |
| |
| CIRGenAction::~CIRGenAction() { MLIRMod.release(); } |
| |
| bool CIRGenAction::BeginSourceFileAction(CompilerInstance &CI) { |
| if (clang::loadLinkModules(CI, *Ctx, LinkModules)) |
| return false; |
| return ASTFrontendAction::BeginSourceFileAction(CI); |
| } |
| |
| static std::unique_ptr<raw_pwrite_stream> |
| getOutputStream(CompilerInstance &CI, StringRef InFile, |
| CIRGenAction::OutputType Action) { |
| switch (Action) { |
| case CIRGenAction::OutputType::EmitAssembly: |
| return CI.createDefaultOutputFile(false, InFile, "s"); |
| case CIRGenAction::OutputType::EmitCIR: |
| return CI.createDefaultOutputFile(false, InFile, "cir"); |
| case CIRGenAction::OutputType::EmitLLVM: |
| return CI.createDefaultOutputFile(false, InFile, "ll"); |
| case CIRGenAction::OutputType::EmitBC: |
| return CI.createDefaultOutputFile(true, InFile, "bc"); |
| case CIRGenAction::OutputType::EmitObj: |
| return CI.createDefaultOutputFile(true, InFile, "o"); |
| } |
| llvm_unreachable("Invalid CIRGenAction::OutputType"); |
| } |
| |
| std::unique_ptr<ASTConsumer> |
| CIRGenAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { |
| std::unique_ptr<llvm::raw_pwrite_stream> Out = CI.takeOutputStream(); |
| |
| if (!Out) |
| Out = getOutputStream(CI, InFile, Action); |
| |
| auto Result = std::make_unique<cir::CIRGenConsumer>( |
| Action, CI, CI.getCodeGenOpts(), std::move(Out), *Ctx, LinkModules); |
| |
| return Result; |
| } |
| |
| void EmitAssemblyAction::anchor() {} |
| EmitAssemblyAction::EmitAssemblyAction(mlir::MLIRContext *MLIRCtx) |
| : CIRGenAction(OutputType::EmitAssembly, MLIRCtx) {} |
| |
| void EmitCIRAction::anchor() {} |
| EmitCIRAction::EmitCIRAction(mlir::MLIRContext *MLIRCtx) |
| : CIRGenAction(OutputType::EmitCIR, MLIRCtx) {} |
| |
| void EmitLLVMAction::anchor() {} |
| EmitLLVMAction::EmitLLVMAction(mlir::MLIRContext *MLIRCtx) |
| : CIRGenAction(OutputType::EmitLLVM, MLIRCtx) {} |
| |
| void EmitBCAction::anchor() {} |
| EmitBCAction::EmitBCAction(mlir::MLIRContext *MLIRCtx) |
| : CIRGenAction(OutputType::EmitBC, MLIRCtx) {} |
| |
| void EmitObjAction::anchor() {} |
| EmitObjAction::EmitObjAction(mlir::MLIRContext *MLIRCtx) |
| : CIRGenAction(OutputType::EmitObj, MLIRCtx) {} |