1 //===--- LoongArch.cpp - LoongArch Helpers for Tools ------------*- 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 "LoongArch.h" 10 #include "../Clang.h" 11 #include "ToolChains/CommonArgs.h" 12 #include "clang/Basic/DiagnosticDriver.h" 13 #include "clang/Driver/Driver.h" 14 #include "clang/Driver/DriverDiagnostic.h" 15 #include "clang/Driver/Options.h" 16 #include "llvm/TargetParser/Host.h" 17 #include "llvm/TargetParser/LoongArchTargetParser.h" 18 19 using namespace clang::driver; 20 using namespace clang::driver::tools; 21 using namespace clang; 22 using namespace llvm::opt; 23 24 StringRef loongarch::getLoongArchABI(const Driver &D, const ArgList &Args, 25 const llvm::Triple &Triple) { 26 assert((Triple.getArch() == llvm::Triple::loongarch32 || 27 Triple.getArch() == llvm::Triple::loongarch64) && 28 "Unexpected triple"); 29 bool IsLA32 = Triple.getArch() == llvm::Triple::loongarch32; 30 31 // Record -mabi value for later use. 32 const Arg *MABIArg = Args.getLastArg(options::OPT_mabi_EQ); 33 StringRef MABIValue; 34 if (MABIArg) { 35 MABIValue = MABIArg->getValue(); 36 } 37 38 // Parse -mfpu value for later use. 39 const Arg *MFPUArg = Args.getLastArg(options::OPT_mfpu_EQ); 40 int FPU = -1; 41 if (MFPUArg) { 42 StringRef V = MFPUArg->getValue(); 43 if (V == "64") 44 FPU = 64; 45 else if (V == "32") 46 FPU = 32; 47 else if (V == "0" || V == "none") 48 FPU = 0; 49 else 50 D.Diag(diag::err_drv_loongarch_invalid_mfpu_EQ) << V; 51 } 52 53 // Check -m*-float firstly since they have highest priority. 54 if (const Arg *A = Args.getLastArg(options::OPT_mdouble_float, 55 options::OPT_msingle_float, 56 options::OPT_msoft_float)) { 57 StringRef ImpliedABI; 58 int ImpliedFPU = -1; 59 if (A->getOption().matches(options::OPT_mdouble_float)) { 60 ImpliedABI = IsLA32 ? "ilp32d" : "lp64d"; 61 ImpliedFPU = 64; 62 } 63 if (A->getOption().matches(options::OPT_msingle_float)) { 64 ImpliedABI = IsLA32 ? "ilp32f" : "lp64f"; 65 ImpliedFPU = 32; 66 } 67 if (A->getOption().matches(options::OPT_msoft_float)) { 68 ImpliedABI = IsLA32 ? "ilp32s" : "lp64s"; 69 ImpliedFPU = 0; 70 } 71 72 // Check `-mabi=` and `-mfpu=` settings and report if they conflict with 73 // the higher-priority settings implied by -m*-float. 74 // 75 // ImpliedABI and ImpliedFPU are guaranteed to have valid values because 76 // one of the match arms must match if execution can arrive here at all. 77 if (!MABIValue.empty() && ImpliedABI != MABIValue) 78 D.Diag(diag::warn_drv_loongarch_conflicting_implied_val) 79 << MABIArg->getAsString(Args) << A->getAsString(Args) << ImpliedABI; 80 81 if (FPU != -1 && ImpliedFPU != FPU) 82 D.Diag(diag::warn_drv_loongarch_conflicting_implied_val) 83 << MFPUArg->getAsString(Args) << A->getAsString(Args) << ImpliedFPU; 84 85 return ImpliedABI; 86 } 87 88 // If `-mabi=` is specified, use it. 89 if (!MABIValue.empty()) 90 return MABIValue; 91 92 // Select abi based on -mfpu=xx. 93 switch (FPU) { 94 case 64: 95 return IsLA32 ? "ilp32d" : "lp64d"; 96 case 32: 97 return IsLA32 ? "ilp32f" : "lp64f"; 98 case 0: 99 return IsLA32 ? "ilp32s" : "lp64s"; 100 } 101 102 // Choose a default based on the triple. 103 // Honor the explicit ABI modifier suffix in triple's environment part if 104 // present, falling back to {ILP32,LP64}D otherwise. 105 switch (Triple.getEnvironment()) { 106 case llvm::Triple::GNUSF: 107 case llvm::Triple::MuslSF: 108 return IsLA32 ? "ilp32s" : "lp64s"; 109 case llvm::Triple::GNUF32: 110 case llvm::Triple::MuslF32: 111 return IsLA32 ? "ilp32f" : "lp64f"; 112 case llvm::Triple::GNUF64: 113 // This was originally permitted (and indeed the canonical way) to 114 // represent the {ILP32,LP64}D ABIs, but in Feb 2023 Loongson decided to 115 // drop the explicit suffix in favor of unmarked `-gnu` for the 116 // "general-purpose" ABIs, among other non-technical reasons. 117 // 118 // The spec change did not mention whether existing usages of "gnuf64" 119 // shall remain valid or not, so we are going to continue recognizing it 120 // for some time, until it is clear that everyone else has migrated away 121 // from it. 122 [[fallthrough]]; 123 case llvm::Triple::GNU: 124 default: 125 return IsLA32 ? "ilp32d" : "lp64d"; 126 } 127 } 128 129 void loongarch::getLoongArchTargetFeatures(const Driver &D, 130 const llvm::Triple &Triple, 131 const ArgList &Args, 132 std::vector<StringRef> &Features) { 133 // Enable the `lsx` feature on 64-bit LoongArch by default. 134 if (Triple.isLoongArch64() && 135 (!Args.hasArgNoClaim(clang::driver::options::OPT_march_EQ))) 136 Features.push_back("+lsx"); 137 138 // FIXME: Now we must use -mrelax to enable relax, maybe -mrelax will be set 139 // as default in the future. 140 if (const Arg *A = 141 Args.getLastArg(options::OPT_mrelax, options::OPT_mno_relax)) { 142 if (A->getOption().matches(options::OPT_mrelax)) { 143 Features.push_back("+relax"); 144 // -gsplit-dwarf -mrelax requires DW_AT_high_pc/DW_AT_ranges/... indexing 145 // into .debug_addr, which is currently not implemented. 146 Arg *A; 147 if (getDebugFissionKind(D, Args, A) != DwarfFissionKind::None) 148 D.Diag( 149 clang::diag::err_drv_loongarch_unsupported_with_linker_relaxation) 150 << A->getAsString(Args); 151 } else { 152 Features.push_back("-relax"); 153 } 154 } 155 156 std::string ArchName; 157 const Arg *MArch = Args.getLastArg(options::OPT_march_EQ); 158 if (MArch) 159 ArchName = MArch->getValue(); 160 ArchName = postProcessTargetCPUString(ArchName, Triple); 161 llvm::LoongArch::getArchFeatures(ArchName, Features); 162 if (MArch && StringRef(MArch->getValue()) == "native") 163 for (auto &F : llvm::sys::getHostCPUFeatures()) 164 Features.push_back( 165 Args.MakeArgString((F.second ? "+" : "-") + F.first())); 166 167 // Select floating-point features determined by -mdouble-float, 168 // -msingle-float, -msoft-float and -mfpu. 169 // Note: -m*-float wins any other options. 170 if (const Arg *A = Args.getLastArg(options::OPT_mdouble_float, 171 options::OPT_msingle_float, 172 options::OPT_msoft_float)) { 173 if (A->getOption().matches(options::OPT_mdouble_float)) { 174 Features.push_back("+f"); 175 Features.push_back("+d"); 176 } else if (A->getOption().matches(options::OPT_msingle_float)) { 177 Features.push_back("+f"); 178 Features.push_back("-d"); 179 Features.push_back("-lsx"); 180 } else /*Soft-float*/ { 181 Features.push_back("-f"); 182 Features.push_back("-d"); 183 Features.push_back("-lsx"); 184 } 185 } else if (const Arg *A = Args.getLastArg(options::OPT_mfpu_EQ)) { 186 StringRef FPU = A->getValue(); 187 if (FPU == "64") { 188 Features.push_back("+f"); 189 Features.push_back("+d"); 190 } else if (FPU == "32") { 191 Features.push_back("+f"); 192 Features.push_back("-d"); 193 Features.push_back("-lsx"); 194 } else if (FPU == "0" || FPU == "none") { 195 Features.push_back("-f"); 196 Features.push_back("-d"); 197 Features.push_back("-lsx"); 198 } else { 199 D.Diag(diag::err_drv_loongarch_invalid_mfpu_EQ) << FPU; 200 } 201 } 202 203 // Accept but warn about these TargetSpecific options. 204 if (Arg *A = Args.getLastArgNoClaim(options::OPT_mabi_EQ)) 205 A->ignoreTargetSpecific(); 206 if (Arg *A = Args.getLastArgNoClaim(options::OPT_mfpu_EQ)) 207 A->ignoreTargetSpecific(); 208 if (Arg *A = Args.getLastArgNoClaim(options::OPT_msimd_EQ)) 209 A->ignoreTargetSpecific(); 210 211 // Select lsx/lasx feature determined by -msimd=. 212 // Option -msimd= precedes -m[no-]lsx and -m[no-]lasx. 213 if (const Arg *A = Args.getLastArg(options::OPT_msimd_EQ)) { 214 StringRef MSIMD = A->getValue(); 215 if (MSIMD == "lsx") { 216 // Option -msimd=lsx depends on 64-bit FPU. 217 // -m*-float and -mfpu=none/0/32 conflict with -msimd=lsx. 218 if (llvm::find(Features, "-d") != Features.end()) 219 D.Diag(diag::err_drv_loongarch_wrong_fpu_width) << /*LSX*/ 0; 220 else 221 Features.push_back("+lsx"); 222 } else if (MSIMD == "lasx") { 223 // Option -msimd=lasx depends on 64-bit FPU and LSX. 224 // -m*-float, -mfpu=none/0/32 and -mno-lsx conflict with -msimd=lasx. 225 if (llvm::find(Features, "-d") != Features.end()) 226 D.Diag(diag::err_drv_loongarch_wrong_fpu_width) << /*LASX*/ 1; 227 else if (llvm::find(Features, "-lsx") != Features.end()) 228 D.Diag(diag::err_drv_loongarch_invalid_simd_option_combination); 229 230 // The command options do not contain -mno-lasx. 231 if (!Args.getLastArg(options::OPT_mno_lasx)) { 232 Features.push_back("+lsx"); 233 Features.push_back("+lasx"); 234 } 235 } else if (MSIMD == "none") { 236 if (llvm::find(Features, "+lsx") != Features.end()) 237 Features.push_back("-lsx"); 238 if (llvm::find(Features, "+lasx") != Features.end()) 239 Features.push_back("-lasx"); 240 } else { 241 D.Diag(diag::err_drv_loongarch_invalid_msimd_EQ) << MSIMD; 242 } 243 } 244 245 // Select lsx feature determined by -m[no-]lsx. 246 if (const Arg *A = Args.getLastArg(options::OPT_mlsx, options::OPT_mno_lsx)) { 247 // LSX depends on 64-bit FPU. 248 // -m*-float and -mfpu=none/0/32 conflict with -mlsx. 249 if (A->getOption().matches(options::OPT_mlsx)) { 250 if (llvm::find(Features, "-d") != Features.end()) 251 D.Diag(diag::err_drv_loongarch_wrong_fpu_width) << /*LSX*/ 0; 252 else /*-mlsx*/ 253 Features.push_back("+lsx"); 254 } else /*-mno-lsx*/ { 255 Features.push_back("-lsx"); 256 } 257 } 258 259 // Select lasx feature determined by -m[no-]lasx. 260 if (const Arg *A = 261 Args.getLastArg(options::OPT_mlasx, options::OPT_mno_lasx)) { 262 // LASX depends on 64-bit FPU and LSX. 263 // -mno-lsx conflicts with -mlasx. 264 if (A->getOption().matches(options::OPT_mlasx)) { 265 if (llvm::find(Features, "-d") != Features.end()) 266 D.Diag(diag::err_drv_loongarch_wrong_fpu_width) << /*LASX*/ 1; 267 else { /*-mlasx*/ 268 Features.push_back("+lsx"); 269 Features.push_back("+lasx"); 270 } 271 } else /*-mno-lasx*/ 272 Features.push_back("-lasx"); 273 } 274 275 AddTargetFeature(Args, Features, options::OPT_mno_strict_align, 276 options::OPT_mstrict_align, "ual"); 277 AddTargetFeature(Args, Features, options::OPT_mno_strict_align, 278 options::OPT_mstrict_align, "ual"); 279 AddTargetFeature(Args, Features, options::OPT_mfrecipe, 280 options::OPT_mno_frecipe, "frecipe"); 281 AddTargetFeature(Args, Features, options::OPT_mlam_bh, 282 options::OPT_mno_lam_bh, "lam-bh"); 283 AddTargetFeature(Args, Features, options::OPT_mlamcas, 284 options::OPT_mno_lamcas, "lamcas"); 285 AddTargetFeature(Args, Features, options::OPT_mld_seq_sa, 286 options::OPT_mno_ld_seq_sa, "ld-seq-sa"); 287 AddTargetFeature(Args, Features, options::OPT_mdiv32, 288 options::OPT_mno_div32, "div32"); 289 AddTargetFeature(Args, Features, options::OPT_mscq, options::OPT_mno_scq, 290 "scq"); 291 } 292 293 std::string loongarch::postProcessTargetCPUString(const std::string &CPU, 294 const llvm::Triple &Triple) { 295 std::string CPUString = CPU; 296 if (CPUString == "native") { 297 CPUString = llvm::sys::getHostCPUName(); 298 if (CPUString == "generic") 299 CPUString = llvm::LoongArch::getDefaultArch(Triple.isLoongArch64()); 300 } 301 if (CPUString.empty()) 302 CPUString = llvm::LoongArch::getDefaultArch(Triple.isLoongArch64()); 303 return CPUString; 304 } 305 306 std::string loongarch::getLoongArchTargetCPU(const llvm::opt::ArgList &Args, 307 const llvm::Triple &Triple) { 308 std::string CPU; 309 std::string Arch; 310 // If we have -march, use that. 311 if (const Arg *A = Args.getLastArg(options::OPT_march_EQ)) { 312 Arch = A->getValue(); 313 if (Arch == "la64v1.0" || Arch == "la64v1.1") 314 CPU = llvm::LoongArch::getDefaultArch(Triple.isLoongArch64()); 315 else 316 CPU = Arch; 317 } 318 return postProcessTargetCPUString(CPU, Triple); 319 } 320