diff --git a/nac3core/src/codegen/model/function_builder.rs b/nac3core/src/codegen/model/function_builder.rs new file mode 100644 index 00000000..fcc3748b --- /dev/null +++ b/nac3core/src/codegen/model/function_builder.rs @@ -0,0 +1,62 @@ +use inkwell::{ + types::{BasicMetadataTypeEnum, BasicType}, + values::{AnyValue, BasicMetadataValueEnum, BasicValueEnum}, +}; + +use crate::codegen::{model::*, CodeGenContext}; + +// TODO: Variadic argument? +pub struct FunctionBuilder<'ctx, 'a> { + ctx: &'a CodeGenContext<'ctx, 'a>, + fn_name: &'a str, + arguments: Vec<(BasicMetadataTypeEnum<'ctx>, BasicMetadataValueEnum<'ctx>)>, +} + +impl<'ctx, 'a> FunctionBuilder<'ctx, 'a> { + pub fn begin(ctx: &'a CodeGenContext<'ctx, 'a>, fn_name: &'a str) -> Self { + FunctionBuilder { ctx, fn_name, arguments: Vec::new() } + } + + // NOTE: `_name` is for self-documentation + #[must_use] + pub fn arg>(mut self, _name: &'static str, model: M, value: M::Value) -> Self { + model.check_value(self.ctx.ctx, value).unwrap(); // Panics if the passed `value` has the incorrect type. + + self.arguments + .push((model.get_llvm_type(self.ctx.ctx).into(), value.get_llvm_value().into())); + self + } + + pub fn returning>(self, name: &'static str, return_model: M) -> M::Value { + let (param_tys, param_vals): (Vec<_>, Vec<_>) = self.arguments.into_iter().unzip(); + + // Get the LLVM function, create (by declaring) the function if it doesn't exist in `ctx.module`. + let function = self.ctx.module.get_function(self.fn_name).unwrap_or_else(|| { + let return_type = return_model.get_llvm_type(self.ctx.ctx); + let fn_type = return_type.fn_type(¶m_tys, false); + self.ctx.module.add_function(self.fn_name, fn_type, None) + }); + + // Build call + let ret = self.ctx.builder.build_call(function, ¶m_vals, name).unwrap(); + + // Check the return value/type + let Ok(ret) = BasicValueEnum::try_from(ret.as_any_value_enum()) else { + panic!("Return type is not a BasicValue"); + }; + return_model.review_value(self.ctx.ctx, ret).unwrap() + } + + // TODO: Code duplication, but otherwise returning> cannot resolve S if return_optic = None + pub fn returning_void(self) { + let (param_tys, param_vals): (Vec<_>, Vec<_>) = self.arguments.into_iter().unzip(); + + let function = self.ctx.module.get_function(self.fn_name).unwrap_or_else(|| { + let return_type = self.ctx.ctx.void_type(); + let fn_type = return_type.fn_type(¶m_tys, false); + self.ctx.module.add_function(self.fn_name, fn_type, None) + }); + + self.ctx.builder.build_call(function, ¶m_vals, "").unwrap(); + } +} diff --git a/nac3core/src/codegen/model/mod.rs b/nac3core/src/codegen/model/mod.rs index 2ebf02b8..0520b530 100644 --- a/nac3core/src/codegen/model/mod.rs +++ b/nac3core/src/codegen/model/mod.rs @@ -1,5 +1,6 @@ pub mod core; pub mod fixed_int; +pub mod function_builder; pub mod int; mod int_util; pub mod opaque;