From b55fc1494175d99435d78477db3d6091383dc90d Mon Sep 17 00:00:00 2001 From: Julien Portalier Date: Mon, 6 Jan 2025 22:18:39 +0100 Subject: [PATCH 01/12] Improve `__crystal_once` performance Co-authored-by: David Keller Based on the PR by @BlobCodes: https://github.com/crystal-lang/crystal/pull/15216 The performance improvement is two-fold: 1. the usage of a i8 instead of an i1 boolean to have 3 states instead of 2, which permits to quickly detect recursive calls without an array; 2. inline tricks to optimize the fast and slow paths. Unlike the PR: 1. Doesn't use atomics: it already uses a mutex that guarantees acquire release memory ordering semantics, and __crystal_once_init is only ever called in the main thread before any other thread is started. 2. Removes the need for a state maintained by the compiler, yet keeps forward and backward compatibility (both signatures are supported). --- src/compiler/crystal/codegen/class_var.cr | 6 +- src/compiler/crystal/codegen/const.cr | 4 +- src/compiler/crystal/codegen/once.cr | 51 +++++--- src/crystal/once.cr | 149 ++++++++++++++++------ 4 files changed, 145 insertions(+), 65 deletions(-) diff --git a/src/compiler/crystal/codegen/class_var.cr b/src/compiler/crystal/codegen/class_var.cr index 07d8ed0f96b1..a31a9b00bac5 100644 --- a/src/compiler/crystal/codegen/class_var.cr +++ b/src/compiler/crystal/codegen/class_var.cr @@ -25,8 +25,8 @@ class Crystal::CodeGenVisitor initialized_flag_name = class_var_global_initialized_name(class_var) initialized_flag = @main_mod.globals[initialized_flag_name]? unless initialized_flag - initialized_flag = @main_mod.globals.add(@main_llvm_context.int1, initialized_flag_name) - initialized_flag.initializer = @main_llvm_context.int1.const_int(0) + initialized_flag = @main_mod.globals.add(@main_llvm_context.int8, initialized_flag_name) + initialized_flag.initializer = @main_llvm_context.int8.const_int(0) initialized_flag.linkage = LLVM::Linkage::Internal if @single_module initialized_flag.thread_local = true if class_var.thread_local? end @@ -61,7 +61,7 @@ class Crystal::CodeGenVisitor initialized_flag_name = class_var_global_initialized_name(class_var) initialized_flag = @llvm_mod.globals[initialized_flag_name]? unless initialized_flag - initialized_flag = @llvm_mod.globals.add(llvm_context.int1, initialized_flag_name) + initialized_flag = @llvm_mod.globals.add(llvm_context.int8, initialized_flag_name) initialized_flag.thread_local = true if class_var.thread_local? end end diff --git a/src/compiler/crystal/codegen/const.cr b/src/compiler/crystal/codegen/const.cr index ec306e97296d..decfb3945e20 100644 --- a/src/compiler/crystal/codegen/const.cr +++ b/src/compiler/crystal/codegen/const.cr @@ -64,8 +64,8 @@ class Crystal::CodeGenVisitor initialized_flag_name = const.initialized_llvm_name initialized_flag = @main_mod.globals[initialized_flag_name]? unless initialized_flag - initialized_flag = @main_mod.globals.add(@main_llvm_context.int1, initialized_flag_name) - initialized_flag.initializer = @main_llvm_context.int1.const_int(0) + initialized_flag = @main_mod.globals.add(@main_llvm_context.int8, initialized_flag_name) + initialized_flag.initializer = @main_llvm_context.int8.const_int(0) initialized_flag.linkage = LLVM::Linkage::Internal if @single_module end initialized_flag diff --git a/src/compiler/crystal/codegen/once.cr b/src/compiler/crystal/codegen/once.cr index 2e91267c1f52..c87318fa9249 100644 --- a/src/compiler/crystal/codegen/once.cr +++ b/src/compiler/crystal/codegen/once.cr @@ -7,31 +7,46 @@ class Crystal::CodeGenVisitor if once_init_fun = typed_fun?(@main_mod, ONCE_INIT) once_init_fun = check_main_fun ONCE_INIT, once_init_fun - once_state_global = @main_mod.globals.add(once_init_fun.type.return_type, ONCE_STATE) - once_state_global.linkage = LLVM::Linkage::Internal if @single_module - once_state_global.initializer = once_init_fun.type.return_type.null - - state = call once_init_fun - store state, once_state_global + if once_init_fun.type.return_type.void? + call once_init_fun + else + # legacy (kept for backward compatibility): the compiler must save the + # state returned by __crystal_once_init + once_state_global = @main_mod.globals.add(once_init_fun.type.return_type, ONCE_STATE) + once_state_global.linkage = LLVM::Linkage::Internal if @single_module + once_state_global.initializer = once_init_fun.type.return_type.null + + state = call once_init_fun + store state, once_state_global + end end end def run_once(flag, func : LLVMTypedFunction) once_fun = main_fun(ONCE) - once_init_fun = main_fun(ONCE_INIT) - - # both of these should be Void* - once_state_type = once_init_fun.type.return_type - once_initializer_type = once_fun.func.params.last.type + once_fun_params = once_fun.func.params + once_initializer_type = once_fun_params.last.type # must be Void* + initializer = pointer_cast(func.func.to_value, once_initializer_type) - once_state_global = @llvm_mod.globals[ONCE_STATE]? || begin - global = @llvm_mod.globals.add(once_state_type, ONCE_STATE) - global.linkage = LLVM::Linkage::External - global + if once_fun_params.size == 2 + args = [flag, initializer] + else + # legacy (kept for backward compatibility): the compiler must pass the + # state returned by __crystal_once_init to __crystal_once as the first + # argument + once_init_fun = main_fun(ONCE_INIT) + once_state_type = once_init_fun.type.return_type # must be Void* + + once_state_global = @llvm_mod.globals[ONCE_STATE]? || begin + global = @llvm_mod.globals.add(once_state_type, ONCE_STATE) + global.linkage = LLVM::Linkage::External + global + end + + state = load(once_state_type, once_state_global) + args = [state, flag, initializer] end - state = load(once_state_type, once_state_global) - initializer = pointer_cast(func.func.to_value, once_initializer_type) - call once_fun, [state, flag, initializer] + call once_fun, args end end diff --git a/src/crystal/once.cr b/src/crystal/once.cr index 56eea2be693a..000d5df688f9 100644 --- a/src/crystal/once.cr +++ b/src/crystal/once.cr @@ -1,54 +1,119 @@ -# This file defines the functions `__crystal_once_init` and `__crystal_once` expected -# by the compiler. `__crystal_once` is called each time a constant or class variable -# has to be initialized and is its responsibility to verify the initializer is executed -# only once. `__crystal_once_init` is executed only once at the beginning of the program -# and the result is passed on each call to `__crystal_once`. - -# This implementation uses an array to store the initialization flag pointers for each value -# to find infinite loops and raise an error. In multithread mode a mutex is used to -# avoid race conditions between threads. - -# :nodoc: -class Crystal::OnceState - @rec = [] of Bool* - - def once(flag : Bool*, initializer : Void*) - unless flag.value - if @rec.includes?(flag) +# This file defines two functions expected by the compiler: +# +# - `__crystal_once_init`: executed only once at the beginning of the program +# and, for the legacy implementation, the result is passed on each call to +# `__crystal_once`. +# +# - `__crystal_once`: called each time a constant or class variable has to be +# initialized and is its responsibility to verify the initializer is executed +# only once and to fail on recursion. + +# In multithread mode a mutex is used to avoid race conditions between threads. +# +# On Win32, `Crystal::System::FileDescriptor#@@reader_thread` spawns a new +# thread even without the `preview_mt` flag, and the thread can also reference +# Crystal constants, leading to race conditions, so we always enable the mutex. + +{% if compare_versions(Crystal::VERSION, "1.15.0-dev") >= 0 %} + # This implementation uses an enum over the initialization flag pointer for + # each value to find infinite loops and raise an error. + + module Crystal + enum OnceState : Int8 + Processing = -1 + Uninitialized = 0 + Initialized = 1 + end + + {% if flag?(:preview_mt) || flag?(:win32) %} + @@once_mutex = uninitialized Mutex + class_property once_mutex : Mutex + {% end %} + + # Using @[NoInline] so LLVM optimizes for the hot path (var already + # initialized). + @[NoInline] + def self.once(flag : OnceState*, initializer : Void*) : Nil + {% if flag?(:preview_mt) || flag?(:win32) %} + Crystal.once_mutex.synchronize { once_exec(flag, initializer) } + {% else %} + once_exec(flag, initializer) + {% end %} + end + + private def self.once_exec(flag : OnceState*, initializer : Void*) : Nil + case flag.value + in .initialized? + return + in .uninitialized? + flag.value = :processing + Proc(Nil).new(initializer, Pointer(Void).null).call + flag.value = :initialized + in .processing? raise "Recursion while initializing class variables and/or constants" end - @rec << flag + end + end - Proc(Nil).new(initializer, Pointer(Void).null).call - flag.value = true + # :nodoc: + fun __crystal_once_init : Nil + {% if flag?(:preview_mt) || flag?(:win32) %} + Crystal.once_mutex = Mutex.new(:reentrant) + {% end %} + end - @rec.pop - end + # :nodoc: + # + # Using `@[AlwaysInline]` allows LLVM to optimize const accesses. Since this + # is a `fun` the function will still appear in the symbol table, though it + # will never be called. + @[AlwaysInline] + fun __crystal_once(flag : Int8*, initializer : Void*) : Nil + flag = flag.as(Crystal::OnceState*) + Crystal.once(flag, initializer) unless flag.value.initialized? end +{% else %} + # This implementation uses a global array to store the initialization flag + # pointers for each value to find infinite loops and raise an error. - # on Win32, `Crystal::System::FileDescriptor#@@reader_thread` spawns a new - # thread even without the `preview_mt` flag, and the thread can also reference - # Crystal constants, leading to race conditions, so we always enable the mutex - # TODO: can this be improved? - {% if flag?(:preview_mt) || flag?(:win32) %} - @mutex = Mutex.new(:reentrant) + # :nodoc: + class Crystal::OnceState + @rec = [] of Bool* def once(flag : Bool*, initializer : Void*) unless flag.value - @mutex.synchronize do - previous_def + if @rec.includes?(flag) + raise "Recursion while initializing class variables and/or constants" end + @rec << flag + + Proc(Nil).new(initializer, Pointer(Void).null).call + flag.value = true + + @rec.pop end end - {% end %} -end - -# :nodoc: -fun __crystal_once_init : Void* - Crystal::OnceState.new.as(Void*) -end - -# :nodoc: -fun __crystal_once(state : Void*, flag : Bool*, initializer : Void*) - state.as(Crystal::OnceState).once(flag, initializer) -end + + {% if flag?(:preview_mt) || flag?(:win32) %} + @mutex = Mutex.new(:reentrant) + + def once(flag : Bool*, initializer : Void*) + unless flag.value + @mutex.synchronize do + previous_def + end + end + end + {% end %} + end + + # :nodoc: + fun __crystal_once_init : Void* + Crystal::OnceState.new.as(Void*) + end + + # :nodoc: + fun __crystal_once(state : Void*, flag : Bool*, initializer : Void*) + state.as(Crystal::OnceState).once(flag, initializer) + end +{% end %} From 55228fd3a713235e99f797bf318108eb437dc2e0 Mon Sep 17 00:00:00 2001 From: Julien Portalier Date: Thu, 9 Jan 2025 12:09:30 +0100 Subject: [PATCH 02/12] LLVM optimization Co-authored-by: David Keller @BlobCodes: I noticed that adding this code prevents LLVM from re-running the once mechanism multiple times for the same variable. Modified to avoid an undefined behavior when the assumption doesn't hold that doubles as a safety net (print error + exit). --- src/crystal/once.cr | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/crystal/once.cr b/src/crystal/once.cr index 000d5df688f9..8b68a78dd3b8 100644 --- a/src/crystal/once.cr +++ b/src/crystal/once.cr @@ -53,6 +53,12 @@ raise "Recursion while initializing class variables and/or constants" end end + + @[NoInline] + def self.once_unreachable : NoReturn + System.print_error "BUG: failed to initialize constant or class variable\n" + LibC._exit(1) + end end # :nodoc: @@ -68,9 +74,14 @@ # is a `fun` the function will still appear in the symbol table, though it # will never be called. @[AlwaysInline] - fun __crystal_once(flag : Int8*, initializer : Void*) : Nil - flag = flag.as(Crystal::OnceState*) - Crystal.once(flag, initializer) unless flag.value.initialized? + fun __crystal_once(flag : Crystal::OnceState*, initializer : Void*) : Nil + return if flag.value.initialized? + + Crystal.once(flag, initializer) + + # make LLVM assume that it musn't call `__crystal_once` anymore for this, + # also doubles as a safety check + Crystal.once_unreachable unless flag.value.initialized? end {% else %} # This implementation uses a global array to store the initialization flag From 9c1da89f51d82a79036cd7b09f415967a4263faa Mon Sep 17 00:00:00 2001 From: Julien Portalier Date: Thu, 9 Jan 2025 14:09:41 +0100 Subject: [PATCH 03/12] LLVM optimization: reduce complexity at call site Co-authored-by: David Keller @BlobCodes: I think it would be better to print the bug message in `Crystal.once` instead of `__crystal_once` to reduce complexity at the callsite. The previous unreachable method can then be used in the inlined `__crystal_once` so LLVM also knows it doesn't have to re-run the method. It's now even safe because `Crystal.once` would panic if it failed; it should already be impossible, but let's err on the safe side. --- src/crystal/once.cr | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/crystal/once.cr b/src/crystal/once.cr index 8b68a78dd3b8..0460a82d0f6b 100644 --- a/src/crystal/once.cr +++ b/src/crystal/once.cr @@ -39,6 +39,13 @@ {% else %} once_exec(flag, initializer) {% end %} + + # safety check, and allows to safely call `#once_unreachable` in + # `__crystal_once` + unless flag.value.initialized? + System.print_error "BUG: failed to initialize constant or class variable\n" + LibC._exit(1) + end end private def self.once_exec(flag : OnceState*, initializer : Void*) : Nil @@ -54,10 +61,10 @@ end end - @[NoInline] + @[AlwaysInline] def self.once_unreachable : NoReturn - System.print_error "BUG: failed to initialize constant or class variable\n" - LibC._exit(1) + x = uninitialized NoReturn + x end end @@ -79,8 +86,9 @@ Crystal.once(flag, initializer) - # make LLVM assume that it musn't call `__crystal_once` anymore for this, - # also doubles as a safety check + # tell LLVM that it can optimize away repeated `__crystal_once` calls for + # this global (e.g. repeated access to constant in a single funtion); + # this is truly unreachable otherwise `Crystal.once` would have panicked Crystal.once_unreachable unless flag.value.initialized? end {% else %} From 6ebfcf4d3da1eeb634323754abb2069c17cf4db9 Mon Sep 17 00:00:00 2001 From: Julien Portalier Date: Thu, 9 Jan 2025 14:15:23 +0100 Subject: [PATCH 04/12] Fix: macro 'class_property' must be defined --- src/crystal/once.cr | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/crystal/once.cr b/src/crystal/once.cr index 0460a82d0f6b..bc190802ac03 100644 --- a/src/crystal/once.cr +++ b/src/crystal/once.cr @@ -27,7 +27,13 @@ {% if flag?(:preview_mt) || flag?(:win32) %} @@once_mutex = uninitialized Mutex - class_property once_mutex : Mutex + + def self.once_mutex : Mutex + @@once_mutex + end + + def self.once_mutex=(@@once_mutex : Mutex) + end {% end %} # Using @[NoInline] so LLVM optimizes for the hot path (var already From 71a1c4d855bc0f9d7b5cfc4ec32e229bff4e0ae8 Mon Sep 17 00:00:00 2001 From: Julien Portalier Date: Fri, 10 Jan 2025 19:36:50 +0100 Subject: [PATCH 05/12] Fix: only enabled for 1.16.0-dev (not 1.15.0-dev) --- src/crystal/once.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/crystal/once.cr b/src/crystal/once.cr index bc190802ac03..10888d477f46 100644 --- a/src/crystal/once.cr +++ b/src/crystal/once.cr @@ -14,7 +14,7 @@ # thread even without the `preview_mt` flag, and the thread can also reference # Crystal constants, leading to race conditions, so we always enable the mutex. -{% if compare_versions(Crystal::VERSION, "1.15.0-dev") >= 0 %} +{% if compare_versions(Crystal::VERSION, "1.16.0-dev") >= 0 %} # This implementation uses an enum over the initialization flag pointer for # each value to find infinite loops and raise an error. From 22960691d2778c61a1c22ca617678e7f5881a419 Mon Sep 17 00:00:00 2001 From: Julien Portalier Date: Mon, 13 Jan 2025 14:41:49 +0100 Subject: [PATCH 06/12] Add LLVM optimization to legacy __crystal_once --- src/crystal/once.cr | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/crystal/once.cr b/src/crystal/once.cr index 10888d477f46..1ebde275c6b7 100644 --- a/src/crystal/once.cr +++ b/src/crystal/once.cr @@ -14,6 +14,14 @@ # thread even without the `preview_mt` flag, and the thread can also reference # Crystal constants, leading to race conditions, so we always enable the mutex. +module Crystal + @[AlwaysInline] + def self.once_unreachable : NoReturn + x = uninitialized NoReturn + x + end +end + {% if compare_versions(Crystal::VERSION, "1.16.0-dev") >= 0 %} # This implementation uses an enum over the initialization flag pointer for # each value to find infinite loops and raise an error. @@ -66,12 +74,6 @@ raise "Recursion while initializing class variables and/or constants" end end - - @[AlwaysInline] - def self.once_unreachable : NoReturn - x = uninitialized NoReturn - x - end end # :nodoc: @@ -105,6 +107,7 @@ class Crystal::OnceState @rec = [] of Bool* + @[NoInline] def once(flag : Bool*, initializer : Void*) unless flag.value if @rec.includes?(flag) @@ -122,6 +125,7 @@ {% if flag?(:preview_mt) || flag?(:win32) %} @mutex = Mutex.new(:reentrant) + @[NoInline] def once(flag : Bool*, initializer : Void*) unless flag.value @mutex.synchronize do @@ -138,7 +142,10 @@ end # :nodoc: + @[AlwaysInline] fun __crystal_once(state : Void*, flag : Bool*, initializer : Void*) + return if flag.value state.as(Crystal::OnceState).once(flag, initializer) + Crystal.once_unreachable unless flag.value end {% end %} From 2c1c20bcb27072954b97e1fb5b37bcaf30036c32 Mon Sep 17 00:00:00 2001 From: Julien Portalier Date: Tue, 14 Jan 2025 10:14:01 +0100 Subject: [PATCH 07/12] Fix: LLVM 14 and below need explicit pointer cast (i8* -> i1*) --- src/compiler/crystal/codegen/once.cr | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/compiler/crystal/codegen/once.cr b/src/compiler/crystal/codegen/once.cr index c87318fa9249..cc2fd2066d00 100644 --- a/src/compiler/crystal/codegen/once.cr +++ b/src/compiler/crystal/codegen/once.cr @@ -44,7 +44,9 @@ class Crystal::CodeGenVisitor end state = load(once_state_type, once_state_global) - args = [state, flag, initializer] + # cast Int8* to Bool* (required for LLVM 14 and below) + bool = bit_cast(flag, @llvm_context.int1.pointer) + args = [state, bool, initializer] end call once_fun, args From 273978b13e2e384bbcc4b4182921c9319647fdbe Mon Sep 17 00:00:00 2001 From: Julien Portalier Date: Tue, 14 Jan 2025 18:58:41 +0100 Subject: [PATCH 08/12] Fix: missing :nodoc: --- src/crystal/once.cr | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/crystal/once.cr b/src/crystal/once.cr index 1ebde275c6b7..534c6e0bb234 100644 --- a/src/crystal/once.cr +++ b/src/crystal/once.cr @@ -15,6 +15,7 @@ # Crystal constants, leading to race conditions, so we always enable the mutex. module Crystal + # :nodoc: @[AlwaysInline] def self.once_unreachable : NoReturn x = uninitialized NoReturn @@ -27,6 +28,7 @@ end # each value to find infinite loops and raise an error. module Crystal + # :nodoc: enum OnceState : Int8 Processing = -1 Uninitialized = 0 @@ -36,14 +38,17 @@ end {% if flag?(:preview_mt) || flag?(:win32) %} @@once_mutex = uninitialized Mutex + # :nodoc: def self.once_mutex : Mutex @@once_mutex end + # :nodoc: def self.once_mutex=(@@once_mutex : Mutex) end {% end %} + # :nodoc: # Using @[NoInline] so LLVM optimizes for the hot path (var already # initialized). @[NoInline] From e7527d3f27ef41d7d6cb26640362afd8397bd15e Mon Sep 17 00:00:00 2001 From: Julien Portalier Date: Thu, 16 Jan 2025 19:46:35 +0100 Subject: [PATCH 09/12] Extract Intrinsics.unreachable --- src/crystal/once.cr | 15 +++------------ src/intrinsics.cr | 17 +++++++++++++++++ 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/crystal/once.cr b/src/crystal/once.cr index 534c6e0bb234..65de635ebe12 100644 --- a/src/crystal/once.cr +++ b/src/crystal/once.cr @@ -14,15 +14,6 @@ # thread even without the `preview_mt` flag, and the thread can also reference # Crystal constants, leading to race conditions, so we always enable the mutex. -module Crystal - # :nodoc: - @[AlwaysInline] - def self.once_unreachable : NoReturn - x = uninitialized NoReturn - x - end -end - {% if compare_versions(Crystal::VERSION, "1.16.0-dev") >= 0 %} # This implementation uses an enum over the initialization flag pointer for # each value to find infinite loops and raise an error. @@ -59,7 +50,7 @@ end once_exec(flag, initializer) {% end %} - # safety check, and allows to safely call `#once_unreachable` in + # safety check, and allows to safely call `Intrinsics.unreachable` in # `__crystal_once` unless flag.value.initialized? System.print_error "BUG: failed to initialize constant or class variable\n" @@ -102,7 +93,7 @@ end # tell LLVM that it can optimize away repeated `__crystal_once` calls for # this global (e.g. repeated access to constant in a single funtion); # this is truly unreachable otherwise `Crystal.once` would have panicked - Crystal.once_unreachable unless flag.value.initialized? + Intrinsics.unreachable unless flag.value.initialized? end {% else %} # This implementation uses a global array to store the initialization flag @@ -151,6 +142,6 @@ end fun __crystal_once(state : Void*, flag : Bool*, initializer : Void*) return if flag.value state.as(Crystal::OnceState).once(flag, initializer) - Crystal.once_unreachable unless flag.value + Intrinsics.unreachable unless flag.value end {% end %} diff --git a/src/intrinsics.cr b/src/intrinsics.cr index dc83ab91c884..d3de22bed11b 100644 --- a/src/intrinsics.cr +++ b/src/intrinsics.cr @@ -354,6 +354,23 @@ module Intrinsics macro va_end(ap) ::LibIntrinsics.va_end({{ap}}) end + + # Should codegen to the following LLVM IR (before being inlined): + # ``` + # define internal void @"*Intrinsics::unreachable:NoReturn"() #12 { + # entry: + # unreachable + #} + # ``` + # + # Can be used like `@llvm.assume(i1 cond)` as `unreachable unless (assumption)`. + # + # WARNING: the behaviour of the program is undefined if the assumption is broken! + @[AlwaysInline] + def self.unreachable : NoReturn + x = uninitialized NoReturn + x + end end macro debugger From e42a57534ba6d87c00e42afb1e58bd9b4d442894 Mon Sep 17 00:00:00 2001 From: Julien Portalier Date: Thu, 16 Jan 2025 19:51:07 +0100 Subject: [PATCH 10/12] Only cast i8* to i1* on LLVM < 15 --- src/compiler/crystal/codegen/once.cr | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/compiler/crystal/codegen/once.cr b/src/compiler/crystal/codegen/once.cr index cc2fd2066d00..ba08f0e7958b 100644 --- a/src/compiler/crystal/codegen/once.cr +++ b/src/compiler/crystal/codegen/once.cr @@ -44,9 +44,10 @@ class Crystal::CodeGenVisitor end state = load(once_state_type, once_state_global) - # cast Int8* to Bool* (required for LLVM 14 and below) - bool = bit_cast(flag, @llvm_context.int1.pointer) - args = [state, bool, initializer] + {% if LibLLVM::IS_LT_150 %} + flag = bit_cast(flag, @llvm_context.int1.pointer) # cast Int8* to Bool* + {% end %} + args = [state, flag, initializer] end call once_fun, args From 4f5092ec073ae833215b05336d222673c0b8b88b Mon Sep 17 00:00:00 2001 From: Julien Portalier Date: Thu, 16 Jan 2025 19:53:29 +0100 Subject: [PATCH 11/12] No need for `Crystal.once_mutex` getter --- src/crystal/once.cr | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/crystal/once.cr b/src/crystal/once.cr index 65de635ebe12..7b6c7e6ed0f2 100644 --- a/src/crystal/once.cr +++ b/src/crystal/once.cr @@ -29,11 +29,6 @@ {% if flag?(:preview_mt) || flag?(:win32) %} @@once_mutex = uninitialized Mutex - # :nodoc: - def self.once_mutex : Mutex - @@once_mutex - end - # :nodoc: def self.once_mutex=(@@once_mutex : Mutex) end @@ -45,7 +40,7 @@ @[NoInline] def self.once(flag : OnceState*, initializer : Void*) : Nil {% if flag?(:preview_mt) || flag?(:win32) %} - Crystal.once_mutex.synchronize { once_exec(flag, initializer) } + @@once_mutex.synchronize { once_exec(flag, initializer) } {% else %} once_exec(flag, initializer) {% end %} From cf58312e38eb5e6698d9f019a84b796be8c531ec Mon Sep 17 00:00:00 2001 From: Julien Portalier Date: Thu, 16 Jan 2025 20:20:32 +0100 Subject: [PATCH 12/12] Fix: crystal tool format --- src/intrinsics.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/intrinsics.cr b/src/intrinsics.cr index d3de22bed11b..3ccc47996e0b 100644 --- a/src/intrinsics.cr +++ b/src/intrinsics.cr @@ -360,7 +360,7 @@ module Intrinsics # define internal void @"*Intrinsics::unreachable:NoReturn"() #12 { # entry: # unreachable - #} + # } # ``` # # Can be used like `@llvm.assume(i1 cond)` as `unreachable unless (assumption)`.