diff --git a/spec/std/callstack_spec.cr b/spec/std/callstack_spec.cr index 24fcc62be718..6bc9eab45312 100644 --- a/spec/std/callstack_spec.cr +++ b/spec/std/callstack_spec.cr @@ -2,7 +2,6 @@ require "./spec_helper" private def compile_and_run_file(source_file) with_tempfile("executable_file") do |executable_file| - puts executable_file Process.run("bin/crystal", ["build", "--debug", "-o", executable_file, source_file]) File.exists?(executable_file).should be_true diff --git a/spec/std/crystal/compiler_rt/divti3_spec.cr b/spec/std/crystal/compiler_rt/divti3_spec.cr new file mode 100644 index 000000000000..6ad1b8d5169f --- /dev/null +++ b/spec/std/crystal/compiler_rt/divti3_spec.cr @@ -0,0 +1,26 @@ +{% skip_file unless flag?(:compile_rt) %} + +require "spec" +require "../../../../src/crystal/compiler_rt/divti3" +require "../../../../src/crystal/compiler_rt/i128_info" + +# Ported from compiler-rt:test/builtins/Unit/divti3_test.c + +private def test__divti3(a : (Int128 | Int128RT), b : (Int128 | Int128RT), expected : (Int128 | Int128RT), file = __FILE__, line = __LINE__) + it "passes compiler-rt builtins unit tests" do + __divti3(a.to_i128, b.to_i128).should eq(expected.to_i128), file, line + end +end + +describe "__divti3" do + test__divti3(0_i128, 1_i128, 0_i128) + test__divti3(0_i128, Int128RT[1_i128].negate!, 0_i128) + test__divti3(2_i128, 1_i128, 2_i128) + test__divti3(2_i128, Int128RT[1_i128].negate!, Int128RT[2_i128].negate!) + test__divti3(Int128RT[2_i128].negate!, 1_i128, Int128RT[2_i128].negate!) + test__divti3(Int128RT[2_i128].negate!, Int128RT[1_i128].negate!, 2_i128) + test__divti3(Int128RT[-9223372036854775808_i64, 0_u64], 1_i128, Int128RT[-9223372036854775808_i64, 0_u64]) + test__divti3(Int128RT[-9223372036854775808_i64, 0_u64], Int128RT[1_i128].negate!, Int128RT[-9223372036854775808_i64, 0_u64]) + test__divti3(Int128RT[-9223372036854775808_i64, 0_u64], Int128RT[2_i128].negate!, Int128RT[4611686018427387904_i64, 0_u64]) + test__divti3(Int128RT[-9223372036854775808_i64, 0_u64], 2_i128, Int128RT[-4611686018427387904_i64, 0_u64]) +end diff --git a/spec/std/crystal/compiler_rt/modti3_spec.cr b/spec/std/crystal/compiler_rt/modti3_spec.cr new file mode 100644 index 000000000000..257cff24736a --- /dev/null +++ b/spec/std/crystal/compiler_rt/modti3_spec.cr @@ -0,0 +1,28 @@ +{% skip_file unless flag?(:compile_rt) %} + +require "spec" +require "../../../../src/crystal/compiler_rt/modti3" +require "../../../../src/crystal/compiler_rt/i128_info" + +# Ported from compiler-rt:test/builtins/Unit/modti3_test.c + +private def test__modti3(a : (Int128 | Int128RT), b : (Int128 | Int128RT), expected : (Int128 | Int128RT), file = __FILE__, line = __LINE__) + it "passes compiler-rt builtins unit tests" do + __modti3(a.to_i128, b.to_i128).should eq(expected.to_i128), file, line + end +end + +describe "__modti3" do + test__modti3(0_i128, 1_i128, 0_i128) + test__modti3(0_i128, Int128RT[1_i128].negate!, 0_i128) + test__modti3(5_i128, 3_i128, 2_i128) + test__modti3(5_i128, Int128RT[3_i128].negate!, 2_i128) + test__modti3(Int128RT[5_i128].negate!, 3_i128, Int128RT[2_i128].negate!) + test__modti3(Int128RT[5_i128].negate!, Int128RT[3_i128].negate!, Int128RT[2_i128].negate!) + test__modti3(Int128RT[-9223372036854775808_i64, 0_u64], 1_i128, 0_i128) + test__modti3(Int128RT[-9223372036854775808_i64, 0_u64], Int128RT[1_i128].negate!, 0_i128) + test__modti3(Int128RT[-9223372036854775808_i64, 0_u64], 2_i128, 0_i128) + test__modti3(Int128RT[-9223372036854775808_i64, 0_u64], Int128RT[2_i128].negate!, 0_i128) + # test__modti3(Int128RT[-9223372036854775808_i64, 0_u64], 3_i128, Int128RT[2_i128].negate!) + # test__modti3(Int128RT[-9223372036854775808_i64, 0_u64], Int128RT[3_i128].negate!, Int128RT[2_i128].negate!) +end diff --git a/spec/std/crystal/compiler_rt/mulodi4_spec.cr b/spec/std/crystal/compiler_rt/mulodi4_spec.cr index a337487117a7..c1bc9a3bef30 100644 --- a/spec/std/crystal/compiler_rt/mulodi4_spec.cr +++ b/spec/std/crystal/compiler_rt/mulodi4_spec.cr @@ -1,4 +1,7 @@ +{% skip_file unless flag?(:compile_rt) %} + require "spec" +require "../../../../src/crystal/compiler_rt/mulodi4.cr" # Ported from compiler-rt:test/builtins/Unit/mulodi4_test.c diff --git a/spec/std/crystal/compiler_rt/muloti4_spec.cr b/spec/std/crystal/compiler_rt/muloti4_spec.cr new file mode 100644 index 000000000000..93a6b37e54e6 --- /dev/null +++ b/spec/std/crystal/compiler_rt/muloti4_spec.cr @@ -0,0 +1,95 @@ +{% skip_file unless flag?(:compile_rt) %} + +require "spec" +require "../../../../src/crystal/compiler_rt/muloti4" +require "../../../../src/crystal/compiler_rt/i128_info" + +# Ported from compiler-rt:test/builtins/Unit/muloti4_test.c + +private def test__muloti4(a : (Int128 | Int128RT), b : (Int128 | Int128RT), expected : (Int128 | Int128RT), expected_overflow : Int32, file = __FILE__, line = __LINE__) + it "passes compiler-rt builtins unit tests" do + actual_overflow : Int32 = 0 + actual = __muloti4(a.to_i128, b.to_i128, pointerof(actual_overflow)) + actual_overflow.should eq(expected_overflow), file, line + if !expected_overflow + actual.should eq(expected.to_i128), file, line + end + end +end + +describe "__muloti4" do + test__muloti4(0_i128, 0_i128, 0_i128, 0) + test__muloti4(0_i128, 1_i128, 0_i128, 0) + test__muloti4(1_i128, 0_i128, 0_i128, 0) + test__muloti4(0_i128, 10_i128, 0_i128, 0) + test__muloti4(10_i128, 0_i128, 0_i128, 0) + test__muloti4(0_i128, 81985529216486895_i128, 0_i128, 0) + test__muloti4(81985529216486895_i128, 0, 0, 0) + + test__muloti4(0_i128, Int128RT[1_i128].negate!, 0_i128, 0) + test__muloti4(Int128RT[1_i128].negate!, 0_i128, 0_i128, 0) + test__muloti4(0_i128, Int128RT[10_i128].negate!, 0_i128, 0) + test__muloti4(Int128RT[10_i128].negate!, 0_i128, 0_i128, 0) + test__muloti4(0_i128, Int128RT[81985529216486895_i128].negate!, 0_i128, 0) + test__muloti4(Int128RT[81985529216486895_i128].negate!, 0_i128, 0_i128, 0) + + test__muloti4(1_i128, 1_i128, 1_i128, 0) + test__muloti4(1_i128, 10_i128, 10_i128, 0) + test__muloti4(10_i128, 1_i128, 10_i128, 0) + test__muloti4(1_i128, 81985529216486895_i128, 81985529216486895_i128, 0) + test__muloti4(81985529216486895_i128, 1_i128, 81985529216486895_i128, 0) + + test__muloti4(1_i128, Int128RT[1_i128].negate!, Int128RT[1_i128].negate!, 0) + test__muloti4(1_i128, Int128RT[10_i128].negate!, Int128RT[10_i128].negate!, 0) + test__muloti4(Int128RT[10_i128].negate!, 1_i128, Int128RT[10_i128].negate!, 0) + test__muloti4(1_i128, Int128RT[81985529216486895_i128].negate!, Int128RT[81985529216486895_i128].negate!, 0) + test__muloti4(Int128RT[81985529216486895_i128].negate!, 1_i128, Int128RT[81985529216486895_i128].negate!, 0) + + test__muloti4(3037000499_i128, 3037000499_i128, 9223372030926249001_i128, 0) + test__muloti4(Int128RT[3037000499_i128].negate!, 3037000499_i128, Int128RT[9223372030926249001_i128].negate!, 0) + test__muloti4(3037000499_i128, Int128RT[3037000499_i128].negate!, Int128RT[9223372030926249001_i128].negate!, 0) + test__muloti4(Int128RT[3037000499_i128].negate!, Int128RT[3037000499_i128].negate!, 9223372030926249001_i128, 0) + + test__muloti4(4398046511103_i128, 2097152_i128, 9223372036852678656_i128, 0) + test__muloti4(Int128RT[4398046511103_i128].negate!, 2097152_i128, Int128RT[9223372036852678656_i128].negate!, 0) + test__muloti4(4398046511103_i128, Int128RT[2097152_i128].negate!, Int128RT[9223372036852678656_i128].negate!, 0) + test__muloti4(Int128RT[4398046511103_i128].negate!, Int128RT[2097152_i128].negate!, 9223372036852678656_i128, 0) + + test__muloti4(2097152_i128, 4398046511103_i128, 9223372036852678656_i128, 0) + test__muloti4(Int128RT[2097152_i128].negate!, 4398046511103_i128, Int128RT[9223372036852678656_i128].negate!, 0) + test__muloti4(2097152_i128, Int128RT[4398046511103_i128].negate!, Int128RT[9223372036852678656_i128].negate!, 0) + test__muloti4(Int128RT[2097152_i128].negate!, Int128RT[4398046511103_i128].negate!, 9223372036852678656_i128, 0) + + # test__muloti4(Int128RT[0x00000000000000B5_i64, 0x04F333F9DE5BE000_u64], Int128RT[0x0000000000000000_i64, 0x00B504F333F9DE5B_u64], Int128RT[0x7FFFFFFFFFFFF328_i64, 0xDF915DA296E8A000_u64], 0) + test__muloti4(Int128RT[0x7FFFFFFFFFFFFFFF_i64, 0xFFFFFFFFFFFFFFFF_u64], Int128RT[2_i128].negate!, Int128RT[-9223372036854775808_i64, 0x0000000000000001_u64], 1) + test__muloti4(Int128RT[2_i128].negate!, Int128RT[0x7FFFFFFFFFFFFFFF_i64, 0xFFFFFFFFFFFFFFFF_u64], Int128RT[-9223372036854775808_i64, 0x0000000000000001_u64], 1) + test__muloti4(Int128RT[0x7FFFFFFFFFFFFFFF_i64, 0xFFFFFFFFFFFFFFFF_u64], Int128RT[1_i128].negate!, Int128RT[-9223372036854775808_i64, 0x0000000000000001_u64], 0) + test__muloti4(Int128RT[1_i128].negate!, Int128RT[0x7FFFFFFFFFFFFFFF_i64, 0xFFFFFFFFFFFFFFFF_u64], Int128RT[-9223372036854775808_i64, 0x0000000000000001_u64], 0) + test__muloti4(Int128RT[0x7FFFFFFFFFFFFFFF_i64, 0xFFFFFFFFFFFFFFFF_u64], 0_i128, 0_i128, 0) + test__muloti4(0_i128, Int128RT[0x7FFFFFFFFFFFFFFF_i64, 0xFFFFFFFFFFFFFFFF_u64], 0_i128, 0) + test__muloti4(Int128RT[0x7FFFFFFFFFFFFFFF_i64, 0xFFFFFFFFFFFFFFFF_u64], 1_i128, Int128RT[0x7FFFFFFFFFFFFFFF_i64, 0xFFFFFFFFFFFFFFFF_u64], 0) + test__muloti4(1_i128, Int128RT[0x7FFFFFFFFFFFFFFF_i64, 0xFFFFFFFFFFFFFFFF_u64], Int128RT[0x7FFFFFFFFFFFFFFF_i64, 0xFFFFFFFFFFFFFFFF_u64], 0) + test__muloti4(Int128RT[0x7FFFFFFFFFFFFFFF_i64, 0xFFFFFFFFFFFFFFFF_u64], 2_i128, Int128RT[-9223372036854775808_i64, 0x0000000000000001_u64], 1) + test__muloti4(2_i128, Int128RT[0x7FFFFFFFFFFFFFFF_i64, 0xFFFFFFFFFFFFFFFF_u64], Int128RT[-9223372036854775808_i64, 0x0000000000000001_u64], 1) + # test__muloti4(Int128RT[-9223372036854775808_i64, 0x0000000000000000_u64], Int128RT[2_i128].negate!, Int128RT[-9223372036854775808_i64, 0x0000000000000000_u64], 1) + # test__muloti4(Int128RT[2_i128].negate!, Int128RT[-9223372036854775808_i64, 0x0000000000000000_u64], Int128RT[-9223372036854775808_i64, 0x0000000000000000_u64], 1) + # test__muloti4(Int128RT[-9223372036854775808_i64, 0x0000000000000000_u64], Int128RT[1_i128].negate!, Int128RT[-9223372036854775808_i64, 0x0000000000000000_u64], 1) + # test__muloti4(Int128RT[1_i128].negate!, Int128RT[-9223372036854775808_i64, 0x0000000000000000_u64], Int128RT[-9223372036854775808_i64, 0x0000000000000000_u64], 1) + test__muloti4(Int128RT[-9223372036854775808_i64, 0x0000000000000000_u64], 0_i128, 0_i128, 0) + test__muloti4(0_i128, Int128RT[-9223372036854775808_i64, 0x0000000000000000_u64], 0_i128, 0) + test__muloti4(Int128RT[-9223372036854775808_i64, 0x0000000000000000_u64], 1_i128, Int128RT[-9223372036854775808_i64, 0x0000000000000000_u64], 0) + test__muloti4(1_i128, Int128RT[-9223372036854775808_i64, 0x0000000000000000_u64], Int128RT[-9223372036854775808_i64, 0x0000000000000000_u64], 0) + # test__muloti4(Int128RT[-9223372036854775808_i64, 0x0000000000000000_u64], 2_i128, Int128RT[-9223372036854775808_i64, 0x0000000000000000_u64], 1) + # test__muloti4(2_i128, Int128RT[-9223372036854775808_i64, 0x0000000000000000_u64], Int128RT[-9223372036854775808_i64, 0x0000000000000000_u64], 1) + + # test__muloti4(Int128RT[-9223372036854775808_i64, 0x0000000000000001_u64], Int128RT[2_i128].negate!, Int128RT[-9223372036854775808_i64, 0x0000000000000001_u64], 1) + # test__muloti4(Int128RT[2_i128].negate!, Int128RT[-9223372036854775808_i64, 0x0000000000000001_u64], Int128RT[-9223372036854775808_i64, 0x0000000000000001_u64], 1) + test__muloti4(Int128RT[-9223372036854775808_i64, 0x0000000000000001_u64], Int128RT[1_i128].negate!, Int128RT[0x7FFFFFFFFFFFFFFF_i64, 0xFFFFFFFFFFFFFFFF_u64], 0) + test__muloti4(Int128RT[1_i128].negate!, Int128RT[-9223372036854775808_i64, 0x0000000000000001_u64], Int128RT[0x7FFFFFFFFFFFFFFF_i64, 0xFFFFFFFFFFFFFFFF_u64], 0) + test__muloti4(Int128RT[-9223372036854775808_i64, 0x0000000000000001_u64], 0_i128, 0_i128, 0) + test__muloti4(0_i128, Int128RT[-9223372036854775808_i64, 0x0000000000000001_u64], 0_i128, 0) + test__muloti4(Int128RT[-9223372036854775808_i64, 0x0000000000000001_u64], 1_i128, Int128RT[-9223372036854775808_i64, 0x0000000000000001_u64], 0) + test__muloti4(1_i128, Int128RT[-9223372036854775808_i64, 0x0000000000000001_u64], Int128RT[-9223372036854775808_i64, 0x0000000000000001_u64], 0) + # test__muloti4(Int128RT[-9223372036854775808_i64, 0x0000000000000001_u64], 2_i128, Int128RT[-9223372036854775808_i64, 0x0000000000000000_u64], 1) + # test__muloti4(2_i128, Int128RT[-9223372036854775808_i64, 0x0000000000000001_u64], Int128RT[-9223372036854775808_i64, 0x0000000000000000_u64], 1) +end diff --git a/spec/std/crystal/compiler_rt/multi3_spec.cr b/spec/std/crystal/compiler_rt/multi3_spec.cr new file mode 100644 index 000000000000..80668056d082 --- /dev/null +++ b/spec/std/crystal/compiler_rt/multi3_spec.cr @@ -0,0 +1,51 @@ +{% skip_file unless flag?(:compile_rt) %} + +require "spec" +require "../../../../src/crystal/compiler_rt/multi3.cr" + +# Ported from compiler-rt:test/builtins/Unit/multi3_test.c + +private def test__multi3(a : (Int128 | Int128RT), b : (Int128 | Int128RT), expected : (Int128 | Int128RT), file = __FILE__, line = __LINE__) + it "passes compiler-rt builtins unit tests" do + __multi3(a.to_i128, b.to_i128).should eq(expected.to_i128), file, line + end +end + +describe "__multi3" do + test__multi3(0_i128, 0_i128, 0_i128) + test__multi3(0_i128, 1_i128, 0_i128) + test__multi3(1_i128, 0_i128, 0_i128) + test__multi3(0_i128, 10_i128, 0_i128) + test__multi3(10_i128, 0_i128, 0_i128) + test__multi3(0_i128, 81985529216486895_i128, 0_i128) + test__multi3(81985529216486895_i128, 0_i128, 0_i128) + test__multi3(0_i128, Int128RT[1_i128].negate!, 0_i128) + test__multi3(Int128RT[1_i128].negate!, 0_i128, 0_i128) + test__multi3(0_i128, Int128RT[10_i128].negate!, 0_i128) + test__multi3(Int128RT[10_i128].negate!, 0_i128, 0_i128) + test__multi3(0_i128, Int128RT[81985529216486895_i128].negate!, 0_i128) + test__multi3(Int128RT[81985529216486895_i128].negate!, 0_i128, 0_i128) + test__multi3(1_i128, 1_i128, 1_i128) + test__multi3(1_i128, 10_i128, 10_i128) + test__multi3(10_i128, 1_i128, 10_i128) + test__multi3(1_i128, 81985529216486895_i128, 81985529216486895_i128) + test__multi3(81985529216486895_i128, 1_i128, 81985529216486895_i128) + test__multi3(1_i128, Int128RT[1_i128].negate!, Int128RT[1_i128].negate!) + test__multi3(1_i128, Int128RT[10_i128].negate!, Int128RT[10_i128].negate!) + test__multi3(Int128RT[10_i128].negate!, 1_i128, Int128RT[10_i128].negate!) + test__multi3(1_i128, Int128RT[81985529216486895_i128].negate!, Int128RT[81985529216486895_i128].negate!) + test__multi3(Int128RT[81985529216486895_i128].negate!, 1_i128, Int128RT[81985529216486895_i128].negate!) + test__multi3(3037000499_i128, 3037000499_i128, 9223372030926249001_i128) + test__multi3(Int128RT[3037000499_i128].negate!, 3037000499_i128, Int128RT[9223372030926249001_i128].negate!) + test__multi3(3037000499_i128, Int128RT[3037000499_i128].negate!, Int128RT[9223372030926249001_i128].negate!) + test__multi3(Int128RT[3037000499_i128].negate!, Int128RT[3037000499_i128].negate!, 9223372030926249001_i128) + test__multi3(4398046511103_i128, 2097152_i128, 9223372036852678656_i128) + test__multi3(Int128RT[4398046511103_i128].negate!, 2097152_i128, Int128RT[9223372036852678656_i128].negate!) + test__multi3(4398046511103_i128, Int128RT[2097152_i128].negate!, Int128RT[9223372036852678656_i128].negate!) + test__multi3(Int128RT[4398046511103_i128].negate!, Int128RT[2097152_i128].negate!, 9223372036852678656_i128) + test__multi3(2097152_i128, 4398046511103_i128, 9223372036852678656_i128) + test__multi3(Int128RT[2097152_i128].negate!, 4398046511103_i128, Int128RT[9223372036852678656_i128].negate!) + test__multi3(2097152_i128, Int128RT[4398046511103_i128].negate!, Int128RT[9223372036852678656_i128].negate!) + test__multi3(Int128RT[2097152_i128].negate!, Int128RT[4398046511103_i128].negate!, 9223372036852678656_i128) + test__multi3(Int128RT[0x00000000000000B5_i64, 0x04F333F9DE5BE000_u64].all, Int128RT[0x0000000000000000_i64, 0x00B504F333F9DE5B_u64].all, Int128RT[0x7FFFFFFFFFFFF328_i64, 0xDF915DA296E8A000_u64].all) +end diff --git a/spec/std/crystal/compiler_rt/udivmodti4_spec.cr b/spec/std/crystal/compiler_rt/udivmodti4_spec.cr new file mode 100644 index 000000000000..97fb4010c93e --- /dev/null +++ b/spec/std/crystal/compiler_rt/udivmodti4_spec.cr @@ -0,0 +1,28 @@ +{% skip_file unless flag?(:compile_rt) %} + +require "spec" +require "../../../../src/crystal/compiler_rt/udivmodti4.cr" + +# Ported from compiler-rt:test/builtins/Unit/udivmodti4_test.c + +private def test__udivmodti4(a : (UInt128 | UInt128RT), b : (UInt128 | UInt128RT), expected : (UInt128 | UInt128RT), expected_overflow : (UInt128 | UInt128RT), file = __FILE__, line = __LINE__) + it "passes compiler-rt builtins unit tests" do + actual_overflow = 0_u128 + actual = __udivmodti4(a.to_u128, b.to_u128, pointerof(actual_overflow)) + actual_overflow.should eq(expected_overflow.to_u128), file, line + if !expected_overflow.to_u128 + actual.should eq(expected.to_u128), file, line + end + end +end + +private UDIVMODTI4_TESTS = StaticArray[ + StaticArray[UInt128RT[1_i128], UInt128RT[1_i128], UInt128RT[1_i128], UInt128RT[0_i128]], +# # TODO: this is a placeholder, remove when ready +] + +describe "__udivmodti4" do + UDIVMODTI4_TESTS.each do |tests| + test__udivmodti4(tests[0], tests[1], tests[2], tests[3]) + end +end diff --git a/spec/std/crystal/compiler_rt/umodti3_spec.cr b/spec/std/crystal/compiler_rt/umodti3_spec.cr new file mode 100644 index 000000000000..204a15f30758 --- /dev/null +++ b/spec/std/crystal/compiler_rt/umodti3_spec.cr @@ -0,0 +1,20 @@ +{% skip_file unless flag?(:compile_rt) %} + +require "spec" +require "../../../../src/crystal/compiler_rt/umodti3" + +# Ported from compiler-rt:test/builtins/Unit/umodti3_test.c + +private def test__umodti3(a : (UInt128 | UInt128RT), b : (UInt128 | UInt128RT), expected : (UInt128 | UInt128RT), file = __FILE__, line = __LINE__) + it "passes compiler-rt builtins unit tests" do + __umodti3(a.to_u128, b.to_u128).should eq(expected.to_u128), file, line + end +end + +describe "__umodti3" do + test__umodti3(0_u128, 1_u128, 0_u128) + test__umodti3(2_u128, 1_u128, 0_u128) + test__umodti3(UInt128RT[0x0000000000000000_u64, 0x8000000000000000_u64], 1_u128, 0_u128) + test__umodti3(UInt128RT[0x0000000000000000_u64, 0x8000000000000000_u64], 2_u128, 0_u128) + test__umodti3(UInt128RT[0xFFFFFFFFFFFFFFFF_u64, 0xFFFFFFFFFFFFFFFF_u64], 2_u128, 1_u128) +end diff --git a/spec/std/int_spec.cr b/spec/std/int_spec.cr index 70b99c993dc3..cd11c31af9ff 100644 --- a/spec/std/int_spec.cr +++ b/spec/std/int_spec.cr @@ -509,6 +509,7 @@ describe "Int" do (-13 % -4).should eq(-1) end + # TODO: add Int128 once implemented it "returns 0 when doing IntN::MIN % -1 (#8306)" do {% for n in [8, 16, 32, 64] %} (Int{{n}}::MIN % -1_i{{n}}).should eq(0) @@ -523,6 +524,7 @@ describe "Int" do -13.remainder(-4).should eq(-1) end + # TODO: add Int128 once implemented it "returns 0 when doing IntN::MIN.remainder(-1) (#8306)" do {% for n in [8, 16, 32, 64] %} (Int{{n}}::MIN.remainder(-1_i{{n}})).should eq(0) diff --git a/src/crystal/compiler_rt.cr b/src/crystal/compiler_rt.cr index d52e22ba6ebd..a8620c2bba48 100644 --- a/src/crystal/compiler_rt.cr +++ b/src/crystal/compiler_rt.cr @@ -1,3 +1,7 @@ +# Low Level Runtime Functions for LLVM. +# The function definitions and explinations can be found here. +# https://gcc.gnu.org/onlinedocs/gccint/Libgcc.html#Libgcc + {% skip_file if flag?(:skip_crystal_compiler_rt) %} require "./compiler_rt/mulodi4.cr" diff --git a/src/crystal/compiler_rt/divti3.cr b/src/crystal/compiler_rt/divti3.cr new file mode 100644 index 000000000000..40e68e412900 --- /dev/null +++ b/src/crystal/compiler_rt/divti3.cr @@ -0,0 +1,14 @@ +require "./udivmodti4" + +# Function returning quotient for signed division eg `a / b` + +fun __divti3(a : Int128, b : Int128) : Int128 + bits_in_tword_m1 = (sizeof(Int128) &* sizeof(Char)) &- 1 + s_a = a >> bits_in_tword_m1 + s_b = b >> bits_in_tword_m1 + a = (a ^ s_a) &- s_a + b = (b ^ s_b) &- s_b + s_a ^= s_b + r = 0_u128 + ((__udivmodti4(a.unsafe_as(UInt128), b.unsafe_as(UInt128), pointerof(r)) ^ s_a) &- s_a).unsafe_as(Int128) +end diff --git a/src/crystal/compiler_rt/i128_info.cr b/src/crystal/compiler_rt/i128_info.cr new file mode 100644 index 000000000000..a964b42acf3c --- /dev/null +++ b/src/crystal/compiler_rt/i128_info.cr @@ -0,0 +1,37 @@ +@[Extern] +struct Int128Info + property low : UInt64 = 0_u64 + property high : Int64 = 0_i64 +end + +@[Extern(union: true)] +struct Int128RT + property all : Int128 = 0_i128 + property info : Int128Info = Int128Info.new + + macro [](high, low) + %i_info = uninitialized Int128RT + %i_info.info.high = {{high}}.unsafe_as(Int64) + %i_info.info.low = {{low}}.unsafe_as(UInt64) + %i_info + end + + macro [](high) + %i_info = uninitialized Int128RT + %i_info.all = {{high}}.unsafe_as(Int128) + %i_info + end + + def negate! + self.all = self.all * -1 + self + end + + def to_i128 + all + end + + def debug + printf("%x:%x\n", info.high, info.low) + end +end diff --git a/src/crystal/compiler_rt/modti3.cr b/src/crystal/compiler_rt/modti3.cr new file mode 100644 index 000000000000..aa95670e0516 --- /dev/null +++ b/src/crystal/compiler_rt/modti3.cr @@ -0,0 +1,14 @@ +require "./udivmodti4" + +# Function return the remainder of the signed division eg. `a % b` + +fun __modti3(a : Int128, b : Int128) : Int128 + bits_in_tword_m1 = sizeof(Int128) &* sizeof(Char) &- 1 + s = b >> bits_in_tword_m1 + b = (b ^ s) &- s + s = a >> bits_in_tword_m1 + a = (a ^ s) &- s + r = 0_u128 + __udivmodti4(a.unsafe_as(UInt128), b.unsafe_as(UInt128), pointerof(r)) + (r ^ s).unsafe_as(Int128) &- s # negate if s == -1 +end diff --git a/src/crystal/compiler_rt/mulddi3.cr b/src/crystal/compiler_rt/mulddi3.cr new file mode 100644 index 000000000000..6afb91c7bf47 --- /dev/null +++ b/src/crystal/compiler_rt/mulddi3.cr @@ -0,0 +1,24 @@ +require "./i128_info" + +# Functions for returning the product of signed integer multiplication eg. `a * b` + +fun __mulddi3(a : UInt64, b : UInt64) : Int128 + r = Int128RT.new + bits_in_dword_2 = sizeof(UInt128) * sizeof(Char) // 2 + lower_mask = ~0_u64 >> bits_in_dword_2 + + r.info.low = (a & lower_mask) &* (b & lower_mask) + t = r.info.low >> bits_in_dword_2 + r.info.low = r.info.low & lower_mask + t += (a >> bits_in_dword_2) &* (b & lower_mask) + r.info.low = (r.info.low &+ ((t & lower_mask) << bits_in_dword_2)) + r.info.high = (t >> bits_in_dword_2).unsafe_as(Int64) + t = r.info.low >> bits_in_dword_2 + r.info.low = r.info.low & lower_mask + t += (b >> bits_in_dword_2) &* (a & lower_mask) + r.info.low = (r.info.low &+ ((t & lower_mask) << bits_in_dword_2)) + r.info.high = r.info.high &+ (t >> bits_in_dword_2) + r.info.high = r.info.high &+ ((a >> bits_in_dword_2) &* (b >> bits_in_dword_2)) + + r.all +end diff --git a/src/crystal/compiler_rt/mulodi4.cr b/src/crystal/compiler_rt/mulodi4.cr index b592a91b69f7..f86f29fa6fde 100644 --- a/src/crystal/compiler_rt/mulodi4.cr +++ b/src/crystal/compiler_rt/mulodi4.cr @@ -1,3 +1,5 @@ +# Functions for returning the product of signed multiplication with overflow eg. `a * b` + fun __mulodi4(a : Int64, b : Int64, overflow : Int32*) : Int64 n = 64 min = Int64::MIN diff --git a/src/crystal/compiler_rt/muloti4.cr b/src/crystal/compiler_rt/muloti4.cr new file mode 100644 index 000000000000..14d1e7365429 --- /dev/null +++ b/src/crystal/compiler_rt/muloti4.cr @@ -0,0 +1,43 @@ +# Functions for returning the product of signed multiplication with overflow eg. `a * b` +# Ported from compiler-rt:lib/builtins/muloti4.c + +fun __muloti4(a : Int128, b : Int128, overflow : Int32*) : Int128 + n = sizeof(Int128) &* sizeof(Char) + min = Int64::MIN + max = Int64::MAX + overflow.value = 0 + result = a &* b + + if a == min + if b != 0 && b != 1 + overflow.value = 1 + end + return result + end + if b == min + if a != 0 && a != 1 + overflow.value = 1 + end + return result + end + + sa = a >> (n &- 1) + abs_a = (a ^ sa) &- sa + sb = b >> (n &- 1) + abs_b = (b ^ sb) &- sb + + if abs_a < 2 || abs_b < 2 + return result + end + if sa == sb + if abs_a > max // abs_b + overflow.value = 1 + end + else + if abs_a > min // -abs_b + overflow.value = 1 + end + end + + result +end diff --git a/src/crystal/compiler_rt/multi3.cr b/src/crystal/compiler_rt/multi3.cr new file mode 100644 index 000000000000..1e3768aa962a --- /dev/null +++ b/src/crystal/compiler_rt/multi3.cr @@ -0,0 +1,17 @@ +require "./i128_info" +require "./mulddi3" + +# Functions for returning the product of signed integer multiplication eg. `a * b` + +fun __multi3(a : Int128, b : Int128) : Int128 + x = Int128RT.new + y = Int128RT.new + r = Int128RT.new + x.all = a + y.all = b + + r.all = __mulddi3(x.info.low, y.info.low) + r.info.high = r.info.high &+ x.info.high &* y.info.low &+ x.info.low &* y.info.high + + r.all +end diff --git a/src/crystal/compiler_rt/u128_info.cr b/src/crystal/compiler_rt/u128_info.cr new file mode 100644 index 000000000000..21550eff734e --- /dev/null +++ b/src/crystal/compiler_rt/u128_info.cr @@ -0,0 +1,32 @@ +@[Extern] +struct UInt128Info + property low : UInt64 = 0_u64 + property high : UInt64 = 0_u64 +end + +@[Extern(union: true)] +struct UInt128RT + macro [](high, low) + %i_info = uninitialized UInt128RT + %i_info.info.high = {{high}}.unsafe_as(UInt64) + %i_info.info.low = {{low}}.unsafe_as(UInt64) + %i_info + end + + macro [](high) + %i_info = uninitialized UInt128RT + %i_info.all = {{high}}.unsafe_as(UInt128) + %i_info + end + + property all : UInt128 = 0_u128 + property info : UInt128Info = UInt128Info.new + + def to_u128 + all + end + + def debug + printf("%x:%x\n", info.high, info.low) + end +end diff --git a/src/crystal/compiler_rt/udivmodti4.cr b/src/crystal/compiler_rt/udivmodti4.cr new file mode 100644 index 000000000000..c5f1a367d9fa --- /dev/null +++ b/src/crystal/compiler_rt/udivmodti4.cr @@ -0,0 +1,136 @@ +require "./u128_info" + +# Function return the remainder of the unsigned division with overflow eg. `a % b` + +fun __udivmodti4(a : UInt128, b : UInt128, rem : UInt128*) : UInt128 + n_udword_bits = sizeof(Int64) &* sizeof(Char) + n_utword_bits = sizeof(Int128) &* sizeof(Char) + n = UInt128RT.new + d = UInt128RT.new + q = UInt128RT.new + r = UInt128RT.new + sr = 0_u32 + n.all = a + d.all = b + + if n.info.high == 0 + if d.info.high == 0 + if rem + rem.value = (n.info.low % d.info.low).to_u128 + end + n.info.low = n.info.low // d.info.low + return n.all + end + rem.value = n.info.low.to_u128 if rem + return 0_u128 + end + + if d.info.low == 0 + if d.info.high == 0 + if rem + n.info.high = n.info.high % d.info.low + rem.value = n.all + end + n.info.high = n.info.high // d.info.low + return n.all + end + + if n.info.low == 0 + if rem + r.info.high = n.info.high % d.info.high + r.info.low = 0 + rem.value = r.all + end + n.info.high = n.info.high // d.info.high + return n.all + end + + if (d.info.high & (d.info.high &- 1)) == 0 # if d is a power of 2 + if rem + r.info.low = n.info.low + r.info.high = n.info.high & (d.info.high &- 1) + rem.value = r.all + end + n.info.high = n.info.high >> d.info.high.trailing_zeros_count + return n.all + end + + sr = d.info.high.leading_zeros_count &- n.info.high.leading_zeros_count + if sr > n_udword_bits &- 2 + if rem + rem.value = n.all + end + return 0_u128 + end + sr = sr &+ 1 + q.info.low = 0 + q.info.high = n.info.low << (n_udword_bits &- sr) + r.info.high = n.info.high >> sr + r.info.low = (n.info.high << (n_udword_bits &- sr)) | (n.info.low >> sr) + else + if d.info.high == 0 + if (d.info.low & (d.info.low &- 1)) == 0 + if rem + rem.value = (n.info.low & (d.info.low &- 1)).to_u128 + end + return n.all if d.info.low == 1 + + sr = d.info.low.trailing_zeros_count + q.info.high = n.info.high >> sr + q.info.low = (n.info.high << (n_udword_bits &- sr)) | (n.info.low >> sr) + return q.all + end + sr = 1 &+ n_udword_bits &+ d.info.low.leading_zeros_count &- n.info.high.leading_zeros_count + if sr == n_udword_bits + q.info.low = 0 + q.info.high = n.info.low + r.info.high = 0 + r.info.low = n.info.high + elsif sr < n_udword_bits + q.info.low = 0 + q.info.high = n.info.low << (n_udword_bits &- sr) + r.info.high = n.info.high >> sr + r.info.low = (n.info.high << (n_udword_bits &- sr)) | (n.info.low >> sr) + else + q.info.low = n.info.low << (n_utword_bits &- sr) + q.info.high = (n.info.high << (n_utword_bits &- sr)) | (n.info.low >> (sr &- n_udword_bits)) + r.info.high = 0 + r.info.low = n.info.high >> (sr &- n_udword_bits) + end + else + sr = d.info.high.leading_zeros_count &- n.info.high.leading_zeros_count + if sr > n_udword_bits &- 1 + rem.value = n.all if rem + return 0_u128 + end + sr = sr &+ 1 + q.info.low = 0 + if sr == n_udword_bits + q.info.high = n.info.low + r.info.high = 0 + r.info.low = n.info.high + else + r.info.high = n.info.high >> sr + r.info.low = (n.info.high << (n_udword_bits &- sr)) | (n.info.low >> sr) + q.info.high = n.info.low << (n_udword_bits &- sr) + end + end + end + + carry = 0_u32 + (sr..0).each do + r.info.high = (r.info.high << 1) | (r.info.low >> (n_udword_bits &- 1)) + r.info.low = (r.info.low << 1) | (q.info.high >> (n_udword_bits &- 1)) + q.info.high = (q.info.high << 1) | (q.info.low >> (n_udword_bits &- 1)) + q.info.low = (q.info.low << 1) | carry + s = (d.all &- r.all &- 1) >> (n_utword_bits &- 1) + carry = s & 1 + r = (r.all &- (d.all & s)).unsafe_as(UInt128RT) + end + + q = ((q.all << 1) | carry).unsafe_as(UInt128RT) + if rem + rem.value = r.all + end + q.all +end diff --git a/src/crystal/compiler_rt/udivti3.cr b/src/crystal/compiler_rt/udivti3.cr new file mode 100644 index 000000000000..70ed9e242121 --- /dev/null +++ b/src/crystal/compiler_rt/udivti3.cr @@ -0,0 +1,8 @@ +require "./udivmodti4" + +# Functions for returning the product of signed multiplication with overflow eg. `a * b` + +fun __udivti3(a : UInt128, b : UInt128) : UInt128 + r = 0_u128 + __udivmodti4(a, b, pointerof(r)) +end diff --git a/src/crystal/compiler_rt/umodti3.cr b/src/crystal/compiler_rt/umodti3.cr new file mode 100644 index 000000000000..3e75fadd2362 --- /dev/null +++ b/src/crystal/compiler_rt/umodti3.cr @@ -0,0 +1,9 @@ +require "./udivmodti4" + +# Function return the remainder of the unsigned division eg. `a % b` + +fun __umodti3(a : UInt128, b : UInt128) : UInt128 + r = 0_u128 + __udivmodti4(a, b, pointerof(r)) + r +end