1bdd1243dSDimitry Andric //===--- LoongArch.cpp - LoongArch Helpers for Tools ------------*- C++ -*-===// 2bdd1243dSDimitry Andric // 3bdd1243dSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4bdd1243dSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5bdd1243dSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6bdd1243dSDimitry Andric // 7bdd1243dSDimitry Andric //===----------------------------------------------------------------------===// 8bdd1243dSDimitry Andric 9bdd1243dSDimitry Andric #include "LoongArch.h" 1006c3fb27SDimitry Andric #include "ToolChains/CommonArgs.h" 11bdd1243dSDimitry Andric #include "clang/Basic/DiagnosticDriver.h" 12bdd1243dSDimitry Andric #include "clang/Driver/Driver.h" 13bdd1243dSDimitry Andric #include "clang/Driver/DriverDiagnostic.h" 14bdd1243dSDimitry Andric #include "clang/Driver/Options.h" 158a4dda33SDimitry Andric #include "llvm/TargetParser/Host.h" 1606c3fb27SDimitry Andric #include "llvm/TargetParser/LoongArchTargetParser.h" 17bdd1243dSDimitry Andric 18bdd1243dSDimitry Andric using namespace clang::driver; 19bdd1243dSDimitry Andric using namespace clang::driver::tools; 20bdd1243dSDimitry Andric using namespace clang; 21bdd1243dSDimitry Andric using namespace llvm::opt; 22bdd1243dSDimitry Andric 23bdd1243dSDimitry Andric StringRef loongarch::getLoongArchABI(const Driver &D, const ArgList &Args, 24bdd1243dSDimitry Andric const llvm::Triple &Triple) { 25bdd1243dSDimitry Andric assert((Triple.getArch() == llvm::Triple::loongarch32 || 26bdd1243dSDimitry Andric Triple.getArch() == llvm::Triple::loongarch64) && 27bdd1243dSDimitry Andric "Unexpected triple"); 28bdd1243dSDimitry Andric bool IsLA32 = Triple.getArch() == llvm::Triple::loongarch32; 29bdd1243dSDimitry Andric 3006c3fb27SDimitry Andric // Record -mabi value for later use. 3106c3fb27SDimitry Andric const Arg *MABIArg = Args.getLastArg(options::OPT_mabi_EQ); 3206c3fb27SDimitry Andric StringRef MABIValue; 3306c3fb27SDimitry Andric if (MABIArg) { 3406c3fb27SDimitry Andric MABIValue = MABIArg->getValue(); 3506c3fb27SDimitry Andric } 3606c3fb27SDimitry Andric 3706c3fb27SDimitry Andric // Parse -mfpu value for later use. 3806c3fb27SDimitry Andric const Arg *MFPUArg = Args.getLastArg(options::OPT_mfpu_EQ); 3906c3fb27SDimitry Andric int FPU = -1; 4006c3fb27SDimitry Andric if (MFPUArg) { 4106c3fb27SDimitry Andric StringRef V = MFPUArg->getValue(); 4206c3fb27SDimitry Andric if (V == "64") 4306c3fb27SDimitry Andric FPU = 64; 4406c3fb27SDimitry Andric else if (V == "32") 4506c3fb27SDimitry Andric FPU = 32; 4606c3fb27SDimitry Andric else if (V == "0" || V == "none") 4706c3fb27SDimitry Andric FPU = 0; 4806c3fb27SDimitry Andric else 4906c3fb27SDimitry Andric D.Diag(diag::err_drv_loongarch_invalid_mfpu_EQ) << V; 5006c3fb27SDimitry Andric } 5106c3fb27SDimitry Andric 52bdd1243dSDimitry Andric // Check -m*-float firstly since they have highest priority. 53bdd1243dSDimitry Andric if (const Arg *A = Args.getLastArg(options::OPT_mdouble_float, 54bdd1243dSDimitry Andric options::OPT_msingle_float, 55bdd1243dSDimitry Andric options::OPT_msoft_float)) { 5606c3fb27SDimitry Andric StringRef ImpliedABI; 5706c3fb27SDimitry Andric int ImpliedFPU = -1; 5806c3fb27SDimitry Andric if (A->getOption().matches(options::OPT_mdouble_float)) { 5906c3fb27SDimitry Andric ImpliedABI = IsLA32 ? "ilp32d" : "lp64d"; 6006c3fb27SDimitry Andric ImpliedFPU = 64; 6106c3fb27SDimitry Andric } 6206c3fb27SDimitry Andric if (A->getOption().matches(options::OPT_msingle_float)) { 6306c3fb27SDimitry Andric ImpliedABI = IsLA32 ? "ilp32f" : "lp64f"; 6406c3fb27SDimitry Andric ImpliedFPU = 32; 6506c3fb27SDimitry Andric } 6606c3fb27SDimitry Andric if (A->getOption().matches(options::OPT_msoft_float)) { 6706c3fb27SDimitry Andric ImpliedABI = IsLA32 ? "ilp32s" : "lp64s"; 6806c3fb27SDimitry Andric ImpliedFPU = 0; 6906c3fb27SDimitry Andric } 7006c3fb27SDimitry Andric 7106c3fb27SDimitry Andric // Check `-mabi=` and `-mfpu=` settings and report if they conflict with 7206c3fb27SDimitry Andric // the higher-priority settings implied by -m*-float. 7306c3fb27SDimitry Andric // 7406c3fb27SDimitry Andric // ImpliedABI and ImpliedFPU are guaranteed to have valid values because 7506c3fb27SDimitry Andric // one of the match arms must match if execution can arrive here at all. 7606c3fb27SDimitry Andric if (!MABIValue.empty() && ImpliedABI != MABIValue) 7706c3fb27SDimitry Andric D.Diag(diag::warn_drv_loongarch_conflicting_implied_val) 7806c3fb27SDimitry Andric << MABIArg->getAsString(Args) << A->getAsString(Args) << ImpliedABI; 7906c3fb27SDimitry Andric 8006c3fb27SDimitry Andric if (FPU != -1 && ImpliedFPU != FPU) 8106c3fb27SDimitry Andric D.Diag(diag::warn_drv_loongarch_conflicting_implied_val) 8206c3fb27SDimitry Andric << MFPUArg->getAsString(Args) << A->getAsString(Args) << ImpliedFPU; 8306c3fb27SDimitry Andric 8406c3fb27SDimitry Andric return ImpliedABI; 85bdd1243dSDimitry Andric } 86bdd1243dSDimitry Andric 87bdd1243dSDimitry Andric // If `-mabi=` is specified, use it. 8806c3fb27SDimitry Andric if (!MABIValue.empty()) 8906c3fb27SDimitry Andric return MABIValue; 90bdd1243dSDimitry Andric 91bdd1243dSDimitry Andric // Select abi based on -mfpu=xx. 9206c3fb27SDimitry Andric switch (FPU) { 9306c3fb27SDimitry Andric case 64: 94bdd1243dSDimitry Andric return IsLA32 ? "ilp32d" : "lp64d"; 9506c3fb27SDimitry Andric case 32: 96bdd1243dSDimitry Andric return IsLA32 ? "ilp32f" : "lp64f"; 9706c3fb27SDimitry Andric case 0: 98bdd1243dSDimitry Andric return IsLA32 ? "ilp32s" : "lp64s"; 99bdd1243dSDimitry Andric } 100bdd1243dSDimitry Andric 101bdd1243dSDimitry Andric // Choose a default based on the triple. 10206c3fb27SDimitry Andric // Honor the explicit ABI modifier suffix in triple's environment part if 10306c3fb27SDimitry Andric // present, falling back to {ILP32,LP64}D otherwise. 10406c3fb27SDimitry Andric switch (Triple.getEnvironment()) { 10506c3fb27SDimitry Andric case llvm::Triple::GNUSF: 10606c3fb27SDimitry Andric return IsLA32 ? "ilp32s" : "lp64s"; 10706c3fb27SDimitry Andric case llvm::Triple::GNUF32: 10806c3fb27SDimitry Andric return IsLA32 ? "ilp32f" : "lp64f"; 10906c3fb27SDimitry Andric case llvm::Triple::GNUF64: 11006c3fb27SDimitry Andric // This was originally permitted (and indeed the canonical way) to 11106c3fb27SDimitry Andric // represent the {ILP32,LP64}D ABIs, but in Feb 2023 Loongson decided to 11206c3fb27SDimitry Andric // drop the explicit suffix in favor of unmarked `-gnu` for the 11306c3fb27SDimitry Andric // "general-purpose" ABIs, among other non-technical reasons. 11406c3fb27SDimitry Andric // 11506c3fb27SDimitry Andric // The spec change did not mention whether existing usages of "gnuf64" 11606c3fb27SDimitry Andric // shall remain valid or not, so we are going to continue recognizing it 11706c3fb27SDimitry Andric // for some time, until it is clear that everyone else has migrated away 11806c3fb27SDimitry Andric // from it. 11906c3fb27SDimitry Andric [[fallthrough]]; 12006c3fb27SDimitry Andric case llvm::Triple::GNU: 12106c3fb27SDimitry Andric default: 122bdd1243dSDimitry Andric return IsLA32 ? "ilp32d" : "lp64d"; 123bdd1243dSDimitry Andric } 12406c3fb27SDimitry Andric } 125bdd1243dSDimitry Andric 126bdd1243dSDimitry Andric void loongarch::getLoongArchTargetFeatures(const Driver &D, 127bdd1243dSDimitry Andric const llvm::Triple &Triple, 128bdd1243dSDimitry Andric const ArgList &Args, 129bdd1243dSDimitry Andric std::vector<StringRef> &Features) { 130*0fca6ea1SDimitry Andric // Enable the `lsx` feature on 64-bit LoongArch by default. 131*0fca6ea1SDimitry Andric if (Triple.isLoongArch64() && 132*0fca6ea1SDimitry Andric (!Args.hasArgNoClaim(clang::driver::options::OPT_march_EQ))) 133*0fca6ea1SDimitry Andric Features.push_back("+lsx"); 134*0fca6ea1SDimitry Andric 1358a4dda33SDimitry Andric std::string ArchName; 1368a4dda33SDimitry Andric if (const Arg *A = Args.getLastArg(options::OPT_march_EQ)) 137bdd1243dSDimitry Andric ArchName = A->getValue(); 1388a4dda33SDimitry Andric ArchName = postProcessTargetCPUString(ArchName, Triple); 139bdd1243dSDimitry Andric llvm::LoongArch::getArchFeatures(ArchName, Features); 140bdd1243dSDimitry Andric 141bdd1243dSDimitry Andric // Select floating-point features determined by -mdouble-float, 142bdd1243dSDimitry Andric // -msingle-float, -msoft-float and -mfpu. 143bdd1243dSDimitry Andric // Note: -m*-float wins any other options. 144bdd1243dSDimitry Andric if (const Arg *A = Args.getLastArg(options::OPT_mdouble_float, 145bdd1243dSDimitry Andric options::OPT_msingle_float, 146bdd1243dSDimitry Andric options::OPT_msoft_float)) { 147bdd1243dSDimitry Andric if (A->getOption().matches(options::OPT_mdouble_float)) { 148bdd1243dSDimitry Andric Features.push_back("+f"); 149bdd1243dSDimitry Andric Features.push_back("+d"); 150bdd1243dSDimitry Andric } else if (A->getOption().matches(options::OPT_msingle_float)) { 151bdd1243dSDimitry Andric Features.push_back("+f"); 152bdd1243dSDimitry Andric Features.push_back("-d"); 153*0fca6ea1SDimitry Andric Features.push_back("-lsx"); 154bdd1243dSDimitry Andric } else /*Soft-float*/ { 155bdd1243dSDimitry Andric Features.push_back("-f"); 156bdd1243dSDimitry Andric Features.push_back("-d"); 157*0fca6ea1SDimitry Andric Features.push_back("-lsx"); 158bdd1243dSDimitry Andric } 159bdd1243dSDimitry Andric } else if (const Arg *A = Args.getLastArg(options::OPT_mfpu_EQ)) { 160bdd1243dSDimitry Andric StringRef FPU = A->getValue(); 161bdd1243dSDimitry Andric if (FPU == "64") { 162bdd1243dSDimitry Andric Features.push_back("+f"); 163bdd1243dSDimitry Andric Features.push_back("+d"); 164bdd1243dSDimitry Andric } else if (FPU == "32") { 165bdd1243dSDimitry Andric Features.push_back("+f"); 166bdd1243dSDimitry Andric Features.push_back("-d"); 167*0fca6ea1SDimitry Andric Features.push_back("-lsx"); 168bdd1243dSDimitry Andric } else if (FPU == "0" || FPU == "none") { 169bdd1243dSDimitry Andric Features.push_back("-f"); 170bdd1243dSDimitry Andric Features.push_back("-d"); 171*0fca6ea1SDimitry Andric Features.push_back("-lsx"); 172bdd1243dSDimitry Andric } else { 173bdd1243dSDimitry Andric D.Diag(diag::err_drv_loongarch_invalid_mfpu_EQ) << FPU; 174bdd1243dSDimitry Andric } 175bdd1243dSDimitry Andric } 17606c3fb27SDimitry Andric 177*0fca6ea1SDimitry Andric // Select the `ual` feature determined by -m[no-]strict-align. 178*0fca6ea1SDimitry Andric AddTargetFeature(Args, Features, options::OPT_mno_strict_align, 179*0fca6ea1SDimitry Andric options::OPT_mstrict_align, "ual"); 18006c3fb27SDimitry Andric 18106c3fb27SDimitry Andric // Accept but warn about these TargetSpecific options. 18206c3fb27SDimitry Andric if (Arg *A = Args.getLastArgNoClaim(options::OPT_mabi_EQ)) 18306c3fb27SDimitry Andric A->ignoreTargetSpecific(); 18406c3fb27SDimitry Andric if (Arg *A = Args.getLastArgNoClaim(options::OPT_mfpu_EQ)) 18506c3fb27SDimitry Andric A->ignoreTargetSpecific(); 186*0fca6ea1SDimitry Andric if (Arg *A = Args.getLastArgNoClaim(options::OPT_msimd_EQ)) 187*0fca6ea1SDimitry Andric A->ignoreTargetSpecific(); 188*0fca6ea1SDimitry Andric 189*0fca6ea1SDimitry Andric // Select lsx/lasx feature determined by -msimd=. 190*0fca6ea1SDimitry Andric // Option -msimd= precedes -m[no-]lsx and -m[no-]lasx. 191*0fca6ea1SDimitry Andric if (const Arg *A = Args.getLastArg(options::OPT_msimd_EQ)) { 192*0fca6ea1SDimitry Andric StringRef MSIMD = A->getValue(); 193*0fca6ea1SDimitry Andric if (MSIMD == "lsx") { 194*0fca6ea1SDimitry Andric // Option -msimd=lsx depends on 64-bit FPU. 195*0fca6ea1SDimitry Andric // -m*-float and -mfpu=none/0/32 conflict with -msimd=lsx. 196*0fca6ea1SDimitry Andric if (llvm::find(Features, "-d") != Features.end()) 197*0fca6ea1SDimitry Andric D.Diag(diag::err_drv_loongarch_wrong_fpu_width) << /*LSX*/ 0; 198*0fca6ea1SDimitry Andric else 199*0fca6ea1SDimitry Andric Features.push_back("+lsx"); 200*0fca6ea1SDimitry Andric } else if (MSIMD == "lasx") { 201*0fca6ea1SDimitry Andric // Option -msimd=lasx depends on 64-bit FPU and LSX. 202*0fca6ea1SDimitry Andric // -m*-float, -mfpu=none/0/32 and -mno-lsx conflict with -msimd=lasx. 203*0fca6ea1SDimitry Andric if (llvm::find(Features, "-d") != Features.end()) 204*0fca6ea1SDimitry Andric D.Diag(diag::err_drv_loongarch_wrong_fpu_width) << /*LASX*/ 1; 205*0fca6ea1SDimitry Andric else if (llvm::find(Features, "-lsx") != Features.end()) 206*0fca6ea1SDimitry Andric D.Diag(diag::err_drv_loongarch_invalid_simd_option_combination); 207*0fca6ea1SDimitry Andric 208*0fca6ea1SDimitry Andric // The command options do not contain -mno-lasx. 209*0fca6ea1SDimitry Andric if (!Args.getLastArg(options::OPT_mno_lasx)) { 210*0fca6ea1SDimitry Andric Features.push_back("+lsx"); 211*0fca6ea1SDimitry Andric Features.push_back("+lasx"); 212*0fca6ea1SDimitry Andric } 213*0fca6ea1SDimitry Andric } else if (MSIMD == "none") { 214*0fca6ea1SDimitry Andric if (llvm::find(Features, "+lsx") != Features.end()) 215*0fca6ea1SDimitry Andric Features.push_back("-lsx"); 216*0fca6ea1SDimitry Andric if (llvm::find(Features, "+lasx") != Features.end()) 217*0fca6ea1SDimitry Andric Features.push_back("-lasx"); 218*0fca6ea1SDimitry Andric } else { 219*0fca6ea1SDimitry Andric D.Diag(diag::err_drv_loongarch_invalid_msimd_EQ) << MSIMD; 220*0fca6ea1SDimitry Andric } 221*0fca6ea1SDimitry Andric } 2225f757f3fSDimitry Andric 2235f757f3fSDimitry Andric // Select lsx feature determined by -m[no-]lsx. 2245f757f3fSDimitry Andric if (const Arg *A = Args.getLastArg(options::OPT_mlsx, options::OPT_mno_lsx)) { 2255f757f3fSDimitry Andric // LSX depends on 64-bit FPU. 2265f757f3fSDimitry Andric // -m*-float and -mfpu=none/0/32 conflict with -mlsx. 2275f757f3fSDimitry Andric if (A->getOption().matches(options::OPT_mlsx)) { 2285f757f3fSDimitry Andric if (llvm::find(Features, "-d") != Features.end()) 229*0fca6ea1SDimitry Andric D.Diag(diag::err_drv_loongarch_wrong_fpu_width) << /*LSX*/ 0; 2305f757f3fSDimitry Andric else /*-mlsx*/ 2315f757f3fSDimitry Andric Features.push_back("+lsx"); 2325f757f3fSDimitry Andric } else /*-mno-lsx*/ { 2335f757f3fSDimitry Andric Features.push_back("-lsx"); 2345f757f3fSDimitry Andric } 2355f757f3fSDimitry Andric } 2365f757f3fSDimitry Andric 2375f757f3fSDimitry Andric // Select lasx feature determined by -m[no-]lasx. 2385f757f3fSDimitry Andric if (const Arg *A = 2395f757f3fSDimitry Andric Args.getLastArg(options::OPT_mlasx, options::OPT_mno_lasx)) { 2405f757f3fSDimitry Andric // LASX depends on 64-bit FPU and LSX. 2415f757f3fSDimitry Andric // -mno-lsx conflicts with -mlasx. 2425f757f3fSDimitry Andric if (A->getOption().matches(options::OPT_mlasx)) { 2435f757f3fSDimitry Andric if (llvm::find(Features, "-d") != Features.end()) 244*0fca6ea1SDimitry Andric D.Diag(diag::err_drv_loongarch_wrong_fpu_width) << /*LASX*/ 1; 2455f757f3fSDimitry Andric else { /*-mlasx*/ 2465f757f3fSDimitry Andric Features.push_back("+lsx"); 2475f757f3fSDimitry Andric Features.push_back("+lasx"); 2485f757f3fSDimitry Andric } 2495f757f3fSDimitry Andric } else /*-mno-lasx*/ 2505f757f3fSDimitry Andric Features.push_back("-lasx"); 2515f757f3fSDimitry Andric } 252bdd1243dSDimitry Andric } 2538a4dda33SDimitry Andric 2548a4dda33SDimitry Andric std::string loongarch::postProcessTargetCPUString(const std::string &CPU, 2558a4dda33SDimitry Andric const llvm::Triple &Triple) { 2568a4dda33SDimitry Andric std::string CPUString = CPU; 2578a4dda33SDimitry Andric if (CPUString == "native") { 2588a4dda33SDimitry Andric CPUString = llvm::sys::getHostCPUName(); 2598a4dda33SDimitry Andric if (CPUString == "generic") 2608a4dda33SDimitry Andric CPUString = llvm::LoongArch::getDefaultArch(Triple.isLoongArch64()); 2618a4dda33SDimitry Andric } 2628a4dda33SDimitry Andric if (CPUString.empty()) 2638a4dda33SDimitry Andric CPUString = llvm::LoongArch::getDefaultArch(Triple.isLoongArch64()); 2648a4dda33SDimitry Andric return CPUString; 2658a4dda33SDimitry Andric } 2668a4dda33SDimitry Andric 2678a4dda33SDimitry Andric std::string loongarch::getLoongArchTargetCPU(const llvm::opt::ArgList &Args, 2688a4dda33SDimitry Andric const llvm::Triple &Triple) { 2698a4dda33SDimitry Andric std::string CPU; 270*0fca6ea1SDimitry Andric std::string Arch; 2718a4dda33SDimitry Andric // If we have -march, use that. 272*0fca6ea1SDimitry Andric if (const Arg *A = Args.getLastArg(options::OPT_march_EQ)) { 273*0fca6ea1SDimitry Andric Arch = A->getValue(); 274*0fca6ea1SDimitry Andric if (Arch == "la64v1.0" || Arch == "la64v1.1") 275*0fca6ea1SDimitry Andric CPU = llvm::LoongArch::getDefaultArch(Triple.isLoongArch64()); 276*0fca6ea1SDimitry Andric else 277*0fca6ea1SDimitry Andric CPU = Arch; 278*0fca6ea1SDimitry Andric } 2798a4dda33SDimitry Andric return postProcessTargetCPUString(CPU, Triple); 2808a4dda33SDimitry Andric } 281