blob: c0a69137b529dc17876b4a501f67f63a2bebc5a8 [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"
zmo@google.comb9f64aa2012-01-20 00:35:15 +000011#include "compiler/MapLongVariableNames.h"
alokp@chromium.org07620a52010-09-23 17:53:56 +000012#include "compiler/ParseHelper.h"
maxvujovic@gmail.com430f5e02012-06-08 17:47:59 +000013#include "compiler/RenameFunction.h"
alokp@chromium.org07620a52010-09-23 17:53:56 +000014#include "compiler/ShHandle.h"
alokp@chromium.orgb59a7782010-11-24 18:38:33 +000015#include "compiler/ValidateLimitations.h"
maxvujovic@gmail.com66ebd012012-05-30 22:18:11 +000016#include "compiler/depgraph/DependencyGraph.h"
17#include "compiler/depgraph/DependencyGraphOutput.h"
18#include "compiler/timing/RestrictFragmentShaderTiming.h"
19#include "compiler/timing/RestrictVertexShaderTiming.h"
alokp@chromium.org07620a52010-09-23 17:53:56 +000020
maxvujovic@gmail.com430f5e02012-06-08 17:47:59 +000021bool isWebGLBasedSpec(ShShaderSpec spec)
22{
23 return spec == SH_WEBGL_SPEC || spec == SH_CSS_SHADERS_SPEC;
24}
25
alokp@chromium.orgbafcbaa2010-11-23 19:07:43 +000026namespace {
27bool InitializeSymbolTable(
28 const TBuiltInStrings& builtInStrings,
29 ShShaderType type, ShShaderSpec spec, const ShBuiltInResources& resources,
30 TInfoSink& infoSink, TSymbolTable& symbolTable)
alokp@chromium.org07620a52010-09-23 17:53:56 +000031{
32 TIntermediate intermediate(infoSink);
33 TExtensionBehavior extBehavior;
zmo@google.com09c323a2011-08-12 18:22:25 +000034 InitExtensionBehavior(resources, extBehavior);
zmo@google.comdc4b4f82011-06-17 00:42:53 +000035 // The builtins deliberately don't specify precisions for the function
36 // arguments and return types. For that reason we don't try to check them.
37 TParseContext parseContext(symbolTable, extBehavior, intermediate, type, spec, 0, false, NULL, infoSink);
alokp@chromium.org07620a52010-09-23 17:53:56 +000038
39 GlobalParseContext = &parseContext;
40
alokp@chromium.org07620a52010-09-23 17:53:56 +000041 assert(symbolTable.isEmpty());
42 //
43 // Parse the built-ins. This should only happen once per
44 // language symbol table.
45 //
46 // Push the symbol table to give it an initial scope. This
47 // push should not have a corresponding pop, so that built-ins
48 // are preserved, and the test for an empty table fails.
49 //
50 symbolTable.push();
alokp@chromium.org07620a52010-09-23 17:53:56 +000051
52 for (TBuiltInStrings::const_iterator i = builtInStrings.begin(); i != builtInStrings.end(); ++i)
53 {
alokp@chromium.org570bfc72010-09-24 17:19:25 +000054 const char* builtInShaders = i->c_str();
55 int builtInLengths = static_cast<int>(i->size());
56 if (builtInLengths <= 0)
57 continue;
alokp@chromium.org07620a52010-09-23 17:53:56 +000058
alokp@chromium.org044a5cf2010-11-12 15:42:16 +000059 if (PaParseStrings(1, &builtInShaders, &builtInLengths, &parseContext) != 0)
alokp@chromium.org07620a52010-09-23 17:53:56 +000060 {
61 infoSink.info.message(EPrefixInternalError, "Unable to parse built-ins");
62 return false;
63 }
64 }
65
alokp@chromium.org4888ceb2010-10-01 21:13:12 +000066 IdentifyBuiltIns(type, spec, resources, symbolTable);
alokp@chromium.org07620a52010-09-23 17:53:56 +000067
alokp@chromium.org07620a52010-09-23 17:53:56 +000068 return true;
69}
70
alokp@chromium.orgbafcbaa2010-11-23 19:07:43 +000071class TScopedPoolAllocator {
72public:
73 TScopedPoolAllocator(TPoolAllocator* allocator, bool pushPop)
74 : mAllocator(allocator), mPushPopAllocator(pushPop) {
75 if (mPushPopAllocator) mAllocator->push();
76 SetGlobalPoolAllocator(mAllocator);
77 }
78 ~TScopedPoolAllocator() {
79 SetGlobalPoolAllocator(NULL);
80 if (mPushPopAllocator) mAllocator->pop();
81 }
82
83private:
84 TPoolAllocator* mAllocator;
85 bool mPushPopAllocator;
86};
87} // namespace
88
89TShHandleBase::TShHandleBase() {
90 allocator.push();
91 SetGlobalPoolAllocator(&allocator);
92}
93
94TShHandleBase::~TShHandleBase() {
95 SetGlobalPoolAllocator(NULL);
96 allocator.popAll();
97}
98
alokp@chromium.org4888ceb2010-10-01 21:13:12 +000099TCompiler::TCompiler(ShShaderType type, ShShaderSpec spec)
100 : shaderType(type),
zmo@google.comf420c422011-09-12 18:27:59 +0000101 shaderSpec(spec),
zmo@google.com9996b8e2012-01-19 01:43:55 +0000102 builtInFunctionEmulator(type)
alokp@chromium.org4888ceb2010-10-01 21:13:12 +0000103{
zmo@google.comb9f64aa2012-01-20 00:35:15 +0000104 longNameMap = LongNameMap::GetInstance();
alokp@chromium.org4888ceb2010-10-01 21:13:12 +0000105}
106
107TCompiler::~TCompiler()
108{
zmo@google.comb9f64aa2012-01-20 00:35:15 +0000109 ASSERT(longNameMap);
110 longNameMap->Release();
alokp@chromium.org4888ceb2010-10-01 21:13:12 +0000111}
112
113bool TCompiler::Init(const ShBuiltInResources& resources)
alokp@chromium.org07620a52010-09-23 17:53:56 +0000114{
alokp@chromium.orgbafcbaa2010-11-23 19:07:43 +0000115 TScopedPoolAllocator scopedAlloc(&allocator, false);
116
alokp@chromium.org07620a52010-09-23 17:53:56 +0000117 // Generate built-in symbol table.
118 if (!InitBuiltInSymbolTable(resources))
119 return false;
alokp@chromium.org07620a52010-09-23 17:53:56 +0000120 InitExtensionBehavior(resources, extensionBehavior);
alokp@chromium.orgbafcbaa2010-11-23 19:07:43 +0000121
alokp@chromium.org07620a52010-09-23 17:53:56 +0000122 return true;
123}
124
125bool TCompiler::compile(const char* const shaderStrings[],
126 const int numStrings,
127 int compileOptions)
128{
alokp@chromium.orgbafcbaa2010-11-23 19:07:43 +0000129 TScopedPoolAllocator scopedAlloc(&allocator, true);
alokp@chromium.org07620a52010-09-23 17:53:56 +0000130 clearResults();
131
132 if (numStrings == 0)
133 return true;
134
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000135 // If compiling for WebGL, validate loop and indexing as well.
maxvujovic@gmail.com430f5e02012-06-08 17:47:59 +0000136 if (isWebGLBasedSpec(shaderSpec))
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000137 compileOptions |= SH_VALIDATE_LOOP_INDEXING;
alokp@chromium.org1f299542010-11-12 15:50:23 +0000138
apatrick@chromium.org0f4cefe2011-01-26 19:30:57 +0000139 // First string is path of source file if flag is set. The actual source follows.
140 const char* sourcePath = NULL;
141 int firstSource = 0;
142 if (compileOptions & SH_SOURCE_PATH)
143 {
144 sourcePath = shaderStrings[0];
145 ++firstSource;
146 }
147
alokp@chromium.org07620a52010-09-23 17:53:56 +0000148 TIntermediate intermediate(infoSink);
149 TParseContext parseContext(symbolTable, extensionBehavior, intermediate,
zmo@google.comdc4b4f82011-06-17 00:42:53 +0000150 shaderType, shaderSpec, compileOptions, true,
apatrick@chromium.org0f4cefe2011-01-26 19:30:57 +0000151 sourcePath, infoSink);
alokp@chromium.org07620a52010-09-23 17:53:56 +0000152 GlobalParseContext = &parseContext;
alokp@chromium.org07620a52010-09-23 17:53:56 +0000153
154 // We preserve symbols at the built-in level from compile-to-compile.
155 // Start pushing the user-defined symbols at global level.
156 symbolTable.push();
157 if (!symbolTable.atGlobalLevel())
158 infoSink.info.message(EPrefixInternalError, "Wrong symbol table level");
159
160 // Parse shader.
161 bool success =
apatrick@chromium.org0f4cefe2011-01-26 19:30:57 +0000162 (PaParseStrings(numStrings - firstSource, &shaderStrings[firstSource], NULL, &parseContext) == 0) &&
alokp@chromium.org07620a52010-09-23 17:53:56 +0000163 (parseContext.treeRoot != NULL);
164 if (success) {
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000165 TIntermNode* root = parseContext.treeRoot;
166 success = intermediate.postProcess(root);
167
zmo@google.comb1762df2011-07-30 02:04:23 +0000168 if (success)
169 success = detectRecursion(root);
170
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000171 if (success && (compileOptions & SH_VALIDATE_LOOP_INDEXING))
172 success = validateLimitations(root);
alokp@chromium.org07620a52010-09-23 17:53:56 +0000173
maxvujovic@gmail.com66ebd012012-05-30 22:18:11 +0000174 if (success && (compileOptions & SH_TIMING_RESTRICTIONS))
maxvujovic@gmail.com77222c92012-06-04 21:06:05 +0000175 success = enforceTimingRestrictions(root, (compileOptions & SH_DEPENDENCY_GRAPH) != 0);
maxvujovic@gmail.com66ebd012012-05-30 22:18:11 +0000176
maxvujovic@gmail.com430f5e02012-06-08 17:47:59 +0000177 if (success && shaderSpec == SH_CSS_SHADERS_SPEC)
178 rewriteCSSShader(root);
179
zmo@google.com0c6bb7a2011-08-17 19:39:58 +0000180 // Unroll for-loop markup needs to happen after validateLimitations pass.
181 if (success && (compileOptions & SH_UNROLL_FOR_LOOP_WITH_INTEGER_INDEX))
182 ForLoopUnroll::MarkForLoopsWithIntegerIndicesForUnrolling(root);
183
zmo@google.com32e97312011-08-24 01:03:11 +0000184 // Built-in function emulation needs to happen after validateLimitations pass.
185 if (success && (compileOptions & SH_EMULATE_BUILT_IN_FUNCTIONS))
186 builtInFunctionEmulator.MarkBuiltInFunctionsForEmulation(root);
187
zmo@google.comfd747b82011-04-23 01:30:07 +0000188 // Call mapLongVariableNames() before collectAttribsUniforms() so in
189 // collectAttribsUniforms() we already have the mapped symbol names and
190 // we could composite mapped and original variable names.
zmo@google.comb1762df2011-07-30 02:04:23 +0000191 if (success && (compileOptions & SH_MAP_LONG_VARIABLE_NAMES))
zmo@google.comfd747b82011-04-23 01:30:07 +0000192 mapLongVariableNames(root);
193
194 if (success && (compileOptions & SH_ATTRIBUTES_UNIFORMS))
195 collectAttribsUniforms(root);
196
alokp@chromium.org4888ceb2010-10-01 21:13:12 +0000197 if (success && (compileOptions & SH_INTERMEDIATE_TREE))
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000198 intermediate.outputTree(root);
alokp@chromium.org07620a52010-09-23 17:53:56 +0000199
alokp@chromium.org4888ceb2010-10-01 21:13:12 +0000200 if (success && (compileOptions & SH_OBJECT_CODE))
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000201 translate(root);
alokp@chromium.org07620a52010-09-23 17:53:56 +0000202 }
203
204 // Cleanup memory.
205 intermediate.remove(parseContext.treeRoot);
206 // Ensure symbol table is returned to the built-in level,
207 // throwing away all but the built-ins.
208 while (!symbolTable.atBuiltInLevel())
209 symbolTable.pop();
alokp@chromium.org07620a52010-09-23 17:53:56 +0000210
211 return success;
212}
213
alokp@chromium.org4888ceb2010-10-01 21:13:12 +0000214bool TCompiler::InitBuiltInSymbolTable(const ShBuiltInResources& resources)
alokp@chromium.org07620a52010-09-23 17:53:56 +0000215{
216 TBuiltIns builtIns;
217
alokp@chromium.org4888ceb2010-10-01 21:13:12 +0000218 builtIns.initialize(shaderType, shaderSpec, resources);
219 return InitializeSymbolTable(builtIns.getBuiltInStrings(),
220 shaderType, shaderSpec, resources, infoSink, symbolTable);
alokp@chromium.org07620a52010-09-23 17:53:56 +0000221}
222
223void TCompiler::clearResults()
224{
225 infoSink.info.erase();
226 infoSink.obj.erase();
227 infoSink.debug.erase();
228
229 attribs.clear();
230 uniforms.clear();
zmo@google.coma3b4ab42011-09-16 00:53:26 +0000231
232 builtInFunctionEmulator.Cleanup();
alokp@chromium.org07620a52010-09-23 17:53:56 +0000233}
234
zmo@google.comb1762df2011-07-30 02:04:23 +0000235bool TCompiler::detectRecursion(TIntermNode* root)
236{
237 DetectRecursion detect;
238 root->traverse(&detect);
239 switch (detect.detectRecursion()) {
240 case DetectRecursion::kErrorNone:
241 return true;
242 case DetectRecursion::kErrorMissingMain:
243 infoSink.info.message(EPrefixError, "Missing main()");
244 return false;
245 case DetectRecursion::kErrorRecursion:
246 infoSink.info.message(EPrefixError, "Function recursion detected");
247 return false;
248 default:
249 UNREACHABLE();
250 return false;
251 }
252}
253
maxvujovic@gmail.com430f5e02012-06-08 17:47:59 +0000254void TCompiler::rewriteCSSShader(TIntermNode* root)
255{
256 RenameFunction renamer("main(", "css_main(");
257 root->traverse(&renamer);
258}
259
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000260bool TCompiler::validateLimitations(TIntermNode* root) {
261 ValidateLimitations validate(shaderType, infoSink.info);
262 root->traverse(&validate);
263 return validate.numErrors() == 0;
264}
265
maxvujovic@gmail.com77222c92012-06-04 21:06:05 +0000266bool TCompiler::enforceTimingRestrictions(TIntermNode* root, bool outputGraph)
maxvujovic@gmail.com66ebd012012-05-30 22:18:11 +0000267{
268 if (shaderSpec != SH_WEBGL_SPEC) {
269 infoSink.info << "Timing restrictions must be enforced under the WebGL spec.";
270 return false;
271 }
272
273 if (shaderType == SH_FRAGMENT_SHADER) {
274 TDependencyGraph graph(root);
275
276 // Output any errors first.
maxvujovic@gmail.com77222c92012-06-04 21:06:05 +0000277 bool success = enforceFragmentShaderTimingRestrictions(graph);
maxvujovic@gmail.com66ebd012012-05-30 22:18:11 +0000278
279 // Then, output the dependency graph.
280 if (outputGraph) {
281 TDependencyGraphOutput output(infoSink.info);
282 output.outputAllSpanningTrees(graph);
283 }
284
285 return success;
286 }
287 else {
maxvujovic@gmail.com77222c92012-06-04 21:06:05 +0000288 return enforceVertexShaderTimingRestrictions(root);
maxvujovic@gmail.com66ebd012012-05-30 22:18:11 +0000289 }
290}
291
maxvujovic@gmail.com77222c92012-06-04 21:06:05 +0000292bool TCompiler::enforceFragmentShaderTimingRestrictions(const TDependencyGraph& graph)
maxvujovic@gmail.com66ebd012012-05-30 22:18:11 +0000293{
maxvujovic@gmail.com77222c92012-06-04 21:06:05 +0000294 RestrictFragmentShaderTiming restrictor(infoSink.info);
maxvujovic@gmail.com66ebd012012-05-30 22:18:11 +0000295 restrictor.enforceRestrictions(graph);
296 return restrictor.numErrors() == 0;
297}
298
maxvujovic@gmail.com77222c92012-06-04 21:06:05 +0000299bool TCompiler::enforceVertexShaderTimingRestrictions(TIntermNode* root)
maxvujovic@gmail.com66ebd012012-05-30 22:18:11 +0000300{
maxvujovic@gmail.com77222c92012-06-04 21:06:05 +0000301 RestrictVertexShaderTiming restrictor(infoSink.info);
maxvujovic@gmail.com66ebd012012-05-30 22:18:11 +0000302 restrictor.enforceRestrictions(root);
303 return restrictor.numErrors() == 0;
304}
305
alokp@chromium.org07620a52010-09-23 17:53:56 +0000306void TCompiler::collectAttribsUniforms(TIntermNode* root)
307{
308 CollectAttribsUniforms collect(attribs, uniforms);
309 root->traverse(&collect);
310}
zmo@google.comfd747b82011-04-23 01:30:07 +0000311
312void TCompiler::mapLongVariableNames(TIntermNode* root)
313{
zmo@google.comb9f64aa2012-01-20 00:35:15 +0000314 ASSERT(longNameMap);
315 MapLongVariableNames map(longNameMap);
zmo@google.com9996b8e2012-01-19 01:43:55 +0000316 root->traverse(&map);
zmo@google.comfd747b82011-04-23 01:30:07 +0000317}
318
319int TCompiler::getMappedNameMaxLength() const
320{
kbr@chromium.org22152112011-10-26 01:18:28 +0000321 return MAX_SHORTENED_IDENTIFIER_SIZE + 1;
zmo@google.comfd747b82011-04-23 01:30:07 +0000322}
zmo@google.com5601ea02011-06-10 18:23:25 +0000323
324const TExtensionBehavior& TCompiler::getExtensionBehavior() const
325{
326 return extensionBehavior;
327}
zmo@google.com32e97312011-08-24 01:03:11 +0000328
329const BuiltInFunctionEmulator& TCompiler::getBuiltInFunctionEmulator() const
330{
331 return builtInFunctionEmulator;
332}