1 // Copyright (c) 2017 Google Inc.
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 <string>
16 #include <vector>
17
18 #include "gmock/gmock.h"
19 #include "spirv-tools/libspirv.hpp"
20 #include "spirv-tools/optimizer.hpp"
21 #include "test/opt/pass_fixture.h"
22
23 namespace spvtools {
24 namespace opt {
25 namespace {
26
27 using ::testing::Eq;
28
29 // Return a string that contains the minimum instructions needed to form
30 // a valid module. Other instructions can be appended to this string.
Header()31 std::string Header() {
32 return R"(OpCapability Shader
33 OpCapability Linkage
34 OpMemoryModel Logical GLSL450
35 )";
36 }
37
TEST(Optimizer,CanRunNullPassWithDistinctInputOutputVectors)38 TEST(Optimizer, CanRunNullPassWithDistinctInputOutputVectors) {
39 SpirvTools tools(SPV_ENV_UNIVERSAL_1_0);
40 std::vector<uint32_t> binary_in;
41 tools.Assemble(Header() + "OpName %foo \"foo\"\n%foo = OpTypeVoid",
42 &binary_in);
43
44 Optimizer opt(SPV_ENV_UNIVERSAL_1_0);
45 opt.RegisterPass(CreateNullPass());
46 std::vector<uint32_t> binary_out;
47 opt.Run(binary_in.data(), binary_in.size(), &binary_out);
48
49 std::string disassembly;
50 tools.Disassemble(binary_out.data(), binary_out.size(), &disassembly);
51 EXPECT_THAT(disassembly,
52 Eq(Header() + "OpName %foo \"foo\"\n%foo = OpTypeVoid\n"));
53 }
54
TEST(Optimizer,CanRunTransformingPassWithDistinctInputOutputVectors)55 TEST(Optimizer, CanRunTransformingPassWithDistinctInputOutputVectors) {
56 SpirvTools tools(SPV_ENV_UNIVERSAL_1_0);
57 std::vector<uint32_t> binary_in;
58 tools.Assemble(Header() + "OpName %foo \"foo\"\n%foo = OpTypeVoid",
59 &binary_in);
60
61 Optimizer opt(SPV_ENV_UNIVERSAL_1_0);
62 opt.RegisterPass(CreateStripDebugInfoPass());
63 std::vector<uint32_t> binary_out;
64 opt.Run(binary_in.data(), binary_in.size(), &binary_out);
65
66 std::string disassembly;
67 tools.Disassemble(binary_out.data(), binary_out.size(), &disassembly);
68 EXPECT_THAT(disassembly, Eq(Header() + "%void = OpTypeVoid\n"));
69 }
70
TEST(Optimizer,CanRunNullPassWithAliasedVectors)71 TEST(Optimizer, CanRunNullPassWithAliasedVectors) {
72 SpirvTools tools(SPV_ENV_UNIVERSAL_1_0);
73 std::vector<uint32_t> binary;
74 tools.Assemble("OpName %foo \"foo\"\n%foo = OpTypeVoid", &binary);
75
76 Optimizer opt(SPV_ENV_UNIVERSAL_1_0);
77 opt.RegisterPass(CreateNullPass());
78 opt.Run(binary.data(), binary.size(), &binary); // This is the key.
79
80 std::string disassembly;
81 tools.Disassemble(binary.data(), binary.size(), &disassembly);
82 EXPECT_THAT(disassembly, Eq("OpName %foo \"foo\"\n%foo = OpTypeVoid\n"));
83 }
84
TEST(Optimizer,CanRunNullPassWithAliasedVectorDataButDifferentSize)85 TEST(Optimizer, CanRunNullPassWithAliasedVectorDataButDifferentSize) {
86 SpirvTools tools(SPV_ENV_UNIVERSAL_1_0);
87 std::vector<uint32_t> binary;
88 tools.Assemble(Header() + "OpName %foo \"foo\"\n%foo = OpTypeVoid", &binary);
89
90 Optimizer opt(SPV_ENV_UNIVERSAL_1_0);
91 opt.RegisterPass(CreateNullPass());
92 auto orig_size = binary.size();
93 // Now change the size. Add a word that will be ignored
94 // by the optimizer.
95 binary.push_back(42);
96 EXPECT_THAT(orig_size + 1, Eq(binary.size()));
97 opt.Run(binary.data(), orig_size, &binary); // This is the key.
98 // The binary vector should have been rewritten.
99 EXPECT_THAT(binary.size(), Eq(orig_size));
100
101 std::string disassembly;
102 tools.Disassemble(binary.data(), binary.size(), &disassembly);
103 EXPECT_THAT(disassembly,
104 Eq(Header() + "OpName %foo \"foo\"\n%foo = OpTypeVoid\n"));
105 }
106
TEST(Optimizer,CanRunTransformingPassWithAliasedVectors)107 TEST(Optimizer, CanRunTransformingPassWithAliasedVectors) {
108 SpirvTools tools(SPV_ENV_UNIVERSAL_1_0);
109 std::vector<uint32_t> binary;
110 tools.Assemble(Header() + "OpName %foo \"foo\"\n%foo = OpTypeVoid", &binary);
111
112 Optimizer opt(SPV_ENV_UNIVERSAL_1_0);
113 opt.RegisterPass(CreateStripDebugInfoPass());
114 opt.Run(binary.data(), binary.size(), &binary); // This is the key
115
116 std::string disassembly;
117 tools.Disassemble(binary.data(), binary.size(), &disassembly);
118 EXPECT_THAT(disassembly, Eq(Header() + "%void = OpTypeVoid\n"));
119 }
120
TEST(Optimizer,CanValidateFlags)121 TEST(Optimizer, CanValidateFlags) {
122 Optimizer opt(SPV_ENV_UNIVERSAL_1_0);
123 EXPECT_FALSE(opt.FlagHasValidForm("bad-flag"));
124 EXPECT_TRUE(opt.FlagHasValidForm("-O"));
125 EXPECT_TRUE(opt.FlagHasValidForm("-Os"));
126 EXPECT_FALSE(opt.FlagHasValidForm("-O2"));
127 EXPECT_TRUE(opt.FlagHasValidForm("--this_flag"));
128 }
129
TEST(Optimizer,CanRegisterPassesFromFlags)130 TEST(Optimizer, CanRegisterPassesFromFlags) {
131 SpirvTools tools(SPV_ENV_UNIVERSAL_1_0);
132 Optimizer opt(SPV_ENV_UNIVERSAL_1_0);
133
134 spv_message_level_t msg_level;
135 const char* msg_fname;
136 spv_position_t msg_position;
137 const char* msg;
138 auto examine_message = [&msg_level, &msg_fname, &msg_position, &msg](
139 spv_message_level_t ml, const char* f,
140 const spv_position_t& p, const char* m) {
141 msg_level = ml;
142 msg_fname = f;
143 msg_position = p;
144 msg = m;
145 };
146 opt.SetMessageConsumer(examine_message);
147
148 std::vector<std::string> pass_flags = {
149 "--strip-debug",
150 "--strip-reflect",
151 "--set-spec-const-default-value=23:42 21:12",
152 "--if-conversion",
153 "--freeze-spec-const",
154 "--inline-entry-points-exhaustive",
155 "--inline-entry-points-opaque",
156 "--convert-local-access-chains",
157 "--eliminate-dead-code-aggressive",
158 "--eliminate-insert-extract",
159 "--eliminate-local-single-block",
160 "--eliminate-local-single-store",
161 "--merge-blocks",
162 "--merge-return",
163 "--eliminate-dead-branches",
164 "--eliminate-dead-functions",
165 "--eliminate-local-multi-store",
166 "--eliminate-dead-const",
167 "--eliminate-dead-inserts",
168 "--eliminate-dead-variables",
169 "--fold-spec-const-op-composite",
170 "--loop-unswitch",
171 "--scalar-replacement=300",
172 "--scalar-replacement",
173 "--strength-reduction",
174 "--unify-const",
175 "--flatten-decorations",
176 "--compact-ids",
177 "--cfg-cleanup",
178 "--local-redundancy-elimination",
179 "--loop-invariant-code-motion",
180 "--reduce-load-size",
181 "--redundancy-elimination",
182 "--private-to-local",
183 "--remove-duplicates",
184 "--workaround-1209",
185 "--replace-invalid-opcode",
186 "--simplify-instructions",
187 "--ssa-rewrite",
188 "--copy-propagate-arrays",
189 "--loop-fission=20",
190 "--loop-fusion=2",
191 "--loop-unroll",
192 "--vector-dce",
193 "--loop-unroll-partial=3",
194 "--loop-peeling",
195 "--ccp",
196 "-O",
197 "-Os",
198 "--legalize-hlsl"};
199 EXPECT_TRUE(opt.RegisterPassesFromFlags(pass_flags));
200
201 // Test some invalid flags.
202 EXPECT_FALSE(opt.RegisterPassFromFlag("-O2"));
203 EXPECT_EQ(msg_level, SPV_MSG_ERROR);
204
205 EXPECT_FALSE(opt.RegisterPassFromFlag("-loop-unroll"));
206 EXPECT_EQ(msg_level, SPV_MSG_ERROR);
207
208 EXPECT_FALSE(opt.RegisterPassFromFlag("--set-spec-const-default-value"));
209 EXPECT_EQ(msg_level, SPV_MSG_ERROR);
210
211 EXPECT_FALSE(opt.RegisterPassFromFlag("--scalar-replacement=s"));
212 EXPECT_EQ(msg_level, SPV_MSG_ERROR);
213
214 EXPECT_FALSE(opt.RegisterPassFromFlag("--loop-fission=-4"));
215 EXPECT_EQ(msg_level, SPV_MSG_ERROR);
216
217 EXPECT_FALSE(opt.RegisterPassFromFlag("--loop-fusion=xx"));
218 EXPECT_EQ(msg_level, SPV_MSG_ERROR);
219
220 EXPECT_FALSE(opt.RegisterPassFromFlag("--loop-unroll-partial"));
221 EXPECT_EQ(msg_level, SPV_MSG_ERROR);
222 }
223
TEST(Optimizer,VulkanToWebGPUSetsCorrectPasses)224 TEST(Optimizer, VulkanToWebGPUSetsCorrectPasses) {
225 Optimizer opt(SPV_ENV_VULKAN_1_1);
226 opt.RegisterVulkanToWebGPUPasses();
227 std::vector<const char*> pass_names = opt.GetPassNames();
228
229 std::vector<std::string> registered_passes;
230 for (auto name = pass_names.begin(); name != pass_names.end(); ++name)
231 registered_passes.push_back(*name);
232
233 std::vector<std::string> expected_passes = {"eliminate-dead-branches",
234 "eliminate-dead-code-aggressive",
235 "eliminate-dead-const",
236 "flatten-decorations",
237 "strip-atomic-counter-memory",
238 "generate-webgpu-initializers",
239 "legalize-vector-shuffle",
240 "split-invalid-unreachable",
241 "compact-ids"};
242 std::sort(registered_passes.begin(), registered_passes.end());
243 std::sort(expected_passes.begin(), expected_passes.end());
244
245 ASSERT_EQ(registered_passes.size(), expected_passes.size());
246 for (size_t i = 0; i < registered_passes.size(); i++)
247 EXPECT_EQ(registered_passes[i], expected_passes[i]);
248 }
249
250 struct VulkanToWebGPUPassCase {
251 // Input SPIR-V
252 std::string input;
253 // Expected result SPIR-V
254 std::string expected;
255 // Specific pass under test, used for logging messages.
256 std::string pass;
257 };
258
259 using VulkanToWebGPUPassTest =
260 PassTest<::testing::TestWithParam<VulkanToWebGPUPassCase>>;
261
TEST_P(VulkanToWebGPUPassTest,Ran)262 TEST_P(VulkanToWebGPUPassTest, Ran) {
263 std::vector<uint32_t> binary;
264 {
265 SpirvTools tools(SPV_ENV_VULKAN_1_1);
266 tools.Assemble(GetParam().input, &binary);
267 }
268
269 Optimizer opt(SPV_ENV_VULKAN_1_1);
270 opt.RegisterVulkanToWebGPUPasses();
271
272 std::vector<uint32_t> optimized;
273 class ValidatorOptions validator_options;
274 ASSERT_TRUE(opt.Run(binary.data(), binary.size(), &optimized,
275 validator_options, true))
276 << GetParam().input << "\n";
277 std::string disassembly;
278 {
279 SpirvTools tools(SPV_ENV_WEBGPU_0);
280 tools.Disassemble(optimized.data(), optimized.size(), &disassembly);
281 }
282
283 EXPECT_EQ(GetParam().expected, disassembly)
284 << "Was expecting pass '" << GetParam().pass << "' to have been run.\n";
285 }
286
287 INSTANTIATE_TEST_SUITE_P(
288 Optimizer, VulkanToWebGPUPassTest,
289 ::testing::ValuesIn(std::vector<VulkanToWebGPUPassCase>{
290 // FlattenDecorations
291 {// input
292 "OpCapability Shader\n"
293 "OpCapability VulkanMemoryModel\n"
294 "OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
295 "OpMemoryModel Logical Vulkan\n"
296 "OpEntryPoint Fragment %main \"main\" %hue %saturation %value\n"
297 "OpExecutionMode %main OriginUpperLeft\n"
298 "OpDecorate %group Flat\n"
299 "OpDecorate %group NoPerspective\n"
300 "%group = OpDecorationGroup\n"
301 "%void = OpTypeVoid\n"
302 "%void_fn = OpTypeFunction %void\n"
303 "%float = OpTypeFloat 32\n"
304 "%_ptr_Input_float = OpTypePointer Input %float\n"
305 "%hue = OpVariable %_ptr_Input_float Input\n"
306 "%saturation = OpVariable %_ptr_Input_float Input\n"
307 "%value = OpVariable %_ptr_Input_float Input\n"
308 "%main = OpFunction %void None %void_fn\n"
309 "%entry = OpLabel\n"
310 "OpReturn\n"
311 "OpFunctionEnd\n",
312 // expected
313 "OpCapability Shader\n"
314 "OpCapability VulkanMemoryModel\n"
315 "OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
316 "OpMemoryModel Logical Vulkan\n"
317 "OpEntryPoint Fragment %1 \"main\" %2 %3 %4\n"
318 "OpExecutionMode %1 OriginUpperLeft\n"
319 "%void = OpTypeVoid\n"
320 "%6 = OpTypeFunction %void\n"
321 "%float = OpTypeFloat 32\n"
322 "%_ptr_Input_float = OpTypePointer Input %float\n"
323 "%2 = OpVariable %_ptr_Input_float Input\n"
324 "%3 = OpVariable %_ptr_Input_float Input\n"
325 "%4 = OpVariable %_ptr_Input_float Input\n"
326 "%1 = OpFunction %void None %6\n"
327 "%9 = OpLabel\n"
328 "OpReturn\n"
329 "OpFunctionEnd\n",
330 // pass
331 "flatten-decorations"},
332 // Eliminate Dead Constants
333 {// input
334 "OpCapability Shader\n"
335 "OpCapability VulkanMemoryModel\n"
336 "OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
337 "OpMemoryModel Logical Vulkan\n"
338 "OpEntryPoint Vertex %func \"shader\"\n"
339 "%u32 = OpTypeInt 32 0\n"
340 "%u32_ptr = OpTypePointer Workgroup %u32\n"
341 "%u32_var = OpVariable %u32_ptr Workgroup\n"
342 "%u32_1 = OpConstant %u32 1\n"
343 "%cross_device = OpConstant %u32 0\n"
344 "%relaxed = OpConstant %u32 0\n"
345 "%acquire_release_atomic_counter_workgroup = OpConstant %u32 1288\n"
346 "%void = OpTypeVoid\n"
347 "%void_f = OpTypeFunction %void\n"
348 "%func = OpFunction %void None %void_f\n"
349 "%label = OpLabel\n"
350 "OpReturn\n"
351 "OpFunctionEnd\n",
352 // expected
353 "OpCapability Shader\n"
354 "OpCapability VulkanMemoryModel\n"
355 "OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
356 "OpMemoryModel Logical Vulkan\n"
357 "OpEntryPoint Vertex %1 \"shader\"\n"
358 "%uint = OpTypeInt 32 0\n"
359 "%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint\n"
360 "%4 = OpVariable %_ptr_Workgroup_uint Workgroup\n"
361 "%void = OpTypeVoid\n"
362 "%6 = OpTypeFunction %void\n"
363 "%1 = OpFunction %void None %6\n"
364 "%7 = OpLabel\n"
365 "OpReturn\n"
366 "OpFunctionEnd\n",
367 "eliminate-dead-const"},
368 // Strip Atomic Counter Memory
369 {// input
370 "OpCapability Shader\n"
371 "OpCapability VulkanMemoryModel\n"
372 "OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
373 "OpMemoryModel Logical Vulkan\n"
374 "OpEntryPoint Vertex %func \"shader\"\n"
375 "%u32 = OpTypeInt 32 0\n"
376 "%u32_ptr = OpTypePointer Workgroup %u32\n"
377 "%u32_var = OpVariable %u32_ptr Workgroup\n"
378 "%u32_0 = OpConstant %u32 0\n"
379 "%u32_1 = OpConstant %u32 1\n"
380 "%cross_device = OpConstant %u32 0\n"
381 "%acquire_release_atomic_counter_workgroup = OpConstant %u32 1288\n"
382 "%void = OpTypeVoid\n"
383 "%void_f = OpTypeFunction %void\n"
384 "%func = OpFunction %void None %void_f\n"
385 "%label = OpLabel\n"
386 " OpAtomicStore %u32_var %cross_device "
387 "%acquire_release_atomic_counter_workgroup %u32_1\n"
388 "%val1 = OpAtomicIIncrement %u32 %u32_var %cross_device "
389 "%acquire_release_atomic_counter_workgroup\n"
390 "%val2 = OpAtomicCompareExchange %u32 %u32_var %cross_device "
391 "%acquire_release_atomic_counter_workgroup "
392 "%acquire_release_atomic_counter_workgroup %u32_0 %u32_0\n"
393 "OpReturn\n"
394 "OpFunctionEnd\n",
395 // expected
396 "OpCapability Shader\n"
397 "OpCapability VulkanMemoryModel\n"
398 "OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
399 "OpMemoryModel Logical Vulkan\n"
400 "OpEntryPoint Vertex %1 \"shader\"\n"
401 "%uint = OpTypeInt 32 0\n"
402 "%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint\n"
403 "%4 = OpVariable %_ptr_Workgroup_uint Workgroup\n"
404 "%uint_0 = OpConstant %uint 0\n"
405 "%uint_1 = OpConstant %uint 1\n"
406 "%uint_0_0 = OpConstant %uint 0\n"
407 "%void = OpTypeVoid\n"
408 "%9 = OpTypeFunction %void\n"
409 "%uint_264 = OpConstant %uint 264\n"
410 "%1 = OpFunction %void None %9\n"
411 "%11 = OpLabel\n"
412 "OpAtomicStore %4 %uint_0_0 %uint_264 %uint_1\n"
413 "%12 = OpAtomicIIncrement %uint %4 %uint_0_0 %uint_264\n"
414 "%13 = OpAtomicCompareExchange %uint %4 %uint_0_0 %uint_264 %uint_264 "
415 "%uint_0 %uint_0\n"
416 "OpReturn\n"
417 "OpFunctionEnd\n",
418 // pass
419 "strip-atomic-counter-memory"},
420 // Generate WebGPU Initializers
421 {// input
422 "OpCapability Shader\n"
423 "OpCapability VulkanMemoryModel\n"
424 "OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
425 "OpMemoryModel Logical Vulkan\n"
426 "OpEntryPoint Vertex %func \"shader\"\n"
427 "%u32 = OpTypeInt 32 0\n"
428 "%u32_ptr = OpTypePointer Private %u32\n"
429 "%u32_var = OpVariable %u32_ptr Private\n"
430 "%u32_0 = OpConstant %u32 0\n"
431 "%void = OpTypeVoid\n"
432 "%void_f = OpTypeFunction %void\n"
433 "%func = OpFunction %void None %void_f\n"
434 "%label = OpLabel\n"
435 "OpStore %u32_var %u32_0\n"
436 "OpReturn\n"
437 "OpFunctionEnd\n",
438 // expected
439 "OpCapability Shader\n"
440 "OpCapability VulkanMemoryModel\n"
441 "OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
442 "OpMemoryModel Logical Vulkan\n"
443 "OpEntryPoint Vertex %1 \"shader\"\n"
444 "%uint = OpTypeInt 32 0\n"
445 "%_ptr_Private_uint = OpTypePointer Private %uint\n"
446 "%4 = OpConstantNull %uint\n"
447 "%5 = OpVariable %_ptr_Private_uint Private %4\n"
448 "%uint_0 = OpConstant %uint 0\n"
449 "%void = OpTypeVoid\n"
450 "%8 = OpTypeFunction %void\n"
451 "%1 = OpFunction %void None %8\n"
452 "%9 = OpLabel\n"
453 "OpStore %5 %uint_0\n"
454 "OpReturn\n"
455 "OpFunctionEnd\n",
456 // pass
457 "generate-webgpu-initializers"},
458 // Legalize Vector Shuffle
459 {// input
460 "OpCapability Shader\n"
461 "OpCapability VulkanMemoryModel\n"
462 "OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
463 "OpMemoryModel Logical Vulkan\n"
464 "OpEntryPoint Vertex %1 \"shader\"\n"
465 "%uint = OpTypeInt 32 0\n"
466 "%v3uint = OpTypeVector %uint 3\n"
467 "%_ptr_Function_v3uint = OpTypePointer Function %v3uint\n"
468 "%void = OpTypeVoid\n"
469 "%6 = OpTypeFunction %void\n"
470 "%1 = OpFunction %void None %6\n"
471 "%7 = OpLabel\n"
472 "%8 = OpVariable %_ptr_Function_v3uint Function\n"
473 "%9 = OpLoad %v3uint %8\n"
474 "%10 = OpLoad %v3uint %8\n"
475 "%11 = OpVectorShuffle %v3uint %9 %10 2 1 0xFFFFFFFF\n"
476 "OpReturn\n"
477 "OpFunctionEnd\n",
478 // expected
479 "OpCapability Shader\n"
480 "OpCapability VulkanMemoryModel\n"
481 "OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
482 "OpMemoryModel Logical Vulkan\n"
483 "OpEntryPoint Vertex %1 \"shader\"\n"
484 "%uint = OpTypeInt 32 0\n"
485 "%v3uint = OpTypeVector %uint 3\n"
486 "%_ptr_Function_v3uint = OpTypePointer Function %v3uint\n"
487 "%void = OpTypeVoid\n"
488 "%6 = OpTypeFunction %void\n"
489 "%7 = OpConstantNull %v3uint\n"
490 "%1 = OpFunction %void None %6\n"
491 "%8 = OpLabel\n"
492 "%9 = OpVariable %_ptr_Function_v3uint Function %7\n"
493 "%10 = OpLoad %v3uint %9\n"
494 "%11 = OpLoad %v3uint %9\n"
495 "%12 = OpVectorShuffle %v3uint %10 %11 2 1 0\n"
496 "OpReturn\n"
497 "OpFunctionEnd\n",
498 // pass
499 "legalize-vector-shuffle"},
500 // Split Invalid Unreachable
501 {// input
502 "OpCapability Shader\n"
503 "OpCapability VulkanMemoryModel\n"
504 "OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
505 "OpMemoryModel Logical Vulkan\n"
506 "OpEntryPoint Vertex %1 \"shader\"\n"
507 "%uint = OpTypeInt 32 0\n"
508 "%uint_1 = OpConstant %uint 1\n"
509 "%uint_2 = OpConstant %uint 2\n"
510 "%void = OpTypeVoid\n"
511 "%bool = OpTypeBool\n"
512 "%7 = OpTypeFunction %void\n"
513 "%1 = OpFunction %void None %7\n"
514 "%8 = OpLabel\n"
515 "OpBranch %9\n"
516 "%9 = OpLabel\n"
517 "OpLoopMerge %10 %11 None\n"
518 "OpBranch %12\n"
519 "%12 = OpLabel\n"
520 "%13 = OpSLessThan %bool %uint_1 %uint_2\n"
521 "OpSelectionMerge %11 None\n"
522 "OpBranchConditional %13 %14 %15\n"
523 "%14 = OpLabel\n"
524 "OpReturn\n"
525 "%15 = OpLabel\n"
526 "OpReturn\n"
527 "%10 = OpLabel\n"
528 "OpUnreachable\n"
529 "%11 = OpLabel\n"
530 "OpBranch %9\n"
531 "OpFunctionEnd\n",
532 // expected
533 "OpCapability Shader\n"
534 "OpCapability VulkanMemoryModel\n"
535 "OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
536 "OpMemoryModel Logical Vulkan\n"
537 "OpEntryPoint Vertex %1 \"shader\"\n"
538 "%uint = OpTypeInt 32 0\n"
539 "%uint_1 = OpConstant %uint 1\n"
540 "%uint_2 = OpConstant %uint 2\n"
541 "%void = OpTypeVoid\n"
542 "%bool = OpTypeBool\n"
543 "%7 = OpTypeFunction %void\n"
544 "%1 = OpFunction %void None %7\n"
545 "%8 = OpLabel\n"
546 "OpBranch %9\n"
547 "%9 = OpLabel\n"
548 "OpLoopMerge %10 %11 None\n"
549 "OpBranch %12\n"
550 "%12 = OpLabel\n"
551 "%13 = OpSLessThan %bool %uint_1 %uint_2\n"
552 "OpSelectionMerge %14 None\n"
553 "OpBranchConditional %13 %15 %16\n"
554 "%15 = OpLabel\n"
555 "OpReturn\n"
556 "%16 = OpLabel\n"
557 "OpReturn\n"
558 "%10 = OpLabel\n"
559 "OpUnreachable\n"
560 "%14 = OpLabel\n"
561 "OpUnreachable\n"
562 "%11 = OpLabel\n"
563 "OpBranch %9\n"
564 "OpFunctionEnd\n",
565 // pass
566 "split-invalid-unreachable"},
567 // Compact IDs
568 {// input
569 "OpCapability Shader\n"
570 "OpCapability VulkanMemoryModel\n"
571 "OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
572 "OpMemoryModel Logical Vulkan\n"
573 "OpEntryPoint Vertex %1000 \"shader\"\n"
574 "%10 = OpTypeVoid\n"
575 "%100 = OpTypeFunction %10\n"
576 "%1000 = OpFunction %10 None %100\n"
577 "%10000 = OpLabel\n"
578 "OpReturn\n"
579 "OpFunctionEnd\n",
580 // expected
581 "OpCapability Shader\n"
582 "OpCapability VulkanMemoryModel\n"
583 "OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
584 "OpMemoryModel Logical Vulkan\n"
585 "OpEntryPoint Vertex %1 \"shader\"\n"
586 "%void = OpTypeVoid\n"
587 "%3 = OpTypeFunction %void\n"
588 "%1 = OpFunction %void None %3\n"
589 "%4 = OpLabel\n"
590 "OpReturn\n"
591 "OpFunctionEnd\n",
592 // pass
593 "compact-ids"}}));
594
TEST(Optimizer,WebGPUToVulkanSetsCorrectPasses)595 TEST(Optimizer, WebGPUToVulkanSetsCorrectPasses) {
596 Optimizer opt(SPV_ENV_WEBGPU_0);
597 opt.RegisterWebGPUToVulkanPasses();
598 std::vector<const char*> pass_names = opt.GetPassNames();
599
600 std::vector<std::string> registered_passes;
601 for (auto name = pass_names.begin(); name != pass_names.end(); ++name)
602 registered_passes.push_back(*name);
603
604 std::vector<std::string> expected_passes = {"decompose-initialized-variables",
605 "compact-ids"};
606 std::sort(registered_passes.begin(), registered_passes.end());
607 std::sort(expected_passes.begin(), expected_passes.end());
608
609 ASSERT_EQ(registered_passes.size(), expected_passes.size());
610 for (size_t i = 0; i < registered_passes.size(); i++)
611 EXPECT_EQ(registered_passes[i], expected_passes[i]);
612 }
613
614 struct WebGPUToVulkanPassCase {
615 // Input SPIR-V
616 std::string input;
617 // Expected result SPIR-V
618 std::string expected;
619 // Specific pass under test, used for logging messages.
620 std::string pass;
621 };
622
623 using WebGPUToVulkanPassTest =
624 PassTest<::testing::TestWithParam<WebGPUToVulkanPassCase>>;
625
TEST_P(WebGPUToVulkanPassTest,Ran)626 TEST_P(WebGPUToVulkanPassTest, Ran) {
627 std::vector<uint32_t> binary;
628 {
629 SpirvTools tools(SPV_ENV_WEBGPU_0);
630 tools.Assemble(GetParam().input, &binary);
631 }
632
633 Optimizer opt(SPV_ENV_WEBGPU_0);
634 opt.RegisterWebGPUToVulkanPasses();
635
636 std::vector<uint32_t> optimized;
637 class ValidatorOptions validator_options;
638 ASSERT_TRUE(opt.Run(binary.data(), binary.size(), &optimized,
639 validator_options, true));
640 std::string disassembly;
641 {
642 SpirvTools tools(SPV_ENV_VULKAN_1_1);
643 tools.Disassemble(optimized.data(), optimized.size(), &disassembly);
644 }
645
646 EXPECT_EQ(GetParam().expected, disassembly)
647 << "Was expecting pass '" << GetParam().pass << "' to have been run.\n";
648 }
649
650 INSTANTIATE_TEST_SUITE_P(
651 Optimizer, WebGPUToVulkanPassTest,
652 ::testing::ValuesIn(std::vector<WebGPUToVulkanPassCase>{
653 // Decompose Initialized Variables
654 {// input
655 "OpCapability Shader\n"
656 "OpCapability VulkanMemoryModel\n"
657 "OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
658 "OpMemoryModel Logical Vulkan\n"
659 "OpEntryPoint Vertex %1 \"shader\"\n"
660 "%uint = OpTypeInt 32 0\n"
661 "%_ptr_Function_uint = OpTypePointer Function %uint\n"
662 "%4 = OpConstantNull %uint\n"
663 "%void = OpTypeVoid\n"
664 "%6 = OpTypeFunction %void\n"
665 "%1 = OpFunction %void None %6\n"
666 "%7 = OpLabel\n"
667 "%8 = OpVariable %_ptr_Function_uint Function %4\n"
668 "OpReturn\n"
669 "OpFunctionEnd\n",
670 // expected
671 "OpCapability Shader\n"
672 "OpCapability VulkanMemoryModel\n"
673 "OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
674 "OpMemoryModel Logical Vulkan\n"
675 "OpEntryPoint Vertex %1 \"shader\"\n"
676 "%uint = OpTypeInt 32 0\n"
677 "%_ptr_Function_uint = OpTypePointer Function %uint\n"
678 "%4 = OpConstantNull %uint\n"
679 "%void = OpTypeVoid\n"
680 "%6 = OpTypeFunction %void\n"
681 "%1 = OpFunction %void None %6\n"
682 "%7 = OpLabel\n"
683 "%8 = OpVariable %_ptr_Function_uint Function\n"
684 "OpStore %8 %4\n"
685 "OpReturn\n"
686 "OpFunctionEnd\n",
687 // pass
688 "decompose-initialized-variables"},
689 // Compact IDs
690 {// input
691 "OpCapability Shader\n"
692 "OpCapability VulkanMemoryModel\n"
693 "OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
694 "OpMemoryModel Logical Vulkan\n"
695 "OpEntryPoint Vertex %1000 \"shader\"\n"
696 "%10 = OpTypeVoid\n"
697 "%100 = OpTypeFunction %10\n"
698 "%1000 = OpFunction %10 None %100\n"
699 "%10000 = OpLabel\n"
700 "OpReturn\n"
701 "OpFunctionEnd\n",
702 // expected
703 "OpCapability Shader\n"
704 "OpCapability VulkanMemoryModel\n"
705 "OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
706 "OpMemoryModel Logical Vulkan\n"
707 "OpEntryPoint Vertex %1 \"shader\"\n"
708 "%void = OpTypeVoid\n"
709 "%3 = OpTypeFunction %void\n"
710 "%1 = OpFunction %void None %3\n"
711 "%4 = OpLabel\n"
712 "OpReturn\n"
713 "OpFunctionEnd\n",
714 // pass
715 "compact-ids"}}));
716
TEST(Optimizer,RemoveNop)717 TEST(Optimizer, RemoveNop) {
718 // Test that OpNops are removed even if no optimizations are run.
719 const std::string before = R"(OpCapability Shader
720 OpCapability Linkage
721 OpMemoryModel Logical GLSL450
722 %void = OpTypeVoid
723 %2 = OpTypeFunction %void
724 %3 = OpFunction %void None %2
725 %4 = OpLabel
726 OpNop
727 OpReturn
728 OpFunctionEnd
729 )";
730
731 const std::string after = R"(OpCapability Shader
732 OpCapability Linkage
733 OpMemoryModel Logical GLSL450
734 %void = OpTypeVoid
735 %2 = OpTypeFunction %void
736 %3 = OpFunction %void None %2
737 %4 = OpLabel
738 OpReturn
739 OpFunctionEnd
740 )";
741
742 std::vector<uint32_t> binary;
743 {
744 SpirvTools tools(SPV_ENV_VULKAN_1_1);
745 tools.Assemble(before, &binary);
746 }
747
748 Optimizer opt(SPV_ENV_VULKAN_1_1);
749
750 std::vector<uint32_t> optimized;
751 class ValidatorOptions validator_options;
752 ASSERT_TRUE(opt.Run(binary.data(), binary.size(), &optimized,
753 validator_options, true))
754 << before << "\n";
755 std::string disassembly;
756 {
757 SpirvTools tools(SPV_ENV_WEBGPU_0);
758 tools.Disassemble(optimized.data(), optimized.size(), &disassembly);
759 }
760
761 EXPECT_EQ(after, disassembly)
762 << "Was expecting the OpNop to have been removed.";
763 }
764
765 } // namespace
766 } // namespace opt
767 } // namespace spvtools
768