From 55f1c131628cc776dcb900d3c77899b090008c45 Mon Sep 17 00:00:00 2001 From: Bas Zalmstra <4995967+baszalmstra@users.noreply.github.com> Date: Sun, 6 Sep 2026 15:28:50 +0200 Subject: [PATCH] refactor: pair callables with signatures --- crates/mun_codegen/src/ir/body.rs | 26 +++++----- crates/mun_codegen/src/ir/dispatch_table.rs | 55 +++++++++++---------- crates/mun_codegen/src/ir/file_group.rs | 1 - crates/mun_codegen/src/ir/value.rs | 47 +++++++++++++++++- 4 files changed, 87 insertions(+), 42 deletions(-) diff --git a/crates/mun_codegen/src/ir/body.rs b/crates/mun_codegen/src/ir/body.rs index fd165335..0ca54def 100644 --- a/crates/mun_codegen/src/ir/body.rs +++ b/crates/mun_codegen/src/ir/body.rs @@ -24,7 +24,7 @@ use crate::{ dispatch_table::DispatchTable, ty::HirTypeCache, type_table::TypeTable, - value::{Operand, Place, PlaceValue}, + value::{Callable, Operand, Place, PlaceValue}, RuntimeArrayValue, RuntimeReferenceValue, }, module_group::ModuleGroup, @@ -422,10 +422,9 @@ impl<'db, 'ink, 't> BodyIrGenerator<'db, 'ink, 't> { let allocator_handle = self.get_allocator_handle_ptr(); // Safety: we can be sure that the new intrinsic returns a reference. - let untyped_reference = self - .builder - .build_call( - new_fn_ptr, + let untyped_reference = new_fn_ptr + .call( + &self.builder, &[type_info_ptr.into(), allocator_handle.into()], "ref", ) @@ -1118,8 +1117,7 @@ impl<'db, 'ink, 't> BodyIrGenerator<'db, 'ink, 't> { &self.builder, function, ); - self.builder - .build_call(ptr_value, args, &function.name(self.db).to_string()) + ptr_value.call(&self.builder, args, &function.name(self.db).to_string()) } else { let llvm_function = self.function_map.get(&function).unwrap_or_else(|| { panic!( @@ -1127,8 +1125,11 @@ impl<'db, 'ink, 't> BodyIrGenerator<'db, 'ink, 't> { function.name(self.db), ) }); - self.builder - .build_call(*llvm_function, args, &function.name(self.db).to_string()) + Callable::from_function(*llvm_function).call( + &self.builder, + args, + &function.name(self.db).to_string(), + ) } } @@ -1507,10 +1508,9 @@ impl<'db, 'ink, 't> BodyIrGenerator<'db, 'ink, 't> { // An object pointer adds an extra layer of indirection to allow for hot // reloading. To make it struct type agnostic, it is stored in a `*const // *mut std::ffi::c_void`. - let untyped_array_ptr = self - .builder - .build_call( - new_array_fn_ptr, + let untyped_array_ptr = new_array_fn_ptr + .call( + &self.builder, &[ type_info_ptr.into(), length_value.into(), diff --git a/crates/mun_codegen/src/ir/dispatch_table.rs b/crates/mun_codegen/src/ir/dispatch_table.rs index e3339c7a..9274dcf5 100644 --- a/crates/mun_codegen/src/ir/dispatch_table.rs +++ b/crates/mun_codegen/src/ir/dispatch_table.rs @@ -6,16 +6,15 @@ use std::{ use inkwell::{ context::Context, module::Module, - targets::TargetData, types::{BasicTypeEnum, FunctionType}, - values::{BasicValueEnum, CallableValue}, + values::BasicValueEnum, }; use mun_hir::{Body, Expr, ExprId, HirDatabase, InferenceResult}; use rustc_hash::FxHashSet; use crate::{ intrinsics::Intrinsic, - ir::{function, ty::HirTypeCache}, + ir::{function, ty::HirTypeCache, value::Callable}, module_group::ModuleGroup, type_info::{HasStaticTypeId, TypeId}, }; @@ -35,16 +34,14 @@ use crate::{ /// hot reloading within Mun. #[derive(Debug, Eq, PartialEq)] pub struct DispatchTable<'ink> { - // The LLVM context in which all LLVM types live - context: &'ink Context, - // The target for which to create the dispatch table - target: TargetData, // This contains the function that map to the DispatchTable struct fields function_to_idx: HashMap, // Prototype to function index prototype_to_idx: HashMap, // This contains an ordered list of all the function in the dispatch table entries: Vec, + // LLVM signatures for entries at the same index. + signatures: Vec>, // Contains a reference to the global value containing the DispatchTable table_ref: Option>, // @@ -88,7 +85,7 @@ impl<'ink> DispatchTable<'ink> { table_ref: Option>, builder: &inkwell::builder::Builder<'ink>, function: mun_hir::Function, - ) -> CallableValue<'ink> { + ) -> Callable<'ink> { let function_name = function.name(db).to_string(); // Get the index of the function @@ -97,7 +94,13 @@ impl<'ink> DispatchTable<'ink> { .get(&function) .expect("unknown function"); - Self::gen_function_lookup_by_index(table_ref, builder, &function_name, index) + Self::gen_function_lookup_by_index( + table_ref, + builder, + &function_name, + index, + self.signatures[index], + ) } /// Generates a function lookup through the `DispatchTable`, equivalent to @@ -108,7 +111,7 @@ impl<'ink> DispatchTable<'ink> { table_ref: Option>, builder: &inkwell::builder::Builder<'ink>, intrinsic: &impl Intrinsic, - ) -> CallableValue<'ink> { + ) -> Callable<'ink> { let prototype = intrinsic.prototype(); // Get the index of the intrinsic @@ -117,7 +120,13 @@ impl<'ink> DispatchTable<'ink> { .get(&prototype) .expect("unknown function"); - Self::gen_function_lookup_by_index(table_ref, builder, &prototype.name, index) + Self::gen_function_lookup_by_index( + table_ref, + builder, + &prototype.name, + index, + self.signatures[index], + ) } /// Generates a function lookup through the `DispatchTable`, equivalent to @@ -128,7 +137,8 @@ impl<'ink> DispatchTable<'ink> { builder: &inkwell::builder::Builder<'ink>, function_name: &str, index: usize, - ) -> CallableValue<'ink> { + signature: FunctionType<'ink>, + ) -> Callable<'ink> { // Get the internal table reference let table_ref = table_ref.expect("no dispatch table defined"); @@ -144,11 +154,10 @@ impl<'ink> DispatchTable<'ink> { panic!("could not get {function_name} (index: {index}) from dispatch table") }); - builder + let pointer = builder .build_load(ptr_to_function_ptr, &format!("{function_name}_ptr")) - .into_pointer_value() - .try_into() - .expect("Pointer value is not a valid function pointer.") + .into_pointer_value(); + Callable::new(pointer, signature) } /// Returns the value that represents the dispatch table in IR or `None` if @@ -166,12 +175,8 @@ impl<'ink> DispatchTable<'ink> { /// A struct that can be used to build the dispatch table from HIR. pub(crate) struct DispatchTableBuilder<'db, 'ink, 't> { db: &'db dyn HirDatabase, - // The LLVM context in which all LLVM types live - context: &'ink Context, - // The module in which all values live + // Module that owns the dispatch table global. module: &'t Module<'ink>, - // The target for which to create the dispatch table - target_data: TargetData, // Converts HIR ty's to inkwell types hir_types: &'t HirTypeCache<'db, 'ink>, // This contains the functions that map to the DispatchTable struct fields @@ -199,7 +204,6 @@ impl<'db, 'ink, 't> DispatchTableBuilder<'db, 'ink, 't> { /// Creates a new builder that can generate a dispatch function. pub fn new( context: &'ink Context, - target_data: TargetData, db: &'db dyn HirDatabase, module: &'t Module<'ink>, intrinsics: &BTreeMap>, @@ -208,9 +212,7 @@ impl<'db, 'ink, 't> DispatchTableBuilder<'db, 'ink, 't> { ) -> Self { let mut table = Self { db, - context, module, - target_data, function_to_idx: HashMap::default(), prototype_to_idx: HashMap::default(), entries: Vec::default(), @@ -332,6 +334,8 @@ impl<'db, 'ink, 't> DispatchTableBuilder<'db, 'ink, 't> { /// /// Returns the `DispatchTable` and a set of dependencies for the module. pub fn build(self) -> (DispatchTable<'ink>, FxHashSet) { + let signatures = self.entries.iter().map(|entry| entry.ir_type).collect(); + // Construct the table body from all the entries in the dispatch table let table_body: Vec> = self .entries @@ -383,12 +387,11 @@ impl<'db, 'ink, 't> DispatchTableBuilder<'db, 'ink, 't> { ( DispatchTable { - context: self.context, - target: self.target_data, function_to_idx: self.function_to_idx, prototype_to_idx: self.prototype_to_idx, table_ref: self.table_ref, table_type, + signatures, entries: self .entries .into_iter() diff --git a/crates/mun_codegen/src/ir/file_group.rs b/crates/mun_codegen/src/ir/file_group.rs index 92ac2b45..15df02c7 100644 --- a/crates/mun_codegen/src/ir/file_group.rs +++ b/crates/mun_codegen/src/ir/file_group.rs @@ -81,7 +81,6 @@ pub(crate) fn gen_file_group_ir<'ink>( // Collect all exposed functions' bodies. let mut dispatch_table_builder = DispatchTableBuilder::new( code_gen.context, - code_gen.target_machine.get_target_data(), code_gen.db, &llvm_module, &intrinsics_map, diff --git a/crates/mun_codegen/src/ir/value.rs b/crates/mun_codegen/src/ir/value.rs index 12b6ab6b..892a4ee3 100644 --- a/crates/mun_codegen/src/ir/value.rs +++ b/crates/mun_codegen/src/ir/value.rs @@ -1,10 +1,53 @@ use inkwell::{ builder::Builder, - types::BasicTypeEnum, - values::{BasicValueEnum, InstructionValue, PointerValue}, + types::{BasicTypeEnum, FunctionType}, + values::{ + BasicMetadataValueEnum, BasicValueEnum, CallSiteValue, CallableValue, FunctionValue, + InstructionValue, PointerValue, + }, + AddressSpace, }; use mun_hir::Ty; +/// A function pointer paired with the signature required to call it. +#[derive(Clone, Copy)] +pub(crate) struct Callable<'ink> { + pointer: PointerValue<'ink>, + signature: FunctionType<'ink>, +} + +impl<'ink> Callable<'ink> { + pub(crate) fn new(pointer: PointerValue<'ink>, signature: FunctionType<'ink>) -> Self { + debug_assert_eq!( + pointer.get_type(), + signature.ptr_type(AddressSpace::default()) + ); + Self { pointer, signature } + } + + pub(crate) fn from_function(function: FunctionValue<'ink>) -> Self { + Self::new( + function.as_global_value().as_pointer_value(), + function.get_type(), + ) + } + + pub(crate) fn call( + self, + builder: &Builder<'ink>, + args: &[BasicMetadataValueEnum<'ink>], + name: &str, + ) -> CallSiteValue<'ink> { + debug_assert_eq!( + self.pointer.get_type(), + self.signature.ptr_type(AddressSpace::default()) + ); + let callable = CallableValue::try_from(self.pointer) + .expect("callable pointer must have a function signature"); + builder.build_call(callable, args, name) + } +} + /// An SSA value paired with the Mun type that gives the value its meaning. #[derive(Clone)] pub(crate) struct Operand<'ink> {