Files
hakorune/src/mir/hints.rs
Selfhosting Dev c8063c9e41 pyvm: split op handlers into ops_core/ops_box/ops_ctrl; add ops_flow + intrinsic; delegate vm.py without behavior change
net-plugin: modularize constants (consts.rs) and sockets (sockets.rs); remove legacy commented socket code; fix unused imports
mir: move instruction unit tests to tests/mir_instruction_unit.rs (file lean-up); no semantic changes
runner/pyvm: ensure using pre-strip; misc docs updates

Build: cargo build ok; legacy cfg warnings remain as before
2025-09-21 08:53:00 +09:00

206 lines
9.3 KiB
Rust

#![allow(dead_code)]
//! MIR Hints — zero-cost structural guidance (scaffold)
//!
//! Hints guide lowering/verification without affecting semantics.
//! They must be stripped before final IR emission.
/// Lightweight set of hint kinds (scaffold).
#[derive(Debug, Clone)]
pub enum HintKind {
ScopeEnter(u32),
ScopeLeave(u32),
Defer(Vec<String>),
JoinResult(String),
LoopCarrier(Vec<String>),
LoopHeader,
LoopLatch,
NoEmptyPhi,
}
/// Hint sink (no-op). Backends/resolvers may hook into this later.
#[derive(Default)]
pub struct HintSink {
enabled: bool,
}
impl HintSink {
pub fn new() -> Self { Self { enabled: false } }
pub fn with_enabled(mut self, enabled: bool) -> Self { self.enabled = enabled; self }
fn cfg() -> HintCfg {
// New unified env: NYASH_MIR_HINTS="<target>|<filters>"
// Examples:
// NYASH_MIR_HINTS=trace|all -> stderr + all kinds
// NYASH_MIR_HINTS=tmp/hints.jsonl|loop -> jsonl file + loop-only
// NYASH_MIR_HINTS=jsonl=tmp/h.jsonl|scope|join
// Back-compat: NYASH_MIR_TRACE_HINTS=1 -> stderr + all kinds
if let Ok(spec) = std::env::var("NYASH_MIR_HINTS") {
return HintCfg::parse(&spec);
}
if std::env::var("NYASH_MIR_TRACE_HINTS").ok().as_deref() == Some("1") {
return HintCfg { sink: HintSinkTarget::Stderr, kinds: HintKinds::All };
}
HintCfg { sink: HintSinkTarget::None, kinds: HintKinds::None }
}
#[inline]
pub fn record(&mut self, hint: HintKind) {
// Resolve config (env-based). Lightweight and robust; acceptable to parse per call.
let cfg = Self::cfg();
if matches!(cfg.sink, HintSinkTarget::None) { return; }
// Filter kinds
let k = hint_tag(&hint);
if !cfg.kinds.contains(k) { return; }
match cfg.sink {
HintSinkTarget::None => {}
HintSinkTarget::Stderr => {
match hint {
HintKind::ScopeEnter(id) => eprintln!("[mir][hint] ScopeEnter({})", id),
HintKind::ScopeLeave(id) => eprintln!("[mir][hint] ScopeLeave({})", id),
HintKind::Defer(calls) => eprintln!("[mir][hint] Defer({})", calls.join(";")),
HintKind::JoinResult(var) => eprintln!("[mir][hint] JoinResult({})", var),
HintKind::LoopCarrier(vars) => eprintln!("[mir][hint] LoopCarrier({})", vars.join(",")),
HintKind::LoopHeader => eprintln!("[mir][hint] LoopHeader"),
HintKind::LoopLatch => eprintln!("[mir][hint] LoopLatch"),
HintKind::NoEmptyPhi => eprintln!("[mir][hint] NoEmptyPhi"),
}
}
HintSinkTarget::Jsonl(ref path) => {
// Append one JSON object per line. Best-effort; ignore errors.
let _ = append_jsonl(path, &hint);
}
}
}
#[inline] pub fn scope_enter(&mut self, id: u32) { self.record(HintKind::ScopeEnter(id)); }
#[inline] pub fn scope_leave(&mut self, id: u32) { self.record(HintKind::ScopeLeave(id)); }
#[inline] pub fn defer_calls<S: Into<String>>(&mut self, calls: impl IntoIterator<Item = S>) {
self.record(HintKind::Defer(calls.into_iter().map(|s| s.into()).collect()))
}
#[inline] pub fn join_result<S: Into<String>>(&mut self, var: S) { self.record(HintKind::JoinResult(var.into())); }
#[inline] pub fn loop_carrier<S: Into<String>>(&mut self, vars: impl IntoIterator<Item = S>) {
self.record(HintKind::LoopCarrier(vars.into_iter().map(|s| s.into()).collect()))
}
#[inline] pub fn loop_header(&mut self) { self.record(HintKind::LoopHeader); }
#[inline] pub fn loop_latch(&mut self) { self.record(HintKind::LoopLatch); }
#[inline] pub fn no_empty_phi(&mut self) { self.record(HintKind::NoEmptyPhi); }
}
// ---- Unified hint config parser ----
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum HintTag { Scope, Join, Loop, Phi }
fn hint_tag(h: &HintKind) -> HintTag {
match h {
HintKind::ScopeEnter(_) | HintKind::ScopeLeave(_) | HintKind::Defer(_) => HintTag::Scope,
HintKind::JoinResult(_) => HintTag::Join,
HintKind::LoopCarrier(_) | HintKind::LoopHeader | HintKind::LoopLatch => HintTag::Loop,
HintKind::NoEmptyPhi => HintTag::Phi,
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum HintKinds { None, Some { scope: bool, join: bool, loopk: bool, phi: bool }, All }
impl HintKinds {
fn contains(&self, tag: HintTag) -> bool {
match self {
HintKinds::All => true,
HintKinds::None => false,
HintKinds::Some { scope, join, loopk, phi } => match tag {
HintTag::Scope => *scope,
HintTag::Join => *join,
HintTag::Loop => *loopk,
HintTag::Phi => *phi,
},
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
enum HintSinkTarget { None, Stderr, Jsonl(String) }
#[derive(Debug, Clone, PartialEq, Eq)]
struct HintCfg { sink: HintSinkTarget, kinds: HintKinds }
impl HintCfg {
fn parse(spec: &str) -> Self {
let mut sink = HintSinkTarget::None;
let mut kinds = HintKinds::None;
let mut saw_filter = false;
for tok in spec.split('|').map(|s| s.trim()).filter(|s| !s.is_empty()) {
let tl = tok.to_ascii_lowercase();
if tl == "off" { sink = HintSinkTarget::None; kinds = HintKinds::None; continue; }
if tl == "trace" || tl == "stderr" { sink = HintSinkTarget::Stderr; continue; }
if tl.starts_with("jsonl=") {
sink = HintSinkTarget::Jsonl(tok[6..].trim().to_string());
continue;
}
// Heuristic: token looks like a path → jsonl
if tok.contains('/') || tok.contains('.') {
sink = HintSinkTarget::Jsonl(tok.to_string());
continue;
}
// Filters
match tl.as_str() {
"all" => { kinds = HintKinds::All; saw_filter = true; }
"scope" => kinds = merge_kind(kinds, |k| HintKinds::Some { scope: true, join: matches!(k, HintKinds::Some{ join: true, .. } | HintKinds::All), loopk: matches!(k, HintKinds::Some{ loopk: true, .. } | HintKinds::All), phi: matches!(k, HintKinds::Some{ phi: true, .. } | HintKinds::All) }),
"join" => kinds = merge_kind(kinds, |k| HintKinds::Some { scope: matches!(k, HintKinds::Some{ scope: true, .. } | HintKinds::All), join: true, loopk: matches!(k, HintKinds::Some{ loopk: true, .. } | HintKinds::All), phi: matches!(k, HintKinds::Some{ phi: true, .. } | HintKinds::All) }),
"loop" => kinds = merge_kind(kinds, |k| HintKinds::Some { scope: matches!(k, HintKinds::Some{ scope: true, .. } | HintKinds::All), join: matches!(k, HintKinds::Some{ join: true, .. } | HintKinds::All), loopk: true, phi: matches!(k, HintKinds::Some{ phi: true, .. } | HintKinds::All) }),
"phi" => kinds = merge_kind(kinds, |k| HintKinds::Some { scope: matches!(k, HintKinds::Some{ scope: true, .. } | HintKinds::All), join: matches!(k, HintKinds::Some{ join: true, .. } | HintKinds::All), loopk: matches!(k, HintKinds::Some{ loopk: true, .. } | HintKinds::All), phi: true }),
_ => {}
}
}
if !saw_filter && !matches!(kinds, HintKinds::All) {
// default to all if no filter specified
kinds = HintKinds::All;
}
// default sink if only filters appear
if matches!(sink, HintSinkTarget::None) {
sink = HintSinkTarget::Stderr;
}
HintCfg { sink, kinds }
}
}
fn merge_kind<F: FnOnce(HintKinds) -> HintKinds>(k: HintKinds, f: F) -> HintKinds {
match k {
HintKinds::All => HintKinds::All,
x => f(x),
}
}
fn append_jsonl(path: &str, hint: &HintKind) -> std::io::Result<()> {
use std::io::Write;
let mut obj = serde_json::json!({ "kind": kind_name(hint) });
match hint {
HintKind::ScopeEnter(id) => obj["value"] = serde_json::json!({"enter": id}),
HintKind::ScopeLeave(id) => obj["value"] = serde_json::json!({"leave": id}),
HintKind::Defer(calls) => obj["value"] = serde_json::json!({"defer": calls}),
HintKind::JoinResult(v) => obj["value"] = serde_json::json!({"join": v}),
HintKind::LoopCarrier(vs) => obj["value"] = serde_json::json!({"carrier": vs}),
HintKind::LoopHeader => obj["value"] = serde_json::json!({"loop": "header"}),
HintKind::LoopLatch => obj["value"] = serde_json::json!({"loop": "latch"}),
HintKind::NoEmptyPhi => obj["value"] = serde_json::json!({"phi": "no_empty"}),
}
let line = obj.to_string();
if let Some(dir) = std::path::Path::new(path).parent() { let _ = std::fs::create_dir_all(dir); }
let mut f = std::fs::OpenOptions::new().create(true).append(true).open(path)?;
writeln!(f, "{}", line)?;
Ok(())
}
fn kind_name(h: &HintKind) -> &'static str {
match h {
HintKind::ScopeEnter(_) => "ScopeEnter",
HintKind::ScopeLeave(_) => "ScopeLeave",
HintKind::Defer(_) => "Defer",
HintKind::JoinResult(_) => "JoinResult",
HintKind::LoopCarrier(_) => "LoopCarrier",
HintKind::LoopHeader => "LoopHeader",
HintKind::LoopLatch => "LoopLatch",
HintKind::NoEmptyPhi => "NoEmptyPhi",
}
}