Files
hakorune/src/mir/effect.rs
nyash-codex 6ecd8f7f52 feat(runtime): Phase 103 CoreServices Optional化 - Memory Constraints対応
- 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)
2025-12-03 13:59:06 +09:00

403 lines
11 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*!
* MIR Effect System - Track side effects for optimization
*
* Based on ChatGPT5's design for parallel execution and optimization safety
*/
use crate::debug::log as dlog;
use crate::runtime::get_global_ring0;
use std::fmt;
/// Effect flags for tracking side effects and enabling optimizations
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct EffectMask(u16);
/// Individual effect types for the 4-category MIR hierarchy
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Effect {
/// Pure computation with no side effects (Tier-0: reorderable, CSE/LICM eligible)
Pure = 0x0001,
/// Mutable operations (Tier-1: same Box/Field dependency preservation)
Mut = 0x0002,
/// I/O operations (Tier-1: no reordering, side effects present)
Io = 0x0004,
/// Control flow operations (Tier-0: affects execution flow)
Control = 0x0008,
// Legacy effects for compatibility (will be mapped to above categories)
/// Reads from heap/memory (maps to Pure if read-only)
ReadHeap = 0x0010,
/// Writes to heap/memory (maps to Mut)
WriteHeap = 0x0020,
/// P2P/network communication (maps to Io)
P2P = 0x0040,
/// Foreign Function Interface calls (maps to Io)
FFI = 0x0080,
/// May panic or throw exceptions (maps to Io)
Panic = 0x0100,
/// Allocates memory (maps to Mut)
Alloc = 0x0200,
/// Accesses global state (maps to Io)
Global = 0x0400,
/// Thread/async operations (maps to Io)
Async = 0x0800,
/// Unsafe operations (maps to Io)
Unsafe = 0x1000,
/// Debug/logging operations (maps to Io)
Debug = 0x2000,
/// Memory barrier operations (maps to Io)
Barrier = 0x4000,
}
impl EffectMask {
/// No effects - pure computation
pub const PURE: Self = Self(Effect::Pure as u16);
/// Mutable operations (writes, ownership changes)
pub const MUT: Self = Self(Effect::Mut as u16);
/// I/O operations (external effects, cannot reorder)
pub const IO: Self = Self(Effect::Io as u16);
/// Control flow operations
pub const CONTROL: Self = Self(Effect::Control as u16);
// Legacy constants for compatibility
/// Memory read effects
pub const READ: Self = Self(Effect::ReadHeap as u16);
pub const READ_ALIAS: Self = Self::READ; // Uppercase alias for compatibility
/// Memory write effects (includes read)
pub const WRITE: Self = Self((Effect::WriteHeap as u16) | (Effect::ReadHeap as u16));
/// P2P communication effects
pub const P2P: Self = Self(Effect::P2P as u16);
/// Panic/exception effects
pub const PANIC: Self = Self(Effect::Panic as u16);
/// All effects - maximum side effects
pub const ALL: Self = Self(0xFFFF);
/// Create an empty effect mask
pub fn new() -> Self {
Self(0)
}
/// Create effect mask from raw bits
pub fn from_bits(bits: u16) -> Self {
Self(bits)
}
/// Get raw bits
pub fn bits(self) -> u16 {
self.0
}
/// Add an effect to the mask
pub fn add(self, effect: Effect) -> Self {
Self(self.0 | (effect as u16))
}
/// Remove an effect from the mask
pub fn remove(self, effect: Effect) -> Self {
Self(self.0 & !(effect as u16))
}
/// Check if mask contains an effect
pub fn contains(self, effect: Effect) -> bool {
(self.0 & (effect as u16)) != 0
}
/// Check if mask contains any of the given effects
pub fn contains_any(self, mask: EffectMask) -> bool {
(self.0 & mask.0) != 0
}
/// Check if mask contains all of the given effects
pub fn contains_all(self, mask: EffectMask) -> bool {
(self.0 & mask.0) == mask.0
}
/// Combine two effect masks
pub fn union(self, other: EffectMask) -> Self {
Self(self.0 | other.0)
}
/// Get intersection of two effect masks
pub fn intersection(self, other: EffectMask) -> Self {
Self(self.0 & other.0)
}
/// Check if the computation is pure (no side effects)
pub fn is_pure(self) -> bool {
// READ/WRITE/IO/CONTROLがあれば純粋ではないREADはreadonly扱い
let pure = !self.contains(Effect::ReadHeap)
&& !self.is_mut()
&& !self.is_io()
&& !self.is_control();
if dlog::on("NYASH_DEBUG_EFFECTS") {
get_global_ring0().log.debug(&format!(
"[EFFECT] bits={:#06x} primary={:?} is_pure={} read_only={} mut={} io={}",
self.bits(),
self.primary_category(),
pure,
self.is_read_only(),
self.is_mut(),
self.is_io()
));
}
pure
}
/// Check if the computation is mutable (modifies state)
pub fn is_mut(self) -> bool {
self.contains(Effect::Mut)
|| self.contains(Effect::WriteHeap)
|| self.contains(Effect::Alloc)
}
/// Check if the computation has I/O effects (external side effects)
pub fn is_io(self) -> bool {
self.contains(Effect::Io)
|| self.contains(Effect::P2P)
|| self.contains(Effect::FFI)
|| self.contains(Effect::Global)
|| self.contains(Effect::Async)
|| self.contains(Effect::Unsafe)
|| self.contains(Effect::Debug)
|| self.contains(Effect::Barrier)
|| self.contains(Effect::Panic)
}
/// Check if the computation affects control flow
pub fn is_control(self) -> bool {
self.contains(Effect::Control)
}
/// Get the primary effect category for MIR optimization
pub fn primary_category(self) -> Effect {
if self.is_control() {
Effect::Control
} else if self.is_io() {
Effect::Io
} else if self.is_mut() {
Effect::Mut
} else {
Effect::Pure
}
}
/// Check if the computation only reads (doesn't modify state)
pub fn is_read_only(self) -> bool {
!self.is_mut() && !self.is_io()
}
/// Check if parallel execution is safe
pub fn is_parallel_safe(self) -> bool {
!self.contains(Effect::WriteHeap)
&& !self.contains(Effect::Global)
&& !self.contains(Effect::Unsafe)
}
/// Check if operation can be moved across other operations
pub fn is_moveable(self) -> bool {
self.is_pure() || self.is_read_only()
}
/// Get a human-readable list of effects
pub fn effect_names(self) -> Vec<&'static str> {
let mut names = Vec::new();
// Primary categories
if self.contains(Effect::Pure) {
names.push("pure");
}
if self.contains(Effect::Mut) {
names.push("mut");
}
if self.contains(Effect::Io) {
names.push("io");
}
if self.contains(Effect::Control) {
names.push("control");
}
// Legacy effects for detailed tracking
if self.contains(Effect::ReadHeap) {
names.push("read");
}
if self.contains(Effect::WriteHeap) {
names.push("write");
}
if self.contains(Effect::P2P) {
names.push("p2p");
}
if self.contains(Effect::FFI) {
names.push("ffi");
}
if self.contains(Effect::Panic) {
names.push("panic");
}
if self.contains(Effect::Alloc) {
names.push("alloc");
}
if self.contains(Effect::Global) {
names.push("global");
}
if self.contains(Effect::Async) {
names.push("async");
}
if self.contains(Effect::Unsafe) {
names.push("unsafe");
}
if self.contains(Effect::Debug) {
names.push("debug");
}
if self.contains(Effect::Barrier) {
names.push("barrier");
}
if names.is_empty() {
names.push("none");
}
names
}
}
impl Default for EffectMask {
fn default() -> Self {
Self::PURE
}
}
impl fmt::Display for EffectMask {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let names = self.effect_names();
if names.is_empty() {
write!(f, "none")
} else {
write!(f, "{}", names.join("|"))
}
}
}
impl std::ops::BitOr for EffectMask {
type Output = Self;
fn bitor(self, rhs: Self) -> Self {
self.union(rhs)
}
}
impl std::ops::BitOrAssign for EffectMask {
fn bitor_assign(&mut self, rhs: Self) {
*self = *self | rhs;
}
}
impl std::ops::BitAnd for EffectMask {
type Output = Self;
fn bitand(self, rhs: Self) -> Self {
self.intersection(rhs)
}
}
impl std::ops::BitAndAssign for EffectMask {
fn bitand_assign(&mut self, rhs: Self) {
*self = *self & rhs;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_effect_mask_creation() {
let pure = EffectMask::PURE;
let read = EffectMask::READ;
let write = EffectMask::WRITE;
assert!(pure.is_pure());
assert!(!read.is_pure());
assert!(!write.is_pure());
assert!(read.is_read_only());
assert!(!write.is_read_only());
}
#[test]
fn test_effect_combination() {
let mut effects = EffectMask::new();
assert!(effects.is_pure());
effects = effects.add(Effect::ReadHeap);
assert!(effects.contains(Effect::ReadHeap));
assert!(effects.is_read_only());
effects = effects.add(Effect::WriteHeap);
assert!(effects.contains(Effect::WriteHeap));
assert!(!effects.is_read_only());
effects = effects.add(Effect::Io);
assert!(effects.contains(Effect::Io));
assert!(!effects.is_parallel_safe());
}
#[test]
fn test_effect_union() {
let read_effect = EffectMask::READ;
let io_effect = EffectMask::IO;
let combined = read_effect | io_effect;
assert!(combined.contains(Effect::ReadHeap));
assert!(combined.contains(Effect::Io));
assert!(!combined.is_pure());
// IO + read remains parallel-safe under current semantics
assert!(combined.is_parallel_safe());
}
#[test]
fn test_parallel_safety() {
let pure = EffectMask::PURE;
let read = EffectMask::READ;
let write = EffectMask::WRITE;
let io = EffectMask::IO;
assert!(pure.is_parallel_safe());
assert!(read.is_parallel_safe());
assert!(!write.is_parallel_safe());
assert!(io.is_parallel_safe()); // I/O can be parallel if properly synchronized
}
#[test]
fn test_effect_names() {
let pure = EffectMask::PURE;
assert_eq!(pure.effect_names(), vec!["pure"]);
let read_write = EffectMask::READ.add(Effect::WriteHeap);
let names = read_write.effect_names();
assert!(names.contains(&"read"));
assert!(names.contains(&"write"));
}
#[test]
fn test_effect_display() {
let pure = EffectMask::PURE;
assert_eq!(format!("{}", pure), "pure");
let read_io = EffectMask::READ | EffectMask::IO;
let display = format!("{}", read_io);
assert!(display.contains("read"));
assert!(display.contains("io"));
// is_pure should be false when IO is present, even if PURE bit was set initially
let mixed = EffectMask::PURE | EffectMask::IO;
assert!(!mixed.is_pure());
}
}