diff --git a/spec/std/big/big_int_spec.cr b/spec/std/big/big_int_spec.cr index 07575892bc08..8a86939efbac 100644 --- a/spec/std/big/big_int_spec.cr +++ b/spec/std/big/big_int_spec.cr @@ -198,7 +198,7 @@ describe "BigInt" do it "raises if factorial of 2^64" do expect_raises ArgumentError do - (LibGMP::ULong::MAX.to_big_i + 1).factorial + (LibGMP::UI::MAX.to_big_i + 1).factorial end end diff --git a/spec/std/int_spec.cr b/spec/std/int_spec.cr index 72a83bd64061..88ef12553c38 100644 --- a/spec/std/int_spec.cr +++ b/spec/std/int_spec.cr @@ -146,6 +146,102 @@ describe "Int" do end end + describe "#to_signed" do + {% for n in [8, 16, 32, 64, 128] %} + it "does for Int{{n}}" do + x = Int{{n}}.new(123).to_signed + x.should be_a(Int{{n}}) + x.should eq(123) + + Int{{n}}.new(-123).to_signed.should eq(-123) + Int{{n}}::MIN.to_signed.should eq(Int{{n}}::MIN) + Int{{n}}::MAX.to_signed.should eq(Int{{n}}::MAX) + end + + it "does for UInt{{n}}" do + x = UInt{{n}}.new(123).to_signed + x.should be_a(Int{{n}}) + x.should eq(123) + + UInt{{n}}::MIN.to_signed.should eq(0) + expect_raises(OverflowError) { UInt{{n}}::MAX.to_signed } + expect_raises(OverflowError) { (UInt{{n}}.new(Int{{n}}::MAX) + 1).to_signed } + end + {% end %} + end + + describe "#to_signed!" do + {% for n in [8, 16, 32, 64, 128] %} + it "does for Int{{n}}" do + x = Int{{n}}.new(123).to_signed! + x.should be_a(Int{{n}}) + x.should eq(123) + + Int{{n}}.new(-123).to_signed!.should eq(-123) + Int{{n}}::MIN.to_signed!.should eq(Int{{n}}::MIN) + Int{{n}}::MAX.to_signed!.should eq(Int{{n}}::MAX) + end + + it "does for UInt{{n}}" do + x = UInt{{n}}.new(123).to_signed! + x.should be_a(Int{{n}}) + x.should eq(123) + + UInt{{n}}::MIN.to_signed!.should eq(0) + UInt{{n}}::MAX.to_signed!.should eq(-1) + (UInt{{n}}::MAX - 122).to_signed!.should eq(-123) + (UInt{{n}}.new(Int{{n}}::MAX) + 1).to_signed!.should eq(Int{{n}}::MIN) + end + {% end %} + end + + describe "#to_unsigned" do + {% for n in [8, 16, 32, 64, 128] %} + it "does for Int{{n}}" do + x = Int{{n}}.new(123).to_unsigned + x.should be_a(UInt{{n}}) + x.should eq(123) + + Int{{n}}.zero.to_unsigned.should eq(UInt{{n}}::MIN) + Int{{n}}::MAX.to_unsigned.should eq(UInt{{n}}.new(Int{{n}}::MAX)) + expect_raises(OverflowError) { Int{{n}}::MIN.to_unsigned } + end + + it "does for UInt{{n}}" do + x = UInt{{n}}.new(123).to_unsigned + x.should be_a(UInt{{n}}) + x.should eq(123) + + UInt{{n}}::MIN.to_unsigned.should eq(UInt{{n}}::MIN) + UInt{{n}}::MAX.to_unsigned.should eq(UInt{{n}}::MAX) + end + {% end %} + end + + describe "#to_unsigned!" do + {% for n in [8, 16, 32, 64, 128] %} + it "does for Int{{n}}" do + x = Int{{n}}.new(123).to_unsigned! + x.should be_a(UInt{{n}}) + x.should eq(123) + + Int{{n}}.new(-123).to_unsigned!.should eq(UInt{{n}}::MAX - 122) + Int{{n}}::MIN.to_unsigned!.should eq(UInt{{n}}::MAX // 2 + 1) + Int{{n}}::MAX.to_unsigned!.should eq(UInt{{n}}::MAX // 2) + Int{{n}}.new(-1).to_unsigned!.should eq(UInt{{n}}::MAX) + end + + it "does for UInt{{n}}" do + x = UInt{{n}}.new(123).to_unsigned! + x.should be_a(UInt{{n}}) + x.should eq(123) + + UInt{{n}}::MIN.to_unsigned!.should eq(UInt{{n}}::MIN) + UInt{{n}}::MAX.to_unsigned!.should eq(UInt{{n}}::MAX) + end + {% end %} + end + describe "#abs_unsigned" do {% for int in Int::Signed.union_types %} it "does for {{ int }}" do diff --git a/src/array.cr b/src/array.cr index df31a2699251..5ce9f2e6148b 100644 --- a/src/array.cr +++ b/src/array.cr @@ -127,12 +127,21 @@ class Array(T) # # ``` # Array.new(3, 'a') # => ['a', 'a', 'a'] + # ``` + # + # WARNING: The initial value is filled into the array as-is. It gets neither + # duplicated nor cloned. For types with reference semantics this means every + # item will point to the *same* object. # + # ``` # ary = Array.new(3, [1]) # ary # => [[1], [1], [1]] # ary[0][0] = 2 # ary # => [[2], [2], [2]] # ``` + # + # * `.new(Int, & : Int32 -> T)` is an alternative that allows using a + # different initial value for each position. def initialize(size : Int, value : T) if size < 0 raise ArgumentError.new("Negative array size: #{size}") diff --git a/src/big/big_float.cr b/src/big/big_float.cr index 5464d948931b..7377afda34b3 100644 --- a/src/big/big_float.cr +++ b/src/big/big_float.cr @@ -40,29 +40,16 @@ struct BigFloat < Float LibGMP.mpf_set(self, num) end - def initialize(num : Int8 | Int16 | Int32) - LibGMP.mpf_init_set_si(out @mpf, num) - end - - def initialize(num : UInt8 | UInt16 | UInt32) - LibGMP.mpf_init_set_ui(out @mpf, num) - end - - def initialize(num : Int64) - if LibGMP::Long == Int64 - LibGMP.mpf_init_set_si(out @mpf, num) - else - LibGMP.mpf_init(out @mpf) - LibGMP.mpf_set_z(self, num.to_big_i) - end - end - - def initialize(num : UInt64) - if LibGMP::ULong == UInt64 - LibGMP.mpf_init_set_ui(out @mpf, num) - else - LibGMP.mpf_init(out @mpf) - LibGMP.mpf_set_z(self, num.to_big_i) + def initialize(num : Int) + Int.primitive_si_ui_check(num) do |si, ui, big_i| + { + si: LibGMP.mpf_init_set_si(out @mpf, {{ si }}), + ui: LibGMP.mpf_init_set_ui(out @mpf, {{ ui }}), + big_i: begin + LibGMP.mpf_init(out @mpf) + LibGMP.mpf_set_z(self, {{ big_i }}) + end, + } end end @@ -113,16 +100,20 @@ struct BigFloat < Float LibGMP.mpf_cmp_d(self, other) unless other.nan? end - def <=>(other : Number) - if other.is_a?(Int8 | Int16 | Int32) || (LibGMP::Long == Int64 && other.is_a?(Int64)) - LibGMP.mpf_cmp_si(self, other) - elsif other.is_a?(UInt8 | UInt16 | UInt32) || (LibGMP::ULong == UInt64 && other.is_a?(UInt64)) - LibGMP.mpf_cmp_ui(self, other) - else - LibGMP.mpf_cmp(self, other.to_big_f) + def <=>(other : Int) + Int.primitive_si_ui_check(other) do |si, ui, big_i| + { + si: LibGMP.mpf_cmp_si(self, {{ si }}), + ui: LibGMP.mpf_cmp_ui(self, {{ ui }}), + big_i: self <=> {{ big_i }}, + } end end + def <=>(other : Number) + LibGMP.mpf_cmp(self, other.to_big_f) + end + def - : BigFloat BigFloat.new { |mpf| LibGMP.mpf_neg(mpf, self) } end diff --git a/src/big/big_int.cr b/src/big/big_int.cr index 9cc3f15e5890..decf7a0828ff 100644 --- a/src/big/big_int.cr +++ b/src/big/big_int.cr @@ -48,29 +48,33 @@ struct BigInt < Int # Creates a `BigInt` from the given *num*. def self.new(num : Int::Primitive) - if LibGMP::SI::MIN <= num <= LibGMP::UI::MAX - if num <= LibGMP::SI::MAX - LibGMP.init_set_si(out mpz1, LibGMP::SI.new!(num)) - new(mpz1) - else - LibGMP.init_set_ui(out mpz2, LibGMP::UI.new!(num)) - new(mpz2) - end - else - negative = num < 0 - num = num.abs_unsigned - capacity = (num.bit_length - 1) // (sizeof(LibGMP::MpLimb) * 8) + 1 - - # This assumes GMP wasn't built with its experimental nails support: - # https://gmplib.org/manual/Low_002dlevel-Functions - unsafe_build(capacity) do |limbs| - appender = limbs.to_unsafe.appender - limbs.size.times do - appender << LibGMP::MpLimb.new!(num) - num = num.unsafe_shr(sizeof(LibGMP::MpLimb) * 8) - end - {capacity, negative} - end + Int.primitive_si_ui_check(num) do |si, ui, _| + { + si: begin + LibGMP.init_set_si(out mpz1, {{ si }}) + new(mpz1) + end, + ui: begin + LibGMP.init_set_ui(out mpz2, {{ ui }}) + new(mpz2) + end, + big_i: begin + negative = num < 0 + num = num.abs_unsigned + capacity = (num.bit_length - 1) // (sizeof(LibGMP::MpLimb) * 8) + 1 + + # This assumes GMP wasn't built with its experimental nails support: + # https://gmplib.org/manual/Low_002dlevel-Functions + unsafe_build(capacity) do |limbs| + appender = limbs.to_unsafe.appender + limbs.size.times do + appender << LibGMP::MpLimb.new!(num) + num = num.unsafe_shr(sizeof(LibGMP::MpLimb) * 8) + end + {capacity, negative} + end + end, + } end end @@ -141,19 +145,13 @@ struct BigInt < Int LibGMP.cmp(mpz, other) end - def <=>(other : Int::Signed) - if LibC::Long::MIN <= other <= LibC::Long::MAX - LibGMP.cmp_si(mpz, other) - else - self <=> BigInt.new(other) - end - end - - def <=>(other : Int::Unsigned) - if other <= LibC::ULong::MAX - LibGMP.cmp_ui(mpz, other) - else - self <=> BigInt.new(other) + def <=>(other : Int) + Int.primitive_si_ui_check(other) do |si, ui, big_i| + { + si: LibGMP.cmp_si(self, {{ si }}), + ui: LibGMP.cmp_ui(self, {{ ui }}), + big_i: self <=> {{ big_i }}, + } end end @@ -166,12 +164,12 @@ struct BigInt < Int end def +(other : Int) : BigInt - if other < 0 - self - other.abs - elsif other <= LibGMP::ULong::MAX - BigInt.new { |mpz| LibGMP.add_ui(mpz, self, other) } - else - self + other.to_big_i + Int.primitive_ui_check(other) do |ui, neg_ui, big_i| + { + ui: BigInt.new { |mpz| LibGMP.add_ui(mpz, self, {{ ui }}) }, + neg_ui: BigInt.new { |mpz| LibGMP.sub_ui(mpz, self, {{ neg_ui }}) }, + big_i: self + {{ big_i }}, + } end end @@ -184,12 +182,12 @@ struct BigInt < Int end def -(other : Int) : BigInt - if other < 0 - self + other.abs - elsif other <= LibGMP::ULong::MAX - BigInt.new { |mpz| LibGMP.sub_ui(mpz, self, other) } - else - self - other.to_big_i + Int.primitive_ui_check(other) do |ui, neg_ui, big_i| + { + ui: BigInt.new { |mpz| LibGMP.sub_ui(mpz, self, {{ ui }}) }, + neg_ui: BigInt.new { |mpz| LibGMP.add_ui(mpz, self, {{ neg_ui }}) }, + big_i: self - {{ big_i }}, + } end end @@ -208,26 +206,24 @@ struct BigInt < Int def factorial : BigInt if self < 0 raise ArgumentError.new("Factorial not defined for negative values") - elsif self > LibGMP::ULong::MAX - raise ArgumentError.new("Factorial not supported for numbers bigger than 2^64") + elsif self > LibGMP::UI::MAX + raise ArgumentError.new("Factorial not supported for numbers bigger than #{LibGMP::UI::MAX}") end - BigInt.new { |mpz| LibGMP.fac_ui(mpz, self) } + BigInt.new { |mpz| LibGMP.fac_ui(mpz, LibGMP::UI.new!(self)) } end def *(other : BigInt) : BigInt BigInt.new { |mpz| LibGMP.mul(mpz, self, other) } end - def *(other : LibGMP::IntPrimitiveSigned) : BigInt - BigInt.new { |mpz| LibGMP.mul_si(mpz, self, other) } - end - - def *(other : LibGMP::IntPrimitiveUnsigned) : BigInt - BigInt.new { |mpz| LibGMP.mul_ui(mpz, self, other) } - end - def *(other : Int) : BigInt - self * other.to_big_i + Int.primitive_si_ui_check(other) do |si, ui, big_i| + { + si: BigInt.new { |mpz| LibGMP.mul_si(mpz, self, {{ si }}) }, + ui: BigInt.new { |mpz| LibGMP.mul_ui(mpz, self, {{ ui }}) }, + big_i: self * {{ big_i }}, + } + end end def &*(other) : BigInt @@ -238,19 +234,10 @@ struct BigInt < Int Number.expand_div [BigDecimal], BigDecimal Number.expand_div [BigRational], BigRational - def //(other : Int::Unsigned) : BigInt - check_division_by_zero other - unsafe_floored_div(other) - end - def //(other : Int) : BigInt check_division_by_zero other - if other < 0 - (-self).unsafe_floored_div(-other) - else - unsafe_floored_div(other) - end + unsafe_floored_div(other) end def tdiv(other : Int) : BigInt @@ -264,12 +251,12 @@ struct BigInt < Int end def unsafe_floored_div(other : Int) : BigInt - if LibGMP::ULong == UInt32 && (other < Int32::MIN || other > UInt32::MAX) - unsafe_floored_div(other.to_big_i) - elsif other < 0 - -BigInt.new { |mpz| LibGMP.fdiv_q_ui(mpz, self, other.abs) } - else - BigInt.new { |mpz| LibGMP.fdiv_q_ui(mpz, self, other) } + Int.primitive_ui_check(other) do |ui, neg_ui, big_i| + { + ui: BigInt.new { |mpz| LibGMP.fdiv_q_ui(mpz, self, {{ ui }}) }, + neg_ui: BigInt.new { |mpz| LibGMP.fdiv_q_ui(mpz, -self, {{ neg_ui }}) }, + big_i: unsafe_floored_div({{ big_i }}), + } end end @@ -278,23 +265,19 @@ struct BigInt < Int end def unsafe_truncated_div(other : Int) : BigInt - if LibGMP::ULong == UInt32 && (other < Int32::MIN || other > UInt32::MAX) - unsafe_truncated_div(other.to_big_i) - elsif other < 0 - -BigInt.new { |mpz| LibGMP.tdiv_q_ui(mpz, self, other.abs) } - else - BigInt.new { |mpz| LibGMP.tdiv_q_ui(mpz, self, other) } + Int.primitive_ui_check(other) do |ui, neg_ui, big_i| + { + ui: BigInt.new { |mpz| LibGMP.tdiv_q_ui(mpz, self, {{ ui }}) }, + neg_ui: BigInt.new { |mpz| LibGMP.tdiv_q_ui(mpz, self, {{ neg_ui }}); LibGMP.neg(mpz, mpz) }, + big_i: unsafe_truncated_div({{ big_i }}), + } end end def %(other : Int) : BigInt check_division_by_zero other - if other < 0 - -(-self).unsafe_floored_mod(other.abs) - else - unsafe_floored_mod(other) - end + unsafe_floored_mod(other) end def remainder(other : Int) : BigInt @@ -303,46 +286,23 @@ struct BigInt < Int unsafe_truncated_mod(other) end - def divmod(number : BigInt) : {BigInt, BigInt} + def divmod(number : Int) : {BigInt, BigInt} check_division_by_zero number unsafe_floored_divmod(number) end - def divmod(number : LibGMP::ULong) - check_division_by_zero number - unsafe_floored_divmod(number) - end - - def divmod(number : Int::Signed) : {BigInt, BigInt} - check_division_by_zero number - if number > 0 && number <= LibC::Long::MAX - unsafe_floored_divmod(LibGMP::ULong.new(number)) - else - divmod(number.to_big_i) - end - end - - def divmod(number : Int::Unsigned) - check_division_by_zero number - if number <= LibC::ULong::MAX - unsafe_floored_divmod(LibGMP::ULong.new(number)) - else - divmod(number.to_big_i) - end - end - def unsafe_floored_mod(other : BigInt) : BigInt BigInt.new { |mpz| LibGMP.fdiv_r(mpz, self, other) } end def unsafe_floored_mod(other : Int) : BigInt - if (other < LibGMP::Long::MIN || other > LibGMP::ULong::MAX) - unsafe_floored_mod(other.to_big_i) - elsif other < 0 - -BigInt.new { |mpz| LibGMP.fdiv_r_ui(mpz, self, other.abs) } - else - BigInt.new { |mpz| LibGMP.fdiv_r_ui(mpz, self, other) } + Int.primitive_ui_check(other) do |ui, neg_ui, big_i| + { + ui: BigInt.new { |mpz| LibGMP.fdiv_r_ui(mpz, self, {{ ui }}) }, + neg_ui: BigInt.new { |mpz| LibGMP.fdiv_r_ui(mpz, self, {{ neg_ui }}); LibGMP.neg(mpz, mpz) }, + big_i: unsafe_floored_mod({{ big_i }}), + } end end @@ -350,12 +310,14 @@ struct BigInt < Int BigInt.new { |mpz| LibGMP.tdiv_r(mpz, self, other) } end - def unsafe_truncated_mod(other : LibGMP::IntPrimitive) : BigInt - BigInt.new { |mpz| LibGMP.tdiv_r_ui(mpz, self, other.abs) } - end - def unsafe_truncated_mod(other : Int) : BigInt - BigInt.new { |mpz| LibGMP.tdiv_r_ui(mpz, self, other.abs.to_big_i) } + Int.primitive_ui_check(other) do |ui, neg_ui, big_i| + { + ui: BigInt.new { |mpz| LibGMP.tdiv_r_ui(mpz, self, {{ ui }}) }, + neg_ui: BigInt.new { |mpz| LibGMP.tdiv_r_ui(mpz, self, {{ neg_ui }}) }, + big_i: unsafe_truncated_mod({{ big_i }}), + } + end end def unsafe_floored_divmod(number : BigInt) : {BigInt, BigInt} @@ -364,9 +326,15 @@ struct BigInt < Int {the_q, the_r} end - def unsafe_floored_divmod(number : LibGMP::ULong) : {BigInt, BigInt} + def unsafe_floored_divmod(number : Int) : {BigInt, BigInt} the_q = BigInt.new - the_r = BigInt.new { |r| LibGMP.fdiv_qr_ui(the_q, r, self, number) } + the_r = Int.primitive_ui_check(number) do |ui, neg_ui, big_i| + { + ui: BigInt.new { |r| LibGMP.fdiv_qr_ui(the_q, r, self, {{ ui }}) }, + neg_ui: BigInt.new { |r| LibGMP.fdiv_qr_ui(the_q, r, -self, {{ neg_ui }}); LibGMP.neg(r, r) }, + big_i: BigInt.new { |r| LibGMP.fdiv_qr(the_q, r, self, {{ big_i }}) }, + } + end {the_q, the_r} end @@ -376,9 +344,15 @@ struct BigInt < Int {the_q, the_r} end - def unsafe_truncated_divmod(number : LibGMP::ULong) + def unsafe_truncated_divmod(number : Int) the_q = BigInt.new - the_r = BigInt.new { |r| LibGMP.tdiv_qr_ui(the_q, r, self, number) } + the_r = Int.primitive_ui_check(number) do |ui, neg_ui, big_i| + { + ui: BigInt.new { |r| LibGMP.tdiv_qr_ui(the_q, r, self, {{ ui }}) }, + neg_ui: BigInt.new { |r| LibGMP.tdiv_qr_ui(the_q, r, self, {{ neg_ui }}); LibGMP.neg(the_q, the_q) }, + big_i: BigInt.new { |r| LibGMP.tdiv_qr(the_q, r, self, {{ big_i }}) }, + } + end {the_q, the_r} end @@ -386,17 +360,13 @@ struct BigInt < Int LibGMP.divisible_p(self, number) != 0 end - def divisible_by?(number : LibGMP::ULong) : Bool - LibGMP.divisible_ui_p(self, number) != 0 - end - def divisible_by?(number : Int) : Bool - if 0 <= number <= LibGMP::ULong::MAX - LibGMP.divisible_ui_p(self, number) != 0 - elsif LibGMP::Long::MIN < number < 0 - LibGMP.divisible_ui_p(self, number.abs) != 0 - else - divisible_by?(number.to_big_i) + Int.primitive_ui_check(number) do |ui, neg_ui, big_i| + { + ui: LibGMP.divisible_ui_p(self, {{ ui }}) != 0, + neg_ui: LibGMP.divisible_ui_p(self, {{ neg_ui }}) != 0, + big_i: divisible_by?({{ big_i }}), + } end end @@ -459,8 +429,19 @@ struct BigInt < Int # :ditto: def gcd(other : Int) : Int - result = LibGMP.gcd_ui(nil, self, other.abs.to_u64) - result == 0 ? self : result + Int.primitive_ui_check(other) do |ui, neg_ui, big_i| + { + ui: begin + result = LibGMP.gcd_ui(nil, self, {{ ui }}) + result == 0 ? self : result + end, + neg_ui: begin + result = LibGMP.gcd_ui(nil, self, {{ neg_ui }}) + result == 0 ? self : result + end, + big_i: gcd({{ big_i }}), + } + end end # Returns the least common multiple of `self` and *other*. @@ -470,7 +451,13 @@ struct BigInt < Int # :ditto: def lcm(other : Int) : BigInt - BigInt.new { |mpz| LibGMP.lcm_ui(mpz, self, other.abs.to_u64) } + Int.primitive_ui_check(other) do |ui, neg_ui, big_i| + { + ui: BigInt.new { |mpz| LibGMP.lcm_ui(mpz, self, {{ ui }}) }, + neg_ui: BigInt.new { |mpz| LibGMP.lcm_ui(mpz, self, {{ neg_ui }}) }, + big_i: lcm({{ big_i }}), + } + end end def bit_length : Int32 @@ -819,15 +806,12 @@ struct Int end def -(other : BigInt) : BigInt - if self < 0 - -(abs + other) - else - # The line below segfault on linux 32 bits for a (yet) unknown reason: - # - # BigInt.new { |mpz| LibGMP.ui_sub(mpz, self.to_u64, other) } - # - # So for now we do it a bit slower. - to_big_i - other + Int.primitive_ui_check(self) do |ui, neg_ui, big_i| + { + ui: BigInt.new { |mpz| LibGMP.neg(mpz, other); LibGMP.add_ui(mpz, mpz, {{ ui }}) }, + neg_ui: BigInt.new { |mpz| LibGMP.neg(mpz, other); LibGMP.sub_ui(mpz, mpz, {{ neg_ui }}) }, + big_i: {{ big_i }} - other, + } end end @@ -982,7 +966,7 @@ struct Crystal::Hasher def int(value : BigInt) # it should calculate `remainder(HASH_MODULUS)` - if LibGMP::ULong == UInt64 + if LibGMP::UI == UInt64 v = LibGMP.tdiv_ui(value, HASH_MODULUS).to_i64 value < 0 ? -v : v elsif value >= HASH_MODULUS_INT_P || value <= HASH_MODULUS_INT_N diff --git a/src/big/big_rational.cr b/src/big/big_rational.cr index 4b81db43f46b..be510e6033bb 100644 --- a/src/big/big_rational.cr +++ b/src/big/big_rational.cr @@ -102,15 +102,13 @@ struct BigRational < Number self <=> other.to_big_r end - def <=>(other : Int::Primitive) - if LibGMP::SI::MIN <= other <= LibGMP::UI::MAX - if other <= LibGMP::SI::MAX - LibGMP.mpq_cmp_si(self, LibGMP::SI.new!(other), 1) - else - LibGMP.mpq_cmp_ui(self, LibGMP::UI.new!(other), 1) - end - else - self <=> other.to_big_i + def <=>(other : Int) + Int.primitive_si_ui_check(other) do |si, ui, big_i| + { + si: LibGMP.mpq_cmp_si(self, {{ si }}, 1), + ui: LibGMP.mpq_cmp_ui(self, {{ ui }}, 1), + big_i: self <=> {{ big_i }}, + } end end diff --git a/src/big/lib_gmp.cr b/src/big/lib_gmp.cr index 52eecdf945fb..9caf408f1494 100644 --- a/src/big/lib_gmp.cr +++ b/src/big/lib_gmp.cr @@ -23,10 +23,6 @@ lib LibGMP alias Double = LibC::Double alias BitcntT = UI - alias IntPrimitiveSigned = Int8 | Int16 | Int32 | LibC::Long - alias IntPrimitiveUnsigned = UInt8 | UInt16 | UInt32 | LibC::ULong - alias IntPrimitive = IntPrimitiveSigned | IntPrimitiveUnsigned - {% if flag?(:win32) && flag?(:bits64) %} alias MpExp = LibC::Long alias MpSize = LibC::LongLong diff --git a/src/big/number.cr b/src/big/number.cr index 8cdacf5413e5..1251e8113db3 100644 --- a/src/big/number.cr +++ b/src/big/number.cr @@ -8,17 +8,18 @@ struct BigFloat self.class.new(self / other) end - def /(other : UInt8 | UInt16 | UInt32 | UInt64) : BigFloat + def /(other : Int::Primitive) : BigFloat # Division by 0 in BigFloat is not allowed, there is no BigFloat::Infinity raise DivisionByZeroError.new if other == 0 - if other.is_a?(UInt8 | UInt16 | UInt32) || (LibGMP::ULong == UInt64 && other.is_a?(UInt64)) - BigFloat.new { |mpf| LibGMP.mpf_div_ui(mpf, self, other) } - else - BigFloat.new { |mpf| LibGMP.mpf_div(mpf, self, other.to_big_f) } + Int.primitive_ui_check(other) do |ui, neg_ui, _| + { + ui: BigFloat.new { |mpf| LibGMP.mpf_div_ui(mpf, self, {{ ui }}) }, + neg_ui: BigFloat.new { |mpf| LibGMP.mpf_div_ui(mpf, self, {{ neg_ui }}); LibGMP.mpf_neg(mpf, mpf) }, + big_i: BigFloat.new { |mpf| LibGMP.mpf_div(mpf, self, BigFloat.new(other)) }, + } end end - Number.expand_div [Int8, Int16, Int32, Int64, Int128, UInt128], BigFloat Number.expand_div [Float32, Float64], BigFloat end @@ -32,6 +33,62 @@ struct BigRational Number.expand_div [Float32, Float64], BigRational end +struct Int + # :nodoc: + # Yields 3 expressions: `Call`s nodes that convert *var* into a `LibGMP::SI`, + # a `LibGMP::UI`, and a `BigInt` respectively. These expressions are not + # evaluated unless they are interpolated in *block*. + # + # *block* should return a named tuple: the value for `:si` is returned by the + # macro if *var* fits into a `LibGMP::SI`, the value for `:ui` returned if + # *var* fits into a `LibGMP::UI`, and the value for `:big_i` otherwise. + macro primitive_si_ui_check(var, &block) + {% + exps = yield( + "::LibGMP::SI.new!(#{var.id})".id, + "::LibGMP::UI.new!(#{var.id})".id, + "::BigInt.new(#{var.id})".id, + ) + %} + if ::LibGMP::SI::MIN <= {{ var }} <= ::LibGMP::UI::MAX + if {{ var }} <= ::LibGMP::SI::MAX + {{ exps[:si] }} + else + {{ exps[:ui] }} + end + else + {{ exps[:big_i] }} + end + end + + # :nodoc: + # Yields 3 expressions: `Call`s nodes that convert *var* into a `LibGMP::UI`, + # the negative of *var* into a `LibGMP::UI`, and *var* into a `BigInt`, + # respectively. These expressions are not evaluated unless they are + # interpolated in *block*. + # + # *block* should return a named tuple: the value for `:ui` is returned by the + # macro if *var* fits into a `LibGMP::UI`, the value for `:neg_ui` returned if + # the negative of *var* fits into a `LibGMP::UI`, and the value for `:big_i` + # otherwise. + macro primitive_ui_check(var, &block) + {% + exps = yield( + "::LibGMP::UI.new!(#{var.id})".id, + "::LibGMP::UI.new!((#{var.id}).abs_unsigned)".id, + "::BigInt.new(#{var.id})".id, + ) + %} + if ::LibGMP::UI::MIN <= {{ var }} <= ::LibGMP::UI::MAX + {{ exps[:ui] }} + elsif {{ var }}.responds_to?(:abs_unsigned) && {{ var }}.abs_unsigned <= ::LibGMP::UI::MAX + {{ exps[:neg_ui] }} + else + {{ exps[:big_i] }} + end + end +end + struct Int8 Number.expand_div [BigInt], BigFloat Number.expand_div [BigFloat], BigFloat diff --git a/src/compiler/crystal/codegen/target.cr b/src/compiler/crystal/codegen/target.cr index 1e731d801b58..8817221c18b9 100644 --- a/src/compiler/crystal/codegen/target.cr +++ b/src/compiler/crystal/codegen/target.cr @@ -195,7 +195,7 @@ class Crystal::Codegen::Target target = LLVM::Target.from_triple(self.to_s) machine = target.create_target_machine(self.to_s, cpu: cpu, features: features, opt_level: opt_level, code_model: code_model).not_nil! - # We need to disable global isel until https://reviews.llvm.org/D80898 is released, + # FIXME: We need to disable global isel until https://reviews.llvm.org/D80898 is released, # or we fixed generating values for 0 sized types. # When removing this, also remove it from the ABI specs and jit compiler. # See https://github.com/crystal-lang/crystal/issues/9297#issuecomment-636512270 diff --git a/src/compiler/crystal/tools/doc/html/_macros_inherited.html b/src/compiler/crystal/tools/doc/html/_macros_inherited.html new file mode 100644 index 000000000000..3ef7f287ca43 --- /dev/null +++ b/src/compiler/crystal/tools/doc/html/_macros_inherited.html @@ -0,0 +1,11 @@ +<% macro_groups = macros.group_by { |m| m.name } %> +<% unless macro_groups.empty? %> +

Macros inherited from <%= ancestor.kind %> <%= ancestor.link_from(type) %>

+ <% i = 0 %> + <% macro_groups.each do |k, v| %> + + <%= v.map { |m| m.name + m.args_to_html(:highlight) } .join("
") %>
+ <%= k %>
<%= ", " if i != macro_groups.size - 1 %> + <% i += 1 %> + <% end %> +<% end %> diff --git a/src/compiler/crystal/tools/doc/html/type.html b/src/compiler/crystal/tools/doc/html/type.html index aaa3d338df9e..cd8115e74f5c 100644 --- a/src/compiler/crystal/tools/doc/html/type.html +++ b/src/compiler/crystal/tools/doc/html/type.html @@ -103,6 +103,7 @@

<%= MethodsInheritedTemplate.new(type, ancestor, ancestor.instance_methods, "Instance") %> <%= MethodsInheritedTemplate.new(type, ancestor, ancestor.constructors, "Constructor") %> <%= MethodsInheritedTemplate.new(type, ancestor, ancestor.class_methods, "Class") %> + <%= MacrosInheritedTemplate.new(type, ancestor, ancestor.macros) %> <% end %> diff --git a/src/compiler/crystal/tools/doc/templates.cr b/src/compiler/crystal/tools/doc/templates.cr index 6463ea637479..91ad32e1d0d1 100644 --- a/src/compiler/crystal/tools/doc/templates.cr +++ b/src/compiler/crystal/tools/doc/templates.cr @@ -49,6 +49,10 @@ module Crystal::Doc ECR.def_to_s "#{__DIR__}/html/_methods_inherited.html" end + record MacrosInheritedTemplate, type : Type, ancestor : Type, macros : Array(Macro) do + ECR.def_to_s "#{__DIR__}/html/_macros_inherited.html" + end + record OtherTypesTemplate, title : String, type : Type, other_types : Array(Type) do ECR.def_to_s "#{__DIR__}/html/_other_types.html" end diff --git a/src/compiler/crystal/tools/unreachable.cr b/src/compiler/crystal/tools/unreachable.cr index e03d64f1becc..f89f90e4c37d 100644 --- a/src/compiler/crystal/tools/unreachable.cr +++ b/src/compiler/crystal/tools/unreachable.cr @@ -1,11 +1,12 @@ require "../syntax/ast" require "../compiler" require "json" +require "csv" module Crystal class Command private def unreachable - config, result = compile_no_codegen "tool unreachable", path_filter: true, unreachable_command: true + config, result = compile_no_codegen "tool unreachable", path_filter: true, unreachable_command: true, allowed_formats: %w[text json csv] unreachable = UnreachableVisitor.new @@ -39,6 +40,8 @@ module Crystal case format when "json" to_json(STDOUT) + when "csv" + to_csv(STDOUT) else to_text(STDOUT) end @@ -83,6 +86,22 @@ module Crystal end end end + + def to_csv(io) + CSV.build(io) do |builder| + builder.row %w[name file line column length annotations] + each do |a_def, location| + builder.row do |row| + row << a_def.short_reference + row << location.filename + row << location.line_number + row << location.column_number + row << a_def.length + row << a_def.all_annotations.try(&.join(" ")) + end + end + end + end end # This visitor walks the entire reachable code tree and collect locations diff --git a/src/crystal/system/thread.cr b/src/crystal/system/thread.cr index 9e58962fd49b..878be639a7a3 100644 --- a/src/crystal/system/thread.cr +++ b/src/crystal/system/thread.cr @@ -1,26 +1,127 @@ +# :nodoc: +module Crystal::System::Thread + # alias Handle + + # def self.new_handle(thread_obj : ::Thread) : Handle + + # def self.current_handle : Handle + + # def self.yield_current : Nil + + # def self.current_thread : ::Thread + + # def self.current_thread=(thread : ::Thread) + + # private def system_join : Exception? + + # private def system_close + + # private def stack_address : Void* +end + +{% if flag?(:wasi) %} + require "./wasi/thread" +{% elsif flag?(:unix) %} + require "./unix/pthread" +{% elsif flag?(:win32) %} + require "./win32/thread" +{% else %} + {% raise "Thread not supported" %} +{% end %} + # :nodoc: class Thread + include Crystal::System::Thread + + # all thread objects, so the GC can see them (it doesn't scan thread locals) + protected class_getter(threads) { Thread::LinkedList(Thread).new } + + @system_handle : Crystal::System::Thread::Handle + @exception : Exception? + @detached = Atomic::Flag.new + + # Returns the Fiber representing the thread's main stack. + getter! main_fiber : Fiber + + # :nodoc: + property next : Thread? + + # :nodoc: + property previous : Thread? + + def self.unsafe_each(&) + threads.unsafe_each { |thread| yield thread } + end + # Creates and starts a new system thread. - # def initialize(&proc : ->) + def initialize(&@func : ->) + @system_handle = uninitialized Crystal::System::Thread::Handle + init_handle + end # Used once to initialize the thread object representing the main thread of # the process (that already exists). - # def initialize + def initialize + @func = ->{} + @system_handle = Crystal::System::Thread.current_handle + @main_fiber = Fiber.new(stack_address, self) - # Suspends the current thread until this thread terminates. - # def join : Nil + Thread.threads.push(self) + end - # Returns the Fiber representing the thread's main stack. - # def main_fiber + private def detach(&) + if @detached.test_and_set + yield + end + end + + # Suspends the current thread until this thread terminates. + def join : Nil + detach do + if ex = system_join + @exception ||= ex + end + end - # Yields the thread. - # def self.yield : Nil + if exception = @exception + raise exception + end + end # Returns the Thread object associated to the running system thread. - # def self.current : Thread + def self.current : Thread + Crystal::System::Thread.current_thread + end # Associates the Thread object to the running system thread. - # def self.current=(thread : Thread) + protected def self.current=(current : Thread) : Thread + Crystal::System::Thread.current_thread = current + current + end + + # Yields the currently running thread. + def self.yield : Nil + Crystal::System::Thread.yield_current + end + + # :nodoc: + getter scheduler : Crystal::Scheduler { Crystal::Scheduler.new(main_fiber) } + + protected def start + Thread.threads.push(self) + Thread.current = self + @main_fiber = fiber = Fiber.new(stack_address, self) + + begin + @func.call + rescue ex + @exception = ex + ensure + Thread.threads.delete(self) + Fiber.inactive(fiber) + detach { system_close } + end + end # Holds the GC thread handler property gc_thread_handler : Void* = Pointer(Void).null @@ -28,13 +129,3 @@ end require "./thread_linked_list" require "./thread_condition_variable" - -{% if flag?(:wasi) %} - require "./wasi/thread" -{% elsif flag?(:unix) %} - require "./unix/pthread" -{% elsif flag?(:win32) %} - require "./win32/thread" -{% else %} - {% raise "Thread not supported" %} -{% end %} diff --git a/src/crystal/system/unix/pthread.cr b/src/crystal/system/unix/pthread.cr index bde5cc7b4333..44076dedcc43 100644 --- a/src/crystal/system/unix/pthread.cr +++ b/src/crystal/system/unix/pthread.cr @@ -1,62 +1,34 @@ require "c/pthread" require "c/sched" -class Thread - # all thread objects, so the GC can see them (it doesn't scan thread locals) - protected class_getter(threads) { Thread::LinkedList(Thread).new } +module Crystal::System::Thread + alias Handle = LibC::PthreadT - @th : LibC::PthreadT - @exception : Exception? - @detached = Atomic(UInt8).new(0) - @main_fiber : Fiber? - - # :nodoc: - property next : Thread? - - # :nodoc: - property previous : Thread? - - def self.unsafe_each(&) - threads.unsafe_each { |thread| yield thread } - end - - # Starts a new system thread. - def initialize(&@func : ->) - @th = uninitialized LibC::PthreadT - - ret = GC.pthread_create(pointerof(@th), Pointer(LibC::PthreadAttrT).null, ->(data : Void*) { - (data.as(Thread)).start - Pointer(Void).null - }, self.as(Void*)) - - if ret != 0 - raise RuntimeError.from_os_error("pthread_create", Errno.new(ret)) - end + def to_unsafe + @system_handle end - # Used once to initialize the thread object representing the main thread of - # the process (that already exists). - def initialize - @func = ->{} - @th = LibC.pthread_self - @main_fiber = Fiber.new(stack_address, self) - - Thread.threads.push(self) + private def init_handle + # NOTE: the thread may start before `pthread_create` returns, so + # `@system_handle` must be set as soon as possible; we cannot use a separate + # handle and assign it to `@system_handle`, which would have been too late + ret = GC.pthread_create( + thread: pointerof(@system_handle), + attr: Pointer(LibC::PthreadAttrT).null, + start: ->(data : Void*) { data.as(::Thread).start; Pointer(Void).null }, + arg: self.as(Void*), + ) + + raise RuntimeError.from_os_error("pthread_create", Errno.new(ret)) unless ret == 0 end - private def detach(&) - if @detached.compare_and_set(0, 1).last - yield - end + def self.current_handle : Handle + LibC.pthread_self end - # Suspends the current thread until this thread terminates. - def join : Nil - detach { GC.pthread_join(@th) } - - if exception = @exception - raise exception - end + def self.yield_current : Nil + ret = LibC.sched_yield + raise RuntimeError.from_errno("sched_yield") unless ret == 0 end {% if flag?(:openbsd) %} @@ -70,68 +42,33 @@ class Thread current_key end - # Returns the Thread object associated to the running system thread. - def self.current : Thread + def self.current_thread : ::Thread if ptr = LibC.pthread_getspecific(@@current_key) - ptr.as(Thread) + ptr.as(::Thread) else - # Thread#start sets @@current as soon it starts. Thus we know - # that if @@current is not set then we are in the main thread - self.current = new + # Thread#start sets `Thread.current` as soon it starts. Thus we know + # that if `Thread.current` is not set then we are in the main thread + self.current_thread = ::Thread.new end end - # Associates the Thread object to the running system thread. - protected def self.current=(thread : Thread) : Thread + def self.current_thread=(thread : ::Thread) ret = LibC.pthread_setspecific(@@current_key, thread.as(Void*)) raise RuntimeError.from_os_error("pthread_setspecific", Errno.new(ret)) unless ret == 0 thread end {% else %} @[ThreadLocal] - @@current : Thread? - - # Returns the Thread object associated to the running system thread. - def self.current : Thread - # 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. - protected def self.current=(@@current : Thread) : Thread - end + class_property current_thread : ::Thread { ::Thread.new } {% end %} - def self.yield : Nil - ret = LibC.sched_yield - raise RuntimeError.from_errno("sched_yield") unless ret == 0 - end - - # Returns the Fiber representing the thread's main stack. - def main_fiber : Fiber - @main_fiber.not_nil! - end - - # :nodoc: - def scheduler : Crystal::Scheduler - @scheduler ||= Crystal::Scheduler.new(main_fiber) + private def system_join : Exception? + ret = GC.pthread_join(@system_handle) + RuntimeError.from_os_error("pthread_join", Errno.new(ret)) unless ret == 0 end - protected def start - Thread.threads.push(self) - Thread.current = self - @main_fiber = fiber = Fiber.new(stack_address, self) - - begin - @func.call - rescue ex - @exception = ex - ensure - Thread.threads.delete(self) - Fiber.inactive(fiber) - detach { GC.pthread_detach(@th) } - end + private def system_close + GC.pthread_detach(@system_handle) end private def stack_address : Void* @@ -139,7 +76,7 @@ class Thread {% if flag?(:darwin) %} # FIXME: pthread_get_stacksize_np returns bogus value on macOS X 10.9.0: - address = LibC.pthread_get_stackaddr_np(@th) - LibC.pthread_get_stacksize_np(@th) + address = LibC.pthread_get_stackaddr_np(@system_handle) - LibC.pthread_get_stacksize_np(@system_handle) {% elsif flag?(:bsd) && !flag?(:openbsd) %} ret = LibC.pthread_attr_init(out attr) unless ret == 0 @@ -147,19 +84,19 @@ class Thread raise RuntimeError.from_os_error("pthread_attr_init", Errno.new(ret)) end - if LibC.pthread_attr_get_np(@th, pointerof(attr)) == 0 + if LibC.pthread_attr_get_np(@system_handle, pointerof(attr)) == 0 LibC.pthread_attr_getstack(pointerof(attr), pointerof(address), out _) end ret = LibC.pthread_attr_destroy(pointerof(attr)) raise RuntimeError.from_os_error("pthread_attr_destroy", Errno.new(ret)) unless ret == 0 {% elsif flag?(:linux) %} - if LibC.pthread_getattr_np(@th, out attr) == 0 + if LibC.pthread_getattr_np(@system_handle, out attr) == 0 LibC.pthread_attr_getstack(pointerof(attr), pointerof(address), out _) end ret = LibC.pthread_attr_destroy(pointerof(attr)) raise RuntimeError.from_os_error("pthread_attr_destroy", Errno.new(ret)) unless ret == 0 {% elsif flag?(:openbsd) %} - ret = LibC.pthread_stackseg_np(@th, out stack) + ret = LibC.pthread_stackseg_np(@system_handle, out stack) raise RuntimeError.from_os_error("pthread_stackseg_np", Errno.new(ret)) unless ret == 0 address = @@ -172,11 +109,6 @@ class Thread address end - - # :nodoc: - def to_unsafe - @th - end end # In musl (alpine) the calls to unwind API segfaults diff --git a/src/crystal/system/wasi/thread.cr b/src/crystal/system/wasi/thread.cr index 805c7fbb77a6..0e641faba785 100644 --- a/src/crystal/system/wasi/thread.cr +++ b/src/crystal/system/wasi/thread.cr @@ -1,53 +1,25 @@ -class Thread - @main_fiber : Fiber? +module Crystal::System::Thread + alias Handle = Nil - def initialize - @main_fiber = Fiber.new(stack_address, self) - - # TODO: Create thread - end - - def initialize(&func : ->) - initialize - end - - def join : Nil - raise NotImplementedError.new("Thread#join") + def self.new_handle(thread_obj : ::Thread) : Handle + raise NotImplementedError.new("Crystal::System::Thread.new_handle") end - def self.yield : Nil - raise NotImplementedError.new("Thread.yield") + def self.current_handle : Handle + nil end - @@current = Thread.new - - # Associates the Thread object to the running system thread. - protected def self.current=(@@current : Thread) : Thread - end - - # Returns the Thread object associated to the running system thread. - def self.current : Thread - @@current + def self.yield_current : Nil + raise NotImplementedError.new("Crystal::System::Thread.yield_current") 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 - - # Returns the Fiber representing the thread's main stack. - def main_fiber - @main_fiber.not_nil! - end + class_property current_thread : ::Thread { ::Thread.new } - # :nodoc: - def scheduler - @scheduler ||= Crystal::Scheduler.new(main_fiber) + private def system_join : Exception? + NotImplementedError.new("Crystal::System::Thread#system_join") end - protected def start - raise NotImplementedError.new("Thread#start") + private def system_close end private def stack_address : Void* diff --git a/src/crystal/system/win32/thread.cr b/src/crystal/system/win32/thread.cr index badfff437ed5..9a13bfb4dc03 100644 --- a/src/crystal/system/win32/thread.cr +++ b/src/crystal/system/win32/thread.cr @@ -1,115 +1,52 @@ require "c/processthreadsapi" require "c/synchapi" -class Thread - # all thread objects, so the GC can see them (it doesn't scan thread locals) - protected class_getter(threads) { Thread::LinkedList(Thread).new } +module Crystal::System::Thread + alias Handle = LibC::HANDLE - @th : LibC::HANDLE - @exception : Exception? - @detached = Atomic(UInt8).new(0) - @main_fiber : Fiber? - - # :nodoc: - property next : Thread? - - # :nodoc: - property previous : Thread? - - def self.unsafe_each(&) - threads.unsafe_each { |thread| yield thread } + def to_unsafe + @system_handle end - # Starts a new system thread. - def initialize(&@func : ->) - @th = uninitialized LibC::HANDLE - - @th = GC.beginthreadex( + private def init_handle + @system_handle = GC.beginthreadex( security: Pointer(Void).null, stack_size: LibC::UInt.zero, - start_address: ->(data : Void*) { data.as(Thread).start; LibC::UInt.zero }, + start_address: ->(data : Void*) { data.as(::Thread).start; LibC::UInt.zero }, arglist: self.as(Void*), initflag: LibC::UInt.zero, - thrdaddr: Pointer(LibC::UInt).null) + thrdaddr: Pointer(LibC::UInt).null, + ) end - # Used once to initialize the thread object representing the main thread of - # the process (that already exists). - def initialize + def self.current_handle : Handle # `GetCurrentThread` returns a _constant_ and is only meaningful as an # argument to Win32 APIs; to uniquely identify it we must duplicate the handle - @th = uninitialized LibC::HANDLE cur_proc = LibC.GetCurrentProcess - LibC.DuplicateHandle(cur_proc, LibC.GetCurrentThread, cur_proc, pointerof(@th), 0, true, LibC::DUPLICATE_SAME_ACCESS) - - @func = ->{} - @main_fiber = Fiber.new(stack_address, self) - - Thread.threads.push(self) - end - - private def detach(&) - if @detached.compare_and_set(0, 1).last - yield + if LibC.DuplicateHandle(cur_proc, LibC.GetCurrentThread, cur_proc, out handle, 0, true, LibC::DUPLICATE_SAME_ACCESS) == 0 + raise RuntimeError.from_winerror("DuplicateHandle") end + handle end - # Suspends the current thread until this thread terminates. - def join : Nil - detach do - if LibC.WaitForSingleObject(@th, LibC::INFINITE) != LibC::WAIT_OBJECT_0 - @exception ||= RuntimeError.from_winerror("WaitForSingleObject") - end - if LibC.CloseHandle(@th) == 0 - @exception ||= RuntimeError.from_winerror("CloseHandle") - end - end - - if exception = @exception - raise exception - end - end - - @[ThreadLocal] - @@current : Thread? - - # Returns the Thread object associated to the running system thread. - def self.current : Thread - @@current ||= new - end - - # Associates the Thread object to the running system thread. - protected def self.current=(@@current : Thread) : Thread - end - - def self.yield : Nil + def self.yield_current : Nil LibC.SwitchToThread end - # Returns the Fiber representing the thread's main stack. - def main_fiber : Fiber - @main_fiber.not_nil! - end + @[ThreadLocal] + class_property current_thread : ::Thread { ::Thread.new } - # :nodoc: - def scheduler : Crystal::Scheduler - @scheduler ||= Crystal::Scheduler.new(main_fiber) + private def system_join : Exception? + if LibC.WaitForSingleObject(@system_handle, LibC::INFINITE) != LibC::WAIT_OBJECT_0 + return RuntimeError.from_winerror("WaitForSingleObject") + end + if LibC.CloseHandle(@system_handle) == 0 + return RuntimeError.from_winerror("CloseHandle") + end end - protected def start - Thread.threads.push(self) - Thread.current = self - @main_fiber = fiber = Fiber.new(stack_address, self) - - begin - @func.call - rescue ex - @exception = ex - ensure - Thread.threads.delete(self) - Fiber.inactive(fiber) - detach { LibC.CloseHandle(@th) } - end + private def system_close + LibC.CloseHandle(@system_handle) end private def stack_address : Void* @@ -124,9 +61,4 @@ class Thread low_limit {% end %} end - - # :nodoc: - def to_unsafe - @th - end end diff --git a/src/gc/boehm.cr b/src/gc/boehm.cr index 28056f75aead..b4dfb0061900 100644 --- a/src/gc/boehm.cr +++ b/src/gc/boehm.cr @@ -262,10 +262,8 @@ module GC end # :nodoc: - def self.pthread_join(thread : LibC::PthreadT) : Void* - ret = LibGC.pthread_join(thread, out value) - raise RuntimeError.from_os_error("pthread_join", Errno.new(ret)) unless ret == 0 - value + def self.pthread_join(thread : LibC::PthreadT) + LibGC.pthread_join(thread, nil) end # :nodoc: diff --git a/src/gc/none.cr b/src/gc/none.cr index 4e4441f6e54e..c71ab05ccd8d 100644 --- a/src/gc/none.cr +++ b/src/gc/none.cr @@ -85,10 +85,8 @@ module GC end # :nodoc: - def self.pthread_join(thread : LibC::PthreadT) : Void* - ret = LibC.pthread_join(thread, out value) - raise RuntimeError.from_errno("pthread_join") unless ret == 0 - value + def self.pthread_join(thread : LibC::PthreadT) + LibC.pthread_join(thread, nil) end # :nodoc: diff --git a/src/hash.cr b/src/hash.cr index 156f2d7d6313..8d8ccea22a0b 100644 --- a/src/hash.cr +++ b/src/hash.cr @@ -266,6 +266,16 @@ class Hash(K, V) # Creates a new empty `Hash` with a *block* that handles missing keys. # # ``` + # inventory = Hash(String, Int32).new(0) + # inventory["socks"] = 3 + # inventory["pickles"] # => 0 + # ``` + # + # WARNING: When the default block is invoked on a missing key, its return + # value is *not* implicitly stored into the hash under that key. If you want + # that behaviour, you need to put it explicitly: + # + # ``` # hash = Hash(String, Int32).new do |hash, key| # hash[key] = key.size # end @@ -294,14 +304,23 @@ class Hash(K, V) # inventory["pickles"] # => 0 # ``` # - # NOTE: The default value is passed by reference: + # WARNING: When the default value gets returned on a missing key, it is *not* + # stored into the hash under that key. If you want that behaviour, please use + # the overload with a block. + # + # WARNING: The default value is returned as-is. It gets neither duplicated nor + # cloned. For types with reference semantics this means it will be exactly the + # *same* object every time. + # # ``` - # arr = [1, 2, 3] - # hash = Hash(String, Array(Int32)).new(arr) - # hash["3"][1] = 4 - # arr # => [1, 4, 3] + # hash = Hash(String, Array(Int32)).new([1]) + # hash["a"][0] = 2 + # hash["b"] # => [2] # ``` # + # * `.new(&block : (Hash(K, V), K -> V))` is an alternative with a block that + # can return a different default value for each invocation. + # # The *initial_capacity* is useful to avoid unnecessary reallocations # of the internal buffer in case of growth. If the number of elements # a hash will hold is known, the hash should be initialized with that diff --git a/src/int.cr b/src/int.cr index 2abfa640763f..6f0d3aa5881a 100644 --- a/src/int.cr +++ b/src/int.cr @@ -897,6 +897,62 @@ struct Int8 0_i8 - self end + # Returns `self` converted to a signed value of the same size. + # + # Simply returns `self` unmodified if `self` is already an `Int::Signed`. + # Raises `OverflowError` in case of overflow. + # + # ``` + # 1_u32.to_signed # => 1_i32 + # 2_u16.to_signed # => 2_i16 + # 3_i64.to_signed # => 3_i64 + # ``` + def to_signed : Int8 + self + end + + # Returns `self` converted to a signed value of the same size, wrapping in + # case of overflow. + # + # Simply returns `self` unmodified if `self` is already an `Int::Signed`. + # + # ``` + # 1_u32.to_signed! # => 1_i32 + # 65530_u16.to_signed! # => -6_i16 + # 3_i64.to_signed! # => 3_i64 + # ``` + def to_signed! : Int8 + self + end + + # Returns `self` converted to an unsigned value of the same size. + # + # Simply returns `self` unmodified if `self` is already an `Int::Unsigned`. + # Raises `OverflowError` in case of overflow. + # + # ``` + # 1_i32.to_unsigned # => 1_u32 + # 2_i16.to_unsigned # => 2_u16 + # 3_u64.to_unsigned # => 3_u64 + # ``` + def to_unsigned : UInt8 + to_u8 + end + + # Returns `self` converted to an unsigned value of the same size, wrapping in + # case of overflow. + # + # Simply returns `self` unmodified if `self` is already an `Int::Unsigned`. + # + # ``` + # 1_i32.to_unsigned! # => 1_u32 + # (-6_i16).to_unsigned! # => 65530_u16 + # 3_u64.to_unsigned! # => 3_u64 + # ``` + def to_unsigned! : UInt8 + to_u8! + end + # Returns the absolute value of `self` as an unsigned value of the same size. # # Returns `self` if `self` is already an `Int::Unsigned`. This method never @@ -1027,6 +1083,62 @@ struct Int16 0_i16 - self end + # Returns `self` converted to a signed value of the same size. + # + # Simply returns `self` unmodified if `self` is already an `Int::Signed`. + # Raises `OverflowError` in case of overflow. + # + # ``` + # 1_u32.to_signed # => 1_i32 + # 2_u16.to_signed # => 2_i16 + # 3_i64.to_signed # => 3_i64 + # ``` + def to_signed : Int16 + self + end + + # Returns `self` converted to a signed value of the same size, wrapping in + # case of overflow. + # + # Simply returns `self` unmodified if `self` is already an `Int::Signed`. + # + # ``` + # 1_u32.to_signed! # => 1_i32 + # 65530_u16.to_signed! # => -6_i16 + # 3_i64.to_signed! # => 3_i64 + # ``` + def to_signed! : Int16 + self + end + + # Returns `self` converted to an unsigned value of the same size. + # + # Simply returns `self` unmodified if `self` is already an `Int::Unsigned`. + # Raises `OverflowError` in case of overflow. + # + # ``` + # 1_i32.to_unsigned # => 1_u32 + # 2_i16.to_unsigned # => 2_u16 + # 3_u64.to_unsigned # => 3_u64 + # ``` + def to_unsigned : UInt16 + to_u16 + end + + # Returns `self` converted to an unsigned value of the same size, wrapping in + # case of overflow. + # + # Simply returns `self` unmodified if `self` is already an `Int::Unsigned`. + # + # ``` + # 1_i32.to_unsigned! # => 1_u32 + # (-6_i16).to_unsigned! # => 65530_u16 + # 3_u64.to_unsigned! # => 3_u64 + # ``` + def to_unsigned! : UInt16 + to_u16! + end + # Returns the absolute value of `self` as an unsigned value of the same size. # # Returns `self` if `self` is already an `Int::Unsigned`. This method never @@ -1157,6 +1269,62 @@ struct Int32 0 - self end + # Returns `self` converted to a signed value of the same size. + # + # Simply returns `self` unmodified if `self` is already an `Int::Signed`. + # Raises `OverflowError` in case of overflow. + # + # ``` + # 1_u32.to_signed # => 1_i32 + # 2_u16.to_signed # => 2_i16 + # 3_i64.to_signed # => 3_i64 + # ``` + def to_signed : Int32 + self + end + + # Returns `self` converted to a signed value of the same size, wrapping in + # case of overflow. + # + # Simply returns `self` unmodified if `self` is already an `Int::Signed`. + # + # ``` + # 1_u32.to_signed! # => 1_i32 + # 65530_u16.to_signed! # => -6_i16 + # 3_i64.to_signed! # => 3_i64 + # ``` + def to_signed! : Int32 + self + end + + # Returns `self` converted to an unsigned value of the same size. + # + # Simply returns `self` unmodified if `self` is already an `Int::Unsigned`. + # Raises `OverflowError` in case of overflow. + # + # ``` + # 1_i32.to_unsigned # => 1_u32 + # 2_i16.to_unsigned # => 2_u16 + # 3_u64.to_unsigned # => 3_u64 + # ``` + def to_unsigned : UInt32 + to_u32 + end + + # Returns `self` converted to an unsigned value of the same size, wrapping in + # case of overflow. + # + # Simply returns `self` unmodified if `self` is already an `Int::Unsigned`. + # + # ``` + # 1_i32.to_unsigned! # => 1_u32 + # (-6_i16).to_unsigned! # => 65530_u16 + # 3_u64.to_unsigned! # => 3_u64 + # ``` + def to_unsigned! : UInt32 + to_u32! + end + # Returns the absolute value of `self` as an unsigned value of the same size. # # Returns `self` if `self` is already an `Int::Unsigned`. This method never @@ -1287,6 +1455,62 @@ struct Int64 0_i64 - self end + # Returns `self` converted to a signed value of the same size. + # + # Simply returns `self` unmodified if `self` is already an `Int::Signed`. + # Raises `OverflowError` in case of overflow. + # + # ``` + # 1_u32.to_signed # => 1_i32 + # 2_u16.to_signed # => 2_i16 + # 3_i64.to_signed # => 3_i64 + # ``` + def to_signed : Int64 + self + end + + # Returns `self` converted to a signed value of the same size, wrapping in + # case of overflow. + # + # Simply returns `self` unmodified if `self` is already an `Int::Signed`. + # + # ``` + # 1_u32.to_signed! # => 1_i32 + # 65530_u16.to_signed! # => -6_i16 + # 3_i64.to_signed! # => 3_i64 + # ``` + def to_signed! : Int64 + self + end + + # Returns `self` converted to an unsigned value of the same size. + # + # Simply returns `self` unmodified if `self` is already an `Int::Unsigned`. + # Raises `OverflowError` in case of overflow. + # + # ``` + # 1_i32.to_unsigned # => 1_u32 + # 2_i16.to_unsigned # => 2_u16 + # 3_u64.to_unsigned # => 3_u64 + # ``` + def to_unsigned : UInt64 + to_u64 + end + + # Returns `self` converted to an unsigned value of the same size, wrapping in + # case of overflow. + # + # Simply returns `self` unmodified if `self` is already an `Int::Unsigned`. + # + # ``` + # 1_i32.to_unsigned! # => 1_u32 + # (-6_i16).to_unsigned! # => 65530_u16 + # 3_u64.to_unsigned! # => 3_u64 + # ``` + def to_unsigned! : UInt64 + to_u64! + end + # Returns the absolute value of `self` as an unsigned value of the same size. # # Returns `self` if `self` is already an `Int::Unsigned`. This method never @@ -1420,6 +1644,62 @@ struct Int128 Int128.new(0) - self end + # Returns `self` converted to a signed value of the same size. + # + # Simply returns `self` unmodified if `self` is already an `Int::Signed`. + # Raises `OverflowError` in case of overflow. + # + # ``` + # 1_u32.to_signed # => 1_i32 + # 2_u16.to_signed # => 2_i16 + # 3_i64.to_signed # => 3_i64 + # ``` + def to_signed : Int128 + self + end + + # Returns `self` converted to a signed value of the same size, wrapping in + # case of overflow. + # + # Simply returns `self` unmodified if `self` is already an `Int::Signed`. + # + # ``` + # 1_u32.to_signed! # => 1_i32 + # 65530_u16.to_signed! # => -6_i16 + # 3_i64.to_signed! # => 3_i64 + # ``` + def to_signed! : Int128 + self + end + + # Returns `self` converted to an unsigned value of the same size. + # + # Simply returns `self` unmodified if `self` is already an `Int::Unsigned`. + # Raises `OverflowError` in case of overflow. + # + # ``` + # 1_i32.to_unsigned # => 1_u32 + # 2_i16.to_unsigned # => 2_u16 + # 3_u64.to_unsigned # => 3_u64 + # ``` + def to_unsigned : UInt128 + to_u128 + end + + # Returns `self` converted to an unsigned value of the same size, wrapping in + # case of overflow. + # + # Simply returns `self` unmodified if `self` is already an `Int::Unsigned`. + # + # ``` + # 1_i32.to_unsigned! # => 1_u32 + # (-6_i16).to_unsigned! # => 65530_u16 + # 3_u64.to_unsigned! # => 3_u64 + # ``` + def to_unsigned! : UInt128 + to_u128! + end + # Returns the absolute value of `self` as an unsigned value of the same size. # # Returns `self` if `self` is already an `Int::Unsigned`. This method never @@ -1550,6 +1830,62 @@ struct UInt8 0_u8 &- self end + # Returns `self` converted to a signed value of the same size. + # + # Simply returns `self` unmodified if `self` is already an `Int::Signed`. + # Raises `OverflowError` in case of overflow. + # + # ``` + # 1_u32.to_signed # => 1_i32 + # 2_u16.to_signed # => 2_i16 + # 3_i64.to_signed # => 3_i64 + # ``` + def to_signed : Int8 + to_i8 + end + + # Returns `self` converted to a signed value of the same size, wrapping in + # case of overflow. + # + # Simply returns `self` unmodified if `self` is already an `Int::Signed`. + # + # ``` + # 1_u32.to_signed! # => 1_i32 + # 65530_u16.to_signed! # => -6_i16 + # 3_i64.to_signed! # => 3_i64 + # ``` + def to_signed! : Int8 + to_i8! + end + + # Returns `self` converted to an unsigned value of the same size. + # + # Simply returns `self` unmodified if `self` is already an `Int::Unsigned`. + # Raises `OverflowError` in case of overflow. + # + # ``` + # 1_i32.to_unsigned # => 1_u32 + # 2_i16.to_unsigned # => 2_u16 + # 3_u64.to_unsigned # => 3_u64 + # ``` + def to_unsigned : UInt8 + self + end + + # Returns `self` converted to an unsigned value of the same size, wrapping in + # case of overflow. + # + # Simply returns `self` unmodified if `self` is already an `Int::Unsigned`. + # + # ``` + # 1_i32.to_unsigned! # => 1_u32 + # (-6_i16).to_unsigned! # => 65530_u16 + # 3_u64.to_unsigned! # => 3_u64 + # ``` + def to_unsigned! : UInt8 + self + end + def abs : self self end @@ -1684,6 +2020,62 @@ struct UInt16 0_u16 &- self end + # Returns `self` converted to a signed value of the same size. + # + # Simply returns `self` unmodified if `self` is already an `Int::Signed`. + # Raises `OverflowError` in case of overflow. + # + # ``` + # 1_u32.to_signed # => 1_i32 + # 2_u16.to_signed # => 2_i16 + # 3_i64.to_signed # => 3_i64 + # ``` + def to_signed : Int16 + to_i16 + end + + # Returns `self` converted to a signed value of the same size, wrapping in + # case of overflow. + # + # Simply returns `self` unmodified if `self` is already an `Int::Signed`. + # + # ``` + # 1_u32.to_signed! # => 1_i32 + # 65530_u16.to_signed! # => -6_i16 + # 3_i64.to_signed! # => 3_i64 + # ``` + def to_signed! : Int16 + to_i16! + end + + # Returns `self` converted to an unsigned value of the same size. + # + # Simply returns `self` unmodified if `self` is already an `Int::Unsigned`. + # Raises `OverflowError` in case of overflow. + # + # ``` + # 1_i32.to_unsigned # => 1_u32 + # 2_i16.to_unsigned # => 2_u16 + # 3_u64.to_unsigned # => 3_u64 + # ``` + def to_unsigned : UInt16 + self + end + + # Returns `self` converted to an unsigned value of the same size, wrapping in + # case of overflow. + # + # Simply returns `self` unmodified if `self` is already an `Int::Unsigned`. + # + # ``` + # 1_i32.to_unsigned! # => 1_u32 + # (-6_i16).to_unsigned! # => 65530_u16 + # 3_u64.to_unsigned! # => 3_u64 + # ``` + def to_unsigned! : UInt16 + self + end + def abs : self self end @@ -1818,6 +2210,62 @@ struct UInt32 0_u32 &- self end + # Returns `self` converted to a signed value of the same size. + # + # Simply returns `self` unmodified if `self` is already an `Int::Signed`. + # Raises `OverflowError` in case of overflow. + # + # ``` + # 1_u32.to_signed # => 1_i32 + # 2_u16.to_signed # => 2_i16 + # 3_i64.to_signed # => 3_i64 + # ``` + def to_signed : Int32 + to_i32 + end + + # Returns `self` converted to a signed value of the same size, wrapping in + # case of overflow. + # + # Simply returns `self` unmodified if `self` is already an `Int::Signed`. + # + # ``` + # 1_u32.to_signed! # => 1_i32 + # 65530_u16.to_signed! # => -6_i16 + # 3_i64.to_signed! # => 3_i64 + # ``` + def to_signed! : Int32 + to_i32! + end + + # Returns `self` converted to an unsigned value of the same size. + # + # Simply returns `self` unmodified if `self` is already an `Int::Unsigned`. + # Raises `OverflowError` in case of overflow. + # + # ``` + # 1_i32.to_unsigned # => 1_u32 + # 2_i16.to_unsigned # => 2_u16 + # 3_u64.to_unsigned # => 3_u64 + # ``` + def to_unsigned : UInt32 + self + end + + # Returns `self` converted to an unsigned value of the same size, wrapping in + # case of overflow. + # + # Simply returns `self` unmodified if `self` is already an `Int::Unsigned`. + # + # ``` + # 1_i32.to_unsigned! # => 1_u32 + # (-6_i16).to_unsigned! # => 65530_u16 + # 3_u64.to_unsigned! # => 3_u64 + # ``` + def to_unsigned! : UInt32 + self + end + def abs : self self end @@ -1952,6 +2400,62 @@ struct UInt64 0_u64 &- self end + # Returns `self` converted to a signed value of the same size. + # + # Simply returns `self` unmodified if `self` is already an `Int::Signed`. + # Raises `OverflowError` in case of overflow. + # + # ``` + # 1_u32.to_signed # => 1_i32 + # 2_u16.to_signed # => 2_i16 + # 3_i64.to_signed # => 3_i64 + # ``` + def to_signed : Int64 + to_i64 + end + + # Returns `self` converted to a signed value of the same size, wrapping in + # case of overflow. + # + # Simply returns `self` unmodified if `self` is already an `Int::Signed`. + # + # ``` + # 1_u32.to_signed! # => 1_i32 + # 65530_u16.to_signed! # => -6_i16 + # 3_i64.to_signed! # => 3_i64 + # ``` + def to_signed! : Int64 + to_i64! + end + + # Returns `self` converted to an unsigned value of the same size. + # + # Simply returns `self` unmodified if `self` is already an `Int::Unsigned`. + # Raises `OverflowError` in case of overflow. + # + # ``` + # 1_i32.to_unsigned # => 1_u32 + # 2_i16.to_unsigned # => 2_u16 + # 3_u64.to_unsigned # => 3_u64 + # ``` + def to_unsigned : UInt64 + self + end + + # Returns `self` converted to an unsigned value of the same size, wrapping in + # case of overflow. + # + # Simply returns `self` unmodified if `self` is already an `Int::Unsigned`. + # + # ``` + # 1_i32.to_unsigned! # => 1_u32 + # (-6_i16).to_unsigned! # => 65530_u16 + # 3_u64.to_unsigned! # => 3_u64 + # ``` + def to_unsigned! : UInt64 + self + end + def abs : self self end @@ -2088,6 +2592,62 @@ struct UInt128 UInt128.new(0) &- self end + # Returns `self` converted to a signed value of the same size. + # + # Simply returns `self` unmodified if `self` is already an `Int::Signed`. + # Raises `OverflowError` in case of overflow. + # + # ``` + # 1_u32.to_signed # => 1_i32 + # 2_u16.to_signed # => 2_i16 + # 3_i64.to_signed # => 3_i64 + # ``` + def to_signed : Int128 + to_i128 + end + + # Returns `self` converted to a signed value of the same size, wrapping in + # case of overflow. + # + # Simply returns `self` unmodified if `self` is already an `Int::Signed`. + # + # ``` + # 1_u32.to_signed! # => 1_i32 + # 65530_u16.to_signed! # => -6_i16 + # 3_i64.to_signed! # => 3_i64 + # ``` + def to_signed! : Int128 + to_i128! + end + + # Returns `self` converted to an unsigned value of the same size. + # + # Simply returns `self` unmodified if `self` is already an `Int::Unsigned`. + # Raises `OverflowError` in case of overflow. + # + # ``` + # 1_i32.to_unsigned # => 1_u32 + # 2_i16.to_unsigned # => 2_u16 + # 3_u64.to_unsigned # => 3_u64 + # ``` + def to_unsigned : UInt128 + self + end + + # Returns `self` converted to an unsigned value of the same size, wrapping in + # case of overflow. + # + # Simply returns `self` unmodified if `self` is already an `Int::Unsigned`. + # + # ``` + # 1_i32.to_unsigned! # => 1_u32 + # (-6_i16).to_unsigned! # => 65530_u16 + # 3_u64.to_unsigned! # => 3_u64 + # ``` + def to_unsigned! : UInt128 + self + end + def abs self end diff --git a/src/kernel.cr b/src/kernel.cr index 7bca29cef605..c3b3106ccae3 100644 --- a/src/kernel.cr +++ b/src/kernel.cr @@ -95,6 +95,13 @@ ARGV = Array.new(ARGC_UNSAFE - 1) { |i| String.new(ARGV_UNSAFE[1 + i]) } # ``` ARGF = IO::ARGF.new(ARGV, STDIN) +# The newline constant +EOL = {% if flag?(:windows) %} + "\r\n" + {% else %} + "\n" + {% end %} + # Repeatedly executes the block. # # ``` diff --git a/src/llvm/ext/llvm_ext.cc b/src/llvm/ext/llvm_ext.cc index ff74958d75b9..0721394e85a1 100644 --- a/src/llvm/ext/llvm_ext.cc +++ b/src/llvm/ext/llvm_ext.cc @@ -1,16 +1,14 @@ #include +#include +#include #define LLVM_VERSION_GE(major, minor) \ (LLVM_VERSION_MAJOR > (major) || LLVM_VERSION_MAJOR == (major) && LLVM_VERSION_MINOR >= (minor)) #include -#include -#include using namespace llvm; -#include - #if LLVM_VERSION_GE(16, 0) #define makeArrayRef ArrayRef #endif @@ -66,75 +64,4 @@ void LLVMExtTargetMachineEnableGlobalIsel(LLVMTargetMachineRef T, LLVMBool Enabl unwrap(T)->setGlobalISel(Enable); } -// Copy paste of https://github.com/llvm/llvm-project/blob/dace8224f38a31636a02fe9c2af742222831f70c/llvm/lib/ExecutionEngine/ExecutionEngineBindings.cpp#L160-L214 -// but with a parameter to set global isel state -LLVMBool LLVMExtCreateMCJITCompilerForModule( - LLVMExecutionEngineRef *OutJIT, LLVMModuleRef M, - LLVMMCJITCompilerOptions *PassedOptions, size_t SizeOfPassedOptions, - LLVMBool EnableGlobalISel, - char **OutError) { - LLVMMCJITCompilerOptions options; - // If the user passed a larger sized options struct, then they were compiled - // against a newer LLVM. Tell them that something is wrong. - if (SizeOfPassedOptions > sizeof(options)) { - *OutError = strdup( - "Refusing to use options struct that is larger than my own; assuming " - "LLVM library mismatch."); - return 1; - } - - - // Defend against the user having an old version of the API by ensuring that - // any fields they didn't see are cleared. We must defend against fields being - // set to the bitwise equivalent of zero, and assume that this means "do the - // default" as if that option hadn't been available. - LLVMInitializeMCJITCompilerOptions(&options, sizeof(options)); - memcpy(&options, PassedOptions, SizeOfPassedOptions); - - - TargetOptions targetOptions; - targetOptions.EnableFastISel = options.EnableFastISel; - targetOptions.EnableGlobalISel = EnableGlobalISel; - std::unique_ptr Mod(unwrap(M)); - - if (Mod) - // Set function attribute "frame-pointer" based on - // NoFramePointerElim. - for (auto &F : *Mod) { - auto Attrs = F.getAttributes(); - StringRef Value = options.NoFramePointerElim ? "all" : "none"; - #if LLVM_VERSION_GE(14, 0) - Attrs = Attrs.addFnAttribute(F.getContext(), "frame-pointer", Value); - #else - Attrs = Attrs.addAttribute(F.getContext(), AttributeList::FunctionIndex, - "frame-pointer", Value); - #endif - F.setAttributes(Attrs); - } - - - std::string Error; - EngineBuilder builder(std::move(Mod)); - builder.setEngineKind(EngineKind::JIT) - .setErrorStr(&Error) - .setOptLevel((CodeGenOpt::Level)options.OptLevel) - .setTargetOptions(targetOptions); - bool JIT; - if (auto CM = unwrap(options.CodeModel, JIT)) - builder.setCodeModel(*CM); - if (options.MCJMM) - builder.setMCJITMemoryManager( - std::unique_ptr(unwrap(options.MCJMM))); - - TargetMachine* tm = builder.selectTarget(); - tm->setGlobalISel(EnableGlobalISel); - - if (ExecutionEngine *JIT = builder.create(tm)) { - *OutJIT = wrap(JIT); - return 0; - } - *OutError = strdup(Error.c_str()); - return 1; -} - } // extern "C" diff --git a/src/llvm/jit_compiler.cr b/src/llvm/jit_compiler.cr index ee0c92803102..3481212e1b93 100644 --- a/src/llvm/jit_compiler.cr +++ b/src/llvm/jit_compiler.cr @@ -5,10 +5,18 @@ class LLVM::JITCompiler mod.take_ownership { raise "Can't create two JIT compilers for the same module" } # if LibLLVM.create_jit_compiler_for_module(out @unwrap, mod, 3, out error) != 0 - if LibLLVMExt.create_mc_jit_compiler_for_module(out @unwrap, mod, nil, 0, false, out error) != 0 + if LibLLVM.create_mc_jit_compiler_for_module(out @unwrap, mod, nil, 0, out error) != 0 raise LLVM.string_and_dispose(error) end + # FIXME: We need to disable global isel until https://reviews.llvm.org/D80898 is released, + # or we fixed generating values for 0 sized types. + # When removing this, also remove it from the ABI specs and Crystal::Codegen::Target. + # See https://github.com/crystal-lang/crystal/issues/9297#issuecomment-636512270 + # for background info + target_machine = LibLLVM.get_execution_engine_target_machine(@unwrap) + LibLLVMExt.target_machine_enable_global_isel(target_machine, false) + @finalized = false end diff --git a/src/llvm/lib_llvm/execution_engine.cr b/src/llvm/lib_llvm/execution_engine.cr index 1c58b5fcd046..f9de5c10ea39 100644 --- a/src/llvm/lib_llvm/execution_engine.cr +++ b/src/llvm/lib_llvm/execution_engine.cr @@ -28,5 +28,6 @@ lib LibLLVM fun create_mc_jit_compiler_for_module = LLVMCreateMCJITCompilerForModule(out_jit : ExecutionEngineRef*, m : ModuleRef, options : MCJITCompilerOptions*, size_of_options : SizeT, out_error : Char**) : Bool fun dispose_execution_engine = LLVMDisposeExecutionEngine(ee : ExecutionEngineRef) fun run_function = LLVMRunFunction(ee : ExecutionEngineRef, f : ValueRef, num_args : UInt, args : GenericValueRef*) : GenericValueRef + fun get_execution_engine_target_machine = LLVMGetExecutionEngineTargetMachine(ee : ExecutionEngineRef) : TargetMachineRef fun get_pointer_to_global = LLVMGetPointerToGlobal(ee : ExecutionEngineRef, global : ValueRef) : Void* end diff --git a/src/llvm/lib_llvm_ext.cr b/src/llvm/lib_llvm_ext.cr index c1a097fd4649..4efb572b6eed 100644 --- a/src/llvm/lib_llvm_ext.cr +++ b/src/llvm/lib_llvm_ext.cr @@ -32,5 +32,4 @@ lib LibLLVMExt name : LibC::Char*) : LibLLVM::ValueRef fun target_machine_enable_global_isel = LLVMExtTargetMachineEnableGlobalIsel(machine : LibLLVM::TargetMachineRef, enable : Bool) - fun create_mc_jit_compiler_for_module = LLVMExtCreateMCJITCompilerForModule(jit : LibLLVM::ExecutionEngineRef*, m : LibLLVM::ModuleRef, options : LibLLVM::MCJITCompilerOptions*, options_length : UInt32, enable_global_isel : Bool, error : UInt8**) : Int32 end