1 // Copyright (c) 2023 Nintendo
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 <iostream>
17 
18 #include "gtest/gtest.h"
19 #include "spirv-tools/libspirv.h"
20 
21 namespace spvtools {
22 namespace {
23 
TEST(OptimizerCInterface,DefaultConsumerWithValidationNoPassesForInvalidInput)24 TEST(OptimizerCInterface, DefaultConsumerWithValidationNoPassesForInvalidInput) {
25   const uint32_t spirv[] = {
26       0xDEADFEED, // Invalid Magic
27       0x00010100, // Version 1.1
28       0x00000000, // No Generator
29       0x01000000, // Bound
30       0x00000000, // Schema
31       0x00020011, // OpCapability
32       0x00000001, // Shader
33       0x00020011, // OpCapability
34       0x00000005, // Linkage
35       0x0003000E, // OpMemoryModel
36       0x00000000, // Logical
37       0x00000001  // GLSL450
38   };
39 
40   auto optimizer = spvOptimizerCreate(SPV_ENV_UNIVERSAL_1_1);
41   ASSERT_NE(optimizer, nullptr);
42 
43   // Do not register any passes
44 
45   auto options = spvOptimizerOptionsCreate();
46   ASSERT_NE(options, nullptr);
47   spvOptimizerOptionsSetRunValidator(options, true);
48 
49   spv_binary binary = nullptr;
50   EXPECT_NE(SPV_SUCCESS,
51             spvOptimizerRun(optimizer, spirv, sizeof(spirv) / sizeof(uint32_t),
52                             &binary, options));
53   ASSERT_EQ(binary, nullptr);
54 
55   spvOptimizerOptionsDestroy(options);
56 
57   spvOptimizerDestroy(optimizer);
58 }
59 
TEST(OptimizerCInterface,SpecifyConsumerWithValidationNoPassesForInvalidInput)60 TEST(OptimizerCInterface, SpecifyConsumerWithValidationNoPassesForInvalidInput) {
61   const uint32_t spirv[] = {
62       0xDEADFEED, // Invalid Magic
63       0x00010100, // Version 1.1
64       0x00000000, // No Generator
65       0x01000000, // Bound
66       0x00000000, // Schema
67       0x00020011, // OpCapability
68       0x00000001, // Shader
69       0x00020011, // OpCapability
70       0x00000005, // Linkage
71       0x0003000E, // OpMemoryModel
72       0x00000000, // Logical
73       0x00000001  // GLSL450
74   };
75 
76   auto optimizer = spvOptimizerCreate(SPV_ENV_UNIVERSAL_1_1);
77   ASSERT_NE(optimizer, nullptr);
78 
79   spvOptimizerSetMessageConsumer(
80       optimizer,
81       [](spv_message_level_t, const char*, const spv_position_t*,
82          const char* message) {
83         std::cout << message << std::endl;
84       });
85 
86   // Do not register any passes
87 
88   auto options = spvOptimizerOptionsCreate();
89   ASSERT_NE(options, nullptr);
90   spvOptimizerOptionsSetRunValidator(options, true);
91 
92   testing::internal::CaptureStdout();
93 
94   spv_binary binary = nullptr;
95   EXPECT_NE(SPV_SUCCESS,
96             spvOptimizerRun(optimizer, spirv, sizeof(spirv) / sizeof(uint32_t),
97                             &binary, options));
98   ASSERT_EQ(binary, nullptr);
99 
100   auto output = testing::internal::GetCapturedStdout();
101   EXPECT_STRNE(output.c_str(), "");
102 
103   spvOptimizerOptionsDestroy(options);
104 
105   spvOptimizerDestroy(optimizer);
106 }
107 
TEST(OptimizerCInterface,DefaultConsumerWithValidationNoPassesForValidInput)108 TEST(OptimizerCInterface, DefaultConsumerWithValidationNoPassesForValidInput) {
109   const uint32_t spirv[] = {
110       0x07230203, // Magic
111       0x00010100, // Version 1.1
112       0x00000000, // No Generator
113       0x00000001, // Bound
114       0x00000000, // Schema
115       0x00020011, // OpCapability
116       0x00000001, // Shader
117       0x00020011, // OpCapability
118       0x00000005, // Linkage
119       0x0003000E, // OpMemoryModel
120       0x00000000, // Logical
121       0x00000001  // GLSL450
122   };
123 
124   auto optimizer = spvOptimizerCreate(SPV_ENV_UNIVERSAL_1_1);
125   ASSERT_NE(optimizer, nullptr);
126 
127   // Do not register any passes
128 
129   auto options = spvOptimizerOptionsCreate();
130   ASSERT_NE(options, nullptr);
131   spvOptimizerOptionsSetRunValidator(options, true);
132 
133   spv_binary binary = nullptr;
134   EXPECT_EQ(SPV_SUCCESS,
135             spvOptimizerRun(optimizer, spirv, sizeof(spirv) / sizeof(uint32_t),
136                             &binary, options));
137   ASSERT_NE(binary, nullptr);
138 
139   spvOptimizerOptionsDestroy(options);
140 
141   // Should remain unchanged
142   EXPECT_EQ(binary->wordCount, sizeof(spirv) / sizeof(uint32_t));
143   EXPECT_EQ(memcmp(binary->code, spirv, sizeof(spirv) / sizeof(uint32_t)), 0);
144 
145   spvBinaryDestroy(binary);
146   spvOptimizerDestroy(optimizer);
147 }
148 
TEST(OptimizerCInterface,DefaultConsumerNoPassesForValidInput)149 TEST(OptimizerCInterface, DefaultConsumerNoPassesForValidInput) {
150   const uint32_t spirv[] = {
151       0x07230203, // Magic
152       0x00010100, // Version 1.1
153       0x00000000, // No Generator
154       0x00000003, // Bound
155       0x00000000, // Schema
156       0x00020011, // OpCapability
157       0x00000001, // Shader
158       0x00020011, // OpCapability
159       0x00000005, // Linkage
160       0x0003000E, // OpMemoryModel
161       0x00000000, // Logical
162       0x00000001, // GLSL450
163       0x00040015, // OpTypeInt
164       0x00000001, // %1
165       0x00000020, // 32 Bits
166       0x00000000, // Unsigned
167       0x0004002B, // OpConstant
168       0x00000001, // %1
169       0x00000002, // %2
170       0x00000001  // 1
171   };
172 
173   auto optimizer = spvOptimizerCreate(SPV_ENV_UNIVERSAL_1_1);
174   ASSERT_NE(optimizer, nullptr);
175 
176   // Do not register any passes
177 
178   auto options = spvOptimizerOptionsCreate();
179   ASSERT_NE(options, nullptr);
180   spvOptimizerOptionsSetRunValidator(options, true);
181 
182   spv_binary binary = nullptr;
183   EXPECT_EQ(SPV_SUCCESS,
184             spvOptimizerRun(optimizer, spirv, sizeof(spirv) / sizeof(uint32_t),
185                             &binary, options));
186   ASSERT_NE(binary, nullptr);
187 
188   spvOptimizerOptionsDestroy(options);
189 
190   // Should remain unchanged
191   EXPECT_EQ(binary->wordCount, sizeof(spirv) / sizeof(uint32_t));
192   EXPECT_EQ(memcmp(binary->code, spirv, sizeof(spirv) / sizeof(uint32_t)), 0);
193 
194   spvBinaryDestroy(binary);
195   spvOptimizerDestroy(optimizer);
196 }
197 
TEST(OptimizerCInterface,DefaultConsumerLegalizationPassesForValidInput)198 TEST(OptimizerCInterface, DefaultConsumerLegalizationPassesForValidInput) {
199   const uint32_t spirv[] = {
200       0x07230203, // Magic
201       0x00010100, // Version 1.1
202       0x00000000, // No Generator
203       0x00000003, // Bound
204       0x00000000, // Schema
205       0x00020011, // OpCapability
206       0x00000001, // Shader
207       0x00020011, // OpCapability
208       0x00000005, // Linkage
209       0x0003000E, // OpMemoryModel
210       0x00000000, // Logical
211       0x00000001, // GLSL450
212       0x00040015, // OpTypeInt
213       0x00000001, // %1
214       0x00000020, // 32 Bits
215       0x00000000, // Unsigned
216       0x0004002B, // OpConstant
217       0x00000001, // %1
218       0x00000002, // %2
219       0x00000001  // 1
220   };
221 
222   auto optimizer = spvOptimizerCreate(SPV_ENV_UNIVERSAL_1_1);
223   ASSERT_NE(optimizer, nullptr);
224 
225   spvOptimizerRegisterLegalizationPasses(optimizer);
226 
227   auto options = spvOptimizerOptionsCreate();
228   ASSERT_NE(options, nullptr);
229   spvOptimizerOptionsSetRunValidator(options, false);
230 
231   spv_binary binary = nullptr;
232   EXPECT_EQ(SPV_SUCCESS,
233             spvOptimizerRun(optimizer, spirv, sizeof(spirv) / sizeof(uint32_t),
234                             &binary, options));
235   ASSERT_NE(binary, nullptr);
236 
237   spvOptimizerOptionsDestroy(options);
238 
239   // Only check that SPV_SUCCESS is returned, do not verify output
240 
241   spvBinaryDestroy(binary);
242   spvOptimizerDestroy(optimizer);
243 }
244 
TEST(OptimizerCInterface,DefaultConsumerPerformancePassesForValidInput)245 TEST(OptimizerCInterface, DefaultConsumerPerformancePassesForValidInput) {
246   const uint32_t spirv[] = {
247       0x07230203, // Magic
248       0x00010100, // Version 1.1
249       0x00000000, // No Generator
250       0x00000003, // Bound
251       0x00000000, // Schema
252       0x00020011, // OpCapability
253       0x00000001, // Shader
254       0x00020011, // OpCapability
255       0x00000005, // Linkage
256       0x0003000E, // OpMemoryModel
257       0x00000000, // Logical
258       0x00000001, // GLSL450
259       0x00040015, // OpTypeInt
260       0x00000001, // %1
261       0x00000020, // 32 Bits
262       0x00000000, // Unsigned
263       0x0004002B, // OpConstant
264       0x00000001, // %1
265       0x00000002, // %2
266       0x00000001  // 1
267   };
268   const uint32_t expected_spirv[] = {
269       0x07230203, // Magic
270       0x00010100, // Version 1.1
271       0x00000000, // No Generator
272       0x00000001, // Bound
273       0x00000000, // Schema
274       0x00020011, // OpCapability
275       0x00000001, // Shader
276       0x00020011, // OpCapability
277       0x00000005, // Linkage
278       0x0003000E, // OpMemoryModel
279       0x00000000, // Logical
280       0x00000001  // GLSL450
281   };
282 
283   auto optimizer = spvOptimizerCreate(SPV_ENV_UNIVERSAL_1_1);
284   ASSERT_NE(optimizer, nullptr);
285 
286   spvOptimizerRegisterPerformancePasses(optimizer);
287 
288   auto options = spvOptimizerOptionsCreate();
289   ASSERT_NE(options, nullptr);
290   spvOptimizerOptionsSetRunValidator(options, false);
291 
292   spv_binary binary = nullptr;
293   EXPECT_EQ(SPV_SUCCESS,
294             spvOptimizerRun(optimizer, spirv, sizeof(spirv) / sizeof(uint32_t),
295                             &binary, options));
296   ASSERT_NE(binary, nullptr);
297 
298   spvOptimizerOptionsDestroy(options);
299 
300   // Unreferenced OpTypeInt and OpConstant should be removed
301   EXPECT_EQ(binary->wordCount, sizeof(expected_spirv) / sizeof(uint32_t));
302   EXPECT_EQ(memcmp(binary->code, expected_spirv,
303                    sizeof(expected_spirv) / sizeof(uint32_t)), 0);
304 
305   spvBinaryDestroy(binary);
306   spvOptimizerDestroy(optimizer);
307 }
308 
TEST(OptimizerCInterface,DefaultConsumerSizePassesForValidInput)309 TEST(OptimizerCInterface, DefaultConsumerSizePassesForValidInput) {
310   const uint32_t spirv[] = {
311       0x07230203, // Magic
312       0x00010100, // Version 1.1
313       0x00000000, // No Generator
314       0x00000003, // Bound
315       0x00000000, // Schema
316       0x00020011, // OpCapability
317       0x00000001, // Shader
318       0x00020011, // OpCapability
319       0x00000005, // Linkage
320       0x0003000E, // OpMemoryModel
321       0x00000000, // Logical
322       0x00000001, // GLSL450
323       0x00040015, // OpTypeInt
324       0x00000001, // %1
325       0x00000020, // 32 Bits
326       0x00000000, // Unsigned
327       0x0004002B, // OpConstant
328       0x00000001, // %1
329       0x00000002, // %2
330       0x00000001  // 1
331   };
332   const uint32_t expected_spirv[] = {
333       0x07230203, // Magic
334       0x00010100, // Version 1.1
335       0x00000000, // No Generator
336       0x00000001, // Bound
337       0x00000000, // Schema
338       0x00020011, // OpCapability
339       0x00000001, // Shader
340       0x00020011, // OpCapability
341       0x00000005, // Linkage
342       0x0003000E, // OpMemoryModel
343       0x00000000, // Logical
344       0x00000001  // GLSL450
345   };
346 
347   auto optimizer = spvOptimizerCreate(SPV_ENV_UNIVERSAL_1_1);
348   ASSERT_NE(optimizer, nullptr);
349 
350   spvOptimizerRegisterSizePasses(optimizer);
351 
352   auto options = spvOptimizerOptionsCreate();
353   ASSERT_NE(options, nullptr);
354   spvOptimizerOptionsSetRunValidator(options, false);
355 
356   spv_binary binary = nullptr;
357   EXPECT_EQ(SPV_SUCCESS,
358             spvOptimizerRun(optimizer, spirv, sizeof(spirv) / sizeof(uint32_t),
359                             &binary, options));
360   ASSERT_NE(binary, nullptr);
361 
362   spvOptimizerOptionsDestroy(options);
363 
364   // Unreferenced OpTypeInt and OpConstant should be removed
365   EXPECT_EQ(binary->wordCount, sizeof(expected_spirv) / sizeof(uint32_t));
366   EXPECT_EQ(memcmp(binary->code, expected_spirv,
367                    sizeof(expected_spirv) / sizeof(uint32_t)), 0);
368 
369   spvBinaryDestroy(binary);
370   spvOptimizerDestroy(optimizer);
371 }
372 
TEST(OptimizerCInterface,DefaultConsumerPassFromFlagForValidInput)373 TEST(OptimizerCInterface, DefaultConsumerPassFromFlagForValidInput) {
374   const uint32_t spirv[] = {
375       0x07230203, // Magic
376       0x00010100, // Version 1.1
377       0x00000000, // No Generator
378       0x00000003, // Bound
379       0x00000000, // Schema
380       0x00020011, // OpCapability
381       0x00000001, // Shader
382       0x00020011, // OpCapability
383       0x00000005, // Linkage
384       0x0003000E, // OpMemoryModel
385       0x00000000, // Logical
386       0x00000001, // GLSL450
387       0x00040015, // OpTypeInt
388       0x00000001, // %1
389       0x00000020, // 32 Bits
390       0x00000000, // Unsigned
391       0x0004002B, // OpConstant
392       0x00000001, // %1
393       0x00000002, // %2
394       0x00000001  // 1
395   };
396   const uint32_t expected_spirv[] = {
397       0x07230203, // Magic
398       0x00010100, // Version 1.1
399       0x00000000, // No Generator
400       0x00000001, // Bound
401       0x00000000, // Schema
402       0x00020011, // OpCapability
403       0x00000001, // Shader
404       0x00020011, // OpCapability
405       0x00000005, // Linkage
406       0x0003000E, // OpMemoryModel
407       0x00000000, // Logical
408       0x00000001  // GLSL450
409   };
410 
411   auto optimizer = spvOptimizerCreate(SPV_ENV_UNIVERSAL_1_1);
412   ASSERT_NE(optimizer, nullptr);
413 
414   EXPECT_TRUE(spvOptimizerRegisterPassFromFlag(
415       optimizer, "--eliminate-dead-code-aggressive"));
416 
417   auto options = spvOptimizerOptionsCreate();
418   ASSERT_NE(options, nullptr);
419   spvOptimizerOptionsSetRunValidator(options, false);
420 
421   spv_binary binary = nullptr;
422   EXPECT_EQ(SPV_SUCCESS,
423             spvOptimizerRun(optimizer, spirv, sizeof(spirv) / sizeof(uint32_t),
424                             &binary, options));
425   ASSERT_NE(binary, nullptr);
426 
427   spvOptimizerOptionsDestroy(options);
428 
429   // Unreferenced OpTypeInt and OpConstant should be removed
430   EXPECT_EQ(binary->wordCount, sizeof(expected_spirv) / sizeof(uint32_t));
431   EXPECT_EQ(memcmp(binary->code, expected_spirv,
432                    sizeof(expected_spirv) / sizeof(uint32_t)), 0);
433 
434   spvBinaryDestroy(binary);
435   spvOptimizerDestroy(optimizer);
436 }
437 
TEST(OptimizerCInterface,DefaultConsumerPassesFromFlagsForValidInput)438 TEST(OptimizerCInterface, DefaultConsumerPassesFromFlagsForValidInput) {
439   const uint32_t spirv[] = {
440       0x07230203, // Magic
441       0x00010100, // Version 1.1
442       0x00000000, // No Generator
443       0x00000003, // Bound
444       0x00000000, // Schema
445       0x00020011, // OpCapability
446       0x00000001, // Shader
447       0x00020011, // OpCapability
448       0x00000005, // Linkage
449       0x0003000E, // OpMemoryModel
450       0x00000000, // Logical
451       0x00000001, // GLSL450
452       0x00040015, // OpTypeInt
453       0x00000001, // %1
454       0x00000020, // 32 Bits
455       0x00000000, // Unsigned
456       0x0004002B, // OpConstant
457       0x00000001, // %1
458       0x00000002, // %2
459       0x00000001  // 1
460   };
461   const uint32_t expected_spirv[] = {
462       0x07230203, // Magic
463       0x00010100, // Version 1.1
464       0x00000000, // No Generator
465       0x00000001, // Bound
466       0x00000000, // Schema
467       0x00020011, // OpCapability
468       0x00000001, // Shader
469       0x00020011, // OpCapability
470       0x00000005, // Linkage
471       0x0003000E, // OpMemoryModel
472       0x00000000, // Logical
473       0x00000001  // GLSL450
474   };
475 
476   auto optimizer = spvOptimizerCreate(SPV_ENV_UNIVERSAL_1_1);
477   ASSERT_NE(optimizer, nullptr);
478 
479   const char* flags[2] = {
480       "--eliminate-dead-const",
481       "--eliminate-dead-code-aggressive"
482   };
483 
484   EXPECT_TRUE(spvOptimizerRegisterPassesFromFlags(
485       optimizer, flags, sizeof(flags) / sizeof(const char*)));
486 
487   auto options = spvOptimizerOptionsCreate();
488   ASSERT_NE(options, nullptr);
489   spvOptimizerOptionsSetRunValidator(options, false);
490 
491   spv_binary binary = nullptr;
492   EXPECT_EQ(SPV_SUCCESS,
493             spvOptimizerRun(optimizer, spirv, sizeof(spirv) / sizeof(uint32_t),
494                             &binary, options));
495   ASSERT_NE(binary, nullptr);
496 
497   spvOptimizerOptionsDestroy(options);
498 
499   // Unreferenced OpTypeInt and OpConstant should be removed
500   EXPECT_EQ(binary->wordCount, sizeof(expected_spirv) / sizeof(uint32_t));
501   EXPECT_EQ(memcmp(binary->code, expected_spirv,
502                    sizeof(expected_spirv) / sizeof(uint32_t)), 0);
503 
504   spvBinaryDestroy(binary);
505   spvOptimizerDestroy(optimizer);
506 }
507 
TEST(OptimizerCInterface,DefaultConsumerInvalidPassFromFlag)508 TEST(OptimizerCInterface, DefaultConsumerInvalidPassFromFlag) {
509   auto optimizer = spvOptimizerCreate(SPV_ENV_UNIVERSAL_1_1);
510   ASSERT_NE(optimizer, nullptr);
511 
512   EXPECT_FALSE(spvOptimizerRegisterPassFromFlag(
513       optimizer, "--this-is-not-a-valid-pass"));
514 
515   spvOptimizerDestroy(optimizer);
516 }
517 
TEST(OptimizerCInterface,DefaultConsumerInvalidPassesFromFlags)518 TEST(OptimizerCInterface, DefaultConsumerInvalidPassesFromFlags) {
519   auto optimizer = spvOptimizerCreate(SPV_ENV_UNIVERSAL_1_1);
520   ASSERT_NE(optimizer, nullptr);
521 
522   const char* flags[2] = {
523       "--eliminate-dead-const",
524       "--this-is-not-a-valid-pass"
525   };
526 
527   EXPECT_FALSE(spvOptimizerRegisterPassesFromFlags(
528       optimizer, flags, sizeof(flags) / sizeof(const char*)));
529 
530   spvOptimizerDestroy(optimizer);
531 }
532 
533 }  // namespace
534 }  // namespace spvtools
535