• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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