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