Return panic tag from roc_panic to test platform

This commit is contained in:
Ayaz Hafiz 2022-11-22 16:54:23 -06:00
parent 9201cf0b32
commit a8122662c2
No known key found for this signature in database
GPG key ID: 0E2A37416A25EF58
5 changed files with 80 additions and 36 deletions

View file

@ -3349,7 +3349,7 @@ fn expose_function_to_host_help_c_abi_generic<'a, 'ctx, 'env>(
builder.position_at_end(entry);
let wrapped_layout = roc_result_layout(env.arena, return_layout, env.target_info);
let wrapped_layout = roc_call_result_layout(env.arena, return_layout, env.target_info);
call_roc_function(env, roc_function, &wrapped_layout, arguments_for_call)
} else {
call_roc_function(env, roc_function, &return_layout, arguments_for_call)
@ -3379,7 +3379,8 @@ fn expose_function_to_host_help_c_abi_gen_test<'a, 'ctx, 'env>(
// a tagged union to indicate to the test loader that a panic occurred.
// especially when running 32-bit binaries on a 64-bit machine, there
// does not seem to be a smarter solution
let wrapper_return_type = roc_result_type(env, basic_type_from_layout(env, &return_layout));
let wrapper_return_type =
roc_call_result_type(env, basic_type_from_layout(env, &return_layout));
let mut cc_argument_types = Vec::with_capacity_in(arguments.len(), env.arena);
for layout in arguments {
@ -3768,7 +3769,7 @@ fn expose_function_to_host_help_c_abi<'a, 'ctx, 'env>(
let return_type = match env.mode {
LlvmBackendMode::GenTest | LlvmBackendMode::WasmGenTest | LlvmBackendMode::CliTest => {
roc_result_type(env, roc_function.get_type().get_return_type().unwrap()).into()
roc_call_result_type(env, roc_function.get_type().get_return_type().unwrap()).into()
}
LlvmBackendMode::Binary | LlvmBackendMode::BinaryDev => {
@ -3889,6 +3890,21 @@ pub fn get_panic_msg_ptr<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> PointerVa
global.as_pointer_value()
}
/// Pointer to the panic tag.
/// Only non-zero values must be written into here.
pub fn get_panic_tag_ptr<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> PointerValue<'ctx> {
let i64_typ = env.context.i64_type();
let global_name = "roc_panic_msg_tag";
let global = env.module.get_global(global_name).unwrap_or_else(|| {
let global = env.module.add_global(i64_typ, None, global_name);
global.set_initializer(&i64_typ.const_zero());
global
});
global.as_pointer_value()
}
fn set_jump_and_catch_long_jump<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
parent: FunctionValue<'ctx>,
@ -3900,7 +3916,7 @@ fn set_jump_and_catch_long_jump<'a, 'ctx, 'env>(
let builder = env.builder;
let return_type = basic_type_from_layout(env, &return_layout);
let call_result_type = roc_result_type(env, return_type.as_basic_type_enum());
let call_result_type = roc_call_result_type(env, return_type.as_basic_type_enum());
let result_alloca = builder.build_alloca(call_result_type, "result");
let then_block = context.append_basic_block(parent, "then_block");
@ -3937,16 +3953,16 @@ fn set_jump_and_catch_long_jump<'a, 'ctx, 'env>(
// RocStr* global
let error_msg_ptr = get_panic_msg_ptr(env);
// i64* global
let error_tag_ptr = get_panic_tag_ptr(env);
let return_value = {
let v1 = call_result_type.const_zero();
// flag is non-zero, indicating failure
let flag = context.i64_type().const_int(1, false);
// tag must be non-zero, indicating failure
let tag = builder.build_load(error_tag_ptr, "load_panic_tag");
let v2 = builder
.build_insert_value(v1, flag, 0, "set_error")
.unwrap();
let v2 = builder.build_insert_value(v1, tag, 0, "set_error").unwrap();
let v3 = builder
.build_insert_value(v2, error_msg_ptr, 1, "set_exception")
@ -3979,7 +3995,7 @@ fn make_exception_catcher<'a, 'ctx, 'env>(
function_value
}
fn roc_result_layout<'a>(
fn roc_call_result_layout<'a>(
arena: &'a Bump,
return_layout: Layout<'a>,
target_info: TargetInfo,
@ -3989,14 +4005,14 @@ fn roc_result_layout<'a>(
Layout::struct_no_name_order(arena.alloc(elements))
}
fn roc_result_type<'a, 'ctx, 'env>(
fn roc_call_result_type<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
return_type: BasicTypeEnum<'ctx>,
) -> StructType<'ctx> {
env.context.struct_type(
&[
env.context.i64_type().into(),
env.context.i8_type().ptr_type(AddressSpace::Generic).into(),
zig_str_type(env).ptr_type(AddressSpace::Generic).into(),
return_type,
],
false,
@ -4011,7 +4027,7 @@ fn make_good_roc_result<'a, 'ctx, 'env>(
let context = env.context;
let builder = env.builder;
let v1 = roc_result_type(env, basic_type_from_layout(env, &return_layout)).const_zero();
let v1 = roc_call_result_type(env, basic_type_from_layout(env, &return_layout)).const_zero();
let v2 = builder
.build_insert_value(v1, context.i64_type().const_zero(), 0, "set_no_error")
@ -4058,7 +4074,8 @@ fn make_exception_catching_wrapper<'a, 'ctx, 'env>(
}
};
let wrapper_return_type = roc_result_type(env, basic_type_from_layout(env, &return_layout));
let wrapper_return_type =
roc_call_result_type(env, basic_type_from_layout(env, &return_layout));
// argument_types.push(wrapper_return_type.ptr_type(AddressSpace::Generic).into());

View file

@ -1,5 +1,5 @@
use crate::llvm::bitcode::call_void_bitcode_fn;
use crate::llvm::build::{add_func, get_panic_msg_ptr, C_CALL_CONV};
use crate::llvm::build::{add_func, get_panic_msg_ptr, get_panic_tag_ptr, C_CALL_CONV};
use crate::llvm::build::{CCReturn, Env, FunctionSpec};
use inkwell::module::Linkage;
use inkwell::types::BasicType;
@ -195,8 +195,7 @@ pub fn add_sjlj_roc_panic(env: &Env<'_, '_, '_>) {
let mut params = fn_val.get_param_iter();
let roc_str_arg = params.next().unwrap();
// in debug mode, this is assumed to be NullTerminatedString
let _tag_id_arg = params.next().unwrap();
let tag_id_arg = params.next().unwrap();
debug_assert!(params.next().is_none());
@ -210,17 +209,38 @@ pub fn add_sjlj_roc_panic(env: &Env<'_, '_, '_>) {
builder.position_at_end(entry);
let loaded_roc_str = match env.target_info.ptr_width() {
roc_target::PtrWidth::Bytes4 => roc_str_arg,
// On 64-bit we pass RocStrs by reference internally
roc_target::PtrWidth::Bytes8 => {
builder.build_load(roc_str_arg.into_pointer_value(), "load_roc_str")
}
};
// write our error message to the RocStr pointer
env.builder
.build_store(get_panic_msg_ptr(env), loaded_roc_str);
{
let loaded_roc_str = match env.target_info.ptr_width() {
roc_target::PtrWidth::Bytes4 => roc_str_arg,
// On 64-bit we pass RocStrs by reference internally
roc_target::PtrWidth::Bytes8 => {
builder.build_load(roc_str_arg.into_pointer_value(), "load_roc_str")
}
};
env.builder
.build_store(get_panic_msg_ptr(env), loaded_roc_str);
}
// write the panic tag.
// increment by 1, since the tag we'll get from the Roc program is 0-based,
// but we use 0 for marking a successful call.
{
let cast_tag_id = builder.build_int_z_extend(
tag_id_arg.into_int_value(),
env.context.i64_type(),
"zext_panic_tag",
);
let inc_tag_id = builder.build_int_add(
cast_tag_id,
env.context.i64_type().const_int(1, false),
"inc_panic_tag",
);
env.builder.build_store(get_panic_tag_ptr(env), inc_tag_id);
}
build_longjmp_call(env);

View file

@ -32,13 +32,13 @@ impl<T: Default> Default for RocCallResult<T> {
}
}
impl<T: Sized> From<RocCallResult<T>> for Result<T, String> {
impl<T: Sized> From<RocCallResult<T>> for Result<T, (String, u32)> {
fn from(call_result: RocCallResult<T>) -> Self {
match call_result.tag {
0 => Ok(unsafe { call_result.value.assume_init() }),
_ => Err({
n => Err({
let msg: &RocStr = unsafe { &*call_result.error_msg };
msg.as_str().to_owned()
(msg.as_str().to_owned(), (n - 1) as _)
}),
}
}