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/LoongArchTargetParser.h"
16 
17 using namespace clang::driver;
18 using namespace clang::driver::tools;
19 using namespace clang;
20 using namespace llvm::opt;
21 
22 StringRef loongarch::getLoongArchABI(const Driver &D, const ArgList &Args,
23                                      const llvm::Triple &Triple) {
24   assert((Triple.getArch() == llvm::Triple::loongarch32 ||
25           Triple.getArch() == llvm::Triple::loongarch64) &&
26          "Unexpected triple");
27   bool IsLA32 = Triple.getArch() == llvm::Triple::loongarch32;
28 
29   // Record -mabi value for later use.
30   const Arg *MABIArg = Args.getLastArg(options::OPT_mabi_EQ);
31   StringRef MABIValue;
32   if (MABIArg) {
33     MABIValue = MABIArg->getValue();
34   }
35 
36   // Parse -mfpu value for later use.
37   const Arg *MFPUArg = Args.getLastArg(options::OPT_mfpu_EQ);
38   int FPU = -1;
39   if (MFPUArg) {
40     StringRef V = MFPUArg->getValue();
41     if (V == "64")
42       FPU = 64;
43     else if (V == "32")
44       FPU = 32;
45     else if (V == "0" || V == "none")
46       FPU = 0;
47     else
48       D.Diag(diag::err_drv_loongarch_invalid_mfpu_EQ) << V;
49   }
50 
51   // Check -m*-float firstly since they have highest priority.
52   if (const Arg *A = Args.getLastArg(options::OPT_mdouble_float,
53                                      options::OPT_msingle_float,
54                                      options::OPT_msoft_float)) {
55     StringRef ImpliedABI;
56     int ImpliedFPU = -1;
57     if (A->getOption().matches(options::OPT_mdouble_float)) {
58       ImpliedABI = IsLA32 ? "ilp32d" : "lp64d";
59       ImpliedFPU = 64;
60     }
61     if (A->getOption().matches(options::OPT_msingle_float)) {
62       ImpliedABI = IsLA32 ? "ilp32f" : "lp64f";
63       ImpliedFPU = 32;
64     }
65     if (A->getOption().matches(options::OPT_msoft_float)) {
66       ImpliedABI = IsLA32 ? "ilp32s" : "lp64s";
67       ImpliedFPU = 0;
68     }
69 
70     // Check `-mabi=` and `-mfpu=` settings and report if they conflict with
71     // the higher-priority settings implied by -m*-float.
72     //
73     // ImpliedABI and ImpliedFPU are guaranteed to have valid values because
74     // one of the match arms must match if execution can arrive here at all.
75     if (!MABIValue.empty() && ImpliedABI != MABIValue)
76       D.Diag(diag::warn_drv_loongarch_conflicting_implied_val)
77           << MABIArg->getAsString(Args) << A->getAsString(Args) << ImpliedABI;
78 
79     if (FPU != -1 && ImpliedFPU != FPU)
80       D.Diag(diag::warn_drv_loongarch_conflicting_implied_val)
81           << MFPUArg->getAsString(Args) << A->getAsString(Args) << ImpliedFPU;
82 
83     return ImpliedABI;
84   }
85 
86   // If `-mabi=` is specified, use it.
87   if (!MABIValue.empty())
88     return MABIValue;
89 
90   // Select abi based on -mfpu=xx.
91   switch (FPU) {
92   case 64:
93     return IsLA32 ? "ilp32d" : "lp64d";
94   case 32:
95     return IsLA32 ? "ilp32f" : "lp64f";
96   case 0:
97     return IsLA32 ? "ilp32s" : "lp64s";
98   }
99 
100   // Choose a default based on the triple.
101   // Honor the explicit ABI modifier suffix in triple's environment part if
102   // present, falling back to {ILP32,LP64}D otherwise.
103   switch (Triple.getEnvironment()) {
104   case llvm::Triple::GNUSF:
105     return IsLA32 ? "ilp32s" : "lp64s";
106   case llvm::Triple::GNUF32:
107     return IsLA32 ? "ilp32f" : "lp64f";
108   case llvm::Triple::GNUF64:
109     // This was originally permitted (and indeed the canonical way) to
110     // represent the {ILP32,LP64}D ABIs, but in Feb 2023 Loongson decided to
111     // drop the explicit suffix in favor of unmarked `-gnu` for the
112     // "general-purpose" ABIs, among other non-technical reasons.
113     //
114     // The spec change did not mention whether existing usages of "gnuf64"
115     // shall remain valid or not, so we are going to continue recognizing it
116     // for some time, until it is clear that everyone else has migrated away
117     // from it.
118     [[fallthrough]];
119   case llvm::Triple::GNU:
120   default:
121     return IsLA32 ? "ilp32d" : "lp64d";
122   }
123 }
124 
125 void loongarch::getLoongArchTargetFeatures(const Driver &D,
126                                            const llvm::Triple &Triple,
127                                            const ArgList &Args,
128                                            std::vector<StringRef> &Features) {
129   StringRef ArchName;
130   if (const Arg *A = Args.getLastArg(options::OPT_march_EQ)) {
131     if (!llvm::LoongArch::isValidArchName(A->getValue())) {
132       D.Diag(clang::diag::err_drv_invalid_arch_name) << A->getAsString(Args);
133       return;
134     }
135     ArchName = A->getValue();
136   }
137 
138   // TODO: handle -march=native and -mtune=xx.
139 
140   // Select a default arch name.
141   if (ArchName.empty() && Triple.isLoongArch64())
142     ArchName = "loongarch64";
143 
144   if (!ArchName.empty())
145     llvm::LoongArch::getArchFeatures(ArchName, Features);
146 
147   // Select floating-point features determined by -mdouble-float,
148   // -msingle-float, -msoft-float and -mfpu.
149   // Note: -m*-float wins any other options.
150   if (const Arg *A = Args.getLastArg(options::OPT_mdouble_float,
151                                      options::OPT_msingle_float,
152                                      options::OPT_msoft_float)) {
153     if (A->getOption().matches(options::OPT_mdouble_float)) {
154       Features.push_back("+f");
155       Features.push_back("+d");
156     } else if (A->getOption().matches(options::OPT_msingle_float)) {
157       Features.push_back("+f");
158       Features.push_back("-d");
159     } else /*Soft-float*/ {
160       Features.push_back("-f");
161       Features.push_back("-d");
162     }
163   } else if (const Arg *A = Args.getLastArg(options::OPT_mfpu_EQ)) {
164     StringRef FPU = A->getValue();
165     if (FPU == "64") {
166       Features.push_back("+f");
167       Features.push_back("+d");
168     } else if (FPU == "32") {
169       Features.push_back("+f");
170       Features.push_back("-d");
171     } else if (FPU == "0" || FPU == "none") {
172       Features.push_back("-f");
173       Features.push_back("-d");
174     } else {
175       D.Diag(diag::err_drv_loongarch_invalid_mfpu_EQ) << FPU;
176     }
177   }
178 
179   // Select the `ual` feature determined by -m[no-]unaligned-access
180   // or the alias -m[no-]strict-align.
181   AddTargetFeature(Args, Features, options::OPT_munaligned_access,
182                    options::OPT_mno_unaligned_access, "ual");
183 
184   // Accept but warn about these TargetSpecific options.
185   if (Arg *A = Args.getLastArgNoClaim(options::OPT_mabi_EQ))
186     A->ignoreTargetSpecific();
187   if (Arg *A = Args.getLastArgNoClaim(options::OPT_mfpu_EQ))
188     A->ignoreTargetSpecific();
189 }
190