#include "flang/Frontend/FrontendActions.h"
#include "flang/Common/default-kinds.h"
#include "flang/Frontend/CompilerInstance.h"
#include "flang/Frontend/FrontendOptions.h"
#include "flang/Frontend/PreprocessorOptions.h"
#include "flang/Lower/Bridge.h"
#include "flang/Lower/PFTBuilder.h"
#include "flang/Lower/Support/Verifier.h"
#include "flang/Optimizer/Support/FIRContext.h"
#include "flang/Optimizer/Support/InitFIR.h"
#include "flang/Optimizer/Support/KindMapping.h"
#include "flang/Optimizer/Support/Utils.h"
#include "flang/Parser/dump-parse-tree.h"
#include "flang/Parser/parsing.h"
#include "flang/Parser/provenance.h"
#include "flang/Parser/source.h"
#include "flang/Parser/unparse.h"
#include "flang/Semantics/runtime-type-info.h"
#include "flang/Semantics/semantics.h"
#include "flang/Semantics/unparse-with-symbols.h"
#include "mlir/IR/Dialect.h"
#include "mlir/Parser/Parser.h"
#include "mlir/Pass/PassManager.h"
#include "mlir/Target/LLVMIR/ModuleTranslation.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/DiagnosticFrontend.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Analysis/TargetTransformInfo.h"
#include "llvm/Bitcode/BitcodeWriterPass.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/Verifier.h"
#include "llvm/IRReader/IRReader.h"
#include "llvm/MC/TargetRegistry.h"
#include "llvm/Passes/PassBuilder.h"
#include "llvm/Passes/StandardInstrumentations.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Target/TargetMachine.h"
#include <memory>
using namespace Fortran::frontend;
bool PrescanAction::beginSourceFileAction() { return runPrescan(); }
bool PrescanAndParseAction::beginSourceFileAction() {
return runPrescan() && runParse();
}
bool PrescanAndSemaAction::beginSourceFileAction() {
return runPrescan() && runParse() && runSemanticChecks() &&
generateRtTypeTables();
}
bool PrescanAndSemaDebugAction::beginSourceFileAction() {
return runPrescan() && runParse() && (runSemanticChecks() || true) &&
(generateRtTypeTables() || true);
}
bool CodeGenAction::beginSourceFileAction() {
llvmCtx = std::make_unique<llvm::LLVMContext>();
CompilerInstance &ci = this->getInstance();
if (this->getCurrentInput().getKind().getLanguage() == Language::LLVM_IR) {
llvm::SMDiagnostic err;
llvmModule = llvm::parseIRFile(getCurrentInput().getFile(), err, *llvmCtx);
if (!llvmModule || llvm::verifyModule(*llvmModule, &llvm::errs())) {
err.print("flang-new", llvm::errs());
unsigned diagID = ci.getDiagnostics().getCustomDiagID(
clang::DiagnosticsEngine::Error, "Could not parse IR");
ci.getDiagnostics().Report(diagID);
return false;
}
return true;
}
mlir::DialectRegistry registry;
mlirCtx = std::make_unique<mlir::MLIRContext>(registry);
fir::support::registerNonCodegenDialects(registry);
fir::support::loadNonCodegenDialects(*mlirCtx);
fir::support::loadDialects(*mlirCtx);
fir::support::registerLLVMTranslation(*mlirCtx);
if (this->getCurrentInput().getKind().getLanguage() == Language::MLIR) {
llvm::SourceMgr sourceMgr;
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> fileOrErr =
llvm::MemoryBuffer::getFileOrSTDIN(getCurrentInput().getFile());
sourceMgr.AddNewSourceBuffer(std::move(*fileOrErr), llvm::SMLoc());
mlir::OwningOpRef<mlir::ModuleOp> module =
mlir::parseSourceFile<mlir::ModuleOp>(sourceMgr, mlirCtx.get());
if (!module || mlir::failed(module->verifyInvariants())) {
unsigned diagID = ci.getDiagnostics().getCustomDiagID(
clang::DiagnosticsEngine::Error, "Could not parse FIR");
ci.getDiagnostics().Report(diagID);
return false;
}
mlirModule = std::make_unique<mlir::ModuleOp>(module.release());
return true;
}
if (getCurrentInput().getKind().getLanguage() != Language::Fortran) {
unsigned diagID = ci.getDiagnostics().getCustomDiagID(
clang::DiagnosticsEngine::Error,
"Invalid input type - expecting a Fortran file");
ci.getDiagnostics().Report(diagID);
return false;
}
bool res = runPrescan() && runParse() && runSemanticChecks() &&
generateRtTypeTables();
if (!res)
return res;
const common::IntrinsicTypeDefaultKinds &defKinds =
ci.getInvocation().getSemanticsContext().defaultKinds();
fir::KindMapping kindMap(mlirCtx.get(),
llvm::ArrayRef<fir::KindTy>{fir::fromDefaultKinds(defKinds)});
lower::LoweringBridge lb = Fortran::lower::LoweringBridge::create(
*mlirCtx, defKinds, ci.getInvocation().getSemanticsContext().intrinsics(),
ci.getInvocation().getSemanticsContext().targetCharacteristics(),
ci.getParsing().allCooked(), ci.getInvocation().getTargetOpts().triple,
kindMap);
Fortran::parser::Program &parseTree{*ci.getParsing().parseTree()};
lb.lower(parseTree, ci.getInvocation().getSemanticsContext());
mlirModule = std::make_unique<mlir::ModuleOp>(lb.getModule());
mlir::PassManager pm(mlirCtx.get(), mlir::OpPassManager::Nesting::Implicit);
pm.enableVerifier(true);
pm.addPass(std::make_unique<Fortran::lower::VerifierPass>());
if (mlir::failed(pm.run(*mlirModule))) {
unsigned diagID = ci.getDiagnostics().getCustomDiagID(
clang::DiagnosticsEngine::Error,
"verification of lowering to FIR failed");
ci.getDiagnostics().Report(diagID);
return false;
}
return true;
}
void InputOutputTestAction::executeAction() {
CompilerInstance &ci = getInstance();
std::string buf;
llvm::raw_string_ostream errorStream{buf};
Fortran::parser::AllSources &allSources{ci.getAllSources()};
std::string path{getCurrentFileOrBufferName()};
const Fortran::parser::SourceFile *sf;
if (path == "-")
sf = allSources.ReadStandardInput(errorStream);
else
sf = allSources.Open(path, errorStream, std::optional<std::string>{"."s});
llvm::ArrayRef<char> fileContent = sf->content();
std::unique_ptr<llvm::raw_ostream> os;
if (!ci.isOutputStreamNull()) {
ci.writeOutputStream(fileContent.data());
} else {
os = ci.createDefaultOutputFile(
true, getCurrentFileOrBufferName(), "txt");
if (!os)
return;
(*os) << fileContent.data();
}
}
void PrintPreprocessedAction::executeAction() {
std::string buf;
llvm::raw_string_ostream outForPP{buf};
CompilerInstance &ci = this->getInstance();
if (ci.getInvocation().getPreprocessorOpts().noReformat) {
ci.getParsing().DumpCookedChars(outForPP);
} else {
ci.getParsing().EmitPreprocessedSource(
outForPP, !ci.getInvocation().getPreprocessorOpts().noLineDirectives);
}
ci.getParsing().messages().Emit(llvm::errs(), ci.getAllCookedSources());
if (!ci.isOutputStreamNull()) {
ci.writeOutputStream(outForPP.str());
return;
}
std::unique_ptr<llvm::raw_pwrite_stream> os{ci.createDefaultOutputFile(
true, getCurrentFileOrBufferName())};
if (!os) {
return;
}
(*os) << outForPP.str();
}
void DebugDumpProvenanceAction::executeAction() {
this->getInstance().getParsing().DumpProvenance(llvm::outs());
}
void ParseSyntaxOnlyAction::executeAction() {}
void DebugUnparseNoSemaAction::executeAction() {
auto &invoc = this->getInstance().getInvocation();
auto &parseTree{getInstance().getParsing().parseTree()};
Unparse(llvm::outs(), *parseTree,
Fortran::parser::Encoding::UTF_8,
true, false,
nullptr,
invoc.getUseAnalyzedObjectsForUnparse() ? &invoc.getAsFortran()
: nullptr);
}
void DebugUnparseAction::executeAction() {
auto &invoc = this->getInstance().getInvocation();
auto &parseTree{getInstance().getParsing().parseTree()};
CompilerInstance &ci = this->getInstance();
auto os{ci.createDefaultOutputFile(
false, getCurrentFileOrBufferName())};
Unparse(*os, *parseTree,
Fortran::parser::Encoding::UTF_8,
true, false,
nullptr,
invoc.getUseAnalyzedObjectsForUnparse() ? &invoc.getAsFortran()
: nullptr);
reportFatalSemanticErrors();
}
void DebugUnparseWithSymbolsAction::executeAction() {
auto &parseTree{*getInstance().getParsing().parseTree()};
Fortran::semantics::UnparseWithSymbols(
llvm::outs(), parseTree, Fortran::parser::Encoding::UTF_8);
reportFatalSemanticErrors();
}
void DebugDumpSymbolsAction::executeAction() {
CompilerInstance &ci = this->getInstance();
if (!ci.getRtTyTables().schemata) {
unsigned diagID = ci.getDiagnostics().getCustomDiagID(
clang::DiagnosticsEngine::Error,
"could not find module file for __fortran_type_info");
ci.getDiagnostics().Report(diagID);
llvm::errs() << "\n";
return;
}
ci.getSemantics().DumpSymbols(llvm::outs());
}
void DebugDumpAllAction::executeAction() {
CompilerInstance &ci = this->getInstance();
auto &parseTree{getInstance().getParsing().parseTree()};
llvm::outs() << "========================";
llvm::outs() << " Flang: parse tree dump ";
llvm::outs() << "========================\n";
Fortran::parser::DumpTree(llvm::outs(), parseTree,
&ci.getInvocation().getAsFortran());
if (!ci.getRtTyTables().schemata) {
unsigned diagID = ci.getDiagnostics().getCustomDiagID(
clang::DiagnosticsEngine::Error,
"could not find module file for __fortran_type_info");
ci.getDiagnostics().Report(diagID);
llvm::errs() << "\n";
return;
}
llvm::outs() << "=====================";
llvm::outs() << " Flang: symbols dump ";
llvm::outs() << "=====================\n";
ci.getSemantics().DumpSymbols(llvm::outs());
}
void DebugDumpParseTreeNoSemaAction::executeAction() {
auto &parseTree{getInstance().getParsing().parseTree()};
Fortran::parser::DumpTree(
llvm::outs(), parseTree,
&this->getInstance().getInvocation().getAsFortran());
}
void DebugDumpParseTreeAction::executeAction() {
auto &parseTree{getInstance().getParsing().parseTree()};
Fortran::parser::DumpTree(
llvm::outs(), parseTree,
&this->getInstance().getInvocation().getAsFortran());
reportFatalSemanticErrors();
}
void DebugMeasureParseTreeAction::executeAction() {
CompilerInstance &ci = this->getInstance();
ci.getParsing().Parse(llvm::outs());
if (!ci.getParsing().messages().empty() &&
(ci.getInvocation().getWarnAsErr() ||
ci.getParsing().messages().AnyFatalError())) {
unsigned diagID = ci.getDiagnostics().getCustomDiagID(
clang::DiagnosticsEngine::Error, "Could not parse %0");
ci.getDiagnostics().Report(diagID) << getCurrentFileOrBufferName();
ci.getParsing().messages().Emit(llvm::errs(),
this->getInstance().getAllCookedSources());
return;
}
ci.getParsing().messages().Emit(llvm::errs(), ci.getAllCookedSources());
auto &parseTree{*ci.getParsing().parseTree()};
MeasurementVisitor visitor;
Fortran::parser::Walk(parseTree, visitor);
llvm::outs() << "Parse tree comprises " << visitor.objects
<< " objects and occupies " << visitor.bytes
<< " total bytes.\n";
}
void DebugPreFIRTreeAction::executeAction() {
CompilerInstance &ci = this->getInstance();
if (reportFatalSemanticErrors()) {
return;
}
auto &parseTree{*ci.getParsing().parseTree()};
if (auto ast{Fortran::lower::createPFT(
parseTree, ci.getInvocation().getSemanticsContext())}) {
Fortran::lower::dumpPFT(llvm::outs(), *ast);
} else {
unsigned diagID = ci.getDiagnostics().getCustomDiagID(
clang::DiagnosticsEngine::Error, "Pre FIR Tree is NULL.");
ci.getDiagnostics().Report(diagID);
}
}
void DebugDumpParsingLogAction::executeAction() {
CompilerInstance &ci = this->getInstance();
ci.getParsing().Parse(llvm::errs());
ci.getParsing().DumpParsingLog(llvm::outs());
}
void GetDefinitionAction::executeAction() {
CompilerInstance &ci = this->getInstance();
if (reportFatalSemanticErrors()) {
return;
}
parser::AllCookedSources &cs = ci.getAllCookedSources();
unsigned diagID = ci.getDiagnostics().getCustomDiagID(
clang::DiagnosticsEngine::Error, "Symbol not found");
auto gdv = ci.getInvocation().getFrontendOpts().getDefVals;
auto charBlock{cs.GetCharBlockFromLineAndColumns(
gdv.line, gdv.startColumn, gdv.endColumn)};
if (!charBlock) {
ci.getDiagnostics().Report(diagID);
return;
}
llvm::outs() << "String range: >" << charBlock->ToString() << "<\n";
auto *symbol{ci.getInvocation()
.getSemanticsContext()
.FindScope(*charBlock)
.FindSymbol(*charBlock)};
if (!symbol) {
ci.getDiagnostics().Report(diagID);
return;
}
llvm::outs() << "Found symbol name: " << symbol->name().ToString() << "\n";
auto sourceInfo{cs.GetSourcePositionRange(symbol->name())};
if (!sourceInfo) {
llvm_unreachable(
"Failed to obtain SourcePosition."
"TODO: Please, write a test and replace this with a diagnostic!");
return;
}
llvm::outs() << "Found symbol name: " << symbol->name().ToString() << "\n";
llvm::outs() << symbol->name().ToString() << ": "
<< sourceInfo->first.file.path() << ", "
<< sourceInfo->first.line << ", " << sourceInfo->first.column
<< "-" << sourceInfo->second.column << "\n";
}
void GetSymbolsSourcesAction::executeAction() {
CompilerInstance &ci = this->getInstance();
if (reportFatalSemanticErrors()) {
return;
}
ci.getSemantics().DumpSymbolsSources(llvm::outs());
}
CodeGenAction::~CodeGenAction() = default;
#include "flang/Tools/CLOptions.inc"
static llvm::OptimizationLevel
mapToLevel(const Fortran::frontend::CodeGenOptions &opts) {
switch (opts.OptimizationLevel) {
default:
llvm_unreachable("Invalid optimization level!");
case 0:
return llvm::OptimizationLevel::O0;
case 1:
return llvm::OptimizationLevel::O1;
case 2:
return llvm::OptimizationLevel::O2;
case 3:
return llvm::OptimizationLevel::O3;
}
}
void CodeGenAction::generateLLVMIR() {
assert(mlirModule && "The MLIR module has not been generated yet.");
CompilerInstance &ci = this->getInstance();
auto opts = ci.getInvocation().getCodeGenOpts();
llvm::OptimizationLevel level = mapToLevel(opts);
fir::support::loadDialects(*mlirCtx);
fir::support::registerLLVMTranslation(*mlirCtx);
mlir::PassManager pm(mlirCtx.get(), mlir::OpPassManager::Nesting::Implicit);
pm.addPass(std::make_unique<Fortran::lower::VerifierPass>());
pm.enableVerifier(true);
fir::createMLIRToLLVMPassPipeline(pm, level);
mlir::applyPassManagerCLOptions(pm);
if (!mlir::succeeded(pm.run(*mlirModule))) {
unsigned diagID = ci.getDiagnostics().getCustomDiagID(
clang::DiagnosticsEngine::Error, "Lowering to LLVM IR failed");
ci.getDiagnostics().Report(diagID);
}
llvm::Optional<llvm::StringRef> moduleName = mlirModule->getName();
llvmModule = mlir::translateModuleToLLVMIR(
*mlirModule, *llvmCtx, moduleName ? *moduleName : "FIRModule");
if (!llvmModule) {
unsigned diagID = ci.getDiagnostics().getCustomDiagID(
clang::DiagnosticsEngine::Error, "failed to create the LLVM module");
ci.getDiagnostics().Report(diagID);
return;
}
}
static llvm::CodeGenOpt::Level
getCGOptLevel(const Fortran::frontend::CodeGenOptions &opts) {
switch (opts.OptimizationLevel) {
default:
llvm_unreachable("Invalid optimization level!");
case 0:
return llvm::CodeGenOpt::None;
case 1:
return llvm::CodeGenOpt::Less;
case 2:
return llvm::CodeGenOpt::Default;
case 3:
return llvm::CodeGenOpt::Aggressive;
}
}
void CodeGenAction::setUpTargetMachine() {
CompilerInstance &ci = this->getInstance();
const std::string &theTriple = ci.getInvocation().getTargetOpts().triple;
if (llvmModule->getTargetTriple() != theTriple) {
ci.getDiagnostics().Report(clang::diag::warn_fe_override_module)
<< theTriple;
llvmModule->setTargetTriple(theTriple);
}
std::string error;
const llvm::Target *theTarget =
llvm::TargetRegistry::lookupTarget(theTriple, error);
assert(theTarget && "Failed to create Target");
llvm::CodeGenOpt::Level OptLevel =
getCGOptLevel(ci.getInvocation().getCodeGenOpts());
tm.reset(theTarget->createTargetMachine(
theTriple, "",
"", llvm::TargetOptions(), llvm::None,
llvm::None, OptLevel));
assert(tm && "Failed to create TargetMachine");
}
static std::unique_ptr<llvm::raw_pwrite_stream>
getOutputStream(CompilerInstance &ci, llvm::StringRef inFile,
BackendActionTy action) {
switch (action) {
case BackendActionTy::Backend_EmitAssembly:
return ci.createDefaultOutputFile(
false, inFile, "s");
case BackendActionTy::Backend_EmitLL:
return ci.createDefaultOutputFile(
false, inFile, "ll");
case BackendActionTy::Backend_EmitMLIR:
return ci.createDefaultOutputFile(
false, inFile, "mlir");
case BackendActionTy::Backend_EmitBC:
return ci.createDefaultOutputFile(
true, inFile, "bc");
case BackendActionTy::Backend_EmitObj:
return ci.createDefaultOutputFile(
true, inFile, "o");
}
llvm_unreachable("Invalid action!");
}
static void generateMachineCodeOrAssemblyImpl(clang::DiagnosticsEngine &diags,
llvm::TargetMachine &tm,
BackendActionTy act,
llvm::Module &llvmModule,
llvm::raw_pwrite_stream &os) {
assert(((act == BackendActionTy::Backend_EmitObj) ||
(act == BackendActionTy::Backend_EmitAssembly)) &&
"Unsupported action");
llvm::legacy::PassManager codeGenPasses;
codeGenPasses.add(
createTargetTransformInfoWrapperPass(tm.getTargetIRAnalysis()));
llvm::Triple triple(llvmModule.getTargetTriple());
std::unique_ptr<llvm::TargetLibraryInfoImpl> tlii =
std::make_unique<llvm::TargetLibraryInfoImpl>(triple);
assert(tlii && "Failed to create TargetLibraryInfo");
codeGenPasses.add(new llvm::TargetLibraryInfoWrapperPass(*tlii));
llvm::CodeGenFileType cgft = (act == BackendActionTy::Backend_EmitAssembly)
? llvm::CodeGenFileType::CGFT_AssemblyFile
: llvm::CodeGenFileType::CGFT_ObjectFile;
if (tm.addPassesToEmitFile(codeGenPasses, os, nullptr, cgft)) {
unsigned diagID =
diags.getCustomDiagID(clang::DiagnosticsEngine::Error,
"emission of this file type is not supported");
diags.Report(diagID);
return;
}
codeGenPasses.run(llvmModule);
}
void CodeGenAction::runOptimizationPipeline(llvm::raw_pwrite_stream &os) {
auto opts = getInstance().getInvocation().getCodeGenOpts();
llvm::OptimizationLevel level = mapToLevel(opts);
llvm::LoopAnalysisManager lam;
llvm::FunctionAnalysisManager fam;
llvm::CGSCCAnalysisManager cgam;
llvm::ModuleAnalysisManager mam;
llvm::PassInstrumentationCallbacks pic;
llvm::PipelineTuningOptions pto;
llvm::Optional<llvm::PGOOptions> pgoOpt;
llvm::StandardInstrumentations si(opts.DebugPassManager);
si.registerCallbacks(pic, &fam);
llvm::PassBuilder pb(tm.get(), pto, pgoOpt, &pic);
pb.registerModuleAnalyses(mam);
pb.registerCGSCCAnalyses(cgam);
pb.registerFunctionAnalyses(fam);
pb.registerLoopAnalyses(lam);
pb.crossRegisterProxies(lam, fam, cgam, mam);
llvm::ModulePassManager mpm;
if (opts.OptimizationLevel == 0)
mpm = pb.buildO0DefaultPipeline(level, false);
else
mpm = pb.buildPerModuleDefaultPipeline(level);
if (action == BackendActionTy::Backend_EmitBC)
mpm.addPass(llvm::BitcodeWriterPass(os));
mpm.run(*llvmModule, mam);
}
void CodeGenAction::executeAction() {
CompilerInstance &ci = this->getInstance();
std::unique_ptr<llvm::raw_pwrite_stream> os;
if (ci.isOutputStreamNull()) {
os = getOutputStream(ci, getCurrentFileOrBufferName(), action);
if (!os) {
unsigned diagID = ci.getDiagnostics().getCustomDiagID(
clang::DiagnosticsEngine::Error, "failed to create the output file");
ci.getDiagnostics().Report(diagID);
return;
}
}
if (action == BackendActionTy::Backend_EmitMLIR) {
mlirModule->print(ci.isOutputStreamNull() ? *os : ci.getOutputStream());
return;
}
if (!llvmModule)
generateLLVMIR();
runOptimizationPipeline(*os);
if (action == BackendActionTy::Backend_EmitLL) {
llvmModule->print(ci.isOutputStreamNull() ? *os : ci.getOutputStream(),
nullptr);
return;
}
setUpTargetMachine();
llvmModule->setDataLayout(tm->createDataLayout());
if (action == BackendActionTy::Backend_EmitBC) {
return;
}
if (action == BackendActionTy::Backend_EmitAssembly ||
action == BackendActionTy::Backend_EmitObj) {
generateMachineCodeOrAssemblyImpl(
ci.getDiagnostics(), *tm, action, *llvmModule,
ci.isOutputStreamNull() ? *os : ci.getOutputStream());
return;
}
}
void InitOnlyAction::executeAction() {
CompilerInstance &ci = this->getInstance();
unsigned diagID = ci.getDiagnostics().getCustomDiagID(
clang::DiagnosticsEngine::Warning,
"Use `-init-only` for testing purposes only");
ci.getDiagnostics().Report(diagID);
}
void PluginParseTreeAction::executeAction() {}
void DebugDumpPFTAction::executeAction() {
CompilerInstance &ci = this->getInstance();
if (auto ast = Fortran::lower::createPFT(*ci.getParsing().parseTree(),
ci.getSemantics().context())) {
Fortran::lower::dumpPFT(llvm::outs(), *ast);
return;
}
unsigned diagID = ci.getDiagnostics().getCustomDiagID(
clang::DiagnosticsEngine::Error, "Pre FIR Tree is NULL.");
ci.getDiagnostics().Report(diagID);
}
Fortran::parser::Parsing &PluginParseTreeAction::getParsing() {
return getInstance().getParsing();
}
std::unique_ptr<llvm::raw_pwrite_stream>
PluginParseTreeAction::createOutputFile(llvm::StringRef extension = "") {
std::unique_ptr<llvm::raw_pwrite_stream> os{
getInstance().createDefaultOutputFile(
false, getCurrentFileOrBufferName(),
extension)};
return os;
}