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 "common/mathutil.h"
11 #include "compiler/translator/Diagnostics.h"
12 #include "compiler/translator/Symbol.h"
13 #include "compiler/translator/SymbolTable.h"
14 #include "compiler/translator/blocklayout.h"
15 #include "compiler/translator/tree_util/IntermTraverse.h"
16 #include "compiler/translator/util.h"
17
18 namespace sh
19 {
20
21 namespace
22 {
23
24 // Arbitrarily enforce that all types declared with a size in bytes of over 2 GB will cause
25 // compilation failure.
26 //
27 // For local and global variables, the limit is much lower (16MB) as that much memory won't fit in
28 // the GPU registers anyway.
29 constexpr size_t kMaxVariableSizeInBytes = static_cast<size_t>(2) * 1024 * 1024 * 1024;
30 constexpr size_t kMaxPrivateVariableSizeInBytes = static_cast<size_t>(16) * 1024 * 1024;
31
32 // Traverses intermediate tree to ensure that the shader does not
33 // exceed certain implementation-defined limits on the sizes of types.
34 // Some code was copied from the CollectVariables pass.
35 class ValidateTypeSizeLimitationsTraverser : public TIntermTraverser
36 {
37 public:
ValidateTypeSizeLimitationsTraverser(TSymbolTable * symbolTable,TDiagnostics * diagnostics)38 ValidateTypeSizeLimitationsTraverser(TSymbolTable *symbolTable, TDiagnostics *diagnostics)
39 : TIntermTraverser(true, false, false, symbolTable),
40 mDiagnostics(diagnostics),
41 mTotalPrivateVariablesSize(0)
42 {
43 ASSERT(diagnostics);
44 }
45
visitDeclaration(Visit visit,TIntermDeclaration * node)46 bool visitDeclaration(Visit visit, TIntermDeclaration *node) override
47 {
48 const TIntermSequence &sequence = *(node->getSequence());
49
50 for (TIntermNode *variableNode : sequence)
51 {
52 // See CollectVariablesTraverser::visitDeclaration for a
53 // deeper analysis of the AST structures that might be
54 // encountered.
55 TIntermSymbol *asSymbol = variableNode->getAsSymbolNode();
56 TIntermBinary *asBinary = variableNode->getAsBinaryNode();
57
58 if (asBinary != nullptr)
59 {
60 ASSERT(asBinary->getOp() == EOpInitialize);
61 asSymbol = asBinary->getLeft()->getAsSymbolNode();
62 }
63
64 ASSERT(asSymbol);
65
66 const TVariable &variable = asSymbol->variable();
67 if (variable.symbolType() == SymbolType::AngleInternal)
68 {
69 // Ignore internal variables.
70 continue;
71 }
72
73 const TType &variableType = asSymbol->getType();
74
75 // Create a ShaderVariable from which to compute
76 // (conservative) sizing information.
77 ShaderVariable shaderVar;
78 setCommonVariableProperties(variableType, variable, &shaderVar);
79
80 // Compute the std140 layout of this variable, assuming
81 // it's a member of a block (which it might not be).
82 Std140BlockEncoder layoutEncoder;
83 BlockEncoderVisitor visitor("", "", &layoutEncoder);
84 // Since the size limit's arbitrary, it doesn't matter
85 // whether the row-major layout is correctly determined.
86 bool isRowMajorLayout = false;
87 TraverseShaderVariable(shaderVar, isRowMajorLayout, &visitor);
88 if (layoutEncoder.getCurrentOffset() > kMaxVariableSizeInBytes)
89 {
90 error(asSymbol->getLine(),
91 "Size of declared variable exceeds implementation-defined limit",
92 asSymbol->getName());
93 return false;
94 }
95
96 const bool isPrivate = variableType.getQualifier() == EvqTemporary ||
97 variableType.getQualifier() == EvqGlobal ||
98 variableType.getQualifier() == EvqConst;
99 if (isPrivate)
100 {
101 if (layoutEncoder.getCurrentOffset() > kMaxPrivateVariableSizeInBytes)
102 {
103 error(asSymbol->getLine(),
104 "Size of declared private variable exceeds implementation-defined limit",
105 asSymbol->getName());
106 return false;
107 }
108 mTotalPrivateVariablesSize += layoutEncoder.getCurrentOffset();
109 }
110 }
111
112 return true;
113 }
114
validateTotalPrivateVariableSize()115 void validateTotalPrivateVariableSize()
116 {
117 if (mTotalPrivateVariablesSize.ValueOrDefault(std::numeric_limits<size_t>::max()) >
118 kMaxPrivateVariableSizeInBytes)
119 {
120 mDiagnostics->error(
121 TSourceLoc{},
122 "Total size of declared private variables exceeds implementation-defined limit",
123 "");
124 }
125 }
126
127 private:
error(TSourceLoc loc,const char * reason,const ImmutableString & token)128 void error(TSourceLoc loc, const char *reason, const ImmutableString &token)
129 {
130 mDiagnostics->error(loc, reason, token.data());
131 }
132
setFieldOrVariableProperties(const TType & type,bool staticUse,bool isShaderIOBlock,bool isPatch,ShaderVariable * variableOut) const133 void setFieldOrVariableProperties(const TType &type,
134 bool staticUse,
135 bool isShaderIOBlock,
136 bool isPatch,
137 ShaderVariable *variableOut) const
138 {
139 ASSERT(variableOut);
140
141 variableOut->staticUse = staticUse;
142 variableOut->isShaderIOBlock = isShaderIOBlock;
143 variableOut->isPatch = isPatch;
144
145 const TStructure *structure = type.getStruct();
146 const TInterfaceBlock *interfaceBlock = type.getInterfaceBlock();
147 if (structure)
148 {
149 // Structures use a NONE type that isn't exposed outside ANGLE.
150 variableOut->type = GL_NONE;
151 if (structure->symbolType() != SymbolType::Empty)
152 {
153 variableOut->structOrBlockName = structure->name().data();
154 }
155
156 const TFieldList &fields = structure->fields();
157
158 for (const TField *field : fields)
159 {
160 // Regardless of the variable type (uniform, in/out etc.) its fields are always
161 // plain ShaderVariable objects.
162 ShaderVariable fieldVariable;
163 setFieldProperties(*field->type(), field->name(), staticUse, isShaderIOBlock,
164 isPatch, &fieldVariable);
165 variableOut->fields.push_back(fieldVariable);
166 }
167 }
168 else if (interfaceBlock && isShaderIOBlock)
169 {
170 variableOut->type = GL_NONE;
171 if (interfaceBlock->symbolType() != SymbolType::Empty)
172 {
173 variableOut->structOrBlockName = interfaceBlock->name().data();
174 }
175 const TFieldList &fields = interfaceBlock->fields();
176 for (const TField *field : fields)
177 {
178 ShaderVariable fieldVariable;
179 setFieldProperties(*field->type(), field->name(), staticUse, true, isPatch,
180 &fieldVariable);
181 fieldVariable.isShaderIOBlock = true;
182 variableOut->fields.push_back(fieldVariable);
183 }
184 }
185 else
186 {
187 variableOut->type = GLVariableType(type);
188 variableOut->precision = GLVariablePrecision(type);
189 }
190
191 const TSpan<const unsigned int> &arraySizes = type.getArraySizes();
192 if (!arraySizes.empty())
193 {
194 variableOut->arraySizes.assign(arraySizes.begin(), arraySizes.end());
195 // WebGL does not support tessellation shaders; removed
196 // code specific to that shader type.
197 }
198 }
199
setFieldProperties(const TType & type,const ImmutableString & name,bool staticUse,bool isShaderIOBlock,bool isPatch,ShaderVariable * variableOut) const200 void setFieldProperties(const TType &type,
201 const ImmutableString &name,
202 bool staticUse,
203 bool isShaderIOBlock,
204 bool isPatch,
205 ShaderVariable *variableOut) const
206 {
207 ASSERT(variableOut);
208 setFieldOrVariableProperties(type, staticUse, isShaderIOBlock, isPatch, variableOut);
209 variableOut->name.assign(name.data(), name.length());
210 }
211
setCommonVariableProperties(const TType & type,const TVariable & variable,ShaderVariable * variableOut) const212 void setCommonVariableProperties(const TType &type,
213 const TVariable &variable,
214 ShaderVariable *variableOut) const
215 {
216 ASSERT(variableOut);
217
218 // Shortcut some processing that's unnecessary for this analysis.
219 const bool staticUse = true;
220 const bool isShaderIOBlock = type.getInterfaceBlock() != nullptr;
221 const bool isPatch = false;
222
223 setFieldOrVariableProperties(type, staticUse, isShaderIOBlock, isPatch, variableOut);
224
225 const bool isNamed = variable.symbolType() != SymbolType::Empty;
226
227 if (isNamed)
228 {
229 variableOut->name.assign(variable.name().data(), variable.name().length());
230 }
231 }
232
233 TDiagnostics *mDiagnostics;
234 std::vector<int> mLoopSymbolIds;
235
236 angle::base::CheckedNumeric<size_t> mTotalPrivateVariablesSize;
237 };
238
239 } // namespace
240
ValidateTypeSizeLimitations(TIntermNode * root,TSymbolTable * symbolTable,TDiagnostics * diagnostics)241 bool ValidateTypeSizeLimitations(TIntermNode *root,
242 TSymbolTable *symbolTable,
243 TDiagnostics *diagnostics)
244 {
245 ValidateTypeSizeLimitationsTraverser validate(symbolTable, diagnostics);
246 root->traverse(&validate);
247 validate.validateTotalPrivateVariableSize();
248 return diagnostics->numErrors() == 0;
249 }
250
251 } // namespace sh
252