diff --git a/Cargo.lock b/Cargo.lock index 9521d0ff..f9253107 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -27,6 +27,8 @@ dependencies = [ "ast", "astoir_hir", "astoir_hir_lowering", + "astoir_mir", + "astoir_mir_lowering", "compiler_errors", ] @@ -50,6 +52,26 @@ dependencies = [ "compiler_errors", ] +[[package]] +name = "astoir_mir" +version = "0.1.0" +dependencies = [ + "astoir_typing", + "compiler_errors", + "compiler_utils", +] + +[[package]] +name = "astoir_mir_lowering" +version = "0.1.0" +dependencies = [ + "astoir_hir", + "astoir_mir", + "astoir_typing", + "compiler_errors", + "lexer", +] + [[package]] name = "astoir_typing" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 65d258a3..fd67e85c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,2 +1,2 @@ [workspace] -members = ["compiler/ast", "compiler/ast_parser", "compiler/astoir", "compiler/astoir_hir", "compiler/astoir_hir_lowering", "compiler/astoir_typing", "compiler/compiler_errors", "compiler/compiler_main", "compiler/compiler_utils", "testing"] +members = ["compiler/ast", "compiler/ast_parser", "compiler/astoir", "compiler/astoir_hir", "compiler/astoir_hir_lowering", "compiler/astoir_mir", "compiler/astoir_mir_lowering", "compiler/astoir_typing", "compiler/compiler_errors", "compiler/compiler_main", "compiler/compiler_utils", "testing"] diff --git a/compiler/ast/src/tree.rs b/compiler/ast/src/tree.rs index 9b810749..80b4edcb 100644 --- a/compiler/ast/src/tree.rs +++ b/compiler/ast/src/tree.rs @@ -25,6 +25,8 @@ pub enum ASTTreeNodeKind { IntegerLit { val: i128, hash: u64 }, StringLit(String), + ThisStructParam, + OperatorBasedConditionMember { lval: Box, rval: Box, operator: ComparingOperator }, BooleanBasedConditionMember { val: Box, negate: bool }, @@ -51,7 +53,7 @@ pub enum ASTTreeNodeKind { ForBlock { initial_state: Box, cond: Box, increment: Box, body: Vec> }, FunctionCall { func: HashedString, args: Vec> }, - FunctionDeclaration { func_name: HashedString, args: Vec, body: Vec>, return_type: Option }, + FunctionDeclaration { func_name: HashedString, args: Vec, body: Vec>, return_type: Option, requires_this: bool }, ShadowFunctionDeclaration { func_name: HashedString, args: Vec, return_type: Option }, @@ -74,7 +76,7 @@ impl ASTTreeNodeKind { pub fn get_tree_name(&self) -> Option { match self { - ASTTreeNodeKind::FunctionDeclaration { func_name, args: _, body: _, return_type: _ } => { + ASTTreeNodeKind::FunctionDeclaration { func_name, args: _, body: _, return_type: _ , requires_this: _} => { return Some(HashedString::new(func_name.val.to_string())); }, diff --git a/compiler/ast_parser/src/functions/arguments.rs b/compiler/ast_parser/src/functions/arguments.rs index f16b6a2b..4a9b64d9 100644 --- a/compiler/ast_parser/src/functions/arguments.rs +++ b/compiler/ast_parser/src/functions/arguments.rs @@ -1,25 +1,44 @@ //! Module for parsing arguments -use ast::tree::FunctionDeclarationArgument; -use compiler_errors::errs::CompilerResult; +use ast::{tree::FunctionDeclarationArgument, types::CompleteType}; +use compiler_errors::errs::{CompilerResult, ErrorKind, normal::CompilerError}; +use compiler_utils::hash::HashedString; use lexer::token::{LexerToken, LexerTokenType}; use crate::types::parse_type; -pub fn parse_function_arguments(tokens: &Vec, ind: &mut usize) -> CompilerResult> { +pub fn parse_function_arguments(tokens: &Vec, ind: &mut usize, struct_type: Option) -> CompilerResult<(Vec, bool)> { *ind += 1; + let mut depends_on_this: bool = false; let mut args: Vec = Vec::new(); - while *ind < tokens.len() && tokens[*ind].is_keyword() { - let var_type = parse_type(tokens, ind)?; + while *ind < tokens.len() && (tokens[*ind].is_keyword() || tokens[*ind].tok_type == LexerTokenType::This) { + + if tokens[*ind].tok_type == LexerTokenType::This { + if struct_type.is_none() { + return Err(CompilerError::from_ast(ErrorKind::Error, "This requires to be within a struct!".to_string(), &tokens[*ind].pos, &tokens[*ind].get_end_pos())) + } - *ind += 1; - let var_name = tokens[*ind].expects_keyword()?; + if !args.is_empty() { + return Err(CompilerError::from_ast(ErrorKind::Error, "this must be the first parameter of the function.".to_string(), &tokens[*ind].pos, &tokens[*ind].get_end_pos())) + } - args.push(FunctionDeclarationArgument::new(var_name.0, var_type)); + depends_on_this = true; - *ind += 1; + *ind += 1; + + args.push(FunctionDeclarationArgument { name: HashedString::new("this".to_string()), argument_type: struct_type.clone().unwrap() }) + } else { + let var_type = parse_type(tokens, ind)?; + + *ind += 1; + let var_name = tokens[*ind].expects_keyword()?; + + args.push(FunctionDeclarationArgument::new(var_name.0, var_type)); + + *ind += 1; + } if tokens[*ind].tok_type == LexerTokenType::ParenClose { break; @@ -32,5 +51,5 @@ pub fn parse_function_arguments(tokens: &Vec, ind: &mut usize) -> Co tokens[*ind].expects(LexerTokenType::ParenClose)?; - Ok(args) + Ok((args, depends_on_this)) } diff --git a/compiler/ast_parser/src/functions/mod.rs b/compiler/ast_parser/src/functions/mod.rs index a5be18f0..61e60814 100644 --- a/compiler/ast_parser/src/functions/mod.rs +++ b/compiler/ast_parser/src/functions/mod.rs @@ -1,6 +1,6 @@ //! Parser module for functions -use ast::tree::{ASTTreeNode, ASTTreeNodeKind}; +use ast::{tree::{ASTTreeNode, ASTTreeNodeKind}, types::CompleteType}; use compiler_errors::errs::CompilerResult; use compiler_utils::hash::HashedString; use lexer::token::{LexerToken, LexerTokenType}; @@ -11,7 +11,7 @@ pub mod shadow; pub mod arguments; pub mod returns; -pub fn parse_function_declaraction(tokens: &Vec, ind: &mut usize) -> CompilerResult> { +pub fn parse_function_declaraction(tokens: &Vec, ind: &mut usize, struct_type: Option) -> CompilerResult> { let start = tokens[*ind].pos.clone(); *ind += 1; @@ -20,7 +20,7 @@ pub fn parse_function_declaraction(tokens: &Vec, ind: &mut usize) -> *ind += 1; tokens[*ind].expects(LexerTokenType::ParenOpen)?; - let args = parse_function_arguments(tokens, ind)?; + let args = parse_function_arguments(tokens, ind, struct_type)?; *ind += 1; @@ -37,7 +37,7 @@ pub fn parse_function_declaraction(tokens: &Vec, ind: &mut usize) -> let end = tokens[*ind - 1].get_end_pos(); - return Ok(Box::new(ASTTreeNode::new(ASTTreeNodeKind::FunctionDeclaration { func_name: HashedString::new(function_name.0), args, body, return_type: ret_type }, start, end))); + return Ok(Box::new(ASTTreeNode::new(ASTTreeNodeKind::FunctionDeclaration { func_name: HashedString::new(function_name.0), args: args.0, body, return_type: ret_type, requires_this: args.1 }, start, end))); } pub fn parse_function_call(tokens: &Vec, ind: &mut usize) -> CompilerResult> { diff --git a/compiler/ast_parser/src/functions/shadow.rs b/compiler/ast_parser/src/functions/shadow.rs index 4c283a73..72c23d3c 100644 --- a/compiler/ast_parser/src/functions/shadow.rs +++ b/compiler/ast_parser/src/functions/shadow.rs @@ -16,7 +16,7 @@ pub fn parse_shadow_function_declaration(tokens: &Vec, ind: &mut usi *ind += 1; tokens[*ind].expects(LexerTokenType::ParenOpen)?; - let args = parse_function_arguments(tokens, ind)?; + let args = parse_function_arguments(tokens, ind, None)?; *ind += 1; @@ -32,5 +32,5 @@ pub fn parse_shadow_function_declaration(tokens: &Vec, ind: &mut usi end = tokens[*ind - 1].get_end_pos().clone(); } - return Ok(Box::new(ASTTreeNode::new(ASTTreeNodeKind::ShadowFunctionDeclaration { func_name: HashedString::new(function_name.0), args, return_type: ret_type }, start, end))) + return Ok(Box::new(ASTTreeNode::new(ASTTreeNodeKind::ShadowFunctionDeclaration { func_name: HashedString::new(function_name.0), args: args.0, return_type: ret_type }, start, end))) } \ No newline at end of file diff --git a/compiler/ast_parser/src/parser.rs b/compiler/ast_parser/src/parser.rs index 3c5ba84e..7273778d 100644 --- a/compiler/ast_parser/src/parser.rs +++ b/compiler/ast_parser/src/parser.rs @@ -15,7 +15,7 @@ use crate::{control::{for_loop::parse_for_loop, if_else::parse_if_statement, whi pub fn parse_ast_node(tokens: &Vec, ind: &mut usize) -> CompilerResult> { match &tokens[*ind].tok_type { LexerTokenType::Function => { - return parse_function_declaraction(tokens, ind); + return parse_function_declaraction(tokens, ind, None); }, LexerTokenType::ShadowFunction => { diff --git a/compiler/ast_parser/src/structs/members.rs b/compiler/ast_parser/src/structs/members.rs index 7d10103d..17748541 100644 --- a/compiler/ast_parser/src/structs/members.rs +++ b/compiler/ast_parser/src/structs/members.rs @@ -11,8 +11,6 @@ pub fn parse_types_field_member(tokens: &Vec, ind: &mut usize) -> Co let start = tokens[*ind].pos.clone(); let member_type = parse_type(tokens, ind)?; - *ind += 1; - let field_name = tokens[*ind].expects_keyword()?; let end = tokens[*ind].get_end_pos().clone(); diff --git a/compiler/ast_parser/src/structs/mod.rs b/compiler/ast_parser/src/structs/mod.rs index 24da2c9f..b01fb120 100644 --- a/compiler/ast_parser/src/structs/mod.rs +++ b/compiler/ast_parser/src/structs/mod.rs @@ -2,9 +2,9 @@ use compiler_errors::errs::CompilerResult; use compiler_utils::hash::HashedString; use lexer::token::{LexerToken, LexerTokenType}; -use ast::tree::{ASTTreeNode, ASTTreeNodeKind}; +use ast::{tree::{ASTTreeNode, ASTTreeNodeKind}, types::CompleteType}; -use crate::structs::members::parse_types_field_member; +use crate::{functions::parse_function_declaraction, structs::members::parse_types_field_member}; pub mod members; @@ -22,8 +22,14 @@ pub fn parse_type_declaration(tokens: &Vec, ind: &mut usize, layout: let mut members: Vec> = Vec::new(); + let temp_type = CompleteType { base_type: type_name.1, sizes: vec![], types: vec![], pointer: false, pointer_array: false, array_sz: 0 }; + while tokens[*ind].tok_type != LexerTokenType::BracketClose { - members.push(parse_types_field_member(tokens, ind)?); + if tokens[*ind].tok_type == LexerTokenType::Function { + members.push(parse_function_declaraction(tokens, ind, Some(temp_type.clone()))?); + } else { + members.push(parse_types_field_member(tokens, ind)?); + } } let end = tokens[*ind].get_end_pos().clone(); diff --git a/compiler/astoir/Cargo.toml b/compiler/astoir/Cargo.toml index 9c575402..5a7ca9b8 100644 --- a/compiler/astoir/Cargo.toml +++ b/compiler/astoir/Cargo.toml @@ -5,6 +5,8 @@ edition = "2024" [dependencies] astoir_hir = {path = "../astoir_hir"} +astoir_mir = {path = "../astoir_mir"} ast = { path = "../ast" } compiler_errors = { path = "../compiler_errors" } -astoir_hir_lowering = { path = "../astoir_hir_lowering" } \ No newline at end of file +astoir_hir_lowering = { path = "../astoir_hir_lowering" } +astoir_mir_lowering = { path = "../astoir_mir_lowering" } \ No newline at end of file diff --git a/compiler/astoir/src/lib.rs b/compiler/astoir/src/lib.rs index 6bc12720..3884df58 100644 --- a/compiler/astoir/src/lib.rs +++ b/compiler/astoir/src/lib.rs @@ -1,12 +1,22 @@ use ast::ctx::ParserCtx; use astoir_hir::ctx::HIRContext; use astoir_hir_lowering::lower_ast; -use compiler_errors::errs::{CompilerResult}; +use astoir_mir::ctx::MIRContext; +use astoir_mir_lowering::lower_hir; +use compiler_errors::errs::{CompilerResult, normal::CompilerError}; pub enum IRLevel { - HIR + HIR, + MIR } pub fn run_astoir_hir(ctx: ParserCtx) -> CompilerResult { return lower_ast(ctx); } + +pub fn run_astoir_mir(ctx: ParserCtx) -> CompilerResult { + return match lower_hir(run_astoir_hir(ctx)?) { + Ok(v) => Ok(v), + Err(e) => Err(CompilerError::from_base_posless(e)) + } +} \ No newline at end of file diff --git a/compiler/astoir_hir/src/ctx.rs b/compiler/astoir_hir/src/ctx.rs index f8dc5eac..fbfbf264 100644 --- a/compiler/astoir_hir/src/ctx.rs +++ b/compiler/astoir_hir/src/ctx.rs @@ -1,12 +1,12 @@ //! The context definitions for the AstoIR HIR layer. -use std::{collections::HashMap}; +use std::{collections::{HashMap, HashSet}, hash::Hash}; use astoir_typing::{complete::{ComplexType}, storage::TypeStorage}; use compiler_errors::{IR_ALREADY_EXISTING_ELEM, IR_FIND_ELEMENT, IR_OUTSIDE_ERA_HIGHER, IR_OUTSIDE_ERA_LOWER, errs::{BaseResult, base::BaseError}}; use compiler_utils::{hash::SelfHash, utils::indexed::IndexStorage}; -use crate::nodes::HIRNode; +use crate::{nodes::HIRNode, structs::HIRStructContainer}; pub type HIRFunction = (Option, Vec<(u64, ComplexType)>); @@ -27,7 +27,7 @@ pub type HIRFunction = (Option, Vec<(u64, ComplexType)>); /// Every branch index stores an end branch index from when it ends (inside of `ending_eras`). This end branch index will be used to calculate when the era of a variable ends. /// /// -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct HIRBranchedContext { pub hash_to_ind: HashMap, // TODO: add a layer system to this so you are able to put multiple variables with the same name. pub ending_eras: HashMap, @@ -62,14 +62,14 @@ impl HIRBranchedContext { } /// Introduces a new variable in the current branch era. - pub fn introduce_variable(&mut self, hash: u64, t: ComplexType) -> BaseResult { + pub fn introduce_variable(&mut self, hash: u64, t: ComplexType, has_default: bool) -> BaseResult { let identity = SelfHash { hash }; if self.hash_to_ind.contains_key(&identity) { return Err(BaseError::err(IR_ALREADY_EXISTING_ELEM!().to_string())); } - let var: HIRBranchedVariable = HIRBranchedVariable { introduced_in_era: self.current_branch, variable_type: t }; + let var: HIRBranchedVariable = HIRBranchedVariable { introduced_in_era: self.current_branch, variable_type: t, has_default, introduced_values: HashSet::new(), requires_address: false }; self.variables.push(var); let ind: usize = self.current_element_index; @@ -80,6 +80,18 @@ impl HIRBranchedContext { return Ok(ind); } + pub fn introduce_variable_assign(&mut self, ind: usize) -> bool { + let var = &mut self.variables[ind]; + + if var.has_default { + return true; + } + + var.introduced_values.insert(self.current_branch); + + return true; + } + /// Determines if the element with the given index is still alive in the current branch. pub fn is_alive(&self, ind: usize) -> bool { let start_branch = self.variables[ind].introduced_in_era; @@ -88,15 +100,17 @@ impl HIRBranchedContext { return false; } - if !self.ending_eras.contains_key(&start_branch) { + return self.is_era_alive(start_branch); + } + + pub fn is_era_alive(&self, era: usize) -> bool { + if !self.ending_eras.contains_key(&era) { // If the era hasn't ended yet, (the ending era isn't added for branch start_branch) // this means that the variable is still alive and we are still inside of the branch start_branch return true; } - let end = self.ending_eras[&start_branch]; - - return end <= self.current_branch; + return false; } pub fn is_dropped_before(&self, ind: usize) -> bool { @@ -109,6 +123,22 @@ impl HIRBranchedContext { return self.ending_eras[&start_branch] < self.current_branch; } + pub fn has_variable_value(&self, ind: usize) -> bool { + let var = &self.variables[ind]; + + if var.has_default { + return true; + } + + for era in var.introduced_values.iter() { + if self.is_era_alive(*era) { + return true; + } + } + + return false; + } + pub fn get_ending_era(&self, ind: usize) -> usize { return self.ending_eras[&self.variables[ind].introduced_in_era]; } @@ -135,12 +165,23 @@ impl HIRBranchedContext { } } + pub fn is_eligible_for_ssa(&self, ind: usize) -> bool { + let var = &self.variables[ind]; + + return !var.requires_address; + } + } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct HIRBranchedVariable { pub introduced_in_era: usize, - pub variable_type: ComplexType + pub variable_type: ComplexType, + + pub requires_address: bool, + + pub has_default: bool, + pub introduced_values: HashSet // TODO: try to potentially reduce this } #[derive(Debug)] @@ -148,6 +189,7 @@ pub struct HIRContext { pub functions: IndexStorage, pub function_declarations: Vec>>, pub static_variables: IndexStorage, + pub struct_func_impls: HashMap, pub type_storage: TypeStorage } @@ -159,7 +201,7 @@ pub enum VariableKind { impl HIRContext { pub fn new() -> BaseResult { - return Ok(HIRContext { functions: IndexStorage::new(), static_variables: IndexStorage::new(), type_storage: TypeStorage::new()?, function_declarations: vec![] }) + return Ok(HIRContext { functions: IndexStorage::new(), static_variables: IndexStorage::new(), type_storage: TypeStorage::new()?, function_declarations: vec![], struct_func_impls: HashMap::new() }) } pub fn translate_function(&self, func_hash: u64) -> BaseResult { diff --git a/compiler/astoir_hir/src/nodes.rs b/compiler/astoir_hir/src/nodes.rs index e65e8436..2e231c98 100644 --- a/compiler/astoir_hir/src/nodes.rs +++ b/compiler/astoir_hir/src/nodes.rs @@ -1,11 +1,12 @@ //! The nodes inside of the AstoIR HIR. use astoir_typing::{complete::{ComplexType, ConcreteType}, hashes::{BOOLEAN_TYPE, STATIC_STR}, structs::StructTypeContainer}; +use compiler_errors::errs::{BaseResult, base::BaseError}; use lexer::toks::{comp::ComparingOperator, math::MathOperator}; use crate::{ctx::{HIRBranchedContext, HIRContext}, structs::{HIRIfBranch, StructLRUStep}}; -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum HIRNode { VarDeclaration { variable: usize, var_type: ComplexType, default_val: Option> }, StaticVariableDeclaration { variable: usize, var_type: ComplexType, default_val: Option> }, @@ -20,7 +21,7 @@ pub enum HIRNode { StructLRU { steps: Vec, last: ComplexType }, StructDeclaration { type_name: usize, container: StructTypeContainer, layout: bool }, - FunctionDeclaration { func_name: usize, arguments: Vec<(u64, ComplexType)>, return_type: Option, body: Vec>, ctx: HIRBranchedContext }, + FunctionDeclaration { func_name: usize, arguments: Vec<(u64, ComplexType)>, return_type: Option, body: Vec>, ctx: HIRBranchedContext, requires_this: bool }, ShadowFunctionDeclaration { func_name: usize, arguments: Vec<(u64, ComplexType)>, return_type: Option }, FunctionCall { func_name: usize, arguments: Vec> }, @@ -40,6 +41,22 @@ pub enum HIRNode { } impl HIRNode { + pub fn is_variable_reference(&self) -> bool { + if let HIRNode::VariableReference { .. } = self { + return true; + } + + return false; + } + + pub fn as_variable_reference(&self) -> BaseResult<(usize, bool)> { + if let HIRNode::VariableReference { index, is_static } = self { + return Ok((*index, *is_static)) + } + + return Err(BaseError::err("Tried using as_variable_reference on a non var ref".to_string())) + } + pub fn get_node_type(&self, context: &HIRContext, curr_ctx: &HIRBranchedContext) -> Option { match self { HIRNode::VariableReference { index, is_static } => { diff --git a/compiler/astoir_hir/src/structs.rs b/compiler/astoir_hir/src/structs.rs index 394e097f..f0fb144b 100644 --- a/compiler/astoir_hir/src/structs.rs +++ b/compiler/astoir_hir/src/structs.rs @@ -2,15 +2,21 @@ use crate::nodes::HIRNode; -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum StructLRUStep { FunctionCall { func: usize, args: Vec> }, VariableStep { variable: usize } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum HIRIfBranch { IfBranch { cond: Box, body: Vec> }, ElseIfBranch { cond: Box, body: Vec> }, ElseBranch { body: Vec> } -} \ No newline at end of file +} + +#[derive(Debug)] +pub struct HIRStructContainer { + pub function_impls: Vec> +} + diff --git a/compiler/astoir_hir_lowering/src/func.rs b/compiler/astoir_hir_lowering/src/func.rs index 5de306d8..71a3b293 100644 --- a/compiler/astoir_hir_lowering/src/func.rs +++ b/compiler/astoir_hir_lowering/src/func.rs @@ -33,7 +33,7 @@ pub fn lower_ast_function_call(context: &HIRContext, curr_ctx: &HIRBranchedConte } pub fn lower_ast_function_declaration(context: &mut HIRContext, node: Box) -> CompilerResult> { - if let ASTTreeNodeKind::FunctionDeclaration { func_name, args, body, return_type } = node.kind { + if let ASTTreeNodeKind::FunctionDeclaration { func_name, args, body, return_type, requires_this } = node.kind { let ret_type; if return_type.is_some() { @@ -65,7 +65,7 @@ pub fn lower_ast_function_declaration(context: &mut HIRContext, node: Box {}, Err(e) => return Err(CompilerError::from_base(e, &node.start, &node.end)) } @@ -77,7 +77,13 @@ pub fn lower_ast_function_declaration(context: &mut HIRContext, node: Box return lower_ast_variable_declaration(context, curr_ctx, node), ASTTreeNodeKind::FunctionCall { .. } => return lower_ast_function_call(context, curr_ctx, node), + ASTTreeNodeKind::VarValueChange { .. } => return lower_ast_variable_assign(context, curr_ctx, node), ASTTreeNodeKind::MathResult { .. } => return lower_ast_math_operation(context, curr_ctx, node, true), @@ -74,9 +75,9 @@ pub fn lower_ast_toplevel(context: &mut HIRContext, node: Box) -> C }, ASTTreeNodeKind::ShadowFunctionDeclaration { .. } => { - lower_ast_shadow_function_declaration(context, node)?; + let func_decl = lower_ast_shadow_function_declaration(context, node)?; - context.function_declarations.push(None); + context.function_declarations.push(Some(func_decl)); return Ok(true); }, diff --git a/compiler/astoir_hir_lowering/src/structs.rs b/compiler/astoir_hir_lowering/src/structs.rs index 459b32df..f25329ef 100644 --- a/compiler/astoir_hir_lowering/src/structs.rs +++ b/compiler/astoir_hir_lowering/src/structs.rs @@ -1,9 +1,9 @@ use ast::tree::{ASTTreeNode, ASTTreeNodeKind}; -use astoir_hir::{ctx::HIRContext, nodes::HIRNode}; +use astoir_hir::{ctx::{HIRBranchedContext, HIRContext}, nodes::HIRNode, structs::HIRStructContainer}; use astoir_typing::{base::BaseType, structs::StructTypeContainer}; -use compiler_errors::{IR_TYPE_WRONG_KIND, errs::{CompilerResult, ErrorKind, normal::CompilerError}}; +use compiler_errors::{IR_TYPE_WRONG_KIND, errs::{CompilerResult, ErrorKind, base::BaseError, normal::CompilerError}}; -use crate::types::lower_ast_type; +use crate::{lower_ast_body, types::lower_ast_type}; fn lower_ast_struct_member(context: &mut HIRContext, node: Box, container: &mut StructTypeContainer) -> CompilerResult { if let ASTTreeNodeKind::StructFieldMember { name, member_type } = node.kind.clone() { @@ -13,6 +13,44 @@ fn lower_ast_struct_member(context: &mut HIRContext, node: Box, con }; container.fields.append(name.hash, t); + return Ok(true); + } + + return Err(CompilerError::from_ast(ErrorKind::Error, IR_TYPE_WRONG_KIND!().to_string(), &node.start, &node.end)) +} + +fn lower_ast_struct_function_decl(context: &mut HIRContext, node: Box, container: &mut StructTypeContainer) -> CompilerResult> { + if let ASTTreeNodeKind::FunctionDeclaration { func_name, args, body, return_type, requires_this } = node.kind.clone() { + let mut arguments = vec![]; + + for arg in args { + let lowered = match lower_ast_type(context, arg.argument_type) { + Ok(v) => v, + Err(e) => return Err(CompilerError::from_base(e, &node.start, &node.end)) + }; + + arguments.push((arg.name.hash, lowered)); + } + + let ret_type; + + if return_type.is_some() { + let lowered = match lower_ast_type(context, return_type.unwrap()) { + Ok(v) => v, + Err(e) => return Err(CompilerError::from_base(e, &node.start, &node.end)) + }; + + ret_type = Some(lowered) + } else { + ret_type = None; + } + + let mut curr_ctx = HIRBranchedContext::new(); + let body = lower_ast_body(context, &mut curr_ctx, body, true)?; + + let ind = container.functions.append(func_name.hash, (ret_type.clone(), arguments.clone())); + + return Ok(Box::new(HIRNode::FunctionDeclaration { func_name: ind, arguments, return_type: ret_type, body, ctx: curr_ctx, requires_this })) } return Err(CompilerError::from_ast(ErrorKind::Error, IR_TYPE_WRONG_KIND!().to_string(), &node.start, &node.end)) @@ -20,11 +58,9 @@ fn lower_ast_struct_member(context: &mut HIRContext, node: Box, con pub fn lower_ast_struct_declaration(context: &mut HIRContext, node: Box) -> CompilerResult> { if let ASTTreeNodeKind::StructLayoutDeclaration { name, layout, members } = node.kind.clone() { - let mut container = StructTypeContainer::new(); + let mut container = StructTypeContainer::new(context.type_storage.types.len()); - for member in members { - lower_ast_struct_member(context, member, &mut container)?; - } + let mut func_impls = vec![]; let base = BaseType::Struct(layout, container.clone()); @@ -33,6 +69,27 @@ pub fn lower_ast_struct_declaration(context: &mut HIRContext, node: Box return Err(CompilerError::from_base(e, &node.start, &node.end)) }; + for member in members { + match &member.kind { + &ASTTreeNodeKind::StructFieldMember { .. } => { + lower_ast_struct_member(context, member, &mut container)?; + + context.type_storage.types[ind] = BaseType::Struct(layout, container.clone()); + }, + &ASTTreeNodeKind::FunctionDeclaration { .. } => { + let body = lower_ast_struct_function_decl(context, member, &mut container)?; + + context.type_storage.types[ind] = BaseType::Struct(layout, container.clone()); + + func_impls.push(body); + }, + + _ => return Err(CompilerError::from_ast(ErrorKind::Error, IR_TYPE_WRONG_KIND!().to_string(), &node.start, &node.end)) + }; + } + + context.struct_func_impls.insert(ind, HIRStructContainer { function_impls: func_impls }); + return Ok(Box::new(HIRNode::StructDeclaration { type_name: ind, container, layout })); } diff --git a/compiler/astoir_hir_lowering/src/values.rs b/compiler/astoir_hir_lowering/src/values.rs index 9a2349f1..8fe84a2b 100644 --- a/compiler/astoir_hir_lowering/src/values.rs +++ b/compiler/astoir_hir_lowering/src/values.rs @@ -138,7 +138,7 @@ pub fn lower_ast_value(context: &HIRContext, curr_ctx: &HIRBranchedContext, node }, ASTTreeNodeKind::VariableReference(_) => { - return lower_ast_variable_reference(context, curr_ctx, node) + return lower_ast_variable_reference(context, curr_ctx, node, true) }, _ => make_invalid_type_err!(node) diff --git a/compiler/astoir_hir_lowering/src/var.rs b/compiler/astoir_hir_lowering/src/var.rs index e7508bf8..859726b8 100644 --- a/compiler/astoir_hir_lowering/src/var.rs +++ b/compiler/astoir_hir_lowering/src/var.rs @@ -1,6 +1,6 @@ use ast::{tree::{ASTTreeNode, ASTTreeNodeKind}}; use astoir_hir::{ctx::{HIRBranchedContext, HIRContext, VariableKind, get_variable}, nodes::HIRNode}; -use compiler_errors::{IR_INVALID_NODE_TYPE, IR_VALUE_TYPE_TRANSMUTE, errs::{CompilerResult, ErrorKind, normal::CompilerError}}; +use compiler_errors::{IR_INVALID_NODE_TYPE, IR_VALUE_TYPE_TRANSMUTE, VARIABLE_REQ_VALUE, errs::{CompilerResult, ErrorKind, normal::CompilerError}}; use crate::{types::lower_ast_type, values::lower_ast_value}; @@ -11,7 +11,7 @@ pub fn lower_ast_variable_declaration(context: &HIRContext, curr_ctx: &mut HIRBr Err(e) => return Err(CompilerError::from_base(e, &node.start, &node.end)) }; - let name_ind = match curr_ctx.introduce_variable(var_name.hash, lowered.clone()) { + let name_ind = match curr_ctx.introduce_variable(var_name.hash, lowered.clone(), value.is_some()) { Ok(v) => v, Err(e) => return Err(CompilerError::from_base(e, &node.start, &node.end)) }; @@ -38,7 +38,7 @@ pub fn lower_ast_variable_declaration(context: &HIRContext, curr_ctx: &mut HIRBr return Err(CompilerError::from_ast(ErrorKind::Error, IR_INVALID_NODE_TYPE!().to_string(), &node.start, &node.end)) } -pub fn lower_ast_variable_reference(context: &HIRContext, curr_ctx: &HIRBranchedContext, node: Box) -> CompilerResult> { +pub fn lower_ast_variable_reference(context: &HIRContext, curr_ctx: &HIRBranchedContext, node: Box, requires_value: bool) -> CompilerResult> { if let ASTTreeNodeKind::VariableReference(str) = node.kind.clone() { let var = match get_variable(context, curr_ctx, str.hash) { Ok(v) => v, @@ -49,8 +49,34 @@ pub fn lower_ast_variable_reference(context: &HIRContext, curr_ctx: &HIRBranched return Ok(Box::new(HIRNode::VariableReference { index: var.2, is_static: true })) } + if requires_value { + if !curr_ctx.has_variable_value(var.2) { + return Err(CompilerError::from_ast(ErrorKind::Error, VARIABLE_REQ_VALUE!().to_string(), &node.start, &node.end)) + } + } + return Ok(Box::new(HIRNode::VariableReference { index: var.2, is_static: false })) } return Err(CompilerError::from_ast(ErrorKind::Error, IR_INVALID_NODE_TYPE!().to_string(), &node.start, &node.end)) } + +pub fn lower_ast_variable_assign(context: &HIRContext, curr_ctx: &mut HIRBranchedContext, node: Box) -> CompilerResult> { + if let ASTTreeNodeKind::VarValueChange { var, value } = node.kind.clone() { + let value = lower_ast_value(context, curr_ctx, value)?; + let variable_reference = lower_ast_variable_reference(context, curr_ctx, var, false)?; + + let var = match variable_reference.as_variable_reference() { + Ok(v) => v, + Err(e) => return Err(CompilerError::from_base(e, &node.start, &node.end)) + }; + + if !var.1 { + curr_ctx.introduce_variable_assign(var.0); + } + + return Ok(Box::new(HIRNode::VarAssigment { variable: var.0, val: value })) + } + + return Err(CompilerError::from_ast(ErrorKind::Error, IR_INVALID_NODE_TYPE!().to_string(), &node.start, &node.end)) +} \ No newline at end of file diff --git a/compiler/astoir_mir/Cargo.toml b/compiler/astoir_mir/Cargo.toml new file mode 100644 index 00000000..81e4726f --- /dev/null +++ b/compiler/astoir_mir/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "astoir_mir" +version = "0.1.0" +edition = "2024" + +[dependencies] +compiler_errors = { path = "../compiler_errors" } +compiler_utils = { path = "../compiler_utils" } +astoir_typing = { path = "../astoir_typing" } diff --git a/compiler/astoir_mir/src/blocks/hints.rs b/compiler/astoir_mir/src/blocks/hints.rs new file mode 100644 index 00000000..b2012459 --- /dev/null +++ b/compiler/astoir_mir/src/blocks/hints.rs @@ -0,0 +1,95 @@ +use astoir_typing::compacted::CompactedType; +use compiler_errors::errs::{BaseResult, base::BaseError}; + +use crate::{blocks::MIRBlockVariableSSAHint, vals::{base::BaseMIRValue, consts::MIRConstantValue}}; + + +/// A hint on a given value, contains constants or pointer types for example +#[derive(Clone)] +pub enum MIRValueHint { + Constant(MIRConstantValue), + Pointer(CompactedType), + Value(CompactedType) +} + +impl MIRValueHint { + pub fn is_determined(&self) -> bool { + if let &MIRValueHint::Constant(_) = self { + return true; + } + + return false; + } + + pub fn as_const(&self) -> BaseResult { + match self { + MIRValueHint::Constant(e) => Ok(e.clone()), + _ => Err(BaseError::critical("Cannot use as_const on a non const!".to_string())) + } + } + + pub fn as_pointer(&self) -> BaseResult { + match self { + MIRValueHint::Pointer(e) => Ok(e.clone()), + _ => Err(BaseError::critical("Cannot use as_pointer on a non pointer!".to_string())) + } + } + + pub fn as_value(&self) -> BaseResult { + match self { + MIRValueHint::Value(e) => Ok(e.clone()), + _ => Err(BaseError::critical("Cannot use as_value on a non value!".to_string())) + } + } + + pub fn from_ptr(val: CompactedType) -> Self { + return MIRValueHint::Pointer(val) + } + +} + +impl Into for MIRConstantValue { + fn into(self) -> MIRValueHint { + return MIRValueHint::Constant(self) + } +} + +impl Into for CompactedType { + fn into(self) -> MIRValueHint { + return MIRValueHint::Value(self) + } +} + +pub struct HintStorage { + pub vec: Vec, +} + +impl HintStorage { + pub fn new() -> Self { + HintStorage { vec: vec![] } + } + + + /// Introduces a new SSA value hint. Returns the hint index. + /// # Usage + /// Every single SSA value should have a hint on what it is. Furthermore, this hint index will be used to identify the different SSA values instead of raw instruction indexes. + /// + /// # Globality + /// Using hint indexes to represent different SSA values allows us to guarantee that SSA values will work on inner blocks. + pub fn append_hint(&mut self, hint: MIRValueHint) -> usize { + let ind = self.vec.len(); + + self.vec.push(hint); + + return ind; + } + + /// Gets the hint based on the hint index. + pub fn get_hint(&self, hint_ind: usize) -> BaseResult { + if self.vec.len() <= hint_ind { + return Err(BaseError::err("Invalid hint".to_string())) + } + + return Ok(self.vec[hint_ind].clone()) + } +} \ No newline at end of file diff --git a/compiler/astoir_mir/src/blocks/mod.rs b/compiler/astoir_mir/src/blocks/mod.rs new file mode 100644 index 00000000..8836d251 --- /dev/null +++ b/compiler/astoir_mir/src/blocks/mod.rs @@ -0,0 +1,160 @@ +use std::{collections::HashMap, fmt::Display}; + +use compiler_errors::errs::{BaseResult, base::BaseError}; + +use crate::{blocks::{refer::MIRBlockReference}, builder::build_phi, ctx::MIRContext, insts::{MIRInstruction}, vals::{base::BaseMIRValue, refer::MIRVariableReference}}; + +pub mod refer; +pub mod hints; + +/// The type of variable inside of a MIR block. +#[derive(Clone, PartialEq)] +pub enum MIRBlockVariableType { + /// SSAs, allow for direct register usage. + /// Requires: + /// - Variable's address not being obtained (value never referenced) + SSA, + + /// Pointer value, uses the stack. + /// Should be used incase SSA fails. + Pointer +} + +#[derive(Clone)] +pub struct MIRBlockVariableSSAHint { + pub kind: MIRBlockVariableType, + pub hint: Option +} + +impl PartialEq for MIRBlockVariableSSAHint { + fn eq(&self, other: &Self) -> bool { + return self.kind == other.kind && self.hint == other.hint; + } +} + +/// Represents a function block or a branch. +pub struct MIRBlock { + instructions: Vec, + + /// The block references that will merge into this one + pub merge_blocks: Vec, + + pub self_ref: MIRBlockReference, + + /// Hints for the index of the SSA value for the given variable. Will be the pointer value if the variable is not SSA. + /// The indexes used are the variable indexes and not the SSA indexes. + pub variables: HashMap +} + +impl MIRBlock { + pub fn new(self_ref: MIRBlockReference) -> Self { + MIRBlock { instructions: vec![], variables: HashMap::new(), merge_blocks: vec![], self_ref } + } + + pub fn new_merge(base: MIRBlockReference, ctx: &mut MIRContext, append_to_merge_blocks: bool) -> MIRBlockReference { + let ind = ctx.create_block(); + + let variables = ctx.blocks[base].variables.clone(); + + let block = &mut ctx.blocks[ind]; + + for (ind, hint) in variables { + block.variables.insert(ind, hint); + } + + if append_to_merge_blocks { + ctx.blocks[base].merge_blocks.push(ind) + } + + return ind; + } + + pub fn get_variable_ref(&self, var_ind: usize) -> BaseResult { + let var = &self.variables[&var_ind]; + + if var.kind == MIRBlockVariableType::SSA { + return Ok(MIRVariableReference::from(var_ind)); + } + + let unpacked = match &var.hint { + Some(v) => v.clone(), + None => return Err(BaseError::err("Missing BaseMIRValue in pointer hint".to_string())) + }; + + return Ok(MIRVariableReference::from(unpacked.as_ptr()?)); + } + + pub fn append(&mut self, instruction: MIRInstruction) { + self.instructions.push(instruction.clone()); + } + + pub fn append_start(&mut self, instruction: MIRInstruction) { + if self.instructions.is_empty() { + self.instructions.push(instruction.clone()); + } else { + self.instructions.insert(0, instruction.clone()); + } + } + + /// Resolves changes in SSA handled variables from the different merge blocks. + /// + /// # Behavior + /// First checks inside of every merge blocks for changes of SSA values for variables in the hinting table. + /// Then uses a `phi` instruction to obtain the SSA values in this block. Also automatically updates the variable hints inside of this block. + /// + pub fn resolve_ssa_changes(&mut self, ctx: &mut MIRContext) -> BaseResult { + let mut vals = vec![]; + + for (ind, hint) in self.variables.iter() { + let mut choices: Vec<(MIRBlockReference, BaseMIRValue)> = vec![]; + + for block_ref in &self.merge_blocks { + let block = &ctx.blocks[*block_ref]; + let block_hint = &block.variables[ind]; + + if hint != block_hint && block_hint.hint.is_some() { + choices.push((*block_ref, block_hint.hint.clone().unwrap())); + } + } + + vals.push((*ind, choices)); + } + + ctx.writer.move_end(self.self_ref); + + for val in vals { + let res = build_phi(ctx, val.1)?; + + let mut hint = self.variables[&val.0].clone(); + hint.hint = Some(res); + + self.variables.insert(val.0, hint); + } + + return Ok(true); + } + + pub fn is_empty(&self) -> bool { + return self.instructions.is_empty(); + } +} + +impl Display for MIRBlock { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + writeln!(f, "%block_{}", self.self_ref)?; + + if !self.merge_blocks.is_empty() { + writeln!(f, "merge_blocks")?; + + for block in &self.merge_blocks { + writeln!(f, "- {}", block)?; + } + } + + for inst in &self.instructions { + write!(f, " {}", inst)?; + } + + Ok(()) + } +} \ No newline at end of file diff --git a/compiler/astoir_mir/src/blocks/refer.rs b/compiler/astoir_mir/src/blocks/refer.rs new file mode 100644 index 00000000..72858e48 --- /dev/null +++ b/compiler/astoir_mir/src/blocks/refer.rs @@ -0,0 +1 @@ +pub type MIRBlockReference = usize; \ No newline at end of file diff --git a/compiler/astoir_mir/src/builder.rs b/compiler/astoir_mir/src/builder.rs new file mode 100644 index 00000000..6e0775be --- /dev/null +++ b/compiler/astoir_mir/src/builder.rs @@ -0,0 +1,417 @@ +//! Utility functions to build instructions and more + +use astoir_typing::compacted::CompactedType; +use compiler_errors::{IR_FUNCTION_INVALID_ARGUMENTS, errs::{BaseResult, base::BaseError}}; + +use crate::{blocks::{hints::MIRValueHint, refer::MIRBlockReference}, ctx::MIRContext, insts::MIRInstruction, vals::{base::BaseMIRValue, float::MIRFloatValue, int::MIRIntValue, ptr::MIRPointerValue}}; + +pub fn build_stack_alloc(ctx: &mut MIRContext, size: usize, t: CompactedType) -> BaseResult { + let res = ctx.append_inst(MIRInstruction::StackAlloc { alloc_size: size, t: t.clone() }).get()?; + + let hint = ctx.ssa_hints.append_hint(MIRValueHint::Pointer(t)); + + if res.get_ssa_index() != hint { + return Err(BaseError::err("Couldn't hint SSA value for pointer! Indexes are different".to_string())) + } + + return res.as_ptr() +} + +pub fn build_load(ctx: &mut MIRContext, ptr: MIRPointerValue) -> BaseResult { + let res = ctx.append_inst(MIRInstruction::Load { value: ptr }).get()?; + + return Ok(res); +} + +pub fn build_store(ctx: &mut MIRContext, ptr: MIRPointerValue, val: BaseMIRValue) -> BaseResult { + let base: BaseMIRValue = ptr.clone().into(); + + let hint = ctx.ssa_hints.get_hint(base.get_ssa_index())?.as_pointer()?; + + if !hint.base.is_equal(&val.vtype.base) { + return Err(BaseError::err("Cannot put this value onto this pointer as it is not the same type!".to_string())) + } + + ctx.append_inst(MIRInstruction::Store { variable: ptr, value: val }); + + return Ok(true) +} + +pub fn build_downcast_int(ctx: &mut MIRContext, val: MIRIntValue, size: usize) -> BaseResult { + if val.size <= size { + return Err(BaseError::critical("Tried using downintcast on a smaller sized int!".to_string())); + } + + let res = ctx.append_inst(MIRInstruction::DowncastInteger { val, size }).get()?; + + return res.as_int(); +} + +pub fn build_upcast_int(ctx: &mut MIRContext, val: MIRIntValue, size: usize) -> BaseResult { + if val.size >= size { + return Err(BaseError::critical("Tried using upintcast on a higher sized int!".to_string())); + } + + let res = ctx.append_inst(MIRInstruction::UpcastInteger { val, size }).get()?; + + return res.as_int(); +} + +pub fn build_downcast_float(ctx: &mut MIRContext, val: MIRFloatValue, exponent: usize, fraction: usize) -> BaseResult { + if val.exponent + val.fraction <= exponent + fraction { + return Err(BaseError::critical("Tried using downfloatcast on a smaller sized int!".to_string())); + } + + let res = ctx.append_inst(MIRInstruction::DowncastFloat { val, exponent, fraction }).get()?; + + return res.as_float(); +} + +pub fn build_upcast_float(ctx: &mut MIRContext, val: MIRFloatValue, exponent: usize, fraction: usize) -> BaseResult { + if val.exponent + val.fraction >= exponent + fraction { + return Err(BaseError::critical("Tried using upfloatcast on a higher sized int!".to_string())); + } + + let res = ctx.append_inst(MIRInstruction::UpcastFloat { val, exponent, fraction }).get()?; + + return res.as_float(); +} + +pub fn build_int_add(ctx: &mut MIRContext, left: MIRIntValue, right: MIRIntValue, signed: bool) -> BaseResult { + if left.size != right.size { + return Err(BaseError::critical("Tried using iadd on different sized integers".to_string())); + } + + let res = ctx.append_inst(MIRInstruction::IntegerAdd { signed, left, right }).get()?; + + return res.as_int(); +} + +pub fn build_int_sub(ctx: &mut MIRContext, left: MIRIntValue, right: MIRIntValue, signed: bool) -> BaseResult { + if left.size != right.size { + return Err(BaseError::critical("Tried using isub on different sized integers".to_string())); + } + + let res = ctx.append_inst(MIRInstruction::IntegerSub { signed, left, right }).get()?; + + return res.as_int(); +} + +pub fn build_int_mul(ctx: &mut MIRContext, left: MIRIntValue, right: MIRIntValue, signed: bool) -> BaseResult { + if left.size != right.size { + return Err(BaseError::critical("Tried using imul on different sized integers".to_string())); + } + + let res = ctx.append_inst(MIRInstruction::IntegerMul { signed, left, right }).get()?; + + return res.as_int(); +} + +pub fn build_int_div(ctx: &mut MIRContext, left: MIRIntValue, right: MIRIntValue, signed: bool) -> BaseResult { + if left.size != right.size { + return Err(BaseError::critical("Tried using idiv on different sized integers".to_string())); + } + + let res = ctx.append_inst(MIRInstruction::IntegerDiv { signed, left, right }).get()?; + + return res.as_int(); +} + +pub fn build_int_neg(ctx: &mut MIRContext, val: MIRIntValue) -> BaseResult { + let res = ctx.append_inst(MIRInstruction::IntegerNeg { val }).get()?; + + return res.as_int(); +} + +pub fn build_float_add(ctx: &mut MIRContext, left: MIRFloatValue, right: MIRFloatValue, signed: bool) -> BaseResult { + if left.exponent != right.exponent || left.fraction != right.fraction { + return Err(BaseError::critical("Tried using fadd on different sized integers".to_string())); + } + + let res = ctx.append_inst(MIRInstruction::FloatAdd { signed, left, right }).get()?; + + return res.as_float(); +} + +pub fn build_float_sub(ctx: &mut MIRContext, left: MIRFloatValue, right: MIRFloatValue, signed: bool) -> BaseResult { + if left.exponent != right.exponent || left.fraction != right.fraction { + return Err(BaseError::critical("Tried using fsub on different sized integers".to_string())); + } + + let res = ctx.append_inst(MIRInstruction::FloatSub { signed, left, right }).get()?; + + return res.as_float(); +} + + +pub fn build_float_mul(ctx: &mut MIRContext, left: MIRFloatValue, right: MIRFloatValue, signed: bool) -> BaseResult { + if left.exponent != right.exponent || left.fraction != right.fraction { + return Err(BaseError::critical("Tried using fmul on different sized integers".to_string())); + } + + let res = ctx.append_inst(MIRInstruction::FloatMul { signed, left, right }).get()?; + + return res.as_float(); +} + + +pub fn build_float_div(ctx: &mut MIRContext, left: MIRFloatValue, right: MIRFloatValue, signed: bool) -> BaseResult { + if left.exponent != right.exponent || left.fraction != right.fraction { + return Err(BaseError::critical("Tried using fdiv on different sized integers".to_string())); + } + + let res = ctx.append_inst(MIRInstruction::FloatDiv { signed, left, right }).get()?; + + return res.as_float(); +} + + +pub fn build_float_neg(ctx: &mut MIRContext, val: MIRFloatValue) -> BaseResult { + let res = ctx.append_inst(MIRInstruction::FloatNeg { val }).get()?; + + return res.as_float(); +} + +pub fn build_bitwise_and(ctx: &mut MIRContext, a: MIRIntValue, b: MIRIntValue) -> BaseResult { + if a.size != 1 || b.size != 1 { + return Err(BaseError::critical("Tried using and on a non 1 bit value".to_string())); + } + + let res = ctx.append_inst(MIRInstruction::BitwiseAnd { a, b }).get()?; + + return res.as_int(); +} + +pub fn build_bitwise_or(ctx: &mut MIRContext, a: MIRIntValue, b: MIRIntValue) -> BaseResult { + if a.size != 1 || b.size != 1 { + return Err(BaseError::critical("Tried using or on a non 1 bit value".to_string())); + } + + let res = ctx.append_inst(MIRInstruction::BitwiseOr { a, b }).get()?; + + return res.as_int(); +} + +pub fn build_bitwise_xor(ctx: &mut MIRContext, a: MIRIntValue, b: MIRIntValue) -> BaseResult { + if a.size != 1 || b.size != 1 { + return Err(BaseError::critical("Tried using xor on a non 1 bit value".to_string())); + } + + let res = ctx.append_inst(MIRInstruction::BitwiseXor { a, b }).get()?; + + return res.as_int(); +} + +pub fn build_bitwise_not(ctx: &mut MIRContext, a: MIRIntValue) -> BaseResult { + if a.size != 1 { + return Err(BaseError::critical("Tried using not on a non 1 bit value".to_string())); + } + + let res = ctx.append_inst(MIRInstruction::BitwiseNot { val: a }).get()?; + + return res.as_int(); +} + +pub fn build_shift_left(ctx: &mut MIRContext, val: MIRIntValue, shift: MIRIntValue) -> BaseResult { + let res = ctx.append_inst(MIRInstruction::ShiftLeft { a: val, shift }).get()?; + + return res.as_int(); +} + +pub fn build_shift_right(ctx: &mut MIRContext, val: MIRIntValue, shift: MIRIntValue) -> BaseResult { + let res = ctx.append_inst(MIRInstruction::ShiftRight { a: val, shift }).get()?; + + return res.as_int(); +} + +pub fn build_comp_eq(ctx: &mut MIRContext, a: MIRIntValue, b: MIRIntValue) -> BaseResult { + if a.size != b.size { + return Err(BaseError::critical("Tried using cmpeq on different sized integers".to_string())); + } + + let res = ctx.append_inst(MIRInstruction::CompEq { a, b }).get()?; + + return res.as_int(); +} + +pub fn build_comp_neg(ctx: &mut MIRContext, a: MIRIntValue, b: MIRIntValue) -> BaseResult { + if a.size != b.size { + return Err(BaseError::critical("Tried using cmpneg on different sized integers".to_string())); + } + + let res = ctx.append_inst(MIRInstruction::CompNeg { a, b }).get()?; + + return res.as_int(); +} + +pub fn build_comp_lt(ctx: &mut MIRContext, a: MIRIntValue, b: MIRIntValue) -> BaseResult { + if a.size != b.size { + return Err(BaseError::critical("Tried using cmplt on different sized integers".to_string())); + } + + let res = ctx.append_inst(MIRInstruction::CompLt { a, b }).get()?; + + return res.as_int(); +} + +pub fn build_comp_le(ctx: &mut MIRContext, a: MIRIntValue, b: MIRIntValue) -> BaseResult { + if a.size != b.size { + return Err(BaseError::critical("Tried using cmple on different sized integers".to_string())); + } + + let res = ctx.append_inst(MIRInstruction::CompLe { a, b }).get()?; + + return res.as_int(); +} + +pub fn build_comp_gt(ctx: &mut MIRContext, a: MIRIntValue, b: MIRIntValue) -> BaseResult { + if a.size != b.size { + return Err(BaseError::critical("Tried using cmpgt on different sized integers".to_string())); + } + + let res = ctx.append_inst(MIRInstruction::CompGt { a, b }).get()?; + + return res.as_int(); +} + +pub fn build_comp_ge(ctx: &mut MIRContext, a: MIRIntValue, b: MIRIntValue) -> BaseResult { + if a.size != b.size { + return Err(BaseError::critical("Tried using cmpge on different sized integers".to_string())); + } + + let res = ctx.append_inst(MIRInstruction::CompGe { a, b }).get()?; + + return res.as_int(); +} + +pub fn build_return(ctx: &mut MIRContext, val: BaseMIRValue) -> BaseResult { + ctx.append_inst(MIRInstruction::Return { val }); + + Ok(true) +} + +pub fn build_unconditional_branch(ctx: &mut MIRContext, branch: MIRBlockReference) -> BaseResult { + ctx.append_inst(MIRInstruction::UnconditionalBranch { branch }); + Ok(true) +} + +pub fn build_conditional_branch(ctx: &mut MIRContext, condition: MIRIntValue, if_branch: MIRBlockReference, else_branch: MIRBlockReference) -> BaseResult { + if condition.size != 1 { + return Err(BaseError::err("Provided cond to build_conditional_branch is not a boolean".to_string())); + } + + ctx.append_inst(MIRInstruction::ConditionalBranch { cond: condition, if_branch, else_branch }); + Ok(true) +} + +pub fn build_select(ctx: &mut MIRContext, condition: MIRIntValue, if_val: BaseMIRValue, else_val: BaseMIRValue) -> BaseResult { + if condition.size != 1 { + return Err(BaseError::err("Provided cond to build_select is not a boolean".to_string())); + } + + if !if_val.vtype.base.is_equal(&else_val.vtype.base) { + return Err(BaseError::err("Both values do not have the same type in build_select!".to_string())) + } + + return ctx.append_inst(MIRInstruction::Select { cond: condition, if_val, else_val }).get() +} + +pub fn build_field_pointer(ctx: &mut MIRContext, ptr: MIRPointerValue, field: usize) -> BaseResult { + let val = ctx.append_inst(MIRInstruction::FieldPointer { val: ptr.clone(), field }).get()?; + let base: &BaseMIRValue = &ptr.into(); + + let pointer_hint = ctx.ssa_hints.get_hint(base.get_ssa_index())?.as_pointer()?; + let ptr_t = pointer_hint.base.get_struct_container()?; + + let t = CompactedType::from(ptr_t.fields.vals[field].clone()); + + let ind = ctx.ssa_hints.append_hint(MIRValueHint::Pointer(t)); + + if ind != val.get_ssa_index() { + return Err(BaseError::err("Couldn't hint SSA value for pointer! Indexes are different".to_string())) + } + + return val.as_ptr(); +} + +pub fn build_index_pointer(ctx: &mut MIRContext, val: MIRPointerValue, index: usize) -> BaseResult { + ctx.append_inst(MIRInstruction::IndexPointer { val, index }); + + Ok(true) +} + +pub fn build_marker_era_drop(ctx: &mut MIRContext, val: BaseMIRValue) -> BaseResult { + ctx.append_inst(MIRInstruction::MarkerEraDrop { value: val }); + + Ok(true) +} + +pub fn build_signed_int_const(ctx: &mut MIRContext, raw: i128, bitsize: usize) -> BaseResult { + let res = ctx.append_inst(MIRInstruction::IntegerSignedConstant { raw, bitsize }).get()?; + + return res.as_int(); +} + +pub fn build_unsigned_int_const(ctx: &mut MIRContext, raw: u128, bitsize: usize) -> BaseResult { + let res = ctx.append_inst(MIRInstruction::IntegerUnsignedConstant { raw, bitsize }).get()?; + + return res.as_int(); +} + +pub fn build_signed_float_const(ctx: &mut MIRContext, raw: f64, exponent: usize, fraction: usize) -> BaseResult { + let res = ctx.append_inst(MIRInstruction::FloatSignedConstant { raw, exponent, fraction }).get()?; + + return res.as_float(); +} + +pub fn build_unsigned_float_const(ctx: &mut MIRContext, raw: f64, exponent: usize, fraction: usize) -> BaseResult { + let res = ctx.append_inst(MIRInstruction::FloatUnsignedConstant { raw, exponent, fraction }).get()?; + + return res.as_float(); +} + +pub fn build_signed_fixed_const(ctx: &mut MIRContext, raw: f64, number: usize, fraction: usize) -> BaseResult { + let res = ctx.append_inst(MIRInstruction::FixedSignedConstant { raw, number, fraction }).get()?; + + return res.as_int(); +} + +pub fn build_unsigned_fixed_const(ctx: &mut MIRContext, raw: f64, number: usize, fraction: usize) -> BaseResult { + let res = ctx.append_inst(MIRInstruction::FixedUnsignedConstant { raw, number, fraction }).get()?; + + return res.as_int(); +} + +pub fn build_static_string_const(ctx: &mut MIRContext, raw: String) -> BaseResult { + let res = ctx.append_inst(MIRInstruction::StaticStringConstant { raw }).get()?; + + return res.as_ptr(); +} + +pub fn build_call(ctx: &mut MIRContext, func: usize, ind: usize, args: Vec) -> BaseResult { + let func = &ctx.functions[func]; + + for(arg, t) in args.iter().zip(func.arguments.iter()) { + if !arg.vtype.eq(t) { + return Err(BaseError::err(IR_FUNCTION_INVALID_ARGUMENTS!().to_string())); + } + } + + let res = ctx.append_inst(MIRInstruction::Call { function: ind, arguments: args }).get()?; + + return Ok(res); +} + +pub fn build_phi(ctx: &mut MIRContext, choices: Vec<(MIRBlockReference, BaseMIRValue)>) -> BaseResult { + let t = &choices[0].1.vtype; + + for choice in &choices { + if &choice.1.vtype != t { + return Err(BaseError::err("phi node must have same type values".to_string())); + } + } + + let res = ctx.append_inst(MIRInstruction::Phi { choices }).get()?; + + return Ok(res); +} \ No newline at end of file diff --git a/compiler/astoir_mir/src/ctx.rs b/compiler/astoir_mir/src/ctx.rs new file mode 100644 index 00000000..8b78ff06 --- /dev/null +++ b/compiler/astoir_mir/src/ctx.rs @@ -0,0 +1,119 @@ +use std::fmt::Display; + +use compiler_errors::errs::BaseResult; + +use crate::{blocks::{MIRBlock, hints::{HintStorage, MIRValueHint}, refer::MIRBlockReference}, builder::build_phi, funcs::MIRFunction, inst_writer::{BlockPosition, InstructionWriterPosition}, insts::{MIRInstruction, val::InstructionValue}, vals::{base::BaseMIRValue}}; + + +pub struct MIRContext { + pub functions: Vec, + pub blocks: Vec, + pub writer: InstructionWriterPosition, + + pub ssa_hints: HintStorage, +} + +impl MIRContext { + pub fn new() -> Self { + MIRContext { functions: vec![], ssa_hints: HintStorage::new(), blocks: vec![], writer: InstructionWriterPosition { curr_block: 0, curr_inst: BlockPosition::START } } + } + + pub fn create_block(&mut self) -> MIRBlockReference { + let ind = self.blocks.len(); + + self.blocks.push(MIRBlock::new(ind)); + + return ind; + } + + pub fn append_function(&mut self, func: MIRFunction) -> usize { + let ind = self.functions.len(); + + self.functions.push(func); + + return ind; + } + + pub fn append_inst(&mut self, inst: MIRInstruction) -> InstructionValue { + match self.writer.curr_inst { + BlockPosition::START => self.blocks[self.writer.curr_block].append_start(inst.clone()), + BlockPosition::END => self.blocks[self.writer.curr_block].append(inst.clone()) + }; + + if inst.has_return(self) { + let ret = inst.get_return_type(self); + + if !inst.should_hint() { + let hint_ind = self.ssa_hints.vec.len(); + + return InstructionValue::new(Some(BaseMIRValue::new(hint_ind, ret))) + } + + let hint_ind = self.ssa_hints.append_hint(MIRValueHint::Value(ret.clone())); + + return InstructionValue::new(Some(BaseMIRValue::new(hint_ind, ret))); + } + + return InstructionValue::new(None); + } + + /// Resolve the different SSA values for the given merge blocks + pub fn resolve_ssa(&mut self, block: MIRBlockReference) -> BaseResult { + let mut vals = vec![]; + + let b = &self.blocks[block]; + + for (ind, hint) in b.variables.iter() { + let mut choices: Vec<(MIRBlockReference, BaseMIRValue)> = vec![]; + let mut capture_initial_phi_val = false; + + for block_ref in &b.merge_blocks { + let block = &self.blocks[*block_ref]; + let block_hint = &block.variables[ind]; + + if hint == block_hint && !capture_initial_phi_val { + choices.push((*block_ref, block_hint.hint.clone().unwrap())); + capture_initial_phi_val = true; + } else if hint != block_hint && block_hint.hint.is_some() { + choices.push((*block_ref, block_hint.hint.clone().unwrap())); + } + } + + // Phi here only matters when there are 2+ choices. Else it's just the default + if choices.len() >= 2 { + vals.push((*ind, choices)); + } + } + + self.writer.move_end(block); + + for val in vals { + // Only update using PHI if needed and value is merged + if !val.1.is_empty() { + let res = build_phi(self, val.1)?; + + let mut hint = self.blocks[block].variables[&val.0].clone(); + hint.hint = Some(res); + + self.blocks[block].variables.insert(val.0, hint); + } + } + + return Ok(true); + } + +} + +impl Display for MIRContext { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + for func in &self.functions { + writeln!(f, "{}", func)?; + } + + for block in &self.blocks { + writeln!(f, "{}", block)?; + } + + Ok(()) + } +} \ No newline at end of file diff --git a/compiler/astoir_mir/src/funcs.rs b/compiler/astoir_mir/src/funcs.rs new file mode 100644 index 00000000..c3c811b4 --- /dev/null +++ b/compiler/astoir_mir/src/funcs.rs @@ -0,0 +1,62 @@ +use std::fmt::Display; + +use astoir_typing::compacted::CompactedType; +use compiler_errors::errs::{BaseResult, base::BaseError}; +use compiler_utils::hash::HashedString; + +use crate::{blocks::{refer::{MIRBlockReference}}, ctx::MIRContext}; + +/// Represents a function in the MIR. Owns one or more blocks +pub struct MIRFunction { + /// The block storage. index 0 is entry block + pub blocks: Vec, + pub name: HashedString, + + /// This will prevent the function from being usable by normal function calls if true + pub is_from_struct: bool, + + pub arguments: Vec, + pub return_type: Option +} + +impl MIRFunction { + pub fn new(name: String, arguments: Vec, return_type: Option, is_from_struct: bool) -> Self { + return MIRFunction { blocks: vec![], name: HashedString::new(name), arguments, return_type, is_from_struct } + } + + pub fn append_entry_block(&mut self, ctx: &mut MIRContext) -> BaseResult { + if !self.blocks.is_empty() { + return Err(BaseError::err("Tried using append_entry_block on non-empty function blocks!".to_string())) + } + + let reference = ctx.create_block(); + + self.blocks.push(reference); + + return Ok(reference); + } + + pub fn append_block(&mut self, ctx: &mut MIRContext) -> BaseResult { + if self.blocks.is_empty() { + return Err(BaseError::err("Tried using append_block on empty function blocks!".to_string())) + } + + let reference = ctx.create_block(); + + self.blocks.push(reference); + + return Ok(reference) + } +} + +impl Display for MIRFunction { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + writeln!(f, ".func_{}_struct{}", self.name.val, self.is_from_struct)?; + + for block in &self.blocks { + writeln!(f, "- block_{}", block)?; + } + + Ok(()) + } +} \ No newline at end of file diff --git a/compiler/astoir_mir/src/inst_writer.rs b/compiler/astoir_mir/src/inst_writer.rs new file mode 100644 index 00000000..4111a0ce --- /dev/null +++ b/compiler/astoir_mir/src/inst_writer.rs @@ -0,0 +1,27 @@ +//! A way to avoid Rust borrowing errors by delegating the Instruction appending logic directly to the MIRContext instead of the builder functions + +use crate::{blocks::refer::MIRBlockReference}; + +pub enum BlockPosition { + START, + END +} + +/// The main instruction. Contains the current position +pub struct InstructionWriterPosition { + pub curr_block: MIRBlockReference, + pub curr_inst: BlockPosition +} + +impl InstructionWriterPosition { + pub fn move_start(&mut self, block: MIRBlockReference) { + self.curr_block = block; + self.curr_inst = BlockPosition::START; + } + + pub fn move_end(&mut self, block: MIRBlockReference) { + self.curr_block = block; + + self.curr_inst = BlockPosition::END; + } +} diff --git a/compiler/astoir_mir/src/insts/mod.rs b/compiler/astoir_mir/src/insts/mod.rs new file mode 100644 index 00000000..28c5bce4 --- /dev/null +++ b/compiler/astoir_mir/src/insts/mod.rs @@ -0,0 +1,276 @@ +//! The definitions for instructions within the MIR. + +use std::fmt::Display; + +use astoir_typing::{base::BaseType, compacted::CompactedType}; + +use crate::{blocks::{refer::MIRBlockReference}, ctx::MIRContext, vals::{base::BaseMIRValue, float::MIRFloatValue, int::MIRIntValue, ptr::MIRPointerValue}}; + +pub mod val; + +/// An instruction inside of the MIR. +#[derive(Clone)] +pub enum MIRInstruction { + StackAlloc { alloc_size: usize, t: CompactedType }, + Load { value: MIRPointerValue }, + Store { variable: MIRPointerValue, value: BaseMIRValue }, + + // Number casting + DowncastInteger { val: MIRIntValue, size: usize }, // make size smaller + UpcastInteger { val: MIRIntValue, size: usize }, // make size bigger + + DowncastFloat { val: MIRFloatValue, exponent: usize, fraction: usize }, + UpcastFloat { val: MIRFloatValue, exponent: usize, fraction: usize }, + + // Arithmetrics + IntegerAdd { signed: bool, left: MIRIntValue, right: MIRIntValue }, + IntegerSub { signed: bool, left: MIRIntValue, right: MIRIntValue }, + IntegerMul { signed: bool, left: MIRIntValue, right: MIRIntValue }, + IntegerDiv { signed: bool, left: MIRIntValue, right: MIRIntValue }, + IntegerMod { signed: bool, left: MIRIntValue, right: MIRIntValue }, + IntegerNeg { val: MIRIntValue }, + + FloatAdd { signed: bool, left: MIRFloatValue, right: MIRFloatValue }, + FloatSub { signed: bool, left: MIRFloatValue, right: MIRFloatValue }, + FloatMul { signed: bool, left: MIRFloatValue, right: MIRFloatValue }, + FloatDiv { signed: bool, left: MIRFloatValue, right: MIRFloatValue }, + FloatNeg { val: MIRFloatValue }, + + // Bitwise (int typed) + BitwiseAnd { a: MIRIntValue, b: MIRIntValue }, + BitwiseOr { a: MIRIntValue, b: MIRIntValue }, + BitwiseXor { a: MIRIntValue, b: MIRIntValue }, + BitwiseNot { val: MIRIntValue }, + + ShiftLeft { a: MIRIntValue, shift: MIRIntValue }, + ShiftRight { a: MIRIntValue, shift: MIRIntValue }, + + // Comparaison / Logical + CompEq { a: MIRIntValue, b: MIRIntValue }, + CompNeg { a: MIRIntValue, b: MIRIntValue }, + CompLt { a: MIRIntValue, b: MIRIntValue}, // < + CompLe { a: MIRIntValue, b: MIRIntValue}, // <= + CompGt { a: MIRIntValue, b: MIRIntValue }, // > + CompGe { a: MIRIntValue, b: MIRIntValue}, // >= + + // Constants + IntegerSignedConstant { raw: i128, bitsize: usize }, + IntegerUnsignedConstant { raw: u128, bitsize: usize }, + FloatSignedConstant { raw: f64, exponent: usize, fraction: usize }, + FloatUnsignedConstant { raw: f64, exponent: usize, fraction: usize }, + FixedSignedConstant { raw: f64, number: usize, fraction: usize }, + FixedUnsignedConstant { raw: f64, number: usize, fraction: usize }, + StaticStringConstant { raw: String }, + + // Control + Return { val: BaseMIRValue }, + UnconditionalBranch { branch: MIRBlockReference }, + ConditionalBranch { cond: MIRIntValue, if_branch: MIRBlockReference, else_branch: MIRBlockReference }, + Phi { choices: Vec<(MIRBlockReference, BaseMIRValue)> }, + Select { cond: MIRIntValue, if_val: BaseMIRValue, else_val: BaseMIRValue }, + + Call { function: usize, arguments: Vec }, + + // Pointer utils + + FieldPointer { val: MIRPointerValue, field: usize }, + IndexPointer { val: MIRPointerValue, index: usize }, + PointerAdd { pointer: MIRPointerValue, right: MIRIntValue }, + PointerSub { pointer: MIRPointerValue, right: MIRIntValue }, + + /// Indicates to the IR processor that this given value's era is finished and thus we drop the value + MarkerEraDrop { value: BaseMIRValue }, +} + +impl MIRInstruction { + pub fn has_return(&self, ctx: &MIRContext) -> bool { + match self { + Self::MarkerEraDrop { .. } | Self::UnconditionalBranch { .. } | Self::ConditionalBranch { .. } | Self::Return { .. } => { + return false; + }, + + Self::Call { function, arguments: _ } => { + let func = &ctx.functions[*function]; + + return func.return_type.is_some(); + } + + _ => true + } + } + + pub fn should_hint(&self) -> bool { + return match self { + Self::StackAlloc { .. } => false, + Self::FieldPointer { .. } => false, + Self::IndexPointer { .. } => false, + + _ => true + } + } + + pub fn get_return_type(&self, ctx: &MIRContext) -> CompactedType { + match self { + Self::StackAlloc { .. } => return CompactedType::from(BaseType::Pointer), + Self::Load { value} => { + let base: BaseMIRValue = value.clone().into(); + + let hint = ctx.ssa_hints.get_hint(base.get_ssa_index()).unwrap(); + + return hint.as_pointer().unwrap(); + }, + + Self::DowncastInteger { val, size } => return CompactedType::from(BaseType::NumericIntegerType(*size as u64, val.signed)), + Self::UpcastInteger { val, size } => return CompactedType::from(BaseType::NumericIntegerType(*size as u64, val.signed)), + + Self::DowncastFloat { val, exponent, fraction } => return CompactedType::from(BaseType::FloatingNumberType(*exponent as u64, *fraction as u64, val.signed)), + Self::UpcastFloat { val, exponent, fraction } => return CompactedType::from(BaseType::FloatingNumberType(*exponent as u64, *fraction as u64, val.signed)), + + Self::IntegerAdd { signed, left, right: _ } => return CompactedType::from(BaseType::NumericIntegerType(left.size as u64, *signed)), + Self::IntegerSub { signed, left, right: _ } => return CompactedType::from(BaseType::NumericIntegerType(left.size as u64, *signed)), + Self::IntegerMul { signed, left, right: _ } => return CompactedType::from(BaseType::NumericIntegerType(left.size as u64, *signed)), + Self::IntegerDiv { signed, left, right: _ } => return CompactedType::from(BaseType::NumericIntegerType(left.size as u64, *signed)), + Self::IntegerMod { signed, left, right: _ } => return CompactedType::from(BaseType::NumericIntegerType(left.size as u64, *signed)), + Self::IntegerNeg { val } => return CompactedType::from(BaseType::NumericIntegerType(val.size as u64, true)), + + Self::FloatAdd { signed: _, left, right: _ } => return CompactedType::from(BaseType::FloatingNumberType(left.exponent as u64, left.fraction as u64, left.signed)), + Self::FloatSub { signed: _, left, right: _ } => return CompactedType::from(BaseType::FloatingNumberType(left.exponent as u64, left.fraction as u64, left.signed)), + Self::FloatMul { signed: _, left, right: _ } => return CompactedType::from(BaseType::FloatingNumberType(left.exponent as u64, left.fraction as u64, left.signed)), + Self::FloatDiv { signed: _, left, right: _ } => return CompactedType::from(BaseType::FloatingNumberType(left.exponent as u64, left.fraction as u64, left.signed)), + Self::FloatNeg { val } => return CompactedType::from(BaseType::FloatingNumberType(val.exponent as u64, val.fraction as u64, true)), + + Self::BitwiseAnd { a, b: _ } => return CompactedType::from(BaseType::NumericIntegerType(a.size as u64, a.signed)), + Self::BitwiseOr { a, b: _ } => return CompactedType::from(BaseType::NumericIntegerType(a.size as u64, a.signed)), + Self::BitwiseXor { a, b: _ } => return CompactedType::from(BaseType::NumericIntegerType(a.size as u64, a.signed)), + Self::BitwiseNot { val } => return CompactedType::from(BaseType::NumericIntegerType(val.size as u64, val.signed)), + + Self::ShiftLeft { a, shift: _ } => return CompactedType::from(BaseType::NumericIntegerType(a.size as u64, a.signed)), + Self::ShiftRight { a, shift: _ } => return CompactedType::from(BaseType::NumericIntegerType(a.size as u64, a.signed)), + + Self::CompEq { .. } => return CompactedType::from(BaseType::Boolean), + Self::CompNeg { .. } => return CompactedType::from(BaseType::Boolean), + Self::CompLt { .. } => return CompactedType::from(BaseType::Boolean), + Self::CompLe { .. } => return CompactedType::from(BaseType::Boolean), + Self::CompGt { .. } => return CompactedType::from(BaseType::Boolean), + Self::CompGe { .. } => return CompactedType::from(BaseType::Boolean), + + Self::IntegerSignedConstant { raw: _, bitsize } => return CompactedType::from(BaseType::NumericIntegerType(*bitsize as u64, true)), + Self::IntegerUnsignedConstant { raw: _, bitsize } => return CompactedType::from(BaseType::NumericIntegerType(*bitsize as u64, true)), + Self::FloatUnsignedConstant { raw: _, exponent, fraction } => return CompactedType::from(BaseType::FloatingNumberType(*exponent as u64, *fraction as u64, false)), + Self::FloatSignedConstant { raw: _, exponent, fraction } => return CompactedType::from(BaseType::FloatingNumberType(*exponent as u64, *fraction as u64, true)), + Self::FixedSignedConstant { raw: _, number, fraction } => return CompactedType::from(BaseType::NumericIntegerType(*number as u64 + *fraction as u64, true)), + Self::FixedUnsignedConstant { raw: _, number, fraction } => return CompactedType::from(BaseType::NumericIntegerType(*number as u64 + *fraction as u64, false)), + + Self::Phi { choices } => { + return choices[0].1.vtype.clone(); + }, + + Self::Select { cond: _, if_val, else_val: _ } => return if_val.vtype.clone(), + + Self::Call { function, arguments: _ } => { + let func = &ctx.functions[*function]; + + return func.return_type.clone().unwrap(); + } + + Self::FieldPointer { .. } => return CompactedType::from(BaseType::Pointer), + Self::IndexPointer { .. } => return CompactedType::from(BaseType::Pointer), + + Self::PointerAdd { .. } => return CompactedType::from(BaseType::Pointer), + Self::PointerSub { .. } => return CompactedType::from(BaseType::Pointer), + + _ => panic!("Tried using get_return_type on non returning type!") + } + } +} + +impl Display for MIRInstruction { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::StackAlloc { alloc_size, t } => writeln!(f, "stkalloc {}", *alloc_size)?, + Self::Load { value } => writeln!(f, "load {}", value)?, + Self::Store { variable, value } => writeln!(f, "store d{} s{}", variable, value)?, + + Self::DowncastInteger { val, size } => writeln!(f, "dintcast {} {}", val, size)?, + Self::DowncastFloat { val, exponent, fraction } => writeln!(f, "dfcast {} {} {}", val, exponent, fraction)?, + Self::UpcastInteger { val, size } => writeln!(f, "uintcast {} {}", val, size)?, + Self::UpcastFloat { val, exponent, fraction } => writeln!(f, "ufcast {} {} {}", val, exponent, fraction)?, + + Self::IntegerAdd { signed, left, right } => writeln!(f, "iadd s{} {} {}", signed, left, right)?, + Self::IntegerSub { signed, left, right } => writeln!(f, "isub s{} {} {}", signed, left, right)?, + Self::IntegerMul { signed, left, right } => writeln!(f, "imul s{} {} {}", signed, left, right)?, + Self::IntegerDiv { signed, left, right } => writeln!(f, "idiv s{} {} {}", signed, left, right)?, + Self::IntegerMod { signed, left, right } => writeln!(f, "imod s{} {} {}", signed, left, right)?, + Self::IntegerNeg { val } => writeln!(f, "ineg {}", val)?, + + Self::FloatAdd { signed, left, right } => writeln!(f, "fadd s{} {} {}", signed, left, right)?, + Self::FloatSub { signed, left, right } => writeln!(f, "fsub s{} {} {}", signed, left, right)?, + Self::FloatMul { signed, left, right } => writeln!(f, "fmul s{} {} {}", signed, left, right)?, + Self::FloatDiv { signed, left, right } => writeln!(f, "fdiv s{} {} {}", signed, left, right)?, + Self::FloatNeg { val } => writeln!(f, "fneg {}", val)?, + + Self::BitwiseAnd { a, b } => writeln!(f, "and {} {}", a, b)?, + Self::BitwiseOr { a, b } => writeln!(f, "or {} {}", a, b)?, + Self::BitwiseXor { a, b } => writeln!(f, "xor {} {}", a, b)?, + Self::BitwiseNot { val } => writeln!(f, "not {}", val)?, + + Self::ShiftLeft { a, shift } => writeln!(f, "shiftl {} {}", a, shift)?, + Self::ShiftRight { a, shift } => writeln!(f, "shiftr {} {}", a, shift)?, + + Self::CompEq { a, b } => writeln!(f, "eq {} {}", a, b)?, + Self::CompNeg { a, b } => writeln!(f, "ne {} {}", a, b)?, + Self::CompLt { a, b } => writeln!(f, "lt {} {}", a, b)?, + Self::CompLe { a, b } => writeln!(f, "le {} {}", a, b)?, + Self::CompGt { a, b } => writeln!(f, "gt {} {}", a, b)?, + Self::CompGe { a, b } => writeln!(f, "ge {} {}", a, b)?, + + Self::IntegerSignedConstant { raw, bitsize } => writeln!(f, "constints {} {}", raw, bitsize)?, + Self::IntegerUnsignedConstant { raw, bitsize } => writeln!(f, "constintu {} {}", raw, bitsize)?, + + Self::FloatSignedConstant { raw, exponent, fraction } => writeln!(f, "constfs {} {} {}", raw, exponent, fraction)?, + Self::FloatUnsignedConstant { raw, exponent, fraction } => writeln!(f, "constfu {} {} {}", raw, exponent, fraction)?, + + Self::FixedSignedConstant { raw, number, fraction } => writeln!(f, "constffs {} {} {}", raw, number, fraction)?, + Self::FixedUnsignedConstant { raw, number, fraction } => writeln!(f, "constffu {} {} {}", raw, number, fraction)?, + + Self::StaticStringConstant { raw } => writeln!(f, "conststr {}", raw)?, + + Self::Return { val } => writeln!(f, "ret {}", val)?, + + Self::UnconditionalBranch { branch } => writeln!(f, "ucondbr {}", branch)?, + Self::ConditionalBranch { cond, if_branch, else_branch } => writeln!(f, "condbr {} {} {}", cond, if_branch, else_branch)?, + + Self::Phi { choices } => { + write!(f, "phi")?; + + for choice in choices { + write!(f, " [b{}, {}]", choice.0, choice.1)?; + } + + write!(f, "\n")?; + }, + + Self::Select { cond, if_val, else_val } => writeln!(f, "select {} {} {}", cond, if_val, else_val)?, + + Self::Call { function, arguments } => { + write!(f, "call {}", function)?; + + for arg in arguments { + write!(f, " {}", arg)?; + } + + write!(f, "\n")?; + }, + + Self::FieldPointer { val, field } => writeln!(f, "fieldptr {} {}", val, field)?, + Self::IndexPointer { val, index } => writeln!(f, "indptr {} {}", val, index)?, + + Self::PointerAdd { pointer, right } => writeln!(f, "ptradd {} {}", pointer, right)?, + Self::PointerSub { pointer, right } => writeln!(f, "ptrsub {} {}", pointer, right)?, + + Self::MarkerEraDrop { value } => writeln!(f, ".marker_era_drop {}", value)? + } + + Ok(()) + } +} diff --git a/compiler/astoir_mir/src/insts/val.rs b/compiler/astoir_mir/src/insts/val.rs new file mode 100644 index 00000000..06a46665 --- /dev/null +++ b/compiler/astoir_mir/src/insts/val.rs @@ -0,0 +1,20 @@ +use compiler_errors::errs::{BaseResult, base::BaseError}; + +use crate::vals::base::BaseMIRValue; + +pub struct InstructionValue { + val: Option +} + +impl InstructionValue { + pub fn new(val: Option) -> Self { + return InstructionValue { val } + } + + pub fn get(self) -> BaseResult { + match self.val { + Some(v) => return Ok(v), + None => return Err(BaseError::err("Tried unpacking InstructionValue when contained null!".to_string())) + } + } +} \ No newline at end of file diff --git a/compiler/astoir_mir/src/lib.rs b/compiler/astoir_mir/src/lib.rs new file mode 100644 index 00000000..ebbe1c84 --- /dev/null +++ b/compiler/astoir_mir/src/lib.rs @@ -0,0 +1,10 @@ +//! The MIR layer of the AstoIR. +//! The MIR layer represents a block based representation of the program. Uses low level instructions near Assembly +//! +pub mod insts; +pub mod vals; +pub mod blocks; +pub mod builder; +pub mod funcs; +pub mod ctx; +pub mod inst_writer; \ No newline at end of file diff --git a/compiler/astoir_mir/src/vals/base.rs b/compiler/astoir_mir/src/vals/base.rs new file mode 100644 index 00000000..65942c08 --- /dev/null +++ b/compiler/astoir_mir/src/vals/base.rs @@ -0,0 +1,50 @@ +use std::fmt::Display; + +use astoir_typing::{compacted::CompactedType}; +use compiler_errors::errs::{BaseResult}; + +use crate::vals::{float::MIRFloatValue, int::MIRIntValue, ptr::MIRPointerValue}; + +/// Represents a basic value in the MIR. +#[derive(Clone, Debug)] +pub struct BaseMIRValue { + val_index: usize, + pub vtype: CompactedType +} + +impl BaseMIRValue { + #[deprecated(note = "This is meant for internal purposes, always use builders to safely create this!")] + pub fn new(val_index: usize, vtype: CompactedType) -> Self { + return BaseMIRValue { val_index, vtype } + } + + pub fn as_int(&self) -> BaseResult { + return Ok(MIRIntValue::new(self.clone())?); + } + + pub fn as_float(&self) -> BaseResult { + return Ok(MIRFloatValue::new(self.clone())?) + } + + pub fn as_ptr(&self) -> BaseResult { + return Ok(MIRPointerValue::new(self.clone())?) + } + + pub fn get_ssa_index(&self) -> usize { + return self.val_index; + } +} + +impl PartialEq for BaseMIRValue { + fn eq(&self, other: &Self) -> bool { + return self.val_index == other.val_index && self.vtype == other.vtype; + } +} + +impl Display for BaseMIRValue { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "#{}", self.val_index)?; + + Ok(()) + } +} \ No newline at end of file diff --git a/compiler/astoir_mir/src/vals/consts.rs b/compiler/astoir_mir/src/vals/consts.rs new file mode 100644 index 00000000..5bbf2c68 --- /dev/null +++ b/compiler/astoir_mir/src/vals/consts.rs @@ -0,0 +1,23 @@ +use compiler_errors::errs::{BaseResult, base::BaseError}; + +#[derive(Clone)] +pub enum MIRConstantValue { + Int(u128), + Float(f64) +} + +impl MIRConstantValue { + pub fn as_int(&self) -> BaseResult { + return match self { + MIRConstantValue::Int(v) => Ok(*v), + _ => Err(BaseError::err("Cannot use as_int on MIRConstantValue if it isn't int!".to_string())) + } + } + + pub fn as_float(&self) -> BaseResult { + return match self { + MIRConstantValue::Float(v) => Ok(*v), + _ => Err(BaseError::err("Cannot use as_float on MIRConstantValue if it isn't float!".to_string())) + } + } +} \ No newline at end of file diff --git a/compiler/astoir_mir/src/vals/float.rs b/compiler/astoir_mir/src/vals/float.rs new file mode 100644 index 00000000..8f9a2e95 --- /dev/null +++ b/compiler/astoir_mir/src/vals/float.rs @@ -0,0 +1,39 @@ +use std::fmt::Display; + +use compiler_errors::{IR_CASTING_ERROR, errs::{BaseResult, base::BaseError}}; + +use crate::vals::base::{BaseMIRValue}; + +#[derive(Clone)] +pub struct MIRFloatValue { + base: BaseMIRValue, + pub signed: bool, + pub exponent: usize, + pub fraction: usize, +} + +impl MIRFloatValue { + pub fn new(base: BaseMIRValue) -> BaseResult { + if base.vtype.base.is_floating() { + let sizes = base.vtype.base.get_floating_size()?; + + return Ok(MIRFloatValue { base: base.clone(), exponent: sizes.0, fraction: sizes.1, signed: base.vtype.base.is_signed() }) + } + + return Err(BaseError::critical(IR_CASTING_ERROR!().to_string())) + } +} + +impl Into for MIRFloatValue { + fn into(self) -> BaseMIRValue { + return self.base; + } +} + +impl Display for MIRFloatValue { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "#{}", self.base.get_ssa_index())?; + + Ok(()) + } +} \ No newline at end of file diff --git a/compiler/astoir_mir/src/vals/int.rs b/compiler/astoir_mir/src/vals/int.rs new file mode 100644 index 00000000..a5d9e7be --- /dev/null +++ b/compiler/astoir_mir/src/vals/int.rs @@ -0,0 +1,36 @@ +use std::fmt::Display; + +use compiler_errors::{IR_CASTING_ERROR, errs::{BaseResult, base::BaseError}}; + +use crate::vals::base::{BaseMIRValue}; + +#[derive(Clone)] +pub struct MIRIntValue { + pub base: BaseMIRValue, + pub signed: bool, + pub size: usize, +} + +impl MIRIntValue { + pub fn new(base: BaseMIRValue) -> BaseResult { + if base.vtype.base.is_integer() | base.vtype.base.is_bool() { + return Ok(MIRIntValue { base: base.clone(), size: base.vtype.base.get_size()?, signed: base.vtype.base.is_signed() }) + } + + return Err(BaseError::critical(IR_CASTING_ERROR!().to_string())) + } +} + +impl Into for MIRIntValue { + fn into(self) -> BaseMIRValue { + return self.base; + } +} + +impl Display for MIRIntValue { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "#{}", self.base.get_ssa_index())?; + + Ok(()) + } +} \ No newline at end of file diff --git a/compiler/astoir_mir/src/vals/mod.rs b/compiler/astoir_mir/src/vals/mod.rs new file mode 100644 index 00000000..fe0dc831 --- /dev/null +++ b/compiler/astoir_mir/src/vals/mod.rs @@ -0,0 +1,34 @@ +//! The definitions for every value kind in the MIR. + +use crate::{vals::{float::MIRFloatValue, int::MIRIntValue, ptr::MIRPointerValue}}; + +pub mod base; +pub mod int; +pub mod float; +pub mod ptr; +pub mod consts; +pub mod refer; + +pub enum MIRAnyValue { + Int(MIRIntValue), + Float(MIRFloatValue), + Ptr(MIRPointerValue) +} + +impl From for MIRAnyValue { + fn from(value: MIRIntValue) -> Self { + return MIRAnyValue::Int(value) + } +} + +impl From for MIRAnyValue { + fn from(value: MIRFloatValue) -> Self { + return MIRAnyValue::Float(value) + } +} + +impl From for MIRAnyValue { + fn from(value: MIRPointerValue) -> Self { + return MIRAnyValue::Ptr(value) + } +} \ No newline at end of file diff --git a/compiler/astoir_mir/src/vals/ptr.rs b/compiler/astoir_mir/src/vals/ptr.rs new file mode 100644 index 00000000..7f61ed0d --- /dev/null +++ b/compiler/astoir_mir/src/vals/ptr.rs @@ -0,0 +1,35 @@ +use std::fmt::Display; + +use astoir_typing::base::BaseType; +use compiler_errors::{IR_CASTING_ERROR, errs::{BaseResult, base::BaseError}}; + +use crate::vals::base::{BaseMIRValue}; + +#[derive(Clone)] +pub struct MIRPointerValue { + base: BaseMIRValue +} + +impl MIRPointerValue { + pub fn new(base: BaseMIRValue) -> BaseResult { + if let BaseType::Pointer = &base.vtype.base { + return Ok(MIRPointerValue { base: base.clone() }) + } + + return Err(BaseError::critical(IR_CASTING_ERROR!().to_string())) + } +} + +impl Into for MIRPointerValue { + fn into(self) -> BaseMIRValue { + return self.base; + } +} + +impl Display for MIRPointerValue { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "#{}", self.base.get_ssa_index())?; + + Ok(()) + } +} \ No newline at end of file diff --git a/compiler/astoir_mir/src/vals/refer.rs b/compiler/astoir_mir/src/vals/refer.rs new file mode 100644 index 00000000..c7c3c805 --- /dev/null +++ b/compiler/astoir_mir/src/vals/refer.rs @@ -0,0 +1,102 @@ +use compiler_errors::errs::{BaseResult, base::BaseError}; + +use crate::{blocks::{refer::MIRBlockReference}, builder::{build_load, build_store}, ctx::MIRContext, vals::{base::BaseMIRValue, ptr::MIRPointerValue}}; + +/// Represents a reference to a variable. This is not a reference to individual SSA values. +/// +/// # Variables +/// Variables can be either: +/// - A pointer (using `store` and `load`) +/// - SSA handled variable (stores variable ID -> current SSA value for the variable) +/// +/// # Finding Guarantee +/// This reference guarantees to point to valid variable within a function. Please note that a reference can **only** be used in the function it was created in. +/// No Safety has been put in place to limit these errors as these references should never leave their origin function anyways +/// +/// # Usage +/// This can be used to reference any true variable to modify it's content or read it. This will automatically handle diverse instructions needed such as `load` and `store`. +pub enum MIRVariableReference { + PointerReference(MIRPointerValue), + SSAReference(usize) +} + +impl MIRVariableReference { + pub fn read(&self, block: MIRBlockReference, ctx: &mut MIRContext) -> BaseResult { + if self.is_pointer_ref() { + let ptr_ref = self.as_pointer_ref()?; + + let res = build_load(ctx, ptr_ref)?; + + return Ok(res); + } + + let ind = self.as_ssa_ref()?; + + return match &ctx.blocks[block].variables[&ind].hint { + Some(v) => Ok(v.clone()), + None => Err(BaseError::err("Cannot unpack SSA reference for variable in MIRVariableReference::read".to_string())) + } + } + + pub fn write(&self, block: MIRBlockReference, ctx: &mut MIRContext, val: BaseMIRValue) -> BaseResult { + if self.is_pointer_ref() { + let ptr_ref = self.as_pointer_ref()?; + + build_store(ctx, ptr_ref, val)?; + + return Ok(true); + } + + let ind = self.as_ssa_ref()?; + + let block = &mut ctx.blocks[block]; + + if block.variables[&ind].hint.is_some() && block.variables[&ind].hint.clone().unwrap().vtype != val.vtype { + return Err(BaseError::err("Cannot write on this variable reference since the two types differ!".to_string())); + } + + let mut hint = block.variables[&ind].clone(); + hint.hint = Some(val); + + block.variables.insert(ind, hint); + + return Ok(true); + } + + pub fn is_pointer_ref(&self) -> bool { + return match self { + Self::PointerReference(_) => true, + _ => false + } + } + + pub fn as_pointer_ref(&self) -> BaseResult { + return match self { + Self::PointerReference(e) => Ok(e.clone()), + _ => Err(BaseError::err("as_pointer_ref requires a pointer var ref!".to_string())) + } + } + + pub fn as_ssa_ref(&self) -> BaseResult { + return match self { + Self::SSAReference(e) => Ok(*e), + _ => Err(BaseError::err("as_ssa_ref requires a SSA var ref!".to_string())) + } + } + + pub fn is_ssa_ref(&self) -> bool { + return !self.is_pointer_ref(); + } +} + +impl From for MIRVariableReference { + fn from(value: MIRPointerValue) -> Self { + return MIRVariableReference::PointerReference(value) + } +} + +impl From for MIRVariableReference { + fn from(value: usize) -> Self { + return MIRVariableReference::SSAReference(value) + } +} \ No newline at end of file diff --git a/compiler/astoir_mir_lowering/Cargo.toml b/compiler/astoir_mir_lowering/Cargo.toml new file mode 100644 index 00000000..3f90afa8 --- /dev/null +++ b/compiler/astoir_mir_lowering/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "astoir_mir_lowering" +version = "0.1.0" +edition = "2024" + +[dependencies] +astoir_mir = {path = "../astoir_mir"} +astoir_hir = {path = "../astoir_hir"} +compiler_errors = {path = "../compiler_errors"} +lexer = {path = "../lexer"} +astoir_typing = { path = "../astoir_typing"} \ No newline at end of file diff --git a/compiler/astoir_mir_lowering/src/body.rs b/compiler/astoir_mir_lowering/src/body.rs new file mode 100644 index 00000000..c61afc18 --- /dev/null +++ b/compiler/astoir_mir_lowering/src/body.rs @@ -0,0 +1,34 @@ +use astoir_hir::nodes::HIRNode; +use astoir_mir::blocks::{refer::MIRBlockReference}; +use compiler_errors::{IR_INVALID_NODE_TYPE, MATH_OP_NO_ASSIGN, errs::{BaseResult, base::BaseError}}; + +use crate::{MIRLoweringContext, control::{forloop::lower_hir_for_loop, ifstatement::lower_hir_if_statement}, math::lower_hir_math_operation, vars::{lower_hir_variable_assignment, lower_hir_variable_declaration}}; + +pub fn lower_hir_body_member(block: MIRBlockReference, node: Box, ctx: &mut MIRLoweringContext) -> BaseResult { + return match *node { + HIRNode::VarAssigment { .. } => lower_hir_variable_assignment(block, node, ctx), + HIRNode::VarDeclaration { .. } => lower_hir_variable_declaration(block, node, ctx), + HIRNode::MathOperation { left: _, right: _, operation: _, assignment } => { + if !assignment { + return Err(BaseError::err(MATH_OP_NO_ASSIGN!().to_string())) + } + + lower_hir_math_operation(block, node, ctx)?; + + return Ok(true); + }, + + HIRNode::ForBlock { .. } => lower_hir_for_loop(block, node, ctx), + HIRNode::IfStatement { .. } => lower_hir_if_statement(block, node, ctx), + + _ => return Err(BaseError::err(IR_INVALID_NODE_TYPE!().to_string())) + } +} + +pub fn lower_hir_body(block: MIRBlockReference, nodes: Vec>, ctx: &mut MIRLoweringContext) -> BaseResult { + for node in nodes { + lower_hir_body_member(block, node, ctx)?; + } + + return Ok(true); +} \ No newline at end of file diff --git a/compiler/astoir_mir_lowering/src/control/forloop.rs b/compiler/astoir_mir_lowering/src/control/forloop.rs new file mode 100644 index 00000000..a0debf06 --- /dev/null +++ b/compiler/astoir_mir_lowering/src/control/forloop.rs @@ -0,0 +1,43 @@ +//! Lowering for for loops + +use astoir_hir::nodes::HIRNode; +use astoir_mir::{blocks::{MIRBlock, refer::MIRBlockReference}, builder::{build_conditional_branch, build_unconditional_branch}}; +use compiler_errors::{IR_INVALID_NODE_TYPE, errs::{BaseResult, base::BaseError}}; + +use crate::{MIRLoweringContext, body::lower_hir_body, math::lower_hir_math_operation, values::lower_hir_value, vars::lower_hir_variable_declaration}; + +pub fn lower_hir_for_loop(block: MIRBlockReference, node: Box, ctx: &mut MIRLoweringContext) -> BaseResult { + if let HIRNode::ForBlock { initial_state, condition, incrementation, body } = *node { + let header_ref = MIRBlock::new_merge(block, &mut ctx.mir_ctx, false); + let cond_ref = MIRBlock::new_merge(header_ref, &mut ctx.mir_ctx, false); + let body_ref = MIRBlock::new_merge(header_ref, &mut ctx.mir_ctx, false); + let exit_ref = MIRBlock::new_merge(block, &mut ctx.mir_ctx, false); + + ctx.mir_ctx.blocks[header_ref].merge_blocks.push(block); + ctx.mir_ctx.blocks[header_ref].merge_blocks.push(body_ref); + + lower_hir_variable_declaration(block, initial_state, ctx)?; + + ctx.mir_ctx.writer.move_end(body_ref); + + lower_hir_body(body_ref, body, ctx)?; + lower_hir_math_operation(body_ref, incrementation, ctx)?; + + build_unconditional_branch(&mut ctx.mir_ctx, header_ref)?; + + ctx.mir_ctx.writer.move_end(header_ref); + + ctx.mir_ctx.resolve_ssa(header_ref)?; + build_unconditional_branch(&mut ctx.mir_ctx, cond_ref)?; + + ctx.mir_ctx.writer.move_end(cond_ref); + let cond_val = lower_hir_value(block, condition, ctx)?; + + build_conditional_branch(&mut ctx.mir_ctx, cond_val.as_int()?, body_ref, exit_ref)?; + + ctx.mir_ctx.writer.move_end(exit_ref); + + } + + return Err(BaseError::err(IR_INVALID_NODE_TYPE!().to_string())) +} \ No newline at end of file diff --git a/compiler/astoir_mir_lowering/src/control/ifstatement.rs b/compiler/astoir_mir_lowering/src/control/ifstatement.rs new file mode 100644 index 00000000..fab7729f --- /dev/null +++ b/compiler/astoir_mir_lowering/src/control/ifstatement.rs @@ -0,0 +1,105 @@ +use astoir_hir::{nodes::HIRNode, structs::HIRIfBranch}; +use astoir_mir::{blocks::{MIRBlock, refer::MIRBlockReference}, builder::{build_conditional_branch, build_unconditional_branch}}; +use compiler_errors::{IR_INVALID_NODE_TYPE, errs::{BaseResult, base::BaseError}}; + +use crate::{MIRLoweringContext, body::lower_hir_body, values::lower_hir_value}; + +pub fn lower_hir_if_statement(block: MIRBlockReference, node: Box, ctx: &mut MIRLoweringContext) -> BaseResult { + if let HIRNode::IfStatement { branches } = *node { + let merge_ref = MIRBlock::new_merge(block, &mut ctx.mir_ctx, false); + let mut branch_blocks = vec![]; + + ctx.mir_ctx.blocks[merge_ref].merge_blocks.push(block); + + for branch in &branches { + match branch { + &HIRIfBranch::IfBranch { .. } => { + let ifbranch_body = MIRBlock::new_merge(block, &mut ctx.mir_ctx, false); + + branch_blocks.push(ifbranch_body); + + ctx.mir_ctx.blocks[merge_ref].merge_blocks.push(ifbranch_body); + }, + + &HIRIfBranch::ElseIfBranch { .. } => { + let ifelsebranch_cond = MIRBlock::new_merge(block, &mut ctx.mir_ctx, false); + let ifelsebranch_body = MIRBlock::new_merge(block, &mut ctx.mir_ctx, false); + + branch_blocks.push(ifelsebranch_cond); + branch_blocks.push(ifelsebranch_body); + + ctx.mir_ctx.blocks[merge_ref].merge_blocks.push(ifelsebranch_body); + }, + + &HIRIfBranch::ElseBranch { .. } => { + let else_body = MIRBlock::new_merge(block, &mut ctx.mir_ctx, false); + + branch_blocks.push(else_body); + + ctx.mir_ctx.blocks[merge_ref].merge_blocks.push(else_body); + } + } + } + + branch_blocks.push(merge_ref); // Allows for array usage for branch descending + + + let mut branch_ind = 0; + + for branch in branches { + match branch { + HIRIfBranch::IfBranch { cond, body } => { + ctx.mir_ctx.writer.move_end(block); + + let val = lower_hir_value(block, cond, ctx)?.as_int()?; + + build_conditional_branch(&mut ctx.mir_ctx, val, branch_blocks[branch_ind], branch_blocks[branch_ind + 1])?; + + ctx.mir_ctx.writer.move_end(branch_blocks[branch_ind]); + + lower_hir_body(branch_blocks[branch_ind], body, ctx)?; + + build_unconditional_branch(&mut ctx.mir_ctx, merge_ref)?; + + branch_ind += 1; + }, + + HIRIfBranch::ElseIfBranch { cond, body } => { + ctx.mir_ctx.writer.move_end(branch_blocks[branch_ind]); + + let val = lower_hir_value(branch_blocks[branch_ind], cond, ctx)?.as_int()?; + + build_conditional_branch(&mut ctx.mir_ctx, val, branch_blocks[branch_ind + 1], branch_blocks[branch_ind + 2])?; + + branch_ind += 1; + ctx.mir_ctx.writer.move_end(branch_blocks[branch_ind]); + + lower_hir_body(branch_blocks[branch_ind], body, ctx)?; + + build_unconditional_branch(&mut ctx.mir_ctx, merge_ref)?; + + branch_ind += 1 + }, + + HIRIfBranch::ElseBranch { body } => { + ctx.mir_ctx.writer.move_end(branch_blocks[branch_ind]); + + lower_hir_body(branch_blocks[branch_ind], body, ctx)?; + + build_unconditional_branch(&mut ctx.mir_ctx, merge_ref)?; + + branch_ind += 1; + } + + } + } + + ctx.mir_ctx.writer.move_end(merge_ref); + ctx.mir_ctx.resolve_ssa(merge_ref)?; + + return Ok(true); + } + + return Err(BaseError::err(IR_INVALID_NODE_TYPE!().to_string())); +} + diff --git a/compiler/astoir_mir_lowering/src/control/mod.rs b/compiler/astoir_mir_lowering/src/control/mod.rs new file mode 100644 index 00000000..cbb0e5d9 --- /dev/null +++ b/compiler/astoir_mir_lowering/src/control/mod.rs @@ -0,0 +1,4 @@ +//! Lowering for control blocks such as for blocks, if statements and more! + +pub mod forloop; +pub mod ifstatement; \ No newline at end of file diff --git a/compiler/astoir_mir_lowering/src/funcs.rs b/compiler/astoir_mir_lowering/src/funcs.rs new file mode 100644 index 00000000..0d9a4ea2 --- /dev/null +++ b/compiler/astoir_mir_lowering/src/funcs.rs @@ -0,0 +1,84 @@ +use astoir_hir::{nodes::HIRNode}; +use astoir_mir::{blocks::refer::MIRBlockReference, builder::build_call, funcs::MIRFunction, vals::base::BaseMIRValue}; +use astoir_typing::compacted::CompactedType; +use compiler_errors::{IR_FUNCTION_INVALID_ARGUMENTS, IR_INVALID_NODE_TYPE, errs::{BaseResult, base::BaseError}}; + +use crate::{MIRLoweringContext, body::lower_hir_body, values::lower_hir_value}; + +pub fn lower_hir_function_decl(node: Box, cctx: &mut MIRLoweringContext) -> BaseResult { + if let HIRNode::FunctionDeclaration { func_name, arguments, return_type, body, ctx, requires_this } = *node { + let mut args = vec![]; + + for argument in arguments { + args.push(CompactedType::from(argument.1)); + } + + let ret_type; + + if return_type.is_some() { + ret_type = Some(CompactedType::from(return_type.unwrap())) + } else { + ret_type = None + } + + let mut func = MIRFunction::new(format!("func_{}", func_name), args, ret_type, requires_this); + let block =func.append_entry_block(&mut cctx.mir_ctx)?; + + lower_hir_body(block, body, cctx)?; + + cctx.mir_ctx.append_function(func); + return Ok(true) + } + + return Err(BaseError::err(IR_INVALID_NODE_TYPE!().to_string())) +} + + +pub fn lower_hir_shadow_decl(node: Box, ctx: &mut MIRLoweringContext) -> BaseResult { + if let HIRNode::ShadowFunctionDeclaration { func_name, arguments, return_type } = *node { + let mut args = vec![]; + + for argument in arguments { + args.push(CompactedType::from(argument.1)); + } + + let ret_type; + + if return_type.is_some() { + ret_type = Some(CompactedType::from(return_type.unwrap())) + } else { + ret_type = None + } + + let func = MIRFunction::new(format!("func_{}", func_name), args, ret_type, false); + + ctx.mir_ctx.append_function(func); + return Ok(true); + } + + return Err(BaseError::err(IR_INVALID_NODE_TYPE!().to_string())) +} + +pub fn lower_hir_function_call(block: MIRBlockReference, node: Box, ctx: &mut MIRLoweringContext) -> BaseResult { + if let HIRNode::FunctionCall { func_name, arguments } = *node { + let mut args = vec![]; + + let mut i = 0; + for arg in arguments { + let t = &ctx.mir_ctx.functions[func_name].arguments[i].clone(); + let mir_val = lower_hir_value(block, arg, ctx)?; + + if !mir_val.vtype.can_transmute(t) { + return Err(BaseError::err(IR_FUNCTION_INVALID_ARGUMENTS!().to_string())) + } + + args.push(mir_val); + + i += 1; + } + + return build_call(&mut ctx.mir_ctx, func_name, func_name, args); + } + + return Err(BaseError::err(IR_INVALID_NODE_TYPE!().to_string())) +} \ No newline at end of file diff --git a/compiler/astoir_mir_lowering/src/lib.rs b/compiler/astoir_mir_lowering/src/lib.rs new file mode 100644 index 00000000..185a201f --- /dev/null +++ b/compiler/astoir_mir_lowering/src/lib.rs @@ -0,0 +1,47 @@ +use astoir_hir::{ctx::HIRContext, nodes::HIRNode}; +use astoir_mir::ctx::MIRContext; +use compiler_errors::{AST_INVALID_TREE, errs::{BaseResult, IS_MIR_STAGE, base::BaseError}}; + +use crate::funcs::{lower_hir_function_decl, lower_hir_shadow_decl}; + +pub mod vars; +pub mod values; +pub mod math; +pub mod funcs; +pub mod body; +pub mod control; + +pub struct MIRLoweringContext { + pub hir_ctx: HIRContext, + pub mir_ctx: MIRContext +} + +pub fn lower_hir_top_level(node: Box, ctx: &mut MIRLoweringContext) -> BaseResult { + return match *node { + HIRNode::FunctionDeclaration { .. } => lower_hir_function_decl(node, ctx), + HIRNode::ShadowFunctionDeclaration { .. } => lower_hir_shadow_decl(node, ctx), + HIRNode::StructDeclaration { .. } => { + // Since Struct declarations are already fulled lowered in HIR, we do need handling here! + + return Ok(true); + }, + + _ => return Err(BaseError::err(AST_INVALID_TREE!().to_string())) + } +} + +pub fn lower_hir(ctx: HIRContext) -> BaseResult { + IS_MIR_STAGE.with_borrow_mut(|e| *e = true); + + let mut lowering_ctx = MIRLoweringContext { hir_ctx: ctx, mir_ctx: MIRContext::new() }; + + let declarations = lowering_ctx.hir_ctx.function_declarations.clone(); + + for decl in declarations { + if let Some(node) = decl { + lower_hir_top_level(node, &mut lowering_ctx)?; + } + } + + return Ok(lowering_ctx.mir_ctx); +} \ No newline at end of file diff --git a/compiler/astoir_mir_lowering/src/math.rs b/compiler/astoir_mir_lowering/src/math.rs new file mode 100644 index 00000000..bb5aeb02 --- /dev/null +++ b/compiler/astoir_mir_lowering/src/math.rs @@ -0,0 +1,76 @@ +use astoir_hir::{nodes::HIRNode}; +use astoir_mir::{blocks::{refer::MIRBlockReference}, builder::{build_float_add, build_float_div, build_float_mul, build_float_sub, build_int_add, build_int_div, build_int_mul, build_int_sub}, vals::base::BaseMIRValue}; +use astoir_typing::base::BaseType; +use compiler_errors::{IR_INVALID_NODE_TYPE, IR_REQ_VARIABLE_ASSIGN, errs::{BaseResult, base::BaseError}}; +use lexer::toks::math::MathOperator; + +use crate::{MIRLoweringContext, values::lower_hir_value, vars::lower_hir_variable_reference}; + +pub fn lower_hir_math_operation(block: MIRBlockReference, node: Box, ctx: &mut MIRLoweringContext) -> BaseResult { + if let HIRNode::MathOperation { left, right, operation, assignment } = *node { + if assignment && !left.is_variable_reference() { + return Err(BaseError::err(IR_REQ_VARIABLE_ASSIGN!().to_string())) + } + + let ptr; + + if assignment { + ptr = Some(lower_hir_variable_reference(block, &left, ctx)?); + } else { + ptr = None + } + + let left_val = lower_hir_value(block, left, ctx)?; + let right_val = lower_hir_value(block, right, ctx)?; + + + let val = match left_val.vtype.base { + BaseType::NumericIntegerType(_, _) => lower_hir_math_operation_int(left_val, right_val, operation, ctx)?, + BaseType::FloatingNumberType(_, _, _) => lower_hir_math_operation_float(left_val, right_val, operation, ctx)?, + + // TODO: see if fixed point are needed or do they automatically fallback to int + + _ => return Err(BaseError::err("Cannot use lower_hir_math_operation on this given value kind!".to_string())) + }; + + if assignment { + let v = ptr.unwrap(); + + v.write(block, &mut ctx.mir_ctx, val)?; + } + } + + return Err(BaseError::err(IR_INVALID_NODE_TYPE!().to_string())) +} + +pub fn lower_hir_math_operation_int(left: BaseMIRValue, right: BaseMIRValue, operator: MathOperator, ctx: &mut MIRLoweringContext) -> BaseResult { + let left = left.as_int()?; + let right = right.as_int()?; + + let signed = left.signed; + + let res = match operator { + MathOperator::ADD => build_int_add(&mut ctx.mir_ctx, left, right, signed)?, + MathOperator::SUBSTRACT => build_int_sub(&mut ctx.mir_ctx, left, right, signed)?, + MathOperator::MULTIPLY => build_int_mul(&mut ctx.mir_ctx, left, right, signed)?, + MathOperator::DIVIDE => build_int_div(&mut ctx.mir_ctx, left, right, signed)? + }; + + return Ok(res.into()); +} + +pub fn lower_hir_math_operation_float(left: BaseMIRValue, right: BaseMIRValue, operator: MathOperator, ctx: &mut MIRLoweringContext) -> BaseResult { + let left = left.as_float()?; + let right = right.as_float()?; + + let signed = left.signed; + + let res = match operator { + MathOperator::ADD => build_float_add(&mut ctx.mir_ctx, left, right, signed)?, + MathOperator::SUBSTRACT => build_float_sub(&mut ctx.mir_ctx, left, right, signed)?, + MathOperator::MULTIPLY => build_float_mul(&mut ctx.mir_ctx, left, right, signed)?, + MathOperator::DIVIDE => build_float_div(&mut ctx.mir_ctx, left, right, signed)? + }; + + return Ok(res.into()); +} \ No newline at end of file diff --git a/compiler/astoir_mir_lowering/src/values/booleans.rs b/compiler/astoir_mir_lowering/src/values/booleans.rs new file mode 100644 index 00000000..bfc59a06 --- /dev/null +++ b/compiler/astoir_mir_lowering/src/values/booleans.rs @@ -0,0 +1,40 @@ +use astoir_hir::{nodes::HIRNode}; +use astoir_mir::{blocks::{refer::MIRBlockReference}, builder::{build_bitwise_not, build_comp_eq, build_comp_ge, build_comp_gt, build_comp_le, build_comp_lt, build_comp_neg}, vals::int::MIRIntValue}; +use compiler_errors::{IR_INVALID_NODE_TYPE, errs::{BaseResult, base::BaseError}}; +use lexer::toks::comp::ComparingOperator; + +use crate::{MIRLoweringContext, values::lower_hir_value}; + +pub fn lower_hir_boolean_operator(block: MIRBlockReference, node: Box, ctx: &mut MIRLoweringContext) -> BaseResult { + if let HIRNode::BooleanOperator { left, right, operator } = *node { + let a = lower_hir_value(block, left, ctx)?.as_int()?; + let b = lower_hir_value(block, right, ctx)?.as_int()?; + + let val = match operator { + ComparingOperator::Equal => build_comp_eq(&mut ctx.mir_ctx, a, b)?, + ComparingOperator::NotEqual => build_comp_neg(&mut ctx.mir_ctx, a, b)?, + ComparingOperator::Lower => build_comp_lt(&mut ctx.mir_ctx, a, b)?, + ComparingOperator::LowerEqual => build_comp_le(&mut ctx.mir_ctx, a, b)?, + ComparingOperator::Higher => build_comp_gt(&mut ctx.mir_ctx, a, b)?, + ComparingOperator::HigherEqual => build_comp_ge(&mut ctx.mir_ctx, a, b)? + }; + + return Ok(val); + } + + return Err(BaseError::err(IR_INVALID_NODE_TYPE!().to_string())) +} + +pub fn lowering_hir_boolean_condition(block: MIRBlockReference, node: Box, ctx: &mut MIRLoweringContext) -> BaseResult { + if let HIRNode::BooleanCondition { value, negation } = *node { + let mut val = lower_hir_value(block, value, ctx)?.as_int()?; + + if negation { + val = build_bitwise_not(&mut ctx.mir_ctx, val)?; + } + + return Ok(val); + } + + return Err(BaseError::err(IR_INVALID_NODE_TYPE!().to_string())) +} \ No newline at end of file diff --git a/compiler/astoir_mir_lowering/src/values/consts.rs b/compiler/astoir_mir_lowering/src/values/consts.rs new file mode 100644 index 00000000..1d4e633f --- /dev/null +++ b/compiler/astoir_mir_lowering/src/values/consts.rs @@ -0,0 +1,31 @@ +use astoir_hir::{nodes::HIRNode}; +use astoir_mir::{builder::{build_signed_int_const, build_static_string_const, build_unsigned_int_const}, vals::base::BaseMIRValue}; +use compiler_errors::{IR_INVALID_NODE_TYPE, errs::{BaseResult, base::BaseError}}; + +use crate::MIRLoweringContext; + +pub fn lower_hir_literal(node: Box, ctx: &mut MIRLoweringContext) -> BaseResult { + match *node { + HIRNode::IntegerLiteral { value, int_type } => { + let t = &ctx.hir_ctx.type_storage.types[int_type]; + + if t.is_signed() { + let val = build_signed_int_const(&mut ctx.mir_ctx, value, t.get_size()?)?; + + return Ok(val.into()); + } + + let val = build_unsigned_int_const(&mut ctx.mir_ctx, value as u128, t.get_size()?)?; + + return Ok(val.into()); + }, + + HIRNode::StringLiteral { value } => { + let val = build_static_string_const(&mut ctx.mir_ctx, value)?; + + return Ok(val.into()); + }, + + _ => return Err(BaseError::err(IR_INVALID_NODE_TYPE!().to_string())) + } +} \ No newline at end of file diff --git a/compiler/astoir_mir_lowering/src/values/mod.rs b/compiler/astoir_mir_lowering/src/values/mod.rs new file mode 100644 index 00000000..b7817f22 --- /dev/null +++ b/compiler/astoir_mir_lowering/src/values/mod.rs @@ -0,0 +1,20 @@ +use astoir_hir::{nodes::HIRNode}; +use astoir_mir::{blocks::{refer::MIRBlockReference}, vals::base::BaseMIRValue}; +use compiler_errors::{IR_INVALID_NODE_TYPE, errs::{BaseResult, base::BaseError}}; + +use crate::{MIRLoweringContext, math::lower_hir_math_operation, values::{booleans::{lower_hir_boolean_operator, lowering_hir_boolean_condition}, consts::lower_hir_literal}, vars::lower_hir_variable_reference_value}; + +pub mod consts; +pub mod booleans; + +pub fn lower_hir_value(block: MIRBlockReference, node: Box, ctx: &mut MIRLoweringContext) -> BaseResult { + match *node { + HIRNode::IntegerLiteral { .. } | HIRNode::StringLiteral { .. } => return lower_hir_literal(node, ctx), + HIRNode::VariableReference { .. } => return lower_hir_variable_reference_value(block, node, ctx), + HIRNode::BooleanCondition { .. } => return Ok(lowering_hir_boolean_condition(block, node, ctx)?.into()), + HIRNode::BooleanOperator { .. } => return Ok(lower_hir_boolean_operator(block, node, ctx)?.into()), + HIRNode::MathOperation { .. } => return Ok(lower_hir_math_operation(block, node, ctx)?), + + _ => return Err(BaseError::err(IR_INVALID_NODE_TYPE!().to_string())) + } +} \ No newline at end of file diff --git a/compiler/astoir_mir_lowering/src/vars.rs b/compiler/astoir_mir_lowering/src/vars.rs new file mode 100644 index 00000000..719bc9f2 --- /dev/null +++ b/compiler/astoir_mir_lowering/src/vars.rs @@ -0,0 +1,64 @@ +//! Variable related lowering + +use astoir_hir::{nodes::HIRNode}; +use astoir_mir::{blocks::{MIRBlockVariableSSAHint, MIRBlockVariableType, refer::MIRBlockReference}, vals::{base::BaseMIRValue, refer::MIRVariableReference}}; +use compiler_errors::{IR_INVALID_NODE_TYPE, errs::{BaseResult, base::BaseError}}; + +use crate::{MIRLoweringContext, values::lower_hir_value}; + +pub fn lower_hir_variable_declaration(block_id: MIRBlockReference, node: Box, ctx: &mut MIRLoweringContext) -> BaseResult { + if let HIRNode::VarDeclaration { variable, var_type: _, default_val } = *node { + //let lowered = CompactedType::from(var_type); + + if default_val.is_some() { + let val = lower_hir_value(block_id, default_val.unwrap(), ctx)?; + + ctx.mir_ctx.blocks[block_id].variables.insert(variable, MIRBlockVariableSSAHint { kind: MIRBlockVariableType::SSA, hint: Some(val) }); + } else { + ctx.mir_ctx.blocks[block_id].variables.insert(variable, MIRBlockVariableSSAHint { kind: MIRBlockVariableType::SSA, hint: None }); + } + + //let ptr = build_stack_alloc(&mut ctx.mir_ctx, lowered.base.get_size()?, lowered)?; + + //ctx.mir_ctx.blocks[block_id].variables.insert(variable, MIRBlockVariableSSAHint { kind: MIRBlockVariableType::Pointer, hint: Some(ptr.clone().into()) }); + + //if default_val.is_some() { + // let val = lower_hir_value(block_id, default_val.unwrap(), ctx)?; + // + // build_store(&mut ctx.mir_ctx, ptr.clone(), val)?; + //} + + return Ok(true) + } + + return Err(BaseError::err(IR_INVALID_NODE_TYPE!().to_string())) +} + +pub fn lower_hir_variable_reference(block: MIRBlockReference, node: &Box, ctx: &MIRLoweringContext) -> BaseResult { + if let HIRNode::VariableReference { index, is_static: _ } = &**node { // TODO: add support for static variables + return ctx.mir_ctx.blocks[block].get_variable_ref(*index) + } + + return Err(BaseError::err(IR_INVALID_NODE_TYPE!().to_string())) +} + + +/// Lowers the HIR variable reference as if to obtain it's value. Requires a load +pub fn lower_hir_variable_reference_value(block: MIRBlockReference, node: Box, ctx: &mut MIRLoweringContext) -> BaseResult { + let ptr = lower_hir_variable_reference(block, &node, ctx)?; + + return Ok(ptr.read(block, &mut ctx.mir_ctx)?); +} + +pub fn lower_hir_variable_assignment(block: MIRBlockReference, node: Box, ctx: &mut MIRLoweringContext) -> BaseResult { + if let HIRNode::VarAssigment { variable, val } = *node { + let val = lower_hir_value(block, val, ctx)?; + + let variable_ref = ctx.mir_ctx.blocks[block].get_variable_ref(variable)?; + + variable_ref.write(block, &mut ctx.mir_ctx, val)?; + return Ok(true); + } + + return Err(BaseError::err(IR_INVALID_NODE_TYPE!().to_string())) +} \ No newline at end of file diff --git a/compiler/astoir_typing/src/base.rs b/compiler/astoir_typing/src/base.rs index 878bb795..01e6901a 100644 --- a/compiler/astoir_typing/src/base.rs +++ b/compiler/astoir_typing/src/base.rs @@ -86,6 +86,14 @@ impl BaseType { } } + pub fn get_floating_size(&self) -> BaseResult<(usize, usize)> { + return match self { + BaseType::FloatingNumberType(a, b, _) => Ok((*a as usize, *b as usize)), + + _ => Err(BaseError::err("Invalid get_floating_size!".to_string())) + } + } + pub fn is_floating(&self) -> bool { return match self { BaseType::FixedPointNumberType(_, _,_ ) => true, @@ -105,6 +113,13 @@ impl BaseType { } } + pub fn is_bool(&self) -> bool { + return match self { + BaseType::Boolean => true, + _ => false + } + } + pub fn is_incomplete(&self) -> bool { return match self { BaseType::IncompleteArbitraryType => true, @@ -116,6 +131,19 @@ impl BaseType { } } + pub fn is_signed(&self) -> bool { + return match self { + BaseType::NumericIntegerType(_, signed) => *signed, + BaseType::FloatingNumberType(_, _, signed) => *signed, + BaseType::FixedPointNumberType(_, _, signed) => *signed, + BaseType::IncompleteNumericType(signed) => *signed, + BaseType::IncompleteFloatingType(signed) => *signed, + BaseType::IncompleteFixedPointType(signed) => *signed, + + _ => false + } + } + pub fn can_transmute_into(&self, into: &BaseType) -> bool { if self.is_number() { if self.is_floating() != into.is_floating() && !into.is_floating() { @@ -136,4 +164,19 @@ impl BaseType { return false; } + pub fn is_equal(&self, t: &BaseType) -> bool { + return match (self, t) { + (BaseType::NumericIntegerType(size, signed), BaseType::NumericIntegerType(a, b)) => *size == *a && *signed == *b, + (BaseType::FloatingNumberType(exponent, fraction, signed), BaseType::FloatingNumberType(a, b, c)) => *exponent == *a && *fraction == *b && *signed == *c, + (BaseType::FixedPointNumberType(number, fraction, signed), BaseType::FixedPointNumberType(a, b, c)) => *number == *a && *fraction == *b && *signed == *c, + (BaseType::Boolean, BaseType::Boolean) => true, + (BaseType::ArbitraryType(size), BaseType::ArbitraryType(a)) => *size == *a, + (BaseType::Pointer, BaseType::Pointer) => true, + (BaseType::StaticStr, BaseType::StaticStr) => true, + (BaseType::Struct(layout, container), BaseType::Struct(a, b)) => *layout == *a && container.ind == b.ind, + + _ => false + } + } + } \ No newline at end of file diff --git a/compiler/astoir_typing/src/compacted.rs b/compiler/astoir_typing/src/compacted.rs new file mode 100644 index 00000000..d0c4939b --- /dev/null +++ b/compiler/astoir_typing/src/compacted.rs @@ -0,0 +1,38 @@ +//! Definitions for types that are compacted. This means every type after the HIR + +use crate::{base::BaseType, complete::ComplexType}; + +#[derive(Clone, Debug)] +pub struct CompactedType { + pub base: BaseType, + pub array: bool, + pub pointer: bool, + pub pointer_array: bool +} + +impl PartialEq for CompactedType { + fn eq(&self, other: &Self) -> bool { + return self.base.is_equal(&other.base) && self.array == other.array && self.pointer == other.pointer && self.pointer_array == other.pointer_array; + } +} + +impl CompactedType { + pub fn can_transmute(&self, other: &CompactedType) -> bool { + return self.base.can_transmute_into(&other.base) && self.array == other.array && self.pointer == other.pointer && self.pointer_array == other.pointer_array; + } +} + +impl From for CompactedType { + fn from(value: ComplexType) -> Self { + let array = value.is_array(); + let concrete = value.get_concrete(); + + return CompactedType { base: concrete.base.clone(), array, pointer: concrete.pointer, pointer_array: concrete.pointer_array } + } +} + +impl From for CompactedType { + fn from(value: BaseType) -> Self { + return CompactedType { base: value, array: false, pointer: false, pointer_array: false } + } +} diff --git a/compiler/astoir_typing/src/lib.rs b/compiler/astoir_typing/src/lib.rs index 5543d782..c9c5e988 100644 --- a/compiler/astoir_typing/src/lib.rs +++ b/compiler/astoir_typing/src/lib.rs @@ -4,4 +4,5 @@ pub mod base; pub mod complete; pub mod storage; pub mod hashes; -pub mod structs; \ No newline at end of file +pub mod structs; +pub mod compacted; \ No newline at end of file diff --git a/compiler/astoir_typing/src/structs.rs b/compiler/astoir_typing/src/structs.rs index 3652a819..d8d452aa 100644 --- a/compiler/astoir_typing/src/structs.rs +++ b/compiler/astoir_typing/src/structs.rs @@ -5,13 +5,14 @@ use crate::complete::ComplexType; #[derive(Clone, Debug)] pub struct StructTypeContainer { + pub ind: usize, pub fields: IndexStorage, pub functions: IndexStorage<(Option, Vec<(u64, ComplexType)>)> } impl StructTypeContainer { - pub fn new() -> Self { - return StructTypeContainer { fields: IndexStorage::new(), functions: IndexStorage::new() } + pub fn new(ind: usize) -> Self { + return StructTypeContainer { fields: IndexStorage::new(), functions: IndexStorage::new(), ind } } pub fn get_field(&self, hash: u64) -> BaseResult { diff --git a/compiler/compiler_errors/src/errors.rs b/compiler/compiler_errors/src/errors.rs index 80cac06b..ecad30d1 100644 --- a/compiler/compiler_errors/src/errors.rs +++ b/compiler/compiler_errors/src/errors.rs @@ -214,6 +214,13 @@ macro_rules! IR_REQ_VARIABLE_ASSIGN { }; } +#[macro_export] +macro_rules! VARIABLE_REQ_VALUE { + () => { + "The variable doesn't have any value here! Every variable must have a value at every point where it is used." + }; +} + #[macro_export] macro_rules! IR_VALUE_TYPE_TRANSMUTE { () => { @@ -332,4 +339,11 @@ macro_rules! IR_INCOMPLETE_TYPE { () => { "Tried obtaining the size of an incomplete type\nPlease send a bug report at https://github.com/quickfall/quickfall" }; +} + +#[macro_export] +macro_rules! IR_CASTING_ERROR { + () => { + "Cannot cast IR raw value to the given type\nPlease send a bug report at https://github.com/quickfall/quickfall" + }; } \ No newline at end of file diff --git a/compiler/compiler_errors/src/errs/base.rs b/compiler/compiler_errors/src/errs/base.rs index 097e2938..20fd4426 100644 --- a/compiler/compiler_errors/src/errs/base.rs +++ b/compiler/compiler_errors/src/errs/base.rs @@ -1,6 +1,6 @@ //! Base-error related declarations -use crate::errs::{ErrorKind, normal::CompilerError}; +use crate::errs::{ErrorKind, IS_MIR_STAGE, normal::CompilerError}; /// Base errors are errors originating from the IR. They do not contain positions or other things #[derive(Clone, Debug)] @@ -10,19 +10,27 @@ pub struct BaseError { } impl BaseError { + pub fn new(kind: ErrorKind, str: String) -> Self { + let base = BaseError { kind, str }; + + IS_MIR_STAGE.with(|e| { + if *e.borrow() { + CompilerError::from_base_posless(base.clone()); + } + }); + + return base; + } + pub fn err(str: String) -> Self { - return BaseError { kind: ErrorKind::Error, str } + return BaseError::new(ErrorKind::Error, str); } pub fn warn(str: String) -> Self { - return BaseError { kind: ErrorKind::Warn, str } + return BaseError::new(ErrorKind::Warn, str); } pub fn critical(str: String) -> Self { - let e = BaseError { kind: ErrorKind::Critical, str }; - - CompilerError::from_base_posless(e.clone()); - - return e; + return BaseError::new(ErrorKind::Critical, str); } } \ No newline at end of file diff --git a/compiler/compiler_errors/src/errs/mod.rs b/compiler/compiler_errors/src/errs/mod.rs index 18a89fca..7b913254 100644 --- a/compiler/compiler_errors/src/errs/mod.rs +++ b/compiler/compiler_errors/src/errs/mod.rs @@ -10,6 +10,7 @@ pub type CompilerResult = Result; thread_local! { static ERR_STORAGE: RefCell = RefCell::new(ErrorStorage { errs: Vec::new() }); + pub static IS_MIR_STAGE: RefCell = RefCell::new(false); } #[derive(Clone, Debug)] @@ -25,6 +26,7 @@ pub struct ErrorStorage { pub fn dump_errors() { ERR_STORAGE.with_borrow(|f| { + for err in &f.errs { println!("{}", err.err); diff --git a/compiler/compiler_errors/src/errs/normal.rs b/compiler/compiler_errors/src/errs/normal.rs index e7f0a848..0bea40e9 100644 --- a/compiler/compiler_errors/src/errs/normal.rs +++ b/compiler/compiler_errors/src/errs/normal.rs @@ -98,7 +98,8 @@ impl fmt::Display for CompilerError { let before = &start_line[0..self.pos.as_ref().unwrap().start.col]; let target = self.pos.as_ref().unwrap().get_bound().cyan().underline(); - let after = &end_line[self.pos.as_ref().unwrap().end.col..]; + + let after = &end_line[self.pos.as_ref().unwrap().end.col.min(end_line.len() - 1)..]; writeln!(f, "{}{}{}", before, target, after)?; } diff --git a/compiler/compiler_main/src/cmds/astoir.rs b/compiler/compiler_main/src/cmds/astoir.rs index 56c49563..721575be 100644 --- a/compiler/compiler_main/src/cmds/astoir.rs +++ b/compiler/compiler_main/src/cmds/astoir.rs @@ -1,7 +1,7 @@ use std::fs; use ast_parser::parse_ast_ctx; -use astoir::{IRLevel, run_astoir_hir}; +use astoir::{IRLevel, run_astoir_hir, run_astoir_mir}; use compiler_errors::errs::{BaseResult, base::BaseError, dump_errors}; use lexer::lexer::lexer_parse_file; @@ -18,16 +18,27 @@ pub fn parse_astoir_command(arguments: Vec) { for i in 3..arguments.len() { let lexer = lexer_parse_file(&arguments[i]).unwrap(); - let ast = parse_ast_ctx(&lexer).unwrap(); + let ast = parse_ast_ctx(&lexer); + + dump_errors(); match level { IRLevel::HIR => { - let ctx = run_astoir_hir(ast).unwrap(); + let ctx = run_astoir_hir(ast.unwrap()); let res_path = arguments[i].clone() + ".qfhir"; dump_errors(); - fs::write(res_path, format!("{:#?}", ctx)).unwrap() + fs::write(res_path, format!("{:#?}", ctx.unwrap())).unwrap() + }, + + IRLevel::MIR => { + let ctx = run_astoir_mir(ast.unwrap()); + let res_path = arguments[i].clone() + ".qfmir"; + + dump_errors(); + + fs::write(res_path, format!("{}", ctx.unwrap())).unwrap() } } } @@ -37,6 +48,7 @@ pub fn parse_astoir_command(arguments: Vec) { fn parse_astoir_level(str: &String) -> BaseResult { match str as &str { "hir" | "HIR" | "h" | "H" => return Ok(IRLevel::HIR), + "mir" | "MIR" | "m" | "M" => return Ok(IRLevel::MIR), _ => return Err(BaseError::critical("Cannot parse AstoIR level".to_string())) }; diff --git a/compiler/compiler_utils/src/hash.rs b/compiler/compiler_utils/src/hash.rs index 7b9441ad..d300365d 100644 --- a/compiler/compiler_utils/src/hash.rs +++ b/compiler/compiler_utils/src/hash.rs @@ -18,7 +18,7 @@ impl HashedString { } } -#[derive(Eq, PartialEq, Debug)] +#[derive(Eq, Clone, PartialEq, Debug)] pub struct SelfHash { pub hash: u64 } diff --git a/compiler/lexer/src/lexer.rs b/compiler/lexer/src/lexer.rs index 5721a716..0b2ba142 100644 --- a/compiler/lexer/src/lexer.rs +++ b/compiler/lexer/src/lexer.rs @@ -24,6 +24,7 @@ const ELSE_KEYWORD_HASH: u64 = hash!("else"); const WHILE_KEYWORD_HASH: u64 = hash!("while"); const FOR_KEYWORD_HASH: u64 = hash!("for"); const STATIC_KEYWORD_HASH: u64 = hash!("static"); +const THIS_KEYWORD_HASH: u64 = hash!("this"); /// Parses a file into a set of lexer tokens. /// @@ -302,6 +303,7 @@ fn parse_keyword(str: &String, ind: &mut usize, start_pos: Position) -> LexerTok WHILE_KEYWORD_HASH => LexerTokenType::While, FOR_KEYWORD_HASH => LexerTokenType::For, STATIC_KEYWORD_HASH => LexerTokenType::Static, + THIS_KEYWORD_HASH => LexerTokenType::This, _ => LexerTokenType::KEYWORD(slice.to_string(), hash) }; diff --git a/compiler/lexer/src/token.rs b/compiler/lexer/src/token.rs index 3bcfa6ef..85d1aeee 100644 --- a/compiler/lexer/src/token.rs +++ b/compiler/lexer/src/token.rs @@ -20,6 +20,8 @@ pub enum LexerTokenType { Layout, Lay, + This, + Static, /// 0: the operator diff --git a/examples/test.qf b/examples/test.qf deleted file mode 100644 index 777141bc..00000000 --- a/examples/test.qf +++ /dev/null @@ -1,13 +0,0 @@ -func main(si32 myTestArg, si32 age) { - var si32 test = test.abc().test; - - var si32 test2 = test.abs() >= test.def(); - - if(test2 >= test) { - var si32 age = 0; - } else if(test2 == test) { - var si32 age = 63; - } else { - var si32 age = 68; - } -} \ No newline at end of file