
This patch adds support for: * `--target` in the compiler driver (`flang-new`) * `--triple` in the frontend driver (`flang-new -fc1`) The semantics of these flags are inherited from `clangDriver`, i.e. consistent with `clang --target` and `clang -cc1 --triple`, respectively. A new structure is defined, `TargetOptions`, that will hold various Frontend options related to the target. Currently, this is mostly a placeholder that contains the target triple. In the future, it will be used for storing e.g. the CPU to tune for or the target features to enable. Additionally, the following target/triple related options are enabled [*]: `-print-effective-triple`, `-print-target-triple`. Definitions in Options.td are updated accordingly and, to facilated testing, `-emit-llvm` is added to the list of options available in `flang-new` (previously it was only enabled in `flang-new -fc1`). [*] These options were actually available before (like all other options defined in `clangDriver`), but not included in `flang-new --help`. Before this change, `flang-new` would just use `native` for defining the target, so these options were of little value. Differential Revision: https://reviews.llvm.org/D120246
509 lines
17 KiB
C++
509 lines
17 KiB
C++
//===--- FrontendActions.cpp ----------------------------------------------===//
|
|
//
|
|
// 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 "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/Pass/PassManager.h"
|
|
#include "mlir/Target/LLVMIR/ModuleTranslation.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/Support/ErrorHandling.h"
|
|
#include <clang/Basic/Diagnostic.h>
|
|
#include <memory>
|
|
|
|
using namespace Fortran::frontend;
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Custom BeginSourceFileAction
|
|
//===----------------------------------------------------------------------===//
|
|
bool PrescanAction::BeginSourceFileAction() { return RunPrescan(); }
|
|
|
|
bool PrescanAndParseAction::BeginSourceFileAction() {
|
|
return RunPrescan() && RunParse();
|
|
}
|
|
|
|
bool PrescanAndSemaAction::BeginSourceFileAction() {
|
|
return RunPrescan() && RunParse() && RunSemanticChecks() &&
|
|
GenerateRtTypeTables();
|
|
}
|
|
|
|
bool PrescanAndSemaDebugAction::BeginSourceFileAction() {
|
|
// This is a "debug" action for development purposes. To facilitate this, the
|
|
// semantic checks are made to succeed unconditionally to prevent this action
|
|
// from exiting early (i.e. in the presence of semantic errors). We should
|
|
// never do this in actions intended for end-users or otherwise regular
|
|
// compiler workflows!
|
|
return RunPrescan() && RunParse() && (RunSemanticChecks() || true) &&
|
|
(GenerateRtTypeTables() || true);
|
|
}
|
|
|
|
bool CodeGenAction::BeginSourceFileAction() {
|
|
bool res = RunPrescan() && RunParse() && RunSemanticChecks();
|
|
if (!res)
|
|
return res;
|
|
|
|
CompilerInstance &ci = this->instance();
|
|
|
|
// Load the MLIR dialects required by Flang
|
|
mlir::DialectRegistry registry;
|
|
mlirCtx = std::make_unique<mlir::MLIRContext>(registry);
|
|
fir::support::registerNonCodegenDialects(registry);
|
|
fir::support::loadNonCodegenDialects(*mlirCtx);
|
|
|
|
// Create a LoweringBridge
|
|
const common::IntrinsicTypeDefaultKinds &defKinds =
|
|
ci.invocation().semanticsContext().defaultKinds();
|
|
fir::KindMapping kindMap(mlirCtx.get(),
|
|
llvm::ArrayRef<fir::KindTy>{fir::fromDefaultKinds(defKinds)});
|
|
lower::LoweringBridge lb = Fortran::lower::LoweringBridge::create(*mlirCtx,
|
|
defKinds, ci.invocation().semanticsContext().intrinsics(),
|
|
ci.parsing().allCooked(), ci.invocation().targetOpts().triple, kindMap);
|
|
|
|
// Create a parse tree and lower it to FIR
|
|
Fortran::parser::Program &parseTree{*ci.parsing().parseTree()};
|
|
lb.lower(parseTree, ci.invocation().semanticsContext());
|
|
mlirModule = std::make_unique<mlir::ModuleOp>(lb.getModule());
|
|
|
|
// Run the default passes.
|
|
mlir::PassManager pm(mlirCtx.get(), mlir::OpPassManager::Nesting::Implicit);
|
|
pm.enableVerifier(/*verifyPasses=*/true);
|
|
pm.addPass(std::make_unique<Fortran::lower::VerifierPass>());
|
|
|
|
if (mlir::failed(pm.run(*mlirModule))) {
|
|
unsigned diagID =
|
|
ci.diagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error,
|
|
"verification of lowering to FIR failed");
|
|
ci.diagnostics().Report(diagID);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Custom ExecuteAction
|
|
//===----------------------------------------------------------------------===//
|
|
void InputOutputTestAction::ExecuteAction() {
|
|
CompilerInstance &ci = instance();
|
|
|
|
// Create a stream for errors
|
|
std::string buf;
|
|
llvm::raw_string_ostream error_stream{buf};
|
|
|
|
// Read the input file
|
|
Fortran::parser::AllSources &allSources{ci.allSources()};
|
|
std::string path{GetCurrentFileOrBufferName()};
|
|
const Fortran::parser::SourceFile *sf;
|
|
if (path == "-")
|
|
sf = allSources.ReadStandardInput(error_stream);
|
|
else
|
|
sf = allSources.Open(path, error_stream, std::optional<std::string>{"."s});
|
|
llvm::ArrayRef<char> fileContent = sf->content();
|
|
|
|
// Output file descriptor to receive the contents of the input file.
|
|
std::unique_ptr<llvm::raw_ostream> os;
|
|
|
|
// Copy the contents from the input file to the output file
|
|
if (!ci.IsOutputStreamNull()) {
|
|
// An output stream (outputStream_) was set earlier
|
|
ci.WriteOutputStream(fileContent.data());
|
|
} else {
|
|
// No pre-set output stream - create an output file
|
|
os = ci.CreateDefaultOutputFile(
|
|
/*binary=*/true, GetCurrentFileOrBufferName(), "txt");
|
|
if (!os)
|
|
return;
|
|
(*os) << fileContent.data();
|
|
}
|
|
}
|
|
|
|
void PrintPreprocessedAction::ExecuteAction() {
|
|
std::string buf;
|
|
llvm::raw_string_ostream outForPP{buf};
|
|
|
|
// Format or dump the prescanner's output
|
|
CompilerInstance &ci = this->instance();
|
|
if (ci.invocation().preprocessorOpts().noReformat) {
|
|
ci.parsing().DumpCookedChars(outForPP);
|
|
} else {
|
|
ci.parsing().EmitPreprocessedSource(
|
|
outForPP, !ci.invocation().preprocessorOpts().noLineDirectives);
|
|
}
|
|
|
|
// Print diagnostics from the prescanner
|
|
ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources());
|
|
|
|
// If a pre-defined output stream exists, dump the preprocessed content there
|
|
if (!ci.IsOutputStreamNull()) {
|
|
// Send the output to the pre-defined output buffer.
|
|
ci.WriteOutputStream(outForPP.str());
|
|
return;
|
|
}
|
|
|
|
// Create a file and save the preprocessed output there
|
|
std::unique_ptr<llvm::raw_pwrite_stream> os{ci.CreateDefaultOutputFile(
|
|
/*Binary=*/true, /*InFile=*/GetCurrentFileOrBufferName())};
|
|
if (!os) {
|
|
return;
|
|
}
|
|
|
|
(*os) << outForPP.str();
|
|
}
|
|
|
|
void DebugDumpProvenanceAction::ExecuteAction() {
|
|
this->instance().parsing().DumpProvenance(llvm::outs());
|
|
}
|
|
|
|
void ParseSyntaxOnlyAction::ExecuteAction() {
|
|
}
|
|
|
|
void DebugUnparseNoSemaAction::ExecuteAction() {
|
|
auto &invoc = this->instance().invocation();
|
|
auto &parseTree{instance().parsing().parseTree()};
|
|
|
|
// TODO: Options should come from CompilerInvocation
|
|
Unparse(llvm::outs(), *parseTree,
|
|
/*encoding=*/Fortran::parser::Encoding::UTF_8,
|
|
/*capitalizeKeywords=*/true, /*backslashEscapes=*/false,
|
|
/*preStatement=*/nullptr,
|
|
invoc.useAnalyzedObjectsForUnparse() ? &invoc.asFortran() : nullptr);
|
|
}
|
|
|
|
void DebugUnparseAction::ExecuteAction() {
|
|
auto &invoc = this->instance().invocation();
|
|
auto &parseTree{instance().parsing().parseTree()};
|
|
|
|
CompilerInstance &ci = this->instance();
|
|
auto os{ci.CreateDefaultOutputFile(
|
|
/*Binary=*/false, /*InFile=*/GetCurrentFileOrBufferName())};
|
|
|
|
// TODO: Options should come from CompilerInvocation
|
|
Unparse(*os, *parseTree,
|
|
/*encoding=*/Fortran::parser::Encoding::UTF_8,
|
|
/*capitalizeKeywords=*/true, /*backslashEscapes=*/false,
|
|
/*preStatement=*/nullptr,
|
|
invoc.useAnalyzedObjectsForUnparse() ? &invoc.asFortran() : nullptr);
|
|
|
|
// Report fatal semantic errors
|
|
reportFatalSemanticErrors();
|
|
}
|
|
|
|
void DebugUnparseWithSymbolsAction::ExecuteAction() {
|
|
auto &parseTree{*instance().parsing().parseTree()};
|
|
|
|
Fortran::semantics::UnparseWithSymbols(
|
|
llvm::outs(), parseTree, /*encoding=*/Fortran::parser::Encoding::UTF_8);
|
|
|
|
// Report fatal semantic errors
|
|
reportFatalSemanticErrors();
|
|
}
|
|
|
|
void DebugDumpSymbolsAction::ExecuteAction() {
|
|
CompilerInstance &ci = this->instance();
|
|
|
|
if (!ci.getRtTyTables().schemata) {
|
|
unsigned DiagID =
|
|
ci.diagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error,
|
|
"could not find module file for __fortran_type_info");
|
|
ci.diagnostics().Report(DiagID);
|
|
llvm::errs() << "\n";
|
|
return;
|
|
}
|
|
|
|
// Dump symbols
|
|
ci.semantics().DumpSymbols(llvm::outs());
|
|
}
|
|
|
|
void DebugDumpAllAction::ExecuteAction() {
|
|
CompilerInstance &ci = this->instance();
|
|
|
|
// Dump parse tree
|
|
auto &parseTree{instance().parsing().parseTree()};
|
|
llvm::outs() << "========================";
|
|
llvm::outs() << " Flang: parse tree dump ";
|
|
llvm::outs() << "========================\n";
|
|
Fortran::parser::DumpTree(
|
|
llvm::outs(), parseTree, &ci.invocation().asFortran());
|
|
|
|
if (!ci.getRtTyTables().schemata) {
|
|
unsigned DiagID =
|
|
ci.diagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error,
|
|
"could not find module file for __fortran_type_info");
|
|
ci.diagnostics().Report(DiagID);
|
|
llvm::errs() << "\n";
|
|
return;
|
|
}
|
|
|
|
// Dump symbols
|
|
llvm::outs() << "=====================";
|
|
llvm::outs() << " Flang: symbols dump ";
|
|
llvm::outs() << "=====================\n";
|
|
ci.semantics().DumpSymbols(llvm::outs());
|
|
}
|
|
|
|
void DebugDumpParseTreeNoSemaAction::ExecuteAction() {
|
|
auto &parseTree{instance().parsing().parseTree()};
|
|
|
|
// Dump parse tree
|
|
Fortran::parser::DumpTree(
|
|
llvm::outs(), parseTree, &this->instance().invocation().asFortran());
|
|
}
|
|
|
|
void DebugDumpParseTreeAction::ExecuteAction() {
|
|
auto &parseTree{instance().parsing().parseTree()};
|
|
|
|
// Dump parse tree
|
|
Fortran::parser::DumpTree(
|
|
llvm::outs(), parseTree, &this->instance().invocation().asFortran());
|
|
|
|
// Report fatal semantic errors
|
|
reportFatalSemanticErrors();
|
|
}
|
|
|
|
void DebugMeasureParseTreeAction::ExecuteAction() {
|
|
CompilerInstance &ci = this->instance();
|
|
|
|
// Parse. In case of failure, report and return.
|
|
ci.parsing().Parse(llvm::outs());
|
|
|
|
if (!ci.parsing().messages().empty() &&
|
|
(ci.invocation().warnAsErr() ||
|
|
ci.parsing().messages().AnyFatalError())) {
|
|
unsigned diagID = ci.diagnostics().getCustomDiagID(
|
|
clang::DiagnosticsEngine::Error, "Could not parse %0");
|
|
ci.diagnostics().Report(diagID) << GetCurrentFileOrBufferName();
|
|
|
|
ci.parsing().messages().Emit(
|
|
llvm::errs(), this->instance().allCookedSources());
|
|
return;
|
|
}
|
|
|
|
// Report the diagnostics from parsing
|
|
ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources());
|
|
|
|
auto &parseTree{*ci.parsing().parseTree()};
|
|
|
|
// Measure the parse tree
|
|
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->instance();
|
|
// Report and exit if fatal semantic errors are present
|
|
if (reportFatalSemanticErrors()) {
|
|
return;
|
|
}
|
|
|
|
auto &parseTree{*ci.parsing().parseTree()};
|
|
|
|
// Dump pre-FIR tree
|
|
if (auto ast{Fortran::lower::createPFT(
|
|
parseTree, ci.invocation().semanticsContext())}) {
|
|
Fortran::lower::dumpPFT(llvm::outs(), *ast);
|
|
} else {
|
|
unsigned diagID = ci.diagnostics().getCustomDiagID(
|
|
clang::DiagnosticsEngine::Error, "Pre FIR Tree is NULL.");
|
|
ci.diagnostics().Report(diagID);
|
|
}
|
|
}
|
|
|
|
void DebugDumpParsingLogAction::ExecuteAction() {
|
|
CompilerInstance &ci = this->instance();
|
|
|
|
ci.parsing().Parse(llvm::errs());
|
|
ci.parsing().DumpParsingLog(llvm::outs());
|
|
}
|
|
|
|
void GetDefinitionAction::ExecuteAction() {
|
|
CompilerInstance &ci = this->instance();
|
|
|
|
// Report and exit if fatal semantic errors are present
|
|
if (reportFatalSemanticErrors()) {
|
|
return;
|
|
}
|
|
|
|
parser::AllCookedSources &cs = ci.allCookedSources();
|
|
unsigned diagID = ci.diagnostics().getCustomDiagID(
|
|
clang::DiagnosticsEngine::Error, "Symbol not found");
|
|
|
|
auto gdv = ci.invocation().frontendOpts().getDefVals;
|
|
auto charBlock{cs.GetCharBlockFromLineAndColumns(
|
|
gdv.line, gdv.startColumn, gdv.endColumn)};
|
|
if (!charBlock) {
|
|
ci.diagnostics().Report(diagID);
|
|
return;
|
|
}
|
|
|
|
llvm::outs() << "String range: >" << charBlock->ToString() << "<\n";
|
|
|
|
auto *symbol{ci.invocation()
|
|
.semanticsContext()
|
|
.FindScope(*charBlock)
|
|
.FindSymbol(*charBlock)};
|
|
if (!symbol) {
|
|
ci.diagnostics().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->instance();
|
|
|
|
// Report and exit if fatal semantic errors are present
|
|
if (reportFatalSemanticErrors()) {
|
|
return;
|
|
}
|
|
|
|
ci.semantics().DumpSymbolsSources(llvm::outs());
|
|
}
|
|
|
|
#include "flang/Tools/CLOptions.inc"
|
|
|
|
// Lower the previously generated MLIR module into an LLVM IR module
|
|
void CodeGenAction::GenerateLLVMIR() {
|
|
assert(mlirModule && "The MLIR module has not been generated yet.");
|
|
|
|
CompilerInstance &ci = this->instance();
|
|
|
|
fir::support::loadDialects(*mlirCtx);
|
|
fir::support::registerLLVMTranslation(*mlirCtx);
|
|
|
|
// Set-up the MLIR pass manager
|
|
mlir::PassManager pm(mlirCtx.get(), mlir::OpPassManager::Nesting::Implicit);
|
|
|
|
pm.addPass(std::make_unique<Fortran::lower::VerifierPass>());
|
|
pm.enableVerifier(/*verifyPasses=*/true);
|
|
mlir::PassPipelineCLParser passPipeline("", "Compiler passes to run");
|
|
|
|
// Create the pass pipeline
|
|
fir::createMLIRToLLVMPassPipeline(pm);
|
|
|
|
// Run the pass manager
|
|
if (!mlir::succeeded(pm.run(*mlirModule))) {
|
|
unsigned diagID = ci.diagnostics().getCustomDiagID(
|
|
clang::DiagnosticsEngine::Error, "Lowering to LLVM IR failed");
|
|
ci.diagnostics().Report(diagID);
|
|
}
|
|
|
|
// Translate to LLVM IR
|
|
llvm::Optional<llvm::StringRef> moduleName = mlirModule->getName();
|
|
llvmCtx = std::make_unique<llvm::LLVMContext>();
|
|
llvmModule = mlir::translateModuleToLLVMIR(
|
|
*mlirModule, *llvmCtx, moduleName ? *moduleName : "FIRModule");
|
|
|
|
if (!llvmModule) {
|
|
unsigned diagID = ci.diagnostics().getCustomDiagID(
|
|
clang::DiagnosticsEngine::Error, "failed to create the LLVM module");
|
|
ci.diagnostics().Report(diagID);
|
|
return;
|
|
}
|
|
}
|
|
|
|
void EmitLLVMAction::ExecuteAction() {
|
|
CompilerInstance &ci = this->instance();
|
|
GenerateLLVMIR();
|
|
|
|
// If set, use the predefined outupt stream to print the generated module.
|
|
if (!ci.IsOutputStreamNull()) {
|
|
llvmModule->print(
|
|
ci.GetOutputStream(), /*AssemblyAnnotationWriter=*/nullptr);
|
|
return;
|
|
}
|
|
|
|
// No predefined output stream was set. Create an output file and dump the
|
|
// generated module there.
|
|
std::unique_ptr<llvm::raw_ostream> os = ci.CreateDefaultOutputFile(
|
|
/*Binary=*/false, /*InFile=*/GetCurrentFileOrBufferName(), "ll");
|
|
if (!os) {
|
|
unsigned diagID = ci.diagnostics().getCustomDiagID(
|
|
clang::DiagnosticsEngine::Error, "failed to create the output file");
|
|
ci.diagnostics().Report(diagID);
|
|
return;
|
|
}
|
|
llvmModule->print(*os, /*AssemblyAnnotationWriter=*/nullptr);
|
|
}
|
|
|
|
void EmitMLIRAction::ExecuteAction() {
|
|
CompilerInstance &ci = this->instance();
|
|
|
|
// Print the output. If a pre-defined output stream exists, dump the MLIR
|
|
// content there.
|
|
if (!ci.IsOutputStreamNull()) {
|
|
mlirModule->print(ci.GetOutputStream());
|
|
return;
|
|
}
|
|
|
|
// ... otherwise, print to a file.
|
|
std::unique_ptr<llvm::raw_pwrite_stream> os{ci.CreateDefaultOutputFile(
|
|
/*Binary=*/true, /*InFile=*/GetCurrentFileOrBufferName(), "mlir")};
|
|
if (!os) {
|
|
unsigned diagID = ci.diagnostics().getCustomDiagID(
|
|
clang::DiagnosticsEngine::Error, "failed to create the output file");
|
|
ci.diagnostics().Report(diagID);
|
|
return;
|
|
}
|
|
|
|
mlirModule->print(*os);
|
|
}
|
|
|
|
void EmitObjAction::ExecuteAction() {
|
|
CompilerInstance &ci = this->instance();
|
|
unsigned DiagID = ci.diagnostics().getCustomDiagID(
|
|
clang::DiagnosticsEngine::Error, "code-generation is not available yet");
|
|
ci.diagnostics().Report(DiagID);
|
|
}
|
|
|
|
void InitOnlyAction::ExecuteAction() {
|
|
CompilerInstance &ci = this->instance();
|
|
unsigned DiagID =
|
|
ci.diagnostics().getCustomDiagID(clang::DiagnosticsEngine::Warning,
|
|
"Use `-init-only` for testing purposes only");
|
|
ci.diagnostics().Report(DiagID);
|
|
}
|
|
|
|
void PluginParseTreeAction::ExecuteAction() {}
|