1 //===-- Flang.cpp - Flang+LLVM ToolChain Implementations --------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9
10 #include "Flang.h"
11 #include "CommonArgs.h"
12
13 #include "clang/Driver/Options.h"
14
15 #include <cassert>
16
17 using namespace clang::driver;
18 using namespace clang::driver::tools;
19 using namespace clang;
20 using namespace llvm::opt;
21
22 /// Add -x lang to \p CmdArgs for \p Input.
addDashXForInput(const ArgList & Args,const InputInfo & Input,ArgStringList & CmdArgs)23 static void addDashXForInput(const ArgList &Args, const InputInfo &Input,
24 ArgStringList &CmdArgs) {
25 CmdArgs.push_back("-x");
26 // Map the driver type to the frontend type.
27 CmdArgs.push_back(types::getTypeName(Input.getType()));
28 }
29
addFortranDialectOptions(const ArgList & Args,ArgStringList & CmdArgs) const30 void Flang::addFortranDialectOptions(const ArgList &Args,
31 ArgStringList &CmdArgs) const {
32 Args.AddAllArgs(
33 CmdArgs, {options::OPT_ffixed_form, options::OPT_ffree_form,
34 options::OPT_ffixed_line_length_EQ, options::OPT_fopenmp,
35 options::OPT_fopenacc, options::OPT_finput_charset_EQ,
36 options::OPT_fimplicit_none, options::OPT_fno_implicit_none,
37 options::OPT_fbackslash, options::OPT_fno_backslash,
38 options::OPT_flogical_abbreviations,
39 options::OPT_fno_logical_abbreviations,
40 options::OPT_fxor_operator, options::OPT_fno_xor_operator,
41 options::OPT_falternative_parameter_statement,
42 options::OPT_fdefault_real_8, options::OPT_fdefault_integer_8,
43 options::OPT_fdefault_double_8, options::OPT_flarge_sizes,
44 options::OPT_fno_automatic});
45 }
46
addPreprocessingOptions(const ArgList & Args,ArgStringList & CmdArgs) const47 void Flang::addPreprocessingOptions(const ArgList &Args,
48 ArgStringList &CmdArgs) const {
49 Args.AddAllArgs(CmdArgs,
50 {options::OPT_P, options::OPT_D, options::OPT_U,
51 options::OPT_I, options::OPT_cpp, options::OPT_nocpp});
52 }
53
addOtherOptions(const ArgList & Args,ArgStringList & CmdArgs) const54 void Flang::addOtherOptions(const ArgList &Args, ArgStringList &CmdArgs) const {
55 Args.AddAllArgs(CmdArgs,
56 {options::OPT_module_dir, options::OPT_fdebug_module_writer,
57 options::OPT_fintrinsic_modules_path, options::OPT_pedantic,
58 options::OPT_std_EQ, options::OPT_W_Joined,
59 options::OPT_fconvert_EQ, options::OPT_fpass_plugin_EQ});
60 }
61
addPicOptions(const ArgList & Args,ArgStringList & CmdArgs) const62 void Flang::addPicOptions(const ArgList &Args, ArgStringList &CmdArgs) const {
63 // ParsePICArgs parses -fPIC/-fPIE and their variants and returns a tuple of
64 // (RelocationModel, PICLevel, IsPIE).
65 llvm::Reloc::Model RelocationModel;
66 unsigned PICLevel;
67 bool IsPIE;
68 std::tie(RelocationModel, PICLevel, IsPIE) =
69 ParsePICArgs(getToolChain(), Args);
70
71 if (auto *RMName = RelocationModelName(RelocationModel)) {
72 CmdArgs.push_back("-mrelocation-model");
73 CmdArgs.push_back(RMName);
74 }
75 if (PICLevel > 0) {
76 CmdArgs.push_back("-pic-level");
77 CmdArgs.push_back(PICLevel == 1 ? "1" : "2");
78 if (IsPIE)
79 CmdArgs.push_back("-pic-is-pie");
80 }
81 }
82
addTargetOptions(const ArgList & Args,ArgStringList & CmdArgs) const83 void Flang::addTargetOptions(const ArgList &Args,
84 ArgStringList &CmdArgs) const {
85 const ToolChain &TC = getToolChain();
86 const llvm::Triple &Triple = TC.getEffectiveTriple();
87 const Driver &D = TC.getDriver();
88
89 std::string CPU = getCPUName(D, Args, Triple);
90 if (!CPU.empty()) {
91 CmdArgs.push_back("-target-cpu");
92 CmdArgs.push_back(Args.MakeArgString(CPU));
93 }
94
95 // Add the target features.
96 switch (TC.getArch()) {
97 default:
98 break;
99 case llvm::Triple::aarch64:
100 [[fallthrough]];
101 case llvm::Triple::x86_64:
102 getTargetFeatures(D, Triple, Args, CmdArgs, /*ForAs*/ false);
103 break;
104 }
105
106 // TODO: Add target specific flags, ABI, mtune option etc.
107 }
108
addFloatingPointOptions(const Driver & D,const ArgList & Args,ArgStringList & CmdArgs)109 static void addFloatingPointOptions(const Driver &D, const ArgList &Args,
110 ArgStringList &CmdArgs) {
111 StringRef FPContract;
112 bool HonorINFs = true;
113 bool HonorNaNs = true;
114 bool ApproxFunc = false;
115 bool SignedZeros = true;
116 bool AssociativeMath = false;
117 bool ReciprocalMath = false;
118
119 if (const Arg *A = Args.getLastArg(options::OPT_ffp_contract)) {
120 const StringRef Val = A->getValue();
121 if (Val == "fast" || Val == "off") {
122 FPContract = Val;
123 } else if (Val == "on") {
124 // Warn instead of error because users might have makefiles written for
125 // gfortran (which accepts -ffp-contract=on)
126 D.Diag(diag::warn_drv_unsupported_option_for_flang)
127 << Val << A->getOption().getName() << "off";
128 FPContract = "off";
129 } else
130 // Clang's "fast-honor-pragmas" option is not supported because it is
131 // non-standard
132 D.Diag(diag::err_drv_unsupported_option_argument)
133 << A->getSpelling() << Val;
134 }
135
136 for (const Arg *A : Args) {
137 auto optId = A->getOption().getID();
138 switch (optId) {
139 // if this isn't an FP option, skip the claim below
140 default:
141 continue;
142
143 case options::OPT_fhonor_infinities:
144 HonorINFs = true;
145 break;
146 case options::OPT_fno_honor_infinities:
147 HonorINFs = false;
148 break;
149 case options::OPT_fhonor_nans:
150 HonorNaNs = true;
151 break;
152 case options::OPT_fno_honor_nans:
153 HonorNaNs = false;
154 break;
155 case options::OPT_fapprox_func:
156 ApproxFunc = true;
157 break;
158 case options::OPT_fno_approx_func:
159 ApproxFunc = false;
160 break;
161 case options::OPT_fsigned_zeros:
162 SignedZeros = true;
163 break;
164 case options::OPT_fno_signed_zeros:
165 SignedZeros = false;
166 break;
167 case options::OPT_fassociative_math:
168 AssociativeMath = true;
169 break;
170 case options::OPT_fno_associative_math:
171 AssociativeMath = false;
172 break;
173 case options::OPT_freciprocal_math:
174 ReciprocalMath = true;
175 break;
176 case options::OPT_fno_reciprocal_math:
177 ReciprocalMath = false;
178 break;
179 case options::OPT_Ofast:
180 [[fallthrough]];
181 case options::OPT_ffast_math:
182 HonorINFs = false;
183 HonorNaNs = false;
184 AssociativeMath = true;
185 ReciprocalMath = true;
186 ApproxFunc = true;
187 SignedZeros = false;
188 FPContract = "fast";
189 break;
190 case options::OPT_fno_fast_math:
191 HonorINFs = true;
192 HonorNaNs = true;
193 AssociativeMath = false;
194 ReciprocalMath = false;
195 ApproxFunc = false;
196 SignedZeros = true;
197 // -fno-fast-math should undo -ffast-math so I return FPContract to the
198 // default. It is important to check it is "fast" (the default) so that
199 // --ffp-contract=off -fno-fast-math --> -ffp-contract=off
200 if (FPContract == "fast")
201 FPContract = "";
202 break;
203 }
204
205 // If we handled this option claim it
206 A->claim();
207 }
208
209 if (!HonorINFs && !HonorNaNs && AssociativeMath && ReciprocalMath &&
210 ApproxFunc && !SignedZeros &&
211 (FPContract == "fast" || FPContract == "")) {
212 CmdArgs.push_back("-ffast-math");
213 return;
214 }
215
216 if (!FPContract.empty())
217 CmdArgs.push_back(Args.MakeArgString("-ffp-contract=" + FPContract));
218
219 if (!HonorINFs)
220 CmdArgs.push_back("-menable-no-infs");
221
222 if (!HonorNaNs)
223 CmdArgs.push_back("-menable-no-nans");
224
225 if (ApproxFunc)
226 CmdArgs.push_back("-fapprox-func");
227
228 if (!SignedZeros)
229 CmdArgs.push_back("-fno-signed-zeros");
230
231 if (AssociativeMath && !SignedZeros)
232 CmdArgs.push_back("-mreassociate");
233
234 if (ReciprocalMath)
235 CmdArgs.push_back("-freciprocal-math");
236 }
237
ConstructJob(Compilation & C,const JobAction & JA,const InputInfo & Output,const InputInfoList & Inputs,const ArgList & Args,const char * LinkingOutput) const238 void Flang::ConstructJob(Compilation &C, const JobAction &JA,
239 const InputInfo &Output, const InputInfoList &Inputs,
240 const ArgList &Args, const char *LinkingOutput) const {
241 const auto &TC = getToolChain();
242 const llvm::Triple &Triple = TC.getEffectiveTriple();
243 const std::string &TripleStr = Triple.getTriple();
244
245 const Driver &D = TC.getDriver();
246 ArgStringList CmdArgs;
247
248 // Invoke ourselves in -fc1 mode.
249 CmdArgs.push_back("-fc1");
250
251 // Add the "effective" target triple.
252 CmdArgs.push_back("-triple");
253 CmdArgs.push_back(Args.MakeArgString(TripleStr));
254
255 if (isa<PreprocessJobAction>(JA)) {
256 CmdArgs.push_back("-E");
257 } else if (isa<CompileJobAction>(JA) || isa<BackendJobAction>(JA)) {
258 if (JA.getType() == types::TY_Nothing) {
259 CmdArgs.push_back("-fsyntax-only");
260 } else if (JA.getType() == types::TY_AST) {
261 CmdArgs.push_back("-emit-ast");
262 } else if (JA.getType() == types::TY_LLVM_IR ||
263 JA.getType() == types::TY_LTO_IR) {
264 CmdArgs.push_back("-emit-llvm");
265 } else if (JA.getType() == types::TY_LLVM_BC ||
266 JA.getType() == types::TY_LTO_BC) {
267 CmdArgs.push_back("-emit-llvm-bc");
268 } else if (JA.getType() == types::TY_PP_Asm) {
269 CmdArgs.push_back("-S");
270 } else {
271 assert(false && "Unexpected output type!");
272 }
273 } else if (isa<AssembleJobAction>(JA)) {
274 CmdArgs.push_back("-emit-obj");
275 } else {
276 assert(false && "Unexpected action class for Flang tool.");
277 }
278
279 const InputInfo &Input = Inputs[0];
280 types::ID InputType = Input.getType();
281
282 // Add preprocessing options like -I, -D, etc. if we are using the
283 // preprocessor (i.e. skip when dealing with e.g. binary files).
284 if (types::getPreprocessedType(InputType) != types::TY_INVALID)
285 addPreprocessingOptions(Args, CmdArgs);
286
287 addFortranDialectOptions(Args, CmdArgs);
288
289 // Color diagnostics are parsed by the driver directly from argv and later
290 // re-parsed to construct this job; claim any possible color diagnostic here
291 // to avoid warn_drv_unused_argument.
292 Args.getLastArg(options::OPT_fcolor_diagnostics,
293 options::OPT_fno_color_diagnostics);
294 if (D.getDiags().getDiagnosticOptions().ShowColors)
295 CmdArgs.push_back("-fcolor-diagnostics");
296
297 // -fPIC and related options.
298 addPicOptions(Args, CmdArgs);
299
300 // Floating point related options
301 addFloatingPointOptions(D, Args, CmdArgs);
302
303 // Add target args, features, etc.
304 addTargetOptions(Args, CmdArgs);
305
306 // Add other compile options
307 addOtherOptions(Args, CmdArgs);
308
309 // Forward -Xflang arguments to -fc1
310 Args.AddAllArgValues(CmdArgs, options::OPT_Xflang);
311
312 // Forward -mllvm options to the LLVM option parser. In practice, this means
313 // forwarding to `-fc1` as that's where the LLVM parser is run.
314 for (const Arg *A : Args.filtered(options::OPT_mllvm)) {
315 A->claim();
316 A->render(Args, CmdArgs);
317 }
318
319 for (const Arg *A : Args.filtered(options::OPT_mmlir)) {
320 A->claim();
321 A->render(Args, CmdArgs);
322 }
323
324 // Optimization level for CodeGen.
325 if (const Arg *A = Args.getLastArg(options::OPT_O_Group)) {
326 if (A->getOption().matches(options::OPT_O4)) {
327 CmdArgs.push_back("-O3");
328 D.Diag(diag::warn_O4_is_O3);
329 } else if (A->getOption().matches(options::OPT_Ofast)) {
330 CmdArgs.push_back("-O3");
331 } else {
332 A->render(Args, CmdArgs);
333 }
334 }
335
336 if (Output.isFilename()) {
337 CmdArgs.push_back("-o");
338 CmdArgs.push_back(Output.getFilename());
339 } else {
340 assert(Output.isNothing() && "Invalid output.");
341 }
342
343 assert(Input.isFilename() && "Invalid input.");
344
345 addDashXForInput(Args, Input, CmdArgs);
346
347 CmdArgs.push_back(Input.getFilename());
348
349 // TODO: Replace flang-new with flang once the new driver replaces the
350 // throwaway driver
351 const char *Exec = Args.MakeArgString(D.GetProgramPath("flang-new", TC));
352 C.addCommand(std::make_unique<Command>(JA, *this,
353 ResponseFileSupport::AtFileUTF8(),
354 Exec, CmdArgs, Inputs, Output));
355 }
356
Flang(const ToolChain & TC)357 Flang::Flang(const ToolChain &TC) : Tool("flang-new", "flang frontend", TC) {}
358
~Flang()359 Flang::~Flang() {}
360