diff --git a/crates/mun_abi/c b/crates/mun_abi/c index 1380c1959..fe351ecde 160000 --- a/crates/mun_abi/c +++ b/crates/mun_abi/c @@ -1 +1 @@ -Subproject commit 1380c1959532df7c889ad994e8ff6338e0105cab +Subproject commit fe351ecdec31624373f92851089e665f1314840f diff --git a/crates/mun_abi/src/autogen.rs b/crates/mun_abi/src/autogen.rs index 8eeb2b693..92a987042 100644 --- a/crates/mun_abi/src/autogen.rs +++ b/crates/mun_abi/src/autogen.rs @@ -9,9 +9,9 @@ use crate::{Privacy, StructMemoryKind, TypeGroup}; #[doc = ""] #[doc = " GUIDs are generated by taking the MD5 hash of a type's name."] #[doc = ""] -#[doc = "
"] +#[doc = "
"] #[repr(C)] -#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)] pub struct Guid { #[doc = " 16-byte MD5 hash"] pub b: [u8; 16usize], @@ -236,6 +236,8 @@ pub struct StructInfo { pub field_sizes: *const u16, #[doc = " Number of fields"] pub num_fields: u16, + #[doc = " Struct memory alignment"] + pub alignment: u16, #[doc = " Struct memory kind"] pub memory_kind: StructMemoryKind, } @@ -312,8 +314,18 @@ fn bindgen_test_layout_StructInfo() { ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::())).memory_kind as *const _ as usize }, + unsafe { &(*(::std::ptr::null::())).alignment as *const _ as usize }, 42usize, + concat!( + "Offset of field: ", + stringify!(StructInfo), + "::", + stringify!(alignment) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).memory_kind as *const _ as usize }, + 44usize, concat!( "Offset of field: ", stringify!(StructInfo), diff --git a/crates/mun_abi/src/autogen_impl.rs b/crates/mun_abi/src/autogen_impl.rs index f6c7b08c2..f2aad2d20 100644 --- a/crates/mun_abi/src/autogen_impl.rs +++ b/crates/mun_abi/src/autogen_impl.rs @@ -162,6 +162,13 @@ impl StructInfo { ) }) } + + /// Returns the size of the struct + pub fn size(&self) -> usize { + (self.field_offsets().last().cloned().unwrap_or(0) + + self.field_sizes().last().cloned().unwrap_or(0)) + .into() + } } impl fmt::Display for StructInfo { @@ -468,6 +475,7 @@ mod tests { field_types: &[&TypeInfo], field_offsets: &[u16], field_sizes: &[u16], + alignment: u16, memory_kind: StructMemoryKind, ) -> StructInfo { assert!(field_names.len() == field_types.len()); @@ -481,6 +489,7 @@ mod tests { field_offsets: field_offsets.as_ptr(), field_sizes: field_sizes.as_ptr(), num_fields: field_names.len() as u16, + alignment, memory_kind, } } @@ -490,7 +499,7 @@ mod tests { #[test] fn test_struct_info_name() { let struct_name = CString::new(FAKE_STRUCT_NAME).expect("Invalid fake struct name."); - let struct_info = fake_struct_info(&struct_name, &[], &[], &[], &[], Default::default()); + let struct_info = fake_struct_info(&struct_name, &[], &[], &[], &[], 1, Default::default()); assert_eq!(struct_info.name(), FAKE_STRUCT_NAME); } @@ -508,6 +517,7 @@ mod tests { field_types, field_offsets, field_sizes, + 1, Default::default(), ); @@ -515,6 +525,7 @@ mod tests { assert_eq!(struct_info.field_types(), field_types); assert_eq!(struct_info.field_offsets(), field_offsets); assert_eq!(struct_info.field_sizes(), field_sizes); + assert_eq!(struct_info.size(), 0); } #[test] @@ -527,6 +538,7 @@ mod tests { let field_types = &[&type_info]; let field_offsets = &[1]; let field_sizes = &[2]; + let alignment = 1; let struct_name = CString::new(FAKE_STRUCT_NAME).expect("Invalid fake struct name."); let struct_info = fake_struct_info( &struct_name, @@ -534,6 +546,7 @@ mod tests { field_types, field_offsets, field_sizes, + alignment, Default::default(), ); @@ -543,28 +556,42 @@ mod tests { assert_eq!(struct_info.field_types(), field_types); assert_eq!(struct_info.field_offsets(), field_offsets); assert_eq!(struct_info.field_sizes(), field_sizes); + assert_eq!( + struct_info.size() as u16, + field_offsets.last().unwrap() + field_sizes.last().unwrap() + ) } - fn fake_module_info( - path: &CStr, - functions: &[FunctionInfo], - types: &[&TypeInfo], - ) -> ModuleInfo { - ModuleInfo { - path: path.as_ptr(), - functions: functions.as_ptr(), - num_functions: functions.len() as u32, - types: types.as_ptr().cast::<*const TypeInfo>(), - num_types: types.len() as u32, - } + #[test] + fn test_struct_info_alignment() { + let struct_name = CString::new(FAKE_STRUCT_NAME).expect("Invalid fake struct name."); + let struct_alignment = 4; + let struct_info = fake_struct_info( + &struct_name, + &[], + &[], + &[], + &[], + struct_alignment, + Default::default(), + ); + + assert_eq!(struct_info.alignment, struct_alignment); } #[test] fn test_struct_info_memory_kind_gc() { let struct_name = CString::new(FAKE_STRUCT_NAME).expect("Invalid fake struct name."); let struct_memory_kind = StructMemoryKind::GC; - let struct_info = - fake_struct_info(&struct_name, &[], &[], &[], &[], struct_memory_kind.clone()); + let struct_info = fake_struct_info( + &struct_name, + &[], + &[], + &[], + &[], + 1, + struct_memory_kind.clone(), + ); assert_eq!(struct_info.memory_kind, struct_memory_kind); } @@ -573,12 +600,33 @@ mod tests { fn test_struct_info_memory_kind_value() { let struct_name = CString::new(FAKE_STRUCT_NAME).expect("Invalid fake struct name."); let struct_memory_kind = StructMemoryKind::Value; - let struct_info = - fake_struct_info(&struct_name, &[], &[], &[], &[], struct_memory_kind.clone()); + let struct_info = fake_struct_info( + &struct_name, + &[], + &[], + &[], + &[], + 1, + struct_memory_kind.clone(), + ); assert_eq!(struct_info.memory_kind, struct_memory_kind); } + fn fake_module_info( + path: &CStr, + functions: &[FunctionInfo], + types: &[&TypeInfo], + ) -> ModuleInfo { + ModuleInfo { + path: path.as_ptr(), + functions: functions.as_ptr(), + num_functions: functions.len() as u32, + types: types.as_ptr().cast::<*const TypeInfo>(), + num_types: types.len() as u32, + } + } + const FAKE_MODULE_PATH: &str = "path::to::module"; #[test] @@ -616,7 +664,7 @@ mod tests { let functions = &[fn_info]; let struct_name = CString::new(FAKE_STRUCT_NAME).expect("Invalid fake struct name"); - let struct_info = fake_struct_info(&struct_name, &[], &[], &[], &[], Default::default()); + let struct_info = fake_struct_info(&struct_name, &[], &[], &[], &[], 1, Default::default()); let struct_type_info = fake_struct_type_info(&struct_name, struct_info); let types = &[unsafe { mem::transmute(&struct_type_info) }]; diff --git a/crates/mun_codegen/src/code_gen.rs b/crates/mun_codegen/src/code_gen.rs index f4e5c5804..7eae75402 100644 --- a/crates/mun_codegen/src/code_gen.rs +++ b/crates/mun_codegen/src/code_gen.rs @@ -1,23 +1,29 @@ use crate::code_gen::linker::LinkerError; use crate::IrDatabase; use failure::Fail; -use hir::FileId; -use inkwell::module::Module; -use inkwell::passes::{PassManager, PassManagerBuilder}; -use inkwell::targets::{CodeModel, FileType, InitializationConfig, RelocMode, Target}; -use inkwell::OptimizationLevel; +use hir::{FileId, RelativePathBuf}; +use inkwell::targets::TargetData; +use inkwell::{ + module::{Linkage, Module}, + passes::{PassManager, PassManagerBuilder}, + targets::{CodeModel, FileType, InitializationConfig, RelocMode, Target}, + types::StructType, + values::{BasicValue, GlobalValue, IntValue, PointerValue, UnnamedAddress}, + AddressSpace, OptimizationLevel, +}; use std::io::{self, Write}; -use std::path::Path; +use std::{ + path::{Path, PathBuf}, + sync::Arc, +}; -mod abi_types; mod linker; +pub mod symbols; #[derive(Debug, Fail)] enum CodeGenerationError { #[fail(display = "{}", 0)] LinkerError(#[fail(cause)] LinkerError), - #[fail(display = "error linking modules: {}", 0)] - ModuleLinkerError(String), #[fail(display = "unknown target triple: {}", 0)] UnknownTargetTriple(String), #[fail(display = "error creating target machine")] @@ -34,87 +40,121 @@ impl From for CodeGenerationError { } } -/// Construct a shared object for the given `hir::FileId` at the specified output file location. -pub fn write_module_shared_object( - db: &impl IrDatabase, +pub struct ModuleBuilder<'a, D: IrDatabase> { + db: &'a D, file_id: FileId, - output_file_path: &Path, -) -> Result<(), failure::Error> { - let target = db.target(); - - // Construct a module for the assembly - let assembly_module = db.context().create_module( - output_file_path - .file_name() - .and_then(|s| s.to_str()) - .unwrap_or("unknown"), - ); - - // Initialize the x86 target - Target::initialize_x86(&InitializationConfig::default()); - - // Construct the LLVM target from the specified target. - let llvm_target = Target::from_triple(&target.llvm_target) - .map_err(|e| CodeGenerationError::UnknownTargetTriple(e.to_string()))?; - assembly_module.set_target(&llvm_target); - - // Construct target machine for machine code generation - let target_machine = llvm_target - .create_target_machine( - &target.llvm_target, - &target.options.cpu, - &target.options.features, - db.optimization_lvl(), - RelocMode::PIC, - CodeModel::Default, - ) - .ok_or(CodeGenerationError::CouldNotCreateTargetMachine)?; - - // Generate IR for the module and clone it so that we can modify it without modifying the - // cached value. - let module = db.module_ir(file_id); - assembly_module - .link_in_module(module.llvm_module.clone()) - .map_err(|e| CodeGenerationError::ModuleLinkerError(e.to_string()))?; - - // Generate the `get_info` method. - symbols::gen_reflection_ir( - db, - &target_machine, - &assembly_module, - &module.types, - &module.functions, - &module.dispatch_table, - ); - - // Optimize the assembly module - optimize_module(&assembly_module, db.optimization_lvl()); - - // Debug print the IR - //println!("{}", assembly_module.print_to_string().to_string()); - - // Generate object file - let obj_file = { - let obj = target_machine - .write_to_memory_buffer(&assembly_module, FileType::Object) - .map_err(|e| CodeGenerationError::CodeGenerationError(e.to_string()))?; - let mut obj_file = tempfile::NamedTempFile::new() - .map_err(CodeGenerationError::CouldNotCreateObjectFile)?; - obj_file - .write(obj.as_slice()) - .map_err(CodeGenerationError::CouldNotCreateObjectFile)?; - obj_file - }; - - // Construct a linker for the target - let mut linker = linker::create_with_target(&target); - linker.add_object(obj_file.path())?; - - // Link the object - linker.build_shared_object(&output_file_path)?; - linker.finalize()?; - - Ok(()) + _target: inkwell::targets::Target, + target_machine: inkwell::targets::TargetMachine, + assembly_module: Arc, +} + +impl<'a, D: IrDatabase> ModuleBuilder<'a, D> { + /// Construct module for the given `hir::FileId` at the specified output file location. + pub fn new(db: &'a mut D, file_id: FileId) -> Result { + let target = db.target(); + + // Construct a module for the assembly + let assembly_module = Arc::new( + db.context() + .create_module(db.file_relative_path(file_id).as_str()), + ); + + // Initialize the x86 target + Target::initialize_x86(&InitializationConfig::default()); + + // Retrieve the LLVM target using the specified target. + let llvm_target = Target::from_triple(&target.llvm_target) + .map_err(|e| CodeGenerationError::UnknownTargetTriple(e.to_string()))?; + assembly_module.set_target(&llvm_target); + + // Construct target machine for machine code generation + let target_machine = llvm_target + .create_target_machine( + &target.llvm_target, + &target.options.cpu, + &target.options.features, + db.optimization_lvl(), + RelocMode::PIC, + CodeModel::Default, + ) + .ok_or(CodeGenerationError::CouldNotCreateTargetMachine)?; + + // Initialize the module and target data + db.set_module(assembly_module.clone()); + + Ok(Self { + db, + file_id, + _target: llvm_target, + target_machine, + assembly_module, + }) + } + + /// Construct a shared object at the specified output file location. + pub fn finalize(&self, out_dir: Option<&Path>) -> Result { + // Generate IR for the module and clone it so that we can modify it without modifying the + // cached value. + let module = self.db.module_ir(self.file_id); + + // Generate the `get_info` method. + symbols::gen_reflection_ir( + self.db, + &self.assembly_module, + &module.functions, + &module.dispatch_table, + &module.type_table, + ); + + // Optimize the assembly module + optimize_module(&self.assembly_module, self.db.optimization_lvl()); + + // Debug print the IR + //println!("{}", assembly_module.print_to_string().to_string()); + + // Generate object file + let obj_file = { + let obj = self + .target_machine + .write_to_memory_buffer(&self.assembly_module, FileType::Object) + .map_err(|e| CodeGenerationError::CodeGenerationError(e.to_string()))?; + let mut obj_file = tempfile::NamedTempFile::new() + .map_err(CodeGenerationError::CouldNotCreateObjectFile)?; + obj_file + .write(obj.as_slice()) + .map_err(CodeGenerationError::CouldNotCreateObjectFile)?; + obj_file + }; + + let target = self.db.target(); + + // Construct a linker for the target + let mut linker = linker::create_with_target(&target); + linker.add_object(obj_file.path())?; + + let output_path = assembly_output_path(self.db, self.file_id, out_dir); + + // Link the object + linker.build_shared_object(&output_path)?; + linker.finalize()?; + + Ok(output_path) + } +} +/// Computes the output path for the assembly of the specified file. +fn assembly_output_path(db: &D, file_id: FileId, out_dir: Option<&Path>) -> PathBuf { + let relative_path: RelativePathBuf = db.file_relative_path(file_id); + let original_filename = Path::new(relative_path.file_name().unwrap()); + + // Add the `munlib` suffix to the original filename + let output_file_name = original_filename.with_extension("munlib"); + + // If there is an out dir specified, prepend the output directory + if let Some(out_dir) = out_dir { + out_dir.join(output_file_name) + } else { + output_file_name + } } /// Optimizes the specified LLVM `Module` using the default passes for the given @@ -128,4 +168,83 @@ fn optimize_module(module: &Module, optimization_lvl: OptimizationLevel) { module_pass_manager.run_on(module); } -pub mod symbols; +/// Intern a string by constructing a global value. Looks something like this: +/// ```c +/// const char[] GLOBAL_ = "str"; +/// ``` +pub(crate) fn intern_string(module: &Module, string: &str, name: &str) -> PointerValue { + let value = module.get_context().const_string(string, true); + gen_global(module, &value, name).as_pointer_value() +} + +/// Construct a global from the specified value +pub(crate) fn gen_global(module: &Module, value: &dyn BasicValue, name: &str) -> GlobalValue { + let global = module.add_global(value.as_basic_value_enum().get_type(), None, name); + global.set_linkage(Linkage::Private); + global.set_constant(true); + global.set_unnamed_address(UnnamedAddress::Global); + global.set_initializer(value); + global +} + +/// Generates a global array from the specified list of strings +pub(crate) fn gen_string_array( + module: &Module, + strings: impl Iterator, + name: &str, +) -> PointerValue { + let str_type = module.get_context().i8_type().ptr_type(AddressSpace::Const); + + let mut strings = strings.peekable(); + if strings.peek().is_none() { + str_type.ptr_type(AddressSpace::Const).const_null() + } else { + let strings = strings + .map(|s| intern_string(module, &s, name)) + .collect::>(); + + let strings_ir = str_type.const_array(&strings); + gen_global(module, &strings_ir, "").as_pointer_value() + } +} + +/// Generates a global array from the specified list of struct pointers +pub(crate) fn gen_struct_ptr_array( + module: &Module, + ir_type: StructType, + ptrs: &[PointerValue], + name: &str, +) -> PointerValue { + if ptrs.is_empty() { + ir_type + .ptr_type(AddressSpace::Const) + .ptr_type(AddressSpace::Const) + .const_null() + } else { + let ptr_array_ir = ir_type.ptr_type(AddressSpace::Const).const_array(&ptrs); + + gen_global(module, &ptr_array_ir, name).as_pointer_value() + } +} + +/// Generates a global array from the specified list of integers +pub(crate) fn gen_u16_array(module: &Module, integers: impl Iterator) -> PointerValue { + let u16_type = module.get_context().i16_type(); + + let mut integers = integers.peekable(); + if integers.peek().is_none() { + u16_type.ptr_type(AddressSpace::Const).const_null() + } else { + let integers = integers + .map(|i| u16_type.const_int(i, false)) + .collect::>(); + + let array_ir = u16_type.const_array(&integers); + gen_global(module, &array_ir, "").as_pointer_value() + } +} + +/// Create an inkwell TargetData from the target in the database +pub(crate) fn target_data_query(db: &impl IrDatabase) -> Arc { + Arc::new(TargetData::create(&db.target().data_layout)) +} diff --git a/crates/mun_codegen/src/code_gen/linker.rs b/crates/mun_codegen/src/code_gen/linker.rs index d829584c9..3bfa1c080 100644 --- a/crates/mun_codegen/src/code_gen/linker.rs +++ b/crates/mun_codegen/src/code_gen/linker.rs @@ -163,6 +163,7 @@ impl Linker for MsvcLinker { self.args.push("/DLL".to_owned()); self.args.push("/NOENTRY".to_owned()); self.args.push("/EXPORT:get_info".to_owned()); + self.args.push("/EXPORT:set_allocator_handle".to_owned()); self.args.push(format!("/IMPLIB:{}", dll_lib_path_str)); self.args.push(format!("/OUT:{}", dll_path_str)); Ok(()) diff --git a/crates/mun_codegen/src/code_gen/symbols.rs b/crates/mun_codegen/src/code_gen/symbols.rs index 6396cd06c..d827f458b 100644 --- a/crates/mun_codegen/src/code_gen/symbols.rs +++ b/crates/mun_codegen/src/code_gen/symbols.rs @@ -1,111 +1,57 @@ -use super::abi_types::{gen_abi_types, AbiTypes}; +use crate::code_gen::{gen_global, gen_struct_ptr_array, intern_string}; use crate::ir::{ + abi_types::{gen_abi_types, AbiTypes}, dispatch_table::{DispatchTable, DispatchableFunction}, function, + type_table::TypeTable, }; -use crate::type_info::{TypeGroup, TypeInfo}; -use crate::values::{BasicValue, GlobalValue}; -use crate::{CodeGenParams, IrDatabase}; +use crate::type_info::TypeInfo; +use crate::IrDatabase; use hir::Ty; use inkwell::{ attributes::Attribute, module::{Linkage, Module}, - targets::TargetMachine, - values::{FunctionValue, IntValue, PointerValue, StructValue, UnnamedAddress}, + values::{FunctionValue, GlobalValue, PointerValue, StructValue}, AddressSpace, }; -use std::collections::{HashMap, HashSet}; - -struct GlobalArrayValue(GlobalValue, usize); - -/// Construct an IR `MunTypeInfo` struct value for the specified `TypeInfo` -fn type_info_ir( - db: &D, - target: &TargetMachine, - module: &Module, - types: &AbiTypes, - global_type_info_lookup_table: &mut HashMap, - type_info: TypeInfo, -) -> PointerValue { - if let Some(value) = global_type_info_lookup_table.get(&type_info) { - *value - } else { - let context = module.get_context(); - let guid_values: [IntValue; 16] = array_init::array_init(|i| { - context - .i8_type() - .const_int(u64::from(type_info.guid.b[i]), false) - }); - - let type_info_ir = context.const_struct( - &[ - context.i8_type().const_array(&guid_values).into(), - intern_string(module, &type_info.name).into(), - context - .i8_type() - .const_int(type_info.group.clone().into(), false) - .into(), - ], - false, - ); - - let type_info_ir = match type_info.group { - TypeGroup::FundamentalTypes => type_info_ir, - TypeGroup::StructTypes(s) => { - let struct_info_ir = - gen_struct_info(db, target, module, types, global_type_info_lookup_table, s); - context.const_struct(&[type_info_ir.into(), struct_info_ir.into()], false) - } - }; - - let type_info_ir = gen_global(module, &type_info_ir, "").as_pointer_value(); - global_type_info_lookup_table.insert(type_info, type_info_ir); - type_info_ir - } -} - -/// Intern a string by constructing a global value. Looks something like this: -/// ```c -/// const char[] GLOBAL_ = "str"; -/// ``` -fn intern_string(module: &Module, str: &str) -> PointerValue { - let value = module.get_context().const_string(str, true); - gen_global(module, &value, ".str").as_pointer_value() -} +use std::collections::HashMap; /// Construct a `MunFunctionSignature` struct for the specified HIR function. fn gen_signature_from_function( db: &D, module: &Module, types: &AbiTypes, - global_type_info_lookup_table: &HashMap, + type_table: &TypeTable, function: hir::Function, ) -> StructValue { - let name_str = intern_string(&module, &function.name(db).to_string()); + let name = function.name(db).to_string(); + + let name_ir = intern_string(&module, &name, &name); let _visibility = match function.visibility(db) { hir::Visibility::Public => 0, _ => 1, }; let fn_sig = function.ty(db).callable_sig(db).unwrap(); - let ret_type_ir = gen_signature_return_type( - db, - types, - global_type_info_lookup_table, - fn_sig.ret().clone(), - ); + let ret_type_ir = gen_signature_return_type(db, types, type_table, fn_sig.ret().clone()); + + let param_types: Vec = fn_sig + .params() + .iter() + .map(|ty| type_table.get(&db.type_info(ty.clone())).unwrap()) + .collect(); - let params_type_ir = gen_type_info_ptr_array( + let param_types = gen_struct_ptr_array( module, - types, - global_type_info_lookup_table, - fn_sig.params().iter().map(|ty| db.type_info(ty.clone())), + types.type_info_type, + ¶m_types, + &format!("fn_sig::<{}>::arg_types", name), ); let num_params = fn_sig.params().len(); types.function_signature_type.const_named_struct(&[ - name_str.into(), - params_type_ir.into(), + name_ir.into(), + param_types.into(), ret_type_ir.into(), module .get_context() @@ -121,10 +67,14 @@ fn gen_signature_from_dispatch_entry( db: &D, module: &Module, types: &AbiTypes, - global_type_info_lookup_table: &HashMap, + type_table: &TypeTable, function: &DispatchableFunction, ) -> StructValue { - let name_str = intern_string(&module, &function.prototype.name); + let name_str = intern_string( + &module, + &function.prototype.name, + &format!("fn_sig::<{}>::name", function.prototype.name), + ); // let _visibility = match function.visibility(db) { // hir::Visibility::Public => 0, // _ => 1, @@ -132,20 +82,26 @@ fn gen_signature_from_dispatch_entry( let ret_type_ir = gen_signature_return_type_from_type_info( db, types, - global_type_info_lookup_table, + type_table, function.prototype.ret_type.clone(), ); - let params_type_ir = gen_type_info_ptr_array( + let param_types: Vec = function + .prototype + .arg_types + .iter() + .map(|type_info| type_table.get(type_info).unwrap()) + .collect(); + let param_types = gen_struct_ptr_array( module, - types, - global_type_info_lookup_table, - function.prototype.arg_types.iter().cloned(), + types.type_info_type, + ¶m_types, + &format!("{}_param_types", function.prototype.name), ); let num_params = function.prototype.arg_types.len(); types.function_signature_type.const_named_struct(&[ name_str.into(), - params_type_ir.into(), + param_types.into(), ret_type_ir.into(), module .get_context() @@ -156,76 +112,18 @@ fn gen_signature_from_dispatch_entry( ]) } -/// Recursively expands an array of TypeInfo into an array of IR pointers -fn expand_type_info( - db: &D, - target: &TargetMachine, - module: &Module, - types: &AbiTypes, - global_type_info_lookup_table: &mut HashMap, - hir_types: impl Iterator, -) -> PointerValue { - let mut hir_types = hir_types.peekable(); - if hir_types.peek().is_none() { - types - .type_info_type - .ptr_type(AddressSpace::Const) - .ptr_type(AddressSpace::Const) - .const_null() - } else { - let type_infos = hir_types - .map(|ty| type_info_ir(db, target, module, types, global_type_info_lookup_table, ty)) - .collect::>(); - - let type_array_ir = types - .type_info_type - .ptr_type(AddressSpace::Const) - .const_array(&type_infos); - - gen_global(module, &type_array_ir, "").as_pointer_value() - } -} - -/// Generates IR for an array of TypeInfo values -fn gen_type_info_ptr_array( - module: &Module, - types: &AbiTypes, - global_type_info_lookup_table: &HashMap, - hir_types: impl Iterator, -) -> PointerValue { - let mut hir_types = hir_types.peekable(); - if hir_types.peek().is_none() { - types - .type_info_type - .ptr_type(AddressSpace::Const) - .ptr_type(AddressSpace::Const) - .const_null() - } else { - let type_infos = hir_types - .map(|ty| *global_type_info_lookup_table.get(&ty).unwrap()) - .collect::>(); - - let type_array_ir = types - .type_info_type - .ptr_type(AddressSpace::Const) - .const_array(&type_infos); - - gen_global(module, &type_array_ir, "").as_pointer_value() - } -} - /// Given a function, construct a pointer to a `MunTypeInfo` global that represents the return type /// of the function; or `null` if the return type is empty. fn gen_signature_return_type( db: &D, types: &AbiTypes, - global_type_info_lookup_table: &HashMap, + type_table: &TypeTable, ret_type: Ty, ) -> PointerValue { gen_signature_return_type_from_type_info( db, types, - global_type_info_lookup_table, + type_table, if ret_type.is_empty() { None } else { @@ -239,11 +137,11 @@ fn gen_signature_return_type( fn gen_signature_return_type_from_type_info( _db: &D, types: &AbiTypes, - global_type_info_lookup_table: &HashMap, + type_table: &TypeTable, ret_type: Option, ) -> PointerValue { if let Some(ret_type) = ret_type { - *global_type_info_lookup_table.get(&ret_type).unwrap() + type_table.get(&ret_type).unwrap() } else { types .type_info_type @@ -258,9 +156,9 @@ fn gen_function_info_array<'a, D: IrDatabase>( db: &D, module: &Module, types: &AbiTypes, - global_type_info_lookup_table: &HashMap, + type_table: &TypeTable, functions: impl Iterator, -) -> GlobalArrayValue { +) -> GlobalValue { let function_infos: Vec = functions .map(|(f, value)| { // Get the function from the cloned module and modify the linkage of the function. @@ -270,8 +168,7 @@ fn gen_function_info_array<'a, D: IrDatabase>( value.set_linkage(Linkage::Private); // Generate the signature from the function - let signature = - gen_signature_from_function(db, module, types, global_type_info_lookup_table, *f); + let signature = gen_signature_from_function(db, module, types, type_table, *f); // Generate the function info value types.function_info_type.const_named_struct(&[ @@ -280,126 +177,8 @@ fn gen_function_info_array<'a, D: IrDatabase>( ]) }) .collect(); - let num_functions = function_infos.len(); let function_infos = types.function_info_type.const_array(&function_infos); - - GlobalArrayValue( - gen_global(module, &function_infos, "fn.get_info.functions"), - num_functions, - ) -} - -/// Construct a global that holds a reference to all structs. e.g.: -/// MunStructInfo[] info = { ... } -fn gen_struct_info( - db: &D, - target: &TargetMachine, - module: &Module, - types: &AbiTypes, - global_type_info_lookup_table: &mut HashMap, - s: hir::Struct, -) -> StructValue { - let name_str = intern_string(&module, &s.name(db).to_string()); - - let fields = s.fields(db); - let field_names = fields.iter().map(|field| field.name(db).to_string()); - let (field_names, num_fields) = gen_string_array(module, field_names); - - let field_types = expand_type_info( - db, - target, - module, - types, - global_type_info_lookup_table, - fields.iter().map(|field| db.type_info(field.ty(db))), - ); - - let target_data = target.get_target_data(); - let t = db.struct_ty(s); - let field_offsets = - (0..fields.len()).map(|idx| target_data.offset_of_element(&t, idx as u32).unwrap()); - let (field_offsets, _) = gen_u16_array(module, field_offsets); - - let field_sizes = fields.iter().map(|field| { - target_data.get_store_size(&db.type_ir( - field.ty(db), - CodeGenParams { - make_marshallable: false, - }, - )) - }); - let (field_sizes, _) = gen_u16_array(module, field_sizes); - - types.struct_info_type.const_named_struct(&[ - name_str.into(), - field_names.into(), - field_types.into(), - field_offsets.into(), - field_sizes.into(), - module - .get_context() - .i16_type() - .const_int(num_fields as u64, false) - .into(), - module - .get_context() - .i8_type() - .const_int(s.data(db).memory_kind.clone().into(), false) - .into(), - ]) -} - -/// Constructs a global from the specified list of strings -fn gen_string_array( - module: &Module, - strings: impl Iterator, -) -> (PointerValue, usize) { - let str_type = module.get_context().i8_type().ptr_type(AddressSpace::Const); - - let mut strings = strings.peekable(); - if strings.peek().is_none() { - (str_type.ptr_type(AddressSpace::Const).const_null(), 0) - } else { - let strings = strings - .map(|s| intern_string(module, &s)) - .collect::>(); - - let strings_ir = str_type.const_array(&strings); - ( - gen_global(module, &strings_ir, "").as_pointer_value(), - strings.len(), - ) - } -} - -/// Constructs a global from the specified list of strings -fn gen_u16_array(module: &Module, integers: impl Iterator) -> (PointerValue, usize) { - let u16_type = module.get_context().i16_type(); - - let mut integers = integers.peekable(); - if integers.peek().is_none() { - (u16_type.ptr_type(AddressSpace::Const).const_null(), 0) - } else { - let integers = integers - .map(|i| u16_type.const_int(i, false)) - .collect::>(); - - let array_ir = u16_type.const_array(&integers); - ( - gen_global(module, &array_ir, "").as_pointer_value(), - integers.len(), - ) - } -} - -/// Construct a global from the specified value -fn gen_global(module: &Module, value: &dyn BasicValue, name: &str) -> GlobalValue { - let global = module.add_global(value.as_basic_value_enum().get_type(), None, name); - global.set_linkage(Linkage::Private); - global.set_constant(true); - global.set_unnamed_address(UnnamedAddress::Global); - global.set_initializer(value); - global + gen_global(module, &function_infos, "fn.get_info.functions") } /// Generate the dispatch table information. e.g.: @@ -410,22 +189,14 @@ fn gen_dispatch_table( db: &D, module: &Module, types: &AbiTypes, - global_type_info_lookup_table: &HashMap, dispatch_table: &DispatchTable, + type_table: &TypeTable, ) -> StructValue { // Generate a vector with all the function signatures let signatures: Vec = dispatch_table .entries() .iter() - .map(|entry| { - gen_signature_from_dispatch_entry( - db, - module, - types, - global_type_info_lookup_table, - entry, - ) - }) + .map(|entry| gen_signature_from_dispatch_entry(db, module, types, type_table, entry)) .collect(); // Construct an IR array from the signatures @@ -470,62 +241,41 @@ fn gen_dispatch_table( /// for the ABI that `get_info` exposes. pub(super) fn gen_reflection_ir( db: &impl IrDatabase, - target: &TargetMachine, module: &Module, - types: &HashSet, function_map: &HashMap, dispatch_table: &DispatchTable, + type_table: &TypeTable, ) { // Get all the types let abi_types = gen_abi_types(module.get_context()); - let mut global_type_info_lookup_table = HashMap::new(); - let num_types = types.len(); - let types = expand_type_info( - db, - target, - module, - &abi_types, - &mut global_type_info_lookup_table, - types.iter().cloned(), - ); - - let GlobalArrayValue(function_info, num_functions) = gen_function_info_array( - db, - module, - &abi_types, - &global_type_info_lookup_table, - function_map.iter(), - ); + let num_functions = function_map.len(); + let function_info = + gen_function_info_array(db, module, &abi_types, type_table, function_map.iter()); // Construct the module info struct let module_info = abi_types.module_info_type.const_named_struct(&[ - intern_string(module, "").into(), + intern_string(module, "", "module_info::path").into(), function_info.as_pointer_value().into(), module .get_context() .i32_type() .const_int(num_functions as u64, false) .into(), - types.into(), + type_table.pointer_value().into(), module .get_context() .i32_type() - .const_int(num_types as u64, false) + .const_int(type_table.num_types() as u64, false) .into(), ]); // Construct the dispatch table struct - let dispatch_table = gen_dispatch_table( - db, - module, - &abi_types, - &global_type_info_lookup_table, - dispatch_table, - ); + let dispatch_table = gen_dispatch_table(db, module, &abi_types, dispatch_table, type_table); // Construct the actual `get_info` function gen_get_info_fn(db, module, &abi_types, module_info, dispatch_table); + gen_set_allocator_handle_fn(db, module); } /// Construct the actual `get_info` function. @@ -617,3 +367,33 @@ fn gen_get_info_fn( // Run the function optimizer on the generate function function::create_pass_manager(&module, db.optimization_lvl()).run_on(&get_symbols_fn); } + +fn gen_set_allocator_handle_fn(db: &impl IrDatabase, module: &Module) { + let context = module.get_context(); + let allocator_handle_type = context.i8_type().ptr_type(AddressSpace::Generic); + + let set_allocator_handle_fn_type = context + .void_type() + .fn_type(&[allocator_handle_type.into()], false); + + let set_allocator_handle_fn = module.add_function( + "set_allocator_handle", + set_allocator_handle_fn_type, + Some(Linkage::DLLExport), + ); + + let builder = db.context().create_builder(); + let body_ir = db + .context() + .append_basic_block(&set_allocator_handle_fn, "body"); + builder.position_at_end(&body_ir); + + if let Some(allocator_handle_global) = module.get_global("allocatorHandle") { + builder.build_store( + allocator_handle_global.as_pointer_value(), + set_allocator_handle_fn.get_nth_param(0).unwrap(), + ); + } + + builder.build_return(None); +} diff --git a/crates/mun_codegen/src/db.rs b/crates/mun_codegen/src/db.rs index 332c4ee73..1e88e5007 100644 --- a/crates/mun_codegen/src/db.rs +++ b/crates/mun_codegen/src/db.rs @@ -1,8 +1,12 @@ #![allow(clippy::type_repetition_in_bounds)] use crate::{ir::module::ModuleIR, type_info::TypeInfo, CodeGenParams, Context}; -use inkwell::types::StructType; -use inkwell::{types::AnyTypeEnum, OptimizationLevel}; +use inkwell::{ + module::Module, + targets::TargetData, + types::{AnyTypeEnum, StructType}, + OptimizationLevel, +}; use mun_target::spec::Target; use std::sync::Arc; @@ -14,6 +18,10 @@ pub trait IrDatabase: hir::HirDatabase { #[salsa::input] fn context(&self) -> Arc; + /// Returns the LLVM module that should be used for all generation steps. + #[salsa::input] + fn module(&self) -> Arc; + /// Gets the optimization level for generation. #[salsa::input] fn optimization_lvl(&self) -> OptimizationLevel; @@ -22,6 +30,10 @@ pub trait IrDatabase: hir::HirDatabase { #[salsa::input] fn target(&self) -> Target; + /// Returns the target machine's data layout for code generation. + #[salsa::invoke(crate::code_gen::target_data_query)] + fn target_data(&self) -> Arc; + /// Given a type and code generation parameters, return the corresponding IR type. #[salsa::invoke(crate::ir::ty::ir_query)] fn type_ir(&self, ty: hir::Ty, params: CodeGenParams) -> AnyTypeEnum; diff --git a/crates/mun_codegen/src/intrinsics.rs b/crates/mun_codegen/src/intrinsics.rs index d4106231d..46492553c 100644 --- a/crates/mun_codegen/src/intrinsics.rs +++ b/crates/mun_codegen/src/intrinsics.rs @@ -2,6 +2,7 @@ use crate::ir::dispatch_table::FunctionPrototype; use crate::type_info::TypeInfo; use inkwell::context::Context; use inkwell::types::FunctionType; +use std::ffi; #[macro_use] mod macros; @@ -17,8 +18,9 @@ pub trait Intrinsic: Sync { } intrinsics! { - /// Allocates memory from the runtime to use in code. - pub fn malloc(size: u64, alignment: u64) -> *mut u8; - /// Allocates memory for and clones the specified type located at `src` into it. - pub fn clone(src: *const u8, ty: *const TypeInfo) -> *mut u8; + /// Allocates memory for the specified `type` in the allocator referred to by `alloc_handle`. + pub fn new(type: *const TypeInfo, alloc_handle: *mut ffi::c_void) -> *const *mut ffi::c_void; + /// Allocates memory for and clones the specified type located at `src` into it. Memory is + /// allocated in the allocator referred to by `alloc_handle`. + pub fn clone(src: *const ffi::c_void, alloc_handle: *mut ffi::c_void) -> *const *mut ffi::c_void; } diff --git a/crates/mun_codegen/src/ir.rs b/crates/mun_codegen/src/ir.rs index fab6ed00e..74e1b103b 100644 --- a/crates/mun_codegen/src/ir.rs +++ b/crates/mun_codegen/src/ir.rs @@ -5,13 +5,16 @@ use inkwell::types::{ }; use inkwell::AddressSpace; +pub(crate) mod abi_types; pub mod adt; pub mod body; #[macro_use] pub(crate) mod dispatch_table; pub mod function; +mod intrinsics; pub mod module; pub mod ty; +pub(crate) mod type_table; /// Try to down cast an `AnyTypeEnum` into a `BasicTypeEnum`. fn try_convert_any_to_basic(ty: AnyTypeEnum) -> Option { @@ -191,6 +194,20 @@ impl IsPointerType for *const TypeInfo { } } +// HACK: Manually add `*const c_void` +impl IsPointerType for *const std::ffi::c_void { + fn ir_type(context: &Context) -> PointerType { + context.i8_type().ptr_type(AddressSpace::Const) + } +} + +// HACK: Manually add `*mut c_void` +impl IsPointerType for *mut std::ffi::c_void { + fn ir_type(context: &Context) -> PointerType { + context.i8_type().ptr_type(AddressSpace::Generic) + } +} + impl> IsPointerType for *mut T { fn ir_type(context: &Context) -> PointerType { T::ir_type(context).ptr_type(AddressSpace::Generic) diff --git a/crates/mun_codegen/src/code_gen/abi_types.rs b/crates/mun_codegen/src/ir/abi_types.rs similarity index 96% rename from crates/mun_codegen/src/code_gen/abi_types.rs rename to crates/mun_codegen/src/ir/abi_types.rs index 4ed140cb1..cc06b71be 100644 --- a/crates/mun_codegen/src/code_gen/abi_types.rs +++ b/crates/mun_codegen/src/ir/abi_types.rs @@ -2,7 +2,7 @@ use inkwell::context::ContextRef; use inkwell::types::{ArrayType, IntType, StructType}; use inkwell::AddressSpace; -pub(super) struct AbiTypes { +pub(crate) struct AbiTypes { pub guid_type: ArrayType, pub type_group_type: IntType, pub privacy_type: IntType, @@ -16,7 +16,7 @@ pub(super) struct AbiTypes { } /// Returns an `AbiTypes` struct that contains references to all LLVM ABI types. -pub(super) fn gen_abi_types(context: ContextRef) -> AbiTypes { +pub(crate) fn gen_abi_types(context: ContextRef) -> AbiTypes { let str_type = context.i8_type().ptr_type(AddressSpace::Const); // Construct the `MunGuid` type @@ -78,6 +78,7 @@ pub(super) fn gen_abi_types(context: ContextRef) -> AbiTypes { context.i16_type().ptr_type(AddressSpace::Const).into(), // field_offsets context.i16_type().ptr_type(AddressSpace::Const).into(), // field_sizes context.i16_type().into(), // num_fields + context.i16_type().into(), // alignment context.i8_type().into(), // memory_kind ], false, diff --git a/crates/mun_codegen/src/ir/body.rs b/crates/mun_codegen/src/ir/body.rs index 2411b6b63..7676d8f4f 100644 --- a/crates/mun_codegen/src/ir/body.rs +++ b/crates/mun_codegen/src/ir/body.rs @@ -1,6 +1,7 @@ use crate::intrinsics; use crate::{ - ir::dispatch_table::DispatchTable, ir::try_convert_any_to_basic, CodeGenParams, IrDatabase, + ir::{dispatch_table::DispatchTable, try_convert_any_to_basic, type_table::TypeTable}, + CodeGenParams, IrDatabase, }; use hir::{ ArenaId, ArithOp, BinaryOp, Body, CmpOp, Expr, ExprId, HirDisplay, InferenceResult, Literal, @@ -8,14 +9,13 @@ use hir::{ }; use inkwell::{ builder::Builder, - module::Module, values::{BasicValueEnum, CallSiteValue, FloatValue, FunctionValue, IntValue, StructValue}, AddressSpace, FloatPredicate, IntPredicate, }; use std::{collections::HashMap, mem, sync::Arc}; use inkwell::basic_block::BasicBlock; -use inkwell::values::{AggregateValueEnum, PointerValue}; +use inkwell::values::{AggregateValueEnum, GlobalValue, PointerValue}; struct LoopInfo { break_values: Vec<( @@ -27,7 +27,6 @@ struct LoopInfo { pub(crate) struct BodyIrGenerator<'a, 'b, D: IrDatabase> { db: &'a D, - module: &'a Module, body: Arc, infer: Arc, builder: Builder, @@ -37,34 +36,37 @@ pub(crate) struct BodyIrGenerator<'a, 'b, D: IrDatabase> { pat_to_name: HashMap, function_map: &'a HashMap, dispatch_table: &'b DispatchTable, + type_table: &'b TypeTable, active_loop: Option, hir_function: hir::Function, params: CodeGenParams, + allocator_handle_global: Option, } impl<'a, 'b, D: IrDatabase> BodyIrGenerator<'a, 'b, D> { pub fn new( db: &'a D, - module: &'a Module, - hir_function: hir::Function, - ir_function: FunctionValue, + function: (hir::Function, FunctionValue), function_map: &'a HashMap, dispatch_table: &'b DispatchTable, + type_table: &'b TypeTable, params: CodeGenParams, + allocator_handle_global: Option, ) -> Self { + let (hir_function, ir_function) = function; + // Get the type information from the `hir::Function` let body = hir_function.body(db); let infer = hir_function.infer(db); // Construct a builder for the IR function - let context = module.get_context(); + let context = db.context(); let builder = context.create_builder(); let body_ir = context.append_basic_block(&ir_function, "body"); builder.position_at_end(&body_ir); BodyIrGenerator { db, - module, body, infer, builder, @@ -74,9 +76,11 @@ impl<'a, 'b, D: IrDatabase> BodyIrGenerator<'a, 'b, D> { pat_to_name: HashMap::default(), function_map, dispatch_table, + type_table, active_loop: None, hir_function, params, + allocator_handle_global, } } @@ -238,21 +242,16 @@ impl<'a, 'b, D: IrDatabase> BodyIrGenerator<'a, 'b, D> { fn gen_literal(&mut self, lit: &Literal) -> BasicValueEnum { match lit { Literal::Int(v) => self - .module - .get_context() + .db + .context() .i64_type() .const_int(unsafe { mem::transmute::(*v) }, true) .into(), - Literal::Float(v) => self - .module - .get_context() - .f64_type() - .const_float(*v as f64) - .into(), + Literal::Float(v) => self.db.context().f64_type().const_float(*v as f64).into(), Literal::Bool(value) => { - let ty = self.module.get_context().bool_type(); + let ty = self.db.context().bool_type(); if *value { ty.const_all_ones().into() } else { @@ -266,7 +265,7 @@ impl<'a, 'b, D: IrDatabase> BodyIrGenerator<'a, 'b, D> { /// Constructs an empty struct value e.g. `{}` fn gen_empty(&mut self) -> BasicValueEnum { - self.module.get_context().const_struct(&[], false).into() + self.db.context().const_struct(&[], false).into() } /// Allocate a struct literal either on the stack or the heap based on the type of the struct. @@ -301,22 +300,39 @@ impl<'a, 'b, D: IrDatabase> BodyIrGenerator<'a, 'b, D> { struct_lit: StructValue, ) -> BasicValueEnum { let struct_ir_ty = self.db.struct_ty(hir_struct); - let malloc_fn_ptr = self + let new_fn_ptr = self .dispatch_table - .gen_intrinsic_lookup(&self.builder, &intrinsics::malloc); - let mem_ptr = self + .gen_intrinsic_lookup(&self.builder, &intrinsics::new); + + let type_info_ptr = self + .type_table + .gen_type_info_lookup(&self.builder, &self.db.type_info(hir_struct.ty(self.db))); + + // HACK: We should be able to use pointers for built-in struct types like `TypeInfo` in intrinsics + let type_info_ptr = self.builder.build_bitcast( + type_info_ptr, + self.db.context().i8_type().ptr_type(AddressSpace::Const), + "type_info_ptr_to_i8_ptr", + ); + + let allocator_handle = self.builder.build_load( + self.allocator_handle_global + .expect("no allocator handle was specified, this is required for structs") + .as_pointer_value(), + "allocator_handle", + ); + + let object_ptr = self .builder - .build_call( - malloc_fn_ptr, - &[ - struct_ir_ty.size_of().unwrap().into(), - struct_ir_ty.get_alignment().into(), - ], - "malloc", - ) + .build_call(new_fn_ptr, &[type_info_ptr, allocator_handle], "new") .try_as_basic_value() .left() - .unwrap(); + .unwrap() + .into_pointer_value(); + let mem_ptr = self + .builder + .build_load(object_ptr, "mem_ptr") + .into_pointer_value(); let struct_ptr = self .builder .build_bitcast( @@ -326,7 +342,7 @@ impl<'a, 'b, D: IrDatabase> BodyIrGenerator<'a, 'b, D> { ) .into_pointer_value(); self.builder.build_store(struct_ptr, struct_lit); - struct_ptr.into() + object_ptr.into() } /// Generates IR for a record literal, e.g. `Foo { a: 1.23, b: 4 }` @@ -463,21 +479,26 @@ impl<'a, 'b, D: IrDatabase> BodyIrGenerator<'a, 'b, D> { /// Given an expression and the type of the expression, optionally dereference the value. fn opt_deref_value(&mut self, ty: hir::Ty, value: BasicValueEnum) -> BasicValueEnum { + /// Derefs a heap-allocated value. As we introduce a layer of indirection for hot + /// reloading, we need to first load the pointer that points to the memory block. + fn deref_heap_value(builder: &Builder, value: BasicValueEnum) -> BasicValueEnum { + let mem_ptr = builder + .build_load(value.into_pointer_value(), "mem_ptr") + .into_pointer_value(); + + builder.build_load(mem_ptr, "deref") + } + match ty { hir::Ty::Apply(hir::ApplicationTy { ctor: hir::TypeCtor::Struct(s), .. }) => match s.data(self.db).memory_kind { - hir::StructMemoryKind::GC => { - self.builder.build_load(value.into_pointer_value(), "deref") - } - hir::StructMemoryKind::Value => { - if self.params.make_marshallable { - self.builder.build_load(value.into_pointer_value(), "deref") - } else { - value - } + hir::StructMemoryKind::GC => deref_heap_value(&self.builder, value), + hir::StructMemoryKind::Value if self.params.make_marshallable => { + deref_heap_value(&self.builder, value) } + hir::StructMemoryKind::Value => value, }, _ => value, } @@ -746,7 +767,7 @@ impl<'a, 'b, D: IrDatabase> BodyIrGenerator<'a, 'b, D> { .into_int_value(); // Generate the code blocks to branch to - let context = self.module.get_context(); + let context = self.db.context(); let mut then_block = context.append_basic_block(&self.fn_value, "then"); let else_block_and_expr = match &else_branch { Some(else_branch) => Some(( @@ -868,7 +889,7 @@ impl<'a, 'b, D: IrDatabase> BodyIrGenerator<'a, 'b, D> { condition_expr: ExprId, body_expr: ExprId, ) -> Option { - let context = self.module.get_context(); + let context = self.db.context(); let cond_block = context.append_basic_block(&self.fn_value, "whilecond"); let loop_block = context.append_basic_block(&self.fn_value, "while"); let exit_block = context.append_basic_block(&self.fn_value, "afterwhile"); @@ -907,7 +928,7 @@ impl<'a, 'b, D: IrDatabase> BodyIrGenerator<'a, 'b, D> { } fn gen_loop(&mut self, _expr: ExprId, body_expr: ExprId) -> Option { - let context = self.module.get_context(); + let context = self.db.context(); let loop_block = context.append_basic_block(&self.fn_value, "loop"); let exit_block = context.append_basic_block(&self.fn_value, "exit"); diff --git a/crates/mun_codegen/src/ir/dispatch_table.rs b/crates/mun_codegen/src/ir/dispatch_table.rs index 65071fb57..af9eaff15 100644 --- a/crates/mun_codegen/src/ir/dispatch_table.rs +++ b/crates/mun_codegen/src/ir/dispatch_table.rs @@ -1,4 +1,3 @@ -use crate::intrinsics; use crate::values::FunctionValue; use crate::{CodeGenParams, IrDatabase}; use inkwell::module::Module; @@ -8,7 +7,7 @@ use inkwell::values::{BasicValueEnum, PointerValue}; use crate::intrinsics::Intrinsic; use crate::type_info::TypeInfo; use hir::{Body, Expr, ExprId, InferenceResult}; -use std::collections::HashMap; +use std::collections::{BTreeMap, HashMap}; use std::sync::Arc; /// A dispatch table in IR is a struct that contains pointers to all functions that are called from @@ -36,7 +35,7 @@ pub struct DispatchTable { } /// A `FunctionPrototype` defines a unique signature that can be added to the dispatch table. -#[derive(Debug, Clone, Eq, PartialEq, Hash)] +#[derive(Debug, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)] pub struct FunctionPrototype { pub name: String, pub arg_types: Vec, @@ -150,8 +149,12 @@ struct TypedDispatchableFunction { impl<'a, D: IrDatabase> DispatchTableBuilder<'a, D> { /// Creates a new builder that can generate a dispatch function. - pub fn new(db: &'a D, module: &'a Module) -> Self { - DispatchTableBuilder { + pub fn new( + db: &'a D, + module: &'a Module, + intrinsics: BTreeMap, + ) -> Self { + let mut table = DispatchTableBuilder { db, module, function_to_idx: Default::default(), @@ -159,7 +162,26 @@ impl<'a, D: IrDatabase> DispatchTableBuilder<'a, D> { entries: Default::default(), table_ref: None, table_type: module.get_context().opaque_struct_type("DispatchTable"), + }; + + if !intrinsics.is_empty() { + table.ensure_table_ref(); + + // Use a `BTreeMap` to guarantee deterministically ordered output + for (prototype, ir_type) in intrinsics.into_iter() { + let index = table.entries.len(); + table.entries.push(TypedDispatchableFunction { + function: DispatchableFunction { + prototype: prototype.clone(), + hir: None, + }, + ir_type, + }); + + table.prototype_to_idx.insert(prototype, index); + } } + table } /// Creates the global dispatch table in the module if it does not exist. @@ -180,46 +202,11 @@ impl<'a, D: IrDatabase> DispatchTableBuilder<'a, D> { if let Expr::Call { callee, .. } = expr { match infer[*callee].as_callable_def() { Some(hir::CallableDef::Function(def)) => self.collect_fn_def(def), - Some(hir::CallableDef::Struct(s)) => { - // self.collect_intrinsic(&intrinsics::new); - self.collect_intrinsic(&intrinsics::clone); - // self.collect_intrinsic(&intrinsics::drop); - if s.data(self.db).memory_kind == hir::StructMemoryKind::GC { - self.collect_intrinsic(&intrinsics::malloc) - } - } + Some(hir::CallableDef::Struct(_)) => (), None => panic!("expected a callable expression"), } } - if let Expr::RecordLit { .. } = expr { - let struct_ty = infer[expr_id].clone(); - let hir_struct = struct_ty.as_struct().unwrap(); // Can only really get here if the type is a struct - // self.collect_intrinsic(&intrinsics::new); - self.collect_intrinsic(&intrinsics::clone); - // self.collect_intrinsic(&intrinsics::drop); - if hir_struct.data(self.db).memory_kind == hir::StructMemoryKind::GC { - self.collect_intrinsic(&intrinsics::malloc) - } - } - - if let Expr::Path(path) = expr { - let resolver = hir::resolver_for_expr(body.clone(), self.db, expr_id); - let resolution = resolver - .resolve_path_without_assoc_items(self.db, path) - .take_values() - .expect("unknown path"); - - if let hir::Resolution::Def(hir::ModuleDef::Struct(s)) = resolution { - // self.collect_intrinsic(&intrinsics::new); - self.collect_intrinsic(&intrinsics::clone); - // self.collect_intrinsic(&intrinsics::drop); - if s.data(self.db).memory_kind == hir::StructMemoryKind::GC { - self.collect_intrinsic(&intrinsics::malloc) - } - } - } - // Recurse further expr.walk_child_exprs(|expr_id| self.collect_expr(expr_id, body, infer)) } @@ -272,38 +259,12 @@ impl<'a, D: IrDatabase> DispatchTableBuilder<'a, D> { } } - /// Collects a call to an intrinsic function. - #[allow(clippy::map_entry)] - fn collect_intrinsic(&mut self, intrinsic: &impl Intrinsic) { - self.ensure_table_ref(); - - // If the function is not yet contained in the table add it - let prototype = intrinsic.prototype(); - if !self.prototype_to_idx.contains_key(&prototype) { - let index = self.entries.len(); - self.entries.push(TypedDispatchableFunction { - function: DispatchableFunction { - prototype: prototype.clone(), - hir: None, - }, - ir_type: intrinsic.ir_type(&self.module.get_context()), - }); - - self.prototype_to_idx.insert(prototype, index); - } - } - /// Collect all the call expressions from the specified body with the given type inference /// result. pub fn collect_body(&mut self, body: &Arc, infer: &InferenceResult) { self.collect_expr(body.body_expr(), body, infer); } - /// Collect the call expression from the body of a wrapper for the specified function. - pub fn collect_wrapper_body(&mut self, _function: hir::Function) { - self.collect_intrinsic(&intrinsics::malloc) - } - /// This creates the final DispatchTable with all *called* functions from within the module /// # Parameters /// * **functions**: Mapping of *defined* Mun functions to their respective IR values. diff --git a/crates/mun_codegen/src/ir/function.rs b/crates/mun_codegen/src/ir/function.rs index a50bfbc6b..8dbd2c078 100644 --- a/crates/mun_codegen/src/ir/function.rs +++ b/crates/mun_codegen/src/ir/function.rs @@ -1,6 +1,5 @@ -use crate::ir::body::BodyIrGenerator; -use crate::ir::dispatch_table::DispatchTable; -use crate::values::FunctionValue; +use crate::ir::{body::BodyIrGenerator, dispatch_table::DispatchTable, type_table::TypeTable}; +use crate::values::{FunctionValue, GlobalValue}; use crate::{CodeGenParams, IrDatabase, Module, OptimizationLevel}; use inkwell::passes::{PassManager, PassManagerBuilder}; use inkwell::types::AnyTypeEnum; @@ -43,52 +42,48 @@ pub(crate) fn gen_signature( /// Generates the body of a `hir::Function` for an associated `FunctionValue`. pub(crate) fn gen_body<'a, 'b, D: IrDatabase>( db: &'a D, - hir_function: hir::Function, - llvm_function: FunctionValue, - module: &'a Module, + function: (hir::Function, FunctionValue), llvm_functions: &'a HashMap, dispatch_table: &'b DispatchTable, -) -> FunctionValue { + type_table: &'b TypeTable, + allocator_handle_global: Option, +) { let mut code_gen = BodyIrGenerator::new( db, - module, - hir_function, - llvm_function, + function, llvm_functions, dispatch_table, + type_table, CodeGenParams { make_marshallable: false, }, + allocator_handle_global, ); code_gen.gen_fn_body(); - - llvm_function } /// Generates the body of a wrapper around `hir::Function` for its associated /// `FunctionValue` pub(crate) fn gen_wrapper_body<'a, 'b, D: IrDatabase>( db: &'a D, - hir_function: hir::Function, - llvm_function: FunctionValue, - module: &'a Module, + function: (hir::Function, FunctionValue), llvm_functions: &'a HashMap, dispatch_table: &'b DispatchTable, -) -> FunctionValue { + type_table: &'b TypeTable, + allocator_handle_global: Option, +) { let mut code_gen = BodyIrGenerator::new( db, - module, - hir_function, - llvm_function, + function, llvm_functions, dispatch_table, + type_table, CodeGenParams { make_marshallable: true, }, + allocator_handle_global, ); code_gen.gen_fn_wrapper(); - - llvm_function } diff --git a/crates/mun_codegen/src/ir/intrinsics.rs b/crates/mun_codegen/src/ir/intrinsics.rs new file mode 100644 index 000000000..34059b15a --- /dev/null +++ b/crates/mun_codegen/src/ir/intrinsics.rs @@ -0,0 +1,97 @@ +use crate::intrinsics::{self, Intrinsic}; +use crate::ir::dispatch_table::FunctionPrototype; +use crate::IrDatabase; +use hir::{Body, Expr, ExprId, InferenceResult}; +use inkwell::module::Module; +use inkwell::types::FunctionType; +use std::collections::BTreeMap; +use std::sync::Arc; + +// Use a `BTreeMap` to guarantee deterministically ordered output +pub type IntrinsicsMap = BTreeMap; + +fn collect_intrinsic(module: &Module, entries: &mut IntrinsicsMap, intrinsic: &impl Intrinsic) { + let prototype = intrinsic.prototype(); + entries + .entry(prototype) + .or_insert_with(|| intrinsic.ir_type(&module.get_context())); +} + +fn collect_expr( + db: &D, + module: &Module, + entries: &mut IntrinsicsMap, + needs_alloc: &mut bool, + expr_id: ExprId, + body: &Arc, + infer: &InferenceResult, +) { + let expr = &body[expr_id]; + + // If this expression is a call, store it in the dispatch table + if let Expr::Call { callee, .. } = expr { + match infer[*callee].as_callable_def() { + Some(hir::CallableDef::Struct(_)) => { + collect_intrinsic(module, entries, &intrinsics::new); + collect_intrinsic(module, entries, &intrinsics::clone); + // self.collect_intrinsic(module, entries, &intrinsics::drop); + *needs_alloc = true; + } + Some(hir::CallableDef::Function(_)) => (), + None => panic!("expected a callable expression"), + } + } + + if let Expr::RecordLit { .. } = expr { + collect_intrinsic(module, entries, &intrinsics::new); + collect_intrinsic(module, entries, &intrinsics::clone); + // self.collect_intrinsic(module, entries, &intrinsics::drop); + *needs_alloc = true; + } + + if let Expr::Path(path) = expr { + let resolver = hir::resolver_for_expr(body.clone(), db, expr_id); + let resolution = resolver + .resolve_path_without_assoc_items(db, path) + .take_values() + .expect("unknown path"); + + if let hir::Resolution::Def(hir::ModuleDef::Struct(_)) = resolution { + collect_intrinsic(module, entries, &intrinsics::new); + collect_intrinsic(module, entries, &intrinsics::clone); + // self.collect_intrinsic( module, entries, &intrinsics::drop); + *needs_alloc = true; + } + } + + // Recurse further + expr.walk_child_exprs(|expr_id| { + collect_expr(db, module, entries, needs_alloc, expr_id, body, infer) + }) +} + +pub fn collect_fn_body( + db: &D, + module: &Module, + entries: &mut IntrinsicsMap, + needs_alloc: &mut bool, + body: &Arc, + infer: &InferenceResult, +) { + collect_expr( + db, + module, + entries, + needs_alloc, + body.body_expr(), + body, + infer, + ); +} + +pub fn collect_wrapper_body(module: &Module, entries: &mut IntrinsicsMap, needs_alloc: &mut bool) { + collect_intrinsic(module, entries, &intrinsics::new); + collect_intrinsic(module, entries, &intrinsics::clone); + // self.collect_intrinsic(entries, &intrinsics::drop, module); + *needs_alloc = true; +} diff --git a/crates/mun_codegen/src/ir/module.rs b/crates/mun_codegen/src/ir/module.rs index 0bc472de2..2fa8677e3 100644 --- a/crates/mun_codegen/src/ir/module.rs +++ b/crates/mun_codegen/src/ir/module.rs @@ -1,11 +1,17 @@ -use super::adt; -use crate::ir::dispatch_table::{DispatchTable, DispatchTableBuilder}; -use crate::ir::function; -use crate::type_info::TypeInfo; +use crate::ir::{ + abi_types::gen_abi_types, + adt, + dispatch_table::{DispatchTable, DispatchTableBuilder}, + function, intrinsics, + type_table::{TypeTable, TypeTableBuilder}, +}; use crate::{CodeGenParams, IrDatabase}; use hir::{FileId, ModuleDef}; -use inkwell::{module::Module, values::FunctionValue}; -use std::collections::{HashMap, HashSet}; +use inkwell::{ + values::{FunctionValue, UnnamedAddress}, + AddressSpace, +}; +use std::collections::{BTreeMap, HashMap}; use std::sync::Arc; #[derive(Debug, Clone, Eq, PartialEq)] @@ -13,58 +19,94 @@ pub struct ModuleIR { /// The original source file pub file_id: FileId, - /// The LLVM module that contains the IR - pub llvm_module: Module, - /// A mapping from HIR functions to LLVM IR values pub functions: HashMap, - /// A set of unique TypeInfo values - pub types: HashSet, - /// The dispatch table pub dispatch_table: DispatchTable, + + /// The type table + pub type_table: TypeTable, } /// Generates IR for the specified file pub(crate) fn ir_query(db: &impl IrDatabase, file_id: FileId) -> Arc { - let llvm_module = db - .context() - .create_module(db.file_relative_path(file_id).as_str()); + let llvm_module = db.module(); + let abi_types = gen_abi_types(llvm_module.get_context()); + + // Collect all intrinsic functions and wrapper function. + // Use a `BTreeMap` to guarantee deterministically ordered output. + let mut intrinsics_map = BTreeMap::new(); + let mut wrappers = BTreeMap::new(); + let mut needs_alloc = false; + for def in db.module_data(file_id).definitions() { + match def { + ModuleDef::Function(f) if !f.is_extern(db) => { + let fn_sig = f.ty(db).callable_sig(db).unwrap(); + let body = f.body(db); + let infer = f.infer(db); - // Collect type definitions for all used types - let mut types = HashSet::new(); + intrinsics::collect_fn_body( + db, + &llvm_module, + &mut intrinsics_map, + &mut needs_alloc, + &body, + &infer, + ); + + if !f.data(db).visibility().is_private() && !fn_sig.marshallable(db) { + intrinsics::collect_wrapper_body( + &llvm_module, + &mut intrinsics_map, + &mut needs_alloc, + ); + + // Generate wrapper function + let wrapper_fun = function::gen_signature( + db, + *f, + &llvm_module, + CodeGenParams { + make_marshallable: true, + }, + ); + wrappers.insert(*f, wrapper_fun); + } + } + ModuleDef::Function(_) => (), // TODO: Extern types? + ModuleDef::Struct(_) => (), + ModuleDef::BuiltinType(_) => (), + } + } + + // Collect all used types + let mut type_table_builder = + TypeTableBuilder::new(db, &llvm_module, &abi_types, intrinsics_map.keys()); for def in db.module_data(file_id).definitions() { match def { ModuleDef::Struct(s) => { - let _t = adt::gen_struct_decl(db, *s); - types.insert(db.type_info(s.ty(db))); + adt::gen_struct_decl(db, *s); + type_table_builder.collect_struct(*s); + } + ModuleDef::Function(f) => { + type_table_builder.collect_fn(*f); } - ModuleDef::BuiltinType(_) | ModuleDef::Function(_) => (), + ModuleDef::BuiltinType(_) => (), } } + let type_table = type_table_builder.build(); + // Generate all the function signatures let mut functions = HashMap::new(); - let mut wrappers = HashMap::new(); - let mut dispatch_table_builder = DispatchTableBuilder::new(db, &llvm_module); + let mut dispatch_table_builder = DispatchTableBuilder::new(db, &llvm_module, intrinsics_map); for def in db.module_data(file_id).definitions() { // TODO: Remove once we have more ModuleDef variants #[allow(clippy::single_match)] match def { ModuleDef::Function(f) if !f.is_extern(db) => { - // Collect argument types - let fn_sig = f.ty(db).callable_sig(db).unwrap(); - for ty in fn_sig.params().iter() { - types.insert(db.type_info(ty.clone())); - } - // Collect return type - let ret_ty = fn_sig.ret(); - if !ret_ty.is_empty() { - types.insert(db.type_info(ret_ty.clone())); - } - // Construct the function signature let fun = function::gen_signature( db, @@ -80,21 +122,6 @@ pub(crate) fn ir_query(db: &impl IrDatabase, file_id: FileId) -> Arc { let body = f.body(db); let infer = f.infer(db); dispatch_table_builder.collect_body(&body, &infer); - - if f.data(db).visibility() != hir::Visibility::Private && !fn_sig.marshallable(db) { - let wrapper_fun = function::gen_signature( - db, - *f, - &llvm_module, - CodeGenParams { - make_marshallable: true, - }, - ); - wrappers.insert(*f, wrapper_fun); - - // Add calls from the function's wrapper to the dispatch table - dispatch_table_builder.collect_wrapper_body(*f); - } } _ => {} } @@ -104,15 +131,28 @@ pub(crate) fn ir_query(db: &impl IrDatabase, file_id: FileId) -> Arc { let dispatch_table = dispatch_table_builder.finalize(&functions); let fn_pass_manager = function::create_pass_manager(&llvm_module, db.optimization_lvl()); + // Create the allocator handle global value + let allocator_handle_global = if needs_alloc { + let allocator_handle_global_type = db.context().i8_type().ptr_type(AddressSpace::Generic); + let allocator_handle_global = + llvm_module.add_global(allocator_handle_global_type, None, "allocatorHandle"); + allocator_handle_global.set_initializer(&allocator_handle_global_type.const_null()); + allocator_handle_global.set_linkage(inkwell::module::Linkage::Private); + allocator_handle_global.set_unnamed_address(UnnamedAddress::Global); + Some(allocator_handle_global) + } else { + None + }; + // Generate the function bodies for (hir_function, llvm_function) in functions.iter() { function::gen_body( db, - *hir_function, - *llvm_function, - &llvm_module, + (*hir_function, *llvm_function), &functions, &dispatch_table, + &type_table, + allocator_handle_global, ); fn_pass_manager.run_on(llvm_function); } @@ -120,27 +160,15 @@ pub(crate) fn ir_query(db: &impl IrDatabase, file_id: FileId) -> Arc { for (hir_function, llvm_function) in wrappers.iter() { function::gen_wrapper_body( db, - *hir_function, - *llvm_function, - &llvm_module, + (*hir_function, *llvm_function), &functions, &dispatch_table, + &type_table, + allocator_handle_global, ); fn_pass_manager.run_on(llvm_function); } - // Dispatch entries can include previously unchecked intrinsics - for entry in dispatch_table.entries().iter() { - // Collect argument types - for ty in entry.prototype.arg_types.iter() { - types.insert(ty.clone()); - } - // Collect return type - if let Some(ret_ty) = entry.prototype.ret_type.as_ref() { - types.insert(ret_ty.clone()); - } - } - // Filter private methods let mut api: HashMap = functions .into_iter() @@ -154,9 +182,8 @@ pub(crate) fn ir_query(db: &impl IrDatabase, file_id: FileId) -> Arc { Arc::new(ModuleIR { file_id, - llvm_module, functions: api, - types, dispatch_table, + type_table, }) } diff --git a/crates/mun_codegen/src/ir/ty.rs b/crates/mun_codegen/src/ir/ty.rs index d1f8417e1..5ed9f959c 100644 --- a/crates/mun_codegen/src/ir/ty.rs +++ b/crates/mun_codegen/src/ir/ty.rs @@ -56,10 +56,10 @@ pub(crate) fn ir_query(db: &impl IrDatabase, ty: Ty, params: CodeGenParams) -> A TypeCtor::Struct(s) => { let struct_ty = db.struct_ty(s); match s.data(db).memory_kind { - hir::StructMemoryKind::GC => struct_ty.ptr_type(AddressSpace::Generic).into(), + hir::StructMemoryKind::GC => struct_ty.ptr_type(AddressSpace::Const).ptr_type(AddressSpace::Generic).into(), hir::StructMemoryKind::Value => { if params.make_marshallable { - struct_ty.ptr_type(AddressSpace::Generic).into() + struct_ty.ptr_type(AddressSpace::Const).ptr_type(AddressSpace::Generic).into() } else { struct_ty.into() } diff --git a/crates/mun_codegen/src/ir/type_table.rs b/crates/mun_codegen/src/ir/type_table.rs new file mode 100644 index 000000000..e02b59088 --- /dev/null +++ b/crates/mun_codegen/src/ir/type_table.rs @@ -0,0 +1,331 @@ +use crate::code_gen::{ + gen_global, gen_string_array, gen_struct_ptr_array, gen_u16_array, intern_string, +}; +use crate::ir::{abi_types::AbiTypes, dispatch_table::FunctionPrototype}; +use crate::type_info::{TypeGroup, TypeInfo}; +use crate::{CodeGenParams, IrDatabase}; +use hir::{Body, ExprId, InferenceResult}; +use inkwell::{ + module::Module, + targets::TargetData, + values::{GlobalValue, IntValue, PointerValue, StructValue}, + AddressSpace, +}; +use std::collections::{BTreeSet, HashMap}; +use std::{mem, sync::Arc}; + +/// A type table in IR is a list of pointers to unique type information that are used to generate +/// function and struct information. +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct TypeTable { + type_info_to_index: HashMap, + entries: Vec, + table_ref: PointerValue, +} + +impl TypeTable { + /// Generates a `TypeInfo` lookup through the `TypeTable`, equivalent to something along the + /// lines of: `type_table[i]`, where `i` is the index of the type and `type_table` is an array + /// of `TypeInfo` pointers. + pub fn gen_type_info_lookup( + &self, + builder: &inkwell::builder::Builder, + type_info: &TypeInfo, + ) -> PointerValue { + let index = *self + .type_info_to_index + .get(type_info) + .expect("unknown type"); + + let ptr_to_type_info_ptr = unsafe { + builder.build_struct_gep( + self.table_ref, + index as u32, + &format!("{}_ptr_ptr", type_info.name), + ) + }; + builder + .build_load(ptr_to_type_info_ptr, &format!("{}_ptr", type_info.name)) + .into_pointer_value() + } + + /// Retrieves the pointer to a `TypeInfo`, if it exists in the `TypeTable`. + pub fn get(&self, type_info: &TypeInfo) -> Option { + self.type_info_to_index + .get(type_info) + .map(|index| unsafe { *self.entries.get_unchecked(*index) }) + } + + /// Returns the number of types in the `TypeTable`. + pub fn num_types(&self) -> usize { + self.entries.len() + } + + /// Returns the value that represents the type table in IR. + pub fn pointer_value(&self) -> PointerValue { + self.table_ref + } +} + +/// Used to build a `TypeTable` from HIR. +pub(crate) struct TypeTableBuilder<'a, D: IrDatabase> { + db: &'a D, + target_data: Arc, + module: &'a Module, + abi_types: &'a AbiTypes, + entries: BTreeSet, // Use a `BTreeSet` to guarantee deterministically ordered output +} + +impl<'a, D: IrDatabase> TypeTableBuilder<'a, D> { + /// Creates a new `TypeTableBuilder`. + pub(crate) fn new<'f>( + db: &'a D, + module: &'a Module, + abi_types: &'a AbiTypes, + intrinsics: impl Iterator, + ) -> Self { + let mut builder = Self { + db, + target_data: db.target_data(), + module, + abi_types, + entries: BTreeSet::new(), + }; + + for prototype in intrinsics { + for arg_type in prototype.arg_types.iter() { + builder.collect_type(arg_type.clone()); + } + if let Some(ret_type) = prototype.ret_type.as_ref() { + builder.collect_type(ret_type.clone()); + } + } + + builder + } + + /// Collects unique `TypeInfo` from the given `Ty`. + fn collect_type(&mut self, type_info: TypeInfo) { + if let TypeGroup::StructTypes(hir_struct) = type_info.group { + self.collect_struct(hir_struct); + } else { + self.entries.insert(type_info); + } + } + + /// Collects unique `TypeInfo` from the specified expression and its sub-expressions. + fn collect_expr(&mut self, expr_id: ExprId, body: &Arc, infer: &InferenceResult) { + let expr = &body[expr_id]; + + // TODO: Collect used external `TypeInfo` for the type dispatch table + + // Recurse further + expr.walk_child_exprs(|expr_id| self.collect_expr(expr_id, body, infer)) + } + + /// Collects unique `TypeInfo` from the specified function signature and body. + pub fn collect_fn(&mut self, hir_fn: hir::Function) { + // Collect type info for exposed function + if !hir_fn.data(self.db).visibility().is_private() { + let fn_sig = hir_fn.ty(self.db).callable_sig(self.db).unwrap(); + + // Collect argument types + for ty in fn_sig.params().iter() { + self.collect_type(self.db.type_info(ty.clone())); + } + + // Collect return type + let ret_ty = fn_sig.ret(); + if !ret_ty.is_empty() { + self.collect_type(self.db.type_info(ret_ty.clone())); + } + } + + // Collect used types from body + let body = hir_fn.body(self.db); + let infer = hir_fn.infer(self.db); + self.collect_expr(body.body_expr(), &body, &infer); + } + + /// Collects unique `TypeInfo` from the specified struct type. + pub fn collect_struct(&mut self, hir_struct: hir::Struct) { + let type_info = self.db.type_info(hir_struct.ty(self.db)); + self.entries.insert(type_info); + + let fields = hir_struct.fields(self.db); + for field in fields.into_iter() { + self.collect_type(self.db.type_info(field.ty(self.db))); + } + } + + fn gen_type_info( + &self, + type_info_to_ir: &mut HashMap, + type_info: &TypeInfo, + ) -> GlobalValue { + let context = self.module.get_context(); + let guid_bytes_ir: [IntValue; 16] = array_init::array_init(|i| { + context + .i8_type() + .const_int(u64::from(type_info.guid.b[i]), false) + }); + let type_info_ir = context.const_struct( + &[ + context.i8_type().const_array(&guid_bytes_ir).into(), + intern_string( + self.module, + &type_info.name, + &format!("type_info::<{}>::name", type_info.name), + ) + .into(), + context + .i8_type() + .const_int(type_info.group.clone().into(), false) + .into(), + ], + false, + ); + let type_info_ir = match type_info.group { + TypeGroup::FundamentalTypes => type_info_ir, + TypeGroup::StructTypes(s) => { + let struct_info_ir = self.gen_struct_info(type_info_to_ir, s); + context.const_struct(&[type_info_ir.into(), struct_info_ir.into()], false) + } + }; + gen_global( + self.module, + &type_info_ir, + &format!("type_info::<{}>", type_info.name), + ) + } + + fn gen_struct_info( + &self, + type_info_to_ir: &mut HashMap, + hir_struct: hir::Struct, + ) -> StructValue { + let struct_ir = self.db.struct_ty(hir_struct); + let fields = hir_struct.fields(self.db); + + let name = hir_struct.name(self.db).to_string(); + let name_str = intern_string( + &self.module, + &name, + &format!("struct_info::<{}>::name", name), + ); + let field_names = gen_string_array( + self.module, + fields.iter().map(|field| field.name(self.db).to_string()), + &format!("struct_info::<{}>::field_names", name), + ); + let field_types: Vec = fields + .iter() + .map(|field| { + let field_type_info = self.db.type_info(field.ty(self.db)); + if let Some(ir_value) = type_info_to_ir.get(&field_type_info) { + *ir_value + } else { + let ir_value = self.gen_type_info(type_info_to_ir, &field_type_info); + type_info_to_ir.insert(field_type_info, ir_value); + ir_value + } + .as_pointer_value() + }) + .collect(); + + let field_types = gen_struct_ptr_array( + self.module, + self.abi_types.type_info_type, + &field_types, + &format!("struct_info::<{}>::field_types", name), + ); + + let field_offsets = gen_u16_array( + self.module, + (0..fields.len()).map(|idx| { + self.target_data + .offset_of_element(&struct_ir, idx as u32) + .unwrap() + }), + ); + let field_sizes = gen_u16_array( + self.module, + fields.iter().map(|field| { + self.target_data.get_store_size(&self.db.type_ir( + field.ty(self.db), + CodeGenParams { + make_marshallable: false, + }, + )) + }), + ); + + let alignment = self.target_data.get_abi_alignment(&struct_ir); + + self.abi_types.struct_info_type.const_named_struct(&[ + name_str.into(), + field_names.into(), + field_types.into(), + field_offsets.into(), + field_sizes.into(), + self.module + .get_context() + .i16_type() + .const_int(fields.len() as u64, false) + .into(), + self.module + .get_context() + .i16_type() + .const_int(alignment.into(), false) + .into(), + self.module + .get_context() + .i8_type() + .const_int(hir_struct.data(self.db).memory_kind.clone().into(), false) + .into(), + ]) + } + + /// Constructs a `TypeTable` from all *used* types. + pub fn build(mut self) -> TypeTable { + let mut entries = BTreeSet::new(); + mem::swap(&mut entries, &mut self.entries); + + let mut type_info_to_ir = HashMap::with_capacity(entries.len()); + let mut type_info_to_index = HashMap::with_capacity(entries.len()); + + let type_info_ptrs: Vec = entries + .into_iter() + .enumerate() + .map(|(index, type_info)| { + let ptr = if let Some(ir_value) = type_info_to_ir.get(&type_info) { + *ir_value + } else { + let ir_value = self.gen_type_info(&mut type_info_to_ir, &type_info); + type_info_to_ir.insert(type_info.clone(), ir_value); + ir_value + } + .as_pointer_value(); + + type_info_to_index.insert(type_info, index); + ptr + }) + .collect(); + + let type_info_ptr_type = self.abi_types.type_info_type.ptr_type(AddressSpace::Const); + let global_type_info_array = if type_info_ptrs.is_empty() { + type_info_ptr_type + .ptr_type(AddressSpace::Const) + .const_null() + } else { + let type_info_ptrs_array = type_info_ptr_type.const_array(&type_info_ptrs); + gen_global(self.module, &type_info_ptrs_array, "global_type_table").as_pointer_value() + }; + + TypeTable { + type_info_to_index, + entries: type_info_ptrs, + table_ref: global_type_info_array, + } + } +} diff --git a/crates/mun_codegen/src/lib.rs b/crates/mun_codegen/src/lib.rs index 69bb77bf6..b9cf8cf18 100644 --- a/crates/mun_codegen/src/lib.rs +++ b/crates/mun_codegen/src/lib.rs @@ -3,7 +3,6 @@ mod code_gen; mod db; #[macro_use] mod ir; -pub(crate) mod symbols; #[cfg(test)] mod mock; @@ -16,7 +15,7 @@ pub(crate) mod type_info; pub use inkwell::{builder, context::Context, module::Module, values, OptimizationLevel}; pub use crate::{ - code_gen::write_module_shared_object, + code_gen::ModuleBuilder, db::{IrDatabase, IrDatabaseStorage}, }; diff --git a/crates/mun_codegen/src/snapshots/test__binary_expressions.snap b/crates/mun_codegen/src/snapshots/test__binary_expressions.snap index b6a85560a..c26d75410 100644 --- a/crates/mun_codegen/src/snapshots/test__binary_expressions.snap +++ b/crates/mun_codegen/src/snapshots/test__binary_expressions.snap @@ -4,6 +4,7 @@ expression: "fn add(a:int, b:int):int {\n a+b\n}\n\nfn subtract(a:int, b:int):i --- ; ModuleID = 'main.mun' source_filename = "main.mun" +target triple = "x86-64" define i64 @add(i64, i64) { body: diff --git a/crates/mun_codegen/src/snapshots/test__conditional_return_expr.snap b/crates/mun_codegen/src/snapshots/test__conditional_return_expr.snap index fe7edca9c..156e39a1d 100644 --- a/crates/mun_codegen/src/snapshots/test__conditional_return_expr.snap +++ b/crates/mun_codegen/src/snapshots/test__conditional_return_expr.snap @@ -4,6 +4,7 @@ expression: "fn main(a:int):int {\n if a > 4 {\n return a;\n }\n --- ; ModuleID = 'main.mun' source_filename = "main.mun" +target triple = "x86-64" define i64 @main(i64) { body: diff --git a/crates/mun_codegen/src/snapshots/test__equality_operands.snap b/crates/mun_codegen/src/snapshots/test__equality_operands.snap index e1df58b00..f7f0102d0 100644 --- a/crates/mun_codegen/src/snapshots/test__equality_operands.snap +++ b/crates/mun_codegen/src/snapshots/test__equality_operands.snap @@ -4,6 +4,7 @@ expression: "fn equals(a:int, b:int):bool { a == b }\nfn not_equa --- ; ModuleID = 'main.mun' source_filename = "main.mun" +target triple = "x86-64" define i1 @equals(i64, i64) { body: diff --git a/crates/mun_codegen/src/snapshots/test__extern_fn.snap b/crates/mun_codegen/src/snapshots/test__extern_fn.snap index 3cfd0445e..249c91654 100644 --- a/crates/mun_codegen/src/snapshots/test__extern_fn.snap +++ b/crates/mun_codegen/src/snapshots/test__extern_fn.snap @@ -4,6 +4,7 @@ expression: "extern fn add(a:int, b:int): int;\nfn main() {\n add(3,4);\n}" --- ; ModuleID = 'main.mun' source_filename = "main.mun" +target triple = "x86-64" %DispatchTable = type { i64 (i64, i64)* } diff --git a/crates/mun_codegen/src/snapshots/test__fibonacci.snap b/crates/mun_codegen/src/snapshots/test__fibonacci.snap index 05bd5a0b4..d70e1e7df 100644 --- a/crates/mun_codegen/src/snapshots/test__fibonacci.snap +++ b/crates/mun_codegen/src/snapshots/test__fibonacci.snap @@ -4,6 +4,7 @@ expression: "fn fibonacci(n:int):int {\n if n <= 1 {\n n\n } else { --- ; ModuleID = 'main.mun' source_filename = "main.mun" +target triple = "x86-64" %DispatchTable = type { i64 (i64)* } diff --git a/crates/mun_codegen/src/snapshots/test__fibonacci_loop.snap b/crates/mun_codegen/src/snapshots/test__fibonacci_loop.snap index a19d6ece6..03c5a9369 100644 --- a/crates/mun_codegen/src/snapshots/test__fibonacci_loop.snap +++ b/crates/mun_codegen/src/snapshots/test__fibonacci_loop.snap @@ -4,6 +4,7 @@ expression: "fn fibonacci(n:int):int {\n let a = 0;\n let b = 1;\n let --- ; ModuleID = 'main.mun' source_filename = "main.mun" +target triple = "x86-64" define i64 @fibonacci(i64) { body: diff --git a/crates/mun_codegen/src/snapshots/test__field_crash.snap b/crates/mun_codegen/src/snapshots/test__field_crash.snap index 5cf383f7e..54decfdd9 100644 --- a/crates/mun_codegen/src/snapshots/test__field_crash.snap +++ b/crates/mun_codegen/src/snapshots/test__field_crash.snap @@ -4,28 +4,56 @@ expression: "struct(gc) Foo { a: int };\n\nfn main(c:int):int {\n let b = Foo --- ; ModuleID = 'main.mun' source_filename = "main.mun" +target triple = "x86-64" -%DispatchTable = type { i8* (i8 addrspace(4)*, i8 addrspace(4)*)*, i8* (i64, i64)* } +%struct.MunTypeInfo = type { [16 x i8], i8 addrspace(4)*, i8 } +%struct.MunStructInfo = type { i8 addrspace(4)*, i8 addrspace(4)* addrspace(4)*, %struct.MunTypeInfo addrspace(4)* addrspace(4)*, i16 addrspace(4)*, i16 addrspace(4)*, i16, i16, i8 } +%DispatchTable = type { i8* addrspace(4)* (i8 addrspace(4)*, i8*)*, i8* addrspace(4)* (i8 addrspace(4)*, i8*)* } %Foo = type { i64 } +@"type_info::::name" = private unnamed_addr constant [4 x i8] c"Foo\00" +@"struct_info::::name" = private unnamed_addr constant [4 x i8] c"Foo\00" +@"struct_info::::field_names" = private unnamed_addr constant [2 x i8] c"a\00" +@0 = private unnamed_addr constant [1 x i8 addrspace(4)*] [i8 addrspace(4)* @"struct_info::::field_names"] +@"type_info::::name" = private unnamed_addr constant [10 x i8] c"core::i64\00" +@"type_info::" = private unnamed_addr constant { [16 x i8], [10 x i8]*, i8 } { [16 x i8] c"G\13;t\97j8\18\D7M\83`\1D\C8\19%", [10 x i8]* @"type_info::::name", i8 0 } +@"struct_info::::field_types" = private unnamed_addr constant [1 x %struct.MunTypeInfo addrspace(4)*] [%struct.MunTypeInfo addrspace(4)* @"type_info::"] +@1 = private unnamed_addr constant [1 x i16] zeroinitializer +@2 = private unnamed_addr constant [1 x i16] [i16 8] +@"type_info::" = private unnamed_addr constant { { [16 x i8], [4 x i8]*, i8 }, %struct.MunStructInfo } { { [16 x i8], [4 x i8]*, i8 } { [16 x i8] c"\13V\C6}z\D1c\8D\81k\FB\82-\D2\C2]", [4 x i8]* @"type_info::::name", i8 1 }, %struct.MunStructInfo { [4 x i8]* @"struct_info::::name", [1 x i8 addrspace(4)*]* @0, [1 x %struct.MunTypeInfo addrspace(4)*]* @"struct_info::::field_types", [1 x i16]* @1, [1 x i16]* @2, i16 1, i16 8, i8 0 } } +@"type_info::<*const TypeInfo>::name" = private unnamed_addr constant [16 x i8] c"*const TypeInfo\00" +@"type_info::<*const TypeInfo>" = private unnamed_addr constant { [16 x i8], [16 x i8]*, i8 } { [16 x i8] c"=\A1-\1F\C2\A7\88`d\90\F4\B5\BEE}x", [16 x i8]* @"type_info::<*const TypeInfo>::name", i8 0 } +@"type_info::<*const *mut core::void>::name" = private unnamed_addr constant [23 x i8] c"*const *mut core::void\00" +@"type_info::<*const *mut core::void>" = private unnamed_addr constant { [16 x i8], [23 x i8]*, i8 } { [16 x i8] c"\C5fO\BD\84\DF\06\BFd+\B1\9Abv\CE\00", [23 x i8]* @"type_info::<*const *mut core::void>::name", i8 0 } +@"type_info::<*const core::void>::name" = private unnamed_addr constant [18 x i8] c"*const core::void\00" +@"type_info::<*const core::void>" = private unnamed_addr constant { [16 x i8], [18 x i8]*, i8 } { [16 x i8] c"\EF\D3\E0ac~\5C\D4\EF\AE\B1}\CA\BE\DA\16", [18 x i8]* @"type_info::<*const core::void>::name", i8 0 } +@"type_info::<*mut core::void>::name" = private unnamed_addr constant [16 x i8] c"*mut core::void\00" +@"type_info::<*mut core::void>" = private unnamed_addr constant { [16 x i8], [16 x i8]*, i8 } { [16 x i8] c"\F0Y\22\FC\95\9E\7F\CE\08T\B1\A2\CD\A7\FAz", [16 x i8]* @"type_info::<*mut core::void>::name", i8 0 } +@global_type_table = private unnamed_addr constant [6 x %struct.MunTypeInfo addrspace(4)*] [%struct.MunTypeInfo addrspace(4)* @"type_info::", %struct.MunTypeInfo addrspace(4)* @"type_info::<*const TypeInfo>", %struct.MunTypeInfo addrspace(4)* @"type_info::", %struct.MunTypeInfo addrspace(4)* @"type_info::<*const *mut core::void>", %struct.MunTypeInfo addrspace(4)* @"type_info::<*const core::void>", %struct.MunTypeInfo addrspace(4)* @"type_info::<*mut core::void>"] @dispatchTable = global %DispatchTable zeroinitializer +@allocatorHandle = private unnamed_addr global i8* null define i64 @main(i64) { body: - %b = alloca %Foo* + %b = alloca %Foo addrspace(4)** %c = alloca i64 store i64 %0, i64* %c %c1 = load i64, i64* %c %add = add i64 %c1, 5 %init = insertvalue %Foo undef, i64 %add, 0 - %malloc_ptr = load i8* (i64, i64)*, i8* (i64, i64)** getelementptr inbounds (%DispatchTable, %DispatchTable* @dispatchTable, i32 0, i32 1) - %malloc = call i8* %malloc_ptr(i64 ptrtoint (i64* getelementptr (i64, i64* null, i32 1) to i64), i64 ptrtoint (i64* getelementptr ({ i1, i64 }, { i1, i64 }* null, i64 0, i32 1) to i64)) - %Foo = bitcast i8* %malloc to %Foo* + %new_ptr = load i8* addrspace(4)* (i8 addrspace(4)*, i8*)*, i8* addrspace(4)* (i8 addrspace(4)*, i8*)** getelementptr inbounds (%DispatchTable, %DispatchTable* @dispatchTable, i32 0, i32 1) + %Foo_ptr = load %struct.MunTypeInfo addrspace(4)*, %struct.MunTypeInfo addrspace(4)** getelementptr inbounds ([6 x %struct.MunTypeInfo addrspace(4)*], [6 x %struct.MunTypeInfo addrspace(4)*]* @global_type_table, i32 0, i32 0) + %type_info_ptr_to_i8_ptr = bitcast %struct.MunTypeInfo addrspace(4)* %Foo_ptr to i8 addrspace(4)* + %allocator_handle = load i8*, i8** @allocatorHandle + %new = call i8* addrspace(4)* %new_ptr(i8 addrspace(4)* %type_info_ptr_to_i8_ptr, i8* %allocator_handle) + %mem_ptr = load i8*, i8* addrspace(4)* %new + %Foo = bitcast i8* %mem_ptr to %Foo* store %Foo %init, %Foo* %Foo - store %Foo* %Foo, %Foo** %b - %deref = load %Foo*, %Foo** %b - %Foo.a = getelementptr inbounds %Foo, %Foo* %deref, i32 0, i32 0 - %a = load i64, i64* %Foo.a + store i8* addrspace(4)* %new, %Foo addrspace(4)*** %b + %mem_ptr2 = load %Foo addrspace(4)**, %Foo addrspace(4)*** %b + %deref = load %Foo addrspace(4)*, %Foo addrspace(4)** %mem_ptr2 + %Foo.a = getelementptr inbounds %Foo, %Foo addrspace(4)* %deref, i32 0, i32 0 + %a = load i64, i64 addrspace(4)* %Foo.a ret i64 %a } diff --git a/crates/mun_codegen/src/snapshots/test__field_expr.snap b/crates/mun_codegen/src/snapshots/test__field_expr.snap index 9f8ea630d..e0b8b62cf 100644 --- a/crates/mun_codegen/src/snapshots/test__field_expr.snap +++ b/crates/mun_codegen/src/snapshots/test__field_expr.snap @@ -4,12 +4,46 @@ expression: "struct(value) Bar(float, Foo);\nstruct(value) Foo { a: int };\n\nfn --- ; ModuleID = 'main.mun' source_filename = "main.mun" +target triple = "x86-64" -%DispatchTable = type { i64 (%Foo)*, %Foo (%Bar)*, i8* (i8 addrspace(4)*, i8 addrspace(4)*)* } +%struct.MunTypeInfo = type { [16 x i8], i8 addrspace(4)*, i8 } +%struct.MunStructInfo = type { i8 addrspace(4)*, i8 addrspace(4)* addrspace(4)*, %struct.MunTypeInfo addrspace(4)* addrspace(4)*, i16 addrspace(4)*, i16 addrspace(4)*, i16, i16, i8 } +%DispatchTable = type { i8* addrspace(4)* (i8 addrspace(4)*, i8*)*, i8* addrspace(4)* (i8 addrspace(4)*, i8*)*, i64 (%Foo)*, %Foo (%Bar)* } %Foo = type { i64 } %Bar = type { double, %Foo } -@dispatchTable = global %DispatchTable { i64 (%Foo)* @foo_a, %Foo (%Bar)* @bar_1, i8* (i8 addrspace(4)*, i8 addrspace(4)*)* null } +@"type_info::::name" = private unnamed_addr constant [4 x i8] c"Foo\00" +@"struct_info::::name" = private unnamed_addr constant [4 x i8] c"Foo\00" +@"struct_info::::field_names" = private unnamed_addr constant [2 x i8] c"a\00" +@0 = private unnamed_addr constant [1 x i8 addrspace(4)*] [i8 addrspace(4)* @"struct_info::::field_names"] +@"type_info::::name" = private unnamed_addr constant [10 x i8] c"core::i64\00" +@"type_info::" = private unnamed_addr constant { [16 x i8], [10 x i8]*, i8 } { [16 x i8] c"G\13;t\97j8\18\D7M\83`\1D\C8\19%", [10 x i8]* @"type_info::::name", i8 0 } +@"struct_info::::field_types" = private unnamed_addr constant [1 x %struct.MunTypeInfo addrspace(4)*] [%struct.MunTypeInfo addrspace(4)* @"type_info::"] +@1 = private unnamed_addr constant [1 x i16] zeroinitializer +@2 = private unnamed_addr constant [1 x i16] [i16 8] +@"type_info::" = private unnamed_addr constant { { [16 x i8], [4 x i8]*, i8 }, %struct.MunStructInfo } { { [16 x i8], [4 x i8]*, i8 } { [16 x i8] c"\13V\C6}z\D1c\8D\81k\FB\82-\D2\C2]", [4 x i8]* @"type_info::::name", i8 1 }, %struct.MunStructInfo { [4 x i8]* @"struct_info::::name", [1 x i8 addrspace(4)*]* @0, [1 x %struct.MunTypeInfo addrspace(4)*]* @"struct_info::::field_types", [1 x i16]* @1, [1 x i16]* @2, i16 1, i16 8, i8 1 } } +@"type_info::<*const TypeInfo>::name" = private unnamed_addr constant [16 x i8] c"*const TypeInfo\00" +@"type_info::<*const TypeInfo>" = private unnamed_addr constant { [16 x i8], [16 x i8]*, i8 } { [16 x i8] c"=\A1-\1F\C2\A7\88`d\90\F4\B5\BEE}x", [16 x i8]* @"type_info::<*const TypeInfo>::name", i8 0 } +@"type_info::::name" = private unnamed_addr constant [10 x i8] c"core::f64\00" +@"type_info::" = private unnamed_addr constant { [16 x i8], [10 x i8]*, i8 } { [16 x i8] c"`\DBF\9C?YJ%G\AD4\9F\D5\92%A", [10 x i8]* @"type_info::::name", i8 0 } +@"type_info::<*const *mut core::void>::name" = private unnamed_addr constant [23 x i8] c"*const *mut core::void\00" +@"type_info::<*const *mut core::void>" = private unnamed_addr constant { [16 x i8], [23 x i8]*, i8 } { [16 x i8] c"\C5fO\BD\84\DF\06\BFd+\B1\9Abv\CE\00", [23 x i8]* @"type_info::<*const *mut core::void>::name", i8 0 } +@"type_info::::name" = private unnamed_addr constant [4 x i8] c"Bar\00" +@"struct_info::::name" = private unnamed_addr constant [4 x i8] c"Bar\00" +@"struct_info::::field_names" = private unnamed_addr constant [2 x i8] c"0\00" +@"struct_info::::field_names.1" = private unnamed_addr constant [2 x i8] c"1\00" +@3 = private unnamed_addr constant [2 x i8 addrspace(4)*] [i8 addrspace(4)* @"struct_info::::field_names", i8 addrspace(4)* @"struct_info::::field_names.1"] +@"struct_info::::field_types" = private unnamed_addr constant [2 x %struct.MunTypeInfo addrspace(4)*] [%struct.MunTypeInfo addrspace(4)* @"type_info::", %struct.MunTypeInfo addrspace(4)* @"type_info::"] +@4 = private unnamed_addr constant [2 x i16] [i16 0, i16 8] +@5 = private unnamed_addr constant [2 x i16] [i16 8, i16 8] +@"type_info::" = private unnamed_addr constant { { [16 x i8], [4 x i8]*, i8 }, %struct.MunStructInfo } { { [16 x i8], [4 x i8]*, i8 } { [16 x i8] c"\DD\C3_\88\FAq\B6\EF\14*\E6\1F56FS", [4 x i8]* @"type_info::::name", i8 1 }, %struct.MunStructInfo { [4 x i8]* @"struct_info::::name", [2 x i8 addrspace(4)*]* @3, [2 x %struct.MunTypeInfo addrspace(4)*]* @"struct_info::::field_types", [2 x i16]* @4, [2 x i16]* @5, i16 2, i16 8, i8 1 } } +@"type_info::<*const core::void>::name" = private unnamed_addr constant [18 x i8] c"*const core::void\00" +@"type_info::<*const core::void>" = private unnamed_addr constant { [16 x i8], [18 x i8]*, i8 } { [16 x i8] c"\EF\D3\E0ac~\5C\D4\EF\AE\B1}\CA\BE\DA\16", [18 x i8]* @"type_info::<*const core::void>::name", i8 0 } +@"type_info::<*mut core::void>::name" = private unnamed_addr constant [16 x i8] c"*mut core::void\00" +@"type_info::<*mut core::void>" = private unnamed_addr constant { [16 x i8], [16 x i8]*, i8 } { [16 x i8] c"\F0Y\22\FC\95\9E\7F\CE\08T\B1\A2\CD\A7\FAz", [16 x i8]* @"type_info::<*mut core::void>::name", i8 0 } +@global_type_table = private unnamed_addr constant [8 x %struct.MunTypeInfo addrspace(4)*] [%struct.MunTypeInfo addrspace(4)* @"type_info::", %struct.MunTypeInfo addrspace(4)* @"type_info::<*const TypeInfo>", %struct.MunTypeInfo addrspace(4)* @"type_info::", %struct.MunTypeInfo addrspace(4)* @"type_info::", %struct.MunTypeInfo addrspace(4)* @"type_info::<*const *mut core::void>", %struct.MunTypeInfo addrspace(4)* @"type_info::", %struct.MunTypeInfo addrspace(4)* @"type_info::<*const core::void>", %struct.MunTypeInfo addrspace(4)* @"type_info::<*mut core::void>"] +@dispatchTable = global %DispatchTable { i8* addrspace(4)* (i8 addrspace(4)*, i8*)* null, i8* addrspace(4)* (i8 addrspace(4)*, i8*)* null, i64 (%Foo)* @foo_a, %Foo (%Bar)* @bar_1 } +@allocatorHandle = private unnamed_addr global i8* null define double @bar_0(%Bar) { body: @@ -40,9 +74,9 @@ define i64 @bar_1_foo_a(%Bar) { body: %.fca.0.extract = extractvalue %Bar %0, 0 %.fca.1.0.extract = extractvalue %Bar %0, 1, 0 - %bar_1_ptr = load %Foo (%Bar)*, %Foo (%Bar)** getelementptr inbounds (%DispatchTable, %DispatchTable* @dispatchTable, i32 0, i32 1) + %bar_1_ptr = load %Foo (%Bar)*, %Foo (%Bar)** getelementptr inbounds (%DispatchTable, %DispatchTable* @dispatchTable, i32 0, i32 3) %bar_1 = call %Foo %bar_1_ptr(%Bar %0) - %foo_a_ptr = load i64 (%Foo)*, i64 (%Foo)** getelementptr inbounds (%DispatchTable, %DispatchTable* @dispatchTable, i32 0, i32 0) + %foo_a_ptr = load i64 (%Foo)*, i64 (%Foo)** getelementptr inbounds (%DispatchTable, %DispatchTable* @dispatchTable, i32 0, i32 2) %foo_a = call i64 %foo_a_ptr(%Foo %bar_1) ret i64 %foo_a } diff --git a/crates/mun_codegen/src/snapshots/test__function.snap b/crates/mun_codegen/src/snapshots/test__function.snap index 28335de17..d6ff392b7 100644 --- a/crates/mun_codegen/src/snapshots/test__function.snap +++ b/crates/mun_codegen/src/snapshots/test__function.snap @@ -4,6 +4,7 @@ expression: "fn main() {\n}" --- ; ModuleID = 'main.mun' source_filename = "main.mun" +target triple = "x86-64" define void @main() { body: diff --git a/crates/mun_codegen/src/snapshots/test__function_arguments.snap b/crates/mun_codegen/src/snapshots/test__function_arguments.snap index 1918d91d5..a32c3c6eb 100644 --- a/crates/mun_codegen/src/snapshots/test__function_arguments.snap +++ b/crates/mun_codegen/src/snapshots/test__function_arguments.snap @@ -4,6 +4,7 @@ expression: "fn main(a:int):int {\n a\n}" --- ; ModuleID = 'main.mun' source_filename = "main.mun" +target triple = "x86-64" define i64 @main(i64) { body: diff --git a/crates/mun_codegen/src/snapshots/test__function_calls.snap b/crates/mun_codegen/src/snapshots/test__function_calls.snap index 4fa5b53b7..eac7ed95e 100644 --- a/crates/mun_codegen/src/snapshots/test__function_calls.snap +++ b/crates/mun_codegen/src/snapshots/test__function_calls.snap @@ -4,6 +4,7 @@ expression: "fn add_impl(a:int, b:int):int {\n a+b\n}\n\nfn add(a:int, b:int) --- ; ModuleID = 'main.mun' source_filename = "main.mun" +target triple = "x86-64" %DispatchTable = type { i64 (i64, i64)*, i64 (i64, i64)* } diff --git a/crates/mun_codegen/src/snapshots/test__gc_struct.snap b/crates/mun_codegen/src/snapshots/test__gc_struct.snap index 0806e5f06..911ef8dde 100644 --- a/crates/mun_codegen/src/snapshots/test__gc_struct.snap +++ b/crates/mun_codegen/src/snapshots/test__gc_struct.snap @@ -4,30 +4,60 @@ expression: "struct(gc) Foo { a: int, b: int };\n\nfn foo() {\n let a = Foo { --- ; ModuleID = 'main.mun' source_filename = "main.mun" +target triple = "x86-64" -%DispatchTable = type { i8* (i8 addrspace(4)*, i8 addrspace(4)*)*, i8* (i64, i64)* } +%struct.MunTypeInfo = type { [16 x i8], i8 addrspace(4)*, i8 } +%struct.MunStructInfo = type { i8 addrspace(4)*, i8 addrspace(4)* addrspace(4)*, %struct.MunTypeInfo addrspace(4)* addrspace(4)*, i16 addrspace(4)*, i16 addrspace(4)*, i16, i16, i8 } +%DispatchTable = type { i8* addrspace(4)* (i8 addrspace(4)*, i8*)*, i8* addrspace(4)* (i8 addrspace(4)*, i8*)* } %Foo = type { i64, i64 } +@"type_info::::name" = private unnamed_addr constant [4 x i8] c"Foo\00" +@"struct_info::::name" = private unnamed_addr constant [4 x i8] c"Foo\00" +@"struct_info::::field_names" = private unnamed_addr constant [2 x i8] c"a\00" +@"struct_info::::field_names.1" = private unnamed_addr constant [2 x i8] c"b\00" +@0 = private unnamed_addr constant [2 x i8 addrspace(4)*] [i8 addrspace(4)* @"struct_info::::field_names", i8 addrspace(4)* @"struct_info::::field_names.1"] +@"type_info::::name" = private unnamed_addr constant [10 x i8] c"core::i64\00" +@"type_info::" = private unnamed_addr constant { [16 x i8], [10 x i8]*, i8 } { [16 x i8] c"G\13;t\97j8\18\D7M\83`\1D\C8\19%", [10 x i8]* @"type_info::::name", i8 0 } +@"struct_info::::field_types" = private unnamed_addr constant [2 x %struct.MunTypeInfo addrspace(4)*] [%struct.MunTypeInfo addrspace(4)* @"type_info::", %struct.MunTypeInfo addrspace(4)* @"type_info::"] +@1 = private unnamed_addr constant [2 x i16] [i16 0, i16 8] +@2 = private unnamed_addr constant [2 x i16] [i16 8, i16 8] +@"type_info::" = private unnamed_addr constant { { [16 x i8], [4 x i8]*, i8 }, %struct.MunStructInfo } { { [16 x i8], [4 x i8]*, i8 } { [16 x i8] c"\13V\C6}z\D1c\8D\81k\FB\82-\D2\C2]", [4 x i8]* @"type_info::::name", i8 1 }, %struct.MunStructInfo { [4 x i8]* @"struct_info::::name", [2 x i8 addrspace(4)*]* @0, [2 x %struct.MunTypeInfo addrspace(4)*]* @"struct_info::::field_types", [2 x i16]* @1, [2 x i16]* @2, i16 2, i16 8, i8 0 } } +@"type_info::<*const TypeInfo>::name" = private unnamed_addr constant [16 x i8] c"*const TypeInfo\00" +@"type_info::<*const TypeInfo>" = private unnamed_addr constant { [16 x i8], [16 x i8]*, i8 } { [16 x i8] c"=\A1-\1F\C2\A7\88`d\90\F4\B5\BEE}x", [16 x i8]* @"type_info::<*const TypeInfo>::name", i8 0 } +@"type_info::<*const *mut core::void>::name" = private unnamed_addr constant [23 x i8] c"*const *mut core::void\00" +@"type_info::<*const *mut core::void>" = private unnamed_addr constant { [16 x i8], [23 x i8]*, i8 } { [16 x i8] c"\C5fO\BD\84\DF\06\BFd+\B1\9Abv\CE\00", [23 x i8]* @"type_info::<*const *mut core::void>::name", i8 0 } +@"type_info::<*const core::void>::name" = private unnamed_addr constant [18 x i8] c"*const core::void\00" +@"type_info::<*const core::void>" = private unnamed_addr constant { [16 x i8], [18 x i8]*, i8 } { [16 x i8] c"\EF\D3\E0ac~\5C\D4\EF\AE\B1}\CA\BE\DA\16", [18 x i8]* @"type_info::<*const core::void>::name", i8 0 } +@"type_info::<*mut core::void>::name" = private unnamed_addr constant [16 x i8] c"*mut core::void\00" +@"type_info::<*mut core::void>" = private unnamed_addr constant { [16 x i8], [16 x i8]*, i8 } { [16 x i8] c"\F0Y\22\FC\95\9E\7F\CE\08T\B1\A2\CD\A7\FAz", [16 x i8]* @"type_info::<*mut core::void>::name", i8 0 } +@global_type_table = private unnamed_addr constant [6 x %struct.MunTypeInfo addrspace(4)*] [%struct.MunTypeInfo addrspace(4)* @"type_info::", %struct.MunTypeInfo addrspace(4)* @"type_info::<*const TypeInfo>", %struct.MunTypeInfo addrspace(4)* @"type_info::", %struct.MunTypeInfo addrspace(4)* @"type_info::<*const *mut core::void>", %struct.MunTypeInfo addrspace(4)* @"type_info::<*const core::void>", %struct.MunTypeInfo addrspace(4)* @"type_info::<*mut core::void>"] @dispatchTable = global %DispatchTable zeroinitializer +@allocatorHandle = private unnamed_addr global i8* null define void @foo() { body: - %b4 = alloca %Foo* - %a = alloca %Foo* - %malloc_ptr = load i8* (i64, i64)*, i8* (i64, i64)** getelementptr inbounds (%DispatchTable, %DispatchTable* @dispatchTable, i32 0, i32 1) - %malloc = call i8* %malloc_ptr(i64 mul nuw (i64 ptrtoint (i64* getelementptr (i64, i64* null, i32 1) to i64), i64 2), i64 ptrtoint (i64* getelementptr ({ i1, i64 }, { i1, i64 }* null, i64 0, i32 1) to i64)) - %Foo = bitcast i8* %malloc to %Foo* + %b6 = alloca %Foo addrspace(4)** + %a = alloca %Foo addrspace(4)** + %new_ptr = load i8* addrspace(4)* (i8 addrspace(4)*, i8*)*, i8* addrspace(4)* (i8 addrspace(4)*, i8*)** getelementptr inbounds (%DispatchTable, %DispatchTable* @dispatchTable, i32 0, i32 1) + %Foo_ptr = load %struct.MunTypeInfo addrspace(4)*, %struct.MunTypeInfo addrspace(4)** getelementptr inbounds ([6 x %struct.MunTypeInfo addrspace(4)*], [6 x %struct.MunTypeInfo addrspace(4)*]* @global_type_table, i32 0, i32 0) + %type_info_ptr_to_i8_ptr = bitcast %struct.MunTypeInfo addrspace(4)* %Foo_ptr to i8 addrspace(4)* + %allocator_handle = load i8*, i8** @allocatorHandle + %new = call i8* addrspace(4)* %new_ptr(i8 addrspace(4)* %type_info_ptr_to_i8_ptr, i8* %allocator_handle) + %mem_ptr = load i8*, i8* addrspace(4)* %new + %Foo = bitcast i8* %mem_ptr to %Foo* store %Foo { i64 3, i64 4 }, %Foo* %Foo - store %Foo* %Foo, %Foo** %a - %deref = load %Foo*, %Foo** %a - %Foo.b = getelementptr inbounds %Foo, %Foo* %deref, i32 0, i32 1 - %b = load i64, i64* %Foo.b + store i8* addrspace(4)* %new, %Foo addrspace(4)*** %a + %mem_ptr1 = load %Foo addrspace(4)**, %Foo addrspace(4)*** %a + %deref = load %Foo addrspace(4)*, %Foo addrspace(4)** %mem_ptr1 + %Foo.b = getelementptr inbounds %Foo, %Foo addrspace(4)* %deref, i32 0, i32 1 + %b = load i64, i64 addrspace(4)* %Foo.b %add = add i64 %b, 3 - %deref1 = load %Foo*, %Foo** %a - %Foo.b2 = getelementptr inbounds %Foo, %Foo* %deref1, i32 0, i32 1 - store i64 %add, i64* %Foo.b2 - %a3 = load %Foo*, %Foo** %a - store %Foo* %a3, %Foo** %b4 + %mem_ptr2 = load %Foo addrspace(4)**, %Foo addrspace(4)*** %a + %deref3 = load %Foo addrspace(4)*, %Foo addrspace(4)** %mem_ptr2 + %Foo.b4 = getelementptr inbounds %Foo, %Foo addrspace(4)* %deref3, i32 0, i32 1 + store i64 %add, i64 addrspace(4)* %Foo.b4 + %a5 = load %Foo addrspace(4)**, %Foo addrspace(4)*** %a + store %Foo addrspace(4)** %a5, %Foo addrspace(4)*** %b6 ret void } diff --git a/crates/mun_codegen/src/snapshots/test__if_statement.snap b/crates/mun_codegen/src/snapshots/test__if_statement.snap index 701abafb2..17cbf1db3 100644 --- a/crates/mun_codegen/src/snapshots/test__if_statement.snap +++ b/crates/mun_codegen/src/snapshots/test__if_statement.snap @@ -4,6 +4,7 @@ expression: "fn foo(a:int):int {\n let b = if a > 3 {\n let c = if a > --- ; ModuleID = 'main.mun' source_filename = "main.mun" +target triple = "x86-64" define i64 @foo(i64) { body: diff --git a/crates/mun_codegen/src/snapshots/test__let_statement.snap b/crates/mun_codegen/src/snapshots/test__let_statement.snap index ff5330f63..5440c910b 100644 --- a/crates/mun_codegen/src/snapshots/test__let_statement.snap +++ b/crates/mun_codegen/src/snapshots/test__let_statement.snap @@ -4,6 +4,7 @@ expression: "fn main(a:int):int {\n let b = a+1\n b\n}" --- ; ModuleID = 'main.mun' source_filename = "main.mun" +target triple = "x86-64" define i64 @main(i64) { body: diff --git a/crates/mun_codegen/src/snapshots/test__loop_break_expr.snap b/crates/mun_codegen/src/snapshots/test__loop_break_expr.snap index d6e3e9909..36d120376 100644 --- a/crates/mun_codegen/src/snapshots/test__loop_break_expr.snap +++ b/crates/mun_codegen/src/snapshots/test__loop_break_expr.snap @@ -4,6 +4,7 @@ expression: "fn foo(n:int):int {\n loop {\n if n > 5 {\n br --- ; ModuleID = 'main.mun' source_filename = "main.mun" +target triple = "x86-64" define i64 @foo(i64) { body: diff --git a/crates/mun_codegen/src/snapshots/test__loop_expr.snap b/crates/mun_codegen/src/snapshots/test__loop_expr.snap index 44f2f7c3a..fe138bc30 100644 --- a/crates/mun_codegen/src/snapshots/test__loop_expr.snap +++ b/crates/mun_codegen/src/snapshots/test__loop_expr.snap @@ -4,6 +4,7 @@ expression: "fn foo() {\n loop {}\n}" --- ; ModuleID = 'main.mun' source_filename = "main.mun" +target triple = "x86-64" define void @foo() { body: diff --git a/crates/mun_codegen/src/snapshots/test__never_conditional_return_expr.snap b/crates/mun_codegen/src/snapshots/test__never_conditional_return_expr.snap index 34a8a1f49..920f568c4 100644 --- a/crates/mun_codegen/src/snapshots/test__never_conditional_return_expr.snap +++ b/crates/mun_codegen/src/snapshots/test__never_conditional_return_expr.snap @@ -4,6 +4,7 @@ expression: "fn main(a:int):int {\n if a > 4 {\n return a;\n } else --- ; ModuleID = 'main.mun' source_filename = "main.mun" +target triple = "x86-64" define i64 @main(i64) { body: diff --git a/crates/mun_codegen/src/snapshots/test__primitive_types.snap b/crates/mun_codegen/src/snapshots/test__primitive_types.snap index a835baffd..b5fd2c72b 100644 --- a/crates/mun_codegen/src/snapshots/test__primitive_types.snap +++ b/crates/mun_codegen/src/snapshots/test__primitive_types.snap @@ -4,6 +4,7 @@ expression: "fn add(a: u8, b: u8): u8 { a+b }\n fn less(a: u16, b: u16): bool --- ; ModuleID = 'main.mun' source_filename = "main.mun" +target triple = "x86-64" define i8 @add(i8, i8) { body: diff --git a/crates/mun_codegen/src/snapshots/test__return_expr.snap b/crates/mun_codegen/src/snapshots/test__return_expr.snap index 552f2cdec..058a4b863 100644 --- a/crates/mun_codegen/src/snapshots/test__return_expr.snap +++ b/crates/mun_codegen/src/snapshots/test__return_expr.snap @@ -4,6 +4,7 @@ expression: "fn main():int {\n return 5;\n let a = 3; // Nothing regarding --- ; ModuleID = 'main.mun' source_filename = "main.mun" +target triple = "x86-64" define i64 @main() { body: diff --git a/crates/mun_codegen/src/snapshots/test__return_type.snap b/crates/mun_codegen/src/snapshots/test__return_type.snap index 4ce17d4d7..5c42186f3 100644 --- a/crates/mun_codegen/src/snapshots/test__return_type.snap +++ b/crates/mun_codegen/src/snapshots/test__return_type.snap @@ -4,6 +4,7 @@ expression: "fn main():int {\n 0\n}" --- ; ModuleID = 'main.mun' source_filename = "main.mun" +target triple = "x86-64" define i64 @main() { body: diff --git a/crates/mun_codegen/src/snapshots/test__shadowing.snap b/crates/mun_codegen/src/snapshots/test__shadowing.snap index 28bbb04a8..9604803e6 100644 --- a/crates/mun_codegen/src/snapshots/test__shadowing.snap +++ b/crates/mun_codegen/src/snapshots/test__shadowing.snap @@ -4,6 +4,7 @@ expression: "fn foo(a:int):int {\n let a = a+1;\n {\n let a = a+2;\ --- ; ModuleID = 'main.mun' source_filename = "main.mun" +target triple = "x86-64" define i64 @foo(i64) { body: diff --git a/crates/mun_codegen/src/snapshots/test__struct_test.snap b/crates/mun_codegen/src/snapshots/test__struct_test.snap index 7c12ba243..c4d871f85 100644 --- a/crates/mun_codegen/src/snapshots/test__struct_test.snap +++ b/crates/mun_codegen/src/snapshots/test__struct_test.snap @@ -4,13 +4,54 @@ expression: "struct(value) Bar(float, int, bool, Foo);\nstruct(value) Foo { a: i --- ; ModuleID = 'main.mun' source_filename = "main.mun" +target triple = "x86-64" -%DispatchTable = type { i8* (i8 addrspace(4)*, i8 addrspace(4)*)* } +%struct.MunTypeInfo = type { [16 x i8], i8 addrspace(4)*, i8 } +%struct.MunStructInfo = type { i8 addrspace(4)*, i8 addrspace(4)* addrspace(4)*, %struct.MunTypeInfo addrspace(4)* addrspace(4)*, i16 addrspace(4)*, i16 addrspace(4)*, i16, i16, i8 } +%DispatchTable = type { i8* addrspace(4)* (i8 addrspace(4)*, i8*)*, i8* addrspace(4)* (i8 addrspace(4)*, i8*)* } %Baz = type {} %Bar = type { double, i64, i1, %Foo } %Foo = type { i64 } +@"type_info::::name" = private unnamed_addr constant [4 x i8] c"Foo\00" +@"struct_info::::name" = private unnamed_addr constant [4 x i8] c"Foo\00" +@"struct_info::::field_names" = private unnamed_addr constant [2 x i8] c"a\00" +@0 = private unnamed_addr constant [1 x i8 addrspace(4)*] [i8 addrspace(4)* @"struct_info::::field_names"] +@"type_info::::name" = private unnamed_addr constant [10 x i8] c"core::i64\00" +@"type_info::" = private unnamed_addr constant { [16 x i8], [10 x i8]*, i8 } { [16 x i8] c"G\13;t\97j8\18\D7M\83`\1D\C8\19%", [10 x i8]* @"type_info::::name", i8 0 } +@"struct_info::::field_types" = private unnamed_addr constant [1 x %struct.MunTypeInfo addrspace(4)*] [%struct.MunTypeInfo addrspace(4)* @"type_info::"] +@1 = private unnamed_addr constant [1 x i16] zeroinitializer +@2 = private unnamed_addr constant [1 x i16] [i16 8] +@"type_info::" = private unnamed_addr constant { { [16 x i8], [4 x i8]*, i8 }, %struct.MunStructInfo } { { [16 x i8], [4 x i8]*, i8 } { [16 x i8] c"\13V\C6}z\D1c\8D\81k\FB\82-\D2\C2]", [4 x i8]* @"type_info::::name", i8 1 }, %struct.MunStructInfo { [4 x i8]* @"struct_info::::name", [1 x i8 addrspace(4)*]* @0, [1 x %struct.MunTypeInfo addrspace(4)*]* @"struct_info::::field_types", [1 x i16]* @1, [1 x i16]* @2, i16 1, i16 8, i8 1 } } +@"type_info::<*const TypeInfo>::name" = private unnamed_addr constant [16 x i8] c"*const TypeInfo\00" +@"type_info::<*const TypeInfo>" = private unnamed_addr constant { [16 x i8], [16 x i8]*, i8 } { [16 x i8] c"=\A1-\1F\C2\A7\88`d\90\F4\B5\BEE}x", [16 x i8]* @"type_info::<*const TypeInfo>::name", i8 0 } +@"type_info::::name" = private unnamed_addr constant [10 x i8] c"core::f64\00" +@"type_info::" = private unnamed_addr constant { [16 x i8], [10 x i8]*, i8 } { [16 x i8] c"`\DBF\9C?YJ%G\AD4\9F\D5\92%A", [10 x i8]* @"type_info::::name", i8 0 } +@"type_info::::name" = private unnamed_addr constant [11 x i8] c"core::bool\00" +@"type_info::" = private unnamed_addr constant { [16 x i8], [11 x i8]*, i8 } { [16 x i8] c"x\82\81m t7\03\CB\F8k\81-;\C9\84", [11 x i8]* @"type_info::::name", i8 0 } +@"type_info::<*const *mut core::void>::name" = private unnamed_addr constant [23 x i8] c"*const *mut core::void\00" +@"type_info::<*const *mut core::void>" = private unnamed_addr constant { [16 x i8], [23 x i8]*, i8 } { [16 x i8] c"\C5fO\BD\84\DF\06\BFd+\B1\9Abv\CE\00", [23 x i8]* @"type_info::<*const *mut core::void>::name", i8 0 } +@"type_info::::name" = private unnamed_addr constant [4 x i8] c"Bar\00" +@"struct_info::::name" = private unnamed_addr constant [4 x i8] c"Bar\00" +@"struct_info::::field_names" = private unnamed_addr constant [2 x i8] c"0\00" +@"struct_info::::field_names.1" = private unnamed_addr constant [2 x i8] c"1\00" +@"struct_info::::field_names.2" = private unnamed_addr constant [2 x i8] c"2\00" +@"struct_info::::field_names.3" = private unnamed_addr constant [2 x i8] c"3\00" +@3 = private unnamed_addr constant [4 x i8 addrspace(4)*] [i8 addrspace(4)* @"struct_info::::field_names", i8 addrspace(4)* @"struct_info::::field_names.1", i8 addrspace(4)* @"struct_info::::field_names.2", i8 addrspace(4)* @"struct_info::::field_names.3"] +@"struct_info::::field_types" = private unnamed_addr constant [4 x %struct.MunTypeInfo addrspace(4)*] [%struct.MunTypeInfo addrspace(4)* @"type_info::", %struct.MunTypeInfo addrspace(4)* @"type_info::", %struct.MunTypeInfo addrspace(4)* @"type_info::", %struct.MunTypeInfo addrspace(4)* @"type_info::"] +@4 = private unnamed_addr constant [4 x i16] [i16 0, i16 8, i16 16, i16 24] +@5 = private unnamed_addr constant [4 x i16] [i16 8, i16 8, i16 1, i16 8] +@"type_info::" = private unnamed_addr constant { { [16 x i8], [4 x i8]*, i8 }, %struct.MunStructInfo } { { [16 x i8], [4 x i8]*, i8 } { [16 x i8] c"\DD\C3_\88\FAq\B6\EF\14*\E6\1F56FS", [4 x i8]* @"type_info::::name", i8 1 }, %struct.MunStructInfo { [4 x i8]* @"struct_info::::name", [4 x i8 addrspace(4)*]* @3, [4 x %struct.MunTypeInfo addrspace(4)*]* @"struct_info::::field_types", [4 x i16]* @4, [4 x i16]* @5, i16 4, i16 8, i8 1 } } +@"type_info::<*const core::void>::name" = private unnamed_addr constant [18 x i8] c"*const core::void\00" +@"type_info::<*const core::void>" = private unnamed_addr constant { [16 x i8], [18 x i8]*, i8 } { [16 x i8] c"\EF\D3\E0ac~\5C\D4\EF\AE\B1}\CA\BE\DA\16", [18 x i8]* @"type_info::<*const core::void>::name", i8 0 } +@"type_info::<*mut core::void>::name" = private unnamed_addr constant [16 x i8] c"*mut core::void\00" +@"type_info::<*mut core::void>" = private unnamed_addr constant { [16 x i8], [16 x i8]*, i8 } { [16 x i8] c"\F0Y\22\FC\95\9E\7F\CE\08T\B1\A2\CD\A7\FAz", [16 x i8]* @"type_info::<*mut core::void>::name", i8 0 } +@"type_info::::name" = private unnamed_addr constant [4 x i8] c"Baz\00" +@"struct_info::::name" = private unnamed_addr constant [4 x i8] c"Baz\00" +@"type_info::" = private unnamed_addr constant { { [16 x i8], [4 x i8]*, i8 }, %struct.MunStructInfo } { { [16 x i8], [4 x i8]*, i8 } { [16 x i8] c"\F8\DC\E6\7F,\948\82\82\ED?\A7\97\96\8A|", [4 x i8]* @"type_info::::name", i8 1 }, %struct.MunStructInfo { [4 x i8]* @"struct_info::::name", i8 addrspace(4)* addrspace(4)* null, %struct.MunTypeInfo addrspace(4)* addrspace(4)* null, i16 addrspace(4)* null, i16 addrspace(4)* null, i16 0, i16 1, i8 1 } } +@global_type_table = private unnamed_addr constant [10 x %struct.MunTypeInfo addrspace(4)*] [%struct.MunTypeInfo addrspace(4)* @"type_info::", %struct.MunTypeInfo addrspace(4)* @"type_info::<*const TypeInfo>", %struct.MunTypeInfo addrspace(4)* @"type_info::", %struct.MunTypeInfo addrspace(4)* @"type_info::", %struct.MunTypeInfo addrspace(4)* @"type_info::", %struct.MunTypeInfo addrspace(4)* @"type_info::<*const *mut core::void>", %struct.MunTypeInfo addrspace(4)* @"type_info::", %struct.MunTypeInfo addrspace(4)* @"type_info::<*const core::void>", %struct.MunTypeInfo addrspace(4)* @"type_info::<*mut core::void>", %struct.MunTypeInfo addrspace(4)* @"type_info::"] @dispatchTable = global %DispatchTable zeroinitializer +@allocatorHandle = private unnamed_addr global i8* null define void @foo() { body: diff --git a/crates/mun_codegen/src/snapshots/test__true_is_true.snap b/crates/mun_codegen/src/snapshots/test__true_is_true.snap index 97257103d..a9923c2d8 100644 --- a/crates/mun_codegen/src/snapshots/test__true_is_true.snap +++ b/crates/mun_codegen/src/snapshots/test__true_is_true.snap @@ -4,6 +4,7 @@ expression: "fn test_true():bool {\n true\n}\n\nfn test_false():bool {\n f --- ; ModuleID = 'main.mun' source_filename = "main.mun" +target triple = "x86-64" define i1 @test_true() { body: diff --git a/crates/mun_codegen/src/snapshots/test__update_operators.snap b/crates/mun_codegen/src/snapshots/test__update_operators.snap index 5d794f463..9db047dfb 100644 --- a/crates/mun_codegen/src/snapshots/test__update_operators.snap +++ b/crates/mun_codegen/src/snapshots/test__update_operators.snap @@ -4,6 +4,7 @@ expression: "fn add(a:int, b:int):int {\n let result = a\n result += b\n resu --- ; ModuleID = 'main.mun' source_filename = "main.mun" +target triple = "x86-64" define i64 @add(i64, i64) { body: diff --git a/crates/mun_codegen/src/snapshots/test__update_parameter.snap b/crates/mun_codegen/src/snapshots/test__update_parameter.snap index 8ac10bef7..755d5891b 100644 --- a/crates/mun_codegen/src/snapshots/test__update_parameter.snap +++ b/crates/mun_codegen/src/snapshots/test__update_parameter.snap @@ -4,6 +4,7 @@ expression: "fn add_three(a:int):int {\n a += 3;\n a\n}" --- ; ModuleID = 'main.mun' source_filename = "main.mun" +target triple = "x86-64" define i64 @add_three(i64) { body: diff --git a/crates/mun_codegen/src/snapshots/test__void_return.snap b/crates/mun_codegen/src/snapshots/test__void_return.snap index 4b87964e2..4b6620b55 100644 --- a/crates/mun_codegen/src/snapshots/test__void_return.snap +++ b/crates/mun_codegen/src/snapshots/test__void_return.snap @@ -4,6 +4,7 @@ expression: "fn bar() {\n let a = 3;\n}\nfn foo(a:int) {\n let c = bar()\n --- ; ModuleID = 'main.mun' source_filename = "main.mun" +target triple = "x86-64" %DispatchTable = type { void ()* } diff --git a/crates/mun_codegen/src/snapshots/test__while_expr.snap b/crates/mun_codegen/src/snapshots/test__while_expr.snap index e56439f69..1d0ee2cf3 100644 --- a/crates/mun_codegen/src/snapshots/test__while_expr.snap +++ b/crates/mun_codegen/src/snapshots/test__while_expr.snap @@ -1,9 +1,10 @@ --- source: crates/mun_codegen/src/test.rs -expression: "fn foo(n:int) {\n while n<3 {\n n += 1;\n };\n\n // This will be completely optimized out\n while n<4 {\n break;\n };\n}" +expression: "fn foo(n:int) {\n while n<3 {\n n += 1;\n };\n\n // This will be completely optimized out\n while n<4 {\n break;\n };\n }" --- ; ModuleID = 'main.mun' source_filename = "main.mun" +target triple = "x86-64" define void @foo(i64) { body: diff --git a/crates/mun_codegen/src/symbols.rs b/crates/mun_codegen/src/symbols.rs deleted file mode 100644 index 8b1378917..000000000 --- a/crates/mun_codegen/src/symbols.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/crates/mun_codegen/src/test.rs b/crates/mun_codegen/src/test.rs index cba26e441..302629654 100644 --- a/crates/mun_codegen/src/test.rs +++ b/crates/mun_codegen/src/test.rs @@ -1,4 +1,4 @@ -use crate::{mock::MockDatabase, IrDatabase}; +use crate::{mock::MockDatabase, IrDatabase, ModuleBuilder}; use hir::{diagnostics::DiagnosticSink, line_index::LineIndex, Module, SourceDatabase}; use inkwell::OptimizationLevel; use mun_target::spec::Target; @@ -358,7 +358,7 @@ fn while_expr() { while n<4 { break; }; - } + } "#, ) } @@ -512,13 +512,12 @@ fn test_snapshot_with_optimization(text: &str, opt: OptimizationLevel) { let name = if !messages.is_empty() { messages.join("\n") } else { - format!( - "{}", - db.module_ir(file_id) - .llvm_module - .print_to_string() - .to_string() - ) + let _module_builder = + ModuleBuilder::new(&mut db, file_id).expect("Failed to initialize module builder"); + + // Generate IR + db.module_ir(file_id); + format!("{}", db.module().print_to_string().to_string()) }; insta::assert_snapshot!(insta::_macro_support::AutoName, name, &text); } diff --git a/crates/mun_codegen/src/type_info.rs b/crates/mun_codegen/src/type_info.rs index 65974b5e1..fee303fa6 100644 --- a/crates/mun_codegen/src/type_info.rs +++ b/crates/mun_codegen/src/type_info.rs @@ -16,7 +16,7 @@ impl From for u64 { } } -#[derive(Clone, Eq, Debug)] +#[derive(Clone, Debug, Eq)] pub struct TypeInfo { pub guid: Guid, pub name: String, @@ -35,6 +35,18 @@ impl PartialEq for TypeInfo { } } +impl Ord for TypeInfo { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.guid.cmp(&other.guid) + } +} + +impl PartialOrd for TypeInfo { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + impl TypeInfo { pub fn new>(name: S, group: TypeGroup) -> TypeInfo { TypeInfo { @@ -92,6 +104,12 @@ impl HasStaticTypeInfo for bool { } } +impl HasStaticTypeInfo for std::ffi::c_void { + fn type_info() -> TypeInfo { + TypeInfo::new("core::void", TypeGroup::FundamentalTypes) + } +} + impl HasStaticTypeInfo for *mut T { fn type_info() -> TypeInfo { TypeInfo::new( diff --git a/crates/mun_compiler/src/driver.rs b/crates/mun_compiler/src/driver.rs index d38f4f5fc..4849391b5 100644 --- a/crates/mun_compiler/src/driver.rs +++ b/crates/mun_compiler/src/driver.rs @@ -6,12 +6,9 @@ use crate::{ diagnostics::{diagnostics, Emit}, PathOrInline, }; -use mun_codegen::IrDatabase; +use mun_codegen::{IrDatabase, ModuleBuilder}; use mun_hir::{FileId, RelativePathBuf, SourceDatabase, SourceRoot, SourceRootId}; -use std::{ - path::{Path, PathBuf}, - sync::Arc, -}; +use std::{path::PathBuf, sync::Arc}; mod config; @@ -123,26 +120,9 @@ impl Driver { } impl Driver { - /// Computes the output path for the assembly of the specified file. - fn assembly_output_path(&self, file_id: FileId) -> PathBuf { - let relative_path: RelativePathBuf = self.db.file_relative_path(file_id); - let original_filename = Path::new(relative_path.file_name().unwrap()); - - // Add the `munlib` suffix to the original filename - let output_file_name = original_filename.with_extension("munlib"); - - // If there is an out dir specified, prepend the output directory - if let Some(ref out_dir) = self.out_dir { - out_dir.join(output_file_name) - } else { - output_file_name - } - } - /// Generate an assembly for the given file - pub fn write_assembly(&self, file_id: FileId) -> Result, failure::Error> { - let output_path = self.assembly_output_path(file_id); - mun_codegen::write_module_shared_object(&self.db, file_id, &output_path)?; - Ok(Some(output_path)) + pub fn write_assembly(&mut self, file_id: FileId) -> Result { + let module_builder = ModuleBuilder::new(&mut self.db, file_id)?; + module_builder.finalize(self.out_dir.as_deref()) } } diff --git a/crates/mun_compiler/src/driver/config.rs b/crates/mun_compiler/src/driver/config.rs index 36dca0750..1ac29a657 100644 --- a/crates/mun_compiler/src/driver/config.rs +++ b/crates/mun_compiler/src/driver/config.rs @@ -1,4 +1,4 @@ -use mun_codegen::OptimizationLevel; +pub use mun_codegen::OptimizationLevel; use mun_target::spec::Target; use std::path::PathBuf; diff --git a/crates/mun_compiler/src/lib.rs b/crates/mun_compiler/src/lib.rs index 6ad9108f6..effd3097d 100644 --- a/crates/mun_compiler/src/lib.rs +++ b/crates/mun_compiler/src/lib.rs @@ -54,12 +54,12 @@ impl CompilerOptions { } pub fn main(options: CompilerOptions) -> Result, failure::Error> { - let (driver, file_id) = Driver::with_file(options.config, options.input)?; + let (mut driver, file_id) = Driver::with_file(options.config, options.input)?; let mut writer = StandardStream::stderr(ColorChoice::Auto); if driver.emit_diagnostics(&mut writer)? { Ok(None) } else { - driver.write_assembly(file_id) + driver.write_assembly(file_id).map(Some) } } diff --git a/crates/mun_hir/src/code_model.rs b/crates/mun_hir/src/code_model.rs index 9f223e3f2..449a6ceb1 100644 --- a/crates/mun_hir/src/code_model.rs +++ b/crates/mun_hir/src/code_model.rs @@ -175,6 +175,16 @@ impl DefWithBody { } } +impl Visibility { + pub fn is_public(self) -> bool { + self == Visibility::Public + } + + pub fn is_private(self) -> bool { + self == Visibility::Private + } +} + /// Definitions that have a struct. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub enum DefWithStruct { @@ -208,7 +218,7 @@ impl DefWithStruct { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)] pub struct Function { pub(crate) id: FunctionId, } diff --git a/crates/mun_hir/src/ids.rs b/crates/mun_hir/src/ids.rs index 8069ebe6e..9e54ed612 100644 --- a/crates/mun_hir/src/ids.rs +++ b/crates/mun_hir/src/ids.rs @@ -90,7 +90,7 @@ pub(crate) trait AstItemDef: salsa::InternKey + Clone { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)] pub struct FunctionId(salsa::InternId); impl_intern_key!(FunctionId); diff --git a/crates/mun_hir/src/ty/infer.rs b/crates/mun_hir/src/ty/infer.rs index 69d6a5b5b..01022ec36 100644 --- a/crates/mun_hir/src/ty/infer.rs +++ b/crates/mun_hir/src/ty/infer.rs @@ -16,7 +16,6 @@ use crate::{ BinaryOp, Function, HirDatabase, Name, Path, TypeCtor, }; use rustc_hash::FxHashSet; -use std::mem; use std::ops::Index; use std::sync::Arc; @@ -629,7 +628,7 @@ impl<'a, D: HirDatabase> InferenceResultBuilder<'a, D> { fn resolve_all(mut self) -> InferenceResult { // FIXME resolve obligations as well (use Guidance if necessary) //let mut tv_stack = Vec::new(); - let mut expr_types = mem::replace(&mut self.type_of_expr, ArenaMap::default()); + let mut expr_types = std::mem::take(&mut self.type_of_expr); for (expr, ty) in expr_types.iter_mut() { //let resolved = self.resolve_ty_completely(&mut tv_stack, mem::replace(ty, Ty::Unknown)); if *ty == Ty::Unknown { @@ -637,7 +636,7 @@ impl<'a, D: HirDatabase> InferenceResultBuilder<'a, D> { } //*ty = resolved; } - let mut pat_types = mem::replace(&mut self.type_of_pat, ArenaMap::default()); + let mut pat_types = std::mem::take(&mut self.type_of_pat); for (pat, ty) in pat_types.iter_mut() { //let resolved = self.resolve_ty_completely(&mut tv_stack, mem::replace(ty, Ty::Unknown)); if *ty == Ty::Unknown { diff --git a/crates/mun_runtime/src/allocator.rs b/crates/mun_runtime/src/allocator.rs new file mode 100644 index 000000000..af6f2f8f2 --- /dev/null +++ b/crates/mun_runtime/src/allocator.rs @@ -0,0 +1,89 @@ +use parking_lot::RwLock; +use std::alloc::Layout; +use std::collections::HashMap; +use std::ops::Deref; +use std::{pin::Pin, ptr}; + +#[derive(Debug)] +#[repr(C)] +pub struct ObjectInfo { + pub ptr: *mut u8, + pub type_info: *const abi::TypeInfo, +} + +pub type ObjectHandle = *const ObjectInfo; + +/// Provides allocator capabilities for a runtime. +#[derive(Debug, Default)] +pub struct Allocator { + objects: RwLock>>>, +} + +impl Allocator { + /// Allocates a new instance of an Allocator + pub fn new() -> Self { + Default::default() + } + + /// Allocates a block of memory + fn alloc(&self, size: u64, alignment: u64) -> *mut u8 { + unsafe { + std::alloc::alloc(Layout::from_size_align(size as usize, alignment as usize).unwrap()) + } + } + + /// Allocates a managed object of the specified type. + /// + /// # Safety + /// + /// `type_info` must be a valid pointer and remain valid throughout the lifetime of the created + /// object. + pub(crate) unsafe fn create_object(&self, type_info: *const abi::TypeInfo) -> ObjectHandle { + let struct_info = type_info.as_ref().unwrap().as_struct().unwrap(); + + let ptr = self.alloc(struct_info.size() as u64, struct_info.alignment.into()); + let object = Box::pin(ObjectInfo { ptr, type_info }); + + // We want to return a pointer to the `ObjectInfo`, to be used as handle. + let handle = object.as_ref().deref() as *const _ as ObjectHandle; + + let mut objects = self.objects.write(); + objects.insert(handle, object); + + handle + } + + /// Creates a shallow clone of the `src` object at a newly allocated memory location. + /// + /// # Safety + /// + /// `src` must be a valid pointer. + pub(crate) unsafe fn clone_object(&self, src: ObjectHandle) -> ObjectHandle { + let clone = { + let objects = self.objects.read(); + let src = objects + .get(&src) + .unwrap_or_else(|| panic!("Object with handle '{:?}' does not exist.", src)); + + let type_info = src.type_info.as_ref().unwrap(); + let struct_info = type_info.as_struct().unwrap(); + + let size = struct_info.size(); + let dest = self.alloc(size as u64, struct_info.alignment.into()); + ptr::copy_nonoverlapping(src.ptr, dest, size); + + Box::pin(ObjectInfo { + ptr: dest, + type_info, + }) + }; + + // We want to return a pointer to the `ObjectInfo`, to be used as handle. + let handle = clone.as_ref().deref() as *const _ as ObjectHandle; + + let mut objects = self.objects.write(); + objects.insert(handle, clone); + + handle + } +} diff --git a/crates/mun_runtime/src/assembly.rs b/crates/mun_runtime/src/assembly.rs index 4086d335c..783d56d37 100644 --- a/crates/mun_runtime/src/assembly.rs +++ b/crates/mun_runtime/src/assembly.rs @@ -1,7 +1,7 @@ use std::io; use std::path::{Path, PathBuf}; -use crate::DispatchTable; +use crate::{Allocator, DispatchTable}; use abi::AssemblyInfo; use failure::Error; use libloading::Symbol; @@ -9,12 +9,14 @@ use libloading::Symbol; mod temp_library; use self::temp_library::TempLibrary; +use std::sync::Arc; /// An assembly is a hot reloadable compilation unit, consisting of one or more Mun modules. pub struct Assembly { library_path: PathBuf, library: Option, info: AssemblyInfo, + allocator: Arc, } impl Assembly { @@ -22,6 +24,7 @@ impl Assembly { pub fn load( library_path: &Path, runtime_dispatch_table: &mut DispatchTable, + allocator: Arc, ) -> Result { let library = TempLibrary::new(library_path)?; @@ -29,6 +32,12 @@ impl Assembly { let get_info: Symbol<'_, extern "C" fn() -> AssemblyInfo> = unsafe { library.library().get(b"get_info") }?; + let set_allocator_handle: Symbol<'_, extern "C" fn(*mut std::ffi::c_void)> = + unsafe { library.library().get(b"set_allocator_handle") }?; + + let allocator_ptr = Arc::into_raw(allocator.clone()) as *mut std::ffi::c_void; + set_allocator_handle(allocator_ptr); + let info = get_info(); for function in info.symbols.functions() { @@ -39,6 +48,7 @@ impl Assembly { library_path: library_path.to_path_buf(), library: Some(library), info, + allocator, }) } @@ -90,7 +100,7 @@ impl Assembly { self.library.take(); // TODO: Partial hot reload of an assembly - *self = Assembly::load(library_path, runtime_dispatch_table)?; + *self = Assembly::load(library_path, runtime_dispatch_table, self.allocator.clone())?; Ok(()) } diff --git a/crates/mun_runtime/src/lib.rs b/crates/mun_runtime/src/lib.rs index 56c98d6e6..19c7ea85c 100644 --- a/crates/mun_runtime/src/lib.rs +++ b/crates/mun_runtime/src/lib.rs @@ -8,6 +8,7 @@ mod assembly; mod function; #[macro_use] mod macros; +mod allocator; mod marshal; mod reflection; mod r#struct; @@ -16,11 +17,11 @@ mod type_info; #[cfg(test)] mod test; -use std::alloc::Layout; use std::collections::HashMap; +use std::ffi; use std::io; +use std::mem; use std::path::{Path, PathBuf}; -use std::ptr; use std::sync::mpsc::{channel, Receiver}; use std::time::Duration; @@ -31,9 +32,11 @@ use notify::{DebouncedEvent, RecommendedWatcher, RecursiveMode, Watcher}; pub use crate::marshal::Marshal; pub use crate::reflection::{ArgumentReflection, ReturnTypeReflection}; +pub use crate::allocator::Allocator; pub use crate::assembly::Assembly; use crate::function::IntoFunctionInfo; pub use crate::r#struct::StructRef; +use std::sync::Arc; /// Options for the construction of a [`Runtime`]. pub struct RuntimeOptions { @@ -61,11 +64,14 @@ impl RuntimeBuilder { }, }; - result.insert_fn("malloc", malloc as extern "C" fn(u64, u64) -> *mut u8); + result.insert_fn( + "new", + new as extern "C" fn(*const abi::TypeInfo, *mut ffi::c_void) -> *const *mut ffi::c_void, + ); result.insert_fn( "clone", - clone as extern "C" fn(*const u8, *const abi::TypeInfo) -> *mut u8, + clone as extern "C" fn(*const ffi::c_void, *mut ffi::c_void) -> *const *mut ffi::c_void, ); result @@ -127,25 +133,44 @@ pub struct Runtime { dispatch_table: DispatchTable, watcher: RecommendedWatcher, watcher_rx: Receiver, + allocator: Arc, _user_functions: Vec, } -extern "C" fn malloc(size: u64, alignment: u64) -> *mut u8 { - unsafe { - std::alloc::alloc(Layout::from_size_align(size as usize, alignment as usize).unwrap()) - } +/// Retrieve the allocator using the provided handle. +/// +/// # Safety +/// +/// The allocator must have been set using the `set_allocator_handle` call - exposed by the Mun +/// library. +unsafe fn get_allocator(alloc_handle: *mut ffi::c_void) -> Arc { + Arc::from_raw(alloc_handle as *const Allocator) +} + +extern "C" fn new( + type_info: *const abi::TypeInfo, + alloc_handle: *mut ffi::c_void, +) -> *const *mut ffi::c_void { + let allocator = unsafe { get_allocator(alloc_handle) }; + let handle = unsafe { allocator.create_object(type_info) as *const _ }; + + // Prevent destruction + mem::forget(allocator); + + handle } -extern "C" fn clone(src: *const u8, ty: *const abi::TypeInfo) -> *mut u8 { - let type_info = unsafe { ty.as_ref().unwrap() }; - let struct_info = type_info.as_struct().unwrap(); - let size = struct_info.field_offsets().last().cloned().unwrap_or(0) - + struct_info.field_sizes().last().cloned().unwrap_or(0); - let alignment = 8; +extern "C" fn clone( + src: *const ffi::c_void, + alloc_handle: *mut ffi::c_void, +) -> *const *mut ffi::c_void { + let allocator = unsafe { get_allocator(alloc_handle) }; + let handle = unsafe { allocator.clone_object(src as *const _) as *const _ }; + + // Prevent destruction + mem::forget(allocator); - let dest = malloc(size as u64, alignment); - unsafe { ptr::copy_nonoverlapping(src, dest, size as usize) }; - dest + handle } impl Runtime { @@ -169,6 +194,7 @@ impl Runtime { dispatch_table, watcher, watcher_rx: rx, + allocator: Arc::new(Allocator::new()), _user_functions: storages, }; @@ -187,7 +213,11 @@ impl Runtime { .into()); } - let mut assembly = Assembly::load(&library_path, &mut self.dispatch_table)?; + let mut assembly = Assembly::load( + &library_path, + &mut self.dispatch_table, + self.allocator.clone(), + )?; for dependency in assembly.info().dependencies() { self.add_assembly(Path::new(dependency))?; } @@ -205,6 +235,11 @@ impl Runtime { self.dispatch_table.get_fn(function_name) } + /// Returns the runtime's allocator. + pub fn get_allocator(&self) -> Arc { + self.allocator.clone() + } + /// Updates the state of the runtime. This includes checking for file changes, and reloading /// compiled assemblies. pub fn update(&mut self) -> bool { diff --git a/crates/mun_runtime/src/marshal.rs b/crates/mun_runtime/src/marshal.rs index 6660b7350..9310cd06b 100644 --- a/crates/mun_runtime/src/marshal.rs +++ b/crates/mun_runtime/src/marshal.rs @@ -31,7 +31,7 @@ impl Marshal for T { self } - fn marshal_from_ptr<'r>( + fn marshal_from_ptr( ptr: NonNull, _runtime: Rc>, _type_info: Option<&abi::TypeInfo>, diff --git a/crates/mun_runtime/src/reflection.rs b/crates/mun_runtime/src/reflection.rs index 2b2c0cf61..ff262997e 100644 --- a/crates/mun_runtime/src/reflection.rs +++ b/crates/mun_runtime/src/reflection.rs @@ -271,14 +271,74 @@ impl ArgumentReflection for *const abi::TypeInfo { } } +impl ArgumentReflection for *const std::ffi::c_void { + type Marshalled = Self; + + fn type_name(&self) -> &str { + "*const core::void" + } + + fn marshal(self) -> Self::Marshalled { + self + } +} + +impl ArgumentReflection for *mut std::ffi::c_void { + type Marshalled = Self; + + fn type_name(&self) -> &str { + "*mut core::void" + } + + fn marshal(self) -> Self::Marshalled { + self + } +} + +impl ArgumentReflection for *const *mut std::ffi::c_void { + type Marshalled = Self; + + fn type_name(&self) -> &str { + "*const *mut core::void" + } + + fn marshal(self) -> Self::Marshalled { + self + } +} + impl ReturnTypeReflection for *const abi::TypeInfo { - type Marshalled = *const abi::TypeInfo; + type Marshalled = Self; fn type_name() -> &'static str { "*const TypeInfo" } } +impl ReturnTypeReflection for *const std::ffi::c_void { + type Marshalled = Self; + + fn type_name() -> &'static str { + "*const core::void" + } +} + +impl ReturnTypeReflection for *mut std::ffi::c_void { + type Marshalled = Self; + + fn type_name() -> &'static str { + "*mut core::void" + } +} + +impl ReturnTypeReflection for *const *mut std::ffi::c_void { + type Marshalled = Self; + + fn type_name() -> &'static str { + "*const *mut core::void" + } +} + impl ReturnTypeReflection for f64 { type Marshalled = f64; diff --git a/crates/mun_runtime/src/struct.rs b/crates/mun_runtime/src/struct.rs index 589275c70..58a834a56 100644 --- a/crates/mun_runtime/src/struct.rs +++ b/crates/mun_runtime/src/struct.rs @@ -1,4 +1,5 @@ use crate::{ + allocator::ObjectHandle, marshal::Marshal, reflection::{ equals_argument_type, equals_return_type, ArgumentReflection, ReturnTypeReflection, @@ -6,15 +7,23 @@ use crate::{ Runtime, }; use std::cell::RefCell; +use std::ffi; use std::ptr::{self, NonNull}; use std::rc::Rc; +use std::sync::Arc; /// Represents a Mun struct pointer. /// /// A byte pointer is used to make pointer arithmetic easier. #[repr(transparent)] #[derive(Clone)] -pub struct RawStruct(*mut u8); +pub struct RawStruct(ObjectHandle); + +impl RawStruct { + pub fn get_ptr(&self) -> *mut u8 { + unsafe { self.0.as_ref().unwrap() }.ptr + } +} /// Type-agnostic wrapper for interoperability with a Mun struct. /// TODO: Handle destruction of `struct(value)` @@ -56,7 +65,7 @@ impl StructRef { unsafe fn offset_unchecked(&self, field_idx: usize) -> NonNull { let offset = *self.info.field_offsets().get_unchecked(field_idx); // self.raw is never null - NonNull::new_unchecked(self.raw.0.add(offset as usize)).cast::() + NonNull::new_unchecked(self.raw.get_ptr().add(offset as usize).cast::()) } /// Retrieves the value of the field corresponding to the specified `field_name`. @@ -166,15 +175,30 @@ impl Marshal for RawStruct { let type_info = type_info.unwrap(); let struct_info = type_info.as_struct().unwrap(); - let ptr = if struct_info.memory_kind == abi::StructMemoryKind::Value { - ptr.cast::().as_ptr() as *const _ + let alloc_handle = Arc::into_raw(runtime.borrow().get_allocator()) as *mut std::ffi::c_void; + let object_handle = if struct_info.memory_kind == abi::StructMemoryKind::Value { + // Create a new object using the runtime's intrinsic + let object_ptr: *const *mut ffi::c_void = + invoke_fn!(runtime.clone(), "new", type_info as *const _, alloc_handle).unwrap(); + let handle = object_ptr as ObjectHandle; + + let src = ptr.cast::().as_ptr() as *const _; + let dest = unsafe { handle.as_ref() }.unwrap().ptr; + let size = struct_info.size(); + unsafe { ptr::copy_nonoverlapping(src, dest, size) }; + + handle } else { - unsafe { ptr.as_ref() }.0 as *const _ + let ptr = unsafe { ptr.as_ref() }.0 as *const ffi::c_void; + + // Clone the struct using the runtime's intrinsic + let cloned_ptr: *const *mut ffi::c_void = + invoke_fn!(runtime.clone(), "clone", ptr, alloc_handle).unwrap(); + + cloned_ptr as ObjectHandle }; - // Clone the struct using the runtime's intrinsic - let cloned_ptr = invoke_fn!(runtime.clone(), "clone", ptr, type_info as *const _).unwrap(); - StructRef::new(runtime, type_info, RawStruct(cloned_ptr)) + StructRef::new(runtime, type_info, RawStruct(object_handle)) } fn marshal_to_ptr(value: RawStruct, mut ptr: NonNull, type_info: Option<&abi::TypeInfo>) { @@ -186,7 +210,7 @@ impl Marshal for RawStruct { let dest = ptr.cast::().as_ptr(); let size = struct_info.field_offsets().last().cloned().unwrap_or(0) + struct_info.field_sizes().last().cloned().unwrap_or(0); - unsafe { ptr::copy_nonoverlapping(value.0, dest, size as usize) }; + unsafe { ptr::copy_nonoverlapping(value.get_ptr(), dest, size as usize) }; } else { unsafe { *ptr.as_mut() = value }; } diff --git a/crates/mun_runtime/src/test.rs b/crates/mun_runtime/src/test.rs index 96d194dda..30f708bc6 100644 --- a/crates/mun_runtime/src/test.rs +++ b/crates/mun_runtime/src/test.rs @@ -48,13 +48,13 @@ impl TestDriver { rel_path: RelativePathBuf::from("main.mun"), contents: text.to_owned(), }; - let (driver, file_id) = Driver::with_file(config, input).unwrap(); + let (mut driver, file_id) = Driver::with_file(config, input).unwrap(); let mut err_stream = mun_compiler::StandardStream::stderr(ColorChoice::Auto); if driver.emit_diagnostics(&mut err_stream).unwrap() { err_stream.flush().unwrap(); panic!("compiler errors..") } - let out_path = driver.write_assembly(file_id).unwrap().unwrap(); + let out_path = driver.write_assembly(file_id).unwrap(); let builder = RuntimeBuilder::new(&out_path); TestDriver { _temp_dir: temp_dir, @@ -69,7 +69,7 @@ impl TestDriver { fn update(&mut self, text: &str) { self.runtime_mut(); // Ensures that the runtime is spawned prior to the update self.driver.set_file_text(self.file_id, text); - let out_path = self.driver.write_assembly(self.file_id).unwrap().unwrap(); + let out_path = self.driver.write_assembly(self.file_id).unwrap(); assert_eq!( &out_path, &self.out_path, "recompiling did not result in the same assembly" diff --git a/crates/mun_runtime/src/type_info.rs b/crates/mun_runtime/src/type_info.rs index 4309a025e..05f035d84 100644 --- a/crates/mun_runtime/src/type_info.rs +++ b/crates/mun_runtime/src/type_info.rs @@ -65,4 +65,3 @@ // } // } // -// diff --git a/crates/mun_runtime_capi/ffi b/crates/mun_runtime_capi/ffi index 75e6f0fec..5c8770e76 160000 --- a/crates/mun_runtime_capi/ffi +++ b/crates/mun_runtime_capi/ffi @@ -1 +1 @@ -Subproject commit 75e6f0fec3b34fd6926517d2a4e99d991a7f1db3 +Subproject commit 5c8770e76ec0a68eafd61949a6fc18b547a73a83 diff --git a/crates/mun_runtime_capi/src/tests.rs b/crates/mun_runtime_capi/src/tests.rs index 98b9ebd63..b43e2916a 100644 --- a/crates/mun_runtime_capi/src/tests.rs +++ b/crates/mun_runtime_capi/src/tests.rs @@ -21,12 +21,12 @@ impl TestDriver { rel_path: RelativePathBuf::from("main.mun"), contents: text.to_owned(), }; - let (driver, file_id) = Driver::with_file(config, input).unwrap(); + let (mut driver, file_id) = Driver::with_file(config, input).unwrap(); let mut err_stream = mun_compiler::StandardStream::stderr(ColorChoice::Auto); if driver.emit_diagnostics(&mut err_stream).unwrap() { panic!("compiler errors..") } - let out_path = driver.write_assembly(file_id).unwrap().unwrap(); + let out_path = driver.write_assembly(file_id).unwrap(); let runtime = make_runtime(&out_path); TestDriver { _temp_dir: temp_dir, diff --git a/crates/mun_syntax/src/ast/expr_extensions.rs b/crates/mun_syntax/src/ast/expr_extensions.rs index d4bf6abe8..95e063eca 100644 --- a/crates/mun_syntax/src/ast/expr_extensions.rs +++ b/crates/mun_syntax/src/ast/expr_extensions.rs @@ -90,7 +90,7 @@ impl BinExpr { } pub fn lhs(&self) -> Option { - children(self).nth(0) + children(self).next() } pub fn rhs(&self) -> Option { @@ -184,7 +184,7 @@ pub enum ElseBranch { impl ast::IfExpr { pub fn then_branch(&self) -> Option { - self.blocks().nth(0) + self.blocks().next() } pub fn else_branch(&self) -> Option { let res = match self.blocks().nth(1) { diff --git a/crates/mun_target/src/spec.rs b/crates/mun_target/src/spec.rs index ad834b78c..979b6d77e 100644 --- a/crates/mun_target/src/spec.rs +++ b/crates/mun_target/src/spec.rs @@ -30,6 +30,9 @@ pub struct Target { /// The name of the architecture. For example "x86" or "x86_64" pub arch: String, + /// [Data layout](http://llvm.org/docs/LangRef.html#data-layout) to pass to LLVM. + pub data_layout: String, + /// Linker flavor pub linker_flavor: LinkerFlavor, diff --git a/crates/mun_target/src/spec/x86_64_apple_darwin.rs b/crates/mun_target/src/spec/x86_64_apple_darwin.rs index 68062a22d..ae9714c44 100644 --- a/crates/mun_target/src/spec/x86_64_apple_darwin.rs +++ b/crates/mun_target/src/spec/x86_64_apple_darwin.rs @@ -16,6 +16,8 @@ pub fn target() -> TargetResult { target_env: String::new(), target_vendor: "apple".to_string(), arch: arch.to_string(), + data_layout: "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + .to_string(), linker_flavor: LinkerFlavor::Ld64, options: base, }) diff --git a/crates/mun_target/src/spec/x86_64_pc_windows_msvc.rs b/crates/mun_target/src/spec/x86_64_pc_windows_msvc.rs index e1b080969..fa846d2d4 100644 --- a/crates/mun_target/src/spec/x86_64_pc_windows_msvc.rs +++ b/crates/mun_target/src/spec/x86_64_pc_windows_msvc.rs @@ -10,6 +10,8 @@ pub fn target() -> TargetResult { target_env: "msvc".to_string(), target_vendor: "pc".to_string(), arch: "x86_64".to_string(), + data_layout: "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + .to_string(), linker_flavor: LinkerFlavor::Msvc, options: base, }) diff --git a/crates/mun_target/src/spec/x86_64_unknown_linux_gnu.rs b/crates/mun_target/src/spec/x86_64_unknown_linux_gnu.rs index f5115cd77..9b2760c16 100644 --- a/crates/mun_target/src/spec/x86_64_unknown_linux_gnu.rs +++ b/crates/mun_target/src/spec/x86_64_unknown_linux_gnu.rs @@ -10,6 +10,8 @@ pub fn target() -> TargetResult { target_env: "gnu".to_string(), target_vendor: "unknown".to_string(), arch: "x86_64".to_string(), + data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + .to_string(), linker_flavor: LinkerFlavor::Ld, options: base, })