Compare commits
3 Commits
1eca4a225b
...
52f9b67018
| Author | SHA1 | Date | |
|---|---|---|---|
| 52f9b67018 | |||
| 669c415bd7 | |||
| c42575c1c6 |
+25
-3
@@ -2,7 +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, BreakStmt, CompileUnit, ContinueStmt, Expr, ExprValue, FuncDeclStmt, GlobalDeclStmt, IfStmt, Param, ReturnStmt, Statement, VarDeclStmt, VarDeclStmtValue, WhileStmt
|
ArrayDimension, BlockStmt, BreakStmt, CompileUnit, ContinueStmt, Expr, ExprValue, FuncDeclStmt, GlobalDeclStmt, IfStmt, Param, ReturnStmt, Statement, VarDeclStmt, VarDeclStmtValue, WhileStmt
|
||||||
};
|
};
|
||||||
|
|
||||||
pub type AstGraph = Graph<String, String>;
|
pub type AstGraph = Graph<String, String>;
|
||||||
@@ -73,7 +73,19 @@ impl AstGraphBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn add_var_decl_value(&mut self, parent: NodeIndex, value: &VarDeclStmtValue) -> NodeIndex {
|
fn add_var_decl_value(&mut self, parent: NodeIndex, value: &VarDeclStmtValue) -> NodeIndex {
|
||||||
self.child(parent, value.to_string())
|
let node = self.child(parent, value.to_string());
|
||||||
|
for dimension in &value.dimensions {
|
||||||
|
self.add_array_dimension(node, dimension);
|
||||||
|
}
|
||||||
|
node
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_array_dimension(&mut self, parent: NodeIndex, dimension: &ArrayDimension) -> NodeIndex {
|
||||||
|
let node = self.child(parent, dimension.to_string());
|
||||||
|
if let Some(value) = &dimension.value {
|
||||||
|
self.add_expr(node, value);
|
||||||
|
}
|
||||||
|
node
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_func_decl(&mut self, parent: NodeIndex, func_decl: &FuncDeclStmt) -> NodeIndex {
|
fn add_func_decl(&mut self, parent: NodeIndex, func_decl: &FuncDeclStmt) -> NodeIndex {
|
||||||
@@ -87,7 +99,11 @@ impl AstGraphBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn add_param(&mut self, parent: NodeIndex, param: &Param) -> NodeIndex {
|
fn add_param(&mut self, parent: NodeIndex, param: &Param) -> NodeIndex {
|
||||||
self.child(parent, param.to_string())
|
let node = self.child(parent, param.to_string());
|
||||||
|
for dimension in ¶m.dimensions {
|
||||||
|
self.add_array_dimension(node, dimension);
|
||||||
|
}
|
||||||
|
node
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_block_stmt(&mut self, parent: NodeIndex, block_stmt: &BlockStmt) -> NodeIndex {
|
fn add_block_stmt(&mut self, parent: NodeIndex, block_stmt: &BlockStmt) -> NodeIndex {
|
||||||
@@ -153,6 +169,12 @@ impl AstGraphBuilder {
|
|||||||
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()),
|
||||||
|
ExprValue::ArrayAccess { array, index } => {
|
||||||
|
let node = self.child(parent, expr.value.to_string());
|
||||||
|
self.add_expr(node, array);
|
||||||
|
self.add_expr(node, index);
|
||||||
|
node
|
||||||
|
}
|
||||||
ExprValue::BinaryOp { lhs, op: _, rhs } => {
|
ExprValue::BinaryOp { lhs, op: _, rhs } => {
|
||||||
let node = self.child(parent, expr.value.to_string());
|
let node = self.child(parent, expr.value.to_string());
|
||||||
self.add_expr(node, lhs);
|
self.add_expr(node, lhs);
|
||||||
|
|||||||
@@ -17,7 +17,12 @@ pub struct VarDeclStmt {
|
|||||||
pub struct VarDeclStmtValue {
|
pub struct VarDeclStmtValue {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub name_span: Span,
|
pub name_span: Span,
|
||||||
|
pub dimensions: Vec<ArrayDimension>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ArrayDimension {
|
||||||
|
pub value: Option<Expr>,
|
||||||
|
pub span: Span,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct FuncDeclStmt {
|
pub struct FuncDeclStmt {
|
||||||
@@ -89,6 +94,10 @@ pub struct Expr {
|
|||||||
pub enum ExprValue {
|
pub enum ExprValue {
|
||||||
IntLit(i64),
|
IntLit(i64),
|
||||||
Var(String),
|
Var(String),
|
||||||
|
ArrayAccess {
|
||||||
|
array: Box<Expr>,
|
||||||
|
index: Box<Expr>,
|
||||||
|
},
|
||||||
BinaryOp {
|
BinaryOp {
|
||||||
lhs: Box<Expr>,
|
lhs: Box<Expr>,
|
||||||
op: BinaryOp,
|
op: BinaryOp,
|
||||||
@@ -159,6 +168,7 @@ 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 dimensions: Vec<ArrayDimension>,
|
||||||
pub name_span: Span,
|
pub name_span: Span,
|
||||||
pub type_span: Span,
|
pub type_span: Span,
|
||||||
}
|
}
|
||||||
@@ -190,6 +200,12 @@ impl fmt::Display for VarDeclStmtValue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for ArrayDimension {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "ArrayDimension")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl fmt::Display for FuncDeclStmt {
|
impl fmt::Display for FuncDeclStmt {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "{} {}", self.return_type, self.name)
|
write!(f, "{} {}", self.return_type, self.name)
|
||||||
@@ -256,6 +272,7 @@ impl fmt::Display for ExprValue {
|
|||||||
match self {
|
match self {
|
||||||
ExprValue::IntLit(value) => write!(f, "IntLit({})", value),
|
ExprValue::IntLit(value) => write!(f, "IntLit({})", value),
|
||||||
ExprValue::Var(name) => write!(f, "Var({})", name),
|
ExprValue::Var(name) => write!(f, "Var({})", name),
|
||||||
|
ExprValue::ArrayAccess { .. } => write!(f, "ArrayAccess"),
|
||||||
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"),
|
||||||
|
|||||||
+121
-17
@@ -1,6 +1,6 @@
|
|||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
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::{backend::{arm_instr::{ARMInstr, AddInstr, BInstr, BlInstr, CmpInstr, ConditionCode, LoadInstr, LoadPseudoInstr, MoveInstr, MulInstr, PopInstr, PushInstr, RegisterOrImm, RsbInstr, SDivInstr, StoreInstr, SubInstr}, register_allocator::{REG_FP, REG_R0, REG_R1, REG_R2, REG_R3, REG_SP, 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::CmpOp as IRCmpOp;
|
||||||
use crate::ir::types::UnaryOp as IRUnaryOp;
|
use crate::ir::types::UnaryOp as IRUnaryOp;
|
||||||
@@ -16,10 +16,82 @@ pub struct Generator {
|
|||||||
const DEFAULT_VAR_ALIGN: usize = 4;
|
const DEFAULT_VAR_ALIGN: usize = 4;
|
||||||
|
|
||||||
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];
|
||||||
|
const ARM_DATA_IMM_CHUNK: i32 = 255;
|
||||||
|
const ARM_LOAD_STORE_MAX_OFFSET: i32 = 4095;
|
||||||
|
|
||||||
|
fn emit_sub_imm(dest: Register, left: Register, imm: i32, instrs: &mut Vec<ARMInstr>) {
|
||||||
|
if imm == 0 {
|
||||||
|
if dest != left {
|
||||||
|
instrs.push(MoveInstr::new_uncond(dest, RegisterOrImm::Reg(left)));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let mut remaining = imm;
|
||||||
|
let first_chunk = remaining.min(ARM_DATA_IMM_CHUNK);
|
||||||
|
instrs.push(SubInstr::new(dest, left, RegisterOrImm::Imm(first_chunk)));
|
||||||
|
remaining -= first_chunk;
|
||||||
|
while remaining > 0 {
|
||||||
|
let chunk = remaining.min(ARM_DATA_IMM_CHUNK);
|
||||||
|
instrs.push(SubInstr::new(dest, dest, RegisterOrImm::Imm(chunk)));
|
||||||
|
remaining -= chunk;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn emit_add_sp_imm(imm: i32, instrs: &mut Vec<ARMInstr>) {
|
||||||
|
let mut remaining = imm;
|
||||||
|
while remaining > 0 {
|
||||||
|
let chunk = remaining.min(ARM_DATA_IMM_CHUNK);
|
||||||
|
instrs.push(AddInstr::new_sp(chunk));
|
||||||
|
remaining -= chunk;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn emit_sub_sp_imm(imm: i32, instrs: &mut Vec<ARMInstr>) {
|
||||||
|
let mut remaining = imm;
|
||||||
|
while remaining > 0 {
|
||||||
|
let chunk = remaining.min(ARM_DATA_IMM_CHUNK);
|
||||||
|
instrs.push(SubInstr::new_sp(chunk));
|
||||||
|
remaining -= chunk;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn emit_load_stack(dest: Register, offset: i32, reg_allocator: &mut RegisterAllocator, instrs: &mut Vec<ARMInstr>) {
|
||||||
|
if offset <= ARM_LOAD_STORE_MAX_OFFSET {
|
||||||
|
instrs.push(LoadInstr::new_stack(dest, offset));
|
||||||
|
} else {
|
||||||
|
let addr_alloc = reg_allocator.alloc_any().expect("Ran out of registers");
|
||||||
|
emit_sub_imm(addr_alloc.reg, REG_FP, offset, instrs);
|
||||||
|
instrs.push(LoadInstr::new(dest, addr_alloc.reg, None));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn emit_store_stack(src: Register, offset: i32, reg_allocator: &mut RegisterAllocator, instrs: &mut Vec<ARMInstr>) {
|
||||||
|
if offset <= ARM_LOAD_STORE_MAX_OFFSET {
|
||||||
|
instrs.push(StoreInstr::new_stack(src, offset));
|
||||||
|
} else {
|
||||||
|
let addr_alloc = reg_allocator.alloc_any().expect("Ran out of registers");
|
||||||
|
emit_sub_imm(addr_alloc.reg, REG_FP, offset, instrs);
|
||||||
|
instrs.push(StoreInstr::new(src, addr_alloc.reg, None));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn load_variable(variable: Variable, reg_allocator: &mut RegisterAllocator, var_index_to_stack_offset: &BTreeMap<usize, usize>, instrs: &mut Vec<ARMInstr>) -> RegisterAlloc {
|
fn load_variable(variable: Variable, reg_allocator: &mut RegisterAllocator, var_index_to_stack_offset: &BTreeMap<usize, usize>, instrs: &mut Vec<ARMInstr>) -> RegisterAlloc {
|
||||||
|
if variable.data_type.is_array() {
|
||||||
|
let var_alloc = reg_allocator.alloc(variable.clone()).expect("Ran out of registers");
|
||||||
|
match variable.var_type {
|
||||||
|
VariableType::Global => {
|
||||||
|
instrs.push(LoadPseudoInstr::new(var_alloc.reg, format!("global_var_{}", variable.index)));
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let stack_offset = var_index_to_stack_offset.get(&variable.index).expect("Variable not declared");
|
||||||
|
emit_sub_imm(var_alloc.reg, REG_FP, *stack_offset as i32, instrs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return var_alloc;
|
||||||
|
}
|
||||||
match variable.var_type {
|
match variable.var_type {
|
||||||
VariableType::Global => {
|
VariableType::Global => {
|
||||||
let var_alloc = reg_allocator.alloc(variable).expect("Ran out of registers");
|
let var_alloc = reg_allocator.alloc(variable.clone()).expect("Ran out of registers");
|
||||||
let var_reg = var_alloc.reg;
|
let var_reg = var_alloc.reg;
|
||||||
// if !var_alloc.is_reused {
|
// if !var_alloc.is_reused {
|
||||||
let address_alloc = reg_allocator.alloc_any().expect("Ran out of registers");
|
let address_alloc = reg_allocator.alloc_any().expect("Ran out of registers");
|
||||||
@@ -34,14 +106,17 @@ fn load_variable(variable: Variable, reg_allocator: &mut RegisterAllocator, var_
|
|||||||
let var_alloc = reg_allocator.alloc_reg(reg).expect("Ran out of registers");
|
let var_alloc = reg_allocator.alloc_reg(reg).expect("Ran out of registers");
|
||||||
var_alloc
|
var_alloc
|
||||||
} else {
|
} else {
|
||||||
todo!("More than 4 parameters not supported yet");
|
let var_alloc = reg_allocator.alloc(variable.clone()).expect("Ran out of registers");
|
||||||
|
let stack_arg_offset = 8 + ((param_index - ARG_REGS.len()) * 4);
|
||||||
|
instrs.push(LoadInstr::new(var_alloc.reg, REG_FP, Some(RegisterOrImm::Imm(stack_arg_offset as i32))));
|
||||||
|
var_alloc
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
let stack_offset = var_index_to_stack_offset.get(&variable.index).expect("Variable not declared");
|
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");
|
let var_alloc = reg_allocator.alloc(variable.clone()).expect("Ran out of registers");
|
||||||
// if !var_alloc.is_reused {
|
// if !var_alloc.is_reused {
|
||||||
instrs.push(LoadInstr::new_stack(var_alloc.reg, *stack_offset as i32));
|
emit_load_stack(var_alloc.reg, *stack_offset as i32, reg_allocator, instrs);
|
||||||
// }
|
// }
|
||||||
var_alloc
|
var_alloc
|
||||||
}
|
}
|
||||||
@@ -60,7 +135,7 @@ fn save_variable(variable: Variable, reg: Register, reg_allocator: &mut Register
|
|||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
let stack_offset = var_index_to_stack_offset.get(&variable.index).expect("Variable not declared");
|
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));
|
emit_store_stack(reg, *stack_offset as i32, reg_allocator, instrs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -134,6 +209,8 @@ impl Generator {
|
|||||||
},
|
},
|
||||||
IRInstr::FuncCall(func, args, ret) => self.emit_func_call(func, args, ret, &var_index_to_stack_offset),
|
IRInstr::FuncCall(func, args, ret) => self.emit_func_call(func, args, ret, &var_index_to_stack_offset),
|
||||||
IRInstr::Move(dest, src) => self.emit_move(dest, src, &var_index_to_stack_offset),
|
IRInstr::Move(dest, src) => self.emit_move(dest, src, &var_index_to_stack_offset),
|
||||||
|
IRInstr::Load(dest, addr) => self.emit_load(dest, addr, &var_index_to_stack_offset),
|
||||||
|
IRInstr::Store(addr, value) => self.emit_store(addr, value, &var_index_to_stack_offset),
|
||||||
IRInstr::Declare(variable) => {
|
IRInstr::Declare(variable) => {
|
||||||
assert!(!encounter_entry, "Variable declarations must come before entry instruction");
|
assert!(!encounter_entry, "Variable declarations must come before entry instruction");
|
||||||
let size = variable.data_type.size_in_bytes();
|
let size = variable.data_type.size_in_bytes();
|
||||||
@@ -143,7 +220,7 @@ impl Generator {
|
|||||||
IRInstr::Entry => {
|
IRInstr::Entry => {
|
||||||
assert!(!encounter_entry, "Multiple entry instructions are not allowed");
|
assert!(!encounter_entry, "Multiple entry instructions are not allowed");
|
||||||
encounter_entry = true;
|
encounter_entry = true;
|
||||||
self.instrs.push(SubInstr::new_sp(stack_size_needed as i32));
|
emit_sub_sp_imm(stack_size_needed as i32, &mut self.instrs);
|
||||||
},
|
},
|
||||||
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::Cmp(variable, left, cmp_op, right) => self.emit_cmp(variable, left, cmp_op, right, &var_index_to_stack_offset),
|
||||||
@@ -163,7 +240,7 @@ impl Generator {
|
|||||||
}
|
}
|
||||||
fn emit_unary(&mut self, dest: Variable, unary_op: IRUnaryOp, variable: Variable, var_index_to_stack_offset: &BTreeMap<usize, usize>) {
|
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 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");
|
let dest_alloc = self.register_allocator.alloc(dest.clone()).expect("Ran out of registers");
|
||||||
match unary_op {
|
match unary_op {
|
||||||
IRUnaryOp::Neg => {
|
IRUnaryOp::Neg => {
|
||||||
self.instrs.push(RsbInstr::new(dest_alloc.reg, variable_alloc.reg, RegisterOrImm::Imm(0)));
|
self.instrs.push(RsbInstr::new(dest_alloc.reg, variable_alloc.reg, RegisterOrImm::Imm(0)));
|
||||||
@@ -193,7 +270,7 @@ impl Generator {
|
|||||||
alloc
|
alloc
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
let variable_alloc = self.register_allocator.alloc(variable).expect("Ran out of registers");
|
let variable_alloc = self.register_allocator.alloc(variable.clone()).expect("Ran out of registers");
|
||||||
let variable_reg = variable_alloc.reg;
|
let variable_reg = variable_alloc.reg;
|
||||||
self.instrs.push(CmpInstr::new(left_alloc.reg, RegisterOrImm::Reg(right_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_uncond(variable_reg, RegisterOrImm::Imm(0)));
|
||||||
@@ -203,25 +280,38 @@ impl Generator {
|
|||||||
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());
|
||||||
if args.len() > 4 {
|
let stack_arg_count = args.len().saturating_sub(ARG_REGS.len());
|
||||||
todo!("More than 4 arguments not supported yet");
|
let stack_arg_size = stack_arg_count * 4;
|
||||||
|
let caller_save_size = 5 * 4;
|
||||||
|
let stack_padding = if (caller_save_size + stack_arg_size) % ARM_STACK_ALIGNMENT == 0 { 0 } else { 4 };
|
||||||
|
let extra_stack_size = stack_arg_size + stack_padding;
|
||||||
|
if extra_stack_size > 0 {
|
||||||
|
emit_sub_sp_imm(extra_stack_size as i32, &mut self.instrs);
|
||||||
}
|
}
|
||||||
let mut arg_reg_allocs = Vec::new();
|
let mut arg_reg_allocs = Vec::new();
|
||||||
for (i, arg) in args.into_iter().enumerate() {
|
for (i, arg) in args.into_iter().enumerate() {
|
||||||
arg_reg_allocs.push(self.register_allocator.alloc_reg(ARG_REGS[i]).expect("Ran out of registers"));
|
|
||||||
let arg_alloc = load_variable(arg, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs);
|
let arg_alloc = load_variable(arg, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs);
|
||||||
self.instrs.push(MoveInstr::new_uncond(ARG_REGS[i], RegisterOrImm::Reg(arg_alloc.reg)));
|
if i < ARG_REGS.len() {
|
||||||
|
arg_reg_allocs.push(self.register_allocator.alloc_reg(ARG_REGS[i]).expect("Ran out of registers"));
|
||||||
|
self.instrs.push(MoveInstr::new_uncond(ARG_REGS[i], RegisterOrImm::Reg(arg_alloc.reg)));
|
||||||
|
} else {
|
||||||
|
let stack_offset = ((i - ARG_REGS.len()) * 4) as i32;
|
||||||
|
self.instrs.push(StoreInstr::new(arg_alloc.reg, REG_SP, Some(RegisterOrImm::Imm(stack_offset))));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
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 {
|
||||||
save_variable(ret, REG_R0, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs);
|
save_variable(ret, REG_R0, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if extra_stack_size > 0 {
|
||||||
|
emit_add_sp_imm(extra_stack_size as i32, &mut self.instrs);
|
||||||
|
}
|
||||||
|
|
||||||
self.instrs.push(PopInstr::new_pop_caller_save());
|
self.instrs.push(PopInstr::new_pop_caller_save());
|
||||||
}
|
}
|
||||||
fn emit_move(&mut self, dest: Variable, src: MoveRValue, var_index_to_stack_offset: &BTreeMap<usize, usize>) {
|
fn emit_move(&mut self, dest: Variable, src: MoveRValue, var_index_to_stack_offset: &BTreeMap<usize, usize>) {
|
||||||
let dest_alloc = self.register_allocator.alloc(dest).expect("Ran out of registers");
|
let dest_alloc = self.register_allocator.alloc(dest.clone()).expect("Ran out of registers");
|
||||||
let dest_register = dest_alloc.reg;
|
let dest_register = dest_alloc.reg;
|
||||||
match src {
|
match src {
|
||||||
MoveRValue::Var(variable) => {
|
MoveRValue::Var(variable) => {
|
||||||
@@ -232,11 +322,25 @@ impl Generator {
|
|||||||
};
|
};
|
||||||
save_variable(dest, dest_alloc.reg, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs);
|
save_variable(dest, dest_alloc.reg, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn emit_load(&mut self, dest: Variable, addr: Variable, var_index_to_stack_offset: &BTreeMap<usize, usize>) {
|
||||||
|
let addr_alloc = load_variable(addr, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs);
|
||||||
|
let dest_alloc = self.register_allocator.alloc(dest.clone()).expect("Ran out of registers");
|
||||||
|
self.instrs.push(LoadInstr::new(dest_alloc.reg, addr_alloc.reg, None));
|
||||||
|
save_variable(dest, dest_alloc.reg, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn emit_store(&mut self, addr: Variable, value: Variable, var_index_to_stack_offset: &BTreeMap<usize, usize>) {
|
||||||
|
let addr_alloc = load_variable(addr, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs);
|
||||||
|
let value_alloc = load_variable(value, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs);
|
||||||
|
self.instrs.push(StoreInstr::new(value_alloc.reg, addr_alloc.reg, None));
|
||||||
|
}
|
||||||
|
|
||||||
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 dest_alloc = self.register_allocator.alloc(dest).expect("Ran out of registers");
|
let dest_alloc = self.register_allocator.alloc(dest.clone()).expect("Ran out of registers");
|
||||||
let dest_reg = dest_alloc.reg;
|
let dest_reg = dest_alloc.reg;
|
||||||
// should consider left == right
|
// should consider left == right
|
||||||
let left_alloc = load_variable(left, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs);
|
let left_alloc = load_variable(left.clone(), &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs);
|
||||||
let (_right_alloc, right_reg) = if left != right {
|
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_alloc = load_variable(right, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs);
|
||||||
let right_reg = right_alloc.reg;
|
let right_reg = right_alloc.reg;
|
||||||
@@ -268,4 +372,4 @@ impl Generator {
|
|||||||
save_variable(dest, dest_alloc.reg, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs);
|
save_variable(dest, dest_alloc.reg, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-7
@@ -15,6 +15,7 @@ mod tests {
|
|||||||
use crate::utils::case_list::CaseList;
|
use crate::utils::case_list::CaseList;
|
||||||
use crate::utils::num_sequence::NumberSequence;
|
use crate::utils::num_sequence::NumberSequence;
|
||||||
use crate::ir::generator::Generator as IRGenerator;
|
use crate::ir::generator::Generator as IRGenerator;
|
||||||
|
use crate::sema::analyzer::Analyzer;
|
||||||
pub use super::generator::Generator as ASMGenerator;
|
pub use super::generator::Generator as ASMGenerator;
|
||||||
fn test_case(case_str: &str) {
|
fn test_case(case_str: &str) {
|
||||||
let case_sequence = NumberSequence::from_str(case_str).unwrap();
|
let case_sequence = NumberSequence::from_str(case_str).unwrap();
|
||||||
@@ -49,12 +50,14 @@ mod tests {
|
|||||||
parser.diagnostics.print(&format!("{}", case_path.display()), &full_text);
|
parser.diagnostics.print(&format!("{}", case_path.display()), &full_text);
|
||||||
is_error = true;
|
is_error = true;
|
||||||
}
|
}
|
||||||
let mut generator = IRGenerator::new();
|
let mut analyzer = Analyzer::new();
|
||||||
let ir = generator.emit(compile_unit);
|
let hir = analyzer.analyze(compile_unit);
|
||||||
// if !generator.diagnostic.is_empty() {
|
if !analyzer.get_diagnostics().is_empty() {
|
||||||
// generator.diagnostic.print(&format!("{}", case_path.display()), &full_text);
|
analyzer.get_diagnostics().print(&format!("{}", case_path.display()), &full_text);
|
||||||
// is_error = true;
|
is_error = true;
|
||||||
// }
|
}
|
||||||
|
let mut generator = IRGenerator::new(&analyzer);
|
||||||
|
let ir = generator.emit(hir);
|
||||||
let mut asm_generator = ASMGenerator::new();
|
let mut asm_generator = ASMGenerator::new();
|
||||||
asm_generator.emit(ir);
|
asm_generator.emit(ir);
|
||||||
let asm_text = asm_generator.to_text();
|
let asm_text = asm_generator.to_text();
|
||||||
@@ -82,4 +85,4 @@ mod tests {
|
|||||||
fn test_func() {
|
fn test_func() {
|
||||||
test_case("12-13,58-60");
|
test_case("12-13,58-60");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ impl RegisterAllocatorInner {
|
|||||||
*use_kind = RegisterUseKind::Free;
|
*use_kind = RegisterUseKind::Free;
|
||||||
},
|
},
|
||||||
RegisterUseKind::UsedByVariable(var) => {
|
RegisterUseKind::UsedByVariable(var) => {
|
||||||
*use_kind = RegisterUseKind::AllocatedToVariable(*var);
|
*use_kind = RegisterUseKind::AllocatedToVariable(var.clone());
|
||||||
},
|
},
|
||||||
_ => panic!("Trying to mark a register as unused that is not in use"),
|
_ => panic!("Trying to mark a register as unused that is not in use"),
|
||||||
}
|
}
|
||||||
@@ -139,8 +139,8 @@ impl RegisterAllocator {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if let RegisterUseKind::Free = use_kind {
|
if let RegisterUseKind::Free = use_kind {
|
||||||
*use_kind = RegisterUseKind::UsedByVariable(var);
|
*use_kind = RegisterUseKind::UsedByVariable(var.clone());
|
||||||
inner.variable_to_register.insert(var, reg);
|
inner.variable_to_register.insert(var.clone(), reg);
|
||||||
return Some(RegisterAlloc {
|
return Some(RegisterAlloc {
|
||||||
allocator: Rc::downgrade(&self.inner),
|
allocator: Rc::downgrade(&self.inner),
|
||||||
reg,
|
reg,
|
||||||
@@ -156,8 +156,8 @@ impl RegisterAllocator {
|
|||||||
}
|
}
|
||||||
if let RegisterUseKind::AllocatedToVariable(ori_var) = use_kind {
|
if let RegisterUseKind::AllocatedToVariable(ori_var) = use_kind {
|
||||||
assert!(variable_to_register.remove(&ori_var).is_some());
|
assert!(variable_to_register.remove(&ori_var).is_some());
|
||||||
*use_kind = RegisterUseKind::UsedByVariable(var);
|
*use_kind = RegisterUseKind::UsedByVariable(var.clone());
|
||||||
variable_to_register.insert(var, reg);
|
variable_to_register.insert(var.clone(), reg);
|
||||||
return Some(RegisterAlloc {
|
return Some(RegisterAlloc {
|
||||||
allocator: Rc::downgrade(&self.inner),
|
allocator: Rc::downgrade(&self.inner),
|
||||||
reg,
|
reg,
|
||||||
@@ -245,4 +245,4 @@ impl RegisterAllocator {
|
|||||||
self.debug_use();
|
self.debug_use();
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-2
@@ -1,9 +1,11 @@
|
|||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::frontend::err::FrontendError;
|
use crate::{frontend::err::FrontendError, sema::err::SemaError};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Error)]
|
#[derive(Debug, Clone, PartialEq, Eq, Error)]
|
||||||
pub enum CompileError {
|
pub enum CompileError {
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Frontend(#[from] FrontendError),
|
Frontend(#[from] FrontendError),
|
||||||
}
|
#[error(transparent)]
|
||||||
|
Sema(#[from] SemaError),
|
||||||
|
}
|
||||||
|
|||||||
@@ -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>,
|
||||||
@@ -248,6 +248,8 @@ fn parse_delimiter(
|
|||||||
')' => TokenValue::RParen,
|
')' => TokenValue::RParen,
|
||||||
'{' => TokenValue::LBrace,
|
'{' => TokenValue::LBrace,
|
||||||
'}' => TokenValue::RBrace,
|
'}' => TokenValue::RBrace,
|
||||||
|
'[' => TokenValue::LBracket,
|
||||||
|
']' => TokenValue::RBracket,
|
||||||
_ => return Err(LexParseError::NotMatched),
|
_ => return Err(LexParseError::NotMatched),
|
||||||
};
|
};
|
||||||
str_iter.advance(1);
|
str_iter.advance(1);
|
||||||
@@ -398,4 +400,4 @@ mod tests {
|
|||||||
fn test_expr() {
|
fn test_expr() {
|
||||||
test_case("0-3,14-25");
|
test_case("0-3,14-25");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+100
-27
@@ -1,6 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
ast::types::{
|
ast::types::{
|
||||||
BinaryOp, BlockStmt, BreakStmt, CompileUnit, ContinueStmt, Expr, ExprValue, FuncDeclStmt, GlobalDeclStmt, IfElseBranch, IfStmt, Param, ReturnStmt, Statement, UnaryOp, VarDeclStmt, VarDeclStmtValue, WhileStmt
|
ArrayDimension, BinaryOp, BlockStmt, BreakStmt, CompileUnit, ContinueStmt, Expr, ExprValue, FuncDeclStmt, GlobalDeclStmt, IfElseBranch, IfStmt, Param, ReturnStmt, Statement, UnaryOp, VarDeclStmt, VarDeclStmtValue, WhileStmt
|
||||||
},
|
},
|
||||||
diagnostic::{Diagnositics, span::Span},
|
diagnostic::{Diagnositics, span::Span},
|
||||||
frontend::{
|
frontend::{
|
||||||
@@ -259,6 +259,7 @@ impl Parser {
|
|||||||
Ok(Param {
|
Ok(Param {
|
||||||
param_type: param_type.into(),
|
param_type: param_type.into(),
|
||||||
name,
|
name,
|
||||||
|
dimensions: self.parse_array_dimensions(true)?,
|
||||||
name_span,
|
name_span,
|
||||||
type_span,
|
type_span,
|
||||||
})
|
})
|
||||||
@@ -300,7 +301,8 @@ impl Parser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
values.push(VarDeclStmtValue { name, name_span });
|
let dimensions = self.parse_array_dimensions(false)?;
|
||||||
|
values.push(VarDeclStmtValue { name, name_span, dimensions });
|
||||||
let mut last_name = true; // indicate whether the last parsed token is a variable name
|
let mut last_name = true; // indicate whether the last parsed token is a variable name
|
||||||
while let Some(t) = self.peek() {
|
while let Some(t) = self.peek() {
|
||||||
if matches!(t.value, TokenValue::Semicolon) { // statement end
|
if matches!(t.value, TokenValue::Semicolon) { // statement end
|
||||||
@@ -319,7 +321,8 @@ impl Parser {
|
|||||||
}
|
}
|
||||||
if let Some(ident) = self.peek().unwrap().value.as_ident() {
|
if let Some(ident) = self.peek().unwrap().value.as_ident() {
|
||||||
let span = self.next().unwrap().span;
|
let span = self.next().unwrap().span;
|
||||||
values.push(VarDeclStmtValue { name: ident, name_span: span });
|
let dimensions = self.parse_array_dimensions(false)?;
|
||||||
|
values.push(VarDeclStmtValue { name: ident, name_span: span, dimensions });
|
||||||
last_name = true;
|
last_name = true;
|
||||||
} else {
|
} else {
|
||||||
let token = self.next().unwrap().clone();
|
let token = self.next().unwrap().clone();
|
||||||
@@ -334,6 +337,50 @@ impl Parser {
|
|||||||
Ok(VarDeclStmt { values, type_span, data_type: var_type.into() })
|
Ok(VarDeclStmt { values, type_span, data_type: var_type.into() })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_array_dimensions(&mut self, allow_empty_first: bool) -> Result<Vec<ArrayDimension>, ParseProcessError> {
|
||||||
|
let mut dimensions = vec![];
|
||||||
|
while self.peek().is_some_and(|t| t.value == TokenValue::LBracket) {
|
||||||
|
let start_span = self.next().unwrap().span;
|
||||||
|
let value = if self.peek().is_some_and(|t| t.value == TokenValue::RBracket) {
|
||||||
|
if !allow_empty_first || !dimensions.is_empty() {
|
||||||
|
let span = self.peek().unwrap().span;
|
||||||
|
self.diagnostics.add_from_frontend_error(
|
||||||
|
ParseError::UnexpectedToken(TokenValue::RBracket, "array dimension expression"),
|
||||||
|
span,
|
||||||
|
);
|
||||||
|
return Err(ParseProcessError::ErrorInMatch);
|
||||||
|
}
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(self.parse_expr()?)
|
||||||
|
};
|
||||||
|
let end_span = match self.peek() {
|
||||||
|
Some(t) if t.value == TokenValue::RBracket => {
|
||||||
|
let span = t.span;
|
||||||
|
self.advance(1);
|
||||||
|
span
|
||||||
|
}
|
||||||
|
Some(_) => {
|
||||||
|
let token = self.next().unwrap().clone();
|
||||||
|
self.diagnostics.add_from_frontend_error(
|
||||||
|
ParseError::UnexpectedToken(token.value, "`]`"),
|
||||||
|
token.span,
|
||||||
|
);
|
||||||
|
return Err(ParseProcessError::ErrorInMatch);
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
self.diagnostics.add_from_frontend_error(ParseError::ExpectButEof("`]`"), start_span);
|
||||||
|
return Err(ParseProcessError::ErrorInMatch);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
dimensions.push(ArrayDimension {
|
||||||
|
value,
|
||||||
|
span: Span::from_two(start_span, end_span),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok(dimensions)
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_block_stmt(&mut self, parse_type: ParseType) -> Result<BlockStmt, ParseProcessError> {
|
fn parse_block_stmt(&mut self, parse_type: ParseType) -> Result<BlockStmt, ParseProcessError> {
|
||||||
assert!(self.peek().is_some());
|
assert!(self.peek().is_some());
|
||||||
if self
|
if self
|
||||||
@@ -598,7 +645,7 @@ impl Parser {
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
fn parse_primary(&mut self) -> Result<Expr, ParseProcessError> {
|
fn parse_primary_atom(&mut self) -> Result<Expr, ParseProcessError> {
|
||||||
assert!(self.peek().is_some());
|
assert!(self.peek().is_some());
|
||||||
let token = self.next().unwrap().clone();
|
let token = self.next().unwrap().clone();
|
||||||
match token.value {
|
match token.value {
|
||||||
@@ -712,6 +759,47 @@ impl Parser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fn parse_primary(&mut self) -> Result<Expr, ParseProcessError> {
|
||||||
|
let mut expr = self.parse_primary_atom()?;
|
||||||
|
while self.peek().is_some_and(|t| t.value == TokenValue::LBracket) {
|
||||||
|
let start_span = expr.span;
|
||||||
|
self.advance(1);
|
||||||
|
let index = match self.peek() {
|
||||||
|
Some(_) => self.parse_expr()?,
|
||||||
|
None => {
|
||||||
|
self.diagnostics.add_from_frontend_error(ParseError::ExpectButEof("array index expression"), start_span);
|
||||||
|
return Err(ParseProcessError::ErrorInMatch);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let end_span = match self.peek() {
|
||||||
|
Some(t) if t.value == TokenValue::RBracket => {
|
||||||
|
let span = t.span;
|
||||||
|
self.advance(1);
|
||||||
|
span
|
||||||
|
}
|
||||||
|
Some(_) => {
|
||||||
|
let token = self.next().unwrap().clone();
|
||||||
|
self.diagnostics.add_from_frontend_error(
|
||||||
|
ParseError::UnexpectedToken(token.value, "`]`"),
|
||||||
|
token.span,
|
||||||
|
);
|
||||||
|
return Err(ParseProcessError::ErrorInMatch);
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
self.diagnostics.add_from_frontend_error(ParseError::ExpectButEof("`]`"), index.span);
|
||||||
|
return Err(ParseProcessError::ErrorInMatch);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
expr = Expr {
|
||||||
|
value: ExprValue::ArrayAccess {
|
||||||
|
array: Box::new(expr),
|
||||||
|
index: Box::new(index),
|
||||||
|
},
|
||||||
|
span: Span::from_two(start_span, end_span),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Ok(expr)
|
||||||
|
}
|
||||||
fn parse_unary(&mut self) -> Result<Expr, ParseProcessError> {
|
fn parse_unary(&mut self) -> Result<Expr, ParseProcessError> {
|
||||||
assert!(self.peek().is_some());
|
assert!(self.peek().is_some());
|
||||||
let token = self.peek().unwrap().clone();
|
let token = self.peek().unwrap().clone();
|
||||||
@@ -937,40 +1025,21 @@ impl Parser {
|
|||||||
}
|
}
|
||||||
fn parse_assign(&mut self) -> Result<Expr, ParseProcessError> {
|
fn parse_assign(&mut self) -> Result<Expr, ParseProcessError> {
|
||||||
assert!(self.peek().is_some());
|
assert!(self.peek().is_some());
|
||||||
let is_assign = matches!(
|
let lvalue = self.parse_logical_or()?;
|
||||||
(self.tokens.get(self.pos), self.tokens.get(self.pos + 1)),
|
if !self.peek().is_some_and(|t| t.value == TokenValue::Equal) {
|
||||||
(
|
return Ok(lvalue);
|
||||||
Some(Token {
|
|
||||||
value: TokenValue::Ident(_),
|
|
||||||
..
|
|
||||||
}),
|
|
||||||
Some(Token {
|
|
||||||
value: TokenValue::Equal,
|
|
||||||
..
|
|
||||||
})
|
|
||||||
)
|
|
||||||
);
|
|
||||||
if !is_assign {
|
|
||||||
return self.parse_logical_or();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let lvalue_token = self.next().unwrap().clone();
|
|
||||||
let name = lvalue_token.value.as_ident().unwrap();
|
|
||||||
self.advance(1);
|
self.advance(1);
|
||||||
let rvalue = match self.peek() {
|
let rvalue = match self.peek() {
|
||||||
Some(_) => self.parse_assign()?,
|
Some(_) => self.parse_assign()?,
|
||||||
None => {
|
None => {
|
||||||
self.diagnostics.add_from_frontend_error(
|
self.diagnostics.add_from_frontend_error(
|
||||||
ParseError::ExpectButEof("expression"),
|
ParseError::ExpectButEof("expression"),
|
||||||
lvalue_token.span,
|
lvalue.span,
|
||||||
);
|
);
|
||||||
return Err(ParseProcessError::ErrorInMatch);
|
return Err(ParseProcessError::ErrorInMatch);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let lvalue = Expr {
|
|
||||||
value: ExprValue::Var(name),
|
|
||||||
span: lvalue_token.span,
|
|
||||||
};
|
|
||||||
let span = Span::from_two(lvalue.span, rvalue.span);
|
let span = Span::from_two(lvalue.span, rvalue.span);
|
||||||
Ok(Expr {
|
Ok(Expr {
|
||||||
value: ExprValue::Assign {
|
value: ExprValue::Assign {
|
||||||
@@ -1100,4 +1169,8 @@ mod tests {
|
|||||||
fn test_func() {
|
fn test_func() {
|
||||||
test_case("12-13,58-60");
|
test_case("12-13,58-60");
|
||||||
}
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_array() {
|
||||||
|
test_case("4-7,11,42,71,74-78,82,84,86,90,95,96,100,101,103");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ pub enum TokenValue {
|
|||||||
|
|
||||||
LParen, RParen,
|
LParen, RParen,
|
||||||
LBrace, RBrace,
|
LBrace, RBrace,
|
||||||
|
LBracket, RBracket,
|
||||||
Comma, Semicolon,
|
Comma, Semicolon,
|
||||||
|
|
||||||
If, Else, While, Return, Break, Continue,
|
If, Else, While, Return, Break, Continue,
|
||||||
@@ -69,6 +70,8 @@ impl std::fmt::Display for TokenValue {
|
|||||||
TokenValue::RParen => write!(f, "`)`"),
|
TokenValue::RParen => write!(f, "`)`"),
|
||||||
TokenValue::LBrace => write!(f, "`{{`"),
|
TokenValue::LBrace => write!(f, "`{{`"),
|
||||||
TokenValue::RBrace => write!(f, "`}}`"),
|
TokenValue::RBrace => write!(f, "`}}`"),
|
||||||
|
TokenValue::LBracket => write!(f, "`[`"),
|
||||||
|
TokenValue::RBracket => write!(f, "`]`"),
|
||||||
TokenValue::Comma => write!(f, "`,`"),
|
TokenValue::Comma => write!(f, "`,`"),
|
||||||
TokenValue::Semicolon => write!(f, "`;`"),
|
TokenValue::Semicolon => write!(f, "`;`"),
|
||||||
TokenValue::If => write!(f, "if"),
|
TokenValue::If => write!(f, "if"),
|
||||||
@@ -94,6 +97,7 @@ pub enum TokenKind {
|
|||||||
|
|
||||||
LParen, RParen,
|
LParen, RParen,
|
||||||
LBrace, RBrace,
|
LBrace, RBrace,
|
||||||
|
LBracket, RBracket,
|
||||||
Comma, Semicolon,
|
Comma, Semicolon,
|
||||||
|
|
||||||
If, Else, While, Return, Break, Continue,
|
If, Else, While, Return, Break, Continue,
|
||||||
@@ -107,4 +111,4 @@ pub enum TypeIdent {
|
|||||||
Int,
|
Int,
|
||||||
#[strum(serialize = "void")]
|
#[strum(serialize = "void")]
|
||||||
Void,
|
Void,
|
||||||
}
|
}
|
||||||
|
|||||||
+174
-271
@@ -1,13 +1,11 @@
|
|||||||
use std::{collections::{BTreeMap, BTreeSet}, vec};
|
use std::{collections::BTreeMap, vec};
|
||||||
|
|
||||||
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::{ir::types::{CmpOp, Function, IRInstr, IRType, MoveRValue, UnaryOp, Variable, VariableOrIntLit, VariableType}, sema::{analyzer::Analyzer as SemaAnalyzer, hir::{HirBlockStmt, HirBreakStmt, HirCompileUnit, HirContinueStmt, HirExpr, HirExprValue, HirFuncDeclStmt, HirGlobalDeclStmt, HirIfElseBranch, HirIfStmt, HirReturnStmt, HirStatement, HirVarDeclStmt, HirWhileStmt}, symbol::{FunctionId, SymbolId}}};
|
||||||
use crate::ast::types::BinaryOp as AstBinaryOp;
|
use crate::ast::types::BinaryOp as AstBinaryOp;
|
||||||
use crate::ast::types::UnaryOp as AstUnaryOp;
|
use crate::ast::types::UnaryOp as AstUnaryOp;
|
||||||
pub struct Generator {
|
pub struct Generator<'a> {
|
||||||
|
sema: &'a SemaAnalyzer,
|
||||||
var_manager: VariableManager,
|
var_manager: VariableManager,
|
||||||
function_map: BTreeMap<String, Function>,
|
|
||||||
current_func_return_type: Option<IRType>,
|
|
||||||
diagnostic: Diagnositics,
|
|
||||||
current_exit_label: Vec<Option<(usize, usize)>>, // true exit, false exit
|
current_exit_label: Vec<Option<(usize, usize)>>, // true exit, false exit
|
||||||
// About exit label passing:
|
// About exit label passing:
|
||||||
// for non-logical parent expr, current_exit_label is None
|
// for non-logical parent expr, current_exit_label is None
|
||||||
@@ -19,24 +17,11 @@ pub struct Generator {
|
|||||||
label_counter: usize
|
label_counter: usize
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Generator {
|
impl<'a> Generator<'a> {
|
||||||
pub fn new() -> Self {
|
pub fn new(sema: &'a SemaAnalyzer) -> Self {
|
||||||
let mut function_map = BTreeMap::new();
|
|
||||||
function_map.insert("putint".to_string(), Function {
|
|
||||||
name: "putint".to_string(),
|
|
||||||
parameter_types: vec![IRType::I32],
|
|
||||||
return_type: IRType::Void,
|
|
||||||
});
|
|
||||||
function_map.insert("getint".to_string(), Function {
|
|
||||||
name: "getint".to_string(),
|
|
||||||
parameter_types: vec![],
|
|
||||||
return_type: IRType::I32,
|
|
||||||
});
|
|
||||||
Self {
|
Self {
|
||||||
|
sema,
|
||||||
var_manager: VariableManager::new(),
|
var_manager: VariableManager::new(),
|
||||||
current_func_return_type: None,
|
|
||||||
diagnostic: Diagnositics::new(),
|
|
||||||
function_map,
|
|
||||||
current_exit_label: vec![],
|
current_exit_label: vec![],
|
||||||
while_exit_label: vec![],
|
while_exit_label: vec![],
|
||||||
func_exit: None,
|
func_exit: None,
|
||||||
@@ -48,15 +33,22 @@ impl Generator {
|
|||||||
self.label_counter += 1;
|
self.label_counter += 1;
|
||||||
label
|
label
|
||||||
}
|
}
|
||||||
pub fn emit(&mut self, compile_unit: CompileUnit) -> Vec<IRInstr> {
|
pub fn emit(&mut self, compile_unit: HirCompileUnit) -> Vec<IRInstr> {
|
||||||
self.generate_compile_unit(compile_unit)
|
self.generate_compile_unit(compile_unit)
|
||||||
}
|
}
|
||||||
pub fn get_diagnostics(&self) -> &Diagnositics {
|
|
||||||
&self.diagnostic
|
fn ir_function(&self, function_id: FunctionId) -> Function {
|
||||||
|
let sig = self.sema.get_function_sig(function_id);
|
||||||
|
Function {
|
||||||
|
name: sig.name.clone(),
|
||||||
|
parameter_types: sig.parameter_types.iter().map(|ty| ty.clone().into()).collect(),
|
||||||
|
return_type: sig.return_type.clone().into(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fn generate_compile_unit(&mut self, compile_unit: CompileUnit) -> Vec<IRInstr> {
|
|
||||||
|
fn generate_compile_unit(&mut self, compile_unit: HirCompileUnit) -> Vec<IRInstr> {
|
||||||
let mut instrs = vec![];
|
let mut instrs = vec![];
|
||||||
use GlobalDeclStmt::*;
|
use HirGlobalDeclStmt::*;
|
||||||
for decl in compile_unit.global_decls {
|
for decl in compile_unit.global_decls {
|
||||||
match decl {
|
match decl {
|
||||||
VarDecl(var_decl) => {
|
VarDecl(var_decl) => {
|
||||||
@@ -70,48 +62,29 @@ impl Generator {
|
|||||||
instrs
|
instrs
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_var_decl(&mut self, var_decl: VarDeclStmt, is_global: bool) -> Vec<IRInstr> {
|
fn generate_var_decl(&mut self, var_decl: HirVarDeclStmt, is_global: bool) -> Vec<IRInstr> {
|
||||||
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, var_decl.data_type.into()) {
|
let var = self.var_manager.declare_symbol(value.symbol, var_type, self.sema.get_symbol_type(value.symbol).into());
|
||||||
Ok(var) => {
|
if is_global {
|
||||||
if is_global { instrs.push(IRInstr::Declare(var)); }
|
instrs.push(IRInstr::Declare(var));
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
self.diagnostic.add_from_ir_error(e, value.name_span);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
instrs
|
instrs
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_func_decl(&mut self, func_decl: FuncDeclStmt) -> Vec<IRInstr> {
|
fn generate_func_decl(&mut self, func_decl: HirFuncDeclStmt) -> Vec<IRInstr> {
|
||||||
if self.function_map.contains_key(&func_decl.name) {
|
let parameters: Vec<Variable> = func_decl.params.iter()
|
||||||
self.diagnostic.add_from_ir_error(IRError::FunctionHasBeenDefined(func_decl.name.clone()), func_decl.name_span);
|
.map(|param| self.var_manager.declare_symbol(param.symbol, VariableType::Local, param.param_type.clone().into()))
|
||||||
return vec![];
|
.collect();
|
||||||
}
|
|
||||||
self.current_func_return_type = Some(func_decl.return_type.into());
|
|
||||||
self.var_manager.enter_scope();
|
|
||||||
let parameters: Vec<Variable> = match func_decl.params.iter().map(|param| {
|
|
||||||
match self.var_manager.declare_variable(¶m.name, VariableType::Local, param.param_type.into()) {
|
|
||||||
Ok(var) => Ok(var),
|
|
||||||
Err(e) => {
|
|
||||||
self.diagnostic.add_from_ir_error(e, param.name_span);
|
|
||||||
Err(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).collect() {
|
|
||||||
Ok(p) => p,
|
|
||||||
Err(()) => return vec![],
|
|
||||||
};
|
|
||||||
let temp_parameters = parameters.iter().enumerate()
|
let temp_parameters = parameters.iter().enumerate()
|
||||||
.map(|(i, param)| self.var_manager.declare_param_temp(param.data_type, i))
|
.map(|(i, param)| self.var_manager.declare_param_temp(param.data_type.clone(), i))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
let mut body_instrs = vec![];
|
let mut body_instrs = vec![];
|
||||||
self.func_exit = Some((self.request_label(), {
|
self.func_exit = Some((self.request_label(), {
|
||||||
let ret_type = func_decl.return_type.into();
|
let ret_type = func_decl.sig.return_type.into();
|
||||||
if ret_type != IRType::Void {
|
if ret_type != IRType::Void {
|
||||||
Some(self.var_manager.declare_unamed_local(ret_type))
|
Some(self.var_manager.declare_unamed_local(ret_type))
|
||||||
} else {
|
} else {
|
||||||
@@ -127,24 +100,18 @@ impl Generator {
|
|||||||
}
|
}
|
||||||
body_instrs.push(IRInstr::Entry);
|
body_instrs.push(IRInstr::Entry);
|
||||||
parameters.iter().zip(temp_parameters.iter()).for_each(|(param, temp_param)| {
|
parameters.iter().zip(temp_parameters.iter()).for_each(|(param, temp_param)| {
|
||||||
body_instrs.push(IRInstr::Move(*param, MoveRValue::Var(*temp_param)));
|
body_instrs.push(IRInstr::Move(param.clone(), MoveRValue::Var(temp_param.clone())));
|
||||||
});
|
});
|
||||||
body_instrs.extend(block_instrs);
|
body_instrs.extend(block_instrs);
|
||||||
let func_exit = self.func_exit.take().unwrap();
|
let func_exit = self.func_exit.take().unwrap();
|
||||||
body_instrs.push(IRInstr::Label(func_exit.0));
|
body_instrs.push(IRInstr::Label(func_exit.0));
|
||||||
body_instrs.push(IRInstr::Exit(func_exit.1));
|
body_instrs.push(IRInstr::Exit(func_exit.1));
|
||||||
self.var_manager.exit_scope();
|
|
||||||
self.current_func_return_type = None;
|
|
||||||
self.var_manager.clear_local_counter();
|
self.var_manager.clear_local_counter();
|
||||||
let func = Function {
|
let mut func = self.ir_function(func_decl.sig.id);
|
||||||
name: func_decl.name,
|
func.parameter_types = temp_parameters.iter().map(|v| v.data_type.clone()).collect();
|
||||||
parameter_types: temp_parameters.iter().map(|v| v.data_type).collect(),
|
|
||||||
return_type: func_decl.return_type.into(),
|
|
||||||
};
|
|
||||||
self.function_map.insert(func.name.clone(), func.clone());
|
|
||||||
vec![IRInstr::DefineFunc(func, temp_parameters, body_instrs)]
|
vec![IRInstr::DefineFunc(func, temp_parameters, body_instrs)]
|
||||||
}
|
}
|
||||||
fn generate_block_stmt(&mut self, block_stmt: BlockStmt) -> Vec<IRInstr> {
|
fn generate_block_stmt(&mut self, block_stmt: HirBlockStmt) -> Vec<IRInstr> {
|
||||||
let mut instrs = vec![];
|
let mut instrs = vec![];
|
||||||
for stmt in block_stmt.statements {
|
for stmt in block_stmt.statements {
|
||||||
instrs.extend(self.generate_statement(stmt));
|
instrs.extend(self.generate_statement(stmt));
|
||||||
@@ -152,8 +119,8 @@ impl Generator {
|
|||||||
instrs
|
instrs
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_statement(&mut self, stmt: Statement) -> Vec<IRInstr> {
|
fn generate_statement(&mut self, stmt: HirStatement) -> Vec<IRInstr> {
|
||||||
use Statement::*;
|
use HirStatement::*;
|
||||||
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),
|
If(if_stmt) => self.generate_if_stmt(if_stmt),
|
||||||
@@ -161,10 +128,7 @@ impl Generator {
|
|||||||
Break(break_stmt) => self.generate_break_stmt(break_stmt),
|
Break(break_stmt) => self.generate_break_stmt(break_stmt),
|
||||||
Continue(continue_stmt) => self.generate_continue_stmt(continue_stmt),
|
Continue(continue_stmt) => self.generate_continue_stmt(continue_stmt),
|
||||||
Block(block_stmt) => {
|
Block(block_stmt) => {
|
||||||
self.var_manager.enter_scope();
|
self.generate_block_stmt(block_stmt)
|
||||||
let block_instrs = self.generate_block_stmt(block_stmt);
|
|
||||||
self.var_manager.exit_scope();
|
|
||||||
block_instrs
|
|
||||||
},
|
},
|
||||||
Expr(expr) => {
|
Expr(expr) => {
|
||||||
self.current_exit_label.push(None);
|
self.current_exit_label.push(None);
|
||||||
@@ -183,16 +147,11 @@ impl Generator {
|
|||||||
instrs
|
instrs
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_return_stmt(&mut self, return_stmt: ReturnStmt) -> Vec<IRInstr> {
|
fn generate_return_stmt(&mut self, return_stmt: HirReturnStmt) -> Vec<IRInstr> {
|
||||||
let mut instrs = vec![];
|
let mut instrs = vec![];
|
||||||
let func_exit = self.func_exit.unwrap();
|
let func_exit = self.func_exit.clone().unwrap();
|
||||||
match return_stmt.value {
|
match return_stmt.value {
|
||||||
Some(expr) => {
|
Some(expr) => {
|
||||||
if func_exit.1.is_none() {
|
|
||||||
// shouldn't return value but return stmt has expr;
|
|
||||||
self.diagnostic.add_from_ir_error(IRError::ReturnExpressionOnVoidFunction, return_stmt.span);
|
|
||||||
return vec![];
|
|
||||||
}
|
|
||||||
self.current_exit_label.push(None);
|
self.current_exit_label.push(None);
|
||||||
let (value_instrs, value_var) = match self.generate_expr(expr) {
|
let (value_instrs, value_var) = match self.generate_expr(expr) {
|
||||||
Some(res) => res,
|
Some(res) => res,
|
||||||
@@ -202,37 +161,21 @@ impl Generator {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
self.current_exit_label.pop();
|
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.extend(value_instrs);
|
||||||
instrs.push(IRInstr::Move(func_exit.1.unwrap(), MoveRValue::Var(value_var.unwrap())));
|
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 => {
|
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.push(IRInstr::Goto(func_exit.0));
|
||||||
instrs
|
instrs
|
||||||
}
|
}
|
||||||
fn generate_while_stmt(&mut self, while_stmt: WhileStmt) -> Vec<IRInstr> {
|
fn generate_while_stmt(&mut self, while_stmt: HirWhileStmt) -> Vec<IRInstr> {
|
||||||
let mut instrs = vec![];
|
let mut instrs = vec![];
|
||||||
let cond_label = self.request_label();
|
let cond_label = self.request_label();
|
||||||
let body_label = self.request_label();
|
let body_label = self.request_label();
|
||||||
let exit_label = self.request_label();
|
let exit_label = self.request_label();
|
||||||
instrs.push(IRInstr::Label(cond_label));
|
instrs.push(IRInstr::Label(cond_label));
|
||||||
self.current_exit_label.push(Some((body_label, exit_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) {
|
let (cond_instrs, cond_var) = match self.generate_expr(while_stmt.condition) {
|
||||||
Some(res) => res,
|
Some(res) => res,
|
||||||
None => {
|
None => {
|
||||||
@@ -241,10 +184,6 @@ impl Generator {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
self.current_exit_label.pop();
|
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.extend(cond_instrs);
|
||||||
instrs.push(IRInstr::Label(body_label));
|
instrs.push(IRInstr::Label(body_label));
|
||||||
self.while_exit_label.push((cond_label, exit_label));
|
self.while_exit_label.push((cond_label, exit_label));
|
||||||
@@ -254,7 +193,7 @@ impl Generator {
|
|||||||
instrs.push(IRInstr::Label(exit_label));
|
instrs.push(IRInstr::Label(exit_label));
|
||||||
instrs
|
instrs
|
||||||
}
|
}
|
||||||
fn generate_if_stmt(&mut self, if_stmt: IfStmt) -> Vec<IRInstr> {
|
fn generate_if_stmt(&mut self, if_stmt: HirIfStmt) -> Vec<IRInstr> {
|
||||||
let mut instrs = vec![];
|
let mut instrs = vec![];
|
||||||
let then_label = self.request_label();
|
let then_label = self.request_label();
|
||||||
let exit_label = self.request_label();
|
let exit_label = self.request_label();
|
||||||
@@ -273,7 +212,6 @@ impl Generator {
|
|||||||
labels.push(exit_label);
|
labels.push(exit_label);
|
||||||
// now generate if expr, true exit to labels[0], false exit to labels[1]
|
// now generate if expr, true exit to labels[0], false exit to labels[1]
|
||||||
self.current_exit_label.push(Some((labels[0], 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) {
|
let (cond_instrs, cond_var) = match self.generate_expr(if_stmt.condition) {
|
||||||
Some(res) => res,
|
Some(res) => res,
|
||||||
None => {
|
None => {
|
||||||
@@ -282,19 +220,14 @@ impl Generator {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
self.current_exit_label.pop();
|
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.extend(cond_instrs);
|
||||||
instrs.push(IRInstr::Label(labels[0]));
|
instrs.push(IRInstr::Label(labels[0]));
|
||||||
instrs.extend(self.generate_block_stmt(if_stmt.then_branch));
|
instrs.extend(self.generate_block_stmt(if_stmt.then_branch));
|
||||||
instrs.push(IRInstr::Goto(exit_label));
|
instrs.push(IRInstr::Goto(exit_label));
|
||||||
for (i, else_if_branch) in if_stmt.ifelse_branch.into_iter().enumerate() {
|
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;
|
let HirIfElseBranch { condition: else_if_cond, then_branch: else_if_block } = else_if_branch;
|
||||||
instrs.push(IRInstr::Label(labels[i * 2 + 1]));
|
instrs.push(IRInstr::Label(labels[i * 2 + 1]));
|
||||||
self.current_exit_label.push(Some((labels[i * 2 + 2], labels[i * 2 + 3])));
|
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) {
|
let (else_if_cond_instrs, else_if_cond_var) = match self.generate_expr(else_if_cond) {
|
||||||
Some(res) => res,
|
Some(res) => res,
|
||||||
None => {
|
None => {
|
||||||
@@ -303,10 +236,6 @@ impl Generator {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
self.current_exit_label.pop();
|
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.extend(else_if_cond_instrs);
|
||||||
instrs.push(IRInstr::Label(labels[i * 2 + 2]));
|
instrs.push(IRInstr::Label(labels[i * 2 + 2]));
|
||||||
instrs.extend(self.generate_block_stmt(else_if_block));
|
instrs.extend(self.generate_block_stmt(else_if_block));
|
||||||
@@ -320,71 +249,70 @@ impl Generator {
|
|||||||
instrs.push(IRInstr::Label(exit_label));
|
instrs.push(IRInstr::Label(exit_label));
|
||||||
instrs
|
instrs
|
||||||
}
|
}
|
||||||
fn generate_continue_stmt(&mut self, stmt: ContinueStmt) -> Vec<IRInstr> {
|
fn generate_continue_stmt(&mut self, _stmt: HirContinueStmt) -> Vec<IRInstr> {
|
||||||
if let Some((continue_label, _)) = self.while_exit_label.last() {
|
if let Some((continue_label, _)) = self.while_exit_label.last() {
|
||||||
vec![IRInstr::Goto(*continue_label)]
|
vec![IRInstr::Goto(*continue_label)]
|
||||||
} else {
|
} else {
|
||||||
self.diagnostic.add_from_ir_error(IRError::ContinueOutsideLoop, stmt.span);
|
|
||||||
vec![]
|
vec![]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn generate_break_stmt(&mut self, stmt: BreakStmt) -> Vec<IRInstr> {
|
fn generate_break_stmt(&mut self, _stmt: HirBreakStmt) -> Vec<IRInstr> {
|
||||||
if let Some((_, break_label)) = self.while_exit_label.last() {
|
if let Some((_, break_label)) = self.while_exit_label.last() {
|
||||||
vec![IRInstr::Goto(*break_label)]
|
vec![IRInstr::Goto(*break_label)]
|
||||||
} else {
|
} else {
|
||||||
self.diagnostic.add_from_ir_error(IRError::BreakOutsideLoop, stmt.span);
|
|
||||||
vec![]
|
vec![]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn generate_expr(&mut self, expr: Expr) -> Option<(Vec<IRInstr>, Option<Variable>)> {
|
fn generate_expr(&mut self, expr: HirExpr) -> Option<(Vec<IRInstr>, Option<Variable>)> {
|
||||||
// there may be some expr that doesn't produce value, like void func call
|
// there may be some expr that doesn't produce value, like void func call
|
||||||
let (mut instrs, var) = match expr.value {
|
let (mut instrs, var) = match expr.value {
|
||||||
ExprValue::IntLit(i) => {
|
HirExprValue::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);
|
||||||
(vec![IRInstr::Move(var, MoveRValue::ConstInt(i as i32))], Some(var))
|
(vec![IRInstr::Move(var.clone(), MoveRValue::ConstInt(i as i32))], Some(var))
|
||||||
},
|
},
|
||||||
ExprValue::Var(name) => {
|
HirExprValue::Var(symbol) => {
|
||||||
if let Some(var) = self.var_manager.get_variable(&name) {
|
let var = self.var_manager.get_symbol(symbol).unwrap();
|
||||||
|
if var.data_type.is_array() {
|
||||||
|
let ptr_ty = var.data_type.decay_to_ptr().unwrap();
|
||||||
|
let dest = self.var_manager.declare_temp(ptr_ty);
|
||||||
|
(vec![IRInstr::Move(dest.clone(), MoveRValue::Var(var))], Some(dest))
|
||||||
|
} else {
|
||||||
(vec![], Some(var))
|
(vec![], Some(var))
|
||||||
} else {
|
|
||||||
self.diagnostic.add_from_ir_error(IRError::VariableNotFound(name.clone()), expr.span);
|
|
||||||
return None;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ExprValue::Assign { lvalue, rvalue } => {
|
HirExprValue::Assign { lvalue, rvalue } => {
|
||||||
// TODO: only support simple variable assignment now, need to support more complex lvalue in the future
|
let lvalue_ty: IRType = lvalue.ty.clone().into();
|
||||||
if let ExprValue::Var(name) = lvalue.value {
|
self.current_exit_label.push(None);
|
||||||
let var = match self.var_manager.get_variable(&name) {
|
let (mut instrs, rvalue_var) = self.generate_expr(*rvalue)?;
|
||||||
Some(var) => var,
|
self.current_exit_label.pop();
|
||||||
None => {
|
let (lvalue_instrs, target, is_addr) = self.generate_lvalue(*lvalue)?;
|
||||||
self.diagnostic.add_from_ir_error(IRError::VariableNotFound(name.clone()), lvalue.span);
|
instrs.extend(lvalue_instrs);
|
||||||
return None;
|
if is_addr {
|
||||||
}
|
instrs.push(IRInstr::Store(target.clone(), rvalue_var.unwrap()));
|
||||||
};
|
|
||||||
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() {
|
|
||||||
self.diagnostic.add_from_ir_error(IRError::InvalidOperand(IRType::Void), rvalue_span);
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
instrs.push(IRInstr::Move(var, MoveRValue::Var(rvalue_var.unwrap())));
|
|
||||||
let temp_var = self.var_manager.declare_temp(var.data_type);
|
|
||||||
instrs.push(IRInstr::Move(temp_var, MoveRValue::Var(var)));
|
|
||||||
(instrs, Some(temp_var))
|
|
||||||
} else {
|
} else {
|
||||||
self.diagnostic.add_from_ir_error(IRError::InvalidAssignmentTarget, lvalue.span);
|
instrs.push(IRInstr::Move(target.clone(), MoveRValue::Var(rvalue_var.unwrap())));
|
||||||
return None;
|
}
|
||||||
|
let temp_var = self.var_manager.declare_temp(lvalue_ty);
|
||||||
|
if is_addr {
|
||||||
|
instrs.push(IRInstr::Load(temp_var.clone(), target));
|
||||||
|
} else {
|
||||||
|
instrs.push(IRInstr::Move(temp_var.clone(), MoveRValue::Var(target)));
|
||||||
|
}
|
||||||
|
(instrs, Some(temp_var))
|
||||||
|
},
|
||||||
|
HirExprValue::ArrayAccess { array, index } => {
|
||||||
|
let result_ty: IRType = expr.ty.clone().into();
|
||||||
|
let (mut instrs, addr) = self.generate_array_element_addr(*array, *index)?;
|
||||||
|
if result_ty.is_array() {
|
||||||
|
(instrs, Some(addr))
|
||||||
|
} else {
|
||||||
|
let dest = self.var_manager.declare_temp(result_ty);
|
||||||
|
instrs.push(IRInstr::Load(dest.clone(), addr));
|
||||||
|
(instrs, Some(dest))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ExprValue::UnaryOp { op, operand } => {
|
HirExprValue::UnaryOp { op, operand } => {
|
||||||
let mut parent_is_logical = true;
|
let mut parent_is_logical = true;
|
||||||
let exit_passdown = if matches!(op, AstUnaryOp::Not) {
|
let exit_passdown = if matches!(op, AstUnaryOp::Not) {
|
||||||
Some(self.current_exit_label.last().cloned().flatten().map_or_else(|| {
|
Some(self.current_exit_label.last().cloned().flatten().map_or_else(|| {
|
||||||
@@ -409,36 +337,31 @@ impl Generator {
|
|||||||
|
|
||||||
|
|
||||||
self.current_exit_label.push(exit_passdown);
|
self.current_exit_label.push(exit_passdown);
|
||||||
let operand_span = operand.span;
|
|
||||||
let (mut instrs, operand_var) = self.generate_expr(*operand)?;
|
let (mut instrs, operand_var) = self.generate_expr(*operand)?;
|
||||||
self.current_exit_label.pop();
|
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 operand_var = operand_var.unwrap();
|
||||||
let dest_var = match op {
|
let dest_var = match op {
|
||||||
AstUnaryOp::Add => {
|
AstUnaryOp::Add => {
|
||||||
let dest_var = self.var_manager.declare_temp(operand_var.data_type);
|
let dest_var = self.var_manager.declare_temp(operand_var.data_type.clone());
|
||||||
instrs.push(IRInstr::Move(dest_var, MoveRValue::Var(operand_var)));
|
instrs.push(IRInstr::Move(dest_var.clone(), MoveRValue::Var(operand_var)));
|
||||||
dest_var
|
dest_var
|
||||||
},
|
},
|
||||||
AstUnaryOp::Sub => {
|
AstUnaryOp::Sub => {
|
||||||
let dest_var = self.var_manager.declare_temp(operand_var.data_type);
|
let dest_var = self.var_manager.declare_temp(operand_var.data_type.clone());
|
||||||
instrs.push(IRInstr::Unary(dest_var, UnaryOp::Neg, operand_var));
|
instrs.push(IRInstr::Unary(dest_var.clone(), UnaryOp::Neg, operand_var));
|
||||||
dest_var
|
dest_var
|
||||||
},
|
},
|
||||||
AstUnaryOp::Not => {
|
AstUnaryOp::Not => {
|
||||||
let dest_var = self.var_manager.declare_unamed_local(operand_var.data_type);
|
let dest_var = self.var_manager.declare_unamed_local(operand_var.data_type.clone());
|
||||||
// child will do the cmp
|
// child will do the cmp
|
||||||
if !parent_is_logical {
|
if !parent_is_logical {
|
||||||
let exit = exit_passdown.unwrap(); // (false_exit, true_exit) (consider `not`)
|
let exit = exit_passdown.unwrap(); // (false_exit, true_exit) (consider `not`)
|
||||||
let final_exit = self.request_label();
|
let final_exit = self.request_label();
|
||||||
instrs.push(IRInstr::Label(exit.1));
|
instrs.push(IRInstr::Label(exit.1));
|
||||||
instrs.push(IRInstr::Move(dest_var, MoveRValue::ConstInt(1)));
|
instrs.push(IRInstr::Move(dest_var.clone(), MoveRValue::ConstInt(1)));
|
||||||
instrs.push(IRInstr::Goto(final_exit));
|
instrs.push(IRInstr::Goto(final_exit));
|
||||||
instrs.push(IRInstr::Label(exit.0));
|
instrs.push(IRInstr::Label(exit.0));
|
||||||
instrs.push(IRInstr::Move(dest_var, MoveRValue::ConstInt(0)));
|
instrs.push(IRInstr::Move(dest_var.clone(), MoveRValue::ConstInt(0)));
|
||||||
instrs.push(IRInstr::Goto(final_exit));
|
instrs.push(IRInstr::Goto(final_exit));
|
||||||
instrs.push(IRInstr::Label(final_exit));
|
instrs.push(IRInstr::Label(final_exit));
|
||||||
|
|
||||||
@@ -449,10 +372,7 @@ impl Generator {
|
|||||||
};
|
};
|
||||||
(instrs, Some(dest_var))
|
(instrs, Some(dest_var))
|
||||||
},
|
},
|
||||||
ExprValue::BinaryOp { lhs, op, rhs } => {
|
HirExprValue::BinaryOp { lhs, op, rhs } => {
|
||||||
let lhs_span = lhs.span;
|
|
||||||
let rhs_span = rhs.span;
|
|
||||||
|
|
||||||
// +--------+-------------------------+-------------------------+-------------------------+
|
// +--------+-------------------------+-------------------------+-------------------------+
|
||||||
// | parent | (true_exit, false_exit) | (true_exit, false_exit) | (true_exit, false_exit) |
|
// | parent | (true_exit, false_exit) | (true_exit, false_exit) | (true_exit, false_exit) |
|
||||||
// | self | and(next) | or(next) | others |
|
// | self | and(next) | or(next) | others |
|
||||||
@@ -494,44 +414,30 @@ impl Generator {
|
|||||||
self.current_exit_label.push(lhs_exit_passdown);
|
self.current_exit_label.push(lhs_exit_passdown);
|
||||||
let (mut instrs, left_var) = self.generate_expr(*lhs)?;
|
let (mut instrs, left_var) = self.generate_expr(*lhs)?;
|
||||||
self.current_exit_label.pop();
|
self.current_exit_label.pop();
|
||||||
if left_var.is_none() {
|
|
||||||
self.diagnostic.add_from_ir_error(IRError::InvalidOperand(IRType::Void), lhs_span);
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let mut left_var = left_var.unwrap();
|
let mut left_var = left_var.unwrap();
|
||||||
self.current_exit_label.push(rhs_exit_passdown);
|
self.current_exit_label.push(rhs_exit_passdown);
|
||||||
let (right_instrs, right_var) = self.generate_expr(*rhs)?;
|
let (right_instrs, right_var) = self.generate_expr(*rhs)?;
|
||||||
self.current_exit_label.pop();
|
self.current_exit_label.pop();
|
||||||
if right_var.is_none() {
|
|
||||||
self.diagnostic.add_from_ir_error(IRError::InvalidOperand(IRType::Void), rhs_span);
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let mut right_var = right_var.unwrap();
|
let mut right_var = right_var.unwrap();
|
||||||
// check implicit convert
|
// check implicit convert
|
||||||
let convert_to;
|
let convert_to;
|
||||||
if op.is_logical() {
|
if op.is_logical() {
|
||||||
// we dont really care since we use cmp
|
// we dont really care since we use cmp
|
||||||
convert_to = left_var.data_type;
|
convert_to = left_var.data_type.clone();
|
||||||
} else {
|
} else {
|
||||||
if let Some(ty) = IRType::get_elevate_result(left_var.data_type, right_var.data_type) {
|
convert_to = IRType::get_elevate_result(&left_var.data_type, &right_var.data_type).unwrap();
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// do implicit convert if needed
|
// do implicit convert if needed
|
||||||
// TODO: further check
|
// TODO: further check
|
||||||
if !op.is_logical() && convert_to != left_var.data_type {
|
if !op.is_logical() && convert_to != left_var.data_type {
|
||||||
let temp_var = self.var_manager.declare_temp(convert_to);
|
let temp_var = self.var_manager.declare_temp(convert_to.clone());
|
||||||
instrs.push(IRInstr::Move(temp_var, MoveRValue::Var(left_var)));
|
instrs.push(IRInstr::Move(temp_var.clone(), MoveRValue::Var(left_var)));
|
||||||
left_var = temp_var;
|
left_var = temp_var;
|
||||||
}
|
}
|
||||||
let result_type;
|
let result_type;
|
||||||
match op {
|
match op {
|
||||||
AstBinaryOp::Add | AstBinaryOp::Sub | AstBinaryOp::Mul | AstBinaryOp::Div | AstBinaryOp::Mod => {
|
AstBinaryOp::Add | AstBinaryOp::Sub | AstBinaryOp::Mul | AstBinaryOp::Div | AstBinaryOp::Mod => {
|
||||||
result_type = left_var.data_type;
|
result_type = left_var.data_type.clone();
|
||||||
},
|
},
|
||||||
AstBinaryOp::Equal | AstBinaryOp::NotEqual | AstBinaryOp::Less | AstBinaryOp::Greater | AstBinaryOp::LessEqual | AstBinaryOp::GreaterEqual
|
AstBinaryOp::Equal | AstBinaryOp::NotEqual | AstBinaryOp::Less | AstBinaryOp::Greater | AstBinaryOp::LessEqual | AstBinaryOp::GreaterEqual
|
||||||
| AstBinaryOp::And | AstBinaryOp::Or => {
|
| AstBinaryOp::And | AstBinaryOp::Or => {
|
||||||
@@ -565,17 +471,17 @@ impl Generator {
|
|||||||
instrs.extend(right_instrs);
|
instrs.extend(right_instrs);
|
||||||
if !op.is_logical() && convert_to != right_var.data_type {
|
if !op.is_logical() && convert_to != right_var.data_type {
|
||||||
let temp_var = self.var_manager.declare_temp(convert_to);
|
let temp_var = self.var_manager.declare_temp(convert_to);
|
||||||
instrs.push(IRInstr::Move(temp_var, MoveRValue::Var(right_var)));
|
instrs.push(IRInstr::Move(temp_var.clone(), MoveRValue::Var(right_var)));
|
||||||
right_var = temp_var;
|
right_var = temp_var;
|
||||||
}
|
}
|
||||||
if !op.is_logical() {
|
if !op.is_logical() {
|
||||||
if let Some((true_exit, false_exit)) = parent_exit {
|
if let Some((true_exit, false_exit)) = parent_exit {
|
||||||
let final_exit = self.request_label();
|
let final_exit = self.request_label();
|
||||||
instrs.push(IRInstr::Label(true_exit));
|
instrs.push(IRInstr::Label(true_exit));
|
||||||
instrs.push(IRInstr::Move(dest_var, MoveRValue::ConstInt(1)));
|
instrs.push(IRInstr::Move(dest_var.clone(), MoveRValue::ConstInt(1)));
|
||||||
instrs.push(IRInstr::Goto(final_exit));
|
instrs.push(IRInstr::Goto(final_exit));
|
||||||
instrs.push(IRInstr::Label(false_exit));
|
instrs.push(IRInstr::Label(false_exit));
|
||||||
instrs.push(IRInstr::Move(dest_var, MoveRValue::ConstInt(0)));
|
instrs.push(IRInstr::Move(dest_var.clone(), MoveRValue::ConstInt(0)));
|
||||||
instrs.push(IRInstr::Label(final_exit));
|
instrs.push(IRInstr::Label(final_exit));
|
||||||
|
|
||||||
|
|
||||||
@@ -585,9 +491,9 @@ impl Generator {
|
|||||||
return Some((instrs, Some(dest_var)));
|
return Some((instrs, Some(dest_var)));
|
||||||
} else {
|
} else {
|
||||||
if op.is_cmp() {
|
if op.is_cmp() {
|
||||||
instrs.push(IRInstr::Cmp(dest_var, VariableOrIntLit::Var(left_var), op.into(), VariableOrIntLit::Var(right_var)));
|
instrs.push(IRInstr::Cmp(dest_var.clone(), VariableOrIntLit::Var(left_var), op.into(), VariableOrIntLit::Var(right_var)));
|
||||||
} else {
|
} else {
|
||||||
instrs.push(IRInstr::Binary(dest_var, left_var, op.into(), right_var));
|
instrs.push(IRInstr::Binary(dest_var.clone(), left_var, op.into(), right_var));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// if !op.is_logical() {
|
// if !op.is_logical() {
|
||||||
@@ -606,73 +512,84 @@ impl Generator {
|
|||||||
// }
|
// }
|
||||||
(instrs, Some(dest_var))
|
(instrs, Some(dest_var))
|
||||||
},
|
},
|
||||||
ExprValue::FuncCall(func_name, args) => {
|
HirExprValue::FuncCall(func_id, args) => {
|
||||||
let mut instrs = vec![];
|
let mut instrs = vec![];
|
||||||
let mut arg_vars = vec![];
|
let mut arg_vars = vec![];
|
||||||
let func_def = if let Some(func_def) = self.function_map.get(&func_name) {
|
let func_def = self.ir_function(func_id);
|
||||||
func_def
|
|
||||||
} else {
|
|
||||||
self.diagnostic.add_from_ir_error(IRError::FunctionNotFound(func_name.clone()), expr.span);
|
|
||||||
return None;
|
|
||||||
}.clone();
|
|
||||||
|
|
||||||
if args.len() < func_def.parameter_types.len() {
|
for arg in args.into_iter() {
|
||||||
self.diagnostic.add_from_ir_error(IRError::TooFewArguments(func_def.parameter_types.len(), args.len()), expr.span);
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let mut has_error = false;
|
|
||||||
for parameter_type in &func_def.parameter_types {
|
|
||||||
if matches!(parameter_type, IRType::Void) {
|
|
||||||
self.diagnostic.add_from_ir_error(IRError::InvalidParameterType(IRType::Void), expr.span);
|
|
||||||
has_error = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if has_error {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
for (i, arg) in args.into_iter().enumerate() {
|
|
||||||
self.current_exit_label.push(None);
|
self.current_exit_label.push(None);
|
||||||
let (arg_instrs, arg_var) = self.generate_expr(arg)?;
|
let (arg_instrs, arg_var) = self.generate_expr(arg)?;
|
||||||
self.current_exit_label.pop();
|
self.current_exit_label.pop();
|
||||||
let parameter_type = func_def.parameter_types.get(i).unwrap();
|
|
||||||
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.map_or(IRType::Void, |v| v.data_type)), expr.span);
|
|
||||||
has_error = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
instrs.extend(arg_instrs);
|
instrs.extend(arg_instrs);
|
||||||
arg_vars.push(arg_var.unwrap());
|
arg_vars.push(arg_var.unwrap());
|
||||||
}
|
}
|
||||||
if has_error {
|
|
||||||
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
|
||||||
} else {
|
} else {
|
||||||
Some(self.var_manager.declare_temp(func_def.return_type))
|
Some(self.var_manager.declare_temp(func_def.return_type.clone()))
|
||||||
};
|
};
|
||||||
instrs.push(IRInstr::FuncCall(func_def.clone(), arg_vars, ret_variable));
|
instrs.push(IRInstr::FuncCall(func_def.clone(), arg_vars, ret_variable.clone()));
|
||||||
(instrs, ret_variable)
|
(instrs, ret_variable)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if let Some((true_exit, false_exit)) = self.current_exit_label.last().cloned().flatten() {
|
if let Some((true_exit, false_exit)) = self.current_exit_label.last().cloned().flatten() {
|
||||||
let cmp_var = self.var_manager.declare_temp(IRType::I1);
|
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::Cmp(cmp_var.clone(), VariableOrIntLit::Var(var.clone().unwrap()), CmpOp::Ne, VariableOrIntLit::IntLit(0)));
|
||||||
instrs.push(IRInstr::CondGoto(cmp_var, true_exit, false_exit));
|
instrs.push(IRInstr::CondGoto(cmp_var, true_exit, false_exit));
|
||||||
Some((instrs, var))
|
Some((instrs, var))
|
||||||
} else {
|
} else {
|
||||||
Some((instrs, var))
|
Some((instrs, var))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn generate_lvalue(&mut self, expr: HirExpr) -> Option<(Vec<IRInstr>, Variable, bool)> {
|
||||||
|
match expr.value {
|
||||||
|
HirExprValue::Var(symbol) => {
|
||||||
|
let var = self.var_manager.get_symbol(symbol).unwrap();
|
||||||
|
Some((vec![], var, false))
|
||||||
|
}
|
||||||
|
HirExprValue::ArrayAccess { array, index } => {
|
||||||
|
let (instrs, addr) = self.generate_array_element_addr(*array, *index)?;
|
||||||
|
Some((instrs, addr, true))
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_array_element_addr(&mut self, array: HirExpr, index: HirExpr) -> Option<(Vec<IRInstr>, Variable)> {
|
||||||
|
let elem_ty = array.ty.indexed_type().unwrap();
|
||||||
|
let elem_size = elem_ty.element_size_in_bytes();
|
||||||
|
let (mut instrs, base) = match array.value {
|
||||||
|
HirExprValue::Var(symbol) => {
|
||||||
|
let var = self.var_manager.get_symbol(symbol).unwrap();
|
||||||
|
if var.data_type.is_array() {
|
||||||
|
let ptr = self.var_manager.declare_temp(var.data_type.decay_to_ptr().unwrap());
|
||||||
|
(vec![IRInstr::Move(ptr.clone(), MoveRValue::Var(var))], ptr)
|
||||||
|
} else {
|
||||||
|
(vec![], var)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HirExprValue::ArrayAccess { array, index } => self.generate_array_element_addr(*array, *index)?,
|
||||||
|
_ => self.generate_expr(array).map(|(instrs, var)| (instrs, var.unwrap()))?,
|
||||||
|
};
|
||||||
|
self.current_exit_label.push(None);
|
||||||
|
let (index_instrs, index_var) = self.generate_expr(index)?;
|
||||||
|
self.current_exit_label.pop();
|
||||||
|
instrs.extend(index_instrs);
|
||||||
|
let addr_ty = IRType::Ptr(Box::new(elem_ty.into()));
|
||||||
|
let dest = self.var_manager.declare_temp(addr_ty);
|
||||||
|
let elem_size_var = self.var_manager.declare_temp(IRType::I32);
|
||||||
|
let offset = self.var_manager.declare_temp(IRType::I32);
|
||||||
|
instrs.push(IRInstr::Move(elem_size_var.clone(), MoveRValue::ConstInt(elem_size as i32)));
|
||||||
|
instrs.push(IRInstr::Binary(offset.clone(), index_var.unwrap(), AstBinaryOp::Mul.into(), elem_size_var));
|
||||||
|
instrs.push(IRInstr::Binary(dest.clone(), base, AstBinaryOp::Add.into(), offset));
|
||||||
|
Some((instrs, dest))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct VariableManager {
|
struct VariableManager {
|
||||||
variable_map: BTreeMap<String, Vec<Variable>>,
|
variable_map: BTreeMap<SymbolId, Variable>,
|
||||||
scopes: Vec<BTreeSet<String>>,
|
|
||||||
global_counter: usize,
|
global_counter: usize,
|
||||||
local_counter: usize,
|
local_counter: usize,
|
||||||
local_var_type: Vec<Variable>,
|
local_var_type: Vec<Variable>,
|
||||||
@@ -682,27 +599,12 @@ impl VariableManager {
|
|||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
variable_map: BTreeMap::new(),
|
variable_map: BTreeMap::new(),
|
||||||
scopes: vec![BTreeSet::new()],
|
|
||||||
global_counter: 0,
|
global_counter: 0,
|
||||||
local_counter: 0,
|
local_counter: 0,
|
||||||
local_var_type: vec![],
|
local_var_type: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn enter_scope(&mut self) {
|
fn declare_symbol(&mut self, symbol: SymbolId, var_type: VariableType, var_data_type: IRType) -> Variable {
|
||||||
self.scopes.push(BTreeSet::new());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn exit_scope(&mut self) {
|
|
||||||
let variables = self.scopes.pop().unwrap();
|
|
||||||
for var in variables {
|
|
||||||
self.variable_map.get_mut(&var).unwrap().pop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn declare_variable(&mut self, name: &str, var_type: VariableType, var_data_type: IRType) -> Result<Variable, IRError> {
|
|
||||||
if self.scopes.last().unwrap().contains(name) {
|
|
||||||
return Err(IRError::VariableHasBeenDefined(name.to_string()));
|
|
||||||
}
|
|
||||||
let variable = match var_type {
|
let variable = match var_type {
|
||||||
VariableType::Global => {
|
VariableType::Global => {
|
||||||
let var = Variable { index: self.global_counter, var_type, data_type: var_data_type };
|
let var = Variable { index: self.global_counter, var_type, data_type: var_data_type };
|
||||||
@@ -716,46 +618,40 @@ impl VariableManager {
|
|||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
self.variable_map.entry(name.to_string()).or_default().push(variable);
|
self.variable_map.insert(symbol, variable.clone());
|
||||||
self.scopes.last_mut().unwrap().insert(name.to_string());
|
|
||||||
if matches!(var_type, VariableType::Local) {
|
if matches!(var_type, VariableType::Local) {
|
||||||
self.local_var_type.push(variable);
|
self.local_var_type.push(variable.clone());
|
||||||
}
|
}
|
||||||
Ok(variable)
|
variable
|
||||||
}
|
|
||||||
pub fn declare_gloabal(&mut self, name: &str, var_data_type: IRType) -> Result<Variable, IRError> {
|
|
||||||
self.declare_variable(name, VariableType::Global, var_data_type)
|
|
||||||
}
|
|
||||||
pub fn declare_local(&mut self, name: &str, var_data_type: IRType) -> Result<Variable, IRError> {
|
|
||||||
self.declare_variable(name, VariableType::Local, var_data_type)
|
|
||||||
}
|
}
|
||||||
pub fn declare_temp(&mut self, var_data_type: IRType) -> Variable {
|
pub fn declare_temp(&mut self, var_data_type: IRType) -> Variable {
|
||||||
let var = Variable { index: self.local_counter, var_type: VariableType::Temp, data_type: var_data_type };
|
let var = Variable { index: self.local_counter, var_type: VariableType::Temp, data_type: var_data_type };
|
||||||
self.local_counter += 1;
|
self.local_counter += 1;
|
||||||
self.local_var_type.push(var);
|
self.local_var_type.push(var.clone());
|
||||||
var
|
var
|
||||||
}
|
}
|
||||||
pub fn declare_unamed_local(&mut self, var_data_type: IRType) -> Variable {
|
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 };
|
let var = Variable { index: self.local_counter, var_type: VariableType::Local, data_type: var_data_type };
|
||||||
self.local_counter += 1;
|
self.local_counter += 1;
|
||||||
self.local_var_type.push(var);
|
self.local_var_type.push(var.clone());
|
||||||
var
|
var
|
||||||
}
|
}
|
||||||
pub fn declare_param_temp(&mut self, var_data_type: IRType, param_index: usize) -> Variable {
|
pub fn declare_param_temp(&mut self, var_data_type: IRType, param_index: usize) -> Variable {
|
||||||
let var = Variable { index: self.local_counter, var_type: VariableType::ParamTemp(param_index), data_type: var_data_type };
|
let var = Variable { index: self.local_counter, var_type: VariableType::ParamTemp(param_index), data_type: var_data_type };
|
||||||
self.local_counter += 1;
|
self.local_counter += 1;
|
||||||
self.local_var_type.push(var);
|
self.local_var_type.push(var.clone());
|
||||||
var
|
var
|
||||||
}
|
}
|
||||||
pub fn clear_local_counter(&mut self) {
|
pub fn clear_local_counter(&mut self) {
|
||||||
self.local_counter = 0;
|
self.local_counter = 0;
|
||||||
self.local_var_type.clear();
|
self.local_var_type.clear();
|
||||||
|
self.variable_map.retain(|_, var| matches!(var.var_type, VariableType::Global));
|
||||||
}
|
}
|
||||||
pub fn get_cur_func_variables(&self) -> Vec<Variable> {
|
pub fn get_cur_func_variables(&self) -> Vec<Variable> {
|
||||||
self.local_var_type.iter().cloned().collect()
|
self.local_var_type.iter().cloned().collect()
|
||||||
}
|
}
|
||||||
pub fn get_variable(&self, name: &str) -> Option<Variable> {
|
pub fn get_symbol(&self, symbol: SymbolId) -> Option<Variable> {
|
||||||
self.variable_map.get(name).and_then(|vars| vars.last()).cloned()
|
self.variable_map.get(&symbol).cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -769,6 +665,7 @@ mod tests {
|
|||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use crate::frontend::lexer::Lexer;
|
use crate::frontend::lexer::Lexer;
|
||||||
use crate::frontend::parser::Parser;
|
use crate::frontend::parser::Parser;
|
||||||
|
use crate::sema::analyzer::Analyzer;
|
||||||
use crate::utils::case_list::CaseList;
|
use crate::utils::case_list::CaseList;
|
||||||
use crate::utils::num_sequence::NumberSequence;
|
use crate::utils::num_sequence::NumberSequence;
|
||||||
|
|
||||||
@@ -806,12 +703,14 @@ mod tests {
|
|||||||
parser.diagnostics.print(&format!("{}", case_path.display()), &full_text);
|
parser.diagnostics.print(&format!("{}", case_path.display()), &full_text);
|
||||||
is_error = true;
|
is_error = true;
|
||||||
}
|
}
|
||||||
let mut generator = Generator::new();
|
let mut analyzer = Analyzer::new();
|
||||||
let ir = generator.emit(compile_unit);
|
let hir = analyzer.analyze(compile_unit);
|
||||||
if !generator.diagnostic.is_empty() {
|
if !analyzer.get_diagnostics().is_empty() {
|
||||||
generator.diagnostic.print(&format!("{}", case_path.display()), &full_text);
|
analyzer.get_diagnostics().print(&format!("{}", case_path.display()), &full_text);
|
||||||
is_error = true;
|
is_error = true;
|
||||||
}
|
}
|
||||||
|
let mut generator = Generator::new(&analyzer);
|
||||||
|
let ir = generator.emit(hir);
|
||||||
let mut ir_file = File::create(format!("output/{}.ir", case_name)).unwrap();
|
let mut ir_file = File::create(format!("output/{}.ir", case_name)).unwrap();
|
||||||
for instr in ir {
|
for instr in ir {
|
||||||
writeln!(ir_file, "{}", instr).unwrap();
|
writeln!(ir_file, "{}", instr).unwrap();
|
||||||
@@ -838,4 +737,8 @@ mod tests {
|
|||||||
fn test_func() {
|
fn test_func() {
|
||||||
test_case("12-13,58-60");
|
test_case("12-13,58-60");
|
||||||
}
|
}
|
||||||
}
|
#[test]
|
||||||
|
fn test_array() {
|
||||||
|
test_case("4-7,11,42,71,74-78,82,84,86,90,95,96,100,101,103");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+89
-8
@@ -30,6 +30,8 @@ pub enum IRInstr {
|
|||||||
CondGoto(Variable, usize, usize), // condition, true label, false label
|
CondGoto(Variable, usize, usize), // condition, true label, false label
|
||||||
Label(usize),
|
Label(usize),
|
||||||
Move(Variable, MoveRValue),
|
Move(Variable, MoveRValue),
|
||||||
|
Load(Variable, Variable),
|
||||||
|
Store(Variable, Variable),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for IRInstr {
|
impl Display for IRInstr {
|
||||||
@@ -47,7 +49,9 @@ impl Display for IRInstr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
IRInstr::Move(dest, src) => write!(f, "{} = {}", dest, src),
|
IRInstr::Move(dest, src) => write!(f, "{} = {}", dest, src),
|
||||||
IRInstr::Declare(var) => write!(f, "declare {} {}", var.data_type, var),
|
IRInstr::Load(dest, addr) => write!(f, "{} = *{}", dest, addr),
|
||||||
|
IRInstr::Store(addr, value) => write!(f, "*{} = {}", addr, value),
|
||||||
|
IRInstr::Declare(var) => write!(f, "declare {}", var.to_decl_string()),
|
||||||
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)
|
||||||
@@ -59,11 +63,13 @@ impl Display for IRInstr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub enum IRType {
|
pub enum IRType {
|
||||||
I32,
|
I32,
|
||||||
I1,
|
I1,
|
||||||
Void,
|
Void,
|
||||||
|
Ptr(Box<IRType>),
|
||||||
|
Array(Box<IRType>, Vec<usize>),
|
||||||
}
|
}
|
||||||
impl IRType {
|
impl IRType {
|
||||||
pub fn size_in_bytes(&self) -> usize {
|
pub fn size_in_bytes(&self) -> usize {
|
||||||
@@ -71,13 +77,47 @@ impl IRType {
|
|||||||
IRType::I32 => 4,
|
IRType::I32 => 4,
|
||||||
IRType::I1 => 1,
|
IRType::I1 => 1,
|
||||||
IRType::Void => 0,
|
IRType::Void => 0,
|
||||||
|
IRType::Ptr(_) => 4,
|
||||||
|
IRType::Array(elem, dims) => elem.size_in_bytes() * dims.iter().product::<usize>(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_elevate_result(lhs: IRType, rhs: IRType) -> Option<IRType> {
|
pub fn is_array(&self) -> bool {
|
||||||
|
matches!(self, IRType::Array(_, _))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decay_to_ptr(&self) -> Option<IRType> {
|
||||||
|
match self {
|
||||||
|
IRType::Array(elem, dims) => {
|
||||||
|
let pointee = if dims.len() == 1 {
|
||||||
|
(**elem).clone()
|
||||||
|
} else {
|
||||||
|
IRType::Array(elem.clone(), dims[1..].to_vec())
|
||||||
|
};
|
||||||
|
Some(IRType::Ptr(Box::new(pointee)))
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn element_type(&self) -> Option<IRType> {
|
||||||
|
match self {
|
||||||
|
IRType::Array(elem, dims) => {
|
||||||
|
if dims.len() == 1 {
|
||||||
|
Some((**elem).clone())
|
||||||
|
} else {
|
||||||
|
Some(IRType::Array(elem.clone(), dims[1..].to_vec()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
IRType::Ptr(elem) => Some((**elem).clone()),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_elevate_result(lhs: &IRType, rhs: &IRType) -> Option<IRType> {
|
||||||
if lhs == rhs {
|
if lhs == rhs {
|
||||||
Some(lhs)
|
Some(lhs.clone())
|
||||||
} else if (lhs == IRType::I32 && rhs == IRType::I1) || (lhs == IRType::I1 && rhs == IRType::I32) {
|
} else if (*lhs == IRType::I32 && *rhs == IRType::I1) || (*lhs == IRType::I1 && *rhs == IRType::I32) {
|
||||||
Some(IRType::I32)
|
Some(IRType::I32)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@@ -90,6 +130,30 @@ impl Display for IRType {
|
|||||||
IRType::I32 => write!(f, "i32"),
|
IRType::I32 => write!(f, "i32"),
|
||||||
IRType::Void => write!(f, "void"),
|
IRType::Void => write!(f, "void"),
|
||||||
IRType::I1 => write!(f, "i1"),
|
IRType::I1 => write!(f, "i1"),
|
||||||
|
IRType::Ptr(elem) => write!(f, "{}*", elem),
|
||||||
|
IRType::Array(elem, dims) => {
|
||||||
|
write!(f, "{}", elem)?;
|
||||||
|
for dim in dims {
|
||||||
|
write!(f, "[{}]", dim)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl IRType {
|
||||||
|
pub fn base_type_and_dims(&self) -> (&IRType, &[usize]) {
|
||||||
|
match self {
|
||||||
|
IRType::Array(elem, dims) => (elem.as_ref(), dims.as_slice()),
|
||||||
|
_ => (self, &[]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn scalar_base_type(&self) -> &IRType {
|
||||||
|
match self {
|
||||||
|
IRType::Array(elem, _) => elem.scalar_base_type(),
|
||||||
|
IRType::Ptr(elem) => elem.scalar_base_type(),
|
||||||
|
_ => self,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -120,7 +184,7 @@ pub enum VariableType {
|
|||||||
Local,
|
Local,
|
||||||
Temp,
|
Temp,
|
||||||
}
|
}
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone)]
|
||||||
pub struct Variable {
|
pub struct Variable {
|
||||||
// pub name: String,
|
// pub name: String,
|
||||||
pub index: usize,
|
pub index: usize,
|
||||||
@@ -160,6 +224,19 @@ impl Display for Variable {
|
|||||||
write!(f, "{}{}", prefix, self.index)
|
write!(f, "{}{}", prefix, self.index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl Variable {
|
||||||
|
pub fn to_decl_string(&self) -> String {
|
||||||
|
match &self.data_type {
|
||||||
|
IRType::Array(_, _) => {
|
||||||
|
let (base_type, dims) = self.data_type.base_type_and_dims();
|
||||||
|
let dims_str = dims.iter().map(|dim| format!("[{}]", dim)).collect::<Vec<_>>().join("");
|
||||||
|
format!("{} {}{}", base_type, self, dims_str)
|
||||||
|
}
|
||||||
|
IRType::Ptr(_) => format!("{}* {}", self.data_type.scalar_base_type(), self),
|
||||||
|
_ => format!("{} {}", self.data_type, self),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Function {
|
pub struct Function {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
@@ -175,7 +252,11 @@ impl Function {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_decl_string(&self, args: &Vec<Variable>) -> String {
|
pub fn to_decl_string(&self, args: &Vec<Variable>) -> String {
|
||||||
let params_str = args.iter().zip(self.parameter_types.iter()).map(|(arg, param_type)| format!("{} {}", param_type, arg)).collect::<Vec<_>>().join(", ");
|
let params_str = args.iter().zip(self.parameter_types.iter()).map(|(arg, param_type)| {
|
||||||
|
let (base_type, dims) = param_type.base_type_and_dims();
|
||||||
|
let dims_str = dims.iter().map(|dim| format!("[{}]", dim)).collect::<Vec<_>>().join("");
|
||||||
|
format!("{} {}{}", base_type, arg, dims_str)
|
||||||
|
}).collect::<Vec<_>>().join(", ");
|
||||||
format!("{} @{}({})", self.return_type, self.name, params_str)
|
format!("{} @{}({})", self.return_type, self.name, params_str)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -269,4 +350,4 @@ impl From<AstBinaryOp> for CmpOp {
|
|||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-5
@@ -5,12 +5,13 @@ mod backend;
|
|||||||
mod utils;
|
mod utils;
|
||||||
mod diagnostic;
|
mod diagnostic;
|
||||||
mod err;
|
mod err;
|
||||||
|
mod sema;
|
||||||
|
|
||||||
use std::{fs::File, io::BufRead};
|
use std::{fs::File, io::BufRead};
|
||||||
|
|
||||||
use clap::Parser as ArgParser;
|
use clap::Parser as ArgParser;
|
||||||
|
|
||||||
use crate::{frontend::{lexer::Lexer, parser::Parser}, ir::generator::Generator};
|
use crate::{frontend::{lexer::Lexer, parser::Parser}, ir::generator::Generator, sema::analyzer::Analyzer};
|
||||||
use crate::backend::generator::Generator as ASMGerenerator;
|
use crate::backend::generator::Generator as ASMGerenerator;
|
||||||
/// Simple minic compiler built by Rust
|
/// Simple minic compiler built by Rust
|
||||||
#[derive(ArgParser, Debug)]
|
#[derive(ArgParser, Debug)]
|
||||||
@@ -78,11 +79,13 @@ fn main() {
|
|||||||
if !parser.diagnostics.is_empty() {
|
if !parser.diagnostics.is_empty() {
|
||||||
parser.diagnostics.print(&format!("{}", source_path.display()), &full_text);
|
parser.diagnostics.print(&format!("{}", source_path.display()), &full_text);
|
||||||
}
|
}
|
||||||
let mut generator = Generator::new();
|
let mut analyzer = Analyzer::new();
|
||||||
let ir = generator.emit(compile_unit);
|
let hir = analyzer.analyze(compile_unit);
|
||||||
if !generator.get_diagnostics().is_empty() {
|
if !analyzer.get_diagnostics().is_empty() {
|
||||||
generator.get_diagnostics().print(&format!("{}", source_path.display()), &full_text);
|
analyzer.get_diagnostics().print(&format!("{}", source_path.display()), &full_text);
|
||||||
}
|
}
|
||||||
|
let mut generator = Generator::new(&analyzer);
|
||||||
|
let ir = generator.emit(hir);
|
||||||
if args.output_ir {
|
if args.output_ir {
|
||||||
if let Some(output_path) = args.output {
|
if let Some(output_path) = args.output {
|
||||||
match std::fs::write(&output_path, ir.iter().map(|instr| instr.to_string()).collect::<Vec<_>>().join("\n")) {
|
match std::fs::write(&output_path, ir.iter().map(|instr| instr.to_string()).collect::<Vec<_>>().join("\n")) {
|
||||||
|
|||||||
@@ -0,0 +1,529 @@
|
|||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
ast::types::{
|
||||||
|
ArrayDimension, BinaryOp, BlockStmt, BreakStmt, CompileUnit, ContinueStmt, Expr, ExprValue, FuncDeclStmt,
|
||||||
|
GlobalDeclStmt, IfElseBranch, IfStmt, ReturnStmt, Statement, VarDeclStmt, WhileStmt,
|
||||||
|
},
|
||||||
|
diagnostic::{span::Span, Diagnositics},
|
||||||
|
sema::{
|
||||||
|
err::SemaError,
|
||||||
|
hir::{
|
||||||
|
HirBlockStmt, HirBreakStmt, HirCompileUnit, HirContinueStmt, HirExpr, HirExprValue,
|
||||||
|
HirFuncDeclStmt, HirGlobalDeclStmt, HirIfElseBranch, HirIfStmt, HirParam,
|
||||||
|
HirReturnStmt, HirStatement, HirVarDeclStmt, HirVarDeclStmtValue, HirWhileStmt,
|
||||||
|
},
|
||||||
|
symbol::{FunctionId, FunctionSig, SymbolId, SymbolKind, SymbolTable},
|
||||||
|
types::SemaType,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct Analyzer {
|
||||||
|
symbols: SymbolTable,
|
||||||
|
function_map: BTreeMap<String, FunctionId>,
|
||||||
|
functions: Vec<FunctionSig>,
|
||||||
|
current_func_return_type: Option<SemaType>,
|
||||||
|
diagnostic: Diagnositics,
|
||||||
|
loop_depth: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Analyzer {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let mut analyzer = Self {
|
||||||
|
symbols: SymbolTable::new(),
|
||||||
|
function_map: BTreeMap::new(),
|
||||||
|
functions: vec![],
|
||||||
|
current_func_return_type: None,
|
||||||
|
diagnostic: Diagnositics::new(),
|
||||||
|
loop_depth: 0,
|
||||||
|
};
|
||||||
|
analyzer.declare_builtin_func("putint", vec![SemaType::I32], SemaType::Void);
|
||||||
|
analyzer.declare_builtin_func("putch", vec![SemaType::I32], SemaType::Void);
|
||||||
|
analyzer.declare_builtin_func("getint", vec![], SemaType::I32);
|
||||||
|
analyzer
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn analyze(&mut self, compile_unit: CompileUnit) -> HirCompileUnit {
|
||||||
|
self.analyze_compile_unit(compile_unit)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_diagnostics(&self) -> &Diagnositics {
|
||||||
|
&self.diagnostic
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_symbol_type(&self, symbol: SymbolId) -> SemaType {
|
||||||
|
self.symbols.get_symbol(symbol).ty.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_symbol_kind(&self, symbol: SymbolId) -> SymbolKind {
|
||||||
|
self.symbols.get_symbol(symbol).kind
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_function_sig(&self, function: FunctionId) -> &FunctionSig {
|
||||||
|
&self.functions[function.0]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn declare_builtin_func(&mut self, name: &str, parameter_types: Vec<SemaType>, return_type: SemaType) {
|
||||||
|
let id = FunctionId(self.functions.len());
|
||||||
|
let sig = FunctionSig {
|
||||||
|
id,
|
||||||
|
name: name.to_string(),
|
||||||
|
return_type,
|
||||||
|
parameter_types,
|
||||||
|
};
|
||||||
|
self.function_map.insert(name.to_string(), id);
|
||||||
|
self.functions.push(sig);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_error(&mut self, error: SemaError, span: Span) {
|
||||||
|
self.diagnostic.add_from_error(error, span);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn analyze_compile_unit(&mut self, compile_unit: CompileUnit) -> HirCompileUnit {
|
||||||
|
let mut global_decls = vec![];
|
||||||
|
for decl in compile_unit.global_decls {
|
||||||
|
match decl {
|
||||||
|
GlobalDeclStmt::VarDecl(var_decl) => {
|
||||||
|
global_decls.push(HirGlobalDeclStmt::VarDecl(self.analyze_var_decl(var_decl, SymbolKind::Global)));
|
||||||
|
}
|
||||||
|
GlobalDeclStmt::FuncDecl(func_decl) => {
|
||||||
|
if let Some(func_decl) = self.analyze_func_decl(func_decl) {
|
||||||
|
global_decls.push(HirGlobalDeclStmt::FuncDecl(func_decl));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HirCompileUnit { global_decls }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn analyze_var_decl(&mut self, var_decl: VarDeclStmt, kind: SymbolKind) -> HirVarDeclStmt {
|
||||||
|
let base_type: SemaType = var_decl.data_type.into();
|
||||||
|
let mut values = vec![];
|
||||||
|
for value in var_decl.values {
|
||||||
|
let data_type = self.build_var_type(base_type.clone(), &value.dimensions, false);
|
||||||
|
match self.symbols.declare_variable(&value.name, kind, data_type) {
|
||||||
|
Ok(symbol) => values.push(HirVarDeclStmtValue {
|
||||||
|
symbol,
|
||||||
|
name_span: value.name_span,
|
||||||
|
}),
|
||||||
|
Err(e) => self.add_error(e, value.name_span),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HirVarDeclStmt {
|
||||||
|
values,
|
||||||
|
data_type: base_type,
|
||||||
|
type_span: var_decl.type_span,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn analyze_func_decl(&mut self, func_decl: FuncDeclStmt) -> Option<HirFuncDeclStmt> {
|
||||||
|
if self.function_map.contains_key(&func_decl.name) {
|
||||||
|
self.add_error(SemaError::FunctionHasBeenDefined(func_decl.name.clone()), func_decl.name_span);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let function_id = FunctionId(self.functions.len());
|
||||||
|
let mut parameter_types = vec![];
|
||||||
|
for param in &func_decl.params {
|
||||||
|
parameter_types.push(self.build_var_type(param.param_type.into(), ¶m.dimensions, true));
|
||||||
|
}
|
||||||
|
let return_type: SemaType = func_decl.return_type.into();
|
||||||
|
let sig = FunctionSig {
|
||||||
|
id: function_id,
|
||||||
|
name: func_decl.name.clone(),
|
||||||
|
return_type: return_type.clone(),
|
||||||
|
parameter_types,
|
||||||
|
};
|
||||||
|
self.function_map.insert(func_decl.name.clone(), function_id);
|
||||||
|
self.functions.push(sig.clone());
|
||||||
|
|
||||||
|
self.current_func_return_type = Some(return_type);
|
||||||
|
self.symbols.enter_scope();
|
||||||
|
|
||||||
|
let mut params = vec![];
|
||||||
|
for param in func_decl.params {
|
||||||
|
let param_type = self.build_var_type(param.param_type.into(), ¶m.dimensions, true);
|
||||||
|
match self.symbols.declare_variable(¶m.name, SymbolKind::Param, param_type.clone()) {
|
||||||
|
Ok(symbol) => params.push(HirParam {
|
||||||
|
symbol,
|
||||||
|
param_type,
|
||||||
|
name_span: param.name_span,
|
||||||
|
type_span: param.type_span,
|
||||||
|
}),
|
||||||
|
Err(e) => self.add_error(e, param.name_span),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let body = self.analyze_block_stmt(func_decl.body);
|
||||||
|
self.symbols.exit_scope();
|
||||||
|
self.current_func_return_type = None;
|
||||||
|
|
||||||
|
Some(HirFuncDeclStmt {
|
||||||
|
sig,
|
||||||
|
params,
|
||||||
|
body,
|
||||||
|
ret_type_span: func_decl.ret_type_span,
|
||||||
|
name_span: func_decl.name_span,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_var_type(&mut self, base_type: SemaType, dimensions: &[ArrayDimension], is_param: bool) -> SemaType {
|
||||||
|
if dimensions.is_empty() {
|
||||||
|
return base_type;
|
||||||
|
}
|
||||||
|
let mut dims = vec![];
|
||||||
|
for (i, dimension) in dimensions.iter().enumerate() {
|
||||||
|
match &dimension.value {
|
||||||
|
Some(expr) => {
|
||||||
|
if let ExprValue::IntLit(value) = expr.value {
|
||||||
|
if value > 0 {
|
||||||
|
dims.push(value as usize);
|
||||||
|
} else {
|
||||||
|
self.add_error(SemaError::InvalidArrayDimension, dimension.span);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.add_error(SemaError::InvalidArrayDimension, dimension.span);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None if is_param && i == 0 => {}
|
||||||
|
None => self.add_error(SemaError::InvalidArrayDimension, dimension.span),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if is_param {
|
||||||
|
let pointee_type = if dims.is_empty() {
|
||||||
|
base_type
|
||||||
|
} else {
|
||||||
|
SemaType::Array(Box::new(base_type), dims)
|
||||||
|
};
|
||||||
|
SemaType::Ptr(Box::new(pointee_type))
|
||||||
|
} else {
|
||||||
|
SemaType::Array(Box::new(base_type), dims)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn analyze_block_stmt(&mut self, block_stmt: BlockStmt) -> HirBlockStmt {
|
||||||
|
let mut statements = vec![];
|
||||||
|
for stmt in block_stmt.statements {
|
||||||
|
if let Some(stmt) = self.analyze_statement(stmt) {
|
||||||
|
statements.push(stmt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HirBlockStmt { statements }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn analyze_statement(&mut self, stmt: Statement) -> Option<HirStatement> {
|
||||||
|
match stmt {
|
||||||
|
Statement::Return(stmt) => Some(HirStatement::Return(self.analyze_return_stmt(stmt))),
|
||||||
|
Statement::If(stmt) => Some(HirStatement::If(self.analyze_if_stmt(stmt))),
|
||||||
|
Statement::While(stmt) => Some(HirStatement::While(self.analyze_while_stmt(stmt))),
|
||||||
|
Statement::Break(stmt) => Some(HirStatement::Break(self.analyze_break_stmt(stmt))),
|
||||||
|
Statement::Continue(stmt) => Some(HirStatement::Continue(self.analyze_continue_stmt(stmt))),
|
||||||
|
Statement::Block(stmt) => {
|
||||||
|
self.symbols.enter_scope();
|
||||||
|
let block = self.analyze_block_stmt(stmt);
|
||||||
|
self.symbols.exit_scope();
|
||||||
|
Some(HirStatement::Block(block))
|
||||||
|
}
|
||||||
|
Statement::Expr(expr) => self.analyze_expr(expr).map(HirStatement::Expr),
|
||||||
|
Statement::VarDecl(var_decl) => Some(HirStatement::VarDecl(self.analyze_var_decl(var_decl, SymbolKind::Local))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn analyze_return_stmt(&mut self, return_stmt: ReturnStmt) -> HirReturnStmt {
|
||||||
|
let expected_ty = self.current_func_return_type.clone().unwrap();
|
||||||
|
let value = match return_stmt.value {
|
||||||
|
Some(expr) => {
|
||||||
|
if expected_ty == SemaType::Void {
|
||||||
|
self.add_error(SemaError::ReturnExpressionOnVoidFunction, return_stmt.span);
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
match self.analyze_expr(expr) {
|
||||||
|
Some(expr) => {
|
||||||
|
if expr.ty == SemaType::Void {
|
||||||
|
self.add_error(SemaError::InvalidOperand(SemaType::Void), return_stmt.span);
|
||||||
|
} else if !self.type_matches(&expected_ty, &expr.ty) {
|
||||||
|
self.add_error(SemaError::TypeMismatch(expected_ty.clone(), expr.ty.clone()), return_stmt.span);
|
||||||
|
}
|
||||||
|
Some(expr)
|
||||||
|
}
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
if expected_ty != SemaType::Void {
|
||||||
|
self.add_error(SemaError::TypeMismatch(expected_ty, SemaType::Void), return_stmt.span);
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
};
|
||||||
|
HirReturnStmt {
|
||||||
|
value,
|
||||||
|
span: return_stmt.span,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn analyze_if_stmt(&mut self, if_stmt: IfStmt) -> HirIfStmt {
|
||||||
|
let condition = self.analyze_condition_expr(if_stmt.condition);
|
||||||
|
let then_branch = self.analyze_block_stmt(if_stmt.then_branch);
|
||||||
|
let mut ifelse_branch = vec![];
|
||||||
|
for branch in if_stmt.ifelse_branch {
|
||||||
|
let IfElseBranch { condition, then_branch } = branch;
|
||||||
|
ifelse_branch.push(HirIfElseBranch {
|
||||||
|
condition: self.analyze_condition_expr(condition),
|
||||||
|
then_branch: self.analyze_block_stmt(then_branch),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let else_branch = if_stmt.else_branch.map(|block| self.analyze_block_stmt(block));
|
||||||
|
HirIfStmt {
|
||||||
|
condition,
|
||||||
|
then_branch,
|
||||||
|
ifelse_branch,
|
||||||
|
else_branch,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn analyze_while_stmt(&mut self, while_stmt: WhileStmt) -> HirWhileStmt {
|
||||||
|
let condition = self.analyze_condition_expr(while_stmt.condition);
|
||||||
|
self.loop_depth += 1;
|
||||||
|
let body = self.analyze_block_stmt(while_stmt.body);
|
||||||
|
self.loop_depth -= 1;
|
||||||
|
HirWhileStmt {
|
||||||
|
condition,
|
||||||
|
body,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn analyze_break_stmt(&mut self, stmt: BreakStmt) -> HirBreakStmt {
|
||||||
|
if self.loop_depth == 0 {
|
||||||
|
self.add_error(SemaError::BreakOutsideLoop, stmt.span);
|
||||||
|
}
|
||||||
|
HirBreakStmt { span: stmt.span }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn analyze_continue_stmt(&mut self, stmt: ContinueStmt) -> HirContinueStmt {
|
||||||
|
if self.loop_depth == 0 {
|
||||||
|
self.add_error(SemaError::ContinueOutsideLoop, stmt.span);
|
||||||
|
}
|
||||||
|
HirContinueStmt { span: stmt.span }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn analyze_condition_expr(&mut self, expr: Expr) -> HirExpr {
|
||||||
|
let span = expr.span;
|
||||||
|
match self.analyze_expr(expr) {
|
||||||
|
Some(expr) => {
|
||||||
|
if expr.ty == SemaType::Void {
|
||||||
|
self.add_error(SemaError::InvalidOperand(SemaType::Void), span);
|
||||||
|
}
|
||||||
|
expr
|
||||||
|
}
|
||||||
|
None => HirExpr {
|
||||||
|
value: HirExprValue::IntLit(0),
|
||||||
|
ty: SemaType::I32,
|
||||||
|
span,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn analyze_expr(&mut self, expr: Expr) -> Option<HirExpr> {
|
||||||
|
let span = expr.span;
|
||||||
|
match expr.value {
|
||||||
|
ExprValue::IntLit(value) => Some(HirExpr {
|
||||||
|
value: HirExprValue::IntLit(value),
|
||||||
|
ty: SemaType::I32,
|
||||||
|
span,
|
||||||
|
}),
|
||||||
|
ExprValue::Var(name) => {
|
||||||
|
let symbol = match self.symbols.get_variable(&name) {
|
||||||
|
Some(symbol) => symbol,
|
||||||
|
None => {
|
||||||
|
self.add_error(SemaError::VariableNotFound(name), span);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Some(HirExpr {
|
||||||
|
value: HirExprValue::Var(symbol),
|
||||||
|
ty: self.symbols.get_symbol(symbol).ty.clone(),
|
||||||
|
span,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
ExprValue::Assign { lvalue, rvalue } => self.analyze_assign_expr(*lvalue, *rvalue, span),
|
||||||
|
ExprValue::ArrayAccess { array, index } => self.analyze_array_access_expr(*array, *index, span),
|
||||||
|
ExprValue::UnaryOp { op, operand } => {
|
||||||
|
let operand_span = operand.span;
|
||||||
|
let operand = self.analyze_expr(*operand)?;
|
||||||
|
if operand.ty == SemaType::Void {
|
||||||
|
self.add_error(SemaError::InvalidOperand(SemaType::Void), operand_span);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let ty = match op {
|
||||||
|
crate::ast::types::UnaryOp::Not => SemaType::I1,
|
||||||
|
_ => operand.ty.clone(),
|
||||||
|
};
|
||||||
|
Some(HirExpr {
|
||||||
|
value: HirExprValue::UnaryOp {
|
||||||
|
op,
|
||||||
|
operand: Box::new(operand),
|
||||||
|
},
|
||||||
|
ty,
|
||||||
|
span,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
ExprValue::BinaryOp { lhs, op, rhs } => self.analyze_binary_expr(*lhs, op, *rhs, span),
|
||||||
|
ExprValue::FuncCall(func_name, args) => self.analyze_func_call_expr(func_name, args, span),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn analyze_assign_expr(&mut self, lvalue: Expr, rvalue: Expr, span: Span) -> Option<HirExpr> {
|
||||||
|
if !matches!(lvalue.value, ExprValue::Var(_) | ExprValue::ArrayAccess { .. }) {
|
||||||
|
self.add_error(SemaError::InvalidAssignmentTarget, lvalue.span);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let lvalue = self.analyze_expr(lvalue)?;
|
||||||
|
if lvalue.ty.is_array_like() {
|
||||||
|
self.add_error(SemaError::InvalidAssignmentTarget, lvalue.span);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let rvalue_span = rvalue.span;
|
||||||
|
let rvalue = self.analyze_expr(rvalue)?;
|
||||||
|
if rvalue.ty == SemaType::Void {
|
||||||
|
self.add_error(SemaError::InvalidOperand(SemaType::Void), rvalue_span);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
if !self.type_matches(&lvalue.ty, &rvalue.ty) {
|
||||||
|
self.add_error(SemaError::TypeMismatch(lvalue.ty.clone(), rvalue.ty.clone()), span);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(HirExpr {
|
||||||
|
ty: lvalue.ty.clone(),
|
||||||
|
value: HirExprValue::Assign {
|
||||||
|
lvalue: Box::new(lvalue),
|
||||||
|
rvalue: Box::new(rvalue),
|
||||||
|
},
|
||||||
|
span,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn analyze_binary_expr(&mut self, lhs: Expr, op: BinaryOp, rhs: Expr, span: Span) -> Option<HirExpr> {
|
||||||
|
let lhs_span = lhs.span;
|
||||||
|
let rhs_span = rhs.span;
|
||||||
|
let lhs = self.analyze_expr(lhs)?;
|
||||||
|
let rhs = self.analyze_expr(rhs)?;
|
||||||
|
if !lhs.ty.is_scalar() {
|
||||||
|
self.add_error(SemaError::InvalidOperand(lhs.ty.clone()), lhs_span);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
if !rhs.ty.is_scalar() {
|
||||||
|
self.add_error(SemaError::InvalidOperand(rhs.ty.clone()), rhs_span);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let result_ty = if op.is_logical() {
|
||||||
|
SemaType::I1
|
||||||
|
} else {
|
||||||
|
match SemaType::get_elevate_result(&lhs.ty, &rhs.ty) {
|
||||||
|
Some(_) if op.is_cmp() => SemaType::I1,
|
||||||
|
Some(ty) => ty,
|
||||||
|
None => {
|
||||||
|
self.add_error(SemaError::IncompatiableOperand(lhs.ty.clone(), rhs.ty.clone()), lhs_span);
|
||||||
|
self.add_error(SemaError::IncompatiableOperand(lhs.ty.clone(), rhs.ty.clone()), rhs_span);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(HirExpr {
|
||||||
|
value: HirExprValue::BinaryOp {
|
||||||
|
lhs: Box::new(lhs),
|
||||||
|
op,
|
||||||
|
rhs: Box::new(rhs),
|
||||||
|
},
|
||||||
|
ty: result_ty,
|
||||||
|
span,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn analyze_func_call_expr(&mut self, func_name: String, args: Vec<Expr>, span: Span) -> Option<HirExpr> {
|
||||||
|
let func_id = match self.function_map.get(&func_name).cloned() {
|
||||||
|
Some(func_id) => func_id,
|
||||||
|
None => {
|
||||||
|
self.add_error(SemaError::FunctionNotFound(func_name), span);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let func_def = self.functions[func_id.0].clone();
|
||||||
|
|
||||||
|
if args.len() < func_def.parameter_types.len() {
|
||||||
|
self.add_error(SemaError::TooFewArguments(func_def.parameter_types.len(), args.len()), span);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
if args.len() > func_def.parameter_types.len() {
|
||||||
|
self.add_error(SemaError::TooManyArguments(func_def.parameter_types.len(), args.len()), span);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut has_error = false;
|
||||||
|
for parameter_type in &func_def.parameter_types {
|
||||||
|
if matches!(parameter_type, SemaType::Void) {
|
||||||
|
self.add_error(SemaError::InvalidParameterType(SemaType::Void), span);
|
||||||
|
has_error = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if has_error {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut hir_args = vec![];
|
||||||
|
for (i, arg) in args.into_iter().enumerate() {
|
||||||
|
let arg = self.analyze_expr(arg)?;
|
||||||
|
let parameter_type = func_def.parameter_types[i].clone();
|
||||||
|
if !self.type_matches(¶meter_type, &arg.ty) {
|
||||||
|
self.add_error(SemaError::TypeMismatch(parameter_type, arg.ty.clone()), span);
|
||||||
|
has_error = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
hir_args.push(arg);
|
||||||
|
}
|
||||||
|
if has_error {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(HirExpr {
|
||||||
|
value: HirExprValue::FuncCall(func_id, hir_args),
|
||||||
|
ty: func_def.return_type,
|
||||||
|
span,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn analyze_array_access_expr(&mut self, array: Expr, index: Expr, span: Span) -> Option<HirExpr> {
|
||||||
|
let index_span = index.span;
|
||||||
|
let array = self.analyze_expr(array)?;
|
||||||
|
let index = self.analyze_expr(index)?;
|
||||||
|
if !index.ty.is_scalar() {
|
||||||
|
self.add_error(SemaError::InvalidOperand(index.ty.clone()), index_span);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let ty = match array.ty.indexed_type() {
|
||||||
|
Some(ty) => ty,
|
||||||
|
None => {
|
||||||
|
self.add_error(SemaError::NotSubscriptable, span);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Some(HirExpr {
|
||||||
|
value: HirExprValue::ArrayAccess {
|
||||||
|
array: Box::new(array),
|
||||||
|
index: Box::new(index),
|
||||||
|
},
|
||||||
|
ty,
|
||||||
|
span,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn type_matches(&self, expected: &SemaType, actual: &SemaType) -> bool {
|
||||||
|
if expected == actual {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
matches!((expected, actual), (SemaType::Ptr(expected_elem), SemaType::Array(_, _)) if actual.indexed_type().is_some_and(|ty| &ty == expected_elem.as_ref()))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
use crate::sema::types::SemaType;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Error)]
|
||||||
|
pub enum SemaError {
|
||||||
|
#[error("variable `{0}` not found")]
|
||||||
|
VariableNotFound(String),
|
||||||
|
#[error("variable `{0}` has already been defined")]
|
||||||
|
VariableHasBeenDefined(String),
|
||||||
|
#[error("function `{0}` not found")]
|
||||||
|
FunctionNotFound(String),
|
||||||
|
#[error("function `{0}` has already been defined")]
|
||||||
|
FunctionHasBeenDefined(String),
|
||||||
|
#[error("incompatible operands: {0} and {1}")]
|
||||||
|
IncompatiableOperand(SemaType, SemaType),
|
||||||
|
#[error("invalid operand type: {0}")]
|
||||||
|
InvalidOperand(SemaType),
|
||||||
|
#[error("too few arguments: expected {0}, got {1}")]
|
||||||
|
TooFewArguments(usize, usize),
|
||||||
|
#[error("too many arguments: expected {0}, got {1}")]
|
||||||
|
TooManyArguments(usize, usize),
|
||||||
|
#[error("type mismatch, expected {0}, got {1}")]
|
||||||
|
TypeMismatch(SemaType, SemaType),
|
||||||
|
#[error("invalid assignment target")]
|
||||||
|
InvalidAssignmentTarget,
|
||||||
|
#[error("break statement outside of loop")]
|
||||||
|
BreakOutsideLoop,
|
||||||
|
#[error("continue statement outside of loop")]
|
||||||
|
ContinueOutsideLoop,
|
||||||
|
#[error("invalid parameter type: {0}")]
|
||||||
|
InvalidParameterType(SemaType),
|
||||||
|
#[error("return expression on void function")]
|
||||||
|
ReturnExpressionOnVoidFunction,
|
||||||
|
#[error("array dimension must be a positive integer literal")]
|
||||||
|
InvalidArrayDimension,
|
||||||
|
#[error("subscripted value is not an array or pointer")]
|
||||||
|
NotSubscriptable,
|
||||||
|
}
|
||||||
+110
@@ -0,0 +1,110 @@
|
|||||||
|
use crate::{ast::types::{BinaryOp, UnaryOp}, diagnostic::span::Span, sema::{symbol::{FunctionId, FunctionSig, SymbolId}, types::SemaType}};
|
||||||
|
|
||||||
|
pub struct HirCompileUnit {
|
||||||
|
pub global_decls: Vec<HirGlobalDeclStmt>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum HirGlobalDeclStmt {
|
||||||
|
VarDecl(HirVarDeclStmt),
|
||||||
|
FuncDecl(HirFuncDeclStmt),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct HirVarDeclStmt {
|
||||||
|
pub values: Vec<HirVarDeclStmtValue>,
|
||||||
|
pub data_type: SemaType,
|
||||||
|
pub type_span: Span,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct HirVarDeclStmtValue {
|
||||||
|
pub symbol: SymbolId,
|
||||||
|
pub name_span: Span,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct HirFuncDeclStmt {
|
||||||
|
pub sig: FunctionSig,
|
||||||
|
pub params: Vec<HirParam>,
|
||||||
|
pub body: HirBlockStmt,
|
||||||
|
pub ret_type_span: Span,
|
||||||
|
pub name_span: Span,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct HirParam {
|
||||||
|
pub symbol: SymbolId,
|
||||||
|
pub param_type: SemaType,
|
||||||
|
pub name_span: Span,
|
||||||
|
pub type_span: Span,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct HirBlockStmt {
|
||||||
|
pub statements: Vec<HirStatement>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum HirStatement {
|
||||||
|
Return(HirReturnStmt),
|
||||||
|
Block(HirBlockStmt),
|
||||||
|
Expr(HirExpr),
|
||||||
|
VarDecl(HirVarDeclStmt),
|
||||||
|
If(HirIfStmt),
|
||||||
|
While(HirWhileStmt),
|
||||||
|
Break(HirBreakStmt),
|
||||||
|
Continue(HirContinueStmt),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct HirIfStmt {
|
||||||
|
pub condition: HirExpr,
|
||||||
|
pub then_branch: HirBlockStmt,
|
||||||
|
pub ifelse_branch: Vec<HirIfElseBranch>,
|
||||||
|
pub else_branch: Option<HirBlockStmt>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct HirIfElseBranch {
|
||||||
|
pub condition: HirExpr,
|
||||||
|
pub then_branch: HirBlockStmt,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct HirWhileStmt {
|
||||||
|
pub condition: HirExpr,
|
||||||
|
pub body: HirBlockStmt,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct HirBreakStmt {
|
||||||
|
pub span: Span,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct HirContinueStmt {
|
||||||
|
pub span: Span,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct HirReturnStmt {
|
||||||
|
pub value: Option<HirExpr>,
|
||||||
|
pub span: Span,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct HirExpr {
|
||||||
|
pub value: HirExprValue,
|
||||||
|
pub ty: SemaType,
|
||||||
|
pub span: Span,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum HirExprValue {
|
||||||
|
IntLit(i64),
|
||||||
|
Var(SymbolId),
|
||||||
|
ArrayAccess {
|
||||||
|
array: Box<HirExpr>,
|
||||||
|
index: Box<HirExpr>,
|
||||||
|
},
|
||||||
|
BinaryOp {
|
||||||
|
lhs: Box<HirExpr>,
|
||||||
|
op: BinaryOp,
|
||||||
|
rhs: Box<HirExpr>,
|
||||||
|
},
|
||||||
|
UnaryOp {
|
||||||
|
op: UnaryOp,
|
||||||
|
operand: Box<HirExpr>,
|
||||||
|
},
|
||||||
|
FuncCall(FunctionId, Vec<HirExpr>),
|
||||||
|
Assign {
|
||||||
|
lvalue: Box<HirExpr>,
|
||||||
|
rvalue: Box<HirExpr>,
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
pub mod analyzer;
|
||||||
|
pub mod err;
|
||||||
|
pub mod hir;
|
||||||
|
pub mod symbol;
|
||||||
|
pub mod types;
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
use std::collections::{BTreeMap, BTreeSet};
|
||||||
|
|
||||||
|
use crate::sema::{err::SemaError, types::SemaType};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub struct SymbolId(pub usize);
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
pub enum SymbolKind {
|
||||||
|
Global,
|
||||||
|
Local,
|
||||||
|
Param,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Symbol {
|
||||||
|
pub id: SymbolId,
|
||||||
|
pub name: String,
|
||||||
|
pub ty: SemaType,
|
||||||
|
pub kind: SymbolKind,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub struct FunctionId(pub usize);
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct FunctionSig {
|
||||||
|
pub id: FunctionId,
|
||||||
|
pub name: String,
|
||||||
|
pub return_type: SemaType,
|
||||||
|
pub parameter_types: Vec<SemaType>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SymbolTable {
|
||||||
|
symbols: Vec<Symbol>,
|
||||||
|
variable_map: BTreeMap<String, Vec<SymbolId>>,
|
||||||
|
scopes: Vec<BTreeSet<String>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SymbolTable {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
symbols: vec![],
|
||||||
|
variable_map: BTreeMap::new(),
|
||||||
|
scopes: vec![BTreeSet::new()],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn enter_scope(&mut self) {
|
||||||
|
self.scopes.push(BTreeSet::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn exit_scope(&mut self) {
|
||||||
|
let variables = self.scopes.pop().unwrap();
|
||||||
|
for var in variables {
|
||||||
|
self.variable_map.get_mut(&var).unwrap().pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn declare_variable(&mut self, name: &str, kind: SymbolKind, ty: SemaType) -> Result<SymbolId, SemaError> {
|
||||||
|
if self.scopes.last().unwrap().contains(name) {
|
||||||
|
return Err(SemaError::VariableHasBeenDefined(name.to_string()));
|
||||||
|
}
|
||||||
|
let id = SymbolId(self.symbols.len());
|
||||||
|
self.symbols.push(Symbol {
|
||||||
|
id,
|
||||||
|
name: name.to_string(),
|
||||||
|
ty,
|
||||||
|
kind,
|
||||||
|
});
|
||||||
|
self.variable_map.entry(name.to_string()).or_default().push(id);
|
||||||
|
self.scopes.last_mut().unwrap().insert(name.to_string());
|
||||||
|
Ok(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_variable(&self, name: &str) -> Option<SymbolId> {
|
||||||
|
self.variable_map.get(name).and_then(|vars| vars.last()).cloned()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_symbol(&self, id: SymbolId) -> &Symbol {
|
||||||
|
&self.symbols[id.0]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
use crate::{ast::types::Type as AstType, ir::types::IRType};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub enum SemaType {
|
||||||
|
I32,
|
||||||
|
I1,
|
||||||
|
Void,
|
||||||
|
Array(Box<SemaType>, Vec<usize>),
|
||||||
|
Ptr(Box<SemaType>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SemaType {
|
||||||
|
pub fn get_elevate_result(lhs: &SemaType, rhs: &SemaType) -> Option<SemaType> {
|
||||||
|
if lhs == rhs {
|
||||||
|
Some(lhs.clone())
|
||||||
|
} else if (*lhs == SemaType::I32 && *rhs == SemaType::I1) || (*lhs == SemaType::I1 && *rhs == SemaType::I32) {
|
||||||
|
Some(SemaType::I32)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_scalar(&self) -> bool {
|
||||||
|
matches!(self, SemaType::I32 | SemaType::I1)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_array_like(&self) -> bool {
|
||||||
|
matches!(self, SemaType::Array(_, _) | SemaType::Ptr(_))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn indexed_type(&self) -> Option<SemaType> {
|
||||||
|
match self {
|
||||||
|
SemaType::Array(elem, dims) => {
|
||||||
|
if dims.len() == 1 {
|
||||||
|
Some((**elem).clone())
|
||||||
|
} else {
|
||||||
|
Some(SemaType::Array(elem.clone(), dims[1..].to_vec()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SemaType::Ptr(elem) => Some((**elem).clone()),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn element_size_in_bytes(&self) -> usize {
|
||||||
|
match self {
|
||||||
|
SemaType::I32 => 4,
|
||||||
|
SemaType::I1 => 1,
|
||||||
|
SemaType::Void => 0,
|
||||||
|
SemaType::Array(elem, dims) => elem.element_size_in_bytes() * dims.iter().product::<usize>(),
|
||||||
|
SemaType::Ptr(_) => 4,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for SemaType {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
SemaType::I32 => write!(f, "i32"),
|
||||||
|
SemaType::I1 => write!(f, "i1"),
|
||||||
|
SemaType::Void => write!(f, "void"),
|
||||||
|
SemaType::Array(elem, dims) => {
|
||||||
|
write!(f, "{}", elem)?;
|
||||||
|
for dim in dims {
|
||||||
|
write!(f, "[{}]", dim)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
SemaType::Ptr(elem) => write!(f, "{}*", elem),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<AstType> for SemaType {
|
||||||
|
fn from(value: AstType) -> Self {
|
||||||
|
match value {
|
||||||
|
AstType::Int => SemaType::I32,
|
||||||
|
AstType::Void => SemaType::Void,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<SemaType> for IRType {
|
||||||
|
fn from(value: SemaType) -> Self {
|
||||||
|
match value {
|
||||||
|
SemaType::I32 => IRType::I32,
|
||||||
|
SemaType::I1 => IRType::I1,
|
||||||
|
SemaType::Void => IRType::Void,
|
||||||
|
SemaType::Array(elem, dims) => IRType::Array(Box::new((*elem).into()), dims),
|
||||||
|
SemaType::Ptr(elem) => IRType::Ptr(Box::new((*elem).into())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user