blob: 48cd83cabfc7d032bb207243fabe8ee2bfeef513 [file] [log] [blame]
//===- 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("Zvfbfmin", RVV_REQ_Zvfbfmin)
.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