| // | |
| // Copyright 2017 The ANGLE Project Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| // | |
| // RemoveUnreferencedVariables_test.cpp: | |
| // Tests for removing unreferenced variables from the AST. | |
| // | |
| #include "GLSLANG/ShaderLang.h" | |
| #include "angle_gl.h" | |
| #include "gtest/gtest.h" | |
| #include "tests/test_utils/compiler_test.h" | |
| using namespace sh; | |
| class RemoveUnreferencedVariablesTest : public MatchOutputCodeTest | |
| { | |
| public: | |
| RemoveUnreferencedVariablesTest() : MatchOutputCodeTest(GL_FRAGMENT_SHADER, SH_ESSL_OUTPUT) {} | |
| }; | |
| // Test that a simple unreferenced declaration is pruned. | |
| TEST_F(RemoveUnreferencedVariablesTest, SimpleDeclaration) | |
| { | |
| const std::string &shaderString = | |
| R"(precision mediump float; | |
| void main() | |
| { | |
| vec4 myUnreferencedVec; | |
| })"; | |
| compile(shaderString); | |
| ASSERT_TRUE(notFoundInCode("myUnreferencedVec")); | |
| } | |
| // Test that a simple unreferenced global declaration is pruned. | |
| TEST_F(RemoveUnreferencedVariablesTest, SimpleGlobalDeclaration) | |
| { | |
| const std::string &shaderString = | |
| R"(precision mediump float; | |
| vec4 myUnreferencedVec; | |
| void main() | |
| { | |
| })"; | |
| compile(shaderString); | |
| ASSERT_TRUE(notFoundInCode("myUnreferencedVec")); | |
| } | |
| // Test that a simple unreferenced variable with an initializer is pruned. | |
| TEST_F(RemoveUnreferencedVariablesTest, SimpleInitializer) | |
| { | |
| const std::string &shaderString = | |
| R"(precision mediump float; | |
| uniform vec4 uVec; | |
| void main() | |
| { | |
| vec4 myUnreferencedVec = uVec; | |
| })"; | |
| compile(shaderString); | |
| ASSERT_TRUE(notFoundInCode("myUnreferencedVec")); | |
| } | |
| // Test that a user-defined function call inside an unreferenced variable initializer is retained. | |
| TEST_F(RemoveUnreferencedVariablesTest, SideEffectInInitializer) | |
| { | |
| const std::string &shaderString = | |
| R"(precision mediump float; | |
| vec4 sideEffect(int i) | |
| { | |
| gl_FragColor = vec4(0, i, 0, 1); | |
| return vec4(0); | |
| } | |
| void main() | |
| { | |
| vec4 myUnreferencedVec = sideEffect(1); | |
| })"; | |
| compile(shaderString); | |
| // We're happy as long as the function with side effects is called. | |
| ASSERT_TRUE(foundInCode("sideEffect(1)")); | |
| } | |
| // Test that a modf call inside an unreferenced variable initializer is retained. | |
| TEST_F(RemoveUnreferencedVariablesTest, BuiltInSideEffectInInitializer) | |
| { | |
| const std::string &shaderString = | |
| R"(#version 300 es | |
| precision mediump float; | |
| uniform float uF; | |
| out vec4 my_FragColor; | |
| void main() | |
| { | |
| float iPart = 0.0; | |
| float myUnreferencedFloat = modf(uF, iPart); | |
| my_FragColor = vec4(0.0, iPart, 0.0, 1.0); | |
| })"; | |
| compile(shaderString); | |
| // We're happy as long as the function with side effects is called. | |
| ASSERT_TRUE(foundInCode("modf(")); | |
| } | |
| // Test that an imageStore call inside an unreferenced variable initializer is retained. | |
| TEST_F(RemoveUnreferencedVariablesTest, ImageStoreSideEffectInInitializer) | |
| { | |
| const std::string &shaderString = | |
| R"(#version 310 es | |
| precision highp float; | |
| layout(rgba32i) uniform highp writeonly iimage2D img; | |
| void main() | |
| { | |
| float myUnreferencedFloat = (imageStore(img, ivec2(0), ivec4(1)), 1.0); | |
| })"; | |
| compile(shaderString); | |
| // We're happy as long as the function with side effects is called. | |
| ASSERT_TRUE(foundInCode("imageStore(")); | |
| } | |
| // Test that multiple variables that are chained but otherwise are unreferenced are removed. | |
| TEST_F(RemoveUnreferencedVariablesTest, MultipleVariablesChained) | |
| { | |
| const std::string &shaderString = | |
| R"(precision mediump float; | |
| uniform vec4 uVec; | |
| void main() | |
| { | |
| vec4 myUnreferencedVec1 = uVec; | |
| vec4 myUnreferencedVec2 = myUnreferencedVec1 * 2.0; | |
| vec4 myUnreferencedVec3 = myUnreferencedVec2 + 1.0; | |
| })"; | |
| compile(shaderString); | |
| ASSERT_TRUE(notFoundInCode("myUnreferencedVec3")); | |
| ASSERT_TRUE(notFoundInCode("myUnreferencedVec2")); | |
| ASSERT_TRUE(notFoundInCode("myUnreferencedVec1")); | |
| } | |
| // Test that multiple variables that are chained with the last one being referenced are kept. | |
| TEST_F(RemoveUnreferencedVariablesTest, MultipleVariablesChainedReferenced) | |
| { | |
| const std::string &shaderString = | |
| R"(precision mediump float; | |
| uniform vec4 uVec; | |
| void main() | |
| { | |
| vec4 myReferencedVec1 = uVec; | |
| vec4 myReferencedVec2 = myReferencedVec1 * 2.0; | |
| vec4 myReferencedVec3 = myReferencedVec2 + 1.0; | |
| gl_FragColor = myReferencedVec3; | |
| })"; | |
| compile(shaderString); | |
| ASSERT_TRUE(foundInCode("myReferencedVec3")); | |
| ASSERT_TRUE(foundInCode("myReferencedVec2")); | |
| ASSERT_TRUE(foundInCode("myReferencedVec1")); | |
| } | |
| // Test that multiple variables that are chained within two scopes but otherwise are unreferenced | |
| // are removed. | |
| TEST_F(RemoveUnreferencedVariablesTest, MultipleVariablesChainedTwoScopes) | |
| { | |
| const std::string &shaderString = | |
| R"(precision mediump float; | |
| uniform vec4 uVec; | |
| void main() | |
| { | |
| vec4 myUnreferencedVec1 = uVec; | |
| vec4 myUnreferencedVec2 = myUnreferencedVec1 * 2.0; | |
| if (uVec.x > 0.0) | |
| { | |
| vec4 myUnreferencedVec3 = myUnreferencedVec2 + 1.0; | |
| } | |
| })"; | |
| compile(shaderString); | |
| ASSERT_TRUE(notFoundInCode("myUnreferencedVec3")); | |
| ASSERT_TRUE(notFoundInCode("myUnreferencedVec2")); | |
| ASSERT_TRUE(notFoundInCode("myUnreferencedVec1")); | |
| } | |
| // Test that multiple variables that are chained with the last one being referenced in an inner | |
| // scope are kept. | |
| TEST_F(RemoveUnreferencedVariablesTest, VariableReferencedInAnotherScope) | |
| { | |
| const std::string &shaderString = | |
| R"(precision mediump float; | |
| uniform vec4 uVec; | |
| void main() | |
| { | |
| vec4 myReferencedVec1 = uVec; | |
| vec4 myReferencedVec2 = myReferencedVec1 * 2.0; | |
| if (uVec.x > 0.0) | |
| { | |
| vec4 myReferencedVec3 = myReferencedVec2 + 1.0; | |
| gl_FragColor = myReferencedVec3; | |
| } | |
| })"; | |
| compile(shaderString); | |
| ASSERT_TRUE(foundInCode("myReferencedVec3")); | |
| ASSERT_TRUE(foundInCode("myReferencedVec2")); | |
| ASSERT_TRUE(foundInCode("myReferencedVec1")); | |
| } | |
| // Test that if there are two variables with the same name, one of them can be removed and another | |
| // one kept. | |
| TEST_F(RemoveUnreferencedVariablesTest, TwoVariablesWithSameNameInDifferentScopes) | |
| { | |
| const std::string &shaderString = | |
| R"(precision mediump float; | |
| uniform vec4 uVec; | |
| void main() | |
| { | |
| vec4 myVec = uVec; // This one is unreferenced. | |
| if (uVec.x > 0.0) | |
| { | |
| vec4 myVec = uVec * 2.0; // This one is referenced. | |
| gl_FragColor = myVec; | |
| } | |
| vec4 myUnreferencedVec = myVec; | |
| })"; | |
| compile(shaderString); | |
| ASSERT_TRUE(foundInCode("myVec", 2)); | |
| } | |
| // Test that an unreferenced variable declared in a for loop header is removed. | |
| TEST_F(RemoveUnreferencedVariablesTest, UnreferencedVariableDeclaredInForLoopHeader) | |
| { | |
| const std::string &shaderString = | |
| R"(#version 300 es | |
| precision highp float; | |
| uniform int ui; | |
| out vec4 my_FragColor; | |
| void main() | |
| { | |
| my_FragColor = vec4(0.0); | |
| int index = 0; | |
| for (int unreferencedInt = ui; index < 10; ++index) | |
| { | |
| my_FragColor += vec4(0.0, float(index) * 0.01, 0.0, 0.0); | |
| } | |
| })"; | |
| compile(shaderString); | |
| ASSERT_TRUE(foundInCode("index")); | |
| ASSERT_TRUE(notFoundInCode("unreferencedInt")); | |
| } | |
| // Test that a loop condition is kept even if it declares an unreferenced variable. | |
| TEST_F(RemoveUnreferencedVariablesTest, UnreferencedVariableDeclaredInWhileLoopCondition) | |
| { | |
| const std::string &shaderString = | |
| R"(#version 300 es | |
| precision highp float; | |
| uniform int ui; | |
| out vec4 my_FragColor; | |
| void main() | |
| { | |
| my_FragColor = vec4(0.0); | |
| int index = 0; | |
| while (bool b = (index < 10)) | |
| { | |
| my_FragColor += vec4(0.0, float(index) * 0.01, 0.0, 0.0); | |
| ++index; | |
| } | |
| })"; | |
| compile(shaderString); | |
| ASSERT_TRUE(foundInCode("index < 10")); | |
| } | |
| // Test that a variable declared in a for loop header that is only referenced in an unreferenced | |
| // variable initializer is removed. | |
| TEST_F(RemoveUnreferencedVariablesTest, | |
| VariableDeclaredInForLoopHeaderAccessedInUnreferencedVariableInitializer) | |
| { | |
| const std::string &shaderString = | |
| R"(#version 300 es | |
| precision highp float; | |
| uniform int ui; | |
| out vec4 my_FragColor; | |
| void main() | |
| { | |
| my_FragColor = vec4(0.0); | |
| int index = 0; | |
| for (int unreferencedInt1 = ui; index < 10; ++index) | |
| { | |
| int unreferencedInt2 = unreferencedInt1; | |
| my_FragColor += vec4(0.0, float(index) * 0.01, 0.0, 0.0); | |
| } | |
| })"; | |
| compile(shaderString); | |
| ASSERT_TRUE(foundInCode("index")); | |
| ASSERT_TRUE(notFoundInCode("unreferencedInt2")); | |
| ASSERT_TRUE(notFoundInCode("unreferencedInt1")); | |
| } | |
| // Test that a user-defined type (struct) declaration that's used is not removed, but that the | |
| // variable that's declared in the same declaration is removed. | |
| TEST_F(RemoveUnreferencedVariablesTest, UserDefinedTypeReferencedAndVariableNotReferenced) | |
| { | |
| const std::string &shaderString = | |
| R"(#version 300 es | |
| precision highp float; | |
| uniform float uF; | |
| out vec4 my_FragColor; | |
| void main() | |
| { | |
| struct myStruct { float member; } unreferencedStruct; | |
| myStruct usedStruct = myStruct(uF); | |
| my_FragColor = vec4(usedStruct.member); | |
| })"; | |
| compile(shaderString); | |
| ASSERT_TRUE(foundInCode("myStruct")); | |
| ASSERT_TRUE(foundInCode("usedStruct")); | |
| ASSERT_TRUE(notFoundInCode("unreferencedStruct")); | |
| } | |
| // Test that a nameless user-defined type (struct) declaration is removed entirely. | |
| TEST_F(RemoveUnreferencedVariablesTest, NamelessUserDefinedTypeUnreferenced) | |
| { | |
| const std::string &shaderString = | |
| R"(#version 300 es | |
| precision highp float; | |
| void main() | |
| { | |
| struct { float member; } unreferencedStruct; | |
| })"; | |
| compile(shaderString); | |
| ASSERT_TRUE(notFoundInCode("unreferencedStruct")); | |
| ASSERT_TRUE(notFoundInCode("member")); | |
| } | |
| // Test that a variable that's only referenced in a unused function is removed. | |
| TEST_F(RemoveUnreferencedVariablesTest, VariableOnlyReferencedInUnusedFunction) | |
| { | |
| const std::string &shaderString = | |
| R"( | |
| int onlyReferencedInUnusedFunction = 0; | |
| void unusedFunc() { | |
| onlyReferencedInUnusedFunction++; | |
| } | |
| void main() | |
| { | |
| })"; | |
| compile(shaderString); | |
| ASSERT_TRUE(notFoundInCode("onlyReferencedInUnusedFunction")); | |
| } | |
| // Test that a variable that's only referenced in an array length() method call is removed. | |
| TEST_F(RemoveUnreferencedVariablesTest, VariableOnlyReferencedInLengthMethod) | |
| { | |
| const std::string &shaderString = | |
| R"(#version 300 es | |
| precision highp float; | |
| out vec4 my_FragColor; | |
| void main() | |
| { | |
| float onlyReferencedInLengthMethodCall[1]; | |
| int len = onlyReferencedInLengthMethodCall.length(); | |
| my_FragColor = vec4(0, len, 0, 1); | |
| })"; | |
| compile(shaderString); | |
| ASSERT_TRUE(notFoundInCode("onlyReferencedInLengthMethodCall")); | |
| } | |
| // Test that an unreferenced user-defined type is removed. | |
| TEST_F(RemoveUnreferencedVariablesTest, UserDefinedTypeUnreferenced) | |
| { | |
| const std::string &shaderString = | |
| R"( | |
| struct myStructType | |
| { | |
| int i; | |
| } myStructVariable; | |
| void main() | |
| { | |
| })"; | |
| compile(shaderString); | |
| ASSERT_TRUE(notFoundInCode("myStructType")); | |
| } | |
| // Test that a user-defined type that's only referenced in an unreferenced variable is removed. | |
| TEST_F(RemoveUnreferencedVariablesTest, UserDefinedTypeReferencedInUnreferencedVariable) | |
| { | |
| const std::string &shaderString = | |
| R"( | |
| struct myStructType | |
| { | |
| int i; | |
| }; | |
| void main() | |
| { | |
| myStructType myStructVariable; | |
| })"; | |
| compile(shaderString); | |
| ASSERT_TRUE(notFoundInCode("myStructType")); | |
| } | |
| // Test that a user-defined type that's declared in an empty declaration and that is only referenced | |
| // in an unreferenced variable is removed also when the shader contains another independent | |
| // user-defined type that's declared in an empty declaration. This tests special case handling of | |
| // reference counting of empty symbols. | |
| TEST_F(RemoveUnreferencedVariablesTest, | |
| TwoUserDefinedTypesDeclaredInEmptyDeclarationsWithOneOfThemUnreferenced) | |
| { | |
| const std::string &shaderString = | |
| R"( | |
| struct myStructTypeA | |
| { | |
| int i; | |
| }; | |
| struct myStructTypeB | |
| { | |
| int j; | |
| }; | |
| uniform myStructTypeB myStructVariableB; | |
| void main() | |
| { | |
| myStructTypeA myStructVariableA; | |
| })"; | |
| compile(shaderString); | |
| ASSERT_TRUE(notFoundInCode("myStructTypeA")); | |
| ASSERT_TRUE(foundInCode("myStructTypeB")); | |
| } | |
| // Test that a user-defined type that is only referenced in another unreferenced type is removed. | |
| TEST_F(RemoveUnreferencedVariablesTest, UserDefinedTypeChain) | |
| { | |
| const std::string &shaderString = | |
| R"( | |
| struct myInnerStructType | |
| { | |
| int i; | |
| }; | |
| struct myOuterStructType | |
| { | |
| myInnerStructType inner; | |
| } myStructVariable; | |
| void main() | |
| { | |
| myOuterStructType myStructVariable2; | |
| })"; | |
| compile(shaderString); | |
| ASSERT_TRUE(notFoundInCode("myInnerStructType")); | |
| } | |
| // Test that a user-defined type that is referenced in another user-defined type that is used is | |
| // kept. | |
| TEST_F(RemoveUnreferencedVariablesTest, UserDefinedTypeChainReferenced) | |
| { | |
| const std::string &shaderString = | |
| R"( | |
| precision mediump float; | |
| struct myInnerStructType | |
| { | |
| int i; | |
| }; | |
| uniform struct | |
| { | |
| myInnerStructType inner; | |
| } myStructVariable; | |
| void main() | |
| { | |
| if (myStructVariable.inner.i > 0) | |
| { | |
| gl_FragColor = vec4(0, 1, 0, 1); | |
| } | |
| })"; | |
| compile(shaderString); | |
| ASSERT_TRUE(foundInCode("struct _umyInnerStructType")); | |
| } | |
| // Test that a struct type that is only referenced in a constructor and function call is kept. | |
| TEST_F(RemoveUnreferencedVariablesTest, UserDefinedTypeReferencedInConstructorAndCall) | |
| { | |
| const std::string &shaderString = | |
| R"( | |
| precision mediump float; | |
| uniform int ui; | |
| struct myStructType | |
| { | |
| int iMember; | |
| }; | |
| void func(myStructType myStructParam) | |
| { | |
| if (myStructParam.iMember > 0) | |
| { | |
| gl_FragColor = vec4(0, 1, 0, 1); | |
| } | |
| } | |
| void main() | |
| { | |
| func(myStructType(ui)); | |
| })"; | |
| compile(shaderString); | |
| ASSERT_TRUE(foundInCode("struct _umyStructType")); | |
| } | |
| // Test that a struct type that is only referenced in a constructor is kept. This assumes that there | |
| // isn't more sophisticated folding of struct field access going on. | |
| TEST_F(RemoveUnreferencedVariablesTest, UserDefinedTypeReferencedInConstructor) | |
| { | |
| const std::string &shaderString = | |
| R"( | |
| precision mediump float; | |
| uniform int ui; | |
| struct myStructType | |
| { | |
| int iMember; | |
| }; | |
| void main() | |
| { | |
| if (myStructType(ui).iMember > 0) | |
| { | |
| gl_FragColor = vec4(0, 1, 0, 1); | |
| } | |
| })"; | |
| compile(shaderString); | |
| ASSERT_TRUE(foundInCode("struct _umyStructType")); | |
| } | |
| // Test that a struct type that is only referenced in an unused function is removed. | |
| TEST_F(RemoveUnreferencedVariablesTest, UserDefinedTypeReferencedInUnusedFunction) | |
| { | |
| const std::string &shaderString = | |
| R"( | |
| precision mediump float; | |
| struct myStructType | |
| { | |
| int iMember; | |
| }; | |
| void func(myStructType myStructParam) | |
| { | |
| if (myStructParam.iMember > 0) | |
| { | |
| gl_FragColor = vec4(0, 1, 0, 1); | |
| } | |
| } | |
| void main() | |
| { | |
| })"; | |
| compile(shaderString); | |
| ASSERT_TRUE(notFoundInCode("myStructType")); | |
| } | |
| // Test that a struct type that is only referenced as a function return value is kept. | |
| TEST_F(RemoveUnreferencedVariablesTest, UserDefinedTypeReturnedFromFunction) | |
| { | |
| const std::string &shaderString = | |
| R"( | |
| precision mediump float; | |
| struct myStructType | |
| { | |
| int iMember; | |
| }; | |
| myStructType func() | |
| { | |
| gl_FragColor = vec4(0, 1, 0, 1); | |
| return myStructType(0); | |
| } | |
| void main() | |
| { | |
| func(); | |
| })"; | |
| compile(shaderString); | |
| ASSERT_TRUE(foundInCode("struct _umyStructType")); | |
| // Ensure that the struct isn't declared as a part of the function header. | |
| ASSERT_TRUE(foundInCode("};")); | |
| } | |
| // Test that a struct type that is only referenced in a uniform block is kept. | |
| TEST_F(RemoveUnreferencedVariablesTest, UserDefinedTypeInUniformBlock) | |
| { | |
| const std::string &shaderString = | |
| R"(#version 300 es | |
| precision highp float; | |
| out vec4 my_FragColor; | |
| struct myStructType | |
| { | |
| int iMember; | |
| }; | |
| layout(std140) uniform myBlock { | |
| myStructType uStruct; | |
| int ui; | |
| }; | |
| void main() | |
| { | |
| if (ui > 0) | |
| { | |
| my_FragColor = vec4(0, 1, 0, 1); | |
| } | |
| })"; | |
| compile(shaderString); | |
| ASSERT_TRUE(foundInCode("struct _umyStructType")); | |
| } | |
| // Test that a struct type that is referenced from an initializer with a constructor can be removed. | |
| TEST_F(RemoveUnreferencedVariablesTest, UserDefinedTypeConstructorInitializer) | |
| { | |
| const std::string &shaderString = | |
| R"(#version 300 es | |
| precision highp float; | |
| out vec4 my_FragColor; | |
| struct myStructType | |
| { | |
| int iMember; | |
| }; | |
| uniform int ui; | |
| void main() | |
| { | |
| myStructType S = myStructType(ui); | |
| my_FragColor = vec4(0, 1, 0, 1); | |
| })"; | |
| compile(shaderString); | |
| ASSERT_TRUE(notFoundInCode("myStructType")); | |
| } |