diff --git a/crates/compiler/builtins/bitcode/src/main.zig b/crates/compiler/builtins/bitcode/src/main.zig index 742c4afff1..43a0df78cc 100644 --- a/crates/compiler/builtins/bitcode/src/main.zig +++ b/crates/compiler/builtins/bitcode/src/main.zig @@ -287,6 +287,82 @@ fn __roc_force_longjmp(a0: [*c]c_int, a1: c_int) callconv(.C) noreturn { longjmp(a0, a1); } +comptime { + if (builtin.os.tag == .windows) { + asm ( + \\.global windows_longjmp; + \\windows_longjmp: + \\ movq 0x00(%rcx), %rdx + \\ movq 0x08(%rcx), %rbx + \\ # note 0x10 is not used yet! + \\ movq 0x18(%rcx), %rbp + \\ movq 0x20(%rcx), %rsi + \\ movq 0x28(%rcx), %rdi + \\ movq 0x30(%rcx), %r12 + \\ movq 0x38(%rcx), %r13 + \\ movq 0x40(%rcx), %r14 + \\ movq 0x48(%rcx), %r15 + \\ + \\ # restore stack pointer + \\ movq 0x10(%rcx), %rsp + \\ + \\ # load jmp address + \\ movq 0x50(%rcx), %r8 + \\ + \\ # set up return value + \\ movq %rbx, %rax + \\ + \\ movdqu 0x60(%rcx), %xmm6 + \\ movdqu 0x70(%rcx), %xmm7 + \\ movdqu 0x80(%rcx), %xmm8 + \\ movdqu 0x90(%rcx), %xmm9 + \\ movdqu 0xa0(%rcx), %xmm10 + \\ movdqu 0xb0(%rcx), %xmm11 + \\ movdqu 0xc0(%rcx), %xmm12 + \\ movdqu 0xd0(%rcx), %xmm13 + \\ movdqu 0xe0(%rcx), %xmm14 + \\ movdqu 0xf0(%rcx), %xmm15 + \\ + \\ jmp *%r8 + \\ + \\.global windows_setjmp; + \\windows_setjmp: + \\ movq %rdx, 0x00(%rcx) + \\ movq %rbx, 0x08(%rcx) + \\ # note 0x10 is not used yet! + \\ movq %rbp, 0x18(%rcx) + \\ movq %rsi, 0x20(%rcx) + \\ movq %rdi, 0x28(%rcx) + \\ movq %r12, 0x30(%rcx) + \\ movq %r13, 0x38(%rcx) + \\ movq %r14, 0x40(%rcx) + \\ movq %r15, 0x48(%rcx) + \\ + \\ # the stack location right after the windows_setjmp call + \\ leaq 0x08(%rsp), %r8 + \\ movq %r8, 0x10(%rcx) + \\ + \\ movq (%rsp), %r8 + \\ movq %r8, 0x50(%rcx) + \\ + \\ movdqu %xmm6, 0x60(%rcx) + \\ movdqu %xmm7, 0x70(%rcx) + \\ movdqu %xmm8, 0x80(%rcx) + \\ movdqu %xmm9, 0x90(%rcx) + \\ movdqu %xmm10, 0xa0(%rcx) + \\ movdqu %xmm11, 0xb0(%rcx) + \\ movdqu %xmm12, 0xc0(%rcx) + \\ movdqu %xmm13, 0xd0(%rcx) + \\ movdqu %xmm14, 0xe0(%rcx) + \\ movdqu %xmm15, 0xf0(%rcx) + \\ + \\ xorl %eax, %eax + \\ ret + \\ + ); + } +} + // 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 }); diff --git a/crates/compiler/builtins/src/bitcode.rs b/crates/compiler/builtins/src/bitcode.rs index 5a1c549f2e..b5daf77e0c 100644 --- a/crates/compiler/builtins/src/bitcode.rs +++ b/crates/compiler/builtins/src/bitcode.rs @@ -445,6 +445,9 @@ pub const NOTIFY_PARENT_EXPECT: &str = "roc_builtins.utils.notify_parent_expect" pub const UTILS_LONGJMP: &str = "longjmp"; pub const UTILS_SETJMP: &str = "setjmp"; +pub const UTILS_WINDOWS_LONGJMP: &str = "windows_longjmp"; +pub const UTILS_WINDOWS_SETJMP: &str = "windows_setjmp"; + #[derive(Debug, Default)] pub struct IntToIntrinsicName { pub options: [IntrinsicName; 10], diff --git a/crates/compiler/gen_llvm/src/llvm/build.rs b/crates/compiler/gen_llvm/src/llvm/build.rs index 03292d1a62..1e68c0e6c0 100644 --- a/crates/compiler/gen_llvm/src/llvm/build.rs +++ b/crates/compiler/gen_llvm/src/llvm/build.rs @@ -5066,6 +5066,11 @@ fn expose_function_to_host_help_c_abi<'a, 'ctx>( } pub fn get_sjlj_buffer<'ctx>(env: &Env<'_, 'ctx, '_>) -> PointerValue<'ctx> { + let word_type = match env.target_info.ptr_width() { + PtrWidth::Bytes4 => env.context.i32_type(), + PtrWidth::Bytes8 => env.context.i64_type(), + }; + // The size of jump_buf is target-dependent. // - AArch64 needs 3 machine-sized words // - LLVM says the following about the SJLJ intrinsic: @@ -5077,11 +5082,15 @@ pub fn get_sjlj_buffer<'ctx>(env: &Env<'_, 'ctx, '_>) -> PointerValue<'ctx> { // The following three words are available for use in a target-specific manner. // // So, let's create a 5-word buffer. - let word_type = match env.target_info.ptr_width() { - PtrWidth::Bytes4 => env.context.i32_type(), - PtrWidth::Bytes8 => env.context.i64_type(), + let size = if env.target_info.operating_system == roc_target::OperatingSystem::Windows { + // Due to https://github.com/llvm/llvm-project/issues/72908 + // on windows, we store the register contents into this buffer directly! + 30 + } else { + 5 }; - let type_ = word_type.array_type(5); + + let type_ = word_type.array_type(size); let global = match env.module.get_global("roc_sjlj_buffer") { Some(global) => global, @@ -5099,9 +5108,12 @@ pub fn get_sjlj_buffer<'ctx>(env: &Env<'_, 'ctx, '_>) -> PointerValue<'ctx> { pub fn build_setjmp_call<'ctx>(env: &Env<'_, 'ctx, '_>) -> BasicValueEnum<'ctx> { let jmp_buf = get_sjlj_buffer(env); - if cfg!(target_arch = "aarch64") { + if env.target_info.architecture == roc_target::Architecture::Aarch64 { // Due to https://github.com/roc-lang/roc/issues/2965, we use a setjmp we linked in from Zig call_bitcode_fn(env, &[jmp_buf.into()], bitcode::UTILS_SETJMP) + } else if env.target_info.operating_system == roc_target::OperatingSystem::Windows { + // Due to https://github.com/llvm/llvm-project/issues/72908, we use a setjmp defined as asm in Zig + call_bitcode_fn(env, &[jmp_buf.into()], bitcode::UTILS_WINDOWS_SETJMP) } else { // Anywhere else, use the LLVM intrinsic. // https://llvm.org/docs/ExceptionHandling.html#llvm-eh-sjlj-setjmp diff --git a/crates/compiler/gen_llvm/src/llvm/externs.rs b/crates/compiler/gen_llvm/src/llvm/externs.rs index 8c5be59ffe..7df0e97311 100644 --- a/crates/compiler/gen_llvm/src/llvm/externs.rs +++ b/crates/compiler/gen_llvm/src/llvm/externs.rs @@ -315,11 +315,18 @@ pub fn add_sjlj_roc_panic(env: &Env<'_, '_, '_>) { pub fn build_longjmp_call(env: &Env) { let jmp_buf = get_sjlj_buffer(env); - if cfg!(target_arch = "aarch64") { - // Call the Zig-linked longjmp: `void longjmp(i32*, i32)` + if env.target_info.architecture == roc_target::Architecture::Aarch64 { + // Due to https://github.com/roc-lang/roc/issues/2965, we use a setjmp we linked in from Zig let tag = env.context.i32_type().const_int(1, false); let _call = call_void_bitcode_fn(env, &[jmp_buf.into(), tag.into()], bitcode::UTILS_LONGJMP); + } else if env.target_info.operating_system == roc_target::OperatingSystem::Windows { + let tag = env.context.i32_type().const_int(1, false); + let _call = call_void_bitcode_fn( + env, + &[jmp_buf.into(), tag.into()], + bitcode::UTILS_WINDOWS_LONGJMP, + ); } else { // Call the LLVM-intrinsic longjmp: `void @llvm.eh.sjlj.longjmp(i8* %setjmp_buf)` let jmp_buf_i8p = env.builder.new_build_pointer_cast(