Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support U/Int128 parsing and U/Int128 literals #11111

Closed
wants to merge 22 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
a870fea
Improve Int parsing performance
BlobCodes Aug 14, 2021
0cabca6
Implement String#to_(i/u)128(?) methods
BlobCodes Aug 17, 2021
3a4e377
Add 128-bit modulo/division methods to compiler-rt
BlobCodes Aug 20, 2021
9e9d7d7
Add u/int128 literals parsing
BlobCodes Aug 20, 2021
6d8dd09
Rewrite consume_?_suffix methods for readability
BlobCodes Aug 20, 2021
58ce452
Fix compiler-rt 128bit division/modulo
BlobCodes Aug 20, 2021
89d4d8d
Fix std_specs
BlobCodes Aug 20, 2021
ab7e976
Run crystal tool format
BlobCodes Aug 20, 2021
08b4f82
Mark specs as non-pending that don't have be
BlobCodes Aug 20, 2021
b308515
Add specs for new compiler-rt methods
BlobCodes Aug 20, 2021
2db1e18
Add documentation to divmod128
BlobCodes Aug 21, 2021
d6bfff4
Remove notion of 32-bit systems in divmod128.cr
BlobCodes Aug 21, 2021
7d0be31
Remove _u128_div_rem specs
BlobCodes Aug 21, 2021
a6a3e4a
Improve code style
BlobCodes Aug 21, 2021
0a2680e
Run crystal tool format
BlobCodes Aug 22, 2021
f58b39b
Merge branch 'int128-parsing' of https://github.com/BlobCodes/crystal…
BlobCodes Aug 22, 2021
ffb979b
Merge branch 'master' into int128-parsing
BlobCodes Aug 23, 2021
f8c50f6
implement __mulo?i4 for multiple ints
BlobCodes Aug 23, 2021
5014b88
Merge branch 'int128-parsing' of https://github.com/BlobCodes/crystal…
BlobCodes Aug 23, 2021
daece38
Set _mul_impl macro to private
BlobCodes Aug 23, 2021
0e8e18f
Undo #10975
BlobCodes Aug 24, 2021
a1ef4fc
Merge branch 'crystal-lang:master' into int128-parsing
BlobCodes Sep 9, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 5 additions & 16 deletions spec/compiler/codegen/arithmetics_spec.cr
Original file line number Diff line number Diff line change
@@ -1,21 +1,10 @@
require "../../spec_helper"

{% if flag?(:darwin) %}
SupportedInts = [UInt8, UInt16, UInt32, UInt64, UInt128, Int8, Int16, Int32, Int64, Int128]
SupportedIntsConversions = {
to_i8: Int8, to_i16: Int16, to_i32: Int32, to_i64: Int64, to_i128: Int128,
to_u8: UInt8, to_u16: UInt16, to_u32: UInt32, to_u64: UInt64, to_u128: UInt128,
}
{% else %}
# Skip Int128 and UInt128 on linux platforms due to compiler-rt dependency.
# PreviewOverflowFlags includes compiler_rt flag to support Int64 overflow
# detection in 32 bits platforms.
SupportedInts = [UInt8, UInt16, UInt32, UInt64, Int8, Int16, Int32, Int64]
SupportedIntsConversions = {
to_i8: Int8, to_i16: Int16, to_i32: Int32, to_i64: Int64,
to_u8: UInt8, to_u16: UInt16, to_u32: UInt32, to_u64: UInt64,
}
{% end %}
SupportedInts = [UInt8, UInt16, UInt32, UInt64, UInt128, Int8, Int16, Int32, Int64, Int128]
SupportedIntsConversions = {
to_i8: Int8, to_i16: Int16, to_i32: Int32, to_i64: Int64, to_i128: Int128,
to_u8: UInt8, to_u16: UInt16, to_u32: UInt32, to_u64: UInt64, to_u128: UInt128,
}

describe "Code gen: arithmetic primitives" do
describe "&+ addition" do
Expand Down
20 changes: 10 additions & 10 deletions spec/compiler/lexer/lexer_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -299,16 +299,16 @@ describe "Lexer" do
assert_syntax_error "18446744073709551616_i32", "18446744073709551616 doesn't fit in an Int32"
assert_syntax_error "9999999999999999999_i32", "9999999999999999999 doesn't fit in an Int32"

assert_syntax_error "-9999999999999999999", "-9999999999999999999 doesn't fit in an Int64"
assert_syntax_error "-99999999999999999999", "-99999999999999999999 doesn't fit in an Int64"
assert_syntax_error "-11111111111111111111", "-11111111111111111111 doesn't fit in an Int64"
assert_syntax_error "-9223372036854775809", "-9223372036854775809 doesn't fit in an Int64"
assert_syntax_error "18446744073709551616", "18446744073709551616 doesn't fit in an UInt64"

assert_syntax_error "9223372036854775808_i128", "9223372036854775808 doesn't fit in an Int64. Int128 literals that don't fit in an Int64 are currently not supported"
assert_syntax_error "-9223372036854775809_i128", "-9223372036854775809 doesn't fit in an Int64. Int128 literals that don't fit in an Int64 are currently not supported"
assert_syntax_error "118446744073709551616_u128", "118446744073709551616 doesn't fit in an UInt64. UInt128 literals that don't fit in an UInt64 are currently not supported"
assert_syntax_error "18446744073709551616_u128", "18446744073709551616 doesn't fit in an UInt64. UInt128 literals that don't fit in an UInt64 are currently not supported"
assert_syntax_error "-9999999999999999999_i64", "-9999999999999999999 doesn't fit in an Int64"
assert_syntax_error "-99999999999999999999_i64", "-99999999999999999999 doesn't fit in an Int64"
assert_syntax_error "-11111111111111111111_i64", "-11111111111111111111 doesn't fit in an Int64"
assert_syntax_error "-9223372036854775809_i64", "-9223372036854775809 doesn't fit in an Int64"
assert_syntax_error "18446744073709551616_u64", "18446744073709551616 doesn't fit in an UInt64"

assert_syntax_error "340282366920938463463374607431768211456", "340282366920938463463374607431768211456 doesn't fit in an UInt128"
assert_syntax_error "-170141183460469231731687303715884105729", "-170141183460469231731687303715884105729 doesn't fit in an Int128"
assert_syntax_error "-999999999999999999999999999999999999999", "-999999999999999999999999999999999999999 doesn't fit in an Int128"

assert_syntax_error "-1_u128", "Invalid negative value -1 for UInt128"

assert_syntax_error "0xFF_i8", "255 doesn't fit in an Int8"
Expand Down
79 changes: 79 additions & 0 deletions spec/std/crystal/compiler_rt/divmod128_spec.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
require "spec"

# Specs ported from compiler-rt

private def test__divti3(a : Int128, b : Int128, expected : Int128, file = __FILE__, line = __LINE__)
it "passes compiler-rt builtins unit tests" do
actual = __divti3(a, b)
actual.should eq(expected), file: file, line: line
end
end

private def test__modti3(a : Int128, b : Int128, expected : Int128, file = __FILE__, line = __LINE__)
it "passes compiler-rt builtins unit tests" do
actual = __modti3(a, b)
actual.should eq(expected), file: file, line: line
end
end

private def test__udivti3(a : UInt128, b : UInt128, expected : UInt128, file = __FILE__, line = __LINE__)
it "passes compiler-rt builtins unit tests" do
actual = __udivti3(a, b)
actual.should eq(expected), file: file, line: line
end
end

private def test__umodti3(a : UInt128, b : UInt128, expected : UInt128, file = __FILE__, line = __LINE__)
it "passes compiler-rt builtins unit tests" do
actual = __umodti3(a, b)
actual.should eq(expected), file: file, line: line
end
end

describe "__divti3" do
test__divti3(0_i128, 1_i128, 0_i128)
test__divti3(0_i128, -1_i128, 0_i128)
test__divti3(2_i128, 1_i128, 2_i128)
test__divti3(2_i128, -1_i128, -2_i128)
test__divti3(-2_i128, 1_i128, -2_i128)
test__divti3(-2_i128, -1_i128, 2_i128)
test__divti3(-170141183460469231731687303715884105728_i128, 1_i128, -170141183460469231731687303715884105728)
test__divti3(-170141183460469231731687303715884105728_i128, -1_i128, -170141183460469231731687303715884105728)
test__divti3(-170141183460469231731687303715884105728_i128, -2_i128, 85070591730234615865843651857942052864)
test__divti3(-170141183460469231731687303715884105728_i128, 2_i128, -85070591730234615865843651857942052864)
end

describe "__modti3" do
test__modti3(0_i128, 1_i128, 0_i128)
test__modti3(0_i128, -1_i128, 0_i128)

test__modti3(5_i128, 3_i128, 2_i128)
test__modti3(5_i128, -3_i128, 2_i128)
test__modti3(-5_i128, 3_i128, -2_i128)
test__modti3(-5_i128, -3_i128, -2_i128)

test__modti3(-170141183460469231731687303715884105728_i128, 1_i128, 0_i128)
test__modti3(-170141183460469231731687303715884105728_i128, -1_i128, 0_i128)
test__modti3(-170141183460469231731687303715884105728_i128, 2_i128, 0_i128)
test__modti3(-170141183460469231731687303715884105728_i128, -2_i128, 0_i128)
test__modti3(-170141183460469231731687303715884105728_i128, 3_i128, -2_i128)
test__modti3(-170141183460469231731687303715884105728_i128, -3_i128, -2_i128)
end

describe "__udivti3" do
test__udivti3(0_u128, 1_u128, 0_u128)
test__udivti3(2_u128, 1_u128, 2_u128)

test__udivti3(0x8000000000000000_u128, 1_u128, 0x8000000000000000_u128)
test__udivti3(0x8000000000000000_u128, 2_u128, 0x4000000000000000_u128)
test__udivti3(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF_u128, 2_u128, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF_u128)
end

describe "__umodti3" do
test__umodti3(0_u128, 1_u128, 0_u128)
test__umodti3(2_u128, 1_u128, 0_u128)

test__umodti3(0x8000000000000000_u128, 1_u128, 0_u128)
test__umodti3(0x8000000000000000_u128, 2_u128, 0_u128)
test__umodti3(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF_u128, 2_u128, 1_u128)
end
41 changes: 33 additions & 8 deletions spec/std/int_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,12 @@ describe "Int" do
Int64.new(" 1 ", whitespace: false)
end

Int128.new("1").should be_a(Int128)
Int128.new("1").should eq(1)
expect_raises ArgumentError do
Int128.new(" 1 ", whitespace: false)
end

UInt8.new("1").should be_a(UInt8)
UInt8.new("1").should eq(1)
expect_raises ArgumentError do
Expand All @@ -453,6 +459,12 @@ describe "Int" do
expect_raises ArgumentError do
UInt64.new(" 1 ", whitespace: false)
end

UInt128.new("1").should be_a(UInt128)
UInt128.new("1").should eq(1)
expect_raises ArgumentError do
UInt128.new(" 1 ", whitespace: false)
end
end

it "fallback overload" do
Expand All @@ -468,6 +480,9 @@ describe "Int" do
Int64.new(1).should be_a(Int64)
Int64.new(1).should eq(1)

Int128.new(1).should be_a(Int128)
Int128.new(1).should eq(1)

UInt8.new(1).should be_a(UInt8)
UInt8.new(1).should eq(1)

Expand All @@ -479,6 +494,9 @@ describe "Int" do

UInt64.new(1).should be_a(UInt64)
UInt64.new(1).should eq(1)

UInt128.new(1).should be_a(UInt128)
UInt128.new(1).should eq(1)
end
end

Expand All @@ -504,14 +522,15 @@ describe "Int" do
(Int16::MIN / -1).should eq(-(Int16::MIN.to_f64))
(Int32::MIN / -1).should eq(-(Int32::MIN.to_f64))
(Int64::MIN / -1).should eq(-(Int64::MIN.to_f64))
(Int128::MIN / -1).should eq(-(Int128::MIN.to_f64))

(UInt8::MIN / -1).should eq(0)
end
end

describe "floor division //" do
it "preserves type of lhs" do
{% for type in [UInt8, UInt16, UInt32, UInt64, Int8, Int16, Int32, Int64] %}
{% for type in [UInt8, UInt16, UInt32, UInt64, UInt128, Int8, Int16, Int32, Int64, Int128] %}
({{type}}.new(7) // 2).should be_a({{type}})
({{type}}.new(7) // 2.0).should be_a({{type}})
({{type}}.new(7) // 2.0_f32).should be_a({{type}})
Expand Down Expand Up @@ -558,6 +577,7 @@ describe "Int" do
expect_raises(ArgumentError) { Int16::MIN // -1 }
expect_raises(ArgumentError) { Int32::MIN // -1 }
expect_raises(ArgumentError) { Int64::MIN // -1 }
expect_raises(ArgumentError) { Int128::MIN // -1 }

(UInt8::MIN // -1).should eq(0)
end
Expand All @@ -583,7 +603,7 @@ describe "Int" do
end

it "returns 0 when doing IntN::MIN % -1 (#8306)" do
{% for n in [8, 16, 32, 64] %}
{% for n in [8, 16, 32, 64, 128] %}
(Int{{n}}::MIN % -1_i{{n}}).should eq(0)
{% end %}
end
Expand All @@ -597,7 +617,7 @@ describe "Int" do
end

it "returns 0 when doing IntN::MIN.remainder(-1) (#8306)" do
{% for n in [8, 16, 32, 64] %}
{% for n in [8, 16, 32, 64, 128] %}
(Int{{n}}::MIN.remainder(-1_i{{n}})).should eq(0)
{% end %}
end
Expand Down Expand Up @@ -734,27 +754,31 @@ describe "Int" do
it { 5_i64.popcount.should eq(2) }
it { 9223372036854775807_i64.popcount.should eq(63) }
it { 18446744073709551615_u64.popcount.should eq(64) }

it { 0_i128.popcount.should eq(0) }
it { 170141183460469231731687303715884105727_i128.popcount.should eq(127) }
it { 340282366920938463463374607431768211455_u128.popcount.should eq(128) }
end

describe "#leading_zeros_count" do
{% for width in %w(8 16 32 64).map(&.id) %}
{% for width in %w(8 16 32 64 128).map(&.id) %}
it { -1_i{{width}}.leading_zeros_count.should eq(0) }
it { 0_i{{width}}.leading_zeros_count.should eq({{width}}) }
it { 0_u{{width}}.leading_zeros_count.should eq({{width}}) }
{% end %}
end

describe "#trailing_zeros_count" do
{% for width in %w(8 16 32 64).map(&.id) %}
{% for width in %w(8 16 32 64 128).map(&.id) %}
it { -2_i{{width}}.trailing_zeros_count.should eq(1) }
it { 2_i{{width}}.trailing_zeros_count.should eq(1) }
it { 2_u{{width}}.trailing_zeros_count.should eq(1) }
{% end %}
end

pending_win32 "compares signed vs. unsigned integers" do
signed_ints = [Int8::MAX, Int16::MAX, Int32::MAX, Int64::MAX, Int8::MIN, Int16::MIN, Int32::MIN, Int64::MIN, 0_i8, 0_i16, 0_i32, 0_i64]
unsigned_ints = [UInt8::MAX, UInt16::MAX, UInt32::MAX, UInt64::MAX, 0_u8, 0_u16, 0_u32, 0_u64]
signed_ints = [Int8::MAX, Int16::MAX, Int32::MAX, Int64::MAX, Int128::MAX, Int8::MIN, Int16::MIN, Int32::MIN, Int64::MIN, Int128::MIN, 0_i8, 0_i16, 0_i32, 0_i64, 0_i128]
unsigned_ints = [UInt8::MAX, UInt16::MAX, UInt32::MAX, UInt64::MAX, UInt128::MAX, 0_u8, 0_u16, 0_u32, 0_u64, 0_u128]

big_signed_ints = signed_ints.map &.to_big_i
big_unsigned_ints = unsigned_ints.map &.to_big_i
Expand All @@ -781,7 +805,7 @@ describe "Int" do
end

it "clones" do
[1_u8, 2_u16, 3_u32, 4_u64, 5_i8, 6_i16, 7_i32, 8_i64].each do |value|
[1_u8, 2_u16, 3_u32, 4_u64, 5_u128, 6_i8, 7_i16, 8_i32, 9_i64, 10_i128].each do |value|
value.clone.should eq(value)
end
end
Expand Down Expand Up @@ -839,6 +863,7 @@ describe "Int" do
Int32::MAX.digits.should eq(Int32::MAX.to_s.chars.map(&.to_i).reverse)
Int64::MAX.digits.should eq(Int64::MAX.to_s.chars.map(&.to_i).reverse)
UInt64::MAX.digits.should eq(UInt64::MAX.to_s.chars.map(&.to_i).reverse)
UInt128::MAX.digits.should eq(UInt128::MAX.to_s.chars.map(&.to_i).reverse)
end

it "works for non-Int32" do
Expand Down
24 changes: 24 additions & 0 deletions spec/std/string_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,30 @@ describe "String" do
it { "18446744073709551616".to_u64 { 0 }.should eq(0) }
end

describe "to_i128" do
it { "170141183460469231731687303715884105727".to_i128.should eq(Int128::MAX) }
it { "-170141183460469231731687303715884105728".to_i128.should eq(Int128::MIN) }
it { expect_raises(ArgumentError) { "170141183460469231731687303715884105728".to_i128 } }
it { expect_raises(ArgumentError) { "-170141183460469231731687303715884105729".to_i128 } }

it { "170141183460469231731687303715884105727".to_i128?.should eq(Int128::MAX) }
it { "170141183460469231731687303715884105728".to_i128?.should be_nil }
it { "170141183460469231731687303715884105728".to_i128 { 0 }.should eq(0) }

it { expect_raises(ArgumentError) { "340282366920938463463374607431768211456".to_i128 } }
end

describe "to_u128" do
it { "340282366920938463463374607431768211455".to_u128.should eq(UInt128::MAX) }
it { "0".to_u128.should eq(0) }
it { expect_raises(ArgumentError) { "340282366920938463463374607431768211456".to_u128 } }
it { expect_raises(ArgumentError) { "-1".to_u128 } }

it { "340282366920938463463374607431768211455".to_u128?.should eq(UInt128::MAX) }
it { "340282366920938463463374607431768211456".to_u128?.should be_nil }
it { "340282366920938463463374607431768211456".to_u128 { 0 }.should eq(0) }
end

it { "1234".to_i32.should eq(1234) }
it { "1234123412341234".to_i64.should eq(1234123412341234_i64) }
it { "9223372036854775808".to_u64.should eq(9223372036854775808_u64) }
Expand Down
6 changes: 2 additions & 4 deletions src/compiler/crystal/codegen/codegen.cr
Original file line number Diff line number Diff line change
Expand Up @@ -475,11 +475,9 @@ module Crystal
when :u64
@last = int64(node.value.to_u64)
when :i128
# TODO: implement String#to_i128 and use it
@last = int128(node.value.to_i64)
@last = int128(node.value.to_i128)
when :u128
# TODO: implement String#to_u128 and use it
@last = int128(node.value.to_u64)
@last = int128(node.value.to_u128)
when :f32
@last = float32(node.value)
when :f64
Expand Down
22 changes: 12 additions & 10 deletions src/compiler/crystal/codegen/const.cr
Original file line number Diff line number Diff line change
Expand Up @@ -194,16 +194,18 @@ class Crystal::CodeGenVisitor
# We inline constants. Otherwise we use an LLVM const global.
@last =
case value = const.compile_time_value
when Bool then int1(value ? 1 : 0)
when Char then int32(value.ord)
when Int8 then int8(value)
when Int16 then int16(value)
when Int32 then int32(value)
when Int64 then int64(value)
when UInt8 then int8(value)
when UInt16 then int16(value)
when UInt32 then int32(value)
when UInt64 then int64(value)
when Bool then int1(value ? 1 : 0)
when Char then int32(value.ord)
when Int8 then int8(value)
when Int16 then int16(value)
when Int32 then int32(value)
when Int64 then int64(value)
when Int128 then int128(value)
when UInt8 then int8(value)
when UInt16 then int16(value)
when UInt32 then int32(value)
when UInt64 then int64(value)
when UInt128 then int128(value)
else
last = read_const_pointer(const)
to_lhs last, const.value.type
Expand Down
18 changes: 10 additions & 8 deletions src/compiler/crystal/syntax/ast.cr
Original file line number Diff line number Diff line change
Expand Up @@ -249,14 +249,16 @@ module Crystal

def integer_value
case kind
when :i8 then value.to_i8
when :i16 then value.to_i16
when :i32 then value.to_i32
when :i64 then value.to_i64
when :u8 then value.to_u8
when :u16 then value.to_u16
when :u32 then value.to_u32
when :u64 then value.to_u64
when :i8 then value.to_i8
when :i16 then value.to_i16
when :i32 then value.to_i32
when :i64 then value.to_i64
when :i128 then value.to_i128
when :u8 then value.to_u8
when :u16 then value.to_u16
when :u32 then value.to_u32
when :u64 then value.to_u64
when :u128 then value.to_u128
else
raise "Bug: called 'integer_value' for non-integer literal"
end
Expand Down
Loading