diff options
author | Kito Cheng <kito.cheng@sifive.com> | 2022-07-13 15:52:17 +0800 |
---|---|---|
committer | Kito Cheng <kito.cheng@sifive.com> | 2022-07-26 15:47:47 +0800 |
commit | 7a5cb15ea6facd82756adafae76d60f36a0b60fd (patch) | |
tree | da7a2edf49c6964a3d4e48ed34fcb1ccf939304e /clang/utils | |
parent | 2f9fa9ef5387de3d87b0c866c678d93695c1c1f3 (diff) | |
download | llvm-7a5cb15ea6facd82756adafae76d60f36a0b60fd.zip llvm-7a5cb15ea6facd82756adafae76d60f36a0b60fd.tar.gz llvm-7a5cb15ea6facd82756adafae76d60f36a0b60fd.tar.bz2 |
[RISCV] Lazily add RVV C intrinsics.
Leverage the method OpenCL uses that adds C intrinsics when the lookup
failed. There is no need to define C intrinsics in the header file any
more. It could help to avoid the large header file to speed up the
compilation of RVV source code. Besides that, only the C intrinsics used
by the users will be added into the declaration table.
This patch is based on https://reviews.llvm.org/D103228 and inspired by
OpenCL implementation.
### Experimental Results
#### TL;DR:
- Binary size of clang increase ~200k, which is +0.07% for debug build and +0.13% for release build.
- Single file compilation speed up ~33x for debug build and ~8.5x for release build
- Regression time reduce ~10% (`ninja check-all`, enable all targets)
#### Header size change
```
| size | LoC |
------------------------------
Before | 4,434,725 | 69,749 |
After | 6,140 | 162 |
```
#### Single File Compilation Time
Testcase:
```
#include <riscv_vector.h>
vint32m1_t test_vadd_vv_vfloat32m1_t(vint32m1_t op1, vint32m1_t op2, size_t vl) {
return vadd(op1, op2, vl);
}
```
##### Debug build:
Before:
```
real 0m19.352s
user 0m19.252s
sys 0m0.092s
```
After:
```
real 0m0.576s
user 0m0.552s
sys 0m0.024s
```
~33x speed up for debug build
##### Release build:
Before:
```
real 0m0.773s
user 0m0.741s
sys 0m0.032s
```
After:
```
real 0m0.092s
user 0m0.080s
sys 0m0.012s
```
~8.5x speed up for release build
#### Regression time
Note: the failed case is `tools/llvm-debuginfod-find/debuginfod.test` which is unrelated to this patch.
##### Debug build
Before:
```
Testing Time: 1358.38s
Skipped : 11
Unsupported : 446
Passed : 75767
Expectedly Failed: 190
Failed : 1
```
After
```
Testing Time: 1220.29s
Skipped : 11
Unsupported : 446
Passed : 75767
Expectedly Failed: 190
Failed : 1
```
##### Release build
Before:
```
Testing Time: 381.98s
Skipped : 12
Unsupported : 1407
Passed : 74765
Expectedly Failed: 176
Failed : 1
```
After:
```
Testing Time: 346.25s
Skipped : 12
Unsupported : 1407
Passed : 74765
Expectedly Failed: 176
Failed : 1
```
#### Binary size of clang
##### Debug build
Before
```
text data bss dec hex filename
335261851 12726004 552812 348540667 14c64efb bin/clang
```
After
```
text data bss dec hex filename
335442803 12798708 552940 348794451 14ca2e53 bin/clang
```
+253K, +0.07% code size
##### Release build
Before
```
text data bss dec hex filename
144123975 8374648 483140 152981763 91e5103 bin/clang
```
After
```
text data bss dec hex filename
144255762 8447296 483268 153186326 9217016 bin/clang
```
+204K, +0.13%
Authored-by: Kito Cheng <kito.cheng@sifive.com>
Co-Authored-by: Hsiangkai Wang <kai.wang@sifive.com>
Reviewed By: khchen, aaron.ballman
Differential Revision: https://reviews.llvm.org/D111617
Diffstat (limited to 'clang/utils')
-rw-r--r-- | clang/utils/TableGen/RISCVVEmitter.cpp | 359 | ||||
-rw-r--r-- | clang/utils/TableGen/TableGen.cpp | 6 | ||||
-rw-r--r-- | clang/utils/TableGen/TableGenBackends.h | 1 |
3 files changed, 256 insertions, 110 deletions
diff --git a/clang/utils/TableGen/RISCVVEmitter.cpp b/clang/utils/TableGen/RISCVVEmitter.cpp index db4cd77..91ee624 100644 --- a/clang/utils/TableGen/RISCVVEmitter.cpp +++ b/clang/utils/TableGen/RISCVVEmitter.cpp @@ -20,6 +20,7 @@ #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" @@ -29,6 +30,59 @@ 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. + unsigned RequiredExtensions; + + // Prototype for this intrinsic. + SmallVector<PrototypeDescriptor> Prototype; + + // Prototype for masked intrinsic. + SmallVector<PrototypeDescriptor> MaskedPrototype; + + // 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; +}; + +// 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; @@ -45,22 +99,22 @@ public: /// 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 - void createRVVIntrinsics(std::vector<std::unique_ptr<RVVIntrinsic>> &Out); + /// 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); - - /// 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 @@ -151,33 +205,83 @@ void emitCodeGenSwitchBody(const RVVIntrinsic *RVVI, raw_ostream &OS) { 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"; +//===----------------------------------------------------------------------===// +// 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()); + + llvm::for_each(SemaRecords, [&](const SemaRecord &SR) { + InsertToSignatureSet(SR.Prototype); + InsertToSignatureSet(SR.MaskedPrototype); + InsertToSignatureSet(SR.Suffix); + InsertToSignatureSet(SR.OverloadedSuffix); + }); + + llvm::for_each(Signatures, [this](auto &Sig) { insert(Sig); }); } -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(); +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; + } } - OS << ");\n"; + + 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"; } //===----------------------------------------------------------------------===// @@ -212,10 +316,9 @@ void RVVEmitter::createHeader(raw_ostream &OS) { OS << "extern \"C\" {\n"; OS << "#endif\n\n"; - printHeaderCode(OS); + OS << "#pragma clang riscv intrinsic vector\n\n"; - std::vector<std::unique_ptr<RVVIntrinsic>> Defs; - createRVVIntrinsics(Defs); + printHeaderCode(OS); auto printType = [&](auto T) { OS << "typedef " << T->getClangBuiltinStr() << " " << T->getTypeStr() @@ -255,7 +358,7 @@ void RVVEmitter::createHeader(raw_ostream &OS) { } OS << "#endif\n"; - OS << "#if defined(__riscv_f)\n"; + OS << "#if (__riscv_v_elen_fp >= 32)\n"; for (int Log2LMUL : Log2LMULs) { auto T = RVVType::computeType(BasicType::Float32, Log2LMUL, PrototypeDescriptor::Vector); @@ -264,7 +367,7 @@ void RVVEmitter::createHeader(raw_ostream &OS) { } OS << "#endif\n"; - OS << "#if defined(__riscv_d)\n"; + OS << "#if (__riscv_v_elen_fp >= 64)\n"; for (int Log2LMUL : Log2LMULs) { auto T = RVVType::computeType(BasicType::Float64, Log2LMUL, PrototypeDescriptor::Vector); @@ -273,37 +376,8 @@ void RVVEmitter::createHeader(raw_ostream &OS) { } 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"; @@ -392,7 +466,8 @@ void RVVEmitter::createCodeGen(raw_ostream &OS) { } void RVVEmitter::createRVVIntrinsics( - std::vector<std::unique_ptr<RVVIntrinsic>> &Out) { + 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"); @@ -502,6 +577,53 @@ void RVVEmitter::createRVVIntrinsics( } } // 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("FullMultiply", RVV_REQ_FullMultiply) + .Default(RVV_REQ_None); + assert(RequireExt != RVV_REQ_None && "Unrecognized required feature?"); + SR.RequiredExtensions |= RequireExt; + } + + SR.NF = NF; + + SR.Prototype = std::move(Prototype); + + if (HasMasked) + SR.MaskedPrototype = std::move(MaskedPrototype); + + SR.Suffix = parsePrototypes(SuffixProto); + SR.OverloadedSuffix = parsePrototypes(OverloadedSuffixProto); + + SemaRecords->push_back(SR); } } @@ -514,47 +636,60 @@ void RVVEmitter::printHeaderCode(raw_ostream &OS) { } } -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); +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.MaskedPrototypeIndex = SST.getIndex(SR.MaskedPrototype); + R.SuffixIndex = SST.getIndex(SR.Suffix); + R.OverloadedSuffixIndex = SST.getIndex(SR.OverloadedSuffix); + R.PrototypeLength = SR.Prototype.size(); + R.MaskedPrototypeLength = SR.MaskedPrototype.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; + + assert(R.PrototypeIndex != + static_cast<uint16_t>(SemaSignatureTable::INVALID_INDEX)); + assert(R.MaskedPrototypeIndex != + 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)); } - 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; +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 { @@ -570,4 +705,8 @@ 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 diff --git a/clang/utils/TableGen/TableGen.cpp b/clang/utils/TableGen/TableGen.cpp index bb9366e..d18a312 100644 --- a/clang/utils/TableGen/TableGen.cpp +++ b/clang/utils/TableGen/TableGen.cpp @@ -88,6 +88,7 @@ enum ActionType { GenRISCVVectorHeader, GenRISCVVectorBuiltins, GenRISCVVectorBuiltinCG, + GenRISCVVectorBuiltinSema, GenAttrDocs, GenDiagDocs, GenOptDocs, @@ -243,6 +244,8 @@ cl::opt<ActionType> Action( "Generate riscv_vector_builtins.inc for clang"), clEnumValN(GenRISCVVectorBuiltinCG, "gen-riscv-vector-builtin-codegen", "Generate riscv_vector_builtin_cg.inc for clang"), + clEnumValN(GenRISCVVectorBuiltinSema, "gen-riscv-vector-builtin-sema", + "Generate riscv_vector_builtin_sema.inc for clang"), clEnumValN(GenAttrDocs, "gen-attr-docs", "Generate attribute documentation"), clEnumValN(GenDiagDocs, "gen-diag-docs", @@ -458,6 +461,9 @@ bool ClangTableGenMain(raw_ostream &OS, RecordKeeper &Records) { case GenRISCVVectorBuiltinCG: EmitRVVBuiltinCG(Records, OS); break; + case GenRISCVVectorBuiltinSema: + EmitRVVBuiltinSema(Records, OS); + break; case GenAttrDocs: EmitClangAttrDocs(Records, OS); break; diff --git a/clang/utils/TableGen/TableGenBackends.h b/clang/utils/TableGen/TableGenBackends.h index fd8b9fc..2ba857f 100644 --- a/clang/utils/TableGen/TableGenBackends.h +++ b/clang/utils/TableGen/TableGenBackends.h @@ -110,6 +110,7 @@ void EmitMveBuiltinAliases(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); void EmitRVVHeader(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); void EmitRVVBuiltins(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); void EmitRVVBuiltinCG(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); +void EmitRVVBuiltinSema(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); void EmitCdeHeader(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); void EmitCdeBuiltinDef(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); |