a working prototype

This commit is contained in:
Folkert 2021-08-15 23:20:05 +02:00
parent d8e38ef2ac
commit a0a0896622
11 changed files with 443 additions and 144 deletions

View file

@ -135,10 +135,6 @@ pub fn gen_and_eval<'a>(
&context, "",
));
// Add roc_alloc, roc_realloc, and roc_dealloc, since the repl has no
// platform to provide them.
add_default_roc_externs(&context, module, &builder, ptr_bytes);
// mark our zig-defined builtins as internal
for function in FunctionIterator::from_module(module) {
let name = function.get_name().to_str().unwrap();
@ -188,6 +184,10 @@ pub fn gen_and_eval<'a>(
exposed_to_host: MutSet::default(),
};
// Add roc_alloc, roc_realloc, and roc_dealloc, since the repl has no
// platform to provide them.
add_default_roc_externs(&env);
let (main_fn_name, main_fn) = roc_gen_llvm::llvm::build::build_procedures_return_main(
&env,
opt_level,

View file

@ -148,6 +148,9 @@ pub fn gen_from_mono_module(
exposed_to_host: loaded.exposed_to_host.keys().copied().collect(),
};
// TODO remove for debug only
roc_gen_llvm::llvm::externs::add_sjlj_roc_panic(&env);
roc_gen_llvm::llvm::build::build_procedures(
&env,
opt_level,

View file

@ -101,6 +101,14 @@ comptime {
exportStrFn(str.fromUtf8RangeC, "from_utf8_range");
}
// Utils
const utils = @import("utils.zig");
comptime {
exportUtilsFn(utils.test_panic, "test_panic");
@export(utils.panic, .{ .name = "roc_builtins.utils." ++ "panic", .linkage = .Weak });
}
// Export helpers - Must be run inside a comptime
fn exportBuiltinFn(comptime func: anytype, comptime func_name: []const u8) void {
@export(func, .{ .name = "roc_builtins." ++ func_name, .linkage = .Strong });
@ -121,6 +129,10 @@ fn exportDecFn(comptime func: anytype, comptime func_name: []const u8) void {
exportBuiltinFn(func, "dec." ++ func_name);
}
fn exportUtilsFn(comptime func: anytype, comptime func_name: []const u8) void {
exportBuiltinFn(func, "utils." ++ func_name);
}
// Custom panic function, as builtin Zig version errors during LLVM verification
pub fn panic(message: []const u8, stacktrace: ?*std.builtin.StackTrace) noreturn {
std.debug.print("{s}: {?}", .{ message, stacktrace });

View file

@ -62,10 +62,21 @@ pub fn dealloc(c_ptr: [*]u8, alignment: u32) void {
}
// must export this explicitly because right now it is not used from zig code
pub export fn panic(c_ptr: *c_void, alignment: u32) void {
pub fn panic(c_ptr: *c_void, alignment: u32) callconv(.C) void {
return @call(.{ .modifier = always_inline }, roc_panic, .{ c_ptr, alignment });
}
// indirection because otherwise zig creats an alias to the panic function which our LLVM code
// does not know how to deal with
pub fn test_panic(c_ptr: *c_void, alignment: u32) callconv(.C) void {
const cstr = @ptrCast([*:0]u8, c_ptr);
// const stderr = std.io.getStdErr().writer();
// stderr.print("Roc panicked: {s}!\n", .{cstr}) catch unreachable;
std.c.exit(1);
}
pub const Inc = fn (?[*]u8) callconv(.C) void;
pub const IncN = fn (?[*]u8, u64) callconv(.C) void;
pub const Dec = fn (?[*]u8) callconv(.C) void;

View file

@ -76,3 +76,5 @@ pub const DEC_ADD_WITH_OVERFLOW: &str = "roc_builtins.dec.add_with_overflow";
pub const DEC_SUB_WITH_OVERFLOW: &str = "roc_builtins.dec.sub_with_overflow";
pub const DEC_MUL_WITH_OVERFLOW: &str = "roc_builtins.dec.mul_with_overflow";
pub const DEC_DIV: &str = "roc_builtins.dec.div";
pub const UTILS_TEST_PANIC: &str = "roc_builtins.utils.test_panic";

View file

@ -179,6 +179,12 @@ fn build_object<'a, B: Backend<'a>>(
"roc_dealloc".into(),
"free".into(),
)?;
generate_wrapper(
&mut backend,
&mut output,
"roc_panic".into(),
"roc_builtins.utils.test_panic".into(),
)?;
}
// Setup layout_ids for procedure calls.

View file

@ -68,7 +68,7 @@ macro_rules! debug_info_init {
($env:expr, $function_value:expr) => {{
use inkwell::debug_info::AsDIScope;
let func_scope = $function_value.get_subprogram().unwrap();
let func_scope = $function_value.get_subprogram().expect("subprogram");
let lexical_block = $env.dibuilder.create_lexical_block(
/* scope */ func_scope.as_debug_info_scope(),
/* file */ $env.compile_unit.get_file(),
@ -163,7 +163,18 @@ pub struct Env<'a, 'ctx, 'env> {
#[repr(u32)]
pub enum PanicTagId {
NullTerminatedString,
NullTerminatedString = 0,
}
impl std::convert::TryFrom<u32> for PanicTagId {
type Error = ();
fn try_from(value: u32) -> Result<Self, Self::Error> {
match value {
0 => Ok(PanicTagId::NullTerminatedString),
_ => Err(()),
}
}
}
impl<'a, 'ctx, 'env> Env<'a, 'ctx, 'env> {
@ -384,14 +395,44 @@ fn add_intrinsics<'ctx>(ctx: &'ctx Context, module: &Module<'ctx>) {
let f64_type = ctx.f64_type();
let i1_type = ctx.bool_type();
let i8_type = ctx.i8_type();
let i8_ptr_type = i8_type.ptr_type(AddressSpace::Generic);
let i16_type = ctx.i16_type();
let i32_type = ctx.i32_type();
let i64_type = ctx.i64_type();
let void_type = ctx.void_type();
if let Some(func) = module.get_function("__muloti4") {
func.set_linkage(Linkage::WeakAny);
}
add_intrinsic(
module,
LLVM_SETJMP,
i32_type.fn_type(&[i8_ptr_type.into()], false),
);
if true {
add_intrinsic(
module,
LLVM_LONGJMP,
void_type.fn_type(&[i8_ptr_type.into()], false),
);
} else {
add_intrinsic(
module,
LLVM_LONGJMP,
void_type.fn_type(&[i8_ptr_type.into(), i32_type.into()], false),
);
}
add_intrinsic(
module,
LLVM_FRAME_ADDRESS,
i8_ptr_type.fn_type(&[i32_type.into()], false),
);
add_intrinsic(module, LLVM_STACK_SAVE, i8_ptr_type.fn_type(&[], false));
add_intrinsic(
module,
LLVM_LOG_F64,
@ -501,6 +542,16 @@ static LLVM_POW_F64: &str = "llvm.pow.f64";
static LLVM_CEILING_F64: &str = "llvm.ceil.f64";
static LLVM_FLOOR_F64: &str = "llvm.floor.f64";
// static LLVM_FRAME_ADDRESS: &str = "llvm.frameaddress";
static LLVM_FRAME_ADDRESS: &str = "llvm.frameaddress.p0i8";
static LLVM_STACK_SAVE: &str = "llvm.stacksave";
static LLVM_SETJMP: &str = "llvm.eh.sjlj.setjmp";
pub static LLVM_LONGJMP: &str = "llvm.eh.sjlj.longjmp";
// static LLVM_SETJMP: &str = "setjmp";
// pub static LLVM_LONGJMP: &str = "longjmp";
pub static LLVM_SADD_WITH_OVERFLOW_I8: &str = "llvm.sadd.with.overflow.i8";
pub static LLVM_SADD_WITH_OVERFLOW_I16: &str = "llvm.sadd.with.overflow.i16";
pub static LLVM_SADD_WITH_OVERFLOW_I32: &str = "llvm.sadd.with.overflow.i32";
@ -3109,13 +3160,21 @@ fn expose_function_to_host_help<'a, 'ctx, 'env>(
roc_function: FunctionValue<'ctx>,
c_function_name: &str,
) -> FunctionValue<'ctx> {
let context = env.context;
let wrapper_return_type = context.struct_type(
&[
context.i64_type().into(),
roc_function.get_type().get_return_type().unwrap(),
],
false,
);
let roc_wrapper_function = make_exception_catcher(env, roc_function);
let roc_function_type = roc_wrapper_function.get_type();
// STEP 1: turn `f : a,b,c -> d` into `f : a,b,c, &d -> {}`
let mut argument_types = roc_function_type.get_param_types();
let return_type = roc_function_type.get_return_type().unwrap();
let mut argument_types = roc_function.get_type().get_param_types();
let return_type = wrapper_return_type;
let output_type = return_type.ptr_type(AddressSpace::Generic);
argument_types.push(output_type.into());
@ -3191,6 +3250,231 @@ fn expose_function_to_host_help<'a, 'ctx, 'env>(
c_function
}
pub fn get_sjlj_buffer<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> PointerValue<'ctx> {
let type_ = env.context.i8_type().array_type(5 * env.ptr_bytes);
let global = match env.module.get_global("roc_sjlj_buffer") {
Some(global) => global,
None => env.module.add_global(type_, None, "roc_sjlj_buffer"),
};
global.set_initializer(&type_.const_zero());
env.builder
.build_bitcast(
global.as_pointer_value(),
env.context.i8_type().ptr_type(AddressSpace::Generic),
"cast_sjlj_buffer",
)
.into_pointer_value()
}
pub fn get_sjlj_message_buffer<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> PointerValue<'ctx> {
let type_ = env.context.i8_type().ptr_type(AddressSpace::Generic);
let global = match env.module.get_global("roc_sjlj_message_buffer") {
Some(global) => global,
None => env
.module
.add_global(type_, None, "roc_sjlj_message_buffer"),
};
global.set_initializer(&type_.const_zero());
global.as_pointer_value()
}
pub fn get_catcher_static<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
type_: BasicTypeEnum<'ctx>,
) -> PointerValue<'ctx> {
let global = match env.module.get_global("catcher_static") {
Some(global) => global,
None => env.module.add_global(type_, None, "catcher_static"),
};
global.set_initializer(&type_.const_zero());
global.as_pointer_value()
}
fn set_jump_and_catch_long_jump<'a, 'ctx, 'env, F, T>(
env: &Env<'a, 'ctx, 'env>,
parent: FunctionValue<'ctx>,
function: F,
calling_convention: u32,
arguments: &[BasicValueEnum<'ctx>],
return_type: T,
) -> BasicValueEnum<'ctx>
where
T: inkwell::types::BasicType<'ctx>,
F: Into<CallableValue<'ctx>>,
{
let context = env.context;
let builder = env.builder;
let call_result_type = context.struct_type(
&[context.i64_type().into(), return_type.as_basic_type_enum()],
false,
);
let result_alloca = builder.build_alloca(call_result_type, "result");
let then_block = context.append_basic_block(parent, "then_block");
let catch_block = context.append_basic_block(parent, "catch_block");
let cont_block = context.append_basic_block(parent, "cont_block");
let buffer = get_sjlj_buffer(env);
let cast = env
.builder
.build_bitcast(
buffer,
env.context
.i8_type()
.ptr_type(AddressSpace::Generic)
.array_type(5)
.ptr_type(AddressSpace::Generic),
"to [5 x i8*]",
)
.into_pointer_value();
let zero = env.context.i32_type().const_zero();
let index = env.context.i32_type().const_zero();
let fa = unsafe {
env.builder
.build_in_bounds_gep(cast, &[zero, index], "name")
};
let index = env.context.i32_type().const_int(2, false);
let ss = unsafe {
env.builder
.build_in_bounds_gep(cast, &[zero, index], "name")
};
let index = env.context.i32_type().const_int(3, false);
let error_msg = unsafe {
env.builder
.build_in_bounds_gep(cast, &[zero, index], "name")
};
let frame_address = env.call_intrinsic(
LLVM_FRAME_ADDRESS,
&[env.context.i32_type().const_zero().into()],
);
env.builder.build_store(fa, frame_address);
let stack_save = env.call_intrinsic(LLVM_STACK_SAVE, &[]);
env.builder.build_store(ss, stack_save);
let panicked_u32 = env.call_intrinsic(LLVM_SETJMP, &[buffer.into()]);
let panicked_bool = env.builder.build_int_compare(
IntPredicate::NE,
panicked_u32.into_int_value(),
panicked_u32.get_type().into_int_type().const_zero(),
"to_bool",
);
env.builder
.build_conditional_branch(panicked_bool, catch_block, then_block);
// all went well
{
builder.position_at_end(then_block);
let call = env.builder.build_call(function, arguments, "call_function");
call.set_call_convention(calling_convention);
let call_result = call.try_as_basic_value().left().unwrap();
let return_value = {
let v1 = call_result_type.const_zero();
let v2 = builder
.build_insert_value(v1, context.i64_type().const_zero(), 0, "set_no_error")
.unwrap();
let v3 = builder
.build_insert_value(v2, call_result, 1, "set_call_result")
.unwrap();
v3
};
builder.build_store(result_alloca, return_value);
env.builder.build_unconditional_branch(cont_block);
}
// something went wrong
{
builder.position_at_end(catch_block);
let error_msg = {
// u8**
let ptr_int_ptr = builder.build_bitcast(
error_msg,
env.context
.i8_type()
.ptr_type(AddressSpace::Generic)
.ptr_type(AddressSpace::Generic),
"cast",
);
// u8* again
let ptr_int = builder.build_load(ptr_int_ptr.into_pointer_value(), "ptr_int");
ptr_int
};
let u8_ptr = env.context.i8_type().ptr_type(AddressSpace::Generic);
let return_type = context.struct_type(&[context.i64_type().into(), u8_ptr.into()], false);
// let return_type = call_result_type;
let return_value = {
let v1 = return_type.const_zero();
// flag is non-zero, indicating failure
let flag = context.i64_type().const_int(1, false);
let v2 = builder
.build_insert_value(v1, flag, 0, "set_error")
.unwrap();
let v3 = builder
.build_insert_value(v2, error_msg, 1, "set_exception")
.unwrap();
v3
};
// bitcast result alloca so we can store our concrete type { flag, error_msg } in there
let result_alloca_bitcast = builder
.build_bitcast(
result_alloca,
return_type.ptr_type(AddressSpace::Generic),
"result_alloca_bitcast",
)
.into_pointer_value();
// store our return value
builder.build_store(result_alloca_bitcast, return_value);
env.builder.build_unconditional_branch(cont_block);
}
env.builder.position_at_end(cont_block);
let result = builder.build_load(result_alloca, "load_result");
// env.builder.build_return(Some(&result));
let our_result = get_catcher_static(env, call_result_type.into());
env.builder.build_store(our_result, result);
result
}
fn invoke_and_catch<'a, 'ctx, 'env, F, T>(
env: &Env<'a, 'ctx, 'env>,
parent: FunctionValue<'ctx>,
@ -3381,11 +3665,14 @@ fn make_exception_catching_wrapper<'a, 'ctx, 'env>(
let wrapper_return_type = context.struct_type(
&[
context.i64_type().into(),
roc_function_type.get_return_type().unwrap(),
roc_function.get_type().get_return_type().unwrap(),
],
false,
);
// argument_types.push(wrapper_return_type.ptr_type(AddressSpace::Generic).into());
// let wrapper_function_type = env.context.void_type().fn_type(&argument_types, false);
let wrapper_function_type = wrapper_return_type.fn_type(&argument_types, false);
// Add main to the module.
@ -3411,7 +3698,8 @@ fn make_exception_catching_wrapper<'a, 'ctx, 'env>(
debug_info_init!(env, wrapper_function);
let result = invoke_and_catch(
// invoke_and_catch(
let result = set_jump_and_catch_long_jump(
env,
wrapper_function,
roc_function,
@ -6161,45 +6449,12 @@ fn define_global_error_str<'a, 'ctx, 'env>(
}
fn throw_exception<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>, message: &str) {
let context = env.context;
let builder = env.builder;
// define the error message as a global
// (a hash is used such that the same value is not defined repeatedly)
let error_msg_global = define_global_error_str(env, message);
let info = {
// we represented both void and char pointers with `u8*`
let u8_ptr = context.i8_type().ptr_type(AddressSpace::Generic);
// allocate an exception (that can hold a pointer to a string)
let str_ptr_size = env
.context
.i64_type()
.const_int(env.ptr_bytes as u64, false);
let initial = cxa_allocate_exception(env, str_ptr_size);
// cast this to a void pointer
let error_msg_ptr =
builder.build_bitcast(error_msg_global.as_pointer_value(), u8_ptr, "unused");
// store this void pointer in the exception
let exception_type = u8_ptr;
let exception_value = error_msg_ptr;
let temp = builder
.build_bitcast(
initial,
exception_type.ptr_type(AddressSpace::Generic),
"exception_object_str_ptr_ptr",
)
.into_pointer_value();
builder.build_store(temp, exception_value);
initial
};
let cast = env
.builder
.build_bitcast(
@ -6215,87 +6470,6 @@ fn throw_exception<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>, message: &str) {
builder.build_unreachable();
}
fn cxa_allocate_exception<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
exception_size: IntValue<'ctx>,
) -> BasicValueEnum<'ctx> {
let name = "__cxa_allocate_exception";
let module = env.module;
let context = env.context;
let u8_ptr = context.i8_type().ptr_type(AddressSpace::Generic);
let function = match module.get_function(name) {
Some(gvalue) => gvalue,
None => {
// void *__cxa_allocate_exception(size_t thrown_size);
let cxa_allocate_exception = add_func(
module,
name,
u8_ptr.fn_type(&[context.i64_type().into()], false),
Linkage::External,
C_CALL_CONV,
);
cxa_allocate_exception
}
};
let call = env.builder.build_call(
function,
&[exception_size.into()],
"exception_object_void_ptr",
);
call.set_call_convention(C_CALL_CONV);
call.try_as_basic_value().left().unwrap()
}
fn cxa_throw_exception<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>, info: BasicValueEnum<'ctx>) {
let name = "__cxa_throw";
let module = env.module;
let context = env.context;
let builder = env.builder;
let u8_ptr = context.i8_type().ptr_type(AddressSpace::Generic);
let function = match module.get_function(name) {
Some(value) => value,
None => {
// void __cxa_throw (void *thrown_exception, std::type_info *tinfo, void (*dest) (void *) );
let cxa_throw = add_func(
module,
name,
context
.void_type()
.fn_type(&[u8_ptr.into(), u8_ptr.into(), u8_ptr.into()], false),
Linkage::External,
C_CALL_CONV,
);
cxa_throw
}
};
// global storing the type info of a c++ int (equivalent to `i32` in llvm)
// we just need any valid such value, and arbitrarily use this one
let ztii = match module.get_global("_ZTIi") {
Some(gvalue) => gvalue.as_pointer_value(),
None => {
let ztii = module.add_global(u8_ptr, Some(AddressSpace::Generic), "_ZTIi");
ztii.set_linkage(Linkage::External);
ztii.as_pointer_value()
}
};
let type_info = builder.build_bitcast(ztii, u8_ptr, "cast");
let null: BasicValueEnum = u8_ptr.const_zero().into();
let call = builder.build_call(function, &[info, type_info, null], "throw");
call.set_call_convention(C_CALL_CONV);
}
fn get_foreign_symbol<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
foreign_symbol: roc_module::ident::ForeignSymbol,

View file

@ -1,19 +1,22 @@
use crate::debug_info_init;
use crate::llvm::bitcode::call_void_bitcode_fn;
use crate::llvm::build::Env;
use crate::llvm::build::{add_func, C_CALL_CONV};
use crate::llvm::convert::ptr_int;
use inkwell::builder::Builder;
use inkwell::context::Context;
use inkwell::module::{Linkage, Module};
use inkwell::values::BasicValue;
use inkwell::module::Linkage;
use inkwell::types::BasicType;
use inkwell::values::{BasicValue, PointerValue};
use inkwell::AddressSpace;
use roc_builtins::bitcode;
/// 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,
) {
pub fn add_default_roc_externs<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) {
let ctx = env.context;
let module = env.module;
let builder = env.builder;
let ptr_bytes = env.ptr_bytes;
let usize_type = ptr_int(ctx, ptr_bytes);
let i8_ptr_type = ctx.i8_type().ptr_type(AddressSpace::Generic);
@ -139,4 +142,69 @@ pub fn add_default_roc_externs<'ctx>(
crate::llvm::build::verify_fn(fn_val);
}
}
add_sjlj_roc_panic(env)
}
pub fn add_sjlj_roc_panic<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) {
let ctx = env.context;
let module = env.module;
let builder = env.builder;
// roc_panic
{
use crate::llvm::build::LLVM_LONGJMP;
// The type of this function (but not the implementation) should have
// already been defined by the builtins, which rely on it.
let fn_val = module.get_function("roc_panic").unwrap();
let mut params = fn_val.get_param_iter();
let ptr_arg = params.next().unwrap();
// in debug mode, this is assumed to be NullTerminatedString
let _tag_id_arg = params.next().unwrap();
debug_assert!(params.next().is_none());
let subprogram = env.new_subprogram("roc_panic");
fn_val.set_subprogram(subprogram);
env.dibuilder.finalize();
// Add a basic block for the entry point
let entry = ctx.append_basic_block(fn_val, "entry");
builder.position_at_end(entry);
let buffer = crate::llvm::build::get_sjlj_buffer(env);
// write our error message pointer
let index = env.ptr_int().const_int(3 * env.ptr_bytes as u64, false);
let message_buffer_raw =
unsafe { builder.build_gep(buffer, &[index], "raw_msg_buffer_ptr") };
let message_buffer = builder.build_bitcast(
message_buffer_raw,
env.context
.i8_type()
.ptr_type(AddressSpace::Generic)
.ptr_type(AddressSpace::Generic),
"to **u8",
);
env.builder
.build_store(message_buffer.into_pointer_value(), ptr_arg);
let tag = env.context.i32_type().const_int(1, false);
if true {
let _call = env.build_intrinsic_call(LLVM_LONGJMP, &[buffer.into()]);
} else {
let _call = env.build_intrinsic_call(LLVM_LONGJMP, &[buffer.into(), tag.into()]);
}
builder.build_unreachable();
if cfg!(debug_assertions) {
crate::llvm::build::verify_fn(fn_val);
}
}
}

View file

@ -27,6 +27,25 @@ pub unsafe fn roc_dealloc(c_ptr: *mut c_void, _alignment: u32) {
libc::free(c_ptr)
}
#[no_mangle]
pub unsafe fn roc_panic(c_ptr: *mut c_void, tag_id: u32) {
use roc_gen_llvm::llvm::build::PanicTagId;
use libc::c_char;
use std::convert::TryFrom;
use std::ffi::CStr;
match PanicTagId::try_from(tag_id) {
Ok(PanicTagId::NullTerminatedString) => {
let slice = CStr::from_ptr(c_ptr as *const c_char);
let string = slice.to_str().unwrap();
eprintln!("Roc hit a panic: {}", string);
std::process::exit(1);
}
Err(_) => unreachable!(),
}
}
#[test]
fn roc_list_construction() {
let list = RocList::from_slice(&[1i64; 23]);

View file

@ -3,6 +3,7 @@
use crate::assert_evals_to;
use crate::assert_llvm_evals_to;
use crate::assert_non_opt_evals_to;
use core::ffi::c_void;
use indoc::indoc;
use roc_std::RocStr;

View file

@ -171,13 +171,6 @@ pub fn helper<'a>(
let builder = context.create_builder();
let module = roc_gen_llvm::llvm::build::module_from_builtins(context, "app");
// Add roc_alloc, roc_realloc, and roc_dealloc, since the repl has no
// platform to provide them.
add_default_roc_externs(context, &module, &builder, ptr_bytes);
// strip Zig debug stuff
module.strip_debug_info();
let opt_level = if cfg!(debug_assertions) {
OptLevel::Normal
} else {
@ -224,6 +217,13 @@ pub fn helper<'a>(
exposed_to_host: MutSet::default(),
};
// strip Zig debug stuff
module.strip_debug_info();
// Add roc_alloc, roc_realloc, and roc_dealloc, since the repl has no
// platform to provide them.
add_default_roc_externs(&env);
let (main_fn_name, main_fn) = roc_gen_llvm::llvm::build::build_procedures_return_main(
&env,
opt_level,
@ -233,6 +233,9 @@ pub fn helper<'a>(
env.dibuilder.finalize();
// strip all debug info: we don't use it at the moment and causes weird validation issues
module.strip_debug_info();
// Uncomment this to see the module's un-optimized LLVM instruction output:
// env.module.print_to_stderr();