| 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 "clang/Basic/DiagnosticDriver.h" |
| 12 | #include "clang/Driver/CommonArgs.h" |
| 13 | #include "clang/Driver/Driver.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(Ids: 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(Ids: 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(DiagID: 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(Ids: options::OPT_mdouble_float, |
| 54 | Ids: options::OPT_msingle_float, |
| 55 | Ids: options::OPT_msoft_float)) { |
| 56 | StringRef ImpliedABI; |
| 57 | int ImpliedFPU = -1; |
| 58 | if (A->getOption().matches(ID: options::OPT_mdouble_float)) { |
| 59 | ImpliedABI = IsLA32 ? "ilp32d" : "lp64d" ; |
| 60 | ImpliedFPU = 64; |
| 61 | } |
| 62 | if (A->getOption().matches(ID: options::OPT_msingle_float)) { |
| 63 | ImpliedABI = IsLA32 ? "ilp32f" : "lp64f" ; |
| 64 | ImpliedFPU = 32; |
| 65 | } |
| 66 | if (A->getOption().matches(ID: 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(DiagID: 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(DiagID: 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 | case llvm::Triple::MuslSF: |
| 107 | return IsLA32 ? "ilp32s" : "lp64s" ; |
| 108 | case llvm::Triple::GNUF32: |
| 109 | case llvm::Triple::MuslF32: |
| 110 | return IsLA32 ? "ilp32f" : "lp64f" ; |
| 111 | case llvm::Triple::GNUF64: |
| 112 | // This was originally permitted (and indeed the canonical way) to |
| 113 | // represent the {ILP32,LP64}D ABIs, but in Feb 2023 Loongson decided to |
| 114 | // drop the explicit suffix in favor of unmarked `-gnu` for the |
| 115 | // "general-purpose" ABIs, among other non-technical reasons. |
| 116 | // |
| 117 | // The spec change did not mention whether existing usages of "gnuf64" |
| 118 | // shall remain valid or not, so we are going to continue recognizing it |
| 119 | // for some time, until it is clear that everyone else has migrated away |
| 120 | // from it. |
| 121 | [[fallthrough]]; |
| 122 | case llvm::Triple::GNU: |
| 123 | default: |
| 124 | return IsLA32 ? "ilp32d" : "lp64d" ; |
| 125 | } |
| 126 | } |
| 127 | |
| 128 | void loongarch::getLoongArchTargetFeatures(const Driver &D, |
| 129 | const llvm::Triple &Triple, |
| 130 | const ArgList &Args, |
| 131 | std::vector<StringRef> &Features) { |
| 132 | // Enable the `lsx` feature on 64-bit LoongArch by default. |
| 133 | if (Triple.isLoongArch64() && |
| 134 | (!Args.hasArgNoClaim(Ids: clang::driver::options::OPT_march_EQ))) |
| 135 | Features.push_back(x: "+lsx" ); |
| 136 | |
| 137 | // FIXME: Now we must use -mrelax to enable relax, maybe -mrelax will be set |
| 138 | // as default in the future. |
| 139 | if (const Arg *A = |
| 140 | Args.getLastArg(Ids: options::OPT_mrelax, Ids: options::OPT_mno_relax)) { |
| 141 | if (A->getOption().matches(ID: options::OPT_mrelax)) { |
| 142 | Features.push_back(x: "+relax" ); |
| 143 | // -gsplit-dwarf -mrelax requires DW_AT_high_pc/DW_AT_ranges/... indexing |
| 144 | // into .debug_addr, which is currently not implemented. |
| 145 | Arg *A; |
| 146 | if (getDebugFissionKind(D, Args, Arg&: A) != DwarfFissionKind::None) |
| 147 | D.Diag( |
| 148 | DiagID: clang::diag::err_drv_loongarch_unsupported_with_linker_relaxation) |
| 149 | << A->getAsString(Args); |
| 150 | } else { |
| 151 | Features.push_back(x: "-relax" ); |
| 152 | } |
| 153 | } |
| 154 | |
| 155 | std::string ArchName; |
| 156 | const Arg *MArch = Args.getLastArg(Ids: options::OPT_march_EQ); |
| 157 | if (MArch) |
| 158 | ArchName = MArch->getValue(); |
| 159 | ArchName = postProcessTargetCPUString(CPU: ArchName, Triple); |
| 160 | llvm::LoongArch::getArchFeatures(Arch: ArchName, Features); |
| 161 | if (MArch && StringRef(MArch->getValue()) == "native" ) |
| 162 | for (auto &F : llvm::sys::getHostCPUFeatures()) |
| 163 | Features.push_back( |
| 164 | x: Args.MakeArgString(Str: (F.second ? "+" : "-" ) + F.first())); |
| 165 | |
| 166 | // Select floating-point features determined by -mdouble-float, |
| 167 | // -msingle-float, -msoft-float and -mfpu. |
| 168 | // Note: -m*-float wins any other options. |
| 169 | if (const Arg *A = Args.getLastArg(Ids: options::OPT_mdouble_float, |
| 170 | Ids: options::OPT_msingle_float, |
| 171 | Ids: options::OPT_msoft_float)) { |
| 172 | if (A->getOption().matches(ID: options::OPT_mdouble_float)) { |
| 173 | Features.push_back(x: "+f" ); |
| 174 | Features.push_back(x: "+d" ); |
| 175 | } else if (A->getOption().matches(ID: options::OPT_msingle_float)) { |
| 176 | Features.push_back(x: "+f" ); |
| 177 | Features.push_back(x: "-d" ); |
| 178 | Features.push_back(x: "-lsx" ); |
| 179 | } else /*Soft-float*/ { |
| 180 | Features.push_back(x: "-f" ); |
| 181 | Features.push_back(x: "-d" ); |
| 182 | Features.push_back(x: "-lsx" ); |
| 183 | } |
| 184 | } else if (const Arg *A = Args.getLastArg(Ids: options::OPT_mfpu_EQ)) { |
| 185 | StringRef FPU = A->getValue(); |
| 186 | if (FPU == "64" ) { |
| 187 | Features.push_back(x: "+f" ); |
| 188 | Features.push_back(x: "+d" ); |
| 189 | } else if (FPU == "32" ) { |
| 190 | Features.push_back(x: "+f" ); |
| 191 | Features.push_back(x: "-d" ); |
| 192 | Features.push_back(x: "-lsx" ); |
| 193 | } else if (FPU == "0" || FPU == "none" ) { |
| 194 | Features.push_back(x: "-f" ); |
| 195 | Features.push_back(x: "-d" ); |
| 196 | Features.push_back(x: "-lsx" ); |
| 197 | } else { |
| 198 | D.Diag(DiagID: diag::err_drv_loongarch_invalid_mfpu_EQ) << FPU; |
| 199 | } |
| 200 | } |
| 201 | |
| 202 | // Accept but warn about these TargetSpecific options. |
| 203 | if (Arg *A = Args.getLastArgNoClaim(Ids: options::OPT_mabi_EQ)) |
| 204 | A->ignoreTargetSpecific(); |
| 205 | if (Arg *A = Args.getLastArgNoClaim(Ids: options::OPT_mfpu_EQ)) |
| 206 | A->ignoreTargetSpecific(); |
| 207 | if (Arg *A = Args.getLastArgNoClaim(Ids: options::OPT_msimd_EQ)) |
| 208 | A->ignoreTargetSpecific(); |
| 209 | |
| 210 | // Select lsx/lasx feature determined by -msimd=. |
| 211 | // Option -msimd= precedes -m[no-]lsx and -m[no-]lasx. |
| 212 | if (const Arg *A = Args.getLastArg(Ids: options::OPT_msimd_EQ)) { |
| 213 | StringRef MSIMD = A->getValue(); |
| 214 | if (MSIMD == "lsx" ) { |
| 215 | // Option -msimd=lsx depends on 64-bit FPU. |
| 216 | // -m*-float and -mfpu=none/0/32 conflict with -msimd=lsx. |
| 217 | if (llvm::is_contained(Range&: Features, Element: "-d" )) |
| 218 | D.Diag(DiagID: diag::err_drv_loongarch_wrong_fpu_width) << /*LSX*/ 0; |
| 219 | else |
| 220 | Features.push_back(x: "+lsx" ); |
| 221 | } else if (MSIMD == "lasx" ) { |
| 222 | // Option -msimd=lasx depends on 64-bit FPU and LSX. |
| 223 | // -m*-float, -mfpu=none/0/32 and -mno-lsx conflict with -msimd=lasx. |
| 224 | if (llvm::is_contained(Range&: Features, Element: "-d" )) |
| 225 | D.Diag(DiagID: diag::err_drv_loongarch_wrong_fpu_width) << /*LASX*/ 1; |
| 226 | else if (llvm::is_contained(Range&: Features, Element: "-lsx" )) |
| 227 | D.Diag(DiagID: diag::err_drv_loongarch_invalid_simd_option_combination); |
| 228 | |
| 229 | // The command options do not contain -mno-lasx. |
| 230 | if (!Args.getLastArg(Ids: options::OPT_mno_lasx)) { |
| 231 | Features.push_back(x: "+lsx" ); |
| 232 | Features.push_back(x: "+lasx" ); |
| 233 | } |
| 234 | } else if (MSIMD == "none" ) { |
| 235 | if (llvm::is_contained(Range&: Features, Element: "+lsx" )) |
| 236 | Features.push_back(x: "-lsx" ); |
| 237 | if (llvm::is_contained(Range&: Features, Element: "+lasx" )) |
| 238 | Features.push_back(x: "-lasx" ); |
| 239 | } else { |
| 240 | D.Diag(DiagID: diag::err_drv_loongarch_invalid_msimd_EQ) << MSIMD; |
| 241 | } |
| 242 | } |
| 243 | |
| 244 | // Select lsx feature determined by -m[no-]lsx. |
| 245 | if (const Arg *A = Args.getLastArg(Ids: options::OPT_mlsx, Ids: options::OPT_mno_lsx)) { |
| 246 | // LSX depends on 64-bit FPU. |
| 247 | // -m*-float and -mfpu=none/0/32 conflict with -mlsx. |
| 248 | if (A->getOption().matches(ID: options::OPT_mlsx)) { |
| 249 | if (llvm::find(Range&: Features, Val: "-d" ) != Features.end()) |
| 250 | D.Diag(DiagID: diag::err_drv_loongarch_wrong_fpu_width) << /*LSX*/ 0; |
| 251 | else /*-mlsx*/ |
| 252 | Features.push_back(x: "+lsx" ); |
| 253 | } else /*-mno-lsx*/ { |
| 254 | Features.push_back(x: "-lsx" ); |
| 255 | Features.push_back(x: "-lasx" ); |
| 256 | } |
| 257 | } |
| 258 | |
| 259 | // Select lasx feature determined by -m[no-]lasx. |
| 260 | if (const Arg *A = |
| 261 | Args.getLastArg(Ids: options::OPT_mlasx, Ids: options::OPT_mno_lasx)) { |
| 262 | // LASX depends on 64-bit FPU and LSX. |
| 263 | // -mno-lsx conflicts with -mlasx. |
| 264 | if (A->getOption().matches(ID: options::OPT_mlasx)) { |
| 265 | if (llvm::find(Range&: Features, Val: "-d" ) != Features.end()) |
| 266 | D.Diag(DiagID: diag::err_drv_loongarch_wrong_fpu_width) << /*LASX*/ 1; |
| 267 | else { /*-mlasx*/ |
| 268 | Features.push_back(x: "+lsx" ); |
| 269 | Features.push_back(x: "+lasx" ); |
| 270 | } |
| 271 | } else /*-mno-lasx*/ |
| 272 | Features.push_back(x: "-lasx" ); |
| 273 | } |
| 274 | |
| 275 | AddTargetFeature(Args, Features, OnOpt: options::OPT_mno_strict_align, |
| 276 | OffOpt: options::OPT_mstrict_align, FeatureName: "ual" ); |
| 277 | AddTargetFeature(Args, Features, OnOpt: options::OPT_mno_strict_align, |
| 278 | OffOpt: options::OPT_mstrict_align, FeatureName: "ual" ); |
| 279 | AddTargetFeature(Args, Features, OnOpt: options::OPT_mfrecipe, |
| 280 | OffOpt: options::OPT_mno_frecipe, FeatureName: "frecipe" ); |
| 281 | AddTargetFeature(Args, Features, OnOpt: options::OPT_mlam_bh, |
| 282 | OffOpt: options::OPT_mno_lam_bh, FeatureName: "lam-bh" ); |
| 283 | AddTargetFeature(Args, Features, OnOpt: options::OPT_mlamcas, |
| 284 | OffOpt: options::OPT_mno_lamcas, FeatureName: "lamcas" ); |
| 285 | AddTargetFeature(Args, Features, OnOpt: options::OPT_mld_seq_sa, |
| 286 | OffOpt: options::OPT_mno_ld_seq_sa, FeatureName: "ld-seq-sa" ); |
| 287 | AddTargetFeature(Args, Features, OnOpt: options::OPT_mdiv32, |
| 288 | OffOpt: options::OPT_mno_div32, FeatureName: "div32" ); |
| 289 | AddTargetFeature(Args, Features, OnOpt: options::OPT_mscq, OffOpt: options::OPT_mno_scq, |
| 290 | FeatureName: "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(Is64Bit: Triple.isLoongArch64()); |
| 300 | } |
| 301 | if (CPUString.empty()) |
| 302 | CPUString = llvm::LoongArch::getDefaultArch(Is64Bit: 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(Ids: options::OPT_march_EQ)) { |
| 312 | Arch = A->getValue(); |
| 313 | if (Arch == "la64v1.0" || Arch == "la64v1.1" ) |
| 314 | CPU = llvm::LoongArch::getDefaultArch(Is64Bit: Triple.isLoongArch64()); |
| 315 | else |
| 316 | CPU = Arch; |
| 317 | } |
| 318 | return postProcessTargetCPUString(CPU, Triple); |
| 319 | } |
| 320 | |