1use super::{
2 super::{
3 decoder::build_decoder,
4 elf::ProgramMetadata,
5 instructions::{
6 execute_with_thread, extract_opcode, handle_invalid_op, instruction_length,
7 is_basic_block_end_instruction, Instruction, Register, Thread, ThreadFactory,
8 },
9 Error,
10 },
11 CoreMachine, DefaultMachine, DefaultMachineRunner, Machine, SupportMachine, VERSION2,
12};
13use bytes::Bytes;
14
15const TRACE_SIZE: usize = 8192;
17const TRACE_MASK: usize = TRACE_SIZE - 1;
19const TRACE_ITEM_LENGTH: usize = 16;
21const TRACE_ADDRESS_SHIFTS: usize = 2;
23
24struct Trace<Inner: Machine> {
25 address: u64,
26 length: usize,
27 instruction_count: u8,
28 instructions: [Instruction; TRACE_ITEM_LENGTH],
29 threads: [Thread<Inner>; TRACE_ITEM_LENGTH],
30}
31
32impl<Inner: Machine> Default for Trace<Inner> {
33 fn default() -> Self {
34 Trace {
35 address: 0,
36 length: 0,
37 instruction_count: 0,
38 instructions: [0; TRACE_ITEM_LENGTH],
39 threads: [handle_invalid_op::<Inner>; TRACE_ITEM_LENGTH],
40 }
41 }
42}
43
44#[inline(always)]
45fn calculate_slot(addr: u64) -> usize {
46 (addr as usize >> TRACE_ADDRESS_SHIFTS) & TRACE_MASK
47}
48
49pub struct TraceMachine<Inner: SupportMachine> {
50 pub machine: DefaultMachine<Inner>,
51
52 factory: ThreadFactory<DefaultMachine<Inner>>,
53 traces: Vec<Trace<DefaultMachine<Inner>>>,
54}
55
56impl<Inner: SupportMachine> CoreMachine for TraceMachine<Inner> {
57 type REG = <Inner as CoreMachine>::REG;
58 type MEM = <Inner as CoreMachine>::MEM;
59
60 fn pc(&self) -> &Self::REG {
61 self.machine.pc()
62 }
63
64 fn update_pc(&mut self, pc: Self::REG) {
65 self.machine.update_pc(pc);
66 }
67
68 fn commit_pc(&mut self) {
69 self.machine.commit_pc();
70 }
71
72 fn memory(&self) -> &Self::MEM {
73 self.machine.memory()
74 }
75
76 fn memory_mut(&mut self) -> &mut Self::MEM {
77 self.machine.memory_mut()
78 }
79
80 fn registers(&self) -> &[Self::REG] {
81 self.machine.registers()
82 }
83
84 fn set_register(&mut self, idx: usize, value: Self::REG) {
85 self.machine.set_register(idx, value)
86 }
87
88 fn isa(&self) -> u8 {
89 self.machine.isa()
90 }
91
92 fn version(&self) -> u32 {
93 self.machine.version()
94 }
95}
96
97impl<Inner: SupportMachine> Machine for TraceMachine<Inner> {
98 fn ecall(&mut self) -> Result<(), Error> {
99 self.machine.ecall()
100 }
101
102 fn ebreak(&mut self) -> Result<(), Error> {
103 self.machine.ebreak()
104 }
105}
106
107impl<Inner: SupportMachine> DefaultMachineRunner for TraceMachine<Inner> {
108 type Inner = Inner;
109
110 fn new(machine: DefaultMachine<Inner>) -> Self {
111 Self {
112 machine,
113 factory: ThreadFactory::create(),
114 traces: vec![],
115 }
116 }
117
118 fn machine(&self) -> &DefaultMachine<Inner> {
119 &self.machine
120 }
121
122 fn machine_mut(&mut self) -> &mut DefaultMachine<Inner> {
123 &mut self.machine
124 }
125
126 fn run(&mut self) -> Result<i8, Error> {
127 let mut decoder = build_decoder::<Inner::REG>(self.isa(), self.version());
128 self.machine.set_running(true);
129 self.traces.resize_with(TRACE_SIZE, Trace::default);
133 while self.machine.running() {
134 if self.machine.pause.has_interrupted() {
135 self.machine.pause.free();
136 return Err(Error::Pause);
137 }
138 if self.machine.reset_signal() {
139 decoder.reset_instructions_cache();
140 for i in self.traces.iter_mut() {
141 *i = Trace::default()
142 }
143 }
144 let pc = self.machine.pc().to_u64();
145 let slot = calculate_slot(pc);
146 let address_match = if self.machine.version() < VERSION2 {
148 (pc as u32 as u64) == self.traces[slot].address
149 } else {
150 pc == self.traces[slot].address
151 };
152 if (!address_match) || self.traces[slot].instruction_count == 0 {
153 self.traces[slot] = Trace::default();
154 let mut current_pc = pc;
155 let mut i = 0;
156 while i < TRACE_ITEM_LENGTH {
157 let instruction = decoder.decode(self.machine.memory_mut(), current_pc)?;
158 let end_instruction = is_basic_block_end_instruction(instruction);
159 current_pc += u64::from(instruction_length(instruction));
160 self.traces[slot].instructions[i] = instruction;
161 self.traces[slot].threads[i] = self.factory[extract_opcode(instruction)];
162 i += 1;
163 if end_instruction {
164 break;
165 }
166 }
167 self.traces[slot].address = pc;
168 self.traces[slot].length = (current_pc - pc) as usize;
169 self.traces[slot].instruction_count = i as u8;
170 }
171 for i in 0..self.traces[slot].instruction_count {
172 let inst = self.traces[slot].instructions[i as usize];
173 let cycles = self.machine.instruction_cycle_func()(inst);
174 self.machine.add_cycles(cycles)?;
175 execute_with_thread(
176 inst,
177 &mut self.machine,
178 &self.traces[slot].threads[i as usize],
179 )?;
180 }
181 }
182 Ok(self.machine.exit_code())
183 }
184}
185
186impl<Inner: SupportMachine> TraceMachine<Inner> {
187 pub fn load_program(
188 &mut self,
189 program: &Bytes,
190 args: impl ExactSizeIterator<Item = Result<Bytes, Error>>,
191 ) -> Result<u64, Error> {
192 self.machine.load_program(program, args)
193 }
194
195 pub fn load_program_with_metadata(
196 &mut self,
197 program: &Bytes,
198 metadata: &ProgramMetadata,
199 args: impl ExactSizeIterator<Item = Result<Bytes, Error>>,
200 ) -> Result<u64, Error> {
201 self.machine
202 .load_program_with_metadata(program, metadata, args)
203 }
204
205 pub fn set_max_cycles(&mut self, cycles: u64) {
206 self.machine.inner_mut().set_max_cycles(cycles)
207 }
208}
209
210#[cfg(test)]
211mod tests {
212 use super::*;
213
214 #[test]
215 fn test_trace_constant_rules() {
216 assert!(TRACE_SIZE.is_power_of_two());
217 assert_eq!(TRACE_MASK, TRACE_SIZE - 1);
218 assert!(TRACE_ITEM_LENGTH.is_power_of_two());
219 assert!(TRACE_ITEM_LENGTH <= 255);
220 }
221}