1 // Copyright 2020 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 "gmock/gmock.h"
16 #include "src/reader/spirv/parser_impl_test_helper.h"
17 #include "src/reader/spirv/spirv_tools_helpers_test.h"
18
19 namespace tint {
20 namespace reader {
21 namespace spirv {
22 namespace {
23
24 using ::testing::HasSubstr;
25
Caps()26 std::string Caps() {
27 return R"(
28 OpCapability Shader
29 OpMemoryModel Logical Simple
30 )";
31 }
32
Preamble()33 std::string Preamble() {
34 return Caps() + R"(
35 OpEntryPoint Fragment %main "x_100"
36 OpExecutionMode %main OriginUpperLeft
37 )";
38 }
39
MainBody()40 std::string MainBody() {
41 return R"(
42 %main = OpFunction %void None %voidfn
43 %main_entry = OpLabel
44 OpReturn
45 OpFunctionEnd
46 )";
47 }
48
49 /// @returns a SPIR-V assembly segment which assigns debug names
50 /// to particular IDs.
Names(std::vector<std::string> ids)51 std::string Names(std::vector<std::string> ids) {
52 std::ostringstream outs;
53 for (auto& id : ids) {
54 outs << " OpName %" << id << " \"" << id << "\"\n";
55 }
56 return outs.str();
57 }
58
CommonTypes()59 std::string CommonTypes() {
60 return R"(
61 %void = OpTypeVoid
62 %voidfn = OpTypeFunction %void
63 %float = OpTypeFloat 32
64 %uint = OpTypeInt 32 0
65 %int = OpTypeInt 32 1
66 )";
67 }
68
BuiltinPosition()69 std::string BuiltinPosition() {
70 return R"(OpDecorate %position BuiltIn Position
71 %float = OpTypeFloat 32
72 %v4float = OpTypeVector %float 4
73 %ptr = OpTypePointer Output %v4float
74 %position = OpVariable %ptr Output
75 %void = OpTypeVoid
76 %voidfn = OpTypeFunction %void
77 %uint = OpTypeInt 32 0
78 %int = OpTypeInt 32 1
79 )";
80 }
81
TEST_F(SpvParserTest,EmitFunctions_NoFunctions)82 TEST_F(SpvParserTest, EmitFunctions_NoFunctions) {
83 auto p = parser(test::Assemble(
84 R"(
85 OpCapability Shader
86 OpMemoryModel Logical Simple
87 )" + CommonTypes()));
88 EXPECT_TRUE(p->BuildAndParseInternalModule());
89 EXPECT_TRUE(p->error().empty());
90 Program program = p->program();
91 const auto program_ast = test::ToString(program);
92 EXPECT_THAT(program_ast, Not(HasSubstr("Function{")));
93 p->SkipDumpingPending("Not valid for Vulkan: needs an entry point");
94 }
95
TEST_F(SpvParserTest,EmitFunctions_FunctionWithoutBody)96 TEST_F(SpvParserTest, EmitFunctions_FunctionWithoutBody) {
97 auto p =
98 parser(test::Assemble(Preamble() + Names({"main"}) + CommonTypes() + R"(
99 %main = OpFunction %void None %voidfn
100 OpFunctionEnd
101 )"));
102 EXPECT_TRUE(p->BuildAndParseInternalModule());
103 EXPECT_TRUE(p->error().empty());
104 Program program = p->program();
105 const auto program_ast = test::ToString(program);
106 EXPECT_THAT(program_ast, Not(HasSubstr("Function{")));
107 p->SkipDumpingPending("Missing an entry point body requires Linkage");
108 }
109
TEST_F(SpvParserTest,EmitFunctions_Function_EntryPoint_Vertex)110 TEST_F(SpvParserTest, EmitFunctions_Function_EntryPoint_Vertex) {
111 std::string input = Caps() +
112 R"(OpEntryPoint Vertex %main "main" %position )" +
113 Names({"main"}) + BuiltinPosition() + R"(
114
115 %main = OpFunction %void None %voidfn
116 %entry = OpLabel
117 OpReturn
118 OpFunctionEnd)";
119
120 auto p = parser(test::Assemble(input));
121 ASSERT_TRUE(p->BuildAndParseInternalModule());
122 ASSERT_TRUE(p->error().empty()) << p->error();
123 Program program = p->program();
124 const auto program_ast = test::ToString(program);
125 EXPECT_THAT(program_ast, HasSubstr(R"(
126 struct main_out {
127 [[builtin(position)]]
128 x_2_1 : vec4<f32>;
129 };
130 )")) << program_ast;
131
132 EXPECT_THAT(program_ast, HasSubstr(R"(
133 [[stage(vertex)]]
134 fn main() -> main_out {
135 )"));
136 }
137
TEST_F(SpvParserTest,EmitFunctions_Function_EntryPoint_Fragment)138 TEST_F(SpvParserTest, EmitFunctions_Function_EntryPoint_Fragment) {
139 std::string input = Caps() + R"(
140 OpEntryPoint Fragment %main "main"
141 OpExecutionMode %main OriginUpperLeft
142 )" + Names({"main"}) + CommonTypes() +
143 MainBody();
144
145 auto p = parser(test::Assemble(input));
146 ASSERT_TRUE(p->BuildAndParseInternalModule());
147 ASSERT_TRUE(p->error().empty()) << p->error();
148 Program program = p->program();
149 const auto program_ast = test::ToString(program);
150 EXPECT_THAT(program_ast, HasSubstr(R"(
151 [[stage(fragment)]]
152 fn main() {
153 )"));
154 }
155
TEST_F(SpvParserTest,EmitFunctions_Function_EntryPoint_GLCompute)156 TEST_F(SpvParserTest, EmitFunctions_Function_EntryPoint_GLCompute) {
157 std::string input = Caps() + R"(
158 OpEntryPoint GLCompute %main "main"
159 OpExecutionMode %main LocalSize 1 1 1
160 )" + Names({"main"}) + CommonTypes() +
161 MainBody();
162
163 auto p = parser(test::Assemble(input));
164 ASSERT_TRUE(p->BuildAndParseInternalModule());
165 ASSERT_TRUE(p->error().empty()) << p->error();
166 Program program = p->program();
167 const auto program_ast = test::ToString(program);
168 EXPECT_THAT(program_ast, HasSubstr(R"(
169 [[stage(compute), workgroup_size(1, 1, 1)]]
170 fn main() {
171 )"));
172 }
173
TEST_F(SpvParserTest,EmitFunctions_Function_EntryPoint_MultipleEntryPoints)174 TEST_F(SpvParserTest, EmitFunctions_Function_EntryPoint_MultipleEntryPoints) {
175 std::string input = Caps() +
176 R"(
177 OpEntryPoint Fragment %main "first_shader"
178 OpEntryPoint Fragment %main "second_shader"
179 OpExecutionMode %main OriginUpperLeft
180 )" + Names({"main"}) + CommonTypes() +
181 MainBody();
182
183 auto p = parser(test::Assemble(input));
184 ASSERT_TRUE(p->BuildAndParseInternalModule());
185 ASSERT_TRUE(p->error().empty()) << p->error();
186 Program program = p->program();
187 const auto program_ast = test::ToString(program);
188 EXPECT_THAT(program_ast, HasSubstr(R"(
189 [[stage(fragment)]]
190 fn first_shader() {
191 )"));
192 EXPECT_THAT(program_ast, HasSubstr(R"(
193 [[stage(fragment)]]
194 fn second_shader() {
195 )"));
196 }
197
TEST_F(SpvParserTest,EmitFunctions_Function_EntryPoint_GLCompute_LocalSize_Only)198 TEST_F(SpvParserTest,
199 EmitFunctions_Function_EntryPoint_GLCompute_LocalSize_Only) {
200 std::string input = Caps() + R"(
201 OpEntryPoint GLCompute %main "comp_main"
202 OpExecutionMode %main LocalSize 2 4 8
203 )" + Names({"main"}) + CommonTypes() +
204 R"(
205 %main = OpFunction %void None %voidfn
206 %entry = OpLabel
207 OpReturn
208 OpFunctionEnd)";
209
210 auto p = parser(test::Assemble(input));
211 ASSERT_TRUE(p->BuildAndParseInternalModule());
212 ASSERT_TRUE(p->error().empty()) << p->error();
213 Program program = p->program();
214 const auto program_ast = test::ToString(program);
215 EXPECT_THAT(program_ast, HasSubstr(R"(
216 [[stage(compute), workgroup_size(2, 4, 8)]]
217 fn comp_main() {
218 )")) << program_ast;
219 }
220
TEST_F(SpvParserTest,EmitFunctions_Function_EntryPoint_WorkgroupSizeBuiltin_Constant_Only)221 TEST_F(SpvParserTest,
222 EmitFunctions_Function_EntryPoint_WorkgroupSizeBuiltin_Constant_Only) {
223 std::string input = Caps() + R"(OpEntryPoint GLCompute %main "comp_main"
224 OpDecorate %wgsize BuiltIn WorkgroupSize
225 )" + CommonTypes() + R"(
226 %uvec3 = OpTypeVector %uint 3
227 %uint_3 = OpConstant %uint 3
228 %uint_5 = OpConstant %uint 5
229 %uint_7 = OpConstant %uint 7
230 %wgsize = OpConstantComposite %uvec3 %uint_3 %uint_5 %uint_7
231 %main = OpFunction %void None %voidfn
232 %entry = OpLabel
233 OpReturn
234 OpFunctionEnd)";
235
236 auto p = parser(test::Assemble(input));
237 ASSERT_TRUE(p->BuildAndParseInternalModule());
238 ASSERT_TRUE(p->error().empty()) << p->error();
239 Program program = p->program();
240 const auto program_ast = test::ToString(program);
241 EXPECT_THAT(program_ast, HasSubstr(R"(
242 [[stage(compute), workgroup_size(3, 5, 7)]]
243 fn comp_main() {
244 )")) << program_ast;
245 }
246
TEST_F(SpvParserTest,EmitFunctions_Function_EntryPoint_WorkgroupSizeBuiltin_SpecConstant_Only)247 TEST_F(
248 SpvParserTest,
249 EmitFunctions_Function_EntryPoint_WorkgroupSizeBuiltin_SpecConstant_Only) {
250 std::string input = Caps() +
251 R"(OpEntryPoint GLCompute %main "comp_main"
252 OpDecorate %wgsize BuiltIn WorkgroupSize
253 OpDecorate %uint_3 SpecId 0
254 OpDecorate %uint_5 SpecId 1
255 OpDecorate %uint_7 SpecId 2
256 )" + CommonTypes() + R"(
257 %uvec3 = OpTypeVector %uint 3
258 %uint_3 = OpSpecConstant %uint 3
259 %uint_5 = OpSpecConstant %uint 5
260 %uint_7 = OpSpecConstant %uint 7
261 %wgsize = OpSpecConstantComposite %uvec3 %uint_3 %uint_5 %uint_7
262 %main = OpFunction %void None %voidfn
263 %entry = OpLabel
264 OpReturn
265 OpFunctionEnd)";
266
267 auto p = parser(test::Assemble(input));
268 ASSERT_TRUE(p->BuildAndParseInternalModule());
269 ASSERT_TRUE(p->error().empty()) << p->error();
270 Program program = p->program();
271 const auto program_ast = test::ToString(program);
272 EXPECT_THAT(program_ast, HasSubstr(R"(
273 [[stage(compute), workgroup_size(3, 5, 7)]]
274 fn comp_main() {
275 )")) << program_ast;
276 }
277
TEST_F(SpvParserTest,EmitFunctions_Function_EntryPoint_WorkgroupSize_MixedConstantSpecConstant)278 TEST_F(
279 SpvParserTest,
280 EmitFunctions_Function_EntryPoint_WorkgroupSize_MixedConstantSpecConstant) {
281 std::string input = Caps() +
282 R"(OpEntryPoint GLCompute %main "comp_main"
283 OpDecorate %wgsize BuiltIn WorkgroupSize
284 OpDecorate %uint_3 SpecId 0
285 OpDecorate %uint_7 SpecId 2
286 )" + CommonTypes() + R"(
287 %uvec3 = OpTypeVector %uint 3
288 %uint_3 = OpSpecConstant %uint 3
289 %uint_5 = OpConstant %uint 5
290 %uint_7 = OpSpecConstant %uint 7
291 %wgsize = OpSpecConstantComposite %uvec3 %uint_3 %uint_5 %uint_7
292 %main = OpFunction %void None %voidfn
293 %entry = OpLabel
294 OpReturn
295 OpFunctionEnd)";
296
297 auto p = parser(test::Assemble(input));
298 ASSERT_TRUE(p->BuildAndParseInternalModule());
299 ASSERT_TRUE(p->error().empty()) << p->error();
300 Program program = p->program();
301 const auto program_ast = test::ToString(program);
302 EXPECT_THAT(program_ast, HasSubstr(R"(
303 [[stage(compute), workgroup_size(3, 5, 7)]]
304 fn comp_main() {
305 )")) << program_ast;
306 }
307
TEST_F(SpvParserTest,EmitFunctions_Function_EntryPoint_LocalSize_And_WGSBuiltin_SpecConstant)308 TEST_F(
309 SpvParserTest,
310 // I had to shorten the name to pass the linter.
311 EmitFunctions_Function_EntryPoint_LocalSize_And_WGSBuiltin_SpecConstant) {
312 // WorkgroupSize builtin wins.
313 std::string input = Caps() +
314 R"(OpEntryPoint GLCompute %main "comp_main"
315 OpExecutionMode %main LocalSize 2 4 8
316 OpDecorate %wgsize BuiltIn WorkgroupSize
317 OpDecorate %uint_3 SpecId 0
318 OpDecorate %uint_5 SpecId 1
319 OpDecorate %uint_7 SpecId 2
320 )" + CommonTypes() + R"(
321 %uvec3 = OpTypeVector %uint 3
322 %uint_3 = OpSpecConstant %uint 3
323 %uint_5 = OpSpecConstant %uint 5
324 %uint_7 = OpSpecConstant %uint 7
325 %wgsize = OpSpecConstantComposite %uvec3 %uint_3 %uint_5 %uint_7
326 %main = OpFunction %void None %voidfn
327 %entry = OpLabel
328 OpReturn
329 OpFunctionEnd)";
330
331 auto p = parser(test::Assemble(input));
332 ASSERT_TRUE(p->BuildAndParseInternalModule());
333 ASSERT_TRUE(p->error().empty()) << p->error();
334 Program program = p->program();
335 const auto program_ast = test::ToString(program);
336 EXPECT_THAT(program_ast, HasSubstr(R"(
337 [[stage(compute), workgroup_size(3, 5, 7)]]
338 fn comp_main() {
339 )")) << program_ast;
340 }
341
TEST_F(SpvParserTest,EmitFunctions_VoidFunctionWithoutParams)342 TEST_F(SpvParserTest, EmitFunctions_VoidFunctionWithoutParams) {
343 auto p = parser(test::Assemble(Preamble() + Names({"another_function"}) +
344 CommonTypes() + R"(
345 %another_function = OpFunction %void None %voidfn
346 %entry = OpLabel
347 OpReturn
348 OpFunctionEnd
349 )" + MainBody()));
350 EXPECT_TRUE(p->BuildAndParseInternalModule());
351 EXPECT_TRUE(p->error().empty());
352 Program program = p->program();
353 const auto program_ast = test::ToString(program);
354 EXPECT_THAT(program_ast, HasSubstr(R"(fn another_function() {
355 )"));
356 }
357
TEST_F(SpvParserTest,EmitFunctions_CalleePrecedesCaller)358 TEST_F(SpvParserTest, EmitFunctions_CalleePrecedesCaller) {
359 auto p = parser(test::Assemble(
360 Preamble() +
361 Names({"root", "branch", "leaf", "leaf_result", "branch_result"}) +
362 CommonTypes() + R"(
363 %uintfn = OpTypeFunction %uint
364 %uint_0 = OpConstant %uint 0
365
366 %root = OpFunction %void None %voidfn
367 %root_entry = OpLabel
368 %branch_result = OpFunctionCall %uint %branch
369 OpReturn
370 OpFunctionEnd
371
372 %branch = OpFunction %uint None %uintfn
373 %branch_entry = OpLabel
374 %leaf_result = OpFunctionCall %uint %leaf
375 OpReturnValue %leaf_result
376 OpFunctionEnd
377
378 %leaf = OpFunction %uint None %uintfn
379 %leaf_entry = OpLabel
380 OpReturnValue %uint_0
381 OpFunctionEnd
382 )" + MainBody()));
383 EXPECT_TRUE(p->BuildAndParseInternalModule());
384 EXPECT_TRUE(p->error().empty());
385 Program program = p->program();
386 const auto program_ast = test::ToString(program);
387 EXPECT_THAT(program_ast, HasSubstr(R"(fn leaf() -> u32 {
388 return 0u;
389 }
390
391 fn branch() -> u32 {
392 let leaf_result : u32 = leaf();
393 return leaf_result;
394 }
395
396 fn root() {
397 let branch_result : u32 = branch();
398 return;
399 }
400 )")) << program_ast;
401 }
402
TEST_F(SpvParserTest,EmitFunctions_NonVoidResultType)403 TEST_F(SpvParserTest, EmitFunctions_NonVoidResultType) {
404 auto p = parser(
405 test::Assemble(Preamble() + Names({"ret_float"}) + CommonTypes() + R"(
406 %float_0 = OpConstant %float 0.0
407 %fn_ret_float = OpTypeFunction %float
408
409 %ret_float = OpFunction %float None %fn_ret_float
410 %ret_float_entry = OpLabel
411 OpReturnValue %float_0
412 OpFunctionEnd
413 )" + MainBody()));
414 EXPECT_TRUE(p->BuildAndParseInternalModule());
415 EXPECT_TRUE(p->error().empty());
416 Program program = p->program();
417 const auto program_ast = test::ToString(program);
418 EXPECT_THAT(program_ast, HasSubstr(R"(fn ret_float() -> f32 {
419 return 0.0;
420 }
421 )")) << program_ast;
422 }
423
TEST_F(SpvParserTest,EmitFunctions_MixedParamTypes)424 TEST_F(SpvParserTest, EmitFunctions_MixedParamTypes) {
425 auto p = parser(test::Assemble(
426 Preamble() + Names({"mixed_params", "a", "b", "c"}) + CommonTypes() + R"(
427 %fn_mixed_params = OpTypeFunction %void %uint %float %int
428
429 %mixed_params = OpFunction %void None %fn_mixed_params
430 %a = OpFunctionParameter %uint
431 %b = OpFunctionParameter %float
432 %c = OpFunctionParameter %int
433 %mixed_entry = OpLabel
434 OpReturn
435 OpFunctionEnd
436 )" + MainBody()));
437 EXPECT_TRUE(p->BuildAndParseInternalModule());
438 EXPECT_TRUE(p->error().empty());
439 Program program = p->program();
440 const auto program_ast = test::ToString(program);
441 EXPECT_THAT(program_ast,
442 HasSubstr(R"(fn mixed_params(a : u32, b : f32, c : i32) {
443 return;
444 }
445 )"));
446 }
447
TEST_F(SpvParserTest,EmitFunctions_GenerateParamNames)448 TEST_F(SpvParserTest, EmitFunctions_GenerateParamNames) {
449 auto p = parser(
450 test::Assemble(Preamble() + Names({"mixed_params"}) + CommonTypes() + R"(
451 %fn_mixed_params = OpTypeFunction %void %uint %float %int
452
453 %mixed_params = OpFunction %void None %fn_mixed_params
454 %14 = OpFunctionParameter %uint
455 %15 = OpFunctionParameter %float
456 %16 = OpFunctionParameter %int
457 %mixed_entry = OpLabel
458 OpReturn
459 OpFunctionEnd
460 )" + MainBody()));
461 EXPECT_TRUE(p->BuildAndParseInternalModule());
462 EXPECT_TRUE(p->error().empty());
463 Program program = p->program();
464 const auto program_ast = test::ToString(program);
465 EXPECT_THAT(program_ast,
466 HasSubstr(R"(fn mixed_params(x_14 : u32, x_15 : f32, x_16 : i32) {
467 return;
468 }
469 )")) << program_ast;
470 }
471
472 } // namespace
473 } // namespace spirv
474 } // namespace reader
475 } // namespace tint
476