vihaco_syntax/lower.rs
1// SPDX-FileCopyrightText: 2026 The vihaco Authors
2// SPDX-License-Identifier: MIT
3
4//! Instruction-set-independent lowering of parsed function bodies.
5
6use crate::{ParsedFunction, ParsedModule};
7use eyre::WrapErr;
8use vihaco_abi::{Lower, Value};
9use vihaco_module::loader::FunctionScope;
10use vihaco_module::module::{LocalModule, ProgramMetadata};
11
12/// Lower functions in source order, preparing each function's complete label
13/// scope before lowering its first instruction.
14///
15/// Each syntax instruction lowers to one runtime instruction. Errors include
16/// function and instruction context; no partial code vector is returned.
17/// Context mutations before an error are not rolled back.
18pub fn lower_functions<I, Ty, C>(
19 functions: Vec<ParsedFunction<I, Ty>>,
20 context: &mut C,
21) -> eyre::Result<Vec<I::Runtime>>
22where
23 I: Lower<C>,
24 C: FunctionScope,
25{
26 let mut code = Vec::new();
27 for function in functions {
28 let name = function.name.as_str();
29 #[cfg(feature = "typed-declarations")]
30 let labels = function
31 .labels
32 .iter()
33 .map(|label| (label.name.as_str(), label.instruction_index));
34 #[cfg(not(feature = "typed-declarations"))]
35 let labels = std::iter::empty();
36 context.enter_function(name, labels)?;
37 for (index, instruction) in function.body.into_iter().enumerate() {
38 code.push(
39 instruction
40 .lower(context)
41 .wrap_err_with(|| format!("lowering function @{name}, instruction {index}"))?,
42 );
43 }
44 }
45 Ok(code)
46}
47
48fn validate_program_functions<I, Ty: PartialEq, V, Info>(
49 functions: &[ParsedFunction<I, Ty>],
50 metadata: &ProgramMetadata<V, Ty, Info>,
51) -> eyre::Result<()> {
52 crate::types::validate_functions(functions, metadata)?;
53 metadata.validate(functions.iter().map(|function| function.body.len()).sum())
54}
55
56/// Validate supplied metadata and lower function bodies without consuming it.
57///
58/// Available with `program-image-execution`. The caller prepares the metadata
59/// and context independently, for example by processing individual header
60/// declarations. The context may borrow prepared configuration or the metadata
61/// itself; neither is moved or cloned by this function.
62///
63/// Function declarations and table bounds are validated before lowering starts.
64/// [`lower_functions`] prepares label scopes and preserves instruction error
65/// context. The returned code can be assembled with
66/// [`ProgramMetadata::into_module`], which also checks standard value references.
67///
68/// # Errors
69/// Returns declaration, table, or instruction-lowering errors. No partial code
70/// is returned; earlier context mutations are not rolled back.
71#[cfg(feature = "program-image-execution")]
72pub fn lower_functions_with_metadata<I, Ty, V, Info, C>(
73 functions: Vec<ParsedFunction<I, Ty>>,
74 metadata: &ProgramMetadata<V, Ty, Info>,
75 context: &mut C,
76) -> eyre::Result<Vec<I::Runtime>>
77where
78 Ty: PartialEq,
79 I: Lower<C>,
80 C: FunctionScope,
81{
82 validate_program_functions(&functions, metadata)?;
83 lower_functions(functions, context)
84}
85
86impl<I, Ty: PartialEq, H> ParsedModule<I, Ty, H> {
87 /// Validate declarations and tables, lower instructions with the supplied
88 /// context, and assemble a module without inventing or reordering metadata.
89 ///
90 /// The consumer extracts the metadata from its chosen header schema and
91 /// prepares section-wide context capabilities before calling this method.
92 /// [`lower_functions`] prepares function-local scopes. Standard CPU symbol
93 /// capabilities are provided by `MetadataLoweringContext` in `vihaco-module`.
94 /// Additional capabilities can be supplied by a consumer-owned context.
95 /// The header itself is not interpreted by this method.
96 ///
97 /// # Errors
98 /// Returns declaration, table, or lowering errors. Context mutations before
99 /// an error are retained; no partial module is returned.
100 pub fn lower_with_metadata<C, Info>(
101 self,
102 metadata: ProgramMetadata<Value, Ty, Info>,
103 context: &mut C,
104 ) -> eyre::Result<LocalModule<I::Runtime, Value, Ty, Info>>
105 where
106 I: Lower<C>,
107 C: FunctionScope,
108 {
109 validate_program_functions(&self.functions, &metadata)?;
110 let code = lower_functions(self.functions, context)?;
111 metadata.into_module(code)
112 }
113}
114
115#[cfg(test)]
116mod tests;