Coverage Report

Created: 2024-01-17 10:31

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