brintos

brintos / llvm-project-archived public Read only

0
0
Text · 20.9 KiB · 944b2e6 Raw
622 lines · cpp
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-exception6//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} // namespace49 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>(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 rework120    // how we're handling overloads and remove the `OverloadKind` proxy enum.121    StructType *ST = cast<StructType>(Ty);122    return getOverloadKind(ST->getElementType(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>(Ty);134    return ST->getStructName().str();135  } else if (Kind == OverloadKind::ObjectType) {136    StructType *ST = cast<StructType>(Ty);137    return ST->getStructName().str();138  } else {139    std::string Str;140    raw_string_ostream OS(Str);141    Ty->print(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 and161// getOpCodeParameterKind which generated by tableGen.162#define DXIL_OP_OPERATION_TABLE163#include "DXILOperation.inc"164#undef DXIL_OP_OPERATION_TABLE165 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(Ctx, Name);189  if (ST)190    return ST;191 192  return StructType::create(Ctx, EltTys, Name);193}194 195static StructType *getResRetType(Type *ElementTy) {196  LLVMContext &Ctx = ElementTy->getContext();197  OverloadKind Kind = getOverloadKind(ElementTy);198  std::string TypeName = constructOverloadTypeName(Kind, "dx.types.ResRet.");199  Type *FieldTypes[5] = {ElementTy, ElementTy, ElementTy, ElementTy,200                         Type::getInt32Ty(Ctx)};201  return getOrCreateStructType(TypeName, FieldTypes, Ctx);202}203 204static StructType *getCBufRetType(Type *ElementTy) {205  LLVMContext &Ctx = ElementTy->getContext();206  OverloadKind Kind = getOverloadKind(ElementTy);207  std::string TypeName = constructOverloadTypeName(Kind, "dx.types.CBufRet.");208 209  // 64-bit types only have two elements210  if (ElementTy->isDoubleTy() || ElementTy->isIntegerTy(64))211    return getOrCreateStructType(TypeName, {ElementTy, ElementTy}, Ctx);212 213  // 16-bit types pack 8 elements and have .8 in their name to differentiate214  // from min-precision types.215  if (ElementTy->isHalfTy() || ElementTy->isIntegerTy(16)) {216    TypeName += ".8";217    return getOrCreateStructType(TypeName,218                                 {ElementTy, ElementTy, ElementTy, ElementTy,219                                  ElementTy, ElementTy, ElementTy, ElementTy},220                                 Ctx);221  }222 223  return getOrCreateStructType(224      TypeName, {ElementTy, ElementTy, ElementTy, ElementTy}, Ctx);225}226 227static StructType *getHandleType(LLVMContext &Ctx) {228  return getOrCreateStructType("dx.types.Handle", PointerType::getUnqual(Ctx),229                               Ctx);230}231 232static StructType *getResBindType(LLVMContext &Context) {233  if (auto *ST = StructType::getTypeByName(Context, "dx.types.ResBind"))234    return ST;235  Type *Int32Ty = Type::getInt32Ty(Context);236  Type *Int8Ty = Type::getInt8Ty(Context);237  return StructType::create({Int32Ty, Int32Ty, Int32Ty, Int8Ty},238                            "dx.types.ResBind");239}240 241static StructType *getResPropsType(LLVMContext &Context) {242  if (auto *ST =243          StructType::getTypeByName(Context, "dx.types.ResourceProperties"))244    return ST;245  Type *Int32Ty = Type::getInt32Ty(Context);246  return StructType::create({Int32Ty, Int32Ty}, "dx.types.ResourceProperties");247}248 249static StructType *getSplitDoubleType(LLVMContext &Context) {250  if (auto *ST = StructType::getTypeByName(Context, "dx.types.splitdouble"))251    return ST;252  Type *Int32Ty = Type::getInt32Ty(Context);253  return StructType::create({Int32Ty, Int32Ty}, "dx.types.splitdouble");254}255 256static StructType *getBinaryWithCarryType(LLVMContext &Context) {257  if (auto *ST = StructType::getTypeByName(Context, "dx.types.i32c"))258    return ST;259  Type *Int32Ty = Type::getInt32Ty(Context);260  Type *Int1Ty = Type::getInt1Ty(Context);261  return StructType::create({Int32Ty, Int1Ty}, "dx.types.i32c");262}263 264static StructType *getDimensionsType(LLVMContext &Ctx) {265  Type *Int32Ty = Type::getInt32Ty(Ctx);266  return getOrCreateStructType("dx.types.Dimensions",267                               {Int32Ty, Int32Ty, Int32Ty, Int32Ty}, Ctx);268}269 270static Type *getTypeFromOpParamType(OpParamType Kind, LLVMContext &Ctx,271                                    Type *OverloadTy) {272  switch (Kind) {273  case OpParamType::VoidTy:274    return Type::getVoidTy(Ctx);275  case OpParamType::HalfTy:276    return Type::getHalfTy(Ctx);277  case OpParamType::FloatTy:278    return Type::getFloatTy(Ctx);279  case OpParamType::DoubleTy:280    return Type::getDoubleTy(Ctx);281  case OpParamType::Int1Ty:282    return Type::getInt1Ty(Ctx);283  case OpParamType::Int8Ty:284    return Type::getInt8Ty(Ctx);285  case OpParamType::Int16Ty:286    return Type::getInt16Ty(Ctx);287  case OpParamType::Int32Ty:288    return Type::getInt32Ty(Ctx);289  case OpParamType::Int64Ty:290    return Type::getInt64Ty(Ctx);291  case OpParamType::OverloadTy:292    return OverloadTy;293  case OpParamType::ResRetHalfTy:294    return getResRetType(Type::getHalfTy(Ctx));295  case OpParamType::ResRetFloatTy:296    return getResRetType(Type::getFloatTy(Ctx));297  case OpParamType::ResRetDoubleTy:298    return getResRetType(Type::getDoubleTy(Ctx));299  case OpParamType::ResRetInt16Ty:300    return getResRetType(Type::getInt16Ty(Ctx));301  case OpParamType::ResRetInt32Ty:302    return getResRetType(Type::getInt32Ty(Ctx));303  case OpParamType::ResRetInt64Ty:304    return getResRetType(Type::getInt64Ty(Ctx));305  case OpParamType::CBufRetHalfTy:306    return getCBufRetType(Type::getHalfTy(Ctx));307  case OpParamType::CBufRetFloatTy:308    return getCBufRetType(Type::getFloatTy(Ctx));309  case OpParamType::CBufRetDoubleTy:310    return getCBufRetType(Type::getDoubleTy(Ctx));311  case OpParamType::CBufRetInt16Ty:312    return getCBufRetType(Type::getInt16Ty(Ctx));313  case OpParamType::CBufRetInt32Ty:314    return getCBufRetType(Type::getInt32Ty(Ctx));315  case OpParamType::CBufRetInt64Ty:316    return getCBufRetType(Type::getInt64Ty(Ctx));317  case OpParamType::HandleTy:318    return getHandleType(Ctx);319  case OpParamType::ResBindTy:320    return getResBindType(Ctx);321  case OpParamType::ResPropsTy:322    return getResPropsType(Ctx);323  case OpParamType::SplitDoubleTy:324    return getSplitDoubleType(Ctx);325  case OpParamType::BinaryWithCarryTy:326    return getBinaryWithCarryType(Ctx);327  case OpParamType::DimensionsTy:328    return getDimensionsType(Ctx);329  }330  llvm_unreachable("Invalid parameter kind");331  return nullptr;332}333 334static ShaderKind getShaderKindEnum(Triple::EnvironmentType EnvType) {335  switch (EnvType) {336  case Triple::Pixel:337    return ShaderKind::pixel;338  case Triple::Vertex:339    return ShaderKind::vertex;340  case Triple::Geometry:341    return ShaderKind::geometry;342  case Triple::Hull:343    return ShaderKind::hull;344  case Triple::Domain:345    return ShaderKind::domain;346  case Triple::Compute:347    return ShaderKind::compute;348  case Triple::Library:349    return ShaderKind::library;350  case Triple::RayGeneration:351    return ShaderKind::raygeneration;352  case Triple::Intersection:353    return ShaderKind::intersection;354  case Triple::AnyHit:355    return ShaderKind::anyhit;356  case Triple::ClosestHit:357    return ShaderKind::closesthit;358  case Triple::Miss:359    return ShaderKind::miss;360  case Triple::Callable:361    return ShaderKind::callable;362  case Triple::Mesh:363    return ShaderKind::mesh;364  case Triple::Amplification:365    return ShaderKind::amplification;366  default:367    break;368  }369  llvm_unreachable(370      "Shader Kind Not Found - Invalid DXIL Environment Specified");371}372 373static SmallVector<Type *>374getArgTypesFromOpParamTypes(ArrayRef<dxil::OpParamType> Types,375                            LLVMContext &Context, Type *OverloadTy) {376  SmallVector<Type *> ArgTys;377  ArgTys.emplace_back(Type::getInt32Ty(Context));378  for (dxil::OpParamType Ty : Types)379    ArgTys.emplace_back(getTypeFromOpParamType(Ty, Context, OverloadTy));380  return ArgTys;381}382 383/// Construct DXIL function type. This is the type of a function with384/// the following prototype385///     OverloadType dx.op.<opclass>.<return-type>(int opcode, <param types>)386/// <param-types> are constructed from types in Prop.387static FunctionType *getDXILOpFunctionType(dxil::OpCode OpCode,388                                           LLVMContext &Context,389                                           Type *OverloadTy) {390 391  switch (OpCode) {392#define DXIL_OP_FUNCTION_TYPE(OpCode, RetType, ...)                            \393  case OpCode:                                                                 \394    return FunctionType::get(                                                  \395        getTypeFromOpParamType(RetType, Context, OverloadTy),                  \396        getArgTypesFromOpParamTypes({__VA_ARGS__}, Context, OverloadTy),       \397        /*isVarArg=*/false);398#include "DXILOperation.inc"399  }400  llvm_unreachable("Invalid OpCode?");401}402 403/// Get index of the property from PropList valid for the most recent404/// DXIL version not greater than DXILVer.405/// PropList is expected to be sorted in ascending order of DXIL version.406template <typename T>407static std::optional<size_t> getPropIndex(ArrayRef<T> PropList,408                                          const VersionTuple DXILVer) {409  size_t Index = PropList.size() - 1;410  for (auto Iter = PropList.rbegin(); Iter != PropList.rend();411       Iter++, Index--) {412    const T &Prop = *Iter;413    if (VersionTuple(Prop.DXILVersion.Major, Prop.DXILVersion.Minor) <=414        DXILVer) {415      return Index;416    }417  }418  return std::nullopt;419}420 421// Helper function to pack an OpCode and VersionTuple into a uint64_t for use422// in a switch statement423constexpr static uint64_t computeSwitchEnum(dxil::OpCode OpCode,424                                            uint16_t VersionMajor,425                                            uint16_t VersionMinor) {426  uint64_t OpCodePack = (uint64_t)OpCode;427  return (OpCodePack << 32) | (VersionMajor << 16) | VersionMinor;428}429 430// Retreive all the set attributes for a DXIL OpCode given the targeted431// DXILVersion432static dxil::Attributes getDXILAttributes(dxil::OpCode OpCode,433                                          VersionTuple DXILVersion) {434  // Instantiate all versions to iterate through435  SmallVector<Version> Versions = {436#define DXIL_VERSION(MAJOR, MINOR) {MAJOR, MINOR},437#include "DXILOperation.inc"438  };439 440  dxil::Attributes Attributes;441  for (auto Version : Versions) {442    if (DXILVersion < VersionTuple(Version.Major, Version.Minor))443      continue;444 445    // Switch through and match an OpCode with the specific version and set the446    // corresponding flag(s) if available447    switch (computeSwitchEnum(OpCode, Version.Major, Version.Minor)) {448#define DXIL_OP_ATTRIBUTES(OpCode, VersionMajor, VersionMinor, ...)            \449  case computeSwitchEnum(OpCode, VersionMajor, VersionMinor): {                \450    auto Other = dxil::Attributes{__VA_ARGS__};                                \451    Attributes |= Other;                                                       \452    break;                                                                     \453  };454#include "DXILOperation.inc"455    }456  }457  return Attributes;458}459 460// Retreive the set of DXIL Attributes given the version and map them to an461// llvm function attribute that is set onto the instruction462static void setDXILAttributes(CallInst *CI, dxil::OpCode OpCode,463                              VersionTuple DXILVersion) {464  dxil::Attributes Attributes = getDXILAttributes(OpCode, DXILVersion);465  if (Attributes.ReadNone)466    CI->setDoesNotAccessMemory();467  if (Attributes.ReadOnly)468    CI->setOnlyReadsMemory();469  if (Attributes.NoReturn)470    CI->setDoesNotReturn();471  if (Attributes.NoDuplicate)472    CI->setCannotDuplicate();473  return;474}475 476namespace llvm {477namespace dxil {478 479// No extra checks on TargetTriple need be performed to verify that the480// Triple is well-formed or that the target is supported since these checks481// would have been done at the time the module M is constructed in the earlier482// stages of compilation.483DXILOpBuilder::DXILOpBuilder(Module &M) : M(M), IRB(M.getContext()) {484  const Triple &TT = M.getTargetTriple();485  DXILVersion = TT.getDXILVersion();486  ShaderStage = TT.getEnvironment();487  // Ensure Environment type is known488  if (ShaderStage == Triple::UnknownEnvironment) {489    reportFatalUsageError(490        Twine(DXILVersion.getAsString()) +491        ": Unknown Compilation Target Shader Stage specified ");492  }493}494 495static Error makeOpError(dxil::OpCode OpCode, Twine Msg) {496  return make_error<StringError>(497      Twine("Cannot create ") + getOpCodeName(OpCode) + " operation: " + Msg,498      inconvertibleErrorCode());499}500 501Expected<CallInst *> DXILOpBuilder::tryCreateOp(dxil::OpCode OpCode,502                                                ArrayRef<Value *> Args,503                                                const Twine &Name,504                                                Type *RetTy) {505  const OpCodeProperty *Prop = getOpCodeProperty(OpCode);506 507  Type *OverloadTy = nullptr;508  if (Prop->OverloadParamIndex == 0) {509    if (!RetTy)510      return makeOpError(OpCode, "Op overloaded on unknown return type");511    OverloadTy = RetTy;512  } else if (Prop->OverloadParamIndex > 0) {513    // The index counts including the return type514    unsigned ArgIndex = Prop->OverloadParamIndex - 1;515    if (static_cast<unsigned>(ArgIndex) >= Args.size())516      return makeOpError(OpCode, "Wrong number of arguments");517    OverloadTy = Args[ArgIndex]->getType();518  }519 520  FunctionType *DXILOpFT =521      getDXILOpFunctionType(OpCode, M.getContext(), OverloadTy);522 523  std::optional<size_t> OlIndexOrErr =524      getPropIndex(ArrayRef(Prop->Overloads), DXILVersion);525  if (!OlIndexOrErr.has_value())526    return makeOpError(OpCode, Twine("No valid overloads for DXIL version ") +527                                   DXILVersion.getAsString());528 529  uint16_t ValidTyMask = Prop->Overloads[*OlIndexOrErr].ValidTys;530 531  OverloadKind Kind = getOverloadKind(OverloadTy);532 533  // Check if the operation supports overload types and OverloadTy is valid534  // per the specified types for the operation535  if ((ValidTyMask != OverloadKind::UNDEFINED) &&536      (ValidTyMask & (uint16_t)Kind) == 0)537    return makeOpError(OpCode, "Invalid overload type");538 539  // Perform necessary checks to ensure Opcode is valid in the targeted shader540  // kind541  std::optional<size_t> StIndexOrErr =542      getPropIndex(ArrayRef(Prop->Stages), DXILVersion);543  if (!StIndexOrErr.has_value())544    return makeOpError(OpCode, Twine("No valid stage for DXIL version ") +545                                   DXILVersion.getAsString());546 547  uint16_t ValidShaderKindMask = Prop->Stages[*StIndexOrErr].ValidStages;548 549  // Ensure valid shader stage properties are specified550  if (ValidShaderKindMask == ShaderKind::removed)551    return makeOpError(OpCode, "Operation has been removed");552 553  // Shader stage need not be validated since getShaderKindEnum() fails554  // for unknown shader stage.555 556  // Verify the target shader stage is valid for the DXIL operation557  ShaderKind ModuleStagekind = getShaderKindEnum(ShaderStage);558  if (!(ValidShaderKindMask & ModuleStagekind))559    return makeOpError(OpCode, "Invalid stage");560 561  std::string DXILFnName = constructOverloadName(Kind, OverloadTy, *Prop);562  FunctionCallee DXILFn = M.getOrInsertFunction(DXILFnName, DXILOpFT);563 564  // We need to inject the opcode as the first argument.565  SmallVector<Value *> OpArgs;566  OpArgs.push_back(IRB.getInt32(llvm::to_underlying(OpCode)));567  OpArgs.append(Args.begin(), Args.end());568 569  // Create the function call instruction570  CallInst *CI = IRB.CreateCall(DXILFn, OpArgs, Name);571 572  // We then need to attach available function attributes573  setDXILAttributes(CI, OpCode, DXILVersion);574 575  return CI;576}577 578CallInst *DXILOpBuilder::createOp(dxil::OpCode OpCode, ArrayRef<Value *> Args,579                                  const Twine &Name, Type *RetTy) {580  Expected<CallInst *> Result = tryCreateOp(OpCode, Args, Name, RetTy);581  if (Error E = Result.takeError())582    llvm_unreachable("Invalid arguments for operation");583  return *Result;584}585 586StructType *DXILOpBuilder::getResRetType(Type *ElementTy) {587  return ::getResRetType(ElementTy);588}589 590StructType *DXILOpBuilder::getCBufRetType(Type *ElementTy) {591  return ::getCBufRetType(ElementTy);592}593 594StructType *DXILOpBuilder::getHandleType() {595  return ::getHandleType(IRB.getContext());596}597 598Constant *DXILOpBuilder::getResBind(uint32_t LowerBound, uint32_t UpperBound,599                                    uint32_t SpaceID, dxil::ResourceClass RC) {600  Type *Int32Ty = IRB.getInt32Ty();601  Type *Int8Ty = IRB.getInt8Ty();602  return ConstantStruct::get(603      getResBindType(IRB.getContext()),604      {ConstantInt::get(Int32Ty, LowerBound),605       ConstantInt::get(Int32Ty, UpperBound),606       ConstantInt::get(Int32Ty, SpaceID),607       ConstantInt::get(Int8Ty, llvm::to_underlying(RC))});608}609 610Constant *DXILOpBuilder::getResProps(uint32_t Word0, uint32_t Word1) {611  Type *Int32Ty = IRB.getInt32Ty();612  return ConstantStruct::get(613      getResPropsType(IRB.getContext()),614      {ConstantInt::get(Int32Ty, Word0), ConstantInt::get(Int32Ty, Word1)});615}616 617const char *DXILOpBuilder::getOpCodeName(dxil::OpCode DXILOp) {618  return ::getOpCodeName(DXILOp);619}620} // namespace dxil621} // namespace llvm622