It is currently not possible to use "RVV type" and "RVV intrinsics" if
the "zve32x" is not enabled globally. However in some cases we may want
to use them only in some functions, for instance:
```
#include <riscv_vector.h>
__attribute__((target("+zve32x")))
vint32m1_t rvv_add(vint32m1_t v1, vint32m1_t v2, size_t vl) {
return __riscv_vadd(v1, v2, vl);
}
int other_add(int i1, int i2) {
return i1 + i2;
}
```
, it is supposed to be compilable even the vector is not specified, e.g.
`clang -target riscv64 -march=rv64gc -S test.c`.
785 lines
28 KiB
C++
785 lines
28 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/SmallSet.h"
|
|
#include "llvm/ADT/StringExtras.h"
|
|
#include "llvm/ADT/StringMap.h"
|
|
#include "llvm/ADT/StringSet.h"
|
|
#include "llvm/ADT/StringSwitch.h"
|
|
#include "llvm/ADT/Twine.h"
|
|
#include "llvm/TableGen/Error.h"
|
|
#include "llvm/TableGen/Record.h"
|
|
#include <numeric>
|
|
#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;
|
|
|
|
// 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:
|
|
RecordKeeper &Records;
|
|
RVVTypeCache TypeCache;
|
|
|
|
public:
|
|
RVVEmitter(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));
|
|
}
|
|
|
|
void emitCodeGenSwitchBody(const RVVIntrinsic *RVVI, raw_ostream &OS) {
|
|
if (!RVVI->getIRName().empty())
|
|
OS << " ID = Intrinsic::riscv_" + RVVI->getIRName() + ";\n";
|
|
if (RVVI->getNF() >= 2)
|
|
OS << " NF = " + utostr(RVVI->getNF()) + ";\n";
|
|
|
|
OS << " PolicyAttrs = " << RVVI->getPolicyAttrsBits() << ";\n";
|
|
|
|
if (RVVI->hasManualCodegen()) {
|
|
OS << "IsMasked = " << (RVVI->isMasked() ? "true" : "false") << ";\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 << "#define __riscv_v_intrinsic_overloading 1\n";
|
|
|
|
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);
|
|
|
|
// Map to keep track of which builtin names have already been emitted.
|
|
StringMap<RVVIntrinsic *> BuiltinMap;
|
|
|
|
OS << "#if defined(TARGET_BUILTIN) && !defined(RISCVV_BUILTIN)\n";
|
|
OS << "#define RISCVV_BUILTIN(ID, TYPE, ATTRS) TARGET_BUILTIN(ID, TYPE, "
|
|
"ATTRS, \"zve32x\")\n";
|
|
OS << "#endif\n";
|
|
for (auto &Def : Defs) {
|
|
auto P =
|
|
BuiltinMap.insert(std::make_pair(Def->getBuiltinName(), Def.get()));
|
|
if (!P.second) {
|
|
// 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");
|
|
continue;
|
|
}
|
|
OS << "RISCVV_BUILTIN(__builtin_rvv_" << Def->getBuiltinName() << ",\"";
|
|
if (!Def->hasBuiltinAlias())
|
|
OS << Def->getBuiltinTypeStr();
|
|
OS << "\", \"n\")\n";
|
|
}
|
|
OS << "#undef RISCVV_BUILTIN\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.
|
|
llvm::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 or policy 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())) {
|
|
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) {
|
|
std::vector<Record *> RV = Records.getAllDerivedDefinitions("RVVBuiltin");
|
|
for (auto *R : RV) {
|
|
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");
|
|
Record *MPSRecord = R->getValueAsDef("MaskedPolicyScheme");
|
|
auto MaskedPolicyScheme =
|
|
static_cast<PolicyScheme>(MPSRecord->getValueAsInt("Value"));
|
|
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);
|
|
llvm::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, RequiredFeatures, 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, RequiredFeatures,
|
|
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, RequiredFeatures, 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, RequiredFeatures, 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;
|
|
|
|
SR.RequiredExtensions = 0;
|
|
for (auto RequiredFeature : RequiredFeatures) {
|
|
RVVRequire 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("Experimental", RVV_REQ_Experimental)
|
|
.Default(RVV_REQ_None);
|
|
assert(RequireExt != RVV_REQ_None && "Unrecognized required feature?");
|
|
SR.RequiredExtensions |= RequireExt;
|
|
}
|
|
|
|
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) {
|
|
std::vector<Record *> RVVHeaders =
|
|
Records.getAllDerivedDefinitions("RVVHeader");
|
|
for (auto *R : RVVHeaders) {
|
|
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();
|
|
R.RequiredExtensions = SR.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(RecordKeeper &Records, raw_ostream &OS) {
|
|
RVVEmitter(Records).createHeader(OS);
|
|
}
|
|
|
|
void EmitRVVBuiltins(RecordKeeper &Records, raw_ostream &OS) {
|
|
RVVEmitter(Records).createBuiltins(OS);
|
|
}
|
|
|
|
void EmitRVVBuiltinCG(RecordKeeper &Records, raw_ostream &OS) {
|
|
RVVEmitter(Records).createCodeGen(OS);
|
|
}
|
|
|
|
void EmitRVVBuiltinSema(RecordKeeper &Records, raw_ostream &OS) {
|
|
RVVEmitter(Records).createSema(OS);
|
|
}
|
|
|
|
} // End namespace clang
|