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