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