#![forbid(unsafe_code)]
use std::collections::BTreeMap;
use fnv::{FnvHashMap, FnvHashSet};
use petgraph::{graph, visit};
pub mod glulx;
#[derive(Debug)]
pub struct DebugFunctionData {
pub addr: u32,
pub len: u32,
pub name: String,
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum FunctionSafety {
Unsafe,
UnsafeDynamicBranches,
SafetyTBD,
}
pub trait VirtualMachine {
fn get_functions(&self) -> FnvHashMap<u32, FunctionSafety>;
fn mark_function_as_unsafe(&mut self, addr: u32);
fn mark_all_unsafe_functions(&mut self, edges: FnvHashSet<(u32, u32)>) {
let mut graph: graph::Graph<u32, ()> = graph::Graph::new();
let functions = self.get_functions();
let mut function_nodes = FnvHashMap::default();
let mut unsafe_functions = Vec::new();
for (addr, safety) in functions {
let node = graph.add_node(addr);
function_nodes.insert(addr, node);
if safety != FunctionSafety::SafetyTBD {
unsafe_functions.push(node);
}
}
graph.extend_with_edges(edges.iter().map(|(caller_addr, callee_addr)| {
let caller_node = function_nodes[caller_addr];
let callee_node = function_nodes[callee_addr];
(callee_node, caller_node)
}));
let mut dfs = visit::Dfs::empty(&graph);
dfs.stack = unsafe_functions;
while let Some(node_index) = dfs.next(&graph) {
let addr = graph[node_index];
self.mark_function_as_unsafe(addr);
}
}
}
pub trait VMInstruction {
fn addr(&self) -> u32;
fn does_halt(&self) -> bool;
}
pub struct BasicBlock<I> {
pub label: u32,
pub code: Vec<I>,
pub branches: FnvHashSet<u32>,
}
pub fn calculate_basic_blocks<I: VMInstruction>(instructions: Vec<I>, entry_points: FnvHashSet<u32>, exit_branches: FnvHashMap<u32, Vec<u32>>) -> BTreeMap<u32, BasicBlock<I>> {
let mut blocks: BTreeMap<u32, BasicBlock<I>> = BTreeMap::new();
let mut current_block_addr = 0;
let mut last_instruction_halted = false;
for instruction in instructions {
let addr = instruction.addr();
if current_block_addr > 0 {
let current_block = blocks.get_mut(¤t_block_addr).unwrap();
if entry_points.contains(&addr) {
if !last_instruction_halted {
current_block.branches.insert(addr);
}
}
else {
if let Some(branches) = exit_branches.get(&addr) {
for branch in branches {
current_block.branches.insert(*branch);
}
current_block_addr = 0;
}
last_instruction_halted = instruction.does_halt();
current_block.code.push(instruction);
continue;
}
}
current_block_addr = addr;
last_instruction_halted = instruction.does_halt();
let mut current_block = BasicBlock::<I> {
label: addr,
code: vec![instruction],
branches: FnvHashSet::default(),
};
if let Some(branches) = exit_branches.get(&addr) {
for branch in branches {
current_block.branches.insert(*branch);
}
}
blocks.insert(addr, current_block);
}
blocks
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum BranchTarget {
Dynamic,
Absolute(u32),
Return(u32),
}