blob: 9ea4cc3f7cb95b0084c0b555dc75b9c99015a3bf [file] [log] [blame]
//===--- LoongArch.cpp - LoongArch Helpers for Tools ------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "LoongArch.h"
#include "ToolChains/CommonArgs.h"
#include "clang/Basic/DiagnosticDriver.h"
#include "clang/Driver/Driver.h"
#include "clang/Driver/DriverDiagnostic.h"
#include "clang/Driver/Options.h"
#include "llvm/TargetParser/Host.h"
#include "llvm/TargetParser/LoongArchTargetParser.h"
using namespace clang::driver;
using namespace clang::driver::tools;
using namespace clang;
using namespace llvm::opt;
StringRef loongarch::getLoongArchABI(const Driver &D, const ArgList &Args,
const llvm::Triple &Triple) {
assert((Triple.getArch() == llvm::Triple::loongarch32 ||
Triple.getArch() == llvm::Triple::loongarch64) &&
"Unexpected triple");
bool IsLA32 = Triple.getArch() == llvm::Triple::loongarch32;
// Record -mabi value for later use.
const Arg *MABIArg = Args.getLastArg(options::OPT_mabi_EQ);
StringRef MABIValue;
if (MABIArg) {
MABIValue = MABIArg->getValue();
}
// Parse -mfpu value for later use.
const Arg *MFPUArg = Args.getLastArg(options::OPT_mfpu_EQ);
int FPU = -1;
if (MFPUArg) {
StringRef V = MFPUArg->getValue();
if (V == "64")
FPU = 64;
else if (V == "32")
FPU = 32;
else if (V == "0" || V == "none")
FPU = 0;
else
D.Diag(diag::err_drv_loongarch_invalid_mfpu_EQ) << V;
}
// Check -m*-float firstly since they have highest priority.
if (const Arg *A = Args.getLastArg(options::OPT_mdouble_float,
options::OPT_msingle_float,
options::OPT_msoft_float)) {
StringRef ImpliedABI;
int ImpliedFPU = -1;
if (A->getOption().matches(options::OPT_mdouble_float)) {
ImpliedABI = IsLA32 ? "ilp32d" : "lp64d";
ImpliedFPU = 64;
}
if (A->getOption().matches(options::OPT_msingle_float)) {
ImpliedABI = IsLA32 ? "ilp32f" : "lp64f";
ImpliedFPU = 32;
}
if (A->getOption().matches(options::OPT_msoft_float)) {
ImpliedABI = IsLA32 ? "ilp32s" : "lp64s";
ImpliedFPU = 0;
}
// Check `-mabi=` and `-mfpu=` settings and report if they conflict with
// the higher-priority settings implied by -m*-float.
//
// ImpliedABI and ImpliedFPU are guaranteed to have valid values because
// one of the match arms must match if execution can arrive here at all.
if (!MABIValue.empty() && ImpliedABI != MABIValue)
D.Diag(diag::warn_drv_loongarch_conflicting_implied_val)
<< MABIArg->getAsString(Args) << A->getAsString(Args) << ImpliedABI;
if (FPU != -1 && ImpliedFPU != FPU)
D.Diag(diag::warn_drv_loongarch_conflicting_implied_val)
<< MFPUArg->getAsString(Args) << A->getAsString(Args) << ImpliedFPU;
return ImpliedABI;
}
// If `-mabi=` is specified, use it.
if (!MABIValue.empty())
return MABIValue;
// Select abi based on -mfpu=xx.
switch (FPU) {
case 64:
return IsLA32 ? "ilp32d" : "lp64d";
case 32:
return IsLA32 ? "ilp32f" : "lp64f";
case 0:
return IsLA32 ? "ilp32s" : "lp64s";
}
// Choose a default based on the triple.
// Honor the explicit ABI modifier suffix in triple's environment part if
// present, falling back to {ILP32,LP64}D otherwise.
switch (Triple.getEnvironment()) {
case llvm::Triple::GNUSF:
return IsLA32 ? "ilp32s" : "lp64s";
case llvm::Triple::GNUF32:
return IsLA32 ? "ilp32f" : "lp64f";
case llvm::Triple::GNUF64:
// This was originally permitted (and indeed the canonical way) to
// represent the {ILP32,LP64}D ABIs, but in Feb 2023 Loongson decided to
// drop the explicit suffix in favor of unmarked `-gnu` for the
// "general-purpose" ABIs, among other non-technical reasons.
//
// The spec change did not mention whether existing usages of "gnuf64"
// shall remain valid or not, so we are going to continue recognizing it
// for some time, until it is clear that everyone else has migrated away
// from it.
[[fallthrough]];
case llvm::Triple::GNU:
default:
return IsLA32 ? "ilp32d" : "lp64d";
}
}
void loongarch::getLoongArchTargetFeatures(const Driver &D,
const llvm::Triple &Triple,
const ArgList &Args,
std::vector<StringRef> &Features) {
std::string ArchName;
if (const Arg *A = Args.getLastArg(options::OPT_march_EQ))
ArchName = A->getValue();
ArchName = postProcessTargetCPUString(ArchName, Triple);
llvm::LoongArch::getArchFeatures(ArchName, Features);
// Select floating-point features determined by -mdouble-float,
// -msingle-float, -msoft-float and -mfpu.
// Note: -m*-float wins any other options.
if (const Arg *A = Args.getLastArg(options::OPT_mdouble_float,
options::OPT_msingle_float,
options::OPT_msoft_float)) {
if (A->getOption().matches(options::OPT_mdouble_float)) {
Features.push_back("+f");
Features.push_back("+d");
} else if (A->getOption().matches(options::OPT_msingle_float)) {
Features.push_back("+f");
Features.push_back("-d");
} else /*Soft-float*/ {
Features.push_back("-f");
Features.push_back("-d");
}
} else if (const Arg *A = Args.getLastArg(options::OPT_mfpu_EQ)) {
StringRef FPU = A->getValue();
if (FPU == "64") {
Features.push_back("+f");
Features.push_back("+d");
} else if (FPU == "32") {
Features.push_back("+f");
Features.push_back("-d");
} else if (FPU == "0" || FPU == "none") {
Features.push_back("-f");
Features.push_back("-d");
} else {
D.Diag(diag::err_drv_loongarch_invalid_mfpu_EQ) << FPU;
}
}
// Select the `ual` feature determined by -m[no-]strict-align.
AddTargetFeature(Args, Features, options::OPT_mno_strict_align,
options::OPT_mstrict_align, "ual");
// Accept but warn about these TargetSpecific options.
if (Arg *A = Args.getLastArgNoClaim(options::OPT_mabi_EQ))
A->ignoreTargetSpecific();
if (Arg *A = Args.getLastArgNoClaim(options::OPT_mfpu_EQ))
A->ignoreTargetSpecific();
// Select lsx feature determined by -m[no-]lsx.
if (const Arg *A = Args.getLastArg(options::OPT_mlsx, options::OPT_mno_lsx)) {
// LSX depends on 64-bit FPU.
// -m*-float and -mfpu=none/0/32 conflict with -mlsx.
if (A->getOption().matches(options::OPT_mlsx)) {
if (llvm::find(Features, "-d") != Features.end())
D.Diag(diag::err_drv_loongarch_wrong_fpu_width) << /*LSX*/ 0;
else /*-mlsx*/
Features.push_back("+lsx");
} else /*-mno-lsx*/ {
Features.push_back("-lsx");
}
}
// Select lasx feature determined by -m[no-]lasx.
if (const Arg *A =
Args.getLastArg(options::OPT_mlasx, options::OPT_mno_lasx)) {
// LASX depends on 64-bit FPU and LSX.
// -mno-lsx conflicts with -mlasx.
if (A->getOption().matches(options::OPT_mlasx)) {
if (llvm::find(Features, "-d") != Features.end())
D.Diag(diag::err_drv_loongarch_wrong_fpu_width) << /*LASX*/ 1;
else if (llvm::find(Features, "-lsx") != Features.end())
D.Diag(diag::err_drv_loongarch_invalid_simd_option_combination);
else { /*-mlasx*/
Features.push_back("+lsx");
Features.push_back("+lasx");
}
} else /*-mno-lasx*/
Features.push_back("-lasx");
}
}
std::string loongarch::postProcessTargetCPUString(const std::string &CPU,
const llvm::Triple &Triple) {
std::string CPUString = CPU;
if (CPUString == "native") {
CPUString = llvm::sys::getHostCPUName();
if (CPUString == "generic")
CPUString = llvm::LoongArch::getDefaultArch(Triple.isLoongArch64());
}
if (CPUString.empty())
CPUString = llvm::LoongArch::getDefaultArch(Triple.isLoongArch64());
return CPUString;
}
std::string loongarch::getLoongArchTargetCPU(const llvm::opt::ArgList &Args,
const llvm::Triple &Triple) {
std::string CPU;
// If we have -march, use that.
if (const Arg *A = Args.getLastArg(options::OPT_march_EQ))
CPU = A->getValue();
return postProcessTargetCPUString(CPU, Triple);
}