Skip to main content

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}