• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright (c) 2002-2013 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/BuiltInFunctionEmulator.h"
8 #include "compiler/DetectCallDepth.h"
9 #include "compiler/ForLoopUnroll.h"
10 #include "compiler/Initialize.h"
11 #include "compiler/InitializeGLPosition.h"
12 #include "compiler/InitializeParseContext.h"
13 #include "compiler/MapLongVariableNames.h"
14 #include "compiler/ParseContext.h"
15 #include "compiler/RenameFunction.h"
16 #include "compiler/ShHandle.h"
17 #include "compiler/UnfoldShortCircuitAST.h"
18 #include "compiler/ValidateLimitations.h"
19 #include "compiler/VariablePacker.h"
20 #include "compiler/depgraph/DependencyGraph.h"
21 #include "compiler/depgraph/DependencyGraphOutput.h"
22 #include "compiler/timing/RestrictFragmentShaderTiming.h"
23 #include "compiler/timing/RestrictVertexShaderTiming.h"
24 #include "third_party/compiler/ArrayBoundsClamper.h"
25 
isWebGLBasedSpec(ShShaderSpec spec)26 bool isWebGLBasedSpec(ShShaderSpec spec)
27 {
28      return spec == SH_WEBGL_SPEC || spec == SH_CSS_SHADERS_SPEC;
29 }
30 
31 namespace {
32 class TScopedPoolAllocator {
33 public:
TScopedPoolAllocator(TPoolAllocator * allocator)34     TScopedPoolAllocator(TPoolAllocator* allocator) : mAllocator(allocator) {
35         mAllocator->push();
36         SetGlobalPoolAllocator(mAllocator);
37     }
~TScopedPoolAllocator()38     ~TScopedPoolAllocator() {
39         SetGlobalPoolAllocator(NULL);
40         mAllocator->pop();
41     }
42 
43 private:
44     TPoolAllocator* mAllocator;
45 };
46 
47 class TScopedSymbolTableLevel {
48 public:
TScopedSymbolTableLevel(TSymbolTable * table)49     TScopedSymbolTableLevel(TSymbolTable* table) : mTable(table) {
50         ASSERT(mTable->atBuiltInLevel());
51         mTable->push();
52     }
~TScopedSymbolTableLevel()53     ~TScopedSymbolTableLevel() {
54         while (!mTable->atBuiltInLevel())
55             mTable->pop();
56     }
57 
58 private:
59     TSymbolTable* mTable;
60 };
61 }  // namespace
62 
TShHandleBase()63 TShHandleBase::TShHandleBase() {
64     allocator.push();
65     SetGlobalPoolAllocator(&allocator);
66 }
67 
~TShHandleBase()68 TShHandleBase::~TShHandleBase() {
69     SetGlobalPoolAllocator(NULL);
70     allocator.popAll();
71 }
72 
TCompiler(ShShaderType type,ShShaderSpec spec)73 TCompiler::TCompiler(ShShaderType type, ShShaderSpec spec)
74     : shaderType(type),
75       shaderSpec(spec),
76       maxUniformVectors(0),
77       maxExpressionComplexity(0),
78       maxCallStackDepth(0),
79       fragmentPrecisionHigh(false),
80       clampingStrategy(SH_CLAMP_WITH_CLAMP_INTRINSIC),
81       builtInFunctionEmulator(type)
82 {
83     longNameMap = LongNameMap::GetInstance();
84 }
85 
~TCompiler()86 TCompiler::~TCompiler()
87 {
88     ASSERT(longNameMap);
89     longNameMap->Release();
90 }
91 
Init(const ShBuiltInResources & resources)92 bool TCompiler::Init(const ShBuiltInResources& resources)
93 {
94     maxUniformVectors = (shaderType == SH_VERTEX_SHADER) ?
95         resources.MaxVertexUniformVectors :
96         resources.MaxFragmentUniformVectors;
97     maxExpressionComplexity = resources.MaxExpressionComplexity;
98     maxCallStackDepth = resources.MaxCallStackDepth;
99 
100     SetGlobalPoolAllocator(&allocator);
101 
102     // Generate built-in symbol table.
103     if (!InitBuiltInSymbolTable(resources))
104         return false;
105     InitExtensionBehavior(resources, extensionBehavior);
106     fragmentPrecisionHigh = resources.FragmentPrecisionHigh == 1;
107 
108     arrayBoundsClamper.SetClampingStrategy(resources.ArrayIndexClampingStrategy);
109     clampingStrategy = resources.ArrayIndexClampingStrategy;
110 
111     hashFunction = resources.HashFunction;
112 
113     return true;
114 }
115 
compile(const char * const shaderStrings[],size_t numStrings,int compileOptions)116 bool TCompiler::compile(const char* const shaderStrings[],
117                         size_t numStrings,
118                         int compileOptions)
119 {
120     TScopedPoolAllocator scopedAlloc(&allocator);
121     clearResults();
122 
123     if (numStrings == 0)
124         return true;
125 
126     // If compiling for WebGL, validate loop and indexing as well.
127     if (isWebGLBasedSpec(shaderSpec))
128         compileOptions |= SH_VALIDATE_LOOP_INDEXING;
129 
130     // First string is path of source file if flag is set. The actual source follows.
131     const char* sourcePath = NULL;
132     size_t firstSource = 0;
133     if (compileOptions & SH_SOURCE_PATH)
134     {
135         sourcePath = shaderStrings[0];
136         ++firstSource;
137     }
138 
139     TIntermediate intermediate(infoSink);
140     TParseContext parseContext(symbolTable, extensionBehavior, intermediate,
141                                shaderType, shaderSpec, compileOptions, true,
142                                sourcePath, infoSink);
143     parseContext.fragmentPrecisionHigh = fragmentPrecisionHigh;
144     SetGlobalParseContext(&parseContext);
145 
146     // We preserve symbols at the built-in level from compile-to-compile.
147     // Start pushing the user-defined symbols at global level.
148     TScopedSymbolTableLevel scopedSymbolLevel(&symbolTable);
149 
150     // Parse shader.
151     bool success =
152         (PaParseStrings(numStrings - firstSource, &shaderStrings[firstSource], NULL, &parseContext) == 0) &&
153         (parseContext.treeRoot != NULL);
154     if (success) {
155         TIntermNode* root = parseContext.treeRoot;
156         success = intermediate.postProcess(root);
157 
158         if (success)
159             success = detectCallDepth(root, infoSink, (compileOptions & SH_LIMIT_CALL_STACK_DEPTH) != 0);
160 
161         if (success && (compileOptions & SH_VALIDATE_LOOP_INDEXING))
162             success = validateLimitations(root);
163 
164         if (success && (compileOptions & SH_TIMING_RESTRICTIONS))
165             success = enforceTimingRestrictions(root, (compileOptions & SH_DEPENDENCY_GRAPH) != 0);
166 
167         if (success && shaderSpec == SH_CSS_SHADERS_SPEC)
168             rewriteCSSShader(root);
169 
170         // Unroll for-loop markup needs to happen after validateLimitations pass.
171         if (success && (compileOptions & SH_UNROLL_FOR_LOOP_WITH_INTEGER_INDEX))
172             ForLoopUnroll::MarkForLoopsWithIntegerIndicesForUnrolling(root);
173 
174         // Built-in function emulation needs to happen after validateLimitations pass.
175         if (success && (compileOptions & SH_EMULATE_BUILT_IN_FUNCTIONS))
176             builtInFunctionEmulator.MarkBuiltInFunctionsForEmulation(root);
177 
178         // Clamping uniform array bounds needs to happen after validateLimitations pass.
179         if (success && (compileOptions & SH_CLAMP_INDIRECT_ARRAY_BOUNDS))
180             arrayBoundsClamper.MarkIndirectArrayBoundsForClamping(root);
181 
182         // Disallow expressions deemed too complex.
183         if (success && (compileOptions & SH_LIMIT_EXPRESSION_COMPLEXITY))
184             success = limitExpressionComplexity(root);
185 
186         // Call mapLongVariableNames() before collectAttribsUniforms() so in
187         // collectAttribsUniforms() we already have the mapped symbol names and
188         // we could composite mapped and original variable names.
189         // Also, if we hash all the names, then no need to do this for long names.
190         if (success && (compileOptions & SH_MAP_LONG_VARIABLE_NAMES) && hashFunction == NULL)
191             mapLongVariableNames(root);
192 
193         if (success && shaderType == SH_VERTEX_SHADER && (compileOptions & SH_INIT_GL_POSITION)) {
194             InitializeGLPosition initGLPosition;
195             root->traverse(&initGLPosition);
196         }
197 
198 	if (success && (compileOptions & SH_UNFOLD_SHORT_CIRCUIT)) {
199             UnfoldShortCircuitAST unfoldShortCircuit;
200             root->traverse(&unfoldShortCircuit);
201             unfoldShortCircuit.updateTree();
202 	}
203 
204         if (success && (compileOptions & SH_VARIABLES)) {
205             collectVariables(root);
206             if (compileOptions & SH_ENFORCE_PACKING_RESTRICTIONS) {
207                 success = enforcePackingRestrictions();
208                 if (!success) {
209                     infoSink.info.prefix(EPrefixError);
210                     infoSink.info << "too many uniforms";
211                 }
212             }
213         }
214 
215         if (success && (compileOptions & SH_INTERMEDIATE_TREE))
216             intermediate.outputTree(root);
217 
218         if (success && (compileOptions & SH_OBJECT_CODE))
219             translate(root);
220     }
221 
222     // Cleanup memory.
223     intermediate.remove(parseContext.treeRoot);
224 
225     return success;
226 }
227 
InitBuiltInSymbolTable(const ShBuiltInResources & resources)228 bool TCompiler::InitBuiltInSymbolTable(const ShBuiltInResources &resources)
229 {
230     compileResources = resources;
231 
232     assert(symbolTable.isEmpty());
233     symbolTable.push();
234 
235     TPublicType integer;
236     integer.type = EbtInt;
237     integer.size = 1;
238     integer.matrix = false;
239     integer.array = false;
240 
241     TPublicType floatingPoint;
242     floatingPoint.type = EbtFloat;
243     floatingPoint.size = 1;
244     floatingPoint.matrix = false;
245     floatingPoint.array = false;
246 
247     TPublicType sampler;
248     sampler.size = 1;
249     sampler.matrix = false;
250     sampler.array = false;
251 
252     switch(shaderType)
253     {
254       case SH_FRAGMENT_SHADER:
255         symbolTable.setDefaultPrecision(integer, EbpMedium);
256         break;
257       case SH_VERTEX_SHADER:
258         symbolTable.setDefaultPrecision(integer, EbpHigh);
259         symbolTable.setDefaultPrecision(floatingPoint, EbpHigh);
260         break;
261       default: assert(false && "Language not supported");
262     }
263     // We set defaults for all the sampler types, even those that are
264     // only available if an extension exists.
265     for (int samplerType = EbtGuardSamplerBegin + 1;
266          samplerType < EbtGuardSamplerEnd; ++samplerType) {
267         sampler.type = static_cast<TBasicType>(samplerType);
268         symbolTable.setDefaultPrecision(sampler, EbpLow);
269     }
270 
271     InsertBuiltInFunctions(shaderType, shaderSpec, resources, symbolTable);
272 
273     IdentifyBuiltIns(shaderType, shaderSpec, resources, symbolTable);
274 
275     return true;
276 }
277 
clearResults()278 void TCompiler::clearResults()
279 {
280     arrayBoundsClamper.Cleanup();
281     infoSink.info.erase();
282     infoSink.obj.erase();
283     infoSink.debug.erase();
284 
285     attribs.clear();
286     uniforms.clear();
287     varyings.clear();
288 
289     builtInFunctionEmulator.Cleanup();
290 
291     nameMap.clear();
292 }
293 
detectCallDepth(TIntermNode * root,TInfoSink & infoSink,bool limitCallStackDepth)294 bool TCompiler::detectCallDepth(TIntermNode* root, TInfoSink& infoSink, bool limitCallStackDepth)
295 {
296     DetectCallDepth detect(infoSink, limitCallStackDepth, maxCallStackDepth);
297     root->traverse(&detect);
298     switch (detect.detectCallDepth()) {
299         case DetectCallDepth::kErrorNone:
300             return true;
301         case DetectCallDepth::kErrorMissingMain:
302             infoSink.info.prefix(EPrefixError);
303             infoSink.info << "Missing main()";
304             return false;
305         case DetectCallDepth::kErrorRecursion:
306             infoSink.info.prefix(EPrefixError);
307             infoSink.info << "Function recursion detected";
308             return false;
309         case DetectCallDepth::kErrorMaxDepthExceeded:
310             infoSink.info.prefix(EPrefixError);
311             infoSink.info << "Function call stack too deep";
312             return false;
313         default:
314             UNREACHABLE();
315             return false;
316     }
317 }
318 
rewriteCSSShader(TIntermNode * root)319 void TCompiler::rewriteCSSShader(TIntermNode* root)
320 {
321     RenameFunction renamer("main(", "css_main(");
322     root->traverse(&renamer);
323 }
324 
validateLimitations(TIntermNode * root)325 bool TCompiler::validateLimitations(TIntermNode* root) {
326     ValidateLimitations validate(shaderType, infoSink.info);
327     root->traverse(&validate);
328     return validate.numErrors() == 0;
329 }
330 
enforceTimingRestrictions(TIntermNode * root,bool outputGraph)331 bool TCompiler::enforceTimingRestrictions(TIntermNode* root, bool outputGraph)
332 {
333     if (shaderSpec != SH_WEBGL_SPEC) {
334         infoSink.info << "Timing restrictions must be enforced under the WebGL spec.";
335         return false;
336     }
337 
338     if (shaderType == SH_FRAGMENT_SHADER) {
339         TDependencyGraph graph(root);
340 
341         // Output any errors first.
342         bool success = enforceFragmentShaderTimingRestrictions(graph);
343 
344         // Then, output the dependency graph.
345         if (outputGraph) {
346             TDependencyGraphOutput output(infoSink.info);
347             output.outputAllSpanningTrees(graph);
348         }
349 
350         return success;
351     }
352     else {
353         return enforceVertexShaderTimingRestrictions(root);
354     }
355 }
356 
limitExpressionComplexity(TIntermNode * root)357 bool TCompiler::limitExpressionComplexity(TIntermNode* root)
358 {
359     TIntermTraverser traverser;
360     root->traverse(&traverser);
361     TDependencyGraph graph(root);
362 
363     for (TFunctionCallVector::const_iterator iter = graph.beginUserDefinedFunctionCalls();
364          iter != graph.endUserDefinedFunctionCalls();
365          ++iter)
366     {
367         TGraphFunctionCall* samplerSymbol = *iter;
368         TDependencyGraphTraverser graphTraverser;
369         samplerSymbol->traverse(&graphTraverser);
370     }
371 
372     if (traverser.getMaxDepth() > maxExpressionComplexity) {
373         infoSink.info << "Expression too complex.";
374         return false;
375     }
376     return true;
377 }
378 
enforceFragmentShaderTimingRestrictions(const TDependencyGraph & graph)379 bool TCompiler::enforceFragmentShaderTimingRestrictions(const TDependencyGraph& graph)
380 {
381     RestrictFragmentShaderTiming restrictor(infoSink.info);
382     restrictor.enforceRestrictions(graph);
383     return restrictor.numErrors() == 0;
384 }
385 
enforceVertexShaderTimingRestrictions(TIntermNode * root)386 bool TCompiler::enforceVertexShaderTimingRestrictions(TIntermNode* root)
387 {
388     RestrictVertexShaderTiming restrictor(infoSink.info);
389     restrictor.enforceRestrictions(root);
390     return restrictor.numErrors() == 0;
391 }
392 
collectVariables(TIntermNode * root)393 void TCompiler::collectVariables(TIntermNode* root)
394 {
395     CollectVariables collect(attribs, uniforms, varyings, hashFunction);
396     root->traverse(&collect);
397 }
398 
enforcePackingRestrictions()399 bool TCompiler::enforcePackingRestrictions()
400 {
401     VariablePacker packer;
402     return packer.CheckVariablesWithinPackingLimits(maxUniformVectors, uniforms);
403 }
404 
mapLongVariableNames(TIntermNode * root)405 void TCompiler::mapLongVariableNames(TIntermNode* root)
406 {
407     ASSERT(longNameMap);
408     MapLongVariableNames map(longNameMap);
409     root->traverse(&map);
410 }
411 
getMappedNameMaxLength() const412 int TCompiler::getMappedNameMaxLength() const
413 {
414     return MAX_SHORTENED_IDENTIFIER_SIZE + 1;
415 }
416 
getExtensionBehavior() const417 const TExtensionBehavior& TCompiler::getExtensionBehavior() const
418 {
419     return extensionBehavior;
420 }
421 
getResources() const422 const ShBuiltInResources& TCompiler::getResources() const
423 {
424     return compileResources;
425 }
426 
getArrayBoundsClamper() const427 const ArrayBoundsClamper& TCompiler::getArrayBoundsClamper() const
428 {
429     return arrayBoundsClamper;
430 }
431 
getArrayIndexClampingStrategy() const432 ShArrayIndexClampingStrategy TCompiler::getArrayIndexClampingStrategy() const
433 {
434     return clampingStrategy;
435 }
436 
getBuiltInFunctionEmulator() const437 const BuiltInFunctionEmulator& TCompiler::getBuiltInFunctionEmulator() const
438 {
439     return builtInFunctionEmulator;
440 }
441