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 #include "Flang.h"
10 #include "Arch/RISCV.h"
11 #include "CommonArgs.h"
12 
13 #include "clang/Basic/CodeGenOptions.h"
14 #include "clang/Driver/Options.h"
15 #include "llvm/Frontend/Debug/Options.h"
16 #include "llvm/Support/FileSystem.h"
17 #include "llvm/Support/Path.h"
18 #include "llvm/Support/RISCVISAInfo.h"
19 #include "llvm/TargetParser/RISCVTargetParser.h"
20 
21 #include <cassert>
22 
23 using namespace clang::driver;
24 using namespace clang::driver::tools;
25 using namespace clang;
26 using namespace llvm::opt;
27 
28 /// Add -x lang to \p CmdArgs for \p Input.
addDashXForInput(const ArgList & Args,const InputInfo & Input,ArgStringList & CmdArgs)29 static void addDashXForInput(const ArgList &Args, const InputInfo &Input,
30                              ArgStringList &CmdArgs) {
31   CmdArgs.push_back("-x");
32   // Map the driver type to the frontend type.
33   CmdArgs.push_back(types::getTypeName(Input.getType()));
34 }
35 
addFortranDialectOptions(const ArgList & Args,ArgStringList & CmdArgs) const36 void Flang::addFortranDialectOptions(const ArgList &Args,
37                                      ArgStringList &CmdArgs) const {
38   Args.addAllArgs(CmdArgs, {options::OPT_ffixed_form,
39                             options::OPT_ffree_form,
40                             options::OPT_ffixed_line_length_EQ,
41                             options::OPT_fopenmp,
42                             options::OPT_fopenmp_version_EQ,
43                             options::OPT_fopenacc,
44                             options::OPT_finput_charset_EQ,
45                             options::OPT_fimplicit_none,
46                             options::OPT_fno_implicit_none,
47                             options::OPT_fbackslash,
48                             options::OPT_fno_backslash,
49                             options::OPT_flogical_abbreviations,
50                             options::OPT_fno_logical_abbreviations,
51                             options::OPT_fxor_operator,
52                             options::OPT_fno_xor_operator,
53                             options::OPT_falternative_parameter_statement,
54                             options::OPT_fdefault_real_8,
55                             options::OPT_fdefault_integer_8,
56                             options::OPT_fdefault_double_8,
57                             options::OPT_flarge_sizes,
58                             options::OPT_fno_automatic});
59 }
60 
addPreprocessingOptions(const ArgList & Args,ArgStringList & CmdArgs) const61 void Flang::addPreprocessingOptions(const ArgList &Args,
62                                     ArgStringList &CmdArgs) const {
63   Args.addAllArgs(CmdArgs,
64                   {options::OPT_P, options::OPT_D, options::OPT_U,
65                    options::OPT_I, options::OPT_cpp, options::OPT_nocpp});
66 }
67 
68 /// @C shouldLoopVersion
69 ///
70 /// Check if Loop Versioning should be enabled.
71 /// We look for the last of one of the following:
72 ///   -Ofast, -O4, -O<number> and -f[no-]version-loops-for-stride.
73 /// Loop versioning is disabled if the last option is
74 ///  -fno-version-loops-for-stride.
75 /// Loop versioning is enabled if the last option is one of:
76 ///  -floop-versioning
77 ///  -Ofast
78 ///  -O4
79 ///  -O3
80 /// For all other cases, loop versioning is is disabled.
81 ///
82 /// The gfortran compiler automatically enables the option for -O3 or -Ofast.
83 ///
84 /// @return true if loop-versioning should be enabled, otherwise false.
shouldLoopVersion(const ArgList & Args)85 static bool shouldLoopVersion(const ArgList &Args) {
86   const Arg *LoopVersioningArg = Args.getLastArg(
87       options::OPT_Ofast, options::OPT_O, options::OPT_O4,
88       options::OPT_floop_versioning, options::OPT_fno_loop_versioning);
89   if (!LoopVersioningArg)
90     return false;
91 
92   if (LoopVersioningArg->getOption().matches(options::OPT_fno_loop_versioning))
93     return false;
94 
95   if (LoopVersioningArg->getOption().matches(options::OPT_floop_versioning))
96     return true;
97 
98   if (LoopVersioningArg->getOption().matches(options::OPT_Ofast) ||
99       LoopVersioningArg->getOption().matches(options::OPT_O4))
100     return true;
101 
102   if (LoopVersioningArg->getOption().matches(options::OPT_O)) {
103     StringRef S(LoopVersioningArg->getValue());
104     unsigned OptLevel = 0;
105     // Note -Os or Oz woould "fail" here, so return false. Which is the
106     // desiered behavior.
107     if (S.getAsInteger(10, OptLevel))
108       return false;
109 
110     return OptLevel > 2;
111   }
112 
113   llvm_unreachable("We should not end up here");
114   return false;
115 }
116 
addOtherOptions(const ArgList & Args,ArgStringList & CmdArgs) const117 void Flang::addOtherOptions(const ArgList &Args, ArgStringList &CmdArgs) const {
118   Args.addAllArgs(CmdArgs,
119                   {options::OPT_module_dir, options::OPT_fdebug_module_writer,
120                    options::OPT_fintrinsic_modules_path, options::OPT_pedantic,
121                    options::OPT_std_EQ, options::OPT_W_Joined,
122                    options::OPT_fconvert_EQ, options::OPT_fpass_plugin_EQ,
123                    options::OPT_funderscoring, options::OPT_fno_underscoring});
124 
125   llvm::codegenoptions::DebugInfoKind DebugInfoKind;
126   if (Args.hasArg(options::OPT_gN_Group)) {
127     Arg *gNArg = Args.getLastArg(options::OPT_gN_Group);
128     DebugInfoKind = debugLevelToInfoKind(*gNArg);
129   } else if (Args.hasArg(options::OPT_g_Flag)) {
130     DebugInfoKind = llvm::codegenoptions::DebugLineTablesOnly;
131   } else {
132     DebugInfoKind = llvm::codegenoptions::NoDebugInfo;
133   }
134   addDebugInfoKind(CmdArgs, DebugInfoKind);
135 }
136 
addCodegenOptions(const ArgList & Args,ArgStringList & CmdArgs) const137 void Flang::addCodegenOptions(const ArgList &Args,
138                               ArgStringList &CmdArgs) const {
139   Arg *stackArrays =
140       Args.getLastArg(options::OPT_Ofast, options::OPT_fstack_arrays,
141                       options::OPT_fno_stack_arrays);
142   if (stackArrays &&
143       !stackArrays->getOption().matches(options::OPT_fno_stack_arrays))
144     CmdArgs.push_back("-fstack-arrays");
145 
146   if (shouldLoopVersion(Args))
147     CmdArgs.push_back("-fversion-loops-for-stride");
148 
149   Args.addAllArgs(CmdArgs, {options::OPT_flang_experimental_hlfir,
150                             options::OPT_flang_deprecated_no_hlfir,
151                             options::OPT_flang_experimental_polymorphism,
152                             options::OPT_fno_ppc_native_vec_elem_order,
153                             options::OPT_fppc_native_vec_elem_order});
154 }
155 
addPicOptions(const ArgList & Args,ArgStringList & CmdArgs) const156 void Flang::addPicOptions(const ArgList &Args, ArgStringList &CmdArgs) const {
157   // ParsePICArgs parses -fPIC/-fPIE and their variants and returns a tuple of
158   // (RelocationModel, PICLevel, IsPIE).
159   llvm::Reloc::Model RelocationModel;
160   unsigned PICLevel;
161   bool IsPIE;
162   std::tie(RelocationModel, PICLevel, IsPIE) =
163       ParsePICArgs(getToolChain(), Args);
164 
165   if (auto *RMName = RelocationModelName(RelocationModel)) {
166     CmdArgs.push_back("-mrelocation-model");
167     CmdArgs.push_back(RMName);
168   }
169   if (PICLevel > 0) {
170     CmdArgs.push_back("-pic-level");
171     CmdArgs.push_back(PICLevel == 1 ? "1" : "2");
172     if (IsPIE)
173       CmdArgs.push_back("-pic-is-pie");
174   }
175 }
176 
AddAArch64TargetArgs(const ArgList & Args,ArgStringList & CmdArgs) const177 void Flang::AddAArch64TargetArgs(const ArgList &Args,
178                                  ArgStringList &CmdArgs) const {
179   // Handle -msve_vector_bits=<bits>
180   if (Arg *A = Args.getLastArg(options::OPT_msve_vector_bits_EQ)) {
181     StringRef Val = A->getValue();
182     const Driver &D = getToolChain().getDriver();
183     if (Val.equals("128") || Val.equals("256") || Val.equals("512") ||
184         Val.equals("1024") || Val.equals("2048") || Val.equals("128+") ||
185         Val.equals("256+") || Val.equals("512+") || Val.equals("1024+") ||
186         Val.equals("2048+")) {
187       unsigned Bits = 0;
188       if (Val.ends_with("+"))
189         Val = Val.substr(0, Val.size() - 1);
190       else {
191         [[maybe_unused]] bool Invalid = Val.getAsInteger(10, Bits);
192         assert(!Invalid && "Failed to parse value");
193         CmdArgs.push_back(
194             Args.MakeArgString("-mvscale-max=" + llvm::Twine(Bits / 128)));
195       }
196 
197       [[maybe_unused]] bool Invalid = Val.getAsInteger(10, Bits);
198       assert(!Invalid && "Failed to parse value");
199       CmdArgs.push_back(
200           Args.MakeArgString("-mvscale-min=" + llvm::Twine(Bits / 128)));
201       // Silently drop requests for vector-length agnostic code as it's implied.
202     } else if (!Val.equals("scalable"))
203       // Handle the unsupported values passed to msve-vector-bits.
204       D.Diag(diag::err_drv_unsupported_option_argument)
205           << A->getSpelling() << Val;
206   }
207 }
208 
AddRISCVTargetArgs(const ArgList & Args,ArgStringList & CmdArgs) const209 void Flang::AddRISCVTargetArgs(const ArgList &Args,
210                                ArgStringList &CmdArgs) const {
211   const llvm::Triple &Triple = getToolChain().getTriple();
212   // Handle -mrvv-vector-bits=<bits>
213   if (Arg *A = Args.getLastArg(options::OPT_mrvv_vector_bits_EQ)) {
214     StringRef Val = A->getValue();
215     const Driver &D = getToolChain().getDriver();
216 
217     // Get minimum VLen from march.
218     unsigned MinVLen = 0;
219     StringRef Arch = riscv::getRISCVArch(Args, Triple);
220     auto ISAInfo = llvm::RISCVISAInfo::parseArchString(
221         Arch, /*EnableExperimentalExtensions*/ true);
222     // Ignore parsing error.
223     if (!errorToBool(ISAInfo.takeError()))
224       MinVLen = (*ISAInfo)->getMinVLen();
225 
226     // If the value is "zvl", use MinVLen from march. Otherwise, try to parse
227     // as integer as long as we have a MinVLen.
228     unsigned Bits = 0;
229     if (Val.equals("zvl") && MinVLen >= llvm::RISCV::RVVBitsPerBlock) {
230       Bits = MinVLen;
231     } else if (!Val.getAsInteger(10, Bits)) {
232       // Only accept power of 2 values beteen RVVBitsPerBlock and 65536 that
233       // at least MinVLen.
234       if (Bits < MinVLen || Bits < llvm::RISCV::RVVBitsPerBlock ||
235           Bits > 65536 || !llvm::isPowerOf2_32(Bits))
236         Bits = 0;
237     }
238 
239     // If we got a valid value try to use it.
240     if (Bits != 0) {
241       unsigned VScaleMin = Bits / llvm::RISCV::RVVBitsPerBlock;
242       CmdArgs.push_back(
243           Args.MakeArgString("-mvscale-max=" + llvm::Twine(VScaleMin)));
244       CmdArgs.push_back(
245           Args.MakeArgString("-mvscale-min=" + llvm::Twine(VScaleMin)));
246     } else if (!Val.equals("scalable")) {
247       // Handle the unsupported values passed to mrvv-vector-bits.
248       D.Diag(diag::err_drv_unsupported_option_argument)
249           << A->getSpelling() << Val;
250     }
251   }
252 }
253 
addVSDefines(const ToolChain & TC,const ArgList & Args,ArgStringList & CmdArgs)254 static void addVSDefines(const ToolChain &TC, const ArgList &Args,
255                          ArgStringList &CmdArgs) {
256 
257   unsigned ver = 0;
258   const VersionTuple vt = TC.computeMSVCVersion(nullptr, Args);
259   ver = vt.getMajor() * 10000000 + vt.getMinor().value_or(0) * 100000 +
260         vt.getSubminor().value_or(0);
261   CmdArgs.push_back(Args.MakeArgString("-D_MSC_VER=" + Twine(ver / 100000)));
262   CmdArgs.push_back(Args.MakeArgString("-D_MSC_FULL_VER=" + Twine(ver)));
263   CmdArgs.push_back(Args.MakeArgString("-D_WIN32"));
264 
265   llvm::Triple triple = TC.getTriple();
266   if (triple.isAArch64()) {
267     CmdArgs.push_back("-D_M_ARM64=1");
268   } else if (triple.isX86() && triple.isArch32Bit()) {
269     CmdArgs.push_back("-D_M_IX86=600");
270   } else if (triple.isX86() && triple.isArch64Bit()) {
271     CmdArgs.push_back("-D_M_X64=100");
272   } else {
273     llvm_unreachable(
274         "Flang on Windows only supports X86_32, X86_64 and AArch64");
275   }
276 }
277 
processVSRuntimeLibrary(const ToolChain & TC,const ArgList & Args,ArgStringList & CmdArgs)278 static void processVSRuntimeLibrary(const ToolChain &TC, const ArgList &Args,
279                                     ArgStringList &CmdArgs) {
280   assert(TC.getTriple().isKnownWindowsMSVCEnvironment() &&
281          "can only add VS runtime library on Windows!");
282   // if -fno-fortran-main has been passed, skip linking Fortran_main.a
283   bool LinkFortranMain = !Args.hasArg(options::OPT_no_fortran_main);
284   if (TC.getTriple().isKnownWindowsMSVCEnvironment()) {
285     CmdArgs.push_back(Args.MakeArgString(
286         "--dependent-lib=" + TC.getCompilerRTBasename(Args, "builtins")));
287   }
288   unsigned RTOptionID = options::OPT__SLASH_MT;
289   if (auto *rtl = Args.getLastArg(options::OPT_fms_runtime_lib_EQ)) {
290     RTOptionID = llvm::StringSwitch<unsigned>(rtl->getValue())
291                      .Case("static", options::OPT__SLASH_MT)
292                      .Case("static_dbg", options::OPT__SLASH_MTd)
293                      .Case("dll", options::OPT__SLASH_MD)
294                      .Case("dll_dbg", options::OPT__SLASH_MDd)
295                      .Default(options::OPT__SLASH_MT);
296   }
297   switch (RTOptionID) {
298   case options::OPT__SLASH_MT:
299     CmdArgs.push_back("-D_MT");
300     CmdArgs.push_back("--dependent-lib=libcmt");
301     if (LinkFortranMain)
302       CmdArgs.push_back("--dependent-lib=Fortran_main.static.lib");
303     CmdArgs.push_back("--dependent-lib=FortranRuntime.static.lib");
304     CmdArgs.push_back("--dependent-lib=FortranDecimal.static.lib");
305     break;
306   case options::OPT__SLASH_MTd:
307     CmdArgs.push_back("-D_MT");
308     CmdArgs.push_back("-D_DEBUG");
309     CmdArgs.push_back("--dependent-lib=libcmtd");
310     if (LinkFortranMain)
311       CmdArgs.push_back("--dependent-lib=Fortran_main.static_dbg.lib");
312     CmdArgs.push_back("--dependent-lib=FortranRuntime.static_dbg.lib");
313     CmdArgs.push_back("--dependent-lib=FortranDecimal.static_dbg.lib");
314     break;
315   case options::OPT__SLASH_MD:
316     CmdArgs.push_back("-D_MT");
317     CmdArgs.push_back("-D_DLL");
318     CmdArgs.push_back("--dependent-lib=msvcrt");
319     if (LinkFortranMain)
320       CmdArgs.push_back("--dependent-lib=Fortran_main.dynamic.lib");
321     CmdArgs.push_back("--dependent-lib=FortranRuntime.dynamic.lib");
322     CmdArgs.push_back("--dependent-lib=FortranDecimal.dynamic.lib");
323     break;
324   case options::OPT__SLASH_MDd:
325     CmdArgs.push_back("-D_MT");
326     CmdArgs.push_back("-D_DEBUG");
327     CmdArgs.push_back("-D_DLL");
328     CmdArgs.push_back("--dependent-lib=msvcrtd");
329     if (LinkFortranMain)
330       CmdArgs.push_back("--dependent-lib=Fortran_main.dynamic_dbg.lib");
331     CmdArgs.push_back("--dependent-lib=FortranRuntime.dynamic_dbg.lib");
332     CmdArgs.push_back("--dependent-lib=FortranDecimal.dynamic_dbg.lib");
333     break;
334   }
335 }
336 
AddAMDGPUTargetArgs(const ArgList & Args,ArgStringList & CmdArgs) const337 void Flang::AddAMDGPUTargetArgs(const ArgList &Args,
338                                 ArgStringList &CmdArgs) const {
339   if (Arg *A = Args.getLastArg(options::OPT_mcode_object_version_EQ)) {
340     StringRef Val = A->getValue();
341     CmdArgs.push_back(Args.MakeArgString("-mcode-object-version=" + Val));
342   }
343 }
344 
addTargetOptions(const ArgList & Args,ArgStringList & CmdArgs) const345 void Flang::addTargetOptions(const ArgList &Args,
346                              ArgStringList &CmdArgs) const {
347   const ToolChain &TC = getToolChain();
348   const llvm::Triple &Triple = TC.getEffectiveTriple();
349   const Driver &D = TC.getDriver();
350 
351   std::string CPU = getCPUName(D, Args, Triple);
352   if (!CPU.empty()) {
353     CmdArgs.push_back("-target-cpu");
354     CmdArgs.push_back(Args.MakeArgString(CPU));
355   }
356 
357   // Add the target features.
358   switch (TC.getArch()) {
359   default:
360     break;
361   case llvm::Triple::aarch64:
362     getTargetFeatures(D, Triple, Args, CmdArgs, /*ForAs*/ false);
363     AddAArch64TargetArgs(Args, CmdArgs);
364     break;
365 
366   case llvm::Triple::r600:
367   case llvm::Triple::amdgcn:
368     getTargetFeatures(D, Triple, Args, CmdArgs, /*ForAs*/ false);
369     AddAMDGPUTargetArgs(Args, CmdArgs);
370     break;
371   case llvm::Triple::riscv64:
372     getTargetFeatures(D, Triple, Args, CmdArgs, /*ForAs*/ false);
373     AddRISCVTargetArgs(Args, CmdArgs);
374     break;
375   case llvm::Triple::x86_64:
376     getTargetFeatures(D, Triple, Args, CmdArgs, /*ForAs*/ false);
377     break;
378   }
379 
380   if (Arg *A = Args.getLastArg(options::OPT_fveclib)) {
381     StringRef Name = A->getValue();
382     if (Name == "SVML") {
383       if (Triple.getArch() != llvm::Triple::x86 &&
384           Triple.getArch() != llvm::Triple::x86_64)
385         D.Diag(diag::err_drv_unsupported_opt_for_target)
386             << Name << Triple.getArchName();
387     } else if (Name == "LIBMVEC-X86") {
388       if (Triple.getArch() != llvm::Triple::x86 &&
389           Triple.getArch() != llvm::Triple::x86_64)
390         D.Diag(diag::err_drv_unsupported_opt_for_target)
391             << Name << Triple.getArchName();
392     } else if (Name == "SLEEF" || Name == "ArmPL") {
393       if (Triple.getArch() != llvm::Triple::aarch64 &&
394           Triple.getArch() != llvm::Triple::aarch64_be)
395         D.Diag(diag::err_drv_unsupported_opt_for_target)
396             << Name << Triple.getArchName();
397     }
398 
399     if (Triple.isOSDarwin()) {
400       // flang doesn't currently suport nostdlib, nodefaultlibs. Adding these
401       // here incase they are added someday
402       if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs)) {
403         if (A->getValue() == StringRef{"Accelerate"}) {
404           CmdArgs.push_back("-framework");
405           CmdArgs.push_back("Accelerate");
406         }
407       }
408     }
409     A->render(Args, CmdArgs);
410   }
411 
412   if (Triple.isKnownWindowsMSVCEnvironment()) {
413     processVSRuntimeLibrary(TC, Args, CmdArgs);
414     addVSDefines(TC, Args, CmdArgs);
415   }
416 
417   // TODO: Add target specific flags, ABI, mtune option etc.
418 }
419 
addOffloadOptions(Compilation & C,const InputInfoList & Inputs,const JobAction & JA,const ArgList & Args,ArgStringList & CmdArgs) const420 void Flang::addOffloadOptions(Compilation &C, const InputInfoList &Inputs,
421                               const JobAction &JA, const ArgList &Args,
422                               ArgStringList &CmdArgs) const {
423   bool IsOpenMPDevice = JA.isDeviceOffloading(Action::OFK_OpenMP);
424   bool IsHostOffloadingAction = JA.isHostOffloading(Action::OFK_OpenMP) ||
425                                 JA.isHostOffloading(C.getActiveOffloadKinds());
426 
427   // Skips the primary input file, which is the input file that the compilation
428   // proccess will be executed upon (e.g. the host bitcode file) and
429   // adds other secondary input (e.g. device bitcode files for embedding to the
430   // -fembed-offload-object argument or the host IR file for proccessing
431   // during device compilation to the fopenmp-host-ir-file-path argument via
432   // OpenMPDeviceInput). This is condensed logic from the ConstructJob
433   // function inside of the Clang driver for pushing on further input arguments
434   // needed for offloading during various phases of compilation.
435   for (size_t i = 1; i < Inputs.size(); ++i) {
436     if (Inputs[i].getType() == types::TY_Nothing) {
437       // contains nothing, so it's skippable
438     } else if (IsHostOffloadingAction) {
439       CmdArgs.push_back(
440           Args.MakeArgString("-fembed-offload-object=" +
441                              getToolChain().getInputFilename(Inputs[i])));
442     } else if (IsOpenMPDevice) {
443       if (Inputs[i].getFilename()) {
444         CmdArgs.push_back("-fopenmp-host-ir-file-path");
445         CmdArgs.push_back(Args.MakeArgString(Inputs[i].getFilename()));
446       } else {
447         llvm_unreachable("missing openmp host-ir file for device offloading");
448       }
449     } else {
450       llvm_unreachable(
451           "unexpectedly given multiple inputs or given unknown input");
452     }
453   }
454 
455   if (IsOpenMPDevice) {
456     // -fopenmp-is-target-device is passed along to tell the frontend that it is
457     // generating code for a device, so that only the relevant code is emitted.
458     CmdArgs.push_back("-fopenmp-is-target-device");
459 
460     // When in OpenMP offloading mode, enable debugging on the device.
461     Args.AddAllArgs(CmdArgs, options::OPT_fopenmp_target_debug_EQ);
462     if (Args.hasFlag(options::OPT_fopenmp_target_debug,
463                      options::OPT_fno_openmp_target_debug, /*Default=*/false))
464       CmdArgs.push_back("-fopenmp-target-debug");
465 
466     // When in OpenMP offloading mode, forward assumptions information about
467     // thread and team counts in the device.
468     if (Args.hasFlag(options::OPT_fopenmp_assume_teams_oversubscription,
469                      options::OPT_fno_openmp_assume_teams_oversubscription,
470                      /*Default=*/false))
471       CmdArgs.push_back("-fopenmp-assume-teams-oversubscription");
472     if (Args.hasFlag(options::OPT_fopenmp_assume_threads_oversubscription,
473                      options::OPT_fno_openmp_assume_threads_oversubscription,
474                      /*Default=*/false))
475       CmdArgs.push_back("-fopenmp-assume-threads-oversubscription");
476     if (Args.hasArg(options::OPT_fopenmp_assume_no_thread_state))
477       CmdArgs.push_back("-fopenmp-assume-no-thread-state");
478     if (Args.hasArg(options::OPT_fopenmp_assume_no_nested_parallelism))
479       CmdArgs.push_back("-fopenmp-assume-no-nested-parallelism");
480     if (Args.hasArg(options::OPT_nogpulib))
481       CmdArgs.push_back("-nogpulib");
482   }
483 }
484 
addFloatingPointOptions(const Driver & D,const ArgList & Args,ArgStringList & CmdArgs)485 static void addFloatingPointOptions(const Driver &D, const ArgList &Args,
486                                     ArgStringList &CmdArgs) {
487   StringRef FPContract;
488   bool HonorINFs = true;
489   bool HonorNaNs = true;
490   bool ApproxFunc = false;
491   bool SignedZeros = true;
492   bool AssociativeMath = false;
493   bool ReciprocalMath = false;
494 
495   if (const Arg *A = Args.getLastArg(options::OPT_ffp_contract)) {
496     const StringRef Val = A->getValue();
497     if (Val == "fast" || Val == "off") {
498       FPContract = Val;
499     } else if (Val == "on") {
500       // Warn instead of error because users might have makefiles written for
501       // gfortran (which accepts -ffp-contract=on)
502       D.Diag(diag::warn_drv_unsupported_option_for_flang)
503           << Val << A->getOption().getName() << "off";
504       FPContract = "off";
505     } else
506       // Clang's "fast-honor-pragmas" option is not supported because it is
507       // non-standard
508       D.Diag(diag::err_drv_unsupported_option_argument)
509           << A->getSpelling() << Val;
510   }
511 
512   for (const Arg *A : Args) {
513     auto optId = A->getOption().getID();
514     switch (optId) {
515     // if this isn't an FP option, skip the claim below
516     default:
517       continue;
518 
519     case options::OPT_fhonor_infinities:
520       HonorINFs = true;
521       break;
522     case options::OPT_fno_honor_infinities:
523       HonorINFs = false;
524       break;
525     case options::OPT_fhonor_nans:
526       HonorNaNs = true;
527       break;
528     case options::OPT_fno_honor_nans:
529       HonorNaNs = false;
530       break;
531     case options::OPT_fapprox_func:
532       ApproxFunc = true;
533       break;
534     case options::OPT_fno_approx_func:
535       ApproxFunc = false;
536       break;
537     case options::OPT_fsigned_zeros:
538       SignedZeros = true;
539       break;
540     case options::OPT_fno_signed_zeros:
541       SignedZeros = false;
542       break;
543     case options::OPT_fassociative_math:
544       AssociativeMath = true;
545       break;
546     case options::OPT_fno_associative_math:
547       AssociativeMath = false;
548       break;
549     case options::OPT_freciprocal_math:
550       ReciprocalMath = true;
551       break;
552     case options::OPT_fno_reciprocal_math:
553       ReciprocalMath = false;
554       break;
555     case options::OPT_Ofast:
556       [[fallthrough]];
557     case options::OPT_ffast_math:
558       HonorINFs = false;
559       HonorNaNs = false;
560       AssociativeMath = true;
561       ReciprocalMath = true;
562       ApproxFunc = true;
563       SignedZeros = false;
564       FPContract = "fast";
565       break;
566     case options::OPT_fno_fast_math:
567       HonorINFs = true;
568       HonorNaNs = true;
569       AssociativeMath = false;
570       ReciprocalMath = false;
571       ApproxFunc = false;
572       SignedZeros = true;
573       // -fno-fast-math should undo -ffast-math so I return FPContract to the
574       // default. It is important to check it is "fast" (the default) so that
575       // --ffp-contract=off -fno-fast-math --> -ffp-contract=off
576       if (FPContract == "fast")
577         FPContract = "";
578       break;
579     }
580 
581     // If we handled this option claim it
582     A->claim();
583   }
584 
585   if (!HonorINFs && !HonorNaNs && AssociativeMath && ReciprocalMath &&
586       ApproxFunc && !SignedZeros &&
587       (FPContract == "fast" || FPContract == "")) {
588     CmdArgs.push_back("-ffast-math");
589     return;
590   }
591 
592   if (!FPContract.empty())
593     CmdArgs.push_back(Args.MakeArgString("-ffp-contract=" + FPContract));
594 
595   if (!HonorINFs)
596     CmdArgs.push_back("-menable-no-infs");
597 
598   if (!HonorNaNs)
599     CmdArgs.push_back("-menable-no-nans");
600 
601   if (ApproxFunc)
602     CmdArgs.push_back("-fapprox-func");
603 
604   if (!SignedZeros)
605     CmdArgs.push_back("-fno-signed-zeros");
606 
607   if (AssociativeMath && !SignedZeros)
608     CmdArgs.push_back("-mreassociate");
609 
610   if (ReciprocalMath)
611     CmdArgs.push_back("-freciprocal-math");
612 }
613 
renderRemarksOptions(const ArgList & Args,ArgStringList & CmdArgs,const InputInfo & Input)614 static void renderRemarksOptions(const ArgList &Args, ArgStringList &CmdArgs,
615                                  const InputInfo &Input) {
616   StringRef Format = "yaml";
617   if (const Arg *A = Args.getLastArg(options::OPT_fsave_optimization_record_EQ))
618     Format = A->getValue();
619 
620   CmdArgs.push_back("-opt-record-file");
621 
622   const Arg *A = Args.getLastArg(options::OPT_foptimization_record_file_EQ);
623   if (A) {
624     CmdArgs.push_back(A->getValue());
625   } else {
626     SmallString<128> F;
627 
628     if (Args.hasArg(options::OPT_c) || Args.hasArg(options::OPT_S)) {
629       if (Arg *FinalOutput = Args.getLastArg(options::OPT_o))
630         F = FinalOutput->getValue();
631     }
632 
633     if (F.empty()) {
634       // Use the input filename.
635       F = llvm::sys::path::stem(Input.getBaseInput());
636     }
637 
638     SmallString<32> Extension;
639     Extension += "opt.";
640     Extension += Format;
641 
642     llvm::sys::path::replace_extension(F, Extension);
643     CmdArgs.push_back(Args.MakeArgString(F));
644   }
645 
646   if (const Arg *A =
647           Args.getLastArg(options::OPT_foptimization_record_passes_EQ)) {
648     CmdArgs.push_back("-opt-record-passes");
649     CmdArgs.push_back(A->getValue());
650   }
651 
652   if (!Format.empty()) {
653     CmdArgs.push_back("-opt-record-format");
654     CmdArgs.push_back(Format.data());
655   }
656 }
657 
ConstructJob(Compilation & C,const JobAction & JA,const InputInfo & Output,const InputInfoList & Inputs,const ArgList & Args,const char * LinkingOutput) const658 void Flang::ConstructJob(Compilation &C, const JobAction &JA,
659                          const InputInfo &Output, const InputInfoList &Inputs,
660                          const ArgList &Args, const char *LinkingOutput) const {
661   const auto &TC = getToolChain();
662   const llvm::Triple &Triple = TC.getEffectiveTriple();
663   const std::string &TripleStr = Triple.getTriple();
664 
665   const Driver &D = TC.getDriver();
666   ArgStringList CmdArgs;
667   DiagnosticsEngine &Diags = D.getDiags();
668 
669   // Invoke ourselves in -fc1 mode.
670   CmdArgs.push_back("-fc1");
671 
672   // Add the "effective" target triple.
673   CmdArgs.push_back("-triple");
674   CmdArgs.push_back(Args.MakeArgString(TripleStr));
675 
676   if (isa<PreprocessJobAction>(JA)) {
677       CmdArgs.push_back("-E");
678   } else if (isa<CompileJobAction>(JA) || isa<BackendJobAction>(JA)) {
679     if (JA.getType() == types::TY_Nothing) {
680       CmdArgs.push_back("-fsyntax-only");
681     } else if (JA.getType() == types::TY_AST) {
682       CmdArgs.push_back("-emit-ast");
683     } else if (JA.getType() == types::TY_LLVM_IR ||
684                JA.getType() == types::TY_LTO_IR) {
685       CmdArgs.push_back("-emit-llvm");
686     } else if (JA.getType() == types::TY_LLVM_BC ||
687                JA.getType() == types::TY_LTO_BC) {
688       CmdArgs.push_back("-emit-llvm-bc");
689     } else if (JA.getType() == types::TY_PP_Asm) {
690       CmdArgs.push_back("-S");
691     } else {
692       assert(false && "Unexpected output type!");
693     }
694   } else if (isa<AssembleJobAction>(JA)) {
695     CmdArgs.push_back("-emit-obj");
696   } else {
697     assert(false && "Unexpected action class for Flang tool.");
698   }
699 
700   const InputInfo &Input = Inputs[0];
701   types::ID InputType = Input.getType();
702 
703   // Add preprocessing options like -I, -D, etc. if we are using the
704   // preprocessor (i.e. skip when dealing with e.g. binary files).
705   if (types::getPreprocessedType(InputType) != types::TY_INVALID)
706     addPreprocessingOptions(Args, CmdArgs);
707 
708   addFortranDialectOptions(Args, CmdArgs);
709 
710   // Color diagnostics are parsed by the driver directly from argv and later
711   // re-parsed to construct this job; claim any possible color diagnostic here
712   // to avoid warn_drv_unused_argument.
713   Args.getLastArg(options::OPT_fcolor_diagnostics,
714                   options::OPT_fno_color_diagnostics);
715   if (Diags.getDiagnosticOptions().ShowColors)
716     CmdArgs.push_back("-fcolor-diagnostics");
717 
718   // LTO mode is parsed by the Clang driver library.
719   LTOKind LTOMode = D.getLTOMode(/* IsOffload */ false);
720   assert(LTOMode != LTOK_Unknown && "Unknown LTO mode.");
721   if (LTOMode == LTOK_Full)
722     CmdArgs.push_back("-flto=full");
723   else if (LTOMode == LTOK_Thin) {
724     Diags.Report(
725         Diags.getCustomDiagID(DiagnosticsEngine::Warning,
726                               "the option '-flto=thin' is a work in progress"));
727     CmdArgs.push_back("-flto=thin");
728   }
729 
730   // -fPIC and related options.
731   addPicOptions(Args, CmdArgs);
732 
733   // Floating point related options
734   addFloatingPointOptions(D, Args, CmdArgs);
735 
736   // Add target args, features, etc.
737   addTargetOptions(Args, CmdArgs);
738 
739   // Add Codegen options
740   addCodegenOptions(Args, CmdArgs);
741 
742   // Add R Group options
743   Args.AddAllArgs(CmdArgs, options::OPT_R_Group);
744 
745   // Remarks can be enabled with any of the `-f.*optimization-record.*` flags.
746   if (willEmitRemarks(Args))
747     renderRemarksOptions(Args, CmdArgs, Input);
748 
749   // Add other compile options
750   addOtherOptions(Args, CmdArgs);
751 
752   // Offloading related options
753   addOffloadOptions(C, Inputs, JA, Args, CmdArgs);
754 
755   // Forward -Xflang arguments to -fc1
756   Args.AddAllArgValues(CmdArgs, options::OPT_Xflang);
757 
758   CodeGenOptions::FramePointerKind FPKeepKind =
759       getFramePointerKind(Args, Triple);
760 
761   const char *FPKeepKindStr = nullptr;
762   switch (FPKeepKind) {
763   case CodeGenOptions::FramePointerKind::None:
764     FPKeepKindStr = "-mframe-pointer=none";
765     break;
766   case CodeGenOptions::FramePointerKind::NonLeaf:
767     FPKeepKindStr = "-mframe-pointer=non-leaf";
768     break;
769   case CodeGenOptions::FramePointerKind::All:
770     FPKeepKindStr = "-mframe-pointer=all";
771     break;
772   }
773   assert(FPKeepKindStr && "unknown FramePointerKind");
774   CmdArgs.push_back(FPKeepKindStr);
775 
776   // Forward -mllvm options to the LLVM option parser. In practice, this means
777   // forwarding to `-fc1` as that's where the LLVM parser is run.
778   for (const Arg *A : Args.filtered(options::OPT_mllvm)) {
779     A->claim();
780     A->render(Args, CmdArgs);
781   }
782 
783   for (const Arg *A : Args.filtered(options::OPT_mmlir)) {
784     A->claim();
785     A->render(Args, CmdArgs);
786   }
787 
788   // Remove any unsupported gfortran diagnostic options
789   for (const Arg *A : Args.filtered(options::OPT_flang_ignored_w_Group)) {
790     A->claim();
791     D.Diag(diag::warn_drv_unsupported_diag_option_for_flang)
792         << A->getOption().getName();
793   }
794 
795   // Optimization level for CodeGen.
796   if (const Arg *A = Args.getLastArg(options::OPT_O_Group)) {
797     if (A->getOption().matches(options::OPT_O4)) {
798       CmdArgs.push_back("-O3");
799       D.Diag(diag::warn_O4_is_O3);
800     } else if (A->getOption().matches(options::OPT_Ofast)) {
801       CmdArgs.push_back("-O3");
802     } else {
803       A->render(Args, CmdArgs);
804     }
805   }
806 
807   assert((Output.isFilename() || Output.isNothing()) && "Invalid output.");
808   if (Output.isFilename()) {
809     CmdArgs.push_back("-o");
810     CmdArgs.push_back(Output.getFilename());
811   }
812 
813   assert(Input.isFilename() && "Invalid input.");
814 
815   if (Args.getLastArg(options::OPT_save_temps_EQ))
816     Args.AddLastArg(CmdArgs, options::OPT_save_temps_EQ);
817 
818   addDashXForInput(Args, Input, CmdArgs);
819 
820   CmdArgs.push_back(Input.getFilename());
821 
822   // TODO: Replace flang-new with flang once the new driver replaces the
823   // throwaway driver
824   const char *Exec = Args.MakeArgString(D.GetProgramPath("flang-new", TC));
825   C.addCommand(std::make_unique<Command>(JA, *this,
826                                          ResponseFileSupport::AtFileUTF8(),
827                                          Exec, CmdArgs, Inputs, Output));
828 }
829 
Flang(const ToolChain & TC)830 Flang::Flang(const ToolChain &TC) : Tool("flang-new", "flang frontend", TC) {}
831 
~Flang()832 Flang::~Flang() {}
833