blob: dce8c687227a41ae22f63c7a0b46961b6bb4de2f [file] [log] [blame]
alokp@chromium.org07620a52010-09-23 17:53:56 +00001//
zmo@google.comb9f64aa2012-01-20 00:35:15 +00002// Copyright (c) 2002-2012 The ANGLE Project Authors. All rights reserved.
alokp@chromium.org07620a52010-09-23 17:53:56 +00003// Use of this source code is governed by a BSD-style license that can be
4// found in the LICENSE file.
5//
6
daniel@transgaming.com4167cc92013-01-11 04:11:53 +00007#include "compiler/ArrayBoundsClamper.h"
zmo@google.com32e97312011-08-24 01:03:11 +00008#include "compiler/BuiltInFunctionEmulator.h"
zmo@google.comb1762df2011-07-30 02:04:23 +00009#include "compiler/DetectRecursion.h"
zmo@google.com0c6bb7a2011-08-17 19:39:58 +000010#include "compiler/ForLoopUnroll.h"
alokp@chromium.org07620a52010-09-23 17:53:56 +000011#include "compiler/Initialize.h"
alokp@chromium.org8b851c62012-06-15 16:25:11 +000012#include "compiler/InitializeParseContext.h"
zmo@google.comb9f64aa2012-01-20 00:35:15 +000013#include "compiler/MapLongVariableNames.h"
alokp@chromium.org07620a52010-09-23 17:53:56 +000014#include "compiler/ParseHelper.h"
maxvujovic@gmail.com430f5e02012-06-08 17:47:59 +000015#include "compiler/RenameFunction.h"
alokp@chromium.org07620a52010-09-23 17:53:56 +000016#include "compiler/ShHandle.h"
alokp@chromium.orgb59a7782010-11-24 18:38:33 +000017#include "compiler/ValidateLimitations.h"
gman@chromium.org8d804792012-10-17 21:33:48 +000018#include "compiler/VariablePacker.h"
maxvujovic@gmail.com66ebd012012-05-30 22:18:11 +000019#include "compiler/depgraph/DependencyGraph.h"
20#include "compiler/depgraph/DependencyGraphOutput.h"
21#include "compiler/timing/RestrictFragmentShaderTiming.h"
22#include "compiler/timing/RestrictVertexShaderTiming.h"
alokp@chromium.org07620a52010-09-23 17:53:56 +000023
maxvujovic@gmail.com430f5e02012-06-08 17:47:59 +000024bool isWebGLBasedSpec(ShShaderSpec spec)
25{
26 return spec == SH_WEBGL_SPEC || spec == SH_CSS_SHADERS_SPEC;
27}
28
alokp@chromium.orgbafcbaa2010-11-23 19:07:43 +000029namespace {
30bool InitializeSymbolTable(
31 const TBuiltInStrings& builtInStrings,
32 ShShaderType type, ShShaderSpec spec, const ShBuiltInResources& resources,
33 TInfoSink& infoSink, TSymbolTable& symbolTable)
alokp@chromium.org07620a52010-09-23 17:53:56 +000034{
35 TIntermediate intermediate(infoSink);
36 TExtensionBehavior extBehavior;
zmo@google.com09c323a2011-08-12 18:22:25 +000037 InitExtensionBehavior(resources, extBehavior);
zmo@google.comdc4b4f82011-06-17 00:42:53 +000038 // The builtins deliberately don't specify precisions for the function
39 // arguments and return types. For that reason we don't try to check them.
40 TParseContext parseContext(symbolTable, extBehavior, intermediate, type, spec, 0, false, NULL, infoSink);
alokp@chromium.org07620a52010-09-23 17:53:56 +000041
42 GlobalParseContext = &parseContext;
43
alokp@chromium.org07620a52010-09-23 17:53:56 +000044 assert(symbolTable.isEmpty());
45 //
46 // Parse the built-ins. This should only happen once per
47 // language symbol table.
48 //
49 // Push the symbol table to give it an initial scope. This
50 // push should not have a corresponding pop, so that built-ins
51 // are preserved, and the test for an empty table fails.
52 //
53 symbolTable.push();
alokp@chromium.org07620a52010-09-23 17:53:56 +000054
55 for (TBuiltInStrings::const_iterator i = builtInStrings.begin(); i != builtInStrings.end(); ++i)
56 {
alokp@chromium.org570bfc72010-09-24 17:19:25 +000057 const char* builtInShaders = i->c_str();
58 int builtInLengths = static_cast<int>(i->size());
59 if (builtInLengths <= 0)
60 continue;
alokp@chromium.org07620a52010-09-23 17:53:56 +000061
alokp@chromium.org044a5cf2010-11-12 15:42:16 +000062 if (PaParseStrings(1, &builtInShaders, &builtInLengths, &parseContext) != 0)
alokp@chromium.org07620a52010-09-23 17:53:56 +000063 {
64 infoSink.info.message(EPrefixInternalError, "Unable to parse built-ins");
65 return false;
66 }
67 }
68
alokp@chromium.org4888ceb2010-10-01 21:13:12 +000069 IdentifyBuiltIns(type, spec, resources, symbolTable);
alokp@chromium.org07620a52010-09-23 17:53:56 +000070
alokp@chromium.org07620a52010-09-23 17:53:56 +000071 return true;
72}
73
alokp@chromium.orgbafcbaa2010-11-23 19:07:43 +000074class TScopedPoolAllocator {
75public:
76 TScopedPoolAllocator(TPoolAllocator* allocator, bool pushPop)
77 : mAllocator(allocator), mPushPopAllocator(pushPop) {
78 if (mPushPopAllocator) mAllocator->push();
79 SetGlobalPoolAllocator(mAllocator);
80 }
81 ~TScopedPoolAllocator() {
82 SetGlobalPoolAllocator(NULL);
83 if (mPushPopAllocator) mAllocator->pop();
84 }
85
86private:
87 TPoolAllocator* mAllocator;
88 bool mPushPopAllocator;
89};
90} // namespace
91
92TShHandleBase::TShHandleBase() {
93 allocator.push();
94 SetGlobalPoolAllocator(&allocator);
95}
96
97TShHandleBase::~TShHandleBase() {
98 SetGlobalPoolAllocator(NULL);
99 allocator.popAll();
100}
101
alokp@chromium.org4888ceb2010-10-01 21:13:12 +0000102TCompiler::TCompiler(ShShaderType type, ShShaderSpec spec)
103 : shaderType(type),
zmo@google.comf420c422011-09-12 18:27:59 +0000104 shaderSpec(spec),
zmo@google.com9996b8e2012-01-19 01:43:55 +0000105 builtInFunctionEmulator(type)
alokp@chromium.org4888ceb2010-10-01 21:13:12 +0000106{
zmo@google.comb9f64aa2012-01-20 00:35:15 +0000107 longNameMap = LongNameMap::GetInstance();
alokp@chromium.org4888ceb2010-10-01 21:13:12 +0000108}
109
110TCompiler::~TCompiler()
111{
zmo@google.comb9f64aa2012-01-20 00:35:15 +0000112 ASSERT(longNameMap);
113 longNameMap->Release();
alokp@chromium.org4888ceb2010-10-01 21:13:12 +0000114}
115
116bool TCompiler::Init(const ShBuiltInResources& resources)
alokp@chromium.org07620a52010-09-23 17:53:56 +0000117{
gman@chromium.org8d804792012-10-17 21:33:48 +0000118 maxUniformVectors = (shaderType == SH_VERTEX_SHADER) ?
119 resources.MaxVertexUniformVectors :
120 resources.MaxFragmentUniformVectors;
alokp@chromium.orgbafcbaa2010-11-23 19:07:43 +0000121 TScopedPoolAllocator scopedAlloc(&allocator, false);
122
alokp@chromium.org07620a52010-09-23 17:53:56 +0000123 // Generate built-in symbol table.
124 if (!InitBuiltInSymbolTable(resources))
125 return false;
alokp@chromium.org07620a52010-09-23 17:53:56 +0000126 InitExtensionBehavior(resources, extensionBehavior);
alokp@chromium.orgbafcbaa2010-11-23 19:07:43 +0000127
daniel@transgaming.comc23f4612012-11-28 19:42:57 +0000128 hashFunction = resources.HashFunction;
129
alokp@chromium.org07620a52010-09-23 17:53:56 +0000130 return true;
131}
132
133bool TCompiler::compile(const char* const shaderStrings[],
134 const int numStrings,
135 int compileOptions)
136{
alokp@chromium.orgbafcbaa2010-11-23 19:07:43 +0000137 TScopedPoolAllocator scopedAlloc(&allocator, true);
alokp@chromium.org07620a52010-09-23 17:53:56 +0000138 clearResults();
139
140 if (numStrings == 0)
141 return true;
142
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000143 // If compiling for WebGL, validate loop and indexing as well.
maxvujovic@gmail.com430f5e02012-06-08 17:47:59 +0000144 if (isWebGLBasedSpec(shaderSpec))
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000145 compileOptions |= SH_VALIDATE_LOOP_INDEXING;
alokp@chromium.org1f299542010-11-12 15:50:23 +0000146
apatrick@chromium.org0f4cefe2011-01-26 19:30:57 +0000147 // First string is path of source file if flag is set. The actual source follows.
148 const char* sourcePath = NULL;
149 int firstSource = 0;
150 if (compileOptions & SH_SOURCE_PATH)
151 {
152 sourcePath = shaderStrings[0];
153 ++firstSource;
154 }
155
alokp@chromium.org07620a52010-09-23 17:53:56 +0000156 TIntermediate intermediate(infoSink);
157 TParseContext parseContext(symbolTable, extensionBehavior, intermediate,
zmo@google.comdc4b4f82011-06-17 00:42:53 +0000158 shaderType, shaderSpec, compileOptions, true,
apatrick@chromium.org0f4cefe2011-01-26 19:30:57 +0000159 sourcePath, infoSink);
alokp@chromium.org07620a52010-09-23 17:53:56 +0000160 GlobalParseContext = &parseContext;
alokp@chromium.org07620a52010-09-23 17:53:56 +0000161
162 // We preserve symbols at the built-in level from compile-to-compile.
163 // Start pushing the user-defined symbols at global level.
164 symbolTable.push();
165 if (!symbolTable.atGlobalLevel())
166 infoSink.info.message(EPrefixInternalError, "Wrong symbol table level");
167
168 // Parse shader.
169 bool success =
apatrick@chromium.org0f4cefe2011-01-26 19:30:57 +0000170 (PaParseStrings(numStrings - firstSource, &shaderStrings[firstSource], NULL, &parseContext) == 0) &&
alokp@chromium.org07620a52010-09-23 17:53:56 +0000171 (parseContext.treeRoot != NULL);
172 if (success) {
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000173 TIntermNode* root = parseContext.treeRoot;
174 success = intermediate.postProcess(root);
175
zmo@google.comb1762df2011-07-30 02:04:23 +0000176 if (success)
177 success = detectRecursion(root);
178
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000179 if (success && (compileOptions & SH_VALIDATE_LOOP_INDEXING))
180 success = validateLimitations(root);
alokp@chromium.org07620a52010-09-23 17:53:56 +0000181
maxvujovic@gmail.com66ebd012012-05-30 22:18:11 +0000182 if (success && (compileOptions & SH_TIMING_RESTRICTIONS))
maxvujovic@gmail.com77222c92012-06-04 21:06:05 +0000183 success = enforceTimingRestrictions(root, (compileOptions & SH_DEPENDENCY_GRAPH) != 0);
maxvujovic@gmail.com66ebd012012-05-30 22:18:11 +0000184
maxvujovic@gmail.com430f5e02012-06-08 17:47:59 +0000185 if (success && shaderSpec == SH_CSS_SHADERS_SPEC)
186 rewriteCSSShader(root);
187
zmo@google.com0c6bb7a2011-08-17 19:39:58 +0000188 // Unroll for-loop markup needs to happen after validateLimitations pass.
189 if (success && (compileOptions & SH_UNROLL_FOR_LOOP_WITH_INTEGER_INDEX))
190 ForLoopUnroll::MarkForLoopsWithIntegerIndicesForUnrolling(root);
191
zmo@google.com32e97312011-08-24 01:03:11 +0000192 // Built-in function emulation needs to happen after validateLimitations pass.
193 if (success && (compileOptions & SH_EMULATE_BUILT_IN_FUNCTIONS))
194 builtInFunctionEmulator.MarkBuiltInFunctionsForEmulation(root);
195
daniel@transgaming.com4167cc92013-01-11 04:11:53 +0000196 // Clamping uniform array bounds needs to happen after validateLimitations pass.
197 if (success && (compileOptions & SH_CLAMP_INDIRECT_ARRAY_BOUNDS))
198 arrayBoundsClamper.MarkIndirectArrayBoundsForClamping(root);
199
zmo@google.comfd747b82011-04-23 01:30:07 +0000200 // Call mapLongVariableNames() before collectAttribsUniforms() so in
201 // collectAttribsUniforms() we already have the mapped symbol names and
202 // we could composite mapped and original variable names.
daniel@transgaming.com0aa3b5a2012-11-28 19:43:24 +0000203 // Also, if we hash all the names, then no need to do this for long names.
204 if (success && (compileOptions & SH_MAP_LONG_VARIABLE_NAMES) && hashFunction == NULL)
zmo@google.comfd747b82011-04-23 01:30:07 +0000205 mapLongVariableNames(root);
206
gman@chromium.org8d804792012-10-17 21:33:48 +0000207 if (success && (compileOptions & SH_ATTRIBUTES_UNIFORMS)) {
zmo@google.comfd747b82011-04-23 01:30:07 +0000208 collectAttribsUniforms(root);
gman@chromium.org8d804792012-10-17 21:33:48 +0000209 if (compileOptions & SH_ENFORCE_PACKING_RESTRICTIONS) {
210 success = enforcePackingRestrictions();
211 if (!success) {
212 infoSink.info.message(EPrefixError, "too many uniforms");
213 }
214 }
215 }
zmo@google.comfd747b82011-04-23 01:30:07 +0000216
alokp@chromium.org4888ceb2010-10-01 21:13:12 +0000217 if (success && (compileOptions & SH_INTERMEDIATE_TREE))
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000218 intermediate.outputTree(root);
alokp@chromium.org07620a52010-09-23 17:53:56 +0000219
alokp@chromium.org4888ceb2010-10-01 21:13:12 +0000220 if (success && (compileOptions & SH_OBJECT_CODE))
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000221 translate(root);
alokp@chromium.org07620a52010-09-23 17:53:56 +0000222 }
223
224 // Cleanup memory.
225 intermediate.remove(parseContext.treeRoot);
226 // Ensure symbol table is returned to the built-in level,
227 // throwing away all but the built-ins.
228 while (!symbolTable.atBuiltInLevel())
229 symbolTable.pop();
alokp@chromium.org07620a52010-09-23 17:53:56 +0000230
231 return success;
232}
233
alokp@chromium.org4888ceb2010-10-01 21:13:12 +0000234bool TCompiler::InitBuiltInSymbolTable(const ShBuiltInResources& resources)
alokp@chromium.org07620a52010-09-23 17:53:56 +0000235{
236 TBuiltIns builtIns;
237
alokp@chromium.org4888ceb2010-10-01 21:13:12 +0000238 builtIns.initialize(shaderType, shaderSpec, resources);
239 return InitializeSymbolTable(builtIns.getBuiltInStrings(),
240 shaderType, shaderSpec, resources, infoSink, symbolTable);
alokp@chromium.org07620a52010-09-23 17:53:56 +0000241}
242
243void TCompiler::clearResults()
244{
daniel@transgaming.com4167cc92013-01-11 04:11:53 +0000245 arrayBoundsClamper.Cleanup();
alokp@chromium.org07620a52010-09-23 17:53:56 +0000246 infoSink.info.erase();
247 infoSink.obj.erase();
248 infoSink.debug.erase();
249
250 attribs.clear();
251 uniforms.clear();
zmo@google.coma3b4ab42011-09-16 00:53:26 +0000252
253 builtInFunctionEmulator.Cleanup();
daniel@transgaming.com0aa3b5a2012-11-28 19:43:24 +0000254
255 nameMap.clear();
alokp@chromium.org07620a52010-09-23 17:53:56 +0000256}
257
zmo@google.comb1762df2011-07-30 02:04:23 +0000258bool TCompiler::detectRecursion(TIntermNode* root)
259{
260 DetectRecursion detect;
261 root->traverse(&detect);
262 switch (detect.detectRecursion()) {
263 case DetectRecursion::kErrorNone:
264 return true;
265 case DetectRecursion::kErrorMissingMain:
266 infoSink.info.message(EPrefixError, "Missing main()");
267 return false;
268 case DetectRecursion::kErrorRecursion:
269 infoSink.info.message(EPrefixError, "Function recursion detected");
270 return false;
271 default:
272 UNREACHABLE();
273 return false;
274 }
275}
276
maxvujovic@gmail.com430f5e02012-06-08 17:47:59 +0000277void TCompiler::rewriteCSSShader(TIntermNode* root)
278{
279 RenameFunction renamer("main(", "css_main(");
280 root->traverse(&renamer);
281}
282
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000283bool TCompiler::validateLimitations(TIntermNode* root) {
284 ValidateLimitations validate(shaderType, infoSink.info);
285 root->traverse(&validate);
286 return validate.numErrors() == 0;
287}
288
maxvujovic@gmail.com77222c92012-06-04 21:06:05 +0000289bool TCompiler::enforceTimingRestrictions(TIntermNode* root, bool outputGraph)
maxvujovic@gmail.com66ebd012012-05-30 22:18:11 +0000290{
291 if (shaderSpec != SH_WEBGL_SPEC) {
292 infoSink.info << "Timing restrictions must be enforced under the WebGL spec.";
293 return false;
294 }
295
296 if (shaderType == SH_FRAGMENT_SHADER) {
297 TDependencyGraph graph(root);
298
299 // Output any errors first.
maxvujovic@gmail.com77222c92012-06-04 21:06:05 +0000300 bool success = enforceFragmentShaderTimingRestrictions(graph);
maxvujovic@gmail.com66ebd012012-05-30 22:18:11 +0000301
302 // Then, output the dependency graph.
303 if (outputGraph) {
304 TDependencyGraphOutput output(infoSink.info);
305 output.outputAllSpanningTrees(graph);
306 }
307
308 return success;
309 }
310 else {
maxvujovic@gmail.com77222c92012-06-04 21:06:05 +0000311 return enforceVertexShaderTimingRestrictions(root);
maxvujovic@gmail.com66ebd012012-05-30 22:18:11 +0000312 }
313}
314
maxvujovic@gmail.com77222c92012-06-04 21:06:05 +0000315bool TCompiler::enforceFragmentShaderTimingRestrictions(const TDependencyGraph& graph)
maxvujovic@gmail.com66ebd012012-05-30 22:18:11 +0000316{
maxvujovic@gmail.com77222c92012-06-04 21:06:05 +0000317 RestrictFragmentShaderTiming restrictor(infoSink.info);
maxvujovic@gmail.com66ebd012012-05-30 22:18:11 +0000318 restrictor.enforceRestrictions(graph);
319 return restrictor.numErrors() == 0;
320}
321
maxvujovic@gmail.com77222c92012-06-04 21:06:05 +0000322bool TCompiler::enforceVertexShaderTimingRestrictions(TIntermNode* root)
maxvujovic@gmail.com66ebd012012-05-30 22:18:11 +0000323{
maxvujovic@gmail.com77222c92012-06-04 21:06:05 +0000324 RestrictVertexShaderTiming restrictor(infoSink.info);
maxvujovic@gmail.com66ebd012012-05-30 22:18:11 +0000325 restrictor.enforceRestrictions(root);
326 return restrictor.numErrors() == 0;
327}
328
alokp@chromium.org07620a52010-09-23 17:53:56 +0000329void TCompiler::collectAttribsUniforms(TIntermNode* root)
330{
daniel@transgaming.com0aa3b5a2012-11-28 19:43:24 +0000331 CollectAttribsUniforms collect(attribs, uniforms, hashFunction);
alokp@chromium.org07620a52010-09-23 17:53:56 +0000332 root->traverse(&collect);
333}
zmo@google.comfd747b82011-04-23 01:30:07 +0000334
gman@chromium.org8d804792012-10-17 21:33:48 +0000335bool TCompiler::enforcePackingRestrictions()
336{
337 VariablePacker packer;
338 return packer.CheckVariablesWithinPackingLimits(maxUniformVectors, uniforms);
339}
340
zmo@google.comfd747b82011-04-23 01:30:07 +0000341void TCompiler::mapLongVariableNames(TIntermNode* root)
342{
zmo@google.comb9f64aa2012-01-20 00:35:15 +0000343 ASSERT(longNameMap);
344 MapLongVariableNames map(longNameMap);
zmo@google.com9996b8e2012-01-19 01:43:55 +0000345 root->traverse(&map);
zmo@google.comfd747b82011-04-23 01:30:07 +0000346}
347
348int TCompiler::getMappedNameMaxLength() const
349{
kbr@chromium.org22152112011-10-26 01:18:28 +0000350 return MAX_SHORTENED_IDENTIFIER_SIZE + 1;
zmo@google.comfd747b82011-04-23 01:30:07 +0000351}
zmo@google.com5601ea02011-06-10 18:23:25 +0000352
353const TExtensionBehavior& TCompiler::getExtensionBehavior() const
354{
355 return extensionBehavior;
356}
zmo@google.com32e97312011-08-24 01:03:11 +0000357
358const BuiltInFunctionEmulator& TCompiler::getBuiltInFunctionEmulator() const
359{
360 return builtInFunctionEmulator;
361}
daniel@transgaming.com4167cc92013-01-11 04:11:53 +0000362
363const ArrayBoundsClamper& TCompiler::getArrayBoundsClamper() const
364{
365 return arrayBoundsClamper;
366}
367