feat(parser,ir,backend): Support array

This commit is contained in:
2026-05-31 22:06:09 +08:00
parent c42575c1c6
commit 669c415bd7
13 changed files with 575 additions and 130 deletions
+111 -44
View File
@@ -1,6 +1,6 @@
use std::{collections::BTreeMap, vec};
use crate::{ir::types::{BinaryOp, 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::{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::UnaryOp as AstUnaryOp;
pub struct Generator<'a> {
@@ -41,8 +41,8 @@ impl<'a> Generator<'a> {
let sig = self.sema.get_function_sig(function_id);
Function {
name: sig.name.clone(),
parameter_types: sig.parameter_types.iter().map(|ty| (*ty).into()).collect(),
return_type: sig.return_type.into(),
parameter_types: sig.parameter_types.iter().map(|ty| ty.clone().into()).collect(),
return_type: sig.return_type.clone().into(),
}
}
@@ -66,7 +66,7 @@ impl<'a> Generator<'a> {
let mut instrs = vec![];
let var_type = if is_global { VariableType::Global } else { VariableType::Local };
for value in var_decl.values {
let var = self.var_manager.declare_symbol(value.symbol, 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());
if is_global {
instrs.push(IRInstr::Declare(var));
}
@@ -77,10 +77,10 @@ impl<'a> Generator<'a> {
fn generate_func_decl(&mut self, func_decl: HirFuncDeclStmt) -> Vec<IRInstr> {
let parameters: Vec<Variable> = func_decl.params.iter()
.map(|param| self.var_manager.declare_symbol(param.symbol, VariableType::Local, param.param_type.into()))
.map(|param| self.var_manager.declare_symbol(param.symbol, VariableType::Local, param.param_type.clone().into()))
.collect();
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<_>>();
let mut body_instrs = vec![];
self.func_exit = Some((self.request_label(), {
@@ -100,7 +100,7 @@ impl<'a> Generator<'a> {
}
body_instrs.push(IRInstr::Entry);
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);
let func_exit = self.func_exit.take().unwrap();
@@ -108,7 +108,7 @@ impl<'a> Generator<'a> {
body_instrs.push(IRInstr::Exit(func_exit.1));
self.var_manager.clear_local_counter();
let mut func = self.ir_function(func_decl.sig.id);
func.parameter_types = temp_parameters.iter().map(|v| v.data_type).collect();
func.parameter_types = temp_parameters.iter().map(|v| v.data_type.clone()).collect();
vec![IRInstr::DefineFunc(func, temp_parameters, body_instrs)]
}
fn generate_block_stmt(&mut self, block_stmt: HirBlockStmt) -> Vec<IRInstr> {
@@ -149,7 +149,7 @@ impl<'a> Generator<'a> {
fn generate_return_stmt(&mut self, return_stmt: HirReturnStmt) -> Vec<IRInstr> {
let mut instrs = vec![];
let func_exit = self.func_exit.unwrap();
let func_exit = self.func_exit.clone().unwrap();
match return_stmt.value {
Some(expr) => {
self.current_exit_label.push(None);
@@ -269,26 +269,49 @@ impl<'a> Generator<'a> {
HirExprValue::IntLit(i) => {
// TODO: convert check
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))
},
HirExprValue::Var(symbol) => {
let var = self.var_manager.get_symbol(symbol).unwrap();
(vec![], Some(var))
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::GetAddr(dest.clone(), var)], Some(dest))
} else {
(vec![], Some(var))
}
},
HirExprValue::Assign { lvalue, rvalue } => {
let var = if let HirExprValue::Var(symbol) = lvalue.value {
self.var_manager.get_symbol(symbol).unwrap()
} else {
return None;
};
let lvalue_ty: IRType = lvalue.ty.clone().into();
self.current_exit_label.push(None);
let (mut instrs, rvalue_var) = self.generate_expr(*rvalue)?;
self.current_exit_label.pop();
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)));
let (lvalue_instrs, target, is_addr) = self.generate_lvalue(*lvalue)?;
instrs.extend(lvalue_instrs);
if is_addr {
instrs.push(IRInstr::Store(target.clone(), rvalue_var.unwrap()));
} else {
instrs.push(IRInstr::Move(target.clone(), MoveRValue::Var(rvalue_var.unwrap())));
}
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))
}
},
HirExprValue::UnaryOp { op, operand } => {
let mut parent_is_logical = true;
let exit_passdown = if matches!(op, AstUnaryOp::Not) {
@@ -319,26 +342,26 @@ impl<'a> Generator<'a> {
let operand_var = operand_var.unwrap();
let dest_var = match op {
AstUnaryOp::Add => {
let dest_var = self.var_manager.declare_temp(operand_var.data_type);
instrs.push(IRInstr::Move(dest_var, MoveRValue::Var(operand_var)));
let dest_var = self.var_manager.declare_temp(operand_var.data_type.clone());
instrs.push(IRInstr::Move(dest_var.clone(), MoveRValue::Var(operand_var)));
dest_var
},
AstUnaryOp::Sub => {
let dest_var = self.var_manager.declare_temp(operand_var.data_type);
instrs.push(IRInstr::Unary(dest_var, UnaryOp::Neg, operand_var));
let dest_var = self.var_manager.declare_temp(operand_var.data_type.clone());
instrs.push(IRInstr::Unary(dest_var.clone(), UnaryOp::Neg, operand_var));
dest_var
},
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
if !parent_is_logical {
let exit = exit_passdown.unwrap(); // (false_exit, true_exit) (consider `not`)
let final_exit = self.request_label();
instrs.push(IRInstr::Label(exit.1));
instrs.push(IRInstr::Move(dest_var, MoveRValue::ConstInt(1)));
instrs.push(IRInstr::Move(dest_var.clone(), MoveRValue::ConstInt(1)));
instrs.push(IRInstr::Goto(final_exit));
instrs.push(IRInstr::Label(exit.0));
instrs.push(IRInstr::Move(dest_var, MoveRValue::ConstInt(0)));
instrs.push(IRInstr::Move(dest_var.clone(), MoveRValue::ConstInt(0)));
instrs.push(IRInstr::Goto(final_exit));
instrs.push(IRInstr::Label(final_exit));
@@ -400,21 +423,21 @@ impl<'a> Generator<'a> {
let convert_to;
if op.is_logical() {
// we dont really care since we use cmp
convert_to = left_var.data_type;
convert_to = left_var.data_type.clone();
} else {
convert_to = IRType::get_elevate_result(left_var.data_type, right_var.data_type).unwrap();
convert_to = IRType::get_elevate_result(&left_var.data_type, &right_var.data_type).unwrap();
}
// do implicit convert if needed
// TODO: further check
if !op.is_logical() && convert_to != left_var.data_type {
let temp_var = self.var_manager.declare_temp(convert_to);
instrs.push(IRInstr::Move(temp_var, MoveRValue::Var(left_var)));
let temp_var = self.var_manager.declare_temp(convert_to.clone());
instrs.push(IRInstr::Move(temp_var.clone(), MoveRValue::Var(left_var)));
left_var = temp_var;
}
let result_type;
match op {
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::And | AstBinaryOp::Or => {
@@ -448,17 +471,17 @@ impl<'a> Generator<'a> {
instrs.extend(right_instrs);
if !op.is_logical() && convert_to != right_var.data_type {
let temp_var = self.var_manager.declare_temp(convert_to);
instrs.push(IRInstr::Move(temp_var, MoveRValue::Var(right_var)));
instrs.push(IRInstr::Move(temp_var.clone(), MoveRValue::Var(right_var)));
right_var = temp_var;
}
if !op.is_logical() {
if let Some((true_exit, false_exit)) = parent_exit {
let final_exit = self.request_label();
instrs.push(IRInstr::Label(true_exit));
instrs.push(IRInstr::Move(dest_var, MoveRValue::ConstInt(1)));
instrs.push(IRInstr::Move(dest_var.clone(), MoveRValue::ConstInt(1)));
instrs.push(IRInstr::Goto(final_exit));
instrs.push(IRInstr::Label(false_exit));
instrs.push(IRInstr::Move(dest_var, MoveRValue::ConstInt(0)));
instrs.push(IRInstr::Move(dest_var.clone(), MoveRValue::ConstInt(0)));
instrs.push(IRInstr::Label(final_exit));
@@ -468,9 +491,9 @@ impl<'a> Generator<'a> {
return Some((instrs, Some(dest_var)));
} else {
if op.is_cmp() {
instrs.push(IRInstr::Cmp(dest_var, VariableOrIntLit::Var(left_var), op.into(), VariableOrIntLit::Var(right_var)));
instrs.push(IRInstr::Cmp(dest_var.clone(), VariableOrIntLit::Var(left_var), op.into(), VariableOrIntLit::Var(right_var)));
} 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() {
@@ -504,21 +527,61 @@ impl<'a> Generator<'a> {
let ret_variable = if matches!(func_def.return_type, IRType::Void) {
None
} 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)
}
};
if let Some((true_exit, false_exit)) = self.current_exit_label.last().cloned().flatten() {
let cmp_var = self.var_manager.declare_temp(IRType::I1);
instrs.push(IRInstr::Cmp(cmp_var, VariableOrIntLit::Var(var.unwrap()), CmpOp::Ne, VariableOrIntLit::IntLit(0)));
instrs.push(IRInstr::Cmp(cmp_var.clone(), VariableOrIntLit::Var(var.clone().unwrap()), CmpOp::Ne, VariableOrIntLit::IntLit(0)));
instrs.push(IRInstr::CondGoto(cmp_var, true_exit, false_exit));
Some((instrs, var))
} else {
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::GetAddr(ptr.clone(), 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);
instrs.push(IRInstr::GetElementPtr(dest.clone(), base, index_var.unwrap(), elem_size));
Some((instrs, dest))
}
}
struct VariableManager {
@@ -551,28 +614,28 @@ impl VariableManager {
}
_ => unreachable!(),
};
self.variable_map.insert(symbol, variable);
self.variable_map.insert(symbol, variable.clone());
if matches!(var_type, VariableType::Local) {
self.local_var_type.push(variable);
self.local_var_type.push(variable.clone());
}
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 };
self.local_counter += 1;
self.local_var_type.push(var);
self.local_var_type.push(var.clone());
var
}
pub fn declare_unamed_local(&mut self, var_data_type: IRType) -> Variable {
let var = Variable { index: self.local_counter, var_type: VariableType::Local, data_type: var_data_type };
self.local_counter += 1;
self.local_var_type.push(var);
self.local_var_type.push(var.clone());
var
}
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 };
self.local_counter += 1;
self.local_var_type.push(var);
self.local_var_type.push(var.clone());
var
}
pub fn clear_local_counter(&mut self) {
@@ -670,4 +733,8 @@ mod tests {
fn test_func() {
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");
}
}
+93 -8
View File
@@ -30,6 +30,10 @@ pub enum IRInstr {
CondGoto(Variable, usize, usize), // condition, true label, false label
Label(usize),
Move(Variable, MoveRValue),
Load(Variable, Variable),
Store(Variable, Variable),
GetAddr(Variable, Variable),
GetElementPtr(Variable, Variable, Variable, usize),
}
impl Display for IRInstr {
@@ -47,7 +51,11 @@ impl Display for IRInstr {
}
}
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::GetAddr(dest, var) => write!(f, "{} = getaddr {}", dest, var),
IRInstr::GetElementPtr(dest, base, index, elem_size) => write!(f, "{} = gep {}, {}, {}", dest, base, index, elem_size),
IRInstr::Declare(var) => write!(f, "declare {}", var.to_decl_string()),
IRInstr::DefineFunc(func, args, body) => {
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)
@@ -59,11 +67,13 @@ impl Display for IRInstr {
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum IRType {
I32,
I1,
Void,
Ptr(Box<IRType>),
Array(Box<IRType>, Vec<usize>),
}
impl IRType {
pub fn size_in_bytes(&self) -> usize {
@@ -71,13 +81,47 @@ impl IRType {
IRType::I32 => 4,
IRType::I1 => 1,
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 {
Some(lhs)
} else if (lhs == IRType::I32 && rhs == IRType::I1) || (lhs == IRType::I1 && rhs == IRType::I32) {
Some(lhs.clone())
} else if (*lhs == IRType::I32 && *rhs == IRType::I1) || (*lhs == IRType::I1 && *rhs == IRType::I32) {
Some(IRType::I32)
} else {
None
@@ -90,6 +134,30 @@ impl Display for IRType {
IRType::I32 => write!(f, "i32"),
IRType::Void => write!(f, "void"),
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 +188,7 @@ pub enum VariableType {
Local,
Temp,
}
#[derive(Clone, Copy)]
#[derive(Clone)]
pub struct Variable {
// pub name: String,
pub index: usize,
@@ -160,6 +228,19 @@ impl Display for Variable {
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)]
pub struct Function {
pub name: String,
@@ -175,7 +256,11 @@ impl Function {
}
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)
}
}
@@ -269,4 +354,4 @@ impl From<AstBinaryOp> for CmpOp {
_ => unreachable!(),
}
}
}
}