Fix variable scoping of do-while
[platform/upstream/glslang.git] / glslang / MachineIndependent / attribute.cpp
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
46 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)
60 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?
77 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.
83 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
102 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 if (name == "subgroup_uniform_control_flow")
127         return EatSubgroupUniformControlFlow;
128     else
129         return EatNone;
130 }
131
132 // Make an initial leaf for the grammar from a no-argument attribute
133 TAttributes* TParseContext::makeAttributes(const TString& identifier) const
134 {
135     TAttributes *attributes = nullptr;
136     attributes = NewPoolObject(attributes);
137     TAttributeArgs args = { attributeFromName(identifier), nullptr };
138     attributes->push_back(args);
139     return attributes;
140 }
141
142 // Make an initial leaf for the grammar from a one-argument attribute
143 TAttributes* TParseContext::makeAttributes(const TString& identifier, TIntermNode* node) const
144 {
145     TAttributes *attributes = nullptr;
146     attributes = NewPoolObject(attributes);
147
148     // for now, node is always a simple single expression, but other code expects
149     // a list, so make it so
150     TIntermAggregate* agg = intermediate.makeAggregate(node);
151     TAttributeArgs args = { attributeFromName(identifier), agg };
152     attributes->push_back(args);
153     return attributes;
154 }
155
156 // Merge two sets of attributes into a single set.
157 // The second argument is destructively consumed.
158 TAttributes* TParseContext::mergeAttributes(TAttributes* attr1, TAttributes* attr2) const
159 {
160     attr1->splice(attr1->end(), *attr2);
161     return attr1;
162 }
163
164 //
165 // Selection attributes
166 //
167 void TParseContext::handleSelectionAttributes(const TAttributes& attributes, TIntermNode* node)
168 {
169     TIntermSelection* selection = node->getAsSelectionNode();
170     if (selection == nullptr)
171         return;
172
173     for (auto it = attributes.begin(); it != attributes.end(); ++it) {
174         if (it->size() > 0) {
175             warn(node->getLoc(), "attribute with arguments not recognized, skipping", "", "");
176             continue;
177         }
178
179         switch (it->name) {
180         case EatFlatten:
181             selection->setFlatten();
182             break;
183         case EatBranch:
184             selection->setDontFlatten();
185             break;
186         default:
187             warn(node->getLoc(), "attribute does not apply to a selection", "", "");
188             break;
189         }
190     }
191 }
192
193 //
194 // Switch attributes
195 //
196 void TParseContext::handleSwitchAttributes(const TAttributes& attributes, TIntermNode* node)
197 {
198     TIntermSwitch* selection = node->getAsSwitchNode();
199     if (selection == nullptr)
200         return;
201
202     for (auto it = attributes.begin(); it != attributes.end(); ++it) {
203         if (it->size() > 0) {
204             warn(node->getLoc(), "attribute with arguments not recognized, skipping", "", "");
205             continue;
206         }
207
208         switch (it->name) {
209         case EatFlatten:
210             selection->setFlatten();
211             break;
212         case EatBranch:
213             selection->setDontFlatten();
214             break;
215         default:
216             warn(node->getLoc(), "attribute does not apply to a switch", "", "");
217             break;
218         }
219     }
220 }
221
222 //
223 // Loop attributes
224 //
225 void TParseContext::handleLoopAttributes(const TAttributes& attributes, TIntermNode* node)
226 {
227     TIntermLoop* loop = node->getAsLoopNode();
228     if (loop == nullptr) {
229         // the actual loop might be part of a sequence
230         TIntermAggregate* agg = node->getAsAggregate();
231         if (agg == nullptr)
232             return;
233         for (auto it = agg->getSequence().begin(); it != agg->getSequence().end(); ++it) {
234             loop = (*it)->getAsLoopNode();
235             if (loop != nullptr)
236                 break;
237         }
238         if (loop == nullptr)
239             return;
240     }
241
242     for (auto it = attributes.begin(); it != attributes.end(); ++it) {
243
244         const auto noArgument = [&](const char* feature) {
245             if (it->size() > 0) {
246                 warn(node->getLoc(), "expected no arguments", feature, "");
247                 return false;
248             }
249             return true;
250         };
251
252         const auto positiveSignedArgument = [&](const char* feature, int& value) {
253             if (it->size() == 1 && it->getInt(value)) {
254                 if (value <= 0) {
255                     error(node->getLoc(), "must be positive", feature, "");
256                     return false;
257                 }
258             } else {
259                 warn(node->getLoc(), "expected a single integer argument", feature, "");
260                 return false;
261             }
262             return true;
263         };
264
265         const auto unsignedArgument = [&](const char* feature, unsigned int& uiValue) {
266             int value;
267             if (!(it->size() == 1 && it->getInt(value))) {
268                 warn(node->getLoc(), "expected a single integer argument", feature, "");
269                 return false;
270             }
271             uiValue = (unsigned int)value;
272             return true;
273         };
274
275         const auto positiveUnsignedArgument = [&](const char* feature, unsigned int& uiValue) {
276             int value;
277             if (it->size() == 1 && it->getInt(value)) {
278                 if (value == 0) {
279                     error(node->getLoc(), "must be greater than or equal to 1", feature, "");
280                     return false;
281                 }
282             } else {
283                 warn(node->getLoc(), "expected a single integer argument", feature, "");
284                 return false;
285             }
286             uiValue = (unsigned int)value;
287             return true;
288         };
289
290         const auto spirv14 = [&](const char* feature) {
291             if (spvVersion.spv > 0 && spvVersion.spv < EShTargetSpv_1_4)
292                 warn(node->getLoc(), "attribute requires a SPIR-V 1.4 target-env", feature, "");
293         };
294
295         int value = 0;
296         unsigned uiValue = 0;
297         switch (it->name) {
298         case EatUnroll:
299             if (noArgument("unroll"))
300                 loop->setUnroll();
301             break;
302         case EatLoop:
303             if (noArgument("dont_unroll"))
304                 loop->setDontUnroll();
305             break;
306         case EatDependencyInfinite:
307             if (noArgument("dependency_infinite"))
308                 loop->setLoopDependency(TIntermLoop::dependencyInfinite);
309             break;
310         case EatDependencyLength:
311             if (positiveSignedArgument("dependency_length", value))
312                 loop->setLoopDependency(value);
313             break;
314         case EatMinIterations:
315             spirv14("min_iterations");
316             if (unsignedArgument("min_iterations", uiValue))
317                 loop->setMinIterations(uiValue);
318             break;
319         case EatMaxIterations:
320             spirv14("max_iterations");
321             if (unsignedArgument("max_iterations", uiValue))
322                 loop->setMaxIterations(uiValue);
323             break;
324         case EatIterationMultiple:
325             spirv14("iteration_multiple");
326             if (positiveUnsignedArgument("iteration_multiple", uiValue))
327                 loop->setIterationMultiple(uiValue);
328             break;
329         case EatPeelCount:
330             spirv14("peel_count");
331             if (unsignedArgument("peel_count", uiValue))
332                 loop->setPeelCount(uiValue);
333             break;
334         case EatPartialCount:
335             spirv14("partial_count");
336             if (unsignedArgument("partial_count", uiValue))
337                 loop->setPartialCount(uiValue);
338             break;
339         default:
340             warn(node->getLoc(), "attribute does not apply to a loop", "", "");
341             break;
342         }
343     }
344 }
345
346
347 //
348 // Function attributes
349 //
350 void TParseContext::handleFunctionAttributes(const TSourceLoc& loc, const TAttributes& attributes)
351 {
352     for (auto it = attributes.begin(); it != attributes.end(); ++it) {
353         if (it->size() > 0) {
354             warn(loc, "attribute with arguments not recognized, skipping", "", "");
355             continue;
356         }
357
358         switch (it->name) {
359         case EatSubgroupUniformControlFlow:
360             intermediate.setSubgroupUniformControlFlow();
361             break;
362         default:
363             warn(loc, "attribute does not apply to a function", "", "");
364             break;
365         }
366     }
367 }
368
369 } // end namespace glslang
370
371 #endif // GLSLANG_WEB