1bdd1243dSDimitry Andric //===-- RISCVTargetParser.cpp - Parser for target features ------*- C++ -*-===//
2bdd1243dSDimitry Andric //
3bdd1243dSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4bdd1243dSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5bdd1243dSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6bdd1243dSDimitry Andric //
7bdd1243dSDimitry Andric //===----------------------------------------------------------------------===//
8bdd1243dSDimitry Andric //
9bdd1243dSDimitry Andric // This file implements a target parser to recognise hardware features
1006c3fb27SDimitry Andric // for RISC-V CPUs.
11bdd1243dSDimitry Andric //
12bdd1243dSDimitry Andric //===----------------------------------------------------------------------===//
13bdd1243dSDimitry Andric
14bdd1243dSDimitry Andric #include "llvm/TargetParser/RISCVTargetParser.h"
15bdd1243dSDimitry Andric #include "llvm/ADT/SmallVector.h"
16bdd1243dSDimitry Andric #include "llvm/ADT/StringSwitch.h"
17*0fca6ea1SDimitry Andric #include "llvm/TargetParser/RISCVISAInfo.h"
181ac55f4cSDimitry Andric #include "llvm/TargetParser/Triple.h"
19bdd1243dSDimitry Andric
20bdd1243dSDimitry Andric namespace llvm {
21bdd1243dSDimitry Andric namespace RISCV {
22bdd1243dSDimitry Andric
2306c3fb27SDimitry Andric enum CPUKind : unsigned {
24*0fca6ea1SDimitry Andric #define PROC(ENUM, NAME, DEFAULT_MARCH, FAST_SCALAR_UNALIGN, \
25*0fca6ea1SDimitry Andric FAST_VECTOR_UNALIGN) \
26*0fca6ea1SDimitry Andric CK_##ENUM,
2706c3fb27SDimitry Andric #define TUNE_PROC(ENUM, NAME) CK_##ENUM,
2806c3fb27SDimitry Andric #include "llvm/TargetParser/RISCVTargetParserDef.inc"
2906c3fb27SDimitry Andric };
3006c3fb27SDimitry Andric
31bdd1243dSDimitry Andric struct CPUInfo {
32bdd1243dSDimitry Andric StringLiteral Name;
33bdd1243dSDimitry Andric StringLiteral DefaultMarch;
34*0fca6ea1SDimitry Andric bool FastScalarUnalignedAccess;
35*0fca6ea1SDimitry Andric bool FastVectorUnalignedAccess;
is64Bitllvm::RISCV::CPUInfo36bdd1243dSDimitry Andric bool is64Bit() const { return DefaultMarch.starts_with("rv64"); }
37bdd1243dSDimitry Andric };
38bdd1243dSDimitry Andric
39bdd1243dSDimitry Andric constexpr CPUInfo RISCVCPUInfo[] = {
40*0fca6ea1SDimitry Andric #define PROC(ENUM, NAME, DEFAULT_MARCH, FAST_SCALAR_UNALIGN, \
41*0fca6ea1SDimitry Andric FAST_VECTOR_UNALIGN) \
42*0fca6ea1SDimitry Andric {NAME, DEFAULT_MARCH, FAST_SCALAR_UNALIGN, FAST_VECTOR_UNALIGN},
43bdd1243dSDimitry Andric #include "llvm/TargetParser/RISCVTargetParserDef.inc"
44bdd1243dSDimitry Andric };
45bdd1243dSDimitry Andric
getCPUInfoByName(StringRef CPU)4606c3fb27SDimitry Andric static const CPUInfo *getCPUInfoByName(StringRef CPU) {
4706c3fb27SDimitry Andric for (auto &C : RISCVCPUInfo)
4806c3fb27SDimitry Andric if (C.Name == CPU)
4906c3fb27SDimitry Andric return &C;
5006c3fb27SDimitry Andric return nullptr;
5106c3fb27SDimitry Andric }
5206c3fb27SDimitry Andric
hasFastScalarUnalignedAccess(StringRef CPU)53*0fca6ea1SDimitry Andric bool hasFastScalarUnalignedAccess(StringRef CPU) {
545f757f3fSDimitry Andric const CPUInfo *Info = getCPUInfoByName(CPU);
55*0fca6ea1SDimitry Andric return Info && Info->FastScalarUnalignedAccess;
56*0fca6ea1SDimitry Andric }
57*0fca6ea1SDimitry Andric
hasFastVectorUnalignedAccess(StringRef CPU)58*0fca6ea1SDimitry Andric bool hasFastVectorUnalignedAccess(StringRef CPU) {
59*0fca6ea1SDimitry Andric const CPUInfo *Info = getCPUInfoByName(CPU);
60*0fca6ea1SDimitry Andric return Info && Info->FastVectorUnalignedAccess;
615f757f3fSDimitry Andric }
625f757f3fSDimitry Andric
parseCPU(StringRef CPU,bool IsRV64)6306c3fb27SDimitry Andric bool parseCPU(StringRef CPU, bool IsRV64) {
6406c3fb27SDimitry Andric const CPUInfo *Info = getCPUInfoByName(CPU);
6506c3fb27SDimitry Andric
6606c3fb27SDimitry Andric if (!Info)
67bdd1243dSDimitry Andric return false;
6806c3fb27SDimitry Andric return Info->is64Bit() == IsRV64;
69bdd1243dSDimitry Andric }
70bdd1243dSDimitry Andric
parseTuneCPU(StringRef TuneCPU,bool IsRV64)7106c3fb27SDimitry Andric bool parseTuneCPU(StringRef TuneCPU, bool IsRV64) {
7206c3fb27SDimitry Andric std::optional<CPUKind> Kind =
7306c3fb27SDimitry Andric llvm::StringSwitch<std::optional<CPUKind>>(TuneCPU)
74bdd1243dSDimitry Andric #define TUNE_PROC(ENUM, NAME) .Case(NAME, CK_##ENUM)
75bdd1243dSDimitry Andric #include "llvm/TargetParser/RISCVTargetParserDef.inc"
7606c3fb27SDimitry Andric .Default(std::nullopt);
7706c3fb27SDimitry Andric
7806c3fb27SDimitry Andric if (Kind.has_value())
7906c3fb27SDimitry Andric return true;
8006c3fb27SDimitry Andric
8106c3fb27SDimitry Andric // Fallback to parsing as a CPU.
8206c3fb27SDimitry Andric return parseCPU(TuneCPU, IsRV64);
83bdd1243dSDimitry Andric }
84bdd1243dSDimitry Andric
getMArchFromMcpu(StringRef CPU)85bdd1243dSDimitry Andric StringRef getMArchFromMcpu(StringRef CPU) {
8606c3fb27SDimitry Andric const CPUInfo *Info = getCPUInfoByName(CPU);
8706c3fb27SDimitry Andric if (!Info)
8806c3fb27SDimitry Andric return "";
8906c3fb27SDimitry Andric return Info->DefaultMarch;
90bdd1243dSDimitry Andric }
91bdd1243dSDimitry Andric
fillValidCPUArchList(SmallVectorImpl<StringRef> & Values,bool IsRV64)92bdd1243dSDimitry Andric void fillValidCPUArchList(SmallVectorImpl<StringRef> &Values, bool IsRV64) {
93bdd1243dSDimitry Andric for (const auto &C : RISCVCPUInfo) {
9406c3fb27SDimitry Andric if (IsRV64 == C.is64Bit())
95bdd1243dSDimitry Andric Values.emplace_back(C.Name);
96bdd1243dSDimitry Andric }
97bdd1243dSDimitry Andric }
98bdd1243dSDimitry Andric
fillValidTuneCPUArchList(SmallVectorImpl<StringRef> & Values,bool IsRV64)99bdd1243dSDimitry Andric void fillValidTuneCPUArchList(SmallVectorImpl<StringRef> &Values, bool IsRV64) {
100bdd1243dSDimitry Andric for (const auto &C : RISCVCPUInfo) {
10106c3fb27SDimitry Andric if (IsRV64 == C.is64Bit())
102bdd1243dSDimitry Andric Values.emplace_back(C.Name);
103bdd1243dSDimitry Andric }
104bdd1243dSDimitry Andric #define TUNE_PROC(ENUM, NAME) Values.emplace_back(StringRef(NAME));
105bdd1243dSDimitry Andric #include "llvm/TargetParser/RISCVTargetParserDef.inc"
106bdd1243dSDimitry Andric }
107bdd1243dSDimitry Andric
108*0fca6ea1SDimitry Andric // This function is currently used by IREE, so it's not dead code.
getFeaturesForCPU(StringRef CPU,SmallVectorImpl<std::string> & EnabledFeatures,bool NeedPlus)109*0fca6ea1SDimitry Andric void getFeaturesForCPU(StringRef CPU,
110*0fca6ea1SDimitry Andric SmallVectorImpl<std::string> &EnabledFeatures,
111*0fca6ea1SDimitry Andric bool NeedPlus) {
112*0fca6ea1SDimitry Andric StringRef MarchFromCPU = llvm::RISCV::getMArchFromMcpu(CPU);
113*0fca6ea1SDimitry Andric if (MarchFromCPU == "")
114*0fca6ea1SDimitry Andric return;
115*0fca6ea1SDimitry Andric
116*0fca6ea1SDimitry Andric EnabledFeatures.clear();
117*0fca6ea1SDimitry Andric auto RII = RISCVISAInfo::parseArchString(
118*0fca6ea1SDimitry Andric MarchFromCPU, /* EnableExperimentalExtension */ true);
119*0fca6ea1SDimitry Andric
120*0fca6ea1SDimitry Andric if (llvm::errorToBool(RII.takeError()))
121*0fca6ea1SDimitry Andric return;
122*0fca6ea1SDimitry Andric
123*0fca6ea1SDimitry Andric std::vector<std::string> FeatStrings =
124*0fca6ea1SDimitry Andric (*RII)->toFeatures(/* AddAllExtensions */ false);
125*0fca6ea1SDimitry Andric for (const auto &F : FeatStrings)
126*0fca6ea1SDimitry Andric if (NeedPlus)
127*0fca6ea1SDimitry Andric EnabledFeatures.push_back(F);
128*0fca6ea1SDimitry Andric else
129*0fca6ea1SDimitry Andric EnabledFeatures.push_back(F.substr(1));
130*0fca6ea1SDimitry Andric }
131*0fca6ea1SDimitry Andric
132*0fca6ea1SDimitry Andric namespace RISCVExtensionBitmaskTable {
133*0fca6ea1SDimitry Andric #define GET_RISCVExtensionBitmaskTable_IMPL
134*0fca6ea1SDimitry Andric #include "llvm/TargetParser/RISCVTargetParserDef.inc"
135*0fca6ea1SDimitry Andric
136*0fca6ea1SDimitry Andric } // namespace RISCVExtensionBitmaskTable
137*0fca6ea1SDimitry Andric
138*0fca6ea1SDimitry Andric namespace {
139*0fca6ea1SDimitry Andric struct LessExtName {
operator ()llvm::RISCV::__anonab35bf6e0111::LessExtName140*0fca6ea1SDimitry Andric bool operator()(const RISCVExtensionBitmaskTable::RISCVExtensionBitmask &LHS,
141*0fca6ea1SDimitry Andric StringRef RHS) {
142*0fca6ea1SDimitry Andric return StringRef(LHS.Name) < RHS;
143*0fca6ea1SDimitry Andric }
144*0fca6ea1SDimitry Andric };
145*0fca6ea1SDimitry Andric } // namespace
146*0fca6ea1SDimitry Andric
147bdd1243dSDimitry Andric } // namespace RISCV
148*0fca6ea1SDimitry Andric
149*0fca6ea1SDimitry Andric namespace RISCVVType {
150*0fca6ea1SDimitry Andric // Encode VTYPE into the binary format used by the the VSETVLI instruction which
151*0fca6ea1SDimitry Andric // is used by our MC layer representation.
152*0fca6ea1SDimitry Andric //
153*0fca6ea1SDimitry Andric // Bits | Name | Description
154*0fca6ea1SDimitry Andric // -----+------------+------------------------------------------------
155*0fca6ea1SDimitry Andric // 7 | vma | Vector mask agnostic
156*0fca6ea1SDimitry Andric // 6 | vta | Vector tail agnostic
157*0fca6ea1SDimitry Andric // 5:3 | vsew[2:0] | Standard element width (SEW) setting
158*0fca6ea1SDimitry Andric // 2:0 | vlmul[2:0] | Vector register group multiplier (LMUL) setting
encodeVTYPE(RISCVII::VLMUL VLMUL,unsigned SEW,bool TailAgnostic,bool MaskAgnostic)159*0fca6ea1SDimitry Andric unsigned encodeVTYPE(RISCVII::VLMUL VLMUL, unsigned SEW, bool TailAgnostic,
160*0fca6ea1SDimitry Andric bool MaskAgnostic) {
161*0fca6ea1SDimitry Andric assert(isValidSEW(SEW) && "Invalid SEW");
162*0fca6ea1SDimitry Andric unsigned VLMULBits = static_cast<unsigned>(VLMUL);
163*0fca6ea1SDimitry Andric unsigned VSEWBits = encodeSEW(SEW);
164*0fca6ea1SDimitry Andric unsigned VTypeI = (VSEWBits << 3) | (VLMULBits & 0x7);
165*0fca6ea1SDimitry Andric if (TailAgnostic)
166*0fca6ea1SDimitry Andric VTypeI |= 0x40;
167*0fca6ea1SDimitry Andric if (MaskAgnostic)
168*0fca6ea1SDimitry Andric VTypeI |= 0x80;
169*0fca6ea1SDimitry Andric
170*0fca6ea1SDimitry Andric return VTypeI;
171*0fca6ea1SDimitry Andric }
172*0fca6ea1SDimitry Andric
decodeVLMUL(RISCVII::VLMUL VLMUL)173*0fca6ea1SDimitry Andric std::pair<unsigned, bool> decodeVLMUL(RISCVII::VLMUL VLMUL) {
174*0fca6ea1SDimitry Andric switch (VLMUL) {
175*0fca6ea1SDimitry Andric default:
176*0fca6ea1SDimitry Andric llvm_unreachable("Unexpected LMUL value!");
177*0fca6ea1SDimitry Andric case RISCVII::VLMUL::LMUL_1:
178*0fca6ea1SDimitry Andric case RISCVII::VLMUL::LMUL_2:
179*0fca6ea1SDimitry Andric case RISCVII::VLMUL::LMUL_4:
180*0fca6ea1SDimitry Andric case RISCVII::VLMUL::LMUL_8:
181*0fca6ea1SDimitry Andric return std::make_pair(1 << static_cast<unsigned>(VLMUL), false);
182*0fca6ea1SDimitry Andric case RISCVII::VLMUL::LMUL_F2:
183*0fca6ea1SDimitry Andric case RISCVII::VLMUL::LMUL_F4:
184*0fca6ea1SDimitry Andric case RISCVII::VLMUL::LMUL_F8:
185*0fca6ea1SDimitry Andric return std::make_pair(1 << (8 - static_cast<unsigned>(VLMUL)), true);
186*0fca6ea1SDimitry Andric }
187*0fca6ea1SDimitry Andric }
188*0fca6ea1SDimitry Andric
printVType(unsigned VType,raw_ostream & OS)189*0fca6ea1SDimitry Andric void printVType(unsigned VType, raw_ostream &OS) {
190*0fca6ea1SDimitry Andric unsigned Sew = getSEW(VType);
191*0fca6ea1SDimitry Andric OS << "e" << Sew;
192*0fca6ea1SDimitry Andric
193*0fca6ea1SDimitry Andric unsigned LMul;
194*0fca6ea1SDimitry Andric bool Fractional;
195*0fca6ea1SDimitry Andric std::tie(LMul, Fractional) = decodeVLMUL(getVLMUL(VType));
196*0fca6ea1SDimitry Andric
197*0fca6ea1SDimitry Andric if (Fractional)
198*0fca6ea1SDimitry Andric OS << ", mf";
199*0fca6ea1SDimitry Andric else
200*0fca6ea1SDimitry Andric OS << ", m";
201*0fca6ea1SDimitry Andric OS << LMul;
202*0fca6ea1SDimitry Andric
203*0fca6ea1SDimitry Andric if (isTailAgnostic(VType))
204*0fca6ea1SDimitry Andric OS << ", ta";
205*0fca6ea1SDimitry Andric else
206*0fca6ea1SDimitry Andric OS << ", tu";
207*0fca6ea1SDimitry Andric
208*0fca6ea1SDimitry Andric if (isMaskAgnostic(VType))
209*0fca6ea1SDimitry Andric OS << ", ma";
210*0fca6ea1SDimitry Andric else
211*0fca6ea1SDimitry Andric OS << ", mu";
212*0fca6ea1SDimitry Andric }
213*0fca6ea1SDimitry Andric
getSEWLMULRatio(unsigned SEW,RISCVII::VLMUL VLMul)214*0fca6ea1SDimitry Andric unsigned getSEWLMULRatio(unsigned SEW, RISCVII::VLMUL VLMul) {
215*0fca6ea1SDimitry Andric unsigned LMul;
216*0fca6ea1SDimitry Andric bool Fractional;
217*0fca6ea1SDimitry Andric std::tie(LMul, Fractional) = decodeVLMUL(VLMul);
218*0fca6ea1SDimitry Andric
219*0fca6ea1SDimitry Andric // Convert LMul to a fixed point value with 3 fractional bits.
220*0fca6ea1SDimitry Andric LMul = Fractional ? (8 / LMul) : (LMul * 8);
221*0fca6ea1SDimitry Andric
222*0fca6ea1SDimitry Andric assert(SEW >= 8 && "Unexpected SEW value");
223*0fca6ea1SDimitry Andric return (SEW * 8) / LMul;
224*0fca6ea1SDimitry Andric }
225*0fca6ea1SDimitry Andric
226*0fca6ea1SDimitry Andric std::optional<RISCVII::VLMUL>
getSameRatioLMUL(unsigned SEW,RISCVII::VLMUL VLMUL,unsigned EEW)227*0fca6ea1SDimitry Andric getSameRatioLMUL(unsigned SEW, RISCVII::VLMUL VLMUL, unsigned EEW) {
228*0fca6ea1SDimitry Andric unsigned Ratio = RISCVVType::getSEWLMULRatio(SEW, VLMUL);
229*0fca6ea1SDimitry Andric unsigned EMULFixedPoint = (EEW * 8) / Ratio;
230*0fca6ea1SDimitry Andric bool Fractional = EMULFixedPoint < 8;
231*0fca6ea1SDimitry Andric unsigned EMUL = Fractional ? 8 / EMULFixedPoint : EMULFixedPoint / 8;
232*0fca6ea1SDimitry Andric if (!isValidLMUL(EMUL, Fractional))
233*0fca6ea1SDimitry Andric return std::nullopt;
234*0fca6ea1SDimitry Andric return RISCVVType::encodeLMUL(EMUL, Fractional);
235*0fca6ea1SDimitry Andric }
236*0fca6ea1SDimitry Andric
237*0fca6ea1SDimitry Andric } // namespace RISCVVType
238*0fca6ea1SDimitry Andric
239bdd1243dSDimitry Andric } // namespace llvm
240