mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-03 11:52:19 +00:00
a working prototype
This commit is contained in:
parent
d8e38ef2ac
commit
a0a0896622
11 changed files with 443 additions and 144 deletions
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 });
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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]);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue