xref: /llvm-project/llvm/lib/Target/DirectX/DXILOpBuilder.cpp (revision 011b618644113996e2c0a8e57db40f89d20878e3)
157006b14SXiang Li //===- DXILOpBuilder.cpp - Helper class for build DIXLOp functions --------===//
257006b14SXiang Li //
357006b14SXiang Li // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
457006b14SXiang Li // See https://llvm.org/LICENSE.txt for license information.
557006b14SXiang Li // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
657006b14SXiang Li //
757006b14SXiang Li //===----------------------------------------------------------------------===//
857006b14SXiang Li ///
957006b14SXiang Li /// \file This file contains class to help build DXIL op functions.
1057006b14SXiang Li //===----------------------------------------------------------------------===//
1157006b14SXiang Li 
1257006b14SXiang Li #include "DXILOpBuilder.h"
1357006b14SXiang Li #include "DXILConstants.h"
1457006b14SXiang Li #include "llvm/IR/Module.h"
15db6bf921SJustin Bogner #include "llvm/Support/DXILABI.h"
1657006b14SXiang Li #include "llvm/Support/ErrorHandling.h"
17cdfd884bSS. Bharadwaj Yadavalli #include <optional>
1857006b14SXiang Li 
1957006b14SXiang Li using namespace llvm;
20e77c40ffSChris Bieneman using namespace llvm::dxil;
2157006b14SXiang Li 
2257006b14SXiang Li constexpr StringLiteral DXILOpNamePrefix = "dx.op.";
2357006b14SXiang Li 
2457006b14SXiang Li namespace {
2557006b14SXiang Li enum OverloadKind : uint16_t {
26cdfd884bSS. Bharadwaj Yadavalli   UNDEFINED = 0,
2757006b14SXiang Li   VOID = 1,
2857006b14SXiang Li   HALF = 1 << 1,
2957006b14SXiang Li   FLOAT = 1 << 2,
3057006b14SXiang Li   DOUBLE = 1 << 3,
3157006b14SXiang Li   I1 = 1 << 4,
3257006b14SXiang Li   I8 = 1 << 5,
3357006b14SXiang Li   I16 = 1 << 6,
3457006b14SXiang Li   I32 = 1 << 7,
3557006b14SXiang Li   I64 = 1 << 8,
3657006b14SXiang Li   UserDefineType = 1 << 9,
3757006b14SXiang Li   ObjectType = 1 << 10,
3857006b14SXiang Li };
39cdfd884bSS. Bharadwaj Yadavalli struct Version {
40cdfd884bSS. Bharadwaj Yadavalli   unsigned Major = 0;
41cdfd884bSS. Bharadwaj Yadavalli   unsigned Minor = 0;
42cdfd884bSS. Bharadwaj Yadavalli };
4357006b14SXiang Li 
44cdfd884bSS. Bharadwaj Yadavalli struct OpOverload {
45cdfd884bSS. Bharadwaj Yadavalli   Version DXILVersion;
46cdfd884bSS. Bharadwaj Yadavalli   uint16_t ValidTys;
47cdfd884bSS. Bharadwaj Yadavalli };
4857006b14SXiang Li } // namespace
4957006b14SXiang Li 
50cdfd884bSS. Bharadwaj Yadavalli struct OpStage {
51cdfd884bSS. Bharadwaj Yadavalli   Version DXILVersion;
52cdfd884bSS. Bharadwaj Yadavalli   uint32_t ValidStages;
53cdfd884bSS. Bharadwaj Yadavalli };
54cdfd884bSS. Bharadwaj Yadavalli 
5557006b14SXiang Li static const char *getOverloadTypeName(OverloadKind Kind) {
5657006b14SXiang Li   switch (Kind) {
5757006b14SXiang Li   case OverloadKind::HALF:
5857006b14SXiang Li     return "f16";
5957006b14SXiang Li   case OverloadKind::FLOAT:
6057006b14SXiang Li     return "f32";
6157006b14SXiang Li   case OverloadKind::DOUBLE:
6257006b14SXiang Li     return "f64";
6357006b14SXiang Li   case OverloadKind::I1:
6457006b14SXiang Li     return "i1";
6557006b14SXiang Li   case OverloadKind::I8:
6657006b14SXiang Li     return "i8";
6757006b14SXiang Li   case OverloadKind::I16:
6857006b14SXiang Li     return "i16";
6957006b14SXiang Li   case OverloadKind::I32:
7057006b14SXiang Li     return "i32";
7157006b14SXiang Li   case OverloadKind::I64:
7257006b14SXiang Li     return "i64";
7357006b14SXiang Li   case OverloadKind::VOID:
74cdfd884bSS. Bharadwaj Yadavalli   case OverloadKind::UNDEFINED:
75cdfd884bSS. Bharadwaj Yadavalli     return "void";
7657006b14SXiang Li   case OverloadKind::ObjectType:
7757006b14SXiang Li   case OverloadKind::UserDefineType:
7857006b14SXiang Li     break;
7957006b14SXiang Li   }
8057006b14SXiang Li   llvm_unreachable("invalid overload type for name");
8157006b14SXiang Li }
8257006b14SXiang Li 
8357006b14SXiang Li static OverloadKind getOverloadKind(Type *Ty) {
847d60f464SJustin Bogner   if (!Ty)
857d60f464SJustin Bogner     return OverloadKind::VOID;
867d60f464SJustin Bogner 
8757006b14SXiang Li   Type::TypeID T = Ty->getTypeID();
8857006b14SXiang Li   switch (T) {
8957006b14SXiang Li   case Type::VoidTyID:
9057006b14SXiang Li     return OverloadKind::VOID;
9157006b14SXiang Li   case Type::HalfTyID:
9257006b14SXiang Li     return OverloadKind::HALF;
9357006b14SXiang Li   case Type::FloatTyID:
9457006b14SXiang Li     return OverloadKind::FLOAT;
9557006b14SXiang Li   case Type::DoubleTyID:
9657006b14SXiang Li     return OverloadKind::DOUBLE;
9757006b14SXiang Li   case Type::IntegerTyID: {
9857006b14SXiang Li     IntegerType *ITy = cast<IntegerType>(Ty);
9957006b14SXiang Li     unsigned Bits = ITy->getBitWidth();
10057006b14SXiang Li     switch (Bits) {
10157006b14SXiang Li     case 1:
10257006b14SXiang Li       return OverloadKind::I1;
10357006b14SXiang Li     case 8:
10457006b14SXiang Li       return OverloadKind::I8;
10557006b14SXiang Li     case 16:
10657006b14SXiang Li       return OverloadKind::I16;
10757006b14SXiang Li     case 32:
10857006b14SXiang Li       return OverloadKind::I32;
10957006b14SXiang Li     case 64:
11057006b14SXiang Li       return OverloadKind::I64;
11157006b14SXiang Li     default:
11257006b14SXiang Li       llvm_unreachable("invalid overload type");
11357006b14SXiang Li       return OverloadKind::VOID;
11457006b14SXiang Li     }
11557006b14SXiang Li   }
11657006b14SXiang Li   case Type::PointerTyID:
11757006b14SXiang Li     return OverloadKind::UserDefineType;
1183f22756fSJustin Bogner   case Type::StructTyID: {
1193f22756fSJustin Bogner     // TODO: This is a hack. As described in DXILEmitter.cpp, we need to rework
1203f22756fSJustin Bogner     // how we're handling overloads and remove the `OverloadKind` proxy enum.
1213f22756fSJustin Bogner     StructType *ST = cast<StructType>(Ty);
1223f22756fSJustin Bogner     return getOverloadKind(ST->getElementType(0));
1233f22756fSJustin Bogner   }
12457006b14SXiang Li   default:
12576be3a00SJustin Bogner     return OverloadKind::UNDEFINED;
12657006b14SXiang Li   }
12757006b14SXiang Li }
12857006b14SXiang Li 
12957006b14SXiang Li static std::string getTypeName(OverloadKind Kind, Type *Ty) {
13057006b14SXiang Li   if (Kind < OverloadKind::UserDefineType) {
13157006b14SXiang Li     return getOverloadTypeName(Kind);
13257006b14SXiang Li   } else if (Kind == OverloadKind::UserDefineType) {
13357006b14SXiang Li     StructType *ST = cast<StructType>(Ty);
13457006b14SXiang Li     return ST->getStructName().str();
13557006b14SXiang Li   } else if (Kind == OverloadKind::ObjectType) {
13657006b14SXiang Li     StructType *ST = cast<StructType>(Ty);
13757006b14SXiang Li     return ST->getStructName().str();
13857006b14SXiang Li   } else {
13957006b14SXiang Li     std::string Str;
14057006b14SXiang Li     raw_string_ostream OS(Str);
14157006b14SXiang Li     Ty->print(OS);
14257006b14SXiang Li     return OS.str();
14357006b14SXiang Li   }
14457006b14SXiang Li }
14557006b14SXiang Li 
14657006b14SXiang Li // Static properties.
14757006b14SXiang Li struct OpCodeProperty {
148e77c40ffSChris Bieneman   dxil::OpCode OpCode;
14957006b14SXiang Li   // Offset in DXILOpCodeNameTable.
15057006b14SXiang Li   unsigned OpCodeNameOffset;
151e77c40ffSChris Bieneman   dxil::OpCodeClass OpCodeClass;
15257006b14SXiang Li   // Offset in DXILOpCodeClassNameTable.
15357006b14SXiang Li   unsigned OpCodeClassNameOffset;
154cdfd884bSS. Bharadwaj Yadavalli   llvm::SmallVector<OpOverload> Overloads;
155cdfd884bSS. Bharadwaj Yadavalli   llvm::SmallVector<OpStage> Stages;
15657006b14SXiang Li   int OverloadParamIndex; // parameter index which control the overload.
15757006b14SXiang Li                           // When < 0, should be only 1 overload type.
15857006b14SXiang Li };
15957006b14SXiang Li 
16057006b14SXiang Li // Include getOpCodeClassName getOpCodeProperty, getOpCodeName and
16157006b14SXiang Li // getOpCodeParameterKind which generated by tableGen.
16257006b14SXiang Li #define DXIL_OP_OPERATION_TABLE
16357006b14SXiang Li #include "DXILOperation.inc"
16457006b14SXiang Li #undef DXIL_OP_OPERATION_TABLE
16557006b14SXiang Li 
16657006b14SXiang Li static std::string constructOverloadName(OverloadKind Kind, Type *Ty,
16757006b14SXiang Li                                          const OpCodeProperty &Prop) {
16857006b14SXiang Li   if (Kind == OverloadKind::VOID) {
16957006b14SXiang Li     return (Twine(DXILOpNamePrefix) + getOpCodeClassName(Prop)).str();
17057006b14SXiang Li   }
17157006b14SXiang Li   return (Twine(DXILOpNamePrefix) + getOpCodeClassName(Prop) + "." +
17257006b14SXiang Li           getTypeName(Kind, Ty))
17357006b14SXiang Li       .str();
17457006b14SXiang Li }
17557006b14SXiang Li 
17657006b14SXiang Li static std::string constructOverloadTypeName(OverloadKind Kind,
17757006b14SXiang Li                                              StringRef TypeName) {
17857006b14SXiang Li   if (Kind == OverloadKind::VOID)
17957006b14SXiang Li     return TypeName.str();
18057006b14SXiang Li 
18157006b14SXiang Li   assert(Kind < OverloadKind::UserDefineType && "invalid overload kind");
18257006b14SXiang Li   return (Twine(TypeName) + getOverloadTypeName(Kind)).str();
18357006b14SXiang Li }
18457006b14SXiang Li 
18557006b14SXiang Li static StructType *getOrCreateStructType(StringRef Name,
18657006b14SXiang Li                                          ArrayRef<Type *> EltTys,
18757006b14SXiang Li                                          LLVMContext &Ctx) {
18857006b14SXiang Li   StructType *ST = StructType::getTypeByName(Ctx, Name);
18957006b14SXiang Li   if (ST)
19057006b14SXiang Li     return ST;
19157006b14SXiang Li 
19257006b14SXiang Li   return StructType::create(Ctx, EltTys, Name);
19357006b14SXiang Li }
19457006b14SXiang Li 
1953f22756fSJustin Bogner static StructType *getResRetType(Type *ElementTy) {
1963f22756fSJustin Bogner   LLVMContext &Ctx = ElementTy->getContext();
1973f22756fSJustin Bogner   OverloadKind Kind = getOverloadKind(ElementTy);
19857006b14SXiang Li   std::string TypeName = constructOverloadTypeName(Kind, "dx.types.ResRet.");
1993f22756fSJustin Bogner   Type *FieldTypes[5] = {ElementTy, ElementTy, ElementTy, ElementTy,
20057006b14SXiang Li                          Type::getInt32Ty(Ctx)};
20157006b14SXiang Li   return getOrCreateStructType(TypeName, FieldTypes, Ctx);
20257006b14SXiang Li }
20357006b14SXiang Li 
20457006b14SXiang Li static StructType *getHandleType(LLVMContext &Ctx) {
2057b9d73c2SPaulo Matos   return getOrCreateStructType("dx.types.Handle", PointerType::getUnqual(Ctx),
2067b9d73c2SPaulo Matos                                Ctx);
20757006b14SXiang Li }
20857006b14SXiang Li 
209aa61925eSJustin Bogner static StructType *getResBindType(LLVMContext &Context) {
210aa61925eSJustin Bogner   if (auto *ST = StructType::getTypeByName(Context, "dx.types.ResBind"))
211aa61925eSJustin Bogner     return ST;
212aa61925eSJustin Bogner   Type *Int32Ty = Type::getInt32Ty(Context);
213aa61925eSJustin Bogner   Type *Int8Ty = Type::getInt8Ty(Context);
214aa61925eSJustin Bogner   return StructType::create({Int32Ty, Int32Ty, Int32Ty, Int8Ty},
215aa61925eSJustin Bogner                             "dx.types.ResBind");
216aa61925eSJustin Bogner }
217aa61925eSJustin Bogner 
218aa61925eSJustin Bogner static StructType *getResPropsType(LLVMContext &Context) {
219aa61925eSJustin Bogner   if (auto *ST =
220aa61925eSJustin Bogner           StructType::getTypeByName(Context, "dx.types.ResourceProperties"))
221aa61925eSJustin Bogner     return ST;
222aa61925eSJustin Bogner   Type *Int32Ty = Type::getInt32Ty(Context);
223aa61925eSJustin Bogner   return StructType::create({Int32Ty, Int32Ty}, "dx.types.ResourceProperties");
224aa61925eSJustin Bogner }
225aa61925eSJustin Bogner 
226481bce01Sjoaosaffran static StructType *getSplitDoubleType(LLVMContext &Context) {
227481bce01Sjoaosaffran   if (auto *ST = StructType::getTypeByName(Context, "dx.types.splitdouble"))
228481bce01Sjoaosaffran     return ST;
229481bce01Sjoaosaffran   Type *Int32Ty = Type::getInt32Ty(Context);
230481bce01Sjoaosaffran   return StructType::create({Int32Ty, Int32Ty}, "dx.types.splitdouble");
231481bce01Sjoaosaffran }
232481bce01Sjoaosaffran 
233f357fe37SJustin Bogner static Type *getTypeFromOpParamType(OpParamType Kind, LLVMContext &Ctx,
2348cf85653SJustin Bogner                                     Type *OverloadTy) {
23557006b14SXiang Li   switch (Kind) {
236f357fe37SJustin Bogner   case OpParamType::VoidTy:
23757006b14SXiang Li     return Type::getVoidTy(Ctx);
238f357fe37SJustin Bogner   case OpParamType::HalfTy:
23957006b14SXiang Li     return Type::getHalfTy(Ctx);
240f357fe37SJustin Bogner   case OpParamType::FloatTy:
24157006b14SXiang Li     return Type::getFloatTy(Ctx);
242f357fe37SJustin Bogner   case OpParamType::DoubleTy:
24357006b14SXiang Li     return Type::getDoubleTy(Ctx);
244f357fe37SJustin Bogner   case OpParamType::Int1Ty:
24557006b14SXiang Li     return Type::getInt1Ty(Ctx);
246f357fe37SJustin Bogner   case OpParamType::Int8Ty:
24757006b14SXiang Li     return Type::getInt8Ty(Ctx);
248f357fe37SJustin Bogner   case OpParamType::Int16Ty:
24957006b14SXiang Li     return Type::getInt16Ty(Ctx);
250f357fe37SJustin Bogner   case OpParamType::Int32Ty:
25157006b14SXiang Li     return Type::getInt32Ty(Ctx);
252f357fe37SJustin Bogner   case OpParamType::Int64Ty:
25357006b14SXiang Li     return Type::getInt64Ty(Ctx);
254f357fe37SJustin Bogner   case OpParamType::OverloadTy:
25557006b14SXiang Li     return OverloadTy;
2563f22756fSJustin Bogner   case OpParamType::ResRetHalfTy:
2573f22756fSJustin Bogner     return getResRetType(Type::getHalfTy(Ctx));
2583f22756fSJustin Bogner   case OpParamType::ResRetFloatTy:
2593f22756fSJustin Bogner     return getResRetType(Type::getFloatTy(Ctx));
260cba9bd5cSJustin Bogner   case OpParamType::ResRetDoubleTy:
261cba9bd5cSJustin Bogner     return getResRetType(Type::getDoubleTy(Ctx));
2623f22756fSJustin Bogner   case OpParamType::ResRetInt16Ty:
2633f22756fSJustin Bogner     return getResRetType(Type::getInt16Ty(Ctx));
2643f22756fSJustin Bogner   case OpParamType::ResRetInt32Ty:
2653f22756fSJustin Bogner     return getResRetType(Type::getInt32Ty(Ctx));
266cba9bd5cSJustin Bogner   case OpParamType::ResRetInt64Ty:
267cba9bd5cSJustin Bogner     return getResRetType(Type::getInt64Ty(Ctx));
268f357fe37SJustin Bogner   case OpParamType::HandleTy:
26957006b14SXiang Li     return getHandleType(Ctx);
270aa61925eSJustin Bogner   case OpParamType::ResBindTy:
271aa61925eSJustin Bogner     return getResBindType(Ctx);
272aa61925eSJustin Bogner   case OpParamType::ResPropsTy:
273aa61925eSJustin Bogner     return getResPropsType(Ctx);
274481bce01Sjoaosaffran   case OpParamType::SplitDoubleTy:
275481bce01Sjoaosaffran     return getSplitDoubleType(Ctx);
27657006b14SXiang Li   }
27757006b14SXiang Li   llvm_unreachable("Invalid parameter kind");
27857006b14SXiang Li   return nullptr;
27957006b14SXiang Li }
28057006b14SXiang Li 
281cdfd884bSS. Bharadwaj Yadavalli static ShaderKind getShaderKindEnum(Triple::EnvironmentType EnvType) {
282cdfd884bSS. Bharadwaj Yadavalli   switch (EnvType) {
283cdfd884bSS. Bharadwaj Yadavalli   case Triple::Pixel:
284cdfd884bSS. Bharadwaj Yadavalli     return ShaderKind::pixel;
285cdfd884bSS. Bharadwaj Yadavalli   case Triple::Vertex:
286cdfd884bSS. Bharadwaj Yadavalli     return ShaderKind::vertex;
287cdfd884bSS. Bharadwaj Yadavalli   case Triple::Geometry:
288cdfd884bSS. Bharadwaj Yadavalli     return ShaderKind::geometry;
289cdfd884bSS. Bharadwaj Yadavalli   case Triple::Hull:
290cdfd884bSS. Bharadwaj Yadavalli     return ShaderKind::hull;
291cdfd884bSS. Bharadwaj Yadavalli   case Triple::Domain:
292cdfd884bSS. Bharadwaj Yadavalli     return ShaderKind::domain;
293cdfd884bSS. Bharadwaj Yadavalli   case Triple::Compute:
294cdfd884bSS. Bharadwaj Yadavalli     return ShaderKind::compute;
295cdfd884bSS. Bharadwaj Yadavalli   case Triple::Library:
296cdfd884bSS. Bharadwaj Yadavalli     return ShaderKind::library;
297cdfd884bSS. Bharadwaj Yadavalli   case Triple::RayGeneration:
298cdfd884bSS. Bharadwaj Yadavalli     return ShaderKind::raygeneration;
299cdfd884bSS. Bharadwaj Yadavalli   case Triple::Intersection:
300cdfd884bSS. Bharadwaj Yadavalli     return ShaderKind::intersection;
301cdfd884bSS. Bharadwaj Yadavalli   case Triple::AnyHit:
302cdfd884bSS. Bharadwaj Yadavalli     return ShaderKind::anyhit;
303cdfd884bSS. Bharadwaj Yadavalli   case Triple::ClosestHit:
304cdfd884bSS. Bharadwaj Yadavalli     return ShaderKind::closesthit;
305cdfd884bSS. Bharadwaj Yadavalli   case Triple::Miss:
306cdfd884bSS. Bharadwaj Yadavalli     return ShaderKind::miss;
307cdfd884bSS. Bharadwaj Yadavalli   case Triple::Callable:
308cdfd884bSS. Bharadwaj Yadavalli     return ShaderKind::callable;
309cdfd884bSS. Bharadwaj Yadavalli   case Triple::Mesh:
310cdfd884bSS. Bharadwaj Yadavalli     return ShaderKind::mesh;
311cdfd884bSS. Bharadwaj Yadavalli   case Triple::Amplification:
312cdfd884bSS. Bharadwaj Yadavalli     return ShaderKind::amplification;
313cdfd884bSS. Bharadwaj Yadavalli   default:
314cdfd884bSS. Bharadwaj Yadavalli     break;
315cdfd884bSS. Bharadwaj Yadavalli   }
316cdfd884bSS. Bharadwaj Yadavalli   llvm_unreachable(
317cdfd884bSS. Bharadwaj Yadavalli       "Shader Kind Not Found - Invalid DXIL Environment Specified");
318cdfd884bSS. Bharadwaj Yadavalli }
319cdfd884bSS. Bharadwaj Yadavalli 
320f357fe37SJustin Bogner static SmallVector<Type *>
321f357fe37SJustin Bogner getArgTypesFromOpParamTypes(ArrayRef<dxil::OpParamType> Types,
322f357fe37SJustin Bogner                             LLVMContext &Context, Type *OverloadTy) {
323f357fe37SJustin Bogner   SmallVector<Type *> ArgTys;
324f357fe37SJustin Bogner   ArgTys.emplace_back(Type::getInt32Ty(Context));
325f357fe37SJustin Bogner   for (dxil::OpParamType Ty : Types)
326f357fe37SJustin Bogner     ArgTys.emplace_back(getTypeFromOpParamType(Ty, Context, OverloadTy));
327f357fe37SJustin Bogner   return ArgTys;
328f357fe37SJustin Bogner }
329f357fe37SJustin Bogner 
330b1c8b9f8SS. Bharadwaj Yadavalli /// Construct DXIL function type. This is the type of a function with
331b1c8b9f8SS. Bharadwaj Yadavalli /// the following prototype
332b1c8b9f8SS. Bharadwaj Yadavalli ///     OverloadType dx.op.<opclass>.<return-type>(int opcode, <param types>)
333b1c8b9f8SS. Bharadwaj Yadavalli /// <param-types> are constructed from types in Prop.
334f357fe37SJustin Bogner static FunctionType *getDXILOpFunctionType(dxil::OpCode OpCode,
3358cf85653SJustin Bogner                                            LLVMContext &Context,
3368cf85653SJustin Bogner                                            Type *OverloadTy) {
33757006b14SXiang Li 
338f357fe37SJustin Bogner   switch (OpCode) {
339f357fe37SJustin Bogner #define DXIL_OP_FUNCTION_TYPE(OpCode, RetType, ...)                            \
340f357fe37SJustin Bogner   case OpCode:                                                                 \
341f357fe37SJustin Bogner     return FunctionType::get(                                                  \
342f357fe37SJustin Bogner         getTypeFromOpParamType(RetType, Context, OverloadTy),                  \
343f357fe37SJustin Bogner         getArgTypesFromOpParamTypes({__VA_ARGS__}, Context, OverloadTy),       \
344f357fe37SJustin Bogner         /*isVarArg=*/false);
345f357fe37SJustin Bogner #include "DXILOperation.inc"
34657006b14SXiang Li   }
347f357fe37SJustin Bogner   llvm_unreachable("Invalid OpCode?");
34857006b14SXiang Li }
34957006b14SXiang Li 
350cdfd884bSS. Bharadwaj Yadavalli /// Get index of the property from PropList valid for the most recent
351cdfd884bSS. Bharadwaj Yadavalli /// DXIL version not greater than DXILVer.
352cdfd884bSS. Bharadwaj Yadavalli /// PropList is expected to be sorted in ascending order of DXIL version.
353cdfd884bSS. Bharadwaj Yadavalli template <typename T>
354cdfd884bSS. Bharadwaj Yadavalli static std::optional<size_t> getPropIndex(ArrayRef<T> PropList,
355cdfd884bSS. Bharadwaj Yadavalli                                           const VersionTuple DXILVer) {
356cdfd884bSS. Bharadwaj Yadavalli   size_t Index = PropList.size() - 1;
357cdfd884bSS. Bharadwaj Yadavalli   for (auto Iter = PropList.rbegin(); Iter != PropList.rend();
358cdfd884bSS. Bharadwaj Yadavalli        Iter++, Index--) {
359cdfd884bSS. Bharadwaj Yadavalli     const T &Prop = *Iter;
360cdfd884bSS. Bharadwaj Yadavalli     if (VersionTuple(Prop.DXILVersion.Major, Prop.DXILVersion.Minor) <=
361cdfd884bSS. Bharadwaj Yadavalli         DXILVer) {
362cdfd884bSS. Bharadwaj Yadavalli       return Index;
363cdfd884bSS. Bharadwaj Yadavalli     }
364cdfd884bSS. Bharadwaj Yadavalli   }
365cdfd884bSS. Bharadwaj Yadavalli   return std::nullopt;
366cdfd884bSS. Bharadwaj Yadavalli }
367cdfd884bSS. Bharadwaj Yadavalli 
368*011b6186SFinn Plummer // Helper function to pack an OpCode and VersionTuple into a uint64_t for use
369*011b6186SFinn Plummer // in a switch statement
370*011b6186SFinn Plummer constexpr static uint64_t computeSwitchEnum(dxil::OpCode OpCode,
371*011b6186SFinn Plummer                                             uint16_t VersionMajor,
372*011b6186SFinn Plummer                                             uint16_t VersionMinor) {
373*011b6186SFinn Plummer   uint64_t OpCodePack = (uint64_t)OpCode;
374*011b6186SFinn Plummer   return (OpCodePack << 32) | (VersionMajor << 16) | VersionMinor;
375*011b6186SFinn Plummer }
376*011b6186SFinn Plummer 
377*011b6186SFinn Plummer // Retreive all the set attributes for a DXIL OpCode given the targeted
378*011b6186SFinn Plummer // DXILVersion
379*011b6186SFinn Plummer static dxil::Attributes getDXILAttributes(dxil::OpCode OpCode,
380*011b6186SFinn Plummer                                           VersionTuple DXILVersion) {
381*011b6186SFinn Plummer   // Instantiate all versions to iterate through
382*011b6186SFinn Plummer   SmallVector<Version> Versions = {
383*011b6186SFinn Plummer #define DXIL_VERSION(MAJOR, MINOR) {MAJOR, MINOR},
384*011b6186SFinn Plummer #include "DXILOperation.inc"
385*011b6186SFinn Plummer   };
386*011b6186SFinn Plummer 
387*011b6186SFinn Plummer   dxil::Attributes Attributes;
388*011b6186SFinn Plummer   for (auto Version : Versions) {
389*011b6186SFinn Plummer     if (DXILVersion < VersionTuple(Version.Major, Version.Minor))
390*011b6186SFinn Plummer       continue;
391*011b6186SFinn Plummer 
392*011b6186SFinn Plummer     // Switch through and match an OpCode with the specific version and set the
393*011b6186SFinn Plummer     // corresponding flag(s) if available
394*011b6186SFinn Plummer     switch (computeSwitchEnum(OpCode, Version.Major, Version.Minor)) {
395*011b6186SFinn Plummer #define DXIL_OP_ATTRIBUTES(OpCode, VersionMajor, VersionMinor, ...)            \
396*011b6186SFinn Plummer   case computeSwitchEnum(OpCode, VersionMajor, VersionMinor): {                \
397*011b6186SFinn Plummer     auto Other = dxil::Attributes{__VA_ARGS__};                                \
398*011b6186SFinn Plummer     Attributes |= Other;                                                       \
399*011b6186SFinn Plummer     break;                                                                     \
400*011b6186SFinn Plummer   };
401*011b6186SFinn Plummer #include "DXILOperation.inc"
402*011b6186SFinn Plummer     }
403*011b6186SFinn Plummer   }
404*011b6186SFinn Plummer   return Attributes;
405*011b6186SFinn Plummer }
406*011b6186SFinn Plummer 
407*011b6186SFinn Plummer // Retreive the set of DXIL Attributes given the version and map them to an
408*011b6186SFinn Plummer // llvm function attribute that is set onto the instruction
409*011b6186SFinn Plummer static void setDXILAttributes(CallInst *CI, dxil::OpCode OpCode,
410*011b6186SFinn Plummer                               VersionTuple DXILVersion) {
411*011b6186SFinn Plummer   dxil::Attributes Attributes = getDXILAttributes(OpCode, DXILVersion);
412*011b6186SFinn Plummer   if (Attributes.ReadNone)
413*011b6186SFinn Plummer     CI->setDoesNotAccessMemory();
414*011b6186SFinn Plummer   if (Attributes.ReadOnly)
415*011b6186SFinn Plummer     CI->setOnlyReadsMemory();
416*011b6186SFinn Plummer   if (Attributes.NoReturn)
417*011b6186SFinn Plummer     CI->setDoesNotReturn();
418*011b6186SFinn Plummer   if (Attributes.NoDuplicate)
419*011b6186SFinn Plummer     CI->setCannotDuplicate();
420*011b6186SFinn Plummer   return;
421*011b6186SFinn Plummer }
422*011b6186SFinn Plummer 
4233f395712SS. Bharadwaj Yadavalli namespace llvm {
4243f395712SS. Bharadwaj Yadavalli namespace dxil {
4253f395712SS. Bharadwaj Yadavalli 
426cdfd884bSS. Bharadwaj Yadavalli // No extra checks on TargetTriple need be performed to verify that the
427cdfd884bSS. Bharadwaj Yadavalli // Triple is well-formed or that the target is supported since these checks
428cdfd884bSS. Bharadwaj Yadavalli // would have been done at the time the module M is constructed in the earlier
429cdfd884bSS. Bharadwaj Yadavalli // stages of compilation.
430e56ad22bSJustin Bogner DXILOpBuilder::DXILOpBuilder(Module &M) : M(M), IRB(M.getContext()) {
431cdfd884bSS. Bharadwaj Yadavalli   Triple TT(Triple(M.getTargetTriple()));
432cdfd884bSS. Bharadwaj Yadavalli   DXILVersion = TT.getDXILVersion();
433cdfd884bSS. Bharadwaj Yadavalli   ShaderStage = TT.getEnvironment();
434cdfd884bSS. Bharadwaj Yadavalli   // Ensure Environment type is known
435cdfd884bSS. Bharadwaj Yadavalli   if (ShaderStage == Triple::UnknownEnvironment) {
436cdfd884bSS. Bharadwaj Yadavalli     report_fatal_error(
437cdfd884bSS. Bharadwaj Yadavalli         Twine(DXILVersion.getAsString()) +
438cdfd884bSS. Bharadwaj Yadavalli             ": Unknown Compilation Target Shader Stage specified ",
439cdfd884bSS. Bharadwaj Yadavalli         /*gen_crash_diag*/ false);
440cdfd884bSS. Bharadwaj Yadavalli   }
441cdfd884bSS. Bharadwaj Yadavalli }
442cdfd884bSS. Bharadwaj Yadavalli 
4438cf85653SJustin Bogner static Error makeOpError(dxil::OpCode OpCode, Twine Msg) {
4448cf85653SJustin Bogner   return make_error<StringError>(
4458cf85653SJustin Bogner       Twine("Cannot create ") + getOpCodeName(OpCode) + " operation: " + Msg,
4468cf85653SJustin Bogner       inconvertibleErrorCode());
4478cf85653SJustin Bogner }
448cdfd884bSS. Bharadwaj Yadavalli 
4498cf85653SJustin Bogner Expected<CallInst *> DXILOpBuilder::tryCreateOp(dxil::OpCode OpCode,
4508cf85653SJustin Bogner                                                 ArrayRef<Value *> Args,
4513d129016SJustin Bogner                                                 const Twine &Name,
4528cf85653SJustin Bogner                                                 Type *RetTy) {
4533f395712SS. Bharadwaj Yadavalli   const OpCodeProperty *Prop = getOpCodeProperty(OpCode);
4548cf85653SJustin Bogner 
4558cf85653SJustin Bogner   Type *OverloadTy = nullptr;
4568cf85653SJustin Bogner   if (Prop->OverloadParamIndex == 0) {
4578cf85653SJustin Bogner     if (!RetTy)
4588cf85653SJustin Bogner       return makeOpError(OpCode, "Op overloaded on unknown return type");
4598cf85653SJustin Bogner     OverloadTy = RetTy;
4608cf85653SJustin Bogner   } else if (Prop->OverloadParamIndex > 0) {
4618cf85653SJustin Bogner     // The index counts including the return type
4628cf85653SJustin Bogner     unsigned ArgIndex = Prop->OverloadParamIndex - 1;
4638cf85653SJustin Bogner     if (static_cast<unsigned>(ArgIndex) >= Args.size())
4648cf85653SJustin Bogner       return makeOpError(OpCode, "Wrong number of arguments");
4658cf85653SJustin Bogner     OverloadTy = Args[ArgIndex]->getType();
4668cf85653SJustin Bogner   }
4673f22756fSJustin Bogner 
4688cf85653SJustin Bogner   FunctionType *DXILOpFT =
469f357fe37SJustin Bogner       getDXILOpFunctionType(OpCode, M.getContext(), OverloadTy);
4708cf85653SJustin Bogner 
471cdfd884bSS. Bharadwaj Yadavalli   std::optional<size_t> OlIndexOrErr =
472cdfd884bSS. Bharadwaj Yadavalli       getPropIndex(ArrayRef(Prop->Overloads), DXILVersion);
4738cf85653SJustin Bogner   if (!OlIndexOrErr.has_value())
4748cf85653SJustin Bogner     return makeOpError(OpCode, Twine("No valid overloads for DXIL version ") +
4758cf85653SJustin Bogner                                    DXILVersion.getAsString());
4768cf85653SJustin Bogner 
477cdfd884bSS. Bharadwaj Yadavalli   uint16_t ValidTyMask = Prop->Overloads[*OlIndexOrErr].ValidTys;
47857006b14SXiang Li 
4797d60f464SJustin Bogner   OverloadKind Kind = getOverloadKind(OverloadTy);
480cdfd884bSS. Bharadwaj Yadavalli 
481cdfd884bSS. Bharadwaj Yadavalli   // Check if the operation supports overload types and OverloadTy is valid
482cdfd884bSS. Bharadwaj Yadavalli   // per the specified types for the operation
483cdfd884bSS. Bharadwaj Yadavalli   if ((ValidTyMask != OverloadKind::UNDEFINED) &&
4848cf85653SJustin Bogner       (ValidTyMask & (uint16_t)Kind) == 0)
4858cf85653SJustin Bogner     return makeOpError(OpCode, "Invalid overload type");
486cdfd884bSS. Bharadwaj Yadavalli 
487cdfd884bSS. Bharadwaj Yadavalli   // Perform necessary checks to ensure Opcode is valid in the targeted shader
488cdfd884bSS. Bharadwaj Yadavalli   // kind
489cdfd884bSS. Bharadwaj Yadavalli   std::optional<size_t> StIndexOrErr =
490cdfd884bSS. Bharadwaj Yadavalli       getPropIndex(ArrayRef(Prop->Stages), DXILVersion);
4918cf85653SJustin Bogner   if (!StIndexOrErr.has_value())
4928cf85653SJustin Bogner     return makeOpError(OpCode, Twine("No valid stage for DXIL version ") +
4938cf85653SJustin Bogner                                    DXILVersion.getAsString());
4948cf85653SJustin Bogner 
495cdfd884bSS. Bharadwaj Yadavalli   uint16_t ValidShaderKindMask = Prop->Stages[*StIndexOrErr].ValidStages;
496cdfd884bSS. Bharadwaj Yadavalli 
497cdfd884bSS. Bharadwaj Yadavalli   // Ensure valid shader stage properties are specified
4988cf85653SJustin Bogner   if (ValidShaderKindMask == ShaderKind::removed)
4998cf85653SJustin Bogner     return makeOpError(OpCode, "Operation has been removed");
500cdfd884bSS. Bharadwaj Yadavalli 
501cdfd884bSS. Bharadwaj Yadavalli   // Shader stage need not be validated since getShaderKindEnum() fails
502cdfd884bSS. Bharadwaj Yadavalli   // for unknown shader stage.
503cdfd884bSS. Bharadwaj Yadavalli 
504cdfd884bSS. Bharadwaj Yadavalli   // Verify the target shader stage is valid for the DXIL operation
505cdfd884bSS. Bharadwaj Yadavalli   ShaderKind ModuleStagekind = getShaderKindEnum(ShaderStage);
5068cf85653SJustin Bogner   if (!(ValidShaderKindMask & ModuleStagekind))
5078cf85653SJustin Bogner     return makeOpError(OpCode, "Invalid stage");
50857006b14SXiang Li 
5093f395712SS. Bharadwaj Yadavalli   std::string DXILFnName = constructOverloadName(Kind, OverloadTy, *Prop);
5108cf85653SJustin Bogner   FunctionCallee DXILFn = M.getOrInsertFunction(DXILFnName, DXILOpFT);
5118cf85653SJustin Bogner 
5128cf85653SJustin Bogner   // We need to inject the opcode as the first argument.
5138cf85653SJustin Bogner   SmallVector<Value *> OpArgs;
514e56ad22bSJustin Bogner   OpArgs.push_back(IRB.getInt32(llvm::to_underlying(OpCode)));
5158cf85653SJustin Bogner   OpArgs.append(Args.begin(), Args.end());
5168cf85653SJustin Bogner 
517*011b6186SFinn Plummer   // Create the function call instruction
518*011b6186SFinn Plummer   CallInst *CI = IRB.CreateCall(DXILFn, OpArgs, Name);
519*011b6186SFinn Plummer 
520*011b6186SFinn Plummer   // We then need to attach available function attributes
521*011b6186SFinn Plummer   setDXILAttributes(CI, OpCode, DXILVersion);
522*011b6186SFinn Plummer 
523*011b6186SFinn Plummer   return CI;
52457006b14SXiang Li }
525060df78cSFarzon Lotfi 
52671d54faaSJustin Bogner CallInst *DXILOpBuilder::createOp(dxil::OpCode OpCode, ArrayRef<Value *> Args,
5273d129016SJustin Bogner                                   const Twine &Name, Type *RetTy) {
5283d129016SJustin Bogner   Expected<CallInst *> Result = tryCreateOp(OpCode, Args, Name, RetTy);
5298cf85653SJustin Bogner   if (Error E = Result.takeError())
5308cf85653SJustin Bogner     llvm_unreachable("Invalid arguments for operation");
5318cf85653SJustin Bogner   return *Result;
53257006b14SXiang Li }
53357006b14SXiang Li 
5343f22756fSJustin Bogner StructType *DXILOpBuilder::getResRetType(Type *ElementTy) {
5353f22756fSJustin Bogner   return ::getResRetType(ElementTy);
5363f22756fSJustin Bogner }
5373f22756fSJustin Bogner 
538481bce01Sjoaosaffran StructType *DXILOpBuilder::getSplitDoubleType(LLVMContext &Context) {
539481bce01Sjoaosaffran   return ::getSplitDoubleType(Context);
540481bce01Sjoaosaffran }
541481bce01Sjoaosaffran 
542aa61925eSJustin Bogner StructType *DXILOpBuilder::getHandleType() {
543aa61925eSJustin Bogner   return ::getHandleType(IRB.getContext());
544aa61925eSJustin Bogner }
545aa61925eSJustin Bogner 
546aa61925eSJustin Bogner Constant *DXILOpBuilder::getResBind(uint32_t LowerBound, uint32_t UpperBound,
547aa61925eSJustin Bogner                                     uint32_t SpaceID, dxil::ResourceClass RC) {
548aa61925eSJustin Bogner   Type *Int32Ty = IRB.getInt32Ty();
549aa61925eSJustin Bogner   Type *Int8Ty = IRB.getInt8Ty();
550aa61925eSJustin Bogner   return ConstantStruct::get(
551aa61925eSJustin Bogner       getResBindType(IRB.getContext()),
552aa61925eSJustin Bogner       {ConstantInt::get(Int32Ty, LowerBound),
553aa61925eSJustin Bogner        ConstantInt::get(Int32Ty, UpperBound),
554aa61925eSJustin Bogner        ConstantInt::get(Int32Ty, SpaceID),
555aa61925eSJustin Bogner        ConstantInt::get(Int8Ty, llvm::to_underlying(RC))});
556aa61925eSJustin Bogner }
557aa61925eSJustin Bogner 
558aa61925eSJustin Bogner Constant *DXILOpBuilder::getResProps(uint32_t Word0, uint32_t Word1) {
559aa61925eSJustin Bogner   Type *Int32Ty = IRB.getInt32Ty();
560aa61925eSJustin Bogner   return ConstantStruct::get(
561aa61925eSJustin Bogner       getResPropsType(IRB.getContext()),
562aa61925eSJustin Bogner       {ConstantInt::get(Int32Ty, Word0), ConstantInt::get(Int32Ty, Word1)});
563aa61925eSJustin Bogner }
564aa61925eSJustin Bogner 
565e77c40ffSChris Bieneman const char *DXILOpBuilder::getOpCodeName(dxil::OpCode DXILOp) {
56657006b14SXiang Li   return ::getOpCodeName(DXILOp);
56757006b14SXiang Li }
568e77c40ffSChris Bieneman } // namespace dxil
56957006b14SXiang Li } // namespace llvm
570