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