Basic interpreter
This commit is contained in:
+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:#?}");
|
||||
}
|
||||
Reference in New Issue
Block a user