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