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