| // |
| // 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, 0, 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 in an unused function is kept in case |
| // SH_DONT_PRUNE_UNUSED_FUNCTIONS is specified. |
| TEST_F(RemoveUnreferencedVariablesTest, UserDefinedTypeReferencedInUnusedFunctionThatIsNotPruned) |
| { |
| const std::string &shaderString = |
| R"( |
| struct myStructType |
| { |
| int iMember; |
| }; |
| |
| myStructType func() |
| { |
| return myStructType(0); |
| } |
| |
| void main() |
| { |
| })"; |
| compile(shaderString, SH_DONT_PRUNE_UNUSED_FUNCTIONS); |
| |
| 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 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")); |
| } |