Skip to content

[HLSL] Fix resource wrapper declaration #129100

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Mar 5, 2025
Merged

Conversation

s-perron
Copy link
Contributor

The resource wrapper should have internal linkage because it contains a
handle to the global resource, and it not the actual global.

Makeing this changed exposed that we were zeroinitializing the resouce,
which is a problem. The handle cannot be zeroinitialized. This is
changed to use poison instead.

Fixes #122767.

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" clang:codegen IR generation bugs: mangling, exceptions, etc. HLSL HLSL Language Support labels Feb 27, 2025
@llvmbot
Copy link
Member

llvmbot commented Feb 27, 2025

@llvm/pr-subscribers-clang

@llvm/pr-subscribers-hlsl

Author: Steven Perron (s-perron)

Changes

The resource wrapper should have internal linkage because it contains a
handle to the global resource, and it not the actual global.

Makeing this changed exposed that we were zeroinitializing the resouce,
which is a problem. The handle cannot be zeroinitialized. This is
changed to use poison instead.

Fixes #122767.


Full diff: https://github.com/llvm/llvm-project/pull/129100.diff

11 Files Affected:

  • (modified) clang/include/clang/AST/Type.h (+1)
  • (modified) clang/lib/AST/Type.cpp (+27)
  • (modified) clang/lib/CodeGen/CGHLSLRuntime.cpp (+5)
  • (modified) clang/lib/CodeGen/CGHLSLRuntime.h (+2)
  • (modified) clang/lib/CodeGen/CodeGenModule.cpp (+5-1)
  • (modified) clang/lib/Sema/SemaHLSL.cpp (+7)
  • (modified) clang/test/CodeGenHLSL/builtins/ByteAddressBuffers-constructors.hlsl (+12-12)
  • (modified) clang/test/CodeGenHLSL/builtins/RWBuffer-constructor-opt.hlsl (+3-11)
  • (modified) clang/test/CodeGenHLSL/builtins/RWBuffer-constructor.hlsl (+5-5)
  • (modified) clang/test/CodeGenHLSL/builtins/StructuredBuffers-constructors.hlsl (+25-25)
  • (modified) clang/test/CodeGenHLSL/resource-bindings.hlsl (+16-16)
diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h
index c3ff7ebd88516..c5afae48b6a5f 100644
--- a/clang/include/clang/AST/Type.h
+++ b/clang/include/clang/AST/Type.h
@@ -2661,6 +2661,7 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
   bool isHLSLSpecificType() const; // Any HLSL specific type
   bool isHLSLBuiltinIntangibleType() const; // Any HLSL builtin intangible type
   bool isHLSLAttributedResourceType() const;
+  bool isHLSLResourceWrapper() const;
   bool isHLSLIntangibleType()
       const; // Any HLSL intangible type (builtin, array, class)
 
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 8c11ec2e1fe24..e924f49168c52 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -5098,6 +5098,33 @@ bool Type::hasSizedVLAType() const {
   return false;
 }
 
+bool Type::isHLSLResourceWrapper() const {
+  const Type *Ty = getUnqualifiedDesugaredType();
+
+  // check if it's a builtin type first
+  if (Ty->isBuiltinType())
+    return Ty->isHLSLBuiltinIntangibleType();
+
+  // unwrap arrays
+  while (isa<ConstantArrayType>(Ty))
+    Ty = Ty->getArrayElementTypeNoTypeQual();
+
+  const RecordType *RT =
+      dyn_cast<RecordType>(Ty->getUnqualifiedDesugaredType());
+  if (!RT)
+    return false;
+
+  CXXRecordDecl *RD = RT->getAsCXXRecordDecl();
+  assert(RD != nullptr &&
+      "all HLSL structs and classes should be CXXRecordDecl");
+  assert(RD->isCompleteDefinition() && "expecting complete type");
+  if (RD->field_empty()) {
+    return false;
+  }
+  const FieldDecl *FirstField = *RD->field_begin();
+  return FirstField->getType()->isHLSLAttributedResourceType();
+}
+
 bool Type::isHLSLIntangibleType() const {
   const Type *Ty = getUnqualifiedDesugaredType();
 
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index ed6d2036cb984..77d42e301f1f8 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -711,3 +711,8 @@ void CGHLSLRuntime::emitInitListOpaqueValues(CodeGenFunction &CGF,
     }
   }
 }
+
+// Returns true if the type is an HLSL resource class
+bool CGHLSLRuntime::isResourceRecordType(const clang::Type *Ty) {
+  return HLSLAttributedResourceType::findHandleTypeOnResource(Ty) != nullptr;
+}
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h b/clang/lib/CodeGen/CGHLSLRuntime.h
index a9da42324a038..31811cc390cde 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.h
+++ b/clang/lib/CodeGen/CGHLSLRuntime.h
@@ -134,6 +134,8 @@ class CGHLSLRuntime {
     BufferResBinding(HLSLResourceBindingAttr *Attr);
   };
 
+  static bool isResourceRecordType(const clang::Type *Ty);
+
 protected:
   CodeGenModule &CGM;
 
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 1b7d0ac89690e..e73a5f6b38218 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -5594,7 +5594,11 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D,
       if (D->getType()->isReferenceType())
         T = D->getType();
 
-      if (getLangOpts().CPlusPlus) {
+      if (getLangOpts().HLSL &&
+          getHLSLRuntime().isResourceRecordType(D->getType().getTypePtr())) {
+        Init = llvm::PoisonValue::get(getTypes().ConvertType(ASTTy));
+        NeedsGlobalCtor = true;
+      } else if (getLangOpts().CPlusPlus) {
         Init = EmitNullConstant(T);
         if (!IsDefinitionAvailableExternally)
           NeedsGlobalCtor = true;
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 283a9801fc707..0227640095af1 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -3062,6 +3062,13 @@ void SemaHLSL::ActOnVariableDeclarator(VarDecl *VD) {
     if (VD->getType()->isHLSLIntangibleType())
       collectResourceBindingsOnVarDecl(VD);
 
+    if (VD->getType()->isHLSLResourceWrapper()) {
+      // Make the variable for resources static. The global externally visible
+      // storage is accessed through the handle, which is a member. The variable
+      // itself is not externally visible.
+      VD->setStorageClass(StorageClass::SC_Static);
+    }
+
     // process explicit bindings
     processExplicitBindingsOnDecl(VD);
   }
diff --git a/clang/test/CodeGenHLSL/builtins/ByteAddressBuffers-constructors.hlsl b/clang/test/CodeGenHLSL/builtins/ByteAddressBuffers-constructors.hlsl
index 7fc6f4bb05745..926a37c689517 100644
--- a/clang/test/CodeGenHLSL/builtins/ByteAddressBuffers-constructors.hlsl
+++ b/clang/test/CodeGenHLSL/builtins/ByteAddressBuffers-constructors.hlsl
@@ -11,24 +11,24 @@ RasterizerOrderedByteAddressBuffer Buffer2: register(u3, space4);
 // CHECK: "class.hlsl::RWByteAddressBuffer" = type { target("dx.RawBuffer", i8, 1, 0) }
 // CHECK: "class.hlsl::RasterizerOrderedByteAddressBuffer" = type { target("dx.RawBuffer", i8, 1, 1) }
 
-// CHECK: @Buffer0 = global %"class.hlsl::ByteAddressBuffer" zeroinitializer, align 4
-// CHECK: @Buffer1 = global %"class.hlsl::RWByteAddressBuffer" zeroinitializer, align 4
-// CHECK: @Buffer2 = global %"class.hlsl::RasterizerOrderedByteAddressBuffer" zeroinitializer, align 4
+// CHECK: @_ZL7Buffer0 = internal global %"class.hlsl::ByteAddressBuffer" poison, align 4
+// CHECK: @_ZL7Buffer1 = internal global %"class.hlsl::RWByteAddressBuffer" poison, align 4
+// CHECK: @_ZL7Buffer2 = internal global %"class.hlsl::RasterizerOrderedByteAddressBuffer" poison, align 4
 
 // CHECK; define internal void @_init_resource_Buffer0()
-// CHECK-DXIL: %Buffer0_h = call target("dx.RawBuffer", i8, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_i8_0_0t(i32 0, i32 0, i32 1, i32 0, i1 false)
-// CHECK-DXIL: store target("dx.RawBuffer", i8, 0, 0) %Buffer0_h, ptr @Buffer0, align 4
+// CHECK-DXIL: [[H:%.*]] = call target("dx.RawBuffer", i8, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_i8_0_0t(i32 0, i32 0, i32 1, i32 0, i1 false)
+// CHECK-DXIL: store target("dx.RawBuffer", i8, 0, 0) [[H]], ptr @_ZL7Buffer0, align 4
 
 // CHECK; define internal void @_init_resource_Buffer1()
-// CHECK-DXIL: %Buffer1_h = call target("dx.RawBuffer", i8, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_i8_1_0t(i32 2, i32 1, i32 1, i32 0, i1 false)
-// CHECK-DXIL: store target("dx.RawBuffer", i8, 1, 0) %Buffer1_h, ptr @Buffer1, align 4
+// CHECK-DXIL: [[H:%.*]] = call target("dx.RawBuffer", i8, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_i8_1_0t(i32 2, i32 1, i32 1, i32 0, i1 false)
+// CHECK-DXIL: store target("dx.RawBuffer", i8, 1, 0) [[H]], ptr @_ZL7Buffer1, align 4
 
 // CHECK; define internal void @_init_resource_Buffer2()
-// CHECK-DXIL: %Buffer2_h = call target("dx.RawBuffer", i8, 1, 1) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_i8_1_1t(i32 4, i32 3, i32 1, i32 0, i1 false)
-// CHECK-DXIL: store target("dx.RawBuffer", i8, 1, 1) %Buffer2_h, ptr @Buffer2, align 4
+// CHECK-DXIL: [[H:%.*]] = call target("dx.RawBuffer", i8, 1, 1) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_i8_1_1t(i32 4, i32 3, i32 1, i32 0, i1 false)
+// CHECK-DXIL: store target("dx.RawBuffer", i8, 1, 1) [[H]], ptr @_ZL7Buffer2, align 4
 
 // CHECK: define internal void @_GLOBAL__sub_I_ByteAddressBuffers_constructors.hlsl()
 // CHECK: entry:
-// CHECK: call void @_init_resource_Buffer0()
-// CHECK: call void @_init_resource_Buffer1()
-// CHECK: call void @_init_resource_Buffer2()
+// CHECK: call void @_init_resource__ZL7Buffer0()
+// CHECK: call void @_init_resource__ZL7Buffer1()
+// CHECK: call void @_init_resource__ZL7Buffer2()
diff --git a/clang/test/CodeGenHLSL/builtins/RWBuffer-constructor-opt.hlsl b/clang/test/CodeGenHLSL/builtins/RWBuffer-constructor-opt.hlsl
index 03f22620a097d..239fb6d556999 100644
--- a/clang/test/CodeGenHLSL/builtins/RWBuffer-constructor-opt.hlsl
+++ b/clang/test/CodeGenHLSL/builtins/RWBuffer-constructor-opt.hlsl
@@ -1,8 +1,7 @@
-// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -emit-llvm -O3 -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-DXIL
-// RUN: %clang_cc1 -triple spirv-vulkan-compute -x hlsl -emit-llvm -O3 -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-SPIRV
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -emit-llvm -O3 -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple spirv-vulkan-compute -x hlsl -emit-llvm -O3 -o - %s | FileCheck %s
 
-// CHECK-SPIRV: %"class.hlsl::RWBuffer" = type { target("spirv.Image", float, 5, 2, 0, 0, 2, 0) }
-// CHECK-DXIL:  %"class.hlsl::RWBuffer" = type { target("dx.TypedBuffer", float, 1, 0, 0) }
+// All referenced to an unused resource should be removed by optimizations.
 RWBuffer<float> Buf : register(u5, space3);
 
 [shader("compute")]
@@ -10,12 +9,5 @@ RWBuffer<float> Buf : register(u5, space3);
 void main() {
 // CHECK: define void @main()
 // CHECK-NEXT: entry:
-
-// CHECK-SPIRV-NEXT: %[[HANDLE:.*]] = tail call target("spirv.Image", float, 5, 2, 0, 0, 2, 0) @llvm.spv.resource.handlefrombinding.tspirv.Image_f32_5_2_0_0_2_0t(i32 3, i32 5, i32 1, i32 0, i1 false)
-// CHECK-SPIRV-NEXT: store target("spirv.Image", float, 5, 2, 0, 0, 2, 0) %[[HANDLE:.*]], ptr @Buf, align 8
-
-// CHECK-DXIL-NEXT: %[[HANDLE:.*]] = tail call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0_0t(i32 3, i32 5, i32 1, i32 0, i1 false)
-// CHECK-DXIL-NEXT: store target("dx.TypedBuffer", float, 1, 0, 0) %[[HANDLE]], ptr @Buf, align 4
-
 // CHECK-NEXT: ret void
 }
diff --git a/clang/test/CodeGenHLSL/builtins/RWBuffer-constructor.hlsl b/clang/test/CodeGenHLSL/builtins/RWBuffer-constructor.hlsl
index d7cc3892a404b..5324176a7b9bb 100644
--- a/clang/test/CodeGenHLSL/builtins/RWBuffer-constructor.hlsl
+++ b/clang/test/CodeGenHLSL/builtins/RWBuffer-constructor.hlsl
@@ -7,14 +7,14 @@
 RWBuffer<float> Buf : register(u5, space3);
 
 // CHECK: %"class.hlsl::RWBuffer" = type { target("dx.TypedBuffer", float, 1, 0, 0) }
-// CHECK: @Buf = global %"class.hlsl::RWBuffer" zeroinitializer, align 4
+// CHECK: @_ZL3Buf = internal global %"class.hlsl::RWBuffer" poison, align 4
 
-// CHECK: define internal void @_init_resource_Buf()
-// CHECK-DXIL: %Buf_h = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0_0t(i32 3, i32 5, i32 1, i32 0, i1 false)
-// CHECK-DXIL: store target("dx.TypedBuffer", float, 1, 0, 0) %Buf_h, ptr @Buf, align 4
+// CHECK: define internal void @_init_resource__ZL3Buf()
+// CHECK-DXIL: [[H:%.*]] = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0_0t(i32 3, i32 5, i32 1, i32 0, i1 false)
+// CHECK-DXIL: store target("dx.TypedBuffer", float, 1, 0, 0) [[H]], ptr @_ZL3Buf, align 4
 
 // CHECK: define linkonce_odr void @_ZN4hlsl8RWBufferIfEC2Ev(ptr noundef nonnull align 4 dereferenceable(4) %this)
 // CHECK-NEXT: entry:
 
 // CHECK: define internal void @_GLOBAL__sub_I_RWBuffer_constructor.hlsl()
-// CHECK: call void @_init_resource_Buf()
+// CHECK: call void @_init_resource__ZL3Buf()
diff --git a/clang/test/CodeGenHLSL/builtins/StructuredBuffers-constructors.hlsl b/clang/test/CodeGenHLSL/builtins/StructuredBuffers-constructors.hlsl
index bd931181045ba..04534c5550252 100644
--- a/clang/test/CodeGenHLSL/builtins/StructuredBuffers-constructors.hlsl
+++ b/clang/test/CodeGenHLSL/builtins/StructuredBuffers-constructors.hlsl
@@ -15,31 +15,31 @@ RasterizerOrderedStructuredBuffer<float> Buf5 : register(u1, space2);
 // CHECK: %"class.hlsl::ConsumeStructuredBuffer" = type { target("dx.RawBuffer", float, 1, 0) }
 // CHECK: %"class.hlsl::RasterizerOrderedStructuredBuffer" = type { target("dx.RawBuffer", float, 1, 1) }
 
-// CHECK: @Buf = global %"class.hlsl::StructuredBuffer" zeroinitializer, align 4
-// CHECK: @Buf2 = global %"class.hlsl::RWStructuredBuffer" zeroinitializer, align 4
-// CHECK: @Buf3 = global %"class.hlsl::AppendStructuredBuffer" zeroinitializer, align 4
-// CHECK: @Buf4 = global %"class.hlsl::ConsumeStructuredBuffer" zeroinitializer, align 4
-// CHECK: @Buf5 = global %"class.hlsl::RasterizerOrderedStructuredBuffer" zeroinitializer, align 4
+// CHECK: @_ZL3Buf = internal global %"class.hlsl::StructuredBuffer" poison, align 4
+// CHECK: @_ZL4Buf2 = internal global %"class.hlsl::RWStructuredBuffer" poison, align 4
+// CHECK: @_ZL4Buf3 = internal global %"class.hlsl::AppendStructuredBuffer" poison, align 4
+// CHECK: @_ZL4Buf4 = internal global %"class.hlsl::ConsumeStructuredBuffer" poison, align 4
+// CHECK: @_ZL4Buf5 = internal global %"class.hlsl::RasterizerOrderedStructuredBuffer" poison, align 4
 
-// CHECK: define internal void @_init_resource_Buf()
-// CHECK-DXIL: %Buf_h = call target("dx.RawBuffer", float, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_0_0t(i32 0, i32 10, i32 1, i32 0, i1 false)
-// CHECK-DXIL: store target("dx.RawBuffer", float, 0, 0) %Buf_h, ptr @Buf, align 4
+// CHECK: define internal void @_init_resource__ZL3Buf()
+// CHECK-DXIL: [[H:%.*]] = call target("dx.RawBuffer", float, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_0_0t(i32 0, i32 10, i32 1, i32 0, i1 false)
+// CHECK-DXIL: store target("dx.RawBuffer", float, 0, 0) [[H]], ptr @_ZL3Buf, align 4
 
-// CHECK: define internal void @_init_resource_Buf2()
-// CHECK-DXIL: %Buf2_h = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_1_0t(i32 1, i32 5, i32 1, i32 0, i1 false)
-// CHECK-DXIL: store target("dx.RawBuffer", float, 1, 0) %Buf2_h, ptr @Buf2, align 4
+// CHECK: define internal void @_init_resource__ZL4Buf2()
+// CHECK-DXIL: [[H:%.*]] = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_1_0t(i32 1, i32 5, i32 1, i32 0, i1 false)
+// CHECK-DXIL: store target("dx.RawBuffer", float, 1, 0) [[H]], ptr @_ZL4Buf2, align 4
 
-// CHECK: define internal void @_init_resource_Buf3()
-// CHECK-DXIL: %Buf3_h = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_1_0t(i32 0, i32 3, i32 1, i32 0, i1 false)
-// CHECK-DXIL: store target("dx.RawBuffer", float, 1, 0) %Buf3_h, ptr @Buf3, align 4
+// CHECK: define internal void @_init_resource__ZL4Buf3()
+// CHECK-DXIL: [[H:%.*]] = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_1_0t(i32 0, i32 3, i32 1, i32 0, i1 false)
+// CHECK-DXIL: store target("dx.RawBuffer", float, 1, 0) [[H]], ptr @_ZL4Buf3, align 4
 
-// CHECK: define internal void @_init_resource_Buf4()
-// CHECK-DXIL: %Buf4_h = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_1_0t(i32 0, i32 4, i32 1, i32 0, i1 false)
-// CHECK-DXIL: store target("dx.RawBuffer", float, 1, 0) %Buf4_h, ptr @Buf4, align 4
+// CHECK: define internal void @_init_resource__ZL4Buf4()
+// CHECK-DXIL: [[H:%.*]] = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_1_0t(i32 0, i32 4, i32 1, i32 0, i1 false)
+// CHECK-DXIL: store target("dx.RawBuffer", float, 1, 0) [[H]], ptr @_ZL4Buf4, align 4
 
-// CHECK: define internal void @_init_resource_Buf5()
-// CHECK-DXIL: %Buf5_h = call target("dx.RawBuffer", float, 1, 1) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_1_1t(i32 2, i32 1, i32 1, i32 0, i1 false)
-// CHECK-DXIL: store target("dx.RawBuffer", float, 1, 1) %Buf5_h, ptr @Buf5, align 4
+// CHECK: define internal void @_init_resource__ZL4Buf5()
+// CHECK-DXIL: [[H:%.*]] = call target("dx.RawBuffer", float, 1, 1) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_1_1t(i32 2, i32 1, i32 1, i32 0, i1 false)
+// CHECK-DXIL: store target("dx.RawBuffer", float, 1, 1) [[H]], ptr @_ZL4Buf5, align 4
 
 // CHECK: define linkonce_odr void @_ZN4hlsl16StructuredBufferIfEC2Ev(ptr noundef nonnull align 4 dereferenceable(4) %this)
 // CHECK-NEXT: entry:
@@ -52,8 +52,8 @@ RasterizerOrderedStructuredBuffer<float> Buf5 : register(u1, space2);
 // CHECK-NEXT: entry:
 
 // CHECK: define internal void @_GLOBAL__sub_I_StructuredBuffers_constructors.hlsl()
-// CHECK: call void @_init_resource_Buf()
-// CHECK: call void @_init_resource_Buf2()
-// CHECK: call void @_init_resource_Buf3()
-// CHECK: call void @_init_resource_Buf4()
-// CHECK: call void @_init_resource_Buf5()
+// CHECK: call void @_init_resource__ZL3Buf()
+// CHECK: call void @_init_resource__ZL4Buf2()
+// CHECK: call void @_init_resource__ZL4Buf3()
+// CHECK: call void @_init_resource__ZL4Buf4()
+// CHECK: call void @_init_resource__ZL4Buf5()
diff --git a/clang/test/CodeGenHLSL/resource-bindings.hlsl b/clang/test/CodeGenHLSL/resource-bindings.hlsl
index 57e8cc29572b1..3342fb55a59a4 100644
--- a/clang/test/CodeGenHLSL/resource-bindings.hlsl
+++ b/clang/test/CodeGenHLSL/resource-bindings.hlsl
@@ -1,34 +1,34 @@
 // RUN: %clang_cc1 -triple dxil--shadermodel6.6-compute -x hlsl -finclude-default-header -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s
 
-// CHECK: define internal void @_init_resource_U0S0()
-// CHECK: %U0S0_h = call target("dx.TypedBuffer", <4 x float>, 1, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_v4f32_1_0_0t(i32 0, i32 0, i32 1, i32 0, i1 false)
-// CHECK: store target("dx.TypedBuffer", <4 x float>, 1, 0, 0) %U0S0_h, ptr @U0S0, align 4
+// CHECK: define internal void @_init_resource__ZL4U0S0()
+// CHECK: [[H:%.*]] = call target("dx.TypedBuffer", <4 x float>, 1, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_v4f32_1_0_0t(i32 0, i32 0, i32 1, i32 0, i1 false)
+// CHECK: store target("dx.TypedBuffer", <4 x float>, 1, 0, 0) [[H]], ptr @_ZL4U0S0, align 4
 RWBuffer<float4> U0S0 : register(u0);
 
-// CHECK: define internal void @_init_resource_U5S3()
-// CHECK: %U5S3_h = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0_0t(i32 3, i32 5, i32 1, i32 0, i1 false)
-// CHECK: store target("dx.TypedBuffer", float, 1, 0, 0) %U5S3_h, ptr @U5S3, align 4
+// CHECK: define internal void @_init_resource__ZL4U5S3()
+// CHECK: [[H:%.*]] = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0_0t(i32 3, i32 5, i32 1, i32 0, i1 false)
+// CHECK: store target("dx.TypedBuffer", float, 1, 0, 0) [[H]], ptr @_ZL4U5S3, align 4
 RWBuffer<float> U5S3 : register(u5, space3);
 
-// CHECK: define internal void @_init_resource_T2S2()
-// CHECK: %T2S2_h = call target("dx.RawBuffer", i32, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_i32_0_0t(i32 2, i32 2, i32 1, i32 0, i1 false)
-// CHECK: store target("dx.RawBuffer", i32, 0, 0) %T2S2_h, ptr @T2S2, align 4
+// CHECK: define internal void @_init_resource__ZL4T2S2()
+// CHECK: [[H:%.*]] = call target("dx.RawBuffer", i32, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_i32_0_0t(i32 2, i32 2, i32 1, i32 0, i1 false)
+// CHECK: store target("dx.RawBuffer", i32, 0, 0) [[H]], ptr @_ZL4T2S2, align 4
 StructuredBuffer<int> T2S2 : register(t2, space2);
 struct S {
   float4 f;
   int i;
 };
 
-// CHECK: define internal void @_init_resource_T3S0()
-// CHECK: %T3S0_h = call target("dx.RawBuffer", %struct.S, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_s_struct.Ss_0_0t(i32 0, i32 3, i32 1, i32 0, i1 false)
-// CHECK: store target("dx.RawBuffer", %struct.S, 0, 0) %T3S0_h, ptr @T3S0, align 4
+// CHECK: define internal void @_init_resource__ZL4T3S0()
+// CHECK: [[H:%.*]] = call target("dx.RawBuffer", %struct.S, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_s_struct.Ss_0_0t(i32 0, i32 3, i32 1, i32 0, i1 false)
+// CHECK: store target("dx.RawBuffer", %struct.S, 0, 0) [[H]], ptr @_ZL4T3S0, align 4
 StructuredBuffer<S> T3S0 : register(t3);
 
 // CHECK: define void @main()
-// CHECK: call void @_init_resource_U0S0()
-// CHECK: call void @_init_resource_U5S3()
-// CHECK: call void @_init_resource_T2S2()
-// CHECK: call void @_init_resource_T3S0()
+// CHECK: call void @_init_resource__ZL4U0S0()
+// CHECK: call void @_init_resource__ZL4U5S3()
+// CHECK: call void @_init_resource__ZL4T2S2()
+// CHECK: call void @_init_resource__ZL4T3S0()
 
 [numthreads(4,1,1)]
 void main() {}

@llvmbot
Copy link
Member

llvmbot commented Feb 27, 2025

@llvm/pr-subscribers-clang-codegen

Author: Steven Perron (s-perron)

Changes

The resource wrapper should have internal linkage because it contains a
handle to the global resource, and it not the actual global.

Makeing this changed exposed that we were zeroinitializing the resouce,
which is a problem. The handle cannot be zeroinitialized. This is
changed to use poison instead.

Fixes #122767.


Full diff: https://github.com/llvm/llvm-project/pull/129100.diff

11 Files Affected:

  • (modified) clang/include/clang/AST/Type.h (+1)
  • (modified) clang/lib/AST/Type.cpp (+27)
  • (modified) clang/lib/CodeGen/CGHLSLRuntime.cpp (+5)
  • (modified) clang/lib/CodeGen/CGHLSLRuntime.h (+2)
  • (modified) clang/lib/CodeGen/CodeGenModule.cpp (+5-1)
  • (modified) clang/lib/Sema/SemaHLSL.cpp (+7)
  • (modified) clang/test/CodeGenHLSL/builtins/ByteAddressBuffers-constructors.hlsl (+12-12)
  • (modified) clang/test/CodeGenHLSL/builtins/RWBuffer-constructor-opt.hlsl (+3-11)
  • (modified) clang/test/CodeGenHLSL/builtins/RWBuffer-constructor.hlsl (+5-5)
  • (modified) clang/test/CodeGenHLSL/builtins/StructuredBuffers-constructors.hlsl (+25-25)
  • (modified) clang/test/CodeGenHLSL/resource-bindings.hlsl (+16-16)
diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h
index c3ff7ebd88516..c5afae48b6a5f 100644
--- a/clang/include/clang/AST/Type.h
+++ b/clang/include/clang/AST/Type.h
@@ -2661,6 +2661,7 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
   bool isHLSLSpecificType() const; // Any HLSL specific type
   bool isHLSLBuiltinIntangibleType() const; // Any HLSL builtin intangible type
   bool isHLSLAttributedResourceType() const;
+  bool isHLSLResourceWrapper() const;
   bool isHLSLIntangibleType()
       const; // Any HLSL intangible type (builtin, array, class)
 
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 8c11ec2e1fe24..e924f49168c52 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -5098,6 +5098,33 @@ bool Type::hasSizedVLAType() const {
   return false;
 }
 
+bool Type::isHLSLResourceWrapper() const {
+  const Type *Ty = getUnqualifiedDesugaredType();
+
+  // check if it's a builtin type first
+  if (Ty->isBuiltinType())
+    return Ty->isHLSLBuiltinIntangibleType();
+
+  // unwrap arrays
+  while (isa<ConstantArrayType>(Ty))
+    Ty = Ty->getArrayElementTypeNoTypeQual();
+
+  const RecordType *RT =
+      dyn_cast<RecordType>(Ty->getUnqualifiedDesugaredType());
+  if (!RT)
+    return false;
+
+  CXXRecordDecl *RD = RT->getAsCXXRecordDecl();
+  assert(RD != nullptr &&
+      "all HLSL structs and classes should be CXXRecordDecl");
+  assert(RD->isCompleteDefinition() && "expecting complete type");
+  if (RD->field_empty()) {
+    return false;
+  }
+  const FieldDecl *FirstField = *RD->field_begin();
+  return FirstField->getType()->isHLSLAttributedResourceType();
+}
+
 bool Type::isHLSLIntangibleType() const {
   const Type *Ty = getUnqualifiedDesugaredType();
 
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index ed6d2036cb984..77d42e301f1f8 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -711,3 +711,8 @@ void CGHLSLRuntime::emitInitListOpaqueValues(CodeGenFunction &CGF,
     }
   }
 }
+
+// Returns true if the type is an HLSL resource class
+bool CGHLSLRuntime::isResourceRecordType(const clang::Type *Ty) {
+  return HLSLAttributedResourceType::findHandleTypeOnResource(Ty) != nullptr;
+}
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h b/clang/lib/CodeGen/CGHLSLRuntime.h
index a9da42324a038..31811cc390cde 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.h
+++ b/clang/lib/CodeGen/CGHLSLRuntime.h
@@ -134,6 +134,8 @@ class CGHLSLRuntime {
     BufferResBinding(HLSLResourceBindingAttr *Attr);
   };
 
+  static bool isResourceRecordType(const clang::Type *Ty);
+
 protected:
   CodeGenModule &CGM;
 
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 1b7d0ac89690e..e73a5f6b38218 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -5594,7 +5594,11 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D,
       if (D->getType()->isReferenceType())
         T = D->getType();
 
-      if (getLangOpts().CPlusPlus) {
+      if (getLangOpts().HLSL &&
+          getHLSLRuntime().isResourceRecordType(D->getType().getTypePtr())) {
+        Init = llvm::PoisonValue::get(getTypes().ConvertType(ASTTy));
+        NeedsGlobalCtor = true;
+      } else if (getLangOpts().CPlusPlus) {
         Init = EmitNullConstant(T);
         if (!IsDefinitionAvailableExternally)
           NeedsGlobalCtor = true;
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 283a9801fc707..0227640095af1 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -3062,6 +3062,13 @@ void SemaHLSL::ActOnVariableDeclarator(VarDecl *VD) {
     if (VD->getType()->isHLSLIntangibleType())
       collectResourceBindingsOnVarDecl(VD);
 
+    if (VD->getType()->isHLSLResourceWrapper()) {
+      // Make the variable for resources static. The global externally visible
+      // storage is accessed through the handle, which is a member. The variable
+      // itself is not externally visible.
+      VD->setStorageClass(StorageClass::SC_Static);
+    }
+
     // process explicit bindings
     processExplicitBindingsOnDecl(VD);
   }
diff --git a/clang/test/CodeGenHLSL/builtins/ByteAddressBuffers-constructors.hlsl b/clang/test/CodeGenHLSL/builtins/ByteAddressBuffers-constructors.hlsl
index 7fc6f4bb05745..926a37c689517 100644
--- a/clang/test/CodeGenHLSL/builtins/ByteAddressBuffers-constructors.hlsl
+++ b/clang/test/CodeGenHLSL/builtins/ByteAddressBuffers-constructors.hlsl
@@ -11,24 +11,24 @@ RasterizerOrderedByteAddressBuffer Buffer2: register(u3, space4);
 // CHECK: "class.hlsl::RWByteAddressBuffer" = type { target("dx.RawBuffer", i8, 1, 0) }
 // CHECK: "class.hlsl::RasterizerOrderedByteAddressBuffer" = type { target("dx.RawBuffer", i8, 1, 1) }
 
-// CHECK: @Buffer0 = global %"class.hlsl::ByteAddressBuffer" zeroinitializer, align 4
-// CHECK: @Buffer1 = global %"class.hlsl::RWByteAddressBuffer" zeroinitializer, align 4
-// CHECK: @Buffer2 = global %"class.hlsl::RasterizerOrderedByteAddressBuffer" zeroinitializer, align 4
+// CHECK: @_ZL7Buffer0 = internal global %"class.hlsl::ByteAddressBuffer" poison, align 4
+// CHECK: @_ZL7Buffer1 = internal global %"class.hlsl::RWByteAddressBuffer" poison, align 4
+// CHECK: @_ZL7Buffer2 = internal global %"class.hlsl::RasterizerOrderedByteAddressBuffer" poison, align 4
 
 // CHECK; define internal void @_init_resource_Buffer0()
-// CHECK-DXIL: %Buffer0_h = call target("dx.RawBuffer", i8, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_i8_0_0t(i32 0, i32 0, i32 1, i32 0, i1 false)
-// CHECK-DXIL: store target("dx.RawBuffer", i8, 0, 0) %Buffer0_h, ptr @Buffer0, align 4
+// CHECK-DXIL: [[H:%.*]] = call target("dx.RawBuffer", i8, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_i8_0_0t(i32 0, i32 0, i32 1, i32 0, i1 false)
+// CHECK-DXIL: store target("dx.RawBuffer", i8, 0, 0) [[H]], ptr @_ZL7Buffer0, align 4
 
 // CHECK; define internal void @_init_resource_Buffer1()
-// CHECK-DXIL: %Buffer1_h = call target("dx.RawBuffer", i8, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_i8_1_0t(i32 2, i32 1, i32 1, i32 0, i1 false)
-// CHECK-DXIL: store target("dx.RawBuffer", i8, 1, 0) %Buffer1_h, ptr @Buffer1, align 4
+// CHECK-DXIL: [[H:%.*]] = call target("dx.RawBuffer", i8, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_i8_1_0t(i32 2, i32 1, i32 1, i32 0, i1 false)
+// CHECK-DXIL: store target("dx.RawBuffer", i8, 1, 0) [[H]], ptr @_ZL7Buffer1, align 4
 
 // CHECK; define internal void @_init_resource_Buffer2()
-// CHECK-DXIL: %Buffer2_h = call target("dx.RawBuffer", i8, 1, 1) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_i8_1_1t(i32 4, i32 3, i32 1, i32 0, i1 false)
-// CHECK-DXIL: store target("dx.RawBuffer", i8, 1, 1) %Buffer2_h, ptr @Buffer2, align 4
+// CHECK-DXIL: [[H:%.*]] = call target("dx.RawBuffer", i8, 1, 1) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_i8_1_1t(i32 4, i32 3, i32 1, i32 0, i1 false)
+// CHECK-DXIL: store target("dx.RawBuffer", i8, 1, 1) [[H]], ptr @_ZL7Buffer2, align 4
 
 // CHECK: define internal void @_GLOBAL__sub_I_ByteAddressBuffers_constructors.hlsl()
 // CHECK: entry:
-// CHECK: call void @_init_resource_Buffer0()
-// CHECK: call void @_init_resource_Buffer1()
-// CHECK: call void @_init_resource_Buffer2()
+// CHECK: call void @_init_resource__ZL7Buffer0()
+// CHECK: call void @_init_resource__ZL7Buffer1()
+// CHECK: call void @_init_resource__ZL7Buffer2()
diff --git a/clang/test/CodeGenHLSL/builtins/RWBuffer-constructor-opt.hlsl b/clang/test/CodeGenHLSL/builtins/RWBuffer-constructor-opt.hlsl
index 03f22620a097d..239fb6d556999 100644
--- a/clang/test/CodeGenHLSL/builtins/RWBuffer-constructor-opt.hlsl
+++ b/clang/test/CodeGenHLSL/builtins/RWBuffer-constructor-opt.hlsl
@@ -1,8 +1,7 @@
-// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -emit-llvm -O3 -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-DXIL
-// RUN: %clang_cc1 -triple spirv-vulkan-compute -x hlsl -emit-llvm -O3 -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-SPIRV
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -emit-llvm -O3 -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple spirv-vulkan-compute -x hlsl -emit-llvm -O3 -o - %s | FileCheck %s
 
-// CHECK-SPIRV: %"class.hlsl::RWBuffer" = type { target("spirv.Image", float, 5, 2, 0, 0, 2, 0) }
-// CHECK-DXIL:  %"class.hlsl::RWBuffer" = type { target("dx.TypedBuffer", float, 1, 0, 0) }
+// All referenced to an unused resource should be removed by optimizations.
 RWBuffer<float> Buf : register(u5, space3);
 
 [shader("compute")]
@@ -10,12 +9,5 @@ RWBuffer<float> Buf : register(u5, space3);
 void main() {
 // CHECK: define void @main()
 // CHECK-NEXT: entry:
-
-// CHECK-SPIRV-NEXT: %[[HANDLE:.*]] = tail call target("spirv.Image", float, 5, 2, 0, 0, 2, 0) @llvm.spv.resource.handlefrombinding.tspirv.Image_f32_5_2_0_0_2_0t(i32 3, i32 5, i32 1, i32 0, i1 false)
-// CHECK-SPIRV-NEXT: store target("spirv.Image", float, 5, 2, 0, 0, 2, 0) %[[HANDLE:.*]], ptr @Buf, align 8
-
-// CHECK-DXIL-NEXT: %[[HANDLE:.*]] = tail call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0_0t(i32 3, i32 5, i32 1, i32 0, i1 false)
-// CHECK-DXIL-NEXT: store target("dx.TypedBuffer", float, 1, 0, 0) %[[HANDLE]], ptr @Buf, align 4
-
 // CHECK-NEXT: ret void
 }
diff --git a/clang/test/CodeGenHLSL/builtins/RWBuffer-constructor.hlsl b/clang/test/CodeGenHLSL/builtins/RWBuffer-constructor.hlsl
index d7cc3892a404b..5324176a7b9bb 100644
--- a/clang/test/CodeGenHLSL/builtins/RWBuffer-constructor.hlsl
+++ b/clang/test/CodeGenHLSL/builtins/RWBuffer-constructor.hlsl
@@ -7,14 +7,14 @@
 RWBuffer<float> Buf : register(u5, space3);
 
 // CHECK: %"class.hlsl::RWBuffer" = type { target("dx.TypedBuffer", float, 1, 0, 0) }
-// CHECK: @Buf = global %"class.hlsl::RWBuffer" zeroinitializer, align 4
+// CHECK: @_ZL3Buf = internal global %"class.hlsl::RWBuffer" poison, align 4
 
-// CHECK: define internal void @_init_resource_Buf()
-// CHECK-DXIL: %Buf_h = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0_0t(i32 3, i32 5, i32 1, i32 0, i1 false)
-// CHECK-DXIL: store target("dx.TypedBuffer", float, 1, 0, 0) %Buf_h, ptr @Buf, align 4
+// CHECK: define internal void @_init_resource__ZL3Buf()
+// CHECK-DXIL: [[H:%.*]] = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0_0t(i32 3, i32 5, i32 1, i32 0, i1 false)
+// CHECK-DXIL: store target("dx.TypedBuffer", float, 1, 0, 0) [[H]], ptr @_ZL3Buf, align 4
 
 // CHECK: define linkonce_odr void @_ZN4hlsl8RWBufferIfEC2Ev(ptr noundef nonnull align 4 dereferenceable(4) %this)
 // CHECK-NEXT: entry:
 
 // CHECK: define internal void @_GLOBAL__sub_I_RWBuffer_constructor.hlsl()
-// CHECK: call void @_init_resource_Buf()
+// CHECK: call void @_init_resource__ZL3Buf()
diff --git a/clang/test/CodeGenHLSL/builtins/StructuredBuffers-constructors.hlsl b/clang/test/CodeGenHLSL/builtins/StructuredBuffers-constructors.hlsl
index bd931181045ba..04534c5550252 100644
--- a/clang/test/CodeGenHLSL/builtins/StructuredBuffers-constructors.hlsl
+++ b/clang/test/CodeGenHLSL/builtins/StructuredBuffers-constructors.hlsl
@@ -15,31 +15,31 @@ RasterizerOrderedStructuredBuffer<float> Buf5 : register(u1, space2);
 // CHECK: %"class.hlsl::ConsumeStructuredBuffer" = type { target("dx.RawBuffer", float, 1, 0) }
 // CHECK: %"class.hlsl::RasterizerOrderedStructuredBuffer" = type { target("dx.RawBuffer", float, 1, 1) }
 
-// CHECK: @Buf = global %"class.hlsl::StructuredBuffer" zeroinitializer, align 4
-// CHECK: @Buf2 = global %"class.hlsl::RWStructuredBuffer" zeroinitializer, align 4
-// CHECK: @Buf3 = global %"class.hlsl::AppendStructuredBuffer" zeroinitializer, align 4
-// CHECK: @Buf4 = global %"class.hlsl::ConsumeStructuredBuffer" zeroinitializer, align 4
-// CHECK: @Buf5 = global %"class.hlsl::RasterizerOrderedStructuredBuffer" zeroinitializer, align 4
+// CHECK: @_ZL3Buf = internal global %"class.hlsl::StructuredBuffer" poison, align 4
+// CHECK: @_ZL4Buf2 = internal global %"class.hlsl::RWStructuredBuffer" poison, align 4
+// CHECK: @_ZL4Buf3 = internal global %"class.hlsl::AppendStructuredBuffer" poison, align 4
+// CHECK: @_ZL4Buf4 = internal global %"class.hlsl::ConsumeStructuredBuffer" poison, align 4
+// CHECK: @_ZL4Buf5 = internal global %"class.hlsl::RasterizerOrderedStructuredBuffer" poison, align 4
 
-// CHECK: define internal void @_init_resource_Buf()
-// CHECK-DXIL: %Buf_h = call target("dx.RawBuffer", float, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_0_0t(i32 0, i32 10, i32 1, i32 0, i1 false)
-// CHECK-DXIL: store target("dx.RawBuffer", float, 0, 0) %Buf_h, ptr @Buf, align 4
+// CHECK: define internal void @_init_resource__ZL3Buf()
+// CHECK-DXIL: [[H:%.*]] = call target("dx.RawBuffer", float, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_0_0t(i32 0, i32 10, i32 1, i32 0, i1 false)
+// CHECK-DXIL: store target("dx.RawBuffer", float, 0, 0) [[H]], ptr @_ZL3Buf, align 4
 
-// CHECK: define internal void @_init_resource_Buf2()
-// CHECK-DXIL: %Buf2_h = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_1_0t(i32 1, i32 5, i32 1, i32 0, i1 false)
-// CHECK-DXIL: store target("dx.RawBuffer", float, 1, 0) %Buf2_h, ptr @Buf2, align 4
+// CHECK: define internal void @_init_resource__ZL4Buf2()
+// CHECK-DXIL: [[H:%.*]] = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_1_0t(i32 1, i32 5, i32 1, i32 0, i1 false)
+// CHECK-DXIL: store target("dx.RawBuffer", float, 1, 0) [[H]], ptr @_ZL4Buf2, align 4
 
-// CHECK: define internal void @_init_resource_Buf3()
-// CHECK-DXIL: %Buf3_h = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_1_0t(i32 0, i32 3, i32 1, i32 0, i1 false)
-// CHECK-DXIL: store target("dx.RawBuffer", float, 1, 0) %Buf3_h, ptr @Buf3, align 4
+// CHECK: define internal void @_init_resource__ZL4Buf3()
+// CHECK-DXIL: [[H:%.*]] = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_1_0t(i32 0, i32 3, i32 1, i32 0, i1 false)
+// CHECK-DXIL: store target("dx.RawBuffer", float, 1, 0) [[H]], ptr @_ZL4Buf3, align 4
 
-// CHECK: define internal void @_init_resource_Buf4()
-// CHECK-DXIL: %Buf4_h = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_1_0t(i32 0, i32 4, i32 1, i32 0, i1 false)
-// CHECK-DXIL: store target("dx.RawBuffer", float, 1, 0) %Buf4_h, ptr @Buf4, align 4
+// CHECK: define internal void @_init_resource__ZL4Buf4()
+// CHECK-DXIL: [[H:%.*]] = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_1_0t(i32 0, i32 4, i32 1, i32 0, i1 false)
+// CHECK-DXIL: store target("dx.RawBuffer", float, 1, 0) [[H]], ptr @_ZL4Buf4, align 4
 
-// CHECK: define internal void @_init_resource_Buf5()
-// CHECK-DXIL: %Buf5_h = call target("dx.RawBuffer", float, 1, 1) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_1_1t(i32 2, i32 1, i32 1, i32 0, i1 false)
-// CHECK-DXIL: store target("dx.RawBuffer", float, 1, 1) %Buf5_h, ptr @Buf5, align 4
+// CHECK: define internal void @_init_resource__ZL4Buf5()
+// CHECK-DXIL: [[H:%.*]] = call target("dx.RawBuffer", float, 1, 1) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_1_1t(i32 2, i32 1, i32 1, i32 0, i1 false)
+// CHECK-DXIL: store target("dx.RawBuffer", float, 1, 1) [[H]], ptr @_ZL4Buf5, align 4
 
 // CHECK: define linkonce_odr void @_ZN4hlsl16StructuredBufferIfEC2Ev(ptr noundef nonnull align 4 dereferenceable(4) %this)
 // CHECK-NEXT: entry:
@@ -52,8 +52,8 @@ RasterizerOrderedStructuredBuffer<float> Buf5 : register(u1, space2);
 // CHECK-NEXT: entry:
 
 // CHECK: define internal void @_GLOBAL__sub_I_StructuredBuffers_constructors.hlsl()
-// CHECK: call void @_init_resource_Buf()
-// CHECK: call void @_init_resource_Buf2()
-// CHECK: call void @_init_resource_Buf3()
-// CHECK: call void @_init_resource_Buf4()
-// CHECK: call void @_init_resource_Buf5()
+// CHECK: call void @_init_resource__ZL3Buf()
+// CHECK: call void @_init_resource__ZL4Buf2()
+// CHECK: call void @_init_resource__ZL4Buf3()
+// CHECK: call void @_init_resource__ZL4Buf4()
+// CHECK: call void @_init_resource__ZL4Buf5()
diff --git a/clang/test/CodeGenHLSL/resource-bindings.hlsl b/clang/test/CodeGenHLSL/resource-bindings.hlsl
index 57e8cc29572b1..3342fb55a59a4 100644
--- a/clang/test/CodeGenHLSL/resource-bindings.hlsl
+++ b/clang/test/CodeGenHLSL/resource-bindings.hlsl
@@ -1,34 +1,34 @@
 // RUN: %clang_cc1 -triple dxil--shadermodel6.6-compute -x hlsl -finclude-default-header -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s
 
-// CHECK: define internal void @_init_resource_U0S0()
-// CHECK: %U0S0_h = call target("dx.TypedBuffer", <4 x float>, 1, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_v4f32_1_0_0t(i32 0, i32 0, i32 1, i32 0, i1 false)
-// CHECK: store target("dx.TypedBuffer", <4 x float>, 1, 0, 0) %U0S0_h, ptr @U0S0, align 4
+// CHECK: define internal void @_init_resource__ZL4U0S0()
+// CHECK: [[H:%.*]] = call target("dx.TypedBuffer", <4 x float>, 1, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_v4f32_1_0_0t(i32 0, i32 0, i32 1, i32 0, i1 false)
+// CHECK: store target("dx.TypedBuffer", <4 x float>, 1, 0, 0) [[H]], ptr @_ZL4U0S0, align 4
 RWBuffer<float4> U0S0 : register(u0);
 
-// CHECK: define internal void @_init_resource_U5S3()
-// CHECK: %U5S3_h = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0_0t(i32 3, i32 5, i32 1, i32 0, i1 false)
-// CHECK: store target("dx.TypedBuffer", float, 1, 0, 0) %U5S3_h, ptr @U5S3, align 4
+// CHECK: define internal void @_init_resource__ZL4U5S3()
+// CHECK: [[H:%.*]] = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0_0t(i32 3, i32 5, i32 1, i32 0, i1 false)
+// CHECK: store target("dx.TypedBuffer", float, 1, 0, 0) [[H]], ptr @_ZL4U5S3, align 4
 RWBuffer<float> U5S3 : register(u5, space3);
 
-// CHECK: define internal void @_init_resource_T2S2()
-// CHECK: %T2S2_h = call target("dx.RawBuffer", i32, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_i32_0_0t(i32 2, i32 2, i32 1, i32 0, i1 false)
-// CHECK: store target("dx.RawBuffer", i32, 0, 0) %T2S2_h, ptr @T2S2, align 4
+// CHECK: define internal void @_init_resource__ZL4T2S2()
+// CHECK: [[H:%.*]] = call target("dx.RawBuffer", i32, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_i32_0_0t(i32 2, i32 2, i32 1, i32 0, i1 false)
+// CHECK: store target("dx.RawBuffer", i32, 0, 0) [[H]], ptr @_ZL4T2S2, align 4
 StructuredBuffer<int> T2S2 : register(t2, space2);
 struct S {
   float4 f;
   int i;
 };
 
-// CHECK: define internal void @_init_resource_T3S0()
-// CHECK: %T3S0_h = call target("dx.RawBuffer", %struct.S, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_s_struct.Ss_0_0t(i32 0, i32 3, i32 1, i32 0, i1 false)
-// CHECK: store target("dx.RawBuffer", %struct.S, 0, 0) %T3S0_h, ptr @T3S0, align 4
+// CHECK: define internal void @_init_resource__ZL4T3S0()
+// CHECK: [[H:%.*]] = call target("dx.RawBuffer", %struct.S, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_s_struct.Ss_0_0t(i32 0, i32 3, i32 1, i32 0, i1 false)
+// CHECK: store target("dx.RawBuffer", %struct.S, 0, 0) [[H]], ptr @_ZL4T3S0, align 4
 StructuredBuffer<S> T3S0 : register(t3);
 
 // CHECK: define void @main()
-// CHECK: call void @_init_resource_U0S0()
-// CHECK: call void @_init_resource_U5S3()
-// CHECK: call void @_init_resource_T2S2()
-// CHECK: call void @_init_resource_T3S0()
+// CHECK: call void @_init_resource__ZL4U0S0()
+// CHECK: call void @_init_resource__ZL4U5S3()
+// CHECK: call void @_init_resource__ZL4T2S2()
+// CHECK: call void @_init_resource__ZL4T3S0()
 
 [numthreads(4,1,1)]
 void main() {}

@s-perron
Copy link
Contributor Author

I created a new PR to make the resource wrappers static. @hekota Was right. Not much needed to change in codegen. We were already initializing all global (internal or external) that have the resource binding attribute. I must have been testing the wrong examples when I said codegen would have to change significantly.

Copy link

github-actions bot commented Feb 27, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

Copy link
Member

@hekota hekota left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Few suggestions.

@s-perron s-perron requested a review from hekota March 4, 2025 15:48
Copy link
Member

@hekota hekota left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Few more comments.

@s-perron s-perron requested a review from hekota March 5, 2025 14:53
@s-perron s-perron moved this to Active in HLSL Support Mar 5, 2025
@s-perron s-perron moved this from Active to Needs Review in HLSL Support Mar 5, 2025
Copy link
Member

@hekota hekota left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Thanks :) One nit - typo in the title.

s-perron and others added 6 commits March 5, 2025 12:40
The resource wrapper should have internal linkage because it contains a
handle to the global resource, and it not the actual global.

Makeing this changed exposed that we were zeroinitializing the resouce,
which is a problem. The handle cannot be zeroinitialized. This is
changed to use poison instead.

Fixes llvm#122767.
Co-authored-by: Helena Kotas <hekotas@microsoft.com>
@s-perron s-perron changed the title [HLSL] Fix resrouce wrapper declaration [HLSL] Fix resource wrapper declaration Mar 5, 2025
@s-perron s-perron merged commit 6d4f8b1 into llvm:main Mar 5, 2025
12 checks passed
@s-perron s-perron deleted the static_wrapper branch March 6, 2025 15:37
jph-13 pushed a commit to jph-13/llvm-project that referenced this pull request Mar 21, 2025
The resource wrapper should have internal linkage because it contains a
handle to the global resource, and it not the actual global.

Makeing this changed exposed that we were zeroinitializing the resouce,
which is a problem. The handle cannot be zeroinitialized. This is
changed to use poison instead.

Fixes llvm#122767.

---------

Co-authored-by: Helena Kotas <hekotas@microsoft.com>
@damyanp damyanp moved this from Needs Review to Closed in HLSL Support Apr 22, 2025
@damyanp damyanp removed this from HLSL Support Jun 25, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:codegen IR generation bugs: mangling, exceptions, etc. clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category HLSL HLSL Language Support
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[HLSL] RWBuffer resource variable has external linkage
3 participants