summaryrefslogtreecommitdiffstats
path: root/llvm/lib/Target/DirectX/DXILOpBuilder.cpp
blob: 21a20d45b922d9a50f96b9007af6b154d2245567 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
//===- DXILOpBuilder.cpp - Helper class for build DIXLOp functions --------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
/// \file This file contains class to help build DXIL op functions.
//===----------------------------------------------------------------------===//

#include "DXILOpBuilder.h"
#include "DXILConstants.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/DXILABI.h"
#include "llvm/Support/ErrorHandling.h"

using namespace llvm;
using namespace llvm::dxil;

constexpr StringLiteral DXILOpNamePrefix = "dx.op.";

namespace {

enum OverloadKind : uint16_t {
  VOID = 1,
  HALF = 1 << 1,
  FLOAT = 1 << 2,
  DOUBLE = 1 << 3,
  I1 = 1 << 4,
  I8 = 1 << 5,
  I16 = 1 << 6,
  I32 = 1 << 7,
  I64 = 1 << 8,
  UserDefineType = 1 << 9,
  ObjectType = 1 << 10,
};

} // namespace

static const char *getOverloadTypeName(OverloadKind Kind) {
  switch (Kind) {
  case OverloadKind::HALF:
    return "f16";
  case OverloadKind::FLOAT:
    return "f32";
  case OverloadKind::DOUBLE:
    return "f64";
  case OverloadKind::I1:
    return "i1";
  case OverloadKind::I8:
    return "i8";
  case OverloadKind::I16:
    return "i16";
  case OverloadKind::I32:
    return "i32";
  case OverloadKind::I64:
    return "i64";
  case OverloadKind::VOID:
  case OverloadKind::ObjectType:
  case OverloadKind::UserDefineType:
    break;
  }
  llvm_unreachable("invalid overload type for name");
  return "void";
}

static OverloadKind getOverloadKind(Type *Ty) {
  Type::TypeID T = Ty->getTypeID();
  switch (T) {
  case Type::VoidTyID:
    return OverloadKind::VOID;
  case Type::HalfTyID:
    return OverloadKind::HALF;
  case Type::FloatTyID:
    return OverloadKind::FLOAT;
  case Type::DoubleTyID:
    return OverloadKind::DOUBLE;
  case Type::IntegerTyID: {
    IntegerType *ITy = cast<IntegerType>(Ty);
    unsigned Bits = ITy->getBitWidth();
    switch (Bits) {
    case 1:
      return OverloadKind::I1;
    case 8:
      return OverloadKind::I8;
    case 16:
      return OverloadKind::I16;
    case 32:
      return OverloadKind::I32;
    case 64:
      return OverloadKind::I64;
    default:
      llvm_unreachable("invalid overload type");
      return OverloadKind::VOID;
    }
  }
  case Type::PointerTyID:
    return OverloadKind::UserDefineType;
  case Type::StructTyID:
    return OverloadKind::ObjectType;
  default:
    llvm_unreachable("invalid overload type");
    return OverloadKind::VOID;
  }
}

static std::string getTypeName(OverloadKind Kind, Type *Ty) {
  if (Kind < OverloadKind::UserDefineType) {
    return getOverloadTypeName(Kind);
  } else if (Kind == OverloadKind::UserDefineType) {
    StructType *ST = cast<StructType>(Ty);
    return ST->getStructName().str();
  } else if (Kind == OverloadKind::ObjectType) {
    StructType *ST = cast<StructType>(Ty);
    return ST->getStructName().str();
  } else {
    std::string Str;
    raw_string_ostream OS(Str);
    Ty->print(OS);
    return OS.str();
  }
}

// Static properties.
struct OpCodeProperty {
  dxil::OpCode OpCode;
  // Offset in DXILOpCodeNameTable.
  unsigned OpCodeNameOffset;
  dxil::OpCodeClass OpCodeClass;
  // Offset in DXILOpCodeClassNameTable.
  unsigned OpCodeClassNameOffset;
  uint16_t OverloadTys;
  llvm::Attribute::AttrKind FuncAttr;
  int OverloadParamIndex;        // parameter index which control the overload.
                                 // When < 0, should be only 1 overload type.
  unsigned NumOfParameters;      // Number of parameters include return value.
  unsigned ParameterTableOffset; // Offset in ParameterTable.
};

// Include getOpCodeClassName getOpCodeProperty, getOpCodeName and
// getOpCodeParameterKind which generated by tableGen.
#define DXIL_OP_OPERATION_TABLE
#include "DXILOperation.inc"
#undef DXIL_OP_OPERATION_TABLE

static std::string constructOverloadName(OverloadKind Kind, Type *Ty,
                                         const OpCodeProperty &Prop) {
  if (Kind == OverloadKind::VOID) {
    return (Twine(DXILOpNamePrefix) + getOpCodeClassName(Prop)).str();
  }
  return (Twine(DXILOpNamePrefix) + getOpCodeClassName(Prop) + "." +
          getTypeName(Kind, Ty))
      .str();
}

static std::string constructOverloadTypeName(OverloadKind Kind,
                                             StringRef TypeName) {
  if (Kind == OverloadKind::VOID)
    return TypeName.str();

  assert(Kind < OverloadKind::UserDefineType && "invalid overload kind");
  return (Twine(TypeName) + getOverloadTypeName(Kind)).str();
}

static StructType *getOrCreateStructType(StringRef Name,
                                         ArrayRef<Type *> EltTys,
                                         LLVMContext &Ctx) {
  StructType *ST = StructType::getTypeByName(Ctx, Name);
  if (ST)
    return ST;

  return StructType::create(Ctx, EltTys, Name);
}

static StructType *getResRetType(Type *OverloadTy, LLVMContext &Ctx) {
  OverloadKind Kind = getOverloadKind(OverloadTy);
  std::string TypeName = constructOverloadTypeName(Kind, "dx.types.ResRet.");
  Type *FieldTypes[5] = {OverloadTy, OverloadTy, OverloadTy, OverloadTy,
                         Type::getInt32Ty(Ctx)};
  return getOrCreateStructType(TypeName, FieldTypes, Ctx);
}

static StructType *getHandleType(LLVMContext &Ctx) {
  return getOrCreateStructType("dx.types.Handle", PointerType::getUnqual(Ctx),
                               Ctx);
}

static Type *getTypeFromParameterKind(ParameterKind Kind, Type *OverloadTy) {
  auto &Ctx = OverloadTy->getContext();
  switch (Kind) {
  case ParameterKind::VOID:
    return Type::getVoidTy(Ctx);
  case ParameterKind::HALF:
    return Type::getHalfTy(Ctx);
  case ParameterKind::FLOAT:
    return Type::getFloatTy(Ctx);
  case ParameterKind::DOUBLE:
    return Type::getDoubleTy(Ctx);
  case ParameterKind::I1:
    return Type::getInt1Ty(Ctx);
  case ParameterKind::I8:
    return Type::getInt8Ty(Ctx);
  case ParameterKind::I16:
    return Type::getInt16Ty(Ctx);
  case ParameterKind::I32:
    return Type::getInt32Ty(Ctx);
  case ParameterKind::I64:
    return Type::getInt64Ty(Ctx);
  case ParameterKind::OVERLOAD:
    return OverloadTy;
  case ParameterKind::RESOURCE_RET:
    return getResRetType(OverloadTy, Ctx);
  case ParameterKind::DXIL_HANDLE:
    return getHandleType(Ctx);
  default:
    break;
  }
  llvm_unreachable("Invalid parameter kind");
  return nullptr;
}

/// Construct DXIL function type. This is the type of a function with
/// the following prototype
///     OverloadType dx.op.<opclass>.<return-type>(int opcode, <param types>)
/// <param-types> are constructed from types in Prop.
/// \param Prop  Structure containing DXIL Operation properties based on
///               its specification in DXIL.td.
/// \param OverloadTy Return type to be used to construct DXIL function type.
static FunctionType *getDXILOpFunctionType(const OpCodeProperty *Prop,
                                           Type *OverloadTy) {
  SmallVector<Type *> ArgTys;

  auto ParamKinds = getOpCodeParameterKind(*Prop);

  // Add OverloadTy as return type of the function
  ArgTys.emplace_back(OverloadTy);

  // Add DXIL Opcode value type viz., Int32 as first argument
  ArgTys.emplace_back(Type::getInt32Ty(OverloadTy->getContext()));

  // Add DXIL Operation parameter types as specified in DXIL properties
  for (unsigned I = 0; I < Prop->NumOfParameters; ++I) {
    ParameterKind Kind = ParamKinds[I];
    ArgTys.emplace_back(getTypeFromParameterKind(Kind, OverloadTy));
  }
  return FunctionType::get(
      ArgTys[0], ArrayRef<Type *>(&ArgTys[1], ArgTys.size() - 1), false);
}

static FunctionCallee getOrCreateDXILOpFunction(dxil::OpCode DXILOp,
                                                Type *OverloadTy, Module &M) {
  const OpCodeProperty *Prop = getOpCodeProperty(DXILOp);

  OverloadKind Kind = getOverloadKind(OverloadTy);
  // FIXME: find the issue and report error in clang instead of check it in
  // backend.
  if ((Prop->OverloadTys & (uint16_t)Kind) == 0) {
    llvm_unreachable("invalid overload");
  }

  std::string FnName = constructOverloadName(Kind, OverloadTy, *Prop);
  // Dependent on name to dedup.
  if (auto *Fn = M.getFunction(FnName))
    return FunctionCallee(Fn);

  FunctionType *DXILOpFT = getDXILOpFunctionType(Prop, OverloadTy);
  return M.getOrInsertFunction(FnName, DXILOpFT);
}

namespace llvm {
namespace dxil {

CallInst *DXILOpBuilder::createDXILOpCall(dxil::OpCode OpCode, Type *OverloadTy,
                                          llvm::iterator_range<Use *> Args) {
  auto Fn = getOrCreateDXILOpFunction(OpCode, OverloadTy, M);
  SmallVector<Value *> FullArgs;
  FullArgs.emplace_back(B.getInt32((int32_t)OpCode));
  FullArgs.append(Args.begin(), Args.end());
  return B.CreateCall(Fn, FullArgs);
}

Type *DXILOpBuilder::getOverloadTy(dxil::OpCode OpCode, FunctionType *FT) {

  const OpCodeProperty *Prop = getOpCodeProperty(OpCode);
  // If DXIL Op has no overload parameter, just return the
  // precise return type specified.
  if (Prop->OverloadParamIndex < 0) {
    auto &Ctx = FT->getContext();
    switch (Prop->OverloadTys) {
    case OverloadKind::VOID:
      return Type::getVoidTy(Ctx);
    case OverloadKind::HALF:
      return Type::getHalfTy(Ctx);
    case OverloadKind::FLOAT:
      return Type::getFloatTy(Ctx);
    case OverloadKind::DOUBLE:
      return Type::getDoubleTy(Ctx);
    case OverloadKind::I1:
      return Type::getInt1Ty(Ctx);
    case OverloadKind::I8:
      return Type::getInt8Ty(Ctx);
    case OverloadKind::I16:
      return Type::getInt16Ty(Ctx);
    case OverloadKind::I32:
      return Type::getInt32Ty(Ctx);
    case OverloadKind::I64:
      return Type::getInt64Ty(Ctx);
    default:
      llvm_unreachable("invalid overload type");
      return nullptr;
    }
  }

  // Prop->OverloadParamIndex is 0, overload type is FT->getReturnType().
  Type *OverloadType = FT->getReturnType();
  if (Prop->OverloadParamIndex != 0) {
    // Skip Return Type.
    OverloadType = FT->getParamType(Prop->OverloadParamIndex - 1);
  }

  auto ParamKinds = getOpCodeParameterKind(*Prop);
  auto Kind = ParamKinds[Prop->OverloadParamIndex];
  // For ResRet and CBufferRet, OverloadTy is in field of StructType.
  if (Kind == ParameterKind::CBUFFER_RET ||
      Kind == ParameterKind::RESOURCE_RET) {
    auto *ST = cast<StructType>(OverloadType);
    OverloadType = ST->getElementType(0);
  }
  return OverloadType;
}

const char *DXILOpBuilder::getOpCodeName(dxil::OpCode DXILOp) {
  return ::getOpCodeName(DXILOp);
}
} // namespace dxil
} // namespace llvm