xref: /llvm-project/clang/test/CodeGenHLSL/BasicFeatures/OutputArguments.hlsl (revision 762f1b17b2815ccdfb4e5cb5412cb6210db92f73)
1// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -disable-llvm-passes -emit-llvm -finclude-default-header -o - %s | FileCheck %s --check-prefixes=CHECK,ALL
2// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -O3 -emit-llvm -finclude-default-header -o - %s | FileCheck %s --check-prefixes=OPT,ALL
3
4// Case 1: Simple floating integral conversion.
5// In this test case a float value is passed to an inout parameter taking an
6// integer. It is converted to an integer on call and converted back after the
7// function.
8
9// CHECK: define void {{.*}}trunc_Param{{.*}}(ptr noalias noundef nonnull align 4 dereferenceable(4) {{%.*}})
10void trunc_Param(inout int X) {}
11
12// ALL-LABEL: define noundef nofpclass(nan inf) float {{.*}}case1
13// CHECK: [[F:%.*]] = alloca float
14// CHECK: [[ArgTmp:%.*]] = alloca i32
15// CHECK: [[FVal:%.*]] = load float, ptr {{.*}}
16// CHECK: [[IVal:%.*]] = fptosi float [[FVal]] to i32
17// CHECK: store i32 [[IVal]], ptr [[ArgTmp]]
18// CHECK: call void {{.*}}trunc_Param{{.*}}(ptr noalias noundef nonnull align 4 dereferenceable(4) [[ArgTmp]])
19// CHECK: [[IRet:%.*]] = load i32, ptr [[ArgTmp]]
20// CHECK: [[FRet:%.*]] = sitofp i32 [[IRet]] to float
21// CHECK: store float [[FRet]], ptr [[F]]
22// OPT: [[IVal:%.*]] = fptosi float {{.*}} to i32
23// OPT: [[FVal:%.*]] = sitofp i32 [[IVal]] to float
24// OPT: ret float [[FVal]]
25export float case1(float F) {
26  trunc_Param(F);
27  return F;
28}
29
30// Case 2: Uninitialized `out` parameters.
31// `out` parameters are not pre-initialized by the caller, so they are
32// uninitialized in the function. If they are not initialized before the
33// function returns the value is undefined.
34
35// CHECK: define void {{.*}}undef{{.*}}(ptr noalias noundef nonnull align 4 dereferenceable(4) {{%.*}})
36void undef(out int Z) { }
37
38// ALL-LABEL: define noundef i32 {{.*}}case2
39// CHECK: [[V:%.*]] = alloca i32
40// CHECK: [[ArgTmp:%.*]] = alloca i32
41// CHECK-NOT: store {{.*}}, ptr [[ArgTmp]]
42// CHECK: call void {{.*}}unde{{.*}}(ptr noalias noundef nonnull align 4 dereferenceable(4) [[ArgTmp]])
43// CHECK-NOT: store {{.*}}, ptr [[ArgTmp]]
44// CHECK: [[Res:%.*]] = load i32, ptr [[ArgTmp]]
45// CHECK: store i32 [[Res]], ptr [[V]], align 4
46// OPT: ret i32 undef
47export int case2() {
48  int V;
49  undef(V);
50  return V;
51}
52
53// Case 3: Simple initialized `out` parameter.
54// This test should verify that an out parameter value is written to as
55// expected.
56
57// CHECK: define void {{.*}}zero{{.*}}(ptr noalias noundef nonnull align 4 dereferenceable(4) {{%.*}})
58void zero(out int Z) { Z = 0; }
59
60// ALL-LABEL: define noundef i32 {{.*}}case3
61// CHECK: [[V:%.*]] = alloca i32
62// CHECK: [[ArgTmp:%.*]] = alloca i32
63// CHECK-NOT: store {{.*}}, ptr [[ArgTmp]]
64// CHECK: call void {{.*}}zero{{.*}}(ptr noalias noundef nonnull align 4 dereferenceable(4) [[ArgTmp]])
65// CHECK-NOT: store {{.*}}, ptr [[ArgTmp]]
66// CHECK: [[Res:%.*]] = load i32, ptr [[ArgTmp]]
67// CHECK: store i32 [[Res]], ptr [[V]], align 4
68// OPT: ret i32 0
69export int case3() {
70  int V;
71  zero(V);
72  return V;
73}
74
75// Case 4: Vector swizzle arguments.
76// Vector swizzles in HLSL produce lvalues, so they can be used as arguments to
77// inout parameters and the swizzle is reversed on writeback.
78
79// CHECK: define void {{.*}}funky{{.*}}(ptr noalias noundef nonnull align 16 dereferenceable(16) {{%.*}})
80void funky(inout int3 X) {
81  X.x += 1;
82  X.y += 2;
83  X.z += 3;
84}
85
86// ALL-LABEL: define noundef {{.*}}<3 x i32> {{.*}}case4
87
88// This block initializes V = 0.xxx.
89// CHECK:  [[V:%.*]] = alloca <3 x i32>
90// CHECK:  [[ArgTmp:%.*]] = alloca <3 x i32>
91// CHECK:  store <1 x i32> zeroinitializer, ptr [[ZeroPtr:%.*]]
92// CHECK:  [[ZeroV1:%.*]] = load <1 x i32>, ptr [[ZeroPtr]]
93// CHECK:  [[ZeroV3:%.*]] = shufflevector <1 x i32> [[ZeroV1]], <1 x i32> poison, <3 x i32> zeroinitializer
94// CHECK:  store <3 x i32> [[ZeroV3]], ptr [[V]]
95
96// Shuffle the vector to the temporary.
97// CHECK:  [[VVal:%.*]] = load <3 x i32>, ptr [[V]]
98// CHECK:  [[Vyzx:%.*]] = shufflevector <3 x i32> [[VVal]], <3 x i32> poison, <3 x i32> <i32 1, i32 2, i32 0>
99// CHECK:  store <3 x i32> [[Vyzx]], ptr [[ArgTmp]]
100
101// Call the function with the temporary.
102// CHECK: call void {{.*}}funky{{.*}}(ptr noalias noundef nonnull align 16 dereferenceable(16) [[ArgTmp]])
103
104// Shuffle it back.
105// CHECK:  [[RetVal:%.*]] = load <3 x i32>, ptr [[ArgTmp]]
106// CHECK:  [[Vxyz:%.*]] = shufflevector <3 x i32> [[RetVal]], <3 x i32> poison, <3 x i32> <i32 2, i32 0, i32 1>
107// CHECK:  store <3 x i32> [[Vxyz]], ptr [[V]]
108
109// OPT: ret <3 x i32> <i32 3, i32 1, i32 2>
110export int3 case4() {
111  int3 V = 0.xxx;
112  funky(V.yzx);
113  return V;
114}
115
116
117// Case 5: Straightforward inout of a scalar value.
118
119// CHECK: define void {{.*}}increment{{.*}}(ptr noalias noundef nonnull align 4 dereferenceable(4) {{%.*}})
120void increment(inout int I) {
121  I += 1;
122}
123
124// ALL-LABEL: define noundef i32 {{.*}}case5
125
126// CHECK: [[I:%.*]] = alloca i32
127// CHECK: [[ArgTmp:%.*]] = alloca i32
128// CHECK: store i32 4, ptr [[I]]
129// CHECK: [[IInit:%.*]] = load i32, ptr [[I]]
130// CHECK: store i32 [[IInit:%.*]], ptr [[ArgTmp]], align 4
131// CHECK: call void {{.*}}increment{{.*}}(ptr noalias noundef nonnull align 4 dereferenceable(4) [[ArgTmp]])
132// CHECK: [[RetVal:%.*]] = load i32, ptr [[ArgTmp]]
133// CHECK: store i32 [[RetVal]], ptr [[I]], align 4
134// OPT: ret i32 5
135export int case5() {
136  int I = 4;
137  increment(I);
138  return I;
139}
140
141// Case 6: Aggregate out parameters.
142struct S {
143  int X;
144  float Y;
145};
146
147// CHECK: define void {{.*}}init{{.*}}(ptr noalias noundef nonnull align 4 dereferenceable(8) {{%.*}})
148void init(out S s) {
149  s.X = 3;
150  s.Y = 4;
151}
152
153// ALL-LABEL: define noundef i32 {{.*}}case6
154
155// CHECK: [[S:%.*]] = alloca %struct.S
156// CHECK: [[Tmp:%.*]] = alloca %struct.S
157// CHECK: call void {{.*}}init{{.*}}(ptr noalias noundef nonnull align 4 dereferenceable(8) [[Tmp]])
158// CHECK: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[S]], ptr align 4 [[Tmp]], i32 8, i1 false)
159
160// OPT: ret i32 7
161export int case6() {
162  S s;
163  init(s);
164  return s.X + s.Y;
165}
166
167// Case 7: Aggregate inout parameters.
168struct R {
169  int X;
170  float Y;
171};
172
173// CHECK: define void {{.*}}init{{.*}}(ptr noalias noundef nonnull align 4 dereferenceable(8) {{%.*}})
174void init(inout R s) {
175  s.X = 3;
176  s.Y = 4;
177}
178
179// ALL-LABEL: define noundef i32 {{.*}}case7
180
181// CHECK: [[S:%.*]] = alloca %struct.R
182// CHECK: [[Tmp:%.*]] = alloca %struct.R
183// CHECK: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Tmp]], ptr align 4 [[S]], i32 8, i1 false)
184// CHECK: call void {{.*}}init{{.*}}(ptr noalias noundef nonnull align 4 dereferenceable(8) [[Tmp]])
185// CHECK: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[S]], ptr align 4 [[Tmp]], i32 8, i1 false)
186
187// OPT: ret i32 7
188export int case7() {
189  R s;
190  init(s);
191  return s.X + s.Y;
192}
193
194
195// Case 8: Non-scalars with a cast expression.
196
197// CHECK: define void {{.*}}trunc_vec{{.*}}(ptr noalias noundef nonnull align 16 dereferenceable(16) {{%.*}})
198void trunc_vec(inout int3 V) {}
199
200// ALL-LABEL: define noundef nofpclass(nan inf) <3 x float> {{.*}}case8
201
202// CHECK: [[V:%.*]] = alloca <3 x float>
203// CHECK: [[Tmp:%.*]] = alloca <3 x i32>
204// CHECK: [[FVal:%.*]] = load <3 x float>, ptr [[V]]
205// CHECK: [[IVal:%.*]] = fptosi <3 x float> [[FVal]] to <3 x i32>
206// CHECK: store <3 x i32> [[IVal]], ptr [[Tmp]]
207// CHECK: call void {{.*}}trunc_vec{{.*}}(ptr noalias noundef nonnull align 16 dereferenceable(16) [[Tmp]])
208// CHECK: [[IRet:%.*]] = load <3 x i32>, ptr [[Tmp]]
209// CHECK: [[FRet:%.*]] = sitofp <3 x i32> [[IRet]] to <3 x float>
210// CHECK: store <3 x float> [[FRet]], ptr [[V]]
211
212// OPT: [[IVal:%.*]] = fptosi <3 x float> {{.*}} to <3 x i32>
213// OPT: [[FVal:%.*]] = sitofp <3 x i32> [[IVal]] to <3 x float>
214// OPT: ret <3 x float> [[FVal]]
215
216export float3 case8(float3 V) {
217  trunc_vec(V);
218  return V;
219}
220
221// Case 9: Side-effecting lvalue argument expression!
222
223void do_nothing(inout int V) {}
224
225// ALL-LABEL: define noundef i32 {{.*}}case9
226// CHECK: [[V:%.*]] = alloca i32
227// CHECK: [[Tmp:%.*]] = alloca i32
228// CHECK: store i32 0, ptr [[V]]
229// CHECK: [[VVal:%.*]] = load i32, ptr [[V]]
230// CHECK: [[VInc:%.*]] = add nsw i32 [[VVal]], 1
231// CHECK: store i32 [[VInc]], ptr [[V]]
232// CHECK: [[VArg:%.*]] = load i32, ptr [[V]]
233// CHECK-NOT: add
234// CHECK: store i32 [[VArg]], ptr [[Tmp]]
235// CHECK: call void {{.*}}do_nothing{{.*}}(ptr noalias noundef nonnull align 4 dereferenceable(4) [[Tmp]])
236// CHECK: [[RetVal:%.*]] = load i32, ptr [[Tmp]]
237// CHECK: store i32 [[RetVal]], ptr [[V]]
238
239// OPT: ret i32 1
240export int case9() {
241  int V = 0;
242  do_nothing(++V);
243  return V;
244}
245
246// Case 10: Verify argument writeback ordering for aliasing arguments.
247
248void order_matters(inout int X, inout int Y) {
249  Y = 2;
250  X = 1;
251}
252
253// ALL-LABEL: define noundef i32 {{.*}}case10
254
255// CHECK: [[V:%.*]] = alloca i32
256// CHECK: [[Tmp0:%.*]] = alloca i32
257// CHECK: [[Tmp1:%.*]] = alloca i32
258// CHECK: store i32 0, ptr [[V]]
259// CHECK: [[VVal:%.*]] = load i32, ptr [[V]]
260// CHECK: store i32 [[VVal]], ptr [[Tmp0]]
261// CHECK: [[VVal:%.*]] = load i32, ptr [[V]]
262// CHECK: store i32 [[VVal]], ptr [[Tmp1]]
263// CHECK: call void {{.*}}order_matters{{.*}}(ptr noalias noundef nonnull align 4 dereferenceable(4) [[Tmp0]], ptr noalias noundef nonnull align 4 dereferenceable(4) [[Tmp1]])
264// CHECK: [[Arg1Val:%.*]] = load i32, ptr [[Tmp0]]
265// CHECK: store i32 [[Arg1Val]], ptr [[V]]
266// CHECK: [[Arg2Val:%.*]] = load i32, ptr [[Tmp1]]
267// CHECK: store i32 [[Arg2Val]], ptr [[V]]
268
269// OPT: ret i32 2
270export int case10() {
271  int V = 0;
272  order_matters(V, V);
273  return V;
274}
275
276// Case 11: Verify inout on bitfield lvalues
277
278struct B {
279  int X : 8;
280  int Y : 8;
281};
282
283void setFour(inout int I) {
284  I = 4;
285}
286
287// ALL-LABEL: define {{.*}} i32 {{.*}}case11
288
289// CHECK: [[B:%.*]] = alloca %struct.B
290// CHECK: [[Tmp:%.*]] = alloca i32
291
292// CHECK: [[BFLoad:%.*]] = load i16, ptr [[B]]
293// CHECK: [[BFshl:%.*]] = shl i16 [[BFLoad]], 8
294// CHECK: [[BFashr:%.*]] = ashr i16 [[BFshl]], 8
295// CHECK: [[BFcast:%.*]] = sext i16 [[BFashr]] to i32
296// CHECK: store i32 [[BFcast]], ptr [[Tmp]]
297// CHECK: call void {{.*}}setFour{{.*}}(ptr noalias noundef nonnull align 4 dereferenceable(4) [[Tmp]])
298// CHECK: [[RetVal:%.*]] = load i32, ptr [[Tmp]]
299// CHECK: [[TruncVal:%.*]] = trunc i32 [[RetVal]] to i16
300// CHECK: [[BFLoad:%.*]] = load i16, ptr [[B]]
301// CHECK: [[BFValue:%.*]] = and i16 [[TruncVal]], 255
302// CHECK: [[ZerodField:%.*]] = and i16 [[BFLoad]], -256
303// CHECK: [[BFSet:%.*]] = or i16 [[ZerodField]], [[BFValue]]
304// CHECK: store i16 [[BFSet]], ptr [[B]]
305
306// OPT: ret i32 8
307export int case11() {
308  B b = {1 , 2};
309  setFour(b.X);
310  return b.X * b.Y;
311}
312
313// Case 12: Uninitialized out parameters are undefined
314
315void oops(out int X) {}
316// ALL-LABEL: define {{.*}} i32 {{.*}}case12
317
318// CHECK: [[V:%.*]] = alloca i32
319// CHECK: [[Tmp:%.*]] = alloca i32
320// CHECK-NOT: store {{.*}}, ptr [[Tmp]]
321// CHECK: call void {{.*}}oops{{.*}}(ptr noalias noundef nonnull align 4 dereferenceable(4) [[Tmp]])
322// CHECK: [[ArgVal:%.*]] = load i32, ptr [[Tmp]]
323// CHECK: store i32 [[ArgVal]], ptr [[V]]
324
325// OPT:  ret i32 undef
326export int case12() {
327  int V = 0;
328  oops(V);
329  return V;
330}
331