Coverage Report

Created: 2025-08-29 07:31

/src/shaderc/third_party/glslang/glslang/MachineIndependent/LiveTraverser.h
Line
Count
Source (jump to first uncovered line)
1
//
2
// Copyright (C) 2016 LunarG, Inc.
3
//
4
// All rights reserved.
5
//
6
// Redistribution and use in source and binary forms, with or without
7
// modification, are permitted provided that the following conditions
8
// are met:
9
//
10
//    Redistributions of source code must retain the above copyright
11
//    notice, this list of conditions and the following disclaimer.
12
//
13
//    Redistributions in binary form must reproduce the above
14
//    copyright notice, this list of conditions and the following
15
//    disclaimer in the documentation and/or other materials provided
16
//    with the distribution.
17
//
18
//    Neither the name of 3Dlabs Inc. Ltd. nor the names of its
19
//    contributors may be used to endorse or promote products derived
20
//    from this software without specific prior written permission.
21
//
22
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26
// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
30
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33
// POSSIBILITY OF SUCH DAMAGE.
34
//
35
36
#pragma once
37
38
#include "../Include/Common.h"
39
#include "reflection.h"
40
#include "localintermediate.h"
41
42
#include "gl_types.h"
43
44
#include <list>
45
#include <unordered_set>
46
47
namespace glslang {
48
49
//
50
// The traverser: mostly pass through, except
51
//  - processing function-call nodes to push live functions onto the stack of functions to process
52
//  - processing selection nodes to trim semantically dead code
53
//
54
// This is in the glslang namespace directly so it can be a friend of TReflection.
55
// This can be derived from to implement reflection database traversers or
56
// binding mappers: anything that wants to traverse the live subset of the tree.
57
//
58
59
class TLiveTraverser : public TIntermTraverser {
60
public:
61
    TLiveTraverser(const TIntermediate& i, bool traverseAll = false,
62
                   bool preVisit = true, bool inVisit = false, bool postVisit = false) :
63
1.14k
        TIntermTraverser(preVisit, inVisit, postVisit),
64
1.14k
        intermediate(i), traverseAll(traverseAll)
65
1.14k
    { }
66
67
    //
68
    // Given a function name, find its subroot in the tree, and push it onto the stack of
69
    // functions left to process.
70
    //
71
    void pushFunction(const TString& name)
72
514
    {
73
514
        TIntermSequence& globals = intermediate.getTreeRoot()->getAsAggregate()->getSequence();
74
796
        for (unsigned int f = 0; f < globals.size(); ++f) {
75
796
            TIntermAggregate* candidate = globals[f]->getAsAggregate();
76
796
            if (candidate && candidate->getOp() == EOpFunction && candidate->getName() == name) {
77
514
                destinations.push_back(candidate);
78
514
                break;
79
514
            }
80
796
        }
81
514
    }
82
83
    void pushGlobalReference(const TString& name)
84
438
    {
85
438
        TIntermSequence& globals = intermediate.getTreeRoot()->getAsAggregate()->getSequence();
86
1.37k
        for (unsigned int f = 0; f < globals.size(); ++f) {
87
978
            TIntermAggregate* candidate = globals[f]->getAsAggregate();
88
978
            if (candidate && candidate->getOp() == EOpSequence &&
89
978
                candidate->getSequence().size() == 1 &&
90
978
                candidate->getSequence()[0]->getAsBinaryNode()) {
91
94
                TIntermBinary* binary = candidate->getSequence()[0]->getAsBinaryNode();
92
94
                TIntermSymbol* symbol = binary->getLeft()->getAsSymbolNode();
93
94
                if (symbol && symbol->getQualifier().storage == EvqGlobal &&
94
94
                    symbol->getName() == name) {
95
38
                    destinations.push_back(candidate);
96
38
                    break;
97
38
                }
98
94
            }
99
978
        }
100
438
    }
101
102
    typedef std::list<TIntermAggregate*> TDestinationStack;
103
    TDestinationStack destinations;
104
105
protected:
106
    // To catch which function calls are not dead, and hence which functions must be visited.
107
    virtual bool visitAggregate(TVisit, TIntermAggregate* node)
108
34.4k
    {
109
34.4k
        if (!traverseAll)
110
9.82k
            if (node->getOp() == EOpFunctionCall)
111
296
                addFunctionCall(node);
112
113
34.4k
        return true; // traverse this subtree
114
34.4k
    }
115
116
    // To prune semantically dead paths.
117
    virtual bool visitSelection(TVisit /* visit */,  TIntermSelection* node)
118
552
    {
119
552
        if (traverseAll)
120
372
            return true; // traverse all code
121
122
180
        TIntermConstantUnion* constant = node->getCondition()->getAsConstantUnion();
123
180
        if (constant) {
124
            // cull the path that is dead
125
48
            if (constant->getConstArray()[0].getBConst() == true && node->getTrueBlock())
126
14
                node->getTrueBlock()->traverse(this);
127
48
            if (constant->getConstArray()[0].getBConst() == false && node->getFalseBlock())
128
2
                node->getFalseBlock()->traverse(this);
129
130
48
            return false; // don't traverse any more, we did it all above
131
48
        } else
132
132
            return true; // traverse the whole subtree
133
180
    }
134
135
    // To prune semantically dead paths in switch statements with constant expressions.
136
    virtual bool visitSwitch(TVisit /* visit */, TIntermSwitch* node)
137
72
    {
138
72
        if (traverseAll)
139
48
            return true; // traverse all code
140
141
24
        TIntermConstantUnion* constant = node->getCondition()->getAsConstantUnion();
142
24
        if (constant) {
143
14
            TConstUnion switchValue = constant->getConstArray()[0];
144
14
            int liveBranch = -1;
145
14
            const auto& body = node->getBody()->getSequence();
146
14
            for (unsigned int i = 0; i < body.size(); ++i) {
147
14
                if (body[i]->getAsBranchNode()) {
148
14
                    if (body[i]->getAsBranchNode()->getFlowOp() == glslang::EOpCase) {
149
14
                        TConstUnion caseValue =
150
14
                            body[i]->getAsBranchNode()->getExpression()->getAsConstantUnion()->getConstArray()[0];
151
14
                        if (switchValue == caseValue.getIConst()) {
152
14
                            liveBranch = (int)i;
153
14
                            break;
154
14
                        }
155
14
                    } else if (body[i]->getAsBranchNode()->getFlowOp() == glslang::EOpDefault) {
156
0
                        liveBranch = (int)i;
157
0
                    }
158
14
                }
159
14
            }
160
14
            if (liveBranch != -1) {
161
32
                for (int i = liveBranch; i < (int)body.size(); ++i) {
162
32
                    if (body[i]->getAsAggregate()) {
163
18
                        for (auto* inst : body[i]->getAsAggregate()->getSequence()) {
164
18
                            if (inst->getAsBranchNode() && (inst->getAsBranchNode()->getFlowOp() == glslang::EOpBreak))
165
14
                                return false; // found and traversed the live case(s)
166
4
                            inst->traverse(this);
167
4
                        }
168
16
                    }
169
32
                }
170
14
            }
171
0
            return false; // finished traversing all cases
172
14
        } else
173
10
            return true; // traverse the whole subtree
174
24
    }
175
176
    // Track live functions as well as uniforms, so that we don't visit dead functions
177
    // and only visit each function once.
178
    void addFunctionCall(TIntermAggregate* call)
179
296
    {
180
        // just use the map to ensure we process each function at most once
181
296
        if (liveFunctions.find(call->getName()) == liveFunctions.end()) {
182
134
            liveFunctions.insert(call->getName());
183
134
            pushFunction(call->getName());
184
134
        }
185
296
    }
186
187
    void addGlobalReference(const TString& name)
188
1.24k
    {
189
        // just use the map to ensure we process each global at most once
190
1.24k
        if (liveGlobals.find(name) == liveGlobals.end()) {
191
438
            liveGlobals.insert(name);
192
438
            pushGlobalReference(name);
193
438
        }
194
1.24k
    }
195
196
    const TIntermediate& intermediate;
197
    typedef std::unordered_set<TString> TLiveFunctions;
198
    TLiveFunctions liveFunctions;
199
    typedef std::unordered_set<TString> TLiveGlobals;
200
    TLiveGlobals liveGlobals;
201
    bool traverseAll;
202
203
private:
204
    // prevent copy & copy construct
205
    TLiveTraverser(TLiveTraverser&);
206
    TLiveTraverser& operator=(TLiveTraverser&);
207
};
208
209
} // namespace glslang