| //===- 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/Twine.h" |
| #include "llvm/TableGen/Error.h" |
| #include "llvm/TableGen/Record.h" |
| #include <numeric> |
| |
| using namespace llvm; |
| using namespace clang::RISCV; |
| |
| namespace { |
| class RVVEmitter { |
| private: |
| RecordKeeper &Records; |
| |
| 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); |
| |
| private: |
| /// Create all intrinsics and add them to \p Out |
| void createRVVIntrinsics(std::vector<std::unique_ptr<RVVIntrinsic>> &Out); |
| /// Print HeaderCode in RVVHeader Record to \p Out |
| void printHeaderCode(raw_ostream &OS); |
| |
| /// Emit Acrh predecessor definitions and body, assume the element of Defs are |
| /// sorted by extension. |
| void emitArchMacroAndBody( |
| std::vector<std::unique_ptr<RVVIntrinsic>> &Defs, raw_ostream &o, |
| std::function<void(raw_ostream &, const RVVIntrinsic &)>); |
| |
| // Emit the architecture preprocessor definitions. Return true when emits |
| // non-empty string. |
| bool emitMacroRestrictionStr(RISCVPredefinedMacroT PredefinedMacros, |
| raw_ostream &o); |
| }; |
| |
| } // 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; |
| |
| default: |
| return BasicType::Unknown; |
| } |
| } |
| |
| 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"; |
| if (RVVI->hasManualCodegen()) { |
| OS << RVVI->getManualCodegen(); |
| OS << "break;\n"; |
| return; |
| } |
| |
| // Cast pointer operand of vector load intrinsic. |
| for (const auto &I : enumerate(RVVI->getInputTypes())) { |
| if (I.value()->isPointer()) { |
| assert(RVVI->getIntrinsicTypes().front() == -1 && |
| "RVVI should be vector load intrinsic."); |
| OS << " Ops[" << I.index() << "] = Builder.CreateBitCast(Ops["; |
| OS << I.index() << "], ResultType->getPointerTo());\n"; |
| } |
| } |
| |
| 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()," |
| " TAIL_UNDISTURBED));\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(), " |
| "TAIL_UNDISTURBED));\n"; |
| else if (RVVI->hasPassthruOperand()) { |
| OS << " Ops.push_back(llvm::UndefValue::get(ResultType));\n"; |
| OS << " std::rotate(Ops.rbegin(), Ops.rbegin() + 1, Ops.rend());\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"; |
| } |
| |
| void emitIntrinsicFuncDef(const RVVIntrinsic &RVVI, raw_ostream &OS) { |
| OS << "__attribute__((__clang_builtin_alias__("; |
| OS << "__builtin_rvv_" << RVVI.getBuiltinName() << ")))\n"; |
| OS << RVVI.getOutputType()->getTypeStr() << " " << RVVI.getName() << "("; |
| // Emit function arguments |
| const RVVTypes &InputTypes = RVVI.getInputTypes(); |
| if (!InputTypes.empty()) { |
| ListSeparator LS; |
| for (unsigned i = 0; i < InputTypes.size(); ++i) |
| OS << LS << InputTypes[i]->getTypeStr(); |
| } |
| OS << ");\n"; |
| } |
| |
| void emitOverloadedFuncDef(const RVVIntrinsic &RVVI, raw_ostream &OS) { |
| OS << "__attribute__((__clang_builtin_alias__("; |
| OS << "__builtin_rvv_" << RVVI.getBuiltinName() << ")))\n"; |
| OS << RVVI.getOutputType()->getTypeStr() << " " << RVVI.getOverloadedName() |
| << "("; |
| // Emit function arguments |
| const RVVTypes &InputTypes = RVVI.getInputTypes(); |
| if (!InputTypes.empty()) { |
| ListSeparator LS; |
| for (unsigned i = 0; i < InputTypes.size(); ++i) |
| OS << LS << InputTypes[i]->getTypeStr(); |
| } |
| OS << ");\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 << "#ifndef __riscv_vector\n"; |
| OS << "#error \"Vector intrinsics require the vector extension.\"\n"; |
| OS << "#endif\n\n"; |
| |
| OS << "#ifdef __cplusplus\n"; |
| OS << "extern \"C\" {\n"; |
| OS << "#endif\n\n"; |
| |
| printHeaderCode(OS); |
| |
| std::vector<std::unique_ptr<RVVIntrinsic>> Defs; |
| createRVVIntrinsics(Defs); |
| |
| 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 = RVVType::computeType(BasicType::Int8, Log2LMUL, |
| PrototypeDescriptor::Mask); |
| if (T) |
| printType(T.value()); |
| } |
| // Print RVV int/float types. |
| for (char I : StringRef("csil")) { |
| BasicType BT = ParseBasicType(I); |
| for (int Log2LMUL : Log2LMULs) { |
| auto T = RVVType::computeType(BT, Log2LMUL, PrototypeDescriptor::Vector); |
| if (T) { |
| printType(T.value()); |
| auto UT = RVVType::computeType( |
| BT, Log2LMUL, |
| PrototypeDescriptor(BaseTypeModifier::Vector, |
| VectorTypeModifier::NoModifier, |
| TypeModifier::UnsignedInteger)); |
| printType(UT.value()); |
| } |
| } |
| } |
| OS << "#if defined(__riscv_zvfh)\n"; |
| for (int Log2LMUL : Log2LMULs) { |
| auto T = RVVType::computeType(BasicType::Float16, Log2LMUL, |
| PrototypeDescriptor::Vector); |
| if (T) |
| printType(T.value()); |
| } |
| OS << "#endif\n"; |
| |
| OS << "#if defined(__riscv_f)\n"; |
| for (int Log2LMUL : Log2LMULs) { |
| auto T = RVVType::computeType(BasicType::Float32, Log2LMUL, |
| PrototypeDescriptor::Vector); |
| if (T) |
| printType(T.value()); |
| } |
| OS << "#endif\n"; |
| |
| OS << "#if defined(__riscv_d)\n"; |
| for (int Log2LMUL : Log2LMULs) { |
| auto T = RVVType::computeType(BasicType::Float64, Log2LMUL, |
| PrototypeDescriptor::Vector); |
| if (T) |
| printType(T.value()); |
| } |
| OS << "#endif\n\n"; |
| |
| // The same extension include in the same arch guard marco. |
| llvm::stable_sort(Defs, [](const std::unique_ptr<RVVIntrinsic> &A, |
| const std::unique_ptr<RVVIntrinsic> &B) { |
| return A->getRISCVPredefinedMacros() < B->getRISCVPredefinedMacros(); |
| }); |
| |
| OS << "#define __rvv_ai static __inline__\n"; |
| |
| // Print intrinsic functions with macro |
| emitArchMacroAndBody(Defs, OS, [](raw_ostream &OS, const RVVIntrinsic &Inst) { |
| OS << "__rvv_ai "; |
| emitIntrinsicFuncDef(Inst, OS); |
| }); |
| |
| OS << "#undef __rvv_ai\n\n"; |
| |
| OS << "#define __riscv_v_intrinsic_overloading 1\n"; |
| |
| // Print Overloaded APIs |
| OS << "#define __rvv_aio static __inline__ " |
| "__attribute__((__overloadable__))\n"; |
| |
| emitArchMacroAndBody(Defs, OS, [](raw_ostream &OS, const RVVIntrinsic &Inst) { |
| if (!Inst.isMasked() && !Inst.hasUnMaskedOverloaded()) |
| return; |
| OS << "__rvv_aio "; |
| emitOverloadedFuncDef(Inst, OS); |
| }); |
| |
| OS << "#undef __rvv_aio\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) { |
| 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 or ManualCodegen 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())) { |
| 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->getNF() != Def->getNF()) |
| PrintFatalError("Builtin with same name has different NF"); |
| 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<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 *MaskedPolicyRecord = R->getValueAsDef("MaskedPolicy"); |
| PolicyScheme MaskedPolicy = |
| static_cast<PolicyScheme>(MaskedPolicyRecord->getValueAsInt("Value")); |
| Record *UnMaskedPolicyRecord = R->getValueAsDef("UnMaskedPolicy"); |
| PolicyScheme UnMaskedPolicy = |
| static_cast<PolicyScheme>(UnMaskedPolicyRecord->getValueAsInt("Value")); |
| bool HasUnMaskedOverloaded = R->getValueAsBit("HasUnMaskedOverloaded"); |
| std::vector<int64_t> Log2LMULList = R->getValueAsListOfInts("Log2LMUL"); |
| bool HasBuiltinAlias = R->getValueAsBit("HasBuiltinAlias"); |
| StringRef ManualCodegen = R->getValueAsString("ManualCodegen"); |
| StringRef MaskedManualCodegen = R->getValueAsString("MaskedManualCodegen"); |
| 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"); |
| |
| // Parse prototype and create a list of primitive type with transformers |
| // (operand) in Prototype. Prototype[0] is output operand. |
| SmallVector<PrototypeDescriptor> Prototype = parsePrototypes(Prototypes); |
| |
| SmallVector<PrototypeDescriptor> SuffixDesc = parsePrototypes(SuffixProto); |
| SmallVector<PrototypeDescriptor> OverloadedSuffixDesc = |
| parsePrototypes(OverloadedSuffixProto); |
| |
| // Compute Builtin types |
| SmallVector<PrototypeDescriptor> MaskedPrototype = Prototype; |
| if (HasMasked) { |
| // If HasMaskedOffOperand, insert result type as first input operand. |
| if (HasMaskedOffOperand) { |
| if (NF == 1) { |
| MaskedPrototype.insert(MaskedPrototype.begin() + 1, Prototype[0]); |
| } else { |
| // Convert |
| // (void, op0 address, op1 address, ...) |
| // to |
| // (void, op0 address, op1 address, ..., maskedoff0, maskedoff1, ...) |
| PrototypeDescriptor MaskoffType = Prototype[1]; |
| MaskoffType.TM &= ~static_cast<uint8_t>(TypeModifier::Pointer); |
| for (unsigned I = 0; I < NF; ++I) |
| MaskedPrototype.insert(MaskedPrototype.begin() + NF + 1, |
| MaskoffType); |
| } |
| } |
| if (HasMaskedOffOperand && NF > 1) { |
| // Convert |
| // (void, op0 address, op1 address, ..., maskedoff0, maskedoff1, ...) |
| // to |
| // (void, op0 address, op1 address, ..., mask, maskedoff0, maskedoff1, |
| // ...) |
| MaskedPrototype.insert(MaskedPrototype.begin() + NF + 1, |
| PrototypeDescriptor::Mask); |
| } else { |
| // If HasMasked, insert PrototypeDescriptor:Mask as first input operand. |
| MaskedPrototype.insert(MaskedPrototype.begin() + 1, |
| PrototypeDescriptor::Mask); |
| } |
| } |
| // If HasVL, append PrototypeDescriptor:VL to last operand |
| if (HasVL) { |
| Prototype.push_back(PrototypeDescriptor::VL); |
| MaskedPrototype.push_back(PrototypeDescriptor::VL); |
| } |
| |
| // Create Intrinsics for each type and LMUL. |
| for (char I : TypeRange) { |
| for (int Log2LMUL : Log2LMULList) { |
| BasicType BT = ParseBasicType(I); |
| Optional<RVVTypes> Types = |
| RVVType::computeTypes(BT, Log2LMUL, NF, Prototype); |
| // Ignored to create new intrinsic if there are any illegal types. |
| if (!Types) |
| continue; |
| |
| auto SuffixStr = RVVIntrinsic::getSuffixStr(BT, Log2LMUL, SuffixDesc); |
| auto OverloadedSuffixStr = |
| RVVIntrinsic::getSuffixStr(BT, Log2LMUL, OverloadedSuffixDesc); |
| // Create a unmasked intrinsic |
| Out.push_back(std::make_unique<RVVIntrinsic>( |
| Name, SuffixStr, OverloadedName, OverloadedSuffixStr, IRName, |
| /*IsMasked=*/false, /*HasMaskedOffOperand=*/false, HasVL, |
| UnMaskedPolicy, HasUnMaskedOverloaded, HasBuiltinAlias, |
| ManualCodegen, *Types, IntrinsicTypes, RequiredFeatures, NF)); |
| if (HasMasked) { |
| // Create a masked intrinsic |
| Optional<RVVTypes> MaskTypes = |
| RVVType::computeTypes(BT, Log2LMUL, NF, MaskedPrototype); |
| Out.push_back(std::make_unique<RVVIntrinsic>( |
| Name, SuffixStr, OverloadedName, OverloadedSuffixStr, |
| MaskedIRName, |
| /*IsMasked=*/true, HasMaskedOffOperand, HasVL, MaskedPolicy, |
| HasUnMaskedOverloaded, HasBuiltinAlias, MaskedManualCodegen, |
| *MaskTypes, IntrinsicTypes, RequiredFeatures, NF)); |
| } |
| } // end for Log2LMULList |
| } // end for TypeRange |
| } |
| } |
| |
| 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::emitArchMacroAndBody( |
| std::vector<std::unique_ptr<RVVIntrinsic>> &Defs, raw_ostream &OS, |
| std::function<void(raw_ostream &, const RVVIntrinsic &)> PrintBody) { |
| RISCVPredefinedMacroT PrevMacros = |
| (*Defs.begin())->getRISCVPredefinedMacros(); |
| bool NeedEndif = emitMacroRestrictionStr(PrevMacros, OS); |
| for (auto &Def : Defs) { |
| RISCVPredefinedMacroT CurMacros = Def->getRISCVPredefinedMacros(); |
| if (CurMacros != PrevMacros) { |
| if (NeedEndif) |
| OS << "#endif\n\n"; |
| NeedEndif = emitMacroRestrictionStr(CurMacros, OS); |
| PrevMacros = CurMacros; |
| } |
| if (Def->hasBuiltinAlias()) |
| PrintBody(OS, *Def); |
| } |
| if (NeedEndif) |
| OS << "#endif\n\n"; |
| } |
| |
| bool RVVEmitter::emitMacroRestrictionStr(RISCVPredefinedMacroT PredefinedMacros, |
| raw_ostream &OS) { |
| if (PredefinedMacros == RISCVPredefinedMacro::Basic) |
| return false; |
| OS << "#if "; |
| ListSeparator LS(" && "); |
| if (PredefinedMacros & RISCVPredefinedMacro::V) |
| OS << LS << "defined(__riscv_v)"; |
| if (PredefinedMacros & RISCVPredefinedMacro::Zvfh) |
| OS << LS << "defined(__riscv_zvfh)"; |
| if (PredefinedMacros & RISCVPredefinedMacro::RV64) |
| OS << LS << "(__riscv_xlen == 64)"; |
| if (PredefinedMacros & RISCVPredefinedMacro::VectorMaxELen64) |
| OS << LS << "(__riscv_v_elen >= 64)"; |
| if (PredefinedMacros & RISCVPredefinedMacro::VectorMaxELenFp32) |
| OS << LS << "(__riscv_v_elen_fp >= 32)"; |
| if (PredefinedMacros & RISCVPredefinedMacro::VectorMaxELenFp64) |
| OS << LS << "(__riscv_v_elen_fp >= 64)"; |
| OS << "\n"; |
| return true; |
| } |
| |
| 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); |
| } |
| |
| } // End namespace clang |