1//===- IntrinsicEmitter.cpp - Generate intrinsic information --------------===//
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 emits information about intrinsic functions.
10//
11//===----------------------------------------------------------------------===//
12
13#include "CodeGenIntrinsics.h"
14#include "SequenceToOffsetTable.h"
15#include "llvm/ADT/STLExtras.h"
16#include "llvm/ADT/SmallVector.h"
17#include "llvm/ADT/StringRef.h"
18#include "llvm/ADT/Twine.h"
19#include "llvm/Support/CommandLine.h"
20#include "llvm/Support/ErrorHandling.h"
21#include "llvm/Support/FormatVariadic.h"
22#include "llvm/Support/ModRef.h"
23#include "llvm/Support/SourceMgr.h"
24#include "llvm/Support/raw_ostream.h"
25#include "llvm/TableGen/CodeGenHelpers.h"
26#include "llvm/TableGen/Error.h"
27#include "llvm/TableGen/Record.h"
28#include "llvm/TableGen/StringToOffsetTable.h"
29#include "llvm/TableGen/TableGenBackend.h"
30#include <algorithm>
31#include <array>
32#include <cassert>
33#include <cctype>
34#include <map>
35#include <optional>
36#include <string>
37#include <utility>
38#include <vector>
39using namespace llvm;
40
41static cl::OptionCategory GenIntrinsicCat("Options for -gen-intrinsic-enums");
42static cl::opt<std::string>
43 IntrinsicPrefix("intrinsic-prefix",
44 cl::desc("Generate intrinsics with this target prefix"),
45 cl::value_desc("target prefix"), cl::cat(GenIntrinsicCat));
46
47namespace {
48class IntrinsicEmitter {
49 const RecordKeeper &Records;
50
51public:
52 IntrinsicEmitter(const RecordKeeper &R) : Records(R) {}
53
54 void run(raw_ostream &OS, bool Enums);
55
56 void EmitEnumInfo(const CodeGenIntrinsicTable &Ints, raw_ostream &OS);
57 void EmitAnyKindEnums(raw_ostream &OS);
58 void EmitIITInfo(raw_ostream &OS);
59 void EmitTargetInfo(const CodeGenIntrinsicTable &Ints, raw_ostream &OS);
60 void EmitIntrinsicToNameTable(const CodeGenIntrinsicTable &Ints,
61 raw_ostream &OS);
62 void EmitIntrinsicToOverloadTable(const CodeGenIntrinsicTable &Ints,
63 raw_ostream &OS);
64 void EmitIntrinsicToScalarizableTable(const CodeGenIntrinsicTable &Ints,
65 raw_ostream &OS);
66 void EmitIntrinsicToPrettyPrintTable(const CodeGenIntrinsicTable &Ints,
67 raw_ostream &OS);
68 void EmitIntrinsicBitTable(
69 const CodeGenIntrinsicTable &Ints, raw_ostream &OS, StringRef Guard,
70 StringRef TableName, StringRef Comment,
71 function_ref<bool(const CodeGenIntrinsic &Int)> GetProperty);
72 void EmitGenerator(const CodeGenIntrinsicTable &Ints, raw_ostream &OS);
73 void EmitAttributes(const CodeGenIntrinsicTable &Ints, raw_ostream &OS);
74 void EmitPrettyPrintArguments(const CodeGenIntrinsicTable &Ints,
75 raw_ostream &OS);
76 void EmitIntrinsicToBuiltinMap(const CodeGenIntrinsicTable &Ints,
77 bool IsClang, raw_ostream &OS);
78};
79
80// Helper class to use with `TableGen::Emitter::OptClass`.
81template <bool Enums> class IntrinsicEmitterOpt : public IntrinsicEmitter {
82public:
83 IntrinsicEmitterOpt(const RecordKeeper &R) : IntrinsicEmitter(R) {}
84 void run(raw_ostream &OS) { IntrinsicEmitter::run(OS, Enums); }
85};
86
87} // End anonymous namespace
88
89//===----------------------------------------------------------------------===//
90// IntrinsicEmitter Implementation
91//===----------------------------------------------------------------------===//
92
93void IntrinsicEmitter::run(raw_ostream &OS, bool Enums) {
94 emitSourceFileHeader(Desc: "Intrinsic Function Source Fragment", OS);
95
96 CodeGenIntrinsicTable Ints(Records);
97
98 if (Enums) {
99 // Emit the enum information.
100 EmitEnumInfo(Ints, OS);
101
102 // Emit AnyKind enums for Intrinsics.h.
103 EmitAnyKindEnums(OS);
104 } else {
105 // Emit IIT_Info constants.
106 EmitIITInfo(OS);
107
108 // Emit the target metadata.
109 EmitTargetInfo(Ints, OS);
110
111 // Emit the intrinsic ID -> name table.
112 EmitIntrinsicToNameTable(Ints, OS);
113
114 // Emit the intrinsic ID -> overload table.
115 EmitIntrinsicToOverloadTable(Ints, OS);
116
117 // Emit the intrinsic ID -> trivially scalarizable table.
118 EmitIntrinsicToScalarizableTable(Ints, OS);
119
120 // Emit the intrinsic declaration generator.
121 EmitGenerator(Ints, OS);
122
123 // Emit the intrinsic parameter attributes.
124 EmitAttributes(Ints, OS);
125
126 // Emit the intrinsic ID -> pretty print table.
127 EmitIntrinsicToPrettyPrintTable(Ints, OS);
128
129 // Emit Pretty Print attribute.
130 EmitPrettyPrintArguments(Ints, OS);
131
132 // Emit code to translate Clang builtins into LLVM intrinsics.
133 EmitIntrinsicToBuiltinMap(Ints, IsClang: true, OS);
134
135 // Emit code to translate MS builtins into LLVM intrinsics.
136 EmitIntrinsicToBuiltinMap(Ints, IsClang: false, OS);
137 }
138}
139
140void IntrinsicEmitter::EmitEnumInfo(const CodeGenIntrinsicTable &Ints,
141 raw_ostream &OS) {
142 // Find the TargetSet for which to generate enums. There will be an initial
143 // set with an empty target prefix which will include target independent
144 // intrinsics like dbg.value.
145 using TargetSet = CodeGenIntrinsicTable::TargetSet;
146 const TargetSet *Set = nullptr;
147 for (const auto &Target : Ints.getTargets()) {
148 if (Target.Name == IntrinsicPrefix) {
149 Set = &Target;
150 break;
151 }
152 }
153 if (!Set) {
154 // The first entry is for target independent intrinsics, so drop it.
155 auto KnowTargets = Ints.getTargets().drop_front();
156 PrintFatalError(PrintMsg: [KnowTargets](raw_ostream &OS) {
157 OS << "tried to generate intrinsics for unknown target "
158 << IntrinsicPrefix << "\nKnown targets are: ";
159 interleaveComma(c: KnowTargets, os&: OS,
160 each_fn: [&OS](const TargetSet &Target) { OS << Target.Name; });
161 OS << '\n';
162 });
163 }
164
165 // Generate a complete header for target specific intrinsics.
166 std::optional<IfDefEmitter> IfDef;
167 std::optional<IncludeGuardEmitter> IncGuard;
168 std::optional<NamespaceEmitter> NS;
169
170 if (IntrinsicPrefix.empty()) {
171 IfDef.emplace(args&: OS, args: "GET_INTRINSIC_ENUM_VALUES");
172 } else {
173 std::string UpperPrefix = StringRef(IntrinsicPrefix).upper();
174 IncGuard.emplace(
175 args&: OS, args: formatv(Fmt: "LLVM_IR_INTRINSIC_{}_ENUMS_H", Vals&: UpperPrefix).str());
176 NS.emplace(args&: OS, args: "llvm::Intrinsic");
177 OS << formatv(Fmt: "enum {}Intrinsics : unsigned {{\n", Vals&: UpperPrefix);
178 }
179
180 OS << "// Enum values for intrinsics.\n";
181 bool First = true;
182 for (const CodeGenIntrinsic &Int : Ints[*Set]) {
183 OS << " " << Int.EnumName;
184
185 // Assign a value to the first intrinsic in this target set so that all
186 // intrinsic ids are distinct.
187 if (First) {
188 OS << " = " << Set->Offset + 1;
189 First = false;
190 }
191
192 OS << ", ";
193 if (Int.EnumName.size() < 40)
194 OS.indent(NumSpaces: 40 - Int.EnumName.size());
195 OS << formatv(
196 Fmt: " // {} ({})\n", Vals: Int.Name,
197 Vals: SrcMgr.getFormattedLocationNoOffset(Loc: Int.TheDef->getLoc().front()));
198 }
199
200 // Emit num_intrinsics into the target neutral enum.
201 if (IntrinsicPrefix.empty())
202 OS << formatv(Fmt: " num_intrinsics = {}\n", Vals: Ints.size() + 1);
203 else
204 OS << "}; // enum\n";
205}
206
207void IntrinsicEmitter::EmitAnyKindEnums(raw_ostream &OS) {
208 if (!IntrinsicPrefix.empty())
209 return;
210 IfDefEmitter IfDef(OS, "GET_INTRINSIC_ANYKIND_ENUMS");
211
212 auto GenerateAnyKindEnums = [&OS, this](StringRef EnumName,
213 StringRef Prefix) {
214 OS << "// llvm::Intrinsic::IITDescriptor::" << EnumName << "\n";
215 if (const Record *EnumDef = Records.getDef(Name: EnumName)) {
216 OS << "enum " << EnumName << " {\n";
217 for (const auto &RV : EnumDef->getValues())
218 OS << " " << Prefix << RV.getName() << " = " << *RV.getValue()
219 << ",\n";
220 OS << "}; // " << EnumName << "\n\n";
221 } else {
222 OS << "#error \"" << EnumName << " is not defined\"\n";
223 }
224 };
225 GenerateAnyKindEnums("AnyKindVectorConstraint", "VC_");
226 GenerateAnyKindEnums("AnyKindElementConstraint", "EC_");
227}
228
229void IntrinsicEmitter::EmitIITInfo(raw_ostream &OS) {
230 IfDefEmitter IfDef(OS, "GET_INTRINSIC_IITINFO");
231 std::array<StringRef, 256> RecsByNumber;
232 auto IIT_Base = Records.getAllDerivedDefinitionsIfDefined(ClassName: "IIT_Base");
233 for (const Record *Rec : IIT_Base) {
234 auto Number = Rec->getValueAsInt(FieldName: "Number");
235 assert(0 <= Number && Number < (int)RecsByNumber.size() &&
236 "IIT_Info.Number should be uint8_t");
237 assert(RecsByNumber[Number].empty() && "Duplicate IIT_Info.Number");
238 RecsByNumber[Number] = Rec->getName();
239 }
240 if (IIT_Base.size() > 0) {
241 if (RecsByNumber[0] != "IIT_Done")
242 PrintFatalError(Msg: "IIT_Done expected to have value 0");
243 for (unsigned I = 0, E = RecsByNumber.size(); I < E; ++I)
244 if (!RecsByNumber[I].empty())
245 OS << " " << RecsByNumber[I] << " = " << I << ",\n";
246 } else {
247 OS << "#error \"class IIT_Base is not defined\"\n";
248 }
249}
250
251void IntrinsicEmitter::EmitTargetInfo(const CodeGenIntrinsicTable &Ints,
252 raw_ostream &OS) {
253 IfDefEmitter IfDef(OS, "GET_INTRINSIC_TARGET_DATA");
254 OS << R"(// Target mapping.
255struct IntrinsicTargetInfo {
256 StringLiteral Name;
257 size_t Offset;
258 size_t Count;
259};
260static constexpr IntrinsicTargetInfo TargetInfos[] = {
261)";
262 for (const auto [Name, Offset, Count] : Ints.getTargets())
263 OS << formatv(Fmt: " {{\"{}\", {}, {}},\n", Vals: Name, Vals: Offset, Vals: Count);
264 OS << "};\n";
265}
266
267/// Helper function to emit a bit table for intrinsic properties.
268/// This is used for both overload and pretty print bit tables.
269void IntrinsicEmitter::EmitIntrinsicBitTable(
270 const CodeGenIntrinsicTable &Ints, raw_ostream &OS, StringRef Guard,
271 StringRef TableName, StringRef Comment,
272 function_ref<bool(const CodeGenIntrinsic &Int)> GetProperty) {
273 IfDefEmitter IfDef(OS, Guard);
274 OS << formatv(Fmt: "// {}\n", Vals&: Comment);
275 OS << formatv(Fmt: "static constexpr uint8_t {}[] = {{\n", Vals&: TableName);
276 OS << " 0\n ";
277 for (auto [I, Int] : enumerate(First: Ints)) {
278 // Add one to the index so we emit a null bit for the invalid #0 intrinsic.
279 size_t Idx = I + 1;
280 if (Idx % 8 == 0)
281 OS << ",\n 0";
282 if (GetProperty(Int))
283 OS << " | (1<<" << Idx % 8 << ')';
284 }
285 OS << "\n};\n\n";
286 OS << formatv(Fmt: "return ({}[id/8] & (1 << (id%8))) != 0;\n", Vals&: TableName);
287}
288
289void IntrinsicEmitter::EmitIntrinsicToNameTable(
290 const CodeGenIntrinsicTable &Ints, raw_ostream &OS) {
291 // Built up a table of the intrinsic names.
292 constexpr StringLiteral NotIntrinsic = "not_intrinsic";
293 StringToOffsetTable Table;
294 Table.GetOrAddStringOffset(Str: NotIntrinsic);
295 for (const auto &Int : Ints)
296 Table.GetOrAddStringOffset(Str: Int.Name);
297
298 IfDefEmitter IfDef(OS, "GET_INTRINSIC_NAME_TABLE");
299 OS << R"(// Intrinsic ID to name table.
300// Note that entry #0 is the invalid intrinsic!
301
302)";
303
304 Table.EmitStringTableDef(OS, Name: "IntrinsicNameTable");
305
306 OS << R"(
307static constexpr unsigned IntrinsicNameOffsetTable[] = {
308)";
309
310 OS << formatv(Fmt: " {}, // {}\n", Vals: Table.GetStringOffset(Str: NotIntrinsic),
311 Vals: NotIntrinsic);
312 for (const auto &Int : Ints)
313 OS << formatv(Fmt: " {}, // {}\n", Vals: Table.GetStringOffset(Str: Int.Name), Vals: Int.Name);
314
315 OS << "\n}; // IntrinsicNameOffsetTable\n";
316}
317
318void IntrinsicEmitter::EmitIntrinsicToOverloadTable(
319 const CodeGenIntrinsicTable &Ints, raw_ostream &OS) {
320 EmitIntrinsicBitTable(
321 Ints, OS, Guard: "GET_INTRINSIC_OVERLOAD_TABLE", TableName: "OTable",
322 Comment: "Intrinsic ID to overload bitset.",
323 GetProperty: [](const CodeGenIntrinsic &Int) { return Int.isOverloaded; });
324}
325
326void IntrinsicEmitter::EmitIntrinsicToScalarizableTable(
327 const CodeGenIntrinsicTable &Ints, raw_ostream &OS) {
328 EmitIntrinsicBitTable(
329 Ints, OS, Guard: "GET_INTRINSIC_SCALARIZABLE_TABLE", TableName: "STable",
330 Comment: "Intrinsic ID to trivially scalarizable bitset.",
331 GetProperty: [](const CodeGenIntrinsic &Int) { return Int.isTriviallyScalarizable; });
332}
333
334using TypeSigTy = SmallVector<unsigned char>;
335
336/// Computes type signature of the intrinsic \p Int.
337static TypeSigTy ComputeTypeSignature(const CodeGenIntrinsic &Int) {
338 TypeSigTy TypeSig;
339 const Record *TypeInfo = Int.TheDef->getValueAsDef(FieldName: "TypeInfo");
340 const ListInit *TypeList = TypeInfo->getValueAsListInit(FieldName: "TypeSig");
341
342 for (const auto *TypeListEntry : TypeList->getElements()) {
343 int64_t Value = cast<IntInit>(Val: TypeListEntry)->getValue();
344 if (Value < 0 || Value > 255)
345 PrintFatalError(Rec: Int.TheDef, Msg: "Unresolved type signature");
346 TypeSig.emplace_back(Args&: Value);
347 }
348 return TypeSig;
349}
350
351// Note: the code below can be switched to use 32-bit fixed encoding by
352// flipping the flag below.
353constexpr bool Use16BitFixedEncoding = true;
354using FixedEncodingTy =
355 std::conditional_t<Use16BitFixedEncoding, uint16_t, uint32_t>;
356
357// Pack the type signature into 16/32-bit fixed encoding word, where each byte
358// in the type signature is packed into a nibble (4 bits) if possible.
359static std::optional<FixedEncodingTy> encodePacked(const TypeSigTy &TypeSig) {
360 constexpr size_t NUM_NIBBLES = sizeof(FixedEncodingTy) * 2;
361 if (TypeSig.size() > NUM_NIBBLES)
362 return std::nullopt;
363
364 FixedEncodingTy Result = 0;
365 for (unsigned char C : reverse(C: TypeSig)) {
366 if (C > 15)
367 return std::nullopt;
368 Result = (Result << 4) | C;
369 }
370 return Result;
371}
372
373void IntrinsicEmitter::EmitGenerator(const CodeGenIntrinsicTable &Ints,
374 raw_ostream &OS) {
375 constexpr unsigned FixedEncodingBits = sizeof(FixedEncodingTy) * CHAR_BIT;
376 constexpr unsigned MSBPosition = FixedEncodingBits - 1;
377 // Mask with all bits 1 except the most significant bit.
378 constexpr FixedEncodingTy Mask = (1U << MSBPosition) - 1;
379 StringRef FixedEncodingTypeName =
380 Use16BitFixedEncoding ? "uint16_t" : "uint32_t";
381
382 // If we can compute a 16/32-bit fixed encoding for this intrinsic, do so and
383 // capture it in this vector, otherwise store a ~0U.
384 std::vector<FixedEncodingTy> FixedEncodings;
385
386 // Each IIT encoding sequence in the long encoding table is terminated by
387 // IIT_Done(=0) token.
388 constexpr unsigned char IIT_Done = 0;
389 SequenceToOffsetTable<TypeSigTy> LongEncodingTable(IIT_Done);
390
391 FixedEncodings.reserve(n: Ints.size());
392
393 // Compute the unique argument type info.
394 for (const CodeGenIntrinsic &Int : Ints) {
395 // Get the signature for the intrinsic.
396 TypeSigTy TypeSig = ComputeTypeSignature(Int);
397
398 // Check to see if we can encode it into a 16/32 bit word.
399 std::optional<FixedEncodingTy> Result = encodePacked(TypeSig);
400 if (Result && (*Result & Mask) == *Result) {
401 FixedEncodings.push_back(x: *Result);
402 continue;
403 }
404
405 LongEncodingTable.add(Seq: TypeSig);
406
407 // This is a placehold that we'll replace after the table is laid out.
408 FixedEncodings.push_back(x: static_cast<FixedEncodingTy>(~0U));
409 }
410
411 LongEncodingTable.layout();
412
413 IfDefEmitter IfDef(OS, "GET_INTRINSIC_GENERATOR_GLOBAL");
414 OS << formatv(Fmt: R"(// Global intrinsic function declaration type table.
415using FixedEncodingTy = {};
416static constexpr FixedEncodingTy IIT_Table[] = {{
417 )",
418 Vals&: FixedEncodingTypeName);
419
420 unsigned MaxOffset = 0;
421 for (auto [Idx, FixedEncoding, Int] : enumerate(First&: FixedEncodings, Rest: Ints)) {
422 if ((Idx & 7) == 7)
423 OS << "\n ";
424
425 // If the entry fit in the table, just emit it.
426 if ((FixedEncoding & Mask) == FixedEncoding) {
427 OS << "0x" << Twine::utohexstr(Val: FixedEncoding) << ", ";
428 continue;
429 }
430
431 TypeSigTy TypeSig = ComputeTypeSignature(Int);
432 unsigned Offset = LongEncodingTable.get(Seq: TypeSig);
433 MaxOffset = std::max(a: MaxOffset, b: Offset);
434
435 // Otherwise, emit the offset into the long encoding table. We emit it this
436 // way so that it is easier to read the offset in the .def file.
437 OS << formatv(Fmt: "(1U<<{}) | {}, ", Vals: MSBPosition, Vals&: Offset);
438 }
439
440 OS << "0\n};\n\n";
441
442 // verify that all offsets will fit in 16/32 bits.
443 if ((MaxOffset & Mask) != MaxOffset)
444 PrintFatalError(Msg: "Offset of long encoding table exceeds encoding bits");
445
446 // Emit the shared table of register lists.
447 OS << "static constexpr unsigned char IIT_LongEncodingTable[] = {\n";
448 if (!LongEncodingTable.empty())
449 LongEncodingTable.emit(
450 OS, Print: [](raw_ostream &OS, unsigned char C) { OS << (unsigned)C; });
451 OS << " 255\n};\n";
452}
453
454/// Returns the effective MemoryEffects for intrinsic \p Int.
455static MemoryEffects getEffectiveME(const CodeGenIntrinsic &Int) {
456 MemoryEffects ME = Int.ME;
457 // TODO: IntrHasSideEffects should affect not only readnone intrinsics.
458 if (ME.doesNotAccessMemory() && Int.hasSideEffects)
459 ME = MemoryEffects::unknown();
460 return ME;
461}
462
463static bool compareFnAttributes(const CodeGenIntrinsic *L,
464 const CodeGenIntrinsic *R) {
465 auto TieBoolAttributes = [](const CodeGenIntrinsic *I) -> auto {
466 // Sort throwing intrinsics after non-throwing intrinsics.
467 return std::tie(args: I->canThrow, args: I->isNoDuplicate, args: I->isNoMerge, args: I->isNoReturn,
468 args: I->isNoCallback, args: I->isNoSync, args: I->isNoFree, args: I->isWillReturn,
469 args: I->isCold, args: I->isConvergent, args: I->isSpeculatable,
470 args: I->hasSideEffects, args: I->isStrictFP,
471 args: I->isNoCreateUndefOrPoison);
472 };
473
474 auto TieL = TieBoolAttributes(L);
475 auto TieR = TieBoolAttributes(R);
476
477 if (TieL != TieR)
478 return TieL < TieR;
479
480 // Try to order by readonly/readnone attribute.
481 uint32_t LME = getEffectiveME(Int: *L).toIntValue();
482 uint32_t RME = getEffectiveME(Int: *R).toIntValue();
483 if (LME != RME)
484 return LME > RME;
485
486 return false;
487}
488
489/// Returns true if \p Int has a non-empty set of function attributes. Note that
490/// NoUnwind = !canThrow, so we need to negate it's sense to test if the
491// intrinsic has NoUnwind attribute.
492static bool hasFnAttributes(const CodeGenIntrinsic &Int) {
493 return !Int.canThrow || Int.isNoReturn || Int.isNoCallback || Int.isNoSync ||
494 Int.isNoFree || Int.isWillReturn || Int.isCold || Int.isNoDuplicate ||
495 Int.isNoMerge || Int.isConvergent || Int.isSpeculatable ||
496 Int.isStrictFP || Int.isNoCreateUndefOrPoison ||
497 getEffectiveME(Int) != MemoryEffects::unknown();
498}
499
500namespace {
501struct FnAttributeComparator {
502 bool operator()(const CodeGenIntrinsic *L, const CodeGenIntrinsic *R) const {
503 return compareFnAttributes(L, R);
504 }
505};
506
507struct AttributeComparator {
508 bool operator()(const CodeGenIntrinsic *L, const CodeGenIntrinsic *R) const {
509 // This comparator is used to unique just the argument attributes of an
510 // intrinsic without considering any function attributes.
511 return L->ArgumentAttributes < R->ArgumentAttributes;
512 }
513};
514} // End anonymous namespace
515
516/// Returns the name of the IR enum for argument attribute kind \p Kind.
517static StringRef getArgAttrEnumName(CodeGenIntrinsic::ArgAttrKind Kind) {
518 switch (Kind) {
519 case CodeGenIntrinsic::NoCapture:
520 llvm_unreachable("Handled separately");
521 case CodeGenIntrinsic::NoAlias:
522 return "NoAlias";
523 case CodeGenIntrinsic::NoUndef:
524 return "NoUndef";
525 case CodeGenIntrinsic::NonNull:
526 return "NonNull";
527 case CodeGenIntrinsic::Returned:
528 return "Returned";
529 case CodeGenIntrinsic::ReadOnly:
530 return "ReadOnly";
531 case CodeGenIntrinsic::WriteOnly:
532 return "WriteOnly";
533 case CodeGenIntrinsic::ReadNone:
534 return "ReadNone";
535 case CodeGenIntrinsic::ImmArg:
536 return "ImmArg";
537 case CodeGenIntrinsic::Alignment:
538 return "Alignment";
539 case CodeGenIntrinsic::Dereferenceable:
540 return "Dereferenceable";
541 case CodeGenIntrinsic::Range:
542 return "Range";
543 }
544 llvm_unreachable("Unknown CodeGenIntrinsic::ArgAttrKind enum");
545}
546
547/// EmitAttributes - This emits the Intrinsic::getAttributes method.
548void IntrinsicEmitter::EmitAttributes(const CodeGenIntrinsicTable &Ints,
549 raw_ostream &OS) {
550 IfDefEmitter IfDef(OS, "GET_INTRINSIC_ATTRIBUTES");
551 OS << R"(// Add parameter attributes that are not common to all intrinsics.
552static AttributeSet getIntrinsicArgAttributeSet(LLVMContext &C, unsigned ID,
553 Type *ArgType) {
554 unsigned BitWidth = ArgType->getScalarSizeInBits();
555 switch (ID) {
556 default: llvm_unreachable("Invalid attribute set number");)";
557 // Compute unique argument attribute sets.
558 std::map<SmallVector<CodeGenIntrinsic::ArgAttribute, 0>, unsigned>
559 UniqArgAttributes;
560 for (const CodeGenIntrinsic &Int : Ints) {
561 for (auto &Attrs : Int.ArgumentAttributes) {
562 if (Attrs.empty())
563 continue;
564
565 unsigned ID = UniqArgAttributes.size();
566 if (!UniqArgAttributes.try_emplace(k: Attrs, args&: ID).second)
567 continue;
568
569 assert(is_sorted(Attrs) && "Argument attributes are not sorted");
570
571 OS << formatv(Fmt: R"(
572 case {}:
573 return AttributeSet::get(C, {{
574)",
575 Vals&: ID);
576 for (const CodeGenIntrinsic::ArgAttribute &Attr : Attrs) {
577 if (Attr.Kind == CodeGenIntrinsic::NoCapture) {
578 OS << " Attribute::getWithCaptureInfo(C, "
579 "CaptureInfo::none()),\n";
580 continue;
581 }
582 StringRef AttrName = getArgAttrEnumName(Kind: Attr.Kind);
583 if (Attr.Kind == CodeGenIntrinsic::Alignment ||
584 Attr.Kind == CodeGenIntrinsic::Dereferenceable)
585 OS << formatv(Fmt: " Attribute::get(C, Attribute::{}, {}),\n",
586 Vals&: AttrName, Vals: Attr.Value);
587 else if (Attr.Kind == CodeGenIntrinsic::Range)
588 // This allows implicitTrunc because the range may only fit the
589 // type based on rules implemented in the IR verifier. E.g. the
590 // [-1, 1] range for ucmp/scmp intrinsics requires a minimum i2 type.
591 // Give the verifier a chance to diagnose this instead of asserting
592 // here.
593 OS << formatv(Fmt: " Attribute::get(C, Attribute::{}, "
594 "ConstantRange(APInt(BitWidth, {}, /*isSigned=*/true, "
595 "/*implicitTrunc=*/true), APInt(BitWidth, {}, "
596 "/*isSigned=*/true, /*implicitTrunc=*/true))),\n",
597 Vals&: AttrName, Vals: (int64_t)Attr.Value, Vals: (int64_t)Attr.Value2);
598 else
599 OS << formatv(Fmt: " Attribute::get(C, Attribute::{}),\n", Vals&: AttrName);
600 }
601 OS << " });";
602 }
603 }
604 OS << R"(
605 }
606} // getIntrinsicArgAttributeSet
607)";
608
609 // Compute unique function attribute sets. Note that ID 255 will be used for
610 // intrinsics with no function attributes.
611 std::map<const CodeGenIntrinsic *, unsigned, FnAttributeComparator>
612 UniqFnAttributes;
613 OS << R"(
614static AttributeSet getIntrinsicFnAttributeSet(LLVMContext &C, unsigned ID) {
615 switch (ID) {
616 default: llvm_unreachable("Invalid attribute set number");)";
617
618 for (const CodeGenIntrinsic &Int : Ints) {
619 if (!hasFnAttributes(Int))
620 continue;
621 unsigned ID = UniqFnAttributes.size();
622 if (!UniqFnAttributes.try_emplace(k: &Int, args&: ID).second)
623 continue;
624 OS << formatv(Fmt: R"(
625 case {}: // {}
626 return AttributeSet::get(C, {{
627)",
628 Vals&: ID, Vals: Int.Name);
629 auto addAttribute = [&OS](StringRef Attr) {
630 OS << formatv(Fmt: " Attribute::get(C, Attribute::{}),\n", Vals&: Attr);
631 };
632 if (!Int.canThrow)
633 addAttribute("NoUnwind");
634 if (Int.isNoReturn)
635 addAttribute("NoReturn");
636 if (Int.isNoCallback)
637 addAttribute("NoCallback");
638 if (Int.isNoSync)
639 addAttribute("NoSync");
640 if (Int.isNoFree)
641 addAttribute("NoFree");
642 if (Int.isWillReturn)
643 addAttribute("WillReturn");
644 if (Int.isCold)
645 addAttribute("Cold");
646 if (Int.isNoDuplicate)
647 addAttribute("NoDuplicate");
648 if (Int.isNoMerge)
649 addAttribute("NoMerge");
650 if (Int.isConvergent)
651 addAttribute("Convergent");
652 if (Int.isSpeculatable)
653 addAttribute("Speculatable");
654 if (Int.isStrictFP)
655 addAttribute("StrictFP");
656 if (Int.isNoCreateUndefOrPoison)
657 addAttribute("NoCreateUndefOrPoison");
658
659 const MemoryEffects ME = getEffectiveME(Int);
660 if (ME != MemoryEffects::unknown()) {
661 OS << formatv(Fmt: " // {}\n", Vals: ME);
662 OS << formatv(Fmt: " Attribute::getWithMemoryEffects(C, "
663 "MemoryEffects::createFromIntValue({})),\n",
664 Vals: ME.toIntValue());
665 }
666 OS << " });";
667 }
668 OS << R"(
669 }
670} // getIntrinsicFnAttributeSet)";
671
672 // Compute unique argument attributes.
673 std::map<const CodeGenIntrinsic *, unsigned, AttributeComparator>
674 UniqAttributes;
675 for (const CodeGenIntrinsic &Int : Ints) {
676 unsigned ID = UniqAttributes.size();
677 UniqAttributes.try_emplace(k: &Int, args&: ID);
678 }
679
680 const uint8_t UniqAttributesBitSize = Log2_32_Ceil(Value: UniqAttributes.size());
681 // Note, max value is used to indicate no function attributes.
682 const uint8_t UniqFnAttributesBitSize =
683 Log2_32_Ceil(Value: UniqFnAttributes.size() + 1);
684 const uint32_t NoFunctionAttrsID =
685 maskTrailingOnes<uint32_t>(N: UniqFnAttributesBitSize);
686 uint8_t AttributesMapDataBitSize =
687 PowerOf2Ceil(A: UniqAttributesBitSize + UniqFnAttributesBitSize);
688 if (AttributesMapDataBitSize < 8)
689 AttributesMapDataBitSize = 8;
690 else if (AttributesMapDataBitSize > 64)
691 PrintFatalError(Msg: "Packed ID of IntrinsicsToAttributesMap exceeds 64b!");
692
693 // Assign a packed ID for each intrinsic. The lower bits will be its
694 // "argument attribute ID" (index in UniqAttributes) and upper bits will be
695 // its "function attribute ID" (index in UniqFnAttributes).
696 OS << formatv(Fmt: "\nstatic constexpr uint{}_t IntrinsicsToAttributesMap[] = {{",
697 Vals&: AttributesMapDataBitSize);
698 for (const CodeGenIntrinsic &Int : Ints) {
699 uint32_t FnAttrIndex =
700 hasFnAttributes(Int) ? UniqFnAttributes[&Int] : NoFunctionAttrsID;
701 OS << formatv(Fmt: "\n {} << {} | {}, // {}", Vals&: FnAttrIndex,
702 Vals: UniqAttributesBitSize, Vals&: UniqAttributes[&Int], Vals: Int.Name);
703 }
704
705 OS << R"(
706}; // IntrinsicsToAttributesMap
707)";
708
709 // For a given intrinsic, its attributes are constructed by populating the
710 // local array `AS` below with its non-empty argument attributes followed by
711 // function attributes if any. Each argument attribute is constructed as:
712 //
713 // getIntrinsicArgAttributeSet(C, ArgAttrID, FT->getContainedType(ArgNo));
714 //
715 // Create a table that records, for each argument attributes, the list of
716 // <ArgNo, ArgAttrID> pairs that are needed to construct its argument
717 // attributes. These tables for all intrinsics will be concatenated into one
718 // large table and then for each intrinsic, we remember the Staring index and
719 // number of size of its slice of entries (i.e., number of arguments with
720 // non-empty attributes), so that we can build the attribute list for an
721 // intrinsic without using a switch-case.
722
723 using ArgNoAttrIDPair = std::pair<uint16_t, uint16_t>;
724
725 // Emit the table of concatenated <ArgNo, AttrId> using SequenceToOffsetTable
726 // so that entries can be reused if possible. Individual sequences in this
727 // table do not have any terminator.
728 using ArgAttrIDSubTable = SmallVector<ArgNoAttrIDPair>;
729 SequenceToOffsetTable<ArgAttrIDSubTable> ArgAttrIdSequenceTable(std::nullopt);
730 SmallVector<ArgAttrIDSubTable> ArgAttrIdSubTables(
731 UniqAttributes.size()); // Indexed by UniqueID.
732
733 // Find the max number of attributes to create the local array.
734 unsigned MaxNumAttrs = 0;
735 for (const auto [IntPtr, UniqueID] : UniqAttributes) {
736 const CodeGenIntrinsic &Int = *IntPtr;
737 ArgAttrIDSubTable SubTable;
738
739 for (const auto &[ArgNo, Attrs] : enumerate(First: Int.ArgumentAttributes)) {
740 if (Attrs.empty())
741 continue;
742
743 uint16_t ArgAttrID = UniqArgAttributes.find(x: Attrs)->second;
744 SubTable.emplace_back(Args: (uint16_t)ArgNo, Args&: ArgAttrID);
745 }
746 ArgAttrIdSubTables[UniqueID] = SubTable;
747 if (!SubTable.empty())
748 ArgAttrIdSequenceTable.add(Seq: SubTable);
749 unsigned NumAttrs = SubTable.size() + hasFnAttributes(Int);
750 MaxNumAttrs = std::max(a: MaxNumAttrs, b: NumAttrs);
751 }
752
753 ArgAttrIdSequenceTable.layout();
754
755 if (ArgAttrIdSequenceTable.size() >= std::numeric_limits<uint16_t>::max())
756 PrintFatalError(Msg: "Size of ArgAttrIdTable exceeds supported limit");
757
758 // Emit the 2 tables (flattened ArgNo, ArgAttrID) and ArgAttributesInfoTable.
759 OS << formatv(Fmt: R"(
760namespace {{
761struct ArgNoAttrIDPair {{
762 uint16_t ArgNo, ArgAttrID;
763};
764} // namespace
765
766// Number of entries: {}
767static constexpr ArgNoAttrIDPair ArgAttrIdTable[] = {{
768)",
769 Vals: ArgAttrIdSequenceTable.size());
770
771 ArgAttrIdSequenceTable.emit(OS, Print: [](raw_ostream &OS, ArgNoAttrIDPair Elem) {
772 OS << formatv(Fmt: "{{{}, {}}", Vals&: Elem.first, Vals&: Elem.second);
773 });
774
775 OS << formatv(Fmt: R"(}; // ArgAttrIdTable
776
777namespace {{
778struct ArgAttributesInfo {{
779 uint16_t StartIndex;
780 uint16_t NumAttrs;
781};
782} // namespace
783
784// Number of entries: {}
785static constexpr ArgAttributesInfo ArgAttributesInfoTable[] = {{
786)",
787 Vals: ArgAttrIdSubTables.size());
788
789 for (const auto &SubTable : ArgAttrIdSubTables) {
790 unsigned NumAttrs = SubTable.size();
791 unsigned StartIndex = NumAttrs ? ArgAttrIdSequenceTable.get(Seq: SubTable) : 0;
792 OS << formatv(Fmt: " {{{}, {}},\n", Vals&: StartIndex, Vals&: NumAttrs);
793 }
794 OS << "}; // ArgAttributesInfoTable\n";
795
796 // Now emit the Intrinsic::getAttributes function. This will first map
797 // from intrinsic ID -> unique arg/function attr ID (using the
798 // IntrinsicsToAttributesMap) table. Then it will use the unique arg ID to
799 // construct all the argument attributes (using the ArgAttributesInfoTable and
800 // ArgAttrIdTable) and then add on the function attributes if any.
801 OS << formatv(Fmt: R"(
802
803template <typename IDTy>
804inline std::pair<uint32_t, uint32_t> unpackID(const IDTy PackedID) {{
805 constexpr uint8_t UniqAttributesBitSize = {};
806 const uint32_t FnAttrID = PackedID >> UniqAttributesBitSize;
807 const uint32_t ArgAttrID = PackedID &
808 maskTrailingOnes<uint32_t>(UniqAttributesBitSize);
809 return {{FnAttrID, ArgAttrID};
810}
811
812AttributeList Intrinsic::getAttributes(LLVMContext &C, ID id,
813 FunctionType *FT) {{
814 if (id == 0)
815 return AttributeList();
816 auto [FnAttrID, ArgAttrID] = unpackID(IntrinsicsToAttributesMap[id - 1]);
817 using PairTy = std::pair<unsigned, AttributeSet>;
818 alignas(PairTy) char ASStorage[sizeof(PairTy) * {}];
819 PairTy *AS = reinterpret_cast<PairTy *>(ASStorage);
820
821 // Construct an ArrayRef for easier range checking.
822 ArrayRef<ArgAttributesInfo> ArgAttributesInfoTableAR(ArgAttributesInfoTable);
823 if (ArgAttrID >= ArgAttributesInfoTableAR.size())
824 llvm_unreachable("Invalid arguments attribute ID");
825
826 auto [StartIndex, NumAttrs] = ArgAttributesInfoTableAR[ArgAttrID];
827 for (unsigned Idx = 0; Idx < NumAttrs; ++Idx) {{
828 auto [ArgNo, ArgAttrID] = ArgAttrIdTable[StartIndex + Idx];
829 AS[Idx] = {{ArgNo,
830 getIntrinsicArgAttributeSet(C, ArgAttrID, FT->getContainedType(ArgNo))};
831 }
832 if (FnAttrID != {}) {
833 AS[NumAttrs++] = {{AttributeList::FunctionIndex,
834 getIntrinsicFnAttributeSet(C, FnAttrID)};
835 }
836 return AttributeList::get(C, ArrayRef(AS, NumAttrs));
837}
838
839AttributeSet Intrinsic::getFnAttributes(LLVMContext &C, ID id) {{
840 if (id == 0)
841 return AttributeSet();
842 auto [FnAttrID, _] = unpackID(IntrinsicsToAttributesMap[id - 1]);
843 if (FnAttrID == {})
844 return AttributeSet();
845 return getIntrinsicFnAttributeSet(C, FnAttrID);
846}
847)",
848 Vals: UniqAttributesBitSize, Vals&: MaxNumAttrs, Vals: NoFunctionAttrsID,
849 Vals: NoFunctionAttrsID);
850}
851
852void IntrinsicEmitter::EmitIntrinsicToPrettyPrintTable(
853 const CodeGenIntrinsicTable &Ints, raw_ostream &OS) {
854 EmitIntrinsicBitTable(Ints, OS, Guard: "GET_INTRINSIC_PRETTY_PRINT_TABLE", TableName: "PPTable",
855 Comment: "Intrinsic ID to pretty print bitset.",
856 GetProperty: [](const CodeGenIntrinsic &Int) {
857 return !Int.PrettyPrintFunctions.empty();
858 });
859}
860
861void IntrinsicEmitter::EmitPrettyPrintArguments(
862 const CodeGenIntrinsicTable &Ints, raw_ostream &OS) {
863 IfDefEmitter IfDef(OS, "GET_INTRINSIC_PRETTY_PRINT_ARGUMENTS");
864 OS << R"(
865void Intrinsic::printImmArg(ID IID, unsigned ArgIdx, raw_ostream &OS, const Constant *ImmArgVal) {
866 using namespace Intrinsic;
867 switch (IID) {
868)";
869
870 for (const CodeGenIntrinsic &Int : Ints) {
871 if (Int.PrettyPrintFunctions.empty())
872 continue;
873
874 OS << " case " << Int.EnumName << ":\n";
875 OS << " switch (ArgIdx) {\n";
876 for (const auto [ArgIdx, ArgName, FuncName] : Int.PrettyPrintFunctions) {
877 OS << " case " << ArgIdx << ":\n";
878 OS << " OS << \"" << ArgName << "=\";\n";
879 if (!FuncName.empty()) {
880 OS << " ";
881 if (!Int.TargetPrefix.empty())
882 OS << Int.TargetPrefix << "::";
883 OS << FuncName << "(OS, ImmArgVal);\n";
884 }
885 OS << " return;\n";
886 }
887 OS << " }\n";
888 OS << " break;\n";
889 }
890 OS << R"( default:
891 break;
892 }
893})";
894}
895
896void IntrinsicEmitter::EmitIntrinsicToBuiltinMap(
897 const CodeGenIntrinsicTable &Ints, bool IsClang, raw_ostream &OS) {
898 StringRef CompilerName = IsClang ? "Clang" : "MS";
899 StringRef UpperCompilerName = IsClang ? "CLANG" : "MS";
900
901 // map<TargetPrefix, pair<map<BuiltinName, EnumName>, CommonPrefix>.
902 // Note that we iterate over both the maps in the code below and both
903 // iterations need to iterate in sorted key order. For the inner map, entries
904 // need to be emitted in the sorted order of `BuiltinName` with `CommonPrefix`
905 // rempved, because we use std::lower_bound to search these entries. For the
906 // outer map as well, entries need to be emitted in sorter order of
907 // `TargetPrefix` as we use std::lower_bound to search these entries.
908 using BIMEntryTy =
909 std::pair<std::map<StringRef, StringRef>, std::optional<StringRef>>;
910 std::map<StringRef, BIMEntryTy> BuiltinMap;
911
912 for (const CodeGenIntrinsic &Int : Ints) {
913 StringRef BuiltinName = IsClang ? Int.ClangBuiltinName : Int.MSBuiltinName;
914 if (BuiltinName.empty())
915 continue;
916 // Get the map for this target prefix.
917 auto &[Map, CommonPrefix] = BuiltinMap[Int.TargetPrefix];
918
919 if (!Map.try_emplace(k: BuiltinName, args: Int.EnumName).second)
920 PrintFatalError(ErrorLoc: Int.TheDef->getLoc(),
921 Msg: "Intrinsic '" + Int.TheDef->getName() + "': duplicate " +
922 CompilerName + " builtin name!");
923
924 // Update common prefix.
925 if (!CommonPrefix) {
926 // For the first builtin for this target, initialize the common prefix.
927 CommonPrefix = BuiltinName;
928 continue;
929 }
930
931 // Update the common prefix. Note that this assumes that `take_front` will
932 // never set the `Data` pointer in CommonPrefix to nullptr.
933 const char *Mismatch = mismatch(Range1&: *CommonPrefix, Range2&: BuiltinName).first;
934 *CommonPrefix = CommonPrefix->take_front(N: Mismatch - CommonPrefix->begin());
935 }
936
937 // Populate the string table with the names of all the builtins after
938 // removing this common prefix.
939 StringToOffsetTable Table;
940 for (const auto &[TargetPrefix, Entry] : BuiltinMap) {
941 auto &[Map, CommonPrefix] = Entry;
942 for (auto &[BuiltinName, EnumName] : Map) {
943 StringRef Suffix = BuiltinName.substr(Start: CommonPrefix->size());
944 Table.GetOrAddStringOffset(Str: Suffix);
945 }
946 }
947
948 IfDefEmitter IfDef(
949 OS,
950 formatv(Fmt: "GET_LLVM_INTRINSIC_FOR_{}_BUILTIN", Vals&: UpperCompilerName).str());
951 OS << formatv(Fmt: R"(
952// Get the LLVM intrinsic that corresponds to a builtin. This is used by the
953// C front-end. The builtin name is passed in as BuiltinName, and a target
954// prefix (e.g. 'ppc') is passed in as TargetPrefix.
955Intrinsic::ID
956Intrinsic::getIntrinsicFor{}Builtin(StringRef TargetPrefix,
957 StringRef BuiltinName) {{
958 using namespace Intrinsic;
959)",
960 Vals&: CompilerName);
961
962 if (BuiltinMap.empty()) {
963 OS << "return not_intrinsic;\n";
964 return;
965 }
966
967 if (!Table.empty()) {
968 Table.EmitStringTableDef(OS, Name: "BuiltinNames");
969
970 OS << R"(
971 struct BuiltinEntry {
972 ID IntrinsicID;
973 unsigned StrTabOffset;
974 const char *getName() const { return BuiltinNames[StrTabOffset].data(); }
975 bool operator<(StringRef RHS) const {
976 return strncmp(getName(), RHS.data(), RHS.size()) < 0;
977 }
978 };
979
980)";
981 }
982
983 // Emit a per target table of bultin names.
984 bool HasTargetIndependentBuiltins = false;
985 StringRef TargetIndepndentCommonPrefix;
986 for (const auto &[TargetPrefix, Entry] : BuiltinMap) {
987 const auto &[Map, CommonPrefix] = Entry;
988 if (!TargetPrefix.empty()) {
989 OS << formatv(Fmt: " // Builtins for {0}.\n", Vals: TargetPrefix);
990 } else {
991 OS << " // Target independent builtins.\n";
992 HasTargetIndependentBuiltins = true;
993 TargetIndepndentCommonPrefix = *CommonPrefix;
994 }
995
996 // Emit the builtin table for this target prefix.
997 OS << formatv(Fmt: " static constexpr BuiltinEntry {}Names[] = {{\n",
998 Vals: TargetPrefix);
999 for (const auto &[BuiltinName, EnumName] : Map) {
1000 StringRef Suffix = BuiltinName.substr(Start: CommonPrefix->size());
1001 OS << formatv(Fmt: " {{{}, {}}, // {}\n", Vals: EnumName,
1002 Vals: *Table.GetStringOffset(Str: Suffix), Vals: BuiltinName);
1003 }
1004 OS << formatv(Fmt: " }; // {}Names\n\n", Vals: TargetPrefix);
1005 }
1006
1007 // After emitting the builtin tables for all targets, emit a lookup table for
1008 // all targets. We will use binary search, similar to the table for builtin
1009 // names to lookup into this table.
1010 OS << R"(
1011 struct TargetEntry {
1012 StringLiteral TargetPrefix;
1013 ArrayRef<BuiltinEntry> Names;
1014 StringLiteral CommonPrefix;
1015 bool operator<(StringRef RHS) const {
1016 return TargetPrefix < RHS;
1017 };
1018 };
1019 static constexpr TargetEntry TargetTable[] = {
1020)";
1021
1022 for (const auto &[TargetPrefix, Entry] : BuiltinMap) {
1023 const auto &[Map, CommonPrefix] = Entry;
1024 if (TargetPrefix.empty())
1025 continue;
1026 OS << formatv(Fmt: R"( {{"{0}", {0}Names, "{1}"},)", Vals: TargetPrefix,
1027 Vals: CommonPrefix)
1028 << "\n";
1029 }
1030 OS << " };\n";
1031
1032 // Now for the actual lookup, first check the target independent table if
1033 // we emitted one.
1034 if (HasTargetIndependentBuiltins) {
1035 OS << formatv(Fmt: R"(
1036 // Check if it's a target independent builtin.
1037 // Copy the builtin name so we can use it in consume_front without clobbering
1038 // if for the lookup in the target specific table.
1039 StringRef Suffix = BuiltinName;
1040 if (Suffix.consume_front("{}")) {{
1041 auto II = lower_bound(Names, Suffix);
1042 if (II != std::end(Names) && II->getName() == Suffix)
1043 return II->IntrinsicID;
1044 }
1045)",
1046 Vals&: TargetIndepndentCommonPrefix);
1047 }
1048
1049 // If a target independent builtin was not found, lookup the target specific.
1050 OS << R"(
1051 auto TI = lower_bound(TargetTable, TargetPrefix);
1052 if (TI == std::end(TargetTable) || TI->TargetPrefix != TargetPrefix)
1053 return not_intrinsic;
1054 // This is the last use of BuiltinName, so no need to copy before using it in
1055 // consume_front.
1056 if (!BuiltinName.consume_front(TI->CommonPrefix))
1057 return not_intrinsic;
1058 auto II = lower_bound(TI->Names, BuiltinName);
1059 if (II == std::end(TI->Names) || II->getName() != BuiltinName)
1060 return not_intrinsic;
1061 return II->IntrinsicID;
1062}
1063)";
1064}
1065
1066static TableGen::Emitter::OptClass<IntrinsicEmitterOpt</*Enums=*/true>>
1067 X("gen-intrinsic-enums", "Generate intrinsic enums");
1068
1069static TableGen::Emitter::OptClass<IntrinsicEmitterOpt</*Enums=*/false>>
1070 Y("gen-intrinsic-impl", "Generate intrinsic implementation code");
1071