Hash :
18fa02be
Author :
Date :
2024-03-12T10:23:55
Rewrite exprs using separated decl variables
Rewrite expressions that use the rewritten struct declaration variables.
Declaration that specfies a struct and defines multiple variables will
get its variable declarations separated. The type of the variable
changes when a struct specifier is removed for the second and rest of
the variable declarations. The type of the variable changes also when a
anonymous struct is named.
The expressions that used the separated variables used the old struct as
their types.
Fix by using TIntermRebuild. Upon creating a new symbol node referencing
the new type, the rebuilder will instantiate also all the needed
intermediate nodes, which then get the correct struct type.
For consistency, fix the case of anonymous struct -> named struct
transform naming the variables similar to named struct separation.
Consider base case:
struct S { .. } a, b; -> struct S { .. } a; S b;
Compare against case:
struct { .. } a, b;
Before, inconsistency:
struct s1 { .. }; s1 a; s1 b;
After, fixed:
struct s1 { .. } a; s1 b;
Bug: angleproject:8590
Change-Id: Iffb0ef4441d6021e076b04485b808b26a7fa4dcb
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/5365201
Auto-Submit: Kimmo Kinnunen <kkinnunen@apple.com>
Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org>
Commit-Queue: Kimmo Kinnunen <kkinnunen@apple.com>
Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
//
// Copyright 2024 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.
//
// Parse_test.cpp:
// Test for parsing erroneous and correct GLSL input.
//
#include <memory>
#include "GLSLANG/ShaderLang.h"
#include "angle_gl.h"
#include "compiler/translator/glsl/TranslatorESSL.h"
#include "gtest/gtest.h"
using namespace sh;
class ParseTest : public testing::Test
{
public:
ParseTest()
{
InitBuiltInResources(&mResources);
mResources.FragmentPrecisionHigh = 1;
mCompileOptions.intermediateTree = true;
}
protected:
void TearDown() override { mTranslator.reset(); }
testing::AssertionResult compile(const std::string &shaderString)
{
if (mTranslator == nullptr)
{
std::unique_ptr<TranslatorESSL> translator =
std::make_unique<TranslatorESSL>(GL_FRAGMENT_SHADER, mShaderSpec);
if (!translator->Init(mResources))
{
return testing::AssertionFailure() << "Failed to initialize translator";
}
mTranslator = std::move(translator);
}
const char *shaderStrings[] = {shaderString.c_str()};
bool compilationSuccess = mTranslator->compile(shaderStrings, 1, mCompileOptions);
TInfoSink &infoSink = mTranslator->getInfoSink();
mInfoLog = RemoveSymbolIdsFromInfoLog(infoSink.info.c_str());
if (!compilationSuccess)
{
return testing::AssertionFailure() << "Shader compilation failed " << mInfoLog;
}
return testing::AssertionSuccess();
}
bool foundErrorInIntermediateTree() const { return foundInIntermediateTree("ERROR:"); }
bool foundInIntermediateTree(const char *stringToFind) const
{
return mInfoLog.find(stringToFind) != std::string::npos;
}
ShBuiltInResources mResources;
ShCompileOptions mCompileOptions{};
ShShaderSpec mShaderSpec = SH_WEBGL_SPEC;
private:
// Remove symbol ids from info log - the tests don't care about them.
static std::string RemoveSymbolIdsFromInfoLog(const char *infoLog)
{
std::string filteredLog(infoLog);
size_t idPrefixPos = 0u;
do
{
idPrefixPos = filteredLog.find(" (symbol id");
if (idPrefixPos != std::string::npos)
{
size_t idSuffixPos = filteredLog.find(")", idPrefixPos);
filteredLog.erase(idPrefixPos, idSuffixPos - idPrefixPos + 1u);
}
} while (idPrefixPos != std::string::npos);
return filteredLog;
}
std::unique_ptr<TranslatorESSL> mTranslator;
std::string mInfoLog;
};
TEST_F(ParseTest, UnsizedArrayConstructorNoCrash)
{
const char kShader[] = R"(#version 310 es\n"
int A[];
int B[int[][](A)];)";
EXPECT_FALSE(compile(kShader));
EXPECT_TRUE(foundErrorInIntermediateTree());
EXPECT_TRUE(foundInIntermediateTree("constructing from an unsized array"));
}
TEST_F(ParseTest, UniformBlockNameReferenceNoCrash)
{
const char kShader[] = R"(#version 300 es
precision mediump float;
out float o;
uniform a { float r; } UBOA;
void main() {
o = float(UBOA);
})";
EXPECT_FALSE(compile(kShader));
EXPECT_TRUE(foundErrorInIntermediateTree());
EXPECT_TRUE(foundInIntermediateTree(
"interface block cannot be used as a constructor argument for this type"));
}
TEST_F(ParseTest, Precise320NoCrash)
{
const char kShader[] = R"(#version 320 es
precision mediump float;
void main(){
float t;
precise t;
})";
EXPECT_FALSE(compile(kShader));
EXPECT_TRUE(foundErrorInIntermediateTree());
EXPECT_TRUE(foundInIntermediateTree("unsupported shader version"));
}
// Tests that layout(index=0) is parsed in es 100 shaders if an extension like
// EXT_shader_framebuffer_fetch is enabled, but this does not cause a crash.
TEST_F(ParseTest, ShaderFramebufferFetchLayoutIndexNoCrash)
{
mResources.EXT_blend_func_extended = 1;
mResources.MaxDualSourceDrawBuffers = 1;
mResources.EXT_shader_framebuffer_fetch = 1;
const char kShader[] = R"(
#extension GL_EXT_blend_func_extended: require
#extension GL_EXT_shader_framebuffer_fetch : require
layout(index=0)mediump vec4 c;
void main() { }
)";
EXPECT_FALSE(compile(kShader));
EXPECT_TRUE(foundErrorInIntermediateTree());
EXPECT_TRUE(foundInIntermediateTree("'index' : invalid layout qualifier"));
}
TEST_F(ParseTest, Radians320NoCrash)
{
const char kShader[] = R"(#version 320 es
precision mediump float;
vec4 s() { writeonly vec4 color; radians(color); return vec4(1); })";
EXPECT_FALSE(compile(kShader));
EXPECT_TRUE(foundErrorInIntermediateTree());
EXPECT_TRUE(foundInIntermediateTree("'writeonly' : Only allowed with shader storage blocks,"));
EXPECT_TRUE(foundInIntermediateTree(
"'radians' : wrong operand type - no operation 'radians' exists that"));
}
TEST_F(ParseTest, CoherentCoherentNoCrash)
{
const char kShader[] = R"(#version 310 es
uniform highp coherent coherent readonly image2D image1;\n"
void main() {
})";
EXPECT_FALSE(compile(kShader));
EXPECT_TRUE(foundErrorInIntermediateTree());
EXPECT_TRUE(foundInIntermediateTree("coherent specified multiple times"));
}
TEST_F(ParseTest, LargeArrayIndexNoCrash)
{
mShaderSpec = SH_WEBGL2_SPEC;
const char kShader[] = R"(#version 300 es
int rr[~1U];
out int o;
void main() {
o = rr[1];
})";
EXPECT_FALSE(compile(kShader));
EXPECT_TRUE(foundErrorInIntermediateTree());
EXPECT_TRUE(
foundInIntermediateTree("Size of declared variable exceeds implementation-defined limit"));
}
// Tests that separating variable declaration of multiple instances of a anonymous structure
// rewrites the expression types for expressions that use the variables. At the time of writing
// the expression types were left referencing the original anonymous function.
TEST_F(ParseTest, SeparateAnonymousFunctionsRewritesExpressions)
{
const char kShader[] = R"(
struct {
mediump vec2 d;
} s0, s1;
void main() {
s0 = s0;
s1 = s1;
})";
EXPECT_TRUE(compile(kShader));
EXPECT_FALSE(foundInIntermediateTree("anonymous"));
}