Casting, GEPs, partial LLVM backend

This commit is contained in:
Mia
2026-03-06 15:21:44 +01:00
parent 0b4f169c6f
commit bd0b619127
27 changed files with 1260 additions and 947 deletions
+2
View File
@@ -7,6 +7,8 @@ edition = "2024"
leaf_parser = { path = "../parser" }
leaf_assembly = { path = "../assembly" }
leaf_allocators = { path = "../allocators" }
leaf_backend_llvm = { path = "../backends/llvm" }
arcstr = "1.2.0"
derive_more = { version = "2.0.1", features = ["deref", "deref_mut", "debug", "display", "try_from", "from", "try_into", "into"] }
fxhash = "0.2.1"
itertools = "0.14.0"
+41 -4
View File
@@ -1,6 +1,13 @@
use arcstr::ArcStr;
use leaf_allocators::SyncArenaAllocator;
use leaf_assembly::assembly::{AssemblyIdentifier, Version};
use leaf_backend_llvm::{
LlvmContext,
inkwell::{
OptimizationLevel,
targets::{CodeModel, FileType, InitializationConfig, RelocMode, Target, TargetMachine},
},
};
use leaf_compiler::CompilationContext;
use leaf_parser::SourceCode;
use std::{path::PathBuf, sync::Arc};
@@ -15,12 +22,42 @@ fn main() {
if let Err(err) = context.extend(
ident.clone(),
&[Arc::new(SourceCode {
text: ArcStr::from(std::fs::read_to_string("../test.leaf").unwrap()),
file: PathBuf::from("../test.leaf"),
text: ArcStr::from(std::fs::read_to_string("test.leaf").unwrap()),
file: PathBuf::from("test.leaf"),
})],
) {
println!("{:#?}", err);
} else {
println!("{:#?}", context.get_assembly(&ident));
return;
}
let assembly = context.get_assembly(&ident).unwrap();
println!("{:#?}\n", assembly);
Target::initialize_all(&InitializationConfig::default());
let target_triple = TargetMachine::get_default_triple();
let target = Target::from_name("aarch64").unwrap();
let target_machine = target
.create_target_machine(
&target_triple,
"generic",
"", // CPU features
OptimizationLevel::Default,
RelocMode::Default,
CodeModel::Default,
)
.unwrap();
let ctx = LlvmContext::create();
let ctx = leaf_backend_llvm::CompilationContext::new(&ctx, &target_machine);
let module = ctx.compile(assembly);
module.print_to_stderr();
module.verify().unwrap();
let asm = target_machine
.write_to_memory_buffer(&module, FileType::Assembly)
.unwrap();
let asm = std::str::from_utf8(asm.as_slice()).unwrap();
eprintln!("{asm}");
std::fs::write("out.asm", asm).unwrap();
}
+196 -22
View File
@@ -5,12 +5,12 @@ use leaf_assembly::{
Function,
ir::{Cmp, FunctionBodyBuilder},
},
types::{Type, derivations::PtrT},
types::{IntT, Type, derivations::PtrT},
values::{AnyConst, Int, Value, ValueFlags},
};
use leaf_parser::{
SourceCode,
ast::{self, BinaryExpr, BinaryOp, ConstDecl, Expr, Ident, NamePattern, While},
ast::{self, BinaryExpr, BinaryOp, ConstDecl, Expr, Ident, IndexingExpr, NamePattern, While},
};
use std::{collections::HashMap, sync::Arc};
@@ -177,12 +177,12 @@ impl<'l> Scope<'l> {
}
}
Expr::Binary(expr) => {
Expr::Binary(bin_expr) => {
let BinaryExpr {
lhs: lhs_expr,
rhs: rhs_expr,
op,
} = &**expr;
} = &**bin_expr;
let mut lhs = self.compile_expression(lhs_expr, ctx)?;
if lhs.flags().contains(ValueFlags::LValue) && !matches!(op, BinaryOp::Assign(_)) {
@@ -196,28 +196,84 @@ impl<'l> Scope<'l> {
let builder = ctx.builder.as_mut().unwrap();
match (lhs.ty(), rhs.ty(), op) {
(Type::U32, Type::U32, BinaryOp::Add(_)) => Ok(builder.add(lhs, rhs).unwrap()),
(Type::U32, Type::U32, BinaryOp::Mul(_)) => Ok(builder.mul(lhs, rhs).unwrap()),
(Type::U32, Type::U32, BinaryOp::Lt(_)) => {
(Type::Int(a_ty), Type::Int(b_ty), BinaryOp::Add(_)) if a_ty == b_ty => {
Ok(builder.add(lhs, rhs).unwrap())
}
(Type::Int(a_ty), Type::Int(b_ty), BinaryOp::Sub(_)) if a_ty == b_ty => {
Ok(builder.sub(lhs, rhs).unwrap())
}
(Type::Int(a_ty), Type::Int(b_ty), BinaryOp::Mul(_)) if a_ty == b_ty => {
Ok(builder.mul(lhs, rhs).unwrap())
}
(Type::Int(a_ty), Type::Int(b_ty), BinaryOp::Div(_)) if a_ty == b_ty => {
Ok(builder.div(lhs, rhs).unwrap())
}
(Type::Int(a_ty), Type::Int(b_ty), BinaryOp::Mod(_)) if a_ty == b_ty => {
Ok(builder.modulo(lhs, rhs).unwrap())
}
(Type::Int(a_ty), Type::Int(b_ty), BinaryOp::Eq(_)) if a_ty == b_ty => {
Ok(builder.cmp(lhs, rhs, Cmp::Eq).unwrap())
}
(Type::Int(a_ty), Type::Int(b_ty), BinaryOp::Ne(_)) if a_ty == b_ty => {
Ok(builder.cmp(lhs, rhs, Cmp::Ne).unwrap())
}
(Type::Int(a_ty), Type::Int(b_ty), BinaryOp::Lt(_)) if a_ty == b_ty => {
Ok(builder.cmp(lhs, rhs, Cmp::Lt).unwrap())
}
(Type::Ptr(PtrT { base, mutable, .. }), ty, BinaryOp::Assign(_)) => {
match *base == ty {
true => Ok(builder.store(lhs, rhs).unwrap()),
false => Err(CompilationError {
kind: Kind::InvalidType,
message: format!(
"Cannot assign a value of type `{ty}` to a value of type `{base}`."
),
(Type::Int(a_ty), Type::Int(b_ty), BinaryOp::Gt(_)) if a_ty == b_ty => {
Ok(builder.cmp(lhs, rhs, Cmp::Gt).unwrap())
}
(Type::Int(a_ty), Type::Int(b_ty), BinaryOp::Le(_)) if a_ty == b_ty => {
Ok(builder.cmp(lhs, rhs, Cmp::Le).unwrap())
}
(Type::Int(a_ty), Type::Int(b_ty), BinaryOp::Ge(_)) if a_ty == b_ty => {
Ok(builder.cmp(lhs, rhs, Cmp::Ge).unwrap())
}
(Type::Ptr(PtrT { base, .. }), ty, BinaryOp::Assign(_)) => match *base == ty {
true => Ok(builder.store(lhs, rhs).unwrap()),
false => Err(CompilationError {
kind: Kind::InvalidType,
message: format!(
"Cannot assign a value of type `{ty}` to a value of type `{base}`."
),
location: Location::Range {
file: self.source.clone(),
range: rhs_expr.range(),
},
cause: None,
}),
},
(src_ty, Type::Type, BinaryOp::Cast(_)) => {
let Value::Constant(AnyConst::Type(dst_ty)) = rhs else {
return Err(CompilationError {
kind: Kind::NotAType,
message: format!("Cannot perform cast."),
location: Location::Range {
file: self.source.clone(),
range: rhs_expr.range(),
range: expr.range(),
},
cause: None,
}),
cause: Some(Box::new(CompilationError {
kind: Kind::NotAType,
message: format!("Cast target is not a type."),
location: Location::Range {
file: self.source.clone(),
range: rhs_expr.range(),
},
cause: None,
})),
});
};
match (src_ty, dst_ty) {
(Type::Int(src_ty), Type::Int(dst_ty)) => {
if dst_ty.precision < src_ty.precision {
return Ok(builder.trunc(lhs, dst_ty).unwrap().into());
}
todo!("{src_ty} as {dst_ty}");
}
_ => todo!("{src_ty} as {dst_ty}"),
}
}
(a, b, _) => unimplemented!("{a} {op:?} {b} | {lhs:?} {op:?} {rhs:?}"),
(a, b, _) => todo!("{a} {op:?} {b} | {lhs:?} {op:?} {rhs:?}"),
}
}
@@ -282,15 +338,123 @@ impl<'l> Scope<'l> {
Ok(builder.call(func, args).unwrap())
}
Expr::Type(expr) => match &**expr {
Expr::Type(ty_expr) => match &**ty_expr {
ast::Type::Ptr { base, mutable } => match self.compile_expression(base, ctx)? {
Value::Constant(AnyConst::Type(ty)) => {
Ok(AnyConst::Type(Type::Ptr(ty.make_ptr(*mutable))).into())
}
_ => todo!(),
Value::Instruction(inst) if inst.value_flags().contains(ValueFlags::LValue) => {
let Type::Ptr(PtrT {
base,
mutable: is_mut,
..
}) = inst.value_ty()
else {
unreachable!()
};
if *mutable && !*is_mut {
return Err(CompilationError {
kind: Kind::NotAFunction,
message: "Cannot obtain a mutable pointer to an immutable value."
.into(),
location: Location::Range {
file: self.source.clone(),
range: expr.range(),
},
cause: None,
});
}
let mut flags = inst.value_flags();
let builder = ctx.builder.as_mut().unwrap();
flags.remove(
ValueFlags::Mutable | ValueFlags::Volatile | ValueFlags::LValue,
);
let ptr = Type::Ptr(base.make_ptr(*mutable));
unsafe {
Ok(builder
.reinterpret(Value::Instruction(inst), ptr, flags)
.unwrap()
.into())
}
}
v => todo!("{v:?}"),
},
},
Expr::List(expr) => {
let mut expr = expr.iter();
let mut values = Vec::with_capacity(expr.len());
match expr.next() {
None => return Ok(Value::Constant(AnyConst::Array(&[]))),
Some(expr) => {
let value = self.compile_expression(expr, ctx)?;
// TODO Check if it matches the ctx type hint
values.push(value);
}
};
let element_ty = values[0].ty();
for expr in expr {
let value = self.compile_expression(expr, ctx)?;
if value.ty() != element_ty {
return Err(CompilationError {
kind: Kind::InvalidType,
message: format!(
"Expected type `{}`, found `{}`",
element_ty,
value.ty()
),
location: Location::Range {
file: self.source.clone(),
range: expr.range(),
},
cause: None,
});
}
values.push(value);
}
if values.iter().all(|v| matches!(v, Value::Constant(_))) {
let alloc = self.assembly.ctx().alloc();
return Ok(Value::Constant(AnyConst::Array(alloc.alloc_slice(
values.into_iter().map(|v| match v {
Value::Constant(c) => c,
_ => unreachable!(),
}),
))));
}
todo!()
}
Expr::Index(expr) => {
let IndexingExpr { value, index } = &**expr;
let value = self.compile_expression(value, ctx)?;
let mut index = self.compile_expression(index, ctx)?;
let builder = ctx.builder.as_mut().unwrap();
if index.flags().contains(ValueFlags::LValue) {
index = builder.load(index).unwrap();
}
// TODO Add support for custom indexing operations
if index.ty() != Type::USIZE {
return Err(CompilationError {
kind: Kind::InvalidType,
message: "Value is not of type `usize`".into(),
location: Location::Range {
file: self.source.clone(),
range: expr.index.range(),
},
cause: None,
});
}
if value.flags().contains(ValueFlags::LValue) {
let gep = builder.gep(value, index).unwrap();
return Ok(gep);
}
todo!("{:#?}", value.ty());
}
_ => todo!("{expr:#?}"),
}
}
@@ -342,7 +506,17 @@ impl<'l> Scope<'l> {
}
if !builder.current_block().has_termination() {
builder.ret(last_expr).unwrap();
match func.ty.ret_t {
Type::Void => builder.ret(None).unwrap(),
_ => {
if let Some(expr) = last_expr.as_mut() {
if expr.flags().contains(ValueFlags::LValue) {
*expr = builder.load(*expr).unwrap();
}
}
builder.ret(last_expr).unwrap()
}
};
}
builder.build().unwrap();