• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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