diff --git a/spec/compiler/codegen/class_spec.cr b/spec/compiler/codegen/class_spec.cr index b8504543f1e5..1475d6ab21ee 100644 --- a/spec/compiler/codegen/class_spec.cr +++ b/spec/compiler/codegen/class_spec.cr @@ -447,6 +447,8 @@ describe "Code gen: class" do it "allows using self in class scope" do run(%( + require "prelude" + class Foo def self.foo 1 @@ -713,6 +715,8 @@ describe "Code gen: class" do it "codegens singleton (#718)" do run(%( + require "prelude" + class Singleton @@instance = new diff --git a/spec/compiler/codegen/class_var_spec.cr b/spec/compiler/codegen/class_var_spec.cr index ec35957803dd..0b63d68f6342 100644 --- a/spec/compiler/codegen/class_var_spec.cr +++ b/spec/compiler/codegen/class_var_spec.cr @@ -106,6 +106,8 @@ describe "Codegen: class var" do it "uses var in class var initializer" do run(%( + require "prelude" + class Foo @@var : Int32 @@var = begin @@ -128,6 +130,8 @@ describe "Codegen: class var" do it "reads simple class var before another complex one" do run(%( + require "prelude" + class Foo @@var2 : Int32 @@var2 = @@var &+ 1 @@ -145,6 +149,8 @@ describe "Codegen: class var" do it "initializes class var of union with single type" do run(%( + require "prelude" + class Foo @@var : Int32 | String @@var = 42 @@ -196,6 +202,8 @@ describe "Codegen: class var" do it "initializes dependent constant before class var" do run(%( + require "prelude" + def foo a = 1 b = 2 @@ -233,6 +241,8 @@ describe "Codegen: class var" do it "doesn't use nilable type for initializer" do run(%( + require "prelude" + class Foo @@foo : Int32? @@foo = 42 @@ -250,7 +260,9 @@ describe "Codegen: class var" do end it "codegens class var with begin and vars" do - run(" + run(%( + require "prelude" + class Foo @@foo : Int32 @@foo = begin @@ -265,11 +277,13 @@ describe "Codegen: class var" do end Foo.foo - ").to_i.should eq(3) + )).to_i.should eq(3) end it "codegens class var with type declaration begin and vars" do - run(" + run(%( + require "prelude" + class Foo @@foo : Int32 = begin a = 1 @@ -283,7 +297,7 @@ describe "Codegen: class var" do end Foo.foo - ").to_i.should eq(3) + )).to_i.should eq(3) end it "codegens class var with nilable reference type" do @@ -338,6 +352,8 @@ describe "Codegen: class var" do it "gets pointerof class var complex constant" do run(%( + require "prelude" + z = Foo.foo class Foo diff --git a/spec/compiler/codegen/const_spec.cr b/spec/compiler/codegen/const_spec.cr index 1a6ce9cd2034..8862075049b0 100644 --- a/spec/compiler/codegen/const_spec.cr +++ b/spec/compiler/codegen/const_spec.cr @@ -87,6 +87,8 @@ describe "Codegen: const" do it "declare constants in right order" do run(%( + require "prelude" + CONST1 = 1 + 1 CONST2 = true ? CONST1 : 0 CONST2 @@ -94,7 +96,9 @@ describe "Codegen: const" do end it "uses correct types lookup" do - run(" + run(%( + require "prelude" + module Moo class B def foo @@ -110,11 +114,13 @@ describe "Codegen: const" do end foo - ").to_i.should eq(1) + )).to_i.should eq(1) end it "codegens variable assignment in const" do - run(" + run(%( + require "prelude" + class Foo def initialize(@x : Int32) end @@ -134,11 +140,13 @@ describe "Codegen: const" do end foo - ").to_i.should eq(1) + )).to_i.should eq(1) end it "declaring var" do - run(" + run(%( + require "prelude" + BAR = begin a = 1 while 1 == 2 @@ -153,7 +161,7 @@ describe "Codegen: const" do end Foo.new.compile - ").to_i.should eq(1) + )).to_i.should eq(1) end it "initialize const that might raise an exception" do @@ -171,7 +179,9 @@ describe "Codegen: const" do end it "allows implicit self in constant, called from another class (bug)" do - run(" + run(%( + require "prelude" + module Foo def self.foo 1 @@ -187,11 +197,13 @@ describe "Codegen: const" do end Bar.new.bar - ").to_i.should eq(1) + )).to_i.should eq(1) end it "codegens two consts with same variable name" do - run(" + run(%( + require "prelude" + CONST1 = begin a = 1 end @@ -201,11 +213,13 @@ describe "Codegen: const" do end (CONST1 + CONST2).to_i - ").to_i.should eq(3) + )).to_i.should eq(3) end it "works with variable declared inside if" do run(%( + require "prelude" + FOO = begin if 1 == 2 x = 3 @@ -220,6 +234,8 @@ describe "Codegen: const" do it "codegens constant that refers to another constant that is a struct" do run(%( + require "prelude" + struct Foo X = Foo.new(1) Y = X @@ -281,6 +297,8 @@ describe "Codegen: const" do it "uses const before declaring it (hoisting)" do run(%( + require "prelude" + x = CONST CONST = foo @@ -342,6 +360,8 @@ describe "Codegen: const" do it "gets pointerof constant" do run(%( + require "prelude" + z = pointerof(FOO).value FOO = 10 z @@ -350,6 +370,8 @@ describe "Codegen: const" do it "gets pointerof complex constant" do run(%( + require "prelude" + z = pointerof(FOO).value FOO = begin a = 10 @@ -446,4 +468,42 @@ describe "Codegen: const" do mod.to_s.should_not contain("CONST") end + + it "synchronize initialization of contants" do + run(%( + require "prelude" + + def foo + v1, v2 = 1, 1 + rand(100000..10000000).times do + v1, v2 = v2, v1 + v2 + end + v2 + end + + ch = Channel(Int32).new + + 10.times do + spawn do + ch.send X + end + end + + X = foo + + def test(ch) + expected = X + + 10.times do + if ch.receive != expected + return false + end + end + + true + end + + test(ch) + )).to_b.should be_true + end end diff --git a/spec/compiler/codegen/debug_spec.cr b/spec/compiler/codegen/debug_spec.cr index a8b883d15409..09c20b5da858 100644 --- a/spec/compiler/codegen/debug_spec.cr +++ b/spec/compiler/codegen/debug_spec.cr @@ -107,6 +107,8 @@ describe "Code gen: debug" do it "has correct debug location after constant initialization in call with block (#4719)" do codegen(%( + require "prelude" + fun __crystal_malloc_atomic(size : UInt32) : Void* x = uninitialized Void* x diff --git a/spec/compiler/codegen/hooks_spec.cr b/spec/compiler/codegen/hooks_spec.cr index eb745bb5d320..c2dfe6a4e568 100644 --- a/spec/compiler/codegen/hooks_spec.cr +++ b/spec/compiler/codegen/hooks_spec.cr @@ -115,7 +115,9 @@ describe "Code gen: hooks" do end it "does inherited macro before class body" do - run(" + run(%( + require "prelude" + class Global @@x = 123 @@ -142,7 +144,7 @@ describe "Code gen: hooks" do end Bar.y - ").to_i.should eq(123) + )).to_i.should eq(123) end it "does finished" do diff --git a/spec/compiler/codegen/macro_spec.cr b/spec/compiler/codegen/macro_spec.cr index c6b22b1af023..85f92ef918e1 100644 --- a/spec/compiler/codegen/macro_spec.cr +++ b/spec/compiler/codegen/macro_spec.cr @@ -663,6 +663,8 @@ describe "Code gen: macro" do it "transforms hooks (bug)" do codegen(%( + require "prelude" + module GC def self.add_finalizer(object : T) object.responds_to?(:finalize) @@ -800,6 +802,8 @@ describe "Code gen: macro" do it "copies base macro def to sub-subtype even after it was copied to a subtype (#448)" do run(%( + require "prelude" + class Object def class_name : String {{@type.name.stringify}} diff --git a/spec/compiler/codegen/pointer_spec.cr b/spec/compiler/codegen/pointer_spec.cr index c95333411e36..1f6a7b75de8c 100644 --- a/spec/compiler/codegen/pointer_spec.cr +++ b/spec/compiler/codegen/pointer_spec.cr @@ -250,10 +250,11 @@ describe "Code gen: pointer" do end it "gets pointer to constant" do - run(" + run(%( + require "prelude" FOO = 1 pointerof(FOO).value - ").to_i.should eq(1) + )).to_i.should eq(1) end it "passes pointer of pointer to method" do @@ -324,6 +325,8 @@ describe "Code gen: pointer" do it "does pointerof class variable with class" do run(%( + require "prelude" + class Bar def initialize(@x : Int32) end diff --git a/spec/compiler/codegen/proc_spec.cr b/spec/compiler/codegen/proc_spec.cr index d78e5c06ea1f..98bb279a2513 100644 --- a/spec/compiler/codegen/proc_spec.cr +++ b/spec/compiler/codegen/proc_spec.cr @@ -595,6 +595,8 @@ describe "Code gen: proc" do it "codegens proc to implicit self in constant (#647)" do run(%( + require "prelude" + module Foo def self.blah 1 diff --git a/spec/compiler/codegen/struct_spec.cr b/spec/compiler/codegen/struct_spec.cr index 02a286169700..c5da68588b74 100644 --- a/spec/compiler/codegen/struct_spec.cr +++ b/spec/compiler/codegen/struct_spec.cr @@ -171,7 +171,9 @@ describe "Code gen: struct" do end it "declares const struct" do - run(" + run(%( + require "prelude" + struct Foo def initialize(@x : Int32) end @@ -184,11 +186,13 @@ describe "Code gen: struct" do FOO = Foo.new(1) FOO.x - ").to_i.should eq(1) + )).to_i.should eq(1) end it "uses struct in if" do - run(" + run(%( + require "prelude" + struct Foo def initialize(@x : Int32) end @@ -206,7 +210,7 @@ describe "Code gen: struct" do foo = FOO end foo.x - ").to_i.should eq(1) + )).to_i.should eq(1) end it "uses nilable struct" do diff --git a/src/compiler/crystal/codegen/class_var.cr b/src/compiler/crystal/codegen/class_var.cr index e01adfeeafda..cbdffd5a8b48 100644 --- a/src/compiler/crystal/codegen/class_var.cr +++ b/src/compiler/crystal/codegen/class_var.cr @@ -82,6 +82,7 @@ class Crystal::CodeGenVisitor def initialize_class_var(class_var : MetaTypeVar, initializer : ClassVarInitializer) init_func = create_initialize_class_var_function(class_var, initializer) + init_func = check_main_fun(init_func.name, init_func) if init_func # For unsafe class var we just initialize them without # using a flag to know if they were initialized @@ -89,7 +90,6 @@ class Crystal::CodeGenVisitor global = declare_class_var(class_var) global = ensure_class_var_in_this_module(global, class_var) if init_func - check_main_fun init_func.name, init_func call init_func end return global @@ -97,20 +97,12 @@ class Crystal::CodeGenVisitor global, initialized_flag = declare_class_var_and_initialized_flag_in_this_module(class_var) - initialized_block, not_initialized_block = new_blocks "initialized", "not_initialized" - - initialized = load(initialized_flag) - cond initialized, initialized_block, not_initialized_block - - position_at_end not_initialized_block - store int1(1), initialized_flag - - init_func = check_main_fun init_func.name, init_func - call init_func - - br initialized_block + lazy_initialize_class_var(initializer.node, init_func, global, initialized_flag) + end - position_at_end initialized_block + def lazy_initialize_class_var(node, init_func, global, initialized_flag) + set_current_debug_location node if @debug.line_numbers? + run_once(initialized_flag, init_func) global end @@ -317,22 +309,8 @@ class Crystal::CodeGenVisitor in_main do define_main_function(fun_name, ([] of LLVM::Type), llvm_type(class_var.type).pointer) do |func| - initialized_block, not_initialized_block = new_blocks "initialized", "not_initialized" - - initialized = load(initialized_flag) - cond initialized, initialized_block, not_initialized_block - - position_at_end not_initialized_block - store int1(1), initialized_flag - - check_main_fun init_func.name, init_func - call init_func - - br initialized_block - - position_at_end initialized_block - - ret global + init_func = check_main_fun init_func.name, init_func + ret lazy_initialize_class_var(initializer.node, init_func, global, initialized_flag) end end end diff --git a/src/compiler/crystal/codegen/codegen.cr b/src/compiler/crystal/codegen/codegen.cr index 7750c2fba9ec..d27a2bdc7004 100644 --- a/src/compiler/crystal/codegen/codegen.cr +++ b/src/compiler/crystal/codegen/codegen.cr @@ -13,6 +13,8 @@ module Crystal MALLOC_ATOMIC_NAME = "__crystal_malloc_atomic64" REALLOC_NAME = "__crystal_realloc64" GET_EXCEPTION_NAME = "__crystal_get_exception" + ONCE_INIT = "__crystal_once_init" + ONCE = "__crystal_once" class Program def run(code, filename = nil, debug = Debug::Default) @@ -243,12 +245,13 @@ module Crystal initialize_argv_and_argc - initialize_simple_constants - - if @debug.line_numbers? && (filename = @program.filename) - set_current_debug_location Location.new(filename, 1, 1) + if @debug.line_numbers? + set_current_debug_location Location.new(@program.filename || "(no name)", 1, 1) end + once_init + initialize_simple_constants + alloca_vars @program.vars, @program emit_vars_debug_info(@program.vars) if @debug.variables? @@ -301,7 +304,9 @@ module Crystal def visit(node : FunDef) case node.name - when MALLOC_NAME, MALLOC_ATOMIC_NAME, REALLOC_NAME, RAISE_NAME, @codegen.personality_name, GET_EXCEPTION_NAME, RAISE_OVERFLOW_NAME + when MALLOC_NAME, MALLOC_ATOMIC_NAME, REALLOC_NAME, RAISE_NAME, + @codegen.personality_name, GET_EXCEPTION_NAME, RAISE_OVERFLOW_NAME, + ONCE_INIT, ONCE @codegen.accept node end false diff --git a/src/compiler/crystal/codegen/const.cr b/src/compiler/crystal/codegen/const.cr index 54e0b509169d..e3ba1e83420c 100644 --- a/src/compiler/crystal/codegen/const.cr +++ b/src/compiler/crystal/codegen/const.cr @@ -29,6 +29,8 @@ require "./codegen" # and can be done in any order (they have no side effects). class Crystal::CodeGenVisitor + @const_mutex : LLVM::Value? + # The special constants ARGC_UNSAFE and ARGV_UNSAFE need to be initialized # as soon as the program starts, because we have access to argc and argv # in the main function @@ -96,27 +98,16 @@ class Crystal::CodeGenVisitor def initialize_const(const) # Maybe the constant was simple and doesn't need a real initialization - return if const.initializer global, initialized_flag = declare_const_and_initialized_flag(const) - - initialized_block, not_initialized_block = new_blocks "initialized", "not_initialized" - - initialized = load(initialized_flag) - cond initialized, initialized_block, not_initialized_block - - position_at_end not_initialized_block - store int1(1), initialized_flag + return global if const.initializer init_function_name = "~#{const.initialized_llvm_name}" func = @main_mod.functions[init_function_name]? || create_initialize_const_function(init_function_name, const) func = check_main_fun init_function_name, func - call func - - br initialized_block - - position_at_end initialized_block + set_current_debug_location const.locations.try &.first? if @debug.line_numbers? + run_once(initialized_flag, func) global end @@ -202,26 +193,9 @@ class Crystal::CodeGenVisitor end def create_read_const_function(fun_name, const) - global, initialized_flag = declare_const_and_initialized_flag(const) - in_main do define_main_function(fun_name, ([] of LLVM::Type), llvm_type(const.value.type).pointer) do |func| - initialized_block, not_initialized_block = new_blocks "initialized", "not_initialized" - - initialized = load(initialized_flag) - cond initialized, initialized_block, not_initialized_block - - position_at_end not_initialized_block - store int1(1), initialized_flag - - init_function_name = "~#{const.initialized_llvm_name}" - func = @main_mod.functions[init_function_name]? || create_initialize_const_function(init_function_name, const) - call func - - br initialized_block - - position_at_end initialized_block - + global = initialize_const(const) ret global end end diff --git a/src/compiler/crystal/codegen/once.cr b/src/compiler/crystal/codegen/once.cr new file mode 100644 index 000000000000..6c833b3e7fcc --- /dev/null +++ b/src/compiler/crystal/codegen/once.cr @@ -0,0 +1,38 @@ +require "./codegen" + +class Crystal::CodeGenVisitor + ONCE_STATE = "~ONCE_STATE" + @have_once_state = false + + def once_init + once_init_fun = @main_mod.functions[ONCE_INIT]? + if once_init_fun + @have_once_state = true + once_init_fun = check_main_fun ONCE_INIT, once_init_fun + + once_state_global = @main_mod.globals.add(once_init_fun.return_type, ONCE_STATE) + once_state_global.linkage = LLVM::Linkage::Internal if @single_module + once_state_global.initializer = once_init_fun.return_type.null + + state = call once_init_fun + store state, once_state_global + end + end + + def run_once(flag, func) + once_fun = main_fun(ONCE) + + once_state_global = @llvm_mod.globals[ONCE_STATE]? || begin + once_init_fun = main_fun(ONCE_INIT) + global = @llvm_mod.globals.add(once_init_fun.return_type, ONCE_STATE) + global.linkage = LLVM::Linkage::External + global + end + + call main_fun(ONCE), [ + load(once_state_global), + flag, + bit_cast(func.to_value, once_fun.params.last.type), + ] + end +end diff --git a/src/compiler/crystal/syntax/parser.cr b/src/compiler/crystal/syntax/parser.cr index 8d7dfc063880..9be592c62cdb 100644 --- a/src/compiler/crystal/syntax/parser.cr +++ b/src/compiler/crystal/syntax/parser.cr @@ -5690,7 +5690,8 @@ module Crystal when :"{{" members << parse_percent_macro_expression when :"{%" - members << parse_percent_macro_control + location = @token.location + members << parse_percent_macro_control.at(location) when :"@[" members << parse_annotation when :";", :NEWLINE diff --git a/src/crystal/once.cr b/src/crystal/once.cr new file mode 100644 index 000000000000..db2c1aaeecde --- /dev/null +++ b/src/crystal/once.cr @@ -0,0 +1,27 @@ +{% if flag?(:preview_mt) %} + fun __crystal_once_init : Void* + Mutex.new.as(Void*) + end + + fun __crystal_once(m : Void*, f : Bool*, init : Void*) + unless f.value + m.as(Mutex).synchronize do + unless f.value + Proc(Nil).new(init, Pointer(Void).null).call + f.value = true + end + end + end + end +{% else %} + fun __crystal_once_init : Void* + Pointer(Void).null + end + + fun __crystal_once(m : Void*, f : Bool*, init : Void*) + unless f.value + Proc(Nil).new(init, Pointer(Void).null).call + f.value = true + end + end +{% end %} diff --git a/src/fiber.cr b/src/fiber.cr index df247b72de43..3eff5957cc56 100644 --- a/src/fiber.cr +++ b/src/fiber.cr @@ -11,7 +11,7 @@ fun _fiber_get_stack_top : Void* end class Fiber - @@fibers = Thread::LinkedList(Fiber).new + @@fibers : Thread::LinkedList(Fiber)? # :nodoc: class_getter stack_pool = StackPool.new @@ -30,14 +30,18 @@ class Fiber # :nodoc: property previous : Fiber? + protected def self.fibers + @@fibers ||= Thread::LinkedList(Fiber).new + end + # :nodoc: def self.inactive(fiber : Fiber) - @@fibers.delete(fiber) + fibers.delete(fiber) end # :nodoc: def self.unsafe_each - @@fibers.unsafe_each { |fiber| yield fiber } + fibers.unsafe_each { |fiber| yield fiber } end def initialize(@name : String? = nil, &@proc : ->) @@ -58,7 +62,7 @@ class Fiber makecontext(stack_ptr, fiber_main) - @@fibers.push(self) + Fiber.fibers.push(self) end # :nodoc: @@ -71,7 +75,7 @@ class Fiber @stack_bottom = GC.current_thread_stack_bottom @name = "main" @current_thread.set(thread) - @@fibers.push(self) + Fiber.fibers.push(self) end # :nodoc: @@ -90,7 +94,7 @@ class Fiber Fiber.stack_pool.release(@stack) # Remove the current fiber from the linked list - @@fibers.delete(self) + Fiber.fibers.delete(self) # Delete the resume event if it was used by `yield` or `sleep` @resume_event.try &.free diff --git a/src/llvm/value_methods.cr b/src/llvm/value_methods.cr index 1e8d2f9664b3..efba6024b4ba 100644 --- a/src/llvm/value_methods.cr +++ b/src/llvm/value_methods.cr @@ -96,7 +96,7 @@ module LLVM::ValueMethods end def to_value - LLVM::Value.new unwrap + LLVM::Value.new @unwrap end def dump diff --git a/src/prelude.cr b/src/prelude.cr index 777b85adb520..6b74c77d4eb2 100644 --- a/src/prelude.cr +++ b/src/prelude.cr @@ -14,6 +14,7 @@ private macro no_win(stmt) end # This list requires ordered statements +require "crystal/once" require "lib_c" require "macros" require "object" diff --git a/src/thread.cr b/src/thread.cr index a88c314719ad..33c4c469b95e 100644 --- a/src/thread.cr +++ b/src/thread.cr @@ -8,7 +8,7 @@ class Thread # Use spawn and channels instead. # all thread objects, so the GC can see them (it doesn't scan thread locals) - @@threads = Thread::LinkedList(Thread).new + @@threads : Thread::LinkedList(Thread)? @th : LibC::PthreadT @exception : Exception? @@ -25,8 +25,12 @@ class Thread property load = 0 {% end %} + protected def self.threads + @@threads ||= Thread::LinkedList(Thread).new + end + def self.unsafe_each - @@threads.unsafe_each { |thread| yield thread } + threads.unsafe_each { |thread| yield thread } end # Starts a new system thread. @@ -39,7 +43,7 @@ class Thread }, self.as(Void*)) if ret == 0 - @@threads.push(self) + Thread.threads.push(self) else raise Errno.new("pthread_create", ret) end @@ -52,7 +56,7 @@ class Thread @th = LibC.pthread_self @main_fiber = Fiber.new(stack_address, self) - @@threads.push(self) + Thread.threads.push(self) end private def detach @@ -102,7 +106,9 @@ class Thread # Returns the Thread object associated to the running system thread. def self.current : Thread - @@current || raise "BUG: Thread.current returned NULL" + # Thread#start sets @@current as soon it starts. Thus we know + # that if @@current is not set then we are in the main thread + @@current ||= new end # Associates the Thread object to the running system thread. @@ -110,12 +116,6 @@ class Thread end {% end %} - # Create the thread object for the current thread (aka the main thread of the - # process). - # - # TODO: consider moving to `kernel.cr` or `crystal/main.cr` - self.current = new - def self.yield ret = LibC.sched_yield raise Errno.new("sched_yield") unless ret == 0 @@ -140,7 +140,7 @@ class Thread rescue ex @exception = ex ensure - @@threads.delete(self) + Thread.threads.delete(self) Fiber.inactive(fiber) detach { GC.pthread_detach(@th) } end diff --git a/src/thread/linked_list.cr b/src/thread/linked_list.cr index f64d9df95bba..2e66345cf2d5 100644 --- a/src/thread/linked_list.cr +++ b/src/thread/linked_list.cr @@ -6,7 +6,7 @@ class Thread # # Thread-safe doubly linked list of `T` objects that must implement # `#previous : T?` and `#next : T?` methods. - struct LinkedList(T) + class LinkedList(T) @mutex = Thread::Mutex.new @head : T? @tail : T?