1#![forbid(unsafe_code)]
13
14use std::collections::BTreeMap;
15
16use fnv::{FnvHashMap, FnvHashSet};
17use petgraph::{graph, visit};
18
19pub mod glulx;
20
21#[derive(Debug)]
23pub struct DebugFunctionData {
24 pub addr: u32,
25 pub len: u32,
26 pub name: String,
27}
28
29#[derive(Copy, Clone, Debug, PartialEq)]
34pub enum FunctionSafety {
35 Unsafe,
36 UnsafeDynamicBranches,
37 SafetyTBD,
38}
39
40pub trait VirtualMachine {
42 fn get_functions(&self) -> FnvHashMap<u32, FunctionSafety>;
43 fn mark_function_as_unsafe(&mut self, addr: u32);
44
45 fn mark_all_unsafe_functions(&mut self, edges: FnvHashSet<(u32, u32)>) {
46 let mut graph: graph::Graph<u32, ()> = graph::Graph::new();
47
48 let functions = self.get_functions();
50 let mut function_nodes = FnvHashMap::default();
51 let mut unsafe_functions = Vec::new();
52 for (addr, safety) in functions {
53 let node = graph.add_node(addr);
54 function_nodes.insert(addr, node);
55 if safety != FunctionSafety::SafetyTBD {
56 unsafe_functions.push(node);
57 }
58 }
59
60 graph.extend_with_edges(edges.iter().map(|(caller_addr, callee_addr)| {
62 let caller_node = function_nodes[caller_addr];
63 let callee_node = function_nodes[callee_addr];
64 (callee_node, caller_node)
66 }));
67
68 let mut dfs = visit::Dfs::empty(&graph);
70 dfs.stack = unsafe_functions;
71 while let Some(node_index) = dfs.next(&graph) {
72 let addr = graph[node_index];
73 self.mark_function_as_unsafe(addr);
74 }
75 }
76}
77
78pub trait VMInstruction {
80 fn addr(&self) -> u32;
81 fn does_halt(&self) -> bool;
82}
83
84pub struct BasicBlock<I> {
86 pub label: u32,
87 pub code: Vec<I>,
88 pub branches: FnvHashSet<u32>,
89}
90
91pub fn calculate_basic_blocks<I: VMInstruction>(instructions: Vec<I>, entry_points: FnvHashSet<u32>, exit_branches: FnvHashMap<u32, Vec<u32>>) -> BTreeMap<u32, BasicBlock<I>> {
93 let mut blocks: BTreeMap<u32, BasicBlock<I>> = BTreeMap::new();
94 let mut current_block_addr = 0;
95 let mut last_instruction_halted = false;
96 for instruction in instructions {
97 let addr = instruction.addr();
98 if current_block_addr > 0 {
100 let current_block = blocks.get_mut(¤t_block_addr).unwrap();
101 if entry_points.contains(&addr) {
103 if !last_instruction_halted {
105 current_block.branches.insert(addr);
106 }
107 }
109 else {
110 if let Some(branches) = exit_branches.get(&addr) {
112 for branch in branches {
113 current_block.branches.insert(*branch);
114 }
115 current_block_addr = 0;
116 }
117 last_instruction_halted = instruction.does_halt();
119 current_block.code.push(instruction);
120 continue;
122 }
123 }
124 current_block_addr = addr;
126 last_instruction_halted = instruction.does_halt();
127 let mut current_block = BasicBlock::<I> {
128 label: addr,
129 code: vec![instruction],
130 branches: FnvHashSet::default(),
131 };
132 if let Some(branches) = exit_branches.get(&addr) {
134 for branch in branches {
135 current_block.branches.insert(*branch);
136 }
137 }
138 blocks.insert(addr, current_block);
139 }
140 blocks
141}
142
143#[derive(Copy, Clone, Debug, PartialEq)]
144pub enum BranchTarget {
145 Dynamic,
146 Absolute(u32),
147 Return(u32),
148}