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