implemented most binary and unary operations for DSCS compiler (excl memory ops and boolean logic)
This commit is contained in:
@@ -1,9 +1,8 @@
|
||||
use std::any::Any;
|
||||
use dsa_tasks::ProcessAgent;
|
||||
|
||||
use crate::model::ast::{
|
||||
Block, Call, Declaration, Expression, Number, Program, Statement, TypeId, Variable,
|
||||
};
|
||||
use crate::model::dsa_ir::{BasicBlock, IRProgram, Label, Literal, Terminator, VirtualReg};
|
||||
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::{CompilerError, dsa_ir as IR};
|
||||
|
||||
pub struct IrLoweringCodeGenerator {
|
||||
@@ -383,8 +382,8 @@ impl IrLoweringCodeGenerator {
|
||||
}
|
||||
|
||||
// TODO! all of the below:
|
||||
Expression::Binary { .. } => Ok(()),
|
||||
Expression::Unary { .. } => Ok(()),
|
||||
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(()),
|
||||
@@ -394,6 +393,139 @@ impl IrLoweringCodeGenerator {
|
||||
Expression::StructLiteral { .. } => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
fn collapse_unary_op(
|
||||
&self,
|
||||
expr: &Expression,
|
||||
op: &UnaryOperator,
|
||||
ctx: &mut FunctionContext,
|
||||
result_reg: VirtualReg,
|
||||
) -> Result<(), CompilerError> {
|
||||
let intermediate_reg = ctx.new_vreg();
|
||||
self.collapse_expression(expr, ctx, intermediate_reg)?;
|
||||
|
||||
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,
|
||||
result: result_reg,
|
||||
},
|
||||
|
||||
UnaryOperator::Minus => IR::Statement::UnaryOp {
|
||||
op: PrimitiveUnaryOperation::Negation,
|
||||
operand: intermediate_reg,
|
||||
result: result_reg,
|
||||
},
|
||||
|
||||
// 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)));
|
||||
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)));
|
||||
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.
|
||||
UnaryOperator::Dereference => IR::Statement::Load {
|
||||
address: intermediate_reg,
|
||||
result: result_reg,
|
||||
},
|
||||
|
||||
// TODO: this may be quite complex as we have to guarantee ahead of time that this value has a memory address!
|
||||
UnaryOperator::AddressOf => todo!(),
|
||||
|
||||
// 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"))?
|
||||
.size();
|
||||
IR::Statement::Const(result_reg, Literal::Int(size as isize))
|
||||
},
|
||||
|
||||
UnaryOperator::BitwiseNot => IR::Statement::UnaryOp {
|
||||
op: PrimitiveUnaryOperation::Not,
|
||||
operand: intermediate_reg,
|
||||
result: result_reg,
|
||||
},
|
||||
|
||||
// TODO: the IR can't represent boolean operators yet. do we add it to the IR or do something here instead?
|
||||
UnaryOperator::LogicalNot => todo!(),
|
||||
};
|
||||
|
||||
ctx.push_stmt(ir_stmt);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn collapse_binary_expr(
|
||||
&self,
|
||||
left: &Expression,
|
||||
right: &Expression,
|
||||
op: &BinaryOperator,
|
||||
ctx: &mut FunctionContext,
|
||||
result_vreg: VirtualReg,
|
||||
) -> Result<(), CompilerError> {
|
||||
let left_vreg = ctx.new_vreg();
|
||||
self.collapse_expression(left, ctx, left_vreg)?;
|
||||
|
||||
let right_vreg = ctx.new_vreg();
|
||||
self.collapse_expression(right, ctx, right_vreg)?;
|
||||
|
||||
let primitive_operation = match op {
|
||||
// arithmetic
|
||||
BinaryOperator::Add => PrimitiveOperation::Add,
|
||||
BinaryOperator::Sub => PrimitiveOperation::Sub,
|
||||
|
||||
// bitwise logic
|
||||
BinaryOperator::BitwiseAnd => PrimitiveOperation::And,
|
||||
BinaryOperator::BitwiseOr => PrimitiveOperation::Or,
|
||||
BinaryOperator::BitwiseXor => PrimitiveOperation::Xor,
|
||||
BinaryOperator::LeftShift => PrimitiveOperation::LeftShift,
|
||||
BinaryOperator::RightShift => PrimitiveOperation::RightShift,
|
||||
|
||||
// comparison logic
|
||||
BinaryOperator::Equal => PrimitiveOperation::IsEqual,
|
||||
BinaryOperator::NotEqual => PrimitiveOperation::NotEqual,
|
||||
BinaryOperator::LessThan => PrimitiveOperation::LessThan,
|
||||
BinaryOperator::GreaterThan => PrimitiveOperation::GreaterThan,
|
||||
BinaryOperator::LessOrEqual => PrimitiveOperation::LessOrEqual,
|
||||
BinaryOperator::GreaterOrEqual => PrimitiveOperation::GreaterOrEqual,
|
||||
|
||||
// boolean logic
|
||||
BinaryOperator::LogicalAnd => todo!(),
|
||||
BinaryOperator::LogicalOr => todo!(),
|
||||
|
||||
// non-primitive arithmetic (TODO: requires library support)
|
||||
BinaryOperator::Mul => todo!(),
|
||||
BinaryOperator::Div => todo!(),
|
||||
BinaryOperator::Mod => todo!(),
|
||||
};
|
||||
|
||||
ctx.push_stmt(IR::Statement::BinaryOp {
|
||||
left: left_vreg,
|
||||
op: primitive_operation,
|
||||
right: right_vreg,
|
||||
result: result_vreg,
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct BasicBlockBuilder {
|
||||
|
||||
@@ -12,6 +12,11 @@ pub enum Statement {
|
||||
right: VirtualReg,
|
||||
result: VirtualReg,
|
||||
},
|
||||
UnaryOp {
|
||||
op: PrimitiveUnaryOperation,
|
||||
operand: VirtualReg,
|
||||
result: VirtualReg,
|
||||
},
|
||||
Copy {
|
||||
src: VirtualReg,
|
||||
dst: VirtualReg,
|
||||
@@ -124,6 +129,18 @@ pub enum Literal {
|
||||
Char(char),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum PrimitiveUnaryOperation {
|
||||
// used by compilers for no-ops such as the unary addition operation
|
||||
// this should be optimised out in almost all cases.
|
||||
Identity,
|
||||
// negate a given value
|
||||
Negation,
|
||||
// Invert the bits of the given operation.
|
||||
Not,
|
||||
}
|
||||
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum PrimitiveOperation {
|
||||
Add,
|
||||
@@ -133,6 +150,16 @@ pub enum PrimitiveOperation {
|
||||
Xor,
|
||||
Nor,
|
||||
Nand,
|
||||
|
||||
LeftShift,
|
||||
RightShift,
|
||||
|
||||
IsEqual,
|
||||
NotEqual,
|
||||
LessThan,
|
||||
LessOrEqual,
|
||||
GreaterThan,
|
||||
GreaterOrEqual,
|
||||
}
|
||||
|
||||
impl fmt::Display for VirtualReg {
|
||||
@@ -158,13 +185,32 @@ impl fmt::Display for Literal {
|
||||
impl fmt::Display for PrimitiveOperation {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
let s = match self {
|
||||
PrimitiveOperation::Add => "+",
|
||||
PrimitiveOperation::Sub => "-",
|
||||
PrimitiveOperation::And => "&",
|
||||
PrimitiveOperation::Or => "|",
|
||||
PrimitiveOperation::Xor => "^",
|
||||
PrimitiveOperation::Nor => "nor",
|
||||
PrimitiveOperation::Nand => "nand",
|
||||
Self::Add => "+",
|
||||
Self::Sub => "-",
|
||||
Self::And => "&",
|
||||
Self::Or => "|",
|
||||
Self::Xor => "^",
|
||||
Self::Nor => "nor",
|
||||
Self::Nand => "nand",
|
||||
Self::LeftShift => "<<",
|
||||
Self::RightShift => ">>",
|
||||
Self::IsEqual => "==",
|
||||
Self::NotEqual => "!=",
|
||||
Self::LessThan => "<",
|
||||
Self::LessOrEqual => "<=",
|
||||
Self::GreaterThan => ">",
|
||||
Self::GreaterOrEqual => ">=",
|
||||
};
|
||||
write!(f, "{}", s)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for PrimitiveUnaryOperation {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
let s = match self {
|
||||
Self::Identity => "+",
|
||||
Self::Negation => "-",
|
||||
Self::Not => "~",
|
||||
};
|
||||
write!(f, "{}", s)
|
||||
}
|
||||
@@ -174,6 +220,13 @@ impl fmt::Display for Statement {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Statement::Const(dst, lit) => write!(f, "{} = literal {}", dst, lit),
|
||||
Statement::UnaryOp {
|
||||
op,
|
||||
operand,
|
||||
result,
|
||||
} => {
|
||||
write!(f, "{} = {}{}", result, op, operand)
|
||||
}
|
||||
Statement::BinaryOp {
|
||||
left,
|
||||
op,
|
||||
|
||||
Reference in New Issue
Block a user