Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: crystal-lang/crystal
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: c82606deaa20
Choose a base ref
...
head repository: crystal-lang/crystal
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 8514651b7721
Choose a head ref
  • 2 commits
  • 13 files changed
  • 1 contributor

Commits on Sep 24, 2017

  1. Add Int128 and UInt128

    asterite committed Sep 24, 2017
    Copy the full SHA
    f6b2942 View commit details

Commits on Sep 29, 2017

  1. Merge pull request #5024 from asterite/feature/128-bit-integers

    Add Int128 and UInt128
    asterite authored Sep 29, 2017
    Copy the full SHA
    8514651 View commit details
20 changes: 20 additions & 0 deletions spec/compiler/codegen/primitives_spec.cr
Original file line number Diff line number Diff line change
@@ -14,6 +14,26 @@ describe "Code gen: primitives" do
run("1_i64").to_i.should eq(1)
end

it "codegens int128" do
# LLVM's JIT doesn't seem to support 128
# bit integers well regarding GenericValue
run(%(
require "prelude"
1_i128.to_i
)).to_i.should eq(1)
end

it "codegens uint128" do
# LLVM's JIT doesn't seem to support 128
# bit integers well regarding GenericValue
run(%(
require "prelude"
1_u128.to_i
)).to_i.should eq(1)
end

it "codegens char" do
run("'a'").to_i.should eq('a'.ord)
end
8 changes: 8 additions & 0 deletions spec/compiler/lexer/lexer_spec.cr
Original file line number Diff line number Diff line change
@@ -53,6 +53,10 @@ private def it_lexes_i64(values)
values.each { |value| it_lexes_number :i64, value }
end

private def it_lexes_i128(values)
values.each { |value| it_lexes_number :i128, value }
end

private def it_lexes_u64(values)
values.each { |value| it_lexes_number :u64, value }
end
@@ -151,6 +155,7 @@ describe "Lexer" do
it_lexes_i32 ["1", ["0i32", "0"], ["1hello", "1"], "+1", "-1", "1234", "+1234", "-1234",
["1.foo", "1"], ["1_000", "1000"], ["100_000", "100000"]]
it_lexes_i64 [["1i64", "1"], ["1_i64", "1"], ["1i64hello", "1"], ["+1_i64", "+1"], ["-1_i64", "-1"]]
it_lexes_i128 [["1i128", "1"], ["1_i128", "1"], ["1i128hello", "1"], ["+1_i128", "+1"], ["-1_i128", "-1"]]
it_lexes_f32 [["0f32", "0"], ["0_f32", "0"], ["1.0f32", "1.0"], ["1.0f32hello", "1.0"],
["+1.0f32", "+1.0"], ["-1.0f32", "-1.0"], ["-0.0f32", "-0.0"], ["1_234.567_890_f32", "1234.567890"]]
it_lexes_f64 ["1.0", ["1.0hello", "1.0"], "+1.0", "-1.0", ["1_234.567_890", "1234.567890"]]
@@ -181,6 +186,9 @@ describe "Lexer" do
it_lexes_number :u64, ["1u64", "1"]
it_lexes_number :u64, ["1_u64", "1"]

it_lexes_number :u128, ["1u128", "1"]
it_lexes_number :u128, ["1_u128", "1"]

it_lexes_number :f32, ["1f32", "1"]
it_lexes_number :f32, ["1.0f32", "1.0"]

3 changes: 3 additions & 0 deletions spec/compiler/parser/parser_spec.cr
Original file line number Diff line number Diff line change
@@ -37,6 +37,9 @@ describe "Parser" do
it_parses "+1_i64", 1.int64
it_parses "-1_i64", -1.int64

it_parses "1_u128", 1.uint128
it_parses "1_i128", 1.int128

it_parses "1.0", 1.0.float64
it_parses "+1.0", 1.0.float64
it_parses "-1.0", -1.0.float64
8 changes: 8 additions & 0 deletions spec/compiler/semantic/primitives_spec.cr
Original file line number Diff line number Diff line change
@@ -13,6 +13,14 @@ describe "Semantic: primitives" do
assert_type("1_i64") { int64 }
end

it "types a int128" do
assert_type("1_i128") { int128 }
end

it "types a uint128" do
assert_type("1_u128") { uint128 }
end

it "types a float32" do
assert_type("2.3_f32") { float32 }
end
8 changes: 8 additions & 0 deletions spec/support/syntax.cr
Original file line number Diff line number Diff line change
@@ -12,6 +12,14 @@ struct Number
NumberLiteral.new to_s, :i64
end

def int128
NumberLiteral.new to_s, :i128
end

def uint128
NumberLiteral.new to_s, :u128
end

def float32
NumberLiteral.new to_f32.to_s, :f32
end
8 changes: 8 additions & 0 deletions src/compiler/crystal/codegen/codegen.cr
Original file line number Diff line number Diff line change
@@ -422,10 +422,18 @@ module Crystal
@last = int64(node.value.to_i64)
when :u64
@last = int64(node.value.to_u64)
when :i128
# TODO: implement String#to_i128 and use it
@last = int128(node.value.to_i64)
when :u128
# TODO: implement String#to_u128 and use it
@last = int128(node.value.to_u64)
when :f32
@last = llvm_context.float.const_float(node.value)
when :f64
@last = llvm_context.double.const_double(node.value)
else
node.raise "Bug: unhandled number kind: #{node.kind}"
end
end

4 changes: 4 additions & 0 deletions src/compiler/crystal/codegen/llvm_builder_helper.cr
Original file line number Diff line number Diff line change
@@ -20,6 +20,10 @@ module Crystal
llvm_context.int64.const_int(n)
end

def int128(n)
llvm_context.int128.const_int(n)
end

def int(n)
int32(n)
end
30 changes: 17 additions & 13 deletions src/compiler/crystal/program.cr
Original file line number Diff line number Diff line change
@@ -150,6 +150,8 @@ module Crystal
types["UInt32"] = @uint32 = IntegerType.new self, self, "UInt32", int, 4, 6, :u32
types["Int64"] = @int64 = IntegerType.new self, self, "Int64", int, 8, 7, :i64
types["UInt64"] = @uint64 = IntegerType.new self, self, "UInt64", int, 8, 8, :u64
types["Int128"] = @int128 = IntegerType.new self, self, "Int128", int, 16, 9, :i128
types["UInt128"] = @uint128 = IntegerType.new self, self, "UInt128", int, 16, 10, :u128

types["Float"] = float = @float = NonGenericClassType.new self, self, "Float", number
abstract_value_type(float)
@@ -426,8 +428,8 @@ module Crystal
crystal_path.find filename, relative_to
end

{% for name in %w(object no_return value number reference void nil bool char int int8 int16 int32 int64
uint8 uint16 uint32 uint64 float float32 float64 string symbol pointer array static_array
{% for name in %w(object no_return value number reference void nil bool char int int8 int16 int32 int64 int128
uint8 uint16 uint32 uint64 uint128 float float32 float64 string symbol pointer array static_array
exception tuple named_tuple proc union enum range regex crystal) %}
def {{name.id}}
@{{name.id}}.not_nil!
@@ -446,17 +448,19 @@ module Crystal

def type_from_literal_kind(kind)
case kind
when :i8 then int8
when :i16 then int16
when :i32 then int32
when :i64 then int64
when :u8 then uint8
when :u16 then uint16
when :u32 then uint32
when :u64 then uint64
when :f32 then float32
when :f64 then float64
else raise "Invalid node kind: #{kind}"
when :i8 then int8
when :i16 then int16
when :i32 then int32
when :i64 then int64
when :i128 then int128
when :u8 then uint8
when :u16 then uint16
when :u32 then uint32
when :u64 then uint64
when :u128 then uint128
when :f32 then float32
when :f64 then float64
else raise "Invalid node kind: #{kind}"
end
end

22 changes: 20 additions & 2 deletions src/compiler/crystal/syntax/lexer.cr
Original file line number Diff line number Diff line change
@@ -1615,7 +1615,16 @@ module Crystal
@token.number_kind = :i8
2
when '1'
if next_char == '6'
case next_char
when '2'
if next_char == '8'
next_char
@token.number_kind = :i128
4
else
raise "invalid int suffix"
end
when '6'
next_char
@token.number_kind = :i16
3
@@ -1650,7 +1659,16 @@ module Crystal
@token.number_kind = :u8
2
when '1'
if next_char == '6'
case next_char
when '2'
if next_char == '8'
next_char
@token.number_kind = :u128
4
else
raise "invalid uint suffix"
end
when '6'
next_char
@token.number_kind = :u16
3
83 changes: 69 additions & 14 deletions src/int.cr
Original file line number Diff line number Diff line change
@@ -56,8 +56,8 @@
# 0xfe012d # == 16646445
# ```
struct Int
alias Signed = Int8 | Int16 | Int32 | Int64
alias Unsigned = UInt8 | UInt16 | UInt32 | UInt64
alias Signed = Int8 | Int16 | Int32 | Int64 | Int128
alias Unsigned = UInt8 | UInt16 | UInt32 | UInt64 | UInt128
alias Primitive = Signed | Unsigned

# Returns a `Char` that has the unicode codepoint of `self`.
@@ -430,10 +430,10 @@ struct Int
end

private def internal_to_s(base, upcase = false)
# Given sizeof(self) <= 64 bits, we need at most 64 bytes for a base 2
# Given sizeof(self) <= 128 bits, we need at most 128 bytes for a base 2
# representation, plus one byte for the trailing 0.
chars = uninitialized UInt8[65]
ptr_end = chars.to_unsafe + 64
chars = uninitialized UInt8[129]
ptr_end = chars.to_unsafe + 128
ptr = ptr_end
num = self

@@ -458,15 +458,17 @@ struct Int

def inspect(io)
type = case self
when Int8 then "_i8"
when Int16 then "_i16"
when Int32 then ""
when Int64 then "_i64"
when UInt8 then "_u8"
when UInt16 then "_u16"
when UInt32 then "_u32"
when UInt64 then "_u64"
else raise "BUG: impossible"
when Int8 then "_i8"
when Int16 then "_i16"
when Int32 then ""
when Int64 then "_i64"
when Int128 then "_i128"
when UInt8 then "_u8"
when UInt16 then "_u16"
when UInt32 then "_u32"
when UInt64 then "_u64"
when UInt128 then "_u128"
else raise "BUG: impossible"
end

to_s(io)
@@ -663,6 +665,33 @@ struct Int64
end
end

struct Int128
# TODO: eventually update to literals once UInt128 bit support is finished
MIN = new(1) << 127
MAX = ~MIN

# Returns an `Int128` by invoking `to_i128` on *value*.
def self.new(value)
value.to_i128
end

def -
# TODO: use 0_i128 - self
Int128.new(0) - self
end

def popcount
# TODO: use after Crystal 0.23.1
# Intrinsics.popcount128(self)
v1, v2 = self.unsafe_as({Int64, Int64})
Int128.new(v1.popcount + v2.popcount)
end

def clone
self
end
end

struct UInt8
MIN = 0_u8
MAX = 255_u8
@@ -750,3 +779,29 @@ struct UInt64
self
end
end

struct UInt128
# TODO: eventually update to literals once UInt128 bit support is finished
MIN = new 0
MAX = ~MIN

# Returns an `UInt128` by invoking `to_u128` on *value*.
def self.new(value)
value.to_u128
end

def abs
self
end

def popcount
# TODO: use after Crystal 0.23.1
# Intrinsics.popcount128(self)
v1, v2 = self.unsafe_as({UInt64, UInt64})
UInt128.new(v1.popcount + v2.popcount)
end

def clone
self
end
end
3 changes: 3 additions & 0 deletions src/intrinsics.cr
Original file line number Diff line number Diff line change
@@ -16,6 +16,9 @@ lib Intrinsics
fun popcount16 = "llvm.ctpop.i16"(src : Int16) : Int16
fun popcount32 = "llvm.ctpop.i32"(src : Int32) : Int32
fun popcount64 = "llvm.ctpop.i64"(src : Int64) : Int64

# TODO: uncomment and use in int.cr after Crystal 0.23.1
# fun popcount128 = "llvm.ctpop.i128"(src : Int128) : Int128
end

macro debugger
10 changes: 9 additions & 1 deletion src/io/byte_format.cr
Original file line number Diff line number Diff line change
@@ -41,6 +41,8 @@ module IO::ByteFormat
abstract def encode(int : UInt32, io : IO)
abstract def encode(int : Int64, io : IO)
abstract def encode(int : UInt64, io : IO)
abstract def encode(int : Int128, io : IO)
abstract def encode(int : UInt128, io : IO)
abstract def encode(int : Float32, io : IO)
abstract def encode(int : Float64, io : IO)

@@ -52,6 +54,8 @@ module IO::ByteFormat
abstract def encode(int : UInt32, bytes : Bytes)
abstract def encode(int : Int64, bytes : Bytes)
abstract def encode(int : UInt64, bytes : Bytes)
abstract def encode(int : Int128, bytes : Bytes)
abstract def encode(int : UInt128, bytes : Bytes)
abstract def encode(int : Float32, bytes : Bytes)
abstract def encode(int : Float64, bytes : Bytes)

@@ -63,6 +67,8 @@ module IO::ByteFormat
abstract def decode(int : UInt32.class, io : IO)
abstract def decode(int : Int64.class, io : IO)
abstract def decode(int : UInt64.class, io : IO)
abstract def decode(int : Int128.class, io : IO)
abstract def decode(int : UInt128.class, io : IO)
abstract def decode(int : Float32.class, io : IO)
abstract def decode(int : Float64.class, io : IO)

@@ -74,6 +80,8 @@ module IO::ByteFormat
abstract def decode(int : UInt32.class, bytes : Bytes)
abstract def decode(int : Int64.class, bytes : Bytes)
abstract def decode(int : UInt64.class, bytes : Bytes)
abstract def decode(int : Int128.class, bytes : Bytes)
abstract def decode(int : UInt128.class, bytes : Bytes)
abstract def decode(int : Float32.class, bytes : Bytes)
abstract def decode(int : Float64.class, bytes : Bytes)

@@ -122,7 +130,7 @@ module IO::ByteFormat

{% for mod in %w(LittleEndian BigEndian) %}
module {{mod.id}}
{% for type, i in %w(Int8 UInt8 Int16 UInt16 Int32 UInt32 Int64 UInt64) %}
{% for type, i in %w(Int8 UInt8 Int16 UInt16 Int32 UInt32 Int64 UInt64 Int128 UInt128) %}
{% bytesize = 2 ** (i / 2) %}

def self.encode(int : {{type.id}}, io : IO)
Loading