Phase 11-12: LLVM backend initial, semantics layer, plugin unification

Major changes:
- LLVM backend initial implementation (compiler.rs, llvm mode)
- Semantics layer integration in interpreter (operators.rs)
- Phase 12 plugin architecture revision (3-layer system)
- Builtin box removal preparation
- MIR instruction set documentation (26→Core-15 migration)
- Cross-backend testing infrastructure
- Await/nowait syntax support

New features:
- LLVM AOT compilation support (--backend llvm)
- Semantics layer for interpreter→VM flow
- Tri-backend smoke tests
- Plugin-only registry mode

Bug fixes:
- Interpreter plugin box arithmetic operations
- Branch test returns incorrect values

Documentation:
- Phase 12 README.md updated with new plugin architecture
- Removed obsolete NYIR proposals
- Added LLVM test programs documentation

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Moe Charm
2025-09-01 23:44:34 +09:00
parent fff9749f47
commit 11506cee3b
196 changed files with 10955 additions and 380 deletions

View File

@ -0,0 +1,37 @@
//! Lightweight global hooks for JIT/extern to reach GC/scheduler without owning NyashRuntime.
use once_cell::sync::OnceCell;
use std::sync::{Arc, RwLock};
use super::{gc::GcHooks, scheduler::Scheduler};
static GLOBAL_GC: OnceCell<RwLock<Option<Arc<dyn GcHooks>>>> = OnceCell::new();
static GLOBAL_SCHED: OnceCell<RwLock<Option<Arc<dyn Scheduler>>>> = OnceCell::new();
fn gc_cell() -> &'static RwLock<Option<Arc<dyn GcHooks>>> { GLOBAL_GC.get_or_init(|| RwLock::new(None)) }
fn sched_cell() -> &'static RwLock<Option<Arc<dyn Scheduler>>> { GLOBAL_SCHED.get_or_init(|| RwLock::new(None)) }
pub fn set_from_runtime(rt: &crate::runtime::nyash_runtime::NyashRuntime) {
if let Ok(mut g) = gc_cell().write() { *g = Some(rt.gc.clone()); }
if let Ok(mut s) = sched_cell().write() { *s = rt.scheduler.as_ref().cloned(); }
}
pub fn set_gc(gc: Arc<dyn GcHooks>) { if let Ok(mut g) = gc_cell().write() { *g = Some(gc); } }
pub fn set_scheduler(s: Arc<dyn Scheduler>) { if let Ok(mut w) = sched_cell().write() { *w = Some(s); } }
/// Perform a runtime safepoint and poll the scheduler if available.
pub fn safepoint_and_poll() {
if let Ok(g) = gc_cell().read() {
if let Some(gc) = g.as_ref() { gc.safepoint(); }
}
if let Ok(s) = sched_cell().read() {
if let Some(sched) = s.as_ref() { sched.poll(); }
}
}
/// Try to schedule a task on the global scheduler. Returns true if scheduled.
pub fn spawn_task(_name: &str, f: Box<dyn FnOnce() + 'static>) -> bool {
// Minimal inline execution to avoid Send bounds; upgrade to true scheduling later
f();
true
}

View File

@ -12,6 +12,8 @@ pub mod unified_registry;
pub mod nyash_runtime;
pub mod gc;
pub mod scheduler;
pub mod global_hooks;
pub mod semantics;
// pub mod plugin_box; // legacy - 古いPluginBox
// pub mod plugin_loader; // legacy - Host VTable使用
pub mod type_meta;

View File

@ -476,14 +476,15 @@ impl PluginLoaderV2 {
Ok(None)
}
("env.runtime", "checkpoint") => {
// Minimal safepoint checkpoint stub (no-op)
// Safepoint + scheduler poll via global hooks
if std::env::var("NYASH_RUNTIME_CHECKPOINT_TRACE").ok().as_deref() == Some("1") {
eprintln!("[runtime.checkpoint] reached");
}
crate::runtime::global_hooks::safepoint_and_poll();
Ok(None)
}
// Future/Await bridge (scaffold): maps MIR Future* to Box operations
("env.future", "new") => {
("env.future", "new") | ("env.future", "birth") => {
// new(value) -> FutureBox(set to value)
let fut = crate::boxes::future::FutureBox::new();
if let Some(v) = args.get(0) { fut.set_result(v.clone_box()); }
@ -512,6 +513,51 @@ impl PluginLoaderV2 {
}
Ok(None)
}
("env.future", "spawn_instance") => {
// spawn_instance(recv, method_name, args...) -> FutureBox
// If a scheduler is available, schedule the call; else invoke synchronously.
use crate::box_trait::{NyashBox, VoidBox, ErrorBox};
let fut = crate::boxes::future::FutureBox::new();
if args.len() >= 2 {
if let Some(pb) = args[0].as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>() {
let method_name = if let Some(sb) = args[1].as_any().downcast_ref::<crate::box_trait::StringBox>() { sb.value.clone() } else { args[1].to_string_box().value };
// Clone boxes for scheduled call (same-thread scheduler; no Send bound)
let tail: Vec<Box<dyn NyashBox>> = args.iter().skip(2).map(|a| a.clone_box()).collect();
let recv_type = pb.box_type.clone();
let recv_id = pb.instance_id();
// Clones for fallback inline path
let recv_type_inline = recv_type.clone();
let method_name_inline = method_name.clone();
let tail_inline: Vec<Box<dyn NyashBox>> = tail.iter().map(|a| a.clone_box()).collect();
let fut_setter = fut.clone();
let scheduled = crate::runtime::global_hooks::spawn_task("spawn_instance", Box::new(move || {
let host = crate::runtime::get_global_plugin_host();
let read_res = host.read();
if let Ok(ro) = read_res {
match ro.invoke_instance_method(&recv_type, &method_name, recv_id, &tail) {
Ok(ret) => { if let Some(v) = ret { fut_setter.set_result(v); } else { fut_setter.set_result(Box::new(VoidBox::new())); } }
Err(e) => { fut_setter.set_result(Box::new(ErrorBox::new("InvokeError", &format!("{}.{}: {:?}", recv_type, method_name, e)))); }
}
}
}));
if !scheduled {
// Fallback: run inline synchronously
let host = crate::runtime::get_global_plugin_host();
let read_res = host.read();
if let Ok(ro) = read_res {
match ro.invoke_instance_method(&recv_type_inline, &method_name_inline, recv_id, &tail_inline) {
Ok(ret) => { if let Some(v) = ret { fut.set_result(v); } else { fut.set_result(Box::new(VoidBox::new())); } }
Err(e) => { fut.set_result(Box::new(ErrorBox::new("InvokeError", &format!("{}.{}: {:?}", recv_type_inline, method_name_inline, e)))); }
}
}
}
return Ok(Some(Box::new(fut)));
}
}
// Fallback: resolved future of first arg
if let Some(v) = args.get(0) { fut.set_result(v.clone_box()); }
Ok(Some(Box::new(fut)))
}
("env.canvas", _) => {
eprintln!("[env.canvas] {} invoked (stub)", method_name);
Ok(None)

97
src/runtime/semantics.rs Normal file
View File

@ -0,0 +1,97 @@
/*!
* Shared semantics for coercions and basic ops
*
* Goal: Unify Script→MIR→VM→AOT semantics by centralizing
* string/number coercions and common operation ordering.
*/
use crate::box_trait::{NyashBox, StringBox, IntegerBox};
/// Try to unwrap InstanceBox and return inner if present
fn maybe_unwrap_instance(b: &dyn NyashBox) -> &dyn NyashBox {
if let Some(inst) = b.as_any().downcast_ref::<crate::instance_v2::InstanceBox>() {
if let Some(ref inner) = inst.inner_content { return inner.as_ref(); }
}
b
}
/// Result.Ok(inner) → recurse helper
fn maybe_unwrap_result_ok(b: &dyn NyashBox) -> &dyn NyashBox {
if let Some(res) = b.as_any().downcast_ref::<crate::boxes::result::NyashResultBox>() {
if let crate::boxes::result::NyashResultBox::Ok(inner) = res { return inner.as_ref(); }
}
b
}
/// Best-effort string coercion used by all backends.
pub fn coerce_to_string(b: &dyn NyashBox) -> Option<String> {
let b = maybe_unwrap_instance(b);
// Internal StringBox
if let Some(s) = b.as_any().downcast_ref::<StringBox>() { return Some(s.value.clone()); }
// Result.Ok recursion
let b2 = maybe_unwrap_result_ok(b);
if !std::ptr::eq(b2 as *const _, b as *const _) {
if let Some(s) = coerce_to_string(b2) { return Some(s); }
}
// Plugin StringBox: prefer toUtf8; fallback to toString
if let Some(pb) = b.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>() {
// StringBox.toUtf8
if pb.box_type == "StringBox" {
let host = crate::runtime::get_global_plugin_host();
let read_res = host.read();
if let Ok(ro) = read_res {
if let Ok(ret) = ro.invoke_instance_method("StringBox", "toUtf8", pb.instance_id(), &[]) {
if let Some(vb) = ret {
if let Some(sb2) = vb.as_any().downcast_ref::<StringBox>() { return Some(sb2.value.clone()); }
}
}
}
}
// AnyBox.toString
let host = crate::runtime::get_global_plugin_host();
let read_res = host.read();
if let Ok(ro) = read_res {
if let Ok(ret) = ro.invoke_instance_method(&pb.box_type, "toString", pb.instance_id(), &[]) {
if let Some(vb) = ret { if let Some(s) = coerce_to_string(vb.as_ref()) { return Some(s); } }
}
}
}
None
}
/// Best-effort integer coercion used by all backends.
pub fn coerce_to_i64(b: &dyn NyashBox) -> Option<i64> {
let b = maybe_unwrap_instance(b);
if let Some(i) = b.as_any().downcast_ref::<IntegerBox>() { return Some(i.value); }
// Plugin numeric getters
if let Some(pb) = b.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>() {
// IntegerBox.get -> IntegerBox
if pb.box_type == "IntegerBox" {
let host = crate::runtime::get_global_plugin_host();
let read_res = host.read();
if let Ok(ro) = read_res {
if let Ok(ret) = ro.invoke_instance_method("IntegerBox", "get", pb.instance_id(), &[]) {
if let Some(vb) = ret { if let Some(ii) = vb.as_any().downcast_ref::<IntegerBox>() { return Some(ii.value); } }
}
}
}
// FloatBox.toDouble -> FloatBox
if pb.box_type == "FloatBox" {
let host = crate::runtime::get_global_plugin_host();
let read_res = host.read();
if let Ok(ro) = read_res {
if let Ok(ret) = ro.invoke_instance_method("FloatBox", "toDouble", pb.instance_id(), &[]) {
if let Some(vb) = ret { if let Some(fb) = vb.as_any().downcast_ref::<crate::boxes::FloatBox>() { return Some(fb.value as i64); } }
}
}
}
}
// Fallback via string coercion -> parse
if let Some(s) = coerce_to_string(b) {
return s.trim().parse::<i64>().ok();
}
None
}