summaryrefslogtreecommitdiffstats
path: root/src/shader_recompiler/ir_opt/verification_pass.cpp
blob: 207355ecce3805b0752f8abb30a91de458d65993 (plain) (blame)
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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.

#include <map>
#include <set>

#include "shader_recompiler/exception.h"
#include "shader_recompiler/frontend/ir/basic_block.h"
#include "shader_recompiler/frontend/ir/value.h"
#include "shader_recompiler/ir_opt/passes.h"

namespace Shader::Optimization {

static void ValidateTypes(const IR::Program& program) {
    for (const auto& block : program.blocks) {
        for (const IR::Inst& inst : *block) {
            if (inst.GetOpcode() == IR::Opcode::Phi) {
                // Skip validation on phi nodes
                continue;
            }
            const size_t num_args{inst.NumArgs()};
            for (size_t i = 0; i < num_args; ++i) {
                const IR::Type t1{inst.Arg(i).Type()};
                const IR::Type t2{IR::ArgTypeOf(inst.GetOpcode(), i)};
                if (!IR::AreTypesCompatible(t1, t2)) {
                    throw LogicError("Invalid types in block:\n{}", IR::DumpBlock(*block));
                }
            }
        }
    }
}

static void ValidateUses(const IR::Program& program) {
    std::map<IR::Inst*, int> actual_uses;
    for (const auto& block : program.blocks) {
        for (const IR::Inst& inst : *block) {
            const size_t num_args{inst.NumArgs()};
            for (size_t i = 0; i < num_args; ++i) {
                const IR::Value arg{inst.Arg(i)};
                if (!arg.IsImmediate()) {
                    ++actual_uses[arg.Inst()];
                }
            }
        }
    }
    for (const auto [inst, uses] : actual_uses) {
        if (inst->UseCount() != uses) {
            throw LogicError("Invalid uses in block: {}", IR::DumpProgram(program));
        }
    }
}

static void ValidateForwardDeclarations(const IR::Program& program) {
    std::set<const IR::Inst*> definitions;
    for (const IR::Block* const block : program.blocks) {
        for (const IR::Inst& inst : *block) {
            definitions.emplace(&inst);
            if (inst.GetOpcode() == IR::Opcode::Phi) {
                // Phi nodes can have forward declarations
                continue;
            }
            const size_t num_args{inst.NumArgs()};
            for (size_t arg = 0; arg < num_args; ++arg) {
                if (inst.Arg(arg).IsImmediate()) {
                    continue;
                }
                if (!definitions.contains(inst.Arg(arg).Inst())) {
                    fmt::print("{}\n", IR::DumpBlock(*block));
                    throw LogicError("Forward declaration in block: {}", IR::DumpBlock(*block));
                }
            }
        }
    }
}

static void ValidatePhiNodes(const IR::Program& program) {
    for (const IR::Block* const block : program.blocks) {
        bool no_more_phis{false};
        for (const IR::Inst& inst : *block) {
            if (inst.GetOpcode() == IR::Opcode::Phi) {
                if (no_more_phis) {
                    fmt::print("{}\n", IR::DumpBlock(*block));
                    throw LogicError("Interleaved phi nodes: {}", IR::DumpBlock(*block));
                }
            } else {
                no_more_phis = true;
            }
        }
    }
}

void VerificationPass(const IR::Program& program) {
    ValidateTypes(program);
    ValidateUses(program);
    ValidateForwardDeclarations(program);
    ValidatePhiNodes(program);
}

} // namespace Shader::Optimization