llvm-project/flang/lib/Frontend/CompilerInvocation.cpp
Peter Klausler 65987954d9
[flang] Add -fhermetic-module-files (#98083)
Module files emitted by this Fortran compiler are valid Fortran source
files. Symbols that are USE-associated into modules are represented in
their module files with USE statements and special comments with hash
codes in them to ensure that those USE statements resolve to the same
modules that were used to build the module when its module file was
generated.

This scheme prevents unchecked module file growth in large applications
by not emitting USE-associated symbols redundantly. This problem can be
especially bad when derived type definitions must be repeated in the
module files of their clients, and the clients of those modules, and so
on. However, this scheme has the disadvantage that clients of modules
must be compiled with dependent modules in the module search path.

This new -fhermetic-module-files option causes module file output to be
free of dependences on any non-intrinsic module files; dependent modules
are instead emitted as part of the module file, rather than being
USE-associated. It is intended for top level library module files that
are shipped with binary libraries when it is not convenient to collect
and ship their dependent module files as well.

Fixes https://github.com/llvm/llvm-project/issues/97398.
2024-07-11 14:02:44 -07:00

1511 lines
57 KiB
C++

//===- CompilerInvocation.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
//
//===----------------------------------------------------------------------===//
//
// Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
//
//===----------------------------------------------------------------------===//
#include "flang/Frontend/CompilerInvocation.h"
#include "flang/Common/Fortran-features.h"
#include "flang/Common/OpenMP-features.h"
#include "flang/Common/Version.h"
#include "flang/Frontend/CodeGenOptions.h"
#include "flang/Frontend/PreprocessorOptions.h"
#include "flang/Frontend/TargetOptions.h"
#include "flang/Semantics/semantics.h"
#include "flang/Tools/TargetSetup.h"
#include "flang/Version.inc"
#include "clang/Basic/AllDiagnostics.h"
#include "clang/Basic/DiagnosticDriver.h"
#include "clang/Basic/DiagnosticOptions.h"
#include "clang/Driver/DriverDiagnostic.h"
#include "clang/Driver/OptionUtils.h"
#include "clang/Driver/Options.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Frontend/Debug/Options.h"
#include "llvm/Option/Arg.h"
#include "llvm/Option/ArgList.h"
#include "llvm/Option/OptTable.h"
#include "llvm/Support/CodeGen.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/FileUtilities.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/TargetParser/Host.h"
#include "llvm/TargetParser/Triple.h"
#include <cstdlib>
#include <memory>
#include <optional>
using namespace Fortran::frontend;
//===----------------------------------------------------------------------===//
// Initialization.
//===----------------------------------------------------------------------===//
CompilerInvocationBase::CompilerInvocationBase()
: diagnosticOpts(new clang::DiagnosticOptions()),
preprocessorOpts(new PreprocessorOptions()) {}
CompilerInvocationBase::CompilerInvocationBase(const CompilerInvocationBase &x)
: diagnosticOpts(new clang::DiagnosticOptions(x.getDiagnosticOpts())),
preprocessorOpts(new PreprocessorOptions(x.getPreprocessorOpts())) {}
CompilerInvocationBase::~CompilerInvocationBase() = default;
//===----------------------------------------------------------------------===//
// Deserialization (from args)
//===----------------------------------------------------------------------===//
static bool parseShowColorsArgs(const llvm::opt::ArgList &args,
bool defaultColor = true) {
// Color diagnostics default to auto ("on" if terminal supports) in the
// compiler driver `flang-new` but default to off in the frontend driver
// `flang-new -fc1`, needing an explicit OPT_fdiagnostics_color.
// Support both clang's -f[no-]color-diagnostics and gcc's
// -f[no-]diagnostics-colors[=never|always|auto].
enum {
Colors_On,
Colors_Off,
Colors_Auto
} showColors = defaultColor ? Colors_Auto : Colors_Off;
for (auto *a : args) {
const llvm::opt::Option &opt = a->getOption();
if (opt.matches(clang::driver::options::OPT_fcolor_diagnostics)) {
showColors = Colors_On;
} else if (opt.matches(clang::driver::options::OPT_fno_color_diagnostics)) {
showColors = Colors_Off;
} else if (opt.matches(clang::driver::options::OPT_fdiagnostics_color_EQ)) {
llvm::StringRef value(a->getValue());
if (value == "always")
showColors = Colors_On;
else if (value == "never")
showColors = Colors_Off;
else if (value == "auto")
showColors = Colors_Auto;
}
}
return showColors == Colors_On ||
(showColors == Colors_Auto &&
llvm::sys::Process::StandardErrHasColors());
}
/// Extracts the optimisation level from \a args.
static unsigned getOptimizationLevel(llvm::opt::ArgList &args,
clang::DiagnosticsEngine &diags) {
unsigned defaultOpt = 0;
if (llvm::opt::Arg *a =
args.getLastArg(clang::driver::options::OPT_O_Group)) {
if (a->getOption().matches(clang::driver::options::OPT_O0))
return 0;
assert(a->getOption().matches(clang::driver::options::OPT_O));
return getLastArgIntValue(args, clang::driver::options::OPT_O, defaultOpt,
diags);
}
return defaultOpt;
}
bool Fortran::frontend::parseDiagnosticArgs(clang::DiagnosticOptions &opts,
llvm::opt::ArgList &args) {
opts.ShowColors = parseShowColorsArgs(args);
return true;
}
static bool parseDebugArgs(Fortran::frontend::CodeGenOptions &opts,
llvm::opt::ArgList &args,
clang::DiagnosticsEngine &diags) {
using DebugInfoKind = llvm::codegenoptions::DebugInfoKind;
if (llvm::opt::Arg *arg =
args.getLastArg(clang::driver::options::OPT_debug_info_kind_EQ)) {
std::optional<DebugInfoKind> val =
llvm::StringSwitch<std::optional<DebugInfoKind>>(arg->getValue())
.Case("line-tables-only", llvm::codegenoptions::DebugLineTablesOnly)
.Case("line-directives-only",
llvm::codegenoptions::DebugDirectivesOnly)
.Case("constructor", llvm::codegenoptions::DebugInfoConstructor)
.Case("limited", llvm::codegenoptions::LimitedDebugInfo)
.Case("standalone", llvm::codegenoptions::FullDebugInfo)
.Case("unused-types", llvm::codegenoptions::UnusedTypeInfo)
.Default(std::nullopt);
if (!val.has_value()) {
diags.Report(clang::diag::err_drv_invalid_value)
<< arg->getAsString(args) << arg->getValue();
return false;
}
opts.setDebugInfo(val.value());
if (val != llvm::codegenoptions::DebugLineTablesOnly &&
val != llvm::codegenoptions::FullDebugInfo &&
val != llvm::codegenoptions::NoDebugInfo) {
const auto debugWarning = diags.getCustomDiagID(
clang::DiagnosticsEngine::Warning, "Unsupported debug option: %0");
diags.Report(debugWarning) << arg->getValue();
}
}
return true;
}
static bool parseVectorLibArg(Fortran::frontend::CodeGenOptions &opts,
llvm::opt::ArgList &args,
clang::DiagnosticsEngine &diags) {
llvm::opt::Arg *arg = args.getLastArg(clang::driver::options::OPT_fveclib);
if (!arg)
return true;
using VectorLibrary = llvm::driver::VectorLibrary;
std::optional<VectorLibrary> val =
llvm::StringSwitch<std::optional<VectorLibrary>>(arg->getValue())
.Case("Accelerate", VectorLibrary::Accelerate)
.Case("LIBMVEC", VectorLibrary::LIBMVEC)
.Case("MASSV", VectorLibrary::MASSV)
.Case("SVML", VectorLibrary::SVML)
.Case("SLEEF", VectorLibrary::SLEEF)
.Case("Darwin_libsystem_m", VectorLibrary::Darwin_libsystem_m)
.Case("ArmPL", VectorLibrary::ArmPL)
.Case("NoLibrary", VectorLibrary::NoLibrary)
.Default(std::nullopt);
if (!val.has_value()) {
diags.Report(clang::diag::err_drv_invalid_value)
<< arg->getAsString(args) << arg->getValue();
return false;
}
opts.setVecLib(val.value());
return true;
}
// Generate an OptRemark object containing info on if the -Rgroup
// specified is enabled or not.
static CodeGenOptions::OptRemark
parseOptimizationRemark(clang::DiagnosticsEngine &diags,
llvm::opt::ArgList &args, llvm::opt::OptSpecifier optEq,
llvm::StringRef remarkOptName) {
assert((remarkOptName == "pass" || remarkOptName == "pass-missed" ||
remarkOptName == "pass-analysis") &&
"Unsupported remark option name provided.");
CodeGenOptions::OptRemark result;
for (llvm::opt::Arg *a : args) {
if (a->getOption().matches(clang::driver::options::OPT_R_Joined)) {
llvm::StringRef value = a->getValue();
if (value == remarkOptName) {
result.Kind = CodeGenOptions::RemarkKind::RK_Enabled;
// Enable everything
result.Pattern = ".*";
result.Regex = std::make_shared<llvm::Regex>(result.Pattern);
} else if (value.split('-') ==
std::make_pair(llvm::StringRef("no"), remarkOptName)) {
result.Kind = CodeGenOptions::RemarkKind::RK_Disabled;
// Disable everything
result.Pattern = "";
result.Regex = nullptr;
}
} else if (a->getOption().matches(optEq)) {
result.Kind = CodeGenOptions::RemarkKind::RK_WithPattern;
result.Pattern = a->getValue();
result.Regex = std::make_shared<llvm::Regex>(result.Pattern);
std::string regexError;
if (!result.Regex->isValid(regexError)) {
diags.Report(clang::diag::err_drv_optimization_remark_pattern)
<< regexError << a->getAsString(args);
return CodeGenOptions::OptRemark();
}
}
}
return result;
}
static void parseCodeGenArgs(Fortran::frontend::CodeGenOptions &opts,
llvm::opt::ArgList &args,
clang::DiagnosticsEngine &diags) {
opts.OptimizationLevel = getOptimizationLevel(args, diags);
if (args.hasFlag(clang::driver::options::OPT_fdebug_pass_manager,
clang::driver::options::OPT_fno_debug_pass_manager, false))
opts.DebugPassManager = 1;
if (args.hasFlag(clang::driver::options::OPT_fstack_arrays,
clang::driver::options::OPT_fno_stack_arrays, false))
opts.StackArrays = 1;
if (args.hasFlag(clang::driver::options::OPT_floop_versioning,
clang::driver::options::OPT_fno_loop_versioning, false))
opts.LoopVersioning = 1;
opts.AliasAnalysis = opts.OptimizationLevel > 0;
// -mframe-pointer=none/non-leaf/all option.
if (const llvm::opt::Arg *a =
args.getLastArg(clang::driver::options::OPT_mframe_pointer_EQ)) {
std::optional<llvm::FramePointerKind> val =
llvm::StringSwitch<std::optional<llvm::FramePointerKind>>(a->getValue())
.Case("none", llvm::FramePointerKind::None)
.Case("non-leaf", llvm::FramePointerKind::NonLeaf)
.Case("all", llvm::FramePointerKind::All)
.Default(std::nullopt);
if (!val.has_value()) {
diags.Report(clang::diag::err_drv_invalid_value)
<< a->getAsString(args) << a->getValue();
} else
opts.setFramePointer(val.value());
}
for (auto *a : args.filtered(clang::driver::options::OPT_fpass_plugin_EQ))
opts.LLVMPassPlugins.push_back(a->getValue());
// -fembed-offload-object option
for (auto *a :
args.filtered(clang::driver::options::OPT_fembed_offload_object_EQ))
opts.OffloadObjects.push_back(a->getValue());
// -flto=full/thin option.
if (const llvm::opt::Arg *a =
args.getLastArg(clang::driver::options::OPT_flto_EQ)) {
llvm::StringRef s = a->getValue();
assert((s == "full" || s == "thin") && "Unknown LTO mode.");
if (s == "full")
opts.PrepareForFullLTO = true;
else
opts.PrepareForThinLTO = true;
}
if (const llvm::opt::Arg *a = args.getLastArg(
clang::driver::options::OPT_mcode_object_version_EQ)) {
llvm::StringRef s = a->getValue();
if (s == "6")
opts.CodeObjectVersion = llvm::CodeObjectVersionKind::COV_6;
if (s == "5")
opts.CodeObjectVersion = llvm::CodeObjectVersionKind::COV_5;
if (s == "4")
opts.CodeObjectVersion = llvm::CodeObjectVersionKind::COV_4;
if (s == "none")
opts.CodeObjectVersion = llvm::CodeObjectVersionKind::COV_None;
}
// -f[no-]save-optimization-record[=<format>]
if (const llvm::opt::Arg *a =
args.getLastArg(clang::driver::options::OPT_opt_record_file))
opts.OptRecordFile = a->getValue();
// Optimization file format. Defaults to yaml
if (const llvm::opt::Arg *a =
args.getLastArg(clang::driver::options::OPT_opt_record_format))
opts.OptRecordFormat = a->getValue();
// Specifies, using a regex, which successful optimization passes(middle and
// backend), to include in the final optimization record file generated. If
// not provided -fsave-optimization-record will include all passes.
if (const llvm::opt::Arg *a =
args.getLastArg(clang::driver::options::OPT_opt_record_passes))
opts.OptRecordPasses = a->getValue();
// Create OptRemark that allows printing of all successful optimization
// passes applied.
opts.OptimizationRemark =
parseOptimizationRemark(diags, args, clang::driver::options::OPT_Rpass_EQ,
/*remarkOptName=*/"pass");
// Create OptRemark that allows all missed optimization passes to be printed.
opts.OptimizationRemarkMissed = parseOptimizationRemark(
diags, args, clang::driver::options::OPT_Rpass_missed_EQ,
/*remarkOptName=*/"pass-missed");
// Create OptRemark that allows all optimization decisions made by LLVM
// to be printed.
opts.OptimizationRemarkAnalysis = parseOptimizationRemark(
diags, args, clang::driver::options::OPT_Rpass_analysis_EQ,
/*remarkOptName=*/"pass-analysis");
if (opts.getDebugInfo() == llvm::codegenoptions::NoDebugInfo) {
// If the user requested a flag that requires source locations available in
// the backend, make sure that the backend tracks source location
// information.
bool needLocTracking = !opts.OptRecordFile.empty() ||
!opts.OptRecordPasses.empty() ||
!opts.OptRecordFormat.empty() ||
opts.OptimizationRemark.hasValidPattern() ||
opts.OptimizationRemarkMissed.hasValidPattern() ||
opts.OptimizationRemarkAnalysis.hasValidPattern();
if (needLocTracking)
opts.setDebugInfo(llvm::codegenoptions::LocTrackingOnly);
}
if (auto *a = args.getLastArg(clang::driver::options::OPT_save_temps_EQ))
opts.SaveTempsDir = a->getValue();
// -mlink-builtin-bitcode
for (auto *a :
args.filtered(clang::driver::options::OPT_mlink_builtin_bitcode))
opts.BuiltinBCLibs.push_back(a->getValue());
// -mrelocation-model option.
if (const llvm::opt::Arg *a =
args.getLastArg(clang::driver::options::OPT_mrelocation_model)) {
llvm::StringRef modelName = a->getValue();
auto relocModel =
llvm::StringSwitch<std::optional<llvm::Reloc::Model>>(modelName)
.Case("static", llvm::Reloc::Static)
.Case("pic", llvm::Reloc::PIC_)
.Case("dynamic-no-pic", llvm::Reloc::DynamicNoPIC)
.Case("ropi", llvm::Reloc::ROPI)
.Case("rwpi", llvm::Reloc::RWPI)
.Case("ropi-rwpi", llvm::Reloc::ROPI_RWPI)
.Default(std::nullopt);
if (relocModel.has_value())
opts.setRelocationModel(*relocModel);
else
diags.Report(clang::diag::err_drv_invalid_value)
<< a->getAsString(args) << modelName;
}
// -pic-level and -pic-is-pie option.
if (int picLevel = getLastArgIntValue(
args, clang::driver::options::OPT_pic_level, 0, diags)) {
if (picLevel > 2)
diags.Report(clang::diag::err_drv_invalid_value)
<< args.getLastArg(clang::driver::options::OPT_pic_level)
->getAsString(args)
<< picLevel;
opts.PICLevel = picLevel;
if (args.hasArg(clang::driver::options::OPT_pic_is_pie))
opts.IsPIE = 1;
}
// -mcmodel option.
if (const llvm::opt::Arg *a =
args.getLastArg(clang::driver::options::OPT_mcmodel_EQ)) {
llvm::StringRef modelName = a->getValue();
std::optional<llvm::CodeModel::Model> codeModel = getCodeModel(modelName);
if (codeModel.has_value())
opts.CodeModel = modelName;
else
diags.Report(clang::diag::err_drv_invalid_value)
<< a->getAsString(args) << modelName;
}
if (const llvm::opt::Arg *arg = args.getLastArg(
clang::driver::options::OPT_mlarge_data_threshold_EQ)) {
uint64_t LDT;
if (llvm::StringRef(arg->getValue()).getAsInteger(/*Radix=*/10, LDT)) {
diags.Report(clang::diag::err_drv_invalid_value)
<< arg->getSpelling() << arg->getValue();
}
opts.LargeDataThreshold = LDT;
}
// This option is compatible with -f[no-]underscoring in gfortran.
if (args.hasFlag(clang::driver::options::OPT_fno_underscoring,
clang::driver::options::OPT_funderscoring, false)) {
opts.Underscoring = 0;
}
}
/// Parses all target input arguments and populates the target
/// options accordingly.
///
/// \param [in] opts The target options instance to update
/// \param [in] args The list of input arguments (from the compiler invocation)
static void parseTargetArgs(TargetOptions &opts, llvm::opt::ArgList &args) {
if (const llvm::opt::Arg *a =
args.getLastArg(clang::driver::options::OPT_triple))
opts.triple = a->getValue();
if (const llvm::opt::Arg *a =
args.getLastArg(clang::driver::options::OPT_target_cpu))
opts.cpu = a->getValue();
for (const llvm::opt::Arg *currentArg :
args.filtered(clang::driver::options::OPT_target_feature))
opts.featuresAsWritten.emplace_back(currentArg->getValue());
}
// Tweak the frontend configuration based on the frontend action
static void setUpFrontendBasedOnAction(FrontendOptions &opts) {
if (opts.programAction == DebugDumpParsingLog)
opts.instrumentedParse = true;
if (opts.programAction == DebugDumpProvenance ||
opts.programAction == Fortran::frontend::GetDefinition)
opts.needProvenanceRangeToCharBlockMappings = true;
}
/// Parse the argument specified for the -fconvert=<value> option
static std::optional<const char *> parseConvertArg(const char *s) {
return llvm::StringSwitch<std::optional<const char *>>(s)
.Case("unknown", "UNKNOWN")
.Case("native", "NATIVE")
.Case("little-endian", "LITTLE_ENDIAN")
.Case("big-endian", "BIG_ENDIAN")
.Case("swap", "SWAP")
.Default(std::nullopt);
}
static bool parseFrontendArgs(FrontendOptions &opts, llvm::opt::ArgList &args,
clang::DiagnosticsEngine &diags) {
unsigned numErrorsBefore = diags.getNumErrors();
// By default the frontend driver creates a ParseSyntaxOnly action.
opts.programAction = ParseSyntaxOnly;
// Treat multiple action options as an invocation error. Note that `clang
// -cc1` does accept multiple action options, but will only consider the
// rightmost one.
if (args.hasMultipleArgs(clang::driver::options::OPT_Action_Group)) {
const unsigned diagID = diags.getCustomDiagID(
clang::DiagnosticsEngine::Error, "Only one action option is allowed");
diags.Report(diagID);
return false;
}
// Identify the action (i.e. opts.ProgramAction)
if (const llvm::opt::Arg *a =
args.getLastArg(clang::driver::options::OPT_Action_Group)) {
switch (a->getOption().getID()) {
default: {
llvm_unreachable("Invalid option in group!");
}
case clang::driver::options::OPT_test_io:
opts.programAction = InputOutputTest;
break;
case clang::driver::options::OPT_E:
opts.programAction = PrintPreprocessedInput;
break;
case clang::driver::options::OPT_fsyntax_only:
opts.programAction = ParseSyntaxOnly;
break;
case clang::driver::options::OPT_emit_fir:
opts.programAction = EmitFIR;
break;
case clang::driver::options::OPT_emit_hlfir:
opts.programAction = EmitHLFIR;
break;
case clang::driver::options::OPT_emit_llvm:
opts.programAction = EmitLLVM;
break;
case clang::driver::options::OPT_emit_llvm_bc:
opts.programAction = EmitLLVMBitcode;
break;
case clang::driver::options::OPT_emit_obj:
opts.programAction = EmitObj;
break;
case clang::driver::options::OPT_S:
opts.programAction = EmitAssembly;
break;
case clang::driver::options::OPT_fdebug_unparse:
opts.programAction = DebugUnparse;
break;
case clang::driver::options::OPT_fdebug_unparse_no_sema:
opts.programAction = DebugUnparseNoSema;
break;
case clang::driver::options::OPT_fdebug_unparse_with_symbols:
opts.programAction = DebugUnparseWithSymbols;
break;
case clang::driver::options::OPT_fdebug_unparse_with_modules:
opts.programAction = DebugUnparseWithModules;
break;
case clang::driver::options::OPT_fdebug_dump_symbols:
opts.programAction = DebugDumpSymbols;
break;
case clang::driver::options::OPT_fdebug_dump_parse_tree:
opts.programAction = DebugDumpParseTree;
break;
case clang::driver::options::OPT_fdebug_dump_pft:
opts.programAction = DebugDumpPFT;
break;
case clang::driver::options::OPT_fdebug_dump_all:
opts.programAction = DebugDumpAll;
break;
case clang::driver::options::OPT_fdebug_dump_parse_tree_no_sema:
opts.programAction = DebugDumpParseTreeNoSema;
break;
case clang::driver::options::OPT_fdebug_dump_provenance:
opts.programAction = DebugDumpProvenance;
break;
case clang::driver::options::OPT_fdebug_dump_parsing_log:
opts.programAction = DebugDumpParsingLog;
break;
case clang::driver::options::OPT_fdebug_measure_parse_tree:
opts.programAction = DebugMeasureParseTree;
break;
case clang::driver::options::OPT_fdebug_pre_fir_tree:
opts.programAction = DebugPreFIRTree;
break;
case clang::driver::options::OPT_fget_symbols_sources:
opts.programAction = GetSymbolsSources;
break;
case clang::driver::options::OPT_fget_definition:
opts.programAction = GetDefinition;
break;
case clang::driver::options::OPT_init_only:
opts.programAction = InitOnly;
break;
// TODO:
// case clang::driver::options::OPT_emit_llvm:
// case clang::driver::options::OPT_emit_llvm_only:
// case clang::driver::options::OPT_emit_codegen_only:
// case clang::driver::options::OPT_emit_module:
// (...)
}
// Parse the values provided with `-fget-definition` (there should be 3
// integers)
if (llvm::opt::OptSpecifier(a->getOption().getID()) ==
clang::driver::options::OPT_fget_definition) {
unsigned optVals[3] = {0, 0, 0};
for (unsigned i = 0; i < 3; i++) {
llvm::StringRef val = a->getValue(i);
if (val.getAsInteger(10, optVals[i])) {
// A non-integer was encountered - that's an error.
diags.Report(clang::diag::err_drv_invalid_value)
<< a->getOption().getName() << val;
break;
}
}
opts.getDefVals.line = optVals[0];
opts.getDefVals.startColumn = optVals[1];
opts.getDefVals.endColumn = optVals[2];
}
}
// Parsing -load <dsopath> option and storing shared object path
if (llvm::opt::Arg *a = args.getLastArg(clang::driver::options::OPT_load)) {
opts.plugins.push_back(a->getValue());
}
// Parsing -plugin <name> option and storing plugin name and setting action
if (const llvm::opt::Arg *a =
args.getLastArg(clang::driver::options::OPT_plugin)) {
opts.programAction = PluginAction;
opts.actionName = a->getValue();
}
opts.outputFile = args.getLastArgValue(clang::driver::options::OPT_o);
opts.showHelp = args.hasArg(clang::driver::options::OPT_help);
opts.showVersion = args.hasArg(clang::driver::options::OPT_version);
// Get the input kind (from the value passed via `-x`)
InputKind dashX(Language::Unknown);
if (const llvm::opt::Arg *a =
args.getLastArg(clang::driver::options::OPT_x)) {
llvm::StringRef xValue = a->getValue();
// Principal languages.
dashX = llvm::StringSwitch<InputKind>(xValue)
// Flang does not differentiate between pre-processed and not
// pre-processed inputs.
.Case("f95", Language::Fortran)
.Case("f95-cpp-input", Language::Fortran)
// CUDA Fortran
.Case("cuda", Language::Fortran)
.Default(Language::Unknown);
// Flang's intermediate representations.
if (dashX.isUnknown())
dashX = llvm::StringSwitch<InputKind>(xValue)
.Case("ir", Language::LLVM_IR)
.Case("fir", Language::MLIR)
.Case("mlir", Language::MLIR)
.Default(Language::Unknown);
if (dashX.isUnknown())
diags.Report(clang::diag::err_drv_invalid_value)
<< a->getAsString(args) << a->getValue();
}
// Collect the input files and save them in our instance of FrontendOptions.
std::vector<std::string> inputs =
args.getAllArgValues(clang::driver::options::OPT_INPUT);
opts.inputs.clear();
if (inputs.empty())
// '-' is the default input if none is given.
inputs.push_back("-");
for (unsigned i = 0, e = inputs.size(); i != e; ++i) {
InputKind ik = dashX;
if (ik.isUnknown()) {
ik = FrontendOptions::getInputKindForExtension(
llvm::StringRef(inputs[i]).rsplit('.').second);
if (ik.isUnknown())
ik = Language::Unknown;
if (i == 0)
dashX = ik;
}
opts.inputs.emplace_back(std::move(inputs[i]), ik);
}
// Set fortranForm based on options -ffree-form and -ffixed-form.
if (const auto *arg =
args.getLastArg(clang::driver::options::OPT_ffixed_form,
clang::driver::options::OPT_ffree_form)) {
opts.fortranForm =
arg->getOption().matches(clang::driver::options::OPT_ffixed_form)
? FortranForm::FixedForm
: FortranForm::FreeForm;
}
// Set fixedFormColumns based on -ffixed-line-length=<value>
if (const auto *arg =
args.getLastArg(clang::driver::options::OPT_ffixed_line_length_EQ)) {
llvm::StringRef argValue = llvm::StringRef(arg->getValue());
std::int64_t columns = -1;
if (argValue == "none") {
columns = 0;
} else if (argValue.getAsInteger(/*Radix=*/10, columns)) {
columns = -1;
}
if (columns < 0) {
diags.Report(clang::diag::err_drv_negative_columns)
<< arg->getOption().getName() << arg->getValue();
} else if (columns == 0) {
opts.fixedFormColumns = 1000000;
} else if (columns < 7) {
diags.Report(clang::diag::err_drv_small_columns)
<< arg->getOption().getName() << arg->getValue() << "7";
} else {
opts.fixedFormColumns = columns;
}
}
// Set conversion based on -fconvert=<value>
if (const auto *arg =
args.getLastArg(clang::driver::options::OPT_fconvert_EQ)) {
const char *argValue = arg->getValue();
if (auto convert = parseConvertArg(argValue))
opts.envDefaults.push_back({"FORT_CONVERT", *convert});
else
diags.Report(clang::diag::err_drv_invalid_value)
<< arg->getAsString(args) << argValue;
}
// -f{no-}implicit-none
opts.features.Enable(
Fortran::common::LanguageFeature::ImplicitNoneTypeAlways,
args.hasFlag(clang::driver::options::OPT_fimplicit_none,
clang::driver::options::OPT_fno_implicit_none, false));
// -f{no-}backslash
opts.features.Enable(Fortran::common::LanguageFeature::BackslashEscapes,
args.hasFlag(clang::driver::options::OPT_fbackslash,
clang::driver::options::OPT_fno_backslash,
false));
// -f{no-}logical-abbreviations
opts.features.Enable(
Fortran::common::LanguageFeature::LogicalAbbreviations,
args.hasFlag(clang::driver::options::OPT_flogical_abbreviations,
clang::driver::options::OPT_fno_logical_abbreviations,
false));
// -f{no-}xor-operator
opts.features.Enable(
Fortran::common::LanguageFeature::XOROperator,
args.hasFlag(clang::driver::options::OPT_fxor_operator,
clang::driver::options::OPT_fno_xor_operator, false));
// -fno-automatic
if (args.hasArg(clang::driver::options::OPT_fno_automatic)) {
opts.features.Enable(Fortran::common::LanguageFeature::DefaultSave);
}
if (args.hasArg(
clang::driver::options::OPT_falternative_parameter_statement)) {
opts.features.Enable(Fortran::common::LanguageFeature::OldStyleParameter);
}
if (const llvm::opt::Arg *arg =
args.getLastArg(clang::driver::options::OPT_finput_charset_EQ)) {
llvm::StringRef argValue = arg->getValue();
if (argValue == "utf-8") {
opts.encoding = Fortran::parser::Encoding::UTF_8;
} else if (argValue == "latin-1") {
opts.encoding = Fortran::parser::Encoding::LATIN_1;
} else {
diags.Report(clang::diag::err_drv_invalid_value)
<< arg->getAsString(args) << argValue;
}
}
setUpFrontendBasedOnAction(opts);
opts.dashX = dashX;
return diags.getNumErrors() == numErrorsBefore;
}
// Generate the path to look for intrinsic modules
static std::string getIntrinsicDir(const char *argv) {
// TODO: Find a system independent API
llvm::SmallString<128> driverPath;
driverPath.assign(llvm::sys::fs::getMainExecutable(argv, nullptr));
llvm::sys::path::remove_filename(driverPath);
driverPath.append("/../include/flang/");
return std::string(driverPath);
}
// Generate the path to look for OpenMP headers
static std::string getOpenMPHeadersDir(const char *argv) {
llvm::SmallString<128> includePath;
includePath.assign(llvm::sys::fs::getMainExecutable(argv, nullptr));
llvm::sys::path::remove_filename(includePath);
includePath.append("/../include/flang/OpenMP/");
return std::string(includePath);
}
/// Parses all preprocessor input arguments and populates the preprocessor
/// options accordingly.
///
/// \param [in] opts The preprocessor options instance
/// \param [out] args The list of input arguments
static void parsePreprocessorArgs(Fortran::frontend::PreprocessorOptions &opts,
llvm::opt::ArgList &args) {
// Add macros from the command line.
for (const auto *currentArg : args.filtered(clang::driver::options::OPT_D,
clang::driver::options::OPT_U)) {
if (currentArg->getOption().matches(clang::driver::options::OPT_D)) {
opts.addMacroDef(currentArg->getValue());
} else {
opts.addMacroUndef(currentArg->getValue());
}
}
// Add the ordered list of -I's.
for (const auto *currentArg : args.filtered(clang::driver::options::OPT_I))
opts.searchDirectoriesFromDashI.emplace_back(currentArg->getValue());
// Prepend the ordered list of -intrinsic-modules-path
// to the default location to search.
for (const auto *currentArg :
args.filtered(clang::driver::options::OPT_fintrinsic_modules_path))
opts.searchDirectoriesFromIntrModPath.emplace_back(currentArg->getValue());
// -cpp/-nocpp
if (const auto *currentArg = args.getLastArg(
clang::driver::options::OPT_cpp, clang::driver::options::OPT_nocpp))
opts.macrosFlag =
(currentArg->getOption().matches(clang::driver::options::OPT_cpp))
? PPMacrosFlag::Include
: PPMacrosFlag::Exclude;
opts.noReformat = args.hasArg(clang::driver::options::OPT_fno_reformat);
opts.noLineDirectives = args.hasArg(clang::driver::options::OPT_P);
opts.showMacros = args.hasArg(clang::driver::options::OPT_dM);
}
/// Parses all semantic related arguments and populates the variables
/// options accordingly. Returns false if new errors are generated.
static bool parseSemaArgs(CompilerInvocation &res, llvm::opt::ArgList &args,
clang::DiagnosticsEngine &diags) {
unsigned numErrorsBefore = diags.getNumErrors();
// -J/module-dir option
auto moduleDirList =
args.getAllArgValues(clang::driver::options::OPT_module_dir);
// User can only specify -J/-module-dir once
// https://gcc.gnu.org/onlinedocs/gfortran/Directory-Options.html
if (moduleDirList.size() > 1) {
const unsigned diagID =
diags.getCustomDiagID(clang::DiagnosticsEngine::Error,
"Only one '-module-dir/-J' option allowed");
diags.Report(diagID);
}
if (moduleDirList.size() == 1)
res.setModuleDir(moduleDirList[0]);
// -fdebug-module-writer option
if (args.hasArg(clang::driver::options::OPT_fdebug_module_writer)) {
res.setDebugModuleDir(true);
}
// -fhermetic-module-files option
if (args.hasArg(clang::driver::options::OPT_fhermetic_module_files)) {
res.setHermeticModuleFileOutput(true);
}
// -module-suffix
if (const auto *moduleSuffix =
args.getLastArg(clang::driver::options::OPT_module_suffix)) {
res.setModuleFileSuffix(moduleSuffix->getValue());
}
// -f{no-}analyzed-objects-for-unparse
res.setUseAnalyzedObjectsForUnparse(args.hasFlag(
clang::driver::options::OPT_fanalyzed_objects_for_unparse,
clang::driver::options::OPT_fno_analyzed_objects_for_unparse, true));
return diags.getNumErrors() == numErrorsBefore;
}
/// Parses all diagnostics related arguments and populates the variables
/// options accordingly. Returns false if new errors are generated.
static bool parseDiagArgs(CompilerInvocation &res, llvm::opt::ArgList &args,
clang::DiagnosticsEngine &diags) {
unsigned numErrorsBefore = diags.getNumErrors();
// -Werror option
// TODO: Currently throws a Diagnostic for anything other than -W<error>,
// this has to change when other -W<opt>'s are supported.
if (args.hasArg(clang::driver::options::OPT_W_Joined)) {
const auto &wArgs =
args.getAllArgValues(clang::driver::options::OPT_W_Joined);
for (const auto &wArg : wArgs) {
if (wArg == "error") {
res.setWarnAsErr(true);
} else {
const unsigned diagID =
diags.getCustomDiagID(clang::DiagnosticsEngine::Error,
"Only `-Werror` is supported currently.");
diags.Report(diagID);
}
}
}
// Default to off for `flang-new -fc1`.
res.getFrontendOpts().showColors =
parseShowColorsArgs(args, /*defaultDiagColor=*/false);
// Honor color diagnostics.
res.getDiagnosticOpts().ShowColors = res.getFrontendOpts().showColors;
return diags.getNumErrors() == numErrorsBefore;
}
/// Parses all Dialect related arguments and populates the variables
/// options accordingly. Returns false if new errors are generated.
static bool parseDialectArgs(CompilerInvocation &res, llvm::opt::ArgList &args,
clang::DiagnosticsEngine &diags) {
unsigned numErrorsBefore = diags.getNumErrors();
// -fdefault* family
if (args.hasArg(clang::driver::options::OPT_fdefault_real_8)) {
res.getDefaultKinds().set_defaultRealKind(8);
res.getDefaultKinds().set_doublePrecisionKind(16);
}
if (args.hasArg(clang::driver::options::OPT_fdefault_integer_8)) {
res.getDefaultKinds().set_defaultIntegerKind(8);
res.getDefaultKinds().set_subscriptIntegerKind(8);
res.getDefaultKinds().set_sizeIntegerKind(8);
res.getDefaultKinds().set_defaultLogicalKind(8);
}
if (args.hasArg(clang::driver::options::OPT_fdefault_double_8)) {
if (!args.hasArg(clang::driver::options::OPT_fdefault_real_8)) {
// -fdefault-double-8 has to be used with -fdefault-real-8
// to be compatible with gfortran
const unsigned diagID = diags.getCustomDiagID(
clang::DiagnosticsEngine::Error,
"Use of `-fdefault-double-8` requires `-fdefault-real-8`");
diags.Report(diagID);
}
// https://gcc.gnu.org/onlinedocs/gfortran/Fortran-Dialect-Options.html
res.getDefaultKinds().set_doublePrecisionKind(8);
}
if (args.hasArg(clang::driver::options::OPT_flarge_sizes))
res.getDefaultKinds().set_sizeIntegerKind(8);
// -x cuda
auto language = args.getLastArgValue(clang::driver::options::OPT_x);
if (language == "cuda") {
res.getFrontendOpts().features.Enable(
Fortran::common::LanguageFeature::CUDA);
}
// -fopenmp and -fopenacc
if (args.hasArg(clang::driver::options::OPT_fopenacc)) {
res.getFrontendOpts().features.Enable(
Fortran::common::LanguageFeature::OpenACC);
}
if (args.hasArg(clang::driver::options::OPT_fopenmp)) {
// By default OpenMP is set to 1.1 version
res.getLangOpts().OpenMPVersion = 11;
res.getFrontendOpts().features.Enable(
Fortran::common::LanguageFeature::OpenMP);
if (int Version = getLastArgIntValue(
args, clang::driver::options::OPT_fopenmp_version_EQ,
res.getLangOpts().OpenMPVersion, diags)) {
res.getLangOpts().OpenMPVersion = Version;
}
if (args.hasArg(clang::driver::options::OPT_fopenmp_force_usm)) {
res.getLangOpts().OpenMPForceUSM = 1;
}
if (args.hasArg(clang::driver::options::OPT_fopenmp_is_target_device)) {
res.getLangOpts().OpenMPIsTargetDevice = 1;
// Get OpenMP host file path if any and report if a non existent file is
// found
if (auto *arg = args.getLastArg(
clang::driver::options::OPT_fopenmp_host_ir_file_path)) {
res.getLangOpts().OMPHostIRFile = arg->getValue();
if (!llvm::sys::fs::exists(res.getLangOpts().OMPHostIRFile))
diags.Report(clang::diag::err_drv_omp_host_ir_file_not_found)
<< res.getLangOpts().OMPHostIRFile;
}
if (args.hasFlag(
clang::driver::options::OPT_fopenmp_assume_teams_oversubscription,
clang::driver::options::
OPT_fno_openmp_assume_teams_oversubscription,
/*Default=*/false))
res.getLangOpts().OpenMPTeamSubscription = true;
if (args.hasArg(
clang::driver::options::OPT_fopenmp_assume_no_thread_state))
res.getLangOpts().OpenMPNoThreadState = 1;
if (args.hasArg(
clang::driver::options::OPT_fopenmp_assume_no_nested_parallelism))
res.getLangOpts().OpenMPNoNestedParallelism = 1;
if (args.hasFlag(clang::driver::options::
OPT_fopenmp_assume_threads_oversubscription,
clang::driver::options::
OPT_fno_openmp_assume_threads_oversubscription,
/*Default=*/false))
res.getLangOpts().OpenMPThreadSubscription = true;
if ((args.hasArg(clang::driver::options::OPT_fopenmp_target_debug) ||
args.hasArg(clang::driver::options::OPT_fopenmp_target_debug_EQ))) {
res.getLangOpts().OpenMPTargetDebug = getLastArgIntValue(
args, clang::driver::options::OPT_fopenmp_target_debug_EQ,
res.getLangOpts().OpenMPTargetDebug, diags);
if (!res.getLangOpts().OpenMPTargetDebug &&
args.hasArg(clang::driver::options::OPT_fopenmp_target_debug))
res.getLangOpts().OpenMPTargetDebug = 1;
}
if (args.hasArg(clang::driver::options::OPT_nogpulib))
res.getLangOpts().NoGPULib = 1;
}
switch (llvm::Triple(res.getTargetOpts().triple).getArch()) {
case llvm::Triple::nvptx:
case llvm::Triple::nvptx64:
case llvm::Triple::amdgcn:
if (!res.getLangOpts().OpenMPIsTargetDevice) {
const unsigned diagID = diags.getCustomDiagID(
clang::DiagnosticsEngine::Error,
"OpenMP AMDGPU/NVPTX is only prepared to deal with device code.");
diags.Report(diagID);
}
res.getLangOpts().OpenMPIsGPU = 1;
break;
default:
res.getLangOpts().OpenMPIsGPU = 0;
break;
}
}
// -pedantic
if (args.hasArg(clang::driver::options::OPT_pedantic)) {
res.setEnableConformanceChecks();
res.setEnableUsageChecks();
}
// -w
if (args.hasArg(clang::driver::options::OPT_w))
res.setDisableWarnings();
// -std=f2018
// TODO: Set proper options when more fortran standards
// are supported.
if (args.hasArg(clang::driver::options::OPT_std_EQ)) {
auto standard = args.getLastArgValue(clang::driver::options::OPT_std_EQ);
// We only allow f2018 as the given standard
if (standard == "f2018") {
res.setEnableConformanceChecks();
} else {
const unsigned diagID =
diags.getCustomDiagID(clang::DiagnosticsEngine::Error,
"Only -std=f2018 is allowed currently.");
diags.Report(diagID);
}
}
return diags.getNumErrors() == numErrorsBefore;
}
/// Parses all floating point related arguments and populates the
/// CompilerInvocation accordingly.
/// Returns false if new errors are generated.
///
/// \param [out] invoc Stores the processed arguments
/// \param [in] args The compiler invocation arguments to parse
/// \param [out] diags DiagnosticsEngine to report erros with
static bool parseFloatingPointArgs(CompilerInvocation &invoc,
llvm::opt::ArgList &args,
clang::DiagnosticsEngine &diags) {
LangOptions &opts = invoc.getLangOpts();
if (const llvm::opt::Arg *a =
args.getLastArg(clang::driver::options::OPT_ffp_contract)) {
const llvm::StringRef val = a->getValue();
enum LangOptions::FPModeKind fpContractMode;
if (val == "off")
fpContractMode = LangOptions::FPM_Off;
else if (val == "fast")
fpContractMode = LangOptions::FPM_Fast;
else {
diags.Report(clang::diag::err_drv_unsupported_option_argument)
<< a->getSpelling() << val;
return false;
}
opts.setFPContractMode(fpContractMode);
}
if (args.getLastArg(clang::driver::options::OPT_menable_no_infinities)) {
opts.NoHonorInfs = true;
}
if (args.getLastArg(clang::driver::options::OPT_menable_no_nans)) {
opts.NoHonorNaNs = true;
}
if (args.getLastArg(clang::driver::options::OPT_fapprox_func)) {
opts.ApproxFunc = true;
}
if (args.getLastArg(clang::driver::options::OPT_fno_signed_zeros)) {
opts.NoSignedZeros = true;
}
if (args.getLastArg(clang::driver::options::OPT_mreassociate)) {
opts.AssociativeMath = true;
}
if (args.getLastArg(clang::driver::options::OPT_freciprocal_math)) {
opts.ReciprocalMath = true;
}
if (args.getLastArg(clang::driver::options::OPT_ffast_math)) {
opts.NoHonorInfs = true;
opts.NoHonorNaNs = true;
opts.AssociativeMath = true;
opts.ReciprocalMath = true;
opts.ApproxFunc = true;
opts.NoSignedZeros = true;
opts.setFPContractMode(LangOptions::FPM_Fast);
}
return true;
}
/// Parses vscale range options and populates the CompilerInvocation
/// accordingly.
/// Returns false if new errors are generated.
///
/// \param [out] invoc Stores the processed arguments
/// \param [in] args The compiler invocation arguments to parse
/// \param [out] diags DiagnosticsEngine to report erros with
static bool parseVScaleArgs(CompilerInvocation &invoc, llvm::opt::ArgList &args,
clang::DiagnosticsEngine &diags) {
const auto *vscaleMin =
args.getLastArg(clang::driver::options::OPT_mvscale_min_EQ);
const auto *vscaleMax =
args.getLastArg(clang::driver::options::OPT_mvscale_max_EQ);
if (!vscaleMin && !vscaleMax)
return true;
llvm::Triple triple = llvm::Triple(invoc.getTargetOpts().triple);
if (!triple.isAArch64() && !triple.isRISCV()) {
const unsigned diagID =
diags.getCustomDiagID(clang::DiagnosticsEngine::Error,
"`-mvscale-max` and `-mvscale-min` are not "
"supported for this architecture: %0");
diags.Report(diagID) << triple.getArchName();
return false;
}
LangOptions &opts = invoc.getLangOpts();
if (vscaleMin) {
llvm::StringRef argValue = llvm::StringRef(vscaleMin->getValue());
unsigned vscaleMinVal;
if (argValue.getAsInteger(/*Radix=*/10, vscaleMinVal)) {
diags.Report(clang::diag::err_drv_unsupported_option_argument)
<< vscaleMax->getSpelling() << argValue;
return false;
}
opts.VScaleMin = vscaleMinVal;
}
if (vscaleMax) {
llvm::StringRef argValue = llvm::StringRef(vscaleMax->getValue());
unsigned vscaleMaxVal;
if (argValue.getAsInteger(/*Radix=w*/ 10, vscaleMaxVal)) {
diags.Report(clang::diag::err_drv_unsupported_option_argument)
<< vscaleMax->getSpelling() << argValue;
return false;
}
opts.VScaleMax = vscaleMaxVal;
}
return true;
}
static bool parseLinkerOptionsArgs(CompilerInvocation &invoc,
llvm::opt::ArgList &args,
clang::DiagnosticsEngine &diags) {
llvm::Triple triple = llvm::Triple(invoc.getTargetOpts().triple);
// TODO: support --dependent-lib on other platforms when MLIR supports
// !llvm.dependent.lib
if (args.hasArg(clang::driver::options::OPT_dependent_lib) &&
!triple.isOSWindows()) {
const unsigned diagID =
diags.getCustomDiagID(clang::DiagnosticsEngine::Error,
"--dependent-lib is only supported on Windows");
diags.Report(diagID);
return false;
}
invoc.getCodeGenOpts().DependentLibs =
args.getAllArgValues(clang::driver::options::OPT_dependent_lib);
return true;
}
bool CompilerInvocation::createFromArgs(
CompilerInvocation &invoc, llvm::ArrayRef<const char *> commandLineArgs,
clang::DiagnosticsEngine &diags, const char *argv0) {
bool success = true;
// Set the default triple for this CompilerInvocation. This might be
// overridden by users with `-triple` (see the call to `ParseTargetArgs`
// below).
// NOTE: Like in Clang, it would be nice to use option marshalling
// for this so that the entire logic for setting-up the triple is in one
// place.
invoc.getTargetOpts().triple =
llvm::Triple::normalize(llvm::sys::getDefaultTargetTriple());
// Parse the arguments
const llvm::opt::OptTable &opts = clang::driver::getDriverOptTable();
llvm::opt::Visibility visibilityMask(clang::driver::options::FC1Option);
unsigned missingArgIndex, missingArgCount;
llvm::opt::InputArgList args = opts.ParseArgs(
commandLineArgs, missingArgIndex, missingArgCount, visibilityMask);
// Check for missing argument error.
if (missingArgCount) {
diags.Report(clang::diag::err_drv_missing_argument)
<< args.getArgString(missingArgIndex) << missingArgCount;
success = false;
}
// Issue errors on unknown arguments
for (const auto *a : args.filtered(clang::driver::options::OPT_UNKNOWN)) {
auto argString = a->getAsString(args);
std::string nearest;
if (opts.findNearest(argString, nearest, visibilityMask) > 1)
diags.Report(clang::diag::err_drv_unknown_argument) << argString;
else
diags.Report(clang::diag::err_drv_unknown_argument_with_suggestion)
<< argString << nearest;
success = false;
}
// -flang-experimental-hlfir
if (args.hasArg(clang::driver::options::OPT_flang_experimental_hlfir) ||
args.hasArg(clang::driver::options::OPT_emit_hlfir)) {
invoc.loweringOpts.setLowerToHighLevelFIR(true);
}
// -flang-deprecated-no-hlfir
if (args.hasArg(clang::driver::options::OPT_flang_deprecated_no_hlfir) &&
!args.hasArg(clang::driver::options::OPT_emit_hlfir)) {
if (args.hasArg(clang::driver::options::OPT_flang_experimental_hlfir)) {
const unsigned diagID = diags.getCustomDiagID(
clang::DiagnosticsEngine::Error,
"Options '-flang-experimental-hlfir' and "
"'-flang-deprecated-no-hlfir' cannot be both specified");
diags.Report(diagID);
}
invoc.loweringOpts.setLowerToHighLevelFIR(false);
}
// -fno-ppc-native-vector-element-order
if (args.hasArg(clang::driver::options::OPT_fno_ppc_native_vec_elem_order)) {
invoc.loweringOpts.setNoPPCNativeVecElemOrder(true);
}
// -flang-experimental-integer-overflow
if (args.hasArg(
clang::driver::options::OPT_flang_experimental_integer_overflow)) {
invoc.loweringOpts.setNSWOnLoopVarInc(true);
}
// Preserve all the remark options requested, i.e. -Rpass, -Rpass-missed or
// -Rpass-analysis. This will be used later when processing and outputting the
// remarks generated by LLVM in ExecuteCompilerInvocation.cpp.
for (auto *a : args.filtered(clang::driver::options::OPT_R_Group)) {
if (a->getOption().matches(clang::driver::options::OPT_R_value_Group))
// This is -Rfoo=, where foo is the name of the diagnostic
// group. Add only the remark option name to the diagnostics. e.g. for
// -Rpass= we will add the string "pass".
invoc.getDiagnosticOpts().Remarks.push_back(
std::string(a->getOption().getName().drop_front(1).rtrim("=-")));
else
// If no regex was provided, add the provided value, e.g. for -Rpass add
// the string "pass".
invoc.getDiagnosticOpts().Remarks.push_back(a->getValue());
}
success &= parseFrontendArgs(invoc.getFrontendOpts(), args, diags);
parseTargetArgs(invoc.getTargetOpts(), args);
parsePreprocessorArgs(invoc.getPreprocessorOpts(), args);
parseCodeGenArgs(invoc.getCodeGenOpts(), args, diags);
success &= parseDebugArgs(invoc.getCodeGenOpts(), args, diags);
success &= parseVectorLibArg(invoc.getCodeGenOpts(), args, diags);
success &= parseSemaArgs(invoc, args, diags);
success &= parseDialectArgs(invoc, args, diags);
success &= parseDiagArgs(invoc, args, diags);
// Collect LLVM (-mllvm) and MLIR (-mmlir) options.
// NOTE: Try to avoid adding any options directly to `llvmArgs` or
// `mlirArgs`. Instead, you can use
// * `-mllvm <your-llvm-option>`, or
// * `-mmlir <your-mlir-option>`.
invoc.frontendOpts.llvmArgs =
args.getAllArgValues(clang::driver::options::OPT_mllvm);
invoc.frontendOpts.mlirArgs =
args.getAllArgValues(clang::driver::options::OPT_mmlir);
success &= parseFloatingPointArgs(invoc, args, diags);
success &= parseVScaleArgs(invoc, args, diags);
success &= parseLinkerOptionsArgs(invoc, args, diags);
// Set the string to be used as the return value of the COMPILER_OPTIONS
// intrinsic of iso_fortran_env. This is either passed in from the parent
// compiler driver invocation with an environment variable, or failing that
// set to the command line arguments of the frontend driver invocation.
invoc.allCompilerInvocOpts = std::string();
llvm::raw_string_ostream os(invoc.allCompilerInvocOpts);
char *compilerOptsEnv = std::getenv("FLANG_COMPILER_OPTIONS_STRING");
if (compilerOptsEnv != nullptr) {
os << compilerOptsEnv;
} else {
os << argv0 << ' ';
for (auto it = commandLineArgs.begin(), e = commandLineArgs.end(); it != e;
++it) {
os << ' ' << *it;
}
}
invoc.setArgv0(argv0);
return success;
}
void CompilerInvocation::collectMacroDefinitions() {
auto &ppOpts = this->getPreprocessorOpts();
for (unsigned i = 0, n = ppOpts.macros.size(); i != n; ++i) {
llvm::StringRef macro = ppOpts.macros[i].first;
bool isUndef = ppOpts.macros[i].second;
std::pair<llvm::StringRef, llvm::StringRef> macroPair = macro.split('=');
llvm::StringRef macroName = macroPair.first;
llvm::StringRef macroBody = macroPair.second;
// For an #undef'd macro, we only care about the name.
if (isUndef) {
parserOpts.predefinitions.emplace_back(macroName.str(),
std::optional<std::string>{});
continue;
}
// For a #define'd macro, figure out the actual definition.
if (macroName.size() == macro.size())
macroBody = "1";
else {
// Note: GCC drops anything following an end-of-line character.
llvm::StringRef::size_type end = macroBody.find_first_of("\n\r");
macroBody = macroBody.substr(0, end);
}
parserOpts.predefinitions.emplace_back(
macroName, std::optional<std::string>(macroBody.str()));
}
}
void CompilerInvocation::setDefaultFortranOpts() {
auto &fortranOptions = getFortranOpts();
std::vector<std::string> searchDirectories{"."s};
fortranOptions.searchDirectories = searchDirectories;
// Add the location of omp_lib.h to the search directories. Currently this is
// identical to the modules' directory.
fortranOptions.searchDirectories.emplace_back(
getOpenMPHeadersDir(getArgv0()));
fortranOptions.isFixedForm = false;
}
// TODO: When expanding this method, consider creating a dedicated API for
// this. Also at some point we will need to differentiate between different
// targets and add dedicated predefines for each.
void CompilerInvocation::setDefaultPredefinitions() {
auto &fortranOptions = getFortranOpts();
const auto &frontendOptions = getFrontendOpts();
// Populate the macro list with version numbers and other predefinitions.
fortranOptions.predefinitions.emplace_back("__flang__", "1");
fortranOptions.predefinitions.emplace_back("__flang_major__",
FLANG_VERSION_MAJOR_STRING);
fortranOptions.predefinitions.emplace_back("__flang_minor__",
FLANG_VERSION_MINOR_STRING);
fortranOptions.predefinitions.emplace_back("__flang_patchlevel__",
FLANG_VERSION_PATCHLEVEL_STRING);
// Add predefinitions based on extensions enabled
if (frontendOptions.features.IsEnabled(
Fortran::common::LanguageFeature::OpenACC)) {
fortranOptions.predefinitions.emplace_back("_OPENACC", "202211");
}
if (frontendOptions.features.IsEnabled(
Fortran::common::LanguageFeature::OpenMP)) {
Fortran::common::setOpenMPMacro(getLangOpts().OpenMPVersion,
fortranOptions.predefinitions);
}
llvm::Triple targetTriple{llvm::Triple(this->targetOpts.triple)};
if (targetTriple.isPPC()) {
// '__powerpc__' is a generic macro for any PowerPC cases. e.g. Max integer
// size.
fortranOptions.predefinitions.emplace_back("__powerpc__", "1");
}
if (targetTriple.isOSLinux()) {
fortranOptions.predefinitions.emplace_back("__linux__", "1");
}
switch (targetTriple.getArch()) {
default:
break;
case llvm::Triple::ArchType::x86_64:
fortranOptions.predefinitions.emplace_back("__x86_64__", "1");
fortranOptions.predefinitions.emplace_back("__x86_64", "1");
break;
}
}
void CompilerInvocation::setFortranOpts() {
auto &fortranOptions = getFortranOpts();
const auto &frontendOptions = getFrontendOpts();
const auto &preprocessorOptions = getPreprocessorOpts();
auto &moduleDirJ = getModuleDir();
if (frontendOptions.fortranForm != FortranForm::Unknown) {
fortranOptions.isFixedForm =
frontendOptions.fortranForm == FortranForm::FixedForm;
}
fortranOptions.fixedFormColumns = frontendOptions.fixedFormColumns;
fortranOptions.features = frontendOptions.features;
fortranOptions.encoding = frontendOptions.encoding;
// Adding search directories specified by -I
fortranOptions.searchDirectories.insert(
fortranOptions.searchDirectories.end(),
preprocessorOptions.searchDirectoriesFromDashI.begin(),
preprocessorOptions.searchDirectoriesFromDashI.end());
// Add the ordered list of -intrinsic-modules-path
fortranOptions.searchDirectories.insert(
fortranOptions.searchDirectories.end(),
preprocessorOptions.searchDirectoriesFromIntrModPath.begin(),
preprocessorOptions.searchDirectoriesFromIntrModPath.end());
// Add the default intrinsic module directory
fortranOptions.intrinsicModuleDirectories.emplace_back(
getIntrinsicDir(getArgv0()));
// Add the directory supplied through -J/-module-dir to the list of search
// directories
if (moduleDirJ != ".")
fortranOptions.searchDirectories.emplace_back(moduleDirJ);
if (frontendOptions.instrumentedParse)
fortranOptions.instrumentedParse = true;
if (frontendOptions.showColors)
fortranOptions.showColors = true;
if (frontendOptions.needProvenanceRangeToCharBlockMappings)
fortranOptions.needProvenanceRangeToCharBlockMappings = true;
if (getEnableConformanceChecks())
fortranOptions.features.WarnOnAllNonstandard();
if (getEnableUsageChecks())
fortranOptions.features.WarnOnAllUsage();
if (getDisableWarnings()) {
fortranOptions.features.DisableAllNonstandardWarnings();
fortranOptions.features.DisableAllUsageWarnings();
}
}
std::unique_ptr<Fortran::semantics::SemanticsContext>
CompilerInvocation::getSemanticsCtx(
Fortran::parser::AllCookedSources &allCookedSources,
const llvm::TargetMachine &targetMachine) {
auto &fortranOptions = getFortranOpts();
auto semanticsContext = std::make_unique<semantics::SemanticsContext>(
getDefaultKinds(), fortranOptions.features, allCookedSources);
semanticsContext->set_moduleDirectory(getModuleDir())
.set_searchDirectories(fortranOptions.searchDirectories)
.set_intrinsicModuleDirectories(fortranOptions.intrinsicModuleDirectories)
.set_warningsAreErrors(getWarnAsErr())
.set_moduleFileSuffix(getModuleFileSuffix())
.set_underscoring(getCodeGenOpts().Underscoring);
std::string compilerVersion = Fortran::common::getFlangFullVersion();
Fortran::tools::setUpTargetCharacteristics(
semanticsContext->targetCharacteristics(), targetMachine, compilerVersion,
allCompilerInvocOpts);
return semanticsContext;
}
/// Set \p loweringOptions controlling lowering behavior based
/// on the \p optimizationLevel.
void CompilerInvocation::setLoweringOptions() {
const CodeGenOptions &codegenOpts = getCodeGenOpts();
// Lower TRANSPOSE as a runtime call under -O0.
loweringOpts.setOptimizeTranspose(codegenOpts.OptimizationLevel > 0);
loweringOpts.setUnderscoring(codegenOpts.Underscoring);
const LangOptions &langOptions = getLangOpts();
Fortran::common::MathOptionsBase &mathOpts = loweringOpts.getMathOptions();
// TODO: when LangOptions are finalized, we can represent
// the math related options using Fortran::commmon::MathOptionsBase,
// so that we can just copy it into LoweringOptions.
mathOpts
.setFPContractEnabled(langOptions.getFPContractMode() ==
LangOptions::FPM_Fast)
.setNoHonorInfs(langOptions.NoHonorInfs)
.setNoHonorNaNs(langOptions.NoHonorNaNs)
.setApproxFunc(langOptions.ApproxFunc)
.setNoSignedZeros(langOptions.NoSignedZeros)
.setAssociativeMath(langOptions.AssociativeMath)
.setReciprocalMath(langOptions.ReciprocalMath);
}