1 //
2 // Copyright (C) 2017 LunarG, Inc.
3 // Copyright (C) 2018 Google, Inc.
4 //
5 // All rights reserved.
6 //
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions
9 // are met:
10 //
11 // Redistributions of source code must retain the above copyright
12 // notice, this list of conditions and the following disclaimer.
13 //
14 // Redistributions in binary form must reproduce the above
15 // copyright notice, this list of conditions and the following
16 // disclaimer in the documentation and/or other materials provided
17 // with the distribution.
18 //
19 // Neither the name of Google, Inc., nor the names of its
20 // contributors may be used to endorse or promote products derived
21 // from this software without specific prior written permission.
22 //
23 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26 // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
27 // COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
29 // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
31 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
33 // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 // POSSIBILITY OF SUCH DAMAGE.
35 //
36
37 #ifndef GLSLANG_WEB
38
39 #include "attribute.h"
40 #include "../Include/intermediate.h"
41 #include "ParseHelper.h"
42
43 namespace glslang {
44
45 // extract integers out of attribute arguments stored in attribute aggregate
getInt(int & value,int argNum) const46 bool TAttributeArgs::getInt(int& value, int argNum) const
47 {
48 const TConstUnion* intConst = getConstUnion(EbtInt, argNum);
49
50 if (intConst == nullptr)
51 return false;
52
53 value = intConst->getIConst();
54 return true;
55 }
56
57
58 // extract strings out of attribute arguments stored in attribute aggregate.
59 // convert to lower case if converToLower is true (for case-insensitive compare convenience)
getString(TString & value,int argNum,bool convertToLower) const60 bool TAttributeArgs::getString(TString& value, int argNum, bool convertToLower) const
61 {
62 const TConstUnion* stringConst = getConstUnion(EbtString, argNum);
63
64 if (stringConst == nullptr)
65 return false;
66
67 value = *stringConst->getSConst();
68
69 // Convenience.
70 if (convertToLower)
71 std::transform(value.begin(), value.end(), value.begin(), ::tolower);
72
73 return true;
74 }
75
76 // How many arguments were supplied?
size() const77 int TAttributeArgs::size() const
78 {
79 return args == nullptr ? 0 : (int)args->getSequence().size();
80 }
81
82 // Helper to get attribute const union. Returns nullptr on failure.
getConstUnion(TBasicType basicType,int argNum) const83 const TConstUnion* TAttributeArgs::getConstUnion(TBasicType basicType, int argNum) const
84 {
85 if (args == nullptr)
86 return nullptr;
87
88 if (argNum >= (int)args->getSequence().size())
89 return nullptr;
90
91 if (args->getSequence()[argNum]->getAsConstantUnion() == nullptr)
92 return nullptr;
93
94 const TConstUnion* constVal = &args->getSequence()[argNum]->getAsConstantUnion()->getConstArray()[0];
95 if (constVal == nullptr || constVal->getType() != basicType)
96 return nullptr;
97
98 return constVal;
99 }
100
101 // Implementation of TParseContext parts of attributes
attributeFromName(const TString & name) const102 TAttributeType TParseContext::attributeFromName(const TString& name) const
103 {
104 if (name == "branch" || name == "dont_flatten")
105 return EatBranch;
106 else if (name == "flatten")
107 return EatFlatten;
108 else if (name == "unroll")
109 return EatUnroll;
110 else if (name == "loop" || name == "dont_unroll")
111 return EatLoop;
112 else if (name == "dependency_infinite")
113 return EatDependencyInfinite;
114 else if (name == "dependency_length")
115 return EatDependencyLength;
116 else if (name == "min_iterations")
117 return EatMinIterations;
118 else if (name == "max_iterations")
119 return EatMaxIterations;
120 else if (name == "iteration_multiple")
121 return EatIterationMultiple;
122 else if (name == "peel_count")
123 return EatPeelCount;
124 else if (name == "partial_count")
125 return EatPartialCount;
126 else
127 return EatNone;
128 }
129
130 // Make an initial leaf for the grammar from a no-argument attribute
makeAttributes(const TString & identifier) const131 TAttributes* TParseContext::makeAttributes(const TString& identifier) const
132 {
133 TAttributes *attributes = nullptr;
134 attributes = NewPoolObject(attributes);
135 TAttributeArgs args = { attributeFromName(identifier), nullptr };
136 attributes->push_back(args);
137 return attributes;
138 }
139
140 // Make an initial leaf for the grammar from a one-argument attribute
makeAttributes(const TString & identifier,TIntermNode * node) const141 TAttributes* TParseContext::makeAttributes(const TString& identifier, TIntermNode* node) const
142 {
143 TAttributes *attributes = nullptr;
144 attributes = NewPoolObject(attributes);
145
146 // for now, node is always a simple single expression, but other code expects
147 // a list, so make it so
148 TIntermAggregate* agg = intermediate.makeAggregate(node);
149 TAttributeArgs args = { attributeFromName(identifier), agg };
150 attributes->push_back(args);
151 return attributes;
152 }
153
154 // Merge two sets of attributes into a single set.
155 // The second argument is destructively consumed.
mergeAttributes(TAttributes * attr1,TAttributes * attr2) const156 TAttributes* TParseContext::mergeAttributes(TAttributes* attr1, TAttributes* attr2) const
157 {
158 attr1->splice(attr1->end(), *attr2);
159 return attr1;
160 }
161
162 //
163 // Selection attributes
164 //
handleSelectionAttributes(const TAttributes & attributes,TIntermNode * node)165 void TParseContext::handleSelectionAttributes(const TAttributes& attributes, TIntermNode* node)
166 {
167 TIntermSelection* selection = node->getAsSelectionNode();
168 if (selection == nullptr)
169 return;
170
171 for (auto it = attributes.begin(); it != attributes.end(); ++it) {
172 if (it->size() > 0) {
173 warn(node->getLoc(), "attribute with arguments not recognized, skipping", "", "");
174 continue;
175 }
176
177 switch (it->name) {
178 case EatFlatten:
179 selection->setFlatten();
180 break;
181 case EatBranch:
182 selection->setDontFlatten();
183 break;
184 default:
185 warn(node->getLoc(), "attribute does not apply to a selection", "", "");
186 break;
187 }
188 }
189 }
190
191 //
192 // Switch attributes
193 //
handleSwitchAttributes(const TAttributes & attributes,TIntermNode * node)194 void TParseContext::handleSwitchAttributes(const TAttributes& attributes, TIntermNode* node)
195 {
196 TIntermSwitch* selection = node->getAsSwitchNode();
197 if (selection == nullptr)
198 return;
199
200 for (auto it = attributes.begin(); it != attributes.end(); ++it) {
201 if (it->size() > 0) {
202 warn(node->getLoc(), "attribute with arguments not recognized, skipping", "", "");
203 continue;
204 }
205
206 switch (it->name) {
207 case EatFlatten:
208 selection->setFlatten();
209 break;
210 case EatBranch:
211 selection->setDontFlatten();
212 break;
213 default:
214 warn(node->getLoc(), "attribute does not apply to a switch", "", "");
215 break;
216 }
217 }
218 }
219
220 //
221 // Loop attributes
222 //
handleLoopAttributes(const TAttributes & attributes,TIntermNode * node)223 void TParseContext::handleLoopAttributes(const TAttributes& attributes, TIntermNode* node)
224 {
225 TIntermLoop* loop = node->getAsLoopNode();
226 if (loop == nullptr) {
227 // the actual loop might be part of a sequence
228 TIntermAggregate* agg = node->getAsAggregate();
229 if (agg == nullptr)
230 return;
231 for (auto it = agg->getSequence().begin(); it != agg->getSequence().end(); ++it) {
232 loop = (*it)->getAsLoopNode();
233 if (loop != nullptr)
234 break;
235 }
236 if (loop == nullptr)
237 return;
238 }
239
240 for (auto it = attributes.begin(); it != attributes.end(); ++it) {
241
242 const auto noArgument = [&](const char* feature) {
243 if (it->size() > 0) {
244 warn(node->getLoc(), "expected no arguments", feature, "");
245 return false;
246 }
247 return true;
248 };
249
250 const auto positiveSignedArgument = [&](const char* feature, int& value) {
251 if (it->size() == 1 && it->getInt(value)) {
252 if (value <= 0) {
253 error(node->getLoc(), "must be positive", feature, "");
254 return false;
255 }
256 } else {
257 warn(node->getLoc(), "expected a single integer argument", feature, "");
258 return false;
259 }
260 return true;
261 };
262
263 const auto unsignedArgument = [&](const char* feature, unsigned int& uiValue) {
264 int value;
265 if (!(it->size() == 1 && it->getInt(value))) {
266 warn(node->getLoc(), "expected a single integer argument", feature, "");
267 return false;
268 }
269 uiValue = (unsigned int)value;
270 return true;
271 };
272
273 const auto positiveUnsignedArgument = [&](const char* feature, unsigned int& uiValue) {
274 int value;
275 if (it->size() == 1 && it->getInt(value)) {
276 if (value == 0) {
277 error(node->getLoc(), "must be greater than or equal to 1", feature, "");
278 return false;
279 }
280 } else {
281 warn(node->getLoc(), "expected a single integer argument", feature, "");
282 return false;
283 }
284 uiValue = (unsigned int)value;
285 return true;
286 };
287
288 const auto spirv14 = [&](const char* feature) {
289 if (spvVersion.spv > 0 && spvVersion.spv < EShTargetSpv_1_4)
290 warn(node->getLoc(), "attribute requires a SPIR-V 1.4 target-env", feature, "");
291 };
292
293 int value = 0;
294 unsigned uiValue = 0;
295 switch (it->name) {
296 case EatUnroll:
297 if (noArgument("unroll"))
298 loop->setUnroll();
299 break;
300 case EatLoop:
301 if (noArgument("dont_unroll"))
302 loop->setDontUnroll();
303 break;
304 case EatDependencyInfinite:
305 if (noArgument("dependency_infinite"))
306 loop->setLoopDependency(TIntermLoop::dependencyInfinite);
307 break;
308 case EatDependencyLength:
309 if (positiveSignedArgument("dependency_length", value))
310 loop->setLoopDependency(value);
311 break;
312 case EatMinIterations:
313 spirv14("min_iterations");
314 if (unsignedArgument("min_iterations", uiValue))
315 loop->setMinIterations(uiValue);
316 break;
317 case EatMaxIterations:
318 spirv14("max_iterations");
319 if (unsignedArgument("max_iterations", uiValue))
320 loop->setMaxIterations(uiValue);
321 break;
322 case EatIterationMultiple:
323 spirv14("iteration_multiple");
324 if (positiveUnsignedArgument("iteration_multiple", uiValue))
325 loop->setIterationMultiple(uiValue);
326 break;
327 case EatPeelCount:
328 spirv14("peel_count");
329 if (unsignedArgument("peel_count", uiValue))
330 loop->setPeelCount(uiValue);
331 break;
332 case EatPartialCount:
333 spirv14("partial_count");
334 if (unsignedArgument("partial_count", uiValue))
335 loop->setPartialCount(uiValue);
336 break;
337 default:
338 warn(node->getLoc(), "attribute does not apply to a loop", "", "");
339 break;
340 }
341 }
342 }
343
344 } // end namespace glslang
345
346 #endif // GLSLANG_WEB
347