Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/llvm/utils/TableGen/DXILEmitter.cpp
35258 views
1
//===- DXILEmitter.cpp - DXIL operation Emitter ---------------------------===//
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
// DXILEmitter uses the descriptions of DXIL operation to construct enum and
10
// helper functions for DXIL operation.
11
//
12
//===----------------------------------------------------------------------===//
13
14
#include "Basic/SequenceToOffsetTable.h"
15
#include "Common/CodeGenTarget.h"
16
#include "llvm/ADT/STLExtras.h"
17
#include "llvm/ADT/SmallSet.h"
18
#include "llvm/ADT/SmallVector.h"
19
#include "llvm/ADT/StringSet.h"
20
#include "llvm/ADT/StringSwitch.h"
21
#include "llvm/CodeGenTypes/MachineValueType.h"
22
#include "llvm/Support/DXILABI.h"
23
#include "llvm/TableGen/Record.h"
24
#include "llvm/TableGen/TableGenBackend.h"
25
#include <string>
26
27
using namespace llvm;
28
using namespace llvm::dxil;
29
30
namespace {
31
32
struct DXILShaderModel {
33
int Major = 0;
34
int Minor = 0;
35
};
36
37
struct DXILOperationDesc {
38
std::string OpName; // name of DXIL operation
39
int OpCode; // ID of DXIL operation
40
StringRef OpClass; // name of the opcode class
41
StringRef Doc; // the documentation description of this instruction
42
SmallVector<Record *> OpTypes; // Vector of operand type records -
43
// return type is at index 0
44
SmallVector<std::string>
45
OpAttributes; // operation attribute represented as strings
46
StringRef Intrinsic; // The llvm intrinsic map to OpName. Default is "" which
47
// means no map exists
48
bool IsDeriv = false; // whether this is some kind of derivative
49
bool IsGradient = false; // whether this requires a gradient calculation
50
bool IsFeedback = false; // whether this is a sampler feedback op
51
bool IsWave =
52
false; // whether this requires in-wave, cross-lane functionality
53
bool RequiresUniformInputs = false; // whether this operation requires that
54
// all of its inputs are uniform across
55
// the wave
56
SmallVector<StringRef, 4>
57
ShaderStages; // shader stages to which this applies, empty for all.
58
DXILShaderModel ShaderModel; // minimum shader model required
59
DXILShaderModel ShaderModelTranslated; // minimum shader model required with
60
// translation by linker
61
int OverloadParamIndex; // Index of parameter with overload type.
62
// -1 : no overload types
63
SmallVector<StringRef, 4> counters; // counters for this inst.
64
DXILOperationDesc(const Record *);
65
};
66
} // end anonymous namespace
67
68
/// Return dxil::ParameterKind corresponding to input LLVMType record
69
///
70
/// \param R TableGen def record of class LLVMType
71
/// \return ParameterKind As defined in llvm/Support/DXILABI.h
72
73
static ParameterKind getParameterKind(const Record *R) {
74
auto VTRec = R->getValueAsDef("VT");
75
switch (getValueType(VTRec)) {
76
case MVT::isVoid:
77
return ParameterKind::Void;
78
case MVT::f16:
79
return ParameterKind::Half;
80
case MVT::f32:
81
return ParameterKind::Float;
82
case MVT::f64:
83
return ParameterKind::Double;
84
case MVT::i1:
85
return ParameterKind::I1;
86
case MVT::i8:
87
return ParameterKind::I8;
88
case MVT::i16:
89
return ParameterKind::I16;
90
case MVT::i32:
91
return ParameterKind::I32;
92
case MVT::fAny:
93
case MVT::iAny:
94
return ParameterKind::Overload;
95
case MVT::Other:
96
// Handle DXIL-specific overload types
97
if (R->getValueAsInt("isHalfOrFloat") || R->getValueAsInt("isI16OrI32")) {
98
return ParameterKind::Overload;
99
}
100
[[fallthrough]];
101
default:
102
llvm_unreachable("Support for specified DXIL Type not yet implemented");
103
}
104
}
105
106
/// Construct an object using the DXIL Operation records specified
107
/// in DXIL.td. This serves as the single source of reference of
108
/// the information extracted from the specified Record R, for
109
/// C++ code generated by this TableGen backend.
110
// \param R Object representing TableGen record of a DXIL Operation
111
DXILOperationDesc::DXILOperationDesc(const Record *R) {
112
OpName = R->getNameInitAsString();
113
OpCode = R->getValueAsInt("OpCode");
114
115
Doc = R->getValueAsString("Doc");
116
117
auto TypeRecs = R->getValueAsListOfDefs("OpTypes");
118
unsigned TypeRecsSize = TypeRecs.size();
119
// Populate OpTypes with return type and parameter types
120
121
// Parameter indices of overloaded parameters.
122
// This vector contains overload parameters in the order used to
123
// resolve an LLVMMatchType in accordance with convention outlined in
124
// the comment before the definition of class LLVMMatchType in
125
// llvm/IR/Intrinsics.td
126
SmallVector<int> OverloadParamIndices;
127
for (unsigned i = 0; i < TypeRecsSize; i++) {
128
auto TR = TypeRecs[i];
129
// Track operation parameter indices of any overload types
130
auto isAny = TR->getValueAsInt("isAny");
131
if (isAny == 1) {
132
// TODO: At present it is expected that all overload types in a DXIL Op
133
// are of the same type. Hence, OverloadParamIndices will have only one
134
// element. This implies we do not need a vector. However, until more
135
// (all?) DXIL Ops are added in DXIL.td, a vector is being used to flag
136
// cases this assumption would not hold.
137
if (!OverloadParamIndices.empty()) {
138
bool knownType = true;
139
// Ensure that the same overload type registered earlier is being used
140
for (auto Idx : OverloadParamIndices) {
141
if (TR != TypeRecs[Idx]) {
142
knownType = false;
143
break;
144
}
145
}
146
if (!knownType) {
147
report_fatal_error("Specification of multiple differing overload "
148
"parameter types not yet supported",
149
false);
150
}
151
} else {
152
OverloadParamIndices.push_back(i);
153
}
154
}
155
// Populate OpTypes array according to the type specification
156
if (TR->isAnonymous()) {
157
// Check prior overload types exist
158
assert(!OverloadParamIndices.empty() &&
159
"No prior overloaded parameter found to match.");
160
// Get the parameter index of anonymous type, TR, references
161
auto OLParamIndex = TR->getValueAsInt("Number");
162
// Resolve and insert the type to that at OLParamIndex
163
OpTypes.emplace_back(TypeRecs[OLParamIndex]);
164
} else {
165
// A non-anonymous type. Just record it in OpTypes
166
OpTypes.emplace_back(TR);
167
}
168
}
169
170
// Set the index of the overload parameter, if any.
171
OverloadParamIndex = -1; // default; indicating none
172
if (!OverloadParamIndices.empty()) {
173
if (OverloadParamIndices.size() > 1)
174
report_fatal_error("Multiple overload type specification not supported",
175
false);
176
OverloadParamIndex = OverloadParamIndices[0];
177
}
178
// Get the operation class
179
OpClass = R->getValueAsDef("OpClass")->getName();
180
181
if (R->getValue("LLVMIntrinsic")) {
182
auto *IntrinsicDef = R->getValueAsDef("LLVMIntrinsic");
183
auto DefName = IntrinsicDef->getName();
184
assert(DefName.starts_with("int_") && "invalid intrinsic name");
185
// Remove the int_ from intrinsic name.
186
Intrinsic = DefName.substr(4);
187
// TODO: For now, assume that attributes of DXIL Operation are the same as
188
// that of the intrinsic. Deviations are expected to be encoded in TableGen
189
// record specification and handled accordingly here. Support to be added
190
// as needed.
191
auto IntrPropList = IntrinsicDef->getValueAsListInit("IntrProperties");
192
auto IntrPropListSize = IntrPropList->size();
193
for (unsigned i = 0; i < IntrPropListSize; i++) {
194
OpAttributes.emplace_back(IntrPropList->getElement(i)->getAsString());
195
}
196
}
197
}
198
199
/// Return a string representation of ParameterKind enum
200
/// \param Kind Parameter Kind enum value
201
/// \return std::string string representation of input Kind
202
static std::string getParameterKindStr(ParameterKind Kind) {
203
switch (Kind) {
204
case ParameterKind::Invalid:
205
return "Invalid";
206
case ParameterKind::Void:
207
return "Void";
208
case ParameterKind::Half:
209
return "Half";
210
case ParameterKind::Float:
211
return "Float";
212
case ParameterKind::Double:
213
return "Double";
214
case ParameterKind::I1:
215
return "I1";
216
case ParameterKind::I8:
217
return "I8";
218
case ParameterKind::I16:
219
return "I16";
220
case ParameterKind::I32:
221
return "I32";
222
case ParameterKind::I64:
223
return "I64";
224
case ParameterKind::Overload:
225
return "Overload";
226
case ParameterKind::CBufferRet:
227
return "CBufferRet";
228
case ParameterKind::ResourceRet:
229
return "ResourceRet";
230
case ParameterKind::DXILHandle:
231
return "DXILHandle";
232
}
233
llvm_unreachable("Unknown llvm::dxil::ParameterKind enum");
234
}
235
236
/// Return a string representation of OverloadKind enum that maps to
237
/// input LLVMType record
238
/// \param R TableGen def record of class LLVMType
239
/// \return std::string string representation of OverloadKind
240
241
static std::string getOverloadKindStr(const Record *R) {
242
auto VTRec = R->getValueAsDef("VT");
243
switch (getValueType(VTRec)) {
244
case MVT::isVoid:
245
return "OverloadKind::VOID";
246
case MVT::f16:
247
return "OverloadKind::HALF";
248
case MVT::f32:
249
return "OverloadKind::FLOAT";
250
case MVT::f64:
251
return "OverloadKind::DOUBLE";
252
case MVT::i1:
253
return "OverloadKind::I1";
254
case MVT::i8:
255
return "OverloadKind::I8";
256
case MVT::i16:
257
return "OverloadKind::I16";
258
case MVT::i32:
259
return "OverloadKind::I32";
260
case MVT::i64:
261
return "OverloadKind::I64";
262
case MVT::iAny:
263
return "OverloadKind::I16 | OverloadKind::I32 | OverloadKind::I64";
264
case MVT::fAny:
265
return "OverloadKind::HALF | OverloadKind::FLOAT | OverloadKind::DOUBLE";
266
case MVT::Other:
267
// Handle DXIL-specific overload types
268
{
269
if (R->getValueAsInt("isHalfOrFloat")) {
270
return "OverloadKind::HALF | OverloadKind::FLOAT";
271
} else if (R->getValueAsInt("isI16OrI32")) {
272
return "OverloadKind::I16 | OverloadKind::I32";
273
}
274
}
275
[[fallthrough]];
276
default:
277
llvm_unreachable(
278
"Support for specified parameter OverloadKind not yet implemented");
279
}
280
}
281
282
/// Emit Enums of DXIL Ops
283
/// \param A vector of DXIL Ops
284
/// \param Output stream
285
static void emitDXILEnums(std::vector<DXILOperationDesc> &Ops,
286
raw_ostream &OS) {
287
// Sort by OpCode
288
llvm::sort(Ops, [](DXILOperationDesc &A, DXILOperationDesc &B) {
289
return A.OpCode < B.OpCode;
290
});
291
292
OS << "// Enumeration for operations specified by DXIL\n";
293
OS << "enum class OpCode : unsigned {\n";
294
295
for (auto &Op : Ops) {
296
// Name = ID, // Doc
297
OS << Op.OpName << " = " << Op.OpCode << ", // " << Op.Doc << "\n";
298
}
299
300
OS << "\n};\n\n";
301
302
OS << "// Groups for DXIL operations with equivalent function templates\n";
303
OS << "enum class OpCodeClass : unsigned {\n";
304
// Build an OpClass set to print
305
SmallSet<StringRef, 2> OpClassSet;
306
for (auto &Op : Ops) {
307
OpClassSet.insert(Op.OpClass);
308
}
309
for (auto &C : OpClassSet) {
310
OS << C << ",\n";
311
}
312
OS << "\n};\n\n";
313
}
314
315
/// Emit map of DXIL operation to LLVM or DirectX intrinsic
316
/// \param A vector of DXIL Ops
317
/// \param Output stream
318
static void emitDXILIntrinsicMap(std::vector<DXILOperationDesc> &Ops,
319
raw_ostream &OS) {
320
OS << "\n";
321
// FIXME: use array instead of SmallDenseMap.
322
OS << "static const SmallDenseMap<Intrinsic::ID, dxil::OpCode> LowerMap = "
323
"{\n";
324
for (auto &Op : Ops) {
325
if (Op.Intrinsic.empty())
326
continue;
327
// {Intrinsic::sin, dxil::OpCode::Sin},
328
OS << " { Intrinsic::" << Op.Intrinsic << ", dxil::OpCode::" << Op.OpName
329
<< "},\n";
330
}
331
OS << "};\n";
332
OS << "\n";
333
}
334
335
/// Convert operation attribute string to Attribute enum
336
///
337
/// \param Attr string reference
338
/// \return std::string Attribute enum string
339
340
static std::string emitDXILOperationAttr(SmallVector<std::string> Attrs) {
341
for (auto Attr : Attrs) {
342
// TODO: For now just recognize IntrNoMem and IntrReadMem as valid and
343
// ignore others.
344
if (Attr == "IntrNoMem") {
345
return "Attribute::ReadNone";
346
} else if (Attr == "IntrReadMem") {
347
return "Attribute::ReadOnly";
348
}
349
}
350
return "Attribute::None";
351
}
352
353
/// Emit DXIL operation table
354
/// \param A vector of DXIL Ops
355
/// \param Output stream
356
static void emitDXILOperationTable(std::vector<DXILOperationDesc> &Ops,
357
raw_ostream &OS) {
358
// Sort by OpCode.
359
llvm::sort(Ops, [](DXILOperationDesc &A, DXILOperationDesc &B) {
360
return A.OpCode < B.OpCode;
361
});
362
363
// Collect Names.
364
SequenceToOffsetTable<std::string> OpClassStrings;
365
SequenceToOffsetTable<std::string> OpStrings;
366
SequenceToOffsetTable<SmallVector<ParameterKind>> Parameters;
367
368
StringMap<SmallVector<ParameterKind>> ParameterMap;
369
StringSet<> ClassSet;
370
for (auto &Op : Ops) {
371
OpStrings.add(Op.OpName);
372
373
if (ClassSet.contains(Op.OpClass))
374
continue;
375
ClassSet.insert(Op.OpClass);
376
OpClassStrings.add(Op.OpClass.data());
377
SmallVector<ParameterKind> ParamKindVec;
378
// ParamKindVec is a vector of parameters. Skip return type at index 0
379
for (unsigned i = 1; i < Op.OpTypes.size(); i++) {
380
ParamKindVec.emplace_back(getParameterKind(Op.OpTypes[i]));
381
}
382
ParameterMap[Op.OpClass] = ParamKindVec;
383
Parameters.add(ParamKindVec);
384
}
385
386
// Layout names.
387
OpStrings.layout();
388
OpClassStrings.layout();
389
Parameters.layout();
390
391
// Emit the DXIL operation table.
392
//{dxil::OpCode::Sin, OpCodeNameIndex, OpCodeClass::unary,
393
// OpCodeClassNameIndex,
394
// OverloadKind::FLOAT | OverloadKind::HALF, Attribute::AttrKind::ReadNone, 0,
395
// 3, ParameterTableOffset},
396
OS << "static const OpCodeProperty *getOpCodeProperty(dxil::OpCode Op) "
397
"{\n";
398
399
OS << " static const OpCodeProperty OpCodeProps[] = {\n";
400
for (auto &Op : Ops) {
401
// Consider Op.OverloadParamIndex as the overload parameter index, by
402
// default
403
auto OLParamIdx = Op.OverloadParamIndex;
404
// If no overload parameter index is set, treat first parameter type as
405
// overload type - unless the Op has no parameters, in which case treat the
406
// return type - as overload parameter to emit the appropriate overload kind
407
// enum.
408
if (OLParamIdx < 0) {
409
OLParamIdx = (Op.OpTypes.size() > 1) ? 1 : 0;
410
}
411
OS << " { dxil::OpCode::" << Op.OpName << ", " << OpStrings.get(Op.OpName)
412
<< ", OpCodeClass::" << Op.OpClass << ", "
413
<< OpClassStrings.get(Op.OpClass.data()) << ", "
414
<< getOverloadKindStr(Op.OpTypes[OLParamIdx]) << ", "
415
<< emitDXILOperationAttr(Op.OpAttributes) << ", "
416
<< Op.OverloadParamIndex << ", " << Op.OpTypes.size() - 1 << ", "
417
<< Parameters.get(ParameterMap[Op.OpClass]) << " },\n";
418
}
419
OS << " };\n";
420
421
OS << " // FIXME: change search to indexing with\n";
422
OS << " // Op once all DXIL operations are added.\n";
423
OS << " OpCodeProperty TmpProp;\n";
424
OS << " TmpProp.OpCode = Op;\n";
425
OS << " const OpCodeProperty *Prop =\n";
426
OS << " llvm::lower_bound(OpCodeProps, TmpProp,\n";
427
OS << " [](const OpCodeProperty &A, const "
428
"OpCodeProperty &B) {\n";
429
OS << " return A.OpCode < B.OpCode;\n";
430
OS << " });\n";
431
OS << " assert(Prop && \"failed to find OpCodeProperty\");\n";
432
OS << " return Prop;\n";
433
OS << "}\n\n";
434
435
// Emit the string tables.
436
OS << "static const char *getOpCodeName(dxil::OpCode Op) {\n\n";
437
438
OpStrings.emitStringLiteralDef(OS,
439
" static const char DXILOpCodeNameTable[]");
440
441
OS << " auto *Prop = getOpCodeProperty(Op);\n";
442
OS << " unsigned Index = Prop->OpCodeNameOffset;\n";
443
OS << " return DXILOpCodeNameTable + Index;\n";
444
OS << "}\n\n";
445
446
OS << "static const char *getOpCodeClassName(const OpCodeProperty &Prop) "
447
"{\n\n";
448
449
OpClassStrings.emitStringLiteralDef(
450
OS, " static const char DXILOpCodeClassNameTable[]");
451
452
OS << " unsigned Index = Prop.OpCodeClassNameOffset;\n";
453
OS << " return DXILOpCodeClassNameTable + Index;\n";
454
OS << "}\n ";
455
456
OS << "static const ParameterKind *getOpCodeParameterKind(const "
457
"OpCodeProperty &Prop) "
458
"{\n\n";
459
OS << " static const ParameterKind DXILOpParameterKindTable[] = {\n";
460
Parameters.emit(
461
OS,
462
[](raw_ostream &ParamOS, ParameterKind Kind) {
463
ParamOS << "ParameterKind::" << getParameterKindStr(Kind);
464
},
465
"ParameterKind::Invalid");
466
OS << " };\n\n";
467
OS << " unsigned Index = Prop.ParameterTableOffset;\n";
468
OS << " return DXILOpParameterKindTable + Index;\n";
469
OS << "}\n ";
470
}
471
472
/// Entry function call that invokes the functionality of this TableGen backend
473
/// \param Records TableGen records of DXIL Operations defined in DXIL.td
474
/// \param OS output stream
475
static void EmitDXILOperation(RecordKeeper &Records, raw_ostream &OS) {
476
OS << "// Generated code, do not edit.\n";
477
OS << "\n";
478
// Get all DXIL Ops to intrinsic mapping records
479
std::vector<Record *> OpIntrMaps =
480
Records.getAllDerivedDefinitions("DXILOpMapping");
481
std::vector<DXILOperationDesc> DXILOps;
482
for (auto *Record : OpIntrMaps) {
483
DXILOps.emplace_back(DXILOperationDesc(Record));
484
}
485
OS << "#ifdef DXIL_OP_ENUM\n";
486
emitDXILEnums(DXILOps, OS);
487
OS << "#endif\n\n";
488
OS << "#ifdef DXIL_OP_INTRINSIC_MAP\n";
489
emitDXILIntrinsicMap(DXILOps, OS);
490
OS << "#endif\n\n";
491
OS << "#ifdef DXIL_OP_OPERATION_TABLE\n";
492
emitDXILOperationTable(DXILOps, OS);
493
OS << "#endif\n\n";
494
}
495
496
static TableGen::Emitter::Opt X("gen-dxil-operation", EmitDXILOperation,
497
"Generate DXIL operation information");
498
499