[clang] [llvm] [HLSL] Add support for dynamic resources (PR #221103)
Helena Kotas via cfe-commits
cfe-commits at lists.llvm.org
Thu Sep 10 22:57:01 PDT 2026
https://github.com/hekota updated https://github.com/llvm/llvm-project/pull/221103
>From 3e70d41f6223c89dd318cdcf57be14c250eb3557 Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Thu, 3 Sep 2026 13:02:10 -0700
Subject: [PATCH 1/2] [HLSL] Add support for dynamic resources
---
clang/include/clang/Basic/Builtins.td | 12 ++
.../clang/Sema/HLSLExternalSemaSource.h | 2 +
clang/lib/CodeGen/CGHLSLBuiltins.cpp | 22 +++
clang/lib/CodeGen/CGHLSLRuntime.h | 2 +
clang/lib/Headers/CMakeLists.txt | 1 +
clang/lib/Headers/hlsl.h | 1 +
clang/lib/Headers/hlsl/hlsl_resources.h | 36 +++++
clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp | 68 +++++++++
clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h | 3 +
clang/lib/Sema/HLSLExternalSemaSource.cpp | 27 ++++
clang/lib/Sema/SemaHLSL.cpp | 26 ++++
.../test/AST/HLSL/ByteAddressBuffers-AST.hlsl | 18 +++
clang/test/AST/HLSL/DynamicResources-AST.hlsl | 132 ++++++++++++++++++
.../test/AST/HLSL/StructuredBuffers-AST.hlsl | 27 ++++
clang/test/AST/HLSL/TypedBuffers-AST.hlsl | 17 +++
.../resources/dynamic-resources.hlsl | 109 +++++++++++++++
clang/test/SemaHLSL/Language/InitLists.hlsl | 1 +
.../Textures-SampleCmpLevelZero.hlsl | 5 +-
.../dynamic-resources-availability.hlsl | 13 ++
.../SemaHLSL/Resources/dynamic-resources.hlsl | 50 +++++++
llvm/include/llvm/IR/IntrinsicsSPIRV.td | 9 ++
21 files changed, 579 insertions(+), 2 deletions(-)
create mode 100644 clang/lib/Headers/hlsl/hlsl_resources.h
create mode 100644 clang/test/AST/HLSL/DynamicResources-AST.hlsl
create mode 100644 clang/test/CodeGenHLSL/resources/dynamic-resources.hlsl
create mode 100644 clang/test/SemaHLSL/Resources/dynamic-resources-availability.hlsl
create mode 100644 clang/test/SemaHLSL/Resources/dynamic-resources.hlsl
diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index 49fe879c6add1..2b33b4a8541b3 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -5431,6 +5431,18 @@ def HLSLResourceCounterHandleFromImplicitBinding : LangBuiltin<"HLSL_LANG"> {
let Prototype = "__hlsl_resource_t(__hlsl_resource_t, uint32_t, uint32_t)";
}
+def HLSLResourceHandleFromHeap : LangBuiltin<"HLSL_LANG"> {
+ let Spellings = ["__builtin_hlsl_resource_handlefromheap"];
+ let Attributes = [NoThrow];
+ let Prototype = "__hlsl_resource_t(__hlsl_resource_t, uint32_t)";
+}
+
+def HLSLResourceCounterHandleFromHeap : LangBuiltin<"HLSL_LANG"> {
+ let Spellings = ["__builtin_hlsl_resource_counterhandlefromheap"];
+ let Attributes = [NoThrow];
+ let Prototype = "__hlsl_resource_t(__hlsl_resource_t, uint32_t)";
+}
+
def HLSLResourceNonUniformIndex : LangBuiltin<"HLSL_LANG"> {
let Spellings = ["__builtin_hlsl_resource_nonuniformindex"];
let Attributes = [NoThrow];
diff --git a/clang/include/clang/Sema/HLSLExternalSemaSource.h b/clang/include/clang/Sema/HLSLExternalSemaSource.h
index 77fd3fafc6d77..840cd3e0a082a 100644
--- a/clang/include/clang/Sema/HLSLExternalSemaSource.h
+++ b/clang/include/clang/Sema/HLSLExternalSemaSource.h
@@ -43,8 +43,10 @@ class HLSLExternalSemaSource : public ExternalSemaSource {
private:
void defineTrivialHLSLTypes();
+ void defineInternalHLSLTypes();
void defineHLSLVectorAlias();
void defineHLSLMatrixAlias();
+ void defineHeapResourceInfoTypes();
void defineHLSLTypesWithForwardDeclarations();
void defineHLSLAtomicIntrinsics();
void onCompletion(CXXRecordDecl *Record, CompletionFunction Fn);
diff --git a/clang/lib/CodeGen/CGHLSLBuiltins.cpp b/clang/lib/CodeGen/CGHLSLBuiltins.cpp
index 062faadcdcab2..42c107405dff3 100644
--- a/clang/lib/CodeGen/CGHLSLBuiltins.cpp
+++ b/clang/lib/CodeGen/CGHLSLBuiltins.cpp
@@ -1063,6 +1063,28 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID,
return EmitIntrinsicCall(IntrinsicID, {HandleTy, MainHandle->getType()},
Args);
}
+ case Builtin::BI__builtin_hlsl_resource_handlefromheap: {
+ llvm::Type *HandleTy = CGM.getTypes().ConvertType(E->getType());
+ Value *IndexOp = EmitScalarExpr(E->getArg(1));
+ llvm::Intrinsic::ID IntrinsicID =
+ CGM.getHLSLRuntime().getCreateHandleFromHeapIntrinsic();
+ SmallVector<Value *> Args{IndexOp};
+ return Builder.CreateIntrinsic(HandleTy, IntrinsicID, Args);
+ }
+ case Builtin::BI__builtin_hlsl_resource_counterhandlefromheap: {
+ Value *MainHandle = EmitScalarExpr(E->getArg(0));
+ if (!CGM.getTriple().isSPIRV())
+ return MainHandle;
+
+ llvm::Type *HandleTy = CGM.getTypes().ConvertType(E->getType());
+ Value *IndexOp = EmitScalarExpr(E->getArg(1));
+ llvm::Intrinsic::ID IntrinsicID =
+ llvm::Intrinsic::spv_resource_counterhandlefromheap;
+ SmallVector<Value *> Args{MainHandle, IndexOp};
+ return EmitIntrinsicCall(IntrinsicID, {HandleTy, MainHandle->getType()},
+ Args);
+ }
+
case Builtin::BI__builtin_hlsl_resource_nonuniformindex: {
Value *IndexOp = EmitScalarExpr(E->getArg(0));
llvm::Type *RetTy = ConvertType(E->getType());
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h b/clang/lib/CodeGen/CGHLSLRuntime.h
index 381653e8f8345..2ceafcc94a0b8 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.h
+++ b/clang/lib/CodeGen/CGHLSLRuntime.h
@@ -186,6 +186,8 @@ class CGHLSLRuntime {
resource_handlefrombinding)
GENERATE_HLSL_INTRINSIC_FUNCTION(CreateHandleFromImplicitBinding,
resource_handlefromimplicitbinding)
+ GENERATE_HLSL_INTRINSIC_FUNCTION(CreateHandleFromHeap,
+ resource_handlefromheap)
GENERATE_HLSL_INTRINSIC_FUNCTION(NonUniformResourceIndex,
resource_nonuniformindex)
GENERATE_HLSL_INTRINSIC_FUNCTION(BufferUpdateCounter, resource_updatecounter)
diff --git a/clang/lib/Headers/CMakeLists.txt b/clang/lib/Headers/CMakeLists.txt
index 3d845423759ac..9befd7b192af7 100644
--- a/clang/lib/Headers/CMakeLists.txt
+++ b/clang/lib/Headers/CMakeLists.txt
@@ -98,6 +98,7 @@ set(hlsl_subdir_files
hlsl/hlsl_intrinsic_helpers.h
hlsl/hlsl_intrinsics.h
hlsl/hlsl_detail.h
+ hlsl/hlsl_resources.h
hlsl/hlsl_spirv.h
)
set(hlsl_files
diff --git a/clang/lib/Headers/hlsl.h b/clang/lib/Headers/hlsl.h
index 8a144191c4695..ff50c9f9b7ac2 100644
--- a/clang/lib/Headers/hlsl.h
+++ b/clang/lib/Headers/hlsl.h
@@ -26,6 +26,7 @@
#if __HLSL_VERSION <= __HLSL_202x
#include "hlsl/hlsl_compat_overloads.h"
#endif
+#include "hlsl/hlsl_resources.h"
#ifdef __spirv__
#include "hlsl/hlsl_spirv.h"
diff --git a/clang/lib/Headers/hlsl/hlsl_resources.h b/clang/lib/Headers/hlsl/hlsl_resources.h
new file mode 100644
index 0000000000000..d43c94dbff572
--- /dev/null
+++ b/clang/lib/Headers/hlsl/hlsl_resources.h
@@ -0,0 +1,36 @@
+//===----- hlsl_resources.h - HLSL definitions for resources ----------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _HLSL_HLSL_RESOURCES_H_
+#define _HLSL_HLSL_RESOURCES_H_
+
+namespace hlsl {
+
+#define _HLSL_AVAILABILITY(platform, version) \
+ __attribute__((availability(platform, introduced = version)))
+
+struct __hlsl_resource_descriptor_heap_struct {
+ __hlsl_heap_resource_info operator[](uint32_t Index) {
+ return __hlsl_heap_resource_info{Index};
+ }
+};
+
+struct __hlsl_sampler_descriptor_heap_struct {
+ __hlsl_heap_sampler_info operator[](uint32_t Index) {
+ return __hlsl_heap_sampler_info{Index};
+ }
+};
+
+_HLSL_AVAILABILITY(shadermodel, 6.6)
+static __hlsl_resource_descriptor_heap_struct ResourceDescriptorHeap;
+
+_HLSL_AVAILABILITY(shadermodel, 6.6)
+static __hlsl_sampler_descriptor_heap_struct SamplerDescriptorHeap;
+
+} // namespace hlsl
+#endif //_HLSL_HLSL_RESOURCES_H_
diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
index 34e883264b5b3..15f78eadad4e2 100644
--- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
+++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
@@ -1216,6 +1216,74 @@ BuiltinTypeDeclBuilder::addDefaultHandleConstructor(AccessSpecifier Access) {
.finalize(Access);
}
+// Adds constructor that takes __hlsl_heap_resource_info:
+// Resource::Resource(__hlsl_heap_resource_info info) {
+// __handle = __builtin_hlsl_resource_handlefromheap(__handle, info.Index);
+// }
+BuiltinTypeDeclBuilder &
+BuiltinTypeDeclBuilder::addHeapResourceInfoConstructor(bool HasCounter) {
+ assert(!Record->isCompleteDefinition() && "record is already complete");
+
+ using PH = BuiltinTypeMethodBuilder::PlaceHolder;
+
+ ASTContext &AST = SemaRef.getASTContext();
+ QualType HandleType = getResourceHandleField()->getType();
+
+ QualType HeapResInfoType = lookupBuiltinType(
+ SemaRef, "__hlsl_heap_resource_info", Record->getDeclContext());
+ CXXRecordDecl *HeapResInfoDecl = HeapResInfoType->getAsCXXRecordDecl();
+
+ FieldDecl *IndexField = *HeapResInfoDecl->field_begin();
+ assert(IndexField && IndexField->getType() == AST.UnsignedIntTy &&
+ "Index field not as expected");
+
+ auto MB = BuiltinTypeMethodBuilder(*this, "", AST.VoidTy, false, true);
+ MB.addParam("HeapResInfo", HeapResInfoType)
+ .callBuiltin("__builtin_hlsl_resource_handlefromheap", HandleType,
+ PH::Handle, MB.createMemberExpr(PH::_0, IndexField))
+ .assign(PH::Handle, PH::LastStmt);
+
+ if (HasCounter) {
+ QualType CounterHandleType = getResourceCounterHandleField()->getType();
+ MB.callBuiltin("__builtin_hlsl_resource_counterhandlefromheap",
+ CounterHandleType, PH::Handle,
+ MB.createMemberExpr(PH::_0, IndexField))
+ .assign(PH::CounterHandle, PH::LastStmt);
+ }
+
+ return MB.finalize();
+}
+
+// Adds constructor that takes __hlsl_heap_sampler_info:
+// Resource::Resource(__hlsl_heap_sampler_info info) {
+// __handle = __builtin_hlsl_resource_handlefromheap(__handle, info.Index);
+// }
+BuiltinTypeDeclBuilder &
+BuiltinTypeDeclBuilder::addHeapSamplerInfoConstructor() {
+ assert(!Record->isCompleteDefinition() && "record is already complete");
+
+ using PH = BuiltinTypeMethodBuilder::PlaceHolder;
+
+ ASTContext &AST = SemaRef.getASTContext();
+ QualType HandleType = getResourceHandleField()->getType();
+
+ QualType HeapResInfoType = lookupBuiltinType(
+ SemaRef, "__hlsl_heap_sampler_info", Record->getDeclContext());
+ CXXRecordDecl *HeapResInfoDecl = HeapResInfoType->getAsCXXRecordDecl();
+
+ FieldDecl *IndexField = *HeapResInfoDecl->field_begin();
+ assert(IndexField && IndexField->getType() == AST.UnsignedIntTy &&
+ "Index field not as expected");
+
+ auto MB = BuiltinTypeMethodBuilder(*this, "", AST.VoidTy, false, true);
+ MB.addParam("HeapResInfo", HeapResInfoType);
+ MB.callBuiltin("__builtin_hlsl_resource_handlefromheap", HandleType,
+ PH::Handle, MB.createMemberExpr(PH::_0, IndexField))
+ .assign(PH::Handle, PH::LastStmt);
+
+ return MB.finalize();
+}
+
BuiltinTypeDeclBuilder &
BuiltinTypeDeclBuilder::addStaticInitializationFunctions(bool HasCounter) {
if (HasCounter) {
diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h
index 62dea7fab8064..226f1026876ca 100644
--- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h
+++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h
@@ -99,6 +99,9 @@ class BuiltinTypeDeclBuilder {
BuiltinTypeDeclBuilder &addDefaultHandleConstructor(
AccessSpecifier Access = AccessSpecifier::AS_public);
BuiltinTypeDeclBuilder &
+ addHeapResourceInfoConstructor(bool HasCounter = false);
+ BuiltinTypeDeclBuilder &addHeapSamplerInfoConstructor();
+ BuiltinTypeDeclBuilder &
addCopyConstructor(AccessSpecifier Access = AccessSpecifier::AS_public);
BuiltinTypeDeclBuilder &addCopyAssignmentOperator(
AccessSpecifier Access = AccessSpecifier::AS_public);
diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp b/clang/lib/Sema/HLSLExternalSemaSource.cpp
index d2d9c97b98a57..75932dc8a136e 100644
--- a/clang/lib/Sema/HLSLExternalSemaSource.cpp
+++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp
@@ -55,6 +55,7 @@ void HLSLExternalSemaSource::InitializeSema(Sema &S) {
// Force external decls in the HLSL namespace to load from the PCH.
(void)HLSLNamespace->getCanonicalDecl()->decls_begin();
defineTrivialHLSLTypes();
+ defineInternalHLSLTypes();
defineHLSLTypesWithForwardDeclarations();
defineHLSLAtomicIntrinsics();
@@ -233,6 +234,29 @@ void HLSLExternalSemaSource::defineTrivialHLSLTypes() {
defineHLSLMatrixAlias();
}
+void HLSLExternalSemaSource::defineHeapResourceInfoTypes() {
+ ASTContext &AST = SemaPtr->getASTContext();
+ CXXRecordDecl *ResDecl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace,
+ "__hlsl_heap_resource_info")
+ .finalizeForwardDeclaration();
+ if (!ResDecl->isCompleteDefinition())
+ BuiltinTypeDeclBuilder(*SemaPtr, ResDecl)
+ .addMemberVariable("Index", AST.UnsignedIntTy, {})
+ .completeDefinition();
+
+ CXXRecordDecl *SampDecl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace,
+ "__hlsl_heap_sampler_info")
+ .finalizeForwardDeclaration();
+ if (!SampDecl->isCompleteDefinition())
+ BuiltinTypeDeclBuilder(*SemaPtr, SampDecl)
+ .addMemberVariable("Index", AST.UnsignedIntTy, {})
+ .completeDefinition();
+}
+
+void HLSLExternalSemaSource::defineInternalHLSLTypes() {
+ defineHeapResourceInfoTypes();
+}
+
/// Set up common members and attributes for buffer types
static BuiltinTypeDeclBuilder setupBufferType(CXXRecordDecl *Decl, Sema &S,
ResourceClass RC, bool IsROV,
@@ -242,6 +266,7 @@ static BuiltinTypeDeclBuilder setupBufferType(CXXRecordDecl *Decl, Sema &S,
.addDefaultHandleConstructor()
.addCopyConstructor()
.addCopyAssignmentOperator()
+ .addHeapResourceInfoConstructor(HasCounter)
.addStaticInitializationFunctions(HasCounter);
}
@@ -252,6 +277,7 @@ static BuiltinTypeDeclBuilder setupSamplerType(CXXRecordDecl *Decl, Sema &S) {
.addDefaultHandleConstructor()
.addCopyConstructor()
.addCopyAssignmentOperator()
+ .addHeapSamplerInfoConstructor()
.addStaticInitializationFunctions(false);
}
@@ -361,6 +387,7 @@ static BuiltinTypeDeclBuilder setupTextureType(CXXRecordDecl *Decl, Sema &S,
B.addDefaultHandleConstructor()
.addCopyConstructor()
.addCopyAssignmentOperator()
+ .addHeapResourceInfoConstructor()
.addStaticInitializationFunctions(false);
if (T.has(TexCap::Load))
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 06828b9ec7fc0..7c64266d855de 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -4397,9 +4397,35 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
}
case Builtin::BI__builtin_hlsl_resource_counterhandlefromimplicitbinding: {
assert(TheCall->getNumArgs() == 3 && "expected 3 args");
+ // Update return type to be the attributed resource type from arg0
+ // with added IsCounter flag.
QualType MainHandleTy = TheCall->getArg(0)->getType();
+ QualType CounterHandleTy =
+ createCounterHandleType(SemaRef.getASTContext(), MainHandleTy);
+ TheCall->setType(CounterHandleTy);
+ break;
+ }
+ case Builtin::BI__builtin_hlsl_resource_handlefromheap: {
+ if (SemaRef.checkArgCount(TheCall, 2) ||
+ CheckResourceHandle(&SemaRef, TheCall, 0) ||
+ CheckArgTypeMatches(&SemaRef, TheCall->getArg(1),
+ SemaRef.getASTContext().UnsignedIntTy))
+ return true;
+
+ // Update return type to be the attributed resource type from arg0.
+ QualType ResourceTy = TheCall->getArg(0)->getType();
+ TheCall->setType(ResourceTy);
+ break;
+ }
+ case Builtin::BI__builtin_hlsl_resource_counterhandlefromheap: {
+ if (SemaRef.checkArgCount(TheCall, 2) ||
+ CheckResourceHandle(&SemaRef, TheCall, 0) ||
+ CheckArgTypeMatches(&SemaRef, TheCall->getArg(1),
+ SemaRef.getASTContext().UnsignedIntTy))
+ return true;
// Update return type to be the attributed resource type from arg0
// with added IsCounter flag.
+ QualType MainHandleTy = TheCall->getArg(0)->getType();
QualType CounterHandleTy =
createCounterHandleType(SemaRef.getASTContext(), MainHandleTy);
TheCall->setType(CounterHandleTy);
diff --git a/clang/test/AST/HLSL/ByteAddressBuffers-AST.hlsl b/clang/test/AST/HLSL/ByteAddressBuffers-AST.hlsl
index d6d2aa0819e7b..3312045cf4def 100644
--- a/clang/test/AST/HLSL/ByteAddressBuffers-AST.hlsl
+++ b/clang/test/AST/HLSL/ByteAddressBuffers-AST.hlsl
@@ -82,6 +82,24 @@ RESOURCE Buffer;
// CHECK-NEXT: CXXThisExpr {{.*}} 'hlsl::[[RESOURCE]]' lvalue implicit this
// CHECK-NEXT: AlwaysInlineAttr
+// Heap info constructor
+
+// CHECK: CXXConstructorDecl {{.*}} [[RESOURCE]] 'void (hlsl::__hlsl_heap_resource_info)' inline
+// CHECK-NEXT: ParmVarDecl {{.*}} HeapResInfo 'hlsl::__hlsl_heap_resource_info'
+// 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 {{.*}} '__hlsl_resource_t (*)(__hlsl_resource_t, unsigned int) noexcept' <BuiltinFnToFnPtr>
+// CHECK-NEXT: DeclRefExpr {{.*}} '<builtin fn type>' Function {{.*}} '__builtin_hlsl_resource_handlefromheap' '__hlsl_resource_t (__hlsl_resource_t, unsigned int) noexcept'
+// CHECK-NEXT: MemberExpr {{.*}} '__hlsl_resource_t {{.*}}' lvalue .__handle
+// CHECK-NEXT: CXXThisExpr {{.*}} 'hlsl::[[RESOURCE]]' lvalue implicit this
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'unsigned int' <LValueToRValue>
+// CHECK-NEXT: MemberExpr {{.*}} 'unsigned int' lvalue .Index
+// CHECK-NEXT: DeclRefExpr {{.*}} 'hlsl::__hlsl_heap_resource_info' lvalue ParmVar {{.*}} 'HeapResInfo' 'hlsl::__hlsl_heap_resource_info'
+// CHECK-NEXT: AlwaysInlineAttr
+
// Static __createFromBinding method
// CHECK: CXXMethodDecl {{.*}} __createFromBinding 'hlsl::[[RESOURCE]] (unsigned int, unsigned int, int, unsigned int, const char *)' static
diff --git a/clang/test/AST/HLSL/DynamicResources-AST.hlsl b/clang/test/AST/HLSL/DynamicResources-AST.hlsl
new file mode 100644
index 0000000000000..00d181388a977
--- /dev/null
+++ b/clang/test/AST/HLSL/DynamicResources-AST.hlsl
@@ -0,0 +1,132 @@
+// RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.6-library -ast-dump %s | FileCheck %s
+
+// Global variables for ResourceDescriptorHeap and SamplerDescriptorHeap
+// CHECK: VarDecl {{.*}} used ResourceDescriptorHeap 'hlsl_private __hlsl_resource_descriptor_heap_struct' static internal-linkage
+// CHECK-NEXT: AvailabilityAttr {{.*}} shadermodel 6.6 0 0 "" "" 0
+
+// CHECK: VarDecl {{.*}} used SamplerDescriptorHeap 'hlsl_private __hlsl_sampler_descriptor_heap_struct' static internal-linkage
+// CHECK-NEXT: AvailabilityAttr {{.*}} shadermodel 6.6 0 0 "" "" 0
+
+void useBuffer(RWBuffer<int> Buffer) {}
+
+// CHECK-LABEL: FunctionDecl {{.*}} testInvocations
+export void testInvocations(unsigned Index) {
+
+// Buf1 declaration with direct initialization
+// CHECK: VarDecl {{.*}} Buf1 'RWBuffer<int>':'hlsl::RWBuffer<int>' cinit
+// CHECK-NEXT: ExprWithCleanups{{.*}} 'RWBuffer<int>':'hlsl::RWBuffer<int>'
+
+// RWBuffer copy constructor
+// CHECK-NEXT: CXXConstructExpr {{.*}} 'RWBuffer<int>':'hlsl::RWBuffer<int>' 'void (const hlsl::RWBuffer<int> &)'
+// CHECK-NEXT: MaterializeTemporaryExpr {{.*}} 'const RWBuffer<int>':'const hlsl::RWBuffer<int>' lvalue
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'const RWBuffer<int>':'const hlsl::RWBuffer<int>' <NoOp>
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'RWBuffer<int>':'hlsl::RWBuffer<int>' <ConstructorConversion>
+
+// RWBuffer heap info constructor
+// CHECK-NEXT: CXXConstructExpr {{.*}} 'RWBuffer<int>':'hlsl::RWBuffer<int>' 'void (hlsl::__hlsl_heap_resource_info)'
+
+// __hlsl_heap_resource_info copy constructor
+// CHECK-NEXT: CXXConstructExpr {{.*}} 'hlsl::__hlsl_heap_resource_info' 'void (__hlsl_heap_resource_info &&) noexcept' elidable
+
+// Indexing into ResourceDescriptorHeap
+// CHECK-NEXT: MaterializeTemporaryExpr {{.*}} '__hlsl_heap_resource_info' xvalue
+// CHECK-NEXT: CXXOperatorCallExpr {{.*}} '__hlsl_heap_resource_info' '[]'
+// CHECK-NEXT: ImplicitCastExpr {{.*}} '__hlsl_heap_resource_info (*)(uint32_t)' <FunctionToPointerDecay>
+// CHECK-NEXT: DeclRefExpr {{.*}} '__hlsl_heap_resource_info (uint32_t)' lvalue CXXMethod {{.*}} 'operator[]' '__hlsl_heap_resource_info (uint32_t)'
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'hlsl::__hlsl_resource_descriptor_heap_struct' lvalue <AddressSpaceConversion>
+// CHECK-NEXT: DeclRefExpr {{.*}} 'hlsl_private __hlsl_resource_descriptor_heap_struct' lvalue Var {{.*}} 'ResourceDescriptorHeap' 'hlsl_private __hlsl_resource_descriptor_heap_struct'
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'unsigned int' <LValueToRValue>
+// CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' lvalue ParmVar {{.*}} 'Index' 'unsigned int'
+
+ RWBuffer<int> Buf1 = ResourceDescriptorHeap[Index];
+
+// Buf2 declaration initialized with default constructor (handle is poison)
+// CHECK: VarDecl {{.*}} Buf2 'RWBuffer<int>':'hlsl::RWBuffer<int>' callinit
+// CHECK-NEXT: CXXConstructExpr {{.*}} 'RWBuffer<int>':'hlsl::RWBuffer<int>' 'void ()'
+
+// Buf2 assignment operator
+// CHECK-NEXT: ExprWithCleanups
+// CHECK-NEXT: CXXOperatorCallExpr {{.*}} 'hlsl::RWBuffer<int>' lvalue '='
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'hlsl::RWBuffer<int> &(*)(const hlsl::RWBuffer<int> &)' <FunctionToPointerDecay>
+// CHECK-NEXT: DeclRefExpr {{.*}} 'hlsl::RWBuffer<int> &(const hlsl::RWBuffer<int> &)' lvalue CXXMethod {{.*}} 'operator=' 'hlsl::RWBuffer<int> &(const hlsl::RWBuffer<int> &)'
+// CHECK-NEXT: DeclRefExpr {{.*}} 'RWBuffer<int>':'hlsl::RWBuffer<int>' lvalue Var {{.*}} 'Buf2' 'RWBuffer<int>':'hlsl::RWBuffer<int>'
+
+// CHECK-NEXT: MaterializeTemporaryExpr {{.*}} 'const hlsl::RWBuffer<int>' lvalue
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'const hlsl::RWBuffer<int>' <NoOp>
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'hlsl::RWBuffer<int>' <ConstructorConversion>
+
+// RWBuffer heap info constructor
+// CHECK-NEXT: CXXConstructExpr {{.*}} 'hlsl::RWBuffer<int>' 'void (hlsl::__hlsl_heap_resource_info)'
+
+// __hlsl_heap_resource_info copy constructor
+// CHECK-NEXT: CXXConstructExpr {{.*}} 'hlsl::__hlsl_heap_resource_info' 'void (__hlsl_heap_resource_info &&) noexcept' elidable
+
+// Indexing into ResourceDescriptorHeap
+// CHECK-NEXT: MaterializeTemporaryExpr {{.*}} '__hlsl_heap_resource_info' xvalue
+// CHECK-NEXT: CXXOperatorCallExpr {{.*}} '__hlsl_heap_resource_info' '[]'
+// CHECK-NEXT: ImplicitCastExpr {{.*}} '__hlsl_heap_resource_info (*)(uint32_t)' <FunctionToPointerDecay>
+// CHECK-NEXT: DeclRefExpr {{.*}} '__hlsl_heap_resource_info (uint32_t)' lvalue CXXMethod {{.*}} 'operator[]' '__hlsl_heap_resource_info (uint32_t)'
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'hlsl::__hlsl_resource_descriptor_heap_struct' lvalue <AddressSpaceConversion>
+// CHECK-NEXT: DeclRefExpr {{.*}} 'hlsl_private __hlsl_resource_descriptor_heap_struct' lvalue Var {{.*}} 'ResourceDescriptorHeap' 'hlsl_private __hlsl_resource_descriptor_heap_struct'
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'unsigned int' <LValueToRValue>
+// CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' lvalue ParmVar {{.*}} 'Index' 'unsigned int'
+
+ RWBuffer<int> Buf2;
+ Buf2 = ResourceDescriptorHeap[Index];
+
+// Call to useBuffer with a temporary RWBuffer constructed from ResourceDescriptorHeap[Index]
+// CHECK: CallExpr {{.*}} 'void'
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(RWBuffer<int>)' <FunctionToPointerDecay>
+// CHECK-NEXT: DeclRefExpr {{.*}} 'void (RWBuffer<int>)' lvalue Function {{.*}} 'useBuffer' 'void (RWBuffer<int>)'
+
+// RWBuffer copy constructor
+// CHECK-NEXT: CXXConstructExpr {{.*}} 'RWBuffer<int>':'hlsl::RWBuffer<int>' 'void (const hlsl::RWBuffer<int> &)'
+// CHECK-NEXT: MaterializeTemporaryExpr {{.*}} 'const RWBuffer<int>':'const hlsl::RWBuffer<int>' lvalue
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'const RWBuffer<int>':'const hlsl::RWBuffer<int>' <NoOp>
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'RWBuffer<int>':'hlsl::RWBuffer<int>' <ConstructorConversion>
+
+// RWBuffer heap info constructor
+// CHECK-NEXT: CXXConstructExpr {{.*}} 'RWBuffer<int>':'hlsl::RWBuffer<int>' 'void (hlsl::__hlsl_heap_resource_info)'
+
+// __hlsl_heap_resource_info copy constructor
+// CHECK-NEXT: CXXConstructExpr {{.*}} 'hlsl::__hlsl_heap_resource_info' 'void (__hlsl_heap_resource_info &&) noexcept' elidable
+
+// Indexing into ResourceDescriptorHeap
+// CHECK-NEXT: MaterializeTemporaryExpr {{.*}} '__hlsl_heap_resource_info' xvalue
+// CHECK-NEXT: CXXOperatorCallExpr {{.*}} '__hlsl_heap_resource_info' '[]'
+// CHECK-NEXT: ImplicitCastExpr {{.*}} '__hlsl_heap_resource_info (*)(uint32_t)' <FunctionToPointerDecay>
+// CHECK-NEXT: DeclRefExpr {{.*}} '__hlsl_heap_resource_info (uint32_t)' lvalue CXXMethod {{.*}} 'operator[]' '__hlsl_heap_resource_info (uint32_t)'
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'hlsl::__hlsl_resource_descriptor_heap_struct' lvalue <AddressSpaceConversion>
+// CHECK-NEXT: DeclRefExpr {{.*}} 'hlsl_private __hlsl_resource_descriptor_heap_struct' lvalue Var {{.*}} 'ResourceDescriptorHeap' 'hlsl_private __hlsl_resource_descriptor_heap_struct'
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'unsigned int' <LValueToRValue>
+// CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' lvalue ParmVar {{.*}} 'Index' 'unsigned int'
+
+ useBuffer(ResourceDescriptorHeap[Index]);
+
+// CHECK: VarDecl {{.*}} Sampler 'SamplerState' cinit
+// CHECK-NEXT: ExprWithCleanups {{.*}} 'SamplerState'
+
+// SamplerState copy constructor
+// CHECK-NEXT: CXXConstructExpr {{.*}} <col:16, col:53> 'SamplerState' 'void (const hlsl::SamplerState &)' elidable
+// CHECK-NEXT: MaterializeTemporaryExpr {{.*}} <col:26, col:53> 'const SamplerState' lvalue
+// CHECK-NEXT: ImplicitCastExpr {{.*}} <col:26, col:53> 'const SamplerState' <NoOp>
+// CHECK-NEXT: ImplicitCastExpr {{.*}} <col:26, col:53> 'SamplerState' <ConstructorConversion>
+
+// SamplerState heap info constructor
+// CHECK-NEXT: CXXConstructExpr {{.*}} <col:26, col:53> 'SamplerState' 'void (hlsl::__hlsl_heap_sampler_info)'
+
+// __hlsl_heap_resource_info copy constructor
+// CHECK-NEXT: CXXConstructExpr {{.*}} <col:26, col:53> 'hlsl::__hlsl_heap_sampler_info' 'void (__hlsl_heap_sampler_info &&) noexcept' elidable
+
+// Indexing into SamplerDescriptorHeap
+// CHECK-NEXT: MaterializeTemporaryExpr {{.*}} <col:26, col:53> '__hlsl_heap_sampler_info' xvalue
+// CHECK-NEXT: CXXOperatorCallExpr {{.*}} <col:26, col:53> '__hlsl_heap_sampler_info' '[]'
+// CHECK-NEXT: ImplicitCastExpr {{.*}} <col:47, col:53> '__hlsl_heap_sampler_info (*)(uint32_t)' <FunctionToPointerDecay>
+// CHECK-NEXT: DeclRefExpr {{.*}} <col:47, col:53> '__hlsl_heap_sampler_info (uint32_t)' lvalue CXXMethod {{.*}} 'operator[]' '__hlsl_heap_sampler_info (uint32_t)'
+// CHECK-NEXT: ImplicitCastExpr {{.*}} <col:26> 'hlsl::__hlsl_sampler_descriptor_heap_struct' lvalue <AddressSpaceConversion>
+// CHECK-NEXT: DeclRefExpr {{.*}} <col:26> 'hlsl_private __hlsl_sampler_descriptor_heap_struct' lvalue Var {{.*}} 'SamplerDescriptorHeap' 'hlsl_private __hlsl_sampler_descriptor_heap_struct'
+// CHECK-NEXT: ImplicitCastExpr {{.*}} <col:48> 'unsigned int' <LValueToRValue>
+// CHECK-NEXT: DeclRefExpr {{.*}} <col:48> 'unsigned int' lvalue ParmVar {{.*}} 'Index' 'unsigned int'
+
+ SamplerState Sampler = SamplerDescriptorHeap[Index];
+}
diff --git a/clang/test/AST/HLSL/StructuredBuffers-AST.hlsl b/clang/test/AST/HLSL/StructuredBuffers-AST.hlsl
index 6b38b73d82321..eb858cd51cdb6 100644
--- a/clang/test/AST/HLSL/StructuredBuffers-AST.hlsl
+++ b/clang/test/AST/HLSL/StructuredBuffers-AST.hlsl
@@ -139,6 +139,33 @@ RESOURCE<float> Buffer;
// CHECK-NEXT: CXXThisExpr {{.*}} 'hlsl::[[RESOURCE]]<element_type>' lvalue implicit this
// CHECK-NEXT: AlwaysInlineAttr
+// Heap info constructor
+
+// CHECK: CXXConstructorDecl {{.*}} [[RESOURCE]]<element_type> 'void (hlsl::__hlsl_heap_resource_info)' inline
+// CHECK-NEXT: ParmVarDecl {{.*}} HeapResInfo 'hlsl::__hlsl_heap_resource_info'
+// CHECK-NEXT: CompoundStmt
+// CHECK-NEXT: BinaryOperator {{.*}} '='
+// CHECK-NEXT: MemberExpr {{.*}} lvalue .__handle
+// CHECK-NEXT: CXXThisExpr {{.*}} 'hlsl::[[RESOURCE]]<element_type>' lvalue implicit this
+// CHECK-NEXT: CStyleCastExpr {{.*}} '__hlsl_resource_t
+// CHECK-NEXT: CallExpr {{.*}} '<dependent type>'
+// CHECK-NEXT: DeclRefExpr {{.*}} '<builtin fn type>' Function {{.*}} '__builtin_hlsl_resource_handlefromheap' '__hlsl_resource_t (__hlsl_resource_t, unsigned int) noexcept'
+// CHECK-NEXT: MemberExpr {{.*}} lvalue .__handle
+// CHECK-NEXT: CXXThisExpr {{.*}} 'hlsl::[[RESOURCE]]<element_type>' lvalue implicit this
+// CHECK-NEXT: MemberExpr {{.*}} 'unsigned int' lvalue .Index
+// CHECK-NEXT: DeclRefExpr {{.*}} 'hlsl::__hlsl_heap_resource_info' lvalue ParmVar {{.*}} 'HeapResInfo' 'hlsl::__hlsl_heap_resource_info'
+// CHECK-COUNTER-HANDLE-NEXT: BinaryOperator {{.*}} '='
+// CHECK-COUNTER-HANDLE-NEXT: MemberExpr {{.*}} lvalue .__counter_handle
+// CHECK-COUNTER-HANDLE-NEXT: CXXThisExpr {{.*}} 'hlsl::[[RESOURCE]]<element_type>' lvalue implicit this
+// CHECK-COUNTER-HANDLE-NEXT: CStyleCastExpr {{.*}} '__hlsl_resource_t
+// CHECK-COUNTER-HANDLE-NEXT: CallExpr {{.*}} '<dependent type>'
+// CHECK-COUNTER-HANDLE-NEXT: DeclRefExpr {{.*}} '<builtin fn type>' Function {{.*}} '__builtin_hlsl_resource_counterhandlefromheap' '__hlsl_resource_t (__hlsl_resource_t, unsigned int) noexcept'
+// CHECK-COUNTER-HANDLE-NEXT: MemberExpr {{.*}} lvalue .__handle
+// CHECK-COUNTER-HANDLE-NEXT: CXXThisExpr {{.*}} 'hlsl::[[RESOURCE]]<element_type>' lvalue implicit this
+// CHECK-COUNTER-HANDLE-NEXT: MemberExpr {{.*}} 'unsigned int' lvalue .Index
+// CHECK-COUNTER-HANDLE-NEXT: DeclRefExpr {{.*}} 'hlsl::__hlsl_heap_resource_info' lvalue ParmVar {{.*}} 'HeapResInfo' 'hlsl::__hlsl_heap_resource_info'
+// CHECK-NEXT: AlwaysInlineAttr
+
// Static __createFromBinding method
// CHECK-BINDING: CXXMethodDecl {{.*}} __createFromBinding 'hlsl::[[RESOURCE]]<element_type> (unsigned int, unsigned int, int, unsigned int, const char *)' static
diff --git a/clang/test/AST/HLSL/TypedBuffers-AST.hlsl b/clang/test/AST/HLSL/TypedBuffers-AST.hlsl
index a2708e0782bef..667486b34e84f 100644
--- a/clang/test/AST/HLSL/TypedBuffers-AST.hlsl
+++ b/clang/test/AST/HLSL/TypedBuffers-AST.hlsl
@@ -104,6 +104,23 @@ RESOURCE<float> Buffer;
// CHECK-NEXT: CXXThisExpr {{.*}} 'hlsl::[[RESOURCE]]<element_type>' lvalue implicit this
// CHECK-NEXT: AlwaysInlineAttr
+// Heap info constructor
+
+// CHECK: CXXConstructorDecl {{.*}} [[RESOURCE]]<element_type> 'void (hlsl::__hlsl_heap_resource_info)' inline
+// CHECK-NEXT: ParmVarDecl {{.*}} HeapResInfo 'hlsl::__hlsl_heap_resource_info'
+// CHECK-NEXT: CompoundStmt
+// CHECK-NEXT: BinaryOperator {{.*}} '='
+// CHECK-NEXT: MemberExpr {{.*}} lvalue .__handle
+// CHECK-NEXT: CXXThisExpr {{.*}} 'hlsl::[[RESOURCE]]<element_type>' lvalue implicit this
+// CHECK-NEXT: CStyleCastExpr {{.*}} '__hlsl_resource_t
+// CHECK-NEXT: CallExpr {{.*}} '<dependent type>'
+// CHECK-NEXT: DeclRefExpr {{.*}} '<builtin fn type>' Function {{.*}} '__builtin_hlsl_resource_handlefromheap' '__hlsl_resource_t (__hlsl_resource_t, unsigned int) noexcept'
+// CHECK-NEXT: MemberExpr {{.*}} lvalue .__handle
+// CHECK-NEXT: CXXThisExpr {{.*}} 'hlsl::[[RESOURCE]]<element_type>' lvalue implicit this
+// CHECK-NEXT: MemberExpr {{.*}} 'unsigned int' lvalue .Index
+// CHECK-NEXT: DeclRefExpr {{.*}} 'hlsl::__hlsl_heap_resource_info' lvalue ParmVar {{.*}} 'HeapResInfo' 'hlsl::__hlsl_heap_resource_info'
+// CHECK-NEXT: AlwaysInlineAttr
+
// Static __createFromBinding method
// CHECK: CXXMethodDecl {{.*}} __createFromBinding 'hlsl::[[RESOURCE]]<element_type> (unsigned int, unsigned int, int, unsigned int, const char *)' static
diff --git a/clang/test/CodeGenHLSL/resources/dynamic-resources.hlsl b/clang/test/CodeGenHLSL/resources/dynamic-resources.hlsl
new file mode 100644
index 0000000000000..02d641fdeef5f
--- /dev/null
+++ b/clang/test/CodeGenHLSL/resources/dynamic-resources.hlsl
@@ -0,0 +1,109 @@
+// RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.6-library \
+// RUN: -emit-llvm -disable-llvm-passes -o - %s | llvm-cxxfilt | FileCheck %s --check-prefixes=CHECK,DXIL
+// RUN: %clang_cc1 -finclude-default-header -triple spirv-vulkan-library \
+// RUN: -emit-llvm -disable-llvm-passes -o - %s | llvm-cxxfilt | FileCheck %s --check-prefixes=CHECK,SPV
+
+// CHECK: %S = type <{ i32 }>
+
+// CHECK: @hlsl::ResourceDescriptorHeap = internal {{.*}}global %"struct.hlsl::__hlsl_resource_descriptor_heap_struct" zeroinitializer, align 1
+// CHECK: @hlsl::SamplerDescriptorHeap = internal {{.*}}global %"struct.hlsl::__hlsl_sampler_descriptor_heap_struct" zeroinitializer, align 1
+
+// CHECK-LABEL: testTypedBuffer
+export void testTypedBuffer(unsigned Index) {
+// CHECK: [[TMP0:%.*]] = alloca %"class.hlsl::__hlsl_heap_resource_info"
+
+// DXIL: call void @hlsl::__hlsl_resource_descriptor_heap_struct::operator[](unsigned int)
+// DXIL-SAME: (ptr {{.*}} sret(%"class.hlsl::__hlsl_heap_resource_info") align 4 [[TMP0]],
+// DXIL-SAME: ptr {{.*}} @hlsl::ResourceDescriptorHeap, i32 noundef %{{[0-9]+}})
+
+// SPV: call spir_func void @hlsl::__hlsl_resource_descriptor_heap_struct::operator[](unsigned int)
+// SPV-SAME: (ptr {{.*}} sret(%"class.hlsl::__hlsl_heap_resource_info") align 4 [[TMP0]],
+// SPV-SAME: ptr {{.*}} addrspacecast ({{.*}} @hlsl::ResourceDescriptorHeap to ptr), i32 noundef %{{[0-9]+}})
+
+// CHECK: call {{(spir_func )*}}void @hlsl::RWBuffer<int>::RWBuffer(hlsl::__hlsl_heap_resource_info)
+// CHECK-SAME: (ptr {{.*}} %Buffer, ptr noundef byval(%"class.hlsl::__hlsl_heap_resource_info") align 4 [[TMP0]])
+ RWBuffer<int> Buffer = ResourceDescriptorHeap[Index];
+}
+
+struct S {
+ int Value;
+};
+
+// CHECK-LABEL: testConstantBuffer
+export void testConstantBuffer(unsigned Index) {
+
+// CHECK: %CBS = alloca %"class.hlsl::ConstantBuffer"
+// CHECK: [[TMP1:%.*]] = alloca %"class.hlsl::ConstantBuffer"
+// CHECK: [[TMP2:%.*]] = alloca %"class.hlsl::__hlsl_heap_resource_info"
+// CHECK: call {{(spir_func )*}}void @hlsl::ConstantBuffer<S>::ConstantBuffer()(ptr {{.*}} %CBS)
+
+// DXIL: call void @hlsl::__hlsl_resource_descriptor_heap_struct::operator[](unsigned int)
+// DXIL-SAME: (ptr {{.*}} sret(%"class.hlsl::__hlsl_heap_resource_info") align 4 [[TMP2]],
+// DXIL-SAME: ptr {{.*}} @hlsl::ResourceDescriptorHeap, i32 noundef %{{[0-9]+}})
+
+// SPV: call spir_func void @hlsl::__hlsl_resource_descriptor_heap_struct::operator[](unsigned int)
+// SPV-SAME: (ptr {{.*}} sret(%"class.hlsl::__hlsl_heap_resource_info") align 4 [[TMP2]],
+// SPV-SAME: ptr {{.*}} addrspacecast ({{.*}} @hlsl::ResourceDescriptorHeap to ptr), i32 noundef %{{[0-9]+}})
+
+// CHECK: call {{(spir_func )*}}void @hlsl::ConstantBuffer<S>::ConstantBuffer(hlsl::__hlsl_heap_resource_info)
+// CHECK-SAME: (ptr {{.*}} [[TMP1]], ptr noundef byval(%"class.hlsl::__hlsl_heap_resource_info") align 4 %{{.*}})
+
+// CHECK: call {{.*}} ptr @hlsl::ConstantBuffer<S>::operator=(hlsl::ConstantBuffer<S> const&)
+// CHECK-SAME: (ptr {{.*}} %CBS, ptr {{.*}} [[TMP1]])
+ ConstantBuffer<S> CBS;
+ CBS = ResourceDescriptorHeap[Index];
+}
+
+// CHECK: define {{(spir_func )*}}void @testSampler(unsigned int)(ptr {{.*}} sret(%"class.hlsl::SamplerState") align {{(4|8)}} [[RESULT:%.*]], i32 noundef %Index)
+// CHECK: [[TMP3:%.*]] = alloca %"class.hlsl::__hlsl_heap_sampler_info"
+export SamplerState testSampler(unsigned Index) {
+
+// DXIL: call void @hlsl::__hlsl_sampler_descriptor_heap_struct::operator[](unsigned int)
+// DXIL-SAME: (ptr {{.*}} sret(%"class.hlsl::__hlsl_heap_sampler_info") align 4 [[TMP3]],
+// DXIL-SAME: ptr {{.*}} @hlsl::SamplerDescriptorHeap, i32 noundef %{{[0-9]+}})
+
+// SPV: call spir_func void @hlsl::__hlsl_sampler_descriptor_heap_struct::operator[](unsigned int)
+// SPV-SAME: (ptr {{.*}} sret(%"class.hlsl::__hlsl_heap_sampler_info") align 4 [[TMP3]],
+// SPV-SAME: ptr {{.*}} addrspacecast ({{.*}} @hlsl::SamplerDescriptorHeap to ptr), i32 noundef %{{[0-9]+}})
+
+// CHECK: call {{(spir_func )*}}void @hlsl::SamplerState::SamplerState(hlsl::__hlsl_heap_sampler_info)
+// CHECK-SAME: (ptr {{.*}} [[RESULT]], ptr noundef byval(%"class.hlsl::__hlsl_heap_sampler_info") align 4 [[TMP3]])
+ return SamplerDescriptorHeap[Index];
+}
+
+void useAppendBuffer(AppendStructuredBuffer<int> Buffer, int Value) {
+ Buffer.Append(1);
+}
+
+// CHECK-LABEL: testCounterBuffer
+export void testCounterBuffer(unsigned Index) {
+// CHECK: [[TMP_BUFFER:%.*]] = alloca %"class.hlsl::AppendStructuredBuffer"
+// CHECK: [[TMP4:%.*]] = alloca %"class.hlsl::__hlsl_heap_resource_info"
+
+// DXIL: call void @hlsl::__hlsl_resource_descriptor_heap_struct::operator[](unsigned int)
+// DXIL-SAME: (ptr {{.*}} sret(%"class.hlsl::__hlsl_heap_resource_info") align 4 [[TMP4]],
+// DXIL-SAME: ptr {{.*}} @hlsl::ResourceDescriptorHeap, i32 noundef %{{[0-9]+}})
+
+// SPV: call spir_func void @hlsl::__hlsl_resource_descriptor_heap_struct::operator[](unsigned int)
+// SPV-SAME: (ptr {{.*}} sret(%"class.hlsl::__hlsl_heap_resource_info") align 4 [[TMP4]],
+// SPV-SAME: ptr {{.*}} addrspacecast ({{.*}} @hlsl::ResourceDescriptorHeap to ptr), i32 noundef %{{[0-9]+}})
+
+// CHECK: call {{(spir_func )*}}void @hlsl::AppendStructuredBuffer<int>::AppendStructuredBuffer(hlsl::__hlsl_heap_resource_info)
+// CHECK-SAME: (ptr {{.*}} [[TMP_BUFFER]], ptr noundef byval(%"class.hlsl::__hlsl_heap_resource_info") align 4 [[TMP4]])
+
+// CHECK: call {{(spir_func )*}}void @useAppendBuffer(hlsl::AppendStructuredBuffer<int>, int)
+// CHECK-SAME: (ptr {{.*}} [[TMP_BUFFER]], i32 noundef 10)
+ useAppendBuffer(ResourceDescriptorHeap[Index], 10);
+}
+
+// DXIL-DAG: call target("dx.TypedBuffer", i32, 1, 0, 1) @llvm.dx.resource.handlefromheap{{.*}}(i32 %{{.*}})
+// DXIL-DAG: call target("dx.CBuffer", %S) @llvm.dx.resource.handlefromheap{{.*}}(i32 %{{.*}})
+// DXIL-DAG: call target("dx.Sampler", 0) @llvm.dx.resource.handlefromheap{{.*}}(i32 %{{.*}})
+// DXIL-DAG: call target("dx.RawBuffer", i32, 1, 0) @llvm.dx.resource.handlefromheap{{.*}}(i32 %{{.*}})
+// DXIL-NOT: counterhandlefromheap
+
+// SPV-DAG: call target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 24) @llvm.spv.resource.handlefromheap{{.*}}(i32 %{{.*}})
+// SPV-DAG: call target("spirv.VulkanBuffer", %S, 2, 0) @llvm.spv.resource.handlefromheap{{.*}}(i32 %{{.*}})
+// SPV-DAG: call target("spirv.Sampler") @llvm.spv.resource.handlefromheap{{.*}}(i32 %{{.*}})
+// SPV-DAG: call target("spirv.VulkanBuffer", [0 x i32], 12, 1) @llvm.spv.resource.handlefromheap{{.*}}(i32 %{{.*}})
+// SPV-DAG: call target("spirv.VulkanBuffer", i32, 12, 1) @llvm.spv.resource.counterhandlefromheap{{.*}}(target("spirv.VulkanBuffer", [0 x i32], 12, 1) %{{.*}}, i32 %{{.*}}) [ "convergencectrl"(token %{{.*}}) ]
diff --git a/clang/test/SemaHLSL/Language/InitLists.hlsl b/clang/test/SemaHLSL/Language/InitLists.hlsl
index 33c5700544449..f86b029713515 100644
--- a/clang/test/SemaHLSL/Language/InitLists.hlsl
+++ b/clang/test/SemaHLSL/Language/InitLists.hlsl
@@ -117,3 +117,4 @@ void Err2(RWBuffer<float4> B) {
// This note refers to the RWBuffer copy constructor that do not have a source locations
// expected-note@*{{candidate constructor not viable}}
+// expected-note@*{{candidate constructor not viable}}
diff --git a/clang/test/SemaHLSL/Resources/Textures-SampleCmpLevelZero.hlsl b/clang/test/SemaHLSL/Resources/Textures-SampleCmpLevelZero.hlsl
index c3d7b41fba2ea..5dbcc3dd5fc05 100644
--- a/clang/test/SemaHLSL/Resources/Textures-SampleCmpLevelZero.hlsl
+++ b/clang/test/SemaHLSL/Resources/Textures-SampleCmpLevelZero.hlsl
@@ -74,8 +74,9 @@ void main(COORD_TYPE loc, float cmp) {
// offset-note@*:* {{candidate function not viable: no known conversion from 'SamplerState' to 'hlsl::SamplerComparisonState' for 1st argument}}
// offset-note@*:* {{candidate function not viable: requires 4 arguments, but 3 were provided}}
// nooffset-note@*:* {{candidate constructor not viable: no known conversion from 'SamplerState' to 'const hlsl::SamplerComparisonState &' for 1st argument}}
- // offset-error at +2 {{no matching member function for call to 'SampleCmpLevelZero'}}
- // nooffset-error at +1 {{no viable conversion from 'SamplerState' to 'hlsl::SamplerComparisonState'}}
+ // offset-error at +3 {{no matching member function for call to 'SampleCmpLevelZero'}}
+ // nooffset-error at +2 {{no viable conversion from 'SamplerState' to 'hlsl::SamplerComparisonState'}}
+ // nooffset-note@*:* {{candidate constructor not viable: no known conversion from 'SamplerState' to 'hlsl::__hlsl_heap_sampler_info' for 1st argument}}
t.SampleCmpLevelZero(s2, loc, cmp);
#ifdef HAS_OFFSET
diff --git a/clang/test/SemaHLSL/Resources/dynamic-resources-availability.hlsl b/clang/test/SemaHLSL/Resources/dynamic-resources-availability.hlsl
new file mode 100644
index 0000000000000..dc9f0332da9c9
--- /dev/null
+++ b/clang/test/SemaHLSL/Resources/dynamic-resources-availability.hlsl
@@ -0,0 +1,13 @@
+// RUN: %clang_cc1 -finclude-default-header -fhlsl-strict-availability \
+// RUN: -triple dxil-pc-shadermodel6.5-library -verify %s
+
+export void testUnavailable(unsigned Index) {
+
+ // expected-error at +2 {{'ResourceDescriptorHeap' is only available on Shader Model 6.6 or newer}}
+ // expected-note at hlsl/hlsl_resources.h:* {{'ResourceDescriptorHeap' has been marked as being introduced in Shader Model 6.6 here, but the deployment target is Shader Model 6.5}}
+ RWBuffer<int> Buffer = ResourceDescriptorHeap[Index];
+
+ // expected-error at +2 {{'SamplerDescriptorHeap' is only available on Shader Model 6.6 or newer}}
+ // expected-note at hlsl/hlsl_resources.h:* {{'SamplerDescriptorHeap' has been marked as being introduced in Shader Model 6.6 here, but the deployment target is Shader Model 6.5}}
+ SamplerState Sampler = SamplerDescriptorHeap[Index];
+}
diff --git a/clang/test/SemaHLSL/Resources/dynamic-resources.hlsl b/clang/test/SemaHLSL/Resources/dynamic-resources.hlsl
new file mode 100644
index 0000000000000..f3300de0f7ce5
--- /dev/null
+++ b/clang/test/SemaHLSL/Resources/dynamic-resources.hlsl
@@ -0,0 +1,50 @@
+// RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.6-library -verify %s
+
+void useBuffer(RWBuffer<int> Buffer) {}
+void useSampler(SamplerState Sampler) {}
+
+export void test(unsigned Index) {
+
+ // valid
+ RWBuffer<int> Buf1 = ResourceDescriptorHeap[Index];
+ Buf1 = ResourceDescriptorHeap[Index + 1];
+ useBuffer(ResourceDescriptorHeap[Index + 2]);
+
+ // valid
+ SamplerState Sampler = SamplerDescriptorHeap[Index];
+ Sampler = SamplerDescriptorHeap[Index + 1];
+ useSampler(SamplerDescriptorHeap[Index + 2]);
+
+ // expected-error at +3 {{no viable conversion from '__hlsl_heap_sampler_info' to 'RWBuffer<int>'}}
+ // expected-note@*:* {{candidate constructor not viable: no known conversion from '__hlsl_heap_sampler_info' to 'const hlsl::RWBuffer<int> &' for 1st argument}}
+ // expected-note@*:* {{candidate constructor not viable: no known conversion from '__hlsl_heap_sampler_info' to 'hlsl::__hlsl_heap_resource_info' for 1st argument}}
+ RWBuffer<int> Buf2 = SamplerDescriptorHeap[Index];
+
+ // expected-error at +3 {{no viable conversion from '__hlsl_heap_resource_info' to 'SamplerState'}}
+ // expected-note@*:* {{candidate constructor not viable: no known conversion from '__hlsl_heap_resource_info' to 'const hlsl::SamplerState &' for 1st argument}}
+ // expected-note@*:* {{candidate constructor not viable: no known conversion from '__hlsl_heap_resource_info' to 'hlsl::__hlsl_heap_sampler_info' for 1st argument}}
+ SamplerState Sampler2 = ResourceDescriptorHeap[Index];
+
+ // expected-error at +2 {{no viable overloaded '='}}
+ // expected-note@*:* {{candidate function not viable: no known conversion from '__hlsl_heap_sampler_info' to 'RWBuffer<int>' for 1st argument}}
+ Buf2 = SamplerDescriptorHeap[Index];
+
+ // expected-error at +2 {{no viable overloaded '='}}
+ // expected-note@*:* {{candidate function not viable: no known conversion from '__hlsl_heap_resource_info' to 'SamplerState' for 1st argument}}
+ Sampler2 = ResourceDescriptorHeap[Index];
+
+ // expected-error at +2 {{no matching function for call to 'useBuffer'}}
+ // expected-note@*:* {{candidate function not viable: no known conversion from '__hlsl_heap_sampler_info' to 'const hlsl::RWBuffer<int>' for 1st argument}}
+ useBuffer(SamplerDescriptorHeap[Index]);
+
+ // expected-error at +2 {{no matching function for call to 'useSampler'}}
+ // expected-note@*:* {{candidate function not viable: no known conversion from '__hlsl_heap_resource_info' to 'const hlsl::SamplerState' for 1st argument}}
+ useSampler(ResourceDescriptorHeap[Index]);
+
+ // expected-error at +1 {{no member named 'Load'}}
+ ResourceDescriptorHeap[Index].Load(0);
+
+ // expected-error at +1 {{no member named 'Sample'}}
+ SamplerDescriptorHeap[Index].Sample(0);
+
+}
diff --git a/llvm/include/llvm/IR/IntrinsicsSPIRV.td b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
index 86b49a8ee446a..28b75badee306 100644
--- a/llvm/include/llvm/IR/IntrinsicsSPIRV.td
+++ b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
@@ -207,6 +207,15 @@ def int_spv_rsqrt : DefaultAttrsIntrinsic<[LLVMMatchType<0>], [llvm_anyfloat_ty]
[llvm_any_ty, llvm_i32_ty, llvm_i32_ty],
[IntrNoMem, IntrConvergent]>;
+ def int_spv_resource_handlefromheap
+ : DefaultAttrsIntrinsic<[llvm_any_ty],
+ [llvm_i32_ty],
+ [IntrNoMem]>;
+ def int_spv_resource_counterhandlefromheap
+ : DefaultAttrsIntrinsic<[llvm_any_ty],
+ [llvm_any_ty, llvm_i32_ty],
+ [IntrNoMem, IntrConvergent]>;
+
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]>;
def int_spv_firstbitlow : DefaultAttrsIntrinsic<[LLVMScalarOrSameVectorWidth<0, llvm_i32_ty>], [llvm_anyint_ty], [IntrNoMem]>;
>From 3d582681329f0d11b669470533f92e5e1875cf14 Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Thu, 10 Sep 2026 22:56:41 -0700
Subject: [PATCH 2/2] Remove heap index argument from
llvm.spv.resource.counterhandlefromheap
The index is not needed - it is copied from the main handle initialization call.
---
clang/include/clang/Basic/Builtins.td | 2 +-
clang/lib/CodeGen/CGHLSLBuiltins.cpp | 3 +--
clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp | 3 +--
clang/lib/Sema/SemaHLSL.cpp | 6 ++----
clang/test/AST/HLSL/StructuredBuffers-AST.hlsl | 4 +---
clang/test/CodeGenHLSL/resources/dynamic-resources.hlsl | 2 +-
llvm/include/llvm/IR/IntrinsicsSPIRV.td | 2 +-
7 files changed, 8 insertions(+), 14 deletions(-)
diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index 2b33b4a8541b3..8b15aecbfde92 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -5440,7 +5440,7 @@ def HLSLResourceHandleFromHeap : LangBuiltin<"HLSL_LANG"> {
def HLSLResourceCounterHandleFromHeap : LangBuiltin<"HLSL_LANG"> {
let Spellings = ["__builtin_hlsl_resource_counterhandlefromheap"];
let Attributes = [NoThrow];
- let Prototype = "__hlsl_resource_t(__hlsl_resource_t, uint32_t)";
+ let Prototype = "__hlsl_resource_t(__hlsl_resource_t)";
}
def HLSLResourceNonUniformIndex : LangBuiltin<"HLSL_LANG"> {
diff --git a/clang/lib/CodeGen/CGHLSLBuiltins.cpp b/clang/lib/CodeGen/CGHLSLBuiltins.cpp
index 42c107405dff3..e4f5ada9a0aef 100644
--- a/clang/lib/CodeGen/CGHLSLBuiltins.cpp
+++ b/clang/lib/CodeGen/CGHLSLBuiltins.cpp
@@ -1077,10 +1077,9 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID,
return MainHandle;
llvm::Type *HandleTy = CGM.getTypes().ConvertType(E->getType());
- Value *IndexOp = EmitScalarExpr(E->getArg(1));
llvm::Intrinsic::ID IntrinsicID =
llvm::Intrinsic::spv_resource_counterhandlefromheap;
- SmallVector<Value *> Args{MainHandle, IndexOp};
+ SmallVector<Value *> Args{MainHandle};
return EmitIntrinsicCall(IntrinsicID, {HandleTy, MainHandle->getType()},
Args);
}
diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
index 15f78eadad4e2..a5e56154984eb 100644
--- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
+++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
@@ -1246,8 +1246,7 @@ BuiltinTypeDeclBuilder::addHeapResourceInfoConstructor(bool HasCounter) {
if (HasCounter) {
QualType CounterHandleType = getResourceCounterHandleField()->getType();
MB.callBuiltin("__builtin_hlsl_resource_counterhandlefromheap",
- CounterHandleType, PH::Handle,
- MB.createMemberExpr(PH::_0, IndexField))
+ CounterHandleType, PH::Handle)
.assign(PH::CounterHandle, PH::LastStmt);
}
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 7c64266d855de..2d03487e99db6 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -4418,10 +4418,8 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
break;
}
case Builtin::BI__builtin_hlsl_resource_counterhandlefromheap: {
- if (SemaRef.checkArgCount(TheCall, 2) ||
- CheckResourceHandle(&SemaRef, TheCall, 0) ||
- CheckArgTypeMatches(&SemaRef, TheCall->getArg(1),
- SemaRef.getASTContext().UnsignedIntTy))
+ if (SemaRef.checkArgCount(TheCall, 1) ||
+ CheckResourceHandle(&SemaRef, TheCall, 0))
return true;
// Update return type to be the attributed resource type from arg0
// with added IsCounter flag.
diff --git a/clang/test/AST/HLSL/StructuredBuffers-AST.hlsl b/clang/test/AST/HLSL/StructuredBuffers-AST.hlsl
index eb858cd51cdb6..c86a4115f412c 100644
--- a/clang/test/AST/HLSL/StructuredBuffers-AST.hlsl
+++ b/clang/test/AST/HLSL/StructuredBuffers-AST.hlsl
@@ -159,11 +159,9 @@ RESOURCE<float> Buffer;
// CHECK-COUNTER-HANDLE-NEXT: CXXThisExpr {{.*}} 'hlsl::[[RESOURCE]]<element_type>' lvalue implicit this
// CHECK-COUNTER-HANDLE-NEXT: CStyleCastExpr {{.*}} '__hlsl_resource_t
// CHECK-COUNTER-HANDLE-NEXT: CallExpr {{.*}} '<dependent type>'
-// CHECK-COUNTER-HANDLE-NEXT: DeclRefExpr {{.*}} '<builtin fn type>' Function {{.*}} '__builtin_hlsl_resource_counterhandlefromheap' '__hlsl_resource_t (__hlsl_resource_t, unsigned int) noexcept'
+// CHECK-COUNTER-HANDLE-NEXT: DeclRefExpr {{.*}} '<builtin fn type>' Function {{.*}} '__builtin_hlsl_resource_counterhandlefromheap' '__hlsl_resource_t (__hlsl_resource_t) noexcept'
// CHECK-COUNTER-HANDLE-NEXT: MemberExpr {{.*}} lvalue .__handle
// CHECK-COUNTER-HANDLE-NEXT: CXXThisExpr {{.*}} 'hlsl::[[RESOURCE]]<element_type>' lvalue implicit this
-// CHECK-COUNTER-HANDLE-NEXT: MemberExpr {{.*}} 'unsigned int' lvalue .Index
-// CHECK-COUNTER-HANDLE-NEXT: DeclRefExpr {{.*}} 'hlsl::__hlsl_heap_resource_info' lvalue ParmVar {{.*}} 'HeapResInfo' 'hlsl::__hlsl_heap_resource_info'
// CHECK-NEXT: AlwaysInlineAttr
// Static __createFromBinding method
diff --git a/clang/test/CodeGenHLSL/resources/dynamic-resources.hlsl b/clang/test/CodeGenHLSL/resources/dynamic-resources.hlsl
index 02d641fdeef5f..39cf212cee889 100644
--- a/clang/test/CodeGenHLSL/resources/dynamic-resources.hlsl
+++ b/clang/test/CodeGenHLSL/resources/dynamic-resources.hlsl
@@ -106,4 +106,4 @@ export void testCounterBuffer(unsigned Index) {
// SPV-DAG: call target("spirv.VulkanBuffer", %S, 2, 0) @llvm.spv.resource.handlefromheap{{.*}}(i32 %{{.*}})
// SPV-DAG: call target("spirv.Sampler") @llvm.spv.resource.handlefromheap{{.*}}(i32 %{{.*}})
// SPV-DAG: call target("spirv.VulkanBuffer", [0 x i32], 12, 1) @llvm.spv.resource.handlefromheap{{.*}}(i32 %{{.*}})
-// SPV-DAG: call target("spirv.VulkanBuffer", i32, 12, 1) @llvm.spv.resource.counterhandlefromheap{{.*}}(target("spirv.VulkanBuffer", [0 x i32], 12, 1) %{{.*}}, i32 %{{.*}}) [ "convergencectrl"(token %{{.*}}) ]
+// SPV-DAG: call target("spirv.VulkanBuffer", i32, 12, 1) @llvm.spv.resource.counterhandlefromheap{{.*}}(target("spirv.VulkanBuffer", [0 x i32], 12, 1) %{{.*}}) [ "convergencectrl"(token %{{.*}}) ]
diff --git a/llvm/include/llvm/IR/IntrinsicsSPIRV.td b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
index 28b75badee306..3f6eba6082152 100644
--- a/llvm/include/llvm/IR/IntrinsicsSPIRV.td
+++ b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
@@ -213,7 +213,7 @@ def int_spv_rsqrt : DefaultAttrsIntrinsic<[LLVMMatchType<0>], [llvm_anyfloat_ty]
[IntrNoMem]>;
def int_spv_resource_counterhandlefromheap
: DefaultAttrsIntrinsic<[llvm_any_ty],
- [llvm_any_ty, llvm_i32_ty],
+ [llvm_any_ty],
[IntrNoMem, IntrConvergent]>;
def int_spv_firstbituhigh : DefaultAttrsIntrinsic<[LLVMScalarOrSameVectorWidth<0, llvm_i32_ty>], [llvm_anyint_ty], [IntrNoMem]>;
More information about the cfe-commits
mailing list