Skip to content

Commit 00ad9ec

Browse files
authored
[SPIRV][HLSL] Implement CBuffer access lowering pass (#159136)
This patch introduces a new pass, SPIRVCBufferAccess, which is responsible for translating accesses to HLSL constant buffer (cbuffer) global variables into accesses to the proper SPIR-V resource. The pass operates by: 1. Identifying all cbuffers via the `!hlsl.cbs` metadata. 2. Replacing all uses of cbuffer member global variables with `llvm.spv.resource.getpointer` intrinsics. 3. Cleaning up the original global variables and metadata. This approach allows subsequent passes, like SPIRVEmitIntrinsics, to correctly fold GEPs into a single OpAccessChain instruction. The patch also includes a comprehensive set of lit tests to cover various scenarios: - Basic cbuffer access direct load and GEPs. - Unused and partially unused cbuffers. This implements the SPIR-V version of https://github.com/llvm/wg-hlsl/blob/main/proposals/0016-constant-buffers.md#lowering-to-buffer-load-intrinsics.
1 parent 0dc2148 commit 00ad9ec

File tree

9 files changed

+295
-2
lines changed

9 files changed

+295
-2
lines changed

llvm/lib/Frontend/HLSL/CBuffer.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ using namespace llvm::hlsl;
1717

1818
static size_t getMemberOffset(GlobalVariable *Handle, size_t Index) {
1919
auto *HandleTy = cast<TargetExtType>(Handle->getValueType());
20-
assert(HandleTy->getName().ends_with(".CBuffer") && "Not a cbuffer type");
20+
assert((HandleTy->getName().ends_with(".CBuffer") ||
21+
HandleTy->getName() == "spirv.VulkanBuffer") &&
22+
"Not a cbuffer type");
2123
assert(HandleTy->getNumTypeParameters() == 1 && "Expected layout type");
2224

2325
auto *LayoutTy = cast<TargetExtType>(HandleTy->getTypeParameter(0));

llvm/lib/Target/SPIRV/CMakeLists.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ add_llvm_target(SPIRVCodeGen
4848
SPIRVTargetTransformInfo.cpp
4949
SPIRVUtils.cpp
5050
SPIRVEmitNonSemanticDI.cpp
51+
SPIRVCBufferAccess.cpp
5152

5253
LINK_COMPONENTS
5354
Analysis
@@ -57,8 +58,9 @@ add_llvm_target(SPIRVCodeGen
5758
Core
5859
Demangle
5960
GlobalISel
60-
SPIRVAnalysis
61+
FrontendHLSL
6162
MC
63+
SPIRVAnalysis
6264
SPIRVDesc
6365
SPIRVInfo
6466
ScalarOpts

llvm/lib/Target/SPIRV/SPIRV.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ class RegisterBankInfo;
2121

2222
ModulePass *createSPIRVPrepareFunctionsPass(const SPIRVTargetMachine &TM);
2323
FunctionPass *createSPIRVStructurizerPass();
24+
ModulePass *createSPIRVCBufferAccessLegacyPass();
2425
FunctionPass *createSPIRVMergeRegionExitTargetsPass();
2526
FunctionPass *createSPIRVStripConvergenceIntrinsicsPass();
2627
ModulePass *createSPIRVLegalizeImplicitBindingPass();
@@ -43,6 +44,7 @@ void initializeSPIRVPreLegalizerPass(PassRegistry &);
4344
void initializeSPIRVPreLegalizerCombinerPass(PassRegistry &);
4445
void initializeSPIRVPostLegalizerPass(PassRegistry &);
4546
void initializeSPIRVStructurizerPass(PassRegistry &);
47+
void initializeSPIRVCBufferAccessLegacyPass(PassRegistry &);
4648
void initializeSPIRVEmitIntrinsicsPass(PassRegistry &);
4749
void initializeSPIRVEmitNonSemanticDIPass(PassRegistry &);
4850
void initializeSPIRVLegalizePointerCastPass(PassRegistry &);
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
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-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// This pass replaces all accesses to constant buffer global variables with
10+
// 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 handle
15+
// and the global variables for each of the cbuffer's members.
16+
// 3. For each member variable, it creates a call to the
17+
// `llvm.spv.resource.getpointer` intrinsic. This intrinsic takes the
18+
// 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 the
21+
// pointer returned by the `getpointer` intrinsic. This effectively retargets
22+
// all loads and GEPs to the new resource pointer.
23+
// 5. Finally, it cleans up by deleting the original global variables and the
24+
// `!hlsl.cbs` metadata.
25+
//
26+
// This approach allows subsequent passes, like SPIRVEmitIntrinsics, to
27+
// 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+
39+
#define DEBUG_TYPE "spirv-cbuffer-access"
40+
using namespace llvm;
41+
42+
// Finds the single instruction that defines the resource handle. This is
43+
// typically a call to `llvm.spv.resource.handlefrombinding`.
44+
static Instruction *findHandleDef(GlobalVariable *HandleVar) {
45+
for (User *U : HandleVar->users()) {
46+
if (auto *SI = dyn_cast<StoreInst>(U)) {
47+
if (auto *I = dyn_cast<Instruction>(SI->getValueOperand())) {
48+
return I;
49+
}
50+
}
51+
}
52+
return nullptr;
53+
}
54+
55+
static bool replaceCBufferAccesses(Module &M) {
56+
std::optional<hlsl::CBufferMetadata> CBufMD = hlsl::CBufferMetadata::get(M);
57+
if (!CBufMD)
58+
return false;
59+
60+
for (const hlsl::CBufferMapping &Mapping : *CBufMD) {
61+
Instruction *HandleDef = findHandleDef(Mapping.Handle);
62+
if (!HandleDef) {
63+
report_fatal_error("Could not find handle definition for cbuffer: " +
64+
Mapping.Handle->getName());
65+
}
66+
67+
// The handle definition should dominate all uses of the cbuffer members.
68+
// We'll insert our getpointer calls right after it.
69+
IRBuilder<> Builder(HandleDef->getNextNode());
70+
71+
for (uint32_t Index = 0; Index < Mapping.Members.size(); ++Index) {
72+
GlobalVariable *MemberGV = Mapping.Members[Index].GV;
73+
if (MemberGV->use_empty()) {
74+
continue;
75+
}
76+
77+
// Create the getpointer intrinsic call.
78+
Value *IndexVal = Builder.getInt32(Index);
79+
Type *PtrType = MemberGV->getType();
80+
Value *GetPointerCall = Builder.CreateIntrinsic(
81+
PtrType, Intrinsic::spv_resource_getpointer, {HandleDef, IndexVal});
82+
83+
// We cannot use replaceAllUsesWith here because some uses may be
84+
// ConstantExprs, which cannot be replaced with non-constants.
85+
SmallVector<User *, 4> Users(MemberGV->users());
86+
for (User *U : Users) {
87+
U->replaceUsesOfWith(MemberGV, GetPointerCall);
88+
}
89+
}
90+
}
91+
92+
// Now that all uses are replaced, clean up the globals and metadata.
93+
for (const hlsl::CBufferMapping &Mapping : *CBufMD) {
94+
for (const auto &Member : Mapping.Members) {
95+
Member.GV->eraseFromParent();
96+
}
97+
// Erase the stores to the handle variable before erasing the handle itself.
98+
SmallVector<Instruction *, 4> HandleStores;
99+
for (User *U : Mapping.Handle->users()) {
100+
if (auto *SI = dyn_cast<StoreInst>(U)) {
101+
HandleStores.push_back(SI);
102+
}
103+
}
104+
for (Instruction *I : HandleStores) {
105+
I->eraseFromParent();
106+
}
107+
Mapping.Handle->eraseFromParent();
108+
}
109+
110+
CBufMD->eraseFromModule();
111+
return true;
112+
}
113+
114+
PreservedAnalyses SPIRVCBufferAccess::run(Module &M,
115+
ModuleAnalysisManager &AM) {
116+
if (replaceCBufferAccesses(M)) {
117+
return PreservedAnalyses::none();
118+
}
119+
return PreservedAnalyses::all();
120+
}
121+
122+
namespace {
123+
class SPIRVCBufferAccessLegacy : public ModulePass {
124+
public:
125+
bool runOnModule(Module &M) override { return replaceCBufferAccesses(M); }
126+
StringRef getPassName() const override { return "SPIRV CBuffer Access"; }
127+
SPIRVCBufferAccessLegacy() : ModulePass(ID) {}
128+
129+
static char ID; // Pass identification.
130+
};
131+
char SPIRVCBufferAccessLegacy::ID = 0;
132+
} // end anonymous namespace
133+
134+
INITIALIZE_PASS(SPIRVCBufferAccessLegacy, DEBUG_TYPE, "SPIRV CBuffer Access",
135+
false, false)
136+
137+
ModulePass *llvm::createSPIRVCBufferAccessLegacyPass() {
138+
return new SPIRVCBufferAccessLegacy();
139+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
//===- SPIRVCBufferAccess.h - 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-exception
6+
//
7+
8+
#ifndef LLVM_LIB_TARGET_SPIRV_SPIRVCBUFFERACCESS_H_
9+
#define LLVM_LIB_TARGET_SPIRV_SPIRVCBUFFERACCESS_H_
10+
11+
#include "llvm/IR/PassManager.h"
12+
13+
namespace llvm {
14+
15+
class SPIRVCBufferAccess : public PassInfoMixin<SPIRVCBufferAccess> {
16+
public:
17+
PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM);
18+
};
19+
20+
} // namespace llvm
21+
22+
#endif // LLVM_LIB_TARGET_SPIRV_SPIRVCBUFFERACCESS_H_

llvm/lib/Target/SPIRV/SPIRVPassRegistry.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@
1313

1414
// NOTE: NO INCLUDE GUARD DESIRED!
1515

16+
#ifndef MODULE_PASS
17+
#define MODULE_PASS(NAME, CREATE_PASS)
18+
#endif
19+
MODULE_PASS("spirv-cbuffer-access", SPIRVCBufferAccess())
20+
#undef MODULE_PASS
1621

1722
#ifndef FUNCTION_PASS
1823
#define FUNCTION_PASS(NAME, CREATE_PASS)

llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#include "SPIRVTargetMachine.h"
1414
#include "SPIRV.h"
15+
#include "SPIRVCBufferAccess.h"
1516
#include "SPIRVGlobalRegistry.h"
1617
#include "SPIRVLegalizerInfo.h"
1718
#include "SPIRVStructurizerWrapper.h"
@@ -48,6 +49,7 @@ extern "C" LLVM_ABI LLVM_EXTERNAL_VISIBILITY void LLVMInitializeSPIRVTarget() {
4849
initializeSPIRVAsmPrinterPass(PR);
4950
initializeSPIRVConvergenceRegionAnalysisWrapperPassPass(PR);
5051
initializeSPIRVStructurizerPass(PR);
52+
initializeSPIRVCBufferAccessLegacyPass(PR);
5153
initializeSPIRVPreLegalizerCombinerPass(PR);
5254
initializeSPIRVLegalizePointerCastPass(PR);
5355
initializeSPIRVRegularizerPass(PR);
@@ -206,6 +208,7 @@ void SPIRVPassConfig::addISelPrepare() {
206208

207209
addPass(createSPIRVStripConvergenceIntrinsicsPass());
208210
addPass(createSPIRVLegalizeImplicitBindingPass());
211+
addPass(createSPIRVCBufferAccessLegacyPass());
209212
addPass(createSPIRVEmitIntrinsicsPass(&getTM<SPIRVTargetMachine>()));
210213
if (TM.getSubtargetImpl()->isLogicalSPIRV())
211214
addPass(createSPIRVLegalizePointerCastPass(&getTM<SPIRVTargetMachine>()));
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
; RUN: llc -O0 -verify-machineinstrs -mtriple=spirv1.6-vulkan1.3-library %s -o - | FileCheck %s
2+
; Test that uses of cbuffer members inside ConstantExprs are handled correctly.
3+
4+
; CHECK-DAG: OpDecorate %[[MyCBuffer:[0-9]+]] DescriptorSet 0
5+
; CHECK-DAG: OpDecorate %[[MyCBuffer]] Binding 0
6+
; CHECK-DAG: OpMemberDecorate %[[__cblayout_MyCBuffer:[0-9]+]] 0 Offset 0
7+
; CHECK-DAG: OpMemberDecorate %[[__cblayout_MyCBuffer]] 1 Offset 16
8+
; CHECK-DAG: %[[uint:[0-9]+]] = OpTypeInt 32 0
9+
; CHECK-DAG: %[[uint_0:[0-9]+]] = OpConstant %[[uint]] 0{{$}}
10+
; CHECK-DAG: %[[uint_1:[0-9]+]] = OpConstant %[[uint]] 1{{$}}
11+
; CHECK-DAG: %[[float:[0-9]+]] = OpTypeFloat 32
12+
; CHECK-DAG: %[[v4float:[0-9]+]] = OpTypeVector %[[float]] 4
13+
; CHECK-DAG: %[[MyStruct:[0-9]+]] = OpTypeStruct %[[v4float]]
14+
; CHECK-DAG: %[[__cblayout_MyCBuffer]] = OpTypeStruct %[[MyStruct]] %[[v4float]]
15+
; CHECK-DAG: %[[wrapper:[0-9]+]] = OpTypeStruct %[[__cblayout_MyCBuffer]]
16+
; CHECK-DAG: %[[wrapper_ptr_t:[0-9]+]] = OpTypePointer Uniform %[[wrapper]]
17+
; CHECK-DAG: %[[MyCBuffer]] = OpVariable %[[wrapper_ptr_t]] Uniform
18+
; CHECK-DAG: %[[_ptr_Uniform_v4float:[0-9]+]] = OpTypePointer Uniform %[[v4float]]
19+
; CHECK-DAG: %[[_ptr_Uniform_float:[0-9]+]] = OpTypePointer Uniform %[[float]]
20+
21+
%MyStruct = type { <4 x float> }
22+
%__cblayout_MyCBuffer = type <{ %MyStruct, <4 x float> }>
23+
24+
@MyCBuffer.cb = local_unnamed_addr global target("spirv.VulkanBuffer", target("spirv.Layout", %__cblayout_MyCBuffer, 32, 0, 16), 2, 0) poison
25+
@s = external hidden local_unnamed_addr addrspace(12) global %MyStruct, align 16
26+
@v = external hidden local_unnamed_addr addrspace(12) global <4 x float>, align 16
27+
@MyCBuffer.str = private unnamed_addr constant [10 x i8] c"MyCBuffer\00", align 1
28+
@.str = private unnamed_addr constant [7 x i8] c"output\00", align 1
29+
30+
define void @main() {
31+
entry:
32+
; CHECK: %[[tmp:[0-9]+]] = OpCopyObject %[[wrapper_ptr_t]] %[[MyCBuffer]]
33+
%MyCBuffer.cb_h.i.i = tail call target("spirv.VulkanBuffer", target("spirv.Layout", %__cblayout_MyCBuffer, 32, 0, 16), 2, 0) @llvm.spv.resource.handlefrombinding.tspirv.VulkanBuffer_tspirv.Layout_s___cblayout_MyCBuffers_32_0_16t_2_0t(i32 0, i32 0, i32 1, i32 0, ptr nonnull @MyCBuffer.str)
34+
store target("spirv.VulkanBuffer", target("spirv.Layout", %__cblayout_MyCBuffer, 32, 0, 16), 2, 0) %MyCBuffer.cb_h.i.i, ptr @MyCBuffer.cb, align 8
35+
%0 = tail call target("spirv.Image", float, 5, 2, 0, 0, 2, 3) @llvm.spv.resource.handlefrombinding.tspirv.Image_f32_5_2_0_0_2_3t(i32 0, i32 0, i32 1, i32 0, ptr nonnull @.str)
36+
37+
; CHECK: %[[tmp_ptr:[0-9]+]] = OpAccessChain {{%[0-9]+}} %[[tmp]] %[[uint_0]] %[[uint_0]]
38+
; CHECK: %[[v_ptr:.+]] = OpAccessChain %[[_ptr_Uniform_v4float]] %[[tmp]] %[[uint_0]] %[[uint_1]]
39+
; CHECK: %[[s_ptr_gep:[0-9]+]] = OpInBoundsAccessChain %[[_ptr_Uniform_float]] %[[tmp_ptr]] %[[uint_0]] %[[uint_1]]
40+
%gep = getelementptr inbounds %MyStruct, ptr addrspace(12) @s, i32 0, i32 0, i32 1
41+
42+
; CHECK: %[[s_val:.+]] = OpLoad %[[float]] %[[s_ptr_gep]]
43+
%load_from_gep = load float, ptr addrspace(12) %gep, align 4
44+
45+
; CHECK: %[[v_val:.+]] = OpLoad %[[v4float]] %[[v_ptr]]
46+
%load_v = load <4 x float>, ptr addrspace(12) @v, align 16
47+
48+
%extract_v = extractelement <4 x float> %load_v, i64 0
49+
%add = fadd float %load_from_gep, %extract_v
50+
%get_output_ptr = tail call noundef align 4 dereferenceable(4) ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.Image_f32_5_2_0_0_2_3t(target("spirv.Image", float, 5, 2, 0, 0, 2, 3) %0, i32 0)
51+
store float %add, ptr addrspace(11) %get_output_ptr, align 4
52+
ret void
53+
}
54+
55+
!hlsl.cbs = !{!0}
56+
!0 = !{ptr @MyCBuffer.cb, ptr addrspace(12) @s, ptr addrspace(12) @v}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
; RUN: llc -O0 -verify-machineinstrs -mtriple=spirv1.6-vulkan1.3-library %s -o - | FileCheck %s
2+
; Test that unused and partially unused cbuffers are handled correctly.
3+
4+
; CHECK-DAG: OpDecorate %[[PartiallyUsedCBuffer:[0-9]+]] DescriptorSet 0
5+
; CHECK-DAG: OpDecorate %[[PartiallyUsedCBuffer]] Binding 1
6+
; CHECK-DAG: OpDecorate %[[AnotherCBuffer:[0-9]+]] DescriptorSet 0
7+
; CHECK-DAG: OpDecorate %[[AnotherCBuffer]] Binding 2
8+
; CHECK-DAG: %[[float:[0-9]+]] = OpTypeFloat 32
9+
; CHECK-DAG: %[[v4float:[0-9]+]] = OpTypeVector %[[float]] 4
10+
; CHECK-DAG: %[[_cblayout_PartiallyUsedCBuffer:[0-9]+]] = OpTypeStruct %[[float]]
11+
; CHECK-DAG: %[[_cblayout_AnotherCBuffer:[0-9]+]] = OpTypeStruct %[[v4float]]
12+
13+
%__cblayout_UnusedCBuffer = type <{ float }>
14+
%__cblayout_PartiallyUsedCBuffer = type <{ float, i32 }>
15+
%__cblayout_AnotherCBuffer = type <{ <4 x float>, <4 x float> }>
16+
17+
@UnusedCBuffer.cb = local_unnamed_addr global target("spirv.VulkanBuffer", target("spirv.Layout", %__cblayout_UnusedCBuffer, 4, 0), 2, 0) poison
18+
@UnusedCBuffer.str = private unnamed_addr constant [14 x i8] c"UnusedCBuffer\00", align 1
19+
@PartiallyUsedCBuffer.cb = local_unnamed_addr global target("spirv.VulkanBuffer", target("spirv.Layout", %__cblayout_PartiallyUsedCBuffer, 8, 0, 4), 2, 0) poison
20+
@used_member = external hidden local_unnamed_addr addrspace(12) global float, align 4
21+
@PartiallyUsedCBuffer.str = private unnamed_addr constant [21 x i8] c"PartiallyUsedCBuffer\00", align 1
22+
@AnotherCBuffer.cb = local_unnamed_addr global target("spirv.VulkanBuffer", target("spirv.Layout", %__cblayout_AnotherCBuffer, 32, 0, 16), 2, 0) poison
23+
@a = external hidden local_unnamed_addr addrspace(12) global <4 x float>, align 16
24+
@AnotherCBuffer.str = private unnamed_addr constant [15 x i8] c"AnotherCBuffer\00", align 1
25+
@.str = private unnamed_addr constant [7 x i8] c"output\00", align 1
26+
27+
28+
; Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(readwrite, argmem: write, inaccessiblemem: none)
29+
define void @main() local_unnamed_addr #1 {
30+
entry:
31+
%UnusedCBuffer.cb_h.i.i = tail call target("spirv.VulkanBuffer", target("spirv.Layout", %__cblayout_UnusedCBuffer, 4, 0), 2, 0) @llvm.spv.resource.handlefromimplicitbinding.tspirv.VulkanBuffer_tspirv.Layout_s___cblayout_UnusedCBuffers_4_0t_2_0t(i32 0, i32 0, i32 1, i32 0, ptr nonnull @UnusedCBuffer.str)
32+
store target("spirv.VulkanBuffer", target("spirv.Layout", %__cblayout_UnusedCBuffer, 4, 0), 2, 0) %UnusedCBuffer.cb_h.i.i, ptr @UnusedCBuffer.cb, align 8
33+
34+
; CHECK: %[[tmp:[0-9]+]] = OpCopyObject {{%[0-9]+}} %[[PartiallyUsedCBuffer]]
35+
; CHECK: %[[used_member_ptr:.+]] = OpAccessChain %{{.+}} %[[tmp]] %{{.+}} %[[uint_0:[0-9]+]]
36+
%PartiallyUsedCBuffer.cb_h.i.i = tail call target("spirv.VulkanBuffer", target("spirv.Layout", %__cblayout_PartiallyUsedCBuffer, 8, 0, 4), 2, 0) @llvm.spv.resource.handlefromimplicitbinding.tspirv.VulkanBuffer_tspirv.Layout_s___cblayout_PartiallyUsedCBuffers_8_0_4t_2_0t(i32 1, i32 0, i32 1, i32 0, ptr nonnull @PartiallyUsedCBuffer.str)
37+
store target("spirv.VulkanBuffer", target("spirv.Layout", %__cblayout_PartiallyUsedCBuffer, 8, 0, 4), 2, 0) %PartiallyUsedCBuffer.cb_h.i.i, ptr @PartiallyUsedCBuffer.cb, align 8
38+
39+
; CHECK: %[[tmp:[0-9]+]] = OpCopyObject {{%[0-9]+}} %[[AnotherCBuffer]]
40+
; CHECK: %[[a_ptr:.+]] = OpAccessChain %{{.+}} %[[tmp]] %{{.+}} %[[uint_0]]
41+
%AnotherCBuffer.cb_h.i.i = tail call target("spirv.VulkanBuffer", target("spirv.Layout", %__cblayout_AnotherCBuffer, 32, 0, 16), 2, 0) @llvm.spv.resource.handlefromimplicitbinding.tspirv.VulkanBuffer_tspirv.Layout_s___cblayout_AnotherCBuffers_32_0_16t_2_0t(i32 2, i32 0, i32 1, i32 0, ptr nonnull @AnotherCBuffer.str)
42+
store target("spirv.VulkanBuffer", target("spirv.Layout", %__cblayout_AnotherCBuffer, 32, 0, 16), 2, 0) %AnotherCBuffer.cb_h.i.i, ptr @AnotherCBuffer.cb, align 8
43+
%0 = tail call target("spirv.Image", float, 5, 2, 0, 0, 2, 1) @llvm.spv.resource.handlefromimplicitbinding.tspirv.Image_f32_5_2_0_0_2_1t(i32 3, i32 0, i32 1, i32 0, ptr nonnull @.str)
44+
45+
%2 = load float, ptr addrspace(12) @used_member, align 4
46+
%3 = load <4 x float>, ptr addrspace(12) @a, align 16
47+
%4 = extractelement <4 x float> %3, i64 0
48+
%add.i = fadd reassoc nnan ninf nsz arcp afn float %4, %2
49+
%vecinit3.i = insertelement <4 x float> <float poison, float 0.000000e+00, float 0.000000e+00, float 0.000000e+00>, float %add.i, i64 0
50+
%5 = tail call noundef align 16 dereferenceable(16) ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.Image_f32_5_2_0_0_2_1t(target("spirv.Image", float, 5, 2, 0, 0, 2, 1) %0, i32 0)
51+
store <4 x float> %vecinit3.i, ptr addrspace(11) %5, align 16
52+
ret void
53+
}
54+
55+
56+
attributes #1 = { mustprogress nofree noinline norecurse nosync nounwind willreturn memory(readwrite, argmem: write, inaccessiblemem: none) "frame-pointer"="all" "hlsl.numthreads"="1,1,1" "hlsl.shader"="compute" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
57+
58+
!hlsl.cbs = !{!0, !1, !2}
59+
60+
!0 = distinct !{ptr @UnusedCBuffer.cb, null}
61+
!1 = distinct !{ptr @PartiallyUsedCBuffer.cb, ptr addrspace(12) @used_member, null}
62+
!2 = distinct !{ptr @AnotherCBuffer.cb, ptr addrspace(12) @a, null}

0 commit comments

Comments
 (0)