<commit_before>//! Converts the from ESIL/CFG representation to SSA.

use std::collections::HashMap;
use petgraph::graph::NodeIndex;
use frontend::structs::LRegInfo;
use middle::cfg::NodeData as CFGNodeData;
use middle::cfg::EdgeType as CFGEdgeType;
use middle::cfg::{CFG, BasicBlock};
use middle::ssa::{BBInfo, SSA, SSAMod, ValueType};
use middle::ssa::verifier::Verify;
use middle::ssa::verifier::VerifiedAdd;
use middle::ir::{MVal, MInst, MOpcode, MValType};
use middle::phiplacement::PhiPlacer;
use middle::regfile::SubRegisterFile;
use middle::dce;

pub type VarId = usize;

pub struct SSAConstruction<'a, T> where
	T: Clone + Verify + SSAMod<BBInfo=BBInfo> + 'a
{
	pub phiplacer: PhiPlacer<'a, T>,
	pub regfile:   SubRegisterFile,
	pub temps:     HashMap<String, T::ValueRef>,
}

impl<'a, T> SSAConstruction<'a, T> where
	T: 'a + Clone + Verify +
       SSAMod<BBInfo=BBInfo, ValueRef=NodeIndex, ActionRef=NodeIndex>
{
	pub fn new(ssa: &'a mut T, reg_info: &LRegInfo) -> SSAConstruction<'a, T> {
		let mut sc = SSAConstruction {
			phiplacer: PhiPlacer::new(ssa),
			regfile:   SubRegisterFile::new(reg_info),
			temps:     HashMap::new(),
		};
		sc.phiplacer.add_variables(vec![
								   ValueType::Integer{width: 64}, // cur
								   ValueType::Integer{width: 64},  // old
								   ValueType::Integer{width: 64}  // lastsz
		]);
		// make the following a method of regfile?
		sc.phiplacer.add_variables(sc.regfile.whole_registers.clone());
		sc
	}

	pub fn run(&mut self, cfg: &CFG) {
		let mut blocks = Vec::<T::ActionRef>::with_capacity(cfg.g.node_count());
		let bb_iter = cfg.bbs.iter();

		for _ in 0..cfg.g.node_count() {
			blocks.push(self.phiplacer.ssa.invalid_action());
		}

		{
			// Insert the entry and exit blocks for the ssa.
			let block = self.phiplacer.add_block(BBInfo { addr: 0 });
			self.phiplacer.ssa.mark_start_node(&block);
			let zero = self.phiplacer.ssa.add_const(block, 0);
			self.phiplacer.write_variable(block, 0, zero); // cur = 0
			self.phiplacer.write_variable(block, 1, zero); // old = 0

			for (i, name) in self.regfile.whole_names.iter().enumerate() {
				let argnode = self.phiplacer.ssa.add_comment(block, self.regfile.whole_registers[i], name.clone());
				self.phiplacer.write_variable(block, i+2, argnode);
			}

			blocks[cfg.entry.index()] = block;

			let block = self.phiplacer.add_dynamic();
			self.phiplacer.ssa.mark_exit_node(&block);
			blocks[cfg.exit.index()] = block;
		}

		for (addr, i) in bb_iter {
			let block = self.phiplacer.add_block(BBInfo { addr: *addr });
			blocks[i.index()] = block;
			match cfg.g[*i] {
				CFGNodeData::Block(ref srcbb) => {
					self.process_block(block, srcbb);
				}
				_ => unreachable!(),
			}
		}

		for edge in cfg.g.raw_edges() {
			let i = match edge.weight.edge_type {
				CFGEdgeType::False => 0,
				CFGEdgeType::True => 1,
				CFGEdgeType::Unconditional => 2,
			};
			self.phiplacer.ssa.add_control_edge(
				blocks[edge.source().index()],
				blocks[edge.target().index()],
				i);
		}
		for &block in &blocks {
			self.phiplacer.seal_block(block);
		}

		//self.phiplacer.ssa.stable_indexing = false;
		//self.phiplacer.ssa.cleanup();

		{
			dce::collect(self.phiplacer.ssa);
		}
		//dce::collect(self.phiplacer.ssa, &[exit_regstate]);
		//self.phiplacer.ssa.cleanup();
	}

	fn process_in_flag(&mut self, block: T::ActionRef, _mval: &MVal) -> T::ValueRef {
		// this would be neccesary if parser didn't use EsilCur/EsilOld
		let _cur = self.phiplacer.read_variable(block, 0);
		let _old = self.phiplacer.read_variable(block, 1);
		let _lastsz = self.phiplacer.read_variable(block, 2);
		//unimplemented!();
		self.phiplacer.ssa.invalid_value()
	}

	fn process_in(&mut self, block: T::ActionRef, mval: &MVal) -> T::ValueRef {
		match mval.val_type {
			MValType::Register  => self.regfile.read_register(&mut self.phiplacer, 2, block, &mval.name),
			MValType::Temporary => self.temps[&mval.name],
			MValType::Internal  => self.process_in_flag(block, mval),
			MValType::EsilCur   => self.phiplacer.read_variable(block, 0),
			MValType::EsilOld   => self.phiplacer.read_variable(block, 1),
			MValType::Lastsz   => self.phiplacer.read_variable(block, 2),
			MValType::Unknown   => self.phiplacer.ssa.invalid_value(),
			                       //self.phiplacer.ssa.add_comment(block, &"Unknown".to_string()), // unimplemented!()
			MValType::Null      => self.phiplacer.ssa.invalid_value(),
		}
	}

	fn process_out(&mut self, block: T::ActionRef, mval: &MVal, value: T::ValueRef) {
		match mval.val_type { 
			MValType::Register  => self.regfile.write_register(&mut self.phiplacer, 2, block, &mval.name, value),
			MValType::Temporary => {self.temps.insert(mval.name.clone(), value);},
			MValType::Null      => {},
			_                   => panic!(),
		}
	}<commit_msg>esilssa.rs: Since all flags are handled now, this code no longer needs to tolerate leftover MValType::Internals.<commit_after>//! Converts the from ESIL/CFG representation to SSA.

use std::collections::HashMap;
use petgraph::graph::NodeIndex;
use frontend::structs::LRegInfo;
use middle::cfg::NodeData as CFGNodeData;
use middle::cfg::EdgeType as CFGEdgeType;
use middle::cfg::{CFG, BasicBlock};
use middle::ssa::{BBInfo, SSA, SSAMod, ValueType};
use middle::ssa::verifier::Verify;
use middle::ssa::verifier::VerifiedAdd;
use middle::ir::{MVal, MInst, MOpcode, MValType};
use middle::phiplacement::PhiPlacer;
use middle::regfile::SubRegisterFile;
use middle::dce;

pub type VarId = usize;

pub struct SSAConstruction<'a, T> where
	T: Clone + Verify + SSAMod<BBInfo=BBInfo> + 'a
{
	pub phiplacer: PhiPlacer<'a, T>,
	pub regfile:   SubRegisterFile,
	pub temps:     HashMap<String, T::ValueRef>,
}

impl<'a, T> SSAConstruction<'a, T> where
	T: 'a + Clone + Verify +
       SSAMod<BBInfo=BBInfo, ValueRef=NodeIndex, ActionRef=NodeIndex>
{
	pub fn new(ssa: &'a mut T, reg_info: &LRegInfo) -> SSAConstruction<'a, T> {
		let mut sc = SSAConstruction {
			phiplacer: PhiPlacer::new(ssa),
			regfile:   SubRegisterFile::new(reg_info),
			temps:     HashMap::new(),
		};
		sc.phiplacer.add_variables(vec![
								   ValueType::Integer{width: 64}, // cur
								   ValueType::Integer{width: 64},  // old
								   ValueType::Integer{width: 64}  // lastsz
		]);
		// make the following a method of regfile?
		sc.phiplacer.add_variables(sc.regfile.whole_registers.clone());
		sc
	}

	pub fn run(&mut self, cfg: &CFG) {
		let mut blocks = Vec::<T::ActionRef>::with_capacity(cfg.g.node_count());
		let bb_iter = cfg.bbs.iter();

		for _ in 0..cfg.g.node_count() {
			blocks.push(self.phiplacer.ssa.invalid_action());
		}

		{
			// Insert the entry and exit blocks for the ssa.
			let block = self.phiplacer.add_block(BBInfo { addr: 0 });
			self.phiplacer.ssa.mark_start_node(&block);
			let zero = self.phiplacer.ssa.add_const(block, 0);
			self.phiplacer.write_variable(block, 0, zero); // cur = 0
			self.phiplacer.write_variable(block, 1, zero); // old = 0

			for (i, name) in self.regfile.whole_names.iter().enumerate() {
				let argnode = self.phiplacer.ssa.add_comment(block, self.regfile.whole_registers[i], name.clone());
				self.phiplacer.write_variable(block, i+2, argnode);
			}

			blocks[cfg.entry.index()] = block;

			let block = self.phiplacer.add_dynamic();
			self.phiplacer.ssa.mark_exit_node(&block);
			blocks[cfg.exit.index()] = block;
		}

		for (addr, i) in bb_iter {
			let block = self.phiplacer.add_block(BBInfo { addr: *addr });
			blocks[i.index()] = block;
			match cfg.g[*i] {
				CFGNodeData::Block(ref srcbb) => {
					self.process_block(block, srcbb);
				}
				_ => unreachable!(),
			}
		}

		for edge in cfg.g.raw_edges() {
			let i = match edge.weight.edge_type {
				CFGEdgeType::False => 0,
				CFGEdgeType::True => 1,
				CFGEdgeType::Unconditional => 2,
			};
			self.phiplacer.ssa.add_control_edge(
				blocks[edge.source().index()],
				blocks[edge.target().index()],
				i);
		}
		for &block in &blocks {
			self.phiplacer.seal_block(block);
		}

		//self.phiplacer.ssa.stable_indexing = false;
		//self.phiplacer.ssa.cleanup();

		{
			dce::collect(self.phiplacer.ssa);
		}
		//dce::collect(self.phiplacer.ssa, &[exit_regstate]);
		//self.phiplacer.ssa.cleanup();
	}

	fn process_in(&mut self, block: T::ActionRef, mval: &MVal) -> T::ValueRef {
		match mval.val_type {
			MValType::Register  => self.regfile.read_register(&mut self.phiplacer, 2, block, &mval.name),
			MValType::Temporary => self.temps[&mval.name],
			MValType::Internal  => panic!("This value type should be eliminated during parsing"),
			MValType::EsilCur   => self.phiplacer.read_variable(block, 0),
			MValType::EsilOld   => self.phiplacer.read_variable(block, 1),
			MValType::Lastsz    => self.phiplacer.read_variable(block, 2),
			MValType::Unknown   => self.phiplacer.ssa.invalid_value(),
			                       //self.phiplacer.ssa.add_comment(block, &"Unknown".to_string()), // unimplemented!()
			MValType::Null      => self.phiplacer.ssa.invalid_value(),
		}
	}

	fn process_out(&mut self, block: T::ActionRef, mval: &MVal, value: T::ValueRef) {
		match mval.val_type { 
			MValType::Register  => self.regfile.write_register(&mut self.phiplacer, 2, block, &mval.name, value),
			MValType::Temporary => {self.temps.insert(mval.name.clone(), value);},
			MValType::Null      => {},
			_                   => panic!(),
		}
	}
{'fraction_non_alphanumeric': 0.1618812941932719, 'fraction_numerical': 0.00503535461752732, 'mean_word_length': 3.897691500524659, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 60, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5489509', 'n_tokens_mistral': 3858, 'n_tokens_neox': 3452, 'n_words': 646}