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 dsa_tasks::ProcessAgent;
|
||||||
|
|
||||||
use crate::model::ast::{
|
use crate::model::ast::{BinaryOperator, Block, Call, Declaration, Expression, Number, Program, Statement, TypeId, UnaryOperator, Variable};
|
||||||
Block, Call, Declaration, Expression, Number, Program, Statement, TypeId, Variable,
|
use crate::model::dsa_ir::{BasicBlock, IRProgram, Label, Literal, PrimitiveOperation, PrimitiveUnaryOperation, Terminator, VirtualReg};
|
||||||
};
|
|
||||||
use crate::model::dsa_ir::{BasicBlock, IRProgram, Label, Literal, Terminator, VirtualReg};
|
|
||||||
use crate::model::{CompilerError, dsa_ir as IR};
|
use crate::model::{CompilerError, dsa_ir as IR};
|
||||||
|
|
||||||
pub struct IrLoweringCodeGenerator {
|
pub struct IrLoweringCodeGenerator {
|
||||||
@@ -383,8 +382,8 @@ impl IrLoweringCodeGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO! all of the below:
|
// TODO! all of the below:
|
||||||
Expression::Binary { .. } => Ok(()),
|
Expression::Binary { op, left, right, .. } => self.collapse_binary_expr(left, right, op, ctx, v_reg),
|
||||||
Expression::Unary { .. } => Ok(()),
|
Expression::Unary { op, operand, .. } => self.collapse_unary_op(operand, op, ctx, v_reg),
|
||||||
Expression::UnaryPostfix { .. } => Ok(()),
|
Expression::UnaryPostfix { .. } => Ok(()),
|
||||||
Expression::TypeCast { .. } => Ok(()),
|
Expression::TypeCast { .. } => Ok(()),
|
||||||
Expression::IndexAccess { .. } => Ok(()),
|
Expression::IndexAccess { .. } => Ok(()),
|
||||||
@@ -394,6 +393,139 @@ impl IrLoweringCodeGenerator {
|
|||||||
Expression::StructLiteral { .. } => Ok(()),
|
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 {
|
struct BasicBlockBuilder {
|
||||||
|
|||||||
@@ -12,6 +12,11 @@ pub enum Statement {
|
|||||||
right: VirtualReg,
|
right: VirtualReg,
|
||||||
result: VirtualReg,
|
result: VirtualReg,
|
||||||
},
|
},
|
||||||
|
UnaryOp {
|
||||||
|
op: PrimitiveUnaryOperation,
|
||||||
|
operand: VirtualReg,
|
||||||
|
result: VirtualReg,
|
||||||
|
},
|
||||||
Copy {
|
Copy {
|
||||||
src: VirtualReg,
|
src: VirtualReg,
|
||||||
dst: VirtualReg,
|
dst: VirtualReg,
|
||||||
@@ -124,6 +129,18 @@ pub enum Literal {
|
|||||||
Char(char),
|
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)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum PrimitiveOperation {
|
pub enum PrimitiveOperation {
|
||||||
Add,
|
Add,
|
||||||
@@ -133,6 +150,16 @@ pub enum PrimitiveOperation {
|
|||||||
Xor,
|
Xor,
|
||||||
Nor,
|
Nor,
|
||||||
Nand,
|
Nand,
|
||||||
|
|
||||||
|
LeftShift,
|
||||||
|
RightShift,
|
||||||
|
|
||||||
|
IsEqual,
|
||||||
|
NotEqual,
|
||||||
|
LessThan,
|
||||||
|
LessOrEqual,
|
||||||
|
GreaterThan,
|
||||||
|
GreaterOrEqual,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for VirtualReg {
|
impl fmt::Display for VirtualReg {
|
||||||
@@ -158,13 +185,32 @@ impl fmt::Display for Literal {
|
|||||||
impl fmt::Display for PrimitiveOperation {
|
impl fmt::Display for PrimitiveOperation {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
let s = match self {
|
let s = match self {
|
||||||
PrimitiveOperation::Add => "+",
|
Self::Add => "+",
|
||||||
PrimitiveOperation::Sub => "-",
|
Self::Sub => "-",
|
||||||
PrimitiveOperation::And => "&",
|
Self::And => "&",
|
||||||
PrimitiveOperation::Or => "|",
|
Self::Or => "|",
|
||||||
PrimitiveOperation::Xor => "^",
|
Self::Xor => "^",
|
||||||
PrimitiveOperation::Nor => "nor",
|
Self::Nor => "nor",
|
||||||
PrimitiveOperation::Nand => "nand",
|
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)
|
write!(f, "{}", s)
|
||||||
}
|
}
|
||||||
@@ -174,6 +220,13 @@ 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::Const(dst, lit) => write!(f, "{} = literal {}", dst, lit),
|
Statement::Const(dst, lit) => write!(f, "{} = literal {}", dst, lit),
|
||||||
|
Statement::UnaryOp {
|
||||||
|
op,
|
||||||
|
operand,
|
||||||
|
result,
|
||||||
|
} => {
|
||||||
|
write!(f, "{} = {}{}", result, op, operand)
|
||||||
|
}
|
||||||
Statement::BinaryOp {
|
Statement::BinaryOp {
|
||||||
left,
|
left,
|
||||||
op,
|
op,
|
||||||
|
|||||||
Reference in New Issue
Block a user