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
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    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
126void loongarch::getLoongArchTargetFeatures(const Driver &D,
127                                           const llvm::Triple &Triple,
128                                           const ArgList &Args,
129                                           std::vector<StringRef> &Features) {
130  std::string ArchName;
131  if (const Arg *A = Args.getLastArg(options::OPT_march_EQ))
132    ArchName = A->getValue();
133  ArchName = postProcessTargetCPUString(ArchName, Triple);
134  llvm::LoongArch::getArchFeatures(ArchName, Features);
135
136  // Select floating-point features determined by -mdouble-float,
137  // -msingle-float, -msoft-float and -mfpu.
138  // Note: -m*-float wins any other options.
139  if (const Arg *A = Args.getLastArg(options::OPT_mdouble_float,
140                                     options::OPT_msingle_float,
141                                     options::OPT_msoft_float)) {
142    if (A->getOption().matches(options::OPT_mdouble_float)) {
143      Features.push_back("+f");
144      Features.push_back("+d");
145    } else if (A->getOption().matches(options::OPT_msingle_float)) {
146      Features.push_back("+f");
147      Features.push_back("-d");
148    } else /*Soft-float*/ {
149      Features.push_back("-f");
150      Features.push_back("-d");
151    }
152  } else if (const Arg *A = Args.getLastArg(options::OPT_mfpu_EQ)) {
153    StringRef FPU = A->getValue();
154    if (FPU == "64") {
155      Features.push_back("+f");
156      Features.push_back("+d");
157    } else if (FPU == "32") {
158      Features.push_back("+f");
159      Features.push_back("-d");
160    } else if (FPU == "0" || FPU == "none") {
161      Features.push_back("-f");
162      Features.push_back("-d");
163    } else {
164      D.Diag(diag::err_drv_loongarch_invalid_mfpu_EQ) << FPU;
165    }
166  }
167
168  // Select the `ual` feature determined by -m[no-]unaligned-access
169  // or the alias -m[no-]strict-align.
170  AddTargetFeature(Args, Features, options::OPT_munaligned_access,
171                   options::OPT_mno_unaligned_access, "ual");
172
173  // Accept but warn about these TargetSpecific options.
174  if (Arg *A = Args.getLastArgNoClaim(options::OPT_mabi_EQ))
175    A->ignoreTargetSpecific();
176  if (Arg *A = Args.getLastArgNoClaim(options::OPT_mfpu_EQ))
177    A->ignoreTargetSpecific();
178
179  // Select lsx feature determined by -m[no-]lsx.
180  if (const Arg *A = Args.getLastArg(options::OPT_mlsx, options::OPT_mno_lsx)) {
181    // LSX depends on 64-bit FPU.
182    // -m*-float and -mfpu=none/0/32 conflict with -mlsx.
183    if (A->getOption().matches(options::OPT_mlsx)) {
184      if (llvm::find(Features, "-d") != Features.end())
185        D.Diag(diag::err_drv_loongarch_wrong_fpu_width_for_lsx);
186      else /*-mlsx*/
187        Features.push_back("+lsx");
188    } else /*-mno-lsx*/ {
189      Features.push_back("-lsx");
190    }
191  }
192
193  // Select lasx feature determined by -m[no-]lasx.
194  if (const Arg *A =
195          Args.getLastArg(options::OPT_mlasx, options::OPT_mno_lasx)) {
196    // LASX depends on 64-bit FPU and LSX.
197    // -mno-lsx conflicts with -mlasx.
198    if (A->getOption().matches(options::OPT_mlasx)) {
199      if (llvm::find(Features, "-d") != Features.end())
200        D.Diag(diag::err_drv_loongarch_wrong_fpu_width_for_lasx);
201      else if (llvm::find(Features, "-lsx") != Features.end())
202        D.Diag(diag::err_drv_loongarch_invalid_simd_option_combination);
203      else { /*-mlasx*/
204        Features.push_back("+lsx");
205        Features.push_back("+lasx");
206      }
207    } else /*-mno-lasx*/
208      Features.push_back("-lasx");
209  }
210}
211
212std::string loongarch::postProcessTargetCPUString(const std::string &CPU,
213                                                  const llvm::Triple &Triple) {
214  std::string CPUString = CPU;
215  if (CPUString == "native") {
216    CPUString = llvm::sys::getHostCPUName();
217    if (CPUString == "generic")
218      CPUString = llvm::LoongArch::getDefaultArch(Triple.isLoongArch64());
219  }
220  if (CPUString.empty())
221    CPUString = llvm::LoongArch::getDefaultArch(Triple.isLoongArch64());
222  return CPUString;
223}
224
225std::string loongarch::getLoongArchTargetCPU(const llvm::opt::ArgList &Args,
226                                             const llvm::Triple &Triple) {
227  std::string CPU;
228  // If we have -march, use that.
229  if (const Arg *A = Args.getLastArg(options::OPT_march_EQ))
230    CPU = A->getValue();
231  return postProcessTargetCPUString(CPU, Triple);
232}
233