1 //===- RISCVVEmitter.cpp - Generate riscv_vector.h for use with clang -----===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This tablegen backend is responsible for emitting riscv_vector.h which 10 // includes a declaration and definition of each intrinsic functions specified 11 // in https://github.com/riscv/rvv-intrinsic-doc. 12 // 13 // See also the documentation in include/clang/Basic/riscv_vector.td. 14 // 15 //===----------------------------------------------------------------------===// 16 17 #include "clang/Support/RISCVVIntrinsicUtils.h" 18 #include "llvm/ADT/ArrayRef.h" 19 #include "llvm/ADT/SmallSet.h" 20 #include "llvm/ADT/StringExtras.h" 21 #include "llvm/ADT/StringMap.h" 22 #include "llvm/ADT/StringSet.h" 23 #include "llvm/ADT/Twine.h" 24 #include "llvm/TableGen/Error.h" 25 #include "llvm/TableGen/Record.h" 26 #include <numeric> 27 28 using namespace llvm; 29 using namespace clang::RISCV; 30 31 namespace { 32 class RVVEmitter { 33 private: 34 RecordKeeper &Records; 35 36 public: 37 RVVEmitter(RecordKeeper &R) : Records(R) {} 38 39 /// Emit riscv_vector.h 40 void createHeader(raw_ostream &o); 41 42 /// Emit all the __builtin prototypes and code needed by Sema. 43 void createBuiltins(raw_ostream &o); 44 45 /// Emit all the information needed to map builtin -> LLVM IR intrinsic. 46 void createCodeGen(raw_ostream &o); 47 48 private: 49 /// Create all intrinsics and add them to \p Out 50 void createRVVIntrinsics(std::vector<std::unique_ptr<RVVIntrinsic>> &Out); 51 /// Print HeaderCode in RVVHeader Record to \p Out 52 void printHeaderCode(raw_ostream &OS); 53 54 /// Emit Acrh predecessor definitions and body, assume the element of Defs are 55 /// sorted by extension. 56 void emitArchMacroAndBody( 57 std::vector<std::unique_ptr<RVVIntrinsic>> &Defs, raw_ostream &o, 58 std::function<void(raw_ostream &, const RVVIntrinsic &)>); 59 60 // Emit the architecture preprocessor definitions. Return true when emits 61 // non-empty string. 62 bool emitMacroRestrictionStr(RISCVPredefinedMacroT PredefinedMacros, 63 raw_ostream &o); 64 }; 65 66 } // namespace 67 68 static BasicType ParseBasicType(char c) { 69 switch (c) { 70 case 'c': 71 return BasicType::Int8; 72 break; 73 case 's': 74 return BasicType::Int16; 75 break; 76 case 'i': 77 return BasicType::Int32; 78 break; 79 case 'l': 80 return BasicType::Int64; 81 break; 82 case 'x': 83 return BasicType::Float16; 84 break; 85 case 'f': 86 return BasicType::Float32; 87 break; 88 case 'd': 89 return BasicType::Float64; 90 break; 91 92 default: 93 return BasicType::Unknown; 94 } 95 } 96 97 void emitCodeGenSwitchBody(const RVVIntrinsic *RVVI, raw_ostream &OS) { 98 if (!RVVI->getIRName().empty()) 99 OS << " ID = Intrinsic::riscv_" + RVVI->getIRName() + ";\n"; 100 if (RVVI->getNF() >= 2) 101 OS << " NF = " + utostr(RVVI->getNF()) + ";\n"; 102 if (RVVI->hasManualCodegen()) { 103 OS << RVVI->getManualCodegen(); 104 OS << "break;\n"; 105 return; 106 } 107 108 if (RVVI->isMasked()) { 109 if (RVVI->hasVL()) { 110 OS << " std::rotate(Ops.begin(), Ops.begin() + 1, Ops.end() - 1);\n"; 111 if (RVVI->hasPolicyOperand()) 112 OS << " Ops.push_back(ConstantInt::get(Ops.back()->getType()," 113 " TAIL_UNDISTURBED));\n"; 114 } else { 115 OS << " std::rotate(Ops.begin(), Ops.begin() + 1, Ops.end());\n"; 116 } 117 } else { 118 if (RVVI->hasPolicyOperand()) 119 OS << " Ops.push_back(ConstantInt::get(Ops.back()->getType(), " 120 "TAIL_UNDISTURBED));\n"; 121 else if (RVVI->hasPassthruOperand()) { 122 OS << " Ops.push_back(llvm::UndefValue::get(ResultType));\n"; 123 OS << " std::rotate(Ops.rbegin(), Ops.rbegin() + 1, Ops.rend());\n"; 124 } 125 } 126 127 OS << " IntrinsicTypes = {"; 128 ListSeparator LS; 129 for (const auto &Idx : RVVI->getIntrinsicTypes()) { 130 if (Idx == -1) 131 OS << LS << "ResultType"; 132 else 133 OS << LS << "Ops[" << Idx << "]->getType()"; 134 } 135 136 // VL could be i64 or i32, need to encode it in IntrinsicTypes. VL is 137 // always last operand. 138 if (RVVI->hasVL()) 139 OS << ", Ops.back()->getType()"; 140 OS << "};\n"; 141 OS << " break;\n"; 142 } 143 144 void emitIntrinsicFuncDef(const RVVIntrinsic &RVVI, raw_ostream &OS) { 145 OS << "__attribute__((__clang_builtin_alias__("; 146 OS << "__builtin_rvv_" << RVVI.getBuiltinName() << ")))\n"; 147 OS << RVVI.getOutputType()->getTypeStr() << " " << RVVI.getName() << "("; 148 // Emit function arguments 149 const RVVTypes &InputTypes = RVVI.getInputTypes(); 150 if (!InputTypes.empty()) { 151 ListSeparator LS; 152 for (unsigned i = 0; i < InputTypes.size(); ++i) 153 OS << LS << InputTypes[i]->getTypeStr(); 154 } 155 OS << ");\n"; 156 } 157 158 void emitOverloadedFuncDef(const RVVIntrinsic &RVVI, raw_ostream &OS) { 159 OS << "__attribute__((__clang_builtin_alias__("; 160 OS << "__builtin_rvv_" << RVVI.getBuiltinName() << ")))\n"; 161 OS << RVVI.getOutputType()->getTypeStr() << " " << RVVI.getOverloadedName() 162 << "("; 163 // Emit function arguments 164 const RVVTypes &InputTypes = RVVI.getInputTypes(); 165 if (!InputTypes.empty()) { 166 ListSeparator LS; 167 for (unsigned i = 0; i < InputTypes.size(); ++i) 168 OS << LS << InputTypes[i]->getTypeStr(); 169 } 170 OS << ");\n"; 171 } 172 173 //===----------------------------------------------------------------------===// 174 // RVVEmitter implementation 175 //===----------------------------------------------------------------------===// 176 void RVVEmitter::createHeader(raw_ostream &OS) { 177 178 OS << "/*===---- riscv_vector.h - RISC-V V-extension RVVIntrinsics " 179 "-------------------===\n" 180 " *\n" 181 " *\n" 182 " * Part of the LLVM Project, under the Apache License v2.0 with LLVM " 183 "Exceptions.\n" 184 " * See https://llvm.org/LICENSE.txt for license information.\n" 185 " * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n" 186 " *\n" 187 " *===-----------------------------------------------------------------" 188 "------===\n" 189 " */\n\n"; 190 191 OS << "#ifndef __RISCV_VECTOR_H\n"; 192 OS << "#define __RISCV_VECTOR_H\n\n"; 193 194 OS << "#include <stdint.h>\n"; 195 OS << "#include <stddef.h>\n\n"; 196 197 OS << "#ifndef __riscv_vector\n"; 198 OS << "#error \"Vector intrinsics require the vector extension.\"\n"; 199 OS << "#endif\n\n"; 200 201 OS << "#ifdef __cplusplus\n"; 202 OS << "extern \"C\" {\n"; 203 OS << "#endif\n\n"; 204 205 printHeaderCode(OS); 206 207 std::vector<std::unique_ptr<RVVIntrinsic>> Defs; 208 createRVVIntrinsics(Defs); 209 210 auto printType = [&](auto T) { 211 OS << "typedef " << T->getClangBuiltinStr() << " " << T->getTypeStr() 212 << ";\n"; 213 }; 214 215 constexpr int Log2LMULs[] = {-3, -2, -1, 0, 1, 2, 3}; 216 // Print RVV boolean types. 217 for (int Log2LMUL : Log2LMULs) { 218 auto T = RVVType::computeType(BasicType::Int8, Log2LMUL, 219 PrototypeDescriptor::Mask); 220 if (T) 221 printType(T.getValue()); 222 } 223 // Print RVV int/float types. 224 for (char I : StringRef("csil")) { 225 BasicType BT = ParseBasicType(I); 226 for (int Log2LMUL : Log2LMULs) { 227 auto T = RVVType::computeType(BT, Log2LMUL, PrototypeDescriptor::Vector); 228 if (T) { 229 printType(T.getValue()); 230 auto UT = RVVType::computeType( 231 BT, Log2LMUL, 232 PrototypeDescriptor(BaseTypeModifier::Vector, 233 VectorTypeModifier::NoModifier, 234 TypeModifier::UnsignedInteger)); 235 printType(UT.getValue()); 236 } 237 } 238 } 239 OS << "#if defined(__riscv_zvfh)\n"; 240 for (int Log2LMUL : Log2LMULs) { 241 auto T = RVVType::computeType(BasicType::Float16, Log2LMUL, 242 PrototypeDescriptor::Vector); 243 if (T) 244 printType(T.getValue()); 245 } 246 OS << "#endif\n"; 247 248 OS << "#if defined(__riscv_f)\n"; 249 for (int Log2LMUL : Log2LMULs) { 250 auto T = RVVType::computeType(BasicType::Float32, Log2LMUL, 251 PrototypeDescriptor::Vector); 252 if (T) 253 printType(T.getValue()); 254 } 255 OS << "#endif\n"; 256 257 OS << "#if defined(__riscv_d)\n"; 258 for (int Log2LMUL : Log2LMULs) { 259 auto T = RVVType::computeType(BasicType::Float64, Log2LMUL, 260 PrototypeDescriptor::Vector); 261 if (T) 262 printType(T.getValue()); 263 } 264 OS << "#endif\n\n"; 265 266 // The same extension include in the same arch guard marco. 267 llvm::stable_sort(Defs, [](const std::unique_ptr<RVVIntrinsic> &A, 268 const std::unique_ptr<RVVIntrinsic> &B) { 269 return A->getRISCVPredefinedMacros() < B->getRISCVPredefinedMacros(); 270 }); 271 272 OS << "#define __rvv_ai static __inline__\n"; 273 274 // Print intrinsic functions with macro 275 emitArchMacroAndBody(Defs, OS, [](raw_ostream &OS, const RVVIntrinsic &Inst) { 276 OS << "__rvv_ai "; 277 emitIntrinsicFuncDef(Inst, OS); 278 }); 279 280 OS << "#undef __rvv_ai\n\n"; 281 282 OS << "#define __riscv_v_intrinsic_overloading 1\n"; 283 284 // Print Overloaded APIs 285 OS << "#define __rvv_aio static __inline__ " 286 "__attribute__((__overloadable__))\n"; 287 288 emitArchMacroAndBody(Defs, OS, [](raw_ostream &OS, const RVVIntrinsic &Inst) { 289 if (!Inst.isMasked() && !Inst.hasUnMaskedOverloaded()) 290 return; 291 OS << "__rvv_aio "; 292 emitOverloadedFuncDef(Inst, OS); 293 }); 294 295 OS << "#undef __rvv_aio\n"; 296 297 OS << "\n#ifdef __cplusplus\n"; 298 OS << "}\n"; 299 OS << "#endif // __cplusplus\n"; 300 OS << "#endif // __RISCV_VECTOR_H\n"; 301 } 302 303 void RVVEmitter::createBuiltins(raw_ostream &OS) { 304 std::vector<std::unique_ptr<RVVIntrinsic>> Defs; 305 createRVVIntrinsics(Defs); 306 307 // Map to keep track of which builtin names have already been emitted. 308 StringMap<RVVIntrinsic *> BuiltinMap; 309 310 OS << "#if defined(TARGET_BUILTIN) && !defined(RISCVV_BUILTIN)\n"; 311 OS << "#define RISCVV_BUILTIN(ID, TYPE, ATTRS) TARGET_BUILTIN(ID, TYPE, " 312 "ATTRS, \"zve32x\")\n"; 313 OS << "#endif\n"; 314 for (auto &Def : Defs) { 315 auto P = 316 BuiltinMap.insert(std::make_pair(Def->getBuiltinName(), Def.get())); 317 if (!P.second) { 318 // Verf that this would have produced the same builtin definition. 319 if (P.first->second->hasBuiltinAlias() != Def->hasBuiltinAlias()) 320 PrintFatalError("Builtin with same name has different hasAutoDef"); 321 else if (!Def->hasBuiltinAlias() && 322 P.first->second->getBuiltinTypeStr() != Def->getBuiltinTypeStr()) 323 PrintFatalError("Builtin with same name has different type string"); 324 continue; 325 } 326 OS << "RISCVV_BUILTIN(__builtin_rvv_" << Def->getBuiltinName() << ",\""; 327 if (!Def->hasBuiltinAlias()) 328 OS << Def->getBuiltinTypeStr(); 329 OS << "\", \"n\")\n"; 330 } 331 OS << "#undef RISCVV_BUILTIN\n"; 332 } 333 334 void RVVEmitter::createCodeGen(raw_ostream &OS) { 335 std::vector<std::unique_ptr<RVVIntrinsic>> Defs; 336 createRVVIntrinsics(Defs); 337 // IR name could be empty, use the stable sort preserves the relative order. 338 llvm::stable_sort(Defs, [](const std::unique_ptr<RVVIntrinsic> &A, 339 const std::unique_ptr<RVVIntrinsic> &B) { 340 return A->getIRName() < B->getIRName(); 341 }); 342 343 // Map to keep track of which builtin names have already been emitted. 344 StringMap<RVVIntrinsic *> BuiltinMap; 345 346 // Print switch body when the ir name or ManualCodegen changes from previous 347 // iteration. 348 RVVIntrinsic *PrevDef = Defs.begin()->get(); 349 for (auto &Def : Defs) { 350 StringRef CurIRName = Def->getIRName(); 351 if (CurIRName != PrevDef->getIRName() || 352 (Def->getManualCodegen() != PrevDef->getManualCodegen())) { 353 emitCodeGenSwitchBody(PrevDef, OS); 354 } 355 PrevDef = Def.get(); 356 357 auto P = 358 BuiltinMap.insert(std::make_pair(Def->getBuiltinName(), Def.get())); 359 if (P.second) { 360 OS << "case RISCVVector::BI__builtin_rvv_" << Def->getBuiltinName() 361 << ":\n"; 362 continue; 363 } 364 365 if (P.first->second->getIRName() != Def->getIRName()) 366 PrintFatalError("Builtin with same name has different IRName"); 367 else if (P.first->second->getManualCodegen() != Def->getManualCodegen()) 368 PrintFatalError("Builtin with same name has different ManualCodegen"); 369 else if (P.first->second->getNF() != Def->getNF()) 370 PrintFatalError("Builtin with same name has different NF"); 371 else if (P.first->second->isMasked() != Def->isMasked()) 372 PrintFatalError("Builtin with same name has different isMasked"); 373 else if (P.first->second->hasVL() != Def->hasVL()) 374 PrintFatalError("Builtin with same name has different hasVL"); 375 else if (P.first->second->getPolicyScheme() != Def->getPolicyScheme()) 376 PrintFatalError("Builtin with same name has different getPolicyScheme"); 377 else if (P.first->second->getIntrinsicTypes() != Def->getIntrinsicTypes()) 378 PrintFatalError("Builtin with same name has different IntrinsicTypes"); 379 } 380 emitCodeGenSwitchBody(Defs.back().get(), OS); 381 OS << "\n"; 382 } 383 384 void RVVEmitter::createRVVIntrinsics( 385 std::vector<std::unique_ptr<RVVIntrinsic>> &Out) { 386 std::vector<Record *> RV = Records.getAllDerivedDefinitions("RVVBuiltin"); 387 for (auto *R : RV) { 388 StringRef Name = R->getValueAsString("Name"); 389 StringRef SuffixProto = R->getValueAsString("Suffix"); 390 StringRef OverloadedName = R->getValueAsString("OverloadedName"); 391 StringRef OverloadedSuffixProto = R->getValueAsString("OverloadedSuffix"); 392 StringRef Prototypes = R->getValueAsString("Prototype"); 393 StringRef TypeRange = R->getValueAsString("TypeRange"); 394 bool HasMasked = R->getValueAsBit("HasMasked"); 395 bool HasMaskedOffOperand = R->getValueAsBit("HasMaskedOffOperand"); 396 bool HasVL = R->getValueAsBit("HasVL"); 397 Record *MaskedPolicyRecord = R->getValueAsDef("MaskedPolicy"); 398 PolicyScheme MaskedPolicy = 399 static_cast<PolicyScheme>(MaskedPolicyRecord->getValueAsInt("Value")); 400 Record *UnMaskedPolicyRecord = R->getValueAsDef("UnMaskedPolicy"); 401 PolicyScheme UnMaskedPolicy = 402 static_cast<PolicyScheme>(UnMaskedPolicyRecord->getValueAsInt("Value")); 403 bool HasUnMaskedOverloaded = R->getValueAsBit("HasUnMaskedOverloaded"); 404 std::vector<int64_t> Log2LMULList = R->getValueAsListOfInts("Log2LMUL"); 405 bool HasBuiltinAlias = R->getValueAsBit("HasBuiltinAlias"); 406 StringRef ManualCodegen = R->getValueAsString("ManualCodegen"); 407 StringRef MaskedManualCodegen = R->getValueAsString("MaskedManualCodegen"); 408 std::vector<int64_t> IntrinsicTypes = 409 R->getValueAsListOfInts("IntrinsicTypes"); 410 std::vector<StringRef> RequiredFeatures = 411 R->getValueAsListOfStrings("RequiredFeatures"); 412 StringRef IRName = R->getValueAsString("IRName"); 413 StringRef MaskedIRName = R->getValueAsString("MaskedIRName"); 414 unsigned NF = R->getValueAsInt("NF"); 415 416 // Parse prototype and create a list of primitive type with transformers 417 // (operand) in Prototype. Prototype[0] is output operand. 418 SmallVector<PrototypeDescriptor> Prototype = parsePrototypes(Prototypes); 419 420 SmallVector<PrototypeDescriptor> SuffixDesc = parsePrototypes(SuffixProto); 421 SmallVector<PrototypeDescriptor> OverloadedSuffixDesc = 422 parsePrototypes(OverloadedSuffixProto); 423 424 // Compute Builtin types 425 SmallVector<PrototypeDescriptor> MaskedPrototype = Prototype; 426 if (HasMasked) { 427 // If HasMaskedOffOperand, insert result type as first input operand. 428 if (HasMaskedOffOperand) { 429 if (NF == 1) { 430 MaskedPrototype.insert(MaskedPrototype.begin() + 1, Prototype[0]); 431 } else { 432 // Convert 433 // (void, op0 address, op1 address, ...) 434 // to 435 // (void, op0 address, op1 address, ..., maskedoff0, maskedoff1, ...) 436 PrototypeDescriptor MaskoffType = Prototype[1]; 437 MaskoffType.TM &= ~static_cast<uint8_t>(TypeModifier::Pointer); 438 for (unsigned I = 0; I < NF; ++I) 439 MaskedPrototype.insert(MaskedPrototype.begin() + NF + 1, 440 MaskoffType); 441 } 442 } 443 if (HasMaskedOffOperand && NF > 1) { 444 // Convert 445 // (void, op0 address, op1 address, ..., maskedoff0, maskedoff1, ...) 446 // to 447 // (void, op0 address, op1 address, ..., mask, maskedoff0, maskedoff1, 448 // ...) 449 MaskedPrototype.insert(MaskedPrototype.begin() + NF + 1, 450 PrototypeDescriptor::Mask); 451 } else { 452 // If HasMasked, insert PrototypeDescriptor:Mask as first input operand. 453 MaskedPrototype.insert(MaskedPrototype.begin() + 1, 454 PrototypeDescriptor::Mask); 455 } 456 } 457 // If HasVL, append PrototypeDescriptor:VL to last operand 458 if (HasVL) { 459 Prototype.push_back(PrototypeDescriptor::VL); 460 MaskedPrototype.push_back(PrototypeDescriptor::VL); 461 } 462 463 // Create Intrinsics for each type and LMUL. 464 for (char I : TypeRange) { 465 for (int Log2LMUL : Log2LMULList) { 466 BasicType BT = ParseBasicType(I); 467 Optional<RVVTypes> Types = 468 RVVType::computeTypes(BT, Log2LMUL, NF, Prototype); 469 // Ignored to create new intrinsic if there are any illegal types. 470 if (!Types) 471 continue; 472 473 auto SuffixStr = RVVIntrinsic::getSuffixStr(BT, Log2LMUL, SuffixDesc); 474 auto OverloadedSuffixStr = 475 RVVIntrinsic::getSuffixStr(BT, Log2LMUL, OverloadedSuffixDesc); 476 // Create a unmasked intrinsic 477 Out.push_back(std::make_unique<RVVIntrinsic>( 478 Name, SuffixStr, OverloadedName, OverloadedSuffixStr, IRName, 479 /*IsMasked=*/false, /*HasMaskedOffOperand=*/false, HasVL, 480 UnMaskedPolicy, HasUnMaskedOverloaded, HasBuiltinAlias, 481 ManualCodegen, *Types, IntrinsicTypes, RequiredFeatures, NF)); 482 if (HasMasked) { 483 // Create a masked intrinsic 484 Optional<RVVTypes> MaskTypes = 485 RVVType::computeTypes(BT, Log2LMUL, NF, MaskedPrototype); 486 Out.push_back(std::make_unique<RVVIntrinsic>( 487 Name, SuffixStr, OverloadedName, OverloadedSuffixStr, 488 MaskedIRName, 489 /*IsMasked=*/true, HasMaskedOffOperand, HasVL, MaskedPolicy, 490 HasUnMaskedOverloaded, HasBuiltinAlias, MaskedManualCodegen, 491 *MaskTypes, IntrinsicTypes, RequiredFeatures, NF)); 492 } 493 } // end for Log2LMULList 494 } // end for TypeRange 495 } 496 } 497 498 void RVVEmitter::printHeaderCode(raw_ostream &OS) { 499 std::vector<Record *> RVVHeaders = 500 Records.getAllDerivedDefinitions("RVVHeader"); 501 for (auto *R : RVVHeaders) { 502 StringRef HeaderCodeStr = R->getValueAsString("HeaderCode"); 503 OS << HeaderCodeStr.str(); 504 } 505 } 506 507 void RVVEmitter::emitArchMacroAndBody( 508 std::vector<std::unique_ptr<RVVIntrinsic>> &Defs, raw_ostream &OS, 509 std::function<void(raw_ostream &, const RVVIntrinsic &)> PrintBody) { 510 RISCVPredefinedMacroT PrevMacros = 511 (*Defs.begin())->getRISCVPredefinedMacros(); 512 bool NeedEndif = emitMacroRestrictionStr(PrevMacros, OS); 513 for (auto &Def : Defs) { 514 RISCVPredefinedMacroT CurMacros = Def->getRISCVPredefinedMacros(); 515 if (CurMacros != PrevMacros) { 516 if (NeedEndif) 517 OS << "#endif\n\n"; 518 NeedEndif = emitMacroRestrictionStr(CurMacros, OS); 519 PrevMacros = CurMacros; 520 } 521 if (Def->hasBuiltinAlias()) 522 PrintBody(OS, *Def); 523 } 524 if (NeedEndif) 525 OS << "#endif\n\n"; 526 } 527 528 bool RVVEmitter::emitMacroRestrictionStr(RISCVPredefinedMacroT PredefinedMacros, 529 raw_ostream &OS) { 530 if (PredefinedMacros == RISCVPredefinedMacro::Basic) 531 return false; 532 OS << "#if "; 533 ListSeparator LS(" && "); 534 if (PredefinedMacros & RISCVPredefinedMacro::V) 535 OS << LS << "defined(__riscv_v)"; 536 if (PredefinedMacros & RISCVPredefinedMacro::Zvfh) 537 OS << LS << "defined(__riscv_zvfh)"; 538 if (PredefinedMacros & RISCVPredefinedMacro::RV64) 539 OS << LS << "(__riscv_xlen == 64)"; 540 if (PredefinedMacros & RISCVPredefinedMacro::VectorMaxELen64) 541 OS << LS << "(__riscv_v_elen >= 64)"; 542 if (PredefinedMacros & RISCVPredefinedMacro::VectorMaxELenFp32) 543 OS << LS << "(__riscv_v_elen_fp >= 32)"; 544 if (PredefinedMacros & RISCVPredefinedMacro::VectorMaxELenFp64) 545 OS << LS << "(__riscv_v_elen_fp >= 64)"; 546 OS << "\n"; 547 return true; 548 } 549 550 namespace clang { 551 void EmitRVVHeader(RecordKeeper &Records, raw_ostream &OS) { 552 RVVEmitter(Records).createHeader(OS); 553 } 554 555 void EmitRVVBuiltins(RecordKeeper &Records, raw_ostream &OS) { 556 RVVEmitter(Records).createBuiltins(OS); 557 } 558 559 void EmitRVVBuiltinCG(RecordKeeper &Records, raw_ostream &OS) { 560 RVVEmitter(Records).createCodeGen(OS); 561 } 562 563 } // End namespace clang 564