|
@@ -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) = ¤t {
|
|
|
- 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)
|