blob: 42baa948d96980c31f56cea61cd1db46a77b5e3a [file] [edit]
//===--- 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) {}