[clang] Test locally scoped resources (PR #190037)
Joshua Batista via cfe-commits
cfe-commits at lists.llvm.org
Wed Apr 1 12:59:03 PDT 2026
https://github.com/bob80905 created https://github.com/llvm/llvm-project/pull/190037
draft
>From ea9445b3fa97f5fa0132985f39a41609ed51e6f5 Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Wed, 25 Mar 2026 16:40:16 -0700
Subject: [PATCH 1/4] first attempt
---
.../Resources/local_resources_failures.hlsl | 175 +++++
.../Resources/local_resources_passes.hlsl | 643 ++++++++++++++++++
2 files changed, 818 insertions(+)
create mode 100644 clang/test/SemaHLSL/Resources/local_resources_failures.hlsl
create mode 100644 clang/test/SemaHLSL/Resources/local_resources_passes.hlsl
diff --git a/clang/test/SemaHLSL/Resources/local_resources_failures.hlsl b/clang/test/SemaHLSL/Resources/local_resources_failures.hlsl
new file mode 100644
index 0000000000000..84637cf8ce99f
--- /dev/null
+++ b/clang/test/SemaHLSL/Resources/local_resources_failures.hlsl
@@ -0,0 +1,175 @@
+// RUN: %clang_cc1 -std=hlsl2021 -finclude-default-header -triple \
+// RUN: dxil-pc-shadermodel6.3-compute %s -emit-llvm -verify
+
+Texture2D<float4> gTex0 : register(t0);
+Texture2D<float4> gTex1 : register(t1);
+SamplerState gSampler : register(s0);
+
+StructuredBuffer<float4> gSB : register(t2);
+RWStructuredBuffer<float4> gRW : register(u0);
+
+Texture2D<float4> gTexArray[4] : register(t10);
+
+//--------------------------------------------------------------
+// FAIL 1: static local resource
+//--------------------------------------------------------------
+// This causes an assert in DXC
+float4 Fail_Static(float2 uv)
+{
+ static Texture2D<float4> tex = gTex0;
+ // expected-error at -1 {{static resource}}
+ return tex.Sample(gSampler, uv);
+}
+
+//--------------------------------------------------------------
+// FAIL 2: arithmetic on resource
+//--------------------------------------------------------------
+
+float Fail_Arithmetic()
+{
+ Texture2D<float4> tex = gTex0;
+ tex = tex + 1;
+ // expected-error at -1 {{scalar, vector, or matrix expected}}
+ return tex;
+ // expected-error at -1 {{cannot initialize return object of type 'float' with an lvalue of type 'Texture2D<float4>'}}
+}
+
+//--------------------------------------------------------------
+// FAIL 3: comparison of resources
+//--------------------------------------------------------------
+
+bool Fail_Compare()
+{
+ Texture2D<float4> a = gTex0;
+ Texture2D<float4> b = gTex1;
+ return a == b;
+ // expected-error at -1 {{operator cannot be used with built-in type 'Texture2D<vector<float, 4> >'}}
+}
+
+//--------------------------------------------------------------
+// FAIL 4: conversion to bool
+//--------------------------------------------------------------
+
+bool Fail_Bool()
+{
+ Texture2D<float4> tex = gTex0;
+ return tex;
+ // expected-error at -1 {{cannot initialize return object of type 'bool' with an lvalue of type 'Texture2D<float4>'}}
+}
+
+//--------------------------------------------------------------
+// FAIL 5: cast from resource
+//--------------------------------------------------------------
+
+uint Fail_Cast()
+{
+ Texture2D<float4> tex = gTex0;
+ return (uint)tex;
+ // expected-error at -1 {{cannot convert from 'Texture2D<float4>' to 'uint'}}
+}
+
+//--------------------------------------------------------------
+// FAIL 6: addition of resources
+//--------------------------------------------------------------
+
+float Fail_Add()
+{
+ Texture2D<float4> tex = gTex0;
+ return tex + tex;
+ // expected-error at -1 {{scalar, vector, or matrix expected}}
+}
+
+
+//--------------------------------------------------------------
+// FAIL 7: default parameter on resource
+//--------------------------------------------------------------
+
+float4 Fail_DefaultParam(Texture2D<float4> tex = gTex0, float2 uv)
+ // expected-error at -1 {{missing default argument on parameter 'uv'}}
+ // expected-note at -2 {{candidate function not viable: requires 2 arguments, but 1 was provided}}
+ // note, this note above does not get emitted when -verify is passed as an option.
+{
+ return tex.Sample(gSampler, uv);
+}
+
+//--------------------------------------------------------------
+// FAIL 8: reinterpret cast
+//--------------------------------------------------------------
+
+float4 Fail_Reinterpret(float2 uv)
+{
+ Texture2D<float4> tex = gTex0;
+ return ((Texture2D<float4>)gSampler).Sample(gSampler, uv);
+ // expected-error at -1 {{cannot convert from 'SamplerState' to 'Texture2D<float4>'}}
+}
+
+//--------------------------------------------------------------
+// FAIL 9: RWStructuredBuffer with resource type
+//--------------------------------------------------------------
+
+void Fail_LocalBuffer()
+{
+ RWStructuredBuffer<Texture2D<float4> > badBuffer;
+ // expected-error at -1 {{object 'Texture2D<float4>' is not allowed in builtin template parameters}}
+}
+
+//--------------------------------------------------------------
+// FAIL 10: wave uniformity violation
+//--------------------------------------------------------------
+
+float4 Fail_WaveUniform(float2 uv)
+{
+ Texture2D<float4> tex = gTex0;
+ // I presume the reason we don't see this expected error is that
+ // DxilCondenseResources is responsible for emitting this.
+ // So, it will not run when -verify is passed to the compiler since
+ // DXIL IR is not the intended output in that situation.
+ // However, in DXC, we do expect an error here.
+ if(WaveActiveAllTrue(true))
+ // expected-error at -1 {{local resource not guaranteed to map to unique global resource}}
+ tex = gTex1;
+ return tex.Sample(gSampler, uv);
+}
+
+//--------------------------------------------------------------
+// Entry point calling all fail functions to prevent DCE
+//--------------------------------------------------------------
+
+[numthreads(1,1,1)]
+void main(uint3 tid : SV_DispatchThreadID)
+{
+ float2 uv = float2(0,0);
+ float4 r = 0;
+ gTex0 = tex;
+ // FAIL 1
+ r += Fail_Static(uv);
+
+ // FAIL 2
+ Fail_Arithmetic();
+
+ // FAIL 3
+ Fail_Compare();
+
+ // FAIL 4
+ Fail_Bool();
+
+ // FAIL 5
+ Fail_Cast();
+
+ // FAIL 6
+ Fail_Add();
+
+ // FAIL 7
+ // note, this error does not get emitted when -verify is passed as an option.
+ // expected-error at +1{{no matching function for call to 'Fail_DefaultParam'}}
+ Fail_DefaultParam(gTex0, uv);
+
+ // FAIL 8
+ Fail_Reinterpret(uv);
+
+ // FAIL 9
+ Fail_LocalBuffer();
+
+ // FAIL 10
+ r += Fail_WaveUniform(uv);
+}
diff --git a/clang/test/SemaHLSL/Resources/local_resources_passes.hlsl b/clang/test/SemaHLSL/Resources/local_resources_passes.hlsl
new file mode 100644
index 0000000000000..cc5fc2835033e
--- /dev/null
+++ b/clang/test/SemaHLSL/Resources/local_resources_passes.hlsl
@@ -0,0 +1,643 @@
+// RUN: %clang_cc1 -std=hlsl2021 -finclude-default-header -triple \
+// RUN: dxil-pc-shadermodel6.3-compute %s -emit-llvm -verify
+
+//==============================================================
+// COMPREHENSIVE LOCAL RESOURCE VARIABLE TEST SUITE
+//
+// Structure
+// PASS TESTS
+// ENTRYPOINT
+//
+// Goal
+// Exhaustively document legal and illegal uses of HLSL
+// resource variables declared in local scope.
+//
+//==============================================================
+
+//--------------------------------------------------------------
+// Global resources
+//--------------------------------------------------------------
+
+Texture2D<float4> gTex0 : register(t0);
+Texture2D<float4> gTex1 : register(t1);
+Texture2D<float4> gTex2 : register(t2);
+
+SamplerState gSampler : register(s0);
+
+RWTexture2D<float4> gOut : register(u0);
+
+StructuredBuffer<float4> gSB : register(t3);
+RWStructuredBuffer<float4> gRW : register(u1);
+
+Texture2D<float4> gTexArray[4] : register(t10);
+
+
+//==============================================================
+// PASS TESTS
+//==============================================================
+
+
+//--------------------------------------------------------------
+// PASS 0
+//--------------------------------------------------------------
+
+groupshared Texture2D<float4> sharedTex;
+
+float4 Use_SharedTex(float2 uv)
+{
+ return gTex0.Sample(gSampler, uv);
+}
+
+//--------------------------------------------------------------
+// PASS 1
+//--------------------------------------------------------------
+
+float4 Fail_TernaryInit(bool cond, float2 uv)
+{
+ Texture2D<float4> tex = cond ? gTex0 : gTex1;
+
+ return tex.Sample(gSampler, uv);
+}
+//--------------------------------------------------------------
+// PASS 2
+//--------------------------------------------------------------
+
+void Fail_LoopVar()
+{
+ for(Texture2D<float4> tex = gTex0; false;)
+ {
+ }
+}
+
+//--------------------------------------------------------------
+// PASS 3
+//--------------------------------------------------------------
+
+float4 Fail_ExpressionInit(float2 uv)
+{
+ Texture2D<float4> tex = (true ? gTex0 : gTex1);
+ return tex.Sample(gSampler, uv);
+}
+//--------------------------------------------------------------
+// PASS 4
+//--------------------------------------------------------------
+
+struct FailSharedStruct
+{
+ Texture2D<float4> tex;
+};
+
+groupshared FailSharedStruct sharedStruct;
+
+float4 Use_FailSharedStruct(float2 uv)
+{
+ return gTex0.Sample(gSampler, uv);
+}
+
+//--------------------------------------------------------------
+// PASS 5
+//--------------------------------------------------------------
+
+struct FailStruct
+{
+ Texture2D<float4> tex;
+};
+
+float4 Fail_StructArray(float2 uv)
+{
+ FailStruct s[2];
+
+ s[0].tex = gTex0;
+ return s[0].tex.Sample(gSampler, uv);
+}
+
+//--------------------------------------------------------------
+// PASS 6
+//--------------------------------------------------------------
+groupshared Texture2D<float4> Fail_Shared;
+
+float4 Use_Fail_Shared(float2 uv)
+{
+ return gTex0.Sample(gSampler, uv);
+}
+
+//--------------------------------------------------------------
+// PASS 7
+//--------------------------------------------------------------
+Texture2D<float4> Fail_ReturnLocal_Uninitialized()
+{
+ Texture2D<float4> tex; // uninitialized local resource
+ return tex;
+}
+
+//--------------------------------------------------------------
+// PASS 8
+//--------------------------------------------------------------
+Texture2D<float4> Fail_ReturnLocal()
+{
+ Texture2D<float4> tex = gTex0;
+ return tex;
+}
+
+
+//--------------------------------------------------------------
+// PASS 9
+//--------------------------------------------------------------
+struct S { Texture2D<float4> arr[2]; };
+float4 Pass_StructArray(float2 uv)
+{
+ S s;
+ s.arr[0] = gTex0;
+ return s.arr[0].Sample(gSampler, uv);
+}
+
+//--------------------------------------------------------------
+// PASS 10
+//--------------------------------------------------------------
+
+float4 Pass_LocalArray(float2 uv)
+{
+ Texture2D<float4> arr[2];
+
+ arr[0] = gTex0;
+ return arr[0].Sample(gSampler, uv);
+}
+
+//--------------------------------------------------------------
+// PASS 11
+// Uninitialized use
+//--------------------------------------------------------------
+
+float4 Pass_Uninitialized(float2 uv)
+{
+ Texture2D<float4> tex;
+
+
+ return tex.Sample(gSampler, uv);
+}
+
+//--------------------------------------------------------------
+// PASS 12
+// Simple local alias
+//--------------------------------------------------------------
+
+float4 Pass_Alias(float2 uv)
+{
+ Texture2D<float4> tex = gTex0;
+ return tex.Sample(gSampler, uv);
+}
+
+
+//--------------------------------------------------------------
+// PASS 13
+// Reassignment
+//--------------------------------------------------------------
+
+float4 Pass_Reassign(float2 uv)
+{
+ Texture2D<float4> tex = gTex0;
+ tex = gTex1;
+ return tex.Sample(gSampler, uv);
+}
+
+
+//--------------------------------------------------------------
+// PASS 14
+// Control flow aliasing
+//--------------------------------------------------------------
+
+float4 Pass_IfAlias(bool cond, float2 uv)
+{
+ Texture2D<float4> tex;
+
+ if (cond)
+ tex = gTex0;
+ else
+ tex = gTex1;
+
+ return tex.Sample(gSampler, uv);
+}
+
+
+//--------------------------------------------------------------
+// PASS 15
+// Loop aliasing
+//--------------------------------------------------------------
+
+float4 Pass_Loop(float2 uv)
+{
+ float4 sum = 0;
+
+ for(int i=0;i<4;i++)
+ {
+ Texture2D<float4> tex = gTexArray[i];
+ sum += tex.Sample(gSampler, uv);
+ }
+
+ return sum;
+}
+
+
+
+//--------------------------------------------------------------
+// PASS 16
+// Struct containing resource
+//--------------------------------------------------------------
+
+struct PassStruct
+{
+ Texture2D<float4> tex;
+ SamplerState samp;
+};
+
+float4 Pass_Struct(float2 uv)
+{
+ PassStruct s;
+ s.tex = gTex0;
+ s.samp = gSampler;
+
+ return s.tex.Sample(s.samp, uv);
+}
+
+
+
+//--------------------------------------------------------------
+// PASS 17
+// Passing resource through multiple functions
+//--------------------------------------------------------------
+
+float4 Pass_Level2(Texture2D<float4> tex, float2 uv)
+{
+ return tex.Sample(gSampler, uv);
+}
+
+float4 Pass_Level1(Texture2D<float4> tex, float2 uv)
+{
+ return Pass_Level2(tex, uv);
+}
+
+float4 Pass_FunctionForward(float2 uv)
+{
+ Texture2D<float4> tex = gTex1;
+ return Pass_Level1(tex, uv);
+}
+
+
+
+//--------------------------------------------------------------
+// PASS 18
+// Resource merge via conditional assignments
+// (SSA-style PHI equivalent)
+//--------------------------------------------------------------
+
+float4 Pass_PhiMerge(bool cond, float2 uv)
+{
+ Texture2D<float4> tex;
+
+ if(cond)
+ tex = gTex0;
+ else
+ tex = gTex2;
+
+ return tex.Sample(gSampler, uv);
+}
+
+
+
+//--------------------------------------------------------------
+// PASS 19
+// Nested scope shadowing
+//--------------------------------------------------------------
+
+float4 Pass_Shadow(float2 uv)
+{
+ Texture2D<float4> tex = gTex0;
+
+ {
+ Texture2D<float4> tex = gTex1;
+ return tex.Sample(gSampler, uv);
+ }
+}
+
+
+
+//--------------------------------------------------------------
+// PASS 20
+// Resource in switch
+//--------------------------------------------------------------
+
+float4 Pass_Switch(int v, float2 uv)
+{
+ Texture2D<float4> tex = gTex0;
+
+ switch(v)
+ {
+ case 1: tex = gTex1; break;
+ case 2: tex = gTex2; break;
+ }
+
+ return tex.Sample(gSampler, uv);
+}
+
+
+
+//--------------------------------------------------------------
+// PASS 21
+// Bindless descriptor indexing
+//--------------------------------------------------------------
+
+float4 Pass_Bindless(uint idx, float2 uv)
+{
+ Texture2D<float4> tex = gTexArray[idx];
+ return tex.Sample(gSampler, uv);
+}
+
+
+
+//--------------------------------------------------------------
+// PASS 22
+// Resource with wave operations
+//--------------------------------------------------------------
+
+float4 Pass_WaveUse(float2 uv)
+{
+ Texture2D<float4> tex = gTex0;
+
+ float4 v = tex.Sample(gSampler, uv);
+
+ uint active = WaveActiveCountBits(true);
+
+ return v * active;
+}
+
+
+
+//--------------------------------------------------------------
+// PASS 23
+// Resource alias used inside nested loops
+//--------------------------------------------------------------
+
+float4 Pass_NestedLoops(float2 uv)
+{
+ float4 sum = 0;
+
+ for(int i=0;i<2;i++)
+ for(int j=0;j<2;j++)
+ {
+ Texture2D<float4> tex = gTexArray[i+j];
+ sum += tex.Sample(gSampler, uv);
+ }
+
+ return sum;
+}
+
+
+
+//--------------------------------------------------------------
+// PASS 24
+// Resource lifetime across blocks
+//--------------------------------------------------------------
+
+float4 Pass_BlockLifetime(float2 uv)
+{
+ Texture2D<float4> tex;
+
+ {
+ tex = gTex1;
+ }
+
+ return tex.Sample(gSampler, uv);
+}
+
+
+//--------------------------------------------------------------
+// PASS 25
+// Deep nested PHI merges
+//--------------------------------------------------------------
+
+float4 Pass_DeepPhi(bool a, bool b, float2 uv)
+{
+ Texture2D<float4> tex;
+
+ if(a)
+ {
+ if(b)
+ tex = gTex0;
+ else
+ tex = gTex1;
+ }
+ else
+ {
+ tex = gTex2;
+ }
+
+ return tex.Sample(gSampler, uv);
+}
+
+
+
+//--------------------------------------------------------------
+// PASS 26
+// Loop-carried resource value
+//--------------------------------------------------------------
+
+float4 Pass_LoopCarried(int iterations, float2 uv)
+{
+ Texture2D<float4> tex = gTex0;
+
+ for(int i=0;i<iterations;i++)
+ {
+ tex = gTexArray[i & 3];
+ }
+
+ return tex.Sample(gSampler, uv);
+}
+
+
+
+//--------------------------------------------------------------
+// PASS 27
+// Resource alias chain
+//--------------------------------------------------------------
+
+float4 Pass_AliasChain(float2 uv)
+{
+ Texture2D<float4> a = gTex0;
+ Texture2D<float4> b = a;
+ Texture2D<float4> c = b;
+
+ return c.Sample(gSampler, uv);
+}
+
+
+
+//--------------------------------------------------------------
+// PASS 28
+// Resource inside nested structs
+//--------------------------------------------------------------
+
+struct PassNestedInner
+{
+ Texture2D<float4> tex;
+};
+
+struct PassNestedOuter
+{
+ PassNestedInner inner;
+};
+
+float4 Pass_NestedStruct(float2 uv)
+{
+ PassNestedOuter s;
+
+ s.inner.tex = gTex1;
+
+ return s.inner.tex.Sample(gSampler, uv);
+}
+
+
+
+//--------------------------------------------------------------
+// PASS 29
+// Resource forwarded through multiple struct layers
+//--------------------------------------------------------------
+
+struct PassForwardA { Texture2D<float4> tex; };
+struct PassForwardB { PassForwardA a; };
+
+float4 Pass_ForwardStructLayers(float2 uv)
+{
+ PassForwardB b;
+ b.a.tex = gTex2;
+
+ return b.a.tex.Sample(gSampler, uv);
+}
+
+
+
+//--------------------------------------------------------------
+// PASS 30
+// Resource alias inside switch fallthrough
+//--------------------------------------------------------------
+
+float4 Pass_SwitchFallthrough(int v, float2 uv)
+{
+ Texture2D<float4> tex = gTex0;
+
+ switch(v)
+ {
+ case 0:
+ tex = gTex1;
+ case 1:
+ tex = gTex2;
+ break;
+ }
+
+ return tex.Sample(gSampler, uv);
+}
+
+
+
+//--------------------------------------------------------------
+// PASS 31
+// Resource used after early-return path merge
+//--------------------------------------------------------------
+
+float4 Pass_EarlyReturn(bool cond, float2 uv)
+{
+ Texture2D<float4> tex = gTex0;
+
+ if(cond)
+ return tex.Sample(gSampler, uv);
+
+ tex = gTex1;
+
+ return tex.Sample(gSampler, uv);
+}
+
+
+//--------------------------------------------------------------
+// PASS 32
+// Resource alias across nested blocks
+//--------------------------------------------------------------
+
+float4 Pass_NestedBlocks(float2 uv)
+{
+ Texture2D<float4> tex;
+
+ {
+ tex = gTex1;
+
+ {
+ tex = gTex2;
+ }
+ }
+
+ return tex.Sample(gSampler, uv);
+}
+
+
+
+//--------------------------------------------------------------
+// PASS 33
+// Resource assigned via bindless selection
+//--------------------------------------------------------------
+
+float4 Pass_BindlessSelection(uint a, uint b, float2 uv)
+{
+ Texture2D<float4> tex;
+
+ tex = gTexArray[a];
+ tex = gTexArray[b];
+
+ return tex.Sample(gSampler, uv);
+}
+
+
+//==============================================================
+// ENTRY POINT
+//==============================================================
+
+
+[numthreads(8,8,1)]
+void main(uint3 tid : SV_DispatchThreadID)
+{
+ float2 uv = float2(tid.xy)/256.0;
+
+ float4 r = 0;
+ r += Pass_TernaryInit(true, uv);
+ Pass_LoopVar();
+ r += Pass_ExpressionInit(uv);
+ r += Use_FailSharedStruct(uv);
+ r += Pass_StructArray(uv);
+ r += Use_Fail_Shared(uv);
+ Texture2D<float4> mytex = Fail_ReturnLocal_Uninitialized();
+ Texture2D<float4> mytex2 = Fail_ReturnLocal();
+ r += Pass_StructArray(uv);
+ r += Pass_LocalArray(uv);
+ r += Pass_Uninitialized(uv);
+ r += Pass_Alias(uv);
+ r += Pass_Reassign(uv);
+ r += Pass_IfAlias(true,uv);
+ r += Pass_Loop(uv);
+ r += Pass_Struct(uv);
+ r += Pass_FunctionForward(uv);
+ r += Pass_PhiMerge(true,uv);
+ r += Pass_Shadow(uv);
+ r += Pass_Switch(1,uv);
+ r += Pass_Bindless(0,uv);
+ r += Pass_WaveUse(uv);
+ r += Pass_NestedLoops(uv);
+ r += Pass_BlockLifetime(uv);
+ r += Pass_DeepPhi(true, false, uv);
+ r += Pass_LoopCarried(15, uv);
+ r += Pass_AliasChain(uv);
+ r += Pass_NestedStruct(uv);
+ r += Pass_ForwardStructLayers(uv);
+ r += Pass_SwitchFallthrough(0, uv);
+ r += Pass_EarlyReturn(true, uv);
+ r += Pass_NestedBlocks(uv);
+ r += Pass_BindlessSelection(2, 3, uv);
+
+ gOut[tid.xy] = r;
+}
\ No newline at end of file
>From bcc445e537ae44546db6ce4928d94039f5e4637e Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Mon, 30 Mar 2026 14:39:07 -0700
Subject: [PATCH 2/4] cleanup
---
.../Resources/local_resources_failures.hlsl | 2 +-
.../Resources/local_resources_passes.hlsl | 641 ++++++------------
2 files changed, 218 insertions(+), 425 deletions(-)
diff --git a/clang/test/SemaHLSL/Resources/local_resources_failures.hlsl b/clang/test/SemaHLSL/Resources/local_resources_failures.hlsl
index 84637cf8ce99f..aee4d33815e51 100644
--- a/clang/test/SemaHLSL/Resources/local_resources_failures.hlsl
+++ b/clang/test/SemaHLSL/Resources/local_resources_failures.hlsl
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -std=hlsl2021 -finclude-default-header -triple \
+// RUN: %clang_cc1 -std=hlsl202x -finclude-default-header -triple \
// RUN: dxil-pc-shadermodel6.3-compute %s -emit-llvm -verify
Texture2D<float4> gTex0 : register(t0);
diff --git a/clang/test/SemaHLSL/Resources/local_resources_passes.hlsl b/clang/test/SemaHLSL/Resources/local_resources_passes.hlsl
index cc5fc2835033e..b44e9ff8896dc 100644
--- a/clang/test/SemaHLSL/Resources/local_resources_passes.hlsl
+++ b/clang/test/SemaHLSL/Resources/local_resources_passes.hlsl
@@ -1,596 +1,389 @@
-// RUN: %clang_cc1 -std=hlsl2021 -finclude-default-header -triple \
-// RUN: dxil-pc-shadermodel6.3-compute %s -emit-llvm -verify
+// RUN: %clang_cc1 -std=hlsl202x -finclude-default-header -triple \
+// RUN: dxil-pc-shadermodel6.6-compute %s -emit-llvm -verify
//==============================================================
-// COMPREHENSIVE LOCAL RESOURCE VARIABLE TEST SUITE
-//
-// Structure
-// PASS TESTS
-// ENTRYPOINT
-//
-// Goal
-// Exhaustively document legal and illegal uses of HLSL
-// resource variables declared in local scope.
-//
+// GLOBAL RESOURCES
//==============================================================
-//--------------------------------------------------------------
-// Global resources
-//--------------------------------------------------------------
+RWByteAddressBuffer gBuf0 : register(u0);
+RWByteAddressBuffer gBuf1 : register(u1);
+RWByteAddressBuffer gBuf2 : register(u2);
-Texture2D<float4> gTex0 : register(t0);
-Texture2D<float4> gTex1 : register(t1);
-Texture2D<float4> gTex2 : register(t2);
+RWByteAddressBuffer gOut : register(u3);
-SamplerState gSampler : register(s0);
+RWByteAddressBuffer gBufArray[4] : register(u10);
-RWTexture2D<float4> gOut : register(u0);
-StructuredBuffer<float4> gSB : register(t3);
-RWStructuredBuffer<float4> gRW : register(u1);
+//==============================================================
+// HELPERS
+//==============================================================
-Texture2D<float4> gTexArray[4] : register(t10);
+uint DoStore(RWByteAddressBuffer buf, uint offset, uint value)
+{
+ buf.Store(offset, value);
+ return value;
+}
//==============================================================
// PASS TESTS
//==============================================================
-
-//--------------------------------------------------------------
// PASS 0
-//--------------------------------------------------------------
-
-groupshared Texture2D<float4> sharedTex;
+groupshared RWByteAddressBuffer sharedBuf;
-float4 Use_SharedTex(float2 uv)
+uint Use_Shared(uint idx)
{
- return gTex0.Sample(gSampler, uv);
+ return DoStore(gBuf0, idx * 4, 1);
}
-//--------------------------------------------------------------
// PASS 1
-//--------------------------------------------------------------
-
-float4 Fail_TernaryInit(bool cond, float2 uv)
+uint Pass_TernaryInit(bool cond, uint idx)
{
- Texture2D<float4> tex = cond ? gTex0 : gTex1;
-
- return tex.Sample(gSampler, uv);
+ // expected-warning at +1{{assignment of 'cond ? gBuf0 : gBuf1' to local resource 'buf' is not to the same unique global resource}}
+ RWByteAddressBuffer buf = cond ? gBuf0 : gBuf1;
+ return DoStore(buf, idx * 4, 2);
}
-//--------------------------------------------------------------
-// PASS 2
-//--------------------------------------------------------------
-void Fail_LoopVar()
+// PASS 2
+void Pass_LoopVar()
{
- for(Texture2D<float4> tex = gTex0; false;)
+ for(RWByteAddressBuffer buf = gBuf0; false;)
{
}
}
-//--------------------------------------------------------------
// PASS 3
-//--------------------------------------------------------------
-
-float4 Fail_ExpressionInit(float2 uv)
+uint Pass_ExpressionInit(uint idx)
{
- Texture2D<float4> tex = (true ? gTex0 : gTex1);
- return tex.Sample(gSampler, uv);
+ RWByteAddressBuffer buf = (true ? gBuf0 : gBuf1);
+ return DoStore(buf, idx * 4, 3);
}
-//--------------------------------------------------------------
-// PASS 4
-//--------------------------------------------------------------
-struct FailSharedStruct
-{
- Texture2D<float4> tex;
-};
+// PASS 4
+struct PassBufStruct { RWByteAddressBuffer buf; };
-groupshared FailSharedStruct sharedStruct;
+groupshared PassBufStruct sharedStruct;
-float4 Use_FailSharedStruct(float2 uv)
+uint Use_PassSharedStruct(uint idx)
{
- return gTex0.Sample(gSampler, uv);
+ return DoStore(gBuf0, idx * 4, 4);
}
-//--------------------------------------------------------------
// PASS 5
-//--------------------------------------------------------------
-
-struct FailStruct
+uint Pass_StructArray(uint idx)
{
- Texture2D<float4> tex;
-};
-
-float4 Fail_StructArray(float2 uv)
-{
- FailStruct s[2];
-
- s[0].tex = gTex0;
- return s[0].tex.Sample(gSampler, uv);
+ PassBufStruct s[2];
+ s[0].buf = gBuf0;
+ return DoStore(s[0].buf, idx * 4, 5);
}
-//--------------------------------------------------------------
// PASS 6
-//--------------------------------------------------------------
-groupshared Texture2D<float4> Fail_Shared;
+groupshared RWByteAddressBuffer Pass_Shared;
-float4 Use_Fail_Shared(float2 uv)
+uint Use_Pass_Shared(uint idx)
{
- return gTex0.Sample(gSampler, uv);
+ return DoStore(gBuf0, idx * 4, 6);
}
-//--------------------------------------------------------------
// PASS 7
-//--------------------------------------------------------------
-Texture2D<float4> Fail_ReturnLocal_Uninitialized()
+RWByteAddressBuffer Pass_ReturnLocal_Uninitialized()
{
- Texture2D<float4> tex; // uninitialized local resource
- return tex;
+ RWByteAddressBuffer buf;
+ return buf;
}
-//--------------------------------------------------------------
// PASS 8
-//--------------------------------------------------------------
-Texture2D<float4> Fail_ReturnLocal()
+RWByteAddressBuffer Pass_ReturnLocal()
{
- Texture2D<float4> tex = gTex0;
- return tex;
+ RWByteAddressBuffer buf = gBuf0;
+ return buf;
}
-
-//--------------------------------------------------------------
// PASS 9
-//--------------------------------------------------------------
-struct S { Texture2D<float4> arr[2]; };
-float4 Pass_StructArray(float2 uv)
+struct S { RWByteAddressBuffer arr[2]; };
+
+uint Pass_StructArrayAssignment(uint idx)
{
S s;
- s.arr[0] = gTex0;
- return s.arr[0].Sample(gSampler, uv);
+ s.arr[0] = gBuf0;
+ return DoStore(s.arr[0], idx * 4, 9);
}
-//--------------------------------------------------------------
// PASS 10
-//--------------------------------------------------------------
-
-float4 Pass_LocalArray(float2 uv)
+uint Pass_LocalArray(uint idx)
{
- Texture2D<float4> arr[2];
-
- arr[0] = gTex0;
- return arr[0].Sample(gSampler, uv);
+ RWByteAddressBuffer arr[2];
+ arr[0] = gBuf0;
+ return DoStore(arr[0], idx * 4, 10);
}
-//--------------------------------------------------------------
// PASS 11
-// Uninitialized use
-//--------------------------------------------------------------
-
-float4 Pass_Uninitialized(float2 uv)
+uint Pass_Uninitialized(uint idx)
{
- Texture2D<float4> tex;
-
-
- return tex.Sample(gSampler, uv);
+ RWByteAddressBuffer buf;
+ return DoStore(buf, idx * 4, 11);
}
-//--------------------------------------------------------------
// PASS 12
-// Simple local alias
-//--------------------------------------------------------------
-
-float4 Pass_Alias(float2 uv)
+uint Pass_Alias(uint idx)
{
- Texture2D<float4> tex = gTex0;
- return tex.Sample(gSampler, uv);
+ RWByteAddressBuffer buf = gBuf0;
+ return DoStore(buf, idx * 4, 12);
}
-
-//--------------------------------------------------------------
// PASS 13
-// Reassignment
-//--------------------------------------------------------------
-
-float4 Pass_Reassign(float2 uv)
+uint Pass_Reassign(uint idx)
{
- Texture2D<float4> tex = gTex0;
- tex = gTex1;
- return tex.Sample(gSampler, uv);
+ // expected-note at +1{{variable 'buf' is declared here}}
+ RWByteAddressBuffer buf = gBuf0;
+ // expected-warning at +1{{assignment of 'gBuf1' to local resource 'buf' is not to the same unique global resource}}
+ buf = gBuf1;
+ return DoStore(buf, idx * 4, 13);
}
-
-//--------------------------------------------------------------
// PASS 14
-// Control flow aliasing
-//--------------------------------------------------------------
-
-float4 Pass_IfAlias(bool cond, float2 uv)
+uint Pass_IfAlias(bool cond, uint idx)
{
- Texture2D<float4> tex;
-
- if (cond)
- tex = gTex0;
- else
- tex = gTex1;
-
- return tex.Sample(gSampler, uv);
+ RWByteAddressBuffer buf;
+ // expected-warning at +1{{assignment of 'cond ? gBuf0 : gBuf1' to local resource 'buf' is not to the same unique global resource}}
+ buf = cond ? gBuf0 : gBuf1;
+ return DoStore(buf, idx * 4, 14);
}
-
-//--------------------------------------------------------------
// PASS 15
-// Loop aliasing
-//--------------------------------------------------------------
-
-float4 Pass_Loop(float2 uv)
+uint Pass_Loop(uint idx)
{
- float4 sum = 0;
-
- for(int i=0;i<4;i++)
- {
- Texture2D<float4> tex = gTexArray[i];
- sum += tex.Sample(gSampler, uv);
+ uint sum = 0;
+ for(unsigned int i=0;i<4;i++)
+ {
+ RWByteAddressBuffer buf = gBufArray[i];
+ sum += DoStore(buf, idx * 4 + i * 4, 15);
}
-
return sum;
}
-
-
-//--------------------------------------------------------------
// PASS 16
-// Struct containing resource
-//--------------------------------------------------------------
-
struct PassStruct
{
- Texture2D<float4> tex;
- SamplerState samp;
+ RWByteAddressBuffer buf;
};
-float4 Pass_Struct(float2 uv)
+uint Pass_Struct(uint idx)
{
PassStruct s;
- s.tex = gTex0;
- s.samp = gSampler;
-
- return s.tex.Sample(s.samp, uv);
+ s.buf = gBuf0;
+ return DoStore(s.buf, idx * 4, 16);
}
-
-
-//--------------------------------------------------------------
// PASS 17
-// Passing resource through multiple functions
-//--------------------------------------------------------------
-
-float4 Pass_Level2(Texture2D<float4> tex, float2 uv)
+uint Pass_Level2(RWByteAddressBuffer buf, uint idx)
{
- return tex.Sample(gSampler, uv);
+ return DoStore(buf, idx * 4, 17);
}
-float4 Pass_Level1(Texture2D<float4> tex, float2 uv)
+uint Pass_Level1(RWByteAddressBuffer buf, uint idx)
{
- return Pass_Level2(tex, uv);
+ return Pass_Level2(buf, idx);
}
-float4 Pass_FunctionForward(float2 uv)
+uint Pass_FunctionForward(uint idx)
{
- Texture2D<float4> tex = gTex1;
- return Pass_Level1(tex, uv);
+ RWByteAddressBuffer buf = gBuf1;
+ return Pass_Level1(buf, idx);
}
-
-
-//--------------------------------------------------------------
// PASS 18
-// Resource merge via conditional assignments
-// (SSA-style PHI equivalent)
-//--------------------------------------------------------------
-
-float4 Pass_PhiMerge(bool cond, float2 uv)
+uint Pass_PhiMerge(bool cond, uint idx)
{
- Texture2D<float4> tex;
-
- if(cond)
- tex = gTex0;
- else
- tex = gTex2;
-
- return tex.Sample(gSampler, uv);
+ RWByteAddressBuffer buf;
+ // expected-warning at +1{{assignment of 'cond ? gBuf0 : gBuf2' to local resource 'buf' is not to the same unique global resource}}
+ buf = cond ? gBuf0 : gBuf2;
+ return DoStore(buf, idx * 4, 18);
}
-
-
-//--------------------------------------------------------------
// PASS 19
-// Nested scope shadowing
-//--------------------------------------------------------------
-
-float4 Pass_Shadow(float2 uv)
+uint Pass_Shadow(uint idx)
{
- Texture2D<float4> tex = gTex0;
-
+ RWByteAddressBuffer buf = gBuf0;
{
- Texture2D<float4> tex = gTex1;
- return tex.Sample(gSampler, uv);
+ RWByteAddressBuffer buf = gBuf1;
+ return DoStore(buf, idx * 4, 19);
}
}
-
-
-//--------------------------------------------------------------
// PASS 20
-// Resource in switch
-//--------------------------------------------------------------
-
-float4 Pass_Switch(int v, float2 uv)
+uint Pass_Switch(int v, uint idx)
{
- Texture2D<float4> tex = gTex0;
+ // expected-note at +2{{variable 'buf' is declared here}}
+ // expected-note at +1{{variable 'buf' is declared here}}
+ RWByteAddressBuffer buf = gBuf0;
switch(v)
{
- case 1: tex = gTex1; break;
- case 2: tex = gTex2; break;
+ // expected-warning at +1{{assignment of 'gBuf1' to local resource 'buf' is not to the same unique global resource}}
+ case 1: buf = gBuf1; break;
+ // expected-warning at +1{{assignment of 'gBuf2' to local resource 'buf' is not to the same unique global resource}}
+ case 2: buf = gBuf2; break;
}
- return tex.Sample(gSampler, uv);
+ return DoStore(buf, idx * 4, 20);
}
-
-
-//--------------------------------------------------------------
// PASS 21
-// Bindless descriptor indexing
-//--------------------------------------------------------------
-
-float4 Pass_Bindless(uint idx, float2 uv)
+uint Pass_Bindless(uint idx)
{
- Texture2D<float4> tex = gTexArray[idx];
- return tex.Sample(gSampler, uv);
+ RWByteAddressBuffer buf = gBufArray[idx & 3];
+ return DoStore(buf, idx * 4, 21);
}
-
-
-//--------------------------------------------------------------
// PASS 22
-// Resource with wave operations
-//--------------------------------------------------------------
-
-float4 Pass_WaveUse(float2 uv)
+uint Pass_WaveUse(uint idx)
{
- Texture2D<float4> tex = gTex0;
-
- float4 v = tex.Sample(gSampler, uv);
-
+ RWByteAddressBuffer buf = gBuf0;
uint active = WaveActiveCountBits(true);
-
- return v * active;
+ return DoStore(buf, idx * 4, active);
}
-
-
-//--------------------------------------------------------------
// PASS 23
-// Resource alias used inside nested loops
-//--------------------------------------------------------------
-
-float4 Pass_NestedLoops(float2 uv)
-{
- float4 sum = 0;
-
- for(int i=0;i<2;i++)
- for(int j=0;j<2;j++)
- {
- Texture2D<float4> tex = gTexArray[i+j];
- sum += tex.Sample(gSampler, uv);
+uint Pass_NestedLoops(uint idx)
+{
+ uint sum = 0;
+ for(unsigned int i=0;i<2;i++)
+ for(unsigned int j=0;j<2;j++)
+ {
+ RWByteAddressBuffer buf = gBufArray[i+j];
+ sum += DoStore(buf, idx * 4 + (i+j)*4, 23);
}
-
return sum;
}
-
-
-//--------------------------------------------------------------
// PASS 24
-// Resource lifetime across blocks
-//--------------------------------------------------------------
-
-float4 Pass_BlockLifetime(float2 uv)
+uint Pass_BlockLifetime(uint idx)
{
- Texture2D<float4> tex;
-
+ RWByteAddressBuffer buf;
{
- tex = gTex1;
+ buf = gBuf1;
}
-
- return tex.Sample(gSampler, uv);
+ return DoStore(buf, idx * 4, 24);
}
-
-//--------------------------------------------------------------
// PASS 25
-// Deep nested PHI merges
-//--------------------------------------------------------------
-
-float4 Pass_DeepPhi(bool a, bool b, float2 uv)
+uint Pass_DeepPhi(bool a, bool b, uint idx)
{
- Texture2D<float4> tex;
+ RWByteAddressBuffer buf;
if(a)
- {
- if(b)
- tex = gTex0;
- else
- tex = gTex1;
- }
+ // expected-warning at +1{{assignment of 'b ? gBuf0 : gBuf1' to local resource 'buf' is not to the same unique global resource}}
+ buf = b ? gBuf0 : gBuf1;
else
- {
- tex = gTex2;
- }
+ buf = gBuf2;
- return tex.Sample(gSampler, uv);
+ return DoStore(buf, idx * 4, 25);
}
-
-
-//--------------------------------------------------------------
// PASS 26
-// Loop-carried resource value
-//--------------------------------------------------------------
-
-float4 Pass_LoopCarried(int iterations, float2 uv)
+uint Pass_LoopCarried(int iterations, uint idx)
{
- Texture2D<float4> tex = gTex0;
+ // expected-note at +1{{variable 'buf' is declared here}}
+ RWByteAddressBuffer buf = gBuf0;
for(int i=0;i<iterations;i++)
- {
- tex = gTexArray[i & 3];
- }
+ // expected-warning at +1{{assignment of 'gBufArray[i & 3]' to local resource 'buf' is not to the same unique global resource}}
+ buf = gBufArray[i & 3];
- return tex.Sample(gSampler, uv);
+ return DoStore(buf, idx * 4, 26);
}
-
-
-//--------------------------------------------------------------
// PASS 27
-// Resource alias chain
-//--------------------------------------------------------------
-
-float4 Pass_AliasChain(float2 uv)
+uint Pass_AliasChain(uint idx)
{
- Texture2D<float4> a = gTex0;
- Texture2D<float4> b = a;
- Texture2D<float4> c = b;
+ RWByteAddressBuffer a = gBuf0;
+ RWByteAddressBuffer b = a;
+ RWByteAddressBuffer c = b;
- return c.Sample(gSampler, uv);
+ return DoStore(c, idx * 4, 27);
}
-
-
-//--------------------------------------------------------------
// PASS 28
-// Resource inside nested structs
-//--------------------------------------------------------------
-
-struct PassNestedInner
-{
- Texture2D<float4> tex;
-};
-
-struct PassNestedOuter
-{
- PassNestedInner inner;
-};
+struct PassNestedInner { RWByteAddressBuffer buf; };
+struct PassNestedOuter { PassNestedInner inner; };
-float4 Pass_NestedStruct(float2 uv)
+uint Pass_NestedStruct(uint idx)
{
PassNestedOuter s;
-
- s.inner.tex = gTex1;
-
- return s.inner.tex.Sample(gSampler, uv);
+ s.inner.buf = gBuf1;
+ return DoStore(s.inner.buf, idx * 4, 28);
}
-
-
-//--------------------------------------------------------------
// PASS 29
-// Resource forwarded through multiple struct layers
-//--------------------------------------------------------------
-
-struct PassForwardA { Texture2D<float4> tex; };
+struct PassForwardA { RWByteAddressBuffer buf; };
struct PassForwardB { PassForwardA a; };
-float4 Pass_ForwardStructLayers(float2 uv)
+uint Pass_ForwardStructLayers(uint idx)
{
PassForwardB b;
- b.a.tex = gTex2;
-
- return b.a.tex.Sample(gSampler, uv);
+ b.a.buf = gBuf2;
+ return DoStore(b.a.buf, idx * 4, 29);
}
-
-
-//--------------------------------------------------------------
// PASS 30
-// Resource alias inside switch fallthrough
-//--------------------------------------------------------------
-
-float4 Pass_SwitchFallthrough(int v, float2 uv)
+uint Pass_SwitchFallthrough(int v, uint idx)
{
- Texture2D<float4> tex = gTex0;
+ // expected-note at +2{{variable 'buf' is declared here}}
+ // expected-note at +1{{variable 'buf' is declared here}}
+ RWByteAddressBuffer buf = gBuf0;
switch(v)
{
- case 0:
- tex = gTex1;
- case 1:
- tex = gTex2;
- break;
+ // expected-warning at +1{{assignment of 'gBuf1' to local resource 'buf' is not to the same unique global resource}}
+ case 0: buf = gBuf1;
+ // expected-warning at +1{{assignment of 'gBuf2' to local resource 'buf' is not to the same unique global resource}}
+ case 1: buf = gBuf2; break;
}
- return tex.Sample(gSampler, uv);
+ return DoStore(buf, idx * 4, 30);
}
-
-
-//--------------------------------------------------------------
// PASS 31
-// Resource used after early-return path merge
-//--------------------------------------------------------------
-
-float4 Pass_EarlyReturn(bool cond, float2 uv)
+uint Pass_EarlyReturn(bool cond, uint idx)
{
- Texture2D<float4> tex = gTex0;
+ // expected-note at +1{{variable 'buf' is declared here}}
+ RWByteAddressBuffer buf = gBuf0;
if(cond)
- return tex.Sample(gSampler, uv);
-
- tex = gTex1;
-
- return tex.Sample(gSampler, uv);
+ return DoStore(buf, idx * 4, 31);
+ // expected-warning at +1{{assignment of 'gBuf1' to local resource 'buf' is not to the same unique global resource}}
+ buf = gBuf1;
+ return DoStore(buf, idx * 4, 31);
}
-
-//--------------------------------------------------------------
// PASS 32
-// Resource alias across nested blocks
-//--------------------------------------------------------------
-
-float4 Pass_NestedBlocks(float2 uv)
+uint Pass_NestedBlocks(uint idx)
{
- Texture2D<float4> tex;
+ // expected-note at +1{{variable 'buf' is declared here}}
+ RWByteAddressBuffer buf;
{
- tex = gTex1;
-
+ buf = gBuf1;
{
- tex = gTex2;
+ // expected-warning at +1{{assignment of 'gBuf2' to local resource 'buf' is not to the same unique global resource}}
+ buf = gBuf2;
}
}
- return tex.Sample(gSampler, uv);
+ return DoStore(buf, idx * 4, 32);
}
-
-
-//--------------------------------------------------------------
// PASS 33
-// Resource assigned via bindless selection
-//--------------------------------------------------------------
-
-float4 Pass_BindlessSelection(uint a, uint b, float2 uv)
+uint Pass_BindlessSelection(uint a, uint b, uint idx)
{
- Texture2D<float4> tex;
+ RWByteAddressBuffer buf;
- tex = gTexArray[a];
- tex = gTexArray[b];
+ buf = gBufArray[a & 3];
+ buf = gBufArray[b & 3];
- return tex.Sample(gSampler, uv);
+ return DoStore(buf, idx * 4, 33);
}
@@ -598,46 +391,46 @@ float4 Pass_BindlessSelection(uint a, uint b, float2 uv)
// ENTRY POINT
//==============================================================
-
[numthreads(8,8,1)]
void main(uint3 tid : SV_DispatchThreadID)
{
- float2 uv = float2(tid.xy)/256.0;
+ uint idx = tid.x + tid.y * 8;
+
+ uint r = 0;
- float4 r = 0;
- r += Pass_TernaryInit(true, uv);
+ r += Pass_TernaryInit(true, idx);
Pass_LoopVar();
- r += Pass_ExpressionInit(uv);
- r += Use_FailSharedStruct(uv);
- r += Pass_StructArray(uv);
- r += Use_Fail_Shared(uv);
- Texture2D<float4> mytex = Fail_ReturnLocal_Uninitialized();
- Texture2D<float4> mytex2 = Fail_ReturnLocal();
- r += Pass_StructArray(uv);
- r += Pass_LocalArray(uv);
- r += Pass_Uninitialized(uv);
- r += Pass_Alias(uv);
- r += Pass_Reassign(uv);
- r += Pass_IfAlias(true,uv);
- r += Pass_Loop(uv);
- r += Pass_Struct(uv);
- r += Pass_FunctionForward(uv);
- r += Pass_PhiMerge(true,uv);
- r += Pass_Shadow(uv);
- r += Pass_Switch(1,uv);
- r += Pass_Bindless(0,uv);
- r += Pass_WaveUse(uv);
- r += Pass_NestedLoops(uv);
- r += Pass_BlockLifetime(uv);
- r += Pass_DeepPhi(true, false, uv);
- r += Pass_LoopCarried(15, uv);
- r += Pass_AliasChain(uv);
- r += Pass_NestedStruct(uv);
- r += Pass_ForwardStructLayers(uv);
- r += Pass_SwitchFallthrough(0, uv);
- r += Pass_EarlyReturn(true, uv);
- r += Pass_NestedBlocks(uv);
- r += Pass_BindlessSelection(2, 3, uv);
-
- gOut[tid.xy] = r;
+ r += Pass_ExpressionInit(idx);
+ r += Use_PassSharedStruct(idx);
+ r += Pass_StructArray(idx);
+ r += Use_Pass_Shared(idx);
+ RWByteAddressBuffer tmp0 = Pass_ReturnLocal_Uninitialized();
+ RWByteAddressBuffer tmp1 = Pass_ReturnLocal();
+ r += Pass_StructArray(idx);
+ r += Pass_LocalArray(idx);
+ r += Pass_Uninitialized(idx);
+ r += Pass_Alias(idx);
+ r += Pass_Reassign(idx);
+ r += Pass_IfAlias(true, idx);
+ r += Pass_Loop(idx);
+ r += Pass_Struct(idx);
+ r += Pass_FunctionForward(idx);
+ r += Pass_PhiMerge(true, idx);
+ r += Pass_Shadow(idx);
+ r += Pass_Switch(1, idx);
+ r += Pass_Bindless(idx);
+ r += Pass_WaveUse(idx);
+ r += Pass_NestedLoops(idx);
+ r += Pass_BlockLifetime(idx);
+ r += Pass_DeepPhi(true, false, idx);
+ r += Pass_LoopCarried(15, idx);
+ r += Pass_AliasChain(idx);
+ r += Pass_NestedStruct(idx);
+ r += Pass_ForwardStructLayers(idx);
+ r += Pass_SwitchFallthrough(0, idx);
+ r += Pass_EarlyReturn(true, idx);
+ r += Pass_NestedBlocks(idx);
+ r += Pass_BindlessSelection(2, 3, idx);
+
+ gOut.Store(idx * 4, r);
}
\ No newline at end of file
>From 9146bb048c6712faeb8113ab2b22051156a7f036 Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Mon, 30 Mar 2026 15:00:21 -0700
Subject: [PATCH 3/4] first test, failing
---
.../Local-Resources/use_groupshared.hlsl | 23 +++++++++++++++++++
1 file changed, 23 insertions(+)
create mode 100644 clang/test/SemaHLSL/Resources/Local-Resources/use_groupshared.hlsl
diff --git a/clang/test/SemaHLSL/Resources/Local-Resources/use_groupshared.hlsl b/clang/test/SemaHLSL/Resources/Local-Resources/use_groupshared.hlsl
new file mode 100644
index 0000000000000..16747acbc19ff
--- /dev/null
+++ b/clang/test/SemaHLSL/Resources/Local-Resources/use_groupshared.hlsl
@@ -0,0 +1,23 @@
+// RUN: %clang_cc1 -std=hlsl202x -finclude-default-header -triple \
+// RUN: dxil-pc-shadermodel6.6-compute %s -emit-llvm -verify
+
+RWByteAddressBuffer gOut : register(u3);
+
+uint DoStore(RWByteAddressBuffer buf, uint offset, uint value)
+{
+ buf.Store(offset, value);
+ return value;
+}
+
+groupshared RWByteAddressBuffer sharedBuf;
+uint Use_Shared(uint idx)
+{
+ return DoStore(sharedBuf, idx * 4, 1);
+}
+
+[numthreads(8,8,1)]
+void main(uint3 tid : SV_DispatchThreadID)
+{
+ uint idx = tid.x + tid.y * 8;
+ Use_Shared(idx);
+}
\ No newline at end of file
>From 20646667ced7fb1a343b793e6c3b23f4075f7e4d Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Tue, 31 Mar 2026 12:04:50 -0700
Subject: [PATCH 4/4] checkpoint
---
.../ternary_initialization.hlsl | 30 ++
.../Local-Resources/expression_init.hlsl | 28 ++
.../Local-Resources/expression_init.ll | 318 ++++++++++++++++++
.../Resources/Local-Resources/loop_var.hlsl | 23 ++
.../Local-Resources/use_groupshared.hlsl | 12 +-
.../use_struct_groupshared.hlsl | 37 ++
.../Local-Resources/use_struct_groupshared.ll | 255 ++++++++++++++
.../Resources/local_resources_passes.hlsl | 31 --
8 files changed, 701 insertions(+), 33 deletions(-)
create mode 100644 clang/test/CodeGenHLSL/resources/Local-Resources/ternary_initialization.hlsl
create mode 100644 clang/test/SemaHLSL/Resources/Local-Resources/expression_init.hlsl
create mode 100644 clang/test/SemaHLSL/Resources/Local-Resources/expression_init.ll
create mode 100644 clang/test/SemaHLSL/Resources/Local-Resources/loop_var.hlsl
create mode 100644 clang/test/SemaHLSL/Resources/Local-Resources/use_struct_groupshared.hlsl
create mode 100644 clang/test/SemaHLSL/Resources/Local-Resources/use_struct_groupshared.ll
diff --git a/clang/test/CodeGenHLSL/resources/Local-Resources/ternary_initialization.hlsl b/clang/test/CodeGenHLSL/resources/Local-Resources/ternary_initialization.hlsl
new file mode 100644
index 0000000000000..71da9d8e1af5b
--- /dev/null
+++ b/clang/test/CodeGenHLSL/resources/Local-Resources/ternary_initialization.hlsl
@@ -0,0 +1,30 @@
+// RUN: %clang_cc1 -std=hlsl202x -finclude-default-header -triple \
+// RUN: dxil-pc-shadermodel6.6-compute -emit-llvm -disable-llvm-passes 2>&1 -o - %s | llvm-cxxfilt | FileCheck %s
+
+// This test fails after verify.
+
+RWByteAddressBuffer gOut : register(u3);
+RWByteAddressBuffer gBuf0 : register(u0);
+RWByteAddressBuffer gBuf1 : register(u1);
+
+uint DoStore(RWByteAddressBuffer buf, uint offset, uint value)
+{
+ buf.Store(offset, value);
+ return value;
+}
+
+uint Pass_TernaryInit(bool cond, uint idx)
+{
+ // DXC emits this warning: local resource not guaranteed to map to unique global resource.
+ // Below is generated by -Whlsl-explicit-binding
+ // CHECK: warning: assignment of 'cond ? gBuf0 : gBuf1' to local resource 'buf' is not to the same unique global resource
+ RWByteAddressBuffer buf = cond ? gBuf0 : gBuf1;
+ return DoStore(buf, idx * 4, 2);
+}
+
+[numthreads(8,8,1)]
+void main(uint3 tid : SV_DispatchThreadID)
+{
+ uint idx = tid.x + tid.y * 8;
+ Pass_TernaryInit(idx < 32, idx);
+}
\ No newline at end of file
diff --git a/clang/test/SemaHLSL/Resources/Local-Resources/expression_init.hlsl b/clang/test/SemaHLSL/Resources/Local-Resources/expression_init.hlsl
new file mode 100644
index 0000000000000..3480b5f65a82e
--- /dev/null
+++ b/clang/test/SemaHLSL/Resources/Local-Resources/expression_init.hlsl
@@ -0,0 +1,28 @@
+// RUN: %clang_cc1 -std=hlsl202x -finclude-default-header -triple \
+// RUN: dxil-pc-shadermodel6.6-compute %s -emit-llvm -verify
+
+// expected-no-diagnostics
+
+RWByteAddressBuffer gBuf0 : register(u0);
+RWByteAddressBuffer gBuf1 : register(u1);
+
+RWByteAddressBuffer gOut : register(u3);
+
+uint DoStore(RWByteAddressBuffer buf, uint offset, uint value)
+{
+ buf.Store(offset, value);
+ return value;
+}
+
+uint Pass_ExpressionInit(uint idx)
+{
+ RWByteAddressBuffer buf = (true ? gBuf0 : gBuf1);
+ return DoStore(buf, idx * 4, 3);
+}
+
+[numthreads(8,8,1)]
+void main(uint3 tid : SV_DispatchThreadID)
+{
+ uint idx = tid.x + tid.y * 8;
+ Pass_ExpressionInit(idx);
+}
\ No newline at end of file
diff --git a/clang/test/SemaHLSL/Resources/Local-Resources/expression_init.ll b/clang/test/SemaHLSL/Resources/Local-Resources/expression_init.ll
new file mode 100644
index 0000000000000..0ff7f24d7fefe
--- /dev/null
+++ b/clang/test/SemaHLSL/Resources/Local-Resources/expression_init.ll
@@ -0,0 +1,318 @@
+; ModuleID = 'D:\llvm-project\clang\test\SemaHLSL\Resources\Local-Resources\expression_init.hlsl'
+source_filename = "D:\\llvm-project\\clang\\test\\SemaHLSL\\Resources\\Local-Resources\\expression_init.hlsl"
+target datalayout = "e-m:e-ve-p:32:32-i1:32-i8:8-i16:16-i32:32-i64:64-f16:16-f32:32-f64:64-n8:16:32:64"
+target triple = "dxilv1.6-pc-shadermodel6.6-compute"
+
+%"class.hlsl::RWByteAddressBuffer" = type { target("dx.RawBuffer", i8, 1, 0) }
+
+ at _ZL5gBuf0 = internal global %"class.hlsl::RWByteAddressBuffer" poison, align 4
+ at .str = private unnamed_addr constant [6 x i8] c"gBuf0\00", align 1
+ at _ZL5gBuf1 = internal global %"class.hlsl::RWByteAddressBuffer" poison, align 4
+ at .str.2 = private unnamed_addr constant [6 x i8] c"gBuf1\00", align 1
+ at _ZL4gOut = internal global %"class.hlsl::RWByteAddressBuffer" poison, align 4
+ at .str.4 = private unnamed_addr constant [5 x i8] c"gOut\00", align 1
+
+; Function Attrs: alwaysinline convergent mustprogress norecurse nounwind
+define hidden noundef i32 @_Z7DoStoreN4hlsl19RWByteAddressBufferEjj(ptr noundef byval(%"class.hlsl::RWByteAddressBuffer") align 4 %buf, i32 noundef %offset, i32 noundef %value) #0 {
+entry:
+ %this.addr.i = alloca ptr, align 4
+ %Index.addr.i = alloca i32, align 4
+ %Value.addr.i = alloca i32, align 4
+ %offset.addr = alloca i32, align 4
+ %value.addr = alloca i32, align 4
+ store i32 %offset, ptr %offset.addr, align 4
+ store i32 %value, ptr %value.addr, align 4
+ %0 = load i32, ptr %offset.addr, align 4
+ %1 = load i32, ptr %value.addr, align 4
+ store ptr %buf, ptr %this.addr.i, align 4
+ store i32 %0, ptr %Index.addr.i, align 4
+ store i32 %1, ptr %Value.addr.i, align 4
+ %this1.i = load ptr, ptr %this.addr.i, align 4
+ %2 = load i32, ptr %Value.addr.i, align 4
+ %3 = load target("dx.RawBuffer", i8, 1, 0), ptr %this1.i, align 4
+ %4 = load i32, ptr %Index.addr.i, align 4
+ %5 = call ptr @llvm.dx.resource.getpointer.p0.tdx.RawBuffer_i8_1_0t.i32(target("dx.RawBuffer", i8, 1, 0) %3, i32 %4)
+ store i32 %2, ptr %5, align 4
+ %6 = load i32, ptr %value.addr, align 4
+ ret i32 %6
+}
+
+; Function Attrs: alwaysinline convergent mustprogress norecurse nounwind
+define hidden noundef i32 @_Z19Pass_ExpressionInitj(i32 noundef %idx) #0 {
+entry:
+ %this.addr.i10 = alloca ptr, align 4
+ %other.addr.i11 = alloca ptr, align 4
+ %this.addr.i7 = alloca ptr, align 4
+ %other.addr.i8 = alloca ptr, align 4
+ %this.addr.i4 = alloca ptr, align 4
+ %other.addr.i5 = alloca ptr, align 4
+ %this.addr.i2 = alloca ptr, align 4
+ %other.addr.i = alloca ptr, align 4
+ %this.addr.i = alloca ptr, align 4
+ %Index.addr.i = alloca i32, align 4
+ %Value.addr.i = alloca i32, align 4
+ %offset.addr.i = alloca i32, align 4
+ %value.addr.i = alloca i32, align 4
+ %agg.tmp1 = alloca %"class.hlsl::RWByteAddressBuffer", align 8
+ %idx.addr = alloca i32, align 4
+ %buf = alloca %"class.hlsl::RWByteAddressBuffer", align 4
+ %agg.tmp = alloca %"class.hlsl::RWByteAddressBuffer", align 4
+ store i32 %idx, ptr %idx.addr, align 4
+ store ptr %buf, ptr %this.addr.i4, align 4
+ store ptr @_ZL5gBuf0, ptr %other.addr.i5, align 4
+ %this1.i6 = load ptr, ptr %this.addr.i4, align 4
+ %0 = load ptr, ptr %other.addr.i5, align 4
+ store ptr %this1.i6, ptr %this.addr.i7, align 4
+ store ptr %0, ptr %other.addr.i8, align 4
+ %this1.i9 = load ptr, ptr %this.addr.i7, align 4
+ %1 = load ptr, ptr %other.addr.i8, align 4, !nonnull !3, !align !4
+ %2 = load target("dx.RawBuffer", i8, 1, 0), ptr %1, align 4
+ store target("dx.RawBuffer", i8, 1, 0) %2, ptr %this1.i9, align 4
+ store ptr %agg.tmp, ptr %this.addr.i2, align 4
+ store ptr %buf, ptr %other.addr.i, align 4
+ %this1.i3 = load ptr, ptr %this.addr.i2, align 4
+ %3 = load ptr, ptr %other.addr.i, align 4
+ store ptr %this1.i3, ptr %this.addr.i10, align 4
+ store ptr %3, ptr %other.addr.i11, align 4
+ %this1.i12 = load ptr, ptr %this.addr.i10, align 4
+ %4 = load ptr, ptr %other.addr.i11, align 4, !nonnull !3, !align !4
+ %5 = load target("dx.RawBuffer", i8, 1, 0), ptr %4, align 4
+ store target("dx.RawBuffer", i8, 1, 0) %5, ptr %this1.i12, align 4
+ %6 = load i32, ptr %idx.addr, align 4
+ %mul = mul i32 %6, 4
+ call void @llvm.memcpy.p0.p0.i64(ptr align 8 %agg.tmp1, ptr align 4 %agg.tmp, i64 4, i1 false)
+ store i32 %mul, ptr %offset.addr.i, align 4
+ store i32 3, ptr %value.addr.i, align 4
+ %7 = load i32, ptr %offset.addr.i, align 4
+ %8 = load i32, ptr %value.addr.i, align 4
+ store ptr %agg.tmp1, ptr %this.addr.i, align 4
+ store i32 %7, ptr %Index.addr.i, align 4
+ store i32 %8, ptr %Value.addr.i, align 4
+ %this1.i = load ptr, ptr %this.addr.i, align 4
+ %9 = load i32, ptr %Value.addr.i, align 4
+ %10 = load target("dx.RawBuffer", i8, 1, 0), ptr %this1.i, align 4
+ %11 = load i32, ptr %Index.addr.i, align 4
+ %12 = call ptr @llvm.dx.resource.getpointer.p0.tdx.RawBuffer_i8_1_0t.i32(target("dx.RawBuffer", i8, 1, 0) %10, i32 %11)
+ store i32 %9, ptr %12, align 4
+ %13 = load i32, ptr %value.addr.i, align 4
+ ret i32 %13
+}
+
+; Function Attrs: convergent noinline norecurse
+define void @main() #1 {
+entry:
+ %this.addr.i19.i = alloca ptr, align 4
+ %other.addr.i20.i = alloca ptr, align 4
+ %this.addr.i16.i = alloca ptr, align 4
+ %other.addr.i17.i = alloca ptr, align 4
+ %this.addr.i13.i = alloca ptr, align 4
+ %other.addr.i14.i = alloca ptr, align 4
+ %this.addr.i10.i = alloca ptr, align 4
+ %other.addr.i11.i = alloca ptr, align 4
+ %this.addr.i7.i = alloca ptr, align 4
+ %other.addr.i8.i = alloca ptr, align 4
+ %this.addr.i.i4 = alloca ptr, align 4
+ %other.addr.i.i5 = alloca ptr, align 4
+ %registerNo.addr.i.i1.i = alloca i32, align 4
+ %spaceNo.addr.i.i2.i = alloca i32, align 4
+ %range.addr.i.i3.i = alloca i32, align 4
+ %index.addr.i.i4.i = alloca i32, align 4
+ %name.addr.i.i5.i = alloca ptr, align 4
+ %tmp.i.i6.i = alloca %"class.hlsl::RWByteAddressBuffer", align 4
+ %registerNo.addr.i.i.i = alloca i32, align 4
+ %spaceNo.addr.i.i.i = alloca i32, align 4
+ %range.addr.i.i.i = alloca i32, align 4
+ %index.addr.i.i.i = alloca i32, align 4
+ %name.addr.i.i.i = alloca ptr, align 4
+ %tmp.i.i.i = alloca %"class.hlsl::RWByteAddressBuffer", align 4
+ %registerNo.addr.i.i = alloca i32, align 4
+ %spaceNo.addr.i.i = alloca i32, align 4
+ %range.addr.i.i = alloca i32, align 4
+ %index.addr.i.i = alloca i32, align 4
+ %name.addr.i.i = alloca ptr, align 4
+ %tmp.i.i = alloca %"class.hlsl::RWByteAddressBuffer", align 4
+ %this.addr.i1 = alloca ptr, align 4
+ %other.addr.i2 = alloca ptr, align 4
+ %this.addr.i = alloca ptr, align 4
+ %other.addr.i = alloca ptr, align 4
+ %this.addr.i1.i = alloca ptr, align 4
+ %other.addr.i2.i = alloca ptr, align 4
+ %this.addr.i.i = alloca ptr, align 4
+ %other.addr.i.i = alloca ptr, align 4
+ %this.addr.i.i.i = alloca ptr, align 4
+ %Index.addr.i.i.i = alloca i32, align 4
+ %Value.addr.i.i.i = alloca i32, align 4
+ %offset.addr.i.i.i = alloca i32, align 4
+ %value.addr.i.i.i = alloca i32, align 4
+ %agg.tmp1.i.i = alloca %"class.hlsl::RWByteAddressBuffer", align 8
+ %idx.addr.i.i = alloca i32, align 4
+ %buf.i.i = alloca %"class.hlsl::RWByteAddressBuffer", align 4
+ %agg.tmp.i.i = alloca %"class.hlsl::RWByteAddressBuffer", align 4
+ %tid.addr.i = alloca <3 x i32>, align 4
+ %idx.i = alloca i32, align 4
+ call void @llvm.experimental.noalias.scope.decl(metadata !5)
+ store i32 0, ptr %registerNo.addr.i.i, align 4, !noalias !5
+ store i32 0, ptr %spaceNo.addr.i.i, align 4, !noalias !5
+ store i32 1, ptr %range.addr.i.i, align 4, !noalias !5
+ store i32 0, ptr %index.addr.i.i, align 4, !noalias !5
+ store ptr @.str, ptr %name.addr.i.i, align 4, !noalias !5
+ %0 = load i32, ptr %registerNo.addr.i.i, align 4, !noalias !5
+ %1 = load i32, ptr %spaceNo.addr.i.i, align 4, !noalias !5
+ %2 = load i32, ptr %range.addr.i.i, align 4, !noalias !5
+ %3 = load i32, ptr %index.addr.i.i, align 4, !noalias !5
+ %4 = load ptr, ptr %name.addr.i.i, align 4, !noalias !5
+ %5 = call target("dx.RawBuffer", i8, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_i8_1_0t(i32 %1, i32 %0, i32 %2, i32 %3, ptr %4)
+ store target("dx.RawBuffer", i8, 1, 0) %5, ptr %tmp.i.i, align 4, !noalias !5
+ store ptr @_ZL5gBuf0, ptr %this.addr.i10.i, align 4
+ store ptr %tmp.i.i, ptr %other.addr.i11.i, align 4
+ %this1.i12.i = load ptr, ptr %this.addr.i10.i, align 4
+ %6 = load ptr, ptr %other.addr.i11.i, align 4
+ store ptr %this1.i12.i, ptr %this.addr.i13.i, align 4
+ store ptr %6, ptr %other.addr.i14.i, align 4
+ %this1.i15.i = load ptr, ptr %this.addr.i13.i, align 4
+ %7 = load ptr, ptr %other.addr.i14.i, align 4, !nonnull !3, !align !4
+ %8 = load target("dx.RawBuffer", i8, 1, 0), ptr %7, align 4
+ store target("dx.RawBuffer", i8, 1, 0) %8, ptr %this1.i15.i, align 4
+ call void @llvm.experimental.noalias.scope.decl(metadata !8)
+ store i32 1, ptr %registerNo.addr.i.i.i, align 4, !noalias !8
+ store i32 0, ptr %spaceNo.addr.i.i.i, align 4, !noalias !8
+ store i32 1, ptr %range.addr.i.i.i, align 4, !noalias !8
+ store i32 0, ptr %index.addr.i.i.i, align 4, !noalias !8
+ store ptr @.str.2, ptr %name.addr.i.i.i, align 4, !noalias !8
+ %9 = load i32, ptr %registerNo.addr.i.i.i, align 4, !noalias !8
+ %10 = load i32, ptr %spaceNo.addr.i.i.i, align 4, !noalias !8
+ %11 = load i32, ptr %range.addr.i.i.i, align 4, !noalias !8
+ %12 = load i32, ptr %index.addr.i.i.i, align 4, !noalias !8
+ %13 = load ptr, ptr %name.addr.i.i.i, align 4, !noalias !8
+ %14 = call target("dx.RawBuffer", i8, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_i8_1_0t(i32 %10, i32 %9, i32 %11, i32 %12, ptr %13)
+ store target("dx.RawBuffer", i8, 1, 0) %14, ptr %tmp.i.i.i, align 4, !noalias !8
+ store ptr @_ZL5gBuf1, ptr %this.addr.i7.i, align 4
+ store ptr %tmp.i.i.i, ptr %other.addr.i8.i, align 4
+ %this1.i9.i = load ptr, ptr %this.addr.i7.i, align 4
+ %15 = load ptr, ptr %other.addr.i8.i, align 4
+ store ptr %this1.i9.i, ptr %this.addr.i16.i, align 4
+ store ptr %15, ptr %other.addr.i17.i, align 4
+ %this1.i18.i = load ptr, ptr %this.addr.i16.i, align 4
+ %16 = load ptr, ptr %other.addr.i17.i, align 4, !nonnull !3, !align !4
+ %17 = load target("dx.RawBuffer", i8, 1, 0), ptr %16, align 4
+ store target("dx.RawBuffer", i8, 1, 0) %17, ptr %this1.i18.i, align 4
+ call void @llvm.experimental.noalias.scope.decl(metadata !11)
+ store i32 3, ptr %registerNo.addr.i.i1.i, align 4, !noalias !11
+ store i32 0, ptr %spaceNo.addr.i.i2.i, align 4, !noalias !11
+ store i32 1, ptr %range.addr.i.i3.i, align 4, !noalias !11
+ store i32 0, ptr %index.addr.i.i4.i, align 4, !noalias !11
+ store ptr @.str.4, ptr %name.addr.i.i5.i, align 4, !noalias !11
+ %18 = load i32, ptr %registerNo.addr.i.i1.i, align 4, !noalias !11
+ %19 = load i32, ptr %spaceNo.addr.i.i2.i, align 4, !noalias !11
+ %20 = load i32, ptr %range.addr.i.i3.i, align 4, !noalias !11
+ %21 = load i32, ptr %index.addr.i.i4.i, align 4, !noalias !11
+ %22 = load ptr, ptr %name.addr.i.i5.i, align 4, !noalias !11
+ %23 = call target("dx.RawBuffer", i8, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_i8_1_0t(i32 %19, i32 %18, i32 %20, i32 %21, ptr %22)
+ store target("dx.RawBuffer", i8, 1, 0) %23, ptr %tmp.i.i6.i, align 4, !noalias !11
+ store ptr @_ZL4gOut, ptr %this.addr.i.i4, align 4
+ store ptr %tmp.i.i6.i, ptr %other.addr.i.i5, align 4
+ %this1.i.i6 = load ptr, ptr %this.addr.i.i4, align 4
+ %24 = load ptr, ptr %other.addr.i.i5, align 4
+ store ptr %this1.i.i6, ptr %this.addr.i19.i, align 4
+ store ptr %24, ptr %other.addr.i20.i, align 4
+ %this1.i21.i = load ptr, ptr %this.addr.i19.i, align 4
+ %25 = load ptr, ptr %other.addr.i20.i, align 4, !nonnull !3, !align !4
+ %26 = load target("dx.RawBuffer", i8, 1, 0), ptr %25, align 4
+ store target("dx.RawBuffer", i8, 1, 0) %26, ptr %this1.i21.i, align 4
+ %27 = call i32 @llvm.dx.thread.id(i32 0)
+ %28 = insertelement <3 x i32> poison, i32 %27, i64 0
+ %29 = call i32 @llvm.dx.thread.id(i32 1)
+ %30 = insertelement <3 x i32> %28, i32 %29, i64 1
+ %31 = call i32 @llvm.dx.thread.id(i32 2)
+ %32 = insertelement <3 x i32> %30, i32 %31, i64 2
+ store <3 x i32> %32, ptr %tid.addr.i, align 4
+ %33 = load <3 x i32>, ptr %tid.addr.i, align 4
+ %34 = extractelement <3 x i32> %33, i32 0
+ %35 = load <3 x i32>, ptr %tid.addr.i, align 4
+ %36 = extractelement <3 x i32> %35, i32 1
+ %mul.i = mul i32 %36, 8
+ %add.i = add i32 %34, %mul.i
+ store i32 %add.i, ptr %idx.i, align 4
+ %37 = load i32, ptr %idx.i, align 4
+ store i32 %37, ptr %idx.addr.i.i, align 4
+ store ptr %buf.i.i, ptr %this.addr.i1.i, align 4
+ store ptr @_ZL5gBuf0, ptr %other.addr.i2.i, align 4
+ %this1.i3.i = load ptr, ptr %this.addr.i1.i, align 4
+ %38 = load ptr, ptr %other.addr.i2.i, align 4
+ store ptr %this1.i3.i, ptr %this.addr.i1, align 4
+ store ptr %38, ptr %other.addr.i2, align 4
+ %this1.i3 = load ptr, ptr %this.addr.i1, align 4
+ %39 = load ptr, ptr %other.addr.i2, align 4, !nonnull !3, !align !4
+ %40 = load target("dx.RawBuffer", i8, 1, 0), ptr %39, align 4
+ store target("dx.RawBuffer", i8, 1, 0) %40, ptr %this1.i3, align 4
+ store ptr %agg.tmp.i.i, ptr %this.addr.i.i, align 4
+ store ptr %buf.i.i, ptr %other.addr.i.i, align 4
+ %this1.i.i = load ptr, ptr %this.addr.i.i, align 4
+ %41 = load ptr, ptr %other.addr.i.i, align 4
+ store ptr %this1.i.i, ptr %this.addr.i, align 4
+ store ptr %41, ptr %other.addr.i, align 4
+ %this1.i = load ptr, ptr %this.addr.i, align 4
+ %42 = load ptr, ptr %other.addr.i, align 4, !nonnull !3, !align !4
+ %43 = load target("dx.RawBuffer", i8, 1, 0), ptr %42, align 4
+ store target("dx.RawBuffer", i8, 1, 0) %43, ptr %this1.i, align 4
+ %44 = load i32, ptr %idx.addr.i.i, align 4
+ %mul.i.i = mul i32 %44, 4
+ call void @llvm.memcpy.p0.p0.i64(ptr align 8 %agg.tmp1.i.i, ptr align 4 %agg.tmp.i.i, i64 4, i1 false)
+ store i32 %mul.i.i, ptr %offset.addr.i.i.i, align 4
+ store i32 3, ptr %value.addr.i.i.i, align 4
+ %45 = load i32, ptr %offset.addr.i.i.i, align 4
+ %46 = load i32, ptr %value.addr.i.i.i, align 4
+ store ptr %agg.tmp1.i.i, ptr %this.addr.i.i.i, align 4
+ store i32 %45, ptr %Index.addr.i.i.i, align 4
+ store i32 %46, ptr %Value.addr.i.i.i, align 4
+ %this1.i.i.i = load ptr, ptr %this.addr.i.i.i, align 4
+ %47 = load i32, ptr %Value.addr.i.i.i, align 4
+ %48 = load target("dx.RawBuffer", i8, 1, 0), ptr %this1.i.i.i, align 4
+ %49 = load i32, ptr %Index.addr.i.i.i, align 4
+ %50 = call ptr @llvm.dx.resource.getpointer.p0.tdx.RawBuffer_i8_1_0t.i32(target("dx.RawBuffer", i8, 1, 0) %48, i32 %49)
+ store i32 %47, ptr %50, align 4
+ %51 = load i32, ptr %value.addr.i.i.i, align 4
+ ret void
+}
+
+; Function Attrs: nounwind willreturn memory(none)
+declare i32 @llvm.dx.thread.id(i32) #2
+
+; Function Attrs: nocallback nofree nosync nounwind willreturn memory(none)
+declare target("dx.RawBuffer", i8, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_i8_1_0t(i32, i32, i32, i32, ptr) #3
+
+; Function Attrs: convergent nocallback nofree nosync nounwind willreturn memory(none)
+declare ptr @llvm.dx.resource.getpointer.p0.tdx.RawBuffer_i8_1_0t.i32(target("dx.RawBuffer", i8, 1, 0), i32) #4
+
+; Function Attrs: nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: readwrite)
+declare void @llvm.experimental.noalias.scope.decl(metadata) #5
+
+; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
+declare void @llvm.memcpy.p0.p0.i64(ptr noalias writeonly captures(none), ptr noalias readonly captures(none), i64, i1 immarg) #6
+
+attributes #0 = { alwaysinline convergent mustprogress norecurse nounwind "no-signed-zeros-fp-math"="true" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
+attributes #1 = { convergent noinline norecurse "hlsl.numthreads"="8,8,1" "hlsl.shader"="compute" "no-signed-zeros-fp-math"="true" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
+attributes #2 = { nounwind willreturn memory(none) }
+attributes #3 = { nocallback nofree nosync nounwind willreturn memory(none) }
+attributes #4 = { convergent nocallback nofree nosync nounwind willreturn memory(none) }
+attributes #5 = { nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: readwrite) }
+attributes #6 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
+
+!dx.valver = !{!0}
+!llvm.module.flags = !{!1}
+!llvm.ident = !{!2}
+
+!0 = !{i32 1, i32 8}
+!1 = !{i32 4, !"dx.disable_optimizations", i32 1}
+!2 = !{!"clang version 23.0.0git (https://github.com/llvm/llvm-project.git b084fa7a5172e2502324f9df2723e22dad071e3a)"}
+!3 = !{}
+!4 = !{i64 4}
+!5 = !{!6}
+!6 = distinct !{!6, !7, !"_ZN4hlsl19RWByteAddressBuffer19__createFromBindingEjjijPKc: %agg.result"}
+!7 = distinct !{!7, !"_ZN4hlsl19RWByteAddressBuffer19__createFromBindingEjjijPKc"}
+!8 = !{!9}
+!9 = distinct !{!9, !10, !"_ZN4hlsl19RWByteAddressBuffer19__createFromBindingEjjijPKc: %agg.result"}
+!10 = distinct !{!10, !"_ZN4hlsl19RWByteAddressBuffer19__createFromBindingEjjijPKc"}
+!11 = !{!12}
+!12 = distinct !{!12, !13, !"_ZN4hlsl19RWByteAddressBuffer19__createFromBindingEjjijPKc: %agg.result"}
+!13 = distinct !{!13, !"_ZN4hlsl19RWByteAddressBuffer19__createFromBindingEjjijPKc"}
diff --git a/clang/test/SemaHLSL/Resources/Local-Resources/loop_var.hlsl b/clang/test/SemaHLSL/Resources/Local-Resources/loop_var.hlsl
new file mode 100644
index 0000000000000..71d1a1b4302b0
--- /dev/null
+++ b/clang/test/SemaHLSL/Resources/Local-Resources/loop_var.hlsl
@@ -0,0 +1,23 @@
+// RUN: %clang_cc1 -std=hlsl202x -finclude-default-header -triple \
+// RUN: dxil-pc-shadermodel6.6-compute %s -emit-llvm -verify
+
+// expected-no-diagnostics
+
+RWByteAddressBuffer gBuf0 : register(u0);
+
+RWByteAddressBuffer gOut : register(u3);
+
+void Pass_LoopVar()
+{
+ for(RWByteAddressBuffer buf = gBuf0; false == false; )
+ {
+ buf.Store(0, 0);
+ break;
+ }
+}
+
+[numthreads(8,8,1)]
+void main(uint3 tid : SV_DispatchThreadID)
+{
+ Pass_LoopVar();
+}
\ No newline at end of file
diff --git a/clang/test/SemaHLSL/Resources/Local-Resources/use_groupshared.hlsl b/clang/test/SemaHLSL/Resources/Local-Resources/use_groupshared.hlsl
index 16747acbc19ff..1c6d0f9e35f0a 100644
--- a/clang/test/SemaHLSL/Resources/Local-Resources/use_groupshared.hlsl
+++ b/clang/test/SemaHLSL/Resources/Local-Resources/use_groupshared.hlsl
@@ -1,17 +1,25 @@
// RUN: %clang_cc1 -std=hlsl202x -finclude-default-header -triple \
// RUN: dxil-pc-shadermodel6.6-compute %s -emit-llvm -verify
+// This test fails validation in DXC, but DXC's sema allows it.
+// Meanwhile, groupshared is immediately detected and rejected in clang's sema.
+
RWByteAddressBuffer gOut : register(u3);
+// expected-note at +1{{passing argument to parameter 'buf' here}}
uint DoStore(RWByteAddressBuffer buf, uint offset, uint value)
{
buf.Store(offset, value);
return value;
}
+// expected-note@*:*{{candidate constructor not viable: cannot bind reference in address space 'groupshared' to object in generic address space in 1st argument}}
+// expected-note@*:*{{candidate constructor not viable: requires 0 arguments, but 1 was provided}}
+
groupshared RWByteAddressBuffer sharedBuf;
uint Use_Shared(uint idx)
{
+ // expected-error at +1{{no matching constructor for initialization of 'RWByteAddressBuffer'}}
return DoStore(sharedBuf, idx * 4, 1);
}
@@ -19,5 +27,5 @@ uint Use_Shared(uint idx)
void main(uint3 tid : SV_DispatchThreadID)
{
uint idx = tid.x + tid.y * 8;
- Use_Shared(idx);
-}
\ No newline at end of file
+ Use_Shared(idx);
+}
diff --git a/clang/test/SemaHLSL/Resources/Local-Resources/use_struct_groupshared.hlsl b/clang/test/SemaHLSL/Resources/Local-Resources/use_struct_groupshared.hlsl
new file mode 100644
index 0000000000000..3f47ae6db82c6
--- /dev/null
+++ b/clang/test/SemaHLSL/Resources/Local-Resources/use_struct_groupshared.hlsl
@@ -0,0 +1,37 @@
+// RUN: %clang_cc1 -std=hlsl202x -finclude-default-header -triple \
+// RUN: dxil-pc-shadermodel6.6-compute %s -emit-llvm -verify
+
+// This test fails validation in DXC, but DXC's sema allows it.
+// Meanwhile, it is for some reason accepted by clang's sema.
+// TODO: Why does this pass clang's sema, but use_groupshared.hlsl fails clang's sema?
+// Run a git bisect on https://github.com/llvm/llvm-project/issues/158107, and figure
+// out which commit seemed to resolve this issue.
+
+RWByteAddressBuffer gOut : register(u3);
+
+// expected-note at +1{{passing argument to parameter 'buf' here}}
+uint DoStore(RWByteAddressBuffer buf, uint offset, uint value)
+{
+ buf.Store(offset, value);
+ return value;
+}
+
+// expected-note@*:*{{candidate constructor not viable: cannot bind reference in address space 'groupshared' to object in generic address space in 1st argument}}
+// expected-note@*:*{{candidate constructor not viable: requires 0 arguments, but 1 was provided}}
+
+struct PassBufStruct { RWByteAddressBuffer buf; };
+
+groupshared PassBufStruct sharedStruct;
+
+uint Use_PassSharedStruct(uint idx)
+{
+ // expected-error at +1{{no matching constructor for initialization of 'RWByteAddressBuffer'}}
+ return DoStore(sharedStruct.buf, idx * 4, 1);
+}
+
+[numthreads(8,8,1)]
+void main(uint3 tid : SV_DispatchThreadID)
+{
+ uint idx = tid.x + tid.y * 8;
+ Use_PassSharedStruct(idx);
+}
diff --git a/clang/test/SemaHLSL/Resources/Local-Resources/use_struct_groupshared.ll b/clang/test/SemaHLSL/Resources/Local-Resources/use_struct_groupshared.ll
new file mode 100644
index 0000000000000..b84ac5379905c
--- /dev/null
+++ b/clang/test/SemaHLSL/Resources/Local-Resources/use_struct_groupshared.ll
@@ -0,0 +1,255 @@
+; ModuleID = 'D:\llvm-project\clang\test\SemaHLSL\Resources\Local-Resources\use_struct_groupshared.hlsl'
+source_filename = "D:\\llvm-project\\clang\\test\\SemaHLSL\\Resources\\Local-Resources\\use_struct_groupshared.hlsl"
+target datalayout = "e-m:e-ve-p:32:32-i1:32-i8:8-i16:16-i32:32-i64:64-f16:16-f32:32-f64:64-n8:16:32:64"
+target triple = "dxilv1.6-pc-shadermodel6.6-compute"
+
+%"class.hlsl::RWByteAddressBuffer" = type { target("dx.RawBuffer", i8, 1, 0) }
+%struct.PassBufStruct = type { %"class.hlsl::RWByteAddressBuffer" }
+
+ at _ZL4gOut = internal global %"class.hlsl::RWByteAddressBuffer" poison, align 4
+ at .str = private unnamed_addr constant [5 x i8] c"gOut\00", align 1
+ at _ZL16sharedStruct.buf = internal global %"class.hlsl::RWByteAddressBuffer" poison, align 4
+ at .str.2 = private unnamed_addr constant [17 x i8] c"sharedStruct.buf\00", align 1
+ at sharedStruct = external hidden addrspace(3) global %struct.PassBufStruct, align 4
+
+; Function Attrs: alwaysinline convergent mustprogress norecurse nounwind
+define hidden noundef i32 @_Z7DoStoreN4hlsl19RWByteAddressBufferEjj(ptr noundef byval(%"class.hlsl::RWByteAddressBuffer") align 4 %buf, i32 noundef %offset, i32 noundef %value) #0 {
+entry:
+ %this.addr.i = alloca ptr, align 4
+ %Index.addr.i = alloca i32, align 4
+ %Value.addr.i = alloca i32, align 4
+ %offset.addr = alloca i32, align 4
+ %value.addr = alloca i32, align 4
+ store i32 %offset, ptr %offset.addr, align 4
+ store i32 %value, ptr %value.addr, align 4
+ %0 = load i32, ptr %offset.addr, align 4
+ %1 = load i32, ptr %value.addr, align 4
+ store ptr %buf, ptr %this.addr.i, align 4
+ store i32 %0, ptr %Index.addr.i, align 4
+ store i32 %1, ptr %Value.addr.i, align 4
+ %this1.i = load ptr, ptr %this.addr.i, align 4
+ %2 = load i32, ptr %Value.addr.i, align 4
+ %3 = load target("dx.RawBuffer", i8, 1, 0), ptr %this1.i, align 4
+ %4 = load i32, ptr %Index.addr.i, align 4
+ %5 = call ptr @llvm.dx.resource.getpointer.p0.tdx.RawBuffer_i8_1_0t.i32(target("dx.RawBuffer", i8, 1, 0) %3, i32 %4)
+ store i32 %2, ptr %5, align 4
+ %6 = load i32, ptr %value.addr, align 4
+ ret i32 %6
+}
+
+; Function Attrs: alwaysinline convergent mustprogress norecurse nounwind
+define hidden noundef i32 @_Z20Use_PassSharedStructj(i32 noundef %idx) #0 {
+entry:
+ %this.addr.i4 = alloca ptr, align 4
+ %other.addr.i5 = alloca ptr, align 4
+ %this.addr.i2 = alloca ptr, align 4
+ %other.addr.i = alloca ptr, align 4
+ %this.addr.i = alloca ptr, align 4
+ %Index.addr.i = alloca i32, align 4
+ %Value.addr.i = alloca i32, align 4
+ %offset.addr.i = alloca i32, align 4
+ %value.addr.i = alloca i32, align 4
+ %agg.tmp1 = alloca %"class.hlsl::RWByteAddressBuffer", align 8
+ %idx.addr = alloca i32, align 4
+ %agg.tmp = alloca %"class.hlsl::RWByteAddressBuffer", align 4
+ store i32 %idx, ptr %idx.addr, align 4
+ store ptr %agg.tmp, ptr %this.addr.i2, align 4
+ store ptr addrspacecast (ptr addrspace(3) @sharedStruct to ptr), ptr %other.addr.i, align 4
+ %this1.i3 = load ptr, ptr %this.addr.i2, align 4
+ %0 = load ptr, ptr %other.addr.i, align 4
+ store ptr %this1.i3, ptr %this.addr.i4, align 4
+ store ptr %0, ptr %other.addr.i5, align 4
+ %this1.i6 = load ptr, ptr %this.addr.i4, align 4
+ %1 = load ptr, ptr %other.addr.i5, align 4, !nonnull !3, !align !4
+ %2 = load target("dx.RawBuffer", i8, 1, 0), ptr %1, align 4
+ store target("dx.RawBuffer", i8, 1, 0) %2, ptr %this1.i6, align 4
+ %3 = load i32, ptr %idx.addr, align 4
+ %mul = mul i32 %3, 4
+ call void @llvm.memcpy.p0.p0.i64(ptr align 8 %agg.tmp1, ptr align 4 %agg.tmp, i64 4, i1 false)
+ store i32 %mul, ptr %offset.addr.i, align 4
+ store i32 1, ptr %value.addr.i, align 4
+ %4 = load i32, ptr %offset.addr.i, align 4
+ %5 = load i32, ptr %value.addr.i, align 4
+ store ptr %agg.tmp1, ptr %this.addr.i, align 4
+ store i32 %4, ptr %Index.addr.i, align 4
+ store i32 %5, ptr %Value.addr.i, align 4
+ %this1.i = load ptr, ptr %this.addr.i, align 4
+ %6 = load i32, ptr %Value.addr.i, align 4
+ %7 = load target("dx.RawBuffer", i8, 1, 0), ptr %this1.i, align 4
+ %8 = load i32, ptr %Index.addr.i, align 4
+ %9 = call ptr @llvm.dx.resource.getpointer.p0.tdx.RawBuffer_i8_1_0t.i32(target("dx.RawBuffer", i8, 1, 0) %7, i32 %8)
+ store i32 %6, ptr %9, align 4
+ %10 = load i32, ptr %value.addr.i, align 4
+ ret i32 %10
+}
+
+; Function Attrs: convergent noinline norecurse
+define void @main() #1 {
+entry:
+ %this.addr.i12.i = alloca ptr, align 4
+ %other.addr.i13.i = alloca ptr, align 4
+ %this.addr.i9.i = alloca ptr, align 4
+ %other.addr.i10.i = alloca ptr, align 4
+ %this.addr.i6.i = alloca ptr, align 4
+ %other.addr.i7.i = alloca ptr, align 4
+ %this.addr.i.i1 = alloca ptr, align 4
+ %other.addr.i.i2 = alloca ptr, align 4
+ %orderId.addr.i.i = alloca i32, align 4
+ %spaceNo.addr.i1.i = alloca i32, align 4
+ %range.addr.i2.i = alloca i32, align 4
+ %index.addr.i3.i = alloca i32, align 4
+ %name.addr.i4.i = alloca ptr, align 4
+ %tmp.i5.i = alloca %"class.hlsl::RWByteAddressBuffer", align 4
+ %registerNo.addr.i.i = alloca i32, align 4
+ %spaceNo.addr.i.i = alloca i32, align 4
+ %range.addr.i.i = alloca i32, align 4
+ %index.addr.i.i = alloca i32, align 4
+ %name.addr.i.i = alloca ptr, align 4
+ %tmp.i.i = alloca %"class.hlsl::RWByteAddressBuffer", align 4
+ %this.addr.i = alloca ptr, align 4
+ %other.addr.i = alloca ptr, align 4
+ %this.addr.i.i = alloca ptr, align 4
+ %other.addr.i.i = alloca ptr, align 4
+ %this.addr.i.i.i = alloca ptr, align 4
+ %Index.addr.i.i.i = alloca i32, align 4
+ %Value.addr.i.i.i = alloca i32, align 4
+ %offset.addr.i.i.i = alloca i32, align 4
+ %value.addr.i.i.i = alloca i32, align 4
+ %agg.tmp1.i.i = alloca %"class.hlsl::RWByteAddressBuffer", align 8
+ %idx.addr.i.i = alloca i32, align 4
+ %agg.tmp.i.i = alloca %"class.hlsl::RWByteAddressBuffer", align 4
+ %tid.addr.i = alloca <3 x i32>, align 4
+ %idx.i = alloca i32, align 4
+ call void @llvm.experimental.noalias.scope.decl(metadata !5)
+ store i32 3, ptr %registerNo.addr.i.i, align 4, !noalias !5
+ store i32 0, ptr %spaceNo.addr.i.i, align 4, !noalias !5
+ store i32 1, ptr %range.addr.i.i, align 4, !noalias !5
+ store i32 0, ptr %index.addr.i.i, align 4, !noalias !5
+ store ptr @.str, ptr %name.addr.i.i, align 4, !noalias !5
+ %0 = load i32, ptr %registerNo.addr.i.i, align 4, !noalias !5
+ %1 = load i32, ptr %spaceNo.addr.i.i, align 4, !noalias !5
+ %2 = load i32, ptr %range.addr.i.i, align 4, !noalias !5
+ %3 = load i32, ptr %index.addr.i.i, align 4, !noalias !5
+ %4 = load ptr, ptr %name.addr.i.i, align 4, !noalias !5
+ %5 = call target("dx.RawBuffer", i8, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_i8_1_0t(i32 %1, i32 %0, i32 %2, i32 %3, ptr %4)
+ store target("dx.RawBuffer", i8, 1, 0) %5, ptr %tmp.i.i, align 4, !noalias !5
+ store ptr @_ZL4gOut, ptr %this.addr.i6.i, align 4
+ store ptr %tmp.i.i, ptr %other.addr.i7.i, align 4
+ %this1.i8.i = load ptr, ptr %this.addr.i6.i, align 4
+ %6 = load ptr, ptr %other.addr.i7.i, align 4
+ store ptr %this1.i8.i, ptr %this.addr.i9.i, align 4
+ store ptr %6, ptr %other.addr.i10.i, align 4
+ %this1.i11.i = load ptr, ptr %this.addr.i9.i, align 4
+ %7 = load ptr, ptr %other.addr.i10.i, align 4, !nonnull !3, !align !4
+ %8 = load target("dx.RawBuffer", i8, 1, 0), ptr %7, align 4
+ store target("dx.RawBuffer", i8, 1, 0) %8, ptr %this1.i11.i, align 4
+ call void @llvm.experimental.noalias.scope.decl(metadata !8)
+ store i32 0, ptr %orderId.addr.i.i, align 4, !noalias !8
+ store i32 0, ptr %spaceNo.addr.i1.i, align 4, !noalias !8
+ store i32 1, ptr %range.addr.i2.i, align 4, !noalias !8
+ store i32 0, ptr %index.addr.i3.i, align 4, !noalias !8
+ store ptr @.str.2, ptr %name.addr.i4.i, align 4, !noalias !8
+ %9 = load i32, ptr %orderId.addr.i.i, align 4, !noalias !8
+ %10 = load i32, ptr %spaceNo.addr.i1.i, align 4, !noalias !8
+ %11 = load i32, ptr %range.addr.i2.i, align 4, !noalias !8
+ %12 = load i32, ptr %index.addr.i3.i, align 4, !noalias !8
+ %13 = load ptr, ptr %name.addr.i4.i, align 4, !noalias !8
+ %14 = call target("dx.RawBuffer", i8, 1, 0) @llvm.dx.resource.handlefromimplicitbinding.tdx.RawBuffer_i8_1_0t(i32 %9, i32 %10, i32 %11, i32 %12, ptr %13)
+ store target("dx.RawBuffer", i8, 1, 0) %14, ptr %tmp.i5.i, align 4, !noalias !8
+ store ptr @_ZL16sharedStruct.buf, ptr %this.addr.i.i1, align 4
+ store ptr %tmp.i5.i, ptr %other.addr.i.i2, align 4
+ %this1.i.i3 = load ptr, ptr %this.addr.i.i1, align 4
+ %15 = load ptr, ptr %other.addr.i.i2, align 4
+ store ptr %this1.i.i3, ptr %this.addr.i12.i, align 4
+ store ptr %15, ptr %other.addr.i13.i, align 4
+ %this1.i14.i = load ptr, ptr %this.addr.i12.i, align 4
+ %16 = load ptr, ptr %other.addr.i13.i, align 4, !nonnull !3, !align !4
+ %17 = load target("dx.RawBuffer", i8, 1, 0), ptr %16, align 4
+ store target("dx.RawBuffer", i8, 1, 0) %17, ptr %this1.i14.i, align 4
+ %18 = call i32 @llvm.dx.thread.id(i32 0)
+ %19 = insertelement <3 x i32> poison, i32 %18, i64 0
+ %20 = call i32 @llvm.dx.thread.id(i32 1)
+ %21 = insertelement <3 x i32> %19, i32 %20, i64 1
+ %22 = call i32 @llvm.dx.thread.id(i32 2)
+ %23 = insertelement <3 x i32> %21, i32 %22, i64 2
+ store <3 x i32> %23, ptr %tid.addr.i, align 4
+ %24 = load <3 x i32>, ptr %tid.addr.i, align 4
+ %25 = extractelement <3 x i32> %24, i32 0
+ %26 = load <3 x i32>, ptr %tid.addr.i, align 4
+ %27 = extractelement <3 x i32> %26, i32 1
+ %mul.i = mul i32 %27, 8
+ %add.i = add i32 %25, %mul.i
+ store i32 %add.i, ptr %idx.i, align 4
+ %28 = load i32, ptr %idx.i, align 4
+ store i32 %28, ptr %idx.addr.i.i, align 4
+ store ptr %agg.tmp.i.i, ptr %this.addr.i.i, align 4
+ store ptr addrspacecast (ptr addrspace(3) @sharedStruct to ptr), ptr %other.addr.i.i, align 4
+ %this1.i.i = load ptr, ptr %this.addr.i.i, align 4
+ %29 = load ptr, ptr %other.addr.i.i, align 4
+ store ptr %this1.i.i, ptr %this.addr.i, align 4
+ store ptr %29, ptr %other.addr.i, align 4
+ %this1.i = load ptr, ptr %this.addr.i, align 4
+ %30 = load ptr, ptr %other.addr.i, align 4, !nonnull !3, !align !4
+ %31 = load target("dx.RawBuffer", i8, 1, 0), ptr %30, align 4
+ store target("dx.RawBuffer", i8, 1, 0) %31, ptr %this1.i, align 4
+ %32 = load i32, ptr %idx.addr.i.i, align 4
+ %mul.i.i = mul i32 %32, 4
+ call void @llvm.memcpy.p0.p0.i64(ptr align 8 %agg.tmp1.i.i, ptr align 4 %agg.tmp.i.i, i64 4, i1 false)
+ store i32 %mul.i.i, ptr %offset.addr.i.i.i, align 4
+ store i32 1, ptr %value.addr.i.i.i, align 4
+ %33 = load i32, ptr %offset.addr.i.i.i, align 4
+ %34 = load i32, ptr %value.addr.i.i.i, align 4
+ store ptr %agg.tmp1.i.i, ptr %this.addr.i.i.i, align 4
+ store i32 %33, ptr %Index.addr.i.i.i, align 4
+ store i32 %34, ptr %Value.addr.i.i.i, align 4
+ %this1.i.i.i = load ptr, ptr %this.addr.i.i.i, align 4
+ %35 = load i32, ptr %Value.addr.i.i.i, align 4
+ %36 = load target("dx.RawBuffer", i8, 1, 0), ptr %this1.i.i.i, align 4
+ %37 = load i32, ptr %Index.addr.i.i.i, align 4
+ %38 = call ptr @llvm.dx.resource.getpointer.p0.tdx.RawBuffer_i8_1_0t.i32(target("dx.RawBuffer", i8, 1, 0) %36, i32 %37)
+ store i32 %35, ptr %38, align 4
+ %39 = load i32, ptr %value.addr.i.i.i, align 4
+ ret void
+}
+
+; Function Attrs: nounwind willreturn memory(none)
+declare i32 @llvm.dx.thread.id(i32) #2
+
+; Function Attrs: nocallback nofree nosync nounwind willreturn memory(none)
+declare target("dx.RawBuffer", i8, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_i8_1_0t(i32, i32, i32, i32, ptr) #3
+
+; Function Attrs: convergent nocallback nofree nosync nounwind willreturn memory(none)
+declare ptr @llvm.dx.resource.getpointer.p0.tdx.RawBuffer_i8_1_0t.i32(target("dx.RawBuffer", i8, 1, 0), i32) #4
+
+; Function Attrs: nocallback nofree nosync nounwind willreturn memory(none)
+declare target("dx.RawBuffer", i8, 1, 0) @llvm.dx.resource.handlefromimplicitbinding.tdx.RawBuffer_i8_1_0t(i32, i32, i32, i32, ptr) #3
+
+; Function Attrs: nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: readwrite)
+declare void @llvm.experimental.noalias.scope.decl(metadata) #5
+
+; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
+declare void @llvm.memcpy.p0.p0.i64(ptr noalias writeonly captures(none), ptr noalias readonly captures(none), i64, i1 immarg) #6
+
+attributes #0 = { alwaysinline convergent mustprogress norecurse nounwind "no-signed-zeros-fp-math"="true" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
+attributes #1 = { convergent noinline norecurse "hlsl.numthreads"="8,8,1" "hlsl.shader"="compute" "no-signed-zeros-fp-math"="true" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
+attributes #2 = { nounwind willreturn memory(none) }
+attributes #3 = { nocallback nofree nosync nounwind willreturn memory(none) }
+attributes #4 = { convergent nocallback nofree nosync nounwind willreturn memory(none) }
+attributes #5 = { nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: readwrite) }
+attributes #6 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
+
+!dx.valver = !{!0}
+!llvm.module.flags = !{!1}
+!llvm.ident = !{!2}
+
+!0 = !{i32 1, i32 8}
+!1 = !{i32 4, !"dx.disable_optimizations", i32 1}
+!2 = !{!"clang version 23.0.0git (https://github.com/llvm/llvm-project.git b084fa7a5172e2502324f9df2723e22dad071e3a)"}
+!3 = !{}
+!4 = !{i64 4}
+!5 = !{!6}
+!6 = distinct !{!6, !7, !"_ZN4hlsl19RWByteAddressBuffer19__createFromBindingEjjijPKc: %agg.result"}
+!7 = distinct !{!7, !"_ZN4hlsl19RWByteAddressBuffer19__createFromBindingEjjijPKc"}
+!8 = !{!9}
+!9 = distinct !{!9, !10, !"_ZN4hlsl19RWByteAddressBuffer27__createFromImplicitBindingEjjijPKc: %agg.result"}
+!10 = distinct !{!10, !"_ZN4hlsl19RWByteAddressBuffer27__createFromImplicitBindingEjjijPKc"}
diff --git a/clang/test/SemaHLSL/Resources/local_resources_passes.hlsl b/clang/test/SemaHLSL/Resources/local_resources_passes.hlsl
index b44e9ff8896dc..d97a03580242c 100644
--- a/clang/test/SemaHLSL/Resources/local_resources_passes.hlsl
+++ b/clang/test/SemaHLSL/Resources/local_resources_passes.hlsl
@@ -29,37 +29,6 @@ uint DoStore(RWByteAddressBuffer buf, uint offset, uint value)
// PASS TESTS
//==============================================================
-// PASS 0
-groupshared RWByteAddressBuffer sharedBuf;
-
-uint Use_Shared(uint idx)
-{
- return DoStore(gBuf0, idx * 4, 1);
-}
-
-// PASS 1
-uint Pass_TernaryInit(bool cond, uint idx)
-{
- // expected-warning at +1{{assignment of 'cond ? gBuf0 : gBuf1' to local resource 'buf' is not to the same unique global resource}}
- RWByteAddressBuffer buf = cond ? gBuf0 : gBuf1;
- return DoStore(buf, idx * 4, 2);
-}
-
-// PASS 2
-void Pass_LoopVar()
-{
- for(RWByteAddressBuffer buf = gBuf0; false;)
- {
- }
-}
-
-// PASS 3
-uint Pass_ExpressionInit(uint idx)
-{
- RWByteAddressBuffer buf = (true ? gBuf0 : gBuf1);
- return DoStore(buf, idx * 4, 3);
-}
-
// PASS 4
struct PassBufStruct { RWByteAddressBuffer buf; };
More information about the cfe-commits
mailing list