1use crate::bytecode::program::ExecutionContext;
2use crate::bytecode::Bytecode;
3use log::debug;
4#[cfg(feature = "native-accel")]
5use runmat_accelerate::graph::AccelGraph;
6#[cfg(feature = "native-accel")]
7use runmat_accelerate::{prepare_fusion_plan, FusionPlan};
8use runmat_builtins::Value;
9use std::collections::{HashMap, HashSet};
10#[cfg(feature = "native-accel")]
11use std::sync::Arc;
12
13#[derive(Debug)]
14pub enum InterpreterOutcome {
15 Completed(Vec<Value>),
16}
17
18#[derive(Debug)]
19pub struct InterpreterState {
20 pub bytecode: Bytecode,
21 pub stack: Vec<Value>,
22 pub vars: Vec<Value>,
23 pub pc: usize,
24 pub context: ExecutionContext,
25 pub try_stack: Vec<(usize, Option<usize>)>,
26 pub last_exception: Option<runmat_builtins::MException>,
27 pub imports: Vec<(Vec<String>, bool)>,
28 pub global_aliases: HashMap<usize, String>,
29 pub persistent_aliases: HashMap<usize, String>,
30 pub missing_input_slots: HashSet<usize>,
31 pub current_function_name: String,
32 pub call_counts: Vec<(usize, usize)>,
33 pub initial_assigned_var_count: usize,
34 #[cfg(feature = "native-accel")]
35 pub fusion_plan: Option<Arc<FusionPlan>>,
36 #[cfg(feature = "native-accel")]
37 pub fusion_accel_graph: Option<AccelGraph>,
38}
39
40impl InterpreterState {
41 pub fn new(
42 bytecode: Bytecode,
43 initial_vars: &mut [Value],
44 current_function_name: Option<&str>,
45 call_counts: Vec<(usize, usize)>,
46 ) -> Self {
47 let initial_assigned_var_count = initial_vars.len();
48 let mut vars = initial_vars.to_vec();
49 if vars.len() < bytecode.var_count {
50 vars.resize(bytecode.var_count, Value::Num(0.0));
51 }
52 if bytecode.async_metadata.mir_spawn_site_count > 0
53 || bytecode.async_metadata.mir_await_site_count > 0
54 {
55 debug!(
56 "async semantics: compiled bytecode carries {} MIR spawn site(s) and {} MIR await site(s); runtime model={} with explicit spawn/await bytecode boundaries",
57 bytecode.async_metadata.mir_spawn_site_count,
58 bytecode.async_metadata.mir_await_site_count,
59 bytecode.async_metadata.runtime_model.as_str()
60 );
61 }
62 #[cfg(feature = "native-accel")]
63 let (fusion_plan, fusion_accel_graph) = {
64 let runtime_groups = bytecode.runtime_fusion_groups();
67 let runtime_graph = bytecode.runtime_accel_graph_for_fusion(&runtime_groups);
68 let runtime_groups = if let Some(graph) = runtime_graph.as_ref() {
69 bytecode.runtime_fusion_groups_for_graph(graph)
70 } else {
71 runtime_groups
72 };
73 let fusion_plan = prepare_fusion_plan(
74 runtime_graph.as_ref(),
75 &runtime_groups,
76 bytecode.fusion_metadata.mir_fusion_candidate_group_count,
77 );
78 (fusion_plan, runtime_graph)
79 };
80 Self {
81 stack: Vec::new(),
82 context: ExecutionContext {
83 call_stack: Vec::new(),
84 locals: Vec::new(),
85 instruction_pointer: 0,
86 spawned_task_ids: std::collections::HashSet::new(),
87 next_spawn_task_id: 0,
88 },
89 try_stack: Vec::new(),
90 last_exception: None,
91 imports: Vec::new(),
92 global_aliases: HashMap::new(),
93 persistent_aliases: HashMap::new(),
94 missing_input_slots: HashSet::new(),
95 vars,
96 pc: 0,
97 call_counts,
98 initial_assigned_var_count,
99 current_function_name: current_function_name
100 .map(|s| s.to_string())
101 .unwrap_or_else(|| "<main>".to_string()),
102 #[cfg(feature = "native-accel")]
103 fusion_plan,
104 #[cfg(feature = "native-accel")]
105 fusion_accel_graph,
106 bytecode,
107 }
108 }
109}
110
111#[cfg(all(test, feature = "native-accel"))]
112mod tests {
113 use super::InterpreterState;
114 use crate::bytecode::{Bytecode, FusionInstructionKind, FusionInstructionWindow};
115 use runmat_accelerate::graph::{AccelNodeLabel, InstrSpan, PrimitiveOp};
116
117 #[test]
118 fn runtime_materialized_graph_is_retained_for_fusion_execution() {
119 let mut bytecode = Bytecode::empty();
120 bytecode.instructions = vec![
121 crate::Instr::LoadVar(0),
122 crate::Instr::LoadVar(1),
123 crate::Instr::Add,
124 ];
125 bytecode.var_types = vec![
126 runmat_builtins::Type::Num,
127 runmat_builtins::Type::Num,
128 runmat_builtins::Type::Num,
129 ];
130 bytecode.fusion_metadata.mir_fusion_candidate_group_count = 1;
131 bytecode.fusion_metadata.instruction_windows = vec![FusionInstructionWindow {
132 span: InstrSpan { start: 2, end: 2 },
133 kind: FusionInstructionKind::Elementwise,
134 }];
135
136 let mut initial_vars = Vec::new();
137 let state = InterpreterState::new(bytecode, &mut initial_vars, Some("<main>"), Vec::new());
138 assert!(
139 state.fusion_plan.is_some(),
140 "expected runtime fusion plan when semantic windows exist"
141 );
142 assert!(
143 state.fusion_accel_graph.is_some(),
144 "expected runtime accel graph to be retained for fusion execution"
145 );
146 }
147
148 #[test]
149 fn runtime_state_ignores_stale_compile_graph_metadata() {
150 let mut bytecode = Bytecode::empty();
151 bytecode.instructions = vec![
152 crate::Instr::LoadVar(0),
153 crate::Instr::LoadVar(1),
154 crate::Instr::Add,
155 ];
156 bytecode.var_types = vec![
157 runmat_builtins::Type::Num,
158 runmat_builtins::Type::Num,
159 runmat_builtins::Type::Num,
160 ];
161 let stale_graph = crate::accel::graph::build_accel_graph(
162 &[
163 crate::Instr::LoadVar(0),
164 crate::Instr::LoadVar(1),
165 crate::Instr::Mul,
166 ],
167 &bytecode.var_types,
168 );
169 bytecode.accel_graph = Some(stale_graph);
170 bytecode.fusion_metadata.mir_fusion_candidate_group_count = 1;
171 bytecode.fusion_metadata.instruction_windows = vec![FusionInstructionWindow {
172 span: InstrSpan { start: 2, end: 2 },
173 kind: FusionInstructionKind::Elementwise,
174 }];
175
176 let mut initial_vars = Vec::new();
177 let state = InterpreterState::new(bytecode, &mut initial_vars, Some("<main>"), Vec::new());
178 let graph = state
179 .fusion_accel_graph
180 .as_ref()
181 .expect("expected runtime accel graph to be retained");
182 assert!(
183 graph
184 .nodes
185 .iter()
186 .any(|node| matches!(node.label, AccelNodeLabel::Primitive(PrimitiveOp::Add))),
187 "runtime retained graph should reflect active bytecode instructions"
188 );
189 assert!(
190 !graph
191 .nodes
192 .iter()
193 .any(|node| matches!(node.label, AccelNodeLabel::Primitive(PrimitiveOp::Mul))),
194 "stale compile graph metadata should not be retained in runtime state"
195 );
196 }
197}