Coverage Report

Created: 2025-11-09 06:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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