[clang] [llvm] [HLSL] Add resource constructor with implicit binding for global resources (PR #138976)
Helena Kotas via cfe-commits
cfe-commits at lists.llvm.org
Wed May 14 15:10:05 PDT 2025
https://github.com/hekota updated https://github.com/llvm/llvm-project/pull/138976
>From d5685ecc132979e88424d06e81fbefe8d1f6450d Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Thu, 24 Apr 2025 13:27:52 -0700
Subject: [PATCH 01/14] [HLSL] Implement DXILResourceBindingAnalysis
DXILResourceBindingAnalysis analyses all explicit resource bindings in the module
and puts together lists of used virtual register spaces and available virtual
register slot ranges for each binding type. It also stores additional information
found during the analysis such as whether the module uses implicit bindings or if
any of the bindings overlap.
This information will be used in DXILResourceImplicitBindings pass to
assign register slots to resources with implicit bindings, and in a
post-optimization validation pass that will raise diagnostic about
overlapping bindings.
Part 1/2 of #136786
---
llvm/include/llvm/Analysis/DXILResource.h | 105 +++++++
llvm/include/llvm/IR/IntrinsicsDirectX.td | 9 +
llvm/lib/Analysis/DXILResource.cpp | 125 ++++++++
llvm/lib/Passes/PassRegistry.def | 1 +
llvm/unittests/Target/DirectX/CMakeLists.txt | 1 +
.../DirectX/ResourceBindingAnalysisTests.cpp | 268 ++++++++++++++++++
6 files changed, 509 insertions(+)
create mode 100644 llvm/unittests/Target/DirectX/ResourceBindingAnalysisTests.cpp
diff --git a/llvm/include/llvm/Analysis/DXILResource.h b/llvm/include/llvm/Analysis/DXILResource.h
index 1d871a448c16c..5019fc38665ff 100644
--- a/llvm/include/llvm/Analysis/DXILResource.h
+++ b/llvm/include/llvm/Analysis/DXILResource.h
@@ -10,6 +10,7 @@
#define LLVM_ANALYSIS_DXILRESOURCE_H
#include "llvm/ADT/MapVector.h"
+#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/GlobalVariable.h"
@@ -17,6 +18,8 @@
#include "llvm/Pass.h"
#include "llvm/Support/Alignment.h"
#include "llvm/Support/DXILABI.h"
+#include <climits>
+#include <cstdint>
namespace llvm {
class CallInst;
@@ -586,6 +589,108 @@ class DXILResourceWrapperPass : public ModulePass {
ModulePass *createDXILResourceWrapperPassPass();
+//===----------------------------------------------------------------------===//
+
+// DXILResourceBindingsInfo stores the results of DXILResourceBindingAnalysis
+// which analyses all llvm.dx.resource.handlefrombinding calls in the module
+// and puts together lists of used virtual register spaces and available
+// virtual register slot ranges for each binding type.
+// It also stores additional information found during the analysis such as
+// whether the module uses implicit bindings or if any of the bindings overlap.
+//
+// This information will be used in DXILResourceImplicitBindings pass to assign
+// register slots to resources with implicit bindings, and in a
+// post-optimization validation pass that will raise diagnostic about
+// overlapping bindings.
+//
+// For example for these resource bindings:
+//
+// RWBuffer<float> A[10] : register(u3);
+// RWBuffer<float> B[] : register(u5, space2)
+//
+// The analysis result for UAV binding type will look like this:
+//
+// UAVSpaces {
+// ResClass = ResourceClass::UAV,
+// Spaces = {
+// { Space = 0, FreeRanges = {{ 0, 2 }, { 13, UINT32_MAX }} },
+// { Space = 2, FreeRanges = {{ 0, 4 }} }
+// }
+// }
+//
+class DXILResourceBindingsInfo {
+public:
+ struct BindingRange {
+ uint32_t LowerBound;
+ uint32_t UpperBound;
+ BindingRange(uint32_t LB, uint32_t UB) : LowerBound(LB), UpperBound(UB) {}
+ };
+
+ struct RegisterSpace {
+ uint32_t Space;
+ SmallVector<BindingRange> FreeRanges;
+ RegisterSpace(uint32_t Space) : Space(Space) {
+ FreeRanges.emplace_back(0, UINT32_MAX);
+ }
+ };
+
+ struct BindingSpaces {
+ dxil::ResourceClass ResClass;
+ llvm::SmallVector<RegisterSpace> Spaces;
+ BindingSpaces(dxil::ResourceClass ResClass) : ResClass(ResClass) {
+ // initialize space0
+ Spaces.emplace_back(0);
+ }
+ };
+
+private:
+ BindingSpaces SRVSpaces, UAVSpaces, CBufferSpaces, SamplerSpaces;
+ bool ImplicitBinding;
+ bool OverlappingBinding;
+
+ // Populate the resource binding info given explicit resource binding calls
+ // in the module.
+ void populate(Module &M, DXILResourceTypeMap &DRTM);
+
+public:
+ DXILResourceBindingsInfo()
+ : SRVSpaces(dxil::ResourceClass::SRV),
+ UAVSpaces(dxil::ResourceClass::UAV),
+ CBufferSpaces(dxil::ResourceClass::CBuffer),
+ SamplerSpaces(dxil::ResourceClass::Sampler), ImplicitBinding(false),
+ OverlappingBinding(false) {}
+
+ bool containsImplicitBinding() const { return ImplicitBinding; }
+ bool containsOverlappingBinding() const { return OverlappingBinding; }
+
+ BindingSpaces &getBindingSpaces(dxil::ResourceClass RC) {
+ switch (RC) {
+ case dxil::ResourceClass::SRV:
+ return SRVSpaces;
+ case dxil::ResourceClass::UAV:
+ return UAVSpaces;
+ case dxil::ResourceClass::CBuffer:
+ return CBufferSpaces;
+ case dxil::ResourceClass::Sampler:
+ return SamplerSpaces;
+ }
+ }
+
+ friend class DXILResourceBindingAnalysis;
+};
+
+class DXILResourceBindingAnalysis
+ : public AnalysisInfoMixin<DXILResourceBindingAnalysis> {
+ friend AnalysisInfoMixin<DXILResourceBindingAnalysis>;
+
+ static AnalysisKey Key;
+
+public:
+ using Result = DXILResourceBindingsInfo;
+
+ DXILResourceBindingsInfo run(Module &M, ModuleAnalysisManager &AM);
+};
+
} // namespace llvm
#endif // LLVM_ANALYSIS_DXILRESOURCE_H
diff --git a/llvm/include/llvm/IR/IntrinsicsDirectX.td b/llvm/include/llvm/IR/IntrinsicsDirectX.td
index b1a27311e2a9c..444600980fc1e 100644
--- a/llvm/include/llvm/IR/IntrinsicsDirectX.td
+++ b/llvm/include/llvm/IR/IntrinsicsDirectX.td
@@ -27,6 +27,15 @@ def int_dx_resource_handlefrombinding
[llvm_i32_ty, llvm_i32_ty, llvm_i32_ty, llvm_i32_ty, llvm_i1_ty],
[IntrNoMem]>;
+// Create resource handle with implicit binding in given register space.
+// Returns a `target("dx.")` type appropriate for the kind of resource and
+// the range size and index of the binding.
+def int_dx_resource_handlefromimplicitbinding
+ : DefaultAttrsIntrinsic<
+ [llvm_any_ty],
+ [llvm_i32_ty, llvm_i32_ty, llvm_i32_ty, llvm_i32_ty],
+ [IntrNoMem]>;
+
def int_dx_resource_getpointer
: DefaultAttrsIntrinsic<[llvm_anyptr_ty], [llvm_any_ty, llvm_i32_ty],
[IntrNoMem]>;
diff --git a/llvm/lib/Analysis/DXILResource.cpp b/llvm/lib/Analysis/DXILResource.cpp
index cc1d931c9e077..1ca73ce81789a 100644
--- a/llvm/lib/Analysis/DXILResource.cpp
+++ b/llvm/lib/Analysis/DXILResource.cpp
@@ -8,7 +8,9 @@
#include "llvm/Analysis/DXILResource.h"
#include "llvm/ADT/APInt.h"
+#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/SmallVector.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/DiagnosticInfo.h"
@@ -19,6 +21,8 @@
#include "llvm/IR/Module.h"
#include "llvm/InitializePasses.h"
#include "llvm/Support/FormatVariadic.h"
+#include <climits>
+#include <cstdint>
#define DEBUG_TYPE "dxil-resource"
@@ -879,8 +883,121 @@ SmallVector<dxil::ResourceInfo *> DXILResourceMap::findByUse(const Value *Key) {
//===----------------------------------------------------------------------===//
+void DXILResourceBindingsInfo::populate(Module &M, DXILResourceTypeMap &DRTM) {
+ struct Binding {
+ ResourceClass ResClass;
+ uint32_t Space;
+ uint32_t LowerBound;
+ uint32_t UpperBound;
+ Binding(ResourceClass RC, uint32_t Sp, uint32_t LB, uint32_t UB)
+ : ResClass(RC), Space(Sp), LowerBound(LB), UpperBound(UB) {}
+ };
+ SmallVector<Binding> Bindings;
+
+ // collect all of the llvm.dx.resource.handlefrombinding calls;
+ // make a note if there is llvm.dx.resource.handlefromimplicitbinding
+ for (Function &F : M.functions()) {
+ if (!F.isDeclaration())
+ continue;
+
+ switch (F.getIntrinsicID()) {
+ default:
+ continue;
+ case Intrinsic::dx_resource_handlefrombinding: {
+ auto *HandleTy = cast<TargetExtType>(F.getReturnType());
+ ResourceTypeInfo &RTI = DRTM[HandleTy];
+
+ for (User *U : F.users())
+ if (CallInst *CI = dyn_cast<CallInst>(U)) {
+ uint32_t Space =
+ cast<ConstantInt>(CI->getArgOperand(0))->getZExtValue();
+ uint32_t LowerBound =
+ cast<ConstantInt>(CI->getArgOperand(1))->getZExtValue();
+ int32_t Size =
+ cast<ConstantInt>(CI->getArgOperand(2))->getZExtValue();
+
+ // negative size means unbounded resource array;
+ // upper bound register overflow should be detected in Sema
+ assert((Size < 0 || (unsigned)LowerBound + Size - 1 <= UINT32_MAX) &&
+ "upper bound register overflow");
+ uint32_t UpperBound = Size < 0 ? UINT32_MAX : LowerBound + Size - 1;
+ Bindings.emplace_back(RTI.getResourceClass(), Space, LowerBound,
+ UpperBound);
+ }
+ break;
+ }
+ case Intrinsic::dx_resource_handlefromimplicitbinding: {
+ if (!F.user_empty())
+ ImplicitBinding = true;
+ break;
+ }
+ }
+ }
+
+ // sort all the collected bindings
+ llvm::stable_sort(Bindings, [](auto &LHS, auto &RHS) {
+ return std::tie(LHS.ResClass, LHS.Space, LHS.LowerBound) <
+ std::tie(RHS.ResClass, RHS.Space, RHS.LowerBound);
+ });
+
+ // remove duplicates
+ llvm::unique(Bindings, [](auto &LHS, auto &RHS) {
+ return std::tie(LHS.ResClass, LHS.Space, LHS.LowerBound, LHS.UpperBound) ==
+ std::tie(RHS.ResClass, RHS.Space, RHS.LowerBound, RHS.UpperBound);
+ });
+
+ // Go over the sorted bindings and build up lists of free register ranges
+ // for each binding type and used spaces. Bindings are sorted by resource
+ // class, space, and lower bound register slot.
+ BindingSpaces *BS = &SRVSpaces;
+ for (unsigned I = 0, E = Bindings.size(); I != E; ++I) {
+ Binding &B = Bindings[I];
+
+ if (BS->ResClass != B.ResClass)
+ // move to the next resource class spaces
+ BS = &getBindingSpaces(B.ResClass);
+
+ RegisterSpace *S = &BS->Spaces.back();
+ assert(S->Space <= B.Space && "bindings not sorted correctly?");
+ if (B.Space != S->Space)
+ // add new space
+ S = &BS->Spaces.emplace_back(B.Space);
+
+ // the space is full - set flag to report overlapping binding later
+ if (S->FreeRanges.empty()) {
+ OverlappingBinding = true;
+ continue;
+ }
+
+ // adjust the last free range lower bound, split it in two, or remove it
+ BindingRange &LastFreeRange = S->FreeRanges.back();
+ assert(LastFreeRange.UpperBound == UINT32_MAX);
+ if (LastFreeRange.LowerBound == B.LowerBound) {
+ if (B.UpperBound < UINT32_MAX)
+ LastFreeRange.LowerBound = B.UpperBound + 1;
+ else
+ S->FreeRanges.pop_back();
+
+ } else if (LastFreeRange.LowerBound < B.LowerBound) {
+ LastFreeRange.UpperBound = B.LowerBound - 1;
+ if (B.UpperBound < UINT32_MAX)
+ S->FreeRanges.emplace_back(B.UpperBound + 1, UINT32_MAX);
+ } else {
+ OverlappingBinding = true;
+ if (B.UpperBound < UINT32_MAX)
+ LastFreeRange.LowerBound =
+ std::max(LastFreeRange.LowerBound, B.UpperBound + 1);
+ else
+ S->FreeRanges.pop_back();
+ }
+ }
+}
+
+//===----------------------------------------------------------------------===//
+
AnalysisKey DXILResourceTypeAnalysis::Key;
AnalysisKey DXILResourceAnalysis::Key;
+AnalysisKey DXILResourceBindingAnalysis::Key;
DXILResourceMap DXILResourceAnalysis::run(Module &M,
ModuleAnalysisManager &AM) {
@@ -890,6 +1007,14 @@ DXILResourceMap DXILResourceAnalysis::run(Module &M,
return Data;
}
+DXILResourceBindingsInfo
+DXILResourceBindingAnalysis::run(Module &M, ModuleAnalysisManager &AM) {
+ DXILResourceBindingsInfo Data;
+ DXILResourceTypeMap &DRTM = AM.getResult<DXILResourceTypeAnalysis>(M);
+ Data.populate(M, DRTM);
+ return Data;
+}
+
PreservedAnalyses DXILResourcePrinterPass::run(Module &M,
ModuleAnalysisManager &AM) {
DXILResourceMap &DRM = AM.getResult<DXILResourceAnalysis>(M);
diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def
index d5d1b2173da69..ea792280ed975 100644
--- a/llvm/lib/Passes/PassRegistry.def
+++ b/llvm/lib/Passes/PassRegistry.def
@@ -24,6 +24,7 @@ MODULE_ANALYSIS("ctx-prof-analysis", CtxProfAnalysis())
MODULE_ANALYSIS("dxil-metadata", DXILMetadataAnalysis())
MODULE_ANALYSIS("dxil-resources", DXILResourceAnalysis())
MODULE_ANALYSIS("dxil-resource-type", DXILResourceTypeAnalysis())
+MODULE_ANALYSIS("dxil-resource-bindings", DXILResourceBindingAnalysis())
MODULE_ANALYSIS("inline-advisor", InlineAdvisorAnalysis())
MODULE_ANALYSIS("ir-similarity", IRSimilarityAnalysis())
MODULE_ANALYSIS("last-run-tracking", LastRunTrackingAnalysis())
diff --git a/llvm/unittests/Target/DirectX/CMakeLists.txt b/llvm/unittests/Target/DirectX/CMakeLists.txt
index b1359b37ad521..2c135c1b4b499 100644
--- a/llvm/unittests/Target/DirectX/CMakeLists.txt
+++ b/llvm/unittests/Target/DirectX/CMakeLists.txt
@@ -22,4 +22,5 @@ add_llvm_target_unittest(DirectXTests
PointerTypeAnalysisTests.cpp
UniqueResourceFromUseTests.cpp
RegisterCostTests.cpp
+ ResourceBindingAnalysisTests.cpp
)
diff --git a/llvm/unittests/Target/DirectX/ResourceBindingAnalysisTests.cpp b/llvm/unittests/Target/DirectX/ResourceBindingAnalysisTests.cpp
new file mode 100644
index 0000000000000..3ff091d67a886
--- /dev/null
+++ b/llvm/unittests/Target/DirectX/ResourceBindingAnalysisTests.cpp
@@ -0,0 +1,268 @@
+//===- llvm/unittests/Target/DirectX/ResourceBindingAnalysisTests.cpp -----===//
+//
+// 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 "llvm/ADT/ArrayRef.h"
+#include "llvm/Analysis/DXILResource.h"
+#include "llvm/AsmParser/Parser.h"
+#include "llvm/Passes/PassBuilder.h"
+#include "llvm/Support/DXILABI.h"
+#include "gtest/gtest.h"
+#include <cstdint>
+
+using namespace llvm;
+using namespace llvm::dxil;
+
+namespace {
+class ResourceBindingAnalysisTest : public testing::Test {
+protected:
+ PassBuilder *PB;
+ ModuleAnalysisManager *MAM;
+ LLVMContext *Context;
+
+ virtual void SetUp() {
+ PB = new PassBuilder();
+ MAM = new ModuleAnalysisManager();
+ Context = new LLVMContext();
+ PB->registerModuleAnalyses(*MAM);
+ MAM->registerPass([&] { return DXILResourceBindingAnalysis(); });
+ }
+
+ std::unique_ptr<Module> parseAsm(StringRef Asm) {
+ SMDiagnostic Error;
+ std::unique_ptr<Module> M = parseAssemblyString(Asm, Error, *Context);
+ EXPECT_TRUE(M) << "Bad assembly?: " << Error.getMessage();
+ return M;
+ }
+
+ virtual void TearDown() {
+ delete PB;
+ delete MAM;
+ delete Context;
+ }
+
+ void checkExpectedSpaceAndFreeRanges(
+ DXILResourceBindingsInfo::RegisterSpace &RegSpace, uint32_t ExpSpace,
+ ArrayRef<uint32_t> ExpValues) {
+ EXPECT_EQ(RegSpace.Space, ExpSpace);
+ EXPECT_EQ(RegSpace.FreeRanges.size() * 2, ExpValues.size());
+ unsigned I = 0;
+ for (auto &R : RegSpace.FreeRanges) {
+ EXPECT_EQ(R.LowerBound, ExpValues[I]);
+ EXPECT_EQ(R.UpperBound, ExpValues[I + 1]);
+ I += 2;
+ }
+ }
+};
+
+TEST_F(ResourceBindingAnalysisTest, TestTrivialCase) {
+ // RWBuffer<float> Buf : register(u5);
+ StringRef Assembly = R"(
+define void @main() {
+entry:
+ %handle = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding(i32 0, i32 5, i32 1, i32 0, i1 false)
+
+ call void @a.func(target("dx.TypedBuffer", float, 1, 0, 0) %handle)
+ ret void
+}
+
+declare void @a.func(target("dx.RawBuffer", float, 1, 0) %handle)
+ )";
+
+ auto M = parseAsm(Assembly);
+
+ DXILResourceBindingsInfo &DRBI =
+ MAM->getResult<DXILResourceBindingAnalysis>(*M);
+
+ EXPECT_EQ(false, DRBI.containsImplicitBinding());
+ EXPECT_EQ(false, DRBI.containsOverlappingBinding());
+
+ // check that UAV has exactly one gap
+ DXILResourceBindingsInfo::BindingSpaces &UAVSpaces =
+ DRBI.getBindingSpaces(ResourceClass::UAV);
+ EXPECT_EQ(UAVSpaces.ResClass, ResourceClass::UAV);
+ EXPECT_EQ(UAVSpaces.Spaces.size(), 1u);
+ checkExpectedSpaceAndFreeRanges(UAVSpaces.Spaces[0], 0,
+ {0, 4, 6, UINT32_MAX});
+
+ // check that other kinds of register spaces are all available
+ for (auto RC :
+ {ResourceClass::SRV, ResourceClass::CBuffer, ResourceClass::Sampler}) {
+ DXILResourceBindingsInfo::BindingSpaces &Spaces = DRBI.getBindingSpaces(RC);
+ EXPECT_EQ(Spaces.ResClass, RC);
+ EXPECT_EQ(Spaces.Spaces.size(), 1u);
+ checkExpectedSpaceAndFreeRanges(Spaces.Spaces[0], 0, {0, UINT32_MAX});
+ }
+}
+
+TEST_F(ResourceBindingAnalysisTest, TestManyBindings) {
+ // cbuffer CB : register(b3) { int a; }
+ // RWBuffer<float4> A[5] : register(u10, space20);
+ // StructuredBuffer<int> B : register(t5);
+ // RWBuffer<float> C : register(u5);
+ // StructuredBuffer<int> D[5] : register(t0);
+ // RWBuffer<float> E[2] : register(u2);
+ StringRef Assembly = R"(
+%__cblayout_CB = type <{ i32 }>
+define void @main() {
+entry:
+ %handleCB = call target("dx.CBuffer", target("dx.Layout", %__cblayout_CB, 4, 0)) @llvm.dx.resource.handlefrombinding(i32 0, i32 3, i32 1, i32 0, i1 false)
+ %handleA = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding(i32 20, i32 10, i32 5, i32 0, i1 false)
+ %handleB = call target("dx.RawBuffer", i32, 0, 0) @llvm.dx.resource.handlefrombinding(i32 0, i32 5, i32 1, i32 0, i1 false)
+ %handleC = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding(i32 0, i32 5, i32 1, i32 0, i1 false)
+ %handleD = call target("dx.RawBuffer", i32, 0, 0) @llvm.dx.resource.handlefrombinding(i32 0, i32 0, i32 5, i32 4, i1 false)
+ %handleE = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding(i32 0, i32 2, i32 2, i32 0, i1 false)
+
+ call void @a.func(target("dx.CBuffer", target("dx.Layout", %__cblayout_CB, 4, 0)) %handleCB)
+ call void @a.func(target("dx.TypedBuffer", float, 1, 0, 0) %handleA)
+ call void @a.func(target("dx.TypedBuffer", float, 1, 0, 0) %handleC)
+ call void @a.func(target("dx.TypedBuffer", float, 1, 0, 0) %handleE)
+ call void @a.func(target("dx.RawBuffer", i32, 0, 0) %handleB)
+ call void @a.func(target("dx.RawBuffer", i32, 0, 0) %handleD)
+
+ ret void
+}
+
+declare void @a.func(target("dx.RawBuffer", float, 1, 0) %handle)
+ )";
+
+ auto M = parseAsm(Assembly);
+
+ DXILResourceBindingsInfo &DRBI =
+ MAM->getResult<DXILResourceBindingAnalysis>(*M);
+
+ EXPECT_EQ(false, DRBI.containsImplicitBinding());
+ EXPECT_EQ(false, DRBI.containsOverlappingBinding());
+
+ DXILResourceBindingsInfo::BindingSpaces &SRVSpaces =
+ DRBI.getBindingSpaces(ResourceClass::SRV);
+ EXPECT_EQ(SRVSpaces.ResClass, ResourceClass::SRV);
+ EXPECT_EQ(SRVSpaces.Spaces.size(), 1u);
+ checkExpectedSpaceAndFreeRanges(SRVSpaces.Spaces[0], 0, {6, UINT32_MAX});
+
+ DXILResourceBindingsInfo::BindingSpaces &UAVSpaces =
+ DRBI.getBindingSpaces(ResourceClass::UAV);
+ EXPECT_EQ(UAVSpaces.ResClass, ResourceClass::UAV);
+ EXPECT_EQ(UAVSpaces.Spaces.size(), 2u);
+ checkExpectedSpaceAndFreeRanges(UAVSpaces.Spaces[0], 0,
+ {0, 1, 4, 4, 6, UINT32_MAX});
+ checkExpectedSpaceAndFreeRanges(UAVSpaces.Spaces[1], 20,
+ {0, 9, 15, UINT32_MAX});
+
+ DXILResourceBindingsInfo::BindingSpaces &CBufferSpaces =
+ DRBI.getBindingSpaces(ResourceClass::CBuffer);
+ EXPECT_EQ(CBufferSpaces.ResClass, ResourceClass::CBuffer);
+ EXPECT_EQ(CBufferSpaces.Spaces.size(), 1u);
+ checkExpectedSpaceAndFreeRanges(CBufferSpaces.Spaces[0], 0,
+ {0, 2, 4, UINT32_MAX});
+}
+
+TEST_F(ResourceBindingAnalysisTest, TestUnboundedAndOverlap) {
+ // StructuredBuffer<float> A[] : register(t5);
+ // StructuredBuffer<float> B[3] : register(t0);
+ // StructuredBuffer<float> C[] : register(t0, space2);
+ // StructuredBuffer<float> D : register(t4, space2); /* overlapping */
+ StringRef Assembly = R"(
+%__cblayout_CB = type <{ i32 }>
+define void @main() {
+entry:
+ %handleA = call target("dx.RawBuffer", float, 0, 0) @llvm.dx.resource.handlefrombinding(i32 0, i32 5, i32 -1, i32 10, i1 false)
+ %handleB = call target("dx.RawBuffer", float, 0, 0) @llvm.dx.resource.handlefrombinding(i32 0, i32 0, i32 3, i32 0, i1 false)
+ %handleC = call target("dx.RawBuffer", float, 0, 0) @llvm.dx.resource.handlefrombinding(i32 2, i32 0, i32 -1, i32 100, i1 false)
+ %handleD = call target("dx.RawBuffer", float, 0, 0) @llvm.dx.resource.handlefrombinding(i32 2, i32 4, i32 1, i32 0, i1 false)
+
+ call void @a.func(target("dx.RawBuffer", float, 0, 0) %handleA)
+ call void @a.func(target("dx.RawBuffer", float, 0, 0) %handleB)
+ call void @a.func(target("dx.RawBuffer", float, 0, 0) %handleC)
+ call void @a.func(target("dx.RawBuffer", float, 0, 0) %handleD)
+
+ ret void
+}
+
+declare void @a.func(target("dx.RawBuffer", float, 0, 0) %handle)
+ )";
+
+ auto M = parseAsm(Assembly);
+
+ DXILResourceBindingsInfo &DRBI =
+ MAM->getResult<DXILResourceBindingAnalysis>(*M);
+
+ EXPECT_EQ(false, DRBI.containsImplicitBinding());
+ EXPECT_EQ(true, DRBI.containsOverlappingBinding());
+
+ DXILResourceBindingsInfo::BindingSpaces &SRVSpaces =
+ DRBI.getBindingSpaces(ResourceClass::SRV);
+ EXPECT_EQ(SRVSpaces.ResClass, ResourceClass::SRV);
+ EXPECT_EQ(SRVSpaces.Spaces.size(), 2u);
+ checkExpectedSpaceAndFreeRanges(SRVSpaces.Spaces[0], 0, {3, 4});
+ checkExpectedSpaceAndFreeRanges(SRVSpaces.Spaces[1], 2, {});
+}
+
+TEST_F(ResourceBindingAnalysisTest, TestEndOfRange) {
+ // RWBuffer<float> A : register(u4294967295); /* UINT32_MAX */
+ // RWBuffer<float> B[10] : register(u4294967286, space1);
+ // /* range (UINT32_MAX - 9, UINT32_MAX )*/
+ // RWBuffer<float> C[10] : register(u2147483647, space2);
+ // /* range (INT32_MAX, INT32_MAX + 9) */
+ StringRef Assembly = R"(
+%__cblayout_CB = type <{ i32 }>
+define void @main() {
+entry:
+ %handleA = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding(i32 0, i32 -1, i32 1, i32 0, i1 false)
+ %handleB = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding(i32 1, i32 -10, i32 10, i32 50, i1 false)
+ %handleC = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding(i32 2, i32 2147483647, i32 10, i32 100, i1 false)
+
+ call void @a.func(target("dx.TypedBuffer", float, 1, 0, 0) %handleA)
+ call void @a.func(target("dx.TypedBuffer", float, 1, 0, 0) %handleB)
+ call void @a.func(target("dx.TypedBuffer", float, 1, 0, 0) %handleC)
+
+ ret void
+}
+
+declare void @a.func(target("dx.TypedBuffer", float, 1, 0, 0) %handle)
+ )";
+
+ auto M = parseAsm(Assembly);
+
+ DXILResourceBindingsInfo &DRBI =
+ MAM->getResult<DXILResourceBindingAnalysis>(*M);
+
+ EXPECT_EQ(false, DRBI.containsImplicitBinding());
+ EXPECT_EQ(false, DRBI.containsOverlappingBinding());
+
+ DXILResourceBindingsInfo::BindingSpaces &UAVSpaces =
+ DRBI.getBindingSpaces(ResourceClass::UAV);
+ EXPECT_EQ(UAVSpaces.ResClass, ResourceClass::UAV);
+ EXPECT_EQ(UAVSpaces.Spaces.size(), 3u);
+ checkExpectedSpaceAndFreeRanges(UAVSpaces.Spaces[0], 0, {0, UINT32_MAX - 1});
+ checkExpectedSpaceAndFreeRanges(UAVSpaces.Spaces[1], 1, {0, UINT32_MAX - 10});
+ checkExpectedSpaceAndFreeRanges(
+ UAVSpaces.Spaces[2], 2,
+ {0, (uint32_t)INT32_MAX - 1, (uint32_t)INT32_MAX + 10, UINT32_MAX});
+}
+
+TEST_F(ResourceBindingAnalysisTest, TestImplicitFlag) {
+ // RWBuffer<float> A;
+ StringRef Assembly = R"(
+%__cblayout_CB = type <{ i32 }>
+define void @main() {
+entry:
+ %handleA = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefromimplicitbinding(i32 0, i32 0, i32 1, i32 0)
+ call void @a.func(target("dx.TypedBuffer", float, 1, 0, 0) %handleA)
+ ret void
+}
+declare void @a.func(target("dx.TypedBuffer", float, 1, 0, 0) %handle)
+ )";
+
+ auto M = parseAsm(Assembly);
+
+ DXILResourceBindingsInfo &DRBI =
+ MAM->getResult<DXILResourceBindingAnalysis>(*M);
+ EXPECT_EQ(true, DRBI.containsImplicitBinding());
+}
+
+} // namespace
>From d8a4a3f91efecebb71b12dff1cef3952bc705390 Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Fri, 25 Apr 2025 13:39:35 -0700
Subject: [PATCH 02/14] Rename DXILResourceBindingsInfo to
DXILResourceBindingInfo
---
llvm/include/llvm/Analysis/DXILResource.h | 10 +++----
llvm/lib/Analysis/DXILResource.cpp | 6 ++---
.../DirectX/ResourceBindingAnalysisTests.cpp | 26 +++++++++----------
3 files changed, 21 insertions(+), 21 deletions(-)
diff --git a/llvm/include/llvm/Analysis/DXILResource.h b/llvm/include/llvm/Analysis/DXILResource.h
index 5019fc38665ff..475249994c4f3 100644
--- a/llvm/include/llvm/Analysis/DXILResource.h
+++ b/llvm/include/llvm/Analysis/DXILResource.h
@@ -591,7 +591,7 @@ ModulePass *createDXILResourceWrapperPassPass();
//===----------------------------------------------------------------------===//
-// DXILResourceBindingsInfo stores the results of DXILResourceBindingAnalysis
+// DXILResourceBindingInfo stores the results of DXILResourceBindingAnalysis
// which analyses all llvm.dx.resource.handlefrombinding calls in the module
// and puts together lists of used virtual register spaces and available
// virtual register slot ranges for each binding type.
@@ -618,7 +618,7 @@ ModulePass *createDXILResourceWrapperPassPass();
// }
// }
//
-class DXILResourceBindingsInfo {
+class DXILResourceBindingInfo {
public:
struct BindingRange {
uint32_t LowerBound;
@@ -653,7 +653,7 @@ class DXILResourceBindingsInfo {
void populate(Module &M, DXILResourceTypeMap &DRTM);
public:
- DXILResourceBindingsInfo()
+ DXILResourceBindingInfo()
: SRVSpaces(dxil::ResourceClass::SRV),
UAVSpaces(dxil::ResourceClass::UAV),
CBufferSpaces(dxil::ResourceClass::CBuffer),
@@ -686,9 +686,9 @@ class DXILResourceBindingAnalysis
static AnalysisKey Key;
public:
- using Result = DXILResourceBindingsInfo;
+ using Result = DXILResourceBindingInfo;
- DXILResourceBindingsInfo run(Module &M, ModuleAnalysisManager &AM);
+ DXILResourceBindingInfo run(Module &M, ModuleAnalysisManager &AM);
};
} // namespace llvm
diff --git a/llvm/lib/Analysis/DXILResource.cpp b/llvm/lib/Analysis/DXILResource.cpp
index 1ca73ce81789a..c55d70b2ef12d 100644
--- a/llvm/lib/Analysis/DXILResource.cpp
+++ b/llvm/lib/Analysis/DXILResource.cpp
@@ -883,7 +883,7 @@ SmallVector<dxil::ResourceInfo *> DXILResourceMap::findByUse(const Value *Key) {
//===----------------------------------------------------------------------===//
-void DXILResourceBindingsInfo::populate(Module &M, DXILResourceTypeMap &DRTM) {
+void DXILResourceBindingInfo::populate(Module &M, DXILResourceTypeMap &DRTM) {
struct Binding {
ResourceClass ResClass;
uint32_t Space;
@@ -1007,9 +1007,9 @@ DXILResourceMap DXILResourceAnalysis::run(Module &M,
return Data;
}
-DXILResourceBindingsInfo
+DXILResourceBindingInfo
DXILResourceBindingAnalysis::run(Module &M, ModuleAnalysisManager &AM) {
- DXILResourceBindingsInfo Data;
+ DXILResourceBindingInfo Data;
DXILResourceTypeMap &DRTM = AM.getResult<DXILResourceTypeAnalysis>(M);
Data.populate(M, DRTM);
return Data;
diff --git a/llvm/unittests/Target/DirectX/ResourceBindingAnalysisTests.cpp b/llvm/unittests/Target/DirectX/ResourceBindingAnalysisTests.cpp
index 3ff091d67a886..bfc4955c51251 100644
--- a/llvm/unittests/Target/DirectX/ResourceBindingAnalysisTests.cpp
+++ b/llvm/unittests/Target/DirectX/ResourceBindingAnalysisTests.cpp
@@ -46,7 +46,7 @@ class ResourceBindingAnalysisTest : public testing::Test {
}
void checkExpectedSpaceAndFreeRanges(
- DXILResourceBindingsInfo::RegisterSpace &RegSpace, uint32_t ExpSpace,
+ DXILResourceBindingInfo::RegisterSpace &RegSpace, uint32_t ExpSpace,
ArrayRef<uint32_t> ExpValues) {
EXPECT_EQ(RegSpace.Space, ExpSpace);
EXPECT_EQ(RegSpace.FreeRanges.size() * 2, ExpValues.size());
@@ -75,14 +75,14 @@ declare void @a.func(target("dx.RawBuffer", float, 1, 0) %handle)
auto M = parseAsm(Assembly);
- DXILResourceBindingsInfo &DRBI =
+ DXILResourceBindingInfo &DRBI =
MAM->getResult<DXILResourceBindingAnalysis>(*M);
EXPECT_EQ(false, DRBI.containsImplicitBinding());
EXPECT_EQ(false, DRBI.containsOverlappingBinding());
// check that UAV has exactly one gap
- DXILResourceBindingsInfo::BindingSpaces &UAVSpaces =
+ DXILResourceBindingInfo::BindingSpaces &UAVSpaces =
DRBI.getBindingSpaces(ResourceClass::UAV);
EXPECT_EQ(UAVSpaces.ResClass, ResourceClass::UAV);
EXPECT_EQ(UAVSpaces.Spaces.size(), 1u);
@@ -92,7 +92,7 @@ declare void @a.func(target("dx.RawBuffer", float, 1, 0) %handle)
// check that other kinds of register spaces are all available
for (auto RC :
{ResourceClass::SRV, ResourceClass::CBuffer, ResourceClass::Sampler}) {
- DXILResourceBindingsInfo::BindingSpaces &Spaces = DRBI.getBindingSpaces(RC);
+ DXILResourceBindingInfo::BindingSpaces &Spaces = DRBI.getBindingSpaces(RC);
EXPECT_EQ(Spaces.ResClass, RC);
EXPECT_EQ(Spaces.Spaces.size(), 1u);
checkExpectedSpaceAndFreeRanges(Spaces.Spaces[0], 0, {0, UINT32_MAX});
@@ -132,19 +132,19 @@ declare void @a.func(target("dx.RawBuffer", float, 1, 0) %handle)
auto M = parseAsm(Assembly);
- DXILResourceBindingsInfo &DRBI =
+ DXILResourceBindingInfo &DRBI =
MAM->getResult<DXILResourceBindingAnalysis>(*M);
EXPECT_EQ(false, DRBI.containsImplicitBinding());
EXPECT_EQ(false, DRBI.containsOverlappingBinding());
- DXILResourceBindingsInfo::BindingSpaces &SRVSpaces =
+ DXILResourceBindingInfo::BindingSpaces &SRVSpaces =
DRBI.getBindingSpaces(ResourceClass::SRV);
EXPECT_EQ(SRVSpaces.ResClass, ResourceClass::SRV);
EXPECT_EQ(SRVSpaces.Spaces.size(), 1u);
checkExpectedSpaceAndFreeRanges(SRVSpaces.Spaces[0], 0, {6, UINT32_MAX});
- DXILResourceBindingsInfo::BindingSpaces &UAVSpaces =
+ DXILResourceBindingInfo::BindingSpaces &UAVSpaces =
DRBI.getBindingSpaces(ResourceClass::UAV);
EXPECT_EQ(UAVSpaces.ResClass, ResourceClass::UAV);
EXPECT_EQ(UAVSpaces.Spaces.size(), 2u);
@@ -153,7 +153,7 @@ declare void @a.func(target("dx.RawBuffer", float, 1, 0) %handle)
checkExpectedSpaceAndFreeRanges(UAVSpaces.Spaces[1], 20,
{0, 9, 15, UINT32_MAX});
- DXILResourceBindingsInfo::BindingSpaces &CBufferSpaces =
+ DXILResourceBindingInfo::BindingSpaces &CBufferSpaces =
DRBI.getBindingSpaces(ResourceClass::CBuffer);
EXPECT_EQ(CBufferSpaces.ResClass, ResourceClass::CBuffer);
EXPECT_EQ(CBufferSpaces.Spaces.size(), 1u);
@@ -188,13 +188,13 @@ declare void @a.func(target("dx.RawBuffer", float, 0, 0) %handle)
auto M = parseAsm(Assembly);
- DXILResourceBindingsInfo &DRBI =
+ DXILResourceBindingInfo &DRBI =
MAM->getResult<DXILResourceBindingAnalysis>(*M);
EXPECT_EQ(false, DRBI.containsImplicitBinding());
EXPECT_EQ(true, DRBI.containsOverlappingBinding());
- DXILResourceBindingsInfo::BindingSpaces &SRVSpaces =
+ DXILResourceBindingInfo::BindingSpaces &SRVSpaces =
DRBI.getBindingSpaces(ResourceClass::SRV);
EXPECT_EQ(SRVSpaces.ResClass, ResourceClass::SRV);
EXPECT_EQ(SRVSpaces.Spaces.size(), 2u);
@@ -228,13 +228,13 @@ declare void @a.func(target("dx.TypedBuffer", float, 1, 0, 0) %handle)
auto M = parseAsm(Assembly);
- DXILResourceBindingsInfo &DRBI =
+ DXILResourceBindingInfo &DRBI =
MAM->getResult<DXILResourceBindingAnalysis>(*M);
EXPECT_EQ(false, DRBI.containsImplicitBinding());
EXPECT_EQ(false, DRBI.containsOverlappingBinding());
- DXILResourceBindingsInfo::BindingSpaces &UAVSpaces =
+ DXILResourceBindingInfo::BindingSpaces &UAVSpaces =
DRBI.getBindingSpaces(ResourceClass::UAV);
EXPECT_EQ(UAVSpaces.ResClass, ResourceClass::UAV);
EXPECT_EQ(UAVSpaces.Spaces.size(), 3u);
@@ -260,7 +260,7 @@ declare void @a.func(target("dx.TypedBuffer", float, 1, 0, 0) %handle)
auto M = parseAsm(Assembly);
- DXILResourceBindingsInfo &DRBI =
+ DXILResourceBindingInfo &DRBI =
MAM->getResult<DXILResourceBindingAnalysis>(*M);
EXPECT_EQ(true, DRBI.containsImplicitBinding());
}
>From c718763b4343390c50f7179cc1abc3fefe260dfd Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Fri, 25 Apr 2025 19:02:52 -0700
Subject: [PATCH 03/14] Add wrapper pass
---
llvm/include/llvm/Analysis/DXILResource.h | 20 +++++++++++++++
llvm/include/llvm/InitializePasses.h | 3 ++-
llvm/lib/Analysis/Analysis.cpp | 3 ++-
llvm/lib/Analysis/DXILResource.cpp | 30 +++++++++++++++++++++++
4 files changed, 54 insertions(+), 2 deletions(-)
diff --git a/llvm/include/llvm/Analysis/DXILResource.h b/llvm/include/llvm/Analysis/DXILResource.h
index 1f09e12716248..556c15868290e 100644
--- a/llvm/include/llvm/Analysis/DXILResource.h
+++ b/llvm/include/llvm/Analysis/DXILResource.h
@@ -677,6 +677,7 @@ class DXILResourceBindingInfo {
}
friend class DXILResourceBindingAnalysis;
+ friend class DXILResourceBindingWrapperPass;
};
class DXILResourceBindingAnalysis
@@ -691,6 +692,25 @@ class DXILResourceBindingAnalysis
DXILResourceBindingInfo run(Module &M, ModuleAnalysisManager &AM);
};
+class DXILResourceBindingWrapperPass : public ModulePass {
+ std::unique_ptr<DXILResourceBindingInfo> BindingInfo;
+
+public:
+ static char ID;
+
+ DXILResourceBindingWrapperPass();
+ ~DXILResourceBindingWrapperPass() override;
+
+ DXILResourceBindingInfo &getBindingInfo() { return *BindingInfo; }
+ const DXILResourceBindingInfo &getBindingInfo() const { return *BindingInfo; }
+
+ void getAnalysisUsage(AnalysisUsage &AU) const override;
+ bool runOnModule(Module &M) override;
+ void releaseMemory() override;
+};
+
+ModulePass *createDXILResourceBindingWrapperPassPass();
+
} // namespace llvm
#endif // LLVM_ANALYSIS_DXILRESOURCE_H
diff --git a/llvm/include/llvm/InitializePasses.h b/llvm/include/llvm/InitializePasses.h
index 1ce36a95317b4..b84c6d6123d58 100644
--- a/llvm/include/llvm/InitializePasses.h
+++ b/llvm/include/llvm/InitializePasses.h
@@ -84,8 +84,9 @@ void initializeDAHPass(PassRegistry &);
void initializeDCELegacyPassPass(PassRegistry &);
void initializeDXILMetadataAnalysisWrapperPassPass(PassRegistry &);
void initializeDXILMetadataAnalysisWrapperPrinterPass(PassRegistry &);
-void initializeDXILResourceWrapperPassPass(PassRegistry &);
+void initializeDXILResourceBindingWrapperPassPass(PassRegistry &);
void initializeDXILResourceTypeWrapperPassPass(PassRegistry &);
+void initializeDXILResourceWrapperPassPass(PassRegistry &);
void initializeDeadMachineInstructionElimPass(PassRegistry &);
void initializeDebugifyMachineModulePass(PassRegistry &);
void initializeDependenceAnalysisWrapperPassPass(PassRegistry &);
diff --git a/llvm/lib/Analysis/Analysis.cpp b/llvm/lib/Analysis/Analysis.cpp
index 43ea30f3a9a81..484a456f49f1b 100644
--- a/llvm/lib/Analysis/Analysis.cpp
+++ b/llvm/lib/Analysis/Analysis.cpp
@@ -27,8 +27,9 @@ void llvm::initializeAnalysis(PassRegistry &Registry) {
initializeCallGraphViewerPass(Registry);
initializeCycleInfoWrapperPassPass(Registry);
initializeDXILMetadataAnalysisWrapperPassPass(Registry);
- initializeDXILResourceWrapperPassPass(Registry);
+ initializeDXILResourceBindingWrapperPassPass(Registry);
initializeDXILResourceTypeWrapperPassPass(Registry);
+ initializeDXILResourceWrapperPassPass(Registry);
initializeDependenceAnalysisWrapperPassPass(Registry);
initializeDominanceFrontierWrapperPassPass(Registry);
initializeDomViewerWrapperPassPass(Registry);
diff --git a/llvm/lib/Analysis/DXILResource.cpp b/llvm/lib/Analysis/DXILResource.cpp
index 5e46463e520ae..4cfe62766acae 100644
--- a/llvm/lib/Analysis/DXILResource.cpp
+++ b/llvm/lib/Analysis/DXILResource.cpp
@@ -1077,3 +1077,33 @@ char DXILResourceWrapperPass::ID = 0;
ModulePass *llvm::createDXILResourceWrapperPassPass() {
return new DXILResourceWrapperPass();
}
+
+DXILResourceBindingWrapperPass::DXILResourceBindingWrapperPass()
+ : ModulePass(ID) {}
+
+DXILResourceBindingWrapperPass::~DXILResourceBindingWrapperPass() = default;
+
+void DXILResourceBindingWrapperPass::getAnalysisUsage(AnalysisUsage &AU) const {
+ AU.addRequiredTransitive<DXILResourceTypeWrapperPass>();
+ AU.setPreservesAll();
+}
+
+bool DXILResourceBindingWrapperPass::runOnModule(Module &M) {
+ BindingInfo.reset(new DXILResourceBindingInfo());
+
+ DXILResourceTypeMap &DRTM =
+ getAnalysis<DXILResourceTypeWrapperPass>().getResourceTypeMap();
+ BindingInfo->populate(M, DRTM);
+
+ return false;
+}
+
+void DXILResourceBindingWrapperPass::releaseMemory() { BindingInfo.reset(); }
+
+INITIALIZE_PASS(DXILResourceBindingWrapperPass, "dxil-resource-binding",
+ "DXIL Resource Binding Analysis", false, true)
+char DXILResourceBindingWrapperPass::ID = 0;
+
+ModulePass *llvm::createDXILResourceBindingWrapperPassPass() {
+ return new DXILResourceWrapperPass();
+}
>From 9a70f060a7accd3a142431422b536abb962e459b Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Mon, 28 Apr 2025 09:56:50 -0700
Subject: [PATCH 04/14] update signature
---
llvm/include/llvm/IR/IntrinsicsDirectX.td | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/include/llvm/IR/IntrinsicsDirectX.td b/llvm/include/llvm/IR/IntrinsicsDirectX.td
index 444600980fc1e..15209c5d229af 100644
--- a/llvm/include/llvm/IR/IntrinsicsDirectX.td
+++ b/llvm/include/llvm/IR/IntrinsicsDirectX.td
@@ -33,7 +33,7 @@ def int_dx_resource_handlefrombinding
def int_dx_resource_handlefromimplicitbinding
: DefaultAttrsIntrinsic<
[llvm_any_ty],
- [llvm_i32_ty, llvm_i32_ty, llvm_i32_ty, llvm_i32_ty],
+ [llvm_i32_ty, llvm_i32_ty, llvm_i32_ty, llvm_i32_ty, llvm_i1_ty],
[IntrNoMem]>;
def int_dx_resource_getpointer
>From bca1bf312e9eb43ae4a0cd5c0086ccdc15f22d74 Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Wed, 30 Apr 2025 13:01:41 -0700
Subject: [PATCH 05/14] code review feedback - rename flags, remove unnecessary
functions, do not initialize space0
---
llvm/include/llvm/Analysis/DXILResource.h | 9 +--
llvm/lib/Analysis/DXILResource.cpp | 8 ++-
.../DirectX/ResourceBindingAnalysisTests.cpp | 67 ++++++-------------
3 files changed, 30 insertions(+), 54 deletions(-)
diff --git a/llvm/include/llvm/Analysis/DXILResource.h b/llvm/include/llvm/Analysis/DXILResource.h
index 556c15868290e..569f5346b9e36 100644
--- a/llvm/include/llvm/Analysis/DXILResource.h
+++ b/llvm/include/llvm/Analysis/DXILResource.h
@@ -637,10 +637,7 @@ class DXILResourceBindingInfo {
struct BindingSpaces {
dxil::ResourceClass ResClass;
llvm::SmallVector<RegisterSpace> Spaces;
- BindingSpaces(dxil::ResourceClass ResClass) : ResClass(ResClass) {
- // initialize space0
- Spaces.emplace_back(0);
- }
+ BindingSpaces(dxil::ResourceClass ResClass) : ResClass(ResClass) {}
};
private:
@@ -660,8 +657,8 @@ class DXILResourceBindingInfo {
SamplerSpaces(dxil::ResourceClass::Sampler), ImplicitBinding(false),
OverlappingBinding(false) {}
- bool containsImplicitBinding() const { return ImplicitBinding; }
- bool containsOverlappingBinding() const { return OverlappingBinding; }
+ bool hasImplicitBinding() const { return ImplicitBinding; }
+ bool hasOverlappingBinding() const { return OverlappingBinding; }
BindingSpaces &getBindingSpaces(dxil::ResourceClass RC) {
switch (RC) {
diff --git a/llvm/lib/Analysis/DXILResource.cpp b/llvm/lib/Analysis/DXILResource.cpp
index 4cfe62766acae..ce8e250a32ebe 100644
--- a/llvm/lib/Analysis/DXILResource.cpp
+++ b/llvm/lib/Analysis/DXILResource.cpp
@@ -927,8 +927,7 @@ void DXILResourceBindingInfo::populate(Module &M, DXILResourceTypeMap &DRTM) {
break;
}
case Intrinsic::dx_resource_handlefromimplicitbinding: {
- if (!F.user_empty())
- ImplicitBinding = true;
+ ImplicitBinding = true;
break;
}
}
@@ -957,7 +956,8 @@ void DXILResourceBindingInfo::populate(Module &M, DXILResourceTypeMap &DRTM) {
// move to the next resource class spaces
BS = &getBindingSpaces(B.ResClass);
- RegisterSpace *S = &BS->Spaces.back();
+ RegisterSpace *S = BS->Spaces.empty() ? &BS->Spaces.emplace_back(B.Space)
+ : &BS->Spaces.back();
assert(S->Space <= B.Space && "bindings not sorted correctly?");
if (B.Space != S->Space)
// add new space
@@ -983,6 +983,8 @@ void DXILResourceBindingInfo::populate(Module &M, DXILResourceTypeMap &DRTM) {
if (B.UpperBound < UINT32_MAX)
S->FreeRanges.emplace_back(B.UpperBound + 1, UINT32_MAX);
} else {
+ // FIXME: This only detects overlapping bindings that are not an exact
+ // match (llvm/llvm-project#110723)
OverlappingBinding = true;
if (B.UpperBound < UINT32_MAX)
LastFreeRange.LowerBound =
diff --git a/llvm/unittests/Target/DirectX/ResourceBindingAnalysisTests.cpp b/llvm/unittests/Target/DirectX/ResourceBindingAnalysisTests.cpp
index bfc4955c51251..091676619f791 100644
--- a/llvm/unittests/Target/DirectX/ResourceBindingAnalysisTests.cpp
+++ b/llvm/unittests/Target/DirectX/ResourceBindingAnalysisTests.cpp
@@ -65,12 +65,8 @@ TEST_F(ResourceBindingAnalysisTest, TestTrivialCase) {
define void @main() {
entry:
%handle = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding(i32 0, i32 5, i32 1, i32 0, i1 false)
-
- call void @a.func(target("dx.TypedBuffer", float, 1, 0, 0) %handle)
ret void
}
-
-declare void @a.func(target("dx.RawBuffer", float, 1, 0) %handle)
)";
auto M = parseAsm(Assembly);
@@ -78,8 +74,8 @@ declare void @a.func(target("dx.RawBuffer", float, 1, 0) %handle)
DXILResourceBindingInfo &DRBI =
MAM->getResult<DXILResourceBindingAnalysis>(*M);
- EXPECT_EQ(false, DRBI.containsImplicitBinding());
- EXPECT_EQ(false, DRBI.containsOverlappingBinding());
+ EXPECT_EQ(false, DRBI.hasImplicitBinding());
+ EXPECT_EQ(false, DRBI.hasOverlappingBinding());
// check that UAV has exactly one gap
DXILResourceBindingInfo::BindingSpaces &UAVSpaces =
@@ -94,8 +90,7 @@ declare void @a.func(target("dx.RawBuffer", float, 1, 0) %handle)
{ResourceClass::SRV, ResourceClass::CBuffer, ResourceClass::Sampler}) {
DXILResourceBindingInfo::BindingSpaces &Spaces = DRBI.getBindingSpaces(RC);
EXPECT_EQ(Spaces.ResClass, RC);
- EXPECT_EQ(Spaces.Spaces.size(), 1u);
- checkExpectedSpaceAndFreeRanges(Spaces.Spaces[0], 0, {0, UINT32_MAX});
+ EXPECT_EQ(Spaces.Spaces.size(), 0u);
}
}
@@ -116,18 +111,8 @@ define void @main() {
%handleC = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding(i32 0, i32 5, i32 1, i32 0, i1 false)
%handleD = call target("dx.RawBuffer", i32, 0, 0) @llvm.dx.resource.handlefrombinding(i32 0, i32 0, i32 5, i32 4, i1 false)
%handleE = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding(i32 0, i32 2, i32 2, i32 0, i1 false)
-
- call void @a.func(target("dx.CBuffer", target("dx.Layout", %__cblayout_CB, 4, 0)) %handleCB)
- call void @a.func(target("dx.TypedBuffer", float, 1, 0, 0) %handleA)
- call void @a.func(target("dx.TypedBuffer", float, 1, 0, 0) %handleC)
- call void @a.func(target("dx.TypedBuffer", float, 1, 0, 0) %handleE)
- call void @a.func(target("dx.RawBuffer", i32, 0, 0) %handleB)
- call void @a.func(target("dx.RawBuffer", i32, 0, 0) %handleD)
-
ret void
}
-
-declare void @a.func(target("dx.RawBuffer", float, 1, 0) %handle)
)";
auto M = parseAsm(Assembly);
@@ -135,8 +120,8 @@ declare void @a.func(target("dx.RawBuffer", float, 1, 0) %handle)
DXILResourceBindingInfo &DRBI =
MAM->getResult<DXILResourceBindingAnalysis>(*M);
- EXPECT_EQ(false, DRBI.containsImplicitBinding());
- EXPECT_EQ(false, DRBI.containsOverlappingBinding());
+ EXPECT_EQ(false, DRBI.hasImplicitBinding());
+ EXPECT_EQ(false, DRBI.hasOverlappingBinding());
DXILResourceBindingInfo::BindingSpaces &SRVSpaces =
DRBI.getBindingSpaces(ResourceClass::SRV);
@@ -174,16 +159,8 @@ define void @main() {
%handleB = call target("dx.RawBuffer", float, 0, 0) @llvm.dx.resource.handlefrombinding(i32 0, i32 0, i32 3, i32 0, i1 false)
%handleC = call target("dx.RawBuffer", float, 0, 0) @llvm.dx.resource.handlefrombinding(i32 2, i32 0, i32 -1, i32 100, i1 false)
%handleD = call target("dx.RawBuffer", float, 0, 0) @llvm.dx.resource.handlefrombinding(i32 2, i32 4, i32 1, i32 0, i1 false)
-
- call void @a.func(target("dx.RawBuffer", float, 0, 0) %handleA)
- call void @a.func(target("dx.RawBuffer", float, 0, 0) %handleB)
- call void @a.func(target("dx.RawBuffer", float, 0, 0) %handleC)
- call void @a.func(target("dx.RawBuffer", float, 0, 0) %handleD)
-
ret void
}
-
-declare void @a.func(target("dx.RawBuffer", float, 0, 0) %handle)
)";
auto M = parseAsm(Assembly);
@@ -191,8 +168,8 @@ declare void @a.func(target("dx.RawBuffer", float, 0, 0) %handle)
DXILResourceBindingInfo &DRBI =
MAM->getResult<DXILResourceBindingAnalysis>(*M);
- EXPECT_EQ(false, DRBI.containsImplicitBinding());
- EXPECT_EQ(true, DRBI.containsOverlappingBinding());
+ EXPECT_EQ(false, DRBI.hasImplicitBinding());
+ EXPECT_EQ(true, DRBI.hasOverlappingBinding());
DXILResourceBindingInfo::BindingSpaces &SRVSpaces =
DRBI.getBindingSpaces(ResourceClass::SRV);
@@ -215,15 +192,8 @@ define void @main() {
%handleA = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding(i32 0, i32 -1, i32 1, i32 0, i1 false)
%handleB = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding(i32 1, i32 -10, i32 10, i32 50, i1 false)
%handleC = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding(i32 2, i32 2147483647, i32 10, i32 100, i1 false)
-
- call void @a.func(target("dx.TypedBuffer", float, 1, 0, 0) %handleA)
- call void @a.func(target("dx.TypedBuffer", float, 1, 0, 0) %handleB)
- call void @a.func(target("dx.TypedBuffer", float, 1, 0, 0) %handleC)
-
ret void
}
-
-declare void @a.func(target("dx.TypedBuffer", float, 1, 0, 0) %handle)
)";
auto M = parseAsm(Assembly);
@@ -231,8 +201,8 @@ declare void @a.func(target("dx.TypedBuffer", float, 1, 0, 0) %handle)
DXILResourceBindingInfo &DRBI =
MAM->getResult<DXILResourceBindingAnalysis>(*M);
- EXPECT_EQ(false, DRBI.containsImplicitBinding());
- EXPECT_EQ(false, DRBI.containsOverlappingBinding());
+ EXPECT_EQ(false, DRBI.hasImplicitBinding());
+ EXPECT_EQ(false, DRBI.hasOverlappingBinding());
DXILResourceBindingInfo::BindingSpaces &UAVSpaces =
DRBI.getBindingSpaces(ResourceClass::UAV);
@@ -246,23 +216,30 @@ declare void @a.func(target("dx.TypedBuffer", float, 1, 0, 0) %handle)
}
TEST_F(ResourceBindingAnalysisTest, TestImplicitFlag) {
- // RWBuffer<float> A;
+ // RWBuffer<float> A : register(u5, space100);
+ // RWBuffer<float> B;
StringRef Assembly = R"(
-%__cblayout_CB = type <{ i32 }>
define void @main() {
entry:
- %handleA = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefromimplicitbinding(i32 0, i32 0, i32 1, i32 0)
- call void @a.func(target("dx.TypedBuffer", float, 1, 0, 0) %handleA)
+ %handleA = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding(i32 100, i32 5, i32 1, i32 0, i1 false)
+ %handleB = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefromimplicitbinding(i32 0, i32 0, i32 1, i32 0, i1 false)
ret void
}
-declare void @a.func(target("dx.TypedBuffer", float, 1, 0, 0) %handle)
)";
auto M = parseAsm(Assembly);
DXILResourceBindingInfo &DRBI =
MAM->getResult<DXILResourceBindingAnalysis>(*M);
- EXPECT_EQ(true, DRBI.containsImplicitBinding());
+ EXPECT_EQ(true, DRBI.hasImplicitBinding());
+ EXPECT_EQ(false, DRBI.hasOverlappingBinding());
+
+ DXILResourceBindingInfo::BindingSpaces &UAVSpaces =
+ DRBI.getBindingSpaces(ResourceClass::UAV);
+ EXPECT_EQ(UAVSpaces.ResClass, ResourceClass::UAV);
+ EXPECT_EQ(UAVSpaces.Spaces.size(), 1u);
+ checkExpectedSpaceAndFreeRanges(UAVSpaces.Spaces[0], 100,
+ {0, 4, 6, UINT32_MAX});
}
} // namespace
>From f58e2d5c079f31d7a4d99d481a569ad4c754c4e7 Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Wed, 30 Apr 2025 15:08:04 -0700
Subject: [PATCH 06/14] [HLSL] Implementation of DXILResourceImplicitBinding
pass
This pass takes advantage of the DXILResourceBinding analysis and assigns
register slots to resources that do not have explicit binding.
Part 2/2 of #136786
Closes #136786
---
llvm/include/llvm/Analysis/DXILResource.h | 8 +
llvm/include/llvm/InitializePasses.h | 1 +
llvm/lib/Analysis/Analysis.cpp | 1 +
llvm/lib/Analysis/DXILResource.cpp | 53 +++++
llvm/lib/Target/DirectX/CMakeLists.txt | 1 +
.../DirectX/DXILResourceImplicitBinding.cpp | 182 ++++++++++++++++++
.../DirectX/DXILResourceImplicitBinding.h | 29 +++
llvm/lib/Target/DirectX/DirectX.h | 6 +
.../Target/DirectX/DirectXPassRegistry.def | 1 +
.../Target/DirectX/DirectXTargetMachine.cpp | 3 +
.../CodeGen/DirectX/ImplicitBinding/arrays.ll | 41 ++++
.../ImplicitBinding/multiple-spaces.ll | 44 +++++
.../CodeGen/DirectX/ImplicitBinding/simple.ll | 30 +++
.../ImplicitBinding/unbounded-arrays-error.ll | 34 ++++
.../ImplicitBinding/unbounded-arrays.ll | 42 ++++
llvm/test/CodeGen/DirectX/llc-pipeline.ll | 2 +
16 files changed, 478 insertions(+)
create mode 100644 llvm/lib/Target/DirectX/DXILResourceImplicitBinding.cpp
create mode 100644 llvm/lib/Target/DirectX/DXILResourceImplicitBinding.h
create mode 100644 llvm/test/CodeGen/DirectX/ImplicitBinding/arrays.ll
create mode 100644 llvm/test/CodeGen/DirectX/ImplicitBinding/multiple-spaces.ll
create mode 100644 llvm/test/CodeGen/DirectX/ImplicitBinding/simple.ll
create mode 100644 llvm/test/CodeGen/DirectX/ImplicitBinding/unbounded-arrays-error.ll
create mode 100644 llvm/test/CodeGen/DirectX/ImplicitBinding/unbounded-arrays.ll
diff --git a/llvm/include/llvm/Analysis/DXILResource.h b/llvm/include/llvm/Analysis/DXILResource.h
index 569f5346b9e36..fd34fdfda4c1c 100644
--- a/llvm/include/llvm/Analysis/DXILResource.h
+++ b/llvm/include/llvm/Analysis/DXILResource.h
@@ -632,12 +632,15 @@ class DXILResourceBindingInfo {
RegisterSpace(uint32_t Space) : Space(Space) {
FreeRanges.emplace_back(0, UINT32_MAX);
}
+ // Size == -1 means unbounded array
+ bool findAvailableBinding(int32_t Size, uint32_t *RegSlot);
};
struct BindingSpaces {
dxil::ResourceClass ResClass;
llvm::SmallVector<RegisterSpace> Spaces;
BindingSpaces(dxil::ResourceClass ResClass) : ResClass(ResClass) {}
+ RegisterSpace &getOrInsertSpace(uint32_t Space);
};
private:
@@ -658,6 +661,7 @@ class DXILResourceBindingInfo {
OverlappingBinding(false) {}
bool hasImplicitBinding() const { return ImplicitBinding; }
+ void setHasImplicitBinding(bool Value) { ImplicitBinding = Value; }
bool hasOverlappingBinding() const { return OverlappingBinding; }
BindingSpaces &getBindingSpaces(dxil::ResourceClass RC) {
@@ -673,6 +677,10 @@ class DXILResourceBindingInfo {
}
}
+ // Size == -1 means unbounded array
+ bool findAvailableBinding(dxil::ResourceClass RC, uint32_t Space,
+ int32_t Size, uint32_t *RegSlot);
+
friend class DXILResourceBindingAnalysis;
friend class DXILResourceBindingWrapperPass;
};
diff --git a/llvm/include/llvm/InitializePasses.h b/llvm/include/llvm/InitializePasses.h
index b84c6d6123d58..71db56d922676 100644
--- a/llvm/include/llvm/InitializePasses.h
+++ b/llvm/include/llvm/InitializePasses.h
@@ -85,6 +85,7 @@ void initializeDCELegacyPassPass(PassRegistry &);
void initializeDXILMetadataAnalysisWrapperPassPass(PassRegistry &);
void initializeDXILMetadataAnalysisWrapperPrinterPass(PassRegistry &);
void initializeDXILResourceBindingWrapperPassPass(PassRegistry &);
+void initializeDXILResourceImplicitBindingLegacyPass(PassRegistry &);
void initializeDXILResourceTypeWrapperPassPass(PassRegistry &);
void initializeDXILResourceWrapperPassPass(PassRegistry &);
void initializeDeadMachineInstructionElimPass(PassRegistry &);
diff --git a/llvm/lib/Analysis/Analysis.cpp b/llvm/lib/Analysis/Analysis.cpp
index 484a456f49f1b..9f5daf32be9a0 100644
--- a/llvm/lib/Analysis/Analysis.cpp
+++ b/llvm/lib/Analysis/Analysis.cpp
@@ -27,6 +27,7 @@ void llvm::initializeAnalysis(PassRegistry &Registry) {
initializeCallGraphViewerPass(Registry);
initializeCycleInfoWrapperPassPass(Registry);
initializeDXILMetadataAnalysisWrapperPassPass(Registry);
+ initializeDXILResourceWrapperPassPass(Registry);
initializeDXILResourceBindingWrapperPassPass(Registry);
initializeDXILResourceTypeWrapperPassPass(Registry);
initializeDXILResourceWrapperPassPass(Registry);
diff --git a/llvm/lib/Analysis/DXILResource.cpp b/llvm/lib/Analysis/DXILResource.cpp
index ce8e250a32ebe..34355c81e77b0 100644
--- a/llvm/lib/Analysis/DXILResource.cpp
+++ b/llvm/lib/Analysis/DXILResource.cpp
@@ -995,6 +995,59 @@ void DXILResourceBindingInfo::populate(Module &M, DXILResourceTypeMap &DRTM) {
}
}
+// returns false if binding could not be found in given space
+bool DXILResourceBindingInfo::findAvailableBinding(dxil::ResourceClass RC,
+ uint32_t Space, int32_t Size,
+ uint32_t *RegSlot) {
+ BindingSpaces &BS = getBindingSpaces(RC);
+ RegisterSpace &RS = BS.getOrInsertSpace(Space);
+ return RS.findAvailableBinding(Size, RegSlot);
+}
+
+DXILResourceBindingInfo::RegisterSpace &
+DXILResourceBindingInfo::BindingSpaces::getOrInsertSpace(uint32_t Space) {
+ for (auto *I = Spaces.begin(); I != Spaces.end(); ++I) {
+ if (I->Space == Space)
+ return *I;
+ if (I->Space < Space)
+ continue;
+ return *Spaces.insert(I, Space);
+ }
+ return Spaces.emplace_back(Space);
+}
+
+bool DXILResourceBindingInfo::RegisterSpace::findAvailableBinding(
+ int32_t Size, uint32_t *RegSlot) {
+ assert((Size == -1 || Size > 0) && "invalid size");
+
+ if (FreeRanges.empty())
+ return false;
+
+ // unbounded array
+ if (Size == -1) {
+ BindingRange &Last = FreeRanges.back();
+ if (Last.UpperBound != UINT32_MAX)
+ // this space is already occupied by an unbounded array
+ return false;
+ *RegSlot = Last.LowerBound;
+ FreeRanges.pop_back();
+ return true;
+ }
+
+ // single resource or fixed-size array
+ for (BindingRange &R : FreeRanges) {
+ if (R.UpperBound - R.LowerBound + 1 < (uint32_t)Size)
+ continue;
+ *RegSlot = R.LowerBound;
+ // This might create a range where (LowerBound == UpperBound + 1), but
+ // that's ok.
+ R.LowerBound += Size;
+ return true;
+ }
+
+ return false;
+}
+
//===----------------------------------------------------------------------===//
AnalysisKey DXILResourceTypeAnalysis::Key;
diff --git a/llvm/lib/Target/DirectX/CMakeLists.txt b/llvm/lib/Target/DirectX/CMakeLists.txt
index 65105d3a5f4c3..9f5550a9a9ce8 100644
--- a/llvm/lib/Target/DirectX/CMakeLists.txt
+++ b/llvm/lib/Target/DirectX/CMakeLists.txt
@@ -31,6 +31,7 @@ add_llvm_target(DirectXCodeGen
DXILPrepare.cpp
DXILPrettyPrinter.cpp
DXILResourceAccess.cpp
+ DXILResourceImplicitBinding.cpp
DXILShaderFlags.cpp
DXILTranslateMetadata.cpp
DXILRootSignature.cpp
diff --git a/llvm/lib/Target/DirectX/DXILResourceImplicitBinding.cpp b/llvm/lib/Target/DirectX/DXILResourceImplicitBinding.cpp
new file mode 100644
index 0000000000000..80293ad7c9716
--- /dev/null
+++ b/llvm/lib/Target/DirectX/DXILResourceImplicitBinding.cpp
@@ -0,0 +1,182 @@
+//===- DXILResourceImplicitBinding.cpp -----------------------------------===//
+//
+// 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 "DXILResourceImplicitBinding.h"
+#include "DirectX.h"
+#include "llvm/ADT/APInt.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Analysis/DXILResource.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/DiagnosticInfo.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/IntrinsicsDirectX.h"
+#include "llvm/IR/Module.h"
+#include "llvm/InitializePasses.h"
+#include <cstdint>
+
+#define DEBUG_TYPE "dxil-resource-implicit-binding"
+
+using namespace llvm;
+using namespace llvm::dxil;
+
+namespace {
+
+static void diagnoseImplicitBindingNotFound(CallInst *ImplBindingCall) {
+ Function *F = ImplBindingCall->getFunction();
+ LLVMContext &Context = F->getParent()->getContext();
+ // FIXME: include the name of the resource in the error message
+ // (llvm/llvm-project#137868)
+ Context.diagnose(
+ DiagnosticInfoGenericWithLoc("resource cannot be allocated", *F,
+ ImplBindingCall->getDebugLoc(), DS_Error));
+}
+
+static bool assignBindings(Module &M, DXILResourceBindingInfo &DRBI,
+ DXILResourceTypeMap &DRTM) {
+ struct ImplBindingCall {
+ int OrderID;
+ CallInst *Call;
+ ImplBindingCall(int OrderID, CallInst *Call)
+ : OrderID(OrderID), Call(Call) {}
+ };
+ SmallVector<ImplBindingCall> Calls;
+ SmallVector<Function *> FunctionsToMaybeRemove;
+
+ // collect all of the llvm.dx.resource.handlefromImplicitbinding calls
+ for (Function &F : M.functions()) {
+ if (!F.isDeclaration())
+ continue;
+
+ if (F.getIntrinsicID() != Intrinsic::dx_resource_handlefromimplicitbinding)
+ continue;
+
+ for (User *U : F.users()) {
+ if (CallInst *CI = dyn_cast<CallInst>(U)) {
+ int OrderID = cast<ConstantInt>(CI->getArgOperand(0))->getZExtValue();
+ Calls.emplace_back(OrderID, CI);
+ }
+ }
+ FunctionsToMaybeRemove.emplace_back(&F);
+ }
+
+ // sort all the collected implicit bindings by OrderID
+ llvm::stable_sort(
+ Calls, [](auto &LHS, auto &RHS) { return LHS.OrderID < RHS.OrderID; });
+
+ // iterate over sorted calls, find binding for each new OrderID and replace
+ // each call with dx_resource_handlefrombinding using the new binding
+ int LastOrderID = -1;
+ llvm::TargetExtType *HandleTy = nullptr;
+ ConstantInt *RegSlotOp = nullptr;
+ bool AllBindingsAssigned = true;
+ bool Changed = false;
+
+ for (auto &IB : Calls) {
+ IRBuilder<> Builder(IB.Call);
+
+ if (IB.OrderID != LastOrderID) {
+ LastOrderID = IB.OrderID;
+ HandleTy = cast<TargetExtType>(IB.Call->getType());
+ ResourceTypeInfo &RTI = DRTM[HandleTy];
+
+ uint32_t Space =
+ cast<ConstantInt>(IB.Call->getArgOperand(1))->getZExtValue();
+ int32_t Size =
+ cast<ConstantInt>(IB.Call->getArgOperand(2))->getZExtValue();
+
+ uint32_t RegSlot;
+ RegSlotOp = nullptr;
+ if (!DRBI.findAvailableBinding(RTI.getResourceClass(), Space, Size,
+ &RegSlot)) {
+ diagnoseImplicitBindingNotFound(IB.Call);
+ AllBindingsAssigned = false;
+ continue;
+ }
+ RegSlotOp = ConstantInt::get(Builder.getInt32Ty(), RegSlot);
+ }
+
+ if (!RegSlotOp)
+ continue;
+
+ auto *NewCall = Builder.CreateIntrinsic(
+ HandleTy, Intrinsic::dx_resource_handlefrombinding,
+ {IB.Call->getOperand(1), /* space */
+ RegSlotOp, /* register slot */
+ IB.Call->getOperand(2), /* size */
+ IB.Call->getOperand(3), /* index */
+ IB.Call->getOperand(4)}); /* non-uniform flag */
+ IB.Call->replaceAllUsesWith(NewCall);
+ IB.Call->eraseFromParent();
+ Changed = true;
+ }
+
+ for (Function *F : FunctionsToMaybeRemove) {
+ if (F->user_empty()) {
+ F->eraseFromParent();
+ Changed = true;
+ }
+ }
+
+ DRBI.setHasImplicitBinding(!AllBindingsAssigned);
+ return Changed;
+}
+
+} // end anonymous namespace
+
+PreservedAnalyses DXILResourceImplicitBinding::run(Module &M,
+ ModuleAnalysisManager &AM) {
+
+ PreservedAnalyses PA;
+
+ DXILResourceBindingInfo &DRBI = AM.getResult<DXILResourceBindingAnalysis>(M);
+ DXILResourceTypeMap &DRTM = AM.getResult<DXILResourceTypeAnalysis>(M);
+ if (DRBI.hasImplicitBinding())
+ if (assignBindings(M, DRBI, DRTM))
+ return PA;
+ return PreservedAnalyses::all();
+}
+
+namespace {
+
+class DXILResourceImplicitBindingLegacy : public ModulePass {
+public:
+ DXILResourceImplicitBindingLegacy() : ModulePass(ID) {}
+
+ bool runOnModule(Module &M) override {
+ DXILResourceTypeMap &DRTM =
+ getAnalysis<DXILResourceTypeWrapperPass>().getResourceTypeMap();
+ DXILResourceBindingInfo &DRBI =
+ getAnalysis<DXILResourceBindingWrapperPass>().getBindingInfo();
+
+ if (DRBI.hasImplicitBinding())
+ return assignBindings(M, DRBI, DRTM);
+ return false;
+ }
+
+ static char ID; // Pass identification.
+ void getAnalysisUsage(llvm::AnalysisUsage &AU) const override {
+ AU.addRequired<DXILResourceTypeWrapperPass>();
+ AU.addRequired<DXILResourceBindingWrapperPass>();
+ }
+};
+
+char DXILResourceImplicitBindingLegacy::ID = 0;
+} // end anonymous namespace
+
+INITIALIZE_PASS_BEGIN(DXILResourceImplicitBindingLegacy, DEBUG_TYPE,
+ "DXIL Resource Implicit Binding", false, false)
+INITIALIZE_PASS_DEPENDENCY(DXILResourceTypeWrapperPass)
+INITIALIZE_PASS_DEPENDENCY(DXILResourceBindingWrapperPass)
+INITIALIZE_PASS_END(DXILResourceImplicitBindingLegacy, DEBUG_TYPE,
+ "DXIL Resource Implicit Binding", false, false)
+
+ModulePass *llvm::createDXILResourceImplicitBindingLegacyPass() {
+ return new DXILResourceImplicitBindingLegacy();
+}
diff --git a/llvm/lib/Target/DirectX/DXILResourceImplicitBinding.h b/llvm/lib/Target/DirectX/DXILResourceImplicitBinding.h
new file mode 100644
index 0000000000000..86ca9ec6842a0
--- /dev/null
+++ b/llvm/lib/Target/DirectX/DXILResourceImplicitBinding.h
@@ -0,0 +1,29 @@
+//===- DXILResourceImplicitBindings.h --_____________-----------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// \file Assign register slots to resources without explicit binding.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIB_TARGET_DIRECTX_DXILRESOURCEIMPLICITBINDING_H
+#define LLVM_LIB_TARGET_DIRECTX_DXILRESOURCEIMPLICITBINDING_H
+
+#include "llvm/IR/PassManager.h"
+#include "llvm/Pass.h"
+
+namespace llvm {
+
+class DXILResourceImplicitBinding
+ : public PassInfoMixin<DXILResourceImplicitBinding> {
+public:
+ PreservedAnalyses run(Module &M, ModuleAnalysisManager &);
+};
+
+} // namespace llvm
+
+#endif // LLVM_LIB_TARGET_DIRECTX_DXILRESOURCEIMPLICITBINDING_H
diff --git a/llvm/lib/Target/DirectX/DirectX.h b/llvm/lib/Target/DirectX/DirectX.h
index f64aaaf65d937..647b8ff42867c 100644
--- a/llvm/lib/Target/DirectX/DirectX.h
+++ b/llvm/lib/Target/DirectX/DirectX.h
@@ -78,6 +78,12 @@ void initializeDXILResourceAccessLegacyPass(PassRegistry &);
/// Pass to update resource accesses to use load/store directly.
FunctionPass *createDXILResourceAccessLegacyPass();
+/// Initializer for DXILResourceImplicitBindingLegacyPass
+void initializeDXILResourceImplicitBindingLegacyPass(PassRegistry &);
+
+/// Pass to assign register slots to resources without binding.
+ModulePass *createDXILResourceImplicitBindingLegacyPass();
+
/// Initializer for DXILTranslateMetadata.
void initializeDXILTranslateMetadataLegacyPass(PassRegistry &);
diff --git a/llvm/lib/Target/DirectX/DirectXPassRegistry.def b/llvm/lib/Target/DirectX/DirectXPassRegistry.def
index da239402d01eb..ef65b96799eae 100644
--- a/llvm/lib/Target/DirectX/DirectXPassRegistry.def
+++ b/llvm/lib/Target/DirectX/DirectXPassRegistry.def
@@ -30,6 +30,7 @@ MODULE_PASS("dxil-intrinsic-expansion", DXILIntrinsicExpansion())
MODULE_PASS("dxil-op-lower", DXILOpLowering())
MODULE_PASS("dxil-pretty-printer", DXILPrettyPrinterPass(dbgs()))
MODULE_PASS("dxil-translate-metadata", DXILTranslateMetadata())
+MODULE_PASS("dxil-resource-implicit-binding", DXILResourceImplicitBinding())
// TODO: rename to print<foo> after NPM switch
MODULE_PASS("print-dx-shader-flags", dxil::ShaderFlagsAnalysisPrinter(dbgs()))
MODULE_PASS("print<dxil-root-signature>", dxil::RootSignatureAnalysisPrinter(dbgs()))
diff --git a/llvm/lib/Target/DirectX/DirectXTargetMachine.cpp b/llvm/lib/Target/DirectX/DirectXTargetMachine.cpp
index d3d1f94f3ab1c..2f2372b779092 100644
--- a/llvm/lib/Target/DirectX/DirectXTargetMachine.cpp
+++ b/llvm/lib/Target/DirectX/DirectXTargetMachine.cpp
@@ -21,6 +21,7 @@
#include "DXILOpLowering.h"
#include "DXILPrettyPrinter.h"
#include "DXILResourceAccess.h"
+#include "DXILResourceImplicitBinding.h"
#include "DXILRootSignature.h"
#include "DXILShaderFlags.h"
#include "DXILTranslateMetadata.h"
@@ -62,6 +63,7 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeDirectXTarget() {
initializeDXContainerGlobalsPass(*PR);
initializeDXILOpLoweringLegacyPass(*PR);
initializeDXILResourceAccessLegacyPass(*PR);
+ initializeDXILResourceImplicitBindingLegacyPass(*PR);
initializeDXILTranslateMetadataLegacyPass(*PR);
initializeShaderFlagsAnalysisWrapperPass(*PR);
initializeRootSignatureAnalysisWrapperPass(*PR);
@@ -99,6 +101,7 @@ class DirectXPassConfig : public TargetPassConfig {
FunctionPass *createTargetRegisterAllocator(bool) override { return nullptr; }
void addCodeGenPrepare() override {
addPass(createDXILFinalizeLinkageLegacyPass());
+ addPass(createDXILResourceImplicitBindingLegacyPass());
addPass(createDXILIntrinsicExpansionLegacyPass());
addPass(createDXILCBufferAccessLegacyPass());
addPass(createDXILDataScalarizationLegacyPass());
diff --git a/llvm/test/CodeGen/DirectX/ImplicitBinding/arrays.ll b/llvm/test/CodeGen/DirectX/ImplicitBinding/arrays.ll
new file mode 100644
index 0000000000000..6196472cb8374
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/ImplicitBinding/arrays.ll
@@ -0,0 +1,41 @@
+; RUN: opt -S -dxil-resource-implicit-binding %s | FileCheck %s
+
+; Resources defined (with random order of handlefromimplicitbinding calls):
+; RWBuffer<float> A : register(u2);
+; RWBuffer<float> B[4]; // gets u3 because it does not fit before A (range 4)
+; RWBuffer<int> C[2]; // gets u0 because it fits before A (range 2)
+; RWBuffer<float> E[5]; // gets u7 which is right after B (range 5)
+
+target triple = "dxil-pc-shadermodel6.6-compute"
+
+define void @test_arrays() {
+
+; RWBuffer<float> A : register(u2);
+ %bufA = call target("dx.TypedBuffer", float, 1, 0, 0)
+ @llvm.dx.resource.handlefrombinding(i32 0, i32 2, i32 1, i32 0, i1 false)
+; no change to llvm.dx.resource.handlefrombinding
+; CHECK: %bufA = call target("dx.TypedBuffer", float, 1, 0, 0)
+; CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0_0t(i32 0, i32 2, i32 1, i32 0, i1 false)
+
+; RWBuffer<float> E[2];
+ %bufE = call target("dx.TypedBuffer", float, 1, 0, 0)
+ @llvm.dx.resource.handlefromimplicitbinding(i32 30, i32 0, i32 5, i32 4, i1 false)
+; CHECK: %{{.*}} = call target("dx.TypedBuffer", float, 1, 0, 0)
+; CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0_0t(i32 0, i32 7, i32 5, i32 4, i1 false)
+
+; RWBuffer<float> B[4];
+ %bufB = call target("dx.TypedBuffer", float, 1, 0, 0)
+ @llvm.dx.resource.handlefromimplicitbinding(i32 10, i32 0, i32 4, i32 2, i1 false)
+; CHECK: %{{.*}} = call target("dx.TypedBuffer", float, 1, 0, 0)
+; CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0_0t(i32 0, i32 3, i32 4, i32 2, i1 false)
+
+; RWBuffer<int> C[2];
+ %bufC = call target("dx.TypedBuffer", i32, 1, 0, 0)
+ @llvm.dx.resource.handlefromimplicitbinding(i32 20, i32 0, i32 2, i32 1, i1 false)
+; CHECK: %{{.*}} = call target("dx.TypedBuffer", i32, 1, 0, 0)
+; CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_i32_1_0_0t(i32 0, i32 0, i32 2, i32 1, i1 false)
+
+; CHECK-NOT: @llvm.dx.resource.handlefromimplicitbinding
+ ret void
+}
+
diff --git a/llvm/test/CodeGen/DirectX/ImplicitBinding/multiple-spaces.ll b/llvm/test/CodeGen/DirectX/ImplicitBinding/multiple-spaces.ll
new file mode 100644
index 0000000000000..fce904e45ba09
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/ImplicitBinding/multiple-spaces.ll
@@ -0,0 +1,44 @@
+; RUN: opt -S -dxil-resource-implicit-binding %s | FileCheck %s
+
+; Resources defined (with random order of handlefromimplicitbinding calls):
+; RWBuffer<float> A : register(u5); // defaults to space0
+; RWBuffer<int> B[]; // gets u6 (unbounded range)
+; RWBuffer<float> C[4] : register(space5); // gets u0 in space5
+; RWBuffer<int> D[] : register(space5); // gets u4 in space5
+; RWBuffer<float> E[3] : register(space10); // gets u0, space10
+; StructuredBuffer<int> F : register(space3);; // gets t0 in space3
+
+target triple = "dxil-pc-shadermodel6.6-compute"
+
+define void @test_many_spaces() {
+
+; RWBuffer<float> A : register(u1);
+ %bufA = call target("dx.TypedBuffer", float, 1, 0, 0)
+ @llvm.dx.resource.handlefrombinding(i32 0, i32 1, i32 1, i32 0, i1 false)
+; no change to llvm.dx.resource.handlefrombinding
+; CHECK: %bufA = call target("dx.TypedBuffer", float, 1, 0, 0)
+; CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0_0t(i32 0, i32 1, i32 1, i32 0, i1 false)
+
+; RWBuffer<float> B[];
+%bufB = call target("dx.TypedBuffer", float, 1, 0, 0)
+ @llvm.dx.resource.handlefromimplicitbinding(i32 100, i32 0, i32 -1, i32 0, i1 false)
+; CHECK: %{{.*}} = call target("dx.TypedBuffer", float, 1, 0, 0)
+; CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0_0t(i32 0, i32 6, i32 -1, i32 0, i1 false)
+
+; RWBuffer<int> C : register(u5);
+ %bufC = call target("dx.TypedBuffer", i32, 1, 0, 0)
+ @llvm.dx.resource.handlefrombinding(i32 0, i32 5, i32 1, i32 0, i1 false)
+; no change to llvm.dx.resource.handlefrombinding
+; CHECK: %bufC = call target("dx.TypedBuffer", i32, 1, 0, 0)
+; CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_i32_1_0_0t(i32 0, i32 5, i32 1, i32 0, i1 false)
+
+; ; RWBuffer<float> D[3];
+ %bufD = call target("dx.TypedBuffer", float, 1, 0, 0)
+ @llvm.dx.resource.handlefromimplicitbinding(i32 101, i32 0, i32 3, i32 1, i1 false)
+; CHECK: %{{.*}} = call target("dx.TypedBuffer", float, 1, 0, 0)
+; CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0_0t(i32 0, i32 2, i32 3, i32 1, i1 false)
+
+; CHECK-NOT: @llvm.dx.resource.handlefromimplicitbinding
+ ret void
+}
+
diff --git a/llvm/test/CodeGen/DirectX/ImplicitBinding/simple.ll b/llvm/test/CodeGen/DirectX/ImplicitBinding/simple.ll
new file mode 100644
index 0000000000000..e942ff652f82a
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/ImplicitBinding/simple.ll
@@ -0,0 +1,30 @@
+; RUN: opt -S -dxil-resource-implicit-binding %s | FileCheck %s
+
+target triple = "dxil-pc-shadermodel6.6-compute"
+
+define void @test_simple_binding() {
+
+; StructuredBuffer<float> A : register(t1);
+ %bufA = call target("dx.RawBuffer", float, 0, 0, 0)
+ @llvm.dx.resource.handlefrombinding(i32 0, i32 1, i32 1, i32 0, i1 false)
+; no change to llvm.dx.resource.handlefrombinding
+; CHECK: %bufA = call target("dx.RawBuffer", float, 0, 0, 0)
+; CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_0_0_0t(i32 0, i32 1, i32 1, i32 0, i1 false)
+
+; StructuredBuffer<float> B; // gets register(t0, space0)
+ %bufB = call target("dx.RawBuffer", float, 0, 0, 0)
+ @llvm.dx.resource.handlefromimplicitbinding(i32 5, i32 0, i32 1, i32 0, i1 false)
+; CHECK: %{{.*}} = call target("dx.RawBuffer", float, 0, 0, 0)
+; CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_0_0_0t(i32 0, i32 0, i32 1, i32 0, i1 false)
+
+; StructuredBuffer<float> C; // gets register(t2, space0)
+ %bufC = call target("dx.RawBuffer", float, 0, 0, 0)
+ @llvm.dx.resource.handlefromimplicitbinding(i32 6, i32 0, i32 1, i32 0, i1 false)
+; CHECK: %{{.*}} = call target("dx.RawBuffer", float, 0, 0, 0)
+; CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_0_0_0t(i32 0, i32 2, i32 1, i32 0, i1 false)
+
+; CHECK-NOT: @llvm.dx.resource.handlefromimplicitbinding
+
+ ret void
+}
+
diff --git a/llvm/test/CodeGen/DirectX/ImplicitBinding/unbounded-arrays-error.ll b/llvm/test/CodeGen/DirectX/ImplicitBinding/unbounded-arrays-error.ll
new file mode 100644
index 0000000000000..af61941d9791f
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/ImplicitBinding/unbounded-arrays-error.ll
@@ -0,0 +1,34 @@
+; RUN: not opt -S -dxil-resource-implicit-binding %s 2>&1 | FileCheck %s
+
+; Resources defined
+; RWBuffer<float> A : register(u1);
+; RWBuffer<float> B[]; // gets u6 (unbounded range)
+; RWBuffer<float> C : register(u5);
+; RWBuffer<float> D[4]; // error - does not fit in the remaining descriptor ranges in space0
+
+; CHECK: error:
+; CHECK-SAME: resource cannot be allocated
+
+target triple = "dxil-pc-shadermodel6.6-compute"
+
+define void @test_many_spaces() {
+
+; RWBuffer<float> A : register(u1);
+%bufA = call target("dx.TypedBuffer", float, 1, 0, 0)
+ @llvm.dx.resource.handlefrombinding(i32 0, i32 1, i32 1, i32 0, i1 false)
+
+; RWBuffer<float> B[];
+%bufB = call target("dx.TypedBuffer", float, 1, 0, 0)
+ @llvm.dx.resource.handlefromimplicitbinding(i32 100, i32 0, i32 -1, i32 0, i1 false)
+
+; RWBuffer<int> C : register(u5);
+%bufC = call target("dx.TypedBuffer", i32, 1, 0, 0)
+ @llvm.dx.resource.handlefrombinding(i32 0, i32 5, i32 1, i32 0, i1 false)
+
+; RWBuffer<float> D[4];
+%bufD = call target("dx.TypedBuffer", float, 1, 0, 0)
+ @llvm.dx.resource.handlefromimplicitbinding(i32 101, i32 0, i32 4, i32 1, i1 false)
+
+ ret void
+}
+
diff --git a/llvm/test/CodeGen/DirectX/ImplicitBinding/unbounded-arrays.ll b/llvm/test/CodeGen/DirectX/ImplicitBinding/unbounded-arrays.ll
new file mode 100644
index 0000000000000..762cbc10b6e66
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/ImplicitBinding/unbounded-arrays.ll
@@ -0,0 +1,42 @@
+; RUN: opt -S -dxil-resource-implicit-binding %s | FileCheck %s
+
+; Resources defined
+; RWBuffer<float> A : register(u1);
+; RWBuffer<float> B[]; // gets u6 (unbounded range)
+; RWBuffer<int> C : register(u5);
+; RWBuffer<float> D[3]; // gets u2 because it fits between A and C but not before A
+
+target triple = "dxil-pc-shadermodel6.6-compute"
+
+define void @test_unbounded_arrays() {
+
+; RWBuffer<float> A : register(u1);
+ %bufA = call target("dx.TypedBuffer", float, 1, 0, 0)
+ @llvm.dx.resource.handlefrombinding(i32 0, i32 1, i32 1, i32 0, i1 false)
+; no change to llvm.dx.resource.handlefrombinding
+; CHECK: %bufA = call target("dx.TypedBuffer", float, 1, 0, 0)
+; CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0_0t(i32 0, i32 1, i32 1, i32 0, i1 false)
+
+; RWBuffer<float> B[];
+%bufB = call target("dx.TypedBuffer", float, 1, 0, 0)
+ @llvm.dx.resource.handlefromimplicitbinding(i32 100, i32 0, i32 -1, i32 0, i1 false)
+; CHECK: %{{.*}} = call target("dx.TypedBuffer", float, 1, 0, 0)
+; CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0_0t(i32 0, i32 6, i32 -1, i32 0, i1 false)
+
+; RWBuffer<int> C : register(u5);
+ %bufC = call target("dx.TypedBuffer", i32, 1, 0, 0)
+ @llvm.dx.resource.handlefrombinding(i32 0, i32 5, i32 1, i32 0, i1 false)
+; no change to llvm.dx.resource.handlefrombinding
+; CHECK: %bufC = call target("dx.TypedBuffer", i32, 1, 0, 0)
+; CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_i32_1_0_0t(i32 0, i32 5, i32 1, i32 0, i1 false)
+
+; ; RWBuffer<float> D[3];
+ %bufD = call target("dx.TypedBuffer", float, 1, 0, 0)
+ @llvm.dx.resource.handlefromimplicitbinding(i32 101, i32 0, i32 3, i32 1, i1 false)
+; CHECK: %{{.*}} = call target("dx.TypedBuffer", float, 1, 0, 0)
+; CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0_0t(i32 0, i32 2, i32 3, i32 1, i1 false)
+
+; CHECK-NOT: @llvm.dx.resource.handlefromimplicitbinding
+ ret void
+}
+
diff --git a/llvm/test/CodeGen/DirectX/llc-pipeline.ll b/llvm/test/CodeGen/DirectX/llc-pipeline.ll
index a2412b6324a05..4ed01a05116f8 100644
--- a/llvm/test/CodeGen/DirectX/llc-pipeline.ll
+++ b/llvm/test/CodeGen/DirectX/llc-pipeline.ll
@@ -14,6 +14,8 @@
; CHECK-NEXT: ModulePass Manager
; CHECK-NEXT: DXIL Finalize Linkage
+; CHECK-NEXT: DXIL Resource Binding Analysis
+; CHECK-NEXT: DXIL Resource Implicit Binding
; CHECK-NEXT: DXIL Intrinsic Expansion
; CHECK-NEXT: DXIL CBuffer Access
; CHECK-NEXT: DXIL Data Scalarization
>From b65d3bcee09367a8db25c7f398a394a3de4dc634 Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Wed, 30 Apr 2025 22:36:43 -0700
Subject: [PATCH 07/14] fix bug & update tests
---
llvm/lib/Analysis/DXILResource.cpp | 4 +-
.../CodeGen/DirectX/ImplicitBinding/arrays.ll | 6 ++
.../ImplicitBinding/multiple-spaces.ll | 58 +++++++++++--------
.../CodeGen/DirectX/ImplicitBinding/simple.ll | 18 +++---
4 files changed, 52 insertions(+), 34 deletions(-)
diff --git a/llvm/lib/Analysis/DXILResource.cpp b/llvm/lib/Analysis/DXILResource.cpp
index 34355c81e77b0..0d8f82e138066 100644
--- a/llvm/lib/Analysis/DXILResource.cpp
+++ b/llvm/lib/Analysis/DXILResource.cpp
@@ -1036,7 +1036,9 @@ bool DXILResourceBindingInfo::RegisterSpace::findAvailableBinding(
// single resource or fixed-size array
for (BindingRange &R : FreeRanges) {
- if (R.UpperBound - R.LowerBound + 1 < (uint32_t)Size)
+ // compare the size as uint64_t to prevent overflow for range (0,
+ // UINT32_MAX)
+ if ((uint64_t)R.UpperBound - R.LowerBound + 1 < (uint64_t)Size)
continue;
*RegSlot = R.LowerBound;
// This might create a range where (LowerBound == UpperBound + 1), but
diff --git a/llvm/test/CodeGen/DirectX/ImplicitBinding/arrays.ll b/llvm/test/CodeGen/DirectX/ImplicitBinding/arrays.ll
index 6196472cb8374..7c656becef11f 100644
--- a/llvm/test/CodeGen/DirectX/ImplicitBinding/arrays.ll
+++ b/llvm/test/CodeGen/DirectX/ImplicitBinding/arrays.ll
@@ -35,6 +35,12 @@ define void @test_arrays() {
; CHECK: %{{.*}} = call target("dx.TypedBuffer", i32, 1, 0, 0)
; CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_i32_1_0_0t(i32 0, i32 0, i32 2, i32 1, i1 false)
+; another access to resource array B to make sure it gets the same binding
+ %bufB2 = call target("dx.TypedBuffer", float, 1, 0, 0)
+ @llvm.dx.resource.handlefromimplicitbinding(i32 10, i32 0, i32 4, i32 0, i1 false)
+; CHECK: %{{.*}} = call target("dx.TypedBuffer", float, 1, 0, 0)
+; CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0_0t(i32 0, i32 3, i32 4, i32 0, i1 false)
+
; CHECK-NOT: @llvm.dx.resource.handlefromimplicitbinding
ret void
}
diff --git a/llvm/test/CodeGen/DirectX/ImplicitBinding/multiple-spaces.ll b/llvm/test/CodeGen/DirectX/ImplicitBinding/multiple-spaces.ll
index fce904e45ba09..b22d7fbff2f0c 100644
--- a/llvm/test/CodeGen/DirectX/ImplicitBinding/multiple-spaces.ll
+++ b/llvm/test/CodeGen/DirectX/ImplicitBinding/multiple-spaces.ll
@@ -1,44 +1,54 @@
; RUN: opt -S -dxil-resource-implicit-binding %s | FileCheck %s
; Resources defined (with random order of handlefromimplicitbinding calls):
-; RWBuffer<float> A : register(u5); // defaults to space0
-; RWBuffer<int> B[]; // gets u6 (unbounded range)
-; RWBuffer<float> C[4] : register(space5); // gets u0 in space5
-; RWBuffer<int> D[] : register(space5); // gets u4 in space5
-; RWBuffer<float> E[3] : register(space10); // gets u0, space10
-; StructuredBuffer<int> F : register(space3);; // gets t0 in space3
+; RWBuffer<float> A : register(u5); // defaults to space0
+; RWBuffer<int> B[]; // gets u6 (unbounded range)
+; RWBuffer<float> C[4] : registcer(space5); // gets u0 in space5
+; RWBuffer<int> D[] : register(space5); // gets u4 in space5
+; RWBuffer<float> E[3] : register(space10); // gets u0, space10
+; StructuredBuffer<int> F : register(space3); // gets t0 in space3
target triple = "dxil-pc-shadermodel6.6-compute"
define void @test_many_spaces() {
-; RWBuffer<float> A : register(u1);
+; RWBuffer<float> A : register(u5);
%bufA = call target("dx.TypedBuffer", float, 1, 0, 0)
- @llvm.dx.resource.handlefrombinding(i32 0, i32 1, i32 1, i32 0, i1 false)
+ @llvm.dx.resource.handlefrombinding(i32 0, i32 5, i32 1, i32 0, i1 false)
; no change to llvm.dx.resource.handlefrombinding
; CHECK: %bufA = call target("dx.TypedBuffer", float, 1, 0, 0)
-; CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0_0t(i32 0, i32 1, i32 1, i32 0, i1 false)
+; CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0_0t(i32 0, i32 5, i32 1, i32 0, i1 false)
-; RWBuffer<float> B[];
-%bufB = call target("dx.TypedBuffer", float, 1, 0, 0)
+; RWBuffer<int> B[];
+%bufB = call target("dx.TypedBuffer", i32, 1, 0, 0)
@llvm.dx.resource.handlefromimplicitbinding(i32 100, i32 0, i32 -1, i32 0, i1 false)
-; CHECK: %{{.*}} = call target("dx.TypedBuffer", float, 1, 0, 0)
-; CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0_0t(i32 0, i32 6, i32 -1, i32 0, i1 false)
+; CHECK: %{{.*}} = call target("dx.TypedBuffer", i32, 1, 0, 0)
+; CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_i32_1_0_0t(i32 0, i32 6, i32 -1, i32 0, i1 false)
-; RWBuffer<int> C : register(u5);
- %bufC = call target("dx.TypedBuffer", i32, 1, 0, 0)
- @llvm.dx.resource.handlefrombinding(i32 0, i32 5, i32 1, i32 0, i1 false)
-; no change to llvm.dx.resource.handlefrombinding
-; CHECK: %bufC = call target("dx.TypedBuffer", i32, 1, 0, 0)
-; CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_i32_1_0_0t(i32 0, i32 5, i32 1, i32 0, i1 false)
+; RWBuffer<float> C[4] : register(space5);
+%bufC = call target("dx.TypedBuffer", i32, 1, 0, 0)
+ @llvm.dx.resource.handlefromimplicitbinding(i32 101, i32 5, i32 4, i32 0, i1 false)
+; CHECK: %{{.*}} = call target("dx.TypedBuffer", i32, 1, 0, 0)
+; CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_i32_1_0_0t(i32 5, i32 0, i32 4, i32 0, i1 false)
-; ; RWBuffer<float> D[3];
- %bufD = call target("dx.TypedBuffer", float, 1, 0, 0)
- @llvm.dx.resource.handlefromimplicitbinding(i32 101, i32 0, i32 3, i32 1, i1 false)
+; RWBuffer<int> D[] : register(space5);
+ %bufD = call target("dx.TypedBuffer", i32, 1, 0, 0)
+ @llvm.dx.resource.handlefromimplicitbinding(i32 102, i32 5, i32 -1, i32 0, i1 false)
+; CHECK: %{{.*}} = call target("dx.TypedBuffer", i32, 1, 0, 0)
+; CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_i32_1_0_0t(i32 5, i32 4, i32 -1, i32 0, i1 false)
+
+; RWBuffer<float> E[3] : register(space10); // gets u0, space10
+%bufE = call target("dx.TypedBuffer", float, 1, 0, 0)
+ @llvm.dx.resource.handlefromimplicitbinding(i32 103, i32 10, i32 4, i32 0, i1 false)
; CHECK: %{{.*}} = call target("dx.TypedBuffer", float, 1, 0, 0)
-; CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0_0t(i32 0, i32 2, i32 3, i32 1, i1 false)
+; CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0_0t(i32 10, i32 0, i32 4, i32 0, i1 false)
+
+; StructuredBuffer<int> F : register(space3); // gets t0 in space3
+%bufF = call target("dx.RawBuffer", i32, 0, 0)
+ @llvm.dx.resource.handlefromimplicitbinding(i32 104, i32 3, i32 1, i32 0, i1 false)
+; CHECK: %{{.*}} = call target("dx.RawBuffer", i32, 0, 0)
+; CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_i32_0_0t(i32 3, i32 0, i32 1, i32 0, i1 false)
; CHECK-NOT: @llvm.dx.resource.handlefromimplicitbinding
ret void
}
-
diff --git a/llvm/test/CodeGen/DirectX/ImplicitBinding/simple.ll b/llvm/test/CodeGen/DirectX/ImplicitBinding/simple.ll
index e942ff652f82a..83a1077a71ca5 100644
--- a/llvm/test/CodeGen/DirectX/ImplicitBinding/simple.ll
+++ b/llvm/test/CodeGen/DirectX/ImplicitBinding/simple.ll
@@ -5,23 +5,23 @@ target triple = "dxil-pc-shadermodel6.6-compute"
define void @test_simple_binding() {
; StructuredBuffer<float> A : register(t1);
- %bufA = call target("dx.RawBuffer", float, 0, 0, 0)
+ %bufA = call target("dx.RawBuffer", float, 0, 0)
@llvm.dx.resource.handlefrombinding(i32 0, i32 1, i32 1, i32 0, i1 false)
; no change to llvm.dx.resource.handlefrombinding
-; CHECK: %bufA = call target("dx.RawBuffer", float, 0, 0, 0)
-; CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_0_0_0t(i32 0, i32 1, i32 1, i32 0, i1 false)
+; CHECK: %bufA = call target("dx.RawBuffer", float, 0, 0)
+; CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_0_0t(i32 0, i32 1, i32 1, i32 0, i1 false)
; StructuredBuffer<float> B; // gets register(t0, space0)
- %bufB = call target("dx.RawBuffer", float, 0, 0, 0)
+ %bufB = call target("dx.RawBuffer", float, 0, 0)
@llvm.dx.resource.handlefromimplicitbinding(i32 5, i32 0, i32 1, i32 0, i1 false)
-; CHECK: %{{.*}} = call target("dx.RawBuffer", float, 0, 0, 0)
-; CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_0_0_0t(i32 0, i32 0, i32 1, i32 0, i1 false)
+; CHECK: %{{.*}} = call target("dx.RawBuffer", float, 0, 0)
+; CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_0_0t(i32 0, i32 0, i32 1, i32 0, i1 false)
; StructuredBuffer<float> C; // gets register(t2, space0)
- %bufC = call target("dx.RawBuffer", float, 0, 0, 0)
+ %bufC = call target("dx.RawBuffer", float, 0, 0)
@llvm.dx.resource.handlefromimplicitbinding(i32 6, i32 0, i32 1, i32 0, i1 false)
-; CHECK: %{{.*}} = call target("dx.RawBuffer", float, 0, 0, 0)
-; CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_0_0_0t(i32 0, i32 2, i32 1, i32 0, i1 false)
+; CHECK: %{{.*}} = call target("dx.RawBuffer", float, 0, 0)
+; CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_0_0t(i32 0, i32 2, i32 1, i32 0, i1 false)
; CHECK-NOT: @llvm.dx.resource.handlefromimplicitbinding
>From 2f0aba1fdb082177d0ce2182538780ceb05fed3d Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Mon, 5 May 2025 12:47:45 -0700
Subject: [PATCH 08/14] Remove extra new line
---
llvm/lib/Analysis/DXILResource.cpp | 1 -
1 file changed, 1 deletion(-)
diff --git a/llvm/lib/Analysis/DXILResource.cpp b/llvm/lib/Analysis/DXILResource.cpp
index ce8e250a32ebe..1f7c7116462f1 100644
--- a/llvm/lib/Analysis/DXILResource.cpp
+++ b/llvm/lib/Analysis/DXILResource.cpp
@@ -977,7 +977,6 @@ void DXILResourceBindingInfo::populate(Module &M, DXILResourceTypeMap &DRTM) {
LastFreeRange.LowerBound = B.UpperBound + 1;
else
S->FreeRanges.pop_back();
-
} else if (LastFreeRange.LowerBound < B.LowerBound) {
LastFreeRange.UpperBound = B.LowerBound - 1;
if (B.UpperBound < UINT32_MAX)
>From dfce6ce8edc520251c0561cfd50cc6c11af46ecd Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Wed, 7 May 2025 14:43:27 -0700
Subject: [PATCH 09/14] [HLSL] Add resource constructor with implicit binding
for global resources
Adds constructor for resources with implicit binding and applies it to
all resources without binding at the global scope. Adds Clang buildin
function __builtin_hlsl_resource_handlefromimplicitbinding that it translated
to llvm.dx|spv.resource.handlefromimplicitbinding calls. Specific bindings are
assigned in DXILResourceImplicitBinding pass.
Depends on #138043
Closes #136784
---
clang/include/clang/Basic/Builtins.td | 6 ++
clang/include/clang/Sema/SemaHLSL.h | 7 ++
clang/lib/CodeGen/CGHLSLBuiltins.cpp | 15 ++++
clang/lib/CodeGen/CGHLSLRuntime.h | 2 +
clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp | 20 ++++++
clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h | 3 +-
clang/lib/Sema/HLSLExternalSemaSource.cpp | 3 +-
clang/lib/Sema/SemaHLSL.cpp | 72 +++++++++++++------
.../test/AST/HLSL/ByteAddressBuffers-AST.hlsl | 22 ++++++
.../test/AST/HLSL/StructuredBuffers-AST.hlsl | 22 ++++++
clang/test/AST/HLSL/TypedBuffers-AST.hlsl | 22 ++++++
.../CodeGenHLSL/GlobalConstructorLib.hlsl | 2 +-
.../ByteAddressBuffers-constructors.hlsl | 55 ++++++++++++--
.../builtins/RWBuffer-constructor.hlsl | 55 ++++++++++++--
.../StructuredBuffers-constructors.hlsl | 54 +++++++++++++-
.../StructuredBuffers-methods-ps.hlsl | 6 +-
clang/test/CodeGenHLSL/static-local-ctor.hlsl | 2 +-
llvm/include/llvm/IR/IntrinsicsSPIRV.td | 5 ++
18 files changed, 335 insertions(+), 38 deletions(-)
diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index 11b1e247237a7..187d3b5ed24a7 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -4819,6 +4819,12 @@ def HLSLResourceHandleFromBinding : LangBuiltin<"HLSL_LANG"> {
let Prototype = "void(...)";
}
+def HLSLResourceHandleFromImplicitBinding : LangBuiltin<"HLSL_LANG"> {
+ let Spellings = ["__builtin_hlsl_resource_handlefromimplicitbinding"];
+ let Attributes = [NoThrow];
+ let Prototype = "void(...)";
+}
+
def HLSLAll : LangBuiltin<"HLSL_LANG"> {
let Spellings = ["__builtin_hlsl_all"];
let Attributes = [NoThrow, Const];
diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h
index 5d260acf92abb..bedf541439dbf 100644
--- a/clang/include/clang/Sema/SemaHLSL.h
+++ b/clang/include/clang/Sema/SemaHLSL.h
@@ -174,6 +174,8 @@ class SemaHLSL : public SemaBase {
// buffer which will be created at the end of the translation unit.
llvm::SmallVector<Decl *> DefaultCBufferDecls;
+ uint32_t ImplicitBindingNextOrderID = 0;
+
private:
void collectResourceBindingsOnVarDecl(VarDecl *D);
void collectResourceBindingsOnUserRecordDecl(const VarDecl *VD,
@@ -181,6 +183,11 @@ class SemaHLSL : public SemaBase {
void processExplicitBindingsOnDecl(VarDecl *D);
void diagnoseAvailabilityViolations(TranslationUnitDecl *TU);
+
+ bool initGlobalResourceDecl(VarDecl *VD);
+ uint32_t getNextImplicitBindingOrderID() {
+ return ImplicitBindingNextOrderID++;
+ }
};
} // namespace clang
diff --git a/clang/lib/CodeGen/CGHLSLBuiltins.cpp b/clang/lib/CodeGen/CGHLSLBuiltins.cpp
index 5d93df34c66b2..d4a0714da07b3 100644
--- a/clang/lib/CodeGen/CGHLSLBuiltins.cpp
+++ b/clang/lib/CodeGen/CGHLSLBuiltins.cpp
@@ -303,6 +303,21 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID,
HandleTy, CGM.getHLSLRuntime().getCreateHandleFromBindingIntrinsic(),
ArrayRef<Value *>{SpaceOp, RegisterOp, RangeOp, IndexOp, NonUniform});
}
+ case Builtin::BI__builtin_hlsl_resource_handlefromimplicitbinding: {
+ llvm::Type *HandleTy = CGM.getTypes().ConvertType(E->getType());
+ Value *SpaceOp = EmitScalarExpr(E->getArg(1));
+ Value *RangeOp = EmitScalarExpr(E->getArg(2));
+ Value *IndexOp = EmitScalarExpr(E->getArg(3));
+ Value *OrderID = EmitScalarExpr(E->getArg(4));
+ // FIXME: NonUniformResourceIndex bit is not yet implemented
+ // (llvm/llvm-project#135452)
+ Value *NonUniform =
+ llvm::ConstantInt::get(llvm::Type::getInt1Ty(getLLVMContext()), false);
+ return Builder.CreateIntrinsic(
+ HandleTy,
+ CGM.getHLSLRuntime().getCreateHandleFromImplicitBindingIntrinsic(),
+ ArrayRef<Value *>{OrderID, SpaceOp, RangeOp, IndexOp, NonUniform});
+ }
case Builtin::BI__builtin_hlsl_all: {
Value *Op0 = EmitScalarExpr(E->getArg(0));
return Builder.CreateIntrinsic(
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h b/clang/lib/CodeGen/CGHLSLRuntime.h
index 4d6db3f5d9f3e..e40864d8ed854 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.h
+++ b/clang/lib/CodeGen/CGHLSLRuntime.h
@@ -119,6 +119,8 @@ class CGHLSLRuntime {
resource_getpointer)
GENERATE_HLSL_INTRINSIC_FUNCTION(CreateHandleFromBinding,
resource_handlefrombinding)
+ GENERATE_HLSL_INTRINSIC_FUNCTION(CreateHandleFromImplicitBinding,
+ resource_handlefromimplicitbinding)
GENERATE_HLSL_INTRINSIC_FUNCTION(BufferUpdateCounter, resource_updatecounter)
GENERATE_HLSL_INTRINSIC_FUNCTION(GroupMemoryBarrierWithGroupSync,
group_memory_barrier_with_group_sync)
diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
index 35364a4d6f2ac..d5fbd5f6ecc9f 100644
--- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
+++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
@@ -668,6 +668,26 @@ BuiltinTypeDeclBuilder::addHandleConstructorFromBinding() {
.finalize();
}
+BuiltinTypeDeclBuilder &
+BuiltinTypeDeclBuilder::addHandleConstructorFromImplicitBinding() {
+ if (Record->isCompleteDefinition())
+ return *this;
+
+ using PH = BuiltinTypeMethodBuilder::PlaceHolder;
+ ASTContext &AST = SemaRef.getASTContext();
+ QualType HandleType = getResourceHandleField()->getType();
+
+ return BuiltinTypeMethodBuilder(*this, "", AST.VoidTy, false, true)
+ .addParam("spaceNo", AST.UnsignedIntTy)
+ .addParam("range", AST.IntTy)
+ .addParam("index", AST.UnsignedIntTy)
+ .addParam("order_id", AST.UnsignedIntTy)
+ .callBuiltin("__builtin_hlsl_resource_handlefromimplicitbinding",
+ HandleType, PH::Handle, PH::_0, PH::_1, PH::_2, PH::_3)
+ .assign(PH::Handle, PH::LastStmt)
+ .finalize();
+}
+
BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addArraySubscriptOperators() {
ASTContext &AST = Record->getASTContext();
DeclarationName Subscript =
diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h
index db617dc53c899..a52e2938104c7 100644
--- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h
+++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h
@@ -76,9 +76,10 @@ class BuiltinTypeDeclBuilder {
AccessSpecifier Access = AccessSpecifier::AS_private);
BuiltinTypeDeclBuilder &addArraySubscriptOperators();
- // Builtin types methods
+ // Builtin types constructors
BuiltinTypeDeclBuilder &addDefaultHandleConstructor();
BuiltinTypeDeclBuilder &addHandleConstructorFromBinding();
+ BuiltinTypeDeclBuilder &addHandleConstructorFromImplicitBinding();
// Builtin types methods
BuiltinTypeDeclBuilder &addLoadMethods();
diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp b/clang/lib/Sema/HLSLExternalSemaSource.cpp
index f09232a9db4da..38bde7c28e946 100644
--- a/clang/lib/Sema/HLSLExternalSemaSource.cpp
+++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp
@@ -132,7 +132,8 @@ static BuiltinTypeDeclBuilder setupBufferType(CXXRecordDecl *Decl, Sema &S,
return BuiltinTypeDeclBuilder(S, Decl)
.addHandleMember(RC, IsROV, RawBuffer)
.addDefaultHandleConstructor()
- .addHandleConstructorFromBinding();
+ .addHandleConstructorFromBinding()
+ .addHandleConstructorFromImplicitBinding();
}
// This function is responsible for constructing the constraint expression for
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 70aacaa2aadbe..c0669a8d60470 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -2427,6 +2427,20 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
TheCall->setType(ResourceTy);
break;
}
+ case Builtin::BI__builtin_hlsl_resource_handlefromimplicitbinding: {
+ ASTContext &AST = SemaRef.getASTContext();
+ if (SemaRef.checkArgCount(TheCall, 5) ||
+ CheckResourceHandle(&SemaRef, TheCall, 0) ||
+ CheckArgTypeMatches(&SemaRef, TheCall->getArg(1), AST.UnsignedIntTy) ||
+ CheckArgTypeMatches(&SemaRef, TheCall->getArg(2), AST.IntTy) ||
+ CheckArgTypeMatches(&SemaRef, TheCall->getArg(3), AST.UnsignedIntTy) ||
+ CheckArgTypeMatches(&SemaRef, TheCall->getArg(4), AST.UnsignedIntTy))
+ return true;
+ // use the type of the handle (arg0) as a return type
+ QualType ResourceTy = TheCall->getArg(0)->getType();
+ TheCall->setType(ResourceTy);
+ break;
+ }
case Builtin::BI__builtin_hlsl_and:
case Builtin::BI__builtin_hlsl_or: {
if (SemaRef.checkArgCount(TheCall, 2))
@@ -3258,8 +3272,10 @@ static bool initVarDeclWithCtor(Sema &S, VarDecl *VD,
VD->getLocation(), SourceLocation(), SourceLocation());
InitializationSequence InitSeq(S, Entity, Kind, Args);
- ExprResult Init = InitSeq.Perform(S, Entity, Kind, Args);
+ if (InitSeq.Failed())
+ return false;
+ ExprResult Init = InitSeq.Perform(S, Entity, Kind, Args);
if (!Init.get())
return false;
@@ -3269,27 +3285,42 @@ static bool initVarDeclWithCtor(Sema &S, VarDecl *VD,
return true;
}
-static bool initGlobalResourceDecl(Sema &S, VarDecl *VD) {
+bool SemaHLSL::initGlobalResourceDecl(VarDecl *VD) {
+ std::optional<uint32_t> RegisterSlot;
+ uint32_t SpaceNo = 0;
HLSLResourceBindingAttr *RBA = VD->getAttr<HLSLResourceBindingAttr>();
- if (!RBA || RBA->isImplicit())
- // FIXME: add support for implicit binding (llvm/llvm-project#110722)
- return false;
+ if (RBA) {
+ if (!RBA->isImplicit())
+ RegisterSlot = RBA->getSlotNumber();
+ SpaceNo = RBA->getSpaceNumber();
+ }
- ASTContext &AST = S.getASTContext();
+ ASTContext &AST = SemaRef.getASTContext();
uint64_t UIntTySize = AST.getTypeSize(AST.UnsignedIntTy);
uint64_t IntTySize = AST.getTypeSize(AST.IntTy);
- Expr *Args[] = {
- IntegerLiteral::Create(AST, llvm::APInt(UIntTySize, RBA->getSlotNumber()),
- AST.UnsignedIntTy, SourceLocation()),
- IntegerLiteral::Create(AST,
- llvm::APInt(UIntTySize, RBA->getSpaceNumber()),
- AST.UnsignedIntTy, SourceLocation()),
- IntegerLiteral::Create(AST, llvm::APInt(IntTySize, 1), AST.IntTy,
- SourceLocation()),
- IntegerLiteral::Create(AST, llvm::APInt(UIntTySize, 0), AST.UnsignedIntTy,
- SourceLocation())};
-
- return initVarDeclWithCtor(S, VD, Args);
+ IntegerLiteral *One = IntegerLiteral::Create(AST, llvm::APInt(IntTySize, 1),
+ AST.IntTy, SourceLocation());
+ IntegerLiteral *Zero = IntegerLiteral::Create(
+ AST, llvm::APInt(UIntTySize, 0), AST.UnsignedIntTy, SourceLocation());
+ IntegerLiteral *Space =
+ IntegerLiteral::Create(AST, llvm::APInt(UIntTySize, SpaceNo),
+ AST.UnsignedIntTy, SourceLocation());
+
+ // resource with explicit binding
+ if (RegisterSlot.has_value()) {
+ IntegerLiteral *RegSlot = IntegerLiteral::Create(
+ AST, llvm::APInt(UIntTySize, RegisterSlot.value()), AST.UnsignedIntTy,
+ SourceLocation());
+ Expr *Args[] = {RegSlot, Space, One, Zero};
+ return initVarDeclWithCtor(SemaRef, VD, Args);
+ }
+
+ // resource with explicit binding
+ IntegerLiteral *OrderId = IntegerLiteral::Create(
+ AST, llvm::APInt(UIntTySize, getNextImplicitBindingOrderID()),
+ AST.UnsignedIntTy, SourceLocation());
+ Expr *Args[] = {Space, One, Zero, OrderId};
+ return initVarDeclWithCtor(SemaRef, VD, Args);
}
// Returns true if the initialization has been handled.
@@ -3307,8 +3338,9 @@ bool SemaHLSL::ActOnUninitializedVarDecl(VarDecl *VD) {
// FIXME: We currectly support only simple resources - no arrays of resources
// or resources in user defined structs.
// (llvm/llvm-project#133835, llvm/llvm-project#133837)
- if (VD->getType()->isHLSLResourceRecord())
- return initGlobalResourceDecl(SemaRef, VD);
+ // Initialize resources at the global scope
+ if (VD->hasGlobalStorage() && VD->getType()->isHLSLResourceRecord())
+ return initGlobalResourceDecl(VD);
return false;
}
diff --git a/clang/test/AST/HLSL/ByteAddressBuffers-AST.hlsl b/clang/test/AST/HLSL/ByteAddressBuffers-AST.hlsl
index 5fba939d29cfe..99f26473dbb02 100644
--- a/clang/test/AST/HLSL/ByteAddressBuffers-AST.hlsl
+++ b/clang/test/AST/HLSL/ByteAddressBuffers-AST.hlsl
@@ -78,5 +78,27 @@ RESOURCE Buffer;
// CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' ParmVar {{.*}} 'index' 'unsigned int'
// CHECK-NEXT: AlwaysInlineAttr
+// Constructor from implicit binding
+
+// CHECK: CXXConstructorDecl {{.*}} [[RESOURCE]] 'void (unsigned int, int, unsigned int, unsigned int)' inline
+// CHECK-NEXT: ParmVarDecl {{.*}} spaceNo 'unsigned int'
+// CHECK-NEXT: ParmVarDecl {{.*}} range 'int'
+// CHECK-NEXT: ParmVarDecl {{.*}} index 'unsigned int'
+// CHECK-NEXT: ParmVarDecl {{.*}} order_id 'unsigned int'
+// CHECK-NEXT: CompoundStmt {{.*}}
+// CHECK-NEXT: BinaryOperator {{.*}} '='
+// CHECK-NEXT: MemberExpr {{.*}} lvalue .__handle
+// CHECK-NEXT: CXXThisExpr {{.*}} 'hlsl::[[RESOURCE]]' lvalue implicit this
+// CHECK-NEXT: CallExpr {{.*}} '__hlsl_resource_t
+// CHECK-NEXT: ImplicitCastExpr {{.*}} <BuiltinFnToFnPtr>
+// CHECK-NEXT: DeclRefExpr {{.*}} '<builtin fn type>' Function {{.*}} '__builtin_hlsl_resource_handlefromimplicitbinding'
+// CHECK-NEXT: MemberExpr {{.*}} lvalue .__handle
+// CHECK-NEXT: CXXThisExpr {{.*}} 'hlsl::[[RESOURCE]]' lvalue implicit this
+// CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' ParmVar {{.*}} 'spaceNo' 'unsigned int'
+// CHECK-NEXT: DeclRefExpr {{.*}} 'int' ParmVar {{.*}} 'range' 'int'
+// CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' ParmVar {{.*}} 'index' 'unsigned int'
+// CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' ParmVar {{.*}} 'order_id' 'unsigned int'
+// CHECK-NEXT: AlwaysInlineAttr
+
// CHECK-NOSUBSCRIPT-NOT: CXXMethodDecl {{.*}} operator[] 'const element_type &(unsigned int) const'
// CHECK-NOSUBSCRIPT-NOT: CXXMethodDecl {{.*}} operator[] 'element_type &(unsigned int)'
diff --git a/clang/test/AST/HLSL/StructuredBuffers-AST.hlsl b/clang/test/AST/HLSL/StructuredBuffers-AST.hlsl
index 63265a0003582..03bfa6cb26003 100644
--- a/clang/test/AST/HLSL/StructuredBuffers-AST.hlsl
+++ b/clang/test/AST/HLSL/StructuredBuffers-AST.hlsl
@@ -125,6 +125,28 @@ RESOURCE<float> Buffer;
// CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' ParmVar {{.*}} 'index' 'unsigned int'
// CHECK-NEXT: AlwaysInlineAttr
+// Constructor from implicit binding
+
+// CHECK: CXXConstructorDecl {{.*}} [[RESOURCE]]<element_type> 'void (unsigned int, int, unsigned int, unsigned int)' inline
+// CHECK-NEXT: ParmVarDecl {{.*}} spaceNo 'unsigned int'
+// CHECK-NEXT: ParmVarDecl {{.*}} range 'int'
+// CHECK-NEXT: ParmVarDecl {{.*}} index 'unsigned int'
+// CHECK-NEXT: ParmVarDecl {{.*}} order_id 'unsigned int'
+// CHECK-NEXT: CompoundStmt {{.*}}
+// CHECK-NEXT: BinaryOperator {{.*}} '='
+// CHECK-NEXT: MemberExpr {{.*}} lvalue .__handle
+// CHECK-NEXT: CXXThisExpr {{.*}} '[[RESOURCE]]<element_type>' lvalue implicit this
+// CHECK-NEXT: CallExpr {{.*}} '__hlsl_resource_t
+// CHECK-NEXT: ImplicitCastExpr {{.*}} <BuiltinFnToFnPtr>
+// CHECK-NEXT: DeclRefExpr {{.*}} '<builtin fn type>' Function {{.*}} '__builtin_hlsl_resource_handlefromimplicitbinding'
+// CHECK-NEXT: MemberExpr {{.*}} lvalue .__handle
+// CHECK-NEXT: CXXThisExpr {{.*}} '[[RESOURCE]]<element_type>' lvalue implicit this
+// CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' ParmVar {{.*}} 'spaceNo' 'unsigned int'
+// CHECK-NEXT: DeclRefExpr {{.*}} 'int' ParmVar {{.*}} 'range' 'int'
+// CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' ParmVar {{.*}} 'index' 'unsigned int'
+// CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' ParmVar {{.*}} 'order_id' 'unsigned int'
+// CHECK-NEXT: AlwaysInlineAttr
+
// Subscript operators
// CHECK-SUBSCRIPT: CXXMethodDecl {{.*}} operator[] 'const hlsl_device element_type &(unsigned int) const'
diff --git a/clang/test/AST/HLSL/TypedBuffers-AST.hlsl b/clang/test/AST/HLSL/TypedBuffers-AST.hlsl
index 6074c1e8bcdd2..f7b720090d436 100644
--- a/clang/test/AST/HLSL/TypedBuffers-AST.hlsl
+++ b/clang/test/AST/HLSL/TypedBuffers-AST.hlsl
@@ -92,6 +92,28 @@ RESOURCE<float> Buffer;
// CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' ParmVar {{.*}} 'index' 'unsigned int'
// CHECK-NEXT: AlwaysInlineAttr
+// Constructor from implicit binding
+
+// CHECK: CXXConstructorDecl {{.*}} [[RESOURCE]]<element_type> 'void (unsigned int, int, unsigned int, unsigned int)' inline
+// CHECK-NEXT: ParmVarDecl {{.*}} spaceNo 'unsigned int'
+// CHECK-NEXT: ParmVarDecl {{.*}} range 'int'
+// CHECK-NEXT: ParmVarDecl {{.*}} index 'unsigned int'
+// CHECK-NEXT: ParmVarDecl {{.*}} order_id 'unsigned int'
+// CHECK-NEXT: CompoundStmt {{.*}}
+// CHECK-NEXT: BinaryOperator {{.*}} '='
+// CHECK-NEXT: MemberExpr {{.*}} lvalue .__handle
+// CHECK-NEXT: CXXThisExpr {{.*}} '[[RESOURCE]]<element_type>' lvalue implicit this
+// CHECK-NEXT: CallExpr {{.*}} '__hlsl_resource_t
+// CHECK-NEXT: ImplicitCastExpr {{.*}} <BuiltinFnToFnPtr>
+// CHECK-NEXT: DeclRefExpr {{.*}} '<builtin fn type>' Function {{.*}} '__builtin_hlsl_resource_handlefromimplicitbinding'
+// CHECK-NEXT: MemberExpr {{.*}} lvalue .__handle
+// CHECK-NEXT: CXXThisExpr {{.*}} '[[RESOURCE]]<element_type>' lvalue implicit this
+// CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' ParmVar {{.*}} 'spaceNo' 'unsigned int'
+// CHECK-NEXT: DeclRefExpr {{.*}} 'int' ParmVar {{.*}} 'range' 'int'
+// CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' ParmVar {{.*}} 'index' 'unsigned int'
+// CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' ParmVar {{.*}} 'order_id' 'unsigned int'
+// CHECK-NEXT: AlwaysInlineAttr
+
// Subsctript operators
// CHECK: CXXMethodDecl {{.*}} operator[] 'const hlsl_device element_type &(unsigned int) const'
diff --git a/clang/test/CodeGenHLSL/GlobalConstructorLib.hlsl b/clang/test/CodeGenHLSL/GlobalConstructorLib.hlsl
index b798c2a6d6c4b..1d451acfc6214 100644
--- a/clang/test/CodeGenHLSL/GlobalConstructorLib.hlsl
+++ b/clang/test/CodeGenHLSL/GlobalConstructorLib.hlsl
@@ -33,7 +33,7 @@ void SecondEntry() {}
// Verify the constructor is alwaysinline
// NOINLINE: ; Function Attrs: {{.*}}alwaysinline
-// NOINLINE-NEXT: define linkonce_odr void @_ZN4hlsl8RWBufferIfEC2Ev({{.*}} [[CtorAttr:\#[0-9]+]]
+// NOINLINE-NEXT: define linkonce_odr void @_ZN4hlsl8RWBufferIfEC2Ejijj({{.*}} [[CtorAttr:\#[0-9]+]]
// NOINLINE: ; Function Attrs: {{.*}}alwaysinline
// NOINLINE-NEXT: define internal void @_GLOBAL__sub_I_GlobalConstructorLib.hlsl() [[InitAttr:\#[0-9]+]]
diff --git a/clang/test/CodeGenHLSL/builtins/ByteAddressBuffers-constructors.hlsl b/clang/test/CodeGenHLSL/builtins/ByteAddressBuffers-constructors.hlsl
index d7c4b03552cdc..7bc9b624ba9b9 100644
--- a/clang/test/CodeGenHLSL/builtins/ByteAddressBuffers-constructors.hlsl
+++ b/clang/test/CodeGenHLSL/builtins/ByteAddressBuffers-constructors.hlsl
@@ -55,11 +55,33 @@ export void foo() {
// CHECK-SAME: i32 noundef %0, i32 noundef %1, i32 noundef %2, i32 noundef %3)
// CHECK-NEXT: ret void
-// Buf2 initialization part 1 - FIXME: constructor with implicit binding does not exist yet;
-// the global init function currently calls the default RWByteAddressBuffer C1 constructor
-// CHECK: define internal void @__cxx_global_var_init.1()
+// Buf2 initialization part 1 - global init function that calls RWByteAddressBuffer C1 constructor with implicit binding
+// CHECK: define internal void @__cxx_global_var_init.1() #0 {
// CHECK-NEXT: entry:
-// CHECK-NEXT: call void @_ZN4hlsl19RWByteAddressBufferC1Ev(ptr noundef nonnull align 4 dereferenceable(4) @_ZL4Buf2)
+// CHECK-NEXT: call void @_ZN4hlsl19RWByteAddressBufferC1Ejijj(ptr noundef nonnull align 4 dereferenceable(4) @_ZL4Buf2,
+// CHECK-SAME: i32 noundef 0, i32 noundef 1, i32 noundef 0, i32 noundef 0)
+
+// Buf2 initialization part 2 - body of RWByteAddressBuffer C1 constructor with implicit binding that calls the C2 constructor
+// CHECK: define linkonce_odr void @_ZN4hlsl19RWByteAddressBufferC1Ejijj(ptr noundef nonnull align 4 dereferenceable(4) %this,
+// CHECK-SAME: i32 noundef %spaceNo, i32 noundef %range, i32 noundef %index, i32 noundef %order_id)
+// CHECK-NEXT: entry:
+// CHECK-NEXT: %this.addr = alloca ptr, align 4
+// CHECK-NEXT: %spaceNo.addr = alloca i32, align 4
+// CHECK-NEXT: %range.addr = alloca i32, align 4
+// CHECK-NEXT: %index.addr = alloca i32, align 4
+// CHECK-NEXT: %order_id.addr = alloca i32, align 4
+// CHECK-NEXT: store ptr %this, ptr %this.addr, align 4
+// CHECK-NEXT: store i32 %spaceNo, ptr %spaceNo.addr, align 4
+// CHECK-NEXT: store i32 %range, ptr %range.addr, align 4
+// CHECK-NEXT: store i32 %index, ptr %index.addr, align 4
+// CHECK-NEXT: store i32 %order_id, ptr %order_id.addr, align 4
+// CHECK-NEXT: %this1 = load ptr, ptr %this.addr, align 4
+// CHECK-NEXT: %0 = load i32, ptr %spaceNo.addr, align 4
+// CHECK-NEXT: %1 = load i32, ptr %range.addr, align 4
+// CHECK-NEXT: %2 = load i32, ptr %index.addr, align 4
+// CHECK-NEXT: %3 = load i32, ptr %order_id.addr, align 4
+// CHECK-NEXT: call void @_ZN4hlsl19RWByteAddressBufferC2Ejijj(ptr noundef nonnull align 4 dereferenceable(4) %this1, i32 noundef %0, i32 noundef %1, i32 noundef %2, i32 noundef %3) #4
+// CHECK-NEXT: ret void
// Buf3 initialization part 1 - local variable declared in function foo() is initialized by
// RasterizerOrderedByteAddressBuffer C1 default constructor
@@ -104,6 +126,31 @@ export void foo() {
// CHECK-DXIL-NEXT: store target("dx.RawBuffer", i8, 0, 0) %4, ptr %__handle, align 4
// CHECK-NEXT: ret void
+// Buf2 initialization part 3 - body of RWByteAddressBuffer C2 constructor with implicit binding that initializes
+// handle with @llvm.dx.resource.handlefromimplicitbinding
+// CHECK: define linkonce_odr void @_ZN4hlsl19RWByteAddressBufferC2Ejijj(ptr noundef nonnull align 4 dereferenceable(4) %this,
+// CHECK-SAME: i32 noundef %spaceNo, i32 noundef %range, i32 noundef %index, i32 noundef %order_id) unnamed_addr #1 align 2 {
+// CHECK-NEXT: entry:
+// CHECK-NEXT: %this.addr = alloca ptr, align 4
+// CHECK-NEXT: %spaceNo.addr = alloca i32, align 4
+// CHECK-NEXT: %range.addr = alloca i32, align 4
+// CHECK-NEXT: %index.addr = alloca i32, align 4
+// CHECK-NEXT: %order_id.addr = alloca i32, align 4
+// CHECK-NEXT: store ptr %this, ptr %this.addr, align 4
+// CHECK-NEXT: store i32 %spaceNo, ptr %spaceNo.addr, align 4
+// CHECK-NEXT: store i32 %range, ptr %range.addr, align 4
+// CHECK-NEXT: store i32 %index, ptr %index.addr, align 4
+// CHECK-NEXT: store i32 %order_id, ptr %order_id.addr, align 4
+// CHECK-NEXT: %this1 = load ptr, ptr %this.addr, align 4
+// CHECK-NEXT: %0 = load i32, ptr %spaceNo.addr, align 4
+// CHECK-NEXT: %1 = load i32, ptr %range.addr, align 4
+// CHECK-NEXT: %2 = load i32, ptr %index.addr, align 4
+// CHECK-NEXT: %3 = load i32, ptr %order_id.addr, align 4
+// CHECK-NEXT: %4 = call target("dx.RawBuffer", i8, 1, 0) @llvm.dx.resource.handlefromimplicitbinding.tdx.RawBuffer_i8_1_0t(i32 %3, i32 %0, i32 %1, i32 %2, i1 false)
+// CHECK-NEXT: %__handle = getelementptr inbounds nuw %"class.hlsl::RWByteAddressBuffer", ptr %this1, i32 0, i32 0
+// CHECK-NEXT: store target("dx.RawBuffer", i8, 1, 0) %4, ptr %__handle, align 4
+// CHECK-NEXT: ret void
+
// Buf3 initialization part 3 - body of RasterizerOrderedByteAddressBuffer default C2 constructor that
// initializes handle to poison
// CHECK: define linkonce_odr void @_ZN4hlsl34RasterizerOrderedByteAddressBufferC2Ev(ptr noundef nonnull align 4 dereferenceable(4) %this)
diff --git a/clang/test/CodeGenHLSL/builtins/RWBuffer-constructor.hlsl b/clang/test/CodeGenHLSL/builtins/RWBuffer-constructor.hlsl
index adf231dedf4cb..7335a3938f476 100644
--- a/clang/test/CodeGenHLSL/builtins/RWBuffer-constructor.hlsl
+++ b/clang/test/CodeGenHLSL/builtins/RWBuffer-constructor.hlsl
@@ -55,11 +55,33 @@ export void foo() {
// CHECK-SAME: i32 noundef %0, i32 noundef %1, i32 noundef %2, i32 noundef %3)
// CHECK-NEXT: ret void
-// Buf2 initialization part 1 - FIXME: constructor with implicit binding does not exist yet;
-// the global init function currently calls the default RWBufer<double> C1 constructor
-// CHECK: define internal void @__cxx_global_var_init.1() #0 {
+// Buf2 initialization part 1 - global init function that calls RWBuffer<float> C1 constructor with implicit binding
+// CHECK: define internal void @__cxx_global_var_init.1()
// CHECK-NEXT: entry:
-// CHECK-NEXT: call void @_ZN4hlsl8RWBufferIdEC1Ev(ptr noundef nonnull align 4 dereferenceable(4) @_ZL4Buf2)
+// CHECK-NEXT: call void @_ZN4hlsl8RWBufferIdEC1Ejijj(ptr noundef nonnull align 4 dereferenceable(4) @_ZL4Buf2,
+// CHECK-SAME: i32 noundef 0, i32 noundef 1, i32 noundef 0, i32 noundef 0)
+
+// Buf2 initialization part 2 - body of RWBuffer<float> C1 constructor with implicit binding that calls the C2 constructor
+// CHECK: define linkonce_odr void @_ZN4hlsl8RWBufferIdEC1Ejijj(ptr noundef nonnull align 4 dereferenceable(4) %this,
+// CHECK-SAME: i32 noundef %spaceNo, i32 noundef %range, i32 noundef %index, i32 noundef %order_id)
+// CHECK-NEXT: entry:
+// CHECK-NEXT: %this.addr = alloca ptr, align 4
+// CHECK-NEXT: %spaceNo.addr = alloca i32, align 4
+// CHECK-NEXT: %range.addr = alloca i32, align 4
+// CHECK-NEXT: %index.addr = alloca i32, align 4
+// CHECK-NEXT: %order_id.addr = alloca i32, align 4
+// CHECK-NEXT: store ptr %this, ptr %this.addr, align 4
+// CHECK-NEXT: store i32 %spaceNo, ptr %spaceNo.addr, align 4
+// CHECK-NEXT: store i32 %range, ptr %range.addr, align 4
+// CHECK-NEXT: store i32 %index, ptr %index.addr, align 4
+// CHECK-NEXT: store i32 %order_id, ptr %order_id.addr, align 4
+// CHECK-NEXT: %this1 = load ptr, ptr %this.addr, align 4
+// CHECK-NEXT: %0 = load i32, ptr %spaceNo.addr, align 4
+// CHECK-NEXT: %1 = load i32, ptr %range.addr, align 4
+// CHECK-NEXT: %2 = load i32, ptr %index.addr, align 4
+// CHECK-NEXT: %3 = load i32, ptr %order_id.addr, align 4
+// CHECK-NEXT: call void @_ZN4hlsl8RWBufferIdEC2Ejijj(ptr noundef nonnull align 4 dereferenceable(4) %this1, i32 noundef %0, i32 noundef %1, i32 noundef %2, i32 noundef %3) #4
+// CHECK-NEXT: ret void
// Buf3 initialization part 1 - local variable declared in function foo() is initialized by RWBuffer<int> C1 default constructor
// CHECK: define void @_Z3foov()
@@ -102,6 +124,31 @@ export void foo() {
// CHECK-DXIL-NEXT: store target("dx.TypedBuffer", float, 1, 0, 0) %4, ptr %__handle, align 4
// CHECK-NEXT: ret void
+// Buf2 initialization part 3 - body of RWBuffer<float> C2 constructor with implicit binding that initializes
+// handle with @llvm.dx.resource.handlefromimplicitbinding
+// CHECK: define linkonce_odr void @_ZN4hlsl8RWBufferIdEC2Ejijj(ptr noundef nonnull align 4 dereferenceable(4) %this,
+// CHECK-SAME: i32 noundef %spaceNo, i32 noundef %range, i32 noundef %index, i32 noundef %order_id) unnamed_addr #1 align 2 {
+// CHECK-NEXT: entry:
+// CHECK-NEXT: %this.addr = alloca ptr, align 4
+// CHECK-NEXT: %spaceNo.addr = alloca i32, align 4
+// CHECK-NEXT: %range.addr = alloca i32, align 4
+// CHECK-NEXT: %index.addr = alloca i32, align 4
+// CHECK-NEXT: %order_id.addr = alloca i32, align 4
+// CHECK-NEXT: store ptr %this, ptr %this.addr, align 4
+// CHECK-NEXT: store i32 %spaceNo, ptr %spaceNo.addr, align 4
+// CHECK-NEXT: store i32 %range, ptr %range.addr, align 4
+// CHECK-NEXT: store i32 %index, ptr %index.addr, align 4
+// CHECK-NEXT: store i32 %order_id, ptr %order_id.addr, align 4
+// CHECK-NEXT: %this1 = load ptr, ptr %this.addr, align 4
+// CHECK-NEXT: %0 = load i32, ptr %spaceNo.addr, align 4
+// CHECK-NEXT: %1 = load i32, ptr %range.addr, align 4
+// CHECK-NEXT: %2 = load i32, ptr %index.addr, align 4
+// CHECK-NEXT: %3 = load i32, ptr %order_id.addr, align 4
+// CHECK-NEXT: %4 = call target("dx.TypedBuffer", double, 1, 0, 0) @llvm.dx.resource.handlefromimplicitbinding.tdx.TypedBuffer_f64_1_0_0t(i32 %3, i32 %0, i32 %1, i32 %2, i1 false)
+// CHECK-NEXT: %__handle = getelementptr inbounds nuw %"class.hlsl::RWBuffer.0", ptr %this1, i32 0, i32 0
+// CHECK-NEXT: store target("dx.TypedBuffer", double, 1, 0, 0) %4, ptr %__handle, align 4
+// CHECK-NEXT: ret void
+
// Buf3 initialization part 3 - body of RWBuffer<int> default C2 constructor that initializes handle to poison
// CHECK: define linkonce_odr void @_ZN4hlsl8RWBufferIiEC2Ev(ptr noundef nonnull align 4 dereferenceable(4) %this)
// CHECK-NEXT: entry:
diff --git a/clang/test/CodeGenHLSL/builtins/StructuredBuffers-constructors.hlsl b/clang/test/CodeGenHLSL/builtins/StructuredBuffers-constructors.hlsl
index ea818a737cf74..f77712a1148af 100644
--- a/clang/test/CodeGenHLSL/builtins/StructuredBuffers-constructors.hlsl
+++ b/clang/test/CodeGenHLSL/builtins/StructuredBuffers-constructors.hlsl
@@ -56,11 +56,34 @@ export void foo() {
// CHECK-SAME: i32 noundef %0, i32 noundef %1, i32 noundef %2, i32 noundef %3)
// CHECK-NEXT: ret void
-// Buf2 initialization part 1 - FIXME: constructor with implicit binding does not exist yet;
-// the global init function currently calls the default RWStructuredBufer<double> C1 constructor
+// Buf2 initialization part 1 - global init function that calls RWStructuredBuffer<float> C1 constructor with
+// implicit binding
// CHECK: define internal void @__cxx_global_var_init.1()
// CHECK-NEXT: entry:
-// CHECK-NEXT: call void @_ZN4hlsl18RWStructuredBufferIfEC1Ev(ptr noundef nonnull align 4 dereferenceable(4) @_ZL4Buf2)
+// CHECK-NEXT: call void @_ZN4hlsl18RWStructuredBufferIfEC1Ejijj(ptr noundef nonnull align 4 dereferenceable(4) @_ZL4Buf2,
+// CHECK-SAME: i32 noundef 0, i32 noundef 1, i32 noundef 0, i32 noundef 0)
+
+// Buf2 initialization part 2 - body of RWStructuredBuffer<float> C1 constructor with implicit binding that calls the C2 constructor
+// CHECK: define linkonce_odr void @_ZN4hlsl18RWStructuredBufferIfEC1Ejijj(ptr noundef nonnull align 4 dereferenceable(4) %this,
+// CHECK-SAME: i32 noundef %spaceNo, i32 noundef %range, i32 noundef %index, i32 noundef %order_id)
+// CHECK-NEXT: entry:
+// CHECK-NEXT: %this.addr = alloca ptr, align 4
+// CHECK-NEXT: %spaceNo.addr = alloca i32, align 4
+// CHECK-NEXT: %range.addr = alloca i32, align 4
+// CHECK-NEXT: %index.addr = alloca i32, align 4
+// CHECK-NEXT: %order_id.addr = alloca i32, align 4
+// CHECK-NEXT: store ptr %this, ptr %this.addr, align 4
+// CHECK-NEXT: store i32 %spaceNo, ptr %spaceNo.addr, align 4
+// CHECK-NEXT: store i32 %range, ptr %range.addr, align 4
+// CHECK-NEXT: store i32 %index, ptr %index.addr, align 4
+// CHECK-NEXT: store i32 %order_id, ptr %order_id.addr, align 4
+// CHECK-NEXT: %this1 = load ptr, ptr %this.addr, align 4
+// CHECK-NEXT: %0 = load i32, ptr %spaceNo.addr, align 4
+// CHECK-NEXT: %1 = load i32, ptr %range.addr, align 4
+// CHECK-NEXT: %2 = load i32, ptr %index.addr, align 4
+// CHECK-NEXT: %3 = load i32, ptr %order_id.addr, align 4
+// CHECK-NEXT: call void @_ZN4hlsl18RWStructuredBufferIfEC2Ejijj(ptr noundef nonnull align 4 dereferenceable(4) %this1, i32 noundef %0, i32 noundef %1, i32 noundef %2, i32 noundef %3) #4
+// CHECK-NEXT: ret void
// Buf3 initialization part 1 - local variable declared in function foo() is initialized by
// AppendStructuredBuffer<float> C1 default constructor
@@ -105,6 +128,31 @@ export void foo() {
// CHECK-DXIL-NEXT: store target("dx.RawBuffer", float, 0, 0) %4, ptr %__handle, align 4
// CHECK-NEXT: ret void
+// Buf2 initialization part 3 - body of RWStructuredBuffer<float> C2 constructor with implicit binding that initializes
+// handle with @llvm.dx.resource.handlefromimplicitbinding
+// CHECK: define linkonce_odr void @_ZN4hlsl18RWStructuredBufferIfEC2Ejijj(ptr noundef nonnull align 4 dereferenceable(4) %this,
+// CHECK-SAME: i32 noundef %spaceNo, i32 noundef %range, i32 noundef %index, i32 noundef %order_id) unnamed_addr #1 align 2 {
+// CHECK-NEXT: entry:
+// CHECK-NEXT: %this.addr = alloca ptr, align 4
+// CHECK-NEXT: %spaceNo.addr = alloca i32, align 4
+// CHECK-NEXT: %range.addr = alloca i32, align 4
+// CHECK-NEXT: %index.addr = alloca i32, align 4
+// CHECK-NEXT: %order_id.addr = alloca i32, align 4
+// CHECK-NEXT: store ptr %this, ptr %this.addr, align 4
+// CHECK-NEXT: store i32 %spaceNo, ptr %spaceNo.addr, align 4
+// CHECK-NEXT: store i32 %range, ptr %range.addr, align 4
+// CHECK-NEXT: store i32 %index, ptr %index.addr, align 4
+// CHECK-NEXT: store i32 %order_id, ptr %order_id.addr, align 4
+// CHECK-NEXT: %this1 = load ptr, ptr %this.addr, align 4
+// CHECK-NEXT: %0 = load i32, ptr %spaceNo.addr, align 4
+// CHECK-NEXT: %1 = load i32, ptr %range.addr, align 4
+// CHECK-NEXT: %2 = load i32, ptr %index.addr, align 4
+// CHECK-NEXT: %3 = load i32, ptr %order_id.addr, align 4
+// CHECK-NEXT: %4 = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefromimplicitbinding.tdx.RawBuffer_f32_1_0t(i32 %3, i32 %0, i32 %1, i32 %2, i1 false)
+// CHECK-NEXT: %__handle = getelementptr inbounds nuw %"class.hlsl::RWStructuredBuffer", ptr %this1, i32 0, i32 0
+// CHECK-NEXT: store target("dx.RawBuffer", float, 1, 0) %4, ptr %__handle, align 4
+// CHECK-NEXT: ret void
+
// Buf3 initialization part 3 - body of AppendStructuredBuffer<float> default C2 constructor that
// initializes handle to poison
// CHECK: define linkonce_odr void @_ZN4hlsl22AppendStructuredBufferIfEC2Ev(ptr noundef nonnull align 4 dereferenceable(4) %this)
diff --git a/clang/test/CodeGenHLSL/builtins/StructuredBuffers-methods-ps.hlsl b/clang/test/CodeGenHLSL/builtins/StructuredBuffers-methods-ps.hlsl
index f2aea4e376b03..47afba27b6ebd 100644
--- a/clang/test/CodeGenHLSL/builtins/StructuredBuffers-methods-ps.hlsl
+++ b/clang/test/CodeGenHLSL/builtins/StructuredBuffers-methods-ps.hlsl
@@ -32,6 +32,6 @@ export float TestLoad() {
// CHECK: %[[PTR1:.*]] = call ptr @llvm.dx.resource.getpointer.p0.tdx.RawBuffer_f32_1_1t(target("dx.RawBuffer", float, 1, 1) %{{[0-9]+}}, i32 %{{[0-9]+}})
// CHECK: %[[VALUE1:.*]] = load float, ptr %[[PTR1]]
-// CHECK: declare i32 @llvm.dx.resource.updatecounter.tdx.RawBuffer_f32_1_0t(target("dx.RawBuffer", float, 1, 0), i8) #3
-// CHECK: declare i32 @llvm.dx.resource.updatecounter.tdx.RawBuffer_f32_1_1t(target("dx.RawBuffer", float, 1, 1), i8) #3
-// CHECK: declare ptr @llvm.dx.resource.getpointer.p0.tdx.RawBuffer_f32_1_1t(target("dx.RawBuffer", float, 1, 1), i32) #4
+// CHECK: declare i32 @llvm.dx.resource.updatecounter.tdx.RawBuffer_f32_1_0t(target("dx.RawBuffer", float, 1, 0), i8)
+// CHECK: declare i32 @llvm.dx.resource.updatecounter.tdx.RawBuffer_f32_1_1t(target("dx.RawBuffer", float, 1, 1), i8)
+// CHECK: declare ptr @llvm.dx.resource.getpointer.p0.tdx.RawBuffer_f32_1_1t(target("dx.RawBuffer", float, 1, 1), i32)
diff --git a/clang/test/CodeGenHLSL/static-local-ctor.hlsl b/clang/test/CodeGenHLSL/static-local-ctor.hlsl
index 7aeb5e987d6b2..474bcf1aff6ac 100644
--- a/clang/test/CodeGenHLSL/static-local-ctor.hlsl
+++ b/clang/test/CodeGenHLSL/static-local-ctor.hlsl
@@ -21,7 +21,7 @@ void InitBuf(RWBuffer<int> buf) {
// CHECK-NEXT: br i1 [[Tmp3]]
// CHECK-NOT: _Init_thread_header
// CHECK: init.check:
-// CHECK-NEXT: call void @_ZN4hlsl8RWBufferIiEC1Ev
+// CHECK-NEXT: call void @_ZN4hlsl8RWBufferIiEC1Ejijj
// CHECK-NEXT: store i8 1, ptr @_ZGVZ4mainvE5mybuf
// CHECK-NOT: _Init_thread_footer
diff --git a/llvm/include/llvm/IR/IntrinsicsSPIRV.td b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
index 404467781b4d0..8d984d6ce58df 100644
--- a/llvm/include/llvm/IR/IntrinsicsSPIRV.td
+++ b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
@@ -119,6 +119,11 @@ let TargetPrefix = "spv" in {
[llvm_any_ty],
[llvm_i32_ty, llvm_i32_ty, llvm_i32_ty, llvm_i32_ty, llvm_i1_ty],
[IntrNoMem]>;
+ def int_spv_resource_handlefromimplicitbinding
+ : DefaultAttrsIntrinsic<
+ [llvm_any_ty],
+ [llvm_i32_ty, llvm_i32_ty, llvm_i32_ty, llvm_i32_ty, llvm_i1_ty],
+ [IntrNoMem]>;
def int_spv_firstbituhigh : DefaultAttrsIntrinsic<[LLVMScalarOrSameVectorWidth<0, llvm_i32_ty>], [llvm_anyint_ty], [IntrNoMem]>;
def int_spv_firstbitshigh : DefaultAttrsIntrinsic<[LLVMScalarOrSameVectorWidth<0, llvm_i32_ty>], [llvm_anyint_ty], [IntrNoMem]>;
>From 8a2770801c06e9f68bd332246417ccf7cbb1b2b4 Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Fri, 9 May 2025 10:44:06 -0700
Subject: [PATCH 10/14] code review feedback - use std::optional, renames
---
llvm/include/llvm/Analysis/DXILResource.h | 6 +--
llvm/lib/Analysis/DXILResource.cpp | 48 +++++++++----------
.../DirectX/DXILResourceImplicitBinding.cpp | 17 ++++---
3 files changed, 35 insertions(+), 36 deletions(-)
diff --git a/llvm/include/llvm/Analysis/DXILResource.h b/llvm/include/llvm/Analysis/DXILResource.h
index fd34fdfda4c1c..7ab069d94742f 100644
--- a/llvm/include/llvm/Analysis/DXILResource.h
+++ b/llvm/include/llvm/Analysis/DXILResource.h
@@ -633,7 +633,7 @@ class DXILResourceBindingInfo {
FreeRanges.emplace_back(0, UINT32_MAX);
}
// Size == -1 means unbounded array
- bool findAvailableBinding(int32_t Size, uint32_t *RegSlot);
+ std::optional<uint32_t> findAvailableBinding(int32_t Size);
};
struct BindingSpaces {
@@ -678,8 +678,8 @@ class DXILResourceBindingInfo {
}
// Size == -1 means unbounded array
- bool findAvailableBinding(dxil::ResourceClass RC, uint32_t Space,
- int32_t Size, uint32_t *RegSlot);
+ std::optional<uint32_t> findAvailableBinding(dxil::ResourceClass RC,
+ uint32_t Space, int32_t Size);
friend class DXILResourceBindingAnalysis;
friend class DXILResourceBindingWrapperPass;
diff --git a/llvm/lib/Analysis/DXILResource.cpp b/llvm/lib/Analysis/DXILResource.cpp
index 7d721d4b16297..ce809c2080805 100644
--- a/llvm/lib/Analysis/DXILResource.cpp
+++ b/llvm/lib/Analysis/DXILResource.cpp
@@ -995,12 +995,12 @@ void DXILResourceBindingInfo::populate(Module &M, DXILResourceTypeMap &DRTM) {
}
// returns false if binding could not be found in given space
-bool DXILResourceBindingInfo::findAvailableBinding(dxil::ResourceClass RC,
- uint32_t Space, int32_t Size,
- uint32_t *RegSlot) {
+std::optional<uint32_t>
+DXILResourceBindingInfo::findAvailableBinding(dxil::ResourceClass RC,
+ uint32_t Space, int32_t Size) {
BindingSpaces &BS = getBindingSpaces(RC);
RegisterSpace &RS = BS.getOrInsertSpace(Space);
- return RS.findAvailableBinding(Size, RegSlot);
+ return RS.findAvailableBinding(Size);
}
DXILResourceBindingInfo::RegisterSpace &
@@ -1015,12 +1015,13 @@ DXILResourceBindingInfo::BindingSpaces::getOrInsertSpace(uint32_t Space) {
return Spaces.emplace_back(Space);
}
-bool DXILResourceBindingInfo::RegisterSpace::findAvailableBinding(
- int32_t Size, uint32_t *RegSlot) {
+std::optional<uint32_t>
+DXILResourceBindingInfo::RegisterSpace::findAvailableBinding(int32_t Size) {
assert((Size == -1 || Size > 0) && "invalid size");
+ std::optional<uint32_t> RegSlot;
if (FreeRanges.empty())
- return false;
+ return RegSlot;
// unbounded array
if (Size == -1) {
@@ -1028,25 +1029,24 @@ bool DXILResourceBindingInfo::RegisterSpace::findAvailableBinding(
if (Last.UpperBound != UINT32_MAX)
// this space is already occupied by an unbounded array
return false;
- *RegSlot = Last.LowerBound;
+ RegSlot = Last.LowerBound;
FreeRanges.pop_back();
- return true;
- }
-
- // single resource or fixed-size array
- for (BindingRange &R : FreeRanges) {
- // compare the size as uint64_t to prevent overflow for range (0,
- // UINT32_MAX)
- if ((uint64_t)R.UpperBound - R.LowerBound + 1 < (uint64_t)Size)
- continue;
- *RegSlot = R.LowerBound;
- // This might create a range where (LowerBound == UpperBound + 1), but
- // that's ok.
- R.LowerBound += Size;
- return true;
+ } else {
+ // single resource or fixed-size array
+ for (BindingRange &R : FreeRanges) {
+ // compare the size as uint64_t to prevent overflow for range (0,
+ // UINT32_MAX)
+ if ((uint64_t)R.UpperBound - R.LowerBound + 1 < (uint64_t)Size)
+ continue;
+ RegSlot = R.LowerBound;
+ // This might create a range where (LowerBound == UpperBound + 1). When
+ // that happens, the next time this function is called the range will
+ // skipped over by the check above (at this point Size is always > 0).
+ R.LowerBound += Size;
+ break;
+ }
}
-
- return false;
+ return RegSlot;
}
//===----------------------------------------------------------------------===//
diff --git a/llvm/lib/Target/DirectX/DXILResourceImplicitBinding.cpp b/llvm/lib/Target/DirectX/DXILResourceImplicitBinding.cpp
index 80293ad7c9716..73eedaf51b425 100644
--- a/llvm/lib/Target/DirectX/DXILResourceImplicitBinding.cpp
+++ b/llvm/lib/Target/DirectX/DXILResourceImplicitBinding.cpp
@@ -40,13 +40,13 @@ static void diagnoseImplicitBindingNotFound(CallInst *ImplBindingCall) {
static bool assignBindings(Module &M, DXILResourceBindingInfo &DRBI,
DXILResourceTypeMap &DRTM) {
- struct ImplBindingCall {
+ struct ImplicitBindingCall {
int OrderID;
CallInst *Call;
- ImplBindingCall(int OrderID, CallInst *Call)
+ ImplicitBindingCall(int OrderID, CallInst *Call)
: OrderID(OrderID), Call(Call) {}
};
- SmallVector<ImplBindingCall> Calls;
+ SmallVector<ImplicitBindingCall> Calls;
SmallVector<Function *> FunctionsToMaybeRemove;
// collect all of the llvm.dx.resource.handlefromImplicitbinding calls
@@ -78,7 +78,7 @@ static bool assignBindings(Module &M, DXILResourceBindingInfo &DRBI,
bool AllBindingsAssigned = true;
bool Changed = false;
- for (auto &IB : Calls) {
+ for (ImplicitBindingCall &IB : Calls) {
IRBuilder<> Builder(IB.Call);
if (IB.OrderID != LastOrderID) {
@@ -91,15 +91,14 @@ static bool assignBindings(Module &M, DXILResourceBindingInfo &DRBI,
int32_t Size =
cast<ConstantInt>(IB.Call->getArgOperand(2))->getZExtValue();
- uint32_t RegSlot;
- RegSlotOp = nullptr;
- if (!DRBI.findAvailableBinding(RTI.getResourceClass(), Space, Size,
- &RegSlot)) {
+ std::optional<uint32_t> RegSlot =
+ DRBI.findAvailableBinding(RTI.getResourceClass(), Space, Size);
+ if (!RegSlot) {
diagnoseImplicitBindingNotFound(IB.Call);
AllBindingsAssigned = false;
continue;
}
- RegSlotOp = ConstantInt::get(Builder.getInt32Ty(), RegSlot);
+ RegSlotOp = ConstantInt::get(Builder.getInt32Ty(), RegSlot.value());
}
if (!RegSlotOp)
>From 4a3e4c597bdc355c3a4d676a34cd5dd9f69361dc Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Fri, 9 May 2025 11:20:08 -0700
Subject: [PATCH 11/14] revert changes to StructuredBuffers-methods-ps.hlsl
---
.../CodeGenHLSL/builtins/StructuredBuffers-methods-ps.hlsl | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/clang/test/CodeGenHLSL/builtins/StructuredBuffers-methods-ps.hlsl b/clang/test/CodeGenHLSL/builtins/StructuredBuffers-methods-ps.hlsl
index 47afba27b6ebd..f2aea4e376b03 100644
--- a/clang/test/CodeGenHLSL/builtins/StructuredBuffers-methods-ps.hlsl
+++ b/clang/test/CodeGenHLSL/builtins/StructuredBuffers-methods-ps.hlsl
@@ -32,6 +32,6 @@ export float TestLoad() {
// CHECK: %[[PTR1:.*]] = call ptr @llvm.dx.resource.getpointer.p0.tdx.RawBuffer_f32_1_1t(target("dx.RawBuffer", float, 1, 1) %{{[0-9]+}}, i32 %{{[0-9]+}})
// CHECK: %[[VALUE1:.*]] = load float, ptr %[[PTR1]]
-// CHECK: declare i32 @llvm.dx.resource.updatecounter.tdx.RawBuffer_f32_1_0t(target("dx.RawBuffer", float, 1, 0), i8)
-// CHECK: declare i32 @llvm.dx.resource.updatecounter.tdx.RawBuffer_f32_1_1t(target("dx.RawBuffer", float, 1, 1), i8)
-// CHECK: declare ptr @llvm.dx.resource.getpointer.p0.tdx.RawBuffer_f32_1_1t(target("dx.RawBuffer", float, 1, 1), i32)
+// CHECK: declare i32 @llvm.dx.resource.updatecounter.tdx.RawBuffer_f32_1_0t(target("dx.RawBuffer", float, 1, 0), i8) #3
+// CHECK: declare i32 @llvm.dx.resource.updatecounter.tdx.RawBuffer_f32_1_1t(target("dx.RawBuffer", float, 1, 1), i8) #3
+// CHECK: declare ptr @llvm.dx.resource.getpointer.p0.tdx.RawBuffer_f32_1_1t(target("dx.RawBuffer", float, 1, 1), i32) #4
>From 2ff496baf91d4a7682d6d148447db4d7698f87e9 Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Tue, 13 May 2025 19:16:26 -0700
Subject: [PATCH 12/14] code review feedback - update comment, rename param
---
clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp | 2 +-
clang/lib/Sema/SemaHLSL.cpp | 2 +-
clang/test/AST/HLSL/ByteAddressBuffers-AST.hlsl | 4 ++--
clang/test/AST/HLSL/StructuredBuffers-AST.hlsl | 4 ++--
clang/test/AST/HLSL/TypedBuffers-AST.hlsl | 4 ++--
.../ByteAddressBuffers-constructors.hlsl | 16 ++++++++--------
.../builtins/RWBuffer-constructor.hlsl | 16 ++++++++--------
.../builtins/StructuredBuffers-constructors.hlsl | 16 ++++++++--------
8 files changed, 32 insertions(+), 32 deletions(-)
diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
index d5fbd5f6ecc9f..7a561638aebbc 100644
--- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
+++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
@@ -681,7 +681,7 @@ BuiltinTypeDeclBuilder::addHandleConstructorFromImplicitBinding() {
.addParam("spaceNo", AST.UnsignedIntTy)
.addParam("range", AST.IntTy)
.addParam("index", AST.UnsignedIntTy)
- .addParam("order_id", AST.UnsignedIntTy)
+ .addParam("orderId", AST.UnsignedIntTy)
.callBuiltin("__builtin_hlsl_resource_handlefromimplicitbinding",
HandleType, PH::Handle, PH::_0, PH::_1, PH::_2, PH::_3)
.assign(PH::Handle, PH::LastStmt)
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 8f3c7ac67e5bb..7e80b7f8a692d 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -3342,7 +3342,7 @@ bool SemaHLSL::initGlobalResourceDecl(VarDecl *VD) {
return initVarDeclWithCtor(SemaRef, VD, Args);
}
- // resource with explicit binding
+ // resource with implicit binding
IntegerLiteral *OrderId = IntegerLiteral::Create(
AST, llvm::APInt(UIntTySize, getNextImplicitBindingOrderID()),
AST.UnsignedIntTy, SourceLocation());
diff --git a/clang/test/AST/HLSL/ByteAddressBuffers-AST.hlsl b/clang/test/AST/HLSL/ByteAddressBuffers-AST.hlsl
index 99f26473dbb02..8b9aa99a5314e 100644
--- a/clang/test/AST/HLSL/ByteAddressBuffers-AST.hlsl
+++ b/clang/test/AST/HLSL/ByteAddressBuffers-AST.hlsl
@@ -84,7 +84,7 @@ RESOURCE Buffer;
// CHECK-NEXT: ParmVarDecl {{.*}} spaceNo 'unsigned int'
// CHECK-NEXT: ParmVarDecl {{.*}} range 'int'
// CHECK-NEXT: ParmVarDecl {{.*}} index 'unsigned int'
-// CHECK-NEXT: ParmVarDecl {{.*}} order_id 'unsigned int'
+// CHECK-NEXT: ParmVarDecl {{.*}} orderId 'unsigned int'
// CHECK-NEXT: CompoundStmt {{.*}}
// CHECK-NEXT: BinaryOperator {{.*}} '='
// CHECK-NEXT: MemberExpr {{.*}} lvalue .__handle
@@ -97,7 +97,7 @@ RESOURCE Buffer;
// CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' ParmVar {{.*}} 'spaceNo' 'unsigned int'
// CHECK-NEXT: DeclRefExpr {{.*}} 'int' ParmVar {{.*}} 'range' 'int'
// CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' ParmVar {{.*}} 'index' 'unsigned int'
-// CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' ParmVar {{.*}} 'order_id' 'unsigned int'
+// CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' ParmVar {{.*}} 'orderId' 'unsigned int'
// CHECK-NEXT: AlwaysInlineAttr
// CHECK-NOSUBSCRIPT-NOT: CXXMethodDecl {{.*}} operator[] 'const element_type &(unsigned int) const'
diff --git a/clang/test/AST/HLSL/StructuredBuffers-AST.hlsl b/clang/test/AST/HLSL/StructuredBuffers-AST.hlsl
index 03bfa6cb26003..f8659313ff19c 100644
--- a/clang/test/AST/HLSL/StructuredBuffers-AST.hlsl
+++ b/clang/test/AST/HLSL/StructuredBuffers-AST.hlsl
@@ -131,7 +131,7 @@ RESOURCE<float> Buffer;
// CHECK-NEXT: ParmVarDecl {{.*}} spaceNo 'unsigned int'
// CHECK-NEXT: ParmVarDecl {{.*}} range 'int'
// CHECK-NEXT: ParmVarDecl {{.*}} index 'unsigned int'
-// CHECK-NEXT: ParmVarDecl {{.*}} order_id 'unsigned int'
+// CHECK-NEXT: ParmVarDecl {{.*}} orderId 'unsigned int'
// CHECK-NEXT: CompoundStmt {{.*}}
// CHECK-NEXT: BinaryOperator {{.*}} '='
// CHECK-NEXT: MemberExpr {{.*}} lvalue .__handle
@@ -144,7 +144,7 @@ RESOURCE<float> Buffer;
// CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' ParmVar {{.*}} 'spaceNo' 'unsigned int'
// CHECK-NEXT: DeclRefExpr {{.*}} 'int' ParmVar {{.*}} 'range' 'int'
// CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' ParmVar {{.*}} 'index' 'unsigned int'
-// CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' ParmVar {{.*}} 'order_id' 'unsigned int'
+// CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' ParmVar {{.*}} 'orderId' 'unsigned int'
// CHECK-NEXT: AlwaysInlineAttr
// Subscript operators
diff --git a/clang/test/AST/HLSL/TypedBuffers-AST.hlsl b/clang/test/AST/HLSL/TypedBuffers-AST.hlsl
index f7b720090d436..dad1ef17a1f86 100644
--- a/clang/test/AST/HLSL/TypedBuffers-AST.hlsl
+++ b/clang/test/AST/HLSL/TypedBuffers-AST.hlsl
@@ -98,7 +98,7 @@ RESOURCE<float> Buffer;
// CHECK-NEXT: ParmVarDecl {{.*}} spaceNo 'unsigned int'
// CHECK-NEXT: ParmVarDecl {{.*}} range 'int'
// CHECK-NEXT: ParmVarDecl {{.*}} index 'unsigned int'
-// CHECK-NEXT: ParmVarDecl {{.*}} order_id 'unsigned int'
+// CHECK-NEXT: ParmVarDecl {{.*}} orderId 'unsigned int'
// CHECK-NEXT: CompoundStmt {{.*}}
// CHECK-NEXT: BinaryOperator {{.*}} '='
// CHECK-NEXT: MemberExpr {{.*}} lvalue .__handle
@@ -111,7 +111,7 @@ RESOURCE<float> Buffer;
// CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' ParmVar {{.*}} 'spaceNo' 'unsigned int'
// CHECK-NEXT: DeclRefExpr {{.*}} 'int' ParmVar {{.*}} 'range' 'int'
// CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' ParmVar {{.*}} 'index' 'unsigned int'
-// CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' ParmVar {{.*}} 'order_id' 'unsigned int'
+// CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' ParmVar {{.*}} 'orderId' 'unsigned int'
// CHECK-NEXT: AlwaysInlineAttr
// Subsctript operators
diff --git a/clang/test/CodeGenHLSL/builtins/ByteAddressBuffers-constructors.hlsl b/clang/test/CodeGenHLSL/builtins/ByteAddressBuffers-constructors.hlsl
index 7bc9b624ba9b9..439da886a227e 100644
--- a/clang/test/CodeGenHLSL/builtins/ByteAddressBuffers-constructors.hlsl
+++ b/clang/test/CodeGenHLSL/builtins/ByteAddressBuffers-constructors.hlsl
@@ -63,23 +63,23 @@ export void foo() {
// Buf2 initialization part 2 - body of RWByteAddressBuffer C1 constructor with implicit binding that calls the C2 constructor
// CHECK: define linkonce_odr void @_ZN4hlsl19RWByteAddressBufferC1Ejijj(ptr noundef nonnull align 4 dereferenceable(4) %this,
-// CHECK-SAME: i32 noundef %spaceNo, i32 noundef %range, i32 noundef %index, i32 noundef %order_id)
+// CHECK-SAME: i32 noundef %spaceNo, i32 noundef %range, i32 noundef %index, i32 noundef %orderId)
// CHECK-NEXT: entry:
// CHECK-NEXT: %this.addr = alloca ptr, align 4
// CHECK-NEXT: %spaceNo.addr = alloca i32, align 4
// CHECK-NEXT: %range.addr = alloca i32, align 4
// CHECK-NEXT: %index.addr = alloca i32, align 4
-// CHECK-NEXT: %order_id.addr = alloca i32, align 4
+// CHECK-NEXT: %orderId.addr = alloca i32, align 4
// CHECK-NEXT: store ptr %this, ptr %this.addr, align 4
// CHECK-NEXT: store i32 %spaceNo, ptr %spaceNo.addr, align 4
// CHECK-NEXT: store i32 %range, ptr %range.addr, align 4
// CHECK-NEXT: store i32 %index, ptr %index.addr, align 4
-// CHECK-NEXT: store i32 %order_id, ptr %order_id.addr, align 4
+// CHECK-NEXT: store i32 %orderId, ptr %orderId.addr, align 4
// CHECK-NEXT: %this1 = load ptr, ptr %this.addr, align 4
// CHECK-NEXT: %0 = load i32, ptr %spaceNo.addr, align 4
// CHECK-NEXT: %1 = load i32, ptr %range.addr, align 4
// CHECK-NEXT: %2 = load i32, ptr %index.addr, align 4
-// CHECK-NEXT: %3 = load i32, ptr %order_id.addr, align 4
+// CHECK-NEXT: %3 = load i32, ptr %orderId.addr, align 4
// CHECK-NEXT: call void @_ZN4hlsl19RWByteAddressBufferC2Ejijj(ptr noundef nonnull align 4 dereferenceable(4) %this1, i32 noundef %0, i32 noundef %1, i32 noundef %2, i32 noundef %3) #4
// CHECK-NEXT: ret void
@@ -129,23 +129,23 @@ export void foo() {
// Buf2 initialization part 3 - body of RWByteAddressBuffer C2 constructor with implicit binding that initializes
// handle with @llvm.dx.resource.handlefromimplicitbinding
// CHECK: define linkonce_odr void @_ZN4hlsl19RWByteAddressBufferC2Ejijj(ptr noundef nonnull align 4 dereferenceable(4) %this,
-// CHECK-SAME: i32 noundef %spaceNo, i32 noundef %range, i32 noundef %index, i32 noundef %order_id) unnamed_addr #1 align 2 {
+// CHECK-SAME: i32 noundef %spaceNo, i32 noundef %range, i32 noundef %index, i32 noundef %orderId) unnamed_addr #1 align 2 {
// CHECK-NEXT: entry:
// CHECK-NEXT: %this.addr = alloca ptr, align 4
// CHECK-NEXT: %spaceNo.addr = alloca i32, align 4
// CHECK-NEXT: %range.addr = alloca i32, align 4
// CHECK-NEXT: %index.addr = alloca i32, align 4
-// CHECK-NEXT: %order_id.addr = alloca i32, align 4
+// CHECK-NEXT: %orderId.addr = alloca i32, align 4
// CHECK-NEXT: store ptr %this, ptr %this.addr, align 4
// CHECK-NEXT: store i32 %spaceNo, ptr %spaceNo.addr, align 4
// CHECK-NEXT: store i32 %range, ptr %range.addr, align 4
// CHECK-NEXT: store i32 %index, ptr %index.addr, align 4
-// CHECK-NEXT: store i32 %order_id, ptr %order_id.addr, align 4
+// CHECK-NEXT: store i32 %orderId, ptr %orderId.addr, align 4
// CHECK-NEXT: %this1 = load ptr, ptr %this.addr, align 4
// CHECK-NEXT: %0 = load i32, ptr %spaceNo.addr, align 4
// CHECK-NEXT: %1 = load i32, ptr %range.addr, align 4
// CHECK-NEXT: %2 = load i32, ptr %index.addr, align 4
-// CHECK-NEXT: %3 = load i32, ptr %order_id.addr, align 4
+// CHECK-NEXT: %3 = load i32, ptr %orderId.addr, align 4
// CHECK-NEXT: %4 = call target("dx.RawBuffer", i8, 1, 0) @llvm.dx.resource.handlefromimplicitbinding.tdx.RawBuffer_i8_1_0t(i32 %3, i32 %0, i32 %1, i32 %2, i1 false)
// CHECK-NEXT: %__handle = getelementptr inbounds nuw %"class.hlsl::RWByteAddressBuffer", ptr %this1, i32 0, i32 0
// CHECK-NEXT: store target("dx.RawBuffer", i8, 1, 0) %4, ptr %__handle, align 4
diff --git a/clang/test/CodeGenHLSL/builtins/RWBuffer-constructor.hlsl b/clang/test/CodeGenHLSL/builtins/RWBuffer-constructor.hlsl
index 7335a3938f476..27f2c1cbabf18 100644
--- a/clang/test/CodeGenHLSL/builtins/RWBuffer-constructor.hlsl
+++ b/clang/test/CodeGenHLSL/builtins/RWBuffer-constructor.hlsl
@@ -63,23 +63,23 @@ export void foo() {
// Buf2 initialization part 2 - body of RWBuffer<float> C1 constructor with implicit binding that calls the C2 constructor
// CHECK: define linkonce_odr void @_ZN4hlsl8RWBufferIdEC1Ejijj(ptr noundef nonnull align 4 dereferenceable(4) %this,
-// CHECK-SAME: i32 noundef %spaceNo, i32 noundef %range, i32 noundef %index, i32 noundef %order_id)
+// CHECK-SAME: i32 noundef %spaceNo, i32 noundef %range, i32 noundef %index, i32 noundef %orderId)
// CHECK-NEXT: entry:
// CHECK-NEXT: %this.addr = alloca ptr, align 4
// CHECK-NEXT: %spaceNo.addr = alloca i32, align 4
// CHECK-NEXT: %range.addr = alloca i32, align 4
// CHECK-NEXT: %index.addr = alloca i32, align 4
-// CHECK-NEXT: %order_id.addr = alloca i32, align 4
+// CHECK-NEXT: %orderId.addr = alloca i32, align 4
// CHECK-NEXT: store ptr %this, ptr %this.addr, align 4
// CHECK-NEXT: store i32 %spaceNo, ptr %spaceNo.addr, align 4
// CHECK-NEXT: store i32 %range, ptr %range.addr, align 4
// CHECK-NEXT: store i32 %index, ptr %index.addr, align 4
-// CHECK-NEXT: store i32 %order_id, ptr %order_id.addr, align 4
+// CHECK-NEXT: store i32 %orderId, ptr %orderId.addr, align 4
// CHECK-NEXT: %this1 = load ptr, ptr %this.addr, align 4
// CHECK-NEXT: %0 = load i32, ptr %spaceNo.addr, align 4
// CHECK-NEXT: %1 = load i32, ptr %range.addr, align 4
// CHECK-NEXT: %2 = load i32, ptr %index.addr, align 4
-// CHECK-NEXT: %3 = load i32, ptr %order_id.addr, align 4
+// CHECK-NEXT: %3 = load i32, ptr %orderId.addr, align 4
// CHECK-NEXT: call void @_ZN4hlsl8RWBufferIdEC2Ejijj(ptr noundef nonnull align 4 dereferenceable(4) %this1, i32 noundef %0, i32 noundef %1, i32 noundef %2, i32 noundef %3) #4
// CHECK-NEXT: ret void
@@ -127,23 +127,23 @@ export void foo() {
// Buf2 initialization part 3 - body of RWBuffer<float> C2 constructor with implicit binding that initializes
// handle with @llvm.dx.resource.handlefromimplicitbinding
// CHECK: define linkonce_odr void @_ZN4hlsl8RWBufferIdEC2Ejijj(ptr noundef nonnull align 4 dereferenceable(4) %this,
-// CHECK-SAME: i32 noundef %spaceNo, i32 noundef %range, i32 noundef %index, i32 noundef %order_id) unnamed_addr #1 align 2 {
+// CHECK-SAME: i32 noundef %spaceNo, i32 noundef %range, i32 noundef %index, i32 noundef %orderId) unnamed_addr #1 align 2 {
// CHECK-NEXT: entry:
// CHECK-NEXT: %this.addr = alloca ptr, align 4
// CHECK-NEXT: %spaceNo.addr = alloca i32, align 4
// CHECK-NEXT: %range.addr = alloca i32, align 4
// CHECK-NEXT: %index.addr = alloca i32, align 4
-// CHECK-NEXT: %order_id.addr = alloca i32, align 4
+// CHECK-NEXT: %orderId.addr = alloca i32, align 4
// CHECK-NEXT: store ptr %this, ptr %this.addr, align 4
// CHECK-NEXT: store i32 %spaceNo, ptr %spaceNo.addr, align 4
// CHECK-NEXT: store i32 %range, ptr %range.addr, align 4
// CHECK-NEXT: store i32 %index, ptr %index.addr, align 4
-// CHECK-NEXT: store i32 %order_id, ptr %order_id.addr, align 4
+// CHECK-NEXT: store i32 %orderId, ptr %orderId.addr, align 4
// CHECK-NEXT: %this1 = load ptr, ptr %this.addr, align 4
// CHECK-NEXT: %0 = load i32, ptr %spaceNo.addr, align 4
// CHECK-NEXT: %1 = load i32, ptr %range.addr, align 4
// CHECK-NEXT: %2 = load i32, ptr %index.addr, align 4
-// CHECK-NEXT: %3 = load i32, ptr %order_id.addr, align 4
+// CHECK-NEXT: %3 = load i32, ptr %orderId.addr, align 4
// CHECK-NEXT: %4 = call target("dx.TypedBuffer", double, 1, 0, 0) @llvm.dx.resource.handlefromimplicitbinding.tdx.TypedBuffer_f64_1_0_0t(i32 %3, i32 %0, i32 %1, i32 %2, i1 false)
// CHECK-NEXT: %__handle = getelementptr inbounds nuw %"class.hlsl::RWBuffer.0", ptr %this1, i32 0, i32 0
// CHECK-NEXT: store target("dx.TypedBuffer", double, 1, 0, 0) %4, ptr %__handle, align 4
diff --git a/clang/test/CodeGenHLSL/builtins/StructuredBuffers-constructors.hlsl b/clang/test/CodeGenHLSL/builtins/StructuredBuffers-constructors.hlsl
index f77712a1148af..16a478eb36c66 100644
--- a/clang/test/CodeGenHLSL/builtins/StructuredBuffers-constructors.hlsl
+++ b/clang/test/CodeGenHLSL/builtins/StructuredBuffers-constructors.hlsl
@@ -65,23 +65,23 @@ export void foo() {
// Buf2 initialization part 2 - body of RWStructuredBuffer<float> C1 constructor with implicit binding that calls the C2 constructor
// CHECK: define linkonce_odr void @_ZN4hlsl18RWStructuredBufferIfEC1Ejijj(ptr noundef nonnull align 4 dereferenceable(4) %this,
-// CHECK-SAME: i32 noundef %spaceNo, i32 noundef %range, i32 noundef %index, i32 noundef %order_id)
+// CHECK-SAME: i32 noundef %spaceNo, i32 noundef %range, i32 noundef %index, i32 noundef %orderId)
// CHECK-NEXT: entry:
// CHECK-NEXT: %this.addr = alloca ptr, align 4
// CHECK-NEXT: %spaceNo.addr = alloca i32, align 4
// CHECK-NEXT: %range.addr = alloca i32, align 4
// CHECK-NEXT: %index.addr = alloca i32, align 4
-// CHECK-NEXT: %order_id.addr = alloca i32, align 4
+// CHECK-NEXT: %orderId.addr = alloca i32, align 4
// CHECK-NEXT: store ptr %this, ptr %this.addr, align 4
// CHECK-NEXT: store i32 %spaceNo, ptr %spaceNo.addr, align 4
// CHECK-NEXT: store i32 %range, ptr %range.addr, align 4
// CHECK-NEXT: store i32 %index, ptr %index.addr, align 4
-// CHECK-NEXT: store i32 %order_id, ptr %order_id.addr, align 4
+// CHECK-NEXT: store i32 %orderId, ptr %orderId.addr, align 4
// CHECK-NEXT: %this1 = load ptr, ptr %this.addr, align 4
// CHECK-NEXT: %0 = load i32, ptr %spaceNo.addr, align 4
// CHECK-NEXT: %1 = load i32, ptr %range.addr, align 4
// CHECK-NEXT: %2 = load i32, ptr %index.addr, align 4
-// CHECK-NEXT: %3 = load i32, ptr %order_id.addr, align 4
+// CHECK-NEXT: %3 = load i32, ptr %orderId.addr, align 4
// CHECK-NEXT: call void @_ZN4hlsl18RWStructuredBufferIfEC2Ejijj(ptr noundef nonnull align 4 dereferenceable(4) %this1, i32 noundef %0, i32 noundef %1, i32 noundef %2, i32 noundef %3) #4
// CHECK-NEXT: ret void
@@ -131,23 +131,23 @@ export void foo() {
// Buf2 initialization part 3 - body of RWStructuredBuffer<float> C2 constructor with implicit binding that initializes
// handle with @llvm.dx.resource.handlefromimplicitbinding
// CHECK: define linkonce_odr void @_ZN4hlsl18RWStructuredBufferIfEC2Ejijj(ptr noundef nonnull align 4 dereferenceable(4) %this,
-// CHECK-SAME: i32 noundef %spaceNo, i32 noundef %range, i32 noundef %index, i32 noundef %order_id) unnamed_addr #1 align 2 {
+// CHECK-SAME: i32 noundef %spaceNo, i32 noundef %range, i32 noundef %index, i32 noundef %orderId) unnamed_addr #1 align 2 {
// CHECK-NEXT: entry:
// CHECK-NEXT: %this.addr = alloca ptr, align 4
// CHECK-NEXT: %spaceNo.addr = alloca i32, align 4
// CHECK-NEXT: %range.addr = alloca i32, align 4
// CHECK-NEXT: %index.addr = alloca i32, align 4
-// CHECK-NEXT: %order_id.addr = alloca i32, align 4
+// CHECK-NEXT: %orderId.addr = alloca i32, align 4
// CHECK-NEXT: store ptr %this, ptr %this.addr, align 4
// CHECK-NEXT: store i32 %spaceNo, ptr %spaceNo.addr, align 4
// CHECK-NEXT: store i32 %range, ptr %range.addr, align 4
// CHECK-NEXT: store i32 %index, ptr %index.addr, align 4
-// CHECK-NEXT: store i32 %order_id, ptr %order_id.addr, align 4
+// CHECK-NEXT: store i32 %orderId, ptr %orderId.addr, align 4
// CHECK-NEXT: %this1 = load ptr, ptr %this.addr, align 4
// CHECK-NEXT: %0 = load i32, ptr %spaceNo.addr, align 4
// CHECK-NEXT: %1 = load i32, ptr %range.addr, align 4
// CHECK-NEXT: %2 = load i32, ptr %index.addr, align 4
-// CHECK-NEXT: %3 = load i32, ptr %order_id.addr, align 4
+// CHECK-NEXT: %3 = load i32, ptr %orderId.addr, align 4
// CHECK-NEXT: %4 = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefromimplicitbinding.tdx.RawBuffer_f32_1_0t(i32 %3, i32 %0, i32 %1, i32 %2, i1 false)
// CHECK-NEXT: %__handle = getelementptr inbounds nuw %"class.hlsl::RWStructuredBuffer", ptr %this1, i32 0, i32 0
// CHECK-NEXT: store target("dx.RawBuffer", float, 1, 0) %4, ptr %__handle, align 4
>From cecd1e4ca17382f9946fdfe6c1272c16961e156e Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Wed, 14 May 2025 11:56:28 -0700
Subject: [PATCH 13/14] simplify constructor tests
---
.../ByteAddressBuffers-constructors.hlsl | 41 ++---------------
.../builtins/RWBuffer-constructor.hlsl | 43 ++----------------
.../StructuredBuffers-constructors.hlsl | 45 +++----------------
3 files changed, 12 insertions(+), 117 deletions(-)
diff --git a/clang/test/CodeGenHLSL/builtins/ByteAddressBuffers-constructors.hlsl b/clang/test/CodeGenHLSL/builtins/ByteAddressBuffers-constructors.hlsl
index 18d2c7f5a7530..5f844fcfe4121 100644
--- a/clang/test/CodeGenHLSL/builtins/ByteAddressBuffers-constructors.hlsl
+++ b/clang/test/CodeGenHLSL/builtins/ByteAddressBuffers-constructors.hlsl
@@ -47,24 +47,7 @@ export void foo() {
// Buf2 initialization part 2 - body of RWByteAddressBuffer C1 constructor with implicit binding that calls the C2 constructor
// CHECK: define linkonce_odr void @_ZN4hlsl19RWByteAddressBufferC1Ejijj(ptr noundef nonnull align 4 dereferenceable(4) %this,
// CHECK-SAME: i32 noundef %spaceNo, i32 noundef %range, i32 noundef %index, i32 noundef %orderId)
-// CHECK-NEXT: entry:
-// CHECK-NEXT: %this.addr = alloca ptr, align 4
-// CHECK-NEXT: %spaceNo.addr = alloca i32, align 4
-// CHECK-NEXT: %range.addr = alloca i32, align 4
-// CHECK-NEXT: %index.addr = alloca i32, align 4
-// CHECK-NEXT: %orderId.addr = alloca i32, align 4
-// CHECK-NEXT: store ptr %this, ptr %this.addr, align 4
-// CHECK-NEXT: store i32 %spaceNo, ptr %spaceNo.addr, align 4
-// CHECK-NEXT: store i32 %range, ptr %range.addr, align 4
-// CHECK-NEXT: store i32 %index, ptr %index.addr, align 4
-// CHECK-NEXT: store i32 %orderId, ptr %orderId.addr, align 4
-// CHECK-NEXT: %this1 = load ptr, ptr %this.addr, align 4
-// CHECK-NEXT: %0 = load i32, ptr %spaceNo.addr, align 4
-// CHECK-NEXT: %1 = load i32, ptr %range.addr, align 4
-// CHECK-NEXT: %2 = load i32, ptr %index.addr, align 4
-// CHECK-NEXT: %3 = load i32, ptr %orderId.addr, align 4
-// CHECK-NEXT: call void @_ZN4hlsl19RWByteAddressBufferC2Ejijj(ptr noundef nonnull align 4 dereferenceable(4) %this1, i32 noundef %0, i32 noundef %1, i32 noundef %2, i32 noundef %3) #4
-// CHECK-NEXT: ret void
+// CHECK: call void @_ZN4hlsl19RWByteAddressBufferC2Ejijj(ptr noundef nonnull align 4 dereferenceable(4) %this1, i32 noundef %0, i32 noundef %1, i32 noundef %2, i32 noundef %3) #4
// Buf3 initialization part 1 - local variable declared in function foo() is initialized by
// RasterizerOrderedByteAddressBuffer C1 default constructor
@@ -87,32 +70,14 @@ export void foo() {
// CHECK-DXIL-SAME: i32 %{{.*}}, i32 %{{.*}}, i32 %{{.*}}, i32 %{{.*}}, i1 false)
// CHECK-NEXT: %__handle = getelementptr inbounds nuw %"class.hlsl::ByteAddressBuffer", ptr %{{.*}}, i32 0, i32 0
// CHECK-DXIL-NEXT: store target("dx.RawBuffer", i8, 0, 0) %[[HANDLE]], ptr %__handle, align 4
-// CHECK-NEXT: ret void
// Buf2 initialization part 3 - body of RWByteAddressBuffer C2 constructor with implicit binding that initializes
// handle with @llvm.dx.resource.handlefromimplicitbinding
// CHECK: define linkonce_odr void @_ZN4hlsl19RWByteAddressBufferC2Ejijj(ptr noundef nonnull align 4 dereferenceable(4) %this,
// CHECK-SAME: i32 noundef %spaceNo, i32 noundef %range, i32 noundef %index, i32 noundef %orderId) unnamed_addr #1 align 2 {
-// CHECK-NEXT: entry:
-// CHECK-NEXT: %this.addr = alloca ptr, align 4
-// CHECK-NEXT: %spaceNo.addr = alloca i32, align 4
-// CHECK-NEXT: %range.addr = alloca i32, align 4
-// CHECK-NEXT: %index.addr = alloca i32, align 4
-// CHECK-NEXT: %orderId.addr = alloca i32, align 4
-// CHECK-NEXT: store ptr %this, ptr %this.addr, align 4
-// CHECK-NEXT: store i32 %spaceNo, ptr %spaceNo.addr, align 4
-// CHECK-NEXT: store i32 %range, ptr %range.addr, align 4
-// CHECK-NEXT: store i32 %index, ptr %index.addr, align 4
-// CHECK-NEXT: store i32 %orderId, ptr %orderId.addr, align 4
-// CHECK-NEXT: %this1 = load ptr, ptr %this.addr, align 4
-// CHECK-NEXT: %0 = load i32, ptr %spaceNo.addr, align 4
-// CHECK-NEXT: %1 = load i32, ptr %range.addr, align 4
-// CHECK-NEXT: %2 = load i32, ptr %index.addr, align 4
-// CHECK-NEXT: %3 = load i32, ptr %orderId.addr, align 4
-// CHECK-NEXT: %4 = call target("dx.RawBuffer", i8, 1, 0) @llvm.dx.resource.handlefromimplicitbinding.tdx.RawBuffer_i8_1_0t(i32 %3, i32 %0, i32 %1, i32 %2, i1 false)
+// CHECK: %[[HANDLE:.*]] = call target("dx.RawBuffer", i8, 1, 0) @llvm.dx.resource.handlefromimplicitbinding.tdx.RawBuffer_i8_1_0t(i32 %{{.*}}, i32 %{{.*}}, i32 %{{.*}}, i32 %{{.*}}, i1 false)
// CHECK-NEXT: %__handle = getelementptr inbounds nuw %"class.hlsl::RWByteAddressBuffer", ptr %this1, i32 0, i32 0
-// CHECK-NEXT: store target("dx.RawBuffer", i8, 1, 0) %4, ptr %__handle, align 4
-// CHECK-NEXT: ret void
+// CHECK-NEXT: store target("dx.RawBuffer", i8, 1, 0) %[[HANDLE]], ptr %__handle, align 4
// Buf3 initialization part 3 - body of RasterizerOrderedByteAddressBuffer default C2 constructor that
// initializes handle to poison
diff --git a/clang/test/CodeGenHLSL/builtins/RWBuffer-constructor.hlsl b/clang/test/CodeGenHLSL/builtins/RWBuffer-constructor.hlsl
index 1fb92e5f8b7dc..ad8ebdf7d8c85 100644
--- a/clang/test/CodeGenHLSL/builtins/RWBuffer-constructor.hlsl
+++ b/clang/test/CodeGenHLSL/builtins/RWBuffer-constructor.hlsl
@@ -47,24 +47,7 @@ export void foo() {
// Buf2 initialization part 2 - body of RWBuffer<float> C1 constructor with implicit binding that calls the C2 constructor
// CHECK: define linkonce_odr void @_ZN4hlsl8RWBufferIdEC1Ejijj(ptr noundef nonnull align 4 dereferenceable(4) %this,
// CHECK-SAME: i32 noundef %spaceNo, i32 noundef %range, i32 noundef %index, i32 noundef %orderId)
-// CHECK-NEXT: entry:
-// CHECK-NEXT: %this.addr = alloca ptr, align 4
-// CHECK-NEXT: %spaceNo.addr = alloca i32, align 4
-// CHECK-NEXT: %range.addr = alloca i32, align 4
-// CHECK-NEXT: %index.addr = alloca i32, align 4
-// CHECK-NEXT: %orderId.addr = alloca i32, align 4
-// CHECK-NEXT: store ptr %this, ptr %this.addr, align 4
-// CHECK-NEXT: store i32 %spaceNo, ptr %spaceNo.addr, align 4
-// CHECK-NEXT: store i32 %range, ptr %range.addr, align 4
-// CHECK-NEXT: store i32 %index, ptr %index.addr, align 4
-// CHECK-NEXT: store i32 %orderId, ptr %orderId.addr, align 4
-// CHECK-NEXT: %this1 = load ptr, ptr %this.addr, align 4
-// CHECK-NEXT: %0 = load i32, ptr %spaceNo.addr, align 4
-// CHECK-NEXT: %1 = load i32, ptr %range.addr, align 4
-// CHECK-NEXT: %2 = load i32, ptr %index.addr, align 4
-// CHECK-NEXT: %3 = load i32, ptr %orderId.addr, align 4
-// CHECK-NEXT: call void @_ZN4hlsl8RWBufferIdEC2Ejijj(ptr noundef nonnull align 4 dereferenceable(4) %this1, i32 noundef %0, i32 noundef %1, i32 noundef %2, i32 noundef %3) #4
-// CHECK-NEXT: ret void
+// CHECK: call void @_ZN4hlsl8RWBufferIdEC2Ejijj(ptr noundef nonnull align 4 dereferenceable(4) %{{.*}}, i32 noundef %{{.*}}, i32 noundef %{{.*}}, i32 noundef %{{.*}}, i32 noundef %{{.*}}) #4
// Buf3 initialization part 1 - local variable declared in function foo() is initialized by RWBuffer<int> C1 default constructor
// CHECK: define void @_Z3foov()
@@ -84,32 +67,14 @@ export void foo() {
// CHECK-DXIL-SAME: i32 %{{.*}}, i32 %{{.*}}, i32 %{{.*}}, i32 %{{.*}}, i1 false)
// CHECK-NEXT: %__handle = getelementptr inbounds nuw %"class.hlsl::RWBuffer", ptr %{{.*}}, i32 0, i32 0
// CHECK-DXIL-NEXT: store target("dx.TypedBuffer", float, 1, 0, 0) %[[HANDLE]], ptr %__handle, align 4
-// CHECK-NEXT: ret void
// Buf2 initialization part 3 - body of RWBuffer<float> C2 constructor with implicit binding that initializes
// handle with @llvm.dx.resource.handlefromimplicitbinding
// CHECK: define linkonce_odr void @_ZN4hlsl8RWBufferIdEC2Ejijj(ptr noundef nonnull align 4 dereferenceable(4) %this,
// CHECK-SAME: i32 noundef %spaceNo, i32 noundef %range, i32 noundef %index, i32 noundef %orderId) unnamed_addr #1 align 2 {
-// CHECK-NEXT: entry:
-// CHECK-NEXT: %this.addr = alloca ptr, align 4
-// CHECK-NEXT: %spaceNo.addr = alloca i32, align 4
-// CHECK-NEXT: %range.addr = alloca i32, align 4
-// CHECK-NEXT: %index.addr = alloca i32, align 4
-// CHECK-NEXT: %orderId.addr = alloca i32, align 4
-// CHECK-NEXT: store ptr %this, ptr %this.addr, align 4
-// CHECK-NEXT: store i32 %spaceNo, ptr %spaceNo.addr, align 4
-// CHECK-NEXT: store i32 %range, ptr %range.addr, align 4
-// CHECK-NEXT: store i32 %index, ptr %index.addr, align 4
-// CHECK-NEXT: store i32 %orderId, ptr %orderId.addr, align 4
-// CHECK-NEXT: %this1 = load ptr, ptr %this.addr, align 4
-// CHECK-NEXT: %0 = load i32, ptr %spaceNo.addr, align 4
-// CHECK-NEXT: %1 = load i32, ptr %range.addr, align 4
-// CHECK-NEXT: %2 = load i32, ptr %index.addr, align 4
-// CHECK-NEXT: %3 = load i32, ptr %orderId.addr, align 4
-// CHECK-NEXT: %4 = call target("dx.TypedBuffer", double, 1, 0, 0) @llvm.dx.resource.handlefromimplicitbinding.tdx.TypedBuffer_f64_1_0_0t(i32 %3, i32 %0, i32 %1, i32 %2, i1 false)
-// CHECK-NEXT: %__handle = getelementptr inbounds nuw %"class.hlsl::RWBuffer.0", ptr %this1, i32 0, i32 0
-// CHECK-NEXT: store target("dx.TypedBuffer", double, 1, 0, 0) %4, ptr %__handle, align 4
-// CHECK-NEXT: ret void
+// CHECK: %[[HANDLE:.*]] = call target("dx.TypedBuffer", double, 1, 0, 0) @llvm.dx.resource.handlefromimplicitbinding.tdx.TypedBuffer_f64_1_0_0t(i32 %{{.*}}, i32 %{{.*}}, i32 %{{.*}}, i32 %{{.*}}, i1 false)
+// CHECK-NEXT: %__handle = getelementptr inbounds nuw %"class.hlsl::RWBuffer.0", ptr %{{.*}}, i32 0, i32 0
+// CHECK-NEXT: store target("dx.TypedBuffer", double, 1, 0, 0) %[[HANDLE]], ptr %__handle, align 4
// Buf3 initialization part 3 - body of RWBuffer<int> default C2 constructor that initializes handle to poison
// CHECK: define linkonce_odr void @_ZN4hlsl8RWBufferIiEC2Ev(ptr noundef nonnull align 4 dereferenceable(4) %this)
diff --git a/clang/test/CodeGenHLSL/builtins/StructuredBuffers-constructors.hlsl b/clang/test/CodeGenHLSL/builtins/StructuredBuffers-constructors.hlsl
index 1e3465a51db51..34ce676a02f83 100644
--- a/clang/test/CodeGenHLSL/builtins/StructuredBuffers-constructors.hlsl
+++ b/clang/test/CodeGenHLSL/builtins/StructuredBuffers-constructors.hlsl
@@ -49,24 +49,8 @@ export void foo() {
// Buf2 initialization part 2 - body of RWStructuredBuffer<float> C1 constructor with implicit binding that calls the C2 constructor
// CHECK: define linkonce_odr void @_ZN4hlsl18RWStructuredBufferIfEC1Ejijj(ptr noundef nonnull align 4 dereferenceable(4) %this,
// CHECK-SAME: i32 noundef %spaceNo, i32 noundef %range, i32 noundef %index, i32 noundef %orderId)
-// CHECK-NEXT: entry:
-// CHECK-NEXT: %this.addr = alloca ptr, align 4
-// CHECK-NEXT: %spaceNo.addr = alloca i32, align 4
-// CHECK-NEXT: %range.addr = alloca i32, align 4
-// CHECK-NEXT: %index.addr = alloca i32, align 4
-// CHECK-NEXT: %orderId.addr = alloca i32, align 4
-// CHECK-NEXT: store ptr %this, ptr %this.addr, align 4
-// CHECK-NEXT: store i32 %spaceNo, ptr %spaceNo.addr, align 4
-// CHECK-NEXT: store i32 %range, ptr %range.addr, align 4
-// CHECK-NEXT: store i32 %index, ptr %index.addr, align 4
-// CHECK-NEXT: store i32 %orderId, ptr %orderId.addr, align 4
-// CHECK-NEXT: %this1 = load ptr, ptr %this.addr, align 4
-// CHECK-NEXT: %0 = load i32, ptr %spaceNo.addr, align 4
-// CHECK-NEXT: %1 = load i32, ptr %range.addr, align 4
-// CHECK-NEXT: %2 = load i32, ptr %index.addr, align 4
-// CHECK-NEXT: %3 = load i32, ptr %orderId.addr, align 4
-// CHECK-NEXT: call void @_ZN4hlsl18RWStructuredBufferIfEC2Ejijj(ptr noundef nonnull align 4 dereferenceable(4) %this1, i32 noundef %0, i32 noundef %1, i32 noundef %2, i32 noundef %3) #4
-// CHECK-NEXT: ret void
+// CHECK: call void @_ZN4hlsl18RWStructuredBufferIfEC2Ejijj(ptr noundef nonnull align 4 dereferenceable(4)
+// CHECK-SAME; %{{.*}}, i32 noundef %{{.*}}, i32 noundef %{{.*}}, i32 noundef %{{.*}}, i32 noundef %{{.*}}) #4
// Buf3 initialization part 1 - local variable declared in function foo() is initialized by
// AppendStructuredBuffer<float> C1 default constructor
@@ -79,7 +63,6 @@ export void foo() {
// the default C2 constructor
// CHECK: define linkonce_odr void @_ZN4hlsl22AppendStructuredBufferIfEC1Ev(ptr noundef nonnull align 4 dereferenceable(4) %this)
// CHECK: call void @_ZN4hlsl22AppendStructuredBufferIfEC2Ev(ptr noundef nonnull align 4 dereferenceable(4) %{{.*}})
-// CHECK-NEXT: ret void
// Buf1 initialization part 3 - body of AppendStructuredBuffer<float> C2 constructor with explicit binding
// that initializes handle with @llvm.dx.resource.handlefrombinding
@@ -89,32 +72,14 @@ export void foo() {
// CHECK-SAME: i32 %{{.*}}, i32 %{{.*}}, i32 %{{.*}}, i32 %{{.*}}, i1 false)
// CHECK-NEXT: %__handle = getelementptr inbounds nuw %"class.hlsl::StructuredBuffer", ptr %{{.*}}, i32 0, i32 0
// CHECK-DXIL-NEXT: store target("dx.RawBuffer", float, 0, 0) %[[HANDLE]], ptr %__handle, align 4
-// CHECK-NEXT: ret void
// Buf2 initialization part 3 - body of RWStructuredBuffer<float> C2 constructor with implicit binding that initializes
// handle with @llvm.dx.resource.handlefromimplicitbinding
// CHECK: define linkonce_odr void @_ZN4hlsl18RWStructuredBufferIfEC2Ejijj(ptr noundef nonnull align 4 dereferenceable(4) %this,
// CHECK-SAME: i32 noundef %spaceNo, i32 noundef %range, i32 noundef %index, i32 noundef %orderId) unnamed_addr #1 align 2 {
-// CHECK-NEXT: entry:
-// CHECK-NEXT: %this.addr = alloca ptr, align 4
-// CHECK-NEXT: %spaceNo.addr = alloca i32, align 4
-// CHECK-NEXT: %range.addr = alloca i32, align 4
-// CHECK-NEXT: %index.addr = alloca i32, align 4
-// CHECK-NEXT: %orderId.addr = alloca i32, align 4
-// CHECK-NEXT: store ptr %this, ptr %this.addr, align 4
-// CHECK-NEXT: store i32 %spaceNo, ptr %spaceNo.addr, align 4
-// CHECK-NEXT: store i32 %range, ptr %range.addr, align 4
-// CHECK-NEXT: store i32 %index, ptr %index.addr, align 4
-// CHECK-NEXT: store i32 %orderId, ptr %orderId.addr, align 4
-// CHECK-NEXT: %this1 = load ptr, ptr %this.addr, align 4
-// CHECK-NEXT: %0 = load i32, ptr %spaceNo.addr, align 4
-// CHECK-NEXT: %1 = load i32, ptr %range.addr, align 4
-// CHECK-NEXT: %2 = load i32, ptr %index.addr, align 4
-// CHECK-NEXT: %3 = load i32, ptr %orderId.addr, align 4
-// CHECK-NEXT: %4 = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefromimplicitbinding.tdx.RawBuffer_f32_1_0t(i32 %3, i32 %0, i32 %1, i32 %2, i1 false)
-// CHECK-NEXT: %__handle = getelementptr inbounds nuw %"class.hlsl::RWStructuredBuffer", ptr %this1, i32 0, i32 0
-// CHECK-NEXT: store target("dx.RawBuffer", float, 1, 0) %4, ptr %__handle, align 4
-// CHECK-NEXT: ret void
+// CHECK: %[[HANDLE:.*]] = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefromimplicitbinding.tdx.RawBuffer_f32_1_0t(i32 %{{.*}}, i32 %{{.*}}, i32 %{{.*}}, i32 %{{.*}}, i1 false)
+// CHECK-NEXT: %__handle = getelementptr inbounds nuw %"class.hlsl::RWStructuredBuffer", ptr %{{.*}}, i32 0, i32 0
+// CHECK-NEXT: store target("dx.RawBuffer", float, 1, 0) %[[HANDLE]], ptr %__handle, align 4
// Buf3 initialization part 3 - body of AppendStructuredBuffer<float> default C2 constructor that
// initializes handle to poison
>From cef2c3e3d766bca599fac2d06c1a56d2ad40a9cb Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Wed, 14 May 2025 15:02:06 -0700
Subject: [PATCH 14/14] rename vars
---
clang/lib/Sema/SemaHLSL.cpp | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 7e80b7f8a692d..16f3986179aea 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -3325,9 +3325,9 @@ bool SemaHLSL::initGlobalResourceDecl(VarDecl *VD) {
ASTContext &AST = SemaRef.getASTContext();
uint64_t UIntTySize = AST.getTypeSize(AST.UnsignedIntTy);
uint64_t IntTySize = AST.getTypeSize(AST.IntTy);
- IntegerLiteral *One = IntegerLiteral::Create(AST, llvm::APInt(IntTySize, 1),
- AST.IntTy, SourceLocation());
- IntegerLiteral *Zero = IntegerLiteral::Create(
+ IntegerLiteral *RangeSize = IntegerLiteral::Create(
+ AST, llvm::APInt(IntTySize, 1), AST.IntTy, SourceLocation());
+ IntegerLiteral *Index = IntegerLiteral::Create(
AST, llvm::APInt(UIntTySize, 0), AST.UnsignedIntTy, SourceLocation());
IntegerLiteral *Space =
IntegerLiteral::Create(AST, llvm::APInt(UIntTySize, SpaceNo),
@@ -3338,7 +3338,7 @@ bool SemaHLSL::initGlobalResourceDecl(VarDecl *VD) {
IntegerLiteral *RegSlot = IntegerLiteral::Create(
AST, llvm::APInt(UIntTySize, RegisterSlot.value()), AST.UnsignedIntTy,
SourceLocation());
- Expr *Args[] = {RegSlot, Space, One, Zero};
+ Expr *Args[] = {RegSlot, Space, RangeSize, Index};
return initVarDeclWithCtor(SemaRef, VD, Args);
}
@@ -3346,7 +3346,7 @@ bool SemaHLSL::initGlobalResourceDecl(VarDecl *VD) {
IntegerLiteral *OrderId = IntegerLiteral::Create(
AST, llvm::APInt(UIntTySize, getNextImplicitBindingOrderID()),
AST.UnsignedIntTy, SourceLocation());
- Expr *Args[] = {Space, One, Zero, OrderId};
+ Expr *Args[] = {Space, RangeSize, Index, OrderId};
return initVarDeclWithCtor(SemaRef, VD, Args);
}
More information about the cfe-commits
mailing list