feat: compile unary op, compile asref, compile deref, reference arguments, avoid some temporaries on direct use

This commit is contained in:
Edgar 2024-02-17 17:19:36 +01:00
parent 6d31a9ea6f
commit 819a70d9f5
No known key found for this signature in database
GPG key ID: 70ADAE8F35904387
5 changed files with 265 additions and 100 deletions

View file

@ -31,20 +31,6 @@ pub struct PathExpr {
pub span: Span,
}
impl PathExpr {
pub fn get_full_path(&self) -> String {
let mut result = self.first.name.clone();
for path in &self.extra {
result.push('.');
match path {
PathSegment::Field(name) => result.push_str(&name.name),
PathSegment::Index { .. } => result.push_str("[]"),
}
}
result
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum PathSegment {
Field(Ident),
@ -109,6 +95,7 @@ pub struct LetStmt {
pub struct AssignStmt {
pub name: PathExpr,
pub value: Expression,
pub deref_times: usize,
pub span: Span,
}
@ -182,6 +169,8 @@ pub enum Expression {
FnCall(FnCallExpr),
Unary(UnaryOp, Box<Self>),
Binary(Box<Self>, BinaryOp, Box<Self>),
Deref(Box<Self>),
AsRef(Box<Self>, bool),
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]

View file

@ -18,7 +18,7 @@ use inkwell::{
AddressSpace,
};
use ir::{LocalKind, ModuleBody, ProgramBody, TypeInfo, ValueTree};
use llvm_sys::debuginfo::{LLVMDIFlagLValueReference, LLVMDIFlagPublic};
use llvm_sys::debuginfo::LLVMDIFlagPublic;
use tracing::{info, trace};
#[derive(Debug, Clone, Copy)]
@ -234,40 +234,7 @@ fn compile_fn_signature(ctx: &ModuleCompileCtx<'_, '_>, fn_id: DefId) {
// https://llvm.org/doxygen/group__LLVMCCoreTypes.html
/* starting from 1 to 80
allocalign allocptr alwaysinline builtin cold convergent disable_sanitizer_instrumentation fn_ret_thunk_extern hot
immarg inreg inlinehint jumptable minsize mustprogress naked nest noalias
nobuiltin nocallback nocapture nocf_check noduplicate nofree noimplicitfloat
noinline nomerge noprofile norecurse noredzone noreturn nosanitize_bounds
nosanitize_coverage nosync noundef nounwind nonlazybind nonnull null_pointer_is_valid
optforfuzzing optsize optnone presplitcoroutine readnone readonly returned returns_twice
signext safestack sanitize_address sanitize_hwaddress sanitize_memtag sanitize_memory
sanitize_thread shadowcallstack skipprofile speculatable speculative_load_hardening ssp
sspreq sspstrong strictfp swiftasync swifterror swiftself willreturn writeonly (67) zeroext byref byval elementtype inalloca
preallocated sret align 0 allockind(\"\") allocsize(0,0) dereferenceable(0) dereferenceable_or_null(0
*/
/*
// nounwind
fn_value.add_attribute(
inkwell::attributes::AttributeLoc::Function,
ctx.ctx.context.create_enum_attribute(36, 0),
);
// nonlazybind
fn_value.add_attribute(
inkwell::attributes::AttributeLoc::Function,
ctx.ctx.context.create_enum_attribute(37, 0),
);
// willreturn
fn_value.add_attribute(
inkwell::attributes::AttributeLoc::Function,
ctx.ctx.context.create_enum_attribute(66, 0),
);
*/
fn_value.set_call_conventions(0);
fn_value.set_call_conventions(0); // cconv
let (_, line, _col) = ctx
.ctx
@ -420,11 +387,29 @@ fn compile_fn(ctx: &ModuleCompileCtx, fn_id: DefId) -> Result<(), BuilderError>
match &stmt.kind {
ir::StatementKind::Assign(place, rvalue) => {
let local = &body.locals[place.local];
let mut local_ty = local.ty.clone();
let mut ptr = *locals.get(&place.local).unwrap();
for proj in &place.projection {
match proj {
ir::PlaceElem::Deref => {
ptr = ctx
.builder
.build_load(compile_basic_type(ctx, &local_ty), ptr, "deref")?
.into_pointer_value();
local_ty = match local_ty.kind {
ir::TypeKind::Ptr(inner) => *inner,
ir::TypeKind::Ref(_, inner) => *inner,
_ => unreachable!(),
}
}
ir::PlaceElem::Field { .. } => todo!(),
ir::PlaceElem::Index { .. } => todo!(),
}
}
let (value, _value_ty) = compile_rvalue(ctx, fn_id, &locals, rvalue)?;
let instruction = ctx
.builder
.build_store(*locals.get(&place.local).unwrap(), value)?;
let instruction = ctx.builder.build_store(ptr, value)?;
if let Some(_debug_name) = &local.debug_name {
let di_local = di_locals.get(&place.local).unwrap();
@ -558,6 +543,41 @@ fn compile_fn(ctx: &ModuleCompileCtx, fn_id: DefId) -> Result<(), BuilderError>
Ok(())
}
fn compile_unary_op<'ctx>(
ctx: &ModuleCompileCtx<'ctx, '_>,
fn_id: DefId,
locals: &HashMap<usize, PointerValue<'ctx>>,
op: ir::UnOp,
value: &ir::Operand,
) -> Result<(BasicValueEnum<'ctx>, TypeInfo), BuilderError> {
let (value, ty) = compile_load_operand(ctx, fn_id, locals, value)?;
let is_float = matches!(ty.kind, ir::TypeKind::Float(_));
Ok(match op {
ir::UnOp::Not => {
assert!(ty.kind.is_integer(), "must be a integer");
let value = ctx
.builder
.build_not(value.into_int_value(), "not")?
.as_basic_value_enum();
(value, ty)
}
ir::UnOp::Neg => {
let value = if is_float {
ctx.builder
.build_float_neg(value.into_float_value(), "negf")?
.as_basic_value_enum()
} else {
ctx.builder
.build_int_neg(value.into_int_value(), "negi")?
.as_basic_value_enum()
};
(value, ty)
}
})
}
fn compile_bin_op<'ctx>(
ctx: &ModuleCompileCtx<'ctx, '_>,
fn_id: DefId,
@ -908,10 +928,40 @@ fn compile_rvalue<'ctx>(
) -> Result<(BasicValueEnum<'ctx>, TypeInfo), BuilderError> {
Ok(match rvalue {
ir::RValue::Use(op) => compile_load_operand(ctx, fn_id, locals, op)?,
ir::RValue::Ref(_, _) => todo!(),
ir::RValue::Ref(_mutable, op) => match op {
ir::Operand::Copy(_) => todo!(),
ir::Operand::Move(place) => {
let mut ptr = *locals.get(&place.local).unwrap();
let mut local_ty = {
let body = ctx.ctx.program.functions.get(&fn_id).unwrap();
body.locals[place.local].ty.clone()
};
for proj in &place.projection {
match proj {
ir::PlaceElem::Deref => {
ptr = ctx
.builder
.build_load(compile_basic_type(ctx, &local_ty), ptr, "deref")?
.into_pointer_value();
local_ty = match local_ty.kind {
ir::TypeKind::Ptr(inner) => *inner,
ir::TypeKind::Ref(_, inner) => *inner,
_ => unreachable!(),
}
}
ir::PlaceElem::Field { .. } => todo!(),
ir::PlaceElem::Index { .. } => todo!(),
}
}
(ptr.as_basic_value_enum(), local_ty)
}
ir::Operand::Constant(_) => todo!("references to constants not yet implemented"),
},
ir::RValue::BinOp(op, lhs, rhs) => compile_bin_op(ctx, fn_id, locals, *op, lhs, rhs)?,
ir::RValue::LogicOp(_, _, _) => todo!(),
ir::RValue::UnOp(_, _) => todo!(),
ir::RValue::UnOp(op, value) => compile_unary_op(ctx, fn_id, locals, *op, value)?,
})
}
@ -933,8 +983,29 @@ fn compile_load_operand<'ctx>(
)
}
ir::Operand::Move(place) => {
let pointee_ty = compile_basic_type(ctx, &body.locals[place.local].ty);
let ptr = *locals.get(&place.local).unwrap();
let mut ptr = *locals.get(&place.local).unwrap();
let mut local_ty = body.locals[place.local].ty.clone();
for proj in &place.projection {
match proj {
ir::PlaceElem::Deref => {
ptr = ctx
.builder
.build_load(compile_basic_type(ctx, &local_ty), ptr, "deref")?
.into_pointer_value();
local_ty = match local_ty.kind {
ir::TypeKind::Ptr(inner) => *inner,
ir::TypeKind::Ref(_, inner) => *inner,
_ => unreachable!(),
}
}
ir::PlaceElem::Field { .. } => todo!(),
ir::PlaceElem::Index { .. } => todo!(),
}
}
let pointee_ty = compile_basic_type(ctx, &local_ty);
(
ctx.builder.build_load(pointee_ty, ptr, "")?,
body.locals[place.local].ty.clone(),

View file

@ -208,10 +208,14 @@ pub enum TypeKind {
}
impl TypeKind {
pub fn is_unit(&self) -> bool {
pub const fn is_unit(&self) -> bool {
matches!(self, Self::Unit)
}
pub const fn is_integer(&self) -> bool {
matches!(self, Self::Int(_) | Self::Uint(_))
}
pub fn get_falsy_value(&self) -> ValueTree {
match self {
Self::Bool => ValueTree::Leaf(ConstValue::Bool(false)),

View file

@ -5,8 +5,8 @@ use common::{BodyBuilder, BuildCtx};
use edlang_ast as ast;
use edlang_ir as ir;
use ir::{
BasicBlock, Body, DefId, Local, LocalKind, Operand, Place, ProgramBody, Statement,
StatementKind, SwitchTarget, Terminator, TypeInfo, TypeKind,
BasicBlock, Body, DefId, Local, LocalKind, Operand, Place, PlaceElem, ProgramBody, RValue,
Statement, StatementKind, SwitchTarget, Terminator, TypeInfo, TypeKind,
};
use tracing::trace;
@ -371,10 +371,40 @@ fn lower_let(builder: &mut BodyBuilder, info: &ast::LetStmt) {
}
fn lower_assign(builder: &mut BodyBuilder, info: &ast::AssignStmt) {
let local = *builder.name_to_local.get(&info.name.first.name).unwrap();
let ty = builder.body.locals[local].ty.clone();
let (rvalue, _ty) = lower_expr(builder, &info.value, Some(&ty.kind));
let (place, _ty) = lower_path(builder, &info.name);
let (mut place, mut ty) = lower_path(builder, &info.name);
if let Some(PlaceElem::Deref) = place.projection.last() {
match &ty {
TypeKind::Ptr(inner) => {
ty = inner.kind.clone();
}
TypeKind::Ref(is_mut, inner) => {
if !is_mut {
panic!("trying to mutate non mut ref");
}
ty = inner.kind.clone();
}
_ => unreachable!(),
}
}
for _ in 0..info.deref_times {
match &ty {
TypeKind::Ptr(inner) => {
ty = inner.kind.clone();
}
TypeKind::Ref(is_mut, inner) => {
if !is_mut {
panic!("trying to mutate non mut ref");
}
ty = inner.kind.clone();
}
_ => unreachable!(),
}
place.projection.push(PlaceElem::Deref);
}
let (rvalue, _ty) = lower_expr(builder, &info.value, Some(&ty));
builder.statements.push(Statement {
span: Some(info.name.first.span),
@ -426,6 +456,8 @@ fn find_expr_type(builder: &mut BodyBuilder, info: &ast::Expression) -> Option<T
find_expr_type(builder, lhs).or(find_expr_type(builder, rhs))?
}
}
ast::Expression::Deref(_) => todo!(),
ast::Expression::AsRef(_, _) => todo!(),
})
}
@ -447,6 +479,50 @@ fn lower_expr(
ast::Expression::Binary(lhs, op, rhs) => {
lower_binary_expr(builder, lhs, op, rhs, type_hint)
}
ast::Expression::Deref(_) => todo!(),
ast::Expression::AsRef(inner, mutable) => {
let type_hint = match type_hint {
Some(inner) => match inner {
TypeKind::Ref(_, inner) => Some(&inner.kind),
_ => unreachable!(),
},
None => None,
};
let (mut value, ty) = lower_expr(builder, inner, type_hint);
// check if its a use directly, to avoid a temporary.
value = match value {
RValue::Use(op) => RValue::Ref(*mutable, op),
value => {
let inner_local = builder.add_local(Local::temp(ty.clone()));
let inner_place = Place {
local: inner_local,
projection: Default::default(),
};
builder.statements.push(Statement {
span: None,
kind: StatementKind::StorageLive(inner_local),
});
builder.statements.push(Statement {
span: None,
kind: StatementKind::Assign(inner_place.clone(), value),
});
RValue::Ref(*mutable, Operand::Move(inner_place))
}
};
let ty = TypeKind::Ref(
*mutable,
Box::new(TypeInfo {
span: None,
kind: ty,
}),
);
(value, ty)
}
}
}
@ -459,7 +535,6 @@ fn lower_binary_expr(
) -> (ir::RValue, TypeKind) {
trace!("lowering binary op: {:?}", op);
// todo: if lhs or rhs is a simple place, dont make another temporary?
let (lhs, lhs_ty) = if type_hint.is_none() {
let ty = find_expr_type(builder, lhs)
.unwrap_or_else(|| find_expr_type(builder, rhs).expect("cant find type"));
@ -474,39 +549,49 @@ fn lower_binary_expr(
lower_expr(builder, rhs, type_hint)
};
let lhs_local = builder.add_local(Local::temp(lhs_ty.clone()));
let rhs_local = builder.add_local(Local::temp(rhs_ty.clone()));
let lhs_place = Place {
local: lhs_local,
projection: Default::default(),
};
let rhs_place = Place {
local: rhs_local,
projection: Default::default(),
let lhs = match lhs {
RValue::Use(op) => op,
lhs => {
let lhs_local = builder.add_local(Local::temp(lhs_ty.clone()));
let lhs_place = Place {
local: lhs_local,
projection: Default::default(),
};
builder.statements.push(Statement {
span: None,
kind: StatementKind::StorageLive(lhs_local),
});
builder.statements.push(Statement {
span: None,
kind: StatementKind::Assign(lhs_place.clone(), lhs),
});
Operand::Move(lhs_place)
}
};
builder.statements.push(Statement {
span: None,
kind: StatementKind::StorageLive(lhs_local),
});
let rhs = match rhs {
RValue::Use(op) => op,
rhs => {
let rhs_local = builder.add_local(Local::temp(rhs_ty.clone()));
let rhs_place = Place {
local: rhs_local,
projection: Default::default(),
};
builder.statements.push(Statement {
span: None,
kind: StatementKind::Assign(lhs_place.clone(), lhs),
});
builder.statements.push(Statement {
span: None,
kind: StatementKind::StorageLive(rhs_local),
});
builder.statements.push(Statement {
span: None,
kind: StatementKind::StorageLive(rhs_local),
});
builder.statements.push(Statement {
span: None,
kind: StatementKind::Assign(rhs_place.clone(), rhs),
});
let lhs = Operand::Move(lhs_place);
let rhs = Operand::Move(rhs_place);
builder.statements.push(Statement {
span: None,
kind: StatementKind::Assign(rhs_place.clone(), rhs),
});
Operand::Move(rhs_place)
}
};
match op {
ast::BinaryOp::Arith(op, _) => (
@ -791,10 +876,19 @@ fn lower_path(builder: &mut BodyBuilder, info: &ast::PathExpr) -> (ir::Place, Ty
.expect("local not found");
let ty = builder.body.locals[local].ty.kind.clone();
let projection = Vec::new();
for extra in &info.extra {
match extra {
ast::PathSegment::Field(_) => todo!(),
ast::PathSegment::Index { .. } => todo!(),
}
}
(
Place {
local,
projection: Default::default(), // todo, field array deref
projection: projection.into(), // todo, field array deref
},
ty,
)

View file

@ -160,7 +160,7 @@ pub(crate) PathExpr: ast::PathExpr = {
first,
extra: extra.unwrap_or(vec![]),
span: ast::Span::new(lo, hi),
}
},
}
pub PathSegments: Vec<ast::PathSegment> = {
@ -215,9 +215,10 @@ pub(crate) LetStmt: ast::LetStmt = {
}
pub(crate) AssignStmt: ast::AssignStmt = {
<lo:@L> <name:PathExpr> "=" <value:Expression> <hi:@R> => ast::AssignStmt {
<lo:@L> <deref:"*"*> <name:PathExpr> "=" <value:Expression> <hi:@R> => ast::AssignStmt {
name,
value,
deref_times: deref.len(),
span: ast::Span::new(lo, hi),
},
}
@ -268,32 +269,38 @@ pub(crate) IfStmt: ast::IfStmt = {
}
pub(crate) Term: ast::Expression = {
#[precedence(level="0")]
<ValueExpr> => ast::Expression::Value(<>),
<FnCallExpr> => ast::Expression::FnCall(<>),
#[precedence(level="2")] #[assoc(side="left")]
"(" <Expression> ")",
}
pub(crate) Expression: ast::Expression = {
#[precedence(level="0")]
<Term>,
#[precedence(level="1")] #[assoc(side="left")]
"&" "mut" <Expression> => ast::Expression::AsRef(Box::new(<>), true),
"&" <Expression> => ast::Expression::AsRef(Box::new(<>), false),
"*" <Expression> => ast::Expression::Deref(Box::new(<>)),
<op:UnaryOp> <rhs:Expression> => ast::Expression::Unary(
op,
Box::new(rhs)
),
// <op:UnaryOp> <e:Expression> => ast::Expression::UnaryOp(op, Box::new(e)),
#[precedence(level="1")] #[assoc(side="left")]
#[precedence(level="2")] #[assoc(side="left")]
<lhs:Expression> <op:BinaryFirstLvlOp> <rhs:Expression> => ast::Expression::Binary(
Box::new(lhs),
op,
Box::new(rhs)
),
#[precedence(level="2")] #[assoc(side="left")]
#[precedence(level="3")] #[assoc(side="left")]
<lhs:Expression> <op:BinarySecondLvlOp> <rhs:Expression> => ast::Expression::Binary(
Box::new(lhs),
op,
Box::new(rhs)
),
#[precedence(level="3")] #[assoc(side="left")]
#[precedence(level="4")] #[assoc(side="left")]
<lhs:Expression> <op:BinaryThirdLvlOp> <rhs:Expression> => ast::Expression::Binary(
Box::new(lhs),
op,