diff --git a/.gitignore b/.gitignore index e22fa32..2599f31 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ *.s *.o tests +test.l2 +output 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/run-tests.sh b/run-tests.sh new file mode 100755 index 0000000..ab465a0 --- /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 --only-failing 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..3a637bf 100644 --- a/src/backend/codegen.rs +++ b/src/backend/codegen.rs @@ -1,18 +1,22 @@ use std::collections::HashMap; +use tracing::{debug, trace}; + use crate::ir::{ - graph::{IRGraph, END_BLOCK}, + block::{Block, NodeIndex}, + graph::{BlockIndex, IRGraph, END_BLOCK}, node::{ - BinaryOperationData, ConstantIntData, Node, BINARY_OPERATION_LEFT, BINARY_OPERATION_RIGHT, - RETURN_RESULT_INDEX, + binary_operation::BinaryOperationData, projection::ProjectionInformation, ConstantIntData, + Node, ReturnData, }, }; use super::regalloc::{HardwareRegister, Register, RegisterAllocator}; -type Registers<'a> = HashMap<&'a Node, Box>; +pub type Registers = HashMap<(BlockIndex, NodeIndex), Box>; -const TEMPLATE: &str = ".global main +const TEMPLATE: &str = " .section .note.GNU-stack,\"\",@progbits +.global main .global _main .text main: @@ -27,17 +31,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)); } @@ -49,36 +57,125 @@ 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)); - code.push_str(&self.generate_for_node(END_BLOCK, ir_graph, ®isters, &mut visited)); + + // 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> = + 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_nodes) in block.entry_points() { + if !jump_information.contains_key(&previous_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(); + for previous_node in previous_nodes { + existing_exits.insert(*previous_node, block_index); + } + jump_information.insert(*previous_block_index, existing_exits); + } + } + } + code.push_str(&self.generate_recursive( + END_BLOCK, + ir_graph, + &mut jump_information, + &mut visited, + ®isters, + )); code } - pub fn generate_for_node( + pub fn generate_recursive( &self, - node_index: usize, + block_index: BlockIndex, ir_graph: &IRGraph, - registers: &Registers, + jump_information: &mut HashMap>, visited: &mut Vec, + registers: &Registers, ) -> String { let mut code = String::new(); - let node = ir_graph.get_node(node_index); - for predecessor in node.predecessors() { - if !visited.contains(predecessor) { - visited.push(*predecessor); - code.push_str(&self.generate_for_node(*predecessor, ir_graph, registers, visited)); + 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 + } + + pub fn generate_for_block( + &self, + block: &Block, + block_index: BlockIndex, + jump_information: &HashMap, + ir_graph: &IRGraph, + registers: &Registers, + ) -> String { + 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, + block_index, + jump_information, + ir_graph, + registers, + )); + } + code + } + + pub fn generate_for_node( + &self, + node: &Node, + node_index: NodeIndex, + block: &Block, + block_index: BlockIndex, + 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(), + block, + block_index, + data, ir_graph, registers, "add", @@ -87,7 +184,9 @@ impl CodeGenerator { Node::Subtraction(data) => { code.push_str(&self.generate_binary_operation( node_index, - data.binary_operation_data(), + block, + block_index, + data, ir_graph, registers, "sub", @@ -96,8 +195,10 @@ impl CodeGenerator { Node::Multiplication(data) => { code.push_str(&self.generate_binary_operation_rax( node_index, - data.binary_operation_data(), + block, + block_index, ir_graph, + data, registers, "imul", "mul", @@ -106,8 +207,10 @@ impl CodeGenerator { Node::Division(data) => { code.push_str(&self.generate_binary_operation_rax( node_index, - data.binary_operation_data(), + block, + block_index, ir_graph, + data, registers, "idiv", "div", @@ -116,49 +219,493 @@ impl CodeGenerator { Node::Modulo(data) => { code.push_str(&self.generate_binary_operation_rax( node_index, - data.binary_operation_data(), + block, + block_index, ir_graph, + data, registers, "idiv", "mod", )); } - Node::Return(_) => { - code.push_str(&self.generate_return(ir_graph, registers, node_index)); + Node::Return(data) => { + code.push_str(&self.generate_return(block, block_index, data, registers)); } 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, + block_index, + 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, + block, + block_index, + ir_graph, + data, + registers, + "sal", + )); + } + Node::ShiftRight(data) => { + code.push_str(&self.generate_shift( + node_index, + block, + block_index, + ir_graph, + data, + registers, + "sar", + )); + } + Node::Equals(_) + | Node::HigherEquals(_) + | Node::LowerEquals(_) + | Node::NotEquals(_) + | Node::Lower(_) + | 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())); + } + 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 => { + trace!( + "Generating assembly for jump: {} with destination XXX", + node, + ); + 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(_) => {} + Node::Projection(data) if data.projection_info().eq(&ProjectionInformation::IfTrue) => { + trace!( + "Generating IR for true projection (including jump) with jump information {:?}", + jump_information + ); + 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, + comparision, + block, + block_index, + *following_block_index, + *false_block_index, + ir_graph, + 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 following_block_index = jump_information.get(&node_index).unwrap(); + let jump_label = self + .jump_label + .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, + #[allow(unreachable_patterns)] + node => panic!("unimplemented node {:?}", node), } + debug!("Generated code for IR: {}", code); code } - pub fn generate_binary_operation( + pub fn generate_phi_moves( &self, - node_index: usize, - _operation_data: &BinaryOperationData, + current_block_index: BlockIndex, + following_block_index: BlockIndex, + 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 { + let destination_register = + registers.get(&(following_block_index, *phi_index)).unwrap(); + for operand in data.operands() { + if operand.0 != current_block_index { + continue; + } + let operand_block = ir_graph.get_block(operand.0); + let source_register = registers + .get(&( + operand.0, + predecessor_skip_projection(operand_block, operand.1), + )) + .expect( + format!("Expected register for {:?}", ir_graph.get_node(operand)) + .as_str(), + ); + 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; + } + } else { + panic!("Block referenced non-phi as phi!") + } + } + 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 { + code.push_str("cmp %rbx,%rbx\n"); + } else { + code.push_str("test %rsp,%rsp\n"); + } + 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_rec( + &self, + inverted: bool, + comparison_index: usize, + current_block: &Block, + current_block_index: BlockIndex, + jump_block_index: usize, + other_jump_block_index: usize, ir_graph: &IRGraph, registers: &Registers, + ) -> Option { + let mut code = String::new(); + 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( + !inverted, + data.input(), + current_block, + current_block_index, + jump_block_index, + other_jump_block_index, + ir_graph, + registers, + )?); + } + Node::And(data) => { + code.push_str(&self.generate_conditional_jump_rec( + inverted, + data.lhs(), + current_block, + current_block_index, + jump_block_index, + other_jump_block_index, + ir_graph, + registers, + )?); + code.push_str(&self.generate_conditional_jump_rec( + inverted, + data.rhs(), + current_block, + current_block_index, + jump_block_index, + other_jump_block_index, + ir_graph, + registers, + )?); + } + 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(_) + | Node::Equals(_) + | Node::NotEquals(_) + | Node::Higher(_) + | 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, + comparison_index, + current_block, + current_block_index, + jump_block_index, + op_code, + ir_graph, + registers, + )?); + } + _ => {} + } + trace!("Code generated for {:?}: {}", comparison_node, code); + Some(code) + } + + pub fn get_jump_opcode(&self, comparision: &Node) -> &str { + match comparision { + Node::Lower(_) => "jl", + Node::LowerEquals(_) => "jle", + Node::Equals(_) => "je", + Node::NotEquals(_) => "jne", + Node::HigherEquals(_) => "jge", + Node::Higher(_) => "jg", + Node::ConstantBool(_) => "je", + 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(_) => "jne", + 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_index)) + .unwrap(); + code.push_str(&format!("test $0x1, {}\n", register.as_32_bit_assembly())); + } + _ => {} + } + code.push_str(&format!("{} {}\n", op_code, jump_label)); + Some(code) + } + + pub fn generate_comparison( + &self, + block: &Block, + block_index: BlockIndex, + operation_data: &BinaryOperationData, + _ir_graph: &IRGraph, + registers: &Registers, ) -> String { let left_value = registers - .get(ir_graph.get_node(predecessor_skip_projection( - node_index, - BINARY_OPERATION_LEFT, - ir_graph, - ))) + .get(&( + block_index, + predecessor_skip_projection(block, operation_data.lhs()), + )) .unwrap(); let right_value = registers - .get(ir_graph.get_node(predecessor_skip_projection( - node_index, - BINARY_OPERATION_RIGHT, - ir_graph, - ))) + .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() { + 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_32_bit_assembly(), + HardwareRegister::Rbx.as_32_bit_assembly(), + )); + } else { + code.push_str(&format!( + "cmp {}, {}\n", + right_value.as_32_bit_assembly(), + left_value.as_32_bit_assembly() + )); + } + code + } - let destination_register = registers.get(ir_graph.get_node(node_index)).unwrap(); + pub fn generate_binary_operation( + &self, + 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_index, predecessor_skip_projection(block, data.lhs()))) + .unwrap(); + let right_value = registers + .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(); if !left_value.hardware_register() && !destination_register.hardware_register() { @@ -166,12 +713,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() { @@ -181,12 +728,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 } @@ -194,32 +741,26 @@ impl CodeGenerator { pub fn generate_binary_operation_rax( &self, node_index: usize, - _operation_data: &BinaryOperationData, - ir_graph: &IRGraph, + block: &Block, + block_index: BlockIndex, + _ir_graph: &IRGraph, + data: &BinaryOperationData, 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_index, predecessor_skip_projection(block, data.lhs()))) .unwrap(); let right_value = registers - .get(ir_graph.get_node(predecessor_skip_projection( - node_index, - BINARY_OPERATION_RIGHT, - ir_graph, - ))) + .get(&(block_index, predecessor_skip_projection(block, data.rhs()))) .unwrap(); - let destination_register = registers.get(ir_graph.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(&destination_register.as_assembly()); + code.push_str(&destination_register.as_32_bit_assembly()); code.push('\n'); code.push_str("mov "); @@ -235,34 +776,82 @@ 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 } + pub fn generate_shift( + &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_index, data.lhs())).unwrap(); + let right_value = registers.get(&(block_index, data.rhs())).unwrap(); + code.push_str(&format!( + "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_32_bit_assembly() + )); + let destination = registers.get(&(block_index, node_index)).unwrap(); + 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 + } + pub fn generate_return( &self, - ir_graph: &IRGraph, + block: &Block, + block_index: BlockIndex, + data: &ReturnData, 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 = predecessor_skip_projection(block, data.input()); + debug!( + "Determined node {} that contains the return result", + block.get_node(return_node) + ); + 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_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"); @@ -273,40 +862,56 @@ impl CodeGenerator { pub fn generate_constant_int( &self, constant_data: &ConstantIntData, - ir_graph: &IRGraph, + _block: &Block, + block_index: BlockIndex, 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_index, node_index)).unwrap(); let mut code = String::new(); 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 } } -fn predecessor_skip_projection(node: usize, predecessor_index: usize, graph: &IRGraph) -> usize { - let predecessor = graph - .get_predecessors(node) - .get(predecessor_index) - .expect("Invalid predecessor index"); - if let Node::Projection(data) = graph.get_node(*predecessor) { +fn predecessor_skip_projection(block: &Block, data: NodeIndex) -> NodeIndex { + let predecessor = block.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(®ister.as_assembly()); + 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 } + +fn calculate_jump_label(ir_graphs: &Vec) -> HashMap { + let mut jump_label = HashMap::new(); + for ir_graph in ir_graphs { + 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_block<'a>( + block_index: BlockIndex, + _block: &Block, + current: &mut HashMap, +) { + current.insert(block_index, format!("LC{}", block_index)); +} diff --git a/src/backend/regalloc.rs b/src/backend/regalloc.rs index a45d849..8766cd0 100644 --- a/src/backend/regalloc.rs +++ b/src/backend/regalloc.rs @@ -3,19 +3,26 @@ use std::{ fmt::{Debug, Display}, }; -use crate::ir::{ - graph::{IRGraph, END_BLOCK}, - node::Node, -}; +use tracing::debug; +use crate::{ + backend::codegen::Registers, + ir::{ + block::NodeIndex, + graph::{BlockIndex, IRGraph, END_BLOCK}, + node::Node, + }, +}; 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; } -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum HardwareRegister { Rax, Rbx, @@ -90,11 +97,20 @@ 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 { fn as_assembly(&self) -> String { - format!("%{}", self.as_string()) + self.as_32_bit_assembly() } fn hardware_register(&self) -> bool { true @@ -107,9 +123,17 @@ impl Register for HardwareRegister { fn as_16_bit_assembly(&self) -> String { 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()) + } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct StackRegister { offset: usize, } @@ -137,9 +161,17 @@ impl Register for StackRegister { self.as_assembly() } + fn as_8_bit_assembly(&self) -> String { + self.as_assembly() + } + fn hardware_register(&self) -> bool { false } + + fn box_clone(&self) -> Box { + Box::new(self.clone()) + } } impl Display for StackRegister { @@ -148,21 +180,24 @@ impl Display for StackRegister { } } -pub struct RegisterAllocator<'a> { +pub struct RegisterAllocator { current_stack_offset: usize, - registers: HashMap<&'a Node, Box>, + registers: Registers, + // 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: HashMap::new(), available_hardware_register: vec![ //HardwareRegister::Rax, //HardwareRegister::Rbx, - HardwareRegister::Rcx, + //HardwareRegister::Rcx, //HardwareRegister::Rdx, HardwareRegister::Rsi, HardwareRegister::Rdi, @@ -178,39 +213,52 @@ impl<'a> RegisterAllocator<'a> { } } - pub fn allocate_registers( - mut self, - graph: &'a IRGraph, - ) -> (HashMap<&'a Node, Box>, usize) { + pub fn allocate_registers(mut self, graph: &IRGraph) -> (Registers, 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: &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_index, node) in block.get_nodes().iter().enumerate() { + if needs_register(node) { + 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) -> Box { + pub fn get_available_register( + &mut self, + node: (BlockIndex, NodeIndex), + ir_graph: &IRGraph, + ) -> Box { + 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: {}", ir_graph.get_node(node)); if self.has_available_hardware_register() { let register = self.available_hardware_register.pop().unwrap(); - Box::new(register) + let register: Box = Box::new(register); + 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); + boxed_register } } @@ -223,14 +271,25 @@ 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(_) + Node::Projection(_) + | Node::Return(_) + | Node::LowerEquals(_) + | Node::Equals(_) + | Node::ConditionalJump(_) + | Node::Jump ) } -impl Default for RegisterAllocator<'_> { +impl Default for RegisterAllocator { fn default() -> Self { Self::new() } diff --git a/src/ir/block.rs b/src/ir/block.rs new file mode 100644 index 0000000..4be4595 --- /dev/null +++ b/src/ir/block.rs @@ -0,0 +1,90 @@ +use std::{collections::HashMap, fmt::Display}; + +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>, + phis: Vec, + nodes: Vec, + name: String, +} + +impl Block { + pub fn new(name: String) -> Block { + Block { + entry_points: HashMap::new(), + phis: Vec::new(), + nodes: Vec::new(), + name, + } + } + + pub fn register_entry_point(&mut self, block: BlockIndex, node_index: NodeIndex) { + 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> { + &self.entry_points + } + + 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; + } + + 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 phis(&self) -> &Vec { + &self.phis + } + + 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 + } + + pub fn empty(&self) -> bool { + self.nodes.is_empty() + } +} + +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 0864f13..4b4b376 100644 --- a/src/ir/constructor.rs +++ b/src/ir/constructor.rs @@ -1,60 +1,92 @@ use core::panic; use std::collections::HashMap; +use tracing::{debug, info, trace}; + use crate::{ + 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, ConstantIntData, DivisionData, ModuloData, MultiplicationData, Node, PhiData, - ProjectionData, ProjectionInformation, ReturnData, StartData, SubtractionData, + unary_operation::UnaryOperationData, ConstantBoolData, ConstantIntData, Node, PhiData, + ReturnData, }, - optimizer::Optimizer, }; pub struct IRGraphConstructor { - optimizer: Optimizer, graph: IRGraph, - current_definitions: HashMap>, - incomplete_phis: HashMap>, + 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: usize, + current_block_index: BlockIndex, + active_loop_entries: Vec, + active_loop_exits: Vec, } impl IRGraphConstructor { pub fn new() -> IRGraphConstructor { IRGraphConstructor { - optimizer: Optimizer::new(), graph: IRGraph::new(), 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![0], - current_block: 0, + sealed_blocks: vec![START_BLOCK], + current_block_index: START_BLOCK, + active_loop_entries: Vec::new(), + active_loop_exits: Vec::new(), } } - pub fn convert(&mut self, tree: Tree) -> Option { + 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); + self.seal_block(self.current_block_index); + + // The last statement after parsing the body can exit the function + 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 { @@ -64,34 +96,69 @@ 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::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, rhs); + self.write_variable(name, self.current_block_index, rhs); } _ => panic!("Assignment has no assignment operator"), }; @@ -117,7 +184,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) } @@ -135,7 +234,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 { @@ -144,7 +243,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!") @@ -157,160 +256,566 @@ 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_logical_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); - self.graph - .end_block_mut() - .predecessors_mut() - .push(return_node); + self.create_return(node); None } Tree::Type(_, _) => None, + Tree::BoolLiteral(boolean, _) => { + let node = if boolean { + self.create_constant_int(1) + } else { + self.create_constant_int(0) + }; + Some(node) + } + Tree::Break(_) => { + debug!( + "Generating IR for break statement with active loops: {:?}", + self.active_loop_exits + ); + let jump = self.create_jump(); + self.graph + .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.last().unwrap()) + ); + None + } + Tree::Continue(_) => { + debug!( + "Generating IR for continue statement with active loops: {:?}", + self.active_loop_entries + ); + let jump = self.create_jump(); + self.graph + .get_block_mut(*self.active_loop_entries.last().unwrap()) + .register_entry_point(self.current_block_index, jump); + None + } + 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 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 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); + + 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), + ]); + self.seal_block(self.current_block_index); + 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(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 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); + let following_block_index = self.graph.register_block(following_block); + self.seal_block(self.current_block_index); + + self.current_block_index = 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(self.current_block_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); + + 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_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); + self.seal_block(loop_body_index); + self.current_block_index = following_block_index; + self.seal_block(self.current_block_index); + None + } + 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 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); + + self.current_block_index = self.graph.register_block(following_block); + self.seal_block(self.current_block_index); + None + } + 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(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("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); + + 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(); + + self.graph + .get_block_mut(loop_post_index) + .register_entry_point(self.current_block_index, loop_body_exit); + self.current_block_index = loop_post_index; + self.seal_block(loop_post_index); + + if let Some(postincrement) = option_postincrement { + self.convert_boxed(postincrement); + } + + 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_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); + self.seal_block(loop_body_index); + self.current_block_index = following_block_index; + self.seal_block(self.current_block_index); + debug!("Following block after for: {}", self.current_block_index); + None + } + #[allow(unreachable_patterns)] + node => todo!("Unimplemented {:?}", node), } } - pub fn convert_boxed(&mut self, tree: Box) -> Option { + pub fn convert_boxed(&mut self, tree: Box) -> Option { self.convert(*tree) } - fn create_start_block(&mut self) -> usize { - self.graph - .register_node(Node::Start(StartData::new(self.current_block))) + 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))) } - 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, - )))) + // 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_index); + self.create_jump() } - 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_jump(&mut self) -> NodeIndex { + let current_block = self.graph.get_block_mut(self.current_block_index); + current_block.register_node(Node::Jump) } - 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_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_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_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_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::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), + } + } else if let Tree::IdentifierExpression(_) = *condition { + let variable = self.convert_boxed(condition).unwrap(); + self.create_logical_not(variable) + } else if let Tree::BoolLiteral(value, _) = *condition { + if value { + self.create_constant_int(0) + } else { + self.create_constant_int(1) + } + } else { + panic!("Condition is not a binary operation, got {:?}", condition); + } + } + + 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: 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: 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_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))), - ) + 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_side_effect_projection(&mut self, node: usize) -> usize { - self.graph - .register_node(Node::Projection(ProjectionData::new( - self.current_block, - node, - ProjectionInformation::SideEffect, - ))) + 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_result_projection(&mut self, node: usize) -> usize { - self.graph - .register_node(Node::Projection(ProjectionData::new( - self.current_block, - node, - ProjectionInformation::Result, - ))) + 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_phi(&mut self) -> usize { - self.graph - .register_node(Node::Phi(PhiData::new(self.current_block))) + 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_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)); - } + 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_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_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_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_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_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_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_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_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))) + } + + 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))) + } + + 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::Phi(PhiData::new_with_operands( - self.current_block, - operands, - ))) + .end_block_mut() + .register_entry_point(self.current_block_index, return_node_index); + return_node_index } - fn create_phi_variable_operands(&mut self, block: usize, variable: Name) -> 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())) + } + + 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_operands(&mut self, block_index: BlockIndex) -> NodeIndex { + let block = self.graph.get_block(block_index); + let operands = block + .entry_points() + .iter() + .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(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 { + // Creating the operands for a block, by iterating over its entry points and reading the + // variable there 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)); + for (block_index, _) in self.graph.get_block(block_index).entry_points().clone() { + operands.push(( + block_index, + self.read_variable(variable.clone(), block_index), + )); } - self.graph - .register_node(Node::Phi(PhiData::new_with_operands( - self.current_block, - operands, - ))) + trace!( + "Created phi operands for block {} whilst reading {:?}: {:?}", + block_index, + variable, + 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, node: usize) -> usize { - let projection_side_effect = self.create_side_effect_projection(node); + 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 @@ -325,57 +830,126 @@ impl IRGraphConstructor { } } - fn read_variable(&mut self, variable: Name, block: usize) -> usize { + fn read_variable(&mut self, variable: Name, block: BlockIndex) -> NodeIndex { + trace!( + "Trying to read from variable {:?} in block {}", + variable, + block + ); if self.current_definitions.contains_key(&variable) { - *self + if self .current_definitions .get(&variable) .unwrap() - .get(&block) - .unwrap() + .contains_key(&block) + { + trace!("Variable defined in the same block! Returning value"); + *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) { - let phi = self.create_phi(); - if let std::collections::hash_map::Entry::Vacant(e) = self.incomplete_phis.entry(block) - { - e.insert(HashMap::from([(variable.clone(), phi)])); + fn read_variable_recursive(&mut self, variable: Name, block_index: 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) { + // Current block is not sealed yet, the list of operands is not final yet + 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(); + entry.insert(variable.clone(), phi); + self.incomplete_phis.insert(block_index, entry); } else { self.incomplete_phis - .get_mut(&block) - .unwrap() - .insert(variable.clone(), phi); - } + .insert(block_index, HashMap::from([(variable.clone(), phi)])); + }; + phi + } else if self.graph.get_block(block_index).entry_points().len() == 1 { + // 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() + .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 if self.graph.get_predecessors(block).len() == 1 { - self.read_variable( - variable.clone(), - *self.graph.get_predecessors(block).first().unwrap(), - ) } else { - let phi = self.create_phi_variable_operands(block, variable.clone()); - self.write_variable(variable.clone(), block, phi); + // 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 + .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 }; - self.write_variable(variable.clone(), block, node); + + // Denote that the newly created phi defines the variable in the current block + self.write_variable(variable.clone(), block_index, node); node } - fn _seal_block(&mut self, block: usize) { + fn seal_block(&mut self, block: BlockIndex) { + 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 (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)); + 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 phi_block = self.graph.get_block_mut(*block_index); + for (prev_block, _prev_nodes) in phi_block.entry_points().clone() { + operands + .push((prev_block, 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); + } } } } @@ -383,7 +957,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) { @@ -391,29 +965,37 @@ impl IRGraphConstructor { } fn read_current_side_effect(&mut self) -> usize { - self.read_side_effect(self.current_block) + return 0; + //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!"); } 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); + let phi = self._create_phi_operands(block); self.write_side_effect(block, phi); phi }; @@ -424,6 +1006,22 @@ impl IRGraphConstructor { pub fn graph(self) -> IRGraph { self.graph } + + 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, 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, + ))) + } } impl Default for IRGraphConstructor { diff --git a/src/ir/graph.rs b/src/ir/graph.rs index 911e4c2..8f1a908 100644 --- a/src/ir/graph.rs +++ b/src/ir/graph.rs @@ -1,66 +1,73 @@ -use std::{collections::HashMap, fmt::Display}; +use std::fmt::Display; -use super::node::{BlockData, Node}; +use super::{ + block::{Block, NodeIndex}, + node::Node, +}; + +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 start_block_mut(&mut self) -> &mut Block { + self.blocks + .get_mut(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 register_block(&mut self, block: Block) -> BlockIndex { + self.blocks.push(block); + self.blocks.len() - 1 } - pub fn get_node(&self, index: usize) -> &Node { - self.nodes.get(&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_node_mut(&mut self, index: usize) -> &Node { - self.nodes - .get_mut(&index) - .expect("Cannot find node at 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_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,11 +79,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, "{}: {}", i, self.get_node(i))? + for (index, block) in self.blocks.iter().enumerate() { + writeln!(f, "{} -- {}", index, 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 33a26ae..0000000 --- a/src/ir/node.rs +++ /dev/null @@ -1,396 +0,0 @@ -use std::fmt::Display; - -#[derive(Eq, Hash, PartialEq, Debug)] -pub enum Node { - Add(AddData), - Block(BlockData), - ConstantInt(ConstantIntData), - Division(DivisionData), - Modulo(ModuloData), - Multiplication(MultiplicationData), - Phi(PhiData), - Projection(ProjectionData), - Return(ReturnData), - Start(StartData), - Subtraction(SubtractionData), -} - -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::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(), - } - } - - 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::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(), - } - } - - 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::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(), - } - } -} - -#[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![]), - } - } -} - -#[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, -} - -#[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 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 - } -} - -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(_) => write!(f, "!!PHI!!"), - 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::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() - ) - } - } - } -} diff --git a/src/ir/node/binary_operation.rs b/src/ir/node/binary_operation.rs new file mode 100644 index 0000000..3b0c486 --- /dev/null +++ b/src/ir/node/binary_operation.rs @@ -0,0 +1,38 @@ +use crate::ir::block::NodeIndex; + +#[derive(Eq, Hash, PartialEq, Clone, Debug)] +pub struct BinaryOperationData { + left_node: NodeIndex, + right_node: NodeIndex, + sideeffect: Option, +} + +impl BinaryOperationData { + pub fn new(left_node: NodeIndex, right_node: NodeIndex) -> BinaryOperationData { + BinaryOperationData { + left_node, + right_node, + sideeffect: None, + } + } + + pub fn new_with_sideeffect( + left_node: NodeIndex, + right_node: NodeIndex, + 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..614f2ae --- /dev/null +++ b/src/ir/node/mod.rs @@ -0,0 +1,147 @@ +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, Clone, 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), + LogicalNot(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::LogicalNot(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, Clone, 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, Clone, Debug)] +pub struct ConstantBoolData { + value: bool, +} + +impl ConstantBoolData { + pub fn new(value: bool) -> ConstantBoolData { + ConstantBoolData { value } + } + + pub fn value(&self) -> bool { + self.value + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +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)) { + match self.operands.binary_search(&operand) { + Ok(_) => {} + Err(pos) => self.operands.insert(pos, operand), + } + } + + pub fn operands(&self) -> Vec<(BlockIndex, NodeIndex)> { + self.operands.iter().copied().collect() + } +} + +#[derive(Eq, Hash, PartialEq, Clone, Debug)] +pub struct ReturnData { + input: NodeIndex, +} + +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 { + write!(f, "{:?}", self) + } +} diff --git a/src/ir/node/projection.rs b/src/ir/node/projection.rs new file mode 100644 index 0000000..b8ac773 --- /dev/null +++ b/src/ir/node/projection.rs @@ -0,0 +1,32 @@ +use crate::ir::block::NodeIndex; + +#[derive(Eq, Hash, PartialEq, Clone, Debug)] +pub enum ProjectionInformation { + SideEffect, + Result, + IfTrue, + IfFalse, +} + +#[derive(Eq, Hash, PartialEq, Clone, Debug)] +pub struct ProjectionData { + input: NodeIndex, + 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..1e8d9dd --- /dev/null +++ b/src/ir/node/unary_operation.rs @@ -0,0 +1,27 @@ +use crate::ir::block::NodeIndex; + +#[derive(Eq, Hash, PartialEq, Clone, Debug)] +pub struct UnaryOperationData { + input: NodeIndex, + sideffect: Option, +} + +impl UnaryOperationData { + pub fn new(input: NodeIndex) -> UnaryOperationData { + UnaryOperationData { + input, + sideffect: None, + } + } + + pub fn new_with_sideeffect(input: NodeIndex, 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/lexer/mod.rs b/src/lexer/mod.rs index 7b7cd60..4f35c13 100644 --- a/src/lexer/mod.rs +++ b/src/lexer/mod.rs @@ -1,6 +1,7 @@ use std::ops::{Range, RangeFrom}; use token::{KeywordType, OperatorType, SeperatorType, Token}; +use tracing::trace; use crate::{ parser::error::ParseError, @@ -27,6 +28,10 @@ impl Lexer { } pub fn next_token(&mut self) -> Result { + trace!( + "Reading next token: {}", + self.get_substring_from(self.position..) + ); let whitespace = self.skip_whitespace(); if whitespace.is_some() { return whitespace @@ -46,22 +51,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 +343,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)?.eq(&'=') { + return Some(Token::Operator(self.build_span(2), comparison_equal_type)); + } else { + return Some(Token::Operator(self.build_span(1), comparison_type)); + } + } + 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..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 { @@ -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,39 @@ 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 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, + _ => false, + } + } + + pub fn is_operator_type(&self, other_operator: &OperatorType) -> bool { match self { Self::Operator(_, operator) => operator.eq(other_operator), _ => false, @@ -198,6 +317,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..fdb0a25 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; @@ -20,9 +21,10 @@ 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"); + error!("Error: Invalid arguments"); exit(3); } let input = Path::new(args.get(1).unwrap()); @@ -35,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 { @@ -54,19 +57,30 @@ 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); + info!("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"); + let assembler = code_generator.generate(); + info!("Assembler contents: {}", &assembler); + fs::write(temp.clone(), assembler).expect("Filesystem: Failed to write assembler output"); + info!( + "Filesystem: Wrote assembler to {}", + temp.clone().to_string() + ); let gcc = Command::new("gcc") .arg(temp) .arg("-o") .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 { @@ -80,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) } } @@ -92,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/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..74cda38 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -3,9 +3,10 @@ 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}; +use crate::lexer::token::{KeywordType, OperatorType, SeperatorType, Token, MAX_PRECEDENCE}; pub mod ast; pub mod error; @@ -58,12 +59,13 @@ 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> { - let body_open = expect_seperator(tokens, SeperatorType::BraceOpen).unwrap(); +fn parse_block(tokens: &mut VecDeque) -> Result { + 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() { @@ -75,45 +77,91 @@ 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::BraceOpen) + { + 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) { + 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) { + 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) { + 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) { + parse_while(tokens) + } else if keyword.is_keyword(&KeywordType::If) { + parse_if(tokens) + } else { + Err(ParseError::Error("Expected control keyword".to_string())) + }; + trace!("Finished parsing control structure: {:?}", tokens); + + 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 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; - if tokens.front().unwrap().is_operator(&OperatorType::Assign) { - expect_operator(tokens, OperatorType::Assign); + if tokens + .front() + .unwrap() + .is_operator_type(&OperatorType::Assign) + { + expect_operator(tokens, OperatorType::Assign).ok_or(ParseError::Error( + "Expected assignment operator".to_string(), + ))?; 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, )) } 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, @@ -121,11 +169,90 @@ 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 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 CONTINUE".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) + .ok_or(ParseError::Error("Expected paren open".to_string()))?; + let initializer = parse_simple(tokens).ok(); + expect_seperator(tokens, SeperatorType::Semicolon) + .ok_or(ParseError::Error("Expected Semicolon".to_string()))?; + let expression = parse_expression(tokens)?; + 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) + .ok_or(ParseError::Error("Expected paren close".to_string()))?; + 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) + .ok_or(ParseError::Error("Expected paren open".to_string()))?; + let expression = parse_expression(tokens)?; + 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)) +} + +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) + .ok_or(ParseError::Error("Expected paren open".to_string()))?; let expression = parse_expression(tokens)?; - Ok(Tree::Assignment(lvalue, assignment_operator, expression)) + 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); + 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 +277,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)?; + 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) + .ok_or(ParseError::Error("Expected ternary colon!".to_string()))?; + 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 +367,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 +377,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 +389,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 @@ -235,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 { @@ -247,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 { @@ -259,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 { @@ -271,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 { @@ -283,6 +461,16 @@ fn expect_identifier(tokens: &mut VecDeque) -> Option { None } -fn _consume(tokens: &mut VecDeque) -> Option { +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() } 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 11ce79a..a027f8c 100644 --- a/src/semantic/mod.rs +++ b/src/semantic/mod.rs @@ -1,14 +1,18 @@ use std::collections::HashMap; +use tracing::trace; + use crate::{ lexer::token::{OperatorType, Token}, - parser::{ast::Tree, symbols::Name}, + parser::{ast::Tree, symbols::Name, types::Type}, util::int_parsing::parse_int, }; +#[derive(Clone, Debug)] pub struct AnalysisState { return_state: ReturnState, namespace: HashMap, + active_loops: usize, } impl Default for AnalysisState { @@ -16,24 +20,68 @@ impl Default for AnalysisState { AnalysisState { return_state: ReturnState::NotReturing, namespace: HashMap::new(), + active_loops: 0, } } } -#[derive(PartialEq)] +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, Clone, Debug)] enum ReturnState { Returning, NotReturing, } -#[derive(PartialEq, PartialOrd)] -enum VariableStatus { +#[derive(Clone, Debug)] +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, Clone, Debug)] +pub enum DeclarationStatus { Declared, Initialized, } #[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 { @@ -43,7 +91,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(()) } @@ -64,25 +118,34 @@ 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()))?; + 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() - .ne(&VariableStatus::Initialized) + .type_status() + .ne(&expression_type) { - state.namespace.insert(name, VariableStatus::Initialized); + 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 +155,28 @@ 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 Some(present_initializer) = initializer { + if get_variable_type(present_initializer.clone(), state) + .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)?; }; if let Tree::Name(identifier, _) = *name { let variable_status = state.namespace.get(&identifier); @@ -108,9 +187,6 @@ pub fn analyze(tree: Box, state: &mut AnalysisState) -> Result<(), String> } state.namespace.insert(identifier.clone(), variable_state); } - if let Some(present_initializer) = initializer { - analyze(present_initializer, state)?; - }; Ok(()) } Tree::IdentifierExpression(identifier) => { @@ -124,7 +200,9 @@ pub fn analyze(tree: Box, state: &mut AnalysisState) -> Result<(), String> .namespace .get(&name) .unwrap() - .eq(&VariableStatus::Declared) + .declaration() + .eq(&DeclarationStatus::Declared) + && state.return_state.ne(&ReturnState::Returning) { return Err(format!( "Uninitialized variable {} used in expression!", @@ -135,18 +213,138 @@ pub fn analyze(tree: Box, state: &mut AnalysisState) -> Result<(), String> Ok(()) } Tree::Name(_, _) => Ok(()), - Tree::BinaryOperation(lhs, rhs, _) => { + Tree::BoolLiteral(_, _) => 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 => { + 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()); + } + } + 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 => { + 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 + | OperatorType::TernaryQuestionMark => return Err("Invalid operator".to_string()), + } analyze(lhs, state)?; analyze(rhs, state) } Tree::Block(statements, _) => { + let mut old_namespace = state.namespace.clone(); for statement in statements { - analyze(Box::new(statement), state)?; + 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(()) } Tree::LValueIdentifier(name) => analyze(name, state), - Tree::Negate(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 { @@ -154,5 +352,191 @@ pub fn analyze(tree: Box, state: &mut AnalysisState) -> Result<(), String> } Ok(()) } + Tree::TernaryOperation(statement, true_statement, false_statement) => { + 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()); + } + 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) + } + 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!")? + .ne(&Type::Bool) + { + return Err("Condition must be a boolean".to_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; + 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) + { + state.return_state = ReturnState::Returning; + } else { + state.return_state = returning_state; + } + 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)) + { + if false_namespace.contains_key(initialized_variable) + && state.namespace.contains_key(initialized_variable) + && false_namespace + .get(initialized_variable) + .unwrap() + .declaration() + .eq(&DeclarationStatus::Initialized) + { + state + .namespace + .get_mut(initialized_variable) + .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; + } + 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!")? + .ne(&Type::Bool) + { + return Err("Condition must be a boolean".to_string()); + } + state.enter_loop(); + analyze(expression, state)?; + state.return_state = returning_state; + state.exit_loop(); + Ok(()) + } + Tree::For(initializer, condition, updater, expression, _) => { + 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) + .ok_or("Variable undefined!")? + .ne(&Type::Bool) + { + 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)?; + 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.namespace = old_namespace; + Ok(()) + } + } +} + +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::ShiftLeft + | OperatorType::ShiftRight + | 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 + } + } + Tree::TernaryOperation(_, true_expression, _) => get_variable_type(true_expression, state), + _ => None, } }