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; 36bdd1243dSDimitry 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 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 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 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 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 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 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 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 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. 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 { 140*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 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 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 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 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> 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