| //===- 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/Frontend/CodeGenOptions.h" | |
| #include "flang/Frontend/PreprocessorOptions.h" | |
| #include "flang/Frontend/TargetOptions.h" | |
| #include "flang/Optimizer/Passes/CommandLineOpts.h" | |
| #include "flang/Semantics/semantics.h" | |
| #include "flang/Support/Fortran-features.h" | |
| #include "flang/Support/OpenMP-features.h" | |
| #include "flang/Support/Version.h" | |
| #include "flang/Tools/TargetSetup.h" | |
| #include "flang/Version.inc" | |
| #include "clang/Basic/DiagnosticDriver.h" | |
| #include "clang/Basic/DiagnosticFrontend.h" | |
| #include "clang/Basic/DiagnosticOptions.h" | |
| #include "clang/Driver/CommonArgs.h" | |
| #include "clang/Driver/Driver.h" | |
| #include "clang/Options/OptionUtils.h" | |
| #include "clang/Options/Options.h" | |
| #include "llvm/ADT/ArrayRef.h" | |
| #include "llvm/ADT/StringRef.h" | |
| #include "llvm/ADT/StringSwitch.h" | |
| #include "llvm/Frontend/Debug/Options.h" | |
| #include "llvm/Frontend/Driver/CodeGenOptions.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/Path.h" | |
| #include "llvm/Support/Process.h" | |
| #include "llvm/Support/VirtualFileSystem.h" | |
| #include "llvm/Support/raw_ostream.h" | |
| #include "llvm/TargetParser/Host.h" | |
| #include "llvm/TargetParser/Triple.h" | |
| #include <algorithm> | |
| #include <cstdlib> | |
| #include <limits> | |
| #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` but default to off in the frontend driver | |
| // `flang -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::options::OPT_fcolor_diagnostics)) { | |
| showColors = Colors_On; | |
| } else if (opt.matches(clang::options::OPT_fno_color_diagnostics)) { | |
| showColors = Colors_Off; | |
| } else if (opt.matches(clang::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::options::OPT_O_Group)) { | |
| if (a->getOption().matches(clang::options::OPT_O0)) | |
| return 0; | |
| assert(a->getOption().matches(clang::options::OPT_O)); | |
| return getLastArgIntValue(args, clang::options::OPT_O, defaultOpt, diags); | |
| } | |
| return defaultOpt; | |
| } | |
| bool Fortran::frontend::parseDiagnosticArgs(clang::DiagnosticOptions &opts, | |
| llvm::opt::ArgList &args) { | |
| opts.setShowColors(parseShowColorsArgs(args) ? clang::ShowColorsKind::On | |
| : clang::ShowColorsKind::Off); | |
| 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::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::DebugDirectivesOnly && | |
| 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(); | |
| } | |
| opts.DwarfVersion = | |
| getLastArgIntValue(args, clang::options::OPT_dwarf_version_EQ, | |
| /*Default=*/0, diags); | |
| if (const llvm::opt::Arg *a = | |
| args.getLastArg(clang::options::OPT_split_dwarf_file)) | |
| opts.SplitDwarfFile = a->getValue(); | |
| if (const llvm::opt::Arg *a = | |
| args.getLastArg(clang::options::OPT_split_dwarf_output)) | |
| opts.SplitDwarfOutput = a->getValue(); | |
| } | |
| if (const llvm::opt::Arg *arg = | |
| args.getLastArg(clang::options::OPT_dwarf_debug_flags)) | |
| opts.DwarfDebugFlags = arg->getValue(); | |
| opts.DebugInfoForProfiling = | |
| args.hasArg(clang::options::OPT_fdebug_info_for_profiling); | |
| if (const llvm::opt::Arg *a = | |
| args.getLastArg(clang::options::OPT_compress_debug_sections_EQ)) { | |
| auto type = llvm::StringSwitch<std::optional<llvm::DebugCompressionType>>( | |
| a->getValue()) | |
| .Case("none", llvm::DebugCompressionType::None) | |
| .Case("zlib", llvm::DebugCompressionType::Zlib) | |
| .Case("zstd", llvm::DebugCompressionType::Zstd) | |
| .Default(std::nullopt); | |
| if (type) { | |
| opts.setCompressDebugSections(*type); | |
| } else { | |
| diags.Report(clang::diag::err_drv_invalid_value) | |
| << a->getAsString(args) << a->getValue(); | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| static bool parseDoConcurrentMapping(Fortran::frontend::CodeGenOptions &opts, | |
| llvm::opt::ArgList &args, | |
| clang::DiagnosticsEngine &diags) { | |
| llvm::opt::Arg *arg = | |
| args.getLastArg(clang::options::OPT_fdo_concurrent_to_openmp_EQ); | |
| if (!arg) | |
| return true; | |
| using DoConcurrentMappingKind = | |
| Fortran::frontend::CodeGenOptions::DoConcurrentMappingKind; | |
| std::optional<DoConcurrentMappingKind> val = | |
| llvm::StringSwitch<std::optional<DoConcurrentMappingKind>>( | |
| arg->getValue()) | |
| .Case("none", DoConcurrentMappingKind::DCMK_None) | |
| .Case("host", DoConcurrentMappingKind::DCMK_Host) | |
| .Case("device", DoConcurrentMappingKind::DCMK_Device) | |
| .Default(std::nullopt); | |
| if (!val.has_value()) { | |
| diags.Report(clang::diag::err_drv_invalid_value) | |
| << arg->getAsString(args) << arg->getValue(); | |
| return false; | |
| } | |
| opts.setDoConcurrentMapping(val.value()); | |
| return true; | |
| } | |
| static bool parseVectorLibArg(Fortran::frontend::CodeGenOptions &opts, | |
| llvm::opt::ArgList &args, | |
| clang::DiagnosticsEngine &diags) { | |
| llvm::opt::Arg *arg = args.getLastArg(clang::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("AMDLIBM", VectorLibrary::AMDLIBM) | |
| .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::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::options::OPT_fdebug_pass_manager, | |
| clang::options::OPT_fno_debug_pass_manager, false)) | |
| opts.DebugPassManager = 1; | |
| if (!args.hasFlag(clang::options::OPT_fprotect_parens, | |
| clang::options::OPT_fno_protect_parens, true)) | |
| opts.ProtectParens = 0; | |
| if (args.hasFlag(clang::options::OPT_fstack_arrays, | |
| clang::options::OPT_fno_stack_arrays, false)) | |
| opts.StackArrays = 1; | |
| if (args.hasFlag(clang::options::OPT_fsafe_trampoline, | |
| clang::options::OPT_fno_safe_trampoline, false)) | |
| opts.EnableSafeTrampoline = 1; | |
| if (args.hasFlag(clang::options::OPT_ffp_sum_reassociation, | |
| clang::options::OPT_fno_fp_sum_reassociation, true)) | |
| opts.SplitSumExpressionTree = 1; | |
| // Match the LLVM pipeline default (PipelineTuningOptions::LoopInterchange), | |
| // which enables the pass whenever the optimization pipeline runs. | |
| opts.InterchangeLoops = | |
| args.hasFlag(clang::options::OPT_floop_interchange, | |
| clang::options::OPT_fno_loop_interchange, true); | |
| if (args.hasArg(clang::options::OPT_funique_internal_linkage_names)) | |
| opts.UniqueInternalLinkageNames = 1; | |
| if (args.getLastArg(clang::options::OPT_fexperimental_loop_fusion)) | |
| opts.FuseLoops = 1; | |
| if (args.getLastArg(clang::options::OPT_vectorize_loops)) | |
| opts.VectorizeLoop = 1; | |
| if (args.getLastArg(clang::options::OPT_vectorize_slp)) | |
| opts.VectorizeSLP = 1; | |
| if (args.hasFlag(clang::options::OPT_floop_versioning, | |
| clang::options::OPT_fno_loop_versioning, false)) | |
| opts.LoopVersioning = 1; | |
| opts.UnrollLoops = args.hasFlag(clang::options::OPT_funroll_loops, | |
| clang::options::OPT_fno_unroll_loops, | |
| (opts.OptimizationLevel > 1)); | |
| opts.AliasAnalysis = opts.OptimizationLevel > 0; | |
| // -mframe-pointer=none/non-leaf/reserved/all option. | |
| if (const llvm::opt::Arg *a = | |
| args.getLastArg(clang::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("non-leaf-no-reserve", | |
| llvm::FramePointerKind::NonLeafNoReserve) | |
| .Case("reserved", llvm::FramePointerKind::Reserved) | |
| .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::options::OPT_fpass_plugin_EQ)) | |
| opts.LLVMPassPlugins.push_back(a->getValue()); | |
| opts.Reciprocals = clang::parseMRecipOption(diags, args); | |
| opts.PreferVectorWidth = clang::parseMPreferVectorWidthOption(diags, args); | |
| // -fembed-offload-object option | |
| for (auto *a : args.filtered(clang::options::OPT_fembed_offload_object_EQ)) | |
| opts.OffloadObjects.push_back(a->getValue()); | |
| if (args.hasArg(clang::options::OPT_finstrument_functions)) | |
| opts.InstrumentFunctions = 1; | |
| if (args.hasArg(clang::options::OPT_fno_optimize_sibling_calls)) | |
| opts.DisableTailCalls = 1; | |
| // -fno-integrated-as: emit GNU Assembler compatible assembly. | |
| if (!args.hasFlag(clang::options::OPT_fintegrated_as, | |
| clang::options::OPT_fno_integrated_as, true)) | |
| opts.DisableIntegratedAS = 1; | |
| opts.FunctionSections = | |
| args.hasFlag(clang::options::OPT_ffunction_sections, | |
| clang::options::OPT_fno_function_sections, | |
| /*Default=*/false); | |
| opts.DataSections = args.hasFlag(clang::options::OPT_fdata_sections, | |
| clang::options::OPT_fno_data_sections, | |
| /*Default=*/false); | |
| if (const llvm::opt::Arg *a = | |
| args.getLastArg(clang::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::options::OPT_opt_record_file)) | |
| opts.OptRecordFile = a->getValue(); | |
| // Optimization file format. Defaults to yaml | |
| if (const llvm::opt::Arg *a = | |
| args.getLastArg(clang::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::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::options::OPT_Rpass_EQ, | |
| /*remarkOptName=*/"pass"); | |
| // Create OptRemark that allows all missed optimization passes to be printed. | |
| opts.OptimizationRemarkMissed = | |
| parseOptimizationRemark(diags, args, clang::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::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::options::OPT_save_temps_EQ)) | |
| opts.SaveTempsDir = a->getValue(); | |
| // -record-command-line option. | |
| if (const llvm::opt::Arg *a = | |
| args.getLastArg(clang::options::OPT_record_command_line)) { | |
| opts.RecordCommandLine = a->getValue(); | |
| } | |
| // -mlink-builtin-bitcode | |
| for (auto *a : args.filtered(clang::options::OPT_mlink_builtin_bitcode)) | |
| opts.BuiltinBCLibs.push_back(a->getValue()); | |
| // -mrelocation-model option. | |
| if (const llvm::opt::Arg *a = | |
| args.getLastArg(clang::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::options::OPT_pic_level, 0, diags)) { | |
| if (picLevel > 2) | |
| diags.Report(clang::diag::err_drv_invalid_value) | |
| << args.getLastArg(clang::options::OPT_pic_level)->getAsString(args) | |
| << picLevel; | |
| opts.PICLevel = picLevel; | |
| if (args.hasArg(clang::options::OPT_pic_is_pie)) | |
| opts.IsPIE = 1; | |
| } | |
| if (const llvm::opt::Arg *a = | |
| args.getLastArg(clang::options::OPT_fprofile_generate, | |
| clang::options::OPT_fprofile_generate_EQ)) { | |
| opts.setProfileInstr(llvm::driver::ProfileInstrKind::ProfileIRInstr); | |
| if (a->getOption().matches(clang::options::OPT_fprofile_generate_EQ)) { | |
| llvm::SmallString<128> path(a->getValue()); | |
| llvm::sys::path::append(path, "default_%m.profraw"); | |
| opts.InstrProfileOutput = std::string(path); | |
| } | |
| } | |
| if (auto A = args.getLastArg(clang::options::OPT_fprofile_use_EQ)) { | |
| opts.setProfileUse(llvm::driver::ProfileInstrKind::ProfileIRInstr); | |
| opts.ProfileInstrumentUsePath = A->getValue(); | |
| } | |
| opts.SampleProfileFile = | |
| args.getLastArgValue(clang::options::OPT_fprofile_sample_use_EQ); | |
| if (args.hasFlag(clang::options::OPT_fpseudo_probe_for_profiling, | |
| clang::options::OPT_fno_pseudo_probe_for_profiling, false)) { | |
| opts.PseudoProbeForProfiling = 1; | |
| } | |
| // -mcmodel option. | |
| if (const llvm::opt::Arg *a = | |
| args.getLastArg(clang::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::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::options::OPT_fno_underscoring, | |
| clang::options::OPT_funderscoring, false)) { | |
| opts.Underscoring = 0; | |
| } | |
| if (const llvm::opt::Arg *arg = | |
| args.getLastArg(clang::options::OPT_complex_range_EQ)) { | |
| llvm::StringRef argValue = llvm::StringRef(arg->getValue()); | |
| if (argValue == "full") { | |
| opts.setComplexRange(CodeGenOptions::ComplexRangeKind::CX_Full); | |
| } else if (argValue == "improved") { | |
| opts.setComplexRange(CodeGenOptions::ComplexRangeKind::CX_Improved); | |
| } else if (argValue == "basic") { | |
| opts.setComplexRange(CodeGenOptions::ComplexRangeKind::CX_Basic); | |
| } else { | |
| diags.Report(clang::diag::err_drv_invalid_value) | |
| << arg->getAsString(args) << arg->getValue(); | |
| } | |
| } | |
| } | |
| /// 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::options::OPT_triple)) | |
| opts.triple = a->getValue(); | |
| opts.atomicIgnoreDenormalMode = | |
| args.hasFlag(clang::options::OPT_fatomic_ignore_denormal_mode, | |
| clang::options::OPT_fno_atomic_ignore_denormal_mode, false); | |
| opts.atomicFineGrainedMemory = | |
| args.hasFlag(clang::options::OPT_fatomic_fine_grained_memory, | |
| clang::options::OPT_fno_atomic_fine_grained_memory, false); | |
| opts.atomicRemoteMemory = | |
| args.hasFlag(clang::options::OPT_fatomic_remote_memory, | |
| clang::options::OPT_fno_atomic_remote_memory, false); | |
| if (const llvm::opt::Arg *a = args.getLastArg(clang::options::OPT_target_cpu)) | |
| opts.cpu = a->getValue(); | |
| if (const llvm::opt::Arg *a = args.getLastArg(clang::options::OPT_tune_cpu)) | |
| opts.cpuToTuneFor = a->getValue(); | |
| for (const llvm::opt::Arg *currentArg : | |
| args.filtered(clang::options::OPT_target_feature)) | |
| opts.featuresAsWritten.emplace_back(currentArg->getValue()); | |
| if (args.hasArg(clang::options::OPT_fdisable_real_10)) | |
| opts.disabledRealKinds.push_back(10); | |
| if (args.hasArg(clang::options::OPT_fdisable_real_3)) | |
| opts.disabledRealKinds.push_back(3); | |
| if (args.hasArg(clang::options::OPT_fdisable_integer_2)) | |
| opts.disabledIntegerKinds.push_back(2); | |
| if (args.hasArg(clang::options::OPT_fdisable_integer_16)) | |
| opts.disabledIntegerKinds.push_back(16); | |
| if (const llvm::opt::Arg *a = args.getLastArg(clang::options::OPT_mabi_EQ)) { | |
| llvm::StringRef V = a->getValue(); | |
| // Normalize "vec-default" to an empty ABI name; the AIX extended Altivec | |
| // ABI is carried to the backend as the "vec-extabi" target-abi module flag. | |
| opts.abi = V == "vec-default" ? "" : V.str(); | |
| } | |
| opts.SplitMachineFunctions = | |
| args.hasArg(clang::options::OPT_fsplit_machine_functions); | |
| opts.asmVerbose = args.hasFlag(clang::options::OPT_fverbose_asm, | |
| clang::options::OPT_fno_verbose_asm, false); | |
| } | |
| // 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) { | |
| // 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::options::OPT_Action_Group)) { | |
| llvm::SmallString<32> buf; | |
| llvm::raw_svector_ostream os(buf); | |
| for (const llvm::opt::Arg *arg : | |
| args.filtered(clang::options::OPT_Action_Group)) { | |
| if (buf.size()) | |
| os << ", "; | |
| os << "'" << arg->getSpelling() << "'"; | |
| } | |
| diags.Report(clang::diag::err_drv_too_many_actions) << buf; | |
| return false; | |
| } | |
| // Identify the action (i.e. opts.ProgramAction) | |
| if (const llvm::opt::Arg *a = | |
| args.getLastArg(clang::options::OPT_Action_Group)) { | |
| switch (a->getOption().getID()) { | |
| default: { | |
| llvm_unreachable("Invalid option in group!"); | |
| } | |
| case clang::options::OPT_test_io: | |
| opts.programAction = InputOutputTest; | |
| break; | |
| case clang::options::OPT_E: | |
| opts.programAction = PrintPreprocessedInput; | |
| break; | |
| case clang::options::OPT_fsyntax_only: | |
| opts.programAction = ParseSyntaxOnly; | |
| break; | |
| case clang::options::OPT_emit_fir: | |
| opts.programAction = EmitFIR; | |
| break; | |
| case clang::options::OPT_emit_hlfir: | |
| opts.programAction = EmitHLFIR; | |
| break; | |
| case clang::options::OPT_emit_llvm: | |
| opts.programAction = EmitLLVM; | |
| break; | |
| case clang::options::OPT_emit_llvm_bc: | |
| opts.programAction = EmitLLVMBitcode; | |
| break; | |
| case clang::options::OPT_emit_obj: | |
| opts.programAction = EmitObj; | |
| break; | |
| case clang::options::OPT_S: | |
| opts.programAction = EmitAssembly; | |
| break; | |
| case clang::options::OPT_fdebug_unparse: | |
| opts.programAction = DebugUnparse; | |
| break; | |
| case clang::options::OPT_fdebug_unparse_no_sema: | |
| opts.programAction = DebugUnparseNoSema; | |
| break; | |
| case clang::options::OPT_fdebug_unparse_with_symbols: | |
| opts.programAction = DebugUnparseWithSymbols; | |
| break; | |
| case clang::options::OPT_fdebug_unparse_with_modules: | |
| opts.programAction = DebugUnparseWithModules; | |
| break; | |
| case clang::options::OPT_fdebug_dump_symbols: | |
| opts.programAction = DebugDumpSymbols; | |
| break; | |
| case clang::options::OPT_fdebug_dump_parse_tree: | |
| opts.programAction = DebugDumpParseTree; | |
| break; | |
| case clang::options::OPT_fdebug_dump_pft: | |
| opts.programAction = DebugDumpPFT; | |
| break; | |
| case clang::options::OPT_fdebug_dump_all: | |
| opts.programAction = DebugDumpAll; | |
| break; | |
| case clang::options::OPT_fdebug_dump_parse_tree_no_sema: | |
| opts.programAction = DebugDumpParseTreeNoSema; | |
| break; | |
| case clang::options::OPT_fdebug_dump_provenance: | |
| opts.programAction = DebugDumpProvenance; | |
| break; | |
| case clang::options::OPT_fdebug_dump_parsing_log: | |
| opts.programAction = DebugDumpParsingLog; | |
| break; | |
| case clang::options::OPT_fdebug_measure_parse_tree: | |
| opts.programAction = DebugMeasureParseTree; | |
| break; | |
| case clang::options::OPT_fdebug_pre_fir_tree: | |
| opts.programAction = DebugPreFIRTree; | |
| break; | |
| case clang::options::OPT_fget_symbols_sources: | |
| opts.programAction = GetSymbolsSources; | |
| break; | |
| case clang::options::OPT_fget_definition: | |
| opts.programAction = GetDefinition; | |
| break; | |
| case clang::options::OPT_init_only: | |
| opts.programAction = InitOnly; | |
| break; | |
| // TODO: | |
| // case clang::options::OPT_emit_llvm: | |
| // case clang::options::OPT_emit_llvm_only: | |
| // case clang::options::OPT_emit_codegen_only: | |
| // case clang::options::OPT_emit_module: | |
| // (...) | |
| } | |
| // Parse the values provided with `-fget-definition` (there should be 3 | |
| // integers) | |
| if (llvm::opt::OptSpecifier(a->getOption().getID()) == | |
| clang::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::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::options::OPT_plugin)) { | |
| opts.programAction = PluginAction; | |
| opts.actionName = a->getValue(); | |
| } | |
| opts.outputFile = args.getLastArgValue(clang::options::OPT_o); | |
| opts.dependencyOutputFile = | |
| args.getLastArgValue(clang::options::OPT_dependency_file); | |
| opts.dependencyTargets = args.getAllArgValues(clang::options::OPT_MT); | |
| opts.showHelp = args.hasArg(clang::options::OPT_help); | |
| opts.showVersion = args.hasArg(clang::options::OPT_version); | |
| opts.printSupportedCPUs = | |
| args.hasArg(clang::options::OPT_print_supported_cpus); | |
| // Get the input kind (from the value passed via `-x`) | |
| InputKind dashX(Language::Unknown); | |
| if (const llvm::opt::Arg *a = args.getLastArg(clang::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::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::options::OPT_ffixed_form, | |
| clang::options::OPT_ffree_form)) { | |
| opts.fortranForm = arg->getOption().matches(clang::options::OPT_ffixed_form) | |
| ? FortranForm::FixedForm | |
| : FortranForm::FreeForm; | |
| } | |
| // Set fixedFormColumns based on -ffixed-line-length=<value> | |
| if (const auto *arg = | |
| args.getLastArg(clang::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 = std::nullopt; | |
| } else if (columns < 7) { | |
| diags.Report(clang::diag::err_drv_small_columns) | |
| << arg->getOption().getName() << arg->getValue() << "7"; | |
| } else if (columns > std::numeric_limits<int>::max()) { | |
| diags.Report(clang::diag::err_drv_large_columns) | |
| << arg->getOption().getName() << arg->getValue() | |
| << std::numeric_limits<int>::max(); | |
| } else { | |
| opts.fixedFormColumns = columns; | |
| } | |
| } | |
| // Set conversion based on -fconvert=<value> | |
| if (const auto *arg = args.getLastArg(clang::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::options::OPT_fimplicit_none, | |
| clang::options::OPT_fno_implicit_none, | |
| false)); | |
| // -f{no-}implicit-none-ext | |
| opts.features.Enable(Fortran::common::LanguageFeature::ImplicitNoneExternal, | |
| args.hasFlag(clang::options::OPT_fimplicit_none_ext, | |
| clang::options::OPT_fno_implicit_none_ext, | |
| false)); | |
| // -f{no-}implicit-module-prefix | |
| opts.features.Enable( | |
| Fortran::common::LanguageFeature::ImplicitModulePrefix, | |
| args.hasFlag(clang::options::OPT_fimplicit_module_prefix, | |
| clang::options::OPT_fno_implicit_module_prefix, false)); | |
| // -f{no-}backslash | |
| opts.features.Enable(Fortran::common::LanguageFeature::BackslashEscapes, | |
| args.hasFlag(clang::options::OPT_fbackslash, | |
| clang::options::OPT_fno_backslash, false)); | |
| // -f{no-}logical-abbreviations | |
| opts.features.Enable( | |
| Fortran::common::LanguageFeature::LogicalAbbreviations, | |
| args.hasFlag(clang::options::OPT_flogical_abbreviations, | |
| clang::options::OPT_fno_logical_abbreviations, false)); | |
| // -f{no-}unsigned | |
| opts.features.Enable(Fortran::common::LanguageFeature::Unsigned, | |
| args.hasFlag(clang::options::OPT_funsigned, | |
| clang::options::OPT_fno_unsigned, false)); | |
| // -f{no-}out-of-bounds-subscripts | |
| opts.features.Enable( | |
| Fortran::common::LanguageFeature::OutOfBoundsSubscripts, | |
| args.hasFlag(clang::options::OPT_fout_of_bounds_subscripts, | |
| clang::options::OPT_fno_out_of_bounds_subscripts, true)); | |
| // -f{no-}enumeration-type (experimental; FIR lowering is incomplete) | |
| opts.features.Enable(Fortran::common::LanguageFeature::EnumerationType, | |
| args.hasFlag(clang::options::OPT_fenumeration_type, | |
| clang::options::OPT_fno_enumeration_type, | |
| false)); | |
| // -frelaxed-c-loc-checks | |
| if (args.hasArg(clang::options::OPT_relaxed_c_loc)) { | |
| opts.features.Enable(Fortran::common::LanguageFeature::RelaxedCLocChecks); | |
| } | |
| // -f{no-}openacc-default-none-scalars-strict | |
| opts.features.Enable( | |
| Fortran::common::LanguageFeature::OpenAccDefaultNoneScalarsStrict, | |
| args.hasFlag(clang::options::OPT_fopenacc_default_none_scalars_strict, | |
| clang::options::OPT_fno_openacc_default_none_scalars_strict, | |
| false)); | |
| // -f{no-}openacc-multiple-names-in-routine | |
| opts.features.Enable( | |
| Fortran::common::LanguageFeature::OpenACCMultipleNamesInRoutine, | |
| args.hasFlag(clang::options::OPT_fopenacc_multiple_names_in_routine, | |
| clang::options::OPT_fno_openacc_multiple_names_in_routine, | |
| true)); | |
| // -f{no-}prefer-intrinsic-module-use-association | |
| if (const auto *arg = args.getLastArg( | |
| clang::options::OPT_fprefer_intrinsic_module_use_association, | |
| clang::options::OPT_fno_prefer_intrinsic_module_use_association)) { | |
| opts.features.Enable( | |
| Fortran::common::LanguageFeature::PreferIntrinsicModuleUseAssociation, | |
| arg->getOption().matches( | |
| clang::options::OPT_fprefer_intrinsic_module_use_association)); | |
| } | |
| // -f{no-}xor-operator | |
| opts.features.Enable(Fortran::common::LanguageFeature::XOROperator, | |
| args.hasFlag(clang::options::OPT_fxor_operator, | |
| clang::options::OPT_fno_xor_operator, | |
| false)); | |
| // -fno-automatic | |
| if (args.hasArg(clang::options::OPT_fno_automatic)) { | |
| opts.features.Enable(Fortran::common::LanguageFeature::DefaultSave); | |
| } | |
| // -f{no}-save-main-program | |
| opts.features.Enable(Fortran::common::LanguageFeature::SaveMainProgram, | |
| args.hasFlag(clang::options::OPT_fsave_main_program, | |
| clang::options::OPT_fno_save_main_program, | |
| false)); | |
| if (args.hasArg(clang::options::OPT_falternative_parameter_statement)) { | |
| opts.features.Enable(Fortran::common::LanguageFeature::OldStyleParameter); | |
| } | |
| if (const llvm::opt::Arg *arg = | |
| args.getLastArg(clang::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.hasUncompilableErrorOccurred(); | |
| } | |
| // 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); | |
| } | |
| /// 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::options::OPT_D, clang::options::OPT_U)) { | |
| if (currentArg->getOption().matches(clang::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::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::options::OPT_fintrinsic_modules_path)) | |
| opts.searchDirectoriesFromIntrModPath.emplace_back(currentArg->getValue()); | |
| // -cpp/-nocpp | |
| if (const auto *currentArg = | |
| args.getLastArg(clang::options::OPT_cpp, clang::options::OPT_nocpp)) | |
| opts.macrosFlag = (currentArg->getOption().matches(clang::options::OPT_cpp)) | |
| ? PPMacrosFlag::Include | |
| : PPMacrosFlag::Exclude; | |
| // Enable -cpp based on -x unless explicitly disabled with -nocpp | |
| if (opts.macrosFlag != PPMacrosFlag::Exclude) | |
| if (const auto *dashX = args.getLastArg(clang::options::OPT_x)) | |
| opts.macrosFlag = llvm::StringSwitch<PPMacrosFlag>(dashX->getValue()) | |
| .Case("f95-cpp-input", PPMacrosFlag::Include) | |
| .Default(opts.macrosFlag); | |
| opts.noReformat = args.hasArg(clang::options::OPT_fno_reformat); | |
| opts.preprocessIncludeLines = | |
| args.hasArg(clang::options::OPT_fpreprocess_include_lines); | |
| opts.noLineDirectives = args.hasArg(clang::options::OPT_P); | |
| opts.showMacros = args.hasArg(clang::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) { | |
| // -J/module-dir option | |
| std::vector<std::string> moduleDirList = | |
| args.getAllArgValues(clang::options::OPT_module_dir); | |
| // User can only specify one -J/-module-dir directory, but may repeat | |
| // -J/-module-dir as long as the directory is the same each time. | |
| // https://gcc.gnu.org/onlinedocs/gfortran/Directory-Options.html | |
| std::sort(moduleDirList.begin(), moduleDirList.end()); | |
| moduleDirList.erase(std::unique(moduleDirList.begin(), moduleDirList.end()), | |
| moduleDirList.end()); | |
| if (moduleDirList.size() > 1) { | |
| const unsigned diagID = | |
| diags.getCustomDiagID(clang::DiagnosticsEngine::Error, | |
| "Only one '-module-dir/-J' directory allowed. " | |
| "'-module-dir/-J' may be given multiple times " | |
| "but the directory must be the same each time."); | |
| diags.Report(diagID); | |
| } | |
| if (moduleDirList.size() == 1) | |
| res.setModuleDir(moduleDirList[0]); | |
| // -fdebug-module-writer option | |
| if (args.hasArg(clang::options::OPT_fdebug_module_writer)) { | |
| res.setDebugModuleDir(true); | |
| } | |
| // -fhermetic-module-files option | |
| if (args.hasArg(clang::options::OPT_fhermetic_module_files)) { | |
| res.setHermeticModuleFileOutput(true); | |
| } | |
| // -module-suffix | |
| if (const auto *moduleSuffix = | |
| args.getLastArg(clang::options::OPT_module_suffix)) { | |
| res.setModuleFileSuffix(moduleSuffix->getValue()); | |
| } | |
| // -f{no-}analyzed-objects-for-unparse | |
| res.setUseAnalyzedObjectsForUnparse( | |
| args.hasFlag(clang::options::OPT_fanalyzed_objects_for_unparse, | |
| clang::options::OPT_fno_analyzed_objects_for_unparse, true)); | |
| return !diags.hasUncompilableErrorOccurred(); | |
| } | |
| /// Parses all diagnostics related arguments and populates the variables | |
| /// options accordingly. Returns false if new errors are generated. | |
| /// FC1 driver entry point for parsing diagnostic arguments. | |
| static bool parseDiagArgs(CompilerInvocation &res, llvm::opt::ArgList &args, | |
| clang::DiagnosticsEngine &diags) { | |
| auto &features{res.getFrontendOpts().features}; | |
| // The order of these flags (-pedantic -W<feature> -w) is important and is | |
| // chosen to match clang's behavior. | |
| // -pedantic | |
| if (args.hasArg(clang::options::OPT_pedantic)) { | |
| features.WarnOnAllNonstandard(); | |
| features.WarnOnAllUsage(); | |
| res.setEnableConformanceChecks(); | |
| res.setEnableUsageChecks(); | |
| } | |
| // -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::options::OPT_W_Joined)) { | |
| const auto &wArgs = args.getAllArgValues(clang::options::OPT_W_Joined); | |
| // TODO: Consider using std::string_view instead of llvm::StringRef | |
| // when moving to C++20: | |
| for (const llvm::StringRef wArg : wArgs) { | |
| if (wArg == "error") { | |
| res.setWarnAsErr(true); | |
| // -Wfatal-errors | |
| } else if (wArg == "fatal-errors") { | |
| res.setMaxErrors(1); | |
| // -W[no-]<feature> | |
| } else if (features.EnableWarning(wArg)) { | |
| if (auto canonical{features.CheckDeprecatedSpelling(wArg)}) { | |
| std::string suggestion{*canonical}; | |
| if (wArg.starts_with("no-")) { | |
| suggestion = "no-" + suggestion; | |
| } | |
| const unsigned diagID = | |
| diags.getCustomDiagID(clang::DiagnosticsEngine::Warning, | |
| "-W%0 is deprecated; use -W%1 instead"); | |
| diags.Report(diagID) << wArg << suggestion; | |
| } | |
| } else { | |
| const unsigned diagID = diags.getCustomDiagID( | |
| clang::DiagnosticsEngine::Error, "Unknown diagnostic option: -W%0"); | |
| diags.Report(diagID) << wArg; | |
| } | |
| } | |
| } | |
| // -w | |
| if (args.hasArg(clang::options::OPT_w)) { | |
| features.DisableAllWarnings(); | |
| res.setDisableWarnings(); | |
| } | |
| // Default to off for `flang -fc1`. | |
| bool showColors{parseShowColorsArgs(args, false)}; | |
| auto colorsMode = | |
| showColors ? clang::ShowColorsKind::On : clang::ShowColorsKind::Off; | |
| diags.getDiagnosticOptions().setShowColors(colorsMode); | |
| res.getDiagnosticOpts().setShowColors(colorsMode); | |
| res.getFrontendOpts().showColors = showColors; | |
| return !diags.hasUncompilableErrorOccurred(); | |
| } | |
| /// 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) { | |
| // -fd-lines-as-code | |
| if (args.hasArg(clang::options::OPT_fd_lines_as_code)) { | |
| if (res.getFrontendOpts().fortranForm == FortranForm::FreeForm) { | |
| const unsigned fdLinesAsWarning = diags.getCustomDiagID( | |
| clang::DiagnosticsEngine::Warning, | |
| "‘-fd-lines-as-code’ has no effect in free form."); | |
| diags.Report(fdLinesAsWarning); | |
| } else { | |
| res.getFrontendOpts().features.Enable( | |
| Fortran::common::LanguageFeature::OldDebugLines, true); | |
| } | |
| } | |
| // -fd-lines-as-comments | |
| if (args.hasArg(clang::options::OPT_fd_lines_as_comments)) { | |
| if (res.getFrontendOpts().fortranForm == FortranForm::FreeForm) { | |
| const unsigned fdLinesAsWarning = diags.getCustomDiagID( | |
| clang::DiagnosticsEngine::Warning, | |
| "‘-fd-lines-as-comments’ has no effect in free form."); | |
| diags.Report(fdLinesAsWarning); | |
| } else { | |
| res.getFrontendOpts().features.Enable( | |
| Fortran::common::LanguageFeature::OldDebugLines, false); | |
| } | |
| } | |
| // -fdefault* family | |
| if (const llvm::opt::Arg *arg = | |
| args.getLastArg(clang::options::OPT_fdefault_real_8, | |
| clang::options::OPT_fdefault_real_4)) { | |
| const llvm::opt::Option &opt = arg->getOption(); | |
| if (opt.matches(clang::options::OPT_fdefault_real_8)) { | |
| res.getDefaultKinds().set_defaultRealKind(8); | |
| res.getDefaultKinds().set_doublePrecisionKind(16); | |
| } else if (opt.matches(clang::options::OPT_fdefault_real_4)) { | |
| res.getDefaultKinds().set_defaultRealKind(4); | |
| res.getDefaultKinds().set_doublePrecisionKind(8); | |
| } | |
| } | |
| if (const llvm::opt::Arg *arg = | |
| args.getLastArg(clang::options::OPT_fdefault_integer_8, | |
| clang::options::OPT_fdefault_integer_4)) { | |
| const llvm::opt::Option &opt = arg->getOption(); | |
| if (opt.matches(clang::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); | |
| } else if (opt.matches(clang::options::OPT_fdefault_integer_4)) { | |
| // Note that the subscript integer kind is set to 8 here. If a | |
| // default-integer-kind is not provided, it is also set to 8. | |
| res.getDefaultKinds().set_defaultIntegerKind(4); | |
| res.getDefaultKinds().set_subscriptIntegerKind(8); | |
| res.getDefaultKinds().set_sizeIntegerKind(4); | |
| res.getDefaultKinds().set_defaultLogicalKind(4); | |
| } | |
| } | |
| if (args.hasArg(clang::options::OPT_fdefault_double_8)) { | |
| if (!args.hasArg(clang::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::options::OPT_flarge_sizes)) | |
| res.getDefaultKinds().set_sizeIntegerKind(8); | |
| // -x cuda | |
| auto language = args.getLastArgValue(clang::options::OPT_x); | |
| if (language == "cuda") { | |
| res.getFrontendOpts().features.Enable( | |
| Fortran::common::LanguageFeature::CUDA); | |
| } | |
| // -fopenacc | |
| if (args.hasArg(clang::options::OPT_fopenacc)) { | |
| res.getFrontendOpts().features.Enable( | |
| Fortran::common::LanguageFeature::OpenACC); | |
| } | |
| // -std=f2018 | |
| // TODO: Set proper options when more fortran standards | |
| // are supported. | |
| if (args.hasArg(clang::options::OPT_std_EQ)) { | |
| auto standard = args.getLastArgValue(clang::options::OPT_std_EQ); | |
| // We only allow f2018 as the given standard | |
| if (standard == "f2018") { | |
| res.setEnableConformanceChecks(); | |
| res.getFrontendOpts().features.WarnOnAllNonstandard(); | |
| } else { | |
| const unsigned diagID = | |
| diags.getCustomDiagID(clang::DiagnosticsEngine::Error, | |
| "Only -std=f2018 is allowed currently."); | |
| diags.Report(diagID); | |
| } | |
| } | |
| // -fcoarray | |
| if (args.hasArg(clang::options::OPT_fcoarray)) { | |
| res.getFrontendOpts().features.Enable( | |
| Fortran::common::LanguageFeature::Coarray); | |
| const unsigned diagID = | |
| diags.getCustomDiagID(clang::DiagnosticsEngine::Warning, | |
| "Support for multi image Fortran features is " | |
| "still experimental and in development."); | |
| diags.Report(diagID); | |
| } | |
| return !diags.hasUncompilableErrorOccurred(); | |
| } | |
| /// Parses all OpenMP related arguments if the -fopenmp option is present, | |
| /// populating the \c res object accordingly. Returns false if new errors are | |
| /// generated. | |
| static bool parseOpenMPArgs(CompilerInvocation &res, llvm::opt::ArgList &args, | |
| clang::DiagnosticsEngine &diags) { | |
| llvm::opt::Arg *arg = args.getLastArg(clang::options::OPT_fopenmp, | |
| clang::options::OPT_fno_openmp); | |
| if (!arg || arg->getOption().matches(clang::options::OPT_fno_openmp)) { | |
| bool isSimdSpecified = | |
| args.hasFlag(clang::options::OPT_fopenmp_simd, | |
| clang::options::OPT_fno_openmp_simd, /*Default=*/false); | |
| if (!isSimdSpecified) | |
| return true; | |
| res.getLangOpts().OpenMPSimd = 1; | |
| } | |
| llvm::Triple t(res.getTargetOpts().triple); | |
| constexpr unsigned newestFullySupported = 31; | |
| constexpr unsigned latestFinalized = 60; | |
| // By default OpenMP is set to the most recent fully supported version | |
| res.getLangOpts().OpenMPVersion = newestFullySupported; | |
| res.getFrontendOpts().features.Enable( | |
| Fortran::common::LanguageFeature::OpenMP); | |
| if (auto *arg = args.getLastArg(clang::options::OPT_fopenmp_version_EQ)) { | |
| llvm::SmallVector<unsigned> ompVersions; | |
| for (llvm::omp::Version v : llvm::omp::getOpenMPVersions()) { | |
| ompVersions.push_back(static_cast<unsigned>(v)); | |
| } | |
| unsigned oldVersions[] = {11, 20, 25, 30}; | |
| unsigned version = 0; | |
| auto reportBadVersion = [&](llvm::StringRef value) { | |
| const unsigned diagID = | |
| diags.getCustomDiagID(clang::DiagnosticsEngine::Error, | |
| "'%0' is not a valid OpenMP version in '%1', " | |
| "valid versions are %2"); | |
| std::string buffer; | |
| llvm::raw_string_ostream versions(buffer); | |
| llvm::interleaveComma(ompVersions, versions); | |
| diags.Report(diagID) << value << arg->getAsString(args) << versions.str(); | |
| }; | |
| llvm::StringRef value = arg->getValue(); | |
| if (!value.getAsInteger(/*radix=*/10, version)) { | |
| if (llvm::is_contained(ompVersions, version)) { | |
| res.getLangOpts().OpenMPVersion = version; | |
| if (version > latestFinalized) | |
| diags.Report(clang::diag::warn_openmp_spec_incomplete) << version; | |
| else if (version > newestFullySupported) | |
| diags.Report(clang::diag::warn_openmp_impl_incomplete) << version; | |
| } else if (llvm::is_contained(oldVersions, version)) { | |
| const unsigned diagID = | |
| diags.getCustomDiagID(clang::DiagnosticsEngine::Warning, | |
| "OpenMP version %0 is no longer supported, " | |
| "assuming version %1"); | |
| std::string assumed = std::to_string(res.getLangOpts().OpenMPVersion); | |
| diags.Report(diagID) << value << assumed; | |
| } else { | |
| reportBadVersion(value); | |
| } | |
| } else { | |
| reportBadVersion(value); | |
| } | |
| } | |
| if (args.hasFlag(clang::options::OPT_fopenmp_assume_teams_oversubscription, | |
| clang::options::OPT_fno_openmp_assume_teams_oversubscription, | |
| /*Default=*/false)) | |
| res.getLangOpts().OpenMPTeamSubscription = true; | |
| if (args.hasFlag( | |
| clang::options::OPT_fopenmp_assume_threads_oversubscription, | |
| clang::options::OPT_fno_openmp_assume_threads_oversubscription, | |
| /*Default=*/false)) | |
| res.getLangOpts().OpenMPThreadSubscription = true; | |
| if (args.hasArg(clang::options::OPT_fopenmp_force_usm)) { | |
| res.getLangOpts().OpenMPForceUSM = 1; | |
| } | |
| if (args.hasArg(clang::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::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_omp_host_ir_file_not_found) | |
| << res.getLangOpts().OMPHostIRFile; | |
| } | |
| if (args.hasArg(clang::options::OPT_fopenmp_assume_no_thread_state)) | |
| res.getLangOpts().OpenMPNoThreadState = 1; | |
| if (args.hasArg(clang::options::OPT_fopenmp_assume_no_nested_parallelism)) | |
| res.getLangOpts().OpenMPNoNestedParallelism = 1; | |
| if ((args.hasArg(clang::options::OPT_fopenmp_target_debug) || | |
| args.hasArg(clang::options::OPT_fopenmp_target_debug_EQ))) { | |
| res.getLangOpts().OpenMPTargetDebug = | |
| getLastArgIntValue(args, clang::options::OPT_fopenmp_target_debug_EQ, | |
| res.getLangOpts().OpenMPTargetDebug, diags); | |
| if (!res.getLangOpts().OpenMPTargetDebug && | |
| args.hasArg(clang::options::OPT_fopenmp_target_debug)) | |
| res.getLangOpts().OpenMPTargetDebug = 1; | |
| } | |
| if (args.hasArg(clang::options::OPT_no_offloadlib)) | |
| res.getLangOpts().NoGPULib = 1; | |
| } | |
| if (llvm::Triple(res.getTargetOpts().triple).isGPU()) { | |
| if (!res.getLangOpts().OpenMPIsTargetDevice) { | |
| const unsigned diagID = diags.getCustomDiagID( | |
| clang::DiagnosticsEngine::Error, | |
| "OpenMP GPU is only prepared to deal with device code."); | |
| diags.Report(diagID); | |
| } | |
| res.getLangOpts().OpenMPIsGPU = 1; | |
| } else { | |
| res.getLangOpts().OpenMPIsGPU = 0; | |
| } | |
| // Get the OpenMP target triples if any. | |
| if (auto *arg = args.getLastArg(clang::options::OPT_offload_targets_EQ)) { | |
| enum ArchPtrSize { Arch16Bit, Arch32Bit, Arch64Bit }; | |
| auto getArchPtrSize = [](const llvm::Triple &triple) { | |
| if (triple.isArch16Bit()) | |
| return Arch16Bit; | |
| if (triple.isArch32Bit()) | |
| return Arch32Bit; | |
| assert(triple.isArch64Bit() && "Expected 64-bit architecture"); | |
| return Arch64Bit; | |
| }; | |
| for (unsigned i = 0; i < arg->getNumValues(); ++i) { | |
| llvm::Triple tt(arg->getValue(i)); | |
| if (tt.getArch() == llvm::Triple::UnknownArch || | |
| !(tt.getArch() == llvm::Triple::aarch64 || tt.isPPC() || | |
| tt.getArch() == llvm::Triple::systemz || | |
| tt.getArch() == llvm::Triple::x86 || | |
| tt.getArch() == llvm::Triple::x86_64 || tt.isGPU())) | |
| diags.Report(clang::diag::err_drv_invalid_omp_target) | |
| << arg->getValue(i); | |
| else if (getArchPtrSize(t) != getArchPtrSize(tt)) | |
| diags.Report(clang::diag::err_drv_incompatible_omp_arch) | |
| << arg->getValue(i) << t.str(); | |
| else | |
| res.getLangOpts().OMPTargetTriples.push_back(tt); | |
| } | |
| } | |
| return !diags.hasUncompilableErrorOccurred(); | |
| } | |
| /// Parses signed integer overflow 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 parseIntegerOverflowArgs(CompilerInvocation &invoc, | |
| llvm::opt::ArgList &args, | |
| clang::DiagnosticsEngine &diags) { | |
| Fortran::common::LangOptions &opts = invoc.getLangOpts(); | |
| if (args.getLastArg(clang::options::OPT_fwrapv)) | |
| opts.setSignedOverflowBehavior(Fortran::common::LangOptions::SOB_Defined); | |
| return true; | |
| } | |
| /// Set the IEEE Floating point rounding modes, underflow mode and halting mode. | |
| /// | |
| /// Initial halting mode: | |
| /// -ffpe-trap= sets the initial floating-point exception halting mode for the | |
| /// main program. Only the last -ffpe-trap= on the command line is effective. | |
| /// The value is a comma-separated set of exception mnemonics: "invalid", | |
| /// "zero", "overflow", "underflow", and "inexact" correspond to the Fortran | |
| /// 2023 IEEE_FLAG_TYPE values (F2023 17.2), and "denormal" is a non-standard, | |
| /// gfortran-compatible extension. "none", as well as an empty list, requests no | |
| /// halting and resets any earlier request. | |
| /// | |
| /// TODO: | |
| /// Rounding modes | |
| /// Underflow mode | |
| /// | |
| /// The value and the target-support warnings are validated in the driver (see | |
| /// addIEEEFPModesOptions() in clang/lib/Driver/ToolChains/Flang.cpp); | |
| /// here we only translate the already-validated list, so an unrecognized | |
| /// mnemonic maps to 0 and is ignored rather than re-diagnosed. | |
| static void setIEEEFPModesArgs(Fortran::common::LangOptions &opts, | |
| llvm::opt::ArgList &args) { | |
| const llvm::opt::Arg *a = args.getLastArg(clang::options::OPT_ffpe_trap_EQ); | |
| if (!a) | |
| return; | |
| using LangOptions = Fortran::common::LangOptions; | |
| unsigned traps = 0; | |
| llvm::SmallVector<llvm::StringRef> trapList; | |
| llvm::StringRef(a->getValue()) | |
| .split(trapList, ',', /*MaxSplit=*/-1, /*KeepEmpty=*/false); | |
| for (llvm::StringRef trap : trapList) { | |
| if (trap == "none") { | |
| // Reset to no halting; a later mnemonic can re-enable. | |
| traps = 0; | |
| continue; | |
| } | |
| traps |= llvm::StringSwitch<unsigned>(trap) | |
| .Case("invalid", LangOptions::FPE_Invalid) | |
| .Case("denormal", LangOptions::FPE_Denormal) | |
| .Case("zero", LangOptions::FPE_DivByZero) | |
| .Case("overflow", LangOptions::FPE_Overflow) | |
| .Case("underflow", LangOptions::FPE_Underflow) | |
| .Case("inexact", LangOptions::FPE_Inexact) | |
| .Default(0); | |
| } | |
| opts.FPExceptionTraps = traps; | |
| } | |
| /// 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) { | |
| Fortran::common::LangOptions &opts = invoc.getLangOpts(); | |
| if (const llvm::opt::Arg *a = | |
| args.getLastArg(clang::options::OPT_ffp_contract)) { | |
| const llvm::StringRef val = a->getValue(); | |
| enum Fortran::common::LangOptions::FPModeKind fpContractMode; | |
| if (val == "off") | |
| fpContractMode = Fortran::common::LangOptions::FPM_Off; | |
| else if (val == "fast") | |
| fpContractMode = Fortran::common::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::options::OPT_menable_no_infs)) { | |
| opts.NoHonorInfs = true; | |
| } | |
| if (args.getLastArg(clang::options::OPT_menable_no_nans)) { | |
| opts.NoHonorNaNs = true; | |
| } | |
| if (args.getLastArg(clang::options::OPT_fapprox_func)) { | |
| opts.ApproxFunc = true; | |
| } | |
| if (args.getLastArg(clang::options::OPT_fno_signed_zeros)) { | |
| opts.NoSignedZeros = true; | |
| } | |
| if (args.getLastArg(clang::options::OPT_mreassociate)) { | |
| opts.AssociativeMath = true; | |
| } | |
| if (args.getLastArg(clang::options::OPT_freciprocal_math)) { | |
| opts.ReciprocalMath = true; | |
| } | |
| if (args.getLastArg(clang::options::OPT_ffast_math)) { | |
| opts.NoHonorInfs = true; | |
| opts.NoHonorNaNs = true; | |
| opts.AssociativeMath = true; | |
| opts.ReciprocalMath = true; | |
| opts.ApproxFunc = true; | |
| opts.NoSignedZeros = true; | |
| opts.FastRealMod = true; | |
| opts.setFPContractMode(Fortran::common::LangOptions::FPM_Fast); | |
| } | |
| if (llvm::opt::Arg *arg = | |
| args.getLastArg(clang::options::OPT_ffast_real_mod, | |
| clang::options::OPT_fno_fast_real_mod)) { | |
| if (arg->getOption().matches(clang::options::OPT_ffast_real_mod)) | |
| opts.FastRealMod = true; | |
| if (arg->getOption().matches(clang::options::OPT_fno_fast_real_mod)) | |
| opts.FastRealMod = false; | |
| } | |
| if (args.getLastArg(clang::options::OPT_fcheck_integer_mod_zero_divisor)) | |
| opts.CheckIntegerModZeroDivisor = true; | |
| // Set the initial IEEE floating point modes | |
| setIEEEFPModesArgs(opts, args); | |
| 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::options::OPT_mvscale_min_EQ); | |
| const auto *vscaleMax = args.getLastArg(clang::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; | |
| } | |
| Fortran::common::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); | |
| CodeGenOptions &opts = invoc.getCodeGenOpts(); | |
| // TODO: support --dependent-lib on other platforms when MLIR supports | |
| // !llvm.dependent.lib | |
| if (args.hasArg(clang::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; | |
| } | |
| opts.DependentLibs = args.getAllArgValues(clang::options::OPT_dependent_lib); | |
| // -flto=full/thin option. | |
| if (const llvm::opt::Arg *a = args.getLastArg(clang::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; | |
| } | |
| // -fsplit-lto-unit option | |
| if (args.hasArg(clang::options::OPT_fsplit_lto_unit)) | |
| opts.EnableSplitLTOUnit = true; | |
| // -ffat-lto-objects | |
| if (const llvm::opt::Arg *arg = | |
| args.getLastArg(clang::options::OPT_ffat_lto_objects, | |
| clang::options::OPT_fno_fat_lto_objects)) { | |
| opts.PrepareForFatLTO = | |
| arg->getOption().matches(clang::options::OPT_ffat_lto_objects); | |
| if (opts.PrepareForFatLTO) { | |
| assert((opts.PrepareForFullLTO || opts.PrepareForThinLTO) && | |
| "Unknown LTO mode"); | |
| if (!triple.isOSBinFormatELF()) | |
| diags.Report(clang::diag::err_drv_unsupported_opt_for_target) | |
| << arg->getAsString(args) << triple.getTriple(); | |
| } | |
| } | |
| return true; | |
| } | |
| static bool parseLangOptionsArgs(CompilerInvocation &invoc, | |
| llvm::opt::ArgList &args, | |
| clang::DiagnosticsEngine &diags) { | |
| bool success = true; | |
| success &= parseIntegerOverflowArgs(invoc, args, diags); | |
| success &= parseFloatingPointArgs(invoc, args, diags); | |
| success &= parseVScaleArgs(invoc, args, diags); | |
| return success; | |
| } | |
| // Copied from clang/lib/Frontend/CompilerInvocation.cpp. | |
| static void addDiagnosticArgs(llvm::opt::ArgList &args, | |
| llvm::opt::OptSpecifier group, | |
| llvm::opt::OptSpecifier groupWithValue, | |
| std::vector<std::string> &diagnostics) { | |
| for (auto *a : args.filtered(group)) { | |
| if (a->getOption().getKind() == llvm::opt::Option::FlagClass) { | |
| // The argument is a pure flag (such as OPT_Wall or OPT_Wdeprecated). Add | |
| // its name (minus the "W" or "R" at the beginning) to the diagnostics. | |
| diagnostics.push_back( | |
| std::string(a->getOption().getName().drop_front(1))); | |
| } else if (a->getOption().matches(groupWithValue)) { | |
| // This is -Wfoo= or -Rfoo=, where foo is the name of the diagnostic | |
| // group. Add only the group name to the diagnostics. | |
| diagnostics.push_back( | |
| std::string(a->getOption().getName().drop_front(1).rtrim("=-"))); | |
| } else { | |
| // Otherwise, add its value (for OPT_W_Joined and similar). | |
| diagnostics.push_back(a->getValue()); | |
| } | |
| } | |
| } | |
| bool CompilerInvocation::createFromArgs( | |
| CompilerInvocation &invoc, llvm::ArrayRef<const char *> commandLineArgs, | |
| clang::DiagnosticsEngine &diags, const char *argv0) { | |
| bool success = true; | |
| clang::DiagnosticOptions &diagOpts = diags.getDiagnosticOptions(); | |
| // 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::getDriverOptTable(); | |
| llvm::opt::Visibility visibilityMask(clang::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::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; | |
| } | |
| // Handle -Wno-<warning> flags. | |
| addDiagnosticArgs(args, clang::options::OPT_W_Group, | |
| clang::options::OPT_W_value_Group, diagOpts.Warnings); | |
| auto vfs = llvm::vfs::getRealFileSystem(); | |
| clang::ProcessWarningOptions(diags, diagOpts, *vfs, /*ReportDiags=*/false); | |
| // -fno-ppc-native-vector-element-order | |
| if (args.hasArg(clang::options::OPT_fno_ppc_native_vec_elem_order)) { | |
| invoc.loweringOpts.setNoPPCNativeVecElemOrder(true); | |
| } | |
| // -f[no-]init-global-zero | |
| if (args.hasFlag(clang::options::OPT_finit_global_zero, | |
| clang::options::OPT_fno_init_global_zero, | |
| /*default=*/true)) | |
| invoc.loweringOpts.setInitGlobalZero(true); | |
| else | |
| invoc.loweringOpts.setInitGlobalZero(false); | |
| // -finit-local=<zero|0x<hex>> and -finit-local-zero | |
| // (-finit-local-zero is an alias that the driver already expands to | |
| // -finit-local=zero, so we only need to handle OPT_finit_local_EQ here.) | |
| if (const llvm::opt::Arg *a = | |
| args.getLastArg(clang::options::OPT_finit_local_EQ)) { | |
| llvm::StringRef val = a->getValue(); | |
| if (val == "zero") { | |
| invoc.loweringOpts.setInitLocalMode(Fortran::lower::InitLocalKind::Zero); | |
| } else if (val.starts_with("0x") || val.starts_with("0X")) { | |
| unsigned long long hexVal = 0; | |
| if (val.drop_front(2).getAsInteger(16, hexVal) || hexVal > 0xFF) { | |
| diags.Report(clang::diag::err_drv_invalid_value) | |
| << a->getAsString(args) << val; | |
| } else { | |
| invoc.loweringOpts.setInitLocalMode(Fortran::lower::InitLocalKind::Hex); | |
| invoc.loweringOpts.setInitLocalPattern(static_cast<uint8_t>(hexVal)); | |
| } | |
| } else { | |
| diags.Report(clang::diag::err_drv_invalid_value) | |
| << a->getAsString(args) << val; | |
| } | |
| } | |
| // 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::options::OPT_R_Group)) { | |
| if (a->getOption().matches(clang::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()); | |
| } | |
| // -frealloc-lhs is the default. | |
| if (!args.hasFlag(clang::options::OPT_frealloc_lhs, | |
| clang::options::OPT_fno_realloc_lhs, true)) { | |
| invoc.loweringOpts.setReallocateLHS(false); | |
| invoc.getLangOpts().NoReallocateLHS = true; | |
| } | |
| invoc.loweringOpts.setRepackArrays(args.hasFlag( | |
| clang::options::OPT_frepack_arrays, clang::options::OPT_fno_repack_arrays, | |
| /*default=*/false)); | |
| invoc.loweringOpts.setStackRepackArrays( | |
| args.hasFlag(clang::options::OPT_fstack_repack_arrays, | |
| clang::options::OPT_fno_stack_repack_arrays, | |
| /*default=*/false)); | |
| if (auto *arg = | |
| args.getLastArg(clang::options::OPT_frepack_arrays_contiguity_EQ)) | |
| invoc.loweringOpts.setRepackArraysWhole(arg->getValue() == | |
| llvm::StringRef{"whole"}); | |
| // -f[no-]openacc-combined-loop-firstprivate | |
| invoc.loweringOpts.setOpenACCCombinedLoopFirstprivate( | |
| args.hasFlag(clang::options::OPT_fopenacc_combined_loop_firstprivate, | |
| clang::options::OPT_fno_openacc_combined_loop_firstprivate, | |
| /*default=*/true)); | |
| // -f[no-]openacc-acc-kernels-do-concurrent-independent | |
| invoc.loweringOpts.setOpenACCKernelsDoConcurrentIndependent(args.hasFlag( | |
| clang::options::OPT_fopenacc_acc_kernels_do_concurrent_independent, | |
| clang::options::OPT_fno_openacc_acc_kernels_do_concurrent_independent, | |
| /*default=*/true)); | |
| if (auto *arg = args.getLastArg(clang::options::OPT_ffp_maxmin_behavior_EQ)) { | |
| auto value = Fortran::common::parseFPMaxminBehavior(arg->getValue()); | |
| invoc.getCodeGenOpts().setFPMaxminBehavior(value); | |
| invoc.loweringOpts.setFPMaxminBehavior(value); | |
| } | |
| success &= parseFrontendArgs(invoc.getFrontendOpts(), args, diags); | |
| parseTargetArgs(invoc.getTargetOpts(), args); | |
| parsePreprocessorArgs(invoc.getPreprocessorOpts(), args); | |
| parseCodeGenArgs(invoc.getCodeGenOpts(), args, diags); | |
| success &= parseDoConcurrentMapping(invoc.getCodeGenOpts(), args, diags); | |
| success &= parseDebugArgs(invoc.getCodeGenOpts(), args, diags); | |
| // Enable USE statement preservation for debug info if debug level is above | |
| // LineTablesOnly. | |
| using DebugInfoKind = llvm::codegenoptions::DebugInfoKind; | |
| DebugInfoKind debugLevel = invoc.getCodeGenOpts().getDebugInfo(); | |
| invoc.loweringOpts.setPreserveUseDebugInfo( | |
| debugLevel > DebugInfoKind::DebugLineTablesOnly); | |
| success &= parseVectorLibArg(invoc.getCodeGenOpts(), args, diags); | |
| success &= parseSemaArgs(invoc, args, diags); | |
| success &= parseDialectArgs(invoc, args, diags); | |
| success &= parseOpenMPArgs(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::options::OPT_mllvm); | |
| invoc.frontendOpts.mlirArgs = args.getAllArgValues(clang::options::OPT_mmlir); | |
| if (args.hasArg(clang::options::OPT_foffload_device)) | |
| invoc.getLangOpts().OffloadDevice = 1; | |
| success &= parseLangOptionsArgs(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; | |
| } | |
| } | |
| // Process the timing-related options. | |
| if (args.hasArg(clang::options::OPT_ftime_report)) | |
| invoc.enableTimers = true; | |
| 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; | |
| 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 the relocation model | |
| if (unsigned PICLevel = getCodeGenOpts().PICLevel) { | |
| fortranOptions.predefinitions.emplace_back("__PIC__", | |
| std::to_string(PICLevel)); | |
| fortranOptions.predefinitions.emplace_back("__pic__", | |
| std::to_string(PICLevel)); | |
| if (getCodeGenOpts().IsPIE) { | |
| fortranOptions.predefinitions.emplace_back("__PIE__", | |
| std::to_string(PICLevel)); | |
| fortranOptions.predefinitions.emplace_back("__pie__", | |
| std::to_string(PICLevel)); | |
| } | |
| } | |
| // 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( | |
| static_cast<unsigned>(getLangOpts().getOpenMPVersion()), | |
| fortranOptions.predefinitions); | |
| } | |
| if (frontendOptions.features.IsEnabled( | |
| Fortran::common::LanguageFeature::CUDA)) { | |
| fortranOptions.predefinitions.emplace_back("_CUDA", "1"); | |
| } | |
| llvm::Triple targetTriple{llvm::Triple(this->targetOpts.triple)}; | |
| if (targetTriple.isOSLinux()) { | |
| fortranOptions.predefinitions.emplace_back("__linux__", "1"); | |
| } else if (targetTriple.isOSAIX()) { | |
| fortranOptions.predefinitions.emplace_back("_AIX", "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; | |
| case llvm::Triple::ArchType::ppc: | |
| case llvm::Triple::ArchType::ppc64: | |
| case llvm::Triple::ArchType::ppcle: | |
| case llvm::Triple::ArchType::ppc64le: | |
| // '__powerpc__' is a generic macro for any PowerPC. | |
| fortranOptions.predefinitions.emplace_back("__powerpc__", "1"); | |
| if (targetTriple.isOSAIX() && targetTriple.isArch64Bit()) { | |
| fortranOptions.predefinitions.emplace_back("__64BIT__", "1"); | |
| } | |
| break; | |
| case llvm::Triple::ArchType::aarch64: | |
| fortranOptions.predefinitions.emplace_back("__aarch64__", "1"); | |
| fortranOptions.predefinitions.emplace_back("__aarch64", "1"); | |
| break; | |
| case llvm::Triple::ArchType::systemz: | |
| fortranOptions.predefinitions.emplace_back("__s390x__", "1"); | |
| fortranOptions.predefinitions.emplace_back("__s390x", "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; | |
| // -E | |
| fortranOptions.prescanAndReformat = | |
| frontendOptions.programAction == PrintPreprocessedInput; | |
| 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 ordered list of -fintrinsic-modules-path | |
| fortranOptions.intrinsicModuleDirectories.insert( | |
| fortranOptions.intrinsicModuleDirectories.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; | |
| fortranOptions.features = frontendOptions.features; | |
| } | |
| 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, getLangOpts(), | |
| allCookedSources, getCodeGenOpts().getFPMaxminBehavior()); | |
| semanticsContext->set_moduleDirectory(getModuleDir()) | |
| .set_searchDirectories(fortranOptions.searchDirectories) | |
| .set_intrinsicModuleDirectories(fortranOptions.intrinsicModuleDirectories) | |
| .set_maxErrors(getMaxErrors()) | |
| .set_warningsAreErrors(getWarnAsErr()) | |
| .set_moduleFileSuffix(getModuleFileSuffix()) | |
| .set_underscoring(getCodeGenOpts().Underscoring) | |
| .set_openAccDefaultNoneScalarsStrictDisableOption( | |
| clang::getDriverOptTable() | |
| .getOptionPrefixedName( | |
| clang::options::OPT_fno_openacc_default_none_scalars_strict) | |
| .str()) | |
| .set_targetTriple(targetMachine.getTargetTriple().str()) | |
| .set_targetFeatures(targetMachine.getTargetFeatureString().str()); | |
| std::string compilerVersion = Fortran::common::getFlangFullVersion(); | |
| Fortran::tools::setUpTargetCharacteristics( | |
| semanticsContext->targetCharacteristics(), targetMachine, getTargetOpts(), | |
| 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); | |
| loweringOpts.setSkipExternalRttiDefinition(skipExternalRttiDefinition); | |
| const Fortran::common::LangOptions &langOptions = getLangOpts(); | |
| loweringOpts.setIntegerWrapAround(langOptions.getSignedOverflowBehavior() == | |
| Fortran::common::LangOptions::SOB_Defined); | |
| loweringOpts.setProtectParens(codegenOpts.ProtectParens); | |
| loweringOpts.setSplitSumExpressionTree(codegenOpts.SplitSumExpressionTree); | |
| 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() == | |
| Fortran::common::LangOptions::FPM_Fast) | |
| .setNoHonorInfs(langOptions.NoHonorInfs) | |
| .setNoHonorNaNs(langOptions.NoHonorNaNs) | |
| .setApproxFunc(langOptions.ApproxFunc) | |
| .setNoSignedZeros(langOptions.NoSignedZeros) | |
| .setAssociativeMath(langOptions.AssociativeMath) | |
| .setReciprocalMath(langOptions.ReciprocalMath); | |
| if (codegenOpts.getComplexRange() == | |
| CodeGenOptions::ComplexRangeKind::CX_Improved || | |
| codegenOpts.getComplexRange() == | |
| CodeGenOptions::ComplexRangeKind::CX_Basic) | |
| loweringOpts.setComplexDivisionToRuntime(false); | |
| loweringOpts.setFPExceptionTraps(langOptions.FPExceptionTraps); | |
| } |