Skip to main content

vihaco_stdlib/
program.rs

1// SPDX-FileCopyrightText: 2026 The vihaco Authors
2// SPDX-License-Identifier: MIT
3
4//! Loaded program state and host access to section-local code and metadata.
5//!
6//! Equal string contents reuse the same section-local index:
7//!
8//! ```
9//! # #[cfg(all(feature = "typed-declarations", not(feature = "program-image-execution")))]
10//! # {
11//! use vihaco_runtime::{Lower, Parse, Syntax, chumsky::Parser as _};
12//! use vihaco_bytecode::NoContext;
13//! use vihaco_stdlib::ProgramImage;
14//! use vihaco_cpu::cpu;
15//! let mut image = ProgramImage::<(), NoContext>::default();
16//! for _ in 0..2 {
17//!     let source = cpu::ConstString::<Syntax>::parser().parse("const_string \"hello\"")
18//!         .into_result().unwrap();
19//!     assert_eq!(source.lower(&mut image).unwrap(), cpu::ConstString(0));
20//! }
21//! assert_eq!(image.module.strings, ["hello"]);
22//! # }
23//! ```
24
25#[cfg(feature = "program-image-execution")]
26mod dialect;
27
28#[cfg(feature = "program-image-execution")]
29pub use dialect::program;
30
31#[cfg(feature = "program-image-execution")]
32mod literal;
33
34#[cfg(feature = "program-image-execution")]
35pub mod lower;
36
37#[cfg(feature = "program-image-execution")]
38pub mod operand;
39
40use vihaco_abi::{Type, Value};
41use vihaco_bytecode::{ConstantId, ContextHandle};
42use vihaco_module::{
43    host::{GetProgramInfo, ProgramCounter},
44    module::{LocalModule, NoInfo},
45};
46
47/// A loaded section, its shared context, and current program counter.
48#[derive(Debug, Clone)]
49pub struct ProgramImage<I, C, V = Value, Ty = Type, Info = NoInfo> {
50    pub module: LocalModule<I, V, Ty, Info>,
51    pub context: Option<ContextHandle<C>>,
52    pub pc: u32,
53}
54
55impl<I, C, V, Ty, Info: Default> Default for ProgramImage<I, C, V, Ty, Info> {
56    fn default() -> Self {
57        Self {
58            module: LocalModule::default(),
59            context: None,
60            pc: 0,
61        }
62    }
63}
64
65impl<I, C, V, Ty, Info> ProgramImage<I, C, V, Ty, Info> {
66    pub fn new() -> Self
67    where
68        Info: Default,
69    {
70        Self::default()
71    }
72
73    pub fn with_extra(extra: Info) -> Self {
74        Self {
75            module: LocalModule {
76                code: Vec::new(),
77                functions: Vec::new(),
78                labels: Vec::new(),
79                constants: Vec::new(),
80                strings: Vec::new(),
81                main_function: None,
82                source_symbols: Vec::new(),
83                extra,
84            },
85            context: None,
86            pc: 0,
87        }
88    }
89
90    pub fn context(&self) -> eyre::Result<&C> {
91        self.context
92            .as_ref()
93            .map(ContextHandle::get)
94            .ok_or_else(|| eyre::eyre!("program image has not been loaded with a context"))
95    }
96}
97
98impl<I, C, V, Ty, Info> ProgramCounter for ProgramImage<I, C, V, Ty, Info> {
99    type Instruction = I;
100
101    fn pc(&self) -> u32 {
102        self.pc
103    }
104
105    fn pc_mut(&mut self) -> &mut u32 {
106        &mut self.pc
107    }
108
109    fn get_instruction(&self, pc: u32) -> eyre::Result<&Self::Instruction> {
110        self.module.code.get(pc as usize).ok_or_else(|| {
111            eyre::eyre!(format!(
112                "program counter out of bounds: {} (max {})",
113                pc,
114                self.module.code.len()
115            ))
116        })
117    }
118}
119
120impl<I, C, V, Ty, Info> GetProgramInfo for ProgramImage<I, C, V, Ty, Info>
121where
122    Ty: Clone,
123{
124    type Type = Ty;
125    type Value = V;
126
127    fn get_function(
128        &self,
129        index: usize,
130    ) -> eyre::Result<vihaco_module::module::FunctionInfo<Self::Type>> {
131        self.module.functions.get(index).cloned().ok_or_else(|| {
132            eyre::eyre!(format!(
133                "function index out of bounds: {} (max {})",
134                index,
135                self.module.functions.len()
136            ))
137        })
138    }
139
140    fn get_string(&self, index: usize) -> eyre::Result<&String> {
141        self.module.strings.get(index).ok_or_else(|| {
142            eyre::eyre!(format!(
143                "string index out of bounds: {} (max {})",
144                index,
145                self.module.strings.len()
146            ))
147        })
148    }
149
150    fn get_constant(&self, id: ConstantId) -> eyre::Result<&Self::Value> {
151        self.module.constants.get(id.0 as usize).ok_or_else(|| {
152            eyre::eyre!(format!(
153                "constant index out of bounds: {} (max {})",
154                id.0,
155                self.module.constants.len()
156            ))
157        })
158    }
159}
160
161#[cfg(all(
162    feature = "typed-declarations",
163    not(feature = "program-image-execution")
164))]
165impl<I, C, V, Ty, Info> vihaco_cpu::lower::StringIntern for ProgramImage<I, C, V, Ty, Info> {
166    fn intern_string(&mut self, value: &str) -> eyre::Result<u32> {
167        if let Some(index) = self
168            .module
169            .strings
170            .iter()
171            .position(|existing| existing == value)
172        {
173            return Ok(u32::try_from(index)?);
174        }
175        let index = u32::try_from(self.module.strings.len())?;
176        self.module.strings.push(value.to_owned());
177        Ok(index)
178    }
179}