Cesar Rodas 9 сар өмнө
parent
commit
c048a71e7b

+ 17 - 0
utxo/src/filter_expr/compiler.rs → utxo/src/filter_expr/compiler/mod.rs

@@ -12,6 +12,8 @@ pub struct Compiler<'a> {
     labels: Addr,
 }
 
+mod optimizations;
+
 impl<'a> Compiler<'a> {
     pub fn new(expr: &'a Expr) -> Self {
         Self {
@@ -148,6 +150,21 @@ impl<'a> Compiler<'a> {
             .collect::<HashMap<_, _>>()
     }
 
+    pub fn optimize(opcodes: Vec<OpCode>) -> (Vec<OpCode>, bool) {
+        vec![
+            optimizations::assign_unique_register_addresses,
+            optimizations::remove_redundant_load_and_mov,
+            optimizations::remove_unused_values,
+            optimizations::calculate_static_values,
+            optimizations::remove_useless_jumps,
+        ]
+        .into_iter()
+        .fold((opcodes, false), |(opcodes, has_optimized), f| {
+            let (opcodes, optimized) = f(opcodes);
+            (opcodes, has_optimized || optimized)
+        })
+    }
+
     pub fn resolve_label_to_addr(opcodes: Vec<OpCode>) -> Result<Vec<OpCode>, Error> {
         let labels_to_addr = Self::labels_to_addr(&opcodes);
 

+ 70 - 0
utxo/src/filter_expr/compiler/optimizations/assign_unique_register_address.rs

@@ -0,0 +1,70 @@
+use crate::filter_expr::{opcode::OpCode, Register};
+use std::collections::HashMap;
+
+/// When code is optimized, it is possible that a few registers are being LOADED and not read
+/// and it is not caught by `remove_unused_values`. This function will make sure that every
+/// LOADED and LOADED_EXTERNAL register has a unique address.
+pub fn assign_unique_register_addresses(mut opcodes: Vec<OpCode>) -> (Vec<OpCode>, bool) {
+    let mut has_changed = false;
+    let mut counter: usize = 0;
+    let mut register_new_mapping = HashMap::<_, Register>::new();
+    let mut new_mapping =
+        |register_new_mapping: &mut HashMap<Register, Register>, current: &Register| -> Register {
+            counter += 1;
+            if counter != **current {
+                has_changed = true;
+            }
+
+            register_new_mapping.insert(current.clone(), counter.into());
+            counter.into()
+        };
+
+    opcodes.iter_mut().for_each(|opcode| match opcode {
+        OpCode::LOAD(current, _) | OpCode::LOAD_EXTERNAL(current, _) => {
+            *current = new_mapping(&mut register_new_mapping, current);
+        }
+        OpCode::SHL(set, reg1, _)
+        | OpCode::SHR(set, reg1, _)
+        | OpCode::NOT(set, reg1)
+        | OpCode::CPY(set, reg1)
+        | OpCode::NEG(set, reg1)
+        | OpCode::MOV(set, reg1) => {
+            *reg1 = register_new_mapping[reg1].clone();
+
+            if let Some(new_set) = register_new_mapping.get(set) {
+                *set = new_set.clone()
+            } else {
+                *set = new_mapping(&mut register_new_mapping, set);
+            }
+        }
+        OpCode::HLT(reg1) | OpCode::JEQ(reg1, _) | OpCode::JNE(reg1, _) => {
+            *reg1 = register_new_mapping[reg1].clone();
+        }
+        OpCode::ADD(set, reg1, reg2)
+        | OpCode::MUL(set, reg1, reg2)
+        | OpCode::NE(set, reg1, reg2)
+        | OpCode::GE(set, reg1, reg2)
+        | OpCode::DIV(set, reg1, reg2)
+        | OpCode::LE(set, reg1, reg2)
+        | OpCode::GT(set, reg1, reg2)
+        | OpCode::LT(set, reg1, reg2)
+        | OpCode::DIV(set, reg1, reg2)
+        | OpCode::MOD(set, reg1, reg2)
+        | OpCode::XOR(set, reg1, reg2)
+        | OpCode::SUB(set, reg1, reg2)
+        | OpCode::AND(set, reg1, reg2)
+        | OpCode::EQ(set, reg1, reg2)
+        | OpCode::OR(set, reg1, reg2) => {
+            *reg1 = register_new_mapping[reg1].clone();
+            *reg2 = register_new_mapping[reg2].clone();
+            if let Some(new_set) = register_new_mapping.get(set) {
+                *set = new_set.clone()
+            } else {
+                *set = new_mapping(&mut register_new_mapping, set);
+            }
+        }
+        OpCode::LABEL(_) | OpCode::JMP(_) => {}
+    });
+
+    (opcodes, has_changed)
+}

+ 105 - 0
utxo/src/filter_expr/compiler/optimizations/calculate_static_values.rs

@@ -0,0 +1,105 @@
+use crate::filter_expr::{opcode::OpCode, Value};
+use std::collections::HashMap;
+
+/// Executes operations that can be performed at compile time, and sets the initial_register to
+/// the result. This is useful for optimizing the program, by executing at `compile` time as
+/// much as possible, when the terms of the opcodes are not dependent on the runtime state.
+pub fn calculate_static_values(mut opcodes: Vec<OpCode>) -> (Vec<OpCode>, bool) {
+    let mut register = HashMap::new();
+    let mut has_changed = false;
+
+    opcodes.iter_mut().for_each(|opcode| {
+        match &opcode {
+            OpCode::LOAD(dst, value) => {
+                register.insert(*dst, value.clone());
+            }
+            OpCode::JMP(_)
+            | OpCode::HLT(_)
+            | OpCode::JEQ(_, _)
+            | OpCode::JNE(_, _)
+            | OpCode::LABEL(_) => {}
+            OpCode::MOV(dst, src) => {
+                if let Some(value) = register.remove(src) {
+                    register.insert(*dst, value);
+                }
+            }
+            OpCode::LOAD_EXTERNAL(dst, _)
+            | OpCode::AND(dst, _, _)
+            | OpCode::OR(dst, _, _)
+            | OpCode::NOT(dst, _)
+            | OpCode::MOD(dst, _, _)
+            | OpCode::NE(dst, _, _)
+            | OpCode::GT(dst, _, _)
+            | OpCode::LT(dst, _, _)
+            | OpCode::NEG(dst, _)
+            | OpCode::SHL(dst, _, _)
+            | OpCode::XOR(dst, _, _)
+            | OpCode::SHR(dst, _, _)
+            | OpCode::GE(dst, _, _)
+            | OpCode::LE(dst, _, _)
+            | OpCode::CPY(dst, _) => {
+                register.remove(dst);
+            }
+            OpCode::JEQ(reg, addr) => {
+                if let Some(Value::Bool(true)) = register.get(reg) {
+                    *opcode = OpCode::JMP(*addr);
+                }
+            }
+            OpCode::JNE(reg, addr) => {
+                if let Some(Value::Bool(false)) = register.get(reg) {
+                    *opcode = OpCode::JMP(*addr);
+                }
+            }
+            OpCode::EQ(dst, reg1, reg2) => {
+                let value1 = if let Some(value) = register.get(reg1) {
+                    value.clone()
+                } else {
+                    register.remove(dst);
+                    return;
+                };
+                let value2 = if let Some(value) = register.get(reg2) {
+                    value.clone()
+                } else {
+                    register.remove(dst);
+                    return;
+                };
+                let result = value1 == value2;
+                register.insert(*dst, result.into());
+                *opcode = OpCode::LOAD(*dst, result.into());
+            }
+            OpCode::MUL(dst, reg1, reg2)
+            | OpCode::SUB(dst, reg1, reg2)
+            | OpCode::DIV(dst, reg1, reg2)
+            | OpCode::ADD(dst, reg1, reg2) => {
+                let number1 = match register.get(reg1) {
+                    Some(Value::Number(number)) => *number,
+                    _ => {
+                        register.remove(dst);
+                        return;
+                    }
+                };
+                let number2 = match register.get(reg2) {
+                    Some(Value::Number(number)) => *number,
+                    _ => {
+                        register.remove(dst);
+                        return;
+                    }
+                };
+                if let Some(calculated_value) = match opcode {
+                    OpCode::ADD(_, _, _) => number1.checked_add(number2).map(Value::Number),
+                    OpCode::MUL(_, _, _) => number1.checked_mul(number2).map(Value::Number),
+                    OpCode::SUB(_, _, _) => number1.checked_sub(number2).map(Value::Number),
+                    OpCode::DIV(_, _, _) => number1.checked_div(number2).map(Value::Number),
+                    _ => None,
+                } {
+                    register.insert(*dst, calculated_value.clone());
+                    *opcode = OpCode::LOAD(*dst, calculated_value);
+                    has_changed = true;
+                } else {
+                    register.remove(dst);
+                }
+            }
+        };
+    });
+    (opcodes, has_changed)
+}

+ 10 - 0
utxo/src/filter_expr/compiler/optimizations/mod.rs

@@ -0,0 +1,10 @@
+mod assign_unique_register_address;
+mod calculate_static_values;
+mod remove_redundant_load_and_mov;
+mod remove_unused_values;
+mod remove_useless_jumps;
+
+pub use self::{
+    assign_unique_register_address::*, calculate_static_values::*,
+    remove_redundant_load_and_mov::*, remove_unused_values::*, remove_useless_jumps::*,
+};

+ 26 - 0
utxo/src/filter_expr/compiler/optimizations/remove_redundant_load_and_mov.rs

@@ -0,0 +1,26 @@
+use crate::filter_expr::opcode::OpCode;
+
+pub fn remove_redundant_load_and_mov(mut opcodes: Vec<OpCode>) -> (Vec<OpCode>, bool) {
+    let old_total_opcodes = opcodes.len();
+    let new_opcodes = opcodes.into_iter().fold(Vec::new(), |mut acc, current| {
+        if let OpCode::MOV(target, from) = &current {
+            if let Some(last) = acc.last_mut() {
+                println!("herex: {:?} {:?}", last, current);
+                match last {
+                    OpCode::LOAD(reg, _) => {
+                        if reg == from {
+                            *reg = *target;
+                            return acc;
+                        }
+                    }
+                    _ => {}
+                }
+            }
+        }
+        acc.push(current);
+        acc
+    });
+    let new_total_opcodes = new_opcodes.len();
+
+    (new_opcodes, old_total_opcodes != new_total_opcodes)
+}

+ 72 - 0
utxo/src/filter_expr/compiler/optimizations/remove_unused_values.rs

@@ -0,0 +1,72 @@
+use std::collections::HashMap;
+
+use crate::filter_expr::opcode::OpCode;
+
+/// Remove loaded values that are not read by any opcode. This is useful for reducing the size
+/// of the opcodes to be executed
+pub fn remove_unused_values(mut opcodes: Vec<OpCode>) -> (Vec<OpCode>, bool) {
+    let used_registers = opcodes
+        .iter()
+        .enumerate()
+        .map(|(pos, opcode)| match opcode {
+            OpCode::LOAD(_, _)
+            | OpCode::LOAD_EXTERNAL(_, _)
+            | OpCode::LABEL(_)
+            | OpCode::JMP(_) => vec![],
+            OpCode::NOT(_, reg1)
+            | OpCode::CPY(_, reg1)
+            | OpCode::MOV(_, reg1)
+            | OpCode::JEQ(reg1, _)
+            | OpCode::SHL(_, reg1, _)
+            | OpCode::SHR(_, reg1, _)
+            | OpCode::NEG(_, reg1)
+            | OpCode::HLT(reg1)
+            | OpCode::JNE(reg1, _)
+            | OpCode::JEQ(reg1, _) => {
+                vec![(*reg1, pos)]
+            }
+            OpCode::ADD(_, reg1, reg2)
+            | OpCode::GE(_, reg1, reg2)
+            | OpCode::LE(_, reg1, reg2)
+            | OpCode::MUL(_, reg1, reg2)
+            | OpCode::NE(_, reg1, reg2)
+            | OpCode::DIV(_, reg1, reg2)
+            | OpCode::MOD(_, reg1, reg2)
+            | OpCode::XOR(_, reg1, reg2)
+            | OpCode::SUB(_, reg1, reg2)
+            | OpCode::AND(_, reg1, reg2)
+            | OpCode::OR(_, reg1, reg2)
+            | OpCode::EQ(_, reg1, reg2)
+            | OpCode::GT(_, reg1, reg2)
+            | OpCode::LT(_, reg1, reg2) => {
+                vec![(*reg1, pos), (*reg2, pos)]
+            }
+        })
+        .flatten()
+        .fold(HashMap::new(), |mut acc, (reg, pos)| {
+            acc.entry(reg).or_insert_with(Vec::new).push(pos);
+            acc
+        });
+
+    let old_total_opcodes = opcodes.len();
+
+    // remove unused registers. If the register has not been read by any opcode, then it can be
+    // removed.
+    let new_opcodes = opcodes
+        .into_iter()
+        .enumerate()
+        .filter_map(|(pos, opcode)| match &opcode {
+            OpCode::LOAD(dst, _) | OpCode::LOAD_EXTERNAL(dst, _) => {
+                if used_registers.get(dst).is_some() {
+                    return Some(opcode);
+                } else {
+                    None
+                }
+            }
+            _ => Some(opcode),
+        })
+        .collect::<Vec<_>>();
+
+    let new_total_opcodes = new_opcodes.len();
+    (new_opcodes, old_total_opcodes != new_total_opcodes)
+}

+ 35 - 0
utxo/src/filter_expr/compiler/optimizations/remove_useless_jumps.rs

@@ -0,0 +1,35 @@
+use crate::filter_expr::{compiler::Compiler, opcode::OpCode};
+
+/// Remove useless jumps
+///
+/// Useless jumps are any kind of jump that is just going to the next line.
+pub fn remove_useless_jumps(mut opcodes: Vec<OpCode>) -> (Vec<OpCode>, bool) {
+    // `pos` is the position of the opcode in the new list of opcodes, where the LABELS are
+    // ignored and not counted
+    let mut pos: usize = 0;
+    let label_to_addr = Compiler::labels_to_addr(&opcodes);
+    let old_total_opcodes = opcodes.len();
+    let new_opcodes = opcodes
+        .into_iter()
+        .map(|opcode| match &opcode {
+            OpCode::LABEL(_) => (pos, opcode),
+            _ => {
+                pos += 1;
+                (pos - 1, opcode)
+            }
+        })
+        .filter_map(|(pos, opcode)| match &opcode {
+            OpCode::JEQ(_, addr) | OpCode::JMP(addr) | OpCode::JNE(_, addr) => {
+                if label_to_addr.get(addr).map(|x| *x) == pos.checked_add(1).map(|r| r.into()) {
+                    None
+                } else {
+                    Some(opcode)
+                }
+            }
+            _ => Some(opcode),
+        })
+        .collect::<Vec<_>>();
+    let new_total_opcodes = new_opcodes.len();
+
+    (new_opcodes, old_total_opcodes != new_total_opcodes)
+}

+ 4 - 327
utxo/src/filter_expr/filter.rs

@@ -58,337 +58,14 @@ impl Filter {
         })
     }
 
-    /// Executes operations that can be performed at compile time, and sets the initial_register to
-    /// the result. This is useful for optimizing the program, by executing at `compile` time as
-    /// much as possible, when the terms of the opcodes are not dependent on the runtime state.
-    fn calculate_static_values(&mut self) -> bool {
-        let mut register = HashMap::new();
-        let mut has_changed = false;
-
-        self.opcodes.iter_mut().for_each(|opcode| {
-            match &opcode {
-                OpCode::LOAD(dst, value) => {
-                    register.insert(*dst, value.clone());
-                }
-                OpCode::JMP(_)
-                | OpCode::HLT(_)
-                | OpCode::JEQ(_, _)
-                | OpCode::JNE(_, _)
-                | OpCode::LABEL(_) => {}
-                OpCode::MOV(dst, src) => {
-                    if let Some(value) = register.remove(src) {
-                        register.insert(*dst, value);
-                    }
-                }
-                OpCode::LOAD_EXTERNAL(dst, _)
-                | OpCode::AND(dst, _, _)
-                | OpCode::OR(dst, _, _)
-                | OpCode::NOT(dst, _)
-                | OpCode::MOD(dst, _, _)
-                | OpCode::NE(dst, _, _)
-                | OpCode::GT(dst, _, _)
-                | OpCode::LT(dst, _, _)
-                | OpCode::NEG(dst, _)
-                | OpCode::SHL(dst, _, _)
-                | OpCode::XOR(dst, _, _)
-                | OpCode::SHR(dst, _, _)
-                | OpCode::GE(dst, _, _)
-                | OpCode::LE(dst, _, _)
-                | OpCode::CPY(dst, _) => {
-                    register.remove(dst);
-                }
-                OpCode::JEQ(reg, addr) => {
-                    if let Some(Value::Bool(true)) = register.get(reg) {
-                        *opcode = OpCode::JMP(*addr);
-                    }
-                }
-                OpCode::JNE(reg, addr) => {
-                    if let Some(Value::Bool(false)) = register.get(reg) {
-                        *opcode = OpCode::JMP(*addr);
-                    }
-                }
-                OpCode::EQ(dst, reg1, reg2) => {
-                    let value1 = if let Some(value) = register.get(reg1) {
-                        value.clone()
-                    } else {
-                        register.remove(dst);
-                        return;
-                    };
-                    let value2 = if let Some(value) = register.get(reg2) {
-                        value.clone()
-                    } else {
-                        register.remove(dst);
-                        return;
-                    };
-                    let result = value1 == value2;
-                    register.insert(*dst, result.into());
-                    *opcode = OpCode::LOAD(*dst, result.into());
-                }
-                OpCode::MUL(dst, reg1, reg2)
-                | OpCode::SUB(dst, reg1, reg2)
-                | OpCode::DIV(dst, reg1, reg2)
-                | OpCode::ADD(dst, reg1, reg2) => {
-                    let number1 = match register.get(reg1) {
-                        Some(Value::Number(number)) => *number,
-                        _ => {
-                            register.remove(dst);
-                            return;
-                        }
-                    };
-                    let number2 = match register.get(reg2) {
-                        Some(Value::Number(number)) => *number,
-                        _ => {
-                            register.remove(dst);
-                            return;
-                        }
-                    };
-                    if let Some(calculated_value) = match opcode {
-                        OpCode::ADD(_, _, _) => number1.checked_add(number2).map(Value::Number),
-                        OpCode::MUL(_, _, _) => number1.checked_mul(number2).map(Value::Number),
-                        OpCode::SUB(_, _, _) => number1.checked_sub(number2).map(Value::Number),
-                        OpCode::DIV(_, _, _) => number1.checked_div(number2).map(Value::Number),
-                        _ => None,
-                    } {
-                        register.insert(*dst, calculated_value.clone());
-                        *opcode = OpCode::LOAD(*dst, calculated_value);
-                        has_changed = true;
-                    } else {
-                        register.remove(dst);
-                    }
-                }
-            };
-        });
-        has_changed
-    }
-
-    /// When code is optimized, it is possible that a few registers are being LOADED and not read
-    /// and it is not caught by `remove_unused_values`. This function will make sure that every
-    /// LOADED and LOADED_EXTERNAL register has a unique address.
-    fn assign_unique_register_addresses(&mut self) -> bool {
-        let mut has_changed = false;
-        let mut counter: usize = 0;
-        let mut register_new_mapping = HashMap::<_, Register>::new();
-        let mut new_mapping = |register_new_mapping: &mut HashMap<Register, Register>,
-                               current: &Register|
-         -> Register {
-            counter += 1;
-            if counter != **current {
-                has_changed = true;
-            }
-
-            register_new_mapping.insert(current.clone(), counter.into());
-            counter.into()
-        };
-
-        self.opcodes.iter_mut().for_each(|opcode| match opcode {
-            OpCode::LOAD(current, _) | OpCode::LOAD_EXTERNAL(current, _) => {
-                *current = new_mapping(&mut register_new_mapping, current);
-            }
-            OpCode::SHL(set, reg1, _)
-            | OpCode::SHR(set, reg1, _)
-            | OpCode::NOT(set, reg1)
-            | OpCode::CPY(set, reg1)
-            | OpCode::NEG(set, reg1)
-            | OpCode::MOV(set, reg1) => {
-                *reg1 = register_new_mapping[reg1].clone();
-
-                if let Some(new_set) = register_new_mapping.get(set) {
-                    *set = new_set.clone()
-                } else {
-                    *set = new_mapping(&mut register_new_mapping, set);
-                }
-            }
-            OpCode::HLT(reg1) | OpCode::JEQ(reg1, _) | OpCode::JNE(reg1, _) => {
-                *reg1 = register_new_mapping[reg1].clone();
-            }
-            OpCode::ADD(set, reg1, reg2)
-            | OpCode::MUL(set, reg1, reg2)
-            | OpCode::NE(set, reg1, reg2)
-            | OpCode::GE(set, reg1, reg2)
-            | OpCode::DIV(set, reg1, reg2)
-            | OpCode::LE(set, reg1, reg2)
-            | OpCode::GT(set, reg1, reg2)
-            | OpCode::LT(set, reg1, reg2)
-            | OpCode::DIV(set, reg1, reg2)
-            | OpCode::MOD(set, reg1, reg2)
-            | OpCode::XOR(set, reg1, reg2)
-            | OpCode::SUB(set, reg1, reg2)
-            | OpCode::AND(set, reg1, reg2)
-            | OpCode::EQ(set, reg1, reg2)
-            | OpCode::OR(set, reg1, reg2) => {
-                *reg1 = register_new_mapping[reg1].clone();
-                *reg2 = register_new_mapping[reg2].clone();
-                if let Some(new_set) = register_new_mapping.get(set) {
-                    *set = new_set.clone()
-                } else {
-                    *set = new_mapping(&mut register_new_mapping, set);
-                }
-            }
-            OpCode::LABEL(_) | OpCode::JMP(_) => {}
-        });
-
-        has_changed
-    }
-
-    /// Remove useless jumps
-    ///
-    /// Useless jumps are any kind of jump that is just going to the next line.
-    fn remove_useless_jumps(mut self) -> (Self, bool) {
-        // `pos` is the position of the opcode in the new list of opcodes, where the LABELS are
-        // ignored and not counted
-        let mut pos: usize = 0;
-        let label_to_addr = Compiler::labels_to_addr(&self.opcodes);
-        let old_total_opcodes = self.opcodes.len();
-        let new_opcodes = self
-            .opcodes
-            .into_iter()
-            .map(|opcode| match &opcode {
-                OpCode::LABEL(_) => (pos, opcode),
-                _ => {
-                    pos += 1;
-                    (pos - 1, opcode)
-                }
-            })
-            .filter_map(|(pos, opcode)| match &opcode {
-                OpCode::JEQ(_, addr) | OpCode::JMP(addr) | OpCode::JNE(_, addr) => {
-                    if label_to_addr.get(addr).map(|x| *x) == pos.checked_add(1).map(|r| r.into()) {
-                        None
-                    } else {
-                        Some(opcode)
-                    }
-                }
-                _ => Some(opcode),
-            })
-            .collect::<Vec<_>>();
-        self.opcodes = new_opcodes;
-        let new_total_opcodes = self.opcodes.len();
-
-        (self, old_total_opcodes != new_total_opcodes)
-    }
-
-    /// Remove loaded values that are not read by any opcode. This is useful for reducing the size
-    /// of the opcodes to be executed
-    fn remove_unused_values(mut self) -> (Self, bool) {
-        let used_registers = self
-            .opcodes
-            .iter()
-            .enumerate()
-            .map(|(pos, opcode)| match opcode {
-                OpCode::LOAD(_, _)
-                | OpCode::LOAD_EXTERNAL(_, _)
-                | OpCode::LABEL(_)
-                | OpCode::JMP(_) => vec![],
-                OpCode::NOT(_, reg1)
-                | OpCode::CPY(_, reg1)
-                | OpCode::MOV(_, reg1)
-                | OpCode::JEQ(reg1, _)
-                | OpCode::SHL(_, reg1, _)
-                | OpCode::SHR(_, reg1, _)
-                | OpCode::NEG(_, reg1)
-                | OpCode::HLT(reg1)
-                | OpCode::JNE(reg1, _)
-                | OpCode::JEQ(reg1, _) => {
-                    vec![(*reg1, pos)]
-                }
-                OpCode::ADD(_, reg1, reg2)
-                | OpCode::GE(_, reg1, reg2)
-                | OpCode::LE(_, reg1, reg2)
-                | OpCode::MUL(_, reg1, reg2)
-                | OpCode::NE(_, reg1, reg2)
-                | OpCode::DIV(_, reg1, reg2)
-                | OpCode::MOD(_, reg1, reg2)
-                | OpCode::XOR(_, reg1, reg2)
-                | OpCode::SUB(_, reg1, reg2)
-                | OpCode::AND(_, reg1, reg2)
-                | OpCode::OR(_, reg1, reg2)
-                | OpCode::EQ(_, reg1, reg2)
-                | OpCode::GT(_, reg1, reg2)
-                | OpCode::LT(_, reg1, reg2) => {
-                    vec![(*reg1, pos), (*reg2, pos)]
-                }
-            })
-            .flatten()
-            .fold(HashMap::new(), |mut acc, (reg, pos)| {
-                acc.entry(reg).or_insert_with(Vec::new).push(pos);
-                acc
-            });
-
-        let old_total_opcodes = self.opcodes.len();
-
-        // remove unused registers. If the register has not been read by any opcode, then it can be
-        // removed.
-        let new_opcodes = self
-            .opcodes
-            .into_iter()
-            .enumerate()
-            .filter_map(|(pos, opcode)| match &opcode {
-                OpCode::LOAD(dst, _) | OpCode::LOAD_EXTERNAL(dst, _) => {
-                    if used_registers.get(dst).is_some() {
-                        return Some(opcode);
-                    } else {
-                        None
-                    }
-                }
-                _ => Some(opcode),
-            })
-            .collect();
-
-        self.opcodes = new_opcodes;
-        let new_total_opcodes = self.opcodes.len();
-
-        (self, old_total_opcodes != new_total_opcodes)
-    }
-
-    pub fn remove_redundant_load_and_mov(mut self) -> (Self, bool) {
-        let old_total_opcodes = self.opcodes.len();
-        let new_opcodes = self
-            .opcodes
-            .into_iter()
-            .fold(Vec::new(), |mut acc, current| {
-                if let OpCode::MOV(target, from) = &current {
-                    if let Some(last) = acc.last_mut() {
-                        println!("herex: {:?} {:?}", last, current);
-                        match last {
-                            OpCode::LOAD(reg, _) => {
-                                if reg == from {
-                                    *reg = *target;
-                                    return acc;
-                                }
-                            }
-                            _ => {}
-                        }
-                    }
-                }
-                acc.push(current);
-                acc
-            });
-        self.opcodes = new_opcodes;
-        let new_total_opcodes = self.opcodes.len();
-
-        (self, old_total_opcodes != new_total_opcodes)
-    }
-
     /// Attempts to optiomize the `raw_opcodes` inside the Filter. Returns a tuple with the new
     /// self with the optimized opcodes, and a boolean indicating if the program was optimized.
     pub fn optimize_round(mut self) -> (Self, bool) {
-        let has_calculated_static_values = self.calculate_static_values();
-        let has_changed_register_addresses = self.assign_unique_register_addresses();
-        let (mut new_self, has_removed_unused_values) = self.remove_unused_values();
-        let (mut new_self, has_removed_useless_jumps) = new_self.remove_useless_jumps();
-        let (mut new_self, has_removed_redundant_load_and_mov) =
-            new_self.remove_redundant_load_and_mov();
-
-        new_self.opcodes_to_execute =
-            Compiler::resolve_label_to_addr(new_self.opcodes.clone()).unwrap();
+        let (new_opcodes, has_changed) = Compiler::optimize(self.opcodes);
+        self.opcodes = new_opcodes;
+        self.opcodes_to_execute = Compiler::resolve_label_to_addr(self.opcodes.clone()).unwrap();
 
-        (
-            new_self,
-            has_calculated_static_values
-                || has_removed_unused_values
-                || has_changed_register_addresses
-                || has_removed_useless_jumps
-                || has_removed_redundant_load_and_mov,
-        )
+        (self, has_changed)
     }
 
     /// Returns a human readable version of the compiled program (generated op-codes)