llvm: restore codegen and interpreter pipeline
This commit is contained in:
15
src/backend/llvm/compiler/aot.rs
Normal file
15
src/backend/llvm/compiler/aot.rs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
use super::LLVMCompiler;
|
||||||
|
use crate::box_trait::NyashBox;
|
||||||
|
use crate::mir::function::MirModule;
|
||||||
|
|
||||||
|
impl LLVMCompiler {
|
||||||
|
pub fn compile_and_execute(
|
||||||
|
&mut self,
|
||||||
|
mir_module: &MirModule,
|
||||||
|
temp_path: &str,
|
||||||
|
) -> Result<Box<dyn NyashBox>, String> {
|
||||||
|
let obj_path = format!("{}.o", temp_path);
|
||||||
|
self.compile_module(mir_module, &obj_path)?;
|
||||||
|
self.run_interpreter(mir_module)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,6 +1,7 @@
|
|||||||
|
use super::helpers::{as_float, as_int, map_type};
|
||||||
use super::LLVMCompiler;
|
use super::LLVMCompiler;
|
||||||
use crate::backend::llvm::context::CodegenContext;
|
use crate::backend::llvm::context::CodegenContext;
|
||||||
use crate::box_trait::{BoolBox, IntegerBox, NyashBox, StringBox};
|
use crate::box_trait::{BoolBox, IntegerBox, StringBox};
|
||||||
use crate::boxes::{function_box::FunctionBox, math_box::FloatBox, null_box::NullBox};
|
use crate::boxes::{function_box::FunctionBox, math_box::FloatBox, null_box::NullBox};
|
||||||
use crate::mir::function::MirModule;
|
use crate::mir::function::MirModule;
|
||||||
use crate::mir::instruction::{BinaryOp, CompareOp, ConstValue, MirInstruction, UnaryOp};
|
use crate::mir::instruction::{BinaryOp, CompareOp, ConstValue, MirInstruction, UnaryOp};
|
||||||
@ -49,32 +50,7 @@ impl LLVMCompiler {
|
|||||||
return Err("Main.main function not found in module".to_string());
|
return Err("Main.main function not found in module".to_string());
|
||||||
};
|
};
|
||||||
|
|
||||||
// Map MIR types to LLVM types
|
// Map MIR types to LLVM types via helpers
|
||||||
fn map_type<'ctx>(
|
|
||||||
ctx: &'ctx Context,
|
|
||||||
ty: &crate::mir::MirType,
|
|
||||||
) -> Result<BasicTypeEnum<'ctx>, String> {
|
|
||||||
Ok(match ty {
|
|
||||||
crate::mir::MirType::Integer => ctx.i64_type().into(),
|
|
||||||
crate::mir::MirType::Float => ctx.f64_type().into(),
|
|
||||||
crate::mir::MirType::Bool => ctx.bool_type().into(),
|
|
||||||
crate::mir::MirType::String => ctx
|
|
||||||
.i8_type()
|
|
||||||
.ptr_type(inkwell::AddressSpace::from(0))
|
|
||||||
.into(),
|
|
||||||
crate::mir::MirType::Void => return Err("Void has no value type".to_string()),
|
|
||||||
crate::mir::MirType::Box(_) => ctx
|
|
||||||
.i8_type()
|
|
||||||
.ptr_type(inkwell::AddressSpace::from(0))
|
|
||||||
.into(),
|
|
||||||
crate::mir::MirType::Array(_)
|
|
||||||
| crate::mir::MirType::Future(_)
|
|
||||||
| crate::mir::MirType::Unknown => ctx
|
|
||||||
.i8_type()
|
|
||||||
.ptr_type(inkwell::AddressSpace::from(0))
|
|
||||||
.into(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load box type-id mapping from nyash_box.toml (central plugin registry)
|
// Load box type-id mapping from nyash_box.toml (central plugin registry)
|
||||||
let box_type_ids = crate::backend::llvm::box_types::load_box_type_ids();
|
let box_type_ids = crate::backend::llvm::box_types::load_box_type_ids();
|
||||||
@ -117,20 +93,6 @@ impl LLVMCompiler {
|
|||||||
let mut vmap: HashMap<ValueId, BasicValueEnum> = HashMap::new();
|
let mut vmap: HashMap<ValueId, BasicValueEnum> = HashMap::new();
|
||||||
|
|
||||||
// Helper ops (centralized conversions and comparisons)
|
// Helper ops (centralized conversions and comparisons)
|
||||||
fn as_int<'ctx>(v: BasicValueEnum<'ctx>) -> Option<IntValue<'ctx>> {
|
|
||||||
if let BasicValueEnum::IntValue(iv) = v {
|
|
||||||
Some(iv)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn as_float<'ctx>(v: BasicValueEnum<'ctx>) -> Option<FloatValue<'ctx>> {
|
|
||||||
if let BasicValueEnum::FloatValue(fv) = v {
|
|
||||||
Some(fv)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn to_i64_any<'ctx>(
|
fn to_i64_any<'ctx>(
|
||||||
ctx: &'ctx Context,
|
ctx: &'ctx Context,
|
||||||
builder: &inkwell::builder::Builder<'ctx>,
|
builder: &inkwell::builder::Builder<'ctx>,
|
||||||
@ -2555,157 +2517,7 @@ impl LLVMCompiler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compile_and_execute(
|
|
||||||
&mut self,
|
|
||||||
mir_module: &MirModule,
|
|
||||||
temp_path: &str,
|
|
||||||
) -> Result<Box<dyn NyashBox>, String> {
|
|
||||||
// 1) Emit object via real LLVM lowering to ensure IR generation remains healthy
|
|
||||||
let obj_path = format!("{}.o", temp_path);
|
|
||||||
self.compile_module(mir_module, &obj_path)?;
|
|
||||||
|
|
||||||
// 2) Execute via a minimal MIR interpreter for parity (until full AOT linkage is wired)
|
|
||||||
// Supports: Const(Integer/Bool/String/Null), BinOp on Integer, Return(Some/None)
|
|
||||||
// This mirrors the non-LLVM mock path just enough for simple parity tests.
|
|
||||||
self.values.clear();
|
|
||||||
let func = mir_module
|
|
||||||
.functions
|
|
||||||
.get("Main.main")
|
|
||||||
.or_else(|| mir_module.functions.get("main"))
|
|
||||||
.or_else(|| mir_module.functions.values().next())
|
|
||||||
.ok_or_else(|| "Main.main function not found".to_string())?;
|
|
||||||
|
|
||||||
use crate::mir::instruction::MirInstruction as I;
|
|
||||||
for inst in &func.get_block(func.entry_block).unwrap().instructions {
|
|
||||||
match inst {
|
|
||||||
I::Const { dst, value } => {
|
|
||||||
let v: Box<dyn NyashBox> = match value {
|
|
||||||
ConstValue::Integer(i) => Box::new(IntegerBox::new(*i)),
|
|
||||||
ConstValue::Float(f) => Box::new(crate::boxes::math_box::FloatBox::new(*f)),
|
|
||||||
ConstValue::String(s) => {
|
|
||||||
Box::new(crate::box_trait::StringBox::new(s.clone()))
|
|
||||||
}
|
|
||||||
ConstValue::Bool(b) => Box::new(crate::box_trait::BoolBox::new(*b)),
|
|
||||||
ConstValue::Null => Box::new(crate::boxes::null_box::NullBox::new()),
|
|
||||||
ConstValue::Void => Box::new(IntegerBox::new(0)),
|
|
||||||
};
|
|
||||||
self.values.insert(*dst, v);
|
|
||||||
}
|
|
||||||
I::BinOp { dst, op, lhs, rhs } => {
|
|
||||||
let l = self
|
|
||||||
.values
|
|
||||||
.get(lhs)
|
|
||||||
.and_then(|b| b.as_any().downcast_ref::<IntegerBox>())
|
|
||||||
.ok_or_else(|| format!("binop lhs %{} not integer", lhs.0))?;
|
|
||||||
let r = self
|
|
||||||
.values
|
|
||||||
.get(rhs)
|
|
||||||
.and_then(|b| b.as_any().downcast_ref::<IntegerBox>())
|
|
||||||
.ok_or_else(|| format!("binop rhs %{} not integer", rhs.0))?;
|
|
||||||
let res = 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".into());
|
|
||||||
}
|
|
||||||
l.value / r.value
|
|
||||||
}
|
|
||||||
BinaryOp::Mod => l.value % r.value,
|
|
||||||
BinaryOp::BitAnd => l.value & r.value,
|
|
||||||
BinaryOp::BitOr => l.value | r.value,
|
|
||||||
BinaryOp::BitXor => l.value ^ r.value,
|
|
||||||
BinaryOp::Shl => l.value << r.value,
|
|
||||||
BinaryOp::Shr => l.value >> r.value,
|
|
||||||
BinaryOp::And => {
|
|
||||||
if (l.value != 0) && (r.value != 0) {
|
|
||||||
1
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BinaryOp::Or => {
|
|
||||||
if (l.value != 0) || (r.value != 0) {
|
|
||||||
1
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
self.values.insert(*dst, Box::new(IntegerBox::new(res)));
|
|
||||||
}
|
|
||||||
I::ExternCall {
|
|
||||||
dst,
|
|
||||||
iface_name,
|
|
||||||
method_name,
|
|
||||||
args,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
// Handle console methods via runtime handles (like AOT does)
|
|
||||||
if iface_name == "env.console" {
|
|
||||||
if let Some(arg0) = args.get(0) {
|
|
||||||
// Convert argument to handle and get string representation
|
|
||||||
use crate::jit::rt::handles;
|
|
||||||
if let Some(boxed_val) = self.values.get(arg0) {
|
|
||||||
// Convert NyashBox to handle
|
|
||||||
let arc: std::sync::Arc<dyn NyashBox> = boxed_val.clone_box().into();
|
|
||||||
let handle = handles::to_handle(arc) as i64;
|
|
||||||
|
|
||||||
// Debug output
|
|
||||||
eprintln!("DEBUG: handle={}", handle);
|
|
||||||
|
|
||||||
// Get object from handle registry and print
|
|
||||||
if let Some(obj) = handles::get(handle as u64) {
|
|
||||||
let s = obj.to_string_box().value;
|
|
||||||
match method_name.as_str() {
|
|
||||||
"log" => println!("{}", s),
|
|
||||||
"warn" => eprintln!("[warn] {}", s),
|
|
||||||
"error" => eprintln!("[error] {}", s),
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
eprintln!("DEBUG: handle {} not found in registry", handle);
|
|
||||||
match method_name.as_str() {
|
|
||||||
"log" => println!("{}", handle),
|
|
||||||
"warn" => eprintln!("[warn] {}", handle),
|
|
||||||
"error" => eprintln!("[error] {}", handle),
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Fallback: try direct string conversion
|
|
||||||
match method_name.as_str() {
|
|
||||||
"log" => println!(""),
|
|
||||||
"warn" => eprintln!("[warn] "),
|
|
||||||
"error" => eprintln!("[error] "),
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(d) = dst {
|
|
||||||
self.values.insert(*d, Box::new(IntegerBox::new(0)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
I::Return { value } => {
|
|
||||||
if let Some(v) = value {
|
|
||||||
return self
|
|
||||||
.values
|
|
||||||
.get(v)
|
|
||||||
.map(|b| b.clone_box())
|
|
||||||
.ok_or_else(|| format!("return %{} missing", v.0));
|
|
||||||
}
|
|
||||||
return Ok(Box::new(IntegerBox::new(0)));
|
|
||||||
}
|
|
||||||
_ => { /* ignore for now */ }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(Box::new(IntegerBox::new(0)))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
218
src/backend/llvm/compiler/helpers.rs
Normal file
218
src/backend/llvm/compiler/helpers.rs
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
use inkwell::builder::Builder;
|
||||||
|
use inkwell::context::Context;
|
||||||
|
use inkwell::types::BasicTypeEnum;
|
||||||
|
use inkwell::values::{BasicValueEnum, FloatValue, IntValue, PointerValue};
|
||||||
|
use inkwell::AddressSpace;
|
||||||
|
|
||||||
|
use crate::mir::MirType;
|
||||||
|
|
||||||
|
use crate::mir::CompareOp;
|
||||||
|
pub(crate) fn map_type<'ctx>(
|
||||||
|
ctx: &'ctx Context,
|
||||||
|
ty: &MirType,
|
||||||
|
) -> Result<BasicTypeEnum<'ctx>, String> {
|
||||||
|
Ok(match ty {
|
||||||
|
MirType::Integer => ctx.i64_type().into(),
|
||||||
|
MirType::Float => ctx.f64_type().into(),
|
||||||
|
MirType::Bool => ctx.bool_type().into(),
|
||||||
|
MirType::String => ctx
|
||||||
|
.i8_type()
|
||||||
|
.ptr_type(inkwell::AddressSpace::from(0))
|
||||||
|
.into(),
|
||||||
|
MirType::Void => return Err("Void has no value type".to_string()),
|
||||||
|
MirType::Box(_) => ctx
|
||||||
|
.i8_type()
|
||||||
|
.ptr_type(inkwell::AddressSpace::from(0))
|
||||||
|
.into(),
|
||||||
|
MirType::Array(_) | MirType::Future(_) | MirType::Unknown => ctx
|
||||||
|
.i8_type()
|
||||||
|
.ptr_type(inkwell::AddressSpace::from(0))
|
||||||
|
.into(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn as_int<'ctx>(v: BasicValueEnum<'ctx>) -> Option<IntValue<'ctx>> {
|
||||||
|
if let BasicValueEnum::IntValue(iv) = v {
|
||||||
|
Some(iv)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn as_float<'ctx>(v: BasicValueEnum<'ctx>) -> Option<FloatValue<'ctx>> {
|
||||||
|
if let BasicValueEnum::FloatValue(fv) = v {
|
||||||
|
Some(fv)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn to_i64_any<'ctx>(
|
||||||
|
ctx: &'ctx Context,
|
||||||
|
builder: &Builder<'ctx>,
|
||||||
|
v: BasicValueEnum<'ctx>,
|
||||||
|
) -> Result<IntValue<'ctx>, String> {
|
||||||
|
let i64t = ctx.i64_type();
|
||||||
|
Ok(match v {
|
||||||
|
BasicValueEnum::IntValue(iv) => {
|
||||||
|
if iv.get_type().get_bit_width() == 64 {
|
||||||
|
iv
|
||||||
|
} else if iv.get_type().get_bit_width() < 64 {
|
||||||
|
builder
|
||||||
|
.build_int_z_extend(iv, i64t, "zext_i64")
|
||||||
|
.map_err(|e| e.to_string())?
|
||||||
|
} else {
|
||||||
|
builder
|
||||||
|
.build_int_truncate(iv, i64t, "trunc_i64")
|
||||||
|
.map_err(|e| e.to_string())?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BasicValueEnum::PointerValue(pv) => builder
|
||||||
|
.build_ptr_to_int(pv, i64t, "p2i64")
|
||||||
|
.map_err(|e| e.to_string())?,
|
||||||
|
BasicValueEnum::FloatValue(fv) => {
|
||||||
|
// Bitcast f64 -> i64 via stack slot
|
||||||
|
let slot = builder
|
||||||
|
.get_insert_block()
|
||||||
|
.and_then(|bb| bb.get_parent())
|
||||||
|
.and_then(|f| f.get_first_basic_block())
|
||||||
|
.map(|entry| {
|
||||||
|
let eb = ctx.create_builder();
|
||||||
|
eb.position_at_end(entry);
|
||||||
|
eb
|
||||||
|
})
|
||||||
|
.unwrap_or_else(|| ctx.create_builder());
|
||||||
|
let i64p = i64t.ptr_type(AddressSpace::from(0));
|
||||||
|
let tmp = slot
|
||||||
|
.build_alloca(i64t, "f2i_tmp")
|
||||||
|
.map_err(|e| e.to_string())?;
|
||||||
|
let fptr_ty = ctx.f64_type().ptr_type(AddressSpace::from(0));
|
||||||
|
let castp = builder
|
||||||
|
.build_pointer_cast(tmp, fptr_ty, "i64p_to_f64p")
|
||||||
|
.map_err(|e| e.to_string())?;
|
||||||
|
builder.build_store(castp, fv).map_err(|e| e.to_string())?;
|
||||||
|
builder
|
||||||
|
.build_load(i64t, tmp, "ld_f2i")
|
||||||
|
.map_err(|e| e.to_string())?
|
||||||
|
.into_int_value()
|
||||||
|
}
|
||||||
|
_ => return Err("unsupported value for i64 conversion".to_string()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn i64_to_ptr<'ctx>(
|
||||||
|
ctx: &'ctx Context,
|
||||||
|
builder: &Builder<'ctx>,
|
||||||
|
iv: IntValue<'ctx>,
|
||||||
|
) -> Result<PointerValue<'ctx>, String> {
|
||||||
|
let pty = ctx.i8_type().ptr_type(AddressSpace::from(0));
|
||||||
|
builder
|
||||||
|
.build_int_to_ptr(iv, pty, "i64_to_ptr")
|
||||||
|
.map_err(|e| e.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn classify_tag<'ctx>(v: BasicValueEnum<'ctx>) -> i64 {
|
||||||
|
match v {
|
||||||
|
BasicValueEnum::FloatValue(_) => 5, // float
|
||||||
|
BasicValueEnum::PointerValue(_) => 8, // handle/ptr
|
||||||
|
BasicValueEnum::IntValue(_) => 3, // integer/bool
|
||||||
|
_ => 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn to_bool<'ctx>(
|
||||||
|
ctx: &'ctx Context,
|
||||||
|
b: BasicValueEnum<'ctx>,
|
||||||
|
builder: &Builder<'ctx>,
|
||||||
|
) -> Result<IntValue<'ctx>, String> {
|
||||||
|
if let Some(bb) = as_int(b) {
|
||||||
|
if bb.get_type().get_bit_width() == 1 {
|
||||||
|
Ok(bb)
|
||||||
|
} else {
|
||||||
|
Ok(builder
|
||||||
|
.build_int_compare(
|
||||||
|
inkwell::IntPredicate::NE,
|
||||||
|
bb,
|
||||||
|
bb.get_type().const_zero(),
|
||||||
|
"tobool",
|
||||||
|
)
|
||||||
|
.map_err(|e| e.to_string())?)
|
||||||
|
}
|
||||||
|
} else if let Some(fv) = as_float(b) {
|
||||||
|
let zero = fv.get_type().const_float(0.0);
|
||||||
|
Ok(builder
|
||||||
|
.build_float_compare(inkwell::FloatPredicate::ONE, fv, zero, "toboolf")
|
||||||
|
.map_err(|e| e.to_string())?)
|
||||||
|
} else if let BasicValueEnum::PointerValue(pv) = b {
|
||||||
|
let i64t = ctx.i64_type();
|
||||||
|
let p2i = builder
|
||||||
|
.build_ptr_to_int(pv, i64t, "p2i")
|
||||||
|
.map_err(|e| e.to_string())?;
|
||||||
|
Ok(builder
|
||||||
|
.build_int_compare(inkwell::IntPredicate::NE, p2i, i64t.const_zero(), "toboolp")
|
||||||
|
.map_err(|e| e.to_string())?)
|
||||||
|
} else {
|
||||||
|
Err("Unsupported value for boolean conversion".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn cmp_eq_ne_any<'ctx>(
|
||||||
|
ctx: &'ctx Context,
|
||||||
|
builder: &Builder<'ctx>,
|
||||||
|
op: &CompareOp,
|
||||||
|
lv: BasicValueEnum<'ctx>,
|
||||||
|
rv: BasicValueEnum<'ctx>,
|
||||||
|
) -> Result<BasicValueEnum<'ctx>, String> {
|
||||||
|
use crate::mir::CompareOp as C;
|
||||||
|
match (lv, rv) {
|
||||||
|
(BasicValueEnum::IntValue(li), BasicValueEnum::IntValue(ri)) => {
|
||||||
|
let pred = if matches!(op, C::Eq) {
|
||||||
|
inkwell::IntPredicate::EQ
|
||||||
|
} else {
|
||||||
|
inkwell::IntPredicate::NE
|
||||||
|
};
|
||||||
|
Ok(builder
|
||||||
|
.build_int_compare(pred, li, ri, "icmp")
|
||||||
|
.map_err(|e| e.to_string())?
|
||||||
|
.into())
|
||||||
|
}
|
||||||
|
(BasicValueEnum::FloatValue(lf), BasicValueEnum::FloatValue(rf)) => {
|
||||||
|
let pred = if matches!(op, C::Eq) {
|
||||||
|
inkwell::FloatPredicate::OEQ
|
||||||
|
} else {
|
||||||
|
inkwell::FloatPredicate::ONE
|
||||||
|
};
|
||||||
|
Ok(builder
|
||||||
|
.build_float_compare(pred, lf, rf, "fcmp")
|
||||||
|
.map_err(|e| e.to_string())?
|
||||||
|
.into())
|
||||||
|
}
|
||||||
|
(BasicValueEnum::PointerValue(_), _) | (_, BasicValueEnum::PointerValue(_)) => {
|
||||||
|
let li = to_i64_any(ctx, builder, lv)?;
|
||||||
|
let ri = to_i64_any(ctx, builder, rv)?;
|
||||||
|
let pred = if matches!(op, C::Eq) {
|
||||||
|
inkwell::IntPredicate::EQ
|
||||||
|
} else {
|
||||||
|
inkwell::IntPredicate::NE
|
||||||
|
};
|
||||||
|
Ok(builder
|
||||||
|
.build_int_compare(pred, li, ri, "pcmp_any")
|
||||||
|
.map_err(|e| e.to_string())?
|
||||||
|
.into())
|
||||||
|
}
|
||||||
|
_ => Err("compare type mismatch".to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn map_mirtype_to_basic<'ctx>(ctx: &'ctx Context, ty: &MirType) -> BasicTypeEnum<'ctx> {
|
||||||
|
match ty {
|
||||||
|
MirType::Integer => ctx.i64_type().into(),
|
||||||
|
MirType::Float => ctx.f64_type().into(),
|
||||||
|
MirType::Bool => ctx.bool_type().into(),
|
||||||
|
MirType::String => ctx.i8_type().ptr_type(AddressSpace::from(0)).into(),
|
||||||
|
MirType::Box(_) | MirType::Array(_) | MirType::Future(_) | MirType::Unknown => {
|
||||||
|
ctx.i8_type().ptr_type(AddressSpace::from(0)).into()
|
||||||
|
}
|
||||||
|
MirType::Void => ctx.i64_type().into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
138
src/backend/llvm/compiler/interpreter.rs
Normal file
138
src/backend/llvm/compiler/interpreter.rs
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
use super::LLVMCompiler;
|
||||||
|
use crate::box_trait::{BoolBox, IntegerBox, NyashBox, StringBox};
|
||||||
|
use crate::boxes::{math_box::FloatBox, null_box::NullBox};
|
||||||
|
use crate::mir::function::MirModule;
|
||||||
|
use crate::mir::instruction::{BinaryOp, ConstValue, MirInstruction as I};
|
||||||
|
|
||||||
|
impl LLVMCompiler {
|
||||||
|
pub(crate) fn run_interpreter(
|
||||||
|
&mut self,
|
||||||
|
mir_module: &MirModule,
|
||||||
|
) -> Result<Box<dyn NyashBox>, String> {
|
||||||
|
self.values.clear();
|
||||||
|
let func = mir_module
|
||||||
|
.functions
|
||||||
|
.get("Main.main")
|
||||||
|
.or_else(|| mir_module.functions.get("main"))
|
||||||
|
.or_else(|| mir_module.functions.values().next())
|
||||||
|
.ok_or_else(|| "Main.main function not found".to_string())?;
|
||||||
|
|
||||||
|
for inst in &func.get_block(func.entry_block).unwrap().instructions {
|
||||||
|
match inst {
|
||||||
|
I::Const { dst, value } => {
|
||||||
|
let v: Box<dyn NyashBox> = match value {
|
||||||
|
ConstValue::Integer(i) => Box::new(IntegerBox::new(*i)),
|
||||||
|
ConstValue::Float(f) => Box::new(FloatBox::new(*f)),
|
||||||
|
ConstValue::String(s) => Box::new(StringBox::new(s.clone())),
|
||||||
|
ConstValue::Bool(b) => Box::new(BoolBox::new(*b)),
|
||||||
|
ConstValue::Null => Box::new(NullBox::new()),
|
||||||
|
ConstValue::Void => Box::new(IntegerBox::new(0)),
|
||||||
|
};
|
||||||
|
self.values.insert(*dst, v);
|
||||||
|
}
|
||||||
|
I::BinOp { dst, op, lhs, rhs } => {
|
||||||
|
let l = self
|
||||||
|
.values
|
||||||
|
.get(lhs)
|
||||||
|
.and_then(|b| b.as_any().downcast_ref::<IntegerBox>())
|
||||||
|
.ok_or_else(|| format!("binop lhs %{} not integer", lhs.0))?;
|
||||||
|
let r = self
|
||||||
|
.values
|
||||||
|
.get(rhs)
|
||||||
|
.and_then(|b| b.as_any().downcast_ref::<IntegerBox>())
|
||||||
|
.ok_or_else(|| format!("binop rhs %{} not integer", rhs.0))?;
|
||||||
|
let res = 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".into());
|
||||||
|
}
|
||||||
|
l.value / r.value
|
||||||
|
}
|
||||||
|
BinaryOp::Mod => l.value % r.value,
|
||||||
|
BinaryOp::BitAnd => l.value & r.value,
|
||||||
|
BinaryOp::BitOr => l.value | r.value,
|
||||||
|
BinaryOp::BitXor => l.value ^ r.value,
|
||||||
|
BinaryOp::Shl => l.value << r.value,
|
||||||
|
BinaryOp::Shr => l.value >> r.value,
|
||||||
|
BinaryOp::And => {
|
||||||
|
if (l.value != 0) && (r.value != 0) {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BinaryOp::Or => {
|
||||||
|
if (l.value != 0) || (r.value != 0) {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.values.insert(*dst, Box::new(IntegerBox::new(res)));
|
||||||
|
}
|
||||||
|
I::ExternCall {
|
||||||
|
dst,
|
||||||
|
iface_name,
|
||||||
|
method_name,
|
||||||
|
args,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
if iface_name == "env.console" {
|
||||||
|
if let Some(arg0) = args.get(0) {
|
||||||
|
use crate::jit::rt::handles;
|
||||||
|
if let Some(boxed_val) = self.values.get(arg0) {
|
||||||
|
let arc: std::sync::Arc<dyn NyashBox> =
|
||||||
|
boxed_val.clone_box().into();
|
||||||
|
let handle = handles::to_handle(arc) as i64;
|
||||||
|
eprintln!("DEBUG: handle={}", handle);
|
||||||
|
if let Some(obj) = handles::get(handle as u64) {
|
||||||
|
let s = obj.to_string_box().value;
|
||||||
|
match method_name.as_str() {
|
||||||
|
"log" => println!("{}", s),
|
||||||
|
"warn" => eprintln!("[warn] {}", s),
|
||||||
|
"error" => eprintln!("[error] {}", s),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
eprintln!("DEBUG: handle {} not found in registry", handle);
|
||||||
|
match method_name.as_str() {
|
||||||
|
"log" => println!("{}", handle),
|
||||||
|
"warn" => eprintln!("[warn] {}", handle),
|
||||||
|
"error" => eprintln!("[error] {}", handle),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
match method_name.as_str() {
|
||||||
|
"log" => println!(""),
|
||||||
|
"warn" => eprintln!("[warn] "),
|
||||||
|
"error" => eprintln!("[error] "),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(d) = dst {
|
||||||
|
self.values.insert(*d, Box::new(IntegerBox::new(0)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
I::Return { value } => {
|
||||||
|
if let Some(v) = value {
|
||||||
|
return self
|
||||||
|
.values
|
||||||
|
.get(v)
|
||||||
|
.map(|b| b.clone_box())
|
||||||
|
.ok_or_else(|| format!("return %{} missing", v.0));
|
||||||
|
}
|
||||||
|
return Ok(Box::new(IntegerBox::new(0)));
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Box::new(IntegerBox::new(0)))
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -12,9 +12,15 @@ mod mock;
|
|||||||
pub use mock::*;
|
pub use mock::*;
|
||||||
|
|
||||||
#[cfg(feature = "llvm")]
|
#[cfg(feature = "llvm")]
|
||||||
mod real;
|
mod aot;
|
||||||
#[cfg(feature = "llvm")]
|
#[cfg(feature = "llvm")]
|
||||||
pub use real::*;
|
mod codegen;
|
||||||
|
#[cfg(feature = "llvm")]
|
||||||
|
mod helpers;
|
||||||
|
#[cfg(feature = "llvm")]
|
||||||
|
mod interpreter;
|
||||||
|
#[cfg(feature = "llvm")]
|
||||||
|
pub use aot::*;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|||||||
Reference in New Issue
Block a user