blob: cab8056bcee5551b9e33bc91010dc0b6ce720d0b [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
zmo@google.com32e97312011-08-24 01:03:11 +00007#include "compiler/BuiltInFunctionEmulator.h"
zmo@google.comb1762df2011-07-30 02:04:23 +00008#include "compiler/DetectRecursion.h"
zmo@google.com0c6bb7a2011-08-17 19:39:58 +00009#include "compiler/ForLoopUnroll.h"
alokp@chromium.org07620a52010-09-23 17:53:56 +000010#include "compiler/Initialize.h"
alokp@chromium.org8b851c62012-06-15 16:25:11 +000011#include "compiler/InitializeParseContext.h"
zmo@google.comb9f64aa2012-01-20 00:35:15 +000012#include "compiler/MapLongVariableNames.h"
alokp@chromium.org07620a52010-09-23 17:53:56 +000013#include "compiler/ParseHelper.h"
maxvujovic@gmail.com430f5e02012-06-08 17:47:59 +000014#include "compiler/RenameFunction.h"
alokp@chromium.org07620a52010-09-23 17:53:56 +000015#include "compiler/ShHandle.h"
alokp@chromium.orgb59a7782010-11-24 18:38:33 +000016#include "compiler/ValidateLimitations.h"
maxvujovic@gmail.com66ebd012012-05-30 22:18:11 +000017#include "compiler/depgraph/DependencyGraph.h"
18#include "compiler/depgraph/DependencyGraphOutput.h"
19#include "compiler/timing/RestrictFragmentShaderTiming.h"
20#include "compiler/timing/RestrictVertexShaderTiming.h"
alokp@chromium.org07620a52010-09-23 17:53:56 +000021
maxvujovic@gmail.com430f5e02012-06-08 17:47:59 +000022bool isWebGLBasedSpec(ShShaderSpec spec)
23{
24 return spec == SH_WEBGL_SPEC || spec == SH_CSS_SHADERS_SPEC;
25}
26
alokp@chromium.orgbafcbaa2010-11-23 19:07:43 +000027namespace {
28bool InitializeSymbolTable(
29 const TBuiltInStrings& builtInStrings,
30 ShShaderType type, ShShaderSpec spec, const ShBuiltInResources& resources,
31 TInfoSink& infoSink, TSymbolTable& symbolTable)
alokp@chromium.org07620a52010-09-23 17:53:56 +000032{
33 TIntermediate intermediate(infoSink);
34 TExtensionBehavior extBehavior;
zmo@google.com09c323a2011-08-12 18:22:25 +000035 InitExtensionBehavior(resources, extBehavior);
zmo@google.comdc4b4f82011-06-17 00:42:53 +000036 // The builtins deliberately don't specify precisions for the function
37 // arguments and return types. For that reason we don't try to check them.
38 TParseContext parseContext(symbolTable, extBehavior, intermediate, type, spec, 0, false, NULL, infoSink);
alokp@chromium.org07620a52010-09-23 17:53:56 +000039
40 GlobalParseContext = &parseContext;
41
alokp@chromium.org07620a52010-09-23 17:53:56 +000042 assert(symbolTable.isEmpty());
43 //
44 // Parse the built-ins. This should only happen once per
45 // language symbol table.
46 //
47 // Push the symbol table to give it an initial scope. This
48 // push should not have a corresponding pop, so that built-ins
49 // are preserved, and the test for an empty table fails.
50 //
51 symbolTable.push();
alokp@chromium.org07620a52010-09-23 17:53:56 +000052
53 for (TBuiltInStrings::const_iterator i = builtInStrings.begin(); i != builtInStrings.end(); ++i)
54 {
alokp@chromium.org570bfc72010-09-24 17:19:25 +000055 const char* builtInShaders = i->c_str();
56 int builtInLengths = static_cast<int>(i->size());
57 if (builtInLengths <= 0)
58 continue;
alokp@chromium.org07620a52010-09-23 17:53:56 +000059
alokp@chromium.org044a5cf2010-11-12 15:42:16 +000060 if (PaParseStrings(1, &builtInShaders, &builtInLengths, &parseContext) != 0)
alokp@chromium.org07620a52010-09-23 17:53:56 +000061 {
62 infoSink.info.message(EPrefixInternalError, "Unable to parse built-ins");
63 return false;
64 }
65 }
66
alokp@chromium.org4888ceb2010-10-01 21:13:12 +000067 IdentifyBuiltIns(type, spec, resources, symbolTable);
alokp@chromium.org07620a52010-09-23 17:53:56 +000068
alokp@chromium.org07620a52010-09-23 17:53:56 +000069 return true;
70}
71
alokp@chromium.orgbafcbaa2010-11-23 19:07:43 +000072class TScopedPoolAllocator {
73public:
74 TScopedPoolAllocator(TPoolAllocator* allocator, bool pushPop)
75 : mAllocator(allocator), mPushPopAllocator(pushPop) {
76 if (mPushPopAllocator) mAllocator->push();
77 SetGlobalPoolAllocator(mAllocator);
78 }
79 ~TScopedPoolAllocator() {
80 SetGlobalPoolAllocator(NULL);
81 if (mPushPopAllocator) mAllocator->pop();
82 }
83
84private:
85 TPoolAllocator* mAllocator;
86 bool mPushPopAllocator;
87};
88} // namespace
89
90TShHandleBase::TShHandleBase() {
91 allocator.push();
92 SetGlobalPoolAllocator(&allocator);
93}
94
95TShHandleBase::~TShHandleBase() {
96 SetGlobalPoolAllocator(NULL);
97 allocator.popAll();
98}
99
alokp@chromium.org4888ceb2010-10-01 21:13:12 +0000100TCompiler::TCompiler(ShShaderType type, ShShaderSpec spec)
101 : shaderType(type),
zmo@google.comf420c422011-09-12 18:27:59 +0000102 shaderSpec(spec),
zmo@google.com9996b8e2012-01-19 01:43:55 +0000103 builtInFunctionEmulator(type)
alokp@chromium.org4888ceb2010-10-01 21:13:12 +0000104{
zmo@google.comb9f64aa2012-01-20 00:35:15 +0000105 longNameMap = LongNameMap::GetInstance();
alokp@chromium.org4888ceb2010-10-01 21:13:12 +0000106}
107
108TCompiler::~TCompiler()
109{
zmo@google.comb9f64aa2012-01-20 00:35:15 +0000110 ASSERT(longNameMap);
111 longNameMap->Release();
alokp@chromium.org4888ceb2010-10-01 21:13:12 +0000112}
113
114bool TCompiler::Init(const ShBuiltInResources& resources)
alokp@chromium.org07620a52010-09-23 17:53:56 +0000115{
alokp@chromium.orgbafcbaa2010-11-23 19:07:43 +0000116 TScopedPoolAllocator scopedAlloc(&allocator, false);
117
alokp@chromium.org07620a52010-09-23 17:53:56 +0000118 // Generate built-in symbol table.
119 if (!InitBuiltInSymbolTable(resources))
120 return false;
alokp@chromium.org07620a52010-09-23 17:53:56 +0000121 InitExtensionBehavior(resources, extensionBehavior);
alokp@chromium.orgbafcbaa2010-11-23 19:07:43 +0000122
alokp@chromium.org07620a52010-09-23 17:53:56 +0000123 return true;
124}
125
126bool TCompiler::compile(const char* const shaderStrings[],
127 const int numStrings,
128 int compileOptions)
129{
alokp@chromium.orgbafcbaa2010-11-23 19:07:43 +0000130 TScopedPoolAllocator scopedAlloc(&allocator, true);
alokp@chromium.org07620a52010-09-23 17:53:56 +0000131 clearResults();
132
133 if (numStrings == 0)
134 return true;
135
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000136 // If compiling for WebGL, validate loop and indexing as well.
maxvujovic@gmail.com430f5e02012-06-08 17:47:59 +0000137 if (isWebGLBasedSpec(shaderSpec))
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000138 compileOptions |= SH_VALIDATE_LOOP_INDEXING;
alokp@chromium.org1f299542010-11-12 15:50:23 +0000139
apatrick@chromium.org0f4cefe2011-01-26 19:30:57 +0000140 // First string is path of source file if flag is set. The actual source follows.
141 const char* sourcePath = NULL;
142 int firstSource = 0;
143 if (compileOptions & SH_SOURCE_PATH)
144 {
145 sourcePath = shaderStrings[0];
146 ++firstSource;
147 }
148
alokp@chromium.org07620a52010-09-23 17:53:56 +0000149 TIntermediate intermediate(infoSink);
150 TParseContext parseContext(symbolTable, extensionBehavior, intermediate,
zmo@google.comdc4b4f82011-06-17 00:42:53 +0000151 shaderType, shaderSpec, compileOptions, true,
apatrick@chromium.org0f4cefe2011-01-26 19:30:57 +0000152 sourcePath, infoSink);
alokp@chromium.org07620a52010-09-23 17:53:56 +0000153 GlobalParseContext = &parseContext;
alokp@chromium.org07620a52010-09-23 17:53:56 +0000154
155 // We preserve symbols at the built-in level from compile-to-compile.
156 // Start pushing the user-defined symbols at global level.
157 symbolTable.push();
158 if (!symbolTable.atGlobalLevel())
159 infoSink.info.message(EPrefixInternalError, "Wrong symbol table level");
160
161 // Parse shader.
162 bool success =
apatrick@chromium.org0f4cefe2011-01-26 19:30:57 +0000163 (PaParseStrings(numStrings - firstSource, &shaderStrings[firstSource], NULL, &parseContext) == 0) &&
alokp@chromium.org07620a52010-09-23 17:53:56 +0000164 (parseContext.treeRoot != NULL);
165 if (success) {
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000166 TIntermNode* root = parseContext.treeRoot;
167 success = intermediate.postProcess(root);
168
zmo@google.comb1762df2011-07-30 02:04:23 +0000169 if (success)
170 success = detectRecursion(root);
171
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000172 if (success && (compileOptions & SH_VALIDATE_LOOP_INDEXING))
173 success = validateLimitations(root);
alokp@chromium.org07620a52010-09-23 17:53:56 +0000174
maxvujovic@gmail.com66ebd012012-05-30 22:18:11 +0000175 if (success && (compileOptions & SH_TIMING_RESTRICTIONS))
maxvujovic@gmail.com77222c92012-06-04 21:06:05 +0000176 success = enforceTimingRestrictions(root, (compileOptions & SH_DEPENDENCY_GRAPH) != 0);
maxvujovic@gmail.com66ebd012012-05-30 22:18:11 +0000177
maxvujovic@gmail.com430f5e02012-06-08 17:47:59 +0000178 if (success && shaderSpec == SH_CSS_SHADERS_SPEC)
179 rewriteCSSShader(root);
180
zmo@google.com0c6bb7a2011-08-17 19:39:58 +0000181 // Unroll for-loop markup needs to happen after validateLimitations pass.
182 if (success && (compileOptions & SH_UNROLL_FOR_LOOP_WITH_INTEGER_INDEX))
183 ForLoopUnroll::MarkForLoopsWithIntegerIndicesForUnrolling(root);
184
zmo@google.com32e97312011-08-24 01:03:11 +0000185 // Built-in function emulation needs to happen after validateLimitations pass.
186 if (success && (compileOptions & SH_EMULATE_BUILT_IN_FUNCTIONS))
187 builtInFunctionEmulator.MarkBuiltInFunctionsForEmulation(root);
188
zmo@google.comfd747b82011-04-23 01:30:07 +0000189 // Call mapLongVariableNames() before collectAttribsUniforms() so in
190 // collectAttribsUniforms() we already have the mapped symbol names and
191 // we could composite mapped and original variable names.
zmo@google.comb1762df2011-07-30 02:04:23 +0000192 if (success && (compileOptions & SH_MAP_LONG_VARIABLE_NAMES))
zmo@google.comfd747b82011-04-23 01:30:07 +0000193 mapLongVariableNames(root);
194
195 if (success && (compileOptions & SH_ATTRIBUTES_UNIFORMS))
196 collectAttribsUniforms(root);
197
alokp@chromium.org4888ceb2010-10-01 21:13:12 +0000198 if (success && (compileOptions & SH_INTERMEDIATE_TREE))
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000199 intermediate.outputTree(root);
alokp@chromium.org07620a52010-09-23 17:53:56 +0000200
alokp@chromium.org4888ceb2010-10-01 21:13:12 +0000201 if (success && (compileOptions & SH_OBJECT_CODE))
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000202 translate(root);
alokp@chromium.org07620a52010-09-23 17:53:56 +0000203 }
204
205 // Cleanup memory.
206 intermediate.remove(parseContext.treeRoot);
207 // Ensure symbol table is returned to the built-in level,
208 // throwing away all but the built-ins.
209 while (!symbolTable.atBuiltInLevel())
210 symbolTable.pop();
alokp@chromium.org07620a52010-09-23 17:53:56 +0000211
212 return success;
213}
214
alokp@chromium.org4888ceb2010-10-01 21:13:12 +0000215bool TCompiler::InitBuiltInSymbolTable(const ShBuiltInResources& resources)
alokp@chromium.org07620a52010-09-23 17:53:56 +0000216{
217 TBuiltIns builtIns;
218
alokp@chromium.org4888ceb2010-10-01 21:13:12 +0000219 builtIns.initialize(shaderType, shaderSpec, resources);
220 return InitializeSymbolTable(builtIns.getBuiltInStrings(),
221 shaderType, shaderSpec, resources, infoSink, symbolTable);
alokp@chromium.org07620a52010-09-23 17:53:56 +0000222}
223
224void TCompiler::clearResults()
225{
226 infoSink.info.erase();
227 infoSink.obj.erase();
228 infoSink.debug.erase();
229
230 attribs.clear();
231 uniforms.clear();
zmo@google.coma3b4ab42011-09-16 00:53:26 +0000232
233 builtInFunctionEmulator.Cleanup();
alokp@chromium.org07620a52010-09-23 17:53:56 +0000234}
235
zmo@google.comb1762df2011-07-30 02:04:23 +0000236bool TCompiler::detectRecursion(TIntermNode* root)
237{
238 DetectRecursion detect;
239 root->traverse(&detect);
240 switch (detect.detectRecursion()) {
241 case DetectRecursion::kErrorNone:
242 return true;
243 case DetectRecursion::kErrorMissingMain:
244 infoSink.info.message(EPrefixError, "Missing main()");
245 return false;
246 case DetectRecursion::kErrorRecursion:
247 infoSink.info.message(EPrefixError, "Function recursion detected");
248 return false;
249 default:
250 UNREACHABLE();
251 return false;
252 }
253}
254
maxvujovic@gmail.com430f5e02012-06-08 17:47:59 +0000255void TCompiler::rewriteCSSShader(TIntermNode* root)
256{
257 RenameFunction renamer("main(", "css_main(");
258 root->traverse(&renamer);
259}
260
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000261bool TCompiler::validateLimitations(TIntermNode* root) {
262 ValidateLimitations validate(shaderType, infoSink.info);
263 root->traverse(&validate);
264 return validate.numErrors() == 0;
265}
266
maxvujovic@gmail.com77222c92012-06-04 21:06:05 +0000267bool TCompiler::enforceTimingRestrictions(TIntermNode* root, bool outputGraph)
maxvujovic@gmail.com66ebd012012-05-30 22:18:11 +0000268{
269 if (shaderSpec != SH_WEBGL_SPEC) {
270 infoSink.info << "Timing restrictions must be enforced under the WebGL spec.";
271 return false;
272 }
273
274 if (shaderType == SH_FRAGMENT_SHADER) {
275 TDependencyGraph graph(root);
276
277 // Output any errors first.
maxvujovic@gmail.com77222c92012-06-04 21:06:05 +0000278 bool success = enforceFragmentShaderTimingRestrictions(graph);
maxvujovic@gmail.com66ebd012012-05-30 22:18:11 +0000279
280 // Then, output the dependency graph.
281 if (outputGraph) {
282 TDependencyGraphOutput output(infoSink.info);
283 output.outputAllSpanningTrees(graph);
284 }
285
286 return success;
287 }
288 else {
maxvujovic@gmail.com77222c92012-06-04 21:06:05 +0000289 return enforceVertexShaderTimingRestrictions(root);
maxvujovic@gmail.com66ebd012012-05-30 22:18:11 +0000290 }
291}
292
maxvujovic@gmail.com77222c92012-06-04 21:06:05 +0000293bool TCompiler::enforceFragmentShaderTimingRestrictions(const TDependencyGraph& graph)
maxvujovic@gmail.com66ebd012012-05-30 22:18:11 +0000294{
maxvujovic@gmail.com77222c92012-06-04 21:06:05 +0000295 RestrictFragmentShaderTiming restrictor(infoSink.info);
maxvujovic@gmail.com66ebd012012-05-30 22:18:11 +0000296 restrictor.enforceRestrictions(graph);
297 return restrictor.numErrors() == 0;
298}
299
maxvujovic@gmail.com77222c92012-06-04 21:06:05 +0000300bool TCompiler::enforceVertexShaderTimingRestrictions(TIntermNode* root)
maxvujovic@gmail.com66ebd012012-05-30 22:18:11 +0000301{
maxvujovic@gmail.com77222c92012-06-04 21:06:05 +0000302 RestrictVertexShaderTiming restrictor(infoSink.info);
maxvujovic@gmail.com66ebd012012-05-30 22:18:11 +0000303 restrictor.enforceRestrictions(root);
304 return restrictor.numErrors() == 0;
305}
306
alokp@chromium.org07620a52010-09-23 17:53:56 +0000307void TCompiler::collectAttribsUniforms(TIntermNode* root)
308{
309 CollectAttribsUniforms collect(attribs, uniforms);
310 root->traverse(&collect);
311}
zmo@google.comfd747b82011-04-23 01:30:07 +0000312
313void TCompiler::mapLongVariableNames(TIntermNode* root)
314{
zmo@google.comb9f64aa2012-01-20 00:35:15 +0000315 ASSERT(longNameMap);
316 MapLongVariableNames map(longNameMap);
zmo@google.com9996b8e2012-01-19 01:43:55 +0000317 root->traverse(&map);
zmo@google.comfd747b82011-04-23 01:30:07 +0000318}
319
320int TCompiler::getMappedNameMaxLength() const
321{
kbr@chromium.org22152112011-10-26 01:18:28 +0000322 return MAX_SHORTENED_IDENTIFIER_SIZE + 1;
zmo@google.comfd747b82011-04-23 01:30:07 +0000323}
zmo@google.com5601ea02011-06-10 18:23:25 +0000324
325const TExtensionBehavior& TCompiler::getExtensionBehavior() const
326{
327 return extensionBehavior;
328}
zmo@google.com32e97312011-08-24 01:03:11 +0000329
330const BuiltInFunctionEmulator& TCompiler::getBuiltInFunctionEmulator() const
331{
332 return builtInFunctionEmulator;
333}