Coverage Report

Created: 2024-05-20 07:14

/src/skia/src/sksl/analysis/SkSLProgramUsage.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright 2021 Google LLC
3
 *
4
 * Use of this source code is governed by a BSD-style license that can be
5
 * found in the LICENSE file.
6
 */
7
8
#include "include/core/SkSpan.h"
9
#include "include/core/SkTypes.h"
10
#include "include/private/base/SkDebug.h"
11
#include "src/base/SkEnumBitMask.h"
12
#include "src/core/SkTHash.h"
13
#include "src/sksl/SkSLAnalysis.h"
14
#include "src/sksl/SkSLCompiler.h"
15
#include "src/sksl/analysis/SkSLProgramUsage.h"
16
#include "src/sksl/analysis/SkSLProgramVisitor.h"
17
#include "src/sksl/ir/SkSLExpression.h"
18
#include "src/sksl/ir/SkSLFunctionCall.h"
19
#include "src/sksl/ir/SkSLFunctionDeclaration.h"
20
#include "src/sksl/ir/SkSLFunctionDefinition.h"
21
#include "src/sksl/ir/SkSLInterfaceBlock.h"
22
#include "src/sksl/ir/SkSLModifierFlags.h"
23
#include "src/sksl/ir/SkSLProgramElement.h"
24
#include "src/sksl/ir/SkSLStatement.h"
25
#include "src/sksl/ir/SkSLStructDefinition.h"
26
#include "src/sksl/ir/SkSLSymbol.h"
27
#include "src/sksl/ir/SkSLType.h"
28
#include "src/sksl/ir/SkSLVarDeclarations.h"
29
#include "src/sksl/ir/SkSLVariable.h"
30
#include "src/sksl/ir/SkSLVariableReference.h"
31
32
#include <cstring>
33
#include <memory>
34
#include <string_view>
35
#include <vector>
36
37
namespace SkSL {
38
39
struct Program;
40
41
namespace {
42
43
class ProgramUsageVisitor : public ProgramVisitor {
44
public:
45
1.34k
    ProgramUsageVisitor(ProgramUsage* usage, int delta) : fUsage(usage), fDelta(delta) {}
46
47
3.77k
    bool visitProgramElement(const ProgramElement& pe) override {
48
3.77k
        if (pe.is<FunctionDefinition>()) {
49
983
            for (const Variable* param : pe.as<FunctionDefinition>().declaration().parameters()) {
50
                // Ensure function-parameter variables exist in the variable usage map. They aren't
51
                // otherwise declared, but ProgramUsage::get() should be able to find them, even if
52
                // they are unread and unwritten.
53
637
                ProgramUsage::VariableCounts& counts = fUsage->fVariableCounts[param];
54
637
                counts.fVarExists += fDelta;
55
56
637
                this->visitType(param->type());
57
637
            }
58
2.79k
        } else if (pe.is<InterfaceBlock>()) {
59
            // Ensure interface-block variables exist in the variable usage map.
60
0
            const Variable* var = pe.as<InterfaceBlock>().var();
61
0
            fUsage->fVariableCounts[var];
62
63
0
            this->visitType(var->type());
64
2.79k
        } else if (pe.is<StructDefinition>()) {
65
            // Ensure that structs referenced as nested types in other structs are counted as used.
66
9
            this->visitStructFields(pe.as<StructDefinition>().type());
67
9
        }
68
3.77k
        return INHERITED::visitProgramElement(pe);
69
3.77k
    }
70
71
8.93k
    bool visitStatement(const Statement& s) override {
72
8.93k
        if (s.is<VarDeclaration>()) {
73
            // Add all declared variables to the usage map (even if never otherwise accessed).
74
2.80k
            const VarDeclaration& vd = s.as<VarDeclaration>();
75
2.80k
            const Variable* var = vd.var();
76
2.80k
            ProgramUsage::VariableCounts& counts = fUsage->fVariableCounts[var];
77
2.80k
            counts.fVarExists += fDelta;
78
2.80k
            SkASSERT(counts.fVarExists >= 0 && counts.fVarExists <= 1);
79
2.80k
            if (vd.value()) {
80
                // The initial-value expression, when present, counts as a write.
81
1.33k
                counts.fWrite += fDelta;
82
1.33k
            }
83
2.80k
            this->visitType(var->type());
84
2.80k
        }
85
8.93k
        return INHERITED::visitStatement(s);
86
8.93k
    }
SkSLProgramUsage.cpp:SkSL::(anonymous namespace)::ProgramUsageVisitor::visitStatement(SkSL::Statement const&)
Line
Count
Source
71
8.93k
    bool visitStatement(const Statement& s) override {
72
8.93k
        if (s.is<VarDeclaration>()) {
73
            // Add all declared variables to the usage map (even if never otherwise accessed).
74
2.80k
            const VarDeclaration& vd = s.as<VarDeclaration>();
75
2.80k
            const Variable* var = vd.var();
76
2.80k
            ProgramUsage::VariableCounts& counts = fUsage->fVariableCounts[var];
77
2.80k
            counts.fVarExists += fDelta;
78
2.80k
            SkASSERT(counts.fVarExists >= 0 && counts.fVarExists <= 1);
79
2.80k
            if (vd.value()) {
80
                // The initial-value expression, when present, counts as a write.
81
1.33k
                counts.fWrite += fDelta;
82
1.33k
            }
83
2.80k
            this->visitType(var->type());
84
2.80k
        }
85
8.93k
        return INHERITED::visitStatement(s);
86
8.93k
    }
Unexecuted instantiation: SkSLProgramUsage.cpp:SkSL::(anonymous namespace)::ProgramUsageVisitor::visitStatement(SkSL::Statement const&)
87
88
24.2k
    bool visitExpression(const Expression& e) override {
89
24.2k
        this->visitType(e.type());
90
24.2k
        if (e.is<FunctionCall>()) {
91
1.95k
            const FunctionDeclaration* f = &e.as<FunctionCall>().function();
92
1.95k
            fUsage->fCallCounts[f] += fDelta;
93
1.95k
            SkASSERT(fUsage->fCallCounts[f] >= 0);
94
22.2k
        } else if (e.is<VariableReference>()) {
95
7.74k
            const VariableReference& ref = e.as<VariableReference>();
96
7.74k
            ProgramUsage::VariableCounts& counts = fUsage->fVariableCounts[ref.variable()];
97
7.74k
            switch (ref.refKind()) {
98
6.49k
                case VariableRefKind::kRead:
99
6.49k
                    counts.fRead += fDelta;
100
6.49k
                    break;
101
1.22k
                case VariableRefKind::kWrite:
102
1.22k
                    counts.fWrite += fDelta;
103
1.22k
                    break;
104
22
                case VariableRefKind::kReadWrite:
105
22
                case VariableRefKind::kPointer:
106
22
                    counts.fRead += fDelta;
107
22
                    counts.fWrite += fDelta;
108
22
                    break;
109
7.74k
            }
110
7.74k
            SkASSERT(counts.fRead >= 0 && counts.fWrite >= 0);
111
7.74k
        }
112
24.2k
        return INHERITED::visitExpression(e);
113
24.2k
    }
SkSLProgramUsage.cpp:SkSL::(anonymous namespace)::ProgramUsageVisitor::visitExpression(SkSL::Expression const&)
Line
Count
Source
88
24.2k
    bool visitExpression(const Expression& e) override {
89
24.2k
        this->visitType(e.type());
90
24.2k
        if (e.is<FunctionCall>()) {
91
1.95k
            const FunctionDeclaration* f = &e.as<FunctionCall>().function();
92
1.95k
            fUsage->fCallCounts[f] += fDelta;
93
1.95k
            SkASSERT(fUsage->fCallCounts[f] >= 0);
94
22.2k
        } else if (e.is<VariableReference>()) {
95
7.74k
            const VariableReference& ref = e.as<VariableReference>();
96
7.74k
            ProgramUsage::VariableCounts& counts = fUsage->fVariableCounts[ref.variable()];
97
7.74k
            switch (ref.refKind()) {
98
6.49k
                case VariableRefKind::kRead:
99
6.49k
                    counts.fRead += fDelta;
100
6.49k
                    break;
101
1.22k
                case VariableRefKind::kWrite:
102
1.22k
                    counts.fWrite += fDelta;
103
1.22k
                    break;
104
22
                case VariableRefKind::kReadWrite:
105
22
                case VariableRefKind::kPointer:
106
22
                    counts.fRead += fDelta;
107
22
                    counts.fWrite += fDelta;
108
22
                    break;
109
7.74k
            }
110
7.74k
            SkASSERT(counts.fRead >= 0 && counts.fWrite >= 0);
111
7.74k
        }
112
24.2k
        return INHERITED::visitExpression(e);
113
24.2k
    }
Unexecuted instantiation: SkSLProgramUsage.cpp:SkSL::(anonymous namespace)::ProgramUsageVisitor::visitExpression(SkSL::Expression const&)
114
115
28.1k
    void visitType(const Type& t) {
116
28.1k
        if (t.isArray()) {
117
387
            this->visitType(t.componentType());
118
387
            return;
119
387
        }
120
27.7k
        if (t.isStruct()) {
121
33
            int& structCount = fUsage->fStructCounts[&t];
122
33
            structCount += fDelta;
123
33
            SkASSERT(structCount >= 0);
124
125
33
            this->visitStructFields(t);
126
33
        }
127
27.7k
    }
128
129
42
    void visitStructFields(const Type& t) {
130
91
        for (const Field& f : t.fields()) {
131
91
            this->visitType(*f.fType);
132
91
        }
133
42
    }
134
135
    using ProgramVisitor::visitProgramElement;
136
    using ProgramVisitor::visitStatement;
137
138
    ProgramUsage* fUsage;
139
    int fDelta;
140
    using INHERITED = ProgramVisitor;
141
};
142
143
}  // namespace
144
145
340
std::unique_ptr<ProgramUsage> Analysis::GetUsage(const Program& program) {
146
340
    auto usage = std::make_unique<ProgramUsage>();
147
340
    ProgramUsageVisitor addRefs(usage.get(), /*delta=*/+1);
148
340
    addRefs.visit(program);
149
340
    return usage;
150
340
}
151
152
22
std::unique_ptr<ProgramUsage> Analysis::GetUsage(const Module& module) {
153
22
    auto usage = std::make_unique<ProgramUsage>();
154
22
    ProgramUsageVisitor addRefs(usage.get(), /*delta=*/+1);
155
156
86
    for (const Module* m = &module; m != nullptr; m = m->fParent) {
157
2.13k
        for (const std::unique_ptr<ProgramElement>& element : m->fElements) {
158
2.13k
            addRefs.visitProgramElement(*element);
159
2.13k
        }
160
64
    }
161
22
    return usage;
162
22
}
163
164
1.29k
ProgramUsage::VariableCounts ProgramUsage::get(const Variable& v) const {
165
1.29k
    const VariableCounts* counts = fVariableCounts.find(&v);
166
1.29k
    SkASSERT(counts);
167
1.29k
    return *counts;
168
1.29k
}
169
170
874
bool ProgramUsage::isDead(const Variable& v) const {
171
874
    ModifierFlags flags = v.modifierFlags();
172
874
    VariableCounts counts = this->get(v);
173
874
    if (flags & (ModifierFlag::kIn | ModifierFlag::kOut | ModifierFlag::kUniform)) {
174
        // Never eliminate ins, outs, or uniforms.
175
323
        return false;
176
323
    }
177
551
    if (v.type().componentType().isOpaque()) {
178
        // Never eliminate samplers, runtime-effect children, or atomics.
179
0
        return false;
180
0
    }
181
    // Consider the variable dead if it's never read and never written (besides the initial-value).
182
551
    return !counts.fRead && (counts.fWrite <= (v.initialValue() ? 1 : 0));
183
551
}
184
185
628
int ProgramUsage::get(const FunctionDeclaration& f) const {
186
628
    const int* count = fCallCounts.find(&f);
187
628
    return count ? *count : 0;
188
628
}
189
190
76
void ProgramUsage::add(const Expression* expr) {
191
76
    ProgramUsageVisitor addRefs(this, /*delta=*/+1);
192
76
    addRefs.visitExpression(*expr);
193
76
}
194
195
76
void ProgramUsage::add(const Statement* stmt) {
196
76
    ProgramUsageVisitor addRefs(this, /*delta=*/+1);
197
76
    addRefs.visitStatement(*stmt);
198
76
}
199
200
27
void ProgramUsage::add(const ProgramElement& element) {
201
27
    ProgramUsageVisitor addRefs(this, /*delta=*/+1);
202
27
    addRefs.visitProgramElement(element);
203
27
}
204
205
76
void ProgramUsage::remove(const Expression* expr) {
206
76
    ProgramUsageVisitor subRefs(this, /*delta=*/-1);
207
76
    subRefs.visitExpression(*expr);
208
76
}
209
210
493
void ProgramUsage::remove(const Statement* stmt) {
211
493
    ProgramUsageVisitor subRefs(this, /*delta=*/-1);
212
493
    subRefs.visitStatement(*stmt);
213
493
}
214
215
230
void ProgramUsage::remove(const ProgramElement& element) {
216
230
    ProgramUsageVisitor subRefs(this, /*delta=*/-1);
217
230
    subRefs.visitProgramElement(element);
218
230
}
219
220
0
static bool contains_matching_data(const ProgramUsage& a, const ProgramUsage& b) {
221
0
    constexpr bool kReportMismatch = false;
222
223
0
    for (const auto& [varA, varCountA] : a.fVariableCounts) {
224
        // Skip variable entries with zero reported usage.
225
0
        if (!varCountA.fVarExists && !varCountA.fRead && !varCountA.fWrite) {
226
0
            continue;
227
0
        }
228
        // Find the matching variable in the other map and ensure that its counts match.
229
0
        const ProgramUsage::VariableCounts* varCountB = b.fVariableCounts.find(varA);
230
0
        if (!varCountB || 0 != memcmp(&varCountA, varCountB, sizeof(varCountA))) {
231
0
            if constexpr (kReportMismatch) {
232
0
                SkDebugf("VariableCounts mismatch: '%.*s' (E%d R%d W%d != E%d R%d W%d)\n",
233
0
                         (int)varA->name().size(), varA->name().data(),
234
0
                         varCountA.fVarExists,
235
0
                         varCountA.fRead,
236
0
                         varCountA.fWrite,
237
0
                         varCountB ? varCountB->fVarExists : 0,
238
0
                         varCountB ? varCountB->fRead : 0,
239
0
                         varCountB ? varCountB->fWrite : 0);
240
0
            }
241
0
            return false;
242
0
        }
243
0
    }
244
245
0
    for (const auto& [callA, callCountA] : a.fCallCounts) {
246
        // Skip function-call entries with zero reported usage.
247
0
        if (!callCountA) {
248
0
            continue;
249
0
        }
250
        // Find the matching function in the other map and ensure that its call-count matches.
251
0
        const int* callCountB = b.fCallCounts.find(callA);
252
0
        if (!callCountB || callCountA != *callCountB) {
253
0
            if constexpr (kReportMismatch) {
254
0
                SkDebugf("CallCounts mismatch: '%.*s' (%d != %d)\n",
255
0
                         (int)callA->name().size(), callA->name().data(),
256
0
                         callCountA,
257
0
                         callCountB ? *callCountB : 0);
258
0
            }
259
0
            return false;
260
0
        }
261
0
    }
262
263
0
    for (const auto& [structA, structCountA] : a.fStructCounts) {
264
        // Skip struct entries with zero reported usage.
265
0
        if (!structCountA) {
266
0
            continue;
267
0
        }
268
        // Find the matching struct in the other map and ensure that its usage-count matches.
269
0
        const int* structCountB = b.fStructCounts.find(structA);
270
0
        if (!structCountB || structCountA != *structCountB) {
271
0
            if constexpr (kReportMismatch) {
272
0
                SkDebugf("StructCounts mismatch: '%.*s' (%d != %d)\n",
273
0
                         (int)structA->name().size(), structA->name().data(),
274
0
                         structCountA,
275
0
                         structCountB ? *structCountB : 0);
276
0
            }
277
0
            return false;
278
0
        }
279
0
    }
280
281
    // Every non-zero entry in A has a matching non-zero entry in B.
282
0
    return true;
283
0
}
284
285
0
bool ProgramUsage::operator==(const ProgramUsage& that) const {
286
    // ProgramUsage can be "equal" while the underlying hash maps look slightly different, because a
287
    // dead-stripped variable or function will have a usage count of zero, but will still exist in
288
    // the maps. If the program usage is re-analyzed from scratch, the maps will not contain an
289
    // entry for these variables or functions at all. This means our maps can be "equal" while
290
    // having different element counts.
291
    //
292
    // In order to check these maps, we compare map entries bi-directionally, skipping zero-usage
293
    // entries. If all the non-zero elements in `this` match the elements in `that`, and all the
294
    // non-zero elements in `that` match the elements in `this`, all the non-zero elements must be
295
    // identical, and all the zero elements must be either zero or non-existent on both sides.
296
0
    return contains_matching_data(*this, that) &&
297
0
           contains_matching_data(that, *this);
298
0
}
299
300
}  // namespace SkSL