mir: read plugin method signatures from nyash_box (#134)

* mir: load plugin method signatures

* llvm: read box type ids from nyash_box

* mir: show value types in MIR printer
This commit is contained in:
moe-charm
2025-09-11 03:33:33 +09:00
committed by GitHub
parent e114f9bfe3
commit 4201c63001
7 changed files with 183 additions and 93 deletions

34
nyash_box.toml Normal file
View File

@ -0,0 +1,34 @@
# Central plugin type definitions
[CounterBox]
type_id = 7
[CounterBox.methods.inc]
returns = { type = "i64" }
[CounterBox.methods.get]
returns = { type = "i64" }
[MathBox]
type_id = 50
[MathBox.methods.sqrt]
returns = { type = "f64" }
[MathBox.methods.sin]
returns = { type = "f64" }
[MathBox.methods.cos]
returns = { type = "f64" }
[MathBox.methods.round]
returns = { type = "f64" }
[FileBox]
type_id = 6
[FileBox.methods.read]
returns = { type = "string" }
[FileBox.methods.exists]
returns = { type = "bool" }

View File

@ -269,13 +269,27 @@ impl LLVMCompiler {
}) })
} }
// Load box type-id mapping from nyash.toml (for NewBox lowering) // Load box type-id mapping from nyash_box.toml (central plugin registry)
let mut box_type_ids: StdHashMap<String, i64> = StdHashMap::new(); let mut box_type_ids: StdHashMap<String, i64> = StdHashMap::new();
if let Ok(cfg) = std::fs::read_to_string("nyash_box.toml") {
if let Ok(doc) = toml::from_str::<toml::Value>(&cfg) {
if let Some(table) = doc.as_table() {
for (box_name, box_val) in table {
if let Some(id) = box_val.get("type_id").and_then(|v| v.as_integer()) {
box_type_ids.insert(box_name.clone(), id as i64);
}
}
}
}
}
// Fallback: legacy box_types table in nyash.toml
if let Ok(cfg) = std::fs::read_to_string("nyash.toml") { if let Ok(cfg) = std::fs::read_to_string("nyash.toml") {
if let Ok(doc) = toml::from_str::<toml::Value>(&cfg) { if let Ok(doc) = toml::from_str::<toml::Value>(&cfg) {
if let Some(bt) = doc.get("box_types").and_then(|v| v.as_table()) { if let Some(bt) = doc.get("box_types").and_then(|v| v.as_table()) {
for (k, v) in bt { for (k, v) in bt {
if let Some(id) = v.as_integer() { box_type_ids.insert(k.clone(), id as i64); } if let Some(id) = v.as_integer() {
box_type_ids.entry(k.clone()).or_insert(id as i64);
}
} }
} }
} }
@ -545,7 +559,7 @@ impl LLVMCompiler {
_ => return Err("box receiver must be pointer or i64 handle".to_string()), _ => return Err("box receiver must be pointer or i64 handle".to_string()),
}; };
let recv_h = codegen.builder.build_ptr_to_int(recv_p, i64t, "recv_p2i").map_err(|e| e.to_string())?; let recv_h = codegen.builder.build_ptr_to_int(recv_p, i64t, "recv_p2i").map_err(|e| e.to_string())?;
// Resolve type_id from metadata (Box("Type")) via nyash.toml // Resolve type_id from metadata (Box("Type")) using box_type_ids
let type_id: i64 = if let Some(crate::mir::MirType::Box(bname)) = func.metadata.value_types.get(box_val) { let type_id: i64 = if let Some(crate::mir::MirType::Box(bname)) = func.metadata.value_types.get(box_val) {
*box_type_ids.get(bname).unwrap_or(&0) *box_type_ids.get(bname).unwrap_or(&0)
} else if let Some(crate::mir::MirType::String) = func.metadata.value_types.get(box_val) { } else if let Some(crate::mir::MirType::String) = func.metadata.value_types.get(box_val) {

View File

@ -69,6 +69,9 @@ pub struct MirBuilder {
/// Optional per-value type annotations (MIR-level): ValueId -> MirType /// Optional per-value type annotations (MIR-level): ValueId -> MirType
pub(super) value_types: HashMap<ValueId, super::MirType>, pub(super) value_types: HashMap<ValueId, super::MirType>,
/// Plugin method return type signatures loaded from nyash_box.toml
plugin_method_sigs: HashMap<(String, String), super::MirType>,
/// Current static box name when lowering a static box body (e.g., "Main") /// Current static box name when lowering a static box body (e.g., "Main")
current_static_box: Option<String>, current_static_box: Option<String>,
@ -105,22 +108,20 @@ impl MirBuilder {
} }
} }
if let Some(bt) = recv_box { if let Some(bt) = recv_box {
let inferred: Option<super::MirType> = match (bt.as_str(), method.as_str()) { if let Some(mt) = self.plugin_method_sigs.get(&(bt.clone(), method.clone())) {
// Built-in box methods self.value_types.insert(d, mt.clone());
("StringBox", "length") | ("StringBox", "len") => Some(super::MirType::Integer), } else {
("StringBox", "is_empty") => Some(super::MirType::Bool), let inferred: Option<super::MirType> = match (bt.as_str(), method.as_str()) {
("StringBox", "charCodeAt") => Some(super::MirType::Integer), // Built-in box methods
("ArrayBox", "length") => Some(super::MirType::Integer), ("StringBox", "length") | ("StringBox", "len") => Some(super::MirType::Integer),
("StringBox", "is_empty") => Some(super::MirType::Bool),
// Plugin box methods ("StringBox", "charCodeAt") => Some(super::MirType::Integer),
("CounterBox", "get") => Some(super::MirType::Integer), ("ArrayBox", "length") => Some(super::MirType::Integer),
("MathBox", "sqrt") => Some(super::MirType::Float), _ => None,
("FileBox", "read") => Some(super::MirType::String), };
("FileBox", "exists") => Some(super::MirType::Bool), if let Some(mt) = inferred {
_ => None, self.value_types.insert(d, mt);
}; }
if let Some(mt) = inferred {
self.value_types.insert(d, mt);
} }
} }
} }
@ -128,6 +129,37 @@ impl MirBuilder {
} }
/// Create a new MIR builder /// Create a new MIR builder
pub fn new() -> Self { pub fn new() -> Self {
// Load plugin method signatures from nyash_box.toml if available
let mut plugin_method_sigs: HashMap<(String, String), super::MirType> = HashMap::new();
if let Ok(content) = fs::read_to_string("nyash_box.toml") {
if let Ok(root) = toml::from_str::<toml::Value>(&content) {
if let Some(table) = root.as_table() {
for (box_name, box_val) in table {
if let Some(methods) = box_val.get("methods").and_then(|v| v.as_table()) {
for (mname, mval) in methods {
if let Some(ret) = mval.get("returns") {
let ty_str = ret
.as_str()
.map(|s| s.to_string())
.or_else(|| ret.get("type").and_then(|t| t.as_str()).map(|s| s.to_string()));
if let Some(ts) = ty_str {
let mir_ty = match ts.to_lowercase().as_str() {
"i64" | "int" | "integer" => super::MirType::Integer,
"f64" | "float" => super::MirType::Float,
"bool" | "boolean" => super::MirType::Bool,
"string" => super::MirType::String,
"void" | "unit" => super::MirType::Void,
other => super::MirType::Box(other.to_string()),
};
plugin_method_sigs.insert((box_name.clone(), mname.clone()), mir_ty);
}
}
}
}
}
}
}
}
Self { Self {
current_module: None, current_module: None,
current_function: None, current_function: None,
@ -141,6 +173,7 @@ impl MirBuilder {
weak_fields_by_box: HashMap::new(), weak_fields_by_box: HashMap::new(),
field_origin_class: HashMap::new(), field_origin_class: HashMap::new(),
value_types: HashMap::new(), value_types: HashMap::new(),
plugin_method_sigs,
current_static_box: None, current_static_box: None,
include_loading: HashSet::new(), include_loading: HashSet::new(),
include_box_map: HashMap::new(), include_box_map: HashMap::new(),

View File

@ -4,7 +4,8 @@
* Implements pretty-printing for MIR modules and functions * Implements pretty-printing for MIR modules and functions
*/ */
use super::{MirModule, MirFunction, BasicBlock, MirInstruction}; use super::{MirModule, MirFunction, BasicBlock, MirInstruction, ValueId, MirType};
use std::collections::HashMap;
use std::fmt::Write; use std::fmt::Write;
use crate::debug::log as dlog; use crate::debug::log as dlog;
@ -219,7 +220,7 @@ impl MirPrinter {
if i > 0 { if i > 0 {
writeln!(output).unwrap(); writeln!(output).unwrap();
} }
output.push_str(&self.print_basic_block(block)); output.push_str(&self.print_basic_block(block, &function.metadata.value_types));
} }
} }
@ -229,7 +230,7 @@ impl MirPrinter {
} }
/// Print a basic block /// Print a basic block
pub fn print_basic_block(&self, block: &BasicBlock) -> String { pub fn print_basic_block(&self, block: &BasicBlock, types: &HashMap<ValueId, MirType>) -> String {
let mut output = String::new(); let mut output = String::new();
// Block header // Block header
@ -253,8 +254,8 @@ impl MirPrinter {
} else { } else {
write!(output, " ").unwrap(); write!(output, " ").unwrap();
} }
let mut line = self.format_instruction(instruction); let mut line = self.format_instruction(instruction, types);
if self.show_effects_inline { if self.show_effects_inline {
let eff = instruction.effects(); let eff = instruction.effects();
let cat = if eff.is_pure() { "pure" } else if eff.is_read_only() { "readonly" } else { "side" }; let cat = if eff.is_pure() { "pure" } else if eff.is_read_only() { "readonly" } else { "side" };
@ -272,29 +273,37 @@ impl MirPrinter {
output output
} }
fn format_dst(&self, dst: &ValueId, types: &HashMap<ValueId, MirType>) -> String {
if let Some(ty) = types.get(dst) {
format!("{}: {:?} =", dst, ty)
} else {
format!("{} =", dst)
}
}
/// Format a single instruction /// Format a single instruction
fn format_instruction(&self, instruction: &MirInstruction) -> String { fn format_instruction(&self, instruction: &MirInstruction, types: &HashMap<ValueId, MirType>) -> String {
match instruction { match instruction {
MirInstruction::Const { dst, value } => { MirInstruction::Const { dst, value } => {
format!("{} = const {}", dst, value) format!("{} const {}", self.format_dst(dst, types), value)
}, },
MirInstruction::BinOp { dst, op, lhs, rhs } => { MirInstruction::BinOp { dst, op, lhs, rhs } => {
format!("{} = {} {:?} {}", dst, lhs, op, rhs) format!("{} {} {:?} {}", self.format_dst(dst, types), lhs, op, rhs)
}, },
MirInstruction::UnaryOp { dst, op, operand } => { MirInstruction::UnaryOp { dst, op, operand } => {
format!("{} = {:?} {}", dst, op, operand) format!("{} {:?} {}", self.format_dst(dst, types), op, operand)
}, },
MirInstruction::Compare { dst, op, lhs, rhs } => { MirInstruction::Compare { dst, op, lhs, rhs } => {
format!("{} = icmp {:?} {}, {}", dst, op, lhs, rhs) format!("{} icmp {:?} {}, {}", self.format_dst(dst, types), op, lhs, rhs)
}, },
MirInstruction::Load { dst, ptr } => { MirInstruction::Load { dst, ptr } => {
format!("{} = load {}", dst, ptr) format!("{} load {}", self.format_dst(dst, types), ptr)
}, },
MirInstruction::Store { value, ptr } => { MirInstruction::Store { value, ptr } => {
format!("store {} -> {}", value, ptr) format!("store {} -> {}", value, ptr)
}, },
@ -304,9 +313,9 @@ impl MirPrinter {
.map(|v| format!("{}", v)) .map(|v| format!("{}", v))
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join(", "); .join(", ");
if let Some(dst) = dst { if let Some(dst) = dst {
format!("{} = call {}({})", dst, func, args_str) format!("{} call {}({})", self.format_dst(dst, types), func, args_str)
} else { } else {
format!("call {}({})", func, args_str) format!("call {}({})", func, args_str)
} }
@ -316,9 +325,9 @@ impl MirPrinter {
let c = captures.iter().map(|(n, v)| format!("{}={}", n, v)).collect::<Vec<_>>().join(", "); let c = captures.iter().map(|(n, v)| format!("{}={}", n, v)).collect::<Vec<_>>().join(", ");
let me_s = me.map(|m| format!(" me={}", m)).unwrap_or_default(); let me_s = me.map(|m| format!(" me={}", m)).unwrap_or_default();
let cap_s = if c.is_empty() { String::new() } else { format!(" [{}]", c) }; let cap_s = if c.is_empty() { String::new() } else { format!(" [{}]", c) };
format!("{} = function_new ({}) {{...{}}}{}{}", dst, p, body.len(), cap_s, me_s) format!("{} function_new ({}) {{...{}}}{}{}", self.format_dst(dst, types), p, body.len(), cap_s, me_s)
}, },
MirInstruction::BoxCall { dst, box_val, method, method_id, args, effects: _ } => { MirInstruction::BoxCall { dst, box_val, method, method_id, args, effects: _ } => {
let args_str = args.iter() let args_str = args.iter()
.map(|v| format!("{}", v)) .map(|v| format!("{}", v))
@ -326,7 +335,7 @@ impl MirPrinter {
.join(", "); .join(", ");
let id_suffix = method_id.map(|id| format!("[#{}]", id)).unwrap_or_default(); let id_suffix = method_id.map(|id| format!("[#{}]", id)).unwrap_or_default();
if let Some(dst) = dst { if let Some(dst) = dst {
format!("{} = call {}.{}{}({})", dst, box_val, method, id_suffix, args_str) format!("{} call {}.{}{}({})", self.format_dst(dst, types), box_val, method, id_suffix, args_str)
} else { } else {
format!("call {}.{}{}({})", box_val, method, id_suffix, args_str) format!("call {}.{}{}({})", box_val, method, id_suffix, args_str)
} }
@ -337,7 +346,7 @@ impl MirPrinter {
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join(", "); .join(", ");
if let Some(dst) = dst { if let Some(dst) = dst {
format!("{} = plugin_invoke {}.{}({})", dst, box_val, method, args_str) format!("{} plugin_invoke {}.{}({})", self.format_dst(dst, types), box_val, method, args_str)
} else { } else {
format!("plugin_invoke {}.{}({})", box_val, method, args_str) format!("plugin_invoke {}.{}({})", box_val, method, args_str)
} }
@ -364,61 +373,61 @@ impl MirPrinter {
.map(|(bb, val)| format!("[{}, {}]", val, bb)) .map(|(bb, val)| format!("[{}, {}]", val, bb))
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join(", "); .join(", ");
format!("{} = phi {}", dst, inputs_str) format!("{} phi {}", self.format_dst(dst, types), inputs_str)
}, },
MirInstruction::NewBox { dst, box_type, args } => { MirInstruction::NewBox { dst, box_type, args } => {
let args_str = args.iter() let args_str = args.iter()
.map(|v| format!("{}", v)) .map(|v| format!("{}", v))
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join(", "); .join(", ");
format!("{} = new {}({})", dst, box_type, args_str) format!("{} new {}({})", self.format_dst(dst, types), box_type, args_str)
}, },
// Legacy -> Unified print: TypeCheck as TypeOp(check) // Legacy -> Unified print: TypeCheck as TypeOp(check)
MirInstruction::TypeCheck { dst, value, expected_type } => { MirInstruction::TypeCheck { dst, value, expected_type } => {
// Print using unified TypeOp style to avoid naming divergence // Print using unified TypeOp style to avoid naming divergence
format!("{} = typeop check {} {}", dst, value, expected_type) format!("{} typeop check {} {}", self.format_dst(dst, types), value, expected_type)
}, },
MirInstruction::Cast { dst, value, target_type } => { MirInstruction::Cast { dst, value, target_type } => {
format!("{} = cast {} to {:?}", dst, value, target_type) format!("{} cast {} to {:?}", self.format_dst(dst, types), value, target_type)
}, },
MirInstruction::TypeOp { dst, op, value, ty } => { MirInstruction::TypeOp { dst, op, value, ty } => {
let op_str = match op { super::TypeOpKind::Check => "check", super::TypeOpKind::Cast => "cast" }; let op_str = match op { super::TypeOpKind::Check => "check", super::TypeOpKind::Cast => "cast" };
format!("{} = typeop {} {} {:?}", dst, op_str, value, ty) format!("{} typeop {} {} {:?}", self.format_dst(dst, types), op_str, value, ty)
}, },
MirInstruction::ArrayGet { dst, array, index } => { MirInstruction::ArrayGet { dst, array, index } => {
format!("{} = {}[{}]", dst, array, index) format!("{} {}[{}]", self.format_dst(dst, types), array, index)
}, },
MirInstruction::ArraySet { array, index, value } => { MirInstruction::ArraySet { array, index, value } => {
format!("{}[{}] = {}", array, index, value) format!("{}[{}] = {}", array, index, value)
}, },
MirInstruction::Copy { dst, src } => { MirInstruction::Copy { dst, src } => {
format!("{} = copy {}", dst, src) format!("{} copy {}", self.format_dst(dst, types), src)
}, },
MirInstruction::Debug { value, message } => { MirInstruction::Debug { value, message } => {
format!("debug {} \"{}\"", value, message) format!("debug {} \"{}\"", value, message)
}, },
MirInstruction::Print { value, effects: _ } => { MirInstruction::Print { value, effects: _ } => {
format!("print {}", value) format!("print {}", value)
}, },
MirInstruction::Nop => { MirInstruction::Nop => {
"nop".to_string() "nop".to_string()
}, },
// Phase 5: Control flow & exception handling // Phase 5: Control flow & exception handling
MirInstruction::Throw { exception, effects: _ } => { MirInstruction::Throw { exception, effects: _ } => {
format!("throw {}", exception) format!("throw {}", exception)
}, },
MirInstruction::Catch { exception_type, exception_value, handler_bb } => { MirInstruction::Catch { exception_type, exception_value, handler_bb } => {
if let Some(ref exc_type) = exception_type { if let Some(ref exc_type) = exception_type {
format!("catch {} {} -> {}", exc_type, exception_value, handler_bb) format!("catch {} {} -> {}", exc_type, exception_value, handler_bb)
@ -433,11 +442,11 @@ impl MirPrinter {
// Phase 6: Box reference operations // Phase 6: Box reference operations
MirInstruction::RefNew { dst, box_val } => { MirInstruction::RefNew { dst, box_val } => {
format!("{} = ref_new {}", dst, box_val) format!("{} ref_new {}", self.format_dst(dst, types), box_val)
}, },
MirInstruction::RefGet { dst, reference, field } => { MirInstruction::RefGet { dst, reference, field } => {
format!("{} = ref_get {}.{}", dst, reference, field) format!("{} ref_get {}.{}", self.format_dst(dst, types), reference, field)
}, },
MirInstruction::RefSet { reference, field, value } => { MirInstruction::RefSet { reference, field, value } => {
@ -446,52 +455,52 @@ impl MirPrinter {
// Legacy -> Unified print: WeakNew as weakref new // Legacy -> Unified print: WeakNew as weakref new
MirInstruction::WeakNew { dst, box_val } => { MirInstruction::WeakNew { dst, box_val } => {
format!("{} = weakref new {}", dst, box_val) format!("{} weakref new {}", self.format_dst(dst, types), box_val)
}, },
// Legacy -> Unified print: WeakLoad as weakref load // Legacy -> Unified print: WeakLoad as weakref load
MirInstruction::WeakLoad { dst, weak_ref } => { MirInstruction::WeakLoad { dst, weak_ref } => {
format!("{} = weakref load {}", dst, weak_ref) format!("{} weakref load {}", self.format_dst(dst, types), weak_ref)
}, },
// Legacy -> Unified print: BarrierRead as barrier read // Legacy -> Unified print: BarrierRead as barrier read
MirInstruction::BarrierRead { ptr } => { MirInstruction::BarrierRead { ptr } => {
format!("barrier read {}", ptr) format!("barrier read {}", ptr)
}, },
// Legacy -> Unified print: BarrierWrite as barrier write // Legacy -> Unified print: BarrierWrite as barrier write
MirInstruction::BarrierWrite { ptr } => { MirInstruction::BarrierWrite { ptr } => {
format!("barrier write {}", ptr) format!("barrier write {}", ptr)
}, },
MirInstruction::WeakRef { dst, op, value } => { MirInstruction::WeakRef { dst, op, value } => {
let op_str = match op { super::WeakRefOp::New => "new", super::WeakRefOp::Load => "load" }; let op_str = match op { super::WeakRefOp::New => "new", super::WeakRefOp::Load => "load" };
format!("{} = weakref {} {}", dst, op_str, value) format!("{} weakref {} {}", self.format_dst(dst, types), op_str, value)
}, },
MirInstruction::Barrier { op, ptr } => { MirInstruction::Barrier { op, ptr } => {
let op_str = match op { super::BarrierOp::Read => "read", super::BarrierOp::Write => "write" }; let op_str = match op { super::BarrierOp::Read => "read", super::BarrierOp::Write => "write" };
format!("barrier {} {}", op_str, ptr) format!("barrier {} {}", op_str, ptr)
}, },
// Phase 7: Async/Future Operations // Phase 7: Async/Future Operations
MirInstruction::FutureNew { dst, value } => { MirInstruction::FutureNew { dst, value } => {
format!("{} = future_new {}", dst, value) format!("{} future_new {}", self.format_dst(dst, types), value)
}, },
MirInstruction::FutureSet { future, value } => { MirInstruction::FutureSet { future, value } => {
format!("future_set {} = {}", future, value) format!("future_set {} = {}", future, value)
}, },
MirInstruction::Await { dst, future } => { MirInstruction::Await { dst, future } => {
format!("{} = await {}", dst, future) format!("{} await {}", self.format_dst(dst, types), future)
}, },
// Phase 9.7: External Function Calls // Phase 9.7: External Function Calls
MirInstruction::ExternCall { dst, iface_name, method_name, args, effects } => { MirInstruction::ExternCall { dst, iface_name, method_name, args, effects } => {
let args_str = args.iter().map(|v| format!("{}", v)).collect::<Vec<_>>().join(", "); let args_str = args.iter().map(|v| format!("{}", v)).collect::<Vec<_>>().join(", ");
if let Some(dst) = dst { if let Some(dst) = dst {
format!("{} = extern_call {}.{}({}) [effects: {}]", dst, iface_name, method_name, args_str, effects) format!("{} extern_call {}.{}({}) [effects: {}]", self.format_dst(dst, types), iface_name, method_name, args_str, effects)
} else { } else {
format!("extern_call {}.{}({}) [effects: {}]", iface_name, method_name, args_str, effects) format!("extern_call {}.{}({}) [effects: {}]", iface_name, method_name, args_str, effects)
} }

View File

@ -1,11 +1,11 @@
; MIR Module: main ; MIR Module: main
define void @main() { define i64 @main() {
bb0: bb0:
0: %0 = const 42 0: %0: Integer = const 42
1: %1 = const 10 1: %1: Integer = const 10
2: %2 = %0 Add %1 2: %2: Integer = %0 Add %1
3: print %2 3: extern_call env.console.log(%2) [effects: pure|io]
4: ret %2 4: ret %2
} }

View File

@ -1,11 +1,11 @@
; MIR Module: main ; MIR Module: main
define void @main() { define i1 @main() {
bb0: bb0:
0: %0 = const 42 0: %0: Integer = const 42
1: %1 = const 42 1: %1: Integer = const 42
2: %2 = icmp Eq %0, %1 2: %2: Bool = icmp Eq %0, %1
3: print %2 3: extern_call env.console.log(%2) [effects: pure|io]
4: ret %2 4: ret %2
} }

View File

@ -1,11 +1,11 @@
; MIR Module: main ; MIR Module: main
define void @main() { define i64 @main() {
bb0: bb0:
0: %0 = const "Hello" 0: %0: String = const "Hello"
1: %1 = const "World" 1: %1: String = const "World"
2: %2 = %0 Add %1 2: %2: Integer = %0 Add %1
3: print %2 3: extern_call env.console.log(%2) [effects: pure|io]
4: ret %2 4: ret %2
} }