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