blob: 8aef072ff9ffa28a00b5c10c8f48d5013866aa65 [file] [log] [blame]
alokp@chromium.org07620a52010-09-23 17:53:56 +00001//
shannon.woods%transgaming.com@gtempaccount.com0bbed382013-04-13 03:38:07 +00002// Copyright (c) 2002-2013 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"
gman@chromium.org8d804792012-10-17 21:33:48 +000017#include "compiler/VariablePacker.h"
maxvujovic@gmail.com66ebd012012-05-30 22:18:11 +000018#include "compiler/depgraph/DependencyGraph.h"
19#include "compiler/depgraph/DependencyGraphOutput.h"
20#include "compiler/timing/RestrictFragmentShaderTiming.h"
21#include "compiler/timing/RestrictVertexShaderTiming.h"
shannon.woods@transgaming.comda1ed362013-01-25 21:54:57 +000022#include "third_party/compiler/ArrayBoundsClamper.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 {
alokp@chromium.orgbafcbaa2010-11-23 19:07:43 +000030class 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
alokp@chromium.org4888ceb2010-10-01 21:13:12 +000058TCompiler::TCompiler(ShShaderType type, ShShaderSpec spec)
59 : shaderType(type),
zmo@google.comf420c422011-09-12 18:27:59 +000060 shaderSpec(spec),
shannon.woods%transgaming.com@gtempaccount.comcbb6b6a2013-04-13 03:27:47 +000061 fragmentPrecisionHigh(false),
shannon.woods@transgaming.com1d432bb2013-01-25 21:57:28 +000062 clampingStrategy(SH_CLAMP_WITH_CLAMP_INTRINSIC),
zmo@google.com9996b8e2012-01-19 01:43:55 +000063 builtInFunctionEmulator(type)
alokp@chromium.org4888ceb2010-10-01 21:13:12 +000064{
zmo@google.comb9f64aa2012-01-20 00:35:15 +000065 longNameMap = LongNameMap::GetInstance();
alokp@chromium.org4888ceb2010-10-01 21:13:12 +000066}
67
68TCompiler::~TCompiler()
69{
zmo@google.comb9f64aa2012-01-20 00:35:15 +000070 ASSERT(longNameMap);
71 longNameMap->Release();
alokp@chromium.org4888ceb2010-10-01 21:13:12 +000072}
73
74bool TCompiler::Init(const ShBuiltInResources& resources)
alokp@chromium.org07620a52010-09-23 17:53:56 +000075{
shannon.woods%transgaming.com@gtempaccount.com0bbed382013-04-13 03:38:07 +000076 shaderVersion = 100;
gman@chromium.org8d804792012-10-17 21:33:48 +000077 maxUniformVectors = (shaderType == SH_VERTEX_SHADER) ?
78 resources.MaxVertexUniformVectors :
79 resources.MaxFragmentUniformVectors;
alokp@chromium.orgbafcbaa2010-11-23 19:07:43 +000080 TScopedPoolAllocator scopedAlloc(&allocator, false);
81
alokp@chromium.org07620a52010-09-23 17:53:56 +000082 // Generate built-in symbol table.
83 if (!InitBuiltInSymbolTable(resources))
84 return false;
alokp@chromium.org07620a52010-09-23 17:53:56 +000085 InitExtensionBehavior(resources, extensionBehavior);
shannon.woods%transgaming.com@gtempaccount.comcbb6b6a2013-04-13 03:27:47 +000086 fragmentPrecisionHigh = resources.FragmentPrecisionHigh == 1;
alokp@chromium.orgbafcbaa2010-11-23 19:07:43 +000087
shannon.woods@transgaming.com1d432bb2013-01-25 21:57:28 +000088 arrayBoundsClamper.SetClampingStrategy(resources.ArrayIndexClampingStrategy);
89 clampingStrategy = resources.ArrayIndexClampingStrategy;
90
daniel@transgaming.comc23f4612012-11-28 19:42:57 +000091 hashFunction = resources.HashFunction;
92
alokp@chromium.org07620a52010-09-23 17:53:56 +000093 return true;
94}
95
96bool TCompiler::compile(const char* const shaderStrings[],
shannon.woods@transgaming.comd64b3da2013-02-28 23:19:26 +000097 size_t numStrings,
alokp@chromium.org07620a52010-09-23 17:53:56 +000098 int compileOptions)
99{
alokp@chromium.orgbafcbaa2010-11-23 19:07:43 +0000100 TScopedPoolAllocator scopedAlloc(&allocator, true);
alokp@chromium.org07620a52010-09-23 17:53:56 +0000101 clearResults();
102
103 if (numStrings == 0)
104 return true;
105
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000106 // If compiling for WebGL, validate loop and indexing as well.
maxvujovic@gmail.com430f5e02012-06-08 17:47:59 +0000107 if (isWebGLBasedSpec(shaderSpec))
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000108 compileOptions |= SH_VALIDATE_LOOP_INDEXING;
alokp@chromium.org1f299542010-11-12 15:50:23 +0000109
apatrick@chromium.org0f4cefe2011-01-26 19:30:57 +0000110 // First string is path of source file if flag is set. The actual source follows.
111 const char* sourcePath = NULL;
shannon.woods@transgaming.comd64b3da2013-02-28 23:19:26 +0000112 size_t firstSource = 0;
apatrick@chromium.org0f4cefe2011-01-26 19:30:57 +0000113 if (compileOptions & SH_SOURCE_PATH)
114 {
115 sourcePath = shaderStrings[0];
116 ++firstSource;
117 }
118
alokp@chromium.org07620a52010-09-23 17:53:56 +0000119 TIntermediate intermediate(infoSink);
120 TParseContext parseContext(symbolTable, extensionBehavior, intermediate,
zmo@google.comdc4b4f82011-06-17 00:42:53 +0000121 shaderType, shaderSpec, compileOptions, true,
apatrick@chromium.org0f4cefe2011-01-26 19:30:57 +0000122 sourcePath, infoSink);
shannon.woods%transgaming.com@gtempaccount.comcbb6b6a2013-04-13 03:27:47 +0000123 parseContext.fragmentPrecisionHigh = fragmentPrecisionHigh;
alokp@chromium.org07620a52010-09-23 17:53:56 +0000124 GlobalParseContext = &parseContext;
alokp@chromium.org07620a52010-09-23 17:53:56 +0000125
126 // We preserve symbols at the built-in level from compile-to-compile.
127 // Start pushing the user-defined symbols at global level.
128 symbolTable.push();
129 if (!symbolTable.atGlobalLevel())
130 infoSink.info.message(EPrefixInternalError, "Wrong symbol table level");
131
132 // Parse shader.
133 bool success =
apatrick@chromium.org0f4cefe2011-01-26 19:30:57 +0000134 (PaParseStrings(numStrings - firstSource, &shaderStrings[firstSource], NULL, &parseContext) == 0) &&
alokp@chromium.org07620a52010-09-23 17:53:56 +0000135 (parseContext.treeRoot != NULL);
shannon.woods%transgaming.com@gtempaccount.com0bbed382013-04-13 03:38:07 +0000136
shannon.woods%transgaming.com@gtempaccount.com5524db02013-04-13 03:38:16 +0000137 shaderVersion = parseContext.getShaderVersion();
shannon.woods%transgaming.com@gtempaccount.com0bbed382013-04-13 03:38:07 +0000138
alokp@chromium.org07620a52010-09-23 17:53:56 +0000139 if (success) {
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000140 TIntermNode* root = parseContext.treeRoot;
141 success = intermediate.postProcess(root);
142
zmo@google.comb1762df2011-07-30 02:04:23 +0000143 if (success)
144 success = detectRecursion(root);
145
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000146 if (success && (compileOptions & SH_VALIDATE_LOOP_INDEXING))
147 success = validateLimitations(root);
alokp@chromium.org07620a52010-09-23 17:53:56 +0000148
maxvujovic@gmail.com66ebd012012-05-30 22:18:11 +0000149 if (success && (compileOptions & SH_TIMING_RESTRICTIONS))
maxvujovic@gmail.com77222c92012-06-04 21:06:05 +0000150 success = enforceTimingRestrictions(root, (compileOptions & SH_DEPENDENCY_GRAPH) != 0);
maxvujovic@gmail.com66ebd012012-05-30 22:18:11 +0000151
maxvujovic@gmail.com430f5e02012-06-08 17:47:59 +0000152 if (success && shaderSpec == SH_CSS_SHADERS_SPEC)
153 rewriteCSSShader(root);
154
zmo@google.com0c6bb7a2011-08-17 19:39:58 +0000155 // Unroll for-loop markup needs to happen after validateLimitations pass.
156 if (success && (compileOptions & SH_UNROLL_FOR_LOOP_WITH_INTEGER_INDEX))
157 ForLoopUnroll::MarkForLoopsWithIntegerIndicesForUnrolling(root);
158
zmo@google.com32e97312011-08-24 01:03:11 +0000159 // Built-in function emulation needs to happen after validateLimitations pass.
160 if (success && (compileOptions & SH_EMULATE_BUILT_IN_FUNCTIONS))
161 builtInFunctionEmulator.MarkBuiltInFunctionsForEmulation(root);
162
daniel@transgaming.com4167cc92013-01-11 04:11:53 +0000163 // Clamping uniform array bounds needs to happen after validateLimitations pass.
164 if (success && (compileOptions & SH_CLAMP_INDIRECT_ARRAY_BOUNDS))
165 arrayBoundsClamper.MarkIndirectArrayBoundsForClamping(root);
166
zmo@google.comfd747b82011-04-23 01:30:07 +0000167 // Call mapLongVariableNames() before collectAttribsUniforms() so in
168 // collectAttribsUniforms() we already have the mapped symbol names and
169 // we could composite mapped and original variable names.
daniel@transgaming.com0aa3b5a2012-11-28 19:43:24 +0000170 // Also, if we hash all the names, then no need to do this for long names.
171 if (success && (compileOptions & SH_MAP_LONG_VARIABLE_NAMES) && hashFunction == NULL)
zmo@google.comfd747b82011-04-23 01:30:07 +0000172 mapLongVariableNames(root);
173
gman@chromium.org8d804792012-10-17 21:33:48 +0000174 if (success && (compileOptions & SH_ATTRIBUTES_UNIFORMS)) {
zmo@google.comfd747b82011-04-23 01:30:07 +0000175 collectAttribsUniforms(root);
gman@chromium.org8d804792012-10-17 21:33:48 +0000176 if (compileOptions & SH_ENFORCE_PACKING_RESTRICTIONS) {
177 success = enforcePackingRestrictions();
178 if (!success) {
179 infoSink.info.message(EPrefixError, "too many uniforms");
180 }
181 }
182 }
zmo@google.comfd747b82011-04-23 01:30:07 +0000183
alokp@chromium.org4888ceb2010-10-01 21:13:12 +0000184 if (success && (compileOptions & SH_INTERMEDIATE_TREE))
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000185 intermediate.outputTree(root);
alokp@chromium.org07620a52010-09-23 17:53:56 +0000186
alokp@chromium.org4888ceb2010-10-01 21:13:12 +0000187 if (success && (compileOptions & SH_OBJECT_CODE))
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000188 translate(root);
alokp@chromium.org07620a52010-09-23 17:53:56 +0000189 }
190
191 // Cleanup memory.
192 intermediate.remove(parseContext.treeRoot);
193 // Ensure symbol table is returned to the built-in level,
194 // throwing away all but the built-ins.
195 while (!symbolTable.atBuiltInLevel())
196 symbolTable.pop();
alokp@chromium.org07620a52010-09-23 17:53:56 +0000197
198 return success;
199}
200
Nicolas Capens49a88872013-06-20 09:54:03 -0400201bool TCompiler::InitBuiltInSymbolTable(const ShBuiltInResources &resources)
alokp@chromium.org07620a52010-09-23 17:53:56 +0000202{
shannon.woods%transgaming.com@gtempaccount.com18b4c4b2013-04-13 03:31:40 +0000203 compileResources = resources;
shannonwoods@chromium.org2ac0be92013-05-30 00:02:27 +0000204
Nicolas Capens49a88872013-06-20 09:54:03 -0400205 assert(symbolTable.isEmpty());
206 symbolTable.push(); // COMMON_BUILTINS
207 symbolTable.push(); // ESSL1_BUILTINS
208 symbolTable.push(); // ESSL3_BUILTINS
shannonwoods@chromium.org2ac0be92013-05-30 00:02:27 +0000209
Nicolas Capens49a88872013-06-20 09:54:03 -0400210 TPublicType integer;
211 integer.type = EbtInt;
212 integer.primarySize = 1;
213 integer.secondarySize = 1;
214 integer.array = false;
215
216 TPublicType floatingPoint;
217 floatingPoint.type = EbtFloat;
218 floatingPoint.primarySize = 1;
219 floatingPoint.secondarySize = 1;
220 floatingPoint.array = false;
221
222 switch(shaderType)
223 {
224 case SH_FRAGMENT_SHADER:
225 symbolTable.setDefaultPrecision(integer, EbpMedium);
226 break;
227 case SH_VERTEX_SHADER:
228 symbolTable.setDefaultPrecision(integer, EbpHigh);
229 symbolTable.setDefaultPrecision(floatingPoint, EbpHigh);
230 break;
231 default: assert(false && "Language not supported");
232 }
233
234 InsertBuiltInFunctions(shaderType, shaderSpec, resources, extensionBehavior, symbolTable);
235
236 IdentifyBuiltIns(shaderType, shaderSpec, resources, symbolTable);
237
238 return true;
alokp@chromium.org07620a52010-09-23 17:53:56 +0000239}
240
241void TCompiler::clearResults()
242{
daniel@transgaming.com4167cc92013-01-11 04:11:53 +0000243 arrayBoundsClamper.Cleanup();
alokp@chromium.org07620a52010-09-23 17:53:56 +0000244 infoSink.info.erase();
245 infoSink.obj.erase();
246 infoSink.debug.erase();
247
248 attribs.clear();
249 uniforms.clear();
zmo@google.coma3b4ab42011-09-16 00:53:26 +0000250
251 builtInFunctionEmulator.Cleanup();
daniel@transgaming.com0aa3b5a2012-11-28 19:43:24 +0000252
253 nameMap.clear();
alokp@chromium.org07620a52010-09-23 17:53:56 +0000254}
255
zmo@google.comb1762df2011-07-30 02:04:23 +0000256bool TCompiler::detectRecursion(TIntermNode* root)
257{
258 DetectRecursion detect;
259 root->traverse(&detect);
260 switch (detect.detectRecursion()) {
261 case DetectRecursion::kErrorNone:
262 return true;
263 case DetectRecursion::kErrorMissingMain:
264 infoSink.info.message(EPrefixError, "Missing main()");
265 return false;
266 case DetectRecursion::kErrorRecursion:
267 infoSink.info.message(EPrefixError, "Function recursion detected");
268 return false;
269 default:
270 UNREACHABLE();
271 return false;
272 }
273}
274
maxvujovic@gmail.com430f5e02012-06-08 17:47:59 +0000275void TCompiler::rewriteCSSShader(TIntermNode* root)
276{
277 RenameFunction renamer("main(", "css_main(");
278 root->traverse(&renamer);
279}
280
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000281bool TCompiler::validateLimitations(TIntermNode* root) {
282 ValidateLimitations validate(shaderType, infoSink.info);
283 root->traverse(&validate);
284 return validate.numErrors() == 0;
285}
286
maxvujovic@gmail.com77222c92012-06-04 21:06:05 +0000287bool TCompiler::enforceTimingRestrictions(TIntermNode* root, bool outputGraph)
maxvujovic@gmail.com66ebd012012-05-30 22:18:11 +0000288{
289 if (shaderSpec != SH_WEBGL_SPEC) {
290 infoSink.info << "Timing restrictions must be enforced under the WebGL spec.";
291 return false;
292 }
293
294 if (shaderType == SH_FRAGMENT_SHADER) {
295 TDependencyGraph graph(root);
296
297 // Output any errors first.
maxvujovic@gmail.com77222c92012-06-04 21:06:05 +0000298 bool success = enforceFragmentShaderTimingRestrictions(graph);
maxvujovic@gmail.com66ebd012012-05-30 22:18:11 +0000299
300 // Then, output the dependency graph.
301 if (outputGraph) {
302 TDependencyGraphOutput output(infoSink.info);
303 output.outputAllSpanningTrees(graph);
304 }
305
306 return success;
307 }
308 else {
maxvujovic@gmail.com77222c92012-06-04 21:06:05 +0000309 return enforceVertexShaderTimingRestrictions(root);
maxvujovic@gmail.com66ebd012012-05-30 22:18:11 +0000310 }
311}
312
maxvujovic@gmail.com77222c92012-06-04 21:06:05 +0000313bool TCompiler::enforceFragmentShaderTimingRestrictions(const TDependencyGraph& graph)
maxvujovic@gmail.com66ebd012012-05-30 22:18:11 +0000314{
maxvujovic@gmail.com77222c92012-06-04 21:06:05 +0000315 RestrictFragmentShaderTiming restrictor(infoSink.info);
maxvujovic@gmail.com66ebd012012-05-30 22:18:11 +0000316 restrictor.enforceRestrictions(graph);
317 return restrictor.numErrors() == 0;
318}
319
maxvujovic@gmail.com77222c92012-06-04 21:06:05 +0000320bool TCompiler::enforceVertexShaderTimingRestrictions(TIntermNode* root)
maxvujovic@gmail.com66ebd012012-05-30 22:18:11 +0000321{
maxvujovic@gmail.com77222c92012-06-04 21:06:05 +0000322 RestrictVertexShaderTiming restrictor(infoSink.info);
maxvujovic@gmail.com66ebd012012-05-30 22:18:11 +0000323 restrictor.enforceRestrictions(root);
324 return restrictor.numErrors() == 0;
325}
326
alokp@chromium.org07620a52010-09-23 17:53:56 +0000327void TCompiler::collectAttribsUniforms(TIntermNode* root)
328{
daniel@transgaming.com0aa3b5a2012-11-28 19:43:24 +0000329 CollectAttribsUniforms collect(attribs, uniforms, hashFunction);
alokp@chromium.org07620a52010-09-23 17:53:56 +0000330 root->traverse(&collect);
331}
zmo@google.comfd747b82011-04-23 01:30:07 +0000332
gman@chromium.org8d804792012-10-17 21:33:48 +0000333bool TCompiler::enforcePackingRestrictions()
334{
335 VariablePacker packer;
336 return packer.CheckVariablesWithinPackingLimits(maxUniformVectors, uniforms);
337}
338
zmo@google.comfd747b82011-04-23 01:30:07 +0000339void TCompiler::mapLongVariableNames(TIntermNode* root)
340{
zmo@google.comb9f64aa2012-01-20 00:35:15 +0000341 ASSERT(longNameMap);
342 MapLongVariableNames map(longNameMap);
zmo@google.com9996b8e2012-01-19 01:43:55 +0000343 root->traverse(&map);
zmo@google.comfd747b82011-04-23 01:30:07 +0000344}
345
346int TCompiler::getMappedNameMaxLength() const
347{
kbr@chromium.org22152112011-10-26 01:18:28 +0000348 return MAX_SHORTENED_IDENTIFIER_SIZE + 1;
zmo@google.comfd747b82011-04-23 01:30:07 +0000349}
zmo@google.com5601ea02011-06-10 18:23:25 +0000350
351const TExtensionBehavior& TCompiler::getExtensionBehavior() const
352{
353 return extensionBehavior;
354}
zmo@google.com32e97312011-08-24 01:03:11 +0000355
shannon.woods%transgaming.com@gtempaccount.com18b4c4b2013-04-13 03:31:40 +0000356const ShBuiltInResources& TCompiler::getResources() const
357{
358 return compileResources;
359}
360
daniel@transgaming.com4167cc92013-01-11 04:11:53 +0000361const ArrayBoundsClamper& TCompiler::getArrayBoundsClamper() const
362{
363 return arrayBoundsClamper;
364}
365
shannon.woods@transgaming.com1d432bb2013-01-25 21:57:28 +0000366ShArrayIndexClampingStrategy TCompiler::getArrayIndexClampingStrategy() const
367{
368 return clampingStrategy;
369}
370
371const BuiltInFunctionEmulator& TCompiler::getBuiltInFunctionEmulator() const
372{
373 return builtInFunctionEmulator;
374}