blob: 3e80add1daa69057c1a45df598a3c9a2e446139b [file] [log] [blame]
//
// 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"));
}