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