summaryrefslogtreecommitdiff
path: root/llvm/lib/Target/DirectX/DXILResourceAccess.cpp
diff options
context:
space:
mode:
authorMichael Kruse <llvm-project@meinersbur.de>2025-01-03 10:22:51 +0100
committerMichael Kruse <llvm-project@meinersbur.de>2025-01-03 10:22:51 +0100
commit38500d63e14ce340236840f60d356cdefb56a52c (patch)
tree17edbec446ce9b50d2f215a483b83afb293a635d /llvm/lib/Target/DirectX/DXILResourceAccess.cpp
parent1a3d5daaef7a6a63448a497da3eff7fc9e23df26 (diff)
parent27f30029741ecf023baece7b3dde1ff9011ffefc (diff)
Merge branch 'main' into users/meinersbur/flang_runtime_split-headersusers/meinersbur/flang_runtime_split-headers
Diffstat (limited to 'llvm/lib/Target/DirectX/DXILResourceAccess.cpp')
-rw-r--r--llvm/lib/Target/DirectX/DXILResourceAccess.cpp192
1 files changed, 192 insertions, 0 deletions
diff --git a/llvm/lib/Target/DirectX/DXILResourceAccess.cpp b/llvm/lib/Target/DirectX/DXILResourceAccess.cpp
new file mode 100644
index 000000000000..1ff8f09f066d
--- /dev/null
+++ b/llvm/lib/Target/DirectX/DXILResourceAccess.cpp
@@ -0,0 +1,192 @@
+//===- DXILResourceAccess.cpp - Resource access via load/store ------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DXILResourceAccess.h"
+#include "DirectX.h"
+#include "llvm/Analysis/DXILResource.h"
+#include "llvm/IR/Dominators.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/IntrinsicInst.h"
+#include "llvm/IR/Intrinsics.h"
+#include "llvm/IR/IntrinsicsDirectX.h"
+#include "llvm/InitializePasses.h"
+
+#define DEBUG_TYPE "dxil-resource-access"
+
+using namespace llvm;
+
+static void replaceTypedBufferAccess(IntrinsicInst *II,
+ dxil::ResourceTypeInfo &RTI) {
+ const DataLayout &DL = II->getDataLayout();
+
+ auto *HandleType = cast<TargetExtType>(II->getOperand(0)->getType());
+ assert(HandleType->getName() == "dx.TypedBuffer" &&
+ "Unexpected typed buffer type");
+ Type *ContainedType = HandleType->getTypeParameter(0);
+
+ // We need the size of an element in bytes so that we can calculate the offset
+ // in elements given a total offset in bytes later.
+ Type *ScalarType = ContainedType->getScalarType();
+ uint64_t ScalarSize = DL.getTypeSizeInBits(ScalarType) / 8;
+
+ // Process users keeping track of indexing accumulated from GEPs.
+ struct AccessAndIndex {
+ User *Access;
+ Value *Index;
+ };
+ SmallVector<AccessAndIndex> Worklist;
+ for (User *U : II->users())
+ Worklist.push_back({U, nullptr});
+
+ SmallVector<Instruction *> DeadInsts;
+ while (!Worklist.empty()) {
+ AccessAndIndex Current = Worklist.back();
+ Worklist.pop_back();
+
+ if (auto *GEP = dyn_cast<GetElementPtrInst>(Current.Access)) {
+ IRBuilder<> Builder(GEP);
+
+ Value *Index;
+ APInt ConstantOffset(DL.getIndexTypeSizeInBits(GEP->getType()), 0);
+ if (GEP->accumulateConstantOffset(DL, ConstantOffset)) {
+ APInt Scaled = ConstantOffset.udiv(ScalarSize);
+ Index = ConstantInt::get(Builder.getInt32Ty(), Scaled);
+ } else {
+ auto IndexIt = GEP->idx_begin();
+ assert(cast<ConstantInt>(IndexIt)->getZExtValue() == 0 &&
+ "GEP is not indexing through pointer");
+ ++IndexIt;
+ Index = *IndexIt;
+ assert(++IndexIt == GEP->idx_end() && "Too many indices in GEP");
+ }
+
+ for (User *U : GEP->users())
+ Worklist.push_back({U, Index});
+ DeadInsts.push_back(GEP);
+
+ } else if (auto *SI = dyn_cast<StoreInst>(Current.Access)) {
+ assert(SI->getValueOperand() != II && "Pointer escaped!");
+ IRBuilder<> Builder(SI);
+
+ Value *V = SI->getValueOperand();
+ if (V->getType() == ContainedType) {
+ // V is already the right type.
+ } else if (V->getType() == ScalarType) {
+ // We're storing a scalar, so we need to load the current value and only
+ // replace the relevant part.
+ auto *Load = Builder.CreateIntrinsic(
+ ContainedType, Intrinsic::dx_resource_load_typedbuffer,
+ {II->getOperand(0), II->getOperand(1)});
+ // If we have an offset from seeing a GEP earlier, use it.
+ Value *IndexOp = Current.Index
+ ? Current.Index
+ : ConstantInt::get(Builder.getInt32Ty(), 0);
+ V = Builder.CreateInsertElement(Load, V, IndexOp);
+ } else {
+ llvm_unreachable("Store to typed resource has invalid type");
+ }
+
+ auto *Inst = Builder.CreateIntrinsic(
+ Builder.getVoidTy(), Intrinsic::dx_resource_store_typedbuffer,
+ {II->getOperand(0), II->getOperand(1), V});
+ SI->replaceAllUsesWith(Inst);
+ DeadInsts.push_back(SI);
+
+ } else if (auto *LI = dyn_cast<LoadInst>(Current.Access)) {
+ IRBuilder<> Builder(LI);
+ Value *V = Builder.CreateIntrinsic(
+ ContainedType, Intrinsic::dx_resource_load_typedbuffer,
+ {II->getOperand(0), II->getOperand(1)});
+ if (Current.Index)
+ V = Builder.CreateExtractElement(V, Current.Index);
+
+ LI->replaceAllUsesWith(V);
+ DeadInsts.push_back(LI);
+
+ } else
+ llvm_unreachable("Unhandled instruction - pointer escaped?");
+ }
+
+ // Traverse the now-dead instructions in RPO and remove them.
+ for (Instruction *Dead : llvm::reverse(DeadInsts))
+ Dead->eraseFromParent();
+ II->eraseFromParent();
+}
+
+static bool transformResourcePointers(Function &F, DXILResourceTypeMap &DRTM) {
+ bool Changed = false;
+ SmallVector<std::pair<IntrinsicInst *, dxil::ResourceTypeInfo>> Resources;
+ for (BasicBlock &BB : F)
+ for (Instruction &I : BB)
+ if (auto *II = dyn_cast<IntrinsicInst>(&I))
+ if (II->getIntrinsicID() == Intrinsic::dx_resource_getpointer) {
+ auto *HandleTy = cast<TargetExtType>(II->getArgOperand(0)->getType());
+ Resources.emplace_back(II, DRTM[HandleTy]);
+ }
+
+ for (auto &[II, RI] : Resources) {
+ if (RI.isTyped()) {
+ Changed = true;
+ replaceTypedBufferAccess(II, RI);
+ }
+
+ // TODO: handle other resource types. We should probably have an
+ // `unreachable` here once we've added support for all of them.
+ }
+
+ return Changed;
+}
+
+PreservedAnalyses DXILResourceAccess::run(Function &F,
+ FunctionAnalysisManager &FAM) {
+ auto &MAMProxy = FAM.getResult<ModuleAnalysisManagerFunctionProxy>(F);
+ DXILResourceTypeMap *DRTM =
+ MAMProxy.getCachedResult<DXILResourceTypeAnalysis>(*F.getParent());
+ assert(DRTM && "DXILResourceTypeAnalysis must be available");
+
+ bool MadeChanges = transformResourcePointers(F, *DRTM);
+ if (!MadeChanges)
+ return PreservedAnalyses::all();
+
+ PreservedAnalyses PA;
+ PA.preserve<DXILResourceTypeAnalysis>();
+ PA.preserve<DominatorTreeAnalysis>();
+ return PA;
+}
+
+namespace {
+class DXILResourceAccessLegacy : public FunctionPass {
+public:
+ bool runOnFunction(Function &F) override {
+ DXILResourceTypeMap &DRTM =
+ getAnalysis<DXILResourceTypeWrapperPass>().getResourceTypeMap();
+
+ return transformResourcePointers(F, DRTM);
+ }
+ StringRef getPassName() const override { return "DXIL Resource Access"; }
+ DXILResourceAccessLegacy() : FunctionPass(ID) {}
+
+ static char ID; // Pass identification.
+ void getAnalysisUsage(llvm::AnalysisUsage &AU) const override {
+ AU.addRequired<DXILResourceTypeWrapperPass>();
+ AU.addPreserved<DominatorTreeWrapperPass>();
+ }
+};
+char DXILResourceAccessLegacy::ID = 0;
+} // end anonymous namespace
+
+INITIALIZE_PASS_BEGIN(DXILResourceAccessLegacy, DEBUG_TYPE,
+ "DXIL Resource Access", false, false)
+INITIALIZE_PASS_DEPENDENCY(DXILResourceTypeWrapperPass)
+INITIALIZE_PASS_END(DXILResourceAccessLegacy, DEBUG_TYPE,
+ "DXIL Resource Access", false, false)
+
+FunctionPass *llvm::createDXILResourceAccessLegacyPass() {
+ return new DXILResourceAccessLegacy();
+}