Coverage Report

Created: 2024-05-20 07:14

/src/skia/src/sksl/transform/SkSLHoistSwitchVarDeclarationsAtTopLevel.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright 2023 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/private/base/SkAssert.h"
9
#include "include/private/base/SkTArray.h"
10
#include "src/sksl/SkSLAnalysis.h"
11
#include "src/sksl/SkSLDefines.h"
12
#include "src/sksl/SkSLPosition.h"
13
#include "src/sksl/ir/SkSLBlock.h"
14
#include "src/sksl/ir/SkSLExpression.h"
15
#include "src/sksl/ir/SkSLIRHelpers.h"
16
#include "src/sksl/ir/SkSLIRNode.h"
17
#include "src/sksl/ir/SkSLModifierFlags.h"
18
#include "src/sksl/ir/SkSLNop.h"
19
#include "src/sksl/ir/SkSLStatement.h"
20
#include "src/sksl/ir/SkSLSwitchStatement.h"
21
#include "src/sksl/ir/SkSLSymbolTable.h"
22
#include "src/sksl/ir/SkSLVarDeclarations.h"
23
#include "src/sksl/ir/SkSLVariable.h"
24
#include "src/sksl/transform/SkSLProgramWriter.h"
25
#include "src/sksl/transform/SkSLTransform.h"
26
27
#include <memory>
28
#include <utility>
29
30
using namespace skia_private;
31
32
namespace SkSL {
33
34
class Context;
35
36
std::unique_ptr<Statement> Transform::HoistSwitchVarDeclarationsAtTopLevel(
37
        const Context& context,
38
16
        std::unique_ptr<SwitchStatement> stmt) {
39
16
    struct HoistSwitchVarDeclsVisitor : public ProgramWriter {
40
16
        HoistSwitchVarDeclsVisitor(const Context& c) : fContext(c) {}
41
42
16
        bool visitExpressionPtr(std::unique_ptr<Expression>& expr) override {
43
            // We don't need to recurse into expressions.
44
0
            return false;
45
0
        }
46
47
416
        bool visitStatementPtr(std::unique_ptr<Statement>& stmt) override {
48
416
            switch (stmt->kind()) {
49
128
                case StatementKind::kSwitchCase:
50
                    // Recurse inward from the switch and its inner switch-cases.
51
128
                    return INHERITED::visitStatementPtr(stmt);
52
53
80
                case StatementKind::kBlock:
54
80
                    if (!stmt->as<Block>().isScope()) {
55
                        // Recurse inward from unscoped blocks.
56
80
                        return INHERITED::visitStatementPtr(stmt);
57
80
                    }
58
0
                    break;
59
60
0
                case StatementKind::kVarDeclaration:
61
                    // Keep track of variable declarations.
62
0
                    fVarDeclarations.push_back(&stmt);
63
0
                    break;
64
65
208
                default:
66
208
                    break;
67
416
            }
68
69
            // We don't need to recurse into other statement types; we're only interested in the top
70
            // level of the switch statement.
71
208
            return false;
72
416
        }
73
74
16
        const Context& fContext;
75
16
        TArray<std::unique_ptr<Statement>*> fVarDeclarations;
76
77
16
        using INHERITED = ProgramWriter;
78
16
    };
79
80
    // Visit every switch-case in the switch, looking for hoistable var-declarations.
81
16
    HoistSwitchVarDeclsVisitor visitor(context);
82
128
    for (std::unique_ptr<Statement>& sc : stmt->as<SwitchStatement>().cases()) {
83
128
        visitor.visitStatementPtr(sc);
84
128
    }
85
86
    // If no declarations were found, return the switch as-is.
87
16
    if (visitor.fVarDeclarations.empty()) {
88
16
        return stmt;
89
16
    }
90
91
    // Move all of the var-declaration statements into a separate block.
92
0
    SymbolTable* switchSymbols = stmt->caseBlock()->as<Block>().symbolTable();
93
0
    std::unique_ptr<SymbolTable> blockSymbols = switchSymbols->insertNewParent();
94
95
0
    StatementArray blockStmts;
96
0
    blockStmts.reserve_exact(visitor.fVarDeclarations.size() + 1);
97
0
    for (std::unique_ptr<Statement>* innerDeclaration : visitor.fVarDeclarations) {
98
0
        VarDeclaration& decl = (*innerDeclaration)->as<VarDeclaration>();
99
0
        Variable* var = decl.var();
100
0
        bool isConst = var->modifierFlags().isConst();
101
102
0
        std::unique_ptr<Statement> replacementStmt;
103
0
        if (decl.value() && !isConst) {
104
            // The inner variable-declaration has an initial-value; we must replace the declaration
105
            // with an assignment to the variable. This also has the helpful effect of stripping off
106
            // the initial-value from the declaration.
107
0
            struct AssignmentHelper : public IRHelpers {
108
0
                using IRHelpers::IRHelpers;
109
110
0
                std::unique_ptr<Statement> makeAssignmentStmt(VarDeclaration& decl) const {
111
0
                    return Assign(Ref(decl.var()), std::move(decl.value()));
112
0
                }
113
0
            };
114
115
0
            AssignmentHelper helper(context);
116
0
            replacementStmt = helper.makeAssignmentStmt(decl);
117
0
        } else {
118
            // The inner variable-declaration has no initial-value, or it's const and has a constant
119
            // value; we can move it upwards as-is and replace its statement with a no-op.
120
0
            SkASSERT(!isConst || Analysis::IsConstantExpression(*decl.value()));
121
122
0
            replacementStmt = Nop::Make();
123
0
        }
124
125
        // Move the var-declaration above the switch, and replace the existing statement with either
126
        // an assignment (if there was an initial-value) or a no-op (if there wasn't one).
127
0
        blockStmts.push_back(std::move(*innerDeclaration));
128
0
        *innerDeclaration = std::move(replacementStmt);
129
130
        // Hoist the variable's symbol outside of the switch's symbol table, and into the enclosing
131
        // block's symbol table.
132
0
        switchSymbols->moveSymbolTo(blockSymbols.get(), var, context);
133
0
    }
134
135
    // Return a scoped Block holding the switch.
136
0
    Position pos = stmt->fPosition;
137
0
    blockStmts.push_back(std::move(stmt));
138
0
    return Block::MakeBlock(pos, std::move(blockStmts), Block::Kind::kBracedScope,
139
0
                            std::move(blockSymbols));
140
16
}
141
142
}  // namespace SkSL