This updates the IME-specific vtype field layout to match the [latest IME spec, version riscv-isa-release-fa55752-2026-05-04](https://github.com/riscv/integrated-matrix-extension/blob/main/src/integrated-matrix.adoc#new-fields-in-the-vector-type-vtype-register). The latest spec places the IME fields as: | Field | New Position | Change Description | |---------------|---------------------------|---------------------------------| | `lambda[2:0]` | `vtype[XLEN-2:XLEN-4]` | Unchanged | | `bs` | `vtype[XLEN-5]` | Moved from `vtype[XLEN-7]` | | `altfmt_A` | `vtype[XLEN-6]` | Moved from `vtype[XLEN-5]` | | `altfmt_B` | `vtype[XLEN-7]` | Moved from `vtype[XLEN-6]` | Previously `bs`, `altfmt_A`, and `altfmt_B` followed the older ordering, which was first introduced in #193956. The new position of altfmt supports growing in the future for more datatypes. This patch also updates tests to reflect field changes. Currently this should be NFC since we didn't have any CodeGen implementation that involves vtype in IME.
553 lines
18 KiB
C++
553 lines
18 KiB
C++
//===-- RISCVTargetParser.cpp - Parser for target features ------*- C++ -*-===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file implements a target parser to recognise hardware features
|
|
// for RISC-V CPUs.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/TargetParser/RISCVTargetParser.h"
|
|
#include "llvm/ADT/SetOperations.h"
|
|
#include "llvm/ADT/SmallSet.h"
|
|
#include "llvm/ADT/SmallVector.h"
|
|
#include "llvm/ADT/StringExtras.h"
|
|
#include "llvm/ADT/StringSwitch.h"
|
|
#include "llvm/ADT/StringTable.h"
|
|
#include "llvm/TargetParser/RISCVISAInfo.h"
|
|
|
|
namespace llvm {
|
|
namespace RISCV {
|
|
|
|
char ParserError::ID = 0;
|
|
char ParserWarning::ID = 0;
|
|
|
|
enum CPUKind : unsigned {
|
|
#define PROC(ENUM, NAME, DEFAULT_MARCH, FAST_SCALAR_UNALIGN, \
|
|
FAST_VECTOR_UNALIGN, MVENDORID, MARCHID, MIMPID) \
|
|
CK_##ENUM,
|
|
#define TUNE_PROC(ENUM, NAME) CK_##ENUM,
|
|
#include "llvm/TargetParser/RISCVTargetParserDef.inc"
|
|
};
|
|
|
|
constexpr CPUInfo RISCVCPUInfo[] = {
|
|
#define PROC(ENUM, NAME, DEFAULT_MARCH, FAST_SCALAR_UNALIGN, \
|
|
FAST_VECTOR_UNALIGN, MVENDORID, MARCHID, MIMPID) \
|
|
{ \
|
|
NAME, \
|
|
DEFAULT_MARCH, \
|
|
FAST_SCALAR_UNALIGN, \
|
|
FAST_VECTOR_UNALIGN, \
|
|
{MVENDORID, MARCHID, MIMPID}, \
|
|
},
|
|
#include "llvm/TargetParser/RISCVTargetParserDef.inc"
|
|
};
|
|
|
|
static const CPUInfo *getCPUInfoByName(StringRef CPU) {
|
|
for (auto &C : RISCVCPUInfo)
|
|
if (C.Name == CPU)
|
|
return &C;
|
|
return nullptr;
|
|
}
|
|
|
|
bool hasFastScalarUnalignedAccess(StringRef CPU) {
|
|
const CPUInfo *Info = getCPUInfoByName(CPU);
|
|
return Info && Info->FastScalarUnalignedAccess;
|
|
}
|
|
|
|
bool hasFastVectorUnalignedAccess(StringRef CPU) {
|
|
const CPUInfo *Info = getCPUInfoByName(CPU);
|
|
return Info && Info->FastVectorUnalignedAccess;
|
|
}
|
|
|
|
bool hasValidCPUModel(StringRef CPU) { return getCPUModel(CPU).isValid(); }
|
|
|
|
CPUModel getCPUModel(StringRef CPU) {
|
|
const CPUInfo *Info = getCPUInfoByName(CPU);
|
|
if (!Info)
|
|
return {0, 0, 0};
|
|
return Info->Model;
|
|
}
|
|
|
|
StringRef getCPUNameFromCPUModel(const CPUModel &Model) {
|
|
if (!Model.isValid())
|
|
return "";
|
|
|
|
for (auto &C : RISCVCPUInfo)
|
|
if (C.Model == Model)
|
|
return C.Name;
|
|
return "";
|
|
}
|
|
|
|
bool parseCPU(StringRef CPU, bool IsRV64) {
|
|
const CPUInfo *Info = getCPUInfoByName(CPU);
|
|
|
|
if (!Info)
|
|
return false;
|
|
return Info->is64Bit() == IsRV64;
|
|
}
|
|
|
|
bool parseTuneCPU(StringRef TuneCPU, bool IsRV64) {
|
|
std::optional<CPUKind> Kind =
|
|
llvm::StringSwitch<std::optional<CPUKind>>(TuneCPU)
|
|
#define TUNE_PROC(ENUM, NAME) .Case(NAME, CK_##ENUM)
|
|
#include "llvm/TargetParser/RISCVTargetParserDef.inc"
|
|
.Default(std::nullopt);
|
|
|
|
if (Kind.has_value())
|
|
return true;
|
|
|
|
// Fallback to parsing as a CPU.
|
|
return parseCPU(TuneCPU, IsRV64);
|
|
}
|
|
|
|
StringRef getMArchFromMcpu(StringRef CPU) {
|
|
const CPUInfo *Info = getCPUInfoByName(CPU);
|
|
if (!Info)
|
|
return "";
|
|
return Info->DefaultMarch;
|
|
}
|
|
|
|
void fillValidCPUArchList(SmallVectorImpl<StringRef> &Values, bool IsRV64) {
|
|
for (const auto &C : RISCVCPUInfo) {
|
|
if (IsRV64 == C.is64Bit())
|
|
Values.emplace_back(C.Name);
|
|
}
|
|
}
|
|
|
|
void fillValidTuneCPUArchList(SmallVectorImpl<StringRef> &Values, bool IsRV64) {
|
|
for (const auto &C : RISCVCPUInfo) {
|
|
if (IsRV64 == C.is64Bit())
|
|
Values.emplace_back(C.Name);
|
|
}
|
|
#define TUNE_PROC(ENUM, NAME) Values.emplace_back(StringRef(NAME));
|
|
#include "llvm/TargetParser/RISCVTargetParserDef.inc"
|
|
}
|
|
|
|
// This function is currently used by IREE, so it's not dead code.
|
|
void getFeaturesForCPU(StringRef CPU,
|
|
SmallVectorImpl<std::string> &EnabledFeatures,
|
|
bool NeedPlus) {
|
|
StringRef MarchFromCPU = llvm::RISCV::getMArchFromMcpu(CPU);
|
|
if (MarchFromCPU == "")
|
|
return;
|
|
|
|
EnabledFeatures.clear();
|
|
auto RII = RISCVISAInfo::parseArchString(
|
|
MarchFromCPU, /* EnableExperimentalExtension */ true);
|
|
|
|
if (llvm::errorToBool(RII.takeError()))
|
|
return;
|
|
|
|
std::vector<std::string> FeatStrings =
|
|
(*RII)->toFeatures(/* AddAllExtensions */ false);
|
|
for (const auto &F : FeatStrings)
|
|
if (NeedPlus)
|
|
EnabledFeatures.push_back(F);
|
|
else
|
|
EnabledFeatures.push_back(F.substr(1));
|
|
}
|
|
|
|
namespace {
|
|
class RISCVTuneFeatureLookupTable {
|
|
struct RISCVTuneFeature {
|
|
unsigned PosIdx;
|
|
unsigned NegIdx;
|
|
unsigned FeatureIdx;
|
|
};
|
|
|
|
struct RISCVImpliedTuneFeature {
|
|
unsigned FeatureIdx;
|
|
unsigned ImpliedFeatureIdx;
|
|
};
|
|
|
|
struct RISCVConfigurableTuneFeatures {
|
|
StringRef Processor;
|
|
unsigned DirectiveIdx;
|
|
|
|
bool operator<(const RISCVConfigurableTuneFeatures &RHS) const {
|
|
return Processor < RHS.Processor;
|
|
}
|
|
};
|
|
|
|
#define GET_TUNE_FEATURES
|
|
#define GET_CONFIGURABLE_TUNE_FEATURES
|
|
#include "llvm/TargetParser/RISCVTargetParserDef.inc"
|
|
|
|
// Positive directive name -> Feature name
|
|
StringMap<StringRef> PositiveMap;
|
|
// Negative directive name -> Feature name
|
|
StringMap<StringRef> NegativeMap;
|
|
|
|
StringMap<SmallVector<StringRef>> ImpliedFeatureMap;
|
|
StringMap<SmallVector<StringRef>> InvImpliedFeatureMap;
|
|
|
|
public:
|
|
using SmallStringSet = SmallSet<StringRef, 4>;
|
|
|
|
static void getAllTuneFeatures(SmallVectorImpl<StringRef> &Features) {
|
|
for (const auto &TuneFeature : TuneFeatures)
|
|
Features.push_back(TuneFeatureStrings[TuneFeature.FeatureIdx]);
|
|
}
|
|
|
|
static void getConfigurableFeatures(StringRef ProcName,
|
|
SmallStringSet &Directives) {
|
|
// Entries for the same processor are always put together.
|
|
auto [ItFirst, ItEnd] =
|
|
std::equal_range(std::begin(ConfigurableTuneFeatures),
|
|
std::end(ConfigurableTuneFeatures),
|
|
RISCVConfigurableTuneFeatures{ProcName, 0});
|
|
for (; ItFirst != ItEnd; ++ItFirst)
|
|
Directives.insert(TuneFeatureStrings[ItFirst->DirectiveIdx]);
|
|
}
|
|
|
|
RISCVTuneFeatureLookupTable() {
|
|
for (const auto &TuneFeature : TuneFeatures) {
|
|
StringRef PosDirective = TuneFeatureStrings[TuneFeature.PosIdx];
|
|
StringRef NegDirective = TuneFeatureStrings[TuneFeature.NegIdx];
|
|
StringRef FeatureName = TuneFeatureStrings[TuneFeature.FeatureIdx];
|
|
PositiveMap[PosDirective] = FeatureName;
|
|
NegativeMap[NegDirective] = FeatureName;
|
|
}
|
|
|
|
for (const auto &Imp : ImpliedTuneFeatures) {
|
|
StringRef Feature = TuneFeatureStrings[Imp.FeatureIdx];
|
|
StringRef ImpliedFeature = TuneFeatureStrings[Imp.ImpliedFeatureIdx];
|
|
ImpliedFeatureMap[Feature].push_back(ImpliedFeature);
|
|
InvImpliedFeatureMap[ImpliedFeature].push_back(Feature);
|
|
}
|
|
}
|
|
|
|
/// Returns {Feature name, Is positive or not}, or empty feature name
|
|
/// if not found.
|
|
std::pair<StringRef, bool> getFeature(StringRef DirectiveName) const {
|
|
auto It = PositiveMap.find(DirectiveName);
|
|
if (It != PositiveMap.end())
|
|
return {It->getValue(), /*IsPositive=*/true};
|
|
|
|
return {NegativeMap.lookup(DirectiveName), /*IsPositive=*/false};
|
|
}
|
|
|
|
/// Returns the implied features, or empty ArrayRef if not found. Note:
|
|
/// ImpliedFeatureMap / InvImpliedFeatureMap are the owners of these implied
|
|
/// feature lists, so we can just return the ArrayRef.
|
|
ArrayRef<StringRef> featureImplies(StringRef FeatureName,
|
|
bool Inverse = false) const {
|
|
const auto &Map = Inverse ? InvImpliedFeatureMap : ImpliedFeatureMap;
|
|
auto It = Map.find(FeatureName);
|
|
if (It == Map.end())
|
|
return {};
|
|
return It->second;
|
|
}
|
|
};
|
|
} // namespace
|
|
|
|
void getAllTuneFeatures(SmallVectorImpl<StringRef> &Features) {
|
|
RISCVTuneFeatureLookupTable::getAllTuneFeatures(Features);
|
|
}
|
|
|
|
Error parseTuneFeatureString(StringRef ProcName, StringRef TFString,
|
|
SmallVectorImpl<std::string> &ResFeatures) {
|
|
RISCVTuneFeatureLookupTable TFLookup;
|
|
using SmallStringSet = RISCVTuneFeatureLookupTable::SmallStringSet;
|
|
|
|
// Do not create ParserWarning right away. Instead, we store the warning
|
|
// message until the last moment.
|
|
std::string WarningMsg;
|
|
|
|
TFString = TFString.trim();
|
|
if (TFString.empty())
|
|
return Error::success();
|
|
|
|
// Note: StringSet is not really ergonomic to use in this case here.
|
|
SmallStringSet PositiveFeatures;
|
|
SmallStringSet NegativeFeatures;
|
|
SmallStringSet PerProcDirectives;
|
|
RISCVTuneFeatureLookupTable::getConfigurableFeatures(ProcName,
|
|
PerProcDirectives);
|
|
if (PerProcDirectives.empty() && !ProcName.empty())
|
|
return make_error<ParserError>("Processor '" + Twine(ProcName) +
|
|
"' has no "
|
|
"configurable tuning features");
|
|
|
|
// Phase 1: Collect explicit features.
|
|
StringRef DirectiveStr;
|
|
do {
|
|
std::tie(DirectiveStr, TFString) = TFString.split(",");
|
|
auto [FeatureName, IsPositive] = TFLookup.getFeature(DirectiveStr);
|
|
if (FeatureName.empty()) {
|
|
raw_string_ostream SS(WarningMsg);
|
|
SS << "unrecognized tune feature directive '" << DirectiveStr << "'";
|
|
continue;
|
|
}
|
|
|
|
auto &Features = IsPositive ? PositiveFeatures : NegativeFeatures;
|
|
if (!Features.insert(FeatureName).second)
|
|
return make_error<ParserError>(
|
|
"cannot specify more than one instance of '" + Twine(DirectiveStr) +
|
|
"'");
|
|
|
|
if (!PerProcDirectives.count(DirectiveStr) && !ProcName.empty())
|
|
return make_error<ParserError>("Directive '" + Twine(DirectiveStr) +
|
|
"' is not "
|
|
"allowed to be used with processor '" +
|
|
Twine(ProcName) + "'");
|
|
} while (!TFString.empty());
|
|
|
|
auto Intersection =
|
|
llvm::set_intersection(PositiveFeatures, NegativeFeatures);
|
|
if (!Intersection.empty()) {
|
|
std::string IntersectedStr = join(Intersection, "', '");
|
|
return make_error<ParserError>("Feature(s) '" + Twine(IntersectedStr) +
|
|
"' cannot appear in both "
|
|
"positive and negative directives");
|
|
}
|
|
|
|
// Phase 2: Derive implied features.
|
|
SmallStringSet DerivedPosFeatures;
|
|
SmallStringSet DerivedNegFeatures;
|
|
for (StringRef PF : PositiveFeatures) {
|
|
if (auto FeatureList = TFLookup.featureImplies(PF); !FeatureList.empty())
|
|
DerivedPosFeatures.insert_range(FeatureList);
|
|
}
|
|
for (StringRef NF : NegativeFeatures) {
|
|
if (auto FeatureList = TFLookup.featureImplies(NF, /*Inverse=*/true);
|
|
!FeatureList.empty())
|
|
DerivedNegFeatures.insert_range(FeatureList);
|
|
}
|
|
PositiveFeatures.insert_range(DerivedPosFeatures);
|
|
NegativeFeatures.insert_range(DerivedNegFeatures);
|
|
|
|
Intersection = llvm::set_intersection(PositiveFeatures, NegativeFeatures);
|
|
if (!Intersection.empty()) {
|
|
std::string IntersectedStr = join(Intersection, "', '");
|
|
return make_error<ParserError>("Feature(s) '" + Twine(IntersectedStr) +
|
|
"' were implied by both "
|
|
"positive and negative directives");
|
|
}
|
|
|
|
// Export the result.
|
|
const std::string PosPrefix("+");
|
|
const std::string NegPrefix("-");
|
|
for (StringRef PF : PositiveFeatures)
|
|
ResFeatures.emplace_back(PosPrefix + PF.str());
|
|
for (StringRef NF : NegativeFeatures)
|
|
ResFeatures.emplace_back(NegPrefix + NF.str());
|
|
|
|
if (WarningMsg.empty())
|
|
return Error::success();
|
|
|
|
return make_error<ParserWarning>(WarningMsg);
|
|
}
|
|
|
|
void getCPUConfigurableTuneFeatures(StringRef CPU,
|
|
SmallVectorImpl<StringRef> &Directives) {
|
|
RISCVTuneFeatureLookupTable::SmallStringSet DirectiveSet;
|
|
RISCVTuneFeatureLookupTable::getConfigurableFeatures(CPU, DirectiveSet);
|
|
Directives.assign(DirectiveSet.begin(), DirectiveSet.end());
|
|
}
|
|
} // namespace RISCV
|
|
|
|
namespace RISCVVType {
|
|
// Encode VTYPE into the binary format used by the the VSETVLI instruction which
|
|
// is used by our MC layer representation.
|
|
//
|
|
// Bits | Name | Description
|
|
// -----+------------+------------------------------------------------
|
|
// 8 | altfmt | Alternative format for bf16/ofp8
|
|
// 7 | vma | Vector mask agnostic
|
|
// 6 | vta | Vector tail agnostic
|
|
// 5:3 | vsew[2:0] | Standard element width (SEW) setting
|
|
// 2:0 | vlmul[2:0] | Vector register group multiplier (LMUL) setting
|
|
unsigned encodeVTYPE(VLMUL VLMul, unsigned SEW, bool TailAgnostic,
|
|
bool MaskAgnostic, bool AltFmt) {
|
|
assert(isValidSEW(SEW) && "Invalid SEW");
|
|
unsigned VLMulBits = static_cast<unsigned>(VLMul);
|
|
unsigned VSEWBits = encodeSEW(SEW);
|
|
unsigned VTypeI = (VSEWBits << 3) | (VLMulBits & 0x7);
|
|
if (TailAgnostic)
|
|
VTypeI |= 0x40;
|
|
if (MaskAgnostic)
|
|
VTypeI |= 0x80;
|
|
if (AltFmt)
|
|
VTypeI |= 0x100;
|
|
|
|
return VTypeI;
|
|
}
|
|
|
|
unsigned encodeXSfmmVType(unsigned SEW, unsigned Widen, bool AltFmt) {
|
|
assert(isValidSEW(SEW) && "Invalid SEW");
|
|
assert((Widen == 1 || Widen == 2 || Widen == 4) && "Invalid Widen");
|
|
unsigned VSEWBits = encodeSEW(SEW);
|
|
unsigned TWiden = Log2_32(Widen) + 1;
|
|
unsigned VTypeI = (VSEWBits << 3) | AltFmt << 8 | TWiden << 9;
|
|
return VTypeI;
|
|
}
|
|
|
|
namespace IME {
|
|
static void assertValidXLenForVType(unsigned XLen) {
|
|
assert((XLen == 32 || XLen == 64) && "Invalid XLEN");
|
|
}
|
|
|
|
static unsigned getLambdaShift(unsigned XLen) {
|
|
assertValidXLenForVType(XLen);
|
|
return XLen - 4;
|
|
}
|
|
|
|
static unsigned getAltFmtAShift(unsigned XLen) {
|
|
assertValidXLenForVType(XLen);
|
|
return XLen - 6;
|
|
}
|
|
|
|
static unsigned getAltFmtBShift(unsigned XLen) {
|
|
assertValidXLenForVType(XLen);
|
|
return XLen - 7;
|
|
}
|
|
|
|
static unsigned getBSShift(unsigned XLen) {
|
|
assertValidXLenForVType(XLen);
|
|
return XLen - 5;
|
|
}
|
|
|
|
unsigned encodeLambda(unsigned Lambda) {
|
|
assert(isValidLambda(Lambda) && "Invalid IME lambda");
|
|
if (Lambda == 0)
|
|
return 0;
|
|
return Log2_32(Lambda) + 1;
|
|
}
|
|
|
|
std::optional<unsigned> decodeLambda(unsigned Encoding) {
|
|
assert(Encoding < 8 && "Invalid IME lambda encoding");
|
|
if (Encoding == 0)
|
|
return std::nullopt;
|
|
return 1U << (Encoding - 1);
|
|
}
|
|
|
|
uint64_t getVTypeFieldsMask(unsigned XLen) {
|
|
assertValidXLenForVType(XLen);
|
|
return (0x7ULL << getLambdaShift(XLen)) | (1ULL << getAltFmtAShift(XLen)) |
|
|
(1ULL << getAltFmtBShift(XLen)) | (1ULL << getBSShift(XLen));
|
|
}
|
|
|
|
uint64_t encodeVTypeFields(unsigned XLen, unsigned Lambda, bool AltFmtA,
|
|
bool AltFmtB, bool BlockSize16) {
|
|
assertValidXLenForVType(XLen);
|
|
uint64_t VType = uint64_t(encodeLambda(Lambda)) << getLambdaShift(XLen);
|
|
if (AltFmtA)
|
|
VType |= 1ULL << getAltFmtAShift(XLen);
|
|
if (AltFmtB)
|
|
VType |= 1ULL << getAltFmtBShift(XLen);
|
|
if (BlockSize16)
|
|
VType |= 1ULL << getBSShift(XLen);
|
|
return VType;
|
|
}
|
|
|
|
uint64_t addVTypeFields(uint64_t VType, unsigned XLen, unsigned Lambda,
|
|
bool AltFmtA, bool AltFmtB, bool BlockSize16) {
|
|
return (VType & ~getVTypeFieldsMask(XLen)) |
|
|
encodeVTypeFields(XLen, Lambda, AltFmtA, AltFmtB, BlockSize16);
|
|
}
|
|
|
|
unsigned getLambdaEncoding(uint64_t VType, unsigned XLen) {
|
|
assertValidXLenForVType(XLen);
|
|
return (VType >> getLambdaShift(XLen)) & 0x7;
|
|
}
|
|
|
|
std::optional<unsigned> getLambda(uint64_t VType, unsigned XLen) {
|
|
return decodeLambda(getLambdaEncoding(VType, XLen));
|
|
}
|
|
|
|
bool isAltFmtA(uint64_t VType, unsigned XLen) {
|
|
assertValidXLenForVType(XLen);
|
|
return VType & (1ULL << getAltFmtAShift(XLen));
|
|
}
|
|
|
|
bool isAltFmtB(uint64_t VType, unsigned XLen) {
|
|
assertValidXLenForVType(XLen);
|
|
return VType & (1ULL << getAltFmtBShift(XLen));
|
|
}
|
|
|
|
bool isBlockSize16(uint64_t VType, unsigned XLen) {
|
|
assertValidXLenForVType(XLen);
|
|
return VType & (1ULL << getBSShift(XLen));
|
|
}
|
|
} // namespace IME
|
|
|
|
std::pair<unsigned, bool> decodeVLMUL(VLMUL VLMul) {
|
|
switch (VLMul) {
|
|
default:
|
|
llvm_unreachable("Unexpected LMUL value!");
|
|
case LMUL_1:
|
|
case LMUL_2:
|
|
case LMUL_4:
|
|
case LMUL_8:
|
|
return std::make_pair(1 << static_cast<unsigned>(VLMul), false);
|
|
case LMUL_F2:
|
|
case LMUL_F4:
|
|
case LMUL_F8:
|
|
return std::make_pair(1 << (8 - static_cast<unsigned>(VLMul)), true);
|
|
}
|
|
}
|
|
|
|
void printVType(unsigned VType, raw_ostream &OS) {
|
|
unsigned Sew = getSEW(VType);
|
|
OS << "e" << Sew;
|
|
|
|
bool AltFmt = RISCVVType::isAltFmt(VType);
|
|
if (AltFmt)
|
|
OS << "alt";
|
|
|
|
unsigned LMul;
|
|
bool Fractional;
|
|
std::tie(LMul, Fractional) = decodeVLMUL(getVLMUL(VType));
|
|
|
|
if (Fractional)
|
|
OS << ", mf";
|
|
else
|
|
OS << ", m";
|
|
OS << LMul;
|
|
|
|
if (isTailAgnostic(VType))
|
|
OS << ", ta";
|
|
else
|
|
OS << ", tu";
|
|
|
|
if (isMaskAgnostic(VType))
|
|
OS << ", ma";
|
|
else
|
|
OS << ", mu";
|
|
}
|
|
|
|
void printXSfmmVType(unsigned VType, raw_ostream &OS) {
|
|
OS << "e" << getSEW(VType) << ", w" << getXSfmmWiden(VType);
|
|
}
|
|
|
|
unsigned getSEWLMULRatio(unsigned SEW, VLMUL VLMul) {
|
|
unsigned LMul;
|
|
bool Fractional;
|
|
std::tie(LMul, Fractional) = decodeVLMUL(VLMul);
|
|
|
|
// Convert LMul to a fixed point value with 3 fractional bits.
|
|
LMul = Fractional ? (8 / LMul) : (LMul * 8);
|
|
|
|
assert(SEW >= 8 && "Unexpected SEW value");
|
|
return (SEW * 8) / LMul;
|
|
}
|
|
|
|
std::optional<VLMUL> getSameRatioLMUL(unsigned Ratio, unsigned EEW) {
|
|
unsigned EMULFixedPoint = (EEW * 8) / Ratio;
|
|
bool Fractional = EMULFixedPoint < 8;
|
|
unsigned EMUL = Fractional ? 8 / EMULFixedPoint : EMULFixedPoint / 8;
|
|
if (!isValidLMUL(EMUL, Fractional))
|
|
return std::nullopt;
|
|
return RISCVVType::encodeLMUL(EMUL, Fractional);
|
|
}
|
|
|
|
} // namespace RISCVVType
|
|
|
|
} // namespace llvm
|