Basic interpreter
This commit is contained in:
Generated
+32
@@ -56,6 +56,12 @@ version = "3.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.45"
|
||||
@@ -137,6 +143,15 @@ version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
||||
|
||||
[[package]]
|
||||
name = "fxhash"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "half"
|
||||
version = "2.7.1"
|
||||
@@ -185,6 +200,17 @@ dependencies = [
|
||||
"scc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "leaf_interpreter"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"fxhash",
|
||||
"leaf_allocators",
|
||||
"leaf_assembly",
|
||||
"scc",
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "leaf_parsing"
|
||||
version = "0.1.0"
|
||||
@@ -373,6 +399,12 @@ version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||
|
||||
[[package]]
|
||||
name = "stacker"
|
||||
version = "0.1.22"
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
[workspace]
|
||||
resolver = "3"
|
||||
members = ["allocators","assembly", "parsing"]
|
||||
members = ["allocators","assembly", "interpreter", "parsing"]
|
||||
|
||||
+223
-27
@@ -5,21 +5,17 @@ use crate::{
|
||||
values::{Value, ValueFlags},
|
||||
};
|
||||
use derive_more::Debug;
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
hash::Hash,
|
||||
ops::Deref,
|
||||
sync::{
|
||||
OnceLock,
|
||||
atomic::{AtomicU32, Ordering},
|
||||
},
|
||||
u32,
|
||||
};
|
||||
use std::{borrow::Cow, cell::UnsafeCell, hash::Hash, ops::Deref, sync::OnceLock};
|
||||
|
||||
// Maybe unsafe but honestly it's extremely unlikely that this will go wrong and it won't cause any issues.
|
||||
struct Id(UnsafeCell<u32>);
|
||||
unsafe impl Send for Id {}
|
||||
unsafe impl Sync for Id {}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[debug("%{}: {variant:?}", self.id())]
|
||||
#[debug("{variant:?}")]
|
||||
pub struct Instruction<'l> {
|
||||
id: AtomicU32,
|
||||
id: Id,
|
||||
pub parent_block: &'l Block<'l>,
|
||||
pub variant: InstructionVariant<'l>,
|
||||
}
|
||||
@@ -64,7 +60,7 @@ impl<'l> Instruction<'l> {
|
||||
|
||||
#[inline]
|
||||
pub fn id(&self) -> u32 {
|
||||
self.id.load(Ordering::Relaxed)
|
||||
unsafe { *self.id.0.get() }
|
||||
}
|
||||
|
||||
pub fn value_flags(&self) -> ValueFlags {
|
||||
@@ -75,26 +71,51 @@ impl<'l> Instruction<'l> {
|
||||
|
||||
pub fn value_ty(&self) -> Type<'l> {
|
||||
match self.variant {
|
||||
_ => todo!(),
|
||||
InstructionVariant::Return(_) => self.ctx().void_t(),
|
||||
InstructionVariant::Store(_, _) => self.ctx().void_t(),
|
||||
InstructionVariant::StackAlloc(ty) => ty.make_ptr(true).into(),
|
||||
InstructionVariant::Load(value) => match value.ty() {
|
||||
Type::Ptr(PtrT { base, .. }) => *base,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
InstructionVariant::IAdd(a, _) => a.ty(),
|
||||
InstructionVariant::FAdd(a, _) => a.ty(),
|
||||
InstructionVariant::ICmp(_, _, _) => self.ctx().bool_t(),
|
||||
_ => todo!("{self:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub enum InstructionVariant<'l> {
|
||||
#[debug("stackalloc {_0}")]
|
||||
StackAlloc(Type<'l>),
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum Cmp {
|
||||
Eq,
|
||||
Lt,
|
||||
Gt,
|
||||
Le,
|
||||
Ge,
|
||||
}
|
||||
|
||||
#[debug("gcalloc {_0}")]
|
||||
#[derive(PartialEq, Eq)]
|
||||
pub enum InstructionVariant<'l> {
|
||||
StackAlloc(Type<'l>),
|
||||
GCAlloc(Type<'l>),
|
||||
|
||||
#[debug("load {_0}")]
|
||||
Load(Value<'l>),
|
||||
|
||||
#[debug("store {_0}, {_1}")]
|
||||
Store(Value<'l>, Value<'l>),
|
||||
|
||||
#[debug("return{}", _0.map(|v| format!(" {v}")).unwrap_or_default())]
|
||||
IAdd(Value<'l>, Value<'l>),
|
||||
FAdd(Value<'l>, Value<'l>),
|
||||
|
||||
ICmp(Value<'l>, Value<'l>, Cmp),
|
||||
FCmp(Value<'l>, Value<'l>, Cmp),
|
||||
|
||||
Call(&'l Function<'l>, Vec<Value<'l>>),
|
||||
Jump(&'l Block<'l>),
|
||||
Branch {
|
||||
cond: Value<'l>,
|
||||
true_case: &'l Block<'l>,
|
||||
false_case: &'l Block<'l>,
|
||||
},
|
||||
Return(Option<Value<'l>>),
|
||||
}
|
||||
|
||||
@@ -102,14 +123,48 @@ impl InstructionVariant<'_> {
|
||||
pub fn is_block_termination(&self) -> bool {
|
||||
match self {
|
||||
Self::Return(_) => true,
|
||||
Self::Jump(_) => true,
|
||||
Self::Branch { .. } => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for InstructionVariant<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::StackAlloc(ty) => write!(f, "stackalloc {ty}"),
|
||||
Self::GCAlloc(ty) => write!(f, "gcalloc {ty}"),
|
||||
Self::Load(v) => write!(f, "load {v}"),
|
||||
Self::Store(t, v) => write!(f, "store {t}, {v}"),
|
||||
Self::IAdd(a, b) => write!(f, "iadd {a}, {b}"),
|
||||
Self::FAdd(a, b) => write!(f, "fadd {a}, {b}"),
|
||||
Self::ICmp(a, b, c) => write!(f, "icmp {c:?} {a}, {b}"),
|
||||
Self::FCmp(a, b, c) => write!(f, "fcmp {c:?} {a}, {b}"),
|
||||
Self::Call(func, args) => {
|
||||
write!(f, "call {:#?}(", *func as *const Function)?;
|
||||
let mut separator = "";
|
||||
for arg in args {
|
||||
write!(f, "{separator}{arg}")?;
|
||||
separator = ", ";
|
||||
}
|
||||
write!(f, ")")
|
||||
}
|
||||
Self::Branch {
|
||||
cond,
|
||||
true_case,
|
||||
false_case,
|
||||
} => write!(f, "br {cond} #{}, #{}", true_case.id, false_case.id),
|
||||
Self::Jump(b) => write!(f, "jump #{}", b.id),
|
||||
Self::Return(None) => write!(f, "return"),
|
||||
Self::Return(Some(v)) => write!(f, "return {v}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Block<'l> {
|
||||
pub id: u32,
|
||||
func: &'l Function<'l>,
|
||||
pub func: &'l Function<'l>,
|
||||
instructions: OnceLock<Vec<&'l Instruction<'l>>>,
|
||||
}
|
||||
|
||||
@@ -123,6 +178,14 @@ impl<'l> Block<'l> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Block<'_> {}
|
||||
|
||||
impl PartialEq for Block<'_> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
std::ptr::eq(self, other)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BlockBuilder<'l> {
|
||||
block: &'l Block<'l>,
|
||||
instructions: Vec<&'l Instruction<'l>>,
|
||||
@@ -179,6 +242,102 @@ impl<'l> BlockBuilder<'l> {
|
||||
Ok(inst.into())
|
||||
}
|
||||
|
||||
pub fn add(&mut self, a: Value<'l>, b: Value<'l>) -> BlockBuilderResult<'l, Value<'l>> {
|
||||
let [a_ty, b_ty] = [a.ty(), b.ty()];
|
||||
|
||||
match (a_ty, b_ty) {
|
||||
(Type::Int(a_ty), Type::Int(b_ty)) if a_ty == b_ty => {
|
||||
let inst = self.push_instruction(InstructionVariant::IAdd(a, b))?;
|
||||
Ok(inst.into())
|
||||
}
|
||||
(Type::Float(a_ty), Type::Float(b_ty)) if a_ty == b_ty => {
|
||||
let inst = self.push_instruction(InstructionVariant::FAdd(a, b))?;
|
||||
Ok(inst.into())
|
||||
}
|
||||
_ => Err(format!("Cannot add values of type `{a_ty}` and `b_ty`.").into()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cmp(
|
||||
&mut self,
|
||||
a: Value<'l>,
|
||||
b: Value<'l>,
|
||||
cmp: Cmp,
|
||||
) -> BlockBuilderResult<'l, Value<'l>> {
|
||||
let [a_ty, b_ty] = [a.ty(), b.ty()];
|
||||
|
||||
match (a_ty, b_ty) {
|
||||
(Type::Int(a_ty), Type::Int(b_ty)) if a_ty == b_ty => {
|
||||
let inst = self.push_instruction(InstructionVariant::ICmp(a, b, cmp))?;
|
||||
Ok(inst.into())
|
||||
}
|
||||
(Type::Float(a_ty), Type::Float(b_ty)) if a_ty == b_ty => {
|
||||
let inst = self.push_instruction(InstructionVariant::FCmp(a, b, cmp))?;
|
||||
Ok(inst.into())
|
||||
}
|
||||
_ => Err(format!("Cannot compare values of type `{a_ty}` and `b_ty`.").into()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn jump(&mut self, block: &'l Block<'l>) -> BlockBuilderResult<'l, Value<'l>> {
|
||||
if !std::ptr::eq(block.func, self.block.func) {
|
||||
return Err("Block does not belong to this function.".into());
|
||||
}
|
||||
let inst = self.push_instruction(InstructionVariant::Jump(block))?;
|
||||
Ok(inst.into())
|
||||
}
|
||||
|
||||
pub fn branch(
|
||||
&mut self,
|
||||
cond: Value<'l>,
|
||||
true_case: &'l Block<'l>,
|
||||
false_case: &'l Block<'l>,
|
||||
) -> BlockBuilderResult<'l, Value<'l>> {
|
||||
if !std::ptr::eq(true_case.func, self.block.func) {
|
||||
return Err("Block does not belong to this function.".into());
|
||||
}
|
||||
if !std::ptr::eq(false_case.func, self.block.func) {
|
||||
return Err("Block does not belong to this function.".into());
|
||||
}
|
||||
if !matches!(cond.ty(), Type::Bool(_)) {
|
||||
return Err(format!("Expected value of type `bool`, found `{}`.", cond.ty()).into());
|
||||
}
|
||||
let inst = self.push_instruction(InstructionVariant::Branch {
|
||||
cond,
|
||||
true_case,
|
||||
false_case,
|
||||
})?;
|
||||
Ok(inst.into())
|
||||
}
|
||||
|
||||
pub fn call(
|
||||
&mut self,
|
||||
func: &'l Function<'l>,
|
||||
args: Vec<Value<'l>>,
|
||||
) -> BlockBuilderResult<'l, Value<'l>> {
|
||||
let par_t = &*func.ty.par_t;
|
||||
if par_t.len() != args.len() {
|
||||
return Err(format!(
|
||||
"Invalid parameter count. Expected {}, found {}.",
|
||||
par_t.len(),
|
||||
args.len()
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
if let Some(i) = par_t.iter().zip(&args).position(|(a, b)| *a != b.ty()) {
|
||||
return Err(format!(
|
||||
"Invalid parameter at position {i}. Expected type `{}`, found `{}`.",
|
||||
&par_t[i],
|
||||
args[i].ty()
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
let inst = self.push_instruction(InstructionVariant::Call(func, args))?;
|
||||
Ok(inst.into())
|
||||
}
|
||||
|
||||
pub fn ret(&mut self, value: Option<Value<'l>>) -> BlockBuilderResult<'l, Value<'l>> {
|
||||
let ret_t = self.block.func.ty.ret_t;
|
||||
let value_ty = match value {
|
||||
@@ -219,7 +378,7 @@ impl<'l> BlockBuilder<'l> {
|
||||
return Err(format!("Block #{} has already terminated", self.block.id).into());
|
||||
}
|
||||
let instruction = &*self.block.func.ctx.alloc.alloc(Instruction {
|
||||
id: AtomicU32::new(u32::MAX),
|
||||
id: Id(UnsafeCell::new(u32::MAX)),
|
||||
parent_block: self.block,
|
||||
variant,
|
||||
});
|
||||
@@ -262,7 +421,7 @@ impl<'l> FunctionBodyBuilder<'l> {
|
||||
|
||||
pub fn create_block(&mut self) -> &'l Block<'l> {
|
||||
let block = &*self.func.ctx.alloc.alloc(Block {
|
||||
id: 0,
|
||||
id: self.blocks.len() as u32,
|
||||
func: self.func,
|
||||
instructions: OnceLock::new(),
|
||||
});
|
||||
@@ -280,7 +439,10 @@ impl<'l> FunctionBodyBuilder<'l> {
|
||||
for block in self.blocks {
|
||||
let block = block.build()?;
|
||||
for inst in block.instructions() {
|
||||
inst.id.store(next_id.next().unwrap(), Ordering::Relaxed);
|
||||
unsafe {
|
||||
let ptr = inst.id.0.get();
|
||||
std::ptr::write(ptr, next_id.next().unwrap());
|
||||
}
|
||||
}
|
||||
blocks.push(block);
|
||||
}
|
||||
@@ -306,6 +468,40 @@ impl<'l> FunctionBodyBuilder<'l> {
|
||||
self.current_builder().load(value)
|
||||
}
|
||||
|
||||
pub fn add(&mut self, a: Value<'l>, b: Value<'l>) -> BlockBuilderResult<'l, Value<'l>> {
|
||||
self.current_builder().add(a, b)
|
||||
}
|
||||
|
||||
pub fn cmp(
|
||||
&mut self,
|
||||
a: Value<'l>,
|
||||
b: Value<'l>,
|
||||
cmp: Cmp,
|
||||
) -> BlockBuilderResult<'l, Value<'l>> {
|
||||
self.current_builder().cmp(a, b, cmp)
|
||||
}
|
||||
|
||||
pub fn jump(&mut self, block: &'l Block<'l>) -> BlockBuilderResult<'l, Value<'l>> {
|
||||
self.current_builder().jump(block)
|
||||
}
|
||||
|
||||
pub fn branch(
|
||||
&mut self,
|
||||
cond: Value<'l>,
|
||||
true_case: &'l Block<'l>,
|
||||
false_case: &'l Block<'l>,
|
||||
) -> BlockBuilderResult<'l, Value<'l>> {
|
||||
self.current_builder().branch(cond, true_case, false_case)
|
||||
}
|
||||
|
||||
pub fn call(
|
||||
&mut self,
|
||||
func: &'l Function<'l>,
|
||||
args: Vec<Value<'l>>,
|
||||
) -> BlockBuilderResult<'l, Value<'l>> {
|
||||
self.current_builder().call(func, args)
|
||||
}
|
||||
|
||||
pub fn ret(&mut self, value: Option<Value<'l>>) -> BlockBuilderResult<'l, Value<'l>> {
|
||||
self.current_builder().ret(value)
|
||||
}
|
||||
|
||||
@@ -16,6 +16,10 @@ pub struct Function<'l> {
|
||||
}
|
||||
|
||||
impl<'l> Function<'l> {
|
||||
pub fn ctx(&self) -> Ctx<'l> {
|
||||
self.ctx
|
||||
}
|
||||
|
||||
pub fn body(&self) -> Option<&FunctionBody<'l>> {
|
||||
self.body.get()
|
||||
}
|
||||
@@ -28,6 +32,14 @@ impl<'l> Function<'l> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Function<'_> {}
|
||||
|
||||
impl PartialEq for Function<'_> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
std::ptr::eq(self, other)
|
||||
}
|
||||
}
|
||||
|
||||
impl FmtDebug for Function<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let body: &dyn FmtDebug = match self.body() {
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
use leaf_allocators::SyncArenaAllocator;
|
||||
use leaf_assembly::context::{Context, CreateConst};
|
||||
use leaf_assembly::types::derivations::MakeTypeDerivations;
|
||||
|
||||
fn main() {
|
||||
let allocator = SyncArenaAllocator::default();
|
||||
let context = Context::new(&allocator);
|
||||
let func = context.create_function(context.u32_t().make_fn([]));
|
||||
let mut builder = func.create_body().unwrap();
|
||||
builder
|
||||
.ret(Some(context.create_const(42u32).into()))
|
||||
.unwrap();
|
||||
builder.build().unwrap();
|
||||
println!("{func:#?}");
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
[package]
|
||||
name = "leaf_interpreter"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
fxhash = "0.2.1"
|
||||
leaf_allocators = { path = "../allocators" }
|
||||
leaf_assembly = { path = "../assembly" }
|
||||
scc = "3.3.7"
|
||||
smallvec = "1.15.1"
|
||||
@@ -0,0 +1,328 @@
|
||||
use crate::layout_cache::{GetLayout, LayoutCache};
|
||||
use fxhash::{FxBuildHasher, FxHashMap};
|
||||
use leaf_assembly::{
|
||||
functions::{
|
||||
Function,
|
||||
ir::{Cmp, Instruction, InstructionVariant},
|
||||
},
|
||||
types::{Type, intrinsics::IntT},
|
||||
values::{Const, Int, Value},
|
||||
};
|
||||
use scc::HashMap;
|
||||
use std::{alloc::Layout, fmt::Debug, ops::Range, sync::Arc};
|
||||
|
||||
#[derive(Debug)]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub enum OpCode<'l> {
|
||||
Store_CL_U8(u8, usize),
|
||||
Store_CL_U16(u16, usize),
|
||||
Store_CL_U32(u32, usize),
|
||||
Store_CL_U64(u64, usize),
|
||||
|
||||
Add_LL_U32(usize, usize, usize),
|
||||
Add_LC_U32(usize, u32, usize),
|
||||
|
||||
CmpEq_LC_U32(usize, u32, usize),
|
||||
CmpLt_LC_U32(usize, u32, usize),
|
||||
CmpGt_LC_U32(usize, u32, usize),
|
||||
CmpLe_LC_U32(usize, u32, usize),
|
||||
CmpGe_LC_U32(usize, u32, usize),
|
||||
|
||||
Call(&'l Function<'l>, Vec<Range<usize>>, Range<usize>),
|
||||
Jump(usize),
|
||||
Branch {
|
||||
cond: usize,
|
||||
true_case: usize,
|
||||
false_case: usize,
|
||||
},
|
||||
|
||||
CopyRange(Range<usize>, Range<usize>),
|
||||
ReturnRange(Range<usize>),
|
||||
}
|
||||
|
||||
#[non_exhaustive]
|
||||
pub struct InstructionCacheEntry<'l> {
|
||||
pub layout: Layout,
|
||||
pub opcodes: Vec<OpCode<'l>>,
|
||||
}
|
||||
|
||||
impl Debug for InstructionCacheEntry<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
struct OpCodes<'a, 'b>(&'a [OpCode<'b>]);
|
||||
impl Debug for OpCodes<'_, '_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let mut dbg = f.debug_list();
|
||||
for op in self.0 {
|
||||
dbg.entry(&format_args!("{op:?}"));
|
||||
}
|
||||
dbg.finish()
|
||||
}
|
||||
}
|
||||
|
||||
f.debug_struct("InstructionCacheEntry")
|
||||
.field("layout", &self.layout)
|
||||
.field("opcodes", &OpCodes(&self.opcodes))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'l> InstructionCacheEntry<'l> {
|
||||
pub fn new(func: &'l Function<'l>, layouts: &LayoutCache<'l>) -> Self {
|
||||
let mut opcodes = Vec::new();
|
||||
let mut layout = Layout::new::<()>();
|
||||
let mut memory_ranges = FxHashMap::default();
|
||||
|
||||
macro_rules! alloc_range {
|
||||
($ty:expr, $id:expr) => {{
|
||||
&*memory_ranges.entry($id).or_insert_with(|| {
|
||||
let l = layouts.get_layout($ty);
|
||||
let (new_layout, offset) = layout.extend(l.layout).unwrap();
|
||||
layout = new_layout;
|
||||
offset..offset + l.layout.size()
|
||||
})
|
||||
}};
|
||||
}
|
||||
|
||||
let Some(body) = func.body() else {
|
||||
unreachable!("InstructionCacheEntry::new called with a function without a body.");
|
||||
};
|
||||
|
||||
let mut backpatch = vec![];
|
||||
let mut block_starts = Vec::with_capacity(body.blocks.len());
|
||||
|
||||
for inst in body.blocks.iter().flat_map(|b| b.instructions()) {
|
||||
match &inst.variant {
|
||||
InstructionVariant::StackAlloc(ty) => {
|
||||
let _ = alloc_range!(*ty, inst.id());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
for block in &body.blocks {
|
||||
block_starts.push(opcodes.len());
|
||||
for inst in block.instructions() {
|
||||
match &inst.variant {
|
||||
InstructionVariant::StackAlloc(_) => {}
|
||||
|
||||
InstructionVariant::Store(
|
||||
Value::Instruction(
|
||||
t @ Instruction {
|
||||
variant: InstructionVariant::StackAlloc(ty),
|
||||
..
|
||||
},
|
||||
),
|
||||
Value::Const(Const::Int(Int::U8(v), ctx)),
|
||||
) => {
|
||||
assert_eq!(ctx.u8_t(), *ty);
|
||||
opcodes.push(OpCode::Store_CL_U8(*v, memory_ranges[&t.id()].start));
|
||||
}
|
||||
|
||||
InstructionVariant::Store(
|
||||
Value::Instruction(
|
||||
t @ Instruction {
|
||||
variant: InstructionVariant::StackAlloc(ty),
|
||||
..
|
||||
},
|
||||
),
|
||||
Value::Const(Const::Int(Int::U16(v), ctx)),
|
||||
) => {
|
||||
assert_eq!(ctx.u16_t(), *ty);
|
||||
opcodes.push(OpCode::Store_CL_U16(*v, memory_ranges[&t.id()].start));
|
||||
}
|
||||
|
||||
InstructionVariant::Store(
|
||||
Value::Instruction(
|
||||
t @ Instruction {
|
||||
variant: InstructionVariant::StackAlloc(ty),
|
||||
..
|
||||
},
|
||||
),
|
||||
Value::Const(Const::Int(Int::U32(v), ctx)),
|
||||
) => {
|
||||
assert_eq!(ctx.u32_t(), *ty);
|
||||
opcodes.push(OpCode::Store_CL_U32(*v, memory_ranges[&t.id()].start));
|
||||
}
|
||||
|
||||
InstructionVariant::Store(
|
||||
Value::Instruction(
|
||||
t @ Instruction {
|
||||
variant: InstructionVariant::StackAlloc(ty),
|
||||
..
|
||||
},
|
||||
),
|
||||
Value::Const(Const::Int(Int::U64(v), ctx)),
|
||||
) => {
|
||||
assert_eq!(ctx.u64_t(), *ty);
|
||||
opcodes.push(OpCode::Store_CL_U64(*v, memory_ranges[&t.id()].start));
|
||||
}
|
||||
|
||||
InstructionVariant::Store(
|
||||
Value::Instruction(
|
||||
t @ Instruction {
|
||||
variant: InstructionVariant::StackAlloc(ty),
|
||||
..
|
||||
},
|
||||
),
|
||||
Value::Instruction(v),
|
||||
) => {
|
||||
let src = alloc_range!(*ty, v.id()).clone();
|
||||
let dst = alloc_range!(*ty, t.id()).clone();
|
||||
opcodes.push(OpCode::CopyRange(src, dst));
|
||||
}
|
||||
|
||||
InstructionVariant::Load(Value::Instruction(
|
||||
t @ Instruction {
|
||||
variant: InstructionVariant::StackAlloc(ty),
|
||||
..
|
||||
},
|
||||
)) => {
|
||||
let src = alloc_range!(*ty, t.id()).clone();
|
||||
let dst = alloc_range!(*ty, inst.id()).clone();
|
||||
opcodes.push(OpCode::CopyRange(src, dst));
|
||||
}
|
||||
|
||||
InstructionVariant::IAdd(Value::Instruction(a), Value::Instruction(b)) => {
|
||||
match a.value_ty() {
|
||||
ty @ Type::Int(IntT {
|
||||
signed: false,
|
||||
precision: 32,
|
||||
..
|
||||
}) => {
|
||||
let a = memory_ranges[&a.id()].start;
|
||||
let b = memory_ranges[&b.id()].start;
|
||||
let c = alloc_range!(ty, inst.id()).start;
|
||||
opcodes.push(OpCode::Add_LL_U32(a, b, c));
|
||||
}
|
||||
_ => todo!("Unimplemented type `{}`", a.value_ty()),
|
||||
}
|
||||
}
|
||||
|
||||
InstructionVariant::IAdd(
|
||||
Value::Instruction(a),
|
||||
Value::Const(Const::Int(Int::U32(b), ctx)),
|
||||
) => {
|
||||
assert!(matches!(
|
||||
a.value_ty(),
|
||||
Type::Int(IntT {
|
||||
signed: false,
|
||||
precision: 32,
|
||||
..
|
||||
})
|
||||
));
|
||||
let a = memory_ranges[&a.id()].start;
|
||||
let c = alloc_range!(ctx.u32_t(), inst.id()).start;
|
||||
opcodes.push(OpCode::Add_LC_U32(a, *b, c));
|
||||
}
|
||||
|
||||
InstructionVariant::ICmp(
|
||||
Value::Instruction(a),
|
||||
Value::Const(Const::Int(Int::U32(b), ctx)),
|
||||
cmp,
|
||||
) => {
|
||||
assert!(matches!(
|
||||
a.value_ty(),
|
||||
Type::Int(IntT {
|
||||
signed: false,
|
||||
precision: 32,
|
||||
..
|
||||
})
|
||||
));
|
||||
let a = memory_ranges[&a.id()].start;
|
||||
let target = alloc_range!(ctx.bool_t(), inst.id()).start;
|
||||
match *cmp {
|
||||
Cmp::Eq => opcodes.push(OpCode::CmpEq_LC_U32(a, *b, target)),
|
||||
Cmp::Lt => opcodes.push(OpCode::CmpLt_LC_U32(a, *b, target)),
|
||||
Cmp::Gt => opcodes.push(OpCode::CmpGt_LC_U32(a, *b, target)),
|
||||
Cmp::Le => opcodes.push(OpCode::CmpLe_LC_U32(a, *b, target)),
|
||||
Cmp::Ge => opcodes.push(OpCode::CmpGe_LC_U32(a, *b, target)),
|
||||
};
|
||||
}
|
||||
|
||||
InstructionVariant::Call(func, args) => {
|
||||
let mut ranges = Vec::with_capacity(args.len());
|
||||
for arg in args {
|
||||
match arg {
|
||||
Value::Instruction(i) => {
|
||||
ranges.push(memory_ranges[&i.id()].clone());
|
||||
}
|
||||
_ => todo!("Unimplemented variant `{arg}`."),
|
||||
}
|
||||
}
|
||||
opcodes.push(OpCode::Call(
|
||||
func,
|
||||
ranges,
|
||||
alloc_range!(func.ty.ret_t, inst.id()).clone(),
|
||||
));
|
||||
}
|
||||
|
||||
InstructionVariant::Jump(target) => {
|
||||
backpatch.push(opcodes.len());
|
||||
opcodes.push(OpCode::Jump(target.id as usize));
|
||||
}
|
||||
|
||||
InstructionVariant::Branch {
|
||||
cond: Value::Instruction(i),
|
||||
true_case,
|
||||
false_case,
|
||||
} => {
|
||||
assert!(matches!(i.value_ty(), Type::Bool(_)));
|
||||
backpatch.push(opcodes.len());
|
||||
opcodes.push(OpCode::Branch {
|
||||
cond: memory_ranges[&i.id()].start,
|
||||
true_case: true_case.id as usize,
|
||||
false_case: false_case.id as usize,
|
||||
});
|
||||
}
|
||||
|
||||
InstructionVariant::Return(Some(Value::Instruction(t))) => {
|
||||
assert_eq!(func.ty.ret_t, t.value_ty());
|
||||
opcodes.push(OpCode::ReturnRange(memory_ranges[&t.id()].clone()));
|
||||
}
|
||||
|
||||
_ => todo!("Unimplemented instruction `{inst:?}`"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for idx in backpatch {
|
||||
match &mut opcodes[idx] {
|
||||
OpCode::Jump(target) => {
|
||||
*target = block_starts[*target];
|
||||
}
|
||||
OpCode::Branch {
|
||||
true_case,
|
||||
false_case,
|
||||
..
|
||||
} => {
|
||||
*true_case = block_starts[*true_case];
|
||||
*false_case = block_starts[*false_case];
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
Self { layout, opcodes }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct InstructionCache<'l> {
|
||||
layouts: Arc<LayoutCache<'l>>,
|
||||
entries: HashMap<*const Function<'l>, Arc<InstructionCacheEntry<'l>>, FxBuildHasher>,
|
||||
}
|
||||
|
||||
impl<'l> InstructionCache<'l> {
|
||||
pub fn new(layouts: Arc<LayoutCache<'l>>) -> Self {
|
||||
Self {
|
||||
layouts,
|
||||
entries: HashMap::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&self, func: &'l Function<'l>) -> Arc<InstructionCacheEntry<'l>> {
|
||||
self.entries
|
||||
.entry_sync(func as *const Function<'l>)
|
||||
.or_insert_with(|| Arc::new(InstructionCacheEntry::new(func, &self.layouts)))
|
||||
.clone()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,226 @@
|
||||
use crate::{
|
||||
instruction_cache::{InstructionCache, InstructionCacheEntry, OpCode},
|
||||
layout_cache::LayoutCache,
|
||||
};
|
||||
use fxhash::FxHashMap;
|
||||
use leaf_assembly::{
|
||||
context::Ctx,
|
||||
functions::Function,
|
||||
types::{Type, intrinsics::IntT},
|
||||
values::{Const, Int, Value},
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
use std::{ops::Range, sync::Arc};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
InvalidParameterCount,
|
||||
InvalidParameterType,
|
||||
FunctionHasNoBody,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum AnyValue {
|
||||
Void,
|
||||
Int(Int),
|
||||
Ptr(*const u8),
|
||||
}
|
||||
|
||||
pub struct Interpreter<'l> {
|
||||
ctx: Ctx<'l>,
|
||||
stack: Vec<u8>,
|
||||
layouts: Arc<LayoutCache<'l>>,
|
||||
instructions: Arc<InstructionCache<'l>>,
|
||||
native_funcs: FxHashMap<
|
||||
*const Function<'l>,
|
||||
Arc<dyn Fn(&mut Self, &[Range<usize>]) -> Result<AnyValue, Error> + 'l>,
|
||||
>,
|
||||
}
|
||||
|
||||
impl<'l> Interpreter<'l> {
|
||||
pub fn new(ctx: Ctx<'l>) -> Self {
|
||||
let layouts = Arc::new(LayoutCache::default());
|
||||
Self {
|
||||
ctx,
|
||||
stack: vec![0; 1024],
|
||||
layouts: layouts.clone(),
|
||||
instructions: Arc::new(InstructionCache::new(layouts)),
|
||||
native_funcs: FxHashMap::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register_function(
|
||||
&mut self,
|
||||
func: &'l Function<'l>,
|
||||
fn_impl: impl Fn(&mut Self, &[Range<usize>]) -> Result<AnyValue, Error> + 'l,
|
||||
) {
|
||||
self.native_funcs.insert(func, Arc::new(fn_impl));
|
||||
}
|
||||
|
||||
pub fn run(&mut self, func: &'l Function<'l>, args: Vec<Value<'l>>) -> Result<AnyValue, Error> {
|
||||
if func.body().is_none() {
|
||||
return Err(Error::FunctionHasNoBody);
|
||||
};
|
||||
|
||||
if func.ty.par_t.len() != args.len() {
|
||||
return Err(Error::InvalidParameterCount);
|
||||
}
|
||||
|
||||
if func
|
||||
.ty
|
||||
.par_t
|
||||
.iter()
|
||||
.zip(&args)
|
||||
.any(|(ty, arg)| *ty != arg.ty())
|
||||
{
|
||||
return Err(Error::InvalidParameterType);
|
||||
}
|
||||
|
||||
let mut sp = 0;
|
||||
let mut args_ranges: SmallVec<[Range<usize>; 4]> = args
|
||||
.into_iter()
|
||||
.map(|v| self.push_ctx_val(&mut sp, &v))
|
||||
.collect();
|
||||
|
||||
let func_entry = self.instructions.get(func);
|
||||
let ret_range = self.call(sp, &mut args_ranges, &func_entry);
|
||||
match func.ty.ret_t {
|
||||
Type::Int(IntT {
|
||||
signed: false,
|
||||
precision: 32,
|
||||
..
|
||||
}) => {
|
||||
let mem = &self.stack[ret_range];
|
||||
let val = u32::from_ne_bytes(mem.try_into().unwrap());
|
||||
Ok(AnyValue::Int(Int::U32(val)))
|
||||
}
|
||||
_ => todo!("Unsupported type `{}`", func.ty.ret_t),
|
||||
}
|
||||
}
|
||||
|
||||
fn call(
|
||||
&mut self,
|
||||
sp: usize,
|
||||
_args: &[Range<usize>],
|
||||
function: &InstructionCacheEntry<'l>,
|
||||
) -> Range<usize> {
|
||||
unsafe {
|
||||
let l = function.layout;
|
||||
let sp = sp + (self.stack.as_ptr().add(sp)).align_offset(l.align());
|
||||
assert!(sp + l.size() < self.stack.len());
|
||||
|
||||
// Since the earlier assertion guarantees the stack won't overflow, we can skip the range checks.
|
||||
let mut i = 0;
|
||||
while let Some(opcode) = function.opcodes.get(i) {
|
||||
match opcode {
|
||||
OpCode::Store_CL_U32(v, offset) => {
|
||||
let start = sp + *offset;
|
||||
self.stack
|
||||
.get_unchecked_mut(start..start + 4)
|
||||
.copy_from_slice(&v.to_ne_bytes());
|
||||
}
|
||||
|
||||
OpCode::CopyRange(src, dst) => {
|
||||
let len = src.len();
|
||||
let src = self.stack.as_ptr().add(sp + src.start);
|
||||
let dst = self.stack.as_mut_ptr().add(sp + dst.start);
|
||||
std::ptr::copy_nonoverlapping(src, dst, len);
|
||||
}
|
||||
|
||||
OpCode::Add_LL_U32(a, b, dst) => {
|
||||
let [a, b, dst] = [
|
||||
sp + a..sp + a + 4,
|
||||
sp + b..sp + b + 4,
|
||||
sp + dst..sp + dst + 4,
|
||||
];
|
||||
let a = u32::from_ne_bytes(self.stack.get_unchecked(a).try_into().unwrap());
|
||||
let b = u32::from_ne_bytes(self.stack.get_unchecked(b).try_into().unwrap());
|
||||
self.stack
|
||||
.get_unchecked_mut(dst)
|
||||
.copy_from_slice(&a.wrapping_add(b).to_ne_bytes());
|
||||
}
|
||||
|
||||
OpCode::Add_LC_U32(a, b, dst) => {
|
||||
let [a, dst] = [sp + a..sp + a + 4, sp + dst..sp + dst + 4];
|
||||
let a = u32::from_ne_bytes(self.stack.get_unchecked(a).try_into().unwrap());
|
||||
self.stack
|
||||
.get_unchecked_mut(dst)
|
||||
.copy_from_slice(&a.wrapping_add(*b).to_ne_bytes());
|
||||
}
|
||||
|
||||
OpCode::CmpLt_LC_U32(a, b, dst) => {
|
||||
let a = sp + a..sp + a + 4;
|
||||
let a = u32::from_ne_bytes(self.stack.get_unchecked(a).try_into().unwrap());
|
||||
*self.stack.get_unchecked_mut(*dst) = (a < *b) as u8;
|
||||
}
|
||||
|
||||
OpCode::Jump(target) => {
|
||||
i = *target;
|
||||
continue;
|
||||
}
|
||||
|
||||
OpCode::Branch {
|
||||
cond,
|
||||
true_case,
|
||||
false_case,
|
||||
} => {
|
||||
i = match self.stack().get_unchecked(*cond) {
|
||||
0 => *false_case,
|
||||
_ => *true_case,
|
||||
};
|
||||
continue;
|
||||
}
|
||||
|
||||
OpCode::Call(func, args, out) => {
|
||||
let key = *func as *const Function<'l>;
|
||||
match self.native_funcs.get(&key).cloned() {
|
||||
Some(native_func) => {
|
||||
let res = native_func(self, args).unwrap();
|
||||
self.write_any_val(out, &res);
|
||||
}
|
||||
None => {
|
||||
let func = self.instructions.get(func);
|
||||
let res = self.call(sp, args, &func);
|
||||
self.stack.copy_within(res, out.start);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OpCode::ReturnRange(range) => {
|
||||
return sp + range.start..sp + range.end;
|
||||
}
|
||||
_ => todo!("Unimplemented opcode `{opcode:?}`"),
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
unreachable!("Execution has produced no results");
|
||||
}
|
||||
|
||||
pub fn stack(&self) -> &[u8] {
|
||||
&self.stack
|
||||
}
|
||||
|
||||
fn push_ctx_val(&mut self, sp: &mut usize, value: &Value) -> Range<usize> {
|
||||
let ptr = &self.stack[*sp] as *const u8;
|
||||
match value {
|
||||
Value::Const(Const::Int(Int::U32(v), _)) => {
|
||||
*sp += ptr.align_offset(align_of::<u32>());
|
||||
let range = *sp..*sp + size_of::<u32>();
|
||||
self.stack[range.clone()].copy_from_slice(&v.to_ne_bytes());
|
||||
range
|
||||
}
|
||||
_ => todo!("Unsupported type `{}`", value.ty()),
|
||||
}
|
||||
}
|
||||
|
||||
fn write_any_val(&mut self, range: &Range<usize>, value: &AnyValue) {
|
||||
match value {
|
||||
AnyValue::Void => assert_eq!(range.len(), 0),
|
||||
AnyValue::Int(Int::U32(v)) => {
|
||||
self.stack[range.clone()].copy_from_slice(&v.to_ne_bytes());
|
||||
}
|
||||
_ => todo!("Unsupported value `{:?}`", value),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
use fxhash::FxBuildHasher;
|
||||
use leaf_assembly::types::{Type, derivations::PtrT, intrinsics::IntT};
|
||||
use scc::HashMap;
|
||||
use std::{
|
||||
alloc::Layout,
|
||||
any::Any,
|
||||
marker::PhantomData,
|
||||
ops::Range,
|
||||
sync::{Arc, OnceLock},
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct LayoutElement {
|
||||
pub offset: usize,
|
||||
pub layout: Arc<TypeLayout>,
|
||||
}
|
||||
|
||||
impl LayoutElement {
|
||||
pub fn range(&self) -> Range<usize> {
|
||||
self.offset..self.offset + self.layout.layout.size()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TypeLayout {
|
||||
pub layout: Layout,
|
||||
pub elements: Vec<(usize, Arc<TypeLayout>)>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct LayoutCache<'l> {
|
||||
phantom: PhantomData<&'l ()>,
|
||||
layouts: HashMap<*const u8, Arc<dyn Any>, FxBuildHasher>,
|
||||
}
|
||||
|
||||
pub trait GetLayout<T> {
|
||||
type LayoutT;
|
||||
fn get_layout(&self, value: T) -> Arc<Self::LayoutT>;
|
||||
}
|
||||
|
||||
impl<'l> GetLayout<Type<'_>> for LayoutCache<'l> {
|
||||
type LayoutT = TypeLayout;
|
||||
|
||||
fn get_layout(&self, ty: Type<'_>) -> Arc<Self::LayoutT> {
|
||||
match ty {
|
||||
Type::Void(_) => {
|
||||
static LAYOUT: OnceLock<Arc<TypeLayout>> = OnceLock::new();
|
||||
LAYOUT
|
||||
.get_or_init(|| {
|
||||
Arc::new(TypeLayout {
|
||||
layout: Layout::new::<()>(),
|
||||
elements: Vec::new(),
|
||||
})
|
||||
})
|
||||
.clone()
|
||||
}
|
||||
|
||||
Type::Bool(_) => {
|
||||
static LAYOUT: OnceLock<Arc<TypeLayout>> = OnceLock::new();
|
||||
LAYOUT
|
||||
.get_or_init(|| {
|
||||
Arc::new(TypeLayout {
|
||||
layout: Layout::new::<bool>(),
|
||||
elements: Vec::new(),
|
||||
})
|
||||
})
|
||||
.clone()
|
||||
}
|
||||
|
||||
Type::Int(IntT {
|
||||
signed: false,
|
||||
precision: 32,
|
||||
..
|
||||
}) => {
|
||||
static LAYOUT: OnceLock<Arc<TypeLayout>> = OnceLock::new();
|
||||
LAYOUT
|
||||
.get_or_init(|| {
|
||||
Arc::new(TypeLayout {
|
||||
layout: Layout::new::<u32>(),
|
||||
elements: Vec::new(),
|
||||
})
|
||||
})
|
||||
.clone()
|
||||
}
|
||||
|
||||
Type::Ptr(PtrT { .. }) => {
|
||||
static LAYOUT: OnceLock<Arc<TypeLayout>> = OnceLock::new();
|
||||
LAYOUT
|
||||
.get_or_init(|| {
|
||||
Arc::new(TypeLayout {
|
||||
layout: Layout::new::<*const u8>(),
|
||||
elements: Vec::new(),
|
||||
})
|
||||
})
|
||||
.clone()
|
||||
}
|
||||
|
||||
_ => todo!("Unsupported type {ty}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
pub mod interpreter;
|
||||
pub mod instruction_cache;
|
||||
pub mod layout_cache;
|
||||
@@ -0,0 +1,108 @@
|
||||
use leaf_allocators::SyncArenaAllocator;
|
||||
use leaf_assembly::context::{Context, CreateConst};
|
||||
use leaf_assembly::functions::ir::Cmp;
|
||||
use leaf_assembly::types::derivations::MakeTypeDerivations;
|
||||
use leaf_interpreter::interpreter::{AnyValue, Error, Interpreter};
|
||||
use std::ffi::CStr;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
fn main() {
|
||||
let allocator = SyncArenaAllocator::default();
|
||||
let context = Context::new(&allocator);
|
||||
|
||||
let puts = context.create_function(
|
||||
context
|
||||
.void_t()
|
||||
.make_fn([context.u8_t().make_ptr(false).into()]),
|
||||
);
|
||||
|
||||
let print_u32 = context.create_function(context.void_t().make_fn([context.u32_t().into()]));
|
||||
|
||||
let random_func = {
|
||||
let func = context.create_function(context.u32_t().make_fn([]));
|
||||
let mut builder = func.create_body().unwrap();
|
||||
|
||||
let loop_check = builder.create_block();
|
||||
let loop_body = builder.create_block();
|
||||
let loop_end = builder.create_block();
|
||||
|
||||
let var0 = builder.stack_alloc(context.u32_t()).unwrap();
|
||||
let var1 = builder.stack_alloc(context.u32_t()).unwrap();
|
||||
|
||||
builder
|
||||
.store(var0, context.create_const(42u32).into())
|
||||
.unwrap();
|
||||
|
||||
builder
|
||||
.store(var1, context.create_const(0u32).into())
|
||||
.unwrap();
|
||||
|
||||
builder.jump(loop_check).unwrap();
|
||||
builder.set_current_block(loop_check);
|
||||
|
||||
let a = builder.load(var0).unwrap();
|
||||
let b = builder.load(var1).unwrap();
|
||||
let c = builder.add(a, b).unwrap();
|
||||
builder.call(print_u32, vec![c]).unwrap();
|
||||
|
||||
let cond = builder
|
||||
.cmp(c, context.create_const(69u32).into(), Cmp::Lt)
|
||||
.unwrap();
|
||||
builder.branch(cond, loop_body, loop_end).unwrap();
|
||||
|
||||
builder.set_current_block(loop_body);
|
||||
let v = builder.load(var1).unwrap();
|
||||
let v = builder.add(v, context.create_const(1u32).into()).unwrap();
|
||||
builder.store(var1, v).unwrap();
|
||||
builder.jump(loop_check).unwrap();
|
||||
|
||||
builder.set_current_block(loop_end);
|
||||
let v = builder.load(var1).unwrap();
|
||||
builder.ret(Some(v)).unwrap();
|
||||
|
||||
let body = builder.build().unwrap();
|
||||
println!("{body:#?}");
|
||||
func
|
||||
};
|
||||
|
||||
let mut interpreter = Interpreter::new(context);
|
||||
|
||||
interpreter.register_function(puts, |int, args| match args {
|
||||
[range] => unsafe {
|
||||
let mem = &int.stack()[range.clone()];
|
||||
let ptr = usize::from_ne_bytes(mem.try_into().unwrap());
|
||||
let cstr = CStr::from_ptr(ptr as *const i8);
|
||||
print!("{cstr:?}");
|
||||
Ok(AnyValue::Void)
|
||||
},
|
||||
_ => Err(Error::InvalidParameterCount),
|
||||
});
|
||||
|
||||
interpreter.register_function(print_u32, |int, args| match args {
|
||||
[range] => {
|
||||
let mem = &int.stack()[range.clone()];
|
||||
let val = u32::from_ne_bytes(mem.try_into().unwrap());
|
||||
println!("{val}");
|
||||
Ok(AnyValue::Void)
|
||||
}
|
||||
_ => Err(Error::InvalidParameterCount),
|
||||
});
|
||||
|
||||
{
|
||||
let now = Instant::now();
|
||||
let result = interpreter.run(random_func, vec![]);
|
||||
let elapsed = now.elapsed();
|
||||
println!("Ret Value: {result:?}");
|
||||
println!("Cold Time: {:?}", elapsed)
|
||||
}
|
||||
{
|
||||
const LOOPS: u32 = 1;
|
||||
let mut duration = Duration::default();
|
||||
for _ in 0..LOOPS {
|
||||
let now = Instant::now();
|
||||
let _ = interpreter.run(random_func, vec![]);
|
||||
duration += now.elapsed();
|
||||
}
|
||||
println!("Warm Time: {:?}", duration / LOOPS);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user