updated compiler IR codegen. still broken but getting there. next job is figuring out how we handle Lvalues.

This commit is contained in:
2026-08-25 00:49:23 +01:00
parent 97e43b8b85
commit a130ef2cf6
6 changed files with 555 additions and 556 deletions
+51 -138
View File
@@ -1,138 +1,51 @@
// GENERATED BY DSC COMPILER fn main() {
// Generated at 2026-08-21 19:34:30 __main_0:
v1 = literal 64
// Imports v2 = literal 32
include alloc: "./lib/memory/block_alloc.dsa" v0_allocator = alloc::init(v1, v2)
include print: "./lib/io/print.dsa" v3 = copy v0_allocator
_v = print::print_hex_word(v3)
// Globals & Reserved Memory _v = print::print_newline()
v5 = copy v0_allocator
// Entry Point v4_ptr = alloc::alloc(v5)
dw stack: 0x010000 v6 = copy v4_ptr
db message: "Process Exited with code:" _v = print::print_hex_word(v6)
_init: v7 = copy v4_ptr
ldw stack, bpr, 0 v8 = literal 200
mov bpr, spr store v7, v8
push zero _v = print::print_newline()
call main v10 = copy v0_allocator
call print::print_newline v9_p2 = alloc::alloc(v10)
lwi message, rg0 v11 = copy v9_p2
push rg0 _v = print::print_hex_word(v11)
call print::print _v = print::print_newline()
pop zero v13 = copy v4_ptr
call print::print_hex_word v12 = load v13
pop zero _v = print::print_num(v12)
hlt v14 = copy v0_allocator
v16 = copy v4_ptr
// Return no-op
_ret: _v = alloc::free(v14, v15)
mov bpr, spr v18 = copy v0_allocator
pop bpr v17_ptr3 = alloc::alloc(v18)
ret _v = print::print_newline()
v19 = copy v17_ptr3
db str_8: "successful free of ptr" _v = print::print_hex_word(v19)
// fn main() -> u32 _v = print::print_newline()
main: v20 = copy v4_ptr
push bpr _v = print::print_hex_word(v20)
mov spr, bpr v22 = copy v4_ptr
lli 32, rg0 v23 = literal 0
lli 64, rg1 v21 = v22 == v23
// push arg 1 if v21 then __then_2 else __else_2
push rg0 __then_2:
// push arg 0 _v = print::print(v24)
push rg1 jump __endif_2
call alloc::init __else_2:
pop rg2 jump __endif_2
pop zero __endif_2:
push rg2 v25 = literal 0
// push arg 0 return v25
push rg2 __unreachable_3:
call print::print_hex_word return
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
+127 -126
View File
@@ -2,8 +2,8 @@ use super::instruction::InsBlock as IB;
use super::registers::RegisterAllocator; use super::registers::RegisterAllocator;
use crate::model::CompilerError; use crate::model::CompilerError;
use crate::model::ast::{ use crate::model::ast::{
AssignmentOperator, BinaryOperator, Call, ConstExpr, Declaration, Dependency, Expression, BinaryOperator, Call, ConstExpr, Declaration, Dependency, Number, Program, RValue, Statement,
Number, Program, Statement, TypeId, UnaryOperator, Variable, TypeId, UnaryOperator, Variable,
}; };
use crate::model::instruction::Comparison::{ use crate::model::instruction::Comparison::{
Equal, Greater, GreaterEqual, Less, LessEqual, NotEqual, Equal, Greater, GreaterEqual, Less, LessEqual, NotEqual,
@@ -240,7 +240,7 @@ impl CodeGenerator {
let mut code = IB::new(); let mut code = IB::new();
match stmt { match stmt {
Statement::Declaration { var, value } => { Statement::Declaration { var, value, .. } => {
if let Some(expr) = value { if let Some(expr) = value {
// Evaluate expression // Evaluate expression
let (result_reg, expr_code) = let (result_reg, expr_code) =
@@ -263,116 +263,117 @@ impl CodeGenerator {
Statement::Continue => unimplemented!("need scope tracking first!"), Statement::Continue => unimplemented!("need scope tracking first!"),
Statement::Defer(_func) => unimplemented!("we need scope tracking first!"), Statement::Defer(_func) => unimplemented!("we need scope tracking first!"),
Statement::PtrWrite { ptr, value } => { _ => todo!(),
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)?; // Statement::PtrWrite { ptr, value } => {
code.append(ptr_code); // let (result_reg, expr_code) = self.generate_expression(value, true, func_body)?;
// code.append(expr_code);
code.push(I::stw_reg(result_reg, ptr_reg)); //
// let (ptr_reg, ptr_code) = self.generate_expression(ptr, true, func_body)?;
self.allocator.free_temp(result_reg); // code.append(ptr_code);
self.allocator.free_temp(ptr_reg); //
} // code.push(I::stw_reg(result_reg, ptr_reg));
//
Statement::Assign { // self.allocator.free_temp(result_reg);
varname, // self.allocator.free_temp(ptr_reg);
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::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) => { Statement::Return(expr) => {
if let Some(e) = expr { if let Some(e) = expr {
let (result_reg, expr_code) = self.generate_expression(e, true, func_body)?; let (result_reg, expr_code) = self.generate_expression(e, true, func_body)?;
@@ -479,16 +480,16 @@ impl CodeGenerator {
// Returns (register containing result, assembly code) // Returns (register containing result, assembly code)
fn generate_expression( fn generate_expression(
&mut self, &mut self,
expr: &Expression, expr: &RValue,
use_result: bool, use_result: bool,
func_body: &mut IB, func_body: &mut IB,
) -> Result<(Register, IB), CompilerError> { ) -> Result<(Register, IB), CompilerError> {
let mut code = IB::new(); let mut code = IB::new();
match expr { 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, _) => { Number::Signed(value, _) => {
let (reg, alloc_code) = self.allocator.alloc_temp()?; let (reg, alloc_code) = self.allocator.alloc_temp()?;
code.append(alloc_code); code.append(alloc_code);
@@ -509,7 +510,7 @@ impl CodeGenerator {
} }
}, },
Expression::CharLiteral(value) => { RValue::CharLiteral(value) => {
let (reg, alloc_code) = self.allocator.alloc_temp()?; let (reg, alloc_code) = self.allocator.alloc_temp()?;
code.append(alloc_code); code.append(alloc_code);
@@ -520,7 +521,7 @@ impl CodeGenerator {
Ok((reg, code)) Ok((reg, code))
} }
Expression::StringLiteral(value) => { RValue::StringLiteral(value) => {
let (reg, alloc_code) = self.allocator.alloc_temp()?; let (reg, alloc_code) = self.allocator.alloc_temp()?;
code.append(alloc_code); code.append(alloc_code);
@@ -535,14 +536,14 @@ impl CodeGenerator {
Ok((reg, code)) Ok((reg, code))
} }
Expression::ArrayLiteral { elements, type_id } => todo!(), RValue::ArrayLiteral { elements, type_id } => todo!(),
Expression::StructLiteral { RValue::StructLiteral {
name, name,
fields, fields,
type_id, type_id,
} => todo!(), } => todo!(),
Expression::Variable { name, .. } => { RValue::Variable { name, .. } => {
if self.is_global(&name.name) { if self.is_global(&name.name) {
// Allocate a temporary register for the global // Allocate a temporary register for the global
let (reg, alloc_code) = self.allocator.alloc_temp()?; let (reg, alloc_code) = self.allocator.alloc_temp()?;
@@ -560,7 +561,7 @@ impl CodeGenerator {
} }
} }
Expression::Binary { RValue::Binary {
op, left, right, .. op, left, right, ..
} => { } => {
// Evaluate left operand // Evaluate left operand
@@ -669,7 +670,7 @@ impl CodeGenerator {
Ok((result_reg, code)) Ok((result_reg, code))
} }
Expression::UnaryPostfix { op, operand, .. } => { RValue::UnaryPostfix { op, operand, .. } => {
let (operand_reg, operand_code) = let (operand_reg, operand_code) =
self.generate_expression(operand, true, func_body)?; self.generate_expression(operand, true, func_body)?;
code.append(operand_code); code.append(operand_code);
@@ -695,7 +696,7 @@ impl CodeGenerator {
Ok((result_reg, code)) Ok((result_reg, code))
} }
Expression::Unary { op, operand, .. } => { RValue::Unary { op, operand, .. } => {
let (operand_reg, operand_code) = let (operand_reg, operand_code) =
self.generate_expression(operand, true, func_body)?; self.generate_expression(operand, true, func_body)?;
code.append(operand_code); code.append(operand_code);
@@ -759,7 +760,7 @@ impl CodeGenerator {
Ok((result_reg, code)) Ok((result_reg, code))
} }
Expression::Call { RValue::Call {
func: Call { name, args }, func: Call { name, args },
.. ..
} => { } => {
@@ -829,7 +830,7 @@ impl CodeGenerator {
Ok((result_reg, code)) Ok((result_reg, code))
} }
Expression::IndexAccess { RValue::IndexAccess {
expr, expr,
index, index,
type_id, type_id,
@@ -853,7 +854,7 @@ impl CodeGenerator {
Ok((result_reg, code)) Ok((result_reg, code))
} }
Expression::MemberAccess { RValue::MemberAccess {
expr, expr,
field_name, field_name,
type_id, type_id,
@@ -861,7 +862,7 @@ impl CodeGenerator {
"Structs are not yet implemented!".to_string(), "Structs are not yet implemented!".to_string(),
)), )),
Expression::TypeCast { RValue::TypeCast {
expr, expr,
target_type, target_type,
type_id, type_id,
@@ -1,8 +1,14 @@
use std::any::Any;
use dsa_tasks::ProcessAgent; 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::ast::{
use crate::model::dsa_ir::{BasicBlock, IRProgram, Label, Literal, PrimitiveOperation, PrimitiveUnaryOperation, Terminator, VirtualReg}; 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}; use crate::model::{CompilerError, dsa_ir as IR};
pub struct IrLoweringCodeGenerator { pub struct IrLoweringCodeGenerator {
@@ -71,7 +77,7 @@ impl IrLoweringCodeGenerator {
) -> Result<IR::Function, CompilerError> { ) -> Result<IR::Function, CompilerError> {
let mut context = FunctionContext::new(Label::new(function_name.to_string())); 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)?; self.collapse_block(body, &mut context)?;
@@ -125,8 +131,8 @@ impl IrLoweringCodeGenerator {
let else_label = Label::new_with_id("else", id); let else_label = Label::new_with_id("else", id);
let end_label = Label::new_with_id("endif", id); let end_label = Label::new_with_id("endif", id);
let eval_result = ctx.new_vreg(); let eval_result = ctx.allocate_vreg();
self.collapse_expression(condition, ctx, eval_result)?; self.collapse_rvalue(condition, ctx, eval_result.clone())?;
ctx.next_block( ctx.next_block(
Terminator::ConditionalJump { Terminator::ConditionalJump {
@@ -157,8 +163,8 @@ impl IrLoweringCodeGenerator {
Statement::Return(expr) => { Statement::Return(expr) => {
// return stmt. calls relevant defer methods and expands the body of the return statement. // return stmt. calls relevant defer methods and expands the body of the return statement.
let result = if let Some(value) = expr { let result = if let Some(value) = expr {
let result_reg = ctx.new_vreg(); let result_reg = ctx.allocate_vreg();
self.collapse_expression(value, ctx, result_reg)?; self.collapse_rvalue(value, ctx, result_reg.clone())?;
result_reg result_reg
} else { } else {
VirtualReg::void() VirtualReg::void()
@@ -177,7 +183,7 @@ impl IrLoweringCodeGenerator {
Statement::Expression { expr } => { Statement::Expression { expr } => {
// generate the code for the expression but discard the result // generate the code for the expression but discard the result
// let the optimiser remove it if it provably has no side effects. // 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(()) Ok(())
} }
@@ -226,8 +232,8 @@ impl IrLoweringCodeGenerator {
ctx.next_block(Terminator::Jump(check_label.clone()), check_label.clone()); ctx.next_block(Terminator::Jump(check_label.clone()), check_label.clone());
let if_flag = ctx.new_vreg(); let if_flag = ctx.allocate_vreg();
self.collapse_expression(condition, ctx, if_flag)?; self.collapse_rvalue(condition, ctx, if_flag.clone())?;
ctx.next_block( ctx.next_block(
Terminator::ConditionalJump { Terminator::ConditionalJump {
@@ -271,48 +277,81 @@ impl IrLoweringCodeGenerator {
Ok(()) Ok(())
} }
Statement::Declaration { var, value } => { Statement::Declaration { var, value, .. } => {
let Some(expr) = value else { // TODO: How do we name this without duplicating the string?
ctx.new_local(VirtualReg::void(), var.clone()); let decl_reg = ctx.allocate_vreg();
return Ok(());
}; // 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(()) Ok(())
} }
Statement::Assign { Statement::Assign {
varname, left,
value, right,
operator, operator,
} => { } => self.collapse_assignment(left, right, operator, ctx), // TODO: support full range of assignment operators!
let Some(v_reg) = ctx.find_local(varname) else { }
}
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!( return Err(CompilerError::generic(format!(
"Variable not defined: {varname}" "Variable not defined: {}",
var.name
))); )));
}; };
self.collapse_expression(value, ctx, v_reg)?; let cmd = match write_to {
Ok(()) VarLoc::Stack(address) => IR::Statement::Store { value, address },
} // todo!!!! VarLoc::Register(register) => IR::Statement::Copy {
src: value,
dst: register,
},
};
Statement::PtrWrite { ptr, value } => { ctx.push_stmt(cmd);
let ptr_reg = ctx.new_vreg(); }
self.collapse_expression(&ptr, ctx, ptr_reg)?;
let value_reg = ctx.new_vreg(); LValue::Pointer(var) => {
self.collapse_expression(&value, ctx, value_reg)?; todo!();
}
ctx.push_stmt(IR::Statement::Store { LValue::ArrayIndex(array, index) => {
value: value_reg, let index_vreg = ctx.allocate_vreg();
address: ptr_reg, self.collapse_rvalue(index, ctx, value.clone())?;
}); }
Ok(()) LValue::StructField(lvalue, field) => {
todo!();
} }
} }
Ok(())
} }
fn collapse_call( fn collapse_call(
@@ -325,8 +364,9 @@ impl IrLoweringCodeGenerator {
.args .args
.iter() .iter()
.map(|arg| { .map(|arg| {
let v_reg = ctx.new_vreg(); let v_reg = ctx.allocate_vreg();
self.collapse_expression(&arg, ctx, v_reg).map(|_| v_reg) self.collapse_rvalue(&arg, ctx, v_reg.clone())
.map(|_| v_reg)
}) })
.collect::<Result<Vec<VirtualReg>, _>>()?; .collect::<Result<Vec<VirtualReg>, _>>()?;
@@ -339,23 +379,23 @@ impl IrLoweringCodeGenerator {
Ok(()) Ok(())
} }
fn collapse_expression( fn collapse_rvalue(
&self, &self,
expr: &Expression, expr: &RValue,
ctx: &mut FunctionContext, ctx: &mut FunctionContext,
v_reg: VirtualReg, v_reg: VirtualReg,
) -> Result<(), CompilerError> { ) -> Result<(), CompilerError> {
match expr { match expr {
Expression::Empty => Ok(()), RValue::Empty => Ok(()),
Expression::Call { func, .. } => { RValue::Call { func, .. } => {
self.collapse_call(func, ctx, v_reg)?; self.collapse_call(func, ctx, v_reg)?;
Ok(()) Ok(())
} }
Expression::CharLiteral(c) => { RValue::CharLiteral(c) => {
ctx.push_stmt(IR::Statement::Const(v_reg, Literal::Char(*c))); ctx.push_stmt(IR::Statement::Const(v_reg, Literal::Char(*c)));
Ok(()) Ok(())
} }
Expression::Number(num) => { RValue::Number(num) => {
match num { match num {
Number::Signed(val, ..) => { Number::Signed(val, ..) => {
ctx.push_stmt(IR::Statement::Const(v_reg, Literal::Int(*val as isize))); ctx.push_stmt(IR::Statement::Const(v_reg, Literal::Int(*val as isize)));
@@ -366,49 +406,60 @@ impl IrLoweringCodeGenerator {
} }
Ok(()) Ok(())
} }
Expression::Variable { name, expr_type } => { RValue::Variable { name, expr_type } => {
let Some(var) = ctx.find_local(&name.name) else { let Some(var) = ctx.find_local(&name.name) else {
return Err(CompilerError::generic(format!( return Err(CompilerError::generic(format!(
"Variable not defined: {name}" "Variable not defined: {name}"
))); )));
}; };
ctx.push_stmt(IR::Statement::Copy { let statement = match var {
src: var, VarLoc::Stack(address) => IR::Statement::Load {
dst: v_reg, address,
}); result: v_reg,
},
VarLoc::Register(src) => IR::Statement::Copy { dst: v_reg, src },
};
ctx.push_stmt(statement);
Ok(()) Ok(())
} }
// TODO! all of the below: // TODO! all of the below:
Expression::Binary { op, left, right, .. } => self.collapse_binary_expr(left, right, op, ctx, v_reg), RValue::Binary {
Expression::Unary { op, operand, .. } => self.collapse_unary_op(operand, op, ctx, v_reg), op, left, right, ..
Expression::UnaryPostfix { .. } => Ok(()), } => self.collapse_binary_expr(left, right, op, ctx, v_reg),
Expression::TypeCast { .. } => Ok(()), RValue::Unary { op, operand, .. } => self.collapse_unary_op(operand, op, ctx, v_reg),
Expression::IndexAccess { .. } => Ok(()), RValue::UnaryPostfix {
Expression::MemberAccess { .. } => Ok(()), op,
Expression::StringLiteral(_) => Ok(()), operand,
Expression::ArrayLiteral { .. } => Ok(()), type_id,
Expression::StructLiteral { .. } => Ok(()), } => Ok(()),
RValue::TypeCast { .. } => Ok(()),
RValue::IndexAccess { .. } => Ok(()),
RValue::MemberAccess { .. } => Ok(()),
RValue::StringLiteral(_) => Ok(()),
RValue::ArrayLiteral { .. } => Ok(()),
RValue::StructLiteral { .. } => Ok(()),
} }
} }
fn collapse_unary_op( fn collapse_unary_op(
&self, &self,
expr: &Expression, expr: &RValue,
op: &UnaryOperator, op: &UnaryOperator,
ctx: &mut FunctionContext, ctx: &mut FunctionContext,
result_reg: VirtualReg, result_reg: VirtualReg,
) -> Result<(), CompilerError> { ) -> Result<(), CompilerError> {
let intermediate_reg = ctx.new_vreg(); let intermediate_reg = ctx.allocate_vreg();
self.collapse_expression(expr, ctx, intermediate_reg)?; self.collapse_rvalue(expr, ctx, intermediate_reg.clone())?;
let ir_stmt = match op { let ir_stmt = match op {
UnaryOperator::Plus => IR::Statement::UnaryOp { UnaryOperator::Plus => IR::Statement::UnaryOp {
// use the identity operation as '+x' on an integer 'x' is a no-op // use the identity operation as '+x' on an integer 'x' is a no-op
op: PrimitiveUnaryOperation::Identity, op: PrimitiveUnaryOperation::Identity,
operand: intermediate_reg, operand: intermediate_reg.clone(),
result: result_reg, 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. // allocating a vreg for an incrementation isn't very elegant but it can be easily optimised out.
UnaryOperator::Increment => { UnaryOperator::Increment => {
let right_vreg = ctx.new_vreg(); let right_vreg = ctx.allocate_vreg();
ctx.push_stmt(IR::Statement::Const(right_vreg, Literal::Int(1))); ctx.push_stmt(IR::Statement::Const(right_vreg.clone(), Literal::Int(1)));
IR::Statement::BinaryOp { IR::Statement::BinaryOp {
left: intermediate_reg, left: intermediate_reg,
op: PrimitiveOperation::Add, op: PrimitiveOperation::Add,
right: right_vreg, right: right_vreg,
result: result_reg, result: result_reg,
} }
}, }
UnaryOperator::Decrement => { UnaryOperator::Decrement => {
let right_vreg = ctx.new_vreg(); let right_vreg = ctx.allocate_vreg();
ctx.push_stmt(IR::Statement::Const(right_vreg, Literal::Int(1))); ctx.push_stmt(IR::Statement::Const(right_vreg.clone(), Literal::Int(1)));
IR::Statement::BinaryOp { IR::Statement::BinaryOp {
left: intermediate_reg, left: intermediate_reg,
op: PrimitiveOperation::Sub, op: PrimitiveOperation::Sub,
right: right_vreg, right: right_vreg,
result: result_reg, result: result_reg,
} }
}, }
// TODO: for now we assume all values are primitives. // TODO: for now we assume all values are primitives.
// hence this implementation is *not correct* for structs and will produce UB. // 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! // 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. // TODO: if the expression is pure, we don't need to evaluate it to know the size.
UnaryOperator::SizeOf => { UnaryOperator::SizeOf => {
let size = expr.type_id() let size = expr
.map_err(|_| CompilerError::generic("All types are expected to be sized at this stage"))? .type_id()
.map_err(|_| {
CompilerError::generic("All types are expected to be sized at this stage")
})?
.size(); .size();
IR::Statement::Const(result_reg, Literal::Int(size as isize)) IR::Statement::Const(result_reg, Literal::Int(size as isize))
}, }
UnaryOperator::BitwiseNot => IR::Statement::UnaryOp { UnaryOperator::BitwiseNot => IR::Statement::UnaryOp {
op: PrimitiveUnaryOperation::Not, 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? // 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); ctx.push_stmt(ir_stmt);
@@ -475,17 +529,17 @@ impl IrLoweringCodeGenerator {
fn collapse_binary_expr( fn collapse_binary_expr(
&self, &self,
left: &Expression, left: &RValue,
right: &Expression, right: &RValue,
op: &BinaryOperator, op: &BinaryOperator,
ctx: &mut FunctionContext, ctx: &mut FunctionContext,
result_vreg: VirtualReg, result_vreg: VirtualReg,
) -> Result<(), CompilerError> { ) -> Result<(), CompilerError> {
let left_vreg = ctx.new_vreg(); let left_vreg = ctx.allocate_vreg();
self.collapse_expression(left, ctx, left_vreg)?; self.collapse_rvalue(left, ctx, left_vreg.clone())?;
let right_vreg = ctx.new_vreg(); let right_vreg = ctx.allocate_vreg();
self.collapse_expression(right, ctx, right_vreg)?; self.collapse_rvalue(right, ctx, right_vreg.clone())?;
let primitive_operation = match op { let primitive_operation = match op {
// arithmetic // 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<String>,
type_id: TypeId,
}
impl LocalVar {
pub fn on_stack(name: impl Into<String>, 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<String>, 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 { pub struct Scope {
start: Label, start: Label,
end: Label, end: Label,
defers: Vec<Call>, defers: Vec<Call>,
locals: Vec<(VirtualReg, Variable)>, locals: Vec<LocalVar>,
} }
impl Scope { 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; 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) { pub fn defer(&mut self, call: Call) {
self.scopes.last_mut().unwrap().defers.push(call); self.scopes.last_mut().unwrap().defers.push(call);
} }
pub fn new_local(&mut self, v_reg: VirtualReg, variable: Variable) { pub fn new_local(&mut self, var: LocalVar) {
self.scopes self.scopes.last_mut().unwrap().locals.push(var);
.last_mut()
.unwrap()
.locals
.push((v_reg, variable));
} }
pub fn enter_scope(&mut self, scope: Scope) { pub fn enter_scope(&mut self, scope: Scope) {
@@ -635,11 +726,11 @@ impl FunctionContext {
.collect() .collect()
} }
pub fn find_local(&self, var: &String) -> Option<VirtualReg> { pub fn find_local(&self, var: &String) -> Option<VarLoc> {
for scope in self.scopes.iter().rev() { for scope in self.scopes.iter().rev() {
for local in &scope.locals { for local in &scope.locals {
if local.1.name == *var { if local.name.as_str() == var {
return Some(local.0); return Some(local.location.clone());
} }
} }
} }
+103 -136
View File
@@ -1,7 +1,7 @@
use crate::model::CompilerError; use crate::model::CompilerError;
use crate::model::ast::{ use crate::model::ast::{
AssignmentOperator, BinaryOperator, Block, Call, ConstExpr, Declaration, Dependency, AssignmentOperator, BinaryOperator, Block, Call, ConstExpr, Declaration, Dependency, LValue,
Expression, Number, Program, Statement, TypeId, UnaryOperator, Variable, Number, Program, RValue, Statement, TypeId, UnaryOperator, Variable,
}; };
use crate::model::tokens::Token; use crate::model::tokens::Token;
use crate::{expect_tt, expect_value}; use crate::{expect_tt, expect_value};
@@ -192,7 +192,7 @@ impl Parser {
if expect_tt!(self.peek_next()?, If).accepted() { if expect_tt!(self.peek_next()?, If).accepted() {
self.next()?; self.next()?;
let condition = self.parse_expression()?; let condition = self.parse_rvalue()?;
let then_stmt = self.parse_block()?; let then_stmt = self.parse_block()?;
@@ -220,7 +220,7 @@ impl Parser {
self.next()?; self.next()?;
// expect valid expression // expect valid expression
let expr = self.parse_expression()?; let expr = self.parse_rvalue()?;
// expect valid block after // expect valid block after
let block = self.parse_block()?; let block = self.parse_block()?;
@@ -248,7 +248,7 @@ impl Parser {
return ParseResult::Accept(Statement::Return(None)); return ParseResult::Accept(Statement::Return(None));
} }
let expr = self.parse_expression()?; let expr = self.parse_rvalue()?;
expect_tt!(self.next()?, Semicolon)?; expect_tt!(self.next()?, Semicolon)?;
return ParseResult::Accept(Statement::Return(Some(expr))); return ParseResult::Accept(Statement::Return(Some(expr)));
@@ -274,45 +274,6 @@ impl Parser {
return ParseResult::Accept(Statement::Continue); 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) // handle let statements (declarations)
if expect_tt!(self.peek_next()?, Let).accepted() { if expect_tt!(self.peek_next()?, Let).accepted() {
self.next(); self.next();
@@ -325,6 +286,7 @@ impl Parser {
self.next(); self.next();
return ParseResult::Accept(Statement::Declaration { return ParseResult::Accept(Statement::Declaration {
var: name, var: name,
mutable: false,
value: None, value: None,
}); });
} }
@@ -334,83 +296,88 @@ impl Parser {
let _ = expect_tt!(self.next()?, Assign)?; let _ = expect_tt!(self.next()?, Assign)?;
// expect a valid expression // expect a valid expression
let expr = self.parse_expression()?; let expr = self.parse_rvalue()?;
let _ = expect_tt!(self.next()?, Semicolon); let _ = expect_tt!(self.next()?, Semicolon);
// return statement // return statement
return ParseResult::Accept(Statement::Declaration { return ParseResult::Accept(Statement::Declaration {
var: name, var: name,
mutable: false, // TODO: add mutability check
value: Some(expr), value: Some(expr),
}); });
} }
// handle an in-place function call let expr = self.parse_rvalue()?;
if let ParseResult::Accept(name) = expect_value!(self.peek_next()?, Identifier)
&& let ParseResult::Accept(operator) = expect_tt!( let assignment_operator = match self.peek_next()? {
self.peek(1)?, Token::Assign => Some(AssignmentOperator::Assign),
Assign, Token::PlusEqual => Some(AssignmentOperator::AddAssign),
PlusEqual, Token::MinusEqual => Some(AssignmentOperator::SubAssign),
MinusEqual, Token::StarEqual => Some(AssignmentOperator::MulAssign),
StarEqual, Token::SlashEqual => Some(AssignmentOperator::DivAssign),
SlashEqual, Token::PercentEqual => Some(AssignmentOperator::ModAssign),
PercentEqual, Token::AndEqual => Some(AssignmentOperator::AndAssign),
AndEqual, Token::OrEqual => Some(AssignmentOperator::OrAssign),
OrEqual, Token::XorEqual => Some(AssignmentOperator::XorAssign),
XorEqual, Token::ShlEqual => Some(AssignmentOperator::LeftShiftAssign),
ShlEqual, Token::ShrEqual => Some(AssignmentOperator::RightShiftAssign),
ShrEqual _ => None,
) };
{
// consume name token if let Some(operator) = assignment_operator {
self.next()?; self.next()?;
let left = Self::rvalue_to_lvalue(expr)?;
// pattern match to find operator let right = self.parse_rvalue()?;
let operator = match operator { let _ = expect_tt!(self.next()?, Semicolon)?;
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);
return ParseResult::Accept(Statement::Assign { return ParseResult::Accept(Statement::Assign {
varname: name.name, left,
operator, operator,
value, right,
}); });
} }
// parse an expression and a semicolon
let expr = self.parse_expression()?;
let _ = expect_tt!(self.next()?, Semicolon)?; let _ = expect_tt!(self.next()?, Semicolon)?;
ParseResult::Accept(Statement::Expression { expr }) ParseResult::Accept(Statement::Expression { expr })
} }
fn parse_expression(&mut self) -> ParseResult<Expression, CompilerError> { fn parse_lvalue(&mut self) -> ParseResult<LValue, CompilerError> {
Self::rvalue_to_lvalue(self.parse_rvalue()?)
}
fn parse_rvalue(&mut self) -> ParseResult<RValue, CompilerError> {
self.parse_logical_or() self.parse_logical_or()
} }
fn parse_logical_or(&mut self) -> ParseResult<Expression, CompilerError> { fn rvalue_to_lvalue(expr: RValue) -> ParseResult<LValue, CompilerError> {
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<RValue, CompilerError> {
let left = self.parse_logical_and()?; let left = self.parse_logical_and()?;
let op = match self.peek_next()? { let op = match self.peek_next()? {
@@ -419,7 +386,7 @@ impl Parser {
}; };
self.next()?; self.next()?;
ParseResult::Accept(Expression::Binary { ParseResult::Accept(RValue::Binary {
op, op,
left: Box::new(left), left: Box::new(left),
right: Box::new(self.parse_logical_or()?), right: Box::new(self.parse_logical_or()?),
@@ -427,7 +394,7 @@ impl Parser {
}) })
} }
fn parse_logical_and(&mut self) -> ParseResult<Expression, CompilerError> { fn parse_logical_and(&mut self) -> ParseResult<RValue, CompilerError> {
let left = self.parse_bitwise_or()?; let left = self.parse_bitwise_or()?;
let op = match self.peek_next()? { let op = match self.peek_next()? {
@@ -436,7 +403,7 @@ impl Parser {
}; };
self.next()?; self.next()?;
ParseResult::Accept(Expression::Binary { ParseResult::Accept(RValue::Binary {
op, op,
left: Box::new(left), left: Box::new(left),
right: Box::new(self.parse_logical_and()?), right: Box::new(self.parse_logical_and()?),
@@ -444,7 +411,7 @@ impl Parser {
}) })
} }
fn parse_bitwise_or(&mut self) -> ParseResult<Expression, CompilerError> { fn parse_bitwise_or(&mut self) -> ParseResult<RValue, CompilerError> {
let left = self.parse_bitwise_xor()?; let left = self.parse_bitwise_xor()?;
let op = match self.peek_next()? { let op = match self.peek_next()? {
@@ -453,7 +420,7 @@ impl Parser {
}; };
self.next()?; self.next()?;
ParseResult::Accept(Expression::Binary { ParseResult::Accept(RValue::Binary {
op, op,
left: Box::new(left), left: Box::new(left),
right: Box::new(self.parse_bitwise_or()?), right: Box::new(self.parse_bitwise_or()?),
@@ -461,7 +428,7 @@ impl Parser {
}) })
} }
fn parse_bitwise_xor(&mut self) -> ParseResult<Expression, CompilerError> { fn parse_bitwise_xor(&mut self) -> ParseResult<RValue, CompilerError> {
let left = self.parse_bitwise_and()?; let left = self.parse_bitwise_and()?;
let op = match self.peek_next()? { let op = match self.peek_next()? {
@@ -470,7 +437,7 @@ impl Parser {
}; };
self.next()?; self.next()?;
ParseResult::Accept(Expression::Binary { ParseResult::Accept(RValue::Binary {
op, op,
left: Box::new(left), left: Box::new(left),
right: Box::new(self.parse_bitwise_xor()?), right: Box::new(self.parse_bitwise_xor()?),
@@ -478,7 +445,7 @@ impl Parser {
}) })
} }
fn parse_bitwise_and(&mut self) -> ParseResult<Expression, CompilerError> { fn parse_bitwise_and(&mut self) -> ParseResult<RValue, CompilerError> {
let left = self.parse_comparison()?; let left = self.parse_comparison()?;
let op = match self.peek_next()? { let op = match self.peek_next()? {
@@ -487,7 +454,7 @@ impl Parser {
}; };
self.next()?; self.next()?;
ParseResult::Accept(Expression::Binary { ParseResult::Accept(RValue::Binary {
op, op,
left: Box::new(left), left: Box::new(left),
right: Box::new(self.parse_bitwise_and()?), right: Box::new(self.parse_bitwise_and()?),
@@ -495,7 +462,7 @@ impl Parser {
}) })
} }
fn parse_comparison(&mut self) -> ParseResult<Expression, CompilerError> { fn parse_comparison(&mut self) -> ParseResult<RValue, CompilerError> {
let left = self.parse_shift()?; let left = self.parse_shift()?;
let op = match self.peek_next()? { let op = match self.peek_next()? {
@@ -509,7 +476,7 @@ impl Parser {
}; };
self.next()?; self.next()?;
ParseResult::Accept(Expression::Binary { ParseResult::Accept(RValue::Binary {
op, op,
left: Box::new(left), left: Box::new(left),
right: Box::new(self.parse_comparison()?), right: Box::new(self.parse_comparison()?),
@@ -517,7 +484,7 @@ impl Parser {
}) })
} }
fn parse_shift(&mut self) -> ParseResult<Expression, CompilerError> { fn parse_shift(&mut self) -> ParseResult<RValue, CompilerError> {
let left = self.parse_additive()?; let left = self.parse_additive()?;
let op = match self.peek_next()? { let op = match self.peek_next()? {
@@ -527,7 +494,7 @@ impl Parser {
}; };
self.next()?; self.next()?;
ParseResult::Accept(Expression::Binary { ParseResult::Accept(RValue::Binary {
op, op,
left: Box::new(left), left: Box::new(left),
right: Box::new(self.parse_shift()?), right: Box::new(self.parse_shift()?),
@@ -535,7 +502,7 @@ impl Parser {
}) })
} }
fn parse_additive(&mut self) -> ParseResult<Expression, CompilerError> { fn parse_additive(&mut self) -> ParseResult<RValue, CompilerError> {
let left = self.parse_multiplicative()?; let left = self.parse_multiplicative()?;
let op = match self.peek_next()? { let op = match self.peek_next()? {
@@ -545,7 +512,7 @@ impl Parser {
}; };
self.next()?; self.next()?;
ParseResult::Accept(Expression::Binary { ParseResult::Accept(RValue::Binary {
op, op,
left: Box::new(left), left: Box::new(left),
right: Box::new(self.parse_additive()?), right: Box::new(self.parse_additive()?),
@@ -553,7 +520,7 @@ impl Parser {
}) })
} }
fn parse_multiplicative(&mut self) -> ParseResult<Expression, CompilerError> { fn parse_multiplicative(&mut self) -> ParseResult<RValue, CompilerError> {
let left = self.parse_unary()?; let left = self.parse_unary()?;
let op = match self.peek_next()? { let op = match self.peek_next()? {
@@ -563,7 +530,7 @@ impl Parser {
}; };
self.next()?; self.next()?;
ParseResult::Accept(Expression::Binary { ParseResult::Accept(RValue::Binary {
op, op,
left: Box::new(left), left: Box::new(left),
right: Box::new(self.parse_multiplicative()?), right: Box::new(self.parse_multiplicative()?),
@@ -571,7 +538,7 @@ impl Parser {
}) })
} }
fn parse_unary(&mut self) -> ParseResult<Expression, CompilerError> { fn parse_unary(&mut self) -> ParseResult<RValue, CompilerError> {
let op = match self.peek_next()? { let op = match self.peek_next()? {
// prefix inc/dec // prefix inc/dec
Token::PlusPlus => UnaryOperator::Increment, Token::PlusPlus => UnaryOperator::Increment,
@@ -598,21 +565,21 @@ impl Parser {
self.next()?; self.next()?;
let operand = Box::new(self.parse_unary()?); let operand = Box::new(self.parse_unary()?);
ParseResult::Accept(Expression::Unary { ParseResult::Accept(RValue::Unary {
op, op,
operand, operand,
type_id: None, type_id: None,
}) })
} }
fn parse_postfix(&mut self, mut expr: Expression) -> ParseResult<Expression, CompilerError> { fn parse_postfix(&mut self, mut expr: RValue) -> ParseResult<RValue, CompilerError> {
loop { loop {
match self.peek_next()? { match self.peek_next()? {
// Type cast: expr as Type // Type cast: expr as Type
Token::As => { Token::As => {
self.next()?; // consume 'as' self.next()?; // consume 'as'
let target_type = self.parse_type()?; let target_type = self.parse_type()?;
expr = Expression::TypeCast { expr = RValue::TypeCast {
expr: Box::new(expr), expr: Box::new(expr),
target_type, target_type,
type_id: None, type_id: None,
@@ -622,7 +589,7 @@ impl Parser {
// Postfix increment/decrement // Postfix increment/decrement
Token::PlusPlus => { Token::PlusPlus => {
self.next()?; self.next()?;
expr = Expression::UnaryPostfix { expr = RValue::UnaryPostfix {
op: UnaryOperator::Increment, op: UnaryOperator::Increment,
operand: Box::new(expr), operand: Box::new(expr),
type_id: None, type_id: None,
@@ -630,7 +597,7 @@ impl Parser {
} }
Token::MinusMinus => { Token::MinusMinus => {
self.next()?; self.next()?;
expr = Expression::UnaryPostfix { expr = RValue::UnaryPostfix {
op: UnaryOperator::Decrement, op: UnaryOperator::Decrement,
operand: Box::new(expr), operand: Box::new(expr),
type_id: None, type_id: None,
@@ -640,11 +607,11 @@ impl Parser {
// Array indexing: expr[index] // Array indexing: expr[index]
Token::LeftBracket => { Token::LeftBracket => {
self.next()?; // consume '[' self.next()?; // consume '['
let index = Box::new(self.parse_expression()?); let index = Box::new(self.parse_rvalue()?);
let _ = expect_tt!(self.next()?, RightBracket)?; let _ = expect_tt!(self.next()?, RightBracket)?;
expr = Expression::IndexAccess { expr = RValue::IndexAccess {
expr: Box::new(expr), expr: Box::new(expr),
index, index,
type_id: None, type_id: None,
@@ -658,7 +625,7 @@ impl Parser {
if !matches!(self.peek_next()?, Token::RightParen) { if !matches!(self.peek_next()?, Token::RightParen) {
loop { loop {
args.push(self.parse_expression()?); args.push(self.parse_rvalue()?);
if !matches!(self.peek_next()?, Token::Comma) { if !matches!(self.peek_next()?, Token::Comma) {
break; break;
} }
@@ -668,8 +635,8 @@ impl Parser {
let _ = expect_tt!(self.next()?, RightParen)?; let _ = expect_tt!(self.next()?, RightParen)?;
if let Expression::Variable { name, .. } = expr { if let RValue::Variable { name, .. } = expr {
expr = Expression::Call { expr = RValue::Call {
func: Call { name, args }, func: Call { name, args },
type_id: None, type_id: None,
}; };
@@ -680,7 +647,7 @@ impl Parser {
Token::Dot => { Token::Dot => {
self.next()?; self.next()?;
let field_name = expect_value!(self.next()?, Identifier)?; let field_name = expect_value!(self.next()?, Identifier)?;
expr = Expression::MemberAccess { expr = RValue::MemberAccess {
expr: Box::new(expr), expr: Box::new(expr),
field_name, field_name,
type_id: None, type_id: None,
@@ -695,23 +662,23 @@ impl Parser {
ParseResult::Accept(expr) ParseResult::Accept(expr)
} }
fn parse_primary(&mut self) -> ParseResult<Expression, CompilerError> { fn parse_primary(&mut self) -> ParseResult<RValue, CompilerError> {
match self.peek_next()? { match self.peek_next()? {
Token::UnsignedInt(value, type_id) => { Token::UnsignedInt(value, type_id) => {
self.next()?; 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) => { Token::SignedInt(value, type_id) => {
self.next()?; self.next()?;
ParseResult::Accept(Expression::Number(Number::Signed(value, type_id))) ParseResult::Accept(RValue::Number(Number::Signed(value, type_id)))
} }
Token::String(value) => { Token::String(value) => {
self.next()?; self.next()?;
ParseResult::Accept(Expression::StringLiteral(value)) ParseResult::Accept(RValue::StringLiteral(value))
} }
Token::Char(value) => { Token::Char(value) => {
self.next()?; self.next()?;
ParseResult::Accept(Expression::CharLiteral(value)) ParseResult::Accept(RValue::CharLiteral(value))
} }
Token::Identifier(name) => { Token::Identifier(name) => {
@@ -726,7 +693,7 @@ impl Parser {
// }); // });
// } // }
// //
ParseResult::Accept(Expression::Variable { ParseResult::Accept(RValue::Variable {
name, name,
expr_type: None, expr_type: None,
}) })
@@ -762,7 +729,7 @@ impl Parser {
if !matches!(self.peek_next()?, Token::RightBracket) { if !matches!(self.peek_next()?, Token::RightBracket) {
loop { loop {
elements.push(self.parse_expression()?); elements.push(self.parse_rvalue()?);
if !matches!(self.peek_next()?, Token::Comma) { if !matches!(self.peek_next()?, Token::Comma) {
break; break;
} }
@@ -771,14 +738,14 @@ impl Parser {
} }
expect_tt!(self.next()?, RightBracket)?; expect_tt!(self.next()?, RightBracket)?;
ParseResult::Accept(Expression::ArrayLiteral { ParseResult::Accept(RValue::ArrayLiteral {
elements, elements,
type_id: None, type_id: None,
}) })
} }
Token::LeftParen => { Token::LeftParen => {
self.next()?; self.next()?;
let expr = self.parse_expression()?; let expr = self.parse_rvalue()?;
let _ = expect_tt!(self.next()?, RightParen)?; let _ = expect_tt!(self.next()?, RightParen)?;
ParseResult::Accept(expr) ParseResult::Accept(expr)
} }
+57 -56
View File
@@ -175,34 +175,31 @@ pub enum Statement {
Block(Block), Block(Block),
Declaration { Declaration {
var: Variable, var: Variable,
value: Option<Expression>, mutable: bool,
value: Option<RValue>,
}, },
Assign { Assign {
varname: String, left: LValue,
operator: AssignmentOperator, operator: AssignmentOperator,
value: Expression, right: RValue,
},
PtrWrite {
ptr: Expression,
value: Expression,
}, },
Expression { Expression {
expr: Expression, expr: RValue,
}, },
If { If {
condition: Expression, condition: RValue,
then_stmt: Block, then_stmt: Block,
else_stmt: Block, else_stmt: Block,
}, },
While { While {
condition: Expression, condition: RValue,
body: Vec<Statement>, body: Vec<Statement>,
}, },
Loop(Block), Loop(Block),
Defer(Call), Defer(Call),
Break, Break,
Continue, Continue,
Return(Option<Expression>), Return(Option<RValue>),
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@@ -220,28 +217,36 @@ impl fmt::Display for ConstExpr {
} }
} }
#[derive(Debug, Clone)]
pub enum LValue {
Variable(Name),
ArrayIndex(Box<LValue>, RValue),
StructField(Box<LValue>, Name),
Pointer(Box<LValue>),
}
#[allow(unused)] #[allow(unused)]
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum Expression { pub enum RValue {
Empty, Empty,
Binary { Binary {
op: BinaryOperator, op: BinaryOperator,
left: Box<Expression>, left: Box<RValue>,
right: Box<Expression>, right: Box<RValue>,
// Post-Semantic Analysis // Post-Semantic Analysis
type_id: Option<TypeId>, type_id: Option<TypeId>,
}, },
Unary { Unary {
op: UnaryOperator, op: UnaryOperator,
operand: Box<Expression>, operand: Box<RValue>,
// Post-Semantic Analysis // Post-Semantic Analysis
type_id: Option<TypeId>, type_id: Option<TypeId>,
}, },
UnaryPostfix { UnaryPostfix {
op: UnaryOperator, op: UnaryOperator,
operand: Box<Expression>, operand: Box<RValue>,
// Post-Semantic Analysis // Post-Semantic Analysis
type_id: Option<TypeId>, type_id: Option<TypeId>,
@@ -251,21 +256,21 @@ pub enum Expression {
expr_type: Option<TypeId>, expr_type: Option<TypeId>,
}, },
TypeCast { TypeCast {
expr: Box<Expression>, expr: Box<RValue>,
target_type: TypeId, target_type: TypeId,
// Post-Semantic Analysis // Post-Semantic Analysis
type_id: Option<TypeId>, type_id: Option<TypeId>,
}, },
IndexAccess { IndexAccess {
expr: Box<Expression>, expr: Box<RValue>,
index: Box<Expression>, index: Box<RValue>,
// Post-Semantic Analysis // Post-Semantic Analysis
type_id: Option<TypeId>, type_id: Option<TypeId>,
}, },
MemberAccess { MemberAccess {
expr: Box<Expression>, expr: Box<RValue>,
field_name: Name, field_name: Name,
// Post-Semantic Analysis // Post-Semantic Analysis
@@ -281,12 +286,12 @@ pub enum Expression {
StringLiteral(String), StringLiteral(String),
CharLiteral(char), CharLiteral(char),
ArrayLiteral { ArrayLiteral {
elements: Vec<Expression>, elements: Vec<RValue>,
type_id: Option<TypeId>, type_id: Option<TypeId>,
}, },
StructLiteral { StructLiteral {
name: Name, name: Name,
fields: Vec<(Name, Expression)>, fields: Vec<(Name, RValue)>,
type_id: Option<TypeId>, type_id: Option<TypeId>,
}, },
} }
@@ -300,56 +305,52 @@ pub enum Number {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Call { pub struct Call {
pub name: Name, pub name: Name,
pub args: Vec<Expression>, pub args: Vec<RValue>,
} }
impl Expression { impl RValue {
pub fn is_pure(&self) -> bool { pub fn is_pure(&self) -> bool {
match self { match self {
Expression::Number { .. } => true, RValue::Number { .. } => true,
Expression::StringLiteral(_) => true, RValue::StringLiteral(_) => true,
Expression::CharLiteral(_) => true, RValue::CharLiteral(_) => true,
Expression::Call { .. } => false, RValue::Call { .. } => false,
Expression::Binary { left, right, .. } => left.is_pure() && right.is_pure(), RValue::Binary { left, right, .. } => left.is_pure() && right.is_pure(),
Expression::Unary { operand, .. } => operand.is_pure(), RValue::Unary { operand, .. } => operand.is_pure(),
Expression::UnaryPostfix { operand, .. } => operand.is_pure(), RValue::UnaryPostfix { operand, .. } => operand.is_pure(),
Expression::Empty => true, RValue::Empty => true,
Expression::Variable { .. } => true, RValue::Variable { .. } => true,
Expression::TypeCast { expr, .. } => expr.is_pure(), RValue::TypeCast { expr, .. } => expr.is_pure(),
Expression::IndexAccess { expr, index, .. } => expr.is_pure() && index.is_pure(), RValue::IndexAccess { expr, index, .. } => expr.is_pure() && index.is_pure(),
Expression::MemberAccess { expr, .. } => expr.is_pure(), RValue::MemberAccess { expr, .. } => expr.is_pure(),
Expression::ArrayLiteral { elements, .. } => { RValue::ArrayLiteral { elements, .. } => {
elements.iter().all(|element| element.is_pure()) elements.iter().all(|element| element.is_pure())
} }
Expression::StructLiteral { fields, .. } => { RValue::StructLiteral { fields, .. } => fields.iter().all(|(_, expr)| expr.is_pure()),
fields.iter().all(|(_, expr)| expr.is_pure())
}
} }
} }
pub fn type_id(&self) -> Result<TypeId, CompilerError> { pub fn type_id(&self) -> Result<TypeId, CompilerError> {
match self { 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) type_id.clone().ok_or(CompilerError::UnknownType)
} }
Expression::StringLiteral(_) => Ok(TypeId::Ptr(Box::new(TypeId::Char))), RValue::StringLiteral(_) => Ok(TypeId::Ptr(Box::new(TypeId::Char))),
Expression::CharLiteral(_) => Ok(TypeId::Char), RValue::CharLiteral(_) => Ok(TypeId::Char),
Expression::Call { type_id, .. } => type_id.clone().ok_or(CompilerError::UnknownType), RValue::Call { type_id, .. } => type_id.clone().ok_or(CompilerError::UnknownType),
Expression::Binary { type_id, .. } => type_id.clone().ok_or(CompilerError::UnknownType), RValue::Binary { type_id, .. } => type_id.clone().ok_or(CompilerError::UnknownType),
Expression::Unary { type_id, .. } => type_id.clone().ok_or(CompilerError::UnknownType), RValue::Unary { type_id, .. } => type_id.clone().ok_or(CompilerError::UnknownType),
Expression::UnaryPostfix { type_id, .. } => { RValue::UnaryPostfix { type_id, .. } => {
type_id.clone().ok_or(CompilerError::UnknownType) type_id.clone().ok_or(CompilerError::UnknownType)
} }
Expression::Empty => Ok(TypeId::Void), RValue::Empty => Ok(TypeId::Void),
Expression::Variable { expr_type, .. } => { RValue::Variable { expr_type, .. } => {
expr_type.clone().ok_or(CompilerError::UnknownType) expr_type.clone().ok_or(CompilerError::UnknownType)
} }
Expression::TypeCast { type_id, .. } => { RValue::TypeCast { type_id, .. } => type_id.clone().ok_or(CompilerError::UnknownType),
type_id.clone().ok_or(CompilerError::UnknownType) RValue::IndexAccess { expr, .. } => expr.type_id(),
} RValue::MemberAccess { expr, .. } => expr.type_id(),
Expression::IndexAccess { expr, .. } => expr.type_id(), RValue::ArrayLiteral { elements, .. } => {
Expression::MemberAccess { expr, .. } => expr.type_id(),
Expression::ArrayLiteral { elements, .. } => {
let element_type = elements let element_type = elements
.first() .first()
.map_or(TypeId::Void, |e| e.type_id().unwrap_or(TypeId::Void)); .map_or(TypeId::Void, |e| e.type_id().unwrap_or(TypeId::Void));
@@ -358,7 +359,7 @@ impl Expression {
size: elements.len(), size: elements.len(),
}) })
} }
Expression::StructLiteral { name, fields, .. } => { RValue::StructLiteral { name, fields, .. } => {
let fields = fields let fields = fields
.iter() .iter()
.map(|(_, expr)| expr.type_id()) .map(|(_, expr)| expr.type_id())
+34 -8
View File
@@ -5,6 +5,8 @@ use std::sync::atomic::{AtomicUsize, Ordering};
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum Statement { pub enum Statement {
// this should always be optimised out.
NoOp,
Const(VirtualReg, Literal), Const(VirtualReg, Literal),
BinaryOp { BinaryOp {
left: VirtualReg, left: VirtualReg,
@@ -29,6 +31,7 @@ pub enum Statement {
address: VirtualReg, address: VirtualReg,
result: VirtualReg, result: VirtualReg,
}, },
// TODO: decide if these are actually needed. this is really just read+offset
GetStructField { GetStructField {
struct_addr: VirtualReg, struct_addr: VirtualReg,
result: VirtualReg, result: VirtualReg,
@@ -44,6 +47,7 @@ pub enum Statement {
func: FunctionName, func: FunctionName,
args: Vec<VirtualReg>, args: Vec<VirtualReg>,
}, },
Alloca(VirtualReg, usize), // usize = size in bytes of allocation
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@@ -113,12 +117,31 @@ impl fmt::Display for Label {
static LABEL_ID: AtomicUsize = AtomicUsize::new(0); static LABEL_ID: AtomicUsize = AtomicUsize::new(0);
#[derive(Copy, Clone, Debug, PartialEq)] // #[derive(Clone, Debug, PartialEq)]
pub struct VirtualReg(pub isize); // pub struct VirtualReg(pub isize, pub Option<Rc<String>>);
#[derive(Clone, Debug, PartialEq)]
pub enum VirtualReg {
Void,
Named(Rc<String>, isize),
Anonymous(isize),
}
impl VirtualReg { impl VirtualReg {
pub fn void() -> Self { pub fn void() -> Self {
Self(-1) Self::Void
}
pub fn named(self, name: impl Into<Rc<String>>) -> 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, Not,
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum PrimitiveOperation { pub enum PrimitiveOperation {
Add, Add,
@@ -164,10 +186,10 @@ pub enum PrimitiveOperation {
impl fmt::Display for VirtualReg { impl fmt::Display for VirtualReg {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
if self.0 == -1 { match self {
write!(f, "_") Self::Void => write!(f, "_v"),
} else { Self::Named(name, id) => write!(f, "v{id}_{name}"),
write!(f, "v_{}", self.0) Self::Anonymous(val) => write!(f, "v{val}"),
} }
} }
} }
@@ -219,7 +241,11 @@ impl fmt::Display for PrimitiveUnaryOperation {
impl fmt::Display for Statement { impl fmt::Display for Statement {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self { match self {
Statement::NoOp => write!(f, "no-op"),
Statement::Const(dst, lit) => write!(f, "{} = literal {}", dst, lit), Statement::Const(dst, lit) => write!(f, "{} = literal {}", dst, lit),
Statement::Alloca(register, size) => {
write!(f, "Allocated {} ({} bytes)", register, size)
}
Statement::UnaryOp { Statement::UnaryOp {
op, op,
operand, operand,