From a130ef2cf691c400190ded5f13ef7f6029b2576b Mon Sep 17 00:00:00 2001 From: zxq5 Date: Tue, 25 Aug 2026 00:49:23 +0100 Subject: [PATCH] updated compiler IR codegen. still broken but getting there. next job is figuring out how we handle Lvalues. --- resources/main.dsa | 189 ++++-------- tooling/dscs_compiler/src/backend/codegen.rs | 253 ++++++++-------- .../src/backend/ir_lowering_codegen.rs | 275 ++++++++++++------ tooling/dscs_compiler/src/frontend/parser.rs | 239 +++++++-------- tooling/dscs_compiler/src/model/ast.rs | 113 +++---- tooling/dscs_compiler/src/model/dsa_ir.rs | 42 ++- 6 files changed, 555 insertions(+), 556 deletions(-) diff --git a/resources/main.dsa b/resources/main.dsa index 73ea3ff..12d35da 100644 --- a/resources/main.dsa +++ b/resources/main.dsa @@ -1,138 +1,51 @@ -// GENERATED BY DSC COMPILER -// Generated at 2026-08-21 19:34:30 - -// Imports -include alloc: "./lib/memory/block_alloc.dsa" -include print: "./lib/io/print.dsa" - -// Globals & Reserved Memory - -// Entry Point -dw stack: 0x010000 -db message: "Process Exited with code:" -_init: - ldw stack, bpr, 0 - mov bpr, spr - push zero - call main - call print::print_newline - lwi message, rg0 - push rg0 - call print::print - pop zero - call print::print_hex_word - pop zero - hlt - -// Return -_ret: - mov bpr, spr - pop bpr - ret - -db str_8: "successful free of ptr" -// fn main() -> u32 -main: - push bpr - mov spr, bpr - lli 32, rg0 - lli 64, rg1 - // push arg 1 - push rg0 - // push arg 0 - push rg1 - call alloc::init - pop rg2 - pop zero - push rg2 - // push arg 0 - push rg2 - call print::print_hex_word - pop zero - call print::print_newline - ldw spr, rg0, 0 - stw rg0, spr, 0 - // push arg 0 - push rg0 - call alloc::alloc - pop rg1 - push rg1 - // push arg 0 - push rg1 - call print::print_hex_word - pop zero - lli 200, rg0 - ldw spr, rg1, 0 - stw rg0, rg1, 0 - stw rg1, spr, 0 - call print::print_newline - ldw spr, rg0, 4 - stw rg0, spr, 4 - // push arg 0 - push rg0 - call alloc::alloc - pop rg2 - push rg2 - // push arg 0 - push rg2 - call print::print_hex_word - pop zero - call print::print_newline - ldw spr, rg0, 4 - ldw rg0, rg2, 0 - stw rg0, spr, 4 - // push arg 0 - push rg2 - call print::print_num - pop zero - ldw spr, rg2, 4 - stw rg2, spr, 4 - addi spr, 4, rg3 - ldw spr, rg2, 8 - stw rg2, spr, 8 - // push arg 1 - push rg3 - // push arg 0 - push rg2 - call alloc::free - pop zero - pop zero - ldw spr, rg2, 8 - stw rg2, spr, 8 - // push arg 0 - push rg2 - call alloc::alloc - pop rg3 - push rg3 - call print::print_newline - ldw spr, rg2, 0 - stw rg2, spr, 0 - // push arg 0 - push rg2 - call print::print_hex_word - pop zero - call print::print_newline - ldw spr, rg2, 8 - stw rg2, spr, 8 - // push arg 0 - push rg2 - call print::print_hex_word - pop zero - ldw spr, rg2, 8 - lli 0, rg4 - ieq rg2, rg4, rg5 - jez rg5, _else_6 -_then_5: - lwi str_8, rg4 - stw rg2, spr, 8 - // push arg 0 - push rg4 - call print::print - pop zero - jmp _end_7 -_else_6: - nop -_end_7: - lli 0, rg4 - stw rg4, bpr, 8 - jmp _ret \ No newline at end of file +fn main() { +__main_0: + v1 = literal 64 + v2 = literal 32 + v0_allocator = alloc::init(v1, v2) + v3 = copy v0_allocator + _v = print::print_hex_word(v3) + _v = print::print_newline() + v5 = copy v0_allocator + v4_ptr = alloc::alloc(v5) + v6 = copy v4_ptr + _v = print::print_hex_word(v6) + v7 = copy v4_ptr + v8 = literal 200 + store v7, v8 + _v = print::print_newline() + v10 = copy v0_allocator + v9_p2 = alloc::alloc(v10) + v11 = copy v9_p2 + _v = print::print_hex_word(v11) + _v = print::print_newline() + v13 = copy v4_ptr + v12 = load v13 + _v = print::print_num(v12) + v14 = copy v0_allocator + v16 = copy v4_ptr + no-op + _v = alloc::free(v14, v15) + v18 = copy v0_allocator + v17_ptr3 = alloc::alloc(v18) + _v = print::print_newline() + v19 = copy v17_ptr3 + _v = print::print_hex_word(v19) + _v = print::print_newline() + v20 = copy v4_ptr + _v = print::print_hex_word(v20) + v22 = copy v4_ptr + v23 = literal 0 + v21 = v22 == v23 + if v21 then __then_2 else __else_2 +__then_2: + _v = print::print(v24) + jump __endif_2 +__else_2: + jump __endif_2 +__endif_2: + v25 = literal 0 + return v25 +__unreachable_3: + return +} diff --git a/tooling/dscs_compiler/src/backend/codegen.rs b/tooling/dscs_compiler/src/backend/codegen.rs index 1e8de30..b2b9eb8 100644 --- a/tooling/dscs_compiler/src/backend/codegen.rs +++ b/tooling/dscs_compiler/src/backend/codegen.rs @@ -2,8 +2,8 @@ use super::instruction::InsBlock as IB; use super::registers::RegisterAllocator; use crate::model::CompilerError; use crate::model::ast::{ - AssignmentOperator, BinaryOperator, Call, ConstExpr, Declaration, Dependency, Expression, - Number, Program, Statement, TypeId, UnaryOperator, Variable, + BinaryOperator, Call, ConstExpr, Declaration, Dependency, Number, Program, RValue, Statement, + TypeId, UnaryOperator, Variable, }; use crate::model::instruction::Comparison::{ Equal, Greater, GreaterEqual, Less, LessEqual, NotEqual, @@ -240,7 +240,7 @@ impl CodeGenerator { let mut code = IB::new(); match stmt { - Statement::Declaration { var, value } => { + Statement::Declaration { var, value, .. } => { if let Some(expr) = value { // Evaluate expression let (result_reg, expr_code) = @@ -263,116 +263,117 @@ impl CodeGenerator { Statement::Continue => unimplemented!("need scope tracking first!"), Statement::Defer(_func) => unimplemented!("we need scope tracking first!"), - Statement::PtrWrite { ptr, value } => { - let (result_reg, expr_code) = self.generate_expression(value, true, func_body)?; - code.append(expr_code); + _ => todo!(), - let (ptr_reg, ptr_code) = self.generate_expression(ptr, true, func_body)?; - code.append(ptr_code); - - code.push(I::stw_reg(result_reg, ptr_reg)); - - self.allocator.free_temp(result_reg); - self.allocator.free_temp(ptr_reg); - } - - Statement::Assign { - varname, - value, - operator, - } => { - // Evaluate expression - let (result_reg, expr_code) = self.generate_expression(value, true, func_body)?; - code.append(expr_code); - - if *operator == AssignmentOperator::Assign { - // Check if this is a global variable - if self.is_global(varname) { - // Store to global label - code.push(I::stw_label(result_reg, varname.clone())) - } else { - // Store result in local variable - let store_code = self.allocator.store_var(varname, &result_reg); - code.append(store_code); - } - - // Free temporary register - self.allocator.free_temp(result_reg); - - return Ok(code); - } - - // for more complex assignment cases we need an intermediate register. - let (temp_reg, temp_code) = self.allocator.alloc_temp()?; - code.append(temp_code); - - // fetch the value of the variable - let var_reg = if self.is_global(varname) { - let instruction = I::ldw_label(varname.clone(), temp_reg); - code.push(instruction); - temp_reg - } else { - let (rg, block) = self.allocator.load_var(varname)?; - code.append(block); - rg - }; - - let assign_code = match operator { - AssignmentOperator::Assign => { - unreachable!("assignment was already checked earlier.") - } - AssignmentOperator::AddAssign => I::add(var_reg, result_reg, temp_reg), - AssignmentOperator::SubAssign => I::sub(var_reg, result_reg, temp_reg), - AssignmentOperator::MulAssign => { - return Err(CompilerError::Unimplemented( - "TODO: implement multiplication for assignment".to_string(), - )); - } - AssignmentOperator::DivAssign => { - return Err(CompilerError::Unimplemented( - "TODO: write proper div function for DSA".to_string(), - )); - } - AssignmentOperator::ModAssign => { - return Err(CompilerError::Unimplemented( - "TODO: write proper mod function for DSA".to_string(), - )); - } - AssignmentOperator::AndAssign => I::and(var_reg, result_reg, temp_reg), - AssignmentOperator::OrAssign => I::or(var_reg, result_reg, temp_reg), - AssignmentOperator::XorAssign => I::xor(var_reg, result_reg, temp_reg), - AssignmentOperator::LeftShiftAssign => { - // this is only useful if we optimise out the register allocation - // inside value. - // if let Expression::Number { value, .. } = *value { - // I::shl(var_reg, value, temp_reg) - // } - I::shl(var_reg, result_reg, 0, temp_reg) - } - AssignmentOperator::RightShiftAssign => { - // this is only useful if we optimise out the register allocation - // if let Expression::Number { value, .. } = *value { - // I::shr(var_reg, value, temp_reg) - // } - I::shr(var_reg, result_reg, 0, temp_reg) - } - }; - code.push(assign_code); - - // Check if this is a global variable - if self.is_global(varname) { - // Store to global label - code.push(I::stw_label(temp_reg, varname.clone())) - } else { - // Store result in local variable - let store_code = self.allocator.store_var(varname, &temp_reg); - code.append(store_code); - } - - self.allocator.free_temp(result_reg); - self.allocator.free_temp(temp_reg); - } + // Statement::PtrWrite { ptr, value } => { + // let (result_reg, expr_code) = self.generate_expression(value, true, func_body)?; + // code.append(expr_code); + // + // let (ptr_reg, ptr_code) = self.generate_expression(ptr, true, func_body)?; + // code.append(ptr_code); + // + // code.push(I::stw_reg(result_reg, ptr_reg)); + // + // self.allocator.free_temp(result_reg); + // self.allocator.free_temp(ptr_reg); + // } + // Statement::Assign { + // varname, + // value, + // operator, + // } => { + // // Evaluate expression + // let (result_reg, expr_code) = self.generate_expression(value, true, func_body)?; + // code.append(expr_code); + // + // if *operator == AssignmentOperator::Assign { + // // Check if this is a global variable + // if self.is_global(varname) { + // // Store to global label + // code.push(I::stw_label(result_reg, varname.clone())) + // } else { + // // Store result in local variable + // let store_code = self.allocator.store_var(varname, &result_reg); + // code.append(store_code); + // } + // + // // Free temporary register + // self.allocator.free_temp(result_reg); + // + // return Ok(code); + // } + // + // // for more complex assignment cases we need an intermediate register. + // let (temp_reg, temp_code) = self.allocator.alloc_temp()?; + // code.append(temp_code); + // + // // fetch the value of the variable + // let var_reg = if self.is_global(varname) { + // let instruction = I::ldw_label(varname.clone(), temp_reg); + // code.push(instruction); + // temp_reg + // } else { + // let (rg, block) = self.allocator.load_var(varname)?; + // code.append(block); + // rg + // }; + // + // let assign_code = match operator { + // AssignmentOperator::Assign => { + // unreachable!("assignment was already checked earlier.") + // } + // AssignmentOperator::AddAssign => I::add(var_reg, result_reg, temp_reg), + // AssignmentOperator::SubAssign => I::sub(var_reg, result_reg, temp_reg), + // AssignmentOperator::MulAssign => { + // return Err(CompilerError::Unimplemented( + // "TODO: implement multiplication for assignment".to_string(), + // )); + // } + // AssignmentOperator::DivAssign => { + // return Err(CompilerError::Unimplemented( + // "TODO: write proper div function for DSA".to_string(), + // )); + // } + // AssignmentOperator::ModAssign => { + // return Err(CompilerError::Unimplemented( + // "TODO: write proper mod function for DSA".to_string(), + // )); + // } + // AssignmentOperator::AndAssign => I::and(var_reg, result_reg, temp_reg), + // AssignmentOperator::OrAssign => I::or(var_reg, result_reg, temp_reg), + // AssignmentOperator::XorAssign => I::xor(var_reg, result_reg, temp_reg), + // AssignmentOperator::LeftShiftAssign => { + // // this is only useful if we optimise out the register allocation + // // inside value. + // // if let Expression::Number { value, .. } = *value { + // // I::shl(var_reg, value, temp_reg) + // // } + // I::shl(var_reg, result_reg, 0, temp_reg) + // } + // AssignmentOperator::RightShiftAssign => { + // // this is only useful if we optimise out the register allocation + // // if let Expression::Number { value, .. } = *value { + // // I::shr(var_reg, value, temp_reg) + // // } + // I::shr(var_reg, result_reg, 0, temp_reg) + // } + // }; + // code.push(assign_code); + // + // // Check if this is a global variable + // if self.is_global(varname) { + // // Store to global label + // code.push(I::stw_label(temp_reg, varname.clone())) + // } else { + // // Store result in local variable + // let store_code = self.allocator.store_var(varname, &temp_reg); + // code.append(store_code); + // } + // + // self.allocator.free_temp(result_reg); + // self.allocator.free_temp(temp_reg); + // } Statement::Return(expr) => { if let Some(e) = expr { let (result_reg, expr_code) = self.generate_expression(e, true, func_body)?; @@ -479,16 +480,16 @@ impl CodeGenerator { // Returns (register containing result, assembly code) fn generate_expression( &mut self, - expr: &Expression, + expr: &RValue, use_result: bool, func_body: &mut IB, ) -> Result<(Register, IB), CompilerError> { let mut code = IB::new(); match expr { - Expression::Empty => Ok((Register::Null, code)), + RValue::Empty => Ok((Register::Null, code)), - Expression::Number(n) => match n { + RValue::Number(n) => match n { Number::Signed(value, _) => { let (reg, alloc_code) = self.allocator.alloc_temp()?; code.append(alloc_code); @@ -509,7 +510,7 @@ impl CodeGenerator { } }, - Expression::CharLiteral(value) => { + RValue::CharLiteral(value) => { let (reg, alloc_code) = self.allocator.alloc_temp()?; code.append(alloc_code); @@ -520,7 +521,7 @@ impl CodeGenerator { Ok((reg, code)) } - Expression::StringLiteral(value) => { + RValue::StringLiteral(value) => { let (reg, alloc_code) = self.allocator.alloc_temp()?; code.append(alloc_code); @@ -535,14 +536,14 @@ impl CodeGenerator { Ok((reg, code)) } - Expression::ArrayLiteral { elements, type_id } => todo!(), - Expression::StructLiteral { + RValue::ArrayLiteral { elements, type_id } => todo!(), + RValue::StructLiteral { name, fields, type_id, } => todo!(), - Expression::Variable { name, .. } => { + RValue::Variable { name, .. } => { if self.is_global(&name.name) { // Allocate a temporary register for the global let (reg, alloc_code) = self.allocator.alloc_temp()?; @@ -560,7 +561,7 @@ impl CodeGenerator { } } - Expression::Binary { + RValue::Binary { op, left, right, .. } => { // Evaluate left operand @@ -669,7 +670,7 @@ impl CodeGenerator { Ok((result_reg, code)) } - Expression::UnaryPostfix { op, operand, .. } => { + RValue::UnaryPostfix { op, operand, .. } => { let (operand_reg, operand_code) = self.generate_expression(operand, true, func_body)?; code.append(operand_code); @@ -695,7 +696,7 @@ impl CodeGenerator { Ok((result_reg, code)) } - Expression::Unary { op, operand, .. } => { + RValue::Unary { op, operand, .. } => { let (operand_reg, operand_code) = self.generate_expression(operand, true, func_body)?; code.append(operand_code); @@ -759,7 +760,7 @@ impl CodeGenerator { Ok((result_reg, code)) } - Expression::Call { + RValue::Call { func: Call { name, args }, .. } => { @@ -829,7 +830,7 @@ impl CodeGenerator { Ok((result_reg, code)) } - Expression::IndexAccess { + RValue::IndexAccess { expr, index, type_id, @@ -853,7 +854,7 @@ impl CodeGenerator { Ok((result_reg, code)) } - Expression::MemberAccess { + RValue::MemberAccess { expr, field_name, type_id, @@ -861,7 +862,7 @@ impl CodeGenerator { "Structs are not yet implemented!".to_string(), )), - Expression::TypeCast { + RValue::TypeCast { expr, target_type, type_id, diff --git a/tooling/dscs_compiler/src/backend/ir_lowering_codegen.rs b/tooling/dscs_compiler/src/backend/ir_lowering_codegen.rs index a36ad7f..e38f57c 100644 --- a/tooling/dscs_compiler/src/backend/ir_lowering_codegen.rs +++ b/tooling/dscs_compiler/src/backend/ir_lowering_codegen.rs @@ -1,8 +1,14 @@ -use std::any::Any; use dsa_tasks::ProcessAgent; +use std::rc::Rc; -use crate::model::ast::{BinaryOperator, Block, Call, Declaration, Expression, Number, Program, Statement, TypeId, UnaryOperator, Variable}; -use crate::model::dsa_ir::{BasicBlock, IRProgram, Label, Literal, PrimitiveOperation, PrimitiveUnaryOperation, Terminator, VirtualReg}; +use crate::model::ast::{ + AssignmentOperator, BinaryOperator, Block, Call, Declaration, LValue, Number, Program, RValue, + Statement, TypeId, UnaryOperator, Variable, +}; +use crate::model::dsa_ir::{ + BasicBlock, IRProgram, Label, Literal, PrimitiveOperation, PrimitiveUnaryOperation, Terminator, + VirtualReg, +}; use crate::model::{CompilerError, dsa_ir as IR}; pub struct IrLoweringCodeGenerator { @@ -71,7 +77,7 @@ impl IrLoweringCodeGenerator { ) -> Result { let mut context = FunctionContext::new(Label::new(function_name.to_string())); - let params = (0..params.len()).map(|_| context.new_vreg()).collect(); + let params = (0..params.len()).map(|_| context.allocate_vreg()).collect(); self.collapse_block(body, &mut context)?; @@ -125,8 +131,8 @@ impl IrLoweringCodeGenerator { let else_label = Label::new_with_id("else", id); let end_label = Label::new_with_id("endif", id); - let eval_result = ctx.new_vreg(); - self.collapse_expression(condition, ctx, eval_result)?; + let eval_result = ctx.allocate_vreg(); + self.collapse_rvalue(condition, ctx, eval_result.clone())?; ctx.next_block( Terminator::ConditionalJump { @@ -157,8 +163,8 @@ impl IrLoweringCodeGenerator { Statement::Return(expr) => { // return stmt. calls relevant defer methods and expands the body of the return statement. let result = if let Some(value) = expr { - let result_reg = ctx.new_vreg(); - self.collapse_expression(value, ctx, result_reg)?; + let result_reg = ctx.allocate_vreg(); + self.collapse_rvalue(value, ctx, result_reg.clone())?; result_reg } else { VirtualReg::void() @@ -177,7 +183,7 @@ impl IrLoweringCodeGenerator { Statement::Expression { expr } => { // generate the code for the expression but discard the result // let the optimiser remove it if it provably has no side effects. - self.collapse_expression(expr, ctx, VirtualReg::void())?; + self.collapse_rvalue(expr, ctx, VirtualReg::void())?; Ok(()) } @@ -226,8 +232,8 @@ impl IrLoweringCodeGenerator { ctx.next_block(Terminator::Jump(check_label.clone()), check_label.clone()); - let if_flag = ctx.new_vreg(); - self.collapse_expression(condition, ctx, if_flag)?; + let if_flag = ctx.allocate_vreg(); + self.collapse_rvalue(condition, ctx, if_flag.clone())?; ctx.next_block( Terminator::ConditionalJump { @@ -271,48 +277,81 @@ impl IrLoweringCodeGenerator { Ok(()) } - Statement::Declaration { var, value } => { - let Some(expr) = value else { - ctx.new_local(VirtualReg::void(), var.clone()); - return Ok(()); - }; + Statement::Declaration { var, value, .. } => { + // TODO: How do we name this without duplicating the string? + let decl_reg = ctx.allocate_vreg(); + + // tell the IR to allocate a stack slot for the var + ctx.push_stmt(IR::Statement::Alloca(decl_reg.clone(), var.type_id.size())); + + // register the new local var with the current context. + let local_var = + LocalVar::on_stack(&var.name, decl_reg.clone(), var.type_id.clone()); + ctx.new_local(local_var); + + if let Some(expr) = value { + self.collapse_rvalue(expr, ctx, decl_reg)?; + } - let v_reg = ctx.new_vreg(); - self.collapse_expression(expr, ctx, v_reg)?; - ctx.new_local(v_reg, var.clone()); Ok(()) } Statement::Assign { - varname, - value, + left, + right, operator, - } => { - let Some(v_reg) = ctx.find_local(varname) else { + } => self.collapse_assignment(left, right, operator, ctx), // TODO: support full range of assignment operators! + } + } + + fn collapse_assignment( + &self, + left: &LValue, + right: &RValue, + operator: &AssignmentOperator, + ctx: &mut FunctionContext, + ) -> Result<(), CompilerError> { + // evaluate right hand side of assignment + let value = ctx.allocate_vreg(); + self.collapse_rvalue(right, ctx, value.clone())?; + + // TODO: make use of the alternative assignment operators! + // TODO: (assuming always '=' for now) + match left { + LValue::Variable(var) => { + let Some(write_to) = ctx.find_local(&var.name) else { return Err(CompilerError::generic(format!( - "Variable not defined: {varname}" + "Variable not defined: {}", + var.name ))); }; - self.collapse_expression(value, ctx, v_reg)?; - Ok(()) - } // todo!!!! + let cmd = match write_to { + VarLoc::Stack(address) => IR::Statement::Store { value, address }, + VarLoc::Register(register) => IR::Statement::Copy { + src: value, + dst: register, + }, + }; - Statement::PtrWrite { ptr, value } => { - let ptr_reg = ctx.new_vreg(); - self.collapse_expression(&ptr, ctx, ptr_reg)?; + ctx.push_stmt(cmd); + } - let value_reg = ctx.new_vreg(); - self.collapse_expression(&value, ctx, value_reg)?; + LValue::Pointer(var) => { + todo!(); + } - ctx.push_stmt(IR::Statement::Store { - value: value_reg, - address: ptr_reg, - }); + LValue::ArrayIndex(array, index) => { + let index_vreg = ctx.allocate_vreg(); + self.collapse_rvalue(index, ctx, value.clone())?; + } - Ok(()) + LValue::StructField(lvalue, field) => { + todo!(); } } + + Ok(()) } fn collapse_call( @@ -325,8 +364,9 @@ impl IrLoweringCodeGenerator { .args .iter() .map(|arg| { - let v_reg = ctx.new_vreg(); - self.collapse_expression(&arg, ctx, v_reg).map(|_| v_reg) + let v_reg = ctx.allocate_vreg(); + self.collapse_rvalue(&arg, ctx, v_reg.clone()) + .map(|_| v_reg) }) .collect::, _>>()?; @@ -339,23 +379,23 @@ impl IrLoweringCodeGenerator { Ok(()) } - fn collapse_expression( + fn collapse_rvalue( &self, - expr: &Expression, + expr: &RValue, ctx: &mut FunctionContext, v_reg: VirtualReg, ) -> Result<(), CompilerError> { match expr { - Expression::Empty => Ok(()), - Expression::Call { func, .. } => { + RValue::Empty => Ok(()), + RValue::Call { func, .. } => { self.collapse_call(func, ctx, v_reg)?; Ok(()) } - Expression::CharLiteral(c) => { + RValue::CharLiteral(c) => { ctx.push_stmt(IR::Statement::Const(v_reg, Literal::Char(*c))); Ok(()) } - Expression::Number(num) => { + RValue::Number(num) => { match num { Number::Signed(val, ..) => { ctx.push_stmt(IR::Statement::Const(v_reg, Literal::Int(*val as isize))); @@ -366,49 +406,60 @@ impl IrLoweringCodeGenerator { } Ok(()) } - Expression::Variable { name, expr_type } => { + RValue::Variable { name, expr_type } => { let Some(var) = ctx.find_local(&name.name) else { return Err(CompilerError::generic(format!( "Variable not defined: {name}" ))); }; - ctx.push_stmt(IR::Statement::Copy { - src: var, - dst: v_reg, - }); + let statement = match var { + VarLoc::Stack(address) => IR::Statement::Load { + address, + result: v_reg, + }, + VarLoc::Register(src) => IR::Statement::Copy { dst: v_reg, src }, + }; + + ctx.push_stmt(statement); Ok(()) } // TODO! all of the below: - Expression::Binary { op, left, right, .. } => self.collapse_binary_expr(left, right, op, ctx, v_reg), - Expression::Unary { op, operand, .. } => self.collapse_unary_op(operand, op, ctx, v_reg), - Expression::UnaryPostfix { .. } => Ok(()), - Expression::TypeCast { .. } => Ok(()), - Expression::IndexAccess { .. } => Ok(()), - Expression::MemberAccess { .. } => Ok(()), - Expression::StringLiteral(_) => Ok(()), - Expression::ArrayLiteral { .. } => Ok(()), - Expression::StructLiteral { .. } => Ok(()), + RValue::Binary { + op, left, right, .. + } => self.collapse_binary_expr(left, right, op, ctx, v_reg), + RValue::Unary { op, operand, .. } => self.collapse_unary_op(operand, op, ctx, v_reg), + RValue::UnaryPostfix { + op, + operand, + type_id, + } => Ok(()), + RValue::TypeCast { .. } => Ok(()), + RValue::IndexAccess { .. } => Ok(()), + RValue::MemberAccess { .. } => Ok(()), + RValue::StringLiteral(_) => Ok(()), + RValue::ArrayLiteral { .. } => Ok(()), + RValue::StructLiteral { .. } => Ok(()), } } fn collapse_unary_op( &self, - expr: &Expression, + expr: &RValue, op: &UnaryOperator, ctx: &mut FunctionContext, result_reg: VirtualReg, ) -> Result<(), CompilerError> { - let intermediate_reg = ctx.new_vreg(); - self.collapse_expression(expr, ctx, intermediate_reg)?; + let intermediate_reg = ctx.allocate_vreg(); + self.collapse_rvalue(expr, ctx, intermediate_reg.clone())?; let ir_stmt = match op { UnaryOperator::Plus => IR::Statement::UnaryOp { // use the identity operation as '+x' on an integer 'x' is a no-op op: PrimitiveUnaryOperation::Identity, - operand: intermediate_reg, + operand: intermediate_reg.clone(), result: result_reg, }, @@ -420,25 +471,25 @@ impl IrLoweringCodeGenerator { // allocating a vreg for an incrementation isn't very elegant but it can be easily optimised out. UnaryOperator::Increment => { - let right_vreg = ctx.new_vreg(); - ctx.push_stmt(IR::Statement::Const(right_vreg, Literal::Int(1))); + let right_vreg = ctx.allocate_vreg(); + ctx.push_stmt(IR::Statement::Const(right_vreg.clone(), Literal::Int(1))); IR::Statement::BinaryOp { left: intermediate_reg, op: PrimitiveOperation::Add, right: right_vreg, result: result_reg, } - }, + } UnaryOperator::Decrement => { - let right_vreg = ctx.new_vreg(); - ctx.push_stmt(IR::Statement::Const(right_vreg, Literal::Int(1))); + let right_vreg = ctx.allocate_vreg(); + ctx.push_stmt(IR::Statement::Const(right_vreg.clone(), Literal::Int(1))); IR::Statement::BinaryOp { left: intermediate_reg, op: PrimitiveOperation::Sub, right: right_vreg, result: result_reg, } - }, + } // TODO: for now we assume all values are primitives. // hence this implementation is *not correct* for structs and will produce UB. @@ -448,15 +499,18 @@ impl IrLoweringCodeGenerator { }, // TODO: this may be quite complex as we have to guarantee ahead of time that this value has a memory address! - UnaryOperator::AddressOf => todo!(), + UnaryOperator::AddressOf => IR::Statement::NoOp, // TODO: if the expression is pure, we don't need to evaluate it to know the size. UnaryOperator::SizeOf => { - let size = expr.type_id() - .map_err(|_| CompilerError::generic("All types are expected to be sized at this stage"))? + let size = expr + .type_id() + .map_err(|_| { + CompilerError::generic("All types are expected to be sized at this stage") + })? .size(); IR::Statement::Const(result_reg, Literal::Int(size as isize)) - }, + } UnaryOperator::BitwiseNot => IR::Statement::UnaryOp { op: PrimitiveUnaryOperation::Not, @@ -465,7 +519,7 @@ impl IrLoweringCodeGenerator { }, // TODO: the IR can't represent boolean operators yet. do we add it to the IR or do something here instead? - UnaryOperator::LogicalNot => todo!(), + UnaryOperator::LogicalNot => IR::Statement::NoOp, }; ctx.push_stmt(ir_stmt); @@ -475,17 +529,17 @@ impl IrLoweringCodeGenerator { fn collapse_binary_expr( &self, - left: &Expression, - right: &Expression, + left: &RValue, + right: &RValue, op: &BinaryOperator, ctx: &mut FunctionContext, result_vreg: VirtualReg, ) -> Result<(), CompilerError> { - let left_vreg = ctx.new_vreg(); - self.collapse_expression(left, ctx, left_vreg)?; + let left_vreg = ctx.allocate_vreg(); + self.collapse_rvalue(left, ctx, left_vreg.clone())?; - let right_vreg = ctx.new_vreg(); - self.collapse_expression(right, ctx, right_vreg)?; + let right_vreg = ctx.allocate_vreg(); + self.collapse_rvalue(right, ctx, right_vreg.clone())?; let primitive_operation = match op { // arithmetic @@ -562,12 +616,53 @@ impl BasicBlockBuilder { } } +#[derive(Debug, Clone)] +enum VarLoc { + Register(VirtualReg), + Stack(VirtualReg), +} + +#[derive(Debug, Clone)] +struct LocalVar { + location: VarLoc, + name: Rc, + type_id: TypeId, +} + +impl LocalVar { + pub fn on_stack(name: impl Into, stack_loc: VirtualReg, type_id: TypeId) -> Self { + Self { + name: Rc::new(name.into()), + location: VarLoc::Stack(stack_loc), + type_id, + } + } + + pub fn new(ctx: &mut FunctionContext, name: impl Into, type_id: TypeId) -> Self { + let name = Rc::new(name.into()); + let v_reg = ctx.allocate_vreg().named(name.clone()); + Self { + name, + location: VarLoc::Stack(v_reg), + type_id, + } + } + + pub fn location(&self) -> VarLoc { + self.location.clone() + } + + pub fn name(&self) -> &str { + self.name.as_str() + } +} + pub struct Scope { start: Label, end: Label, defers: Vec, - locals: Vec<(VirtualReg, Variable)>, + locals: Vec, } impl Scope { @@ -602,21 +697,17 @@ impl FunctionContext { } } - pub fn new_vreg(&mut self) -> VirtualReg { + pub fn allocate_vreg(&mut self) -> VirtualReg { self.v_register_idx += 1; - VirtualReg(self.v_register_idx as isize - 1) + VirtualReg::Anonymous(self.v_register_idx as isize - 1) } pub fn defer(&mut self, call: Call) { self.scopes.last_mut().unwrap().defers.push(call); } - pub fn new_local(&mut self, v_reg: VirtualReg, variable: Variable) { - self.scopes - .last_mut() - .unwrap() - .locals - .push((v_reg, variable)); + pub fn new_local(&mut self, var: LocalVar) { + self.scopes.last_mut().unwrap().locals.push(var); } pub fn enter_scope(&mut self, scope: Scope) { @@ -635,11 +726,11 @@ impl FunctionContext { .collect() } - pub fn find_local(&self, var: &String) -> Option { + pub fn find_local(&self, var: &String) -> Option { for scope in self.scopes.iter().rev() { for local in &scope.locals { - if local.1.name == *var { - return Some(local.0); + if local.name.as_str() == var { + return Some(local.location.clone()); } } } diff --git a/tooling/dscs_compiler/src/frontend/parser.rs b/tooling/dscs_compiler/src/frontend/parser.rs index a1c3c87..87675fb 100644 --- a/tooling/dscs_compiler/src/frontend/parser.rs +++ b/tooling/dscs_compiler/src/frontend/parser.rs @@ -1,7 +1,7 @@ use crate::model::CompilerError; use crate::model::ast::{ - AssignmentOperator, BinaryOperator, Block, Call, ConstExpr, Declaration, Dependency, - Expression, Number, Program, Statement, TypeId, UnaryOperator, Variable, + AssignmentOperator, BinaryOperator, Block, Call, ConstExpr, Declaration, Dependency, LValue, + Number, Program, RValue, Statement, TypeId, UnaryOperator, Variable, }; use crate::model::tokens::Token; use crate::{expect_tt, expect_value}; @@ -192,7 +192,7 @@ impl Parser { if expect_tt!(self.peek_next()?, If).accepted() { self.next()?; - let condition = self.parse_expression()?; + let condition = self.parse_rvalue()?; let then_stmt = self.parse_block()?; @@ -220,7 +220,7 @@ impl Parser { self.next()?; // expect valid expression - let expr = self.parse_expression()?; + let expr = self.parse_rvalue()?; // expect valid block after let block = self.parse_block()?; @@ -248,7 +248,7 @@ impl Parser { return ParseResult::Accept(Statement::Return(None)); } - let expr = self.parse_expression()?; + let expr = self.parse_rvalue()?; expect_tt!(self.next()?, Semicolon)?; return ParseResult::Accept(Statement::Return(Some(expr))); @@ -274,45 +274,6 @@ impl Parser { return ParseResult::Accept(Statement::Continue); } - // handle writes to pointers! - if expect_tt!(self.peek_next()?, Star).accepted() { - self.next()?; - - let left = if expect_tt!(self.peek_next()?, Identifier).accepted() { - let identifier = expect_value!(self.next()?, Identifier)?; - - Expression::Variable { - name: identifier, - expr_type: None, - } - } else if expect_tt!(self.peek_next()?, LeftParen).accepted() { - self.next()?; - - let expr = self.parse_expression()?; - - let _ = expect_tt!(self.next()?, RightParen).accepted(); - - expr - } else { - return ParseResult::Reject(CompilerError::UnexpectedToken( - self.peek_next()?.tt().to_string(), - )); - }; - - let _ = expect_tt!(self.next()?, Assign)?; - - let right = self.parse_expression()?; - - // expect semicolon - expect_tt!(self.next()?, Semicolon)?; - - // return result - return ParseResult::Accept(Statement::PtrWrite { - ptr: left, - value: right, - }); - } - // handle let statements (declarations) if expect_tt!(self.peek_next()?, Let).accepted() { self.next(); @@ -325,6 +286,7 @@ impl Parser { self.next(); return ParseResult::Accept(Statement::Declaration { var: name, + mutable: false, value: None, }); } @@ -334,83 +296,88 @@ impl Parser { let _ = expect_tt!(self.next()?, Assign)?; // expect a valid expression - let expr = self.parse_expression()?; + let expr = self.parse_rvalue()?; let _ = expect_tt!(self.next()?, Semicolon); // return statement return ParseResult::Accept(Statement::Declaration { var: name, + mutable: false, // TODO: add mutability check value: Some(expr), }); } - // handle an in-place function call - if let ParseResult::Accept(name) = expect_value!(self.peek_next()?, Identifier) - && let ParseResult::Accept(operator) = expect_tt!( - self.peek(1)?, - Assign, - PlusEqual, - MinusEqual, - StarEqual, - SlashEqual, - PercentEqual, - AndEqual, - OrEqual, - XorEqual, - ShlEqual, - ShrEqual - ) - { - // consume name token + let expr = self.parse_rvalue()?; + + let assignment_operator = match self.peek_next()? { + Token::Assign => Some(AssignmentOperator::Assign), + Token::PlusEqual => Some(AssignmentOperator::AddAssign), + Token::MinusEqual => Some(AssignmentOperator::SubAssign), + Token::StarEqual => Some(AssignmentOperator::MulAssign), + Token::SlashEqual => Some(AssignmentOperator::DivAssign), + Token::PercentEqual => Some(AssignmentOperator::ModAssign), + Token::AndEqual => Some(AssignmentOperator::AndAssign), + Token::OrEqual => Some(AssignmentOperator::OrAssign), + Token::XorEqual => Some(AssignmentOperator::XorAssign), + Token::ShlEqual => Some(AssignmentOperator::LeftShiftAssign), + Token::ShrEqual => Some(AssignmentOperator::RightShiftAssign), + _ => None, + }; + + if let Some(operator) = assignment_operator { self.next()?; - - // pattern match to find operator - let operator = match operator { - Token::Assign => AssignmentOperator::Assign, - Token::PlusEqual => AssignmentOperator::AddAssign, - Token::MinusEqual => AssignmentOperator::SubAssign, - Token::StarEqual => AssignmentOperator::MulAssign, - Token::SlashEqual => AssignmentOperator::DivAssign, - Token::PercentEqual => AssignmentOperator::ModAssign, - Token::AndEqual => AssignmentOperator::AndAssign, - Token::OrEqual => AssignmentOperator::OrAssign, - Token::XorEqual => AssignmentOperator::XorAssign, - Token::ShlEqual => AssignmentOperator::LeftShiftAssign, - Token::ShrEqual => AssignmentOperator::RightShiftAssign, - _ => { - return ParseResult::Reject(CompilerError::UnexpectedToken( - self.peek_next()?.tt().to_string(), - )); - } - }; - - // consume operator token - self.next()?; - - let value = self.parse_expression()?; - - let _ = expect_tt!(self.next()?, Semicolon); - + let left = Self::rvalue_to_lvalue(expr)?; + let right = self.parse_rvalue()?; + let _ = expect_tt!(self.next()?, Semicolon)?; return ParseResult::Accept(Statement::Assign { - varname: name.name, + left, operator, - value, + right, }); } - // parse an expression and a semicolon - let expr = self.parse_expression()?; let _ = expect_tt!(self.next()?, Semicolon)?; - ParseResult::Accept(Statement::Expression { expr }) } - fn parse_expression(&mut self) -> ParseResult { + fn parse_lvalue(&mut self) -> ParseResult { + Self::rvalue_to_lvalue(self.parse_rvalue()?) + } + + fn parse_rvalue(&mut self) -> ParseResult { self.parse_logical_or() } - fn parse_logical_or(&mut self) -> ParseResult { + fn rvalue_to_lvalue(expr: RValue) -> ParseResult { + match expr { + RValue::Variable { name, .. } => ParseResult::Accept(LValue::Variable(name)), + RValue::MemberAccess { + expr, field_name, .. + } => { + let base = Self::rvalue_to_lvalue(*expr)?; + ParseResult::Accept(LValue::StructField(Box::new(base), field_name)) + } + RValue::IndexAccess { expr, index, .. } => { + let base = Self::rvalue_to_lvalue(*expr)?; + ParseResult::Accept(LValue::ArrayIndex(Box::new(base), *index)) + } + RValue::Unary { + op: UnaryOperator::Dereference, + operand, + .. + } => { + let base = Self::rvalue_to_lvalue(*operand)?; + ParseResult::Accept(LValue::Pointer(Box::new(base))) + } + other => ParseResult::Reject(CompilerError::Generic(format!( + "not a valid assignment target: {:?}", + other + ))), + } + } + + fn parse_logical_or(&mut self) -> ParseResult { let left = self.parse_logical_and()?; let op = match self.peek_next()? { @@ -419,7 +386,7 @@ impl Parser { }; self.next()?; - ParseResult::Accept(Expression::Binary { + ParseResult::Accept(RValue::Binary { op, left: Box::new(left), right: Box::new(self.parse_logical_or()?), @@ -427,7 +394,7 @@ impl Parser { }) } - fn parse_logical_and(&mut self) -> ParseResult { + fn parse_logical_and(&mut self) -> ParseResult { let left = self.parse_bitwise_or()?; let op = match self.peek_next()? { @@ -436,7 +403,7 @@ impl Parser { }; self.next()?; - ParseResult::Accept(Expression::Binary { + ParseResult::Accept(RValue::Binary { op, left: Box::new(left), right: Box::new(self.parse_logical_and()?), @@ -444,7 +411,7 @@ impl Parser { }) } - fn parse_bitwise_or(&mut self) -> ParseResult { + fn parse_bitwise_or(&mut self) -> ParseResult { let left = self.parse_bitwise_xor()?; let op = match self.peek_next()? { @@ -453,7 +420,7 @@ impl Parser { }; self.next()?; - ParseResult::Accept(Expression::Binary { + ParseResult::Accept(RValue::Binary { op, left: Box::new(left), right: Box::new(self.parse_bitwise_or()?), @@ -461,7 +428,7 @@ impl Parser { }) } - fn parse_bitwise_xor(&mut self) -> ParseResult { + fn parse_bitwise_xor(&mut self) -> ParseResult { let left = self.parse_bitwise_and()?; let op = match self.peek_next()? { @@ -470,7 +437,7 @@ impl Parser { }; self.next()?; - ParseResult::Accept(Expression::Binary { + ParseResult::Accept(RValue::Binary { op, left: Box::new(left), right: Box::new(self.parse_bitwise_xor()?), @@ -478,7 +445,7 @@ impl Parser { }) } - fn parse_bitwise_and(&mut self) -> ParseResult { + fn parse_bitwise_and(&mut self) -> ParseResult { let left = self.parse_comparison()?; let op = match self.peek_next()? { @@ -487,7 +454,7 @@ impl Parser { }; self.next()?; - ParseResult::Accept(Expression::Binary { + ParseResult::Accept(RValue::Binary { op, left: Box::new(left), right: Box::new(self.parse_bitwise_and()?), @@ -495,7 +462,7 @@ impl Parser { }) } - fn parse_comparison(&mut self) -> ParseResult { + fn parse_comparison(&mut self) -> ParseResult { let left = self.parse_shift()?; let op = match self.peek_next()? { @@ -509,7 +476,7 @@ impl Parser { }; self.next()?; - ParseResult::Accept(Expression::Binary { + ParseResult::Accept(RValue::Binary { op, left: Box::new(left), right: Box::new(self.parse_comparison()?), @@ -517,7 +484,7 @@ impl Parser { }) } - fn parse_shift(&mut self) -> ParseResult { + fn parse_shift(&mut self) -> ParseResult { let left = self.parse_additive()?; let op = match self.peek_next()? { @@ -527,7 +494,7 @@ impl Parser { }; self.next()?; - ParseResult::Accept(Expression::Binary { + ParseResult::Accept(RValue::Binary { op, left: Box::new(left), right: Box::new(self.parse_shift()?), @@ -535,7 +502,7 @@ impl Parser { }) } - fn parse_additive(&mut self) -> ParseResult { + fn parse_additive(&mut self) -> ParseResult { let left = self.parse_multiplicative()?; let op = match self.peek_next()? { @@ -545,7 +512,7 @@ impl Parser { }; self.next()?; - ParseResult::Accept(Expression::Binary { + ParseResult::Accept(RValue::Binary { op, left: Box::new(left), right: Box::new(self.parse_additive()?), @@ -553,7 +520,7 @@ impl Parser { }) } - fn parse_multiplicative(&mut self) -> ParseResult { + fn parse_multiplicative(&mut self) -> ParseResult { let left = self.parse_unary()?; let op = match self.peek_next()? { @@ -563,7 +530,7 @@ impl Parser { }; self.next()?; - ParseResult::Accept(Expression::Binary { + ParseResult::Accept(RValue::Binary { op, left: Box::new(left), right: Box::new(self.parse_multiplicative()?), @@ -571,7 +538,7 @@ impl Parser { }) } - fn parse_unary(&mut self) -> ParseResult { + fn parse_unary(&mut self) -> ParseResult { let op = match self.peek_next()? { // prefix inc/dec Token::PlusPlus => UnaryOperator::Increment, @@ -598,21 +565,21 @@ impl Parser { self.next()?; let operand = Box::new(self.parse_unary()?); - ParseResult::Accept(Expression::Unary { + ParseResult::Accept(RValue::Unary { op, operand, type_id: None, }) } - fn parse_postfix(&mut self, mut expr: Expression) -> ParseResult { + fn parse_postfix(&mut self, mut expr: RValue) -> ParseResult { loop { match self.peek_next()? { // Type cast: expr as Type Token::As => { self.next()?; // consume 'as' let target_type = self.parse_type()?; - expr = Expression::TypeCast { + expr = RValue::TypeCast { expr: Box::new(expr), target_type, type_id: None, @@ -622,7 +589,7 @@ impl Parser { // Postfix increment/decrement Token::PlusPlus => { self.next()?; - expr = Expression::UnaryPostfix { + expr = RValue::UnaryPostfix { op: UnaryOperator::Increment, operand: Box::new(expr), type_id: None, @@ -630,7 +597,7 @@ impl Parser { } Token::MinusMinus => { self.next()?; - expr = Expression::UnaryPostfix { + expr = RValue::UnaryPostfix { op: UnaryOperator::Decrement, operand: Box::new(expr), type_id: None, @@ -640,11 +607,11 @@ impl Parser { // Array indexing: expr[index] Token::LeftBracket => { self.next()?; // consume '[' - let index = Box::new(self.parse_expression()?); + let index = Box::new(self.parse_rvalue()?); let _ = expect_tt!(self.next()?, RightBracket)?; - expr = Expression::IndexAccess { + expr = RValue::IndexAccess { expr: Box::new(expr), index, type_id: None, @@ -658,7 +625,7 @@ impl Parser { if !matches!(self.peek_next()?, Token::RightParen) { loop { - args.push(self.parse_expression()?); + args.push(self.parse_rvalue()?); if !matches!(self.peek_next()?, Token::Comma) { break; } @@ -668,8 +635,8 @@ impl Parser { let _ = expect_tt!(self.next()?, RightParen)?; - if let Expression::Variable { name, .. } = expr { - expr = Expression::Call { + if let RValue::Variable { name, .. } = expr { + expr = RValue::Call { func: Call { name, args }, type_id: None, }; @@ -680,7 +647,7 @@ impl Parser { Token::Dot => { self.next()?; let field_name = expect_value!(self.next()?, Identifier)?; - expr = Expression::MemberAccess { + expr = RValue::MemberAccess { expr: Box::new(expr), field_name, type_id: None, @@ -695,23 +662,23 @@ impl Parser { ParseResult::Accept(expr) } - fn parse_primary(&mut self) -> ParseResult { + fn parse_primary(&mut self) -> ParseResult { match self.peek_next()? { Token::UnsignedInt(value, type_id) => { self.next()?; - ParseResult::Accept(Expression::Number(Number::Unsigned(value, type_id))) + ParseResult::Accept(RValue::Number(Number::Unsigned(value, type_id))) } Token::SignedInt(value, type_id) => { self.next()?; - ParseResult::Accept(Expression::Number(Number::Signed(value, type_id))) + ParseResult::Accept(RValue::Number(Number::Signed(value, type_id))) } Token::String(value) => { self.next()?; - ParseResult::Accept(Expression::StringLiteral(value)) + ParseResult::Accept(RValue::StringLiteral(value)) } Token::Char(value) => { self.next()?; - ParseResult::Accept(Expression::CharLiteral(value)) + ParseResult::Accept(RValue::CharLiteral(value)) } Token::Identifier(name) => { @@ -726,7 +693,7 @@ impl Parser { // }); // } // - ParseResult::Accept(Expression::Variable { + ParseResult::Accept(RValue::Variable { name, expr_type: None, }) @@ -762,7 +729,7 @@ impl Parser { if !matches!(self.peek_next()?, Token::RightBracket) { loop { - elements.push(self.parse_expression()?); + elements.push(self.parse_rvalue()?); if !matches!(self.peek_next()?, Token::Comma) { break; } @@ -771,14 +738,14 @@ impl Parser { } expect_tt!(self.next()?, RightBracket)?; - ParseResult::Accept(Expression::ArrayLiteral { + ParseResult::Accept(RValue::ArrayLiteral { elements, type_id: None, }) } Token::LeftParen => { self.next()?; - let expr = self.parse_expression()?; + let expr = self.parse_rvalue()?; let _ = expect_tt!(self.next()?, RightParen)?; ParseResult::Accept(expr) } diff --git a/tooling/dscs_compiler/src/model/ast.rs b/tooling/dscs_compiler/src/model/ast.rs index 86dda5d..9a8f5e1 100644 --- a/tooling/dscs_compiler/src/model/ast.rs +++ b/tooling/dscs_compiler/src/model/ast.rs @@ -175,34 +175,31 @@ pub enum Statement { Block(Block), Declaration { var: Variable, - value: Option, + mutable: bool, + value: Option, }, Assign { - varname: String, + left: LValue, operator: AssignmentOperator, - value: Expression, - }, - PtrWrite { - ptr: Expression, - value: Expression, + right: RValue, }, Expression { - expr: Expression, + expr: RValue, }, If { - condition: Expression, + condition: RValue, then_stmt: Block, else_stmt: Block, }, While { - condition: Expression, + condition: RValue, body: Vec, }, Loop(Block), Defer(Call), Break, Continue, - Return(Option), + Return(Option), } #[derive(Debug, Clone)] @@ -220,28 +217,36 @@ impl fmt::Display for ConstExpr { } } +#[derive(Debug, Clone)] +pub enum LValue { + Variable(Name), + ArrayIndex(Box, RValue), + StructField(Box, Name), + Pointer(Box), +} + #[allow(unused)] #[derive(Debug, Clone)] -pub enum Expression { +pub enum RValue { Empty, Binary { op: BinaryOperator, - left: Box, - right: Box, + left: Box, + right: Box, // Post-Semantic Analysis type_id: Option, }, Unary { op: UnaryOperator, - operand: Box, + operand: Box, // Post-Semantic Analysis type_id: Option, }, UnaryPostfix { op: UnaryOperator, - operand: Box, + operand: Box, // Post-Semantic Analysis type_id: Option, @@ -251,21 +256,21 @@ pub enum Expression { expr_type: Option, }, TypeCast { - expr: Box, + expr: Box, target_type: TypeId, // Post-Semantic Analysis type_id: Option, }, IndexAccess { - expr: Box, - index: Box, + expr: Box, + index: Box, // Post-Semantic Analysis type_id: Option, }, MemberAccess { - expr: Box, + expr: Box, field_name: Name, // Post-Semantic Analysis @@ -281,12 +286,12 @@ pub enum Expression { StringLiteral(String), CharLiteral(char), ArrayLiteral { - elements: Vec, + elements: Vec, type_id: Option, }, StructLiteral { name: Name, - fields: Vec<(Name, Expression)>, + fields: Vec<(Name, RValue)>, type_id: Option, }, } @@ -300,56 +305,52 @@ pub enum Number { #[derive(Debug, Clone)] pub struct Call { pub name: Name, - pub args: Vec, + pub args: Vec, } -impl Expression { +impl RValue { pub fn is_pure(&self) -> bool { match self { - Expression::Number { .. } => true, - Expression::StringLiteral(_) => true, - Expression::CharLiteral(_) => true, - Expression::Call { .. } => false, - Expression::Binary { left, right, .. } => left.is_pure() && right.is_pure(), - Expression::Unary { operand, .. } => operand.is_pure(), - Expression::UnaryPostfix { operand, .. } => operand.is_pure(), - Expression::Empty => true, - Expression::Variable { .. } => true, - Expression::TypeCast { expr, .. } => expr.is_pure(), - Expression::IndexAccess { expr, index, .. } => expr.is_pure() && index.is_pure(), - Expression::MemberAccess { expr, .. } => expr.is_pure(), - Expression::ArrayLiteral { elements, .. } => { + RValue::Number { .. } => true, + RValue::StringLiteral(_) => true, + RValue::CharLiteral(_) => true, + RValue::Call { .. } => false, + RValue::Binary { left, right, .. } => left.is_pure() && right.is_pure(), + RValue::Unary { operand, .. } => operand.is_pure(), + RValue::UnaryPostfix { operand, .. } => operand.is_pure(), + RValue::Empty => true, + RValue::Variable { .. } => true, + RValue::TypeCast { expr, .. } => expr.is_pure(), + RValue::IndexAccess { expr, index, .. } => expr.is_pure() && index.is_pure(), + RValue::MemberAccess { expr, .. } => expr.is_pure(), + RValue::ArrayLiteral { elements, .. } => { elements.iter().all(|element| element.is_pure()) } - Expression::StructLiteral { fields, .. } => { - fields.iter().all(|(_, expr)| expr.is_pure()) - } + RValue::StructLiteral { fields, .. } => fields.iter().all(|(_, expr)| expr.is_pure()), } } pub fn type_id(&self) -> Result { match self { - Expression::Number(Number::Signed(_, type_id) | Number::Unsigned(_, type_id)) => { + RValue::Number(Number::Signed(_, type_id) | Number::Unsigned(_, type_id)) => { type_id.clone().ok_or(CompilerError::UnknownType) } - Expression::StringLiteral(_) => Ok(TypeId::Ptr(Box::new(TypeId::Char))), - Expression::CharLiteral(_) => Ok(TypeId::Char), - Expression::Call { type_id, .. } => type_id.clone().ok_or(CompilerError::UnknownType), - Expression::Binary { type_id, .. } => type_id.clone().ok_or(CompilerError::UnknownType), - Expression::Unary { type_id, .. } => type_id.clone().ok_or(CompilerError::UnknownType), - Expression::UnaryPostfix { type_id, .. } => { + RValue::StringLiteral(_) => Ok(TypeId::Ptr(Box::new(TypeId::Char))), + RValue::CharLiteral(_) => Ok(TypeId::Char), + RValue::Call { type_id, .. } => type_id.clone().ok_or(CompilerError::UnknownType), + RValue::Binary { type_id, .. } => type_id.clone().ok_or(CompilerError::UnknownType), + RValue::Unary { type_id, .. } => type_id.clone().ok_or(CompilerError::UnknownType), + RValue::UnaryPostfix { type_id, .. } => { type_id.clone().ok_or(CompilerError::UnknownType) } - Expression::Empty => Ok(TypeId::Void), - Expression::Variable { expr_type, .. } => { + RValue::Empty => Ok(TypeId::Void), + RValue::Variable { expr_type, .. } => { expr_type.clone().ok_or(CompilerError::UnknownType) } - Expression::TypeCast { type_id, .. } => { - type_id.clone().ok_or(CompilerError::UnknownType) - } - Expression::IndexAccess { expr, .. } => expr.type_id(), - Expression::MemberAccess { expr, .. } => expr.type_id(), - Expression::ArrayLiteral { elements, .. } => { + RValue::TypeCast { type_id, .. } => type_id.clone().ok_or(CompilerError::UnknownType), + RValue::IndexAccess { expr, .. } => expr.type_id(), + RValue::MemberAccess { expr, .. } => expr.type_id(), + RValue::ArrayLiteral { elements, .. } => { let element_type = elements .first() .map_or(TypeId::Void, |e| e.type_id().unwrap_or(TypeId::Void)); @@ -358,7 +359,7 @@ impl Expression { size: elements.len(), }) } - Expression::StructLiteral { name, fields, .. } => { + RValue::StructLiteral { name, fields, .. } => { let fields = fields .iter() .map(|(_, expr)| expr.type_id()) diff --git a/tooling/dscs_compiler/src/model/dsa_ir.rs b/tooling/dscs_compiler/src/model/dsa_ir.rs index 0869336..9037a0b 100644 --- a/tooling/dscs_compiler/src/model/dsa_ir.rs +++ b/tooling/dscs_compiler/src/model/dsa_ir.rs @@ -5,6 +5,8 @@ use std::sync::atomic::{AtomicUsize, Ordering}; #[derive(Clone, Debug)] pub enum Statement { + // this should always be optimised out. + NoOp, Const(VirtualReg, Literal), BinaryOp { left: VirtualReg, @@ -29,6 +31,7 @@ pub enum Statement { address: VirtualReg, result: VirtualReg, }, + // TODO: decide if these are actually needed. this is really just read+offset GetStructField { struct_addr: VirtualReg, result: VirtualReg, @@ -44,6 +47,7 @@ pub enum Statement { func: FunctionName, args: Vec, }, + Alloca(VirtualReg, usize), // usize = size in bytes of allocation } #[derive(Clone, Debug)] @@ -113,12 +117,31 @@ impl fmt::Display for Label { static LABEL_ID: AtomicUsize = AtomicUsize::new(0); -#[derive(Copy, Clone, Debug, PartialEq)] -pub struct VirtualReg(pub isize); +// #[derive(Clone, Debug, PartialEq)] +// pub struct VirtualReg(pub isize, pub Option>); + +#[derive(Clone, Debug, PartialEq)] +pub enum VirtualReg { + Void, + Named(Rc, isize), + Anonymous(isize), +} impl VirtualReg { pub fn void() -> Self { - Self(-1) + Self::Void + } + + pub fn named(self, name: impl Into>) -> Self { + match self { + Self::Void => todo!( + "we need to properly handle an error here. we should never be trying to name something assigned to void" + ), + Self::Named(..) => { + todo!("v_reg's assigned to variables should never be reassigned. this is a bug!") + } + Self::Anonymous(val) => Self::Named(name.into(), val), + } } } @@ -140,7 +163,6 @@ pub enum PrimitiveUnaryOperation { Not, } - #[derive(Clone, Debug)] pub enum PrimitiveOperation { Add, @@ -164,10 +186,10 @@ pub enum PrimitiveOperation { impl fmt::Display for VirtualReg { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - if self.0 == -1 { - write!(f, "_") - } else { - write!(f, "v_{}", self.0) + match self { + Self::Void => write!(f, "_v"), + Self::Named(name, id) => write!(f, "v{id}_{name}"), + Self::Anonymous(val) => write!(f, "v{val}"), } } } @@ -219,7 +241,11 @@ impl fmt::Display for PrimitiveUnaryOperation { impl fmt::Display for Statement { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { + Statement::NoOp => write!(f, "no-op"), Statement::Const(dst, lit) => write!(f, "{} = literal {}", dst, lit), + Statement::Alloca(register, size) => { + write!(f, "Allocated {} ({} bytes)", register, size) + } Statement::UnaryOp { op, operand,