diff --git a/cli/src/repl/gen.rs b/cli/src/repl/gen.rs index c73234651f..a0f013a4b6 100644 --- a/cli/src/repl/gen.rs +++ b/cli/src/repl/gen.rs @@ -1,17 +1,15 @@ use crate::repl::eval; use bumpalo::Bump; -use inkwell::builder::Builder; use inkwell::context::Context; -use inkwell::module::{Linkage, Module}; -use inkwell::AddressSpace; +use inkwell::module::Linkage; use roc_build::link::module_to_dylib; use roc_build::program::FunctionIterator; use roc_can::builtins::builtin_defs_map; use roc_collections::all::{MutMap, MutSet}; use roc_fmt::annotation::Formattable; use roc_fmt::annotation::{Newlines, Parens}; -use roc_gen::llvm::build::{build_proc, build_proc_header, set_name, OptLevel, C_CALL_CONV}; -use roc_gen::llvm::convert::ptr_int; +use roc_gen::llvm::build::{build_proc, build_proc_header, OptLevel}; +use roc_gen::llvm::externs::add_default_roc_externs; use roc_load::file::LoadingProblem; use roc_parse::parser::SyntaxError; use roc_types::pretty_print::{content_to_string, name_all_type_vars}; @@ -319,188 +317,3 @@ fn promote_expr_to_module(src: &str) -> String { buffer } - -/// Define functions for roc_alloc, roc_realloc, and roc_dealloc -/// which use libc implementations (malloc, realloc, and free) -fn add_default_roc_externs<'ctx>( - ctx: &'ctx Context, - module: &Module<'ctx>, - builder: &Builder<'ctx>, - ptr_bytes: u32, -) { - let usize_type = ptr_int(ctx, ptr_bytes); - let i32_type = ctx.i32_type(); - let i8_ptr_type = ctx.i8_type().ptr_type(AddressSpace::Generic); - - // roc_alloc - { - let fn_val = module.add_function( - "roc_alloc", - i8_ptr_type.fn_type( - &[ - // alignment: u32 - i32_type.into(), - // size: usize - usize_type.into(), - ], - false, - ), - Some(Linkage::External), - ); - - fn_val.set_call_conventions(C_CALL_CONV); - - let mut params = fn_val.get_param_iter(); - let alignment_arg = params.next().unwrap(); - let size_arg = params.next().unwrap(); - - debug_assert!(params.next().is_none()); - - set_name(alignment_arg, "alignment"); - set_name(size_arg, "size"); - - // Add a basic block for the entry point - let entry = ctx.append_basic_block(fn_val, "entry"); - - builder.position_at_end(entry); - - // Call libc malloc() - let retval = builder - .build_array_malloc(ctx.i8_type(), size_arg.into_int_value(), "call_malloc") - .unwrap(); - - builder.build_return(Some(&retval)); - - if cfg!(debug_assertions) { - roc_gen::llvm::build::verify_fn(fn_val); - } - } - - // roc_realloc - { - let libc_realloc_val = { - let fn_val = module.add_function( - "realloc", - i8_ptr_type.fn_type( - &[ - // ptr: *void - i8_ptr_type.into(), - // size: usize - usize_type.into(), - ], - false, - ), - Some(Linkage::External), - ); - - fn_val.set_call_conventions(C_CALL_CONV); - - let mut params = fn_val.get_param_iter(); - let ptr_arg = params.next().unwrap(); - let size_arg = params.next().unwrap(); - - debug_assert!(params.next().is_none()); - - set_name(ptr_arg, "ptr"); - set_name(size_arg, "size"); - - if cfg!(debug_assertions) { - roc_gen::llvm::build::verify_fn(fn_val); - } - - fn_val - }; - - let fn_val = module.add_function( - "roc_realloc", - i8_ptr_type.fn_type( - &[ - // alignment: u32 - i32_type.into(), - // ptr: *void - i8_ptr_type.into(), - // old_size: usize - usize_type.into(), - // new_size: usize - usize_type.into(), - ], - false, - ), - Some(Linkage::External), - ); - - fn_val.set_call_conventions(C_CALL_CONV); - - let mut params = fn_val.get_param_iter(); - let alignment_arg = params.next().unwrap(); - let ptr_arg = params.next().unwrap(); - let old_size_arg = params.next().unwrap(); - let new_size_arg = params.next().unwrap(); - - debug_assert!(params.next().is_none()); - - set_name(alignment_arg, "alignment"); - set_name(ptr_arg, "ptr"); - set_name(old_size_arg, "old_size"); - set_name(new_size_arg, "new_size"); - - // Add a basic block for the entry point - let entry = ctx.append_basic_block(fn_val, "entry"); - - builder.position_at_end(entry); - - // Call libc realloc() - let call = builder.build_call( - libc_realloc_val, - &[ptr_arg, new_size_arg], - "call_libc_realloc", - ); - - call.set_call_convention(C_CALL_CONV); - - let retval = call.try_as_basic_value().left().unwrap(); - - builder.build_return(Some(&retval)); - - if cfg!(debug_assertions) { - roc_gen::llvm::build::verify_fn(fn_val); - } - } - - // roc_dealloc - { - let fn_val = module.add_function( - "roc_dealloc", - ctx.void_type().fn_type( - &[ - // alignment: u32 - i32_type.into(), - // ptr: *void - i8_ptr_type.into(), - ], - false, - ), - Some(Linkage::External), - ); - - fn_val.set_call_conventions(C_CALL_CONV); - - let mut params = fn_val.get_param_iter(); - let alignment_arg = params.next().unwrap(); - let ptr_arg = params.next().unwrap(); - - debug_assert!(params.next().is_none()); - - set_name(alignment_arg, "alignment"); - set_name(ptr_arg, "ptr"); - - // Call libc free() - builder.build_free(ptr_arg.into_pointer_value()); - - builder.build_return(None); - - if cfg!(debug_assertions) { - roc_gen::llvm::build::verify_fn(fn_val); - } - } -} diff --git a/compiler/gen/src/llvm/externs.rs b/compiler/gen/src/llvm/externs.rs new file mode 100644 index 0000000000..7e6a202924 --- /dev/null +++ b/compiler/gen/src/llvm/externs.rs @@ -0,0 +1,191 @@ +use crate::llvm::build::{set_name, C_CALL_CONV}; +use crate::llvm::convert::ptr_int; +use inkwell::builder::Builder; +use inkwell::context::Context; +use inkwell::module::{Linkage, Module}; +use inkwell::AddressSpace; + +/// Define functions for roc_alloc, roc_realloc, and roc_dealloc +/// which use libc implementations (malloc, realloc, and free) +pub fn add_default_roc_externs<'ctx>( + ctx: &'ctx Context, + module: &Module<'ctx>, + builder: &Builder<'ctx>, + ptr_bytes: u32, +) { + let usize_type = ptr_int(ctx, ptr_bytes); + let i32_type = ctx.i32_type(); + let i8_ptr_type = ctx.i8_type().ptr_type(AddressSpace::Generic); + + // roc_alloc + { + let fn_val = module.add_function( + "roc_alloc", + i8_ptr_type.fn_type( + &[ + // alignment: u32 + i32_type.into(), + // size: usize + usize_type.into(), + ], + false, + ), + Some(Linkage::External), + ); + + fn_val.set_call_conventions(C_CALL_CONV); + + let mut params = fn_val.get_param_iter(); + let alignment_arg = params.next().unwrap(); + let size_arg = params.next().unwrap(); + + debug_assert!(params.next().is_none()); + + set_name(alignment_arg, "alignment"); + set_name(size_arg, "size"); + + // Add a basic block for the entry point + let entry = ctx.append_basic_block(fn_val, "entry"); + + builder.position_at_end(entry); + + // Call libc malloc() + let retval = builder + .build_array_malloc(ctx.i8_type(), size_arg.into_int_value(), "call_malloc") + .unwrap(); + + builder.build_return(Some(&retval)); + + if cfg!(debug_assertions) { + crate::llvm::build::verify_fn(fn_val); + } + } + + // roc_realloc + { + let libc_realloc_val = { + let fn_val = module.add_function( + "realloc", + i8_ptr_type.fn_type( + &[ + // ptr: *void + i8_ptr_type.into(), + // size: usize + usize_type.into(), + ], + false, + ), + Some(Linkage::External), + ); + + fn_val.set_call_conventions(C_CALL_CONV); + + let mut params = fn_val.get_param_iter(); + let ptr_arg = params.next().unwrap(); + let size_arg = params.next().unwrap(); + + debug_assert!(params.next().is_none()); + + set_name(ptr_arg, "ptr"); + set_name(size_arg, "size"); + + if cfg!(debug_assertions) { + crate::llvm::build::verify_fn(fn_val); + } + + fn_val + }; + + let fn_val = module.add_function( + "roc_realloc", + i8_ptr_type.fn_type( + &[ + // alignment: u32 + i32_type.into(), + // ptr: *void + i8_ptr_type.into(), + // old_size: usize + usize_type.into(), + // new_size: usize + usize_type.into(), + ], + false, + ), + Some(Linkage::External), + ); + + fn_val.set_call_conventions(C_CALL_CONV); + + let mut params = fn_val.get_param_iter(); + let alignment_arg = params.next().unwrap(); + let ptr_arg = params.next().unwrap(); + let old_size_arg = params.next().unwrap(); + let new_size_arg = params.next().unwrap(); + + debug_assert!(params.next().is_none()); + + set_name(alignment_arg, "alignment"); + set_name(ptr_arg, "ptr"); + set_name(old_size_arg, "old_size"); + set_name(new_size_arg, "new_size"); + + // Add a basic block for the entry point + let entry = ctx.append_basic_block(fn_val, "entry"); + + builder.position_at_end(entry); + + // Call libc realloc() + let call = builder.build_call( + libc_realloc_val, + &[ptr_arg, new_size_arg], + "call_libc_realloc", + ); + + call.set_call_convention(C_CALL_CONV); + + let retval = call.try_as_basic_value().left().unwrap(); + + builder.build_return(Some(&retval)); + + if cfg!(debug_assertions) { + crate::llvm::build::verify_fn(fn_val); + } + } + + // roc_dealloc + { + let fn_val = module.add_function( + "roc_dealloc", + ctx.void_type().fn_type( + &[ + // alignment: u32 + i32_type.into(), + // ptr: *void + i8_ptr_type.into(), + ], + false, + ), + Some(Linkage::External), + ); + + fn_val.set_call_conventions(C_CALL_CONV); + + let mut params = fn_val.get_param_iter(); + let alignment_arg = params.next().unwrap(); + let ptr_arg = params.next().unwrap(); + + debug_assert!(params.next().is_none()); + + set_name(alignment_arg, "alignment"); + set_name(ptr_arg, "ptr"); + + // Call libc free() + builder.build_free(ptr_arg.into_pointer_value()); + + builder.build_return(None); + + if cfg!(debug_assertions) { + crate::llvm::build::verify_fn(fn_val); + } + } +} diff --git a/compiler/gen/src/llvm/mod.rs b/compiler/gen/src/llvm/mod.rs index 195cbc9c98..72ae3851aa 100644 --- a/compiler/gen/src/llvm/mod.rs +++ b/compiler/gen/src/llvm/mod.rs @@ -6,4 +6,5 @@ pub mod build_list; pub mod build_str; pub mod compare; pub mod convert; +pub mod externs; pub mod refcounting; diff --git a/compiler/test_gen/src/helpers/eval.rs b/compiler/test_gen/src/helpers/eval.rs index 18ee2d8c62..f655fbede5 100644 --- a/compiler/test_gen/src/helpers/eval.rs +++ b/compiler/test_gen/src/helpers/eval.rs @@ -4,6 +4,7 @@ use roc_build::program::FunctionIterator; use roc_can::builtins::builtin_defs_map; use roc_can::def::Def; use roc_collections::all::{MutMap, MutSet}; +use roc_gen::llvm::externs::add_default_roc_externs; use roc_module::symbol::Symbol; use roc_types::subs::VarStore; @@ -184,7 +185,6 @@ pub fn helper<'a>( // strip Zig debug stuff module.strip_debug_info(); - let builder = context.create_builder(); let opt_level = if cfg!(debug_assertions) { roc_gen::llvm::build::OptLevel::Normal } else { @@ -216,6 +216,11 @@ pub fn helper<'a>( } } + // Add roc_alloc, roc_realloc, and roc_dealloc, since the repl has no + // platform to provide them. + let builder = context.create_builder(); + add_default_roc_externs(&context, module, &builder, ptr_bytes); + // Compile and add all the Procs before adding main let env = roc_gen::llvm::build::Env { arena: &arena,