1//===--- CSKY.cpp - CSKY 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 "CSKY.h"
10#include "ToolChains/CommonArgs.h"
11#include "clang/Basic/CharInfo.h"
12#include "clang/Driver/Driver.h"
13#include "clang/Driver/DriverDiagnostic.h"
14#include "clang/Driver/Options.h"
15#include "llvm/ADT/StringSwitch.h"
16#include "llvm/Option/ArgList.h"
17#include "llvm/Support/raw_ostream.h"
18#include "llvm/TargetParser/CSKYTargetParser.h"
19#include "llvm/TargetParser/Host.h"
20#include "llvm/TargetParser/TargetParser.h"
21
22using namespace clang::driver;
23using namespace clang::driver::tools;
24using namespace clang;
25using namespace llvm::opt;
26
27std::optional<llvm::StringRef>
28csky::getCSKYArchName(const Driver &D, const ArgList &Args,
29                      const llvm::Triple &Triple) {
30  if (const Arg *A = Args.getLastArg(options::OPT_march_EQ)) {
31    llvm::CSKY::ArchKind ArchKind = llvm::CSKY::parseArch(A->getValue());
32
33    if (ArchKind == llvm::CSKY::ArchKind::INVALID) {
34      D.Diag(clang::diag::err_drv_invalid_arch_name) << A->getAsString(Args);
35      return std::nullopt;
36    }
37    return std::optional<llvm::StringRef>(A->getValue());
38  }
39
40  if (const Arg *A = Args.getLastArg(clang::driver::options::OPT_mcpu_EQ)) {
41    llvm::CSKY::ArchKind ArchKind = llvm::CSKY::parseCPUArch(A->getValue());
42    if (ArchKind == llvm::CSKY::ArchKind::INVALID) {
43      D.Diag(clang::diag::err_drv_clang_unsupported) << A->getAsString(Args);
44      return std::nullopt;
45    }
46    return std::optional<llvm::StringRef>(llvm::CSKY::getArchName(ArchKind));
47  }
48
49  return std::optional<llvm::StringRef>("ck810");
50}
51
52csky::FloatABI csky::getCSKYFloatABI(const Driver &D, const ArgList &Args) {
53  csky::FloatABI ABI = FloatABI::Soft;
54  if (Arg *A =
55          Args.getLastArg(options::OPT_msoft_float, options::OPT_mhard_float,
56                          options::OPT_mfloat_abi_EQ)) {
57    if (A->getOption().matches(options::OPT_msoft_float)) {
58      ABI = FloatABI::Soft;
59    } else if (A->getOption().matches(options::OPT_mhard_float)) {
60      ABI = FloatABI::Hard;
61    } else {
62      ABI = llvm::StringSwitch<csky::FloatABI>(A->getValue())
63                .Case("soft", FloatABI::Soft)
64                .Case("softfp", FloatABI::SoftFP)
65                .Case("hard", FloatABI::Hard)
66                .Default(FloatABI::Invalid);
67      if (ABI == FloatABI::Invalid) {
68        D.Diag(diag::err_drv_invalid_mfloat_abi) << A->getAsString(Args);
69        ABI = FloatABI::Soft;
70      }
71    }
72  }
73
74  return ABI;
75}
76
77// Handle -mfpu=.
78static llvm::CSKY::CSKYFPUKind
79getCSKYFPUFeatures(const Driver &D, const Arg *A, const ArgList &Args,
80                   StringRef FPU, std::vector<StringRef> &Features) {
81
82  llvm::CSKY::CSKYFPUKind FPUID =
83      llvm::StringSwitch<llvm::CSKY::CSKYFPUKind>(FPU)
84          .Case("auto", llvm::CSKY::FK_AUTO)
85          .Case("fpv2", llvm::CSKY::FK_FPV2)
86          .Case("fpv2_divd", llvm::CSKY::FK_FPV2_DIVD)
87          .Case("fpv2_sf", llvm::CSKY::FK_FPV2_SF)
88          .Case("fpv3", llvm::CSKY::FK_FPV3)
89          .Case("fpv3_hf", llvm::CSKY::FK_FPV3_HF)
90          .Case("fpv3_hsf", llvm::CSKY::FK_FPV3_HSF)
91          .Case("fpv3_sdf", llvm::CSKY::FK_FPV3_SDF)
92          .Default(llvm::CSKY::FK_INVALID);
93  if (FPUID == llvm::CSKY::FK_INVALID) {
94    D.Diag(clang::diag::err_drv_clang_unsupported) << A->getAsString(Args);
95    return llvm::CSKY::FK_INVALID;
96  }
97
98  auto RemoveTargetFPUFeature =
99      [&Features](ArrayRef<const char *> FPUFeatures) {
100        for (auto FPUFeature : FPUFeatures) {
101          auto it = llvm::find(Features, FPUFeature);
102          if (it != Features.end())
103            Features.erase(it);
104        }
105      };
106
107  RemoveTargetFPUFeature({"+fpuv2_sf", "+fpuv2_df", "+fdivdu", "+fpuv3_hi",
108                          "+fpuv3_hf", "+fpuv3_sf", "+fpuv3_df"});
109
110  if (!llvm::CSKY::getFPUFeatures(FPUID, Features)) {
111    D.Diag(clang::diag::err_drv_clang_unsupported) << A->getAsString(Args);
112    return llvm::CSKY::FK_INVALID;
113  }
114
115  return FPUID;
116}
117
118void csky::getCSKYTargetFeatures(const Driver &D, const llvm::Triple &Triple,
119                                 const ArgList &Args, ArgStringList &CmdArgs,
120                                 std::vector<llvm::StringRef> &Features) {
121  llvm::StringRef archName;
122  llvm::StringRef cpuName;
123  llvm::CSKY::ArchKind ArchKind = llvm::CSKY::ArchKind::INVALID;
124  if (const Arg *A = Args.getLastArg(options::OPT_march_EQ)) {
125    ArchKind = llvm::CSKY::parseArch(A->getValue());
126    if (ArchKind == llvm::CSKY::ArchKind::INVALID) {
127      D.Diag(clang::diag::err_drv_invalid_arch_name) << A->getAsString(Args);
128      return;
129    }
130    archName = A->getValue();
131  }
132
133  if (const Arg *A = Args.getLastArg(clang::driver::options::OPT_mcpu_EQ)) {
134    llvm::CSKY::ArchKind Kind = llvm::CSKY::parseCPUArch(A->getValue());
135    if (Kind == llvm::CSKY::ArchKind::INVALID) {
136      D.Diag(clang::diag::err_drv_clang_unsupported) << A->getAsString(Args);
137      return;
138    }
139    if (!archName.empty() && Kind != ArchKind) {
140      D.Diag(clang::diag::err_drv_clang_unsupported) << A->getAsString(Args);
141      return;
142    }
143    cpuName = A->getValue();
144    if (archName.empty())
145      archName = llvm::CSKY::getArchName(Kind);
146  }
147
148  if (archName.empty() && cpuName.empty()) {
149    archName = "ck810";
150    cpuName = "ck810";
151  } else if (!archName.empty() && cpuName.empty()) {
152    cpuName = archName;
153  }
154
155  csky::FloatABI FloatABI = csky::getCSKYFloatABI(D, Args);
156
157  if (FloatABI == csky::FloatABI::Hard) {
158    Features.push_back("+hard-float-abi");
159    Features.push_back("+hard-float");
160  } else if (FloatABI == csky::FloatABI::SoftFP) {
161    Features.push_back("+hard-float");
162  }
163
164  uint64_t Extension = llvm::CSKY::getDefaultExtensions(cpuName);
165  llvm::CSKY::getExtensionFeatures(Extension, Features);
166
167  if (const Arg *FPUArg = Args.getLastArg(options::OPT_mfpu_EQ))
168    getCSKYFPUFeatures(D, FPUArg, Args, FPUArg->getValue(), Features);
169}
170