From 07cff6b22b6aa2eb6d851338fb17672711539133 Mon Sep 17 00:00:00 2001 From: Felix Schwickerath Date: Mon, 2 Jun 2025 11:54:08 +0200 Subject: [PATCH 01/52] feat: implement various instructions, IR and parsing elements --- Cargo.lock | 152 ++++++++++++++ Cargo.toml | 2 + src/backend/aasm.rs | 23 +++ src/backend/codegen.rs | 319 +++++++++++++++++++++++++++++- src/backend/regalloc.rs | 1 - src/ir/constructor.rs | 411 ++++++++++++++++++++++++++++++++++++-- src/ir/graph.rs | 10 +- src/ir/node.rs | 427 +++++++++++++++++++++++++++++++++++++++- src/lexer/mod.rs | 121 +++++++++++- src/lexer/token.rs | 113 ++++++++++- src/main.rs | 5 + src/parser/ast.rs | 67 ++++++- src/parser/mod.rs | 248 ++++++++++++++++++----- src/semantic/mod.rs | 32 ++- 14 files changed, 1841 insertions(+), 90 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 550940f..93a0283 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,6 +19,8 @@ name = "compiler_design" version = "0.1.0" dependencies = [ "rand", + "tracing", + "tracing-subscriber", ] [[package]] @@ -33,12 +35,52 @@ dependencies = [ "wasi", ] +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + [[package]] name = "libc" version = "0.2.172" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + [[package]] name = "ppv-lite86" version = "0.2.21" @@ -101,6 +143,21 @@ dependencies = [ "getrandom", ] +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "smallvec" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" + [[package]] name = "syn" version = "2.0.101" @@ -112,12 +169,85 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +dependencies = [ + "nu-ansi-term", + "sharded-slab", + "smallvec", + "thread_local", + "tracing-core", + "tracing-log", +] + [[package]] name = "unicode-ident" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + [[package]] name = "wasi" version = "0.14.2+wasi-0.2.4" @@ -127,6 +257,28 @@ dependencies = [ "wit-bindgen-rt", ] +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "wit-bindgen-rt" version = "0.39.0" diff --git a/Cargo.toml b/Cargo.toml index 1c9dd12..6a4efe3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,3 +5,5 @@ edition = "2021" [dependencies] rand = "0.9.1" +tracing = { version = "0.1.41", features=["release_max_level_warn"]} +tracing-subscriber = "0.3.19" diff --git a/src/backend/aasm.rs b/src/backend/aasm.rs index e69de29..11290bc 100644 --- a/src/backend/aasm.rs +++ b/src/backend/aasm.rs @@ -0,0 +1,23 @@ +use super::regalloc::Register; + +pub enum Instruction { + Mov(Box, Box), + Add(Box, Box), + Sub(Box, Box), + Idiv(Box, Box), + Imul(Box, Box), + Sall(Box, Box), + Sarl(Box, Box), + Jmp(String), + Cmp(Box, Box), + Test(Box, Box), + Jb(String), + Jbe(String), + Je(String), + Jne(String), + Jae(String), + Ja(String), + Cdq, + Leave, + Ret, +} diff --git a/src/backend/codegen.rs b/src/backend/codegen.rs index b2a851c..49fd37a 100644 --- a/src/backend/codegen.rs +++ b/src/backend/codegen.rs @@ -1,9 +1,17 @@ -use std::collections::HashMap; +use std::{ + cmp::Ordering, + collections::{HashMap, VecDeque}, + thread::current, +}; + +use rand::rand_core::block; +use tracing::{debug, error, event, info, trace, Level}; use crate::ir::{ graph::{IRGraph, END_BLOCK}, node::{ - BinaryOperationData, ConstantIntData, Node, BINARY_OPERATION_LEFT, BINARY_OPERATION_RIGHT, + self, BinaryOperationData, ConditionalJumpData, ConstantBoolData, ConstantIntData, Node, + ProjectionInformation, BINARY_OPERATION_LEFT, BINARY_OPERATION_RIGHT, COMPARISON_INDEX, RETURN_RESULT_INDEX, }, }; @@ -27,17 +35,21 @@ _main: pub struct CodeGenerator { ir_graphs: Vec, + jump_label: HashMap, } impl CodeGenerator { pub fn new(ir_graphs: Vec) -> CodeGenerator { - CodeGenerator { ir_graphs } + CodeGenerator { + jump_label: calculate_jump_label(&ir_graphs), + ir_graphs, + } } pub fn generate(self) -> String { let mut code = String::new(); code.push_str(TEMPLATE); - for ir_graph in self.ir_graphs.iter() { + for ir_graph in &self.ir_graphs { let register_allocator = RegisterAllocator::new(); code.push_str(&self.generate_for_graph(ir_graph, register_allocator)); } @@ -55,7 +67,62 @@ impl CodeGenerator { code.push_str("pushq %rbp\n"); code.push_str("mov %rsp, %rbp\n"); code.push_str(&format!("subq ${}, %rsp\n", stack_offset)); - code.push_str(&self.generate_for_node(END_BLOCK, ir_graph, ®isters, &mut visited)); + + let mut open_blocks = VecDeque::new(); + // FIXME: Can enter exit block from multiple points, therefore different points to starting + // analysis + let mut recorded_ends: HashMap> = HashMap::new(); + open_blocks.push_back(END_BLOCK); + recorded_ends.insert(END_BLOCK, vec![(END_BLOCK, END_BLOCK)]); + let mut block_codes = String::new(); + while !open_blocks.is_empty() { + trace!("Current recorded ends: {:?}", recorded_ends); + let current_block = open_blocks.pop_front().unwrap(); + trace!("Current block: {}", current_block); + let mut current_block_ends = recorded_ends.remove(¤t_block).unwrap(); + current_block_ends.sort_by(|(x, _), (y, _)| x.cmp(y)); + trace!("After recorded ends: {:?}", recorded_ends); + let mut full_block_code = String::new(); + for current_block_end in current_block_ends { + trace!( + "Generating Assembly for Block {}, with origins at {}", + current_block, + current_block_end.1 + ); + trace!("Current code:{}\n", block_codes); + let (block_code, previous_blocks) = &self.generate_for_node( + current_block_end.0, + ir_graph, + ®isters, + &mut visited, + current_block_end.1, + ); + full_block_code.push_str(&block_code); + info!( + "Generated assembly for block {:?}: {}", + current_block, block_code + ); + trace!( + "New open blocks: {:?} with previous node {:?}", + previous_blocks, + current_block + ); + for previous_block in previous_blocks { + let block_index = ir_graph.get_node(*previous_block).block(); + if !open_blocks.contains(&block_index) { + open_blocks.push_back(block_index); + } + if let Some(mut old_end) = recorded_ends.remove(&block_index) { + old_end.push((*previous_block, current_block)); + recorded_ends.insert(block_index, old_end); + } else { + recorded_ends.insert(block_index, vec![(*previous_block, current_block)]); + } + } + } + block_codes = full_block_code + &block_codes; + } + code.push_str(&block_codes); code } @@ -65,13 +132,49 @@ impl CodeGenerator { ir_graph: &IRGraph, registers: &Registers, visited: &mut Vec, - ) -> String { + following_block: usize, + ) -> (String, Vec) { + debug!( + "Generating assembly for node {}", + ir_graph.get_node(node_index) + ); let mut code = String::new(); + let mut previous_blocks = Vec::new(); let node = ir_graph.get_node(node_index); + if let Node::Block(data) = node { + if let Some(label) = self.jump_label.get(&node.block()) { + code.push_str(&format!("{}:\n", label)); + } + debug!("Finished generating code for block: {:?}", data); + previous_blocks.append(&mut data.predecessors().clone()); + return (code, previous_blocks); + } for predecessor in node.predecessors() { + let predecessor_block = ir_graph.get_node(*predecessor).block(); + if predecessor_block.ne(&node.block()) { + // DO NOT ANALYZE BLOCKS NOT IN CURRENT BLOCK + trace!( + "Not in current analyzed block({:?}): {:?}", + node.block(), + predecessor_block + ); + continue; + } if !visited.contains(predecessor) { visited.push(*predecessor); - code.push_str(&self.generate_for_node(*predecessor, ir_graph, registers, visited)); + let (predecessor_code, predecessor_blocks) = self.generate_for_node( + *predecessor, + ir_graph, + registers, + visited, + following_block, + ); + code.push_str(&predecessor_code); + for previous_block in predecessor_blocks { + if !previous_blocks.contains(&previous_block) { + previous_blocks.push(previous_block); + } + } } } match node { @@ -129,12 +232,131 @@ impl CodeGenerator { Node::ConstantInt(data) => { code.push_str(&self.generate_constant_int(data, ir_graph, registers, node_index)); } - Node::Phi(_) => panic!("PHI present in IR Graph!"), - Node::Block(_) | Node::Projection(_) | Node::Start(_) => return code, + Node::ShiftLeft(data) => { + code.push_str(&self.generate_shift( + node_index, + data.binary_operation_data(), + ir_graph, + registers, + "sall", + )); + } + Node::ShiftRight(data) => { + code.push_str(&self.generate_shift( + node_index, + data.binary_operation_data(), + ir_graph, + registers, + "sarl", + )); + } + Node::Equals(data) => { + code.push_str(&self.generate_comparison( + node_index, + data.binary_operation_data(), + ir_graph, + registers, + )); + } + Node::ConstantBool(data) => code.push_str(&self.generate_constant_bool(data.value())), + Node::Phi(data) => { + println!( + "Warning! Phi present: Aliasing {:?}", + data.node_data().predecessors() + ); + } + Node::Jump(_) => { + trace!( + "Generating assembly for jump: {} with destination {}", + node, + following_block + ); + let label = self.jump_label.get(&following_block).unwrap(); + code.push_str(&format!("jmp {}\n", label)); + } + Node::ConditionalJump(_) => {} + Node::Projection(data) if data.projection_info().eq(&ProjectionInformation::IfTrue) => { + let conditional_jump_code = + self.generate_conditional_jump(node_index, following_block, ir_graph); + code.push_str(&conditional_jump_code.expect("Expected jump code")); + } + Node::Projection(data) + if data.projection_info().eq(&ProjectionInformation::IfFalse) => + { + let jump_label = self + .jump_label + .get(&following_block) + .expect("Expected jump label for false if"); + code.push_str(&format!("jmp {}\n", jump_label)); + } + Node::Block(data) => return (code, previous_blocks), + Node::Projection(_) | Node::Start(_) => return (code, previous_blocks), + node => panic!("unimplemented node {:?}", node), + } + (code, previous_blocks) + } + + pub fn generate_constant_bool(&self, value: bool) -> String { + let mut code = String::new(); + if value { + code.push_str("cmp %rbx,%rbx\n"); + } else { + code.push_str("test %rsp,%rsp\n"); } code } + pub fn generate_conditional_jump( + &self, + projection_index: usize, + suceeding_block: usize, + ir_graph: &IRGraph, + ) -> Option { + let mut code = String::new(); + let true_label = self.jump_label.get(&suceeding_block).unwrap(); + let projection = ir_graph.get_node(projection_index); + let comparision = ir_graph + .get_node(*projection.predecessors().get(0).unwrap()) + .predecessors() + .get(COMPARISON_INDEX) + .unwrap(); + let op_code = match ir_graph.get_node(*comparision) { + Node::Lower(_) => "jb", + Node::LowerEquals(_) => "jbe", + Node::Equals(_) => "je", + Node::NotEquals(_) => "jne", + Node::HigherEquals(_) => "jae", + Node::Higher(_) => "ja", + Node::ConstantBool(_) => "je", + node => panic!("Invalid operation before conditional jump: {}", node), + }; + code.push_str(&format!("{} {}\n", op_code, true_label)); + Some(code) + } + + pub fn generate_comparison( + &self, + node_index: usize, + operation_data: &BinaryOperationData, + ir_graph: &IRGraph, + registers: &Registers, + ) -> String { + let left_value = registers + .get(ir_graph.get_node(operation_data.left())) + .unwrap(); + let right_value = registers + .get(ir_graph.get_node(operation_data.right())) + .unwrap(); + let mut code = String::new(); + //TODO: Both registers can be in memory + code.push_str(&format!( + "cmp {}, {}\n", + left_value.as_assembly(), + right_value.as_assembly() + )); + code + } + pub fn generate_binary_operation( &self, node_index: usize, @@ -245,6 +467,42 @@ impl CodeGenerator { code } + pub fn generate_shift( + &self, + node: usize, + data: &BinaryOperationData, + ir_graph: &IRGraph, + registers: &Registers, + op_code: &str, + ) -> String { + let mut code = String::new(); + let left_value = registers.get(ir_graph.get_node(data.left())).unwrap(); + let right_value = registers.get(ir_graph.get_node(data.right())).unwrap(); + if !left_value.hardware_register() && !right_value.hardware_register() { + code.push_str(&move_stack_variable(right_value)); + code.push_str(&format!( + "{} {}, {}", + op_code, + left_value.as_assembly(), + HardwareRegister::Rbx.as_assembly_16_bit() + )); + } else { + code.push_str(&format!( + "{} {}, {}", + op_code, + left_value.as_assembly(), + right_value.as_16_bit_assembly() + )); + } + let destination = registers.get(ir_graph.get_node(node)).unwrap(); + code.push_str(&format!( + "movq {}, {}", + left_value.as_assembly(), + destination.as_assembly() + )); + code + } + pub fn generate_return( &self, ir_graph: &IRGraph, @@ -310,3 +568,46 @@ fn move_stack_variable(register: &Box) -> String { code.push('\n'); code } + +fn calculate_jump_label(ir_graphs: &Vec) -> HashMap { + let mut jump_label = HashMap::new(); + let mut visited = Vec::new(); + let mut current_label = 0_usize; + for ir_graph in ir_graphs { + calculate_jump_label_node( + &mut current_label, + ir_graph, + END_BLOCK, + &mut jump_label, + &mut visited, + ); + } + jump_label +} + +fn calculate_jump_label_node<'a>( + current_label: &mut usize, + ir_graph: &IRGraph, + node_index: usize, + current: &mut HashMap, + visited: &mut Vec, +) { + let node = ir_graph.get_node(node_index); + for predecessor in node.predecessors() { + if !visited.contains(predecessor) { + visited.push(*predecessor); + calculate_jump_label_node(current_label, ir_graph, *predecessor, current, visited); + } + } + if node_index == END_BLOCK { + return; + } + match node { + Node::Block(_) => { + let block = ir_graph.get_node(node_index).block(); + current.insert(block, format!("LC{}", current_label.to_string())); + *current_label += 1; + } + _ => {} + }; +} diff --git a/src/backend/regalloc.rs b/src/backend/regalloc.rs index a45d849..bfd018e 100644 --- a/src/backend/regalloc.rs +++ b/src/backend/regalloc.rs @@ -7,7 +7,6 @@ use crate::ir::{ graph::{IRGraph, END_BLOCK}, node::Node, }; - pub trait Register { fn as_assembly(&self) -> String; fn as_32_bit_assembly(&self) -> String; diff --git a/src/ir/constructor.rs b/src/ir/constructor.rs index 0864f13..78b873b 100644 --- a/src/ir/constructor.rs +++ b/src/ir/constructor.rs @@ -1,7 +1,10 @@ use core::panic; use std::collections::HashMap; +use tracing::{debug, event, Level}; + use crate::{ + ir::{graph, node::JumpData}, lexer::token::{OperatorType, Token}, parser::{ast::Tree, symbols::Name}, util::int_parsing::parse_int, @@ -10,8 +13,11 @@ use crate::{ use super::{ graph::{IRGraph, START_BLOCK}, node::{ - AddData, ConstantIntData, DivisionData, ModuloData, MultiplicationData, Node, PhiData, - ProjectionData, ProjectionInformation, ReturnData, StartData, SubtractionData, + AddData, AndData, BitwiseNegateData, BlockData, ConditionalJumpData, ConstantBoolData, + ConstantIntData, DivisionData, EqualsData, HigherData, HigherEqualsData, LowerData, + LowerEqualsData, ModuloData, MultiplicationData, Node, NodeData, NotEqualsData, OrData, + PhiData, ProjectionData, ProjectionInformation, ReturnData, ShiftLeftData, ShiftRightData, + StartData, SubtractionData, XorData, }, optimizer::Optimizer, }; @@ -24,7 +30,10 @@ pub struct IRGraphConstructor { current_side_effect: HashMap, incomplete_side_effect_phis: HashMap, sealed_blocks: Vec, + loop_starts: Vec, + loop_ends: Vec, current_block: usize, + next_block_number: usize, } impl IRGraphConstructor { @@ -38,11 +47,15 @@ impl IRGraphConstructor { incomplete_side_effect_phis: HashMap::new(), // Start Block never gets more predecessors sealed_blocks: vec![0], + loop_starts: Vec::new(), + loop_ends: Vec::new(), current_block: 0, + next_block_number: 2, } } pub fn convert(&mut self, tree: Tree) -> Option { + debug!("Converting AST {} to IR!", tree); match tree { Tree::Program(functions) => { for function in functions { @@ -117,7 +130,39 @@ impl IRGraphConstructor { let mod_node = self.create_mod(lhs_node, rhs_node); self.create_div_mod_projection(mod_node) } - _ => panic!("Not a binary operator"), + OperatorType::ShiftLeft => self.create_shift_left(lhs_node, rhs_node), + OperatorType::ShiftRight => self.create_shift_right(lhs_node, rhs_node), + OperatorType::Lower => self.create_lower(lhs_node, rhs_node), + OperatorType::LowerEquals => self.create_lower_equals(lhs_node, rhs_node), + OperatorType::Equals => self.create_equals(lhs_node, rhs_node), + OperatorType::NotEquals => self.create_not_equals(lhs_node, rhs_node), + OperatorType::HigherEquals => self.create_higher_equals(lhs_node, rhs_node), + OperatorType::Higher => self.create_higher(lhs_node, rhs_node), + OperatorType::BitwiseOr => self.create_or(lhs_node, rhs_node), + OperatorType::BitwiseAnd => self.create_and(lhs_node, rhs_node), + OperatorType::BitwiseXor => self.create_xor(lhs_node, rhs_node), + OperatorType::LogicalAnd => self.create_and(lhs_node, rhs_node), + OperatorType::LogicalOr => self.create_or(lhs_node, rhs_node), + OperatorType::Assign + | OperatorType::AssignMul + | OperatorType::AssignDiv + | OperatorType::AssignMod + | OperatorType::AssignPlus + | OperatorType::AssignMinus + | OperatorType::AssignShiftLeft + | OperatorType::AssignShiftRight + | OperatorType::AssignBitwiseOr + | OperatorType::AssignBitwiseNot + | OperatorType::AssignBitwiseAnd + | OperatorType::AssignBitwiseXor => { + panic!("Expected binary operator, got assignment operator!") + } + OperatorType::LogicalNot | OperatorType::BitwiseNot => { + panic!("Expected binary operator, got unary operator!") + } + OperatorType::TernaryQuestionMark | OperatorType::TernaryColon => { + panic!("Expected binary operator, got ternary operator!") + } }; Some(result) } @@ -157,12 +202,25 @@ impl IRGraphConstructor { } Tree::LValueIdentifier(_) => None, Tree::Name(_, _) => None, - Tree::Negate(expression, _) => { - let node = self.convert_boxed(expression).unwrap(); - let zero = self.create_constant_int(0); - let result = self.create_sub(zero, node); - Some(result) - } + Tree::UnaryOperation(expression, operator_type, _) => match operator_type { + OperatorType::Minus => { + let node = self.convert_boxed(expression)?; + let zero = self.create_constant_int(0); + let result = self.create_sub(zero, node); + Some(result) + } + OperatorType::BitwiseNot => { + let node = self.convert_boxed(expression)?; + let result = self.create_bitwise_not(node); + Some(result) + } + OperatorType::LogicalNot => { + let node = self.convert_boxed(expression)?; + let result = self.create_bitwise_not(node); + Some(result) + } + _ => panic!("Unregistered Unary Operation {:?}", operator_type), + }, Tree::Return(expression, _) => { let node = self.convert_boxed(expression).unwrap(); let return_node = self.create_return(node); @@ -173,6 +231,165 @@ impl IRGraphConstructor { None } Tree::Type(_, _) => None, + Tree::BoolLiteral(boolean, _) => { + let node = if boolean { + self.create_constant_bool(true) + } else { + self.create_constant_bool(false) + }; + Some(node) + } + Tree::Break(_) => { + // Jump to after loop + todo!("Create unconditional jump with correct target"); + } + Tree::Continue(_) => { + // Jump to post condition of loop + todo!("Create unconditional jump with correct target"); + } + Tree::TernaryOperation(expression, true_value, false_value) => { + debug!("Generating IR for TernaryOperation"); + let condition_node = self.convert_boxed(expression)?; + let conditional_jump = self.create_conditional_jump(condition_node); + + let true_projection = self.create_true_projection(conditional_jump); + let false_projection = self.create_false_projection(conditional_jump); + + let false_block = self.create_block("ternary-false".to_string()); + self.graph + .get_node_mut(false_block) + .predecessors_mut() + .push(false_projection); + + let true_block = self.create_block("ternary-true".to_string()); + self.graph + .get_node_mut(true_block) + .predecessors_mut() + .push(true_projection); + self.seal_block(self.current_block); + + self.current_block = self.graph.get_node(false_block).block(); + let false_expression = self.convert_boxed(false_value)?; + self.graph + .get_node_mut(false_expression) + .predecessors_mut() + .push(false_block); + let false_jump = self.create_jump(); + self.graph + .get_node_mut(false_jump) + .predecessors_mut() + .push(false_expression); + + self.current_block = self.graph.get_node(true_block).block(); + let true_expression = self.convert_boxed(true_value)?; + self.graph + .get_node_mut(true_expression) + .predecessors_mut() + .push(true_block); + let true_jump = self.create_jump(); + self.graph + .get_node_mut(true_jump) + .predecessors_mut() + .push(true_expression); + + let following_block = self.create_block("ternary-following".to_string()); + self.graph + .get_node_mut(following_block) + .predecessors_mut() + .push(true_jump); + self.graph + .get_node_mut(following_block) + .predecessors_mut() + .push(false_jump); + self.current_block = self.graph.get_node(following_block).block(); + self.seal_block(true_block); + self.seal_block(false_block); + let phi = self.create_phi(); + self.graph + .get_node_mut(phi) + .predecessors_mut() + .push(true_expression); + self.graph + .get_node_mut(phi) + .predecessors_mut() + .push(false_expression); + self.graph + .get_node_mut(phi) + .predecessors_mut() + .push(following_block); + Some(phi) + } + Tree::While(condition, expression, _) => { + self.seal_block(self.current_block); + + let while_block = self.create_block("while".to_string()); + self.graph + .get_node_mut(while_block) + .predecessors_mut() + .push(self.current_block); + self.current_block = while_block; + self.loop_starts.push(while_block); + + let condition_block = self.convert_boxed(condition)?; + + let conditional_jump = self.create_conditional_jump(condition_block); + let true_block = self.create_true_projection(conditional_jump); + let false_block = self.create_false_projection(conditional_jump); + + let follow_block = self.create_block("while-follow".to_string()); + self.graph + .get_node_mut(follow_block) + .predecessors_mut() + .push(false_block); + self.loop_ends.push(follow_block); + + let body_block = self.create_block("while-body".to_string()); + self.graph + .get_node_mut(body_block) + .predecessors_mut() + .push(true_block); + self.seal_block(body_block); + self.current_block = body_block; + self.convert_boxed(expression); + let continue_jump = self.create_jump(); + self.seal_block(self.current_block); + self.graph + .get_node_mut(while_block) + .predecessors_mut() + .push(continue_jump); + + self.loop_starts.pop(); + self.seal_block(while_block); + self.loop_ends.pop(); + self.seal_block(follow_block); + self.current_block = follow_block; + None + } + Tree::If(condition, body, else_body, _) => { + let condition_block = self.convert_boxed(condition)?; + let conditional_jump = self.create_conditional_jump(condition_block); + let true_projection = self.create_true_projection(conditional_jump); + let false_projection = self.create_false_projection(conditional_jump); + self.seal_block(self.current_block); + + let true_block = self.process_branch(body, true_projection, "true"); + let false_block = if let Some(else_body_ab) = else_body { + Some(self.process_branch(else_body_ab, false_projection, "false")) + } else { + None + }; + + let following_block = self.create_block("following-if".to_string()); + self.current_block = following_block; + let following_node = self.graph.get_node_mut(following_block); + following_node.predecessors_mut().push(true_block); + if let Some(false_block_index) = false_block { + following_node.predecessors_mut().push(false_block_index); + } + self.seal_block(following_block); + None + } + node => todo!("Unimplemented {:?}", node), } } @@ -180,11 +397,46 @@ impl IRGraphConstructor { self.convert(*tree) } + pub fn create_conditional_jump(&mut self, condition: usize) -> usize { + self.graph + .register_node(Node::ConditionalJump(ConditionalJumpData::new( + self.current_block, + condition, + ))) + } + + pub fn process_branch(&mut self, a: Box, b: usize, label: &str) -> usize { + let block = self.create_block(format!("if-body-{}", label)); + self.current_block = block; + self.graph.get_node_mut(block).predecessors_mut().push(b); + self.seal_block(block); + self.convert_boxed(a); + self.seal_block(self.current_block); + self.create_jump() + } + pub fn create_block(&mut self, name: String) -> usize { + let result = self + .graph + .register_node(Node::Block(BlockData::new(self.next_block_number))); + self.next_block_number += 1; + result + } + + pub fn create_jump(&mut self) -> usize { + self.graph + .register_node(Node::Jump(JumpData::new(self.current_block))) + } + fn create_start_block(&mut self) -> usize { self.graph .register_node(Node::Start(StartData::new(self.current_block))) } + fn create_noop(&mut self) -> usize { + self.graph + .register_node(Node::NoOp(NodeData::new(self.current_block, vec![]))) + } + fn create_add(&mut self, left: usize, right: usize) -> usize { self.graph .register_node(self.optimizer.transform(Node::Add(AddData::new( @@ -205,6 +457,17 @@ impl IRGraphConstructor { ) } + fn create_bitwise_not(&mut self, node: usize) -> usize { + self.graph + .register_node( + self.optimizer + .transform(Node::BitwiseNegate(BitwiseNegateData::new( + self.current_block, + node, + ))), + ) + } + fn create_mul(&mut self, left: usize, right: usize) -> usize { self.graph .register_node( @@ -239,6 +502,103 @@ impl IRGraphConstructor { )))) } + fn create_shift_right(&mut self, left: usize, right: usize) -> usize { + self.graph.register_node( + self.optimizer + .transform(Node::ShiftRight(ShiftRightData::new( + self.current_block, + left, + right, + ))), + ) + } + + fn create_shift_left(&mut self, left: usize, right: usize) -> usize { + self.graph + .register_node(self.optimizer.transform(Node::ShiftLeft(ShiftLeftData::new( + self.current_block, + left, + right, + )))) + } + fn create_or(&mut self, left: usize, right: usize) -> usize { + self.graph + .register_node(self.optimizer.transform(Node::Or(OrData::new( + self.current_block, + left, + right, + )))) + } + fn create_and(&mut self, left: usize, right: usize) -> usize { + self.graph + .register_node(self.optimizer.transform(Node::And(AndData::new( + self.current_block, + left, + right, + )))) + } + fn create_xor(&mut self, left: usize, right: usize) -> usize { + self.graph + .register_node(self.optimizer.transform(Node::Xor(XorData::new( + self.current_block, + left, + right, + )))) + } + fn create_lower(&mut self, left: usize, right: usize) -> usize { + self.graph + .register_node(self.optimizer.transform(Node::Lower(LowerData::new( + self.current_block, + left, + right, + )))) + } + fn create_lower_equals(&mut self, left: usize, right: usize) -> usize { + self.graph.register_node( + self.optimizer + .transform(Node::LowerEquals(LowerEqualsData::new( + self.current_block, + left, + right, + ))), + ) + } + fn create_equals(&mut self, left: usize, right: usize) -> usize { + self.graph + .register_node(self.optimizer.transform(Node::Equals(EqualsData::new( + self.current_block, + left, + right, + )))) + } + fn create_not_equals(&mut self, left: usize, right: usize) -> usize { + self.graph + .register_node(self.optimizer.transform(Node::NotEquals(NotEqualsData::new( + self.current_block, + left, + right, + )))) + } + fn create_higher_equals(&mut self, left: usize, right: usize) -> usize { + self.graph + .register_node( + self.optimizer + .transform(Node::HigherEquals(HigherEqualsData::new( + self.current_block, + left, + right, + ))), + ) + } + fn create_higher(&mut self, left: usize, right: usize) -> usize { + self.graph + .register_node(self.optimizer.transform(Node::Higher(HigherData::new( + self.current_block, + left, + right, + )))) + } + fn create_return(&mut self, result: usize) -> usize { let current_side_effect = self.read_current_side_effect(); self.graph.register_node(Node::Return(ReturnData::new( @@ -249,10 +609,12 @@ impl IRGraphConstructor { } fn create_constant_int(&mut self, value: i32) -> usize { - // Always move const into start block, allows for better deduplication self.graph.register_node( self.optimizer - .transform(Node::ConstantInt(ConstantIntData::new(START_BLOCK, value))), + .transform(Node::ConstantInt(ConstantIntData::new( + self.current_block, + value, + ))), ) } @@ -365,7 +727,7 @@ impl IRGraphConstructor { node } - fn _seal_block(&mut self, block: usize) { + fn seal_block(&mut self, block: usize) { if !self.incomplete_phis.contains_key(&block) { self.sealed_blocks.push(block); return; @@ -424,6 +786,31 @@ impl IRGraphConstructor { pub fn graph(self) -> IRGraph { self.graph } + + pub fn create_constant_bool(&mut self, value: bool) -> usize { + self.graph + .register_node(Node::ConstantBool(ConstantBoolData::new( + self.current_block, + value, + ))) + } + pub fn create_true_projection(&mut self, block: usize) -> usize { + self.graph + .register_node(Node::Projection(ProjectionData::new( + self.current_block, + block, + ProjectionInformation::IfTrue, + ))) + } + + pub fn create_false_projection(&mut self, block: usize) -> usize { + self.graph + .register_node(Node::Projection(ProjectionData::new( + self.current_block, + block, + ProjectionInformation::IfFalse, + ))) + } } impl Default for IRGraphConstructor { diff --git a/src/ir/graph.rs b/src/ir/graph.rs index 911e4c2..e1fc899 100644 --- a/src/ir/graph.rs +++ b/src/ir/graph.rs @@ -37,7 +37,7 @@ impl IRGraph { self.nodes.get(&index).expect("Cannot find node at index") } - pub fn get_node_mut(&mut self, index: usize) -> &Node { + pub fn get_node_mut(&mut self, index: usize) -> &mut Node { self.nodes .get_mut(&index) .expect("Cannot find node at index") @@ -74,7 +74,13 @@ impl Display for IRGraph { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { writeln!(f, "START: {}", self.get_node(START_BLOCK))?; for i in END_BLOCK + 1..self.next_node_index { - writeln!(f, "{}: {}", i, self.get_node(i))? + writeln!( + f, + "{}-{}: {}", + self.get_node(i).block(), + i, + self.get_node(i) + )? } writeln!(f, "END: {}", self.get_node(END_BLOCK))?; Ok(()) diff --git a/src/ir/node.rs b/src/ir/node.rs index 33a26ae..f93235e 100644 --- a/src/ir/node.rs +++ b/src/ir/node.rs @@ -1,10 +1,13 @@ use std::fmt::Display; +use crate::lexer::token::OperatorType; + #[derive(Eq, Hash, PartialEq, Debug)] pub enum Node { Add(AddData), Block(BlockData), ConstantInt(ConstantIntData), + ConstantBool(ConstantBoolData), Division(DivisionData), Modulo(ModuloData), Multiplication(MultiplicationData), @@ -13,6 +16,21 @@ pub enum Node { Return(ReturnData), Start(StartData), Subtraction(SubtractionData), + ShiftLeft(ShiftLeftData), + ShiftRight(ShiftRightData), + Lower(LowerData), + LowerEquals(LowerEqualsData), + Equals(EqualsData), + NotEquals(NotEqualsData), + HigherEquals(HigherEqualsData), + Higher(HigherData), + BitwiseNegate(BitwiseNegateData), + Or(OrData), + And(AndData), + Xor(XorData), + NoOp(NodeData), + ConditionalJump(ConditionalJumpData), + Jump(JumpData), } impl Node { @@ -21,6 +39,7 @@ impl Node { Node::Add(data) => data.binary_operation_data.node_data.predecessors(), Node::Block(data) => data.node_data.predecessors(), Node::ConstantInt(data) => data.node_data.predecessors(), + Node::ConstantBool(data) => data.node_data.predecessors(), Node::Division(data) => data.binary_operation_data.node_data.predecessors(), Node::Modulo(data) => data.binary_operation_data.node_data.predecessors(), Node::Multiplication(data) => data.binary_operation_data.node_data.predecessors(), @@ -29,6 +48,21 @@ impl Node { Node::Return(data) => data.node_data.predecessors(), Node::Start(data) => data.node_data.predecessors(), Node::Subtraction(data) => data.binary_operation_data.node_data.predecessors(), + Node::ShiftLeft(data) => data.binary_operation_data.node_data.predecessors(), + Node::ShiftRight(data) => data.binary_operation_data.node_data.predecessors(), + Node::BitwiseNegate(data) => data.node_data.predecessors(), + Node::Lower(data) => data.binary_operation_data.node_data.predecessors(), + Node::LowerEquals(data) => data.binary_operation_data.node_data.predecessors(), + Node::Equals(data) => data.binary_operation_data.node_data.predecessors(), + Node::NotEquals(data) => data.binary_operation_data.node_data.predecessors(), + Node::HigherEquals(data) => data.binary_operation_data.node_data.predecessors(), + Node::Higher(data) => data.binary_operation_data.node_data.predecessors(), + Node::And(data) => data.binary_operation_data.node_data.predecessors(), + Node::Or(data) => data.binary_operation_data.node_data.predecessors(), + Node::Xor(data) => data.binary_operation_data.node_data.predecessors(), + Node::NoOp(data) => data.predecessors(), + Node::ConditionalJump(data) => data.node_data.predecessors(), + Node::Jump(data) => data.node_data.predecessors(), } } @@ -37,6 +71,7 @@ impl Node { Node::Add(data) => data.binary_operation_data.node_data.predecessors_mut(), Node::Block(data) => data.node_data.predecessors_mut(), Node::ConstantInt(data) => data.node_data.predecessors_mut(), + Node::ConstantBool(data) => data.node_data.predecessors_mut(), Node::Division(data) => data.binary_operation_data.node_data.predecessors_mut(), Node::Modulo(data) => data.binary_operation_data.node_data.predecessors_mut(), Node::Multiplication(data) => data.binary_operation_data.node_data.predecessors_mut(), @@ -45,6 +80,21 @@ impl Node { Node::Return(data) => data.node_data.predecessors_mut(), Node::Start(data) => data.node_data.predecessors_mut(), Node::Subtraction(data) => data.binary_operation_data.node_data.predecessors_mut(), + Node::ShiftLeft(data) => data.binary_operation_data.node_data.predecessors_mut(), + Node::ShiftRight(data) => data.binary_operation_data.node_data.predecessors_mut(), + Node::BitwiseNegate(data) => data.node_data.predecessors_mut(), + Node::Lower(data) => data.binary_operation_data.node_data.predecessors_mut(), + Node::LowerEquals(data) => data.binary_operation_data.node_data.predecessors_mut(), + Node::Equals(data) => data.binary_operation_data.node_data.predecessors_mut(), + Node::NotEquals(data) => data.binary_operation_data.node_data.predecessors_mut(), + Node::HigherEquals(data) => data.binary_operation_data.node_data.predecessors_mut(), + Node::Higher(data) => data.binary_operation_data.node_data.predecessors_mut(), + Node::And(data) => data.binary_operation_data.node_data.predecessors_mut(), + Node::Or(data) => data.binary_operation_data.node_data.predecessors_mut(), + Node::Xor(data) => data.binary_operation_data.node_data.predecessors_mut(), + Node::NoOp(data) => data.predecessors_mut(), + Node::ConditionalJump(data) => data.node_data.predecessors_mut(), + Node::Jump(data) => data.node_data.predecessors_mut(), } } @@ -53,6 +103,7 @@ impl Node { Node::Add(data) => data.binary_operation_data.node_data.block(), Node::Block(data) => data.node_data.block(), Node::ConstantInt(data) => data.node_data.block(), + Node::ConstantBool(data) => data.node_data.block(), Node::Division(data) => data.binary_operation_data.node_data.block(), Node::Modulo(data) => data.binary_operation_data.node_data.block(), Node::Multiplication(data) => data.binary_operation_data.node_data.block(), @@ -61,6 +112,21 @@ impl Node { Node::Return(data) => data.node_data.block(), Node::Start(data) => data.node_data.block(), Node::Subtraction(data) => data.binary_operation_data.node_data.block(), + Node::ShiftLeft(data) => data.binary_operation_data.node_data.block(), + Node::ShiftRight(data) => data.binary_operation_data.node_data.block(), + Node::BitwiseNegate(data) => data.node_data.block(), + Node::Lower(data) => data.binary_operation_data.node_data.block(), + Node::LowerEquals(data) => data.binary_operation_data.node_data.block(), + Node::Equals(data) => data.binary_operation_data.node_data.block(), + Node::NotEquals(data) => data.binary_operation_data.node_data.block(), + Node::HigherEquals(data) => data.binary_operation_data.node_data.block(), + Node::Higher(data) => data.binary_operation_data.node_data.block(), + Node::And(data) => data.binary_operation_data.node_data.block(), + Node::Or(data) => data.binary_operation_data.node_data.block(), + Node::Xor(data) => data.binary_operation_data.node_data.block(), + Node::NoOp(data) => data.block(), + Node::ConditionalJump(data) => data.node_data.block(), + Node::Jump(data) => data.node_data.block(), } } } @@ -166,6 +232,10 @@ impl BlockData { node_data: NodeData::new(block_index, vec![]), } } + + pub fn predecessors(&self) -> &Vec { + &self.node_data.predecessors + } } #[derive(Eq, Hash, PartialEq, Debug)] @@ -282,6 +352,8 @@ impl PhiData { pub enum ProjectionInformation { SideEffect, Result, + IfTrue, + IfFalse, } #[derive(Eq, Hash, PartialEq, Debug)] @@ -306,6 +378,10 @@ impl ProjectionData { &self.projection_info } + pub fn node_data(&self) -> &NodeData { + &self.node_data + } + pub fn input(&self) -> usize { self.node_data.predecessors()[0] } @@ -355,6 +431,258 @@ impl SubtractionData { } } +#[derive(Eq, Hash, PartialEq, Debug)] +pub struct ShiftLeftData { + binary_operation_data: BinaryOperationData, +} + +impl ShiftLeftData { + pub fn new(block: usize, left: usize, right: usize) -> ShiftLeftData { + ShiftLeftData { + binary_operation_data: BinaryOperationData::new(block, left, right), + } + } + + pub fn binary_operation_data(&self) -> &BinaryOperationData { + &self.binary_operation_data + } +} + +#[derive(Eq, Hash, PartialEq, Debug)] +pub struct ShiftRightData { + binary_operation_data: BinaryOperationData, +} + +impl ShiftRightData { + pub fn new(block: usize, left: usize, right: usize) -> ShiftRightData { + ShiftRightData { + binary_operation_data: BinaryOperationData::new(block, left, right), + } + } + + pub fn binary_operation_data(&self) -> &BinaryOperationData { + &self.binary_operation_data + } +} + +#[derive(Eq, Hash, PartialEq, Debug)] +pub struct BitwiseNegateData { + node_data: NodeData, +} + +impl BitwiseNegateData { + pub fn new(block: usize, operand: usize) -> BitwiseNegateData { + BitwiseNegateData { + node_data: NodeData::new(block, vec![operand]), + } + } + + pub fn node_data(&self) -> &NodeData { + &self.node_data + } +} + +#[derive(Eq, Hash, PartialEq, Debug)] +pub struct LowerData { + binary_operation_data: BinaryOperationData, +} + +impl LowerData { + pub fn new(block: usize, left: usize, right: usize) -> LowerData { + LowerData { + binary_operation_data: BinaryOperationData::new(block, left, right), + } + } + + pub fn binary_operation_data(&self) -> &BinaryOperationData { + &self.binary_operation_data + } +} + +#[derive(Eq, Hash, PartialEq, Debug)] +pub struct LowerEqualsData { + binary_operation_data: BinaryOperationData, +} + +impl LowerEqualsData { + pub fn new(block: usize, left: usize, right: usize) -> LowerEqualsData { + LowerEqualsData { + binary_operation_data: BinaryOperationData::new(block, left, right), + } + } + + pub fn binary_operation_data(&self) -> &BinaryOperationData { + &self.binary_operation_data + } +} + +#[derive(Eq, Hash, PartialEq, Debug)] +pub struct EqualsData { + binary_operation_data: BinaryOperationData, +} + +impl EqualsData { + pub fn new(block: usize, left: usize, right: usize) -> EqualsData { + EqualsData { + binary_operation_data: BinaryOperationData::new(block, left, right), + } + } + + pub fn binary_operation_data(&self) -> &BinaryOperationData { + &self.binary_operation_data + } +} + +#[derive(Eq, Hash, PartialEq, Debug)] +pub struct NotEqualsData { + binary_operation_data: BinaryOperationData, +} + +impl NotEqualsData { + pub fn new(block: usize, left: usize, right: usize) -> NotEqualsData { + NotEqualsData { + binary_operation_data: BinaryOperationData::new(block, left, right), + } + } + + pub fn binary_operation_data(&self) -> &BinaryOperationData { + &self.binary_operation_data + } +} +#[derive(Eq, Hash, PartialEq, Debug)] +pub struct HigherEqualsData { + binary_operation_data: BinaryOperationData, +} + +impl HigherEqualsData { + pub fn new(block: usize, left: usize, right: usize) -> HigherEqualsData { + HigherEqualsData { + binary_operation_data: BinaryOperationData::new(block, left, right), + } + } + + pub fn binary_operation_data(&self) -> &BinaryOperationData { + &self.binary_operation_data + } +} + +#[derive(Eq, Hash, PartialEq, Debug)] +pub struct HigherData { + binary_operation_data: BinaryOperationData, +} + +impl HigherData { + pub fn new(block: usize, left: usize, right: usize) -> HigherData { + HigherData { + binary_operation_data: BinaryOperationData::new(block, left, right), + } + } + + pub fn binary_operation_data(&self) -> &BinaryOperationData { + &self.binary_operation_data + } +} +#[derive(Eq, Hash, PartialEq, Debug)] +pub struct OrData { + binary_operation_data: BinaryOperationData, +} + +impl OrData { + pub fn new(block: usize, left: usize, right: usize) -> OrData { + OrData { + binary_operation_data: BinaryOperationData::new(block, left, right), + } + } + + pub fn binary_operation_data(&self) -> &BinaryOperationData { + &self.binary_operation_data + } +} +#[derive(Eq, Hash, PartialEq, Debug)] +pub struct AndData { + binary_operation_data: BinaryOperationData, +} + +impl AndData { + pub fn new(block: usize, left: usize, right: usize) -> AndData { + AndData { + binary_operation_data: BinaryOperationData::new(block, left, right), + } + } + + pub fn binary_operation_data(&self) -> &BinaryOperationData { + &self.binary_operation_data + } +} +#[derive(Eq, Hash, PartialEq, Debug)] +pub struct XorData { + binary_operation_data: BinaryOperationData, +} + +impl XorData { + pub fn new(block: usize, left: usize, right: usize) -> XorData { + XorData { + binary_operation_data: BinaryOperationData::new(block, left, right), + } + } + + pub fn binary_operation_data(&self) -> &BinaryOperationData { + &self.binary_operation_data + } +} + +pub const COMPARISON_INDEX: usize = 0; +#[derive(Eq, Hash, PartialEq, Debug)] +pub struct ConditionalJumpData { + node_data: NodeData, +} + +impl ConditionalJumpData { + pub fn new(block: usize, condition: usize) -> ConditionalJumpData { + ConditionalJumpData { + node_data: NodeData::new(block, vec![condition]), + } + } + + pub fn node_data(&self) -> &NodeData { + &self.node_data + } +} + +#[derive(Eq, Hash, PartialEq, Debug)] +pub struct JumpData { + node_data: NodeData, +} + +impl JumpData { + pub fn new(block: usize) -> JumpData { + JumpData { + node_data: NodeData::new(block, vec![]), + } + } + + pub fn node_data(&self) -> &NodeData { + &self.node_data + } +} +#[derive(Eq, Hash, PartialEq, Debug)] +pub struct ConstantBoolData { + node_data: NodeData, + value: bool, +} + +impl ConstantBoolData { + pub fn new(block: usize, value: bool) -> ConstantBoolData { + ConstantBoolData { + node_data: NodeData::new(block, vec![]), + value, + } + } + + pub fn value(&self) -> bool { + self.value + } +} impl Display for Node { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -363,10 +691,15 @@ impl Display for Node { "Add: {:?}", data.binary_operation_data.node_data.predecessors() ), - Node::Phi(_) => write!(f, "!!PHI!!"), + Node::Phi(data) => write!(f, "!!PHI!! {:?}", data.node_data().predecessors()), Node::Block(data) => write!(f, "Block: {:?}", data.node_data.predecessors()), Node::Start(data) => write!(f, "Start: {:?}", data.node_data.predecessors()), - Node::ConstantInt(data) => write!(f, "Constant: {}", data.value), + Node::ConstantInt(data) => write!( + f, + "Constant: {} {:?}", + data.value, + data.node_data.predecessors() + ), Node::Division(data) => write!( f, "Div: {:?}", @@ -391,6 +724,96 @@ impl Display for Node { data.binary_operation_data.node_data.predecessors() ) } + Node::BitwiseNegate(data) => { + write!(f, "BitwiseNegate: {:?}", data.node_data.predecessors()) + } + Node::ShiftRight(data) => { + write!( + f, + "ShiftRight: {:?}", + data.binary_operation_data.node_data.predecessors() + ) + } + Node::ShiftLeft(data) => { + write!( + f, + "ShiftLeft: {:?}", + data.binary_operation_data.node_data.predecessors() + ) + } + Node::Lower(data) => { + write!( + f, + "Lower: {:?}", + data.binary_operation_data.node_data.predecessors() + ) + } + Node::LowerEquals(data) => { + write!( + f, + "LowerEquals: {:?}", + data.binary_operation_data.node_data.predecessors() + ) + } + Node::Equals(data) => { + write!( + f, + "Equals: {:?}", + data.binary_operation_data.node_data.predecessors() + ) + } + Node::NotEquals(data) => { + write!( + f, + "NotEquals: {:?}", + data.binary_operation_data.node_data.predecessors() + ) + } + Node::HigherEquals(data) => { + write!( + f, + "HigherEquals: {:?}", + data.binary_operation_data.node_data.predecessors() + ) + } + Node::Higher(data) => { + write!( + f, + "Higher: {:?}", + data.binary_operation_data.node_data.predecessors() + ) + } + Node::Or(data) => { + write!( + f, + "Or: {:?}", + data.binary_operation_data.node_data.predecessors() + ) + } + Node::And(data) => { + write!( + f, + "And: {:?}", + data.binary_operation_data.node_data.predecessors() + ) + } + Node::Xor(data) => { + write!( + f, + "Xor: {:?}", + data.binary_operation_data.node_data.predecessors() + ) + } + Node::NoOp(_) => Ok(()), + Node::ConditionalJump(data) => { + write!(f, "ConditionalJump: {:?}", data.node_data.predecessors()) + } + Node::Jump(data) => { + write!(f, "Jump: {:?}", data.node_data.predecessors()) + } + Node::ConstantBool(data) => { + write!(f, "{}", data.value) + } } } } diff --git a/src/lexer/mod.rs b/src/lexer/mod.rs index 7b7cd60..0c12acf 100644 --- a/src/lexer/mod.rs +++ b/src/lexer/mod.rs @@ -46,22 +46,69 @@ impl Lexer { '{' => self.seperator(SeperatorType::BraceOpen), '}' => self.seperator(SeperatorType::BraceClose), ';' => self.seperator(SeperatorType::Semicolon), + '?' => Token::Operator(self.build_span(1), OperatorType::TernaryQuestionMark), + ':' => Token::Operator(self.build_span(1), OperatorType::TernaryColon), '-' => self - .single_assign(OperatorType::Minus, OperatorType::AssignMinus) + .single_assign(OperatorType::Minus, OperatorType::AssignMinus, 1) .ok_or(ParseError::Error("Not a character".to_string()))?, '+' => self - .single_assign(OperatorType::Plus, OperatorType::AssignPlus) + .single_assign(OperatorType::Plus, OperatorType::AssignPlus, 1) .ok_or(ParseError::Error("Not a character".to_string()))?, '*' => self - .single_assign(OperatorType::Mul, OperatorType::AssignMul) + .single_assign(OperatorType::Mul, OperatorType::AssignMul, 1) .ok_or(ParseError::Error("Not a character".to_string()))?, '/' => self - .single_assign(OperatorType::Div, OperatorType::AssignDiv) + .single_assign(OperatorType::Div, OperatorType::AssignDiv, 1) .ok_or(ParseError::Error("Not a character".to_string()))?, '%' => self - .single_assign(OperatorType::Mod, OperatorType::AssignMod) + .single_assign(OperatorType::Mod, OperatorType::AssignMod, 1) + .ok_or(ParseError::Error("Not a character".to_string()))?, + '&' => self + .single_assign_logical( + '&', + OperatorType::BitwiseAnd, + OperatorType::AssignBitwiseAnd, + OperatorType::LogicalAnd, + ) + .ok_or(ParseError::Error("Not a character".to_string()))?, + '~' => self + .single_assign(OperatorType::BitwiseNot, OperatorType::AssignBitwiseNot, 1) + .ok_or(ParseError::Error("Not a character".to_string()))?, + '^' => self + .single_assign(OperatorType::BitwiseXor, OperatorType::AssignBitwiseXor, 1) + .ok_or(ParseError::Error("Not a character".to_string()))?, + '|' => self + .single_assign_logical( + '|', + OperatorType::BitwiseOr, + OperatorType::AssignBitwiseOr, + OperatorType::LogicalOr, + ) + .ok_or(ParseError::Error("Not a character".to_string()))?, + '<' => self + .shift_or_comparison( + '<', + OperatorType::ShiftLeft, + OperatorType::AssignShiftLeft, + OperatorType::Lower, + OperatorType::LowerEquals, + ) + .ok_or(ParseError::Error("Not a character".to_string()))?, + '>' => self + .shift_or_comparison( + '>', + OperatorType::ShiftRight, + OperatorType::AssignShiftRight, + OperatorType::Higher, + OperatorType::HigherEquals, + ) + .ok_or(ParseError::Error("Not a character".to_string()))?, + '!' => self + .not_or_comparison() + .ok_or(ParseError::Error("Not a character".to_string()))?, + '=' => self + .assign_or_comparison() .ok_or(ParseError::Error("Not a character".to_string()))?, - '=' => Token::Operator(self.build_span(1), OperatorType::Assign), char => { if self.is_identifier_char(char) { if self.is_numeric(char) { @@ -291,13 +338,75 @@ impl Lexer { &mut self, single_type: OperatorType, assign_type: OperatorType, + equals_offset: usize, + ) -> Option { + if self.has_more(equals_offset) && self.peek_pos(equals_offset)?.eq(&'=') { + return Some(Token::Operator( + self.build_span(equals_offset + 1), + assign_type, + )); + } + Some(Token::Operator(self.build_span(1), single_type)) + } + + fn single_assign_logical( + &mut self, + operator: char, + single_type: OperatorType, + assign_type: OperatorType, + logical_type: OperatorType, ) -> Option { if self.has_more(1) && self.peek_pos(1)?.eq(&'=') { return Some(Token::Operator(self.build_span(2), assign_type)); } + if self.has_more(1) && self.peek_pos(1)?.eq(&operator) { + return Some(Token::Operator(self.build_span(2), logical_type)); + } Some(Token::Operator(self.build_span(1), single_type)) } + fn assign_or_comparison(&mut self) -> Option { + if self.has_more(1) && self.peek_pos(1)?.eq(&'=') { + return Some(Token::Operator(self.build_span(2), OperatorType::Equals)); + } + Some(Token::Operator(self.build_span(1), OperatorType::Assign)) + } + + fn not_or_comparison(&mut self) -> Option { + if self.has_more(1) && self.peek_pos(1)?.eq(&'=') { + return Some(Token::Operator(self.build_span(2), OperatorType::NotEquals)); + } + Some(Token::Operator( + self.build_span(1), + OperatorType::LogicalNot, + )) + } + + fn shift_or_comparison( + &mut self, + shift: char, + single_type: OperatorType, + assign_type: OperatorType, + comparison_type: OperatorType, + comparison_equal_type: OperatorType, + ) -> Option { + if !self.has_more(1) { + return Some(Token::Operator(self.build_span(1), comparison_type)); + } + + if self.peek_pos(1)?.ne(&shift) { + if self.peek_pos(1)?.ne(&'=') { + return Some(Token::Operator(self.build_span(2), comparison_equal_type)); + } else { + return None; + } + } + if self.has_more(2) && self.peek_pos(2)?.eq(&'=') { + return Some(Token::Operator(self.build_span(3), assign_type)); + } + Some(Token::Operator(self.build_span(2), single_type)) + } + fn is_identifier_char(&self, char: char) -> bool { char.is_ascii_alphanumeric() || char.eq(&'_') } diff --git a/src/lexer/token.rs b/src/lexer/token.rs index f5b9d52..8466c3e 100644 --- a/src/lexer/token.rs +++ b/src/lexer/token.rs @@ -78,9 +78,20 @@ impl KeywordType { _ => None, } } + + pub fn is_control_keyword(&self) -> bool { + matches!( + self, + Self::If | Self::While | Self::For | Self::Continue | Self::Break | Self::Return + ) + } + + pub fn is_type(&self) -> bool { + matches!(self, Self::Bool | Self::Int) + } } -#[derive(PartialEq, Clone, Debug)] +#[derive(Eq, Hash, Clone, Debug, PartialEq)] pub enum OperatorType { AssignMinus, Minus, @@ -92,9 +103,34 @@ pub enum OperatorType { Div, AssignMod, Mod, + LogicalNot, + BitwiseNot, + AssignBitwiseNot, + ShiftLeft, + AssignShiftLeft, + ShiftRight, + AssignShiftRight, + Lower, + LowerEquals, + Higher, + HigherEquals, + Equals, + NotEquals, + BitwiseAnd, + AssignBitwiseAnd, + BitwiseXor, + AssignBitwiseXor, + BitwiseOr, + AssignBitwiseOr, + LogicalAnd, + LogicalOr, + TernaryQuestionMark, + TernaryColon, Assign, } +pub const MAX_PRECEDENCE: u8 = 13; + impl OperatorType { pub fn is_assignment_operator(&self) -> bool { matches!( @@ -105,9 +141,35 @@ impl OperatorType { | Self::AssignMod | Self::AssignPlus | Self::AssignMinus + | Self::AssignBitwiseNot + | Self::AssignShiftLeft + | Self::AssignShiftRight + | Self::AssignBitwiseAnd + | Self::AssignBitwiseXor + | Self::AssignBitwiseOr ) } + pub fn get_precedence(&self) -> Vec { + match self { + Self::LogicalNot | Self::BitwiseNot => vec![1], + Self::Minus => vec![1, 3], + Self::Mul | Self::Div | Self::Mod => vec![2], + Self::Plus => vec![3], + Self::ShiftLeft | Self::ShiftRight => vec![4], + Self::Lower | Self::LowerEquals | Self::Higher | Self::HigherEquals => vec![5], + Self::Equals | Self::NotEquals => vec![6], + Self::BitwiseAnd => vec![7], + Self::BitwiseXor => vec![8], + Self::BitwiseOr => vec![9], + Self::LogicalAnd => vec![10], + Self::LogicalOr => vec![11], + Self::TernaryQuestionMark | Self::TernaryColon => vec![12], + operator if operator.is_assignment_operator() => vec![13], + _ => panic!("Attempted to get predecence of operator {:?}", self), + } + } + pub fn as_str(&self) -> &str { match self { Self::AssignMinus => "-=", @@ -120,6 +182,29 @@ impl OperatorType { Self::Div => "/", Self::AssignMod => "%=", Self::Mod => "%", + Self::LogicalNot => "!", + Self::BitwiseNot => "~", + Self::AssignBitwiseNot => "~=", + Self::ShiftLeft => "<<", + Self::AssignShiftLeft => "<<=", + Self::ShiftRight => ">>", + Self::AssignShiftRight => ">>=", + Self::Lower => "<", + Self::LowerEquals => "<=", + Self::Higher => ">", + Self::HigherEquals => ">=", + Self::Equals => "==", + Self::NotEquals => "!=", + Self::BitwiseAnd => "&", + Self::AssignBitwiseAnd => "&=", + Self::BitwiseXor => "^", + Self::AssignBitwiseXor => "^=", + Self::BitwiseOr => "|", + Self::AssignBitwiseOr => "|=", + Self::LogicalAnd => "&&", + Self::LogicalOr => "||", + Self::TernaryQuestionMark => "?", + Self::TernaryColon => ":", Self::Assign => "=", } } @@ -152,6 +237,7 @@ pub enum Token { Identifier(Span, String), Keyword(Span, KeywordType), NumberLiteral(Span, String, u64), + BoolLiteral(Span, String), Operator(Span, OperatorType), Separator(Span, SeperatorType), } @@ -163,6 +249,7 @@ impl Token { Self::Identifier(span, _) => span, Self::Keyword(span, _) => span, Self::NumberLiteral(span, _, _) => span, + Self::BoolLiteral(span, _) => span, Self::Operator(span, _) => span, Self::Separator(span, _) => span, } @@ -174,7 +261,28 @@ impl Token { } } - pub fn is_operator(&self, other_operator: &OperatorType) -> bool { + pub fn is_control_keyword(&self) -> bool { + match self { + Self::Keyword(_, keyword_type) => keyword_type.is_control_keyword(), + _ => false, + } + } + + pub fn is_type_keyword(&self) -> bool { + match self { + Self::Keyword(_, keyword_type) => keyword_type.is_type(), + _ => false, + } + } + + pub fn is_operator(&self) -> bool { + match self { + Self::Operator(_, _) => true, + _ => false, + } + } + + pub fn is_operator_type(&self, other_operator: &OperatorType) -> bool { match self { Self::Operator(_, operator) => operator.eq(other_operator), _ => false, @@ -198,6 +306,7 @@ impl Token { Self::Identifier(_, value) => value.as_str(), Self::Keyword(_, keyword) => keyword.as_str(), Self::NumberLiteral(_, value, _) => value.as_str(), + Self::BoolLiteral(_, value) => value.as_str(), Self::Operator(_, operator) => operator.as_str(), Self::Separator(_, seperator) => seperator.as_str(), } diff --git a/src/main.rs b/src/main.rs index 5389f86..b852b6a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -20,6 +20,7 @@ pub mod semantic; pub mod util; fn main() { + tracing_subscriber::fmt::init(); let args: Vec = env::args().collect(); if args.len() != 3 { println!("Error: Invalid arguments"); @@ -60,6 +61,10 @@ fn main() { let code_generator = CodeGenerator::new(ir_graphs); fs::write(temp.clone(), code_generator.generate()) .expect("Filesystem: Failed to write assembler output"); + println!( + "Filesystem: Wrote assembler to {}", + temp.clone().to_string() + ); let gcc = Command::new("gcc") .arg(temp) .arg("-o") diff --git a/src/parser/ast.rs b/src/parser/ast.rs index 31dca77..171273f 100644 --- a/src/parser/ast.rs +++ b/src/parser/ast.rs @@ -18,9 +18,22 @@ pub enum Tree { IdentifierExpression(Box), LValueIdentifier(Box), Literal(String, u64, Span), + BoolLiteral(bool, Span), + TernaryOperation(Box, Box, Box), Name(Name, Span), - Negate(Box, Span), + UnaryOperation(Box, OperatorType, Span), Return(Box, Position), + Break(Span), + Continue(Span), + For( + Option>, + Box, + Option>, + Box, + Span, + ), + While(Box, Box, Span), + If(Box, Box, Option>, Span), Type(Type, Span), Program(Vec), } @@ -44,7 +57,7 @@ impl Tree { Tree::LValueIdentifier(name) => name.span(), Tree::Literal(_, _, span) => span.clone(), Tree::Name(_, span) => span.clone(), - Tree::Negate(expression, span) => span.clone().merge(expression.span()), + Tree::UnaryOperation(expression, _, span) => span.clone().merge(expression.span()), Tree::Return(expression, start) => { Span::new(start.clone(), expression.span().end_owned()) } @@ -54,6 +67,13 @@ impl Tree { let last = trees.last().unwrap(); return Span::new(first.span().start().clone(), last.span().end().clone()); } + Tree::BoolLiteral(_, span) => span.clone(), + Tree::TernaryOperation(start, _, end) => start.span().merge(end.span()), + Tree::Break(span) => span.clone(), + Tree::Continue(span) => span.clone(), + Tree::For(_, _, _, _, span) => span.clone(), + Tree::While(_, _, span) => span.clone(), + Tree::If(_, _, _, span) => span.clone(), } } @@ -117,15 +137,52 @@ impl Display for Tree { Tree::Literal(value, _, _) => { write!(f, "{}", value) } - Tree::Negate(tree, _) => { - write!(f, "!{}", tree) + Tree::UnaryOperation(tree, operator, _) => { + write!(f, "{}{}", operator.as_str(), tree) } Tree::Return(expresion, _) => { - write!(f, "return {}", expresion) + writeln!(f, "return {}", expresion) } Tree::Type(type_tree, _) => { write!(f, "{}", type_tree.as_string()) } + Tree::BoolLiteral(value, _) => { + write!(f, "{}", value.to_string()) + } + Tree::TernaryOperation(expression, true_expression, false_expression) => { + write!( + f, + "{} ? {} : {}", + expression, true_expression, false_expression + ) + } + Tree::Continue(_) => { + writeln!(f, "continue") + } + Tree::Break(_) => { + writeln!(f, "break") + } + Tree::For(init, condition, post, statement, _) => { + writeln!(f, "for {:?}; {}; {:?} {{", init, condition, post)?; + writeln!(f, "{}", statement)?; + writeln!(f, "}}") + } + Tree::While(condition, statement, _) => { + writeln!(f, "while {} {{", condition)?; + writeln!(f, "{}", statement)?; + writeln!(f, "}}") + } + Tree::If(condition, statement, else_statement, _) => { + writeln!(f, "if {} {{", condition)?; + writeln!(f, "{}", statement)?; + if let Some(other_statement) = else_statement { + writeln!(f, "}} else {{")?; + writeln!(f, "{}", other_statement)?; + writeln!(f, "}}") + } else { + writeln!(f, "}}") + } + } } } } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index d431436..c3b0269 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -5,7 +5,7 @@ use error::ParseError; use symbols::Name; use types::Type; -use crate::lexer::token::{KeywordType, OperatorType, SeperatorType, Token}; +use crate::lexer::token::{KeywordType, OperatorType, SeperatorType, Token, MAX_PRECEDENCE}; pub mod ast; pub mod error; @@ -58,11 +58,11 @@ fn parse_function(tokens: &mut VecDeque) -> Result { Ok(Tree::Function( Box::new(Tree::Type(Type::Int, return_type.span())), name(identifier)?, - body, + Box::new(body), )) } -fn parse_block(tokens: &mut VecDeque) -> Result, ParseError> { +fn parse_block(tokens: &mut VecDeque) -> Result { let body_open = expect_seperator(tokens, SeperatorType::BraceOpen).unwrap(); let mut statements = vec![]; while !tokens.is_empty() { @@ -75,32 +75,62 @@ fn parse_block(tokens: &mut VecDeque) -> Result, ParseError> { } let body_close = expect_seperator(tokens, SeperatorType::BraceClose) .ok_or(ParseError::Error("Expected BraceClose".to_string()))?; - Ok(Box::new(Tree::Block( + Ok(Tree::Block( statements, body_open.span().merge(body_close.span()), - ))) + )) } fn parse_statement(tokens: &mut VecDeque) -> Result { - let statement = if tokens.front().unwrap().is_keyword(&KeywordType::Int) { - parse_decleration(tokens)? - } else if tokens.front().unwrap().is_keyword(&KeywordType::Return) { - parse_return(tokens)? + let statement = if tokens + .front() + .unwrap() + .is_separator(&SeperatorType::ParenOpen) + { + parse_block(tokens)? + } else if tokens.front().unwrap().is_control_keyword() { + parse_control(tokens)? } else { - parse_simple(tokens)? + let simple = parse_simple(tokens)?; + expect_seperator(tokens, SeperatorType::Semicolon) + .ok_or(ParseError::Error("Expecting Semicolon!".to_string()))?; + simple }; - expect_seperator(tokens, SeperatorType::Semicolon) - .ok_or(ParseError::Error("Expecting Semicolon!".to_string()))?; Ok(statement) } +fn parse_control(tokens: &mut VecDeque) -> Result { + let keyword = tokens.front().unwrap(); + let expression = if keyword.is_keyword(&KeywordType::Return) { + parse_return(tokens) + } else if keyword.is_keyword(&KeywordType::Break) { + parse_break(tokens) + } else if keyword.is_keyword(&KeywordType::Continue) { + parse_continue(tokens) + } else if keyword.is_keyword(&KeywordType::For) { + parse_for(tokens) + } else if keyword.is_keyword(&KeywordType::While) { + parse_while(tokens) + } else if keyword.is_keyword(&KeywordType::If) { + parse_if(tokens) + } else { + Err(ParseError::Error("Expected control keyword".to_string())) + }; + expect_seperator(tokens, SeperatorType::Semicolon); + expression +} + fn parse_decleration(tokens: &mut VecDeque) -> Result { let type_token = expect_keyword(tokens, KeywordType::Int) .ok_or(ParseError::Error("Expected keyword".to_string()))?; let identifier = expect_identifier(tokens).ok_or(ParseError::Error("Expected identifier".to_string()))?; let mut expression = None; - if tokens.front().unwrap().is_operator(&OperatorType::Assign) { + if tokens + .front() + .unwrap() + .is_operator_type(&OperatorType::Assign) + { expect_operator(tokens, OperatorType::Assign); expression = Some(parse_expression(tokens)?); } @@ -121,11 +151,82 @@ fn parse_return(tokens: &mut VecDeque) -> Result { )) } -fn parse_simple(tokens: &mut VecDeque) -> Result { - let lvalue = parse_lvalue(tokens)?; - let assignment_operator = parse_assignment_operator(tokens)?; +fn parse_break(tokens: &mut VecDeque) -> Result { + let break_keyword = expect_keyword(tokens, KeywordType::Break) + .ok_or(ParseError::Error("Expected keyword".to_string()))?; + Ok(Tree::Break(break_keyword.span())) +} + +fn parse_continue(tokens: &mut VecDeque) -> Result { + let continue_keyword = expect_keyword(tokens, KeywordType::Continue) + .ok_or(ParseError::Error("Expected keyword".to_string()))?; + Ok(Tree::Continue(continue_keyword.span())) +} + +fn parse_for(tokens: &mut VecDeque) -> Result { + let keyword = expect_keyword(tokens, KeywordType::For) + .ok_or(ParseError::Error("Expected keyword IF".to_string()))?; + expect_seperator(tokens, SeperatorType::ParenOpen); + let initializer = parse_simple(tokens).ok(); + expect_seperator(tokens, SeperatorType::Semicolon); let expression = parse_expression(tokens)?; - Ok(Tree::Assignment(lvalue, assignment_operator, expression)) + expect_seperator(tokens, SeperatorType::Semicolon); + let updating_expression = parse_simple(tokens).ok(); + expect_seperator(tokens, SeperatorType::ParenClose); + let statement = parse_statement(tokens)?; + let span = keyword.span().merge(statement.span()); + Ok(Tree::For( + initializer.map(|v| Box::new(v)), + expression, + updating_expression.map(|v| Box::new(v)), + Box::new(statement), + span, + )) +} + +fn parse_while(tokens: &mut VecDeque) -> Result { + let keyword = expect_keyword(tokens, KeywordType::While) + .ok_or(ParseError::Error("Expected keyword WHILE".to_string()))?; + expect_seperator(tokens, SeperatorType::ParenOpen); + let expression = parse_expression(tokens)?; + expect_seperator(tokens, SeperatorType::ParenClose); + let statement = parse_statement(tokens)?; + let span = keyword.span().merge(statement.span()); + Ok(Tree::While(expression, Box::new(statement), span)) +} + +fn parse_if(tokens: &mut VecDeque) -> Result { + let keyword = expect_keyword(tokens, KeywordType::If) + .ok_or(ParseError::Error("Expected keyword IF".to_string()))?; + expect_seperator(tokens, SeperatorType::ParenOpen); + let expression = parse_expression(tokens)?; + expect_seperator(tokens, SeperatorType::ParenClose); + let statement = parse_statement(tokens)?; + if tokens.front().unwrap().is_keyword(&KeywordType::Else) { + consume(tokens); + let else_statement = parse_statement(tokens)?; + let span = keyword.span().merge(else_statement.span()); + Ok(Tree::If( + expression, + Box::new(statement), + Some(Box::new(else_statement)), + span, + )) + } else { + let span = keyword.span().merge(statement.span()); + Ok(Tree::If(expression, Box::new(statement), None, span)) + } +} + +fn parse_simple(tokens: &mut VecDeque) -> Result { + if tokens.front().unwrap().is_type_keyword() { + parse_decleration(tokens) + } else { + let lvalue = parse_lvalue(tokens)?; + let assignment_operator = parse_assignment_operator(tokens)?; + let expression = parse_expression(tokens)?; + Ok(Tree::Assignment(lvalue, assignment_operator, expression)) + } } fn parse_assignment_operator(tokens: &mut VecDeque) -> Result { @@ -150,53 +251,89 @@ fn parse_lvalue(tokens: &mut VecDeque) -> Result, ParseError> { .ok_or(ParseError::Error("Expected ParenClose".to_string()))?; return Ok(inner); } - let identifier = - expect_identifier(tokens).ok_or(ParseError::Error("Expected identifier!".to_string()))?; + let identifier = expect_identifier(tokens).ok_or(ParseError::Error(format!( + "Expected identifier, but got {:?}!", + tokens.front().unwrap() + )))?; Ok(Box::new(Tree::LValueIdentifier(name(identifier)?))) } fn parse_expression(tokens: &mut VecDeque) -> Result, ParseError> { - let mut lhs = parse_term(tokens)?; - loop { - match tokens.pop_front().unwrap() { - Token::Operator(_, operator) - if operator.eq(&OperatorType::Plus) || operator.eq(&OperatorType::Minus) => - { - lhs = Box::new(Tree::BinaryOperation(lhs, parse_term(tokens)?, operator)); - } - token => { - tokens.push_front(token); - return Ok(lhs); - } + let lhs = parse_precedence_expression(tokens, MAX_PRECEDENCE)?; + println!("DBG: {:?}", tokens.front().unwrap()); + if !tokens.front().unwrap().is_operator() { + return Ok(lhs); + } + if let Token::Operator(_, operator) = tokens.front().unwrap() { + if !operator.eq(&OperatorType::TernaryQuestionMark) { + return Ok(lhs); } } + consume(tokens); + let true_expression = parse_expression(tokens)?; + expect_operator(tokens, OperatorType::TernaryColon); + let false_expression = parse_expression(tokens)?; + Ok(Box::new(Tree::TernaryOperation( + lhs, + true_expression, + false_expression, + ))) } -fn parse_term(tokens: &mut VecDeque) -> Result, ParseError> { - let mut lhs = parse_factor(tokens)?; +fn parse_precedence_expression( + tokens: &mut VecDeque, + predecence: u8, +) -> Result, ParseError> { + if predecence == 1 { + return parse_unary_expression(tokens, predecence); + } else if predecence == 0 { + return parse_basic_expression(tokens); + } + + let mut lhs = parse_precedence_expression(tokens, predecence - 1)?; loop { - match tokens.pop_front().unwrap() { - Token::Operator(_, operator_type) - if matches!( - operator_type, - OperatorType::Mul | OperatorType::Div | OperatorType::Mod - ) => + let next_operator = tokens.pop_front().unwrap(); + if let Token::Operator(_, ref operator) = next_operator { + if matches!( + operator, + OperatorType::TernaryColon | OperatorType::TernaryQuestionMark + ) || operator.is_assignment_operator() { - lhs = Box::new(Tree::BinaryOperation( - lhs, - parse_factor(tokens)?, - operator_type, - )); - } - token => { - tokens.push_front(token); + tokens.push_front(next_operator); return Ok(lhs); } + if operator.get_precedence().contains(&predecence) { + let rhs = parse_precedence_expression(tokens, predecence - 1)?; + lhs = Box::new(Tree::BinaryOperation(lhs, rhs, operator.clone())); + continue; + } } + tokens.push_front(next_operator); + return Ok(lhs); } } -fn parse_factor(tokens: &mut VecDeque) -> Result, ParseError> { +fn parse_unary_expression( + tokens: &mut VecDeque, + predecence: u8, +) -> Result, ParseError> { + let token = tokens.pop_front().unwrap(); + if let Token::Operator(_, ref operator_type) = token { + if operator_type.get_precedence().contains(&predecence) { + let value = parse_precedence_expression(tokens, predecence)?; + let span = token.clone().span().merge(value.span()); + return Ok(Box::new(Tree::UnaryOperation( + value, + operator_type.clone(), + span, + ))); + } + } + tokens.push_front(token); + parse_precedence_expression(tokens, predecence - 1) +} + +fn parse_basic_expression(tokens: &mut VecDeque) -> Result, ParseError> { match tokens.pop_front().unwrap() { Token::Separator(_, seperator) if seperator.eq(&SeperatorType::ParenOpen) => { let expression = parse_expression(tokens); @@ -204,6 +341,7 @@ fn parse_factor(tokens: &mut VecDeque) -> Result, ParseError> { .ok_or(ParseError::Error("Expecting ParenClose".to_string()))?; expression } + // FIXME: Janky negative hack, because I wanted nothing to do with int parsing Token::Operator(span, operator) if operator.eq(&OperatorType::Minus) => { match tokens.pop_front().unwrap() { Token::NumberLiteral(span, value, base) => Ok(Box::new(Tree::Literal( @@ -213,7 +351,11 @@ fn parse_factor(tokens: &mut VecDeque) -> Result, ParseError> { ))), token => { tokens.push_front(token); - Ok(Box::new(Tree::Negate(parse_factor(tokens)?, span))) + Ok(Box::new(Tree::UnaryOperation( + parse_expression(tokens)?, + OperatorType::Minus, + span, + ))) } } } @@ -221,6 +363,12 @@ fn parse_factor(tokens: &mut VecDeque) -> Result, ParseError> { Ok(Box::new(Tree::IdentifierExpression(name(identifier)?))) } Token::NumberLiteral(span, value, base) => Ok(Box::new(Tree::Literal(value, base, span))), + Token::Keyword(span, keyword) if keyword.eq(&KeywordType::True) => { + Ok(Box::new(Tree::BoolLiteral(true, span))) + } + Token::Keyword(span, keyword) if keyword.eq(&KeywordType::False) => { + Ok(Box::new(Tree::BoolLiteral(false, span))) + } token => Err(ParseError::Error(format!( "Expected ParenOpen, Minus, Identifier or Number Literal, but got {:?}", token @@ -283,6 +431,6 @@ fn expect_identifier(tokens: &mut VecDeque) -> Option { None } -fn _consume(tokens: &mut VecDeque) -> Option { +fn consume(tokens: &mut VecDeque) -> Option { tokens.pop_front() } diff --git a/src/semantic/mod.rs b/src/semantic/mod.rs index 11ce79a..b90e37b 100644 --- a/src/semantic/mod.rs +++ b/src/semantic/mod.rs @@ -135,6 +135,9 @@ pub fn analyze(tree: Box, state: &mut AnalysisState) -> Result<(), String> Ok(()) } Tree::Name(_, _) => Ok(()), + Tree::BoolLiteral(_, _) => Ok(()), + Tree::Continue(_) => Ok(()), + Tree::Break(_) => Ok(()), Tree::BinaryOperation(lhs, rhs, _) => { analyze(lhs, state)?; analyze(rhs, state) @@ -146,7 +149,7 @@ pub fn analyze(tree: Box, state: &mut AnalysisState) -> Result<(), String> Ok(()) } Tree::LValueIdentifier(name) => analyze(name, state), - Tree::Negate(expression, _) => analyze(expression, state), + Tree::UnaryOperation(expression, _, _) => analyze(expression, state), Tree::Type(_, _) => Ok(()), Tree::Program(statements) => { for statement in statements { @@ -154,5 +157,32 @@ pub fn analyze(tree: Box, state: &mut AnalysisState) -> Result<(), String> } Ok(()) } + Tree::TernaryOperation(statement, true_statement, false_statement) => { + analyze(statement, state)?; + analyze(true_statement, state)?; + analyze(false_statement, state) + } + Tree::If(condition, expression, else_expression, _) => { + analyze(condition, state)?; + analyze(expression, state)?; + if let Some(other_expression) = else_expression { + analyze(other_expression, state)?; + } + Ok(()) + } + Tree::While(condition, expression, _) => { + analyze(condition, state)?; + analyze(expression, state) + } + Tree::For(initializer, condition, updater, expression, _) => { + if let Some(initializer_expression) = initializer { + analyze(initializer_expression, state)?; + } + analyze(condition, state)?; + if let Some(updater_expression) = updater { + analyze(updater_expression, state)?; + } + analyze(expression, state) + } } } From d5170b1b95289645f77641acd4fd6164a99e5f36 Mon Sep 17 00:00:00 2001 From: Felix Schwickerath Date: Tue, 10 Jun 2025 15:52:15 +0200 Subject: [PATCH 02/52] feat(ir): implement new more flexible ir --- output | Bin 0 -> 15720 bytes src/backend/codegen.rs | 452 +++++++----------- src/backend/regalloc.rs | 89 +++- src/ir/block.rs | 70 +++ src/ir/constructor.rs | 767 +++++++++++++----------------- src/ir/graph.rs | 79 ++- src/ir/mod.rs | 2 +- src/ir/node.rs | 819 -------------------------------- src/ir/node/binary_operation.rs | 38 ++ src/ir/node/mod.rs | 143 ++++++ src/ir/node/projection.rs | 32 ++ src/ir/node/unary_operation.rs | 27 ++ src/ir/optimizer.rs | 19 - src/main.rs | 33 +- src/parser/mod.rs | 3 +- test.l2 | 3 + 16 files changed, 940 insertions(+), 1636 deletions(-) create mode 100755 output create mode 100644 src/ir/block.rs delete mode 100644 src/ir/node.rs create mode 100644 src/ir/node/binary_operation.rs create mode 100644 src/ir/node/mod.rs create mode 100644 src/ir/node/projection.rs create mode 100644 src/ir/node/unary_operation.rs delete mode 100644 src/ir/optimizer.rs create mode 100644 test.l2 diff --git a/output b/output new file mode 100755 index 0000000000000000000000000000000000000000..a2931697a2a5062940aa89eb7b5f3fb2ea6717b1 GIT binary patch literal 15720 zcmeHOU2Ggz6+XLm631z3J0Yzhfs7O>4G%kBukE!PNHYFQ*2wv(<31FrOxL^P-D!7c zH@o9v2O)|AqNXoKsC|bA@C#9q0EGv@!KiL{K+O|=;31$!vScVm6jTt(a?ZWyjMq~a zsvseR<|;GiobR6R-gD1f&)&)0`O^60`FJb_QsQtH$TB)D1?e+DF|AC-hTtGra14&Z zUPM~)uv866t%+QZ>k5%=?AL;lc-^EZY3-0#l?5PDA|y$??P9U2W!oNYiiA#W6HisK z4Lm-PPjLq#n|Z1~L$?Elr4^QMGAy#0N4e=XwL|w=ODQ7XlL2vlghwXvPBHHk^F+SR zJdu)5ijj_@fhR0Qj!&ee(CzCK<}DlAVVQX%83vJ(`>Xf;{KNEjg?X*qU@P}0@|X?- z=aw$g-&4q=ynNUqmwAf&`=INv!TA&^m!-#Z=X>gYrRMbH3QyJt(y4m3ukVGyp>n#< zdw$mIt$n_KFkgN0deTi4O72W9kuv*}iFCRLjh<3IQF6Vx`NaHCI+5-->lHJlBd7Lh z%F}h0LRj-5_=l0iJG6`AO~i~{aIkHP)~@^6ns1l0t_NgOglV`iIWaP7r_8>7_>aba zx`{oQOTL(VVWw8g=TfR;Oh_ z<_w+v!?(zVy4e3S0vh*bmsV%DojZ^Xr*?Ag`#^+*1jU*C9y0$W_mtgU<+wT?Z+ z#I#_rZ~F%}VPfqz!mzm?6?&T3go}%J@ZaR(n|D?r$YGE*a*j%rfhYq}2BHi^8Hh3v zWgyBxlz}J%Q3j$6L>c(s$v}+Ws2y$jR5yl1@57F5H5&aWCsAflegqFHvFlS1o9~Pr zePmzT5RR6A_)rS{36_*CdUYP-^+QG_XJ zwZ&5MFAo|OQeI|To;SDHrhT7Omf4o)#Ghyw4zQ%>UMfel2+sRhTW__LCUMX+ES3FQ zfbbtQbUe4%A747F_+spbkJHM&$@L8X*M#gJ|92f;7#%%roWdvUPa1ktt|6Op|231w z*$RJZguN(lF?@0l??wdmV)Fu zOe_2l$UIY(c=`e!)GzRS75r%zE{Kt%+4B>j7 zw!=^ezx@II{u1#RJPOYlmTIy2v|F6(%KtEg=f!Uo@gT^4d}-%h<$nY=&RGH}*^Y^& zYZ)$S#0_{1fb%c)PaxijxLg;(O=>580OWolct87x+ZjgO5T_=21@W%qJ!&J%<2lic?rw-X-Mw-w$Fv{|OPyoxx* z8TS7c`#&D?|1RQ(;&|8LeuK7hHNV%a;7=q<#7P`K#m8_Vn<`;e z4FWyM&|ejxvEvg1tgBb-V%E!-9Iz)YqgUSb?76y=2OKw^@+G_Glq$Kb?@$xIo3pDf z`r0$~deG+d#ZeiE`o=<~1i-8>l>O`sO24LMQ8d@X%Txuk;LVxEY`q9(e!)X`E&ZCV zdC{rW-HNxvu(1v!$`S!g$~~CAGmmGwWLQ^GV>g{5U!-Cl8DfFyReVQvTdh^9PR(Cn zXn15IK@&hwx=xvdnz?y`n-fQtZN)^~C^U!BrAmb2wj`_iFrD5qR zLyNG#*do7(Zq&czPkx_UnJ@ECxAy+=zY^jb+`qy6F(I;){0salDzqk+{^h&EqMs&Fd$1mp@A|_1i7oQy$n9w2%lgmY?<~a+ zT~rZU)O#K=YX@_pKR4|(f%TUyg+;fwqY!p(eH zhix!l?i136@I>AZ@q_hLQZppZFkkFVRH!Y9U)C+1%$I(|pVJ{GY@tRNy9c=N!+*cR zZ = HashMap<&'a Node, Box>; -const TEMPLATE: &str = ".global main +const TEMPLATE: &str = " .section .note.GNU-stack,\"\",@progbits +.global main .global _main .text main: @@ -61,239 +57,179 @@ impl CodeGenerator { ir_graph: &IRGraph, register_allocator: RegisterAllocator, ) -> String { - let mut visited = Vec::new(); let (registers, stack_offset) = register_allocator.allocate_registers(ir_graph); let mut code = String::new(); code.push_str("pushq %rbp\n"); code.push_str("mov %rsp, %rbp\n"); code.push_str(&format!("subq ${}, %rsp\n", stack_offset)); - let mut open_blocks = VecDeque::new(); - // FIXME: Can enter exit block from multiple points, therefore different points to starting - // analysis - let mut recorded_ends: HashMap> = HashMap::new(); - open_blocks.push_back(END_BLOCK); - recorded_ends.insert(END_BLOCK, vec![(END_BLOCK, END_BLOCK)]); - let mut block_codes = String::new(); - while !open_blocks.is_empty() { - trace!("Current recorded ends: {:?}", recorded_ends); - let current_block = open_blocks.pop_front().unwrap(); - trace!("Current block: {}", current_block); - let mut current_block_ends = recorded_ends.remove(¤t_block).unwrap(); - current_block_ends.sort_by(|(x, _), (y, _)| x.cmp(y)); - trace!("After recorded ends: {:?}", recorded_ends); - let mut full_block_code = String::new(); - for current_block_end in current_block_ends { - trace!( - "Generating Assembly for Block {}, with origins at {}", - current_block, - current_block_end.1 - ); - trace!("Current code:{}\n", block_codes); - let (block_code, previous_blocks) = &self.generate_for_node( - current_block_end.0, - ir_graph, - ®isters, - &mut visited, - current_block_end.1, - ); - full_block_code.push_str(&block_code); - info!( - "Generated assembly for block {:?}: {}", - current_block, block_code - ); - trace!( - "New open blocks: {:?} with previous node {:?}", - previous_blocks, - current_block - ); - for previous_block in previous_blocks { - let block_index = ir_graph.get_node(*previous_block).block(); - if !open_blocks.contains(&block_index) { - open_blocks.push_back(block_index); - } - if let Some(mut old_end) = recorded_ends.remove(&block_index) { - old_end.push((*previous_block, current_block)); - recorded_ends.insert(block_index, old_end); - } else { - recorded_ends.insert(block_index, vec![(*previous_block, current_block)]); - } + let mut block_code = String::new(); + // Jump Information contains for each block(key) the map of exits (NodeIndex[Node that jumps], BlockIndex[Block that is jumped to]) + let mut jump_information = HashMap::new(); + let mut visited = Vec::new(); + let mut queue = VecDeque::new(); + jump_information.insert(END_BLOCK, HashMap::new()); + visited.push(END_BLOCK); + queue.push_back((ir_graph.end_block(), END_BLOCK)); + while !queue.is_empty() { + let (block, block_index) = queue.pop_front().unwrap(); + for (previous_block_index, previous_node) in block.entry_points() { + if !visited.contains(previous_block_index) { + let previous_block = ir_graph.get_block(*previous_block_index); + queue.push_back((previous_block, *previous_block_index)); + visited.push(*previous_block_index); + jump_information.insert( + *previous_block_index, + HashMap::from([(*previous_node, block_index)]), + ); + } else { + let mut existing_exits = jump_information + .get_mut(previous_block_index) + .unwrap() + .clone(); + existing_exits.insert(*previous_node, block_index); + jump_information.insert(*previous_block_index, existing_exits); } } - block_codes = full_block_code + &block_codes; + block_code = self.generate_for_block( + block, + block_index, + jump_information.get(&block_index).unwrap(), + ir_graph, + ®isters, + ) + block_code.as_str(); } - code.push_str(&block_codes); + code.push_str(&block_code); code } - pub fn generate_for_node( + pub fn generate_for_block( &self, - node_index: usize, + block: &Block, + block_index: BlockIndex, + jump_information: &HashMap, ir_graph: &IRGraph, registers: &Registers, - visited: &mut Vec, - following_block: usize, - ) -> (String, Vec) { - debug!( - "Generating assembly for node {}", - ir_graph.get_node(node_index) - ); - let mut code = String::new(); - let mut previous_blocks = Vec::new(); - let node = ir_graph.get_node(node_index); - if let Node::Block(data) = node { - if let Some(label) = self.jump_label.get(&node.block()) { - code.push_str(&format!("{}:\n", label)); - } - debug!("Finished generating code for block: {:?}", data); - previous_blocks.append(&mut data.predecessors().clone()); - return (code, previous_blocks); + ) -> String { + // Start and End Nodes should not emit code + if block.get_nodes().is_empty() { + return String::new(); } - for predecessor in node.predecessors() { - let predecessor_block = ir_graph.get_node(*predecessor).block(); - if predecessor_block.ne(&node.block()) { - // DO NOT ANALYZE BLOCKS NOT IN CURRENT BLOCK - trace!( - "Not in current analyzed block({:?}): {:?}", - node.block(), - predecessor_block - ); - continue; - } - if !visited.contains(predecessor) { - visited.push(*predecessor); - let (predecessor_code, predecessor_blocks) = self.generate_for_node( - *predecessor, - ir_graph, - registers, - visited, - following_block, - ); - code.push_str(&predecessor_code); - for previous_block in predecessor_blocks { - if !previous_blocks.contains(&previous_block) { - previous_blocks.push(previous_block); - } - } - } + + let mut code = String::new(); + let block_label = self.jump_label.get(&block_index).unwrap(); + code.push_str(&format!("{}:\n", block_label)); + for (node_index, node) in block.get_nodes().iter().enumerate() { + code.push_str(&self.generate_for_node( + node, + node_index, + block, + jump_information, + ir_graph, + registers, + )); } + code + } + + pub fn generate_for_node( + &self, + node: &Node, + node_index: NodeIndex, + block: &Block, + jump_information: &HashMap, + ir_graph: &IRGraph, + registers: &Registers, + ) -> String { + debug!("Generating assembly for node {}", node); + let mut code = String::new(); match node { - Node::Add(data) => { - code.push_str(&self.generate_binary_operation( - node_index, - data.binary_operation_data(), - ir_graph, - registers, - "add", - )); + Node::Add(_) => { + code.push_str(&self.generate_binary_operation(node_index, block, registers, "add")); } - Node::Subtraction(data) => { - code.push_str(&self.generate_binary_operation( - node_index, - data.binary_operation_data(), - ir_graph, - registers, - "sub", - )); + Node::Subtraction(_) => { + code.push_str(&self.generate_binary_operation(node_index, block, registers, "sub")); } - Node::Multiplication(data) => { - code.push_str(&self.generate_binary_operation_rax( - node_index, - data.binary_operation_data(), - ir_graph, - registers, - "imul", - "mul", - )); + Node::Multiplication(_) => { + code.push_str( + &self + .generate_binary_operation_rax(node_index, block, registers, "imul", "mul"), + ); } - Node::Division(data) => { - code.push_str(&self.generate_binary_operation_rax( - node_index, - data.binary_operation_data(), - ir_graph, - registers, - "idiv", - "div", - )); + Node::Division(_) => { + code.push_str( + &self + .generate_binary_operation_rax(node_index, block, registers, "idiv", "div"), + ); } - Node::Modulo(data) => { - code.push_str(&self.generate_binary_operation_rax( - node_index, - data.binary_operation_data(), - ir_graph, - registers, - "idiv", - "mod", - )); + Node::Modulo(_) => { + code.push_str( + &self + .generate_binary_operation_rax(node_index, block, registers, "idiv", "mod"), + ); } Node::Return(_) => { - code.push_str(&self.generate_return(ir_graph, registers, node_index)); + code.push_str(&self.generate_return(block, registers, node_index)); } Node::ConstantInt(data) => { - code.push_str(&self.generate_constant_int(data, ir_graph, registers, node_index)); + code.push_str(&self.generate_constant_int(data, block, registers, node_index)); } Node::ShiftLeft(data) => { - code.push_str(&self.generate_shift( - node_index, - data.binary_operation_data(), - ir_graph, - registers, - "sall", - )); + code.push_str(&self.generate_shift(node_index, block, data, registers, "sall")); } Node::ShiftRight(data) => { - code.push_str(&self.generate_shift( - node_index, - data.binary_operation_data(), - ir_graph, - registers, - "sarl", - )); + code.push_str(&self.generate_shift(node_index, block, data, registers, "sarl")); } Node::Equals(data) => { - code.push_str(&self.generate_comparison( - node_index, - data.binary_operation_data(), - ir_graph, - registers, - )); + code.push_str(&self.generate_comparison(block, data, registers)); } Node::ConstantBool(data) => code.push_str(&self.generate_constant_bool(data.value())), Node::Phi(data) => { - println!( - "Warning! Phi present: Aliasing {:?}", - data.node_data().predecessors() - ); + debug!("Warning! Phi present: Aliasing {:?}", data.operands()); + let destination_register = registers.get(node).unwrap(); + for (operand_block, operand_node) in data.operands() { + let operand_block = ir_graph.get_block(operand_block); + let operand_node = operand_block.get_node(operand_node); + let register = registers + .get(operand_node) + .expect("Expected register for phi operand"); + code.push_str(&format!( + "mov {}, {}\n", + register.as_assembly(), + destination_register.as_assembly() + )); + } } - Node::Jump(_) => { + Node::Jump => { trace!( - "Generating assembly for jump: {} with destination {}", + "Generating assembly for jump: {} with destination XXX", node, - following_block ); - let label = self.jump_label.get(&following_block).unwrap(); + let previous_block_index = jump_information.get(&node_index).unwrap(); + let label = self.jump_label.get(previous_block_index).unwrap(); code.push_str(&format!("jmp {}\n", label)); } Node::ConditionalJump(_) => {} Node::Projection(data) if data.projection_info().eq(&ProjectionInformation::IfTrue) => { + let previous_block_index = jump_information.get(&node_index).unwrap(); let conditional_jump_code = - self.generate_conditional_jump(node_index, following_block, ir_graph); + self.generate_conditional_jump(node_index, block, *previous_block_index); code.push_str(&conditional_jump_code.expect("Expected jump code")); } Node::Projection(data) if data.projection_info().eq(&ProjectionInformation::IfFalse) => { + let previous_block_index = jump_information.get(&node_index).unwrap(); let jump_label = self .jump_label - .get(&following_block) + .get(previous_block_index) .expect("Expected jump label for false if"); code.push_str(&format!("jmp {}\n", jump_label)); } - Node::Block(data) => return (code, previous_blocks), - Node::Projection(_) | Node::Start(_) => return (code, previous_blocks), + Node::Projection(_) => return code, node => panic!("unimplemented node {:?}", node), } - (code, previous_blocks) + code } pub fn generate_constant_bool(&self, value: bool) -> String { @@ -309,18 +245,18 @@ impl CodeGenerator { pub fn generate_conditional_jump( &self, projection_index: usize, - suceeding_block: usize, - ir_graph: &IRGraph, + current_block: &Block, + previous_block: usize, ) -> Option { let mut code = String::new(); - let true_label = self.jump_label.get(&suceeding_block).unwrap(); - let projection = ir_graph.get_node(projection_index); - let comparision = ir_graph + let true_label = self.jump_label.get(&previous_block).unwrap(); + let projection = current_block.get_node(projection_index); + let comparision = *current_block .get_node(*projection.predecessors().get(0).unwrap()) .predecessors() - .get(COMPARISON_INDEX) + .get(0) .unwrap(); - let op_code = match ir_graph.get_node(*comparision) { + let op_code = match current_block.get_node(comparision) { Node::Lower(_) => "jb", Node::LowerEquals(_) => "jbe", Node::Equals(_) => "je", @@ -336,17 +272,12 @@ impl CodeGenerator { pub fn generate_comparison( &self, - node_index: usize, + block: &Block, operation_data: &BinaryOperationData, - ir_graph: &IRGraph, registers: &Registers, ) -> String { - let left_value = registers - .get(ir_graph.get_node(operation_data.left())) - .unwrap(); - let right_value = registers - .get(ir_graph.get_node(operation_data.right())) - .unwrap(); + let left_value = registers.get(block.get_node(operation_data.lhs())).unwrap(); + let right_value = registers.get(block.get_node(operation_data.rhs())).unwrap(); let mut code = String::new(); //TODO: Both registers can be in memory code.push_str(&format!( @@ -360,27 +291,18 @@ impl CodeGenerator { pub fn generate_binary_operation( &self, node_index: usize, - _operation_data: &BinaryOperationData, - ir_graph: &IRGraph, + block: &Block, registers: &Registers, op_code: &str, ) -> String { let left_value = registers - .get(ir_graph.get_node(predecessor_skip_projection( - node_index, - BINARY_OPERATION_LEFT, - ir_graph, - ))) + .get(block.get_node(predecessor_skip_projection(node_index, 0, block))) .unwrap(); let right_value = registers - .get(ir_graph.get_node(predecessor_skip_projection( - node_index, - BINARY_OPERATION_RIGHT, - ir_graph, - ))) + .get(block.get_node(predecessor_skip_projection(node_index, 1, block))) .unwrap(); - let destination_register = registers.get(ir_graph.get_node(node_index)).unwrap(); + let destination_register = registers.get(block.get_node(node_index)).unwrap(); let mut code = String::new(); if !left_value.hardware_register() && !destination_register.hardware_register() { @@ -416,27 +338,18 @@ impl CodeGenerator { pub fn generate_binary_operation_rax( &self, node_index: usize, - _operation_data: &BinaryOperationData, - ir_graph: &IRGraph, + block: &Block, registers: &Registers, op_code: &str, mode: &str, ) -> String { let left_value = registers - .get(ir_graph.get_node(predecessor_skip_projection( - node_index, - BINARY_OPERATION_LEFT, - ir_graph, - ))) + .get(block.get_node(predecessor_skip_projection(node_index, 0, block))) .unwrap(); let right_value = registers - .get(ir_graph.get_node(predecessor_skip_projection( - node_index, - BINARY_OPERATION_RIGHT, - ir_graph, - ))) + .get(block.get_node(predecessor_skip_projection(node_index, 1, block))) .unwrap(); - let destination_register = registers.get(ir_graph.get_node(node_index)).unwrap(); + let destination_register = registers.get(block.get_node(node_index)).unwrap(); let mut code = String::new(); code.push_str("mov $0, %rdx\n"); code.push_str("mov $0, %rax\n"); @@ -469,15 +382,15 @@ impl CodeGenerator { pub fn generate_shift( &self, - node: usize, + node_index: NodeIndex, + block: &Block, data: &BinaryOperationData, - ir_graph: &IRGraph, registers: &Registers, op_code: &str, ) -> String { let mut code = String::new(); - let left_value = registers.get(ir_graph.get_node(data.left())).unwrap(); - let right_value = registers.get(ir_graph.get_node(data.right())).unwrap(); + let left_value = registers.get(block.get_node(data.lhs())).unwrap(); + let right_value = registers.get(block.get_node(data.rhs())).unwrap(); if !left_value.hardware_register() && !right_value.hardware_register() { code.push_str(&move_stack_variable(right_value)); code.push_str(&format!( @@ -494,7 +407,7 @@ impl CodeGenerator { right_value.as_16_bit_assembly() )); } - let destination = registers.get(ir_graph.get_node(node)).unwrap(); + let destination = registers.get(block.get_node(node_index)).unwrap(); code.push_str(&format!( "movq {}, {}", left_value.as_assembly(), @@ -505,18 +418,23 @@ impl CodeGenerator { pub fn generate_return( &self, - ir_graph: &IRGraph, + block: &Block, registers: &Registers, node_index: usize, ) -> String { - let return_node_index = - predecessor_skip_projection(node_index, RETURN_RESULT_INDEX, ir_graph); + debug!("Generating assembly for return"); + let return_node_index = predecessor_skip_projection(node_index, 0, block); + debug!( + "Determined node {} that contains the return result", + return_node_index + ); + debug!("Registers: {:?}", registers); let mut code = String::new(); code.push_str("mov "); code.push_str( ®isters - .get(ir_graph.get_node(return_node_index)) + .get(block.get_node(return_node_index)) .unwrap() .as_assembly(), ); @@ -531,11 +449,11 @@ impl CodeGenerator { pub fn generate_constant_int( &self, constant_data: &ConstantIntData, - ir_graph: &IRGraph, + block: &Block, registers: &Registers, - node_index: usize, + node_index: NodeIndex, ) -> String { - let register = registers.get(ir_graph.get_node(node_index)).unwrap(); + let register = registers.get(block.get_node(node_index)).unwrap(); let mut code = String::new(); code.push_str("mov "); @@ -547,15 +465,16 @@ impl CodeGenerator { } } -fn predecessor_skip_projection(node: usize, predecessor_index: usize, graph: &IRGraph) -> usize { - let predecessor = graph - .get_predecessors(node) +fn predecessor_skip_projection(node: usize, predecessor_index: usize, block: &Block) -> usize { + let predecessor = *block + .get_node(node) + .predecessors() .get(predecessor_index) .expect("Invalid predecessor index"); - if let Node::Projection(data) = graph.get_node(*predecessor) { + if let Node::Projection(data) = block.get_node(predecessor) { data.input() } else { - *predecessor + predecessor } } @@ -571,43 +490,18 @@ fn move_stack_variable(register: &Box) -> String { fn calculate_jump_label(ir_graphs: &Vec) -> HashMap { let mut jump_label = HashMap::new(); - let mut visited = Vec::new(); - let mut current_label = 0_usize; for ir_graph in ir_graphs { - calculate_jump_label_node( - &mut current_label, - ir_graph, - END_BLOCK, - &mut jump_label, - &mut visited, - ); + for (block_index, block) in ir_graph.get_blocks().iter().enumerate() { + calculate_jump_label_block(block_index, block, &mut jump_label); + } } jump_label } -fn calculate_jump_label_node<'a>( - current_label: &mut usize, - ir_graph: &IRGraph, - node_index: usize, +fn calculate_jump_label_block<'a>( + block_index: BlockIndex, + _block: &Block, current: &mut HashMap, - visited: &mut Vec, ) { - let node = ir_graph.get_node(node_index); - for predecessor in node.predecessors() { - if !visited.contains(predecessor) { - visited.push(*predecessor); - calculate_jump_label_node(current_label, ir_graph, *predecessor, current, visited); - } - } - if node_index == END_BLOCK { - return; - } - match node { - Node::Block(_) => { - let block = ir_graph.get_node(node_index).block(); - current.insert(block, format!("LC{}", current_label.to_string())); - *current_label += 1; - } - _ => {} - }; + current.insert(block_index, format!("LC{}", block_index)); } diff --git a/src/backend/regalloc.rs b/src/backend/regalloc.rs index bfd018e..8c7a28c 100644 --- a/src/backend/regalloc.rs +++ b/src/backend/regalloc.rs @@ -3,8 +3,10 @@ use std::{ fmt::{Debug, Display}, }; +use tracing::debug; + use crate::ir::{ - graph::{IRGraph, END_BLOCK}, + graph::{BlockIndex, IRGraph, END_BLOCK}, node::Node, }; pub trait Register { @@ -12,9 +14,10 @@ pub trait Register { fn as_32_bit_assembly(&self) -> String; fn as_16_bit_assembly(&self) -> String; fn hardware_register(&self) -> bool; + fn box_clone(&self) -> Box; } -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum HardwareRegister { Rax, Rbx, @@ -106,9 +109,13 @@ impl Register for HardwareRegister { fn as_16_bit_assembly(&self) -> String { format!("%{}", self.as_assembly_16_bit()) } + + fn box_clone(&self) -> Box { + Box::new(self.clone()) + } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct StackRegister { offset: usize, } @@ -139,6 +146,10 @@ impl Register for StackRegister { fn hardware_register(&self) -> bool { false } + + fn box_clone(&self) -> Box { + Box::new(self.clone()) + } } impl Display for StackRegister { @@ -150,6 +161,7 @@ impl Display for StackRegister { pub struct RegisterAllocator<'a> { current_stack_offset: usize, registers: HashMap<&'a Node, Box>, + aliased_nodes: Vec<&'a Node>, available_hardware_register: Vec, } @@ -158,6 +170,7 @@ impl<'a> RegisterAllocator<'a> { RegisterAllocator { current_stack_offset: 0, registers: HashMap::new(), + aliased_nodes: Vec::new(), available_hardware_register: vec![ //HardwareRegister::Rax, //HardwareRegister::Rbx, @@ -182,34 +195,71 @@ impl<'a> RegisterAllocator<'a> { graph: &'a IRGraph, ) -> (HashMap<&'a Node, Box>, usize) { let mut visited = Vec::new(); - visited.push(END_BLOCK); self.scan(END_BLOCK, graph, &mut visited); - //dbg!(&self.registers); (self.registers, self.current_stack_offset) } - pub fn scan(&mut self, current_index: usize, graph: &'a IRGraph, visited: &mut Vec) { - let node = graph.get_node(current_index); - for predecessor in node.predecessors() { + pub fn scan( + &mut self, + block_index: BlockIndex, + graph: &'a IRGraph, + visited: &mut Vec, + ) { + let block = graph.get_block(block_index); + for (predecessor, _) in block.entry_points() { if !visited.contains(predecessor) { visited.push(*predecessor); self.scan(*predecessor, graph, visited); } } - if needs_register(node) { - let register = self.get_available_register(); - self.registers.insert(node, register); + debug!("Scanning block {}, for register allocation", block); + for node in block.get_nodes() { + if needs_register(node) { + let register = self.get_available_register(node, graph); + self.registers.insert(node, register); + } } } - pub fn get_available_register(&mut self) -> Box { + pub fn get_available_register( + &mut self, + node: &Node, + ir_graph: &'a IRGraph, + ) -> Box { + if self.aliased_nodes.contains(&node) { + debug!("Not generating new register for node that is aliased by a following phi!"); + } + debug!("Allocating new register for: {}", node); if self.has_available_hardware_register() { let register = self.available_hardware_register.pop().unwrap(); - Box::new(register) + let register: Box = Box::new(register); + self.handle_phi_register(node, ir_graph, ®ister); + register } else { let register = StackRegister::new(self.current_stack_offset); self.current_stack_offset += 8; - Box::new(register) + let boxed_register: Box = Box::new(register); + self.handle_phi_register(node, ir_graph, &boxed_register); + boxed_register + } + } + + pub fn handle_phi_register( + &mut self, + node: &Node, + ir_graph: &'a IRGraph, + register: &Box, + ) { + if let Node::Phi(data) = node { + debug!("Handling phi in register allocation: Aliasing the following nodes to store in the same node: {:?}", data.operands()); + for (predecessor_block, predecessor_index) in data.operands() { + let predecessor_node = ir_graph + .get_block(predecessor_block) + .get_node(predecessor_index); + self.registers + .insert(predecessor_node, register.box_clone()); + self.aliased_nodes.push(predecessor_node); + } } } @@ -222,11 +272,14 @@ impl<'a> RegisterAllocator<'a> { } } +impl Clone for Box { + fn clone(&self) -> Self { + self.box_clone() + } +} + fn needs_register(node: &Node) -> bool { - !matches!( - node, - Node::Projection(_) | Node::Start(_) | Node::Block(_) | Node::Return(_) - ) + !matches!(node, Node::Projection(_) | Node::Return(_)) } impl Default for RegisterAllocator<'_> { diff --git a/src/ir/block.rs b/src/ir/block.rs new file mode 100644 index 0000000..3f3b7e5 --- /dev/null +++ b/src/ir/block.rs @@ -0,0 +1,70 @@ +use std::{collections::HashMap, fmt::Display}; + +use super::{graph::BlockIndex, node::Node}; + +pub type NodeIndex = usize; + +pub struct Block { + // Denote that block can be entered from block (key) at node index (value) within that block + entry_points: HashMap, + nodes: Vec, + name: String, +} + +impl Block { + pub fn new(name: String) -> Block { + Block { + entry_points: HashMap::new(), + nodes: Vec::new(), + name, + } + } + + pub fn register_entry_point(&mut self, block: BlockIndex, node_index: NodeIndex) { + self.entry_points.insert(block, node_index); + } + + pub fn entry_points(&self) -> &HashMap { + &self.entry_points + } + + pub fn register_node(&mut self, node: Node) -> NodeIndex { + self.nodes.push(node); + return self.nodes.len() - 1; + } + + pub fn get_node(&self, node_index: NodeIndex) -> &Node { + self.nodes.get(node_index).expect("Expected node at index") + } + + pub fn get_node_mut(&mut self, node_index: NodeIndex) -> &mut Node { + self.nodes + .get_mut(node_index) + .expect("Expected node at index") + } + + pub fn get_nodes(&self) -> &Vec { + &self.nodes + } + + pub fn get_last_node_index(&self) -> NodeIndex { + self.nodes.len() - 1 + } + + pub fn get_label(&self, index: usize) -> String { + let mut label = self.name.to_owned(); + label.push_str(&index.to_string()); + label + } +} + +impl Display for Block { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + writeln!(f, "-- Block {} --", self.name)?; + writeln!(f, "Entry Points: {:?}", self.entry_points)?; + for node in &self.nodes { + writeln!(f, "{}", node)?; + } + Ok(()) + } +} diff --git a/src/ir/constructor.rs b/src/ir/constructor.rs index 78b873b..5d6fd3c 100644 --- a/src/ir/constructor.rs +++ b/src/ir/constructor.rs @@ -1,45 +1,44 @@ use core::panic; use std::collections::HashMap; -use tracing::{debug, event, Level}; +use tracing::{debug, trace}; use crate::{ - ir::{graph, node::JumpData}, + ir::{ + block::Block, + graph::START_BLOCK, + node::{ + binary_operation::BinaryOperationData, + projection::{ProjectionData, ProjectionInformation}, + }, + }, lexer::token::{OperatorType, Token}, parser::{ast::Tree, symbols::Name}, util::int_parsing::parse_int, }; use super::{ - graph::{IRGraph, START_BLOCK}, + block::NodeIndex, + graph::{BlockIndex, IRGraph}, node::{ - AddData, AndData, BitwiseNegateData, BlockData, ConditionalJumpData, ConstantBoolData, - ConstantIntData, DivisionData, EqualsData, HigherData, HigherEqualsData, LowerData, - LowerEqualsData, ModuloData, MultiplicationData, Node, NodeData, NotEqualsData, OrData, - PhiData, ProjectionData, ProjectionInformation, ReturnData, ShiftLeftData, ShiftRightData, - StartData, SubtractionData, XorData, + unary_operation::UnaryOperationData, ConstantBoolData, ConstantIntData, Node, PhiData, + ReturnData, }, - optimizer::Optimizer, }; pub struct IRGraphConstructor { - optimizer: Optimizer, graph: IRGraph, current_definitions: HashMap>, incomplete_phis: HashMap>, current_side_effect: HashMap, incomplete_side_effect_phis: HashMap, sealed_blocks: Vec, - loop_starts: Vec, - loop_ends: Vec, - current_block: usize, - next_block_number: usize, + current_block_index: BlockIndex, } impl IRGraphConstructor { pub fn new() -> IRGraphConstructor { IRGraphConstructor { - optimizer: Optimizer::new(), graph: IRGraph::new(), current_definitions: HashMap::new(), incomplete_phis: HashMap::new(), @@ -47,27 +46,36 @@ impl IRGraphConstructor { incomplete_side_effect_phis: HashMap::new(), // Start Block never gets more predecessors sealed_blocks: vec![0], - loop_starts: Vec::new(), - loop_ends: Vec::new(), - current_block: 0, - next_block_number: 2, + current_block_index: START_BLOCK, } } pub fn convert(&mut self, tree: Tree) -> Option { debug!("Converting AST {} to IR!", tree); match tree { - Tree::Program(functions) => { - for function in functions { - self.convert(function); - } - None + Tree::Program(_) => { + unimplemented!("Program Trees cannot be parsed to a single IR Representation!") } Tree::Function(_, _, body) => { - let start = self.create_start_block(); - let side_effect_projection = self.create_side_effect_projection(start); + let mut function_body_block = Block::new("fn-body".to_string()); + // Function body can be entered from start function block + function_body_block.register_entry_point(START_BLOCK, 0); + let side_effect_projection = function_body_block.register_node(Node::Projection( + ProjectionData::new(0, ProjectionInformation::SideEffect), + )); + self.current_block_index = self.graph.register_block(function_body_block); + self.write_current_side_effect(side_effect_projection); self.convert_boxed(body); + + // The last statement after parsing the body can exit the function + let last_statement_index = self + .graph + .get_block(self.current_block_index) + .get_last_node_index(); + self.graph + .end_block_mut() + .register_entry_point(self.current_block_index, last_statement_index); None } Tree::Assignment(lvalue, operator, expression) => match *lvalue { @@ -77,34 +85,39 @@ impl IRGraphConstructor { if let Token::Operator(_, operator_type) = operator { match operator_type { OperatorType::AssignMinus => { - let lhs = self.read_variable(name.clone(), self.current_block); + let lhs = + self.read_variable(name.clone(), self.current_block_index); let desugar = self.create_sub(lhs, rhs); - self.write_variable(name, self.current_block, desugar); + self.write_variable(name, self.current_block_index, desugar); } OperatorType::AssignPlus => { - let lhs = self.read_variable(name.clone(), self.current_block); + let lhs = + self.read_variable(name.clone(), self.current_block_index); let desugar = self.create_add(lhs, rhs); - self.write_variable(name, self.current_block, desugar); + self.write_variable(name, self.current_block_index, desugar); } OperatorType::AssignMul => { - let lhs = self.read_variable(name.clone(), self.current_block); + let lhs = + self.read_variable(name.clone(), self.current_block_index); let desugar = self.create_mul(lhs, rhs); - self.write_variable(name, self.current_block, desugar); + self.write_variable(name, self.current_block_index, desugar); } OperatorType::AssignDiv => { - let lhs = self.read_variable(name.clone(), self.current_block); + let lhs = + self.read_variable(name.clone(), self.current_block_index); let div = self.create_div(lhs, rhs); let desugar = self.create_div_mod_projection(div); - self.write_variable(name, self.current_block, desugar); + self.write_variable(name, self.current_block_index, desugar); } OperatorType::AssignMod => { - let lhs = self.read_variable(name.clone(), self.current_block); + let lhs = + self.read_variable(name.clone(), self.current_block_index); let mod_node = self.create_mod(lhs, rhs); let desugar = self.create_div_mod_projection(mod_node); - self.write_variable(name, self.current_block, desugar); + self.write_variable(name, self.current_block_index, desugar); } OperatorType::Assign => { - self.write_variable(name, self.current_block, rhs); + self.write_variable(name, self.current_block_index, rhs); } _ => panic!("Assignment has no assignment operator"), }; @@ -180,7 +193,7 @@ impl IRGraphConstructor { if let Tree::Name(name, _) = *identifier { if initializer.is_some() { let rhs = self.convert_boxed(initializer.unwrap()).unwrap(); - self.write_variable(name, self.current_block, rhs); + self.write_variable(name, self.current_block_index, rhs); } None } else { @@ -189,7 +202,7 @@ impl IRGraphConstructor { } Tree::IdentifierExpression(identifier) => { if let Tree::Name(name, _) = *identifier { - let value = self.read_variable(name, self.current_block); + let value = self.read_variable(name, self.current_block_index); Some(value) } else { panic!("Identifier expression did not have name as identifier!") @@ -223,11 +236,7 @@ impl IRGraphConstructor { }, Tree::Return(expression, _) => { let node = self.convert_boxed(expression).unwrap(); - let return_node = self.create_return(node); - self.graph - .end_block_mut() - .predecessors_mut() - .push(return_node); + self.create_return(node); None } Tree::Type(_, _) => None, @@ -255,140 +264,44 @@ impl IRGraphConstructor { let true_projection = self.create_true_projection(conditional_jump); let false_projection = self.create_false_projection(conditional_jump); - let false_block = self.create_block("ternary-false".to_string()); - self.graph - .get_node_mut(false_block) - .predecessors_mut() - .push(false_projection); + let mut false_block = Block::new("ternary-false".to_string()); + false_block.register_entry_point(self.current_block_index, false_projection); + let mut true_block = Block::new("ternary-true".to_string()); + true_block.register_entry_point(self.current_block_index, true_projection); + self.seal_block(self.current_block_index); - let true_block = self.create_block("ternary-true".to_string()); - self.graph - .get_node_mut(true_block) - .predecessors_mut() - .push(true_projection); - self.seal_block(self.current_block); - - self.current_block = self.graph.get_node(false_block).block(); + let false_block_index = self.graph.register_block(false_block); + self.current_block_index = false_block_index; let false_expression = self.convert_boxed(false_value)?; - self.graph - .get_node_mut(false_expression) - .predecessors_mut() - .push(false_block); let false_jump = self.create_jump(); - self.graph - .get_node_mut(false_jump) - .predecessors_mut() - .push(false_expression); - self.current_block = self.graph.get_node(true_block).block(); + let true_block_index = self.graph.register_block(true_block); + self.current_block_index = true_block_index; let true_expression = self.convert_boxed(true_value)?; - self.graph - .get_node_mut(true_expression) - .predecessors_mut() - .push(true_block); let true_jump = self.create_jump(); - self.graph - .get_node_mut(true_jump) - .predecessors_mut() - .push(true_expression); - let following_block = self.create_block("ternary-following".to_string()); - self.graph - .get_node_mut(following_block) - .predecessors_mut() - .push(true_jump); - self.graph - .get_node_mut(following_block) - .predecessors_mut() - .push(false_jump); - self.current_block = self.graph.get_node(following_block).block(); - self.seal_block(true_block); - self.seal_block(false_block); - let phi = self.create_phi(); - self.graph - .get_node_mut(phi) - .predecessors_mut() - .push(true_expression); - self.graph - .get_node_mut(phi) - .predecessors_mut() - .push(false_expression); - self.graph - .get_node_mut(phi) - .predecessors_mut() - .push(following_block); + let mut following_block = Block::new("ternary-following".to_string()); + following_block.register_entry_point(false_block_index, false_jump); + following_block.register_entry_point(true_block_index, true_jump); + self.current_block_index = self.graph.register_block(following_block); + self.seal_block(true_block_index); + self.seal_block(false_block_index); + let phi = self.create_phi_from_operands(vec![ + (false_block_index, false_expression), + (true_block_index, true_expression), + ]); Some(phi) } - Tree::While(condition, expression, _) => { - self.seal_block(self.current_block); - - let while_block = self.create_block("while".to_string()); - self.graph - .get_node_mut(while_block) - .predecessors_mut() - .push(self.current_block); - self.current_block = while_block; - self.loop_starts.push(while_block); - - let condition_block = self.convert_boxed(condition)?; - - let conditional_jump = self.create_conditional_jump(condition_block); - let true_block = self.create_true_projection(conditional_jump); - let false_block = self.create_false_projection(conditional_jump); - - let follow_block = self.create_block("while-follow".to_string()); - self.graph - .get_node_mut(follow_block) - .predecessors_mut() - .push(false_block); - self.loop_ends.push(follow_block); - - let body_block = self.create_block("while-body".to_string()); - self.graph - .get_node_mut(body_block) - .predecessors_mut() - .push(true_block); - self.seal_block(body_block); - self.current_block = body_block; - self.convert_boxed(expression); - let continue_jump = self.create_jump(); - self.seal_block(self.current_block); - self.graph - .get_node_mut(while_block) - .predecessors_mut() - .push(continue_jump); - - self.loop_starts.pop(); - self.seal_block(while_block); - self.loop_ends.pop(); - self.seal_block(follow_block); - self.current_block = follow_block; - None + Tree::While(_condition, _expression, _) => { + todo!("Implement while") } - Tree::If(condition, body, else_body, _) => { - let condition_block = self.convert_boxed(condition)?; - let conditional_jump = self.create_conditional_jump(condition_block); - let true_projection = self.create_true_projection(conditional_jump); - let false_projection = self.create_false_projection(conditional_jump); - self.seal_block(self.current_block); - - let true_block = self.process_branch(body, true_projection, "true"); - let false_block = if let Some(else_body_ab) = else_body { - Some(self.process_branch(else_body_ab, false_projection, "false")) - } else { - None - }; - - let following_block = self.create_block("following-if".to_string()); - self.current_block = following_block; - let following_node = self.graph.get_node_mut(following_block); - following_node.predecessors_mut().push(true_block); - if let Some(false_block_index) = false_block { - following_node.predecessors_mut().push(false_block_index); - } - self.seal_block(following_block); - None + Tree::If(_condition, _body, _else_body, _) => { + todo!("Implement if") + } + Tree::For(_option_initializer, _comparison, _option_postincrement, _statement, _) => { + todo!("Implement for") } + #[allow(unreachable_patterns)] node => todo!("Unimplemented {:?}", node), } } @@ -397,282 +310,226 @@ impl IRGraphConstructor { self.convert(*tree) } - pub fn create_conditional_jump(&mut self, condition: usize) -> usize { - self.graph - .register_node(Node::ConditionalJump(ConditionalJumpData::new( - self.current_block, - condition, - ))) + pub fn create_conditional_jump(&mut self, condition: NodeIndex) -> NodeIndex { + let current_block = self.graph.get_block_mut(self.current_block_index); + current_block.register_node(Node::ConditionalJump(UnaryOperationData::new(condition))) } - pub fn process_branch(&mut self, a: Box, b: usize, label: &str) -> usize { - let block = self.create_block(format!("if-body-{}", label)); - self.current_block = block; - self.graph.get_node_mut(block).predecessors_mut().push(b); - self.seal_block(block); + // TODO: Refactor + pub fn process_branch(&mut self, a: Box, _b: usize, label: &str) -> usize { + let block = Block::new(format!("if-body-{}", label)); + self.current_block_index = self.graph.register_block(block); + //self.graph.get_node_mut(block).predecessors_mut().push(b); + self.seal_block(self.current_block_index); self.convert_boxed(a); - self.seal_block(self.current_block); + self.seal_block(self.current_block_index); self.create_jump() } - pub fn create_block(&mut self, name: String) -> usize { - let result = self - .graph - .register_node(Node::Block(BlockData::new(self.next_block_number))); - self.next_block_number += 1; - result + + fn create_jump(&mut self) -> NodeIndex { + let current_block = self.graph.get_block_mut(self.current_block_index); + current_block.register_node(Node::Jump) } - pub fn create_jump(&mut self) -> usize { - self.graph - .register_node(Node::Jump(JumpData::new(self.current_block))) + fn create_add(&mut self, lhs: NodeIndex, rhs: NodeIndex) -> NodeIndex { + let current_block = self.graph.get_block_mut(self.current_block_index); + current_block.register_node(Node::Add(BinaryOperationData::new(lhs, rhs))) } - fn create_start_block(&mut self) -> usize { - self.graph - .register_node(Node::Start(StartData::new(self.current_block))) + fn create_sub(&mut self, lhs: NodeIndex, rhs: NodeIndex) -> NodeIndex { + let current_block = self.graph.get_block_mut(self.current_block_index); + current_block.register_node(Node::Subtraction(BinaryOperationData::new(lhs, rhs))) } - fn create_noop(&mut self) -> usize { - self.graph - .register_node(Node::NoOp(NodeData::new(self.current_block, vec![]))) + fn create_mul(&mut self, lhs: NodeIndex, rhs: NodeIndex) -> NodeIndex { + let current_block = self.graph.get_block_mut(self.current_block_index); + current_block.register_node(Node::Multiplication(BinaryOperationData::new(lhs, rhs))) } - fn create_add(&mut self, left: usize, right: usize) -> usize { - self.graph - .register_node(self.optimizer.transform(Node::Add(AddData::new( - self.current_block, - left, - right, - )))) + fn create_div(&mut self, lhs: NodeIndex, rhs: NodeIndex) -> NodeIndex { + let sideeffect = self.read_current_side_effect(); + let current_block = self.graph.get_block_mut(self.current_block_index); + current_block.register_node(Node::Division(BinaryOperationData::new_with_sideeffect( + lhs, rhs, sideeffect, + ))) } - fn create_sub(&mut self, left: usize, right: usize) -> usize { - self.graph.register_node( - self.optimizer - .transform(Node::Subtraction(SubtractionData::new( - self.current_block, - left, - right, - ))), - ) + fn create_mod(&mut self, lhs: NodeIndex, rhs: NodeIndex) -> NodeIndex { + let sideeffect = self.read_current_side_effect(); + let current_block = self.graph.get_block_mut(self.current_block_index); + current_block.register_node(Node::Modulo(BinaryOperationData::new_with_sideeffect( + lhs, rhs, sideeffect, + ))) } - fn create_bitwise_not(&mut self, node: usize) -> usize { - self.graph - .register_node( - self.optimizer - .transform(Node::BitwiseNegate(BitwiseNegateData::new( - self.current_block, - node, - ))), - ) + fn create_shift_left(&mut self, lhs: NodeIndex, rhs: NodeIndex) -> NodeIndex { + let sideeffect = self.read_current_side_effect(); + let current_block = self.graph.get_block_mut(self.current_block_index); + current_block.register_node(Node::ShiftLeft(BinaryOperationData::new_with_sideeffect( + lhs, rhs, sideeffect, + ))) } - fn create_mul(&mut self, left: usize, right: usize) -> usize { - self.graph - .register_node( - self.optimizer - .transform(Node::Multiplication(MultiplicationData::new( - self.current_block, - left, - right, - ))), - ) - } - - fn create_div(&mut self, left: usize, right: usize) -> usize { - let current_side_effect = self.read_current_side_effect(); - self.graph - .register_node(self.optimizer.transform(Node::Division(DivisionData::new( - self.current_block, - left, - right, - current_side_effect, - )))) + fn create_shift_right(&mut self, lhs: NodeIndex, rhs: NodeIndex) -> NodeIndex { + let sideeffect = self.read_current_side_effect(); + let current_block = self.graph.get_block_mut(self.current_block_index); + current_block.register_node(Node::ShiftRight(BinaryOperationData::new_with_sideeffect( + lhs, rhs, sideeffect, + ))) } - fn create_mod(&mut self, left: usize, right: usize) -> usize { - let current_side_effect = self.read_current_side_effect(); - self.graph - .register_node(self.optimizer.transform(Node::Modulo(ModuloData::new( - self.current_block, - left, - right, - current_side_effect, - )))) - } - - fn create_shift_right(&mut self, left: usize, right: usize) -> usize { - self.graph.register_node( - self.optimizer - .transform(Node::ShiftRight(ShiftRightData::new( - self.current_block, - left, - right, - ))), - ) - } - - fn create_shift_left(&mut self, left: usize, right: usize) -> usize { - self.graph - .register_node(self.optimizer.transform(Node::ShiftLeft(ShiftLeftData::new( - self.current_block, - left, - right, - )))) + fn create_lower(&mut self, lhs: NodeIndex, rhs: NodeIndex) -> NodeIndex { + let sideeffect = self.read_current_side_effect(); + let current_block = self.graph.get_block_mut(self.current_block_index); + current_block.register_node(Node::Lower(BinaryOperationData::new_with_sideeffect( + lhs, rhs, sideeffect, + ))) } - fn create_or(&mut self, left: usize, right: usize) -> usize { - self.graph - .register_node(self.optimizer.transform(Node::Or(OrData::new( - self.current_block, - left, - right, - )))) + + fn create_lower_equals(&mut self, lhs: NodeIndex, rhs: NodeIndex) -> NodeIndex { + let sideeffect = self.read_current_side_effect(); + let current_block = self.graph.get_block_mut(self.current_block_index); + current_block.register_node(Node::LowerEquals(BinaryOperationData::new_with_sideeffect( + lhs, rhs, sideeffect, + ))) } - fn create_and(&mut self, left: usize, right: usize) -> usize { - self.graph - .register_node(self.optimizer.transform(Node::And(AndData::new( - self.current_block, - left, - right, - )))) + + fn create_equals(&mut self, lhs: NodeIndex, rhs: NodeIndex) -> NodeIndex { + let sideeffect = self.read_current_side_effect(); + let current_block = self.graph.get_block_mut(self.current_block_index); + current_block.register_node(Node::Equals(BinaryOperationData::new_with_sideeffect( + lhs, rhs, sideeffect, + ))) } - fn create_xor(&mut self, left: usize, right: usize) -> usize { - self.graph - .register_node(self.optimizer.transform(Node::Xor(XorData::new( - self.current_block, - left, - right, - )))) + + fn create_not_equals(&mut self, lhs: NodeIndex, rhs: NodeIndex) -> NodeIndex { + let sideeffect = self.read_current_side_effect(); + let current_block = self.graph.get_block_mut(self.current_block_index); + current_block.register_node(Node::Lower(BinaryOperationData::new_with_sideeffect( + lhs, rhs, sideeffect, + ))) } - fn create_lower(&mut self, left: usize, right: usize) -> usize { - self.graph - .register_node(self.optimizer.transform(Node::Lower(LowerData::new( - self.current_block, - left, - right, - )))) - } - fn create_lower_equals(&mut self, left: usize, right: usize) -> usize { - self.graph.register_node( - self.optimizer - .transform(Node::LowerEquals(LowerEqualsData::new( - self.current_block, - left, - right, - ))), - ) - } - fn create_equals(&mut self, left: usize, right: usize) -> usize { - self.graph - .register_node(self.optimizer.transform(Node::Equals(EqualsData::new( - self.current_block, - left, - right, - )))) + + fn create_higher_equals(&mut self, lhs: NodeIndex, rhs: NodeIndex) -> NodeIndex { + let sideeffect = self.read_current_side_effect(); + let current_block = self.graph.get_block_mut(self.current_block_index); + current_block.register_node(Node::HigherEquals( + BinaryOperationData::new_with_sideeffect(lhs, rhs, sideeffect), + )) } - fn create_not_equals(&mut self, left: usize, right: usize) -> usize { - self.graph - .register_node(self.optimizer.transform(Node::NotEquals(NotEqualsData::new( - self.current_block, - left, - right, - )))) + + fn create_higher(&mut self, lhs: NodeIndex, rhs: NodeIndex) -> NodeIndex { + let sideeffect = self.read_current_side_effect(); + let current_block = self.graph.get_block_mut(self.current_block_index); + current_block.register_node(Node::Higher(BinaryOperationData::new_with_sideeffect( + lhs, rhs, sideeffect, + ))) } - fn create_higher_equals(&mut self, left: usize, right: usize) -> usize { - self.graph - .register_node( - self.optimizer - .transform(Node::HigherEquals(HigherEqualsData::new( - self.current_block, - left, - right, - ))), - ) - } - fn create_higher(&mut self, left: usize, right: usize) -> usize { - self.graph - .register_node(self.optimizer.transform(Node::Higher(HigherData::new( - self.current_block, - left, - right, - )))) - } - - fn create_return(&mut self, result: usize) -> usize { - let current_side_effect = self.read_current_side_effect(); - self.graph.register_node(Node::Return(ReturnData::new( - self.current_block, - result, - current_side_effect, + + fn create_or(&mut self, lhs: NodeIndex, rhs: NodeIndex) -> NodeIndex { + let sideeffect = self.read_current_side_effect(); + let current_block = self.graph.get_block_mut(self.current_block_index); + current_block.register_node(Node::Or(BinaryOperationData::new_with_sideeffect( + lhs, rhs, sideeffect, ))) } - fn create_constant_int(&mut self, value: i32) -> usize { - self.graph.register_node( - self.optimizer - .transform(Node::ConstantInt(ConstantIntData::new( - self.current_block, - value, - ))), - ) + fn create_and(&mut self, lhs: NodeIndex, rhs: NodeIndex) -> NodeIndex { + let sideeffect = self.read_current_side_effect(); + let current_block = self.graph.get_block_mut(self.current_block_index); + current_block.register_node(Node::And(BinaryOperationData::new_with_sideeffect( + lhs, rhs, sideeffect, + ))) } - fn create_side_effect_projection(&mut self, node: usize) -> usize { - self.graph - .register_node(Node::Projection(ProjectionData::new( - self.current_block, - node, - ProjectionInformation::SideEffect, - ))) + fn create_xor(&mut self, lhs: NodeIndex, rhs: NodeIndex) -> NodeIndex { + let sideeffect = self.read_current_side_effect(); + let current_block = self.graph.get_block_mut(self.current_block_index); + current_block.register_node(Node::Xor(BinaryOperationData::new_with_sideeffect( + lhs, rhs, sideeffect, + ))) + } + + fn create_bitwise_not(&mut self, node: NodeIndex) -> NodeIndex { + let sideeffect = self.read_current_side_effect(); + let current_block = self.graph.get_block_mut(self.current_block_index); + current_block.register_node(Node::BitwiseNegate( + UnaryOperationData::new_with_sideeffect(node, sideeffect), + )) + } + + fn create_constant_int(&mut self, value: i32) -> NodeIndex { + let current_block = self.graph.get_block_mut(self.current_block_index); + current_block.register_node(Node::ConstantInt(ConstantIntData::new(value))) + } + + fn create_constant_bool(&mut self, value: bool) -> NodeIndex { + let current_block = self.graph.get_block_mut(self.current_block_index); + current_block.register_node(Node::ConstantBool(ConstantBoolData::new(value))) } - fn create_result_projection(&mut self, node: usize) -> usize { + fn create_return(&mut self, input: NodeIndex) -> NodeIndex { + let current_block = self.graph.get_block_mut(self.current_block_index); + let return_node_index = current_block.register_node(Node::Return(ReturnData::new(input))); self.graph - .register_node(Node::Projection(ProjectionData::new( - self.current_block, - node, - ProjectionInformation::Result, - ))) + .end_block_mut() + .register_entry_point(self.current_block_index, return_node_index); + return_node_index } fn create_phi(&mut self) -> usize { - self.graph - .register_node(Node::Phi(PhiData::new(self.current_block))) + let current_block = self.graph.get_block_mut(self.current_block_index); + current_block.register_node(Node::Phi(PhiData::empty())) } - fn create_phi_operands(&mut self, block: usize) -> usize { - let mut operands = Vec::new(); - for operand in self.graph.get_predecessors(block).clone() { - operands.push(self.read_side_effect(operand)); - } - self.graph - .register_node(Node::Phi(PhiData::new_with_operands( - self.current_block, - operands, - ))) + fn create_phi_from_operands(&mut self, operands: Vec<(BlockIndex, NodeIndex)>) -> usize { + let current_block = self.graph.get_block_mut(self.current_block_index); + current_block.register_node(Node::Phi(PhiData::new(operands))) } - fn create_phi_variable_operands(&mut self, block: usize, variable: Name) -> usize { - let mut operands = Vec::new(); - for operand in self.graph.get_predecessors(block).clone() { - let block = self.graph.get_node(operand).block(); - operands.push(self.read_variable(variable.clone(), block)); - } - self.graph - .register_node(Node::Phi(PhiData::new_with_operands( - self.current_block, - operands, - ))) + fn create_phi_operands(&mut self, block_index: BlockIndex) -> NodeIndex { + let block = self.graph.get_block(block_index); + let operands = block + .entry_points() + .iter() + .map(|(v1, v2)| (*v1, *v2)) + .collect(); + trace!("Creating phi with operands {:?}", operands); + let current_block = self.graph.get_block_mut(self.current_block_index); + current_block.register_node(Node::Phi(PhiData::new(operands))) } - fn create_div_mod_projection(&mut self, node: usize) -> usize { - let projection_side_effect = self.create_side_effect_projection(node); + fn create_phi_variable_operands( + &mut self, + block_index: BlockIndex, + variable: Name, + ) -> NodeIndex { + let mut operands = Vec::new(); + for (block_index, _) in self.graph.get_block(block_index).entry_points().clone() { + operands.push(( + block_index, + self.read_variable(variable.clone(), block_index), + )); + } + let current_block = self.graph.get_block_mut(self.current_block_index); + current_block.register_node(Node::Phi(PhiData::new(operands))) + } + + fn create_div_mod_projection(&mut self, input: NodeIndex) -> NodeIndex { + let current_block = self.graph.get_block_mut(self.current_block_index); + let projection_side_effect = current_block.register_node(Node::Projection( + ProjectionData::new(input, ProjectionInformation::SideEffect), + )); + let result_projection = current_block.register_node(Node::Projection(ProjectionData::new( + input, + ProjectionInformation::Result, + ))); self.write_current_side_effect(projection_side_effect); - self.create_result_projection(node) + result_projection } fn write_variable(&mut self, variable: Name, block: usize, node: usize) { + trace!("Trying to write into variable {:?}", variable); match self.current_definitions.contains_key(&variable) { true => { self.current_definitions @@ -688,56 +545,86 @@ impl IRGraphConstructor { } fn read_variable(&mut self, variable: Name, block: usize) -> usize { + trace!("Trying to read from variable {:?}", variable); if self.current_definitions.contains_key(&variable) { - *self + if self .current_definitions .get(&variable) .unwrap() - .get(&block) - .unwrap() + .contains_key(&block) + { + *self + .current_definitions + .get(&variable) + .unwrap() + .get(&block) + .unwrap() + } else { + self.read_variable_recursive(variable, block) + } } else { self.read_variable_recursive(variable, block) } } - fn read_variable_recursive(&mut self, variable: Name, block: usize) -> usize { - let node = if !self.sealed_blocks.contains(&block) { + fn read_variable_recursive(&mut self, variable: Name, block_index: BlockIndex) -> usize { + let node = if !self.sealed_blocks.contains(&block_index) { let phi = self.create_phi(); - if let std::collections::hash_map::Entry::Vacant(e) = self.incomplete_phis.entry(block) + trace!("Writing incomplete phi: ({:?}, {})", variable, phi); + if let std::collections::hash_map::Entry::Vacant(e) = + self.incomplete_phis.entry(block_index) { e.insert(HashMap::from([(variable.clone(), phi)])); } else { self.incomplete_phis - .get_mut(&block) + .get_mut(&block_index) .unwrap() .insert(variable.clone(), phi); } phi - } else if self.graph.get_predecessors(block).len() == 1 { - self.read_variable( - variable.clone(), - *self.graph.get_predecessors(block).first().unwrap(), - ) + } else if self.graph.get_block(block_index).entry_points().len() == 1 { + let (previous_block, _) = self + .graph + .get_block(block_index) + .entry_points() + .iter() + .last() + .unwrap(); + self.read_variable(variable.clone(), *previous_block) } else { - let phi = self.create_phi_variable_operands(block, variable.clone()); - self.write_variable(variable.clone(), block, phi); + let phi = self.create_phi_variable_operands(block_index, variable.clone()); + self.write_variable(variable.clone(), block_index, phi); phi }; - self.write_variable(variable.clone(), block, node); + self.write_variable(variable.clone(), block_index, node); node } - fn seal_block(&mut self, block: usize) { + fn seal_block(&mut self, block: BlockIndex) { + trace!("Current graph before sealing block: {}", self.graph); + trace!("Incomplete Phis: {:?}", self.incomplete_phis); if !self.incomplete_phis.contains_key(&block) { self.sealed_blocks.push(block); return; } for (variable, index) in self.incomplete_phis.get(&block).unwrap().clone() { - let phi = self.graph.remove_node(index); - if let Node::Phi(mut data) = phi { - for predecessor in data.node_data().predecessors().clone() { - let block = self.graph.get_node(predecessor).block(); - data.add_operand(self.read_variable(variable.clone(), block)); + let operands = { + let mut operands = Vec::new(); + let block = self.graph.get_block_mut(block); + if let Node::Phi(data) = block.get_node_mut(index) { + for (block_index, _) in data.operands() { + operands.push(( + block_index, + self.read_variable(variable.clone(), block_index), + )); + } + } + operands + }; + let block = self.graph.get_block_mut(block); + if let Node::Phi(data) = block.get_node_mut(index) { + for operand in operands { + data.add_operand(operand); } } } @@ -745,7 +632,7 @@ impl IRGraphConstructor { } fn write_current_side_effect(&mut self, node: usize) { - self.write_side_effect(self.current_block, node); + self.write_side_effect(self.current_block_index, node); } fn write_side_effect(&mut self, block: usize, node: usize) { @@ -753,7 +640,7 @@ impl IRGraphConstructor { } fn read_current_side_effect(&mut self) -> usize { - self.read_side_effect(self.current_block) + self.read_side_effect(self.current_block_index) } fn read_side_effect(&mut self, block: usize) -> usize { @@ -772,8 +659,15 @@ impl IRGraphConstructor { panic!("Double read side effect recursive!"); } phi - } else if self.graph.get_predecessors(block).len() == 1 { - self.read_side_effect(*self.graph.get_predecessors(block).first().unwrap()) + } else if self.graph.get_block(block).entry_points().len() == 1 { + let (previous_block, _) = self + .graph + .get_block(block) + .entry_points() + .iter() + .last() + .unwrap(); + self.read_side_effect(*previous_block) } else { let phi = self.create_phi_operands(block); self.write_side_effect(block, phi); @@ -787,29 +681,20 @@ impl IRGraphConstructor { self.graph } - pub fn create_constant_bool(&mut self, value: bool) -> usize { - self.graph - .register_node(Node::ConstantBool(ConstantBoolData::new( - self.current_block, - value, - ))) - } - pub fn create_true_projection(&mut self, block: usize) -> usize { - self.graph - .register_node(Node::Projection(ProjectionData::new( - self.current_block, - block, - ProjectionInformation::IfTrue, - ))) + pub fn create_true_projection(&mut self, conditional_jump: NodeIndex) -> NodeIndex { + let current_block = self.graph.get_block_mut(self.current_block_index); + current_block.register_node(Node::Projection(ProjectionData::new( + conditional_jump, + ProjectionInformation::IfTrue, + ))) } - pub fn create_false_projection(&mut self, block: usize) -> usize { - self.graph - .register_node(Node::Projection(ProjectionData::new( - self.current_block, - block, - ProjectionInformation::IfFalse, - ))) + pub fn create_false_projection(&mut self, conditional_jump: NodeIndex) -> NodeIndex { + let current_block = self.graph.get_block_mut(self.current_block_index); + current_block.register_node(Node::Projection(ProjectionData::new( + conditional_jump, + ProjectionInformation::IfFalse, + ))) } } diff --git a/src/ir/graph.rs b/src/ir/graph.rs index e1fc899..13f5131 100644 --- a/src/ir/graph.rs +++ b/src/ir/graph.rs @@ -1,66 +1,63 @@ -use std::{collections::HashMap, fmt::Display}; +use std::fmt::Display; -use super::node::{BlockData, Node}; +use super::block::Block; + +pub type BlockIndex = usize; pub const START_BLOCK: usize = 0; pub const END_BLOCK: usize = 1; pub struct IRGraph { - nodes: HashMap, - next_node_index: usize, + blocks: Vec, } impl IRGraph { pub fn new() -> IRGraph { IRGraph { - nodes: HashMap::from([ - (START_BLOCK, Node::Block(BlockData::new(START_BLOCK))), - (END_BLOCK, Node::Block(BlockData::new(END_BLOCK))), - ]), - next_node_index: 2, + blocks: vec![ + Block::new("start".to_string()), + Block::new("end".to_string()), + ], } } - pub fn register_node(&mut self, node: Node) -> usize { - self.nodes.insert(self.next_node_index, node); - self.next_node_index += 1; - self.next_node_index - 1 + pub fn start_block(&self) -> &Block { + self.blocks.get(START_BLOCK).expect("Start Block missing!") } - pub fn remove_node(&mut self, index: usize) -> Node { - self.nodes - .remove(&index) - .expect("Cannot remove node at index") + pub fn start_block_mut(&mut self) -> &mut Block { + self.blocks + .get_mut(START_BLOCK) + .expect("Start Block missing!") } - pub fn get_node(&self, index: usize) -> &Node { - self.nodes.get(&index).expect("Cannot find node at index") + pub fn register_block(&mut self, block: Block) -> BlockIndex { + self.blocks.push(block); + self.blocks.len() - 1 } - pub fn get_node_mut(&mut self, index: usize) -> &mut Node { - self.nodes - .get_mut(&index) - .expect("Cannot find node at index") + pub fn get_block(&self, block_index: BlockIndex) -> &Block { + self.blocks + .get(block_index) + .expect("Expected block at block index") } - pub fn get_predecessors(&self, index: usize) -> &Vec { - let node = self - .nodes - .get(&index) - .expect("Cannot find node for predecessors"); - node.predecessors() + pub fn get_block_mut(&mut self, block_index: BlockIndex) -> &mut Block { + self.blocks + .get_mut(block_index) + .expect("Expected block at block index") } - pub fn start_block(&self) -> &Node { - self.nodes.get(&START_BLOCK).expect("Start Block missing!") + pub fn get_blocks(&self) -> &Vec { + &self.blocks } - pub fn end_block(&self) -> &Node { - self.nodes.get(&END_BLOCK).expect("End Block missing!") + pub fn end_block(&self) -> &Block { + self.blocks.get(END_BLOCK).expect("End Block missing!") } - pub fn end_block_mut(&mut self) -> &mut Node { - self.nodes.get_mut(&END_BLOCK).expect("End Block missing!") + pub fn end_block_mut(&mut self) -> &mut Block { + self.blocks.get_mut(END_BLOCK).expect("End Block missing!") } } @@ -72,17 +69,9 @@ impl Default for IRGraph { impl Display for IRGraph { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - writeln!(f, "START: {}", self.get_node(START_BLOCK))?; - for i in END_BLOCK + 1..self.next_node_index { - writeln!( - f, - "{}-{}: {}", - self.get_node(i).block(), - i, - self.get_node(i) - )? + for block in &self.blocks { + writeln!(f, "{}", block)?; } - writeln!(f, "END: {}", self.get_node(END_BLOCK))?; Ok(()) } } diff --git a/src/ir/mod.rs b/src/ir/mod.rs index c7cdd6d..57f0874 100644 --- a/src/ir/mod.rs +++ b/src/ir/mod.rs @@ -1,4 +1,4 @@ +pub mod block; pub mod constructor; pub mod graph; pub mod node; -mod optimizer; diff --git a/src/ir/node.rs b/src/ir/node.rs deleted file mode 100644 index f93235e..0000000 --- a/src/ir/node.rs +++ /dev/null @@ -1,819 +0,0 @@ -use std::fmt::Display; - -use crate::lexer::token::OperatorType; - -#[derive(Eq, Hash, PartialEq, Debug)] -pub enum Node { - Add(AddData), - Block(BlockData), - ConstantInt(ConstantIntData), - ConstantBool(ConstantBoolData), - Division(DivisionData), - Modulo(ModuloData), - Multiplication(MultiplicationData), - Phi(PhiData), - Projection(ProjectionData), - Return(ReturnData), - Start(StartData), - Subtraction(SubtractionData), - ShiftLeft(ShiftLeftData), - ShiftRight(ShiftRightData), - Lower(LowerData), - LowerEquals(LowerEqualsData), - Equals(EqualsData), - NotEquals(NotEqualsData), - HigherEquals(HigherEqualsData), - Higher(HigherData), - BitwiseNegate(BitwiseNegateData), - Or(OrData), - And(AndData), - Xor(XorData), - NoOp(NodeData), - ConditionalJump(ConditionalJumpData), - Jump(JumpData), -} - -impl Node { - pub fn predecessors(&self) -> &Vec { - match self { - Node::Add(data) => data.binary_operation_data.node_data.predecessors(), - Node::Block(data) => data.node_data.predecessors(), - Node::ConstantInt(data) => data.node_data.predecessors(), - Node::ConstantBool(data) => data.node_data.predecessors(), - Node::Division(data) => data.binary_operation_data.node_data.predecessors(), - Node::Modulo(data) => data.binary_operation_data.node_data.predecessors(), - Node::Multiplication(data) => data.binary_operation_data.node_data.predecessors(), - Node::Phi(data) => data.node_data.predecessors(), - Node::Projection(data) => data.node_data.predecessors(), - Node::Return(data) => data.node_data.predecessors(), - Node::Start(data) => data.node_data.predecessors(), - Node::Subtraction(data) => data.binary_operation_data.node_data.predecessors(), - Node::ShiftLeft(data) => data.binary_operation_data.node_data.predecessors(), - Node::ShiftRight(data) => data.binary_operation_data.node_data.predecessors(), - Node::BitwiseNegate(data) => data.node_data.predecessors(), - Node::Lower(data) => data.binary_operation_data.node_data.predecessors(), - Node::LowerEquals(data) => data.binary_operation_data.node_data.predecessors(), - Node::Equals(data) => data.binary_operation_data.node_data.predecessors(), - Node::NotEquals(data) => data.binary_operation_data.node_data.predecessors(), - Node::HigherEquals(data) => data.binary_operation_data.node_data.predecessors(), - Node::Higher(data) => data.binary_operation_data.node_data.predecessors(), - Node::And(data) => data.binary_operation_data.node_data.predecessors(), - Node::Or(data) => data.binary_operation_data.node_data.predecessors(), - Node::Xor(data) => data.binary_operation_data.node_data.predecessors(), - Node::NoOp(data) => data.predecessors(), - Node::ConditionalJump(data) => data.node_data.predecessors(), - Node::Jump(data) => data.node_data.predecessors(), - } - } - - pub fn predecessors_mut(&mut self) -> &mut Vec { - match self { - Node::Add(data) => data.binary_operation_data.node_data.predecessors_mut(), - Node::Block(data) => data.node_data.predecessors_mut(), - Node::ConstantInt(data) => data.node_data.predecessors_mut(), - Node::ConstantBool(data) => data.node_data.predecessors_mut(), - Node::Division(data) => data.binary_operation_data.node_data.predecessors_mut(), - Node::Modulo(data) => data.binary_operation_data.node_data.predecessors_mut(), - Node::Multiplication(data) => data.binary_operation_data.node_data.predecessors_mut(), - Node::Phi(data) => data.node_data.predecessors_mut(), - Node::Projection(data) => data.node_data.predecessors_mut(), - Node::Return(data) => data.node_data.predecessors_mut(), - Node::Start(data) => data.node_data.predecessors_mut(), - Node::Subtraction(data) => data.binary_operation_data.node_data.predecessors_mut(), - Node::ShiftLeft(data) => data.binary_operation_data.node_data.predecessors_mut(), - Node::ShiftRight(data) => data.binary_operation_data.node_data.predecessors_mut(), - Node::BitwiseNegate(data) => data.node_data.predecessors_mut(), - Node::Lower(data) => data.binary_operation_data.node_data.predecessors_mut(), - Node::LowerEquals(data) => data.binary_operation_data.node_data.predecessors_mut(), - Node::Equals(data) => data.binary_operation_data.node_data.predecessors_mut(), - Node::NotEquals(data) => data.binary_operation_data.node_data.predecessors_mut(), - Node::HigherEquals(data) => data.binary_operation_data.node_data.predecessors_mut(), - Node::Higher(data) => data.binary_operation_data.node_data.predecessors_mut(), - Node::And(data) => data.binary_operation_data.node_data.predecessors_mut(), - Node::Or(data) => data.binary_operation_data.node_data.predecessors_mut(), - Node::Xor(data) => data.binary_operation_data.node_data.predecessors_mut(), - Node::NoOp(data) => data.predecessors_mut(), - Node::ConditionalJump(data) => data.node_data.predecessors_mut(), - Node::Jump(data) => data.node_data.predecessors_mut(), - } - } - - pub fn block(&self) -> usize { - match self { - Node::Add(data) => data.binary_operation_data.node_data.block(), - Node::Block(data) => data.node_data.block(), - Node::ConstantInt(data) => data.node_data.block(), - Node::ConstantBool(data) => data.node_data.block(), - Node::Division(data) => data.binary_operation_data.node_data.block(), - Node::Modulo(data) => data.binary_operation_data.node_data.block(), - Node::Multiplication(data) => data.binary_operation_data.node_data.block(), - Node::Phi(data) => data.node_data.block(), - Node::Projection(data) => data.node_data.block(), - Node::Return(data) => data.node_data.block(), - Node::Start(data) => data.node_data.block(), - Node::Subtraction(data) => data.binary_operation_data.node_data.block(), - Node::ShiftLeft(data) => data.binary_operation_data.node_data.block(), - Node::ShiftRight(data) => data.binary_operation_data.node_data.block(), - Node::BitwiseNegate(data) => data.node_data.block(), - Node::Lower(data) => data.binary_operation_data.node_data.block(), - Node::LowerEquals(data) => data.binary_operation_data.node_data.block(), - Node::Equals(data) => data.binary_operation_data.node_data.block(), - Node::NotEquals(data) => data.binary_operation_data.node_data.block(), - Node::HigherEquals(data) => data.binary_operation_data.node_data.block(), - Node::Higher(data) => data.binary_operation_data.node_data.block(), - Node::And(data) => data.binary_operation_data.node_data.block(), - Node::Or(data) => data.binary_operation_data.node_data.block(), - Node::Xor(data) => data.binary_operation_data.node_data.block(), - Node::NoOp(data) => data.block(), - Node::ConditionalJump(data) => data.node_data.block(), - Node::Jump(data) => data.node_data.block(), - } - } -} - -#[derive(Eq, Hash, PartialEq, Debug)] -pub struct NodeData { - block_index: usize, - predecessors: Vec, -} - -impl NodeData { - pub fn new(block_index: usize, predecessors: Vec) -> NodeData { - NodeData { - block_index, - predecessors, - } - } - - pub fn block(&self) -> usize { - self.block_index - } - - pub fn predecessors(&self) -> &Vec { - &self.predecessors - } - - pub fn predecessors_mut(&mut self) -> &mut Vec { - &mut self.predecessors - } - - pub fn set_predecessor(&mut self, index: usize, node: usize) { - self.predecessors[index] = node; - } - - pub fn add_predecessor(&mut self, node: usize) { - self.predecessors.push(node); - } - - pub fn get_predecessor(&self, index: usize) -> Option { - self.predecessors.get(index).copied() - } -} - -#[derive(Eq, Hash, PartialEq, Debug)] -pub struct AddData { - binary_operation_data: BinaryOperationData, -} - -impl AddData { - pub fn new(block: usize, left: usize, right: usize) -> AddData { - AddData { - binary_operation_data: BinaryOperationData::new(block, left, right), - } - } - - pub fn binary_operation_data(&self) -> &BinaryOperationData { - &self.binary_operation_data - } -} - -pub const BINARY_OPERATION_LEFT: usize = 0; -pub const BINARY_OPERATION_RIGHT: usize = 1; -#[derive(Eq, Hash, PartialEq, Debug)] -pub struct BinaryOperationData { - node_data: NodeData, -} - -impl BinaryOperationData { - pub fn new(block_index: usize, left: usize, right: usize) -> BinaryOperationData { - BinaryOperationData { - node_data: NodeData::new(block_index, vec![left, right]), - } - } - - pub fn new_with_side_effect( - block: usize, - left: usize, - right: usize, - side_effect: usize, - ) -> BinaryOperationData { - BinaryOperationData { - node_data: NodeData::new(block, vec![left, right, side_effect]), - } - } - - pub fn left(&self) -> usize { - self.node_data.predecessors[0] - } - - pub fn right(&self) -> usize { - self.node_data.predecessors[1] - } -} - -#[derive(Eq, Hash, PartialEq, Debug)] -pub struct BlockData { - node_data: NodeData, -} - -impl BlockData { - pub fn new(block_index: usize) -> BlockData { - BlockData { - node_data: NodeData::new(block_index, vec![]), - } - } - - pub fn predecessors(&self) -> &Vec { - &self.node_data.predecessors - } -} - -#[derive(Eq, Hash, PartialEq, Debug)] -pub struct ConstantIntData { - node_data: NodeData, - value: i32, -} - -impl ConstantIntData { - pub fn new(block: usize, value: i32) -> ConstantIntData { - ConstantIntData { - node_data: NodeData::new(block, vec![]), - value, - } - } - - pub fn value(&self) -> i32 { - self.value - } -} - -#[derive(Eq, Hash, PartialEq, Debug)] -pub struct DivisionData { - binary_operation_data: BinaryOperationData, -} - -impl DivisionData { - pub fn new(block: usize, left: usize, right: usize, side_effect: usize) -> DivisionData { - DivisionData { - binary_operation_data: BinaryOperationData::new_with_side_effect( - block, - left, - right, - side_effect, - ), - } - } - - pub fn binary_operation_data(&self) -> &BinaryOperationData { - &self.binary_operation_data - } -} - -#[derive(Eq, Hash, PartialEq, Debug)] -pub struct ModuloData { - binary_operation_data: BinaryOperationData, -} - -impl ModuloData { - pub fn new(block: usize, left: usize, right: usize, side_effect: usize) -> ModuloData { - ModuloData { - binary_operation_data: BinaryOperationData::new_with_side_effect( - block, - left, - right, - side_effect, - ), - } - } - - pub fn binary_operation_data(&self) -> &BinaryOperationData { - &self.binary_operation_data - } -} - -#[derive(Eq, Hash, PartialEq, Debug)] -pub struct MultiplicationData { - binary_operation_data: BinaryOperationData, -} - -impl MultiplicationData { - pub fn new(block: usize, left: usize, right: usize) -> MultiplicationData { - MultiplicationData { - binary_operation_data: BinaryOperationData::new(block, left, right), - } - } - - pub fn binary_operation_data(&self) -> &BinaryOperationData { - &self.binary_operation_data - } -} - -#[derive(Eq, Hash, PartialEq, Debug)] -pub struct PhiData { - node_data: NodeData, -} - -impl PhiData { - pub fn new(block: usize) -> PhiData { - PhiData { - node_data: NodeData::new(block, vec![]), - } - } - - pub fn new_with_operands(block: usize, operands: Vec) -> PhiData { - PhiData { - node_data: NodeData { - block_index: block, - predecessors: operands, - }, - } - } - - pub fn node_data(&self) -> &NodeData { - &self.node_data - } - - pub fn add_operand(&mut self, operand: usize) { - self.node_data.predecessors.push(operand); - } -} - -#[derive(Eq, Hash, PartialEq, Debug)] -pub enum ProjectionInformation { - SideEffect, - Result, - IfTrue, - IfFalse, -} - -#[derive(Eq, Hash, PartialEq, Debug)] -pub struct ProjectionData { - node_data: NodeData, - projection_info: ProjectionInformation, -} - -impl ProjectionData { - pub fn new( - block: usize, - input: usize, - projection_info: ProjectionInformation, - ) -> ProjectionData { - ProjectionData { - node_data: NodeData::new(block, vec![input]), - projection_info, - } - } - - pub fn projection_info(&self) -> &ProjectionInformation { - &self.projection_info - } - - pub fn node_data(&self) -> &NodeData { - &self.node_data - } - - pub fn input(&self) -> usize { - self.node_data.predecessors()[0] - } -} - -pub const RETURN_RESULT_INDEX: usize = 0; -#[derive(Eq, Hash, PartialEq, Debug)] -pub struct ReturnData { - node_data: NodeData, -} - -impl ReturnData { - pub fn new(block: usize, result: usize, side_effect: usize) -> ReturnData { - ReturnData { - node_data: NodeData::new(block, vec![result, side_effect]), - } - } -} - -#[derive(Eq, Hash, PartialEq, Debug)] -pub struct StartData { - node_data: NodeData, -} - -impl StartData { - pub fn new(block: usize) -> StartData { - StartData { - node_data: NodeData::new(block, vec![]), - } - } -} - -#[derive(Eq, Hash, PartialEq, Debug)] -pub struct SubtractionData { - binary_operation_data: BinaryOperationData, -} - -impl SubtractionData { - pub fn new(block: usize, left: usize, right: usize) -> SubtractionData { - SubtractionData { - binary_operation_data: BinaryOperationData::new(block, left, right), - } - } - - pub fn binary_operation_data(&self) -> &BinaryOperationData { - &self.binary_operation_data - } -} - -#[derive(Eq, Hash, PartialEq, Debug)] -pub struct ShiftLeftData { - binary_operation_data: BinaryOperationData, -} - -impl ShiftLeftData { - pub fn new(block: usize, left: usize, right: usize) -> ShiftLeftData { - ShiftLeftData { - binary_operation_data: BinaryOperationData::new(block, left, right), - } - } - - pub fn binary_operation_data(&self) -> &BinaryOperationData { - &self.binary_operation_data - } -} - -#[derive(Eq, Hash, PartialEq, Debug)] -pub struct ShiftRightData { - binary_operation_data: BinaryOperationData, -} - -impl ShiftRightData { - pub fn new(block: usize, left: usize, right: usize) -> ShiftRightData { - ShiftRightData { - binary_operation_data: BinaryOperationData::new(block, left, right), - } - } - - pub fn binary_operation_data(&self) -> &BinaryOperationData { - &self.binary_operation_data - } -} - -#[derive(Eq, Hash, PartialEq, Debug)] -pub struct BitwiseNegateData { - node_data: NodeData, -} - -impl BitwiseNegateData { - pub fn new(block: usize, operand: usize) -> BitwiseNegateData { - BitwiseNegateData { - node_data: NodeData::new(block, vec![operand]), - } - } - - pub fn node_data(&self) -> &NodeData { - &self.node_data - } -} - -#[derive(Eq, Hash, PartialEq, Debug)] -pub struct LowerData { - binary_operation_data: BinaryOperationData, -} - -impl LowerData { - pub fn new(block: usize, left: usize, right: usize) -> LowerData { - LowerData { - binary_operation_data: BinaryOperationData::new(block, left, right), - } - } - - pub fn binary_operation_data(&self) -> &BinaryOperationData { - &self.binary_operation_data - } -} - -#[derive(Eq, Hash, PartialEq, Debug)] -pub struct LowerEqualsData { - binary_operation_data: BinaryOperationData, -} - -impl LowerEqualsData { - pub fn new(block: usize, left: usize, right: usize) -> LowerEqualsData { - LowerEqualsData { - binary_operation_data: BinaryOperationData::new(block, left, right), - } - } - - pub fn binary_operation_data(&self) -> &BinaryOperationData { - &self.binary_operation_data - } -} - -#[derive(Eq, Hash, PartialEq, Debug)] -pub struct EqualsData { - binary_operation_data: BinaryOperationData, -} - -impl EqualsData { - pub fn new(block: usize, left: usize, right: usize) -> EqualsData { - EqualsData { - binary_operation_data: BinaryOperationData::new(block, left, right), - } - } - - pub fn binary_operation_data(&self) -> &BinaryOperationData { - &self.binary_operation_data - } -} - -#[derive(Eq, Hash, PartialEq, Debug)] -pub struct NotEqualsData { - binary_operation_data: BinaryOperationData, -} - -impl NotEqualsData { - pub fn new(block: usize, left: usize, right: usize) -> NotEqualsData { - NotEqualsData { - binary_operation_data: BinaryOperationData::new(block, left, right), - } - } - - pub fn binary_operation_data(&self) -> &BinaryOperationData { - &self.binary_operation_data - } -} -#[derive(Eq, Hash, PartialEq, Debug)] -pub struct HigherEqualsData { - binary_operation_data: BinaryOperationData, -} - -impl HigherEqualsData { - pub fn new(block: usize, left: usize, right: usize) -> HigherEqualsData { - HigherEqualsData { - binary_operation_data: BinaryOperationData::new(block, left, right), - } - } - - pub fn binary_operation_data(&self) -> &BinaryOperationData { - &self.binary_operation_data - } -} - -#[derive(Eq, Hash, PartialEq, Debug)] -pub struct HigherData { - binary_operation_data: BinaryOperationData, -} - -impl HigherData { - pub fn new(block: usize, left: usize, right: usize) -> HigherData { - HigherData { - binary_operation_data: BinaryOperationData::new(block, left, right), - } - } - - pub fn binary_operation_data(&self) -> &BinaryOperationData { - &self.binary_operation_data - } -} -#[derive(Eq, Hash, PartialEq, Debug)] -pub struct OrData { - binary_operation_data: BinaryOperationData, -} - -impl OrData { - pub fn new(block: usize, left: usize, right: usize) -> OrData { - OrData { - binary_operation_data: BinaryOperationData::new(block, left, right), - } - } - - pub fn binary_operation_data(&self) -> &BinaryOperationData { - &self.binary_operation_data - } -} -#[derive(Eq, Hash, PartialEq, Debug)] -pub struct AndData { - binary_operation_data: BinaryOperationData, -} - -impl AndData { - pub fn new(block: usize, left: usize, right: usize) -> AndData { - AndData { - binary_operation_data: BinaryOperationData::new(block, left, right), - } - } - - pub fn binary_operation_data(&self) -> &BinaryOperationData { - &self.binary_operation_data - } -} -#[derive(Eq, Hash, PartialEq, Debug)] -pub struct XorData { - binary_operation_data: BinaryOperationData, -} - -impl XorData { - pub fn new(block: usize, left: usize, right: usize) -> XorData { - XorData { - binary_operation_data: BinaryOperationData::new(block, left, right), - } - } - - pub fn binary_operation_data(&self) -> &BinaryOperationData { - &self.binary_operation_data - } -} - -pub const COMPARISON_INDEX: usize = 0; -#[derive(Eq, Hash, PartialEq, Debug)] -pub struct ConditionalJumpData { - node_data: NodeData, -} - -impl ConditionalJumpData { - pub fn new(block: usize, condition: usize) -> ConditionalJumpData { - ConditionalJumpData { - node_data: NodeData::new(block, vec![condition]), - } - } - - pub fn node_data(&self) -> &NodeData { - &self.node_data - } -} - -#[derive(Eq, Hash, PartialEq, Debug)] -pub struct JumpData { - node_data: NodeData, -} - -impl JumpData { - pub fn new(block: usize) -> JumpData { - JumpData { - node_data: NodeData::new(block, vec![]), - } - } - - pub fn node_data(&self) -> &NodeData { - &self.node_data - } -} -#[derive(Eq, Hash, PartialEq, Debug)] -pub struct ConstantBoolData { - node_data: NodeData, - value: bool, -} - -impl ConstantBoolData { - pub fn new(block: usize, value: bool) -> ConstantBoolData { - ConstantBoolData { - node_data: NodeData::new(block, vec![]), - value, - } - } - - pub fn value(&self) -> bool { - self.value - } -} -impl Display for Node { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Node::Add(data) => write!( - f, - "Add: {:?}", - data.binary_operation_data.node_data.predecessors() - ), - Node::Phi(data) => write!(f, "!!PHI!! {:?}", data.node_data().predecessors()), - Node::Block(data) => write!(f, "Block: {:?}", data.node_data.predecessors()), - Node::Start(data) => write!(f, "Start: {:?}", data.node_data.predecessors()), - Node::ConstantInt(data) => write!( - f, - "Constant: {} {:?}", - data.value, - data.node_data.predecessors() - ), - Node::Division(data) => write!( - f, - "Div: {:?}", - data.binary_operation_data.node_data.predecessors() - ), - Node::Modulo(data) => write!( - f, - "Mod: {:?}", - data.binary_operation_data.node_data.predecessors() - ), - Node::Projection(data) => write!(f, "Projection: {:?}", data.node_data.predecessors()), - Node::Multiplication(data) => write!( - f, - "Multiplication: {:?}", - data.binary_operation_data.node_data.predecessors() - ), - Node::Return(data) => write!(f, "Return: {:?}", data.node_data.predecessors()), - Node::Subtraction(data) => { - write!( - f, - "Subtraction: {:?}", - data.binary_operation_data.node_data.predecessors() - ) - } - Node::BitwiseNegate(data) => { - write!(f, "BitwiseNegate: {:?}", data.node_data.predecessors()) - } - Node::ShiftRight(data) => { - write!( - f, - "ShiftRight: {:?}", - data.binary_operation_data.node_data.predecessors() - ) - } - Node::ShiftLeft(data) => { - write!( - f, - "ShiftLeft: {:?}", - data.binary_operation_data.node_data.predecessors() - ) - } - Node::Lower(data) => { - write!( - f, - "Lower: {:?}", - data.binary_operation_data.node_data.predecessors() - ) - } - Node::LowerEquals(data) => { - write!( - f, - "LowerEquals: {:?}", - data.binary_operation_data.node_data.predecessors() - ) - } - Node::Equals(data) => { - write!( - f, - "Equals: {:?}", - data.binary_operation_data.node_data.predecessors() - ) - } - Node::NotEquals(data) => { - write!( - f, - "NotEquals: {:?}", - data.binary_operation_data.node_data.predecessors() - ) - } - Node::HigherEquals(data) => { - write!( - f, - "HigherEquals: {:?}", - data.binary_operation_data.node_data.predecessors() - ) - } - Node::Higher(data) => { - write!( - f, - "Higher: {:?}", - data.binary_operation_data.node_data.predecessors() - ) - } - Node::Or(data) => { - write!( - f, - "Or: {:?}", - data.binary_operation_data.node_data.predecessors() - ) - } - Node::And(data) => { - write!( - f, - "And: {:?}", - data.binary_operation_data.node_data.predecessors() - ) - } - Node::Xor(data) => { - write!( - f, - "Xor: {:?}", - data.binary_operation_data.node_data.predecessors() - ) - } - Node::NoOp(_) => Ok(()), - Node::ConditionalJump(data) => { - write!(f, "ConditionalJump: {:?}", data.node_data.predecessors()) - } - Node::Jump(data) => { - write!(f, "Jump: {:?}", data.node_data.predecessors()) - } - Node::ConstantBool(data) => { - write!(f, "{}", data.value) - } - } - } -} diff --git a/src/ir/node/binary_operation.rs b/src/ir/node/binary_operation.rs new file mode 100644 index 0000000..7d7e9c0 --- /dev/null +++ b/src/ir/node/binary_operation.rs @@ -0,0 +1,38 @@ +use crate::ir::block::NodeIndex; + +#[derive(Eq, Hash, PartialEq, Debug)] +pub struct BinaryOperationData { + left_node: usize, + right_node: usize, + sideeffect: Option, +} + +impl BinaryOperationData { + pub fn new(left_node: usize, right_node: usize) -> BinaryOperationData { + BinaryOperationData { + left_node, + right_node, + sideeffect: None, + } + } + + pub fn new_with_sideeffect( + left_node: usize, + right_node: usize, + sideeffect: usize, + ) -> BinaryOperationData { + BinaryOperationData { + left_node, + right_node, + sideeffect: Some(sideeffect), + } + } + + pub fn lhs(&self) -> NodeIndex { + self.left_node + } + + pub fn rhs(&self) -> NodeIndex { + self.right_node + } +} diff --git a/src/ir/node/mod.rs b/src/ir/node/mod.rs new file mode 100644 index 0000000..1a59be9 --- /dev/null +++ b/src/ir/node/mod.rs @@ -0,0 +1,143 @@ +pub mod binary_operation; +pub mod projection; +pub mod unary_operation; + +use std::fmt::Display; + +use binary_operation::BinaryOperationData; +use projection::ProjectionData; +use unary_operation::UnaryOperationData; + +use super::{block::NodeIndex, graph::BlockIndex}; + +#[derive(Eq, Hash, PartialEq, Debug)] +pub enum Node { + Add(BinaryOperationData), + ConstantInt(ConstantIntData), + ConstantBool(ConstantBoolData), + Division(BinaryOperationData), + Modulo(BinaryOperationData), + Multiplication(BinaryOperationData), + Phi(PhiData), + Projection(ProjectionData), + Return(ReturnData), + Subtraction(BinaryOperationData), + ShiftLeft(BinaryOperationData), + ShiftRight(BinaryOperationData), + Lower(BinaryOperationData), + LowerEquals(BinaryOperationData), + Equals(BinaryOperationData), + NotEquals(BinaryOperationData), + HigherEquals(BinaryOperationData), + Higher(BinaryOperationData), + BitwiseNegate(UnaryOperationData), + Or(BinaryOperationData), + And(BinaryOperationData), + Xor(BinaryOperationData), + ConditionalJump(UnaryOperationData), + Jump, +} + +impl Node { + pub fn predecessors(&self) -> Vec { + match self { + Node::Jump | Node::ConstantBool(_) | Node::ConstantInt(_) => vec![], + Node::Projection(data) => vec![data.input()], + Node::Phi(_data) => todo!("What to return?"), + Node::Return(data) => vec![data.input()], + Node::ConditionalJump(data) | Node::BitwiseNegate(data) => vec![data.input()], + Node::Add(data) + | Node::Division(data) + | Node::ShiftRight(data) + | Node::Lower(data) + | Node::Higher(data) + | Node::LowerEquals(data) + | Node::HigherEquals(data) + | Node::Equals(data) + | Node::NotEquals(data) + | Node::Multiplication(data) + | Node::Modulo(data) + | Node::And(data) + | Node::Xor(data) + | Node::Or(data) + | Node::Subtraction(data) + | Node::ShiftLeft(data) => vec![data.lhs(), data.rhs()], + } + } +} + +#[derive(Eq, Hash, PartialEq, Debug)] +pub struct ConstantIntData { + value: i32, +} + +impl ConstantIntData { + pub fn new(value: i32) -> ConstantIntData { + ConstantIntData { value } + } + + pub fn value(&self) -> i32 { + self.value + } +} + +#[derive(Eq, Hash, PartialEq, Debug)] +pub struct ConstantBoolData { + value: bool, +} + +impl ConstantBoolData { + pub fn new(value: bool) -> ConstantBoolData { + ConstantBoolData { value } + } + + pub fn value(&self) -> bool { + self.value + } +} + +#[derive(Eq, Hash, PartialEq, Debug)] +pub struct PhiData { + operands: Vec<(BlockIndex, NodeIndex)>, +} + +impl PhiData { + pub fn empty() -> PhiData { + PhiData { operands: vec![] } + } + + pub fn new(operands: Vec<(BlockIndex, NodeIndex)>) -> PhiData { + PhiData { operands } + } + + pub fn add_operand(&mut self, operand: (BlockIndex, NodeIndex)) { + self.operands.push(operand); + } + + pub fn operands(&self) -> Vec<(BlockIndex, NodeIndex)> { + self.operands.iter().copied().collect() + } +} + +#[derive(Eq, Hash, PartialEq, Debug)] +pub struct ReturnData { + input: usize, +} + +impl ReturnData { + pub fn new(input: NodeIndex) -> ReturnData { + ReturnData { input } + } + + pub fn input(&self) -> NodeIndex { + self.input + } +} + +impl Display for Node { + fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + _ => todo!(), + } + } +} diff --git a/src/ir/node/projection.rs b/src/ir/node/projection.rs new file mode 100644 index 0000000..a7c47a5 --- /dev/null +++ b/src/ir/node/projection.rs @@ -0,0 +1,32 @@ +use crate::ir::block::NodeIndex; + +#[derive(Eq, Hash, PartialEq, Debug)] +pub enum ProjectionInformation { + SideEffect, + Result, + IfTrue, + IfFalse, +} + +#[derive(Eq, Hash, PartialEq, Debug)] +pub struct ProjectionData { + input: usize, + projection_information: ProjectionInformation, +} + +impl ProjectionData { + pub fn new(input: NodeIndex, projection_information: ProjectionInformation) -> ProjectionData { + ProjectionData { + input, + projection_information, + } + } + + pub fn input(&self) -> NodeIndex { + self.input + } + + pub fn projection_info(&self) -> &ProjectionInformation { + &self.projection_information + } +} diff --git a/src/ir/node/unary_operation.rs b/src/ir/node/unary_operation.rs new file mode 100644 index 0000000..1853ec2 --- /dev/null +++ b/src/ir/node/unary_operation.rs @@ -0,0 +1,27 @@ +use crate::ir::block::NodeIndex; + +#[derive(Eq, Hash, PartialEq, Debug)] +pub struct UnaryOperationData { + input: usize, + sideffect: Option, +} + +impl UnaryOperationData { + pub fn new(input: usize) -> UnaryOperationData { + UnaryOperationData { + input, + sideffect: None, + } + } + + pub fn new_with_sideeffect(input: usize, sideeffect: usize) -> UnaryOperationData { + UnaryOperationData { + input, + sideffect: Some(sideeffect), + } + } + + pub fn input(&self) -> NodeIndex { + self.input + } +} diff --git a/src/ir/optimizer.rs b/src/ir/optimizer.rs deleted file mode 100644 index 1452f96..0000000 --- a/src/ir/optimizer.rs +++ /dev/null @@ -1,19 +0,0 @@ -use std::collections::HashMap; - -use super::node::Node; - -pub struct Optimizer { - _known_nodes: HashMap, -} - -impl Optimizer { - pub fn new() -> Optimizer { - Optimizer { - _known_nodes: HashMap::new(), - } - } - - pub fn transform(&self, node: Node) -> Node { - node - } -} diff --git a/src/main.rs b/src/main.rs index b852b6a..a38f860 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,6 +11,7 @@ use lexer::Lexer; use parser::{ast::Tree, error::ParseError, Parser}; use rand::{distr::Alphanumeric, Rng}; use semantic::{analyze, AnalysisState}; +use tracing::{debug, error, info}; pub mod backend; pub mod ir; @@ -23,7 +24,7 @@ fn main() { tracing_subscriber::fmt::init(); let args: Vec = env::args().collect(); if args.len() != 3 { - println!("Error: Invalid arguments"); + error!("Error: Invalid arguments"); exit(3); } let input = Path::new(args.get(1).unwrap()); @@ -36,14 +37,15 @@ fn main() { let output = Path::new(args.get(2).unwrap()); let program = lex_parse(input); - println!("Program AST:"); - println!("{}", program); + debug!("Program AST: {}", program); + let mut state = AnalysisState::default(); let semantic_analysis = analyze(Box::new(program.clone()), &mut state); if semantic_analysis.is_err() { - println!("Semantic Error: {}", semantic_analysis.err().unwrap()); + error!("Semantic Error: {}", semantic_analysis.err().unwrap()); exit(7) } + let mut ir_graphs = Vec::new(); if let Tree::Program(functions) = program { for function in functions { @@ -55,13 +57,13 @@ fn main() { panic!("Result from parser is not a program tree!"); } for ir_graph in ir_graphs.iter().clone() { - println!("Constructed IR:"); - println!("{}", &ir_graph); + debug!("Constructed IR: {}", ir_graph); } let code_generator = CodeGenerator::new(ir_graphs); - fs::write(temp.clone(), code_generator.generate()) - .expect("Filesystem: Failed to write assembler output"); - println!( + let assembler = code_generator.generate(); + debug!("Assembler contents: {}", &assembler); + fs::write(temp.clone(), assembler).expect("Filesystem: Failed to write assembler output"); + info!( "Filesystem: Wrote assembler to {}", temp.clone().to_string() ); @@ -71,7 +73,14 @@ fn main() { .arg(output) .output() .expect("Failed to invoke GCC!"); - println!("{}", std::str::from_utf8(gcc.stderr.as_slice()).unwrap()); + if !gcc.stderr.as_slice().is_empty() { + error!("{}", std::str::from_utf8(gcc.stderr.as_slice()).unwrap()); + } else { + info!( + "Successfully compiled {}!", + input.file_name().unwrap().to_str().unwrap() + ) + } } fn lex_parse(path: &Path) -> Tree { @@ -85,7 +94,7 @@ fn lex_parse(path: &Path) -> Tree { if error.eq(&ParseError::Finished) { None } else { - println!("Lexing Error: {:?}", error); + error!("Lexing Error: {:?}", error); exit(42) } } @@ -97,7 +106,7 @@ fn lex_parse(path: &Path) -> Tree { if parse_result.is_err() && matches!(parse_result.as_ref().err().unwrap(), ParseError::Error(_)) { if let ParseError::Error(error) = parse_result.as_ref().err().unwrap() { - println!("Parsing Error {}", error); + error!("Parsing Error {}", error); exit(42) } } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index c3b0269..465757e 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -85,7 +85,7 @@ fn parse_statement(tokens: &mut VecDeque) -> Result { let statement = if tokens .front() .unwrap() - .is_separator(&SeperatorType::ParenOpen) + .is_separator(&SeperatorType::BraceOpen) { parse_block(tokens)? } else if tokens.front().unwrap().is_control_keyword() { @@ -260,7 +260,6 @@ fn parse_lvalue(tokens: &mut VecDeque) -> Result, ParseError> { fn parse_expression(tokens: &mut VecDeque) -> Result, ParseError> { let lhs = parse_precedence_expression(tokens, MAX_PRECEDENCE)?; - println!("DBG: {:?}", tokens.front().unwrap()); if !tokens.front().unwrap().is_operator() { return Ok(lhs); } diff --git a/test.l2 b/test.l2 new file mode 100644 index 0000000..dacea65 --- /dev/null +++ b/test.l2 @@ -0,0 +1,3 @@ +int main() { +return false ? 1 : 0; +} From 81f019b55f7c53400d93f92182fcf093b5919535 Mon Sep 17 00:00:00 2001 From: Felix Schwickerath Date: Wed, 11 Jun 2025 14:12:53 +0200 Subject: [PATCH 03/52] feat(ir): implement IR for if expressions --- .gitignore | 1 + run-tests.sh | 3 ++ src/backend/codegen.rs | 13 -------- src/ir/block.rs | 4 +++ src/ir/constructor.rs | 71 ++++++++++++++++++++++++++++++++++++------ src/ir/node/mod.rs | 6 ++-- test.l2 | 3 -- 7 files changed, 71 insertions(+), 30 deletions(-) create mode 100755 run-tests.sh delete mode 100644 test.l2 diff --git a/.gitignore b/.gitignore index e22fa32..c212b2e 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ *.s *.o tests +test.l2 diff --git a/run-tests.sh b/run-tests.sh new file mode 100755 index 0000000..fe019a7 --- /dev/null +++ b/run-tests.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env fish +cargo build --release +nix run github:I-Al-Istannen/crow#client -- run-tests --test-dir tests/ --compiler-run ./run.sh diff --git a/src/backend/codegen.rs b/src/backend/codegen.rs index 1246854..3870b18 100644 --- a/src/backend/codegen.rs +++ b/src/backend/codegen.rs @@ -186,19 +186,6 @@ impl CodeGenerator { Node::ConstantBool(data) => code.push_str(&self.generate_constant_bool(data.value())), Node::Phi(data) => { debug!("Warning! Phi present: Aliasing {:?}", data.operands()); - let destination_register = registers.get(node).unwrap(); - for (operand_block, operand_node) in data.operands() { - let operand_block = ir_graph.get_block(operand_block); - let operand_node = operand_block.get_node(operand_node); - let register = registers - .get(operand_node) - .expect("Expected register for phi operand"); - code.push_str(&format!( - "mov {}, {}\n", - register.as_assembly(), - destination_register.as_assembly() - )); - } } Node::Jump => { trace!( diff --git a/src/ir/block.rs b/src/ir/block.rs index 3f3b7e5..fb22229 100644 --- a/src/ir/block.rs +++ b/src/ir/block.rs @@ -56,6 +56,10 @@ impl Block { label.push_str(&index.to_string()); label } + + pub fn empty(&self) -> bool { + self.nodes.is_empty() + } } impl Display for Block { diff --git a/src/ir/constructor.rs b/src/ir/constructor.rs index 5d6fd3c..9e5e23a 100644 --- a/src/ir/constructor.rs +++ b/src/ir/constructor.rs @@ -45,7 +45,7 @@ impl IRGraphConstructor { current_side_effect: HashMap::new(), incomplete_side_effect_phis: HashMap::new(), // Start Block never gets more predecessors - sealed_blocks: vec![0], + sealed_blocks: vec![START_BLOCK], current_block_index: START_BLOCK, } } @@ -67,15 +67,22 @@ impl IRGraphConstructor { self.write_current_side_effect(side_effect_projection); self.convert_boxed(body); + self.seal_block(self.current_block_index); // The last statement after parsing the body can exit the function - let last_statement_index = self - .graph - .get_block(self.current_block_index) - .get_last_node_index(); - self.graph - .end_block_mut() - .register_entry_point(self.current_block_index, last_statement_index); + if !self.graph.get_block(self.current_block_index).empty() { + let last_statement_index = self + .graph + .get_block(self.current_block_index) + .get_last_node_index(); + self.graph + .end_block_mut() + .register_entry_point(self.current_block_index, last_statement_index); + } else { + self.graph + .end_block_mut() + .register_entry_point(self.current_block_index, 0); + } None } Tree::Assignment(lvalue, operator, expression) => match *lvalue { @@ -290,13 +297,46 @@ impl IRGraphConstructor { (false_block_index, false_expression), (true_block_index, true_expression), ]); + self.seal_block(self.current_block_index); Some(phi) } Tree::While(_condition, _expression, _) => { todo!("Implement while") } - Tree::If(_condition, _body, _else_body, _) => { - todo!("Implement if") + Tree::If(condition, body, else_body, _) => { + debug!("Generating IR for If"); + let condition_node = self.convert_boxed(condition)?; + let conditional_jump = self.create_conditional_jump(condition_node); + + let true_projection = self.create_true_projection(conditional_jump); + let false_projection = self.create_false_projection(conditional_jump); + + let mut false_block = Block::new("if-false".to_string()); + false_block.register_entry_point(self.current_block_index, false_projection); + let mut true_block = Block::new("if-true".to_string()); + true_block.register_entry_point(self.current_block_index, true_projection); + self.seal_block(self.current_block_index); + + let false_block_index = self.graph.register_block(false_block); + self.current_block_index = false_block_index; + if let Some(else_statements) = else_body { + self.convert_boxed(else_statements); + } + let false_jump = self.create_jump(); + + let true_block_index = self.graph.register_block(true_block); + self.current_block_index = true_block_index; + self.convert_boxed(body); + let true_jump = self.create_jump(); + + let mut following_block = Block::new("if-following".to_string()); + following_block.register_entry_point(false_block_index, false_jump); + following_block.register_entry_point(true_block_index, true_jump); + self.current_block_index = self.graph.register_block(following_block); + self.seal_block(false_block_index); + self.seal_block(true_block_index); + self.seal_block(self.current_block_index); + None } Tree::For(_option_initializer, _comparison, _option_postincrement, _statement, _) => { todo!("Implement for") @@ -511,6 +551,12 @@ impl IRGraphConstructor { self.read_variable(variable.clone(), block_index), )); } + trace!( + "Created phi operands for block {} whilst reading {:?}: {:?}", + block_index, + variable, + operands + ); let current_block = self.graph.get_block_mut(self.current_block_index); current_block.register_node(Node::Phi(PhiData::new(operands))) } @@ -568,6 +614,11 @@ impl IRGraphConstructor { } fn read_variable_recursive(&mut self, variable: Name, block_index: BlockIndex) -> usize { + trace!( + "Reading variable {:?} recursively in block {}", + variable, + block_index + ); let node = if !self.sealed_blocks.contains(&block_index) { let phi = self.create_phi(); trace!("Writing incomplete phi: ({:?}, {})", variable, phi); diff --git a/src/ir/node/mod.rs b/src/ir/node/mod.rs index 1a59be9..afb6cd2 100644 --- a/src/ir/node/mod.rs +++ b/src/ir/node/mod.rs @@ -135,9 +135,7 @@ impl ReturnData { } impl Display for Node { - fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - _ => todo!(), - } + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self) } } diff --git a/test.l2 b/test.l2 deleted file mode 100644 index dacea65..0000000 --- a/test.l2 +++ /dev/null @@ -1,3 +0,0 @@ -int main() { -return false ? 1 : 0; -} From e81d427ab2726f7d13a7e38fc4f32a121b4b4e60 Mon Sep 17 00:00:00 2001 From: Felix Schwickerath Date: Sat, 14 Jun 2025 15:27:18 +0200 Subject: [PATCH 04/52] feat(ir/codegen): finally implement while --- output | Bin 15720 -> 15696 bytes src/backend/codegen.rs | 277 +++++++++++++++++-------- src/backend/regalloc.rs | 70 ++++--- src/ir/constructor.rs | 355 +++++++++++++++++++++++--------- src/ir/graph.rs | 12 +- src/ir/node/binary_operation.rs | 19 +- src/ir/node/mod.rs | 21 +- src/ir/node/projection.rs | 11 +- src/ir/node/unary_operation.rs | 13 +- src/lexer/mod.rs | 4 +- 10 files changed, 549 insertions(+), 233 deletions(-) diff --git a/output b/output index a2931697a2a5062940aa89eb7b5f3fb2ea6717b1..50a019a353f98dcaec08268a644e93bb5353f452 100755 GIT binary patch delta 485 zcmaD+b)jm424lcP&HJ2*j0|9qxLJ^qU%=$}F%Zx5_%SXZ?%vKv^xItPm47LkODf29p;WiL=^*^iJN$B+TeD`65ttB#JCY8t)e=%gahM4b{tM@NqT*ViO=X-rQiK!8G{*6U*imW(iD;CX+cW^*Ldl3EAvu k+0M*c!3=ST6p#{~eAPyqbA>TPWaVT|TXV+1$&R+_0N@YxpJ~U=rA*kif_2vN=(YpP5mAa-pHPqne-tNFyBV1+%yqEYQS3#|2$Wp}*2~4fhUT8BlM{im3!$>&XtK*EPc+^y z;pt=)6_lcv&*0;11jHslYz)Mvn^R0Qm^M!^OJHJDp8U~5pA+U;m(7}%?aaIrm?2J( g0#ag=_u6Q49x#T8teyPP#+)Kn0RR91 diff --git a/src/backend/codegen.rs b/src/backend/codegen.rs index 3870b18..dc92fca 100644 --- a/src/backend/codegen.rs +++ b/src/backend/codegen.rs @@ -7,13 +7,13 @@ use crate::ir::{ graph::{BlockIndex, IRGraph, END_BLOCK}, node::{ binary_operation::BinaryOperationData, projection::ProjectionInformation, ConstantIntData, - Node, + Node, ReturnData, }, }; use super::regalloc::{HardwareRegister, Register, RegisterAllocator}; -type Registers<'a> = HashMap<&'a Node, Box>; +type Registers<'a> = HashMap<(BlockIndex, NodeIndex), Box>; const TEMPLATE: &str = " .section .note.GNU-stack,\"\",@progbits .global main @@ -63,21 +63,14 @@ impl CodeGenerator { code.push_str("mov %rsp, %rbp\n"); code.push_str(&format!("subq ${}, %rsp\n", stack_offset)); - let mut block_code = String::new(); // Jump Information contains for each block(key) the map of exits (NodeIndex[Node that jumps], BlockIndex[Block that is jumped to]) let mut jump_information = HashMap::new(); let mut visited = Vec::new(); - let mut queue = VecDeque::new(); jump_information.insert(END_BLOCK, HashMap::new()); visited.push(END_BLOCK); - queue.push_back((ir_graph.end_block(), END_BLOCK)); - while !queue.is_empty() { - let (block, block_index) = queue.pop_front().unwrap(); + for (block_index, block) in ir_graph.get_blocks().iter().enumerate() { for (previous_block_index, previous_node) in block.entry_points() { - if !visited.contains(previous_block_index) { - let previous_block = ir_graph.get_block(*previous_block_index); - queue.push_back((previous_block, *previous_block_index)); - visited.push(*previous_block_index); + if !jump_information.contains_key(&previous_block_index) { jump_information.insert( *previous_block_index, HashMap::from([(*previous_node, block_index)]), @@ -91,15 +84,46 @@ impl CodeGenerator { jump_information.insert(*previous_block_index, existing_exits); } } - block_code = self.generate_for_block( - block, - block_index, - jump_information.get(&block_index).unwrap(), - ir_graph, - ®isters, - ) + block_code.as_str(); } - code.push_str(&block_code); + code.push_str(&self.generate_recursive( + END_BLOCK, + ir_graph, + &mut jump_information, + &mut visited, + ®isters, + )); + code + } + + pub fn generate_recursive( + &self, + block_index: BlockIndex, + ir_graph: &IRGraph, + jump_information: &mut HashMap>, + visited: &mut Vec, + registers: &Registers, + ) -> String { + let mut code = String::new(); + let block = ir_graph.get_block(block_index); + for (previous_block_index, _) in block.entry_points() { + if !visited.contains(previous_block_index) { + visited.push(*previous_block_index); + code.push_str(&self.generate_recursive( + *previous_block_index, + ir_graph, + jump_information, + visited, + registers, + )); + } + } + code.push_str(&self.generate_for_block( + block, + block_index, + jump_information.get(&block_index).unwrap(), + ir_graph, + ®isters, + )); code } @@ -124,6 +148,7 @@ impl CodeGenerator { node, node_index, block, + block_index, jump_information, ir_graph, registers, @@ -137,6 +162,7 @@ impl CodeGenerator { node: &Node, node_index: NodeIndex, block: &Block, + block_index: BlockIndex, jump_information: &HashMap, ir_graph: &IRGraph, registers: &Registers, @@ -144,45 +170,108 @@ impl CodeGenerator { debug!("Generating assembly for node {}", node); let mut code = String::new(); match node { - Node::Add(_) => { - code.push_str(&self.generate_binary_operation(node_index, block, registers, "add")); + Node::Add(data) => { + code.push_str(&self.generate_binary_operation( + node_index, + block, + block_index, + data, + ir_graph, + registers, + "add", + )); } - Node::Subtraction(_) => { - code.push_str(&self.generate_binary_operation(node_index, block, registers, "sub")); + Node::Subtraction(data) => { + code.push_str(&self.generate_binary_operation( + node_index, + block, + block_index, + data, + ir_graph, + registers, + "sub", + )); } - Node::Multiplication(_) => { - code.push_str( - &self - .generate_binary_operation_rax(node_index, block, registers, "imul", "mul"), - ); + Node::Multiplication(data) => { + code.push_str(&self.generate_binary_operation_rax( + node_index, + block, + block_index, + ir_graph, + data, + registers, + "imul", + "mul", + )); } - Node::Division(_) => { - code.push_str( - &self - .generate_binary_operation_rax(node_index, block, registers, "idiv", "div"), - ); + Node::Division(data) => { + code.push_str(&self.generate_binary_operation_rax( + node_index, + block, + block_index, + ir_graph, + data, + registers, + "idiv", + "div", + )); } - Node::Modulo(_) => { - code.push_str( - &self - .generate_binary_operation_rax(node_index, block, registers, "idiv", "mod"), - ); + Node::Modulo(data) => { + code.push_str(&self.generate_binary_operation_rax( + node_index, + block, + block_index, + ir_graph, + data, + registers, + "idiv", + "mod", + )); } - Node::Return(_) => { - code.push_str(&self.generate_return(block, registers, node_index)); + Node::Return(data) => { + code.push_str(&self.generate_return(ir_graph, data, registers)); } Node::ConstantInt(data) => { - code.push_str(&self.generate_constant_int(data, block, registers, node_index)); + code.push_str(&self.generate_constant_int( + data, + block, + block_index, + registers, + node_index, + )); } Node::ShiftLeft(data) => { - code.push_str(&self.generate_shift(node_index, block, data, registers, "sall")); + code.push_str(&self.generate_shift( + node_index, + block, + block_index, + ir_graph, + data, + registers, + "sall", + )); } Node::ShiftRight(data) => { - code.push_str(&self.generate_shift(node_index, block, data, registers, "sarl")); + code.push_str(&self.generate_shift( + node_index, + block, + block_index, + ir_graph, + data, + registers, + "sarl", + )); } Node::Equals(data) => { code.push_str(&self.generate_comparison(block, data, registers)); } + Node::LowerEquals(data) => { + code.push_str(&self.generate_comparison(block, data, registers)); + } + Node::Lower(data) => code.push_str(&self.generate_comparison(block, data, registers)), + Node::HigherEquals(data) => { + code.push_str(&self.generate_comparison(block, data, registers)) + } Node::ConstantBool(data) => code.push_str(&self.generate_constant_bool(data.value())), Node::Phi(data) => { debug!("Warning! Phi present: Aliasing {:?}", data.operands()); @@ -198,6 +287,10 @@ impl CodeGenerator { } Node::ConditionalJump(_) => {} Node::Projection(data) if data.projection_info().eq(&ProjectionInformation::IfTrue) => { + trace!( + "Generating IR for true projection (including jump) with jump information {:?}", + jump_information + ); let previous_block_index = jump_information.get(&node_index).unwrap(); let conditional_jump_code = self.generate_conditional_jump(node_index, block, *previous_block_index); @@ -216,6 +309,7 @@ impl CodeGenerator { Node::Projection(_) => return code, node => panic!("unimplemented node {:?}", node), } + debug!("Generated code for IR: {}", code); code } @@ -263,33 +357,46 @@ impl CodeGenerator { operation_data: &BinaryOperationData, registers: &Registers, ) -> String { - let left_value = registers.get(block.get_node(operation_data.lhs())).unwrap(); - let right_value = registers.get(block.get_node(operation_data.rhs())).unwrap(); + let left_value = registers.get(&operation_data.lhs()).unwrap(); + let right_value = registers.get(&operation_data.rhs()).unwrap(); let mut code = String::new(); - //TODO: Both registers can be in memory - code.push_str(&format!( - "cmp {}, {}\n", - left_value.as_assembly(), - right_value.as_assembly() - )); + if !left_value.hardware_register() && !right_value.hardware_register() { + code.push_str(&move_stack_variable(left_value)); + } + if !left_value.hardware_register() && !right_value.hardware_register() { + code.push_str(&format!( + "cmp {}, {}\n", + right_value.as_assembly(), + HardwareRegister::Rbx.as_assembly(), + )); + } else { + code.push_str(&format!( + "cmp {}, {}\n", + right_value.as_assembly(), + left_value.as_assembly() + )); + } code } pub fn generate_binary_operation( &self, - node_index: usize, + node_index: NodeIndex, block: &Block, + block_index: BlockIndex, + data: &BinaryOperationData, + ir_graph: &IRGraph, registers: &Registers, op_code: &str, ) -> String { let left_value = registers - .get(block.get_node(predecessor_skip_projection(node_index, 0, block))) + .get(&predecessor_skip_projection(ir_graph, data.lhs())) .unwrap(); let right_value = registers - .get(block.get_node(predecessor_skip_projection(node_index, 1, block))) + .get(&predecessor_skip_projection(ir_graph, data.rhs())) .unwrap(); - let destination_register = registers.get(block.get_node(node_index)).unwrap(); + let destination_register = registers.get(&(block_index, node_index)).unwrap(); let mut code = String::new(); if !left_value.hardware_register() && !destination_register.hardware_register() { @@ -326,21 +433,24 @@ impl CodeGenerator { &self, node_index: usize, block: &Block, + block_index: BlockIndex, + ir_graph: &IRGraph, + data: &BinaryOperationData, registers: &Registers, op_code: &str, mode: &str, ) -> String { let left_value = registers - .get(block.get_node(predecessor_skip_projection(node_index, 0, block))) + .get(&predecessor_skip_projection(ir_graph, data.lhs())) .unwrap(); let right_value = registers - .get(block.get_node(predecessor_skip_projection(node_index, 1, block))) + .get(&predecessor_skip_projection(ir_graph, data.rhs())) .unwrap(); - let destination_register = registers.get(block.get_node(node_index)).unwrap(); + let destination_register = registers.get(&(block_index, node_index)).unwrap(); let mut code = String::new(); - code.push_str("mov $0, %rdx\n"); - code.push_str("mov $0, %rax\n"); - code.push_str("mov $0, "); + code.push_str("movq $0, %rdx\n"); + code.push_str("movq $0, %rax\n"); + code.push_str("movq $0, "); code.push_str(&destination_register.as_assembly()); code.push('\n'); @@ -371,13 +481,15 @@ impl CodeGenerator { &self, node_index: NodeIndex, block: &Block, + block_index: BlockIndex, + ir_graph: &IRGraph, data: &BinaryOperationData, registers: &Registers, op_code: &str, ) -> String { let mut code = String::new(); - let left_value = registers.get(block.get_node(data.lhs())).unwrap(); - let right_value = registers.get(block.get_node(data.rhs())).unwrap(); + let left_value = registers.get(&data.lhs()).unwrap(); + let right_value = registers.get(&data.rhs()).unwrap(); if !left_value.hardware_register() && !right_value.hardware_register() { code.push_str(&move_stack_variable(right_value)); code.push_str(&format!( @@ -394,7 +506,7 @@ impl CodeGenerator { right_value.as_16_bit_assembly() )); } - let destination = registers.get(block.get_node(node_index)).unwrap(); + let destination = registers.get(&(block_index, node_index)).unwrap(); code.push_str(&format!( "movq {}, {}", left_value.as_assembly(), @@ -405,26 +517,21 @@ impl CodeGenerator { pub fn generate_return( &self, - block: &Block, + ir_graph: &IRGraph, + data: &ReturnData, registers: &Registers, - node_index: usize, ) -> String { debug!("Generating assembly for return"); - let return_node_index = predecessor_skip_projection(node_index, 0, block); + let return_node = predecessor_skip_projection(ir_graph, data.input()); debug!( "Determined node {} that contains the return result", - return_node_index + ir_graph.get_node(return_node) ); debug!("Registers: {:?}", registers); let mut code = String::new(); code.push_str("mov "); - code.push_str( - ®isters - .get(block.get_node(return_node_index)) - .unwrap() - .as_assembly(), - ); + code.push_str(®isters.get(&return_node).unwrap().as_assembly()); code.push_str(", %rax"); code.push('\n'); @@ -437,13 +544,14 @@ impl CodeGenerator { &self, constant_data: &ConstantIntData, block: &Block, + block_index: BlockIndex, registers: &Registers, node_index: NodeIndex, ) -> String { - let register = registers.get(block.get_node(node_index)).unwrap(); + let register = registers.get(&(block_index, node_index)).unwrap(); let mut code = String::new(); - code.push_str("mov "); + code.push_str("movq "); code.push_str(&(format!("$0x{:X}", &constant_data.value()).to_string())); code.push_str(", "); code.push_str(®ister.as_assembly()); @@ -452,22 +560,21 @@ impl CodeGenerator { } } -fn predecessor_skip_projection(node: usize, predecessor_index: usize, block: &Block) -> usize { - let predecessor = *block - .get_node(node) - .predecessors() - .get(predecessor_index) - .expect("Invalid predecessor index"); - if let Node::Projection(data) = block.get_node(predecessor) { +fn predecessor_skip_projection( + ir_graph: &IRGraph, + data: (BlockIndex, NodeIndex), +) -> (BlockIndex, NodeIndex) { + let predecessor = ir_graph.get_node(data); + if let Node::Projection(data) = predecessor { data.input() } else { - predecessor + data } } fn move_stack_variable(register: &Box) -> String { let mut code = String::new(); - code.push_str("mov "); + code.push_str("movq "); code.push_str(®ister.as_assembly()); code.push_str(", "); code.push_str(&HardwareRegister::Rbx.as_assembly()); diff --git a/src/backend/regalloc.rs b/src/backend/regalloc.rs index 8c7a28c..005e42f 100644 --- a/src/backend/regalloc.rs +++ b/src/backend/regalloc.rs @@ -6,6 +6,7 @@ use std::{ use tracing::debug; use crate::ir::{ + block::NodeIndex, graph::{BlockIndex, IRGraph, END_BLOCK}, node::Node, }; @@ -158,19 +159,20 @@ impl Display for StackRegister { } } -pub struct RegisterAllocator<'a> { +pub struct RegisterAllocator { current_stack_offset: usize, - registers: HashMap<&'a Node, Box>, - aliased_nodes: Vec<&'a Node>, + registers: HashMap<(BlockIndex, NodeIndex), Box>, + // Denotes that the key has been aliased by the value + aliased_nodes: HashMap<(BlockIndex, NodeIndex), (BlockIndex, NodeIndex)>, available_hardware_register: Vec, } -impl<'a> RegisterAllocator<'a> { - pub fn new() -> RegisterAllocator<'a> { +impl RegisterAllocator { + pub fn new() -> RegisterAllocator { RegisterAllocator { current_stack_offset: 0, registers: HashMap::new(), - aliased_nodes: Vec::new(), + aliased_nodes: HashMap::new(), available_hardware_register: vec![ //HardwareRegister::Rax, //HardwareRegister::Rbx, @@ -192,8 +194,8 @@ impl<'a> RegisterAllocator<'a> { pub fn allocate_registers( mut self, - graph: &'a IRGraph, - ) -> (HashMap<&'a Node, Box>, usize) { + graph: &IRGraph, + ) -> (HashMap<(BlockIndex, NodeIndex), Box>, usize) { let mut visited = Vec::new(); self.scan(END_BLOCK, graph, &mut visited); (self.registers, self.current_stack_offset) @@ -202,7 +204,7 @@ impl<'a> RegisterAllocator<'a> { pub fn scan( &mut self, block_index: BlockIndex, - graph: &'a IRGraph, + graph: &IRGraph, visited: &mut Vec, ) { let block = graph.get_block(block_index); @@ -213,23 +215,23 @@ impl<'a> RegisterAllocator<'a> { } } debug!("Scanning block {}, for register allocation", block); - for node in block.get_nodes() { + for (node_index, node) in block.get_nodes().iter().enumerate() { if needs_register(node) { - let register = self.get_available_register(node, graph); - self.registers.insert(node, register); + let register = self.get_available_register((block_index, node_index), graph); + self.registers.insert((block_index, node_index), register); } } } pub fn get_available_register( &mut self, - node: &Node, - ir_graph: &'a IRGraph, + node: (BlockIndex, NodeIndex), + ir_graph: &IRGraph, ) -> Box { - if self.aliased_nodes.contains(&node) { + if self.aliased_nodes.contains_key(&node) { debug!("Not generating new register for node that is aliased by a following phi!"); } - debug!("Allocating new register for: {}", node); + debug!("Allocating new register for: {}", ir_graph.get_node(node)); if self.has_available_hardware_register() { let register = self.available_hardware_register.pop().unwrap(); let register: Box = Box::new(register); @@ -246,19 +248,30 @@ impl<'a> RegisterAllocator<'a> { pub fn handle_phi_register( &mut self, - node: &Node, - ir_graph: &'a IRGraph, + node_index: (BlockIndex, NodeIndex), + ir_graph: &IRGraph, register: &Box, ) { + let node = ir_graph.get_node(node_index); if let Node::Phi(data) = node { debug!("Handling phi in register allocation: Aliasing the following nodes to store in the same node: {:?}", data.operands()); for (predecessor_block, predecessor_index) in data.operands() { - let predecessor_node = ir_graph - .get_block(predecessor_block) - .get_node(predecessor_index); + if self + .aliased_nodes + .contains_key(&(predecessor_block, predecessor_index)) + { + self.registers.insert( + *self + .aliased_nodes + .get(&(predecessor_block, predecessor_index)) + .unwrap(), + register.box_clone(), + ); + } self.registers - .insert(predecessor_node, register.box_clone()); - self.aliased_nodes.push(predecessor_node); + .insert((predecessor_block, predecessor_index), register.box_clone()); + self.aliased_nodes + .insert((predecessor_block, predecessor_index), node_index); } } } @@ -279,10 +292,17 @@ impl Clone for Box { } fn needs_register(node: &Node) -> bool { - !matches!(node, Node::Projection(_) | Node::Return(_)) + !matches!( + node, + Node::Projection(_) + | Node::Return(_) + | Node::LowerEquals(_) + | Node::Equals(_) + | Node::ConditionalJump(_) + ) } -impl Default for RegisterAllocator<'_> { +impl Default for RegisterAllocator { fn default() -> Self { Self::new() } diff --git a/src/ir/constructor.rs b/src/ir/constructor.rs index 9e5e23a..a60d0cc 100644 --- a/src/ir/constructor.rs +++ b/src/ir/constructor.rs @@ -1,7 +1,7 @@ use core::panic; use std::collections::HashMap; -use tracing::{debug, trace}; +use tracing::{debug, info, trace}; use crate::{ ir::{ @@ -28,12 +28,13 @@ use super::{ pub struct IRGraphConstructor { graph: IRGraph, - current_definitions: HashMap>, - incomplete_phis: HashMap>, + current_definitions: HashMap>, + incomplete_phis: HashMap>, current_side_effect: HashMap, incomplete_side_effect_phis: HashMap, sealed_blocks: Vec, current_block_index: BlockIndex, + active_loops: Vec, } impl IRGraphConstructor { @@ -47,10 +48,11 @@ impl IRGraphConstructor { // Start Block never gets more predecessors sealed_blocks: vec![START_BLOCK], current_block_index: START_BLOCK, + active_loops: Vec::new(), } } - pub fn convert(&mut self, tree: Tree) -> Option { + pub fn convert(&mut self, tree: Tree) -> Option<(BlockIndex, NodeIndex)> { debug!("Converting AST {} to IR!", tree); match tree { Tree::Program(_) => { @@ -61,7 +63,7 @@ impl IRGraphConstructor { // Function body can be entered from start function block function_body_block.register_entry_point(START_BLOCK, 0); let side_effect_projection = function_body_block.register_node(Node::Projection( - ProjectionData::new(0, ProjectionInformation::SideEffect), + ProjectionData::new((0, 0), ProjectionInformation::SideEffect), )); self.current_block_index = self.graph.register_block(function_body_block); @@ -113,18 +115,22 @@ impl IRGraphConstructor { let lhs = self.read_variable(name.clone(), self.current_block_index); let div = self.create_div(lhs, rhs); - let desugar = self.create_div_mod_projection(div); + let desugar = self + .create_div_mod_projection((self.current_block_index, div)); self.write_variable(name, self.current_block_index, desugar); } OperatorType::AssignMod => { let lhs = self.read_variable(name.clone(), self.current_block_index); let mod_node = self.create_mod(lhs, rhs); - let desugar = self.create_div_mod_projection(mod_node); + let desugar = self.create_div_mod_projection(( + self.current_block_index, + mod_node, + )); self.write_variable(name, self.current_block_index, desugar); } OperatorType::Assign => { - self.write_variable(name, self.current_block_index, rhs); + self.write_variable(name, rhs.0, rhs.1); } _ => panic!("Assignment has no assignment operator"), }; @@ -144,11 +150,11 @@ impl IRGraphConstructor { OperatorType::Mul => self.create_mul(lhs_node, rhs_node), OperatorType::Div => { let div_node = self.create_div(lhs_node, rhs_node); - self.create_div_mod_projection(div_node) + self.create_div_mod_projection((self.current_block_index, div_node)) } OperatorType::Mod => { let mod_node = self.create_mod(lhs_node, rhs_node); - self.create_div_mod_projection(mod_node) + self.create_div_mod_projection((self.current_block_index, mod_node)) } OperatorType::ShiftLeft => self.create_shift_left(lhs_node, rhs_node), OperatorType::ShiftRight => self.create_shift_right(lhs_node, rhs_node), @@ -184,7 +190,7 @@ impl IRGraphConstructor { panic!("Expected binary operator, got ternary operator!") } }; - Some(result) + Some((self.current_block_index, result)) } Tree::Block(statements, _) => { for statement in statements { @@ -200,7 +206,7 @@ impl IRGraphConstructor { if let Tree::Name(name, _) = *identifier { if initializer.is_some() { let rhs = self.convert_boxed(initializer.unwrap()).unwrap(); - self.write_variable(name, self.current_block_index, rhs); + self.write_variable(name, rhs.0, rhs.1); } None } else { @@ -218,7 +224,7 @@ impl IRGraphConstructor { Tree::Literal(constant, base, _) => { let value = parse_int(constant, base)?; let node = self.create_constant_int(value); - Some(node) + Some((self.current_block_index, node)) } Tree::LValueIdentifier(_) => None, Tree::Name(_, _) => None, @@ -226,18 +232,18 @@ impl IRGraphConstructor { OperatorType::Minus => { let node = self.convert_boxed(expression)?; let zero = self.create_constant_int(0); - let result = self.create_sub(zero, node); - Some(result) + let result = self.create_sub((self.current_block_index, zero), node); + Some((self.current_block_index, result)) } OperatorType::BitwiseNot => { let node = self.convert_boxed(expression)?; let result = self.create_bitwise_not(node); - Some(result) + Some((self.current_block_index, result)) } OperatorType::LogicalNot => { let node = self.convert_boxed(expression)?; let result = self.create_bitwise_not(node); - Some(result) + Some((self.current_block_index, result)) } _ => panic!("Unregistered Unary Operation {:?}", operator_type), }, @@ -253,15 +259,29 @@ impl IRGraphConstructor { } else { self.create_constant_bool(false) }; - Some(node) + Some((self.current_block_index, node)) } Tree::Break(_) => { - // Jump to after loop - todo!("Create unconditional jump with correct target"); + debug!( + "Generating IR for break statement with active loops: {:?}", + self.active_loops + ); + let jump = self.create_jump(); + self.graph + .get_block_mut(*self.active_loops.first().unwrap()) + .register_entry_point(self.current_block_index, jump); + None } Tree::Continue(_) => { - // Jump to post condition of loop - todo!("Create unconditional jump with correct target"); + debug!( + "Generating IR for continue statement with active loops: {:?}", + self.active_loops + ); + let jump = self.create_jump(); + self.graph + .get_block_mut(*self.active_loops.first().unwrap()) + .register_entry_point(self.current_block_index, jump); + None } Tree::TernaryOperation(expression, true_value, false_value) => { debug!("Generating IR for TernaryOperation"); @@ -293,15 +313,48 @@ impl IRGraphConstructor { self.current_block_index = self.graph.register_block(following_block); self.seal_block(true_block_index); self.seal_block(false_block_index); - let phi = self.create_phi_from_operands(vec![ - (false_block_index, false_expression), - (true_block_index, true_expression), - ]); + let phi = self.create_phi_from_operands(vec![false_expression, true_expression]); self.seal_block(self.current_block_index); - Some(phi) + Some((self.current_block_index, phi)) } - Tree::While(_condition, _expression, _) => { - todo!("Implement while") + Tree::While(condition, expression, _) => { + debug!("Generating IR for while"); + let inverted_entry_condition_node = + self.create_inverted_condition(condition.clone()); + let entry_conditional_jump = self.create_conditional_jump(( + self.current_block_index, + inverted_entry_condition_node, + )); + + let entry_true_projection = self.create_true_projection(entry_conditional_jump); + let entry_false_projection = self.create_false_projection(entry_conditional_jump); + + let mut loop_body = Block::new("while-body".to_string()); + loop_body.register_entry_point(self.current_block_index, entry_false_projection); + let mut following_block = Block::new("while-following".to_string()); + following_block + .register_entry_point(self.current_block_index, entry_true_projection); + self.seal_block(self.current_block_index); + + let loop_body_index = self.graph.register_block(loop_body); + self.current_block_index = loop_body_index; + self.convert_boxed(expression); + + let condition_node = self.convert_boxed(condition)?; + let conditional_jump = self.create_conditional_jump(condition_node); + + let true_projection = self.create_true_projection(conditional_jump); + let false_projection = self.create_false_projection(conditional_jump); + + self.graph + .get_block_mut(loop_body_index) + .register_entry_point(loop_body_index, true_projection); + + following_block.register_entry_point(loop_body_index, false_projection); + self.seal_block(loop_body_index); + self.current_block_index = self.graph.register_block(following_block); + self.seal_block(self.current_block_index); + None } Tree::If(condition, body, else_body, _) => { debug!("Generating IR for If"); @@ -346,11 +399,11 @@ impl IRGraphConstructor { } } - pub fn convert_boxed(&mut self, tree: Box) -> Option { + pub fn convert_boxed(&mut self, tree: Box) -> Option<(BlockIndex, NodeIndex)> { self.convert(*tree) } - pub fn create_conditional_jump(&mut self, condition: NodeIndex) -> NodeIndex { + pub fn create_conditional_jump(&mut self, condition: (BlockIndex, NodeIndex)) -> NodeIndex { let current_block = self.graph.get_block_mut(self.current_block_index); current_block.register_node(Node::ConditionalJump(UnaryOperationData::new(condition))) } @@ -371,22 +424,83 @@ impl IRGraphConstructor { current_block.register_node(Node::Jump) } - fn create_add(&mut self, lhs: NodeIndex, rhs: NodeIndex) -> NodeIndex { + fn create_add( + &mut self, + lhs: (BlockIndex, NodeIndex), + rhs: (BlockIndex, NodeIndex), + ) -> NodeIndex { let current_block = self.graph.get_block_mut(self.current_block_index); current_block.register_node(Node::Add(BinaryOperationData::new(lhs, rhs))) } - fn create_sub(&mut self, lhs: NodeIndex, rhs: NodeIndex) -> NodeIndex { + fn create_inverted_condition(&mut self, condition: Box) -> NodeIndex { + if let Tree::BinaryOperation(lhs, rhs, operator) = *condition { + let left_node = self.convert_boxed(lhs).unwrap(); + let right_node = self.convert_boxed(rhs).unwrap(); + let current_block = self.graph.get_block_mut(self.current_block_index); + match operator { + OperatorType::TernaryColon + | OperatorType::Mul + | OperatorType::Div + | OperatorType::Mod + | OperatorType::Plus + | OperatorType::Assign + | OperatorType::AssignMul + | OperatorType::AssignDiv + | OperatorType::AssignMod + | OperatorType::ShiftLeft + | OperatorType::BitwiseOr + | OperatorType::LogicalOr + | OperatorType::AssignPlus + | OperatorType::LogicalNot + | OperatorType::BitwiseNot + | OperatorType::ShiftRight + | OperatorType::BitwiseAnd + | OperatorType::BitwiseXor + | OperatorType::Minus => panic!("Invalid operator for condition!"), + OperatorType::Lower => current_block.register_node(Node::HigherEquals( + BinaryOperationData::new(left_node, right_node), + )), + OperatorType::Higher => current_block.register_node(Node::LowerEquals( + BinaryOperationData::new(left_node, right_node), + )), + OperatorType::Equals => current_block.register_node(Node::NotEquals( + BinaryOperationData::new(left_node, right_node), + )), + OperatorType::NotEquals => current_block.register_node(Node::Equals( + BinaryOperationData::new(left_node, right_node), + )), + OperatorType::LogicalAnd => todo!(), + _ => panic!(), + } + } else { + panic!() + } + } + + fn create_sub( + &mut self, + lhs: (BlockIndex, NodeIndex), + rhs: (BlockIndex, NodeIndex), + ) -> NodeIndex { let current_block = self.graph.get_block_mut(self.current_block_index); current_block.register_node(Node::Subtraction(BinaryOperationData::new(lhs, rhs))) } - fn create_mul(&mut self, lhs: NodeIndex, rhs: NodeIndex) -> NodeIndex { + fn create_mul( + &mut self, + lhs: (BlockIndex, NodeIndex), + rhs: (BlockIndex, NodeIndex), + ) -> NodeIndex { let current_block = self.graph.get_block_mut(self.current_block_index); current_block.register_node(Node::Multiplication(BinaryOperationData::new(lhs, rhs))) } - fn create_div(&mut self, lhs: NodeIndex, rhs: NodeIndex) -> NodeIndex { + fn create_div( + &mut self, + lhs: (BlockIndex, NodeIndex), + rhs: (BlockIndex, NodeIndex), + ) -> NodeIndex { let sideeffect = self.read_current_side_effect(); let current_block = self.graph.get_block_mut(self.current_block_index); current_block.register_node(Node::Division(BinaryOperationData::new_with_sideeffect( @@ -394,7 +508,11 @@ impl IRGraphConstructor { ))) } - fn create_mod(&mut self, lhs: NodeIndex, rhs: NodeIndex) -> NodeIndex { + fn create_mod( + &mut self, + lhs: (BlockIndex, NodeIndex), + rhs: (BlockIndex, NodeIndex), + ) -> NodeIndex { let sideeffect = self.read_current_side_effect(); let current_block = self.graph.get_block_mut(self.current_block_index); current_block.register_node(Node::Modulo(BinaryOperationData::new_with_sideeffect( @@ -402,7 +520,11 @@ impl IRGraphConstructor { ))) } - fn create_shift_left(&mut self, lhs: NodeIndex, rhs: NodeIndex) -> NodeIndex { + fn create_shift_left( + &mut self, + lhs: (BlockIndex, NodeIndex), + rhs: (BlockIndex, NodeIndex), + ) -> NodeIndex { let sideeffect = self.read_current_side_effect(); let current_block = self.graph.get_block_mut(self.current_block_index); current_block.register_node(Node::ShiftLeft(BinaryOperationData::new_with_sideeffect( @@ -410,7 +532,11 @@ impl IRGraphConstructor { ))) } - fn create_shift_right(&mut self, lhs: NodeIndex, rhs: NodeIndex) -> NodeIndex { + fn create_shift_right( + &mut self, + lhs: (BlockIndex, NodeIndex), + rhs: (BlockIndex, NodeIndex), + ) -> NodeIndex { let sideeffect = self.read_current_side_effect(); let current_block = self.graph.get_block_mut(self.current_block_index); current_block.register_node(Node::ShiftRight(BinaryOperationData::new_with_sideeffect( @@ -418,7 +544,11 @@ impl IRGraphConstructor { ))) } - fn create_lower(&mut self, lhs: NodeIndex, rhs: NodeIndex) -> NodeIndex { + fn create_lower( + &mut self, + lhs: (BlockIndex, NodeIndex), + rhs: (BlockIndex, NodeIndex), + ) -> NodeIndex { let sideeffect = self.read_current_side_effect(); let current_block = self.graph.get_block_mut(self.current_block_index); current_block.register_node(Node::Lower(BinaryOperationData::new_with_sideeffect( @@ -426,7 +556,11 @@ impl IRGraphConstructor { ))) } - fn create_lower_equals(&mut self, lhs: NodeIndex, rhs: NodeIndex) -> NodeIndex { + fn create_lower_equals( + &mut self, + lhs: (BlockIndex, NodeIndex), + rhs: (BlockIndex, NodeIndex), + ) -> NodeIndex { let sideeffect = self.read_current_side_effect(); let current_block = self.graph.get_block_mut(self.current_block_index); current_block.register_node(Node::LowerEquals(BinaryOperationData::new_with_sideeffect( @@ -434,7 +568,11 @@ impl IRGraphConstructor { ))) } - fn create_equals(&mut self, lhs: NodeIndex, rhs: NodeIndex) -> NodeIndex { + fn create_equals( + &mut self, + lhs: (BlockIndex, NodeIndex), + rhs: (BlockIndex, NodeIndex), + ) -> NodeIndex { let sideeffect = self.read_current_side_effect(); let current_block = self.graph.get_block_mut(self.current_block_index); current_block.register_node(Node::Equals(BinaryOperationData::new_with_sideeffect( @@ -442,7 +580,11 @@ impl IRGraphConstructor { ))) } - fn create_not_equals(&mut self, lhs: NodeIndex, rhs: NodeIndex) -> NodeIndex { + fn create_not_equals( + &mut self, + lhs: (BlockIndex, NodeIndex), + rhs: (BlockIndex, NodeIndex), + ) -> NodeIndex { let sideeffect = self.read_current_side_effect(); let current_block = self.graph.get_block_mut(self.current_block_index); current_block.register_node(Node::Lower(BinaryOperationData::new_with_sideeffect( @@ -450,7 +592,11 @@ impl IRGraphConstructor { ))) } - fn create_higher_equals(&mut self, lhs: NodeIndex, rhs: NodeIndex) -> NodeIndex { + fn create_higher_equals( + &mut self, + lhs: (BlockIndex, NodeIndex), + rhs: (BlockIndex, NodeIndex), + ) -> NodeIndex { let sideeffect = self.read_current_side_effect(); let current_block = self.graph.get_block_mut(self.current_block_index); current_block.register_node(Node::HigherEquals( @@ -458,7 +604,11 @@ impl IRGraphConstructor { )) } - fn create_higher(&mut self, lhs: NodeIndex, rhs: NodeIndex) -> NodeIndex { + fn create_higher( + &mut self, + lhs: (BlockIndex, NodeIndex), + rhs: (BlockIndex, NodeIndex), + ) -> NodeIndex { let sideeffect = self.read_current_side_effect(); let current_block = self.graph.get_block_mut(self.current_block_index); current_block.register_node(Node::Higher(BinaryOperationData::new_with_sideeffect( @@ -466,7 +616,11 @@ impl IRGraphConstructor { ))) } - fn create_or(&mut self, lhs: NodeIndex, rhs: NodeIndex) -> NodeIndex { + fn create_or( + &mut self, + lhs: (BlockIndex, NodeIndex), + rhs: (BlockIndex, NodeIndex), + ) -> NodeIndex { let sideeffect = self.read_current_side_effect(); let current_block = self.graph.get_block_mut(self.current_block_index); current_block.register_node(Node::Or(BinaryOperationData::new_with_sideeffect( @@ -474,7 +628,11 @@ impl IRGraphConstructor { ))) } - fn create_and(&mut self, lhs: NodeIndex, rhs: NodeIndex) -> NodeIndex { + fn create_and( + &mut self, + lhs: (BlockIndex, NodeIndex), + rhs: (BlockIndex, NodeIndex), + ) -> NodeIndex { let sideeffect = self.read_current_side_effect(); let current_block = self.graph.get_block_mut(self.current_block_index); current_block.register_node(Node::And(BinaryOperationData::new_with_sideeffect( @@ -482,7 +640,11 @@ impl IRGraphConstructor { ))) } - fn create_xor(&mut self, lhs: NodeIndex, rhs: NodeIndex) -> NodeIndex { + fn create_xor( + &mut self, + lhs: (BlockIndex, NodeIndex), + rhs: (BlockIndex, NodeIndex), + ) -> NodeIndex { let sideeffect = self.read_current_side_effect(); let current_block = self.graph.get_block_mut(self.current_block_index); current_block.register_node(Node::Xor(BinaryOperationData::new_with_sideeffect( @@ -490,7 +652,7 @@ impl IRGraphConstructor { ))) } - fn create_bitwise_not(&mut self, node: NodeIndex) -> NodeIndex { + fn create_bitwise_not(&mut self, node: (BlockIndex, NodeIndex)) -> NodeIndex { let sideeffect = self.read_current_side_effect(); let current_block = self.graph.get_block_mut(self.current_block_index); current_block.register_node(Node::BitwiseNegate( @@ -508,7 +670,7 @@ impl IRGraphConstructor { current_block.register_node(Node::ConstantBool(ConstantBoolData::new(value))) } - fn create_return(&mut self, input: NodeIndex) -> NodeIndex { + fn create_return(&mut self, input: (BlockIndex, NodeIndex)) -> NodeIndex { let current_block = self.graph.get_block_mut(self.current_block_index); let return_node_index = current_block.register_node(Node::Return(ReturnData::new(input))); self.graph @@ -546,10 +708,7 @@ impl IRGraphConstructor { ) -> NodeIndex { let mut operands = Vec::new(); for (block_index, _) in self.graph.get_block(block_index).entry_points().clone() { - operands.push(( - block_index, - self.read_variable(variable.clone(), block_index), - )); + operands.push(self.read_variable(variable.clone(), block_index)); } trace!( "Created phi operands for block {} whilst reading {:?}: {:?}", @@ -561,7 +720,7 @@ impl IRGraphConstructor { current_block.register_node(Node::Phi(PhiData::new(operands))) } - fn create_div_mod_projection(&mut self, input: NodeIndex) -> NodeIndex { + fn create_div_mod_projection(&mut self, input: (BlockIndex, NodeIndex)) -> NodeIndex { let current_block = self.graph.get_block_mut(self.current_block_index); let projection_side_effect = current_block.register_node(Node::Projection( ProjectionData::new(input, ProjectionInformation::SideEffect), @@ -590,8 +749,12 @@ impl IRGraphConstructor { } } - fn read_variable(&mut self, variable: Name, block: usize) -> usize { - trace!("Trying to read from variable {:?}", variable); + fn read_variable(&mut self, variable: Name, block: BlockIndex) -> (BlockIndex, NodeIndex) { + trace!( + "Trying to read from variable {:?} in block {}", + variable, + block + ); if self.current_definitions.contains_key(&variable) { if self .current_definitions @@ -599,12 +762,16 @@ impl IRGraphConstructor { .unwrap() .contains_key(&block) { - *self - .current_definitions - .get(&variable) - .unwrap() - .get(&block) - .unwrap() + trace!("Variable defined in the same block! Returning value"); + ( + block, + *self + .current_definitions + .get(&variable) + .unwrap() + .get(&block) + .unwrap(), + ) } else { self.read_variable_recursive(variable, block) } @@ -613,26 +780,29 @@ impl IRGraphConstructor { } } - fn read_variable_recursive(&mut self, variable: Name, block_index: BlockIndex) -> usize { + fn read_variable_recursive( + &mut self, + variable: Name, + block_index: BlockIndex, + ) -> (BlockIndex, NodeIndex) { trace!( "Reading variable {:?} recursively in block {}", variable, block_index ); + trace!("Sealed blocks: {:?}", self.sealed_blocks); let node = if !self.sealed_blocks.contains(&block_index) { let phi = self.create_phi(); trace!("Writing incomplete phi: ({:?}, {})", variable, phi); - if let std::collections::hash_map::Entry::Vacant(e) = - self.incomplete_phis.entry(block_index) - { - e.insert(HashMap::from([(variable.clone(), phi)])); + if self.incomplete_phis.contains_key(&block_index) { + let mut entry = self.incomplete_phis.get_mut(&block_index).unwrap().clone(); + entry.insert(variable.clone(), phi); + self.incomplete_phis.insert(block_index, entry); } else { self.incomplete_phis - .get_mut(&block_index) - .unwrap() - .insert(variable.clone(), phi); - } - phi + .insert(block_index, HashMap::from([(variable.clone(), phi)])); + }; + (block_index, phi) } else if self.graph.get_block(block_index).entry_points().len() == 1 { let (previous_block, _) = self .graph @@ -645,37 +815,34 @@ impl IRGraphConstructor { } else { let phi = self.create_phi_variable_operands(block_index, variable.clone()); self.write_variable(variable.clone(), block_index, phi); - phi + (block_index, phi) }; - self.write_variable(variable.clone(), block_index, node); + self.write_variable(variable.clone(), node.0, node.1); node } fn seal_block(&mut self, block: BlockIndex) { trace!("Current graph before sealing block: {}", self.graph); - trace!("Incomplete Phis: {:?}", self.incomplete_phis); + info!("Incomplete Phis: {:?}", self.incomplete_phis); if !self.incomplete_phis.contains_key(&block) { self.sealed_blocks.push(block); return; } - for (variable, index) in self.incomplete_phis.get(&block).unwrap().clone() { - let operands = { - let mut operands = Vec::new(); - let block = self.graph.get_block_mut(block); - if let Node::Phi(data) = block.get_node_mut(index) { - for (block_index, _) in data.operands() { - operands.push(( - block_index, - self.read_variable(variable.clone(), block_index), - )); + for (block_index, definitions) in &self.incomplete_phis.clone() { + for (variable, phi) in definitions { + let operands = { + let mut operands = Vec::new(); + let block = self.graph.get_block_mut(*block_index); + for (prev_block, _) in block.entry_points().clone() { + operands.push(self.read_variable(variable.clone(), prev_block)); + } + operands + }; + let block = self.graph.get_block_mut(*block_index); + if let Node::Phi(data) = block.get_node_mut(*phi) { + for operand in operands { + data.add_operand(operand); } - } - operands - }; - let block = self.graph.get_block_mut(block); - if let Node::Phi(data) = block.get_node_mut(index) { - for operand in operands { - data.add_operand(operand); } } } @@ -735,7 +902,7 @@ impl IRGraphConstructor { pub fn create_true_projection(&mut self, conditional_jump: NodeIndex) -> NodeIndex { let current_block = self.graph.get_block_mut(self.current_block_index); current_block.register_node(Node::Projection(ProjectionData::new( - conditional_jump, + (self.current_block_index, conditional_jump), ProjectionInformation::IfTrue, ))) } @@ -743,7 +910,7 @@ impl IRGraphConstructor { pub fn create_false_projection(&mut self, conditional_jump: NodeIndex) -> NodeIndex { let current_block = self.graph.get_block_mut(self.current_block_index); current_block.register_node(Node::Projection(ProjectionData::new( - conditional_jump, + (self.current_block_index, conditional_jump), ProjectionInformation::IfFalse, ))) } diff --git a/src/ir/graph.rs b/src/ir/graph.rs index 13f5131..98af7d6 100644 --- a/src/ir/graph.rs +++ b/src/ir/graph.rs @@ -1,6 +1,9 @@ use std::fmt::Display; -use super::block::Block; +use super::{ + block::{Block, NodeIndex}, + node::Node, +}; pub type BlockIndex = usize; @@ -42,6 +45,13 @@ impl IRGraph { .expect("Expected block at block index") } + pub fn get_node(&self, data: (BlockIndex, NodeIndex)) -> &Node { + self.blocks + .get(data.0) + .expect("Expected block at block index") + .get_node(data.1) + } + pub fn get_block_mut(&mut self, block_index: BlockIndex) -> &mut Block { self.blocks .get_mut(block_index) diff --git a/src/ir/node/binary_operation.rs b/src/ir/node/binary_operation.rs index 7d7e9c0..a1263e6 100644 --- a/src/ir/node/binary_operation.rs +++ b/src/ir/node/binary_operation.rs @@ -1,14 +1,17 @@ -use crate::ir::block::NodeIndex; +use crate::ir::{block::NodeIndex, graph::BlockIndex}; #[derive(Eq, Hash, PartialEq, Debug)] pub struct BinaryOperationData { - left_node: usize, - right_node: usize, + left_node: (BlockIndex, NodeIndex), + right_node: (BlockIndex, NodeIndex), sideeffect: Option, } impl BinaryOperationData { - pub fn new(left_node: usize, right_node: usize) -> BinaryOperationData { + pub fn new( + left_node: (BlockIndex, NodeIndex), + right_node: (BlockIndex, NodeIndex), + ) -> BinaryOperationData { BinaryOperationData { left_node, right_node, @@ -17,8 +20,8 @@ impl BinaryOperationData { } pub fn new_with_sideeffect( - left_node: usize, - right_node: usize, + left_node: (BlockIndex, NodeIndex), + right_node: (BlockIndex, NodeIndex), sideeffect: usize, ) -> BinaryOperationData { BinaryOperationData { @@ -28,11 +31,11 @@ impl BinaryOperationData { } } - pub fn lhs(&self) -> NodeIndex { + pub fn lhs(&self) -> (BlockIndex, NodeIndex) { self.left_node } - pub fn rhs(&self) -> NodeIndex { + pub fn rhs(&self) -> (BlockIndex, NodeIndex) { self.right_node } } diff --git a/src/ir/node/mod.rs b/src/ir/node/mod.rs index afb6cd2..9df850b 100644 --- a/src/ir/node/mod.rs +++ b/src/ir/node/mod.rs @@ -42,10 +42,10 @@ impl Node { pub fn predecessors(&self) -> Vec { match self { Node::Jump | Node::ConstantBool(_) | Node::ConstantInt(_) => vec![], - Node::Projection(data) => vec![data.input()], + Node::Projection(data) => vec![data.input().1], Node::Phi(_data) => todo!("What to return?"), - Node::Return(data) => vec![data.input()], - Node::ConditionalJump(data) | Node::BitwiseNegate(data) => vec![data.input()], + Node::Return(data) => vec![data.input().1], + Node::ConditionalJump(data) | Node::BitwiseNegate(data) => vec![data.input().1], Node::Add(data) | Node::Division(data) | Node::ShiftRight(data) @@ -61,7 +61,7 @@ impl Node { | Node::Xor(data) | Node::Or(data) | Node::Subtraction(data) - | Node::ShiftLeft(data) => vec![data.lhs(), data.rhs()], + | Node::ShiftLeft(data) => vec![data.lhs().1, data.rhs().1], } } } @@ -96,7 +96,7 @@ impl ConstantBoolData { } } -#[derive(Eq, Hash, PartialEq, Debug)] +#[derive(Debug, PartialEq, Eq, Hash)] pub struct PhiData { operands: Vec<(BlockIndex, NodeIndex)>, } @@ -111,7 +111,10 @@ impl PhiData { } pub fn add_operand(&mut self, operand: (BlockIndex, NodeIndex)) { - self.operands.push(operand); + match self.operands.binary_search(&operand) { + Ok(_) => {} + Err(pos) => self.operands.insert(pos, operand), + } } pub fn operands(&self) -> Vec<(BlockIndex, NodeIndex)> { @@ -121,15 +124,15 @@ impl PhiData { #[derive(Eq, Hash, PartialEq, Debug)] pub struct ReturnData { - input: usize, + input: (BlockIndex, NodeIndex), } impl ReturnData { - pub fn new(input: NodeIndex) -> ReturnData { + pub fn new(input: (BlockIndex, NodeIndex)) -> ReturnData { ReturnData { input } } - pub fn input(&self) -> NodeIndex { + pub fn input(&self) -> (BlockIndex, NodeIndex) { self.input } } diff --git a/src/ir/node/projection.rs b/src/ir/node/projection.rs index a7c47a5..1d45137 100644 --- a/src/ir/node/projection.rs +++ b/src/ir/node/projection.rs @@ -1,4 +1,4 @@ -use crate::ir::block::NodeIndex; +use crate::ir::{block::NodeIndex, graph::BlockIndex}; #[derive(Eq, Hash, PartialEq, Debug)] pub enum ProjectionInformation { @@ -10,19 +10,22 @@ pub enum ProjectionInformation { #[derive(Eq, Hash, PartialEq, Debug)] pub struct ProjectionData { - input: usize, + input: (BlockIndex, NodeIndex), projection_information: ProjectionInformation, } impl ProjectionData { - pub fn new(input: NodeIndex, projection_information: ProjectionInformation) -> ProjectionData { + pub fn new( + input: (BlockIndex, NodeIndex), + projection_information: ProjectionInformation, + ) -> ProjectionData { ProjectionData { input, projection_information, } } - pub fn input(&self) -> NodeIndex { + pub fn input(&self) -> (BlockIndex, NodeIndex) { self.input } diff --git a/src/ir/node/unary_operation.rs b/src/ir/node/unary_operation.rs index 1853ec2..be205b2 100644 --- a/src/ir/node/unary_operation.rs +++ b/src/ir/node/unary_operation.rs @@ -1,27 +1,30 @@ -use crate::ir::block::NodeIndex; +use crate::ir::{block::NodeIndex, graph::BlockIndex}; #[derive(Eq, Hash, PartialEq, Debug)] pub struct UnaryOperationData { - input: usize, + input: (BlockIndex, NodeIndex), sideffect: Option, } impl UnaryOperationData { - pub fn new(input: usize) -> UnaryOperationData { + pub fn new(input: (BlockIndex, NodeIndex)) -> UnaryOperationData { UnaryOperationData { input, sideffect: None, } } - pub fn new_with_sideeffect(input: usize, sideeffect: usize) -> UnaryOperationData { + pub fn new_with_sideeffect( + input: (BlockIndex, NodeIndex), + sideeffect: usize, + ) -> UnaryOperationData { UnaryOperationData { input, sideffect: Some(sideeffect), } } - pub fn input(&self) -> NodeIndex { + pub fn input(&self) -> (BlockIndex, NodeIndex) { self.input } } diff --git a/src/lexer/mod.rs b/src/lexer/mod.rs index 0c12acf..c30a938 100644 --- a/src/lexer/mod.rs +++ b/src/lexer/mod.rs @@ -395,10 +395,10 @@ impl Lexer { } if self.peek_pos(1)?.ne(&shift) { - if self.peek_pos(1)?.ne(&'=') { + if self.peek_pos(1)?.eq(&'=') { return Some(Token::Operator(self.build_span(2), comparison_equal_type)); } else { - return None; + return Some(Token::Operator(self.build_span(1), comparison_type)); } } if self.has_more(2) && self.peek_pos(2)?.eq(&'=') { From c3484caeb1e8fcda882aedc52230a108f1321996 Mon Sep 17 00:00:00 2001 From: Felix Schwickerath Date: Sat, 14 Jun 2025 15:38:27 +0200 Subject: [PATCH 05/52] fix(parsing): fix missing check/fail for missing semicolon --- .gitignore | 1 + output | Bin 15696 -> 0 bytes src/lexer/mod.rs | 10 +++++++++- src/parser/mod.rs | 20 ++++++++++++++++---- 4 files changed, 26 insertions(+), 5 deletions(-) delete mode 100755 output diff --git a/.gitignore b/.gitignore index c212b2e..2599f31 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ *.o tests test.l2 +output diff --git a/output b/output deleted file mode 100755 index 50a019a353f98dcaec08268a644e93bb5353f452..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15696 zcmeHOU2Ggz6~4Q65;tvXJ0YzhDH$nJ2}nC$uh(``NHUJ&WQ;aHO`L~3z;wJj_D;Gx z>&}jY9fT+f6*YZHh2RZdkdS$aTB)G$03^7w=}Xl}NcaJy2obOpC`Le35Xy4Sz2}U_ zQv(%|KtlIOGv}Uj&v);+-(1h$@!a|1Xz4^YlMyUgaYUeHbXn4+&O)3WlqUHOiXFld zd&M5H6+G>5Sh5DSDzRS4stVQ}6xRkCaXna&+S;JcOG^mWoFQt&t&2rPr8~ALleOql zHsWLz*@DK$+NBOyCvmbrL$xCeOBG1}l-#V7IOL|sQ~^ClELmXv9uJ84VH}!>J3zPt zgk$|0;aD@Dm?Iul15O}S%8#`rz3bO0jax8Ozyje|lUuN6?$6(iD*s7;X9?F%4YpH% ztoN!kU~Vbl{+KPhfB*AC z2kV3V!KKNdFZ^6_s4{)%O5V>+)%=Nau3#4Pxxv9+FnVj1T+I(=W^=O#2Xli(v)M2U zDsyZfr#xO~7W5?#_J0tKxSg8>UI(ACiC;JlX6rN~H;kOR>jwgD%y3YgER7u-aSCRC zQ9RW6pGlyH%K6Lr=O@CjQZAHVnDU$c(7{WU7iNQ@{-D2@@5@b<%elUySup#|LcTDN z?=KX9JmAoufMloYjewGMa5!1X6!Mi3yTIn_{Kl)_fa#GHPtllHj#{#Wa|StLxs&iG zuL0LYd5+q zOW23;9PPS+A=t$uKSoFE&g8Yl$@SpQeU|uMP@$dj7C1!SAu;=ftUTL*jOI!AXC-a%b_w|SI_rALN7$!EqY%kyZ6qGu* zAQEU1_VN9V2fnqp7Gq9h7O@R2 zngPv#WDd79Cy=$#j5p)T(4f;cH zpk%I`6Pekr%$~=#bzFm|UwrR|?`ZG*q1AdC1CDR&LSG(^zk`on!()vUe2Gy|Fe&46Y=GoTsJ z3_Jt_@NyUB9oi?&&uO^l5{36!9#E#h7pPJweg~Q3)Xs;==Karm$mV^>A0gW~V#yNk z|K)mo{zI8=v!wpZgH{6zFO$vBn`>m_zE6||viUjjhsrG;C5_L$D7%%4&)cZ9(r(ET z=Yh|#D32&582_N9^0`6r@X}f4m!UX#oR;==swekB6o1N#*;U)lc^C}c#p;Cajb??7R*{9l&=+r_?AJKrP! znUwzrc8NXYs9<|sd z^;RGduUT3O;5Wn*LJ-efzaRWv;Aj5X{|Rg-`>5di0{in6pKj+c_zez9=o0w5vrmYQ zRKFL|k9Q;DhwmFz@OQy{=t;%b!M`)RU7V$P#rKgx^uv5G$p19;{Svj49@n>|f4jg< zF-dt9{FrAt{+ks4WGepK;NO{ry9@Ojw^a_KKC=Nk;@t_~iQFjxjtOtd55QURLMSyO zFLa{1Q?4}vuL+TrhBH-bOt>|t0v|Fo9d~9{lpFQwniqK$GrxI+GwBDu{G4!3md+d-E;(mToH#!^?u-u~D~$po{qcg8_+-KH zDsJQoIRG3R80yObFgAQf0CmlVQ+0z%%@fYp8HlR*fiu(eDgwp~r%KHUy;`H}Mjkd1 z`DJI?hgfH#*^Jv9KQ+Pwk>62BI)pHrbM?rb03C%&SGnW|aEYdcITg&9Rkv9cW@Rpb zaHXSA)m-+%rr!uQ7!K4yMlJ$`iM$Il@@C)H293y*-A;#% zX)lcC$TfUyEQgaxHmNS9YLd&)pIi6KkW2$QgOfR3gM#V}*bpYns5(rPc%nXB0oMia zCm{b9jV<-GS*YUg#Li1={e-be`4|}ZH=HFWuNS8IJ2Ue0eSv@1+1?NJ_u;pi@w~ox zneeN*D2wx1m^rXj`gRZ9@jE#^Lpk60Sv;Y^C!2s2n7sfJg=Wt2+!*$?ms>LGGK8` zoM(I=#DD&S@VZf1sxL01_Tfn4`TlJXzL+A6+;J1QNK4}RcWS&_h48wT0Xz=2X9yqv zRvMP7GL%Zkv(5So5QhCTfBgI0PI#VwszG=AYePhXSsBxqtqC zuxh9XOR4yr#xFok63^?t1w%ndDFe-2y@%MY_{@7;y6To&R@Vw45 z=zB_zgAgffvwj=AIQO`JUN`agn6J_N!TFEEIG#s82Oowop1(s|>BVUE4oj8PnenWD z0q!K8*I}!K=lcY=!8q1;Q}}p2l~)MPGmU5a9u%-G&Y#yUU4-X;IG*TWQ>;M=eH0%1 z8K3_9WqeZe+>6239yK^iiQ)6&Za6p}<^@(xaqduzWuadczsns_P0EC| Result { + trace!( + "Reading next token: {}", + self.get_substring_from(self.position..) + ); let whitespace = self.skip_whitespace(); if whitespace.is_some() { return whitespace diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 465757e..44e61df 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -3,6 +3,7 @@ use std::collections::VecDeque; use ast::Tree; use error::ParseError; use symbols::Name; +use tracing::trace; use types::Type; use crate::lexer::token::{KeywordType, OperatorType, SeperatorType, Token, MAX_PRECEDENCE}; @@ -102,11 +103,20 @@ fn parse_statement(tokens: &mut VecDeque) -> Result { fn parse_control(tokens: &mut VecDeque) -> Result { let keyword = tokens.front().unwrap(); let expression = if keyword.is_keyword(&KeywordType::Return) { - parse_return(tokens) + let result = parse_return(tokens); + expect_seperator(tokens, SeperatorType::Semicolon) + .ok_or(ParseError::Error("Expected Semicolon".to_string()))?; + result } else if keyword.is_keyword(&KeywordType::Break) { - parse_break(tokens) + let result = parse_break(tokens); + expect_seperator(tokens, SeperatorType::Semicolon) + .ok_or(ParseError::Error("Expected Semicolon".to_string()))?; + result } else if keyword.is_keyword(&KeywordType::Continue) { - parse_continue(tokens) + let result = parse_continue(tokens); + expect_seperator(tokens, SeperatorType::Semicolon) + .ok_or(ParseError::Error("Expected Semicolon".to_string()))?; + result } else if keyword.is_keyword(&KeywordType::For) { parse_for(tokens) } else if keyword.is_keyword(&KeywordType::While) { @@ -116,7 +126,8 @@ fn parse_control(tokens: &mut VecDeque) -> Result { } else { Err(ParseError::Error("Expected control keyword".to_string())) }; - expect_seperator(tokens, SeperatorType::Semicolon); + trace!("Finished parsing control structure: {:?}", tokens); + expression } @@ -142,6 +153,7 @@ fn parse_decleration(tokens: &mut VecDeque) -> Result { } fn parse_return(tokens: &mut VecDeque) -> Result { + trace!("Parsing return: {:?}", tokens); let return_keyword = expect_keyword(tokens, KeywordType::Return) .ok_or(ParseError::Error("Expected keyword".to_string()))?; let expression = parse_expression(tokens)?; From 3d9edbfa24c194fa948ec26f16b5916652f36908 Mon Sep 17 00:00:00 2001 From: Felix Schwickerath Date: Sat, 14 Jun 2025 16:00:01 +0200 Subject: [PATCH 06/52] feat(ir/codegen): implement for --- src/ir/constructor.rs | 46 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/src/ir/constructor.rs b/src/ir/constructor.rs index a60d0cc..af8bc46 100644 --- a/src/ir/constructor.rs +++ b/src/ir/constructor.rs @@ -14,7 +14,7 @@ use crate::{ }, lexer::token::{OperatorType, Token}, parser::{ast::Tree, symbols::Name}, - util::int_parsing::parse_int, + util::{int_parsing::parse_int, position}, }; use super::{ @@ -391,8 +391,48 @@ impl IRGraphConstructor { self.seal_block(self.current_block_index); None } - Tree::For(_option_initializer, _comparison, _option_postincrement, _statement, _) => { - todo!("Implement for") + Tree::For(option_initializer, comparison, option_postincrement, statement, _) => { + if let Some(initializer) = option_initializer { + self.convert_boxed(initializer); + } + + self.seal_block(self.current_block_index); + let condition_block = Block::new("for-condition".to_string()); + let condition_block_index = self.graph.register_block(condition_block); + self.current_block_index = condition_block_index; + let condition_node = self.convert_boxed(comparison)?; + let conditional_jump = self.create_conditional_jump(condition_node); + + let true_projection = self.create_true_projection(conditional_jump); + let false_projection = self.create_false_projection(conditional_jump); + + let mut loop_body = Block::new("for-body".to_string()); + loop_body.register_entry_point(condition_block_index, true_projection); + let loop_body_index = self.graph.register_block(loop_body); + self.seal_block(loop_body_index); + self.current_block_index = loop_body_index; + self.active_loops.push(condition_block_index); + self.convert_boxed(statement); + let loop_exit = self.create_jump(); + + let mut loop_postincrement = Block::new("for-post".to_string()); + loop_postincrement.register_entry_point(loop_body_index, loop_exit); + let loop_postincrement_index = self.graph.register_block(loop_postincrement); + self.seal_block(loop_postincrement_index); + self.current_block_index = loop_postincrement_index; + if let Some(postincrement) = option_postincrement { + self.convert_boxed(postincrement); + } + let loop_jump = self.create_jump(); + self.graph + .get_block_mut(condition_block_index) + .register_entry_point(loop_postincrement_index, loop_jump); + self.seal_block(condition_block_index); + + let mut following_block = Block::new("for-following".to_string()); + following_block.register_entry_point(condition_block_index, false_projection); + self.current_block_index = self.graph.register_block(following_block); + None } #[allow(unreachable_patterns)] node => todo!("Unimplemented {:?}", node), From e836bd360d6879631e26ba4096556572f1f1c945 Mon Sep 17 00:00:00 2001 From: Felix Schwickerath Date: Sat, 14 Jun 2025 16:05:56 +0200 Subject: [PATCH 07/52] feat(codegen): implement missing comparison operators --- src/backend/codegen.rs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/backend/codegen.rs b/src/backend/codegen.rs index dc92fca..a93ee7a 100644 --- a/src/backend/codegen.rs +++ b/src/backend/codegen.rs @@ -262,16 +262,14 @@ impl CodeGenerator { "sarl", )); } - Node::Equals(data) => { + Node::Equals(data) + | Node::HigherEquals(data) + | Node::LowerEquals(data) + | Node::NotEquals(data) + | Node::Lower(data) + | Node::Higher(data) => { code.push_str(&self.generate_comparison(block, data, registers)); } - Node::LowerEquals(data) => { - code.push_str(&self.generate_comparison(block, data, registers)); - } - Node::Lower(data) => code.push_str(&self.generate_comparison(block, data, registers)), - Node::HigherEquals(data) => { - code.push_str(&self.generate_comparison(block, data, registers)) - } Node::ConstantBool(data) => code.push_str(&self.generate_constant_bool(data.value())), Node::Phi(data) => { debug!("Warning! Phi present: Aliasing {:?}", data.operands()); From cad8fb72cc7338ac010133aaab57d4f28918b7bd Mon Sep 17 00:00:00 2001 From: Felix Schwickerath Date: Sat, 14 Jun 2025 17:21:11 +0200 Subject: [PATCH 08/52] fix(phi): fixes for reading/writing with phis --- src/backend/regalloc.rs | 1 + src/ir/constructor.rs | 74 ++++++++++++++++++++++++----------------- 2 files changed, 45 insertions(+), 30 deletions(-) diff --git a/src/backend/regalloc.rs b/src/backend/regalloc.rs index 005e42f..86f4dfb 100644 --- a/src/backend/regalloc.rs +++ b/src/backend/regalloc.rs @@ -299,6 +299,7 @@ fn needs_register(node: &Node) -> bool { | Node::LowerEquals(_) | Node::Equals(_) | Node::ConditionalJump(_) + | Node::Jump ) } diff --git a/src/ir/constructor.rs b/src/ir/constructor.rs index af8bc46..6b7d249 100644 --- a/src/ir/constructor.rs +++ b/src/ir/constructor.rs @@ -5,7 +5,7 @@ use tracing::{debug, info, trace}; use crate::{ ir::{ - block::Block, + block::{self, Block}, graph::START_BLOCK, node::{ binary_operation::BinaryOperationData, @@ -391,47 +391,58 @@ impl IRGraphConstructor { self.seal_block(self.current_block_index); None } - Tree::For(option_initializer, comparison, option_postincrement, statement, _) => { + Tree::For(option_initializer, condition, option_postincrement, expression, _) => { + debug!("Generating IR for for expression"); if let Some(initializer) = option_initializer { self.convert_boxed(initializer); } + let inverted_entry_condition_node = + self.create_inverted_condition(condition.clone()); + let entry_conditional_jump = self.create_conditional_jump(( + self.current_block_index, + inverted_entry_condition_node, + )); - self.seal_block(self.current_block_index); - let condition_block = Block::new("for-condition".to_string()); - let condition_block_index = self.graph.register_block(condition_block); - self.current_block_index = condition_block_index; - let condition_node = self.convert_boxed(comparison)?; - let conditional_jump = self.create_conditional_jump(condition_node); - - let true_projection = self.create_true_projection(conditional_jump); - let false_projection = self.create_false_projection(conditional_jump); + let entry_true_projection = self.create_true_projection(entry_conditional_jump); + let entry_false_projection = self.create_false_projection(entry_conditional_jump); let mut loop_body = Block::new("for-body".to_string()); - loop_body.register_entry_point(condition_block_index, true_projection); + loop_body.register_entry_point(self.current_block_index, entry_false_projection); + let mut following_block = Block::new("for-following".to_string()); + following_block + .register_entry_point(self.current_block_index, entry_true_projection); + self.seal_block(self.current_block_index); + let loop_body_index = self.graph.register_block(loop_body); - self.seal_block(loop_body_index); self.current_block_index = loop_body_index; - self.active_loops.push(condition_block_index); - self.convert_boxed(statement); - let loop_exit = self.create_jump(); - - let mut loop_postincrement = Block::new("for-post".to_string()); - loop_postincrement.register_entry_point(loop_body_index, loop_exit); - let loop_postincrement_index = self.graph.register_block(loop_postincrement); - self.seal_block(loop_postincrement_index); - self.current_block_index = loop_postincrement_index; + self.convert_boxed(expression); + let loop_body_exit = self.create_jump(); + + let mut loop_post = Block::new("for-post".to_string()); + loop_post.register_entry_point(loop_body_index, loop_body_exit); + let loop_post_index = self.graph.register_block(loop_post); + self.current_block_index = loop_post_index; + self.seal_block(loop_post_index); + if let Some(postincrement) = option_postincrement { self.convert_boxed(postincrement); } - let loop_jump = self.create_jump(); + + let condition_node = self.convert_boxed(condition)?; + let conditional_jump = self.create_conditional_jump(condition_node); + + let true_projection = self.create_true_projection(conditional_jump); + let false_projection = self.create_false_projection(conditional_jump); + self.graph - .get_block_mut(condition_block_index) - .register_entry_point(loop_postincrement_index, loop_jump); - self.seal_block(condition_block_index); + .get_block_mut(loop_body_index) + .register_entry_point(loop_post_index, true_projection); - let mut following_block = Block::new("for-following".to_string()); - following_block.register_entry_point(condition_block_index, false_projection); + following_block.register_entry_point(loop_post_index, false_projection); + self.seal_block(loop_body_index); self.current_block_index = self.graph.register_block(following_block); + self.seal_block(self.current_block_index); + debug!("Following block after for: {}", self.current_block_index); None } #[allow(unreachable_patterns)] @@ -756,7 +767,7 @@ impl IRGraphConstructor { variable, operands ); - let current_block = self.graph.get_block_mut(self.current_block_index); + let current_block = self.graph.get_block_mut(block_index); current_block.register_node(Node::Phi(PhiData::new(operands))) } @@ -832,7 +843,10 @@ impl IRGraphConstructor { ); trace!("Sealed blocks: {:?}", self.sealed_blocks); let node = if !self.sealed_blocks.contains(&block_index) { - let phi = self.create_phi(); + let phi = self + .graph + .get_block_mut(block_index) + .register_node(Node::Phi(PhiData::empty())); trace!("Writing incomplete phi: ({:?}, {})", variable, phi); if self.incomplete_phis.contains_key(&block_index) { let mut entry = self.incomplete_phis.get_mut(&block_index).unwrap().clone(); From 7fab231eeca63cf82b510b26c066868fab08fb02 Mon Sep 17 00:00:00 2001 From: Felix Schwickerath Date: Sat, 14 Jun 2025 17:23:51 +0200 Subject: [PATCH 09/52] fix(codegen): fix missing newlines for shift operations --- src/backend/codegen.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/backend/codegen.rs b/src/backend/codegen.rs index a93ee7a..5098df0 100644 --- a/src/backend/codegen.rs +++ b/src/backend/codegen.rs @@ -491,14 +491,14 @@ impl CodeGenerator { if !left_value.hardware_register() && !right_value.hardware_register() { code.push_str(&move_stack_variable(right_value)); code.push_str(&format!( - "{} {}, {}", + "{} {}, {}\n", op_code, left_value.as_assembly(), HardwareRegister::Rbx.as_assembly_16_bit() )); } else { code.push_str(&format!( - "{} {}, {}", + "{} {}, {}\n", op_code, left_value.as_assembly(), right_value.as_16_bit_assembly() @@ -506,7 +506,7 @@ impl CodeGenerator { } let destination = registers.get(&(block_index, node_index)).unwrap(); code.push_str(&format!( - "movq {}, {}", + "movq {}, {}\n", left_value.as_assembly(), destination.as_assembly() )); From bc51776b967d8a8044a6e84fa56ec9802fda0b2c Mon Sep 17 00:00:00 2001 From: Felix Schwickerath Date: Sat, 14 Jun 2025 17:41:53 +0200 Subject: [PATCH 10/52] fix(codegen): fix assembly for shift operations --- src/backend/codegen.rs | 31 +++++++++++++------------------ src/backend/regalloc.rs | 20 +++++++++++++++++++- 2 files changed, 32 insertions(+), 19 deletions(-) diff --git a/src/backend/codegen.rs b/src/backend/codegen.rs index 5098df0..9c88b87 100644 --- a/src/backend/codegen.rs +++ b/src/backend/codegen.rs @@ -248,7 +248,7 @@ impl CodeGenerator { ir_graph, data, registers, - "sall", + "sal", )); } Node::ShiftRight(data) => { @@ -259,7 +259,7 @@ impl CodeGenerator { ir_graph, data, registers, - "sarl", + "sar", )); } Node::Equals(data) @@ -488,22 +488,17 @@ impl CodeGenerator { let mut code = String::new(); let left_value = registers.get(&data.lhs()).unwrap(); let right_value = registers.get(&data.rhs()).unwrap(); - if !left_value.hardware_register() && !right_value.hardware_register() { - code.push_str(&move_stack_variable(right_value)); - code.push_str(&format!( - "{} {}, {}\n", - op_code, - left_value.as_assembly(), - HardwareRegister::Rbx.as_assembly_16_bit() - )); - } else { - code.push_str(&format!( - "{} {}, {}\n", - op_code, - left_value.as_assembly(), - right_value.as_16_bit_assembly() - )); - } + code.push_str(&format!( + "movq {}, {}\n", + right_value.as_assembly(), + HardwareRegister::Rcx.as_assembly() + )); + code.push_str(&format!( + "{} {}, {}\n", + op_code, + HardwareRegister::Rcx.as_8_bit_assembly(), + left_value.as_assembly() + )); let destination = registers.get(&(block_index, node_index)).unwrap(); code.push_str(&format!( "movq {}, {}\n", diff --git a/src/backend/regalloc.rs b/src/backend/regalloc.rs index 86f4dfb..f8eeda0 100644 --- a/src/backend/regalloc.rs +++ b/src/backend/regalloc.rs @@ -14,6 +14,7 @@ pub trait Register { fn as_assembly(&self) -> String; fn as_32_bit_assembly(&self) -> String; fn as_16_bit_assembly(&self) -> String; + fn as_8_bit_assembly(&self) -> String; fn hardware_register(&self) -> bool; fn box_clone(&self) -> Box; } @@ -93,6 +94,15 @@ impl HardwareRegister { HardwareRegister::R15 => "r15w".to_string(), } } + + pub fn as_assembly_8_bit(&self) -> String { + match self { + HardwareRegister::Rax => "al".to_string(), + HardwareRegister::Rbx => "bl".to_string(), + HardwareRegister::Rcx => "cl".to_string(), + _ => panic!(), + } + } } impl Register for HardwareRegister { @@ -111,6 +121,10 @@ impl Register for HardwareRegister { format!("%{}", self.as_assembly_16_bit()) } + fn as_8_bit_assembly(&self) -> String { + format!("%{}", self.as_assembly_8_bit()) + } + fn box_clone(&self) -> Box { Box::new(self.clone()) } @@ -144,6 +158,10 @@ impl Register for StackRegister { self.as_assembly() } + fn as_8_bit_assembly(&self) -> String { + self.as_assembly() + } + fn hardware_register(&self) -> bool { false } @@ -176,7 +194,7 @@ impl RegisterAllocator { available_hardware_register: vec![ //HardwareRegister::Rax, //HardwareRegister::Rbx, - HardwareRegister::Rcx, + //HardwareRegister::Rcx, //HardwareRegister::Rdx, HardwareRegister::Rsi, HardwareRegister::Rdi, From c1bd5a95607422d9bf3fbd100af7d5e0ebd6b6b1 Mon Sep 17 00:00:00 2001 From: Felix Schwickerath Date: Sun, 15 Jun 2025 12:05:30 +0200 Subject: [PATCH 11/52] fix: fix expecting only int types --- src/backend/codegen.rs | 11 ++++++++--- src/parser/mod.rs | 20 +++++++++++++++----- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/src/backend/codegen.rs b/src/backend/codegen.rs index 9c88b87..edf9a22 100644 --- a/src/backend/codegen.rs +++ b/src/backend/codegen.rs @@ -268,7 +268,7 @@ impl CodeGenerator { | Node::NotEquals(data) | Node::Lower(data) | Node::Higher(data) => { - code.push_str(&self.generate_comparison(block, data, registers)); + code.push_str(&self.generate_comparison(block, data, ir_graph, registers)); } Node::ConstantBool(data) => code.push_str(&self.generate_constant_bool(data.value())), Node::Phi(data) => { @@ -353,10 +353,15 @@ impl CodeGenerator { &self, block: &Block, operation_data: &BinaryOperationData, + ir_graph: &IRGraph, registers: &Registers, ) -> String { - let left_value = registers.get(&operation_data.lhs()).unwrap(); - let right_value = registers.get(&operation_data.rhs()).unwrap(); + let left_value = registers + .get(&predecessor_skip_projection(ir_graph, operation_data.lhs())) + .unwrap(); + let right_value = registers + .get(&predecessor_skip_projection(ir_graph, operation_data.rhs())) + .unwrap(); let mut code = String::new(); if !left_value.hardware_register() && !right_value.hardware_register() { code.push_str(&move_stack_variable(left_value)); diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 44e61df..7a871f7 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -132,8 +132,8 @@ fn parse_control(tokens: &mut VecDeque) -> Result { } fn parse_decleration(tokens: &mut VecDeque) -> Result { - let type_token = expect_keyword(tokens, KeywordType::Int) - .ok_or(ParseError::Error("Expected keyword".to_string()))?; + let type_token = + expect_type(tokens).ok_or(ParseError::Error("Expected type keyword".to_string()))?; let identifier = expect_identifier(tokens).ok_or(ParseError::Error("Expected identifier".to_string()))?; let mut expression = None; @@ -155,7 +155,7 @@ fn parse_decleration(tokens: &mut VecDeque) -> Result { fn parse_return(tokens: &mut VecDeque) -> Result { trace!("Parsing return: {:?}", tokens); let return_keyword = expect_keyword(tokens, KeywordType::Return) - .ok_or(ParseError::Error("Expected keyword".to_string()))?; + .ok_or(ParseError::Error("Expected keyword RETURN".to_string()))?; let expression = parse_expression(tokens)?; Ok(Tree::Return( expression, @@ -165,13 +165,13 @@ fn parse_return(tokens: &mut VecDeque) -> Result { fn parse_break(tokens: &mut VecDeque) -> Result { let break_keyword = expect_keyword(tokens, KeywordType::Break) - .ok_or(ParseError::Error("Expected keyword".to_string()))?; + .ok_or(ParseError::Error("Expected keyword BREAK".to_string()))?; Ok(Tree::Break(break_keyword.span())) } fn parse_continue(tokens: &mut VecDeque) -> Result { let continue_keyword = expect_keyword(tokens, KeywordType::Continue) - .ok_or(ParseError::Error("Expected keyword".to_string()))?; + .ok_or(ParseError::Error("Expected keyword CONTINUE".to_string()))?; Ok(Tree::Continue(continue_keyword.span())) } @@ -442,6 +442,16 @@ fn expect_identifier(tokens: &mut VecDeque) -> Option { None } +fn expect_type(tokens: &mut VecDeque) -> Option { + if let Some(token) = tokens.front() { + match token { + Token::Keyword(_, keyword_type) if keyword_type.is_type() => return tokens.pop_front(), + _ => return None, + } + } + None +} + fn consume(tokens: &mut VecDeque) -> Option { tokens.pop_front() } From c8a1a34c5ff9de8fe7b50221d47999f9a90ad4f5 Mon Sep 17 00:00:00 2001 From: Felix Schwickerath Date: Sun, 15 Jun 2025 16:42:46 +0200 Subject: [PATCH 12/52] fix(phi): finally understand when which phi should be moved to --- run-tests.sh | 2 +- src/backend/codegen.rs | 150 +++++++++++++++---- src/backend/regalloc.rs | 41 ++---- src/ir/block.rs | 9 ++ src/ir/constructor.rs | 248 +++++++++++++------------------- src/ir/node/binary_operation.rs | 19 ++- src/ir/node/mod.rs | 24 ++-- src/ir/node/projection.rs | 13 +- src/ir/node/unary_operation.rs | 13 +- 9 files changed, 272 insertions(+), 247 deletions(-) diff --git a/run-tests.sh b/run-tests.sh index fe019a7..ab465a0 100755 --- a/run-tests.sh +++ b/run-tests.sh @@ -1,3 +1,3 @@ #!/usr/bin/env fish cargo build --release -nix run github:I-Al-Istannen/crow#client -- run-tests --test-dir tests/ --compiler-run ./run.sh +nix run github:I-Al-Istannen/crow#client -- run-tests --test-dir tests/ --compiler-run ./run.sh --only-failing diff --git a/src/backend/codegen.rs b/src/backend/codegen.rs index edf9a22..3a0eedf 100644 --- a/src/backend/codegen.rs +++ b/src/backend/codegen.rs @@ -13,7 +13,7 @@ use crate::ir::{ use super::regalloc::{HardwareRegister, Register, RegisterAllocator}; -type Registers<'a> = HashMap<(BlockIndex, NodeIndex), Box>; +pub type Registers = HashMap<(BlockIndex, NodeIndex), Box>; const TEMPLATE: &str = " .section .note.GNU-stack,\"\",@progbits .global main @@ -135,7 +135,7 @@ impl CodeGenerator { ir_graph: &IRGraph, registers: &Registers, ) -> String { - // Start and End Nodes should not emit code + // End Node should not emit code if block.get_nodes().is_empty() { return String::new(); } @@ -143,6 +143,7 @@ impl CodeGenerator { let mut code = String::new(); let block_label = self.jump_label.get(&block_index).unwrap(); code.push_str(&format!("{}:\n", block_label)); + for (node_index, node) in block.get_nodes().iter().enumerate() { code.push_str(&self.generate_for_node( node, @@ -229,7 +230,7 @@ impl CodeGenerator { )); } Node::Return(data) => { - code.push_str(&self.generate_return(ir_graph, data, registers)); + code.push_str(&self.generate_return(block, block_index, data, registers)); } Node::ConstantInt(data) => { code.push_str(&self.generate_constant_int( @@ -268,19 +269,33 @@ impl CodeGenerator { | Node::NotEquals(data) | Node::Lower(data) | Node::Higher(data) => { - code.push_str(&self.generate_comparison(block, data, ir_graph, registers)); + code.push_str(&self.generate_comparison( + block, + block_index, + data, + ir_graph, + registers, + )); } - Node::ConstantBool(data) => code.push_str(&self.generate_constant_bool(data.value())), - Node::Phi(data) => { - debug!("Warning! Phi present: Aliasing {:?}", data.operands()); + Node::BitwiseNegate(data) => { + let register = registers.get(&(block_index, data.input())).unwrap(); + code.push_str(&format!("not {}", register.as_assembly())); } + Node::ConstantBool(data) => code.push_str(&self.generate_constant_bool(data.value())), + Node::Phi(data) => {} Node::Jump => { trace!( "Generating assembly for jump: {} with destination XXX", node, ); - let previous_block_index = jump_information.get(&node_index).unwrap(); - let label = self.jump_label.get(previous_block_index).unwrap(); + let following_block_index = jump_information.get(&node_index).unwrap(); + let label = self.jump_label.get(following_block_index).unwrap(); + code.push_str(&self.generate_phi_moves( + block_index, + *following_block_index, + registers, + ir_graph, + )); code.push_str(&format!("jmp {}\n", label)); } Node::ConditionalJump(_) => {} @@ -289,19 +304,37 @@ impl CodeGenerator { "Generating IR for true projection (including jump) with jump information {:?}", jump_information ); - let previous_block_index = jump_information.get(&node_index).unwrap(); - let conditional_jump_code = - self.generate_conditional_jump(node_index, block, *previous_block_index); + let following_block_index = jump_information.get(&node_index).unwrap(); + let conditional_jump_code = self.generate_conditional_jump( + node_index, + block, + block_index, + *following_block_index, + registers, + ); + code.push_str(&self.generate_phi_moves( + block_index, + *following_block_index, + registers, + ir_graph, + )); code.push_str(&conditional_jump_code.expect("Expected jump code")); } Node::Projection(data) if data.projection_info().eq(&ProjectionInformation::IfFalse) => { - let previous_block_index = jump_information.get(&node_index).unwrap(); + let following_block_index = jump_information.get(&node_index).unwrap(); let jump_label = self .jump_label - .get(previous_block_index) + .get(following_block_index) .expect("Expected jump label for false if"); + code.push_str(&self.generate_phi_moves( + block_index, + *following_block_index, + registers, + ir_graph, + )); + code.push_str(&format!("jmp {}\n", jump_label)); } Node::Projection(_) => return code, @@ -311,6 +344,42 @@ impl CodeGenerator { code } + pub fn generate_phi_moves( + &self, + current_block_index: BlockIndex, + following_block_index: BlockIndex, + registers: &Registers, + ir_graph: &IRGraph, + ) -> String { + let mut code = String::new(); + let following_block = ir_graph.get_block(following_block_index); + for phi_index in following_block.phis() { + let phi = following_block.get_node(*phi_index); + if let Node::Phi(data) = phi { + let destination_register = + registers.get(&(following_block_index, *phi_index)).unwrap(); + for operand in data.operands() { + if operand.0 != current_block_index { + continue; + } + let source_register = registers.get(&operand).unwrap(); + code.push_str(&format!( + "movq {}, {}\n", + source_register.as_assembly(), + destination_register.as_assembly() + )); + break; + } + } + } + trace!( + "Generated the following phi moves for block {}: {}", + current_block_index, + code + ); + code + } + pub fn generate_constant_bool(&self, value: bool) -> String { let mut code = String::new(); if value { @@ -325,7 +394,9 @@ impl CodeGenerator { &self, projection_index: usize, current_block: &Block, + current_block_index: BlockIndex, previous_block: usize, + registers: &Registers, ) -> Option { let mut code = String::new(); let true_label = self.jump_label.get(&previous_block).unwrap(); @@ -343,6 +414,13 @@ impl CodeGenerator { Node::HigherEquals(_) => "jae", Node::Higher(_) => "ja", Node::ConstantBool(_) => "je", + Node::BitwiseNegate(_) => "jne", + Node::Phi(_) | Node::ConstantInt(_) => { + let register = registers.get(&(current_block_index, comparision)).unwrap(); + code.push_str(&format!("testq $0x1, {}\n", register.as_assembly())); + code.push_str(&format!("jnz {}\n", true_label)); + return Some(code); + } node => panic!("Invalid operation before conditional jump: {}", node), }; code.push_str(&format!("{} {}\n", op_code, true_label)); @@ -352,15 +430,22 @@ impl CodeGenerator { pub fn generate_comparison( &self, block: &Block, + block_index: BlockIndex, operation_data: &BinaryOperationData, ir_graph: &IRGraph, registers: &Registers, ) -> String { let left_value = registers - .get(&predecessor_skip_projection(ir_graph, operation_data.lhs())) + .get(&( + block_index, + predecessor_skip_projection(block, operation_data.lhs()), + )) .unwrap(); let right_value = registers - .get(&predecessor_skip_projection(ir_graph, operation_data.rhs())) + .get(&( + block_index, + predecessor_skip_projection(block, operation_data.rhs()), + )) .unwrap(); let mut code = String::new(); if !left_value.hardware_register() && !right_value.hardware_register() { @@ -393,10 +478,10 @@ impl CodeGenerator { op_code: &str, ) -> String { let left_value = registers - .get(&predecessor_skip_projection(ir_graph, data.lhs())) + .get(&(block_index, predecessor_skip_projection(block, data.lhs()))) .unwrap(); let right_value = registers - .get(&predecessor_skip_projection(ir_graph, data.rhs())) + .get(&(block_index, predecessor_skip_projection(block, data.rhs()))) .unwrap(); let destination_register = registers.get(&(block_index, node_index)).unwrap(); @@ -444,10 +529,10 @@ impl CodeGenerator { mode: &str, ) -> String { let left_value = registers - .get(&predecessor_skip_projection(ir_graph, data.lhs())) + .get(&(block_index, predecessor_skip_projection(block, data.lhs()))) .unwrap(); let right_value = registers - .get(&predecessor_skip_projection(ir_graph, data.rhs())) + .get(&(block_index, predecessor_skip_projection(block, data.rhs()))) .unwrap(); let destination_register = registers.get(&(block_index, node_index)).unwrap(); let mut code = String::new(); @@ -491,8 +576,8 @@ impl CodeGenerator { op_code: &str, ) -> String { let mut code = String::new(); - let left_value = registers.get(&data.lhs()).unwrap(); - let right_value = registers.get(&data.rhs()).unwrap(); + let left_value = registers.get(&(block_index, data.lhs())).unwrap(); + let right_value = registers.get(&(block_index, data.rhs())).unwrap(); code.push_str(&format!( "movq {}, {}\n", right_value.as_assembly(), @@ -515,21 +600,27 @@ impl CodeGenerator { pub fn generate_return( &self, - ir_graph: &IRGraph, + block: &Block, + block_index: BlockIndex, data: &ReturnData, registers: &Registers, ) -> String { debug!("Generating assembly for return"); - let return_node = predecessor_skip_projection(ir_graph, data.input()); + let return_node = predecessor_skip_projection(block, data.input()); debug!( "Determined node {} that contains the return result", - ir_graph.get_node(return_node) + block.get_node(return_node) ); debug!("Registers: {:?}", registers); let mut code = String::new(); code.push_str("mov "); - code.push_str(®isters.get(&return_node).unwrap().as_assembly()); + code.push_str( + ®isters + .get(&(block_index, return_node)) + .unwrap() + .as_assembly(), + ); code.push_str(", %rax"); code.push('\n'); @@ -558,11 +649,8 @@ impl CodeGenerator { } } -fn predecessor_skip_projection( - ir_graph: &IRGraph, - data: (BlockIndex, NodeIndex), -) -> (BlockIndex, NodeIndex) { - let predecessor = ir_graph.get_node(data); +fn predecessor_skip_projection(block: &Block, data: NodeIndex) -> NodeIndex { + let predecessor = block.get_node(data); if let Node::Projection(data) = predecessor { data.input() } else { diff --git a/src/backend/regalloc.rs b/src/backend/regalloc.rs index f8eeda0..051bde6 100644 --- a/src/backend/regalloc.rs +++ b/src/backend/regalloc.rs @@ -5,10 +5,13 @@ use std::{ use tracing::debug; -use crate::ir::{ - block::NodeIndex, - graph::{BlockIndex, IRGraph, END_BLOCK}, - node::Node, +use crate::{ + backend::codegen::Registers, + ir::{ + block::NodeIndex, + graph::{BlockIndex, IRGraph, END_BLOCK}, + node::Node, + }, }; pub trait Register { fn as_assembly(&self) -> String; @@ -179,7 +182,7 @@ impl Display for StackRegister { pub struct RegisterAllocator { current_stack_offset: usize, - registers: HashMap<(BlockIndex, NodeIndex), Box>, + registers: Registers, // Denotes that the key has been aliased by the value aliased_nodes: HashMap<(BlockIndex, NodeIndex), (BlockIndex, NodeIndex)>, available_hardware_register: Vec, @@ -210,10 +213,7 @@ impl RegisterAllocator { } } - pub fn allocate_registers( - mut self, - graph: &IRGraph, - ) -> (HashMap<(BlockIndex, NodeIndex), Box>, usize) { + pub fn allocate_registers(mut self, graph: &IRGraph) -> (Registers, usize) { let mut visited = Vec::new(); self.scan(END_BLOCK, graph, &mut visited); (self.registers, self.current_stack_offset) @@ -270,28 +270,7 @@ impl RegisterAllocator { ir_graph: &IRGraph, register: &Box, ) { - let node = ir_graph.get_node(node_index); - if let Node::Phi(data) = node { - debug!("Handling phi in register allocation: Aliasing the following nodes to store in the same node: {:?}", data.operands()); - for (predecessor_block, predecessor_index) in data.operands() { - if self - .aliased_nodes - .contains_key(&(predecessor_block, predecessor_index)) - { - self.registers.insert( - *self - .aliased_nodes - .get(&(predecessor_block, predecessor_index)) - .unwrap(), - register.box_clone(), - ); - } - self.registers - .insert((predecessor_block, predecessor_index), register.box_clone()); - self.aliased_nodes - .insert((predecessor_block, predecessor_index), node_index); - } - } + return; } pub fn has_available_hardware_register(&self) -> bool { diff --git a/src/ir/block.rs b/src/ir/block.rs index fb22229..4a154a2 100644 --- a/src/ir/block.rs +++ b/src/ir/block.rs @@ -7,6 +7,7 @@ pub type NodeIndex = usize; pub struct Block { // Denote that block can be entered from block (key) at node index (value) within that block entry_points: HashMap, + phis: Vec, nodes: Vec, name: String, } @@ -15,6 +16,7 @@ impl Block { pub fn new(name: String) -> Block { Block { entry_points: HashMap::new(), + phis: Vec::new(), nodes: Vec::new(), name, } @@ -29,6 +31,9 @@ impl Block { } pub fn register_node(&mut self, node: Node) -> NodeIndex { + if let Node::Phi(_) = node { + self.phis.push(self.nodes.len()); + } self.nodes.push(node); return self.nodes.len() - 1; } @@ -47,6 +52,10 @@ impl Block { &self.nodes } + pub fn phis(&self) -> &Vec { + &self.phis + } + pub fn get_last_node_index(&self) -> NodeIndex { self.nodes.len() - 1 } diff --git a/src/ir/constructor.rs b/src/ir/constructor.rs index 6b7d249..9d094dc 100644 --- a/src/ir/constructor.rs +++ b/src/ir/constructor.rs @@ -52,7 +52,7 @@ impl IRGraphConstructor { } } - pub fn convert(&mut self, tree: Tree) -> Option<(BlockIndex, NodeIndex)> { + pub fn convert(&mut self, tree: Tree) -> Option { debug!("Converting AST {} to IR!", tree); match tree { Tree::Program(_) => { @@ -63,7 +63,7 @@ impl IRGraphConstructor { // Function body can be entered from start function block function_body_block.register_entry_point(START_BLOCK, 0); let side_effect_projection = function_body_block.register_node(Node::Projection( - ProjectionData::new((0, 0), ProjectionInformation::SideEffect), + ProjectionData::new(0, ProjectionInformation::SideEffect), )); self.current_block_index = self.graph.register_block(function_body_block); @@ -115,22 +115,18 @@ impl IRGraphConstructor { let lhs = self.read_variable(name.clone(), self.current_block_index); let div = self.create_div(lhs, rhs); - let desugar = self - .create_div_mod_projection((self.current_block_index, div)); + let desugar = self.create_div_mod_projection(div); self.write_variable(name, self.current_block_index, desugar); } OperatorType::AssignMod => { let lhs = self.read_variable(name.clone(), self.current_block_index); let mod_node = self.create_mod(lhs, rhs); - let desugar = self.create_div_mod_projection(( - self.current_block_index, - mod_node, - )); + let desugar = self.create_div_mod_projection(mod_node); self.write_variable(name, self.current_block_index, desugar); } OperatorType::Assign => { - self.write_variable(name, rhs.0, rhs.1); + self.write_variable(name, self.current_block_index, rhs); } _ => panic!("Assignment has no assignment operator"), }; @@ -150,11 +146,11 @@ impl IRGraphConstructor { OperatorType::Mul => self.create_mul(lhs_node, rhs_node), OperatorType::Div => { let div_node = self.create_div(lhs_node, rhs_node); - self.create_div_mod_projection((self.current_block_index, div_node)) + self.create_div_mod_projection(div_node) } OperatorType::Mod => { let mod_node = self.create_mod(lhs_node, rhs_node); - self.create_div_mod_projection((self.current_block_index, mod_node)) + self.create_div_mod_projection(mod_node) } OperatorType::ShiftLeft => self.create_shift_left(lhs_node, rhs_node), OperatorType::ShiftRight => self.create_shift_right(lhs_node, rhs_node), @@ -190,7 +186,7 @@ impl IRGraphConstructor { panic!("Expected binary operator, got ternary operator!") } }; - Some((self.current_block_index, result)) + Some(result) } Tree::Block(statements, _) => { for statement in statements { @@ -206,7 +202,7 @@ impl IRGraphConstructor { if let Tree::Name(name, _) = *identifier { if initializer.is_some() { let rhs = self.convert_boxed(initializer.unwrap()).unwrap(); - self.write_variable(name, rhs.0, rhs.1); + self.write_variable(name, self.current_block_index, rhs); } None } else { @@ -224,7 +220,7 @@ impl IRGraphConstructor { Tree::Literal(constant, base, _) => { let value = parse_int(constant, base)?; let node = self.create_constant_int(value); - Some((self.current_block_index, node)) + Some(node) } Tree::LValueIdentifier(_) => None, Tree::Name(_, _) => None, @@ -232,18 +228,18 @@ impl IRGraphConstructor { OperatorType::Minus => { let node = self.convert_boxed(expression)?; let zero = self.create_constant_int(0); - let result = self.create_sub((self.current_block_index, zero), node); - Some((self.current_block_index, result)) + let result = self.create_sub(zero, node); + Some(result) } OperatorType::BitwiseNot => { let node = self.convert_boxed(expression)?; let result = self.create_bitwise_not(node); - Some((self.current_block_index, result)) + Some(result) } OperatorType::LogicalNot => { let node = self.convert_boxed(expression)?; let result = self.create_bitwise_not(node); - Some((self.current_block_index, result)) + Some(result) } _ => panic!("Unregistered Unary Operation {:?}", operator_type), }, @@ -255,11 +251,11 @@ impl IRGraphConstructor { Tree::Type(_, _) => None, Tree::BoolLiteral(boolean, _) => { let node = if boolean { - self.create_constant_bool(true) + self.create_constant_int(1) } else { - self.create_constant_bool(false) + self.create_constant_int(0) }; - Some((self.current_block_index, node)) + Some(node) } Tree::Break(_) => { debug!( @@ -313,18 +309,19 @@ impl IRGraphConstructor { self.current_block_index = self.graph.register_block(following_block); self.seal_block(true_block_index); self.seal_block(false_block_index); - let phi = self.create_phi_from_operands(vec![false_expression, true_expression]); + let phi = self.create_phi_from_operands(vec![ + (self.current_block_index, false_expression), + (self.current_block_index, true_expression), + ]); self.seal_block(self.current_block_index); - Some((self.current_block_index, phi)) + Some(phi) } Tree::While(condition, expression, _) => { debug!("Generating IR for while"); let inverted_entry_condition_node = self.create_inverted_condition(condition.clone()); - let entry_conditional_jump = self.create_conditional_jump(( - self.current_block_index, - inverted_entry_condition_node, - )); + let entry_conditional_jump = + self.create_conditional_jump(inverted_entry_condition_node); let entry_true_projection = self.create_true_projection(entry_conditional_jump); let entry_false_projection = self.create_false_projection(entry_conditional_jump); @@ -398,10 +395,8 @@ impl IRGraphConstructor { } let inverted_entry_condition_node = self.create_inverted_condition(condition.clone()); - let entry_conditional_jump = self.create_conditional_jump(( - self.current_block_index, - inverted_entry_condition_node, - )); + let entry_conditional_jump = + self.create_conditional_jump(inverted_entry_condition_node); let entry_true_projection = self.create_true_projection(entry_conditional_jump); let entry_false_projection = self.create_false_projection(entry_conditional_jump); @@ -450,11 +445,11 @@ impl IRGraphConstructor { } } - pub fn convert_boxed(&mut self, tree: Box) -> Option<(BlockIndex, NodeIndex)> { + pub fn convert_boxed(&mut self, tree: Box) -> Option { self.convert(*tree) } - pub fn create_conditional_jump(&mut self, condition: (BlockIndex, NodeIndex)) -> NodeIndex { + pub fn create_conditional_jump(&mut self, condition: NodeIndex) -> NodeIndex { let current_block = self.graph.get_block_mut(self.current_block_index); current_block.register_node(Node::ConditionalJump(UnaryOperationData::new(condition))) } @@ -475,11 +470,7 @@ impl IRGraphConstructor { current_block.register_node(Node::Jump) } - fn create_add( - &mut self, - lhs: (BlockIndex, NodeIndex), - rhs: (BlockIndex, NodeIndex), - ) -> NodeIndex { + fn create_add(&mut self, lhs: NodeIndex, rhs: NodeIndex) -> NodeIndex { let current_block = self.graph.get_block_mut(self.current_block_index); current_block.register_node(Node::Add(BinaryOperationData::new(lhs, rhs))) } @@ -522,36 +513,33 @@ impl IRGraphConstructor { BinaryOperationData::new(left_node, right_node), )), OperatorType::LogicalAnd => todo!(), - _ => panic!(), + _ => unimplemented!("Unimplemented inverted operator {:?}", operator), + } + } else if let Tree::IdentifierExpression(_) = *condition { + let variable = self.convert_boxed(condition).unwrap(); + self.create_bitwise_not(variable) + } else if let Tree::BoolLiteral(value, _) = *condition { + if value { + self.create_constant_int(0) + } else { + self.create_constant_int(1) } } else { - panic!() + panic!("Condition is not a binary operation, got {:?}", condition); } } - fn create_sub( - &mut self, - lhs: (BlockIndex, NodeIndex), - rhs: (BlockIndex, NodeIndex), - ) -> NodeIndex { + fn create_sub(&mut self, lhs: NodeIndex, rhs: NodeIndex) -> NodeIndex { let current_block = self.graph.get_block_mut(self.current_block_index); current_block.register_node(Node::Subtraction(BinaryOperationData::new(lhs, rhs))) } - fn create_mul( - &mut self, - lhs: (BlockIndex, NodeIndex), - rhs: (BlockIndex, NodeIndex), - ) -> NodeIndex { + fn create_mul(&mut self, lhs: NodeIndex, rhs: NodeIndex) -> NodeIndex { let current_block = self.graph.get_block_mut(self.current_block_index); current_block.register_node(Node::Multiplication(BinaryOperationData::new(lhs, rhs))) } - fn create_div( - &mut self, - lhs: (BlockIndex, NodeIndex), - rhs: (BlockIndex, NodeIndex), - ) -> NodeIndex { + fn create_div(&mut self, lhs: NodeIndex, rhs: NodeIndex) -> NodeIndex { let sideeffect = self.read_current_side_effect(); let current_block = self.graph.get_block_mut(self.current_block_index); current_block.register_node(Node::Division(BinaryOperationData::new_with_sideeffect( @@ -559,11 +547,7 @@ impl IRGraphConstructor { ))) } - fn create_mod( - &mut self, - lhs: (BlockIndex, NodeIndex), - rhs: (BlockIndex, NodeIndex), - ) -> NodeIndex { + fn create_mod(&mut self, lhs: NodeIndex, rhs: NodeIndex) -> NodeIndex { let sideeffect = self.read_current_side_effect(); let current_block = self.graph.get_block_mut(self.current_block_index); current_block.register_node(Node::Modulo(BinaryOperationData::new_with_sideeffect( @@ -571,11 +555,7 @@ impl IRGraphConstructor { ))) } - fn create_shift_left( - &mut self, - lhs: (BlockIndex, NodeIndex), - rhs: (BlockIndex, NodeIndex), - ) -> NodeIndex { + fn create_shift_left(&mut self, lhs: NodeIndex, rhs: NodeIndex) -> NodeIndex { let sideeffect = self.read_current_side_effect(); let current_block = self.graph.get_block_mut(self.current_block_index); current_block.register_node(Node::ShiftLeft(BinaryOperationData::new_with_sideeffect( @@ -583,11 +563,7 @@ impl IRGraphConstructor { ))) } - fn create_shift_right( - &mut self, - lhs: (BlockIndex, NodeIndex), - rhs: (BlockIndex, NodeIndex), - ) -> NodeIndex { + fn create_shift_right(&mut self, lhs: NodeIndex, rhs: NodeIndex) -> NodeIndex { let sideeffect = self.read_current_side_effect(); let current_block = self.graph.get_block_mut(self.current_block_index); current_block.register_node(Node::ShiftRight(BinaryOperationData::new_with_sideeffect( @@ -595,11 +571,7 @@ impl IRGraphConstructor { ))) } - fn create_lower( - &mut self, - lhs: (BlockIndex, NodeIndex), - rhs: (BlockIndex, NodeIndex), - ) -> NodeIndex { + fn create_lower(&mut self, lhs: NodeIndex, rhs: NodeIndex) -> NodeIndex { let sideeffect = self.read_current_side_effect(); let current_block = self.graph.get_block_mut(self.current_block_index); current_block.register_node(Node::Lower(BinaryOperationData::new_with_sideeffect( @@ -607,11 +579,7 @@ impl IRGraphConstructor { ))) } - fn create_lower_equals( - &mut self, - lhs: (BlockIndex, NodeIndex), - rhs: (BlockIndex, NodeIndex), - ) -> NodeIndex { + fn create_lower_equals(&mut self, lhs: NodeIndex, rhs: NodeIndex) -> NodeIndex { let sideeffect = self.read_current_side_effect(); let current_block = self.graph.get_block_mut(self.current_block_index); current_block.register_node(Node::LowerEquals(BinaryOperationData::new_with_sideeffect( @@ -619,11 +587,7 @@ impl IRGraphConstructor { ))) } - fn create_equals( - &mut self, - lhs: (BlockIndex, NodeIndex), - rhs: (BlockIndex, NodeIndex), - ) -> NodeIndex { + fn create_equals(&mut self, lhs: NodeIndex, rhs: NodeIndex) -> NodeIndex { let sideeffect = self.read_current_side_effect(); let current_block = self.graph.get_block_mut(self.current_block_index); current_block.register_node(Node::Equals(BinaryOperationData::new_with_sideeffect( @@ -631,11 +595,7 @@ impl IRGraphConstructor { ))) } - fn create_not_equals( - &mut self, - lhs: (BlockIndex, NodeIndex), - rhs: (BlockIndex, NodeIndex), - ) -> NodeIndex { + fn create_not_equals(&mut self, lhs: NodeIndex, rhs: NodeIndex) -> NodeIndex { let sideeffect = self.read_current_side_effect(); let current_block = self.graph.get_block_mut(self.current_block_index); current_block.register_node(Node::Lower(BinaryOperationData::new_with_sideeffect( @@ -643,11 +603,7 @@ impl IRGraphConstructor { ))) } - fn create_higher_equals( - &mut self, - lhs: (BlockIndex, NodeIndex), - rhs: (BlockIndex, NodeIndex), - ) -> NodeIndex { + fn create_higher_equals(&mut self, lhs: NodeIndex, rhs: NodeIndex) -> NodeIndex { let sideeffect = self.read_current_side_effect(); let current_block = self.graph.get_block_mut(self.current_block_index); current_block.register_node(Node::HigherEquals( @@ -655,11 +611,7 @@ impl IRGraphConstructor { )) } - fn create_higher( - &mut self, - lhs: (BlockIndex, NodeIndex), - rhs: (BlockIndex, NodeIndex), - ) -> NodeIndex { + fn create_higher(&mut self, lhs: NodeIndex, rhs: NodeIndex) -> NodeIndex { let sideeffect = self.read_current_side_effect(); let current_block = self.graph.get_block_mut(self.current_block_index); current_block.register_node(Node::Higher(BinaryOperationData::new_with_sideeffect( @@ -667,11 +619,7 @@ impl IRGraphConstructor { ))) } - fn create_or( - &mut self, - lhs: (BlockIndex, NodeIndex), - rhs: (BlockIndex, NodeIndex), - ) -> NodeIndex { + fn create_or(&mut self, lhs: NodeIndex, rhs: NodeIndex) -> NodeIndex { let sideeffect = self.read_current_side_effect(); let current_block = self.graph.get_block_mut(self.current_block_index); current_block.register_node(Node::Or(BinaryOperationData::new_with_sideeffect( @@ -679,11 +627,7 @@ impl IRGraphConstructor { ))) } - fn create_and( - &mut self, - lhs: (BlockIndex, NodeIndex), - rhs: (BlockIndex, NodeIndex), - ) -> NodeIndex { + fn create_and(&mut self, lhs: NodeIndex, rhs: NodeIndex) -> NodeIndex { let sideeffect = self.read_current_side_effect(); let current_block = self.graph.get_block_mut(self.current_block_index); current_block.register_node(Node::And(BinaryOperationData::new_with_sideeffect( @@ -691,11 +635,7 @@ impl IRGraphConstructor { ))) } - fn create_xor( - &mut self, - lhs: (BlockIndex, NodeIndex), - rhs: (BlockIndex, NodeIndex), - ) -> NodeIndex { + fn create_xor(&mut self, lhs: NodeIndex, rhs: NodeIndex) -> NodeIndex { let sideeffect = self.read_current_side_effect(); let current_block = self.graph.get_block_mut(self.current_block_index); current_block.register_node(Node::Xor(BinaryOperationData::new_with_sideeffect( @@ -703,7 +643,7 @@ impl IRGraphConstructor { ))) } - fn create_bitwise_not(&mut self, node: (BlockIndex, NodeIndex)) -> NodeIndex { + fn create_bitwise_not(&mut self, node: NodeIndex) -> NodeIndex { let sideeffect = self.read_current_side_effect(); let current_block = self.graph.get_block_mut(self.current_block_index); current_block.register_node(Node::BitwiseNegate( @@ -717,11 +657,11 @@ impl IRGraphConstructor { } fn create_constant_bool(&mut self, value: bool) -> NodeIndex { - let current_block = self.graph.get_block_mut(self.current_block_index); - current_block.register_node(Node::ConstantBool(ConstantBoolData::new(value))) + let start_block = self.graph.get_block_mut(START_BLOCK); + start_block.register_node(Node::ConstantBool(ConstantBoolData::new(value))) } - fn create_return(&mut self, input: (BlockIndex, NodeIndex)) -> NodeIndex { + fn create_return(&mut self, input: NodeIndex) -> NodeIndex { let current_block = self.graph.get_block_mut(self.current_block_index); let return_node_index = current_block.register_node(Node::Return(ReturnData::new(input))); self.graph @@ -757,9 +697,14 @@ impl IRGraphConstructor { block_index: BlockIndex, variable: Name, ) -> NodeIndex { + // Creating the operands for a block, by iterating over its entry points and reading the + // variable there let mut operands = Vec::new(); for (block_index, _) in self.graph.get_block(block_index).entry_points().clone() { - operands.push(self.read_variable(variable.clone(), block_index)); + operands.push(( + block_index, + self.read_variable(variable.clone(), block_index), + )); } trace!( "Created phi operands for block {} whilst reading {:?}: {:?}", @@ -771,7 +716,7 @@ impl IRGraphConstructor { current_block.register_node(Node::Phi(PhiData::new(operands))) } - fn create_div_mod_projection(&mut self, input: (BlockIndex, NodeIndex)) -> NodeIndex { + fn create_div_mod_projection(&mut self, input: NodeIndex) -> NodeIndex { let current_block = self.graph.get_block_mut(self.current_block_index); let projection_side_effect = current_block.register_node(Node::Projection( ProjectionData::new(input, ProjectionInformation::SideEffect), @@ -800,7 +745,7 @@ impl IRGraphConstructor { } } - fn read_variable(&mut self, variable: Name, block: BlockIndex) -> (BlockIndex, NodeIndex) { + fn read_variable(&mut self, variable: Name, block: BlockIndex) -> NodeIndex { trace!( "Trying to read from variable {:?} in block {}", variable, @@ -814,15 +759,12 @@ impl IRGraphConstructor { .contains_key(&block) { trace!("Variable defined in the same block! Returning value"); - ( - block, - *self - .current_definitions - .get(&variable) - .unwrap() - .get(&block) - .unwrap(), - ) + *self + .current_definitions + .get(&variable) + .unwrap() + .get(&block) + .unwrap() } else { self.read_variable_recursive(variable, block) } @@ -831,11 +773,7 @@ impl IRGraphConstructor { } } - fn read_variable_recursive( - &mut self, - variable: Name, - block_index: BlockIndex, - ) -> (BlockIndex, NodeIndex) { + fn read_variable_recursive(&mut self, variable: Name, block_index: BlockIndex) -> NodeIndex { trace!( "Reading variable {:?} recursively in block {}", variable, @@ -843,6 +781,7 @@ impl IRGraphConstructor { ); trace!("Sealed blocks: {:?}", self.sealed_blocks); let node = if !self.sealed_blocks.contains(&block_index) { + // Current block is not sealed yet, the list of operands is not final yet let phi = self .graph .get_block_mut(block_index) @@ -856,22 +795,40 @@ impl IRGraphConstructor { self.incomplete_phis .insert(block_index, HashMap::from([(variable.clone(), phi)])); }; - (block_index, phi) + phi } else if self.graph.get_block(block_index).entry_points().len() == 1 { - let (previous_block, _) = self + // The block we are reading the variable in is sealed and has one previous block. + // We can read the variable from there + let previous_block = self .graph .get_block(block_index) .entry_points() .iter() .last() - .unwrap(); - self.read_variable(variable.clone(), *previous_block) + .unwrap() + .0 + .clone(); + let defining_node = self.read_variable(variable.clone(), previous_block); + let phi = self + .graph + .get_block_mut(block_index) + .register_node(Node::Phi(PhiData::new(vec![( + previous_block, + defining_node, + )]))); + phi } else { + // The block we are reading the variable in has multiple entry points and is sealed. + // The value for the variable can come from multiple previous blocks. + // The value of the variable is dependent on the values of the variable in the previous + // blocks let phi = self.create_phi_variable_operands(block_index, variable.clone()); self.write_variable(variable.clone(), block_index, phi); - (block_index, phi) + phi }; - self.write_variable(variable.clone(), node.0, node.1); + + // Denote that the newly created phi defines the variable in the current block + self.write_variable(variable.clone(), self.current_block_index, node); node } @@ -888,7 +845,8 @@ impl IRGraphConstructor { let mut operands = Vec::new(); let block = self.graph.get_block_mut(*block_index); for (prev_block, _) in block.entry_points().clone() { - operands.push(self.read_variable(variable.clone(), prev_block)); + operands + .push((prev_block, self.read_variable(variable.clone(), prev_block))); } operands }; @@ -956,7 +914,7 @@ impl IRGraphConstructor { pub fn create_true_projection(&mut self, conditional_jump: NodeIndex) -> NodeIndex { let current_block = self.graph.get_block_mut(self.current_block_index); current_block.register_node(Node::Projection(ProjectionData::new( - (self.current_block_index, conditional_jump), + conditional_jump, ProjectionInformation::IfTrue, ))) } @@ -964,7 +922,7 @@ impl IRGraphConstructor { pub fn create_false_projection(&mut self, conditional_jump: NodeIndex) -> NodeIndex { let current_block = self.graph.get_block_mut(self.current_block_index); current_block.register_node(Node::Projection(ProjectionData::new( - (self.current_block_index, conditional_jump), + conditional_jump, ProjectionInformation::IfFalse, ))) } diff --git a/src/ir/node/binary_operation.rs b/src/ir/node/binary_operation.rs index a1263e6..4591509 100644 --- a/src/ir/node/binary_operation.rs +++ b/src/ir/node/binary_operation.rs @@ -1,17 +1,14 @@ use crate::ir::{block::NodeIndex, graph::BlockIndex}; -#[derive(Eq, Hash, PartialEq, Debug)] +#[derive(Eq, Hash, PartialEq, Clone, Debug)] pub struct BinaryOperationData { - left_node: (BlockIndex, NodeIndex), - right_node: (BlockIndex, NodeIndex), + left_node: NodeIndex, + right_node: NodeIndex, sideeffect: Option, } impl BinaryOperationData { - pub fn new( - left_node: (BlockIndex, NodeIndex), - right_node: (BlockIndex, NodeIndex), - ) -> BinaryOperationData { + pub fn new(left_node: NodeIndex, right_node: NodeIndex) -> BinaryOperationData { BinaryOperationData { left_node, right_node, @@ -20,8 +17,8 @@ impl BinaryOperationData { } pub fn new_with_sideeffect( - left_node: (BlockIndex, NodeIndex), - right_node: (BlockIndex, NodeIndex), + left_node: NodeIndex, + right_node: NodeIndex, sideeffect: usize, ) -> BinaryOperationData { BinaryOperationData { @@ -31,11 +28,11 @@ impl BinaryOperationData { } } - pub fn lhs(&self) -> (BlockIndex, NodeIndex) { + pub fn lhs(&self) -> NodeIndex { self.left_node } - pub fn rhs(&self) -> (BlockIndex, NodeIndex) { + pub fn rhs(&self) -> NodeIndex { self.right_node } } diff --git a/src/ir/node/mod.rs b/src/ir/node/mod.rs index 9df850b..d833a44 100644 --- a/src/ir/node/mod.rs +++ b/src/ir/node/mod.rs @@ -10,7 +10,7 @@ use unary_operation::UnaryOperationData; use super::{block::NodeIndex, graph::BlockIndex}; -#[derive(Eq, Hash, PartialEq, Debug)] +#[derive(Eq, Hash, PartialEq, Clone, Debug)] pub enum Node { Add(BinaryOperationData), ConstantInt(ConstantIntData), @@ -42,10 +42,10 @@ impl Node { pub fn predecessors(&self) -> Vec { match self { Node::Jump | Node::ConstantBool(_) | Node::ConstantInt(_) => vec![], - Node::Projection(data) => vec![data.input().1], + Node::Projection(data) => vec![data.input()], Node::Phi(_data) => todo!("What to return?"), - Node::Return(data) => vec![data.input().1], - Node::ConditionalJump(data) | Node::BitwiseNegate(data) => vec![data.input().1], + Node::Return(data) => vec![data.input()], + Node::ConditionalJump(data) | Node::BitwiseNegate(data) => vec![data.input()], Node::Add(data) | Node::Division(data) | Node::ShiftRight(data) @@ -61,12 +61,12 @@ impl Node { | Node::Xor(data) | Node::Or(data) | Node::Subtraction(data) - | Node::ShiftLeft(data) => vec![data.lhs().1, data.rhs().1], + | Node::ShiftLeft(data) => vec![data.lhs(), data.rhs()], } } } -#[derive(Eq, Hash, PartialEq, Debug)] +#[derive(Eq, Hash, PartialEq, Clone, Debug)] pub struct ConstantIntData { value: i32, } @@ -81,7 +81,7 @@ impl ConstantIntData { } } -#[derive(Eq, Hash, PartialEq, Debug)] +#[derive(Eq, Hash, PartialEq, Clone, Debug)] pub struct ConstantBoolData { value: bool, } @@ -96,7 +96,7 @@ impl ConstantBoolData { } } -#[derive(Debug, PartialEq, Eq, Hash)] +#[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct PhiData { operands: Vec<(BlockIndex, NodeIndex)>, } @@ -122,17 +122,17 @@ impl PhiData { } } -#[derive(Eq, Hash, PartialEq, Debug)] +#[derive(Eq, Hash, PartialEq, Clone, Debug)] pub struct ReturnData { - input: (BlockIndex, NodeIndex), + input: NodeIndex, } impl ReturnData { - pub fn new(input: (BlockIndex, NodeIndex)) -> ReturnData { + pub fn new(input: NodeIndex) -> ReturnData { ReturnData { input } } - pub fn input(&self) -> (BlockIndex, NodeIndex) { + pub fn input(&self) -> NodeIndex { self.input } } diff --git a/src/ir/node/projection.rs b/src/ir/node/projection.rs index 1d45137..7917b59 100644 --- a/src/ir/node/projection.rs +++ b/src/ir/node/projection.rs @@ -1,6 +1,6 @@ use crate::ir::{block::NodeIndex, graph::BlockIndex}; -#[derive(Eq, Hash, PartialEq, Debug)] +#[derive(Eq, Hash, PartialEq, Clone, Debug)] pub enum ProjectionInformation { SideEffect, Result, @@ -8,24 +8,21 @@ pub enum ProjectionInformation { IfFalse, } -#[derive(Eq, Hash, PartialEq, Debug)] +#[derive(Eq, Hash, PartialEq, Clone, Debug)] pub struct ProjectionData { - input: (BlockIndex, NodeIndex), + input: NodeIndex, projection_information: ProjectionInformation, } impl ProjectionData { - pub fn new( - input: (BlockIndex, NodeIndex), - projection_information: ProjectionInformation, - ) -> ProjectionData { + pub fn new(input: NodeIndex, projection_information: ProjectionInformation) -> ProjectionData { ProjectionData { input, projection_information, } } - pub fn input(&self) -> (BlockIndex, NodeIndex) { + pub fn input(&self) -> NodeIndex { self.input } diff --git a/src/ir/node/unary_operation.rs b/src/ir/node/unary_operation.rs index be205b2..54e0903 100644 --- a/src/ir/node/unary_operation.rs +++ b/src/ir/node/unary_operation.rs @@ -1,30 +1,27 @@ use crate::ir::{block::NodeIndex, graph::BlockIndex}; -#[derive(Eq, Hash, PartialEq, Debug)] +#[derive(Eq, Hash, PartialEq, Clone, Debug)] pub struct UnaryOperationData { - input: (BlockIndex, NodeIndex), + input: NodeIndex, sideffect: Option, } impl UnaryOperationData { - pub fn new(input: (BlockIndex, NodeIndex)) -> UnaryOperationData { + pub fn new(input: NodeIndex) -> UnaryOperationData { UnaryOperationData { input, sideffect: None, } } - pub fn new_with_sideeffect( - input: (BlockIndex, NodeIndex), - sideeffect: usize, - ) -> UnaryOperationData { + pub fn new_with_sideeffect(input: NodeIndex, sideeffect: usize) -> UnaryOperationData { UnaryOperationData { input, sideffect: Some(sideeffect), } } - pub fn input(&self) -> (BlockIndex, NodeIndex) { + pub fn input(&self) -> NodeIndex { self.input } } From 06a7a794f35f922aaf61ce2881396f951f6f99c8 Mon Sep 17 00:00:00 2001 From: Felix Schwickerath Date: Sun, 15 Jun 2025 16:57:04 +0200 Subject: [PATCH 13/52] fix(ir): fix missing inverted operators --- src/ir/constructor.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/ir/constructor.rs b/src/ir/constructor.rs index 9d094dc..34d812b 100644 --- a/src/ir/constructor.rs +++ b/src/ir/constructor.rs @@ -512,6 +512,11 @@ impl IRGraphConstructor { OperatorType::NotEquals => current_block.register_node(Node::Equals( BinaryOperationData::new(left_node, right_node), )), + OperatorType::LowerEquals => current_block.register_node(Node::Higher( + BinaryOperationData::new(left_node, right_node), + )), + OperatorType::HigherEquals => current_block + .register_node(Node::Lower(BinaryOperationData::new(left_node, right_node))), OperatorType::LogicalAnd => todo!(), _ => unimplemented!("Unimplemented inverted operator {:?}", operator), } From 8285a56c2fd055b5dba313ae062cc3dc5eb0608d Mon Sep 17 00:00:00 2001 From: Felix Schwickerath Date: Sun, 15 Jun 2025 18:55:06 +0200 Subject: [PATCH 14/52] feat(semantic): add type checking --- src/parser/types.rs | 4 +- src/semantic/mod.rs | 201 ++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 188 insertions(+), 17 deletions(-) diff --git a/src/parser/types.rs b/src/parser/types.rs index 0ae2b05..831fe63 100644 --- a/src/parser/types.rs +++ b/src/parser/types.rs @@ -1,12 +1,14 @@ -#[derive(Clone, Debug)] +#[derive(Clone, PartialEq, Debug)] pub enum Type { Int, + Bool, } impl Type { pub fn as_string(&self) -> &str { match self { Type::Int => "int", + Type::Bool => "bool", } } } diff --git a/src/semantic/mod.rs b/src/semantic/mod.rs index b90e37b..cca8497 100644 --- a/src/semantic/mod.rs +++ b/src/semantic/mod.rs @@ -1,8 +1,8 @@ -use std::collections::HashMap; +use std::{collections::HashMap, env::var}; use crate::{ lexer::token::{OperatorType, Token}, - parser::{ast::Tree, symbols::Name}, + parser::{ast::Tree, symbols::Name, types::Type}, util::int_parsing::parse_int, }; @@ -26,8 +26,34 @@ enum ReturnState { NotReturing, } +pub struct VariableStatus { + type_status: Type, + declaration_status: DeclarationStatus, +} + +impl VariableStatus { + pub fn new(type_status: Type, declaration_status: DeclarationStatus) -> VariableStatus { + VariableStatus { + type_status, + declaration_status, + } + } + + pub fn declaration(&self) -> &DeclarationStatus { + &self.declaration_status + } + + pub fn type_status(&self) -> &Type { + &self.type_status + } + + pub fn set_initialized(&mut self) { + self.declaration_status = DeclarationStatus::Initialized + } +} + #[derive(PartialEq, PartialOrd)] -enum VariableStatus { +pub enum DeclarationStatus { Declared, Initialized, } @@ -72,17 +98,33 @@ pub fn analyze(tree: Box, state: &mut AnalysisState) -> Result<(), String> .namespace .get(&name) .unwrap() - .ne(&VariableStatus::Initialized) + .declaration() + .ne(&DeclarationStatus::Initialized) { - state.namespace.insert(name, VariableStatus::Initialized); + state.namespace.get_mut(&name).unwrap().set_initialized(); } + let expression_type = get_variable_type(expression.clone(), state) + .ok_or("Variable undefined!")?; + if state + .namespace + .get(&name) + .unwrap() + .type_status() + .ne(&expression_type) + { + return Err( + "Variable must be of the same type as expression!".to_string() + ); + } + {} } else if !state.namespace.contains_key(&name) { return Err(format!("Undecleared variable {} used!", name.as_string())); } else if state .namespace .get(&name) .unwrap() - .eq(&VariableStatus::Declared) + .declaration() + .eq(&DeclarationStatus::Declared) { return Err("Decleared variable without value used!".to_string()); } @@ -92,12 +134,14 @@ pub fn analyze(tree: Box, state: &mut AnalysisState) -> Result<(), String> Ok(()) } Tree::Declaration(variable_type, name, initializer) => { - analyze(variable_type, state)?; + analyze(variable_type.clone(), state)?; analyze(name.clone(), state)?; + let variable_type = + get_variable_type(variable_type, state).ok_or("Variable undefined!")?; let variable_state = if initializer.is_some() { - VariableStatus::Initialized + VariableStatus::new(variable_type.clone(), DeclarationStatus::Initialized) } else { - VariableStatus::Declared + VariableStatus::new(variable_type.clone(), DeclarationStatus::Declared) }; if let Tree::Name(identifier, _) = *name { let variable_status = state.namespace.get(&identifier); @@ -109,6 +153,12 @@ pub fn analyze(tree: Box, state: &mut AnalysisState) -> Result<(), String> state.namespace.insert(identifier.clone(), variable_state); } if let Some(present_initializer) = initializer { + if get_variable_type(present_initializer.clone(), state) + .ok_or("Variable undefined!")? + .ne(&variable_type) + { + return Err("initializer must be of same type as variable".to_string()); + } analyze(present_initializer, state)?; }; Ok(()) @@ -124,7 +174,8 @@ pub fn analyze(tree: Box, state: &mut AnalysisState) -> Result<(), String> .namespace .get(&name) .unwrap() - .eq(&VariableStatus::Declared) + .declaration() + .eq(&DeclarationStatus::Declared) { return Err(format!( "Uninitialized variable {} used in expression!", @@ -138,7 +189,41 @@ pub fn analyze(tree: Box, state: &mut AnalysisState) -> Result<(), String> Tree::BoolLiteral(_, _) => Ok(()), Tree::Continue(_) => Ok(()), Tree::Break(_) => Ok(()), - Tree::BinaryOperation(lhs, rhs, _) => { + Tree::BinaryOperation(lhs, rhs, operator_type) => { + match operator_type { + OperatorType::LogicalOr | OperatorType::LogicalAnd => { + if get_variable_type(lhs.clone(), state) + .ok_or("Variable undefined!")? + .ne(&Type::Bool) + || get_variable_type(rhs.clone(), state) + .ok_or("Variable undefined!")? + .ne(&Type::Bool) + { + return Err("Expression must be a boolean".to_string()); + } + } + OperatorType::Minus + | OperatorType::ShiftRight + | OperatorType::ShiftLeft + | OperatorType::BitwiseXor + | OperatorType::BitwiseAnd + | OperatorType::BitwiseOr + | OperatorType::Plus + | OperatorType::Mul + | OperatorType::Mod + | OperatorType::Div => { + if get_variable_type(lhs.clone(), state) + .ok_or("Variable undefined!")? + .ne(&Type::Int) + || get_variable_type(rhs.clone(), state) + .ok_or("Variable undefined!")? + .ne(&Type::Int) + { + return Err("Expression must be a integer".to_string()); + } + } + _ => {} + } analyze(lhs, state)?; analyze(rhs, state) } @@ -149,7 +234,28 @@ pub fn analyze(tree: Box, state: &mut AnalysisState) -> Result<(), String> Ok(()) } Tree::LValueIdentifier(name) => analyze(name, state), - Tree::UnaryOperation(expression, _, _) => analyze(expression, state), + Tree::UnaryOperation(expression, operator_type, _) => { + match operator_type { + OperatorType::LogicalNot => { + if get_variable_type(expression.clone(), state) + .ok_or("Variable undefined!")? + .ne(&Type::Bool) + { + return Err("Expression must be a boolean".to_string()); + } + } + OperatorType::BitwiseNot | OperatorType::Minus => { + if get_variable_type(expression.clone(), state) + .ok_or("Variable undefined!")? + .ne(&Type::Int) + { + return Err("Expression must be a integer".to_string()); + } + } + _ => {} + } + analyze(expression, state) + } Tree::Type(_, _) => Ok(()), Tree::Program(statements) => { for statement in statements { @@ -158,12 +264,24 @@ pub fn analyze(tree: Box, state: &mut AnalysisState) -> Result<(), String> Ok(()) } Tree::TernaryOperation(statement, true_statement, false_statement) => { - analyze(statement, state)?; + analyze(statement.clone(), state)?; + if get_variable_type(statement, state) + .ok_or("Variable undefined!")? + .ne(&Type::Bool) + { + return Err("Condition must be a boolean".to_string()); + } analyze(true_statement, state)?; analyze(false_statement, state) } Tree::If(condition, expression, else_expression, _) => { - analyze(condition, state)?; + analyze(condition.clone(), state)?; + if get_variable_type(condition, state) + .ok_or("Variable undefined!")? + .ne(&Type::Bool) + { + return Err("Condition must be a boolean".to_string()); + } analyze(expression, state)?; if let Some(other_expression) = else_expression { analyze(other_expression, state)?; @@ -171,14 +289,26 @@ pub fn analyze(tree: Box, state: &mut AnalysisState) -> Result<(), String> Ok(()) } Tree::While(condition, expression, _) => { - analyze(condition, state)?; + analyze(condition.clone(), state)?; + if get_variable_type(condition, state) + .ok_or("Variable undefined!")? + .ne(&Type::Bool) + { + return Err("Condition must be a boolean".to_string()); + } analyze(expression, state) } Tree::For(initializer, condition, updater, expression, _) => { if let Some(initializer_expression) = initializer { analyze(initializer_expression, state)?; } - analyze(condition, state)?; + analyze(condition.clone(), state)?; + if get_variable_type(condition, state) + .ok_or("Variable undefined!")? + .ne(&Type::Bool) + { + return Err("Condition must be a boolean".to_string()); + } if let Some(updater_expression) = updater { analyze(updater_expression, state)?; } @@ -186,3 +316,42 @@ pub fn analyze(tree: Box, state: &mut AnalysisState) -> Result<(), String> } } } + +fn get_variable_type(type_tree: Box, state: &mut AnalysisState) -> Option { + match *type_tree { + Tree::Type(variable_type, _) => Some(variable_type), + Tree::Literal(_, _, _) => Some(Type::Int), + Tree::BoolLiteral(_, _) => Some(Type::Bool), + Tree::BinaryOperation(_, _, operator_type) | Tree::UnaryOperation(_, operator_type, _) => { + match operator_type { + OperatorType::Mul + | OperatorType::Div + | OperatorType::Plus + | OperatorType::Mod + | OperatorType::Minus => Some(Type::Int), + OperatorType::Lower + | OperatorType::LowerEquals + | OperatorType::Equals + | OperatorType::NotEquals + | OperatorType::HigherEquals + | OperatorType::Higher => Some(Type::Bool), + OperatorType::BitwiseOr + | OperatorType::BitwiseNot + | OperatorType::BitwiseAnd + | OperatorType::BitwiseXor => Some(Type::Int), + OperatorType::LogicalOr | OperatorType::LogicalNot | OperatorType::LogicalAnd => { + Some(Type::Bool) + } + _ => None, + } + } + Tree::IdentifierExpression(name) => { + if let Tree::Name(name, _) = *name { + Some(state.namespace.get(&name)?.type_status().clone()) + } else { + None + } + } + _ => None, + } +} From ecdd765f6c5e659b3d40afe52d2af8f7788f461e Mon Sep 17 00:00:00 2001 From: Felix Schwickerath Date: Sun, 15 Jun 2025 19:12:28 +0200 Subject: [PATCH 15/52] fix(parsing): use correct type for statement --- src/backend/codegen.rs | 2 +- src/lexer/token.rs | 13 ++++++++++++- src/parser/mod.rs | 5 ++++- src/semantic/mod.rs | 7 +++++++ 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/backend/codegen.rs b/src/backend/codegen.rs index 3a0eedf..487fe9f 100644 --- a/src/backend/codegen.rs +++ b/src/backend/codegen.rs @@ -279,7 +279,7 @@ impl CodeGenerator { } Node::BitwiseNegate(data) => { let register = registers.get(&(block_index, data.input())).unwrap(); - code.push_str(&format!("not {}", register.as_assembly())); + code.push_str(&format!("not {}\n", register.as_assembly())); } Node::ConstantBool(data) => code.push_str(&self.generate_constant_bool(data.value())), Node::Phi(data) => {} diff --git a/src/lexer/token.rs b/src/lexer/token.rs index 8466c3e..4322a7a 100644 --- a/src/lexer/token.rs +++ b/src/lexer/token.rs @@ -1,4 +1,4 @@ -use crate::util::span::Span; +use crate::{parser::types::Type, util::span::Span}; #[derive(Eq, Hash, PartialEq, Clone, Debug)] pub enum KeywordType { @@ -275,6 +275,17 @@ impl Token { } } + pub fn get_type_value(&self) -> Option { + match self { + Self::Keyword(_, keyword_type) if keyword_type.is_type() => match keyword_type { + KeywordType::Int => Some(Type::Int), + KeywordType::Bool => Some(Type::Bool), + _ => None, + }, + _ => None, + } + } + pub fn is_operator(&self) -> bool { match self { Self::Operator(_, _) => true, diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 7a871f7..b98e9ca 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -146,7 +146,10 @@ fn parse_decleration(tokens: &mut VecDeque) -> Result { expression = Some(parse_expression(tokens)?); } Ok(Tree::Declaration( - Box::new(Tree::Type(Type::Int, type_token.span())), + Box::new(Tree::Type( + type_token.get_type_value().unwrap(), + type_token.span(), + )), name(identifier)?, expression, )) diff --git a/src/semantic/mod.rs b/src/semantic/mod.rs index cca8497..9954e5d 100644 --- a/src/semantic/mod.rs +++ b/src/semantic/mod.rs @@ -1,5 +1,7 @@ use std::{collections::HashMap, env::var}; +use tracing::trace; + use crate::{ lexer::token::{OperatorType, Token}, parser::{ast::Tree, symbols::Name, types::Type}, @@ -157,6 +159,11 @@ pub fn analyze(tree: Box, state: &mut AnalysisState) -> Result<(), String> .ok_or("Variable undefined!")? .ne(&variable_type) { + trace!( + "Initializer is {:?}", + get_variable_type(present_initializer.clone(), state) + ); + trace!("Variable should be {:?}", variable_type); return Err("initializer must be of same type as variable".to_string()); } analyze(present_initializer, state)?; From dd5c600fc538b364a89cfc4b0e4cb068daf91ebc Mon Sep 17 00:00:00 2001 From: Felix Schwickerath Date: Sun, 15 Jun 2025 19:19:47 +0200 Subject: [PATCH 16/52] fix(semantic): check type of return value --- src/semantic/mod.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/semantic/mod.rs b/src/semantic/mod.rs index 9954e5d..10f8dd5 100644 --- a/src/semantic/mod.rs +++ b/src/semantic/mod.rs @@ -1,4 +1,4 @@ -use std::{collections::HashMap, env::var}; +use std::{collections::HashMap, env::var, error::Error}; use tracing::trace; @@ -71,7 +71,13 @@ pub fn analyze(tree: Box, state: &mut AnalysisState) -> Result<(), String> Ok(()) } Tree::Return(sub, _) => { - analyze(sub, state)?; + analyze(sub.clone(), state)?; + if get_variable_type(sub, state) + .ok_or("Variable not defined!")? + .ne(&Type::Int) + { + return Err("Function must return an int".to_string()); + } state.return_state = ReturnState::Returning; Ok(()) } From 103e24b0f49817dfe4baa6b44fb27c6050d028d4 Mon Sep 17 00:00:00 2001 From: Felix Schwickerath Date: Sun, 15 Jun 2025 19:34:30 +0200 Subject: [PATCH 17/52] fix(ir): fix logical and bitwise not --- src/backend/codegen.rs | 9 +++++++++ src/ir/constructor.rs | 12 ++++++++++-- src/ir/node/mod.rs | 5 ++++- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/backend/codegen.rs b/src/backend/codegen.rs index 487fe9f..15f4e0b 100644 --- a/src/backend/codegen.rs +++ b/src/backend/codegen.rs @@ -277,6 +277,14 @@ impl CodeGenerator { registers, )); } + Node::LogicalNot(data) => { + let register = registers.get(&(block_index, data.input())).unwrap(); + code.push_str(&format!( + "test {}, {}\n", + register.as_assembly(), + register.as_assembly() + )); + } Node::BitwiseNegate(data) => { let register = registers.get(&(block_index, data.input())).unwrap(); code.push_str(&format!("not {}\n", register.as_assembly())); @@ -415,6 +423,7 @@ impl CodeGenerator { Node::Higher(_) => "ja", Node::ConstantBool(_) => "je", Node::BitwiseNegate(_) => "jne", + Node::LogicalNot(_) => "je", Node::Phi(_) | Node::ConstantInt(_) => { let register = registers.get(&(current_block_index, comparision)).unwrap(); code.push_str(&format!("testq $0x1, {}\n", register.as_assembly())); diff --git a/src/ir/constructor.rs b/src/ir/constructor.rs index 34d812b..134472f 100644 --- a/src/ir/constructor.rs +++ b/src/ir/constructor.rs @@ -238,7 +238,7 @@ impl IRGraphConstructor { } OperatorType::LogicalNot => { let node = self.convert_boxed(expression)?; - let result = self.create_bitwise_not(node); + let result = self.create_logical_not(node); Some(result) } _ => panic!("Unregistered Unary Operation {:?}", operator_type), @@ -522,7 +522,7 @@ impl IRGraphConstructor { } } else if let Tree::IdentifierExpression(_) = *condition { let variable = self.convert_boxed(condition).unwrap(); - self.create_bitwise_not(variable) + self.create_logical_not(variable) } else if let Tree::BoolLiteral(value, _) = *condition { if value { self.create_constant_int(0) @@ -656,6 +656,14 @@ impl IRGraphConstructor { )) } + fn create_logical_not(&mut self, node: NodeIndex) -> NodeIndex { + let sideeffect = self.read_current_side_effect(); + let current_block = self.graph.get_block_mut(self.current_block_index); + current_block.register_node(Node::LogicalNot(UnaryOperationData::new_with_sideeffect( + node, sideeffect, + ))) + } + fn create_constant_int(&mut self, value: i32) -> NodeIndex { let current_block = self.graph.get_block_mut(self.current_block_index); current_block.register_node(Node::ConstantInt(ConstantIntData::new(value))) diff --git a/src/ir/node/mod.rs b/src/ir/node/mod.rs index d833a44..614f2ae 100644 --- a/src/ir/node/mod.rs +++ b/src/ir/node/mod.rs @@ -31,6 +31,7 @@ pub enum Node { HigherEquals(BinaryOperationData), Higher(BinaryOperationData), BitwiseNegate(UnaryOperationData), + LogicalNot(UnaryOperationData), Or(BinaryOperationData), And(BinaryOperationData), Xor(BinaryOperationData), @@ -45,7 +46,9 @@ impl Node { Node::Projection(data) => vec![data.input()], Node::Phi(_data) => todo!("What to return?"), Node::Return(data) => vec![data.input()], - Node::ConditionalJump(data) | Node::BitwiseNegate(data) => vec![data.input()], + Node::ConditionalJump(data) | Node::LogicalNot(data) | Node::BitwiseNegate(data) => { + vec![data.input()] + } Node::Add(data) | Node::Division(data) | Node::ShiftRight(data) From 280f8054fdd63dda9c20b670f4833a4e2761adae Mon Sep 17 00:00:00 2001 From: Felix Schwickerath Date: Sun, 15 Jun 2025 20:51:27 +0200 Subject: [PATCH 18/52] feat: 32 bit assembly fixes --- src/backend/codegen.rs | 91 +++++++++++++++++++++++------------------ src/backend/regalloc.rs | 2 +- src/semantic/mod.rs | 13 +++++- 3 files changed, 64 insertions(+), 42 deletions(-) diff --git a/src/backend/codegen.rs b/src/backend/codegen.rs index 15f4e0b..8aaa82f 100644 --- a/src/backend/codegen.rs +++ b/src/backend/codegen.rs @@ -281,13 +281,13 @@ impl CodeGenerator { let register = registers.get(&(block_index, data.input())).unwrap(); code.push_str(&format!( "test {}, {}\n", - register.as_assembly(), - register.as_assembly() + register.as_32_bit_assembly(), + register.as_32_bit_assembly() )); } Node::BitwiseNegate(data) => { let register = registers.get(&(block_index, data.input())).unwrap(); - code.push_str(&format!("not {}\n", register.as_assembly())); + code.push_str(&format!("not {}\n", register.as_32_bit_assembly())); } Node::ConstantBool(data) => code.push_str(&self.generate_constant_bool(data.value())), Node::Phi(data) => {} @@ -371,11 +371,22 @@ impl CodeGenerator { continue; } let source_register = registers.get(&operand).unwrap(); - code.push_str(&format!( - "movq {}, {}\n", - source_register.as_assembly(), - destination_register.as_assembly() - )); + if !source_register.hardware_register() + && !destination_register.hardware_register() + { + code.push_str(&move_stack_variable(source_register)); + code.push_str(&format!( + "mov {}, {}\n", + HardwareRegister::Rbx.as_32_bit_assembly(), + destination_register.as_32_bit_assembly() + )); + } else { + code.push_str(&format!( + "mov {}, {}\n", + source_register.as_32_bit_assembly(), + destination_register.as_32_bit_assembly() + )); + } break; } } @@ -426,7 +437,7 @@ impl CodeGenerator { Node::LogicalNot(_) => "je", Node::Phi(_) | Node::ConstantInt(_) => { let register = registers.get(&(current_block_index, comparision)).unwrap(); - code.push_str(&format!("testq $0x1, {}\n", register.as_assembly())); + code.push_str(&format!("test $0x1, {}\n", register.as_32_bit_assembly())); code.push_str(&format!("jnz {}\n", true_label)); return Some(code); } @@ -463,14 +474,14 @@ impl CodeGenerator { if !left_value.hardware_register() && !right_value.hardware_register() { code.push_str(&format!( "cmp {}, {}\n", - right_value.as_assembly(), - HardwareRegister::Rbx.as_assembly(), + right_value.as_32_bit_assembly(), + HardwareRegister::Rbx.as_32_bit_assembly(), )); } else { code.push_str(&format!( "cmp {}, {}\n", - right_value.as_assembly(), - left_value.as_assembly() + right_value.as_32_bit_assembly(), + left_value.as_32_bit_assembly() )); } code @@ -501,12 +512,12 @@ impl CodeGenerator { } code.push_str("mov "); if !left_value.hardware_register() && !destination_register.hardware_register() { - code.push_str(&HardwareRegister::Rbx.as_assembly()); + code.push_str(&HardwareRegister::Rbx.as_32_bit_assembly()); } else { - code.push_str(&left_value.as_assembly()); + code.push_str(&left_value.as_32_bit_assembly()); } code.push_str(", "); - code.push_str(&destination_register.as_assembly()); + code.push_str(&destination_register.as_32_bit_assembly()); code.push('\n'); if !right_value.hardware_register() && !destination_register.hardware_register() { @@ -516,12 +527,12 @@ impl CodeGenerator { code.push_str(op_code); code.push(' '); if !right_value.hardware_register() && !destination_register.hardware_register() { - code.push_str(&HardwareRegister::Rbx.as_assembly()); + code.push_str(&HardwareRegister::Rbx.as_32_bit_assembly()); } else { - code.push_str(&right_value.as_assembly()); + code.push_str(&right_value.as_32_bit_assembly()); } code.push_str(", "); - code.push_str(&destination_register.as_assembly()); + code.push_str(&destination_register.as_32_bit_assembly()); code.push('\n'); code } @@ -545,10 +556,10 @@ impl CodeGenerator { .unwrap(); let destination_register = registers.get(&(block_index, node_index)).unwrap(); let mut code = String::new(); - code.push_str("movq $0, %rdx\n"); - code.push_str("movq $0, %rax\n"); - code.push_str("movq $0, "); - code.push_str(&destination_register.as_assembly()); + code.push_str("mov $0, %rdx\n"); + code.push_str("mov $0, %rax\n"); + code.push_str("mov $0, "); + code.push_str(&destination_register.as_32_bit_assembly()); code.push('\n'); code.push_str("mov "); @@ -564,12 +575,12 @@ impl CodeGenerator { code.push_str("mov "); if mode == "mod" { - code.push_str("%rdx"); + code.push_str("%edx"); } else { - code.push_str("%rax"); + code.push_str("%eax"); } code.push_str(", "); - code.push_str(&destination_register.as_assembly()); + code.push_str(&destination_register.as_32_bit_assembly()); code.push('\n'); code } @@ -588,21 +599,21 @@ impl CodeGenerator { let left_value = registers.get(&(block_index, data.lhs())).unwrap(); let right_value = registers.get(&(block_index, data.rhs())).unwrap(); code.push_str(&format!( - "movq {}, {}\n", - right_value.as_assembly(), - HardwareRegister::Rcx.as_assembly() + "mov {}, {}\n", + right_value.as_32_bit_assembly(), + HardwareRegister::Rcx.as_32_bit_assembly() )); code.push_str(&format!( "{} {}, {}\n", op_code, HardwareRegister::Rcx.as_8_bit_assembly(), - left_value.as_assembly() + left_value.as_32_bit_assembly() )); let destination = registers.get(&(block_index, node_index)).unwrap(); code.push_str(&format!( - "movq {}, {}\n", - left_value.as_assembly(), - destination.as_assembly() + "mov {}, {}\n", + left_value.as_32_bit_assembly(), + destination.as_32_bit_assembly() )); code } @@ -628,9 +639,9 @@ impl CodeGenerator { ®isters .get(&(block_index, return_node)) .unwrap() - .as_assembly(), + .as_32_bit_assembly(), ); - code.push_str(", %rax"); + code.push_str(", %eax"); code.push('\n'); code.push_str("leave\n"); @@ -649,10 +660,10 @@ impl CodeGenerator { let register = registers.get(&(block_index, node_index)).unwrap(); let mut code = String::new(); - code.push_str("movq "); + code.push_str("mov "); code.push_str(&(format!("$0x{:X}", &constant_data.value()).to_string())); code.push_str(", "); - code.push_str(®ister.as_assembly()); + code.push_str(®ister.as_32_bit_assembly()); code.push('\n'); code } @@ -669,10 +680,10 @@ fn predecessor_skip_projection(block: &Block, data: NodeIndex) -> NodeIndex { fn move_stack_variable(register: &Box) -> String { let mut code = String::new(); - code.push_str("movq "); - code.push_str(®ister.as_assembly()); + code.push_str("mov "); + code.push_str(®ister.as_32_bit_assembly()); code.push_str(", "); - code.push_str(&HardwareRegister::Rbx.as_assembly()); + code.push_str(&HardwareRegister::Rbx.as_32_bit_assembly()); code.push('\n'); code } diff --git a/src/backend/regalloc.rs b/src/backend/regalloc.rs index 051bde6..1e00fa0 100644 --- a/src/backend/regalloc.rs +++ b/src/backend/regalloc.rs @@ -110,7 +110,7 @@ impl HardwareRegister { impl Register for HardwareRegister { fn as_assembly(&self) -> String { - format!("%{}", self.as_string()) + self.as_32_bit_assembly() } fn hardware_register(&self) -> bool { true diff --git a/src/semantic/mod.rs b/src/semantic/mod.rs index 10f8dd5..181308f 100644 --- a/src/semantic/mod.rs +++ b/src/semantic/mod.rs @@ -242,7 +242,10 @@ pub fn analyze(tree: Box, state: &mut AnalysisState) -> Result<(), String> } Tree::Block(statements, _) => { for statement in statements { - analyze(Box::new(statement), state)?; + analyze(Box::new(statement.clone()), state)?; + if let Tree::Return(_, _) = statement { + break; + } } Ok(()) } @@ -284,6 +287,13 @@ pub fn analyze(tree: Box, state: &mut AnalysisState) -> Result<(), String> { return Err("Condition must be a boolean".to_string()); } + let true_type = + get_variable_type(true_statement.clone(), state).ok_or("Variable undefined!")?; + let false_type = + get_variable_type(false_statement.clone(), state).ok_or("Variable undefined!")?; + if true_type.ne(&false_type) { + return Err("Ternary operator types not equal".to_string()); + } analyze(true_statement, state)?; analyze(false_statement, state) } @@ -365,6 +375,7 @@ fn get_variable_type(type_tree: Box, state: &mut AnalysisState) -> Option< None } } + Tree::TernaryOperation(_, true_expression, _) => get_variable_type(true_expression, state), _ => None, } } From c3c2fc81110c0cd63de1769297742ab442392a81 Mon Sep 17 00:00:00 2001 From: Felix Schwickerath Date: Sun, 15 Jun 2025 21:00:42 +0200 Subject: [PATCH 19/52] fix(ir): fix phi operands for ternary operator --- src/ir/constructor.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ir/constructor.rs b/src/ir/constructor.rs index 134472f..4faeb6c 100644 --- a/src/ir/constructor.rs +++ b/src/ir/constructor.rs @@ -310,8 +310,8 @@ impl IRGraphConstructor { self.seal_block(true_block_index); self.seal_block(false_block_index); let phi = self.create_phi_from_operands(vec![ - (self.current_block_index, false_expression), - (self.current_block_index, true_expression), + (false_block_index, false_expression), + (true_block_index, true_expression), ]); self.seal_block(self.current_block_index); Some(phi) From 09ea149f5520b8301d82c003572015b304db41a6 Mon Sep 17 00:00:00 2001 From: Felix Schwickerath Date: Sun, 15 Jun 2025 21:14:49 +0200 Subject: [PATCH 20/52] fix(codegen): skip projections when moving phis --- src/backend/codegen.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/backend/codegen.rs b/src/backend/codegen.rs index 8aaa82f..6e517dc 100644 --- a/src/backend/codegen.rs +++ b/src/backend/codegen.rs @@ -370,7 +370,13 @@ impl CodeGenerator { if operand.0 != current_block_index { continue; } - let source_register = registers.get(&operand).unwrap(); + let operand_block = ir_graph.get_block(operand.0); + let source_register = registers + .get(&( + operand.0, + predecessor_skip_projection(operand_block, operand.1), + )) + .unwrap(); if !source_register.hardware_register() && !destination_register.hardware_register() { From f12508479945dbc5d74d373fde32de40e9f12a69 Mon Sep 17 00:00:00 2001 From: Felix Schwickerath Date: Sun, 15 Jun 2025 21:27:06 +0200 Subject: [PATCH 21/52] fix(semantic): add shift left and shift right to type operators --- src/semantic/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/semantic/mod.rs b/src/semantic/mod.rs index 181308f..22b5ef2 100644 --- a/src/semantic/mod.rs +++ b/src/semantic/mod.rs @@ -351,6 +351,8 @@ fn get_variable_type(type_tree: Box, state: &mut AnalysisState) -> Option< | OperatorType::Div | OperatorType::Plus | OperatorType::Mod + | OperatorType::ShiftLeft + | OperatorType::ShiftRight | OperatorType::Minus => Some(Type::Int), OperatorType::Lower | OperatorType::LowerEquals From 3fb2781900a46a86a2389aa40df9072243e67413 Mon Sep 17 00:00:00 2001 From: Felix Schwickerath Date: Sun, 15 Jun 2025 22:17:03 +0200 Subject: [PATCH 22/52] fix(ir): fix break and continue statements --- src/ir/constructor.rs | 47 +++++++++++++++++++++++++++++-------------- src/semantic/mod.rs | 40 ++++++++++++++++++++++++++++++++---- 2 files changed, 68 insertions(+), 19 deletions(-) diff --git a/src/ir/constructor.rs b/src/ir/constructor.rs index 4faeb6c..53f17d6 100644 --- a/src/ir/constructor.rs +++ b/src/ir/constructor.rs @@ -34,7 +34,8 @@ pub struct IRGraphConstructor { incomplete_side_effect_phis: HashMap, sealed_blocks: Vec, current_block_index: BlockIndex, - active_loops: Vec, + active_loop_entries: Vec, + active_loop_exits: Vec, } impl IRGraphConstructor { @@ -48,7 +49,8 @@ impl IRGraphConstructor { // Start Block never gets more predecessors sealed_blocks: vec![START_BLOCK], current_block_index: START_BLOCK, - active_loops: Vec::new(), + active_loop_entries: Vec::new(), + active_loop_exits: Vec::new(), } } @@ -260,22 +262,22 @@ impl IRGraphConstructor { Tree::Break(_) => { debug!( "Generating IR for break statement with active loops: {:?}", - self.active_loops + self.active_loop_entries ); let jump = self.create_jump(); self.graph - .get_block_mut(*self.active_loops.first().unwrap()) + .get_block_mut(*self.active_loop_exits.first().unwrap()) .register_entry_point(self.current_block_index, jump); None } Tree::Continue(_) => { debug!( "Generating IR for continue statement with active loops: {:?}", - self.active_loops + self.active_loop_entries ); let jump = self.create_jump(); self.graph - .get_block_mut(*self.active_loops.first().unwrap()) + .get_block_mut(*self.active_loop_entries.first().unwrap()) .register_entry_point(self.current_block_index, jump); None } @@ -328,13 +330,16 @@ impl IRGraphConstructor { let mut loop_body = Block::new("while-body".to_string()); loop_body.register_entry_point(self.current_block_index, entry_false_projection); + let loop_body_index = self.graph.register_block(loop_body); let mut following_block = Block::new("while-following".to_string()); following_block .register_entry_point(self.current_block_index, entry_true_projection); + let following_block_index = self.graph.register_block(following_block); self.seal_block(self.current_block_index); - let loop_body_index = self.graph.register_block(loop_body); self.current_block_index = loop_body_index; + self.active_loop_entries.push(loop_body_index); + self.active_loop_exits.push(following_block_index); self.convert_boxed(expression); let condition_node = self.convert_boxed(condition)?; @@ -347,9 +352,11 @@ impl IRGraphConstructor { .get_block_mut(loop_body_index) .register_entry_point(loop_body_index, true_projection); - following_block.register_entry_point(loop_body_index, false_projection); + self.graph + .get_block_mut(following_block_index) + .register_entry_point(loop_body_index, false_projection); self.seal_block(loop_body_index); - self.current_block_index = self.graph.register_block(following_block); + self.current_block_index = following_block_index; self.seal_block(self.current_block_index); None } @@ -403,19 +410,27 @@ impl IRGraphConstructor { let mut loop_body = Block::new("for-body".to_string()); loop_body.register_entry_point(self.current_block_index, entry_false_projection); + let loop_body_index = self.graph.register_block(loop_body); let mut following_block = Block::new("for-following".to_string()); following_block .register_entry_point(self.current_block_index, entry_true_projection); + let following_block_index = self.graph.register_block(following_block); + let loop_post = Block::new("for-post".to_string()); + let loop_post_index = self.graph.register_block(loop_post); + self.seal_block(self.current_block_index); - let loop_body_index = self.graph.register_block(loop_body); self.current_block_index = loop_body_index; + self.active_loop_entries.push(loop_post_index); + self.active_loop_exits.push(following_block_index); self.convert_boxed(expression); + self.active_loop_entries.pop(); + self.active_loop_exits.pop(); let loop_body_exit = self.create_jump(); - let mut loop_post = Block::new("for-post".to_string()); - loop_post.register_entry_point(loop_body_index, loop_body_exit); - let loop_post_index = self.graph.register_block(loop_post); + self.graph + .get_block_mut(loop_post_index) + .register_entry_point(loop_body_index, loop_body_exit); self.current_block_index = loop_post_index; self.seal_block(loop_post_index); @@ -433,9 +448,11 @@ impl IRGraphConstructor { .get_block_mut(loop_body_index) .register_entry_point(loop_post_index, true_projection); - following_block.register_entry_point(loop_post_index, false_projection); + self.graph + .get_block_mut(following_block_index) + .register_entry_point(loop_post_index, false_projection); self.seal_block(loop_body_index); - self.current_block_index = self.graph.register_block(following_block); + self.current_block_index = following_block_index; self.seal_block(self.current_block_index); debug!("Following block after for: {}", self.current_block_index); None diff --git a/src/semantic/mod.rs b/src/semantic/mod.rs index 22b5ef2..b130a58 100644 --- a/src/semantic/mod.rs +++ b/src/semantic/mod.rs @@ -11,6 +11,7 @@ use crate::{ pub struct AnalysisState { return_state: ReturnState, namespace: HashMap, + active_loops: usize, } impl Default for AnalysisState { @@ -18,10 +19,25 @@ impl Default for AnalysisState { AnalysisState { return_state: ReturnState::NotReturing, namespace: HashMap::new(), + active_loops: 0, } } } +impl AnalysisState { + pub fn enter_loop(&mut self) { + self.active_loops += 1; + } + + pub fn exit_loop(&mut self) { + self.active_loops -= 1; + } + + pub fn loop_active(&self) -> bool { + self.active_loops > 0 + } +} + #[derive(PartialEq)] enum ReturnState { Returning, @@ -200,8 +216,18 @@ pub fn analyze(tree: Box, state: &mut AnalysisState) -> Result<(), String> } Tree::Name(_, _) => Ok(()), Tree::BoolLiteral(_, _) => Ok(()), - Tree::Continue(_) => Ok(()), - Tree::Break(_) => Ok(()), + Tree::Continue(_) => { + if !state.loop_active() { + return Err("Continue can only be used in a loop".to_string()); + } + Ok(()) + } + Tree::Break(_) => { + if !state.loop_active() { + return Err("Break can only be used in a loop".to_string()); + } + Ok(()) + } Tree::BinaryOperation(lhs, rhs, operator_type) => { match operator_type { OperatorType::LogicalOr | OperatorType::LogicalAnd => { @@ -319,7 +345,10 @@ pub fn analyze(tree: Box, state: &mut AnalysisState) -> Result<(), String> { return Err("Condition must be a boolean".to_string()); } - analyze(expression, state) + state.enter_loop(); + analyze(expression, state)?; + state.exit_loop(); + Ok(()) } Tree::For(initializer, condition, updater, expression, _) => { if let Some(initializer_expression) = initializer { @@ -335,7 +364,10 @@ pub fn analyze(tree: Box, state: &mut AnalysisState) -> Result<(), String> if let Some(updater_expression) = updater { analyze(updater_expression, state)?; } - analyze(expression, state) + state.enter_loop(); + analyze(expression, state)?; + state.exit_loop(); + Ok(()) } } } From 8423648168695c442e3b72c869229b3742bc11c6 Mon Sep 17 00:00:00 2001 From: Felix Schwickerath Date: Mon, 16 Jun 2025 09:59:46 +0200 Subject: [PATCH 23/52] fix(ir): some more loop fixes --- src/backend/codegen.rs | 23 +++++++++++------------ src/ir/block.rs | 13 ++++++++++--- src/ir/constructor.rs | 10 ++++++++-- src/parser/mod.rs | 3 ++- 4 files changed, 31 insertions(+), 18 deletions(-) diff --git a/src/backend/codegen.rs b/src/backend/codegen.rs index 6e517dc..ccdd747 100644 --- a/src/backend/codegen.rs +++ b/src/backend/codegen.rs @@ -64,23 +64,27 @@ impl CodeGenerator { code.push_str(&format!("subq ${}, %rsp\n", stack_offset)); // Jump Information contains for each block(key) the map of exits (NodeIndex[Node that jumps], BlockIndex[Block that is jumped to]) - let mut jump_information = HashMap::new(); + let mut jump_information: HashMap> = + HashMap::new(); let mut visited = Vec::new(); jump_information.insert(END_BLOCK, HashMap::new()); visited.push(END_BLOCK); for (block_index, block) in ir_graph.get_blocks().iter().enumerate() { - for (previous_block_index, previous_node) in block.entry_points() { + for (previous_block_index, previous_nodes) in block.entry_points() { if !jump_information.contains_key(&previous_block_index) { - jump_information.insert( - *previous_block_index, - HashMap::from([(*previous_node, block_index)]), - ); + let mut value = HashMap::new(); + for previous_node in previous_nodes { + value.insert(*previous_node, block_index); + } + jump_information.insert(*previous_block_index, value); } else { let mut existing_exits = jump_information .get_mut(previous_block_index) .unwrap() .clone(); - existing_exits.insert(*previous_node, block_index); + for previous_node in previous_nodes { + existing_exits.insert(*previous_node, block_index); + } jump_information.insert(*previous_block_index, existing_exits); } } @@ -135,11 +139,6 @@ impl CodeGenerator { ir_graph: &IRGraph, registers: &Registers, ) -> String { - // End Node should not emit code - if block.get_nodes().is_empty() { - return String::new(); - } - let mut code = String::new(); let block_label = self.jump_label.get(&block_index).unwrap(); code.push_str(&format!("{}:\n", block_label)); diff --git a/src/ir/block.rs b/src/ir/block.rs index 4a154a2..4be4595 100644 --- a/src/ir/block.rs +++ b/src/ir/block.rs @@ -4,9 +4,10 @@ use super::{graph::BlockIndex, node::Node}; pub type NodeIndex = usize; +#[derive(Debug)] pub struct Block { // Denote that block can be entered from block (key) at node index (value) within that block - entry_points: HashMap, + entry_points: HashMap>, phis: Vec, nodes: Vec, name: String, @@ -23,10 +24,16 @@ impl Block { } pub fn register_entry_point(&mut self, block: BlockIndex, node_index: NodeIndex) { - self.entry_points.insert(block, node_index); + if self.entry_points.contains_key(&block) { + let mut value = self.entry_points.remove(&block).unwrap(); + value.push(node_index); + self.entry_points.insert(block, value); + } else { + self.entry_points.insert(block, vec![node_index]); + } } - pub fn entry_points(&self) -> &HashMap { + pub fn entry_points(&self) -> &HashMap> { &self.entry_points } diff --git a/src/ir/constructor.rs b/src/ir/constructor.rs index 53f17d6..6f17a8b 100644 --- a/src/ir/constructor.rs +++ b/src/ir/constructor.rs @@ -262,12 +262,17 @@ impl IRGraphConstructor { Tree::Break(_) => { debug!( "Generating IR for break statement with active loops: {:?}", - self.active_loop_entries + self.active_loop_exits ); let jump = self.create_jump(); self.graph .get_block_mut(*self.active_loop_exits.first().unwrap()) .register_entry_point(self.current_block_index, jump); + debug!( + "Modifying entry_points of {:?}", + self.graph + .get_block_mut(*self.active_loop_exits.first().unwrap()) + ); None } Tree::Continue(_) => { @@ -715,7 +720,8 @@ impl IRGraphConstructor { let operands = block .entry_points() .iter() - .map(|(v1, v2)| (*v1, *v2)) + .flat_map(|(v1, v2)| v2.iter().map(|v| (v1.clone(), v))) + .map(|(v1, v2)| (v1, *v2)) .collect(); trace!("Creating phi with operands {:?}", operands); let current_block = self.graph.get_block_mut(self.current_block_index); diff --git a/src/parser/mod.rs b/src/parser/mod.rs index b98e9ca..80a33fa 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -64,7 +64,8 @@ fn parse_function(tokens: &mut VecDeque) -> Result { } fn parse_block(tokens: &mut VecDeque) -> Result { - let body_open = expect_seperator(tokens, SeperatorType::BraceOpen).unwrap(); + let body_open = expect_seperator(tokens, SeperatorType::BraceOpen) + .ok_or(ParseError::Error("Expected block for function".to_string()))?; let mut statements = vec![]; while !tokens.is_empty() { match tokens.front().unwrap() { From fc297e28bd39ef0c1ba7d7e7cf5b4cb58e7be609 Mon Sep 17 00:00:00 2001 From: Felix Schwickerath Date: Mon, 16 Jun 2025 10:12:49 +0200 Subject: [PATCH 24/52] fix(semantic): enable type checking for comparison operators --- src/backend/codegen.rs | 16 ++++++++-------- src/backend/regalloc.rs | 11 ----------- src/ir/constructor.rs | 6 +++--- src/semantic/mod.rs | 33 +++++++++++++++++++++++++++++++-- 4 files changed, 42 insertions(+), 24 deletions(-) diff --git a/src/backend/codegen.rs b/src/backend/codegen.rs index ccdd747..8652e5d 100644 --- a/src/backend/codegen.rs +++ b/src/backend/codegen.rs @@ -1,4 +1,4 @@ -use std::collections::{HashMap, VecDeque}; +use std::collections::HashMap; use tracing::{debug, trace}; @@ -289,7 +289,7 @@ impl CodeGenerator { code.push_str(&format!("not {}\n", register.as_32_bit_assembly())); } Node::ConstantBool(data) => code.push_str(&self.generate_constant_bool(data.value())), - Node::Phi(data) => {} + Node::Phi(_) => {} Node::Jump => { trace!( "Generating assembly for jump: {} with destination XXX", @@ -457,7 +457,7 @@ impl CodeGenerator { block: &Block, block_index: BlockIndex, operation_data: &BinaryOperationData, - ir_graph: &IRGraph, + _ir_graph: &IRGraph, registers: &Registers, ) -> String { let left_value = registers @@ -498,7 +498,7 @@ impl CodeGenerator { block: &Block, block_index: BlockIndex, data: &BinaryOperationData, - ir_graph: &IRGraph, + _ir_graph: &IRGraph, registers: &Registers, op_code: &str, ) -> String { @@ -547,7 +547,7 @@ impl CodeGenerator { node_index: usize, block: &Block, block_index: BlockIndex, - ir_graph: &IRGraph, + _ir_graph: &IRGraph, data: &BinaryOperationData, registers: &Registers, op_code: &str, @@ -593,9 +593,9 @@ impl CodeGenerator { pub fn generate_shift( &self, node_index: NodeIndex, - block: &Block, + _block: &Block, block_index: BlockIndex, - ir_graph: &IRGraph, + _ir_graph: &IRGraph, data: &BinaryOperationData, registers: &Registers, op_code: &str, @@ -657,7 +657,7 @@ impl CodeGenerator { pub fn generate_constant_int( &self, constant_data: &ConstantIntData, - block: &Block, + _block: &Block, block_index: BlockIndex, registers: &Registers, node_index: NodeIndex, diff --git a/src/backend/regalloc.rs b/src/backend/regalloc.rs index 1e00fa0..8766cd0 100644 --- a/src/backend/regalloc.rs +++ b/src/backend/regalloc.rs @@ -253,26 +253,15 @@ impl RegisterAllocator { if self.has_available_hardware_register() { let register = self.available_hardware_register.pop().unwrap(); let register: Box = Box::new(register); - self.handle_phi_register(node, ir_graph, ®ister); register } else { let register = StackRegister::new(self.current_stack_offset); self.current_stack_offset += 8; let boxed_register: Box = Box::new(register); - self.handle_phi_register(node, ir_graph, &boxed_register); boxed_register } } - pub fn handle_phi_register( - &mut self, - node_index: (BlockIndex, NodeIndex), - ir_graph: &IRGraph, - register: &Box, - ) { - return; - } - pub fn has_available_hardware_register(&self) -> bool { !self.available_hardware_register.is_empty() } diff --git a/src/ir/constructor.rs b/src/ir/constructor.rs index 6f17a8b..0c82117 100644 --- a/src/ir/constructor.rs +++ b/src/ir/constructor.rs @@ -5,7 +5,7 @@ use tracing::{debug, info, trace}; use crate::{ ir::{ - block::{self, Block}, + block::Block, graph::START_BLOCK, node::{ binary_operation::BinaryOperationData, @@ -14,7 +14,7 @@ use crate::{ }, lexer::token::{OperatorType, Token}, parser::{ast::Tree, symbols::Name}, - util::{int_parsing::parse_int, position}, + util::int_parsing::parse_int, }; use super::{ @@ -691,7 +691,7 @@ impl IRGraphConstructor { current_block.register_node(Node::ConstantInt(ConstantIntData::new(value))) } - fn create_constant_bool(&mut self, value: bool) -> NodeIndex { + fn _create_constant_bool(&mut self, value: bool) -> NodeIndex { let start_block = self.graph.get_block_mut(START_BLOCK); start_block.register_node(Node::ConstantBool(ConstantBoolData::new(value))) } diff --git a/src/semantic/mod.rs b/src/semantic/mod.rs index b130a58..792f977 100644 --- a/src/semantic/mod.rs +++ b/src/semantic/mod.rs @@ -1,4 +1,4 @@ -use std::{collections::HashMap, env::var, error::Error}; +use std::collections::HashMap; use tracing::trace; @@ -261,7 +261,36 @@ pub fn analyze(tree: Box, state: &mut AnalysisState) -> Result<(), String> return Err("Expression must be a integer".to_string()); } } - _ => {} + OperatorType::Lower + | OperatorType::LowerEquals + | OperatorType::Equals + | OperatorType::NotEquals + | OperatorType::Higher + | OperatorType::HigherEquals => { + let lhs_type = + get_variable_type(lhs.clone(), state).ok_or("Variable undefined!")?; + let rhs_type = + get_variable_type(rhs.clone(), state).ok_or("Variable undefined!")?; + if lhs_type.ne(&rhs_type) { + return Err("Comparison operators must have equal types".to_string()); + } + } + OperatorType::Assign + | OperatorType::AssignMul + | OperatorType::AssignDiv + | OperatorType::AssignMod + | OperatorType::AssignMinus + | OperatorType::AssignShiftLeft + | OperatorType::AssignBitwiseOr + | OperatorType::AssignBitwiseNot + | OperatorType::AssignBitwiseAnd + | OperatorType::AssignBitwiseXor + | OperatorType::AssignShiftRight + | OperatorType::AssignPlus => {} + OperatorType::LogicalNot + | OperatorType::BitwiseNot + | OperatorType::TernaryColon + | OperatorType::TernaryQuestionMark => return Err("Invalid operator".to_string()), } analyze(lhs, state)?; analyze(rhs, state) From 243ad122050a3d7cab6232d293b84cbf98502281 Mon Sep 17 00:00:00 2001 From: Felix Schwickerath Date: Mon, 16 Jun 2025 10:34:50 +0200 Subject: [PATCH 25/52] fix(while): fixes for continue --- src/backend/codegen.rs | 2 +- src/ir/constructor.rs | 16 +++++++++++++--- src/ir/node/binary_operation.rs | 2 +- src/ir/node/projection.rs | 2 +- src/ir/node/unary_operation.rs | 2 +- src/lexer/mod.rs | 5 +---- 6 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/backend/codegen.rs b/src/backend/codegen.rs index 8652e5d..570f999 100644 --- a/src/backend/codegen.rs +++ b/src/backend/codegen.rs @@ -375,7 +375,7 @@ impl CodeGenerator { operand.0, predecessor_skip_projection(operand_block, operand.1), )) - .unwrap(); + .expect(format!("Expected register for {:?}", operand).as_str()); if !source_register.hardware_register() && !destination_register.hardware_register() { diff --git a/src/ir/constructor.rs b/src/ir/constructor.rs index 0c82117..2961c19 100644 --- a/src/ir/constructor.rs +++ b/src/ir/constructor.rs @@ -336,6 +336,8 @@ impl IRGraphConstructor { let mut loop_body = Block::new("while-body".to_string()); loop_body.register_entry_point(self.current_block_index, entry_false_projection); let loop_body_index = self.graph.register_block(loop_body); + let loop_back_block = Block::new("while-condition".to_string()); + let loop_back_block_index = self.graph.register_block(loop_back_block); let mut following_block = Block::new("while-following".to_string()); following_block .register_entry_point(self.current_block_index, entry_true_projection); @@ -343,10 +345,18 @@ impl IRGraphConstructor { self.seal_block(self.current_block_index); self.current_block_index = loop_body_index; - self.active_loop_entries.push(loop_body_index); + self.active_loop_entries.push(loop_back_block_index); self.active_loop_exits.push(following_block_index); self.convert_boxed(expression); + let loop_body_jump = self.create_jump(); + self.graph + .get_block_mut(loop_back_block_index) + .register_entry_point(loop_body_index, loop_body_jump); + self.active_loop_entries.pop(); + self.active_loop_exits.pop(); + self.current_block_index = loop_back_block_index; + self.seal_block(loop_back_block_index); let condition_node = self.convert_boxed(condition)?; let conditional_jump = self.create_conditional_jump(condition_node); @@ -355,11 +365,11 @@ impl IRGraphConstructor { self.graph .get_block_mut(loop_body_index) - .register_entry_point(loop_body_index, true_projection); + .register_entry_point(loop_back_block_index, true_projection); self.graph .get_block_mut(following_block_index) - .register_entry_point(loop_body_index, false_projection); + .register_entry_point(loop_back_block_index, false_projection); self.seal_block(loop_body_index); self.current_block_index = following_block_index; self.seal_block(self.current_block_index); diff --git a/src/ir/node/binary_operation.rs b/src/ir/node/binary_operation.rs index 4591509..3b0c486 100644 --- a/src/ir/node/binary_operation.rs +++ b/src/ir/node/binary_operation.rs @@ -1,4 +1,4 @@ -use crate::ir::{block::NodeIndex, graph::BlockIndex}; +use crate::ir::block::NodeIndex; #[derive(Eq, Hash, PartialEq, Clone, Debug)] pub struct BinaryOperationData { diff --git a/src/ir/node/projection.rs b/src/ir/node/projection.rs index 7917b59..b8ac773 100644 --- a/src/ir/node/projection.rs +++ b/src/ir/node/projection.rs @@ -1,4 +1,4 @@ -use crate::ir::{block::NodeIndex, graph::BlockIndex}; +use crate::ir::block::NodeIndex; #[derive(Eq, Hash, PartialEq, Clone, Debug)] pub enum ProjectionInformation { diff --git a/src/ir/node/unary_operation.rs b/src/ir/node/unary_operation.rs index 54e0903..1e8d9dd 100644 --- a/src/ir/node/unary_operation.rs +++ b/src/ir/node/unary_operation.rs @@ -1,4 +1,4 @@ -use crate::ir::{block::NodeIndex, graph::BlockIndex}; +use crate::ir::block::NodeIndex; #[derive(Eq, Hash, PartialEq, Clone, Debug)] pub struct UnaryOperationData { diff --git a/src/lexer/mod.rs b/src/lexer/mod.rs index 40a7b45..4f35c13 100644 --- a/src/lexer/mod.rs +++ b/src/lexer/mod.rs @@ -5,10 +5,7 @@ use tracing::trace; use crate::{ parser::error::ParseError, - util::{ - position::{self, Position}, - span::Span, - }, + util::{position::Position, span::Span}, }; pub mod token; From d5af8d3c3464e6b0c4653baa5f0fe507c3e08054 Mon Sep 17 00:00:00 2001 From: Felix Schwickerath Date: Mon, 16 Jun 2025 10:50:51 +0200 Subject: [PATCH 26/52] feat(main): change levels to debug to see differences in compilations --- src/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index a38f860..fdb0a25 100644 --- a/src/main.rs +++ b/src/main.rs @@ -57,11 +57,11 @@ fn main() { panic!("Result from parser is not a program tree!"); } for ir_graph in ir_graphs.iter().clone() { - debug!("Constructed IR: {}", ir_graph); + info!("Constructed IR: {}", ir_graph); } let code_generator = CodeGenerator::new(ir_graphs); let assembler = code_generator.generate(); - debug!("Assembler contents: {}", &assembler); + info!("Assembler contents: {}", &assembler); fs::write(temp.clone(), assembler).expect("Filesystem: Failed to write assembler output"); info!( "Filesystem: Wrote assembler to {}", From efc208afd9013e13cb721a21ad0e836d0621c319 Mon Sep 17 00:00:00 2001 From: Felix Schwickerath Date: Mon, 16 Jun 2025 11:25:33 +0200 Subject: [PATCH 27/52] fix(ir): fix generation of conditional jumps --- src/backend/codegen.rs | 61 +++++++++++++++++++++++++++++------------- 1 file changed, 43 insertions(+), 18 deletions(-) diff --git a/src/backend/codegen.rs b/src/backend/codegen.rs index 570f999..ffb34c7 100644 --- a/src/backend/codegen.rs +++ b/src/backend/codegen.rs @@ -262,20 +262,12 @@ impl CodeGenerator { "sar", )); } - Node::Equals(data) - | Node::HigherEquals(data) - | Node::LowerEquals(data) - | Node::NotEquals(data) - | Node::Lower(data) - | Node::Higher(data) => { - code.push_str(&self.generate_comparison( - block, - block_index, - data, - ir_graph, - registers, - )); - } + Node::Equals(_) + | Node::HigherEquals(_) + | Node::LowerEquals(_) + | Node::NotEquals(_) + | Node::Lower(_) + | Node::Higher(_) => {} Node::LogicalNot(data) => { let register = registers.get(&(block_index, data.input())).unwrap(); code.push_str(&format!( @@ -317,6 +309,7 @@ impl CodeGenerator { block, block_index, *following_block_index, + ir_graph, registers, ); code.push_str(&self.generate_phi_moves( @@ -414,12 +407,26 @@ impl CodeGenerator { code } + pub fn generate_condition_for_jump( + &self, + block: &Block, + block_index: BlockIndex, + data: &BinaryOperationData, + ir_graph: &IRGraph, + registers: &Registers, + ) -> String { + let mut code = String::new(); + code.push_str(&self.generate_comparison(block, block_index, data, ir_graph, registers)); + code + } + pub fn generate_conditional_jump( &self, projection_index: usize, current_block: &Block, current_block_index: BlockIndex, previous_block: usize, + ir_graph: &IRGraph, registers: &Registers, ) -> Option { let mut code = String::new(); @@ -430,13 +437,31 @@ impl CodeGenerator { .predecessors() .get(0) .unwrap(); + let comparison_node = current_block.get_node(comparision); + match comparison_node { + Node::Lower(data) + | Node::LowerEquals(data) + | Node::Equals(data) + | Node::NotEquals(data) + | Node::Higher(data) + | Node::HigherEquals(data) => { + code.push_str(&self.generate_condition_for_jump( + current_block, + current_block_index, + data, + ir_graph, + registers, + )); + } + _ => {} + } let op_code = match current_block.get_node(comparision) { - Node::Lower(_) => "jb", - Node::LowerEquals(_) => "jbe", + Node::Lower(_) => "jl", + Node::LowerEquals(_) => "jle", Node::Equals(_) => "je", Node::NotEquals(_) => "jne", - Node::HigherEquals(_) => "jae", - Node::Higher(_) => "ja", + Node::HigherEquals(_) => "jge", + Node::Higher(_) => "jg", Node::ConstantBool(_) => "je", Node::BitwiseNegate(_) => "jne", Node::LogicalNot(_) => "je", From e22e0f2696a5eec7e1e2614160b6a16e1d05a208 Mon Sep 17 00:00:00 2001 From: Felix Schwickerath Date: Mon, 16 Jun 2025 11:36:48 +0200 Subject: [PATCH 28/52] feat(semantic): add return analysis for ifs --- Cargo.toml | 2 +- src/semantic/mod.rs | 14 +++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6a4efe3..c7625da 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,5 +5,5 @@ edition = "2021" [dependencies] rand = "0.9.1" -tracing = { version = "0.1.41", features=["release_max_level_warn"]} +tracing = { version = "0.1.41", features=["release_max_level_info"]} tracing-subscriber = "0.3.19" diff --git a/src/semantic/mod.rs b/src/semantic/mod.rs index 792f977..bedcd0a 100644 --- a/src/semantic/mod.rs +++ b/src/semantic/mod.rs @@ -38,7 +38,7 @@ impl AnalysisState { } } -#[derive(PartialEq)] +#[derive(PartialEq, Clone)] enum ReturnState { Returning, NotReturing, @@ -360,9 +360,21 @@ pub fn analyze(tree: Box, state: &mut AnalysisState) -> Result<(), String> { return Err("Condition must be a boolean".to_string()); } + state.return_state = ReturnState::NotReturing; analyze(expression, state)?; + let if_return_state = state.return_state.clone(); if let Some(other_expression) = else_expression { + state.return_state = ReturnState::NotReturing; analyze(other_expression, state)?; + if state.return_state.eq(&ReturnState::Returning) + && if_return_state.eq(&ReturnState::Returning) + { + state.return_state = ReturnState::Returning; + } else { + state.return_state = ReturnState::NotReturing; + } + } else { + state.return_state = ReturnState::NotReturing; } Ok(()) } From 64b7f54371fbd1499c91aa5908f54a387c4e9d6c Mon Sep 17 00:00:00 2001 From: Felix Schwickerath Date: Mon, 16 Jun 2025 13:49:48 +0200 Subject: [PATCH 29/52] feat(ir): recursive generation of some jump conditions --- src/backend/codegen.rs | 147 +++++++++++++++++++++++++++++++++-------- 1 file changed, 118 insertions(+), 29 deletions(-) diff --git a/src/backend/codegen.rs b/src/backend/codegen.rs index ffb34c7..b9ceb4e 100644 --- a/src/backend/codegen.rs +++ b/src/backend/codegen.rs @@ -267,15 +267,10 @@ impl CodeGenerator { | Node::LowerEquals(_) | Node::NotEquals(_) | Node::Lower(_) - | Node::Higher(_) => {} - Node::LogicalNot(data) => { - let register = registers.get(&(block_index, data.input())).unwrap(); - code.push_str(&format!( - "test {}, {}\n", - register.as_32_bit_assembly(), - register.as_32_bit_assembly() - )); - } + | Node::Higher(_) + | Node::LogicalNot(_) + | Node::And(_) + | Node::Or(_) => {} Node::BitwiseNegate(data) => { let register = registers.get(&(block_index, data.input())).unwrap(); code.push_str(&format!("not {}\n", register.as_32_bit_assembly())); @@ -304,7 +299,7 @@ impl CodeGenerator { jump_information ); let following_block_index = jump_information.get(&node_index).unwrap(); - let conditional_jump_code = self.generate_conditional_jump( + let conditional_jump_code = self.generate_conditional_jump_rec( node_index, block, block_index, @@ -420,7 +415,7 @@ impl CodeGenerator { code } - pub fn generate_conditional_jump( + pub fn generate_conditional_jump_rec( &self, projection_index: usize, current_block: &Block, @@ -430,7 +425,6 @@ impl CodeGenerator { registers: &Registers, ) -> Option { let mut code = String::new(); - let true_label = self.jump_label.get(&previous_block).unwrap(); let projection = current_block.get_node(projection_index); let comparision = *current_block .get_node(*projection.predecessors().get(0).unwrap()) @@ -439,23 +433,72 @@ impl CodeGenerator { .unwrap(); let comparison_node = current_block.get_node(comparision); match comparison_node { - Node::Lower(data) - | Node::LowerEquals(data) - | Node::Equals(data) - | Node::NotEquals(data) - | Node::Higher(data) - | Node::HigherEquals(data) => { - code.push_str(&self.generate_condition_for_jump( + Node::LogicalNot(data) => { + let node = current_block.get_node(data.input()); + code.push_str(&self.generate_conditional_jump( + node, + data.input(), current_block, current_block_index, - data, + previous_block, + self.get_inverted_jump_opcode(node), ir_graph, registers, - )); + )?); + } + Node::And(data) => { + let lhs_node = current_block.get_node(data.lhs()); + code.push_str(&self.generate_conditional_jump( + lhs_node, + data.lhs(), + current_block, + current_block_index, + previous_block, + self.get_jump_opcode(comparison_node), + ir_graph, + registers, + )?); + let rhs_node = current_block.get_node(data.rhs()); + code.push_str(&self.generate_conditional_jump( + rhs_node, + data.rhs(), + current_block, + current_block_index, + previous_block, + self.get_jump_opcode(comparison_node), + ir_graph, + registers, + )?); + } + Node::Or(_) => { + unimplemented!() + } + Node::Lower(_) + | Node::LowerEquals(_) + | Node::Equals(_) + | Node::NotEquals(_) + | Node::Higher(_) + | Node::HigherEquals(_) + | Node::Phi(_) + | Node::ConstantInt(_) => { + code.push_str(&self.generate_conditional_jump( + comparison_node, + comparision, + current_block, + current_block_index, + previous_block, + self.get_jump_opcode(comparison_node), + ir_graph, + registers, + )?); } _ => {} } - let op_code = match current_block.get_node(comparision) { + Some(code) + } + + pub fn get_jump_opcode(&self, comparision: &Node) -> &str { + match comparision { Node::Lower(_) => "jl", Node::LowerEquals(_) => "jle", Node::Equals(_) => "je", @@ -463,17 +506,63 @@ impl CodeGenerator { Node::HigherEquals(_) => "jge", Node::Higher(_) => "jg", Node::ConstantBool(_) => "je", - Node::BitwiseNegate(_) => "jne", Node::LogicalNot(_) => "je", + Node::Phi(_) | Node::ConstantInt(_) => "jnz", + node => panic!("Invalid operation before conditional jump: {}", node), + } + } + pub fn get_inverted_jump_opcode(&self, comparision: &Node) -> &str { + match comparision { + Node::Lower(_) => "jge", + Node::LowerEquals(_) => "jg", + Node::Equals(_) => "jne", + Node::NotEquals(_) => "je", + Node::HigherEquals(_) => "jl", + Node::Higher(_) => "jle", + Node::ConstantBool(_) => "jne", + Node::LogicalNot(_) => "je", + Node::Phi(_) | Node::ConstantInt(_) => "jz", + node => panic!("Invalid operation before conditional jump: {}", node), + } + } + + pub fn generate_conditional_jump( + &self, + comparision: &Node, + comparision_index: NodeIndex, + current_block: &Block, + current_block_index: BlockIndex, + jump_target: BlockIndex, + op_code: &str, + ir_graph: &IRGraph, + registers: &Registers, + ) -> Option { + let mut code = String::new(); + let jump_label = self.jump_label.get(&jump_target).unwrap(); + match comparision { + Node::Lower(data) + | Node::LowerEquals(data) + | Node::Equals(data) + | Node::NotEquals(data) + | Node::Higher(data) + | Node::HigherEquals(data) => { + code.push_str(&self.generate_condition_for_jump( + current_block, + current_block_index, + data, + ir_graph, + registers, + )); + } Node::Phi(_) | Node::ConstantInt(_) => { - let register = registers.get(&(current_block_index, comparision)).unwrap(); + let register = registers + .get(&(current_block_index, comparision_index)) + .unwrap(); code.push_str(&format!("test $0x1, {}\n", register.as_32_bit_assembly())); - code.push_str(&format!("jnz {}\n", true_label)); - return Some(code); } - node => panic!("Invalid operation before conditional jump: {}", node), - }; - code.push_str(&format!("{} {}\n", op_code, true_label)); + _ => {} + } + code.push_str(&format!("{} {}\n", op_code, jump_label)); Some(code) } From bf6365b6dc5d596d9b67846fcc280389ca48fe33 Mon Sep 17 00:00:00 2001 From: Felix Schwickerath Date: Mon, 16 Jun 2025 14:09:59 +0200 Subject: [PATCH 30/52] feat(ir): recursive generation of boolean conditions --- Cargo.toml | 2 +- src/backend/codegen.rs | 68 +++++++++++++++++++++++++++++------------- 2 files changed, 48 insertions(+), 22 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c7625da..6a4efe3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,5 +5,5 @@ edition = "2021" [dependencies] rand = "0.9.1" -tracing = { version = "0.1.41", features=["release_max_level_info"]} +tracing = { version = "0.1.41", features=["release_max_level_warn"]} tracing-subscriber = "0.3.19" diff --git a/src/backend/codegen.rs b/src/backend/codegen.rs index b9ceb4e..bbd781e 100644 --- a/src/backend/codegen.rs +++ b/src/backend/codegen.rs @@ -299,11 +299,14 @@ impl CodeGenerator { jump_information ); let following_block_index = jump_information.get(&node_index).unwrap(); + let false_block_index = jump_information.get(&(node_index + 1)).unwrap(); let conditional_jump_code = self.generate_conditional_jump_rec( + false, node_index, block, block_index, *following_block_index, + *false_block_index, ir_graph, registers, ); @@ -417,10 +420,12 @@ impl CodeGenerator { pub fn generate_conditional_jump_rec( &self, + inverted: bool, projection_index: usize, current_block: &Block, current_block_index: BlockIndex, - previous_block: usize, + jump_block_index: usize, + other_jump_block_index: usize, ir_graph: &IRGraph, registers: &Registers, ) -> Option { @@ -434,44 +439,60 @@ impl CodeGenerator { let comparison_node = current_block.get_node(comparision); match comparison_node { Node::LogicalNot(data) => { - let node = current_block.get_node(data.input()); - code.push_str(&self.generate_conditional_jump( - node, + code.push_str(&self.generate_conditional_jump_rec( + !inverted, data.input(), current_block, current_block_index, - previous_block, - self.get_inverted_jump_opcode(node), + jump_block_index, + other_jump_block_index, ir_graph, registers, )?); } Node::And(data) => { - let lhs_node = current_block.get_node(data.lhs()); - code.push_str(&self.generate_conditional_jump( - lhs_node, + code.push_str(&self.generate_conditional_jump_rec( + inverted, data.lhs(), current_block, current_block_index, - previous_block, - self.get_jump_opcode(comparison_node), + jump_block_index, + other_jump_block_index, ir_graph, registers, )?); - let rhs_node = current_block.get_node(data.rhs()); - code.push_str(&self.generate_conditional_jump( - rhs_node, + code.push_str(&self.generate_conditional_jump_rec( + inverted, data.rhs(), current_block, current_block_index, - previous_block, - self.get_jump_opcode(comparison_node), + jump_block_index, + other_jump_block_index, ir_graph, registers, )?); } - Node::Or(_) => { - unimplemented!() + Node::Or(data) => { + code.push_str(&self.generate_conditional_jump_rec( + !inverted, + data.lhs(), + current_block, + current_block_index, + other_jump_block_index, + jump_block_index, + ir_graph, + registers, + )?); + code.push_str(&self.generate_conditional_jump_rec( + !inverted, + data.rhs(), + current_block, + current_block_index, + other_jump_block_index, + jump_block_index, + ir_graph, + registers, + )?); } Node::Lower(_) | Node::LowerEquals(_) @@ -481,13 +502,18 @@ impl CodeGenerator { | Node::HigherEquals(_) | Node::Phi(_) | Node::ConstantInt(_) => { + let op_code = if inverted { + self.get_inverted_jump_opcode(comparison_node) + } else { + self.get_jump_opcode(comparison_node) + }; code.push_str(&self.generate_conditional_jump( comparison_node, comparision, current_block, current_block_index, - previous_block, - self.get_jump_opcode(comparison_node), + jump_block_index, + op_code, ir_graph, registers, )?); @@ -520,7 +546,7 @@ impl CodeGenerator { Node::HigherEquals(_) => "jl", Node::Higher(_) => "jle", Node::ConstantBool(_) => "jne", - Node::LogicalNot(_) => "je", + Node::LogicalNot(_) => "jne", Node::Phi(_) | Node::ConstantInt(_) => "jz", node => panic!("Invalid operation before conditional jump: {}", node), } From b82e8a44ab5d57f6adf9100ab96dbe1c6adc7473 Mon Sep 17 00:00:00 2001 From: Felix Schwickerath Date: Mon, 16 Jun 2025 14:45:45 +0200 Subject: [PATCH 31/52] fix(ir): fix boolean conditional jump generation --- src/backend/codegen.rs | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/backend/codegen.rs b/src/backend/codegen.rs index bbd781e..80e6d30 100644 --- a/src/backend/codegen.rs +++ b/src/backend/codegen.rs @@ -300,9 +300,16 @@ impl CodeGenerator { ); let following_block_index = jump_information.get(&node_index).unwrap(); let false_block_index = jump_information.get(&(node_index + 1)).unwrap(); + let projection = block.get_node(node_index); + let comparision = *block + .get_node(*projection.predecessors().get(0).unwrap()) + .predecessors() + .get(0) + .unwrap(); + let conditional_jump_code = self.generate_conditional_jump_rec( false, - node_index, + comparision, block, block_index, *following_block_index, @@ -421,7 +428,7 @@ impl CodeGenerator { pub fn generate_conditional_jump_rec( &self, inverted: bool, - projection_index: usize, + comparison_index: usize, current_block: &Block, current_block_index: BlockIndex, jump_block_index: usize, @@ -430,13 +437,8 @@ impl CodeGenerator { registers: &Registers, ) -> Option { let mut code = String::new(); - let projection = current_block.get_node(projection_index); - let comparision = *current_block - .get_node(*projection.predecessors().get(0).unwrap()) - .predecessors() - .get(0) - .unwrap(); - let comparison_node = current_block.get_node(comparision); + let comparison_node = current_block.get_node(comparison_index); + trace!("Generating conditional jump code for {:?}", comparison_node); match comparison_node { Node::LogicalNot(data) => { code.push_str(&self.generate_conditional_jump_rec( @@ -509,7 +511,7 @@ impl CodeGenerator { }; code.push_str(&self.generate_conditional_jump( comparison_node, - comparision, + comparison_index, current_block, current_block_index, jump_block_index, @@ -520,6 +522,7 @@ impl CodeGenerator { } _ => {} } + trace!("Code generated for {:?}: {}", comparison_node, code); Some(code) } From 0d37aa9325ee465bacce4d0cc1dc3678ab99bfaf Mon Sep 17 00:00:00 2001 From: Felix Schwickerath Date: Mon, 16 Jun 2025 14:48:55 +0200 Subject: [PATCH 32/52] fix(semantic): returns in for and while should be ignored for return analysis --- src/semantic/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/semantic/mod.rs b/src/semantic/mod.rs index bedcd0a..63eb73c 100644 --- a/src/semantic/mod.rs +++ b/src/semantic/mod.rs @@ -388,6 +388,7 @@ pub fn analyze(tree: Box, state: &mut AnalysisState) -> Result<(), String> } state.enter_loop(); analyze(expression, state)?; + state.return_state = ReturnState::NotReturing; state.exit_loop(); Ok(()) } @@ -407,6 +408,7 @@ pub fn analyze(tree: Box, state: &mut AnalysisState) -> Result<(), String> } state.enter_loop(); analyze(expression, state)?; + state.return_state = ReturnState::NotReturing; state.exit_loop(); Ok(()) } From 28a160db2a2cccb0279528805ec1f497936bb67d Mon Sep 17 00:00:00 2001 From: Felix Schwickerath Date: Mon, 16 Jun 2025 14:53:00 +0200 Subject: [PATCH 33/52] fix(semantic): do not count definitions in for postcondition --- src/semantic/mod.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/semantic/mod.rs b/src/semantic/mod.rs index 63eb73c..513439e 100644 --- a/src/semantic/mod.rs +++ b/src/semantic/mod.rs @@ -8,6 +8,7 @@ use crate::{ util::int_parsing::parse_int, }; +#[derive(Clone)] pub struct AnalysisState { return_state: ReturnState, namespace: HashMap, @@ -44,6 +45,7 @@ enum ReturnState { NotReturing, } +#[derive(Clone)] pub struct VariableStatus { type_status: Type, declaration_status: DeclarationStatus, @@ -70,7 +72,7 @@ impl VariableStatus { } } -#[derive(PartialEq, PartialOrd)] +#[derive(PartialEq, PartialOrd, Clone)] pub enum DeclarationStatus { Declared, Initialized, @@ -298,9 +300,6 @@ pub fn analyze(tree: Box, state: &mut AnalysisState) -> Result<(), String> Tree::Block(statements, _) => { for statement in statements { analyze(Box::new(statement.clone()), state)?; - if let Tree::Return(_, _) = statement { - break; - } } Ok(()) } @@ -404,7 +403,9 @@ pub fn analyze(tree: Box, state: &mut AnalysisState) -> Result<(), String> return Err("Condition must be a boolean".to_string()); } if let Some(updater_expression) = updater { + let old_state = state.clone(); analyze(updater_expression, state)?; + *state = old_state; } state.enter_loop(); analyze(expression, state)?; From 8a6cedb69eb54c4e76ead4737e6f732c38ed9fcf Mon Sep 17 00:00:00 2001 From: Felix Schwickerath Date: Mon, 16 Jun 2025 15:00:46 +0200 Subject: [PATCH 34/52] fix(semantic): do not reset returning state for loops if function returns earlier --- src/semantic/mod.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/semantic/mod.rs b/src/semantic/mod.rs index 513439e..c488e06 100644 --- a/src/semantic/mod.rs +++ b/src/semantic/mod.rs @@ -352,6 +352,7 @@ pub fn analyze(tree: Box, state: &mut AnalysisState) -> Result<(), String> analyze(false_statement, state) } Tree::If(condition, expression, else_expression, _) => { + let returning_state = state.return_state.clone(); analyze(condition.clone(), state)?; if get_variable_type(condition, state) .ok_or("Variable undefined!")? @@ -370,14 +371,15 @@ pub fn analyze(tree: Box, state: &mut AnalysisState) -> Result<(), String> { state.return_state = ReturnState::Returning; } else { - state.return_state = ReturnState::NotReturing; + state.return_state = returning_state; } } else { - state.return_state = ReturnState::NotReturing; + state.return_state = returning_state; } Ok(()) } Tree::While(condition, expression, _) => { + let returning_state = state.return_state.clone(); analyze(condition.clone(), state)?; if get_variable_type(condition, state) .ok_or("Variable undefined!")? @@ -387,11 +389,12 @@ pub fn analyze(tree: Box, state: &mut AnalysisState) -> Result<(), String> } state.enter_loop(); analyze(expression, state)?; - state.return_state = ReturnState::NotReturing; + state.return_state = returning_state; state.exit_loop(); Ok(()) } Tree::For(initializer, condition, updater, expression, _) => { + let returning_state = state.return_state.clone(); if let Some(initializer_expression) = initializer { analyze(initializer_expression, state)?; } @@ -409,7 +412,7 @@ pub fn analyze(tree: Box, state: &mut AnalysisState) -> Result<(), String> } state.enter_loop(); analyze(expression, state)?; - state.return_state = ReturnState::NotReturing; + state.return_state = returning_state; state.exit_loop(); Ok(()) } From 00c31a4e3a40bb31ca137c38e2b5e80543eed224 Mon Sep 17 00:00:00 2001 From: Felix Schwickerath Date: Mon, 16 Jun 2025 15:07:06 +0200 Subject: [PATCH 35/52] fix(semantic): fixing more namespace issues --- src/semantic/mod.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/semantic/mod.rs b/src/semantic/mod.rs index c488e06..086d58c 100644 --- a/src/semantic/mod.rs +++ b/src/semantic/mod.rs @@ -80,6 +80,7 @@ pub enum DeclarationStatus { #[must_use] pub fn analyze(tree: Box, state: &mut AnalysisState) -> Result<(), String> { + trace!("Analysing: {:?}", tree); match *tree { Tree::Literal(value, base, _) => { if base != 16 && base != 10 { @@ -298,9 +299,11 @@ pub fn analyze(tree: Box, state: &mut AnalysisState) -> Result<(), String> analyze(rhs, state) } Tree::Block(statements, _) => { + let old_namespace = state.namespace.clone(); for statement in statements { analyze(Box::new(statement.clone()), state)?; } + state.namespace = old_namespace; Ok(()) } Tree::LValueIdentifier(name) => analyze(name, state), @@ -398,6 +401,7 @@ pub fn analyze(tree: Box, state: &mut AnalysisState) -> Result<(), String> if let Some(initializer_expression) = initializer { analyze(initializer_expression, state)?; } + let old_namespace = state.namespace.clone(); analyze(condition.clone(), state)?; if get_variable_type(condition, state) .ok_or("Variable undefined!")? @@ -406,14 +410,13 @@ pub fn analyze(tree: Box, state: &mut AnalysisState) -> Result<(), String> return Err("Condition must be a boolean".to_string()); } if let Some(updater_expression) = updater { - let old_state = state.clone(); analyze(updater_expression, state)?; - *state = old_state; } state.enter_loop(); analyze(expression, state)?; state.return_state = returning_state; state.exit_loop(); + state.namespace = old_namespace; Ok(()) } } From 101f22bcc578a086842a3a4dac5dd747ac08e5c1 Mon Sep 17 00:00:00 2001 From: Felix Schwickerath Date: Mon, 16 Jun 2025 16:52:26 +0200 Subject: [PATCH 36/52] fix(ir): fix referencing for entry points loops and nesting --- src/backend/codegen.rs | 5 ++++- src/ir/constructor.rs | 43 ++++++++++++++++++++++++++++++------------ src/ir/graph.rs | 4 ++-- 3 files changed, 37 insertions(+), 15 deletions(-) diff --git a/src/backend/codegen.rs b/src/backend/codegen.rs index 80e6d30..468ab14 100644 --- a/src/backend/codegen.rs +++ b/src/backend/codegen.rs @@ -373,7 +373,10 @@ impl CodeGenerator { operand.0, predecessor_skip_projection(operand_block, operand.1), )) - .expect(format!("Expected register for {:?}", operand).as_str()); + .expect( + format!("Expected register for {:?}", ir_graph.get_node(operand)) + .as_str(), + ); if !source_register.hardware_register() && !destination_register.hardware_register() { diff --git a/src/ir/constructor.rs b/src/ir/constructor.rs index 2961c19..5719d14 100644 --- a/src/ir/constructor.rs +++ b/src/ir/constructor.rs @@ -55,7 +55,7 @@ impl IRGraphConstructor { } pub fn convert(&mut self, tree: Tree) -> Option { - debug!("Converting AST {} to IR!", tree); + debug!("Converting AST {:?} to IR!", tree); match tree { Tree::Program(_) => { unimplemented!("Program Trees cannot be parsed to a single IR Representation!") @@ -351,7 +351,8 @@ impl IRGraphConstructor { let loop_body_jump = self.create_jump(); self.graph .get_block_mut(loop_back_block_index) - .register_entry_point(loop_body_index, loop_body_jump); + .register_entry_point(self.current_block_index, loop_body_jump); + self.seal_block(self.current_block_index); self.active_loop_entries.pop(); self.active_loop_exits.pop(); @@ -442,10 +443,11 @@ impl IRGraphConstructor { self.active_loop_entries.pop(); self.active_loop_exits.pop(); let loop_body_exit = self.create_jump(); + self.seal_block(self.current_block_index); self.graph .get_block_mut(loop_post_index) - .register_entry_point(loop_body_index, loop_body_exit); + .register_entry_point(self.current_block_index, loop_body_exit); self.current_block_index = loop_post_index; self.seal_block(loop_post_index); @@ -734,12 +736,13 @@ impl IRGraphConstructor { .map(|(v1, v2)| (v1, *v2)) .collect(); trace!("Creating phi with operands {:?}", operands); - let current_block = self.graph.get_block_mut(self.current_block_index); + let current_block = self.graph.get_block_mut(block_index); current_block.register_node(Node::Phi(PhiData::new(operands))) } fn create_phi_variable_operands( &mut self, + phi: usize, block_index: BlockIndex, variable: Name, ) -> NodeIndex { @@ -758,8 +761,13 @@ impl IRGraphConstructor { variable, operands ); - let current_block = self.graph.get_block_mut(block_index); - current_block.register_node(Node::Phi(PhiData::new(operands))) + + if let Node::Phi(data) = self.graph.get_block_mut(block_index).get_node_mut(phi) { + for operand in operands { + data.add_operand(operand); + } + } + phi } fn create_div_mod_projection(&mut self, input: NodeIndex) -> NodeIndex { @@ -868,29 +876,39 @@ impl IRGraphConstructor { // The value for the variable can come from multiple previous blocks. // The value of the variable is dependent on the values of the variable in the previous // blocks - let phi = self.create_phi_variable_operands(block_index, variable.clone()); + let phi = self + .graph + .get_block_mut(block_index) + .register_node(Node::Phi(PhiData::empty())); self.write_variable(variable.clone(), block_index, phi); + self.create_phi_variable_operands(phi, block_index, variable.clone()); phi }; // Denote that the newly created phi defines the variable in the current block - self.write_variable(variable.clone(), self.current_block_index, node); + self.write_variable(variable.clone(), block_index, node); node } fn seal_block(&mut self, block: BlockIndex) { - trace!("Current graph before sealing block: {}", self.graph); + debug!( + "Current graph before sealing block {}: {}", + block, self.graph + ); info!("Incomplete Phis: {:?}", self.incomplete_phis); if !self.incomplete_phis.contains_key(&block) { self.sealed_blocks.push(block); return; } for (block_index, definitions) in &self.incomplete_phis.clone() { + if block.ne(block_index) { + continue; + } for (variable, phi) in definitions { let operands = { let mut operands = Vec::new(); - let block = self.graph.get_block_mut(*block_index); - for (prev_block, _) in block.entry_points().clone() { + let phi_block = self.graph.get_block_mut(*block_index); + for (prev_block, prev_node) in phi_block.entry_points().clone() { operands .push((prev_block, self.read_variable(variable.clone(), prev_block))); } @@ -916,7 +934,8 @@ impl IRGraphConstructor { } fn read_current_side_effect(&mut self) -> usize { - self.read_side_effect(self.current_block_index) + return 0; + //self.read_side_effect(self.current_block_index) } fn read_side_effect(&mut self, block: usize) -> usize { diff --git a/src/ir/graph.rs b/src/ir/graph.rs index 98af7d6..8f1a908 100644 --- a/src/ir/graph.rs +++ b/src/ir/graph.rs @@ -79,8 +79,8 @@ impl Default for IRGraph { impl Display for IRGraph { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - for block in &self.blocks { - writeln!(f, "{}", block)?; + for (index, block) in self.blocks.iter().enumerate() { + writeln!(f, "{} -- {}", index, block)?; } Ok(()) } From fa4e2fcfebaa38b00d58dca47dd0f1dda1b07831 Mon Sep 17 00:00:00 2001 From: Felix Schwickerath Date: Mon, 16 Jun 2025 17:17:35 +0200 Subject: [PATCH 37/52] fix(semantic): namespace fixes for if --- src/semantic/mod.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/semantic/mod.rs b/src/semantic/mod.rs index 086d58c..ebbce9a 100644 --- a/src/semantic/mod.rs +++ b/src/semantic/mod.rs @@ -356,6 +356,7 @@ pub fn analyze(tree: Box, state: &mut AnalysisState) -> Result<(), String> } Tree::If(condition, expression, else_expression, _) => { let returning_state = state.return_state.clone(); + let old_namespace = state.namespace.clone(); analyze(condition.clone(), state)?; if get_variable_type(condition, state) .ok_or("Variable undefined!")? @@ -365,6 +366,8 @@ pub fn analyze(tree: Box, state: &mut AnalysisState) -> Result<(), String> } state.return_state = ReturnState::NotReturing; analyze(expression, state)?; + let true_namespace = state.namespace.clone(); + state.namespace = old_namespace.clone(); let if_return_state = state.return_state.clone(); if let Some(other_expression) = else_expression { state.return_state = ReturnState::NotReturing; @@ -376,6 +379,26 @@ pub fn analyze(tree: Box, state: &mut AnalysisState) -> Result<(), String> } else { state.return_state = returning_state; } + let false_namespace = state.namespace.clone(); + state.namespace = old_namespace; + for (initialized_variable, _) in true_namespace + .iter() + .filter(|(_, v)| v.declaration().eq(&DeclarationStatus::Initialized)) + { + if false_namespace.contains_key(initialized_variable) + && false_namespace + .get(initialized_variable) + .unwrap() + .declaration() + .eq(&DeclarationStatus::Initialized) + { + state + .namespace + .get_mut(initialized_variable) + .unwrap() + .set_initialized(); + } + } } else { state.return_state = returning_state; } From 062f507d3a4d3ebcf3f7e3dd8e9d8736b9b20fa5 Mon Sep 17 00:00:00 2001 From: Felix Schwickerath Date: Mon, 16 Jun 2025 17:24:36 +0200 Subject: [PATCH 38/52] fix(semantic): enforce types for assignment operators --- src/semantic/mod.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/semantic/mod.rs b/src/semantic/mod.rs index ebbce9a..a4cd2d3 100644 --- a/src/semantic/mod.rs +++ b/src/semantic/mod.rs @@ -289,7 +289,15 @@ pub fn analyze(tree: Box, state: &mut AnalysisState) -> Result<(), String> | OperatorType::AssignBitwiseAnd | OperatorType::AssignBitwiseXor | OperatorType::AssignShiftRight - | OperatorType::AssignPlus => {} + | OperatorType::AssignPlus => { + let lhs_type = + get_variable_type(lhs.clone(), state).ok_or("Variable undefined!")?; + let rhs_type = + get_variable_type(rhs.clone(), state).ok_or("Variable undefined!")?; + if lhs_type.ne(&rhs_type) { + return Err("Comparison operators must have equal types".to_string()); + } + } OperatorType::LogicalNot | OperatorType::BitwiseNot | OperatorType::TernaryColon From c4f6ed90c73d63ed68b6727af432dd04d6fd78c8 Mon Sep 17 00:00:00 2001 From: Felix Schwickerath Date: Mon, 16 Jun 2025 19:22:42 +0200 Subject: [PATCH 39/52] feat(codegen): implement XOR --- src/backend/codegen.rs | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/backend/codegen.rs b/src/backend/codegen.rs index 468ab14..99cecff 100644 --- a/src/backend/codegen.rs +++ b/src/backend/codegen.rs @@ -275,6 +275,47 @@ impl CodeGenerator { let register = registers.get(&(block_index, data.input())).unwrap(); code.push_str(&format!("not {}\n", register.as_32_bit_assembly())); } + Node::Xor(data) => { + let lhs_register = registers.get(&(block_index, data.lhs())).unwrap(); + let rhs_register = registers.get(&(block_index, data.rhs())).unwrap(); + let destination = registers.get(&(block_index, node_index)).unwrap(); + + if !lhs_register.hardware_register() && !rhs_register.hardware_register() { + code.push_str(&move_stack_variable(lhs_register)); + code.push_str(&format!( + "xor {}, {}\n", + HardwareRegister::Rbx.as_32_bit_assembly(), + rhs_register.as_32_bit_assembly() + )); + code.push_str(&format!( + "mov {}, {}\n", + HardwareRegister::Rbx.as_32_bit_assembly(), + destination.as_32_bit_assembly() + )); + } else if lhs_register.hardware_register() { + code.push_str(&format!( + "xor {}, {}\n", + lhs_register.as_32_bit_assembly(), + rhs_register.as_32_bit_assembly() + )); + code.push_str(&format!( + "mov {}, {}\n", + lhs_register.as_32_bit_assembly(), + destination.as_32_bit_assembly() + )); + } else { + code.push_str(&format!( + "xor {}, {}\n", + rhs_register.as_32_bit_assembly(), + lhs_register.as_32_bit_assembly() + )); + code.push_str(&format!( + "mov {}, {}\n", + rhs_register.as_32_bit_assembly(), + destination.as_32_bit_assembly() + )); + } + } Node::ConstantBool(data) => code.push_str(&self.generate_constant_bool(data.value())), Node::Phi(_) => {} Node::Jump => { From 3cb75ddc2da3ca34ccd35361b0bb7ae45b26d45f Mon Sep 17 00:00:00 2001 From: Felix Schwickerath Date: Mon, 16 Jun 2025 19:28:36 +0200 Subject: [PATCH 40/52] feat(ir): implement missing assignment operators --- src/ir/constructor.rs | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/ir/constructor.rs b/src/ir/constructor.rs index 5719d14..df3d7ac 100644 --- a/src/ir/constructor.rs +++ b/src/ir/constructor.rs @@ -127,6 +127,36 @@ impl IRGraphConstructor { let desugar = self.create_div_mod_projection(mod_node); self.write_variable(name, self.current_block_index, desugar); } + OperatorType::AssignShiftLeft => { + let lhs = + self.read_variable(name.clone(), self.current_block_index); + let desugar = self.create_shift_left(lhs, rhs); + self.write_variable(name, self.current_block_index, desugar); + } + OperatorType::AssignShiftRight => { + let lhs = + self.read_variable(name.clone(), self.current_block_index); + let desugar = self.create_shift_right(lhs, rhs); + self.write_variable(name, self.current_block_index, desugar); + } + OperatorType::AssignBitwiseOr => { + let lhs = + self.read_variable(name.clone(), self.current_block_index); + let desugar = self.create_or(lhs, rhs); + self.write_variable(name, self.current_block_index, desugar); + } + OperatorType::AssignBitwiseAnd => { + let lhs = + self.read_variable(name.clone(), self.current_block_index); + let desugar = self.create_and(lhs, rhs); + self.write_variable(name, self.current_block_index, desugar); + } + OperatorType::AssignBitwiseXor => { + let lhs = + self.read_variable(name.clone(), self.current_block_index); + let desugar = self.create_xor(lhs, rhs); + self.write_variable(name, self.current_block_index, desugar); + } OperatorType::Assign => { self.write_variable(name, self.current_block_index, rhs); } From 19c61c7a3088fa420e6bf4ab7ea9736fbcb5c9da Mon Sep 17 00:00:00 2001 From: Felix Schwickerath Date: Mon, 16 Jun 2025 19:33:01 +0200 Subject: [PATCH 41/52] fix(semantic): fix order of assignment initialization checking --- src/semantic/mod.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/semantic/mod.rs b/src/semantic/mod.rs index a4cd2d3..e04cfa3 100644 --- a/src/semantic/mod.rs +++ b/src/semantic/mod.rs @@ -170,15 +170,6 @@ pub fn analyze(tree: Box, state: &mut AnalysisState) -> Result<(), String> } else { VariableStatus::new(variable_type.clone(), DeclarationStatus::Declared) }; - if let Tree::Name(identifier, _) = *name { - let variable_status = state.namespace.get(&identifier); - if variable_status.is_some() { - return Err( - "Reinitializing or redeclaring variables are not allowed!".to_string() - ); - } - state.namespace.insert(identifier.clone(), variable_state); - } if let Some(present_initializer) = initializer { if get_variable_type(present_initializer.clone(), state) .ok_or("Variable undefined!")? @@ -193,6 +184,15 @@ pub fn analyze(tree: Box, state: &mut AnalysisState) -> Result<(), String> } analyze(present_initializer, state)?; }; + if let Tree::Name(identifier, _) = *name { + let variable_status = state.namespace.get(&identifier); + if variable_status.is_some() { + return Err( + "Reinitializing or redeclaring variables are not allowed!".to_string() + ); + } + state.namespace.insert(identifier.clone(), variable_state); + } Ok(()) } Tree::IdentifierExpression(identifier) => { From eea1bccf5d1d85c636e27a89824916e576f9582d Mon Sep 17 00:00:00 2001 From: Felix Schwickerath Date: Mon, 16 Jun 2025 19:36:38 +0200 Subject: [PATCH 42/52] fix(semantics): blocks do not have innate scoping --- src/semantic/mod.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/semantic/mod.rs b/src/semantic/mod.rs index e04cfa3..1bf51f5 100644 --- a/src/semantic/mod.rs +++ b/src/semantic/mod.rs @@ -307,11 +307,9 @@ pub fn analyze(tree: Box, state: &mut AnalysisState) -> Result<(), String> analyze(rhs, state) } Tree::Block(statements, _) => { - let old_namespace = state.namespace.clone(); for statement in statements { analyze(Box::new(statement.clone()), state)?; } - state.namespace = old_namespace; Ok(()) } Tree::LValueIdentifier(name) => analyze(name, state), From 1de6bc25153903fd491ddbbf2d7b9f50096d8e40 Mon Sep 17 00:00:00 2001 From: Felix Schwickerath Date: Mon, 16 Jun 2025 19:58:56 +0200 Subject: [PATCH 43/52] fix(ir): fix sealing of blocks for loops --- src/backend/codegen.rs | 8 ++++++++ src/ir/constructor.rs | 5 ++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/backend/codegen.rs b/src/backend/codegen.rs index 99cecff..28e49c7 100644 --- a/src/backend/codegen.rs +++ b/src/backend/codegen.rs @@ -397,8 +397,14 @@ impl CodeGenerator { registers: &Registers, ir_graph: &IRGraph, ) -> String { + trace!( + "Generating phi moves from {} to {}", + current_block_index, + following_block_index + ); let mut code = String::new(); let following_block = ir_graph.get_block(following_block_index); + trace!("Phis to be fulfilled: {:?}", following_block.phis()); for phi_index in following_block.phis() { let phi = following_block.get_node(*phi_index); if let Node::Phi(data) = phi { @@ -436,6 +442,8 @@ impl CodeGenerator { } break; } + } else { + panic!("Block referenced non-phi as phi!") } } trace!( diff --git a/src/ir/constructor.rs b/src/ir/constructor.rs index df3d7ac..daba3b4 100644 --- a/src/ir/constructor.rs +++ b/src/ir/constructor.rs @@ -382,7 +382,6 @@ impl IRGraphConstructor { self.graph .get_block_mut(loop_back_block_index) .register_entry_point(self.current_block_index, loop_body_jump); - self.seal_block(self.current_block_index); self.active_loop_entries.pop(); self.active_loop_exits.pop(); @@ -397,7 +396,7 @@ impl IRGraphConstructor { self.graph .get_block_mut(loop_body_index) .register_entry_point(loop_back_block_index, true_projection); - + self.seal_block(loop_body_index); self.graph .get_block_mut(following_block_index) .register_entry_point(loop_back_block_index, false_projection); @@ -473,7 +472,6 @@ impl IRGraphConstructor { self.active_loop_entries.pop(); self.active_loop_exits.pop(); let loop_body_exit = self.create_jump(); - self.seal_block(self.current_block_index); self.graph .get_block_mut(loop_post_index) @@ -495,6 +493,7 @@ impl IRGraphConstructor { .get_block_mut(loop_body_index) .register_entry_point(loop_post_index, true_projection); + self.seal_block(loop_body_index); self.graph .get_block_mut(following_block_index) .register_entry_point(loop_post_index, false_projection); From a5219679b1dd6172cff1784f74d119eda616ebde Mon Sep 17 00:00:00 2001 From: Felix Schwickerath Date: Mon, 16 Jun 2025 20:10:58 +0200 Subject: [PATCH 44/52] fix(semantic): additional return/type checking fixes --- src/backend/codegen.rs | 19 ++++++++++++++----- src/semantic/mod.rs | 27 +++++++++++++++++++++++---- 2 files changed, 37 insertions(+), 9 deletions(-) diff --git a/src/backend/codegen.rs b/src/backend/codegen.rs index 28e49c7..0e03c66 100644 --- a/src/backend/codegen.rs +++ b/src/backend/codegen.rs @@ -810,11 +810,20 @@ impl CodeGenerator { left_value.as_32_bit_assembly() )); let destination = registers.get(&(block_index, node_index)).unwrap(); - code.push_str(&format!( - "mov {}, {}\n", - left_value.as_32_bit_assembly(), - destination.as_32_bit_assembly() - )); + if !left_value.hardware_register() && !destination.hardware_register() { + code.push_str(&move_stack_variable(left_value)); + code.push_str(&format!( + "mov {}, {}\n", + HardwareRegister::Rbx.as_32_bit_assembly(), + destination.as_32_bit_assembly(), + )); + } else { + code.push_str(&format!( + "mov {}, {}\n", + left_value.as_32_bit_assembly(), + destination.as_32_bit_assembly() + )); + } code } diff --git a/src/semantic/mod.rs b/src/semantic/mod.rs index 1bf51f5..ba473f7 100644 --- a/src/semantic/mod.rs +++ b/src/semantic/mod.rs @@ -8,7 +8,7 @@ use crate::{ util::int_parsing::parse_int, }; -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct AnalysisState { return_state: ReturnState, namespace: HashMap, @@ -39,13 +39,13 @@ impl AnalysisState { } } -#[derive(PartialEq, Clone)] +#[derive(PartialEq, Clone, Debug)] enum ReturnState { Returning, NotReturing, } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct VariableStatus { type_status: Type, declaration_status: DeclarationStatus, @@ -72,7 +72,7 @@ impl VariableStatus { } } -#[derive(PartialEq, PartialOrd, Clone)] +#[derive(PartialEq, PartialOrd, Clone, Debug)] pub enum DeclarationStatus { Declared, Initialized, @@ -208,6 +208,7 @@ pub fn analyze(tree: Box, state: &mut AnalysisState) -> Result<(), String> .unwrap() .declaration() .eq(&DeclarationStatus::Declared) + && state.return_state.ne(&ReturnState::Returning) { return Err(format!( "Uninitialized variable {} used in expression!", @@ -378,6 +379,7 @@ pub fn analyze(tree: Box, state: &mut AnalysisState) -> Result<(), String> if let Some(other_expression) = else_expression { state.return_state = ReturnState::NotReturing; analyze(other_expression, state)?; + let else_return_state = state.return_state.clone(); if state.return_state.eq(&ReturnState::Returning) && if_return_state.eq(&ReturnState::Returning) { @@ -387,6 +389,7 @@ pub fn analyze(tree: Box, state: &mut AnalysisState) -> Result<(), String> } let false_namespace = state.namespace.clone(); state.namespace = old_namespace; + trace!("True: {:?}, False:{:?}", true_namespace, false_namespace); for (initialized_variable, _) in true_namespace .iter() .filter(|(_, v)| v.declaration().eq(&DeclarationStatus::Initialized)) @@ -404,6 +407,22 @@ pub fn analyze(tree: Box, state: &mut AnalysisState) -> Result<(), String> .unwrap() .set_initialized(); } + if else_return_state.eq(&ReturnState::Returning) { + state + .namespace + .get_mut(initialized_variable) + .unwrap() + .set_initialized(); + } + } + if if_return_state.eq(&ReturnState::Returning) { + for (initialized_variable, _) in false_namespace { + state + .namespace + .get_mut(&initialized_variable) + .unwrap() + .set_initialized(); + } } } else { state.return_state = returning_state; From bc28279e1ab38aaddef5786c544bb23e3265eb8d Mon Sep 17 00:00:00 2001 From: Felix Schwickerath Date: Mon, 16 Jun 2025 20:34:34 +0200 Subject: [PATCH 45/52] fix(semantic): disallow decleration of variables in for updater --- src/semantic/mod.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/semantic/mod.rs b/src/semantic/mod.rs index ba473f7..661b0c6 100644 --- a/src/semantic/mod.rs +++ b/src/semantic/mod.rs @@ -308,9 +308,11 @@ pub fn analyze(tree: Box, state: &mut AnalysisState) -> Result<(), String> analyze(rhs, state) } Tree::Block(statements, _) => { + let old_namespace = state.namespace.clone(); for statement in statements { analyze(Box::new(statement.clone()), state)?; } + state.namespace = old_namespace; Ok(()) } Tree::LValueIdentifier(name) => analyze(name, state), @@ -395,6 +397,7 @@ pub fn analyze(tree: Box, state: &mut AnalysisState) -> Result<(), String> .filter(|(_, v)| v.declaration().eq(&DeclarationStatus::Initialized)) { if false_namespace.contains_key(initialized_variable) + && state.namespace.contains_key(initialized_variable) && false_namespace .get(initialized_variable) .unwrap() @@ -445,11 +448,11 @@ pub fn analyze(tree: Box, state: &mut AnalysisState) -> Result<(), String> Ok(()) } Tree::For(initializer, condition, updater, expression, _) => { + let old_namespace = state.namespace.clone(); let returning_state = state.return_state.clone(); if let Some(initializer_expression) = initializer { analyze(initializer_expression, state)?; } - let old_namespace = state.namespace.clone(); analyze(condition.clone(), state)?; if get_variable_type(condition, state) .ok_or("Variable undefined!")? @@ -458,7 +461,12 @@ pub fn analyze(tree: Box, state: &mut AnalysisState) -> Result<(), String> return Err("Condition must be a boolean".to_string()); } if let Some(updater_expression) = updater { + let names = state.namespace.iter().map(|v| v.0).len(); analyze(updater_expression, state)?; + let additional_names = state.namespace.iter().map(|v| v.0).len(); + if names != additional_names { + return Err("Updater Expression cannot define variables".to_string()); + } } state.enter_loop(); analyze(expression, state)?; From b1db801c6988e9296261014db0487de58d1b25d9 Mon Sep 17 00:00:00 2001 From: Felix Schwickerath Date: Mon, 16 Jun 2025 20:52:56 +0200 Subject: [PATCH 46/52] fix(semantic): additional fixes for initalizers in for loops --- src/semantic/mod.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/semantic/mod.rs b/src/semantic/mod.rs index 661b0c6..aeb8b1b 100644 --- a/src/semantic/mod.rs +++ b/src/semantic/mod.rs @@ -81,6 +81,7 @@ pub enum DeclarationStatus { #[must_use] pub fn analyze(tree: Box, state: &mut AnalysisState) -> Result<(), String> { trace!("Analysing: {:?}", tree); + trace!("State: {:?}", state); match *tree { Tree::Literal(value, base, _) => { if base != 16 && base != 10 { @@ -448,10 +449,23 @@ pub fn analyze(tree: Box, state: &mut AnalysisState) -> Result<(), String> Ok(()) } Tree::For(initializer, condition, updater, expression, _) => { - let old_namespace = state.namespace.clone(); + let mut old_namespace = state.namespace.clone(); let returning_state = state.return_state.clone(); if let Some(initializer_expression) = initializer { analyze(initializer_expression, state)?; + for initialized_variable in state + .namespace + .iter() + .filter(|v| old_namespace.contains_key(v.0)) + .filter(|v| v.1.declaration().eq(&DeclarationStatus::Initialized)) + .map(|v| v.0) + .collect::>() + { + old_namespace + .get_mut(initialized_variable) + .unwrap() + .set_initialized(); + } } analyze(condition.clone(), state)?; if get_variable_type(condition, state) From 75ed62131229cfb52487c92e45386655677075e8 Mon Sep 17 00:00:00 2001 From: Felix Schwickerath Date: Mon, 16 Jun 2025 20:59:51 +0200 Subject: [PATCH 47/52] refactor: formatting and checks --- src/backend/codegen.rs | 1 + src/ir/constructor.rs | 24 ++++++++++++------------ 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/backend/codegen.rs b/src/backend/codegen.rs index 0e03c66..3a637bf 100644 --- a/src/backend/codegen.rs +++ b/src/backend/codegen.rs @@ -384,6 +384,7 @@ impl CodeGenerator { code.push_str(&format!("jmp {}\n", jump_label)); } Node::Projection(_) => return code, + #[allow(unreachable_patterns)] node => panic!("unimplemented node {:?}", node), } debug!("Generated code for IR: {}", code); diff --git a/src/ir/constructor.rs b/src/ir/constructor.rs index daba3b4..8123225 100644 --- a/src/ir/constructor.rs +++ b/src/ir/constructor.rs @@ -31,7 +31,7 @@ pub struct IRGraphConstructor { current_definitions: HashMap>, incomplete_phis: HashMap>, current_side_effect: HashMap, - incomplete_side_effect_phis: HashMap, + _incomplete_side_effect_phis: HashMap, sealed_blocks: Vec, current_block_index: BlockIndex, active_loop_entries: Vec, @@ -45,7 +45,7 @@ impl IRGraphConstructor { current_definitions: HashMap::new(), incomplete_phis: HashMap::new(), current_side_effect: HashMap::new(), - incomplete_side_effect_phis: HashMap::new(), + _incomplete_side_effect_phis: HashMap::new(), // Start Block never gets more predecessors sealed_blocks: vec![START_BLOCK], current_block_index: START_BLOCK, @@ -746,7 +746,7 @@ impl IRGraphConstructor { return_node_index } - fn create_phi(&mut self) -> usize { + fn _create_phi(&mut self) -> usize { let current_block = self.graph.get_block_mut(self.current_block_index); current_block.register_node(Node::Phi(PhiData::empty())) } @@ -756,7 +756,7 @@ impl IRGraphConstructor { current_block.register_node(Node::Phi(PhiData::new(operands))) } - fn create_phi_operands(&mut self, block_index: BlockIndex) -> NodeIndex { + fn _create_phi_operands(&mut self, block_index: BlockIndex) -> NodeIndex { let block = self.graph.get_block(block_index); let operands = block .entry_points() @@ -937,7 +937,7 @@ impl IRGraphConstructor { let operands = { let mut operands = Vec::new(); let phi_block = self.graph.get_block_mut(*block_index); - for (prev_block, prev_node) in phi_block.entry_points().clone() { + for (prev_block, _prev_nodes) in phi_block.entry_points().clone() { operands .push((prev_block, self.read_variable(variable.clone(), prev_block))); } @@ -967,18 +967,18 @@ impl IRGraphConstructor { //self.read_side_effect(self.current_block_index) } - fn read_side_effect(&mut self, block: usize) -> usize { + fn _read_side_effect(&mut self, block: usize) -> usize { if self.current_side_effect.contains_key(&block) { *self.current_side_effect.get(&block).unwrap() } else { - self.read_side_effect_recusive(block) + self._read_side_effect_recusive(block) } } - fn read_side_effect_recusive(&mut self, block: usize) -> usize { + fn _read_side_effect_recusive(&mut self, block: usize) -> usize { let node = if !self.sealed_blocks.contains(&block) { - let phi = self.create_phi(); - let old_phi = self.incomplete_side_effect_phis.insert(block, phi); + let phi = self._create_phi(); + let old_phi = self._incomplete_side_effect_phis.insert(block, phi); if old_phi.is_some() { panic!("Double read side effect recursive!"); } @@ -991,9 +991,9 @@ impl IRGraphConstructor { .iter() .last() .unwrap(); - self.read_side_effect(*previous_block) + self._read_side_effect(*previous_block) } else { - let phi = self.create_phi_operands(block); + let phi = self._create_phi_operands(block); self.write_side_effect(block, phi); phi }; From dae55109e49107cc90111bacc69c773fb3b0d75f Mon Sep 17 00:00:00 2001 From: Felix Schwickerath Date: Mon, 16 Jun 2025 21:12:48 +0200 Subject: [PATCH 48/52] fix(ir): fix if block generation --- src/ir/constructor.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/ir/constructor.rs b/src/ir/constructor.rs index 8123225..038fb6a 100644 --- a/src/ir/constructor.rs +++ b/src/ir/constructor.rs @@ -418,25 +418,25 @@ impl IRGraphConstructor { let mut true_block = Block::new("if-true".to_string()); true_block.register_entry_point(self.current_block_index, true_projection); self.seal_block(self.current_block_index); + let mut following_block = Block::new("if-following".to_string()); let false_block_index = self.graph.register_block(false_block); + self.seal_block(false_block_index); self.current_block_index = false_block_index; if let Some(else_statements) = else_body { self.convert_boxed(else_statements); } let false_jump = self.create_jump(); + following_block.register_entry_point(self.current_block_index, false_jump); let true_block_index = self.graph.register_block(true_block); + self.seal_block(true_block_index); self.current_block_index = true_block_index; self.convert_boxed(body); let true_jump = self.create_jump(); + following_block.register_entry_point(self.current_block_index, true_jump); - let mut following_block = Block::new("if-following".to_string()); - following_block.register_entry_point(false_block_index, false_jump); - following_block.register_entry_point(true_block_index, true_jump); self.current_block_index = self.graph.register_block(following_block); - self.seal_block(false_block_index); - self.seal_block(true_block_index); self.seal_block(self.current_block_index); None } From 92efca8b7b5b915b3312423c7fe684dcd05f8fc4 Mon Sep 17 00:00:00 2001 From: Felix Schwickerath Date: Mon, 16 Jun 2025 21:23:26 +0200 Subject: [PATCH 49/52] fix(ir/semantic): fix for loop checking order and fix break/continues --- src/ir/constructor.rs | 6 +++--- src/semantic/mod.rs | 9 +++++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/ir/constructor.rs b/src/ir/constructor.rs index 038fb6a..0abeaa3 100644 --- a/src/ir/constructor.rs +++ b/src/ir/constructor.rs @@ -296,12 +296,12 @@ impl IRGraphConstructor { ); let jump = self.create_jump(); self.graph - .get_block_mut(*self.active_loop_exits.first().unwrap()) + .get_block_mut(*self.active_loop_exits.last().unwrap()) .register_entry_point(self.current_block_index, jump); debug!( "Modifying entry_points of {:?}", self.graph - .get_block_mut(*self.active_loop_exits.first().unwrap()) + .get_block_mut(*self.active_loop_exits.last().unwrap()) ); None } @@ -312,7 +312,7 @@ impl IRGraphConstructor { ); let jump = self.create_jump(); self.graph - .get_block_mut(*self.active_loop_entries.first().unwrap()) + .get_block_mut(*self.active_loop_entries.last().unwrap()) .register_entry_point(self.current_block_index, jump); None } diff --git a/src/semantic/mod.rs b/src/semantic/mod.rs index aeb8b1b..0cff3e2 100644 --- a/src/semantic/mod.rs +++ b/src/semantic/mod.rs @@ -474,6 +474,11 @@ pub fn analyze(tree: Box, state: &mut AnalysisState) -> Result<(), String> { return Err("Condition must be a boolean".to_string()); } + state.enter_loop(); + analyze(expression, state)?; + state.return_state = returning_state; + state.exit_loop(); + if let Some(updater_expression) = updater { let names = state.namespace.iter().map(|v| v.0).len(); analyze(updater_expression, state)?; @@ -482,10 +487,6 @@ pub fn analyze(tree: Box, state: &mut AnalysisState) -> Result<(), String> return Err("Updater Expression cannot define variables".to_string()); } } - state.enter_loop(); - analyze(expression, state)?; - state.return_state = returning_state; - state.exit_loop(); state.namespace = old_namespace; Ok(()) } From 95837d20b9b073e298e9213ea90ca17aa2e49965 Mon Sep 17 00:00:00 2001 From: Felix Schwickerath Date: Mon, 16 Jun 2025 21:38:23 +0200 Subject: [PATCH 50/52] fix(parser): mark expect methods as must use and fix resulting bugs --- src/parser/mod.rs | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 80a33fa..74cda38 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -143,7 +143,9 @@ fn parse_decleration(tokens: &mut VecDeque) -> Result { .unwrap() .is_operator_type(&OperatorType::Assign) { - expect_operator(tokens, OperatorType::Assign); + expect_operator(tokens, OperatorType::Assign).ok_or(ParseError::Error( + "Expected assignment operator".to_string(), + ))?; expression = Some(parse_expression(tokens)?); } Ok(Tree::Declaration( @@ -182,13 +184,17 @@ fn parse_continue(tokens: &mut VecDeque) -> Result { fn parse_for(tokens: &mut VecDeque) -> Result { let keyword = expect_keyword(tokens, KeywordType::For) .ok_or(ParseError::Error("Expected keyword IF".to_string()))?; - expect_seperator(tokens, SeperatorType::ParenOpen); + expect_seperator(tokens, SeperatorType::ParenOpen) + .ok_or(ParseError::Error("Expected paren open".to_string()))?; let initializer = parse_simple(tokens).ok(); - expect_seperator(tokens, SeperatorType::Semicolon); + expect_seperator(tokens, SeperatorType::Semicolon) + .ok_or(ParseError::Error("Expected Semicolon".to_string()))?; let expression = parse_expression(tokens)?; - expect_seperator(tokens, SeperatorType::Semicolon); + expect_seperator(tokens, SeperatorType::Semicolon) + .ok_or(ParseError::Error("Expected Semicolon".to_string()))?; let updating_expression = parse_simple(tokens).ok(); - expect_seperator(tokens, SeperatorType::ParenClose); + expect_seperator(tokens, SeperatorType::ParenClose) + .ok_or(ParseError::Error("Expected paren close".to_string()))?; let statement = parse_statement(tokens)?; let span = keyword.span().merge(statement.span()); Ok(Tree::For( @@ -203,9 +209,11 @@ fn parse_for(tokens: &mut VecDeque) -> Result { fn parse_while(tokens: &mut VecDeque) -> Result { let keyword = expect_keyword(tokens, KeywordType::While) .ok_or(ParseError::Error("Expected keyword WHILE".to_string()))?; - expect_seperator(tokens, SeperatorType::ParenOpen); + expect_seperator(tokens, SeperatorType::ParenOpen) + .ok_or(ParseError::Error("Expected paren open".to_string()))?; let expression = parse_expression(tokens)?; - expect_seperator(tokens, SeperatorType::ParenClose); + expect_seperator(tokens, SeperatorType::ParenClose) + .ok_or(ParseError::Error("Expected paren close".to_string()))?; let statement = parse_statement(tokens)?; let span = keyword.span().merge(statement.span()); Ok(Tree::While(expression, Box::new(statement), span)) @@ -214,9 +222,11 @@ fn parse_while(tokens: &mut VecDeque) -> Result { fn parse_if(tokens: &mut VecDeque) -> Result { let keyword = expect_keyword(tokens, KeywordType::If) .ok_or(ParseError::Error("Expected keyword IF".to_string()))?; - expect_seperator(tokens, SeperatorType::ParenOpen); + expect_seperator(tokens, SeperatorType::ParenOpen) + .ok_or(ParseError::Error("Expected paren open".to_string()))?; let expression = parse_expression(tokens)?; - expect_seperator(tokens, SeperatorType::ParenClose); + expect_seperator(tokens, SeperatorType::ParenClose) + .ok_or(ParseError::Error("Expected paren close".to_string()))?; let statement = parse_statement(tokens)?; if tokens.front().unwrap().is_keyword(&KeywordType::Else) { consume(tokens); @@ -286,7 +296,8 @@ fn parse_expression(tokens: &mut VecDeque) -> Result, ParseErro } consume(tokens); let true_expression = parse_expression(tokens)?; - expect_operator(tokens, OperatorType::TernaryColon); + expect_operator(tokens, OperatorType::TernaryColon) + .ok_or(ParseError::Error("Expected ternary colon!".to_string()))?; let false_expression = parse_expression(tokens)?; Ok(Box::new(Tree::TernaryOperation( lhs, @@ -398,6 +409,7 @@ fn name(identifier: Token) -> Result, ParseError> { Err(ParseError::Error("Expected identifier as name".to_string())) } +#[must_use] fn expect_keyword(tokens: &mut VecDeque, keyword: KeywordType) -> Option { if let Some(token) = tokens.front() { match token { @@ -410,6 +422,7 @@ fn expect_keyword(tokens: &mut VecDeque, keyword: KeywordType) -> Option< None } +#[must_use] fn expect_seperator(tokens: &mut VecDeque, seperator: SeperatorType) -> Option { if let Some(token) = tokens.front() { match token { @@ -422,6 +435,7 @@ fn expect_seperator(tokens: &mut VecDeque, seperator: SeperatorType) -> O None } +#[must_use] fn expect_operator(tokens: &mut VecDeque, operator: OperatorType) -> Option { if let Some(token) = tokens.front() { match token { @@ -434,6 +448,7 @@ fn expect_operator(tokens: &mut VecDeque, operator: OperatorType) -> Opti None } +#[must_use] fn expect_identifier(tokens: &mut VecDeque) -> Option { if let Some(token) = tokens.front() { match token { From 17b05a3aef019c77da845a5927725e1ab5af2b4a Mon Sep 17 00:00:00 2001 From: Felix Schwickerath Date: Mon, 16 Jun 2025 21:47:50 +0200 Subject: [PATCH 51/52] fix(semantic): more block scoping fixes --- src/semantic/mod.rs | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/semantic/mod.rs b/src/semantic/mod.rs index 0cff3e2..a027f8c 100644 --- a/src/semantic/mod.rs +++ b/src/semantic/mod.rs @@ -118,19 +118,12 @@ pub fn analyze(tree: Box, state: &mut AnalysisState) -> Result<(), String> if let Tree::Name(name, _) = *identifier { if let Token::Operator(_, operator_type) = operator { if let OperatorType::Assign = operator_type { + trace!("Variable {:?} is now defined!", name); state .namespace .get(&name) .ok_or(format!("Undeclared variable {}!", name.as_string()))?; - if state - .namespace - .get(&name) - .unwrap() - .declaration() - .ne(&DeclarationStatus::Initialized) - { - state.namespace.get_mut(&name).unwrap().set_initialized(); - } + state.namespace.get_mut(&name).unwrap().set_initialized(); let expression_type = get_variable_type(expression.clone(), state) .ok_or("Variable undefined!")?; if state @@ -309,10 +302,23 @@ pub fn analyze(tree: Box, state: &mut AnalysisState) -> Result<(), String> analyze(rhs, state) } Tree::Block(statements, _) => { - let old_namespace = state.namespace.clone(); + let mut old_namespace = state.namespace.clone(); for statement in statements { analyze(Box::new(statement.clone()), state)?; } + for initialized_variable in state + .namespace + .iter() + .filter(|v| old_namespace.contains_key(v.0)) + .filter(|v| v.1.declaration().eq(&DeclarationStatus::Initialized)) + .map(|v| v.0) + .collect::>() + { + old_namespace + .get_mut(initialized_variable) + .unwrap() + .set_initialized(); + } state.namespace = old_namespace; Ok(()) } From 23d94cd2d00c3ce9ce8f003f8bed57dff868a63c Mon Sep 17 00:00:00 2001 From: Felix Schwickerath Date: Mon, 16 Jun 2025 21:57:10 +0200 Subject: [PATCH 52/52] =?UTF-8?q?fix(ir):=20some=20more=20block=20order=20?= =?UTF-8?q?fixes=20=E2=9A=B0=EF=B8=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/ir/constructor.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/ir/constructor.rs b/src/ir/constructor.rs index 0abeaa3..4b4b376 100644 --- a/src/ir/constructor.rs +++ b/src/ir/constructor.rs @@ -329,20 +329,22 @@ impl IRGraphConstructor { let mut true_block = Block::new("ternary-true".to_string()); true_block.register_entry_point(self.current_block_index, true_projection); self.seal_block(self.current_block_index); + let mut following_block = Block::new("ternary-following".to_string()); let false_block_index = self.graph.register_block(false_block); self.current_block_index = false_block_index; let false_expression = self.convert_boxed(false_value)?; let false_jump = self.create_jump(); + following_block.register_entry_point(self.current_block_index, false_jump); + self.seal_block(false_block_index); let true_block_index = self.graph.register_block(true_block); self.current_block_index = true_block_index; let true_expression = self.convert_boxed(true_value)?; let true_jump = self.create_jump(); + following_block.register_entry_point(self.current_block_index, true_jump); + self.seal_block(true_block_index); - let mut following_block = Block::new("ternary-following".to_string()); - following_block.register_entry_point(false_block_index, false_jump); - following_block.register_entry_point(true_block_index, true_jump); self.current_block_index = self.graph.register_block(following_block); self.seal_block(true_block_index); self.seal_block(false_block_index);