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