1//===- DXILCBufferAccess.cpp - Translate CBuffer Loads --------------------===//
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-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "DXILCBufferAccess.h"
10#include "DirectX.h"
11#include "llvm/Analysis/DXILResource.h"
12#include "llvm/Frontend/HLSL/CBuffer.h"
13#include "llvm/Frontend/HLSL/HLSLResource.h"
14#include "llvm/IR/IRBuilder.h"
15#include "llvm/IR/IntrinsicInst.h"
16#include "llvm/IR/IntrinsicsDirectX.h"
17#include "llvm/IR/ReplaceConstant.h"
18#include "llvm/InitializePasses.h"
19#include "llvm/Pass.h"
20#include "llvm/Support/FormatVariadic.h"
21#include "llvm/Transforms/Utils/Local.h"
22#include "llvm/Transforms/Utils/ModuleUtils.h"
23
24#define DEBUG_TYPE "dxil-cbuffer-access"
25using namespace llvm;
26
27static void replaceUsersOfGlobal(GlobalVariable *Global,
28 GlobalVariable *HandleGV, size_t Offset) {
29 for (Use &U : make_early_inc_range(Range: Global->uses())) {
30 auto UseInst = dyn_cast<Instruction>(Val: U.getUser());
31 // TODO: Constants? Metadata?
32 assert(UseInst && "Non-instruction use of cbuffer");
33
34 IRBuilder<> Builder(UseInst);
35 LoadInst *Handle = Builder.CreateLoad(Ty: HandleGV->getValueType(), Ptr: HandleGV,
36 Name: HandleGV->getName());
37 Value *Ptr = Builder.CreateIntrinsic(
38 RetTy: Global->getType(), ID: Intrinsic::dx_resource_getpointer,
39 Args: ArrayRef<Value *>{Handle,
40 ConstantInt::get(Ty: Builder.getInt32Ty(), V: Offset)});
41 U.set(Ptr);
42 }
43
44 Global->eraseFromParent();
45}
46
47static bool replaceCBufferAccesses(Module &M) {
48 std::optional<hlsl::CBufferMetadata> CBufMD = hlsl::CBufferMetadata::get(
49 M, IsPadding: [](Type *Ty) { return isa<llvm::dxil::PaddingExtType>(Val: Ty); });
50 if (!CBufMD)
51 return false;
52
53 SmallVector<Constant *> CBufferGlobals;
54 SmallPtrSet<GlobalVariable *, 8> CBufferHandles;
55 for (const hlsl::CBufferMapping &Mapping : *CBufMD) {
56 CBufferHandles.insert(Ptr: Mapping.Handle);
57 for (const hlsl::CBufferMember &Member : Mapping.Members)
58 CBufferGlobals.push_back(Elt: Member.GV);
59 }
60 convertUsersOfConstantsToInstructions(Consts: CBufferGlobals);
61
62 for (const hlsl::CBufferMapping &Mapping : *CBufMD)
63 for (const hlsl::CBufferMember &Member : Mapping.Members)
64 replaceUsersOfGlobal(Global: Member.GV, HandleGV: Mapping.Handle, Offset: Member.Offset);
65
66 // Remove cbuffer handle globals from @llvm.compiler.used list.
67 llvm::removeFromUsedLists(M, ShouldRemove: [&](Constant *C) -> bool {
68 auto *GV = dyn_cast<GlobalVariable>(Val: C);
69 return GV && CBufferHandles.contains(Ptr: GV);
70 });
71 for (GlobalVariable *HandleGV : CBufferHandles)
72 HandleGV->removeDeadConstantUsers();
73
74 CBufMD->eraseFromModule();
75 return true;
76}
77
78PreservedAnalyses DXILCBufferAccess::run(Module &M, ModuleAnalysisManager &AM) {
79 PreservedAnalyses PA;
80 bool Changed = replaceCBufferAccesses(M);
81
82 if (!Changed)
83 return PreservedAnalyses::all();
84 return PA;
85}
86
87namespace {
88class DXILCBufferAccessLegacy : public ModulePass {
89public:
90 bool runOnModule(Module &M) override { return replaceCBufferAccesses(M); }
91 StringRef getPassName() const override { return "DXIL CBuffer Access"; }
92 DXILCBufferAccessLegacy() : ModulePass(ID) {}
93
94 static char ID; // Pass identification.
95};
96char DXILCBufferAccessLegacy::ID = 0;
97} // end anonymous namespace
98
99INITIALIZE_PASS(DXILCBufferAccessLegacy, DEBUG_TYPE, "DXIL CBuffer Access",
100 false, false)
101
102ModulePass *llvm::createDXILCBufferAccessLegacyPass() {
103 return new DXILCBufferAccessLegacy();
104}
105