From 14827269ec1be6b2c52fee3fdc33ba30575928c2 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Fri, 22 Mar 2024 11:48:12 +0100 Subject: [PATCH 1/3] Factor out the hashing of 32-bit integers --- StandardLibrary/Sources/Core/Hasher.hylo | 8 ++++++++ StandardLibrary/Sources/Core/Numbers/Integers/Int32.hylo | 5 +---- StandardLibrary/Sources/Core/Numbers/Integers/UInt32.hylo | 5 +---- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/StandardLibrary/Sources/Core/Hasher.hylo b/StandardLibrary/Sources/Core/Hasher.hylo index 7aa18803e..ba1dea08f 100644 --- a/StandardLibrary/Sources/Core/Hasher.hylo +++ b/StandardLibrary/Sources/Core/Hasher.hylo @@ -33,6 +33,14 @@ public type Hasher { &hash = hash &* FNV.prime } + /// Adds `i32` to the hash value computed by `self`. + public fun combine(i32: Int32) inout { + &combine(byte: Int8(truncating_or_extending: i32)) + &combine(byte: Int8(truncating_or_extending: i32 >> 8)) + &combine(byte: Int8(truncating_or_extending: i32 >> 16)) + &combine(byte: Int8(truncating_or_extending: i32 >> 24)) + } + /// Adds `bytes` to the hash value computed by `self`. public fun unsafe_combine(bytes: PointerToBuffer) inout { var i = 0 diff --git a/StandardLibrary/Sources/Core/Numbers/Integers/Int32.hylo b/StandardLibrary/Sources/Core/Numbers/Integers/Int32.hylo index badfc207e..1c706bef6 100644 --- a/StandardLibrary/Sources/Core/Numbers/Integers/Int32.hylo +++ b/StandardLibrary/Sources/Core/Numbers/Integers/Int32.hylo @@ -59,10 +59,7 @@ public conformance Int32: Equatable { public conformance Int32: Hashable { public fun hash(into hasher: inout Hasher) { - &hasher.combine(byte: Int8(truncating_or_extending: self)) - &hasher.combine(byte: Int8(truncating_or_extending: self >> 8)) - &hasher.combine(byte: Int8(truncating_or_extending: self >> 16)) - &hasher.combine(byte: Int8(truncating_or_extending: self >> 24)) + &hasher.combine(i32: self) } } diff --git a/StandardLibrary/Sources/Core/Numbers/Integers/UInt32.hylo b/StandardLibrary/Sources/Core/Numbers/Integers/UInt32.hylo index 235ce73a2..819e60d48 100644 --- a/StandardLibrary/Sources/Core/Numbers/Integers/UInt32.hylo +++ b/StandardLibrary/Sources/Core/Numbers/Integers/UInt32.hylo @@ -42,10 +42,7 @@ public conformance UInt32: Equatable { public conformance UInt32: Hashable { public fun hash(into hasher: inout Hasher) { - &hasher.combine(byte: Int8(truncating_or_extending: self)) - &hasher.combine(byte: Int8(truncating_or_extending: self >> 8)) - &hasher.combine(byte: Int8(truncating_or_extending: self >> 16)) - &hasher.combine(byte: Int8(truncating_or_extending: self >> 24)) + &hasher.combine(i32: .new(bit_pattern: self)) } } From d9df9670f3de67f27aa530f5935b641ebcb725a0 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Fri, 22 Mar 2024 12:17:35 +0100 Subject: [PATCH 2/3] Add a helper to hash 64 bit integers --- StandardLibrary/Sources/Core/Hasher.hylo | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/StandardLibrary/Sources/Core/Hasher.hylo b/StandardLibrary/Sources/Core/Hasher.hylo index ba1dea08f..be4287c4d 100644 --- a/StandardLibrary/Sources/Core/Hasher.hylo +++ b/StandardLibrary/Sources/Core/Hasher.hylo @@ -41,6 +41,12 @@ public type Hasher { &combine(byte: Int8(truncating_or_extending: i32 >> 24)) } + /// Adds `i64` to the hash value computed by `self`. + public fun combine(i64: Int64) inout { + &combine(i32: Int32(truncating_or_extending: i64)) + &combine(i32: Int32(truncating_or_extending: i64 >> 32)) + } + /// Adds `bytes` to the hash value computed by `self`. public fun unsafe_combine(bytes: PointerToBuffer) inout { var i = 0 From 503e577eb63ef3ab340722b2246250cf1d53f562 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Fri, 22 Mar 2024 12:18:09 +0100 Subject: [PATCH 3/3] Implement 'Int64' and 'UInt64' --- Sources/IR/Emitter.swift | 8 +- .../Sources/Core/Numbers/Integers/Int64.hylo | 356 ++++++++++++++++++ .../Sources/Core/Numbers/Integers/UInt64.hylo | 301 +++++++++++++++ 3 files changed, 663 insertions(+), 2 deletions(-) create mode 100644 StandardLibrary/Sources/Core/Numbers/Integers/Int64.hylo create mode 100644 StandardLibrary/Sources/Core/Numbers/Integers/UInt64.hylo diff --git a/Sources/IR/Emitter.swift b/Sources/IR/Emitter.swift index e6b07f852..a0e37e2cb 100644 --- a/Sources/IR/Emitter.swift +++ b/Sources/IR/Emitter.swift @@ -1777,16 +1777,20 @@ struct Emitter { switch literalType { case ast.coreType("Int")!: emitStore(integer: literal, signed: true, bitWidth: 64, to: storage) - case ast.coreType("Int32")!: - emitStore(integer: literal, signed: true, bitWidth: 32, to: storage) case ast.coreType("Int8")!: emitStore(integer: literal, signed: true, bitWidth: 8, to: storage) + case ast.coreType("Int32")!: + emitStore(integer: literal, signed: true, bitWidth: 32, to: storage) + case ast.coreType("Int64")!: + emitStore(integer: literal, signed: true, bitWidth: 64, to: storage) case ast.coreType("UInt")!: emitStore(integer: literal, signed: false, bitWidth: 64, to: storage) case ast.coreType("UInt8")!: emitStore(integer: literal, signed: false, bitWidth: 8, to: storage) case ast.coreType("UInt32")!: emitStore(integer: literal, signed: false, bitWidth: 32, to: storage) + case ast.coreType("UInt64")!: + emitStore(integer: literal, signed: false, bitWidth: 64, to: storage) case ast.coreType("Float64")!: emitStore(floatingPoint: literal, to: storage, evaluatedBy: FloatingPointConstant.float64(_:)) case ast.coreType("Float32")!: diff --git a/StandardLibrary/Sources/Core/Numbers/Integers/Int64.hylo b/StandardLibrary/Sources/Core/Numbers/Integers/Int64.hylo new file mode 100644 index 000000000..c0053b466 --- /dev/null +++ b/StandardLibrary/Sources/Core/Numbers/Integers/Int64.hylo @@ -0,0 +1,356 @@ +/// A 64-bit signed integer value. +public type Int64: Regular { + + internal var value: Builtin.i64 + + internal memberwise init + + /// Creates an instance with the same memory representation as `other`. + public init(bit_pattern other: UInt64) { + &self.value = other.value + } + + /// Creates a copy of `other`. + /// + /// - Requires: The value of `other` must be representable in this type. + public init(_ other: Int) { + &self.value = Builtin.trunc_word_i64(other.value) + } + + /// Returns the absolute value of `self`. + public fun abs() -> Int64 { + if self < 0 { -self } else { +self } + } + + /// Returns `self`. + public fun prefix+ () -> Self { + self.copy() + } + + /// Returns the bitwise inverse of `self`. + public fun prefix~ () -> Self { + self ^ -1 + } + +} + +public conformance Int64: ExpressibleByIntegerLiteral {} + +public conformance Int64: Copyable { + + public fun copy() -> Self { + Int64(value: value) + } + +} + +public conformance Int64: Equatable { + + public fun infix== (_ other: Self) -> Bool { + Bool(value: Builtin.icmp_eq_i64(value, other.value)) + } + + public fun infix!= (_ other: Self) -> Bool { + Bool(value: Builtin.icmp_ne_i64(value, other.value)) + } + +} + +public conformance Int64: Hashable { + + public fun hash(into hasher: inout Hasher) { + &hasher.combine(i64: self) + } + +} + +public conformance Int64: Comparable { + + public fun infix< (_ other: Self) -> Bool { + Bool(value: Builtin.icmp_slt_i64(value, other.value)) + } + + public fun infix<= (_ other: Self) -> Bool { + Bool(value: Builtin.icmp_sle_i64(value, other.value)) + } + + public fun infix> (_ other: Self) -> Bool { + Bool(value: Builtin.icmp_sgt_i64(value, other.value)) + } + + public fun infix>= (_ other: Self) -> Bool { + Bool(value: Builtin.icmp_sge_i64(value, other.value)) + } + +} + +public conformance Int64: AdditiveArithmetic { + + public fun infix+ (_ other: Self) -> Self { + Int64(value: Builtin.add_i64(value, other.value)) + } + + public fun infix+= (_ other: Self) inout { + &self.value = Builtin.add_i64(value, other.value) + } + + public fun infix- (_ other: Self) -> Self { + Int64(value: Builtin.sub_i64(value, other.value)) + } + + public fun infix-= (_ other: Self) inout { + &self.value = Builtin.sub_i64(value, other.value) + } + + public static fun zero() -> Self { + 0 + } + +} + +public conformance Int64: Numeric { + + public typealias Magnitude = UInt64 + + public fun magnitude() -> UInt64 { + UInt64(bit_pattern: self) + } + + public fun infix* (_ other: Self) -> Self { + Int64(value: Builtin.mul_i64(value, other.value)) + } + + public fun infix*= (_ other: Self) inout { + &self.value = Builtin.mul_i64(value, other.value) + } + +} + +public conformance Int64: SignedNumeric { + + public fun prefix- () -> Self { + Int64() - self + } + + public fun negate() inout { + &self = -self + } + +} + +public conformance Int64: BinaryInteger { + + public init() { + &self.value = Builtin.zeroinitializer_i64() + } + + public init(truncating_or_extending source: T) { + let w = source.words() + &self.value = Builtin.trunc_word_i64(w[w.start_position()].value) + } + + public fun instance_bit_width() -> Int { + 32 + } + + public fun signum() -> Int { + (if self > 0 { 1 } else { 0 }) - (if self < 0 { 1 } else { 0 }) + } + + public fun trailing_zeros() -> Int { + Int(value: Builtin.zext_i64_word(Builtin.cttz_i64(value))) + } + + public fun quotient_and_remainder(dividing_by other: Self) -> {quotient: Self, remainder: Self} { + (quotient: self / other, remainder: self % other) + } + + public fun words() -> CollectionOfOne { + // TODO: Handle the case where Int64 is larger than UInt. + return CollectionOfOne(UInt(value: Builtin.sext_i64_word(value))) + } + + public fun infix/ (_ other: Self) -> Self { + Int64(value: Builtin.sdiv_i64(value, other.value)) + } + + public fun infix/= (_ other: Self) inout { + &self.value = Builtin.sdiv_i64(value, other.value) + } + + public fun infix% (_ other: Self) -> Self { + Int64(value: Builtin.srem_i64(value, other.value)) + } + + public fun infix%= (_ other: Self) inout { + &self.value = Builtin.srem_i64(value, other.value) + } + + public fun infix& (_ other: Self) -> Self { + Int64(value: Builtin.and_i64(value, other.value)) + } + + public fun infix&= (_ other: Self) inout { + &self.value = Builtin.and_i64(value, other.value) + } + + public fun infix| (_ other: Self) -> Self { + Int64(value: Builtin.or_i64(value, other.value)) + } + + public fun infix|= (_ other: Self) inout { + &self.value = Builtin.or_i64(value, other.value) + } + + public fun infix^ (_ other: Self) -> Self { + Int64(value: Builtin.xor_i64(value, other.value)) + } + + public fun infix^= (_ other: Self) inout { + &self.value = Builtin.xor_i64(value, other.value) + } + + public fun infix<< (_ n: Int) -> Self { + if n >= 0 { + return if n < Self.bit_width() { self &<< n } else { 0 } + } else if n <= -Self.bit_width() { + return self &>> (Self.bit_width() - 1) + } else { + return self &>> -n + } + } + + public fun infix<<= (_ n: Int) inout { + &self = self << n + } + + public fun infix>> (_ n: Int) -> Self { + if n >= 0 { + return if n < Self.bit_width() { self &>> n } else { self &>> (Self.bit_width() - 1) } + } else if n <= -Self.bit_width() { + return 0 + } else { + return self &<< -n + } + } + + public fun infix>>= (_ n: Int) inout { + &self = self >> n + } + + public static fun is_signed() -> Bool { + true + } + +} + +public conformance Int64: SignedInteger { + + public fun successor() -> Self { + self + 1 + } + +} + +public conformance Int64: FixedWidthInteger { + + public fun matches(_ mask: Self) -> Bool { + (self & mask) == mask + } + + public fun adding_reporting_overflow(_ other: Self) -> {partial_value: Self, overflow: Bool} { + let r = Builtin.sadd_with_overflow_i64(value, other.value) + return (partial_value: Int64(value: r.0), overflow: Bool(value: r.1)) + } + + public fun subtracting_reporting_overflow( + _ other: Self + ) -> {partial_value: Self, overflow: Bool} { + let r = Builtin.ssub_with_overflow_i64(value, other.value) + return (partial_value: Int64(value: r.0), overflow: Bool(value: r.1)) + } + + public fun multiplied_reporting_overflow( + by other: Self + ) -> {partial_value: Self, overflow: Bool} { + let r = Builtin.smul_with_overflow_i64(value, other.value) + return (partial_value: Int64(value: r.0), overflow: Bool(value: r.1)) + } + + public fun divided_reporting_overflow(by other: Self) -> {partial_value: Self, overflow: Bool} { + if other == 0 { + (partial_value: self.copy(), overflow: true) + } else if (self == Self.min()) && (other == -1) { + (partial_value: self.copy(), overflow: true) + } else { + (partial_value: Int64(value: Builtin.sdiv_i64(value, other.value)), overflow: false) + } + } + + public fun remainder_reporting_overflow( + dividing_by other: Self + ) -> {partial_value: Self, overflow: Bool} { + if other == 0 { + (partial_value: self.copy(), overflow: true) + } else if (self == Self.min()) && (other == -1) { + (partial_value: 0, overflow: true) + } else { + (partial_value: Int64(value: Builtin.srem_i64(value, other.value)), overflow: false) + } + } + + public fun nonzero_bit_count() -> Int { + Int(value: Builtin.zext_i64_word(Builtin.ctpop_i64(value))) + } + + public fun leading_zeros() -> Int { + Int(value: Builtin.zext_i64_word(Builtin.ctlz_i64(value))) + } + + public fun infix&<< (_ n: Int) -> Self { + var lhs = self.copy() + lhs &<<= n + return lhs + } + + public fun infix&<<= (_ n: Int) inout { + &self.value = Builtin.shl_i64(value, UInt64(truncating_or_extending: n).value) + } + + public fun infix&>> (_ n: Int) -> Self { + var lhs = self.copy() + lhs &>>= n + return lhs + } + + public fun infix&>>= (_ n: Int) inout { + &self.value = Builtin.ashr_i64(value, UInt64(truncating_or_extending: n).value) + } + + public static fun bit_width() -> Int { + 32 + } + + public static fun max() -> Self { + 2147483647 + } + + public static fun min() -> Self { + -2147483648 + } + +} + +public conformance Int64: Strideable { + + public typealias Stride = Int64 + + public fun offset(to other: Self) -> Int64 { + other - self + } + + public fun advance(by offset: Int64) -> Self { + self + offset + } + +} diff --git a/StandardLibrary/Sources/Core/Numbers/Integers/UInt64.hylo b/StandardLibrary/Sources/Core/Numbers/Integers/UInt64.hylo new file mode 100644 index 000000000..150a13ee6 --- /dev/null +++ b/StandardLibrary/Sources/Core/Numbers/Integers/UInt64.hylo @@ -0,0 +1,301 @@ +/// A 64-bit unsigned integer value. +public type UInt64: Regular { + + internal var value: Builtin.i64 + + internal memberwise init + + /// Creates an instance with the same memory representation as `other`. + public init(bit_pattern other: Int64) { + &self.value = other.value + } + + /// Returns the bitwise inverse of `self`. + public fun prefix~ () -> Self { + self ^ UInt64(bit_pattern: -1 as Int64) + } + +} + +public conformance UInt64: ExpressibleByIntegerLiteral {} + +public conformance UInt64: Copyable { + + public fun copy() -> Self { + UInt64(value: value) + } + +} + +public conformance UInt64: Equatable { + + public fun infix== (_ other: Self) -> Bool { + Bool(value: Builtin.icmp_eq_i64(value, other.value)) + } + + public fun infix!= (_ other: Self) -> Bool { + Bool(value: Builtin.icmp_ne_i64(value, other.value)) + } + +} + +public conformance UInt64: Hashable { + + public fun hash(into hasher: inout Hasher) { + &hasher.combine(i64: .new(bit_pattern: self)) + } + +} + +public conformance UInt64: Comparable { + + public fun infix< (_ other: Self) -> Bool { + Bool(value: Builtin.icmp_ult_i64(value, other.value)) + } + + public fun infix<= (_ other: Self) -> Bool { + Bool(value: Builtin.icmp_ule_i64(value, other.value)) + } + + public fun infix> (_ other: Self) -> Bool { + Bool(value: Builtin.icmp_ugt_i64(value, other.value)) + } + + public fun infix>= (_ other: Self) -> Bool { + Bool(value: Builtin.icmp_uge_i64(value, other.value)) + } + +} + +public conformance UInt64: AdditiveArithmetic { + + public fun infix+ (_ other: Self) -> Self { + UInt64(value: Builtin.add_i64(value, other.value)) + } + + public fun infix+= (_ other: Self) inout { + &self.value = Builtin.add_i64(value, other.value) + } + + public fun infix- (_ other: Self) -> Self { + UInt64(value: Builtin.sub_i64(value, other.value)) + } + + public fun infix-= (_ other: Self) inout { + &self.value = Builtin.sub_i64(value, other.value) + } + + public static fun zero() -> Self { + 0 + } + +} + +public conformance UInt64: Numeric { + + public typealias Magnitude = UInt64 + + public fun magnitude() -> UInt64 { + self.copy() + } + + public fun infix* (_ other: Self) -> Self { + UInt64(value: Builtin.mul_i64(value, other.value)) + } + + public fun infix*= (_ other: Self) inout { + &self.value = Builtin.mul_i64(value, other.value) + } + +} + +public conformance UInt64: BinaryInteger { + + public init() { + &self.value = Builtin.zeroinitializer_i64() + } + + public init(truncating_or_extending source: T) { + let w = source.words() + &self.value = Builtin.trunc_word_i64(w[w.start_position()].value) + } + + public fun instance_bit_width() -> Int { + 32 + } + + public fun signum() -> Int { + Int(value: Builtin.zext_i1_word((self > 0).value)) + } + + public fun trailing_zeros() -> Int { + Int(value: Builtin.zext_i64_word(Builtin.cttz_i64(value))) + } + + public fun quotient_and_remainder(dividing_by other: Self) -> {quotient: Self, remainder: Self} { + (quotient: self / other, remainder: self % other) + } + + public fun words() -> CollectionOfOne { + // TODO: Handle the case where UInt64 is larger than UInt. + CollectionOfOne(UInt(value: Builtin.zext_i64_word(value))) + } + + public fun infix/ (_ other: Self) -> Self { + UInt64(value: Builtin.udiv_i64(value, other.value)) + } + + public fun infix/= (_ other: Self) inout { + &self.value = Builtin.udiv_i64(value, other.value) + } + + public fun infix% (_ other: Self) -> Self { + UInt64(value: Builtin.urem_i64(value, other.value)) + } + + public fun infix%= (_ other: Self) inout { + &self.value = Builtin.urem_i64(value, other.value) + } + + public fun infix& (_ other: Self) -> Self { + UInt64(value: Builtin.and_i64(value, other.value)) + } + + public fun infix&= (_ other: Self) inout { + &self.value = Builtin.and_i64(value, other.value) + } + + public fun infix| (_ other: Self) -> Self { + UInt64(value: Builtin.or_i64(value, other.value)) + } + + public fun infix|= (_ other: Self) inout { + &self.value = Builtin.or_i64(value, other.value) + } + + public fun infix^ (_ other: Self) -> Self { + UInt64(value: Builtin.xor_i64(value, other.value)) + } + + public fun infix^= (_ other: Self) inout { + &self.value = Builtin.xor_i64(value, other.value) + } + + public fun infix<< (_ n: Int) -> Self { + if n >= 0 { + return if n < Self.bit_width() { self &<< n } else { 0 } + } else if n <= -Self.bit_width() { + return 0 + } else { + return self &>> -n + } + } + + public fun infix<<= (_ n: Int) inout { + &self = self << n + } + + public fun infix>> (_ n: Int) -> Self { + if n >= 0 { + return if n < Self.bit_width() { self &>> n } else { 0 } + } else if n <= -Self.bit_width() { + return 0 + } else { + return self &<< -n + } + } + + public fun infix>>= (_ n: Int) inout { + &self = self >> n + } + + public static fun is_signed() -> Bool { + false + } + +} + +public conformance UInt64: FixedWidthInteger { + + public fun matches(_ mask: Self) -> Bool { + (self & mask) == mask + } + + public fun adding_reporting_overflow(_ other: Self) -> {partial_value: Self, overflow: Bool} { + let r = Builtin.uadd_with_overflow_i64(value, other.value) + return (partial_value: UInt64(value: r.0), overflow: Bool(value: r.1)) + } + + public fun subtracting_reporting_overflow( + _ other: Self + ) -> {partial_value: Self, overflow: Bool} { + let r = Builtin.usub_with_overflow_i64(value, other.value) + return (partial_value: UInt64(value: r.0), overflow: Bool(value: r.1)) + } + + public fun multiplied_reporting_overflow( + by other: Self + ) -> {partial_value: Self, overflow: Bool} { + let r = Builtin.umul_with_overflow_i64(value, other.value) + return (partial_value: UInt64(value: r.0), overflow: Bool(value: r.1)) + } + + public fun divided_reporting_overflow(by other: Self) -> {partial_value: Self, overflow: Bool} { + if other == 0 { + (partial_value: self.copy(), overflow: true) + } else { + (partial_value: UInt64(value: Builtin.udiv_i64(value, other.value)), overflow: false) + } + } + + public fun remainder_reporting_overflow( + dividing_by other: Self + ) -> {partial_value: Self, overflow: Bool} { + if other == 0 { + (partial_value: self.copy(), overflow: true) + } else { + (partial_value: UInt64(value: Builtin.urem_i64(value, other.value)), overflow: false) + } + } + + public fun nonzero_bit_count() -> Int { + Int(value: Builtin.zext_i64_word(Builtin.ctpop_i64(value))) + } + + public fun leading_zeros() -> Int { + Int(value: Builtin.zext_i64_word(Builtin.ctlz_i64(value))) + } + + public fun infix&<< (_ n: Int) -> Self { + var lhs = self.copy() + lhs &<<= n + return lhs + } + + public fun infix&<<= (_ n: Int) inout { + &self.value = Builtin.shl_i64(value, UInt64(truncating_or_extending: n).value) + } + + public fun infix&>> (_ n: Int) -> Self { + var lhs = self.copy() + lhs &>>= n + return lhs + } + + public fun infix&>>= (_ n: Int) inout { + &self.value = Builtin.lshr_i64(value, UInt64(truncating_or_extending: n).value) + } + + public static fun bit_width() -> Int { + 32 + } + + public static fun max() -> Self { + 4294967295 + } + + public static fun min() -> Self { + 0 + } + +}