vihaco_stdlib/execution_state/runner.rs
1// SPDX-FileCopyrightText: 2026 The vihaco Authors
2// SPDX-License-Identifier: MIT
3
4use super::ExecutionState;
5use super::instruction_out_of_bounds;
6use vihaco_abi::word::Word;
7use vihaco_module::module::LocalModule;
8use vihaco_runtime::{Dispatch, HasProgram};
9
10/// Enter main and execute through the composite's authored Steps.
11///
12/// Devices remain in place. Instructions borrow the caller-owned module; there
13/// is no instruction clone, generated Step, or imposed effect-application policy.
14///
15/// # Errors
16/// Returns entry, checked-fetch, or authored Dispatch/Step errors. Operand and
17/// component mutations made before an error are retained.
18pub fn execute<M, I, V, Ty, Info>(
19 module: &LocalModule<I, V, Ty, Info>,
20 machine: &mut M,
21 arguments: &[Word],
22) -> eyre::Result<()>
23where
24 M: HasProgram<Program = ExecutionState>
25 + Dispatch<LocalModule<I, V, Ty, Info>, Instruction = I>,
26{
27 machine.program_mut().start(module, arguments)?;
28 resume(module, machine)
29}
30
31/// Continue an invocation until Halt/root return or an error.
32///
33/// Fetch establishes the sequential continuation before the Step runs. Program
34/// instructions can replace that continuation. A fault or terminal instruction
35/// leaves its original position available through [`ExecutionState::pc()`].
36///
37/// The caller must supply the same unchanged module used to start the active
38/// invocation. This checked API validates accesses, but does not enforce module
39/// identity. After an error, earlier operand consumption and effects remain;
40/// the caller is responsible for deciding whether the invocation can resume.
41///
42/// # Errors
43/// Rejects reentrant execution, invalid running state, invalid fetches, PC
44/// overflow, or a failure returned by the authored Dispatch/Step.
45pub fn resume<M, I, V, Ty, Info>(
46 module: &LocalModule<I, V, Ty, Info>,
47 machine: &mut M,
48) -> eyre::Result<()>
49where
50 M: HasProgram<Program = ExecutionState>
51 + Dispatch<LocalModule<I, V, Ty, Info>, Instruction = I>,
52{
53 let program = machine.program_mut();
54 eyre::ensure!(
55 !program.executing,
56 "program execution is already in progress"
57 );
58 program.executing = true;
59 // Capture code once from the independent module borrow.
60 let code = &module.code;
61 let result = (|| {
62 while !machine.program().halted {
63 let program = machine.program_mut();
64 program.ensure_running()?;
65 let position = program.pc;
66 let instruction = code
67 .get(position as usize)
68 .ok_or_else(|| instruction_out_of_bounds(position, code.len()))?;
69 program.pc = position
70 .checked_add(1)
71 .ok_or_else(|| eyre::eyre!("program counter overflow"))?;
72 // `instruction` borrows the external module, never machine.
73 let result = machine.dispatch(instruction, module);
74 let program = machine.program_mut();
75 if result.is_err() || program.halted {
76 program.pc = position;
77 }
78 result?;
79 }
80 Ok(())
81 })();
82 machine.program_mut().executing = false;
83 result
84}