Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions src/compiler/crystal/codegen/codegen.cr
Original file line number Diff line number Diff line change
Expand Up @@ -424,10 +424,10 @@ module Crystal
@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)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove the TODO above

when :f32
@last = llvm_context.float.const_float(node.value)
when :f64
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/crystal/codegen/llvm_builder_helper.cr
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ module Crystal
end

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

def int(n)
Expand Down
4 changes: 2 additions & 2 deletions src/compiler/crystal/syntax/lexer.cr
Original file line number Diff line number Diff line change
Expand Up @@ -1557,7 +1557,7 @@ module Crystal
def scan_hex_number(start, negative = false)
next_char

num = 0_u64
num = 0_u128
while true
char = next_char
if char == '_'
Expand All @@ -1576,7 +1576,7 @@ module Crystal

def finish_scan_prefixed_number(num, negative, start)
if negative
string_value = (num.to_i64 * -1).to_s
string_value = (num.to_i128 * -1).to_s
else
string_value = num.to_s
end
Expand Down
2 changes: 1 addition & 1 deletion src/int.cr
Original file line number Diff line number Diff line change
Expand Up @@ -779,7 +779,7 @@ end

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

# Returns an `UInt128` by invoking `to_u128` on *value*.
Expand Down
1 change: 1 addition & 0 deletions src/llvm/lib_llvm.cr
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ lib LibLLVM
fun build_zext = LLVMBuildZExt(builder : BuilderRef, val : ValueRef, dest_ty : TypeRef, name : UInt8*) : ValueRef
fun const_array = LLVMConstArray(element_type : TypeRef, constant_vals : ValueRef*, length : UInt32) : ValueRef
fun const_int = LLVMConstInt(int_type : TypeRef, value : UInt64, sign_extend : Int32) : ValueRef
fun const_int_of_string = LLVMConstIntOfString(int_type : TypeRef, value : UInt8*, radix : UInt8) : ValueRef
fun const_null = LLVMConstNull(ty : TypeRef) : ValueRef
fun const_pointer_null = LLVMConstPointerNull(ty : TypeRef) : ValueRef
fun const_real = LLVMConstReal(real_ty : TypeRef, n : Float64) : ValueRef
Expand Down
4 changes: 4 additions & 0 deletions src/llvm/type.cr
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,10 @@ struct LLVM::Type
Value.new LibLLVM.const_int(self, value, 0)
end

def const_int_of_string(string) : Value
Value.new LibLLVM.const_int_of_string(self, string, 10)
end

def const_float(value : Float32) : Value
Value.new LibLLVM.const_real(self, value)
end
Expand Down
46 changes: 39 additions & 7 deletions src/string.cr
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,38 @@ class String
gen_to_ u64
end

### to_i128/to_u128

# Same as `#to_i` but returns an `Int128`.
def to_i128(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true) : Int128
to_i128(base, whitespace, underscore, prefix, strict) { raise ArgumentError.new("Invalid Int128: #{self}") }
end

# Same as `#to_i` but returns an `Int128` or `nil`.
def to_i128?(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true) : Int128?
to_i128(base, whitespace, underscore, prefix, strict) { nil }
end

# Same as `#to_i` but returns an `Int128` or the block's value.
def to_i128(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true, &block)
gen_to_ i128, ((1<<127)-1).to_u128, (1<<127).to_u128
end

# Same as `#to_i` but returns an `UInt128`.
def to_u128(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true) : UInt128
to_u128(base, whitespace, underscore, prefix, strict) { raise ArgumentError.new("Invalid UInt128: #{self}") }
end

# Same as `#to_i` but returns an `UInt128` or `nil`.
def to_u128?(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true) : UInt128?
to_u128(base, whitespace, underscore, prefix, strict) { nil }
end

# Same as `#to_i` but returns an `UInt128` or the block's value.
def to_u128(base : Int = 10, whitespace = true, underscore = false, prefix = false, strict = true, &block)
gen_to_ u128
end

# :nodoc:
CHAR_TO_DIGIT = begin
table = StaticArray(Int8, 256).new(-1_i8)
Expand All @@ -485,13 +517,13 @@ class String
end

# :nodoc:
record ToU64Info,
value : UInt64,
record ToU128Info,
value : UInt128,
negative : Bool,
invalid : Bool

private macro gen_to_(method, max_positive = nil, max_negative = nil)
info = to_u64_info(base, whitespace, underscore, prefix, strict)
info = to_u128_info(base, whitespace, underscore, prefix, strict)
return yield if info.invalid

if info.negative
Expand All @@ -509,7 +541,7 @@ class String
end
end

private def to_u64_info(base, whitespace, underscore, prefix, strict)
private def to_u128_info(base, whitespace, underscore, prefix, strict)
Copy link
Copy Markdown
Member

@RX14 RX14 Jan 10, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm concerned about all to_i calls having to go through 128bit mathematics: it probably slows down the conversion process on processors without 128bit maths support (i.e. most of them).

I think it's a good idea to duplicate this method, and have both to_u64_info and to_u128_info. This can probably be done quite easily without extra boilerplate with macros.

If only we had a benchmark suite :)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I agree. Also two test are failed because I extend to_u64_info method to support 128 bit integer and now they works incorrect with 64 bit.
I will fix it.

raise ArgumentError.new("Invalid base #{base}") unless 2 <= base <= 36 || base == 62

ptr = to_unsafe
Expand All @@ -523,7 +555,7 @@ class String

negative = false
found_digit = false
mul_overflow = ~0_u64 / base
mul_overflow = ~0_u128 / base

# Check + and -
case ptr.value.unsafe_chr
Expand Down Expand Up @@ -555,7 +587,7 @@ class String
end
end

value = 0_u64
value = 0_u128
last_is_underscore = true
invalid = false

Expand Down Expand Up @@ -608,7 +640,7 @@ class String
invalid = true
end

ToU64Info.new value, negative, invalid
ToU128Info.new value, negative, invalid
end

# Returns the result of interpreting characters in this string as a floating point number (`Float64`).
Expand Down