1use sp1_hypercube::air::PROOF_NONCE_NUM_WORDS;
2use sp1_jit::MinimalTrace;
3use sp1_primitives::consts::LOG_PAGE_SIZE;
4use std::{marker::PhantomData, sync::Arc};
5
6use crate::{
7 events::{MemoryReadRecord, MemoryRecord, MemoryWriteRecord, PageProtRecord},
8 vm::{
9 gas::ReportGenerator,
10 results::{
11 AluResult, BranchResult, CycleResult, FetchResult, JumpResult, LoadResult,
12 LoadResultSupervisor, MaybeImmediate, StoreResult, StoreResultSupervisor, TrapResult,
13 UTypeResult,
14 },
15 syscall::SyscallRuntime,
16 CoreVM,
17 },
18 ExecutionError, ExecutionMode, ExecutionReport, Instruction, Opcode, Program, Register,
19 SP1CoreOpts, SupervisorMode, SyscallCode, TrapError, UserMode,
20};
21
22pub struct GasEstimatingVM<'a, M: ExecutionMode> {
26 pub core: CoreVM<'a, M>,
28 pub gas_calculator: ReportGenerator,
30 pub hint_lens_idx: usize,
32 _mode: PhantomData<M>,
34}
35
36impl GasEstimatingVM<'_, SupervisorMode> {
37 pub fn execute(&mut self) -> Result<ExecutionReport, ExecutionError> {
39 if self.core.is_done() {
40 return Ok(self.gas_calculator.generate_report());
41 }
42
43 loop {
44 match self.execute_instruction()? {
45 CycleResult::Done(false) => {}
46 CycleResult::TraceEnd | CycleResult::ShardBoundary | CycleResult::Done(true) => {
47 return Ok(self.gas_calculator.generate_report());
48 }
49 }
50 }
51 }
52
53 fn execute_instruction(&mut self) -> Result<CycleResult, ExecutionError> {
55 let instruction = self.core.fetch();
56
57 match &instruction.opcode {
58 Opcode::ADD
59 | Opcode::ADDI
60 | Opcode::SUB
61 | Opcode::XOR
62 | Opcode::OR
63 | Opcode::AND
64 | Opcode::SLL
65 | Opcode::SLLW
66 | Opcode::SRL
67 | Opcode::SRA
68 | Opcode::SRLW
69 | Opcode::SRAW
70 | Opcode::SLT
71 | Opcode::SLTU
72 | Opcode::MUL
73 | Opcode::MULHU
74 | Opcode::MULHSU
75 | Opcode::MULH
76 | Opcode::MULW
77 | Opcode::DIVU
78 | Opcode::REMU
79 | Opcode::DIV
80 | Opcode::REM
81 | Opcode::DIVW
82 | Opcode::ADDW
83 | Opcode::SUBW
84 | Opcode::DIVUW
85 | Opcode::REMUW
86 | Opcode::REMW => {
87 self.execute_alu(&instruction);
88 }
89 Opcode::LB
90 | Opcode::LBU
91 | Opcode::LH
92 | Opcode::LHU
93 | Opcode::LW
94 | Opcode::LWU
95 | Opcode::LD => self.execute_load(&instruction)?,
96 Opcode::SB | Opcode::SH | Opcode::SW | Opcode::SD => {
97 self.execute_store(&instruction)?;
98 }
99 Opcode::JAL | Opcode::JALR => {
100 self.execute_jump(&instruction);
101 }
102 Opcode::BEQ | Opcode::BNE | Opcode::BLT | Opcode::BGE | Opcode::BLTU | Opcode::BGEU => {
103 self.execute_branch(&instruction);
104 }
105 Opcode::LUI | Opcode::AUIPC => {
106 self.execute_utype(&instruction);
107 }
108 Opcode::ECALL => self.execute_ecall(&instruction)?,
109 Opcode::EBREAK | Opcode::UNIMP => {
110 unreachable!("Invalid opcode for `execute_instruction`: {:?}", instruction.opcode)
111 }
112 }
113
114 Ok(self.core.advance())
115 }
116}
117
118impl GasEstimatingVM<'_, SupervisorMode> {
119 pub fn execute_load(&mut self, instruction: &Instruction) -> Result<(), ExecutionError> {
121 let LoadResultSupervisor { addr, rd, mr_record, rr_record, rw_record, rs1, .. } =
122 self.core.execute_load(instruction)?;
123
124 self.gas_calculator.handle_instruction(
125 instruction,
126 self.core.needs_bump_clk_high(),
127 rd == Register::X0,
128 self.core.needs_state_bump(instruction),
129 );
130
131 self.gas_calculator.handle_mem_event(addr, mr_record.prev_timestamp);
132 self.gas_calculator.handle_mem_event(rs1 as u64, rr_record.prev_timestamp);
133 self.gas_calculator.handle_mem_event(rd as u64, rw_record.prev_timestamp);
134
135 Ok(())
136 }
137
138 pub fn execute_store(&mut self, instruction: &Instruction) -> Result<(), ExecutionError> {
140 let StoreResultSupervisor { addr, mw_record, rs1_record, rs2_record, rs1, rs2, .. } =
141 self.core.execute_store(instruction)?;
142
143 self.gas_calculator.handle_instruction(
144 instruction,
145 self.core.needs_bump_clk_high(),
146 false,
147 self.core.needs_state_bump(instruction),
148 );
149
150 self.gas_calculator.handle_mem_event(addr, mw_record.prev_timestamp);
151 self.gas_calculator.handle_mem_event(rs1 as u64, rs1_record.prev_timestamp);
152 self.gas_calculator.handle_mem_event(rs2 as u64, rs2_record.prev_timestamp);
153
154 Ok(())
155 }
156}
157
158impl GasEstimatingVM<'_, UserMode> {
159 pub fn execute(&mut self) -> Result<ExecutionReport, ExecutionError> {
161 if self.core.is_done() {
162 return Ok(self.gas_calculator.generate_report());
163 }
164
165 loop {
166 match self.execute_instruction()? {
167 CycleResult::Done(false) => {}
168 CycleResult::TraceEnd | CycleResult::ShardBoundary | CycleResult::Done(true) => {
169 return Ok(self.gas_calculator.generate_report());
170 }
171 }
172 }
173 }
174
175 fn execute_instruction(&mut self) -> Result<CycleResult, ExecutionError> {
177 let FetchResult { instruction, mr_record, pc, error } = self.core.fetch()?;
178
179 if let Some(error) = error {
180 self.handle_error(error)?;
181 self.gas_calculator.handle_page_prot_event(
182 pc >> LOG_PAGE_SIZE,
183 mr_record.unwrap().prev_page_prot_record.unwrap().timestamp,
184 );
185 self.gas_calculator.handle_page_prot_check();
186 self.gas_calculator.handle_trap_exec_event();
187 self.gas_calculator.handle_trap_events(self.core.needs_bump_clk_high());
188 self.gas_calculator.update_page_chip_counts();
189 return Ok(self.core.advance());
190 }
191
192 if instruction.is_none() {
193 unreachable!("Fetching the next instruction failed");
194 }
195
196 if let Some(mr_record) = mr_record {
197 self.gas_calculator.handle_untrusted_instruction();
198 self.gas_calculator.handle_mem_event(pc & !0b111, mr_record.prev_timestamp);
199 self.gas_calculator.handle_page_prot_event(
200 pc >> LOG_PAGE_SIZE,
201 mr_record.prev_page_prot_record.unwrap().timestamp,
202 );
203 self.gas_calculator.handle_page_prot_check();
204 }
205
206 let instruction = unsafe { instruction.unwrap_unchecked() };
208
209 match &instruction.opcode {
210 Opcode::ADD
211 | Opcode::ADDI
212 | Opcode::SUB
213 | Opcode::XOR
214 | Opcode::OR
215 | Opcode::AND
216 | Opcode::SLL
217 | Opcode::SLLW
218 | Opcode::SRL
219 | Opcode::SRA
220 | Opcode::SRLW
221 | Opcode::SRAW
222 | Opcode::SLT
223 | Opcode::SLTU
224 | Opcode::MUL
225 | Opcode::MULHU
226 | Opcode::MULHSU
227 | Opcode::MULH
228 | Opcode::MULW
229 | Opcode::DIVU
230 | Opcode::REMU
231 | Opcode::DIV
232 | Opcode::REM
233 | Opcode::DIVW
234 | Opcode::ADDW
235 | Opcode::SUBW
236 | Opcode::DIVUW
237 | Opcode::REMUW
238 | Opcode::REMW => {
239 self.execute_alu(&instruction);
240 }
241 Opcode::LB
242 | Opcode::LBU
243 | Opcode::LH
244 | Opcode::LHU
245 | Opcode::LW
246 | Opcode::LWU
247 | Opcode::LD => self.execute_load(&instruction)?,
248 Opcode::SB | Opcode::SH | Opcode::SW | Opcode::SD => {
249 self.execute_store(&instruction)?;
250 }
251 Opcode::JAL | Opcode::JALR => {
252 self.execute_jump(&instruction);
253 }
254 Opcode::BEQ | Opcode::BNE | Opcode::BLT | Opcode::BGE | Opcode::BLTU | Opcode::BGEU => {
255 self.execute_branch(&instruction);
256 }
257 Opcode::LUI | Opcode::AUIPC => {
258 self.execute_utype(&instruction);
259 }
260 Opcode::ECALL => self.execute_ecall(&instruction)?,
261 Opcode::EBREAK | Opcode::UNIMP => {
262 unreachable!("Invalid opcode for `execute_instruction`: {:?}", instruction.opcode)
263 }
264 }
265
266 self.gas_calculator.update_page_chip_counts();
267 Ok(self.core.advance())
268 }
269}
270
271impl GasEstimatingVM<'_, UserMode> {
272 pub fn execute_load(&mut self, instruction: &Instruction) -> Result<(), ExecutionError> {
279 let LoadResult { addr, rd, mr_record, error, rr_record, rw_record, rs1, .. } =
280 self.core.execute_load(instruction)?;
281
282 if let Some(error) = error {
283 self.handle_error(error)?;
284 self.gas_calculator.handle_trap_mem_event();
285 } else {
286 self.gas_calculator.handle_mem_event(addr, mr_record.prev_timestamp);
287 }
288
289 if let Some(record) = mr_record.prev_page_prot_record {
290 self.gas_calculator.handle_page_prot_event(record.page_idx, record.timestamp);
291 self.gas_calculator.handle_page_prot_check();
292 }
293
294 self.gas_calculator.handle_mem_event(rs1 as u64, rr_record.prev_timestamp);
295 self.gas_calculator.handle_mem_event(rd as u64, rw_record.prev_timestamp);
296
297 self.gas_calculator.handle_instruction(
298 instruction,
299 self.core.needs_bump_clk_high(),
300 rd == Register::X0,
301 self.core.needs_state_bump(instruction),
302 );
303
304 Ok(())
305 }
306
307 pub fn execute_store(&mut self, instruction: &Instruction) -> Result<(), ExecutionError> {
314 let StoreResult { addr, mw_record, error, rs1_record, rs2_record, rs1, rs2, .. } =
315 self.core.execute_store(instruction)?;
316
317 if let Some(error) = error {
318 self.handle_error(error)?;
319 self.gas_calculator.handle_trap_mem_event();
320 } else {
321 self.gas_calculator.handle_mem_event(addr, mw_record.prev_timestamp);
322 }
323
324 if let Some(record) = mw_record.prev_page_prot_record {
325 self.gas_calculator.handle_page_prot_event(record.page_idx, record.timestamp);
326 self.gas_calculator.handle_page_prot_check();
327 }
328
329 self.gas_calculator.handle_mem_event(rs1 as u64, rs1_record.prev_timestamp);
330 self.gas_calculator.handle_mem_event(rs2 as u64, rs2_record.prev_timestamp);
331
332 self.gas_calculator.handle_instruction(
333 instruction,
334 self.core.needs_bump_clk_high(),
335 false,
336 self.core.needs_state_bump(instruction),
337 );
338
339 Ok(())
340 }
341}
342
343impl<'a, M: ExecutionMode> GasEstimatingVM<'a, M> {
344 pub fn new<T: MinimalTrace>(
346 trace: &'a T,
347 program: Arc<Program>,
348 proof_nonce: [u32; PROOF_NONCE_NUM_WORDS],
349 opts: SP1CoreOpts,
350 ) -> Self {
351 let enable_untrusted_programs = program.enable_untrusted_programs;
352 Self {
353 core: CoreVM::new(trace, program, opts, proof_nonce),
354 gas_calculator: ReportGenerator::new(trace.clk_start(), enable_untrusted_programs),
355 hint_lens_idx: 0,
356 _mode: PhantomData,
357 }
358 }
359
360 pub fn handle_error(&mut self, e: TrapError) -> Result<(), ExecutionError> {
362 let TrapResult { context, code_record, pc_record, handler_record } =
363 self.core.handle_error(e)?;
364
365 self.gas_calculator.handle_mem_event(context, handler_record.prev_timestamp);
366 self.gas_calculator.handle_mem_event(context + 8, code_record.prev_timestamp);
367 self.gas_calculator.handle_mem_event(context + 16, pc_record.prev_timestamp);
368
369 Ok(())
370 }
371
372 #[inline]
374 pub fn execute_alu(&mut self, instruction: &Instruction) {
375 let AluResult { rd, rw_record, rs1, rs2, .. } = self.core.execute_alu(instruction);
376
377 self.gas_calculator.handle_mem_event(rd as u64, rw_record.prev_timestamp);
378
379 if let MaybeImmediate::Register(register, record) = rs1 {
380 self.gas_calculator.handle_mem_event(register as u64, record.prev_timestamp);
381 }
382
383 if let MaybeImmediate::Register(register, record) = rs2 {
384 self.gas_calculator.handle_mem_event(register as u64, record.prev_timestamp);
385 }
386
387 self.gas_calculator.handle_instruction(
388 instruction,
389 self.core.needs_bump_clk_high(),
390 false,
391 self.core.needs_state_bump(instruction),
392 );
393 }
394
395 #[inline]
397 pub fn execute_jump(&mut self, instruction: &Instruction) {
398 let JumpResult { rd, rd_record, rs1, .. } = self.core.execute_jump(instruction);
399
400 self.gas_calculator.handle_mem_event(rd as u64, rd_record.prev_timestamp);
401
402 if let MaybeImmediate::Register(register, record) = rs1 {
403 self.gas_calculator.handle_mem_event(register as u64, record.prev_timestamp);
404 }
405
406 self.gas_calculator.handle_instruction(
407 instruction,
408 self.core.needs_bump_clk_high(),
409 false,
410 self.core.needs_state_bump(instruction),
411 );
412 }
413
414 #[inline]
416 pub fn execute_branch(&mut self, instruction: &Instruction) {
417 let BranchResult { rs1, a_record, rs2, b_record, .. } =
418 self.core.execute_branch(instruction);
419
420 self.gas_calculator.handle_mem_event(rs1 as u64, a_record.prev_timestamp);
421 self.gas_calculator.handle_mem_event(rs2 as u64, b_record.prev_timestamp);
422
423 self.gas_calculator.handle_instruction(
424 instruction,
425 self.core.needs_bump_clk_high(),
426 false,
427 self.core.needs_state_bump(instruction),
428 );
429 }
430
431 #[inline]
433 pub fn execute_utype(&mut self, instruction: &Instruction) {
434 let UTypeResult { rd, rw_record, .. } = self.core.execute_utype(instruction);
435
436 self.gas_calculator.handle_mem_event(rd as u64, rw_record.prev_timestamp);
437
438 self.gas_calculator.handle_instruction(
439 instruction,
440 self.core.needs_bump_clk_high(),
441 false,
442 self.core.needs_state_bump(instruction),
443 );
444 }
445
446 #[inline]
448 pub fn execute_ecall(&mut self, instruction: &Instruction) -> Result<(), ExecutionError> {
449 let code = self.core.read_code();
450
451 if code == SyscallCode::HINT_LEN {
452 self.hint_lens_idx += 1;
453 }
454
455 let result = CoreVM::execute_ecall(self, instruction, code)?;
456
457 if let Some(error) = result.error {
458 self.handle_error(error)?;
459 }
460
461 if let Some(record) = result.sig_return_pc_record {
462 self.gas_calculator.handle_mem_event(result.b, record.prev_timestamp);
463 }
464
465 if code == SyscallCode::HALT {
466 self.gas_calculator.set_exit_code(result.b);
467 }
468
469 if code.should_send() == 1 {
470 if self.core.is_retained_syscall(code) {
471 self.gas_calculator.handle_retained_syscall(code);
472 } else {
473 self.gas_calculator.syscall_sent(code);
474 }
475 }
476
477 self.gas_calculator.handle_instruction(
478 instruction,
479 self.core.needs_bump_clk_high(),
480 false,
481 self.core.needs_state_bump(instruction),
482 );
483
484 Ok(())
485 }
486}
487
488impl<'a, M: ExecutionMode> SyscallRuntime<'a, M> for GasEstimatingVM<'a, M> {
489 const TRACING: bool = false;
490
491 fn core(&self) -> &CoreVM<'a, M> {
492 &self.core
493 }
494
495 fn core_mut(&mut self) -> &mut CoreVM<'a, M> {
496 &mut self.core
497 }
498
499 fn rr(&mut self, register: usize) -> MemoryReadRecord {
500 let record = SyscallRuntime::rr(self.core_mut(), register);
501 self.gas_calculator.handle_mem_event(register as u64, record.prev_timestamp);
502 record
503 }
504
505 fn rw(&mut self, register: usize, value: u64) -> MemoryWriteRecord {
506 let record = SyscallRuntime::rw(self.core_mut(), register, value);
507 self.gas_calculator.handle_mem_event(register as u64, record.prev_timestamp);
508 record
509 }
510
511 fn page_prot_write(&mut self, page_idx: u64, prot: u8) -> PageProtRecord {
512 let prev_page_prot_record = self.core_mut().page_prot_write(page_idx, prot);
513 self.gas_calculator.handle_page_prot_event(
514 prev_page_prot_record.page_idx,
515 prev_page_prot_record.timestamp,
516 );
517 prev_page_prot_record
518 }
519
520 fn page_prot_range_check(
521 &mut self,
522 start_page_idx: u64,
523 end_page_idx: u64,
524 page_prot_bitmap: u8,
525 ) -> (Vec<PageProtRecord>, Option<TrapError>) {
526 let (page_prot_records, error) =
527 self.core_mut().page_prot_range_check(start_page_idx, end_page_idx, page_prot_bitmap);
528 for record in page_prot_records.iter() {
529 self.gas_calculator.handle_page_prot_event(record.page_idx, record.timestamp);
530 }
531 (page_prot_records, error)
532 }
533
534 fn mr_without_prot(&mut self, addr: u64) -> MemoryReadRecord {
535 let record = self.core_mut().mr_without_prot(addr);
536 self.gas_calculator.handle_mem_event(addr, record.prev_timestamp);
537 record
538 }
539
540 fn mw_without_prot(&mut self, addr: u64) -> MemoryWriteRecord {
541 let record = self.core_mut().mw_without_prot(addr);
542 self.gas_calculator.handle_mem_event(addr, record.prev_timestamp);
543 record
544 }
545
546 fn mr_slice_without_prot(&mut self, addr: u64, len: usize) -> Vec<MemoryReadRecord> {
547 let records = self.core_mut().mr_slice_without_prot(addr, len);
548 for (i, record) in records.iter().enumerate() {
549 self.gas_calculator.handle_mem_event(addr + i as u64 * 8, record.prev_timestamp);
550 }
551
552 records
553 }
554
555 fn mw_slice_without_prot(&mut self, addr: u64, len: usize) -> Vec<MemoryWriteRecord> {
556 let records = self.core_mut().mw_slice_without_prot(addr, len);
557 for (i, record) in records.iter().enumerate() {
558 self.gas_calculator.handle_mem_event(addr + i as u64 * 8, record.prev_timestamp);
559 }
560
561 records
562 }
563}
564
565pub enum GasEstimatingVMEnum<'a> {
567 Supervisor(GasEstimatingVM<'a, SupervisorMode>),
569 User(GasEstimatingVM<'a, UserMode>),
571}
572
573impl<'a> GasEstimatingVMEnum<'a> {
574 pub fn new<T: MinimalTrace>(
576 trace: &'a T,
577 program: Arc<Program>,
578 proof_nonce: [u32; PROOF_NONCE_NUM_WORDS],
579 opts: SP1CoreOpts,
580 ) -> Self {
581 if program.enable_untrusted_programs {
582 Self::User(GasEstimatingVM::<UserMode>::new(trace, program, proof_nonce, opts))
583 } else {
584 Self::Supervisor(GasEstimatingVM::<SupervisorMode>::new(
585 trace,
586 program,
587 proof_nonce,
588 opts,
589 ))
590 }
591 }
592
593 pub fn execute(&mut self) -> Result<ExecutionReport, ExecutionError> {
595 match self {
596 Self::Supervisor(vm) => vm.execute(),
597 Self::User(vm) => vm.execute(),
598 }
599 }
600
601 #[must_use]
603 pub fn is_done(&self) -> bool {
604 match self {
605 Self::Supervisor(vm) => vm.core.is_done(),
606 Self::User(vm) => vm.core.is_done(),
607 }
608 }
609
610 #[must_use]
612 pub fn pc(&self) -> u64 {
613 match self {
614 Self::Supervisor(vm) => vm.core.pc(),
615 Self::User(vm) => vm.core.pc(),
616 }
617 }
618
619 #[must_use]
621 pub fn registers(&self) -> [MemoryRecord; 32] {
622 match self {
623 Self::Supervisor(vm) => *vm.core.registers(),
624 Self::User(vm) => *vm.core.registers(),
625 }
626 }
627
628 #[must_use]
630 pub fn exit_code(&self) -> u32 {
631 match self {
632 Self::Supervisor(vm) => vm.core.exit_code(),
633 Self::User(vm) => vm.core.exit_code(),
634 }
635 }
636
637 #[must_use]
639 pub fn clk(&self) -> u64 {
640 match self {
641 Self::Supervisor(vm) => vm.core.clk(),
642 Self::User(vm) => vm.core.clk(),
643 }
644 }
645
646 #[must_use]
648 pub fn public_value_digest(&self) -> [u32; sp1_hypercube::air::PV_DIGEST_NUM_WORDS] {
649 match self {
650 Self::Supervisor(vm) => vm.core.public_value_digest,
651 Self::User(vm) => vm.core.public_value_digest,
652 }
653 }
654
655 #[must_use]
657 pub fn proof_nonce(&self) -> [u32; sp1_hypercube::air::PROOF_NONCE_NUM_WORDS] {
658 match self {
659 Self::Supervisor(vm) => vm.core.proof_nonce,
660 Self::User(vm) => vm.core.proof_nonce,
661 }
662 }
663}