1 //
2 // Copyright 2017 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // RemoveUnreferencedVariables_test.cpp:
7 // Tests for removing unreferenced variables from the AST.
8 //
9
10 #include "GLSLANG/ShaderLang.h"
11 #include "angle_gl.h"
12 #include "gtest/gtest.h"
13 #include "tests/test_utils/compiler_test.h"
14
15 using namespace sh;
16
17 class RemoveUnreferencedVariablesTest : public MatchOutputCodeTest
18 {
19 public:
RemoveUnreferencedVariablesTest()20 RemoveUnreferencedVariablesTest() : MatchOutputCodeTest(GL_FRAGMENT_SHADER, SH_ESSL_OUTPUT) {}
21 };
22
23 // Test that a simple unreferenced declaration is pruned.
TEST_F(RemoveUnreferencedVariablesTest,SimpleDeclaration)24 TEST_F(RemoveUnreferencedVariablesTest, SimpleDeclaration)
25 {
26 const std::string &shaderString =
27 R"(precision mediump float;
28 void main()
29 {
30 vec4 myUnreferencedVec;
31 })";
32 compile(shaderString);
33
34 ASSERT_TRUE(notFoundInCode("myUnreferencedVec"));
35 }
36
37 // Test that a simple unreferenced global declaration is pruned.
TEST_F(RemoveUnreferencedVariablesTest,SimpleGlobalDeclaration)38 TEST_F(RemoveUnreferencedVariablesTest, SimpleGlobalDeclaration)
39 {
40 const std::string &shaderString =
41 R"(precision mediump float;
42
43 vec4 myUnreferencedVec;
44
45 void main()
46 {
47 })";
48 compile(shaderString);
49
50 ASSERT_TRUE(notFoundInCode("myUnreferencedVec"));
51 }
52
53 // Test that a simple unreferenced variable with an initializer is pruned.
TEST_F(RemoveUnreferencedVariablesTest,SimpleInitializer)54 TEST_F(RemoveUnreferencedVariablesTest, SimpleInitializer)
55 {
56 const std::string &shaderString =
57 R"(precision mediump float;
58 uniform vec4 uVec;
59 void main()
60 {
61 vec4 myUnreferencedVec = uVec;
62 })";
63 compile(shaderString);
64
65 ASSERT_TRUE(notFoundInCode("myUnreferencedVec"));
66 }
67
68 // Test that a user-defined function call inside an unreferenced variable initializer is retained.
TEST_F(RemoveUnreferencedVariablesTest,SideEffectInInitializer)69 TEST_F(RemoveUnreferencedVariablesTest, SideEffectInInitializer)
70 {
71 const std::string &shaderString =
72 R"(precision mediump float;
73 vec4 sideEffect(int i)
74 {
75 gl_FragColor = vec4(0, i, 0, 1);
76 return vec4(0);
77 }
78 void main()
79 {
80 vec4 myUnreferencedVec = sideEffect(1);
81 })";
82 compile(shaderString);
83
84 // We're happy as long as the function with side effects is called.
85 ASSERT_TRUE(foundInCode("sideEffect(1)"));
86 }
87
88 // Test that a modf call inside an unreferenced variable initializer is retained.
TEST_F(RemoveUnreferencedVariablesTest,BuiltInSideEffectInInitializer)89 TEST_F(RemoveUnreferencedVariablesTest, BuiltInSideEffectInInitializer)
90 {
91 const std::string &shaderString =
92 R"(#version 300 es
93 precision mediump float;
94 uniform float uF;
95 out vec4 my_FragColor;
96
97 void main()
98 {
99 float iPart = 0.0;
100 float myUnreferencedFloat = modf(uF, iPart);
101 my_FragColor = vec4(0.0, iPart, 0.0, 1.0);
102 })";
103 compile(shaderString);
104
105 // We're happy as long as the function with side effects is called.
106 ASSERT_TRUE(foundInCode("modf("));
107 }
108
109 // Test that an imageStore call inside an unreferenced variable initializer is retained.
TEST_F(RemoveUnreferencedVariablesTest,ImageStoreSideEffectInInitializer)110 TEST_F(RemoveUnreferencedVariablesTest, ImageStoreSideEffectInInitializer)
111 {
112 const std::string &shaderString =
113 R"(#version 310 es
114 precision highp float;
115 layout(rgba32i) uniform highp writeonly iimage2D img;
116
117 void main()
118 {
119 float myUnreferencedFloat = (imageStore(img, ivec2(0), ivec4(1)), 1.0);
120 })";
121 compile(shaderString);
122
123 // We're happy as long as the function with side effects is called.
124 ASSERT_TRUE(foundInCode("imageStore("));
125 }
126
127 // Test that multiple variables that are chained but otherwise are unreferenced are removed.
TEST_F(RemoveUnreferencedVariablesTest,MultipleVariablesChained)128 TEST_F(RemoveUnreferencedVariablesTest, MultipleVariablesChained)
129 {
130 const std::string &shaderString =
131 R"(precision mediump float;
132 uniform vec4 uVec;
133 void main()
134 {
135 vec4 myUnreferencedVec1 = uVec;
136 vec4 myUnreferencedVec2 = myUnreferencedVec1 * 2.0;
137 vec4 myUnreferencedVec3 = myUnreferencedVec2 + 1.0;
138 })";
139 compile(shaderString);
140
141 ASSERT_TRUE(notFoundInCode("myUnreferencedVec3"));
142 ASSERT_TRUE(notFoundInCode("myUnreferencedVec2"));
143 ASSERT_TRUE(notFoundInCode("myUnreferencedVec1"));
144 }
145
146 // Test that multiple variables that are chained with the last one being referenced are kept.
TEST_F(RemoveUnreferencedVariablesTest,MultipleVariablesChainedReferenced)147 TEST_F(RemoveUnreferencedVariablesTest, MultipleVariablesChainedReferenced)
148 {
149 const std::string &shaderString =
150 R"(precision mediump float;
151 uniform vec4 uVec;
152 void main()
153 {
154 vec4 myReferencedVec1 = uVec;
155 vec4 myReferencedVec2 = myReferencedVec1 * 2.0;
156 vec4 myReferencedVec3 = myReferencedVec2 + 1.0;
157 gl_FragColor = myReferencedVec3;
158 })";
159 compile(shaderString);
160
161 ASSERT_TRUE(foundInCode("myReferencedVec3"));
162 ASSERT_TRUE(foundInCode("myReferencedVec2"));
163 ASSERT_TRUE(foundInCode("myReferencedVec1"));
164 }
165
166 // Test that multiple variables that are chained within two scopes but otherwise are unreferenced
167 // are removed.
TEST_F(RemoveUnreferencedVariablesTest,MultipleVariablesChainedTwoScopes)168 TEST_F(RemoveUnreferencedVariablesTest, MultipleVariablesChainedTwoScopes)
169 {
170 const std::string &shaderString =
171 R"(precision mediump float;
172 uniform vec4 uVec;
173 void main()
174 {
175 vec4 myUnreferencedVec1 = uVec;
176 vec4 myUnreferencedVec2 = myUnreferencedVec1 * 2.0;
177 if (uVec.x > 0.0)
178 {
179 vec4 myUnreferencedVec3 = myUnreferencedVec2 + 1.0;
180 }
181 })";
182 compile(shaderString);
183
184 ASSERT_TRUE(notFoundInCode("myUnreferencedVec3"));
185 ASSERT_TRUE(notFoundInCode("myUnreferencedVec2"));
186 ASSERT_TRUE(notFoundInCode("myUnreferencedVec1"));
187 }
188
189 // Test that multiple variables that are chained with the last one being referenced in an inner
190 // scope are kept.
TEST_F(RemoveUnreferencedVariablesTest,VariableReferencedInAnotherScope)191 TEST_F(RemoveUnreferencedVariablesTest, VariableReferencedInAnotherScope)
192 {
193 const std::string &shaderString =
194 R"(precision mediump float;
195 uniform vec4 uVec;
196 void main()
197 {
198 vec4 myReferencedVec1 = uVec;
199 vec4 myReferencedVec2 = myReferencedVec1 * 2.0;
200 if (uVec.x > 0.0)
201 {
202 vec4 myReferencedVec3 = myReferencedVec2 + 1.0;
203 gl_FragColor = myReferencedVec3;
204 }
205 })";
206 compile(shaderString);
207
208 ASSERT_TRUE(foundInCode("myReferencedVec3"));
209 ASSERT_TRUE(foundInCode("myReferencedVec2"));
210 ASSERT_TRUE(foundInCode("myReferencedVec1"));
211 }
212
213 // Test that if there are two variables with the same name, one of them can be removed and another
214 // one kept.
TEST_F(RemoveUnreferencedVariablesTest,TwoVariablesWithSameNameInDifferentScopes)215 TEST_F(RemoveUnreferencedVariablesTest, TwoVariablesWithSameNameInDifferentScopes)
216 {
217 const std::string &shaderString =
218 R"(precision mediump float;
219 uniform vec4 uVec;
220 void main()
221 {
222 vec4 myVec = uVec; // This one is unreferenced.
223 if (uVec.x > 0.0)
224 {
225 vec4 myVec = uVec * 2.0; // This one is referenced.
226 gl_FragColor = myVec;
227 }
228 vec4 myUnreferencedVec = myVec;
229 })";
230 compile(shaderString);
231
232 ASSERT_TRUE(foundInCode("myVec", 2));
233 }
234
235 // Test that an unreferenced variable declared in a for loop header is removed.
TEST_F(RemoveUnreferencedVariablesTest,UnreferencedVariableDeclaredInForLoopHeader)236 TEST_F(RemoveUnreferencedVariablesTest, UnreferencedVariableDeclaredInForLoopHeader)
237 {
238 const std::string &shaderString =
239 R"(#version 300 es
240 precision highp float;
241 uniform int ui;
242
243 out vec4 my_FragColor;
244
245 void main()
246 {
247 my_FragColor = vec4(0.0);
248 int index = 0;
249 for (int unreferencedInt = ui; index < 10; ++index)
250 {
251 my_FragColor += vec4(0.0, float(index) * 0.01, 0.0, 0.0);
252 }
253 })";
254 compile(shaderString);
255
256 ASSERT_TRUE(foundInCode("index"));
257 ASSERT_TRUE(notFoundInCode("unreferencedInt"));
258 }
259
260 // Test that a loop condition is kept even if it declares an unreferenced variable.
TEST_F(RemoveUnreferencedVariablesTest,UnreferencedVariableDeclaredInWhileLoopCondition)261 TEST_F(RemoveUnreferencedVariablesTest, UnreferencedVariableDeclaredInWhileLoopCondition)
262 {
263 const std::string &shaderString =
264 R"(#version 300 es
265 precision highp float;
266 uniform int ui;
267
268 out vec4 my_FragColor;
269
270 void main()
271 {
272 my_FragColor = vec4(0.0);
273 int index = 0;
274 while (bool b = (index < 10))
275 {
276 my_FragColor += vec4(0.0, float(index) * 0.01, 0.0, 0.0);
277 ++index;
278 }
279 })";
280 compile(shaderString);
281
282 ASSERT_TRUE(foundInCode("index < 10"));
283 }
284
285 // Test that a variable declared in a for loop header that is only referenced in an unreferenced
286 // variable initializer is removed.
TEST_F(RemoveUnreferencedVariablesTest,VariableDeclaredInForLoopHeaderAccessedInUnreferencedVariableInitializer)287 TEST_F(RemoveUnreferencedVariablesTest,
288 VariableDeclaredInForLoopHeaderAccessedInUnreferencedVariableInitializer)
289 {
290 const std::string &shaderString =
291 R"(#version 300 es
292 precision highp float;
293 uniform int ui;
294
295 out vec4 my_FragColor;
296
297 void main()
298 {
299 my_FragColor = vec4(0.0);
300 int index = 0;
301 for (int unreferencedInt1 = ui; index < 10; ++index)
302 {
303 int unreferencedInt2 = unreferencedInt1;
304 my_FragColor += vec4(0.0, float(index) * 0.01, 0.0, 0.0);
305 }
306 })";
307 compile(shaderString);
308
309 ASSERT_TRUE(foundInCode("index"));
310 ASSERT_TRUE(notFoundInCode("unreferencedInt2"));
311 ASSERT_TRUE(notFoundInCode("unreferencedInt1"));
312 }
313
314 // Test that a user-defined type (struct) declaration that's used is not removed, but that the
315 // variable that's declared in the same declaration is removed.
TEST_F(RemoveUnreferencedVariablesTest,UserDefinedTypeReferencedAndVariableNotReferenced)316 TEST_F(RemoveUnreferencedVariablesTest, UserDefinedTypeReferencedAndVariableNotReferenced)
317 {
318 const std::string &shaderString =
319 R"(#version 300 es
320 precision highp float;
321 uniform float uF;
322
323 out vec4 my_FragColor;
324
325 void main()
326 {
327 struct myStruct { float member; } unreferencedStruct;
328 myStruct usedStruct = myStruct(uF);
329 my_FragColor = vec4(usedStruct.member);
330 })";
331 compile(shaderString);
332
333 ASSERT_TRUE(foundInCode("myStruct"));
334 ASSERT_TRUE(foundInCode("usedStruct"));
335 ASSERT_TRUE(notFoundInCode("unreferencedStruct"));
336 }
337
338 // Test that a nameless user-defined type (struct) declaration is removed entirely.
TEST_F(RemoveUnreferencedVariablesTest,NamelessUserDefinedTypeUnreferenced)339 TEST_F(RemoveUnreferencedVariablesTest, NamelessUserDefinedTypeUnreferenced)
340 {
341 const std::string &shaderString =
342 R"(#version 300 es
343 precision highp float;
344 void main()
345 {
346 struct { float member; } unreferencedStruct;
347 })";
348 compile(shaderString);
349
350 ASSERT_TRUE(notFoundInCode("unreferencedStruct"));
351 ASSERT_TRUE(notFoundInCode("member"));
352 }
353
354 // Test that a variable that's only referenced in a unused function is removed.
TEST_F(RemoveUnreferencedVariablesTest,VariableOnlyReferencedInUnusedFunction)355 TEST_F(RemoveUnreferencedVariablesTest, VariableOnlyReferencedInUnusedFunction)
356 {
357 const std::string &shaderString =
358 R"(
359 int onlyReferencedInUnusedFunction = 0;
360 void unusedFunc() {
361 onlyReferencedInUnusedFunction++;
362 }
363
364 void main()
365 {
366 })";
367 compile(shaderString);
368
369 ASSERT_TRUE(notFoundInCode("onlyReferencedInUnusedFunction"));
370 }
371
372 // Test that a variable that's only referenced in an array length() method call is removed.
TEST_F(RemoveUnreferencedVariablesTest,VariableOnlyReferencedInLengthMethod)373 TEST_F(RemoveUnreferencedVariablesTest, VariableOnlyReferencedInLengthMethod)
374 {
375 const std::string &shaderString =
376 R"(#version 300 es
377 precision highp float;
378
379 out vec4 my_FragColor;
380
381 void main()
382 {
383 float onlyReferencedInLengthMethodCall[1];
384 int len = onlyReferencedInLengthMethodCall.length();
385 my_FragColor = vec4(0, len, 0, 1);
386 })";
387 compile(shaderString);
388
389 ASSERT_TRUE(notFoundInCode("onlyReferencedInLengthMethodCall"));
390 }
391
392 // Test that an unreferenced user-defined type is removed.
TEST_F(RemoveUnreferencedVariablesTest,UserDefinedTypeUnreferenced)393 TEST_F(RemoveUnreferencedVariablesTest, UserDefinedTypeUnreferenced)
394 {
395 const std::string &shaderString =
396 R"(
397 struct myStructType
398 {
399 int i;
400 } myStructVariable;
401
402 void main()
403 {
404 })";
405 compile(shaderString);
406
407 ASSERT_TRUE(notFoundInCode("myStructType"));
408 }
409
410 // Test that a user-defined type that's only referenced in an unreferenced variable is removed.
TEST_F(RemoveUnreferencedVariablesTest,UserDefinedTypeReferencedInUnreferencedVariable)411 TEST_F(RemoveUnreferencedVariablesTest, UserDefinedTypeReferencedInUnreferencedVariable)
412 {
413 const std::string &shaderString =
414 R"(
415 struct myStructType
416 {
417 int i;
418 };
419
420 void main()
421 {
422 myStructType myStructVariable;
423 })";
424 compile(shaderString);
425
426 ASSERT_TRUE(notFoundInCode("myStructType"));
427 }
428
429 // Test that a user-defined type that's declared in an empty declaration and that is only referenced
430 // in an unreferenced variable is removed also when the shader contains another independent
431 // user-defined type that's declared in an empty declaration. This tests special case handling of
432 // reference counting of empty symbols.
TEST_F(RemoveUnreferencedVariablesTest,TwoUserDefinedTypesDeclaredInEmptyDeclarationsWithOneOfThemUnreferenced)433 TEST_F(RemoveUnreferencedVariablesTest,
434 TwoUserDefinedTypesDeclaredInEmptyDeclarationsWithOneOfThemUnreferenced)
435 {
436 const std::string &shaderString =
437 R"(
438 struct myStructTypeA
439 {
440 int i;
441 };
442
443 struct myStructTypeB
444 {
445 int j;
446 };
447
448 uniform myStructTypeB myStructVariableB;
449
450 void main()
451 {
452 myStructTypeA myStructVariableA;
453 })";
454 compile(shaderString);
455
456 ASSERT_TRUE(notFoundInCode("myStructTypeA"));
457 ASSERT_TRUE(foundInCode("myStructTypeB"));
458 }
459
460 // Test that a user-defined type that is only referenced in another unreferenced type is removed.
TEST_F(RemoveUnreferencedVariablesTest,UserDefinedTypeChain)461 TEST_F(RemoveUnreferencedVariablesTest, UserDefinedTypeChain)
462 {
463 const std::string &shaderString =
464 R"(
465 struct myInnerStructType
466 {
467 int i;
468 };
469
470 struct myOuterStructType
471 {
472 myInnerStructType inner;
473 } myStructVariable;
474
475 void main()
476 {
477 myOuterStructType myStructVariable2;
478 })";
479 compile(shaderString);
480
481 ASSERT_TRUE(notFoundInCode("myInnerStructType"));
482 }
483
484 // Test that a user-defined type that is referenced in another user-defined type that is used is
485 // kept.
TEST_F(RemoveUnreferencedVariablesTest,UserDefinedTypeChainReferenced)486 TEST_F(RemoveUnreferencedVariablesTest, UserDefinedTypeChainReferenced)
487 {
488 const std::string &shaderString =
489 R"(
490 precision mediump float;
491
492 struct myInnerStructType
493 {
494 int i;
495 };
496
497 uniform struct
498 {
499 myInnerStructType inner;
500 } myStructVariable;
501
502 void main()
503 {
504 if (myStructVariable.inner.i > 0)
505 {
506 gl_FragColor = vec4(0, 1, 0, 1);
507 }
508 })";
509 compile(shaderString);
510
511 ASSERT_TRUE(foundInCode("struct _umyInnerStructType"));
512 }
513
514 // Test that a struct type that is only referenced in a constructor and function call is kept.
TEST_F(RemoveUnreferencedVariablesTest,UserDefinedTypeReferencedInConstructorAndCall)515 TEST_F(RemoveUnreferencedVariablesTest, UserDefinedTypeReferencedInConstructorAndCall)
516 {
517 const std::string &shaderString =
518 R"(
519 precision mediump float;
520
521 uniform int ui;
522
523 struct myStructType
524 {
525 int iMember;
526 };
527
528 void func(myStructType myStructParam)
529 {
530 if (myStructParam.iMember > 0)
531 {
532 gl_FragColor = vec4(0, 1, 0, 1);
533 }
534 }
535
536 void main()
537 {
538 func(myStructType(ui));
539 })";
540 compile(shaderString);
541
542 ASSERT_TRUE(foundInCode("struct _umyStructType"));
543 }
544
545 // Test that a struct type that is only referenced in a constructor is kept. This assumes that there
546 // isn't more sophisticated folding of struct field access going on.
TEST_F(RemoveUnreferencedVariablesTest,UserDefinedTypeReferencedInConstructor)547 TEST_F(RemoveUnreferencedVariablesTest, UserDefinedTypeReferencedInConstructor)
548 {
549 const std::string &shaderString =
550 R"(
551 precision mediump float;
552
553 uniform int ui;
554
555 struct myStructType
556 {
557 int iMember;
558 };
559
560 void main()
561 {
562 if (myStructType(ui).iMember > 0)
563 {
564 gl_FragColor = vec4(0, 1, 0, 1);
565 }
566 })";
567 compile(shaderString);
568
569 ASSERT_TRUE(foundInCode("struct _umyStructType"));
570 }
571
572 // Test that a struct type that is only referenced in an unused function is removed.
TEST_F(RemoveUnreferencedVariablesTest,UserDefinedTypeReferencedInUnusedFunction)573 TEST_F(RemoveUnreferencedVariablesTest, UserDefinedTypeReferencedInUnusedFunction)
574 {
575 const std::string &shaderString =
576 R"(
577 precision mediump float;
578
579 struct myStructType
580 {
581 int iMember;
582 };
583
584 void func(myStructType myStructParam)
585 {
586 if (myStructParam.iMember > 0)
587 {
588 gl_FragColor = vec4(0, 1, 0, 1);
589 }
590 }
591
592 void main()
593 {
594 })";
595 compile(shaderString);
596
597 ASSERT_TRUE(notFoundInCode("myStructType"));
598 }
599
600 // Test that a struct type that is only referenced as a function return value is kept.
TEST_F(RemoveUnreferencedVariablesTest,UserDefinedTypeReturnedFromFunction)601 TEST_F(RemoveUnreferencedVariablesTest, UserDefinedTypeReturnedFromFunction)
602 {
603 const std::string &shaderString =
604 R"(
605 precision mediump float;
606
607 struct myStructType
608 {
609 int iMember;
610 };
611
612 myStructType func()
613 {
614 gl_FragColor = vec4(0, 1, 0, 1);
615 return myStructType(0);
616 }
617
618 void main()
619 {
620 func();
621 })";
622 compile(shaderString);
623
624 ASSERT_TRUE(foundInCode("struct _umyStructType"));
625
626 // Ensure that the struct isn't declared as a part of the function header.
627 ASSERT_TRUE(foundInCode("};"));
628 }
629
630 // Test that a struct type that is only referenced in a uniform block is kept.
TEST_F(RemoveUnreferencedVariablesTest,UserDefinedTypeInUniformBlock)631 TEST_F(RemoveUnreferencedVariablesTest, UserDefinedTypeInUniformBlock)
632 {
633 const std::string &shaderString =
634 R"(#version 300 es
635
636 precision highp float;
637 out vec4 my_FragColor;
638
639 struct myStructType
640 {
641 int iMember;
642 };
643
644 layout(std140) uniform myBlock {
645 myStructType uStruct;
646 int ui;
647 };
648
649 void main()
650 {
651 if (ui > 0)
652 {
653 my_FragColor = vec4(0, 1, 0, 1);
654 }
655 })";
656 compile(shaderString);
657
658 ASSERT_TRUE(foundInCode("struct _umyStructType"));
659 }
660
661 // Test that a struct type that is referenced from an initializer with a constructor can be removed.
TEST_F(RemoveUnreferencedVariablesTest,UserDefinedTypeConstructorInitializer)662 TEST_F(RemoveUnreferencedVariablesTest, UserDefinedTypeConstructorInitializer)
663 {
664 const std::string &shaderString =
665 R"(#version 300 es
666
667 precision highp float;
668 out vec4 my_FragColor;
669
670 struct myStructType
671 {
672 int iMember;
673 };
674
675 uniform int ui;
676
677 void main()
678 {
679 myStructType S = myStructType(ui);
680 my_FragColor = vec4(0, 1, 0, 1);
681 })";
682 compile(shaderString);
683
684 ASSERT_TRUE(notFoundInCode("myStructType"));
685 }
686