From c8fc31e74f1b1a0efd4cf466d8d32032ed3965c7 Mon Sep 17 00:00:00 2001 From: Lakshay Date: Sun, 11 Aug 2024 21:15:12 +0530 Subject: [PATCH 1/3] (#1) initial implementation of reduce ast pass (this doe s not work as of yet) --- eg/test.irl | 2 + eg/test.irl.dot | 33 ++++++++++++ src/cli.rs | 23 +++++++++ src/fe/ast.rs | 80 ++++++++++++++++++++++++++++-- src/fe/loc.rs | 5 +- src/main.rs | 76 ++++++++++++++++++---------- src/mw/default_ast_pass_manager.rs | 17 ++++++- src/mw/mod.rs | 1 + src/mw/reduce_ast_pass.rs | 46 +++++++++++++++++ src/mw/validate_iden_pass.rs | 3 ++ 10 files changed, 252 insertions(+), 34 deletions(-) create mode 100644 eg/test.irl.dot create mode 100644 src/mw/reduce_ast_pass.rs diff --git a/eg/test.irl b/eg/test.irl index 9590057..f0d1266 100644 --- a/eg/test.irl +++ b/eg/test.irl @@ -11,6 +11,8 @@ function fib, 1 a = t i = i + 1 goto begin + d = 0 + d = d + 1 // label hello // ret 3 label end diff --git a/eg/test.irl.dot b/eg/test.irl.dot new file mode 100644 index 0000000..3a92f4e --- /dev/null +++ b/eg/test.irl.dot @@ -0,0 +1,33 @@ +digraph "./eg/test.irl.dot" { + subgraph cluster_fib { + label="fib"; + graph [style=filled]; + fib_ENTRY [label="ENTRY"]; + fib_EXIT [label="EXIT"]; + fib_ENTRY -> fib_BB0; + fib_BB0 [shape=record label="[0]\n"]; + fib_BB0 -> fib_BB1; + fib_BB1 [shape=record label="[1]\na = 0\lb = 1\li = 1\l"]; + fib_BB1 -> fib_BB2; + fib_BB2 [shape=record label="[2 - begin]\n"]; + fib_BB2 -> fib_BB3; + fib_BB2 -> fib_BB4; + fib_BB3 [shape=record label="[3 - end]\nret b\l"]; + fib_BB3 -> fib_EXIT; + fib_BB4 [shape=record label="[4]\nt = b\lb = a + b\la = t\li = i + 1\l"]; + fib_BB4 -> fib_BB2; + } + + subgraph cluster_main { + label="main"; + graph [style=filled]; + main_ENTRY [label="ENTRY"]; + main_EXIT [label="EXIT"]; + main_ENTRY -> main_BB0; + main_BB0 [shape=record label="[0]\n"]; + main_BB0 -> main_BB1; + main_BB1 [shape=record label="[1]\nparam 3\la = call fib, 1\lret a\l"]; + main_BB1 -> main_EXIT; + } + +} diff --git a/src/cli.rs b/src/cli.rs index 60b35ac..be13d1f 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,5 +1,28 @@ use clap::{Arg, Command, ArgAction}; +pub struct CliOptions { + pub filepath: String, + pub cfg: bool, + pub debug: bool, + pub verbose: bool, +} + +impl CliOptions { + pub fn new() -> Self { + let match_result = cli().get_matches(); + let compile_args = match_result.subcommand_matches("compile"); + Self { + filepath: compile_args.unwrap().get_one::("filepath").unwrap().to_string(), + cfg: *compile_args.unwrap().get_one::("cfg").unwrap(), + debug: *compile_args.unwrap().get_one::("debug").unwrap(), + verbose: *compile_args.unwrap().get_one::("verbose").unwrap(), + } + } + pub fn verbose_message(&self, message: String) { + if self.verbose {println!("{}: info: {}", self.filepath, message);} + } +} + pub fn cli() -> Command { Command::new("irl") .about("Intermediate Representation Language: Minimal implementation of LLVM") diff --git a/src/fe/ast.rs b/src/fe/ast.rs index 98904e3..7001a72 100644 --- a/src/fe/ast.rs +++ b/src/fe/ast.rs @@ -13,6 +13,12 @@ pub struct IdenAstNode { pub loc: Loc, } +impl PartialEq for IdenAstNode { + fn eq(&self, other: &Self) -> bool { + self.name == other.name + } +} + impl IdenAstNode { fn print(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{}", self.name) @@ -25,6 +31,12 @@ pub struct NumAstNode { pub loc: Loc, } +impl PartialEq for NumAstNode { + fn eq(&self, other: &Self) -> bool { + self.num == other.num + } +} + impl NumAstNode { fn print(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{}", self.num) @@ -39,6 +51,12 @@ pub struct CallAstNode { pub loc: Loc, } +impl PartialEq for CallAstNode { + fn eq(&self, other: &Self) -> bool { + self.name == other.name && self.params == other.params + } +} + impl CallAstNode { fn print(&self, f: &mut std::fmt::Formatter, indent_sz: usize) -> std::fmt::Result { for param in self.params.iter() { @@ -50,7 +68,7 @@ impl CallAstNode { } } -#[derive(Clone)] +#[derive(Clone, PartialEq)] pub enum ArithOp { Sum, Sub, Mul, Div, } @@ -91,13 +109,19 @@ pub struct ArithAstNode { pub loc: Loc, } +impl PartialEq for ArithAstNode { + fn eq(&self, other: &Self) -> bool { + self.lhs == other.lhs && self.op == other.op && self.rhs == other.rhs + } +} + impl ArithAstNode { fn print(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{} {} {}", self.lhs, self.op, self.rhs) } } -#[derive(Clone)] +#[derive(Clone, PartialEq)] pub enum RelOp { Eq, Neq, Gt, Lt, Ge, Le, } @@ -142,13 +166,19 @@ pub struct RelopAstNode { pub loc: Loc, } +impl PartialEq for RelopAstNode { + fn eq(&self, other: &Self) -> bool { + self.lhs == other.lhs && self.op == other.op && self.rhs == other.rhs + } +} + impl RelopAstNode { fn print(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{} {} {}", self.lhs, self.op, self.rhs) } } -#[derive(Clone)] +#[derive(Clone, PartialEq)] pub enum UnaryOp { Neg, } @@ -180,6 +210,12 @@ pub struct UnaryAstNode { pub loc: Loc, } +impl PartialEq for UnaryAstNode { + fn eq(&self, other: &Self) -> bool { + self.var == other.var && self.op == other.op + } +} + impl UnaryAstNode { fn print(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{}{}", self.op, self.var) @@ -194,6 +230,12 @@ pub struct FunctionAstNode { pub loc: Loc, } +impl PartialEq for FunctionAstNode { + fn eq(&self, other: &Self) -> bool { + self.name == other.name && self.args == other.args && self.body == other.body + } +} + impl FunctionAstNode { fn print(&self, f: &mut std::fmt::Formatter, indent_sz: usize) -> std::fmt::Result { print_indent(f, indent_sz); @@ -216,6 +258,12 @@ pub struct AssignmentAstNode { pub loc: Loc, } +impl PartialEq for AssignmentAstNode { + fn eq(&self, other: &Self) -> bool { + self.name == other.name && self.var == other.var + } +} + impl AssignmentAstNode { fn print(&self, f: &mut std::fmt::Formatter, indent_sz: usize) -> std::fmt::Result { print_indent(f, indent_sz); @@ -229,6 +277,12 @@ pub struct GotoAstNode { pub loc: Loc, } +impl PartialEq for GotoAstNode { + fn eq(&self, other: &Self) -> bool { + self.name == other.name + } +} + impl GotoAstNode { fn print(&self, f: &mut std::fmt::Formatter, indent_sz: usize) -> std::fmt::Result { print_indent(f, indent_sz); @@ -243,6 +297,12 @@ pub struct LabelAstNode { pub loc: Loc, } +impl PartialEq for LabelAstNode { + fn eq(&self, other: &Self) -> bool { + self.name == other.name && self.body == other.body + } +} + impl LabelAstNode { fn print(&self, f: &mut std::fmt::Formatter, indent_sz: usize) -> std::fmt::Result { print_indent(f, indent_sz); @@ -261,6 +321,12 @@ pub struct IfAstNode { pub loc: Loc, } +impl PartialEq for IfAstNode { + fn eq(&self, other: &Self) -> bool { + self.condition == other.condition && self.label == other.label + } +} + impl IfAstNode { fn print(&self, f: &mut std::fmt::Formatter, indent_sz: usize) -> std::fmt::Result { print_indent(f, indent_sz); @@ -274,6 +340,12 @@ pub struct RetAstNode { pub loc: Loc, } +impl PartialEq for RetAstNode { + fn eq(&self, other: &Self) -> bool { + self.var == other.var + } +} + impl RetAstNode { fn print(&self, f: &mut std::fmt::Formatter, indent_sz: usize) -> std::fmt::Result { print_indent(f, indent_sz); @@ -299,7 +371,7 @@ pub fn value_join(v1: Value, v2: Value) -> Value { } } -#[derive(Clone)] +#[derive(Clone, PartialEq)] pub enum AstNode { Iden(IdenAstNode), Num(NumAstNode), diff --git a/src/fe/loc.rs b/src/fe/loc.rs index 3d1dd5b..e7eb190 100644 --- a/src/fe/loc.rs +++ b/src/fe/loc.rs @@ -15,12 +15,13 @@ impl Loc { pub fn new(row: usize, col: usize, filepath: String) -> Self { Self{row, col, filepath} } - pub fn error(&self, message: String) { eprintln!("{}: error: {}", self, message); std::process::exit(1); } - + pub fn message(&self, message: String) { + eprintln!("{}: {}", self, message); + } pub fn null() -> Self { Self{row: 0, col: 0, filepath: String::from("")} } diff --git a/src/main.rs b/src/main.rs index fdbd412..1081fad 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,7 @@ -use crate::fe::{lexer::Lexer, parser::Parser}; +use crate::fe::{lexer::Lexer, parser::Parser, ast::AstNode}; use crate::mw::default_ast_pass_manager::*; use crate::opt::{default_compiler_pass_manager::*, cfg::*}; -use crate::cli::cli; +use crate::cli::CliOptions; use std::process::Command; pub mod fe; @@ -10,40 +10,64 @@ pub mod opt; pub mod cli; fn main() { - let match_result = cli().get_matches(); - let compile_args = match_result.subcommand_matches("compile"); - let filepath: String = compile_args.unwrap().get_one::("filepath").unwrap().to_string(); - let cfg: bool = *compile_args.unwrap().get_one::("cfg").unwrap(); - let debug: bool = *compile_args.unwrap().get_one::("debug").unwrap(); - let verbose: bool = *compile_args.unwrap().get_one::("verbose").unwrap(); - - let lexer = Lexer::new(filepath.clone()); - let mut parser = Parser::new(lexer.tokens); - run_default_ast_pass_manager(&mut parser.nodes); + let options: CliOptions = CliOptions::new(); - if debug { - for node in parser.nodes.iter() { + // module: fe + let lexer = Lexer::new(options.filepath.clone()); + options.verbose_message(String::from("lexing complete")); + let parser = Parser::new(lexer.tokens); + options.verbose_message(String::from("parsing complete")); + let mut ast: Vec = parser.nodes; + if options.debug { + println!("Initial AST"); + println!("==========="); + for node in ast.iter() { println!("{}", node); } } + options.verbose_message(String::from("FE over")); - let mut cfg_table: Vec = cfg_table_from_program(&parser.nodes); - - run_default_compiler_pass_manager(&mut cfg_table); + // module: mw + run_default_ast_pass_manager(&mut ast); + options.verbose_message(String::from("MW over")); + if options.debug { + println!("MW Optimized AST"); + println!("==========="); + for node in ast.iter() { + println!("{}", node); + } + } - if cfg { - let dot_filepath: String = format!("{}.dot", filepath.clone()); + // module: opt + let mut cfg_table: Vec = cfg_table_from_program(&ast); + run_default_compiler_pass_manager(&mut cfg_table); + if options.cfg { + let dot_filepath: String = format!("{}.dot", options.filepath.clone()); dump_cfg_table_to_svg(&cfg_table, dot_filepath.clone()); Command::new("dot").arg("-Tsvg").arg("-O").arg(dot_filepath.clone()); - if verbose { - println!("info: created control flow graph svg '{}'", dot_filepath.clone()); + options.verbose_message(format!("created control flow graph svg '{}'", dot_filepath)); + } + options.verbose_message(format!("OPT over")); + + ast.clear(); + for cfg in cfg_table.iter() { + ast.push(cfg.generate_ast()); + } + if options.debug { + println!("OPT Optimized AST"); + println!("==========="); + for node in ast.iter() { + println!("{}", node); } } - - if debug { - println!("converted AST"); - for cfg in cfg_table.iter() { - println!("{}", cfg.generate_ast()); + + // module: mw + run_default_ast_pass_manager(&mut ast); + if options.debug { + println!("optimized AST"); + println!("============="); + for node in ast.iter() { + println!("{}", node); println!(""); } } diff --git a/src/mw/default_ast_pass_manager.rs b/src/mw/default_ast_pass_manager.rs index 28e06ff..d3f2716 100644 --- a/src/mw/default_ast_pass_manager.rs +++ b/src/mw/default_ast_pass_manager.rs @@ -1,10 +1,23 @@ use crate::{fe::ast::AstNode, mw::pass::*}; use crate::mw::validate_iden_pass::ValidateIdenPass; use crate::mw::add_goto_pass::AddGotoPass; +use crate::mw::reduce_ast_pass::ReduceAstPass; + +const PASS_MAX_APPLICATION_LIMIT: usize = 100; pub fn run_default_ast_pass_manager(nodes: &mut Vec) { let mut ast_pass_manager: AstPassManager = AstPassManager::new(); - ast_pass_manager.add(ValidateIdenPass{}); ast_pass_manager.add(AddGotoPass{}); - ast_pass_manager.run(nodes); + // ast_pass_manager.add(ReduceAstPass{}); + // ast_pass_manager.add(ValidateIdenPass{}); + + let mut new_nodes: Vec = Vec::new(); + let mut i: usize = 0; + while i < PASS_MAX_APPLICATION_LIMIT && new_nodes != *nodes { + new_nodes = nodes.clone(); + ast_pass_manager.run(nodes); + i += 1; + } + + *nodes = new_nodes; } diff --git a/src/mw/mod.rs b/src/mw/mod.rs index c38a6ee..c78da40 100644 --- a/src/mw/mod.rs +++ b/src/mw/mod.rs @@ -1,4 +1,5 @@ pub mod pass; pub mod validate_iden_pass; pub mod add_goto_pass; +pub mod reduce_ast_pass; pub mod default_ast_pass_manager; diff --git a/src/mw/reduce_ast_pass.rs b/src/mw/reduce_ast_pass.rs new file mode 100644 index 0000000..f55e998 --- /dev/null +++ b/src/mw/reduce_ast_pass.rs @@ -0,0 +1,46 @@ +use std::collections::HashSet; +use crate::{fe::ast::*, mw::pass::AstPass}; + +pub struct ReduceAstPass; + +/* + * Currently this pass just removes redundent labels and gotos + */ + +impl AstPass for ReduceAstPass { + fn apply(&self, nodes: &mut Vec) { + for node in nodes.iter_mut() { + if let AstNode::Function(ref mut function_node) = node { + reduce_function_node(function_node); + } + } + } +} + +fn get_useful_labels(body: &Vec) -> HashSet { + let mut labels: HashSet = HashSet::new(); + for node in body.iter() { + match node { + AstNode::Function(_) => unreachable!(), + AstNode::Label(label_node) => labels.extend(get_useful_labels(&label_node.body)), + AstNode::Goto(goto_node) => {labels.insert(goto_node.name.clone());}, + AstNode::If(if_node) => {labels.insert(if_node.label.clone());}, + _ => {} + } + } + labels +} + +fn reduce_function_node(function_node: &mut FunctionAstNode) { + let useful_labels: HashSet = get_useful_labels(&function_node.body); + let mut new_body: Vec = Vec::new(); + for node in function_node.body.iter() { + if let AstNode::Label(label_node) = node { + if !useful_labels.contains(&label_node.name) { + continue; + } + } + new_body.push(node.clone()); + } + function_node.body = new_body; +} diff --git a/src/mw/validate_iden_pass.rs b/src/mw/validate_iden_pass.rs index e58f6c9..741dc13 100644 --- a/src/mw/validate_iden_pass.rs +++ b/src/mw/validate_iden_pass.rs @@ -15,6 +15,7 @@ impl Context { } fn validate_iden(&self, node: &IdenAstNode) { if !self.idens.contains(&node.name) { + node.loc.message(String::from("validate_iden_pass: failed")); node.loc.error(format!("unknown identifier '{}'", node.name)); } } @@ -23,11 +24,13 @@ impl Context { } fn validate_label(&self, label: &String, loc: Loc) { if !self.labels.contains(label) { + loc.message(String::from("validate_iden_pass: failed")); loc.error(format!("unknown label identifier '{}'", label)); } } fn insert_label(&mut self, label: &String, loc: Loc) { if self.labels.contains(label) { + loc.message(String::from("validate_iden_pass: failed")); loc.error(format!("label identifier already exists '{}'", label)); } let _ = self.labels.insert(label.clone()); From 7d19a3af79959eee6aefefd2ebccc6b49b98a222 Mon Sep 17 00:00:00 2001 From: Lakshay Date: Sun, 11 Aug 2024 22:55:34 +0530 Subject: [PATCH 2/3] Revert "Update .gitlab-ci.yml file" This reverts commit 8e042c2f24eaf08f364695777a570b35565f34be. --- .gitlab-ci.yml | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 .gitlab-ci.yml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml deleted file mode 100644 index 6b90db9..0000000 --- a/.gitlab-ci.yml +++ /dev/null @@ -1,17 +0,0 @@ -stages: - - build - -rust-latest: - stage: build - image: rust:latest - script: - - cargo build --verbose - - cargo test --verbose - -rust-nightly: - stage: build - image: rustlang/rust:nightly - script: - - cargo build --verbose - - cargo test --verbose - allow_failure: true \ No newline at end of file From 0d65b0daa3bd99dc703628762b6b2dfa1a6e75b0 Mon Sep 17 00:00:00 2001 From: Lakshay Date: Sun, 11 Aug 2024 23:22:26 +0530 Subject: [PATCH 3/3] (resolved #1, #2) removed reduce ast pass and fixed goto before label in function entry --- src/main.rs | 11 ------- src/mw/default_ast_pass_manager.rs | 16 ++++------- src/mw/mod.rs | 1 - src/mw/reduce_ast_pass.rs | 46 ------------------------------ src/opt/cfg.rs | 12 ++++++-- 5 files changed, 15 insertions(+), 71 deletions(-) delete mode 100644 src/mw/reduce_ast_pass.rs diff --git a/src/main.rs b/src/main.rs index 1081fad..e6069eb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -60,15 +60,4 @@ fn main() { println!("{}", node); } } - - // module: mw - run_default_ast_pass_manager(&mut ast); - if options.debug { - println!("optimized AST"); - println!("============="); - for node in ast.iter() { - println!("{}", node); - println!(""); - } - } } diff --git a/src/mw/default_ast_pass_manager.rs b/src/mw/default_ast_pass_manager.rs index d3f2716..6811b22 100644 --- a/src/mw/default_ast_pass_manager.rs +++ b/src/mw/default_ast_pass_manager.rs @@ -1,23 +1,17 @@ use crate::{fe::ast::AstNode, mw::pass::*}; use crate::mw::validate_iden_pass::ValidateIdenPass; use crate::mw::add_goto_pass::AddGotoPass; -use crate::mw::reduce_ast_pass::ReduceAstPass; -const PASS_MAX_APPLICATION_LIMIT: usize = 100; +const PASS_MAX_APPLICATION_LIMIT: usize = 1; pub fn run_default_ast_pass_manager(nodes: &mut Vec) { let mut ast_pass_manager: AstPassManager = AstPassManager::new(); + ast_pass_manager.add(ValidateIdenPass{}); ast_pass_manager.add(AddGotoPass{}); - // ast_pass_manager.add(ReduceAstPass{}); - // ast_pass_manager.add(ValidateIdenPass{}); - let mut new_nodes: Vec = Vec::new(); - let mut i: usize = 0; - while i < PASS_MAX_APPLICATION_LIMIT && new_nodes != *nodes { - new_nodes = nodes.clone(); + for _ in 0..PASS_MAX_APPLICATION_LIMIT { + let prev_nodes: Vec = nodes.clone(); ast_pass_manager.run(nodes); - i += 1; + if prev_nodes == *nodes {break;} } - - *nodes = new_nodes; } diff --git a/src/mw/mod.rs b/src/mw/mod.rs index c78da40..c38a6ee 100644 --- a/src/mw/mod.rs +++ b/src/mw/mod.rs @@ -1,5 +1,4 @@ pub mod pass; pub mod validate_iden_pass; pub mod add_goto_pass; -pub mod reduce_ast_pass; pub mod default_ast_pass_manager; diff --git a/src/mw/reduce_ast_pass.rs b/src/mw/reduce_ast_pass.rs deleted file mode 100644 index f55e998..0000000 --- a/src/mw/reduce_ast_pass.rs +++ /dev/null @@ -1,46 +0,0 @@ -use std::collections::HashSet; -use crate::{fe::ast::*, mw::pass::AstPass}; - -pub struct ReduceAstPass; - -/* - * Currently this pass just removes redundent labels and gotos - */ - -impl AstPass for ReduceAstPass { - fn apply(&self, nodes: &mut Vec) { - for node in nodes.iter_mut() { - if let AstNode::Function(ref mut function_node) = node { - reduce_function_node(function_node); - } - } - } -} - -fn get_useful_labels(body: &Vec) -> HashSet { - let mut labels: HashSet = HashSet::new(); - for node in body.iter() { - match node { - AstNode::Function(_) => unreachable!(), - AstNode::Label(label_node) => labels.extend(get_useful_labels(&label_node.body)), - AstNode::Goto(goto_node) => {labels.insert(goto_node.name.clone());}, - AstNode::If(if_node) => {labels.insert(if_node.label.clone());}, - _ => {} - } - } - labels -} - -fn reduce_function_node(function_node: &mut FunctionAstNode) { - let useful_labels: HashSet = get_useful_labels(&function_node.body); - let mut new_body: Vec = Vec::new(); - for node in function_node.body.iter() { - if let AstNode::Label(label_node) = node { - if !useful_labels.contains(&label_node.name) { - continue; - } - } - new_body.push(node.clone()); - } - function_node.body = new_body; -} diff --git a/src/opt/cfg.rs b/src/opt/cfg.rs index 3bf3e04..2267bea 100644 --- a/src/opt/cfg.rs +++ b/src/opt/cfg.rs @@ -213,8 +213,16 @@ impl ControlFlowGraph { pub fn generate_ast(&self) -> AstNode { let mut function_node: FunctionAstNode = self.function.clone(); function_node.body.clear(); - for basic_block_ref in self.basic_blocks.iter() { - function_node.body.push(AstNode::Label(basic_block_ref.borrow().generate_label_ast_node())); + for (id, basic_block_ref) in self.basic_blocks.iter().enumerate() { + if id != self.entry { + function_node.body.push(AstNode::Label(basic_block_ref.borrow().generate_label_ast_node())); + continue; + } + if let Some(Jump::Unconditional(jump_node_ref)) = &basic_block_ref.borrow().next { + function_node.body.push(AstNode::Goto(GotoAstNode{ + name: jump_node_ref.upgrade().unwrap().borrow().label(), loc: Loc::null() + })); + } } return AstNode::Function(function_node); }