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