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, 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)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum PathSegment { pub enum PathSegment {
Field(Ident), Field(Ident),
@ -109,6 +95,7 @@ pub struct LetStmt {
pub struct AssignStmt { pub struct AssignStmt {
pub name: PathExpr, pub name: PathExpr,
pub value: Expression, pub value: Expression,
pub deref_times: usize,
pub span: Span, pub span: Span,
} }
@ -182,6 +169,8 @@ pub enum Expression {
FnCall(FnCallExpr), FnCall(FnCallExpr),
Unary(UnaryOp, Box<Self>), Unary(UnaryOp, Box<Self>),
Binary(Box<Self>, BinaryOp, Box<Self>), Binary(Box<Self>, BinaryOp, Box<Self>),
Deref(Box<Self>),
AsRef(Box<Self>, bool),
} }
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]

View file

@ -18,7 +18,7 @@ use inkwell::{
AddressSpace, AddressSpace,
}; };
use ir::{LocalKind, ModuleBody, ProgramBody, TypeInfo, ValueTree}; use ir::{LocalKind, ModuleBody, ProgramBody, TypeInfo, ValueTree};
use llvm_sys::debuginfo::{LLVMDIFlagLValueReference, LLVMDIFlagPublic}; use llvm_sys::debuginfo::LLVMDIFlagPublic;
use tracing::{info, trace}; use tracing::{info, trace};
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
@ -234,40 +234,7 @@ fn compile_fn_signature(ctx: &ModuleCompileCtx<'_, '_>, fn_id: DefId) {
// https://llvm.org/doxygen/group__LLVMCCoreTypes.html // https://llvm.org/doxygen/group__LLVMCCoreTypes.html
/* starting from 1 to 80 fn_value.set_call_conventions(0); // cconv
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);
let (_, line, _col) = ctx let (_, line, _col) = ctx
.ctx .ctx
@ -420,11 +387,29 @@ fn compile_fn(ctx: &ModuleCompileCtx, fn_id: DefId) -> Result<(), BuilderError>
match &stmt.kind { match &stmt.kind {
ir::StatementKind::Assign(place, rvalue) => { ir::StatementKind::Assign(place, rvalue) => {
let local = &body.locals[place.local]; 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 (value, _value_ty) = compile_rvalue(ctx, fn_id, &locals, rvalue)?;
let instruction = ctx let instruction = ctx.builder.build_store(ptr, value)?;
.builder
.build_store(*locals.get(&place.local).unwrap(), value)?;
if let Some(_debug_name) = &local.debug_name { if let Some(_debug_name) = &local.debug_name {
let di_local = di_locals.get(&place.local).unwrap(); let di_local = di_locals.get(&place.local).unwrap();
@ -558,6 +543,41 @@ fn compile_fn(ctx: &ModuleCompileCtx, fn_id: DefId) -> Result<(), BuilderError>
Ok(()) 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>( fn compile_bin_op<'ctx>(
ctx: &ModuleCompileCtx<'ctx, '_>, ctx: &ModuleCompileCtx<'ctx, '_>,
fn_id: DefId, fn_id: DefId,
@ -908,10 +928,40 @@ fn compile_rvalue<'ctx>(
) -> Result<(BasicValueEnum<'ctx>, TypeInfo), BuilderError> { ) -> Result<(BasicValueEnum<'ctx>, TypeInfo), BuilderError> {
Ok(match rvalue { Ok(match rvalue {
ir::RValue::Use(op) => compile_load_operand(ctx, fn_id, locals, op)?, 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::BinOp(op, lhs, rhs) => compile_bin_op(ctx, fn_id, locals, *op, lhs, rhs)?,
ir::RValue::LogicOp(_, _, _) => todo!(), 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) => { ir::Operand::Move(place) => {
let pointee_ty = compile_basic_type(ctx, &body.locals[place.local].ty); let mut ptr = *locals.get(&place.local).unwrap();
let 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, "")?, ctx.builder.build_load(pointee_ty, ptr, "")?,
body.locals[place.local].ty.clone(), body.locals[place.local].ty.clone(),

View file

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

View file

@ -5,8 +5,8 @@ use common::{BodyBuilder, BuildCtx};
use edlang_ast as ast; use edlang_ast as ast;
use edlang_ir as ir; use edlang_ir as ir;
use ir::{ use ir::{
BasicBlock, Body, DefId, Local, LocalKind, Operand, Place, ProgramBody, Statement, BasicBlock, Body, DefId, Local, LocalKind, Operand, Place, PlaceElem, ProgramBody, RValue,
StatementKind, SwitchTarget, Terminator, TypeInfo, TypeKind, Statement, StatementKind, SwitchTarget, Terminator, TypeInfo, TypeKind,
}; };
use tracing::trace; 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) { fn lower_assign(builder: &mut BodyBuilder, info: &ast::AssignStmt) {
let local = *builder.name_to_local.get(&info.name.first.name).unwrap(); let (mut place, mut ty) = lower_path(builder, &info.name);
let ty = builder.body.locals[local].ty.clone();
let (rvalue, _ty) = lower_expr(builder, &info.value, Some(&ty.kind)); if let Some(PlaceElem::Deref) = place.projection.last() {
let (place, _ty) = lower_path(builder, &info.name); 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 { builder.statements.push(Statement {
span: Some(info.name.first.span), 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))? 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) => { ast::Expression::Binary(lhs, op, rhs) => {
lower_binary_expr(builder, lhs, op, rhs, type_hint) 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) { ) -> (ir::RValue, TypeKind) {
trace!("lowering binary op: {:?}", op); 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 (lhs, lhs_ty) = if type_hint.is_none() {
let ty = find_expr_type(builder, lhs) let ty = find_expr_type(builder, lhs)
.unwrap_or_else(|| find_expr_type(builder, rhs).expect("cant find type")); .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) lower_expr(builder, rhs, type_hint)
}; };
let lhs_local = builder.add_local(Local::temp(lhs_ty.clone())); let lhs = match lhs {
let rhs_local = builder.add_local(Local::temp(rhs_ty.clone())); RValue::Use(op) => op,
let lhs_place = Place { lhs => {
local: lhs_local, let lhs_local = builder.add_local(Local::temp(lhs_ty.clone()));
projection: Default::default(), let lhs_place = Place {
}; local: lhs_local,
let rhs_place = Place { projection: Default::default(),
local: rhs_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 { let rhs = match rhs {
span: None, RValue::Use(op) => op,
kind: StatementKind::StorageLive(lhs_local), 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 { builder.statements.push(Statement {
span: None, span: None,
kind: StatementKind::Assign(lhs_place.clone(), lhs), kind: StatementKind::StorageLive(rhs_local),
}); });
builder.statements.push(Statement { builder.statements.push(Statement {
span: None, span: None,
kind: StatementKind::StorageLive(rhs_local), kind: StatementKind::Assign(rhs_place.clone(), rhs),
}); });
Operand::Move(rhs_place)
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);
match op { match op {
ast::BinaryOp::Arith(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"); .expect("local not found");
let ty = builder.body.locals[local].ty.kind.clone(); 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 { Place {
local, local,
projection: Default::default(), // todo, field array deref projection: projection.into(), // todo, field array deref
}, },
ty, ty,
) )

View file

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