Merge branch 'feat/cond'
This commit is contained in:
@@ -0,0 +1,2 @@
|
|||||||
|
[toolchain]
|
||||||
|
channel = "1.85.0"
|
||||||
+32
-4
@@ -2,8 +2,7 @@ use petgraph::dot::{Config, Dot};
|
|||||||
use petgraph::graph::{Graph, NodeIndex};
|
use petgraph::graph::{Graph, NodeIndex};
|
||||||
|
|
||||||
use crate::ast::types::{
|
use crate::ast::types::{
|
||||||
BlockStmt, CompileUnit, Expr, ExprValue, FuncDeclStmt, GlobalDeclStmt, Param, ReturnStmt,
|
BlockStmt, BreakStmt, CompileUnit, ContinueStmt, Expr, ExprValue, FuncDeclStmt, GlobalDeclStmt, IfStmt, Param, ReturnStmt, Statement, VarDeclStmt, VarDeclStmtValue, WhileStmt
|
||||||
Statement, VarDeclStmt, VarDeclStmtValue,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub type AstGraph = Graph<String, String>;
|
pub type AstGraph = Graph<String, String>;
|
||||||
@@ -116,7 +115,11 @@ impl AstGraphBuilder {
|
|||||||
let node = self.child(parent, stmt.to_string());
|
let node = self.child(parent, stmt.to_string());
|
||||||
self.add_var_decl(node, var_decl);
|
self.add_var_decl(node, var_decl);
|
||||||
node
|
node
|
||||||
}
|
},
|
||||||
|
Statement::If(if_stmt) => self.add_if_stmt(parent, if_stmt),
|
||||||
|
Statement::While(while_stmt) => self.add_while_stmt(parent, while_stmt),
|
||||||
|
Statement::Break(break_stmt) => self.add_break_stmt(parent, break_stmt),
|
||||||
|
Statement::Continue(continue_stmt) => self.add_continue_stmt(parent, continue_stmt),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,7 +129,27 @@ impl AstGraphBuilder {
|
|||||||
None => self.child(parent, "Void"),
|
None => self.child(parent, "Void"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fn add_if_stmt(&mut self, parent: NodeIndex, if_stmt: &IfStmt) -> NodeIndex {
|
||||||
|
let node = self.child(parent, if_stmt.to_string());
|
||||||
|
self.add_expr(node, &if_stmt.condition);
|
||||||
|
self.add_block_stmt(node, &if_stmt.then_branch);
|
||||||
|
if let Some(else_branch) = &if_stmt.else_branch {
|
||||||
|
self.add_block_stmt(node, else_branch);
|
||||||
|
}
|
||||||
|
node
|
||||||
|
}
|
||||||
|
fn add_while_stmt(&mut self, parent: NodeIndex, while_stmt: &WhileStmt) -> NodeIndex {
|
||||||
|
let node = self.child(parent, while_stmt.to_string());
|
||||||
|
self.add_expr(node, &while_stmt.condition);
|
||||||
|
self.add_block_stmt(node, &while_stmt.body);
|
||||||
|
node
|
||||||
|
}
|
||||||
|
fn add_break_stmt(&mut self, parent: NodeIndex, break_stmt: &BreakStmt) -> NodeIndex {
|
||||||
|
self.child(parent, break_stmt.to_string())
|
||||||
|
}
|
||||||
|
fn add_continue_stmt(&mut self, parent: NodeIndex, continue_stmt: &ContinueStmt) -> NodeIndex {
|
||||||
|
self.child(parent, continue_stmt.to_string())
|
||||||
|
}
|
||||||
fn add_expr(&mut self, parent: NodeIndex, expr: &Expr) -> NodeIndex {
|
fn add_expr(&mut self, parent: NodeIndex, expr: &Expr) -> NodeIndex {
|
||||||
match &expr.value {
|
match &expr.value {
|
||||||
ExprValue::IntLit(_) | ExprValue::Var(_) => self.child(parent, expr.value.to_string()),
|
ExprValue::IntLit(_) | ExprValue::Var(_) => self.child(parent, expr.value.to_string()),
|
||||||
@@ -149,6 +172,11 @@ impl AstGraphBuilder {
|
|||||||
self.add_expr(node, lvalue);
|
self.add_expr(node, lvalue);
|
||||||
self.add_expr(node, rvalue);
|
self.add_expr(node, rvalue);
|
||||||
node
|
node
|
||||||
|
},
|
||||||
|
ExprValue::UnaryOp { op: _, operand } => {
|
||||||
|
let node = self.child(parent, expr.value.to_string());
|
||||||
|
self.add_expr(node, operand);
|
||||||
|
node
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+107
-19
@@ -1,4 +1,4 @@
|
|||||||
use crate::{diagnostic::span::Span, frontend::types::{TokenValue, TypeIdent}};
|
use crate::{diagnostic::span::Span, frontend::types::{Token, TokenValue, TypeIdent}};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
pub struct CompileUnit {
|
pub struct CompileUnit {
|
||||||
@@ -11,12 +11,12 @@ pub enum GlobalDeclStmt {
|
|||||||
|
|
||||||
pub struct VarDeclStmt {
|
pub struct VarDeclStmt {
|
||||||
pub values: Vec<VarDeclStmtValue>,
|
pub values: Vec<VarDeclStmtValue>,
|
||||||
pub span: Span,
|
pub data_type: Type,
|
||||||
|
pub type_span: Span,
|
||||||
}
|
}
|
||||||
pub struct VarDeclStmtValue {
|
pub struct VarDeclStmtValue {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub var_type: Type,
|
pub name_span: Span,
|
||||||
pub span: Span,
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -25,11 +25,11 @@ pub struct FuncDeclStmt {
|
|||||||
pub return_type: Type,
|
pub return_type: Type,
|
||||||
pub params: Vec<Param>,
|
pub params: Vec<Param>,
|
||||||
pub body: BlockStmt,
|
pub body: BlockStmt,
|
||||||
pub span: Span,
|
pub ret_type_span: Span,
|
||||||
|
pub name_span: Span,
|
||||||
}
|
}
|
||||||
pub struct BlockStmt {
|
pub struct BlockStmt {
|
||||||
pub statements: Vec<Statement>,
|
pub statements: Vec<Statement>,
|
||||||
pub span: Span,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum Statement {
|
pub enum Statement {
|
||||||
@@ -37,16 +37,46 @@ pub enum Statement {
|
|||||||
Block(BlockStmt),
|
Block(BlockStmt),
|
||||||
Expr(Expr),
|
Expr(Expr),
|
||||||
VarDecl(VarDeclStmt),
|
VarDecl(VarDeclStmt),
|
||||||
|
If(IfStmt),
|
||||||
|
While(WhileStmt),
|
||||||
|
Break(BreakStmt),
|
||||||
|
Continue(ContinueStmt),
|
||||||
}
|
}
|
||||||
impl Statement {
|
// impl Statement {
|
||||||
pub fn span(&self) -> Span {
|
// pub fn span(&self) -> Span {
|
||||||
match self {
|
// match self {
|
||||||
Statement::Return(s) => s.span,
|
// Statement::Return(s) => s.span,
|
||||||
Statement::Block(s) => s.span,
|
// Statement::Block(s) => s.span,
|
||||||
Statement::Expr(s) => s.span,
|
// Statement::Expr(s) => s.span,
|
||||||
Statement::VarDecl(s) => s.span,
|
// Statement::VarDecl(s) => s.span,
|
||||||
}
|
// Statement::If(s) => s.span,
|
||||||
}
|
// Statement::While(s) => s.span,
|
||||||
|
// Statement::Break(s) => s.span,
|
||||||
|
// Statement::Continue(s) => s.span,
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
pub struct IfStmt {
|
||||||
|
pub condition: Expr,
|
||||||
|
pub then_branch: BlockStmt,
|
||||||
|
pub ifelse_branch: Vec<IfElseBranch>,
|
||||||
|
pub else_branch: Option<BlockStmt>,
|
||||||
|
// pub span: Span,
|
||||||
|
}
|
||||||
|
pub struct IfElseBranch {
|
||||||
|
pub condition: Expr,
|
||||||
|
pub then_branch: BlockStmt,
|
||||||
|
}
|
||||||
|
pub struct WhileStmt {
|
||||||
|
pub condition: Expr,
|
||||||
|
pub body: BlockStmt,
|
||||||
|
// pub span: Span,
|
||||||
|
}
|
||||||
|
pub struct BreakStmt {
|
||||||
|
pub span: Span,
|
||||||
|
}
|
||||||
|
pub struct ContinueStmt {
|
||||||
|
pub span: Span,
|
||||||
}
|
}
|
||||||
pub struct ReturnStmt {
|
pub struct ReturnStmt {
|
||||||
pub value: Option<Expr>,
|
pub value: Option<Expr>,
|
||||||
@@ -64,6 +94,10 @@ pub enum ExprValue {
|
|||||||
op: BinaryOp,
|
op: BinaryOp,
|
||||||
rhs: Box<Expr>
|
rhs: Box<Expr>
|
||||||
},
|
},
|
||||||
|
UnaryOp {
|
||||||
|
op: UnaryOp,
|
||||||
|
operand: Box<Expr>,
|
||||||
|
},
|
||||||
FuncCall(String, Vec<Expr>),
|
FuncCall(String, Vec<Expr>),
|
||||||
Assign {
|
Assign {
|
||||||
lvalue: Box<Expr>,
|
lvalue: Box<Expr>,
|
||||||
@@ -74,8 +108,13 @@ pub enum ExprValue {
|
|||||||
pub enum BinaryOp {
|
pub enum BinaryOp {
|
||||||
Add, Sub, Mul, Div, Mod,
|
Add, Sub, Mul, Div, Mod,
|
||||||
Equal, NotEqual, Less, LessEqual, Greater, GreaterEqual,
|
Equal, NotEqual, Less, LessEqual, Greater, GreaterEqual,
|
||||||
|
And, Or,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum UnaryOp {
|
||||||
|
Add, Sub, Not,
|
||||||
|
}
|
||||||
impl BinaryOp {
|
impl BinaryOp {
|
||||||
pub fn from_token_value(token_value: &TokenValue) -> Option<Self> {
|
pub fn from_token_value(token_value: &TokenValue) -> Option<Self> {
|
||||||
match token_value {
|
match token_value {
|
||||||
@@ -90,10 +129,20 @@ impl BinaryOp {
|
|||||||
TokenValue::LessEqual => Some(BinaryOp::LessEqual),
|
TokenValue::LessEqual => Some(BinaryOp::LessEqual),
|
||||||
TokenValue::Greater => Some(BinaryOp::Greater),
|
TokenValue::Greater => Some(BinaryOp::Greater),
|
||||||
TokenValue::GreaterEqual => Some(BinaryOp::GreaterEqual),
|
TokenValue::GreaterEqual => Some(BinaryOp::GreaterEqual),
|
||||||
|
TokenValue::And => Some(BinaryOp::And),
|
||||||
|
TokenValue::Or => Some(BinaryOp::Or),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn is_logical(&self) -> bool {
|
||||||
|
matches!(self, BinaryOp::And | BinaryOp::Or)
|
||||||
|
}
|
||||||
|
pub fn is_cmp(&self) -> bool {
|
||||||
|
matches!(self, BinaryOp::Equal | BinaryOp::NotEqual | BinaryOp::Less | BinaryOp::LessEqual | BinaryOp::Greater | BinaryOp::GreaterEqual)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub enum Type {
|
pub enum Type {
|
||||||
Int,
|
Int,
|
||||||
@@ -110,7 +159,8 @@ impl From<TypeIdent> for Type {
|
|||||||
pub struct Param {
|
pub struct Param {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub param_type: Type,
|
pub param_type: Type,
|
||||||
pub span: Span,
|
pub name_span: Span,
|
||||||
|
pub type_span: Span,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for CompileUnit {
|
impl fmt::Display for CompileUnit {
|
||||||
@@ -136,7 +186,7 @@ impl fmt::Display for VarDeclStmt {
|
|||||||
|
|
||||||
impl fmt::Display for VarDeclStmtValue {
|
impl fmt::Display for VarDeclStmtValue {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "{} {}", self.var_type, self.name)
|
write!(f, "{}", self.name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,16 +209,42 @@ impl fmt::Display for Statement {
|
|||||||
Statement::Block(_) => write!(f, "BlockStmt"),
|
Statement::Block(_) => write!(f, "BlockStmt"),
|
||||||
Statement::Expr(_) => write!(f, "ExprStmt"),
|
Statement::Expr(_) => write!(f, "ExprStmt"),
|
||||||
Statement::VarDecl(_) => write!(f, "VarDeclStmt"),
|
Statement::VarDecl(_) => write!(f, "VarDeclStmt"),
|
||||||
|
Statement::If(_) => write!(f, "IfStmt"),
|
||||||
|
Statement::While(_) => write!(f, "WhileStmt"),
|
||||||
|
Statement::Break(_) => write!(f, "BreakStmt"),
|
||||||
|
Statement::Continue(_) => write!(f, "ContinueStmt"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl fmt::Display for IfStmt {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "IfStmt")
|
||||||
|
}
|
||||||
|
}
|
||||||
impl fmt::Display for ReturnStmt {
|
impl fmt::Display for ReturnStmt {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "ReturnStmt")
|
write!(f, "ReturnStmt")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for WhileStmt {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "WhileStmt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for BreakStmt {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "BreakStmt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for ContinueStmt {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "ContinueStmt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl fmt::Display for Expr {
|
impl fmt::Display for Expr {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "{}", self.value)
|
write!(f, "{}", self.value)
|
||||||
@@ -183,6 +259,7 @@ impl fmt::Display for ExprValue {
|
|||||||
ExprValue::BinaryOp { op, .. } => write!(f, "BinaryOp({})", op),
|
ExprValue::BinaryOp { op, .. } => write!(f, "BinaryOp({})", op),
|
||||||
ExprValue::FuncCall(name, _) => write!(f, "FuncCall({})", name),
|
ExprValue::FuncCall(name, _) => write!(f, "FuncCall({})", name),
|
||||||
ExprValue::Assign { .. } => write!(f, "Assign"),
|
ExprValue::Assign { .. } => write!(f, "Assign"),
|
||||||
|
ExprValue::UnaryOp { op, .. } => write!(f, "UnaryOp({})", op),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -207,11 +284,22 @@ impl fmt::Display for BinaryOp {
|
|||||||
BinaryOp::LessEqual => "<=",
|
BinaryOp::LessEqual => "<=",
|
||||||
BinaryOp::Greater => ">",
|
BinaryOp::Greater => ">",
|
||||||
BinaryOp::GreaterEqual => ">=",
|
BinaryOp::GreaterEqual => ">=",
|
||||||
|
BinaryOp::And => "&&",
|
||||||
|
BinaryOp::Or => "||",
|
||||||
|
};
|
||||||
|
write!(f, "{}", op)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl fmt::Display for UnaryOp {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
let op = match self {
|
||||||
|
UnaryOp::Add => "+",
|
||||||
|
UnaryOp::Sub => "-",
|
||||||
|
UnaryOp::Not => "!",
|
||||||
};
|
};
|
||||||
write!(f, "{}", op)
|
write!(f, "{}", op)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Type {
|
impl fmt::Display for Type {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use std::{fmt::Display, ops::Add};
|
use std::{fmt::Display, ops::Add};
|
||||||
|
|
||||||
use crate::backend::register_allocator::{REG_FP, REG_LR, REG_PC, REG_R0, REG_R1, REG_R2, REG_R3, REG_R12, REG_SP, Register};
|
use crate::{backend::register_allocator::{REG_FP, REG_LR, REG_PC, REG_R0, REG_R1, REG_R2, REG_R3, REG_R12, REG_SP, Register}, ir::types::CmpOp as IRCmpOp};
|
||||||
pub enum ARMInstr{
|
pub enum ARMInstr{
|
||||||
Move(MoveInstr),
|
Move(MoveInstr),
|
||||||
Load(LoadInstr),
|
Load(LoadInstr),
|
||||||
@@ -10,11 +10,14 @@ pub enum ARMInstr{
|
|||||||
SDiv(SDivInstr),
|
SDiv(SDivInstr),
|
||||||
Add(AddInstr),
|
Add(AddInstr),
|
||||||
Sub(SubInstr),
|
Sub(SubInstr),
|
||||||
|
Rsb(RsbInstr),
|
||||||
Cmp(CmpInstr),
|
Cmp(CmpInstr),
|
||||||
Push(PushInstr),
|
Push(PushInstr),
|
||||||
Pop(PopInstr),
|
Pop(PopInstr),
|
||||||
FunctionHead(String, usize),
|
FunctionHead(String, usize),
|
||||||
Bl(BlInstr),
|
Bl(BlInstr),
|
||||||
|
B(BInstr),
|
||||||
|
Label(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for ARMInstr {
|
impl Display for ARMInstr {
|
||||||
@@ -28,11 +31,14 @@ impl Display for ARMInstr {
|
|||||||
ARMInstr::SDiv(instr) => write!(f, "{}", instr),
|
ARMInstr::SDiv(instr) => write!(f, "{}", instr),
|
||||||
ARMInstr::Add(instr) => write!(f, "{}", instr),
|
ARMInstr::Add(instr) => write!(f, "{}", instr),
|
||||||
ARMInstr::Sub(instr) => write!(f, "{}", instr),
|
ARMInstr::Sub(instr) => write!(f, "{}", instr),
|
||||||
|
ARMInstr::Rsb(instr) => write!(f, "{}", instr),
|
||||||
ARMInstr::Cmp(instr) => write!(f, "{}", instr),
|
ARMInstr::Cmp(instr) => write!(f, "{}", instr),
|
||||||
ARMInstr::Push(instr) => write!(f, "{}", instr),
|
ARMInstr::Push(instr) => write!(f, "{}", instr),
|
||||||
ARMInstr::Pop(instr) => write!(f, "{}", instr),
|
ARMInstr::Pop(instr) => write!(f, "{}", instr),
|
||||||
ARMInstr::Bl(instr) => write!(f, "{}", instr),
|
ARMInstr::Bl(instr) => write!(f, "{}", instr),
|
||||||
|
ARMInstr::B(instr) => write!(f, "{}", instr),
|
||||||
ARMInstr::FunctionHead(name, align_size) => write!(f, ".align {}\n.global {}\n.type {}, %function\n{}:", align_size, name, name, name),
|
ARMInstr::FunctionHead(name, align_size) => write!(f, ".align {}\n.global {}\n.type {}, %function\n{}:", align_size, name, name, name),
|
||||||
|
ARMInstr::Label(name) => write!(f, "{}:", name),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -56,6 +62,18 @@ pub enum ConditionCode {
|
|||||||
Gt,
|
Gt,
|
||||||
Ge,
|
Ge,
|
||||||
}
|
}
|
||||||
|
impl From<IRCmpOp> for ConditionCode {
|
||||||
|
fn from(cmp_op: IRCmpOp) -> Self {
|
||||||
|
match cmp_op {
|
||||||
|
IRCmpOp::Eq => ConditionCode::Eq,
|
||||||
|
IRCmpOp::Ne => ConditionCode::Ne,
|
||||||
|
IRCmpOp::Lt => ConditionCode::Lt,
|
||||||
|
IRCmpOp::Le => ConditionCode::Le,
|
||||||
|
IRCmpOp::Gt => ConditionCode::Gt,
|
||||||
|
IRCmpOp::Ge => ConditionCode::Ge,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
impl Display for ConditionCode {
|
impl Display for ConditionCode {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
let code_str = match self {
|
let code_str = match self {
|
||||||
@@ -89,6 +107,9 @@ impl MoveInstr {
|
|||||||
pub fn new_uncond(dest: Register, src: RegisterOrImm) -> ARMInstr {
|
pub fn new_uncond(dest: Register, src: RegisterOrImm) -> ARMInstr {
|
||||||
ARMInstr::Move(MoveInstr(None, dest, src))
|
ARMInstr::Move(MoveInstr(None, dest, src))
|
||||||
}
|
}
|
||||||
|
pub fn new_cond(condition: ConditionCode, dest: Register, src: RegisterOrImm) -> ARMInstr {
|
||||||
|
ARMInstr::Move(MoveInstr(Some(condition), dest, src))
|
||||||
|
}
|
||||||
pub fn new_sp_to_fp() -> ARMInstr {
|
pub fn new_sp_to_fp() -> ARMInstr {
|
||||||
ARMInstr::Move(MoveInstr(None, REG_FP, RegisterOrImm::Reg(REG_SP)))
|
ARMInstr::Move(MoveInstr(None, REG_FP, RegisterOrImm::Reg(REG_SP)))
|
||||||
}
|
}
|
||||||
@@ -205,20 +226,37 @@ impl SubInstr {
|
|||||||
ARMInstr::Sub(SubInstr(REG_SP, REG_SP, RegisterOrImm::Imm(offset)))
|
ARMInstr::Sub(SubInstr(REG_SP, REG_SP, RegisterOrImm::Imm(offset)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for SubInstr {
|
impl Display for SubInstr {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
let SubInstr(dest, left, right) = self;
|
let SubInstr(dest, left, right) = self;
|
||||||
write!(f, "sub {}, {}, {}", dest, left, right)
|
write!(f, "sub {}, {}, {}", dest, left, right)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub struct CmpInstr(Register, Register);
|
pub struct RsbInstr(Register, Register, RegisterOrImm);
|
||||||
|
impl RsbInstr {
|
||||||
|
pub fn new(dest: Register, left: Register, right: RegisterOrImm) -> ARMInstr {
|
||||||
|
ARMInstr::Rsb(RsbInstr(dest, left, right))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Display for RsbInstr {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let RsbInstr(dest, left, right) = self;
|
||||||
|
write!(f, "rsb {}, {}, {}", dest, left, right)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub struct CmpInstr(Register, RegisterOrImm);
|
||||||
impl Display for CmpInstr {
|
impl Display for CmpInstr {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
let CmpInstr(left, right) = self;
|
let CmpInstr(left, right) = self;
|
||||||
write!(f, "cmp {}, {}", left, right)
|
write!(f, "cmp {}, {}", left, right)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl CmpInstr {
|
||||||
|
pub fn new(left: Register, right: RegisterOrImm) -> ARMInstr {
|
||||||
|
ARMInstr::Cmp(CmpInstr(left, right))
|
||||||
|
}
|
||||||
|
}
|
||||||
pub struct PushInstr(Vec<Register>);
|
pub struct PushInstr(Vec<Register>);
|
||||||
impl PushInstr {
|
impl PushInstr {
|
||||||
pub fn new_push_fp_lr() -> ARMInstr {
|
pub fn new_push_fp_lr() -> ARMInstr {
|
||||||
@@ -272,16 +310,41 @@ impl Display for PopInstr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct BlInstr(String);
|
pub struct BlInstr(Option<ConditionCode>, String);
|
||||||
impl Display for BlInstr {
|
impl Display for BlInstr {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
let BlInstr(func_name) = self;
|
match &self.0 {
|
||||||
write!(f, "bl {}", func_name)
|
Some(condition) => write!(f, "bl{} {}", condition, self.1),
|
||||||
|
None => write!(f, "bl {}", self.1),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BlInstr {
|
impl BlInstr {
|
||||||
pub fn new(func_name: String) -> ARMInstr {
|
pub fn new(label_name: String) -> ARMInstr {
|
||||||
ARMInstr::Bl(BlInstr(func_name))
|
ARMInstr::Bl(BlInstr(None, label_name))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_cond(condition: ConditionCode, label_name: String) -> ARMInstr {
|
||||||
|
ARMInstr::Bl(BlInstr(Some(condition), label_name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub struct BInstr(Option<ConditionCode>, String);
|
||||||
|
impl Display for BInstr {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match &self.0 {
|
||||||
|
Some(condition) => write!(f, "b{} {}", condition, self.1),
|
||||||
|
None => write!(f, "b {}", self.1),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BInstr {
|
||||||
|
pub fn new(label_name: String) -> ARMInstr {
|
||||||
|
ARMInstr::B(BInstr(None, label_name))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_cond(condition: ConditionCode, label_name: String) -> ARMInstr {
|
||||||
|
ARMInstr::B(BInstr(Some(condition), label_name))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+128
-66
@@ -1,7 +1,10 @@
|
|||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use crate::{backend::{arm_instr::{ARMInstr, AddInstr, BlInstr, LoadInstr, LoadPseudoInstr, MoveInstr, MulInstr, PopInstr, PushInstr, RegisterOrImm, SDivInstr, StoreInstr, SubInstr}, register_allocator::{REG_R0, REG_R1, REG_R2, REG_R3, Register, RegisterAlloc, RegisterAllocator}, types::ARMAsmVar}, ir::types::{Function, IRInstr, MoveRValue, Variable, VariableType}};
|
use crate::{backend::{arm_instr::{ARMInstr, AddInstr, BInstr, BlInstr, CmpInstr, ConditionCode, LoadInstr, LoadPseudoInstr, MoveInstr, MulInstr, PopInstr, PushInstr, RegisterOrImm, RsbInstr, SDivInstr, StoreInstr, SubInstr}, register_allocator::{REG_R0, REG_R1, REG_R2, REG_R3, Register, RegisterAlloc, RegisterAllocator}, types::ARMAsmVar}, ir::types::{Function, IRInstr, MoveRValue, Variable, VariableOrIntLit, VariableType}};
|
||||||
use crate::ir::types::BinaryOp as IRBinaryOp;
|
use crate::ir::types::BinaryOp as IRBinaryOp;
|
||||||
|
use crate::ir::types::CmpOp as IRCmpOp;
|
||||||
|
use crate::ir::types::UnaryOp as IRUnaryOp;
|
||||||
|
|
||||||
pub const ARM_STACK_ALIGNMENT: usize = 8;
|
pub const ARM_STACK_ALIGNMENT: usize = 8;
|
||||||
pub struct Generator {
|
pub struct Generator {
|
||||||
instrs: Vec<ARMInstr>,
|
instrs: Vec<ARMInstr>,
|
||||||
@@ -11,6 +14,49 @@ pub struct Generator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const DEFAULT_VAR_ALIGN: usize = 4;
|
const DEFAULT_VAR_ALIGN: usize = 4;
|
||||||
|
|
||||||
|
fn load_variable(variable: Variable, reg_allocator: &mut RegisterAllocator, var_index_to_stack_offset: &BTreeMap<usize, usize>, instrs: &mut Vec<ARMInstr>) -> RegisterAlloc {
|
||||||
|
match variable.var_type {
|
||||||
|
VariableType::Global => {
|
||||||
|
let var_alloc = reg_allocator.alloc(variable).expect("Ran out of registers");
|
||||||
|
let var_reg = var_alloc.reg;
|
||||||
|
// if !var_alloc.is_reused {
|
||||||
|
let address_alloc = reg_allocator.alloc_any().expect("Ran out of registers");
|
||||||
|
instrs.push(LoadPseudoInstr::new(address_alloc.reg, format!("global_var_{}", variable.index)));
|
||||||
|
instrs.push(LoadInstr::new(var_reg, address_alloc.reg, None));
|
||||||
|
// }
|
||||||
|
var_alloc
|
||||||
|
},
|
||||||
|
VariableType::ParamTemp => {
|
||||||
|
todo!()
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
let stack_offset = var_index_to_stack_offset.get(&variable.index).expect("Variable not declared");
|
||||||
|
let var_alloc = reg_allocator.alloc(variable).expect("Ran out of registers");
|
||||||
|
// if !var_alloc.is_reused {
|
||||||
|
instrs.push(LoadInstr::new_stack(var_alloc.reg, *stack_offset as i32));
|
||||||
|
// }
|
||||||
|
var_alloc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn save_variable(variable: Variable, reg: Register, reg_allocator: &mut RegisterAllocator, var_index_to_stack_offset: &BTreeMap<usize, usize>, instrs: &mut Vec<ARMInstr>) {
|
||||||
|
match variable.var_type {
|
||||||
|
VariableType::Global => {
|
||||||
|
let address_alloc = reg_allocator.alloc_any().expect("Ran out of registers");
|
||||||
|
instrs.push(LoadPseudoInstr::new(address_alloc.reg, format!("global_var_{}", variable.index)));
|
||||||
|
instrs.push(StoreInstr::new(reg, address_alloc.reg, None));
|
||||||
|
},
|
||||||
|
VariableType::ParamTemp => {
|
||||||
|
todo!()
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
let stack_offset = var_index_to_stack_offset.get(&variable.index).expect("Variable not declared");
|
||||||
|
instrs.push(StoreInstr::new_stack(reg, *stack_offset as i32));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
impl Generator {
|
impl Generator {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
@@ -59,10 +105,10 @@ impl Generator {
|
|||||||
self.instrs.push(ARMInstr::FunctionHead(func.name.clone(), 4)); // Assuming 4-byte alignment for simplicity
|
self.instrs.push(ARMInstr::FunctionHead(func.name.clone(), 4)); // Assuming 4-byte alignment for simplicity
|
||||||
self.instrs.push(PushInstr::new_push_fp_lr());
|
self.instrs.push(PushInstr::new_push_fp_lr());
|
||||||
self.instrs.push(MoveInstr::new_sp_to_fp());
|
self.instrs.push(MoveInstr::new_sp_to_fp());
|
||||||
self.emit_func(body);
|
self.emit_func(&func.name, body);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emit_func(&mut self, instrs: Vec<IRInstr>) {
|
fn emit_func(&mut self, func_name: &str, instrs: Vec<IRInstr>) {
|
||||||
let mut encounter_entry = false;
|
let mut encounter_entry = false;
|
||||||
let mut stack_size_needed = 0;
|
let mut stack_size_needed = 0;
|
||||||
let mut var_index_to_stack_offset = BTreeMap::new();
|
let mut var_index_to_stack_offset = BTreeMap::new();
|
||||||
@@ -73,13 +119,8 @@ impl Generator {
|
|||||||
if let Some(v) = v {
|
if let Some(v) = v {
|
||||||
let ret_alloc = self.register_allocator.alloc_reg(REG_R0).expect("Ran out of registers");
|
let ret_alloc = self.register_allocator.alloc_reg(REG_R0).expect("Ran out of registers");
|
||||||
let ret_reg = ret_alloc.reg;
|
let ret_reg = ret_alloc.reg;
|
||||||
let v_alloc = self.register_allocator.alloc(v).expect("Ran out of registers");
|
let v_alloc = load_variable(v, &mut self.register_allocator, &var_index_to_stack_offset, &mut self.instrs);
|
||||||
let v_reg = v_alloc.reg;
|
self.instrs.push(MoveInstr::new_uncond(ret_reg, RegisterOrImm::Reg(v_alloc.reg)));
|
||||||
if !v_alloc.is_reused {
|
|
||||||
let v_stack_offset = var_index_to_stack_offset.get(&v.index).expect("Variable not declared");
|
|
||||||
self.instrs.push(LoadInstr::new_stack(v_reg, *v_stack_offset as i32));
|
|
||||||
}
|
|
||||||
self.instrs.push(MoveInstr::new_uncond(ret_reg, RegisterOrImm::Reg(v_reg)));
|
|
||||||
}
|
}
|
||||||
self.instrs.push(MoveInstr::new_fp_to_sp());
|
self.instrs.push(MoveInstr::new_fp_to_sp());
|
||||||
self.instrs.push(PopInstr::new_pop_fp_pc());
|
self.instrs.push(PopInstr::new_pop_fp_pc());
|
||||||
@@ -98,9 +139,60 @@ impl Generator {
|
|||||||
self.instrs.push(SubInstr::new_sp(stack_size_needed as i32));
|
self.instrs.push(SubInstr::new_sp(stack_size_needed as i32));
|
||||||
},
|
},
|
||||||
IRInstr::DefineFunc(_, _, _) => unreachable!(),
|
IRInstr::DefineFunc(_, _, _) => unreachable!(),
|
||||||
|
IRInstr::Cmp(variable, left, cmp_op, right) => self.emit_cmp(variable, left, cmp_op, right, &var_index_to_stack_offset),
|
||||||
|
IRInstr::Unary(variable, unary_op, variable1) => self.emit_unary(variable, unary_op, variable1, &var_index_to_stack_offset),
|
||||||
|
IRInstr::Goto(index) => self.instrs.push(BInstr::new(format!("label_{}_{}", func_name, index))),
|
||||||
|
IRInstr::CondGoto(variable, true_label_index, false_label_index) => {
|
||||||
|
let variable_alloc = load_variable(variable, &mut self.register_allocator, &var_index_to_stack_offset, &mut self.instrs);
|
||||||
|
let variable_reg = variable_alloc.reg;
|
||||||
|
self.instrs.push(CmpInstr::new(variable_reg, RegisterOrImm::Imm(0)));
|
||||||
|
self.instrs.push(BInstr::new_cond(ConditionCode::Ne, format!("label_{}_{}", func_name, true_label_index)));
|
||||||
|
self.instrs.push(BInstr::new(format!("label_{}_{}", func_name, false_label_index)));
|
||||||
|
|
||||||
|
},
|
||||||
|
IRInstr::Label(index) => self.instrs.push(ARMInstr::Label(format!("label_{}_{}", func_name, index))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fn emit_unary(&mut self, dest: Variable, unary_op: IRUnaryOp, variable: Variable, var_index_to_stack_offset: &BTreeMap<usize, usize>) {
|
||||||
|
let variable_alloc = load_variable(variable, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs);
|
||||||
|
let dest_alloc = self.register_allocator.alloc(dest).expect("Ran out of registers");
|
||||||
|
match unary_op {
|
||||||
|
IRUnaryOp::Neg => {
|
||||||
|
self.instrs.push(RsbInstr::new(dest_alloc.reg, variable_alloc.reg, RegisterOrImm::Imm(0)));
|
||||||
|
},
|
||||||
|
}
|
||||||
|
save_variable(dest, dest_alloc.reg, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs);
|
||||||
|
}
|
||||||
|
fn emit_cmp(&mut self, variable: Variable, left: VariableOrIntLit, cmp_op: IRCmpOp, right: VariableOrIntLit, var_index_to_stack_offset: &BTreeMap<usize, usize>) {
|
||||||
|
let left_alloc = match left {
|
||||||
|
VariableOrIntLit::Var(var) => {
|
||||||
|
load_variable(var, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs)
|
||||||
|
},
|
||||||
|
VariableOrIntLit::IntLit(lit) => {
|
||||||
|
let alloc = self.register_allocator.alloc_any().expect("Ran out of registers");
|
||||||
|
self.instrs.push(MoveInstr::new_uncond(alloc.reg, RegisterOrImm::Imm(lit)));
|
||||||
|
alloc
|
||||||
|
},
|
||||||
|
};
|
||||||
|
// TODO: check left == right
|
||||||
|
let right_alloc = match right {
|
||||||
|
VariableOrIntLit::Var(var) => {
|
||||||
|
load_variable(var, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs)
|
||||||
|
},
|
||||||
|
VariableOrIntLit::IntLit(lit) => {
|
||||||
|
let alloc = self.register_allocator.alloc_any().expect("Ran out of registers");
|
||||||
|
self.instrs.push(MoveInstr::new_uncond(alloc.reg, RegisterOrImm::Imm(lit)));
|
||||||
|
alloc
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let variable_alloc = self.register_allocator.alloc(variable).expect("Ran out of registers");
|
||||||
|
let variable_reg = variable_alloc.reg;
|
||||||
|
self.instrs.push(CmpInstr::new(left_alloc.reg, RegisterOrImm::Reg(right_alloc.reg)));
|
||||||
|
self.instrs.push(MoveInstr::new_uncond(variable_reg, RegisterOrImm::Imm(0)));
|
||||||
|
self.instrs.push(MoveInstr::new_cond(cmp_op.into(), variable_reg, RegisterOrImm::Imm(1)));
|
||||||
|
save_variable(variable, variable_alloc.reg, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs);
|
||||||
|
}
|
||||||
fn emit_func_call(&mut self, func: Function, args: Vec<Variable>, ret: Option<Variable>, var_index_to_stack_offset: &BTreeMap<usize, usize>) {
|
fn emit_func_call(&mut self, func: Function, args: Vec<Variable>, ret: Option<Variable>, var_index_to_stack_offset: &BTreeMap<usize, usize>) {
|
||||||
|
|
||||||
self.instrs.push(PushInstr::new_push_caller_save());
|
self.instrs.push(PushInstr::new_push_caller_save());
|
||||||
@@ -108,20 +200,15 @@ impl Generator {
|
|||||||
todo!("More than 4 arguments not supported yet");
|
todo!("More than 4 arguments not supported yet");
|
||||||
}
|
}
|
||||||
const ARG_REGS: [Register; 4] = [REG_R0, REG_R1, REG_R2, REG_R3];
|
const ARG_REGS: [Register; 4] = [REG_R0, REG_R1, REG_R2, REG_R3];
|
||||||
|
let mut arg_reg_allocs = Vec::new();
|
||||||
for (i, arg) in args.into_iter().enumerate() {
|
for (i, arg) in args.into_iter().enumerate() {
|
||||||
let arg_alloc = self.register_allocator.alloc(arg).expect("Ran out of registers");
|
arg_reg_allocs.push(self.register_allocator.alloc_reg(ARG_REGS[i]).expect("Ran out of registers"));
|
||||||
let arg_reg = arg_alloc.reg;
|
let arg_alloc = load_variable(arg, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs);
|
||||||
if !arg_alloc.is_reused {
|
self.instrs.push(MoveInstr::new_uncond(ARG_REGS[i], RegisterOrImm::Reg(arg_alloc.reg)));
|
||||||
let arg_stack_offset = var_index_to_stack_offset.get(&arg.index).expect("Variable not declared");
|
|
||||||
self.instrs.push(LoadInstr::new_stack(arg_reg, *arg_stack_offset as i32));
|
|
||||||
}
|
|
||||||
self.instrs.push(MoveInstr::new_uncond(ARG_REGS[i], RegisterOrImm::Reg(arg_reg)));
|
|
||||||
}
|
}
|
||||||
self.instrs.push(BlInstr::new(func.name.clone()));
|
self.instrs.push(BlInstr::new(func.name.clone()));
|
||||||
if let Some(ret) = ret {
|
if let Some(ret) = ret {
|
||||||
let ret_alloc = self.register_allocator.alloc(ret).expect("Ran out of registers");
|
save_variable(ret, REG_R0, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs);
|
||||||
let ret_reg = ret_alloc.reg;
|
|
||||||
self.instrs.push(MoveInstr::new_uncond(ret_reg, RegisterOrImm::Reg(REG_R0)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -132,72 +219,47 @@ impl Generator {
|
|||||||
let dest_register = dest_alloc.reg;
|
let dest_register = dest_alloc.reg;
|
||||||
match src {
|
match src {
|
||||||
MoveRValue::Var(variable) => {
|
MoveRValue::Var(variable) => {
|
||||||
if !dest_alloc.is_reused {
|
let src_alloc = load_variable(variable, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs);
|
||||||
let var_stack_offset = var_index_to_stack_offset.get(&variable.index).expect("Variable not found");
|
self.instrs.push(MoveInstr::new_uncond(dest_register, RegisterOrImm::Reg(src_alloc.reg)));
|
||||||
self.instrs.push(LoadInstr::new_stack(dest_register, *var_stack_offset as i32));
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
MoveRValue::ConstInt(literal_int) => self.instrs.push(MoveInstr::new_uncond(dest_register, RegisterOrImm::Imm(literal_int))),
|
MoveRValue::ConstInt(literal_int) => self.instrs.push(MoveInstr::new_uncond(dest_register, RegisterOrImm::Imm(literal_int))),
|
||||||
};
|
};
|
||||||
match dest.var_type {
|
save_variable(dest, dest_alloc.reg, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs);
|
||||||
VariableType::Global => {
|
|
||||||
let address_reg = self.register_allocator.alloc_any().expect("Ran out of registers");
|
|
||||||
self.instrs.push(LoadPseudoInstr::new(address_reg, format!("global_var_{}", dest.index)));
|
|
||||||
self.instrs.push(StoreInstr::new(dest_register, address_reg, None));
|
|
||||||
},
|
|
||||||
VariableType::ParamTemp => {
|
|
||||||
todo!()
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
let offset = *var_index_to_stack_offset.get(&dest.index).expect("Variable not declared");
|
|
||||||
self.instrs.push(StoreInstr::new_stack(dest_register, offset as i32));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
fn emit_binary(&mut self, dest: Variable, left: Variable, op: IRBinaryOp, right: Variable, var_index_to_stack_offset: &BTreeMap<usize, usize>) {
|
fn emit_binary(&mut self, dest: Variable, left: Variable, op: IRBinaryOp, right: Variable, var_index_to_stack_offset: &BTreeMap<usize, usize>) {
|
||||||
let left_alloc = self.register_allocator.alloc(left).expect("Ran out of registers");
|
|
||||||
let right_alloc = self.register_allocator.alloc(right).expect("Ran out of registers");
|
|
||||||
let dest_alloc = self.register_allocator.alloc(dest).expect("Ran out of registers");
|
let dest_alloc = self.register_allocator.alloc(dest).expect("Ran out of registers");
|
||||||
let left_reg = left_alloc.reg;
|
|
||||||
let right_reg = right_alloc.reg;
|
|
||||||
let dest_reg = dest_alloc.reg;
|
let dest_reg = dest_alloc.reg;
|
||||||
if !left_alloc.is_reused {
|
// should consider left == right
|
||||||
let left_offset = var_index_to_stack_offset.get(&left.index).expect("Variable not declared");
|
let left_alloc = load_variable(left, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs);
|
||||||
self.instrs.push(LoadInstr::new_stack(left_reg, *left_offset as i32));
|
let (_right_alloc, right_reg) = if left != right {
|
||||||
|
let right_alloc = load_variable(right, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs);
|
||||||
}
|
let right_reg = right_alloc.reg;
|
||||||
if !right_alloc.is_reused {
|
(Some(right_alloc), right_reg)
|
||||||
let right_offset = var_index_to_stack_offset.get(&right.index).expect("Variable not declared");
|
} else {
|
||||||
self.instrs.push(LoadInstr::new_stack(right_reg, *right_offset as i32));
|
(None, left_alloc.reg)
|
||||||
}
|
};
|
||||||
match op {
|
match op {
|
||||||
IRBinaryOp::Add => {
|
IRBinaryOp::Add => {
|
||||||
self.instrs.push(AddInstr::new(dest_reg, left_reg, RegisterOrImm::Reg(right_reg)));
|
self.instrs.push(AddInstr::new(dest_reg, left_alloc.reg, RegisterOrImm::Reg(right_reg)));
|
||||||
},
|
},
|
||||||
IRBinaryOp::Sub => {
|
IRBinaryOp::Sub => {
|
||||||
self.instrs.push(SubInstr::new(dest_reg, left_reg, RegisterOrImm::Reg(right_reg)));
|
self.instrs.push(SubInstr::new(dest_reg, left_alloc.reg, RegisterOrImm::Reg(right_reg)));
|
||||||
},
|
},
|
||||||
IRBinaryOp::Mul => {
|
IRBinaryOp::Mul => {
|
||||||
self.instrs.push(MulInstr::new(dest_reg, left_reg, RegisterOrImm::Reg(right_reg)));
|
self.instrs.push(MulInstr::new(dest_reg, left_alloc.reg, RegisterOrImm::Reg(right_reg)));
|
||||||
},
|
},
|
||||||
IRBinaryOp::Div => {
|
IRBinaryOp::Div => {
|
||||||
self.instrs.push(SDivInstr::new(dest_reg, left_reg, RegisterOrImm::Reg(right_reg)));
|
self.instrs.push(SDivInstr::new(dest_reg, left_alloc.reg, RegisterOrImm::Reg(right_reg)));
|
||||||
},
|
},
|
||||||
IRBinaryOp::Mod => {
|
IRBinaryOp::Mod => {
|
||||||
let temp_reg = self.register_allocator.alloc_any().expect("Ran out of registers");
|
let temp_alloc = self.register_allocator.alloc_any().expect("Ran out of registers");
|
||||||
self.instrs.push(SDivInstr::new(temp_reg, left_reg, RegisterOrImm::Reg(right_reg)));
|
let temp_reg = temp_alloc.reg;
|
||||||
|
self.instrs.push(SDivInstr::new(temp_reg, left_alloc.reg, RegisterOrImm::Reg(right_reg)));
|
||||||
self.instrs.push(MulInstr::new(temp_reg, temp_reg, RegisterOrImm::Reg(right_reg)));
|
self.instrs.push(MulInstr::new(temp_reg, temp_reg, RegisterOrImm::Reg(right_reg)));
|
||||||
self.instrs.push(SubInstr::new(dest_reg, left_reg, RegisterOrImm::Reg(temp_reg)));
|
self.instrs.push(SubInstr::new(dest_reg, left_alloc.reg, RegisterOrImm::Reg(temp_reg)));
|
||||||
},
|
},
|
||||||
IRBinaryOp::Le => todo!(),
|
|
||||||
IRBinaryOp::Lt => todo!(),
|
|
||||||
IRBinaryOp::Gt => todo!(),
|
|
||||||
IRBinaryOp::Ge => todo!(),
|
|
||||||
IRBinaryOp::Ne => todo!(),
|
|
||||||
IRBinaryOp::Eq => todo!(),
|
|
||||||
}
|
}
|
||||||
let dest_stack_offset = var_index_to_stack_offset.get(&dest.index).expect("Variable not declared");
|
save_variable(dest, dest_alloc.reg, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs);
|
||||||
self.instrs.push(StoreInstr::new_stack(dest_reg, *dest_stack_offset as i32));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -74,4 +74,8 @@ mod tests {
|
|||||||
test_case("0-3,14-25");
|
test_case("0-3,14-25");
|
||||||
// test_case("0-3,14-25");
|
// test_case("0-3,14-25");
|
||||||
}
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_if_while() {
|
||||||
|
test_case("26-32,34-41,46-51,57");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -109,6 +109,9 @@ impl RegisterAllocator {
|
|||||||
// Variable already has a register allocated
|
// Variable already has a register allocated
|
||||||
let use_kind = inner.register_map.get_mut(®).expect("Inconsistent state: variable has a register but it's not in the register map");
|
let use_kind = inner.register_map.get_mut(®).expect("Inconsistent state: variable has a register but it's not in the register map");
|
||||||
assert!(matches!(use_kind, RegisterUseKind::UsedByVariable(v) | RegisterUseKind::AllocatedToVariable(v) if *v == var));
|
assert!(matches!(use_kind, RegisterUseKind::UsedByVariable(v) | RegisterUseKind::AllocatedToVariable(v) if *v == var));
|
||||||
|
if matches!(use_kind, RegisterUseKind::UsedByVariable(_)) {
|
||||||
|
panic!("variable already actively borrowed");
|
||||||
|
}
|
||||||
*use_kind = RegisterUseKind::UsedByVariable(var);
|
*use_kind = RegisterUseKind::UsedByVariable(var);
|
||||||
return Some(RegisterAlloc {
|
return Some(RegisterAlloc {
|
||||||
allocator: Rc::downgrade(&self.inner),
|
allocator: Rc::downgrade(&self.inner),
|
||||||
@@ -179,13 +182,17 @@ impl RegisterAllocator {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn alloc_any(&mut self) -> Option<Register> {
|
pub fn alloc_any(&mut self) -> Option<RegisterAlloc> {
|
||||||
let mut inner = self.inner.borrow_mut();
|
let mut inner = self.inner.borrow_mut();
|
||||||
|
|
||||||
for (®, use_kind) in inner.register_map.iter_mut() {
|
for (®, use_kind) in inner.register_map.iter_mut() {
|
||||||
if let RegisterUseKind::Free = use_kind {
|
if let RegisterUseKind::Free = use_kind {
|
||||||
*use_kind = RegisterUseKind::Designated;
|
*use_kind = RegisterUseKind::Designated;
|
||||||
return Some(reg);
|
return Some(RegisterAlloc {
|
||||||
|
allocator: Rc::downgrade(&self.inner),
|
||||||
|
reg,
|
||||||
|
is_reused: false,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let RegisterAllocatorInner{register_map, variable_to_register} = &mut *inner;
|
let RegisterAllocatorInner{register_map, variable_to_register} = &mut *inner;
|
||||||
@@ -193,7 +200,11 @@ impl RegisterAllocator {
|
|||||||
if let RegisterUseKind::AllocatedToVariable(ori_var) = use_kind {
|
if let RegisterUseKind::AllocatedToVariable(ori_var) = use_kind {
|
||||||
variable_to_register.remove(&ori_var);
|
variable_to_register.remove(&ori_var);
|
||||||
*use_kind = RegisterUseKind::Designated;
|
*use_kind = RegisterUseKind::Designated;
|
||||||
return Some(reg);
|
return Some(RegisterAlloc {
|
||||||
|
allocator: Rc::downgrade(&self.inner),
|
||||||
|
reg,
|
||||||
|
is_reused: false,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -28,7 +28,7 @@ pub enum ParseError {
|
|||||||
UnexpectedToken(TokenValue, &'static str),
|
UnexpectedToken(TokenValue, &'static str),
|
||||||
#[error("cannot combine with previous {}", .0)]
|
#[error("cannot combine with previous {}", .0)]
|
||||||
CantCombineWith(TokenValue),
|
CantCombineWith(TokenValue),
|
||||||
#[error("expect {0}")]
|
#[error("expect {0} after")]
|
||||||
ExpectButEof(&'static str),
|
ExpectButEof(&'static str),
|
||||||
}
|
}
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Error)]
|
#[derive(Debug, Clone, PartialEq, Eq, Error)]
|
||||||
|
|||||||
+36
-16
@@ -17,7 +17,7 @@ pub struct Lexer {
|
|||||||
|
|
||||||
const WHITESPACE_CHARS: &[char] = &[' ', '\t', '\n', '\r'];
|
const WHITESPACE_CHARS: &[char] = &[' ', '\t', '\n', '\r'];
|
||||||
const DELIMITER_CHARS: &[char] = &[
|
const DELIMITER_CHARS: &[char] = &[
|
||||||
'+', '-', '*', '/', '%', '=', '!', '<', '>', '(', ')', ',', ';'
|
'+', '-', '*', '/', '%', '=', '!', '<', '>', '(', ')', ',', ';', '{', '|', '&'
|
||||||
];
|
];
|
||||||
struct Cursor {
|
struct Cursor {
|
||||||
chars: Vec<char>,
|
chars: Vec<char>,
|
||||||
@@ -185,22 +185,10 @@ fn parse_litint(
|
|||||||
) -> Result<TokenValue, LexParseError> {
|
) -> Result<TokenValue, LexParseError> {
|
||||||
let mut c1 = str_iter.peek().ok_or(LexParseError::NotMatched)?;
|
let mut c1 = str_iter.peek().ok_or(LexParseError::NotMatched)?;
|
||||||
// c1 is the peek value from here
|
// c1 is the peek value from here
|
||||||
let mut sign_base: i64 = 1;
|
|
||||||
let mut base: i64 = 10;
|
let mut base: i64 = 10;
|
||||||
if !(c1.is_ascii_digit() || c1 == '-') {
|
if !(c1.is_ascii_digit()) {
|
||||||
return Err(LexParseError::NotMatched);
|
return Err(LexParseError::NotMatched);
|
||||||
}
|
}
|
||||||
if c1 == '-' {
|
|
||||||
sign_base = -1;
|
|
||||||
str_iter.advance(1);
|
|
||||||
c1 = str_iter.peek().ok_or(LexParseError::NotMatched)?;
|
|
||||||
if !c1.is_ascii_digit() {
|
|
||||||
// only a minus sign, not a number
|
|
||||||
// back one so cursor still points to the minus sign
|
|
||||||
str_iter.back(1);
|
|
||||||
return Err(LexParseError::NotMatched);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let mut number = 0i64;
|
let mut number = 0i64;
|
||||||
let mut has_digits = false;
|
let mut has_digits = false;
|
||||||
if c1 == '0' {
|
if c1 == '0' {
|
||||||
@@ -248,7 +236,6 @@ fn parse_litint(
|
|||||||
// No valid digits found, add a diagnostic
|
// No valid digits found, add a diagnostic
|
||||||
return Err(LexParseError::InvalidInMatch(LexingError::InvalidIntLiteral));
|
return Err(LexParseError::InvalidInMatch(LexingError::InvalidIntLiteral));
|
||||||
}
|
}
|
||||||
number *= sign_base;
|
|
||||||
Ok(TokenValue::IntLit(number))
|
Ok(TokenValue::IntLit(number))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -293,6 +280,7 @@ fn parse_puncuation(
|
|||||||
if let Some('=') = str_iter.peek() {
|
if let Some('=') = str_iter.peek() {
|
||||||
TokenValue::NotEqual
|
TokenValue::NotEqual
|
||||||
} else {
|
} else {
|
||||||
|
str_iter.back(1);
|
||||||
TokenValue::Not
|
TokenValue::Not
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -301,7 +289,24 @@ fn parse_puncuation(
|
|||||||
|
|
||||||
',' => TokenValue::Comma,
|
',' => TokenValue::Comma,
|
||||||
';' => TokenValue::Semicolon,
|
';' => TokenValue::Semicolon,
|
||||||
|
'|' => {
|
||||||
|
str_iter.advance(1);
|
||||||
|
if let Some('|') = str_iter.peek() {
|
||||||
|
TokenValue::Or
|
||||||
|
} else {
|
||||||
|
// unrecognized token starting with '|'
|
||||||
|
return Err(LexParseError::InvalidInMatch(LexingError::UnrecognizedToken("|".to_string())));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'&' => {
|
||||||
|
str_iter.advance(1);
|
||||||
|
if let Some('&') = str_iter.peek() {
|
||||||
|
TokenValue::And
|
||||||
|
} else {
|
||||||
|
// unrecognized token starting with '&'
|
||||||
|
return Err(LexParseError::InvalidInMatch(LexingError::UnrecognizedToken("&".to_string())));
|
||||||
|
}
|
||||||
|
},
|
||||||
_ => return Err(LexParseError::NotMatched),
|
_ => return Err(LexParseError::NotMatched),
|
||||||
};
|
};
|
||||||
str_iter.advance(1);
|
str_iter.advance(1);
|
||||||
@@ -330,6 +335,21 @@ fn parse_ident(
|
|||||||
if name.eq("return") {
|
if name.eq("return") {
|
||||||
return Ok(TokenValue::Return);
|
return Ok(TokenValue::Return);
|
||||||
}
|
}
|
||||||
|
if name.eq("if") {
|
||||||
|
return Ok(TokenValue::If);
|
||||||
|
}
|
||||||
|
if name.eq("else") {
|
||||||
|
return Ok(TokenValue::Else);
|
||||||
|
}
|
||||||
|
if name.eq("while") {
|
||||||
|
return Ok(TokenValue::While);
|
||||||
|
}
|
||||||
|
if name.eq("break") {
|
||||||
|
return Ok(TokenValue::Break);
|
||||||
|
}
|
||||||
|
if name.eq("continue") {
|
||||||
|
return Ok(TokenValue::Continue);
|
||||||
|
}
|
||||||
if let Some(type_ident) = TypeIdent::from_str(&name).ok() {
|
if let Some(type_ident) = TypeIdent::from_str(&name).ok() {
|
||||||
return Ok(TokenValue::TypeIdent(type_ident));
|
return Ok(TokenValue::TypeIdent(type_ident));
|
||||||
}
|
}
|
||||||
|
|||||||
+556
-229
File diff suppressed because it is too large
Load Diff
@@ -17,7 +17,7 @@ pub enum TokenValue {
|
|||||||
TypeIdent(TypeIdent),
|
TypeIdent(TypeIdent),
|
||||||
|
|
||||||
Plus, Minus, Star, Slash, Percent,
|
Plus, Minus, Star, Slash, Percent,
|
||||||
Equal, DoubleEqual, Not, NotEqual, Less, LessEqual, Greater, GreaterEqual,
|
Equal, DoubleEqual, Not, NotEqual, Less, LessEqual, Greater, GreaterEqual, And, Or,
|
||||||
|
|
||||||
LParen, RParen,
|
LParen, RParen,
|
||||||
LBrace, RBrace,
|
LBrace, RBrace,
|
||||||
@@ -58,6 +58,8 @@ impl std::fmt::Display for TokenValue {
|
|||||||
TokenValue::Equal => write!(f, "`=`"),
|
TokenValue::Equal => write!(f, "`=`"),
|
||||||
TokenValue::DoubleEqual => write!(f, "`==`"),
|
TokenValue::DoubleEqual => write!(f, "`==`"),
|
||||||
TokenValue::Not => write!(f, "`!`"),
|
TokenValue::Not => write!(f, "`!`"),
|
||||||
|
TokenValue::And => write!(f, "`&&`"),
|
||||||
|
TokenValue::Or => write!(f, "`||`"),
|
||||||
TokenValue::NotEqual => write!(f, "`!=`"),
|
TokenValue::NotEqual => write!(f, "`!=`"),
|
||||||
TokenValue::Less => write!(f, "`<`"),
|
TokenValue::Less => write!(f, "`<`"),
|
||||||
TokenValue::LessEqual => write!(f, "`<=`"),
|
TokenValue::LessEqual => write!(f, "`<=`"),
|
||||||
|
|||||||
@@ -24,4 +24,12 @@ pub enum IRError {
|
|||||||
TypeMismatch(IRType, IRType),
|
TypeMismatch(IRType, IRType),
|
||||||
#[error("invalid assignment target")]
|
#[error("invalid assignment target")]
|
||||||
InvalidAssignmentTarget,
|
InvalidAssignmentTarget,
|
||||||
|
#[error("break statement outside of loop")]
|
||||||
|
BreakOutsideLoop,
|
||||||
|
#[error("continue statement outside of loop")]
|
||||||
|
ContinueOutsideLoop,
|
||||||
|
#[error("invalid parameter type: {0}")]
|
||||||
|
InvalidParameterType(IRType),
|
||||||
|
#[error("return expression on void function")]
|
||||||
|
ReturnExpressionOnVoidFunction,
|
||||||
}
|
}
|
||||||
+457
-66
@@ -1,12 +1,22 @@
|
|||||||
use std::{collections::{BTreeMap, BTreeSet}, result, vec};
|
use std::{collections::{BTreeMap, BTreeSet}, vec};
|
||||||
|
|
||||||
use crate::{ast::types::{BlockStmt, CompileUnit, Expr, FuncDeclStmt, GlobalDeclStmt, ReturnStmt, Statement, VarDeclStmt}, diagnostic::Diagnositics, ir::{err::IRError, types::{BinaryOp, Function, IRInstr, IRType, MoveRValue, Variable, VariableType}}};
|
|
||||||
|
|
||||||
|
use crate::{ast::types::{BlockStmt, BreakStmt, CompileUnit, ContinueStmt, Expr, ExprValue, FuncDeclStmt, GlobalDeclStmt, IfElseBranch, IfStmt, ReturnStmt, Statement, VarDeclStmt, WhileStmt}, diagnostic::Diagnositics, ir::{err::IRError, types::{BinaryOp, CmpOp, Function, IRInstr, IRType, MoveRValue, UnaryOp, Variable, VariableOrIntLit, VariableType}}};
|
||||||
|
use crate::ast::types::BinaryOp as AstBinaryOp;
|
||||||
|
use crate::ast::types::UnaryOp as AstUnaryOp;
|
||||||
pub struct Generator {
|
pub struct Generator {
|
||||||
var_manager: VariableManager,
|
var_manager: VariableManager,
|
||||||
function_map: BTreeMap<String, Function>,
|
function_map: BTreeMap<String, Function>,
|
||||||
current_func_return_type: Option<IRType>,
|
current_func_return_type: Option<IRType>,
|
||||||
diagnostic: Diagnositics,
|
diagnostic: Diagnositics,
|
||||||
|
current_exit_label: Vec<Option<(usize, usize)>>, // true exit, false exit
|
||||||
|
// About exit label passing:
|
||||||
|
// for non-logical parent expr, current_exit_label is None
|
||||||
|
// this means we need to create label for logical child expr, and calculate the value
|
||||||
|
// for logical parent expr, we can directly use the label for logical child expr, and no need to calculate
|
||||||
|
// if child expr isn't logical, we need to do cmp to decide which label to goto
|
||||||
|
while_exit_label: Vec<(usize, usize)>, // continue exit, break exit
|
||||||
|
func_exit: Option<(usize, Option<Variable>)>, // (label, return_var)
|
||||||
|
label_counter: usize
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Generator {
|
impl Generator {
|
||||||
@@ -17,14 +27,27 @@ impl Generator {
|
|||||||
parameter_types: vec![IRType::I32],
|
parameter_types: vec![IRType::I32],
|
||||||
return_type: IRType::Void,
|
return_type: IRType::Void,
|
||||||
});
|
});
|
||||||
|
function_map.insert("getint".to_string(), Function {
|
||||||
|
name: "getint".to_string(),
|
||||||
|
parameter_types: vec![],
|
||||||
|
return_type: IRType::I32,
|
||||||
|
});
|
||||||
Self {
|
Self {
|
||||||
var_manager: VariableManager::new(),
|
var_manager: VariableManager::new(),
|
||||||
current_func_return_type: None,
|
current_func_return_type: None,
|
||||||
diagnostic: Diagnositics::new(),
|
diagnostic: Diagnositics::new(),
|
||||||
function_map,
|
function_map,
|
||||||
|
current_exit_label: vec![],
|
||||||
|
while_exit_label: vec![],
|
||||||
|
func_exit: None,
|
||||||
|
label_counter: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fn request_label(&mut self) -> usize {
|
||||||
|
let label = self.label_counter;
|
||||||
|
self.label_counter += 1;
|
||||||
|
label
|
||||||
|
}
|
||||||
pub fn emit(&mut self, compile_unit: CompileUnit) -> Vec<IRInstr> {
|
pub fn emit(&mut self, compile_unit: CompileUnit) -> Vec<IRInstr> {
|
||||||
self.generate_compile_unit(compile_unit)
|
self.generate_compile_unit(compile_unit)
|
||||||
}
|
}
|
||||||
@@ -51,12 +74,12 @@ impl Generator {
|
|||||||
let mut instrs = vec![];
|
let mut instrs = vec![];
|
||||||
let var_type = if is_global { VariableType::Global } else { VariableType::Local };
|
let var_type = if is_global { VariableType::Global } else { VariableType::Local };
|
||||||
for value in var_decl.values {
|
for value in var_decl.values {
|
||||||
match self.var_manager.declare_variable(&value.name, var_type, value.var_type.into()) {
|
match self.var_manager.declare_variable(&value.name, var_type, var_decl.data_type.into()) {
|
||||||
Ok(var) => {
|
Ok(var) => {
|
||||||
if is_global { instrs.push(IRInstr::Declare(var)); }
|
if is_global { instrs.push(IRInstr::Declare(var)); }
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
self.diagnostic.add_from_ir_error(e, value.span);
|
self.diagnostic.add_from_ir_error(e, value.name_span);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -66,7 +89,7 @@ impl Generator {
|
|||||||
|
|
||||||
fn generate_func_decl(&mut self, func_decl: FuncDeclStmt) -> Vec<IRInstr> {
|
fn generate_func_decl(&mut self, func_decl: FuncDeclStmt) -> Vec<IRInstr> {
|
||||||
if self.function_map.contains_key(&func_decl.name) {
|
if self.function_map.contains_key(&func_decl.name) {
|
||||||
self.diagnostic.add_from_ir_error(IRError::FunctionHasBeenDefined(func_decl.name.clone()), func_decl.span);
|
self.diagnostic.add_from_ir_error(IRError::FunctionHasBeenDefined(func_decl.name.clone()), func_decl.name_span);
|
||||||
return vec![];
|
return vec![];
|
||||||
}
|
}
|
||||||
self.current_func_return_type = Some(func_decl.return_type.into());
|
self.current_func_return_type = Some(func_decl.return_type.into());
|
||||||
@@ -75,7 +98,7 @@ impl Generator {
|
|||||||
match self.var_manager.declare_variable(¶m.name, VariableType::Local, param.param_type.into()) {
|
match self.var_manager.declare_variable(¶m.name, VariableType::Local, param.param_type.into()) {
|
||||||
Ok(var) => Ok(var),
|
Ok(var) => Ok(var),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
self.diagnostic.add_from_ir_error(e, param.span);
|
self.diagnostic.add_from_ir_error(e, param.name_span);
|
||||||
Err(())
|
Err(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -85,6 +108,14 @@ impl Generator {
|
|||||||
};
|
};
|
||||||
let temp_parameters = parameters.iter().map(|param| self.var_manager.declare_param_temp(param.data_type)).collect::<Vec<_>>();
|
let temp_parameters = parameters.iter().map(|param| self.var_manager.declare_param_temp(param.data_type)).collect::<Vec<_>>();
|
||||||
let mut body_instrs = vec![];
|
let mut body_instrs = vec![];
|
||||||
|
self.func_exit = Some((self.request_label(), {
|
||||||
|
let ret_type = func_decl.return_type.into();
|
||||||
|
if ret_type != IRType::Void {
|
||||||
|
Some(self.var_manager.declare_unamed_local(ret_type))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}));
|
||||||
let block_instrs = self.generate_block_stmt(func_decl.body);
|
let block_instrs = self.generate_block_stmt(func_decl.body);
|
||||||
for var in self.var_manager.get_cur_func_variables() {
|
for var in self.var_manager.get_cur_func_variables() {
|
||||||
if matches!(var.var_type, VariableType::ParamTemp) {
|
if matches!(var.var_type, VariableType::ParamTemp) {
|
||||||
@@ -97,7 +128,9 @@ impl Generator {
|
|||||||
body_instrs.push(IRInstr::Move(*temp_param, MoveRValue::Var(*param)));
|
body_instrs.push(IRInstr::Move(*temp_param, MoveRValue::Var(*param)));
|
||||||
});
|
});
|
||||||
body_instrs.extend(block_instrs);
|
body_instrs.extend(block_instrs);
|
||||||
|
let func_exit = self.func_exit.take().unwrap();
|
||||||
|
body_instrs.push(IRInstr::Label(func_exit.0));
|
||||||
|
body_instrs.push(IRInstr::Exit(func_exit.1));
|
||||||
self.var_manager.exit_scope();
|
self.var_manager.exit_scope();
|
||||||
self.current_func_return_type = None;
|
self.current_func_return_type = None;
|
||||||
self.var_manager.clear_local_counter();
|
self.var_manager.clear_local_counter();
|
||||||
@@ -121,13 +154,28 @@ impl Generator {
|
|||||||
use Statement::*;
|
use Statement::*;
|
||||||
let instrs = match stmt {
|
let instrs = match stmt {
|
||||||
Return(return_stmt) => self.generate_return_stmt(return_stmt),
|
Return(return_stmt) => self.generate_return_stmt(return_stmt),
|
||||||
|
If(if_stmt) => self.generate_if_stmt(if_stmt),
|
||||||
|
While(while_stmt) => self.generate_while_stmt(while_stmt),
|
||||||
|
Break(break_stmt) => self.generate_break_stmt(break_stmt),
|
||||||
|
Continue(continue_stmt) => self.generate_continue_stmt(continue_stmt),
|
||||||
Block(block_stmt) => {
|
Block(block_stmt) => {
|
||||||
self.var_manager.enter_scope();
|
self.var_manager.enter_scope();
|
||||||
let block_instrs = self.generate_block_stmt(block_stmt);
|
let block_instrs = self.generate_block_stmt(block_stmt);
|
||||||
self.var_manager.exit_scope();
|
self.var_manager.exit_scope();
|
||||||
block_instrs
|
block_instrs
|
||||||
},
|
},
|
||||||
Expr(expr) => self.generate_expr(expr).0,
|
Expr(expr) => {
|
||||||
|
self.current_exit_label.push(None);
|
||||||
|
let (instrs, _) = match self.generate_expr(expr) {
|
||||||
|
Some(res) => res,
|
||||||
|
None => {
|
||||||
|
self.current_exit_label.pop();
|
||||||
|
return vec![];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.current_exit_label.pop();
|
||||||
|
instrs
|
||||||
|
},
|
||||||
VarDecl(var_decl) => self.generate_var_decl(var_decl, false),
|
VarDecl(var_decl) => self.generate_var_decl(var_decl, false),
|
||||||
};
|
};
|
||||||
instrs
|
instrs
|
||||||
@@ -135,22 +183,160 @@ impl Generator {
|
|||||||
|
|
||||||
fn generate_return_stmt(&mut self, return_stmt: ReturnStmt) -> Vec<IRInstr> {
|
fn generate_return_stmt(&mut self, return_stmt: ReturnStmt) -> Vec<IRInstr> {
|
||||||
let mut instrs = vec![];
|
let mut instrs = vec![];
|
||||||
|
let func_exit = self.func_exit.unwrap();
|
||||||
match return_stmt.value {
|
match return_stmt.value {
|
||||||
Some(expr) => {
|
Some(expr) => {
|
||||||
let (value_instrs, value_var) = self.generate_expr(expr);
|
if func_exit.1.is_none() {
|
||||||
instrs.extend(value_instrs);
|
// shouldn't return value but return stmt has expr;
|
||||||
if value_var.is_some() {
|
self.diagnostic.add_from_ir_error(IRError::ReturnExpressionOnVoidFunction, return_stmt.span);
|
||||||
instrs.push(IRInstr::Exit(value_var));
|
return vec![];
|
||||||
}
|
}
|
||||||
|
self.current_exit_label.push(None);
|
||||||
|
let (value_instrs, value_var) = match self.generate_expr(expr) {
|
||||||
|
Some(res) => res,
|
||||||
|
None => {
|
||||||
|
self.current_exit_label.pop();
|
||||||
|
return vec![];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.current_exit_label.pop();
|
||||||
|
if value_var.is_none() {
|
||||||
|
self.diagnostic.add_from_ir_error(IRError::InvalidOperand(IRType::Void), return_stmt.span);
|
||||||
|
self.current_exit_label.pop();
|
||||||
|
return vec![];
|
||||||
|
}
|
||||||
|
instrs.extend(value_instrs);
|
||||||
|
instrs.push(IRInstr::Move(func_exit.1.unwrap(), MoveRValue::Var(value_var.unwrap())));
|
||||||
|
// if value_var.is_none() {
|
||||||
|
// self.diagnostic.add_from_ir_error(IRError::InvalidOperand(IRType::Void), return_stmt.span);
|
||||||
|
// return vec![];
|
||||||
|
// }
|
||||||
|
// let value_var = value_var.unwrap();
|
||||||
}
|
}
|
||||||
None => instrs.push(IRInstr::Exit(None)),
|
None => {
|
||||||
|
if func_exit.1.is_some() {
|
||||||
|
// should return value but return stmt has no expr;
|
||||||
|
self.diagnostic.add_from_ir_error(IRError::TypeMismatch(self.current_func_return_type.unwrap(), IRType::Void), return_stmt.span);
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
instrs.push(IRInstr::Goto(func_exit.0));
|
||||||
instrs
|
instrs
|
||||||
}
|
}
|
||||||
|
fn generate_while_stmt(&mut self, while_stmt: WhileStmt) -> Vec<IRInstr> {
|
||||||
|
let mut instrs = vec![];
|
||||||
|
let cond_label = self.request_label();
|
||||||
|
let body_label = self.request_label();
|
||||||
|
let exit_label = self.request_label();
|
||||||
|
instrs.push(IRInstr::Label(cond_label));
|
||||||
|
self.current_exit_label.push(Some((body_label, exit_label)));
|
||||||
|
let while_cond_span = while_stmt.condition.span;
|
||||||
|
let (cond_instrs, cond_var) = match self.generate_expr(while_stmt.condition) {
|
||||||
|
Some(res) => res,
|
||||||
|
None => {
|
||||||
|
self.current_exit_label.pop();
|
||||||
|
return vec![];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.current_exit_label.pop();
|
||||||
|
if cond_var.is_none() {
|
||||||
|
self.diagnostic.add_from_ir_error(IRError::InvalidOperand(IRType::Void), while_cond_span);
|
||||||
|
return vec![];
|
||||||
|
}
|
||||||
|
instrs.extend(cond_instrs);
|
||||||
|
instrs.push(IRInstr::Label(body_label));
|
||||||
|
self.while_exit_label.push((cond_label, exit_label));
|
||||||
|
instrs.extend(self.generate_block_stmt(while_stmt.body));
|
||||||
|
self.while_exit_label.pop();
|
||||||
|
instrs.push(IRInstr::Goto(cond_label));
|
||||||
|
instrs.push(IRInstr::Label(exit_label));
|
||||||
|
instrs
|
||||||
|
}
|
||||||
|
fn generate_if_stmt(&mut self, if_stmt: IfStmt) -> Vec<IRInstr> {
|
||||||
|
let mut instrs = vec![];
|
||||||
|
let then_label = self.request_label();
|
||||||
|
let exit_label = self.request_label();
|
||||||
|
let mut labels = vec![then_label];
|
||||||
|
for _ in &if_stmt.ifelse_branch {
|
||||||
|
let else_if_label = self.request_label();
|
||||||
|
let else_if_body_label = self.request_label();
|
||||||
|
|
||||||
fn generate_expr(&mut self, expr: Expr) -> (Vec<IRInstr>, Option<Variable>) {
|
labels.push(else_if_label);
|
||||||
use crate::ast::types::ExprValue;
|
labels.push(else_if_body_label);
|
||||||
match expr.value {
|
}
|
||||||
|
if if_stmt.else_branch.is_some() {
|
||||||
|
let else_label = self.request_label();
|
||||||
|
labels.push(else_label);
|
||||||
|
}
|
||||||
|
labels.push(exit_label);
|
||||||
|
// now generate if expr, true exit to labels[0], false exit to labels[1]
|
||||||
|
self.current_exit_label.push(Some((labels[0], labels[1])));
|
||||||
|
let cond_span = if_stmt.condition.span;
|
||||||
|
let (cond_instrs, cond_var) = match self.generate_expr(if_stmt.condition) {
|
||||||
|
Some(res) => res,
|
||||||
|
None => {
|
||||||
|
self.current_exit_label.pop();
|
||||||
|
return vec![];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.current_exit_label.pop();
|
||||||
|
if cond_var.is_none() {
|
||||||
|
self.diagnostic.add_from_ir_error(IRError::InvalidOperand(IRType::Void), cond_span);
|
||||||
|
return vec![];
|
||||||
|
}
|
||||||
|
instrs.extend(cond_instrs);
|
||||||
|
instrs.push(IRInstr::Label(labels[0]));
|
||||||
|
instrs.extend(self.generate_block_stmt(if_stmt.then_branch));
|
||||||
|
instrs.push(IRInstr::Goto(exit_label));
|
||||||
|
for (i, else_if_branch) in if_stmt.ifelse_branch.into_iter().enumerate() {
|
||||||
|
let IfElseBranch { condition: else_if_cond, then_branch: else_if_block } = else_if_branch;
|
||||||
|
instrs.push(IRInstr::Label(labels[i * 2 + 1]));
|
||||||
|
self.current_exit_label.push(Some((labels[i * 2 + 2], labels[i * 2 + 3])));
|
||||||
|
let else_if_cond_span = else_if_cond.span;
|
||||||
|
let (else_if_cond_instrs, else_if_cond_var) = match self.generate_expr(else_if_cond) {
|
||||||
|
Some(res) => res,
|
||||||
|
None => {
|
||||||
|
self.current_exit_label.pop();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.current_exit_label.pop();
|
||||||
|
if else_if_cond_var.is_none() {
|
||||||
|
self.diagnostic.add_from_ir_error(IRError::InvalidOperand(IRType::Void), else_if_cond_span);
|
||||||
|
return vec![];
|
||||||
|
}
|
||||||
|
instrs.extend(else_if_cond_instrs);
|
||||||
|
instrs.push(IRInstr::Label(labels[i * 2 + 2]));
|
||||||
|
instrs.extend(self.generate_block_stmt(else_if_block));
|
||||||
|
instrs.push(IRInstr::Goto(exit_label));
|
||||||
|
}
|
||||||
|
if let Some(else_block) = if_stmt.else_branch {
|
||||||
|
instrs.push(IRInstr::Label(labels[labels.len() - 2]));
|
||||||
|
instrs.extend(self.generate_block_stmt(else_block));
|
||||||
|
instrs.push(IRInstr::Goto(exit_label));
|
||||||
|
}
|
||||||
|
instrs.push(IRInstr::Label(exit_label));
|
||||||
|
instrs
|
||||||
|
}
|
||||||
|
fn generate_continue_stmt(&mut self, stmt: ContinueStmt) -> Vec<IRInstr> {
|
||||||
|
if let Some((continue_label, _)) = self.while_exit_label.last() {
|
||||||
|
vec![IRInstr::Goto(*continue_label)]
|
||||||
|
} else {
|
||||||
|
self.diagnostic.add_from_ir_error(IRError::ContinueOutsideLoop, stmt.span);
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn generate_break_stmt(&mut self, stmt: BreakStmt) -> Vec<IRInstr> {
|
||||||
|
if let Some((_, break_label)) = self.while_exit_label.last() {
|
||||||
|
vec![IRInstr::Goto(*break_label)]
|
||||||
|
} else {
|
||||||
|
self.diagnostic.add_from_ir_error(IRError::BreakOutsideLoop, stmt.span);
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn generate_expr(&mut self, expr: Expr) -> Option<(Vec<IRInstr>, Option<Variable>)> {
|
||||||
|
// there may be some expr that doesn't produce value, like void func call
|
||||||
|
let (mut instrs, var) = match expr.value {
|
||||||
ExprValue::IntLit(i) => {
|
ExprValue::IntLit(i) => {
|
||||||
// TODO: convert check
|
// TODO: convert check
|
||||||
let var = self.var_manager.declare_temp(IRType::I32);
|
let var = self.var_manager.declare_temp(IRType::I32);
|
||||||
@@ -161,7 +347,7 @@ impl Generator {
|
|||||||
(vec![], Some(var))
|
(vec![], Some(var))
|
||||||
} else {
|
} else {
|
||||||
self.diagnostic.add_from_ir_error(IRError::VariableNotFound(name.clone()), expr.span);
|
self.diagnostic.add_from_ir_error(IRError::VariableNotFound(name.clone()), expr.span);
|
||||||
(vec![], None)
|
return None;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ExprValue::Assign { lvalue, rvalue } => {
|
ExprValue::Assign { lvalue, rvalue } => {
|
||||||
@@ -171,69 +357,251 @@ impl Generator {
|
|||||||
Some(var) => var,
|
Some(var) => var,
|
||||||
None => {
|
None => {
|
||||||
self.diagnostic.add_from_ir_error(IRError::VariableNotFound(name.clone()), lvalue.span);
|
self.diagnostic.add_from_ir_error(IRError::VariableNotFound(name.clone()), lvalue.span);
|
||||||
return (vec![], None);
|
return None;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let (mut instrs, rvalue_var) = self.generate_expr(*rvalue);
|
self.current_exit_label.push(None);
|
||||||
|
let rvalue_span = rvalue.span;
|
||||||
|
let (mut instrs, rvalue_var) = self.generate_expr(*rvalue)?;
|
||||||
|
self.current_exit_label.pop();
|
||||||
|
// TODO: further check
|
||||||
|
// if var.data_type != rvalue_var.data_type {
|
||||||
|
// self.diagnostic.add_from_ir_error(IRError::TypeMismatch(var.data_type, rvalue_var.data_type), lvalue.span);
|
||||||
|
// return (vec![], None);
|
||||||
|
// }
|
||||||
if rvalue_var.is_none() {
|
if rvalue_var.is_none() {
|
||||||
return (vec![], None);
|
self.diagnostic.add_from_ir_error(IRError::InvalidOperand(IRType::Void), rvalue_span);
|
||||||
|
return None;
|
||||||
}
|
}
|
||||||
let rvalue_var = rvalue_var.unwrap();
|
instrs.push(IRInstr::Move(var, MoveRValue::Var(rvalue_var.unwrap())));
|
||||||
if var.data_type != rvalue_var.data_type {
|
|
||||||
self.diagnostic.add_from_ir_error(IRError::TypeMismatch(var.data_type, rvalue_var.data_type), lvalue.span);
|
|
||||||
return (vec![], None);
|
|
||||||
}
|
|
||||||
instrs.push(IRInstr::Move(var, MoveRValue::Var(rvalue_var)));
|
|
||||||
let temp_var = self.var_manager.declare_temp(var.data_type);
|
let temp_var = self.var_manager.declare_temp(var.data_type);
|
||||||
instrs.push(IRInstr::Move(temp_var, MoveRValue::Var(var)));
|
instrs.push(IRInstr::Move(temp_var, MoveRValue::Var(var)));
|
||||||
(instrs, Some(temp_var))
|
(instrs, Some(temp_var))
|
||||||
} else {
|
} else {
|
||||||
self.diagnostic.add_from_ir_error(IRError::InvalidAssignmentTarget, lvalue.span);
|
self.diagnostic.add_from_ir_error(IRError::InvalidAssignmentTarget, lvalue.span);
|
||||||
return (vec![], None);
|
return None;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
ExprValue::UnaryOp { op, operand } => {
|
||||||
|
let mut parent_is_logical = true;
|
||||||
|
let exit_passdown = if matches!(op, AstUnaryOp::Not) {
|
||||||
|
Some(self.current_exit_label.last().cloned().flatten().map_or_else(|| {
|
||||||
|
parent_is_logical = false;
|
||||||
|
(self.request_label(), self.request_label())
|
||||||
|
}, |(true_exit, false_exit)| (false_exit, true_exit)))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
// NOT:
|
||||||
|
// $x = cmp eq left_var, 0
|
||||||
|
// bc, $x, true_exit, false_exit
|
||||||
|
// false_exit: #only when parent isn't logical
|
||||||
|
// true_exit:
|
||||||
|
// $dest = $x
|
||||||
|
|
||||||
|
// +--------+-------------------------+-------------------------+----------------------------+
|
||||||
|
// | parent | (true_exit, false_exit) | (true_exit, false_exit) | None |
|
||||||
|
// | self | not | add/sub | not(true_exit, false_exit) |
|
||||||
|
// | child | (false_exit, true_exit) | None | (true_exit, false_exit) |
|
||||||
|
// +--------+-------------------------+-------------------------+----------------------------+
|
||||||
|
|
||||||
|
|
||||||
|
self.current_exit_label.push(exit_passdown);
|
||||||
|
let operand_span = operand.span;
|
||||||
|
let (mut instrs, operand_var) = self.generate_expr(*operand)?;
|
||||||
|
self.current_exit_label.pop();
|
||||||
|
if operand_var.is_none() {
|
||||||
|
self.diagnostic.add_from_ir_error(IRError::InvalidOperand(IRType::Void), operand_span);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let operand_var = operand_var.unwrap();
|
||||||
|
let dest_var = match op {
|
||||||
|
AstUnaryOp::Add => {
|
||||||
|
let dest_var = self.var_manager.declare_temp(operand_var.data_type);
|
||||||
|
instrs.push(IRInstr::Move(dest_var, MoveRValue::Var(operand_var)));
|
||||||
|
dest_var
|
||||||
|
},
|
||||||
|
AstUnaryOp::Sub => {
|
||||||
|
let dest_var = self.var_manager.declare_temp(operand_var.data_type);
|
||||||
|
instrs.push(IRInstr::Unary(dest_var, UnaryOp::Neg, operand_var));
|
||||||
|
dest_var
|
||||||
|
},
|
||||||
|
AstUnaryOp::Not => {
|
||||||
|
let dest_var = self.var_manager.declare_unamed_local(operand_var.data_type);
|
||||||
|
// child will do the cmp
|
||||||
|
if !parent_is_logical {
|
||||||
|
let exit = exit_passdown.unwrap(); // (false_exit, true_exit) (consider `not`)
|
||||||
|
let final_exit = self.request_label();
|
||||||
|
instrs.push(IRInstr::Label(exit.1));
|
||||||
|
instrs.push(IRInstr::Move(dest_var, MoveRValue::ConstInt(1)));
|
||||||
|
instrs.push(IRInstr::Goto(final_exit));
|
||||||
|
instrs.push(IRInstr::Label(exit.0));
|
||||||
|
instrs.push(IRInstr::Move(dest_var, MoveRValue::ConstInt(0)));
|
||||||
|
instrs.push(IRInstr::Goto(final_exit));
|
||||||
|
instrs.push(IRInstr::Label(final_exit));
|
||||||
|
|
||||||
|
}
|
||||||
|
// return directly since it's logical expr
|
||||||
|
return Some((instrs, Some(dest_var)));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
(instrs, Some(dest_var))
|
||||||
|
},
|
||||||
ExprValue::BinaryOp { lhs, op, rhs } => {
|
ExprValue::BinaryOp { lhs, op, rhs } => {
|
||||||
let lhs_span = lhs.span;
|
let lhs_span = lhs.span;
|
||||||
let rhs_span = rhs.span;
|
let rhs_span = rhs.span;
|
||||||
let (mut instrs, left_var) = self.generate_expr(*lhs);
|
|
||||||
|
// +--------+-------------------------+-------------------------+-------------------------+
|
||||||
|
// | parent | (true_exit, false_exit) | (true_exit, false_exit) | (true_exit, false_exit) |
|
||||||
|
// | self | and(next) | or(next) | others |
|
||||||
|
// | lhs | (next, false_exit) | (true_exit, next) | None |
|
||||||
|
// | rhs | (true_exit, false_exit) | (next, false_exit) | None |
|
||||||
|
// +--------+-------------------------+-------------------------+-------------------------+
|
||||||
|
|
||||||
|
|
||||||
|
// +--------+----------------------------------+-------------------------------+--------+
|
||||||
|
// | parent | None | None | None |
|
||||||
|
// | self | and(true_exit, next, false_exit) | or(true_exit,next,false_exit) | others |
|
||||||
|
// | lhs | (next, false_exit) | (true_exit, next) | None |
|
||||||
|
// | rhs | (true_exit, false_exit) | (next, false_exit) | None |
|
||||||
|
// +--------+----------------------------------+-------------------------------+--------+
|
||||||
|
|
||||||
|
|
||||||
|
let mut parent_exit = None;
|
||||||
|
let exit = if op.is_logical() {
|
||||||
|
Some(self.current_exit_label.last().cloned().flatten().map_or(
|
||||||
|
(self.request_label(), self.request_label(), self.request_label()),
|
||||||
|
|(true_exit, false_exit)| {
|
||||||
|
parent_exit = Some((true_exit, false_exit));
|
||||||
|
(true_exit, self.request_label(), false_exit)
|
||||||
|
}
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}; // (true_exit, next_label, false_exit)
|
||||||
|
let lhs_exit_passdown = exit.map(|(true_exit, next_label, false_exit)| match op {
|
||||||
|
AstBinaryOp::And => (next_label, false_exit),
|
||||||
|
AstBinaryOp::Or => (true_exit, next_label),
|
||||||
|
_ => unreachable!(),
|
||||||
|
});
|
||||||
|
let rhs_exit_passdown = exit.map(|(true_exit, _, false_exit)| match op {
|
||||||
|
AstBinaryOp::And => (true_exit, false_exit),
|
||||||
|
AstBinaryOp::Or => (true_exit, false_exit),
|
||||||
|
_ => unreachable!(),
|
||||||
|
});
|
||||||
|
self.current_exit_label.push(lhs_exit_passdown);
|
||||||
|
let (mut instrs, left_var) = self.generate_expr(*lhs)?;
|
||||||
|
self.current_exit_label.pop();
|
||||||
if left_var.is_none() {
|
if left_var.is_none() {
|
||||||
return (vec![], None);
|
|
||||||
}
|
|
||||||
let left_var = left_var.unwrap();
|
|
||||||
let (right_instrs, right_var) = self.generate_expr(*rhs);
|
|
||||||
if right_var.is_none() {
|
|
||||||
return (vec![], None);
|
|
||||||
}
|
|
||||||
let right_var = right_var.unwrap();
|
|
||||||
instrs.extend(right_instrs);
|
|
||||||
let mut has_void = false;
|
|
||||||
if matches!(left_var.data_type, IRType::Void) {
|
|
||||||
self.diagnostic.add_from_ir_error(IRError::InvalidOperand(IRType::Void), lhs_span);
|
self.diagnostic.add_from_ir_error(IRError::InvalidOperand(IRType::Void), lhs_span);
|
||||||
has_void = true;
|
return None;
|
||||||
}
|
}
|
||||||
if matches!(right_var.data_type, IRType::Void) {
|
let mut left_var = left_var.unwrap();
|
||||||
|
self.current_exit_label.push(rhs_exit_passdown);
|
||||||
|
let (right_instrs, right_var) = self.generate_expr(*rhs)?;
|
||||||
|
self.current_exit_label.pop();
|
||||||
|
if right_var.is_none() {
|
||||||
self.diagnostic.add_from_ir_error(IRError::InvalidOperand(IRType::Void), rhs_span);
|
self.diagnostic.add_from_ir_error(IRError::InvalidOperand(IRType::Void), rhs_span);
|
||||||
has_void = true;
|
return None;
|
||||||
}
|
}
|
||||||
if has_void {
|
let mut right_var = right_var.unwrap();
|
||||||
return (vec![], None);
|
// check implicit convert
|
||||||
|
let convert_to;
|
||||||
|
if op.is_logical() {
|
||||||
|
// we dont really care since we use cmp
|
||||||
|
convert_to = left_var.data_type;
|
||||||
|
} else {
|
||||||
|
if let Some(ty) = IRType::get_elevate_result(left_var.data_type, right_var.data_type) {
|
||||||
|
convert_to = ty;
|
||||||
|
} else {
|
||||||
|
self.diagnostic.add_from_ir_error(IRError::IncompatiableOperand(left_var.data_type, right_var.data_type), lhs_span);
|
||||||
|
self.diagnostic.add_from_ir_error(IRError::IncompatiableOperand(left_var.data_type, right_var.data_type), rhs_span);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if left_var.data_type != right_var.data_type {
|
// do implicit convert if needed
|
||||||
self.diagnostic.add_from_ir_error(IRError::IncompatiableOperand(left_var.data_type, right_var.data_type), lhs_span);
|
// TODO: further check
|
||||||
self.diagnostic.add_from_ir_error(IRError::InvalidOperand(left_var.data_type), rhs_span);
|
if !op.is_logical() && convert_to != left_var.data_type {
|
||||||
return (vec![], None);
|
let temp_var = self.var_manager.declare_temp(convert_to);
|
||||||
|
instrs.push(IRInstr::Move(temp_var, MoveRValue::Var(left_var)));
|
||||||
|
left_var = temp_var;
|
||||||
}
|
}
|
||||||
let result_type;
|
let result_type;
|
||||||
match Into::<BinaryOp>::into(op) {
|
match op {
|
||||||
BinaryOp::Add | BinaryOp::Sub | BinaryOp::Mul | BinaryOp::Div | BinaryOp::Mod => {
|
AstBinaryOp::Add | AstBinaryOp::Sub | AstBinaryOp::Mul | AstBinaryOp::Div | AstBinaryOp::Mod => {
|
||||||
result_type = left_var.data_type;
|
result_type = left_var.data_type;
|
||||||
},
|
},
|
||||||
BinaryOp::Eq | BinaryOp::Ne | BinaryOp::Lt | BinaryOp::Gt | BinaryOp::Le | BinaryOp::Ge => {
|
AstBinaryOp::Equal | AstBinaryOp::NotEqual | AstBinaryOp::Less | AstBinaryOp::Greater | AstBinaryOp::LessEqual | AstBinaryOp::GreaterEqual
|
||||||
|
| AstBinaryOp::And | AstBinaryOp::Or => {
|
||||||
result_type = IRType::I1;
|
result_type = IRType::I1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let dest_var = self.var_manager.declare_temp(result_type);
|
let dest_var = self.var_manager.declare_temp(result_type);
|
||||||
instrs.push(IRInstr::Binary(dest_var, left_var, op.into(), right_var));
|
match op {
|
||||||
|
AstBinaryOp::And | AstBinaryOp::Or => {
|
||||||
|
instrs.push(IRInstr::Label(exit.unwrap().1));
|
||||||
|
},
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
// for logical operator, considering short-circuit
|
||||||
|
// AND:
|
||||||
|
// $x = cmp ne left_var, 0
|
||||||
|
// bc, $x, next, false_exit
|
||||||
|
// next:
|
||||||
|
// $x = cmp eq right_var, 0
|
||||||
|
// bc, $x, true_exit, false_exit
|
||||||
|
// false_exit: #only when parent isn't logical
|
||||||
|
// true_exit:
|
||||||
|
// OR:
|
||||||
|
// $x = cmp ne left_var, 0
|
||||||
|
// bc, $x, true_exit, next
|
||||||
|
// next:
|
||||||
|
// $x = cmp ne right_var, 0
|
||||||
|
// bc, $x, true_exit, false_exit
|
||||||
|
// false_exit: #only when parent isn't logical
|
||||||
|
// true_exit:
|
||||||
|
instrs.extend(right_instrs);
|
||||||
|
if !op.is_logical() && convert_to != right_var.data_type {
|
||||||
|
let temp_var = self.var_manager.declare_temp(convert_to);
|
||||||
|
instrs.push(IRInstr::Move(temp_var, MoveRValue::Var(right_var)));
|
||||||
|
right_var = temp_var;
|
||||||
|
}
|
||||||
|
if !op.is_logical() {
|
||||||
|
if let Some((true_exit, false_exit)) = parent_exit {
|
||||||
|
let final_exit = self.request_label();
|
||||||
|
instrs.push(IRInstr::Label(true_exit));
|
||||||
|
instrs.push(IRInstr::Move(dest_var, MoveRValue::ConstInt(1)));
|
||||||
|
instrs.push(IRInstr::Goto(final_exit));
|
||||||
|
instrs.push(IRInstr::Label(false_exit));
|
||||||
|
instrs.push(IRInstr::Move(dest_var, MoveRValue::ConstInt(0)));
|
||||||
|
instrs.push(IRInstr::Label(final_exit));
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if op.is_logical() {
|
||||||
|
return Some((instrs, Some(dest_var)));
|
||||||
|
} else {
|
||||||
|
if op.is_cmp() {
|
||||||
|
instrs.push(IRInstr::Cmp(dest_var, VariableOrIntLit::Var(left_var), op.into(), VariableOrIntLit::Var(right_var)));
|
||||||
|
} else {
|
||||||
|
instrs.push(IRInstr::Binary(dest_var, left_var, op.into(), right_var));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if !op.is_logical() {
|
||||||
|
// if let Some((true_exit, false_exit)) = self.current_exit_label.last().cloned().flatten() {
|
||||||
|
// // parent is logical
|
||||||
|
// instrs.push(IRInstr::Cmp(dest_var, VariableOrIntLit::Var(dest_var), CmpOp::Ne, VariableOrIntLit::IntLit(0)));
|
||||||
|
// instrs.push(IRInstr::CondGoto(dest_var, true_exit, false_exit));
|
||||||
|
// } else {
|
||||||
|
// // parent isn't logical
|
||||||
|
// if op.is_cmp() {
|
||||||
|
// instrs.push(IRInstr::Cmp(dest_var, VariableOrIntLit::Var(left_var), op.into(), VariableOrIntLit::Var(right_var)));
|
||||||
|
// } else {
|
||||||
|
// instrs.push(IRInstr::Binary(dest_var, left_var, op.into(), right_var));
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
(instrs, Some(dest_var))
|
(instrs, Some(dest_var))
|
||||||
},
|
},
|
||||||
ExprValue::FuncCall(func_name, args) => {
|
ExprValue::FuncCall(func_name, args) => {
|
||||||
@@ -243,35 +611,40 @@ impl Generator {
|
|||||||
func_def
|
func_def
|
||||||
} else {
|
} else {
|
||||||
self.diagnostic.add_from_ir_error(IRError::FunctionNotFound(func_name.clone()), expr.span);
|
self.diagnostic.add_from_ir_error(IRError::FunctionNotFound(func_name.clone()), expr.span);
|
||||||
return (vec![], None);
|
return None;
|
||||||
}.clone();
|
}.clone();
|
||||||
|
|
||||||
if args.len() < func_def.parameter_types.len() {
|
if args.len() < func_def.parameter_types.len() {
|
||||||
self.diagnostic.add_from_ir_error(IRError::TooFewArguments(func_def.parameter_types.len(), args.len()), expr.span);
|
self.diagnostic.add_from_ir_error(IRError::TooFewArguments(func_def.parameter_types.len(), args.len()), expr.span);
|
||||||
return (vec![], None);
|
return None;
|
||||||
}
|
}
|
||||||
if args.len() > func_def.parameter_types.len() {
|
if args.len() > func_def.parameter_types.len() {
|
||||||
self.diagnostic.add_from_ir_error(IRError::TooManyArguments(func_def.parameter_types.len(), args.len()), expr.span);
|
self.diagnostic.add_from_ir_error(IRError::TooManyArguments(func_def.parameter_types.len(), args.len()), expr.span);
|
||||||
return (vec![], None);
|
return None;
|
||||||
}
|
}
|
||||||
let mut has_error = false;
|
let mut has_error = false;
|
||||||
for (i, arg) in args.into_iter().enumerate() {
|
for parameter_type in &func_def.parameter_types {
|
||||||
let (arg_instrs, arg_var) = self.generate_expr(arg);
|
if matches!(parameter_type, IRType::Void) {
|
||||||
if arg_var.is_none() {
|
self.diagnostic.add_from_ir_error(IRError::InvalidParameterType(IRType::Void), expr.span);
|
||||||
has_error = true;
|
has_error = true;
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
let arg_var = arg_var.unwrap();
|
}
|
||||||
|
if has_error {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
for (i, arg) in args.into_iter().enumerate() {
|
||||||
|
let (arg_instrs, arg_var) = self.generate_expr(arg)?;
|
||||||
let parameter_type = func_def.parameter_types.get(i).unwrap();
|
let parameter_type = func_def.parameter_types.get(i).unwrap();
|
||||||
if *parameter_type != arg_var.data_type {
|
if *parameter_type != arg_var.map_or(IRType::Void, |v| v.data_type) {
|
||||||
self.diagnostic.add_from_ir_error(IRError::TypeMismatch(*parameter_type, arg_var.data_type), expr.span);
|
self.diagnostic.add_from_ir_error(IRError::TypeMismatch(*parameter_type, arg_var.map_or(IRType::Void, |v| v.data_type)), expr.span);
|
||||||
has_error = true;
|
has_error = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
instrs.extend(arg_instrs);
|
instrs.extend(arg_instrs);
|
||||||
arg_vars.push(arg_var);
|
arg_vars.push(arg_var.unwrap());
|
||||||
}
|
}
|
||||||
if has_error {
|
if has_error {
|
||||||
return (vec![], None);
|
return None;
|
||||||
}
|
}
|
||||||
let ret_variable = if matches!(func_def.return_type, IRType::Void) {
|
let ret_variable = if matches!(func_def.return_type, IRType::Void) {
|
||||||
None
|
None
|
||||||
@@ -281,6 +654,14 @@ impl Generator {
|
|||||||
instrs.push(IRInstr::FuncCall(func_def.clone(), arg_vars, ret_variable));
|
instrs.push(IRInstr::FuncCall(func_def.clone(), arg_vars, ret_variable));
|
||||||
(instrs, ret_variable)
|
(instrs, ret_variable)
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
if let Some((true_exit, false_exit)) = self.current_exit_label.last().cloned().flatten() {
|
||||||
|
let cmp_var = self.var_manager.declare_temp(IRType::I1);
|
||||||
|
instrs.push(IRInstr::Cmp(cmp_var, VariableOrIntLit::Var(var.unwrap()), CmpOp::Ne, VariableOrIntLit::IntLit(0)));
|
||||||
|
instrs.push(IRInstr::CondGoto(cmp_var, true_exit, false_exit));
|
||||||
|
Some((instrs, var))
|
||||||
|
} else {
|
||||||
|
Some((instrs, var))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -350,6 +731,12 @@ impl VariableManager {
|
|||||||
self.local_var_type.push(var);
|
self.local_var_type.push(var);
|
||||||
var
|
var
|
||||||
}
|
}
|
||||||
|
pub fn declare_unamed_local(&mut self, var_data_type: IRType) -> Variable {
|
||||||
|
let var = Variable { index: self.local_counter, var_type: VariableType::Local, data_type: var_data_type };
|
||||||
|
self.local_counter += 1;
|
||||||
|
self.local_var_type.push(var);
|
||||||
|
var
|
||||||
|
}
|
||||||
pub fn declare_param_temp(&mut self, var_data_type: IRType) -> Variable {
|
pub fn declare_param_temp(&mut self, var_data_type: IRType) -> Variable {
|
||||||
let var = Variable { index: self.local_counter, var_type: VariableType::ParamTemp, data_type: var_data_type };
|
let var = Variable { index: self.local_counter, var_type: VariableType::ParamTemp, data_type: var_data_type };
|
||||||
self.local_counter += 1;
|
self.local_counter += 1;
|
||||||
@@ -439,4 +826,8 @@ mod tests {
|
|||||||
test_case("0-3,14-25");
|
test_case("0-3,14-25");
|
||||||
// test_case("0-3,14-25");
|
// test_case("0-3,14-25");
|
||||||
}
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_if_while() {
|
||||||
|
test_case("26-32,34-41,46-51,57");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+89
-15
@@ -3,15 +3,32 @@ use std::fmt::Display;
|
|||||||
use crate::ast::types::Type as AstType;
|
use crate::ast::types::Type as AstType;
|
||||||
use crate::ast::types::BinaryOp as AstBinaryOp;
|
use crate::ast::types::BinaryOp as AstBinaryOp;
|
||||||
use crate::ir::err::IRError;
|
use crate::ir::err::IRError;
|
||||||
|
|
||||||
|
pub enum VariableOrIntLit {
|
||||||
|
Var(Variable),
|
||||||
|
IntLit(i32),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for VariableOrIntLit {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
VariableOrIntLit::Var(v) => write!(f, "{}", v),
|
||||||
|
VariableOrIntLit::IntLit(i) => write!(f, "{}", i),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
pub enum IRInstr {
|
pub enum IRInstr {
|
||||||
Declare(Variable),
|
Declare(Variable),
|
||||||
DefineFunc(Function, Vec<Variable>, Vec<IRInstr>),
|
DefineFunc(Function, Vec<Variable>, Vec<IRInstr>),
|
||||||
Entry,
|
Entry,
|
||||||
Binary(Variable, Variable, BinaryOp, Variable),
|
Binary(Variable, Variable, BinaryOp, Variable),
|
||||||
|
Cmp(Variable, VariableOrIntLit, CmpOp, VariableOrIntLit),
|
||||||
|
Unary(Variable, UnaryOp, Variable),
|
||||||
Exit(Option<Variable>),
|
Exit(Option<Variable>),
|
||||||
FuncCall(Function, Vec<Variable>, Option<Variable>),
|
FuncCall(Function, Vec<Variable>, Option<Variable>),
|
||||||
// Goto,
|
Goto(usize),
|
||||||
// Label,
|
CondGoto(Variable, usize, usize), // condition, true label, false label
|
||||||
|
Label(usize),
|
||||||
Move(Variable, MoveRValue),
|
Move(Variable, MoveRValue),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -20,6 +37,7 @@ impl Display for IRInstr {
|
|||||||
match self {
|
match self {
|
||||||
IRInstr::Entry => write!(f, "entry"),
|
IRInstr::Entry => write!(f, "entry"),
|
||||||
IRInstr::Binary(dest, left, op, right) => write!(f, "{} = {} {},{}", dest, op, left, right),
|
IRInstr::Binary(dest, left, op, right) => write!(f, "{} = {} {},{}", dest, op, left, right),
|
||||||
|
IRInstr::Cmp(dest, left, op, right) => write!(f, "{} = icmp {} {},{}", dest, op, left, right),
|
||||||
IRInstr::Exit(v) => if let Some(v) = v { write!(f, "exit {}", v) } else { write!(f, "exit") },
|
IRInstr::Exit(v) => if let Some(v) = v { write!(f, "exit {}", v) } else { write!(f, "exit") },
|
||||||
IRInstr::FuncCall(func, args, dest) => {
|
IRInstr::FuncCall(func, args, dest) => {
|
||||||
if let Some(dest) = dest {
|
if let Some(dest) = dest {
|
||||||
@@ -33,7 +51,11 @@ impl Display for IRInstr {
|
|||||||
IRInstr::DefineFunc(func, args, body) => {
|
IRInstr::DefineFunc(func, args, body) => {
|
||||||
let body_str = body.iter().map(|instr| format!(" {}", instr)).collect::<Vec<_>>().join("\n");
|
let body_str = body.iter().map(|instr| format!(" {}", instr)).collect::<Vec<_>>().join("\n");
|
||||||
write!(f, "define {} {{\n{}\n}}", func.to_decl_string(args), body_str)
|
write!(f, "define {} {{\n{}\n}}", func.to_decl_string(args), body_str)
|
||||||
}
|
},
|
||||||
|
IRInstr::Goto(label) => write!(f, "br label .L{}", label),
|
||||||
|
IRInstr::CondGoto(cond, true_label, false_label) => write!(f, "bc {}, .L{}, .L{}", cond, true_label, false_label),
|
||||||
|
IRInstr::Label(label) => write!(f, ".L{}:", label),
|
||||||
|
IRInstr::Unary(dest, op, src) => write!(f, "{} = {} {}", dest, op, src),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -51,6 +73,16 @@ impl IRType {
|
|||||||
IRType::Void => 0,
|
IRType::Void => 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_elevate_result(lhs: IRType, rhs: IRType) -> Option<IRType> {
|
||||||
|
if lhs == rhs {
|
||||||
|
Some(lhs)
|
||||||
|
} else if (lhs == IRType::I32 && rhs == IRType::I1) || (lhs == IRType::I1 && rhs == IRType::I32) {
|
||||||
|
Some(IRType::I32)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
impl Display for IRType {
|
impl Display for IRType {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
@@ -153,6 +185,8 @@ pub enum BinaryOp {
|
|||||||
Mul,
|
Mul,
|
||||||
Div,
|
Div,
|
||||||
Mod,
|
Mod,
|
||||||
|
}
|
||||||
|
pub enum CmpOp {
|
||||||
Le,
|
Le,
|
||||||
Lt,
|
Lt,
|
||||||
Gt,
|
Gt,
|
||||||
@@ -160,6 +194,9 @@ pub enum BinaryOp {
|
|||||||
Ne,
|
Ne,
|
||||||
Eq,
|
Eq,
|
||||||
}
|
}
|
||||||
|
pub enum UnaryOp {
|
||||||
|
Neg
|
||||||
|
}
|
||||||
|
|
||||||
impl Display for BinaryOp {
|
impl Display for BinaryOp {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
@@ -169,12 +206,35 @@ impl Display for BinaryOp {
|
|||||||
BinaryOp::Mul => "mul",
|
BinaryOp::Mul => "mul",
|
||||||
BinaryOp::Div => "div",
|
BinaryOp::Div => "div",
|
||||||
BinaryOp::Mod => "mod",
|
BinaryOp::Mod => "mod",
|
||||||
BinaryOp::Le => "le",
|
// BinaryOp::Le => "cmp le",
|
||||||
BinaryOp::Lt => "lt",
|
// BinaryOp::Lt => "cmp lt",
|
||||||
BinaryOp::Gt => "gt",
|
// BinaryOp::Gt => "cmp gt",
|
||||||
BinaryOp::Ge => "ge",
|
// BinaryOp::Ge => "cmp ge",
|
||||||
BinaryOp::Ne => "ne",
|
// BinaryOp::Ne => "cmp ne",
|
||||||
BinaryOp::Eq => "eq",
|
// BinaryOp::Eq => "cmp eq",
|
||||||
|
};
|
||||||
|
write!(f, "{}", op_str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for CmpOp {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let op_str = match self {
|
||||||
|
CmpOp::Le => "le",
|
||||||
|
CmpOp::Lt => "lt",
|
||||||
|
CmpOp::Gt => "gt",
|
||||||
|
CmpOp::Ge => "ge",
|
||||||
|
CmpOp::Ne => "ne",
|
||||||
|
CmpOp::Eq => "eq",
|
||||||
|
};
|
||||||
|
write!(f, "{}", op_str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for UnaryOp {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let op_str = match self {
|
||||||
|
UnaryOp::Neg => "neg",
|
||||||
};
|
};
|
||||||
write!(f, "{}", op_str)
|
write!(f, "{}", op_str)
|
||||||
}
|
}
|
||||||
@@ -187,12 +247,26 @@ impl From<AstBinaryOp> for BinaryOp {
|
|||||||
AstBinaryOp::Mul => BinaryOp::Mul,
|
AstBinaryOp::Mul => BinaryOp::Mul,
|
||||||
AstBinaryOp::Div => BinaryOp::Div,
|
AstBinaryOp::Div => BinaryOp::Div,
|
||||||
AstBinaryOp::Mod => BinaryOp::Mod,
|
AstBinaryOp::Mod => BinaryOp::Mod,
|
||||||
AstBinaryOp::Equal => BinaryOp::Eq,
|
// AstBinaryOp::Equal => BinaryOp::Eq,
|
||||||
AstBinaryOp::NotEqual => BinaryOp::Ne,
|
// AstBinaryOp::NotEqual => BinaryOp::Ne,
|
||||||
AstBinaryOp::Less => BinaryOp::Lt,
|
// AstBinaryOp::Less => BinaryOp::Lt,
|
||||||
AstBinaryOp::LessEqual => BinaryOp::Le,
|
// AstBinaryOp::LessEqual => BinaryOp::Le,
|
||||||
AstBinaryOp::Greater => BinaryOp::Gt,
|
// AstBinaryOp::Greater => BinaryOp::Gt,
|
||||||
AstBinaryOp::GreaterEqual => BinaryOp::Ge,
|
// AstBinaryOp::GreaterEqual => BinaryOp::Ge,
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<AstBinaryOp> for CmpOp {
|
||||||
|
fn from(ast_op: AstBinaryOp) -> Self {
|
||||||
|
match ast_op {
|
||||||
|
AstBinaryOp::Equal => CmpOp::Eq,
|
||||||
|
AstBinaryOp::NotEqual => CmpOp::Ne,
|
||||||
|
AstBinaryOp::Less => CmpOp::Lt,
|
||||||
|
AstBinaryOp::LessEqual => CmpOp::Le,
|
||||||
|
AstBinaryOp::Greater => CmpOp::Gt,
|
||||||
|
AstBinaryOp::GreaterEqual => CmpOp::Ge,
|
||||||
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user