• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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