brintos

brintos / llvm-project-archived public Read only

0
0
Text · 14.8 KiB · 6e95a42 Raw
397 lines · cpp
1//===- DXILPostOptimizationValidation.cpp - Opt DXIL validation ----------===//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#include "DXILPostOptimizationValidation.h"10#include "DXILRootSignature.h"11#include "DXILShaderFlags.h"12#include "DirectX.h"13#include "llvm/ADT/SmallString.h"14#include "llvm/Analysis/DXILMetadataAnalysis.h"15#include "llvm/Analysis/DXILResource.h"16#include "llvm/IR/DiagnosticInfo.h"17#include "llvm/IR/Instructions.h"18#include "llvm/IR/IntrinsicsDirectX.h"19#include "llvm/IR/Module.h"20#include "llvm/InitializePasses.h"21#include "llvm/Support/DXILABI.h"22 23#define DEBUG_TYPE "dxil-post-optimization-validation"24 25using namespace llvm;26using namespace llvm::dxil;27 28static ResourceClass toResourceClass(dxbc::RootParameterType Type) {29  using namespace dxbc;30  switch (Type) {31  case RootParameterType::Constants32Bit:32    return ResourceClass::CBuffer;33  case RootParameterType::SRV:34    return ResourceClass::SRV;35  case RootParameterType::UAV:36    return ResourceClass::UAV;37  case RootParameterType::CBV:38    return ResourceClass::CBuffer;39  case dxbc::RootParameterType::DescriptorTable:40    llvm_unreachable("DescriptorTable is not convertible to ResourceClass");41  }42  llvm_unreachable("Unknown RootParameterType");43}44 45static void reportInvalidDirection(Module &M, DXILResourceMap &DRM) {46  for (const auto &UAV : DRM.uavs()) {47    if (UAV.CounterDirection != ResourceCounterDirection::Invalid)48      continue;49 50    CallInst *ResourceHandle = nullptr;51    for (CallInst *MaybeHandle : DRM.calls()) {52      if (*DRM.find(MaybeHandle) == UAV) {53        ResourceHandle = MaybeHandle;54        break;55      }56    }57 58    StringRef Message = "RWStructuredBuffers may increment or decrement their "59                        "counters, but not both.";60    for (const auto &U : ResourceHandle->users()) {61      const CallInst *CI = dyn_cast<CallInst>(U);62      if (!CI && CI->getIntrinsicID() != Intrinsic::dx_resource_updatecounter)63        continue;64 65      M.getContext().diagnose(DiagnosticInfoGenericWithLoc(66          Message, *CI->getFunction(), CI->getDebugLoc()));67    }68  }69}70 71static void reportOverlappingError(Module &M, ResourceInfo R1,72                                   ResourceInfo R2) {73  SmallString<128> Message;74  raw_svector_ostream OS(Message);75  OS << "resource " << R1.getName() << " at register "76     << R1.getBinding().LowerBound << " overlaps with resource " << R2.getName()77     << " at register " << R2.getBinding().LowerBound << " in space "78     << R2.getBinding().Space;79  M.getContext().diagnose(DiagnosticInfoGeneric(Message));80}81 82static void reportOverlappingBinding(Module &M, DXILResourceMap &DRM) {83  [[maybe_unused]] bool ErrorFound = false;84  for (const auto &ResList :85       {DRM.srvs(), DRM.uavs(), DRM.cbuffers(), DRM.samplers()}) {86    if (ResList.empty())87      continue;88    const ResourceInfo *PrevRI = &*ResList.begin();89    for (auto *I = ResList.begin() + 1; I != ResList.end(); ++I) {90      const ResourceInfo *CurrentRI = &*I;91      const ResourceInfo *RI = CurrentRI;92      while (RI != ResList.end() &&93             PrevRI->getBinding().overlapsWith(RI->getBinding())) {94        reportOverlappingError(M, *PrevRI, *RI);95        ErrorFound = true;96        RI++;97      }98      PrevRI = CurrentRI;99    }100  }101  assert(ErrorFound && "this function should be called only when if "102                       "DXILResourceBindingInfo::hasOverlapingBinding() is "103                       "true, yet no overlapping binding was found");104}105 106static void reportInvalidHandleTyError(Module &M, ResourceClass RC,107                                       ResourceInfo::ResourceBinding Binding) {108  SmallString<160> Message;109  raw_svector_ostream OS(Message);110  StringRef RCName = getResourceClassName(RC);111  OS << RCName << " at register " << Binding.LowerBound << " and space "112     << Binding.Space << " is bound to a texture or typed buffer. " << RCName113     << " root descriptors can only be Raw or Structured buffers.";114  M.getContext().diagnose(DiagnosticInfoGeneric(Message));115}116 117static void reportOverlappingRegisters(Module &M, const llvm::hlsl::Binding &R1,118                                       const llvm::hlsl::Binding &R2) {119  SmallString<128> Message;120 121  raw_svector_ostream OS(Message);122  OS << "resource " << getResourceClassName(R1.RC) << " (space=" << R1.Space123     << ", registers=[" << R1.LowerBound << ", " << R1.UpperBound124     << "]) overlaps with resource " << getResourceClassName(R2.RC)125     << " (space=" << R2.Space << ", registers=[" << R2.LowerBound << ", "126     << R2.UpperBound << "])";127  M.getContext().diagnose(DiagnosticInfoGeneric(Message));128}129 130static void131reportRegNotBound(Module &M, ResourceClass Class,132                  const llvm::dxil::ResourceInfo::ResourceBinding &Unbound) {133  SmallString<128> Message;134  raw_svector_ostream OS(Message);135  OS << getResourceClassName(Class) << " register " << Unbound.LowerBound136     << " in space " << Unbound.Space137     << " does not have a binding in the Root Signature";138  M.getContext().diagnose(DiagnosticInfoGeneric(Message));139}140 141static dxbc::ShaderVisibility142tripleToVisibility(llvm::Triple::EnvironmentType ET) {143  switch (ET) {144  case Triple::Pixel:145    return dxbc::ShaderVisibility::Pixel;146  case Triple::Vertex:147    return dxbc::ShaderVisibility::Vertex;148  case Triple::Geometry:149    return dxbc::ShaderVisibility::Geometry;150  case Triple::Hull:151    return dxbc::ShaderVisibility::Hull;152  case Triple::Domain:153    return dxbc::ShaderVisibility::Domain;154  case Triple::Mesh:155    return dxbc::ShaderVisibility::Mesh;156  case Triple::Compute:157    return dxbc::ShaderVisibility::All;158  default:159    llvm_unreachable("Invalid triple to shader stage conversion");160  }161}162 163static void reportIfDeniedShaderStageAccess(Module &M,164                                            const dxbc::RootFlags &Flags,165                                            const dxbc::RootFlags &Mask) {166  if ((Flags & Mask) != Mask)167    return;168 169  SmallString<128> Message;170  raw_svector_ostream OS(Message);171  OS << "Shader has root bindings but root signature uses a DENY flag to "172        "disallow root binding access to the shader stage.";173  M.getContext().diagnose(DiagnosticInfoGeneric(Message));174}175 176static std::optional<dxbc::RootFlags>177getEnvironmentDenyFlagMask(Triple::EnvironmentType ShaderProfile) {178  switch (ShaderProfile) {179  case Triple::Pixel:180    return dxbc::RootFlags::DenyPixelShaderRootAccess;181  case Triple::Vertex:182    return dxbc::RootFlags::DenyVertexShaderRootAccess;183  case Triple::Geometry:184    return dxbc::RootFlags::DenyGeometryShaderRootAccess;185  case Triple::Hull:186    return dxbc::RootFlags::DenyHullShaderRootAccess;187  case Triple::Domain:188    return dxbc::RootFlags::DenyDomainShaderRootAccess;189  case Triple::Mesh:190    return dxbc::RootFlags::DenyMeshShaderRootAccess;191  case Triple::Amplification:192    return dxbc::RootFlags::DenyAmplificationShaderRootAccess;193  default:194    return std::nullopt;195  }196}197 198static void validateRootSignature(Module &M,199                                  const mcdxbc::RootSignatureDesc &RSD,200                                  dxil::ModuleMetadataInfo &MMI,201                                  DXILResourceMap &DRM,202                                  DXILResourceTypeMap &DRTM) {203 204  hlsl::BindingInfoBuilder Builder;205  dxbc::ShaderVisibility Visibility = tripleToVisibility(MMI.ShaderProfile);206 207  for (const mcdxbc::RootParameterInfo &ParamInfo : RSD.ParametersContainer) {208    dxbc::ShaderVisibility ParamVisibility =209        dxbc::ShaderVisibility(ParamInfo.Visibility);210    if (ParamVisibility != dxbc::ShaderVisibility::All &&211        ParamVisibility != Visibility)212      continue;213    dxbc::RootParameterType ParamType = dxbc::RootParameterType(ParamInfo.Type);214    switch (ParamType) {215    case dxbc::RootParameterType::Constants32Bit: {216      mcdxbc::RootConstants Const =217          RSD.ParametersContainer.getConstant(ParamInfo.Location);218      Builder.trackBinding(dxil::ResourceClass::CBuffer, Const.RegisterSpace,219                           Const.ShaderRegister, Const.ShaderRegister,220                           &ParamInfo);221      break;222    }223 224    case dxbc::RootParameterType::SRV:225    case dxbc::RootParameterType::UAV:226    case dxbc::RootParameterType::CBV: {227      mcdxbc::RootDescriptor Desc =228          RSD.ParametersContainer.getRootDescriptor(ParamInfo.Location);229      Builder.trackBinding(toResourceClass(ParamInfo.Type), Desc.RegisterSpace,230                           Desc.ShaderRegister, Desc.ShaderRegister,231                           &ParamInfo);232 233      break;234    }235    case dxbc::RootParameterType::DescriptorTable: {236      const mcdxbc::DescriptorTable &Table =237          RSD.ParametersContainer.getDescriptorTable(ParamInfo.Location);238 239      for (const mcdxbc::DescriptorRange &Range : Table.Ranges) {240        uint32_t UpperBound =241            Range.NumDescriptors == ~0U242                ? Range.BaseShaderRegister243                : Range.BaseShaderRegister + Range.NumDescriptors - 1;244        Builder.trackBinding(Range.RangeType, Range.RegisterSpace,245                             Range.BaseShaderRegister, UpperBound, &ParamInfo);246      }247      break;248    }249    }250  }251 252  for (const mcdxbc::StaticSampler &S : RSD.StaticSamplers)253    Builder.trackBinding(dxil::ResourceClass::Sampler, S.RegisterSpace,254                         S.ShaderRegister, S.ShaderRegister, &S);255 256  Builder.calculateBindingInfo(257      [&M](const llvm::hlsl::BindingInfoBuilder &Builder,258           const llvm::hlsl::Binding &ReportedBinding) {259        const llvm::hlsl::Binding &Overlaping =260            Builder.findOverlapping(ReportedBinding);261        reportOverlappingRegisters(M, ReportedBinding, Overlaping);262      });263 264  const hlsl::BoundRegs &BoundRegs = Builder.takeBoundRegs();265  bool HasBindings = false;266  for (const ResourceInfo &RI : DRM) {267    const ResourceInfo::ResourceBinding &Binding = RI.getBinding();268    const dxil::ResourceTypeInfo &RTI = DRTM[RI.getHandleTy()];269    dxil::ResourceClass RC = RTI.getResourceClass();270    dxil::ResourceKind RK = RTI.getResourceKind();271 272    const llvm::hlsl::Binding *Reg =273        BoundRegs.findBoundReg(RC, Binding.Space, Binding.LowerBound,274                               Binding.LowerBound + Binding.Size - 1);275 276    if (!Reg) {277      reportRegNotBound(M, RC, Binding);278      continue;279    }280 281    const auto *ParamInfo =282        static_cast<const mcdxbc::RootParameterInfo *>(Reg->Cookie);283 284    bool IsSRVOrUAV = RC == ResourceClass::SRV || RC == ResourceClass::UAV;285    bool IsDescriptorTable =286        ParamInfo->Type == dxbc::RootParameterType::DescriptorTable;287    bool IsRawOrStructuredBuffer =288        RK != ResourceKind::RawBuffer && RK != ResourceKind::StructuredBuffer;289    if (IsSRVOrUAV && !IsDescriptorTable && IsRawOrStructuredBuffer) {290      reportInvalidHandleTyError(M, RC, Binding);291      continue;292    }293 294    HasBindings = true;295  }296 297  if (!HasBindings)298    return;299 300  if (std::optional<dxbc::RootFlags> Mask =301          getEnvironmentDenyFlagMask(MMI.ShaderProfile))302    reportIfDeniedShaderStageAccess(M, dxbc::RootFlags(RSD.Flags), *Mask);303}304 305static mcdxbc::RootSignatureDesc *306getRootSignature(RootSignatureBindingInfo &RSBI,307                 dxil::ModuleMetadataInfo &MMI) {308  if (MMI.EntryPropertyVec.size() == 0)309    return nullptr;310  return RSBI.getDescForFunction(MMI.EntryPropertyVec[0].Entry);311}312 313static void reportErrors(Module &M, DXILResourceMap &DRM,314                         DXILResourceBindingInfo &DRBI,315                         RootSignatureBindingInfo &RSBI,316                         dxil::ModuleMetadataInfo &MMI,317                         DXILResourceTypeMap &DRTM) {318  if (DRM.hasInvalidCounterDirection())319    reportInvalidDirection(M, DRM);320 321  if (DRBI.hasOverlappingBinding())322    reportOverlappingBinding(M, DRM);323 324  assert(!DRBI.hasImplicitBinding() && "implicit bindings should be handled in "325                                       "DXILResourceImplicitBinding pass");326 327  if (mcdxbc::RootSignatureDesc *RSD = getRootSignature(RSBI, MMI))328    validateRootSignature(M, *RSD, MMI, DRM, DRTM);329}330 331PreservedAnalyses332DXILPostOptimizationValidation::run(Module &M, ModuleAnalysisManager &MAM) {333  DXILResourceMap &DRM = MAM.getResult<DXILResourceAnalysis>(M);334  DXILResourceBindingInfo &DRBI = MAM.getResult<DXILResourceBindingAnalysis>(M);335  RootSignatureBindingInfo &RSBI = MAM.getResult<RootSignatureAnalysis>(M);336  ModuleMetadataInfo &MMI = MAM.getResult<DXILMetadataAnalysis>(M);337  DXILResourceTypeMap &DRTM = MAM.getResult<DXILResourceTypeAnalysis>(M);338 339  reportErrors(M, DRM, DRBI, RSBI, MMI, DRTM);340  return PreservedAnalyses::all();341}342 343namespace {344class DXILPostOptimizationValidationLegacy : public ModulePass {345public:346  bool runOnModule(Module &M) override {347    DXILResourceMap &DRM =348        getAnalysis<DXILResourceWrapperPass>().getResourceMap();349    DXILResourceBindingInfo &DRBI =350        getAnalysis<DXILResourceBindingWrapperPass>().getBindingInfo();351    RootSignatureBindingInfo &RSBI =352        getAnalysis<RootSignatureAnalysisWrapper>().getRSInfo();353    dxil::ModuleMetadataInfo &MMI =354        getAnalysis<DXILMetadataAnalysisWrapperPass>().getModuleMetadata();355    DXILResourceTypeMap &DRTM =356        getAnalysis<DXILResourceTypeWrapperPass>().getResourceTypeMap();357 358    reportErrors(M, DRM, DRBI, RSBI, MMI, DRTM);359    return false;360  }361  StringRef getPassName() const override {362    return "DXIL Post Optimization Validation";363  }364  DXILPostOptimizationValidationLegacy() : ModulePass(ID) {}365 366  static char ID; // Pass identification.367  void getAnalysisUsage(llvm::AnalysisUsage &AU) const override {368    AU.addRequired<DXILResourceWrapperPass>();369    AU.addRequired<DXILResourceBindingWrapperPass>();370    AU.addRequired<DXILMetadataAnalysisWrapperPass>();371    AU.addRequired<RootSignatureAnalysisWrapper>();372    AU.addRequired<DXILResourceTypeWrapperPass>();373    AU.addPreserved<DXILResourceWrapperPass>();374    AU.addPreserved<DXILResourceBindingWrapperPass>();375    AU.addPreserved<DXILMetadataAnalysisWrapperPass>();376    AU.addPreserved<ShaderFlagsAnalysisWrapper>();377    AU.addPreserved<RootSignatureAnalysisWrapper>();378  }379};380char DXILPostOptimizationValidationLegacy::ID = 0;381} // end anonymous namespace382 383INITIALIZE_PASS_BEGIN(DXILPostOptimizationValidationLegacy, DEBUG_TYPE,384                      "DXIL Post Optimization Validation", false, false)385INITIALIZE_PASS_DEPENDENCY(DXILResourceBindingWrapperPass)386INITIALIZE_PASS_DEPENDENCY(DXILResourceTypeWrapperPass)387INITIALIZE_PASS_DEPENDENCY(DXILResourceWrapperPass)388INITIALIZE_PASS_DEPENDENCY(DXILMetadataAnalysisWrapperPass)389INITIALIZE_PASS_DEPENDENCY(RootSignatureAnalysisWrapper)390INITIALIZE_PASS_DEPENDENCY(DXILResourceTypeWrapperPass)391INITIALIZE_PASS_END(DXILPostOptimizationValidationLegacy, DEBUG_TYPE,392                    "DXIL Post Optimization Validation", false, false)393 394ModulePass *llvm::createDXILPostOptimizationValidationLegacyPass() {395  return new DXILPostOptimizationValidationLegacy();396}397