runmat_vm/interpreter/
engine.rs1use crate::bytecode::instr::Instr;
2use crate::bytecode::program::Bytecode;
3use crate::interpreter::timing::InterpreterTiming;
4use crate::runtime::gc::InterpretContext;
5use crate::runtime::workspace::{
6 refresh_workspace_state, set_workspace_state, take_pending_workspace_state, WorkspaceStateGuard,
7};
8use runmat_builtins::Value;
9use runmat_runtime::RuntimeError;
10use std::collections::HashSet;
11
12pub fn prepare_workspace_guard(vars: &mut Vec<Value>) -> Option<WorkspaceStateGuard> {
13 let pending_state = take_pending_workspace_state();
14 let guard = pending_state.map(|(names, assigned)| {
15 let filtered_assigned: HashSet<String> = assigned
16 .into_iter()
17 .filter(|name| names.contains_key(name))
18 .collect();
19 set_workspace_state(names, filtered_assigned, vars)
20 });
21 refresh_workspace_state(vars);
22 guard
23}
24
25pub fn create_gc_context(
26 stack: &Vec<Value>,
27 vars: &Vec<Value>,
28 thread_roots: Vec<Value>,
29) -> Result<InterpretContext, String> {
30 let mut gc_context = InterpretContext::new(stack, vars)?;
31 let _ = gc_context.register_global_values(thread_roots, "thread_globals_persistents");
32 Ok(gc_context)
33}
34
35pub fn debug_stack_enabled() -> bool {
36 std::env::var("RUNMAT_DEBUG_STACK")
37 .map(|v| v == "1" || v.eq_ignore_ascii_case("true"))
38 .unwrap_or(false)
39}
40
41pub fn check_cancelled() -> Result<(), RuntimeError> {
42 if runmat_runtime::interrupt::is_cancelled() {
43 return Err(crate::interpreter::errors::mex(
44 "ExecutionCancelled",
45 "Execution cancelled by user",
46 ));
47 }
48 Ok(())
49}
50
51pub fn note_pre_dispatch(
52 interpreter_timing: &mut InterpreterTiming,
53 debug_stack: bool,
54 pc: usize,
55 instr: &Instr,
56 stack_len: usize,
57) {
58 interpreter_timing.note_host_instr(pc);
59 if debug_stack {
60 log::debug!(
61 "[vm] instr pc={} instr={:?} stack_len={}",
62 pc,
63 instr,
64 stack_len
65 );
66 }
67}
68
69#[cfg(feature = "native-accel")]
70#[inline]
71pub fn fusion_debug_enabled() -> bool {
72 static FLAG: once_cell::sync::OnceCell<bool> = once_cell::sync::OnceCell::new();
73 *FLAG.get_or_init(|| match std::env::var("RUNMAT_DEBUG_FUSION") {
74 Ok(v) => v == "1" || v.eq_ignore_ascii_case("true") || v.eq_ignore_ascii_case("yes"),
75 Err(_) => false,
76 })
77}
78
79#[cfg(feature = "native-accel")]
80pub fn log_fusion_span_window(
81 plan: &runmat_accelerate::FusionGroupPlan,
82 bytecode: &Bytecode,
83 pc: usize,
84) {
85 if !fusion_debug_enabled() || !log::log_enabled!(log::Level::Debug) {
86 return;
87 }
88 if bytecode.instructions.is_empty() {
89 return;
90 }
91 let window = 3usize;
92 let span = plan.group.span.clone();
93 let total = bytecode.instructions.len();
94 let start = span.start.saturating_sub(window);
95 let mut end = span.end + window;
96 if end >= total {
97 end = total.saturating_sub(1);
98 }
99 if end < span.end {
100 end = span.end;
101 }
102 let mut ops: Vec<String> = Vec::new();
103 for idx in start..=end {
104 let instr = &bytecode.instructions[idx];
105 let mut tags: Vec<&'static str> = Vec::new();
106 if idx == pc {
107 tags.push("pc");
108 }
109 if idx == span.start {
110 tags.push("start");
111 }
112 if idx == span.end {
113 tags.push("end");
114 }
115 let tag_str = if tags.is_empty() {
116 String::new()
117 } else {
118 format!("<{}>", tags.join(","))
119 };
120 ops.push(format!("{}{} {:?}", idx, tag_str, instr));
121 }
122 log::debug!(
123 "fusion plan {} span window [{}..{}]: {}",
124 plan.index,
125 start,
126 end,
127 ops.join(" | ")
128 );
129}
130
131#[cfg(feature = "native-accel")]
132pub fn note_fusion_gate(
133 interpreter_timing: &mut InterpreterTiming,
134 plan: &runmat_accelerate::FusionGroupPlan,
135 bytecode: &Bytecode,
136 pc: usize,
137 has_barrier: bool,
138 live_result_count: Option<usize>,
139) {
140 let detail = format!(
141 "plan={} kind={:?} span=[{}..{}]",
142 plan.index, plan.group.kind, plan.group.span.start, plan.group.span.end
143 );
144 interpreter_timing.flush_host_span("before_fusion", Some(detail.as_str()));
145 log_fusion_span_window(plan, bytecode, pc);
146 if fusion_debug_enabled() {
147 log::trace!(
148 "fusion gate pc={} kind={:?} span={}..{} has_barrier={} live_results={:?}",
149 pc,
150 plan.group.kind,
151 plan.group.span.start,
152 plan.group.span.end,
153 has_barrier,
154 live_result_count
155 );
156 }
157}
158
159#[cfg(feature = "native-accel")]
160pub fn note_fusion_skip(pc: usize, span: &runmat_accelerate::InstrSpan) {
161 if fusion_debug_enabled() {
162 log::debug!(
163 "fusion skip at pc {}: side-effecting instrs in span {}..{}",
164 pc,
165 span.start,
166 span.end
167 );
168 }
169}