brintos

brintos / llvm-project-archived public Read only

0
0
Text · 5.6 KiB · 227d871 Raw
152 lines · cpp
1//===- SPIRVCBufferAccess.cpp - Translate CBuffer Loads ---------*- C++ -*-===//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// This pass replaces all accesses to constant buffer global variables with10// accesses to the proper SPIR-V resource.11//12// The pass operates as follows:13// 1. It finds all constant buffers by looking for the `!hlsl.cbs` metadata.14// 2. For each cbuffer, it finds the global variable holding the resource handle15//    and the global variables for each of the cbuffer's members.16// 3. For each member variable, it creates a call to the17//    `llvm.spv.resource.getpointer` intrinsic. This intrinsic takes the18//    resource handle and the member's index within the cbuffer as arguments.19//    The result is a pointer to that member within the SPIR-V resource.20// 4. It then replaces all uses of the original member global variable with the21//    pointer returned by the `getpointer` intrinsic. This effectively retargets22//    all loads and GEPs to the new resource pointer.23// 5. Finally, it cleans up by deleting the original global variables and the24//    `!hlsl.cbs` metadata.25//26// This approach allows subsequent passes, like SPIRVEmitIntrinsics, to27// correctly handle GEPs that operate on the result of the `getpointer` call,28// folding them into a single OpAccessChain instruction.29//30//===----------------------------------------------------------------------===//31 32#include "SPIRVCBufferAccess.h"33#include "SPIRV.h"34#include "llvm/Frontend/HLSL/CBuffer.h"35#include "llvm/IR/IRBuilder.h"36#include "llvm/IR/IntrinsicsSPIRV.h"37#include "llvm/IR/Module.h"38#include "llvm/IR/ReplaceConstant.h"39 40#define DEBUG_TYPE "spirv-cbuffer-access"41using namespace llvm;42 43// Finds the single instruction that defines the resource handle. This is44// typically a call to `llvm.spv.resource.handlefrombinding`.45static Instruction *findHandleDef(GlobalVariable *HandleVar) {46  for (User *U : HandleVar->users()) {47    if (auto *SI = dyn_cast<StoreInst>(U)) {48      if (auto *I = dyn_cast<Instruction>(SI->getValueOperand())) {49        return I;50      }51    }52  }53  return nullptr;54}55 56static bool replaceCBufferAccesses(Module &M) {57  std::optional<hlsl::CBufferMetadata> CBufMD =58      hlsl::CBufferMetadata::get(M, [](Type *Ty) {59        if (auto *TET = dyn_cast<TargetExtType>(Ty))60          return TET->getName() == "spirv.Padding";61        return false;62      });63  if (!CBufMD)64    return false;65 66  SmallVector<Constant *> CBufferGlobals;67  for (const hlsl::CBufferMapping &Mapping : *CBufMD)68    for (const hlsl::CBufferMember &Member : Mapping.Members)69      CBufferGlobals.push_back(Member.GV);70  convertUsersOfConstantsToInstructions(CBufferGlobals);71 72  for (const hlsl::CBufferMapping &Mapping : *CBufMD) {73    Instruction *HandleDef = findHandleDef(Mapping.Handle);74    if (!HandleDef) {75      report_fatal_error("Could not find handle definition for cbuffer: " +76                         Mapping.Handle->getName());77    }78 79    // The handle definition should dominate all uses of the cbuffer members.80    // We'll insert our getpointer calls right after it.81    IRBuilder<> Builder(HandleDef->getNextNode());82    auto *HandleTy = cast<TargetExtType>(Mapping.Handle->getValueType());83    auto *LayoutTy = cast<StructType>(HandleTy->getTypeParameter(0));84    const StructLayout *SL = M.getDataLayout().getStructLayout(LayoutTy);85 86    for (const hlsl::CBufferMember &Member : Mapping.Members) {87      GlobalVariable *MemberGV = Member.GV;88      if (MemberGV->use_empty()) {89        continue;90      }91 92      uint32_t IndexInStruct = SL->getElementContainingOffset(Member.Offset);93 94      // Create the getpointer intrinsic call.95      Value *IndexVal = Builder.getInt32(IndexInStruct);96      Type *PtrType = MemberGV->getType();97      Value *GetPointerCall = Builder.CreateIntrinsic(98          PtrType, Intrinsic::spv_resource_getpointer, {HandleDef, IndexVal});99 100      MemberGV->replaceAllUsesWith(GetPointerCall);101    }102  }103 104  // Now that all uses are replaced, clean up the globals and metadata.105  for (const hlsl::CBufferMapping &Mapping : *CBufMD) {106    for (const auto &Member : Mapping.Members) {107      Member.GV->eraseFromParent();108    }109    // Erase the stores to the handle variable before erasing the handle itself.110    SmallVector<Instruction *, 4> HandleStores;111    for (User *U : Mapping.Handle->users()) {112      if (auto *SI = dyn_cast<StoreInst>(U)) {113        HandleStores.push_back(SI);114      }115    }116    for (Instruction *I : HandleStores) {117      I->eraseFromParent();118    }119    Mapping.Handle->eraseFromParent();120  }121 122  CBufMD->eraseFromModule();123  return true;124}125 126PreservedAnalyses SPIRVCBufferAccess::run(Module &M,127                                          ModuleAnalysisManager &AM) {128  if (replaceCBufferAccesses(M)) {129    return PreservedAnalyses::none();130  }131  return PreservedAnalyses::all();132}133 134namespace {135class SPIRVCBufferAccessLegacy : public ModulePass {136public:137  bool runOnModule(Module &M) override { return replaceCBufferAccesses(M); }138  StringRef getPassName() const override { return "SPIRV CBuffer Access"; }139  SPIRVCBufferAccessLegacy() : ModulePass(ID) {}140 141  static char ID; // Pass identification.142};143char SPIRVCBufferAccessLegacy::ID = 0;144} // end anonymous namespace145 146INITIALIZE_PASS(SPIRVCBufferAccessLegacy, DEBUG_TYPE, "SPIRV CBuffer Access",147                false, false)148 149ModulePass *llvm::createSPIRVCBufferAccessLegacyPass() {150  return new SPIRVCBufferAccessLegacy();151}152