소스 검색

Add another optimization

Keep track where data are being loaded to the register, and check they are
indeed being read anywhere or before the same register is updated.
Cesar Rodas 1 년 전
부모
커밋
caf65c34b9

+ 1 - 3
utxo/src/filter_expr/compiler/mod.rs

@@ -154,13 +154,11 @@ impl<'a> Compiler<'a> {
 
 
     pub fn optimize(opcodes: Vec<OpCode>) -> (Vec<OpCode>, bool) {
     pub fn optimize(opcodes: Vec<OpCode>) -> (Vec<OpCode>, bool) {
         vec![
         vec![
-            //optimizations::assign_unique_register_addresses,
             optimizations::remove_redundant_load_and_mov,
             optimizations::remove_redundant_load_and_mov,
-            optimizations::remove_unused_values,
+            optimizations::remove_unused_load,
             optimizations::calculate_static_values,
             optimizations::calculate_static_values,
             optimizations::remove_useless_jumps,
             optimizations::remove_useless_jumps,
             optimizations::remove_dead_code,
             optimizations::remove_dead_code,
-            //optimizations::move_load_top,
         ]
         ]
         .into_iter()
         .into_iter()
         .fold((opcodes, false), |(opcodes, has_optimized), f| {
         .fold((opcodes, false), |(opcodes, has_optimized), f| {

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

@@ -1,70 +0,0 @@
-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)
-}

+ 3 - 6
utxo/src/filter_expr/compiler/optimizations/mod.rs

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

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

@@ -1,105 +0,0 @@
-use crate::filter_expr::opcode::OpCode;
-use std::collections::HashMap;
-
-/// Move the LOAD to the Top of the program
-///
-/// This optimization will allow the VM to execute all the LOAD operations at the beginning of the
-/// program, once, then clone the initial state of the registers and execute the rest of them.
-///
-/// This optimization works under the assumption that cloning the registers is faster than executing
-/// it
-pub fn move_load_top(mut opcodes: Vec<OpCode>) -> (Vec<OpCode>, bool) {
-    let mut load_new_indexes = HashMap::new();
-    let mut new_index = opcodes.len();
-
-    if opcodes
-        .iter()
-        .fold((true, false), |(is_sorted, non_load_stage), opcode| {
-            let is_load_opcode = match opcode {
-                OpCode::LOAD(reg, _) => {
-                    load_new_indexes.insert(*reg, new_index.into());
-                    new_index += 1;
-                    true
-                }
-                _ => false,
-            };
-
-            if non_load_stage && is_load_opcode {
-                (false, true)
-            } else if !is_load_opcode {
-                (is_sorted, true)
-            } else {
-                (is_sorted, non_load_stage)
-            }
-        })
-        .0
-    {
-        // already sorted, all the LOAD opcodes are at the beginning of the program
-        return (opcodes, false);
-    }
-
-    /// assign new indexes to the static registers and rewrite the entire opcodes to use the new
-    /// addresses
-    opcodes.iter_mut().for_each(|opcode| match opcode {
-        OpCode::LOAD(reg, _) => {
-            *reg = load_new_indexes[reg];
-        }
-        OpCode::LABEL(_) | OpCode::JMP(_) => {}
-        OpCode::LOAD_EXTERNAL(dst, _)
-        | OpCode::HLT(dst)
-        | OpCode::JEQ(dst, _)
-        | OpCode::JNE(dst, _) => {
-            if let Some(new_dst) = load_new_indexes.get(dst) {
-                *dst = *new_dst;
-            }
-        }
-        OpCode::MOV(dst, reg1)
-        | OpCode::SHL(dst, reg1, _)
-        | OpCode::CPY(dst, reg1)
-        | OpCode::SHR(dst, reg1, _)
-        | OpCode::NEG(dst, reg1)
-        | OpCode::NOT(dst, reg1) => {
-            if let Some(value) = load_new_indexes.get(reg1) {
-                *reg1 = *value;
-            }
-            if let Some(new_dst) = load_new_indexes.get(dst) {
-                *dst = *new_dst;
-            }
-        }
-        OpCode::EQ(dst, reg1, reg2)
-        | OpCode::GE(dst, reg1, reg2)
-        | OpCode::GT(dst, reg1, reg2)
-        | OpCode::LE(dst, reg1, reg2)
-        | OpCode::LT(dst, reg1, reg2)
-        | OpCode::NE(dst, reg1, reg2)
-        | OpCode::AND(dst, reg1, reg2)
-        | OpCode::OR(dst, reg1, reg2)
-        | OpCode::XOR(dst, reg1, reg2)
-        | OpCode::ADD(dst, reg1, reg2)
-        | OpCode::SUB(dst, reg1, reg2)
-        | OpCode::MUL(dst, reg1, reg2)
-        | OpCode::DIV(dst, reg1, reg2)
-        | OpCode::MOD(dst, reg1, reg2) => {
-            if let Some(value) = load_new_indexes.get(reg1) {
-                *reg1 = *value;
-            }
-            if let Some(value) = load_new_indexes.get(reg2) {
-                *reg2 = *value;
-            }
-            if let Some(new_dst) = load_new_indexes.get(dst) {
-                *dst = *new_dst;
-            }
-        }
-    });
-
-    // split the opcodes into two parts, the LOAD opcodes and the rest
-    let mut parts: (Vec<_>, Vec<_>) = opcodes
-        .into_iter()
-        .partition(|opcode| matches!(opcode, OpCode::LOAD(_, _)));
-
-    // merge both parts
-    let mut new_opcodes = parts.0;
-    new_opcodes.extend(parts.1);
-
-    (new_opcodes, true)
-}

+ 131 - 0
utxo/src/filter_expr/compiler/optimizations/remove_unused_load.rs

@@ -0,0 +1,131 @@
+use crate::filter_expr::opcode::OpCode;
+use std::collections::HashMap;
+
+/// 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_load(mut opcodes: Vec<OpCode>) -> (Vec<OpCode>, bool) {
+    // list of opcodes where the register are accessed
+    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::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
+        });
+
+    // a list of registers that are loaded or overwritten
+    let loaded_registers = opcodes
+        .iter()
+        .enumerate()
+        .filter_map(|(pos, opcode)| match opcode {
+            OpCode::LOAD(dst, _) | OpCode::LOAD_EXTERNAL(dst, _) => Some((*dst, pos)),
+            OpCode::ADD(dst, _, _)
+            | OpCode::NOT(dst, _)
+            | OpCode::CPY(dst, _)
+            | OpCode::MOV(dst, _)
+            | OpCode::SHL(dst, _, _)
+            | OpCode::SHR(dst, _, _)
+            | OpCode::NEG(dst, _)
+            | OpCode::GE(dst, _, _)
+            | OpCode::LE(dst, _, _)
+            | OpCode::MUL(dst, _, _)
+            | OpCode::NE(dst, _, _)
+            | OpCode::DIV(dst, _, _)
+            | OpCode::MOD(dst, _, _)
+            | OpCode::XOR(dst, _, _)
+            | OpCode::SUB(dst, _, _)
+            | OpCode::AND(dst, _, _)
+            | OpCode::OR(dst, _, _)
+            | OpCode::EQ(dst, _, _)
+            | OpCode::GT(dst, _, _)
+            | OpCode::LT(dst, _, _) => Some((*dst, pos)),
+            _ => None,
+        })
+        .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, _) => {
+                // get the address where the register is reloaded.
+                //
+                // The main goal is to check if readed before it is reloaded. If it is not readed
+                // then it is redundant and can be removed.
+                let next_load = loaded_registers
+                    .get(dst)
+                    .expect("Invalid register")
+                    .iter()
+                    .fold(None, |mut next, loaded_at| {
+                        if next.is_none() && *loaded_at > pos {
+                            next = Some(*loaded_at);
+                        }
+                        next
+                    });
+
+                if let Some(readed_at) = used_registers.get(dst) {
+                    if readed_at.iter().any(|readed_at| {
+                        if let Some(next_load) = next_load {
+                            *readed_at < next_load && *readed_at > pos
+                        } else if *readed_at > pos {
+                            true
+                        } else {
+                            false
+                        }
+                    }) {
+                        Some(opcode)
+                    } else {
+                        None
+                    }
+                } else {
+                    None
+                }
+            }
+            _ => Some(opcode),
+        })
+        .collect::<Vec<_>>();
+
+    let new_total_opcodes = new_opcodes.len();
+    (new_opcodes, old_total_opcodes != new_total_opcodes)
+}

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

@@ -1,70 +0,0 @@
-use crate::filter_expr::opcode::OpCode;
-use std::collections::HashMap;
-
-/// 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::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)
-}