xref: /freebsd-src/contrib/llvm-project/llvm/lib/Target/DirectX/DXILOpBuilder.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
1972a253aSDimitry Andric //===- DXILOpBuilder.cpp - Helper class for build DIXLOp functions --------===//
2972a253aSDimitry Andric //
3972a253aSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4972a253aSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5972a253aSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6972a253aSDimitry Andric //
7972a253aSDimitry Andric //===----------------------------------------------------------------------===//
8972a253aSDimitry Andric ///
9972a253aSDimitry Andric /// \file This file contains class to help build DXIL op functions.
10972a253aSDimitry Andric //===----------------------------------------------------------------------===//
11972a253aSDimitry Andric 
12972a253aSDimitry Andric #include "DXILOpBuilder.h"
13972a253aSDimitry Andric #include "DXILConstants.h"
14972a253aSDimitry Andric #include "llvm/IR/IRBuilder.h"
15972a253aSDimitry Andric #include "llvm/IR/Module.h"
16*0fca6ea1SDimitry Andric #include "llvm/Support/DXILABI.h"
17972a253aSDimitry Andric #include "llvm/Support/ErrorHandling.h"
18972a253aSDimitry Andric 
19972a253aSDimitry Andric using namespace llvm;
20bdd1243dSDimitry Andric using namespace llvm::dxil;
21972a253aSDimitry Andric 
22972a253aSDimitry Andric constexpr StringLiteral DXILOpNamePrefix = "dx.op.";
23972a253aSDimitry Andric 
24972a253aSDimitry Andric namespace {
25972a253aSDimitry Andric 
26972a253aSDimitry Andric enum OverloadKind : uint16_t {
27972a253aSDimitry Andric   VOID = 1,
28972a253aSDimitry Andric   HALF = 1 << 1,
29972a253aSDimitry Andric   FLOAT = 1 << 2,
30972a253aSDimitry Andric   DOUBLE = 1 << 3,
31972a253aSDimitry Andric   I1 = 1 << 4,
32972a253aSDimitry Andric   I8 = 1 << 5,
33972a253aSDimitry Andric   I16 = 1 << 6,
34972a253aSDimitry Andric   I32 = 1 << 7,
35972a253aSDimitry Andric   I64 = 1 << 8,
36972a253aSDimitry Andric   UserDefineType = 1 << 9,
37972a253aSDimitry Andric   ObjectType = 1 << 10,
38972a253aSDimitry Andric };
39972a253aSDimitry Andric 
40972a253aSDimitry Andric } // namespace
41972a253aSDimitry Andric 
42972a253aSDimitry Andric static const char *getOverloadTypeName(OverloadKind Kind) {
43972a253aSDimitry Andric   switch (Kind) {
44972a253aSDimitry Andric   case OverloadKind::HALF:
45972a253aSDimitry Andric     return "f16";
46972a253aSDimitry Andric   case OverloadKind::FLOAT:
47972a253aSDimitry Andric     return "f32";
48972a253aSDimitry Andric   case OverloadKind::DOUBLE:
49972a253aSDimitry Andric     return "f64";
50972a253aSDimitry Andric   case OverloadKind::I1:
51972a253aSDimitry Andric     return "i1";
52972a253aSDimitry Andric   case OverloadKind::I8:
53972a253aSDimitry Andric     return "i8";
54972a253aSDimitry Andric   case OverloadKind::I16:
55972a253aSDimitry Andric     return "i16";
56972a253aSDimitry Andric   case OverloadKind::I32:
57972a253aSDimitry Andric     return "i32";
58972a253aSDimitry Andric   case OverloadKind::I64:
59972a253aSDimitry Andric     return "i64";
60972a253aSDimitry Andric   case OverloadKind::VOID:
61972a253aSDimitry Andric   case OverloadKind::ObjectType:
62972a253aSDimitry Andric   case OverloadKind::UserDefineType:
63972a253aSDimitry Andric     break;
64972a253aSDimitry Andric   }
65972a253aSDimitry Andric   llvm_unreachable("invalid overload type for name");
66972a253aSDimitry Andric   return "void";
67972a253aSDimitry Andric }
68972a253aSDimitry Andric 
69972a253aSDimitry Andric static OverloadKind getOverloadKind(Type *Ty) {
70972a253aSDimitry Andric   Type::TypeID T = Ty->getTypeID();
71972a253aSDimitry Andric   switch (T) {
72972a253aSDimitry Andric   case Type::VoidTyID:
73972a253aSDimitry Andric     return OverloadKind::VOID;
74972a253aSDimitry Andric   case Type::HalfTyID:
75972a253aSDimitry Andric     return OverloadKind::HALF;
76972a253aSDimitry Andric   case Type::FloatTyID:
77972a253aSDimitry Andric     return OverloadKind::FLOAT;
78972a253aSDimitry Andric   case Type::DoubleTyID:
79972a253aSDimitry Andric     return OverloadKind::DOUBLE;
80972a253aSDimitry Andric   case Type::IntegerTyID: {
81972a253aSDimitry Andric     IntegerType *ITy = cast<IntegerType>(Ty);
82972a253aSDimitry Andric     unsigned Bits = ITy->getBitWidth();
83972a253aSDimitry Andric     switch (Bits) {
84972a253aSDimitry Andric     case 1:
85972a253aSDimitry Andric       return OverloadKind::I1;
86972a253aSDimitry Andric     case 8:
87972a253aSDimitry Andric       return OverloadKind::I8;
88972a253aSDimitry Andric     case 16:
89972a253aSDimitry Andric       return OverloadKind::I16;
90972a253aSDimitry Andric     case 32:
91972a253aSDimitry Andric       return OverloadKind::I32;
92972a253aSDimitry Andric     case 64:
93972a253aSDimitry Andric       return OverloadKind::I64;
94972a253aSDimitry Andric     default:
95972a253aSDimitry Andric       llvm_unreachable("invalid overload type");
96972a253aSDimitry Andric       return OverloadKind::VOID;
97972a253aSDimitry Andric     }
98972a253aSDimitry Andric   }
99972a253aSDimitry Andric   case Type::PointerTyID:
100972a253aSDimitry Andric     return OverloadKind::UserDefineType;
101972a253aSDimitry Andric   case Type::StructTyID:
102972a253aSDimitry Andric     return OverloadKind::ObjectType;
103972a253aSDimitry Andric   default:
104972a253aSDimitry Andric     llvm_unreachable("invalid overload type");
105972a253aSDimitry Andric     return OverloadKind::VOID;
106972a253aSDimitry Andric   }
107972a253aSDimitry Andric }
108972a253aSDimitry Andric 
109972a253aSDimitry Andric static std::string getTypeName(OverloadKind Kind, Type *Ty) {
110972a253aSDimitry Andric   if (Kind < OverloadKind::UserDefineType) {
111972a253aSDimitry Andric     return getOverloadTypeName(Kind);
112972a253aSDimitry Andric   } else if (Kind == OverloadKind::UserDefineType) {
113972a253aSDimitry Andric     StructType *ST = cast<StructType>(Ty);
114972a253aSDimitry Andric     return ST->getStructName().str();
115972a253aSDimitry Andric   } else if (Kind == OverloadKind::ObjectType) {
116972a253aSDimitry Andric     StructType *ST = cast<StructType>(Ty);
117972a253aSDimitry Andric     return ST->getStructName().str();
118972a253aSDimitry Andric   } else {
119972a253aSDimitry Andric     std::string Str;
120972a253aSDimitry Andric     raw_string_ostream OS(Str);
121972a253aSDimitry Andric     Ty->print(OS);
122972a253aSDimitry Andric     return OS.str();
123972a253aSDimitry Andric   }
124972a253aSDimitry Andric }
125972a253aSDimitry Andric 
126972a253aSDimitry Andric // Static properties.
127972a253aSDimitry Andric struct OpCodeProperty {
128bdd1243dSDimitry Andric   dxil::OpCode OpCode;
129972a253aSDimitry Andric   // Offset in DXILOpCodeNameTable.
130972a253aSDimitry Andric   unsigned OpCodeNameOffset;
131bdd1243dSDimitry Andric   dxil::OpCodeClass OpCodeClass;
132972a253aSDimitry Andric   // Offset in DXILOpCodeClassNameTable.
133972a253aSDimitry Andric   unsigned OpCodeClassNameOffset;
134972a253aSDimitry Andric   uint16_t OverloadTys;
135972a253aSDimitry Andric   llvm::Attribute::AttrKind FuncAttr;
136972a253aSDimitry Andric   int OverloadParamIndex;        // parameter index which control the overload.
137972a253aSDimitry Andric                                  // When < 0, should be only 1 overload type.
138972a253aSDimitry Andric   unsigned NumOfParameters;      // Number of parameters include return value.
139972a253aSDimitry Andric   unsigned ParameterTableOffset; // Offset in ParameterTable.
140972a253aSDimitry Andric };
141972a253aSDimitry Andric 
142972a253aSDimitry Andric // Include getOpCodeClassName getOpCodeProperty, getOpCodeName and
143972a253aSDimitry Andric // getOpCodeParameterKind which generated by tableGen.
144972a253aSDimitry Andric #define DXIL_OP_OPERATION_TABLE
145972a253aSDimitry Andric #include "DXILOperation.inc"
146972a253aSDimitry Andric #undef DXIL_OP_OPERATION_TABLE
147972a253aSDimitry Andric 
148972a253aSDimitry Andric static std::string constructOverloadName(OverloadKind Kind, Type *Ty,
149972a253aSDimitry Andric                                          const OpCodeProperty &Prop) {
150972a253aSDimitry Andric   if (Kind == OverloadKind::VOID) {
151972a253aSDimitry Andric     return (Twine(DXILOpNamePrefix) + getOpCodeClassName(Prop)).str();
152972a253aSDimitry Andric   }
153972a253aSDimitry Andric   return (Twine(DXILOpNamePrefix) + getOpCodeClassName(Prop) + "." +
154972a253aSDimitry Andric           getTypeName(Kind, Ty))
155972a253aSDimitry Andric       .str();
156972a253aSDimitry Andric }
157972a253aSDimitry Andric 
158972a253aSDimitry Andric static std::string constructOverloadTypeName(OverloadKind Kind,
159972a253aSDimitry Andric                                              StringRef TypeName) {
160972a253aSDimitry Andric   if (Kind == OverloadKind::VOID)
161972a253aSDimitry Andric     return TypeName.str();
162972a253aSDimitry Andric 
163972a253aSDimitry Andric   assert(Kind < OverloadKind::UserDefineType && "invalid overload kind");
164972a253aSDimitry Andric   return (Twine(TypeName) + getOverloadTypeName(Kind)).str();
165972a253aSDimitry Andric }
166972a253aSDimitry Andric 
167972a253aSDimitry Andric static StructType *getOrCreateStructType(StringRef Name,
168972a253aSDimitry Andric                                          ArrayRef<Type *> EltTys,
169972a253aSDimitry Andric                                          LLVMContext &Ctx) {
170972a253aSDimitry Andric   StructType *ST = StructType::getTypeByName(Ctx, Name);
171972a253aSDimitry Andric   if (ST)
172972a253aSDimitry Andric     return ST;
173972a253aSDimitry Andric 
174972a253aSDimitry Andric   return StructType::create(Ctx, EltTys, Name);
175972a253aSDimitry Andric }
176972a253aSDimitry Andric 
177972a253aSDimitry Andric static StructType *getResRetType(Type *OverloadTy, LLVMContext &Ctx) {
178972a253aSDimitry Andric   OverloadKind Kind = getOverloadKind(OverloadTy);
179972a253aSDimitry Andric   std::string TypeName = constructOverloadTypeName(Kind, "dx.types.ResRet.");
180972a253aSDimitry Andric   Type *FieldTypes[5] = {OverloadTy, OverloadTy, OverloadTy, OverloadTy,
181972a253aSDimitry Andric                          Type::getInt32Ty(Ctx)};
182972a253aSDimitry Andric   return getOrCreateStructType(TypeName, FieldTypes, Ctx);
183972a253aSDimitry Andric }
184972a253aSDimitry Andric 
185972a253aSDimitry Andric static StructType *getHandleType(LLVMContext &Ctx) {
1865f757f3fSDimitry Andric   return getOrCreateStructType("dx.types.Handle", PointerType::getUnqual(Ctx),
1875f757f3fSDimitry Andric                                Ctx);
188972a253aSDimitry Andric }
189972a253aSDimitry Andric 
190972a253aSDimitry Andric static Type *getTypeFromParameterKind(ParameterKind Kind, Type *OverloadTy) {
191972a253aSDimitry Andric   auto &Ctx = OverloadTy->getContext();
192972a253aSDimitry Andric   switch (Kind) {
193*0fca6ea1SDimitry Andric   case ParameterKind::Void:
194972a253aSDimitry Andric     return Type::getVoidTy(Ctx);
195*0fca6ea1SDimitry Andric   case ParameterKind::Half:
196972a253aSDimitry Andric     return Type::getHalfTy(Ctx);
197*0fca6ea1SDimitry Andric   case ParameterKind::Float:
198972a253aSDimitry Andric     return Type::getFloatTy(Ctx);
199*0fca6ea1SDimitry Andric   case ParameterKind::Double:
200972a253aSDimitry Andric     return Type::getDoubleTy(Ctx);
201972a253aSDimitry Andric   case ParameterKind::I1:
202972a253aSDimitry Andric     return Type::getInt1Ty(Ctx);
203972a253aSDimitry Andric   case ParameterKind::I8:
204972a253aSDimitry Andric     return Type::getInt8Ty(Ctx);
205972a253aSDimitry Andric   case ParameterKind::I16:
206972a253aSDimitry Andric     return Type::getInt16Ty(Ctx);
207972a253aSDimitry Andric   case ParameterKind::I32:
208972a253aSDimitry Andric     return Type::getInt32Ty(Ctx);
209972a253aSDimitry Andric   case ParameterKind::I64:
210972a253aSDimitry Andric     return Type::getInt64Ty(Ctx);
211*0fca6ea1SDimitry Andric   case ParameterKind::Overload:
212972a253aSDimitry Andric     return OverloadTy;
213*0fca6ea1SDimitry Andric   case ParameterKind::ResourceRet:
214972a253aSDimitry Andric     return getResRetType(OverloadTy, Ctx);
215*0fca6ea1SDimitry Andric   case ParameterKind::DXILHandle:
216972a253aSDimitry Andric     return getHandleType(Ctx);
217972a253aSDimitry Andric   default:
218972a253aSDimitry Andric     break;
219972a253aSDimitry Andric   }
220972a253aSDimitry Andric   llvm_unreachable("Invalid parameter kind");
221972a253aSDimitry Andric   return nullptr;
222972a253aSDimitry Andric }
223972a253aSDimitry Andric 
224*0fca6ea1SDimitry Andric /// Construct DXIL function type. This is the type of a function with
225*0fca6ea1SDimitry Andric /// the following prototype
226*0fca6ea1SDimitry Andric ///     OverloadType dx.op.<opclass>.<return-type>(int opcode, <param types>)
227*0fca6ea1SDimitry Andric /// <param-types> are constructed from types in Prop.
228*0fca6ea1SDimitry Andric /// \param Prop  Structure containing DXIL Operation properties based on
229*0fca6ea1SDimitry Andric ///               its specification in DXIL.td.
230*0fca6ea1SDimitry Andric /// \param OverloadTy Return type to be used to construct DXIL function type.
231972a253aSDimitry Andric static FunctionType *getDXILOpFunctionType(const OpCodeProperty *Prop,
232*0fca6ea1SDimitry Andric                                            Type *ReturnTy, Type *OverloadTy) {
233972a253aSDimitry Andric   SmallVector<Type *> ArgTys;
234972a253aSDimitry Andric 
235972a253aSDimitry Andric   auto ParamKinds = getOpCodeParameterKind(*Prop);
236972a253aSDimitry Andric 
237*0fca6ea1SDimitry Andric   // Add ReturnTy as return type of the function
238*0fca6ea1SDimitry Andric   ArgTys.emplace_back(ReturnTy);
239*0fca6ea1SDimitry Andric 
240*0fca6ea1SDimitry Andric   // Add DXIL Opcode value type viz., Int32 as first argument
241*0fca6ea1SDimitry Andric   ArgTys.emplace_back(Type::getInt32Ty(OverloadTy->getContext()));
242*0fca6ea1SDimitry Andric 
243*0fca6ea1SDimitry Andric   // Add DXIL Operation parameter types as specified in DXIL properties
244972a253aSDimitry Andric   for (unsigned I = 0; I < Prop->NumOfParameters; ++I) {
245972a253aSDimitry Andric     ParameterKind Kind = ParamKinds[I];
246972a253aSDimitry Andric     ArgTys.emplace_back(getTypeFromParameterKind(Kind, OverloadTy));
247972a253aSDimitry Andric   }
248972a253aSDimitry Andric   return FunctionType::get(
249972a253aSDimitry Andric       ArgTys[0], ArrayRef<Type *>(&ArgTys[1], ArgTys.size() - 1), false);
250972a253aSDimitry Andric }
251972a253aSDimitry Andric 
252972a253aSDimitry Andric namespace llvm {
253bdd1243dSDimitry Andric namespace dxil {
254972a253aSDimitry Andric 
255*0fca6ea1SDimitry Andric CallInst *DXILOpBuilder::createDXILOpCall(dxil::OpCode OpCode, Type *ReturnTy,
256*0fca6ea1SDimitry Andric                                           Type *OverloadTy,
257*0fca6ea1SDimitry Andric                                           SmallVector<Value *> Args) {
258*0fca6ea1SDimitry Andric   const OpCodeProperty *Prop = getOpCodeProperty(OpCode);
259*0fca6ea1SDimitry Andric 
260*0fca6ea1SDimitry Andric   OverloadKind Kind = getOverloadKind(OverloadTy);
261*0fca6ea1SDimitry Andric   if ((Prop->OverloadTys & (uint16_t)Kind) == 0) {
262*0fca6ea1SDimitry Andric     report_fatal_error("Invalid Overload Type", /* gen_crash_diag=*/false);
263972a253aSDimitry Andric   }
264972a253aSDimitry Andric 
265*0fca6ea1SDimitry Andric   std::string DXILFnName = constructOverloadName(Kind, OverloadTy, *Prop);
266*0fca6ea1SDimitry Andric   FunctionCallee DXILFn;
267*0fca6ea1SDimitry Andric   // Get the function with name DXILFnName, if one exists
268*0fca6ea1SDimitry Andric   if (auto *Func = M.getFunction(DXILFnName)) {
269*0fca6ea1SDimitry Andric     DXILFn = FunctionCallee(Func);
270*0fca6ea1SDimitry Andric   } else {
271*0fca6ea1SDimitry Andric     // Construct and add a function with name DXILFnName
272*0fca6ea1SDimitry Andric     FunctionType *DXILOpFT = getDXILOpFunctionType(Prop, ReturnTy, OverloadTy);
273*0fca6ea1SDimitry Andric     DXILFn = M.getOrInsertFunction(DXILFnName, DXILOpFT);
274*0fca6ea1SDimitry Andric   }
275*0fca6ea1SDimitry Andric 
276*0fca6ea1SDimitry Andric   return B.CreateCall(DXILFn, Args);
277*0fca6ea1SDimitry Andric }
278*0fca6ea1SDimitry Andric 
279*0fca6ea1SDimitry Andric Type *DXILOpBuilder::getOverloadTy(dxil::OpCode OpCode, FunctionType *FT) {
280972a253aSDimitry Andric 
281972a253aSDimitry Andric   const OpCodeProperty *Prop = getOpCodeProperty(OpCode);
282*0fca6ea1SDimitry Andric   // If DXIL Op has no overload parameter, just return the
283*0fca6ea1SDimitry Andric   // precise return type specified.
284972a253aSDimitry Andric   if (Prop->OverloadParamIndex < 0) {
285972a253aSDimitry Andric     auto &Ctx = FT->getContext();
286972a253aSDimitry Andric     switch (Prop->OverloadTys) {
287972a253aSDimitry Andric     case OverloadKind::VOID:
288972a253aSDimitry Andric       return Type::getVoidTy(Ctx);
289972a253aSDimitry Andric     case OverloadKind::HALF:
290972a253aSDimitry Andric       return Type::getHalfTy(Ctx);
291972a253aSDimitry Andric     case OverloadKind::FLOAT:
292972a253aSDimitry Andric       return Type::getFloatTy(Ctx);
293972a253aSDimitry Andric     case OverloadKind::DOUBLE:
294972a253aSDimitry Andric       return Type::getDoubleTy(Ctx);
295972a253aSDimitry Andric     case OverloadKind::I1:
296972a253aSDimitry Andric       return Type::getInt1Ty(Ctx);
297972a253aSDimitry Andric     case OverloadKind::I8:
298972a253aSDimitry Andric       return Type::getInt8Ty(Ctx);
299972a253aSDimitry Andric     case OverloadKind::I16:
300972a253aSDimitry Andric       return Type::getInt16Ty(Ctx);
301972a253aSDimitry Andric     case OverloadKind::I32:
302972a253aSDimitry Andric       return Type::getInt32Ty(Ctx);
303972a253aSDimitry Andric     case OverloadKind::I64:
304972a253aSDimitry Andric       return Type::getInt64Ty(Ctx);
305972a253aSDimitry Andric     default:
306972a253aSDimitry Andric       llvm_unreachable("invalid overload type");
307972a253aSDimitry Andric       return nullptr;
308972a253aSDimitry Andric     }
309972a253aSDimitry Andric   }
310972a253aSDimitry Andric 
311972a253aSDimitry Andric   // Prop->OverloadParamIndex is 0, overload type is FT->getReturnType().
312972a253aSDimitry Andric   Type *OverloadType = FT->getReturnType();
313972a253aSDimitry Andric   if (Prop->OverloadParamIndex != 0) {
314*0fca6ea1SDimitry Andric     // Skip Return Type.
315*0fca6ea1SDimitry Andric     OverloadType = FT->getParamType(Prop->OverloadParamIndex - 1);
316972a253aSDimitry Andric   }
317972a253aSDimitry Andric 
318972a253aSDimitry Andric   auto ParamKinds = getOpCodeParameterKind(*Prop);
319972a253aSDimitry Andric   auto Kind = ParamKinds[Prop->OverloadParamIndex];
320972a253aSDimitry Andric   // For ResRet and CBufferRet, OverloadTy is in field of StructType.
321*0fca6ea1SDimitry Andric   if (Kind == ParameterKind::CBufferRet ||
322*0fca6ea1SDimitry Andric       Kind == ParameterKind::ResourceRet) {
323972a253aSDimitry Andric     auto *ST = cast<StructType>(OverloadType);
324972a253aSDimitry Andric     OverloadType = ST->getElementType(0);
325972a253aSDimitry Andric   }
326972a253aSDimitry Andric   return OverloadType;
327972a253aSDimitry Andric }
328972a253aSDimitry Andric 
329bdd1243dSDimitry Andric const char *DXILOpBuilder::getOpCodeName(dxil::OpCode DXILOp) {
330972a253aSDimitry Andric   return ::getOpCodeName(DXILOp);
331972a253aSDimitry Andric }
332bdd1243dSDimitry Andric } // namespace dxil
333972a253aSDimitry Andric } // namespace llvm
334