summaryrefslogtreecommitdiffstats
path: root/gfx/skia/skia/src/sksl/analysis/SkSLGetLoopControlFlowInfo.cpp
blob: 65c9e5e424006e42e3299bafa47866d50cb5fd6b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
/*
 * Copyright 2023 Google LLC
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "src/sksl/SkSLAnalysis.h"

#include "include/private/SkSLIRNode.h"
#include "include/private/SkSLStatement.h"
#include "src/sksl/analysis/SkSLProgramVisitor.h"

namespace SkSL {

class Expression;

namespace Analysis {
namespace {

class LoopControlFlowVisitor : public ProgramVisitor {
public:
    LoopControlFlowVisitor() {}

    bool visitExpression(const Expression& expr) override {
        // We can avoid processing expressions entirely.
        return false;
    }

    bool visitStatement(const Statement& stmt) override {
        switch (stmt.kind()) {
            case Statement::Kind::kContinue:
                // A continue only affects the control flow of the loop if it's not nested inside
                // another looping structure. (Inside a switch, SkSL disallows continue entirely.)
                fResult.fHasContinue |= (fDepth == 0);
                break;

            case Statement::Kind::kBreak:
                // A break only affects the control flow of the loop if it's not nested inside
                // another loop/switch structure.
                fResult.fHasBreak |= (fDepth == 0);
                break;

            case Statement::Kind::kReturn:
                // A return will abort the loop's control flow no matter how deeply it is nested.
                fResult.fHasReturn = true;
                break;

            case Statement::Kind::kFor:
            case Statement::Kind::kDo:
            case Statement::Kind::kSwitch: {
                ++fDepth;
                bool done = ProgramVisitor::visitStatement(stmt);
                --fDepth;
                return done;
            }

            default:
                return ProgramVisitor::visitStatement(stmt);
        }

        // If we've already found everything we're hunting for, we can stop searching early.
        return fResult.fHasContinue && fResult.fHasBreak && fResult.fHasReturn;
    }

    LoopControlFlowInfo fResult;
    int fDepth = 0;
};

}  // namespace

LoopControlFlowInfo GetLoopControlFlowInfo(const Statement& stmt) {
    LoopControlFlowVisitor visitor;
    visitor.visitStatement(stmt);
    return visitor.fResult;
}

}  // namespace Analysis
}  // namespace SkSL