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