diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 51a040a72..1be53e5de 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -2003,7 +2003,8 @@ gb_internal void lb_verify_function(lbModule *m, lbProcedure *p, bool dump_ll=fa } } LLVMVerifyFunction(p->value, LLVMPrintMessageAction); - exit_with_errors(); + lb_record_worker_failure(); + return; } } @@ -2023,11 +2024,11 @@ gb_internal WORKER_TASK_PROC(lb_llvm_module_verification_worker_proc) { String filepath_ll = lb_filepath_ll_for_module(m); if (LLVMPrintModuleToFile(m->mod, cast(char const *)filepath_ll.text, &llvm_error)) { gb_printf_err("LLVM Error: %s\n", llvm_error); - exit_with_errors(); - return false; + lb_record_worker_failure(); + return 1; } } - exit_with_errors(); + lb_record_worker_failure(); return 1; } return 0; @@ -2390,11 +2391,13 @@ gb_internal WORKER_TASK_PROC(lb_llvm_emit_worker_proc) { if (build_context.lto_kind != LTO_None) { if (LLVMWriteBitcodeToFile(wd->m->mod, cast(char *)wd->filepath_obj.text)) { gb_printf_err("Failed to write bitcode file: %.*s\n", LIT(wd->filepath_obj)); - exit_with_errors(); + lb_record_worker_failure(); + return 1; } } else if (LLVMTargetMachineEmitToFile(wd->target_machine, wd->m->mod, cast(char *)wd->filepath_obj.text, wd->code_gen_file_type, &llvm_error)) { gb_printf_err("LLVM Error: %s\n", llvm_error); - exit_with_errors(); + lb_record_worker_failure(); + return 1; } debugf("Generated File: %.*s\n", LIT(wd->filepath_obj)); return 0; @@ -2561,7 +2564,7 @@ gb_internal WORKER_TASK_PROC(lb_llvm_module_pass_worker_proc) { gb_printf_err("LLVM Error: %s\n", llvm_error); } } - exit_with_errors(); + lb_record_worker_failure(); return 1; } @@ -2602,6 +2605,8 @@ gb_internal void lb_generate_procedures(lbGenerator *gen, bool do_threading) { lb_generate_procedures_worker_proc(m); } } + + lb_exit_if_worker_failed(); } gb_internal WORKER_TASK_PROC(lb_generate_missing_procedures_to_check_worker_proc) { @@ -2674,6 +2679,8 @@ gb_internal void lb_llvm_function_passes(lbGenerator *gen, bool do_threading) { lb_llvm_function_pass_per_module(m); } } + + lb_exit_if_worker_failed(); } @@ -2699,6 +2706,8 @@ gb_internal void lb_llvm_module_passes_and_verification(lbGenerator *gen, bool d lb_llvm_module_pass_worker_proc(wd); } } + + lb_exit_if_worker_failed(); } gb_internal String lb_filepath_ll_for_module(lbModule *m) { @@ -2829,6 +2838,7 @@ gb_internal bool lb_llvm_object_generation(lbGenerator *gen, bool do_threading) } thread_pool_wait(&global_thread_pool); + lb_exit_if_worker_failed(); } else { for (auto const &entry : gen->modules) { lbModule *m = entry.value; diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index f798dd34d..b0610e511 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -16,6 +16,41 @@ gb_global isize lb_global_type_info_member_offsets_index = 0; gb_global isize lb_global_type_info_member_usings_index = 0; gb_global isize lb_global_type_info_member_tags_index = 0; +// A backend worker must not end the process: its siblings are still inside LLVM, and tearing the +// process down under them is what turns a reported error into a crash. A failing worker records the +// failure and returns; the driver exits once the pool has drained. Work stealing can run a task on +// the main thread, so this must not depend on which thread is executing +gb_global std::atomic lb_worker_failure; + +gb_internal void lb_record_worker_failure(void) { + lb_worker_failure.store(true, std::memory_order_release); +} + +gb_internal void lb_exit_if_worker_failed(void) { + if (lb_worker_failure.load(std::memory_order_acquire)) { + exit_with_errors(); + } +} + +// Without a handler installed, LLVM prints an error of its own and calls exit(1) from whichever +// thread it is on. +gb_internal void lb_llvm_diagnostic_handler(LLVMDiagnosticInfoRef di, void *) { + char *description = LLVMGetDiagInfoDescription(di); + defer (LLVMDisposeMessage(description)); + + switch (LLVMGetDiagInfoSeverity(di)) { + case LLVMDSError: + gb_printf_err("LLVM Error: %s\n", description); + lb_record_worker_failure(); + break; + case LLVMDSWarning: + gb_printf_err("LLVM Warning: %s\n", description); + break; + default: + break; + } +} + gb_internal WORKER_TASK_PROC(lb_init_module_worker_proc) { lbModule *m = cast(lbModule *)data; Checker *c = m->checker; @@ -58,6 +93,7 @@ gb_internal WORKER_TASK_PROC(lb_init_module_worker_proc) { m->module_name = module_name; m->ctx = LLVMContextCreate(); + LLVMContextSetDiagnosticHandler(m->ctx, lb_llvm_diagnostic_handler, nullptr); m->mod = LLVMModuleCreateWithNameInContext(m->module_name, m->ctx); // m->debug_builder = nullptr; if (build_context.no_plt) {