summaryrefslogtreecommitdiffstats
path: root/src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp
blob: 6c7c7b32dbcb4ccd96fad8b1400720894b19bd46 (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
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.

#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 {
namespace {
template <bool TEST_USES>
void DeadInstElimination(IR::Block* const block) {
    // We iterate over the instructions in reverse order.
    // This is because removing an instruction reduces the number of uses for earlier instructions.
    auto it{block->end()};
    while (it != block->begin()) {
        --it;
        if constexpr (TEST_USES) {
            if (it->HasUses() || it->MayHaveSideEffects()) {
                continue;
            }
        }
        it->Invalidate();
        it = block->Instructions().erase(it);
    }
}

void DeadBranchElimination(IR::Program& program) {
    const auto begin_it{program.syntax_list.begin()};
    for (auto node_it = begin_it; node_it != program.syntax_list.end(); ++node_it) {
        if (node_it->type != IR::AbstractSyntaxNode::Type::If) {
            continue;
        }
        IR::Inst* const cond_ref{node_it->data.if_node.cond.Inst()};
        const IR::U1 cond{cond_ref->Arg(0)};
        if (!cond.IsImmediate()) {
            continue;
        }
        if (cond.U1()) {
            continue;
        }
        // False immediate condition. Remove condition ref, erase the entire branch.
        cond_ref->Invalidate();
        // Account for nested if-statements within the if(false) branch
        u32 nested_ifs{1u};
        while (node_it->type != IR::AbstractSyntaxNode::Type::EndIf || nested_ifs > 0) {
            node_it = program.syntax_list.erase(node_it);
            switch (node_it->type) {
            case IR::AbstractSyntaxNode::Type::If:
                ++nested_ifs;
                break;
            case IR::AbstractSyntaxNode::Type::EndIf:
                --nested_ifs;
                break;
            case IR::AbstractSyntaxNode::Type::Block: {
                IR::Block* const block{node_it->data.block};
                DeadInstElimination<false>(block);
                break;
            }
            default:
                break;
            }
        }
        // Erase EndIf node of the if(false) branch
        node_it = program.syntax_list.erase(node_it);
        // Account for loop increment
        --node_it;
    }
}
} // namespace

void DeadCodeEliminationPass(IR::Program& program) {
    DeadBranchElimination(program);
    for (IR::Block* const block : program.post_order_blocks) {
        DeadInstElimination<true>(block);
    }
}

} // namespace Shader::Optimization