diff --git a/src/irgen/mod.rs b/src/irgen/mod.rs index 3ab0d04..4b6244b 100644 --- a/src/irgen/mod.rs +++ b/src/irgen/mod.rs @@ -38,8 +38,10 @@ use core::cmp::Ordering; use core::convert::TryFrom; use core::{fmt, mem}; use std::collections::{BTreeMap, HashMap}; +use std::fmt::Binary; use std::ops::Deref; +use ir::BlockExit; use itertools::izip; use lang_c::ast::*; use lang_c::driver::Parse; @@ -590,7 +592,921 @@ impl IrgenFunc<'_> { bid_continue: Option, bid_break: Option, ) -> Result<(), IrgenError> { - todo!() + match stmt { + Statement::Labeled(stmt) => Err(IrgenError::new( + stmt.node.label.write_string(), + IrgenErrorMessage::Misc { + message: "label statement not within a switch".to_string(), + }, + )), + Statement::Compound(items) => { + self.enter_scope(); + + for item in items { + match &item.node { + BlockItem::Declaration(decl) => self + .translate_decl(&decl.node, context) + .map_err(|e| IrgenError::new(decl.write_string(), e))?, + BlockItem::StaticAssert(_) => { + panic!("BlockItem::StaticAssert is unsupported") + } + BlockItem::Statement(stmt) => { + self.translate_stmt(&stmt.node, context, bid_continue, bid_break)? + } + } + } + + self.exit_scope(); + Ok(()) + } + Statement::Expression(expr) => { + if let Some(expr) = expr { + let _unused = self + .translate_expr_rvalue(&expr.node, context) + .map_err(|e| IrgenError::new(expr.write_string(), e))?; + } + Ok(()) + } + Statement::If(stmt) => { + let stmt = &stmt.node; + + let then_bid = self.alloc_bid(); + let else_bid = self.alloc_bid(); + let end_bid = self.alloc_bid(); + self.translate_condition( + &stmt.condition.node, + mem::replace(context, Context::new(end_bid)), + then_bid, + else_bid, + ) + .map_err(|e| IrgenError::new(stmt.condition.write_string(), e))?; + + let mut then_context = Context::new(then_bid); + self.translate_stmt( + &stmt.then_statement.node, + &mut then_context, + bid_continue, + bid_break, + )?; + self.insert_block( + then_context, + ir::BlockExit::Jump { + arg: ir::JumpArg::new(end_bid, Vec::new()), + }, + ); + + let mut else_context = Context::new(else_bid); + if let Some(else_stmt) = &stmt.else_statement { + self.translate_stmt( + &else_stmt.node, + &mut else_context, + bid_continue, + bid_break, + )?; + } + self.insert_block( + else_context, + ir::BlockExit::Jump { + arg: ir::JumpArg::new(end_bid, Vec::new()), + }, + ); + + Ok(()) + } + Statement::Switch(stmt) => { + let stmt = &stmt.node; + let value = self + .translate_expr_rvalue(&stmt.expression.node, context) + .map_err(|e| IrgenError::new(stmt.expression.write_string(), e))?; + + let end_bid = self.alloc_bid(); + let (cases, default_bid) = + self.translate_switch_body(&stmt.statement.node, end_bid)?; + + self.insert_block( + mem::replace(context, Context::new(end_bid)), + ir::BlockExit::Switch { + value, + default: ir::JumpArg::new(default_bid, Vec::new()), + cases, + }, + ); + Ok(()) + } + Statement::While(stmt) => { + let stmt = &stmt.node; + + let cond_bid = self.alloc_bid(); + self.insert_block( + mem::replace(context, Context::new(cond_bid)), + ir::BlockExit::Jump { + arg: ir::JumpArg::new(cond_bid, Vec::new()), + }, + ); + + let body_bid = self.alloc_bid(); + let end_bid = self.alloc_bid(); + self.translate_condition( + &stmt.expression.node, + mem::replace(context, Context::new(end_bid)), + body_bid, + end_bid, + ); + + self.enter_scope(); + + let mut body_context = Context::new(body_bid); + self.translate_stmt( + &stmt.statement.node, + &mut body_context, + Some(cond_bid), + Some(end_bid), + )?; + self.insert_block( + body_context, + ir::BlockExit::Jump { + arg: ir::JumpArg::new(cond_bid, Vec::new()), + }, + ); + + self.exit_scope(); + Ok(()) + } + Statement::DoWhile(stmt) => { + let stmt = &stmt.node; + + let body_bid = self.alloc_bid(); + self.insert_block( + mem::replace(context, Context::new(body_bid)), + ir::BlockExit::Jump { + arg: ir::JumpArg::new(body_bid, Vec::new()), + }, + ); + + self.enter_scope(); + + let cond_bid = self.alloc_bid(); + let end_bid = self.alloc_bid(); + self.translate_stmt(&stmt.statement.node, context, Some(cond_bid), Some(end_bid))?; + + self.exit_scope(); + + self.insert_block( + mem::replace(context, Context::new(cond_bid)), + ir::BlockExit::Jump { + arg: ir::JumpArg::new(cond_bid, Vec::new()), + }, + ); + self.translate_condition( + &stmt.expression.node, + mem::replace(context, Context::new(end_bid)), + body_bid, + end_bid, + ) + .map_err(|e| IrgenError::new(stmt.expression.write_string(), e))?; + + Ok(()) + } + Statement::For(stmt) => { + let stmt = &stmt.node; + + let init_bid = self.alloc_bid(); + self.insert_block( + mem::replace(context, Context::new(init_bid)), + ir::BlockExit::Jump { + arg: ir::JumpArg::new(init_bid, Vec::new()), + }, + ); + + self.enter_scope(); + + self.translate_for_initializer(&stmt.initializer.node, context) + .map_err(|e| IrgenError::new(stmt.initializer.write_string(), e))?; + + let cond_bid = self.alloc_bid(); + self.insert_block( + mem::replace(context, Context::new(cond_bid)), + ir::BlockExit::Jump { + arg: ir::JumpArg::new(cond_bid, Vec::new()), + }, + ); + + let body_bid = self.alloc_bid(); + let step_bid = self.alloc_bid(); + let end_bid = self.alloc_bid(); + + self.translate_opt_condition( + &stmt.condition, + mem::replace(context, Context::new(end_bid)), + body_bid, + end_bid, + ) + .map_err(|e| IrgenError::new(stmt.condition.write_string(), e))?; + + self.enter_scope(); + + let mut body_context = Context::new(body_bid); + self.translate_stmt( + &stmt.statement.node, + &mut body_context, + Some(step_bid), + Some(end_bid), + )?; + + self.exit_scope(); + self.insert_block( + body_context, + ir::BlockExit::Jump { + arg: ir::JumpArg::new(step_bid, Vec::new()), + }, + ); + + let mut step_context = Context::new(step_bid); + if let Some(step_expr) = &stmt.step { + let _unused = self + .translate_expr_rvalue(&step_expr.node, &mut step_context) + .map_err(|e| IrgenError::new(stmt.step.write_string(), e))?; + } + self.insert_block( + step_context, + ir::BlockExit::Jump { + arg: ir::JumpArg::new(cond_bid, Vec::new()), + }, + ); + + Ok(()) + } + Statement::Continue => { + let bid_continue = bid_continue.ok_or_else(|| { + IrgenError::new( + "continue;".to_string(), + IrgenErrorMessage::Misc { + message: "continue statement not within a loop".to_string(), + }, + ) + })?; + let next_context = Context::new(self.alloc_bid()); + self.insert_block( + mem::replace(context, next_context), + ir::BlockExit::Jump { + arg: ir::JumpArg::new(bid_continue, Vec::new()), + }, + ); + Ok(()) + } + Statement::Break => { + let bid_break = bid_break.ok_or_else(|| { + IrgenError::new( + "break;".to_string(), + IrgenErrorMessage::Misc { + message: "break statement not within a loop".to_string(), + }, + ) + })?; + let next_context = Context::new(self.alloc_bid()); + self.insert_block( + mem::replace(context, next_context), + ir::BlockExit::Jump { + arg: ir::JumpArg::new(bid_break, Vec::new()), + }, + ); + Ok(()) + } + Statement::Return(expr) => { + let value = match expr { + Some(expr) => self + .translate_expr_rvalue(&expr.node, context) + .map_err(|e| IrgenError::new(expr.write_string(), e))?, + None => ir::Operand::constant(ir::Constant::unit()), + }; + let value = self + .translate_typecast(value, self.return_type.clone(), context) + .map_err(|e| IrgenError::new(expr.write_string(), e))?; + let end_bid = self.alloc_bid(); + self.insert_block( + mem::replace(context, Context::new(end_bid)), + ir::BlockExit::Return { value }, + ); + Ok(()) + } + _ => todo!("e"), + } + } + + fn translate_decl( + &mut self, + decl: &Declaration, + context: &mut Context, + ) -> Result<(), IrgenErrorMessage> { + let (base_dtype, is_typedef) = + ir::Dtype::try_from_ast_declaration_specifiers(&decl.specifiers) + .map_err(|e| IrgenErrorMessage::InvalidDtype { dtype_error: e })?; + assert!(!is_typedef); + + for init_decl in &decl.declarators { + let declarator = &init_decl.node.declarator.node; + let name = name_of_declarator(declarator); + let dtype = base_dtype + .clone() + .with_ast_declarator(declarator) + .and_then(|dtype| dtype.deref().clone().resolve_typedefs(self.typedefs)) // TODO: Why? + .map_err(|e| IrgenErrorMessage::InvalidDtype { dtype_error: e })?; + match &dtype { + ir::Dtype::Unit { .. } => todo!("f"), + ir::Dtype::Int { .. } + | ir::Dtype::Float { .. } + | ir::Dtype::Pointer { .. } + | ir::Dtype::Array { .. } => { + let value = if let Some(initializer) = &init_decl.node.initializer { + Some(self.translate_initializer(&initializer.node, context)?) + } else { + None + }; + let _unused = self.translate_alloc(name, &dtype, value, context)?; + } + ir::Dtype::Function { .. } => todo!("g"), + ir::Dtype::Struct { .. } => todo!("h"), + ir::Dtype::Typedef { .. } => unreachable!(), + } + } + Ok(()) + } + + fn translate_initializer( + &mut self, + initializer: &Initializer, + context: &mut Context, + ) -> Result { + match initializer { + Initializer::Expression(expr) => self.translate_expr_rvalue(&expr.node, context), + Initializer::List(_) => panic!("Initializer::List is unsupported"), + } + } + + fn translate_expr_rvalue( + &mut self, + expr: &Expression, + context: &mut Context, + ) -> Result { + match expr { + Expression::Identifier(identifier) => { + let ptr = self.lookup_symbol_table(&identifier.node.name)?; + let ptr_dtype = ptr.dtype(); + let ptr_inner = ptr_dtype.get_pointer_inner().unwrap(); + if ptr_inner.get_function_inner().is_some() { + Ok(ptr) + } else if let Some(array_inner) = ptr_inner.get_array_inner() { + self.convert_array_to_pointer(ptr, array_inner, context) + } else { + context.insert_instruction(ir::Instruction::Load { ptr }) + } + } + Expression::Constant(constant) => Ok(ir::Operand::constant( + ir::Constant::try_from(&constant.node).unwrap(), + )), + Expression::Cast(cast) => { + let target_dtype = ir::Dtype::try_from(&cast.node.type_name.node) + .map_err(|e| IrgenErrorMessage::InvalidDtype { dtype_error: e })?; + let target_dtype = target_dtype + .resolve_typedefs(self.typedefs) + .map_err(|e| IrgenErrorMessage::InvalidDtype { dtype_error: e })?; + let operand = self.translate_expr_rvalue(&cast.node.expression.node, context)?; + self.translate_typecast(operand, target_dtype, context) + } + Expression::Call(call) => self.translate_func_call(&call.node, context), + Expression::BinaryOperator(binary) => self.translate_binary_op( + &binary.node.operator.node, + &binary.node.lhs.node, + &binary.node.rhs.node, + context, + ), + Expression::UnaryOperator(unary) => self.translate_unary_op( + &unary.node.operator.node, + &unary.node.operand.node, + context, + ), + Expression::Conditional(cond) => self.translate_conditional(&cond.node, context), + _ => todo!("c"), + } + } + + fn translate_conditional( + &mut self, + cond: &ConditionalExpression, + context: &mut Context, + ) -> Result { + let then_bid = self.alloc_bid(); + let else_bid = self.alloc_bid(); + let end_bid = self.alloc_bid(); + + self.translate_condition( + &cond.condition.node, + mem::replace(context, Context::new(end_bid)), + then_bid, + else_bid, + )?; + + let mut then_context = Context::new(then_bid); + let then_val = self.translate_expr_rvalue(&cond.then_expression.node, &mut then_context)?; + + let mut else_context = Context::new(else_bid); + let else_val = self.translate_expr_rvalue(&cond.else_expression.node, &mut else_context)?; + + // println!("conditional: {} {}", cond.then_expression.write_string(), cond.else_expression.write_string()); + let merged_dtype = self.merge_dtype(then_val.dtype(), else_val.dtype()); + let then_val = + self.translate_typecast(then_val, merged_dtype.clone(), &mut then_context)?; + let else_val = + self.translate_typecast(else_val, merged_dtype.clone(), &mut else_context)?; + + let var = self.alloc_tempid(); + let ptr = self.translate_alloc(var, &merged_dtype.clone(), None, context)?; + + let _unused = then_context.insert_instruction(ir::Instruction::Store { + ptr: ptr.clone(), + value: then_val, + })?; + self.insert_block( + then_context, + ir::BlockExit::Jump { + arg: ir::JumpArg::new(end_bid, Vec::new()), + }, + ); + + let _unused = else_context.insert_instruction(ir::Instruction::Store { + ptr: ptr.clone(), + value: else_val, + })?; + self.insert_block( + else_context, + ir::BlockExit::Jump { + arg: ir::JumpArg::new(end_bid, Vec::new()), + }, + ); + + let r = context.insert_instruction(ir::Instruction::Load { ptr: ptr.clone() })?; + // println!("ptr: {}, rtype: {}", ptr.dtype(), r.dtype()); + Ok(r) + } + + fn merge_dtype(&self, lhs_dtype: ir::Dtype, rhs_dtype: ir::Dtype) -> ir::Dtype { + if lhs_dtype == rhs_dtype { + lhs_dtype + } else { + match (&lhs_dtype, &rhs_dtype) { + (&ir::Dtype::DOUBLE, _) => lhs_dtype, + (_, &ir::Dtype::DOUBLE) => rhs_dtype, + (&ir::Dtype::FLOAT, _) => lhs_dtype, + (_, &ir::Dtype::FLOAT) => rhs_dtype, + ( + ir::Dtype::Int { + width: lhs_width, + is_signed: lhs_is_signed, + is_const: lhs_is_const, + }, + ir::Dtype::Int { + width: rhs_width, + is_signed: rhs_is_signed, + is_const: rhs_is_const, + }, + ) => { + if lhs_is_signed == rhs_is_signed { + if lhs_width > rhs_width { + lhs_dtype + } else { + rhs_dtype + } + } else if !lhs_is_signed { + if lhs_width >= rhs_width { + lhs_dtype + } else { + rhs_dtype + } + } else if lhs_width <= rhs_width { + rhs_dtype + } else { + lhs_dtype + } + } + _ => { + // println!("merge failed: {} {}", lhs_dtype, rhs_dtype); + todo!("merge") + } + } + } + } + + fn translate_binary_op( + &mut self, + op: &BinaryOperator, + lhs: &Expression, + rhs: &Expression, + context: &mut Context, + ) -> Result { + match op { + BinaryOperator::Assign => { + let lhs = self.translate_expr_lvalue(lhs, context)?; + let rhs = self.translate_expr_rvalue(rhs, context)?; + let _unused = context.insert_instruction(ir::Instruction::Store { + ptr: lhs, + value: rhs.clone(), + })?; + Ok(rhs) + } + BinaryOperator::AssignPlus => { + let rhs = self.translate_binary_op(&BinaryOperator::Plus, lhs, rhs, context)?; + let lhs = self.translate_expr_lvalue(lhs, context)?; + let _unused = context.insert_instruction(ir::Instruction::Store { + ptr: lhs, + value: rhs.clone(), + })?; + Ok(rhs) + } + BinaryOperator::AssignMinus => { + let rhs = self.translate_binary_op(&BinaryOperator::Minus, lhs, rhs, context)?; + let lhs = self.translate_expr_lvalue(lhs, context)?; + let _unused = context.insert_instruction(ir::Instruction::Store { + ptr: lhs, + value: rhs.clone(), + })?; + Ok(rhs) + } + _ => { + let lhs = self.translate_expr_rvalue(lhs, context)?; + let rhs = self.translate_expr_rvalue(rhs, context)?; + let operand_dtype = self.merge_dtype(lhs.dtype(), rhs.dtype()); + let result_dtype = match op { + BinaryOperator::Less + | BinaryOperator::Greater + | BinaryOperator::LessOrEqual + | BinaryOperator::GreaterOrEqual + | BinaryOperator::Equals + | BinaryOperator::NotEquals => ir::Dtype::BOOL, + _ => operand_dtype.clone(), + }; + let lhs = self.translate_typecast(lhs, operand_dtype.clone(), context)?; + let rhs = self.translate_typecast(rhs, operand_dtype.clone(), context)?; + context.insert_instruction(ir::Instruction::BinOp { + op: op.clone(), + lhs, + rhs, + dtype: result_dtype, + }) + } + } + } + + fn translate_unary_op( + &mut self, + op: &UnaryOperator, + operand: &Expression, + context: &mut Context, + ) -> Result { + match op { + UnaryOperator::PreIncrement => { + let lhs = self.translate_expr_rvalue(operand, context)?; + let dtype = lhs.dtype(); + let rhs = context.insert_instruction(ir::Instruction::BinOp { + op: BinaryOperator::Plus, + lhs, + rhs: ir::Operand::constant(ir::Constant::int(1, dtype.clone())), + dtype: dtype.clone(), + })?; + let lhs = self.translate_expr_lvalue(operand, context)?; + let _unused = context.insert_instruction(ir::Instruction::Store { + ptr: lhs, + value: rhs.clone(), + })?; + Ok(rhs) + } + UnaryOperator::Complement => { + let lhs = self.translate_expr_rvalue(operand, context)?; + let dtype = lhs.dtype(); + context.insert_instruction(ir::Instruction::BinOp { + op: BinaryOperator::BitwiseXor, + lhs, + rhs: ir::Operand::constant(ir::Constant::int(u128::MAX, dtype.clone())), + dtype: dtype.clone(), + }) + } + UnaryOperator::Plus | UnaryOperator::Minus | UnaryOperator::Negate => { + let operand = self.translate_expr_rvalue(operand, context)?; + let dtype = operand.dtype(); + context.insert_instruction(ir::Instruction::UnaryOp { + op: op.clone(), + operand, + dtype, + }) + } + _ => todo!("unary op"), + } + } + + fn lookup_symbol_table(&self, name: &String) -> Result { + for layer in self.symbol_table.iter().rev() { + if let Some(ptr) = layer.get(name) { + return Ok(ptr.clone()); + } + } + Err(IrgenErrorMessage::RequireLvalue { + message: format!("Undefined symbol {name}"), + }) + } + + fn convert_array_to_pointer( + &mut self, + ptr: ir::Operand, + array_inner: &ir::Dtype, + context: &mut Context, + ) -> Result { + todo!("array to pointer") + } + + fn translate_condition( + &mut self, + condition: &Expression, + mut context: Context, + then_bid: ir::BlockId, + else_bid: ir::BlockId, + ) -> Result<(), IrgenErrorMessage> { + let condition = self.translate_expr_rvalue(condition, &mut context)?; + let condition = self.translate_typecast_to_bool(condition, &mut context)?; + self.insert_block( + context, + ir::BlockExit::ConditionalJump { + condition, + arg_then: ir::JumpArg::new(then_bid, Vec::new()), + arg_else: ir::JumpArg::new(else_bid, Vec::new()), + }, + ); + Ok(()) + } + + fn translate_switch_body( + &mut self, + stmt: &Statement, + end_bid: ir::BlockId, + ) -> Result<(Vec<(ir::Constant, ir::JumpArg)>, ir::BlockId), IrgenError> { + let mut cases = Vec::new(); + let mut default = None; + + let items = if let Statement::Compound(items) = stmt { + items + } else { + panic!("`Statement` in the `switch` is unsupported except `Statement::Compound`") + }; + + self.enter_scope(); + + for item in items { + match &item.node { + BlockItem::Statement(stmt) => { + self.translate_switch_body_inner( + &stmt.node, + &mut cases, + &mut default, + end_bid, + )?; + } + _ => panic!("is unsupported"), + } + } + + self.exit_scope(); + + let default = default.unwrap_or(end_bid); + + Ok((cases, default)) + } + + fn translate_switch_body_inner( + &mut self, + stmt: &Statement, + cases: &mut Vec<(ir::Constant, ir::JumpArg)>, + default: &mut Option, + end_bid: ir::BlockId, + ) -> Result<(), IrgenError> { + let stmt = if let Statement::Labeled(stmt) = stmt { + &stmt.node + } else { + panic!( + "`BlockItem::Statement` in the `Statement::Compound` of the `switch` is unsupported except `Statement::Labeled`" + ) + }; + + let bid = self.alloc_bid(); + let case = self.translate_switch_body_label_statement(stmt, bid, end_bid)?; + if let Some(case) = case { + if !case.is_integer_constant() { + return Err(IrgenError::new( + stmt.label.write_string(), + IrgenErrorMessage::Misc { + message: "expression is not an integer constant expression".to_string(), + }, + )); + } + + if cases.iter().any(|(c, _)| &case == c) { + return Err(IrgenError::new( + stmt.label.write_string(), + IrgenErrorMessage::Misc { + message: "duplicate case value".to_string(), + }, + )); + } + + cases.push((case, ir::JumpArg::new(bid, Vec::new()))); + } else { + if default.is_some() { + return Err(IrgenError::new( + stmt.label.write_string(), + IrgenErrorMessage::Misc { + message: "previous default already exists".to_string(), + }, + )); + } + *default = Some(bid); + } + Ok(()) + } + + fn translate_switch_body_label_statement( + &mut self, + stmt: &LabeledStatement, + bid: ir::BlockId, + end_bid: ir::BlockId, + ) -> Result, IrgenError> { + let case = match &stmt.label.node { + Label::Identifier(_) => panic!("`Label::Identifier` is unsupported"), + Label::Case(expr) => { + let constant = ir::Constant::try_from(&expr.node).map_err(|_| { + IrgenError::new( + expr.write_string(), + IrgenErrorMessage::Misc { + message: "case label does not reduce to an integer constant" + .to_string(), + }, + ) + })?; + Some(constant) + } + Label::Default => None, + _ => unreachable!(), + }; + let items = if let Statement::Compound(items) = &stmt.statement.node { + items + } else { + panic!("`Statement` in the `label` is unsupported except `Sattement::Compound`") + }; + self.enter_scope(); + let (last, items) = items + .split_last() + .expect("`Statement::Compound` has no item"); + let mut context = Context::new(bid); + for item in items { + match &item.node { + BlockItem::Declaration(decl) => self + .translate_decl(&decl.node, &mut context) + .map_err(|e| IrgenError::new(decl.write_string(), e))?, + BlockItem::Statement(stmt) => { + self.translate_stmt(&stmt.node, &mut context, None, None)?; + } + _ => panic!("is unsupported"), + } + } + + let stmt = if let BlockItem::Statement(stmt) = &last.node { + &stmt.node + } else { + panic!( + "`BlockItem` in the `Statement::Compound` of the `label` is unsupported except `BlockItem::Statement`" + ) + }; + + assert_eq!( + stmt, + &Statement::Break, + "the last `BlockItem` in the `Statement::Compound` of the `label` must be `Statement::Break`" + ); + + self.insert_block( + context, + ir::BlockExit::Jump { + arg: ir::JumpArg::new(end_bid, Vec::new()), + }, + ); + + self.exit_scope(); + + Ok(case) + } + + fn translate_for_initializer( + &mut self, + initializer: &ForInitializer, + context: &mut Context, + ) -> Result<(), IrgenErrorMessage> { + match initializer { + ForInitializer::Empty => (), + ForInitializer::Expression(expr) => { + let _unused = self.translate_expr_rvalue(&expr.node, context)?; + } + ForInitializer::Declaration(decl) => { + return self.translate_decl(&decl.node, context); + } + ForInitializer::StaticAssert(_) => { + panic!("ForInitializer::StaticAssert is unsupported") + } + } + Ok(()) + } + + fn translate_opt_condition( + &mut self, + condition: &Option>>, + context: Context, + body_bid: ir::BlockId, + end_bid: ir::BlockId, + ) -> Result<(), IrgenErrorMessage> { + if let Some(condition) = condition { + self.translate_condition(&condition.node, context, body_bid, end_bid) + } else { + self.insert_block( + context, + ir::BlockExit::Jump { + arg: ir::JumpArg::new(body_bid, Vec::new()), + }, + ); + Ok(()) + } + } + + fn translate_expr_lvalue( + &mut self, + expr: &Expression, + context: &mut Context, + ) -> Result { + match expr { + Expression::Identifier(identifier) => self.lookup_symbol_table(&identifier.node.name), + Expression::UnaryOperator(unary) => match &unary.node.operator.node { + UnaryOperator::Indirection => self.translate_expr_rvalue(&unary.node.operand.node, context), + _ => Err(IrgenErrorMessage::Misc { message: "This error occurred at `IrgenFunc::translate_expr_lvalue`".to_string() }) + } + Expression::BinaryOperator(binary) => match &binary.node.operator.node { + BinaryOperator::Index => self.translate_index_op(&binary.node.lhs.node, &binary.node.rhs.node, context), + _ => Err(IrgenErrorMessage::Misc { message: "binary operator expression cannot be used as l-value except index operator expression".to_string() }), + } + _ => todo!("expr lvalue") + } + } + + fn translate_index_op( + &mut self, + lhs: &Expression, + rhs: &Expression, + context: &mut Context, + ) -> Result { + todo!("index op") + } + + fn translate_func_call( + &mut self, + call: &CallExpression, + context: &mut Context, + ) -> Result { + let callee = self.translate_expr_rvalue(&call.callee.node, context)?; + let function_pointer_type = callee.dtype(); + let function = function_pointer_type.get_pointer_inner().ok_or_else(|| { + IrgenErrorMessage::NeedFunctionOrFunctionPointer { + callee: callee.clone(), + } + })?; + let (return_type, parameters) = function.get_function_inner().ok_or_else(|| { + IrgenErrorMessage::NeedFunctionOrFunctionPointer { + callee: callee.clone(), + } + })?; + + let args = call + .arguments + .iter() + .map(|a| self.translate_expr_rvalue(&a.node, context)) + .collect::, _>>()?; + if args.len() != parameters.len() { + return Err(IrgenErrorMessage::Misc { + message: format!("too few arguments to function `{}`", "TODO"), + }); + } + let args = izip!(args, parameters) + .map(|(a, p)| self.translate_typecast(a, p.clone(), context)) + .collect::, _>>()?; + + let return_type = return_type.clone().set_const(false); + context.insert_instruction(ir::Instruction::Call { + callee, + args, + return_type, + }) } /// Translate initial parameter declarations of the functions to IR. @@ -666,7 +1582,59 @@ impl IrgenFunc<'_> { name_of_params: &[String], context: &mut Context, ) -> Result<(), IrgenErrorMessage> { - todo!() + for (i, (dtype, var)) in izip!(&signature.params, name_of_params).enumerate() { + let value = Some(ir::Operand::register( + ir::RegisterId::arg(bid_init, i), + dtype.clone(), + )); + let _unused = self.translate_alloc(var.clone(), dtype, value, context)?; + self.phinodes_init + .push(Named::new(Some(var.clone()), dtype.clone())); + } + + Ok(()) + } + + fn translate_alloc( + &mut self, + var: String, + dtype: &ir::Dtype, + value: Option, + context: &mut Context, + ) -> Result { + let rid = self.insert_alloc(Named::new(Some(var.clone()), dtype.clone())); + let ptr = ir::Operand::register(rid, ir::Dtype::pointer(dtype.clone())); + self.insert_symbol_table_entry(var, ptr.clone())?; + + if let Some(value) = value { + let value = self.translate_typecast(value, dtype.clone(), context)?; + context.insert_instruction(ir::Instruction::Store { ptr, value }) + } else { + Ok(ptr) + } + } + + fn translate_typecast( + &mut self, + value: ir::Operand, + dtype: ir::Dtype, + context: &mut Context, + ) -> Result { + if value.dtype() == dtype { + return Ok(value); + } + context.insert_instruction(ir::Instruction::TypeCast { + value, + target_dtype: dtype, + }) + } + + fn translate_typecast_to_bool( + &mut self, + value: ir::Operand, + context: &mut Context, + ) -> Result { + self.translate_typecast(value, ir::Dtype::BOOL, context) // TODO: Right? } }