blob: cb4f29f14513e368571407dd8836218589ae70e0 [file] [log] [blame]
Antonio Maiorano9970ec62021-03-18 17:59:54 +00001// Copyright 2021 The Tint Authors.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#include "src/ast/access_decoration.h"
16#include "src/ast/constant_id_decoration.h"
Antonio Maiorano03c01b52021-03-19 14:04:51 +000017#include "src/ast/return_statement.h"
Antonio Maiorano9970ec62021-03-18 17:59:54 +000018#include "src/ast/stage_decoration.h"
19#include "src/ast/struct_block_decoration.h"
20#include "src/ast/workgroup_decoration.h"
21#include "src/resolver/resolver.h"
22#include "src/resolver/resolver_test_helper.h"
23
24#include "gmock/gmock.h"
25
26namespace tint {
Antonio Maioranofc03a462021-04-16 02:36:44 +000027namespace resolver {
28
29namespace DecorationTests {
Antonio Maiorano9970ec62021-03-18 17:59:54 +000030namespace {
31
32enum class DecorationKind {
33 kAccess,
34 kAlign,
35 kBinding,
36 kBuiltin,
37 kConstantId,
38 kGroup,
39 kLocation,
40 kOffset,
41 kSize,
42 kStage,
43 kStride,
44 kStructBlock,
45 kWorkgroup,
46};
47struct TestParams {
48 DecorationKind kind;
49 bool should_pass;
50};
Antonio Maioranofc03a462021-04-16 02:36:44 +000051struct TestWithParams : ResolverTestWithParam<TestParams> {};
Antonio Maiorano9970ec62021-03-18 17:59:54 +000052
Antonio Maioranofc03a462021-04-16 02:36:44 +000053static ast::Decoration* createDecoration(const Source& source,
54 ProgramBuilder& builder,
55 DecorationKind kind) {
Antonio Maiorano9970ec62021-03-18 17:59:54 +000056 switch (kind) {
57 case DecorationKind::kAccess:
58 return builder.create<ast::AccessDecoration>(
59 source, ast::AccessControl::kReadOnly);
60 case DecorationKind::kAlign:
61 return builder.create<ast::StructMemberAlignDecoration>(source, 4u);
62 case DecorationKind::kBinding:
63 return builder.create<ast::BindingDecoration>(source, 1);
64 case DecorationKind::kBuiltin:
65 return builder.create<ast::BuiltinDecoration>(source,
66 ast::Builtin::kPosition);
67 case DecorationKind::kConstantId:
68 return builder.create<ast::ConstantIdDecoration>(source, 0u);
69 case DecorationKind::kGroup:
70 return builder.create<ast::GroupDecoration>(source, 1u);
71 case DecorationKind::kLocation:
72 return builder.create<ast::LocationDecoration>(source, 1);
73 case DecorationKind::kOffset:
74 return builder.create<ast::StructMemberOffsetDecoration>(source, 4u);
75 case DecorationKind::kSize:
76 return builder.create<ast::StructMemberSizeDecoration>(source, 4u);
77 case DecorationKind::kStage:
78 return builder.create<ast::StageDecoration>(source,
79 ast::PipelineStage::kCompute);
80 case DecorationKind::kStride:
81 return builder.create<ast::StrideDecoration>(source, 4u);
82 case DecorationKind::kStructBlock:
83 return builder.create<ast::StructBlockDecoration>(source);
84 case DecorationKind::kWorkgroup:
85 return builder.create<ast::WorkgroupDecoration>(source, 1u, 1u, 1u);
86 }
87 return nullptr;
88}
89
Antonio Maiorano03c01b52021-03-19 14:04:51 +000090using FunctionReturnTypeDecorationTest = TestWithParams;
91TEST_P(FunctionReturnTypeDecorationTest, IsValid) {
Antonio Maioranofc03a462021-04-16 02:36:44 +000092 auto& params = GetParam();
Antonio Maiorano03c01b52021-03-19 14:04:51 +000093
94 Func("main", ast::VariableList{}, ty.f32(),
95 ast::StatementList{create<ast::ReturnStatement>(Expr(1.f))},
96 ast::DecorationList{
97 create<ast::StageDecoration>(ast::PipelineStage::kVertex)},
98 ast::DecorationList{createDecoration({}, *this, params.kind)});
99
100 if (params.should_pass) {
101 EXPECT_TRUE(r()->Resolve()) << r()->error();
102 } else {
103 EXPECT_FALSE(r()->Resolve()) << r()->error();
104 EXPECT_EQ(r()->error(),
105 "error: decoration is not valid for function return types");
106 }
107}
108INSTANTIATE_TEST_SUITE_P(
109 ResolverDecorationValidationTest,
110 FunctionReturnTypeDecorationTest,
111 testing::Values(TestParams{DecorationKind::kAccess, false},
112 TestParams{DecorationKind::kAlign, false},
113 TestParams{DecorationKind::kBinding, false},
114 TestParams{DecorationKind::kBuiltin, true},
115 TestParams{DecorationKind::kConstantId, false},
116 TestParams{DecorationKind::kGroup, false},
117 TestParams{DecorationKind::kLocation, true},
118 TestParams{DecorationKind::kOffset, false},
119 TestParams{DecorationKind::kSize, false},
120 TestParams{DecorationKind::kStage, false},
121 TestParams{DecorationKind::kStride, false},
122 TestParams{DecorationKind::kStructBlock, false},
123 TestParams{DecorationKind::kWorkgroup, false}));
124
Antonio Maiorano9970ec62021-03-18 17:59:54 +0000125using ArrayDecorationTest = TestWithParams;
Antonio Maiorano9970ec62021-03-18 17:59:54 +0000126TEST_P(ArrayDecorationTest, IsValid) {
Antonio Maioranofc03a462021-04-16 02:36:44 +0000127 auto& params = GetParam();
Antonio Maiorano9970ec62021-03-18 17:59:54 +0000128
129 ast::StructMemberList members{Member(
Antonio Maiorano3751fd22021-04-19 22:51:23 +0000130 "a", create<sem::ArrayType>(ty.f32(), 0,
131 ast::DecorationList{createDecoration(
132 Source{{12, 34}}, *this, params.kind)}))};
Antonio Maiorano9970ec62021-03-18 17:59:54 +0000133 auto* s = create<ast::Struct>(
134 members, ast::DecorationList{create<ast::StructBlockDecoration>()});
135 auto* s_ty = ty.struct_("mystruct", s);
136 AST().AddConstructedType(s_ty);
137
138 WrapInFunction();
139
140 if (params.should_pass) {
141 EXPECT_TRUE(r()->Resolve()) << r()->error();
142 } else {
143 EXPECT_FALSE(r()->Resolve()) << r()->error();
144 EXPECT_EQ(r()->error(),
145 "12:34 error: decoration is not valid for array types");
146 }
147}
148INSTANTIATE_TEST_SUITE_P(
149 ResolverDecorationValidationTest,
150 ArrayDecorationTest,
151 testing::Values(TestParams{DecorationKind::kAccess, false},
152 TestParams{DecorationKind::kAlign, false},
153 TestParams{DecorationKind::kBinding, false},
154 TestParams{DecorationKind::kBuiltin, false},
155 TestParams{DecorationKind::kConstantId, false},
156 TestParams{DecorationKind::kGroup, false},
157 TestParams{DecorationKind::kLocation, false},
158 TestParams{DecorationKind::kOffset, false},
159 TestParams{DecorationKind::kSize, false},
160 TestParams{DecorationKind::kStage, false},
161 TestParams{DecorationKind::kStride, true},
162 TestParams{DecorationKind::kStructBlock, false},
163 TestParams{DecorationKind::kWorkgroup, false}));
164
165using StructDecorationTest = TestWithParams;
166TEST_P(StructDecorationTest, IsValid) {
Antonio Maioranofc03a462021-04-16 02:36:44 +0000167 auto& params = GetParam();
Antonio Maiorano9970ec62021-03-18 17:59:54 +0000168
169 auto* s = create<ast::Struct>(ast::StructMemberList{},
170 ast::DecorationList{createDecoration(
171 Source{{12, 34}}, *this, params.kind)});
172 auto* s_ty = ty.struct_("mystruct", s);
173 AST().AddConstructedType(s_ty);
174
175 WrapInFunction();
176
177 if (params.should_pass) {
178 EXPECT_TRUE(r()->Resolve()) << r()->error();
179 } else {
180 EXPECT_FALSE(r()->Resolve()) << r()->error();
181 EXPECT_EQ(r()->error(),
182 "12:34 error: decoration is not valid for struct declarations");
183 }
184}
185INSTANTIATE_TEST_SUITE_P(
186 ResolverDecorationValidationTest,
187 StructDecorationTest,
188 testing::Values(TestParams{DecorationKind::kAccess, false},
189 TestParams{DecorationKind::kAlign, false},
190 TestParams{DecorationKind::kBinding, false},
191 TestParams{DecorationKind::kBuiltin, false},
192 TestParams{DecorationKind::kConstantId, false},
193 TestParams{DecorationKind::kGroup, false},
194 TestParams{DecorationKind::kLocation, false},
195 TestParams{DecorationKind::kOffset, false},
196 TestParams{DecorationKind::kSize, false},
197 TestParams{DecorationKind::kStage, false},
198 TestParams{DecorationKind::kStride, false},
199 TestParams{DecorationKind::kStructBlock, true},
200 TestParams{DecorationKind::kWorkgroup, false}));
201
202using StructMemberDecorationTest = TestWithParams;
203TEST_P(StructMemberDecorationTest, IsValid) {
Antonio Maioranofc03a462021-04-16 02:36:44 +0000204 auto& params = GetParam();
Antonio Maiorano9970ec62021-03-18 17:59:54 +0000205
206 ast::StructMemberList members{
207 Member("a", ty.i32(),
208 ast::DecorationList{
209 createDecoration(Source{{12, 34}}, *this, params.kind)})};
210 auto* s = create<ast::Struct>(members, ast::DecorationList{});
211 auto* s_ty = ty.struct_("mystruct", s);
212 AST().AddConstructedType(s_ty);
213
214 WrapInFunction();
215
216 if (params.should_pass) {
217 EXPECT_TRUE(r()->Resolve()) << r()->error();
218 } else {
219 EXPECT_FALSE(r()->Resolve()) << r()->error();
220 EXPECT_EQ(r()->error(),
221 "12:34 error: decoration is not valid for structure members");
222 }
223}
224INSTANTIATE_TEST_SUITE_P(
225 ResolverDecorationValidationTest,
226 StructMemberDecorationTest,
227 testing::Values(TestParams{DecorationKind::kAccess, false},
228 TestParams{DecorationKind::kAlign, true},
229 TestParams{DecorationKind::kBinding, false},
230 TestParams{DecorationKind::kBuiltin, true},
231 TestParams{DecorationKind::kConstantId, false},
232 TestParams{DecorationKind::kGroup, false},
233 TestParams{DecorationKind::kLocation, true},
234 TestParams{DecorationKind::kOffset, true},
235 TestParams{DecorationKind::kSize, true},
236 TestParams{DecorationKind::kStage, false},
237 TestParams{DecorationKind::kStride, false},
238 TestParams{DecorationKind::kStructBlock, false},
239 TestParams{DecorationKind::kWorkgroup, false}));
240
Antonio Maioranobbbb0ed2021-04-06 20:18:57 +0000241using VariableDecorationTest = TestWithParams;
242TEST_P(VariableDecorationTest, IsValid) {
Antonio Maioranofc03a462021-04-16 02:36:44 +0000243 auto& params = GetParam();
Antonio Maioranobbbb0ed2021-04-06 20:18:57 +0000244
245 Global("a", ty.f32(), ast::StorageClass::kInput, nullptr,
246 ast::DecorationList{
247 createDecoration(Source{{12, 34}}, *this, params.kind)});
248
249 WrapInFunction();
250
251 if (params.should_pass) {
252 EXPECT_TRUE(r()->Resolve()) << r()->error();
253 } else {
254 EXPECT_FALSE(r()->Resolve()) << r()->error();
255 EXPECT_EQ(r()->error(),
256 "12:34 error: decoration is not valid for variables");
257 }
258}
259INSTANTIATE_TEST_SUITE_P(
Antonio Maioranod15391e2021-04-08 14:08:47 +0000260 ResolverDecorationValidationTest,
Antonio Maioranobbbb0ed2021-04-06 20:18:57 +0000261 VariableDecorationTest,
262 testing::Values(TestParams{DecorationKind::kAccess, false},
263 TestParams{DecorationKind::kAlign, false},
264 TestParams{DecorationKind::kBinding, true},
265 TestParams{DecorationKind::kBuiltin, true},
266 TestParams{DecorationKind::kConstantId, true},
267 TestParams{DecorationKind::kGroup, true},
268 TestParams{DecorationKind::kLocation, true},
269 TestParams{DecorationKind::kOffset, false},
270 TestParams{DecorationKind::kSize, false},
271 TestParams{DecorationKind::kStage, false},
272 TestParams{DecorationKind::kStride, false},
273 TestParams{DecorationKind::kStructBlock, false},
274 TestParams{DecorationKind::kWorkgroup, false}));
275
Antonio Maioranobfc97942021-04-06 20:41:07 +0000276using FunctionDecorationTest = TestWithParams;
277TEST_P(FunctionDecorationTest, IsValid) {
Antonio Maioranofc03a462021-04-16 02:36:44 +0000278 auto& params = GetParam();
Antonio Maioranobfc97942021-04-06 20:41:07 +0000279
280 Func("foo", ast::VariableList{}, ty.void_(), ast::StatementList{},
281 ast::DecorationList{
282 create<ast::StageDecoration>(ast::PipelineStage::kCompute),
283 createDecoration(Source{{12, 34}}, *this, params.kind)});
284
285 if (params.should_pass) {
286 EXPECT_TRUE(r()->Resolve()) << r()->error();
287 } else {
288 EXPECT_FALSE(r()->Resolve()) << r()->error();
289 EXPECT_EQ(r()->error(),
290 "12:34 error: decoration is not valid for functions");
291 }
292}
293INSTANTIATE_TEST_SUITE_P(
Antonio Maioranod15391e2021-04-08 14:08:47 +0000294 ResolverDecorationValidationTest,
Antonio Maioranobfc97942021-04-06 20:41:07 +0000295 FunctionDecorationTest,
296 testing::Values(TestParams{DecorationKind::kAccess, false},
297 TestParams{DecorationKind::kAlign, false},
298 TestParams{DecorationKind::kBinding, false},
299 TestParams{DecorationKind::kBuiltin, false},
300 TestParams{DecorationKind::kConstantId, false},
301 TestParams{DecorationKind::kGroup, false},
302 TestParams{DecorationKind::kLocation, false},
303 TestParams{DecorationKind::kOffset, false},
304 TestParams{DecorationKind::kSize, false},
305 // Skip kStage as we always apply it in this test
306 TestParams{DecorationKind::kStride, false},
307 TestParams{DecorationKind::kStructBlock, false},
308 TestParams{DecorationKind::kWorkgroup, true}));
309
Antonio Maiorano9970ec62021-03-18 17:59:54 +0000310} // namespace
Antonio Maioranofc03a462021-04-16 02:36:44 +0000311} // namespace DecorationTests
312
313namespace ArrayStrideTests {
314namespace {
315
316struct Params {
317 create_type_func_ptr create_el_type;
318 uint32_t stride;
319 bool should_pass;
320};
321
322struct TestWithParams : ResolverTestWithParam<Params> {};
323
324using ArrayStrideTest = TestWithParams;
325TEST_P(ArrayStrideTest, All) {
326 auto& params = GetParam();
327 auto* el_ty = params.create_el_type(ty);
328
329 std::stringstream ss;
330 ss << "el_ty: " << el_ty->FriendlyName(Symbols())
331 << ", stride: " << params.stride
332 << ", should_pass: " << params.should_pass;
333 SCOPED_TRACE(ss.str());
334
335 auto* arr =
Antonio Maiorano3751fd22021-04-19 22:51:23 +0000336 create<sem::ArrayType>(el_ty, 4,
337 ast::DecorationList{
338 create<ast::StrideDecoration>(params.stride),
339 });
Antonio Maioranofc03a462021-04-16 02:36:44 +0000340
341 Global(Source{{12, 34}}, "myarray", arr, ast::StorageClass::kInput);
342
343 if (params.should_pass) {
344 EXPECT_TRUE(r()->Resolve()) << r()->error();
345 } else {
346 EXPECT_FALSE(r()->Resolve());
347 EXPECT_EQ(r()->error(),
348 "12:34 error: arrays decorated with the stride attribute must "
349 "have a stride that is at least the size of the element type, "
350 "and be a multiple of the element type's alignment value.");
351 }
352}
353
354// Helpers and typedefs
355using i32 = ProgramBuilder::i32;
356using u32 = ProgramBuilder::u32;
357using f32 = ProgramBuilder::f32;
358
359struct SizeAndAlignment {
360 uint32_t size;
361 uint32_t align;
362};
363constexpr SizeAndAlignment default_u32 = {4, 4};
364constexpr SizeAndAlignment default_i32 = {4, 4};
365constexpr SizeAndAlignment default_f32 = {4, 4};
366constexpr SizeAndAlignment default_vec2 = {8, 8};
367constexpr SizeAndAlignment default_vec3 = {12, 16};
368constexpr SizeAndAlignment default_vec4 = {16, 16};
369constexpr SizeAndAlignment default_mat2x2 = {16, 8};
370constexpr SizeAndAlignment default_mat3x3 = {48, 16};
371constexpr SizeAndAlignment default_mat4x4 = {64, 16};
372
373INSTANTIATE_TEST_SUITE_P(
374 ResolverDecorationValidationTest,
375 ArrayStrideTest,
376 testing::Values(
377 // Succeed because stride >= element size (while being multiple of
378 // element alignment)
379 Params{ty_u32, default_u32.size, true},
380 Params{ty_i32, default_i32.size, true},
381 Params{ty_f32, default_f32.size, true},
382 Params{ty_vec2<f32>, default_vec2.size, true},
383 // vec3's default size is not a multiple of its alignment
384 // Params{ty_vec3<f32>, default_vec3.size, true},
385 Params{ty_vec4<f32>, default_vec4.size, true},
386 Params{ty_mat2x2<f32>, default_mat2x2.size, true},
387 Params{ty_mat3x3<f32>, default_mat3x3.size, true},
388 Params{ty_mat4x4<f32>, default_mat4x4.size, true},
389
390 // Fail because stride is < element size
391 Params{ty_u32, default_u32.size - 1, false},
392 Params{ty_i32, default_i32.size - 1, false},
393 Params{ty_f32, default_f32.size - 1, false},
394 Params{ty_vec2<f32>, default_vec2.size - 1, false},
395 Params{ty_vec3<f32>, default_vec3.size - 1, false},
396 Params{ty_vec4<f32>, default_vec4.size - 1, false},
397 Params{ty_mat2x2<f32>, default_mat2x2.size - 1, false},
398 Params{ty_mat3x3<f32>, default_mat3x3.size - 1, false},
399 Params{ty_mat4x4<f32>, default_mat4x4.size - 1, false},
400
401 // Succeed because stride equals multiple of element alignment
402 Params{ty_u32, default_u32.align * 7, true},
403 Params{ty_i32, default_i32.align * 7, true},
404 Params{ty_f32, default_f32.align * 7, true},
405 Params{ty_vec2<f32>, default_vec2.align * 7, true},
406 Params{ty_vec3<f32>, default_vec3.align * 7, true},
407 Params{ty_vec4<f32>, default_vec4.align * 7, true},
408 Params{ty_mat2x2<f32>, default_mat2x2.align * 7, true},
409 Params{ty_mat3x3<f32>, default_mat3x3.align * 7, true},
410 Params{ty_mat4x4<f32>, default_mat4x4.align * 7, true},
411
412 // Fail because stride is not multiple of element alignment
413 Params{ty_u32, (default_u32.align - 1) * 7, false},
414 Params{ty_i32, (default_i32.align - 1) * 7, false},
415 Params{ty_f32, (default_f32.align - 1) * 7, false},
416 Params{ty_vec2<f32>, (default_vec2.align - 1) * 7, false},
417 Params{ty_vec3<f32>, (default_vec3.align - 1) * 7, false},
418 Params{ty_vec4<f32>, (default_vec4.align - 1) * 7, false},
419 Params{ty_mat2x2<f32>, (default_mat2x2.align - 1) * 7, false},
420 Params{ty_mat3x3<f32>, (default_mat3x3.align - 1) * 7, false},
Ben Clayton648b05e2021-04-16 10:37:34 +0000421 Params{ty_mat4x4<f32>, (default_mat4x4.align - 1) * 7, false}));
Antonio Maioranofc03a462021-04-16 02:36:44 +0000422
Ben Clayton6fcefe42021-04-19 19:16:12 +0000423TEST_F(ArrayStrideTest, MultipleDecorations) {
Antonio Maiorano3751fd22021-04-19 22:51:23 +0000424 auto* arr = create<sem::ArrayType>(ty.i32(), 4,
425 ast::DecorationList{
426 create<ast::StrideDecoration>(4),
427 create<ast::StrideDecoration>(4),
428 });
Ben Clayton6fcefe42021-04-19 19:16:12 +0000429
430 Global(Source{{12, 34}}, "myarray", arr, ast::StorageClass::kInput);
431
432 EXPECT_FALSE(r()->Resolve());
433 EXPECT_EQ(r()->error(),
434 "12:34 error: array must have at most one [[stride]] decoration");
435}
436
Antonio Maioranofc03a462021-04-16 02:36:44 +0000437} // namespace
438} // namespace ArrayStrideTests
439} // namespace resolver
Antonio Maiorano9970ec62021-03-18 17:59:54 +0000440} // namespace tint