1 //===-- Flang.cpp - Flang+LLVM ToolChain Implementations --------*- C++ -*-===//
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 
10 #include "Flang.h"
11 #include "CommonArgs.h"
12 
13 #include "clang/Driver/Options.h"
14 #include "llvm/Frontend/Debug/Options.h"
15 
16 #include <cassert>
17 
18 using namespace clang::driver;
19 using namespace clang::driver::tools;
20 using namespace clang;
21 using namespace llvm::opt;
22 
23 /// Add -x lang to \p CmdArgs for \p Input.
24 static void addDashXForInput(const ArgList &Args, const InputInfo &Input,
25                              ArgStringList &CmdArgs) {
26   CmdArgs.push_back("-x");
27   // Map the driver type to the frontend type.
28   CmdArgs.push_back(types::getTypeName(Input.getType()));
29 }
30 
31 void Flang::addFortranDialectOptions(const ArgList &Args,
32                                      ArgStringList &CmdArgs) const {
33   Args.AddAllArgs(CmdArgs, {options::OPT_ffixed_form,
34                             options::OPT_ffree_form,
35                             options::OPT_ffixed_line_length_EQ,
36                             options::OPT_fopenmp,
37                             options::OPT_fopenmp_version_EQ,
38                             options::OPT_fopenacc,
39                             options::OPT_finput_charset_EQ,
40                             options::OPT_fimplicit_none,
41                             options::OPT_fno_implicit_none,
42                             options::OPT_fbackslash,
43                             options::OPT_fno_backslash,
44                             options::OPT_flogical_abbreviations,
45                             options::OPT_fno_logical_abbreviations,
46                             options::OPT_fxor_operator,
47                             options::OPT_fno_xor_operator,
48                             options::OPT_falternative_parameter_statement,
49                             options::OPT_fdefault_real_8,
50                             options::OPT_fdefault_integer_8,
51                             options::OPT_fdefault_double_8,
52                             options::OPT_flarge_sizes,
53                             options::OPT_fno_automatic});
54 }
55 
56 void Flang::addPreprocessingOptions(const ArgList &Args,
57                                     ArgStringList &CmdArgs) const {
58   Args.AddAllArgs(CmdArgs,
59                   {options::OPT_P, options::OPT_D, options::OPT_U,
60                    options::OPT_I, options::OPT_cpp, options::OPT_nocpp});
61 }
62 
63 /// @C shouldLoopVersion
64 ///
65 /// Check if Loop Versioning should be enabled.
66 /// We look for the last of one of the following:
67 ///   -Ofast, -O4, -O<number> and -f[no-]version-loops-for-stride.
68 /// Loop versioning is disabled if the last option is
69 ///  -fno-version-loops-for-stride.
70 /// Loop versioning is enabled if the last option is one of:
71 ///  -floop-versioning
72 ///  -Ofast
73 ///  -O4
74 ///  -O3
75 /// For all other cases, loop versioning is is disabled.
76 ///
77 /// The gfortran compiler automatically enables the option for -O3 or -Ofast.
78 ///
79 /// @return true if loop-versioning should be enabled, otherwise false.
80 static bool shouldLoopVersion(const ArgList &Args) {
81   const Arg *LoopVersioningArg = Args.getLastArg(
82       options::OPT_Ofast, options::OPT_O, options::OPT_O4,
83       options::OPT_floop_versioning, options::OPT_fno_loop_versioning);
84   if (!LoopVersioningArg)
85     return false;
86 
87   if (LoopVersioningArg->getOption().matches(options::OPT_fno_loop_versioning))
88     return false;
89 
90   if (LoopVersioningArg->getOption().matches(options::OPT_floop_versioning))
91     return true;
92 
93   if (LoopVersioningArg->getOption().matches(options::OPT_Ofast) ||
94       LoopVersioningArg->getOption().matches(options::OPT_O4))
95     return true;
96 
97   if (LoopVersioningArg->getOption().matches(options::OPT_O)) {
98     StringRef S(LoopVersioningArg->getValue());
99     unsigned OptLevel = 0;
100     // Note -Os or Oz woould "fail" here, so return false. Which is the
101     // desiered behavior.
102     if (S.getAsInteger(10, OptLevel))
103       return false;
104 
105     return OptLevel > 2;
106   }
107 
108   llvm_unreachable("We should not end up here");
109   return false;
110 }
111 
112 void Flang::addOtherOptions(const ArgList &Args, ArgStringList &CmdArgs) const {
113   Args.AddAllArgs(CmdArgs,
114                   {options::OPT_module_dir, options::OPT_fdebug_module_writer,
115                    options::OPT_fintrinsic_modules_path, options::OPT_pedantic,
116                    options::OPT_std_EQ, options::OPT_W_Joined,
117                    options::OPT_fconvert_EQ, options::OPT_fpass_plugin_EQ,
118                    options::OPT_funderscoring, options::OPT_fno_underscoring});
119 
120   llvm::codegenoptions::DebugInfoKind DebugInfoKind;
121   if (Args.hasArg(options::OPT_gN_Group)) {
122     Arg *gNArg = Args.getLastArg(options::OPT_gN_Group);
123     DebugInfoKind = debugLevelToInfoKind(*gNArg);
124   } else if (Args.hasArg(options::OPT_g_Flag)) {
125     DebugInfoKind = llvm::codegenoptions::DebugLineTablesOnly;
126   } else {
127     DebugInfoKind = llvm::codegenoptions::NoDebugInfo;
128   }
129   addDebugInfoKind(CmdArgs, DebugInfoKind);
130 }
131 
132 void Flang::addCodegenOptions(const ArgList &Args,
133                               ArgStringList &CmdArgs) const {
134   Arg *stackArrays =
135       Args.getLastArg(options::OPT_Ofast, options::OPT_fstack_arrays,
136                       options::OPT_fno_stack_arrays);
137   if (stackArrays &&
138       !stackArrays->getOption().matches(options::OPT_fno_stack_arrays))
139     CmdArgs.push_back("-fstack-arrays");
140 
141   if (Args.hasArg(options::OPT_flang_experimental_hlfir))
142     CmdArgs.push_back("-flang-experimental-hlfir");
143   if (Args.hasArg(options::OPT_flang_experimental_polymorphism))
144     CmdArgs.push_back("-flang-experimental-polymorphism");
145   if (shouldLoopVersion(Args))
146     CmdArgs.push_back("-fversion-loops-for-stride");
147 }
148 
149 void Flang::addPicOptions(const ArgList &Args, ArgStringList &CmdArgs) const {
150   // ParsePICArgs parses -fPIC/-fPIE and their variants and returns a tuple of
151   // (RelocationModel, PICLevel, IsPIE).
152   llvm::Reloc::Model RelocationModel;
153   unsigned PICLevel;
154   bool IsPIE;
155   std::tie(RelocationModel, PICLevel, IsPIE) =
156       ParsePICArgs(getToolChain(), Args);
157 
158   if (auto *RMName = RelocationModelName(RelocationModel)) {
159     CmdArgs.push_back("-mrelocation-model");
160     CmdArgs.push_back(RMName);
161   }
162   if (PICLevel > 0) {
163     CmdArgs.push_back("-pic-level");
164     CmdArgs.push_back(PICLevel == 1 ? "1" : "2");
165     if (IsPIE)
166       CmdArgs.push_back("-pic-is-pie");
167   }
168 }
169 
170 void Flang::addTargetOptions(const ArgList &Args,
171                              ArgStringList &CmdArgs) const {
172   const ToolChain &TC = getToolChain();
173   const llvm::Triple &Triple = TC.getEffectiveTriple();
174   const Driver &D = TC.getDriver();
175 
176   std::string CPU = getCPUName(D, Args, Triple);
177   if (!CPU.empty()) {
178     CmdArgs.push_back("-target-cpu");
179     CmdArgs.push_back(Args.MakeArgString(CPU));
180   }
181 
182   // Add the target features.
183   switch (TC.getArch()) {
184   default:
185     break;
186   case llvm::Triple::r600:
187   case llvm::Triple::amdgcn:
188   case llvm::Triple::aarch64:
189   case llvm::Triple::riscv64:
190   case llvm::Triple::x86_64:
191     getTargetFeatures(D, Triple, Args, CmdArgs, /*ForAs*/ false);
192     break;
193   }
194 
195   // TODO: Add target specific flags, ABI, mtune option etc.
196 }
197 
198 void Flang::addOffloadOptions(Compilation &C, const InputInfoList &Inputs,
199                               const JobAction &JA, const ArgList &Args,
200                               ArgStringList &CmdArgs) const {
201   bool IsOpenMPDevice = JA.isDeviceOffloading(Action::OFK_OpenMP);
202   bool IsHostOffloadingAction = JA.isHostOffloading(Action::OFK_OpenMP) ||
203                                 JA.isHostOffloading(C.getActiveOffloadKinds());
204 
205   // Skips the primary input file, which is the input file that the compilation
206   // proccess will be executed upon (e.g. the host bitcode file) and
207   // adds other secondary input (e.g. device bitcode files for embedding to the
208   // -fembed-offload-object argument or the host IR file for proccessing
209   // during device compilation to the fopenmp-host-ir-file-path argument via
210   // OpenMPDeviceInput). This is condensed logic from the ConstructJob
211   // function inside of the Clang driver for pushing on further input arguments
212   // needed for offloading during various phases of compilation.
213   for (size_t i = 1; i < Inputs.size(); ++i) {
214     if (Inputs[i].getType() == types::TY_Nothing) {
215       // contains nothing, so it's skippable
216     } else if (IsHostOffloadingAction) {
217       CmdArgs.push_back(
218           Args.MakeArgString("-fembed-offload-object=" +
219                              getToolChain().getInputFilename(Inputs[i])));
220     } else if (IsOpenMPDevice) {
221       if (Inputs[i].getFilename()) {
222         CmdArgs.push_back("-fopenmp-host-ir-file-path");
223         CmdArgs.push_back(Args.MakeArgString(Inputs[i].getFilename()));
224       } else {
225         llvm_unreachable("missing openmp host-ir file for device offloading");
226       }
227     } else {
228       llvm_unreachable(
229           "unexpectedly given multiple inputs or given unknown input");
230     }
231   }
232 
233   if (IsOpenMPDevice) {
234     // -fopenmp-is-target-device is passed along to tell the frontend that it is
235     // generating code for a device, so that only the relevant code is emitted.
236     CmdArgs.push_back("-fopenmp-is-target-device");
237 
238     // When in OpenMP offloading mode, enable debugging on the device.
239     Args.AddAllArgs(CmdArgs, options::OPT_fopenmp_target_debug_EQ);
240     if (Args.hasFlag(options::OPT_fopenmp_target_debug,
241                      options::OPT_fno_openmp_target_debug, /*Default=*/false))
242       CmdArgs.push_back("-fopenmp-target-debug");
243 
244     // When in OpenMP offloading mode, forward assumptions information about
245     // thread and team counts in the device.
246     if (Args.hasFlag(options::OPT_fopenmp_assume_teams_oversubscription,
247                      options::OPT_fno_openmp_assume_teams_oversubscription,
248                      /*Default=*/false))
249       CmdArgs.push_back("-fopenmp-assume-teams-oversubscription");
250     if (Args.hasFlag(options::OPT_fopenmp_assume_threads_oversubscription,
251                      options::OPT_fno_openmp_assume_threads_oversubscription,
252                      /*Default=*/false))
253       CmdArgs.push_back("-fopenmp-assume-threads-oversubscription");
254     if (Args.hasArg(options::OPT_fopenmp_assume_no_thread_state))
255       CmdArgs.push_back("-fopenmp-assume-no-thread-state");
256     if (Args.hasArg(options::OPT_fopenmp_assume_no_nested_parallelism))
257       CmdArgs.push_back("-fopenmp-assume-no-nested-parallelism");
258   }
259 }
260 
261 static void addFloatingPointOptions(const Driver &D, const ArgList &Args,
262                                     ArgStringList &CmdArgs) {
263   StringRef FPContract;
264   bool HonorINFs = true;
265   bool HonorNaNs = true;
266   bool ApproxFunc = false;
267   bool SignedZeros = true;
268   bool AssociativeMath = false;
269   bool ReciprocalMath = false;
270 
271   if (const Arg *A = Args.getLastArg(options::OPT_ffp_contract)) {
272     const StringRef Val = A->getValue();
273     if (Val == "fast" || Val == "off") {
274       FPContract = Val;
275     } else if (Val == "on") {
276       // Warn instead of error because users might have makefiles written for
277       // gfortran (which accepts -ffp-contract=on)
278       D.Diag(diag::warn_drv_unsupported_option_for_flang)
279           << Val << A->getOption().getName() << "off";
280       FPContract = "off";
281     } else
282       // Clang's "fast-honor-pragmas" option is not supported because it is
283       // non-standard
284       D.Diag(diag::err_drv_unsupported_option_argument)
285           << A->getSpelling() << Val;
286   }
287 
288   for (const Arg *A : Args) {
289     auto optId = A->getOption().getID();
290     switch (optId) {
291     // if this isn't an FP option, skip the claim below
292     default:
293       continue;
294 
295     case options::OPT_fhonor_infinities:
296       HonorINFs = true;
297       break;
298     case options::OPT_fno_honor_infinities:
299       HonorINFs = false;
300       break;
301     case options::OPT_fhonor_nans:
302       HonorNaNs = true;
303       break;
304     case options::OPT_fno_honor_nans:
305       HonorNaNs = false;
306       break;
307     case options::OPT_fapprox_func:
308       ApproxFunc = true;
309       break;
310     case options::OPT_fno_approx_func:
311       ApproxFunc = false;
312       break;
313     case options::OPT_fsigned_zeros:
314       SignedZeros = true;
315       break;
316     case options::OPT_fno_signed_zeros:
317       SignedZeros = false;
318       break;
319     case options::OPT_fassociative_math:
320       AssociativeMath = true;
321       break;
322     case options::OPT_fno_associative_math:
323       AssociativeMath = false;
324       break;
325     case options::OPT_freciprocal_math:
326       ReciprocalMath = true;
327       break;
328     case options::OPT_fno_reciprocal_math:
329       ReciprocalMath = false;
330       break;
331     case options::OPT_Ofast:
332       [[fallthrough]];
333     case options::OPT_ffast_math:
334       HonorINFs = false;
335       HonorNaNs = false;
336       AssociativeMath = true;
337       ReciprocalMath = true;
338       ApproxFunc = true;
339       SignedZeros = false;
340       FPContract = "fast";
341       break;
342     case options::OPT_fno_fast_math:
343       HonorINFs = true;
344       HonorNaNs = true;
345       AssociativeMath = false;
346       ReciprocalMath = false;
347       ApproxFunc = false;
348       SignedZeros = true;
349       // -fno-fast-math should undo -ffast-math so I return FPContract to the
350       // default. It is important to check it is "fast" (the default) so that
351       // --ffp-contract=off -fno-fast-math --> -ffp-contract=off
352       if (FPContract == "fast")
353         FPContract = "";
354       break;
355     }
356 
357     // If we handled this option claim it
358     A->claim();
359   }
360 
361   if (!HonorINFs && !HonorNaNs && AssociativeMath && ReciprocalMath &&
362       ApproxFunc && !SignedZeros &&
363       (FPContract == "fast" || FPContract == "")) {
364     CmdArgs.push_back("-ffast-math");
365     return;
366   }
367 
368   if (!FPContract.empty())
369     CmdArgs.push_back(Args.MakeArgString("-ffp-contract=" + FPContract));
370 
371   if (!HonorINFs)
372     CmdArgs.push_back("-menable-no-infs");
373 
374   if (!HonorNaNs)
375     CmdArgs.push_back("-menable-no-nans");
376 
377   if (ApproxFunc)
378     CmdArgs.push_back("-fapprox-func");
379 
380   if (!SignedZeros)
381     CmdArgs.push_back("-fno-signed-zeros");
382 
383   if (AssociativeMath && !SignedZeros)
384     CmdArgs.push_back("-mreassociate");
385 
386   if (ReciprocalMath)
387     CmdArgs.push_back("-freciprocal-math");
388 }
389 
390 void Flang::ConstructJob(Compilation &C, const JobAction &JA,
391                          const InputInfo &Output, const InputInfoList &Inputs,
392                          const ArgList &Args, const char *LinkingOutput) const {
393   const auto &TC = getToolChain();
394   const llvm::Triple &Triple = TC.getEffectiveTriple();
395   const std::string &TripleStr = Triple.getTriple();
396 
397   const Driver &D = TC.getDriver();
398   ArgStringList CmdArgs;
399   DiagnosticsEngine &Diags = D.getDiags();
400 
401   // Invoke ourselves in -fc1 mode.
402   CmdArgs.push_back("-fc1");
403 
404   // Add the "effective" target triple.
405   CmdArgs.push_back("-triple");
406   CmdArgs.push_back(Args.MakeArgString(TripleStr));
407 
408   if (isa<PreprocessJobAction>(JA)) {
409       CmdArgs.push_back("-E");
410   } else if (isa<CompileJobAction>(JA) || isa<BackendJobAction>(JA)) {
411     if (JA.getType() == types::TY_Nothing) {
412       CmdArgs.push_back("-fsyntax-only");
413     } else if (JA.getType() == types::TY_AST) {
414       CmdArgs.push_back("-emit-ast");
415     } else if (JA.getType() == types::TY_LLVM_IR ||
416                JA.getType() == types::TY_LTO_IR) {
417       CmdArgs.push_back("-emit-llvm");
418     } else if (JA.getType() == types::TY_LLVM_BC ||
419                JA.getType() == types::TY_LTO_BC) {
420       CmdArgs.push_back("-emit-llvm-bc");
421     } else if (JA.getType() == types::TY_PP_Asm) {
422       CmdArgs.push_back("-S");
423     } else {
424       assert(false && "Unexpected output type!");
425     }
426   } else if (isa<AssembleJobAction>(JA)) {
427     CmdArgs.push_back("-emit-obj");
428   } else {
429     assert(false && "Unexpected action class for Flang tool.");
430   }
431 
432   const InputInfo &Input = Inputs[0];
433   types::ID InputType = Input.getType();
434 
435   // Add preprocessing options like -I, -D, etc. if we are using the
436   // preprocessor (i.e. skip when dealing with e.g. binary files).
437   if (types::getPreprocessedType(InputType) != types::TY_INVALID)
438     addPreprocessingOptions(Args, CmdArgs);
439 
440   addFortranDialectOptions(Args, CmdArgs);
441 
442   // Color diagnostics are parsed by the driver directly from argv and later
443   // re-parsed to construct this job; claim any possible color diagnostic here
444   // to avoid warn_drv_unused_argument.
445   Args.getLastArg(options::OPT_fcolor_diagnostics,
446                   options::OPT_fno_color_diagnostics);
447   if (Diags.getDiagnosticOptions().ShowColors)
448     CmdArgs.push_back("-fcolor-diagnostics");
449 
450   // LTO mode is parsed by the Clang driver library.
451   LTOKind LTOMode = D.getLTOMode(/* IsOffload */ false);
452   assert(LTOMode != LTOK_Unknown && "Unknown LTO mode.");
453   if (LTOMode == LTOK_Full)
454     CmdArgs.push_back("-flto=full");
455   else if (LTOMode == LTOK_Thin) {
456     Diags.Report(
457         Diags.getCustomDiagID(DiagnosticsEngine::Warning,
458                               "the option '-flto=thin' is a work in progress"));
459     CmdArgs.push_back("-flto=thin");
460   }
461 
462   // -fPIC and related options.
463   addPicOptions(Args, CmdArgs);
464 
465   // Floating point related options
466   addFloatingPointOptions(D, Args, CmdArgs);
467 
468   // Add target args, features, etc.
469   addTargetOptions(Args, CmdArgs);
470 
471   // Add Codegen options
472   addCodegenOptions(Args, CmdArgs);
473 
474   // Add other compile options
475   addOtherOptions(Args, CmdArgs);
476 
477   // Offloading related options
478   addOffloadOptions(C, Inputs, JA, Args, CmdArgs);
479 
480   // Forward -Xflang arguments to -fc1
481   Args.AddAllArgValues(CmdArgs, options::OPT_Xflang);
482 
483   // Forward -mllvm options to the LLVM option parser. In practice, this means
484   // forwarding to `-fc1` as that's where the LLVM parser is run.
485   for (const Arg *A : Args.filtered(options::OPT_mllvm)) {
486     A->claim();
487     A->render(Args, CmdArgs);
488   }
489 
490   for (const Arg *A : Args.filtered(options::OPT_mmlir)) {
491     A->claim();
492     A->render(Args, CmdArgs);
493   }
494 
495   // Remove any unsupported gfortran diagnostic options
496   for (const Arg *A : Args.filtered(options::OPT_flang_ignored_w_Group)) {
497     A->claim();
498     D.Diag(diag::warn_drv_unsupported_diag_option_for_flang)
499         << A->getOption().getName();
500   }
501 
502   // Optimization level for CodeGen.
503   if (const Arg *A = Args.getLastArg(options::OPT_O_Group)) {
504     if (A->getOption().matches(options::OPT_O4)) {
505       CmdArgs.push_back("-O3");
506       D.Diag(diag::warn_O4_is_O3);
507     } else if (A->getOption().matches(options::OPT_Ofast)) {
508       CmdArgs.push_back("-O3");
509     } else {
510       A->render(Args, CmdArgs);
511     }
512   }
513 
514   if (Output.isFilename()) {
515     CmdArgs.push_back("-o");
516     CmdArgs.push_back(Output.getFilename());
517   } else {
518     assert(Output.isNothing() && "Invalid output.");
519   }
520 
521   assert(Input.isFilename() && "Invalid input.");
522 
523   if (Args.getLastArg(options::OPT_save_temps_EQ))
524     Args.AddLastArg(CmdArgs, options::OPT_save_temps_EQ);
525 
526   addDashXForInput(Args, Input, CmdArgs);
527 
528   CmdArgs.push_back(Input.getFilename());
529 
530   // TODO: Replace flang-new with flang once the new driver replaces the
531   // throwaway driver
532   const char *Exec = Args.MakeArgString(D.GetProgramPath("flang-new", TC));
533   C.addCommand(std::make_unique<Command>(JA, *this,
534                                          ResponseFileSupport::AtFileUTF8(),
535                                          Exec, CmdArgs, Inputs, Output));
536 }
537 
538 Flang::Flang(const ToolChain &TC) : Tool("flang-new", "flang frontend", TC) {}
539 
540 Flang::~Flang() {}
541