feat(phase-9.78): Improve MIR generation for built-in Box types and enhance LLVM mock
- Fix MIR builder to handle built-in Box types (IntegerBox, StringBox, etc.) specially - Generate MIR with actual constant values instead of ref_new instructions - Enhance LLVM mock compiler with MIR interpreter foundation - Add value storage HashMap for future MIR instruction interpretation This enables proper MIR generation where 'new IntegerBox(42)' becomes '%0 = const 42' instead of '%0 = ref_new "IntegerBox"'. This is essential for future LLVM code generation. Example MIR improvement: Before: %1 = const "IntegerBox" %0 = ref_new %1 After: %0 = const 42 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -3,20 +3,23 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
use crate::mir::function::MirModule;
|
use crate::mir::function::MirModule;
|
||||||
use crate::mir::instruction::MirInstruction;
|
use crate::mir::instruction::{MirInstruction, ConstValue, BinaryOp, UnaryOp, CompareOp};
|
||||||
use crate::box_trait::{NyashBox, IntegerBox};
|
use crate::mir::ValueId;
|
||||||
|
use crate::box_trait::{NyashBox, IntegerBox, FloatBox, StringBox, BoolBox, NullBox};
|
||||||
use super::context::CodegenContext;
|
use super::context::CodegenContext;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
/// Mock LLVM Compiler for demonstration (no inkwell dependency)
|
/// Mock LLVM Compiler with MIR interpreter for demonstration
|
||||||
/// This demonstrates the API structure needed for LLVM integration
|
/// This simulates LLVM behavior by interpreting MIR instructions
|
||||||
pub struct LLVMCompiler {
|
pub struct LLVMCompiler {
|
||||||
_phantom: std::marker::PhantomData<()>,
|
/// Values stored during mock execution
|
||||||
|
values: HashMap<ValueId, Box<dyn NyashBox>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LLVMCompiler {
|
impl LLVMCompiler {
|
||||||
pub fn new() -> Result<Self, String> {
|
pub fn new() -> Result<Self, String> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
_phantom: std::marker::PhantomData,
|
values: HashMap::new(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,56 +52,108 @@ impl LLVMCompiler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn compile_and_execute(
|
pub fn compile_and_execute(
|
||||||
&self,
|
&mut self,
|
||||||
mir_module: &MirModule,
|
mir_module: &MirModule,
|
||||||
temp_path: &str,
|
temp_path: &str,
|
||||||
) -> Result<Box<dyn NyashBox>, String> {
|
) -> Result<Box<dyn NyashBox>, String> {
|
||||||
// Mock implementation - simulates the complete compilation and execution pipeline
|
// Mock implementation - interprets MIR instructions to simulate execution
|
||||||
|
|
||||||
println!("🚀 Mock LLVM Compile & Execute:");
|
println!("🚀 Mock LLVM Compile & Execute (MIR Interpreter Mode):");
|
||||||
|
|
||||||
// 1. Mock object file generation
|
// 1. Mock object file generation
|
||||||
let obj_path = format!("{}.o", temp_path);
|
let obj_path = format!("{}.o", temp_path);
|
||||||
self.compile_module(mir_module, &obj_path)?;
|
self.compile_module(mir_module, &obj_path)?;
|
||||||
|
|
||||||
// 2. Mock linking (would use system cc in real implementation)
|
// 2. Find and execute main function
|
||||||
println!(" 🔗 Mock linking...");
|
let main_func = mir_module.functions.get("Main.main")
|
||||||
let executable_path = format!("{}_exec", temp_path);
|
.ok_or("Main.main function not found")?;
|
||||||
|
|
||||||
// 3. Mock execution - hardcoded return 42 for PoC
|
println!(" ⚡ Interpreting MIR instructions...");
|
||||||
println!(" ⚡ Mock execution...");
|
|
||||||
|
|
||||||
// Find main function and analyze return instructions
|
// 3. Execute MIR instructions
|
||||||
if let Some(main_func) = mir_module.functions.get("Main.main") {
|
let result = self.interpret_function(main_func)?;
|
||||||
for (_block_id, block) in &main_func.blocks {
|
|
||||||
for inst in &block.instructions {
|
// 4. Cleanup mock files
|
||||||
match inst {
|
let _ = std::fs::remove_file(&obj_path);
|
||||||
MirInstruction::Return { value: Some(_value_id) } => {
|
|
||||||
println!(" 📊 Found return instruction - simulating exit code 42");
|
Ok(result)
|
||||||
|
}
|
||||||
// 4. Cleanup mock files
|
|
||||||
let _ = std::fs::remove_file(&obj_path);
|
/// Interpret a MIR function by executing its instructions
|
||||||
|
fn interpret_function(
|
||||||
return Ok(Box::new(IntegerBox::new(42)));
|
&mut self,
|
||||||
|
func: &crate::mir::function::MirFunction,
|
||||||
|
) -> Result<Box<dyn NyashBox>, String> {
|
||||||
|
// Clear value storage
|
||||||
|
self.values.clear();
|
||||||
|
|
||||||
|
// For now, just execute the entry block
|
||||||
|
if let Some(entry_block) = func.blocks.get(&0) {
|
||||||
|
for inst in &entry_block.instructions {
|
||||||
|
match inst {
|
||||||
|
MirInstruction::Const { dst, value } => {
|
||||||
|
let nyash_value = match value {
|
||||||
|
ConstValue::Integer(i) => Box::new(IntegerBox::new(*i)) as Box<dyn NyashBox>,
|
||||||
|
ConstValue::Float(f) => Box::new(FloatBox::new(*f)) as Box<dyn NyashBox>,
|
||||||
|
ConstValue::String(s) => Box::new(StringBox::new(s.clone())) as Box<dyn NyashBox>,
|
||||||
|
ConstValue::Bool(b) => Box::new(BoolBox::new(*b)) as Box<dyn NyashBox>,
|
||||||
|
ConstValue::Null => Box::new(NullBox::new()) as Box<dyn NyashBox>,
|
||||||
|
};
|
||||||
|
self.values.insert(*dst, nyash_value);
|
||||||
|
println!(" 📝 %{} = const {:?}", dst.0, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
MirInstruction::BinOp { dst, op, lhs, rhs } => {
|
||||||
|
// Get operands
|
||||||
|
let left = self.values.get(lhs)
|
||||||
|
.ok_or_else(|| format!("Value %{} not found", lhs.0))?;
|
||||||
|
let right = self.values.get(rhs)
|
||||||
|
.ok_or_else(|| format!("Value %{} not found", rhs.0))?;
|
||||||
|
|
||||||
|
// Simple integer arithmetic for now
|
||||||
|
if let (Some(l), Some(r)) = (left.as_any().downcast_ref::<IntegerBox>(),
|
||||||
|
right.as_any().downcast_ref::<IntegerBox>()) {
|
||||||
|
let result = match op {
|
||||||
|
BinaryOp::Add => l.value() + r.value(),
|
||||||
|
BinaryOp::Sub => l.value() - r.value(),
|
||||||
|
BinaryOp::Mul => l.value() * r.value(),
|
||||||
|
BinaryOp::Div => {
|
||||||
|
if r.value() == 0 {
|
||||||
|
return Err("Division by zero".to_string());
|
||||||
|
}
|
||||||
|
l.value() / r.value()
|
||||||
|
}
|
||||||
|
BinaryOp::Mod => l.value() % r.value(),
|
||||||
|
};
|
||||||
|
self.values.insert(*dst, Box::new(IntegerBox::new(result)));
|
||||||
|
println!(" 📊 %{} = %{} {:?} %{} = {}", dst.0, lhs.0, op, rhs.0, result);
|
||||||
|
} else {
|
||||||
|
return Err("Binary operation on non-integer values not supported in mock".to_string());
|
||||||
}
|
}
|
||||||
MirInstruction::Return { value: None } => {
|
}
|
||||||
println!(" 📊 Found void return - simulating exit code 0");
|
|
||||||
|
MirInstruction::Return { value } => {
|
||||||
// 4. Cleanup mock files
|
if let Some(val_id) = value {
|
||||||
let _ = std::fs::remove_file(&obj_path);
|
let result = self.values.get(val_id)
|
||||||
|
.ok_or_else(|| format!("Return value %{} not found", val_id.0))?
|
||||||
|
.clone_box();
|
||||||
|
println!(" ✅ Returning value from %{}", val_id.0);
|
||||||
|
return Ok(result);
|
||||||
|
} else {
|
||||||
|
println!(" ✅ Void return");
|
||||||
return Ok(Box::new(IntegerBox::new(0)));
|
return Ok(Box::new(IntegerBox::new(0)));
|
||||||
}
|
}
|
||||||
_ => {
|
}
|
||||||
// Other instructions would be processed here
|
|
||||||
}
|
_ => {
|
||||||
|
// Other instructions not yet implemented
|
||||||
|
println!(" ⚠️ Skipping instruction: {:?}", inst);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default case
|
// Default return
|
||||||
let _ = std::fs::remove_file(&obj_path);
|
|
||||||
Ok(Box::new(IntegerBox::new(0)))
|
Ok(Box::new(IntegerBox::new(0)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,7 +16,7 @@ pub fn compile_and_execute(
|
|||||||
mir_module: &MirModule,
|
mir_module: &MirModule,
|
||||||
output_path: &str,
|
output_path: &str,
|
||||||
) -> Result<Box<dyn NyashBox>, String> {
|
) -> Result<Box<dyn NyashBox>, String> {
|
||||||
let compiler = compiler::LLVMCompiler::new()?;
|
let mut compiler = compiler::LLVMCompiler::new()?;
|
||||||
compiler.compile_and_execute(mir_module, output_path)
|
compiler.compile_and_execute(mir_module, output_path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -716,9 +716,63 @@ impl MirBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Build new expression: new ClassName(arguments)
|
/// Build new expression: new ClassName(arguments)
|
||||||
fn build_new_expression(&mut self, class: String, _arguments: Vec<ASTNode>) -> Result<ValueId, String> {
|
fn build_new_expression(&mut self, class: String, arguments: Vec<ASTNode>) -> Result<ValueId, String> {
|
||||||
// For Phase 6.1, we'll create a simple RefNew without processing arguments
|
// Special handling for built-in Box types that have single literal arguments
|
||||||
// In a full implementation, arguments would be used for constructor calls
|
match class.as_str() {
|
||||||
|
"IntegerBox" => {
|
||||||
|
if arguments.len() == 1 {
|
||||||
|
if let ASTNode::Literal { value: LiteralValue::Integer(n), .. } = &arguments[0] {
|
||||||
|
// For built-in boxes, just return the value directly
|
||||||
|
// The VM/interpreter will handle boxing when needed
|
||||||
|
let dst = self.value_gen.next();
|
||||||
|
self.emit_instruction(MirInstruction::Const {
|
||||||
|
dst,
|
||||||
|
value: ConstValue::Integer(*n),
|
||||||
|
})?;
|
||||||
|
return Ok(dst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"StringBox" => {
|
||||||
|
if arguments.len() == 1 {
|
||||||
|
if let ASTNode::Literal { value: LiteralValue::String(s), .. } = &arguments[0] {
|
||||||
|
let dst = self.value_gen.next();
|
||||||
|
self.emit_instruction(MirInstruction::Const {
|
||||||
|
dst,
|
||||||
|
value: ConstValue::String(s.clone()),
|
||||||
|
})?;
|
||||||
|
return Ok(dst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"FloatBox" => {
|
||||||
|
if arguments.len() == 1 {
|
||||||
|
if let ASTNode::Literal { value: LiteralValue::Float(f), .. } = &arguments[0] {
|
||||||
|
let dst = self.value_gen.next();
|
||||||
|
self.emit_instruction(MirInstruction::Const {
|
||||||
|
dst,
|
||||||
|
value: ConstValue::Float(*f),
|
||||||
|
})?;
|
||||||
|
return Ok(dst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"BoolBox" => {
|
||||||
|
if arguments.len() == 1 {
|
||||||
|
if let ASTNode::Literal { value: LiteralValue::Bool(b), .. } = &arguments[0] {
|
||||||
|
let dst = self.value_gen.next();
|
||||||
|
self.emit_instruction(MirInstruction::Const {
|
||||||
|
dst,
|
||||||
|
value: ConstValue::Bool(*b),
|
||||||
|
})?;
|
||||||
|
return Ok(dst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// For other classes, use the original implementation
|
||||||
let dst = self.value_gen.next();
|
let dst = self.value_gen.next();
|
||||||
|
|
||||||
// For now, create a "box type" value representing the class
|
// For now, create a "box type" value representing the class
|
||||||
|
|||||||
Reference in New Issue
Block a user