Compare commits
3 Commits
52f9b67018
...
b6b45bd27c
| Author | SHA1 | Date | |
|---|---|---|---|
| b6b45bd27c | |||
| b7c2924e8b | |||
| 55636e07af |
+46
-1
@@ -2,7 +2,7 @@ use petgraph::dot::{Config, Dot};
|
|||||||
use petgraph::graph::{Graph, NodeIndex};
|
use petgraph::graph::{Graph, NodeIndex};
|
||||||
|
|
||||||
use crate::ast::types::{
|
use crate::ast::types::{
|
||||||
ArrayDimension, BlockStmt, BreakStmt, CompileUnit, ContinueStmt, Expr, ExprValue, FuncDeclStmt, GlobalDeclStmt, IfStmt, Param, ReturnStmt, Statement, VarDeclStmt, VarDeclStmtValue, WhileStmt
|
ArrayDimension, BlockStmt, BreakStmt, CompileUnit, ContinueStmt, Expr, ExprValue, ForInit, ForStmt, FuncDeclStmt, GlobalDeclStmt, IfStmt, Param, ReturnStmt, Statement, VarDeclStmt, VarDeclStmtValue, WhileStmt
|
||||||
};
|
};
|
||||||
|
|
||||||
pub type AstGraph = Graph<String, String>;
|
pub type AstGraph = Graph<String, String>;
|
||||||
@@ -77,6 +77,9 @@ impl AstGraphBuilder {
|
|||||||
for dimension in &value.dimensions {
|
for dimension in &value.dimensions {
|
||||||
self.add_array_dimension(node, dimension);
|
self.add_array_dimension(node, dimension);
|
||||||
}
|
}
|
||||||
|
if let Some(expr) = &value.value {
|
||||||
|
self.add_expr(node, expr);
|
||||||
|
}
|
||||||
node
|
node
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,6 +137,7 @@ impl AstGraphBuilder {
|
|||||||
},
|
},
|
||||||
Statement::If(if_stmt) => self.add_if_stmt(parent, if_stmt),
|
Statement::If(if_stmt) => self.add_if_stmt(parent, if_stmt),
|
||||||
Statement::While(while_stmt) => self.add_while_stmt(parent, while_stmt),
|
Statement::While(while_stmt) => self.add_while_stmt(parent, while_stmt),
|
||||||
|
Statement::For(for_stmt) => self.add_for_stmt(parent, for_stmt),
|
||||||
Statement::Break(break_stmt) => self.add_break_stmt(parent, break_stmt),
|
Statement::Break(break_stmt) => self.add_break_stmt(parent, break_stmt),
|
||||||
Statement::Continue(continue_stmt) => self.add_continue_stmt(parent, continue_stmt),
|
Statement::Continue(continue_stmt) => self.add_continue_stmt(parent, continue_stmt),
|
||||||
}
|
}
|
||||||
@@ -160,6 +164,42 @@ impl AstGraphBuilder {
|
|||||||
self.add_block_stmt(node, &while_stmt.body);
|
self.add_block_stmt(node, &while_stmt.body);
|
||||||
node
|
node
|
||||||
}
|
}
|
||||||
|
fn add_for_stmt(&mut self, parent: NodeIndex, for_stmt: &ForStmt) -> NodeIndex {
|
||||||
|
let node = self.child(parent, for_stmt.to_string());
|
||||||
|
match &for_stmt.init {
|
||||||
|
Some(ForInit::Expr(expr)) => {
|
||||||
|
let init = self.child(node, "Init");
|
||||||
|
self.add_expr(init, expr);
|
||||||
|
}
|
||||||
|
Some(ForInit::VarDecl(var_decl)) => {
|
||||||
|
let init = self.child(node, "Init");
|
||||||
|
self.add_var_decl(init, var_decl);
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
self.child(node, "NoInit");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match &for_stmt.condition {
|
||||||
|
Some(expr) => {
|
||||||
|
let condition = self.child(node, "Condition");
|
||||||
|
self.add_expr(condition, expr);
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
self.child(node, "NoCondition");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match &for_stmt.update {
|
||||||
|
Some(expr) => {
|
||||||
|
let update = self.child(node, "Update");
|
||||||
|
self.add_expr(update, expr);
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
self.child(node, "NoUpdate");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.add_block_stmt(node, &for_stmt.body);
|
||||||
|
node
|
||||||
|
}
|
||||||
fn add_break_stmt(&mut self, parent: NodeIndex, break_stmt: &BreakStmt) -> NodeIndex {
|
fn add_break_stmt(&mut self, parent: NodeIndex, break_stmt: &BreakStmt) -> NodeIndex {
|
||||||
self.child(parent, break_stmt.to_string())
|
self.child(parent, break_stmt.to_string())
|
||||||
}
|
}
|
||||||
@@ -200,6 +240,11 @@ impl AstGraphBuilder {
|
|||||||
self.add_expr(node, operand);
|
self.add_expr(node, operand);
|
||||||
node
|
node
|
||||||
}
|
}
|
||||||
|
ExprValue::IncDec { operand, .. } => {
|
||||||
|
let node = self.child(parent, expr.value.to_string());
|
||||||
|
self.add_expr(node, operand);
|
||||||
|
node
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
pub mod graph;
|
pub mod graph;
|
||||||
|
pub mod parser;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
+39
-1
@@ -1,4 +1,4 @@
|
|||||||
use crate::{diagnostic::span::Span, frontend::types::{Token, TokenValue, TypeIdent}};
|
use crate::{diagnostic::span::Span, lexer::types::{TokenValue, TypeIdent}};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
pub struct CompileUnit {
|
pub struct CompileUnit {
|
||||||
@@ -18,6 +18,7 @@ pub struct VarDeclStmtValue {
|
|||||||
pub name: String,
|
pub name: String,
|
||||||
pub name_span: Span,
|
pub name_span: Span,
|
||||||
pub dimensions: Vec<ArrayDimension>,
|
pub dimensions: Vec<ArrayDimension>,
|
||||||
|
pub value: Option<Expr>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ArrayDimension {
|
pub struct ArrayDimension {
|
||||||
@@ -44,6 +45,7 @@ pub enum Statement {
|
|||||||
VarDecl(VarDeclStmt),
|
VarDecl(VarDeclStmt),
|
||||||
If(IfStmt),
|
If(IfStmt),
|
||||||
While(WhileStmt),
|
While(WhileStmt),
|
||||||
|
For(ForStmt),
|
||||||
Break(BreakStmt),
|
Break(BreakStmt),
|
||||||
Continue(ContinueStmt),
|
Continue(ContinueStmt),
|
||||||
}
|
}
|
||||||
@@ -77,6 +79,16 @@ pub struct WhileStmt {
|
|||||||
pub body: BlockStmt,
|
pub body: BlockStmt,
|
||||||
// pub span: Span,
|
// pub span: Span,
|
||||||
}
|
}
|
||||||
|
pub struct ForStmt {
|
||||||
|
pub init: Option<ForInit>,
|
||||||
|
pub condition: Option<Expr>,
|
||||||
|
pub update: Option<Expr>,
|
||||||
|
pub body: BlockStmt,
|
||||||
|
}
|
||||||
|
pub enum ForInit {
|
||||||
|
Expr(Expr),
|
||||||
|
VarDecl(VarDeclStmt),
|
||||||
|
}
|
||||||
pub struct BreakStmt {
|
pub struct BreakStmt {
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
}
|
}
|
||||||
@@ -107,6 +119,11 @@ pub enum ExprValue {
|
|||||||
op: UnaryOp,
|
op: UnaryOp,
|
||||||
operand: Box<Expr>,
|
operand: Box<Expr>,
|
||||||
},
|
},
|
||||||
|
IncDec {
|
||||||
|
op: IncDecOp,
|
||||||
|
operand: Box<Expr>,
|
||||||
|
is_prefix: bool,
|
||||||
|
},
|
||||||
FuncCall(String, Vec<Expr>),
|
FuncCall(String, Vec<Expr>),
|
||||||
Assign {
|
Assign {
|
||||||
lvalue: Box<Expr>,
|
lvalue: Box<Expr>,
|
||||||
@@ -124,6 +141,11 @@ pub enum BinaryOp {
|
|||||||
pub enum UnaryOp {
|
pub enum UnaryOp {
|
||||||
Add, Sub, Not,
|
Add, Sub, Not,
|
||||||
}
|
}
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum IncDecOp {
|
||||||
|
Inc,
|
||||||
|
Dec,
|
||||||
|
}
|
||||||
impl BinaryOp {
|
impl BinaryOp {
|
||||||
pub fn from_token_value(token_value: &TokenValue) -> Option<Self> {
|
pub fn from_token_value(token_value: &TokenValue) -> Option<Self> {
|
||||||
match token_value {
|
match token_value {
|
||||||
@@ -227,6 +249,7 @@ impl fmt::Display for Statement {
|
|||||||
Statement::VarDecl(_) => write!(f, "VarDeclStmt"),
|
Statement::VarDecl(_) => write!(f, "VarDeclStmt"),
|
||||||
Statement::If(_) => write!(f, "IfStmt"),
|
Statement::If(_) => write!(f, "IfStmt"),
|
||||||
Statement::While(_) => write!(f, "WhileStmt"),
|
Statement::While(_) => write!(f, "WhileStmt"),
|
||||||
|
Statement::For(_) => write!(f, "ForStmt"),
|
||||||
Statement::Break(_) => write!(f, "BreakStmt"),
|
Statement::Break(_) => write!(f, "BreakStmt"),
|
||||||
Statement::Continue(_) => write!(f, "ContinueStmt"),
|
Statement::Continue(_) => write!(f, "ContinueStmt"),
|
||||||
}
|
}
|
||||||
@@ -249,6 +272,12 @@ impl fmt::Display for WhileStmt {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for ForStmt {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "ForStmt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl fmt::Display for BreakStmt {
|
impl fmt::Display for BreakStmt {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "BreakStmt")
|
write!(f, "BreakStmt")
|
||||||
@@ -277,6 +306,7 @@ impl fmt::Display for ExprValue {
|
|||||||
ExprValue::FuncCall(name, _) => write!(f, "FuncCall({})", name),
|
ExprValue::FuncCall(name, _) => write!(f, "FuncCall({})", name),
|
||||||
ExprValue::Assign { .. } => write!(f, "Assign"),
|
ExprValue::Assign { .. } => write!(f, "Assign"),
|
||||||
ExprValue::UnaryOp { op, .. } => write!(f, "UnaryOp({})", op),
|
ExprValue::UnaryOp { op, .. } => write!(f, "UnaryOp({})", op),
|
||||||
|
ExprValue::IncDec { op, is_prefix, .. } => write!(f, "{}{}", if *is_prefix { "Prefix" } else { "Postfix" }, op),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -317,6 +347,14 @@ impl fmt::Display for UnaryOp {
|
|||||||
write!(f, "{}", op)
|
write!(f, "{}", op)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl fmt::Display for IncDecOp {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
IncDecOp::Inc => write!(f, "Inc"),
|
||||||
|
IncDecOp::Dec => write!(f, "Dec"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
impl fmt::Display for Type {
|
impl fmt::Display for Type {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
|
|||||||
@@ -78,6 +78,23 @@ 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, reg_allocator: &mut RegisterAllocator, var_index_to_stack_offset: &BTreeMap<usize, usize>, instrs: &mut Vec<ARMInstr>) -> RegisterAlloc {
|
||||||
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() {
|
||||||
|
match variable.var_type {
|
||||||
|
VariableType::ParamTemp(param_index) => {
|
||||||
|
if param_index < ARG_REGS.len() {
|
||||||
|
instrs.push(MoveInstr::new_uncond(var_alloc.reg, RegisterOrImm::Reg(ARG_REGS[param_index])));
|
||||||
|
} else {
|
||||||
|
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))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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)));
|
instrs.push(LoadPseudoInstr::new(var_alloc.reg, format!("global_var_{}", variable.index)));
|
||||||
@@ -213,7 +230,7 @@ impl Generator {
|
|||||||
IRInstr::Store(addr, value) => self.emit_store(addr, value, &var_index_to_stack_offset),
|
IRInstr::Store(addr, value) => self.emit_store(addr, value, &var_index_to_stack_offset),
|
||||||
IRInstr::Declare(variable) => {
|
IRInstr::Declare(variable) => {
|
||||||
assert!(!encounter_entry, "Variable declarations must come before entry instruction");
|
assert!(!encounter_entry, "Variable declarations must come before entry instruction");
|
||||||
let size = variable.data_type.size_in_bytes();
|
let size = if variable.data_type.is_array_param() { 4 } else { variable.data_type.size_in_bytes() };
|
||||||
stack_size_needed = (stack_size_needed + size).next_multiple_of(ARM_STACK_ALIGNMENT);
|
stack_size_needed = (stack_size_needed + size).next_multiple_of(ARM_STACK_ALIGNMENT);
|
||||||
var_index_to_stack_offset.insert(variable.index, stack_size_needed);
|
var_index_to_stack_offset.insert(variable.index, stack_size_needed);
|
||||||
},
|
},
|
||||||
|
|||||||
+2
-2
@@ -10,8 +10,8 @@ mod tests {
|
|||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use crate::frontend::lexer::Lexer;
|
use crate::ast::parser::Parser;
|
||||||
use crate::frontend::parser::Parser;
|
use crate::lexer::lexer::Lexer;
|
||||||
use crate::utils::case_list::CaseList;
|
use crate::utils::case_list::CaseList;
|
||||||
use crate::utils::num_sequence::NumberSequence;
|
use crate::utils::num_sequence::NumberSequence;
|
||||||
use crate::ir::generator::Generator as IRGenerator;
|
use crate::ir::generator::Generator as IRGenerator;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::{diagnostic::span::Span, err::CompileError, frontend::err::FrontendError, ir::err::IRError};
|
use crate::{diagnostic::span::Span, err::CompileError, ir::err::IRError, lexer::err::FrontendError};
|
||||||
|
|
||||||
pub mod span;
|
pub mod span;
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::{frontend::err::FrontendError, sema::err::SemaError};
|
use crate::{lexer::err::FrontendError, sema::err::SemaError};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Error)]
|
#[derive(Debug, Clone, PartialEq, Eq, Error)]
|
||||||
pub enum CompileError {
|
pub enum CompileError {
|
||||||
|
|||||||
+446
@@ -0,0 +1,446 @@
|
|||||||
|
use std::collections::{BTreeMap, BTreeSet, HashSet};
|
||||||
|
|
||||||
|
use petgraph::dot::Dot;
|
||||||
|
use petgraph::graph::Graph;
|
||||||
|
|
||||||
|
use crate::ir::types::IRInstr;
|
||||||
|
|
||||||
|
pub type CfgGraph = Graph<String, String>;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct BasicBlock {
|
||||||
|
pub id: usize,
|
||||||
|
pub instrs: Vec<IRInstr>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ControlFlowGraph {
|
||||||
|
pub func_name: String,
|
||||||
|
pub blocks: Vec<BasicBlock>,
|
||||||
|
pub edges: Vec<(usize, usize, String)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||||
|
enum EdgeKind {
|
||||||
|
Branch,
|
||||||
|
Fallthrough,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ControlFlowGraph {
|
||||||
|
pub fn from_body(func_name: impl Into<String>, body: &[IRInstr]) -> Self {
|
||||||
|
let blocks = split_basic_blocks(body);
|
||||||
|
let edges = collect_edges(&blocks)
|
||||||
|
.into_iter()
|
||||||
|
.map(|(from, to, label, _)| (from, to, label))
|
||||||
|
.collect();
|
||||||
|
Self {
|
||||||
|
func_name: func_name.into(),
|
||||||
|
blocks,
|
||||||
|
edges,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_to_graph(&self, graph: &mut CfgGraph) {
|
||||||
|
let mut nodes = Vec::new();
|
||||||
|
for block in &self.blocks {
|
||||||
|
nodes.push(graph.add_node(block_dot_label(&self.func_name, block)));
|
||||||
|
}
|
||||||
|
for (from, to, label) in &self.edges {
|
||||||
|
if let (Some(from), Some(to)) = (nodes.get(*from), nodes.get(*to)) {
|
||||||
|
graph.add_edge(*from, *to, label.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn refresh_edges(&mut self) {
|
||||||
|
self.edges = collect_edges(&self.blocks)
|
||||||
|
.into_iter()
|
||||||
|
.map(|(from, to, label, _)| (from, to, label))
|
||||||
|
.collect();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_unreachable(&mut self) -> bool {
|
||||||
|
if self.blocks.is_empty() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let edges = collect_edges(&self.blocks);
|
||||||
|
let mut adjacency = vec![Vec::new(); self.blocks.len()];
|
||||||
|
for (from, to, _, _) in edges {
|
||||||
|
adjacency[from].push(to);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut reachable = vec![false; self.blocks.len()];
|
||||||
|
let mut stack = vec![0];
|
||||||
|
while let Some(index) = stack.pop() {
|
||||||
|
if reachable[index] {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
reachable[index] = true;
|
||||||
|
for next in &adjacency[index] {
|
||||||
|
stack.push(*next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let old_len = self.blocks.len();
|
||||||
|
self.blocks = self
|
||||||
|
.blocks
|
||||||
|
.drain(..)
|
||||||
|
.enumerate()
|
||||||
|
.filter_map(|(index, block)| reachable[index].then_some(block))
|
||||||
|
.collect();
|
||||||
|
let changed = self.blocks.len() != old_len;
|
||||||
|
if changed {
|
||||||
|
self.refresh_edges();
|
||||||
|
}
|
||||||
|
changed
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_trivial_blocks(&mut self) -> bool {
|
||||||
|
if self.blocks.is_empty() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let edges = collect_edges(&self.blocks);
|
||||||
|
let explicit_pred_count = explicit_predecessor_counts(self.blocks.len(), &edges);
|
||||||
|
let mut redirect = BTreeMap::new();
|
||||||
|
let mut remove = HashSet::new();
|
||||||
|
let mut fallthrough_gotos = Vec::new();
|
||||||
|
|
||||||
|
for (index, block) in self.blocks.iter().enumerate() {
|
||||||
|
if let Some((label, target)) = jump_only_block(block) {
|
||||||
|
let target = resolve_label(target, &redirect);
|
||||||
|
redirect.insert(label, target);
|
||||||
|
for (from, to, _, kind) in &edges {
|
||||||
|
if *to == index && *kind == EdgeKind::Fallthrough {
|
||||||
|
fallthrough_gotos.push((*from, target));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
remove.insert(index);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(label) = label_only_block(block) {
|
||||||
|
if let Some(next_index) = next_kept_index(index, self.blocks.len(), &remove) {
|
||||||
|
if let Some(next_label) = first_label(&self.blocks[next_index]) {
|
||||||
|
redirect.insert(label, resolve_label(next_label, &redirect));
|
||||||
|
remove.insert(index);
|
||||||
|
} else if explicit_pred_count[index] == 0 {
|
||||||
|
remove.insert(index);
|
||||||
|
}
|
||||||
|
} else if explicit_pred_count[index] == 0 {
|
||||||
|
remove.insert(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if redirect.is_empty() && remove.is_empty() && fallthrough_gotos.is_empty() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (from, target) in fallthrough_gotos {
|
||||||
|
if !remove.contains(&from) && !ends_with_terminal(&self.blocks[from]) {
|
||||||
|
self.blocks[from].instrs.push(IRInstr::Goto(target));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for block in &mut self.blocks {
|
||||||
|
rewrite_targets(&mut block.instrs, &redirect);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.blocks = self
|
||||||
|
.blocks
|
||||||
|
.drain(..)
|
||||||
|
.enumerate()
|
||||||
|
.filter_map(|(index, block)| (!remove.contains(&index)).then_some(block))
|
||||||
|
.collect();
|
||||||
|
self.refresh_edges();
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn merge_linear_blocks(&mut self) -> bool {
|
||||||
|
let edges = collect_edges(&self.blocks);
|
||||||
|
let pred_count = predecessor_counts(self.blocks.len(), &edges);
|
||||||
|
|
||||||
|
let mut index = 0;
|
||||||
|
while index + 1 < self.blocks.len() {
|
||||||
|
let successors = edges
|
||||||
|
.iter()
|
||||||
|
.filter_map(|(from, to, _, _)| (*from == index).then_some(*to))
|
||||||
|
.collect::<BTreeSet<_>>();
|
||||||
|
|
||||||
|
if successors.len() == 1 {
|
||||||
|
let successor = *successors.iter().next().unwrap();
|
||||||
|
if successor == index + 1 && pred_count[successor] == 1 {
|
||||||
|
if matches!(self.blocks[index].instrs.last(), Some(IRInstr::Goto(_))) {
|
||||||
|
self.blocks[index].instrs.pop();
|
||||||
|
}
|
||||||
|
let successor_block = self.blocks.remove(successor);
|
||||||
|
self.blocks[index].instrs.extend(successor_block.instrs);
|
||||||
|
self.refresh_edges();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
index += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_instrs(self) -> Vec<IRInstr> {
|
||||||
|
self.blocks
|
||||||
|
.into_iter()
|
||||||
|
.flat_map(|block| block.instrs)
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn optimize_ir(ir: Vec<IRInstr>) -> Vec<IRInstr> {
|
||||||
|
ir.into_iter()
|
||||||
|
.map(|instr| match instr {
|
||||||
|
IRInstr::DefineFunc(func, args, body) => {
|
||||||
|
let func_name = func.name.clone();
|
||||||
|
IRInstr::DefineFunc(func, args, optimize_body(&func_name, body))
|
||||||
|
}
|
||||||
|
other => other,
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ir_to_cfg_dot(ir: &[IRInstr]) -> String {
|
||||||
|
let mut graph = Graph::new();
|
||||||
|
for instr in ir {
|
||||||
|
if let IRInstr::DefineFunc(func, _, body) = instr {
|
||||||
|
ControlFlowGraph::from_body(func.name.clone(), body).add_to_graph(&mut graph);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
format!("{}", Dot::new(&graph))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn optimize_body(func_name: &str, body: Vec<IRInstr>) -> Vec<IRInstr> {
|
||||||
|
let (mut prologue, cfg_body) = split_function_prologue(body);
|
||||||
|
if cfg_body.is_empty() {
|
||||||
|
return prologue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut cfg = ControlFlowGraph::from_body(func_name, &cfg_body);
|
||||||
|
cfg.remove_unreachable();
|
||||||
|
loop {
|
||||||
|
let mut changed = false;
|
||||||
|
changed |= cfg.remove_trivial_blocks();
|
||||||
|
changed |= cfg.remove_unreachable();
|
||||||
|
changed |= cfg.merge_linear_blocks();
|
||||||
|
if !changed {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut body = cfg.into_instrs();
|
||||||
|
remove_redundant_fallthrough_gotos(&mut body);
|
||||||
|
remove_unused_labels(&mut body);
|
||||||
|
prologue.extend(body);
|
||||||
|
prologue
|
||||||
|
}
|
||||||
|
|
||||||
|
fn split_function_prologue(mut body: Vec<IRInstr>) -> (Vec<IRInstr>, Vec<IRInstr>) {
|
||||||
|
if let Some(entry_index) = body.iter().position(|instr| matches!(instr, IRInstr::Entry)) {
|
||||||
|
let cfg_body = body.split_off(entry_index + 1);
|
||||||
|
(body, cfg_body)
|
||||||
|
} else {
|
||||||
|
(Vec::new(), body)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn split_basic_blocks(instrs: &[IRInstr]) -> Vec<BasicBlock> {
|
||||||
|
if instrs.is_empty() {
|
||||||
|
return Vec::new();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut leaders = BTreeSet::new();
|
||||||
|
leaders.insert(0);
|
||||||
|
for (index, instr) in instrs.iter().enumerate() {
|
||||||
|
if matches!(instr, IRInstr::Label(_)) {
|
||||||
|
leaders.insert(index);
|
||||||
|
}
|
||||||
|
if is_terminal(instr) && index + 1 < instrs.len() {
|
||||||
|
leaders.insert(index + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let leader_list = leaders.into_iter().collect::<Vec<_>>();
|
||||||
|
let mut blocks = Vec::new();
|
||||||
|
for (block_id, start) in leader_list.iter().enumerate() {
|
||||||
|
let end = leader_list
|
||||||
|
.get(block_id + 1)
|
||||||
|
.copied()
|
||||||
|
.unwrap_or(instrs.len());
|
||||||
|
blocks.push(BasicBlock {
|
||||||
|
id: block_id,
|
||||||
|
instrs: instrs[*start..end].to_vec(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
blocks
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collect_edges(blocks: &[BasicBlock]) -> Vec<(usize, usize, String, EdgeKind)> {
|
||||||
|
let label_to_block = label_to_block(blocks);
|
||||||
|
let mut edges = Vec::new();
|
||||||
|
|
||||||
|
for (index, block) in blocks.iter().enumerate() {
|
||||||
|
match block.instrs.last() {
|
||||||
|
Some(IRInstr::Goto(label)) => {
|
||||||
|
if let Some(target) = label_to_block.get(label) {
|
||||||
|
edges.push((index, *target, "br".to_string(), EdgeKind::Branch));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(IRInstr::CondGoto(_, true_label, false_label)) => {
|
||||||
|
if let Some(target) = label_to_block.get(true_label) {
|
||||||
|
edges.push((index, *target, "true".to_string(), EdgeKind::Branch));
|
||||||
|
}
|
||||||
|
if let Some(target) = label_to_block.get(false_label) {
|
||||||
|
edges.push((index, *target, "false".to_string(), EdgeKind::Branch));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(IRInstr::Exit(_)) => {}
|
||||||
|
Some(_) | None => {
|
||||||
|
if index + 1 < blocks.len() {
|
||||||
|
edges.push((index, index + 1, "fallthrough".to_string(), EdgeKind::Fallthrough));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
edges
|
||||||
|
}
|
||||||
|
|
||||||
|
fn label_to_block(blocks: &[BasicBlock]) -> BTreeMap<usize, usize> {
|
||||||
|
let mut map = BTreeMap::new();
|
||||||
|
for (index, block) in blocks.iter().enumerate() {
|
||||||
|
for instr in &block.instrs {
|
||||||
|
if let IRInstr::Label(label) = instr {
|
||||||
|
map.insert(*label, index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
map
|
||||||
|
}
|
||||||
|
|
||||||
|
fn predecessor_counts(block_count: usize, edges: &[(usize, usize, String, EdgeKind)]) -> Vec<usize> {
|
||||||
|
let mut counts = vec![0; block_count];
|
||||||
|
for (_, to, _, _) in edges {
|
||||||
|
counts[*to] += 1;
|
||||||
|
}
|
||||||
|
counts
|
||||||
|
}
|
||||||
|
|
||||||
|
fn explicit_predecessor_counts(block_count: usize, edges: &[(usize, usize, String, EdgeKind)]) -> Vec<usize> {
|
||||||
|
let mut counts = vec![0; block_count];
|
||||||
|
for (_, to, _, kind) in edges {
|
||||||
|
if *kind == EdgeKind::Branch {
|
||||||
|
counts[*to] += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
counts
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_kept_index(index: usize, block_count: usize, remove: &HashSet<usize>) -> Option<usize> {
|
||||||
|
(index + 1..block_count).find(|candidate| !remove.contains(candidate))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn first_label(block: &BasicBlock) -> Option<usize> {
|
||||||
|
match block.instrs.first() {
|
||||||
|
Some(IRInstr::Label(label)) => Some(*label),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn label_only_block(block: &BasicBlock) -> Option<usize> {
|
||||||
|
match block.instrs.as_slice() {
|
||||||
|
[IRInstr::Label(label)] => Some(*label),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn jump_only_block(block: &BasicBlock) -> Option<(usize, usize)> {
|
||||||
|
match block.instrs.as_slice() {
|
||||||
|
[IRInstr::Label(label), IRInstr::Goto(target)] => Some((*label, *target)),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_terminal(instr: &IRInstr) -> bool {
|
||||||
|
matches!(instr, IRInstr::Goto(_) | IRInstr::CondGoto(_, _, _) | IRInstr::Exit(_))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ends_with_terminal(block: &BasicBlock) -> bool {
|
||||||
|
block.instrs.last().is_some_and(is_terminal)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rewrite_targets(instrs: &mut [IRInstr], redirect: &BTreeMap<usize, usize>) {
|
||||||
|
for instr in instrs {
|
||||||
|
match instr {
|
||||||
|
IRInstr::Goto(label) => *label = resolve_label(*label, redirect),
|
||||||
|
IRInstr::CondGoto(_, true_label, false_label) => {
|
||||||
|
*true_label = resolve_label(*true_label, redirect);
|
||||||
|
*false_label = resolve_label(*false_label, redirect);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_label(label: usize, redirect: &BTreeMap<usize, usize>) -> usize {
|
||||||
|
let mut current = label;
|
||||||
|
let mut visited = HashSet::new();
|
||||||
|
while let Some(next) = redirect.get(¤t) {
|
||||||
|
if !visited.insert(current) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
current = *next;
|
||||||
|
}
|
||||||
|
current
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_unused_labels(instrs: &mut Vec<IRInstr>) {
|
||||||
|
let mut used = BTreeSet::new();
|
||||||
|
for instr in instrs.iter() {
|
||||||
|
match instr {
|
||||||
|
IRInstr::Goto(label) => {
|
||||||
|
used.insert(*label);
|
||||||
|
}
|
||||||
|
IRInstr::CondGoto(_, true_label, false_label) => {
|
||||||
|
used.insert(*true_label);
|
||||||
|
used.insert(*false_label);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
instrs.retain(|instr| match instr {
|
||||||
|
IRInstr::Label(label) => used.contains(label),
|
||||||
|
_ => true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_redundant_fallthrough_gotos(instrs: &mut Vec<IRInstr>) {
|
||||||
|
let mut index = 0;
|
||||||
|
while index + 1 < instrs.len() {
|
||||||
|
let remove = match (&instrs[index], &instrs[index + 1]) {
|
||||||
|
(IRInstr::Goto(target), IRInstr::Label(label)) => target == label,
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
if remove {
|
||||||
|
instrs.remove(index);
|
||||||
|
} else {
|
||||||
|
index += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn block_dot_label(func_name: &str, block: &BasicBlock) -> String {
|
||||||
|
let instrs = block
|
||||||
|
.instrs
|
||||||
|
.iter()
|
||||||
|
.map(|instr| instr.to_string())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join("\\l");
|
||||||
|
format!("{}::B{}\\l{}\\l", func_name, block.id, instrs)
|
||||||
|
}
|
||||||
+118
-4
@@ -1,6 +1,6 @@
|
|||||||
use std::{collections::BTreeMap, vec};
|
use std::{collections::BTreeMap, vec};
|
||||||
|
|
||||||
use crate::{ir::types::{CmpOp, Function, IRInstr, IRType, MoveRValue, UnaryOp, Variable, VariableOrIntLit, VariableType}, sema::{analyzer::Analyzer as SemaAnalyzer, hir::{HirBlockStmt, HirBreakStmt, HirCompileUnit, HirContinueStmt, HirExpr, HirExprValue, HirFuncDeclStmt, HirGlobalDeclStmt, HirIfElseBranch, HirIfStmt, HirReturnStmt, HirStatement, HirVarDeclStmt, HirWhileStmt}, symbol::{FunctionId, SymbolId}}};
|
use crate::{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::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> {
|
||||||
@@ -64,11 +64,24 @@ 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![];
|
||||||
let var_type = if is_global { VariableType::Global } else { VariableType::Local };
|
|
||||||
for value in var_decl.values {
|
for value in var_decl.values {
|
||||||
|
let var_type = if is_global { 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 is_global {
|
||||||
instrs.push(IRInstr::Declare(var));
|
instrs.push(IRInstr::Declare(var));
|
||||||
|
} else if let Some(init) = value.value {
|
||||||
|
self.current_exit_label.push(None);
|
||||||
|
let (init_instrs, init_var) = match self.generate_expr(init) {
|
||||||
|
Some(res) => res,
|
||||||
|
None => {
|
||||||
|
self.current_exit_label.pop();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.current_exit_label.pop();
|
||||||
|
let target = self.var_manager.get_symbol(value.symbol).unwrap();
|
||||||
|
instrs.extend(init_instrs);
|
||||||
|
instrs.push(IRInstr::Move(target, MoveRValue::Var(init_var.unwrap())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,6 +138,7 @@ impl<'a> Generator<'a> {
|
|||||||
Return(return_stmt) => self.generate_return_stmt(return_stmt),
|
Return(return_stmt) => self.generate_return_stmt(return_stmt),
|
||||||
If(if_stmt) => self.generate_if_stmt(if_stmt),
|
If(if_stmt) => self.generate_if_stmt(if_stmt),
|
||||||
While(while_stmt) => self.generate_while_stmt(while_stmt),
|
While(while_stmt) => self.generate_while_stmt(while_stmt),
|
||||||
|
For(for_stmt) => self.generate_for_stmt(for_stmt),
|
||||||
Break(break_stmt) => self.generate_break_stmt(break_stmt),
|
Break(break_stmt) => self.generate_break_stmt(break_stmt),
|
||||||
Continue(continue_stmt) => self.generate_continue_stmt(continue_stmt),
|
Continue(continue_stmt) => self.generate_continue_stmt(continue_stmt),
|
||||||
Block(block_stmt) => {
|
Block(block_stmt) => {
|
||||||
@@ -193,6 +207,69 @@ impl<'a> Generator<'a> {
|
|||||||
instrs.push(IRInstr::Label(exit_label));
|
instrs.push(IRInstr::Label(exit_label));
|
||||||
instrs
|
instrs
|
||||||
}
|
}
|
||||||
|
fn generate_for_stmt(&mut self, for_stmt: HirForStmt) -> Vec<IRInstr> {
|
||||||
|
let mut instrs = vec![];
|
||||||
|
let cond_label = self.request_label();
|
||||||
|
let body_label = self.request_label();
|
||||||
|
let update_label = self.request_label();
|
||||||
|
let exit_label = self.request_label();
|
||||||
|
|
||||||
|
if let Some(init) = for_stmt.init {
|
||||||
|
match init {
|
||||||
|
HirForInit::Expr(init) => {
|
||||||
|
self.current_exit_label.push(None);
|
||||||
|
let (init_instrs, _) = match self.generate_expr(init) {
|
||||||
|
Some(res) => res,
|
||||||
|
None => {
|
||||||
|
self.current_exit_label.pop();
|
||||||
|
return vec![];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.current_exit_label.pop();
|
||||||
|
instrs.extend(init_instrs);
|
||||||
|
}
|
||||||
|
HirForInit::VarDecl(var_decl) => {
|
||||||
|
instrs.extend(self.generate_var_decl(var_decl, false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
instrs.push(IRInstr::Label(cond_label));
|
||||||
|
if let Some(condition) = for_stmt.condition {
|
||||||
|
self.current_exit_label.push(Some((body_label, exit_label)));
|
||||||
|
let (cond_instrs, _) = match self.generate_expr(condition) {
|
||||||
|
Some(res) => res,
|
||||||
|
None => {
|
||||||
|
self.current_exit_label.pop();
|
||||||
|
return vec![];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.current_exit_label.pop();
|
||||||
|
instrs.extend(cond_instrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
instrs.push(IRInstr::Label(body_label));
|
||||||
|
self.while_exit_label.push((update_label, exit_label));
|
||||||
|
instrs.extend(self.generate_block_stmt(for_stmt.body));
|
||||||
|
self.while_exit_label.pop();
|
||||||
|
|
||||||
|
instrs.push(IRInstr::Label(update_label));
|
||||||
|
if let Some(update) = for_stmt.update {
|
||||||
|
self.current_exit_label.push(None);
|
||||||
|
let (update_instrs, _) = match self.generate_expr(update) {
|
||||||
|
Some(res) => res,
|
||||||
|
None => {
|
||||||
|
self.current_exit_label.pop();
|
||||||
|
return vec![];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.current_exit_label.pop();
|
||||||
|
instrs.extend(update_instrs);
|
||||||
|
}
|
||||||
|
instrs.push(IRInstr::Goto(cond_label));
|
||||||
|
instrs.push(IRInstr::Label(exit_label));
|
||||||
|
instrs
|
||||||
|
}
|
||||||
fn generate_if_stmt(&mut self, if_stmt: HirIfStmt) -> Vec<IRInstr> {
|
fn generate_if_stmt(&mut self, if_stmt: HirIfStmt) -> Vec<IRInstr> {
|
||||||
let mut instrs = vec![];
|
let mut instrs = vec![];
|
||||||
let then_label = self.request_label();
|
let then_label = self.request_label();
|
||||||
@@ -274,9 +351,13 @@ impl<'a> Generator<'a> {
|
|||||||
HirExprValue::Var(symbol) => {
|
HirExprValue::Var(symbol) => {
|
||||||
let var = self.var_manager.get_symbol(symbol).unwrap();
|
let var = self.var_manager.get_symbol(symbol).unwrap();
|
||||||
if var.data_type.is_array() {
|
if var.data_type.is_array() {
|
||||||
|
if var.data_type.is_array_param() {
|
||||||
|
(vec![], Some(var))
|
||||||
|
} else {
|
||||||
let ptr_ty = var.data_type.decay_to_ptr().unwrap();
|
let ptr_ty = var.data_type.decay_to_ptr().unwrap();
|
||||||
let dest = self.var_manager.declare_temp(ptr_ty);
|
let dest = self.var_manager.declare_temp(ptr_ty);
|
||||||
(vec![IRInstr::Move(dest.clone(), MoveRValue::Var(var))], Some(dest))
|
(vec![IRInstr::Move(dest.clone(), MoveRValue::Var(var))], Some(dest))
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
(vec![], Some(var))
|
(vec![], Some(var))
|
||||||
}
|
}
|
||||||
@@ -372,6 +453,35 @@ impl<'a> Generator<'a> {
|
|||||||
};
|
};
|
||||||
(instrs, Some(dest_var))
|
(instrs, Some(dest_var))
|
||||||
},
|
},
|
||||||
|
HirExprValue::IncDec { op, operand, is_prefix } => {
|
||||||
|
let result_ty: IRType = expr.ty.clone().into();
|
||||||
|
let (mut instrs, target, is_addr) = self.generate_lvalue(*operand)?;
|
||||||
|
let old_var = self.var_manager.declare_temp(result_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())));
|
||||||
|
}
|
||||||
|
|
||||||
|
let one = self.var_manager.declare_temp(result_ty.clone());
|
||||||
|
instrs.push(IRInstr::Move(one.clone(), MoveRValue::ConstInt(1)));
|
||||||
|
let new_var = self.var_manager.declare_temp(result_ty.clone());
|
||||||
|
let ir_op = match op {
|
||||||
|
crate::ast::types::IncDecOp::Inc => IRBinaryOp::Add,
|
||||||
|
crate::ast::types::IncDecOp::Dec => IRBinaryOp::Sub,
|
||||||
|
};
|
||||||
|
instrs.push(IRInstr::Binary(new_var.clone(), old_var.clone(), ir_op, one));
|
||||||
|
if is_addr {
|
||||||
|
instrs.push(IRInstr::Store(target, new_var.clone()));
|
||||||
|
} else {
|
||||||
|
instrs.push(IRInstr::Move(target, MoveRValue::Var(new_var.clone())));
|
||||||
|
}
|
||||||
|
if is_prefix {
|
||||||
|
(instrs, Some(new_var))
|
||||||
|
} else {
|
||||||
|
(instrs, Some(old_var))
|
||||||
|
}
|
||||||
|
},
|
||||||
HirExprValue::BinaryOp { lhs, op, rhs } => {
|
HirExprValue::BinaryOp { lhs, op, rhs } => {
|
||||||
// +--------+-------------------------+-------------------------+-------------------------+
|
// +--------+-------------------------+-------------------------+-------------------------+
|
||||||
// | parent | (true_exit, false_exit) | (true_exit, false_exit) | (true_exit, false_exit) |
|
// | parent | (true_exit, false_exit) | (true_exit, false_exit) | (true_exit, false_exit) |
|
||||||
@@ -564,8 +674,12 @@ impl<'a> Generator<'a> {
|
|||||||
HirExprValue::Var(symbol) => {
|
HirExprValue::Var(symbol) => {
|
||||||
let var = self.var_manager.get_symbol(symbol).unwrap();
|
let var = self.var_manager.get_symbol(symbol).unwrap();
|
||||||
if var.data_type.is_array() {
|
if var.data_type.is_array() {
|
||||||
|
if var.data_type.is_array_param() {
|
||||||
|
(vec![], var)
|
||||||
|
} else {
|
||||||
let ptr = self.var_manager.declare_temp(var.data_type.decay_to_ptr().unwrap());
|
let ptr = self.var_manager.declare_temp(var.data_type.decay_to_ptr().unwrap());
|
||||||
(vec![IRInstr::Move(ptr.clone(), MoveRValue::Var(var))], ptr)
|
(vec![IRInstr::Move(ptr.clone(), MoveRValue::Var(var))], ptr)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
(vec![], var)
|
(vec![], var)
|
||||||
}
|
}
|
||||||
@@ -663,8 +777,8 @@ mod tests {
|
|||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use crate::ast::graph::AstGraphExt;
|
use crate::ast::graph::AstGraphExt;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use crate::frontend::lexer::Lexer;
|
use crate::ast::parser::Parser;
|
||||||
use crate::frontend::parser::Parser;
|
use crate::lexer::lexer::Lexer;
|
||||||
use crate::sema::analyzer::Analyzer;
|
use crate::sema::analyzer::Analyzer;
|
||||||
use crate::utils::case_list::CaseList;
|
use crate::utils::case_list::CaseList;
|
||||||
use crate::utils::num_sequence::NumberSequence;
|
use crate::utils::num_sequence::NumberSequence;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
pub mod generator;
|
pub mod generator;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
pub mod err;
|
pub mod err;
|
||||||
|
pub mod cfg;
|
||||||
|
|||||||
+23
-1
@@ -4,6 +4,7 @@ use crate::ast::types::Type as AstType;
|
|||||||
use crate::ast::types::BinaryOp as AstBinaryOp;
|
use crate::ast::types::BinaryOp as AstBinaryOp;
|
||||||
use crate::ir::err::IRError;
|
use crate::ir::err::IRError;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub enum VariableOrIntLit {
|
pub enum VariableOrIntLit {
|
||||||
Var(Variable),
|
Var(Variable),
|
||||||
IntLit(i32),
|
IntLit(i32),
|
||||||
@@ -17,6 +18,7 @@ impl Display for VariableOrIntLit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[derive(Clone)]
|
||||||
pub enum IRInstr {
|
pub enum IRInstr {
|
||||||
Declare(Variable),
|
Declare(Variable),
|
||||||
DefineFunc(Function, Vec<Variable>, Vec<IRInstr>),
|
DefineFunc(Function, Vec<Variable>, Vec<IRInstr>),
|
||||||
@@ -86,6 +88,10 @@ impl IRType {
|
|||||||
matches!(self, IRType::Array(_, _))
|
matches!(self, IRType::Array(_, _))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_array_param(&self) -> bool {
|
||||||
|
matches!(self, IRType::Array(_, dims) if dims.first().is_some_and(|dim| *dim == 0))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn decay_to_ptr(&self) -> Option<IRType> {
|
pub fn decay_to_ptr(&self) -> Option<IRType> {
|
||||||
match self {
|
match self {
|
||||||
IRType::Array(elem, dims) => {
|
IRType::Array(elem, dims) => {
|
||||||
@@ -165,6 +171,7 @@ impl From<AstType> for IRType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[derive(Clone)]
|
||||||
pub enum MoveRValue {
|
pub enum MoveRValue {
|
||||||
Var(Variable),
|
Var(Variable),
|
||||||
ConstInt(i32),
|
ConstInt(i32),
|
||||||
@@ -236,6 +243,18 @@ impl Variable {
|
|||||||
_ => format!("{} {}", self.data_type, self),
|
_ => format!("{} {}", self.data_type, self),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn to_typed_string_with_type(&self, ty: &IRType) -> String {
|
||||||
|
match ty {
|
||||||
|
IRType::Array(_, _) => {
|
||||||
|
let (base_type, dims) = ty.base_type_and_dims();
|
||||||
|
let dims_str = dims.iter().map(|dim| format!("[{}]", dim)).collect::<Vec<_>>().join("");
|
||||||
|
format!("{} {}{}", base_type, self, dims_str)
|
||||||
|
}
|
||||||
|
IRType::Ptr(_) => format!("{}* {}", ty.scalar_base_type(), self),
|
||||||
|
_ => format!("{} {}", ty, self),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Function {
|
pub struct Function {
|
||||||
@@ -247,7 +266,7 @@ pub struct Function {
|
|||||||
impl Function {
|
impl Function {
|
||||||
pub fn to_call_string(&self, args: &Vec<Variable>) -> String {
|
pub fn to_call_string(&self, args: &Vec<Variable>) -> String {
|
||||||
assert!(args.len() == self.parameter_types.len());
|
assert!(args.len() == self.parameter_types.len());
|
||||||
let args_str = args.iter().zip(self.parameter_types.iter()).map(|(arg, param)| format!("{} {}", param, arg)).collect::<Vec<_>>().join(", ");
|
let args_str = args.iter().zip(self.parameter_types.iter()).map(|(arg, param)| arg.to_typed_string_with_type(param)).collect::<Vec<_>>().join(", ");
|
||||||
format!("{} @{}({})", self.return_type, self.name, args_str)
|
format!("{} @{}({})", self.return_type, self.name, args_str)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -260,6 +279,7 @@ impl Function {
|
|||||||
format!("{} @{}({})", self.return_type, self.name, params_str)
|
format!("{} @{}({})", self.return_type, self.name, params_str)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[derive(Clone)]
|
||||||
pub enum BinaryOp {
|
pub enum BinaryOp {
|
||||||
Add,
|
Add,
|
||||||
Sub,
|
Sub,
|
||||||
@@ -267,6 +287,7 @@ pub enum BinaryOp {
|
|||||||
Div,
|
Div,
|
||||||
Mod,
|
Mod,
|
||||||
}
|
}
|
||||||
|
#[derive(Clone)]
|
||||||
pub enum CmpOp {
|
pub enum CmpOp {
|
||||||
Le,
|
Le,
|
||||||
Lt,
|
Lt,
|
||||||
@@ -275,6 +296,7 @@ pub enum CmpOp {
|
|||||||
Ne,
|
Ne,
|
||||||
Eq,
|
Eq,
|
||||||
}
|
}
|
||||||
|
#[derive(Clone)]
|
||||||
pub enum UnaryOp {
|
pub enum UnaryOp {
|
||||||
Neg
|
Neg
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::frontend::types::{Token, TokenValue};
|
use crate::lexer::types::TokenValue;
|
||||||
|
|
||||||
// #[derive(Debug, Clone, PartialEq, Eq, Error)]
|
// #[derive(Debug, Clone, PartialEq, Eq, Error)]
|
||||||
// pub enum ParseError {
|
// pub enum ParseError {
|
||||||
@@ -1,9 +1,8 @@
|
|||||||
use std::{io::BufRead, str::FromStr};
|
use std::str::FromStr;
|
||||||
|
|
||||||
use codespan_reporting::diagnostic;
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::{diagnostic::{Diagnositics, span::{self, Span}}, frontend::{err::LexingError, types::{TokenValue, TypeIdent}}};
|
use crate::{diagnostic::{Diagnositics, span::Span}, lexer::{err::LexingError, types::{TokenValue, TypeIdent}}};
|
||||||
|
|
||||||
use super::types::Token;
|
use super::types::Token;
|
||||||
|
|
||||||
@@ -346,6 +345,9 @@ fn parse_ident(
|
|||||||
if name.eq("while") {
|
if name.eq("while") {
|
||||||
return Ok(TokenValue::While);
|
return Ok(TokenValue::While);
|
||||||
}
|
}
|
||||||
|
if name.eq("for") {
|
||||||
|
return Ok(TokenValue::For);
|
||||||
|
}
|
||||||
if name.eq("break") {
|
if name.eq("break") {
|
||||||
return Ok(TokenValue::Break);
|
return Ok(TokenValue::Break);
|
||||||
}
|
}
|
||||||
@@ -359,6 +361,7 @@ fn parse_ident(
|
|||||||
}
|
}
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use std::io::BufRead;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use crate::utils::case_list::CaseList;
|
use crate::utils::case_list::CaseList;
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
pub mod types;
|
|
||||||
pub mod lexer;
|
|
||||||
pub mod parser;
|
|
||||||
pub mod err;
|
pub mod err;
|
||||||
|
pub mod lexer;
|
||||||
|
pub mod types;
|
||||||
@@ -24,9 +24,9 @@ pub enum TokenValue {
|
|||||||
LBracket, RBracket,
|
LBracket, RBracket,
|
||||||
Comma, Semicolon,
|
Comma, Semicolon,
|
||||||
|
|
||||||
If, Else, While, Return, Break, Continue,
|
If, Else, While, For, Return, Break, Continue,
|
||||||
|
|
||||||
// Eof,
|
Eof,
|
||||||
Unrecognized,
|
Unrecognized,
|
||||||
}
|
}
|
||||||
impl TokenValue {
|
impl TokenValue {
|
||||||
@@ -77,10 +77,11 @@ impl std::fmt::Display for TokenValue {
|
|||||||
TokenValue::If => write!(f, "if"),
|
TokenValue::If => write!(f, "if"),
|
||||||
TokenValue::Else => write!(f, "else"),
|
TokenValue::Else => write!(f, "else"),
|
||||||
TokenValue::While => write!(f, "while"),
|
TokenValue::While => write!(f, "while"),
|
||||||
|
TokenValue::For => write!(f, "for"),
|
||||||
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::Eof => write!(f, "<EOF>"),
|
||||||
TokenValue::Unrecognized => write!(f, "unrecognized"),
|
TokenValue::Unrecognized => write!(f, "unrecognized"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -100,9 +101,9 @@ pub enum TokenKind {
|
|||||||
LBracket, RBracket,
|
LBracket, RBracket,
|
||||||
Comma, Semicolon,
|
Comma, Semicolon,
|
||||||
|
|
||||||
If, Else, While, Return, Break, Continue,
|
If, Else, While, For, Return, Break, Continue,
|
||||||
|
|
||||||
// Eof,
|
Eof,
|
||||||
Unrecognized,
|
Unrecognized,
|
||||||
}
|
}
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, EnumString, AsRefStr)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, EnumString, AsRefStr)]
|
||||||
+12
-3
@@ -1,4 +1,4 @@
|
|||||||
mod frontend;
|
mod lexer;
|
||||||
mod ast;
|
mod ast;
|
||||||
mod ir;
|
mod ir;
|
||||||
mod backend;
|
mod backend;
|
||||||
@@ -11,7 +11,7 @@ use std::{fs::File, io::BufRead};
|
|||||||
|
|
||||||
use clap::Parser as ArgParser;
|
use clap::Parser as ArgParser;
|
||||||
|
|
||||||
use crate::{frontend::{lexer::Lexer, parser::Parser}, ir::generator::Generator, 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)]
|
||||||
@@ -33,6 +33,9 @@ struct Args {
|
|||||||
/// Output file for the generated code (default: stdout)
|
/// Output file for the generated code (default: stdout)
|
||||||
#[arg(short = 'o', long = "output")]
|
#[arg(short = 'o', long = "output")]
|
||||||
output: Option<String>,
|
output: Option<String>,
|
||||||
|
/// Output CFG graph in Graphviz dot format
|
||||||
|
#[arg(long = "cfg-dot")]
|
||||||
|
cfg_dot: Option<String>,
|
||||||
/// Source file to compile
|
/// Source file to compile
|
||||||
source: String,
|
source: String,
|
||||||
}
|
}
|
||||||
@@ -85,7 +88,13 @@ fn main() {
|
|||||||
analyzer.get_diagnostics().print(&format!("{}", source_path.display()), &full_text);
|
analyzer.get_diagnostics().print(&format!("{}", source_path.display()), &full_text);
|
||||||
}
|
}
|
||||||
let mut generator = Generator::new(&analyzer);
|
let mut generator = Generator::new(&analyzer);
|
||||||
let ir = generator.emit(hir);
|
let ir = cfg::optimize_ir(generator.emit(hir));
|
||||||
|
if let Some(cfg_dot_path) = &args.cfg_dot {
|
||||||
|
match std::fs::write(cfg_dot_path, cfg::ir_to_cfg_dot(&ir)) {
|
||||||
|
Ok(_) => println!("CFG graph written to {}", cfg_dot_path),
|
||||||
|
Err(e) => eprintln!("Failed to write CFG graph to {}: {}", cfg_dot_path, e),
|
||||||
|
}
|
||||||
|
}
|
||||||
if args.output_ir {
|
if args.output_ir {
|
||||||
if let Some(output_path) = args.output {
|
if let Some(output_path) = args.output {
|
||||||
match std::fs::write(&output_path, ir.iter().map(|instr| instr.to_string()).collect::<Vec<_>>().join("\n")) {
|
match std::fs::write(&output_path, ir.iter().map(|instr| instr.to_string()).collect::<Vec<_>>().join("\n")) {
|
||||||
|
|||||||
+68
-14
@@ -2,7 +2,7 @@ use std::collections::BTreeMap;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::types::{
|
ast::types::{
|
||||||
ArrayDimension, BinaryOp, BlockStmt, BreakStmt, CompileUnit, ContinueStmt, Expr, ExprValue, FuncDeclStmt,
|
ArrayDimension, BinaryOp, BlockStmt, BreakStmt, CompileUnit, ContinueStmt, Expr, ExprValue, ForInit, ForStmt, FuncDeclStmt,
|
||||||
GlobalDeclStmt, IfElseBranch, IfStmt, ReturnStmt, Statement, VarDeclStmt, WhileStmt,
|
GlobalDeclStmt, IfElseBranch, IfStmt, ReturnStmt, Statement, VarDeclStmt, WhileStmt,
|
||||||
},
|
},
|
||||||
diagnostic::{span::Span, Diagnositics},
|
diagnostic::{span::Span, Diagnositics},
|
||||||
@@ -10,7 +10,7 @@ use crate::{
|
|||||||
err::SemaError,
|
err::SemaError,
|
||||||
hir::{
|
hir::{
|
||||||
HirBlockStmt, HirBreakStmt, HirCompileUnit, HirContinueStmt, HirExpr, HirExprValue,
|
HirBlockStmt, HirBreakStmt, HirCompileUnit, HirContinueStmt, HirExpr, HirExprValue,
|
||||||
HirFuncDeclStmt, HirGlobalDeclStmt, HirIfElseBranch, HirIfStmt, HirParam,
|
HirForInit, HirForStmt, HirFuncDeclStmt, HirGlobalDeclStmt, HirIfElseBranch, HirIfStmt, HirParam,
|
||||||
HirReturnStmt, HirStatement, HirVarDeclStmt, HirVarDeclStmtValue, HirWhileStmt,
|
HirReturnStmt, HirStatement, HirVarDeclStmt, HirVarDeclStmtValue, HirWhileStmt,
|
||||||
},
|
},
|
||||||
symbol::{FunctionId, FunctionSig, SymbolId, SymbolKind, SymbolTable},
|
symbol::{FunctionId, FunctionSig, SymbolId, SymbolKind, SymbolTable},
|
||||||
@@ -39,7 +39,9 @@ impl Analyzer {
|
|||||||
};
|
};
|
||||||
analyzer.declare_builtin_func("putint", vec![SemaType::I32], SemaType::Void);
|
analyzer.declare_builtin_func("putint", vec![SemaType::I32], SemaType::Void);
|
||||||
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("getint", vec![], SemaType::I32);
|
analyzer.declare_builtin_func("getint", vec![], SemaType::I32);
|
||||||
|
analyzer.declare_builtin_func("getarray", vec![SemaType::Array(Box::new(SemaType::I32), vec![0])], SemaType::I32);
|
||||||
analyzer
|
analyzer
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,10 +104,24 @@ impl Analyzer {
|
|||||||
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) {
|
match self.symbols.declare_variable(&value.name, kind, data_type) {
|
||||||
Ok(symbol) => values.push(HirVarDeclStmtValue {
|
Ok(symbol) => {
|
||||||
|
let symbol_type = self.symbols.get_symbol(symbol).ty.clone();
|
||||||
|
let init = value.value.and_then(|expr| {
|
||||||
|
let span = expr.span;
|
||||||
|
let init = self.analyze_expr(expr)?;
|
||||||
|
if init.ty == SemaType::Void {
|
||||||
|
self.add_error(SemaError::InvalidOperand(SemaType::Void), span);
|
||||||
|
} else if !self.type_matches(&symbol_type, &init.ty) {
|
||||||
|
self.add_error(SemaError::TypeMismatch(symbol_type.clone(), init.ty.clone()), span);
|
||||||
|
}
|
||||||
|
Some(init)
|
||||||
|
});
|
||||||
|
values.push(HirVarDeclStmtValue {
|
||||||
symbol,
|
symbol,
|
||||||
name_span: value.name_span,
|
name_span: value.name_span,
|
||||||
}),
|
value: init,
|
||||||
|
});
|
||||||
|
}
|
||||||
Err(e) => self.add_error(e, value.name_span),
|
Err(e) => self.add_error(e, value.name_span),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -185,20 +201,11 @@ impl Analyzer {
|
|||||||
self.add_error(SemaError::InvalidArrayDimension, dimension.span);
|
self.add_error(SemaError::InvalidArrayDimension, dimension.span);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None if is_param && i == 0 => {}
|
None if is_param && i == 0 => dims.push(0),
|
||||||
None => self.add_error(SemaError::InvalidArrayDimension, dimension.span),
|
None => self.add_error(SemaError::InvalidArrayDimension, dimension.span),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if is_param {
|
|
||||||
let pointee_type = if dims.is_empty() {
|
|
||||||
base_type
|
|
||||||
} else {
|
|
||||||
SemaType::Array(Box::new(base_type), dims)
|
SemaType::Array(Box::new(base_type), dims)
|
||||||
};
|
|
||||||
SemaType::Ptr(Box::new(pointee_type))
|
|
||||||
} else {
|
|
||||||
SemaType::Array(Box::new(base_type), dims)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn analyze_block_stmt(&mut self, block_stmt: BlockStmt) -> HirBlockStmt {
|
fn analyze_block_stmt(&mut self, block_stmt: BlockStmt) -> HirBlockStmt {
|
||||||
@@ -216,6 +223,7 @@ impl Analyzer {
|
|||||||
Statement::Return(stmt) => Some(HirStatement::Return(self.analyze_return_stmt(stmt))),
|
Statement::Return(stmt) => Some(HirStatement::Return(self.analyze_return_stmt(stmt))),
|
||||||
Statement::If(stmt) => Some(HirStatement::If(self.analyze_if_stmt(stmt))),
|
Statement::If(stmt) => Some(HirStatement::If(self.analyze_if_stmt(stmt))),
|
||||||
Statement::While(stmt) => Some(HirStatement::While(self.analyze_while_stmt(stmt))),
|
Statement::While(stmt) => Some(HirStatement::While(self.analyze_while_stmt(stmt))),
|
||||||
|
Statement::For(stmt) => Some(HirStatement::For(self.analyze_for_stmt(stmt))),
|
||||||
Statement::Break(stmt) => Some(HirStatement::Break(self.analyze_break_stmt(stmt))),
|
Statement::Break(stmt) => Some(HirStatement::Break(self.analyze_break_stmt(stmt))),
|
||||||
Statement::Continue(stmt) => Some(HirStatement::Continue(self.analyze_continue_stmt(stmt))),
|
Statement::Continue(stmt) => Some(HirStatement::Continue(self.analyze_continue_stmt(stmt))),
|
||||||
Statement::Block(stmt) => {
|
Statement::Block(stmt) => {
|
||||||
@@ -294,6 +302,26 @@ impl Analyzer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn analyze_for_stmt(&mut self, for_stmt: ForStmt) -> HirForStmt {
|
||||||
|
self.symbols.enter_scope();
|
||||||
|
let init = for_stmt.init.and_then(|init| match init {
|
||||||
|
ForInit::Expr(expr) => self.analyze_expr(expr).map(HirForInit::Expr),
|
||||||
|
ForInit::VarDecl(var_decl) => Some(HirForInit::VarDecl(self.analyze_var_decl(var_decl, SymbolKind::Local))),
|
||||||
|
});
|
||||||
|
let condition = for_stmt.condition.map(|expr| self.analyze_condition_expr(expr));
|
||||||
|
let update = for_stmt.update.and_then(|expr| self.analyze_expr(expr));
|
||||||
|
self.loop_depth += 1;
|
||||||
|
let body = self.analyze_block_stmt(for_stmt.body);
|
||||||
|
self.loop_depth -= 1;
|
||||||
|
self.symbols.exit_scope();
|
||||||
|
HirForStmt {
|
||||||
|
init,
|
||||||
|
condition,
|
||||||
|
update,
|
||||||
|
body,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn analyze_break_stmt(&mut self, stmt: BreakStmt) -> HirBreakStmt {
|
fn analyze_break_stmt(&mut self, stmt: BreakStmt) -> HirBreakStmt {
|
||||||
if self.loop_depth == 0 {
|
if self.loop_depth == 0 {
|
||||||
self.add_error(SemaError::BreakOutsideLoop, stmt.span);
|
self.add_error(SemaError::BreakOutsideLoop, stmt.span);
|
||||||
@@ -369,6 +397,27 @@ impl Analyzer {
|
|||||||
span,
|
span,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
ExprValue::IncDec { op, operand, is_prefix } => {
|
||||||
|
if !matches!(operand.value, ExprValue::Var(_) | ExprValue::ArrayAccess { .. }) {
|
||||||
|
self.add_error(SemaError::InvalidAssignmentTarget, operand.span);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let operand_span = operand.span;
|
||||||
|
let operand = self.analyze_expr(*operand)?;
|
||||||
|
if !operand.ty.is_scalar() {
|
||||||
|
self.add_error(SemaError::InvalidOperand(operand.ty.clone()), operand_span);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(HirExpr {
|
||||||
|
ty: operand.ty.clone(),
|
||||||
|
value: HirExprValue::IncDec {
|
||||||
|
op,
|
||||||
|
operand: Box::new(operand),
|
||||||
|
is_prefix,
|
||||||
|
},
|
||||||
|
span,
|
||||||
|
})
|
||||||
|
}
|
||||||
ExprValue::BinaryOp { lhs, op, rhs } => self.analyze_binary_expr(*lhs, op, *rhs, span),
|
ExprValue::BinaryOp { lhs, op, rhs } => self.analyze_binary_expr(*lhs, op, *rhs, span),
|
||||||
ExprValue::FuncCall(func_name, args) => self.analyze_func_call_expr(func_name, args, span),
|
ExprValue::FuncCall(func_name, args) => self.analyze_func_call_expr(func_name, args, span),
|
||||||
}
|
}
|
||||||
@@ -524,6 +573,11 @@ impl Analyzer {
|
|||||||
if expected == actual {
|
if expected == actual {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if let (SemaType::Array(expected_elem, expected_dims), SemaType::Array(actual_elem, actual_dims)) = (expected, actual) {
|
||||||
|
return expected_elem == actual_elem
|
||||||
|
&& expected_dims.len() == actual_dims.len()
|
||||||
|
&& expected_dims.iter().zip(actual_dims.iter()).all(|(expected_dim, actual_dim)| *expected_dim == 0 || expected_dim == actual_dim);
|
||||||
|
}
|
||||||
matches!((expected, actual), (SemaType::Ptr(expected_elem), SemaType::Array(_, _)) if actual.indexed_type().is_some_and(|ty| &ty == expected_elem.as_ref()))
|
matches!((expected, actual), (SemaType::Ptr(expected_elem), SemaType::Array(_, _)) if actual.indexed_type().is_some_and(|ty| &ty == expected_elem.as_ref()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ pub struct HirVarDeclStmt {
|
|||||||
pub struct HirVarDeclStmtValue {
|
pub struct HirVarDeclStmtValue {
|
||||||
pub symbol: SymbolId,
|
pub symbol: SymbolId,
|
||||||
pub name_span: Span,
|
pub name_span: Span,
|
||||||
|
pub value: Option<HirExpr>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct HirFuncDeclStmt {
|
pub struct HirFuncDeclStmt {
|
||||||
@@ -46,6 +47,7 @@ pub enum HirStatement {
|
|||||||
VarDecl(HirVarDeclStmt),
|
VarDecl(HirVarDeclStmt),
|
||||||
If(HirIfStmt),
|
If(HirIfStmt),
|
||||||
While(HirWhileStmt),
|
While(HirWhileStmt),
|
||||||
|
For(HirForStmt),
|
||||||
Break(HirBreakStmt),
|
Break(HirBreakStmt),
|
||||||
Continue(HirContinueStmt),
|
Continue(HirContinueStmt),
|
||||||
}
|
}
|
||||||
@@ -67,6 +69,18 @@ pub struct HirWhileStmt {
|
|||||||
pub body: HirBlockStmt,
|
pub body: HirBlockStmt,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct HirForStmt {
|
||||||
|
pub init: Option<HirForInit>,
|
||||||
|
pub condition: Option<HirExpr>,
|
||||||
|
pub update: Option<HirExpr>,
|
||||||
|
pub body: HirBlockStmt,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum HirForInit {
|
||||||
|
Expr(HirExpr),
|
||||||
|
VarDecl(HirVarDeclStmt),
|
||||||
|
}
|
||||||
|
|
||||||
pub struct HirBreakStmt {
|
pub struct HirBreakStmt {
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
}
|
}
|
||||||
@@ -102,6 +116,11 @@ pub enum HirExprValue {
|
|||||||
op: UnaryOp,
|
op: UnaryOp,
|
||||||
operand: Box<HirExpr>,
|
operand: Box<HirExpr>,
|
||||||
},
|
},
|
||||||
|
IncDec {
|
||||||
|
op: crate::ast::types::IncDecOp,
|
||||||
|
operand: Box<HirExpr>,
|
||||||
|
is_prefix: bool,
|
||||||
|
},
|
||||||
FuncCall(FunctionId, Vec<HirExpr>),
|
FuncCall(FunctionId, Vec<HirExpr>),
|
||||||
Assign {
|
Assign {
|
||||||
lvalue: Box<HirExpr>,
|
lvalue: Box<HirExpr>,
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ impl SemaType {
|
|||||||
SemaType::Array(elem, dims) => {
|
SemaType::Array(elem, dims) => {
|
||||||
if dims.len() == 1 {
|
if dims.len() == 1 {
|
||||||
Some((**elem).clone())
|
Some((**elem).clone())
|
||||||
|
} else if dims.first().is_some_and(|dim| *dim == 0) {
|
||||||
|
Some(SemaType::Array(elem.clone(), dims[1..].to_vec()))
|
||||||
} else {
|
} else {
|
||||||
Some(SemaType::Array(elem.clone(), dims[1..].to_vec()))
|
Some(SemaType::Array(elem.clone(), dims[1..].to_vec()))
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user