Skip to content

Commit 3a4b7f2

Browse files
committed
Tests for local resource arrays
1 parent f0d05cd commit 3a4b7f2

File tree

3 files changed

+156
-0
lines changed

3 files changed

+156
-0
lines changed
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-compute -finclude-default-header \
2+
// RUN: -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s
3+
4+
// This test verifies local arrays of resources in HLSL.
5+
6+
// CHECK: @_ZL1A = internal global %"class.hlsl::RWBuffer" poison, align 4
7+
// CHECK: @_ZL1B = internal global %"class.hlsl::RWBuffer" poison, align 4
8+
// CHECK: @_ZL1C = internal global %"class.hlsl::RWBuffer" poison, align 4
9+
10+
RWBuffer<float> A : register(u1);
11+
RWBuffer<float> B : register(u2);
12+
RWBuffer<float> C : register(u3);
13+
RWStructuredBuffer<float> Out : register(u0);
14+
15+
// CHECK: define internal void @_Z4mainv()
16+
// CHECK-NEXT: entry:
17+
[numthreads(4,1,1)]
18+
void main() {
19+
// CHECK-NEXT: %First = alloca [3 x %"class.hlsl::RWBuffer"], align 4
20+
// CHECK-NEXT: %Second = alloca [4 x %"class.hlsl::RWBuffer"], align 4
21+
RWBuffer<float> First[3] = { A, B, C };
22+
RWBuffer<float> Second[4];
23+
24+
// Verify initialization of First array from an initialization list
25+
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 %First, ptr align 4 @_ZL1A, i32 4, i1 false)
26+
// CHECK-NEXT: %[[Ptr1:.*]] = getelementptr inbounds %"class.hlsl::RWBuffer", ptr %First, i32 1
27+
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 %[[Ptr1]], ptr align 4 @_ZL1B, i32 4, i1 false)
28+
// CHECK-NEXT: %[[Ptr2:.*]] = getelementptr inbounds %"class.hlsl::RWBuffer", ptr %First, i32 2
29+
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 %[[Ptr2]], ptr align 4 @_ZL1C, i32 4, i1 false)
30+
31+
// Verify default initialization of Second array, which means there is a loop iterating
32+
// over the array elements and calling the default constructor for each
33+
// CHECK-NEXT: %[[ArrayBeginPtr:.*]] = getelementptr inbounds [4 x %"class.hlsl::RWBuffer"], ptr %Second, i32 0, i32 0
34+
// CHECK-NEXT: %[[ArrayEndPtr:.*]] = getelementptr inbounds %"class.hlsl::RWBuffer", ptr %[[ArrayBeginPtr]], i32 4
35+
// CHECK-NEXT: br label %[[ArrayInitLoop:.*]]
36+
// CHECK: [[ArrayInitLoop]]:
37+
// CHECK-NEXT: %[[ArrayCurPtr:.*]] = phi ptr [ %[[ArrayBeginPtr]], %entry ], [ %[[ArrayNextPtr:.*]], %[[ArrayInitLoop]] ]
38+
// CHECK-NEXT: call void @_ZN4hlsl8RWBufferIfEC1Ev(ptr {{.*}} %[[ArrayCurPtr]])
39+
// CHECK-NEXT: %[[ArrayNextPtr]] = getelementptr inbounds %"class.hlsl::RWBuffer", ptr %[[ArrayCurPtr]], i32 1
40+
// CHECK-NEXT: %[[ArrayInitDone:.*]] = icmp eq ptr %[[ArrayNextPtr]], %[[ArrayEndPtr]]
41+
// CHECK-NEXT: br i1 %[[ArrayInitDone]], label %[[AfterArrayInit:.*]], label %[[ArrayInitLoop]]
42+
// CHECK: [[AfterArrayInit]]:
43+
44+
// Initialize First[2] with C
45+
// CHECK: %[[Ptr3:.*]] = getelementptr inbounds [4 x %"class.hlsl::RWBuffer"], ptr %Second, i32 0, i32 2
46+
// CHECK: call void @llvm.memcpy.p0.p0.i32(ptr align 4 %[[Ptr3]], ptr align 4 @_ZL1C, i32 4, i1 false)
47+
Second[2] = C;
48+
49+
// get First[1][0] value
50+
// CHECK: %[[First_1_Ptr:.*]] = getelementptr inbounds [3 x %"class.hlsl::RWBuffer"], ptr %First, i32 0, i32 1
51+
// CHECK: %[[BufPtr1:.*]] = call {{.*}} ptr @_ZN4hlsl8RWBufferIfEixEj(ptr {{.*}} %[[First_1_Ptr]], i32 noundef 0)
52+
// CHECK: %[[Value1:.*]] = load float, ptr %[[BufPtr1]], align 4
53+
54+
// get Second[2][0] value
55+
// CHECK: %[[Second_2_Ptr:.*]] = getelementptr inbounds [4 x %"class.hlsl::RWBuffer"], ptr %Second, i32 0, i32 2
56+
// CHECK: %[[BufPtr2:.*]] = call {{.*}} ptr @_ZN4hlsl8RWBufferIfEixEj(ptr {{.*}} %[[Second_2_Ptr]], i32 noundef 0)
57+
// CHECK: %[[Value2:.*]] = load float, ptr %[[BufPtr2]], align 4
58+
59+
// add them
60+
// CHECK: %{{.*}} = fadd {{.*}} float %[[Value1]], %[[Value2]]
61+
Out[0] = First[1][0] + Second[2][0];
62+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-compute -finclude-default-header \
2+
// RUN: -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s
3+
4+
// This test verifies handling of local arrays of resources when used as a function argument.
5+
6+
// CHECK: @_ZL1A = internal global [3 x %"class.hlsl::RWBuffer"] poison, align 4
7+
8+
RWBuffer<float> A[3] : register(u0);
9+
RWStructuredBuffer<float> Out : register(u0);
10+
11+
// CHECK: define {{.*}} float @_Z3fooA3_N4hlsl8RWBufferIfEE(ptr noundef byval([3 x %"class.hlsl::RWBuffer"]) align 4 %LocalA)
12+
// CHECK-NEXT: entry:
13+
float foo(RWBuffer<float> LocalA[3]) {
14+
// CHECK-NEXT: %[[LocalA_2_Ptr:.*]] = getelementptr inbounds [3 x %"class.hlsl::RWBuffer"], ptr %LocalA, i32 0, i32 2
15+
// CHECK-NEXT: %[[BufPtr:.*]] = call {{.*}} ptr @_ZN4hlsl8RWBufferIfEixEj(ptr {{.*}} %[[LocalA_2_Ptr]], i32 noundef 0)
16+
// CHECK-NEXT: %[[Value:.*]] = load float, ptr %[[BufPtr]], align 4
17+
// CHECK-NEXT: ret float %[[Value]]
18+
return LocalA[2][0];
19+
}
20+
21+
// CHECK: define internal void @_Z4mainv()
22+
// CHECK-NEXT: entry:
23+
[numthreads(4,1,1)]
24+
void main() {
25+
// Check that the `main` function calls `foo` with a local copy of the array
26+
// CHECK-NEXT: %[[Tmp:.*]] = alloca [3 x %"class.hlsl::RWBuffer"], align 4
27+
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 %[[Tmp]], ptr align 4 @_ZL1A, i32 12, i1 false)
28+
29+
// CHECK-NEXT: %[[ReturnedValue:.*]] = call {{.*}} float @_Z3fooA3_N4hlsl8RWBufferIfEE(ptr noundef byval([3 x %"class.hlsl::RWBuffer"]) align 4 %[[Tmp]])
30+
// CHECK-NEXT: %[[OutBufPtr:.*]] = call {{.*}} ptr @_ZN4hlsl18RWStructuredBufferIfEixEj(ptr {{.*}} @_ZL3Out, i32 noundef 0)
31+
// CHECK-NEXT: store float %[[ReturnedValue]], ptr %[[OutBufPtr]], align 4
32+
// CHECK-NEXT: ret void
33+
Out[0] = foo(A);
34+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-compute -finclude-default-header \
2+
// RUN: -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s
3+
4+
// This test verifies handling of local arrays of resources when used
5+
// as a function argument that is modified inside the function.
6+
7+
// CHECK: @_ZL1X = internal global %"class.hlsl::RWBuffer" poison, align 4
8+
// CHECK: @_ZL1Y = internal global %"class.hlsl::RWBuffer" poison, align 4
9+
10+
RWBuffer<int> X : register(u0);
11+
RWBuffer<int> Y : register(u1);
12+
13+
// CHECK: define {{.*}} @_Z6SomeFnA2_N4hlsl8RWBufferIiEEji(
14+
// CHECK-SAME: ptr noundef byval([2 x %"class.hlsl::RWBuffer"]) align 4 %B, i32 noundef %Idx, i32 noundef %Val0)
15+
// CHECK-NEXT: entry:
16+
// CHECK-NEXT: %[[Idx_addr:.*]] = alloca i32, align 4
17+
// CHECK-NEXT: %[[Val0_addr:.*]] = alloca i32, align 4
18+
// CHECK-NEXT: store i32 %Idx, ptr %[[Idx_addr]], align 4
19+
// CHECK-NEXT: store i32 %Val0, ptr %[[Val0_addr]], align 4
20+
void SomeFn(RWBuffer<int> B[2], uint Idx, int Val0) {
21+
22+
// CHECK-NEXT: %[[B_0_Ptr:.*]] = getelementptr inbounds [2 x %"class.hlsl::RWBuffer"], ptr %B, i32 0, i32 0
23+
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 %[[B_0_Ptr]], ptr align 4 @_ZL1Y, i32 4, i1 false)
24+
B[0] = Y;
25+
26+
// CHECK-NEXT: %[[Val0:.*]] = load i32, ptr %[[Val0_addr]], align 4
27+
// CHECK-NEXT: %[[B_0_Ptr:.*]] = getelementptr inbounds [2 x %"class.hlsl::RWBuffer"], ptr %B, i32 0, i32 0
28+
// CHECK-NEXT: %[[Idx:.*]] = load i32, ptr %[[Idx_addr]], align 4
29+
// CHECK-NEXT: %[[BufPtr:.*]] = call {{.*}} ptr @_ZN4hlsl8RWBufferIiEixEj(ptr {{.*}} %[[B_0_Ptr]], i32 noundef %[[Idx]])
30+
// CHECK-NEXT: store i32 %[[Val0]], ptr %[[BufPtr]], align 4
31+
B[0][Idx] = Val0;
32+
}
33+
34+
// CHECK: define {{.*}} void @_Z4mainj(i32 noundef %GI)
35+
// CHECK-NEXT: entry:
36+
// CHECK-NEXT: %[[GI_addr:.*]] = alloca i32, align 4
37+
[numthreads(4,1,1)]
38+
void main(uint GI : SV_GroupIndex) {
39+
// CHECK-NEXT: %A = alloca [2 x %"class.hlsl::RWBuffer"], align 4
40+
// CHECK-NEXT: %[[Tmp:.*]] = alloca [2 x %"class.hlsl::RWBuffer"], align 4
41+
// CHECK-NEXT: store i32 %GI, ptr %GI.addr, align 4
42+
43+
// Initialization of array A with resources X and Y
44+
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 %A, ptr align 4 @_ZL1X, i32 4, i1 false)
45+
// CHECK-NEXT: %[[A_1_Ptr:.*]] = getelementptr inbounds %"class.hlsl::RWBuffer", ptr %A, i32 1
46+
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 %[[A_1_Ptr]], ptr align 4 @_ZL1Y, i32 4, i1 false)
47+
RWBuffer<int> A[2] = {X, Y};
48+
49+
// Verify that SomeFn is called with a local copy of the array A
50+
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 %[[Tmp]], ptr align 4 %A, i32 8, i1 false)
51+
// CHECK-NEXT: %[[GI:.*]] = load i32, ptr %[[GI_addr]], align 4
52+
// CHECK-NEXT: call void @_Z6SomeFnA2_N4hlsl8RWBufferIiEEji(ptr noundef byval([2 x %"class.hlsl::RWBuffer"]) align 4 %[[Tmp]], i32 noundef %[[GI]], i32 noundef 1)
53+
SomeFn(A, GI, 1);
54+
55+
// CHECK-NEXT: %[[A_0_Ptr:.*]] = getelementptr inbounds [2 x %"class.hlsl::RWBuffer"], ptr %A, i32 0, i32 0
56+
// CHECK-NEXT: %[[GI:.*]] = load i32, ptr %[[GI_addr]], align 4
57+
// CHECK-NEXT: %[[BufPtr:.*]] = call {{.*}} ptr @_ZN4hlsl8RWBufferIiEixEj(ptr {{.*}} %[[A_0_Ptr]], i32 noundef %[[GI]])
58+
// CHECK-NEXT: store i32 2, ptr %[[BufPtr]], align 4
59+
A[0][GI] = 2;
60+
}

0 commit comments

Comments
 (0)