use std::{collections::HashMap, mem}; use reid_lib::{ builder::{InstructionValue, TypeValue}, compile::CompiledModule, Block, CmpPredicate, ConstValue, Context, CustomTypeKind, Function, FunctionFlags, Instr, Module, NamedStruct, TerminatorKind as Term, Type, }; use crate::mir::{ self, types::ReturnType, IndexedVariableReference, NamedVariableRef, StructField, StructType, TypeDefinitionKind, TypeKind, }; /// Context that contains all of the given modules as complete codegenerated /// LLIR that can then be finally compiled into LLVM IR. #[derive(Debug)] pub struct CodegenContext<'ctx> { context: &'ctx Context, } impl<'ctx> CodegenContext<'ctx> { /// Compile contained LLIR into LLVM IR and produce `hello.o` and /// `hello.asm` pub fn compile(&self) -> CompiledModule { self.context.compile() } } impl mir::Context { /// Compile MIR [`Context`] into [`CodegenContext`] containing LLIR. pub fn codegen<'ctx>(&self, context: &'ctx Context) -> CodegenContext<'ctx> { let mut modules = Vec::new(); for module in &self.modules { modules.push(module.codegen(context)); } CodegenContext { context } } } struct ModuleCodegen<'ctx> { module: Module<'ctx>, } impl<'ctx> std::fmt::Debug for ModuleCodegen<'ctx> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Debug::fmt(&self.module.as_printable(), f) } } impl mir::Module { fn codegen<'ctx>(&self, context: &'ctx Context) -> ModuleCodegen<'ctx> { let mut module = context.module(&self.name, self.is_main); let mut types = HashMap::new(); let mut type_values = HashMap::new(); for typedef in &self.typedefs { let type_value = match &typedef.kind { TypeDefinitionKind::Struct(StructType(fields)) => { module.custom_type(CustomTypeKind::NamedStruct(NamedStruct( typedef.name.clone(), fields .iter() // TODO: Reorder custom-type definitions such that // inner types get evaluated first. Otherwise this // will cause a panic! .map(|StructField(_, t, _)| t.get_type(&type_values, &types)) .collect(), ))) } }; types.insert(type_value, typedef.kind.clone()); type_values.insert(typedef.name.clone(), type_value); } let mut functions = HashMap::new(); for function in &self.functions { let param_types: Vec = function .parameters .iter() .map(|(_, p)| p.get_type(&type_values, &types)) .collect(); let is_main = self.is_main && function.name == "main"; let func = match &function.kind { mir::FunctionDefinitionKind::Local(_, _) => module.function( &function.name, function.return_type.get_type(&type_values, &types), param_types, FunctionFlags { is_pub: function.is_pub || is_main, is_main, is_imported: function.is_imported, ..FunctionFlags::default() }, ), mir::FunctionDefinitionKind::Extern(imported) => module.function( &function.name, function.return_type.get_type(&type_values, &types), param_types, FunctionFlags { is_extern: true, is_imported: *imported, ..FunctionFlags::default() }, ), }; functions.insert(function.name.clone(), func); } for mir_function in &self.functions { let function = functions.get(&mir_function.name).unwrap(); let mut entry = function.block("entry"); let mut stack_values = HashMap::new(); for (i, (p_name, p_ty)) in mir_function.parameters.iter().enumerate() { stack_values.insert( p_name.clone(), StackValue( StackValueKind::Immutable(entry.build(Instr::Param(i)).unwrap()), p_ty.get_type(&type_values, &types), ), ); } let mut scope = Scope { context, module: &module, function, block: entry, functions: &functions, types: &types, type_values: &type_values, stack_values, }; match &mir_function.kind { mir::FunctionDefinitionKind::Local(block, _) => { if let Some(ret) = block.codegen(&mut scope) { scope.block.terminate(Term::Ret(ret)).unwrap(); } else { if !scope.block.delete_if_unused().unwrap() { // Add a void return just in case if the block // wasn't unused but didn't have a terminator yet scope.block.terminate(Term::RetVoid).ok(); } } } mir::FunctionDefinitionKind::Extern(_) => {} } } ModuleCodegen { module } } } pub struct Scope<'ctx, 'a> { context: &'ctx Context, module: &'ctx Module<'ctx>, function: &'ctx Function<'ctx>, block: Block<'ctx>, types: &'a HashMap, type_values: &'a HashMap, functions: &'a HashMap>, stack_values: HashMap, } #[derive(Debug, Clone, PartialEq, Eq)] pub struct StackValue(StackValueKind, Type); #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum StackValueKind { Immutable(InstructionValue), Mutable(InstructionValue), } impl<'ctx, 'a> Scope<'ctx, 'a> { fn with_block(&self, block: Block<'ctx>) -> Scope<'ctx, 'a> { Scope { block, function: self.function, context: self.context, module: self.module, functions: self.functions, types: self.types, type_values: self.type_values, stack_values: self.stack_values.clone(), } } /// Takes the block out from this scope, swaps the given block in it's place /// and returns the old block. fn swap_block(&mut self, block: Block<'ctx>) -> Block<'ctx> { let mut old_block = block; mem::swap(&mut self.block, &mut old_block); old_block } fn get_typedef(&self, name: &String) -> Option<&TypeDefinitionKind> { self.type_values.get(name).and_then(|v| self.types.get(v)) } } impl mir::Statement { fn codegen<'ctx, 'a>(&self, scope: &mut Scope<'ctx, 'a>) -> Option { match &self.0 { mir::StmtKind::Let(NamedVariableRef(ty, name, _), mutable, expression) => { let value = expression.codegen(scope).unwrap(); scope.stack_values.insert( name.clone(), StackValue( match mutable { false => StackValueKind::Immutable(value), true => match ty { // Struct is already allocated at initialization TypeKind::Array(_, _) => StackValueKind::Mutable(value), TypeKind::CustomType(n) => match scope .types .get(scope.type_values.get(n).unwrap()) .unwrap() { // Struct also is allocated at initialization TypeDefinitionKind::Struct(_) => StackValueKind::Mutable(value), }, _ => StackValueKind::Mutable({ let alloca = scope .block .build(Instr::Alloca( name.clone(), ty.get_type(scope.type_values, scope.types), )) .unwrap(); scope.block.build(Instr::Store(alloca, value)).unwrap(); alloca }), }, }, ty.get_type(scope.type_values, scope.types), ), ); None } mir::StmtKind::Set(var, val) => { if let Some((StackValue(kind, mut ty), indices)) = var.get_stack_value(scope) { match kind { StackValueKind::Immutable(_) => { panic!("Tried to mutate an immutable variable") } StackValueKind::Mutable(mut ptr) => { for (i, idx) in indices.iter().enumerate() { let Type::Ptr(inner) = ty else { panic!() }; ty = *inner; ptr = scope .block .build(Instr::GetElemPtr(ptr, vec![*idx])) .unwrap(); if i < (indices.len() - 1) { ptr = scope.block.build(Instr::Load(ptr, ty.clone())).unwrap() } } let expression = val.codegen(scope).unwrap(); Some(scope.block.build(Instr::Store(ptr, expression)).unwrap()) } } } else { panic!("") } } // mir::StmtKind::If(if_expression) => if_expression.codegen(scope), mir::StmtKind::Import(_) => todo!(), mir::StmtKind::Expression(expression) => expression.codegen(scope), } } } impl mir::IfExpression { fn codegen<'ctx, 'a>(&self, scope: &mut Scope<'ctx, 'a>) -> Option { let condition = self.0.codegen(scope).unwrap(); // Create blocks let then_b = scope.function.block("then"); let mut else_b = scope.function.block("else"); let after_b = scope.function.block("after"); // Store for convenience let then_bb = then_b.value(); let else_bb = else_b.value(); let after_bb = after_b.value(); // Generate then-block content let mut then_scope = scope.with_block(then_b); let then_res = self.1.codegen(&mut then_scope); then_scope.block.terminate(Term::Br(after_bb)).ok(); let else_res = if let Some(else_block) = &self.2 { let mut else_scope = scope.with_block(else_b); scope .block .terminate(Term::CondBr(condition, then_bb, else_bb)) .unwrap(); let opt = else_block.codegen(&mut else_scope); if let Some(ret) = opt { else_scope.block.terminate(Term::Br(after_bb)).ok(); Some(ret) } else { None } } else { else_b.terminate(Term::Br(after_bb)).unwrap(); scope .block .terminate(Term::CondBr(condition, then_bb, after_bb)) .unwrap(); None }; // Swap block to the after-block so that construction can continue correctly scope.swap_block(after_b); if then_res.is_none() && else_res.is_none() { None } else { let mut incoming = Vec::from(then_res.as_slice()); incoming.extend(else_res); Some(scope.block.build(Instr::Phi(incoming)).unwrap()) } } } impl mir::Expression { fn codegen<'ctx, 'a>(&self, scope: &mut Scope<'ctx, 'a>) -> Option { match &self.0 { mir::ExprKind::Variable(varref) => { varref.0.known().expect("variable type unknown"); let v = scope .stack_values .get(&varref.1) .expect("Variable reference not found?!"); Some(match v.0 { StackValueKind::Immutable(val) => val.clone(), StackValueKind::Mutable(val) => match v.1 { // TODO probably wrong ..? Type::Ptr(_) => val, _ => scope.block.build(Instr::Load(val, v.1.clone())).unwrap(), }, }) } mir::ExprKind::Literal(lit) => Some(lit.as_const(&mut scope.block)), mir::ExprKind::BinOp(binop, lhs_exp, rhs_exp) => { lhs_exp .return_type() .expect("No ret type in lhs?") .1 .known() .expect("lhs ret type is unknown"); rhs_exp .return_type() .expect("No ret type in rhs?") .1 .known() .expect("rhs ret type is unknown"); let lhs = lhs_exp.codegen(scope).expect("lhs has no return value"); let rhs = rhs_exp.codegen(scope).expect("rhs has no return value"); Some(match binop { mir::BinaryOperator::Add => scope.block.build(Instr::Add(lhs, rhs)).unwrap(), mir::BinaryOperator::Minus => scope.block.build(Instr::Sub(lhs, rhs)).unwrap(), mir::BinaryOperator::Mult => scope.block.build(Instr::Mult(lhs, rhs)).unwrap(), mir::BinaryOperator::And => scope.block.build(Instr::And(lhs, rhs)).unwrap(), mir::BinaryOperator::Cmp(l) => scope .block .build(Instr::ICmp(l.int_predicate(), lhs, rhs)) .unwrap(), }) } mir::ExprKind::FunctionCall(call) => { call.return_type .known() .expect("function return type unknown"); let params = call .parameters .iter() .map(|e| e.codegen(scope).unwrap()) .collect(); let callee = scope .functions .get(&call.name) .expect("function not found!"); Some( scope .block .build(Instr::FunctionCall(callee.value(), params)) .unwrap(), ) } mir::ExprKind::If(if_expression) => if_expression.codegen(scope), mir::ExprKind::Block(block) => { let mut inner_scope = scope.with_block(scope.function.block("inner")); if let Some(ret) = block.codegen(&mut inner_scope) { inner_scope .block .terminate(Term::Br(scope.block.value())) .unwrap(); Some(ret) } else { None } } mir::ExprKind::ArrayIndex(expression, val_t, idx) => { let array = expression.codegen(scope)?; let ptr = scope .block .build(Instr::GetElemPtr(array, vec![*idx as u32])) .unwrap(); Some( scope .block .build(Instr::Load( ptr, val_t.get_type(scope.type_values, scope.types), )) .unwrap(), ) } mir::ExprKind::Array(expressions) => { let instr_list = expressions .iter() .map(|e| e.codegen(scope).unwrap()) .collect::>(); let instr_t = expressions .iter() .map(|e| e.return_type().unwrap().1) .next() .unwrap_or(TypeKind::Void); let array = scope .block .build(Instr::ArrayAlloca( instr_t.get_type(scope.type_values, scope.types), instr_list.len() as u32, )) .unwrap(); for (i, instr) in instr_list.iter().enumerate() { let ptr = scope .block .build(Instr::GetElemPtr(array, vec![i as u32])) .unwrap(); scope.block.build(Instr::Store(ptr, *instr)).unwrap(); } Some(array) } mir::ExprKind::StructIndex(expression, type_kind, field) => { let struct_val = expression.codegen(scope)?; let struct_ty = expression.return_type().ok()?.1.known().ok()?; let TypeKind::CustomType(name) = struct_ty else { return None; }; let TypeDefinitionKind::Struct(struct_ty) = scope.get_typedef(&name)?; let idx = struct_ty.find_index(field)?; let ptr = scope .block .build(Instr::GetStructElemPtr(struct_val, idx as u32)) .unwrap(); dbg!(&type_kind.get_type(scope.type_values, scope.types)); Some( scope .block .build(Instr::Load( ptr, type_kind.get_type(scope.type_values, scope.types), )) .unwrap(), ) } mir::ExprKind::Struct(name, items) => { let struct_ptr = scope .block .build(Instr::Alloca( name.clone(), Type::CustomType(*scope.type_values.get(name)?), )) .unwrap(); for (i, (_, exp)) in items.iter().enumerate() { let elem_ptr = scope .block .build(Instr::GetStructElemPtr(struct_ptr, i as u32)) .unwrap(); if let Some(val) = exp.codegen(scope) { scope.block.build(Instr::Store(elem_ptr, val)).unwrap(); } } Some(struct_ptr) } } } } impl IndexedVariableReference { fn get_stack_value(&self, scope: &mut Scope) -> Option<(StackValue, Vec)> { match &self.kind { mir::IndexedVariableReferenceKind::Named(NamedVariableRef(_, name, _)) => scope .stack_values .get(name) .cloned() .map(|v| (v, Vec::new())), mir::IndexedVariableReferenceKind::ArrayIndex(inner, idx) => { let (inner_val, mut indices) = inner.get_stack_value(scope)?; match &inner_val.1 { Type::Ptr(_) => { indices.push(*idx as u32); Some((inner_val, indices)) } _ => panic!("Tried to codegen indexing a non-indexable value!"), } } mir::IndexedVariableReferenceKind::StructIndex(inner, field) => { let (inner_val, mut indices) = inner.get_stack_value(scope)?; let (idx, elem_ty) = if let Type::Ptr(inner_ty) = inner_val.1 { if let Type::CustomType(ty_val) = *inner_ty { match scope.types.get(&ty_val).unwrap() { TypeDefinitionKind::Struct(struct_type) => Some(( struct_type.find_index(field)?, struct_type.get_field_ty(field)?, )), } } else { None } } else { None }?; indices.push(idx as u32); Some(( StackValue( inner_val.0, Type::Ptr(Box::new(elem_ty.get_type(scope.type_values, scope.types))), ), indices, )) } } } } impl mir::CmpOperator { fn int_predicate(&self) -> CmpPredicate { match self { mir::CmpOperator::LT => CmpPredicate::LT, mir::CmpOperator::GT => CmpPredicate::GT, mir::CmpOperator::LE => CmpPredicate::LE, mir::CmpOperator::GE => CmpPredicate::GE, mir::CmpOperator::EQ => CmpPredicate::EQ, mir::CmpOperator::NE => CmpPredicate::NE, } } } impl mir::Block { fn codegen<'ctx, 'a>(&self, mut scope: &mut Scope<'ctx, 'a>) -> Option { for stmt in &self.statements { stmt.codegen(&mut scope); } if let Some((kind, expr)) = &self.return_expression { if let Some(ret) = expr.codegen(&mut scope) { match kind { mir::ReturnKind::Hard => { scope.block.terminate(Term::Ret(ret)).unwrap(); None } mir::ReturnKind::Soft => Some(ret), } } else { None } } else { None } } } impl mir::Literal { fn as_const(&self, block: &mut Block) -> InstructionValue { block.build(self.as_const_kind()).unwrap() } fn as_const_kind(&self) -> Instr { Instr::Constant(match self.clone() { mir::Literal::I8(val) => ConstValue::I8(val), mir::Literal::I16(val) => ConstValue::I16(val), mir::Literal::I32(val) => ConstValue::I32(val), mir::Literal::I64(val) => ConstValue::I64(val), mir::Literal::I128(val) => ConstValue::I128(val), mir::Literal::U8(val) => ConstValue::U8(val), mir::Literal::U16(val) => ConstValue::U16(val), mir::Literal::U32(val) => ConstValue::U32(val), mir::Literal::U64(val) => ConstValue::U64(val), mir::Literal::U128(val) => ConstValue::U128(val), mir::Literal::Bool(val) => ConstValue::Bool(val), mir::Literal::String(val) => ConstValue::StringPtr(val.clone()), mir::Literal::Vague(_) => panic!("Got vague literal!"), }) } } impl TypeKind { fn get_type( &self, type_vals: &HashMap, typedefs: &HashMap, ) -> Type { match &self { TypeKind::I8 => Type::I8, TypeKind::I16 => Type::I16, TypeKind::I32 => Type::I32, TypeKind::I64 => Type::I64, TypeKind::I128 => Type::I128, TypeKind::U8 => Type::U8, TypeKind::U16 => Type::U16, TypeKind::U32 => Type::U32, TypeKind::U64 => Type::U64, TypeKind::U128 => Type::U128, TypeKind::Bool => Type::Bool, TypeKind::StringPtr => Type::Ptr(Box::new(Type::I8)), TypeKind::Array(elem_t, _) => Type::Ptr(Box::new(elem_t.get_type(type_vals, typedefs))), TypeKind::Void => Type::Void, TypeKind::Vague(_) => panic!("Tried to compile a vague type!"), TypeKind::CustomType(n) => { let type_val = type_vals.get(n).unwrap().clone(); let custom_t = Type::CustomType(type_val); match typedefs.get(&type_val).unwrap() { TypeDefinitionKind::Struct(_) => Type::Ptr(Box::new(custom_t)), } } } } }