2
// Copyright (c) 2002-2012 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.
7
#include "compiler/ArrayBoundsClamper.h"
8
#include "compiler/BuiltInFunctionEmulator.h"
9
#include "compiler/DetectRecursion.h"
10
#include "compiler/ForLoopUnroll.h"
11
#include "compiler/Initialize.h"
12
#include "compiler/InitializeParseContext.h"
13
#include "compiler/MapLongVariableNames.h"
14
#include "compiler/ParseHelper.h"
15
#include "compiler/RenameFunction.h"
16
#include "compiler/ShHandle.h"
17
#include "compiler/ValidateLimitations.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"
23
bool isWebGLBasedSpec(ShShaderSpec spec)
25
return spec == SH_WEBGL_SPEC || spec == SH_CSS_SHADERS_SPEC;
29
bool InitializeSymbolTable(
30
const TBuiltInStrings& builtInStrings,
31
ShShaderType type, ShShaderSpec spec, const ShBuiltInResources& resources,
32
TInfoSink& infoSink, TSymbolTable& symbolTable)
34
TIntermediate intermediate(infoSink);
35
TExtensionBehavior extBehavior;
36
InitExtensionBehavior(resources, extBehavior);
37
// The builtins deliberately don't specify precisions for the function
38
// arguments and return types. For that reason we don't try to check them.
39
TParseContext parseContext(symbolTable, extBehavior, intermediate, type, spec, 0, false, NULL, infoSink);
41
GlobalParseContext = &parseContext;
43
assert(symbolTable.isEmpty());
45
// Parse the built-ins. This should only happen once per
46
// language symbol table.
48
// Push the symbol table to give it an initial scope. This
49
// push should not have a corresponding pop, so that built-ins
50
// are preserved, and the test for an empty table fails.
54
for (TBuiltInStrings::const_iterator i = builtInStrings.begin(); i != builtInStrings.end(); ++i)
56
const char* builtInShaders = i->c_str();
57
int builtInLengths = static_cast<int>(i->size());
58
if (builtInLengths <= 0)
61
if (PaParseStrings(1, &builtInShaders, &builtInLengths, &parseContext) != 0)
63
infoSink.info.message(EPrefixInternalError, "Unable to parse built-ins");
68
IdentifyBuiltIns(type, spec, resources, symbolTable);
73
class TScopedPoolAllocator {
75
TScopedPoolAllocator(TPoolAllocator* allocator, bool pushPop)
76
: mAllocator(allocator), mPushPopAllocator(pushPop) {
77
if (mPushPopAllocator) mAllocator->push();
78
SetGlobalPoolAllocator(mAllocator);
80
~TScopedPoolAllocator() {
81
SetGlobalPoolAllocator(NULL);
82
if (mPushPopAllocator) mAllocator->pop();
86
TPoolAllocator* mAllocator;
87
bool mPushPopAllocator;
91
TShHandleBase::TShHandleBase() {
93
SetGlobalPoolAllocator(&allocator);
96
TShHandleBase::~TShHandleBase() {
97
SetGlobalPoolAllocator(NULL);
101
TCompiler::TCompiler(ShShaderType type, ShShaderSpec spec)
104
builtInFunctionEmulator(type)
106
longNameMap = LongNameMap::GetInstance();
109
TCompiler::~TCompiler()
112
longNameMap->Release();
115
bool TCompiler::Init(const ShBuiltInResources& resources)
117
TScopedPoolAllocator scopedAlloc(&allocator, false);
119
// Generate built-in symbol table.
120
if (!InitBuiltInSymbolTable(resources))
122
InitExtensionBehavior(resources, extensionBehavior);
127
bool TCompiler::compile(const char* const shaderStrings[],
128
const int numStrings,
131
TScopedPoolAllocator scopedAlloc(&allocator, true);
137
// If compiling for WebGL, validate loop and indexing as well.
138
if (isWebGLBasedSpec(shaderSpec))
139
compileOptions |= SH_VALIDATE_LOOP_INDEXING;
141
// First string is path of source file if flag is set. The actual source follows.
142
const char* sourcePath = NULL;
144
if (compileOptions & SH_SOURCE_PATH)
146
sourcePath = shaderStrings[0];
150
TIntermediate intermediate(infoSink);
151
TParseContext parseContext(symbolTable, extensionBehavior, intermediate,
152
shaderType, shaderSpec, compileOptions, true,
153
sourcePath, infoSink);
154
GlobalParseContext = &parseContext;
156
// We preserve symbols at the built-in level from compile-to-compile.
157
// Start pushing the user-defined symbols at global level.
159
if (!symbolTable.atGlobalLevel())
160
infoSink.info.message(EPrefixInternalError, "Wrong symbol table level");
164
(PaParseStrings(numStrings - firstSource, &shaderStrings[firstSource], NULL, &parseContext) == 0) &&
165
(parseContext.treeRoot != NULL);
167
TIntermNode* root = parseContext.treeRoot;
168
success = intermediate.postProcess(root);
171
success = detectRecursion(root);
173
if (success && (compileOptions & SH_VALIDATE_LOOP_INDEXING))
174
success = validateLimitations(root);
176
if (success && (compileOptions & SH_TIMING_RESTRICTIONS))
177
success = enforceTimingRestrictions(root, (compileOptions & SH_DEPENDENCY_GRAPH) != 0);
179
if (success && shaderSpec == SH_CSS_SHADERS_SPEC)
180
rewriteCSSShader(root);
182
// Unroll for-loop markup needs to happen after validateLimitations pass.
183
if (success && (compileOptions & SH_UNROLL_FOR_LOOP_WITH_INTEGER_INDEX))
184
ForLoopUnroll::MarkForLoopsWithIntegerIndicesForUnrolling(root);
186
// Built-in function emulation needs to happen after validateLimitations pass.
187
if (success && (compileOptions & SH_EMULATE_BUILT_IN_FUNCTIONS))
188
builtInFunctionEmulator.MarkBuiltInFunctionsForEmulation(root);
190
// Clamping uniform array bounds needs to happen after validateLimitations pass.
191
if (success && (compileOptions & SH_CLAMP_INDIRECT_ARRAY_BOUNDS))
192
arrayBoundsClamper.MarkIndirectArrayBoundsForClamping(root);
194
// Call mapLongVariableNames() before collectAttribsUniforms() so in
195
// collectAttribsUniforms() we already have the mapped symbol names and
196
// we could composite mapped and original variable names.
197
if (success && (compileOptions & SH_MAP_LONG_VARIABLE_NAMES))
198
mapLongVariableNames(root);
200
if (success && (compileOptions & SH_ATTRIBUTES_UNIFORMS))
201
collectAttribsUniforms(root);
203
if (success && (compileOptions & SH_INTERMEDIATE_TREE))
204
intermediate.outputTree(root);
206
if (success && (compileOptions & SH_OBJECT_CODE))
211
intermediate.remove(parseContext.treeRoot);
212
// Ensure symbol table is returned to the built-in level,
213
// throwing away all but the built-ins.
214
while (!symbolTable.atBuiltInLevel())
220
bool TCompiler::InitBuiltInSymbolTable(const ShBuiltInResources& resources)
224
builtIns.initialize(shaderType, shaderSpec, resources);
225
return InitializeSymbolTable(builtIns.getBuiltInStrings(),
226
shaderType, shaderSpec, resources, infoSink, symbolTable);
229
void TCompiler::clearResults()
231
infoSink.info.erase();
232
infoSink.obj.erase();
233
infoSink.debug.erase();
238
builtInFunctionEmulator.Cleanup();
239
arrayBoundsClamper.Cleanup();
242
bool TCompiler::detectRecursion(TIntermNode* root)
244
DetectRecursion detect;
245
root->traverse(&detect);
246
switch (detect.detectRecursion()) {
247
case DetectRecursion::kErrorNone:
249
case DetectRecursion::kErrorMissingMain:
250
infoSink.info.message(EPrefixError, "Missing main()");
252
case DetectRecursion::kErrorRecursion:
253
infoSink.info.message(EPrefixError, "Function recursion detected");
261
void TCompiler::rewriteCSSShader(TIntermNode* root)
263
RenameFunction renamer("main(", "css_main(");
264
root->traverse(&renamer);
267
bool TCompiler::validateLimitations(TIntermNode* root) {
268
ValidateLimitations validate(shaderType, infoSink.info);
269
root->traverse(&validate);
270
return validate.numErrors() == 0;
273
bool TCompiler::enforceTimingRestrictions(TIntermNode* root, bool outputGraph)
275
if (shaderSpec != SH_WEBGL_SPEC) {
276
infoSink.info << "Timing restrictions must be enforced under the WebGL spec.";
280
if (shaderType == SH_FRAGMENT_SHADER) {
281
TDependencyGraph graph(root);
283
// Output any errors first.
284
bool success = enforceFragmentShaderTimingRestrictions(graph);
286
// Then, output the dependency graph.
288
TDependencyGraphOutput output(infoSink.info);
289
output.outputAllSpanningTrees(graph);
295
return enforceVertexShaderTimingRestrictions(root);
299
bool TCompiler::enforceFragmentShaderTimingRestrictions(const TDependencyGraph& graph)
301
RestrictFragmentShaderTiming restrictor(infoSink.info);
302
restrictor.enforceRestrictions(graph);
303
return restrictor.numErrors() == 0;
306
bool TCompiler::enforceVertexShaderTimingRestrictions(TIntermNode* root)
308
RestrictVertexShaderTiming restrictor(infoSink.info);
309
restrictor.enforceRestrictions(root);
310
return restrictor.numErrors() == 0;
313
void TCompiler::collectAttribsUniforms(TIntermNode* root)
315
CollectAttribsUniforms collect(attribs, uniforms);
316
root->traverse(&collect);
319
void TCompiler::mapLongVariableNames(TIntermNode* root)
322
MapLongVariableNames map(longNameMap);
323
root->traverse(&map);
326
int TCompiler::getMappedNameMaxLength() const
328
return MAX_SHORTENED_IDENTIFIER_SIZE + 1;
331
const TExtensionBehavior& TCompiler::getExtensionBehavior() const
333
return extensionBehavior;
336
const BuiltInFunctionEmulator& TCompiler::getBuiltInFunctionEmulator() const
338
return builtInFunctionEmulator;
341
const ArrayBoundsClamper& TCompiler::getArrayBoundsClamper() const
343
return arrayBoundsClamper;