xref: /llvm-project/flang/lib/Frontend/CompilerInvocation.cpp (revision fa3587d63eed78dd90663b7fe81cf9f77d403ff1)
1 //===- CompilerInvocation.cpp ---------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "flang/Frontend/CompilerInvocation.h"
14 #include "flang/Common/Fortran-features.h"
15 #include "flang/Frontend/CodeGenOptions.h"
16 #include "flang/Frontend/PreprocessorOptions.h"
17 #include "flang/Frontend/TargetOptions.h"
18 #include "flang/Semantics/semantics.h"
19 #include "flang/Version.inc"
20 #include "clang/Basic/AllDiagnostics.h"
21 #include "clang/Basic/DiagnosticDriver.h"
22 #include "clang/Basic/DiagnosticOptions.h"
23 #include "clang/Driver/DriverDiagnostic.h"
24 #include "clang/Driver/OptionUtils.h"
25 #include "clang/Driver/Options.h"
26 #include "llvm/ADT/StringRef.h"
27 #include "llvm/ADT/StringSwitch.h"
28 #include "llvm/ADT/Triple.h"
29 #include "llvm/Option/Arg.h"
30 #include "llvm/Option/ArgList.h"
31 #include "llvm/Option/OptTable.h"
32 #include "llvm/Support/FileSystem.h"
33 #include "llvm/Support/FileUtilities.h"
34 #include "llvm/Support/Host.h"
35 #include "llvm/Support/Path.h"
36 #include "llvm/Support/Process.h"
37 #include "llvm/Support/raw_ostream.h"
38 #include <memory>
39 
40 using namespace Fortran::frontend;
41 
42 //===----------------------------------------------------------------------===//
43 // Initialization.
44 //===----------------------------------------------------------------------===//
45 CompilerInvocationBase::CompilerInvocationBase()
46     : diagnosticOpts(new clang::DiagnosticOptions()),
47       preprocessorOpts(new PreprocessorOptions()) {}
48 
49 CompilerInvocationBase::CompilerInvocationBase(const CompilerInvocationBase &x)
50     : diagnosticOpts(new clang::DiagnosticOptions(x.getDiagnosticOpts())),
51       preprocessorOpts(new PreprocessorOptions(x.getPreprocessorOpts())) {}
52 
53 CompilerInvocationBase::~CompilerInvocationBase() = default;
54 
55 //===----------------------------------------------------------------------===//
56 // Deserialization (from args)
57 //===----------------------------------------------------------------------===//
58 static bool parseShowColorsArgs(const llvm::opt::ArgList &args,
59                                 bool defaultColor = true) {
60   // Color diagnostics default to auto ("on" if terminal supports) in the
61   // compiler driver `flang-new` but default to off in the frontend driver
62   // `flang-new -fc1`, needing an explicit OPT_fdiagnostics_color.
63   // Support both clang's -f[no-]color-diagnostics and gcc's
64   // -f[no-]diagnostics-colors[=never|always|auto].
65   enum {
66     Colors_On,
67     Colors_Off,
68     Colors_Auto
69   } showColors = defaultColor ? Colors_Auto : Colors_Off;
70 
71   for (auto *a : args) {
72     const llvm::opt::Option &opt = a->getOption();
73     if (opt.matches(clang::driver::options::OPT_fcolor_diagnostics)) {
74       showColors = Colors_On;
75     } else if (opt.matches(clang::driver::options::OPT_fno_color_diagnostics)) {
76       showColors = Colors_Off;
77     } else if (opt.matches(clang::driver::options::OPT_fdiagnostics_color_EQ)) {
78       llvm::StringRef value(a->getValue());
79       if (value == "always")
80         showColors = Colors_On;
81       else if (value == "never")
82         showColors = Colors_Off;
83       else if (value == "auto")
84         showColors = Colors_Auto;
85     }
86   }
87 
88   return showColors == Colors_On ||
89          (showColors == Colors_Auto &&
90           llvm::sys::Process::StandardErrHasColors());
91 }
92 
93 /// Extracts the optimisation level from \a args.
94 static unsigned getOptimizationLevel(llvm::opt::ArgList &args,
95                                      clang::DiagnosticsEngine &diags) {
96   unsigned defaultOpt = llvm::CodeGenOpt::None;
97 
98   if (llvm::opt::Arg *a =
99           args.getLastArg(clang::driver::options::OPT_O_Group)) {
100     if (a->getOption().matches(clang::driver::options::OPT_O0))
101       return llvm::CodeGenOpt::None;
102 
103     assert(a->getOption().matches(clang::driver::options::OPT_O));
104 
105     return getLastArgIntValue(args, clang::driver::options::OPT_O, defaultOpt,
106                               diags);
107   }
108 
109   return defaultOpt;
110 }
111 
112 bool Fortran::frontend::parseDiagnosticArgs(clang::DiagnosticOptions &opts,
113                                             llvm::opt::ArgList &args) {
114   opts.ShowColors = parseShowColorsArgs(args);
115 
116   return true;
117 }
118 
119 static void parseCodeGenArgs(Fortran::frontend::CodeGenOptions &opts,
120                              llvm::opt::ArgList &args,
121                              clang::DiagnosticsEngine &diags) {
122   opts.OptimizationLevel = getOptimizationLevel(args, diags);
123 
124   if (args.hasFlag(clang::driver::options::OPT_fdebug_pass_manager,
125                    clang::driver::options::OPT_fno_debug_pass_manager, false))
126     opts.DebugPassManager = 1;
127 
128   for (auto *a : args.filtered(clang::driver::options::OPT_fpass_plugin_EQ))
129     opts.LLVMPassPlugins.push_back(a->getValue());
130 
131   // -mrelocation-model option.
132   if (const llvm::opt::Arg *A =
133           args.getLastArg(clang::driver::options::OPT_mrelocation_model)) {
134     llvm::StringRef ModelName = A->getValue();
135     auto RM = llvm::StringSwitch<llvm::Optional<llvm::Reloc::Model>>(ModelName)
136                   .Case("static", llvm::Reloc::Static)
137                   .Case("pic", llvm::Reloc::PIC_)
138                   .Case("dynamic-no-pic", llvm::Reloc::DynamicNoPIC)
139                   .Case("ropi", llvm::Reloc::ROPI)
140                   .Case("rwpi", llvm::Reloc::RWPI)
141                   .Case("ropi-rwpi", llvm::Reloc::ROPI_RWPI)
142                   .Default(llvm::None);
143     if (RM.has_value())
144       opts.setRelocationModel(*RM);
145     else
146       diags.Report(clang::diag::err_drv_invalid_value)
147           << A->getAsString(args) << ModelName;
148   }
149 
150   // -pic-level and -pic-is-pie option.
151   if (int PICLevel = getLastArgIntValue(
152           args, clang::driver::options::OPT_pic_level, 0, diags)) {
153     if (PICLevel > 2)
154       diags.Report(clang::diag::err_drv_invalid_value)
155           << args.getLastArg(clang::driver::options::OPT_pic_level)
156                  ->getAsString(args)
157           << PICLevel;
158 
159     opts.PICLevel = PICLevel;
160     if (args.hasArg(clang::driver::options::OPT_pic_is_pie))
161       opts.IsPIE = 1;
162   }
163 }
164 
165 /// Parses all target input arguments and populates the target
166 /// options accordingly.
167 ///
168 /// \param [in] opts The target options instance to update
169 /// \param [in] args The list of input arguments (from the compiler invocation)
170 static void parseTargetArgs(TargetOptions &opts, llvm::opt::ArgList &args) {
171   if (const llvm::opt::Arg *a =
172           args.getLastArg(clang::driver::options::OPT_triple))
173     opts.triple = a->getValue();
174 }
175 
176 // Tweak the frontend configuration based on the frontend action
177 static void setUpFrontendBasedOnAction(FrontendOptions &opts) {
178   if (opts.programAction == DebugDumpParsingLog)
179     opts.instrumentedParse = true;
180 
181   if (opts.programAction == DebugDumpProvenance ||
182       opts.programAction == Fortran::frontend::GetDefinition)
183     opts.needProvenanceRangeToCharBlockMappings = true;
184 }
185 
186 /// Parse the argument specified for the -fconvert=<value> option
187 static std::optional<const char *> parseConvertArg(const char *s) {
188   return llvm::StringSwitch<std::optional<const char *>>(s)
189       .Case("unknown", "UNKNOWN")
190       .Case("native", "NATIVE")
191       .Case("little-endian", "LITTLE_ENDIAN")
192       .Case("big-endian", "BIG_ENDIAN")
193       .Case("swap", "SWAP")
194       .Default(std::nullopt);
195 }
196 
197 static bool parseFrontendArgs(FrontendOptions &opts, llvm::opt::ArgList &args,
198                               clang::DiagnosticsEngine &diags) {
199   unsigned numErrorsBefore = diags.getNumErrors();
200 
201   // By default the frontend driver creates a ParseSyntaxOnly action.
202   opts.programAction = ParseSyntaxOnly;
203 
204   // Treat multiple action options as an invocation error. Note that `clang
205   // -cc1` does accept multiple action options, but will only consider the
206   // rightmost one.
207   if (args.hasMultipleArgs(clang::driver::options::OPT_Action_Group)) {
208     const unsigned diagID = diags.getCustomDiagID(
209         clang::DiagnosticsEngine::Error, "Only one action option is allowed");
210     diags.Report(diagID);
211     return false;
212   }
213 
214   // Identify the action (i.e. opts.ProgramAction)
215   if (const llvm::opt::Arg *a =
216           args.getLastArg(clang::driver::options::OPT_Action_Group)) {
217     switch (a->getOption().getID()) {
218     default: {
219       llvm_unreachable("Invalid option in group!");
220     }
221     case clang::driver::options::OPT_test_io:
222       opts.programAction = InputOutputTest;
223       break;
224     case clang::driver::options::OPT_E:
225       opts.programAction = PrintPreprocessedInput;
226       break;
227     case clang::driver::options::OPT_fsyntax_only:
228       opts.programAction = ParseSyntaxOnly;
229       break;
230     case clang::driver::options::OPT_emit_mlir:
231       opts.programAction = EmitMLIR;
232       break;
233     case clang::driver::options::OPT_emit_llvm:
234       opts.programAction = EmitLLVM;
235       break;
236     case clang::driver::options::OPT_emit_llvm_bc:
237       opts.programAction = EmitLLVMBitcode;
238       break;
239     case clang::driver::options::OPT_emit_obj:
240       opts.programAction = EmitObj;
241       break;
242     case clang::driver::options::OPT_S:
243       opts.programAction = EmitAssembly;
244       break;
245     case clang::driver::options::OPT_fdebug_unparse:
246       opts.programAction = DebugUnparse;
247       break;
248     case clang::driver::options::OPT_fdebug_unparse_no_sema:
249       opts.programAction = DebugUnparseNoSema;
250       break;
251     case clang::driver::options::OPT_fdebug_unparse_with_symbols:
252       opts.programAction = DebugUnparseWithSymbols;
253       break;
254     case clang::driver::options::OPT_fdebug_dump_symbols:
255       opts.programAction = DebugDumpSymbols;
256       break;
257     case clang::driver::options::OPT_fdebug_dump_parse_tree:
258       opts.programAction = DebugDumpParseTree;
259       break;
260     case clang::driver::options::OPT_fdebug_dump_pft:
261       opts.programAction = DebugDumpPFT;
262       break;
263     case clang::driver::options::OPT_fdebug_dump_all:
264       opts.programAction = DebugDumpAll;
265       break;
266     case clang::driver::options::OPT_fdebug_dump_parse_tree_no_sema:
267       opts.programAction = DebugDumpParseTreeNoSema;
268       break;
269     case clang::driver::options::OPT_fdebug_dump_provenance:
270       opts.programAction = DebugDumpProvenance;
271       break;
272     case clang::driver::options::OPT_fdebug_dump_parsing_log:
273       opts.programAction = DebugDumpParsingLog;
274       break;
275     case clang::driver::options::OPT_fdebug_measure_parse_tree:
276       opts.programAction = DebugMeasureParseTree;
277       break;
278     case clang::driver::options::OPT_fdebug_pre_fir_tree:
279       opts.programAction = DebugPreFIRTree;
280       break;
281     case clang::driver::options::OPT_fget_symbols_sources:
282       opts.programAction = GetSymbolsSources;
283       break;
284     case clang::driver::options::OPT_fget_definition:
285       opts.programAction = GetDefinition;
286       break;
287     case clang::driver::options::OPT_init_only:
288       opts.programAction = InitOnly;
289       break;
290 
291       // TODO:
292       // case clang::driver::options::OPT_emit_llvm:
293       // case clang::driver::options::OPT_emit_llvm_only:
294       // case clang::driver::options::OPT_emit_codegen_only:
295       // case clang::driver::options::OPT_emit_module:
296       // (...)
297     }
298 
299     // Parse the values provided with `-fget-definition` (there should be 3
300     // integers)
301     if (llvm::opt::OptSpecifier(a->getOption().getID()) ==
302         clang::driver::options::OPT_fget_definition) {
303       unsigned optVals[3] = {0, 0, 0};
304 
305       for (unsigned i = 0; i < 3; i++) {
306         llvm::StringRef val = a->getValue(i);
307 
308         if (val.getAsInteger(10, optVals[i])) {
309           // A non-integer was encountered - that's an error.
310           diags.Report(clang::diag::err_drv_invalid_value)
311               << a->getOption().getName() << val;
312           break;
313         }
314       }
315       opts.getDefVals.line = optVals[0];
316       opts.getDefVals.startColumn = optVals[1];
317       opts.getDefVals.endColumn = optVals[2];
318     }
319   }
320 
321   // Parsing -load <dsopath> option and storing shared object path
322   if (llvm::opt::Arg *a = args.getLastArg(clang::driver::options::OPT_load)) {
323     opts.plugins.push_back(a->getValue());
324   }
325 
326   // Parsing -plugin <name> option and storing plugin name and setting action
327   if (const llvm::opt::Arg *a =
328           args.getLastArg(clang::driver::options::OPT_plugin)) {
329     opts.programAction = PluginAction;
330     opts.actionName = a->getValue();
331   }
332 
333   opts.outputFile = args.getLastArgValue(clang::driver::options::OPT_o);
334   opts.showHelp = args.hasArg(clang::driver::options::OPT_help);
335   opts.showVersion = args.hasArg(clang::driver::options::OPT_version);
336 
337   // Get the input kind (from the value passed via `-x`)
338   InputKind dashX(Language::Unknown);
339   if (const llvm::opt::Arg *a =
340           args.getLastArg(clang::driver::options::OPT_x)) {
341     llvm::StringRef xValue = a->getValue();
342     // Principal languages.
343     dashX = llvm::StringSwitch<InputKind>(xValue)
344                 // Flang does not differentiate between pre-processed and not
345                 // pre-processed inputs.
346                 .Case("f95", Language::Fortran)
347                 .Case("f95-cpp-input", Language::Fortran)
348                 .Default(Language::Unknown);
349 
350     // Flang's intermediate representations.
351     if (dashX.isUnknown())
352       dashX = llvm::StringSwitch<InputKind>(xValue)
353                   .Case("ir", Language::LLVM_IR)
354                   .Case("fir", Language::MLIR)
355                   .Case("mlir", Language::MLIR)
356                   .Default(Language::Unknown);
357 
358     if (dashX.isUnknown())
359       diags.Report(clang::diag::err_drv_invalid_value)
360           << a->getAsString(args) << a->getValue();
361   }
362 
363   // Collect the input files and save them in our instance of FrontendOptions.
364   std::vector<std::string> inputs =
365       args.getAllArgValues(clang::driver::options::OPT_INPUT);
366   opts.inputs.clear();
367   if (inputs.empty())
368     // '-' is the default input if none is given.
369     inputs.push_back("-");
370   for (unsigned i = 0, e = inputs.size(); i != e; ++i) {
371     InputKind ik = dashX;
372     if (ik.isUnknown()) {
373       ik = FrontendOptions::getInputKindForExtension(
374           llvm::StringRef(inputs[i]).rsplit('.').second);
375       if (ik.isUnknown())
376         ik = Language::Unknown;
377       if (i == 0)
378         dashX = ik;
379     }
380 
381     opts.inputs.emplace_back(std::move(inputs[i]), ik);
382   }
383 
384   // Set fortranForm based on options -ffree-form and -ffixed-form.
385   if (const auto *arg =
386           args.getLastArg(clang::driver::options::OPT_ffixed_form,
387                           clang::driver::options::OPT_ffree_form)) {
388     opts.fortranForm =
389         arg->getOption().matches(clang::driver::options::OPT_ffixed_form)
390             ? FortranForm::FixedForm
391             : FortranForm::FreeForm;
392   }
393 
394   // Set fixedFormColumns based on -ffixed-line-length=<value>
395   if (const auto *arg =
396           args.getLastArg(clang::driver::options::OPT_ffixed_line_length_EQ)) {
397     llvm::StringRef argValue = llvm::StringRef(arg->getValue());
398     std::int64_t columns = -1;
399     if (argValue == "none") {
400       columns = 0;
401     } else if (argValue.getAsInteger(/*Radix=*/10, columns)) {
402       columns = -1;
403     }
404     if (columns < 0) {
405       diags.Report(clang::diag::err_drv_negative_columns)
406           << arg->getOption().getName() << arg->getValue();
407     } else if (columns == 0) {
408       opts.fixedFormColumns = 1000000;
409     } else if (columns < 7) {
410       diags.Report(clang::diag::err_drv_small_columns)
411           << arg->getOption().getName() << arg->getValue() << "7";
412     } else {
413       opts.fixedFormColumns = columns;
414     }
415   }
416 
417   // Set conversion based on -fconvert=<value>
418   if (const auto *arg =
419           args.getLastArg(clang::driver::options::OPT_fconvert_EQ)) {
420     const char *argValue = arg->getValue();
421     if (auto convert = parseConvertArg(argValue))
422       opts.envDefaults.push_back({"FORT_CONVERT", *convert});
423     else
424       diags.Report(clang::diag::err_drv_invalid_value)
425           << arg->getAsString(args) << argValue;
426   }
427 
428   // -f{no-}implicit-none
429   opts.features.Enable(
430       Fortran::common::LanguageFeature::ImplicitNoneTypeAlways,
431       args.hasFlag(clang::driver::options::OPT_fimplicit_none,
432                    clang::driver::options::OPT_fno_implicit_none, false));
433 
434   // -f{no-}backslash
435   opts.features.Enable(Fortran::common::LanguageFeature::BackslashEscapes,
436                        args.hasFlag(clang::driver::options::OPT_fbackslash,
437                                     clang::driver::options::OPT_fno_backslash,
438                                     false));
439 
440   // -f{no-}logical-abbreviations
441   opts.features.Enable(
442       Fortran::common::LanguageFeature::LogicalAbbreviations,
443       args.hasFlag(clang::driver::options::OPT_flogical_abbreviations,
444                    clang::driver::options::OPT_fno_logical_abbreviations,
445                    false));
446 
447   // -f{no-}xor-operator
448   opts.features.Enable(
449       Fortran::common::LanguageFeature::XOROperator,
450       args.hasFlag(clang::driver::options::OPT_fxor_operator,
451                    clang::driver::options::OPT_fno_xor_operator, false));
452 
453   // -fno-automatic
454   if (args.hasArg(clang::driver::options::OPT_fno_automatic)) {
455     opts.features.Enable(Fortran::common::LanguageFeature::DefaultSave);
456   }
457 
458   if (args.hasArg(
459           clang::driver::options::OPT_falternative_parameter_statement)) {
460     opts.features.Enable(Fortran::common::LanguageFeature::OldStyleParameter);
461   }
462   if (const llvm::opt::Arg *arg =
463           args.getLastArg(clang::driver::options::OPT_finput_charset_EQ)) {
464     llvm::StringRef argValue = arg->getValue();
465     if (argValue == "utf-8") {
466       opts.encoding = Fortran::parser::Encoding::UTF_8;
467     } else if (argValue == "latin-1") {
468       opts.encoding = Fortran::parser::Encoding::LATIN_1;
469     } else {
470       diags.Report(clang::diag::err_drv_invalid_value)
471           << arg->getAsString(args) << argValue;
472     }
473   }
474 
475   setUpFrontendBasedOnAction(opts);
476   opts.dashX = dashX;
477 
478   return diags.getNumErrors() == numErrorsBefore;
479 }
480 
481 // Generate the path to look for intrinsic modules
482 static std::string getIntrinsicDir() {
483   // TODO: Find a system independent API
484   llvm::SmallString<128> driverPath;
485   driverPath.assign(llvm::sys::fs::getMainExecutable(nullptr, nullptr));
486   llvm::sys::path::remove_filename(driverPath);
487   driverPath.append("/../include/flang/");
488   return std::string(driverPath);
489 }
490 
491 // Generate the path to look for OpenMP headers
492 static std::string getOpenMPHeadersDir() {
493   llvm::SmallString<128> includePath;
494   includePath.assign(llvm::sys::fs::getMainExecutable(nullptr, nullptr));
495   llvm::sys::path::remove_filename(includePath);
496   includePath.append("/../include/flang/OpenMP/");
497   return std::string(includePath);
498 }
499 
500 /// Parses all preprocessor input arguments and populates the preprocessor
501 /// options accordingly.
502 ///
503 /// \param [in] opts The preprocessor options instance
504 /// \param [out] args The list of input arguments
505 static void parsePreprocessorArgs(Fortran::frontend::PreprocessorOptions &opts,
506                                   llvm::opt::ArgList &args) {
507   // Add macros from the command line.
508   for (const auto *currentArg : args.filtered(clang::driver::options::OPT_D,
509                                               clang::driver::options::OPT_U)) {
510     if (currentArg->getOption().matches(clang::driver::options::OPT_D)) {
511       opts.addMacroDef(currentArg->getValue());
512     } else {
513       opts.addMacroUndef(currentArg->getValue());
514     }
515   }
516 
517   // Add the ordered list of -I's.
518   for (const auto *currentArg : args.filtered(clang::driver::options::OPT_I))
519     opts.searchDirectoriesFromDashI.emplace_back(currentArg->getValue());
520 
521   // Prepend the ordered list of -intrinsic-modules-path
522   // to the default location to search.
523   for (const auto *currentArg :
524        args.filtered(clang::driver::options::OPT_fintrinsic_modules_path))
525     opts.searchDirectoriesFromIntrModPath.emplace_back(currentArg->getValue());
526 
527   // -cpp/-nocpp
528   if (const auto *currentArg = args.getLastArg(
529           clang::driver::options::OPT_cpp, clang::driver::options::OPT_nocpp))
530     opts.macrosFlag =
531         (currentArg->getOption().matches(clang::driver::options::OPT_cpp))
532             ? PPMacrosFlag::Include
533             : PPMacrosFlag::Exclude;
534 
535   opts.noReformat = args.hasArg(clang::driver::options::OPT_fno_reformat);
536   opts.noLineDirectives = args.hasArg(clang::driver::options::OPT_P);
537 }
538 
539 /// Parses all semantic related arguments and populates the variables
540 /// options accordingly. Returns false if new errors are generated.
541 static bool parseSemaArgs(CompilerInvocation &res, llvm::opt::ArgList &args,
542                           clang::DiagnosticsEngine &diags) {
543   unsigned numErrorsBefore = diags.getNumErrors();
544 
545   // -J/module-dir option
546   auto moduleDirList =
547       args.getAllArgValues(clang::driver::options::OPT_module_dir);
548   // User can only specify -J/-module-dir once
549   // https://gcc.gnu.org/onlinedocs/gfortran/Directory-Options.html
550   if (moduleDirList.size() > 1) {
551     const unsigned diagID =
552         diags.getCustomDiagID(clang::DiagnosticsEngine::Error,
553                               "Only one '-module-dir/-J' option allowed");
554     diags.Report(diagID);
555   }
556   if (moduleDirList.size() == 1)
557     res.setModuleDir(moduleDirList[0]);
558 
559   // -fdebug-module-writer option
560   if (args.hasArg(clang::driver::options::OPT_fdebug_module_writer)) {
561     res.setDebugModuleDir(true);
562   }
563 
564   // -module-suffix
565   if (const auto *moduleSuffix =
566           args.getLastArg(clang::driver::options::OPT_module_suffix)) {
567     res.setModuleFileSuffix(moduleSuffix->getValue());
568   }
569 
570   // -f{no-}analyzed-objects-for-unparse
571   res.setUseAnalyzedObjectsForUnparse(args.hasFlag(
572       clang::driver::options::OPT_fanalyzed_objects_for_unparse,
573       clang::driver::options::OPT_fno_analyzed_objects_for_unparse, true));
574 
575   return diags.getNumErrors() == numErrorsBefore;
576 }
577 
578 /// Parses all diagnostics related arguments and populates the variables
579 /// options accordingly. Returns false if new errors are generated.
580 static bool parseDiagArgs(CompilerInvocation &res, llvm::opt::ArgList &args,
581                           clang::DiagnosticsEngine &diags) {
582   unsigned numErrorsBefore = diags.getNumErrors();
583 
584   // -Werror option
585   // TODO: Currently throws a Diagnostic for anything other than -W<error>,
586   // this has to change when other -W<opt>'s are supported.
587   if (args.hasArg(clang::driver::options::OPT_W_Joined)) {
588     if (args.getLastArgValue(clang::driver::options::OPT_W_Joined)
589             .equals("error")) {
590       res.setWarnAsErr(true);
591     } else {
592       const unsigned diagID =
593           diags.getCustomDiagID(clang::DiagnosticsEngine::Error,
594                                 "Only `-Werror` is supported currently.");
595       diags.Report(diagID);
596     }
597   }
598 
599   // Default to off for `flang-new -fc1`.
600   res.getFrontendOpts().showColors =
601       parseShowColorsArgs(args, /*defaultDiagColor=*/false);
602 
603   return diags.getNumErrors() == numErrorsBefore;
604 }
605 
606 /// Parses all Dialect related arguments and populates the variables
607 /// options accordingly. Returns false if new errors are generated.
608 static bool parseDialectArgs(CompilerInvocation &res, llvm::opt::ArgList &args,
609                              clang::DiagnosticsEngine &diags) {
610   unsigned numErrorsBefore = diags.getNumErrors();
611 
612   // -fdefault* family
613   if (args.hasArg(clang::driver::options::OPT_fdefault_real_8)) {
614     res.getDefaultKinds().set_defaultRealKind(8);
615     res.getDefaultKinds().set_doublePrecisionKind(16);
616   }
617   if (args.hasArg(clang::driver::options::OPT_fdefault_integer_8)) {
618     res.getDefaultKinds().set_defaultIntegerKind(8);
619     res.getDefaultKinds().set_subscriptIntegerKind(8);
620     res.getDefaultKinds().set_sizeIntegerKind(8);
621   }
622   if (args.hasArg(clang::driver::options::OPT_fdefault_double_8)) {
623     if (!args.hasArg(clang::driver::options::OPT_fdefault_real_8)) {
624       // -fdefault-double-8 has to be used with -fdefault-real-8
625       // to be compatible with gfortran
626       const unsigned diagID = diags.getCustomDiagID(
627           clang::DiagnosticsEngine::Error,
628           "Use of `-fdefault-double-8` requires `-fdefault-real-8`");
629       diags.Report(diagID);
630     }
631     // https://gcc.gnu.org/onlinedocs/gfortran/Fortran-Dialect-Options.html
632     res.getDefaultKinds().set_doublePrecisionKind(8);
633   }
634   if (args.hasArg(clang::driver::options::OPT_flarge_sizes))
635     res.getDefaultKinds().set_sizeIntegerKind(8);
636 
637   // -fopenmp and -fopenacc
638   if (args.hasArg(clang::driver::options::OPT_fopenacc)) {
639     res.getFrontendOpts().features.Enable(
640         Fortran::common::LanguageFeature::OpenACC);
641   }
642   if (args.hasArg(clang::driver::options::OPT_fopenmp)) {
643     res.getFrontendOpts().features.Enable(
644         Fortran::common::LanguageFeature::OpenMP);
645   }
646 
647   // -pedantic
648   if (args.hasArg(clang::driver::options::OPT_pedantic)) {
649     res.setEnableConformanceChecks();
650   }
651   // -std=f2018 (currently this implies -pedantic)
652   // TODO: Set proper options when more fortran standards
653   // are supported.
654   if (args.hasArg(clang::driver::options::OPT_std_EQ)) {
655     auto standard = args.getLastArgValue(clang::driver::options::OPT_std_EQ);
656     // We only allow f2018 as the given standard
657     if (standard.equals("f2018")) {
658       res.setEnableConformanceChecks();
659     } else {
660       const unsigned diagID =
661           diags.getCustomDiagID(clang::DiagnosticsEngine::Error,
662                                 "Only -std=f2018 is allowed currently.");
663       diags.Report(diagID);
664     }
665   }
666   return diags.getNumErrors() == numErrorsBefore;
667 }
668 
669 /// Parses all floating point related arguments and populates the
670 /// CompilerInvocation accordingly.
671 /// Returns false if new errors are generated.
672 ///
673 /// \param [out] invoc Stores the processed arguments
674 /// \param [in] args The compiler invocation arguments to parse
675 /// \param [out] diags DiagnosticsEngine to report erros with
676 static bool parseFloatingPointArgs(CompilerInvocation &invoc,
677                                    llvm::opt::ArgList &args,
678                                    clang::DiagnosticsEngine &diags) {
679   LangOptions &opts = invoc.getLangOpts();
680   const unsigned diagUnimplemented = diags.getCustomDiagID(
681       clang::DiagnosticsEngine::Warning, "%0 is not currently implemented");
682 
683   if (const llvm::opt::Arg *a =
684           args.getLastArg(clang::driver::options::OPT_ffp_contract)) {
685     const llvm::StringRef val = a->getValue();
686     enum LangOptions::FPModeKind fpContractMode;
687 
688     if (val == "off")
689       fpContractMode = LangOptions::FPM_Off;
690     else if (val == "fast")
691       fpContractMode = LangOptions::FPM_Fast;
692     else {
693       diags.Report(clang::diag::err_drv_unsupported_option_argument)
694           << a->getSpelling() << val;
695       return false;
696     }
697 
698     diags.Report(diagUnimplemented) << a->getOption().getName();
699     opts.setFPContractMode(fpContractMode);
700   }
701 
702   if (const llvm::opt::Arg *a =
703           args.getLastArg(clang::driver::options::OPT_menable_no_infinities)) {
704     diags.Report(diagUnimplemented) << a->getOption().getName();
705     opts.NoHonorInfs = true;
706   }
707 
708   if (const llvm::opt::Arg *a =
709           args.getLastArg(clang::driver::options::OPT_menable_no_nans)) {
710     diags.Report(diagUnimplemented) << a->getOption().getName();
711     opts.NoHonorNaNs = true;
712   }
713 
714   if (const llvm::opt::Arg *a =
715           args.getLastArg(clang::driver::options::OPT_fapprox_func)) {
716     diags.Report(diagUnimplemented) << a->getOption().getName();
717     opts.ApproxFunc = true;
718   }
719 
720   if (const llvm::opt::Arg *a =
721           args.getLastArg(clang::driver::options::OPT_fno_signed_zeros)) {
722     diags.Report(diagUnimplemented) << a->getOption().getName();
723     opts.NoSignedZeros = true;
724   }
725 
726   if (const llvm::opt::Arg *a =
727           args.getLastArg(clang::driver::options::OPT_mreassociate)) {
728     diags.Report(diagUnimplemented) << a->getOption().getName();
729     opts.AssociativeMath = true;
730   }
731 
732   if (const llvm::opt::Arg *a =
733           args.getLastArg(clang::driver::options::OPT_freciprocal_math)) {
734     diags.Report(diagUnimplemented) << a->getOption().getName();
735     opts.ReciprocalMath = true;
736   }
737 
738   return true;
739 }
740 
741 bool CompilerInvocation::createFromArgs(
742     CompilerInvocation &res, llvm::ArrayRef<const char *> commandLineArgs,
743     clang::DiagnosticsEngine &diags) {
744 
745   bool success = true;
746 
747   // Set the default triple for this CompilerInvocation. This might be
748   // overridden by users with `-triple` (see the call to `ParseTargetArgs`
749   // below).
750   // NOTE: Like in Clang, it would be nice to use option marshalling
751   // for this so that the entire logic for setting-up the triple is in one
752   // place.
753   res.getTargetOpts().triple =
754       llvm::Triple::normalize(llvm::sys::getDefaultTargetTriple());
755 
756   // Parse the arguments
757   const llvm::opt::OptTable &opts = clang::driver::getDriverOptTable();
758   const unsigned includedFlagsBitmask = clang::driver::options::FC1Option;
759   unsigned missingArgIndex, missingArgCount;
760   llvm::opt::InputArgList args = opts.ParseArgs(
761       commandLineArgs, missingArgIndex, missingArgCount, includedFlagsBitmask);
762 
763   // Check for missing argument error.
764   if (missingArgCount) {
765     diags.Report(clang::diag::err_drv_missing_argument)
766         << args.getArgString(missingArgIndex) << missingArgCount;
767     success = false;
768   }
769 
770   // Issue errors on unknown arguments
771   for (const auto *a : args.filtered(clang::driver::options::OPT_UNKNOWN)) {
772     auto argString = a->getAsString(args);
773     std::string nearest;
774     if (opts.findNearest(argString, nearest, includedFlagsBitmask) > 1)
775       diags.Report(clang::diag::err_drv_unknown_argument) << argString;
776     else
777       diags.Report(clang::diag::err_drv_unknown_argument_with_suggestion)
778           << argString << nearest;
779     success = false;
780   }
781 
782   success &= parseFrontendArgs(res.getFrontendOpts(), args, diags);
783   parseTargetArgs(res.getTargetOpts(), args);
784   parsePreprocessorArgs(res.getPreprocessorOpts(), args);
785   parseCodeGenArgs(res.getCodeGenOpts(), args, diags);
786   success &= parseSemaArgs(res, args, diags);
787   success &= parseDialectArgs(res, args, diags);
788   success &= parseDiagArgs(res, args, diags);
789   res.frontendOpts.llvmArgs =
790       args.getAllArgValues(clang::driver::options::OPT_mllvm);
791 
792   res.frontendOpts.mlirArgs =
793       args.getAllArgValues(clang::driver::options::OPT_mmlir);
794 
795   success &= parseFloatingPointArgs(res, args, diags);
796 
797   return success;
798 }
799 
800 void CompilerInvocation::collectMacroDefinitions() {
801   auto &ppOpts = this->getPreprocessorOpts();
802 
803   for (unsigned i = 0, n = ppOpts.macros.size(); i != n; ++i) {
804     llvm::StringRef macro = ppOpts.macros[i].first;
805     bool isUndef = ppOpts.macros[i].second;
806 
807     std::pair<llvm::StringRef, llvm::StringRef> macroPair = macro.split('=');
808     llvm::StringRef macroName = macroPair.first;
809     llvm::StringRef macroBody = macroPair.second;
810 
811     // For an #undef'd macro, we only care about the name.
812     if (isUndef) {
813       parserOpts.predefinitions.emplace_back(macroName.str(),
814                                              std::optional<std::string>{});
815       continue;
816     }
817 
818     // For a #define'd macro, figure out the actual definition.
819     if (macroName.size() == macro.size())
820       macroBody = "1";
821     else {
822       // Note: GCC drops anything following an end-of-line character.
823       llvm::StringRef::size_type end = macroBody.find_first_of("\n\r");
824       macroBody = macroBody.substr(0, end);
825     }
826     parserOpts.predefinitions.emplace_back(
827         macroName, std::optional<std::string>(macroBody.str()));
828   }
829 }
830 
831 void CompilerInvocation::setDefaultFortranOpts() {
832   auto &fortranOptions = getFortranOpts();
833 
834   std::vector<std::string> searchDirectories{"."s};
835   fortranOptions.searchDirectories = searchDirectories;
836 
837   // Add the location of omp_lib.h to the search directories. Currently this is
838   // identical to the modules' directory.
839   fortranOptions.searchDirectories.emplace_back(getOpenMPHeadersDir());
840 
841   fortranOptions.isFixedForm = false;
842 }
843 
844 // TODO: When expanding this method, consider creating a dedicated API for
845 // this. Also at some point we will need to differentiate between different
846 // targets and add dedicated predefines for each.
847 void CompilerInvocation::setDefaultPredefinitions() {
848   auto &fortranOptions = getFortranOpts();
849   const auto &frontendOptions = getFrontendOpts();
850 
851   // Populate the macro list with version numbers and other predefinitions.
852   fortranOptions.predefinitions.emplace_back("__flang__", "1");
853   fortranOptions.predefinitions.emplace_back("__flang_major__",
854                                              FLANG_VERSION_MAJOR_STRING);
855   fortranOptions.predefinitions.emplace_back("__flang_minor__",
856                                              FLANG_VERSION_MINOR_STRING);
857   fortranOptions.predefinitions.emplace_back("__flang_patchlevel__",
858                                              FLANG_VERSION_PATCHLEVEL_STRING);
859 
860   // Add predefinitions based on extensions enabled
861   if (frontendOptions.features.IsEnabled(
862           Fortran::common::LanguageFeature::OpenACC)) {
863     fortranOptions.predefinitions.emplace_back("_OPENACC", "202011");
864   }
865   if (frontendOptions.features.IsEnabled(
866           Fortran::common::LanguageFeature::OpenMP)) {
867     fortranOptions.predefinitions.emplace_back("_OPENMP", "201511");
868   }
869   llvm::Triple targetTriple{llvm::Triple(this->targetOpts.triple)};
870   if (targetTriple.getArch() == llvm::Triple::ArchType::x86_64) {
871     fortranOptions.predefinitions.emplace_back("__x86_64__", "1");
872     fortranOptions.predefinitions.emplace_back("__x86_64", "1");
873   }
874 }
875 
876 void CompilerInvocation::setFortranOpts() {
877   auto &fortranOptions = getFortranOpts();
878   const auto &frontendOptions = getFrontendOpts();
879   const auto &preprocessorOptions = getPreprocessorOpts();
880   auto &moduleDirJ = getModuleDir();
881 
882   if (frontendOptions.fortranForm != FortranForm::Unknown) {
883     fortranOptions.isFixedForm =
884         frontendOptions.fortranForm == FortranForm::FixedForm;
885   }
886   fortranOptions.fixedFormColumns = frontendOptions.fixedFormColumns;
887 
888   fortranOptions.features = frontendOptions.features;
889   fortranOptions.encoding = frontendOptions.encoding;
890 
891   // Adding search directories specified by -I
892   fortranOptions.searchDirectories.insert(
893       fortranOptions.searchDirectories.end(),
894       preprocessorOptions.searchDirectoriesFromDashI.begin(),
895       preprocessorOptions.searchDirectoriesFromDashI.end());
896 
897   // Add the ordered list of -intrinsic-modules-path
898   fortranOptions.searchDirectories.insert(
899       fortranOptions.searchDirectories.end(),
900       preprocessorOptions.searchDirectoriesFromIntrModPath.begin(),
901       preprocessorOptions.searchDirectoriesFromIntrModPath.end());
902 
903   //  Add the default intrinsic module directory
904   fortranOptions.intrinsicModuleDirectories.emplace_back(getIntrinsicDir());
905 
906   // Add the directory supplied through -J/-module-dir to the list of search
907   // directories
908   if (moduleDirJ != ".")
909     fortranOptions.searchDirectories.emplace_back(moduleDirJ);
910 
911   if (frontendOptions.instrumentedParse)
912     fortranOptions.instrumentedParse = true;
913 
914   if (frontendOptions.showColors)
915     fortranOptions.showColors = true;
916 
917   if (frontendOptions.needProvenanceRangeToCharBlockMappings)
918     fortranOptions.needProvenanceRangeToCharBlockMappings = true;
919 
920   if (getEnableConformanceChecks()) {
921     fortranOptions.features.WarnOnAllNonstandard();
922   }
923 }
924 
925 void CompilerInvocation::setSemanticsOpts(
926     Fortran::parser::AllCookedSources &allCookedSources) {
927   auto &fortranOptions = getFortranOpts();
928 
929   semanticsContext = std::make_unique<semantics::SemanticsContext>(
930       getDefaultKinds(), fortranOptions.features, allCookedSources);
931 
932   semanticsContext->set_moduleDirectory(getModuleDir())
933       .set_searchDirectories(fortranOptions.searchDirectories)
934       .set_intrinsicModuleDirectories(fortranOptions.intrinsicModuleDirectories)
935       .set_warnOnNonstandardUsage(getEnableConformanceChecks())
936       .set_warningsAreErrors(getWarnAsErr())
937       .set_moduleFileSuffix(getModuleFileSuffix());
938 
939   llvm::Triple targetTriple{llvm::Triple(this->targetOpts.triple)};
940   // FIXME: Handle real(3) ?
941   if (targetTriple.getArch() != llvm::Triple::ArchType::x86_64) {
942     semanticsContext->targetCharacteristics().DisableType(
943         Fortran::common::TypeCategory::Real, /*kind=*/10);
944   }
945 }
946 
947 /// Set \p loweringOptions controlling lowering behavior based
948 /// on the \p optimizationLevel.
949 void CompilerInvocation::setLoweringOptions() {
950   const CodeGenOptions &codegenOpts = getCodeGenOpts();
951 
952   // Lower TRANSPOSE as a runtime call under -O0.
953   loweringOpts.setOptimizeTranspose(codegenOpts.OptimizationLevel > 0);
954 
955   const LangOptions &langOptions = getLangOpts();
956   Fortran::common::MathOptionsBase &mathOpts = loweringOpts.getMathOptions();
957   // TODO: when LangOptions are finalized, we can represent
958   //       the math related options using Fortran::commmon::MathOptionsBase,
959   //       so that we can just copy it into LoweringOptions.
960   mathOpts
961       .setFPContractEnabled(langOptions.getFPContractMode() ==
962                             LangOptions::FPM_Fast)
963       .setNoHonorInfs(langOptions.NoHonorInfs)
964       .setNoHonorNaNs(langOptions.NoHonorNaNs)
965       .setApproxFunc(langOptions.ApproxFunc)
966       .setNoSignedZeros(langOptions.NoSignedZeros)
967       .setAssociativeMath(langOptions.AssociativeMath)
968       .setReciprocalMath(langOptions.ReciprocalMath);
969 }
970