updated compiler IR codegen. still broken but getting there. next job is figuring out how we handle Lvalues.
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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<IR::Function, CompilerError> {
|
||||
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::<Result<Vec<VirtualReg>, _>>()?;
|
||||
|
||||
@@ -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<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 {
|
||||
start: Label,
|
||||
end: Label,
|
||||
|
||||
defers: Vec<Call>,
|
||||
locals: Vec<(VirtualReg, Variable)>,
|
||||
locals: Vec<LocalVar>,
|
||||
}
|
||||
|
||||
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<VirtualReg> {
|
||||
pub fn find_local(&self, var: &String) -> Option<VarLoc> {
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<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()
|
||||
}
|
||||
|
||||
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 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<Expression, CompilerError> {
|
||||
fn parse_logical_and(&mut self) -> ParseResult<RValue, CompilerError> {
|
||||
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<Expression, CompilerError> {
|
||||
fn parse_bitwise_or(&mut self) -> ParseResult<RValue, CompilerError> {
|
||||
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<Expression, CompilerError> {
|
||||
fn parse_bitwise_xor(&mut self) -> ParseResult<RValue, CompilerError> {
|
||||
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<Expression, CompilerError> {
|
||||
fn parse_bitwise_and(&mut self) -> ParseResult<RValue, CompilerError> {
|
||||
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<Expression, CompilerError> {
|
||||
fn parse_comparison(&mut self) -> ParseResult<RValue, CompilerError> {
|
||||
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<Expression, CompilerError> {
|
||||
fn parse_shift(&mut self) -> ParseResult<RValue, CompilerError> {
|
||||
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<Expression, CompilerError> {
|
||||
fn parse_additive(&mut self) -> ParseResult<RValue, CompilerError> {
|
||||
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<Expression, CompilerError> {
|
||||
fn parse_multiplicative(&mut self) -> ParseResult<RValue, CompilerError> {
|
||||
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<Expression, CompilerError> {
|
||||
fn parse_unary(&mut self) -> ParseResult<RValue, CompilerError> {
|
||||
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<Expression, CompilerError> {
|
||||
fn parse_postfix(&mut self, mut expr: RValue) -> ParseResult<RValue, CompilerError> {
|
||||
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<Expression, CompilerError> {
|
||||
fn parse_primary(&mut self) -> ParseResult<RValue, CompilerError> {
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -175,34 +175,31 @@ pub enum Statement {
|
||||
Block(Block),
|
||||
Declaration {
|
||||
var: Variable,
|
||||
value: Option<Expression>,
|
||||
mutable: bool,
|
||||
value: Option<RValue>,
|
||||
},
|
||||
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<Statement>,
|
||||
},
|
||||
Loop(Block),
|
||||
Defer(Call),
|
||||
Break,
|
||||
Continue,
|
||||
Return(Option<Expression>),
|
||||
Return(Option<RValue>),
|
||||
}
|
||||
|
||||
#[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)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Expression {
|
||||
pub enum RValue {
|
||||
Empty,
|
||||
Binary {
|
||||
op: BinaryOperator,
|
||||
left: Box<Expression>,
|
||||
right: Box<Expression>,
|
||||
left: Box<RValue>,
|
||||
right: Box<RValue>,
|
||||
|
||||
// Post-Semantic Analysis
|
||||
type_id: Option<TypeId>,
|
||||
},
|
||||
Unary {
|
||||
op: UnaryOperator,
|
||||
operand: Box<Expression>,
|
||||
operand: Box<RValue>,
|
||||
|
||||
// Post-Semantic Analysis
|
||||
type_id: Option<TypeId>,
|
||||
},
|
||||
UnaryPostfix {
|
||||
op: UnaryOperator,
|
||||
operand: Box<Expression>,
|
||||
operand: Box<RValue>,
|
||||
|
||||
// Post-Semantic Analysis
|
||||
type_id: Option<TypeId>,
|
||||
@@ -251,21 +256,21 @@ pub enum Expression {
|
||||
expr_type: Option<TypeId>,
|
||||
},
|
||||
TypeCast {
|
||||
expr: Box<Expression>,
|
||||
expr: Box<RValue>,
|
||||
target_type: TypeId,
|
||||
|
||||
// Post-Semantic Analysis
|
||||
type_id: Option<TypeId>,
|
||||
},
|
||||
IndexAccess {
|
||||
expr: Box<Expression>,
|
||||
index: Box<Expression>,
|
||||
expr: Box<RValue>,
|
||||
index: Box<RValue>,
|
||||
|
||||
// Post-Semantic Analysis
|
||||
type_id: Option<TypeId>,
|
||||
},
|
||||
MemberAccess {
|
||||
expr: Box<Expression>,
|
||||
expr: Box<RValue>,
|
||||
field_name: Name,
|
||||
|
||||
// Post-Semantic Analysis
|
||||
@@ -281,12 +286,12 @@ pub enum Expression {
|
||||
StringLiteral(String),
|
||||
CharLiteral(char),
|
||||
ArrayLiteral {
|
||||
elements: Vec<Expression>,
|
||||
elements: Vec<RValue>,
|
||||
type_id: Option<TypeId>,
|
||||
},
|
||||
StructLiteral {
|
||||
name: Name,
|
||||
fields: Vec<(Name, Expression)>,
|
||||
fields: Vec<(Name, RValue)>,
|
||||
type_id: Option<TypeId>,
|
||||
},
|
||||
}
|
||||
@@ -300,56 +305,52 @@ pub enum Number {
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Call {
|
||||
pub name: Name,
|
||||
pub args: Vec<Expression>,
|
||||
pub args: Vec<RValue>,
|
||||
}
|
||||
|
||||
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<TypeId, CompilerError> {
|
||||
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())
|
||||
|
||||
@@ -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<VirtualReg>,
|
||||
},
|
||||
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<Rc<String>>);
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum VirtualReg {
|
||||
Void,
|
||||
Named(Rc<String>, isize),
|
||||
Anonymous(isize),
|
||||
}
|
||||
|
||||
impl VirtualReg {
|
||||
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,
|
||||
}
|
||||
|
||||
|
||||
#[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,
|
||||
|
||||
Reference in New Issue
Block a user