Phase 11.8/12: MIR Core-13 roadmap, Nyash ABI design, async/await enhancements with TaskGroupBox foundation
Major additions:
- Phase 11.8 MIR cleanup specification (Core-15→14→13 roadmap)
- Nyash ABI unified design document (3×u64 structure)
- TaskGroupBox foundation with cancelAll/joinAll methods
- Enhanced async/await with checkpoint auto-insertion
- Structured concurrency preparation (parent-child task relationships)
Documentation:
- docs/development/roadmap/phases/phase-11.8_mir_cleanup/: Complete Core-13 path
- docs/development/roadmap/phases/phase-12/NYASH-ABI-DESIGN.md: Unified ABI spec
- Updated Phase 12 README with AOT/JIT explanation for script performance
- Added async_task_system/ design docs
Implementation progress:
- FutureBox spawn tracking with weak/strong reference management
- VM checkpoint integration before/after await
- LLVM backend async support preparation
- Verifier rules for await-checkpoint enforcement
- Result<T,E> normalization for timeout/cancellation
Technical insights:
- MIR as 'atomic instructions', Box as 'molecules' philosophy
- 'Everything is Box' enables full-stack with minimal instructions
- Unified BoxCall for array/plugin/async operations future consolidation
Next steps:
- Complete TaskGroupBox implementation
- Migrate from global to scoped task management
- Implement LIFO cleanup on scope exit
- Continue Core-13 instruction consolidation
🚀 'From 15 atoms to infinite programs: The Nyash Box Theory'
This commit is contained in:
18
src/jit/extern/async.rs
vendored
18
src/jit/extern/async.rs
vendored
@ -5,6 +5,7 @@ use crate::{backend::vm::VMValue, box_trait::{NyashBox, IntegerBox, BoolBox, Str
|
||||
|
||||
/// Symbol name for awaiting a FutureBox and returning a value/handle (i64)
|
||||
pub const SYM_FUTURE_AWAIT_H: &str = "nyash.future.await_h";
|
||||
pub const SYM_FUTURE_SPAWN_INSTANCE3_I64: &str = "nyash.future.spawn_instance3_i64";
|
||||
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
pub extern "C" fn nyash_future_await_h(arg0: i64) -> i64 {
|
||||
@ -34,12 +35,19 @@ pub extern "C" fn nyash_future_await_h(arg0: i64) -> i64 {
|
||||
});
|
||||
}
|
||||
let Some(fut) = fut_opt else { return 0; };
|
||||
// Block until completion, get NyashBox result
|
||||
// Cooperative wait with scheduler polling and timeout
|
||||
let max_ms: u64 = std::env::var("NYASH_AWAIT_MAX_MS").ok().and_then(|s| s.parse().ok()).unwrap_or(5000);
|
||||
let start = std::time::Instant::now();
|
||||
while !fut.ready() {
|
||||
crate::runtime::global_hooks::safepoint_and_poll();
|
||||
std::thread::yield_now();
|
||||
if start.elapsed() >= std::time::Duration::from_millis(max_ms) {
|
||||
// Timeout: return 0 (caller may handle as failure)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
// Get NyashBox result and always return a handle
|
||||
let out_box: Box<dyn NyashBox> = fut.get();
|
||||
// Fast-path: primitive returns
|
||||
if let Some(ib) = out_box.as_any().downcast_ref::<IntegerBox>() { return ib.value; }
|
||||
if let Some(bb) = out_box.as_any().downcast_ref::<BoolBox>() { return if bb.value { 1 } else { 0 }; }
|
||||
// Otherwise, register handle and return id (works for String/Map/Array/Instance/etc.)
|
||||
let arc: std::sync::Arc<dyn NyashBox> = std::sync::Arc::from(out_box);
|
||||
let h = handles::to_handle(arc);
|
||||
h as i64
|
||||
|
||||
1
src/jit/extern/mod.rs
vendored
1
src/jit/extern/mod.rs
vendored
@ -9,3 +9,4 @@ pub mod handles;
|
||||
pub mod birth;
|
||||
pub mod runtime;
|
||||
pub mod r#async;
|
||||
pub mod result;
|
||||
|
||||
41
src/jit/extern/result.rs
vendored
Normal file
41
src/jit/extern/result.rs
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
//! Result-related JIT extern symbols
|
||||
|
||||
use crate::box_trait::NyashBox;
|
||||
|
||||
/// Symbol name for wrapping a handle into Result.Ok(handle)
|
||||
pub const SYM_RESULT_OK_H: &str = "nyash.result.ok_h";
|
||||
/// Symbol name for wrapping a handle into Result.Err(handle)
|
||||
pub const SYM_RESULT_ERR_H: &str = "nyash.result.err_h";
|
||||
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
pub extern "C" fn nyash_result_ok_h(handle: i64) -> i64 {
|
||||
use crate::jit::rt::handles;
|
||||
use crate::boxes::result::NyashResultBox;
|
||||
if handle <= 0 { return 0; }
|
||||
if let Some(obj) = handles::get(handle as u64) {
|
||||
let boxed = obj.clone_box();
|
||||
let res = NyashResultBox::new_ok(boxed);
|
||||
let arc: std::sync::Arc<dyn NyashBox> = std::sync::Arc::new(res);
|
||||
let h = handles::to_handle(arc);
|
||||
return h as i64;
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
pub extern "C" fn nyash_result_err_h(handle: i64) -> i64 {
|
||||
use crate::jit::rt::handles;
|
||||
use crate::boxes::result::NyashResultBox;
|
||||
// If handle <= 0, synthesize a Timeout StringBox error for await paths.
|
||||
let err_box: Box<dyn NyashBox> = if handle <= 0 {
|
||||
Box::new(crate::box_trait::StringBox::new("Timeout".to_string()))
|
||||
} else if let Some(obj) = handles::get(handle as u64) {
|
||||
obj.clone_box()
|
||||
} else {
|
||||
Box::new(crate::box_trait::StringBox::new("UnknownError".to_string()))
|
||||
};
|
||||
let res = NyashResultBox::new_err(err_box);
|
||||
let arc: std::sync::Arc<dyn NyashBox> = std::sync::Arc::new(res);
|
||||
let h = handles::to_handle(arc);
|
||||
h as i64
|
||||
}
|
||||
@ -557,6 +557,8 @@ use super::extern_thunks::{
|
||||
};
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
use crate::jit::r#extern::r#async::nyash_future_await_h;
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
use crate::jit::r#extern::result::{nyash_result_ok_h, nyash_result_err_h};
|
||||
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
use crate::{
|
||||
@ -1941,9 +1943,13 @@ impl CraneliftBuilder {
|
||||
builder.symbol("nyash.jit.dbg_i64", nyash_jit_dbg_i64 as *const u8);
|
||||
// Async/Future
|
||||
builder.symbol(crate::jit::r#extern::r#async::SYM_FUTURE_AWAIT_H, nyash_future_await_h as *const u8);
|
||||
builder.symbol(crate::jit::r#extern::result::SYM_RESULT_OK_H, nyash_result_ok_h as *const u8);
|
||||
builder.symbol(crate::jit::r#extern::result::SYM_RESULT_ERR_H, nyash_result_err_h as *const u8);
|
||||
builder.symbol("nyash.jit.block_enter", nyash_jit_block_enter as *const u8);
|
||||
// Async/Future
|
||||
builder.symbol(crate::jit::r#extern::r#async::SYM_FUTURE_AWAIT_H, nyash_future_await_h as *const u8);
|
||||
builder.symbol(crate::jit::r#extern::result::SYM_RESULT_OK_H, nyash_result_ok_h as *const u8);
|
||||
builder.symbol(crate::jit::r#extern::result::SYM_RESULT_ERR_H, nyash_result_err_h as *const u8);
|
||||
builder.symbol("nyash.jit.dbg_i64", nyash_jit_dbg_i64 as *const u8);
|
||||
{
|
||||
use crate::jit::r#extern::collections as c;
|
||||
|
||||
@ -578,8 +578,30 @@ impl LowerCore {
|
||||
I::Await { dst, future } => {
|
||||
// Push future param index when known; otherwise -1 to trigger legacy search in shim
|
||||
if let Some(pidx) = self.param_index.get(future).copied() { b.emit_param_i64(pidx); } else { b.emit_const_i64(-1); }
|
||||
// Call await_h to obtain a handle to the value (0 on timeout)
|
||||
b.emit_host_call(crate::jit::r#extern::r#async::SYM_FUTURE_AWAIT_H, 1, true);
|
||||
// Treat result as handle (or primitive packed into i64). Store for reuse.
|
||||
// Store the awaited handle temporarily
|
||||
let hslot = { let id = self.next_local; self.next_local += 1; id };
|
||||
b.store_local_i64(hslot);
|
||||
// Build Ok result: ok_h(handle)
|
||||
b.load_local_i64(hslot);
|
||||
b.emit_host_call(crate::jit::r#extern::result::SYM_RESULT_OK_H, 1, true);
|
||||
let ok_slot = { let id = self.next_local; self.next_local += 1; id };
|
||||
b.store_local_i64(ok_slot);
|
||||
// Build Err result: err_h(0) → Timeout
|
||||
b.emit_const_i64(0);
|
||||
b.emit_host_call(crate::jit::r#extern::result::SYM_RESULT_ERR_H, 1, true);
|
||||
let err_slot = { let id = self.next_local; self.next_local += 1; id };
|
||||
b.store_local_i64(err_slot);
|
||||
// Cond: (handle == 0)
|
||||
b.load_local_i64(hslot);
|
||||
b.emit_const_i64(0);
|
||||
b.emit_compare(crate::jit::lower::builder::CmpKind::Eq);
|
||||
// Stack for select: cond, then(err), else(ok)
|
||||
b.load_local_i64(err_slot);
|
||||
b.load_local_i64(ok_slot);
|
||||
b.emit_select_i64();
|
||||
// Store selected Result handle to destination
|
||||
let d = *dst;
|
||||
self.handle_values.insert(d);
|
||||
let slot = *self.local_index.entry(d).or_insert_with(|| { let id = self.next_local; self.next_local += 1; id });
|
||||
@ -757,6 +779,29 @@ impl LowerCore {
|
||||
if dst.is_some() { b.emit_const_i64(0); }
|
||||
}
|
||||
} else {
|
||||
// Async spawn bridge: env.future.spawn_instance(recv, method_name, args...)
|
||||
if iface_name == "env.future" && method_name == "spawn_instance" {
|
||||
// Stack layout for hostcall: argc_total, a0(recv), a1(method_name), a2(first payload)
|
||||
// 1) receiver
|
||||
if let Some(recv) = args.get(0) {
|
||||
if let Some(pidx) = self.param_index.get(recv).copied() { b.emit_param_i64(pidx); } else { b.emit_const_i64(-1); }
|
||||
} else { b.emit_const_i64(-1); }
|
||||
// 2) method name (best-effort)
|
||||
if let Some(meth) = args.get(1) { self.push_value_if_known_or_param(b, meth); } else { b.emit_const_i64(0); }
|
||||
// 3) first payload argument if present
|
||||
if let Some(arg2) = args.get(2) { self.push_value_if_known_or_param(b, arg2); } else { b.emit_const_i64(0); }
|
||||
// argc_total = explicit args including method name and payload (exclude receiver)
|
||||
let argc_total = args.len().saturating_sub(1).max(0);
|
||||
b.emit_const_i64(argc_total as i64);
|
||||
// Call spawn shim; it returns Future handle
|
||||
b.emit_host_call(crate::jit::r#extern::r#async::SYM_FUTURE_SPAWN_INSTANCE3_I64, 4, true);
|
||||
if let Some(d) = dst {
|
||||
self.handle_values.insert(*d);
|
||||
let slot = *self.local_index.entry(*d).or_insert_with(|| { let id = self.next_local; self.next_local += 1; id });
|
||||
b.store_local_i64(slot);
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
// Unknown extern: strictではno-opにしてfailを避ける
|
||||
if dst.is_some() { b.emit_const_i64(0); }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user