• 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 "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