1//===----------------------------------------------------------------------===//
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 generates hlsl_alias_intrinsics_gen.inc (alias
10// overloads) and hlsl_inline_intrinsics_gen.inc (inline/detail overloads) for
11// HLSL intrinsic functions.
12//
13//===----------------------------------------------------------------------===//
14
15#include "TableGenBackends.h"
16#include "llvm/ADT/STLExtras.h"
17#include "llvm/ADT/SmallVector.h"
18#include "llvm/ADT/StringExtras.h"
19#include "llvm/ADT/StringRef.h"
20#include "llvm/ADT/StringSwitch.h"
21#include "llvm/Support/ErrorHandling.h"
22#include "llvm/Support/raw_ostream.h"
23#include "llvm/TableGen/Record.h"
24
25using namespace llvm;
26
27/// Minimum shader model version that supports 16-bit types.
28static constexpr StringLiteral SM6_2 = "6.2";
29
30//===----------------------------------------------------------------------===//
31// Type name helpers
32//===----------------------------------------------------------------------===//
33
34static std::string getVectorTypeName(StringRef ElemType, unsigned N) {
35 return (ElemType + Twine(N)).str();
36}
37
38static std::string getMatrixTypeName(StringRef ElemType, unsigned Rows,
39 unsigned Cols) {
40 return (ElemType + Twine(Rows) + "x" + Twine(Cols)).str();
41}
42
43/// Get the fixed type name string for a VectorType or HLSLType record.
44static std::string getFixedTypeName(const Record *R) {
45 if (R->isSubClassOf(Name: "VectorType"))
46 return getVectorTypeName(
47 ElemType: R->getValueAsDef(FieldName: "ElementType")->getValueAsString(FieldName: "Name"),
48 N: R->getValueAsInt(FieldName: "Size"));
49 assert(R->isSubClassOf("HLSLType"));
50 return R->getValueAsString(FieldName: "Name").str();
51}
52
53/// For a VectorType, return its ElementType record; for an HLSLType, return
54/// the record itself (it is already a scalar element type).
55static const Record *getElementTypeRecord(const Record *R) {
56 if (R->isSubClassOf(Name: "VectorType"))
57 return R->getValueAsDef(FieldName: "ElementType");
58 assert(R->isSubClassOf("HLSLType"));
59 return R;
60}
61
62//===----------------------------------------------------------------------===//
63// Type information
64//===----------------------------------------------------------------------===//
65
66namespace {
67
68/// Classifies how a type varies across overloads.
69enum TypeKindEnum {
70 TK_Varying = 0, ///< Type matches the full varying type (e.g. float3).
71 TK_ElemType = 1, ///< Type is the scalar element type (e.g. float).
72 TK_VaryingShape = 2, ///< Type uses the varying shape with a fixed element.
73 TK_FixedType = 3, ///< Type is a fixed concrete type (e.g. "half2").
74 TK_Void = 4 ///< Type is void (only valid for return types).
75};
76
77/// Metadata describing how a type (argument or return) varies across overloads.
78struct TypeInfo {
79 /// Classification of how this type varies across overloads.
80 TypeKindEnum Kind = TK_Varying;
81
82 /// Fixed type name (e.g. "half2") for types with a concrete type that does
83 /// not vary across overloads. Empty for varying types.
84 std::string FixedType;
85
86 /// Element type name for TK_VaryingShape types (e.g. "bool" for
87 /// VaryingShape<BoolTy>). Empty for other type kinds.
88 StringRef ShapeElemType;
89
90 /// Explicit parameter name (e.g. "eta"). Empty to use the default "p0",
91 /// "p1", ... naming. Only meaningful for argument types.
92 StringRef Name;
93
94 /// Construct a TypeInfo from a TableGen record.
95 static TypeInfo resolve(const Record *Rec) {
96 TypeInfo TI;
97 if (Rec->getName() == "VoidTy") {
98 TI.Kind = TK_Void;
99 } else if (Rec->getName() == "Varying") {
100 TI.Kind = TK_Varying;
101 } else if (Rec->getName() == "VaryingElemType") {
102 TI.Kind = TK_ElemType;
103 } else if (Rec->isSubClassOf(Name: "VaryingShape")) {
104 TI.Kind = TK_VaryingShape;
105 TI.ShapeElemType =
106 Rec->getValueAsDef(FieldName: "ElementType")->getValueAsString(FieldName: "Name");
107 } else if (Rec->isSubClassOf(Name: "VectorType") ||
108 Rec->isSubClassOf(Name: "HLSLType")) {
109 TI.Kind = TK_FixedType;
110 TI.FixedType = getFixedTypeName(R: Rec);
111 } else {
112 llvm_unreachable("unhandled record for type resolution");
113 }
114 return TI;
115 }
116
117 /// Resolve this type to a concrete type name string.
118 /// \p ElemType is the scalar element type for the current overload.
119 /// \p FormatVarying formats a scalar element type into the shaped type name.
120 std::string
121 toTypeString(StringRef ElemType,
122 function_ref<std::string(StringRef)> FormatVarying) const {
123 switch (Kind) {
124 case TK_Void:
125 return "void";
126 case TK_Varying:
127 return FormatVarying(ElemType);
128 case TK_ElemType:
129 return ElemType.str();
130 case TK_VaryingShape:
131 return FormatVarying(ShapeElemType);
132 case TK_FixedType:
133 assert(!FixedType.empty() && "TK_FixedType requires non-empty FixedType");
134 return FixedType;
135 }
136 llvm_unreachable("unhandled TypeKindEnum");
137 }
138};
139
140} // anonymous namespace
141
142//===----------------------------------------------------------------------===//
143// Availability helpers
144//===----------------------------------------------------------------------===//
145
146static void emitAvailability(raw_ostream &OS, StringRef Version,
147 bool Use16Bit = false) {
148 if (Use16Bit) {
149 OS << "_HLSL_16BIT_AVAILABILITY(shadermodel, " << SM6_2;
150 if (!Version.empty())
151 OS << ", " << Version;
152 OS << ")\n";
153 } else
154 OS << "_HLSL_AVAILABILITY(shadermodel, " << Version << ")\n";
155}
156
157static std::string getVersionString(const Record *SM) {
158 unsigned Major = SM->getValueAsInt(FieldName: "Major");
159 unsigned Minor = SM->getValueAsInt(FieldName: "Minor");
160 if (Major == 0 && Minor == 0)
161 return "";
162 return (Twine(Major) + "." + Twine(Minor)).str();
163}
164
165//===----------------------------------------------------------------------===//
166// Type work item — describes one element type to emit overloads for
167//===----------------------------------------------------------------------===//
168
169namespace {
170
171/// A single entry in the worklist of types to process for an intrinsic.
172struct TypeWorkItem {
173 /// Element type name (e.g. "half", "float"). Empty for fixed-arg-only
174 /// intrinsics with no type expansion.
175 StringRef ElemType;
176
177 /// Version string for the availability attribute (e.g. "6.2"). Empty if
178 /// no availability annotation is needed.
179 std::string Availability;
180
181 /// If true, emit _HLSL_16BIT_AVAILABILITY instead of _HLSL_AVAILABILITY.
182 bool Use16BitAvail = false;
183
184 /// If true, wrap overloads in #ifdef __HLSL_ENABLE_16_BIT / #endif.
185 bool NeedsIfdefGuard = false;
186};
187
188} // anonymous namespace
189
190/// Fixed canonical ordering for overload types. Types are grouped as:
191/// 0: conditionally-16-bit (half)
192/// 1-2: 16-bit integers (int16_t, uint16_t) — ifdef-guarded
193/// 3+: regular types (bool, int, uint, int64_t, uint64_t, float, double)
194/// Within each group, signed precedes unsigned, smaller precedes larger,
195/// and integer types precede floating-point types.
196static int getTypeSortPriority(const Record *ET) {
197 return StringSwitch<int>(ET->getValueAsString(FieldName: "Name"))
198 .Case(S: "half", Value: 0)
199 .Case(S: "int16_t", Value: 1)
200 .Case(S: "uint16_t", Value: 2)
201 .Case(S: "bool", Value: 3)
202 .Case(S: "int", Value: 4)
203 .Case(S: "uint", Value: 5)
204 .Case(S: "int64_t", Value: 7)
205 .Case(S: "uint64_t", Value: 8)
206 .Case(S: "float", Value: 9)
207 .Case(S: "double", Value: 10)
208 .Default(Value: 11);
209}
210
211//===----------------------------------------------------------------------===//
212// Overload context — shared state across all overloads of one intrinsic
213//===----------------------------------------------------------------------===//
214
215namespace {
216
217/// Shared state for emitting all overloads of a single HLSL intrinsic.
218struct OverloadContext {
219 /// Output stream to write generated code to.
220 raw_ostream &OS;
221
222 /// Builtin name for _HLSL_BUILTIN_ALIAS (e.g. "__builtin_hlsl_dot").
223 /// Empty for inline/detail intrinsics.
224 StringRef Builtin;
225
226 /// __detail helper function to call (e.g. "refract_impl").
227 /// Empty for alias and inline-body intrinsics.
228 StringRef DetailFunc;
229
230 /// Literal inline function body (e.g. "return p0;").
231 /// Empty for alias and detail intrinsics.
232 StringRef Body;
233
234 /// The HLSL function name to emit (e.g. "dot", "refract").
235 StringRef FuncName;
236
237 /// Metadata describing the return type and its variation behavior.
238 TypeInfo RetType;
239
240 /// Per-argument metadata describing type and variation behavior.
241 SmallVector<TypeInfo, 4> Args;
242
243 /// Whether to emit the function as constexpr.
244 bool IsConstexpr = false;
245
246 /// Whether to emit the __attribute__((convergent)) annotation.
247 bool IsConvergent = false;
248
249 /// Whether any fixed arg has a 16-bit integer type (e.g. int16_t).
250 bool Uses16BitType = false;
251
252 /// Whether any fixed arg has a conditionally-16-bit type (half).
253 bool UsesConditionally16BitType = false;
254
255 explicit OverloadContext(raw_ostream &OS) : OS(OS) {}
256};
257
258} // anonymous namespace
259
260/// Emit a complete function declaration or definition with pre-resolved types.
261static void emitDeclaration(const OverloadContext &Ctx, StringRef RetType,
262 ArrayRef<std::string> ArgTypes) {
263 raw_ostream &OS = Ctx.OS;
264 bool IsDetail = !Ctx.DetailFunc.empty();
265 bool IsInline = !Ctx.Body.empty();
266 bool HasBody = IsDetail || IsInline;
267
268 bool EmitNames = HasBody || llvm::any_of(Range: Ctx.Args, P: [](const TypeInfo &A) {
269 return !A.Name.empty();
270 });
271
272 auto GetParamName = [&](unsigned I) -> std::string {
273 if (!Ctx.Args[I].Name.empty())
274 return Ctx.Args[I].Name.str();
275 return ("p" + Twine(I)).str();
276 };
277
278 if (!HasBody)
279 OS << "_HLSL_BUILTIN_ALIAS(" << Ctx.Builtin << ")\n";
280 if (Ctx.IsConvergent)
281 OS << "__attribute__((convergent)) ";
282 if (HasBody)
283 OS << (Ctx.IsConstexpr ? "constexpr " : "inline ");
284 OS << RetType << " " << Ctx.FuncName << "(";
285
286 {
287 ListSeparator LS;
288 for (unsigned I = 0, N = ArgTypes.size(); I < N; ++I) {
289 OS << LS << ArgTypes[I];
290 if (EmitNames)
291 OS << " " << GetParamName(I);
292 }
293 }
294
295 if (IsDetail) {
296 OS << ") {\n return __detail::" << Ctx.DetailFunc << "(";
297 ListSeparator LS;
298 for (unsigned I = 0, N = ArgTypes.size(); I < N; ++I)
299 OS << LS << GetParamName(I);
300 OS << ");\n}\n";
301 } else if (IsInline) {
302 OS << ") { " << Ctx.Body << " }\n";
303 } else {
304 OS << ");\n";
305 }
306}
307
308/// Emit a single overload declaration by resolving all types through
309/// \p FormatVarying, which maps element types to their shaped form.
310static void emitOverload(const OverloadContext &Ctx, StringRef ElemType,
311 function_ref<std::string(StringRef)> FormatVarying) {
312 std::string RetType = Ctx.RetType.toTypeString(ElemType, FormatVarying);
313 SmallVector<std::string> ArgTypes;
314 for (const TypeInfo &TI : Ctx.Args)
315 ArgTypes.push_back(Elt: TI.toTypeString(ElemType, FormatVarying));
316 emitDeclaration(Ctx, RetType, ArgTypes);
317}
318
319/// Emit a scalar overload for the given element type.
320static void emitScalarOverload(const OverloadContext &Ctx, StringRef ElemType) {
321 emitOverload(Ctx, ElemType, FormatVarying: [](StringRef ET) { return ET.str(); });
322}
323
324/// Emit a vector overload for the given element type and vector size.
325static void emitVectorOverload(const OverloadContext &Ctx, StringRef ElemType,
326 unsigned VecSize) {
327 emitOverload(Ctx, ElemType, FormatVarying: [VecSize](StringRef ET) {
328 return getVectorTypeName(ElemType: ET, N: VecSize);
329 });
330}
331
332/// Emit a dependent-size vector template overload for the given element type.
333static void emitLongVectorOverload(const OverloadContext &Ctx,
334 StringRef ElemType) {
335 emitOverload(Ctx, ElemType, FormatVarying: [](StringRef ET) {
336 return ("vector<__detail::enable_if_t<(N > 4), " + ET + ">, N>").str();
337 });
338}
339
340/// Emit a matrix overload for the given element type and matrix dimensions.
341static void emitMatrixOverload(const OverloadContext &Ctx, StringRef ElemType,
342 unsigned Rows, unsigned Cols) {
343 emitOverload(Ctx, ElemType, FormatVarying: [Rows, Cols](StringRef ET) {
344 return getMatrixTypeName(ElemType: ET, Rows, Cols);
345 });
346}
347
348//===----------------------------------------------------------------------===//
349// Main emission logic
350//===----------------------------------------------------------------------===//
351
352/// Build an OverloadContext from an HLSLBuiltin record.
353static void buildOverloadContext(const Record *R, OverloadContext &Ctx) {
354 Ctx.Builtin = R->getValueAsString(FieldName: "Builtin");
355 Ctx.DetailFunc = R->getValueAsString(FieldName: "DetailFunc");
356 Ctx.Body = R->getValueAsString(FieldName: "Body");
357 Ctx.FuncName = R->getValueAsString(FieldName: "Name");
358 Ctx.IsConstexpr = R->getValueAsBit(FieldName: "IsConstexpr");
359 Ctx.IsConvergent = R->getValueAsBit(FieldName: "IsConvergent");
360
361 // Note use of 16-bit fixed types in the overload context.
362 auto Update16BitFlags = [&Ctx](const Record *Rec) {
363 const Record *ElemTy = getElementTypeRecord(R: Rec);
364 Ctx.Uses16BitType |= ElemTy->getValueAsBit(FieldName: "Is16Bit");
365 Ctx.UsesConditionally16BitType |=
366 ElemTy->getValueAsBit(FieldName: "IsConditionally16Bit");
367 };
368
369 // Resolve return and argument types.
370 const Record *RetRec = R->getValueAsDef(FieldName: "ReturnType");
371 Ctx.RetType = TypeInfo::resolve(Rec: RetRec);
372 if (Ctx.RetType.Kind == TK_FixedType)
373 Update16BitFlags(RetRec);
374
375 std::vector<const Record *> ArgRecords = R->getValueAsListOfDefs(FieldName: "Args");
376 std::vector<StringRef> ParamNames = R->getValueAsListOfStrings(FieldName: "ParamNames");
377
378 for (const auto &[I, Arg] : llvm::enumerate(First&: ArgRecords)) {
379 TypeInfo TI = TypeInfo::resolve(Rec: Arg);
380 if (I < ParamNames.size())
381 TI.Name = ParamNames[I];
382 if (TI.Kind == TK_FixedType)
383 Update16BitFlags(Arg);
384 Ctx.Args.push_back(Elt: TI);
385 }
386}
387
388/// Build the worklist of element types to emit overloads for, sorted in
389/// canonical order (see getTypeSortPriority).
390static void buildWorklist(const Record *R,
391 SmallVectorImpl<TypeWorkItem> &Worklist,
392 const OverloadContext &Ctx) {
393 const Record *AvailRec = R->getValueAsDef(FieldName: "Availability");
394 std::string Availability = getVersionString(SM: AvailRec);
395 bool AvailabilityIsAtLeastSM6_2 = AvailRec->getValueAsInt(FieldName: "Major") > 6 ||
396 (AvailRec->getValueAsInt(FieldName: "Major") == 6 &&
397 AvailRec->getValueAsInt(FieldName: "Minor") >= 2);
398
399 std::vector<const Record *> VaryingTypeRecords =
400 R->getValueAsListOfDefs(FieldName: "VaryingTypes");
401
402 // Populate the availability and guard fields of a TypeWorkItem based on
403 // whether the type is 16-bit, conditionally 16-bit, or a regular type.
404 auto SetAvailability = [&](TypeWorkItem &Item, bool Is16Bit,
405 bool IsCond16Bit) {
406 Item.NeedsIfdefGuard = Is16Bit;
407 if (Is16Bit || IsCond16Bit) {
408 if (AvailabilityIsAtLeastSM6_2) {
409 Item.Availability = Availability;
410 } else {
411 Item.Use16BitAvail = IsCond16Bit;
412 if (IsCond16Bit)
413 Item.Availability = Availability;
414 else
415 Item.Availability = SM6_2;
416 }
417 } else {
418 Item.Availability = Availability;
419 }
420 };
421
422 // If no Varying types are specified, just add a single work item.
423 // This is for HLSLBuiltin records that don't use Varying types.
424 if (VaryingTypeRecords.empty()) {
425 TypeWorkItem Item;
426 SetAvailability(Item, Ctx.Uses16BitType, Ctx.UsesConditionally16BitType);
427 Worklist.push_back(Elt: Item);
428 return;
429 }
430
431 // Sort Varying types so that overloads are always emitted in canonical order.
432 llvm::sort(C&: VaryingTypeRecords, Comp: [](const Record *A, const Record *B) {
433 return getTypeSortPriority(ET: A) < getTypeSortPriority(ET: B);
434 });
435
436 // Add a work item for each Varying element type.
437 for (const Record *ElemTy : VaryingTypeRecords) {
438 TypeWorkItem Item;
439 Item.ElemType = ElemTy->getValueAsString(FieldName: "Name");
440 bool Is16Bit = Ctx.Uses16BitType || ElemTy->getValueAsBit(FieldName: "Is16Bit");
441 bool IsCond16Bit = Ctx.UsesConditionally16BitType ||
442 ElemTy->getValueAsBit(FieldName: "IsConditionally16Bit");
443 SetAvailability(Item, Is16Bit, IsCond16Bit);
444 Worklist.push_back(Elt: Item);
445 }
446}
447
448/// Emit a Doxygen documentation comment from the Doc field.
449static void emitDocComment(raw_ostream &OS, const Record *R) {
450 StringRef Doc = R->getValueAsString(FieldName: "Doc");
451 if (Doc.empty())
452 return;
453 Doc = Doc.trim();
454 SmallVector<StringRef> DocLines;
455 Doc.split(A&: DocLines, Separator: '\n');
456 for (StringRef Line : DocLines) {
457 if (Line.empty())
458 OS << "///\n";
459 else
460 OS << "/// " << Line << "\n";
461 }
462}
463
464/// Process the worklist: emit all shape variants for each type with
465/// availability annotations and #ifdef guards.
466static void emitWorklistOverloads(raw_ostream &OS, const OverloadContext &Ctx,
467 ArrayRef<TypeWorkItem> Worklist,
468 bool EmitScalarOverload,
469 ArrayRef<int64_t> VectorSizes,
470 bool EmitLongVectorOverload,
471 ArrayRef<const Record *> MatrixDimensions) {
472 bool InIfdef = false;
473 for (size_t I = 0, E = Worklist.size(); I != E; ++I) {
474 const TypeWorkItem &Item = Worklist[I];
475 if (Item.NeedsIfdefGuard && !InIfdef) {
476 OS << "#ifdef __HLSL_ENABLE_16_BIT\n";
477 InIfdef = true;
478 }
479
480 auto EmitAvail = [&]() {
481 if (!Item.Availability.empty() || Item.Use16BitAvail)
482 emitAvailability(OS, Version: Item.Availability, Use16Bit: Item.Use16BitAvail);
483 };
484
485 if (EmitScalarOverload) {
486 EmitAvail();
487 emitScalarOverload(Ctx, ElemType: Item.ElemType);
488 }
489 for (int64_t N : VectorSizes) {
490 EmitAvail();
491 emitVectorOverload(Ctx, ElemType: Item.ElemType, VecSize: N);
492 }
493 if (EmitLongVectorOverload) {
494 OS << "template <int N>\n";
495 EmitAvail();
496 emitLongVectorOverload(Ctx, ElemType: Item.ElemType);
497 }
498 for (const Record *MD : MatrixDimensions) {
499 EmitAvail();
500 emitMatrixOverload(Ctx, ElemType: Item.ElemType, Rows: MD->getValueAsInt(FieldName: "Rows"),
501 Cols: MD->getValueAsInt(FieldName: "Cols"));
502 }
503
504 if (InIfdef) {
505 bool NextIsUnguarded = (I + 1 == E) || !Worklist[I + 1].NeedsIfdefGuard;
506 if (NextIsUnguarded) {
507 OS << "#endif\n";
508 InIfdef = false;
509 }
510 }
511
512 OS << "\n";
513 }
514}
515
516/// Emit all overloads for a single HLSLBuiltin record.
517static void emitBuiltinOverloads(raw_ostream &OS, const Record *R) {
518 OverloadContext Ctx(OS);
519 buildOverloadContext(R, Ctx);
520
521 SmallVector<TypeWorkItem> Worklist;
522 buildWorklist(R, Worklist, Ctx);
523
524 emitDocComment(OS, R);
525 OS << "// " << Ctx.FuncName << " overloads\n";
526
527 // Emit a scalar overload if a scalar Varying overload was requested.
528 // If no Varying types are used at all, emit a scalar overload to handle
529 // emitting a single overload for fixed-typed args or arg-less functions.
530 bool EmitScalarOverload = R->getValueAsBit(FieldName: "VaryingScalar") ||
531 R->getValueAsListOfDefs(FieldName: "VaryingTypes").empty();
532
533 std::vector<int64_t> VectorSizes = R->getValueAsListOfInts(FieldName: "VaryingVecSizes");
534 bool EmitLongVectorOverload = R->getValueAsBit(FieldName: "VaryingLongVector");
535 std::vector<const Record *> MatrixDimensions =
536 R->getValueAsListOfDefs(FieldName: "VaryingMatDims");
537
538 // Sort vector sizes and matrix dimensions for consistent output order.
539 llvm::sort(C&: VectorSizes);
540 llvm::sort(C&: MatrixDimensions, Comp: [](const Record *A, const Record *B) {
541 int RowA = A->getValueAsInt(FieldName: "Rows"), RowB = B->getValueAsInt(FieldName: "Rows");
542 if (RowA != RowB)
543 return RowA < RowB;
544 return A->getValueAsInt(FieldName: "Cols") < B->getValueAsInt(FieldName: "Cols");
545 });
546
547 emitWorklistOverloads(OS, Ctx, Worklist, EmitScalarOverload, VectorSizes,
548 EmitLongVectorOverload, MatrixDimensions);
549}
550
551/// Emit alias overloads for a single HLSLBuiltin record.
552/// Skips records that have inline bodies (DetailFunc or Body).
553static void emitAliasBuiltin(raw_ostream &OS, const Record *R) {
554 if (!R->getValueAsString(FieldName: "DetailFunc").empty() ||
555 !R->getValueAsString(FieldName: "Body").empty())
556 return;
557 emitBuiltinOverloads(OS, R);
558}
559
560/// Emit inline overloads for a single HLSLBuiltin record.
561/// Skips records that are pure alias declarations.
562static void emitInlineBuiltin(raw_ostream &OS, const Record *R) {
563 if (R->getValueAsString(FieldName: "DetailFunc").empty() &&
564 R->getValueAsString(FieldName: "Body").empty())
565 return;
566 emitBuiltinOverloads(OS, R);
567}
568
569void clang::EmitHLSLAliasIntrinsics(const RecordKeeper &Records,
570 raw_ostream &OS) {
571 OS << "// This file is auto-generated by clang-tblgen from "
572 "HLSLIntrinsics.td.\n";
573 OS << "// Do not edit this file directly.\n\n";
574
575 for (const Record *R : Records.getAllDerivedDefinitions(ClassName: "HLSLBuiltin"))
576 emitAliasBuiltin(OS, R);
577}
578
579void clang::EmitHLSLInlineIntrinsics(const RecordKeeper &Records,
580 raw_ostream &OS) {
581 OS << "// This file is auto-generated by clang-tblgen from "
582 "HLSLIntrinsics.td.\n";
583 OS << "// Do not edit this file directly.\n\n";
584
585 for (const Record *R : Records.getAllDerivedDefinitions(ClassName: "HLSLBuiltin"))
586 emitInlineBuiltin(OS, R);
587}
588