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:
@ -1445,19 +1445,31 @@ impl MirBuilder {
|
||||
|
||||
/// Build nowait statement: nowait variable = expression
|
||||
fn build_nowait_statement(&mut self, variable: String, expression: ASTNode) -> Result<ValueId, String> {
|
||||
// Evaluate the expression
|
||||
// If expression is a method call, prefer true async via env.future.spawn_instance
|
||||
if let ASTNode::MethodCall { object, method, arguments, .. } = expression.clone() {
|
||||
let recv_val = self.build_expression(*object)?;
|
||||
let mname_id = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const { dst: mname_id, value: crate::mir::ConstValue::String(method.clone()) })?;
|
||||
let mut arg_vals: Vec<ValueId> = Vec::with_capacity(2 + arguments.len());
|
||||
arg_vals.push(recv_val);
|
||||
arg_vals.push(mname_id);
|
||||
for a in arguments.into_iter() { arg_vals.push(self.build_expression(a)?); }
|
||||
let future_id = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::ExternCall {
|
||||
dst: Some(future_id),
|
||||
iface_name: "env.future".to_string(),
|
||||
method_name: "spawn_instance".to_string(),
|
||||
args: arg_vals,
|
||||
effects: crate::mir::effect::EffectMask::PURE.add(crate::mir::effect::Effect::Io),
|
||||
})?;
|
||||
self.variable_map.insert(variable.clone(), future_id);
|
||||
return Ok(future_id);
|
||||
}
|
||||
// Fallback: resolved future
|
||||
let expression_value = self.build_expression(expression)?;
|
||||
|
||||
// Create a new Future with the evaluated expression as the initial value
|
||||
let future_id = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::FutureNew {
|
||||
dst: future_id,
|
||||
value: expression_value,
|
||||
})?;
|
||||
|
||||
// Store the future in the variable
|
||||
self.emit_instruction(MirInstruction::FutureNew { dst: future_id, value: expression_value })?;
|
||||
self.variable_map.insert(variable.clone(), future_id);
|
||||
|
||||
Ok(future_id)
|
||||
}
|
||||
|
||||
@ -1466,6 +1478,9 @@ impl MirBuilder {
|
||||
// Evaluate the expression (should be a Future)
|
||||
let future_value = self.build_expression(expression)?;
|
||||
|
||||
// Insert checkpoint before await (safepoint)
|
||||
self.emit_instruction(MirInstruction::Safepoint)?;
|
||||
|
||||
// Create destination for await result
|
||||
let result_id = self.value_gen.next();
|
||||
|
||||
@ -1474,6 +1489,8 @@ impl MirBuilder {
|
||||
dst: result_id,
|
||||
future: future_value,
|
||||
})?;
|
||||
// Insert checkpoint after await (safepoint)
|
||||
self.emit_instruction(MirInstruction::Safepoint)?;
|
||||
|
||||
Ok(result_id)
|
||||
}
|
||||
|
||||
@ -142,19 +142,36 @@ impl MirBuilder {
|
||||
|
||||
/// Build nowait statement: nowait variable = expression
|
||||
pub(super) fn build_nowait_statement(&mut self, variable: String, expression: ASTNode) -> Result<ValueId, String> {
|
||||
// Evaluate the expression
|
||||
// If the expression is a method call on a receiver, spawn it asynchronously via env.future.spawn_instance
|
||||
if let ASTNode::MethodCall { object, method, arguments, .. } = expression.clone() {
|
||||
// Build receiver value
|
||||
let recv_val = self.build_expression(*object)?;
|
||||
// Build method name as Const String
|
||||
let mname_id = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::Const { dst: mname_id, value: crate::mir::ConstValue::String(method.clone()) })?;
|
||||
// Build argument values
|
||||
let mut arg_vals: Vec<ValueId> = Vec::with_capacity(2 + arguments.len());
|
||||
arg_vals.push(recv_val);
|
||||
arg_vals.push(mname_id);
|
||||
for a in arguments.into_iter() { arg_vals.push(self.build_expression(a)?); }
|
||||
// Emit extern call to env.future.spawn_instance, capturing Future result
|
||||
let future_id = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::ExternCall {
|
||||
dst: Some(future_id),
|
||||
iface_name: "env.future".to_string(),
|
||||
method_name: "spawn_instance".to_string(),
|
||||
args: arg_vals,
|
||||
effects: crate::mir::effect::EffectMask::PURE.add(crate::mir::effect::Effect::Io),
|
||||
})?;
|
||||
// Store the future in the variable
|
||||
self.variable_map.insert(variable.clone(), future_id);
|
||||
return Ok(future_id);
|
||||
}
|
||||
// Fallback: evaluate synchronously and wrap into a resolved Future
|
||||
let expression_value = self.build_expression(expression)?;
|
||||
|
||||
// Create a new Future with the evaluated expression as the initial value
|
||||
let future_id = self.value_gen.next();
|
||||
self.emit_instruction(MirInstruction::FutureNew {
|
||||
dst: future_id,
|
||||
value: expression_value,
|
||||
})?;
|
||||
|
||||
// Store the future in the variable
|
||||
self.emit_instruction(MirInstruction::FutureNew { dst: future_id, value: expression_value })?;
|
||||
self.variable_map.insert(variable.clone(), future_id);
|
||||
|
||||
Ok(future_id)
|
||||
}
|
||||
}
|
||||
|
||||
@ -81,6 +81,12 @@ pub enum VerificationError {
|
||||
instruction_index: usize,
|
||||
name: String,
|
||||
},
|
||||
/// Await must be surrounded by checkpoints (before and after)
|
||||
MissingCheckpointAroundAwait {
|
||||
block: BasicBlockId,
|
||||
instruction_index: usize,
|
||||
position: &'static str, // "before" | "after"
|
||||
},
|
||||
}
|
||||
|
||||
/// MIR verifier for SSA form and semantic correctness
|
||||
@ -152,6 +158,10 @@ impl MirVerifier {
|
||||
if let Err(mut legacy_errors) = self.verify_no_legacy_ops(function) {
|
||||
local_errors.append(&mut legacy_errors);
|
||||
}
|
||||
// 8. Async semantics: ensure checkpoints around await
|
||||
if let Err(mut await_cp) = self.verify_await_checkpoints(function) {
|
||||
local_errors.append(&mut await_cp);
|
||||
}
|
||||
|
||||
if local_errors.is_empty() {
|
||||
Ok(())
|
||||
@ -246,6 +256,34 @@ impl MirVerifier {
|
||||
if errors.is_empty() { Ok(()) } else { Err(errors) }
|
||||
}
|
||||
|
||||
/// Ensure that each Await instruction is immediately preceded and followed by a checkpoint
|
||||
/// A checkpoint is either MirInstruction::Safepoint or ExternCall("env.runtime", "checkpoint").
|
||||
fn verify_await_checkpoints(&self, function: &MirFunction) -> Result<(), Vec<VerificationError>> {
|
||||
use super::MirInstruction as I;
|
||||
let mut errors = Vec::new();
|
||||
let is_cp = |inst: &I| match inst {
|
||||
I::Safepoint => true,
|
||||
I::ExternCall { iface_name, method_name, .. } => iface_name == "env.runtime" && method_name == "checkpoint",
|
||||
_ => false,
|
||||
};
|
||||
for (bid, block) in &function.blocks {
|
||||
let instrs = &block.instructions;
|
||||
for (idx, inst) in instrs.iter().enumerate() {
|
||||
if let I::Await { .. } = inst {
|
||||
// Check immediate previous
|
||||
if idx == 0 || !is_cp(&instrs[idx - 1]) {
|
||||
errors.push(VerificationError::MissingCheckpointAroundAwait { block: *bid, instruction_index: idx, position: "before" });
|
||||
}
|
||||
// Check immediate next (within instructions list)
|
||||
if idx + 1 >= instrs.len() || !is_cp(&instrs[idx + 1]) {
|
||||
errors.push(VerificationError::MissingCheckpointAroundAwait { block: *bid, instruction_index: idx, position: "after" });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if errors.is_empty() { Ok(()) } else { Err(errors) }
|
||||
}
|
||||
|
||||
/// Verify WeakRef/Barrier minimal semantics
|
||||
fn verify_weakref_and_barrier(&self, function: &MirFunction) -> Result<(), Vec<VerificationError>> {
|
||||
use super::MirInstruction;
|
||||
@ -696,6 +734,9 @@ impl std::fmt::Display for VerificationError {
|
||||
VerificationError::UnsupportedLegacyInstruction { block, instruction_index, name } => {
|
||||
write!(f, "Unsupported legacy instruction '{}' in block {} at {} (enable rewrite passes)", name, block, instruction_index)
|
||||
},
|
||||
VerificationError::MissingCheckpointAroundAwait { block, instruction_index, position } => {
|
||||
write!(f, "Missing {} checkpoint around await in block {} at instruction {}", position, block, instruction_index)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user