Path: blob/main/contrib/llvm-project/llvm/lib/Target/DirectX/DXILResourceAccess.cpp
213799 views
//===- DXILResourceAccess.cpp - Resource access via load/store ------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "DXILResourceAccess.h"9#include "DirectX.h"10#include "llvm/Analysis/DXILResource.h"11#include "llvm/IR/Dominators.h"12#include "llvm/IR/IRBuilder.h"13#include "llvm/IR/Instructions.h"14#include "llvm/IR/IntrinsicInst.h"15#include "llvm/IR/Intrinsics.h"16#include "llvm/IR/IntrinsicsDirectX.h"17#include "llvm/InitializePasses.h"1819#define DEBUG_TYPE "dxil-resource-access"2021using namespace llvm;2223static Value *calculateGEPOffset(GetElementPtrInst *GEP, Value *PrevOffset,24dxil::ResourceTypeInfo &RTI) {25assert(!PrevOffset && "Non-constant GEP chains not handled yet");2627const DataLayout &DL = GEP->getDataLayout();2829uint64_t ScalarSize = 1;30if (RTI.isTyped()) {31Type *ContainedType = RTI.getHandleTy()->getTypeParameter(0);32// We need the size of an element in bytes so that we can calculate the33// offset in elements given a total offset in bytes.34Type *ScalarType = ContainedType->getScalarType();35ScalarSize = DL.getTypeSizeInBits(ScalarType) / 8;36}3738APInt ConstantOffset(DL.getIndexTypeSizeInBits(GEP->getType()), 0);39if (GEP->accumulateConstantOffset(DL, ConstantOffset)) {40APInt Scaled = ConstantOffset.udiv(ScalarSize);41return ConstantInt::get(Type::getInt32Ty(GEP->getContext()), Scaled);42}4344auto IndexIt = GEP->idx_begin();45assert(cast<ConstantInt>(IndexIt)->getZExtValue() == 0 &&46"GEP is not indexing through pointer");47++IndexIt;48Value *Offset = *IndexIt;49assert(++IndexIt == GEP->idx_end() && "Too many indices in GEP");50return Offset;51}5253static void createTypedBufferStore(IntrinsicInst *II, StoreInst *SI,54Value *Offset, dxil::ResourceTypeInfo &RTI) {55IRBuilder<> Builder(SI);56Type *ContainedType = RTI.getHandleTy()->getTypeParameter(0);57Type *LoadType = StructType::get(ContainedType, Builder.getInt1Ty());5859Value *V = SI->getValueOperand();60if (V->getType() == ContainedType) {61// V is already the right type.62assert(!Offset && "store of whole element has offset?");63} else if (V->getType() == ContainedType->getScalarType()) {64// We're storing a scalar, so we need to load the current value and only65// replace the relevant part.66auto *Load = Builder.CreateIntrinsic(67LoadType, Intrinsic::dx_resource_load_typedbuffer,68{II->getOperand(0), II->getOperand(1)});69auto *Struct = Builder.CreateExtractValue(Load, {0});7071// If we have an offset from seeing a GEP earlier, use that. Otherwise, 0.72if (!Offset)73Offset = ConstantInt::get(Builder.getInt32Ty(), 0);74V = Builder.CreateInsertElement(Struct, V, Offset);75} else {76llvm_unreachable("Store to typed resource has invalid type");77}7879auto *Inst = Builder.CreateIntrinsic(80Builder.getVoidTy(), Intrinsic::dx_resource_store_typedbuffer,81{II->getOperand(0), II->getOperand(1), V});82SI->replaceAllUsesWith(Inst);83}8485static void createRawStore(IntrinsicInst *II, StoreInst *SI, Value *Offset) {86IRBuilder<> Builder(SI);8788if (!Offset)89Offset = ConstantInt::get(Builder.getInt32Ty(), 0);90Value *V = SI->getValueOperand();91// TODO: break up larger types92auto *Inst = Builder.CreateIntrinsic(93Builder.getVoidTy(), Intrinsic::dx_resource_store_rawbuffer,94{II->getOperand(0), II->getOperand(1), Offset, V});95SI->replaceAllUsesWith(Inst);96}9798static void createStoreIntrinsic(IntrinsicInst *II, StoreInst *SI,99Value *Offset, dxil::ResourceTypeInfo &RTI) {100switch (RTI.getResourceKind()) {101case dxil::ResourceKind::TypedBuffer:102return createTypedBufferStore(II, SI, Offset, RTI);103case dxil::ResourceKind::RawBuffer:104case dxil::ResourceKind::StructuredBuffer:105return createRawStore(II, SI, Offset);106case dxil::ResourceKind::Texture1D:107case dxil::ResourceKind::Texture2D:108case dxil::ResourceKind::Texture2DMS:109case dxil::ResourceKind::Texture3D:110case dxil::ResourceKind::TextureCube:111case dxil::ResourceKind::Texture1DArray:112case dxil::ResourceKind::Texture2DArray:113case dxil::ResourceKind::Texture2DMSArray:114case dxil::ResourceKind::TextureCubeArray:115case dxil::ResourceKind::FeedbackTexture2D:116case dxil::ResourceKind::FeedbackTexture2DArray:117reportFatalUsageError("DXIL Load not implemented yet");118return;119case dxil::ResourceKind::CBuffer:120case dxil::ResourceKind::Sampler:121case dxil::ResourceKind::TBuffer:122case dxil::ResourceKind::RTAccelerationStructure:123case dxil::ResourceKind::Invalid:124case dxil::ResourceKind::NumEntries:125llvm_unreachable("Invalid resource kind for store");126}127llvm_unreachable("Unhandled case in switch");128}129130static void createTypedBufferLoad(IntrinsicInst *II, LoadInst *LI,131Value *Offset, dxil::ResourceTypeInfo &RTI) {132IRBuilder<> Builder(LI);133Type *ContainedType = RTI.getHandleTy()->getTypeParameter(0);134Type *LoadType = StructType::get(ContainedType, Builder.getInt1Ty());135136Value *V =137Builder.CreateIntrinsic(LoadType, Intrinsic::dx_resource_load_typedbuffer,138{II->getOperand(0), II->getOperand(1)});139V = Builder.CreateExtractValue(V, {0});140141if (Offset)142V = Builder.CreateExtractElement(V, Offset);143144// If we loaded a <1 x ...> instead of a scalar (presumably to feed a145// shufflevector), then make sure we're maintaining the resulting type.146if (auto *VT = dyn_cast<FixedVectorType>(LI->getType()))147if (VT->getNumElements() == 1 && !isa<FixedVectorType>(V->getType()))148V = Builder.CreateInsertElement(PoisonValue::get(VT), V,149Builder.getInt32(0));150151LI->replaceAllUsesWith(V);152}153154static void createRawLoad(IntrinsicInst *II, LoadInst *LI, Value *Offset) {155IRBuilder<> Builder(LI);156// TODO: break up larger types157Type *LoadType = StructType::get(LI->getType(), Builder.getInt1Ty());158if (!Offset)159Offset = ConstantInt::get(Builder.getInt32Ty(), 0);160Value *V =161Builder.CreateIntrinsic(LoadType, Intrinsic::dx_resource_load_rawbuffer,162{II->getOperand(0), II->getOperand(1), Offset});163V = Builder.CreateExtractValue(V, {0});164165LI->replaceAllUsesWith(V);166}167168static void createLoadIntrinsic(IntrinsicInst *II, LoadInst *LI, Value *Offset,169dxil::ResourceTypeInfo &RTI) {170switch (RTI.getResourceKind()) {171case dxil::ResourceKind::TypedBuffer:172return createTypedBufferLoad(II, LI, Offset, RTI);173case dxil::ResourceKind::RawBuffer:174case dxil::ResourceKind::StructuredBuffer:175return createRawLoad(II, LI, Offset);176case dxil::ResourceKind::Texture1D:177case dxil::ResourceKind::Texture2D:178case dxil::ResourceKind::Texture2DMS:179case dxil::ResourceKind::Texture3D:180case dxil::ResourceKind::TextureCube:181case dxil::ResourceKind::Texture1DArray:182case dxil::ResourceKind::Texture2DArray:183case dxil::ResourceKind::Texture2DMSArray:184case dxil::ResourceKind::TextureCubeArray:185case dxil::ResourceKind::FeedbackTexture2D:186case dxil::ResourceKind::FeedbackTexture2DArray:187case dxil::ResourceKind::CBuffer:188case dxil::ResourceKind::TBuffer:189// TODO: handle these190return;191case dxil::ResourceKind::Sampler:192case dxil::ResourceKind::RTAccelerationStructure:193case dxil::ResourceKind::Invalid:194case dxil::ResourceKind::NumEntries:195llvm_unreachable("Invalid resource kind for load");196}197llvm_unreachable("Unhandled case in switch");198}199200static void replaceAccess(IntrinsicInst *II, dxil::ResourceTypeInfo &RTI) {201// Process users keeping track of indexing accumulated from GEPs.202struct AccessAndOffset {203User *Access;204Value *Offset;205};206SmallVector<AccessAndOffset> Worklist;207for (User *U : II->users())208Worklist.push_back({U, nullptr});209210SmallVector<Instruction *> DeadInsts;211while (!Worklist.empty()) {212AccessAndOffset Current = Worklist.back();213Worklist.pop_back();214215if (auto *GEP = dyn_cast<GetElementPtrInst>(Current.Access)) {216IRBuilder<> Builder(GEP);217218Value *Offset = calculateGEPOffset(GEP, Current.Offset, RTI);219for (User *U : GEP->users())220Worklist.push_back({U, Offset});221DeadInsts.push_back(GEP);222223} else if (auto *SI = dyn_cast<StoreInst>(Current.Access)) {224assert(SI->getValueOperand() != II && "Pointer escaped!");225createStoreIntrinsic(II, SI, Current.Offset, RTI);226DeadInsts.push_back(SI);227228} else if (auto *LI = dyn_cast<LoadInst>(Current.Access)) {229createLoadIntrinsic(II, LI, Current.Offset, RTI);230DeadInsts.push_back(LI);231232} else233llvm_unreachable("Unhandled instruction - pointer escaped?");234}235236// Traverse the now-dead instructions in RPO and remove them.237for (Instruction *Dead : llvm::reverse(DeadInsts))238Dead->eraseFromParent();239II->eraseFromParent();240}241242static bool transformResourcePointers(Function &F, DXILResourceTypeMap &DRTM) {243bool Changed = false;244SmallVector<std::pair<IntrinsicInst *, dxil::ResourceTypeInfo>> Resources;245for (BasicBlock &BB : F)246for (Instruction &I : BB)247if (auto *II = dyn_cast<IntrinsicInst>(&I))248if (II->getIntrinsicID() == Intrinsic::dx_resource_getpointer) {249auto *HandleTy = cast<TargetExtType>(II->getArgOperand(0)->getType());250Resources.emplace_back(II, DRTM[HandleTy]);251}252253for (auto &[II, RI] : Resources)254replaceAccess(II, RI);255256return Changed;257}258259PreservedAnalyses DXILResourceAccess::run(Function &F,260FunctionAnalysisManager &FAM) {261auto &MAMProxy = FAM.getResult<ModuleAnalysisManagerFunctionProxy>(F);262DXILResourceTypeMap *DRTM =263MAMProxy.getCachedResult<DXILResourceTypeAnalysis>(*F.getParent());264assert(DRTM && "DXILResourceTypeAnalysis must be available");265266bool MadeChanges = transformResourcePointers(F, *DRTM);267if (!MadeChanges)268return PreservedAnalyses::all();269270PreservedAnalyses PA;271PA.preserve<DXILResourceTypeAnalysis>();272PA.preserve<DominatorTreeAnalysis>();273return PA;274}275276namespace {277class DXILResourceAccessLegacy : public FunctionPass {278public:279bool runOnFunction(Function &F) override {280DXILResourceTypeMap &DRTM =281getAnalysis<DXILResourceTypeWrapperPass>().getResourceTypeMap();282283return transformResourcePointers(F, DRTM);284}285StringRef getPassName() const override { return "DXIL Resource Access"; }286DXILResourceAccessLegacy() : FunctionPass(ID) {}287288static char ID; // Pass identification.289void getAnalysisUsage(llvm::AnalysisUsage &AU) const override {290AU.addRequired<DXILResourceTypeWrapperPass>();291AU.addPreserved<DominatorTreeWrapperPass>();292}293};294char DXILResourceAccessLegacy::ID = 0;295} // end anonymous namespace296297INITIALIZE_PASS_BEGIN(DXILResourceAccessLegacy, DEBUG_TYPE,298"DXIL Resource Access", false, false)299INITIALIZE_PASS_DEPENDENCY(DXILResourceTypeWrapperPass)300INITIALIZE_PASS_END(DXILResourceAccessLegacy, DEBUG_TYPE,301"DXIL Resource Access", false, false)302303FunctionPass *llvm::createDXILResourceAccessLegacyPass() {304return new DXILResourceAccessLegacy();305}306307308