- Add CoreServicesConfig struct (from_env, minimal, all_enabled) - Implement with_core_from_registry_optional() for selective initialization - Update CoreBoxesImpl fields to Option<Arc<dyn XyzService>> - Maintain backward compatibility (with_core_from_registry calls all_enabled) - Add NYASH_CORE_DISABLE_* environment variable support - ConsoleBox remains mandatory (Graceful Degradation principle) - Add unit tests for optional initialization - Update console_println! macro to handle Option type - Fix direct console.println() calls in vm.rs and selfhost.rs - Create core_optional_design.md documentation Note: Phase 104 will extend ConsoleService to be optional as well with graceful fallback in console_println! macro. Files modified: - src/runtime/plugin_host.rs (CoreServicesConfig, with_core_from_registry_optional, tests) - src/runtime/core_services.rs (CoreBoxesImpl fields → Option type) - src/runtime/mod.rs (console_println! macro updated) - src/runner/modes/vm.rs (handle Option console) - src/runner/selfhost.rs (handle Option console) - docs/development/current/main/core_optional_design.md (new) - docs/development/current/main/ring0-inventory.md (Phase 103 entry) Test results: - Build: ✅ Success (0 errors, 7 warnings) - Unit tests: ✅ 3/3 passed (optional_core_tests) - Runtime tests: ✅ 63/63 passed - Smoke tests: ✅ 30/31 passed (1 pre-existing timeout)
334 lines
11 KiB
Rust
334 lines
11 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.
|
|
|
|
use crate::runtime::get_global_ring0;
|
|
|
|
/// 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) => {
|
|
get_global_ring0()
|
|
.log
|
|
.debug(&format!("[mir][hint] ScopeEnter({})", id))
|
|
}
|
|
HintKind::ScopeLeave(id) => {
|
|
get_global_ring0()
|
|
.log
|
|
.debug(&format!("[mir][hint] ScopeLeave({})", id))
|
|
}
|
|
HintKind::Defer(calls) => get_global_ring0()
|
|
.log
|
|
.debug(&format!("[mir][hint] Defer({})", calls.join(";"))),
|
|
HintKind::JoinResult(var) => get_global_ring0()
|
|
.log
|
|
.debug(&format!("[mir][hint] JoinResult({})", var)),
|
|
HintKind::LoopCarrier(vars) => {
|
|
get_global_ring0()
|
|
.log
|
|
.debug(&format!("[mir][hint] LoopCarrier({})", vars.join(",")))
|
|
}
|
|
HintKind::LoopHeader => {
|
|
get_global_ring0().log.debug("[mir][hint] LoopHeader")
|
|
}
|
|
HintKind::LoopLatch => {
|
|
get_global_ring0().log.debug("[mir][hint] LoopLatch")
|
|
}
|
|
HintKind::NoEmptyPhi => get_global_ring0().log.debug("[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",
|
|
}
|
|
}
|