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