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
26bool isWebGLBasedSpec(ShShaderSpec spec)
27{
28     return spec == SH_WEBGL_SPEC || spec == SH_CSS_SHADERS_SPEC;
29}
30
31namespace {
32class TScopedPoolAllocator {
33public:
34    TScopedPoolAllocator(TPoolAllocator* allocator) : mAllocator(allocator) {
35        mAllocator->push();
36        SetGlobalPoolAllocator(mAllocator);
37    }
38    ~TScopedPoolAllocator() {
39        SetGlobalPoolAllocator(NULL);
40        mAllocator->pop();
41    }
42
43private:
44    TPoolAllocator* mAllocator;
45};
46
47class TScopedSymbolTableLevel {
48public:
49    TScopedSymbolTableLevel(TSymbolTable* table) : mTable(table) {
50        ASSERT(mTable->atBuiltInLevel());
51        mTable->push();
52    }
53    ~TScopedSymbolTableLevel() {
54        while (!mTable->atBuiltInLevel())
55            mTable->pop();
56    }
57
58private:
59    TSymbolTable* mTable;
60};
61}  // namespace
62
63TShHandleBase::TShHandleBase() {
64    allocator.push();
65    SetGlobalPoolAllocator(&allocator);
66}
67
68TShHandleBase::~TShHandleBase() {
69    SetGlobalPoolAllocator(NULL);
70    allocator.popAll();
71}
72
73TCompiler::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
86TCompiler::~TCompiler()
87{
88    ASSERT(longNameMap);
89    longNameMap->Release();
90}
91
92bool 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
116bool 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
228bool 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
278void 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
294bool 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
319void TCompiler::rewriteCSSShader(TIntermNode* root)
320{
321    RenameFunction renamer("main(", "css_main(");
322    root->traverse(&renamer);
323}
324
325bool TCompiler::validateLimitations(TIntermNode* root) {
326    ValidateLimitations validate(shaderType, infoSink.info);
327    root->traverse(&validate);
328    return validate.numErrors() == 0;
329}
330
331bool 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
357bool 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
379bool TCompiler::enforceFragmentShaderTimingRestrictions(const TDependencyGraph& graph)
380{
381    RestrictFragmentShaderTiming restrictor(infoSink.info);
382    restrictor.enforceRestrictions(graph);
383    return restrictor.numErrors() == 0;
384}
385
386bool TCompiler::enforceVertexShaderTimingRestrictions(TIntermNode* root)
387{
388    RestrictVertexShaderTiming restrictor(infoSink.info);
389    restrictor.enforceRestrictions(root);
390    return restrictor.numErrors() == 0;
391}
392
393void TCompiler::collectVariables(TIntermNode* root)
394{
395    CollectVariables collect(attribs, uniforms, varyings, hashFunction);
396    root->traverse(&collect);
397}
398
399bool TCompiler::enforcePackingRestrictions()
400{
401    VariablePacker packer;
402    return packer.CheckVariablesWithinPackingLimits(maxUniformVectors, uniforms);
403}
404
405void TCompiler::mapLongVariableNames(TIntermNode* root)
406{
407    ASSERT(longNameMap);
408    MapLongVariableNames map(longNameMap);
409    root->traverse(&map);
410}
411
412int TCompiler::getMappedNameMaxLength() const
413{
414    return MAX_SHORTENED_IDENTIFIER_SIZE + 1;
415}
416
417const TExtensionBehavior& TCompiler::getExtensionBehavior() const
418{
419    return extensionBehavior;
420}
421
422const ShBuiltInResources& TCompiler::getResources() const
423{
424    return compileResources;
425}
426
427const ArrayBoundsClamper& TCompiler::getArrayBoundsClamper() const
428{
429    return arrayBoundsClamper;
430}
431
432ShArrayIndexClampingStrategy TCompiler::getArrayIndexClampingStrategy() const
433{
434    return clampingStrategy;
435}
436
437const BuiltInFunctionEmulator& TCompiler::getBuiltInFunctionEmulator() const
438{
439    return builtInFunctionEmulator;
440}
441