| //===- DXILCBufferAccess.cpp - Translate CBuffer Loads --------------------===// | |
| // | |
| // 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 "DXILCBufferAccess.h" | |
| #include "DirectX.h" | |
| #include "llvm/Analysis/DXILResource.h" | |
| #include "llvm/Frontend/HLSL/CBuffer.h" | |
| #include "llvm/Frontend/HLSL/HLSLResource.h" | |
| #include "llvm/IR/IRBuilder.h" | |
| #include "llvm/IR/IntrinsicInst.h" | |
| #include "llvm/IR/IntrinsicsDirectX.h" | |
| #include "llvm/IR/ReplaceConstant.h" | |
| #include "llvm/InitializePasses.h" | |
| #include "llvm/Pass.h" | |
| #include "llvm/Support/FormatVariadic.h" | |
| #include "llvm/Transforms/Utils/Local.h" | |
| #include "llvm/Transforms/Utils/ModuleUtils.h" | |
| #define DEBUG_TYPE "dxil-cbuffer-access" | |
| using namespace llvm; | |
| static void replaceUsersOfGlobal(GlobalVariable *Global, | |
| GlobalVariable *HandleGV, size_t Offset) { | |
| for (Use &U : make_early_inc_range(Global->uses())) { | |
| auto UseInst = dyn_cast<Instruction>(U.getUser()); | |
| // TODO: Constants? Metadata? | |
| assert(UseInst && "Non-instruction use of cbuffer"); | |
| IRBuilder<> Builder(UseInst); | |
| LoadInst *Handle = Builder.CreateLoad(HandleGV->getValueType(), HandleGV, | |
| HandleGV->getName()); | |
| Value *Ptr = Builder.CreateIntrinsic( | |
| Global->getType(), Intrinsic::dx_resource_getpointer, | |
| ArrayRef<Value *>{Handle, | |
| ConstantInt::get(Builder.getInt32Ty(), Offset)}); | |
| U.set(Ptr); | |
| } | |
| Global->eraseFromParent(); | |
| } | |
| static bool replaceCBufferAccesses(Module &M) { | |
| std::optional<hlsl::CBufferMetadata> CBufMD = hlsl::CBufferMetadata::get( | |
| M, [](Type *Ty) { return isa<llvm::dxil::PaddingExtType>(Ty); }); | |
| if (!CBufMD) | |
| return false; | |
| SmallVector<Constant *> CBufferGlobals; | |
| SmallPtrSet<GlobalVariable *, 8> CBufferHandles; | |
| for (const hlsl::CBufferMapping &Mapping : *CBufMD) { | |
| CBufferHandles.insert(Mapping.Handle); | |
| for (const hlsl::CBufferMember &Member : Mapping.Members) | |
| CBufferGlobals.push_back(Member.GV); | |
| } | |
| convertUsersOfConstantsToInstructions(CBufferGlobals); | |
| for (const hlsl::CBufferMapping &Mapping : *CBufMD) | |
| for (const hlsl::CBufferMember &Member : Mapping.Members) | |
| replaceUsersOfGlobal(Member.GV, Mapping.Handle, Member.Offset); | |
| // Remove cbuffer handle globals from @llvm.compiler.used list. | |
| llvm::removeFromUsedLists(M, [&](Constant *C) -> bool { | |
| auto *GV = dyn_cast<GlobalVariable>(C); | |
| return GV && CBufferHandles.contains(GV); | |
| }); | |
| for (GlobalVariable *HandleGV : CBufferHandles) | |
| HandleGV->removeDeadConstantUsers(); | |
| CBufMD->eraseFromModule(); | |
| return true; | |
| } | |
| PreservedAnalyses DXILCBufferAccess::run(Module &M, ModuleAnalysisManager &AM) { | |
| PreservedAnalyses PA; | |
| bool Changed = replaceCBufferAccesses(M); | |
| if (!Changed) | |
| return PreservedAnalyses::all(); | |
| return PA; | |
| } | |
| namespace { | |
| class DXILCBufferAccessLegacy : public ModulePass { | |
| public: | |
| bool runOnModule(Module &M) override { return replaceCBufferAccesses(M); } | |
| StringRef getPassName() const override { return "DXIL CBuffer Access"; } | |
| DXILCBufferAccessLegacy() : ModulePass(ID) {} | |
| static char ID; // Pass identification. | |
| }; | |
| char DXILCBufferAccessLegacy::ID = 0; | |
| } // end anonymous namespace | |
| INITIALIZE_PASS(DXILCBufferAccessLegacy, DEBUG_TYPE, "DXIL CBuffer Access", | |
| false, false) | |
| ModulePass *llvm::createDXILCBufferAccessLegacyPass() { | |
| return new DXILCBufferAccessLegacy(); | |
| } |