blob: c9607db74b53487950d31f6a56d55f3e834556a0 [file] [log] [blame]
//
// Copyright 2021 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/ValidateTypeSizeLimitations.h"
#include "angle_gl.h"
#include "compiler/translator/Diagnostics.h"
#include "compiler/translator/Symbol.h"
#include "compiler/translator/SymbolTable.h"
#include "compiler/translator/blocklayout.h"
#include "compiler/translator/tree_util/IntermTraverse.h"
#include "compiler/translator/util.h"
namespace sh
{
namespace
{
// Arbitrarily enforce that types - even local variables' - declared
// with a size in bytes of over 2 GB will cause compilation failure.
constexpr size_t kMaxTypeSizeInBytes = static_cast<size_t>(2) * 1024 * 1024 * 1024;
// Traverses intermediate tree to ensure that the shader does not
// exceed certain implementation-defined limits on the sizes of types.
// Some code was copied from the CollectVariables pass.
class ValidateTypeSizeLimitationsTraverser : public TIntermTraverser
{
public:
ValidateTypeSizeLimitationsTraverser(TSymbolTable *symbolTable, TDiagnostics *diagnostics)
: TIntermTraverser(true, false, false, symbolTable), mDiagnostics(diagnostics)
{
ASSERT(diagnostics);
}
bool visitDeclaration(Visit visit, TIntermDeclaration *node) override
{
const TIntermSequence &sequence = *(node->getSequence());
for (TIntermNode *variableNode : sequence)
{
// See CollectVariablesTraverser::visitDeclaration for a
// deeper analysis of the AST structures that might be
// encountered.
TIntermSymbol *asSymbol = variableNode->getAsSymbolNode();
TIntermBinary *asBinary = variableNode->getAsBinaryNode();
if (asBinary != nullptr)
{
ASSERT(asBinary->getOp() == EOpInitialize);
asSymbol = asBinary->getLeft()->getAsSymbolNode();
}
ASSERT(asSymbol);
const TVariable &variable = asSymbol->variable();
if (variable.symbolType() == SymbolType::AngleInternal)
{
// Ignore internal variables.
continue;
}
const TType &variableType = asSymbol->getType();
// Create a ShaderVariable from which to compute
// (conservative) sizing information.
ShaderVariable shaderVar;
setCommonVariableProperties(variableType, variable, &shaderVar);
// Compute the std140 layout of this variable, assuming
// it's a member of a block (which it might not be).
Std140BlockEncoder layoutEncoder;
BlockEncoderVisitor visitor("", "", &layoutEncoder);
// Since the size limit's arbitrary, it doesn't matter
// whether the row-major layout is correctly determined.
bool isRowMajorLayout = false;
TraverseShaderVariable(shaderVar, isRowMajorLayout, &visitor);
if (layoutEncoder.getCurrentOffset() > kMaxTypeSizeInBytes)
{
error(asSymbol->getLine(),
"Size of declared variable exceeds implementation-defined limit",
asSymbol->getName());
return false;
}
}
return true;
}
private:
void error(TSourceLoc loc, const char *reason, const ImmutableString &token)
{
mDiagnostics->error(loc, reason, token.data());
}
void setFieldOrVariableProperties(const TType &type,
bool staticUse,
bool isShaderIOBlock,
bool isPatch,
ShaderVariable *variableOut) const
{
ASSERT(variableOut);
variableOut->staticUse = staticUse;
variableOut->isShaderIOBlock = isShaderIOBlock;
variableOut->isPatch = isPatch;
const TStructure *structure = type.getStruct();
const TInterfaceBlock *interfaceBlock = type.getInterfaceBlock();
if (structure)
{
// Structures use a NONE type that isn't exposed outside ANGLE.
variableOut->type = GL_NONE;
if (structure->symbolType() != SymbolType::Empty)
{
variableOut->structOrBlockName = structure->name().data();
}
const TFieldList &fields = structure->fields();
for (const TField *field : fields)
{
// Regardless of the variable type (uniform, in/out etc.) its fields are always
// plain ShaderVariable objects.
ShaderVariable fieldVariable;
setFieldProperties(*field->type(), field->name(), staticUse, isShaderIOBlock,
isPatch, &fieldVariable);
variableOut->fields.push_back(fieldVariable);
}
}
else if (interfaceBlock && isShaderIOBlock)
{
variableOut->type = GL_NONE;
if (interfaceBlock->symbolType() != SymbolType::Empty)
{
variableOut->structOrBlockName = interfaceBlock->name().data();
}
const TFieldList &fields = interfaceBlock->fields();
for (const TField *field : fields)
{
ShaderVariable fieldVariable;
setFieldProperties(*field->type(), field->name(), staticUse, true, isPatch,
&fieldVariable);
fieldVariable.isShaderIOBlock = true;
variableOut->fields.push_back(fieldVariable);
}
}
else
{
variableOut->type = GLVariableType(type);
variableOut->precision = GLVariablePrecision(type);
}
const TSpan<const unsigned int> &arraySizes = type.getArraySizes();
if (!arraySizes.empty())
{
variableOut->arraySizes.assign(arraySizes.begin(), arraySizes.end());
// WebGL does not support tessellation shaders; removed
// code specific to that shader type.
}
}
void setFieldProperties(const TType &type,
const ImmutableString &name,
bool staticUse,
bool isShaderIOBlock,
bool isPatch,
ShaderVariable *variableOut) const
{
ASSERT(variableOut);
setFieldOrVariableProperties(type, staticUse, isShaderIOBlock, isPatch, variableOut);
variableOut->name.assign(name.data(), name.length());
}
void setCommonVariableProperties(const TType &type,
const TVariable &variable,
ShaderVariable *variableOut) const
{
ASSERT(variableOut);
// Shortcut some processing that's unnecessary for this analysis.
const bool staticUse = true;
const bool isShaderIOBlock = type.getInterfaceBlock() != nullptr;
const bool isPatch = false;
setFieldOrVariableProperties(type, staticUse, isShaderIOBlock, isPatch, variableOut);
const bool isNamed = variable.symbolType() != SymbolType::Empty;
if (isNamed)
{
variableOut->name.assign(variable.name().data(), variable.name().length());
}
}
TDiagnostics *mDiagnostics;
std::vector<int> mLoopSymbolIds;
};
} // namespace
bool ValidateTypeSizeLimitations(TIntermNode *root,
TSymbolTable *symbolTable,
TDiagnostics *diagnostics)
{
ValidateTypeSizeLimitationsTraverser validate(symbolTable, diagnostics);
root->traverse(&validate);
return diagnostics->numErrors() == 0;
}
} // namespace sh