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/Common/OpenMP-features.h" |
16 | #include "flang/Common/Version.h" |
17 | #include "flang/Frontend/CodeGenOptions.h" |
18 | #include "flang/Frontend/PreprocessorOptions.h" |
19 | #include "flang/Frontend/TargetOptions.h" |
20 | #include "flang/Semantics/semantics.h" |
21 | #include "flang/Tools/TargetSetup.h" |
22 | #include "flang/Version.inc" |
23 | #include "clang/Basic/AllDiagnostics.h" |
24 | #include "clang/Basic/DiagnosticDriver.h" |
25 | #include "clang/Basic/DiagnosticOptions.h" |
26 | #include "clang/Driver/DriverDiagnostic.h" |
27 | #include "clang/Driver/OptionUtils.h" |
28 | #include "clang/Driver/Options.h" |
29 | #include "llvm/ADT/StringRef.h" |
30 | #include "llvm/ADT/StringSwitch.h" |
31 | #include "llvm/Frontend/Debug/Options.h" |
32 | #include "llvm/Option/Arg.h" |
33 | #include "llvm/Option/ArgList.h" |
34 | #include "llvm/Option/OptTable.h" |
35 | #include "llvm/Support/FileSystem.h" |
36 | #include "llvm/Support/FileUtilities.h" |
37 | #include "llvm/Support/Path.h" |
38 | #include "llvm/Support/Process.h" |
39 | #include "llvm/Support/raw_ostream.h" |
40 | #include "llvm/TargetParser/Host.h" |
41 | #include "llvm/TargetParser/Triple.h" |
42 | #include <cstdlib> |
43 | #include <memory> |
44 | #include <optional> |
45 | |
46 | using namespace Fortran::frontend; |
47 | |
48 | //===----------------------------------------------------------------------===// |
49 | // Initialization. |
50 | //===----------------------------------------------------------------------===// |
51 | CompilerInvocationBase::CompilerInvocationBase() |
52 | : diagnosticOpts(new clang::DiagnosticOptions()), |
53 | preprocessorOpts(new PreprocessorOptions()) {} |
54 | |
55 | CompilerInvocationBase::CompilerInvocationBase(const CompilerInvocationBase &x) |
56 | : diagnosticOpts(new clang::DiagnosticOptions(x.getDiagnosticOpts())), |
57 | preprocessorOpts(new PreprocessorOptions(x.getPreprocessorOpts())) {} |
58 | |
59 | CompilerInvocationBase::~CompilerInvocationBase() = default; |
60 | |
61 | //===----------------------------------------------------------------------===// |
62 | // Deserialization (from args) |
63 | //===----------------------------------------------------------------------===// |
64 | static bool parseShowColorsArgs(const llvm::opt::ArgList &args, |
65 | bool defaultColor = true) { |
66 | // Color diagnostics default to auto ("on" if terminal supports) in the |
67 | // compiler driver `flang-new` but default to off in the frontend driver |
68 | // `flang-new -fc1`, needing an explicit OPT_fdiagnostics_color. |
69 | // Support both clang's -f[no-]color-diagnostics and gcc's |
70 | // -f[no-]diagnostics-colors[=never|always|auto]. |
71 | enum { |
72 | Colors_On, |
73 | Colors_Off, |
74 | Colors_Auto |
75 | } showColors = defaultColor ? Colors_Auto : Colors_Off; |
76 | |
77 | for (auto *a : args) { |
78 | const llvm::opt::Option &opt = a->getOption(); |
79 | if (opt.matches(clang::driver::options::ID: OPT_fcolor_diagnostics)) { |
80 | showColors = Colors_On; |
81 | } else if (opt.matches(clang::driver::options::ID: OPT_fno_color_diagnostics)) { |
82 | showColors = Colors_Off; |
83 | } else if (opt.matches(clang::driver::options::ID: OPT_fdiagnostics_color_EQ)) { |
84 | llvm::StringRef value(a->getValue()); |
85 | if (value == "always" ) |
86 | showColors = Colors_On; |
87 | else if (value == "never" ) |
88 | showColors = Colors_Off; |
89 | else if (value == "auto" ) |
90 | showColors = Colors_Auto; |
91 | } |
92 | } |
93 | |
94 | return showColors == Colors_On || |
95 | (showColors == Colors_Auto && |
96 | llvm::sys::Process::StandardErrHasColors()); |
97 | } |
98 | |
99 | /// Extracts the optimisation level from \a args. |
100 | static unsigned getOptimizationLevel(llvm::opt::ArgList &args, |
101 | clang::DiagnosticsEngine &diags) { |
102 | unsigned defaultOpt = 0; |
103 | |
104 | if (llvm::opt::Arg *a = |
105 | args.getLastArg(clang::driver::options::OPT_O_Group)) { |
106 | if (a->getOption().matches(clang::driver::options::ID: OPT_O0)) |
107 | return 0; |
108 | |
109 | assert(a->getOption().matches(clang::driver::options::OPT_O)); |
110 | |
111 | return getLastArgIntValue(args, clang::driver::options::OPT_O, defaultOpt, |
112 | diags); |
113 | } |
114 | |
115 | return defaultOpt; |
116 | } |
117 | |
118 | bool Fortran::frontend::parseDiagnosticArgs(clang::DiagnosticOptions &opts, |
119 | llvm::opt::ArgList &args) { |
120 | opts.ShowColors = parseShowColorsArgs(args); |
121 | |
122 | return true; |
123 | } |
124 | |
125 | static bool parseDebugArgs(Fortran::frontend::CodeGenOptions &opts, |
126 | llvm::opt::ArgList &args, |
127 | clang::DiagnosticsEngine &diags) { |
128 | using DebugInfoKind = llvm::codegenoptions::DebugInfoKind; |
129 | if (llvm::opt::Arg *arg = |
130 | args.getLastArg(clang::driver::options::OPT_debug_info_kind_EQ)) { |
131 | std::optional<DebugInfoKind> val = |
132 | llvm::StringSwitch<std::optional<DebugInfoKind>>(arg->getValue()) |
133 | .Case(S: "line-tables-only" , Value: llvm::codegenoptions::DebugLineTablesOnly) |
134 | .Case(S: "line-directives-only" , |
135 | Value: llvm::codegenoptions::DebugDirectivesOnly) |
136 | .Case(S: "constructor" , Value: llvm::codegenoptions::DebugInfoConstructor) |
137 | .Case(S: "limited" , Value: llvm::codegenoptions::LimitedDebugInfo) |
138 | .Case(S: "standalone" , Value: llvm::codegenoptions::FullDebugInfo) |
139 | .Case(S: "unused-types" , Value: llvm::codegenoptions::UnusedTypeInfo) |
140 | .Default(Value: std::nullopt); |
141 | if (!val.has_value()) { |
142 | diags.Report(clang::diag::err_drv_invalid_value) |
143 | << arg->getAsString(Args: args) << arg->getValue(); |
144 | return false; |
145 | } |
146 | opts.setDebugInfo(val.value()); |
147 | if (val != llvm::codegenoptions::DebugLineTablesOnly && |
148 | val != llvm::codegenoptions::FullDebugInfo && |
149 | val != llvm::codegenoptions::NoDebugInfo) { |
150 | const auto debugWarning = diags.getCustomDiagID( |
151 | L: clang::DiagnosticsEngine::Warning, FormatString: "Unsupported debug option: %0" ); |
152 | diags.Report(DiagID: debugWarning) << arg->getValue(); |
153 | } |
154 | } |
155 | return true; |
156 | } |
157 | |
158 | static bool parseVectorLibArg(Fortran::frontend::CodeGenOptions &opts, |
159 | llvm::opt::ArgList &args, |
160 | clang::DiagnosticsEngine &diags) { |
161 | llvm::opt::Arg *arg = args.getLastArg(clang::driver::options::OPT_fveclib); |
162 | if (!arg) |
163 | return true; |
164 | |
165 | using VectorLibrary = llvm::driver::VectorLibrary; |
166 | std::optional<VectorLibrary> val = |
167 | llvm::StringSwitch<std::optional<VectorLibrary>>(arg->getValue()) |
168 | .Case("Accelerate" , VectorLibrary::Accelerate) |
169 | .Case("LIBMVEC" , VectorLibrary::LIBMVEC) |
170 | .Case("MASSV" , VectorLibrary::MASSV) |
171 | .Case("SVML" , VectorLibrary::SVML) |
172 | .Case("SLEEF" , VectorLibrary::SLEEF) |
173 | .Case("Darwin_libsystem_m" , VectorLibrary::Darwin_libsystem_m) |
174 | .Case("ArmPL" , VectorLibrary::ArmPL) |
175 | .Case("NoLibrary" , VectorLibrary::NoLibrary) |
176 | .Default(std::nullopt); |
177 | if (!val.has_value()) { |
178 | diags.Report(clang::diag::err_drv_invalid_value) |
179 | << arg->getAsString(Args: args) << arg->getValue(); |
180 | return false; |
181 | } |
182 | opts.setVecLib(val.value()); |
183 | return true; |
184 | } |
185 | |
186 | // Generate an OptRemark object containing info on if the -Rgroup |
187 | // specified is enabled or not. |
188 | static CodeGenOptions::OptRemark |
189 | (clang::DiagnosticsEngine &diags, |
190 | llvm::opt::ArgList &args, llvm::opt::OptSpecifier optEq, |
191 | llvm::StringRef ) { |
192 | assert((remarkOptName == "pass" || remarkOptName == "pass-missed" || |
193 | remarkOptName == "pass-analysis" ) && |
194 | "Unsupported remark option name provided." ); |
195 | CodeGenOptions::OptRemark result; |
196 | |
197 | for (llvm::opt::Arg *a : args) { |
198 | if (a->getOption().matches(clang::driver::options::ID: OPT_R_Joined)) { |
199 | llvm::StringRef value = a->getValue(); |
200 | |
201 | if (value == remarkOptName) { |
202 | result.Kind = CodeGenOptions::RemarkKind::RK_Enabled; |
203 | // Enable everything |
204 | result.Pattern = ".*" ; |
205 | result.Regex = std::make_shared<llvm::Regex>(result.Pattern); |
206 | |
207 | } else if (value.split(Separator: '-') == |
208 | std::make_pair(x: llvm::StringRef("no" ), y&: remarkOptName)) { |
209 | result.Kind = CodeGenOptions::RemarkKind::RK_Disabled; |
210 | // Disable everything |
211 | result.Pattern = "" ; |
212 | result.Regex = nullptr; |
213 | } |
214 | } else if (a->getOption().matches(ID: optEq)) { |
215 | result.Kind = CodeGenOptions::RemarkKind::RK_WithPattern; |
216 | result.Pattern = a->getValue(); |
217 | result.Regex = std::make_shared<llvm::Regex>(result.Pattern); |
218 | std::string regexError; |
219 | |
220 | if (!result.Regex->isValid(regexError)) { |
221 | diags.Report(clang::diag::err_drv_optimization_remark_pattern) |
222 | << regexError << a->getAsString(Args: args); |
223 | return CodeGenOptions::OptRemark(); |
224 | } |
225 | } |
226 | } |
227 | return result; |
228 | } |
229 | |
230 | static void parseCodeGenArgs(Fortran::frontend::CodeGenOptions &opts, |
231 | llvm::opt::ArgList &args, |
232 | clang::DiagnosticsEngine &diags) { |
233 | opts.OptimizationLevel = getOptimizationLevel(args, diags); |
234 | |
235 | if (args.hasFlag(clang::driver::options::OPT_fdebug_pass_manager, |
236 | clang::driver::options::OPT_fno_debug_pass_manager, false)) |
237 | opts.DebugPassManager = 1; |
238 | |
239 | if (args.hasFlag(clang::driver::options::OPT_fstack_arrays, |
240 | clang::driver::options::OPT_fno_stack_arrays, false)) |
241 | opts.StackArrays = 1; |
242 | |
243 | if (args.hasFlag(clang::driver::options::OPT_floop_versioning, |
244 | clang::driver::options::OPT_fno_loop_versioning, false)) |
245 | opts.LoopVersioning = 1; |
246 | |
247 | opts.AliasAnalysis = opts.OptimizationLevel > 0; |
248 | |
249 | // -mframe-pointer=none/non-leaf/all option. |
250 | if (const llvm::opt::Arg *a = |
251 | args.getLastArg(clang::driver::options::OPT_mframe_pointer_EQ)) { |
252 | std::optional<llvm::FramePointerKind> val = |
253 | llvm::StringSwitch<std::optional<llvm::FramePointerKind>>(a->getValue()) |
254 | .Case("none" , llvm::FramePointerKind::None) |
255 | .Case("non-leaf" , llvm::FramePointerKind::NonLeaf) |
256 | .Case("all" , llvm::FramePointerKind::All) |
257 | .Default(std::nullopt); |
258 | |
259 | if (!val.has_value()) { |
260 | diags.Report(clang::diag::err_drv_invalid_value) |
261 | << a->getAsString(args) << a->getValue(); |
262 | } else |
263 | opts.setFramePointer(val.value()); |
264 | } |
265 | |
266 | for (auto *a : args.filtered(clang::driver::options::OPT_fpass_plugin_EQ)) |
267 | opts.LLVMPassPlugins.push_back(a->getValue()); |
268 | |
269 | // -fembed-offload-object option |
270 | for (auto *a : |
271 | args.filtered(clang::driver::options::OPT_fembed_offload_object_EQ)) |
272 | opts.OffloadObjects.push_back(a->getValue()); |
273 | |
274 | // -flto=full/thin option. |
275 | if (const llvm::opt::Arg *a = |
276 | args.getLastArg(clang::driver::options::OPT_flto_EQ)) { |
277 | llvm::StringRef s = a->getValue(); |
278 | assert((s == "full" || s == "thin" ) && "Unknown LTO mode." ); |
279 | if (s == "full" ) |
280 | opts.PrepareForFullLTO = true; |
281 | else |
282 | opts.PrepareForThinLTO = true; |
283 | } |
284 | |
285 | if (const llvm::opt::Arg *a = args.getLastArg( |
286 | clang::driver::options::OPT_mcode_object_version_EQ)) { |
287 | llvm::StringRef s = a->getValue(); |
288 | if (s == "6" ) |
289 | opts.CodeObjectVersion = llvm::CodeObjectVersionKind::COV_6; |
290 | if (s == "5" ) |
291 | opts.CodeObjectVersion = llvm::CodeObjectVersionKind::COV_5; |
292 | if (s == "4" ) |
293 | opts.CodeObjectVersion = llvm::CodeObjectVersionKind::COV_4; |
294 | if (s == "none" ) |
295 | opts.CodeObjectVersion = llvm::CodeObjectVersionKind::COV_None; |
296 | } |
297 | |
298 | // -f[no-]save-optimization-record[=<format>] |
299 | if (const llvm::opt::Arg *a = |
300 | args.getLastArg(clang::driver::options::OPT_opt_record_file)) |
301 | opts.OptRecordFile = a->getValue(); |
302 | |
303 | // Optimization file format. Defaults to yaml |
304 | if (const llvm::opt::Arg *a = |
305 | args.getLastArg(clang::driver::options::OPT_opt_record_format)) |
306 | opts.OptRecordFormat = a->getValue(); |
307 | |
308 | // Specifies, using a regex, which successful optimization passes(middle and |
309 | // backend), to include in the final optimization record file generated. If |
310 | // not provided -fsave-optimization-record will include all passes. |
311 | if (const llvm::opt::Arg *a = |
312 | args.getLastArg(clang::driver::options::OPT_opt_record_passes)) |
313 | opts.OptRecordPasses = a->getValue(); |
314 | |
315 | // Create OptRemark that allows printing of all successful optimization |
316 | // passes applied. |
317 | opts.OptimizationRemark = |
318 | parseOptimizationRemark(diags, args, clang::driver::options::OPT_Rpass_EQ, |
319 | /*remarkOptName=*/"pass" ); |
320 | |
321 | // Create OptRemark that allows all missed optimization passes to be printed. |
322 | opts.OptimizationRemarkMissed = parseOptimizationRemark( |
323 | diags, args, clang::driver::options::OPT_Rpass_missed_EQ, |
324 | /*remarkOptName=*/"pass-missed" ); |
325 | |
326 | // Create OptRemark that allows all optimization decisions made by LLVM |
327 | // to be printed. |
328 | opts.OptimizationRemarkAnalysis = parseOptimizationRemark( |
329 | diags, args, clang::driver::options::OPT_Rpass_analysis_EQ, |
330 | /*remarkOptName=*/"pass-analysis" ); |
331 | |
332 | if (opts.getDebugInfo() == llvm::codegenoptions::NoDebugInfo) { |
333 | // If the user requested a flag that requires source locations available in |
334 | // the backend, make sure that the backend tracks source location |
335 | // information. |
336 | bool needLocTracking = !opts.OptRecordFile.empty() || |
337 | !opts.OptRecordPasses.empty() || |
338 | !opts.OptRecordFormat.empty() || |
339 | opts.OptimizationRemark.hasValidPattern() || |
340 | opts.OptimizationRemarkMissed.hasValidPattern() || |
341 | opts.OptimizationRemarkAnalysis.hasValidPattern(); |
342 | |
343 | if (needLocTracking) |
344 | opts.setDebugInfo(llvm::codegenoptions::LocTrackingOnly); |
345 | } |
346 | |
347 | if (auto *a = args.getLastArg(clang::driver::options::OPT_save_temps_EQ)) |
348 | opts.SaveTempsDir = a->getValue(); |
349 | |
350 | // -mrelocation-model option. |
351 | if (const llvm::opt::Arg *a = |
352 | args.getLastArg(clang::driver::options::OPT_mrelocation_model)) { |
353 | llvm::StringRef modelName = a->getValue(); |
354 | auto relocModel = |
355 | llvm::StringSwitch<std::optional<llvm::Reloc::Model>>(modelName) |
356 | .Case("static" , llvm::Reloc::Static) |
357 | .Case("pic" , llvm::Reloc::PIC_) |
358 | .Case("dynamic-no-pic" , llvm::Reloc::DynamicNoPIC) |
359 | .Case("ropi" , llvm::Reloc::ROPI) |
360 | .Case("rwpi" , llvm::Reloc::RWPI) |
361 | .Case("ropi-rwpi" , llvm::Reloc::ROPI_RWPI) |
362 | .Default(std::nullopt); |
363 | if (relocModel.has_value()) |
364 | opts.setRelocationModel(*relocModel); |
365 | else |
366 | diags.Report(clang::diag::err_drv_invalid_value) |
367 | << a->getAsString(args) << modelName; |
368 | } |
369 | |
370 | // -pic-level and -pic-is-pie option. |
371 | if (int picLevel = getLastArgIntValue( |
372 | args, clang::driver::options::OPT_pic_level, 0, diags)) { |
373 | if (picLevel > 2) |
374 | diags.Report(clang::diag::err_drv_invalid_value) |
375 | << args.getLastArg(clang::driver::options::OPT_pic_level) |
376 | ->getAsString(args) |
377 | << picLevel; |
378 | |
379 | opts.PICLevel = picLevel; |
380 | if (args.hasArg(clang::driver::options::OPT_pic_is_pie)) |
381 | opts.IsPIE = 1; |
382 | } |
383 | |
384 | // This option is compatible with -f[no-]underscoring in gfortran. |
385 | if (args.hasFlag(clang::driver::options::OPT_fno_underscoring, |
386 | clang::driver::options::OPT_funderscoring, false)) { |
387 | opts.Underscoring = 0; |
388 | } |
389 | } |
390 | |
391 | /// Parses all target input arguments and populates the target |
392 | /// options accordingly. |
393 | /// |
394 | /// \param [in] opts The target options instance to update |
395 | /// \param [in] args The list of input arguments (from the compiler invocation) |
396 | static void parseTargetArgs(TargetOptions &opts, llvm::opt::ArgList &args) { |
397 | if (const llvm::opt::Arg *a = |
398 | args.getLastArg(clang::driver::options::OPT_triple)) |
399 | opts.triple = a->getValue(); |
400 | |
401 | if (const llvm::opt::Arg *a = |
402 | args.getLastArg(clang::driver::options::OPT_target_cpu)) |
403 | opts.cpu = a->getValue(); |
404 | |
405 | for (const llvm::opt::Arg *currentArg : |
406 | args.filtered(clang::driver::options::OPT_target_feature)) |
407 | opts.featuresAsWritten.emplace_back(currentArg->getValue()); |
408 | } |
409 | |
410 | // Tweak the frontend configuration based on the frontend action |
411 | static void setUpFrontendBasedOnAction(FrontendOptions &opts) { |
412 | if (opts.programAction == DebugDumpParsingLog) |
413 | opts.instrumentedParse = true; |
414 | |
415 | if (opts.programAction == DebugDumpProvenance || |
416 | opts.programAction == Fortran::frontend::GetDefinition) |
417 | opts.needProvenanceRangeToCharBlockMappings = true; |
418 | } |
419 | |
420 | /// Parse the argument specified for the -fconvert=<value> option |
421 | static std::optional<const char *> parseConvertArg(const char *s) { |
422 | return llvm::StringSwitch<std::optional<const char *>>(s) |
423 | .Case(S: "unknown" , Value: "UNKNOWN" ) |
424 | .Case(S: "native" , Value: "NATIVE" ) |
425 | .Case(S: "little-endian" , Value: "LITTLE_ENDIAN" ) |
426 | .Case(S: "big-endian" , Value: "BIG_ENDIAN" ) |
427 | .Case(S: "swap" , Value: "SWAP" ) |
428 | .Default(Value: std::nullopt); |
429 | } |
430 | |
431 | static bool parseFrontendArgs(FrontendOptions &opts, llvm::opt::ArgList &args, |
432 | clang::DiagnosticsEngine &diags) { |
433 | unsigned numErrorsBefore = diags.getNumErrors(); |
434 | |
435 | // By default the frontend driver creates a ParseSyntaxOnly action. |
436 | opts.programAction = ParseSyntaxOnly; |
437 | |
438 | // Treat multiple action options as an invocation error. Note that `clang |
439 | // -cc1` does accept multiple action options, but will only consider the |
440 | // rightmost one. |
441 | if (args.hasMultipleArgs(clang::driver::options::OPT_Action_Group)) { |
442 | const unsigned diagID = diags.getCustomDiagID( |
443 | L: clang::DiagnosticsEngine::Error, FormatString: "Only one action option is allowed" ); |
444 | diags.Report(DiagID: diagID); |
445 | return false; |
446 | } |
447 | |
448 | // Identify the action (i.e. opts.ProgramAction) |
449 | if (const llvm::opt::Arg *a = |
450 | args.getLastArg(clang::driver::options::OPT_Action_Group)) { |
451 | switch (a->getOption().getID()) { |
452 | default: { |
453 | llvm_unreachable("Invalid option in group!" ); |
454 | } |
455 | case clang::driver::options::OPT_test_io: |
456 | opts.programAction = InputOutputTest; |
457 | break; |
458 | case clang::driver::options::OPT_E: |
459 | opts.programAction = PrintPreprocessedInput; |
460 | break; |
461 | case clang::driver::options::OPT_fsyntax_only: |
462 | opts.programAction = ParseSyntaxOnly; |
463 | break; |
464 | case clang::driver::options::OPT_emit_fir: |
465 | opts.programAction = EmitFIR; |
466 | break; |
467 | case clang::driver::options::OPT_emit_hlfir: |
468 | opts.programAction = EmitHLFIR; |
469 | break; |
470 | case clang::driver::options::OPT_emit_llvm: |
471 | opts.programAction = EmitLLVM; |
472 | break; |
473 | case clang::driver::options::OPT_emit_llvm_bc: |
474 | opts.programAction = EmitLLVMBitcode; |
475 | break; |
476 | case clang::driver::options::OPT_emit_obj: |
477 | opts.programAction = EmitObj; |
478 | break; |
479 | case clang::driver::options::OPT_S: |
480 | opts.programAction = EmitAssembly; |
481 | break; |
482 | case clang::driver::options::OPT_fdebug_unparse: |
483 | opts.programAction = DebugUnparse; |
484 | break; |
485 | case clang::driver::options::OPT_fdebug_unparse_no_sema: |
486 | opts.programAction = DebugUnparseNoSema; |
487 | break; |
488 | case clang::driver::options::OPT_fdebug_unparse_with_symbols: |
489 | opts.programAction = DebugUnparseWithSymbols; |
490 | break; |
491 | case clang::driver::options::OPT_fdebug_dump_symbols: |
492 | opts.programAction = DebugDumpSymbols; |
493 | break; |
494 | case clang::driver::options::OPT_fdebug_dump_parse_tree: |
495 | opts.programAction = DebugDumpParseTree; |
496 | break; |
497 | case clang::driver::options::OPT_fdebug_dump_pft: |
498 | opts.programAction = DebugDumpPFT; |
499 | break; |
500 | case clang::driver::options::OPT_fdebug_dump_all: |
501 | opts.programAction = DebugDumpAll; |
502 | break; |
503 | case clang::driver::options::OPT_fdebug_dump_parse_tree_no_sema: |
504 | opts.programAction = DebugDumpParseTreeNoSema; |
505 | break; |
506 | case clang::driver::options::OPT_fdebug_dump_provenance: |
507 | opts.programAction = DebugDumpProvenance; |
508 | break; |
509 | case clang::driver::options::OPT_fdebug_dump_parsing_log: |
510 | opts.programAction = DebugDumpParsingLog; |
511 | break; |
512 | case clang::driver::options::OPT_fdebug_measure_parse_tree: |
513 | opts.programAction = DebugMeasureParseTree; |
514 | break; |
515 | case clang::driver::options::OPT_fdebug_pre_fir_tree: |
516 | opts.programAction = DebugPreFIRTree; |
517 | break; |
518 | case clang::driver::options::OPT_fget_symbols_sources: |
519 | opts.programAction = GetSymbolsSources; |
520 | break; |
521 | case clang::driver::options::OPT_fget_definition: |
522 | opts.programAction = GetDefinition; |
523 | break; |
524 | case clang::driver::options::OPT_init_only: |
525 | opts.programAction = InitOnly; |
526 | break; |
527 | |
528 | // TODO: |
529 | // case clang::driver::options::OPT_emit_llvm: |
530 | // case clang::driver::options::OPT_emit_llvm_only: |
531 | // case clang::driver::options::OPT_emit_codegen_only: |
532 | // case clang::driver::options::OPT_emit_module: |
533 | // (...) |
534 | } |
535 | |
536 | // Parse the values provided with `-fget-definition` (there should be 3 |
537 | // integers) |
538 | if (llvm::opt::OptSpecifier(a->getOption().getID()) == |
539 | clang::driver::options::OPT_fget_definition) { |
540 | unsigned optVals[3] = {0, 0, 0}; |
541 | |
542 | for (unsigned i = 0; i < 3; i++) { |
543 | llvm::StringRef val = a->getValue(N: i); |
544 | |
545 | if (val.getAsInteger(Radix: 10, Result&: optVals[i])) { |
546 | // A non-integer was encountered - that's an error. |
547 | diags.Report(clang::diag::err_drv_invalid_value) |
548 | << a->getOption().getName() << val; |
549 | break; |
550 | } |
551 | } |
552 | opts.getDefVals.line = optVals[0]; |
553 | opts.getDefVals.startColumn = optVals[1]; |
554 | opts.getDefVals.endColumn = optVals[2]; |
555 | } |
556 | } |
557 | |
558 | // Parsing -load <dsopath> option and storing shared object path |
559 | if (llvm::opt::Arg *a = args.getLastArg(clang::driver::options::OPT_load)) { |
560 | opts.plugins.push_back(a->getValue()); |
561 | } |
562 | |
563 | // Parsing -plugin <name> option and storing plugin name and setting action |
564 | if (const llvm::opt::Arg *a = |
565 | args.getLastArg(clang::driver::options::OPT_plugin)) { |
566 | opts.programAction = PluginAction; |
567 | opts.actionName = a->getValue(); |
568 | } |
569 | |
570 | opts.outputFile = args.getLastArgValue(clang::driver::options::OPT_o); |
571 | opts.showHelp = args.hasArg(clang::driver::options::OPT_help); |
572 | opts.showVersion = args.hasArg(clang::driver::options::OPT_version); |
573 | |
574 | // Get the input kind (from the value passed via `-x`) |
575 | InputKind dashX(Language::Unknown); |
576 | if (const llvm::opt::Arg *a = |
577 | args.getLastArg(clang::driver::options::OPT_x)) { |
578 | llvm::StringRef xValue = a->getValue(); |
579 | // Principal languages. |
580 | dashX = llvm::StringSwitch<InputKind>(xValue) |
581 | // Flang does not differentiate between pre-processed and not |
582 | // pre-processed inputs. |
583 | .Case("f95" , Language::Fortran) |
584 | .Case("f95-cpp-input" , Language::Fortran) |
585 | // CUDA Fortran |
586 | .Case("cuda" , Language::Fortran) |
587 | .Default(Language::Unknown); |
588 | |
589 | // Flang's intermediate representations. |
590 | if (dashX.isUnknown()) |
591 | dashX = llvm::StringSwitch<InputKind>(xValue) |
592 | .Case("ir" , Language::LLVM_IR) |
593 | .Case("fir" , Language::MLIR) |
594 | .Case("mlir" , Language::MLIR) |
595 | .Default(Language::Unknown); |
596 | |
597 | if (dashX.isUnknown()) |
598 | diags.Report(clang::diag::err_drv_invalid_value) |
599 | << a->getAsString(args) << a->getValue(); |
600 | } |
601 | |
602 | // Collect the input files and save them in our instance of FrontendOptions. |
603 | std::vector<std::string> inputs = |
604 | args.getAllArgValues(clang::driver::options::OPT_INPUT); |
605 | opts.inputs.clear(); |
606 | if (inputs.empty()) |
607 | // '-' is the default input if none is given. |
608 | inputs.push_back(x: "-" ); |
609 | for (unsigned i = 0, e = inputs.size(); i != e; ++i) { |
610 | InputKind ik = dashX; |
611 | if (ik.isUnknown()) { |
612 | ik = FrontendOptions::getInputKindForExtension( |
613 | llvm::StringRef(inputs[i]).rsplit('.').second); |
614 | if (ik.isUnknown()) |
615 | ik = Language::Unknown; |
616 | if (i == 0) |
617 | dashX = ik; |
618 | } |
619 | |
620 | opts.inputs.emplace_back(std::move(inputs[i]), ik); |
621 | } |
622 | |
623 | // Set fortranForm based on options -ffree-form and -ffixed-form. |
624 | if (const auto *arg = |
625 | args.getLastArg(clang::driver::options::OPT_ffixed_form, |
626 | clang::driver::options::OPT_ffree_form)) { |
627 | opts.fortranForm = |
628 | arg->getOption().matches(clang::driver::options::OPT_ffixed_form) |
629 | ? FortranForm::FixedForm |
630 | : FortranForm::FreeForm; |
631 | } |
632 | |
633 | // Set fixedFormColumns based on -ffixed-line-length=<value> |
634 | if (const auto *arg = |
635 | args.getLastArg(clang::driver::options::OPT_ffixed_line_length_EQ)) { |
636 | llvm::StringRef argValue = llvm::StringRef(arg->getValue()); |
637 | std::int64_t columns = -1; |
638 | if (argValue == "none" ) { |
639 | columns = 0; |
640 | } else if (argValue.getAsInteger(/*Radix=*/10, Result&: columns)) { |
641 | columns = -1; |
642 | } |
643 | if (columns < 0) { |
644 | diags.Report(clang::diag::err_drv_negative_columns) |
645 | << arg->getOption().getName() << arg->getValue(); |
646 | } else if (columns == 0) { |
647 | opts.fixedFormColumns = 1000000; |
648 | } else if (columns < 7) { |
649 | diags.Report(clang::diag::err_drv_small_columns) |
650 | << arg->getOption().getName() << arg->getValue() << "7" ; |
651 | } else { |
652 | opts.fixedFormColumns = columns; |
653 | } |
654 | } |
655 | |
656 | // Set conversion based on -fconvert=<value> |
657 | if (const auto *arg = |
658 | args.getLastArg(clang::driver::options::OPT_fconvert_EQ)) { |
659 | const char *argValue = arg->getValue(); |
660 | if (auto convert = parseConvertArg(argValue)) |
661 | opts.envDefaults.push_back({"FORT_CONVERT" , *convert}); |
662 | else |
663 | diags.Report(clang::diag::err_drv_invalid_value) |
664 | << arg->getAsString(args) << argValue; |
665 | } |
666 | |
667 | // -f{no-}implicit-none |
668 | opts.features.Enable( |
669 | Fortran::common::LanguageFeature::ImplicitNoneTypeAlways, |
670 | args.hasFlag(clang::driver::options::OPT_fimplicit_none, |
671 | clang::driver::options::OPT_fno_implicit_none, false)); |
672 | |
673 | // -f{no-}backslash |
674 | opts.features.Enable(Fortran::common::LanguageFeature::BackslashEscapes, |
675 | args.hasFlag(clang::driver::options::OPT_fbackslash, |
676 | clang::driver::options::OPT_fno_backslash, |
677 | false)); |
678 | |
679 | // -f{no-}logical-abbreviations |
680 | opts.features.Enable( |
681 | Fortran::common::LanguageFeature::LogicalAbbreviations, |
682 | args.hasFlag(clang::driver::options::OPT_flogical_abbreviations, |
683 | clang::driver::options::OPT_fno_logical_abbreviations, |
684 | false)); |
685 | |
686 | // -f{no-}xor-operator |
687 | opts.features.Enable( |
688 | Fortran::common::LanguageFeature::XOROperator, |
689 | args.hasFlag(clang::driver::options::OPT_fxor_operator, |
690 | clang::driver::options::OPT_fno_xor_operator, false)); |
691 | |
692 | // -fno-automatic |
693 | if (args.hasArg(clang::driver::options::OPT_fno_automatic)) { |
694 | opts.features.Enable(Fortran::common::LanguageFeature::DefaultSave); |
695 | } |
696 | |
697 | if (args.hasArg( |
698 | clang::driver::options::OPT_falternative_parameter_statement)) { |
699 | opts.features.Enable(Fortran::common::LanguageFeature::OldStyleParameter); |
700 | } |
701 | if (const llvm::opt::Arg *arg = |
702 | args.getLastArg(clang::driver::options::OPT_finput_charset_EQ)) { |
703 | llvm::StringRef argValue = arg->getValue(); |
704 | if (argValue == "utf-8" ) { |
705 | opts.encoding = Fortran::parser::Encoding::UTF_8; |
706 | } else if (argValue == "latin-1" ) { |
707 | opts.encoding = Fortran::parser::Encoding::LATIN_1; |
708 | } else { |
709 | diags.Report(clang::diag::err_drv_invalid_value) |
710 | << arg->getAsString(args) << argValue; |
711 | } |
712 | } |
713 | |
714 | setUpFrontendBasedOnAction(opts); |
715 | opts.dashX = dashX; |
716 | |
717 | return diags.getNumErrors() == numErrorsBefore; |
718 | } |
719 | |
720 | // Generate the path to look for intrinsic modules |
721 | static std::string getIntrinsicDir(const char *argv) { |
722 | // TODO: Find a system independent API |
723 | llvm::SmallString<128> driverPath; |
724 | driverPath.assign(RHS: llvm::sys::fs::getMainExecutable(argv0: argv, MainExecAddr: nullptr)); |
725 | llvm::sys::path::remove_filename(path&: driverPath); |
726 | driverPath.append(RHS: "/../include/flang/" ); |
727 | return std::string(driverPath); |
728 | } |
729 | |
730 | // Generate the path to look for OpenMP headers |
731 | static std::string (const char *argv) { |
732 | llvm::SmallString<128> includePath; |
733 | includePath.assign(RHS: llvm::sys::fs::getMainExecutable(argv0: argv, MainExecAddr: nullptr)); |
734 | llvm::sys::path::remove_filename(path&: includePath); |
735 | includePath.append(RHS: "/../include/flang/OpenMP/" ); |
736 | return std::string(includePath); |
737 | } |
738 | |
739 | /// Parses all preprocessor input arguments and populates the preprocessor |
740 | /// options accordingly. |
741 | /// |
742 | /// \param [in] opts The preprocessor options instance |
743 | /// \param [out] args The list of input arguments |
744 | static void parsePreprocessorArgs(Fortran::frontend::PreprocessorOptions &opts, |
745 | llvm::opt::ArgList &args) { |
746 | // Add macros from the command line. |
747 | for (const auto *currentArg : args.filtered(clang::driver::options::OPT_D, |
748 | clang::driver::options::OPT_U)) { |
749 | if (currentArg->getOption().matches(clang::driver::options::OPT_D)) { |
750 | opts.addMacroDef(currentArg->getValue()); |
751 | } else { |
752 | opts.addMacroUndef(currentArg->getValue()); |
753 | } |
754 | } |
755 | |
756 | // Add the ordered list of -I's. |
757 | for (const auto *currentArg : args.filtered(clang::driver::options::OPT_I)) |
758 | opts.searchDirectoriesFromDashI.emplace_back(currentArg->getValue()); |
759 | |
760 | // Prepend the ordered list of -intrinsic-modules-path |
761 | // to the default location to search. |
762 | for (const auto *currentArg : |
763 | args.filtered(clang::driver::options::OPT_fintrinsic_modules_path)) |
764 | opts.searchDirectoriesFromIntrModPath.emplace_back(currentArg->getValue()); |
765 | |
766 | // -cpp/-nocpp |
767 | if (const auto *currentArg = args.getLastArg( |
768 | clang::driver::options::OPT_cpp, clang::driver::options::OPT_nocpp)) |
769 | opts.macrosFlag = |
770 | (currentArg->getOption().matches(clang::driver::options::OPT_cpp)) |
771 | ? PPMacrosFlag::Include |
772 | : PPMacrosFlag::Exclude; |
773 | |
774 | opts.noReformat = args.hasArg(clang::driver::options::OPT_fno_reformat); |
775 | opts.noLineDirectives = args.hasArg(clang::driver::options::OPT_P); |
776 | opts.showMacros = args.hasArg(clang::driver::options::OPT_dM); |
777 | } |
778 | |
779 | /// Parses all semantic related arguments and populates the variables |
780 | /// options accordingly. Returns false if new errors are generated. |
781 | static bool parseSemaArgs(CompilerInvocation &res, llvm::opt::ArgList &args, |
782 | clang::DiagnosticsEngine &diags) { |
783 | unsigned numErrorsBefore = diags.getNumErrors(); |
784 | |
785 | // -J/module-dir option |
786 | auto moduleDirList = |
787 | args.getAllArgValues(clang::driver::options::OPT_module_dir); |
788 | // User can only specify -J/-module-dir once |
789 | // https://gcc.gnu.org/onlinedocs/gfortran/Directory-Options.html |
790 | if (moduleDirList.size() > 1) { |
791 | const unsigned diagID = |
792 | diags.getCustomDiagID(L: clang::DiagnosticsEngine::Error, |
793 | FormatString: "Only one '-module-dir/-J' option allowed" ); |
794 | diags.Report(DiagID: diagID); |
795 | } |
796 | if (moduleDirList.size() == 1) |
797 | res.setModuleDir(moduleDirList[0]); |
798 | |
799 | // -fdebug-module-writer option |
800 | if (args.hasArg(clang::driver::options::OPT_fdebug_module_writer)) { |
801 | res.setDebugModuleDir(true); |
802 | } |
803 | |
804 | // -module-suffix |
805 | if (const auto *moduleSuffix = |
806 | args.getLastArg(clang::driver::options::OPT_module_suffix)) { |
807 | res.setModuleFileSuffix(moduleSuffix->getValue()); |
808 | } |
809 | |
810 | // -f{no-}analyzed-objects-for-unparse |
811 | res.setUseAnalyzedObjectsForUnparse(args.hasFlag( |
812 | clang::driver::options::OPT_fanalyzed_objects_for_unparse, |
813 | clang::driver::options::OPT_fno_analyzed_objects_for_unparse, true)); |
814 | |
815 | return diags.getNumErrors() == numErrorsBefore; |
816 | } |
817 | |
818 | /// Parses all diagnostics related arguments and populates the variables |
819 | /// options accordingly. Returns false if new errors are generated. |
820 | static bool parseDiagArgs(CompilerInvocation &res, llvm::opt::ArgList &args, |
821 | clang::DiagnosticsEngine &diags) { |
822 | unsigned numErrorsBefore = diags.getNumErrors(); |
823 | |
824 | // -Werror option |
825 | // TODO: Currently throws a Diagnostic for anything other than -W<error>, |
826 | // this has to change when other -W<opt>'s are supported. |
827 | if (args.hasArg(clang::driver::options::OPT_W_Joined)) { |
828 | const auto &wArgs = |
829 | args.getAllArgValues(clang::driver::options::OPT_W_Joined); |
830 | for (const auto &wArg : wArgs) { |
831 | if (wArg == "error" ) { |
832 | res.setWarnAsErr(true); |
833 | } else { |
834 | const unsigned diagID = |
835 | diags.getCustomDiagID(clang::DiagnosticsEngine::Error, |
836 | "Only `-Werror` is supported currently." ); |
837 | diags.Report(diagID); |
838 | } |
839 | } |
840 | } |
841 | |
842 | // Default to off for `flang-new -fc1`. |
843 | res.getFrontendOpts().showColors = |
844 | parseShowColorsArgs(args, /*defaultDiagColor=*/defaultColor: false); |
845 | |
846 | // Honor color diagnostics. |
847 | res.getDiagnosticOpts().ShowColors = res.getFrontendOpts().showColors; |
848 | |
849 | return diags.getNumErrors() == numErrorsBefore; |
850 | } |
851 | |
852 | /// Parses all Dialect related arguments and populates the variables |
853 | /// options accordingly. Returns false if new errors are generated. |
854 | static bool parseDialectArgs(CompilerInvocation &res, llvm::opt::ArgList &args, |
855 | clang::DiagnosticsEngine &diags) { |
856 | unsigned numErrorsBefore = diags.getNumErrors(); |
857 | |
858 | // -fdefault* family |
859 | if (args.hasArg(clang::driver::options::OPT_fdefault_real_8)) { |
860 | res.getDefaultKinds().set_defaultRealKind(8); |
861 | res.getDefaultKinds().set_doublePrecisionKind(16); |
862 | } |
863 | if (args.hasArg(clang::driver::options::OPT_fdefault_integer_8)) { |
864 | res.getDefaultKinds().set_defaultIntegerKind(8); |
865 | res.getDefaultKinds().set_subscriptIntegerKind(8); |
866 | res.getDefaultKinds().set_sizeIntegerKind(8); |
867 | res.getDefaultKinds().set_defaultLogicalKind(8); |
868 | } |
869 | if (args.hasArg(clang::driver::options::OPT_fdefault_double_8)) { |
870 | if (!args.hasArg(clang::driver::options::OPT_fdefault_real_8)) { |
871 | // -fdefault-double-8 has to be used with -fdefault-real-8 |
872 | // to be compatible with gfortran |
873 | const unsigned diagID = diags.getCustomDiagID( |
874 | L: clang::DiagnosticsEngine::Error, |
875 | FormatString: "Use of `-fdefault-double-8` requires `-fdefault-real-8`" ); |
876 | diags.Report(DiagID: diagID); |
877 | } |
878 | // https://gcc.gnu.org/onlinedocs/gfortran/Fortran-Dialect-Options.html |
879 | res.getDefaultKinds().set_doublePrecisionKind(8); |
880 | } |
881 | if (args.hasArg(clang::driver::options::OPT_flarge_sizes)) |
882 | res.getDefaultKinds().set_sizeIntegerKind(8); |
883 | |
884 | // -x cuda |
885 | auto language = args.getLastArgValue(clang::driver::options::OPT_x); |
886 | if (language.equals("cuda" )) { |
887 | res.getFrontendOpts().features.Enable( |
888 | Fortran::common::LanguageFeature::CUDA); |
889 | } |
890 | |
891 | // -fopenmp and -fopenacc |
892 | if (args.hasArg(clang::driver::options::OPT_fopenacc)) { |
893 | res.getFrontendOpts().features.Enable( |
894 | Fortran::common::LanguageFeature::OpenACC); |
895 | } |
896 | if (args.hasArg(clang::driver::options::OPT_fopenmp)) { |
897 | // By default OpenMP is set to 1.1 version |
898 | res.getLangOpts().OpenMPVersion = 11; |
899 | res.getFrontendOpts().features.Enable( |
900 | Fortran::common::LanguageFeature::OpenMP); |
901 | if (int Version = getLastArgIntValue( |
902 | args, clang::driver::options::OPT_fopenmp_version_EQ, |
903 | res.getLangOpts().OpenMPVersion, diags)) { |
904 | res.getLangOpts().OpenMPVersion = Version; |
905 | } |
906 | if (args.hasArg(clang::driver::options::OPT_fopenmp_is_target_device)) { |
907 | res.getLangOpts().OpenMPIsTargetDevice = 1; |
908 | |
909 | // Get OpenMP host file path if any and report if a non existent file is |
910 | // found |
911 | if (auto *arg = args.getLastArg( |
912 | clang::driver::options::OPT_fopenmp_host_ir_file_path)) { |
913 | res.getLangOpts().OMPHostIRFile = arg->getValue(); |
914 | if (!llvm::sys::fs::exists(res.getLangOpts().OMPHostIRFile)) |
915 | diags.Report(clang::diag::err_drv_omp_host_ir_file_not_found) |
916 | << res.getLangOpts().OMPHostIRFile; |
917 | } |
918 | |
919 | if (args.hasFlag( |
920 | clang::driver::options::OPT_fopenmp_assume_teams_oversubscription, |
921 | clang::driver::options:: |
922 | OPT_fno_openmp_assume_teams_oversubscription, |
923 | /*Default=*/false)) |
924 | res.getLangOpts().OpenMPTeamSubscription = true; |
925 | |
926 | if (args.hasArg( |
927 | clang::driver::options::OPT_fopenmp_assume_no_thread_state)) |
928 | res.getLangOpts().OpenMPNoThreadState = 1; |
929 | |
930 | if (args.hasArg( |
931 | clang::driver::options::OPT_fopenmp_assume_no_nested_parallelism)) |
932 | res.getLangOpts().OpenMPNoNestedParallelism = 1; |
933 | |
934 | if (args.hasFlag(clang::driver::options:: |
935 | OPT_fopenmp_assume_threads_oversubscription, |
936 | clang::driver::options:: |
937 | OPT_fno_openmp_assume_threads_oversubscription, |
938 | /*Default=*/false)) |
939 | res.getLangOpts().OpenMPThreadSubscription = true; |
940 | |
941 | if ((args.hasArg(clang::driver::options::OPT_fopenmp_target_debug) || |
942 | args.hasArg(clang::driver::options::OPT_fopenmp_target_debug_EQ))) { |
943 | res.getLangOpts().OpenMPTargetDebug = getLastArgIntValue( |
944 | args, clang::driver::options::OPT_fopenmp_target_debug_EQ, |
945 | res.getLangOpts().OpenMPTargetDebug, diags); |
946 | |
947 | if (!res.getLangOpts().OpenMPTargetDebug && |
948 | args.hasArg(clang::driver::options::OPT_fopenmp_target_debug)) |
949 | res.getLangOpts().OpenMPTargetDebug = 1; |
950 | } |
951 | if (args.hasArg(clang::driver::options::OPT_nogpulib)) |
952 | res.getLangOpts().NoGPULib = 1; |
953 | } |
954 | |
955 | switch (llvm::Triple(res.getTargetOpts().triple).getArch()) { |
956 | case llvm::Triple::nvptx: |
957 | case llvm::Triple::nvptx64: |
958 | case llvm::Triple::amdgcn: |
959 | if (!res.getLangOpts().OpenMPIsTargetDevice) { |
960 | const unsigned diagID = diags.getCustomDiagID( |
961 | L: clang::DiagnosticsEngine::Error, |
962 | FormatString: "OpenMP AMDGPU/NVPTX is only prepared to deal with device code." ); |
963 | diags.Report(DiagID: diagID); |
964 | } |
965 | res.getLangOpts().OpenMPIsGPU = 1; |
966 | break; |
967 | default: |
968 | res.getLangOpts().OpenMPIsGPU = 0; |
969 | break; |
970 | } |
971 | } |
972 | |
973 | // -pedantic |
974 | if (args.hasArg(clang::driver::options::OPT_pedantic)) { |
975 | res.setEnableConformanceChecks(); |
976 | res.setEnableUsageChecks(); |
977 | } |
978 | // -std=f2018 |
979 | // TODO: Set proper options when more fortran standards |
980 | // are supported. |
981 | if (args.hasArg(clang::driver::options::OPT_std_EQ)) { |
982 | auto standard = args.getLastArgValue(clang::driver::options::OPT_std_EQ); |
983 | // We only allow f2018 as the given standard |
984 | if (standard.equals("f2018" )) { |
985 | res.setEnableConformanceChecks(); |
986 | } else { |
987 | const unsigned diagID = |
988 | diags.getCustomDiagID(L: clang::DiagnosticsEngine::Error, |
989 | FormatString: "Only -std=f2018 is allowed currently." ); |
990 | diags.Report(DiagID: diagID); |
991 | } |
992 | } |
993 | return diags.getNumErrors() == numErrorsBefore; |
994 | } |
995 | |
996 | /// Parses all floating point related arguments and populates the |
997 | /// CompilerInvocation accordingly. |
998 | /// Returns false if new errors are generated. |
999 | /// |
1000 | /// \param [out] invoc Stores the processed arguments |
1001 | /// \param [in] args The compiler invocation arguments to parse |
1002 | /// \param [out] diags DiagnosticsEngine to report erros with |
1003 | static bool parseFloatingPointArgs(CompilerInvocation &invoc, |
1004 | llvm::opt::ArgList &args, |
1005 | clang::DiagnosticsEngine &diags) { |
1006 | LangOptions &opts = invoc.getLangOpts(); |
1007 | |
1008 | if (const llvm::opt::Arg *a = |
1009 | args.getLastArg(clang::driver::options::OPT_ffp_contract)) { |
1010 | const llvm::StringRef val = a->getValue(); |
1011 | enum LangOptions::FPModeKind fpContractMode; |
1012 | |
1013 | if (val == "off" ) |
1014 | fpContractMode = LangOptions::FPM_Off; |
1015 | else if (val == "fast" ) |
1016 | fpContractMode = LangOptions::FPM_Fast; |
1017 | else { |
1018 | diags.Report(clang::diag::err_drv_unsupported_option_argument) |
1019 | << a->getSpelling() << val; |
1020 | return false; |
1021 | } |
1022 | |
1023 | opts.setFPContractMode(fpContractMode); |
1024 | } |
1025 | |
1026 | if (args.getLastArg(clang::driver::options::OPT_menable_no_infinities)) { |
1027 | opts.NoHonorInfs = true; |
1028 | } |
1029 | |
1030 | if (args.getLastArg(clang::driver::options::OPT_menable_no_nans)) { |
1031 | opts.NoHonorNaNs = true; |
1032 | } |
1033 | |
1034 | if (args.getLastArg(clang::driver::options::OPT_fapprox_func)) { |
1035 | opts.ApproxFunc = true; |
1036 | } |
1037 | |
1038 | if (args.getLastArg(clang::driver::options::OPT_fno_signed_zeros)) { |
1039 | opts.NoSignedZeros = true; |
1040 | } |
1041 | |
1042 | if (args.getLastArg(clang::driver::options::OPT_mreassociate)) { |
1043 | opts.AssociativeMath = true; |
1044 | } |
1045 | |
1046 | if (args.getLastArg(clang::driver::options::OPT_freciprocal_math)) { |
1047 | opts.ReciprocalMath = true; |
1048 | } |
1049 | |
1050 | if (args.getLastArg(clang::driver::options::OPT_ffast_math)) { |
1051 | opts.NoHonorInfs = true; |
1052 | opts.NoHonorNaNs = true; |
1053 | opts.AssociativeMath = true; |
1054 | opts.ReciprocalMath = true; |
1055 | opts.ApproxFunc = true; |
1056 | opts.NoSignedZeros = true; |
1057 | opts.setFPContractMode(LangOptions::FPM_Fast); |
1058 | } |
1059 | |
1060 | return true; |
1061 | } |
1062 | |
1063 | /// Parses vscale range options and populates the CompilerInvocation |
1064 | /// accordingly. |
1065 | /// Returns false if new errors are generated. |
1066 | /// |
1067 | /// \param [out] invoc Stores the processed arguments |
1068 | /// \param [in] args The compiler invocation arguments to parse |
1069 | /// \param [out] diags DiagnosticsEngine to report erros with |
1070 | static bool parseVScaleArgs(CompilerInvocation &invoc, llvm::opt::ArgList &args, |
1071 | clang::DiagnosticsEngine &diags) { |
1072 | const auto *vscaleMin = |
1073 | args.getLastArg(clang::driver::options::OPT_mvscale_min_EQ); |
1074 | const auto *vscaleMax = |
1075 | args.getLastArg(clang::driver::options::OPT_mvscale_max_EQ); |
1076 | |
1077 | if (!vscaleMin && !vscaleMax) |
1078 | return true; |
1079 | |
1080 | llvm::Triple triple = llvm::Triple(invoc.getTargetOpts().triple); |
1081 | if (!triple.isAArch64() && !triple.isRISCV()) { |
1082 | const unsigned diagID = |
1083 | diags.getCustomDiagID(L: clang::DiagnosticsEngine::Error, |
1084 | FormatString: "`-mvscale-max` and `-mvscale-min` are not " |
1085 | "supported for this architecture: %0" ); |
1086 | diags.Report(DiagID: diagID) << triple.getArchName(); |
1087 | return false; |
1088 | } |
1089 | |
1090 | LangOptions &opts = invoc.getLangOpts(); |
1091 | if (vscaleMin) { |
1092 | llvm::StringRef argValue = llvm::StringRef(vscaleMin->getValue()); |
1093 | unsigned vscaleMinVal; |
1094 | if (argValue.getAsInteger(/*Radix=*/10, Result&: vscaleMinVal)) { |
1095 | diags.Report(clang::diag::err_drv_unsupported_option_argument) |
1096 | << vscaleMax->getSpelling() << argValue; |
1097 | return false; |
1098 | } |
1099 | opts.VScaleMin = vscaleMinVal; |
1100 | } |
1101 | |
1102 | if (vscaleMax) { |
1103 | llvm::StringRef argValue = llvm::StringRef(vscaleMax->getValue()); |
1104 | unsigned vscaleMaxVal; |
1105 | if (argValue.getAsInteger(/*Radix=w*/ Radix: 10, Result&: vscaleMaxVal)) { |
1106 | diags.Report(clang::diag::err_drv_unsupported_option_argument) |
1107 | << vscaleMax->getSpelling() << argValue; |
1108 | return false; |
1109 | } |
1110 | opts.VScaleMax = vscaleMaxVal; |
1111 | } |
1112 | return true; |
1113 | } |
1114 | |
1115 | static bool parseLinkerOptionsArgs(CompilerInvocation &invoc, |
1116 | llvm::opt::ArgList &args, |
1117 | clang::DiagnosticsEngine &diags) { |
1118 | llvm::Triple triple = llvm::Triple(invoc.getTargetOpts().triple); |
1119 | |
1120 | // TODO: support --dependent-lib on other platforms when MLIR supports |
1121 | // !llvm.dependent.lib |
1122 | if (args.hasArg(clang::driver::options::OPT_dependent_lib) && |
1123 | !triple.isOSWindows()) { |
1124 | const unsigned diagID = |
1125 | diags.getCustomDiagID(L: clang::DiagnosticsEngine::Error, |
1126 | FormatString: "--dependent-lib is only supported on Windows" ); |
1127 | diags.Report(DiagID: diagID); |
1128 | return false; |
1129 | } |
1130 | |
1131 | invoc.getCodeGenOpts().DependentLibs = |
1132 | args.getAllArgValues(clang::driver::options::OPT_dependent_lib); |
1133 | return true; |
1134 | } |
1135 | |
1136 | bool CompilerInvocation::createFromArgs( |
1137 | CompilerInvocation &invoc, llvm::ArrayRef<const char *> commandLineArgs, |
1138 | clang::DiagnosticsEngine &diags, const char *argv0) { |
1139 | |
1140 | bool success = true; |
1141 | |
1142 | // Set the default triple for this CompilerInvocation. This might be |
1143 | // overridden by users with `-triple` (see the call to `ParseTargetArgs` |
1144 | // below). |
1145 | // NOTE: Like in Clang, it would be nice to use option marshalling |
1146 | // for this so that the entire logic for setting-up the triple is in one |
1147 | // place. |
1148 | invoc.getTargetOpts().triple = |
1149 | llvm::Triple::normalize(llvm::sys::getDefaultTargetTriple()); |
1150 | |
1151 | // Parse the arguments |
1152 | const llvm::opt::OptTable &opts = clang::driver::getDriverOptTable(); |
1153 | llvm::opt::Visibility visibilityMask(clang::driver::options::FC1Option); |
1154 | unsigned missingArgIndex, missingArgCount; |
1155 | llvm::opt::InputArgList args = opts.ParseArgs( |
1156 | commandLineArgs, missingArgIndex, missingArgCount, visibilityMask); |
1157 | |
1158 | // Check for missing argument error. |
1159 | if (missingArgCount) { |
1160 | diags.Report(clang::diag::err_drv_missing_argument) |
1161 | << args.getArgString(missingArgIndex) << missingArgCount; |
1162 | success = false; |
1163 | } |
1164 | |
1165 | // Issue errors on unknown arguments |
1166 | for (const auto *a : args.filtered(clang::driver::options::OPT_UNKNOWN)) { |
1167 | auto argString = a->getAsString(args); |
1168 | std::string nearest; |
1169 | if (opts.findNearest(argString, nearest, visibilityMask) > 1) |
1170 | diags.Report(clang::diag::err_drv_unknown_argument) << argString; |
1171 | else |
1172 | diags.Report(clang::diag::err_drv_unknown_argument_with_suggestion) |
1173 | << argString << nearest; |
1174 | success = false; |
1175 | } |
1176 | |
1177 | // -flang-experimental-hlfir |
1178 | if (args.hasArg(clang::driver::options::OPT_flang_experimental_hlfir) || |
1179 | args.hasArg(clang::driver::options::OPT_emit_hlfir)) { |
1180 | invoc.loweringOpts.setLowerToHighLevelFIR(true); |
1181 | } |
1182 | |
1183 | // -flang-deprecated-no-hlfir |
1184 | if (args.hasArg(clang::driver::options::OPT_flang_deprecated_no_hlfir) && |
1185 | !args.hasArg(clang::driver::options::OPT_emit_hlfir)) { |
1186 | if (args.hasArg(clang::driver::options::OPT_flang_experimental_hlfir)) { |
1187 | const unsigned diagID = diags.getCustomDiagID( |
1188 | clang::DiagnosticsEngine::Error, |
1189 | "Options '-flang-experimental-hlfir' and " |
1190 | "'-flang-deprecated-no-hlfir' cannot be both specified" ); |
1191 | diags.Report(diagID); |
1192 | } |
1193 | invoc.loweringOpts.setLowerToHighLevelFIR(false); |
1194 | } |
1195 | |
1196 | // -fno-ppc-native-vector-element-order |
1197 | if (args.hasArg(clang::driver::options::OPT_fno_ppc_native_vec_elem_order)) { |
1198 | invoc.loweringOpts.setNoPPCNativeVecElemOrder(true); |
1199 | } |
1200 | |
1201 | // Preserve all the remark options requested, i.e. -Rpass, -Rpass-missed or |
1202 | // -Rpass-analysis. This will be used later when processing and outputting the |
1203 | // remarks generated by LLVM in ExecuteCompilerInvocation.cpp. |
1204 | for (auto *a : args.filtered(clang::driver::options::OPT_R_Group)) { |
1205 | if (a->getOption().matches(clang::driver::options::OPT_R_value_Group)) |
1206 | // This is -Rfoo=, where foo is the name of the diagnostic |
1207 | // group. Add only the remark option name to the diagnostics. e.g. for |
1208 | // -Rpass= we will add the string "pass". |
1209 | invoc.getDiagnosticOpts().Remarks.push_back( |
1210 | std::string(a->getOption().getName().drop_front(1).rtrim("=-" ))); |
1211 | else |
1212 | // If no regex was provided, add the provided value, e.g. for -Rpass add |
1213 | // the string "pass". |
1214 | invoc.getDiagnosticOpts().Remarks.push_back(a->getValue()); |
1215 | } |
1216 | |
1217 | success &= parseFrontendArgs(invoc.getFrontendOpts(), args, diags); |
1218 | parseTargetArgs(invoc.getTargetOpts(), args); |
1219 | parsePreprocessorArgs(invoc.getPreprocessorOpts(), args); |
1220 | parseCodeGenArgs(invoc.getCodeGenOpts(), args, diags); |
1221 | success &= parseDebugArgs(invoc.getCodeGenOpts(), args, diags); |
1222 | success &= parseVectorLibArg(invoc.getCodeGenOpts(), args, diags); |
1223 | success &= parseSemaArgs(invoc, args, diags); |
1224 | success &= parseDialectArgs(invoc, args, diags); |
1225 | success &= parseDiagArgs(invoc, args, diags); |
1226 | |
1227 | // Collect LLVM (-mllvm) and MLIR (-mmlir) options. |
1228 | // NOTE: Try to avoid adding any options directly to `llvmArgs` or |
1229 | // `mlirArgs`. Instead, you can use |
1230 | // * `-mllvm <your-llvm-option>`, or |
1231 | // * `-mmlir <your-mlir-option>`. |
1232 | invoc.frontendOpts.llvmArgs = |
1233 | args.getAllArgValues(clang::driver::options::OPT_mllvm); |
1234 | invoc.frontendOpts.mlirArgs = |
1235 | args.getAllArgValues(clang::driver::options::OPT_mmlir); |
1236 | |
1237 | success &= parseFloatingPointArgs(invoc, args, diags); |
1238 | |
1239 | success &= parseVScaleArgs(invoc, args, diags); |
1240 | |
1241 | success &= parseLinkerOptionsArgs(invoc, args, diags); |
1242 | |
1243 | // Set the string to be used as the return value of the COMPILER_OPTIONS |
1244 | // intrinsic of iso_fortran_env. This is either passed in from the parent |
1245 | // compiler driver invocation with an environment variable, or failing that |
1246 | // set to the command line arguments of the frontend driver invocation. |
1247 | invoc.allCompilerInvocOpts = std::string(); |
1248 | llvm::raw_string_ostream os(invoc.allCompilerInvocOpts); |
1249 | char *compilerOptsEnv = std::getenv("FLANG_COMPILER_OPTIONS_STRING" ); |
1250 | if (compilerOptsEnv != nullptr) { |
1251 | os << compilerOptsEnv; |
1252 | } else { |
1253 | os << argv0 << ' '; |
1254 | for (auto it = commandLineArgs.begin(), e = commandLineArgs.end(); it != e; |
1255 | ++it) { |
1256 | os << ' ' << *it; |
1257 | } |
1258 | } |
1259 | |
1260 | invoc.setArgv0(argv0); |
1261 | |
1262 | return success; |
1263 | } |
1264 | |
1265 | void CompilerInvocation::collectMacroDefinitions() { |
1266 | auto &ppOpts = this->getPreprocessorOpts(); |
1267 | |
1268 | for (unsigned i = 0, n = ppOpts.macros.size(); i != n; ++i) { |
1269 | llvm::StringRef macro = ppOpts.macros[i].first; |
1270 | bool isUndef = ppOpts.macros[i].second; |
1271 | |
1272 | std::pair<llvm::StringRef, llvm::StringRef> macroPair = macro.split('='); |
1273 | llvm::StringRef macroName = macroPair.first; |
1274 | llvm::StringRef macroBody = macroPair.second; |
1275 | |
1276 | // For an #undef'd macro, we only care about the name. |
1277 | if (isUndef) { |
1278 | parserOpts.predefinitions.emplace_back(macroName.str(), |
1279 | std::optional<std::string>{}); |
1280 | continue; |
1281 | } |
1282 | |
1283 | // For a #define'd macro, figure out the actual definition. |
1284 | if (macroName.size() == macro.size()) |
1285 | macroBody = "1" ; |
1286 | else { |
1287 | // Note: GCC drops anything following an end-of-line character. |
1288 | llvm::StringRef::size_type end = macroBody.find_first_of("\n\r" ); |
1289 | macroBody = macroBody.substr(0, end); |
1290 | } |
1291 | parserOpts.predefinitions.emplace_back( |
1292 | macroName, std::optional<std::string>(macroBody.str())); |
1293 | } |
1294 | } |
1295 | |
1296 | void CompilerInvocation::setDefaultFortranOpts() { |
1297 | auto &fortranOptions = getFortranOpts(); |
1298 | |
1299 | std::vector<std::string> searchDirectories{"."s }; |
1300 | fortranOptions.searchDirectories = searchDirectories; |
1301 | |
1302 | // Add the location of omp_lib.h to the search directories. Currently this is |
1303 | // identical to the modules' directory. |
1304 | fortranOptions.searchDirectories.emplace_back( |
1305 | getOpenMPHeadersDir(getArgv0())); |
1306 | |
1307 | fortranOptions.isFixedForm = false; |
1308 | } |
1309 | |
1310 | // TODO: When expanding this method, consider creating a dedicated API for |
1311 | // this. Also at some point we will need to differentiate between different |
1312 | // targets and add dedicated predefines for each. |
1313 | void CompilerInvocation::setDefaultPredefinitions() { |
1314 | auto &fortranOptions = getFortranOpts(); |
1315 | const auto &frontendOptions = getFrontendOpts(); |
1316 | // Populate the macro list with version numbers and other predefinitions. |
1317 | fortranOptions.predefinitions.emplace_back("__flang__" , "1" ); |
1318 | fortranOptions.predefinitions.emplace_back("__flang_major__" , |
1319 | FLANG_VERSION_MAJOR_STRING); |
1320 | fortranOptions.predefinitions.emplace_back("__flang_minor__" , |
1321 | FLANG_VERSION_MINOR_STRING); |
1322 | fortranOptions.predefinitions.emplace_back("__flang_patchlevel__" , |
1323 | FLANG_VERSION_PATCHLEVEL_STRING); |
1324 | |
1325 | // Add predefinitions based on extensions enabled |
1326 | if (frontendOptions.features.IsEnabled( |
1327 | Fortran::common::LanguageFeature::OpenACC)) { |
1328 | fortranOptions.predefinitions.emplace_back("_OPENACC" , "202211" ); |
1329 | } |
1330 | if (frontendOptions.features.IsEnabled( |
1331 | Fortran::common::LanguageFeature::OpenMP)) { |
1332 | Fortran::common::setOpenMPMacro(getLangOpts().OpenMPVersion, |
1333 | fortranOptions.predefinitions); |
1334 | } |
1335 | |
1336 | llvm::Triple targetTriple{llvm::Triple(this->targetOpts.triple)}; |
1337 | if (targetTriple.isPPC()) { |
1338 | // '__powerpc__' is a generic macro for any PowerPC cases. e.g. Max integer |
1339 | // size. |
1340 | fortranOptions.predefinitions.emplace_back("__powerpc__" , "1" ); |
1341 | } |
1342 | if (targetTriple.isOSLinux()) { |
1343 | fortranOptions.predefinitions.emplace_back("__linux__" , "1" ); |
1344 | } |
1345 | |
1346 | switch (targetTriple.getArch()) { |
1347 | default: |
1348 | break; |
1349 | case llvm::Triple::ArchType::x86_64: |
1350 | fortranOptions.predefinitions.emplace_back("__x86_64__" , "1" ); |
1351 | fortranOptions.predefinitions.emplace_back("__x86_64" , "1" ); |
1352 | break; |
1353 | } |
1354 | } |
1355 | |
1356 | void CompilerInvocation::setFortranOpts() { |
1357 | auto &fortranOptions = getFortranOpts(); |
1358 | const auto &frontendOptions = getFrontendOpts(); |
1359 | const auto &preprocessorOptions = getPreprocessorOpts(); |
1360 | auto &moduleDirJ = getModuleDir(); |
1361 | |
1362 | if (frontendOptions.fortranForm != FortranForm::Unknown) { |
1363 | fortranOptions.isFixedForm = |
1364 | frontendOptions.fortranForm == FortranForm::FixedForm; |
1365 | } |
1366 | fortranOptions.fixedFormColumns = frontendOptions.fixedFormColumns; |
1367 | |
1368 | fortranOptions.features = frontendOptions.features; |
1369 | fortranOptions.encoding = frontendOptions.encoding; |
1370 | |
1371 | // Adding search directories specified by -I |
1372 | fortranOptions.searchDirectories.insert( |
1373 | fortranOptions.searchDirectories.end(), |
1374 | preprocessorOptions.searchDirectoriesFromDashI.begin(), |
1375 | preprocessorOptions.searchDirectoriesFromDashI.end()); |
1376 | |
1377 | // Add the ordered list of -intrinsic-modules-path |
1378 | fortranOptions.searchDirectories.insert( |
1379 | fortranOptions.searchDirectories.end(), |
1380 | preprocessorOptions.searchDirectoriesFromIntrModPath.begin(), |
1381 | preprocessorOptions.searchDirectoriesFromIntrModPath.end()); |
1382 | |
1383 | // Add the default intrinsic module directory |
1384 | fortranOptions.intrinsicModuleDirectories.emplace_back( |
1385 | getIntrinsicDir(getArgv0())); |
1386 | |
1387 | // Add the directory supplied through -J/-module-dir to the list of search |
1388 | // directories |
1389 | if (moduleDirJ != "." ) |
1390 | fortranOptions.searchDirectories.emplace_back(moduleDirJ); |
1391 | |
1392 | if (frontendOptions.instrumentedParse) |
1393 | fortranOptions.instrumentedParse = true; |
1394 | |
1395 | if (frontendOptions.showColors) |
1396 | fortranOptions.showColors = true; |
1397 | |
1398 | if (frontendOptions.needProvenanceRangeToCharBlockMappings) |
1399 | fortranOptions.needProvenanceRangeToCharBlockMappings = true; |
1400 | |
1401 | if (getEnableConformanceChecks()) |
1402 | fortranOptions.features.WarnOnAllNonstandard(); |
1403 | |
1404 | if (getEnableUsageChecks()) |
1405 | fortranOptions.features.WarnOnAllUsage(); |
1406 | } |
1407 | |
1408 | std::unique_ptr<Fortran::semantics::SemanticsContext> |
1409 | CompilerInvocation::getSemanticsCtx( |
1410 | Fortran::parser::AllCookedSources &allCookedSources, |
1411 | const llvm::TargetMachine &targetMachine) { |
1412 | auto &fortranOptions = getFortranOpts(); |
1413 | |
1414 | auto semanticsContext = std::make_unique<semantics::SemanticsContext>( |
1415 | getDefaultKinds(), fortranOptions.features, allCookedSources); |
1416 | |
1417 | semanticsContext->set_moduleDirectory(getModuleDir()) |
1418 | .set_searchDirectories(fortranOptions.searchDirectories) |
1419 | .set_intrinsicModuleDirectories(fortranOptions.intrinsicModuleDirectories) |
1420 | .set_warningsAreErrors(getWarnAsErr()) |
1421 | .set_moduleFileSuffix(getModuleFileSuffix()) |
1422 | .set_underscoring(getCodeGenOpts().Underscoring); |
1423 | |
1424 | std::string compilerVersion = Fortran::common::getFlangFullVersion(); |
1425 | Fortran::tools::setUpTargetCharacteristics( |
1426 | semanticsContext->targetCharacteristics(), targetMachine, compilerVersion, |
1427 | allCompilerInvocOpts); |
1428 | return semanticsContext; |
1429 | } |
1430 | |
1431 | /// Set \p loweringOptions controlling lowering behavior based |
1432 | /// on the \p optimizationLevel. |
1433 | void CompilerInvocation::setLoweringOptions() { |
1434 | const CodeGenOptions &codegenOpts = getCodeGenOpts(); |
1435 | |
1436 | // Lower TRANSPOSE as a runtime call under -O0. |
1437 | loweringOpts.setOptimizeTranspose(codegenOpts.OptimizationLevel > 0); |
1438 | loweringOpts.setUnderscoring(codegenOpts.Underscoring); |
1439 | |
1440 | const LangOptions &langOptions = getLangOpts(); |
1441 | Fortran::common::MathOptionsBase &mathOpts = loweringOpts.getMathOptions(); |
1442 | // TODO: when LangOptions are finalized, we can represent |
1443 | // the math related options using Fortran::commmon::MathOptionsBase, |
1444 | // so that we can just copy it into LoweringOptions. |
1445 | mathOpts |
1446 | .setFPContractEnabled(langOptions.getFPContractMode() == |
1447 | LangOptions::FPM_Fast) |
1448 | .setNoHonorInfs(langOptions.NoHonorInfs) |
1449 | .setNoHonorNaNs(langOptions.NoHonorNaNs) |
1450 | .setApproxFunc(langOptions.ApproxFunc) |
1451 | .setNoSignedZeros(langOptions.NoSignedZeros) |
1452 | .setAssociativeMath(langOptions.AssociativeMath) |
1453 | .setReciprocalMath(langOptions.ReciprocalMath); |
1454 | } |
1455 | |