1//===- DXILOpBuilder.cpp - Helper class for build DIXLOp functions --------===//
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/// \file This file contains class to help build DXIL op functions.
10//===----------------------------------------------------------------------===//
11
12#include "DXILOpBuilder.h"
13#include "DXILConstants.h"
14#include "llvm/IR/Module.h"
15#include "llvm/Support/DXILABI.h"
16#include "llvm/Support/ErrorHandling.h"
17#include <optional>
18
19using namespace llvm;
20using namespace llvm::dxil;
21
22constexpr StringLiteral DXILOpNamePrefix = "dx.op.";
23
24namespace {
25enum OverloadKind : uint16_t {
26 UNDEFINED = 0,
27 VOID = 1,
28 HALF = 1 << 1,
29 FLOAT = 1 << 2,
30 DOUBLE = 1 << 3,
31 I1 = 1 << 4,
32 I8 = 1 << 5,
33 I16 = 1 << 6,
34 I32 = 1 << 7,
35 I64 = 1 << 8,
36 UserDefineType = 1 << 9,
37 ObjectType = 1 << 10,
38};
39struct Version {
40 unsigned Major = 0;
41 unsigned Minor = 0;
42};
43
44struct OpOverload {
45 Version DXILVersion;
46 uint16_t ValidTys;
47};
48} // namespace
49
50struct OpStage {
51 Version DXILVersion;
52 uint32_t ValidStages;
53};
54
55static const char *getOverloadTypeName(OverloadKind Kind) {
56 switch (Kind) {
57 case OverloadKind::HALF:
58 return "f16";
59 case OverloadKind::FLOAT:
60 return "f32";
61 case OverloadKind::DOUBLE:
62 return "f64";
63 case OverloadKind::I1:
64 return "i1";
65 case OverloadKind::I8:
66 return "i8";
67 case OverloadKind::I16:
68 return "i16";
69 case OverloadKind::I32:
70 return "i32";
71 case OverloadKind::I64:
72 return "i64";
73 case OverloadKind::VOID:
74 case OverloadKind::UNDEFINED:
75 return "void";
76 case OverloadKind::ObjectType:
77 case OverloadKind::UserDefineType:
78 break;
79 }
80 llvm_unreachable("invalid overload type for name");
81}
82
83static OverloadKind getOverloadKind(Type *Ty) {
84 if (!Ty)
85 return OverloadKind::VOID;
86
87 Type::TypeID T = Ty->getTypeID();
88 switch (T) {
89 case Type::VoidTyID:
90 return OverloadKind::VOID;
91 case Type::HalfTyID:
92 return OverloadKind::HALF;
93 case Type::FloatTyID:
94 return OverloadKind::FLOAT;
95 case Type::DoubleTyID:
96 return OverloadKind::DOUBLE;
97 case Type::IntegerTyID: {
98 IntegerType *ITy = cast<IntegerType>(Val: Ty);
99 unsigned Bits = ITy->getBitWidth();
100 switch (Bits) {
101 case 1:
102 return OverloadKind::I1;
103 case 8:
104 return OverloadKind::I8;
105 case 16:
106 return OverloadKind::I16;
107 case 32:
108 return OverloadKind::I32;
109 case 64:
110 return OverloadKind::I64;
111 default:
112 llvm_unreachable("invalid overload type");
113 return OverloadKind::VOID;
114 }
115 }
116 case Type::PointerTyID:
117 return OverloadKind::UserDefineType;
118 case Type::StructTyID: {
119 // TODO: This is a hack. As described in DXILEmitter.cpp, we need to rework
120 // how we're handling overloads and remove the `OverloadKind` proxy enum.
121 StructType *ST = cast<StructType>(Val: Ty);
122 return getOverloadKind(Ty: ST->getElementType(N: 0));
123 }
124 default:
125 return OverloadKind::UNDEFINED;
126 }
127}
128
129static std::string getTypeName(OverloadKind Kind, Type *Ty) {
130 if (Kind < OverloadKind::UserDefineType) {
131 return getOverloadTypeName(Kind);
132 } else if (Kind == OverloadKind::UserDefineType) {
133 StructType *ST = cast<StructType>(Val: Ty);
134 return ST->getStructName().str();
135 } else if (Kind == OverloadKind::ObjectType) {
136 StructType *ST = cast<StructType>(Val: Ty);
137 return ST->getStructName().str();
138 } else {
139 std::string Str;
140 raw_string_ostream OS(Str);
141 Ty->print(O&: OS);
142 return OS.str();
143 }
144}
145
146// Static properties.
147struct OpCodeProperty {
148 dxil::OpCode OpCode;
149 // Offset in DXILOpCodeNameTable.
150 unsigned OpCodeNameOffset;
151 dxil::OpCodeClass OpCodeClass;
152 // Offset in DXILOpCodeClassNameTable.
153 unsigned OpCodeClassNameOffset;
154 llvm::SmallVector<OpOverload> Overloads;
155 llvm::SmallVector<OpStage> Stages;
156 int OverloadParamIndex; // parameter index which control the overload.
157 // When < 0, should be only 1 overload type.
158};
159
160// Include getOpCodeClassName getOpCodeProperty, getOpCodeName and
161// getOpCodeParameterKind which generated by tableGen.
162#define DXIL_OP_OPERATION_TABLE
163#include "DXILOperation.inc"
164#undef DXIL_OP_OPERATION_TABLE
165
166static std::string constructOverloadName(OverloadKind Kind, Type *Ty,
167 const OpCodeProperty &Prop) {
168 if (Kind == OverloadKind::VOID) {
169 return (Twine(DXILOpNamePrefix) + getOpCodeClassName(Prop)).str();
170 }
171 return (Twine(DXILOpNamePrefix) + getOpCodeClassName(Prop) + "." +
172 getTypeName(Kind, Ty))
173 .str();
174}
175
176static std::string constructOverloadTypeName(OverloadKind Kind,
177 StringRef TypeName) {
178 if (Kind == OverloadKind::VOID)
179 return TypeName.str();
180
181 assert(Kind < OverloadKind::UserDefineType && "invalid overload kind");
182 return (Twine(TypeName) + getOverloadTypeName(Kind)).str();
183}
184
185static StructType *getOrCreateStructType(StringRef Name,
186 ArrayRef<Type *> EltTys,
187 LLVMContext &Ctx) {
188 StructType *ST = StructType::getTypeByName(C&: Ctx, Name);
189 if (ST)
190 return ST;
191
192 return StructType::create(Context&: Ctx, Elements: EltTys, Name);
193}
194
195static StructType *getResRetType(Type *ElementTy) {
196 LLVMContext &Ctx = ElementTy->getContext();
197 OverloadKind Kind = getOverloadKind(Ty: ElementTy);
198 std::string TypeName = constructOverloadTypeName(Kind, TypeName: "dx.types.ResRet.");
199 Type *FieldTypes[5] = {ElementTy, ElementTy, ElementTy, ElementTy,
200 Type::getInt32Ty(C&: Ctx)};
201 return getOrCreateStructType(Name: TypeName, EltTys: FieldTypes, Ctx);
202}
203
204static StructType *getCBufRetType(Type *ElementTy) {
205 LLVMContext &Ctx = ElementTy->getContext();
206 OverloadKind Kind = getOverloadKind(Ty: ElementTy);
207 std::string TypeName = constructOverloadTypeName(Kind, TypeName: "dx.types.CBufRet.");
208
209 // 64-bit types only have two elements
210 if (ElementTy->isDoubleTy() || ElementTy->isIntegerTy(BitWidth: 64))
211 return getOrCreateStructType(Name: TypeName, EltTys: {ElementTy, ElementTy}, Ctx);
212
213 // 16-bit types pack 8 elements and have .8 in their name to differentiate
214 // from min-precision types.
215 if (ElementTy->isHalfTy() || ElementTy->isIntegerTy(BitWidth: 16)) {
216 TypeName += ".8";
217 return getOrCreateStructType(Name: TypeName,
218 EltTys: {ElementTy, ElementTy, ElementTy, ElementTy,
219 ElementTy, ElementTy, ElementTy, ElementTy},
220 Ctx);
221 }
222
223 return getOrCreateStructType(
224 Name: TypeName, EltTys: {ElementTy, ElementTy, ElementTy, ElementTy}, Ctx);
225}
226
227static StructType *getHandleType(LLVMContext &Ctx) {
228 return getOrCreateStructType(Name: "dx.types.Handle", EltTys: PointerType::getUnqual(C&: Ctx),
229 Ctx);
230}
231
232static StructType *getResBindType(LLVMContext &Context) {
233 if (auto *ST = StructType::getTypeByName(C&: Context, Name: "dx.types.ResBind"))
234 return ST;
235 Type *Int32Ty = Type::getInt32Ty(C&: Context);
236 Type *Int8Ty = Type::getInt8Ty(C&: Context);
237 return StructType::create(Elements: {Int32Ty, Int32Ty, Int32Ty, Int8Ty},
238 Name: "dx.types.ResBind");
239}
240
241static StructType *getResPropsType(LLVMContext &Context) {
242 if (auto *ST =
243 StructType::getTypeByName(C&: Context, Name: "dx.types.ResourceProperties"))
244 return ST;
245 Type *Int32Ty = Type::getInt32Ty(C&: Context);
246 return StructType::create(Elements: {Int32Ty, Int32Ty}, Name: "dx.types.ResourceProperties");
247}
248
249static StructType *getSplitDoubleType(LLVMContext &Context) {
250 if (auto *ST = StructType::getTypeByName(C&: Context, Name: "dx.types.splitdouble"))
251 return ST;
252 Type *Int32Ty = Type::getInt32Ty(C&: Context);
253 return StructType::create(Elements: {Int32Ty, Int32Ty}, Name: "dx.types.splitdouble");
254}
255
256static StructType *getBinaryWithCarryType(LLVMContext &Context) {
257 if (auto *ST = StructType::getTypeByName(C&: Context, Name: "dx.types.i32c"))
258 return ST;
259 Type *Int32Ty = Type::getInt32Ty(C&: Context);
260 Type *Int1Ty = Type::getInt1Ty(C&: Context);
261 return StructType::create(Elements: {Int32Ty, Int1Ty}, Name: "dx.types.i32c");
262}
263
264static StructType *getDimensionsType(LLVMContext &Context) {
265 Type *Int32Ty = Type::getInt32Ty(C&: Context);
266 return getOrCreateStructType(Name: "dx.types.Dimensions",
267 EltTys: {Int32Ty, Int32Ty, Int32Ty, Int32Ty}, Ctx&: Context);
268}
269
270static StructType *getFouri32sType(LLVMContext &Context) {
271 if (auto *ST = StructType::getTypeByName(C&: Context, Name: "dx.types.fouri32"))
272 return ST;
273 Type *Int32Ty = Type::getInt32Ty(C&: Context);
274 return getOrCreateStructType(Name: "dx.types.fouri32",
275 EltTys: {Int32Ty, Int32Ty, Int32Ty, Int32Ty}, Ctx&: Context);
276}
277
278static StructType *getTwoI32Type(LLVMContext &Context) {
279 if (auto *ST = StructType::getTypeByName(C&: Context, Name: "dx.types.twoi32"))
280 return ST;
281 Type *Int32Ty = Type::getInt32Ty(C&: Context);
282 return StructType::create(Elements: {Int32Ty, Int32Ty}, Name: "dx.types.twoi32");
283}
284
285static Type *getTypeFromOpParamType(OpParamType Kind, LLVMContext &Ctx,
286 Type *OverloadTy) {
287 switch (Kind) {
288 case OpParamType::VoidTy:
289 return Type::getVoidTy(C&: Ctx);
290 case OpParamType::HalfTy:
291 return Type::getHalfTy(C&: Ctx);
292 case OpParamType::FloatTy:
293 return Type::getFloatTy(C&: Ctx);
294 case OpParamType::DoubleTy:
295 return Type::getDoubleTy(C&: Ctx);
296 case OpParamType::Int1Ty:
297 return Type::getInt1Ty(C&: Ctx);
298 case OpParamType::Int8Ty:
299 return Type::getInt8Ty(C&: Ctx);
300 case OpParamType::Int16Ty:
301 return Type::getInt16Ty(C&: Ctx);
302 case OpParamType::Int32Ty:
303 return Type::getInt32Ty(C&: Ctx);
304 case OpParamType::Int64Ty:
305 return Type::getInt64Ty(C&: Ctx);
306 case OpParamType::OverloadTy:
307 return OverloadTy;
308 case OpParamType::ResRetHalfTy:
309 return getResRetType(ElementTy: Type::getHalfTy(C&: Ctx));
310 case OpParamType::ResRetFloatTy:
311 return getResRetType(ElementTy: Type::getFloatTy(C&: Ctx));
312 case OpParamType::ResRetDoubleTy:
313 return getResRetType(ElementTy: Type::getDoubleTy(C&: Ctx));
314 case OpParamType::ResRetInt16Ty:
315 return getResRetType(ElementTy: Type::getInt16Ty(C&: Ctx));
316 case OpParamType::ResRetInt32Ty:
317 return getResRetType(ElementTy: Type::getInt32Ty(C&: Ctx));
318 case OpParamType::ResRetInt64Ty:
319 return getResRetType(ElementTy: Type::getInt64Ty(C&: Ctx));
320 case OpParamType::CBufRetHalfTy:
321 return getCBufRetType(ElementTy: Type::getHalfTy(C&: Ctx));
322 case OpParamType::CBufRetFloatTy:
323 return getCBufRetType(ElementTy: Type::getFloatTy(C&: Ctx));
324 case OpParamType::CBufRetDoubleTy:
325 return getCBufRetType(ElementTy: Type::getDoubleTy(C&: Ctx));
326 case OpParamType::CBufRetInt16Ty:
327 return getCBufRetType(ElementTy: Type::getInt16Ty(C&: Ctx));
328 case OpParamType::CBufRetInt32Ty:
329 return getCBufRetType(ElementTy: Type::getInt32Ty(C&: Ctx));
330 case OpParamType::CBufRetInt64Ty:
331 return getCBufRetType(ElementTy: Type::getInt64Ty(C&: Ctx));
332 case OpParamType::HandleTy:
333 return getHandleType(Ctx);
334 case OpParamType::ResBindTy:
335 return getResBindType(Context&: Ctx);
336 case OpParamType::ResPropsTy:
337 return getResPropsType(Context&: Ctx);
338 case OpParamType::SplitDoubleTy:
339 return getSplitDoubleType(Context&: Ctx);
340 case OpParamType::BinaryWithCarryTy:
341 return getBinaryWithCarryType(Context&: Ctx);
342 case OpParamType::DimensionsTy:
343 return getDimensionsType(Context&: Ctx);
344 case OpParamType::Fouri32s:
345 return getFouri32sType(Context&: Ctx);
346 case OpParamType::TwoI32Ty:
347 return getTwoI32Type(Context&: Ctx);
348 }
349
350 llvm_unreachable("Invalid parameter kind");
351 return nullptr;
352}
353
354static ShaderKind getShaderKindEnum(Triple::EnvironmentType EnvType) {
355 switch (EnvType) {
356 case Triple::Pixel:
357 return ShaderKind::pixel;
358 case Triple::Vertex:
359 return ShaderKind::vertex;
360 case Triple::Geometry:
361 return ShaderKind::geometry;
362 case Triple::Hull:
363 return ShaderKind::hull;
364 case Triple::Domain:
365 return ShaderKind::domain;
366 case Triple::Compute:
367 return ShaderKind::compute;
368 case Triple::Library:
369 return ShaderKind::library;
370 case Triple::RayGeneration:
371 return ShaderKind::raygeneration;
372 case Triple::Intersection:
373 return ShaderKind::intersection;
374 case Triple::AnyHit:
375 return ShaderKind::anyhit;
376 case Triple::ClosestHit:
377 return ShaderKind::closesthit;
378 case Triple::Miss:
379 return ShaderKind::miss;
380 case Triple::Callable:
381 return ShaderKind::callable;
382 case Triple::Mesh:
383 return ShaderKind::mesh;
384 case Triple::Amplification:
385 return ShaderKind::amplification;
386 default:
387 break;
388 }
389 llvm_unreachable(
390 "Shader Kind Not Found - Invalid DXIL Environment Specified");
391}
392
393static SmallVector<Type *>
394getArgTypesFromOpParamTypes(ArrayRef<dxil::OpParamType> Types,
395 LLVMContext &Context, Type *OverloadTy) {
396 SmallVector<Type *> ArgTys;
397 ArgTys.emplace_back(Args: Type::getInt32Ty(C&: Context));
398 for (dxil::OpParamType Ty : Types)
399 ArgTys.emplace_back(Args: getTypeFromOpParamType(Kind: Ty, Ctx&: Context, OverloadTy));
400 return ArgTys;
401}
402
403/// Construct DXIL function type. This is the type of a function with
404/// the following prototype
405/// OverloadType dx.op.<opclass>.<return-type>(int opcode, <param types>)
406/// <param-types> are constructed from types in Prop.
407static FunctionType *getDXILOpFunctionType(dxil::OpCode OpCode,
408 LLVMContext &Context,
409 Type *OverloadTy) {
410
411 switch (OpCode) {
412#define DXIL_OP_FUNCTION_TYPE(OpCode, RetType, ...) \
413 case OpCode: \
414 return FunctionType::get( \
415 getTypeFromOpParamType(RetType, Context, OverloadTy), \
416 getArgTypesFromOpParamTypes({__VA_ARGS__}, Context, OverloadTy), \
417 /*isVarArg=*/false);
418#include "DXILOperation.inc"
419 }
420 llvm_unreachable("Invalid OpCode?");
421}
422
423/// Get index of the property from PropList valid for the most recent
424/// DXIL version not greater than DXILVer.
425/// PropList is expected to be sorted in ascending order of DXIL version.
426template <typename T>
427static std::optional<size_t> getPropIndex(ArrayRef<T> PropList,
428 const VersionTuple DXILVer) {
429 size_t Index = PropList.size() - 1;
430 for (auto Iter = PropList.rbegin(); Iter != PropList.rend();
431 Iter++, Index--) {
432 const T &Prop = *Iter;
433 if (VersionTuple(Prop.DXILVersion.Major, Prop.DXILVersion.Minor) <=
434 DXILVer) {
435 return Index;
436 }
437 }
438 return std::nullopt;
439}
440
441// Helper function to pack an OpCode and VersionTuple into a uint64_t for use
442// in a switch statement
443constexpr static uint64_t computeSwitchEnum(dxil::OpCode OpCode,
444 uint16_t VersionMajor,
445 uint16_t VersionMinor) {
446 uint64_t OpCodePack = (uint64_t)OpCode;
447 return (OpCodePack << 32) | (VersionMajor << 16) | VersionMinor;
448}
449
450/// Get the set of attributes for a given DXIL OpCode and the DXIL version.
451static dxil::Attributes getDXILAttributes(dxil::OpCode OpCode,
452 VersionTuple DXILVersion) {
453 // Instantiate all versions to iterate through
454 SmallVector<Version> Versions = {
455#define DXIL_VERSION(MAJOR, MINOR) {MAJOR, MINOR},
456#include "DXILOperation.inc"
457 };
458
459 dxil::Attributes Attributes;
460 for (auto Version : Versions) {
461 if (DXILVersion < VersionTuple(Version.Major, Version.Minor))
462 continue;
463
464 // Switch through and match an OpCode with the specific version and set the
465 // corresponding flag(s) if available
466 switch (computeSwitchEnum(OpCode, VersionMajor: Version.Major, VersionMinor: Version.Minor)) {
467#define DXIL_OP_ATTRIBUTES(OpCode, VersionMajor, VersionMinor, ...) \
468 case computeSwitchEnum(OpCode, VersionMajor, VersionMinor): { \
469 auto Other = dxil::Attributes{__VA_ARGS__}; \
470 Attributes |= Other; \
471 break; \
472 };
473#include "DXILOperation.inc"
474 }
475 }
476 return Attributes;
477}
478
479/// Get the attributes to apply to the function for the DXIL operation with the
480/// given OpCode and DXIL version.
481static AttributeList getDXILFnAttributeList(LLVMContext &Ctx,
482 dxil::OpCode OpCode,
483 VersionTuple DXILVersion) {
484 dxil::Attributes Attributes = getDXILAttributes(OpCode, DXILVersion);
485 AttrBuilder FnAttrs(Ctx);
486
487 if (Attributes.ReadNone)
488 FnAttrs.addMemoryAttr(ME: MemoryEffects::none());
489 if (Attributes.ReadOnly)
490 FnAttrs.addMemoryAttr(ME: MemoryEffects::readOnly());
491 if (Attributes.NoReturn)
492 FnAttrs.addAttribute(Val: Attribute::NoReturn);
493 if (Attributes.NoDuplicate)
494 FnAttrs.addAttribute(Val: Attribute::NoDuplicate);
495 FnAttrs.addAttribute(Val: Attribute::NoUnwind);
496
497 return AttributeList::get(C&: Ctx, Index: AttributeList::FunctionIndex, B: FnAttrs);
498}
499
500namespace llvm {
501namespace dxil {
502
503// No extra checks on TargetTriple need be performed to verify that the
504// Triple is well-formed or that the target is supported since these checks
505// would have been done at the time the module M is constructed in the earlier
506// stages of compilation.
507DXILOpBuilder::DXILOpBuilder(Module &M) : M(M), IRB(M.getContext()) {
508 const Triple &TT = M.getTargetTriple();
509 DXILVersion = TT.getDXILVersion();
510 ShaderStage = TT.getEnvironment();
511 // Ensure Environment type is known
512 if (ShaderStage == Triple::UnknownEnvironment) {
513 reportFatalUsageError(
514 reason: Twine(DXILVersion.getAsString()) +
515 ": Unknown Compilation Target Shader Stage specified ");
516 }
517}
518
519static Error makeOpError(dxil::OpCode OpCode, Twine Msg) {
520 return make_error<StringError>(
521 Args: Twine("Cannot create ") + getOpCodeName(Op: OpCode) + " operation: " + Msg,
522 Args: inconvertibleErrorCode());
523}
524
525Expected<CallInst *> DXILOpBuilder::tryCreateOp(dxil::OpCode OpCode,
526 ArrayRef<Value *> Args,
527 const Twine &Name,
528 Type *RetTy) {
529 const OpCodeProperty *Prop = getOpCodeProperty(Op: OpCode);
530
531 Type *OverloadTy = nullptr;
532 if (Prop->OverloadParamIndex == 0) {
533 if (!RetTy)
534 return makeOpError(OpCode, Msg: "Op overloaded on unknown return type");
535 OverloadTy = RetTy;
536 } else if (Prop->OverloadParamIndex > 0) {
537 // The index counts including the return type
538 unsigned ArgIndex = Prop->OverloadParamIndex - 1;
539 if (static_cast<unsigned>(ArgIndex) >= Args.size())
540 return makeOpError(OpCode, Msg: "Wrong number of arguments");
541 OverloadTy = Args[ArgIndex]->getType();
542 }
543
544 FunctionType *DXILOpFT =
545 getDXILOpFunctionType(OpCode, Context&: M.getContext(), OverloadTy);
546
547 std::optional<size_t> OlIndexOrErr =
548 getPropIndex(PropList: ArrayRef(Prop->Overloads), DXILVer: DXILVersion);
549 if (!OlIndexOrErr.has_value())
550 return makeOpError(OpCode, Msg: Twine("No valid overloads for DXIL version ") +
551 DXILVersion.getAsString());
552
553 uint16_t ValidTyMask = Prop->Overloads[*OlIndexOrErr].ValidTys;
554
555 OverloadKind Kind = getOverloadKind(Ty: OverloadTy);
556
557 // Check if the operation supports overload types and OverloadTy is valid
558 // per the specified types for the operation
559 if ((ValidTyMask != OverloadKind::UNDEFINED) &&
560 (ValidTyMask & (uint16_t)Kind) == 0)
561 return makeOpError(OpCode, Msg: "Invalid overload type");
562
563 // Perform necessary checks to ensure Opcode is valid in the targeted shader
564 // kind
565 std::optional<size_t> StIndexOrErr =
566 getPropIndex(PropList: ArrayRef(Prop->Stages), DXILVer: DXILVersion);
567 if (!StIndexOrErr.has_value())
568 return makeOpError(OpCode, Msg: Twine("No valid stage for DXIL version ") +
569 DXILVersion.getAsString());
570
571 uint16_t ValidShaderKindMask = Prop->Stages[*StIndexOrErr].ValidStages;
572
573 // Ensure valid shader stage properties are specified
574 if (ValidShaderKindMask == ShaderKind::removed)
575 return makeOpError(OpCode, Msg: "Operation has been removed");
576
577 // Shader stage need not be validated since getShaderKindEnum() fails
578 // for unknown shader stage.
579
580 // Verify the target shader stage is valid for the DXIL operation
581 ShaderKind ModuleStagekind = getShaderKindEnum(EnvType: ShaderStage);
582 if (!(ValidShaderKindMask & ModuleStagekind))
583 return makeOpError(OpCode, Msg: "Invalid stage");
584
585 AttributeList DXILFnAttrs =
586 getDXILFnAttributeList(Ctx&: M.getContext(), OpCode, DXILVersion);
587 std::string DXILFnName = constructOverloadName(Kind, Ty: OverloadTy, Prop: *Prop);
588 FunctionCallee DXILFn =
589 M.getOrInsertFunction(Name: DXILFnName, T: DXILOpFT, AttributeList: DXILFnAttrs);
590
591 // We need to inject the opcode as the first argument.
592 SmallVector<Value *> OpArgs;
593 OpArgs.push_back(Elt: IRB.getInt32(C: llvm::to_underlying(E: OpCode)));
594 OpArgs.append(in_start: Args.begin(), in_end: Args.end());
595
596 // Create the function call instruction
597 CallInst *CI = IRB.CreateCall(Callee: DXILFn, Args: OpArgs, Name);
598
599 return CI;
600}
601
602CallInst *DXILOpBuilder::createOp(dxil::OpCode OpCode, ArrayRef<Value *> Args,
603 const Twine &Name, Type *RetTy) {
604 Expected<CallInst *> Result = tryCreateOp(OpCode, Args, Name, RetTy);
605 if (Error E = Result.takeError())
606 llvm_unreachable("Invalid arguments for operation");
607 return *Result;
608}
609
610StructType *DXILOpBuilder::getResRetType(Type *ElementTy) {
611 return ::getResRetType(ElementTy);
612}
613
614StructType *DXILOpBuilder::getCBufRetType(Type *ElementTy) {
615 return ::getCBufRetType(ElementTy);
616}
617
618StructType *DXILOpBuilder::getHandleType() {
619 return ::getHandleType(Ctx&: IRB.getContext());
620}
621
622Constant *DXILOpBuilder::getResBind(uint32_t LowerBound, uint32_t UpperBound,
623 uint32_t SpaceID, dxil::ResourceClass RC) {
624 Type *Int32Ty = IRB.getInt32Ty();
625 Type *Int8Ty = IRB.getInt8Ty();
626 return ConstantStruct::get(
627 T: getResBindType(Context&: IRB.getContext()),
628 V: {ConstantInt::get(Ty: Int32Ty, V: LowerBound),
629 ConstantInt::get(Ty: Int32Ty, V: UpperBound),
630 ConstantInt::get(Ty: Int32Ty, V: SpaceID),
631 ConstantInt::get(Ty: Int8Ty, V: llvm::to_underlying(E: RC))});
632}
633
634Constant *DXILOpBuilder::getResProps(uint32_t Word0, uint32_t Word1) {
635 Type *Int32Ty = IRB.getInt32Ty();
636 return ConstantStruct::get(
637 T: getResPropsType(Context&: IRB.getContext()),
638 V: {ConstantInt::get(Ty: Int32Ty, V: Word0), ConstantInt::get(Ty: Int32Ty, V: Word1)});
639}
640
641const char *DXILOpBuilder::getOpCodeName(dxil::OpCode DXILOp) {
642 return ::getOpCodeName(Op: DXILOp);
643}
644} // namespace dxil
645} // namespace llvm
646