chore(fmt): add legacy stubs and strip trailing whitespace to unblock cargo fmt

This commit is contained in:
Selfhosting Dev
2025-09-17 07:43:07 +09:00
parent fcf8ce1f3c
commit adbb0201a9
385 changed files with 35622 additions and 15004 deletions

View File

@ -5,8 +5,8 @@
* Initial scope focuses on value coercions used by the MIR interpreter and JIT.
*/
use crate::box_trait::{NyashBox, IntegerBox, BoolBox, StringBox, VoidBox};
use crate::backend::vm::VMValue;
use crate::box_trait::{BoolBox, IntegerBox, NyashBox, StringBox, VoidBox};
use std::sync::Arc;
/// Opaque handle type used by JIT/runtime bridges.
@ -80,4 +80,3 @@ pub fn handle_of(boxref: Arc<dyn NyashBox>) -> Handle {
pub fn handle_get(h: Handle) -> Option<Arc<dyn NyashBox>> {
crate::jit::rt::handles::get(h)
}

View File

@ -1,14 +1,14 @@
/*!
* AOT Compiler - Converts MIR to precompiled native code
*
*
* Handles the MIR -> WASM -> Native compilation pipeline
*/
use super::{AotError, AotConfig, AotStats};
use crate::mir::MirModule;
use super::{AotConfig, AotError, AotStats};
use crate::backend::wasm::{WasmBackend, WasmError};
use wasmtime::{Engine, Module};
use crate::mir::MirModule;
use std::time::Instant;
use wasmtime::{Engine, Module};
/// AOT compiler that handles the full compilation pipeline
pub struct AotCompiler {
@ -21,109 +21,123 @@ impl AotCompiler {
/// Create a new AOT compiler with the given configuration
pub fn new(config: &AotConfig) -> Result<Self, AotError> {
// Create wasmtime engine with optimized configuration
let engine = Engine::new(config.wasmtime_config())
.map_err(|e| AotError::WasmtimeError(format!("Failed to create wasmtime engine: {}", e)))?;
let engine = Engine::new(config.wasmtime_config()).map_err(|e| {
AotError::WasmtimeError(format!("Failed to create wasmtime engine: {}", e))
})?;
// Create WASM backend for MIR -> WASM compilation
let wasm_backend = WasmBackend::new();
let stats = AotStats {
wasm_size: 0,
precompiled_size: 0,
compilation_time_ms: 0,
optimization_level: format!("O{}", config.optimization_level()),
};
Ok(Self {
wasm_backend,
wasmtime_engine: engine,
stats,
})
}
/// Compile MIR module to WASM bytecode
pub fn compile_mir_to_wasm(&mut self, mir_module: MirModule) -> Result<Vec<u8>, AotError> {
let start_time = Instant::now();
// Use existing WASM backend to compile MIR to WASM
let wasm_bytes = self.wasm_backend.compile_module(mir_module)
let wasm_bytes = self
.wasm_backend
.compile_module(mir_module)
.map_err(|e| match e {
WasmError::CodegenError(msg) => AotError::CompilationError(format!("WASM codegen failed: {}", msg)),
WasmError::MemoryError(msg) => AotError::CompilationError(format!("WASM memory error: {}", msg)),
WasmError::UnsupportedInstruction(msg) => AotError::CompilationError(format!("Unsupported MIR instruction: {}", msg)),
WasmError::WasmValidationError(msg) => AotError::CompilationError(format!("WASM validation failed: {}", msg)),
WasmError::CodegenError(msg) => {
AotError::CompilationError(format!("WASM codegen failed: {}", msg))
}
WasmError::MemoryError(msg) => {
AotError::CompilationError(format!("WASM memory error: {}", msg))
}
WasmError::UnsupportedInstruction(msg) => {
AotError::CompilationError(format!("Unsupported MIR instruction: {}", msg))
}
WasmError::WasmValidationError(msg) => {
AotError::CompilationError(format!("WASM validation failed: {}", msg))
}
WasmError::IOError(msg) => AotError::IOError(msg),
})?;
self.stats.wasm_size = wasm_bytes.len();
self.stats.compilation_time_ms += start_time.elapsed().as_millis() as u64;
Ok(wasm_bytes)
}
/// Precompile WASM bytecode to native machine code
pub fn precompile_wasm(&mut self, wasm_bytes: &[u8]) -> Result<Vec<u8>, AotError> {
let start_time = Instant::now();
// Parse and validate the WASM module
let module = Module::from_binary(&self.wasmtime_engine, wasm_bytes)
.map_err(|e| AotError::WasmtimeError(format!("Failed to parse WASM module: {}", e)))?;
// Serialize the precompiled module to bytes
let precompiled_bytes = module.serialize()
.map_err(|e| AotError::WasmtimeError(format!("Failed to serialize precompiled module: {}", e)))?;
let precompiled_bytes = module.serialize().map_err(|e| {
AotError::WasmtimeError(format!("Failed to serialize precompiled module: {}", e))
})?;
self.stats.precompiled_size = precompiled_bytes.len();
self.stats.compilation_time_ms += start_time.elapsed().as_millis() as u64;
Ok(precompiled_bytes)
}
/// Compile MIR directly to precompiled native code (convenience method)
pub fn compile_mir_to_native(&mut self, mir_module: MirModule) -> Result<Vec<u8>, AotError> {
let wasm_bytes = self.compile_mir_to_wasm(mir_module)?;
self.precompile_wasm(&wasm_bytes)
}
/// Load and execute a precompiled module (for testing)
pub fn execute_precompiled(&self, precompiled_bytes: &[u8]) -> Result<i32, AotError> {
// Deserialize the precompiled module
let module = unsafe {
Module::deserialize(&self.wasmtime_engine, precompiled_bytes)
.map_err(|e| AotError::WasmtimeError(format!("Failed to deserialize module: {}", e)))?
Module::deserialize(&self.wasmtime_engine, precompiled_bytes).map_err(|e| {
AotError::WasmtimeError(format!("Failed to deserialize module: {}", e))
})?
};
// Create instance and execute
let mut store = wasmtime::Store::new(&self.wasmtime_engine, ());
let instance = wasmtime::Instance::new(&mut store, &module, &[])
.map_err(|e| AotError::RuntimeError(format!("Failed to create instance: {}", e)))?;
// Look for main function or default export
let main_func = instance
.get_typed_func::<(), i32>(&mut store, "main")
.or_else(|_| instance.get_typed_func::<(), i32>(&mut store, "_start"))
.or_else(|_| instance.get_typed_func::<(), i32>(&mut store, "run"))
.map_err(|e| AotError::RuntimeError(format!("No main function found: {}", e)))?;
// Execute the function
let result = main_func.call(&mut store, ())
let result = main_func
.call(&mut store, ())
.map_err(|e| AotError::RuntimeError(format!("Execution failed: {}", e)))?;
Ok(result)
}
/// Validate a WASM module before precompilation
pub fn validate_wasm(&self, wasm_bytes: &[u8]) -> Result<(), AotError> {
Module::validate(&self.wasmtime_engine, wasm_bytes)
.map_err(|e| AotError::WasmtimeError(format!("WASM validation failed: {}", e)))?;
Ok(())
}
/// Get compilation statistics
pub fn get_stats(&self) -> AotStats {
self.stats.clone()
}
/// Reset statistics
pub fn reset_stats(&mut self) {
self.stats = AotStats {
@ -133,7 +147,7 @@ impl AotCompiler {
optimization_level: self.stats.optimization_level.clone(),
};
}
/// Get compression ratio (precompiled size / WASM size)
pub fn compression_ratio(&self) -> f64 {
if self.stats.wasm_size == 0 {
@ -141,7 +155,7 @@ impl AotCompiler {
}
self.stats.precompiled_size as f64 / self.stats.wasm_size as f64
}
/// Get wasmtime engine info
pub fn engine_info(&self) -> String {
format!(
@ -155,7 +169,7 @@ impl AotCompiler {
mod tests {
use super::*;
use crate::mir::MirModule;
#[test]
fn test_compiler_creation() {
let config = AotConfig::new().expect("Failed to create config");
@ -163,13 +177,13 @@ mod tests {
// Should not panic
assert!(true);
}
#[test]
fn test_empty_module_compilation() {
let config = AotConfig::new().expect("Failed to create config");
let mut compiler = AotCompiler::new(&config).expect("Failed to create compiler");
let module = MirModule::new("test".to_string());
// Should handle empty module gracefully
let result = compiler.compile_mir_to_wasm(module);
// Note: This might fail due to empty module, but should not panic
@ -179,45 +193,45 @@ mod tests {
Err(_) => assert!(true), // Empty modules might legitimately fail
}
}
#[test]
fn test_stats_tracking() {
let config = AotConfig::new().expect("Failed to create config");
let compiler = AotCompiler::new(&config).expect("Failed to create compiler");
let stats = compiler.get_stats();
assert_eq!(stats.wasm_size, 0);
assert_eq!(stats.precompiled_size, 0);
assert_eq!(stats.compilation_time_ms, 0);
assert!(stats.optimization_level.contains("O"));
}
#[test]
fn test_wasm_validation() {
let config = AotConfig::new().expect("Failed to create config");
let compiler = AotCompiler::new(&config).expect("Failed to create compiler");
// Test with invalid WASM bytes
let invalid_wasm = vec![0x00, 0x61, 0x73, 0x6d]; // Incomplete WASM header
assert!(compiler.validate_wasm(&invalid_wasm).is_err());
}
#[test]
fn test_compression_ratio() {
let config = AotConfig::new().expect("Failed to create config");
let compiler = AotCompiler::new(&config).expect("Failed to create compiler");
// With no compilation done, ratio should be 0
assert_eq!(compiler.compression_ratio(), 0.0);
}
#[test]
fn test_engine_info() {
let config = AotConfig::new().expect("Failed to create config");
let compiler = AotCompiler::new(&config).expect("Failed to create compiler");
let info = compiler.engine_info();
assert!(info.contains("Wasmtime"));
assert!(info.contains("Cranelift"));
}
}
}

View File

@ -1,6 +1,6 @@
/*!
* AOT Configuration - Wasmtime optimization settings
*
*
* Manages compilation settings, CPU features, and performance tuning
*/
@ -22,25 +22,25 @@ impl AotConfig {
/// Create default configuration optimized for performance
pub fn new() -> Result<Self, AotError> {
let mut config = Config::new();
// Enable maximum optimizations
config.strategy(Strategy::Cranelift);
config.cranelift_opt_level(OptLevel::Speed);
// Enable WebAssembly features for better performance
config.wasm_simd(true);
config.wasm_bulk_memory(true);
config.wasm_multi_memory(true);
// Enable advanced optimizations
unsafe {
config.cranelift_flag_enable("enable_verifier");
config.cranelift_flag_enable("enable_nan_canonicalization");
}
// Set memory limits for safety (64MB max)
config.max_wasm_stack(8 * 1024 * 1024); // 8MB stack
let target_arch = if cfg!(target_arch = "x86_64") {
"x86_64"
} else if cfg!(target_arch = "aarch64") {
@ -49,8 +49,9 @@ impl AotConfig {
"x86"
} else {
"unknown"
}.to_string();
}
.to_string();
Ok(Self {
wasmtime_config: config,
optimization_level: 3, // Maximum optimization
@ -60,23 +61,23 @@ impl AotConfig {
target_arch,
})
}
/// Create configuration optimized for debug builds
pub fn debug() -> Result<Self, AotError> {
let mut config = Config::new();
config.strategy(Strategy::Cranelift);
config.cranelift_opt_level(OptLevel::None);
// Enable debug features
config.debug_info(true);
// Basic WASM features only
config.wasm_simd(false);
config.wasm_bulk_memory(true);
let target_arch = std::env::consts::ARCH.to_string();
Ok(Self {
wasmtime_config: config,
optimization_level: 0,
@ -86,60 +87,63 @@ impl AotConfig {
target_arch,
})
}
/// Create configuration for specific target architecture
pub fn for_target(target: &str) -> Result<Self, AotError> {
let mut config = Self::new()?;
config.target_arch = target.to_string();
// Adjust features based on target
match target {
"x86_64" => {
// Enable all advanced features for x86_64
config.enable_simd = true;
config.enable_multi_memory = true;
},
}
"aarch64" => {
// ARM64 - enable SIMD but be conservative with memory features
config.enable_simd = true;
config.enable_multi_memory = false;
},
}
"x86" => {
// x86 - be conservative
config.enable_simd = false;
config.enable_multi_memory = false;
},
}
_ => {
return Err(AotError::ConfigError(format!("Unsupported target architecture: {}", target)));
return Err(AotError::ConfigError(format!(
"Unsupported target architecture: {}",
target
)));
}
}
// Rebuild wasmtime config with new settings
config.rebuild_wasmtime_config()?;
Ok(config)
}
/// Get the wasmtime configuration
pub fn wasmtime_config(&self) -> &Config {
&self.wasmtime_config
}
/// Get optimization level (0-3)
pub fn optimization_level(&self) -> u8 {
self.optimization_level
}
/// Get target architecture
pub fn target_arch(&self) -> &str {
&self.target_arch
}
/// Check if SIMD is enabled
pub fn simd_enabled(&self) -> bool {
self.enable_simd
}
/// Get compatibility key for cache validation
pub fn compatibility_key(&self) -> String {
format!(
@ -152,13 +156,13 @@ impl AotConfig {
"18.0" // Wasmtime version from Cargo.toml
)
}
/// Rebuild wasmtime config with current settings
fn rebuild_wasmtime_config(&mut self) -> Result<(), AotError> {
let mut config = Config::new();
config.strategy(Strategy::Cranelift);
let opt_level = match self.optimization_level {
0 => OptLevel::None,
1 => OptLevel::Speed,
@ -166,35 +170,37 @@ impl AotConfig {
3 => OptLevel::SpeedAndSize,
_ => OptLevel::Speed,
};
config.cranelift_opt_level(opt_level);
config.wasm_simd(self.enable_simd);
config.wasm_bulk_memory(self.enable_bulk_memory);
config.wasm_multi_memory(self.enable_multi_memory);
// Set memory limits
config.max_wasm_stack(8 * 1024 * 1024); // 8MB stack
if self.optimization_level >= 2 {
unsafe {
config.cranelift_flag_enable("enable_verifier");
}
}
self.wasmtime_config = config;
Ok(())
}
/// Set custom optimization level
pub fn set_optimization_level(&mut self, level: u8) -> Result<(), AotError> {
if level > 3 {
return Err(AotError::ConfigError("Optimization level must be 0-3".to_string()));
return Err(AotError::ConfigError(
"Optimization level must be 0-3".to_string(),
));
}
self.optimization_level = level;
self.rebuild_wasmtime_config()
}
/// Enable or disable SIMD
pub fn set_simd(&mut self, enabled: bool) -> Result<(), AotError> {
self.enable_simd = enabled;
@ -211,21 +217,21 @@ impl Default for AotConfig {
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_config() {
let config = AotConfig::new().expect("Failed to create config");
assert_eq!(config.optimization_level(), 3);
assert!(config.simd_enabled());
}
#[test]
fn test_debug_config() {
let config = AotConfig::debug().expect("Failed to create debug config");
assert_eq!(config.optimization_level(), 0);
assert!(!config.simd_enabled());
}
#[test]
fn test_compatibility_key() {
let config = AotConfig::new().expect("Failed to create config");
@ -233,24 +239,26 @@ mod tests {
assert!(key.contains("nyash-aot"));
assert!(key.contains("wasmtime"));
}
#[test]
fn test_target_config() {
let config = AotConfig::for_target("x86_64").expect("Failed to create x86_64 config");
assert_eq!(config.target_arch(), "x86_64");
assert!(config.simd_enabled());
}
#[test]
fn test_optimization_level_setting() {
let mut config = AotConfig::new().expect("Failed to create config");
config.set_optimization_level(1).expect("Failed to set opt level");
config
.set_optimization_level(1)
.expect("Failed to set opt level");
assert_eq!(config.optimization_level(), 1);
}
#[test]
fn test_invalid_optimization_level() {
let mut config = AotConfig::new().expect("Failed to create config");
assert!(config.set_optimization_level(4).is_err());
}
}
}

View File

@ -1,12 +1,12 @@
/*!
* Executable Builder - Creates standalone native executables
*
*
* Embeds precompiled WASM modules into self-contained executables
*/
use super::{AotError, AotConfig};
use std::path::Path;
use super::{AotConfig, AotError};
use std::fs;
use std::path::Path;
/// Builder for creating standalone executable files
pub struct ExecutableBuilder<'a> {
@ -24,73 +24,79 @@ impl<'a> ExecutableBuilder<'a> {
runtime_template: RUNTIME_TEMPLATE,
}
}
/// Embed precompiled module data
pub fn embed_precompiled_module(&mut self, module_data: Vec<u8>) -> Result<(), AotError> {
self.precompiled_module = Some(module_data);
Ok(())
}
/// Create the standalone executable
pub fn create_executable<P: AsRef<Path>>(&self, output_path: P) -> Result<(), AotError> {
let module_data = self.precompiled_module.as_ref()
.ok_or_else(|| AotError::CompilationError("No precompiled module embedded".to_string()))?;
let module_data = self.precompiled_module.as_ref().ok_or_else(|| {
AotError::CompilationError("No precompiled module embedded".to_string())
})?;
// Generate the runtime code with embedded module
let runtime_code = self.generate_runtime_code(module_data)?;
// Write to temporary Rust source file
let temp_dir = std::env::temp_dir();
let temp_main = temp_dir.join("nyash_aot_main.rs");
let temp_cargo = temp_dir.join("Cargo.toml");
fs::write(&temp_main, runtime_code)?;
fs::write(&temp_cargo, self.generate_cargo_toml())?;
// Compile with Rust compiler
self.compile_rust_executable(&temp_dir, output_path)?;
// Clean up temporary files
let _ = fs::remove_file(&temp_main);
let _ = fs::remove_file(&temp_cargo);
Ok(())
}
/// Generate the runtime code with embedded module
fn generate_runtime_code(&self, module_data: &[u8]) -> Result<String, AotError> {
let module_bytes = self.format_module_bytes(module_data);
let compatibility_key = self.config.compatibility_key();
let runtime_code = self.runtime_template
let runtime_code = self
.runtime_template
.replace("{{MODULE_BYTES}}", &module_bytes)
.replace("{{COMPATIBILITY_KEY}}", &compatibility_key)
.replace("{{OPTIMIZATION_LEVEL}}", &self.config.optimization_level().to_string())
.replace(
"{{OPTIMIZATION_LEVEL}}",
&self.config.optimization_level().to_string(),
)
.replace("{{TARGET_ARCH}}", self.config.target_arch())
.replace("{{WASMTIME_VERSION}}", "18.0");
Ok(runtime_code)
}
/// Format module bytes as Rust byte array literal
fn format_module_bytes(&self, data: &[u8]) -> String {
let mut result = String::with_capacity(data.len() * 6);
result.push_str("&[\n ");
for (i, byte) in data.iter().enumerate() {
if i > 0 && i % 16 == 0 {
result.push_str("\n ");
}
result.push_str(&format!("0x{:02x}, ", byte));
}
result.push_str("\n]");
result
}
/// Generate Cargo.toml for the executable
fn generate_cargo_toml(&self) -> String {
format!(r#"[package]
format!(
r#"[package]
name = "nyash-aot-executable"
version = "0.1.0"
edition = "2021"
@ -108,27 +114,36 @@ strip = true
[[bin]]
name = "nyash-aot-executable"
path = "nyash_aot_main.rs"
"#)
"#
)
}
/// Compile the Rust executable
fn compile_rust_executable<P: AsRef<Path>, Q: AsRef<Path>>(&self, temp_dir: P, output_path: Q) -> Result<(), AotError> {
fn compile_rust_executable<P: AsRef<Path>, Q: AsRef<Path>>(
&self,
temp_dir: P,
output_path: Q,
) -> Result<(), AotError> {
let temp_dir = temp_dir.as_ref();
let output_path = output_path.as_ref();
// Use cargo to compile
let mut cmd = std::process::Command::new("cargo");
cmd.current_dir(temp_dir)
.args(&["build", "--release", "--bin", "nyash-aot-executable"]);
let output = cmd.output()
.args(&["build", "--release", "--bin", "nyash-aot-executable"]);
let output = cmd
.output()
.map_err(|e| AotError::CompilationError(format!("Failed to run cargo: {}", e)))?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
return Err(AotError::CompilationError(format!("Cargo build failed: {}", stderr)));
return Err(AotError::CompilationError(format!(
"Cargo build failed: {}",
stderr
)));
}
// Copy the compiled executable to the desired location
let compiled_exe = temp_dir.join("target/release/nyash-aot-executable");
let compiled_exe = if cfg!(windows) {
@ -136,14 +151,16 @@ path = "nyash_aot_main.rs"
} else {
compiled_exe
};
if !compiled_exe.exists() {
return Err(AotError::CompilationError("Compiled executable not found".to_string()));
return Err(AotError::CompilationError(
"Compiled executable not found".to_string(),
));
}
fs::copy(&compiled_exe, output_path)
.map_err(|e| AotError::IOError(format!("Failed to copy executable: {}", e)))?;
Ok(())
}
}
@ -223,7 +240,7 @@ fn run_aot_module() -> Result<(), Box<dyn std::error::Error>> {
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_executable_builder_creation() {
let config = AotConfig::new().expect("Failed to create config");
@ -231,50 +248,54 @@ mod tests {
// Should not panic
assert!(true);
}
#[test]
fn test_embed_module() {
let config = AotConfig::new().expect("Failed to create config");
let mut builder = ExecutableBuilder::new(&config);
let test_data = vec![1, 2, 3, 4, 5];
builder.embed_precompiled_module(test_data).expect("Failed to embed module");
builder
.embed_precompiled_module(test_data)
.expect("Failed to embed module");
assert!(builder.precompiled_module.is_some());
}
#[test]
fn test_format_module_bytes() {
let config = AotConfig::new().expect("Failed to create config");
let builder = ExecutableBuilder::new(&config);
let test_data = vec![0x00, 0x61, 0x73, 0x6d];
let formatted = builder.format_module_bytes(&test_data);
assert!(formatted.contains("0x00"));
assert!(formatted.contains("0x61"));
assert!(formatted.contains("0x73"));
assert!(formatted.contains("0x6d"));
}
#[test]
fn test_cargo_toml_generation() {
let config = AotConfig::new().expect("Failed to create config");
let builder = ExecutableBuilder::new(&config);
let cargo_toml = builder.generate_cargo_toml();
assert!(cargo_toml.contains("nyash-aot-executable"));
assert!(cargo_toml.contains("wasmtime"));
assert!(cargo_toml.contains("opt-level = 3"));
}
#[test]
fn test_runtime_code_generation() {
let config = AotConfig::new().expect("Failed to create config");
let builder = ExecutableBuilder::new(&config);
let test_data = vec![0x00, 0x61, 0x73, 0x6d];
let runtime_code = builder.generate_runtime_code(&test_data).expect("Failed to generate runtime");
let runtime_code = builder
.generate_runtime_code(&test_data)
.expect("Failed to generate runtime");
assert!(runtime_code.contains("MODULE_DATA"));
assert!(runtime_code.contains("0x00"));
assert!(runtime_code.contains("18.0")); // Wasmtime version
assert!(runtime_code.contains("18.0")); // Wasmtime version
}
}
}

View File

@ -1,17 +1,17 @@
/*!
* AOT (Ahead-of-Time) Backend - Phase 9 Implementation
*
*
* Provides native executable generation using wasmtime precompilation
* for maximum performance and zero JIT startup overhead
*/
mod compiler;
mod executable;
mod config;
mod executable;
pub use compiler::AotCompiler;
pub use executable::ExecutableBuilder;
pub use config::AotConfig;
pub use executable::ExecutableBuilder;
use crate::mir::MirModule;
use std::path::Path;
@ -64,53 +64,47 @@ impl AotBackend {
pub fn new() -> Result<Self, AotError> {
let config = AotConfig::new()?;
let compiler = AotCompiler::new(&config)?;
Ok(Self {
compiler,
config,
})
Ok(Self { compiler, config })
}
/// Create AOT backend with custom configuration
pub fn with_config(config: AotConfig) -> Result<Self, AotError> {
let compiler = AotCompiler::new(&config)?;
Ok(Self {
compiler,
config,
})
Ok(Self { compiler, config })
}
/// Compile MIR module to standalone native executable
pub fn compile_to_executable<P: AsRef<Path>>(
&mut self,
mir_module: MirModule,
output_path: P
&mut self,
mir_module: MirModule,
output_path: P,
) -> Result<(), AotError> {
// For now, just create a .cwasm precompiled module
// TODO: Implement full standalone executable generation
let cwasm_path = output_path.as_ref().with_extension("cwasm");
self.compile_to_precompiled(mir_module, cwasm_path)
}
/// Compile MIR module to .cwasm precompiled module
pub fn compile_to_precompiled<P: AsRef<Path>>(
&mut self,
mir_module: MirModule,
output_path: P
output_path: P,
) -> Result<(), AotError> {
// Compile MIR to WASM
let wasm_bytes = self.compiler.compile_mir_to_wasm(mir_module)?;
// Precompile WASM to .cwasm
let precompiled_module = self.compiler.precompile_wasm(&wasm_bytes)?;
// Write to file
std::fs::write(output_path, precompiled_module)?;
Ok(())
}
/// Get performance statistics
pub fn get_stats(&self) -> AotStats {
self.compiler.get_stats()
@ -136,17 +130,17 @@ pub struct AotStats {
mod tests {
use super::*;
use crate::mir::MirModule;
#[test]
fn test_aot_backend_creation() {
let _backend = AotBackend::new();
// Should not panic - basic creation test
assert!(true);
}
#[test]
#[test]
fn test_default_config() {
let config = AotConfig::new().expect("Failed to create default config");
assert!(config.optimization_level() >= 1);
}
}
}

View File

@ -5,8 +5,8 @@
* Status: Initial skeleton for future extraction from vm.rs
*/
use super::vm::VMError;
use crate::mir::BasicBlockId;
use super::vm::{VMError};
/// Result of a block step when evaluating a terminator
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@ -30,4 +30,3 @@ pub fn record_transition(
loop_recorder.record_transition(from, to);
Ok(())
}

View File

@ -7,11 +7,16 @@
#![cfg(feature = "cranelift-jit")]
use crate::jit::lower::builder::{IRBuilder, BinOpKind, CmpKind, ParamKind};
use crate::jit::lower::builder::{BinOpKind, CmpKind, IRBuilder, ParamKind};
use cranelift_codegen::ir::InstBuilder;
// Minimal recorded opcodes for Const/Add/Return first
enum RecOp { ConstI64(i64), ConstF64(f64), BinOp(BinOpKind), Return }
enum RecOp {
ConstI64(i64),
ConstF64(f64),
BinOp(BinOpKind),
Return,
}
pub struct ClifBuilder {
pub consts: usize,
@ -23,24 +28,36 @@ pub struct ClifBuilder {
}
impl ClifBuilder {
pub fn new() -> Self { Self { consts: 0, binops: 0, cmps: 0, branches: 0, rets: 0, ops: Vec::new() } }
pub fn new() -> Self {
Self {
consts: 0,
binops: 0,
cmps: 0,
branches: 0,
rets: 0,
ops: Vec::new(),
}
}
/// Build and execute the recorded ops as a native function using Cranelift
pub fn finish_and_execute(&self) -> Result<i64, String> {
use cranelift_codegen::ir::{Signature, AbiParam, types};
use cranelift_codegen::ir::{types, AbiParam, Signature};
use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext};
use cranelift_module::{Module, Linkage};
use cranelift_module::{Linkage, Module};
// JIT setup
let isa_builder = cranelift_native::builder().map_err(|e| e.to_string())?;
let flag_builder = cranelift_codegen::settings::builder();
let flags = cranelift_codegen::settings::Flags::new(flag_builder);
let isa = isa_builder.finish(flags).map_err(|e| e.to_string())?;
let jit_builder = cranelift_jit::JITBuilder::with_isa(isa, cranelift_module::default_libcall_names());
let jit_builder =
cranelift_jit::JITBuilder::with_isa(isa, cranelift_module::default_libcall_names());
let mut module = cranelift_jit::JITModule::new(jit_builder);
// Signature ()->i64
let mut sig = Signature::new(module.target_config().default_call_conv);
sig.returns.push(AbiParam::new(types::I64));
let func_id = module.declare_function("ny_lowercore_main", Linkage::Export, &sig).map_err(|e| e.to_string())?;
let func_id = module
.declare_function("ny_lowercore_main", Linkage::Export, &sig)
.map_err(|e| e.to_string())?;
let mut ctx = module.make_context();
ctx.func.signature = sig;
let mut fbc = FunctionBuilderContext::new();
@ -53,16 +70,31 @@ impl ClifBuilder {
let mut did_return = false;
for op in &self.ops {
match *op {
RecOp::ConstI64(i) => { vs.push(fb.ins().iconst(types::I64, i)); }
RecOp::ConstF64(f) => { let fv = fb.ins().f64const(f); let iv = fb.ins().fcvt_to_sint(types::I64, fv); vs.push(iv); }
RecOp::ConstI64(i) => {
vs.push(fb.ins().iconst(types::I64, i));
}
RecOp::ConstF64(f) => {
let fv = fb.ins().f64const(f);
let iv = fb.ins().fcvt_to_sint(types::I64, fv);
vs.push(iv);
}
RecOp::BinOp(BinOpKind::Add) => {
if vs.len() < 2 { vs.clear(); vs.push(fb.ins().iconst(types::I64, 0)); } else {
let r = vs.pop().unwrap(); let l = vs.pop().unwrap(); vs.push(fb.ins().iadd(l, r));
if vs.len() < 2 {
vs.clear();
vs.push(fb.ins().iconst(types::I64, 0));
} else {
let r = vs.pop().unwrap();
let l = vs.pop().unwrap();
vs.push(fb.ins().iadd(l, r));
}
}
RecOp::BinOp(_) => { /* ignore others for now */ }
RecOp::Return => {
let retv = if let Some(v) = vs.last().copied() { v } else { fb.ins().iconst(types::I64, 0) };
let retv = if let Some(v) = vs.last().copied() {
v
} else {
fb.ins().iconst(types::I64, 0)
};
fb.ins().return_(&[retv]);
did_return = true;
}
@ -70,12 +102,18 @@ impl ClifBuilder {
}
// Ensure function ends with return
if !did_return {
let retv = if let Some(v) = vs.last().copied() { v } else { fb.ins().iconst(types::I64, 0) };
let retv = if let Some(v) = vs.last().copied() {
v
} else {
fb.ins().iconst(types::I64, 0)
};
fb.ins().return_(&[retv]);
}
fb.seal_block(entry);
fb.finalize();
module.define_function(func_id, &mut ctx).map_err(|e| e.to_string())?;
module
.define_function(func_id, &mut ctx)
.map_err(|e| e.to_string())?;
module.clear_context(&mut ctx);
let _ = module.finalize_definitions();
let code = module.get_finalized_function(func_id);
@ -87,35 +125,77 @@ impl ClifBuilder {
impl IRBuilder for ClifBuilder {
fn begin_function(&mut self, _name: &str) {}
fn end_function(&mut self) {}
fn prepare_signature_i64(&mut self, _argc: usize, _has_ret: bool) { }
fn prepare_signature_typed(&mut self, _params: &[ParamKind], _ret_is_f64: bool) { }
fn emit_param_i64(&mut self, _index: usize) { }
fn emit_const_i64(&mut self, val: i64) { self.consts += 1; self.ops.push(RecOp::ConstI64(val)); }
fn emit_const_f64(&mut self, val: f64) { self.consts += 1; self.ops.push(RecOp::ConstF64(val)); }
fn emit_binop(&mut self, op: BinOpKind) { self.binops += 1; self.ops.push(RecOp::BinOp(op)); }
fn emit_compare(&mut self, _op: CmpKind) { self.cmps += 1; }
fn prepare_signature_i64(&mut self, _argc: usize, _has_ret: bool) {}
fn prepare_signature_typed(&mut self, _params: &[ParamKind], _ret_is_f64: bool) {}
fn emit_param_i64(&mut self, _index: usize) {}
fn emit_const_i64(&mut self, val: i64) {
self.consts += 1;
self.ops.push(RecOp::ConstI64(val));
}
fn emit_const_f64(&mut self, val: f64) {
self.consts += 1;
self.ops.push(RecOp::ConstF64(val));
}
fn emit_binop(&mut self, op: BinOpKind) {
self.binops += 1;
self.ops.push(RecOp::BinOp(op));
}
fn emit_compare(&mut self, _op: CmpKind) {
self.cmps += 1;
}
fn emit_jump(&mut self) {}
fn emit_branch(&mut self) { self.branches += 1; }
fn emit_return(&mut self) { self.rets += 1; self.ops.push(RecOp::Return); }
fn emit_host_call(&mut self, _symbol: &str, _argc: usize, _has_ret: bool) { }
fn emit_host_call_typed(&mut self, _symbol: &str, _params: &[ParamKind], _has_ret: bool, _ret_is_f64: bool) { }
fn emit_plugin_invoke(&mut self, _type_id: u32, _method_id: u32, _argc: usize, _has_ret: bool) { }
fn emit_plugin_invoke_by_name(&mut self, _method: &str, _argc: usize, _has_ret: bool) { }
fn prepare_blocks(&mut self, _count: usize) { }
fn switch_to_block(&mut self, _index: usize) { }
fn seal_block(&mut self, _index: usize) { }
fn br_if_top_is_true(&mut self, _then_index: usize, _else_index: usize) { }
fn jump_to(&mut self, _target_index: usize) { }
fn ensure_block_params_i64(&mut self, _index: usize, _count: usize) { }
fn ensure_block_params_b1(&mut self, index: usize, count: usize) { self.ensure_block_params_i64(index, count); }
fn ensure_block_param_i64(&mut self, index: usize) { self.ensure_block_params_i64(index, 1); }
fn push_block_param_i64_at(&mut self, _pos: usize) { }
fn push_block_param_b1_at(&mut self, pos: usize) { self.push_block_param_i64_at(pos); }
fn push_block_param_i64(&mut self) { self.push_block_param_i64_at(0); }
fn br_if_with_args(&mut self, _then_index: usize, _else_index: usize, _then_n: usize, _else_n: usize) { self.emit_branch(); }
fn jump_with_args(&mut self, _target_index: usize, _n: usize) { self.emit_jump(); }
fn hint_ret_bool(&mut self, _is_b1: bool) { }
fn ensure_local_i64(&mut self, _index: usize) { }
fn store_local_i64(&mut self, _index: usize) { }
fn load_local_i64(&mut self, _index: usize) { }
fn emit_branch(&mut self) {
self.branches += 1;
}
fn emit_return(&mut self) {
self.rets += 1;
self.ops.push(RecOp::Return);
}
fn emit_host_call(&mut self, _symbol: &str, _argc: usize, _has_ret: bool) {}
fn emit_host_call_typed(
&mut self,
_symbol: &str,
_params: &[ParamKind],
_has_ret: bool,
_ret_is_f64: bool,
) {
}
fn emit_plugin_invoke(&mut self, _type_id: u32, _method_id: u32, _argc: usize, _has_ret: bool) {
}
fn emit_plugin_invoke_by_name(&mut self, _method: &str, _argc: usize, _has_ret: bool) {}
fn prepare_blocks(&mut self, _count: usize) {}
fn switch_to_block(&mut self, _index: usize) {}
fn seal_block(&mut self, _index: usize) {}
fn br_if_top_is_true(&mut self, _then_index: usize, _else_index: usize) {}
fn jump_to(&mut self, _target_index: usize) {}
fn ensure_block_params_i64(&mut self, _index: usize, _count: usize) {}
fn ensure_block_params_b1(&mut self, index: usize, count: usize) {
self.ensure_block_params_i64(index, count);
}
fn ensure_block_param_i64(&mut self, index: usize) {
self.ensure_block_params_i64(index, 1);
}
fn push_block_param_i64_at(&mut self, _pos: usize) {}
fn push_block_param_b1_at(&mut self, pos: usize) {
self.push_block_param_i64_at(pos);
}
fn push_block_param_i64(&mut self) {
self.push_block_param_i64_at(0);
}
fn br_if_with_args(
&mut self,
_then_index: usize,
_else_index: usize,
_then_n: usize,
_else_n: usize,
) {
self.emit_branch();
}
fn jump_with_args(&mut self, _target_index: usize, _n: usize) {
self.emit_jump();
}
fn hint_ret_bool(&mut self, _is_b1: bool) {}
fn ensure_local_i64(&mut self, _index: usize) {}
fn store_local_i64(&mut self, _index: usize) {}
fn load_local_i64(&mut self, _index: usize) {}
}

View File

@ -2,10 +2,10 @@
use std::collections::HashMap;
use cranelift_codegen::ir::{Block, Signature, AbiParam, types};
use cranelift_codegen::ir::{types, AbiParam, Block, Signature};
use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext};
use cranelift_jit::{JITBuilder, JITModule};
use cranelift_module::{Module, Linkage, FuncId};
use cranelift_module::{FuncId, Linkage, Module};
use crate::mir::{BasicBlockId, MirFunction, ValueId};
@ -13,9 +13,15 @@ use crate::mir::{BasicBlockId, MirFunction, ValueId};
pub struct BlockMap(pub HashMap<BasicBlockId, Block>);
impl BlockMap {
pub fn new() -> Self { Self(HashMap::new()) }
pub fn get(&self, bb: &BasicBlockId) -> Option<&Block> { self.0.get(bb) }
pub fn insert(&mut self, bb: BasicBlockId, blk: Block) { self.0.insert(bb, blk); }
pub fn new() -> Self {
Self(HashMap::new())
}
pub fn get(&self, bb: &BasicBlockId) -> Option<&Block> {
self.0.get(bb)
}
pub fn insert(&mut self, bb: BasicBlockId, blk: Block) {
self.0.insert(bb, blk);
}
/// Create a CLIF block for each MIR block id
pub fn create_for_function(func: &MirFunction, builder: &mut FunctionBuilder) -> Self {
let mut m = HashMap::new();
@ -33,15 +39,31 @@ pub struct ValueEnv {
}
impl ValueEnv {
pub fn new() -> Self { Self { vals: HashMap::new(), mem: HashMap::new() } }
pub fn get_val(&self, id: &ValueId) -> Result<cranelift_codegen::ir::Value, String> {
self.vals.get(id).cloned().ok_or_else(|| format!("undef {:?}", id))
pub fn new() -> Self {
Self {
vals: HashMap::new(),
mem: HashMap::new(),
}
}
pub fn set_val(&mut self, id: ValueId, v: cranelift_codegen::ir::Value) { self.vals.insert(id, v); }
pub fn get_mem_or(&self, id: &ValueId, default: cranelift_codegen::ir::Value) -> cranelift_codegen::ir::Value {
pub fn get_val(&self, id: &ValueId) -> Result<cranelift_codegen::ir::Value, String> {
self.vals
.get(id)
.cloned()
.ok_or_else(|| format!("undef {:?}", id))
}
pub fn set_val(&mut self, id: ValueId, v: cranelift_codegen::ir::Value) {
self.vals.insert(id, v);
}
pub fn get_mem_or(
&self,
id: &ValueId,
default: cranelift_codegen::ir::Value,
) -> cranelift_codegen::ir::Value {
*self.mem.get(id).unwrap_or(&default)
}
pub fn set_mem(&mut self, id: ValueId, v: cranelift_codegen::ir::Value) { self.mem.insert(id, v); }
pub fn set_mem(&mut self, id: ValueId, v: cranelift_codegen::ir::Value) {
self.mem.insert(id, v);
}
}
/// Cranelift JIT module wrapper (context)
@ -56,24 +78,37 @@ impl ClifContext {
let flags = cranelift_codegen::settings::Flags::new(flag_builder);
let isa = isa_builder.finish(flags).map_err(|e| e.to_string())?;
let jit_builder = JITBuilder::with_isa(isa, cranelift_module::default_libcall_names());
Ok(Self { module: JITModule::new(jit_builder) })
Ok(Self {
module: JITModule::new(jit_builder),
})
}
/// Declare an exported i64-return function and return its id and Cranelift context/signature
pub fn declare_i64_fn(&mut self, name: &str) -> Result<(FuncId, cranelift_codegen::Context, Signature), String> {
pub fn declare_i64_fn(
&mut self,
name: &str,
) -> Result<(FuncId, cranelift_codegen::Context, Signature), String> {
let mut sig = Signature::new(self.module.target_config().default_call_conv);
sig.returns.push(AbiParam::new(types::I64));
let func_id = self.module.declare_function(name, Linkage::Export, &sig).map_err(|e| e.to_string())?;
let func_id = self
.module
.declare_function(name, Linkage::Export, &sig)
.map_err(|e| e.to_string())?;
let mut ctx = self.module.make_context();
ctx.func.signature = sig.clone();
Ok((func_id, ctx, sig))
}
pub fn finalize(&mut self, func_id: FuncId, ctx: &mut cranelift_codegen::Context) -> Result<*const u8, String> {
self.module.define_function(func_id, ctx).map_err(|e| e.to_string())?;
pub fn finalize(
&mut self,
func_id: FuncId,
ctx: &mut cranelift_codegen::Context,
) -> Result<*const u8, String> {
self.module
.define_function(func_id, ctx)
.map_err(|e| e.to_string())?;
self.module.clear_context(ctx);
let _ = self.module.finalize_definitions();
Ok(self.module.get_finalized_function(func_id))
}
}

View File

@ -1,12 +1,15 @@
#![cfg(feature = "cranelift-jit")]
use cranelift_codegen::ir::{AbiParam, InstBuilder, Signature, types, condcodes::IntCC, StackSlot, StackSlotData, StackSlotKind};
use cranelift_codegen::ir::{
condcodes::IntCC, types, AbiParam, InstBuilder, Signature, StackSlot, StackSlotData,
StackSlotKind,
};
use cranelift_codegen::isa;
use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext};
use cranelift_jit::{JITBuilder, JITModule};
use cranelift_module::{Module, Linkage};
use cranelift_module::{Linkage, Module};
use crate::mir::{MirFunction, MirInstruction, ConstValue, ValueId, BasicBlockId, CompareOp};
use crate::mir::{BasicBlockId, CompareOp, ConstValue, MirFunction, MirInstruction, ValueId};
/// Compile a minimal subset of MIR(main) to a native function and execute it.
/// Supported: Const(Integer), BinOp(Add for integers), Return(Integer or default 0).
@ -22,8 +25,11 @@ pub fn compile_and_execute_minimal(main: &MirFunction) -> Result<i64, String> {
// Signature: () -> i64
let mut sig = Signature::new(module.target_config().default_call_conv);
sig.returns.push(AbiParam::new(cranelift_codegen::ir::types::I64));
let func_id = module.declare_function("ny_main", Linkage::Export, &sig).map_err(|e| e.to_string())?;
sig.returns
.push(AbiParam::new(cranelift_codegen::ir::types::I64));
let func_id = module
.declare_function("ny_main", Linkage::Export, &sig)
.map_err(|e| e.to_string())?;
let mut ctx = module.make_context();
ctx.func.signature = sig;
@ -36,7 +42,9 @@ pub fn compile_and_execute_minimal(main: &MirFunction) -> Result<i64, String> {
let mut vals: HashMap<ValueId, cranelift_codegen::ir::Value> = HashMap::new();
let mut slots: HashMap<ValueId, StackSlot> = HashMap::new();
for (bb_id, _) in &main.blocks { clif_blocks.insert(*bb_id, builder.create_block()); }
for (bb_id, _) in &main.blocks {
clif_blocks.insert(*bb_id, builder.create_block());
}
// Switch to entry
let entry = *clif_blocks.get(&main.entry_block).unwrap();
builder.switch_to_block(entry);
@ -55,9 +63,16 @@ pub fn compile_and_execute_minimal(main: &MirFunction) -> Result<i64, String> {
MirInstruction::Const { dst, value } => {
let v = match value {
ConstValue::Integer(i) => builder.ins().iconst(types::I64, *i),
ConstValue::Bool(b) => builder.ins().iconst(types::I64, if *b { 1 } else { 0 }),
ConstValue::Float(f) => { let fv = builder.ins().f64const(*f); builder.ins().fcvt_to_sint(types::I64, fv) },
ConstValue::String(_) | ConstValue::Null | ConstValue::Void => builder.ins().iconst(types::I64, 0),
ConstValue::Bool(b) => {
builder.ins().iconst(types::I64, if *b { 1 } else { 0 })
}
ConstValue::Float(f) => {
let fv = builder.ins().f64const(*f);
builder.ins().fcvt_to_sint(types::I64, fv)
}
ConstValue::String(_) | ConstValue::Null | ConstValue::Void => {
builder.ins().iconst(types::I64, 0)
}
};
vals.insert(*dst, v);
}
@ -78,7 +93,14 @@ pub fn compile_and_execute_minimal(main: &MirFunction) -> Result<i64, String> {
MirInstruction::Compare { dst, op, lhs, rhs } => {
let l = *vals.get(lhs).ok_or_else(|| format!("undef {:?}", lhs))?;
let r = *vals.get(rhs).ok_or_else(|| format!("undef {:?}", rhs))?;
let cc = match op { CompareOp::Eq => IntCC::Equal, CompareOp::Ne => IntCC::NotEqual, CompareOp::Lt => IntCC::SignedLessThan, CompareOp::Le => IntCC::SignedLessThanOrEqual, CompareOp::Gt => IntCC::SignedGreaterThan, CompareOp::Ge => IntCC::SignedGreaterThanOrEqual };
let cc = match op {
CompareOp::Eq => IntCC::Equal,
CompareOp::Ne => IntCC::NotEqual,
CompareOp::Lt => IntCC::SignedLessThan,
CompareOp::Le => IntCC::SignedLessThanOrEqual,
CompareOp::Gt => IntCC::SignedGreaterThan,
CompareOp::Ge => IntCC::SignedGreaterThanOrEqual,
};
let b1 = builder.ins().icmp(cc, l, r);
let one = builder.ins().iconst(types::I64, 1);
let zero = builder.ins().iconst(types::I64, 0);
@ -94,8 +116,15 @@ pub fn compile_and_execute_minimal(main: &MirFunction) -> Result<i64, String> {
}
}
MirInstruction::Store { value, ptr } => {
let v = *vals.get(value).ok_or_else(|| format!("undef {:?}", value))?;
let ss = *slots.entry(*ptr).or_insert_with(|| builder.create_sized_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, 8)));
let v = *vals
.get(value)
.ok_or_else(|| format!("undef {:?}", value))?;
let ss = *slots.entry(*ptr).or_insert_with(|| {
builder.create_sized_stack_slot(StackSlotData::new(
StackSlotKind::ExplicitSlot,
8,
))
});
builder.ins().stack_store(v, ss, 0);
}
MirInstruction::Copy { dst, src } => {
@ -108,21 +137,32 @@ pub fn compile_and_execute_minimal(main: &MirFunction) -> Result<i64, String> {
// Terminator
match &bb.terminator {
Some(MirInstruction::Return { value }) => {
let retv = if let Some(v) = value { *vals.get(v).unwrap_or(&builder.ins().iconst(types::I64, 0)) } else { builder.ins().iconst(types::I64, 0) };
let retv = if let Some(v) = value {
*vals.get(v).unwrap_or(&builder.ins().iconst(types::I64, 0))
} else {
builder.ins().iconst(types::I64, 0)
};
builder.ins().return_(&[retv]);
}
Some(MirInstruction::Jump { target }) => {
let t = *clif_blocks.get(target).unwrap();
builder.ins().jump(t, &[]);
}
Some(MirInstruction::Branch { condition, then_bb, else_bb }) => {
let cond_i64 = *vals.get(condition).unwrap_or(&builder.ins().iconst(types::I64, 0));
Some(MirInstruction::Branch {
condition,
then_bb,
else_bb,
}) => {
let cond_i64 = *vals
.get(condition)
.unwrap_or(&builder.ins().iconst(types::I64, 0));
let is_true = builder.ins().icmp_imm(IntCC::NotEqual, cond_i64, 0);
let tb = *clif_blocks.get(then_bb).unwrap();
let eb = *clif_blocks.get(else_bb).unwrap();
builder.ins().brif(is_true, tb, &[], eb, &[]);
}
_ => { /* fallthrough not allowed: insert return 0 to keep verifier happy */
_ => {
/* fallthrough not allowed: insert return 0 to keep verifier happy */
let z = builder.ins().iconst(types::I64, 0);
builder.ins().return_(&[z]);
}
@ -132,7 +172,9 @@ pub fn compile_and_execute_minimal(main: &MirFunction) -> Result<i64, String> {
builder.finalize();
module.define_function(func_id, &mut ctx).map_err(|e| e.to_string())?;
module
.define_function(func_id, &mut ctx)
.map_err(|e| e.to_string())?;
module.clear_context(&mut ctx);
let _ = module.finalize_definitions();

View File

@ -6,31 +6,43 @@
#![cfg(feature = "cranelift-jit")]
use crate::mir::{function::MirModule, MirInstruction, ConstValue, ValueId, BinaryOp};
use crate::box_trait::{BoolBox, IntegerBox, NyashBox, StringBox, VoidBox};
use crate::jit::lower::{
builder::{IRBuilder, NoopBuilder},
core::LowerCore,
};
use crate::jit::semantics::clif::ClifSemanticsSkeleton;
use crate::mir::{function::MirModule, BinaryOp, ConstValue, MirInstruction, ValueId};
#[cfg(feature = "cranelift-jit")]
use crate::semantics::Semantics;
use crate::box_trait::{NyashBox, IntegerBox, BoolBox, StringBox, VoidBox};
use std::collections::HashMap;
use crate::jit::lower::{builder::{NoopBuilder, IRBuilder}, core::LowerCore};
use crate::jit::semantics::clif::ClifSemanticsSkeleton;
pub mod context; // Context/Block/Value env wrappers
pub mod builder; // Clif IRBuilder implementation (skeleton)
pub mod builder;
pub mod context; // Context/Block/Value env wrappers // Clif IRBuilder implementation (skeleton)
pub mod lower {}
pub mod jit; // JIT compile/execute using Cranelift (minimal)
pub mod object {}
/// JIT: compile and execute a MIR module (skeleton)
pub fn compile_and_execute(mir_module: &MirModule, _temp_name: &str) -> Result<Box<dyn NyashBox>, String> {
pub fn compile_and_execute(
mir_module: &MirModule,
_temp_name: &str,
) -> Result<Box<dyn NyashBox>, String> {
// Minimal semantics: Const/Return/Add only (straight-line code)
let main = mir_module.functions.get("main").ok_or("missing main function")?;
let main = mir_module
.functions
.get("main")
.ok_or("missing main function")?;
// Minimal ClifSem lowering pass (NoopBuilder): Const/Return/Add カバレッジ確認
if std::env::var("NYASH_JIT_DUMP").ok().as_deref() == Some("1") {
let mut builder = NoopBuilder::new();
let mut lower = LowerCore::new();
let _ = lower.lower_function(main, &mut builder);
eprintln!("[CLIF-LOWER] covered={} unsupported={}", lower.covered, lower.unsupported);
eprintln!(
"[CLIF-LOWER] covered={} unsupported={}",
lower.covered, lower.unsupported
);
}
// まずは新JITエンジン経路を試すLowerCore -> CraneliftBuilder -> 実行)
@ -41,10 +53,13 @@ pub fn compile_and_execute(mir_module: &MirModule, _temp_name: &str) -> Result<B
// 実行(引数なし)。戻り値は MIR の型に合わせて変換
let out = engine.execute_handle(h, &[]);
if let Some(jv) = out {
let vmv = crate::jit::boundary::CallBoundaryBox::to_vm(&main.signature.return_type, jv);
let vmv =
crate::jit::boundary::CallBoundaryBox::to_vm(&main.signature.return_type, jv);
let boxed: Box<dyn NyashBox> = match vmv {
crate::backend::vm::VMValue::Integer(i) => Box::new(IntegerBox::new(i)),
crate::backend::vm::VMValue::Float(f) => Box::new(crate::boxes::FloatBox::new(f)),
crate::backend::vm::VMValue::Float(f) => {
Box::new(crate::boxes::FloatBox::new(f))
}
crate::backend::vm::VMValue::Bool(b) => Box::new(BoolBox::new(b)),
crate::backend::vm::VMValue::String(s) => Box::new(StringBox::new(&s)),
crate::backend::vm::VMValue::BoxRef(b) => b.share_box(),
@ -63,19 +78,30 @@ pub fn compile_and_execute(mir_module: &MirModule, _temp_name: &str) -> Result<B
let mut cur = main.entry_block;
let mut last_pred: Option<crate::mir::BasicBlockId> = None;
loop {
let bb = main.blocks.get(&cur).ok_or_else(|| format!("invalid bb {:?}", cur))?;
let bb = main
.blocks
.get(&cur)
.ok_or_else(|| format!("invalid bb {:?}", cur))?;
// PHI (very minimal): choose first input or predecessor match
for inst in &bb.instructions {
if let MirInstruction::Phi { dst, inputs } = inst {
if let Some(pred) = last_pred {
if let Some((_, v)) = inputs.iter().find(|(b, _)| *b == pred) { if let Some(val) = regs.get(v).cloned() { regs.insert(*dst, val); } }
} else if let Some((_, v)) = inputs.first() { if let Some(val) = regs.get(v).cloned() { regs.insert(*dst, val); } }
if let Some((_, v)) = inputs.iter().find(|(b, _)| *b == pred) {
if let Some(val) = regs.get(v).cloned() {
regs.insert(*dst, val);
}
}
} else if let Some((_, v)) = inputs.first() {
if let Some(val) = regs.get(v).cloned() {
regs.insert(*dst, val);
}
}
}
}
let mut sem = ClifSemanticsSkeleton::new();
for inst in &bb.instructions {
match inst {
MirInstruction::Const { dst, value } => {
let mut sem = ClifSemanticsSkeleton::new();
for inst in &bb.instructions {
match inst {
MirInstruction::Const { dst, value } => {
let vv = match value {
ConstValue::Integer(i) => sem.const_i64(*i),
ConstValue::Float(f) => sem.const_f64(*f),
@ -84,39 +110,87 @@ pub fn compile_and_execute(mir_module: &MirModule, _temp_name: &str) -> Result<B
ConstValue::Null | ConstValue::Void => sem.const_null(),
};
regs.insert(*dst, vv);
}
MirInstruction::BinOp { dst, op, lhs, rhs } if matches!(op, BinaryOp::Add) => {
use crate::backend::vm::VMValue as V;
let a = regs.get(lhs).cloned().ok_or_else(|| format!("undef {:?}", lhs))?;
let b = regs.get(rhs).cloned().ok_or_else(|| format!("undef {:?}", rhs))?;
let out = sem.add(a, b);
regs.insert(*dst, out);
}
MirInstruction::Copy { dst, src } => {
if let Some(v) = regs.get(src).cloned() { regs.insert(*dst, v); }
}
MirInstruction::Debug { .. } | MirInstruction::Print { .. } | MirInstruction::Barrier { .. } | MirInstruction::BarrierRead { .. } | MirInstruction::BarrierWrite { .. } | MirInstruction::Safepoint | MirInstruction::Load { .. } | MirInstruction::Store { .. } | MirInstruction::TypeOp { .. } | MirInstruction::Compare { .. } | MirInstruction::NewBox { .. } | MirInstruction::PluginInvoke { .. } | MirInstruction::BoxCall { .. } | MirInstruction::RefGet { .. } | MirInstruction::RefSet { .. } | MirInstruction::WeakRef { .. } | MirInstruction::FutureNew { .. } | MirInstruction::FutureSet { .. } | MirInstruction::Await { .. } | MirInstruction::Throw { .. } | MirInstruction::Catch { .. } => {
MirInstruction::BinOp { dst, op, lhs, rhs } if matches!(op, BinaryOp::Add) => {
use crate::backend::vm::VMValue as V;
let a = regs
.get(lhs)
.cloned()
.ok_or_else(|| format!("undef {:?}", lhs))?;
let b = regs
.get(rhs)
.cloned()
.ok_or_else(|| format!("undef {:?}", rhs))?;
let out = sem.add(a, b);
regs.insert(*dst, out);
}
MirInstruction::Copy { dst, src } => {
if let Some(v) = regs.get(src).cloned() {
regs.insert(*dst, v);
}
}
MirInstruction::Debug { .. }
| MirInstruction::Print { .. }
| MirInstruction::Barrier { .. }
| MirInstruction::BarrierRead { .. }
| MirInstruction::BarrierWrite { .. }
| MirInstruction::Safepoint
| MirInstruction::Load { .. }
| MirInstruction::Store { .. }
| MirInstruction::TypeOp { .. }
| MirInstruction::Compare { .. }
| MirInstruction::NewBox { .. }
| MirInstruction::PluginInvoke { .. }
| MirInstruction::BoxCall { .. }
| MirInstruction::RefGet { .. }
| MirInstruction::RefSet { .. }
| MirInstruction::WeakRef { .. }
| MirInstruction::FutureNew { .. }
| MirInstruction::FutureSet { .. }
| MirInstruction::Await { .. }
| MirInstruction::Throw { .. }
| MirInstruction::Catch { .. } => {
// ignore for minimal path
}
MirInstruction::ExternCall { dst, iface_name, method_name, args, .. } => {
MirInstruction::ExternCall {
dst,
iface_name,
method_name,
args,
..
} => {
use crate::backend::vm::VMValue as V;
match (iface_name.as_str(), method_name.as_str()) {
("env.local", "get") => {
if let Some(d) = dst { if let Some(a0) = args.get(0) { if let Some(v) = regs.get(a0).cloned() { regs.insert(*d, v); } } }
if let Some(d) = dst {
if let Some(a0) = args.get(0) {
if let Some(v) = regs.get(a0).cloned() {
regs.insert(*d, v);
}
}
}
}
("env.local", "set") => {
if args.len() >= 2 { if let Some(v) = regs.get(&args[1]).cloned() { regs.insert(args[0], v); } }
if args.len() >= 2 {
if let Some(v) = regs.get(&args[1]).cloned() {
regs.insert(args[0], v);
}
}
// dst ignored
}
("env.box", "new") => {
if let Some(d) = dst {
if let Some(a0) = args.get(0) {
if let Some(V::String(ty)) = regs.get(a0).cloned() {
let reg = crate::runtime::box_registry::get_global_registry();
let reg =
crate::runtime::box_registry::get_global_registry();
// Collect args as NyashBox
let mut ny_args: Vec<Box<dyn crate::box_trait::NyashBox>> = Vec::new();
let mut ny_args: Vec<Box<dyn crate::box_trait::NyashBox>> =
Vec::new();
for vid in args.iter().skip(1) {
if let Some(v) = regs.get(vid).cloned() { ny_args.push(v.to_nyash_box()); }
if let Some(v) = regs.get(vid).cloned() {
ny_args.push(v.to_nyash_box());
}
}
if let Ok(b) = reg.create_box(&ty, &ny_args) {
regs.insert(*d, V::from_nyash_box(b));
@ -151,11 +225,25 @@ pub fn compile_and_execute(mir_module: &MirModule, _temp_name: &str) -> Result<B
};
return Ok(out);
}
Some(MirInstruction::Jump { target }) => { last_pred = Some(bb.id); cur = *target; }
Some(MirInstruction::Branch { condition, then_bb, else_bb }) => {
Some(MirInstruction::Jump { target }) => {
last_pred = Some(bb.id);
cur = *target;
}
Some(MirInstruction::Branch {
condition,
then_bb,
else_bb,
}) => {
// Minimal: integer/bool truthiness
let c = regs.get(condition).cloned().unwrap_or(crate::backend::vm::VMValue::Void);
let t = match c { crate::backend::vm::VMValue::Bool(b) => b, crate::backend::vm::VMValue::Integer(i) => i != 0, _ => false };
let c = regs
.get(condition)
.cloned()
.unwrap_or(crate::backend::vm::VMValue::Void);
let t = match c {
crate::backend::vm::VMValue::Bool(b) => b,
crate::backend::vm::VMValue::Integer(i) => i != 0,
_ => false,
};
last_pred = Some(bb.id);
cur = if t { *then_bb } else { *else_bb };
}

View File

@ -5,15 +5,19 @@
* Status: Initial skeleton; currently unused. Future: build static table for hot-path dispatch.
*/
use crate::mir::MirInstruction;
use super::vm::{VM, VMError};
use super::vm::ControlFlow;
use crate::mir::CompareOp;
use super::vm::VMValue;
use super::vm::{VMError, VM};
use crate::mir::CompareOp;
use crate::mir::MirInstruction;
/// Minimal dispatcher that routes a single instruction to the appropriate handler.
/// Keeps behavior identical to the big match in vm.rs but centralized here.
pub(super) fn execute_instruction(vm: &mut VM, instruction: &MirInstruction, debug_global: bool) -> Result<ControlFlow, VMError> {
pub(super) fn execute_instruction(
vm: &mut VM,
instruction: &MirInstruction,
debug_global: bool,
) -> Result<ControlFlow, VMError> {
match instruction {
// Basic operations
MirInstruction::Const { dst, value } => vm.execute_const(*dst, value),
@ -23,30 +27,59 @@ pub(super) fn execute_instruction(vm: &mut VM, instruction: &MirInstruction, deb
eprintln!("[VM] execute_instruction -> BinOp({:?})", op);
}
vm.execute_binop(*dst, op, *lhs, *rhs)
},
}
MirInstruction::UnaryOp { dst, op, operand } => vm.execute_unaryop(*dst, op, *operand),
MirInstruction::Compare { dst, op, lhs, rhs } => {
let debug_cmp = debug_global || std::env::var("NYASH_VM_DEBUG_CMP").ok().as_deref() == Some("1");
if debug_cmp { eprintln!("[VM] dispatch Compare op={:?} lhs={:?} rhs={:?}", op, lhs, rhs); }
let debug_cmp =
debug_global || std::env::var("NYASH_VM_DEBUG_CMP").ok().as_deref() == Some("1");
if debug_cmp {
eprintln!(
"[VM] dispatch Compare op={:?} lhs={:?} rhs={:?}",
op, lhs, rhs
);
}
if let (Ok(lv), Ok(rv)) = (vm.get_value(*lhs), vm.get_value(*rhs)) {
if debug_cmp { eprintln!("[VM] values before fastpath: left={:?} right={:?}", lv, rv); }
if debug_cmp {
eprintln!("[VM] values before fastpath: left={:?} right={:?}", lv, rv);
}
if let (VMValue::BoxRef(lb), VMValue::BoxRef(rb)) = (&lv, &rv) {
if debug_cmp { eprintln!("[VM] BoxRef types: lty={} rty={} lstr={} rstr={}", lb.type_name(), rb.type_name(), lb.to_string_box().value, rb.to_string_box().value); }
let li = lb.as_any().downcast_ref::<crate::box_trait::IntegerBox>().map(|x| x.value)
if debug_cmp {
eprintln!(
"[VM] BoxRef types: lty={} rty={} lstr={} rstr={}",
lb.type_name(),
rb.type_name(),
lb.to_string_box().value,
rb.to_string_box().value
);
}
let li = lb
.as_any()
.downcast_ref::<crate::box_trait::IntegerBox>()
.map(|x| x.value)
.or_else(|| lb.to_string_box().value.trim().parse::<i64>().ok());
let ri = rb.as_any().downcast_ref::<crate::box_trait::IntegerBox>().map(|x| x.value)
let ri = rb
.as_any()
.downcast_ref::<crate::box_trait::IntegerBox>()
.map(|x| x.value)
.or_else(|| rb.to_string_box().value.trim().parse::<i64>().ok());
if let (Some(li), Some(ri)) = (li, ri) {
let out = match op { CompareOp::Eq => li == ri, CompareOp::Ne => li != ri, CompareOp::Lt => li < ri, CompareOp::Le => li <= ri, CompareOp::Gt => li > ri, CompareOp::Ge => li >= ri };
let out = match op {
CompareOp::Eq => li == ri,
CompareOp::Ne => li != ri,
CompareOp::Lt => li < ri,
CompareOp::Le => li <= ri,
CompareOp::Gt => li > ri,
CompareOp::Ge => li >= ri,
};
vm.set_value(*dst, VMValue::Bool(out));
return Ok(ControlFlow::Continue);
}
}
}
vm.execute_compare(*dst, op, *lhs, *rhs)
},
}
// I/O operations
MirInstruction::Print { value, .. } => vm.execute_print(*value),
@ -57,12 +90,20 @@ pub(super) fn execute_instruction(vm: &mut VM, instruction: &MirInstruction, deb
// Control flow
MirInstruction::Return { value } => {
if crate::config::env::vm_vt_trace() {
if let Some(v) = value { eprintln!("[VT] Dispatch Return val_id={}", v.to_usize()); } else { eprintln!("[VT] Dispatch Return void"); }
if let Some(v) = value {
eprintln!("[VT] Dispatch Return val_id={}", v.to_usize());
} else {
eprintln!("[VT] Dispatch Return void");
}
}
vm.execute_return(*value)
},
}
MirInstruction::Jump { target } => vm.execute_jump(*target),
MirInstruction::Branch { condition, then_bb, else_bb } => vm.execute_branch(*condition, *then_bb, *else_bb),
MirInstruction::Branch {
condition,
then_bb,
else_bb,
} => vm.execute_branch(*condition, *then_bb, *else_bb),
MirInstruction::Phi { dst, inputs } => vm.execute_phi(*dst, inputs),
// Memory operations
@ -71,20 +112,63 @@ pub(super) fn execute_instruction(vm: &mut VM, instruction: &MirInstruction, deb
MirInstruction::Copy { dst, src } => vm.execute_copy(*dst, *src),
// Complex operations
MirInstruction::Call { dst, func, args, effects: _ } => vm.execute_call(*dst, *func, args),
MirInstruction::FunctionNew { dst, params, body, captures, me } => vm.execute_function_new(*dst, params, body, captures, me),
MirInstruction::BoxCall { dst, box_val, method, method_id, args, effects: _ , .. } => vm.execute_boxcall(*dst, *box_val, method, *method_id, args),
MirInstruction::PluginInvoke { dst, box_val, method, args, effects: _ } => vm.execute_plugin_invoke(*dst, *box_val, method, args),
MirInstruction::NewBox { dst, box_type, args } => vm.execute_newbox(*dst, box_type, args),
MirInstruction::Call {
dst,
func,
args,
effects: _,
} => vm.execute_call(*dst, *func, args),
MirInstruction::FunctionNew {
dst,
params,
body,
captures,
me,
} => vm.execute_function_new(*dst, params, body, captures, me),
MirInstruction::BoxCall {
dst,
box_val,
method,
method_id,
args,
effects: _,
..
} => vm.execute_boxcall(*dst, *box_val, method, *method_id, args),
MirInstruction::PluginInvoke {
dst,
box_val,
method,
args,
effects: _,
} => vm.execute_plugin_invoke(*dst, *box_val, method, args),
MirInstruction::NewBox {
dst,
box_type,
args,
} => vm.execute_newbox(*dst, box_type, args),
// Array operations
MirInstruction::ArrayGet { dst, array, index } => vm.execute_array_get(*dst, *array, *index),
MirInstruction::ArraySet { array, index, value } => vm.execute_array_set(*array, *index, *value),
MirInstruction::ArrayGet { dst, array, index } => {
vm.execute_array_get(*dst, *array, *index)
}
MirInstruction::ArraySet {
array,
index,
value,
} => vm.execute_array_set(*array, *index, *value),
// Reference operations
MirInstruction::RefNew { dst, box_val } => vm.execute_ref_new(*dst, *box_val),
MirInstruction::RefGet { dst, reference, field } => vm.execute_ref_get(*dst, *reference, field),
MirInstruction::RefSet { reference, field, value } => vm.execute_ref_set(*reference, field, *value),
MirInstruction::RefGet {
dst,
reference,
field,
} => vm.execute_ref_get(*dst, *reference, field),
MirInstruction::RefSet {
reference,
field,
value,
} => vm.execute_ref_set(*reference, field, *value),
// Weak references
MirInstruction::WeakNew { dst, box_val } => vm.execute_weak_new(*dst, *box_val),
@ -108,11 +192,16 @@ pub(super) fn execute_instruction(vm: &mut VM, instruction: &MirInstruction, deb
let (func, bb, pc) = vm.gc_site_info();
eprintln!("[GC] barrier: Write @{} bb={} pc={}", func, bb, pc);
}
vm.runtime.gc.barrier(crate::runtime::gc::BarrierKind::Write);
vm.runtime
.gc
.barrier(crate::runtime::gc::BarrierKind::Write);
Ok(ControlFlow::Continue)
}
MirInstruction::Barrier { op, .. } => {
let k = match op { crate::mir::BarrierOp::Read => crate::runtime::gc::BarrierKind::Read, crate::mir::BarrierOp::Write => crate::runtime::gc::BarrierKind::Write };
let k = match op {
crate::mir::BarrierOp::Read => crate::runtime::gc::BarrierKind::Read,
crate::mir::BarrierOp::Write => crate::runtime::gc::BarrierKind::Write,
};
if crate::config::env::gc_trace() {
let (func, bb, pc) = vm.gc_site_info();
eprintln!("[GC] barrier: {:?} @{} bb={} pc={}", k, func, bb, pc);
@ -123,7 +212,9 @@ pub(super) fn execute_instruction(vm: &mut VM, instruction: &MirInstruction, deb
// Exceptions
MirInstruction::Throw { exception, .. } => vm.execute_throw(*exception),
MirInstruction::Catch { exception_value, .. } => vm.execute_catch(*exception_value),
MirInstruction::Catch {
exception_value, ..
} => vm.execute_catch(*exception_value),
// Futures
MirInstruction::FutureNew { dst, value } => {
@ -141,15 +232,31 @@ pub(super) fn execute_instruction(vm: &mut VM, instruction: &MirInstruction, deb
future_box.set_result(new_value.to_nyash_box());
Ok(ControlFlow::Continue)
} else {
Err(VMError::TypeError(format!("Expected Future, got {:?}", future_val)))
Err(VMError::TypeError(format!(
"Expected Future, got {:?}",
future_val
)))
}
}
// Special
MirInstruction::Await { dst, future } => vm.execute_await(*dst, *future),
MirInstruction::ExternCall { dst, iface_name, method_name, args, .. } => vm.execute_extern_call(*dst, iface_name, method_name, args),
MirInstruction::TypeCheck { dst, .. } => { vm.set_value(*dst, VMValue::Bool(true)); Ok(ControlFlow::Continue) }
MirInstruction::Cast { dst, value, .. } => { let val = vm.get_value(*value)?; vm.set_value(*dst, val); Ok(ControlFlow::Continue) }
MirInstruction::ExternCall {
dst,
iface_name,
method_name,
args,
..
} => vm.execute_extern_call(*dst, iface_name, method_name, args),
MirInstruction::TypeCheck { dst, .. } => {
vm.set_value(*dst, VMValue::Bool(true));
Ok(ControlFlow::Continue)
}
MirInstruction::Cast { dst, value, .. } => {
let val = vm.get_value(*value)?;
vm.set_value(*dst, val);
Ok(ControlFlow::Continue)
}
MirInstruction::Debug { .. } => Ok(ControlFlow::Continue),
MirInstruction::Nop => Ok(ControlFlow::Continue),
MirInstruction::Safepoint => {
@ -159,9 +266,11 @@ pub(super) fn execute_instruction(vm: &mut VM, instruction: &MirInstruction, deb
}
vm.runtime.gc.safepoint();
// Cooperative scheduling: poll single-thread scheduler
if let Some(s) = &vm.runtime.scheduler { s.poll(); }
if let Some(s) = &vm.runtime.scheduler {
s.poll();
}
Ok(ControlFlow::Continue)
},
}
}
}
@ -172,11 +281,17 @@ pub struct DispatchEntry;
pub struct DispatchTable;
impl DispatchTable {
pub fn new() -> Self { Self }
pub fn new() -> Self {
Self
}
/// Example API for future use: resolve a handler for an instruction
pub fn resolve(&self, _instr: &MirInstruction) -> Option<DispatchEntry> { None }
pub fn resolve(&self, _instr: &MirInstruction) -> Option<DispatchEntry> {
None
}
}
/// Example execution of a dispatch entry
pub fn execute_entry(_entry: &DispatchEntry) -> Result<(), VMError> { Ok(()) }
pub fn execute_entry(_entry: &DispatchEntry) -> Result<(), VMError> {
Ok(())
}

View File

@ -15,7 +15,16 @@ pub struct ExecutionFrame {
}
impl ExecutionFrame {
pub fn new() -> Self { Self { current_block: None, pc: 0, last_result: None } }
pub fn reset(&mut self) { self.current_block = None; self.pc = 0; self.last_result = None; }
pub fn new() -> Self {
Self {
current_block: None,
pc: 0,
last_result: None,
}
}
pub fn reset(&mut self) {
self.current_block = None;
self.pc = 0;
self.last_result = None;
}
}

View File

@ -12,10 +12,16 @@ pub fn is_mutating_builtin_call(recv: &VMValue, method: &str) -> bool {
match recv {
VMValue::BoxRef(b) => {
if b.as_any().downcast_ref::<crate::boxes::array::ArrayBox>().is_some() {
if b.as_any()
.downcast_ref::<crate::boxes::array::ArrayBox>()
.is_some()
{
return ARRAY_METHODS.iter().any(|m| *m == method);
}
if b.as_any().downcast_ref::<crate::boxes::map_box::MapBox>().is_some() {
if b.as_any()
.downcast_ref::<crate::boxes::map_box::MapBox>()
.is_some()
{
return MAP_METHODS.iter().any(|m| *m == method);
}
false
@ -28,7 +34,11 @@ pub fn is_mutating_builtin_call(recv: &VMValue, method: &str) -> bool {
pub fn gc_write_barrier_site(runtime: &crate::runtime::NyashRuntime, site: &str) {
let trace = crate::config::env::gc_trace();
let strict = crate::config::env::gc_barrier_strict();
let before = if strict { runtime.gc.snapshot_counters() } else { None };
let before = if strict {
runtime.gc.snapshot_counters()
} else {
None
};
if trace {
eprintln!("[GC] barrier: Write @{}", site);
}
@ -38,10 +48,16 @@ pub fn gc_write_barrier_site(runtime: &crate::runtime::NyashRuntime, site: &str)
match (before, after) {
(Some((_, _, bw)), Some((_, _, aw))) if aw > bw => {}
(Some(_), Some(_)) => {
panic!("[GC][STRICT] write barrier did not increment at site='{}'", site);
panic!(
"[GC][STRICT] write barrier did not increment at site='{}'",
site
);
}
_ => {
panic!("[GC][STRICT] CountingGc required for strict verification at site='{}'", site);
panic!(
"[GC][STRICT] CountingGc required for strict verification at site='{}'",
site
);
}
}
}

View File

@ -13,4 +13,3 @@ pub mod compiler {
pub mod box_types {
pub use crate::backend::llvm_legacy::box_types::*;
}

View File

@ -3,4 +3,3 @@
pub fn load_box_type_ids() -> std::collections::HashMap<String, u32> {
std::collections::HashMap::new()
}

View File

@ -1,2 +1 @@
// legacy aot placeholder; full implementation retained in archived branch or prior history

View File

@ -0,0 +1,10 @@
//! Legacy LLVM codegen placeholder.
//!
//! The original inkwell-based implementation was removed during the Phase-15
//! refactor. We keep a stub module so tools like `cargo fmt` can resolve the
//! module tree even when the legacy feature is gated off.
#[allow(dead_code)]
pub(crate) fn lower_module() {
// Intentionally left blank.
}

View File

@ -1,2 +1 @@
// legacy helpers placeholder; kept to satisfy module structure after move

View File

@ -0,0 +1,6 @@
//! Legacy LLVM interpreter placeholder.
#[allow(dead_code)]
pub(crate) fn execute() {
// Stub implementation.
}

View File

@ -1,5 +1,5 @@
use crate::box_trait::{IntegerBox, NyashBox};
use crate::mir::function::MirModule;
use crate::box_trait::{NyashBox, IntegerBox};
use std::collections::HashMap;
pub struct LLVMCompiler {
@ -8,7 +8,9 @@ pub struct LLVMCompiler {
impl LLVMCompiler {
pub fn new() -> Result<Self, String> {
Ok(Self { values: HashMap::new() })
Ok(Self {
values: HashMap::new(),
})
}
pub fn compile_module(&self, _mir: &MirModule, _out: &str) -> Result<(), String> {
@ -16,8 +18,11 @@ impl LLVMCompiler {
Ok(())
}
pub fn compile_and_execute(&mut self, _mir: &MirModule, _out: &str) -> Result<Box<dyn NyashBox>, String> {
pub fn compile_and_execute(
&mut self,
_mir: &MirModule,
_out: &str,
) -> Result<Box<dyn NyashBox>, String> {
Ok(Box::new(IntegerBox::new(0)))
}
}

View File

@ -31,4 +31,3 @@ mod tests {
assert!(true);
}
}

View File

@ -11,19 +11,21 @@ pub struct CodegenContext {
#[cfg(not(feature = "llvm-inkwell-legacy"))]
impl CodegenContext {
pub fn new(_module_name: &str) -> Result<Self, String> {
Ok(Self { _phantom: std::marker::PhantomData })
Ok(Self {
_phantom: std::marker::PhantomData,
})
}
}
// Real implementation (compiled only when feature "llvm-inkwell-legacy" is enabled)
#[cfg(feature = "llvm-inkwell-legacy")]
use inkwell::builder::Builder;
#[cfg(feature = "llvm-inkwell-legacy")]
use inkwell::context::Context;
#[cfg(feature = "llvm-inkwell-legacy")]
use inkwell::module::Module;
#[cfg(feature = "llvm-inkwell-legacy")]
use inkwell::builder::Builder;
#[cfg(feature = "llvm-inkwell-legacy")]
use inkwell::targets::{Target, TargetMachine, InitializationConfig};
use inkwell::targets::{InitializationConfig, Target, TargetMachine};
#[cfg(feature = "llvm-inkwell-legacy")]
pub struct CodegenContext<'ctx> {
@ -40,8 +42,8 @@ impl<'ctx> CodegenContext<'ctx> {
.map_err(|e| format!("Failed to initialize native target: {}", e))?;
let module = context.create_module(module_name);
let triple = TargetMachine::get_default_triple();
let target = Target::from_triple(&triple)
.map_err(|e| format!("Failed to get target: {}", e))?;
let target =
Target::from_triple(&triple).map_err(|e| format!("Failed to get target: {}", e))?;
let target_machine = target
.create_target_machine(
&triple,
@ -53,7 +55,11 @@ impl<'ctx> CodegenContext<'ctx> {
)
.ok_or_else(|| "Failed to create target machine".to_string())?;
let builder = context.create_builder();
Ok(Self { context, module, builder, target_machine })
Ok(Self {
context,
module,
builder,
target_machine,
})
}
}

View File

@ -34,4 +34,3 @@ mod tests {
assert!(true);
}
}

View File

@ -8,10 +8,12 @@
use std::collections::HashMap;
use crate::backend::vm::{VMValue, VMError};
use crate::backend::abi_util::{eq_vm, to_bool_vm};
use crate::backend::vm::{VMError, VMValue};
use crate::box_trait::NyashBox;
use crate::mir::{MirModule, MirFunction, MirInstruction, ConstValue, BinaryOp, CompareOp, ValueId, BasicBlockId};
use crate::backend::abi_util::{to_bool_vm, eq_vm};
use crate::mir::{
BasicBlockId, BinaryOp, CompareOp, ConstValue, MirFunction, MirInstruction, MirModule, ValueId,
};
pub struct MirInterpreter {
// SSA value table
@ -23,11 +25,20 @@ pub struct MirInterpreter {
}
impl MirInterpreter {
pub fn new() -> Self { Self { regs: HashMap::new(), mem: HashMap::new(), obj_fields: HashMap::new() } }
pub fn new() -> Self {
Self {
regs: HashMap::new(),
mem: HashMap::new(),
obj_fields: HashMap::new(),
}
}
/// Execute module entry (main) and return boxed result
pub fn execute_module(&mut self, module: &MirModule) -> Result<Box<dyn NyashBox>, VMError> {
let func = module.functions.get("main").ok_or_else(|| VMError::InvalidInstruction("missing main".into()))?;
let func = module
.functions
.get("main")
.ok_or_else(|| VMError::InvalidInstruction("missing main".into()))?;
let ret = self.execute_function(func)?;
Ok(ret.to_nyash_box())
}
@ -36,7 +47,10 @@ impl MirInterpreter {
let mut cur = func.entry_block;
let mut last_pred: Option<BasicBlockId> = None;
loop {
let block = func.blocks.get(&cur).ok_or_else(|| VMError::InvalidBasicBlock(format!("bb {:?} not found", cur)))?;
let block = func
.blocks
.get(&cur)
.ok_or_else(|| VMError::InvalidBasicBlock(format!("bb {:?} not found", cur)))?;
// Resolve incoming phi nodes using predecessor
for inst in &block.instructions {
if let MirInstruction::Phi { dst, inputs } = inst {
@ -67,17 +81,34 @@ impl MirInterpreter {
};
self.regs.insert(*dst, v);
}
MirInstruction::NewBox { dst, box_type, args } => {
MirInstruction::NewBox {
dst,
box_type,
args,
} => {
// Build arg boxes
let mut a: Vec<Box<dyn crate::box_trait::NyashBox>> = Vec::new();
for vid in args { a.push(self.reg_load(*vid)?.to_nyash_box()); }
for vid in args {
a.push(self.reg_load(*vid)?.to_nyash_box());
}
// Use unified global registry (plugins already initialized by runner)
let reg = crate::runtime::unified_registry::get_global_unified_registry();
let created = reg.lock().unwrap().create_box(box_type, &a)
.map_err(|e| VMError::InvalidInstruction(format!("NewBox {} failed: {}", box_type, e)))?;
let created =
reg.lock().unwrap().create_box(box_type, &a).map_err(|e| {
VMError::InvalidInstruction(format!(
"NewBox {} failed: {}",
box_type, e
))
})?;
self.regs.insert(*dst, VMValue::from_nyash_box(created));
}
MirInstruction::PluginInvoke { dst, box_val, method, args, .. } => {
MirInstruction::PluginInvoke {
dst,
box_val,
method,
args,
..
} => {
// Resolve receiver
let recv = self.reg_load(*box_val)?;
let recv_box: Box<dyn crate::box_trait::NyashBox> = match recv.clone() {
@ -85,40 +116,101 @@ impl MirInterpreter {
other => other.to_nyash_box(),
};
// If PluginBoxV2 → invoke via unified plugin host
if let Some(p) = recv_box.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>() {
let host = crate::runtime::plugin_loader_unified::get_global_plugin_host();
if let Some(p) = recv_box
.as_any()
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>(
) {
let host =
crate::runtime::plugin_loader_unified::get_global_plugin_host();
let host = host.read().unwrap();
let mut argv: Vec<Box<dyn crate::box_trait::NyashBox>> = Vec::new();
for a in args { argv.push(self.reg_load(*a)?.to_nyash_box()); }
match host.invoke_instance_method(&p.box_type, method, p.inner.instance_id, &argv) {
for a in args {
argv.push(self.reg_load(*a)?.to_nyash_box());
}
match host.invoke_instance_method(
&p.box_type,
method,
p.inner.instance_id,
&argv,
) {
Ok(Some(ret)) => {
if let Some(d) = dst { self.regs.insert(*d, VMValue::from_nyash_box(ret)); }
if let Some(d) = dst {
self.regs.insert(*d, VMValue::from_nyash_box(ret));
}
}
Ok(None) => {
if let Some(d) = dst {
self.regs.insert(*d, VMValue::Void);
}
}
Err(e) => {
return Err(VMError::InvalidInstruction(format!(
"PluginInvoke {}.{} failed: {:?}",
p.box_type, method, e
)));
}
Ok(None) => { if let Some(d) = dst { self.regs.insert(*d, VMValue::Void); } }
Err(e) => { return Err(VMError::InvalidInstruction(format!("PluginInvoke {}.{} failed: {:?}", p.box_type, method, e))); }
}
} else {
// Minimal fallback: toString
if method == "toString" {
if let Some(d) = dst { self.regs.insert(*d, VMValue::String(recv_box.to_string_box().value)); }
if let Some(d) = dst {
self.regs.insert(
*d,
VMValue::String(recv_box.to_string_box().value),
);
}
} else {
return Err(VMError::InvalidInstruction(format!("PluginInvoke unsupported on {} for method {}", recv_box.type_name(), method)));
return Err(VMError::InvalidInstruction(format!(
"PluginInvoke unsupported on {} for method {}",
recv_box.type_name(),
method
)));
}
}
}
MirInstruction::BoxCall { dst, box_val, method, args, .. } => {
MirInstruction::BoxCall {
dst,
box_val,
method,
args,
..
} => {
// Support getField/setField for normalized RefGet/RefSet
if method == "getField" {
if args.len() != 1 { return Err(VMError::InvalidInstruction("getField expects 1 arg".into())); }
let fname = match self.reg_load(args[0].clone())? { VMValue::String(s) => s, v => v.to_string() };
let v = self.obj_fields.get(box_val).and_then(|m| m.get(&fname)).cloned().unwrap_or(VMValue::Void);
if let Some(d) = dst { self.regs.insert(*d, v); }
if args.len() != 1 {
return Err(VMError::InvalidInstruction(
"getField expects 1 arg".into(),
));
}
let fname = match self.reg_load(args[0].clone())? {
VMValue::String(s) => s,
v => v.to_string(),
};
let v = self
.obj_fields
.get(box_val)
.and_then(|m| m.get(&fname))
.cloned()
.unwrap_or(VMValue::Void);
if let Some(d) = dst {
self.regs.insert(*d, v);
}
continue;
} else if method == "setField" {
if args.len() != 2 { return Err(VMError::InvalidInstruction("setField expects 2 args".into())); }
let fname = match self.reg_load(args[0].clone())? { VMValue::String(s) => s, v => v.to_string() };
if args.len() != 2 {
return Err(VMError::InvalidInstruction(
"setField expects 2 args".into(),
));
}
let fname = match self.reg_load(args[0].clone())? {
VMValue::String(s) => s,
v => v.to_string(),
};
let valv = self.reg_load(args[1].clone())?;
self.obj_fields.entry(*box_val).or_default().insert(fname, valv);
self.obj_fields
.entry(*box_val)
.or_default()
.insert(fname, valv);
continue;
}
// Fallback: treat like PluginInvoke for plugin-backed boxes
@ -127,7 +219,10 @@ impl MirInterpreter {
VMValue::BoxRef(b) => b.share_box(),
other => other.to_nyash_box(),
};
if let Some(p) = recv_box.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>() {
if let Some(p) = recv_box
.as_any()
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>(
) {
// Special-case: ConsoleBox.readLine → stdin fallback if not provided by plugin
if p.box_type == "ConsoleBox" && method == "readLine" {
use std::io::{self, Read};
@ -136,64 +231,145 @@ impl MirInterpreter {
// Read a single line (blocking)
let mut buf = [0u8; 1];
while let Ok(n) = stdin.read(&mut buf) {
if n == 0 { break; }
if n == 0 {
break;
}
let ch = buf[0] as char;
if ch == '\n' { break; }
if ch == '\n' {
break;
}
s.push(ch);
if s.len() > 1_000_000 { break; }
if s.len() > 1_000_000 {
break;
}
}
if let Some(d) = dst {
self.regs.insert(*d, VMValue::String(s));
}
if let Some(d) = dst { self.regs.insert(*d, VMValue::String(s)); }
continue;
}
let host = crate::runtime::plugin_loader_unified::get_global_plugin_host();
let host =
crate::runtime::plugin_loader_unified::get_global_plugin_host();
let host = host.read().unwrap();
let mut argv: Vec<Box<dyn crate::box_trait::NyashBox>> = Vec::new();
for a in args { argv.push(self.reg_load(*a)?.to_nyash_box()); }
match host.invoke_instance_method(&p.box_type, method, p.inner.instance_id, &argv) {
Ok(Some(ret)) => { if let Some(d) = dst { self.regs.insert(*d, VMValue::from_nyash_box(ret)); } }
Ok(None) => { if let Some(d) = dst { self.regs.insert(*d, VMValue::Void); } }
Err(e) => { return Err(VMError::InvalidInstruction(format!("BoxCall {}.{} failed: {:?}", p.box_type, method, e))); }
for a in args {
argv.push(self.reg_load(*a)?.to_nyash_box());
}
match host.invoke_instance_method(
&p.box_type,
method,
p.inner.instance_id,
&argv,
) {
Ok(Some(ret)) => {
if let Some(d) = dst {
self.regs.insert(*d, VMValue::from_nyash_box(ret));
}
}
Ok(None) => {
if let Some(d) = dst {
self.regs.insert(*d, VMValue::Void);
}
}
Err(e) => {
return Err(VMError::InvalidInstruction(format!(
"BoxCall {}.{} failed: {:?}",
p.box_type, method, e
)));
}
}
} else {
return Err(VMError::InvalidInstruction(format!("BoxCall unsupported on {}.{}", recv_box.type_name(), method)));
return Err(VMError::InvalidInstruction(format!(
"BoxCall unsupported on {}.{}",
recv_box.type_name(),
method
)));
}
}
MirInstruction::ExternCall { dst, iface_name, method_name, args, .. } => {
MirInstruction::ExternCall {
dst,
iface_name,
method_name,
args,
..
} => {
// Minimal env.console.log bridge
if iface_name == "env.console" && method_name == "log" {
if let Some(a0) = args.get(0) {
let v = self.reg_load(*a0)?;
println!("{}", v.to_string());
}
if let Some(d) = dst { self.regs.insert(*d, VMValue::Void); }
if let Some(d) = dst {
self.regs.insert(*d, VMValue::Void);
}
} else {
return Err(VMError::InvalidInstruction(format!("ExternCall {}.{} not supported", iface_name, method_name)));
return Err(VMError::InvalidInstruction(format!(
"ExternCall {}.{} not supported",
iface_name, method_name
)));
}
}
MirInstruction::RefSet { reference, field, value } => {
MirInstruction::RefSet {
reference,
field,
value,
} => {
let v = self.reg_load(*value)?;
self.obj_fields.entry(*reference).or_default().insert(field.clone(), v);
self.obj_fields
.entry(*reference)
.or_default()
.insert(field.clone(), v);
}
MirInstruction::RefGet { dst, reference, field } => {
let v = self.obj_fields.get(reference).and_then(|m| m.get(field)).cloned().unwrap_or(VMValue::Void);
MirInstruction::RefGet {
dst,
reference,
field,
} => {
let v = self
.obj_fields
.get(reference)
.and_then(|m| m.get(field))
.cloned()
.unwrap_or(VMValue::Void);
self.regs.insert(*dst, v);
}
MirInstruction::BinOp { dst, op, lhs, rhs } => {
let a = self.reg_load(*lhs)?; let b = self.reg_load(*rhs)?;
let a = self.reg_load(*lhs)?;
let b = self.reg_load(*rhs)?;
let v = self.eval_binop(*op, a, b)?;
self.regs.insert(*dst, v);
}
MirInstruction::UnaryOp { dst, op, operand } => {
let x = self.reg_load(*operand)?;
let v = match op {
crate::mir::UnaryOp::Neg => match x { VMValue::Integer(i) => VMValue::Integer(-i), VMValue::Float(f)=>VMValue::Float(-f), _=> return Err(VMError::TypeError(format!("neg expects number, got {:?}", x))) },
crate::mir::UnaryOp::Not => VMValue::Bool(!to_bool_vm(&x).map_err(|e| VMError::TypeError(e))?),
crate::mir::UnaryOp::BitNot => match x { VMValue::Integer(i) => VMValue::Integer(!i), _=> return Err(VMError::TypeError(format!("bitnot expects integer, got {:?}", x))) },
crate::mir::UnaryOp::Neg => match x {
VMValue::Integer(i) => VMValue::Integer(-i),
VMValue::Float(f) => VMValue::Float(-f),
_ => {
return Err(VMError::TypeError(format!(
"neg expects number, got {:?}",
x
)))
}
},
crate::mir::UnaryOp::Not => {
VMValue::Bool(!to_bool_vm(&x).map_err(|e| VMError::TypeError(e))?)
}
crate::mir::UnaryOp::BitNot => match x {
VMValue::Integer(i) => VMValue::Integer(!i),
_ => {
return Err(VMError::TypeError(format!(
"bitnot expects integer, got {:?}",
x
)))
}
},
};
self.regs.insert(*dst, v);
}
MirInstruction::Compare { dst, op, lhs, rhs } => {
let a = self.reg_load(*lhs)?; let b = self.reg_load(*rhs)?;
let a = self.reg_load(*lhs)?;
let b = self.reg_load(*rhs)?;
let v = self.eval_cmp(*op, a, b)?;
self.regs.insert(*dst, VMValue::Bool(v));
}
@ -206,7 +382,8 @@ impl MirInterpreter {
self.mem.insert(*ptr, v);
}
MirInstruction::Copy { dst, src } => {
let v = self.reg_load(*src)?; self.regs.insert(*dst, v);
let v = self.reg_load(*src)?;
self.regs.insert(*dst, v);
}
MirInstruction::Debug { value, message } => {
let v = self.reg_load(*value)?;
@ -215,38 +392,75 @@ impl MirInterpreter {
}
}
MirInstruction::Print { value, .. } => {
let v = self.reg_load(*value)?; println!("{}", v.to_string());
let v = self.reg_load(*value)?;
println!("{}", v.to_string());
}
// No-ops in the interpreter for now
MirInstruction::BarrierRead { .. } | MirInstruction::BarrierWrite { .. } | MirInstruction::Barrier { .. } | MirInstruction::Safepoint | MirInstruction::Nop => {}
MirInstruction::BarrierRead { .. }
| MirInstruction::BarrierWrite { .. }
| MirInstruction::Barrier { .. }
| MirInstruction::Safepoint
| MirInstruction::Nop => {}
// Unimplemented but recognized — return clear error for visibility
other => return Err(VMError::InvalidInstruction(format!("MIR interp: unimplemented instruction: {:?}", other))),
other => {
return Err(VMError::InvalidInstruction(format!(
"MIR interp: unimplemented instruction: {:?}",
other
)))
}
}
}
// Handle terminator
match &block.terminator {
Some(MirInstruction::Return { value }) => {
if let Some(v) = value { return self.reg_load(*v); } else { return Ok(VMValue::Void); }
if let Some(v) = value {
return self.reg_load(*v);
} else {
return Ok(VMValue::Void);
}
}
Some(MirInstruction::Jump { target }) => {
last_pred = Some(block.id); cur = *target; continue;
last_pred = Some(block.id);
cur = *target;
continue;
}
Some(MirInstruction::Branch { condition, then_bb, else_bb }) => {
let c = self.reg_load(*condition)?; let t = to_bool_vm(&c).map_err(|e| VMError::TypeError(e))?;
last_pred = Some(block.id); cur = if t { *then_bb } else { *else_bb }; continue;
Some(MirInstruction::Branch {
condition,
then_bb,
else_bb,
}) => {
let c = self.reg_load(*condition)?;
let t = to_bool_vm(&c).map_err(|e| VMError::TypeError(e))?;
last_pred = Some(block.id);
cur = if t { *then_bb } else { *else_bb };
continue;
}
None => {
return Err(VMError::InvalidBasicBlock(format!(
"unterminated block {:?}",
block.id
)))
}
Some(other) => {
return Err(VMError::InvalidInstruction(format!(
"invalid terminator in MIR interp: {:?}",
other
)))
}
None => return Err(VMError::InvalidBasicBlock(format!("unterminated block {:?}", block.id))),
Some(other) => return Err(VMError::InvalidInstruction(format!("invalid terminator in MIR interp: {:?}", other))),
}
}
}
fn reg_load(&self, id: ValueId) -> Result<VMValue, VMError> {
self.regs.get(&id).cloned().ok_or_else(|| VMError::InvalidValue(format!("use of undefined value {:?}", id)))
self.regs
.get(&id)
.cloned()
.ok_or_else(|| VMError::InvalidValue(format!("use of undefined value {:?}", id)))
}
fn eval_binop(&self, op: BinaryOp, a: VMValue, b: VMValue) -> Result<VMValue, VMError> {
use BinaryOp::*; use VMValue::*;
use BinaryOp::*;
use VMValue::*;
Ok(match (op, a, b) {
(Add, Integer(x), Integer(y)) => Integer(x + y),
// String concat: ifいずれかがStringなら文字列連結
@ -277,12 +491,18 @@ impl MirInterpreter {
(Shl, Integer(x), Integer(y)) => Integer(x.wrapping_shl(y as u32)),
(Shr, Integer(x), Integer(y)) => Integer(x.wrapping_shr(y as u32)),
// Fallbacks not yet supported
(opk, va, vb) => return Err(VMError::TypeError(format!("unsupported binop {:?} on {:?} and {:?}", opk, va, vb))),
(opk, va, vb) => {
return Err(VMError::TypeError(format!(
"unsupported binop {:?} on {:?} and {:?}",
opk, va, vb
)))
}
})
}
fn eval_cmp(&self, op: CompareOp, a: VMValue, b: VMValue) -> Result<bool, VMError> {
use CompareOp::*; use VMValue::*;
use CompareOp::*;
use VMValue::*;
Ok(match (op, &a, &b) {
(Eq, _, _) => eq_vm(&a, &b),
(Ne, _, _) => !eq_vm(&a, &b),
@ -294,7 +514,12 @@ impl MirInterpreter {
(Le, Float(x), Float(y)) => x <= y,
(Gt, Float(x), Float(y)) => x > y,
(Ge, Float(x), Float(y)) => x >= y,
(opk, va, vb) => return Err(VMError::TypeError(format!("unsupported compare {:?} on {:?} and {:?}", opk, va, vb))),
(opk, va, vb) => {
return Err(VMError::TypeError(format!(
"unsupported compare {:?} on {:?} and {:?}",
opk, va, vb
)))
}
})
}
}

View File

@ -3,49 +3,54 @@
*/
pub mod vm;
pub mod vm_phi;
pub mod vm_instructions;
pub mod vm_values;
pub mod vm_types;
pub mod vm_boxcall;
pub mod vm_instructions;
pub mod vm_phi;
pub mod vm_stats;
pub mod vm_types;
pub mod vm_values;
// Phase 9.78h: VM split scaffolding (control_flow/dispatch/frame)
pub mod abi_util; // Shared ABI/utility helpers
pub mod control_flow;
pub mod dispatch;
pub mod frame;
pub mod gc_helpers;
pub mod mir_interpreter;
pub mod vm_control_flow;
mod vm_gc; // A3: GC roots & diagnostics extracted
mod vm_exec; // A3: execution loop extracted
mod vm_state; // A3: state & basic helpers extracted
mod vm_gc; // A3: GC roots & diagnostics extracted
mod vm_methods; // A3-S1: method dispatch wrappers extracted
pub mod abi_util; // Shared ABI/utility helpers
pub mod mir_interpreter; // Lightweight MIR interpreter
mod vm_state; // A3: state & basic helpers extracted // Lightweight MIR interpreter
#[cfg(feature = "wasm-backend")]
pub mod wasm;
#[cfg(feature = "wasm-backend")]
pub mod aot;
#[cfg(feature = "wasm-backend")]
pub mod wasm;
#[cfg(feature = "wasm-backend")]
pub mod wasm_v2;
#[cfg(feature = "llvm-inkwell-legacy")]
pub mod llvm_legacy;
// Back-compat shim so existing paths crate::backend::llvm::* keep working
#[cfg(feature = "llvm-inkwell-legacy")]
pub mod llvm;
#[cfg(feature = "cranelift-jit")]
pub mod cranelift;
#[cfg(feature = "llvm-inkwell-legacy")]
pub mod llvm;
pub use vm::{VM, VMError, VMValue};
pub use mir_interpreter::MirInterpreter;
pub use vm::{VMError, VMValue, VM};
#[cfg(feature = "wasm-backend")]
pub use aot::{AotBackend, AotConfig, AotError, AotStats};
#[cfg(feature = "wasm-backend")]
pub use wasm::{WasmBackend, WasmError};
#[cfg(feature = "wasm-backend")]
pub use aot::{AotBackend, AotError, AotConfig, AotStats};
#[cfg(feature = "llvm-inkwell-legacy")]
pub use llvm_legacy::{compile_and_execute as llvm_compile_and_execute, compile_to_object as llvm_compile_to_object};
#[cfg(feature = "cranelift-jit")]
pub use cranelift::{compile_and_execute as cranelift_compile_and_execute, compile_to_object as cranelift_compile_to_object};
pub use cranelift::{
compile_and_execute as cranelift_compile_and_execute,
compile_to_object as cranelift_compile_to_object,
};
#[cfg(feature = "llvm-inkwell-legacy")]
pub use llvm_legacy::{
compile_and_execute as llvm_compile_and_execute, compile_to_object as llvm_compile_to_object,
};

File diff suppressed because it is too large Load Diff

View File

@ -7,60 +7,109 @@
* Typical Callers: vm_instructions::execute_boxcall / VM::call_unified_method
*/
use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox, VoidBox};
use super::vm::{VM, VMError, VMValue};
use super::vm::{VMError, VMValue, VM};
use crate::box_trait::{BoolBox, IntegerBox, NyashBox, StringBox, VoidBox};
impl VM {
/// Call a method on a Box - simplified version of interpreter method dispatch
pub(super) fn call_box_method_impl(&self, box_value: Box<dyn NyashBox>, method: &str, _args: Vec<Box<dyn NyashBox>>) -> Result<Box<dyn NyashBox>, VMError> {
pub(super) fn call_box_method_impl(
&self,
box_value: Box<dyn NyashBox>,
method: &str,
_args: Vec<Box<dyn NyashBox>>,
) -> Result<Box<dyn NyashBox>, VMError> {
// PluginBoxV2: delegate to unified plugin host (BID-FFI v1)
if let Some(pbox) = box_value.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>() {
if let Some(pbox) = box_value
.as_any()
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
{
if std::env::var("NYASH_DEBUG_PLUGIN").ok().as_deref() == Some("1") {
eprintln!("[VM][BoxCall→PluginInvoke] {}.{} inst_id={}", pbox.box_type, method, pbox.inner.instance_id);
eprintln!(
"[VM][BoxCall→PluginInvoke] {}.{} inst_id={}",
pbox.box_type, method, pbox.inner.instance_id
);
}
let host = crate::runtime::get_global_plugin_host();
let host = host.read().unwrap();
match host.invoke_instance_method(&pbox.box_type, method, pbox.inner.instance_id, &_args) {
Ok(Some(val)) => { return Ok(val); }
Ok(None) => { return Ok(Box::new(crate::box_trait::VoidBox::new())); }
match host.invoke_instance_method(
&pbox.box_type,
method,
pbox.inner.instance_id,
&_args,
) {
Ok(Some(val)) => {
return Ok(val);
}
Ok(None) => {
return Ok(Box::new(crate::box_trait::VoidBox::new()));
}
Err(e) => {
return Err(VMError::InvalidInstruction(format!("PluginInvoke failed via BoxCall: {}.{} ({})", pbox.box_type, method, e.message())));
return Err(VMError::InvalidInstruction(format!(
"PluginInvoke failed via BoxCall: {}.{} ({})",
pbox.box_type,
method,
e.message()
)));
}
}
}
// MathBox methods (minimal set used in 10.9)
if let Some(math) = box_value.as_any().downcast_ref::<crate::boxes::math_box::MathBox>() {
if let Some(math) = box_value
.as_any()
.downcast_ref::<crate::boxes::math_box::MathBox>()
{
match method {
"min" => {
if _args.len() >= 2 { return Ok(math.min(_args[0].clone_or_share(), _args[1].clone_or_share())); }
if _args.len() >= 2 {
return Ok(math.min(_args[0].clone_or_share(), _args[1].clone_or_share()));
}
return Ok(Box::new(StringBox::new("Error: min(a, b) requires 2 args")));
}
"max" => {
if _args.len() >= 2 { return Ok(math.max(_args[0].clone_or_share(), _args[1].clone_or_share())); }
if _args.len() >= 2 {
return Ok(math.max(_args[0].clone_or_share(), _args[1].clone_or_share()));
}
return Ok(Box::new(StringBox::new("Error: max(a, b) requires 2 args")));
}
"abs" => {
if let Some(v) = _args.get(0) { return Ok(math.abs(v.clone_or_share())); }
if let Some(v) = _args.get(0) {
return Ok(math.abs(v.clone_or_share()));
}
return Ok(Box::new(StringBox::new("Error: abs(x) requires 1 arg")));
}
"sin" => {
if let Some(v) = _args.get(0) { return Ok(math.sin(v.clone_or_share())); }
if let Some(v) = _args.get(0) {
return Ok(math.sin(v.clone_or_share()));
}
return Ok(Box::new(StringBox::new("Error: sin(x) requires 1 arg")));
}
"cos" => {
if let Some(v) = _args.get(0) { return Ok(math.cos(v.clone_or_share())); }
if let Some(v) = _args.get(0) {
return Ok(math.cos(v.clone_or_share()));
}
return Ok(Box::new(StringBox::new("Error: cos(x) requires 1 arg")));
}
_ => { return Ok(Box::new(VoidBox::new())); }
_ => {
return Ok(Box::new(VoidBox::new()));
}
}
}
// ResultBox (NyashResultBox - new)
if let Some(result_box) = box_value.as_any().downcast_ref::<crate::boxes::result::NyashResultBox>() {
if let Some(result_box) = box_value
.as_any()
.downcast_ref::<crate::boxes::result::NyashResultBox>()
{
match method {
"is_ok" | "isOk" => { return Ok(result_box.is_ok()); }
"get_value" | "getValue" => { return Ok(result_box.get_value()); }
"get_error" | "getError" => { return Ok(result_box.get_error()); }
"is_ok" | "isOk" => {
return Ok(result_box.is_ok());
}
"get_value" | "getValue" => {
return Ok(result_box.get_value());
}
"get_error" | "getError" => {
return Ok(result_box.get_error());
}
_ => return Ok(Box::new(VoidBox::new())),
}
}
@ -73,10 +122,17 @@ impl VM {
}
// JitConfigBox methods
if let Some(jcb) = box_value.as_any().downcast_ref::<crate::boxes::jit_config_box::JitConfigBox>() {
if let Some(jcb) = box_value
.as_any()
.downcast_ref::<crate::boxes::jit_config_box::JitConfigBox>()
{
match method {
"get" => {
if let Some(k) = _args.get(0) { return Ok(jcb.get_flag(&k.to_string_box().value).unwrap_or_else(|e| Box::new(StringBox::new(e.to_string())))); }
if let Some(k) = _args.get(0) {
return Ok(jcb
.get_flag(&k.to_string_box().value)
.unwrap_or_else(|e| Box::new(StringBox::new(e.to_string()))));
}
return Ok(Box::new(StringBox::new("get(name) requires 1 arg")));
}
"set" => {
@ -84,42 +140,73 @@ impl VM {
let k = _args[0].to_string_box().value;
let v = _args[1].to_string_box().value;
let on = matches!(v.as_str(), "1" | "true" | "True" | "on" | "ON");
return Ok(jcb.set_flag(&k, on).unwrap_or_else(|e| Box::new(StringBox::new(e.to_string()))));
return Ok(jcb
.set_flag(&k, on)
.unwrap_or_else(|e| Box::new(StringBox::new(e.to_string()))));
}
return Ok(Box::new(StringBox::new("set(name, bool) requires 2 args")));
}
"getThreshold" => { return Ok(jcb.get_threshold()); }
"getThreshold" => {
return Ok(jcb.get_threshold());
}
"setThreshold" => {
if let Some(v) = _args.get(0) {
if let Some(v) = _args.get(0) {
let iv = v.to_string_box().value.parse::<i64>().unwrap_or(0);
return Ok(jcb.set_threshold(iv).unwrap_or_else(|e| Box::new(StringBox::new(e.to_string()))));
return Ok(jcb
.set_threshold(iv)
.unwrap_or_else(|e| Box::new(StringBox::new(e.to_string()))));
}
return Ok(Box::new(StringBox::new("setThreshold(n) requires 1 arg")));
}
"apply" => { return Ok(jcb.apply()); }
"toJson" => { return Ok(jcb.to_json()); }
"apply" => {
return Ok(jcb.apply());
}
"toJson" => {
return Ok(jcb.to_json());
}
"fromJson" => {
if let Some(s) = _args.get(0) { return Ok(jcb.from_json(&s.to_string_box().value).unwrap_or_else(|e| Box::new(StringBox::new(e.to_string())))); }
if let Some(s) = _args.get(0) {
return Ok(jcb
.from_json(&s.to_string_box().value)
.unwrap_or_else(|e| Box::new(StringBox::new(e.to_string()))));
}
return Ok(Box::new(StringBox::new("fromJson(json) requires 1 arg")));
}
"enable" => {
if let Some(k) = _args.get(0) { return Ok(jcb.set_flag(&k.to_string_box().value, true).unwrap_or_else(|e| Box::new(StringBox::new(e.to_string())))); }
if let Some(k) = _args.get(0) {
return Ok(jcb
.set_flag(&k.to_string_box().value, true)
.unwrap_or_else(|e| Box::new(StringBox::new(e.to_string()))));
}
return Ok(Box::new(StringBox::new("enable(name) requires 1 arg")));
}
"disable" => {
if let Some(k) = _args.get(0) { return Ok(jcb.set_flag(&k.to_string_box().value, false).unwrap_or_else(|e| Box::new(StringBox::new(e.to_string())))); }
if let Some(k) = _args.get(0) {
return Ok(jcb
.set_flag(&k.to_string_box().value, false)
.unwrap_or_else(|e| Box::new(StringBox::new(e.to_string()))));
}
return Ok(Box::new(StringBox::new("disable(name) requires 1 arg")));
}
"summary" => { return Ok(jcb.summary()); }
_ => { return Ok(Box::new(VoidBox::new())); }
"summary" => {
return Ok(jcb.summary());
}
_ => {
return Ok(Box::new(VoidBox::new()));
}
}
}
// JitPolicyBox methods
if let Some(jpb) = box_value.as_any().downcast_ref::<crate::boxes::jit_policy_box::JitPolicyBox>() {
if let Some(jpb) = box_value
.as_any()
.downcast_ref::<crate::boxes::jit_policy_box::JitPolicyBox>()
{
match method {
"get" => {
if let Some(k) = _args.get(0) { return Ok(jpb.get_flag(&k.to_string_box().value)); }
if let Some(k) = _args.get(0) {
return Ok(jpb.get_flag(&k.to_string_box().value));
}
return Ok(Box::new(StringBox::new("get(name) requires 1 arg")));
}
"set" => {
@ -132,37 +219,61 @@ impl VM {
return Ok(Box::new(StringBox::new("set(name, bool) requires 2 args")));
}
"setWhitelistCsv" | "set_whitelist_csv" => {
if let Some(s) = _args.get(0) { return Ok(jpb.set_whitelist_csv(&s.to_string_box().value)); }
return Ok(Box::new(StringBox::new("setWhitelistCsv(csv) requires 1 arg")));
if let Some(s) = _args.get(0) {
return Ok(jpb.set_whitelist_csv(&s.to_string_box().value));
}
return Ok(Box::new(StringBox::new(
"setWhitelistCsv(csv) requires 1 arg",
)));
}
"addWhitelist" | "add_whitelist" => {
if let Some(s) = _args.get(0) { return Ok(jpb.add_whitelist(&s.to_string_box().value)); }
return Ok(Box::new(StringBox::new("addWhitelist(name) requires 1 arg")));
if let Some(s) = _args.get(0) {
return Ok(jpb.add_whitelist(&s.to_string_box().value));
}
return Ok(Box::new(StringBox::new(
"addWhitelist(name) requires 1 arg",
)));
}
"clearWhitelist" | "clear_whitelist" => {
return Ok(jpb.clear_whitelist());
}
"clearWhitelist" | "clear_whitelist" => { return Ok(jpb.clear_whitelist()); }
"enablePreset" | "enable_preset" => {
if let Some(s) = _args.get(0) { return Ok(jpb.enable_preset(&s.to_string_box().value)); }
return Ok(Box::new(StringBox::new("enablePreset(name) requires 1 arg")));
if let Some(s) = _args.get(0) {
return Ok(jpb.enable_preset(&s.to_string_box().value));
}
return Ok(Box::new(StringBox::new(
"enablePreset(name) requires 1 arg",
)));
}
_ => {
return Ok(Box::new(VoidBox::new()));
}
_ => { return Ok(Box::new(VoidBox::new())); }
}
}
// JitStatsBox methods
if let Some(jsb) = box_value.as_any().downcast_ref::<crate::boxes::jit_stats_box::JitStatsBox>() {
if let Some(jsb) = box_value
.as_any()
.downcast_ref::<crate::boxes::jit_stats_box::JitStatsBox>()
{
match method {
"toJson" => { return Ok(jsb.to_json()); }
"toJson" => {
return Ok(jsb.to_json());
}
"top5" => {
if let Some(jm) = &self.jit_manager {
let v = jm.top_hits(5);
let arr: Vec<serde_json::Value> = v.into_iter().map(|(name, hits, compiled, handle)| {
serde_json::json!({
"name": name,
"hits": hits,
"compiled": compiled,
"handle": handle
let arr: Vec<serde_json::Value> = v
.into_iter()
.map(|(name, hits, compiled, handle)| {
serde_json::json!({
"name": name,
"hits": hits,
"compiled": compiled,
"handle": handle
})
})
}).collect();
.collect();
let s = serde_json::to_string(&arr).unwrap_or_else(|_| "[]".to_string());
return Ok(Box::new(StringBox::new(s)));
}
@ -171,7 +282,11 @@ impl VM {
"summary" => {
let cfg = crate::jit::config::current();
let caps = crate::jit::config::probe_capabilities();
let abi_mode = if cfg.native_bool_abi && caps.supports_b1_sig { "b1_bool" } else { "i64_bool" };
let abi_mode = if cfg.native_bool_abi && caps.supports_b1_sig {
"b1_bool"
} else {
"i64_bool"
};
let b1_norm = crate::jit::rt::b1_norm_get();
let ret_b1 = crate::jit::rt::ret_bool_hint_get();
let mut payload = serde_json::json!({
@ -192,41 +307,66 @@ impl VM {
let per_arr: Vec<serde_json::Value> = perf.into_iter().map(|(name, phi_t, phi_b1, rb, hits, compiled, handle)| serde_json::json!({
"name": name, "phi_total": phi_t, "phi_b1": phi_b1, "ret_bool_hint": rb, "hits": hits, "compiled": compiled, "handle": handle
})).collect();
if let Some(obj) = payload.as_object_mut() { obj.insert("top5".to_string(), serde_json::Value::Array(top5)); obj.insert("perFunction".to_string(), serde_json::Value::Array(per_arr)); }
if let Some(obj) = payload.as_object_mut() {
obj.insert("top5".to_string(), serde_json::Value::Array(top5));
obj.insert(
"perFunction".to_string(),
serde_json::Value::Array(per_arr),
);
}
}
let s = serde_json::to_string_pretty(&payload).unwrap_or_else(|_| "{}".to_string());
let s =
serde_json::to_string_pretty(&payload).unwrap_or_else(|_| "{}".to_string());
return Ok(Box::new(StringBox::new(s)));
}
"perFunction" | "per_function" => {
if let Some(jm) = &self.jit_manager {
let v = jm.per_function_stats();
let arr: Vec<serde_json::Value> = v.into_iter().map(|(name, phi_t, phi_b1, rb, hits, compiled, handle)| serde_json::json!({
"name": name,
"phi_total": phi_t,
"phi_b1": phi_b1,
"ret_bool_hint": rb,
"hits": hits,
"compiled": compiled,
"handle": handle,
})).collect();
let s = serde_json::to_string_pretty(&arr).unwrap_or_else(|_| "[]".to_string());
let arr: Vec<serde_json::Value> = v
.into_iter()
.map(|(name, phi_t, phi_b1, rb, hits, compiled, handle)| {
serde_json::json!({
"name": name,
"phi_total": phi_t,
"phi_b1": phi_b1,
"ret_bool_hint": rb,
"hits": hits,
"compiled": compiled,
"handle": handle,
})
})
.collect();
let s =
serde_json::to_string_pretty(&arr).unwrap_or_else(|_| "[]".to_string());
return Ok(Box::new(StringBox::new(s)));
}
return Ok(Box::new(StringBox::new("[]")));
}
_ => { return Ok(Box::new(VoidBox::new())); }
_ => {
return Ok(Box::new(VoidBox::new()));
}
}
}
// StringBox methods
if let Some(string_box) = box_value.as_any().downcast_ref::<StringBox>() {
match method {
"length" | "len" => { return Ok(Box::new(IntegerBox::new(string_box.value.len() as i64))); }
"toString" => { return Ok(Box::new(StringBox::new(string_box.value.clone()))); }
"length" | "len" => {
return Ok(Box::new(IntegerBox::new(string_box.value.len() as i64)));
}
"toString" => {
return Ok(Box::new(StringBox::new(string_box.value.clone())));
}
"substring" => {
if _args.len() >= 2 {
let s = match _args[0].to_string_box().value.parse::<i64>() { Ok(v) => v.max(0) as usize, Err(_) => 0 };
let e = match _args[1].to_string_box().value.parse::<i64>() { Ok(v) => v.max(0) as usize, Err(_) => string_box.value.chars().count() };
let s = match _args[0].to_string_box().value.parse::<i64>() {
Ok(v) => v.max(0) as usize,
Err(_) => 0,
};
let e = match _args[1].to_string_box().value.parse::<i64>() {
Ok(v) => v.max(0) as usize,
Err(_) => string_box.value.chars().count(),
};
return Ok(string_box.substring(s, e));
}
return Ok(Box::new(VoidBox::new()));
@ -243,115 +383,345 @@ impl VM {
}
// ArrayBox methods (minimal set)
if let Some(array_box) = box_value.as_any().downcast_ref::<crate::boxes::array::ArrayBox>() {
if let Some(array_box) = box_value
.as_any()
.downcast_ref::<crate::boxes::array::ArrayBox>()
{
match method {
"push" => { if let Some(v) = _args.get(0) { return Ok(array_box.push(v.clone_or_share())); } return Ok(Box::new(StringBox::new("Error: push(value) requires 1 arg"))); }
"pop" => { return Ok(array_box.pop()); },
"length" | "len" => { return Ok(array_box.length()); },
"get" => { if let Some(i) = _args.get(0) { return Ok(array_box.get(i.clone_or_share())); } return Ok(Box::new(StringBox::new("Error: get(index) requires 1 arg"))); }
"set" => { if _args.len() >= 2 { return Ok(array_box.set(_args[0].clone_or_share(), _args[1].clone_or_share())); } return Ok(Box::new(StringBox::new("Error: set(index, value) requires 2 args"))); }
"remove" => { if let Some(i) = _args.get(0) { return Ok(array_box.remove(i.clone_or_share())); } return Ok(Box::new(StringBox::new("Error: remove(index) requires 1 arg"))); }
"contains" => { if let Some(v) = _args.get(0) { return Ok(array_box.contains(v.clone_or_share())); } return Ok(Box::new(StringBox::new("Error: contains(value) requires 1 arg"))); }
"indexOf" => { if let Some(v) = _args.get(0) { return Ok(array_box.indexOf(v.clone_or_share())); } return Ok(Box::new(StringBox::new("Error: indexOf(value) requires 1 arg"))); }
"clear" => { return Ok(array_box.clear()); },
"join" => { if let Some(sep) = _args.get(0) { return Ok(array_box.join(sep.clone_or_share())); } return Ok(Box::new(StringBox::new("Error: join(sep) requires 1 arg"))); }
"sort" => { return Ok(array_box.sort()); },
"reverse" => { return Ok(array_box.reverse()); },
"slice" => { if _args.len() >= 2 { return Ok(array_box.slice(_args[0].clone_or_share(), _args[1].clone_or_share())); } return Ok(Box::new(StringBox::new("Error: slice(start, end) requires 2 args"))); }
"push" => {
if let Some(v) = _args.get(0) {
return Ok(array_box.push(v.clone_or_share()));
}
return Ok(Box::new(StringBox::new(
"Error: push(value) requires 1 arg",
)));
}
"pop" => {
return Ok(array_box.pop());
}
"length" | "len" => {
return Ok(array_box.length());
}
"get" => {
if let Some(i) = _args.get(0) {
return Ok(array_box.get(i.clone_or_share()));
}
return Ok(Box::new(StringBox::new("Error: get(index) requires 1 arg")));
}
"set" => {
if _args.len() >= 2 {
return Ok(
array_box.set(_args[0].clone_or_share(), _args[1].clone_or_share())
);
}
return Ok(Box::new(StringBox::new(
"Error: set(index, value) requires 2 args",
)));
}
"remove" => {
if let Some(i) = _args.get(0) {
return Ok(array_box.remove(i.clone_or_share()));
}
return Ok(Box::new(StringBox::new(
"Error: remove(index) requires 1 arg",
)));
}
"contains" => {
if let Some(v) = _args.get(0) {
return Ok(array_box.contains(v.clone_or_share()));
}
return Ok(Box::new(StringBox::new(
"Error: contains(value) requires 1 arg",
)));
}
"indexOf" => {
if let Some(v) = _args.get(0) {
return Ok(array_box.indexOf(v.clone_or_share()));
}
return Ok(Box::new(StringBox::new(
"Error: indexOf(value) requires 1 arg",
)));
}
"clear" => {
return Ok(array_box.clear());
}
"join" => {
if let Some(sep) = _args.get(0) {
return Ok(array_box.join(sep.clone_or_share()));
}
return Ok(Box::new(StringBox::new("Error: join(sep) requires 1 arg")));
}
"sort" => {
return Ok(array_box.sort());
}
"reverse" => {
return Ok(array_box.reverse());
}
"slice" => {
if _args.len() >= 2 {
return Ok(
array_box.slice(_args[0].clone_or_share(), _args[1].clone_or_share())
);
}
return Ok(Box::new(StringBox::new(
"Error: slice(start, end) requires 2 args",
)));
}
_ => return Ok(Box::new(VoidBox::new())),
}
}
// MapBox methods (minimal set)
if let Some(map_box) = box_value.as_any().downcast_ref::<crate::boxes::map_box::MapBox>() {
if let Some(map_box) = box_value
.as_any()
.downcast_ref::<crate::boxes::map_box::MapBox>()
{
match method {
"set" => { if _args.len() >= 2 { return Ok(map_box.set(_args[0].clone_or_share(), _args[1].clone_or_share())); } return Ok(Box::new(StringBox::new("Error: set(key, value) requires 2 args"))); }
"get" => { if let Some(k) = _args.get(0) { return Ok(map_box.get(k.clone_or_share())); } return Ok(Box::new(StringBox::new("Error: get(key) requires 1 arg"))); }
"has" => { if let Some(k) = _args.get(0) { return Ok(map_box.has(k.clone_or_share())); } return Ok(Box::new(StringBox::new("Error: has(key) requires 1 arg"))); }
"delete" | "remove" => { if let Some(k) = _args.get(0) { return Ok(map_box.delete(k.clone_or_share())); } return Ok(Box::new(StringBox::new("Error: delete(key) requires 1 arg"))); }
"keys" => { return Ok(map_box.keys()); },
"values" => { return Ok(map_box.values()); },
"size" => { return Ok(map_box.size()); },
"clear" => { return Ok(map_box.clear()); },
"set" => {
if _args.len() >= 2 {
return Ok(
map_box.set(_args[0].clone_or_share(), _args[1].clone_or_share())
);
}
return Ok(Box::new(StringBox::new(
"Error: set(key, value) requires 2 args",
)));
}
"get" => {
if let Some(k) = _args.get(0) {
return Ok(map_box.get(k.clone_or_share()));
}
return Ok(Box::new(StringBox::new("Error: get(key) requires 1 arg")));
}
"has" => {
if let Some(k) = _args.get(0) {
return Ok(map_box.has(k.clone_or_share()));
}
return Ok(Box::new(StringBox::new("Error: has(key) requires 1 arg")));
}
"delete" | "remove" => {
if let Some(k) = _args.get(0) {
return Ok(map_box.delete(k.clone_or_share()));
}
return Ok(Box::new(StringBox::new(
"Error: delete(key) requires 1 arg",
)));
}
"keys" => {
return Ok(map_box.keys());
}
"values" => {
return Ok(map_box.values());
}
"size" => {
return Ok(map_box.size());
}
"clear" => {
return Ok(map_box.clear());
}
_ => return Ok(Box::new(VoidBox::new())),
}
}
// TaskGroupBox methods (scaffold → instance内の所有Futureに対して実行)
if box_value.as_any().downcast_ref::<crate::boxes::task_group_box::TaskGroupBox>().is_some() {
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>() {
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()); }
"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));
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()));
}
}
}
return Ok(Box::new(VoidBox::new()));
}
// P2PBox methods (minimal)
if let Some(p2p) = box_value.as_any().downcast_ref::<crate::boxes::p2p_box::P2PBox>() {
if let Some(p2p) = box_value
.as_any()
.downcast_ref::<crate::boxes::p2p_box::P2PBox>()
{
match method {
"send" => {
if _args.len() >= 2 { return Ok(p2p.send(_args[0].clone_or_share(), _args[1].clone_or_share())); }
return Ok(Box::new(StringBox::new("Error: send(to, intent) requires 2 args")));
if _args.len() >= 2 {
return Ok(p2p.send(_args[0].clone_or_share(), _args[1].clone_or_share()));
}
return Ok(Box::new(StringBox::new(
"Error: send(to, intent) requires 2 args",
)));
}
"on" => {
if _args.len() >= 2 { return Ok(p2p.on(_args[0].clone_or_share(), _args[1].clone_or_share())); }
return Ok(Box::new(StringBox::new("Error: on(intent, handler) requires 2 args")));
if _args.len() >= 2 {
return Ok(p2p.on(_args[0].clone_or_share(), _args[1].clone_or_share()));
}
return Ok(Box::new(StringBox::new(
"Error: on(intent, handler) requires 2 args",
)));
}
"onOnce" | "on_once" => {
if _args.len() >= 2 { return Ok(p2p.on_once(_args[0].clone_or_share(), _args[1].clone_or_share())); }
return Ok(Box::new(StringBox::new("Error: onOnce(intent, handler) requires 2 args")));
if _args.len() >= 2 {
return Ok(
p2p.on_once(_args[0].clone_or_share(), _args[1].clone_or_share())
);
}
return Ok(Box::new(StringBox::new(
"Error: onOnce(intent, handler) requires 2 args",
)));
}
"off" => {
if _args.len() >= 1 { return Ok(p2p.off(_args[0].clone_or_share())); }
return Ok(Box::new(StringBox::new("Error: off(intent) requires 1 arg")));
if _args.len() >= 1 {
return Ok(p2p.off(_args[0].clone_or_share()));
}
return Ok(Box::new(StringBox::new(
"Error: off(intent) requires 1 arg",
)));
}
"getLastFrom" => {
return Ok(p2p.get_last_from());
}
"getLastIntentName" => {
return Ok(p2p.get_last_intent_name());
}
"debug_nodes" | "debugNodes" => {
return Ok(p2p.debug_nodes());
}
"getNodeId" | "getId" => {
return Ok(p2p.get_node_id());
}
"getTransportType" | "transport" => {
return Ok(p2p.get_transport_type());
}
"isReachable" => {
if let Some(n) = _args.get(0) {
return Ok(p2p.is_reachable(n.clone_or_share()));
}
return Ok(Box::new(BoolBox::new(false)));
}
"getLastFrom" => { return Ok(p2p.get_last_from()); }
"getLastIntentName" => { return Ok(p2p.get_last_intent_name()); }
"debug_nodes" | "debugNodes" => { return Ok(p2p.debug_nodes()); }
"getNodeId" | "getId" => { return Ok(p2p.get_node_id()); }
"getTransportType" | "transport" => { return Ok(p2p.get_transport_type()); }
"isReachable" => { if let Some(n) = _args.get(0) { return Ok(p2p.is_reachable(n.clone_or_share())); } return Ok(Box::new(BoolBox::new(false))); }
_ => return Ok(Box::new(VoidBox::new())),
}
}
// SocketBox methods (minimal + timeouts)
if let Some(sock) = box_value.as_any().downcast_ref::<crate::boxes::socket_box::SocketBox>() {
if let Some(sock) = box_value
.as_any()
.downcast_ref::<crate::boxes::socket_box::SocketBox>()
{
match method {
"bind" => { if _args.len() >= 2 { return Ok(sock.bind(_args[0].clone_or_share(), _args[1].clone_or_share())); } return Ok(Box::new(StringBox::new("Error: bind(address, port) requires 2 args"))); }
"listen" => { if let Some(b) = _args.get(0) { return Ok(sock.listen(b.clone_or_share())); } return Ok(Box::new(StringBox::new("Error: listen(backlog) requires 1 arg"))); }
"accept" => { return Ok(sock.accept()); },
"acceptTimeout" | "accept_timeout" => { if let Some(ms) = _args.get(0) { return Ok(sock.accept_timeout(ms.clone_or_share())); } return Ok(Box::new(crate::box_trait::VoidBox::new())); }
"connect" => { if _args.len() >= 2 { return Ok(sock.connect(_args[0].clone_or_share(), _args[1].clone_or_share())); } return Ok(Box::new(StringBox::new("Error: connect(address, port) requires 2 args"))); }
"read" => { return Ok(sock.read()); },
"recvTimeout" | "recv_timeout" => { if let Some(ms) = _args.get(0) { return Ok(sock.recv_timeout(ms.clone_or_share())); } return Ok(Box::new(StringBox::new(""))); }
"write" => { if let Some(d) = _args.get(0) { return Ok(sock.write(d.clone_or_share())); } return Ok(Box::new(crate::box_trait::BoolBox::new(false))); }
"close" => { return Ok(sock.close()); },
"isServer" | "is_server" => { return Ok(sock.is_server()); },
"isConnected" | "is_connected" => { return Ok(sock.is_connected()); },
"bind" => {
if _args.len() >= 2 {
return Ok(sock.bind(_args[0].clone_or_share(), _args[1].clone_or_share()));
}
return Ok(Box::new(StringBox::new(
"Error: bind(address, port) requires 2 args",
)));
}
"listen" => {
if let Some(b) = _args.get(0) {
return Ok(sock.listen(b.clone_or_share()));
}
return Ok(Box::new(StringBox::new(
"Error: listen(backlog) requires 1 arg",
)));
}
"accept" => {
return Ok(sock.accept());
}
"acceptTimeout" | "accept_timeout" => {
if let Some(ms) = _args.get(0) {
return Ok(sock.accept_timeout(ms.clone_or_share()));
}
return Ok(Box::new(crate::box_trait::VoidBox::new()));
}
"connect" => {
if _args.len() >= 2 {
return Ok(
sock.connect(_args[0].clone_or_share(), _args[1].clone_or_share())
);
}
return Ok(Box::new(StringBox::new(
"Error: connect(address, port) requires 2 args",
)));
}
"read" => {
return Ok(sock.read());
}
"recvTimeout" | "recv_timeout" => {
if let Some(ms) = _args.get(0) {
return Ok(sock.recv_timeout(ms.clone_or_share()));
}
return Ok(Box::new(StringBox::new("")));
}
"write" => {
if let Some(d) = _args.get(0) {
return Ok(sock.write(d.clone_or_share()));
}
return Ok(Box::new(crate::box_trait::BoolBox::new(false)));
}
"close" => {
return Ok(sock.close());
}
"isServer" | "is_server" => {
return Ok(sock.is_server());
}
"isConnected" | "is_connected" => {
return Ok(sock.is_connected());
}
_ => return Ok(Box::new(VoidBox::new())),
}
}
// IntegerBox methods
if let Some(integer_box) = box_value.as_any().downcast_ref::<IntegerBox>() { match method { "toString" => { return Ok(Box::new(StringBox::new(integer_box.value.to_string()))); }, "abs" => { return Ok(Box::new(IntegerBox::new(integer_box.value.abs()))); }, _ => return Ok(Box::new(VoidBox::new())), } }
if let Some(integer_box) = box_value.as_any().downcast_ref::<IntegerBox>() {
match method {
"toString" => {
return Ok(Box::new(StringBox::new(integer_box.value.to_string())));
}
"abs" => {
return Ok(Box::new(IntegerBox::new(integer_box.value.abs())));
}
_ => return Ok(Box::new(VoidBox::new())),
}
}
// BoolBox methods
if let Some(bool_box) = box_value.as_any().downcast_ref::<BoolBox>() { match method { "toString" => { return Ok(Box::new(StringBox::new(bool_box.value.to_string()))); }, _ => return Ok(Box::new(VoidBox::new())), } }
if let Some(bool_box) = box_value.as_any().downcast_ref::<BoolBox>() {
match method {
"toString" => {
return Ok(Box::new(StringBox::new(bool_box.value.to_string())));
}
_ => return Ok(Box::new(VoidBox::new())),
}
}
// Default: return void for any unrecognized box type or method
Ok(Box::new(VoidBox::new()))
}
/// Debug helper for BoxCall tracing (enabled via NYASH_VM_DEBUG_BOXCALL=1)
pub(super) fn debug_log_boxcall(&self, recv: &VMValue, method: &str, args: &[Box<dyn NyashBox>], stage: &str, result: Option<&VMValue>) {
pub(super) fn debug_log_boxcall(
&self,
recv: &VMValue,
method: &str,
args: &[Box<dyn NyashBox>],
stage: &str,
result: Option<&VMValue>,
) {
if std::env::var("NYASH_VM_DEBUG_BOXCALL").ok().as_deref() == Some("1") {
let recv_ty = match recv {
VMValue::BoxRef(arc) => arc.type_name().to_string(),
@ -373,9 +743,24 @@ impl VM {
VMValue::Future(_) => "Future".to_string(),
VMValue::Void => "Void".to_string(),
};
eprintln!("[VM-BOXCALL][{}] recv_ty={} method={} argc={} args={:?} => result_ty={}", stage, recv_ty, method, args.len(), args_desc, res_ty);
eprintln!(
"[VM-BOXCALL][{}] recv_ty={} method={} argc={} args={:?} => result_ty={}",
stage,
recv_ty,
method,
args.len(),
args_desc,
res_ty
);
} else {
eprintln!("[VM-BOXCALL][{}] recv_ty={} method={} argc={} args={:?}", stage, recv_ty, method, args.len(), args_desc);
eprintln!(
"[VM-BOXCALL][{}] recv_ty={} method={} argc={} args={:?}",
stage,
recv_ty,
method,
args.len(),
args_desc
);
}
}
}

View File

@ -1,5 +1,5 @@
use crate::mir::BasicBlockId;
use crate::backend::vm::VMValue;
use crate::mir::BasicBlockId;
/// Control flow result from instruction execution
pub(crate) enum ControlFlow {

View File

@ -11,10 +11,10 @@
* Behavior and public APIs are preserved. This is a pure move/refactor.
*/
use crate::mir::{MirModule, MirFunction, MirInstruction, BasicBlockId};
use crate::box_trait::NyashBox;
use super::{vm::VM, vm::VMError, vm::VMValue};
use super::{vm::VMError, vm::VMValue, vm::VM};
use crate::backend::vm_control_flow::ControlFlow;
use crate::box_trait::NyashBox;
use crate::mir::{BasicBlockId, MirFunction, MirInstruction, MirModule};
impl VM {
/// Execute a MIR module
@ -34,12 +34,17 @@ impl VM {
if crate::config::env::vm_pic_stats() {
self.print_cache_stats_summary();
}
if let Some(jm) = &self.jit_manager { jm.print_summary(); }
if let Some(jm) = &self.jit_manager {
jm.print_summary();
}
{
let lvl = crate::config::env::gc_trace_level();
if lvl > 0 {
if let Some((sp, rd, wr)) = self.runtime.gc.snapshot_counters() {
eprintln!("[GC] counters: safepoints={} read_barriers={} write_barriers={}", sp, rd, wr);
eprintln!(
"[GC] counters: safepoints={} read_barriers={} write_barriers={}",
sp, rd, wr
);
}
let roots_total = self.scope_tracker.root_count_total();
let root_regions = self.scope_tracker.root_regions();
@ -48,8 +53,12 @@ impl VM {
"[GC] mock_mark: roots_total={} regions={} object_field_slots={}",
roots_total, root_regions, field_slots
);
if lvl >= 2 { self.gc_print_roots_breakdown(); }
if lvl >= 3 { self.gc_print_reachability_depth2(); }
if lvl >= 2 {
self.gc_print_roots_breakdown();
}
if lvl >= 3 {
self.gc_print_reachability_depth2();
}
}
}
Ok(result.to_nyash_box())
@ -97,9 +106,9 @@ impl VM {
.module
.as_ref()
.ok_or_else(|| VMError::InvalidInstruction("No active module".to_string()))?;
let function_ref = module_ref
.get_function(func_name)
.ok_or_else(|| VMError::InvalidInstruction(format!("Function '{}' not found", func_name)))?;
let function_ref = module_ref.get_function(func_name).ok_or_else(|| {
VMError::InvalidInstruction(format!("Function '{}' not found", func_name))
})?;
let function = function_ref.clone();
let saved_values = std::mem::take(&mut self.values);
@ -143,7 +152,11 @@ impl VM {
self.current_function = Some(function.signature.name.clone());
if let Some(jm) = &mut self.jit_manager {
if let Ok(s) = std::env::var("NYASH_JIT_THRESHOLD") {
if let Ok(t) = s.parse::<u32>() { if t > 0 { jm.set_threshold(t); } }
if let Ok(t) = s.parse::<u32>() {
if t > 0 {
jm.set_threshold(t);
}
}
}
jm.record_entry(&function.signature.name);
let _ = jm.maybe_compile(&function.signature.name, function);
@ -197,7 +210,10 @@ impl VM {
} 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")
{
eprintln!("[JIT] fallback: VM path taken for {}", function.signature.name);
eprintln!(
"[JIT] fallback: VM path taken for {}",
function.signature.name
);
if jit_only {
self.leave_root_region();
self.scope_tracker.pop_scope();
@ -285,9 +301,13 @@ impl VM {
if should_return.is_none() && next_block.is_none() {
if let Some(term) = &block.terminator {
match self.execute_instruction(term)? {
ControlFlow::Continue => {},
ControlFlow::Jump(target) => { next_block = Some(target); },
ControlFlow::Return(value) => { should_return = Some(value); },
ControlFlow::Continue => {}
ControlFlow::Jump(target) => {
next_block = Some(target);
}
ControlFlow::Return(value) => {
should_return = Some(value);
}
}
}
}
@ -320,10 +340,16 @@ impl VM {
}
/// Execute a single instruction
pub(super) fn execute_instruction(&mut self, instruction: &MirInstruction) -> Result<ControlFlow, VMError> {
pub(super) fn execute_instruction(
&mut self,
instruction: &MirInstruction,
) -> Result<ControlFlow, VMError> {
let debug_global = std::env::var("NYASH_VM_DEBUG").ok().as_deref() == Some("1");
let debug_exec = debug_global || std::env::var("NYASH_VM_DEBUG_EXEC").ok().as_deref() == Some("1");
if debug_exec { eprintln!("[VM] execute_instruction: {:?}", instruction); }
let debug_exec =
debug_global || std::env::var("NYASH_VM_DEBUG_EXEC").ok().as_deref() == Some("1");
if debug_exec {
eprintln!("[VM] execute_instruction: {:?}", instruction);
}
self.record_instruction(instruction);
super::dispatch::execute_instruction(self, instruction, debug_global)
}

View File

@ -6,7 +6,7 @@
* - Debug prints for roots snapshot and shallow reachability
*/
use super::vm::{VM, VMValue};
use super::vm::{VMValue, VM};
impl VM {
/// Enter a GC root region and return a guard that leaves on drop
@ -22,11 +22,17 @@ impl VM {
}
/// Leave current GC root region
pub(super) fn leave_root_region(&mut self) { self.scope_tracker.leave_root_region(); }
pub(super) fn leave_root_region(&mut self) {
self.scope_tracker.leave_root_region();
}
/// Site info for GC logs: (func, bb, pc)
pub(super) fn gc_site_info(&self) -> (String, i64, i64) {
let func = self.current_function.as_deref().unwrap_or("<none>").to_string();
let func = self
.current_function
.as_deref()
.unwrap_or("<none>")
.to_string();
let bb = self.frame.current_block.map(|b| b.0 as i64).unwrap_or(-1);
let pc = self.frame.pc as i64;
(func, bb, pc)
@ -78,7 +84,10 @@ impl VM {
}
if let Some(map) = b.as_any().downcast_ref::<crate::boxes::map_box::MapBox>() {
let vals = map.values();
if let Some(arr2) = vals.as_any().downcast_ref::<crate::boxes::array::ArrayBox>() {
if let Some(arr2) = vals
.as_any()
.downcast_ref::<crate::boxes::array::ArrayBox>()
{
if let Ok(items) = arr2.items.read() {
for item in items.iter() {
let tn = item.type_name().to_string();

File diff suppressed because it is too large Load Diff

View File

@ -1,41 +1,66 @@
use crate::backend::vm::ControlFlow;
use crate::backend::{VMError, VMValue, VM};
use crate::box_trait::NyashBox;
use crate::mir::ValueId;
use crate::backend::vm::ControlFlow;
use crate::backend::{VM, VMError, VMValue};
impl VM {
/// Execute Call instruction (supports function name or FunctionBox value)
pub(crate) fn execute_call(&mut self, dst: Option<ValueId>, func: ValueId, args: &[ValueId]) -> Result<ControlFlow, VMError> {
pub(crate) fn execute_call(
&mut self,
dst: Option<ValueId>,
func: ValueId,
args: &[ValueId],
) -> Result<ControlFlow, VMError> {
// Evaluate function value
let fval = self.get_value(func)?;
match fval {
VMValue::String(func_name) => {
// Legacy: call function by name
let arg_values: Vec<VMValue> = args.iter().map(|arg| self.get_value(*arg)).collect::<Result<Vec<_>, _>>()?;
let arg_values: Vec<VMValue> = args
.iter()
.map(|arg| self.get_value(*arg))
.collect::<Result<Vec<_>, _>>()?;
let result = self.call_function_by_name(&func_name, arg_values)?;
if let Some(dst_id) = dst { self.set_value(dst_id, result); }
if let Some(dst_id) = dst {
self.set_value(dst_id, result);
}
Ok(ControlFlow::Continue)
}
VMValue::BoxRef(arc_box) => {
// FunctionBox call path
if let Some(fun) = arc_box.as_any().downcast_ref::<crate::boxes::function_box::FunctionBox>() {
if let Some(fun) = arc_box
.as_any()
.downcast_ref::<crate::boxes::function_box::FunctionBox>()
{
// Convert args to NyashBox for interpreter helper
let nyash_args: Vec<Box<dyn NyashBox>> = args.iter()
let nyash_args: Vec<Box<dyn NyashBox>> = args
.iter()
.map(|a| self.get_value(*a).map(|v| v.to_nyash_box()))
.collect::<Result<Vec<_>, VMError>>()?;
// Execute via interpreter helper
match crate::interpreter::run_function_box(fun, nyash_args) {
Ok(out) => {
if let Some(dst_id) = dst { self.set_value(dst_id, VMValue::from_nyash_box(out)); }
if let Some(dst_id) = dst {
self.set_value(dst_id, VMValue::from_nyash_box(out));
}
Ok(ControlFlow::Continue)
}
Err(e) => Err(VMError::InvalidInstruction(format!("FunctionBox call failed: {:?}", e)))
Err(e) => Err(VMError::InvalidInstruction(format!(
"FunctionBox call failed: {:?}",
e
))),
}
} else {
Err(VMError::TypeError(format!("Call target not callable: {}", arc_box.type_name())))
Err(VMError::TypeError(format!(
"Call target not callable: {}",
arc_box.type_name()
)))
}
}
other => Err(VMError::TypeError(format!("Call target must be function name or FunctionBox, got {:?}", other))),
other => Err(VMError::TypeError(format!(
"Call target must be function name or FunctionBox, got {:?}",
other
))),
}
}
}

View File

@ -1,16 +1,27 @@
use crate::mir::{ConstValue, BinaryOp, CompareOp, UnaryOp, ValueId, BasicBlockId, TypeOpKind, MirType};
use crate::backend::vm::ControlFlow;
use crate::backend::{VMError, VMValue, VM};
use crate::box_trait::{BoolBox, VoidBox};
use crate::boxes::ArrayBox;
use crate::mir::{
BasicBlockId, BinaryOp, CompareOp, ConstValue, MirType, TypeOpKind, UnaryOp, ValueId,
};
use std::sync::Arc;
use crate::backend::vm::ControlFlow;
use crate::backend::{VM, VMError, VMValue};
impl VM {
// ---- Helpers (PIC/VTable bookkeeping) ----
pub(super) fn build_pic_key(&self, recv: &VMValue, method: &str, method_id: Option<u16>) -> String {
pub(super) fn build_pic_key(
&self,
recv: &VMValue,
method: &str,
method_id: Option<u16>,
) -> String {
let label = self.cache_label_for_recv(recv);
let ver = self.cache_version_for_label(&label);
if let Some(mid) = method_id { format!("v{}:{}#{}", ver, label, mid) } else { format!("v{}:{}#{}", ver, label, method) }
if let Some(mid) = method_id {
format!("v{}:{}#{}", ver, label, mid)
} else {
format!("v{}:{}#{}", ver, label, method)
}
}
pub(super) fn pic_record_hit(&mut self, key: &str) {
use std::collections::hash_map::Entry;
@ -19,23 +30,43 @@ impl VM {
let v = e.get_mut();
*v = v.saturating_add(1);
if std::env::var("NYASH_VM_PIC_DEBUG").ok().as_deref() == Some("1") {
if *v == 8 || *v == 32 { eprintln!("[PIC] Hot BoxCall site '{}' hits={} (skeleton)", key, v); }
if *v == 8 || *v == 32 {
eprintln!("[PIC] Hot BoxCall site '{}' hits={} (skeleton)", key, v);
}
}
}
Entry::Vacant(v) => { v.insert(1); }
Entry::Vacant(v) => {
v.insert(1);
}
}
}
pub(super) fn pic_hits(&self, key: &str) -> u32 { *self.boxcall_pic_hits.get(key).unwrap_or(&0) }
pub(super) fn build_vtable_key(&self, class_name: &str, method_id: u16, arity: usize) -> String {
pub(super) fn pic_hits(&self, key: &str) -> u32 {
*self.boxcall_pic_hits.get(key).unwrap_or(&0)
}
pub(super) fn build_vtable_key(
&self,
class_name: &str,
method_id: u16,
arity: usize,
) -> String {
let label = format!("BoxRef:{}", class_name);
let ver = self.cache_version_for_label(&label);
format!("VT@v{}:{}#{}{}", ver, class_name, method_id, format!("/{}", arity))
format!(
"VT@v{}:{}#{}{}",
ver,
class_name,
method_id,
format!("/{}", arity)
)
}
pub(super) fn try_poly_pic(&mut self, pic_site_key: &str, recv: &VMValue) -> Option<String> {
let label = self.cache_label_for_recv(recv);
let ver = self.cache_version_for_label(&label);
if let Some(entries) = self.boxcall_poly_pic.get_mut(pic_site_key) {
if let Some(idx) = entries.iter().position(|(l, v, _)| *l == label && *v == ver) {
if let Some(idx) = entries
.iter()
.position(|(l, v, _)| *l == label && *v == ver)
{
let entry = entries.remove(idx);
entries.push(entry.clone());
return Some(entry.2);
@ -50,15 +81,28 @@ impl VM {
match self.boxcall_poly_pic.entry(pic_site_key.to_string()) {
Entry::Occupied(mut e) => {
let v = e.get_mut();
if let Some(idx) = v.iter().position(|(l, vv, _)| *l == label && *vv == ver) { v.remove(idx); }
if v.len() >= 4 { v.remove(0); }
if let Some(idx) = v.iter().position(|(l, vv, _)| *l == label && *vv == ver) {
v.remove(idx);
}
if v.len() >= 4 {
v.remove(0);
}
v.push((label.clone(), ver, func_name.to_string()));
}
Entry::Vacant(v) => { v.insert(vec![(label.clone(), ver, func_name.to_string())]); }
Entry::Vacant(v) => {
v.insert(vec![(label.clone(), ver, func_name.to_string())]);
}
}
if crate::config::env::vm_pic_stats() {
if let Some(v) = self.boxcall_poly_pic.get(pic_site_key) {
eprintln!("[PIC] site={} size={} last=({}, v{}) -> {}", pic_site_key, v.len(), label, ver, func_name);
eprintln!(
"[PIC] site={} size={} last=({}, v{}) -> {}",
pic_site_key,
v.len(),
label,
ver,
func_name
);
}
}
}
@ -73,25 +117,45 @@ impl VM {
VMValue::BoxRef(b) => format!("BoxRef:{}", b.type_name()),
}
}
pub(super) fn cache_version_for_label(&self, label: &str) -> u32 { crate::runtime::cache_versions::get_version(label) }
pub(super) fn cache_version_for_label(&self, label: &str) -> u32 {
crate::runtime::cache_versions::get_version(label)
}
#[allow(dead_code)]
pub fn bump_cache_version(&mut self, label: &str) { crate::runtime::cache_versions::bump_version(label) }
pub fn bump_cache_version(&mut self, label: &str) {
crate::runtime::cache_versions::bump_version(label)
}
// ---- Basics ----
pub(crate) fn execute_const(&mut self, dst: ValueId, value: &ConstValue) -> Result<ControlFlow, VMError> {
pub(crate) fn execute_const(
&mut self,
dst: ValueId,
value: &ConstValue,
) -> Result<ControlFlow, VMError> {
let vm_value = VMValue::from(value);
self.set_value(dst, vm_value);
Ok(ControlFlow::Continue)
}
pub(crate) fn execute_binop(&mut self, dst: ValueId, op: &BinaryOp, lhs: ValueId, rhs: ValueId) -> Result<ControlFlow, VMError> {
pub(crate) fn execute_binop(
&mut self,
dst: ValueId,
op: &BinaryOp,
lhs: ValueId,
rhs: ValueId,
) -> Result<ControlFlow, VMError> {
match *op {
BinaryOp::And | BinaryOp::Or => {
if std::env::var("NYASH_VM_DEBUG_ANDOR").ok().as_deref() == Some("1") { eprintln!("[VM] And/Or short-circuit path"); }
if std::env::var("NYASH_VM_DEBUG_ANDOR").ok().as_deref() == Some("1") {
eprintln!("[VM] And/Or short-circuit path");
}
let left = self.get_value(lhs)?;
let right = self.get_value(rhs)?;
let lb = left.as_bool()?;
let rb = right.as_bool()?;
let out = match *op { BinaryOp::And => lb && rb, BinaryOp::Or => lb || rb, _ => unreachable!() };
let out = match *op {
BinaryOp::And => lb && rb,
BinaryOp::Or => lb || rb,
_ => unreachable!(),
};
self.set_value(dst, VMValue::Bool(out));
Ok(ControlFlow::Continue)
}
@ -104,29 +168,63 @@ impl VM {
}
}
}
pub(crate) fn execute_unaryop(&mut self, dst: ValueId, op: &UnaryOp, operand: ValueId) -> Result<ControlFlow, VMError> {
pub(crate) fn execute_unaryop(
&mut self,
dst: ValueId,
op: &UnaryOp,
operand: ValueId,
) -> Result<ControlFlow, VMError> {
let operand_val = self.get_value(operand)?;
let result = self.execute_unary_op(op, &operand_val)?;
self.set_value(dst, result);
Ok(ControlFlow::Continue)
}
pub(crate) fn execute_compare(&mut self, dst: ValueId, op: &CompareOp, lhs: ValueId, rhs: ValueId) -> Result<ControlFlow, VMError> {
let debug_cmp = std::env::var("NYASH_VM_DEBUG").ok().as_deref() == Some("1") || std::env::var("NYASH_VM_DEBUG_CMP").ok().as_deref() == Some("1");
if debug_cmp { eprintln!("[VM] execute_compare enter op={:?} lhs={:?} rhs={:?}", op, lhs, rhs); }
pub(crate) fn execute_compare(
&mut self,
dst: ValueId,
op: &CompareOp,
lhs: ValueId,
rhs: ValueId,
) -> Result<ControlFlow, VMError> {
let debug_cmp = std::env::var("NYASH_VM_DEBUG").ok().as_deref() == Some("1")
|| std::env::var("NYASH_VM_DEBUG_CMP").ok().as_deref() == Some("1");
if debug_cmp {
eprintln!(
"[VM] execute_compare enter op={:?} lhs={:?} rhs={:?}",
op, lhs, rhs
);
}
let mut left = self.get_value(lhs)?;
let mut right = self.get_value(rhs)?;
if debug_cmp { eprintln!("[VM] execute_compare values: left={:?} right={:?}", left, right); }
if debug_cmp {
eprintln!(
"[VM] execute_compare values: left={:?} right={:?}",
left, right
);
}
left = match left {
VMValue::BoxRef(b) => {
if let Some(ib) = b.as_any().downcast_ref::<crate::box_trait::IntegerBox>() { VMValue::Integer(ib.value) }
else { match b.to_string_box().value.trim().parse::<i64>() { Ok(n) => VMValue::Integer(n), Err(_) => VMValue::BoxRef(b) } }
if let Some(ib) = b.as_any().downcast_ref::<crate::box_trait::IntegerBox>() {
VMValue::Integer(ib.value)
} else {
match b.to_string_box().value.trim().parse::<i64>() {
Ok(n) => VMValue::Integer(n),
Err(_) => VMValue::BoxRef(b),
}
}
}
other => other,
};
right = match right {
VMValue::BoxRef(b) => {
if let Some(ib) = b.as_any().downcast_ref::<crate::box_trait::IntegerBox>() { VMValue::Integer(ib.value) }
else { match b.to_string_box().value.trim().parse::<i64>() { Ok(n) => VMValue::Integer(n), Err(_) => VMValue::BoxRef(b) } }
if let Some(ib) = b.as_any().downcast_ref::<crate::box_trait::IntegerBox>() {
VMValue::Integer(ib.value)
} else {
match b.to_string_box().value.trim().parse::<i64>() {
Ok(n) => VMValue::Integer(n),
Err(_) => VMValue::BoxRef(b),
}
}
}
other => other,
};
@ -139,33 +237,70 @@ impl VM {
println!("{}", val.to_string());
Ok(ControlFlow::Continue)
}
pub(crate) fn execute_jump(&self, target: BasicBlockId) -> Result<ControlFlow, VMError> { Ok(ControlFlow::Jump(target)) }
pub(crate) fn execute_branch(&self, condition: ValueId, then_bb: BasicBlockId, else_bb: BasicBlockId) -> Result<ControlFlow, VMError> {
pub(crate) fn execute_jump(&self, target: BasicBlockId) -> Result<ControlFlow, VMError> {
Ok(ControlFlow::Jump(target))
}
pub(crate) fn execute_branch(
&self,
condition: ValueId,
then_bb: BasicBlockId,
else_bb: BasicBlockId,
) -> Result<ControlFlow, VMError> {
let cond_val = self.get_value(condition)?;
let should_branch = match &cond_val {
VMValue::Bool(b) => *b,
VMValue::Void => false,
VMValue::Integer(i) => *i != 0,
VMValue::BoxRef(b) => {
if let Some(bool_box) = b.as_any().downcast_ref::<BoolBox>() { bool_box.value }
else if b.as_any().downcast_ref::<VoidBox>().is_some() { false }
else { return Err(VMError::TypeError(format!("Branch condition must be bool, void, or integer, got BoxRef({})", b.type_name()))); }
if let Some(bool_box) = b.as_any().downcast_ref::<BoolBox>() {
bool_box.value
} else if b.as_any().downcast_ref::<VoidBox>().is_some() {
false
} else {
return Err(VMError::TypeError(format!(
"Branch condition must be bool, void, or integer, got BoxRef({})",
b.type_name()
)));
}
}
_ => {
return Err(VMError::TypeError(format!(
"Branch condition must be bool, void, or integer, got {:?}",
cond_val
)))
}
_ => return Err(VMError::TypeError(format!("Branch condition must be bool, void, or integer, got {:?}", cond_val))),
};
Ok(ControlFlow::Jump(if should_branch { then_bb } else { else_bb }))
Ok(ControlFlow::Jump(if should_branch {
then_bb
} else {
else_bb
}))
}
pub(crate) fn execute_return(&self, value: Option<ValueId>) -> Result<ControlFlow, VMError> {
if let Some(val_id) = value {
let return_val = self.get_value(val_id)?;
if crate::config::env::vm_vt_trace() { eprintln!("[VT] Return id={} val={}", val_id.to_usize(), return_val.to_string()); }
if crate::config::env::vm_vt_trace() {
eprintln!(
"[VT] Return id={} val={}",
val_id.to_usize(),
return_val.to_string()
);
}
Ok(ControlFlow::Return(return_val))
} else {
if crate::config::env::vm_vt_trace() { eprintln!("[VT] Return void"); }
if crate::config::env::vm_vt_trace() {
eprintln!("[VT] Return void");
}
Ok(ControlFlow::Return(VMValue::Void))
}
}
pub(crate) fn execute_typeop(&mut self, dst: ValueId, op: &TypeOpKind, value: ValueId, ty: &MirType) -> Result<ControlFlow, VMError> {
pub(crate) fn execute_typeop(
&mut self,
dst: ValueId,
op: &TypeOpKind,
value: ValueId,
ty: &MirType,
) -> Result<ControlFlow, VMError> {
let val = self.get_value(value)?;
match op {
TypeOpKind::Check => {
@ -175,7 +310,9 @@ impl VM {
(VMValue::Bool(_), MirType::Bool) => true,
(VMValue::String(_), MirType::String) => true,
(VMValue::Void, MirType::Void) => true,
(VMValue::BoxRef(arc_box), MirType::Box(box_name)) => arc_box.type_name() == box_name,
(VMValue::BoxRef(arc_box), MirType::Box(box_name)) => {
arc_box.type_name() == box_name
}
_ => false,
};
self.set_value(dst, VMValue::Bool(is_type));
@ -185,34 +322,65 @@ impl VM {
let result = match (&val, ty) {
(VMValue::Integer(i), MirType::Float) => VMValue::Float(*i as f64),
(VMValue::Float(f), MirType::Integer) => VMValue::Integer(*f as i64),
(VMValue::Integer(_), MirType::Integer) | (VMValue::Float(_), MirType::Float) | (VMValue::Bool(_), MirType::Bool) | (VMValue::String(_), MirType::String) => val.clone(),
(VMValue::BoxRef(arc_box), MirType::Box(box_name)) if arc_box.type_name() == box_name => val.clone(),
_ => { return Err(VMError::TypeError(format!("Cannot cast {:?} to {:?}", val, ty))); }
(VMValue::Integer(_), MirType::Integer)
| (VMValue::Float(_), MirType::Float)
| (VMValue::Bool(_), MirType::Bool)
| (VMValue::String(_), MirType::String) => val.clone(),
(VMValue::BoxRef(arc_box), MirType::Box(box_name))
if arc_box.type_name() == box_name =>
{
val.clone()
}
_ => {
return Err(VMError::TypeError(format!(
"Cannot cast {:?} to {:?}",
val, ty
)));
}
};
self.set_value(dst, result);
Ok(ControlFlow::Continue)
}
}
}
pub(crate) fn execute_phi(&mut self, dst: ValueId, inputs: &[(BasicBlockId, ValueId)]) -> Result<ControlFlow, VMError> {
pub(crate) fn execute_phi(
&mut self,
dst: ValueId,
inputs: &[(BasicBlockId, ValueId)],
) -> Result<ControlFlow, VMError> {
let selected = self.loop_execute_phi(dst, inputs)?;
self.set_value(dst, selected);
Ok(ControlFlow::Continue)
}
pub(crate) fn execute_load(&mut self, dst: ValueId, ptr: ValueId) -> Result<ControlFlow, VMError> {
pub(crate) fn execute_load(
&mut self,
dst: ValueId,
ptr: ValueId,
) -> Result<ControlFlow, VMError> {
let loaded_value = self.get_value(ptr)?;
self.set_value(dst, loaded_value);
Ok(ControlFlow::Continue)
}
pub(crate) fn execute_store(&mut self, value: ValueId, ptr: ValueId) -> Result<ControlFlow, VMError> {
pub(crate) fn execute_store(
&mut self,
value: ValueId,
ptr: ValueId,
) -> Result<ControlFlow, VMError> {
let val = self.get_value(value)?;
self.set_value(ptr, val);
Ok(ControlFlow::Continue)
}
pub(crate) fn execute_copy(&mut self, dst: ValueId, src: ValueId) -> Result<ControlFlow, VMError> {
pub(crate) fn execute_copy(
&mut self,
dst: ValueId,
src: ValueId,
) -> Result<ControlFlow, VMError> {
let value = self.get_value(src)?;
let cloned = match &value {
VMValue::BoxRef(arc_box) => { let cloned_box = arc_box.clone_or_share(); VMValue::BoxRef(Arc::from(cloned_box)) }
VMValue::BoxRef(arc_box) => {
let cloned_box = arc_box.clone_or_share();
VMValue::BoxRef(Arc::from(cloned_box))
}
other => other.clone(),
};
self.set_value(dst, cloned);
@ -220,7 +388,12 @@ impl VM {
}
// ---- Arrays ----
pub(crate) fn execute_array_get(&mut self, dst: ValueId, array: ValueId, index: ValueId) -> Result<ControlFlow, VMError> {
pub(crate) fn execute_array_get(
&mut self,
dst: ValueId,
array: ValueId,
index: ValueId,
) -> Result<ControlFlow, VMError> {
let array_val = self.get_value(array)?;
let index_val = self.get_value(index)?;
if let VMValue::BoxRef(array_box) = &array_val {
@ -229,10 +402,23 @@ impl VM {
let result = array.get(index_box);
self.set_value(dst, VMValue::BoxRef(Arc::from(result)));
Ok(ControlFlow::Continue)
} else { Err(VMError::TypeError("ArrayGet requires an ArrayBox".to_string())) }
} else { Err(VMError::TypeError("ArrayGet requires array and integer index".to_string())) }
} else {
Err(VMError::TypeError(
"ArrayGet requires an ArrayBox".to_string(),
))
}
} else {
Err(VMError::TypeError(
"ArrayGet requires array and integer index".to_string(),
))
}
}
pub(crate) fn execute_array_set(&mut self, array: ValueId, index: ValueId, value: ValueId) -> Result<ControlFlow, VMError> {
pub(crate) fn execute_array_set(
&mut self,
array: ValueId,
index: ValueId,
value: ValueId,
) -> Result<ControlFlow, VMError> {
let array_val = self.get_value(array)?;
let index_val = self.get_value(index)?;
let value_val = self.get_value(value)?;
@ -243,27 +429,50 @@ impl VM {
let box_value = value_val.to_nyash_box();
array.set(index_box, box_value);
Ok(ControlFlow::Continue)
} else { Err(VMError::TypeError("ArraySet requires an ArrayBox".to_string())) }
} else { Err(VMError::TypeError("ArraySet requires array and integer index".to_string())) }
} else {
Err(VMError::TypeError(
"ArraySet requires an ArrayBox".to_string(),
))
}
} else {
Err(VMError::TypeError(
"ArraySet requires array and integer index".to_string(),
))
}
}
// ---- Refs/Weak/Barriers ----
pub(crate) fn execute_ref_new(&mut self, dst: ValueId, box_val: ValueId) -> Result<ControlFlow, VMError> {
pub(crate) fn execute_ref_new(
&mut self,
dst: ValueId,
box_val: ValueId,
) -> Result<ControlFlow, VMError> {
let box_value = self.get_value(box_val)?;
self.set_value(dst, box_value);
Ok(ControlFlow::Continue)
}
pub(crate) fn execute_ref_get(&mut self, dst: ValueId, reference: ValueId, field: &str) -> Result<ControlFlow, VMError> {
pub(crate) fn execute_ref_get(
&mut self,
dst: ValueId,
reference: ValueId,
field: &str,
) -> Result<ControlFlow, VMError> {
let debug_ref = std::env::var("NYASH_VM_DEBUG_REF").ok().as_deref() == Some("1");
if debug_ref { eprintln!("[VM] RefGet ref={:?} field={}", reference, field); }
if debug_ref {
eprintln!("[VM] RefGet ref={:?} field={}", reference, field);
}
let is_internal = self.object_internal.contains(&reference);
if !is_internal {
if let Some(class_name) = self.object_class.get(&reference) {
if let Ok(decls) = self.runtime.box_declarations.read() {
if let Some(decl) = decls.get(class_name) {
let has_vis = !decl.public_fields.is_empty() || !decl.private_fields.is_empty();
let has_vis =
!decl.public_fields.is_empty() || !decl.private_fields.is_empty();
if has_vis && !decl.public_fields.iter().any(|f| f == field) {
return Err(VMError::TypeError(format!("Field '{}' is private in {}", field, class_name)));
return Err(VMError::TypeError(format!(
"Field '{}' is private in {}",
field, class_name
)));
}
}
}
@ -271,78 +480,140 @@ impl VM {
}
let mut field_value = if let Some(fields) = self.object_fields.get(&reference) {
if let Some(value) = fields.get(field) {
if debug_ref { eprintln!("[VM] RefGet hit: {} -> {:?}", field, value); }
if debug_ref {
eprintln!("[VM] RefGet hit: {} -> {:?}", field, value);
}
value.clone()
} else {
if debug_ref { eprintln!("[VM] RefGet miss: {} -> default 0", field); }
if debug_ref {
eprintln!("[VM] RefGet miss: {} -> default 0", field);
}
VMValue::Integer(0)
}
} else {
if debug_ref { eprintln!("[VM] RefGet no fields: -> default 0"); }
if debug_ref {
eprintln!("[VM] RefGet no fields: -> default 0");
}
VMValue::Integer(0)
};
if matches!(field_value, VMValue::Integer(0)) && field == "console" {
if debug_ref { eprintln!("[VM] RefGet special binding: console -> Plugin ConsoleBox"); }
if debug_ref {
eprintln!("[VM] RefGet special binding: console -> Plugin ConsoleBox");
}
let host = crate::runtime::get_global_plugin_host();
let host = host.read().unwrap();
if let Ok(pbox) = host.create_box("ConsoleBox", &[]) {
field_value = VMValue::from_nyash_box(pbox);
if !self.object_fields.contains_key(&reference) { self.object_fields.insert(reference, std::collections::HashMap::new()); }
if let Some(fields) = self.object_fields.get_mut(&reference) { fields.insert(field.to_string(), field_value.clone()); }
if !self.object_fields.contains_key(&reference) {
self.object_fields
.insert(reference, std::collections::HashMap::new());
}
if let Some(fields) = self.object_fields.get_mut(&reference) {
fields.insert(field.to_string(), field_value.clone());
}
}
}
self.set_value(dst, field_value);
Ok(ControlFlow::Continue)
}
pub(crate) fn execute_ref_set(&mut self, reference: ValueId, field: &str, value: ValueId) -> Result<ControlFlow, VMError> {
pub(crate) fn execute_ref_set(
&mut self,
reference: ValueId,
field: &str,
value: ValueId,
) -> Result<ControlFlow, VMError> {
let debug_ref = std::env::var("NYASH_VM_DEBUG_REF").ok().as_deref() == Some("1");
let new_value = self.get_value(value)?;
if debug_ref { eprintln!("[VM] RefSet ref={:?} field={} value={:?}", reference, field, new_value); }
if debug_ref {
eprintln!(
"[VM] RefSet ref={:?} field={} value={:?}",
reference, field, new_value
);
}
let is_internal = self.object_internal.contains(&reference);
if !is_internal {
if let Some(class_name) = self.object_class.get(&reference) {
if let Ok(decls) = self.runtime.box_declarations.read() {
if let Some(decl) = decls.get(class_name) {
let has_vis = !decl.public_fields.is_empty() || !decl.private_fields.is_empty();
let has_vis =
!decl.public_fields.is_empty() || !decl.private_fields.is_empty();
if has_vis && !decl.public_fields.iter().any(|f| f == field) {
return Err(VMError::TypeError(format!("Field '{}' is private in {}", field, class_name)));
return Err(VMError::TypeError(format!(
"Field '{}' is private in {}",
field, class_name
)));
}
}
}
}
}
if !self.object_fields.contains_key(&reference) { self.object_fields.insert(reference, std::collections::HashMap::new()); }
if !self.object_fields.contains_key(&reference) {
self.object_fields
.insert(reference, std::collections::HashMap::new());
}
crate::backend::gc_helpers::gc_write_barrier_site(&self.runtime, "RefSet");
if let Some(fields) = self.object_fields.get_mut(&reference) { fields.insert(field.to_string(), new_value); if debug_ref { eprintln!("[VM] RefSet stored: {}", field); } }
if let Some(fields) = self.object_fields.get_mut(&reference) {
fields.insert(field.to_string(), new_value);
if debug_ref {
eprintln!("[VM] RefSet stored: {}", field);
}
}
Ok(ControlFlow::Continue)
}
pub(crate) fn execute_weak_new(&mut self, dst: ValueId, box_val: ValueId) -> Result<ControlFlow, VMError> {
pub(crate) fn execute_weak_new(
&mut self,
dst: ValueId,
box_val: ValueId,
) -> Result<ControlFlow, VMError> {
let box_value = self.get_value(box_val)?;
self.set_value(dst, box_value);
Ok(ControlFlow::Continue)
}
pub(crate) fn execute_weak_load(&mut self, dst: ValueId, weak_ref: ValueId) -> Result<ControlFlow, VMError> {
pub(crate) fn execute_weak_load(
&mut self,
dst: ValueId,
weak_ref: ValueId,
) -> Result<ControlFlow, VMError> {
let weak_value = self.get_value(weak_ref)?;
self.set_value(dst, weak_value);
Ok(ControlFlow::Continue)
}
pub(crate) fn execute_barrier_read(&mut self, dst: ValueId, value: ValueId) -> Result<ControlFlow, VMError> {
pub(crate) fn execute_barrier_read(
&mut self,
dst: ValueId,
value: ValueId,
) -> Result<ControlFlow, VMError> {
let val = self.get_value(value)?;
self.set_value(dst, val);
Ok(ControlFlow::Continue)
}
pub(crate) fn execute_barrier_write(&mut self, _value: ValueId) -> Result<ControlFlow, VMError> { Ok(ControlFlow::Continue) }
pub(crate) fn execute_barrier_write(
&mut self,
_value: ValueId,
) -> Result<ControlFlow, VMError> {
Ok(ControlFlow::Continue)
}
pub(crate) fn execute_throw(&mut self, exception: ValueId) -> Result<ControlFlow, VMError> {
let exc_value = self.get_value(exception)?;
Err(VMError::InvalidInstruction(format!("Exception thrown: {:?}", exc_value)))
Err(VMError::InvalidInstruction(format!(
"Exception thrown: {:?}",
exc_value
)))
}
pub(crate) fn execute_catch(&mut self, exception_value: ValueId) -> Result<ControlFlow, VMError> {
pub(crate) fn execute_catch(
&mut self,
exception_value: ValueId,
) -> Result<ControlFlow, VMError> {
self.set_value(exception_value, VMValue::Void);
Ok(ControlFlow::Continue)
}
// ---- Futures ----
pub(crate) fn execute_await(&mut self, dst: ValueId, future: ValueId) -> Result<ControlFlow, VMError> {
pub(crate) fn execute_await(
&mut self,
dst: ValueId,
future: ValueId,
) -> Result<ControlFlow, VMError> {
let future_val = self.get_value(future)?;
if let VMValue::Future(ref future_box) = future_val {
let max_ms: u64 = crate::config::env::await_max_ms();
@ -350,10 +621,14 @@ impl VM {
let mut spins = 0usize;
while !future_box.ready() {
self.runtime.gc.safepoint();
if let Some(s) = &self.runtime.scheduler { s.poll(); }
if let Some(s) = &self.runtime.scheduler {
s.poll();
}
std::thread::yield_now();
spins += 1;
if spins % 1024 == 0 { std::thread::sleep(std::time::Duration::from_millis(1)); }
if spins % 1024 == 0 {
std::thread::sleep(std::time::Duration::from_millis(1));
}
if start.elapsed() >= std::time::Duration::from_millis(max_ms) {
let err = Box::new(crate::box_trait::StringBox::new("Timeout"));
let rb = crate::boxes::result::NyashResultBox::new_err(err);
@ -367,6 +642,11 @@ impl VM {
let vm_value = VMValue::from_nyash_box(Box::new(ok));
self.set_value(dst, vm_value);
Ok(ControlFlow::Continue)
} else { Err(VMError::TypeError(format!("Expected Future, got {:?}", future_val))) }
} else {
Err(VMError::TypeError(format!(
"Expected Future, got {:?}",
future_val
)))
}
}
}

View File

@ -1,33 +1,60 @@
use crate::backend::vm::ControlFlow;
use crate::backend::{VMError, VMValue, VM};
use crate::box_trait::NyashBox;
use crate::mir::ValueId;
use crate::backend::vm::ControlFlow;
use crate::backend::{VM, VMError, VMValue};
impl VM {
/// Execute ExternCall instruction
pub(crate) fn execute_extern_call(&mut self, dst: Option<ValueId>, iface_name: &str, method_name: &str, args: &[ValueId]) -> Result<ControlFlow, VMError> {
pub(crate) fn execute_extern_call(
&mut self,
dst: Option<ValueId>,
iface_name: &str,
method_name: &str,
args: &[ValueId],
) -> Result<ControlFlow, VMError> {
// Core-13 pure shims: env.local.{get,set}, env.box.new
match (iface_name, method_name) {
("env.local", "get") => {
if args.len() != 1 { return Err(VMError::InvalidInstruction("env.local.get arity".into())); }
if args.len() != 1 {
return Err(VMError::InvalidInstruction("env.local.get arity".into()));
}
let ptr = args[0];
let v = self.get_value(ptr).unwrap_or(crate::backend::vm::VMValue::Void);
if let Some(d) = dst { self.set_value(d, v); }
let v = self
.get_value(ptr)
.unwrap_or(crate::backend::vm::VMValue::Void);
if let Some(d) = dst {
self.set_value(d, v);
}
return Ok(ControlFlow::Continue);
}
("env.local", "set") => {
if args.len() != 2 { return Err(VMError::InvalidInstruction("env.local.set arity".into())); }
if args.len() != 2 {
return Err(VMError::InvalidInstruction("env.local.set arity".into()));
}
let ptr = args[0];
let val = self.get_value(args[1])?;
self.set_value(ptr, val);
if let Some(d) = dst { self.set_value(d, crate::backend::vm::VMValue::Void); }
if let Some(d) = dst {
self.set_value(d, crate::backend::vm::VMValue::Void);
}
return Ok(ControlFlow::Continue);
}
("env.box", "new") => {
if args.is_empty() { return Err(VMError::InvalidInstruction("env.box.new requires type name".into())); }
if args.is_empty() {
return Err(VMError::InvalidInstruction(
"env.box.new requires type name".into(),
));
}
// first arg must be Const String type name
let ty = self.get_value(args[0])?;
let ty_name = match ty { crate::backend::vm::VMValue::String(s) => s, _ => return Err(VMError::InvalidInstruction("env.box.new first arg must be string".into())) };
let ty_name = match ty {
crate::backend::vm::VMValue::String(s) => s,
_ => {
return Err(VMError::InvalidInstruction(
"env.box.new first arg must be string".into(),
))
}
};
// remaining args as NyashBox
let mut ny_args: Vec<Box<dyn NyashBox>> = Vec::new();
for id in args.iter().skip(1) {
@ -36,8 +63,17 @@ impl VM {
}
let reg = crate::runtime::box_registry::get_global_registry();
match reg.create_box(&ty_name, &ny_args) {
Ok(b) => { if let Some(d) = dst { self.set_value(d, crate::backend::vm::VMValue::from_nyash_box(b)); } }
Err(e) => { return Err(VMError::InvalidInstruction(format!("env.box.new failed for {}: {}", ty_name, e))); }
Ok(b) => {
if let Some(d) = dst {
self.set_value(d, crate::backend::vm::VMValue::from_nyash_box(b));
}
}
Err(e) => {
return Err(VMError::InvalidInstruction(format!(
"env.box.new failed for {}: {}",
ty_name, e
)));
}
}
return Ok(ControlFlow::Continue);
}
@ -45,41 +81,90 @@ impl VM {
}
// Optional routing to name→slot handlers for stability and diagnostics
if crate::config::env::extern_route_slots() {
if let Some(slot) = crate::runtime::extern_registry::resolve_slot(iface_name, method_name) {
if let Some(slot) =
crate::runtime::extern_registry::resolve_slot(iface_name, method_name)
{
// Decode args to VMValue as needed by handlers below
let vm_args: Vec<VMValue> = args.iter().filter_map(|a| self.get_value(*a).ok()).collect();
let vm_args: Vec<VMValue> = args
.iter()
.filter_map(|a| self.get_value(*a).ok())
.collect();
match (iface_name, method_name, slot) {
("env.local", "get", 40) => {
if let Some(d) = dst { if let Some(a0) = args.get(0) { let v = self.get_value(*a0).unwrap_or(VMValue::Void); self.set_value(d, v); } }
if let Some(d) = dst {
if let Some(a0) = args.get(0) {
let v = self.get_value(*a0).unwrap_or(VMValue::Void);
self.set_value(d, v);
}
}
return Ok(ControlFlow::Continue);
}
("env.local", "set", 41) => {
if args.len() >= 2 { let ptr = args[0]; let val = vm_args.get(1).cloned().unwrap_or(VMValue::Void); self.set_value(ptr, val); }
if let Some(d) = dst { self.set_value(d, VMValue::Void); }
if args.len() >= 2 {
let ptr = args[0];
let val = vm_args.get(1).cloned().unwrap_or(VMValue::Void);
self.set_value(ptr, val);
}
if let Some(d) = dst {
self.set_value(d, VMValue::Void);
}
return Ok(ControlFlow::Continue);
}
("env.box", "new", 50) => {
if vm_args.is_empty() { return Err(VMError::InvalidInstruction("env.box.new requires type".into())); }
let ty = &vm_args[0]; let ty_name = match ty { VMValue::String(s) => s.clone(), _ => return Err(VMError::InvalidInstruction("env.box.new first arg must be string".into())) };
if vm_args.is_empty() {
return Err(VMError::InvalidInstruction(
"env.box.new requires type".into(),
));
}
let ty = &vm_args[0];
let ty_name = match ty {
VMValue::String(s) => s.clone(),
_ => {
return Err(VMError::InvalidInstruction(
"env.box.new first arg must be string".into(),
))
}
};
let mut ny_args: Vec<Box<dyn NyashBox>> = Vec::new();
for v in vm_args.iter().skip(1) { ny_args.push(v.to_nyash_box()); }
for v in vm_args.iter().skip(1) {
ny_args.push(v.to_nyash_box());
}
let reg = crate::runtime::box_registry::get_global_registry();
match reg.create_box(&ty_name, &ny_args) {
Ok(b) => { if let Some(d) = dst { self.set_value(d, VMValue::from_nyash_box(b)); } }
Err(e) => { return Err(VMError::InvalidInstruction(format!("env.box.new failed for {}: {}", ty_name, e))); }
Ok(b) => {
if let Some(d) = dst {
self.set_value(d, VMValue::from_nyash_box(b));
}
}
Err(e) => {
return Err(VMError::InvalidInstruction(format!(
"env.box.new failed for {}: {}",
ty_name, e
)));
}
}
return Ok(ControlFlow::Continue);
}
("env.console", m @ ("log" | "warn" | "error" | "println"), 10) => {
if let Some(a0) = vm_args.get(0) {
match m { "warn" => eprintln!("[warn] {}", a0.to_string()), "error" => eprintln!("[error] {}", a0.to_string()), _ => println!("{}", a0.to_string()), }
match m {
"warn" => eprintln!("[warn] {}", a0.to_string()),
"error" => eprintln!("[error] {}", a0.to_string()),
_ => println!("{}", a0.to_string()),
}
}
if let Some(d) = dst {
self.set_value(d, VMValue::Void);
}
if let Some(d) = dst { self.set_value(d, VMValue::Void); }
return Ok(ControlFlow::Continue);
}
("env.debug", "trace", 11) => {
if let Some(a0) = vm_args.get(0) { eprintln!("[trace] {}", a0.to_string()); }
if let Some(d) = dst { self.set_value(d, VMValue::Void); }
if let Some(a0) = vm_args.get(0) {
eprintln!("[trace] {}", a0.to_string());
}
if let Some(d) = dst {
self.set_value(d, VMValue::Void);
}
return Ok(ControlFlow::Continue);
}
("env.runtime", "checkpoint", 12) => {
@ -88,21 +173,35 @@ impl VM {
eprintln!("[rt] checkpoint @{} bb={} pc={}", func, bb, pc);
}
self.runtime.gc.safepoint();
if let Some(s) = &self.runtime.scheduler { s.poll(); }
if let Some(d) = dst { self.set_value(d, VMValue::Void); }
if let Some(s) = &self.runtime.scheduler {
s.poll();
}
if let Some(d) = dst {
self.set_value(d, VMValue::Void);
}
return Ok(ControlFlow::Continue);
}
("env.future", "new", 20) | ("env.future", "birth", 20) => {
// Create a new Future and optionally set initial value from arg0
let fut = crate::boxes::future::FutureBox::new();
if let Some(a0) = vm_args.get(0) { fut.set_result(a0.to_nyash_box()); }
if let Some(d) = dst { self.set_value(d, VMValue::Future(fut)); }
if let Some(a0) = vm_args.get(0) {
fut.set_result(a0.to_nyash_box());
}
if let Some(d) = dst {
self.set_value(d, VMValue::Future(fut));
}
return Ok(ControlFlow::Continue);
}
("env.future", "set", 21) => {
// set(future, value)
if vm_args.len() >= 2 { if let VMValue::Future(f) = &vm_args[0] { f.set_result(vm_args[1].to_nyash_box()); } }
if let Some(d) = dst { self.set_value(d, VMValue::Void); }
if vm_args.len() >= 2 {
if let VMValue::Future(f) = &vm_args[0] {
f.set_result(vm_args[1].to_nyash_box());
}
}
if let Some(d) = dst {
self.set_value(d, VMValue::Void);
}
return Ok(ControlFlow::Continue);
}
("env.future", "await", 22) => {
@ -112,21 +211,57 @@ impl VM {
let max_ms = crate::config::env::await_max_ms();
while !fb.ready() {
std::thread::yield_now();
if start.elapsed() >= std::time::Duration::from_millis(max_ms) { break; }
if start.elapsed() >= std::time::Duration::from_millis(max_ms) {
break;
}
}
let result = if fb.ready() { fb.get() } else { Box::new(crate::box_trait::StringBox::new("Timeout")) };
let result = if fb.ready() {
fb.get()
} else {
Box::new(crate::box_trait::StringBox::new("Timeout"))
};
let ok = crate::boxes::result::NyashResultBox::new_ok(result);
if let Some(d) = dst { self.set_value(d, VMValue::from_nyash_box(Box::new(ok))); }
} else if let Some(d) = dst { self.set_value(d, VMValue::Void); }
if let Some(d) = dst {
self.set_value(d, VMValue::from_nyash_box(Box::new(ok)));
}
} else if let Some(d) = dst {
self.set_value(d, VMValue::Void);
}
return Ok(ControlFlow::Continue);
}
("env.task", "cancelCurrent", 30) => {
if let Some(d) = dst {
self.set_value(d, VMValue::Void);
}
return Ok(ControlFlow::Continue);
}
("env.task", "currentToken", 31) => {
if let Some(d) = dst {
self.set_value(d, VMValue::Integer(0));
}
return Ok(ControlFlow::Continue);
}
("env.task", "yieldNow", 32) => {
std::thread::yield_now();
if let Some(d) = dst {
self.set_value(d, VMValue::Void);
}
return Ok(ControlFlow::Continue);
}
("env.task", "cancelCurrent", 30) => { if let Some(d) = dst { self.set_value(d, VMValue::Void); } return Ok(ControlFlow::Continue); }
("env.task", "currentToken", 31) => { if let Some(d) = dst { self.set_value(d, VMValue::Integer(0)); } return Ok(ControlFlow::Continue); }
("env.task", "yieldNow", 32) => { std::thread::yield_now(); if let Some(d) = dst { self.set_value(d, VMValue::Void); } return Ok(ControlFlow::Continue); }
("env.task", "sleepMs", 33) => {
let ms = vm_args.get(0).map(|v| match v { VMValue::Integer(i) => *i, _ => 0 }).unwrap_or(0);
if ms > 0 { std::thread::sleep(std::time::Duration::from_millis(ms as u64)); }
if let Some(d) = dst { self.set_value(d, VMValue::Void); }
let ms = vm_args
.get(0)
.map(|v| match v {
VMValue::Integer(i) => *i,
_ => 0,
})
.unwrap_or(0);
if ms > 0 {
std::thread::sleep(std::time::Duration::from_millis(ms as u64));
}
if let Some(d) = dst {
self.set_value(d, VMValue::Void);
}
return Ok(ControlFlow::Continue);
}
_ => { /* fallthrough to host */ }
@ -137,24 +272,38 @@ impl VM {
match (iface_name, method_name) {
("env.modules", "set") => {
// Expect two args
let vm_args: Vec<VMValue> = args.iter().filter_map(|a| self.get_value(*a).ok()).collect();
let vm_args: Vec<VMValue> = args
.iter()
.filter_map(|a| self.get_value(*a).ok())
.collect();
if vm_args.len() >= 2 {
let key = vm_args[0].to_string();
let val_box = vm_args[1].to_nyash_box();
crate::runtime::modules_registry::set(key, val_box);
}
if let Some(d) = dst { self.set_value(d, VMValue::Void); }
if let Some(d) = dst {
self.set_value(d, VMValue::Void);
}
return Ok(ControlFlow::Continue);
}
("env.modules", "get") => {
let vm_args: Vec<VMValue> = args.iter().filter_map(|a| self.get_value(*a).ok()).collect();
let vm_args: Vec<VMValue> = args
.iter()
.filter_map(|a| self.get_value(*a).ok())
.collect();
if let Some(k) = vm_args.get(0) {
let key = k.to_string();
if let Some(v) = crate::runtime::modules_registry::get(&key) {
if let Some(d) = dst { self.set_value(d, VMValue::from_nyash_box(v)); }
else { /* no dst */ }
} else if let Some(d) = dst { self.set_value(d, VMValue::Void); }
} else if let Some(d) = dst { self.set_value(d, VMValue::Void); }
if let Some(d) = dst {
self.set_value(d, VMValue::from_nyash_box(v));
} else { /* no dst */
}
} else if let Some(d) = dst {
self.set_value(d, VMValue::Void);
}
} else if let Some(d) = dst {
self.set_value(d, VMValue::Void);
}
return Ok(ControlFlow::Continue);
}
_ => {}
@ -164,7 +313,10 @@ impl VM {
// Name-route minimal registry even when slot routing is disabled
if iface_name == "env.modules" {
// Decode args as VMValue for convenience
let vm_args: Vec<VMValue> = args.iter().filter_map(|a| self.get_value(*a).ok()).collect();
let vm_args: Vec<VMValue> = args
.iter()
.filter_map(|a| self.get_value(*a).ok())
.collect();
match method_name {
"set" => {
if vm_args.len() >= 2 {
@ -172,16 +324,24 @@ impl VM {
let val_box = vm_args[1].to_nyash_box();
crate::runtime::modules_registry::set(key, val_box);
}
if let Some(d) = dst { self.set_value(d, VMValue::Void); }
if let Some(d) = dst {
self.set_value(d, VMValue::Void);
}
return Ok(ControlFlow::Continue);
}
"get" => {
if let Some(k) = vm_args.get(0) {
let key = k.to_string();
if let Some(v) = crate::runtime::modules_registry::get(&key) {
if let Some(d) = dst { self.set_value(d, VMValue::from_nyash_box(v)); }
} else if let Some(d) = dst { self.set_value(d, VMValue::Void); }
} else if let Some(d) = dst { self.set_value(d, VMValue::Void); }
if let Some(d) = dst {
self.set_value(d, VMValue::from_nyash_box(v));
}
} else if let Some(d) = dst {
self.set_value(d, VMValue::Void);
}
} else if let Some(d) = dst {
self.set_value(d, VMValue::Void);
}
return Ok(ControlFlow::Continue);
}
_ => {}
@ -190,30 +350,78 @@ impl VM {
// Evaluate arguments as NyashBox for loader
let mut nyash_args: Vec<Box<dyn NyashBox>> = Vec::new();
for arg_id in args { let arg_value = self.get_value(*arg_id)?; nyash_args.push(arg_value.to_nyash_box()); }
for arg_id in args {
let arg_value = self.get_value(*arg_id)?;
nyash_args.push(arg_value.to_nyash_box());
}
if crate::config::env::extern_trace() {
if let Some(slot) = crate::runtime::extern_registry::resolve_slot(iface_name, method_name) {
eprintln!("[EXT] call {}.{} slot={} argc={}", iface_name, method_name, slot, nyash_args.len());
} else { eprintln!("[EXT] call {}.{} argc={}", iface_name, method_name, nyash_args.len()); }
if let Some(slot) =
crate::runtime::extern_registry::resolve_slot(iface_name, method_name)
{
eprintln!(
"[EXT] call {}.{} slot={} argc={}",
iface_name,
method_name,
slot,
nyash_args.len()
);
} else {
eprintln!(
"[EXT] call {}.{} argc={}",
iface_name,
method_name,
nyash_args.len()
);
}
}
let host = crate::runtime::get_global_plugin_host();
let host = host.read().map_err(|_| VMError::InvalidInstruction("Plugin host lock poisoned".into()))?;
let host = host
.read()
.map_err(|_| VMError::InvalidInstruction("Plugin host lock poisoned".into()))?;
match host.extern_call(iface_name, method_name, &nyash_args) {
Ok(Some(result_box)) => { if let Some(dst_id) = dst { self.set_value(dst_id, VMValue::from_nyash_box(result_box)); } }
Ok(None) => { if let Some(dst_id) = dst { self.set_value(dst_id, VMValue::Void); } }
Ok(Some(result_box)) => {
if let Some(dst_id) = dst {
self.set_value(dst_id, VMValue::from_nyash_box(result_box));
}
}
Ok(None) => {
if let Some(dst_id) = dst {
self.set_value(dst_id, VMValue::Void);
}
}
Err(_) => {
let strict = crate::config::env::extern_strict() || crate::config::env::abi_strict();
let strict =
crate::config::env::extern_strict() || crate::config::env::abi_strict();
let mut msg = String::new();
if strict { msg.push_str("ExternCall STRICT: unregistered or unsupported call "); } else { msg.push_str("ExternCall failed: "); }
if strict {
msg.push_str("ExternCall STRICT: unregistered or unsupported call ");
} else {
msg.push_str("ExternCall failed: ");
}
msg.push_str(&format!("{}.{}", iface_name, method_name));
if let Err(detail) = crate::runtime::extern_registry::check_arity(iface_name, method_name, nyash_args.len()) { msg.push_str(&format!(" ({})", detail)); }
if let Some(spec) = crate::runtime::extern_registry::resolve(iface_name, method_name) {
msg.push_str(&format!(" (expected arity {}..{})", spec.min_arity, spec.max_arity));
if let Err(detail) = crate::runtime::extern_registry::check_arity(
iface_name,
method_name,
nyash_args.len(),
) {
msg.push_str(&format!(" ({})", detail));
}
if let Some(spec) =
crate::runtime::extern_registry::resolve(iface_name, method_name)
{
msg.push_str(&format!(
" (expected arity {}..{})",
spec.min_arity, spec.max_arity
));
} else {
let known = crate::runtime::extern_registry::known_for_iface(iface_name);
if !known.is_empty() { msg.push_str(&format!("; known methods: {}", known.join(", "))); }
else { let ifaces = crate::runtime::extern_registry::all_ifaces(); msg.push_str(&format!("; known interfaces: {}", ifaces.join(", "))); }
if !known.is_empty() {
msg.push_str(&format!("; known methods: {}", known.join(", ")));
} else {
let ifaces = crate::runtime::extern_registry::all_ifaces();
msg.push_str(&format!("; known interfaces: {}", ifaces.join(", ")));
}
}
return Err(VMError::InvalidInstruction(msg));
}

View File

@ -1,7 +1,7 @@
use crate::backend::vm::ControlFlow;
use crate::backend::{VMError, VMValue, VM};
use crate::mir::ValueId;
use std::sync::Arc;
use crate::backend::vm::ControlFlow;
use crate::backend::{VM, VMError, VMValue};
impl VM {
/// Execute FunctionNew instruction (construct a FunctionBox value)
@ -23,11 +23,14 @@ impl VM {
// Capture 'me' weakly if provided and is a BoxRef
if let Some(m) = me {
match self.get_value(*m)? {
VMValue::BoxRef(b) => { env.me_value = Some(Arc::downgrade(&b)); }
VMValue::BoxRef(b) => {
env.me_value = Some(Arc::downgrade(&b));
}
_ => {}
}
}
let fun = crate::boxes::function_box::FunctionBox::with_env(params.to_vec(), body.to_vec(), env);
let fun =
crate::boxes::function_box::FunctionBox::with_env(params.to_vec(), body.to_vec(), env);
self.set_value(dst, VMValue::BoxRef(Arc::new(fun)));
Ok(ControlFlow::Continue)
}

View File

@ -5,11 +5,10 @@
* behavior and public APIs. Methods remain as `impl VM` across submodules.
*/
pub mod core; // const/binop/unary/compare/print/ctrl/type/phi/mem/array/refs/weak/barriers/exn/await
pub mod call; // execute_call (Function name or FunctionBox)
pub mod newbox; // execute_newbox
pub mod function_new; // execute_function_new
pub mod extern_call; // execute_extern_call
pub mod boxcall; // execute_boxcall + vtable stub
pub mod plugin_invoke; // execute_plugin_invoke + helpers
pub mod boxcall; // execute_boxcall + vtable stub
pub mod call; // execute_call (Function name or FunctionBox)
pub mod core; // const/binop/unary/compare/print/ctrl/type/phi/mem/array/refs/weak/barriers/exn/await
pub mod extern_call; // execute_extern_call
pub mod function_new; // execute_function_new
pub mod newbox; // execute_newbox
pub mod plugin_invoke; // execute_plugin_invoke + helpers

View File

@ -1,14 +1,20 @@
use crate::backend::vm::ControlFlow;
use crate::backend::{VMError, VMValue, VM};
use crate::box_trait::NyashBox;
use crate::mir::ValueId;
use std::sync::Arc;
use crate::backend::vm::ControlFlow;
use crate::backend::{VM, VMError, VMValue};
impl VM {
/// Execute NewBox instruction
pub(crate) fn execute_newbox(&mut self, dst: ValueId, box_type: &str, args: &[ValueId]) -> Result<ControlFlow, VMError> {
pub(crate) fn execute_newbox(
&mut self,
dst: ValueId,
box_type: &str,
args: &[ValueId],
) -> Result<ControlFlow, VMError> {
// Convert args to NyashBox values
let arg_values: Vec<Box<dyn NyashBox>> = args.iter()
let arg_values: Vec<Box<dyn NyashBox>> = args
.iter()
.map(|arg| {
let val = self.get_value(*arg)?;
Ok(val.to_nyash_box())
@ -17,15 +23,20 @@ impl VM {
// Create new box using runtime's registry
let new_box = {
let registry = self.runtime.box_registry.lock()
.map_err(|_| VMError::InvalidInstruction("Failed to lock box registry".to_string()))?;
registry.create_box(box_type, &arg_values)
.map_err(|e| VMError::InvalidInstruction(format!("Failed to create {}: {}", box_type, e)))?
let registry = self.runtime.box_registry.lock().map_err(|_| {
VMError::InvalidInstruction("Failed to lock box registry".to_string())
})?;
registry.create_box(box_type, &arg_values).map_err(|e| {
VMError::InvalidInstruction(format!("Failed to create {}: {}", box_type, e))
})?
};
// 80/20: Basic boxes are stored as primitives in VMValue for simpler ops
if box_type == "IntegerBox" {
if let Some(ib) = new_box.as_any().downcast_ref::<crate::box_trait::IntegerBox>() {
if let Some(ib) = new_box
.as_any()
.downcast_ref::<crate::box_trait::IntegerBox>()
{
self.set_value(dst, VMValue::Integer(ib.value));
return Ok(ControlFlow::Continue);
}
@ -35,7 +46,10 @@ impl VM {
return Ok(ControlFlow::Continue);
}
} else if box_type == "StringBox" {
if let Some(sb) = new_box.as_any().downcast_ref::<crate::box_trait::StringBox>() {
if let Some(sb) = new_box
.as_any()
.downcast_ref::<crate::box_trait::StringBox>()
{
self.set_value(dst, VMValue::String(sb.value.clone()));
return Ok(ControlFlow::Continue);
}

View File

@ -1,79 +1,186 @@
use crate::mir::ValueId;
use crate::backend::vm::ControlFlow;
use crate::backend::{VM, VMError, VMValue};
use crate::backend::{VMError, VMValue, VM};
use crate::mir::ValueId;
impl VM {
/// Execute a forced plugin invocation (no builtin fallback)
pub(crate) fn execute_plugin_invoke(&mut self, dst: Option<ValueId>, box_val: ValueId, method: &str, args: &[ValueId]) -> Result<ControlFlow, VMError> {
pub(crate) fn execute_plugin_invoke(
&mut self,
dst: Option<ValueId>,
box_val: ValueId,
method: &str,
args: &[ValueId],
) -> Result<ControlFlow, VMError> {
// Helper: extract UTF-8 string from internal StringBox, Result.Ok(String-like), or plugin StringBox via toUtf8
fn extract_string_from_box(bx: &dyn crate::box_trait::NyashBox) -> Option<String> {
if let Some(sb) = bx.as_any().downcast_ref::<crate::box_trait::StringBox>() { return Some(sb.value.clone()); }
if let Some(res) = bx.as_any().downcast_ref::<crate::boxes::result::NyashResultBox>() {
if let crate::boxes::result::NyashResultBox::Ok(inner) = res { return extract_string_from_box(inner.as_ref()); }
if let Some(sb) = bx.as_any().downcast_ref::<crate::box_trait::StringBox>() {
return Some(sb.value.clone());
}
if let Some(p) = bx.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>() {
if let Some(res) = bx
.as_any()
.downcast_ref::<crate::boxes::result::NyashResultBox>()
{
if let crate::boxes::result::NyashResultBox::Ok(inner) = res {
return extract_string_from_box(inner.as_ref());
}
}
if let Some(p) = bx
.as_any()
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
{
if p.box_type == "StringBox" {
let host = crate::runtime::get_global_plugin_host();
let tmp: Option<String> = if let Ok(ro) = host.read() {
if let Ok(val_opt) = ro.invoke_instance_method("StringBox", "toUtf8", p.inner.instance_id, &[]) {
if let Some(vb) = val_opt { if let Some(sb2) = vb.as_any().downcast_ref::<crate::box_trait::StringBox>() { Some(sb2.value.clone()) } else { None } } else { None }
} else { None }
} else { None };
if tmp.is_some() { return tmp; }
if let Ok(val_opt) = ro.invoke_instance_method(
"StringBox",
"toUtf8",
p.inner.instance_id,
&[],
) {
if let Some(vb) = val_opt {
if let Some(sb2) =
vb.as_any().downcast_ref::<crate::box_trait::StringBox>()
{
Some(sb2.value.clone())
} else {
None
}
} else {
None
}
} else {
None
}
} else {
None
};
if tmp.is_some() {
return tmp;
}
}
}
None
}
let recv = self.get_value(box_val)?;
if method == "birth" && !matches!(recv, VMValue::BoxRef(ref b) if b.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>().is_some()) {
if method == "birth"
&& !matches!(recv, VMValue::BoxRef(ref b) if b.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>().is_some())
{
eprintln!("[VM PluginInvoke] static birth fallback recv={:?}", recv);
let mut created: Option<VMValue> = None;
match &recv {
VMValue::String(s) => {
let host = crate::runtime::get_global_plugin_host();
let host = host.read().unwrap();
let sb: Box<dyn crate::box_trait::NyashBox> = Box::new(crate::box_trait::StringBox::new(s.clone()));
if let Ok(b) = host.create_box("StringBox", &[sb]) { created = Some(VMValue::from_nyash_box(b)); }
let sb: Box<dyn crate::box_trait::NyashBox> =
Box::new(crate::box_trait::StringBox::new(s.clone()));
if let Ok(b) = host.create_box("StringBox", &[sb]) {
created = Some(VMValue::from_nyash_box(b));
}
}
VMValue::Integer(_n) => {
let host = crate::runtime::get_global_plugin_host();
let host = host.read().unwrap();
if let Ok(b) = host.create_box("IntegerBox", &[]) { created = Some(VMValue::from_nyash_box(b)); }
if let Ok(b) = host.create_box("IntegerBox", &[]) {
created = Some(VMValue::from_nyash_box(b));
}
}
_ => {}
}
if let Some(val) = created { if let Some(dst_id) = dst { self.set_value(dst_id, val); } return Ok(ControlFlow::Continue); }
if let Some(val) = created {
if let Some(dst_id) = dst {
self.set_value(dst_id, val);
}
return Ok(ControlFlow::Continue);
}
}
if let VMValue::BoxRef(pbox) = &recv {
if let Some(p) = pbox.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>() {
if let Some(p) = pbox
.as_any()
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
{
let host = crate::runtime::get_global_plugin_host();
let host = host.read().unwrap();
let mh = host.resolve_method(&p.box_type, method).map_err(|_| VMError::InvalidInstruction(format!("Plugin method not found: {}.{}", p.box_type, method)))?;
let mut tlv = crate::runtime::plugin_ffi_common::encode_tlv_header(args.len() as u16);
let mh = host.resolve_method(&p.box_type, method).map_err(|_| {
VMError::InvalidInstruction(format!(
"Plugin method not found: {}.{}",
p.box_type, method
))
})?;
let mut tlv =
crate::runtime::plugin_ffi_common::encode_tlv_header(args.len() as u16);
for (idx, a) in args.iter().enumerate() {
let v = self.get_value(*a)?;
match v {
VMValue::Integer(n) => { if std::env::var("NYASH_DEBUG_PLUGIN").ok().as_deref() == Some("1") { eprintln!("[VM→Plugin][vm] arg[{}] encode I64 {}", idx, n); } crate::runtime::plugin_ffi_common::encode::i64(&mut tlv, n) }
VMValue::Float(x) => { if std::env::var("NYASH_DEBUG_PLUGIN").ok().as_deref() == Some("1") { eprintln!("[VM→Plugin][vm] arg[{}] encode F64 {}", idx, x); } crate::runtime::plugin_ffi_common::encode::f64(&mut tlv, x) }
VMValue::Bool(b) => crate::runtime::plugin_ffi_common::encode::bool(&mut tlv, b),
VMValue::String(ref s) => crate::runtime::plugin_ffi_common::encode::string(&mut tlv, s),
VMValue::Integer(n) => {
if std::env::var("NYASH_DEBUG_PLUGIN").ok().as_deref() == Some("1") {
eprintln!("[VM→Plugin][vm] arg[{}] encode I64 {}", idx, n);
}
crate::runtime::plugin_ffi_common::encode::i64(&mut tlv, n)
}
VMValue::Float(x) => {
if std::env::var("NYASH_DEBUG_PLUGIN").ok().as_deref() == Some("1") {
eprintln!("[VM→Plugin][vm] arg[{}] encode F64 {}", idx, x);
}
crate::runtime::plugin_ffi_common::encode::f64(&mut tlv, x)
}
VMValue::Bool(b) => {
crate::runtime::plugin_ffi_common::encode::bool(&mut tlv, b)
}
VMValue::String(ref s) => {
crate::runtime::plugin_ffi_common::encode::string(&mut tlv, s)
}
VMValue::BoxRef(ref b) => {
if let Some(h) = b.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>() {
if let Some(h) = b
.as_any()
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
{
if h.box_type == "StringBox" {
let host = crate::runtime::get_global_plugin_host();
let host = host.read().unwrap();
if let Ok(val_opt) = host.invoke_instance_method("StringBox", "toUtf8", h.inner.instance_id, &[]) {
if let Some(sb) = val_opt.and_then(|bx| bx.as_any().downcast_ref::<crate::box_trait::StringBox>().map(|s| s.value.clone())) { crate::runtime::plugin_ffi_common::encode::string(&mut tlv, &sb); continue; }
if let Ok(val_opt) = host.invoke_instance_method(
"StringBox",
"toUtf8",
h.inner.instance_id,
&[],
) {
if let Some(sb) = val_opt.and_then(|bx| {
bx.as_any()
.downcast_ref::<crate::box_trait::StringBox>()
.map(|s| s.value.clone())
}) {
crate::runtime::plugin_ffi_common::encode::string(
&mut tlv, &sb,
);
continue;
}
}
} else if h.box_type == "IntegerBox" {
let host = crate::runtime::get_global_plugin_host();
let host = host.read().unwrap();
if let Ok(val_opt) = host.invoke_instance_method("IntegerBox", "get", h.inner.instance_id, &[]) {
if let Some(ib) = val_opt.and_then(|bx| bx.as_any().downcast_ref::<crate::box_trait::IntegerBox>().map(|i| i.value)) { crate::runtime::plugin_ffi_common::encode::i64(&mut tlv, ib); continue; }
if let Ok(val_opt) = host.invoke_instance_method(
"IntegerBox",
"get",
h.inner.instance_id,
&[],
) {
if let Some(ib) = val_opt.and_then(|bx| {
bx.as_any()
.downcast_ref::<crate::box_trait::IntegerBox>()
.map(|i| i.value)
}) {
crate::runtime::plugin_ffi_common::encode::i64(
&mut tlv, ib,
);
continue;
}
}
}
crate::runtime::plugin_ffi_common::encode::plugin_handle(&mut tlv, h.inner.type_id, h.inner.instance_id);
crate::runtime::plugin_ffi_common::encode::plugin_handle(
&mut tlv,
h.inner.type_id,
h.inner.instance_id,
);
} else {
let h = crate::runtime::host_handles::to_handle_arc(b.clone());
crate::runtime::plugin_ffi_common::encode::host_handle(&mut tlv, h);
@ -84,28 +191,63 @@ impl VM {
}
let mut out = vec![0u8; 32768];
let mut out_len: usize = out.len();
unsafe { (p.inner.invoke_fn)(p.inner.type_id, mh.method_id as u32, p.inner.instance_id, tlv.as_ptr(), tlv.len(), out.as_mut_ptr(), &mut out_len) };
let vm_out_raw: VMValue = if let Some((tag, _sz, payload)) = crate::runtime::plugin_ffi_common::decode::tlv_first(&out[..out_len]) {
unsafe {
(p.inner.invoke_fn)(
p.inner.type_id,
mh.method_id as u32,
p.inner.instance_id,
tlv.as_ptr(),
tlv.len(),
out.as_mut_ptr(),
&mut out_len,
)
};
let vm_out_raw: VMValue = if let Some((tag, _sz, payload)) =
crate::runtime::plugin_ffi_common::decode::tlv_first(&out[..out_len])
{
if std::env::var("NYASH_DEBUG_PLUGIN").ok().as_deref() == Some("1") {
eprintln!("[VM←Plugin] tag={} size={} bytes={}", tag, _sz, payload.len());
eprintln!(
"[VM←Plugin] tag={} size={} bytes={}",
tag,
_sz,
payload.len()
);
}
match tag {
1 => crate::runtime::plugin_ffi_common::decode::bool(payload).map(VMValue::Bool).unwrap_or(VMValue::Void),
1 => crate::runtime::plugin_ffi_common::decode::bool(payload)
.map(VMValue::Bool)
.unwrap_or(VMValue::Void),
2 => {
let v = crate::runtime::plugin_ffi_common::decode::i32(payload).unwrap_or_default();
if std::env::var("NYASH_DEBUG_PLUGIN").ok().as_deref() == Some("1") { eprintln!("[VM←Plugin] decode i32={}", v); }
let v = crate::runtime::plugin_ffi_common::decode::i32(payload)
.unwrap_or_default();
if std::env::var("NYASH_DEBUG_PLUGIN").ok().as_deref() == Some("1") {
eprintln!("[VM←Plugin] decode i32={}", v);
}
VMValue::Integer(v as i64)
},
5 => crate::runtime::plugin_ffi_common::decode::f64(payload).map(VMValue::Float).unwrap_or(VMValue::Void),
6 | 7 => VMValue::String(crate::runtime::plugin_ffi_common::decode::string(payload)),
}
5 => crate::runtime::plugin_ffi_common::decode::f64(payload)
.map(VMValue::Float)
.unwrap_or(VMValue::Void),
6 | 7 => VMValue::String(
crate::runtime::plugin_ffi_common::decode::string(payload),
),
8 => {
if let Some(u) = crate::runtime::plugin_ffi_common::decode::u64(payload) {
if let Some(arc) = crate::runtime::host_handles::get(u) { VMValue::BoxRef(arc) } else { VMValue::Void }
} else { VMValue::Void }
if let Some(u) = crate::runtime::plugin_ffi_common::decode::u64(payload)
{
if let Some(arc) = crate::runtime::host_handles::get(u) {
VMValue::BoxRef(arc)
} else {
VMValue::Void
}
} else {
VMValue::Void
}
}
_ => VMValue::Void,
}
} else { VMValue::Void };
} else {
VMValue::Void
};
// Wrap into Result.Ok when method is declared returns_result
let vm_out = {
let host = crate::runtime::get_global_plugin_host();
@ -119,13 +261,19 @@ impl VM {
VMValue::String(s) => Box::new(crate::box_trait::StringBox::new(s)),
VMValue::BoxRef(b) => b.share_box(),
VMValue::Void => Box::new(crate::box_trait::VoidBox::new()),
_ => Box::new(crate::box_trait::StringBox::new(vm_out_raw.to_string()))
_ => Box::new(crate::box_trait::StringBox::new(vm_out_raw.to_string())),
};
let res = crate::boxes::result::NyashResultBox::new_ok(boxed);
VMValue::BoxRef(std::sync::Arc::from(Box::new(res) as Box<dyn crate::box_trait::NyashBox>))
} else { vm_out_raw }
VMValue::BoxRef(std::sync::Arc::from(
Box::new(res) as Box<dyn crate::box_trait::NyashBox>
))
} else {
vm_out_raw
}
};
if let Some(dst_id) = dst { self.set_value(dst_id, vm_out); }
if let Some(dst_id) = dst {
self.set_value(dst_id, vm_out);
}
return Ok(ControlFlow::Continue);
}
}
@ -133,28 +281,66 @@ impl VM {
if let VMValue::BoxRef(ref bx) = recv {
if let Some(s) = extract_string_from_box(bx.as_ref()) {
match method {
"length" => { if let Some(dst_id) = dst { self.set_value(dst_id, VMValue::Integer(s.len() as i64)); } return Ok(ControlFlow::Continue); }
"is_empty" | "isEmpty" => { if let Some(dst_id) = dst { self.set_value(dst_id, VMValue::Bool(s.is_empty())); } return Ok(ControlFlow::Continue); }
"length" => {
if let Some(dst_id) = dst {
self.set_value(dst_id, VMValue::Integer(s.len() as i64));
}
return Ok(ControlFlow::Continue);
}
"is_empty" | "isEmpty" => {
if let Some(dst_id) = dst {
self.set_value(dst_id, VMValue::Bool(s.is_empty()));
}
return Ok(ControlFlow::Continue);
}
"charCodeAt" => {
let idx_v = if let Some(a0) = args.get(0) { self.get_value(*a0)? } else { VMValue::Integer(0) };
let idx = match idx_v { VMValue::Integer(i) => i.max(0) as usize, _ => 0 };
let idx_v = if let Some(a0) = args.get(0) {
self.get_value(*a0)?
} else {
VMValue::Integer(0)
};
let idx = match idx_v {
VMValue::Integer(i) => i.max(0) as usize,
_ => 0,
};
let code = s.chars().nth(idx).map(|c| c as u32 as i64).unwrap_or(0);
if let Some(dst_id) = dst { self.set_value(dst_id, VMValue::Integer(code)); }
if let Some(dst_id) = dst {
self.set_value(dst_id, VMValue::Integer(code));
}
return Ok(ControlFlow::Continue);
}
"concat" => {
let rhs_v = if let Some(a0) = args.get(0) { self.get_value(*a0)? } else { VMValue::String(String::new()) };
let rhs_s = match rhs_v { VMValue::String(ss) => ss, VMValue::BoxRef(br) => extract_string_from_box(br.as_ref()).unwrap_or_else(|| br.to_string_box().value), _ => rhs_v.to_string(), };
let rhs_v = if let Some(a0) = args.get(0) {
self.get_value(*a0)?
} else {
VMValue::String(String::new())
};
let rhs_s = match rhs_v {
VMValue::String(ss) => ss,
VMValue::BoxRef(br) => extract_string_from_box(br.as_ref())
.unwrap_or_else(|| br.to_string_box().value),
_ => rhs_v.to_string(),
};
let mut new_s = s.clone();
new_s.push_str(&rhs_s);
let out = Box::new(crate::box_trait::StringBox::new(new_s));
if let Some(dst_id) = dst { self.set_value(dst_id, VMValue::BoxRef(std::sync::Arc::from(out as Box<dyn crate::box_trait::NyashBox>))); }
if let Some(dst_id) = dst {
self.set_value(
dst_id,
VMValue::BoxRef(std::sync::Arc::from(
out as Box<dyn crate::box_trait::NyashBox>,
)),
);
}
return Ok(ControlFlow::Continue);
}
_ => {}
}
}
}
Err(VMError::InvalidInstruction(format!("PluginInvoke requires PluginBox receiver; method={} got {:?}", method, recv)))
Err(VMError::InvalidInstruction(format!(
"PluginInvoke requires PluginBox receiver; method={} got {:?}",
method, recv
)))
}
}

View File

@ -6,8 +6,8 @@
* the existing VM API surface.
*/
use super::vm::{VMError, VM};
use crate::box_trait::NyashBox;
use super::vm::{VM, VMError};
impl VM {
/// Unified method dispatch entry. Currently delegates to `call_box_method_impl`.
@ -31,4 +31,3 @@ impl VM {
self.call_box_method_impl(box_value, method, args)
}
}

View File

@ -7,7 +7,7 @@
* Typical Callers: VM 実行ループ(ブロック遷移/phi評価
*/
use super::vm::{VMValue, VMError};
use super::vm::{VMError, VMValue};
use crate::mir::{BasicBlockId, ValueId};
use std::collections::HashMap;
@ -15,7 +15,7 @@ use std::collections::HashMap;
pub struct PhiHandler {
/// 現在のブロックに到達する前のブロック
previous_block: Option<BasicBlockId>,
/// Phi nodeの値キャッシュ最適化用
phi_cache: HashMap<ValueId, VMValue>,
}
@ -28,7 +28,7 @@ impl PhiHandler {
phi_cache: HashMap::new(),
}
}
/// ブロック遷移を記録
pub fn record_block_transition(&mut self, from: BasicBlockId, to: BasicBlockId) {
self.previous_block = Some(from);
@ -37,13 +37,13 @@ impl PhiHandler {
self.phi_cache.clear();
}
}
/// 初期ブロックへのエントリを記録
pub fn record_entry(&mut self) {
self.previous_block = None;
self.phi_cache.clear();
}
/// Phi命令を実行
pub fn execute_phi(
&mut self,
@ -55,16 +55,16 @@ impl PhiHandler {
// if let Some(cached) = self.phi_cache.get(&dst) {
// return Ok(cached.clone());
// }
// Phi nodeの入力を選択
let selected_value = self.select_phi_input(inputs, get_value_fn)?;
// キャッシュに保存(デバッグ用に残すが使わない)
// self.phi_cache.insert(dst, selected_value.clone());
Ok(selected_value)
}
/// Phi nodeの適切な入力を選択
fn select_phi_input(
&self,
@ -72,9 +72,11 @@ impl PhiHandler {
get_value_fn: impl Fn(ValueId) -> Result<VMValue, VMError>,
) -> Result<VMValue, VMError> {
if inputs.is_empty() {
return Err(VMError::InvalidInstruction("Phi node has no inputs".to_string()));
return Err(VMError::InvalidInstruction(
"Phi node has no inputs".to_string(),
));
}
// previous_blockに基づいて入力を選択
if let Some(prev_block) = self.previous_block {
// 対応するブロックからの入力を探す
@ -84,16 +86,16 @@ impl PhiHandler {
return Ok(value);
}
}
// フォールバック:見つからない場合は最初の入力を使用
// これは通常起こらないはずだが、安全のため
}
// previous_blockがない場合エントリポイントは最初の入力を使用
let (_, value_id) = &inputs[0];
get_value_fn(*value_id)
}
/// ループヘッダーかどうかを判定(簡易版)
fn is_loop_header(&self, _block_id: BasicBlockId) -> bool {
// TODO: MIR情報からループヘッダーを判定する機能を追加
@ -106,7 +108,7 @@ impl PhiHandler {
pub struct LoopExecutor {
/// Phiハンドラー
phi_handler: PhiHandler,
/// ループイテレーション数(デバッグ用)
iteration_count: HashMap<BasicBlockId, usize>,
}
@ -119,23 +121,24 @@ impl LoopExecutor {
iteration_count: HashMap::new(),
}
}
/// ブロック遷移を記録
pub fn record_transition(&mut self, from: BasicBlockId, to: BasicBlockId) {
self.phi_handler.record_block_transition(from, to);
// ループイテレーション数を更新(デバッグ用)
if from > to { // 単純なバックエッジ検出
if from > to {
// 単純なバックエッジ検出
*self.iteration_count.entry(to).or_insert(0) += 1;
}
}
/// エントリポイントでの初期化
pub fn initialize(&mut self) {
self.phi_handler.record_entry();
self.iteration_count.clear();
}
/// Phi命令を実行
pub fn execute_phi(
&mut self,
@ -145,25 +148,25 @@ impl LoopExecutor {
) -> Result<VMValue, VMError> {
self.phi_handler.execute_phi(dst, inputs, get_value_fn)
}
/// デバッグ情報を取得
pub fn debug_info(&self) -> String {
let mut info = String::new();
info.push_str("Loop Executor Debug Info:\n");
if let Some(prev) = self.phi_handler.previous_block {
info.push_str(&format!(" Previous block: {:?}\n", prev));
} else {
info.push_str(" Previous block: None (entry)\n");
}
if !self.iteration_count.is_empty() {
info.push_str(" Loop iterations:\n");
for (block, count) in &self.iteration_count {
info.push_str(&format!(" Block {:?}: {} iterations\n", block, count));
}
}
info
}
}
@ -171,46 +174,38 @@ impl LoopExecutor {
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_phi_selection() {
let mut handler = PhiHandler::new();
// テスト用の値
let inputs = vec![
(BasicBlockId::new(0), ValueId::new(1)), // エントリブロックからの初期値
(BasicBlockId::new(2), ValueId::new(2)), // ループボディからの更新値
(BasicBlockId::new(0), ValueId::new(1)), // エントリブロックからの初期値
(BasicBlockId::new(2), ValueId::new(2)), // ループボディからの更新値
];
// エントリポイントからの実行
handler.record_entry();
let result = handler.execute_phi(
ValueId::new(3),
&inputs,
|id| {
if id == ValueId::new(1) {
Ok(VMValue::Integer(0))
} else {
Ok(VMValue::Integer(10))
}
let result = handler.execute_phi(ValueId::new(3), &inputs, |id| {
if id == ValueId::new(1) {
Ok(VMValue::Integer(0))
} else {
Ok(VMValue::Integer(10))
}
);
});
assert_eq!(result.unwrap(), VMValue::Integer(0));
// ループボディからの実行
handler.record_block_transition(BasicBlockId::new(2), BasicBlockId::new(1));
handler.phi_cache.clear(); // テスト用にキャッシュクリア
let result = handler.execute_phi(
ValueId::new(3),
&inputs,
|id| {
if id == ValueId::new(1) {
Ok(VMValue::Integer(0))
} else {
Ok(VMValue::Integer(10))
}
handler.phi_cache.clear(); // テスト用にキャッシュクリア
let result = handler.execute_phi(ValueId::new(3), &inputs, |id| {
if id == ValueId::new(1) {
Ok(VMValue::Integer(0))
} else {
Ok(VMValue::Integer(10))
}
);
});
assert_eq!(result.unwrap(), VMValue::Integer(10));
}
}

View File

@ -5,9 +5,9 @@
* phi selection delegation, and small utilities that support the exec loop.
*/
use super::vm::{VM, VMError, VMValue};
use super::vm_phi::LoopExecutor;
use super::frame::ExecutionFrame;
use super::vm::{VMError, VMValue, VM};
use super::vm_phi::LoopExecutor;
use crate::mir::{BasicBlockId, ValueId};
use crate::runtime::NyashRuntime;
use crate::scope_tracker::ScopeTracker;
@ -30,7 +30,9 @@ impl VM {
inputs: &[(BasicBlockId, ValueId)],
) -> Result<VMValue, VMError> {
if inputs.is_empty() {
return Err(VMError::InvalidInstruction("Phi node has no inputs".to_string()));
return Err(VMError::InvalidInstruction(
"Phi node has no inputs".to_string(),
));
}
let debug_phi = std::env::var("NYASH_VM_DEBUG").ok().as_deref() == Some("1")
|| std::env::var("NYASH_VM_DEBUG_PHI").ok().as_deref() == Some("1");
@ -50,7 +52,10 @@ impl VM {
Err(VMError::InvalidValue(format!("Value {} not set", val_id)))
}
} else {
Err(VMError::InvalidValue(format!("Value {} out of bounds", val_id)))
Err(VMError::InvalidValue(format!(
"Value {} out of bounds",
val_id
)))
}
});
if debug_phi {
@ -88,7 +93,9 @@ impl VM {
boxcall_vtable_funcname: std::collections::HashMap::new(),
type_versions: std::collections::HashMap::new(),
#[cfg(not(feature = "jit-direct-only"))]
jit_manager: Some(crate::jit::manager::JitManager::new(Self::jit_threshold_from_env())),
jit_manager: Some(crate::jit::manager::JitManager::new(
Self::jit_threshold_from_env(),
)),
#[cfg(feature = "jit-direct-only")]
jit_manager: None,
}
@ -120,7 +127,9 @@ impl VM {
boxcall_vtable_funcname: std::collections::HashMap::new(),
type_versions: std::collections::HashMap::new(),
#[cfg(not(feature = "jit-direct-only"))]
jit_manager: Some(crate::jit::manager::JitManager::new(Self::jit_threshold_from_env())),
jit_manager: Some(crate::jit::manager::JitManager::new(
Self::jit_threshold_from_env(),
)),
#[cfg(feature = "jit-direct-only")]
jit_manager: None,
}
@ -136,7 +145,10 @@ impl VM {
Err(VMError::InvalidValue(format!("Value {} not set", value_id)))
}
} else {
Err(VMError::InvalidValue(format!("Value {} out of bounds", value_id)))
Err(VMError::InvalidValue(format!(
"Value {} out of bounds",
value_id
)))
}
}

View File

@ -12,25 +12,45 @@ use super::vm::VM;
impl VM {
/// Print simple VM execution statistics when enabled via env var
pub(super) fn maybe_print_stats(&mut self) {
let enabled = std::env::var("NYASH_VM_STATS").ok().map(|v| v != "0").unwrap_or(false);
if !enabled { return; }
let enabled = std::env::var("NYASH_VM_STATS")
.ok()
.map(|v| v != "0")
.unwrap_or(false);
if !enabled {
return;
}
let elapsed_ms = self.exec_start.map(|t| t.elapsed().as_secs_f64() * 1000.0).unwrap_or(0.0);
let mut items: Vec<(&str, usize)> = self.instr_counter.iter().map(|(k,v)| (*k, *v)).collect();
items.sort_by(|a,b| b.1.cmp(&a.1).then_with(|| a.0.cmp(&b.0)));
let total: usize = items.iter().map(|(_,v)| *v).sum();
let elapsed_ms = self
.exec_start
.map(|t| t.elapsed().as_secs_f64() * 1000.0)
.unwrap_or(0.0);
let mut items: Vec<(&str, usize)> =
self.instr_counter.iter().map(|(k, v)| (*k, *v)).collect();
items.sort_by(|a, b| b.1.cmp(&a.1).then_with(|| a.0.cmp(&b.0)));
let total: usize = items.iter().map(|(_, v)| *v).sum();
let json_enabled = std::env::var("NYASH_VM_STATS_JSON").ok().map(|v| v != "0").unwrap_or(false)
|| std::env::var("NYASH_VM_STATS_FORMAT").map(|v| v == "json").unwrap_or(false);
let json_enabled = std::env::var("NYASH_VM_STATS_JSON")
.ok()
.map(|v| v != "0")
.unwrap_or(false)
|| std::env::var("NYASH_VM_STATS_FORMAT")
.map(|v| v == "json")
.unwrap_or(false);
if json_enabled {
let counts_obj: std::collections::BTreeMap<&str, usize> = self.instr_counter.iter().map(|(k,v)| (*k, *v)).collect();
let top20: Vec<_> = items.iter().take(20).map(|(op,cnt)| {
serde_json::json!({ "op": op, "count": cnt })
}).collect();
let counts_obj: std::collections::BTreeMap<&str, usize> =
self.instr_counter.iter().map(|(k, v)| (*k, *v)).collect();
let top20: Vec<_> = items
.iter()
.take(20)
.map(|(op, cnt)| serde_json::json!({ "op": op, "count": cnt }))
.collect();
let now_ms = {
use std::time::{SystemTime, UNIX_EPOCH};
SystemTime::now().duration_since(UNIX_EPOCH).map(|d| d.as_millis() as u64).unwrap_or(0)
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_millis() as u64)
.unwrap_or(0)
};
let payload = serde_json::json!({
"total": total,
@ -44,7 +64,10 @@ impl VM {
Err(_) => println!("{{\"total\":{},\"elapsed_ms\":{:.3}}}", total, elapsed_ms),
}
} else {
println!("\n🧮 VM Stats: {} instructions in {:.3} ms", total, elapsed_ms);
println!(
"\n🧮 VM Stats: {} instructions in {:.3} ms",
total, elapsed_ms
);
for (k, v) in items.into_iter().take(20) {
println!(" {:>10}: {:>8}", k, v);
}
@ -55,9 +78,14 @@ impl VM {
pub(super) fn maybe_print_jit_unified_stats(&self) {
// Show when either JIT stats requested or VM stats are on
let jit_enabled = std::env::var("NYASH_JIT_STATS").ok().as_deref() == Some("1");
let vm_enabled = std::env::var("NYASH_VM_STATS").ok().map(|v| v != "0").unwrap_or(false);
let vm_enabled = std::env::var("NYASH_VM_STATS")
.ok()
.map(|v| v != "0")
.unwrap_or(false);
let jit_json = std::env::var("NYASH_JIT_STATS_JSON").ok().as_deref() == Some("1");
if !jit_enabled && !vm_enabled { return; }
if !jit_enabled && !vm_enabled {
return;
}
if let Some(jm) = &self.jit_manager {
// Gather basic counters
let sites = jm.sites();
@ -66,11 +94,19 @@ impl VM {
let ok = jm.exec_ok_count();
let tr = jm.exec_trap_count();
let total_exec = ok + tr;
let fb_rate = if total_exec > 0 { (tr as f64) / (total_exec as f64) } else { 0.0 };
let fb_rate = if total_exec > 0 {
(tr as f64) / (total_exec as f64)
} else {
0.0
};
let handles = crate::jit::rt::handles::len();
let cfg = crate::jit::config::current();
let caps = crate::jit::config::probe_capabilities();
let abi_mode = if cfg.native_bool_abi && caps.supports_b1_sig { "b1_bool" } else { "i64_bool" };
let abi_mode = if cfg.native_bool_abi && caps.supports_b1_sig {
"b1_bool"
} else {
"i64_bool"
};
let b1_norm = crate::jit::rt::b1_norm_get();
let ret_b1_hints = crate::jit::rt::ret_bool_hint_get();
if jit_json {
@ -99,11 +135,17 @@ impl VM {
})
}).collect::<Vec<_>>()
});
println!("{}", serde_json::to_string_pretty(&payload).unwrap_or_else(|_| String::from("{}")));
println!(
"{}",
serde_json::to_string_pretty(&payload).unwrap_or_else(|_| String::from("{}"))
);
} else {
eprintln!("[JIT] summary: sites={} compiled={} hits={} exec_ok={} trap={} fallback_rate={:.2} handles={}",
sites, compiled, total_hits, ok, tr, fb_rate, handles);
eprintln!(" abi_mode={} b1_norm_count={} ret_bool_hint_count={}", abi_mode, b1_norm, ret_b1_hints);
eprintln!(
" abi_mode={} b1_norm_count={} ret_bool_hint_count={}",
abi_mode, b1_norm, ret_b1_hints
);
}
}
}

View File

@ -5,7 +5,7 @@
* Kept separate to thin vm.rs and allow reuse across helpers.
*/
use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox, VoidBox};
use crate::box_trait::{BoolBox, IntegerBox, NyashBox, StringBox, VoidBox};
use crate::mir::ConstValue;
use std::sync::Arc;
@ -94,7 +94,10 @@ impl VMValue {
pub fn as_integer(&self) -> Result<i64, VMError> {
match self {
VMValue::Integer(i) => Ok(*i),
_ => Err(VMError::TypeError(format!("Expected integer, got {:?}", self))),
_ => Err(VMError::TypeError(format!(
"Expected integer, got {:?}",
self
))),
}
}
@ -105,11 +108,25 @@ impl VMValue {
VMValue::Integer(i) => Ok(*i != 0),
// Pragmatic coercions for dynamic boxes (preserve legacy semantics)
VMValue::BoxRef(b) => {
if let Some(bb) = b.as_any().downcast_ref::<BoolBox>() { return Ok(bb.value); }
if let Some(ib) = b.as_any().downcast_ref::<IntegerBox>() { return Ok(ib.value != 0); }
if let Some(ib) = b.as_any().downcast_ref::<crate::boxes::integer_box::IntegerBox>() { return Ok(ib.value != 0); }
if b.as_any().downcast_ref::<VoidBox>().is_some() { return Ok(false); }
Err(VMError::TypeError(format!("Expected bool, got BoxRef({})", b.type_name())))
if let Some(bb) = b.as_any().downcast_ref::<BoolBox>() {
return Ok(bb.value);
}
if let Some(ib) = b.as_any().downcast_ref::<IntegerBox>() {
return Ok(ib.value != 0);
}
if let Some(ib) = b
.as_any()
.downcast_ref::<crate::boxes::integer_box::IntegerBox>()
{
return Ok(ib.value != 0);
}
if b.as_any().downcast_ref::<VoidBox>().is_some() {
return Ok(false);
}
Err(VMError::TypeError(format!(
"Expected bool, got BoxRef({})",
b.type_name()
)))
}
VMValue::Void => Ok(false),
VMValue::Float(f) => Ok(*f != 0.0),
@ -120,7 +137,11 @@ impl VMValue {
/// Convert from NyashBox to VMValue
pub fn from_nyash_box(nyash_box: Box<dyn crate::box_trait::NyashBox>) -> VMValue {
if nyash_box.as_any().downcast_ref::<crate::boxes::null_box::NullBox>().is_some() {
if nyash_box
.as_any()
.downcast_ref::<crate::boxes::null_box::NullBox>()
.is_some()
{
// Treat NullBox as Void in VMValue to align with `null` literal semantics
VMValue::Void
} else if let Some(int_box) = nyash_box.as_any().downcast_ref::<IntegerBox>() {
@ -129,7 +150,10 @@ impl VMValue {
VMValue::Bool(bool_box.value)
} else if let Some(string_box) = nyash_box.as_any().downcast_ref::<StringBox>() {
VMValue::String(string_box.value.clone())
} else if let Some(future_box) = nyash_box.as_any().downcast_ref::<crate::boxes::future::FutureBox>() {
} else if let Some(future_box) = nyash_box
.as_any()
.downcast_ref::<crate::boxes::future::FutureBox>()
{
VMValue::Future(future_box.clone())
} else {
VMValue::BoxRef(Arc::from(nyash_box))

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +1,14 @@
/*!
* WASM Code Generation - Core MIR to WASM instruction conversion
*
*
* Phase 8.2 PoC1: Basic operations (arithmetic, control flow, print)
* Phase 8.3 PoC2: Reference operations (RefNew/RefGet/RefSet)
*/
use crate::mir::{MirModule, MirFunction, MirInstruction, ConstValue, BinaryOp, CompareOp, ValueId, BasicBlockId};
use super::{WasmError, MemoryManager, RuntimeImports};
use super::{MemoryManager, RuntimeImports, WasmError};
use crate::mir::{
BasicBlockId, BinaryOp, CompareOp, ConstValue, MirFunction, MirInstruction, MirModule, ValueId,
};
use std::collections::HashMap;
/// WASM module representation for WAT generation
@ -30,42 +32,42 @@ impl WasmModule {
exports: Vec::new(),
}
}
/// Generate WAT text format
pub fn to_wat(&self) -> String {
let mut wat = String::new();
wat.push_str("(module\n");
// Add imports first (must come before other definitions in WASM)
for import in &self.imports {
wat.push_str(&format!(" {}\n", import));
}
// Add memory declaration
// Add memory declaration
if !self.memory.is_empty() {
wat.push_str(&format!(" {}\n", self.memory));
}
// Add data segments (must come after memory)
for data_segment in &self.data_segments {
wat.push_str(&format!(" {}\n", data_segment));
}
// Add globals
for global in &self.globals {
wat.push_str(&format!(" {}\n", global));
}
// Add functions
for function in &self.functions {
wat.push_str(&format!(" {}\n", function));
}
// Add exports
for export in &self.exports {
wat.push_str(&format!(" {}\n", export));
}
wat.push_str(")\n");
wat
}
@ -90,72 +92,87 @@ impl WasmCodegen {
next_data_offset: 0x1000, // Start data after initial heap space
}
}
/// Generate WASM module from MIR module
pub fn generate_module(
&mut self,
mir_module: MirModule,
memory_manager: &MemoryManager,
runtime: &RuntimeImports
&mut self,
mir_module: MirModule,
memory_manager: &MemoryManager,
runtime: &RuntimeImports,
) -> Result<WasmModule, WasmError> {
let mut wasm_module = WasmModule::new();
// Add memory declaration (64KB initial)
wasm_module.memory = "(memory (export \"memory\") 1)".to_string();
// Add runtime imports (env.print for debugging)
wasm_module.imports.extend(runtime.get_imports());
// Add globals (heap pointer)
wasm_module.globals.extend(memory_manager.get_globals());
// Add memory management functions
wasm_module.functions.push(memory_manager.get_malloc_function());
wasm_module.functions.push(memory_manager.get_generic_box_alloc_function());
wasm_module
.functions
.push(memory_manager.get_malloc_function());
wasm_module
.functions
.push(memory_manager.get_generic_box_alloc_function());
// Add Box-specific allocation functions for known types
for box_type in ["StringBox", "IntegerBox", "BoolBox", "DataBox"] {
if let Ok(alloc_func) = memory_manager.get_box_alloc_function(box_type) {
wasm_module.functions.push(alloc_func);
}
}
// Generate functions
for (name, function) in &mir_module.functions {
let wasm_function = self.generate_function(name, function.clone())?;
wasm_module.functions.push(wasm_function);
}
// Add string literal data segments
wasm_module.data_segments.extend(self.generate_data_segments());
wasm_module
.data_segments
.extend(self.generate_data_segments());
// Add main function export if it exists
if mir_module.functions.contains_key("main") {
wasm_module.exports.push("(export \"main\" (func $main))".to_string());
wasm_module
.exports
.push("(export \"main\" (func $main))".to_string());
}
Ok(wasm_module)
}
/// Generate WASM function from MIR function
fn generate_function(&mut self, name: &str, mir_function: MirFunction) -> Result<String, WasmError> {
fn generate_function(
&mut self,
name: &str,
mir_function: MirFunction,
) -> Result<String, WasmError> {
// Reset local variable tracking for this function
self.current_locals.clear();
self.next_local_index = 0;
let mut function_body = String::new();
function_body.push_str(&format!("(func ${}", name));
// Add return type if not void
match mir_function.signature.return_type {
crate::mir::MirType::Integer => function_body.push_str(" (result i32)"),
crate::mir::MirType::Bool => function_body.push_str(" (result i32)"),
crate::mir::MirType::Void => {}, // No return type
_ => return Err(WasmError::UnsupportedInstruction(
format!("Unsupported return type: {:?}", mir_function.signature.return_type)
)),
crate::mir::MirType::Void => {} // No return type
_ => {
return Err(WasmError::UnsupportedInstruction(format!(
"Unsupported return type: {:?}",
mir_function.signature.return_type
)))
}
}
// Collect all local variables needed
let local_count = self.count_locals(&mir_function)?;
if local_count > 0 {
@ -164,23 +181,24 @@ impl WasmCodegen {
function_body.push_str(&format!(" (local ${} i32)", i));
}
}
function_body.push('\n');
// Generate body from entry block
let entry_instructions = self.generate_basic_block(&mir_function, mir_function.entry_block)?;
let entry_instructions =
self.generate_basic_block(&mir_function, mir_function.entry_block)?;
for instruction in entry_instructions {
function_body.push_str(&format!(" {}\n", instruction));
}
function_body.push_str(" )");
Ok(function_body)
}
/// Count local variables needed for the function
fn count_locals(&mut self, mir_function: &MirFunction) -> Result<u32, WasmError> {
let mut max_value_id = 0;
for (_, block) in &mir_function.blocks {
for instruction in &block.instructions {
if let Some(value_id) = instruction.dst_value() {
@ -191,63 +209,65 @@ impl WasmCodegen {
}
}
}
// Assign local indices to value IDs
for i in 0..=max_value_id {
let value_id = ValueId::new(i);
self.current_locals.insert(value_id, self.next_local_index);
self.next_local_index += 1;
}
Ok(self.next_local_index)
}
/// Generate WASM instructions for a basic block
fn generate_basic_block(&mut self, mir_function: &MirFunction, block_id: BasicBlockId) -> Result<Vec<String>, WasmError> {
let block = mir_function.blocks.get(&block_id)
.ok_or_else(|| WasmError::CodegenError(format!("Basic block {:?} not found", block_id)))?;
fn generate_basic_block(
&mut self,
mir_function: &MirFunction,
block_id: BasicBlockId,
) -> Result<Vec<String>, WasmError> {
let block = mir_function.blocks.get(&block_id).ok_or_else(|| {
WasmError::CodegenError(format!("Basic block {:?} not found", block_id))
})?;
let mut instructions = Vec::new();
// Process regular instructions
for mir_instruction in &block.instructions {
let wasm_instructions = self.generate_instruction(mir_instruction)?;
instructions.extend(wasm_instructions);
}
// Process terminator instruction
if let Some(ref terminator) = block.terminator {
let wasm_instructions = self.generate_instruction(terminator)?;
instructions.extend(wasm_instructions);
}
Ok(instructions)
}
/// Generate WASM instructions for a single MIR instruction
fn generate_instruction(&mut self, instruction: &MirInstruction) -> Result<Vec<String>, WasmError> {
fn generate_instruction(
&mut self,
instruction: &MirInstruction,
) -> Result<Vec<String>, WasmError> {
match instruction {
// Phase 8.2 PoC1: Basic operations
MirInstruction::Const { dst, value } => {
self.generate_const(*dst, value)
},
MirInstruction::Const { dst, value } => self.generate_const(*dst, value),
MirInstruction::BinOp { dst, op, lhs, rhs } => {
self.generate_binop(*dst, *op, *lhs, *rhs)
},
}
MirInstruction::Compare { dst, op, lhs, rhs } => {
self.generate_compare(*dst, *op, *lhs, *rhs)
},
MirInstruction::Return { value } => {
self.generate_return(value.as_ref())
},
MirInstruction::Print { value, .. } => {
self.generate_print(*value)
},
}
MirInstruction::Return { value } => self.generate_return(value.as_ref()),
MirInstruction::Print { value, .. } => self.generate_print(*value),
// Phase 8.3 PoC2: Reference operations
MirInstruction::RefNew { dst, box_val } => {
// Create a new reference to a Box by copying the Box value
@ -256,9 +276,13 @@ impl WasmCodegen {
format!("local.get ${}", self.get_local_index(*box_val)?),
format!("local.set ${}", self.get_local_index(*dst)?),
])
},
MirInstruction::RefGet { dst, reference, field: _ } => {
}
MirInstruction::RefGet {
dst,
reference,
field: _,
} => {
// Load field value from Box through reference
// reference contains Box pointer, field is the field name
// For now, assume all fields are at offset 12 (first field after header)
@ -270,10 +294,14 @@ impl WasmCodegen {
"i32.load".to_string(),
format!("local.set ${}", self.get_local_index(*dst)?),
])
},
MirInstruction::RefSet { reference, field: _, value } => {
// Store field value to Box through reference
}
MirInstruction::RefSet {
reference,
field: _,
value,
} => {
// Store field value to Box through reference
// reference contains Box pointer, field is the field name, value is new value
// For now, assume all fields are at offset 12 (first field after header)
// TODO: Add proper field offset calculation
@ -284,9 +312,13 @@ impl WasmCodegen {
format!("local.get ${}", self.get_local_index(*value)?),
"i32.store".to_string(),
])
},
MirInstruction::NewBox { dst, box_type, args } => {
}
MirInstruction::NewBox {
dst,
box_type,
args,
} => {
// Create a new Box using the generic allocator
match box_type.as_str() {
"DataBox" => {
@ -295,7 +327,7 @@ impl WasmCodegen {
"call $alloc_databox".to_string(),
format!("local.set ${}", self.get_local_index(*dst)?),
];
// Initialize fields with arguments if provided
for (i, arg) in args.iter().enumerate() {
instructions.extend(vec![
@ -306,9 +338,9 @@ impl WasmCodegen {
"i32.store".to_string(),
]);
}
Ok(instructions)
},
}
_ => {
// Use generic allocator for unknown types
// This is a fallback - in a real implementation, all Box types should be known
@ -320,45 +352,53 @@ impl WasmCodegen {
])
}
}
},
}
// Phase 8.4 PoC3: Extension stubs
MirInstruction::WeakNew { dst, box_val } |
MirInstruction::FutureNew { dst, value: box_val } => {
MirInstruction::WeakNew { dst, box_val }
| MirInstruction::FutureNew {
dst,
value: box_val,
} => {
// Treat as regular reference for now
Ok(vec![
format!("local.get ${}", self.get_local_index(*box_val)?),
format!("local.set ${}", self.get_local_index(*dst)?),
])
},
MirInstruction::WeakLoad { dst, weak_ref } |
MirInstruction::Await { dst, future: weak_ref } => {
}
MirInstruction::WeakLoad { dst, weak_ref }
| MirInstruction::Await {
dst,
future: weak_ref,
} => {
// Always succeed for now
Ok(vec![
format!("local.get ${}", self.get_local_index(*weak_ref)?),
format!("local.set ${}", self.get_local_index(*dst)?),
])
},
MirInstruction::BarrierRead { .. } |
MirInstruction::BarrierWrite { .. } |
MirInstruction::FutureSet { .. } |
MirInstruction::Safepoint => {
}
MirInstruction::BarrierRead { .. }
| MirInstruction::BarrierWrite { .. }
| MirInstruction::FutureSet { .. }
| MirInstruction::Safepoint => {
// No-op for now
Ok(vec!["nop".to_string()])
},
}
// Control Flow Instructions (Critical for loops and conditions)
MirInstruction::Jump { target } => {
// Unconditional jump to target basic block
// Use WASM br instruction to break to the target block
Ok(vec![
format!("br $block_{}", target.as_u32()),
])
},
MirInstruction::Branch { condition, then_bb, else_bb } => {
Ok(vec![format!("br $block_{}", target.as_u32())])
}
MirInstruction::Branch {
condition,
then_bb,
else_bb,
} => {
// Conditional branch based on condition value
// Load condition value and branch accordingly
Ok(vec![
@ -369,54 +409,73 @@ impl WasmCodegen {
// Otherwise, fall through to else_bb
format!("br $block_{}", else_bb.as_u32()),
])
},
}
// Phase 9.7: External Function Calls
MirInstruction::ExternCall { dst, iface_name, method_name, args, effects: _ } => {
MirInstruction::ExternCall {
dst,
iface_name,
method_name,
args,
effects: _,
} => {
// Generate call to external function import
let call_target = match (iface_name.as_str(), method_name.as_str()) {
("env.console", "log") => "console_log",
("env.canvas", "fillRect") => "canvas_fillRect",
("env.canvas", "fillRect") => "canvas_fillRect",
("env.canvas", "fillText") => "canvas_fillText",
_ => return Err(WasmError::UnsupportedInstruction(
format!("Unsupported extern call: {}.{}", iface_name, method_name)
)),
_ => {
return Err(WasmError::UnsupportedInstruction(format!(
"Unsupported extern call: {}.{}",
iface_name, method_name
)))
}
};
let mut instructions = Vec::new();
// Load all arguments onto stack in order
for arg in args {
instructions.push(format!("local.get ${}", self.get_local_index(*arg)?));
}
// Call the external function
instructions.push(format!("call ${}", call_target));
// Store result if destination is provided
if let Some(dst) = dst {
// For void functions, we still need to provide a dummy value
instructions.push("i32.const 0".to_string()); // Void result
instructions.push(format!("local.set ${}", self.get_local_index(*dst)?));
}
Ok(instructions)
},
}
// BoxCall codegen - critical Box method calls
MirInstruction::BoxCall { dst, box_val, method, args, effects: _ , .. } => {
self.generate_box_call(*dst, *box_val, method, args)
},
MirInstruction::BoxCall {
dst,
box_val,
method,
args,
effects: _,
..
} => self.generate_box_call(*dst, *box_val, method, args),
// Unsupported instructions
_ => Err(WasmError::UnsupportedInstruction(
format!("Instruction not yet supported: {:?}", instruction)
)),
_ => Err(WasmError::UnsupportedInstruction(format!(
"Instruction not yet supported: {:?}",
instruction
))),
}
}
/// Generate constant loading
fn generate_const(&mut self, dst: ValueId, value: &ConstValue) -> Result<Vec<String>, WasmError> {
fn generate_const(
&mut self,
dst: ValueId,
value: &ConstValue,
) -> Result<Vec<String>, WasmError> {
let const_instruction = match value {
ConstValue::Integer(n) => format!("i32.const {}", n),
ConstValue::Bool(b) => format!("i32.const {}", if *b { 1 } else { 0 }),
@ -425,36 +484,48 @@ impl WasmCodegen {
// Register the string literal and get its offset
let data_offset = self.register_string_literal(s);
let string_len = s.len() as u32;
// Generate code to allocate a StringBox and return its pointer
// This is more complex and will need StringBox allocation
return self.generate_string_box_const(dst, data_offset, string_len);
},
_ => return Err(WasmError::UnsupportedInstruction(
format!("Unsupported constant type: {:?}", value)
)),
}
_ => {
return Err(WasmError::UnsupportedInstruction(format!(
"Unsupported constant type: {:?}",
value
)))
}
};
Ok(vec![
const_instruction,
format!("local.set ${}", self.get_local_index(dst)?),
])
}
/// Generate binary operation
fn generate_binop(&self, dst: ValueId, op: BinaryOp, lhs: ValueId, rhs: ValueId) -> Result<Vec<String>, WasmError> {
fn generate_binop(
&self,
dst: ValueId,
op: BinaryOp,
lhs: ValueId,
rhs: ValueId,
) -> Result<Vec<String>, WasmError> {
let wasm_op = match op {
BinaryOp::Add => "i32.add",
BinaryOp::Sub => "i32.sub",
BinaryOp::Sub => "i32.sub",
BinaryOp::Mul => "i32.mul",
BinaryOp::Div => "i32.div_s",
BinaryOp::And => "i32.and",
BinaryOp::Or => "i32.or",
_ => return Err(WasmError::UnsupportedInstruction(
format!("Unsupported binary operation: {:?}", op)
)),
_ => {
return Err(WasmError::UnsupportedInstruction(format!(
"Unsupported binary operation: {:?}",
op
)))
}
};
Ok(vec![
format!("local.get ${}", self.get_local_index(lhs)?),
format!("local.get ${}", self.get_local_index(rhs)?),
@ -462,9 +533,15 @@ impl WasmCodegen {
format!("local.set ${}", self.get_local_index(dst)?),
])
}
/// Generate comparison operation
fn generate_compare(&self, dst: ValueId, op: CompareOp, lhs: ValueId, rhs: ValueId) -> Result<Vec<String>, WasmError> {
fn generate_compare(
&self,
dst: ValueId,
op: CompareOp,
lhs: ValueId,
rhs: ValueId,
) -> Result<Vec<String>, WasmError> {
let wasm_op = match op {
CompareOp::Eq => "i32.eq",
CompareOp::Ne => "i32.ne",
@ -473,7 +550,7 @@ impl WasmCodegen {
CompareOp::Gt => "i32.gt_s",
CompareOp::Ge => "i32.ge_s",
};
Ok(vec![
format!("local.get ${}", self.get_local_index(lhs)?),
format!("local.get ${}", self.get_local_index(rhs)?),
@ -481,7 +558,7 @@ impl WasmCodegen {
format!("local.set ${}", self.get_local_index(dst)?),
])
}
/// Generate return instruction
fn generate_return(&self, value: Option<&ValueId>) -> Result<Vec<String>, WasmError> {
if let Some(value_id) = value {
@ -493,9 +570,14 @@ impl WasmCodegen {
Ok(vec!["return".to_string()])
}
}
/// Generate StringBox allocation for a string constant
fn generate_string_box_const(&self, dst: ValueId, data_offset: u32, string_len: u32) -> Result<Vec<String>, WasmError> {
fn generate_string_box_const(
&self,
dst: ValueId,
data_offset: u32,
string_len: u32,
) -> Result<Vec<String>, WasmError> {
// Allocate a StringBox using the StringBox allocator
// StringBox layout: [type_id:0x1001][ref_count:1][field_count:2][data_ptr:offset][length:len]
Ok(vec![
@ -503,7 +585,6 @@ impl WasmCodegen {
"call $alloc_stringbox".to_string(),
// Store the result (StringBox pointer) in local variable
format!("local.set ${}", self.get_local_index(dst)?),
// Initialize StringBox fields
// Get StringBox pointer back
format!("local.get ${}", self.get_local_index(dst)?),
@ -512,8 +593,7 @@ impl WasmCodegen {
"i32.add".to_string(),
format!("i32.const {}", data_offset),
"i32.store".to_string(),
// Get StringBox pointer again
// Get StringBox pointer again
format!("local.get ${}", self.get_local_index(dst)?),
// Set length field (offset 16 from StringBox pointer)
"i32.const 16".to_string(),
@ -522,7 +602,7 @@ impl WasmCodegen {
"i32.store".to_string(),
])
}
/// Generate print instruction (calls env.print import)
fn generate_print(&self, value: ValueId) -> Result<Vec<String>, WasmError> {
Ok(vec![
@ -530,89 +610,112 @@ impl WasmCodegen {
"call $print".to_string(),
])
}
/// Register a string literal and return its data offset
fn register_string_literal(&mut self, string: &str) -> u32 {
if let Some(&offset) = self.string_literals.get(string) {
return offset;
}
let offset = self.next_data_offset;
let string_bytes = string.as_bytes();
self.string_literals.insert(string.to_string(), offset);
self.next_data_offset += string_bytes.len() as u32;
offset
}
/// Generate data segments for all registered string literals
fn generate_data_segments(&self) -> Vec<String> {
let mut segments = Vec::new();
for (string, &offset) in &self.string_literals {
let string_bytes = string.as_bytes();
// Convert to hex-escaped string for WAT
let byte_string = string_bytes.iter()
let byte_string = string_bytes
.iter()
.map(|b| format!("\\{:02x}", b))
.collect::<String>();
let data_segment = format!(
"(data (i32.const {}) \"{}\")",
offset,
byte_string
);
let data_segment = format!("(data (i32.const {}) \"{}\")", offset, byte_string);
segments.push(data_segment);
}
segments
}
/// Get WASM local variable index for ValueId
fn get_local_index(&self, value_id: ValueId) -> Result<u32, WasmError> {
self.current_locals.get(&value_id)
.copied()
.ok_or_else(|| WasmError::CodegenError(format!("Local variable not found for ValueId: {:?}", value_id)))
self.current_locals.get(&value_id).copied().ok_or_else(|| {
WasmError::CodegenError(format!(
"Local variable not found for ValueId: {:?}",
value_id
))
})
}
/// Generate BoxCall method invocation
/// Implements critical Box methods: toString, print, equals, clone
fn generate_box_call(&mut self, dst: Option<ValueId>, box_val: ValueId, method: &str, args: &[ValueId]) -> Result<Vec<String>, WasmError> {
fn generate_box_call(
&mut self,
dst: Option<ValueId>,
box_val: ValueId,
method: &str,
args: &[ValueId],
) -> Result<Vec<String>, WasmError> {
match method {
"toString" => self.generate_to_string_call(dst, box_val),
"print" => self.generate_print_call(dst, box_val),
"equals" => self.generate_equals_call(dst, box_val, args),
"clone" => self.generate_clone_call(dst, box_val),
"log" => self.generate_log_call(dst, box_val, args),
_ => Err(WasmError::UnsupportedInstruction(
format!("Unsupported BoxCall method: {}", method)
))
_ => Err(WasmError::UnsupportedInstruction(format!(
"Unsupported BoxCall method: {}",
method
))),
}
}
/// Generate toString() method call - Box → String conversion
fn generate_to_string_call(&mut self, dst: Option<ValueId>, box_val: ValueId) -> Result<Vec<String>, WasmError> {
fn generate_to_string_call(
&mut self,
dst: Option<ValueId>,
box_val: ValueId,
) -> Result<Vec<String>, WasmError> {
let Some(dst) = dst else {
return Err(WasmError::CodegenError("toString() requires destination".to_string()));
return Err(WasmError::CodegenError(
"toString() requires destination".to_string(),
));
};
Ok(vec![
format!(";; toString() implementation for ValueId({})", box_val.as_u32()),
format!(
";; toString() implementation for ValueId({})",
box_val.as_u32()
),
format!("local.get ${}", self.get_local_index(box_val)?),
"call $box_to_string".to_string(),
format!("local.set ${}", self.get_local_index(dst)?),
])
}
/// Generate print() method call - Basic output
fn generate_print_call(&mut self, dst: Option<ValueId>, box_val: ValueId) -> Result<Vec<String>, WasmError> {
fn generate_print_call(
&mut self,
dst: Option<ValueId>,
box_val: ValueId,
) -> Result<Vec<String>, WasmError> {
let mut instructions = vec![
format!(";; print() implementation for ValueId({})", box_val.as_u32()),
format!(
";; print() implementation for ValueId({})",
box_val.as_u32()
),
format!("local.get ${}", self.get_local_index(box_val)?),
"call $box_print".to_string(),
];
// Store void result if destination is provided
if let Some(dst) = dst {
instructions.extend(vec![
@ -620,62 +723,89 @@ impl WasmCodegen {
format!("local.set ${}", self.get_local_index(dst)?),
]);
}
Ok(instructions)
}
/// Generate equals() method call - Box comparison
fn generate_equals_call(&mut self, dst: Option<ValueId>, box_val: ValueId, args: &[ValueId]) -> Result<Vec<String>, WasmError> {
fn generate_equals_call(
&mut self,
dst: Option<ValueId>,
box_val: ValueId,
args: &[ValueId],
) -> Result<Vec<String>, WasmError> {
let Some(dst) = dst else {
return Err(WasmError::CodegenError("equals() requires destination".to_string()));
};
if args.len() != 1 {
return Err(WasmError::CodegenError(
format!("equals() expects 1 argument, got {}", args.len())
"equals() requires destination".to_string(),
));
};
if args.len() != 1 {
return Err(WasmError::CodegenError(format!(
"equals() expects 1 argument, got {}",
args.len()
)));
}
Ok(vec![
format!(";; equals() implementation for ValueId({}) == ValueId({})", box_val.as_u32(), args[0].as_u32()),
format!(
";; equals() implementation for ValueId({}) == ValueId({})",
box_val.as_u32(),
args[0].as_u32()
),
format!("local.get ${}", self.get_local_index(box_val)?),
format!("local.get ${}", self.get_local_index(args[0])?),
"call $box_equals".to_string(),
format!("local.set ${}", self.get_local_index(dst)?),
])
}
/// Generate clone() method call - Box duplication
fn generate_clone_call(&mut self, dst: Option<ValueId>, box_val: ValueId) -> Result<Vec<String>, WasmError> {
fn generate_clone_call(
&mut self,
dst: Option<ValueId>,
box_val: ValueId,
) -> Result<Vec<String>, WasmError> {
let Some(dst) = dst else {
return Err(WasmError::CodegenError("clone() requires destination".to_string()));
return Err(WasmError::CodegenError(
"clone() requires destination".to_string(),
));
};
Ok(vec![
format!(";; clone() implementation for ValueId({})", box_val.as_u32()),
format!(
";; clone() implementation for ValueId({})",
box_val.as_u32()
),
format!("local.get ${}", self.get_local_index(box_val)?),
"call $box_clone".to_string(),
format!("local.set ${}", self.get_local_index(dst)?),
])
}
/// Generate log() method call - Console logging (ConsoleBox.log)
fn generate_log_call(&mut self, dst: Option<ValueId>, box_val: ValueId, args: &[ValueId]) -> Result<Vec<String>, WasmError> {
let mut instructions = vec![
format!(";; log() implementation for ValueId({})", box_val.as_u32()),
];
fn generate_log_call(
&mut self,
dst: Option<ValueId>,
box_val: ValueId,
args: &[ValueId],
) -> Result<Vec<String>, WasmError> {
let mut instructions = vec![format!(
";; log() implementation for ValueId({})",
box_val.as_u32()
)];
// Load box_val (ConsoleBox instance)
instructions.push(format!("local.get ${}", self.get_local_index(box_val)?));
// Load all arguments
for arg in args {
instructions.push(format!("local.get ${}", self.get_local_index(*arg)?));
}
// Call console log function
instructions.push("call $console_log".to_string());
// Store void result if destination is provided
if let Some(dst) = dst {
instructions.extend(vec![
@ -683,7 +813,7 @@ impl WasmCodegen {
format!("local.set ${}", self.get_local_index(dst)?),
]);
}
Ok(instructions)
}
}
@ -691,25 +821,30 @@ impl WasmCodegen {
#[cfg(test)]
mod tests {
use super::*;
use crate::mir::{MirModule, MirFunction, FunctionSignature, MirType, EffectMask, BasicBlock, BasicBlockId, ValueId};
use crate::mir::{
BasicBlock, BasicBlockId, EffectMask, FunctionSignature, MirFunction, MirModule, MirType,
ValueId,
};
#[test]
fn test_wasm_module_wat_generation() {
let mut module = WasmModule::new();
module.memory = "(memory (export \"memory\") 1)".to_string();
module.imports.push("(import \"env\" \"print\" (func $print (param i32)))".to_string());
module
.imports
.push("(import \"env\" \"print\" (func $print (param i32)))".to_string());
let wat = module.to_wat();
assert!(wat.contains("(module"));
assert!(wat.contains("memory"));
assert!(wat.contains("import"));
}
#[test]
fn test_constant_generation() {
let mut codegen = WasmCodegen::new();
let dst = ValueId::new(0);
let result = codegen.generate_const(dst, &ConstValue::Integer(42));
assert!(result.is_err()); // Should fail without local mapping
}

View File

@ -1,6 +1,6 @@
/*!
* WASM Memory Management - Box layout and heap allocation
*
*
* Phase 8.3 PoC2: Implements bump allocator and Box memory layout
* Memory Layout: 0x000-0x3FF (reserved), 0x400-0x7FF (stack), 0x800+ (heap)
*/
@ -24,26 +24,26 @@ impl BoxLayout {
"IntegerBox" => 0x1002,
"BoolBox" => 0x1003,
"ArrayBox" => 0x1004,
"DataBox" => 0x1005, // For testing
"DataBox" => 0x1005, // For testing
_ => {
// Generate ID from hash for custom types
type_name.chars().map(|c| c as u32).sum::<u32>() % 65536 + 0x2000
}
};
Self {
type_id,
size: 12, // Header: type_id + ref_count + field_count
field_offsets: HashMap::new(),
}
}
pub fn add_field(&mut self, field_name: String) {
let offset = self.size;
self.field_offsets.insert(field_name, offset);
self.size += 4; // Each field is 4 bytes (i32)
}
pub fn get_field_offset(&self, field_name: &str) -> Option<u32> {
self.field_offsets.get(field_name).copied()
}
@ -63,50 +63,54 @@ impl MemoryManager {
box_layouts: HashMap::new(),
heap_start: 0x800, // 2KB reserved for stack/globals
};
// Register standard Box types
manager.register_standard_box_types();
manager
}
/// Register standard built-in Box types
fn register_standard_box_types(&mut self) {
// StringBox: [type_id][ref_count][field_count][ptr_to_chars][length]
self.register_box_type("StringBox".to_string(), vec!["data_ptr".to_string(), "length".to_string()]);
self.register_box_type(
"StringBox".to_string(),
vec!["data_ptr".to_string(), "length".to_string()],
);
// IntegerBox: [type_id][ref_count][field_count][value]
self.register_box_type("IntegerBox".to_string(), vec!["value".to_string()]);
// BoolBox: [type_id][ref_count][field_count][value]
self.register_box_type("BoolBox".to_string(), vec!["value".to_string()]);
// DataBox: [type_id][ref_count][field_count][value] - for testing
self.register_box_type("DataBox".to_string(), vec!["value".to_string()]);
}
/// Register a Box type layout
pub fn register_box_type(&mut self, type_name: String, fields: Vec<String>) {
let mut layout = BoxLayout::new(&type_name);
for field in fields {
layout.add_field(field);
}
self.box_layouts.insert(type_name, layout);
}
/// Get Box layout by type name
pub fn get_box_layout(&self, type_name: &str) -> Option<&BoxLayout> {
self.box_layouts.get(type_name)
}
/// Generate WASM globals for heap management
pub fn get_globals(&self) -> Vec<String> {
vec![
format!("(global $heap_ptr (mut i32) (i32.const {}))", self.heap_start),
]
vec![format!(
"(global $heap_ptr (mut i32) (i32.const {}))",
self.heap_start
)]
}
/// Generate heap allocation function with 4-byte alignment
pub fn get_malloc_function(&self) -> String {
format!(
@ -137,12 +141,13 @@ impl MemoryManager {
)"#
)
}
/// Generate Box allocation function for specific type
pub fn get_box_alloc_function(&self, type_name: &str) -> Result<String, WasmError> {
let layout = self.get_box_layout(type_name)
let layout = self
.get_box_layout(type_name)
.ok_or_else(|| WasmError::MemoryError(format!("Unknown box type: {}", type_name)))?;
Ok(format!(
r#"(func $alloc_{} (result i32)
(local $ptr i32)
@ -180,15 +185,21 @@ impl MemoryManager {
layout.field_offsets.len()
))
}
/// Generate field getter function
pub fn get_field_get_function(&self, type_name: &str, field_name: &str) -> Result<String, WasmError> {
let layout = self.get_box_layout(type_name)
pub fn get_field_get_function(
&self,
type_name: &str,
field_name: &str,
) -> Result<String, WasmError> {
let layout = self
.get_box_layout(type_name)
.ok_or_else(|| WasmError::MemoryError(format!("Unknown box type: {}", type_name)))?;
let offset = layout.get_field_offset(field_name)
.ok_or_else(|| WasmError::MemoryError(format!("Unknown field: {}.{}", type_name, field_name)))?;
let offset = layout.get_field_offset(field_name).ok_or_else(|| {
WasmError::MemoryError(format!("Unknown field: {}.{}", type_name, field_name))
})?;
Ok(format!(
r#"(func $get_{}_{} (param $box_ptr i32) (result i32)
;; Verify type_id (optional safety check)
@ -213,15 +224,21 @@ impl MemoryManager {
offset
))
}
/// Generate field setter function
pub fn get_field_set_function(&self, type_name: &str, field_name: &str) -> Result<String, WasmError> {
let layout = self.get_box_layout(type_name)
pub fn get_field_set_function(
&self,
type_name: &str,
field_name: &str,
) -> Result<String, WasmError> {
let layout = self
.get_box_layout(type_name)
.ok_or_else(|| WasmError::MemoryError(format!("Unknown box type: {}", type_name)))?;
let offset = layout.get_field_offset(field_name)
.ok_or_else(|| WasmError::MemoryError(format!("Unknown field: {}.{}", type_name, field_name)))?;
let offset = layout.get_field_offset(field_name).ok_or_else(|| {
WasmError::MemoryError(format!("Unknown field: {}.{}", type_name, field_name))
})?;
Ok(format!(
r#"(func $set_{}_{} (param $box_ptr i32) (param $value i32)
;; Verify type_id (optional safety check)
@ -246,7 +263,7 @@ impl MemoryManager {
offset
))
}
/// Get memory layout constants for documentation
pub fn get_memory_layout_info(&self) -> String {
format!(
@ -268,12 +285,12 @@ impl MemoryManager {
self.heap_start
)
}
/// Get type ID for a Box type
pub fn get_type_id(&self, type_name: &str) -> Option<u32> {
self.box_layouts.get(type_name).map(|layout| layout.type_id)
}
/// Generate generic Box creation helper
pub fn get_generic_box_alloc_function(&self) -> String {
format!(
@ -323,7 +340,7 @@ impl MemoryManager {
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_box_layout_creation() {
let layout = BoxLayout::new("DataBox");
@ -331,76 +348,79 @@ mod tests {
assert_eq!(layout.type_id, 0x1005); // DataBox has specific ID
assert!(layout.field_offsets.is_empty());
}
#[test]
fn test_box_layout_field_addition() {
let mut layout = BoxLayout::new("DataBox");
layout.add_field("field1".to_string());
layout.add_field("field2".to_string());
assert_eq!(layout.size, 20); // 12 + 4 + 4
assert_eq!(layout.get_field_offset("field1"), Some(12));
assert_eq!(layout.get_field_offset("field2"), Some(16));
}
#[test]
fn test_memory_manager_standard_types() {
let manager = MemoryManager::new();
// Verify standard types are registered
assert!(manager.get_box_layout("StringBox").is_some());
assert!(manager.get_box_layout("IntegerBox").is_some());
assert!(manager.get_box_layout("BoolBox").is_some());
assert!(manager.get_box_layout("DataBox").is_some());
// Verify type IDs
assert_eq!(manager.get_type_id("StringBox"), Some(0x1001));
assert_eq!(manager.get_type_id("IntegerBox"), Some(0x1002));
assert_eq!(manager.get_type_id("DataBox"), Some(0x1005));
}
#[test]
fn test_memory_manager_registration() {
let mut manager = MemoryManager::new();
manager.register_box_type("CustomBox".to_string(), vec!["x".to_string(), "y".to_string()]);
manager.register_box_type(
"CustomBox".to_string(),
vec!["x".to_string(), "y".to_string()],
);
let layout = manager.get_box_layout("CustomBox").unwrap();
assert_eq!(layout.field_offsets.len(), 2);
assert!(layout.get_field_offset("x").is_some());
assert!(layout.get_field_offset("y").is_some());
assert!(layout.type_id >= 0x2000); // Custom types start at 0x2000
}
#[test]
fn test_malloc_function_generation() {
let manager = MemoryManager::new();
let malloc_func = manager.get_malloc_function();
assert!(malloc_func.contains("$malloc"));
assert!(malloc_func.contains("$heap_ptr"));
assert!(malloc_func.contains("global.get"));
assert!(malloc_func.contains("i32.and")); // Alignment check
}
#[test]
fn test_box_alloc_function_generation() {
let manager = MemoryManager::new();
let alloc_func = manager.get_box_alloc_function("DataBox").unwrap();
assert!(alloc_func.contains("$alloc_databox"));
assert!(alloc_func.contains("call $malloc"));
assert!(alloc_func.contains("4101")); // 0x1005 type ID for DataBox
assert!(alloc_func.contains("i32.const 1")); // ref_count initialization
}
#[test]
fn test_generic_box_alloc_function() {
let manager = MemoryManager::new();
let generic_func = manager.get_generic_box_alloc_function();
assert!(generic_func.contains("$box_alloc"));
assert!(generic_func.contains("$type_id"));
assert!(generic_func.contains("$field_count"));
assert!(generic_func.contains("i32.const 12")); // Header size
}
}
}

View File

@ -1,6 +1,6 @@
/*!
* WASM Backend - Phase 8 Implementation
*
*
* Converts MIR instructions to WebAssembly for sandboxed execution
* Targets browser execution and wasmtime runtime
*/
@ -11,7 +11,7 @@ mod runtime;
// mod executor; // TODO: Fix WASM executor build errors
pub use codegen::{WasmCodegen, WasmModule};
pub use memory::{MemoryManager, BoxLayout};
pub use memory::{BoxLayout, MemoryManager};
pub use runtime::RuntimeImports;
// pub use executor::WasmExecutor; // TODO: Fix WASM executor build errors
@ -57,98 +57,120 @@ impl WasmBackend {
runtime: RuntimeImports::new(),
}
}
/// Compile MIR module to WASM bytes
pub fn compile_module(&mut self, mir_module: MirModule) -> Result<Vec<u8>, WasmError> {
// Generate WAT (WebAssembly Text) first for debugging
let wat_text = self.compile_to_wat(mir_module)?;
// Phase 9.77 Task 1.3: Fix UTF-8 encoding error in WAT→WASM conversion
self.wat_to_wasm(&wat_text)
}
/// Convert WAT text to WASM binary with proper UTF-8 handling
fn wat_to_wasm(&self, wat_source: &str) -> Result<Vec<u8>, WasmError> {
// Debug: Print WAT source for analysis
eprintln!("🔍 WAT Source Debug (length: {}):", wat_source.len());
eprintln!("WAT Content:\n{}", wat_source);
// UTF-8 validation to prevent encoding errors
if !wat_source.is_ascii() {
eprintln!("❌ WAT source contains non-ASCII characters");
return Err(WasmError::WasmValidationError(
"WAT source contains non-ASCII characters".to_string()
"WAT source contains non-ASCII characters".to_string(),
));
}
eprintln!("✅ WAT source is ASCII-compatible");
// Convert to bytes as required by wabt::wat2wasm
eprintln!("🔄 Converting WAT to WASM bytes...");
let wasm_bytes = wabt::wat2wasm(wat_source.as_bytes())
.map_err(|e| {
eprintln!("❌ wabt::wat2wasm failed: {}", e);
WasmError::WasmValidationError(format!("WAT to WASM conversion failed: {}", e))
})?;
eprintln!("✅ WASM conversion successful, {} bytes generated", wasm_bytes.len());
let wasm_bytes = wabt::wat2wasm(wat_source.as_bytes()).map_err(|e| {
eprintln!("❌ wabt::wat2wasm failed: {}", e);
WasmError::WasmValidationError(format!("WAT to WASM conversion failed: {}", e))
})?;
eprintln!(
"✅ WASM conversion successful, {} bytes generated",
wasm_bytes.len()
);
Ok(wasm_bytes)
}
/// Compile MIR module to WAT text format (for debugging)
pub fn compile_to_wat(&mut self, mir_module: MirModule) -> Result<String, WasmError> {
let wasm_module = self.codegen.generate_module(mir_module, &self.memory_manager, &self.runtime)?;
let wasm_module =
self.codegen
.generate_module(mir_module, &self.memory_manager, &self.runtime)?;
Ok(wasm_module.to_wat())
}
/// Execute WASM bytes using wasmtime (for testing)
pub fn execute_wasm(&self, wasm_bytes: &[u8]) -> Result<i32, WasmError> {
let engine = wasmtime::Engine::default();
let module = wasmtime::Module::new(&engine, wasm_bytes)
.map_err(|e| WasmError::WasmValidationError(format!("Module creation failed: {}", e)))?;
let module = wasmtime::Module::new(&engine, wasm_bytes).map_err(|e| {
WasmError::WasmValidationError(format!("Module creation failed: {}", e))
})?;
let mut store = wasmtime::Store::new(&engine, ());
// Create print function import
let print_func = wasmtime::Func::wrap(&mut store, |value: i32| {
println!("{}", value);
});
// Create print_str function import for string debugging
let print_str_func = wasmtime::Func::wrap(&mut store, |mut caller: wasmtime::Caller<'_, ()>, ptr: i32, len: i32| -> Result<(), wasmtime::Error> {
let memory = caller.get_export("memory")
.and_then(|export| export.into_memory())
.ok_or_else(|| wasmtime::Error::msg("Memory export not found"))?;
let data = memory.data(&caller);
let start = ptr as usize;
let end = start + len as usize;
if end <= data.len() {
let bytes = &data[start..end];
if let Ok(s) = std::str::from_utf8(bytes) {
println!("String: {}", s);
let print_str_func = wasmtime::Func::wrap(
&mut store,
|mut caller: wasmtime::Caller<'_, ()>,
ptr: i32,
len: i32|
-> Result<(), wasmtime::Error> {
let memory = caller
.get_export("memory")
.and_then(|export| export.into_memory())
.ok_or_else(|| wasmtime::Error::msg("Memory export not found"))?;
let data = memory.data(&caller);
let start = ptr as usize;
let end = start + len as usize;
if end <= data.len() {
let bytes = &data[start..end];
if let Ok(s) = std::str::from_utf8(bytes) {
println!("String: {}", s);
} else {
println!("Invalid UTF-8 bytes: {:?}", bytes);
}
} else {
println!("Invalid UTF-8 bytes: {:?}", bytes);
println!(
"String out of bounds: ptr={}, len={}, memory_size={}",
ptr,
len,
data.len()
);
}
} else {
println!("String out of bounds: ptr={}, len={}, memory_size={}", ptr, len, data.len());
}
Ok(())
});
Ok(())
},
);
let imports = [print_func.into(), print_str_func.into()];
let instance = wasmtime::Instance::new(&mut store, &module, &imports)
.map_err(|e| WasmError::WasmValidationError(format!("Instance creation failed: {}", e)))?;
let instance = wasmtime::Instance::new(&mut store, &module, &imports).map_err(|e| {
WasmError::WasmValidationError(format!("Instance creation failed: {}", e))
})?;
// Call main function
let main_func = instance.get_typed_func::<(), i32>(&mut store, "main")
.map_err(|e| WasmError::WasmValidationError(format!("Main function not found: {}", e)))?;
let result = main_func.call(&mut store, ())
let main_func = instance
.get_typed_func::<(), i32>(&mut store, "main")
.map_err(|e| {
WasmError::WasmValidationError(format!("Main function not found: {}", e))
})?;
let result = main_func
.call(&mut store, ())
.map_err(|e| WasmError::WasmValidationError(format!("Execution failed: {}", e)))?;
Ok(result)
}
}
@ -163,21 +185,21 @@ impl Default for WasmBackend {
mod tests {
use super::*;
use crate::mir::MirModule;
#[test]
fn test_backend_creation() {
let _backend = WasmBackend::new();
// Should not panic
assert!(true);
}
#[test]
fn test_empty_module_compilation() {
let mut backend = WasmBackend::new();
let module = MirModule::new("test".to_string());
// Should handle empty module gracefully
let result = backend.compile_to_wat(module);
assert!(result.is_ok());
}
}
}

View File

@ -1,6 +1,6 @@
/*!
* WASM Runtime Imports - External function imports for WASM modules
*
*
* Phase 8.2 PoC1: Implements env.print import for debugging
* Future: Additional runtime functions (file I/O, network, etc.)
*/
@ -27,12 +27,12 @@ impl RuntimeImports {
let mut runtime = Self {
imports: Vec::new(),
};
// Add standard runtime imports
runtime.add_standard_imports();
runtime
}
/// Add standard runtime function imports
fn add_standard_imports(&mut self) {
// env.print for debugging output
@ -42,7 +42,7 @@ impl RuntimeImports {
params: vec!["i32".to_string()],
result: None,
});
// env.print_str for string debugging (ptr, len)
self.imports.push(ImportFunction {
module: "env".to_string(),
@ -50,9 +50,9 @@ impl RuntimeImports {
params: vec!["i32".to_string(), "i32".to_string()],
result: None,
});
// Phase 9.7: Box FFI/ABI imports per BID specifications
// env.console_log for console.log(message) - (string_ptr, string_len)
self.imports.push(ImportFunction {
module: "env".to_string(),
@ -60,37 +60,47 @@ impl RuntimeImports {
params: vec!["i32".to_string(), "i32".to_string()],
result: None,
});
// env.canvas_fillRect for canvas.fillRect(canvas_id, x, y, w, h, color)
// Parameters: (canvas_id_ptr, canvas_id_len, x, y, w, h, color_ptr, color_len)
self.imports.push(ImportFunction {
module: "env".to_string(),
name: "canvas_fillRect".to_string(),
params: vec![
"i32".to_string(), "i32".to_string(), // canvas_id (ptr, len)
"i32".to_string(), "i32".to_string(), "i32".to_string(), "i32".to_string(), // x, y, w, h
"i32".to_string(), "i32".to_string(), // color (ptr, len)
"i32".to_string(),
"i32".to_string(), // canvas_id (ptr, len)
"i32".to_string(),
"i32".to_string(),
"i32".to_string(),
"i32".to_string(), // x, y, w, h
"i32".to_string(),
"i32".to_string(), // color (ptr, len)
],
result: None,
});
// env.canvas_fillText for canvas.fillText(canvas_id, text, x, y, font, color)
// Parameters: (canvas_id_ptr, canvas_id_len, text_ptr, text_len, x, y, font_ptr, font_len, color_ptr, color_len)
self.imports.push(ImportFunction {
module: "env".to_string(),
name: "canvas_fillText".to_string(),
params: vec![
"i32".to_string(), "i32".to_string(), // canvas_id (ptr, len)
"i32".to_string(), "i32".to_string(), // text (ptr, len)
"i32".to_string(), "i32".to_string(), // x, y
"i32".to_string(), "i32".to_string(), // font (ptr, len)
"i32".to_string(), "i32".to_string(), // color (ptr, len)
"i32".to_string(),
"i32".to_string(), // canvas_id (ptr, len)
"i32".to_string(),
"i32".to_string(), // text (ptr, len)
"i32".to_string(),
"i32".to_string(), // x, y
"i32".to_string(),
"i32".to_string(), // font (ptr, len)
"i32".to_string(),
"i32".to_string(), // color (ptr, len)
],
result: None,
});
// Phase 9.77: BoxCall runtime functions
// box_to_string - Convert any Box to string representation
self.imports.push(ImportFunction {
module: "env".to_string(),
@ -98,7 +108,7 @@ impl RuntimeImports {
params: vec!["i32".to_string()], // box_ptr
result: Some("i32".to_string()), // string_box_ptr
});
// box_print - Print any Box to console
self.imports.push(ImportFunction {
module: "env".to_string(),
@ -106,15 +116,15 @@ impl RuntimeImports {
params: vec!["i32".to_string()], // box_ptr
result: None,
});
// box_equals - Compare two Boxes for equality
self.imports.push(ImportFunction {
module: "env".to_string(),
name: "box_equals".to_string(),
params: vec!["i32".to_string(), "i32".to_string()], // box1_ptr, box2_ptr
result: Some("i32".to_string()), // bool result
result: Some("i32".to_string()), // bool result
});
// box_clone - Clone a Box
self.imports.push(ImportFunction {
module: "env".to_string(),
@ -122,39 +132,44 @@ impl RuntimeImports {
params: vec!["i32".to_string()], // box_ptr
result: Some("i32".to_string()), // cloned_box_ptr
});
// Future: env.file_read, env.file_write for file I/O
// Future: env.http_request for network access
}
/// Get all import declarations in WAT format
pub fn get_imports(&self) -> Vec<String> {
self.imports.iter().map(|import| {
let params = if import.params.is_empty() {
String::new()
} else {
format!("(param {})", import.params.join(" "))
};
let result = if let Some(ref result_type) = import.result {
format!("(result {})", result_type)
} else {
String::new()
};
format!(
"(import \"{}\" \"{}\" (func ${} {} {}))",
import.module,
import.name,
import.name,
params,
result
)
}).collect()
self.imports
.iter()
.map(|import| {
let params = if import.params.is_empty() {
String::new()
} else {
format!("(param {})", import.params.join(" "))
};
let result = if let Some(ref result_type) = import.result {
format!("(result {})", result_type)
} else {
String::new()
};
format!(
"(import \"{}\" \"{}\" (func ${} {} {}))",
import.module, import.name, import.name, params, result
)
})
.collect()
}
/// Add custom import function
pub fn add_import(&mut self, module: String, name: String, params: Vec<String>, result: Option<String>) {
pub fn add_import(
&mut self,
module: String,
name: String,
params: Vec<String>,
result: Option<String>,
) {
self.imports.push(ImportFunction {
module,
name,
@ -162,50 +177,54 @@ impl RuntimeImports {
result,
});
}
/// Check if an import is available
pub fn has_import(&self, name: &str) -> bool {
self.imports.iter().any(|import| import.name == name)
}
/// Get import function by name
pub fn get_import(&self, name: &str) -> Option<&ImportFunction> {
self.imports.iter().find(|import| import.name == name)
}
/// Generate JavaScript import object for browser execution
pub fn get_js_import_object(&self) -> String {
let mut js = String::new();
js.push_str("const importObject = {\n");
// Group by module
let mut modules: std::collections::HashMap<String, Vec<&ImportFunction>> = std::collections::HashMap::new();
let mut modules: std::collections::HashMap<String, Vec<&ImportFunction>> =
std::collections::HashMap::new();
for import in &self.imports {
modules.entry(import.module.clone()).or_default().push(import);
modules
.entry(import.module.clone())
.or_default()
.push(import);
}
for (module_name, functions) in modules {
js.push_str(&format!(" {}: {{\n", module_name));
for function in functions {
match function.name.as_str() {
"print" => {
js.push_str(" print: (value) => console.log(value),\n");
},
}
"print_str" => {
js.push_str(" print_str: (ptr, len) => {\n");
js.push_str(" const memory = instance.exports.memory;\n");
js.push_str(" const str = new TextDecoder().decode(new Uint8Array(memory.buffer, ptr, len));\n");
js.push_str(" console.log(str);\n");
js.push_str(" },\n");
},
}
"console_log" => {
js.push_str(" console_log: (ptr, len) => {\n");
js.push_str(" const memory = instance.exports.memory;\n");
js.push_str(" const str = new TextDecoder().decode(new Uint8Array(memory.buffer, ptr, len));\n");
js.push_str(" console.log(str);\n");
js.push_str(" },\n");
},
}
"canvas_fillRect" => {
js.push_str(" canvas_fillRect: (canvasIdPtr, canvasIdLen, x, y, w, h, colorPtr, colorLen) => {\n");
js.push_str(" const memory = instance.exports.memory;\n");
@ -218,7 +237,7 @@ impl RuntimeImports {
js.push_str(" ctx.fillRect(x, y, w, h);\n");
js.push_str(" }\n");
js.push_str(" },\n");
},
}
"canvas_fillText" => {
js.push_str(" canvas_fillText: (canvasIdPtr, canvasIdLen, textPtr, textLen, x, y, fontPtr, fontLen, colorPtr, colorLen) => {\n");
js.push_str(" const memory = instance.exports.memory;\n");
@ -234,49 +253,56 @@ impl RuntimeImports {
js.push_str(" ctx.fillText(text, x, y);\n");
js.push_str(" }\n");
js.push_str(" },\n");
},
}
_ => {
js.push_str(&format!(" {}: () => {{ throw new Error('Not implemented: {}'); }},\n",
function.name, function.name));
js.push_str(&format!(
" {}: () => {{ throw new Error('Not implemented: {}'); }},\n",
function.name, function.name
));
}
}
}
js.push_str(" },\n");
}
js.push_str("};\n");
js
}
/// Generate Rust wasmtime import bindings
pub fn get_wasmtime_imports(&self) -> Result<String, WasmError> {
let mut rust_code = String::new();
rust_code.push_str("// Wasmtime import bindings\n");
rust_code.push_str("let mut imports = Vec::new();\n\n");
for import in &self.imports {
match import.name.as_str() {
"print" => {
rust_code.push_str(r#"
rust_code.push_str(
r#"
let print_func = wasmtime::Func::wrap(&mut store, |value: i32| {
println!("{}", value);
});
imports.push(print_func.into());
"#);
},
"#,
);
}
_ => {
rust_code.push_str(&format!(r#"
rust_code.push_str(&format!(
r#"
// TODO: Implement {} import
let {}_func = wasmtime::Func::wrap(&mut store, || {{
panic!("Not implemented: {}")
}});
imports.push({}_func.into());
"#, import.name, import.name, import.name, import.name));
"#,
import.name, import.name, import.name, import.name
));
}
}
}
Ok(rust_code)
}
}
@ -284,25 +310,25 @@ imports.push({}_func.into());
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_runtime_imports_creation() {
let runtime = RuntimeImports::new();
assert!(!runtime.imports.is_empty());
assert!(runtime.has_import("print"));
}
#[test]
fn test_import_wat_generation() {
let runtime = RuntimeImports::new();
let imports = runtime.get_imports();
assert!(!imports.is_empty());
assert!(imports[0].contains("import"));
assert!(imports[0].contains("env"));
assert!(imports[0].contains("print"));
}
#[test]
fn test_custom_import_addition() {
let mut runtime = RuntimeImports::new();
@ -310,34 +336,34 @@ mod tests {
"custom".to_string(),
"test_func".to_string(),
vec!["i32".to_string(), "i32".to_string()],
Some("i32".to_string())
Some("i32".to_string()),
);
assert!(runtime.has_import("test_func"));
let import = runtime.get_import("test_func").unwrap();
assert_eq!(import.module, "custom");
assert_eq!(import.params.len(), 2);
assert!(import.result.is_some());
}
#[test]
fn test_js_import_object_generation() {
let runtime = RuntimeImports::new();
let js = runtime.get_js_import_object();
assert!(js.contains("importObject"));
assert!(js.contains("env"));
assert!(js.contains("print"));
assert!(js.contains("console.log"));
}
#[test]
fn test_wasmtime_imports_generation() {
let runtime = RuntimeImports::new();
let rust_code = runtime.get_wasmtime_imports().unwrap();
assert!(rust_code.contains("wasmtime::Func::wrap"));
assert!(rust_code.contains("print_func"));
assert!(rust_code.contains("println!"));
}
}
}

View File

@ -13,14 +13,20 @@ use crate::box_trait::{NyashBox, StringBox};
use crate::boxes::ConsoleBox;
/// WASM v2エントリポイント: 統一vtableディスパッチの最小テスト
pub fn compile_and_execute_v2(_module: &crate::mir::MirModule, _temp_name: &str) -> Result<Box<dyn crate::box_trait::NyashBox>, String> {
pub fn compile_and_execute_v2(
_module: &crate::mir::MirModule,
_temp_name: &str,
) -> Result<Box<dyn crate::box_trait::NyashBox>, String> {
// 1) ConsoleBoxを生成WASM環境ではブラウザコンソールに委譲
let console = Box::new(ConsoleBox::new());
// 2) slot解決→dispatchでlogを呼ぶ最小疎通
if let Some(slot_id) = unified_dispatch::resolve_slot(console.as_ref(), "log", 1) {
let args = vec![Box::new(StringBox::new("🎉 WASM v2 console.log working!")) as Box<dyn NyashBox>];
let args =
vec![Box::new(StringBox::new("🎉 WASM v2 console.log working!")) as Box<dyn NyashBox>];
let _ = unified_dispatch::dispatch_by_slot(slot_id, console.as_ref(), &args);
}
// 3) 結果を返す
Ok(Box::new(StringBox::new("WASM v2 unified dispatch test completed")))
Ok(Box::new(StringBox::new(
"WASM v2 unified dispatch test completed",
)))
}

View File

@ -5,8 +5,8 @@
#![cfg(feature = "wasm-backend")]
use crate::box_trait::{NyashBox, StringBox, VoidBox, BoolBox};
use crate::boxes::{ConsoleBox, ArrayBox, MapBox};
use crate::box_trait::{BoolBox, NyashBox, StringBox, VoidBox};
use crate::boxes::{ArrayBox, ConsoleBox, MapBox};
/// 受信ボックス/メソッド名/アリティからスロットを解決し、識別子を返す。
pub fn resolve_slot(recv: &dyn NyashBox, method: &str, arity: usize) -> Option<u16> {
@ -65,7 +65,7 @@ pub fn dispatch_by_slot(
}
None
}
// ArrayBox slots (100番台)
100 => {
// array.get(index)
@ -84,8 +84,8 @@ pub fn dispatch_by_slot(
}
None
}
// MapBox slots (200番台)
// MapBox slots (200番台)
200 => {
// map.size()
if let Some(map) = recv.as_any().downcast_ref::<MapBox>() {
@ -113,7 +113,7 @@ pub fn dispatch_by_slot(
}
None
}
_ => None
_ => None,
}
}

View File

@ -13,6 +13,8 @@ pub struct GeneratedVTableInfo {
pub fn generate_tables() -> GeneratedVTableInfo {
// 未実装: TypeRegistry::resolve_typebox_by_name()/methods を走査して集計
GeneratedVTableInfo { types: 0, methods: 0 }
GeneratedVTableInfo {
types: 0,
methods: 0,
}
}