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