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 #include "attribute.h"
38 #include "../Include/intermediate.h"
39 #include "ParseHelper.h"
40
41 namespace glslang {
42
43 // extract integers out of attribute arguments stored in attribute aggregate
getInt(int & value,int argNum) const44 bool TAttributeArgs::getInt(int& value, int argNum) const
45 {
46 const TConstUnion* intConst = getConstUnion(EbtInt, argNum);
47
48 if (intConst == nullptr)
49 return false;
50
51 value = intConst->getIConst();
52 return true;
53 }
54
55
56 // extract strings out of attribute arguments stored in attribute aggregate.
57 // convert to lower case if converToLower is true (for case-insensitive compare convenience)
getString(TString & value,int argNum,bool convertToLower) const58 bool TAttributeArgs::getString(TString& value, int argNum, bool convertToLower) const
59 {
60 const TConstUnion* stringConst = getConstUnion(EbtString, argNum);
61
62 if (stringConst == nullptr)
63 return false;
64
65 value = *stringConst->getSConst();
66
67 // Convenience.
68 if (convertToLower)
69 std::transform(value.begin(), value.end(), value.begin(), ::tolower);
70
71 return true;
72 }
73
74 // How many arguments were supplied?
size() const75 int TAttributeArgs::size() const
76 {
77 return args == nullptr ? 0 : (int)args->getSequence().size();
78 }
79
80 // Helper to get attribute const union. Returns nullptr on failure.
getConstUnion(TBasicType basicType,int argNum) const81 const TConstUnion* TAttributeArgs::getConstUnion(TBasicType basicType, int argNum) const
82 {
83 if (args == nullptr)
84 return nullptr;
85
86 if (argNum >= (int)args->getSequence().size())
87 return nullptr;
88
89 if (args->getSequence()[argNum]->getAsConstantUnion() == nullptr)
90 return nullptr;
91
92 const TConstUnion* constVal = &args->getSequence()[argNum]->getAsConstantUnion()->getConstArray()[0];
93 if (constVal == nullptr || constVal->getType() != basicType)
94 return nullptr;
95
96 return constVal;
97 }
98
99 // Implementation of TParseContext parts of attributes
attributeFromName(const TString & name) const100 TAttributeType TParseContext::attributeFromName(const TString& name) const
101 {
102 if (name == "branch" || name == "dont_flatten")
103 return EatBranch;
104 else if (name == "flatten")
105 return EatFlatten;
106 else if (name == "unroll")
107 return EatUnroll;
108 else if (name == "loop" || name == "dont_unroll")
109 return EatLoop;
110 else if (name == "dependency_infinite")
111 return EatDependencyInfinite;
112 else if (name == "dependency_length")
113 return EatDependencyLength;
114 else if (name == "min_iterations")
115 return EatMinIterations;
116 else if (name == "max_iterations")
117 return EatMaxIterations;
118 else if (name == "iteration_multiple")
119 return EatIterationMultiple;
120 else if (name == "peel_count")
121 return EatPeelCount;
122 else if (name == "partial_count")
123 return EatPartialCount;
124 else if (name == "subgroup_uniform_control_flow")
125 return EatSubgroupUniformControlFlow;
126 else if (name == "export")
127 return EatExport;
128 else if (name == "maximally_reconverges")
129 return EatMaximallyReconverges;
130 else
131 return EatNone;
132 }
133
134 // Make an initial leaf for the grammar from a no-argument attribute
makeAttributes(const TString & identifier) const135 TAttributes* TParseContext::makeAttributes(const TString& identifier) const
136 {
137 TAttributes *attributes = nullptr;
138 attributes = NewPoolObject(attributes);
139 TAttributeArgs args = { attributeFromName(identifier), nullptr };
140 attributes->push_back(args);
141 return attributes;
142 }
143
144 // Make an initial leaf for the grammar from a one-argument attribute
makeAttributes(const TString & identifier,TIntermNode * node) const145 TAttributes* TParseContext::makeAttributes(const TString& identifier, TIntermNode* node) const
146 {
147 TAttributes *attributes = nullptr;
148 attributes = NewPoolObject(attributes);
149
150 // for now, node is always a simple single expression, but other code expects
151 // a list, so make it so
152 TIntermAggregate* agg = intermediate.makeAggregate(node);
153 TAttributeArgs args = { attributeFromName(identifier), agg };
154 attributes->push_back(args);
155 return attributes;
156 }
157
158 // Merge two sets of attributes into a single set.
159 // The second argument is destructively consumed.
mergeAttributes(TAttributes * attr1,TAttributes * attr2) const160 TAttributes* TParseContext::mergeAttributes(TAttributes* attr1, TAttributes* attr2) const
161 {
162 attr1->splice(attr1->end(), *attr2);
163 return attr1;
164 }
165
166 //
167 // Selection attributes
168 //
handleSelectionAttributes(const TAttributes & attributes,TIntermNode * node)169 void TParseContext::handleSelectionAttributes(const TAttributes& attributes, TIntermNode* node)
170 {
171 TIntermSelection* selection = node->getAsSelectionNode();
172 if (selection == nullptr)
173 return;
174
175 for (auto it = attributes.begin(); it != attributes.end(); ++it) {
176 if (it->size() > 0) {
177 warn(node->getLoc(), "attribute with arguments not recognized, skipping", "", "");
178 continue;
179 }
180
181 switch (it->name) {
182 case EatFlatten:
183 selection->setFlatten();
184 break;
185 case EatBranch:
186 selection->setDontFlatten();
187 break;
188 default:
189 warn(node->getLoc(), "attribute does not apply to a selection", "", "");
190 break;
191 }
192 }
193 }
194
195 //
196 // Switch attributes
197 //
handleSwitchAttributes(const TAttributes & attributes,TIntermNode * node)198 void TParseContext::handleSwitchAttributes(const TAttributes& attributes, TIntermNode* node)
199 {
200 TIntermSwitch* selection = node->getAsSwitchNode();
201 if (selection == nullptr)
202 return;
203
204 for (auto it = attributes.begin(); it != attributes.end(); ++it) {
205 if (it->size() > 0) {
206 warn(node->getLoc(), "attribute with arguments not recognized, skipping", "", "");
207 continue;
208 }
209
210 switch (it->name) {
211 case EatFlatten:
212 selection->setFlatten();
213 break;
214 case EatBranch:
215 selection->setDontFlatten();
216 break;
217 default:
218 warn(node->getLoc(), "attribute does not apply to a switch", "", "");
219 break;
220 }
221 }
222 }
223
224 //
225 // Loop attributes
226 //
handleLoopAttributes(const TAttributes & attributes,TIntermNode * node)227 void TParseContext::handleLoopAttributes(const TAttributes& attributes, TIntermNode* node)
228 {
229 TIntermLoop* loop = node->getAsLoopNode();
230 if (loop == nullptr) {
231 // the actual loop might be part of a sequence
232 TIntermAggregate* agg = node->getAsAggregate();
233 if (agg == nullptr)
234 return;
235 for (auto it = agg->getSequence().begin(); it != agg->getSequence().end(); ++it) {
236 loop = (*it)->getAsLoopNode();
237 if (loop != nullptr)
238 break;
239 }
240 if (loop == nullptr)
241 return;
242 }
243
244 for (auto it = attributes.begin(); it != attributes.end(); ++it) {
245
246 const auto noArgument = [&](const char* feature) {
247 if (it->size() > 0) {
248 warn(node->getLoc(), "expected no arguments", feature, "");
249 return false;
250 }
251 return true;
252 };
253
254 const auto positiveSignedArgument = [&](const char* feature, int& value) {
255 if (it->size() == 1 && it->getInt(value)) {
256 if (value <= 0) {
257 error(node->getLoc(), "must be positive", feature, "");
258 return false;
259 }
260 } else {
261 warn(node->getLoc(), "expected a single integer argument", feature, "");
262 return false;
263 }
264 return true;
265 };
266
267 const auto unsignedArgument = [&](const char* feature, unsigned int& uiValue) {
268 int value;
269 if (!(it->size() == 1 && it->getInt(value))) {
270 warn(node->getLoc(), "expected a single integer argument", feature, "");
271 return false;
272 }
273 uiValue = (unsigned int)value;
274 return true;
275 };
276
277 const auto positiveUnsignedArgument = [&](const char* feature, unsigned int& uiValue) {
278 int value;
279 if (it->size() == 1 && it->getInt(value)) {
280 if (value == 0) {
281 error(node->getLoc(), "must be greater than or equal to 1", feature, "");
282 return false;
283 }
284 } else {
285 warn(node->getLoc(), "expected a single integer argument", feature, "");
286 return false;
287 }
288 uiValue = (unsigned int)value;
289 return true;
290 };
291
292 const auto spirv14 = [&](const char* feature) {
293 if (spvVersion.spv > 0 && spvVersion.spv < EShTargetSpv_1_4)
294 warn(node->getLoc(), "attribute requires a SPIR-V 1.4 target-env", feature, "");
295 };
296
297 int value = 0;
298 unsigned uiValue = 0;
299 switch (it->name) {
300 case EatUnroll:
301 if (noArgument("unroll"))
302 loop->setUnroll();
303 break;
304 case EatLoop:
305 if (noArgument("dont_unroll"))
306 loop->setDontUnroll();
307 break;
308 case EatDependencyInfinite:
309 if (noArgument("dependency_infinite"))
310 loop->setLoopDependency(TIntermLoop::dependencyInfinite);
311 break;
312 case EatDependencyLength:
313 if (positiveSignedArgument("dependency_length", value))
314 loop->setLoopDependency(value);
315 break;
316 case EatMinIterations:
317 spirv14("min_iterations");
318 if (unsignedArgument("min_iterations", uiValue))
319 loop->setMinIterations(uiValue);
320 break;
321 case EatMaxIterations:
322 spirv14("max_iterations");
323 if (unsignedArgument("max_iterations", uiValue))
324 loop->setMaxIterations(uiValue);
325 break;
326 case EatIterationMultiple:
327 spirv14("iteration_multiple");
328 if (positiveUnsignedArgument("iteration_multiple", uiValue))
329 loop->setIterationMultiple(uiValue);
330 break;
331 case EatPeelCount:
332 spirv14("peel_count");
333 if (unsignedArgument("peel_count", uiValue))
334 loop->setPeelCount(uiValue);
335 break;
336 case EatPartialCount:
337 spirv14("partial_count");
338 if (unsignedArgument("partial_count", uiValue))
339 loop->setPartialCount(uiValue);
340 break;
341 default:
342 warn(node->getLoc(), "attribute does not apply to a loop", "", "");
343 break;
344 }
345 }
346 }
347
348
349 //
350 // Function attributes
351 //
handleFunctionAttributes(const TSourceLoc & loc,const TAttributes & attributes)352 void TParseContext::handleFunctionAttributes(const TSourceLoc& loc, const TAttributes& attributes)
353 {
354 for (auto it = attributes.begin(); it != attributes.end(); ++it) {
355 if (it->size() > 0) {
356 warn(loc, "attribute with arguments not recognized, skipping", "", "");
357 continue;
358 }
359
360 switch (it->name) {
361 case EatSubgroupUniformControlFlow:
362 requireExtensions(loc, 1, &E_GL_EXT_subgroup_uniform_control_flow, "attribute");
363 intermediate.setSubgroupUniformControlFlow();
364 break;
365 case EatMaximallyReconverges:
366 requireExtensions(loc, 1, &E_GL_EXT_maximal_reconvergence, "attribute");
367 intermediate.setMaximallyReconverges();
368 break;
369 default:
370 warn(loc, "attribute does not apply to a function", "", "");
371 break;
372 }
373 }
374 }
375
376 } // end namespace glslang
377