xref: /freebsd/contrib/llvm-project/llvm/lib/TargetParser/RISCVTargetParser.cpp (revision 770cf0a5f02dc8983a89c6568d741fbc25baa999)
1 //===-- RISCVTargetParser.cpp - Parser for target features ------*- 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 // This file implements a target parser to recognise hardware features
10 // for RISC-V CPUs.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "llvm/TargetParser/RISCVTargetParser.h"
15 #include "llvm/ADT/SmallVector.h"
16 #include "llvm/ADT/StringSwitch.h"
17 #include "llvm/TargetParser/RISCVISAInfo.h"
18 
19 namespace llvm {
20 namespace RISCV {
21 
22 enum CPUKind : unsigned {
23 #define PROC(ENUM, NAME, DEFAULT_MARCH, FAST_SCALAR_UNALIGN,                   \
24              FAST_VECTOR_UNALIGN, MVENDORID, MARCHID, MIMPID)                  \
25   CK_##ENUM,
26 #define TUNE_PROC(ENUM, NAME) CK_##ENUM,
27 #include "llvm/TargetParser/RISCVTargetParserDef.inc"
28 };
29 
30 constexpr CPUInfo RISCVCPUInfo[] = {
31 #define PROC(ENUM, NAME, DEFAULT_MARCH, FAST_SCALAR_UNALIGN,                   \
32              FAST_VECTOR_UNALIGN, MVENDORID, MARCHID, MIMPID)                  \
33   {                                                                            \
34       NAME,                                                                    \
35       DEFAULT_MARCH,                                                           \
36       FAST_SCALAR_UNALIGN,                                                     \
37       FAST_VECTOR_UNALIGN,                                                     \
38       {MVENDORID, MARCHID, MIMPID},                                            \
39   },
40 #include "llvm/TargetParser/RISCVTargetParserDef.inc"
41 };
42 
43 static const CPUInfo *getCPUInfoByName(StringRef CPU) {
44   for (auto &C : RISCVCPUInfo)
45     if (C.Name == CPU)
46       return &C;
47   return nullptr;
48 }
49 
50 bool hasFastScalarUnalignedAccess(StringRef CPU) {
51   const CPUInfo *Info = getCPUInfoByName(CPU);
52   return Info && Info->FastScalarUnalignedAccess;
53 }
54 
55 bool hasFastVectorUnalignedAccess(StringRef CPU) {
56   const CPUInfo *Info = getCPUInfoByName(CPU);
57   return Info && Info->FastVectorUnalignedAccess;
58 }
59 
60 bool hasValidCPUModel(StringRef CPU) { return getCPUModel(CPU).isValid(); }
61 
62 CPUModel getCPUModel(StringRef CPU) {
63   const CPUInfo *Info = getCPUInfoByName(CPU);
64   if (!Info)
65     return {0, 0, 0};
66   return Info->Model;
67 }
68 
69 StringRef getCPUNameFromCPUModel(const CPUModel &Model) {
70   if (!Model.isValid())
71     return "";
72 
73   for (auto &C : RISCVCPUInfo)
74     if (C.Model == Model)
75       return C.Name;
76   return "";
77 }
78 
79 bool parseCPU(StringRef CPU, bool IsRV64) {
80   const CPUInfo *Info = getCPUInfoByName(CPU);
81 
82   if (!Info)
83     return false;
84   return Info->is64Bit() == IsRV64;
85 }
86 
87 bool parseTuneCPU(StringRef TuneCPU, bool IsRV64) {
88   std::optional<CPUKind> Kind =
89       llvm::StringSwitch<std::optional<CPUKind>>(TuneCPU)
90 #define TUNE_PROC(ENUM, NAME) .Case(NAME, CK_##ENUM)
91   #include "llvm/TargetParser/RISCVTargetParserDef.inc"
92       .Default(std::nullopt);
93 
94   if (Kind.has_value())
95     return true;
96 
97   // Fallback to parsing as a CPU.
98   return parseCPU(TuneCPU, IsRV64);
99 }
100 
101 StringRef getMArchFromMcpu(StringRef CPU) {
102   const CPUInfo *Info = getCPUInfoByName(CPU);
103   if (!Info)
104     return "";
105   return Info->DefaultMarch;
106 }
107 
108 void fillValidCPUArchList(SmallVectorImpl<StringRef> &Values, bool IsRV64) {
109   for (const auto &C : RISCVCPUInfo) {
110     if (IsRV64 == C.is64Bit())
111       Values.emplace_back(C.Name);
112   }
113 }
114 
115 void fillValidTuneCPUArchList(SmallVectorImpl<StringRef> &Values, bool IsRV64) {
116   for (const auto &C : RISCVCPUInfo) {
117     if (IsRV64 == C.is64Bit())
118       Values.emplace_back(C.Name);
119   }
120 #define TUNE_PROC(ENUM, NAME) Values.emplace_back(StringRef(NAME));
121 #include "llvm/TargetParser/RISCVTargetParserDef.inc"
122 }
123 
124 // This function is currently used by IREE, so it's not dead code.
125 void getFeaturesForCPU(StringRef CPU,
126                        SmallVectorImpl<std::string> &EnabledFeatures,
127                        bool NeedPlus) {
128   StringRef MarchFromCPU = llvm::RISCV::getMArchFromMcpu(CPU);
129   if (MarchFromCPU == "")
130     return;
131 
132   EnabledFeatures.clear();
133   auto RII = RISCVISAInfo::parseArchString(
134       MarchFromCPU, /* EnableExperimentalExtension */ true);
135 
136   if (llvm::errorToBool(RII.takeError()))
137     return;
138 
139   std::vector<std::string> FeatStrings =
140       (*RII)->toFeatures(/* AddAllExtensions */ false);
141   for (const auto &F : FeatStrings)
142     if (NeedPlus)
143       EnabledFeatures.push_back(F);
144     else
145       EnabledFeatures.push_back(F.substr(1));
146 }
147 
148 } // namespace RISCV
149 
150 namespace RISCVVType {
151 // Encode VTYPE into the binary format used by the the VSETVLI instruction which
152 // is used by our MC layer representation.
153 //
154 // Bits | Name       | Description
155 // -----+------------+------------------------------------------------
156 // 7    | vma        | Vector mask agnostic
157 // 6    | vta        | Vector tail agnostic
158 // 5:3  | vsew[2:0]  | Standard element width (SEW) setting
159 // 2:0  | vlmul[2:0] | Vector register group multiplier (LMUL) setting
160 unsigned encodeVTYPE(VLMUL VLMul, unsigned SEW, bool TailAgnostic,
161                      bool MaskAgnostic) {
162   assert(isValidSEW(SEW) && "Invalid SEW");
163   unsigned VLMulBits = static_cast<unsigned>(VLMul);
164   unsigned VSEWBits = encodeSEW(SEW);
165   unsigned VTypeI = (VSEWBits << 3) | (VLMulBits & 0x7);
166   if (TailAgnostic)
167     VTypeI |= 0x40;
168   if (MaskAgnostic)
169     VTypeI |= 0x80;
170 
171   return VTypeI;
172 }
173 
174 unsigned encodeXSfmmVType(unsigned SEW, unsigned Widen, bool AltFmt) {
175   assert(isValidSEW(SEW) && "Invalid SEW");
176   assert((Widen == 1 || Widen == 2 || Widen == 4) && "Invalid Widen");
177   unsigned VSEWBits = encodeSEW(SEW);
178   unsigned TWiden = Log2_32(Widen) + 1;
179   unsigned VTypeI = (VSEWBits << 3) | AltFmt << 8 | TWiden << 9;
180   return VTypeI;
181 }
182 
183 std::pair<unsigned, bool> decodeVLMUL(VLMUL VLMul) {
184   switch (VLMul) {
185   default:
186     llvm_unreachable("Unexpected LMUL value!");
187   case LMUL_1:
188   case LMUL_2:
189   case LMUL_4:
190   case LMUL_8:
191     return std::make_pair(1 << static_cast<unsigned>(VLMul), false);
192   case LMUL_F2:
193   case LMUL_F4:
194   case LMUL_F8:
195     return std::make_pair(1 << (8 - static_cast<unsigned>(VLMul)), true);
196   }
197 }
198 
199 void printVType(unsigned VType, raw_ostream &OS) {
200   unsigned Sew = getSEW(VType);
201   OS << "e" << Sew;
202 
203   unsigned LMul;
204   bool Fractional;
205   std::tie(LMul, Fractional) = decodeVLMUL(getVLMUL(VType));
206 
207   if (Fractional)
208     OS << ", mf";
209   else
210     OS << ", m";
211   OS << LMul;
212 
213   if (isTailAgnostic(VType))
214     OS << ", ta";
215   else
216     OS << ", tu";
217 
218   if (isMaskAgnostic(VType))
219     OS << ", ma";
220   else
221     OS << ", mu";
222 }
223 
224 unsigned getSEWLMULRatio(unsigned SEW, VLMUL VLMul) {
225   unsigned LMul;
226   bool Fractional;
227   std::tie(LMul, Fractional) = decodeVLMUL(VLMul);
228 
229   // Convert LMul to a fixed point value with 3 fractional bits.
230   LMul = Fractional ? (8 / LMul) : (LMul * 8);
231 
232   assert(SEW >= 8 && "Unexpected SEW value");
233   return (SEW * 8) / LMul;
234 }
235 
236 std::optional<VLMUL> getSameRatioLMUL(unsigned SEW, VLMUL VLMul, unsigned EEW) {
237   unsigned Ratio = RISCVVType::getSEWLMULRatio(SEW, VLMul);
238   unsigned EMULFixedPoint = (EEW * 8) / Ratio;
239   bool Fractional = EMULFixedPoint < 8;
240   unsigned EMUL = Fractional ? 8 / EMULFixedPoint : EMULFixedPoint / 8;
241   if (!isValidLMUL(EMUL, Fractional))
242     return std::nullopt;
243   return RISCVVType::encodeLMUL(EMUL, Fractional);
244 }
245 
246 } // namespace RISCVVType
247 
248 } // namespace llvm
249