xref: /aosp_15_r20/external/angle/src/tests/compiler_tests/RemoveUnreferencedVariables_test.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
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