mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-04 12:18:19 +00:00
SJLJ roc_panic on AArch64
``` $ uname -m arm64 $ cargo test -p test_gen gen_num::abs_min_int_overflow Finished test [unoptimized + debuginfo] target(s) in 0.09s Running src/tests.rs (target/debug/deps/test_gen-b2041868d2cf26f3) running 1 test test gen_num::abs_min_int_overflow - should panic ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 933 filtered out; finished in 0.14s ```
This commit is contained in:
parent
d8d3d7b30c
commit
fa8108e3a8
4 changed files with 55 additions and 107 deletions
|
@ -163,6 +163,27 @@ comptime {
|
|||
@export(utils.panic, .{ .name = "roc_builtins.utils." ++ "panic", .linkage = .Weak });
|
||||
}
|
||||
|
||||
// Utils continued - SJLJ
|
||||
// For tests (in particular test_gen), roc_panic is implemented in terms of
|
||||
// setjmp/longjmp. LLVM is unable to generate code for longjmp on AArch64 (https://github.com/rtfeldman/roc/issues/2965),
|
||||
// so instead we ask Zig to please provide implementations for us, which is does
|
||||
// (seemingly via musl).
|
||||
pub usingnamespace @import("std").c.builtins;
|
||||
pub extern fn setjmp([*c]c_int) c_int;
|
||||
pub extern fn longjmp([*c]c_int, c_int) noreturn;
|
||||
pub extern fn _setjmp([*c]c_int) c_int;
|
||||
pub extern fn _longjmp([*c]c_int, c_int) noreturn;
|
||||
pub extern fn sigsetjmp([*c]c_int, c_int) c_int;
|
||||
pub extern fn siglongjmp([*c]c_int, c_int) noreturn;
|
||||
pub extern fn longjmperror() void;
|
||||
// Zig won't expose the externs (and hence link correctly) unless we force them to be used.
|
||||
pub export fn __roc_force_setjmp(it: [*c]c_int) c_int {
|
||||
return setjmp(it);
|
||||
}
|
||||
pub export fn __roc_force_longjmp(a0: [*c]c_int, a1: c_int) noreturn {
|
||||
longjmp(a0, a1);
|
||||
}
|
||||
|
||||
// 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 });
|
||||
|
|
|
@ -365,6 +365,9 @@ pub const UTILS_EXPECT_FAILED: &str = "roc_builtins.expect.expect_failed";
|
|||
pub const UTILS_GET_EXPECT_FAILURES: &str = "roc_builtins.expect.get_expect_failures";
|
||||
pub const UTILS_DEINIT_FAILURES: &str = "roc_builtins.expect.deinit_failures";
|
||||
|
||||
pub const UTILS_LONGJMP: &str = "longjmp";
|
||||
pub const UTILS_SETJMP: &str = "setjmp";
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct IntToIntrinsicName {
|
||||
pub options: [IntrinsicName; 10],
|
||||
|
|
|
@ -521,35 +521,11 @@ fn add_intrinsics<'ctx>(ctx: &'ctx Context, module: &Module<'ctx>) {
|
|||
let i8_ptr_type = i8_type.ptr_type(AddressSpace::Generic);
|
||||
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(
|
||||
ctx,
|
||||
module,
|
||||
LLVM_SETJMP,
|
||||
i32_type.fn_type(&[i8_ptr_type.into()], false),
|
||||
);
|
||||
|
||||
if true {
|
||||
add_intrinsic(
|
||||
ctx,
|
||||
module,
|
||||
LLVM_LONGJMP,
|
||||
void_type.fn_type(&[i8_ptr_type.into()], false),
|
||||
);
|
||||
} else {
|
||||
add_intrinsic(
|
||||
ctx,
|
||||
module,
|
||||
LLVM_LONGJMP,
|
||||
void_type.fn_type(&[i8_ptr_type.into(), i32_type.into()], false),
|
||||
);
|
||||
}
|
||||
|
||||
add_intrinsic(
|
||||
ctx,
|
||||
module,
|
||||
|
@ -628,9 +604,6 @@ static LLVM_LROUND_I64_F64: &str = "llvm.lround.i64.f64";
|
|||
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";
|
||||
|
||||
const LLVM_ADD_WITH_OVERFLOW: IntrinsicName =
|
||||
llvm_int_intrinsic!("llvm.sadd.with.overflow", "llvm.uadd.with.overflow");
|
||||
const LLVM_SUB_WITH_OVERFLOW: IntrinsicName =
|
||||
|
@ -3677,10 +3650,15 @@ fn expose_function_to_host_help_c_abi<'a, 'ctx, 'env>(
|
|||
}
|
||||
|
||||
pub fn get_sjlj_buffer<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> PointerValue<'ctx> {
|
||||
// The size of jump_buf is platform-dependent.
|
||||
// AArch64 needs 3 64-bit words, x86 seemingly needs less than that. Let's just make it 3
|
||||
// 64-bit words.
|
||||
// We can always increase the size, since we hand it off opaquely to both `setjmp` and `longjmp`.
|
||||
// The unused space won't be touched.
|
||||
let type_ = env
|
||||
.context
|
||||
.i8_type()
|
||||
.array_type(5 * env.target_info.ptr_width() as u32);
|
||||
.i32_type()
|
||||
.array_type(6 * env.target_info.ptr_width() as u32);
|
||||
|
||||
let global = match env.module.get_global("roc_sjlj_buffer") {
|
||||
Some(global) => global,
|
||||
|
@ -3692,12 +3670,26 @@ pub fn get_sjlj_buffer<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> PointerValu
|
|||
env.builder
|
||||
.build_bitcast(
|
||||
global.as_pointer_value(),
|
||||
env.context.i8_type().ptr_type(AddressSpace::Generic),
|
||||
env.context.i32_type().ptr_type(AddressSpace::Generic),
|
||||
"cast_sjlj_buffer",
|
||||
)
|
||||
.into_pointer_value()
|
||||
}
|
||||
|
||||
/// Pointer to pointer of the panic message.
|
||||
pub fn get_panic_msg_ptr<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> PointerValue<'ctx> {
|
||||
let ptr_to_u8_ptr = env.context.i8_type().ptr_type(AddressSpace::Generic);
|
||||
|
||||
let global_name = "roc_panic_msg_ptr";
|
||||
let global = env.module.get_global(global_name).unwrap_or_else(|| {
|
||||
let global = env.module.add_global(ptr_to_u8_ptr, None, global_name);
|
||||
global.set_initializer(&ptr_to_u8_ptr.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>,
|
||||
|
@ -3718,51 +3710,7 @@ fn set_jump_and_catch_long_jump<'a, 'ctx, 'env>(
|
|||
|
||||
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_u32 = call_bitcode_fn(env, &[buffer.into()], bitcode::UTILS_SETJMP);
|
||||
let panicked_bool = env.builder.build_int_compare(
|
||||
IntPredicate::NE,
|
||||
panicked_u32.into_int_value(),
|
||||
|
@ -3792,17 +3740,10 @@ fn set_jump_and_catch_long_jump<'a, 'ctx, 'env>(
|
|||
|
||||
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",
|
||||
);
|
||||
let ptr_int_ptr = get_panic_msg_ptr(env);
|
||||
|
||||
// u8* again
|
||||
let ptr_int = builder.build_load(ptr_int_ptr.into_pointer_value(), "ptr_int");
|
||||
let ptr_int = builder.build_load(ptr_int_ptr, "ptr_int");
|
||||
|
||||
ptr_int
|
||||
};
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
use crate::llvm::build::{add_func, C_CALL_CONV};
|
||||
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::{CCReturn, Env, FunctionSpec};
|
||||
use inkwell::module::Linkage;
|
||||
use inkwell::types::BasicType;
|
||||
use inkwell::values::BasicValue;
|
||||
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)
|
||||
|
@ -184,7 +186,7 @@ pub fn add_sjlj_roc_panic(env: &Env<'_, '_, '_>) {
|
|||
|
||||
// roc_panic
|
||||
{
|
||||
use crate::llvm::build::LLVM_LONGJMP;
|
||||
// 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.
|
||||
|
@ -210,29 +212,10 @@ pub fn add_sjlj_roc_panic(env: &Env<'_, '_, '_>) {
|
|||
let buffer = crate::llvm::build::get_sjlj_buffer(env);
|
||||
|
||||
// write our error message pointer
|
||||
let index = env
|
||||
.ptr_int()
|
||||
.const_int(3 * env.target_info.ptr_width() 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);
|
||||
env.builder.build_store(get_panic_msg_ptr(env), 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()]);
|
||||
}
|
||||
let _call = call_void_bitcode_fn(env, &[buffer.into(), tag.into()], bitcode::UTILS_LONGJMP);
|
||||
|
||||
builder.build_unreachable();
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue