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. 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 30 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 47 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 54 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 62 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 83 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 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 238 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 357 Flang::Flang(const ToolChain &TC) : Tool("flang-new", "flang frontend", TC) {} 358 359 Flang::~Flang() {} 360