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