mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-28 06:36:05 +00:00

We have more than 32 extensions in our downstream and had to change this type from uint32_t to uint64_t. To simplify our downstream and make the code more flexible, I propose to make it an array of uint32_t that we can size based on the number of extensions. I really wanted to use std::bitset, but we have to print the bits to a .inc file which can't easily be done with std::bitset.
904 lines
33 KiB
C++
904 lines
33 KiB
C++
//===-- RISCVVEmitter.cpp - Generate riscv_vector.h for use with clang ----===//
|
|
//
|
|
// 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 tablegen backend is responsible for emitting riscv_vector.h which
|
|
// includes a declaration and definition of each intrinsic functions specified
|
|
// in https://github.com/riscv/rvv-intrinsic-doc.
|
|
//
|
|
// See also the documentation in include/clang/Basic/riscv_vector.td.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "clang/Support/RISCVVIntrinsicUtils.h"
|
|
#include "llvm/ADT/ArrayRef.h"
|
|
#include "llvm/ADT/StringExtras.h"
|
|
#include "llvm/ADT/StringMap.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/ADT/StringSwitch.h"
|
|
#include "llvm/ADT/Twine.h"
|
|
#include "llvm/TableGen/Error.h"
|
|
#include "llvm/TableGen/Record.h"
|
|
#include "llvm/TableGen/StringToOffsetTable.h"
|
|
#include <optional>
|
|
|
|
using namespace llvm;
|
|
using namespace clang::RISCV;
|
|
|
|
namespace {
|
|
struct SemaRecord {
|
|
// Intrinsic name, e.g. vadd_vv
|
|
std::string Name;
|
|
|
|
// Overloaded intrinsic name, could be empty if can be computed from Name
|
|
// e.g. vadd
|
|
std::string OverloadedName;
|
|
|
|
// Supported type, mask of BasicType.
|
|
unsigned TypeRangeMask;
|
|
|
|
// Supported LMUL.
|
|
unsigned Log2LMULMask;
|
|
|
|
// Required extensions for this intrinsic.
|
|
uint32_t RequiredExtensions[(RVV_REQ_NUM + 31) / 32];
|
|
|
|
// Prototype for this intrinsic.
|
|
SmallVector<PrototypeDescriptor> Prototype;
|
|
|
|
// Suffix of intrinsic name.
|
|
SmallVector<PrototypeDescriptor> Suffix;
|
|
|
|
// Suffix of overloaded intrinsic name.
|
|
SmallVector<PrototypeDescriptor> OverloadedSuffix;
|
|
|
|
// Number of field, large than 1 if it's segment load/store.
|
|
unsigned NF;
|
|
|
|
bool HasMasked :1;
|
|
bool HasVL :1;
|
|
bool HasMaskedOffOperand :1;
|
|
bool HasTailPolicy : 1;
|
|
bool HasMaskPolicy : 1;
|
|
bool HasFRMRoundModeOp : 1;
|
|
bool IsTuple : 1;
|
|
LLVM_PREFERRED_TYPE(PolicyScheme)
|
|
uint8_t UnMaskedPolicyScheme : 2;
|
|
LLVM_PREFERRED_TYPE(PolicyScheme)
|
|
uint8_t MaskedPolicyScheme : 2;
|
|
};
|
|
|
|
// Compressed function signature table.
|
|
class SemaSignatureTable {
|
|
private:
|
|
std::vector<PrototypeDescriptor> SignatureTable;
|
|
|
|
void insert(ArrayRef<PrototypeDescriptor> Signature);
|
|
|
|
public:
|
|
static constexpr unsigned INVALID_INDEX = ~0U;
|
|
|
|
// Create compressed signature table from SemaRecords.
|
|
void init(ArrayRef<SemaRecord> SemaRecords);
|
|
|
|
// Query the Signature, return INVALID_INDEX if not found.
|
|
unsigned getIndex(ArrayRef<PrototypeDescriptor> Signature);
|
|
|
|
/// Print signature table in RVVHeader Record to \p OS
|
|
void print(raw_ostream &OS);
|
|
};
|
|
|
|
class RVVEmitter {
|
|
private:
|
|
const RecordKeeper &Records;
|
|
RVVTypeCache TypeCache;
|
|
|
|
public:
|
|
RVVEmitter(const RecordKeeper &R) : Records(R) {}
|
|
|
|
/// Emit riscv_vector.h
|
|
void createHeader(raw_ostream &o);
|
|
|
|
/// Emit all the __builtin prototypes and code needed by Sema.
|
|
void createBuiltins(raw_ostream &o);
|
|
|
|
/// Emit all the information needed to map builtin -> LLVM IR intrinsic.
|
|
void createCodeGen(raw_ostream &o);
|
|
|
|
/// Emit all the information needed by SemaRISCVVectorLookup.cpp.
|
|
/// We've large number of intrinsic function for RVV, creating a customized
|
|
/// could speed up the compilation time.
|
|
void createSema(raw_ostream &o);
|
|
|
|
private:
|
|
/// Create all intrinsics and add them to \p Out and SemaRecords.
|
|
void createRVVIntrinsics(std::vector<std::unique_ptr<RVVIntrinsic>> &Out,
|
|
std::vector<SemaRecord> *SemaRecords = nullptr);
|
|
/// Create all intrinsic records and SemaSignatureTable from SemaRecords.
|
|
void createRVVIntrinsicRecords(std::vector<RVVIntrinsicRecord> &Out,
|
|
SemaSignatureTable &SST,
|
|
ArrayRef<SemaRecord> SemaRecords);
|
|
|
|
/// Print HeaderCode in RVVHeader Record to \p Out
|
|
void printHeaderCode(raw_ostream &OS);
|
|
};
|
|
|
|
} // namespace
|
|
|
|
static BasicType ParseBasicType(char c) {
|
|
switch (c) {
|
|
case 'c':
|
|
return BasicType::Int8;
|
|
break;
|
|
case 's':
|
|
return BasicType::Int16;
|
|
break;
|
|
case 'i':
|
|
return BasicType::Int32;
|
|
break;
|
|
case 'l':
|
|
return BasicType::Int64;
|
|
break;
|
|
case 'x':
|
|
return BasicType::Float16;
|
|
break;
|
|
case 'f':
|
|
return BasicType::Float32;
|
|
break;
|
|
case 'd':
|
|
return BasicType::Float64;
|
|
break;
|
|
case 'y':
|
|
return BasicType::BFloat16;
|
|
break;
|
|
default:
|
|
return BasicType::Unknown;
|
|
}
|
|
}
|
|
|
|
static VectorTypeModifier getTupleVTM(unsigned NF) {
|
|
assert(2 <= NF && NF <= 8 && "2 <= NF <= 8");
|
|
return static_cast<VectorTypeModifier>(
|
|
static_cast<uint8_t>(VectorTypeModifier::Tuple2) + (NF - 2));
|
|
}
|
|
|
|
static unsigned getIndexedLoadStorePtrIdx(const RVVIntrinsic *RVVI) {
|
|
// We need a special rule for segment load/store since the data width is not
|
|
// encoded in the intrinsic name itself.
|
|
const StringRef IRName = RVVI->getIRName();
|
|
constexpr unsigned RVV_VTA = 0x1;
|
|
constexpr unsigned RVV_VMA = 0x2;
|
|
|
|
if (IRName.starts_with("vloxseg") || IRName.starts_with("vluxseg")) {
|
|
bool NoPassthru =
|
|
(RVVI->isMasked() && (RVVI->getPolicyAttrsBits() & RVV_VTA) &&
|
|
(RVVI->getPolicyAttrsBits() & RVV_VMA)) ||
|
|
(!RVVI->isMasked() && (RVVI->getPolicyAttrsBits() & RVV_VTA));
|
|
return RVVI->isMasked() ? NoPassthru ? 1 : 2 : NoPassthru ? 0 : 1;
|
|
}
|
|
if (IRName.starts_with("vsoxseg") || IRName.starts_with("vsuxseg"))
|
|
return RVVI->isMasked() ? 1 : 0;
|
|
|
|
return (unsigned)-1;
|
|
}
|
|
|
|
// This function is used to get the log2SEW of each segment load/store, this
|
|
// prevent to add a member to RVVIntrinsic.
|
|
static unsigned getSegInstLog2SEW(StringRef InstName) {
|
|
// clang-format off
|
|
// We need a special rule for indexed segment load/store since the data width
|
|
// is not encoded in the intrinsic name itself.
|
|
if (InstName.starts_with("vloxseg") || InstName.starts_with("vluxseg") ||
|
|
InstName.starts_with("vsoxseg") || InstName.starts_with("vsuxseg"))
|
|
return (unsigned)-1;
|
|
|
|
#define KEY_VAL(KEY, VAL) {#KEY, VAL}
|
|
#define KEY_VAL_ALL_W_POLICY(KEY, VAL) \
|
|
KEY_VAL(KEY, VAL), \
|
|
KEY_VAL(KEY ## _tu, VAL), \
|
|
KEY_VAL(KEY ## _tum, VAL), \
|
|
KEY_VAL(KEY ## _tumu, VAL), \
|
|
KEY_VAL(KEY ## _mu, VAL)
|
|
|
|
#define KEY_VAL_ALL_NF_BASE(MACRO_NAME, NAME, SEW, LOG2SEW, FF) \
|
|
MACRO_NAME(NAME ## 2e ## SEW ## FF, LOG2SEW), \
|
|
MACRO_NAME(NAME ## 3e ## SEW ## FF, LOG2SEW), \
|
|
MACRO_NAME(NAME ## 4e ## SEW ## FF, LOG2SEW), \
|
|
MACRO_NAME(NAME ## 5e ## SEW ## FF, LOG2SEW), \
|
|
MACRO_NAME(NAME ## 6e ## SEW ## FF, LOG2SEW), \
|
|
MACRO_NAME(NAME ## 7e ## SEW ## FF, LOG2SEW), \
|
|
MACRO_NAME(NAME ## 8e ## SEW ## FF, LOG2SEW)
|
|
|
|
#define KEY_VAL_ALL_NF(NAME, SEW, LOG2SEW) \
|
|
KEY_VAL_ALL_NF_BASE(KEY_VAL_ALL_W_POLICY, NAME, SEW, LOG2SEW,)
|
|
|
|
#define KEY_VAL_FF_ALL_NF(NAME, SEW, LOG2SEW) \
|
|
KEY_VAL_ALL_NF_BASE(KEY_VAL_ALL_W_POLICY, NAME, SEW, LOG2SEW, ff)
|
|
|
|
#define KEY_VAL_ALL_NF_SEW_BASE(MACRO_NAME, NAME) \
|
|
MACRO_NAME(NAME, 8, 3), \
|
|
MACRO_NAME(NAME, 16, 4), \
|
|
MACRO_NAME(NAME, 32, 5), \
|
|
MACRO_NAME(NAME, 64, 6)
|
|
|
|
#define KEY_VAL_ALL_NF_SEW(NAME) \
|
|
KEY_VAL_ALL_NF_SEW_BASE(KEY_VAL_ALL_NF, NAME)
|
|
|
|
#define KEY_VAL_FF_ALL_NF_SEW(NAME) \
|
|
KEY_VAL_ALL_NF_SEW_BASE(KEY_VAL_FF_ALL_NF, NAME)
|
|
// clang-format on
|
|
|
|
static StringMap<unsigned> SegInsts = {
|
|
KEY_VAL_ALL_NF_SEW(vlseg), KEY_VAL_FF_ALL_NF_SEW(vlseg),
|
|
KEY_VAL_ALL_NF_SEW(vlsseg), KEY_VAL_ALL_NF_SEW(vsseg),
|
|
KEY_VAL_ALL_NF_SEW(vssseg)};
|
|
|
|
#undef KEY_VAL_ALL_NF_SEW
|
|
#undef KEY_VAL_ALL_NF
|
|
#undef KEY_VAL
|
|
|
|
return SegInsts.lookup(InstName);
|
|
}
|
|
|
|
void emitCodeGenSwitchBody(const RVVIntrinsic *RVVI, raw_ostream &OS) {
|
|
if (!RVVI->getIRName().empty())
|
|
OS << " ID = Intrinsic::riscv_" + RVVI->getIRName() + ";\n";
|
|
|
|
OS << " PolicyAttrs = " << RVVI->getPolicyAttrsBits() << ";\n";
|
|
OS << " SegInstSEW = " << getSegInstLog2SEW(RVVI->getOverloadedName())
|
|
<< ";\n";
|
|
|
|
if (RVVI->hasManualCodegen()) {
|
|
OS << "IsMasked = " << (RVVI->isMasked() ? "true" : "false") << ";\n";
|
|
|
|
// Skip the non-indexed load/store and compatible header load/store.
|
|
OS << "if (SegInstSEW == (unsigned)-1) {\n";
|
|
OS << " auto PointeeType = E->getArg(" << getIndexedLoadStorePtrIdx(RVVI)
|
|
<< " )->getType()->getPointeeType();\n";
|
|
OS << " SegInstSEW = "
|
|
" llvm::Log2_64(getContext().getTypeSize(PointeeType));\n}\n";
|
|
|
|
OS << RVVI->getManualCodegen();
|
|
OS << "break;\n";
|
|
return;
|
|
}
|
|
|
|
for (const auto &I : enumerate(RVVI->getInputTypes())) {
|
|
if (I.value()->isPointer()) {
|
|
assert(RVVI->getIntrinsicTypes().front() == -1 &&
|
|
"RVVI should be vector load intrinsic.");
|
|
}
|
|
}
|
|
|
|
if (RVVI->isMasked()) {
|
|
if (RVVI->hasVL()) {
|
|
OS << " std::rotate(Ops.begin(), Ops.begin() + 1, Ops.end() - 1);\n";
|
|
if (RVVI->hasPolicyOperand())
|
|
OS << " Ops.push_back(ConstantInt::get(Ops.back()->getType(),"
|
|
" PolicyAttrs));\n";
|
|
if (RVVI->hasMaskedOffOperand() && RVVI->getPolicyAttrs().isTAMAPolicy())
|
|
OS << " Ops.insert(Ops.begin(), "
|
|
"llvm::PoisonValue::get(ResultType));\n";
|
|
// Masked reduction cases.
|
|
if (!RVVI->hasMaskedOffOperand() && RVVI->hasPassthruOperand() &&
|
|
RVVI->getPolicyAttrs().isTAMAPolicy())
|
|
OS << " Ops.insert(Ops.begin(), "
|
|
"llvm::PoisonValue::get(ResultType));\n";
|
|
} else {
|
|
OS << " std::rotate(Ops.begin(), Ops.begin() + 1, Ops.end());\n";
|
|
}
|
|
} else {
|
|
if (RVVI->hasPolicyOperand())
|
|
OS << " Ops.push_back(ConstantInt::get(Ops.back()->getType(), "
|
|
"PolicyAttrs));\n";
|
|
else if (RVVI->hasPassthruOperand() && RVVI->getPolicyAttrs().isTAPolicy())
|
|
OS << " Ops.insert(Ops.begin(), llvm::PoisonValue::get(ResultType));\n";
|
|
}
|
|
|
|
OS << " IntrinsicTypes = {";
|
|
ListSeparator LS;
|
|
for (const auto &Idx : RVVI->getIntrinsicTypes()) {
|
|
if (Idx == -1)
|
|
OS << LS << "ResultType";
|
|
else
|
|
OS << LS << "Ops[" << Idx << "]->getType()";
|
|
}
|
|
|
|
// VL could be i64 or i32, need to encode it in IntrinsicTypes. VL is
|
|
// always last operand.
|
|
if (RVVI->hasVL())
|
|
OS << ", Ops.back()->getType()";
|
|
OS << "};\n";
|
|
OS << " break;\n";
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// SemaSignatureTable implementation
|
|
//===----------------------------------------------------------------------===//
|
|
void SemaSignatureTable::init(ArrayRef<SemaRecord> SemaRecords) {
|
|
// Sort signature entries by length, let longer signature insert first, to
|
|
// make it more possible to reuse table entries, that can reduce ~10% table
|
|
// size.
|
|
struct Compare {
|
|
bool operator()(const SmallVector<PrototypeDescriptor> &A,
|
|
const SmallVector<PrototypeDescriptor> &B) const {
|
|
if (A.size() != B.size())
|
|
return A.size() > B.size();
|
|
|
|
size_t Len = A.size();
|
|
for (size_t i = 0; i < Len; ++i) {
|
|
if (A[i] != B[i])
|
|
return A[i] < B[i];
|
|
}
|
|
|
|
return false;
|
|
}
|
|
};
|
|
|
|
std::set<SmallVector<PrototypeDescriptor>, Compare> Signatures;
|
|
auto InsertToSignatureSet =
|
|
[&](const SmallVector<PrototypeDescriptor> &Signature) {
|
|
if (Signature.empty())
|
|
return;
|
|
|
|
Signatures.insert(Signature);
|
|
};
|
|
|
|
assert(!SemaRecords.empty());
|
|
|
|
for (const SemaRecord &SR : SemaRecords) {
|
|
InsertToSignatureSet(SR.Prototype);
|
|
InsertToSignatureSet(SR.Suffix);
|
|
InsertToSignatureSet(SR.OverloadedSuffix);
|
|
}
|
|
|
|
for (auto &Sig : Signatures)
|
|
insert(Sig);
|
|
}
|
|
|
|
void SemaSignatureTable::insert(ArrayRef<PrototypeDescriptor> Signature) {
|
|
if (getIndex(Signature) != INVALID_INDEX)
|
|
return;
|
|
|
|
// Insert Signature into SignatureTable if not found in the table.
|
|
SignatureTable.insert(SignatureTable.begin(), Signature.begin(),
|
|
Signature.end());
|
|
}
|
|
|
|
unsigned SemaSignatureTable::getIndex(ArrayRef<PrototypeDescriptor> Signature) {
|
|
// Empty signature could be point into any index since there is length
|
|
// field when we use, so just always point it to 0.
|
|
if (Signature.empty())
|
|
return 0;
|
|
|
|
// Checking Signature already in table or not.
|
|
if (Signature.size() <= SignatureTable.size()) {
|
|
size_t Bound = SignatureTable.size() - Signature.size() + 1;
|
|
for (size_t Index = 0; Index < Bound; ++Index) {
|
|
if (equal(Signature.begin(), Signature.end(),
|
|
SignatureTable.begin() + Index))
|
|
return Index;
|
|
}
|
|
}
|
|
|
|
return INVALID_INDEX;
|
|
}
|
|
|
|
void SemaSignatureTable::print(raw_ostream &OS) {
|
|
for (const auto &Sig : SignatureTable)
|
|
OS << "PrototypeDescriptor(" << static_cast<int>(Sig.PT) << ", "
|
|
<< static_cast<int>(Sig.VTM) << ", " << static_cast<int>(Sig.TM)
|
|
<< "),\n";
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// RVVEmitter implementation
|
|
//===----------------------------------------------------------------------===//
|
|
void RVVEmitter::createHeader(raw_ostream &OS) {
|
|
|
|
OS << "/*===---- riscv_vector.h - RISC-V V-extension RVVIntrinsics "
|
|
"-------------------===\n"
|
|
" *\n"
|
|
" *\n"
|
|
" * Part of the LLVM Project, under the Apache License v2.0 with LLVM "
|
|
"Exceptions.\n"
|
|
" * See https://llvm.org/LICENSE.txt for license information.\n"
|
|
" * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n"
|
|
" *\n"
|
|
" *===-----------------------------------------------------------------"
|
|
"------===\n"
|
|
" */\n\n";
|
|
|
|
OS << "#ifndef __RISCV_VECTOR_H\n";
|
|
OS << "#define __RISCV_VECTOR_H\n\n";
|
|
|
|
OS << "#include <stdint.h>\n";
|
|
OS << "#include <stddef.h>\n\n";
|
|
|
|
OS << "#ifdef __cplusplus\n";
|
|
OS << "extern \"C\" {\n";
|
|
OS << "#endif\n\n";
|
|
|
|
OS << "#pragma clang riscv intrinsic vector\n\n";
|
|
|
|
printHeaderCode(OS);
|
|
|
|
auto printType = [&](auto T) {
|
|
OS << "typedef " << T->getClangBuiltinStr() << " " << T->getTypeStr()
|
|
<< ";\n";
|
|
};
|
|
|
|
constexpr int Log2LMULs[] = {-3, -2, -1, 0, 1, 2, 3};
|
|
// Print RVV boolean types.
|
|
for (int Log2LMUL : Log2LMULs) {
|
|
auto T = TypeCache.computeType(BasicType::Int8, Log2LMUL,
|
|
PrototypeDescriptor::Mask);
|
|
if (T)
|
|
printType(*T);
|
|
}
|
|
// Print RVV int/float types.
|
|
for (char I : StringRef("csil")) {
|
|
BasicType BT = ParseBasicType(I);
|
|
for (int Log2LMUL : Log2LMULs) {
|
|
auto T = TypeCache.computeType(BT, Log2LMUL, PrototypeDescriptor::Vector);
|
|
if (T) {
|
|
printType(*T);
|
|
auto UT = TypeCache.computeType(
|
|
BT, Log2LMUL,
|
|
PrototypeDescriptor(BaseTypeModifier::Vector,
|
|
VectorTypeModifier::NoModifier,
|
|
TypeModifier::UnsignedInteger));
|
|
printType(*UT);
|
|
}
|
|
for (int NF = 2; NF <= 8; ++NF) {
|
|
auto TupleT = TypeCache.computeType(
|
|
BT, Log2LMUL,
|
|
PrototypeDescriptor(BaseTypeModifier::Vector, getTupleVTM(NF),
|
|
TypeModifier::SignedInteger));
|
|
auto TupleUT = TypeCache.computeType(
|
|
BT, Log2LMUL,
|
|
PrototypeDescriptor(BaseTypeModifier::Vector, getTupleVTM(NF),
|
|
TypeModifier::UnsignedInteger));
|
|
if (TupleT)
|
|
printType(*TupleT);
|
|
if (TupleUT)
|
|
printType(*TupleUT);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (BasicType BT : {BasicType::Float16, BasicType::Float32,
|
|
BasicType::Float64, BasicType::BFloat16}) {
|
|
for (int Log2LMUL : Log2LMULs) {
|
|
auto T = TypeCache.computeType(BT, Log2LMUL, PrototypeDescriptor::Vector);
|
|
if (T)
|
|
printType(*T);
|
|
for (int NF = 2; NF <= 8; ++NF) {
|
|
auto TupleT = TypeCache.computeType(
|
|
BT, Log2LMUL,
|
|
PrototypeDescriptor(BaseTypeModifier::Vector, getTupleVTM(NF),
|
|
(BT == BasicType::BFloat16
|
|
? TypeModifier::BFloat
|
|
: TypeModifier::Float)));
|
|
if (TupleT)
|
|
printType(*TupleT);
|
|
}
|
|
}
|
|
}
|
|
|
|
OS << "\n#ifdef __cplusplus\n";
|
|
OS << "}\n";
|
|
OS << "#endif // __cplusplus\n";
|
|
OS << "#endif // __RISCV_VECTOR_H\n";
|
|
}
|
|
|
|
void RVVEmitter::createBuiltins(raw_ostream &OS) {
|
|
std::vector<std::unique_ptr<RVVIntrinsic>> Defs;
|
|
createRVVIntrinsics(Defs);
|
|
|
|
llvm::StringToOffsetTable Table;
|
|
// Ensure offset zero is the empty string.
|
|
Table.GetOrAddStringOffset("");
|
|
// Hard coded strings used in the builtin structures.
|
|
Table.GetOrAddStringOffset("n");
|
|
Table.GetOrAddStringOffset("zve32x");
|
|
|
|
// Map to unique the builtin names.
|
|
StringMap<RVVIntrinsic *> BuiltinMap;
|
|
std::vector<RVVIntrinsic *> UniqueDefs;
|
|
for (auto &Def : Defs) {
|
|
auto P = BuiltinMap.insert({Def->getBuiltinName(), Def.get()});
|
|
if (P.second) {
|
|
Table.GetOrAddStringOffset(Def->getBuiltinName());
|
|
if (!Def->hasBuiltinAlias())
|
|
Table.GetOrAddStringOffset(Def->getBuiltinTypeStr());
|
|
UniqueDefs.push_back(Def.get());
|
|
continue;
|
|
}
|
|
|
|
// Verf that this would have produced the same builtin definition.
|
|
if (P.first->second->hasBuiltinAlias() != Def->hasBuiltinAlias())
|
|
PrintFatalError("Builtin with same name has different hasAutoDef");
|
|
else if (!Def->hasBuiltinAlias() &&
|
|
P.first->second->getBuiltinTypeStr() != Def->getBuiltinTypeStr())
|
|
PrintFatalError("Builtin with same name has different type string");
|
|
}
|
|
|
|
// Emit the enumerators of RVV builtins. Note that these are emitted without
|
|
// any outer context to enable concatenating them.
|
|
OS << "// RISCV Vector builtin enumerators\n";
|
|
OS << "#ifdef GET_RISCVV_BUILTIN_ENUMERATORS\n";
|
|
for (RVVIntrinsic *Def : UniqueDefs)
|
|
OS << " BI__builtin_rvv_" << Def->getBuiltinName() << ",\n";
|
|
OS << "#endif // GET_RISCVV_BUILTIN_ENUMERATORS\n\n";
|
|
|
|
// Emit the string table for the RVV builtins.
|
|
OS << "// RISCV Vector builtin enumerators\n";
|
|
OS << "#ifdef GET_RISCVV_BUILTIN_STR_TABLE\n";
|
|
Table.EmitStringTableDef(OS, "BuiltinStrings");
|
|
OS << "#endif // GET_RISCVV_BUILTIN_STR_TABLE\n\n";
|
|
|
|
// Emit the info structs of RVV builtins. Note that these are emitted without
|
|
// any outer context to enable concatenating them.
|
|
OS << "// RISCV Vector builtin infos\n";
|
|
OS << "#ifdef GET_RISCVV_BUILTIN_INFOS\n";
|
|
for (RVVIntrinsic *Def : UniqueDefs) {
|
|
OS << " Builtin::Info{Builtin::Info::StrOffsets{"
|
|
<< Table.GetStringOffset(Def->getBuiltinName()) << " /* "
|
|
<< Def->getBuiltinName() << " */, ";
|
|
if (Def->hasBuiltinAlias()) {
|
|
OS << "0, ";
|
|
} else {
|
|
OS << Table.GetStringOffset(Def->getBuiltinTypeStr()) << " /* "
|
|
<< Def->getBuiltinTypeStr() << " */, ";
|
|
}
|
|
OS << Table.GetStringOffset("n") << " /* n */, ";
|
|
OS << Table.GetStringOffset("zve32x") << " /* zve32x */}, ";
|
|
|
|
OS << "HeaderDesc::NO_HEADER, ALL_LANGUAGES},\n";
|
|
}
|
|
OS << "#endif // GET_RISCVV_BUILTIN_INFOS\n\n";
|
|
}
|
|
|
|
void RVVEmitter::createCodeGen(raw_ostream &OS) {
|
|
std::vector<std::unique_ptr<RVVIntrinsic>> Defs;
|
|
createRVVIntrinsics(Defs);
|
|
// IR name could be empty, use the stable sort preserves the relative order.
|
|
stable_sort(Defs, [](const std::unique_ptr<RVVIntrinsic> &A,
|
|
const std::unique_ptr<RVVIntrinsic> &B) {
|
|
if (A->getIRName() == B->getIRName())
|
|
return (A->getPolicyAttrs() < B->getPolicyAttrs());
|
|
return (A->getIRName() < B->getIRName());
|
|
});
|
|
|
|
// Map to keep track of which builtin names have already been emitted.
|
|
StringMap<RVVIntrinsic *> BuiltinMap;
|
|
|
|
// Print switch body when the ir name, ManualCodegen, policy or log2sew
|
|
// changes from previous iteration.
|
|
RVVIntrinsic *PrevDef = Defs.begin()->get();
|
|
for (auto &Def : Defs) {
|
|
StringRef CurIRName = Def->getIRName();
|
|
if (CurIRName != PrevDef->getIRName() ||
|
|
(Def->getManualCodegen() != PrevDef->getManualCodegen()) ||
|
|
(Def->getPolicyAttrs() != PrevDef->getPolicyAttrs()) ||
|
|
(getSegInstLog2SEW(Def->getOverloadedName()) !=
|
|
getSegInstLog2SEW(PrevDef->getOverloadedName()))) {
|
|
emitCodeGenSwitchBody(PrevDef, OS);
|
|
}
|
|
PrevDef = Def.get();
|
|
|
|
auto P =
|
|
BuiltinMap.insert(std::make_pair(Def->getBuiltinName(), Def.get()));
|
|
if (P.second) {
|
|
OS << "case RISCVVector::BI__builtin_rvv_" << Def->getBuiltinName()
|
|
<< ":\n";
|
|
continue;
|
|
}
|
|
|
|
if (P.first->second->getIRName() != Def->getIRName())
|
|
PrintFatalError("Builtin with same name has different IRName");
|
|
else if (P.first->second->getManualCodegen() != Def->getManualCodegen())
|
|
PrintFatalError("Builtin with same name has different ManualCodegen");
|
|
else if (P.first->second->isMasked() != Def->isMasked())
|
|
PrintFatalError("Builtin with same name has different isMasked");
|
|
else if (P.first->second->hasVL() != Def->hasVL())
|
|
PrintFatalError("Builtin with same name has different hasVL");
|
|
else if (P.first->second->getPolicyScheme() != Def->getPolicyScheme())
|
|
PrintFatalError("Builtin with same name has different getPolicyScheme");
|
|
else if (P.first->second->getIntrinsicTypes() != Def->getIntrinsicTypes())
|
|
PrintFatalError("Builtin with same name has different IntrinsicTypes");
|
|
}
|
|
emitCodeGenSwitchBody(Defs.back().get(), OS);
|
|
OS << "\n";
|
|
}
|
|
|
|
void RVVEmitter::createRVVIntrinsics(
|
|
std::vector<std::unique_ptr<RVVIntrinsic>> &Out,
|
|
std::vector<SemaRecord> *SemaRecords) {
|
|
for (const Record *R : Records.getAllDerivedDefinitions("RVVBuiltin")) {
|
|
StringRef Name = R->getValueAsString("Name");
|
|
StringRef SuffixProto = R->getValueAsString("Suffix");
|
|
StringRef OverloadedName = R->getValueAsString("OverloadedName");
|
|
StringRef OverloadedSuffixProto = R->getValueAsString("OverloadedSuffix");
|
|
StringRef Prototypes = R->getValueAsString("Prototype");
|
|
StringRef TypeRange = R->getValueAsString("TypeRange");
|
|
bool HasMasked = R->getValueAsBit("HasMasked");
|
|
bool HasMaskedOffOperand = R->getValueAsBit("HasMaskedOffOperand");
|
|
bool HasVL = R->getValueAsBit("HasVL");
|
|
const Record *MPSRecord = R->getValueAsDef("MaskedPolicyScheme");
|
|
auto MaskedPolicyScheme =
|
|
static_cast<PolicyScheme>(MPSRecord->getValueAsInt("Value"));
|
|
const Record *UMPSRecord = R->getValueAsDef("UnMaskedPolicyScheme");
|
|
auto UnMaskedPolicyScheme =
|
|
static_cast<PolicyScheme>(UMPSRecord->getValueAsInt("Value"));
|
|
std::vector<int64_t> Log2LMULList = R->getValueAsListOfInts("Log2LMUL");
|
|
bool HasTailPolicy = R->getValueAsBit("HasTailPolicy");
|
|
bool HasMaskPolicy = R->getValueAsBit("HasMaskPolicy");
|
|
bool SupportOverloading = R->getValueAsBit("SupportOverloading");
|
|
bool HasBuiltinAlias = R->getValueAsBit("HasBuiltinAlias");
|
|
StringRef ManualCodegen = R->getValueAsString("ManualCodegen");
|
|
std::vector<int64_t> IntrinsicTypes =
|
|
R->getValueAsListOfInts("IntrinsicTypes");
|
|
std::vector<StringRef> RequiredFeatures =
|
|
R->getValueAsListOfStrings("RequiredFeatures");
|
|
StringRef IRName = R->getValueAsString("IRName");
|
|
StringRef MaskedIRName = R->getValueAsString("MaskedIRName");
|
|
unsigned NF = R->getValueAsInt("NF");
|
|
bool IsTuple = R->getValueAsBit("IsTuple");
|
|
bool HasFRMRoundModeOp = R->getValueAsBit("HasFRMRoundModeOp");
|
|
|
|
const Policy DefaultPolicy;
|
|
SmallVector<Policy> SupportedUnMaskedPolicies =
|
|
RVVIntrinsic::getSupportedUnMaskedPolicies();
|
|
SmallVector<Policy> SupportedMaskedPolicies =
|
|
RVVIntrinsic::getSupportedMaskedPolicies(HasTailPolicy, HasMaskPolicy);
|
|
|
|
// Parse prototype and create a list of primitive type with transformers
|
|
// (operand) in Prototype. Prototype[0] is output operand.
|
|
SmallVector<PrototypeDescriptor> BasicPrototype =
|
|
parsePrototypes(Prototypes);
|
|
|
|
SmallVector<PrototypeDescriptor> SuffixDesc = parsePrototypes(SuffixProto);
|
|
SmallVector<PrototypeDescriptor> OverloadedSuffixDesc =
|
|
parsePrototypes(OverloadedSuffixProto);
|
|
|
|
// Compute Builtin types
|
|
auto Prototype = RVVIntrinsic::computeBuiltinTypes(
|
|
BasicPrototype, /*IsMasked=*/false,
|
|
/*HasMaskedOffOperand=*/false, HasVL, NF, UnMaskedPolicyScheme,
|
|
DefaultPolicy, IsTuple);
|
|
SmallVector<PrototypeDescriptor> MaskedPrototype;
|
|
if (HasMasked)
|
|
MaskedPrototype = RVVIntrinsic::computeBuiltinTypes(
|
|
BasicPrototype, /*IsMasked=*/true, HasMaskedOffOperand, HasVL, NF,
|
|
MaskedPolicyScheme, DefaultPolicy, IsTuple);
|
|
|
|
// Create Intrinsics for each type and LMUL.
|
|
for (char I : TypeRange) {
|
|
for (int Log2LMUL : Log2LMULList) {
|
|
BasicType BT = ParseBasicType(I);
|
|
std::optional<RVVTypes> Types =
|
|
TypeCache.computeTypes(BT, Log2LMUL, NF, Prototype);
|
|
// Ignored to create new intrinsic if there are any illegal types.
|
|
if (!Types)
|
|
continue;
|
|
|
|
auto SuffixStr =
|
|
RVVIntrinsic::getSuffixStr(TypeCache, BT, Log2LMUL, SuffixDesc);
|
|
auto OverloadedSuffixStr = RVVIntrinsic::getSuffixStr(
|
|
TypeCache, BT, Log2LMUL, OverloadedSuffixDesc);
|
|
// Create a unmasked intrinsic
|
|
Out.push_back(std::make_unique<RVVIntrinsic>(
|
|
Name, SuffixStr, OverloadedName, OverloadedSuffixStr, IRName,
|
|
/*IsMasked=*/false, /*HasMaskedOffOperand=*/false, HasVL,
|
|
UnMaskedPolicyScheme, SupportOverloading, HasBuiltinAlias,
|
|
ManualCodegen, *Types, IntrinsicTypes, NF, DefaultPolicy,
|
|
HasFRMRoundModeOp));
|
|
if (UnMaskedPolicyScheme != PolicyScheme::SchemeNone)
|
|
for (auto P : SupportedUnMaskedPolicies) {
|
|
SmallVector<PrototypeDescriptor> PolicyPrototype =
|
|
RVVIntrinsic::computeBuiltinTypes(
|
|
BasicPrototype, /*IsMasked=*/false,
|
|
/*HasMaskedOffOperand=*/false, HasVL, NF,
|
|
UnMaskedPolicyScheme, P, IsTuple);
|
|
std::optional<RVVTypes> PolicyTypes =
|
|
TypeCache.computeTypes(BT, Log2LMUL, NF, PolicyPrototype);
|
|
Out.push_back(std::make_unique<RVVIntrinsic>(
|
|
Name, SuffixStr, OverloadedName, OverloadedSuffixStr, IRName,
|
|
/*IsMask=*/false, /*HasMaskedOffOperand=*/false, HasVL,
|
|
UnMaskedPolicyScheme, SupportOverloading, HasBuiltinAlias,
|
|
ManualCodegen, *PolicyTypes, IntrinsicTypes, NF, P,
|
|
HasFRMRoundModeOp));
|
|
}
|
|
if (!HasMasked)
|
|
continue;
|
|
// Create a masked intrinsic
|
|
std::optional<RVVTypes> MaskTypes =
|
|
TypeCache.computeTypes(BT, Log2LMUL, NF, MaskedPrototype);
|
|
Out.push_back(std::make_unique<RVVIntrinsic>(
|
|
Name, SuffixStr, OverloadedName, OverloadedSuffixStr, MaskedIRName,
|
|
/*IsMasked=*/true, HasMaskedOffOperand, HasVL, MaskedPolicyScheme,
|
|
SupportOverloading, HasBuiltinAlias, ManualCodegen, *MaskTypes,
|
|
IntrinsicTypes, NF, DefaultPolicy, HasFRMRoundModeOp));
|
|
if (MaskedPolicyScheme == PolicyScheme::SchemeNone)
|
|
continue;
|
|
for (auto P : SupportedMaskedPolicies) {
|
|
SmallVector<PrototypeDescriptor> PolicyPrototype =
|
|
RVVIntrinsic::computeBuiltinTypes(
|
|
BasicPrototype, /*IsMasked=*/true, HasMaskedOffOperand, HasVL,
|
|
NF, MaskedPolicyScheme, P, IsTuple);
|
|
std::optional<RVVTypes> PolicyTypes =
|
|
TypeCache.computeTypes(BT, Log2LMUL, NF, PolicyPrototype);
|
|
Out.push_back(std::make_unique<RVVIntrinsic>(
|
|
Name, SuffixStr, OverloadedName, OverloadedSuffixStr,
|
|
MaskedIRName, /*IsMasked=*/true, HasMaskedOffOperand, HasVL,
|
|
MaskedPolicyScheme, SupportOverloading, HasBuiltinAlias,
|
|
ManualCodegen, *PolicyTypes, IntrinsicTypes, NF, P,
|
|
HasFRMRoundModeOp));
|
|
}
|
|
} // End for Log2LMULList
|
|
} // End for TypeRange
|
|
|
|
// We don't emit vsetvli and vsetvlimax for SemaRecord.
|
|
// They are written in riscv_vector.td and will emit those marco define in
|
|
// riscv_vector.h
|
|
if (Name == "vsetvli" || Name == "vsetvlimax")
|
|
continue;
|
|
|
|
if (!SemaRecords)
|
|
continue;
|
|
|
|
// Create SemaRecord
|
|
SemaRecord SR;
|
|
SR.Name = Name.str();
|
|
SR.OverloadedName = OverloadedName.str();
|
|
BasicType TypeRangeMask = BasicType::Unknown;
|
|
for (char I : TypeRange)
|
|
TypeRangeMask |= ParseBasicType(I);
|
|
|
|
SR.TypeRangeMask = static_cast<unsigned>(TypeRangeMask);
|
|
|
|
unsigned Log2LMULMask = 0;
|
|
for (int Log2LMUL : Log2LMULList)
|
|
Log2LMULMask |= 1 << (Log2LMUL + 3);
|
|
|
|
SR.Log2LMULMask = Log2LMULMask;
|
|
|
|
memset(SR.RequiredExtensions, 0, sizeof(SR.RequiredExtensions));
|
|
for (auto RequiredFeature : RequiredFeatures) {
|
|
unsigned RequireExt =
|
|
StringSwitch<RVVRequire>(RequiredFeature)
|
|
.Case("RV64", RVV_REQ_RV64)
|
|
.Case("Zvfhmin", RVV_REQ_Zvfhmin)
|
|
.Case("Xsfvcp", RVV_REQ_Xsfvcp)
|
|
.Case("Xsfvfnrclipxfqf", RVV_REQ_Xsfvfnrclipxfqf)
|
|
.Case("Xsfvfwmaccqqq", RVV_REQ_Xsfvfwmaccqqq)
|
|
.Case("Xsfvqmaccdod", RVV_REQ_Xsfvqmaccdod)
|
|
.Case("Xsfvqmaccqoq", RVV_REQ_Xsfvqmaccqoq)
|
|
.Case("Zvbb", RVV_REQ_Zvbb)
|
|
.Case("Zvbc", RVV_REQ_Zvbc)
|
|
.Case("Zvkb", RVV_REQ_Zvkb)
|
|
.Case("Zvkg", RVV_REQ_Zvkg)
|
|
.Case("Zvkned", RVV_REQ_Zvkned)
|
|
.Case("Zvknha", RVV_REQ_Zvknha)
|
|
.Case("Zvknhb", RVV_REQ_Zvknhb)
|
|
.Case("Zvksed", RVV_REQ_Zvksed)
|
|
.Case("Zvksh", RVV_REQ_Zvksh)
|
|
.Case("Zvfbfwma", RVV_REQ_Zvfbfwma)
|
|
.Case("Zvfbfmin", RVV_REQ_Zvfbfmin)
|
|
.Case("Zvfh", RVV_REQ_Zvfh)
|
|
.Case("Experimental", RVV_REQ_Experimental);
|
|
SR.RequiredExtensions[RequireExt / 32] |= 1U << (RequireExt % 32);
|
|
}
|
|
|
|
SR.NF = NF;
|
|
SR.HasMasked = HasMasked;
|
|
SR.HasVL = HasVL;
|
|
SR.HasMaskedOffOperand = HasMaskedOffOperand;
|
|
SR.HasTailPolicy = HasTailPolicy;
|
|
SR.HasMaskPolicy = HasMaskPolicy;
|
|
SR.UnMaskedPolicyScheme = static_cast<uint8_t>(UnMaskedPolicyScheme);
|
|
SR.MaskedPolicyScheme = static_cast<uint8_t>(MaskedPolicyScheme);
|
|
SR.Prototype = std::move(BasicPrototype);
|
|
SR.Suffix = parsePrototypes(SuffixProto);
|
|
SR.OverloadedSuffix = parsePrototypes(OverloadedSuffixProto);
|
|
SR.IsTuple = IsTuple;
|
|
SR.HasFRMRoundModeOp = HasFRMRoundModeOp;
|
|
|
|
SemaRecords->push_back(SR);
|
|
}
|
|
}
|
|
|
|
void RVVEmitter::printHeaderCode(raw_ostream &OS) {
|
|
for (const Record *R : Records.getAllDerivedDefinitions("RVVHeader")) {
|
|
StringRef HeaderCodeStr = R->getValueAsString("HeaderCode");
|
|
OS << HeaderCodeStr.str();
|
|
}
|
|
}
|
|
|
|
void RVVEmitter::createRVVIntrinsicRecords(std::vector<RVVIntrinsicRecord> &Out,
|
|
SemaSignatureTable &SST,
|
|
ArrayRef<SemaRecord> SemaRecords) {
|
|
SST.init(SemaRecords);
|
|
|
|
for (const auto &SR : SemaRecords) {
|
|
Out.emplace_back(RVVIntrinsicRecord());
|
|
RVVIntrinsicRecord &R = Out.back();
|
|
R.Name = SR.Name.c_str();
|
|
R.OverloadedName = SR.OverloadedName.c_str();
|
|
R.PrototypeIndex = SST.getIndex(SR.Prototype);
|
|
R.SuffixIndex = SST.getIndex(SR.Suffix);
|
|
R.OverloadedSuffixIndex = SST.getIndex(SR.OverloadedSuffix);
|
|
R.PrototypeLength = SR.Prototype.size();
|
|
R.SuffixLength = SR.Suffix.size();
|
|
R.OverloadedSuffixSize = SR.OverloadedSuffix.size();
|
|
memcpy(R.RequiredExtensions, SR.RequiredExtensions,
|
|
sizeof(R.RequiredExtensions));
|
|
R.TypeRangeMask = SR.TypeRangeMask;
|
|
R.Log2LMULMask = SR.Log2LMULMask;
|
|
R.NF = SR.NF;
|
|
R.HasMasked = SR.HasMasked;
|
|
R.HasVL = SR.HasVL;
|
|
R.HasMaskedOffOperand = SR.HasMaskedOffOperand;
|
|
R.HasTailPolicy = SR.HasTailPolicy;
|
|
R.HasMaskPolicy = SR.HasMaskPolicy;
|
|
R.UnMaskedPolicyScheme = SR.UnMaskedPolicyScheme;
|
|
R.MaskedPolicyScheme = SR.MaskedPolicyScheme;
|
|
R.IsTuple = SR.IsTuple;
|
|
R.HasFRMRoundModeOp = SR.HasFRMRoundModeOp;
|
|
|
|
assert(R.PrototypeIndex !=
|
|
static_cast<uint16_t>(SemaSignatureTable::INVALID_INDEX));
|
|
assert(R.SuffixIndex !=
|
|
static_cast<uint16_t>(SemaSignatureTable::INVALID_INDEX));
|
|
assert(R.OverloadedSuffixIndex !=
|
|
static_cast<uint16_t>(SemaSignatureTable::INVALID_INDEX));
|
|
}
|
|
}
|
|
|
|
void RVVEmitter::createSema(raw_ostream &OS) {
|
|
std::vector<std::unique_ptr<RVVIntrinsic>> Defs;
|
|
std::vector<RVVIntrinsicRecord> RVVIntrinsicRecords;
|
|
SemaSignatureTable SST;
|
|
std::vector<SemaRecord> SemaRecords;
|
|
|
|
createRVVIntrinsics(Defs, &SemaRecords);
|
|
|
|
createRVVIntrinsicRecords(RVVIntrinsicRecords, SST, SemaRecords);
|
|
|
|
// Emit signature table for SemaRISCVVectorLookup.cpp.
|
|
OS << "#ifdef DECL_SIGNATURE_TABLE\n";
|
|
SST.print(OS);
|
|
OS << "#endif\n";
|
|
|
|
// Emit RVVIntrinsicRecords for SemaRISCVVectorLookup.cpp.
|
|
OS << "#ifdef DECL_INTRINSIC_RECORDS\n";
|
|
for (const RVVIntrinsicRecord &Record : RVVIntrinsicRecords)
|
|
OS << Record;
|
|
OS << "#endif\n";
|
|
}
|
|
|
|
namespace clang {
|
|
void EmitRVVHeader(const RecordKeeper &Records, raw_ostream &OS) {
|
|
RVVEmitter(Records).createHeader(OS);
|
|
}
|
|
|
|
void EmitRVVBuiltins(const RecordKeeper &Records, raw_ostream &OS) {
|
|
RVVEmitter(Records).createBuiltins(OS);
|
|
}
|
|
|
|
void EmitRVVBuiltinCG(const RecordKeeper &Records, raw_ostream &OS) {
|
|
RVVEmitter(Records).createCodeGen(OS);
|
|
}
|
|
|
|
void EmitRVVBuiltinSema(const RecordKeeper &Records, raw_ostream &OS) {
|
|
RVVEmitter(Records).createSema(OS);
|
|
}
|
|
|
|
} // End namespace clang
|