blob: 59e8ffed70a7771626e7d1b823c269ddbad081b5 [file] [log] [blame]
John Bauman66b8ab22014-05-06 15:57:45 -04001//
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.
5//
6
7#include "compiler/DetectRecursion.h"
8#include "compiler/Initialize.h"
9#include "compiler/InitializeParseContext.h"
10#include "compiler/ParseHelper.h"
11#include "compiler/ShHandle.h"
12#include "compiler/ValidateLimitations.h"
13
14namespace {
15bool InitializeSymbolTable(
16 const TBuiltInStrings& builtInStrings,
17 ShShaderType type, ShShaderSpec spec, const ShBuiltInResources& resources,
18 TInfoSink& infoSink, TSymbolTable& symbolTable)
19{
20 TIntermediate intermediate(infoSink);
21 TExtensionBehavior extBehavior;
22 InitExtensionBehavior(resources, extBehavior);
23 // The builtins deliberately don't specify precisions for the function
24 // arguments and return types. For that reason we don't try to check them.
25 TParseContext parseContext(symbolTable, extBehavior, intermediate, type, spec, 0, false, NULL, infoSink);
26
27 GlobalParseContext = &parseContext;
28
29 assert(symbolTable.isEmpty());
30 //
31 // Parse the built-ins. This should only happen once per
32 // language symbol table.
33 //
34 // Push the symbol table to give it an initial scope. This
35 // push should not have a corresponding pop, so that built-ins
36 // are preserved, and the test for an empty table fails.
37 //
38 symbolTable.push();
39
40 for (TBuiltInStrings::const_iterator i = builtInStrings.begin(); i != builtInStrings.end(); ++i)
41 {
42 const char* builtInShaders = i->c_str();
43 int builtInLengths = static_cast<int>(i->size());
44 if (builtInLengths <= 0)
45 continue;
46
47 if (PaParseStrings(1, &builtInShaders, &builtInLengths, &parseContext) != 0)
48 {
49 infoSink.info.message(EPrefixInternalError, "Unable to parse built-ins");
50 return false;
51 }
52 }
53
54 IdentifyBuiltIns(type, spec, resources, symbolTable);
55
56 return true;
57}
58
59class TScopedPoolAllocator {
60public:
61 TScopedPoolAllocator(TPoolAllocator* allocator, bool pushPop)
62 : mAllocator(allocator), mPushPopAllocator(pushPop) {
63 if (mPushPopAllocator) mAllocator->push();
64 SetGlobalPoolAllocator(mAllocator);
65 }
66 ~TScopedPoolAllocator() {
67 SetGlobalPoolAllocator(NULL);
68 if (mPushPopAllocator) mAllocator->pop();
69 }
70
71private:
72 TPoolAllocator* mAllocator;
73 bool mPushPopAllocator;
74};
75} // namespace
76
77TShHandleBase::TShHandleBase() {
78 allocator.push();
79 SetGlobalPoolAllocator(&allocator);
80}
81
82TShHandleBase::~TShHandleBase() {
83 SetGlobalPoolAllocator(NULL);
84 allocator.popAll();
85}
86
87TCompiler::TCompiler(ShShaderType type, ShShaderSpec spec)
88 : shaderType(type),
89 shaderSpec(spec)
90{
91}
92
93TCompiler::~TCompiler()
94{
95}
96
97bool TCompiler::Init(const ShBuiltInResources& resources)
98{
99 TScopedPoolAllocator scopedAlloc(&allocator, false);
100
101 // Generate built-in symbol table.
102 if (!InitBuiltInSymbolTable(resources))
103 return false;
104 InitExtensionBehavior(resources, extensionBehavior);
105
106 return true;
107}
108
109bool TCompiler::compile(const char* const shaderStrings[],
110 const int numStrings,
111 int compileOptions)
112{
113 TScopedPoolAllocator scopedAlloc(&allocator, true);
114 clearResults();
115
116 if (numStrings == 0)
117 return true;
118
119 // If compiling for WebGL, validate loop and indexing as well.
120 if (shaderSpec == SH_WEBGL_SPEC)
121 compileOptions |= SH_VALIDATE_LOOP_INDEXING;
122
123 // First string is path of source file if flag is set. The actual source follows.
124 const char* sourcePath = NULL;
125 int firstSource = 0;
126 if (compileOptions & SH_SOURCE_PATH)
127 {
128 sourcePath = shaderStrings[0];
129 ++firstSource;
130 }
131
132 TIntermediate intermediate(infoSink);
133 TParseContext parseContext(symbolTable, extensionBehavior, intermediate,
134 shaderType, shaderSpec, compileOptions, true,
135 sourcePath, infoSink);
136 GlobalParseContext = &parseContext;
137
138 // We preserve symbols at the built-in level from compile-to-compile.
139 // Start pushing the user-defined symbols at global level.
140 symbolTable.push();
141 if (!symbolTable.atGlobalLevel())
142 infoSink.info.message(EPrefixInternalError, "Wrong symbol table level");
143
144 // Parse shader.
145 bool success =
146 (PaParseStrings(numStrings - firstSource, &shaderStrings[firstSource], NULL, &parseContext) == 0) &&
147 (parseContext.treeRoot != NULL);
148 if (success) {
149 TIntermNode* root = parseContext.treeRoot;
150 success = intermediate.postProcess(root);
151
152 if (success)
153 success = detectRecursion(root);
154
155 if (success && (compileOptions & SH_VALIDATE_LOOP_INDEXING))
156 success = validateLimitations(root);
157
158 if (success && (compileOptions & SH_ATTRIBUTES_UNIFORMS))
159 collectAttribsUniforms(root);
160
161 if (success && (compileOptions & SH_INTERMEDIATE_TREE))
162 intermediate.outputTree(root);
163
164 if (success && (compileOptions & SH_OBJECT_CODE))
165 translate(root);
166 }
167
168 // Cleanup memory.
169 intermediate.remove(parseContext.treeRoot);
170 // Ensure symbol table is returned to the built-in level,
171 // throwing away all but the built-ins.
172 while (!symbolTable.atBuiltInLevel())
173 symbolTable.pop();
174
175 return success;
176}
177
178bool TCompiler::InitBuiltInSymbolTable(const ShBuiltInResources& resources)
179{
180 TBuiltIns builtIns;
181
182 builtIns.initialize(shaderType, shaderSpec, resources);
183 return InitializeSymbolTable(builtIns.getBuiltInStrings(),
184 shaderType, shaderSpec, resources, infoSink, symbolTable);
185}
186
187void TCompiler::clearResults()
188{
189 infoSink.info.erase();
190 infoSink.obj.erase();
191 infoSink.debug.erase();
192
193 attribs.clear();
194 uniforms.clear();
195}
196
197bool TCompiler::detectRecursion(TIntermNode* root)
198{
199 DetectRecursion detect;
200 root->traverse(&detect);
201 switch (detect.detectRecursion()) {
202 case DetectRecursion::kErrorNone:
203 return true;
204 case DetectRecursion::kErrorMissingMain:
205 infoSink.info.message(EPrefixError, "Missing main()");
206 return false;
207 case DetectRecursion::kErrorRecursion:
208 infoSink.info.message(EPrefixError, "Function recursion detected");
209 return false;
210 default:
211 UNREACHABLE();
212 return false;
213 }
214}
215
216bool TCompiler::validateLimitations(TIntermNode* root) {
217 ValidateLimitations validate(shaderType, infoSink.info);
218 root->traverse(&validate);
219 return validate.numErrors() == 0;
220}
221
222void TCompiler::collectAttribsUniforms(TIntermNode* root)
223{
224 CollectAttribsUniforms collect(attribs, uniforms);
225 root->traverse(&collect);
226}
227
228const TExtensionBehavior& TCompiler::getExtensionBehavior() const
229{
230 return extensionBehavior;
231}