316 lines · cpp
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-exception6//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/Options/Options.h"15#include "llvm/TargetParser/Host.h"16#include "llvm/TargetParser/LoongArchTargetParser.h"17 18using namespace clang::driver;19using namespace clang::driver::tools;20using namespace clang;21using namespace llvm::opt;22 23StringRef 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 else49 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 with72 // the higher-priority settings implied by -m*-float.73 //74 // ImpliedABI and ImpliedFPU are guaranteed to have valid values because75 // 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 if103 // 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) to113 // represent the {ILP32,LP64}D ABIs, but in Feb 2023 Loongson decided to114 // drop the explicit suffix in favor of unmarked `-gnu` for the115 // "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 it119 // for some time, until it is clear that everyone else has migrated away120 // from it.121 [[fallthrough]];122 case llvm::Triple::GNU:123 default:124 return IsLA32 ? "ilp32d" : "lp64d";125 }126}127 128void 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() && (!Args.hasArgNoClaim(options::OPT_march_EQ)))134 Features.push_back("+lsx");135 136 // -mrelax is default, unless -mno-relax is specified.137 // FIXME: Only for loongarch64, loongarch32 has not been fully verified.138 if (Args.hasFlag(options::OPT_mrelax, options::OPT_mno_relax,139 Triple.isLoongArch64() ? true : false)) {140 Features.push_back("+relax");141 // -gsplit-dwarf -mrelax requires DW_AT_high_pc/DW_AT_ranges/... indexing142 // into .debug_addr, which is currently not implemented.143 Arg *A;144 if (getDebugFissionKind(D, Args, A) != DwarfFissionKind::None)145 D.Diag(clang::diag::err_drv_loongarch_unsupported_with_linker_relaxation)146 << A->getAsString(Args);147 } else if (Args.getLastArg(options::OPT_mno_relax)) {148 Features.push_back("-relax");149 }150 151 std::string ArchName;152 const Arg *MArch = Args.getLastArg(options::OPT_march_EQ);153 if (MArch)154 ArchName = MArch->getValue();155 ArchName = postProcessTargetCPUString(ArchName, Triple);156 llvm::LoongArch::getArchFeatures(ArchName, Features);157 if (MArch && StringRef(MArch->getValue()) == "native")158 for (auto &F : llvm::sys::getHostCPUFeatures())159 Features.push_back(160 Args.MakeArgString((F.second ? "+" : "-") + F.first()));161 162 // Select floating-point features determined by -mdouble-float,163 // -msingle-float, -msoft-float and -mfpu.164 // Note: -m*-float wins any other options.165 if (const Arg *A = Args.getLastArg(options::OPT_mdouble_float,166 options::OPT_msingle_float,167 options::OPT_msoft_float)) {168 if (A->getOption().matches(options::OPT_mdouble_float)) {169 Features.push_back("+f");170 Features.push_back("+d");171 } else if (A->getOption().matches(options::OPT_msingle_float)) {172 Features.push_back("+f");173 Features.push_back("-d");174 Features.push_back("-lsx");175 } else /*Soft-float*/ {176 Features.push_back("-f");177 Features.push_back("-d");178 Features.push_back("-lsx");179 }180 } else if (const Arg *A = Args.getLastArg(options::OPT_mfpu_EQ)) {181 StringRef FPU = A->getValue();182 if (FPU == "64") {183 Features.push_back("+f");184 Features.push_back("+d");185 } else if (FPU == "32") {186 Features.push_back("+f");187 Features.push_back("-d");188 Features.push_back("-lsx");189 } else if (FPU == "0" || FPU == "none") {190 Features.push_back("-f");191 Features.push_back("-d");192 Features.push_back("-lsx");193 } else {194 D.Diag(diag::err_drv_loongarch_invalid_mfpu_EQ) << FPU;195 }196 }197 198 // Accept but warn about these TargetSpecific options.199 if (Arg *A = Args.getLastArgNoClaim(options::OPT_mabi_EQ))200 A->ignoreTargetSpecific();201 if (Arg *A = Args.getLastArgNoClaim(options::OPT_mfpu_EQ))202 A->ignoreTargetSpecific();203 if (Arg *A = Args.getLastArgNoClaim(options::OPT_msimd_EQ))204 A->ignoreTargetSpecific();205 206 // Select lsx/lasx feature determined by -msimd=.207 // Option -msimd= precedes -m[no-]lsx and -m[no-]lasx.208 if (const Arg *A = Args.getLastArg(options::OPT_msimd_EQ)) {209 StringRef MSIMD = A->getValue();210 if (MSIMD == "lsx") {211 // Option -msimd=lsx depends on 64-bit FPU.212 // -m*-float and -mfpu=none/0/32 conflict with -msimd=lsx.213 if (llvm::is_contained(Features, "-d"))214 D.Diag(diag::err_drv_loongarch_wrong_fpu_width) << /*LSX*/ 0;215 else216 Features.push_back("+lsx");217 } else if (MSIMD == "lasx") {218 // Option -msimd=lasx depends on 64-bit FPU and LSX.219 // -m*-float, -mfpu=none/0/32 and -mno-lsx conflict with -msimd=lasx.220 if (llvm::is_contained(Features, "-d"))221 D.Diag(diag::err_drv_loongarch_wrong_fpu_width) << /*LASX*/ 1;222 else if (llvm::is_contained(Features, "-lsx"))223 D.Diag(diag::err_drv_loongarch_invalid_simd_option_combination);224 225 // The command options do not contain -mno-lasx.226 if (!Args.getLastArg(options::OPT_mno_lasx)) {227 Features.push_back("+lsx");228 Features.push_back("+lasx");229 }230 } else if (MSIMD == "none") {231 if (llvm::is_contained(Features, "+lsx"))232 Features.push_back("-lsx");233 if (llvm::is_contained(Features, "+lasx"))234 Features.push_back("-lasx");235 } else {236 D.Diag(diag::err_drv_loongarch_invalid_msimd_EQ) << MSIMD;237 }238 }239 240 // Select lsx feature determined by -m[no-]lsx.241 if (const Arg *A = Args.getLastArg(options::OPT_mlsx, options::OPT_mno_lsx)) {242 // LSX depends on 64-bit FPU.243 // -m*-float and -mfpu=none/0/32 conflict with -mlsx.244 if (A->getOption().matches(options::OPT_mlsx)) {245 if (llvm::find(Features, "-d") != Features.end())246 D.Diag(diag::err_drv_loongarch_wrong_fpu_width) << /*LSX*/ 0;247 else /*-mlsx*/248 Features.push_back("+lsx");249 } else /*-mno-lsx*/ {250 Features.push_back("-lsx");251 Features.push_back("-lasx");252 }253 }254 255 // Select lasx feature determined by -m[no-]lasx.256 if (const Arg *A =257 Args.getLastArg(options::OPT_mlasx, options::OPT_mno_lasx)) {258 // LASX depends on 64-bit FPU and LSX.259 // -mno-lsx conflicts with -mlasx.260 if (A->getOption().matches(options::OPT_mlasx)) {261 if (llvm::find(Features, "-d") != Features.end())262 D.Diag(diag::err_drv_loongarch_wrong_fpu_width) << /*LASX*/ 1;263 else { /*-mlasx*/264 Features.push_back("+lsx");265 Features.push_back("+lasx");266 }267 } else /*-mno-lasx*/268 Features.push_back("-lasx");269 }270 271 AddTargetFeature(Args, Features, options::OPT_mno_strict_align,272 options::OPT_mstrict_align, "ual");273 AddTargetFeature(Args, Features, options::OPT_mno_strict_align,274 options::OPT_mstrict_align, "ual");275 AddTargetFeature(Args, Features, options::OPT_mfrecipe,276 options::OPT_mno_frecipe, "frecipe");277 AddTargetFeature(Args, Features, options::OPT_mlam_bh,278 options::OPT_mno_lam_bh, "lam-bh");279 AddTargetFeature(Args, Features, options::OPT_mlamcas,280 options::OPT_mno_lamcas, "lamcas");281 AddTargetFeature(Args, Features, options::OPT_mld_seq_sa,282 options::OPT_mno_ld_seq_sa, "ld-seq-sa");283 AddTargetFeature(Args, Features, options::OPT_mdiv32,284 options::OPT_mno_div32, "div32");285 AddTargetFeature(Args, Features, options::OPT_mscq, options::OPT_mno_scq,286 "scq");287}288 289std::string loongarch::postProcessTargetCPUString(const std::string &CPU,290 const llvm::Triple &Triple) {291 std::string CPUString = CPU;292 if (CPUString == "native") {293 CPUString = llvm::sys::getHostCPUName();294 if (CPUString == "generic")295 CPUString = llvm::LoongArch::getDefaultArch(Triple.isLoongArch64());296 }297 if (CPUString.empty())298 CPUString = llvm::LoongArch::getDefaultArch(Triple.isLoongArch64());299 return CPUString;300}301 302std::string loongarch::getLoongArchTargetCPU(const llvm::opt::ArgList &Args,303 const llvm::Triple &Triple) {304 std::string CPU;305 std::string Arch;306 // If we have -march, use that.307 if (const Arg *A = Args.getLastArg(options::OPT_march_EQ)) {308 Arch = A->getValue();309 if (Arch == "la64v1.0" || Arch == "la64v1.1")310 CPU = llvm::LoongArch::getDefaultArch(Triple.isLoongArch64());311 else312 CPU = Arch;313 }314 return postProcessTargetCPUString(CPU, Triple);315}316