From 61c12b22689495abcd1dd6f8834c1fb97e35d5e1 Mon Sep 17 00:00:00 2001 From: Bas Zalmstra Date: Sat, 23 May 2020 22:48:41 +0200 Subject: [PATCH] misc: refactored symbols.rs --- crates/mun_codegen/src/code_gen.rs | 53 +--- crates/mun_codegen/src/code_gen/symbols.rs | 309 ++++++++------------- crates/mun_codegen/src/ir/body.rs | 4 +- crates/mun_codegen/src/ir/file.rs | 3 +- crates/mun_codegen/src/ir/ir_types.rs | 2 +- crates/mun_codegen/src/ir/type_table.rs | 23 +- 6 files changed, 141 insertions(+), 253 deletions(-) diff --git a/crates/mun_codegen/src/code_gen.rs b/crates/mun_codegen/src/code_gen.rs index de07e495a..a60a65428 100644 --- a/crates/mun_codegen/src/code_gen.rs +++ b/crates/mun_codegen/src/code_gen.rs @@ -1,19 +1,16 @@ use crate::code_gen::linker::LinkerError; -use crate::value::{CanInternalize, IrTypeContext, IrValueContext}; +use crate::value::{IrTypeContext, IrValueContext}; use crate::IrDatabase; use failure::Fail; use hir::{FileId, RelativePathBuf}; use inkwell::targets::TargetData; use inkwell::{ - module::{Linkage, Module}, + module::Module, passes::{PassManager, PassManagerBuilder}, targets::{CodeModel, FileType, InitializationConfig, RelocMode, Target, TargetMachine}, - types::StructType, - values::{BasicValue, GlobalValue, UnnamedAddress}, - AddressSpace, OptimizationLevel, + OptimizationLevel, }; use mun_target::spec; -use std::ffi::CString; use std::io::{self, Write}; use std::{ path::{Path, PathBuf}, @@ -217,50 +214,6 @@ fn optimize_module(module: &Module, optimization_lvl: OptimizationLevel) { module_pass_manager.run_on(module); } -/// Intern a string by constructing a global value. Looks something like this: -/// ```c -/// const char[] GLOBAL_ = "str"; -/// ``` -pub(crate) fn intern_string( - context: &IrValueContext, - string: &str, - name: &str, -) -> inkwell::values::PointerValue { - CString::new(string) - .expect("could not convert IR string to CString") - .intern(name, context) - .into() -} - -/// 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 struct pointers -pub(crate) fn gen_struct_ptr_array( - context: &IrValueContext, - ir_type: StructType, - ptrs: &[inkwell::values::PointerValue], - name: &str, -) -> inkwell::values::PointerValue { - if ptrs.is_empty() { - ir_type - .ptr_type(AddressSpace::Generic) - .ptr_type(AddressSpace::Generic) - .const_null() - } else { - let ptr_array_ir = ir_type.ptr_type(AddressSpace::Generic).const_array(&ptrs); - - gen_global(context.module, &ptr_array_ir, name).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/symbols.rs b/crates/mun_codegen/src/code_gen/symbols.rs index 5e7c535d7..015ad6c5a 100644 --- a/crates/mun_codegen/src/code_gen/symbols.rs +++ b/crates/mun_codegen/src/code_gen/symbols.rs @@ -1,4 +1,4 @@ -use crate::code_gen::{gen_global, gen_struct_ptr_array, intern_string}; +use crate::ir::ir_types as ir; use crate::ir::{ abi_types::{gen_abi_types, AbiTypes}, dispatch_table::{DispatchTable, DispatchableFunction}, @@ -6,134 +6,105 @@ use crate::ir::{ type_table::TypeTable, }; use crate::type_info::TypeInfo; -use crate::value::IrValueContext; +use crate::value::{AsValue, CanInternalize, Global, IrValueContext, IterAsIrValue, Value}; use crate::IrDatabase; use hir::Ty; -use inkwell::{ - attributes::Attribute, - module::Linkage, - values::{GlobalValue, PointerValue, StructValue}, - AddressSpace, -}; +use inkwell::{attributes::Attribute, module::Linkage, AddressSpace}; use std::collections::HashSet; +use std::ffi::CString; /// Construct a `MunFunctionPrototype` struct for the specified HIR function. fn gen_prototype_from_function( db: &D, context: &IrValueContext, - types: &AbiTypes, function: hir::Function, -) -> StructValue { +) -> ir::FunctionPrototype { let module = context.module; let name = function.name(db).to_string(); - let name_ir = intern_string(context, &name, &name); - let _visibility = match function.visibility(db) { - hir::Visibility::Public => 0, - _ => 1, - }; + // Internalize the name of the function prototype + let name_str = CString::new(name.clone()) + .expect("function prototype name is not a valid CString") + .intern(format!("fn_sig::<{}>::name", &name), context); + // Get the `ir::TypeInfo` pointer for the return type of the function let fn_sig = function.ty(db).callable_sig(db).unwrap(); - let ret_type_ir = gen_signature_return_type(db, context, types, fn_sig.ret().clone()); + let return_type = gen_signature_return_type(db, context, fn_sig.ret().clone()); - let param_types: Vec = fn_sig + // Construct an array of pointers to `ir::TypeInfo`s for the arguments of the prototype + let arg_types = fn_sig .params() .iter() .map(|ty| { TypeTable::get(module, &db.type_info(ty.clone())) - .unwrap() - .as_pointer_value() + .expect("expected a TypeInfo for a prototype argument but it was not found") + .as_value(context) }) - .collect(); - - let param_types = gen_struct_ptr_array( - context, - types.type_info_type, - ¶m_types, - &format!("fn_sig::<{}>::arg_types", name), - ); - let num_params = fn_sig.params().len(); - - let signature = types.function_signature_type.const_named_struct(&[ - param_types.into(), - ret_type_ir.into(), - module - .get_context() - .i16_type() - .const_int(num_params as u64, false) - .into(), - ]); - - types - .function_prototype_type - .const_named_struct(&[name_ir.into(), signature.into()]) + .into_const_private_pointer_or_null(format!("fn_sig::<{}>::arg_types", &name), context); + + ir::FunctionPrototype { + name: name_str.as_value(context), + signature: ir::FunctionSignature { + arg_types, + return_type, + num_arg_types: fn_sig.params().len() as u16, + }, + } } /// Construct a `MunFunctionPrototype` struct for the specified dispatch table function. fn gen_prototype_from_dispatch_entry( context: &IrValueContext, - types: &AbiTypes, function: &DispatchableFunction, -) -> StructValue { +) -> ir::FunctionPrototype { let module = context.module; - let name_str = intern_string( - &context, - &function.prototype.name, - &format!("fn_sig::<{}>::name", function.prototype.name), - ); - // let _visibility = match function.visibility(db) { - // hir::Visibility::Public => 0, - // _ => 1, - // }; - let ret_type_ir = gen_signature_return_type_from_type_info( - context, - types, - function.prototype.ret_type.clone(), - ); - let param_types: Vec = function + + // Internalize the name of the function prototype + let name_str = CString::new(function.prototype.name.clone()) + .expect("function prototype name is not a valid CString") + .intern( + format!("fn_sig::<{}>::name", function.prototype.name), + context, + ); + + // Get the `ir::TypeInfo` pointer for the return type of the function + let return_type = + gen_signature_return_type_from_type_info(context, function.prototype.ret_type.clone()); + + // Construct an array of pointers to `ir::TypeInfo`s for the arguments of the prototype + let arg_types = function .prototype .arg_types .iter() .map(|type_info| { TypeTable::get(module, type_info) - .unwrap() - .as_pointer_value() + .expect("expected a TypeInfo for a prototype argument but it was not found") + .as_value(context) }) - .collect(); - let param_types = gen_struct_ptr_array( - context, - types.type_info_type, - ¶m_types, - &format!("{}_param_types", function.prototype.name), - ); - let num_params = function.prototype.arg_types.len(); - - let signature = types.function_signature_type.const_named_struct(&[ - param_types.into(), - ret_type_ir.into(), - module - .get_context() - .i16_type() - .const_int(num_params as u64, false) - .into(), - ]); - - types - .function_prototype_type - .const_named_struct(&[name_str.into(), signature.into()]) + .into_const_private_pointer_or_null( + format!("{}_param_types", function.prototype.name), + context, + ); + + ir::FunctionPrototype { + name: name_str.as_value(context), + signature: ir::FunctionSignature { + arg_types, + return_type, + num_arg_types: function.prototype.arg_types.len() as u16, + }, + } } -/// Given a function, construct a pointer to a `MunTypeInfo` global that represents the return type +/// Given a function, construct a pointer to a `ir::TypeInfo` global that represents the return type /// of the function; or `null` if the return type is empty. fn gen_signature_return_type( db: &D, context: &IrValueContext, - types: &AbiTypes, ret_type: Ty, -) -> PointerValue { +) -> Value<*const ir::TypeInfo> { gen_signature_return_type_from_type_info( context, - types, if ret_type.is_empty() { None } else { @@ -142,23 +113,19 @@ fn gen_signature_return_type( ) } -/// Given a function, construct a pointer to a `MunTypeInfo` global that represents the return type +/// Given a function, construct a pointer to a `ir::TypeInfo` global that represents the return type /// of the function; or `null` if the return type is empty. fn gen_signature_return_type_from_type_info( context: &IrValueContext, - types: &AbiTypes, ret_type: Option, -) -> PointerValue { - if let Some(ret_type) = ret_type { - TypeTable::get(context.module, &ret_type) - .unwrap() - .as_pointer_value() - } else { - types - .type_info_type - .ptr_type(AddressSpace::Generic) - .const_null() - } +) -> Value<*const ir::TypeInfo> { + ret_type + .map(|info| { + TypeTable::get(context.module, &info) + .expect("could not find TypeInfo that should definitely be there") + .as_value(context) + }) + .unwrap_or_else(|| Value::null(context)) } /// Construct a global that holds a reference to all functions. e.g.: @@ -166,13 +133,13 @@ fn gen_signature_return_type_from_type_info( fn get_function_definition_array<'a, D: IrDatabase>( db: &D, context: &IrValueContext, - types: &AbiTypes, functions: impl Iterator, -) -> GlobalValue { +) -> Global<[ir::FunctionDefinition]> { let module = context.module; - let function_infos: Vec = functions + functions .map(|f| { let name = f.name(db).to_string(); + // Get the function from the cloned module and modify the linkage of the function. let value = module // If a wrapper function exists, use that (required for struct types) @@ -183,17 +150,14 @@ fn get_function_definition_array<'a, D: IrDatabase>( value.set_linkage(Linkage::Private); // Generate the signature from the function - let prototype = gen_prototype_from_function(db, context, types, *f); - - // Generate the function info value - types.function_definition_type.const_named_struct(&[ - prototype.into(), - value.as_global_value().as_pointer_value().into(), - ]) + let prototype = gen_prototype_from_function(db, context, *f); + ir::FunctionDefinition { + prototype, + fn_ptr: Value::from_raw(value.as_global_value().as_pointer_value()), + } }) - .collect(); - let function_infos = types.function_definition_type.const_array(&function_infos); - gen_global(module, &function_infos, "fn.get_info.functions") + .as_value(context) + .into_const_private_global("fn.get_info.functions", context) } /// Generate the dispatch table information. e.g.: @@ -202,52 +166,33 @@ fn get_function_definition_array<'a, D: IrDatabase>( /// ``` fn gen_dispatch_table( context: &IrValueContext, - types: &AbiTypes, dispatch_table: &DispatchTable, -) -> StructValue { +) -> ir::DispatchTable { let module = context.module; - // Generate a vector with all the function signatures - let signatures: Vec = dispatch_table + + // Generate an internal array that holds all the function prototypes + let prototypes = dispatch_table .entries() .iter() - .map(|entry| gen_prototype_from_dispatch_entry(context, types, entry)) - .collect(); - - // Construct an IR array from the signatures - let signatures = gen_global( - module, - &types.function_signature_type.const_array(&signatures), - "fn.get_info.dispatchTable.signatures", - ); + .map(|entry| gen_prototype_from_dispatch_entry(context, entry)) + .into_const_private_pointer("fn.get_info.dispatchTable.signatures", context); // Get the pointer to the global table (or nullptr if no global table was defined). - let dispatch_table_ptr = dispatch_table + let fn_ptrs = dispatch_table .global_value() .map(|_g| // TODO: This is a hack, the passed module here is a clone of the module with which the // dispatch table was created. Because of this we have to lookup the dispatch table // global again. There is however not a `GlobalValue::get_name` method so I just // hardcoded the name here. - module.get_global("dispatchTable").unwrap().as_pointer_value()) - .unwrap_or_else(|| { - module - .get_context() - .void_type() - .fn_type(&[], false) - .ptr_type(AddressSpace::Generic) - .ptr_type(AddressSpace::Generic) - .const_null() - }); - - types.dispatch_table_type.const_named_struct(&[ - signatures.as_pointer_value().into(), - dispatch_table_ptr.into(), - module - .get_context() - .i32_type() - .const_int(dispatch_table.entries().len() as u64, false) - .into(), - ]) + Value::from_raw(module.get_global("dispatchTable").unwrap().as_pointer_value())) + .unwrap_or_else(|| Value::null(context)); + + ir::DispatchTable { + prototypes, + fn_ptrs, + num_entries: dispatch_table.entries().len() as u32, + } } /// Constructs IR that exposes the types and symbols in the specified module. A function called @@ -265,34 +210,28 @@ pub(super) fn gen_reflection_ir( // Get all the types let abi_types = gen_abi_types(context.type_context); - let num_functions = api.len(); - let function_info = get_function_definition_array(db, context, &abi_types, api.iter()); + let num_functions = api.len() as u32; + let functions = get_function_definition_array(db, context, api.iter()); - let type_table_ir = if let Some(type_table) = module.get_global(TypeTable::NAME) { - type_table.as_pointer_value() - } else { - type_table.ty().ptr_type(AddressSpace::Generic).const_null() - }; + // Get the TypeTable global + let types = TypeTable::find_global(module) + .map(|g| g.as_value(context)) + .unwrap_or_else(|| Value::null(context)); // Construct the module info struct - let module_info = abi_types.module_info_type.const_named_struct(&[ - intern_string(context, "", "module_info::path").into(), - function_info.as_pointer_value().into(), - module - .get_context() - .i32_type() - .const_int(num_functions as u64, false) - .into(), - type_table_ir.into(), - module - .get_context() - .i32_type() - .const_int(type_table.num_types() as u64, false) - .into(), - ]); + let module_info = ir::ModuleInfo { + path: CString::new("") + .unwrap() + .intern("module_info::path", context) + .as_value(context), + functions: functions.as_value(context), + num_functions, + types, + num_types: type_table.num_types() as u32, + }; // Construct the dispatch table struct - let dispatch_table = gen_dispatch_table(context, &abi_types, dispatch_table); + let dispatch_table = gen_dispatch_table(context, dispatch_table); // Construct the actual `get_info` function gen_get_info_fn(db, context, &abi_types, module_info, dispatch_table); @@ -304,8 +243,8 @@ fn gen_get_info_fn( db: &impl IrDatabase, context: &IrValueContext, abi_types: &AbiTypes, - module_info: StructValue, - dispatch_table: StructValue, + module_info: ir::ModuleInfo, + dispatch_table: ir::DispatchTable, ) { let target = db.target(); let str_type = context.context.i8_type().ptr_type(AddressSpace::Generic); @@ -322,15 +261,9 @@ fn gen_get_info_fn( // MunModuleInfo get_info() { ... } // ``` let get_symbols_type = if target.options.is_like_windows { - context.context.void_type().fn_type( - &[abi_types - .assembly_info_type - .ptr_type(AddressSpace::Generic) - .into()], - false, - ) + Value::::get_ir_type(context.type_context) } else { - abi_types.assembly_info_type.fn_type(&[], false) + Value:: ir::AssemblyInfo>::get_ir_type(context.type_context) }; let get_symbols_fn = @@ -370,8 +303,8 @@ fn gen_get_info_fn( unsafe { builder.build_struct_gep(result_ptr, 3, "num_dependencies") }; // Assign the struct values one by one. - builder.build_store(symbols_addr, module_info); - builder.build_store(dispatch_table_addr, dispatch_table); + builder.build_store(symbols_addr, module_info.as_value(context).value); + builder.build_store(dispatch_table_addr, dispatch_table.as_value(context).value); builder.build_store( dependencies_addr, str_type.ptr_type(AddressSpace::Generic).const_null(), @@ -392,17 +325,13 @@ fn gen_get_info_fn( function::create_pass_manager(&context.module, db.optimization_lvl()).run_on(&get_symbols_fn); } +/// Generates a method `void set_allocator_handle(void*)` that stores the argument into the global +/// `allocatorHandle`. This global is used internally to reference the allocator used by this +/// munlib. fn gen_set_allocator_handle_fn(db: &impl IrDatabase, context: &IrValueContext) { - let allocator_handle_type = context.context.i8_type().ptr_type(AddressSpace::Generic); - - let set_allocator_handle_fn_type = context - .context - .void_type() - .fn_type(&[allocator_handle_type.into()], false); - let set_allocator_handle_fn = context.module.add_function( "set_allocator_handle", - set_allocator_handle_fn_type, + Value::::get_ir_type(context.type_context), Some(Linkage::DLLExport), ); diff --git a/crates/mun_codegen/src/ir/body.rs b/crates/mun_codegen/src/ir/body.rs index dad675afa..93c119259 100644 --- a/crates/mun_codegen/src/ir/body.rs +++ b/crates/mun_codegen/src/ir/body.rs @@ -14,6 +14,8 @@ use inkwell::{ }; use std::{collections::HashMap, sync::Arc}; +use crate::ir::ir_types as ir; +use crate::value::Global; use hir::ResolveBitness; use inkwell::basic_block::BasicBlock; use inkwell::values::{AggregateValueEnum, GlobalValue, PointerValue}; @@ -30,7 +32,7 @@ struct LoopInfo { pub(crate) struct ExternalGlobals { pub alloc_handle: Option, pub dispatch_table: Option, - pub type_table: Option, + pub type_table: Option>, } pub(crate) struct BodyIrGenerator<'a, 'b, D: IrDatabase> { diff --git a/crates/mun_codegen/src/ir/file.rs b/crates/mun_codegen/src/ir/file.rs index d5a8b0dd1..34e389abf 100644 --- a/crates/mun_codegen/src/ir/file.rs +++ b/crates/mun_codegen/src/ir/file.rs @@ -1,5 +1,6 @@ use super::body::ExternalGlobals; use crate::ir::{function, type_table::TypeTable}; +use crate::value::Global; use crate::{CodeGenParams, IrDatabase}; use hir::{FileId, ModuleDef}; use inkwell::module::Module; @@ -74,7 +75,7 @@ pub(crate) fn ir_query(db: &impl IrDatabase, file_id: FileId) -> Arc { ExternalGlobals { alloc_handle, dispatch_table, - type_table, + type_table: type_table.map(Global::from_raw), } }; diff --git a/crates/mun_codegen/src/ir/ir_types.rs b/crates/mun_codegen/src/ir/ir_types.rs index 294161980..ad2280a4c 100644 --- a/crates/mun_codegen/src/ir/ir_types.rs +++ b/crates/mun_codegen/src/ir/ir_types.rs @@ -87,7 +87,7 @@ pub struct ModuleInfo { #[derive(AsValue)] #[ir_name = "struct.MunDispatchTable"] pub struct DispatchTable { - pub signatures: Value<*const FunctionSignature>, + pub prototypes: Value<*const FunctionPrototype>, pub fn_ptrs: Value<*const *mut fn()>, pub num_entries: u32, } diff --git a/crates/mun_codegen/src/ir/type_table.rs b/crates/mun_codegen/src/ir/type_table.rs index 9b7e45cbb..433191b57 100644 --- a/crates/mun_codegen/src/ir/type_table.rs +++ b/crates/mun_codegen/src/ir/type_table.rs @@ -5,12 +5,7 @@ use crate::value::{AsValue, CanInternalize, Global, IrValueContext, IterAsIrValu use crate::IrDatabase; use hir::{Body, ExprId, InferenceResult}; use inkwell::module::Linkage; -use inkwell::{ - module::Module, - targets::TargetData, - types::ArrayType, - values::{GlobalValue, PointerValue}, -}; +use inkwell::{module::Module, targets::TargetData, types::ArrayType, values::PointerValue}; use std::collections::{BTreeSet, HashMap}; use std::ffi::CString; use std::{mem, sync::Arc}; @@ -27,6 +22,12 @@ impl TypeTable { /// The name of the TypeTable's LLVM `GlobalValue`. pub(crate) const NAME: &'static str = "global_type_table"; + /// Looks for a global symbol with the name of the TypeTable global in the specified `module`. + /// Returns the global value if it could be found, `None` otherwise. + pub fn find_global(module: &Module) -> Option> { + module.get_global(Self::NAME).map(Global::from_raw) + } + /// 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. @@ -34,7 +35,7 @@ impl TypeTable { &self, builder: &inkwell::builder::Builder, type_info: &TypeInfo, - table_ref: Option, + table_ref: Option>, ) -> PointerValue { let table_ref = table_ref.expect("no type table defined"); @@ -45,7 +46,7 @@ impl TypeTable { let ptr_to_type_info_ptr = unsafe { builder.build_struct_gep( - table_ref.as_pointer_value(), + table_ref.into(), index as u32, &format!("{}_ptr_ptr", type_info.name), ) @@ -56,8 +57,10 @@ impl TypeTable { } /// Retrieves the global `TypeInfo` IR value corresponding to `type_info`, if it exists. - pub fn get(module: &Module, type_info: &TypeInfo) -> Option { - module.get_global(&type_info_global_name(type_info)) + pub fn get(module: &Module, type_info: &TypeInfo) -> Option> { + module + .get_global(&type_info_global_name(type_info)) + .map(Global::from_raw) } /// Returns the number of types in the `TypeTable`.