Skip to main content

runmat_vm/interpreter/
engine.rs

1use 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}