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:
Moe Charm
2025-09-02 03:41:51 +09:00
parent 11506cee3b
commit c9366d5c54
37 changed files with 2203 additions and 90 deletions

View File

@ -788,6 +788,58 @@ impl LLVMCompiler {
let rv = call.try_as_basic_value().left().ok_or("readline returned void".to_string())?;
vmap.insert(*d, rv);
}
} else if iface_name == "env.future" && method_name == "spawn_instance" {
// Lower to NyRT: i64 nyash.future.spawn_instance3_i64(i64 a0, i64 a1, i64 a2, i64 argc)
// a0: receiver handle (or param index→handle via nyash.handle.of upstream if needed)
// a1: method name pointer (i8*) or handle; we pass pointer as i64 here
// a2: first payload (i64/handle); more args currently unsupported in LLVM lowering
if args.len() < 2 { return Err("env.future.spawn_instance expects at least (recv, method_name)".to_string()); }
let i64t = codegen.context.i64_type();
let i8p = codegen.context.i8_type().ptr_type(AddressSpace::from(0));
// a0
let a0_v = *vmap.get(&args[0]).ok_or("recv missing")?;
let a0 = to_i64_any(codegen.context, &codegen.builder, a0_v)?;
// a1 (method name)
let a1_v = *vmap.get(&args[1]).ok_or("method_name missing")?;
let a1 = match a1_v {
BasicValueEnum::PointerValue(pv) => codegen.builder.build_ptr_to_int(pv, i64t, "mname_p2i").map_err(|e| e.to_string())?,
_ => to_i64_any(codegen.context, &codegen.builder, a1_v)?,
};
// a2 (first payload if any)
let a2 = if args.len() >= 3 {
let v = *vmap.get(&args[2]).ok_or("arg2 missing")?;
to_i64_any(codegen.context, &codegen.builder, v)?
} else { i64t.const_zero() };
let argc_total = i64t.const_int(args.len().saturating_sub(1) as u64, false);
// declare and call
let fnty = i64t.fn_type(&[i64t.into(), i64t.into(), i64t.into(), i64t.into()], false);
let callee = codegen
.module
.get_function("nyash.future.spawn_instance3_i64")
.unwrap_or_else(|| codegen.module.add_function("nyash.future.spawn_instance3_i64", fnty, None));
let call = codegen
.builder
.build_call(callee, &[a0.into(), a1.into(), a2.into(), argc_total.into()], "spawn_i3")
.map_err(|e| e.to_string())?;
if let Some(d) = dst {
let rv = call
.try_as_basic_value()
.left()
.ok_or("spawn_instance3 returned void".to_string())?;
// Treat as handle → pointer for Box return types; otherwise keep i64
if let Some(mt) = func.metadata.value_types.get(d) {
match mt {
crate::mir::MirType::Integer | crate::mir::MirType::Bool => { vmap.insert(*d, rv); }
crate::mir::MirType::Box(_) | crate::mir::MirType::String | crate::mir::MirType::Array(_) | crate::mir::MirType::Future(_) | crate::mir::MirType::Unknown => {
let iv = if let BasicValueEnum::IntValue(iv) = rv { iv } else { return Err("spawn ret expected i64".to_string()); };
let pty = codegen.context.i8_type().ptr_type(AddressSpace::from(0));
let ptr = codegen.builder.build_int_to_ptr(iv, pty, "ret_handle_to_ptr").map_err(|e| e.to_string())?;
vmap.insert(*d, ptr.into());
}
_ => { vmap.insert(*d, rv); }
}
} else { vmap.insert(*d, rv); }
}
} else {
return Err(format!("ExternCall lowering unsupported: {}.{} (enable NYASH_LLVM_ALLOW_BY_NAME=1 to try by-name, or add a NyRT shim)", iface_name, method_name));
}

View File

@ -580,6 +580,7 @@ impl VM {
// Enter a new scope for this function
self.scope_tracker.push_scope();
crate::runtime::global_hooks::push_task_scope();
// Phase 10_c: try a JIT dispatch when enabled; fallback to VM on trap/miss
// Prepare arguments from current frame params before borrowing jit_manager mutably
@ -599,6 +600,7 @@ impl VM {
// Exit scope before returning
self.leave_root_region();
self.scope_tracker.pop_scope();
crate::runtime::global_hooks::pop_task_scope();
return Ok(val);
} else if std::env::var("NYASH_JIT_STATS").ok().as_deref() == Some("1") ||
std::env::var("NYASH_JIT_TRAP_LOG").ok().as_deref() == Some("1") {
@ -606,6 +608,7 @@ impl VM {
if jit_only {
self.leave_root_region();
self.scope_tracker.pop_scope();
crate::runtime::global_hooks::pop_task_scope();
return Err(VMError::InvalidInstruction(format!("JIT-only enabled and JIT trap occurred for {}", function.signature.name)));
}
}
@ -616,15 +619,18 @@ impl VM {
if let Some(val) = jm_mut.execute_compiled(&function.signature.name, &function.signature.return_type, &args_vec) {
self.leave_root_region();
self.scope_tracker.pop_scope();
crate::runtime::global_hooks::pop_task_scope();
return Ok(val);
} else {
self.leave_root_region();
self.scope_tracker.pop_scope();
crate::runtime::global_hooks::pop_task_scope();
return Err(VMError::InvalidInstruction(format!("JIT-only enabled and JIT execution failed for {}", function.signature.name)));
}
} else {
self.leave_root_region();
self.scope_tracker.pop_scope();
crate::runtime::global_hooks::pop_task_scope();
return Err(VMError::InvalidInstruction(format!("JIT-only enabled but function not compiled: {}", function.signature.name)));
}
}
@ -673,6 +679,7 @@ impl VM {
if let Some(return_value) = should_return {
// Exit scope before returning
self.scope_tracker.pop_scope();
crate::runtime::global_hooks::pop_task_scope();
return Ok(return_value);
} else if let Some(target) = next_block {
// Update previous block before jumping and record transition via control_flow helper
@ -683,6 +690,7 @@ impl VM {
// but let's handle it gracefully by returning void
// Exit scope before returning
self.scope_tracker.pop_scope();
crate::runtime::global_hooks::pop_task_scope();
return Ok(VMValue::Void);
}
}

View File

@ -262,6 +262,22 @@ impl VM {
}
}
// TaskGroupBox methods (scaffold → instance内の所有Futureに対して実行)
if box_value.as_any().downcast_ref::<crate::boxes::task_group_box::TaskGroupBox>().is_some() {
let mut owned = box_value;
if let Some(tg) = (&mut *owned).as_any_mut().downcast_mut::<crate::boxes::task_group_box::TaskGroupBox>() {
match method {
"cancelAll" | "cancel_all" => { return Ok(tg.cancelAll()); }
"joinAll" | "join_all" => {
let ms = _args.get(0).map(|a| a.to_string_box().value.parse::<i64>().unwrap_or(2000));
return Ok(tg.joinAll(ms));
}
_ => { return Ok(Box::new(VoidBox::new())); }
}
}
return Ok(Box::new(VoidBox::new()));
}
// P2PBox methods (minimal)
if let Some(p2p) = box_value.as_any().downcast_ref::<crate::boxes::p2p_box::P2PBox>() {
match method {

View File

@ -589,10 +589,11 @@ impl VM {
let future_val = self.get_value(future)?;
if let VMValue::Future(ref future_box) = future_val {
// This blocks until the future is ready
// This blocks until the future is ready (Condvar-based)
let result = future_box.get();
// Convert NyashBox back to VMValue
let vm_value = VMValue::from_nyash_box(result);
// Wrap into Result.Ok for unified semantics
let ok = crate::boxes::result::NyashResultBox::new_ok(result);
let vm_value = VMValue::from_nyash_box(Box::new(ok));
self.set_value(dst, vm_value);
Ok(ControlFlow::Continue)
} else {