Casting, GEPs, partial LLVM backend
This commit is contained in:
@@ -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
@@ -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
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user