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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
use crate::{
    llir_nodes::{Branch, FastStore, FastStoreFromResult, Node, VariableAccess},
    minecraft_utils::ScoreboardValue,
    opt::{
        global_opt::{Commands, Optimizer},
        optimize_commands::{OptimizeCommand, OptimizeCommandDeque, OptimizeCommandKind},
        variable_metadata::{Hint, ValueHints},
        NodeId,
    },
};

/// Optimizes nodes which are const-evaluatable.
/// This optimizer tracks all const assignments to variables in
/// a given function and replaces reads from const variables by their
/// const value. Also contains functionality to evaluate [`BinaryOperation`](crate::llir_nodes::BinaryOperation)
/// and [`Condition`](crate::llir_nodes::Condition).
/// In order to be more efficient, this optimizer optimizes an entire function.
/// This means that the current state must always be synced correctly!
#[derive(Default)]
pub struct ConstOptimizer {
    value_hints: ValueHints,
}

impl Optimizer for ConstOptimizer {
    fn optimize(&mut self, commands: &mut Commands) {
        for function_iter in commands.optimizer.iter_functions() {
            self.value_hints.clear_all();
            for (node_id, node) in function_iter {
                let could_optimize = self.optimize_node(commands.commands, node_id, node);
                self.value_hints.update_hints(node, true);
                if could_optimize {
                    continue;
                }

                let commands_vec = &mut *commands.commands;
                let variable_information = &commands.stats.variable_information;
                node.variable_accesses(&mut |access| {
                    if let VariableAccess::Read(ScoreboardValue::Scoreboard(id)) = access {
                        let hint = self.value_hints.get_hint(*id).exact();
                        let global_const = variable_information[id].constant_value;
                        if let Some(exact_value) = hint.or(global_const) {
                            commands_vec.push(OptimizeCommand::new(
                                node_id,
                                OptimizeCommandKind::ChangeReads(
                                    *id,
                                    ScoreboardValue::Static(exact_value),
                                ),
                            ));
                            self.value_hints.update_hints(node, true);
                        }
                    }
                });
            }
        }
    }
}

impl ConstOptimizer {
    pub fn optimize_node(
        &mut self,
        commands: &mut OptimizeCommandDeque<OptimizeCommand>,
        node_id: NodeId,
        node: &Node,
    ) -> bool {
        match node {
            Node::BinaryOperation(bin_op) => {
                if let Some(result) = self.value_hints.static_binary_operation(bin_op) {
                    commands.push(OptimizeCommand::new(
                        node_id,
                        OptimizeCommandKind::Replace(Node::FastStore(FastStore {
                            id: bin_op.id,
                            value: ScoreboardValue::Static(result),
                        })),
                    ));
                    self.value_hints.set_hint(bin_op.id, Hint::Exact(result));
                    return true;
                }
            }
            Node::FastStoreFromResult(FastStoreFromResult { id, command }) => {
                if let Node::Condition(condition) = &**command {
                    if let Some(result) = self.value_hints.static_condition(condition) {
                        commands.push(OptimizeCommand::new(
                            node_id,
                            OptimizeCommandKind::Replace(Node::FastStore(FastStore {
                                id: *id,
                                value: ScoreboardValue::Static(i32::from(result)),
                            })),
                        ));
                        self.value_hints
                            .set_hint(*id, Hint::Exact(i32::from(result)));
                        return true;
                    }
                }
            }
            Node::Branch(Branch {
                condition,
                pos_branch,
                neg_branch,
            }) => {
                if let Some(result) = self.value_hints.static_condition(condition) {
                    commands.push(OptimizeCommand::new(
                        node_id,
                        OptimizeCommandKind::InlineBranch(result),
                    ));
                    let branch = if result { pos_branch } else { neg_branch };
                    self.value_hints.update_hints(branch, true);
                    return true;
                } else if let Some(simplified_condition) =
                    self.value_hints.simplify_condition(condition)
                {
                    commands.push(OptimizeCommand::new(
                        node_id,
                        OptimizeCommandKind::UpdateBranchCondition(simplified_condition),
                    ));
                    return true;
                }
            }
            _ => {}
        }

        false
    }
}