Compare commits

...

13 Commits

27 changed files with 1976 additions and 1429 deletions
+3 -1
View File
@@ -2,4 +2,6 @@
/testcases /testcases
/output /output
*.pyc *.pyc
*.zip *.zip
*.ir
*.s
+31
View File
@@ -0,0 +1,31 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Debug executable 'compiler'",
"type": "lldb",
"request": "launch",
"cargo": {
"args": [
"run",
"--bin=compiler"
]
},
"args": []
},
{
"name": "Debug unit tests in executable 'compiler'",
"type": "lldb",
"request": "launch",
"cargo": {
"args": [
"test",
"--bin=compiler"
]
}
}
]
}
+32
View File
@@ -0,0 +1,32 @@
use crate::diagnostic::span::Span;
pub struct Type {
span: Span,
kind: TypeKind,
}
pub enum TypeKind {
Builtin(BuiltinType),
Array(ArrayType),
}
pub struct BuiltinType {
span: Span,
kind: BuiltinTypeKind,
}
pub enum BuiltinTypeKind {
Int,
Void,
}
pub struct ArrayType {
span: Span,
element_type: Box<Type>,
dimensions: Vec<ArrayDimension>,
}
pub struct ArrayDimension {
span: Span,
size: Option<Expr>,
}
+11
View File
@@ -0,0 +1,11 @@
use thiserror::Error;
use crate::lexer::types::TokenValue;
#[derive(Debug, Clone, PartialEq, Eq, Error)]
pub enum ParseError {
#[error("expected {} before {}", .1, .0)]
ExpectedBefore(TokenValue, &'static str),
#[error("expected identifier after {}", .0)]
ExpectedIdentAfter(TokenValue),
}
+6
View File
@@ -235,6 +235,12 @@ impl AstGraphBuilder {
self.add_expr(node, rvalue); self.add_expr(node, rvalue);
node node
}, },
ExprValue::CompoundAssign { lvalue, rvalue, .. } => {
let node = self.child(parent, expr.value.to_string());
self.add_expr(node, lvalue);
self.add_expr(node, rvalue);
node
},
ExprValue::UnaryOp { op: _, operand } => { ExprValue::UnaryOp { op: _, operand } => {
let node = self.child(parent, expr.value.to_string()); let node = self.child(parent, expr.value.to_string());
self.add_expr(node, operand); self.add_expr(node, operand);
+1
View File
@@ -1,3 +1,4 @@
pub mod err;
pub mod graph; pub mod graph;
pub mod parser; pub mod parser;
pub mod types; pub mod types;
-1284
View File
File diff suppressed because it is too large Load Diff
+459
View File
@@ -0,0 +1,459 @@
use crate::{
ast::err::ParseError,
ast::types::{
ArrayDimension, CompileUnit, Expr, FuncDeclStmt, GlobalDeclStmt, Param, StorageClass,
VarDeclStmt, VarDeclStmtValue,
},
diagnostic::span::Span,
lexer::types::{TokenValue, TypeIdent},
};
use super::{ParseProcessError, ParseType, Parser};
struct DeclSpecifiers {
storage_class: Option<StorageClass>,
type_specifier: TypeIdent,
type_span: Span,
}
struct Declarator {
name: String,
name_span: Span,
suffixes: Vec<DeclaratorSuffix>,
}
enum DeclaratorSuffix {
Array(ArrayDimension),
Function {
params: Vec<Param>,
span: Span,
},
}
struct InitDeclarator {
declarator: Declarator,
initializer: Option<Expr>,
}
impl Parser {
pub(super) fn parse_compile_unit(&mut self) -> CompileUnit {
let mut global_decls = vec![];
while !self.is_eof() {
if let Some(decl) = self.parse_external_decl_stmt() {
global_decls.push(decl);
} else {
self.until_next_token(&[TokenValue::Semicolon, TokenValue::RBrace]);
}
}
CompileUnit { global_decls }
}
fn parse_external_decl_stmt(&mut self) -> Option<GlobalDeclStmt> {
let specifiers = match self.parse_decl_specifiers(ParseType::MustParse) {
Ok(specifiers) => specifiers,
Err(ParseProcessError::ErrorInMatch) => return None,
Err(ParseProcessError::TryNext) => unreachable!(),
};
let declarator = match self.parse_declarator() {
Ok(declarator) => declarator,
Err(ParseProcessError::ErrorInMatch) => return None,
Err(ParseProcessError::TryNext) => unreachable!(),
};
if self.peek().value == TokenValue::LBrace {
match self.finish_func_def_stmt(specifiers, declarator) {
Ok(func_decl) => return Some(GlobalDeclStmt::FuncDecl(func_decl)),
Err(ParseProcessError::ErrorInMatch) => return None,
Err(ParseProcessError::TryNext) => unreachable!(),
}
}
match self.finish_var_decl_stmt(specifiers, declarator, true, true) {
Ok(var_decl) => Some(GlobalDeclStmt::VarDecl(var_decl)),
Err(ParseProcessError::ErrorInMatch) => None,
Err(ParseProcessError::TryNext) => unreachable!(),
}
}
fn parse_decl_specifiers(
&mut self,
parse_type: ParseType,
) -> Result<DeclSpecifiers, ParseProcessError> {
let storage_class = if self.peek().value == TokenValue::Static {
self.advance(1);
Some(StorageClass::Static)
} else {
None
};
let type_token = self.peek().clone();
let type_specifier = match type_token.value.as_type_ident() {
Some(type_specifier) => {
self.advance(1);
type_specifier
}
None if parse_type == ParseType::TryParse && storage_class.is_none() => {
return Err(ParseProcessError::TryNext);
}
None => {
let token = self.next();
self.diagnostics.add_from_frontend_error(
ParseError::ExpectedBefore(token.value, "type specifier"),
token.span,
);
return Err(ParseProcessError::ErrorInMatch);
}
};
Ok(DeclSpecifiers {
storage_class,
type_specifier,
type_span: type_token.span,
})
}
fn parse_declarator(&mut self) -> Result<Declarator, ParseProcessError> {
self.parse_direct_declarator()
}
fn parse_direct_declarator(&mut self) -> Result<Declarator, ParseProcessError> {
let mut declarator = match self.peek().value.clone() {
TokenValue::Ident(name) => {
let name_span = self.peek().span;
self.advance(1);
Declarator {
name,
name_span,
suffixes: vec![],
}
}
TokenValue::LParen => {
self.advance(1);
let declarator = self.parse_declarator()?;
self.must_match_token(&TokenValue::RParen, "`)`")?;
declarator
}
_ => {
let token = self.next();
self.diagnostics.add_from_frontend_error(
ParseError::ExpectedBefore(token.value, "declarator"),
token.span,
);
return Err(ParseProcessError::ErrorInMatch);
}
};
loop {
match self.peek().value {
TokenValue::LBracket => {
let dimension = self.parse_array_declarator_suffix()?;
declarator.suffixes.push(DeclaratorSuffix::Array(dimension));
}
TokenValue::LParen => {
let start_span = self.next().span;
let params = self.parse_parameter_list_after_lparen()?;
let end_span = self.last().span;
declarator.suffixes.push(DeclaratorSuffix::Function {
params,
span: Span::from_two(start_span, end_span),
});
}
_ => break,
}
}
Ok(declarator)
}
fn parse_array_declarator_suffix(&mut self) -> Result<ArrayDimension, ParseProcessError> {
let start_span = self.next().span;
let value = if self.peek().value == TokenValue::RBracket {
None
} else {
Some(self.parse_expr()?)
};
let end_span = match &self.peek().value {
TokenValue::RBracket => {
let span = self.peek().span;
self.advance(1);
span
}
// TokenValue::Eof => {
// self.diagnostics.add_from_frontend_error(
// ParseError::ExpectedBefore(TokenValue::Eof, "`]`"),
// start_span,
// );
// return Err(ParseProcessError::ErrorInMatch);
// }
_ => {
let token = self.next();
self.diagnostics.add_from_frontend_error(
ParseError::ExpectedBefore(token.value, "`]`"),
token.span,
);
return Err(ParseProcessError::ErrorInMatch);
}
};
Ok(ArrayDimension {
value,
span: Span::from_two(start_span, end_span),
})
}
fn parse_parameter_list_after_lparen(&mut self) -> Result<Vec<Param>, ParseProcessError> {
if self.peek().value == TokenValue::RParen {
self.advance(1);
return Ok(vec![]);
}
if self.peek().value == TokenValue::TypeIdent(TypeIdent::Void)
&& self.peek_n(1).value == TokenValue::RParen
{
self.advance(2);
return Ok(vec![]);
}
let mut params = vec![];
loop {
match self.parse_param() {
Ok(param) => params.push(param),
Err(error) => {
self.until_next_token(&[TokenValue::Comma, TokenValue::RParen]);
if self.last().value == TokenValue::RParen {
return Err(error);
}
continue;
}
}
match self.peek().value {
TokenValue::Comma => {
self.advance(1);
}
TokenValue::RParen => {
self.advance(1);
break;
}
_ => {
self.must_match_token(&TokenValue::RParen, "`,` or `)`")?;
break;
}
}
}
Ok(params)
}
fn parse_param(&mut self) -> Result<Param, ParseProcessError> {
let specifiers = self.parse_decl_specifiers(ParseType::MustParse)?;
let declarator = self.parse_declarator()?;
self.declarator_to_param(specifiers, declarator)
}
pub(super) fn parse_var_decl_stmt(
&mut self,
parse_type: ParseType,
) -> Result<VarDeclStmt, ParseProcessError> {
self.parse_var_decl_stmt_inner(parse_type, true)
}
pub(super) fn parse_var_decl_stmt_inner(
&mut self,
parse_type: ParseType,
consume_semicolon: bool,
) -> Result<VarDeclStmt, ParseProcessError> {
let specifiers = self.parse_decl_specifiers(parse_type)?;
let declarator = self.parse_declarator()?;
self.finish_var_decl_stmt(specifiers, declarator, consume_semicolon, false)
}
fn finish_func_def_stmt(
&mut self,
specifiers: DeclSpecifiers,
declarator: Declarator,
) -> Result<FuncDeclStmt, ParseProcessError> {
let Declarator {
name,
name_span,
suffixes,
} = declarator;
let params = self.take_function_params(name_span, suffixes)?;
let body = self.parse_block_stmt(ParseType::MustParse)?;
Ok(FuncDeclStmt {
return_type: specifiers.type_specifier.into(),
storage_class: specifiers.storage_class,
name,
params,
body,
ret_type_span: specifiers.type_span,
name_span,
})
}
fn finish_var_decl_stmt(
&mut self,
specifiers: DeclSpecifiers,
first_declarator: Declarator,
consume_semicolon: bool,
_allow_empty_declarator_list: bool,
) -> Result<VarDeclStmt, ParseProcessError> {
let mut values = vec![];
let initializer = self.parse_initializer()?;
values.push(self.init_declarator_to_var_value(InitDeclarator {
declarator: first_declarator,
initializer,
})?);
while self.peek().value == TokenValue::Comma {
self.advance(1);
let declarator = self.parse_declarator()?;
let initializer = self.parse_initializer()?;
values.push(self.init_declarator_to_var_value(InitDeclarator {
declarator,
initializer,
})?);
}
if consume_semicolon {
self.must_match_token(&TokenValue::Semicolon, "`;`")?;
}
Ok(VarDeclStmt {
storage_class: specifiers.storage_class,
values,
type_span: specifiers.type_span,
data_type: specifiers.type_specifier.into(),
})
}
fn parse_initializer(&mut self) -> Result<Option<Expr>, ParseProcessError> {
if self.peek().value != TokenValue::Equal {
return Ok(None);
}
self.advance(1);
Ok(Some(self.parse_expr()?))
}
fn skip_initializer_list(&mut self) -> Result<(), ParseProcessError> {
let mut depth = 0usize;
loop {
let token = self.next().clone();
match token.value {
TokenValue::LBrace => depth += 1,
TokenValue::RBrace => {
depth -= 1;
if depth == 0 {
return Ok(());
}
}
TokenValue::Eof => {
self.diagnostics.add_from_frontend_error(
ParseError::ExpectedBefore(TokenValue::Eof, "`}`"),
token.span,
);
return Err(ParseProcessError::ErrorInMatch);
}
_ => {}
}
}
}
fn init_declarator_to_var_value(
&mut self,
init_declarator: InitDeclarator,
) -> Result<VarDeclStmtValue, ParseProcessError> {
let Declarator {
name,
name_span,
suffixes,
} = init_declarator.declarator;
let dimensions = self.take_array_dimensions(suffixes)?;
Ok(VarDeclStmtValue {
name,
name_span,
dimensions,
value: init_declarator.initializer,
})
}
fn declarator_to_param(
&mut self,
specifiers: DeclSpecifiers,
declarator: Declarator,
) -> Result<Param, ParseProcessError> {
let Declarator {
name,
name_span,
suffixes,
} = declarator;
let dimensions = self.take_array_dimensions(suffixes)?;
Ok(Param {
param_type: specifiers.type_specifier.into(),
name,
dimensions,
name_span,
type_span: specifiers.type_span,
})
}
fn take_array_dimensions(
&mut self,
suffixes: Vec<DeclaratorSuffix>,
) -> Result<Vec<ArrayDimension>, ParseProcessError> {
let mut dimensions = vec![];
for suffix in suffixes {
match suffix {
DeclaratorSuffix::Array(dimension) => dimensions.push(dimension),
DeclaratorSuffix::Function { span, .. } => {
self.diagnostics.add_from_frontend_error(
ParseError::ExpectedBefore(TokenValue::LParen, "variable declarator"),
span,
);
return Err(ParseProcessError::ErrorInMatch);
}
}
}
Ok(dimensions)
}
fn take_function_params(
&mut self,
name_span: Span,
suffixes: Vec<DeclaratorSuffix>,
) -> Result<Vec<Param>, ParseProcessError> {
let mut params = None;
for suffix in suffixes {
match suffix {
DeclaratorSuffix::Function {
params: suffix_params,
..
} if params.is_none() => params = Some(suffix_params),
DeclaratorSuffix::Function { span, .. } => {
self.diagnostics.add_from_frontend_error(
ParseError::ExpectedBefore(TokenValue::LParen, "function body"),
span,
);
return Err(ParseProcessError::ErrorInMatch);
}
DeclaratorSuffix::Array(dimension) => {
self.diagnostics.add_from_frontend_error(
ParseError::ExpectedBefore(TokenValue::LBracket, "function declarator"),
dimension.span,
);
return Err(ParseProcessError::ErrorInMatch);
}
}
}
match params {
Some(params) => Ok(params),
None => {
self.diagnostics.add_from_frontend_error(
ParseError::ExpectedBefore(TokenValue::LBrace, "function declarator"),
name_span,
);
Err(ParseProcessError::ErrorInMatch)
}
}
}
}
+380
View File
@@ -0,0 +1,380 @@
use crate::{
ast::{err::ParseError, types::{BinaryOp, Expr, ExprValue, IncDecOp, UnaryOp}},
diagnostic::span::Span,
lexer::types::{Token, TokenValue},
};
use super::{ParseProcessError, Parser};
impl Parser {
fn parse_primary(&mut self) -> Result<Expr, ParseProcessError> {
let token = self.next().clone();
match token.value {
TokenValue::Ident(name) => {
if matches!(self.peek().value, TokenValue::LParen) {
self.advance(1);
let mut args = vec![];
loop {
match &self.peek().value {
TokenValue::RParen => {
let end_span = self.peek().span;
self.advance(1);
return Ok(Expr {
value: ExprValue::FuncCall(name, args),
span: Span::from_two(token.span, end_span),
});
}
TokenValue::Eof => {
self.diagnostics.add_from_frontend_error(
ParseError::ExpectedBefore(TokenValue::Eof, "`)`"),
token.span,
);
return Err(ParseProcessError::ErrorInMatch);
}
_ => {}
}
args.push(self.parse_expr()?);
match &self.peek().value {
TokenValue::Comma => {
self.advance(1);
}
TokenValue::RParen => {
let end_span = self.peek().span;
self.advance(1);
return Ok(Expr {
value: ExprValue::FuncCall(name, args),
span: Span::from_two(token.span, end_span),
});
}
_ => {
let token = self.next();
self.diagnostics.add_from_frontend_error(
ParseError::ExpectedBefore(token.value, "`,` or `)`"),
token.span,
);
return Err(ParseProcessError::ErrorInMatch);
}
}
}
}
Ok(Expr {
value: ExprValue::Var(name),
span: token.span,
})
},
TokenValue::IntLit(value) => Ok(Expr {
value: ExprValue::IntLit(value),
span: token.span,
}),
TokenValue::LParen => {
let expr = self.parse_expr()?;
match &self.peek().value {
TokenValue::RParen => {
let end_span = self.peek().span;
self.advance(1);
Ok(Expr {
span: Span::from_two(token.span, end_span),
..expr
})
}
_ => {
let token = self.next();
self.diagnostics.add_from_frontend_error(
ParseError::ExpectedBefore(token.value, "`)`"),
token.span,
);
Err(ParseProcessError::ErrorInMatch)
}
}
}
_ => {
self.diagnostics.add_from_frontend_error(
ParseError::ExpectedBefore(token.value, "expression"),
token.span,
);
Err(ParseProcessError::ErrorInMatch)
}
}
}
fn parse_postfix(&mut self) -> Result<Expr, ParseProcessError> {
let mut expr = self.parse_primary()?;
loop {
if self.peek().value == TokenValue::LBracket {
let start_span = expr.span;
self.advance(1);
let index = self.parse_expr()?;
let end_span = match &self.peek().value {
TokenValue::RBracket => {
let span = self.peek().span;
self.advance(1);
span
}
_ => {
let token = self.next();
self.diagnostics.add_from_frontend_error(
ParseError::ExpectedBefore(token.value, "`]`"),
token.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),
};
continue;
}
if let TokenValue::PlusPlus | TokenValue::MinusMinus = self.peek().value {
let op = if self.peek().value == TokenValue::PlusPlus {
IncDecOp::Inc
} else {
IncDecOp::Dec
};
let end_span = self.peek().span;
self.advance(1);
expr = Expr {
span: Span::from_two(expr.span, end_span),
value: ExprValue::IncDec {
op,
operand: Box::new(expr),
is_prefix: false,
},
};
continue;
}
break;
}
Ok(expr)
}
fn parse_unary(&mut self) -> Result<Expr, ParseProcessError> {
let token = self.peek().clone();
match token.value {
TokenValue::Plus => {
self.advance(1);
let expr = self.parse_unary()?;
let span = Span::from_two(token.span, expr.span);
Ok(Expr {
value: ExprValue::UnaryOp {
op: UnaryOp::Add,
operand: Box::new(expr),
},
span,
})
}
TokenValue::Minus => {
self.advance(1);
let rhs = self.parse_unary()?;
let span = Span::from_two(token.span, rhs.span);
Ok(Expr {
value: ExprValue::UnaryOp {
op: UnaryOp::Sub,
operand: Box::new(rhs),
},
span,
})
}
TokenValue::Not => {
self.advance(1);
let rhs = self.parse_unary()?;
let span = Span::from_two(token.span, rhs.span);
Ok(Expr {
value: ExprValue::UnaryOp {
op: UnaryOp::Not,
operand: Box::new(rhs),
},
span,
})
}
TokenValue::PlusPlus | TokenValue::MinusMinus => {
let op = if token.value == TokenValue::PlusPlus {
IncDecOp::Inc
} else {
IncDecOp::Dec
};
self.advance(1);
let operand = self.parse_unary()?;
let span = Span::from_two(token.span, operand.span);
Ok(Expr {
value: ExprValue::IncDec {
op,
operand: Box::new(operand),
is_prefix: true,
},
span,
})
}
_ => self.parse_postfix(),
}
}
fn parse_multiplicative(&mut self) -> Result<Expr, ParseProcessError> {
let mut left = self.parse_unary()?;
loop {
let op = match self.peek().value {
TokenValue::Star => BinaryOp::Mul,
TokenValue::Slash => BinaryOp::Div,
TokenValue::Percent => BinaryOp::Mod,
_ => break,
};
self.advance(1);
let right = self.parse_unary()?;
let span = Span::from_two(left.span, right.span);
left = Expr {
value: ExprValue::BinaryOp {
lhs: Box::new(left),
op,
rhs: Box::new(right),
},
span,
};
}
Ok(left)
}
fn parse_additive(&mut self) -> Result<Expr, ParseProcessError> {
let mut left = self.parse_multiplicative()?;
loop {
let op = match self.peek().value {
TokenValue::Plus => BinaryOp::Add,
TokenValue::Minus => BinaryOp::Sub,
_ => break,
};
self.advance(1);
let right = self.parse_multiplicative()?;
let span = Span::from_two(left.span, right.span);
left = Expr {
value: ExprValue::BinaryOp {
lhs: Box::new(left),
op,
rhs: Box::new(right),
},
span,
};
}
Ok(left)
}
fn parse_relational(&mut self) -> Result<Expr, ParseProcessError> {
let mut left = self.parse_additive()?;
loop {
let op = match self.peek().value {
TokenValue::Less => BinaryOp::Less,
TokenValue::Greater => BinaryOp::Greater,
TokenValue::LessEqual => BinaryOp::LessEqual,
TokenValue::GreaterEqual => BinaryOp::GreaterEqual,
_ => break,
};
self.advance(1);
let right = self.parse_additive()?;
let span = Span::from_two(left.span, right.span);
left = Expr {
value: ExprValue::BinaryOp {
lhs: Box::new(left),
op,
rhs: Box::new(right),
},
span,
};
}
Ok(left)
}
fn parse_equality(&mut self) -> Result<Expr, ParseProcessError> {
let mut left = self.parse_relational()?;
loop {
let op = match self.peek().value {
TokenValue::DoubleEqual => BinaryOp::Equal,
TokenValue::NotEqual => BinaryOp::NotEqual,
_ => break,
};
self.advance(1);
let right = self.parse_relational()?;
let span = Span::from_two(left.span, right.span);
left = Expr {
value: ExprValue::BinaryOp {
lhs: Box::new(left),
op,
rhs: Box::new(right),
},
span,
};
}
Ok(left)
}
fn parse_logical_and(&mut self) -> Result<Expr, ParseProcessError> {
let mut left = self.parse_equality()?;
loop {
let op = match self.peek().value {
TokenValue::And => BinaryOp::And,
_ => break,
};
self.advance(1);
let right = self.parse_equality()?;
let span = Span::from_two(left.span, right.span);
left = Expr {
value: ExprValue::BinaryOp {
lhs: Box::new(left),
op,
rhs: Box::new(right),
},
span,
};
}
Ok(left)
}
fn parse_logical_or(&mut self) -> Result<Expr, ParseProcessError> {
let mut left = self.parse_logical_and()?;
loop {
let op = match self.peek().value {
TokenValue::Or => BinaryOp::Or,
_ => break,
};
self.advance(1);
let right = self.parse_logical_and()?;
let span = Span::from_two(left.span, right.span);
left = Expr {
value: ExprValue::BinaryOp {
lhs: Box::new(left),
op,
rhs: Box::new(right),
},
span,
};
}
Ok(left)
}
fn parse_assign(&mut self) -> Result<Expr, ParseProcessError> {
let lvalue = self.parse_logical_or()?;
let compound_op = match self.peek().value {
TokenValue::Equal => None,
TokenValue::PlusEqual => Some(BinaryOp::Add),
TokenValue::MinusEqual => Some(BinaryOp::Sub),
TokenValue::StarEqual => Some(BinaryOp::Mul),
TokenValue::SlashEqual => Some(BinaryOp::Div),
TokenValue::PercentEqual => Some(BinaryOp::Mod),
_ => return Ok(lvalue),
};
self.advance(1);
let rvalue = self.parse_assign()?;
let span = Span::from_two(lvalue.span, rvalue.span);
if let Some(op) = compound_op {
return Ok(Expr {
value: ExprValue::CompoundAssign {
lvalue: Box::new(lvalue),
op,
rvalue: Box::new(rvalue),
},
span,
});
}
Ok(Expr {
value: ExprValue::Assign {
lvalue: Box::new(lvalue),
rvalue: Box::new(rvalue),
},
span,
})
}
pub(super) fn parse_expr(&mut self) -> Result<Expr, ParseProcessError> {
self.parse_assign()
}
}
+112
View File
@@ -0,0 +1,112 @@
use crate::{
ast::err::ParseError,
ast::types::CompileUnit,
diagnostic::{Diagnositics, span::Span},
lexer::types::{Token, TokenValue},
};
mod decl;
mod expr;
mod stmt;
#[cfg(test)]
mod tests;
pub struct Parser {
tokens: Vec<Token>,
eof_token: Token,
pub diagnostics: Diagnositics,
pos: usize,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum ParseType {
MustParse,
TryParse,
}
enum ParseProcessError {
TryNext,
ErrorInMatch
}
impl Parser {
pub fn new(tokens: Vec<Token>, diagnostics: Diagnositics) -> Self {
let eof_pos = tokens.last().map_or(0, |token| token.span.end);
Self {
tokens,
eof_token: Token {
value: TokenValue::Eof,
span: Span {
start: eof_pos,
end: eof_pos,
},
},
diagnostics,
pos: 0,
}
}
pub fn parse(&mut self) -> CompileUnit {
self.parse_compile_unit()
}
fn peek(&self) -> &Token {
self.tokens.get(self.pos).unwrap_or(&self.eof_token)
}
fn peek_n(&self, n: usize) -> &Token {
self.tokens.get(self.pos + n).unwrap_or(&self.eof_token)
}
fn next(&mut self) -> Token {
let token = self.peek().clone();
if !matches!(token.value, TokenValue::Eof) {
self.pos += 1;
}
token
}
fn advance(&mut self, n: usize) {
self.pos += n;
assert!(self.pos <= self.tokens.len());
}
fn back(&mut self, n: usize) {
assert!(self.pos >= n);
self.pos -= n;
}
fn last(&self) -> &Token {
if self.pos == 0 {
&self.eof_token
} else {
self.tokens.get(self.pos - 1).unwrap_or(&self.eof_token)
}
}
fn is_eof(&self) -> bool {
matches!(self.peek().value, TokenValue::Eof)
}
fn must_match_token(&mut self, expected: &TokenValue, diagnostic_text: &'static str) -> Result<(), ParseProcessError> {
if &self.peek().value == expected {
self.advance(1);
Ok(())
} else {
let token = self.next();
self.diagnostics.add_from_frontend_error(
ParseError::ExpectedBefore(token.value, diagnostic_text),
token.span,
);
Err(ParseProcessError::ErrorInMatch)
}
}
fn must_have_some(&mut self, diagnostic_text: &'static str) -> Result<(), ParseProcessError> {
if self.is_eof() {
let span = self.last().span;
self.diagnostics
.add_from_frontend_error(ParseError::ExpectedBefore(TokenValue::Eof, diagnostic_text), span);
return Err(ParseProcessError::ErrorInMatch);
}
Ok(())
}
fn until_next_token(&mut self, expected: &[TokenValue]) {
while !self.is_eof() {
if expected.contains(&self.peek().value) {
self.advance(1);
break;
}
self.advance(1);
}
}
}
+290
View File
@@ -0,0 +1,290 @@
use crate::{
ast::err::ParseError,
ast::types::{
BlockStmt, BreakStmt, ContinueStmt, ForInit, ForStmt, IfElseBranch, IfStmt, ReturnStmt,
Statement, WhileStmt,
},
lexer::types::TokenValue,
};
use super::{ParseProcessError, ParseType, Parser};
impl Parser {
pub(super) fn parse_block_stmt(&mut self, parse_type: ParseType) -> Result<BlockStmt, ParseProcessError> {
if self.peek().value != TokenValue::LBrace {
if parse_type == ParseType::MustParse {
let token = self.next().clone();
self.diagnostics.add_from_frontend_error(
ParseError::ExpectedBefore(token.value, "`{`"),
token.span,
);
return Err(ParseProcessError::ErrorInMatch);
}
return Err(ParseProcessError::TryNext);
}
self.advance(1);
let mut statements = vec![];
// println!("parse block stmt");
loop {
if self.is_eof() {
let span = self.last().span;
self.diagnostics
.add_from_frontend_error(ParseError::ExpectedBefore(TokenValue::Eof, "`}`"), span);
return Err(ParseProcessError::ErrorInMatch);
}
if matches!(self.peek().value, TokenValue::Semicolon) {
// like a();;
self.advance(1);
continue;
}
if matches!(self.peek().value, TokenValue::RBrace) {
self.advance(1);
break;
}
// parse statement here
match self.parse_stmt() {
Ok(stmt) => statements.push(stmt),
Err(_) => {
self.until_next_token(&[TokenValue::Semicolon, TokenValue::RBrace]);
if self.last().value == TokenValue::RBrace {
break;
}
}
}
}
// println!("finish parse block stmt");
Ok(BlockStmt {
statements
})
}
fn parse_stmt(&mut self) -> Result<Statement, ParseProcessError> {
if self.peek().value == TokenValue::Semicolon {
self.advance(1);
return Ok(Statement::Block(BlockStmt { statements: vec![] }));
}
match self.parse_var_decl_stmt(ParseType::TryParse) {
Ok(var_decl) => return Ok(Statement::VarDecl(var_decl)),
Err(ParseProcessError::ErrorInMatch) => return Err(ParseProcessError::ErrorInMatch),
Err(_) => {},
};
match self.parse_return_stmt() {
Ok(return_stmt) => return Ok(Statement::Return(return_stmt)),
Err(ParseProcessError::ErrorInMatch) => return Err(ParseProcessError::ErrorInMatch),
Err(_) => {},
};
match self.parse_if_stmt() {
Ok(if_stmt) => return Ok(Statement::If(if_stmt)),
Err(ParseProcessError::ErrorInMatch) => return Err(ParseProcessError::ErrorInMatch),
Err(_) => {},
}
match self.parse_while_stmt() {
Ok(while_stmt) => return Ok(Statement::While(while_stmt)),
Err(ParseProcessError::ErrorInMatch) => return Err(ParseProcessError::ErrorInMatch),
Err(_) => {},
}
match self.parse_for_stmt() {
Ok(for_stmt) => return Ok(Statement::For(for_stmt)),
Err(ParseProcessError::ErrorInMatch) => return Err(ParseProcessError::ErrorInMatch),
Err(_) => {},
}
match self.parse_break_stmt() {
Ok(break_stmt) => return Ok(Statement::Break(break_stmt)),
Err(ParseProcessError::ErrorInMatch) => return Err(ParseProcessError::ErrorInMatch),
Err(_) => {},
}
match self.parse_continue_stmt() {
Ok(continue_stmt) => return Ok(Statement::Continue(continue_stmt)),
Err(ParseProcessError::ErrorInMatch) => return Err(ParseProcessError::ErrorInMatch),
Err(_) => {},
}
match self.parse_block_stmt(ParseType::TryParse) {
Ok(block_stmt) => return Ok(Statement::Block(block_stmt)),
Err(ParseProcessError::ErrorInMatch) => return Err(ParseProcessError::ErrorInMatch),
Err(_) => {},
}
match self.parse_expr() {
Ok(expr) => {
self.must_match_token(&TokenValue::Semicolon, "`;`")?;
return Ok(Statement::Expr(expr))
},
Err(ParseProcessError::ErrorInMatch) => return Err(ParseProcessError::ErrorInMatch),
Err(_) => {},
}
self.until_next_token(&[TokenValue::Semicolon]);
Err(ParseProcessError::ErrorInMatch)
}
fn parse_return_stmt(&mut self) -> Result<ReturnStmt, ParseProcessError> {
if self.peek().value != TokenValue::Return {
return Err(ParseProcessError::TryNext);
}
let span = self.next().span;
let value = if matches!(self.peek().value, TokenValue::Semicolon) {
None
} else {
Some(self.parse_expr()?)
};
self.must_match_token(&TokenValue::Semicolon, "`;`")?;
Ok(ReturnStmt {
value,
span,
})
}
fn parse_if_stmt(&mut self) -> Result<IfStmt, ParseProcessError> {
if self.peek().value != TokenValue::If {
return Err(ParseProcessError::TryNext);
}
self.advance(1);
self.must_match_token(&TokenValue::LParen, "`(`")?;
self.must_have_some("if condition expr")?;
let condition = self.parse_expr()?;
self.must_match_token(&TokenValue::RParen, "`)`")?;
let then_branch;
let mut ifelse_branch = vec![];
let mut else_branch = None;
self.must_have_some("if statement body")?;
if self.peek().value != TokenValue::LBrace {
let stmt = self.parse_stmt()?;
then_branch = BlockStmt { statements: vec![stmt] };
} else {
then_branch = self.parse_block_stmt(ParseType::MustParse)?;
}
loop {
if self.is_eof() {
break;
}
if self.peek().value == TokenValue::Else {
self.advance(1);
self.must_have_some("else body")?;
if self.peek().value != TokenValue::If {
self.back(1);
break;
}
// else if
self.advance(1);
self.must_match_token(&TokenValue::LParen, "`(`")?;
self.must_have_some("else if condition expr")?;
let condition = self.parse_expr()?;
self.must_match_token(&TokenValue::RParen, "`)`")?;
let then_branch;
if self.peek().value != TokenValue::LBrace {
let stmt = self.parse_stmt()?;
then_branch = BlockStmt { statements: vec![stmt] };
} else {
then_branch = self.parse_block_stmt(ParseType::MustParse)?;
}
ifelse_branch.push(IfElseBranch { condition, then_branch });
} else {
// if end ?
break;
}
}
if self.peek().value == TokenValue::Else {
// Parse else branch
self.advance(1);
self.must_have_some("else body")?;
if self.peek().value != TokenValue::LBrace {
let stmt = self.parse_stmt()?;
else_branch = Some(BlockStmt { statements: vec![stmt] });
} else {
else_branch = Some(self.parse_block_stmt(ParseType::MustParse)?);
}
}
Ok(IfStmt {
condition,
then_branch,
ifelse_branch,
else_branch,
})
}
fn parse_while_stmt(&mut self) -> Result<WhileStmt, ParseProcessError> {
if self.peek().value != TokenValue::While {
return Err(ParseProcessError::TryNext);
}
self.advance(1);
self.must_match_token(&TokenValue::LParen, "`(`")?;
self.must_have_some("while condition expr")?;
let condition = self.parse_expr()?;
self.must_match_token(&TokenValue::RParen, "`)`")?;
let body;
if self.peek().value != TokenValue::LBrace {
let stmt = self.parse_stmt()?;
body = BlockStmt { statements: vec![stmt] };
} else {
body = self.parse_block_stmt(ParseType::MustParse)?;
}
Ok(WhileStmt {
condition,
body,
})
}
fn parse_for_stmt(&mut self) -> Result<ForStmt, ParseProcessError> {
if self.peek().value != TokenValue::For {
return Err(ParseProcessError::TryNext);
}
self.advance(1);
self.must_match_token(&TokenValue::LParen, "`(`")?;
let init = if self.peek().value == TokenValue::Semicolon {
None
} else if matches!(self.peek().value, TokenValue::Static | TokenValue::TypeIdent(_)) {
Some(ForInit::VarDecl(self.parse_var_decl_stmt_inner(ParseType::MustParse, false)?))
} else {
Some(ForInit::Expr(self.parse_expr()?))
};
self.must_match_token(&TokenValue::Semicolon, "`;`")?;
let condition = if self.peek().value == TokenValue::Semicolon {
None
} else {
Some(self.parse_expr()?)
};
self.must_match_token(&TokenValue::Semicolon, "`;`")?;
let update = if self.peek().value == TokenValue::RParen {
None
} else {
Some(self.parse_expr()?)
};
self.must_match_token(&TokenValue::RParen, "`)`")?;
let body;
if self.peek().value != TokenValue::LBrace {
let stmt = self.parse_stmt()?;
body = BlockStmt { statements: vec![stmt] };
} else {
body = self.parse_block_stmt(ParseType::MustParse)?;
}
Ok(ForStmt {
init,
condition,
update,
body,
})
}
fn parse_break_stmt(&mut self) -> Result<BreakStmt, ParseProcessError> {
let start_span = self.peek().span;
if self.peek().value == TokenValue::Break {
self.advance(1);
self.must_match_token(&TokenValue::Semicolon, "`;`")?;
Ok(BreakStmt {
span: start_span,
})
} else {
Err(ParseProcessError::TryNext)
}
}
fn parse_continue_stmt(&mut self) -> Result<ContinueStmt, ParseProcessError> {
let start_span = self.peek().span;
if self.peek().value == TokenValue::Continue {
self.advance(1);
self.must_match_token(&TokenValue::Semicolon, "`;`")?;
Ok(ContinueStmt {
span: start_span,
})
} else {
Err(ParseProcessError::TryNext)
}
}
}
+76
View File
@@ -0,0 +1,76 @@
use std::io::BufRead;
use std::path::Path;
use std::fs::File;
use crate::ast::graph::AstGraphExt;
use crate::lexer::lexer::Lexer;
use crate::utils::case_list::CaseList;
use crate::utils::num_sequence::NumberSequence;
pub use super::*;
fn test_case(case_str: &str) {
let case_sequence = NumberSequence::from_str(case_str).unwrap();
let case_list = CaseList::from_dir(&Path::new("./testcases")).unwrap();
let mut error_case_cnt = 0;
for case_no in case_sequence {
let case_path = case_list.get_case_path(case_no).unwrap();
println!("{}", case_path.display());
let file = File::open(&case_path).unwrap();
let mut buf_reader = std::io::BufReader::new(file);
let mut lexer = Lexer::new();
let mut full_text = String::new();
loop {
let mut line = String::new();
let bytes_read = buf_reader.read_line(&mut line).unwrap();
if bytes_read == 0 {
break;
}
full_text.push_str(&line);
lexer.parse_next_str(&line);
}
let (tokens, diagnostics) = lexer.finish();
let mut is_error = false;
if !diagnostics.is_empty() {
diagnostics.print(&format!("{}", case_path.display()), &full_text);
is_error = true;
}
let mut parser = Parser::new(tokens, diagnostics);
let compile_unit = parser.parse_compile_unit();
let dot = compile_unit.to_dot();
let case_name = case_list.get_case_name(case_no).unwrap().strip_suffix(".c").unwrap();
std::fs::write(format!("output/{}.dot", case_name), dot).unwrap();
if !parser.diagnostics.is_empty() {
parser.diagnostics.print(&format!("{}", case_path.display()), &full_text);
is_error = true;
}
if is_error {
error_case_cnt += 1;
}
}
if error_case_cnt > 0 {
panic!("Found {} cases with errors", error_case_cnt);
}
}
#[test]
fn test_expr() {
test_case("0-3,14-25");
// test_case("0-3,14-25");
}
#[test]
fn test_if_while() {
test_case("26-32,34-41,46-51,57");
}
#[test]
fn test_error() {
test_case("999");
}
#[test]
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");
}
+14 -1
View File
@@ -8,9 +8,13 @@ pub enum GlobalDeclStmt {
VarDecl(VarDeclStmt), VarDecl(VarDeclStmt),
FuncDecl(FuncDeclStmt), FuncDecl(FuncDeclStmt),
} }
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum StorageClass {
Static,
}
pub struct VarDeclStmt { pub struct VarDeclStmt {
pub values: Vec<VarDeclStmtValue>, pub values: Vec<VarDeclStmtValue>,
pub storage_class: Option<StorageClass>,
pub data_type: Type, pub data_type: Type,
pub type_span: Span, pub type_span: Span,
} }
@@ -28,6 +32,7 @@ pub struct ArrayDimension {
pub struct FuncDeclStmt { pub struct FuncDeclStmt {
pub name: String, pub name: String,
pub storage_class: Option<StorageClass>,
pub return_type: Type, pub return_type: Type,
pub params: Vec<Param>, pub params: Vec<Param>,
pub body: BlockStmt, pub body: BlockStmt,
@@ -99,10 +104,12 @@ pub struct ReturnStmt {
pub value: Option<Expr>, pub value: Option<Expr>,
pub span: Span, pub span: Span,
} }
#[derive(Clone)]
pub struct Expr { pub struct Expr {
pub value: ExprValue, pub value: ExprValue,
pub span: Span, pub span: Span,
} }
#[derive(Clone)]
pub enum ExprValue { pub enum ExprValue {
IntLit(i64), IntLit(i64),
Var(String), Var(String),
@@ -129,6 +136,11 @@ pub enum ExprValue {
lvalue: Box<Expr>, lvalue: Box<Expr>,
rvalue: Box<Expr> rvalue: Box<Expr>
}, },
CompoundAssign {
lvalue: Box<Expr>,
op: BinaryOp,
rvalue: Box<Expr>,
},
} }
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub enum BinaryOp { pub enum BinaryOp {
@@ -305,6 +317,7 @@ impl fmt::Display for ExprValue {
ExprValue::BinaryOp { op, .. } => write!(f, "BinaryOp({})", op), ExprValue::BinaryOp { op, .. } => write!(f, "BinaryOp({})", op),
ExprValue::FuncCall(name, _) => write!(f, "FuncCall({})", name), ExprValue::FuncCall(name, _) => write!(f, "FuncCall({})", name),
ExprValue::Assign { .. } => write!(f, "Assign"), ExprValue::Assign { .. } => write!(f, "Assign"),
ExprValue::CompoundAssign { op, .. } => write!(f, "CompoundAssign({})", op),
ExprValue::UnaryOp { op, .. } => write!(f, "UnaryOp({})", op), ExprValue::UnaryOp { op, .. } => write!(f, "UnaryOp({})", op),
ExprValue::IncDec { op, is_prefix, .. } => write!(f, "{}{}", if *is_prefix { "Prefix" } else { "Postfix" }, op), ExprValue::IncDec { op, is_prefix, .. } => write!(f, "{}{}", if *is_prefix { "Prefix" } else { "Postfix" }, op),
} }
+55 -17
View File
@@ -3,8 +3,9 @@ use std::{fmt::Display, ops::Add};
use crate::{backend::register_allocator::{REG_FP, REG_LR, REG_PC, REG_R0, REG_R1, REG_R2, REG_R3, REG_R12, REG_SP, Register}, ir::types::CmpOp as IRCmpOp}; use crate::{backend::register_allocator::{REG_FP, REG_LR, REG_PC, REG_R0, REG_R1, REG_R2, REG_R3, REG_R12, REG_SP, Register}, ir::types::CmpOp as IRCmpOp};
pub enum ARMInstr{ pub enum ARMInstr{
Move(MoveInstr), Move(MoveInstr),
Movw(MovwInstr),
Movt(MovtInstr),
Load(LoadInstr), Load(LoadInstr),
LoadPseudo(LoadPseudoInstr),
Store(StoreInstr), Store(StoreInstr),
Mul(MulInstr), Mul(MulInstr),
SDiv(SDivInstr), SDiv(SDivInstr),
@@ -18,14 +19,16 @@ pub enum ARMInstr{
Bl(BlInstr), Bl(BlInstr),
B(BInstr), B(BInstr),
Label(String), Label(String),
LocalLabel(String),
} }
impl Display for ARMInstr { impl Display for ARMInstr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
ARMInstr::Move(instr) => write!(f, "{}", instr), ARMInstr::Move(instr) => write!(f, "{}", instr),
ARMInstr::Movw(instr) => write!(f, "{}", instr),
ARMInstr::Movt(instr) => write!(f, "{}", instr),
ARMInstr::Load(instr) => write!(f, "{}", instr), ARMInstr::Load(instr) => write!(f, "{}", instr),
ARMInstr::LoadPseudo(instr) => write!(f, "{}", instr),
ARMInstr::Store(instr) => write!(f, "{}", instr), ARMInstr::Store(instr) => write!(f, "{}", instr),
ARMInstr::Mul(instr) => write!(f, "{}", instr), ARMInstr::Mul(instr) => write!(f, "{}", instr),
ARMInstr::SDiv(instr) => write!(f, "{}", instr), ARMInstr::SDiv(instr) => write!(f, "{}", instr),
@@ -39,13 +42,14 @@ impl Display for ARMInstr {
ARMInstr::B(instr) => write!(f, "{}", instr), ARMInstr::B(instr) => write!(f, "{}", instr),
ARMInstr::FunctionHead(name, align_size) => write!(f, ".align {}\n.global {}\n.type {}, %function\n{}:", align_size, name, name, name), ARMInstr::FunctionHead(name, align_size) => write!(f, ".align {}\n.global {}\n.type {}, %function\n{}:", align_size, name, name, name),
ARMInstr::Label(name) => write!(f, "{}:", name), ARMInstr::Label(name) => write!(f, "{}:", name),
ARMInstr::LocalLabel(name) => write!(f, "{}:", name),
} }
} }
} }
pub enum RegisterOrImm { pub enum RegisterOrImm {
Reg(Register), Reg(Register),
Imm(i32), Imm(i32),
} }
impl Display for RegisterOrImm { impl Display for RegisterOrImm {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
@@ -73,7 +77,7 @@ impl From<IRCmpOp> for ConditionCode {
IRCmpOp::Ge => ConditionCode::Ge, IRCmpOp::Ge => ConditionCode::Ge,
} }
} }
} }
impl Display for ConditionCode { impl Display for ConditionCode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let code_str = match self { let code_str = match self {
@@ -127,6 +131,52 @@ impl Display for MoveInstr {
} }
} }
} }
pub enum MoveWideOperand {
Imm(u16),
Symbol(String, String),
}
pub struct MovwInstr(Register, MoveWideOperand);
impl MovwInstr {
pub fn new_imm(dest: Register, value: u16) -> ARMInstr {
ARMInstr::Movw(MovwInstr(dest, MoveWideOperand::Imm(value)))
}
pub fn new_symbol(dest: Register, name: String, anchor: String) -> ARMInstr {
ARMInstr::Movw(MovwInstr(dest, MoveWideOperand::Symbol(name, anchor)))
}
}
impl Display for MovwInstr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let MovwInstr(dest, operand) = self;
match operand {
MoveWideOperand::Imm(value) => write!(f, "movw {}, #{}", dest, value),
MoveWideOperand::Symbol(name, anchor) => write!(f, "movw {}, #:lower16:{}-({}+8)", dest, name, anchor),
}
}
}
pub struct MovtInstr(Register, MoveWideOperand);
impl MovtInstr {
pub fn new_imm(dest: Register, value: u16) -> ARMInstr {
ARMInstr::Movt(MovtInstr(dest, MoveWideOperand::Imm(value)))
}
pub fn new_symbol(dest: Register, name: String, anchor: String) -> ARMInstr {
ARMInstr::Movt(MovtInstr(dest, MoveWideOperand::Symbol(name, anchor)))
}
}
impl Display for MovtInstr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let MovtInstr(dest, operand) = self;
match operand {
MoveWideOperand::Imm(value) => write!(f, "movt {}, #{}", dest, value),
MoveWideOperand::Symbol(name, anchor) => write!(f, "movt {}, #:upper16:{}-({}+8)", dest, name, anchor),
}
}
}
pub struct LoadInstr(Register, Register, Option<RegisterOrImm>); pub struct LoadInstr(Register, Register, Option<RegisterOrImm>);
impl LoadInstr { impl LoadInstr {
pub fn new(dest: Register, base: Register, offset: Option<RegisterOrImm>) -> ARMInstr { pub fn new(dest: Register, base: Register, offset: Option<RegisterOrImm>) -> ARMInstr {
@@ -146,18 +196,6 @@ impl Display for LoadInstr {
} }
} }
} }
pub struct LoadPseudoInstr(Register, String);
impl LoadPseudoInstr {
pub fn new(dest: Register, name: String) -> ARMInstr {
ARMInstr::LoadPseudo(LoadPseudoInstr(dest, name))
}
}
impl Display for LoadPseudoInstr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let LoadPseudoInstr(dest, name) = self;
write!(f, "ldr {}, ={}", dest, name)
}
}
pub struct StoreInstr(Register, Register, Option<RegisterOrImm>); pub struct StoreInstr(Register, Register, Option<RegisterOrImm>);
impl StoreInstr { impl StoreInstr {
pub fn new(src: Register, dest: Register, offset: Option<RegisterOrImm>) -> ARMInstr { pub fn new(src: Register, dest: Register, offset: Option<RegisterOrImm>) -> ARMInstr {
@@ -347,4 +385,4 @@ impl BInstr {
pub fn new_cond(condition: ConditionCode, label_name: String) -> ARMInstr { pub fn new_cond(condition: ConditionCode, label_name: String) -> ARMInstr {
ARMInstr::B(BInstr(Some(condition), label_name)) ARMInstr::B(BInstr(Some(condition), label_name))
} }
} }
+72 -45
View File
@@ -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_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::{backend::{arm_instr::{ARMInstr, AddInstr, BInstr, BlInstr, CmpInstr, ConditionCode, LoadInstr, MoveInstr, MovtInstr, MovwInstr, MulInstr, PopInstr, PushInstr, RegisterOrImm, RsbInstr, SDivInstr, StoreInstr, SubInstr}, register_allocator::{REG_FP, REG_PC, 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;
@@ -11,6 +11,7 @@ pub struct Generator {
var_inited: Vec<ARMAsmVar>, var_inited: Vec<ARMAsmVar>,
var_uninited: Vec<ARMAsmVar>, var_uninited: Vec<ARMAsmVar>,
register_allocator: RegisterAllocator, register_allocator: RegisterAllocator,
symbol_addr_label_counter: usize,
} }
const DEFAULT_VAR_ALIGN: usize = 4; const DEFAULT_VAR_ALIGN: usize = 4;
@@ -19,6 +20,17 @@ const ARG_REGS: [Register; 4] = [REG_R0, REG_R1, REG_R2, REG_R3];
const ARM_DATA_IMM_CHUNK: i32 = 255; const ARM_DATA_IMM_CHUNK: i32 = 255;
const ARM_LOAD_STORE_MAX_OFFSET: i32 = 4095; const ARM_LOAD_STORE_MAX_OFFSET: i32 = 4095;
fn emit_load_imm32(dest: Register, value: i32, instrs: &mut Vec<ARMInstr>) {
let bits = value as u32;
let lower = (bits & 0xffff) as u16;
let upper = (bits >> 16) as u16;
instrs.push(MovwInstr::new_imm(dest, lower));
if upper != 0 {
instrs.push(MovtInstr::new_imm(dest, upper));
}
}
fn emit_sub_imm(dest: Register, left: Register, imm: i32, instrs: &mut Vec<ARMInstr>) { fn emit_sub_imm(dest: Register, left: Register, imm: i32, instrs: &mut Vec<ARMInstr>) {
if imm == 0 { if imm == 0 {
if dest != left { if dest != left {
@@ -75,33 +87,34 @@ fn emit_store_stack(src: Register, offset: i32, reg_allocator: &mut RegisterAllo
} }
} }
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, generator: &mut Generator, var_index_to_stack_offset: &BTreeMap<usize, usize>) -> RegisterAlloc {
let reg_allocator = &mut generator.register_allocator;
if variable.data_type.is_array() { if variable.data_type.is_array() {
let var_alloc = reg_allocator.alloc(variable.clone()).expect("Ran out of registers"); let var_alloc = reg_allocator.alloc(variable.clone()).expect("Ran out of registers");
if variable.data_type.is_array_param() { if variable.data_type.is_array_param() {
match variable.var_type { match variable.var_type {
VariableType::ParamTemp(param_index) => { VariableType::ParamTemp(param_index) => {
if param_index < ARG_REGS.len() { if param_index < ARG_REGS.len() {
instrs.push(MoveInstr::new_uncond(var_alloc.reg, RegisterOrImm::Reg(ARG_REGS[param_index]))); generator.instrs.push(MoveInstr::new_uncond(var_alloc.reg, RegisterOrImm::Reg(ARG_REGS[param_index])));
} else { } else {
let stack_arg_offset = 8 + ((param_index - ARG_REGS.len()) * 4); 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)))); generator.instrs.push(LoadInstr::new(var_alloc.reg, REG_FP, Some(RegisterOrImm::Imm(stack_arg_offset as i32))));
} }
} }
_ => { _ => {
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");
emit_load_stack(var_alloc.reg, *stack_offset as i32, reg_allocator, instrs); emit_load_stack(var_alloc.reg, *stack_offset as i32, &mut generator.register_allocator, &mut generator.instrs);
} }
} }
return var_alloc; return var_alloc;
} }
match variable.var_type { match variable.var_type {
VariableType::Global => { VariableType::Global => {
instrs.push(LoadPseudoInstr::new(var_alloc.reg, format!("global_var_{}", variable.index))); generator.emit_load_symbol_addr(var_alloc.reg, format!("global_var_{}", variable.index));
} }
_ => { _ => {
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");
emit_sub_imm(var_alloc.reg, REG_FP, *stack_offset as i32, instrs); emit_sub_imm(var_alloc.reg, REG_FP, *stack_offset as i32, &mut generator.instrs);
} }
} }
return var_alloc; return var_alloc;
@@ -112,8 +125,8 @@ fn load_variable(variable: Variable, reg_allocator: &mut RegisterAllocator, var_
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");
instrs.push(LoadPseudoInstr::new(address_alloc.reg, format!("global_var_{}", variable.index))); generator.emit_load_symbol_addr(address_alloc.reg, format!("global_var_{}", variable.index));
instrs.push(LoadInstr::new(var_reg, address_alloc.reg, None)); generator.instrs.push(LoadInstr::new(var_reg, address_alloc.reg, None));
// } // }
var_alloc var_alloc
}, },
@@ -125,7 +138,7 @@ fn load_variable(variable: Variable, reg_allocator: &mut RegisterAllocator, var_
} else { } else {
let var_alloc = reg_allocator.alloc(variable.clone()).expect("Ran out of registers"); let var_alloc = reg_allocator.alloc(variable.clone()).expect("Ran out of registers");
let stack_arg_offset = 8 + ((param_index - ARG_REGS.len()) * 4); 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)))); generator.instrs.push(LoadInstr::new(var_alloc.reg, REG_FP, Some(RegisterOrImm::Imm(stack_arg_offset as i32))));
var_alloc var_alloc
} }
}, },
@@ -133,26 +146,26 @@ fn load_variable(variable: Variable, reg_allocator: &mut RegisterAllocator, var_
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.clone()).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 {
emit_load_stack(var_alloc.reg, *stack_offset as i32, reg_allocator, instrs); emit_load_stack(var_alloc.reg, *stack_offset as i32, &mut generator.register_allocator, &mut generator.instrs);
// } // }
var_alloc var_alloc
} }
} }
} }
fn save_variable(variable: Variable, reg: Register, reg_allocator: &mut RegisterAllocator, var_index_to_stack_offset: &BTreeMap<usize, usize>, instrs: &mut Vec<ARMInstr>) { fn save_variable(variable: Variable, reg: Register, generator: &mut Generator, var_index_to_stack_offset: &BTreeMap<usize, usize>) {
match variable.var_type { match variable.var_type {
VariableType::Global => { VariableType::Global => {
let address_alloc = reg_allocator.alloc_any().expect("Ran out of registers"); let address_alloc = generator.register_allocator.alloc_any().expect("Ran out of registers");
instrs.push(LoadPseudoInstr::new(address_alloc.reg, format!("global_var_{}", variable.index))); generator.emit_load_symbol_addr(address_alloc.reg, format!("global_var_{}", variable.index));
instrs.push(StoreInstr::new(reg, address_alloc.reg, None)); generator.instrs.push(StoreInstr::new(reg, address_alloc.reg, None));
}, },
VariableType::ParamTemp(_) => { VariableType::ParamTemp(_) => {
todo!() todo!()
}, },
_ => { _ => {
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");
emit_store_stack(reg, *stack_offset as i32, reg_allocator, instrs); emit_store_stack(reg, *stack_offset as i32, &mut generator.register_allocator, &mut generator.instrs);
} }
} }
} }
@@ -163,13 +176,22 @@ impl Generator {
var_inited: Vec::new(), var_inited: Vec::new(),
var_uninited: Vec::new(), var_uninited: Vec::new(),
register_allocator: RegisterAllocator::new(), register_allocator: RegisterAllocator::new(),
symbol_addr_label_counter: 0,
} }
} }
fn emit_load_symbol_addr(&mut self, dest: Register, name: String) {
let anchor = format!(".Laddr_{}", self.symbol_addr_label_counter);
self.symbol_addr_label_counter += 1;
self.instrs.push(MovwInstr::new_symbol(dest, name.clone(), anchor.clone()));
self.instrs.push(MovtInstr::new_symbol(dest, name, anchor.clone()));
self.instrs.push(ARMInstr::LocalLabel(anchor));
self.instrs.push(AddInstr::new(dest, REG_PC, RegisterOrImm::Reg(dest)));
}
pub fn emit(&mut self, ir_instrs: Vec<IRInstr>) { pub fn emit(&mut self, ir_instrs: Vec<IRInstr>) {
for ir_instr in ir_instrs { for ir_instr in ir_instrs {
match ir_instr { match ir_instr {
IRInstr::DefineFunc(func, args, body) => self.emit_func_def(func, args, body), IRInstr::DefineFunc(func, args, body) => self.emit_func_def(func, args, body),
IRInstr::Declare(var) => self.emit_global_decl(var), IRInstr::DeclareGlobal(var, init) => self.emit_global_decl(var, init),
_ => unreachable!(), _ => unreachable!(),
} }
} }
@@ -181,8 +203,8 @@ impl Generator {
text.push_str(&format!(".comm {}, {}, {}\n", var.name, var.size, var.align)); text.push_str(&format!(".comm {}, {}, {}\n", var.name, var.size, var.align));
} }
for var in &self.var_inited { for var in &self.var_inited {
text.push_str(&format!(".data\n.align {}\n.global {}\n.type {}, @object\n:{}\n", var.align, var.name, var.name, var.name)); text.push_str(&format!(".data\n.align {}\n.global {}\n.type {}, %object\n{}:\n", var.align, var.name, var.name, var.name));
text.push_str(&format!(".word 0\n")); text.push_str(&format!(".word {}\n", var.init.unwrap_or(0)));
} }
text.push_str(".text\n"); text.push_str(".text\n");
for instr in &self.instrs { for instr in &self.instrs {
@@ -191,13 +213,18 @@ impl Generator {
text text
} }
fn emit_global_decl(&mut self, var: Variable, init: Option<i32>) {
fn emit_global_decl(&mut self, var: Variable) { let asm_var = ARMAsmVar {
self.var_uninited.push(ARMAsmVar {
name: format!("global_var_{}", var.index), name: format!("global_var_{}", var.index),
size: var.data_type.size_in_bytes(), size: var.data_type.size_in_bytes(),
align: DEFAULT_VAR_ALIGN, align: DEFAULT_VAR_ALIGN,
}); init,
};
if asm_var.init.is_some() {
self.var_inited.push(asm_var);
} else {
self.var_uninited.push(asm_var);
}
} }
fn emit_func_def(&mut self, func: Function, _args: Vec<Variable>, body: Vec<IRInstr>) { fn emit_func_def(&mut self, func: Function, _args: Vec<Variable>, body: Vec<IRInstr>) {
@@ -218,7 +245,7 @@ impl Generator {
if let Some(v) = v { if let Some(v) = v {
let ret_alloc = self.register_allocator.alloc_reg(REG_R0).expect("Ran out of registers"); let ret_alloc = self.register_allocator.alloc_reg(REG_R0).expect("Ran out of registers");
let ret_reg = ret_alloc.reg; let ret_reg = ret_alloc.reg;
let v_alloc = load_variable(v, &mut self.register_allocator, &var_index_to_stack_offset, &mut self.instrs); let v_alloc = load_variable(v, self, &var_index_to_stack_offset);
self.instrs.push(MoveInstr::new_uncond(ret_reg, RegisterOrImm::Reg(v_alloc.reg))); self.instrs.push(MoveInstr::new_uncond(ret_reg, RegisterOrImm::Reg(v_alloc.reg)));
} }
self.instrs.push(MoveInstr::new_fp_to_sp()); self.instrs.push(MoveInstr::new_fp_to_sp());
@@ -239,12 +266,12 @@ impl Generator {
encounter_entry = true; encounter_entry = true;
emit_sub_sp_imm(stack_size_needed as i32, &mut self.instrs); emit_sub_sp_imm(stack_size_needed as i32, &mut self.instrs);
}, },
IRInstr::DefineFunc(_, _, _) => unreachable!(), IRInstr::DefineFunc(_, _, _) | IRInstr::DeclareGlobal(_, _) => 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),
IRInstr::Unary(variable, unary_op, variable1) => self.emit_unary(variable, unary_op, variable1, &var_index_to_stack_offset), IRInstr::Unary(variable, unary_op, variable1) => self.emit_unary(variable, unary_op, variable1, &var_index_to_stack_offset),
IRInstr::Goto(index) => self.instrs.push(BInstr::new(format!("label_{}_{}", func_name, index))), IRInstr::Goto(index) => self.instrs.push(BInstr::new(format!("label_{}_{}", func_name, index))),
IRInstr::CondGoto(variable, true_label_index, false_label_index) => { IRInstr::CondGoto(variable, true_label_index, false_label_index) => {
let variable_alloc = load_variable(variable, &mut self.register_allocator, &var_index_to_stack_offset, &mut self.instrs); let variable_alloc = load_variable(variable, self, &var_index_to_stack_offset);
let variable_reg = variable_alloc.reg; let variable_reg = variable_alloc.reg;
self.instrs.push(CmpInstr::new(variable_reg, RegisterOrImm::Imm(0))); self.instrs.push(CmpInstr::new(variable_reg, RegisterOrImm::Imm(0)));
self.instrs.push(BInstr::new_cond(ConditionCode::Ne, format!("label_{}_{}", func_name, true_label_index))); self.instrs.push(BInstr::new_cond(ConditionCode::Ne, format!("label_{}_{}", func_name, true_label_index)));
@@ -256,34 +283,34 @@ 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, self, var_index_to_stack_offset);
let dest_alloc = self.register_allocator.alloc(dest.clone()).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)));
}, },
} }
save_variable(dest, dest_alloc.reg, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs); save_variable(dest, dest_alloc.reg, self, var_index_to_stack_offset);
} }
fn emit_cmp(&mut self, variable: Variable, left: VariableOrIntLit, cmp_op: IRCmpOp, right: VariableOrIntLit, var_index_to_stack_offset: &BTreeMap<usize, usize>) { fn emit_cmp(&mut self, variable: Variable, left: VariableOrIntLit, cmp_op: IRCmpOp, right: VariableOrIntLit, var_index_to_stack_offset: &BTreeMap<usize, usize>) {
let left_alloc = match left { let left_alloc = match left {
VariableOrIntLit::Var(var) => { VariableOrIntLit::Var(var) => {
load_variable(var, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs) load_variable(var, self, var_index_to_stack_offset)
}, },
VariableOrIntLit::IntLit(lit) => { VariableOrIntLit::IntLit(lit) => {
let alloc = self.register_allocator.alloc_any().expect("Ran out of registers"); let alloc = self.register_allocator.alloc_any().expect("Ran out of registers");
self.instrs.push(MoveInstr::new_uncond(alloc.reg, RegisterOrImm::Imm(lit))); emit_load_imm32(alloc.reg, lit, &mut self.instrs);
alloc alloc
}, },
}; };
// TODO: check left == right // TODO: check left == right
let right_alloc = match right { let right_alloc = match right {
VariableOrIntLit::Var(var) => { VariableOrIntLit::Var(var) => {
load_variable(var, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs) load_variable(var, self, var_index_to_stack_offset)
}, },
VariableOrIntLit::IntLit(lit) => { VariableOrIntLit::IntLit(lit) => {
let alloc = self.register_allocator.alloc_any().expect("Ran out of registers"); let alloc = self.register_allocator.alloc_any().expect("Ran out of registers");
self.instrs.push(MoveInstr::new_uncond(alloc.reg, RegisterOrImm::Imm(lit))); emit_load_imm32(alloc.reg, lit, &mut self.instrs);
alloc alloc
}, },
}; };
@@ -292,7 +319,7 @@ impl Generator {
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)));
self.instrs.push(MoveInstr::new_cond(cmp_op.into(), variable_reg, RegisterOrImm::Imm(1))); self.instrs.push(MoveInstr::new_cond(cmp_op.into(), variable_reg, RegisterOrImm::Imm(1)));
save_variable(variable, variable_alloc.reg, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs); save_variable(variable, variable_alloc.reg, self, var_index_to_stack_offset);
} }
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>) {
@@ -307,7 +334,7 @@ impl Generator {
} }
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() {
let arg_alloc = load_variable(arg, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs); let arg_alloc = load_variable(arg, self, var_index_to_stack_offset);
if i < ARG_REGS.len() { if i < ARG_REGS.len() {
arg_reg_allocs.push(self.register_allocator.alloc_reg(ARG_REGS[i]).expect("Ran out of registers")); 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))); self.instrs.push(MoveInstr::new_uncond(ARG_REGS[i], RegisterOrImm::Reg(arg_alloc.reg)));
@@ -318,7 +345,7 @@ impl Generator {
} }
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, self, var_index_to_stack_offset);
} }
if extra_stack_size > 0 { if extra_stack_size > 0 {
@@ -332,24 +359,24 @@ impl Generator {
let dest_register = dest_alloc.reg; let dest_register = dest_alloc.reg;
match src { match src {
MoveRValue::Var(variable) => { MoveRValue::Var(variable) => {
let src_alloc = load_variable(variable, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs); let src_alloc = load_variable(variable, self, var_index_to_stack_offset);
self.instrs.push(MoveInstr::new_uncond(dest_register, RegisterOrImm::Reg(src_alloc.reg))); self.instrs.push(MoveInstr::new_uncond(dest_register, RegisterOrImm::Reg(src_alloc.reg)));
}, },
MoveRValue::ConstInt(literal_int) => self.instrs.push(MoveInstr::new_uncond(dest_register, RegisterOrImm::Imm(literal_int))), MoveRValue::ConstInt(literal_int) => emit_load_imm32(dest_register, literal_int, &mut self.instrs),
}; };
save_variable(dest, dest_alloc.reg, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs); save_variable(dest, dest_alloc.reg, self, var_index_to_stack_offset);
} }
fn emit_load(&mut self, dest: Variable, addr: Variable, var_index_to_stack_offset: &BTreeMap<usize, usize>) { 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 addr_alloc = load_variable(addr, self, var_index_to_stack_offset);
let dest_alloc = self.register_allocator.alloc(dest.clone()).expect("Ran out of registers"); 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)); 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); save_variable(dest, dest_alloc.reg, self, var_index_to_stack_offset);
} }
fn emit_store(&mut self, addr: Variable, value: Variable, var_index_to_stack_offset: &BTreeMap<usize, usize>) { 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 addr_alloc = load_variable(addr, self, var_index_to_stack_offset);
let value_alloc = load_variable(value, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs); let value_alloc = load_variable(value, self, var_index_to_stack_offset);
self.instrs.push(StoreInstr::new(value_alloc.reg, addr_alloc.reg, None)); self.instrs.push(StoreInstr::new(value_alloc.reg, addr_alloc.reg, None));
} }
@@ -357,9 +384,9 @@ impl Generator {
let dest_alloc = self.register_allocator.alloc(dest.clone()).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.clone(), &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs); let left_alloc = load_variable(left.clone(), self, var_index_to_stack_offset);
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, self, var_index_to_stack_offset);
let right_reg = right_alloc.reg; let right_reg = right_alloc.reg;
(Some(right_alloc), right_reg) (Some(right_alloc), right_reg)
} else { } else {
@@ -386,7 +413,7 @@ impl Generator {
self.instrs.push(SubInstr::new(dest_reg, left_alloc.reg, RegisterOrImm::Reg(temp_reg))); self.instrs.push(SubInstr::new(dest_reg, left_alloc.reg, RegisterOrImm::Reg(temp_reg)));
}, },
} }
save_variable(dest, dest_alloc.reg, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs); save_variable(dest, dest_alloc.reg, self, var_index_to_stack_offset);
} }
} }
+2 -1
View File
@@ -2,4 +2,5 @@ pub struct ARMAsmVar {
pub name: String, pub name: String,
pub size: usize, pub size: usize,
pub align: usize, pub align: usize,
} pub init: Option<i32>,
}
+180 -29
View File
@@ -1,6 +1,6 @@
use std::{collections::BTreeMap, vec}; use std::{collections::BTreeMap, vec};
use crate::{ir::types::{BinaryOp as IRBinaryOp, CmpOp, Function, IRInstr, IRType, MoveRValue, UnaryOp, Variable, VariableOrIntLit, VariableType}, sema::{analyzer::Analyzer as SemaAnalyzer, hir::{HirBlockStmt, HirBreakStmt, HirCompileUnit, HirContinueStmt, HirExpr, HirExprValue, HirForInit, HirForStmt, HirFuncDeclStmt, HirGlobalDeclStmt, HirIfElseBranch, HirIfStmt, HirReturnStmt, HirStatement, HirVarDeclStmt, HirWhileStmt}, symbol::{FunctionId, SymbolId}}}; use crate::{ir::types::{BinaryOp as IRBinaryOp, CmpOp, Function, IRInstr, IRType, MoveRValue, UnaryOp, Variable, VariableOrIntLit, VariableType}, sema::{analyzer::Analyzer as SemaAnalyzer, hir::{HirBlockStmt, HirBreakStmt, HirCompileUnit, HirContinueStmt, HirExpr, HirExprValue, HirForInit, HirForStmt, HirFuncDeclStmt, HirGlobalDeclStmt, HirIfElseBranch, HirIfStmt, HirReturnStmt, HirStatement, HirVarDeclStmt, HirWhileStmt}, symbol::{FunctionId, SymbolId, SymbolKind}}};
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<'a> { pub struct Generator<'a> {
@@ -14,6 +14,7 @@ pub struct Generator<'a> {
// if child expr isn't logical, we need to do cmp to decide which label to goto // if child expr isn't logical, we need to do cmp to decide which label to goto
while_exit_label: Vec<(usize, usize)>, // continue exit, break exit while_exit_label: Vec<(usize, usize)>, // continue exit, break exit
func_exit: Option<(usize, Option<Variable>)>, // (label, return_var) func_exit: Option<(usize, Option<Variable>)>, // (label, return_var)
extra_global_instrs: Vec<IRInstr>,
label_counter: usize label_counter: usize
} }
@@ -25,6 +26,7 @@ impl<'a> Generator<'a> {
current_exit_label: vec![], current_exit_label: vec![],
while_exit_label: vec![], while_exit_label: vec![],
func_exit: None, func_exit: None,
extra_global_instrs: vec![],
label_counter: 0, label_counter: 0,
} }
} }
@@ -55,7 +57,10 @@ impl<'a> Generator<'a> {
instrs.extend(self.generate_var_decl(var_decl, true)); instrs.extend(self.generate_var_decl(var_decl, true));
} }
FuncDecl(func_decl) => { FuncDecl(func_decl) => {
instrs.extend(self.generate_func_decl(func_decl)); let extra_global_start = self.extra_global_instrs.len();
let func_instrs = self.generate_func_decl(func_decl);
instrs.extend(self.extra_global_instrs.drain(extra_global_start..));
instrs.extend(func_instrs);
} }
} }
} }
@@ -65,13 +70,21 @@ impl<'a> Generator<'a> {
fn generate_var_decl(&mut self, var_decl: HirVarDeclStmt, 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![];
for value in var_decl.values { for value in var_decl.values {
let var_type = if is_global { VariableType::Global } else { VariableType::Local }; let symbol_kind = self.sema.get_symbol_kind(value.symbol);
let has_static_storage = is_global || symbol_kind == SymbolKind::StaticLocal || value.is_static_local;
let var_type = if has_static_storage { VariableType::Global } else { VariableType::Local };
let var = self.var_manager.declare_symbol(value.symbol, var_type, self.sema.get_symbol_type(value.symbol).into()); let var = self.var_manager.declare_symbol(value.symbol, var_type, self.sema.get_symbol_type(value.symbol).into());
if is_global { if has_static_storage {
instrs.push(IRInstr::Declare(var)); let init = value.value.as_ref().and_then(|expr| Self::const_init_value(expr));
let decl = IRInstr::DeclareGlobal(var, init);
if is_global {
instrs.push(decl);
} else {
self.extra_global_instrs.push(decl);
}
} else if let Some(init) = value.value { } else if let Some(init) = value.value {
self.current_exit_label.push(None); self.current_exit_label.push(None);
let (init_instrs, init_var) = match self.generate_expr(init) { let (mut init_instrs, init_var) = match self.generate_expr(init) {
Some(res) => res, Some(res) => res,
None => { None => {
self.current_exit_label.pop(); self.current_exit_label.pop();
@@ -80,14 +93,56 @@ impl<'a> Generator<'a> {
}; };
self.current_exit_label.pop(); self.current_exit_label.pop();
let target = self.var_manager.get_symbol(value.symbol).unwrap(); let target = self.var_manager.get_symbol(value.symbol).unwrap();
let init_var = self.coerce_value(&mut init_instrs, init_var.unwrap(), &target.data_type);
instrs.extend(init_instrs); instrs.extend(init_instrs);
instrs.push(IRInstr::Move(target, MoveRValue::Var(init_var.unwrap()))); instrs.push(IRInstr::Move(target, MoveRValue::Var(init_var)));
} }
} }
instrs instrs
} }
fn const_init_value(expr: &HirExpr) -> Option<i32> {
match &expr.value {
HirExprValue::IntLit(value) => Some(*value as i32),
HirExprValue::UnaryOp { op, operand } => {
let value = Self::const_init_value(operand)?;
match op {
AstUnaryOp::Add => Some(value),
AstUnaryOp::Sub => Some(value.wrapping_neg()),
AstUnaryOp::Not => Some((value == 0) as i32),
}
}
HirExprValue::BinaryOp { lhs, op, rhs } => {
let lhs = Self::const_init_value(lhs)?;
match op {
AstBinaryOp::And if lhs == 0 => return Some(0),
AstBinaryOp::Or if lhs != 0 => return Some(1),
_ => {}
}
let rhs = Self::const_init_value(rhs)?;
match op {
AstBinaryOp::Add => Some(lhs.wrapping_add(rhs)),
AstBinaryOp::Sub => Some(lhs.wrapping_sub(rhs)),
AstBinaryOp::Mul => Some(lhs.wrapping_mul(rhs)),
AstBinaryOp::Div if rhs != 0 => Some(lhs.wrapping_div(rhs)),
AstBinaryOp::Div => None,
AstBinaryOp::Mod if rhs != 0 => Some(lhs.wrapping_rem(rhs)),
AstBinaryOp::Mod => None,
AstBinaryOp::Equal => Some((lhs == rhs) as i32),
AstBinaryOp::NotEqual => Some((lhs != rhs) as i32),
AstBinaryOp::Less => Some((lhs < rhs) as i32),
AstBinaryOp::LessEqual => Some((lhs <= rhs) as i32),
AstBinaryOp::Greater => Some((lhs > rhs) as i32),
AstBinaryOp::GreaterEqual => Some((lhs >= rhs) as i32),
AstBinaryOp::And => Some((lhs != 0 && rhs != 0) as i32),
AstBinaryOp::Or => Some((lhs != 0 || rhs != 0) as i32),
}
}
_ => None,
}
}
fn generate_func_decl(&mut self, func_decl: HirFuncDeclStmt) -> Vec<IRInstr> { fn generate_func_decl(&mut self, func_decl: HirFuncDeclStmt) -> Vec<IRInstr> {
let parameters: Vec<Variable> = func_decl.params.iter() let parameters: Vec<Variable> = func_decl.params.iter()
.map(|param| self.var_manager.declare_symbol(param.symbol, VariableType::Local, param.param_type.clone().into())) .map(|param| self.var_manager.declare_symbol(param.symbol, VariableType::Local, param.param_type.clone().into()))
@@ -167,7 +222,7 @@ impl<'a> Generator<'a> {
match return_stmt.value { match return_stmt.value {
Some(expr) => { Some(expr) => {
self.current_exit_label.push(None); self.current_exit_label.push(None);
let (value_instrs, value_var) = match self.generate_expr(expr) { let (mut value_instrs, value_var) = match self.generate_expr(expr) {
Some(res) => res, Some(res) => res,
None => { None => {
self.current_exit_label.pop(); self.current_exit_label.pop();
@@ -175,8 +230,10 @@ impl<'a> Generator<'a> {
} }
}; };
self.current_exit_label.pop(); self.current_exit_label.pop();
let target = func_exit.1.unwrap();
let value_var = self.coerce_value(&mut value_instrs, value_var.unwrap(), &target.data_type);
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(target, MoveRValue::Var(value_var)));
} }
None => {}, None => {},
} }
@@ -340,6 +397,35 @@ impl<'a> Generator<'a> {
vec![] vec![]
} }
} }
fn coerce_value(&mut self, instrs: &mut Vec<IRInstr>, var: Variable, target_type: &IRType) -> Variable {
if &var.data_type == target_type {
return var;
}
match (&var.data_type, target_type) {
(IRType::I1, IRType::I32) => {
let dest = self.var_manager.declare_unamed_local(IRType::I32);
let true_label = self.request_label();
let false_label = self.request_label();
let final_label = self.request_label();
instrs.push(IRInstr::CondGoto(var, true_label, false_label));
instrs.push(IRInstr::Label(true_label));
instrs.push(IRInstr::Move(dest.clone(), MoveRValue::ConstInt(1)));
instrs.push(IRInstr::Goto(final_label));
instrs.push(IRInstr::Label(false_label));
instrs.push(IRInstr::Move(dest.clone(), MoveRValue::ConstInt(0)));
instrs.push(IRInstr::Goto(final_label));
instrs.push(IRInstr::Label(final_label));
dest
}
(IRType::I32, IRType::I1) => {
let dest = self.var_manager.declare_temp(IRType::I1);
instrs.push(IRInstr::Cmp(dest.clone(), VariableOrIntLit::Var(var), CmpOp::Ne, VariableOrIntLit::IntLit(0)));
dest
}
_ => var,
}
}
fn generate_expr(&mut self, expr: HirExpr) -> 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 {
@@ -367,12 +453,52 @@ impl<'a> Generator<'a> {
self.current_exit_label.push(None); self.current_exit_label.push(None);
let (mut instrs, rvalue_var) = self.generate_expr(*rvalue)?; let (mut instrs, rvalue_var) = self.generate_expr(*rvalue)?;
self.current_exit_label.pop(); self.current_exit_label.pop();
let rvalue_var = self.coerce_value(&mut instrs, rvalue_var.unwrap(), &lvalue_ty);
let (lvalue_instrs, target, is_addr) = self.generate_lvalue(*lvalue)?; let (lvalue_instrs, target, is_addr) = self.generate_lvalue(*lvalue)?;
instrs.extend(lvalue_instrs); instrs.extend(lvalue_instrs);
if is_addr { if is_addr {
instrs.push(IRInstr::Store(target.clone(), rvalue_var.unwrap())); instrs.push(IRInstr::Store(target.clone(), rvalue_var));
} else { } else {
instrs.push(IRInstr::Move(target.clone(), MoveRValue::Var(rvalue_var.unwrap()))); instrs.push(IRInstr::Move(target.clone(), MoveRValue::Var(rvalue_var)));
}
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::CompoundAssign { lvalue, op, rvalue } => {
let lvalue_ty: IRType = lvalue.ty.clone().into();
let (mut instrs, target, is_addr) = self.generate_lvalue(*lvalue)?;
let mut old_var = self.var_manager.declare_temp(lvalue_ty.clone());
if is_addr {
instrs.push(IRInstr::Load(old_var.clone(), target.clone()));
} else {
instrs.push(IRInstr::Move(old_var.clone(), MoveRValue::Var(target.clone())));
}
self.current_exit_label.push(None);
let (rvalue_instrs, rvalue_var) = self.generate_expr(*rvalue)?;
self.current_exit_label.pop();
instrs.extend(rvalue_instrs);
let mut rvalue_var = rvalue_var.unwrap();
let convert_to = IRType::get_elevate_result(&old_var.data_type, &rvalue_var.data_type).unwrap();
if convert_to != old_var.data_type {
old_var = self.coerce_value(&mut instrs, old_var, &convert_to);
}
if convert_to != rvalue_var.data_type {
rvalue_var = self.coerce_value(&mut instrs, rvalue_var, &convert_to);
}
let computed = self.var_manager.declare_temp(convert_to);
instrs.push(IRInstr::Binary(computed.clone(), old_var, op.into(), rvalue_var));
let stored = self.coerce_value(&mut instrs, computed, &lvalue_ty);
if is_addr {
instrs.push(IRInstr::Store(target.clone(), stored));
} else {
instrs.push(IRInstr::Move(target.clone(), MoveRValue::Var(stored)));
} }
let temp_var = self.var_manager.declare_temp(lvalue_ty); let temp_var = self.var_manager.declare_temp(lvalue_ty);
if is_addr { if is_addr {
@@ -423,17 +549,22 @@ impl<'a> Generator<'a> {
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.clone()); let target_ty = if operand_var.data_type == IRType::I1 { IRType::I32 } else { operand_var.data_type.clone() };
let operand_var = self.coerce_value(&mut instrs, operand_var, &target_ty);
let dest_var = self.var_manager.declare_temp(target_ty);
instrs.push(IRInstr::Move(dest_var.clone(), 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.clone()); let target_ty = if operand_var.data_type == IRType::I1 { IRType::I32 } else { operand_var.data_type.clone() };
let operand_var = self.coerce_value(&mut instrs, operand_var, &target_ty);
let dest_var = self.var_manager.declare_temp(target_ty);
instrs.push(IRInstr::Unary(dest_var.clone(), 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.clone()); let dest_ty = if parent_is_logical { IRType::I1 } else { IRType::I32 };
let dest_var = self.var_manager.declare_unamed_local(dest_ty);
// 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`)
@@ -540,9 +671,7 @@ impl<'a> Generator<'a> {
// 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.clone()); left_var = self.coerce_value(&mut instrs, left_var, &convert_to);
instrs.push(IRInstr::Move(temp_var.clone(), MoveRValue::Var(left_var)));
left_var = temp_var;
} }
let result_type; let result_type;
match op { match op {
@@ -554,7 +683,13 @@ impl<'a> Generator<'a> {
result_type = IRType::I1; result_type = IRType::I1;
} }
} }
let dest_var = self.var_manager.declare_temp(result_type); let dest_var = if op.is_logical() && parent_exit.is_none() {
self.var_manager.declare_unamed_local(IRType::I32)
} else if op.is_logical() {
self.var_manager.declare_unamed_local(result_type)
} else {
self.var_manager.declare_temp(result_type)
};
match op { match op {
AstBinaryOp::And | AstBinaryOp::Or => { AstBinaryOp::And | AstBinaryOp::Or => {
instrs.push(IRInstr::Label(exit.unwrap().1)); instrs.push(IRInstr::Label(exit.unwrap().1));
@@ -580,9 +715,7 @@ impl<'a> Generator<'a> {
// true_exit: // true_exit:
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); right_var = self.coerce_value(&mut instrs, right_var, &convert_to);
instrs.push(IRInstr::Move(temp_var.clone(), MoveRValue::Var(right_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 {
@@ -598,6 +731,17 @@ impl<'a> Generator<'a> {
} }
} }
if op.is_logical() { if op.is_logical() {
if parent_exit.is_none() {
let (true_exit, _, false_exit) = exit.unwrap();
let final_exit = self.request_label();
instrs.push(IRInstr::Label(true_exit));
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.clone(), MoveRValue::ConstInt(0)));
instrs.push(IRInstr::Goto(final_exit));
instrs.push(IRInstr::Label(final_exit));
}
return Some((instrs, Some(dest_var))); return Some((instrs, Some(dest_var)));
} else { } else {
if op.is_cmp() { if op.is_cmp() {
@@ -627,12 +771,13 @@ impl<'a> Generator<'a> {
let mut arg_vars = vec![]; let mut arg_vars = vec![];
let func_def = self.ir_function(func_id); let func_def = self.ir_function(func_id);
for arg in args.into_iter() { for (arg, param_type) in args.into_iter().zip(func_def.parameter_types.iter()) {
self.current_exit_label.push(None); self.current_exit_label.push(None);
let (arg_instrs, arg_var) = self.generate_expr(arg)?; let (mut arg_instrs, arg_var) = self.generate_expr(arg)?;
self.current_exit_label.pop(); self.current_exit_label.pop();
let arg_var = self.coerce_value(&mut arg_instrs, arg_var.unwrap(), param_type);
instrs.extend(arg_instrs); instrs.extend(arg_instrs);
arg_vars.push(arg_var.unwrap()); arg_vars.push(arg_var);
} }
let ret_variable = if matches!(func_def.return_type, IRType::Void) { let ret_variable = if matches!(func_def.return_type, IRType::Void) {
None None
@@ -644,10 +789,15 @@ impl<'a> Generator<'a> {
} }
}; };
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 var = var.clone().unwrap();
instrs.push(IRInstr::Cmp(cmp_var.clone(), VariableOrIntLit::Var(var.clone().unwrap()), CmpOp::Ne, VariableOrIntLit::IntLit(0))); if matches!(var.data_type, IRType::Array(_, _) | IRType::Ptr(_)) {
instrs.push(IRInstr::CondGoto(cmp_var, true_exit, false_exit)); instrs.push(IRInstr::Goto(true_exit));
Some((instrs, var)) } else {
let cmp_var = self.var_manager.declare_temp(IRType::I1);
instrs.push(IRInstr::Cmp(cmp_var.clone(), VariableOrIntLit::Var(var.clone()), CmpOp::Ne, VariableOrIntLit::IntLit(0)));
instrs.push(IRInstr::CondGoto(cmp_var, true_exit, false_exit));
}
Some((instrs, Some(var)))
} else { } else {
Some((instrs, var)) Some((instrs, var))
} }
@@ -691,12 +841,13 @@ impl<'a> Generator<'a> {
let (index_instrs, index_var) = self.generate_expr(index)?; let (index_instrs, index_var) = self.generate_expr(index)?;
self.current_exit_label.pop(); self.current_exit_label.pop();
instrs.extend(index_instrs); instrs.extend(index_instrs);
let index_var = self.coerce_value(&mut instrs, index_var.unwrap(), &IRType::I32);
let addr_ty = IRType::Ptr(Box::new(elem_ty.into())); let addr_ty = IRType::Ptr(Box::new(elem_ty.into()));
let dest = self.var_manager.declare_temp(addr_ty); let dest = self.var_manager.declare_temp(addr_ty);
let elem_size_var = self.var_manager.declare_temp(IRType::I32); let elem_size_var = self.var_manager.declare_temp(IRType::I32);
let offset = 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::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(offset.clone(), index_var, AstBinaryOp::Mul.into(), elem_size_var));
instrs.push(IRInstr::Binary(dest.clone(), base, AstBinaryOp::Add.into(), offset)); instrs.push(IRInstr::Binary(dest.clone(), base, AstBinaryOp::Add.into(), offset));
Some((instrs, dest)) Some((instrs, dest))
} }
+6 -2
View File
@@ -21,6 +21,7 @@ impl Display for VariableOrIntLit {
#[derive(Clone)] #[derive(Clone)]
pub enum IRInstr { pub enum IRInstr {
Declare(Variable), Declare(Variable),
DeclareGlobal(Variable, Option<i32>),
DefineFunc(Function, Vec<Variable>, Vec<IRInstr>), DefineFunc(Function, Vec<Variable>, Vec<IRInstr>),
Entry, Entry,
Binary(Variable, Variable, BinaryOp, Variable), Binary(Variable, Variable, BinaryOp, Variable),
@@ -54,6 +55,8 @@ impl Display for IRInstr {
IRInstr::Load(dest, addr) => write!(f, "{} = *{}", dest, addr), IRInstr::Load(dest, addr) => write!(f, "{} = *{}", dest, addr),
IRInstr::Store(addr, value) => write!(f, "*{} = {}", addr, value), IRInstr::Store(addr, value) => write!(f, "*{} = {}", addr, value),
IRInstr::Declare(var) => write!(f, "declare {}", var.to_decl_string()), IRInstr::Declare(var) => write!(f, "declare {}", var.to_decl_string()),
IRInstr::DeclareGlobal(var, Some(init)) => write!(f, "declare {} = {}", var.to_decl_string(), init),
IRInstr::DeclareGlobal(var, None) => 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)
@@ -121,10 +124,11 @@ impl IRType {
} }
pub fn get_elevate_result(lhs: &IRType, rhs: &IRType) -> Option<IRType> { pub fn get_elevate_result(lhs: &IRType, rhs: &IRType) -> Option<IRType> {
if matches!((lhs, rhs), (IRType::I32 | IRType::I1, IRType::I32 | IRType::I1)) {
return Some(IRType::I32);
}
if lhs == rhs { if lhs == rhs {
Some(lhs.clone()) Some(lhs.clone())
} else if (*lhs == IRType::I32 && *rhs == IRType::I1) || (*lhs == IRType::I1 && *rhs == IRType::I32) {
Some(IRType::I32)
} else { } else {
None None
} }
+1 -19
View File
@@ -1,16 +1,7 @@
use thiserror::Error; use thiserror::Error;
use crate::lexer::types::TokenValue; use crate::ast::err::ParseError;
// #[derive(Debug, Clone, PartialEq, Eq, Error)]
// pub enum ParseError {
// BlockStmt(#[from] BlockStmtError)
// }
// #[derive(Debug, Clone, PartialEq, Eq, Error)]
// pub enum BlockStmtError {
// MissingLBrace,
// MissingRBrace,
// }
#[derive(Debug, Clone, PartialEq, Eq, Error)] #[derive(Debug, Clone, PartialEq, Eq, Error)]
pub enum LexingError { pub enum LexingError {
#[error("invalid int literal")] #[error("invalid int literal")]
@@ -23,15 +14,6 @@ pub enum LexingError {
UnrecognizedToken(String), UnrecognizedToken(String),
} }
#[derive(Debug, Clone, PartialEq, Eq, Error)] #[derive(Debug, Clone, PartialEq, Eq, Error)]
pub enum ParseError {
#[error("unexpected token {}, expect {}", .0, .1)]
UnexpectedToken(TokenValue, &'static str),
#[error("cannot combine with previous {}", .0)]
CantCombineWith(TokenValue),
#[error("expect {0} after")]
ExpectButEof(&'static str),
}
#[derive(Debug, Clone, PartialEq, Eq, Error)]
pub enum FrontendError { pub enum FrontendError {
#[error(transparent)] #[error(transparent)]
Lexing(#[from] LexingError), Lexing(#[from] LexingError),
+65 -7
View File
@@ -1,4 +1,4 @@
use std::str::FromStr; use std::{str::FromStr, sync::LazyLock};
use thiserror::Error; use thiserror::Error;
@@ -16,7 +16,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>,
@@ -269,11 +269,55 @@ fn parse_puncuation(
}; };
let c = str_iter.peek().ok_or(LexParseError::NotMatched)?; let c = str_iter.peek().ok_or(LexParseError::NotMatched)?;
let token_value = match c { let token_value = match c {
'+' => TokenValue::Plus, '+' => {
'-' => TokenValue::Minus, str_iter.advance(1);
'*' => TokenValue::Star, if let Some('+') = str_iter.peek() {
'/' => TokenValue::Slash, TokenValue::PlusPlus
'%' => TokenValue::Percent, } else if let Some('=') = str_iter.peek() {
TokenValue::PlusEqual
} else {
str_iter.back(1);
TokenValue::Plus
}
},
'-' => {
str_iter.advance(1);
if let Some('-') = str_iter.peek() {
TokenValue::MinusMinus
} else if let Some('=') = str_iter.peek() {
TokenValue::MinusEqual
} else {
str_iter.back(1);
TokenValue::Minus
}
},
'*' => {
str_iter.advance(1);
if let Some('=') = str_iter.peek() {
TokenValue::StarEqual
} else {
str_iter.back(1);
TokenValue::Star
}
},
'/' => {
str_iter.advance(1);
if let Some('=') = str_iter.peek() {
TokenValue::SlashEqual
} else {
str_iter.back(1);
TokenValue::Slash
}
},
'%' => {
str_iter.advance(1);
if let Some('=') = str_iter.peek() {
TokenValue::PercentEqual
} else {
str_iter.back(1);
TokenValue::Percent
}
},
'=' => get_value_by_next_char(str_iter, TokenValue::Equal, TokenValue::DoubleEqual), '=' => get_value_by_next_char(str_iter, TokenValue::Equal, TokenValue::DoubleEqual),
'!' => { '!' => {
@@ -314,6 +358,17 @@ fn parse_puncuation(
Ok(token_value) Ok(token_value)
} }
// static KEYWORDS: LazyLock<HashMap<&'static str, TokenValue>> = LazyLock::new(|| {
// let mut m = HashMap::new();
// m.insert("if", TokenValue::If);
// m.insert("else", TokenValue::Else);
// m.insert("while", TokenValue::While);
// m.insert("for", TokenValue::For);
// m.insert("return", TokenValue::Return);
// m.insert("break", TokenValue::Break);
// m.insert("continue", TokenValue::Continue);
// m
// });
fn parse_ident( fn parse_ident(
str_iter: &mut Cursor, str_iter: &mut Cursor,
) -> Result<TokenValue, LexParseError> { ) -> Result<TokenValue, LexParseError> {
@@ -354,6 +409,9 @@ fn parse_ident(
if name.eq("continue") { if name.eq("continue") {
return Ok(TokenValue::Continue); return Ok(TokenValue::Continue);
} }
if name.eq("static") {
return Ok(TokenValue::Static);
}
if let Some(type_ident) = TypeIdent::from_str(&name).ok() { if let Some(type_ident) = TypeIdent::from_str(&name).ok() {
return Ok(TokenValue::TypeIdent(type_ident)); return Ok(TokenValue::TypeIdent(type_ident));
} }
+14 -3
View File
@@ -17,6 +17,8 @@ pub enum TokenValue {
TypeIdent(TypeIdent), TypeIdent(TypeIdent),
Plus, Minus, Star, Slash, Percent, Plus, Minus, Star, Slash, Percent,
PlusPlus, MinusMinus,
PlusEqual, MinusEqual, StarEqual, SlashEqual, PercentEqual,
Equal, DoubleEqual, Not, NotEqual, Less, LessEqual, Greater, GreaterEqual, And, Or, Equal, DoubleEqual, Not, NotEqual, Less, LessEqual, Greater, GreaterEqual, And, Or,
LParen, RParen, LParen, RParen,
@@ -24,7 +26,7 @@ pub enum TokenValue {
LBracket, RBracket, LBracket, RBracket,
Comma, Semicolon, Comma, Semicolon,
If, Else, While, For, Return, Break, Continue, If, Else, While, For, Return, Break, Continue, Static,
Eof, Eof,
Unrecognized, Unrecognized,
@@ -53,6 +55,13 @@ impl std::fmt::Display for TokenValue {
TokenValue::TypeIdent(t) => write!(f, "type {}", t.as_ref()), TokenValue::TypeIdent(t) => write!(f, "type {}", t.as_ref()),
TokenValue::Plus => write!(f, "`+`"), TokenValue::Plus => write!(f, "`+`"),
TokenValue::Minus => write!(f, "`-`"), TokenValue::Minus => write!(f, "`-`"),
TokenValue::PlusPlus => write!(f, "`++`"),
TokenValue::MinusMinus => write!(f, "`--`"),
TokenValue::PlusEqual => write!(f, "`+=`"),
TokenValue::MinusEqual => write!(f, "`-=`"),
TokenValue::StarEqual => write!(f, "`*=`"),
TokenValue::SlashEqual => write!(f, "`/=`"),
TokenValue::PercentEqual => write!(f, "`%=`"),
TokenValue::Star => write!(f, "`*`"), TokenValue::Star => write!(f, "`*`"),
TokenValue::Slash => write!(f, "`/`"), TokenValue::Slash => write!(f, "`/`"),
TokenValue::Percent => write!(f, "`%`"), TokenValue::Percent => write!(f, "`%`"),
@@ -81,7 +90,8 @@ impl std::fmt::Display for TokenValue {
TokenValue::Return => write!(f, "return"), TokenValue::Return => write!(f, "return"),
TokenValue::Break => write!(f, "break"), TokenValue::Break => write!(f, "break"),
TokenValue::Continue => write!(f, "continue"), TokenValue::Continue => write!(f, "continue"),
TokenValue::Eof => write!(f, "<EOF>"), TokenValue::Static => write!(f, "static"),
TokenValue::Eof => write!(f, "end of input"),
TokenValue::Unrecognized => write!(f, "unrecognized"), TokenValue::Unrecognized => write!(f, "unrecognized"),
} }
} }
@@ -94,6 +104,7 @@ pub enum TokenKind {
TypeIdent, TypeIdent,
Plus, Minus, Star, Slash, Percent, Plus, Minus, Star, Slash, Percent,
PlusEqual, MinusEqual, StarEqual, SlashEqual, PercentEqual,
Equal, DoubleEqual, NotEqual, Less, LessEqual, Greater, GreaterEqual, Equal, DoubleEqual, NotEqual, Less, LessEqual, Greater, GreaterEqual,
LParen, RParen, LParen, RParen,
@@ -101,7 +112,7 @@ pub enum TokenKind {
LBracket, RBracket, LBracket, RBracket,
Comma, Semicolon, Comma, Semicolon,
If, Else, While, For, Return, Break, Continue, If, Else, While, For, Return, Break, Continue, Static,
Eof, Eof,
Unrecognized, Unrecognized,
+4
View File
@@ -13,6 +13,7 @@ use clap::Parser as ArgParser;
use crate::{ast::parser::Parser, ir::{cfg, generator::Generator}, lexer::lexer::Lexer, sema::analyzer::Analyzer}; use crate::{ast::parser::Parser, ir::{cfg, generator::Generator}, lexer::lexer::Lexer, 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)]
#[command(version, about, long_about = None)] #[command(version, about, long_about = None)]
@@ -76,16 +77,19 @@ fn main() {
let (tokens, diagnostics) = lexer.finish(); let (tokens, diagnostics) = lexer.finish();
if !diagnostics.is_empty() { if !diagnostics.is_empty() {
diagnostics.print(&format!("{}", source_path.display()), &full_text); diagnostics.print(&format!("{}", source_path.display()), &full_text);
std::process::exit(1);
} }
let mut parser = Parser::new(tokens, diagnostics); let mut parser = Parser::new(tokens, diagnostics);
let compile_unit = parser.parse(); let compile_unit = parser.parse();
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);
std::process::exit(1);
} }
let mut analyzer = Analyzer::new(); let mut analyzer = Analyzer::new();
let hir = analyzer.analyze(compile_unit); let hir = analyzer.analyze(compile_unit);
if !analyzer.get_diagnostics().is_empty() { if !analyzer.get_diagnostics().is_empty() {
analyzer.get_diagnostics().print(&format!("{}", source_path.display()), &full_text); analyzer.get_diagnostics().print(&format!("{}", source_path.display()), &full_text);
std::process::exit(1);
} }
let mut generator = Generator::new(&analyzer); let mut generator = Generator::new(&analyzer);
let ir = cfg::optimize_ir(generator.emit(hir)); let ir = cfg::optimize_ir(generator.emit(hir));
+148 -16
View File
@@ -1,9 +1,9 @@
use std::collections::BTreeMap; use std::collections::{BTreeMap, BTreeSet};
use crate::{ use crate::{
ast::types::{ ast::types::{
ArrayDimension, BinaryOp, BlockStmt, BreakStmt, CompileUnit, ContinueStmt, Expr, ExprValue, ForInit, ForStmt, FuncDeclStmt, ArrayDimension, BinaryOp, BlockStmt, BreakStmt, CompileUnit, ContinueStmt, Expr, ExprValue, ForInit, ForStmt, FuncDeclStmt,
GlobalDeclStmt, IfElseBranch, IfStmt, ReturnStmt, Statement, VarDeclStmt, WhileStmt, GlobalDeclStmt, IfElseBranch, IfStmt, ReturnStmt, Statement, StorageClass, VarDeclStmt, WhileStmt,
}, },
diagnostic::{span::Span, Diagnositics}, diagnostic::{span::Span, Diagnositics},
sema::{ sema::{
@@ -21,6 +21,7 @@ use crate::{
pub struct Analyzer { pub struct Analyzer {
symbols: SymbolTable, symbols: SymbolTable,
function_map: BTreeMap<String, FunctionId>, function_map: BTreeMap<String, FunctionId>,
builtin_functions: BTreeSet<FunctionId>,
functions: Vec<FunctionSig>, functions: Vec<FunctionSig>,
current_func_return_type: Option<SemaType>, current_func_return_type: Option<SemaType>,
diagnostic: Diagnositics, diagnostic: Diagnositics,
@@ -32,6 +33,7 @@ impl Analyzer {
let mut analyzer = Self { let mut analyzer = Self {
symbols: SymbolTable::new(), symbols: SymbolTable::new(),
function_map: BTreeMap::new(), function_map: BTreeMap::new(),
builtin_functions: BTreeSet::new(),
functions: vec![], functions: vec![],
current_func_return_type: None, current_func_return_type: None,
diagnostic: Diagnositics::new(), diagnostic: Diagnositics::new(),
@@ -41,6 +43,7 @@ impl Analyzer {
analyzer.declare_builtin_func("putch", vec![SemaType::I32], SemaType::Void); analyzer.declare_builtin_func("putch", vec![SemaType::I32], SemaType::Void);
analyzer.declare_builtin_func("putarray", vec![SemaType::I32, SemaType::Array(Box::new(SemaType::I32), vec![0])], SemaType::Void); analyzer.declare_builtin_func("putarray", vec![SemaType::I32, SemaType::Array(Box::new(SemaType::I32), vec![0])], SemaType::Void);
analyzer.declare_builtin_func("getint", vec![], SemaType::I32); analyzer.declare_builtin_func("getint", vec![], SemaType::I32);
analyzer.declare_builtin_func("getch", vec![], SemaType::I32);
analyzer.declare_builtin_func("getarray", vec![SemaType::Array(Box::new(SemaType::I32), vec![0])], SemaType::I32); analyzer.declare_builtin_func("getarray", vec![SemaType::Array(Box::new(SemaType::I32), vec![0])], SemaType::I32);
analyzer analyzer
} }
@@ -74,6 +77,7 @@ impl Analyzer {
parameter_types, parameter_types,
}; };
self.function_map.insert(name.to_string(), id); self.function_map.insert(name.to_string(), id);
self.builtin_functions.insert(id);
self.functions.push(sig); self.functions.push(sig);
} }
@@ -100,13 +104,33 @@ impl Analyzer {
fn analyze_var_decl(&mut self, var_decl: VarDeclStmt, kind: SymbolKind) -> HirVarDeclStmt { fn analyze_var_decl(&mut self, var_decl: VarDeclStmt, kind: SymbolKind) -> HirVarDeclStmt {
let base_type: SemaType = var_decl.data_type.into(); let base_type: SemaType = var_decl.data_type.into();
let is_static_local = kind == SymbolKind::Local && var_decl.storage_class == Some(StorageClass::Static);
let has_static_storage = kind == SymbolKind::Global || is_static_local;
let actual_kind = if is_static_local {
SymbolKind::StaticLocal
} else {
kind
};
let mut values = vec![]; let mut values = vec![];
for value in var_decl.values { for value in var_decl.values {
let data_type = self.build_var_type(base_type.clone(), &value.dimensions, false); let data_type = self.build_var_type(base_type.clone(), &value.dimensions, false);
match self.symbols.declare_variable(&value.name, kind, data_type) { let initializer_is_not_constant = has_static_storage
&& value
.value
.as_ref()
.is_some_and(|expr| Self::eval_const_expr(expr).is_none());
if initializer_is_not_constant {
if let Some(expr) = &value.value {
self.add_error(SemaError::InitializerNotConstant, expr.span);
}
}
match self.symbols.declare_variable(&value.name, actual_kind, data_type) {
Ok(symbol) => { Ok(symbol) => {
let symbol_type = self.symbols.get_symbol(symbol).ty.clone(); let symbol_type = self.symbols.get_symbol(symbol).ty.clone();
let init = value.value.and_then(|expr| { let init = if initializer_is_not_constant {
None
} else {
value.value.and_then(|expr| {
let span = expr.span; let span = expr.span;
let init = self.analyze_expr(expr)?; let init = self.analyze_expr(expr)?;
if init.ty == SemaType::Void { if init.ty == SemaType::Void {
@@ -115,11 +139,13 @@ impl Analyzer {
self.add_error(SemaError::TypeMismatch(symbol_type.clone(), init.ty.clone()), span); self.add_error(SemaError::TypeMismatch(symbol_type.clone(), init.ty.clone()), span);
} }
Some(init) Some(init)
}); })
};
values.push(HirVarDeclStmtValue { values.push(HirVarDeclStmtValue {
symbol, symbol,
name_span: value.name_span, name_span: value.name_span,
value: init, value: init,
is_static_local,
}); });
} }
Err(e) => self.add_error(e, value.name_span), Err(e) => self.add_error(e, value.name_span),
@@ -133,7 +159,11 @@ impl Analyzer {
} }
fn analyze_func_decl(&mut self, func_decl: FuncDeclStmt) -> Option<HirFuncDeclStmt> { fn analyze_func_decl(&mut self, func_decl: FuncDeclStmt) -> Option<HirFuncDeclStmt> {
if self.function_map.contains_key(&func_decl.name) { let is_static = func_decl.storage_class == Some(StorageClass::Static);
let existing_function = self.function_map.get(&func_decl.name).cloned();
let can_shadow_builtin = is_static
&& existing_function.is_some_and(|function| self.builtin_functions.contains(&function));
if existing_function.is_some() && !can_shadow_builtin {
self.add_error(SemaError::FunctionHasBeenDefined(func_decl.name.clone()), func_decl.name_span); self.add_error(SemaError::FunctionHasBeenDefined(func_decl.name.clone()), func_decl.name_span);
return None; return None;
} }
@@ -144,9 +174,14 @@ impl Analyzer {
parameter_types.push(self.build_var_type(param.param_type.into(), &param.dimensions, true)); parameter_types.push(self.build_var_type(param.param_type.into(), &param.dimensions, true));
} }
let return_type: SemaType = func_decl.return_type.into(); let return_type: SemaType = func_decl.return_type.into();
let ir_name = if is_static {
format!("__static_func_{}_{}", function_id.0, func_decl.name)
} else {
func_decl.name.clone()
};
let sig = FunctionSig { let sig = FunctionSig {
id: function_id, id: function_id,
name: func_decl.name.clone(), name: ir_name,
return_type: return_type.clone(), return_type: return_type.clone(),
parameter_types, parameter_types,
}; };
@@ -183,6 +218,47 @@ impl Analyzer {
}) })
} }
fn eval_const_expr(expr: &Expr) -> Option<i32> {
match &expr.value {
ExprValue::IntLit(value) => Some(*value as i32),
ExprValue::UnaryOp { op, operand } => {
let value = Self::eval_const_expr(operand)?;
match op {
crate::ast::types::UnaryOp::Add => Some(value),
crate::ast::types::UnaryOp::Sub => Some(value.wrapping_neg()),
crate::ast::types::UnaryOp::Not => Some((value == 0) as i32),
}
}
ExprValue::BinaryOp { lhs, op, rhs } => {
let lhs = Self::eval_const_expr(lhs)?;
match op {
BinaryOp::And if lhs == 0 => return Some(0),
BinaryOp::Or if lhs != 0 => return Some(1),
_ => {}
}
let rhs = Self::eval_const_expr(rhs)?;
match op {
BinaryOp::Add => Some(lhs.wrapping_add(rhs)),
BinaryOp::Sub => Some(lhs.wrapping_sub(rhs)),
BinaryOp::Mul => Some(lhs.wrapping_mul(rhs)),
BinaryOp::Div if rhs != 0 => Some(lhs.wrapping_div(rhs)),
BinaryOp::Div => None,
BinaryOp::Mod if rhs != 0 => Some(lhs.wrapping_rem(rhs)),
BinaryOp::Mod => None,
BinaryOp::Equal => Some((lhs == rhs) as i32),
BinaryOp::NotEqual => Some((lhs != rhs) as i32),
BinaryOp::Less => Some((lhs < rhs) as i32),
BinaryOp::LessEqual => Some((lhs <= rhs) as i32),
BinaryOp::Greater => Some((lhs > rhs) as i32),
BinaryOp::GreaterEqual => Some((lhs >= rhs) as i32),
BinaryOp::And => Some((lhs != 0 && rhs != 0) as i32),
BinaryOp::Or => Some((lhs != 0 || rhs != 0) as i32),
}
}
_ => None,
}
}
fn build_var_type(&mut self, base_type: SemaType, dimensions: &[ArrayDimension], is_param: bool) -> SemaType { fn build_var_type(&mut self, base_type: SemaType, dimensions: &[ArrayDimension], is_param: bool) -> SemaType {
if dimensions.is_empty() { if dimensions.is_empty() {
return base_type; return base_type;
@@ -191,12 +267,12 @@ impl Analyzer {
for (i, dimension) in dimensions.iter().enumerate() { for (i, dimension) in dimensions.iter().enumerate() {
match &dimension.value { match &dimension.value {
Some(expr) => { Some(expr) => {
if let ExprValue::IntLit(value) = expr.value { if let Some(value) = Self::eval_const_expr(expr) {
if value > 0 { if value > 0 {
dims.push(value as usize); dims.push(value as usize);
} else { continue;
self.add_error(SemaError::InvalidArrayDimension, dimension.span);
} }
self.add_error(SemaError::InvalidArrayDimension, dimension.span);
} else { } else {
self.add_error(SemaError::InvalidArrayDimension, dimension.span); self.add_error(SemaError::InvalidArrayDimension, dimension.span);
} }
@@ -218,6 +294,13 @@ impl Analyzer {
HirBlockStmt { statements } HirBlockStmt { statements }
} }
fn analyze_scoped_block_stmt(&mut self, block_stmt: BlockStmt) -> HirBlockStmt {
self.symbols.enter_scope();
let block = self.analyze_block_stmt(block_stmt);
self.symbols.exit_scope();
block
}
fn analyze_statement(&mut self, stmt: Statement) -> Option<HirStatement> { fn analyze_statement(&mut self, stmt: Statement) -> Option<HirStatement> {
match stmt { match stmt {
Statement::Return(stmt) => Some(HirStatement::Return(self.analyze_return_stmt(stmt))), Statement::Return(stmt) => Some(HirStatement::Return(self.analyze_return_stmt(stmt))),
@@ -273,16 +356,16 @@ impl Analyzer {
fn analyze_if_stmt(&mut self, if_stmt: IfStmt) -> HirIfStmt { fn analyze_if_stmt(&mut self, if_stmt: IfStmt) -> HirIfStmt {
let condition = self.analyze_condition_expr(if_stmt.condition); let condition = self.analyze_condition_expr(if_stmt.condition);
let then_branch = self.analyze_block_stmt(if_stmt.then_branch); let then_branch = self.analyze_scoped_block_stmt(if_stmt.then_branch);
let mut ifelse_branch = vec![]; let mut ifelse_branch = vec![];
for branch in if_stmt.ifelse_branch { for branch in if_stmt.ifelse_branch {
let IfElseBranch { condition, then_branch } = branch; let IfElseBranch { condition, then_branch } = branch;
ifelse_branch.push(HirIfElseBranch { ifelse_branch.push(HirIfElseBranch {
condition: self.analyze_condition_expr(condition), condition: self.analyze_condition_expr(condition),
then_branch: self.analyze_block_stmt(then_branch), then_branch: self.analyze_scoped_block_stmt(then_branch),
}); });
} }
let else_branch = if_stmt.else_branch.map(|block| self.analyze_block_stmt(block)); let else_branch = if_stmt.else_branch.map(|block| self.analyze_scoped_block_stmt(block));
HirIfStmt { HirIfStmt {
condition, condition,
then_branch, then_branch,
@@ -294,7 +377,7 @@ impl Analyzer {
fn analyze_while_stmt(&mut self, while_stmt: WhileStmt) -> HirWhileStmt { fn analyze_while_stmt(&mut self, while_stmt: WhileStmt) -> HirWhileStmt {
let condition = self.analyze_condition_expr(while_stmt.condition); let condition = self.analyze_condition_expr(while_stmt.condition);
self.loop_depth += 1; self.loop_depth += 1;
let body = self.analyze_block_stmt(while_stmt.body); let body = self.analyze_scoped_block_stmt(while_stmt.body);
self.loop_depth -= 1; self.loop_depth -= 1;
HirWhileStmt { HirWhileStmt {
condition, condition,
@@ -311,7 +394,7 @@ impl Analyzer {
let condition = for_stmt.condition.map(|expr| self.analyze_condition_expr(expr)); let condition = for_stmt.condition.map(|expr| self.analyze_condition_expr(expr));
let update = for_stmt.update.and_then(|expr| self.analyze_expr(expr)); let update = for_stmt.update.and_then(|expr| self.analyze_expr(expr));
self.loop_depth += 1; self.loop_depth += 1;
let body = self.analyze_block_stmt(for_stmt.body); let body = self.analyze_scoped_block_stmt(for_stmt.body);
self.loop_depth -= 1; self.loop_depth -= 1;
self.symbols.exit_scope(); self.symbols.exit_scope();
HirForStmt { HirForStmt {
@@ -376,6 +459,7 @@ impl Analyzer {
}) })
} }
ExprValue::Assign { lvalue, rvalue } => self.analyze_assign_expr(*lvalue, *rvalue, span), ExprValue::Assign { lvalue, rvalue } => self.analyze_assign_expr(*lvalue, *rvalue, span),
ExprValue::CompoundAssign { lvalue, op, rvalue } => self.analyze_compound_assign_expr(*lvalue, op, *rvalue, span),
ExprValue::ArrayAccess { array, index } => self.analyze_array_access_expr(*array, *index, span), ExprValue::ArrayAccess { array, index } => self.analyze_array_access_expr(*array, *index, span),
ExprValue::UnaryOp { op, operand } => { ExprValue::UnaryOp { op, operand } => {
let operand_span = operand.span; let operand_span = operand.span;
@@ -386,6 +470,7 @@ impl Analyzer {
} }
let ty = match op { let ty = match op {
crate::ast::types::UnaryOp::Not => SemaType::I1, crate::ast::types::UnaryOp::Not => SemaType::I1,
crate::ast::types::UnaryOp::Add | crate::ast::types::UnaryOp::Sub if operand.ty == SemaType::I1 => SemaType::I32,
_ => operand.ty.clone(), _ => operand.ty.clone(),
}; };
Some(HirExpr { Some(HirExpr {
@@ -453,6 +538,50 @@ impl Analyzer {
}) })
} }
fn analyze_compound_assign_expr(&mut self, lvalue: Expr, op: BinaryOp, 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_span = lvalue.span;
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 !lvalue.ty.is_scalar() {
self.add_error(SemaError::InvalidOperand(lvalue.ty.clone()), lvalue_span);
return None;
}
if !rvalue.ty.is_scalar() {
self.add_error(SemaError::InvalidOperand(rvalue.ty.clone()), rvalue_span);
return None;
}
let result_ty = match SemaType::get_elevate_result(&lvalue.ty, &rvalue.ty) {
Some(ty) => ty,
None => {
self.add_error(SemaError::IncompatiableOperand(lvalue.ty.clone(), rvalue.ty.clone()), lvalue_span);
self.add_error(SemaError::IncompatiableOperand(lvalue.ty.clone(), rvalue.ty.clone()), rvalue_span);
return None;
}
};
if !self.type_matches(&lvalue.ty, &result_ty) {
self.add_error(SemaError::TypeMismatch(lvalue.ty.clone(), result_ty), span);
return None;
}
Some(HirExpr {
ty: lvalue.ty.clone(),
value: HirExprValue::CompoundAssign {
lvalue: Box::new(lvalue),
op,
rvalue: Box::new(rvalue),
},
span,
})
}
fn analyze_binary_expr(&mut self, lhs: Expr, op: BinaryOp, rhs: Expr, span: Span) -> Option<HirExpr> { fn analyze_binary_expr(&mut self, lhs: Expr, op: BinaryOp, rhs: Expr, span: Span) -> Option<HirExpr> {
let lhs_span = lhs.span; let lhs_span = lhs.span;
let rhs_span = rhs.span; let rhs_span = rhs.span;
@@ -555,7 +684,7 @@ impl Analyzer {
let ty = match array.ty.indexed_type() { let ty = match array.ty.indexed_type() {
Some(ty) => ty, Some(ty) => ty,
None => { None => {
self.add_error(SemaError::NotSubscriptable, span); self.add_error(SemaError::NotSubscriptable(array.ty.clone()), span);
return None; return None;
} }
}; };
@@ -573,6 +702,9 @@ impl Analyzer {
if expected == actual { if expected == actual {
return true; return true;
} }
if matches!((expected, actual), (SemaType::I32, SemaType::I1)) {
return true;
}
if let (SemaType::Array(expected_elem, expected_dims), SemaType::Array(actual_elem, actual_dims)) = (expected, actual) { if let (SemaType::Array(expected_elem, expected_dims), SemaType::Array(actual_elem, actual_dims)) = (expected, actual) {
return expected_elem == actual_elem return expected_elem == actual_elem
&& expected_dims.len() == actual_dims.len() && expected_dims.len() == actual_dims.len()
+4 -2
View File
@@ -34,6 +34,8 @@ pub enum SemaError {
ReturnExpressionOnVoidFunction, ReturnExpressionOnVoidFunction,
#[error("array dimension must be a positive integer literal")] #[error("array dimension must be a positive integer literal")]
InvalidArrayDimension, InvalidArrayDimension,
#[error("subscripted value is not an array or pointer")] #[error("subscripted value of type `{0}` is not an array or pointer")]
NotSubscriptable, NotSubscriptable(SemaType),
#[error("initializer element is not constant")]
InitializerNotConstant,
} }
+6
View File
@@ -19,6 +19,7 @@ pub struct HirVarDeclStmtValue {
pub symbol: SymbolId, pub symbol: SymbolId,
pub name_span: Span, pub name_span: Span,
pub value: Option<HirExpr>, pub value: Option<HirExpr>,
pub is_static_local: bool,
} }
pub struct HirFuncDeclStmt { pub struct HirFuncDeclStmt {
@@ -126,4 +127,9 @@ pub enum HirExprValue {
lvalue: Box<HirExpr>, lvalue: Box<HirExpr>,
rvalue: Box<HirExpr>, rvalue: Box<HirExpr>,
}, },
CompoundAssign {
lvalue: Box<HirExpr>,
op: BinaryOp,
rvalue: Box<HirExpr>,
},
} }
+1
View File
@@ -10,6 +10,7 @@ pub enum SymbolKind {
Global, Global,
Local, Local,
Param, Param,
StaticLocal,
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
+3 -2
View File
@@ -13,10 +13,11 @@ pub enum SemaType {
impl SemaType { impl SemaType {
pub fn get_elevate_result(lhs: &SemaType, rhs: &SemaType) -> Option<SemaType> { pub fn get_elevate_result(lhs: &SemaType, rhs: &SemaType) -> Option<SemaType> {
if matches!((lhs, rhs), (SemaType::I32 | SemaType::I1, SemaType::I32 | SemaType::I1)) {
return Some(SemaType::I32);
}
if lhs == rhs { if lhs == rhs {
Some(lhs.clone()) Some(lhs.clone())
} else if (*lhs == SemaType::I32 && *rhs == SemaType::I1) || (*lhs == SemaType::I1 && *rhs == SemaType::I32) {
Some(SemaType::I32)
} else { } else {
None None
} }