Commit 6caa5e81 by Olli Etuaho

Validate global initializer qualifiers

Allow only constants, other globals, temporaries, and uniforms to be used in global variable initialization for now. The spec limits global variable initialization further to only constant expressions, but fully implementing this has a too large compatibility cost for ESSL 1.00, so implement it only partially. In the case of ESSL 3.00 we can use stricter validation, since there's no legacy to worry about. TEST=angle_unittests, WebGL conformance tests BUG=angleproject:988 Change-Id: I6a66f6a31130b44717dd2bef3082a0fc395a60b6 Reviewed-on: https://chromium-review.googlesource.com/270430Reviewed-by: 's avatarKenneth Russell <kbr@chromium.org> Tested-by: 's avatarOlli Etuaho <oetuaho@nvidia.com>
parent c5146c76
...@@ -99,6 +99,8 @@ ...@@ -99,6 +99,8 @@
'compiler/translator/Types.h', 'compiler/translator/Types.h',
'compiler/translator/UnfoldShortCircuitAST.cpp', 'compiler/translator/UnfoldShortCircuitAST.cpp',
'compiler/translator/UnfoldShortCircuitAST.h', 'compiler/translator/UnfoldShortCircuitAST.h',
'compiler/translator/ValidateGlobalInitializer.cpp',
'compiler/translator/ValidateGlobalInitializer.h',
'compiler/translator/ValidateLimitations.cpp', 'compiler/translator/ValidateLimitations.cpp',
'compiler/translator/ValidateLimitations.h', 'compiler/translator/ValidateLimitations.h',
'compiler/translator/ValidateOutputs.cpp', 'compiler/translator/ValidateOutputs.cpp',
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "compiler/preprocessor/SourceLocation.h" #include "compiler/preprocessor/SourceLocation.h"
#include "compiler/translator/glslang.h" #include "compiler/translator/glslang.h"
#include "compiler/translator/ValidateSwitch.h" #include "compiler/translator/ValidateSwitch.h"
#include "compiler/translator/ValidateGlobalInitializer.h"
#include "compiler/translator/util.h" #include "compiler/translator/util.h"
/////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////
...@@ -1110,6 +1111,20 @@ bool TParseContext::executeInitializer(const TSourceLoc &line, const TString &id ...@@ -1110,6 +1111,20 @@ bool TParseContext::executeInitializer(const TSourceLoc &line, const TString &id
return true; return true;
} }
bool globalInitWarning = false;
if (symbolTable.atGlobalLevel() && !ValidateGlobalInitializer(initializer, this, &globalInitWarning))
{
// Error message does not completely match behavior with ESSL 1.00, but
// we want to steer developers towards only using constant expressions.
error(line, "global variable initializers must be constant expressions", "=");
return true;
}
if (globalInitWarning)
{
warning(line, "global variable initializers should be constant expressions "
"(uniforms and globals are allowed in global initializers for legacy compatibility)", "=");
}
// //
// identifier must be of type constant, a global, or a temporary // identifier must be of type constant, a global, or a temporary
// //
......
//
// Copyright (c) 2002-2015 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.
//
#include "compiler/translator/ValidateGlobalInitializer.h"
#include "compiler/translator/ParseContext.h"
namespace
{
class ValidateGlobalInitializerTraverser : public TIntermTraverser
{
public:
ValidateGlobalInitializerTraverser(const TParseContext *context);
void visitSymbol(TIntermSymbol *node) override;
bool isValid() const { return mIsValid; }
bool issueWarning() const { return mIssueWarning; }
private:
const TParseContext *mContext;
bool mIsValid;
bool mIssueWarning;
};
void ValidateGlobalInitializerTraverser::visitSymbol(TIntermSymbol *node)
{
const TSymbol *sym = mContext->symbolTable.find(node->getSymbol(), mContext->getShaderVersion());
if (sym->isVariable())
{
// ESSL 1.00 section 4.3 (or ESSL 3.00 section 4.3):
// Global initializers must be constant expressions.
const TVariable *var = static_cast<const TVariable *>(sym);
switch (var->getType().getQualifier())
{
case EvqConst:
break;
case EvqGlobal:
case EvqTemporary:
case EvqUniform:
// We allow these cases to be compatible with legacy ESSL 1.00 content.
// Implement stricter rules for ESSL 3.00 since there's no legacy content to deal with.
if (mContext->getShaderVersion() >= 300)
{
mIsValid = false;
}
else
{
mIssueWarning = true;
}
break;
default:
mIsValid = false;
}
}
}
ValidateGlobalInitializerTraverser::ValidateGlobalInitializerTraverser(const TParseContext *context)
: TIntermTraverser(true, false, false),
mContext(context),
mIsValid(true),
mIssueWarning(false)
{
}
} // namespace
bool ValidateGlobalInitializer(TIntermTyped *initializer, const TParseContext *context, bool *warning)
{
ValidateGlobalInitializerTraverser validate(context);
initializer->traverse(&validate);
ASSERT(warning != nullptr);
*warning = validate.issueWarning();
return validate.isValid();
}
//
// Copyright (c) 2002-2015 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.
//
#ifndef COMPILER_TRANSLATOR_VALIDATEGLOBALINITIALIZER_H_
#define COMPILER_TRANSLATOR_VALIDATEGLOBALINITIALIZER_H_
class TIntermTyped;
struct TParseContext;
// Returns true if the initializer is valid.
bool ValidateGlobalInitializer(TIntermTyped *initializer, const TParseContext *context, bool *warning);
#endif // COMPILER_TRANSLATOR_VALIDATEGLOBALINITIALIZER_H_
...@@ -42,6 +42,11 @@ class MalformedShaderTest : public testing::Test ...@@ -42,6 +42,11 @@ class MalformedShaderTest : public testing::Test
return compilationSuccess; return compilationSuccess;
} }
bool hasWarning() const
{
return mInfoLog.find("WARNING: ") != std::string::npos;
}
protected: protected:
std::string mInfoLog; std::string mInfoLog;
...@@ -441,3 +446,102 @@ TEST_F(MalformedShaderTest, AssignArrayLengthToUnsigned) ...@@ -441,3 +446,102 @@ TEST_F(MalformedShaderTest, AssignArrayLengthToUnsigned)
FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog;
} }
} }
// Global variable initializers need to be constant expressions (ESSL 1.00 section 4.3)
// Initializing with a varying should be an error.
TEST_F(MalformedShaderTest, AssignVaryingToGlobal)
{
const std::string &shaderString =
"precision mediump float;\n"
"varying float a;\n"
"float b = a * 2.0;\n"
"void main() {\n"
" gl_FragColor = vec4(b);\n"
"}\n";
if (compile(shaderString))
{
FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog;
}
}
// Global variable initializers need to be constant expressions (ESSL 3.00 section 4.3)
// Initializing with an uniform should be an error.
TEST_F(MalformedShaderTest, AssignUniformToGlobalESSL3)
{
const std::string &shaderString =
"#version 300 es\n"
"precision mediump float;\n"
"uniform float a;\n"
"float b = a * 2.0;\n"
"out vec4 my_FragColor;\n"
"void main() {\n"
" my_FragColor = vec4(b);\n"
"}\n";
if (compile(shaderString))
{
FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog;
}
}
// Global variable initializers need to be constant expressions (ESSL 1.00 section 4.3)
// Initializing with an uniform should generate a warning
// (we don't generate an error on ESSL 1.00 because of legacy compatibility)
TEST_F(MalformedShaderTest, AssignUniformToGlobalESSL1)
{
const std::string &shaderString =
"precision mediump float;\n"
"uniform float a;\n"
"float b = a * 2.0;\n"
"void main() {\n"
" gl_FragColor = vec4(b);\n"
"}\n";
if (compile(shaderString))
{
if (!hasWarning())
{
FAIL() << "Shader compilation succeeded without warnings, expecting warning " << mInfoLog;
}
}
else
{
FAIL() << "Shader compilation failed, expecting success with warning " << mInfoLog;
}
}
// Global variable initializers need to be constant expressions (ESSL 3.00 section 4.3)
// Initializing with a non-constant global should be an error.
TEST_F(MalformedShaderTest, AssignNonConstGlobalToGlobal)
{
const std::string &shaderString =
"#version 300 es\n"
"precision mediump float;\n"
"float a = 1.0;\n"
"float b = a * 2.0;\n"
"out vec4 my_FragColor;\n"
"void main() {\n"
" my_FragColor = vec4(b);\n"
"}\n";
if (compile(shaderString))
{
FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog;
}
}
// Global variable initializers need to be constant expressions (ESSL 3.00 section 4.3)
// Initializing with a constant global should be fine.
TEST_F(MalformedShaderTest, AssignConstGlobalToGlobal)
{
const std::string &shaderString =
"#version 300 es\n"
"precision mediump float;\n"
"const float a = 1.0;\n"
"float b = a * 2.0;\n"
"out vec4 my_FragColor;\n"
"void main() {\n"
" my_FragColor = vec4(b);\n"
"}\n";
if (!compile(shaderString))
{
FAIL() << "Shader compilation failed, expecting success " << mInfoLog;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment