diff --git a/lib/edlang_ast/src/lib.rs b/lib/edlang_ast/src/lib.rs index ba9a6fd23..d70c7643f 100644 --- a/lib/edlang_ast/src/lib.rs +++ b/lib/edlang_ast/src/lib.rs @@ -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), Binary(Box, BinaryOp, Box), + Deref(Box), + AsRef(Box, bool), } #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] diff --git a/lib/edlang_codegen_llvm/src/codegen.rs b/lib/edlang_codegen_llvm/src/codegen.rs index 9f24a7ed7..f4d433412 100644 --- a/lib/edlang_codegen_llvm/src/codegen.rs +++ b/lib/edlang_codegen_llvm/src/codegen.rs @@ -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>, + 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(), diff --git a/lib/edlang_ir/src/lib.rs b/lib/edlang_ir/src/lib.rs index 1bc3cfb06..78adedc7c 100644 --- a/lib/edlang_ir/src/lib.rs +++ b/lib/edlang_ir/src/lib.rs @@ -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)), diff --git a/lib/edlang_lowering/src/lib.rs b/lib/edlang_lowering/src/lib.rs index cbf91f6a3..2e3b242a5 100644 --- a/lib/edlang_lowering/src/lib.rs +++ b/lib/edlang_lowering/src/lib.rs @@ -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 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, ) diff --git a/lib/edlang_parser/src/grammar.lalrpop b/lib/edlang_parser/src/grammar.lalrpop index 429a84764..ad6658a62 100644 --- a/lib/edlang_parser/src/grammar.lalrpop +++ b/lib/edlang_parser/src/grammar.lalrpop @@ -160,7 +160,7 @@ pub(crate) PathExpr: ast::PathExpr = { first, extra: extra.unwrap_or(vec![]), span: ast::Span::new(lo, hi), - } + }, } pub PathSegments: Vec = { @@ -215,9 +215,10 @@ pub(crate) LetStmt: ast::LetStmt = { } pub(crate) AssignStmt: ast::AssignStmt = { - "=" => ast::AssignStmt { + "=" => 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")] => ast::Expression::Value(<>), => ast::Expression::FnCall(<>), + #[precedence(level="2")] #[assoc(side="left")] "(" ")", } pub(crate) Expression: ast::Expression = { #[precedence(level="0")] , + #[precedence(level="1")] #[assoc(side="left")] + "&" "mut" => ast::Expression::AsRef(Box::new(<>), true), + "&" => ast::Expression::AsRef(Box::new(<>), false), + "*" => ast::Expression::Deref(Box::new(<>)), => ast::Expression::Unary( op, Box::new(rhs) ), - // => ast::Expression::UnaryOp(op, Box::new(e)), - #[precedence(level="1")] #[assoc(side="left")] + + #[precedence(level="2")] #[assoc(side="left")] => ast::Expression::Binary( Box::new(lhs), op, Box::new(rhs) ), - #[precedence(level="2")] #[assoc(side="left")] + #[precedence(level="3")] #[assoc(side="left")] => ast::Expression::Binary( Box::new(lhs), op, Box::new(rhs) ), - #[precedence(level="3")] #[assoc(side="left")] + #[precedence(level="4")] #[assoc(side="left")] => ast::Expression::Binary( Box::new(lhs), op,