1 //
2 // Copyright 2021 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6
7 #include "compiler/translator/ValidateTypeSizeLimitations.h"
8
9 #include "angle_gl.h"
10 #include "compiler/translator/Diagnostics.h"
11 #include "compiler/translator/Symbol.h"
12 #include "compiler/translator/SymbolTable.h"
13 #include "compiler/translator/blocklayout.h"
14 #include "compiler/translator/tree_util/IntermTraverse.h"
15 #include "compiler/translator/util.h"
16
17 namespace sh
18 {
19
20 namespace
21 {
22
23 // Arbitrarily enforce that types - even local variables' - declared
24 // with a size in bytes of over 2 GB will cause compilation failure.
25 constexpr size_t kMaxTypeSizeInBytes = static_cast<size_t>(2) * 1024 * 1024 * 1024;
26
27 // Traverses intermediate tree to ensure that the shader does not
28 // exceed certain implementation-defined limits on the sizes of types.
29 // Some code was copied from the CollectVariables pass.
30 class ValidateTypeSizeLimitationsTraverser : public TIntermTraverser
31 {
32 public:
ValidateTypeSizeLimitationsTraverser(TSymbolTable * symbolTable,TDiagnostics * diagnostics)33 ValidateTypeSizeLimitationsTraverser(TSymbolTable *symbolTable, TDiagnostics *diagnostics)
34 : TIntermTraverser(true, false, false, symbolTable), mDiagnostics(diagnostics)
35 {
36 ASSERT(diagnostics);
37 }
38
visitDeclaration(Visit visit,TIntermDeclaration * node)39 bool visitDeclaration(Visit visit, TIntermDeclaration *node) override
40 {
41 const TIntermSequence &sequence = *(node->getSequence());
42
43 for (TIntermNode *variableNode : sequence)
44 {
45 // See CollectVariablesTraverser::visitDeclaration for a
46 // deeper analysis of the AST structures that might be
47 // encountered.
48 TIntermSymbol *asSymbol = variableNode->getAsSymbolNode();
49 TIntermBinary *asBinary = variableNode->getAsBinaryNode();
50
51 if (asBinary != nullptr)
52 {
53 ASSERT(asBinary->getOp() == EOpInitialize);
54 asSymbol = asBinary->getLeft()->getAsSymbolNode();
55 }
56
57 ASSERT(asSymbol);
58
59 const TVariable &variable = asSymbol->variable();
60 if (variable.symbolType() == SymbolType::AngleInternal)
61 {
62 // Ignore internal variables.
63 continue;
64 }
65
66 const TType &variableType = asSymbol->getType();
67
68 // Create a ShaderVariable from which to compute
69 // (conservative) sizing information.
70 ShaderVariable shaderVar;
71 setCommonVariableProperties(variableType, variable, &shaderVar);
72
73 // Compute the std140 layout of this variable, assuming
74 // it's a member of a block (which it might not be).
75 Std140BlockEncoder layoutEncoder;
76 BlockEncoderVisitor visitor("", "", &layoutEncoder);
77 // Since the size limit's arbitrary, it doesn't matter
78 // whether the row-major layout is correctly determined.
79 bool isRowMajorLayout = false;
80 TraverseShaderVariable(shaderVar, isRowMajorLayout, &visitor);
81 if (layoutEncoder.getCurrentOffset() > kMaxTypeSizeInBytes)
82 {
83 error(asSymbol->getLine(),
84 "Size of declared variable exceeds implementation-defined limit",
85 asSymbol->getName());
86 return false;
87 }
88 }
89
90 return true;
91 }
92
93 private:
error(TSourceLoc loc,const char * reason,const ImmutableString & token)94 void error(TSourceLoc loc, const char *reason, const ImmutableString &token)
95 {
96 mDiagnostics->error(loc, reason, token.data());
97 }
98
setFieldOrVariableProperties(const TType & type,bool staticUse,bool isShaderIOBlock,bool isPatch,ShaderVariable * variableOut) const99 void setFieldOrVariableProperties(const TType &type,
100 bool staticUse,
101 bool isShaderIOBlock,
102 bool isPatch,
103 ShaderVariable *variableOut) const
104 {
105 ASSERT(variableOut);
106
107 variableOut->staticUse = staticUse;
108 variableOut->isShaderIOBlock = isShaderIOBlock;
109 variableOut->isPatch = isPatch;
110
111 const TStructure *structure = type.getStruct();
112 const TInterfaceBlock *interfaceBlock = type.getInterfaceBlock();
113 if (structure)
114 {
115 // Structures use a NONE type that isn't exposed outside ANGLE.
116 variableOut->type = GL_NONE;
117 if (structure->symbolType() != SymbolType::Empty)
118 {
119 variableOut->structOrBlockName = structure->name().data();
120 }
121
122 const TFieldList &fields = structure->fields();
123
124 for (const TField *field : fields)
125 {
126 // Regardless of the variable type (uniform, in/out etc.) its fields are always
127 // plain ShaderVariable objects.
128 ShaderVariable fieldVariable;
129 setFieldProperties(*field->type(), field->name(), staticUse, isShaderIOBlock,
130 isPatch, &fieldVariable);
131 variableOut->fields.push_back(fieldVariable);
132 }
133 }
134 else if (interfaceBlock && isShaderIOBlock)
135 {
136 variableOut->type = GL_NONE;
137 if (interfaceBlock->symbolType() != SymbolType::Empty)
138 {
139 variableOut->structOrBlockName = interfaceBlock->name().data();
140 }
141 const TFieldList &fields = interfaceBlock->fields();
142 for (const TField *field : fields)
143 {
144 ShaderVariable fieldVariable;
145 setFieldProperties(*field->type(), field->name(), staticUse, true, isPatch,
146 &fieldVariable);
147 fieldVariable.isShaderIOBlock = true;
148 variableOut->fields.push_back(fieldVariable);
149 }
150 }
151 else
152 {
153 variableOut->type = GLVariableType(type);
154 variableOut->precision = GLVariablePrecision(type);
155 }
156
157 const TSpan<const unsigned int> &arraySizes = type.getArraySizes();
158 if (!arraySizes.empty())
159 {
160 variableOut->arraySizes.assign(arraySizes.begin(), arraySizes.end());
161 // WebGL does not support tessellation shaders; removed
162 // code specific to that shader type.
163 }
164 }
165
setFieldProperties(const TType & type,const ImmutableString & name,bool staticUse,bool isShaderIOBlock,bool isPatch,ShaderVariable * variableOut) const166 void setFieldProperties(const TType &type,
167 const ImmutableString &name,
168 bool staticUse,
169 bool isShaderIOBlock,
170 bool isPatch,
171 ShaderVariable *variableOut) const
172 {
173 ASSERT(variableOut);
174 setFieldOrVariableProperties(type, staticUse, isShaderIOBlock, isPatch, variableOut);
175 variableOut->name.assign(name.data(), name.length());
176 }
177
setCommonVariableProperties(const TType & type,const TVariable & variable,ShaderVariable * variableOut) const178 void setCommonVariableProperties(const TType &type,
179 const TVariable &variable,
180 ShaderVariable *variableOut) const
181 {
182 ASSERT(variableOut);
183
184 // Shortcut some processing that's unnecessary for this analysis.
185 const bool staticUse = true;
186 const bool isShaderIOBlock = type.getInterfaceBlock() != nullptr;
187 const bool isPatch = false;
188
189 setFieldOrVariableProperties(type, staticUse, isShaderIOBlock, isPatch, variableOut);
190
191 const bool isNamed = variable.symbolType() != SymbolType::Empty;
192
193 if (isNamed)
194 {
195 variableOut->name.assign(variable.name().data(), variable.name().length());
196 }
197 }
198
199 TDiagnostics *mDiagnostics;
200 std::vector<int> mLoopSymbolIds;
201 };
202
203 } // namespace
204
ValidateTypeSizeLimitations(TIntermNode * root,TSymbolTable * symbolTable,TDiagnostics * diagnostics)205 bool ValidateTypeSizeLimitations(TIntermNode *root,
206 TSymbolTable *symbolTable,
207 TDiagnostics *diagnostics)
208 {
209 ValidateTypeSizeLimitationsTraverser validate(symbolTable, diagnostics);
210 root->traverse(&validate);
211 return diagnostics->numErrors() == 0;
212 }
213
214 } // namespace sh
215