|
@@ -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)
|
|
|
+}
|