llvm-project/flang/lib/Frontend/FrontendActions.cpp
Andrzej Warzynski 2e9439e489 [flang][driver] Add support for --target/--triple
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
2022-02-25 09:38:10 +00:00

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() {}