From c94098c2ab550425075509e3031bacc3a588a6db Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Thu, 9 Dec 2021 16:14:04 +0100 Subject: [PATCH 01/14] [math/big] Fix int_set and int_get. --- core/math/big/common.odin | 13 +++---- core/math/big/internal.odin | 67 ++++++++++++++++++++++++------------- tests/core/math/big/test.py | 27 +++++++++------ 3 files changed, 66 insertions(+), 41 deletions(-) diff --git a/core/math/big/common.odin b/core/math/big/common.odin index 5b7d162bc..c9aab4afa 100644 --- a/core/math/big/common.odin +++ b/core/math/big/common.odin @@ -158,13 +158,14 @@ Error :: enum int { Invalid_Pointer = 2, Invalid_Argument = 3, - Assignment_To_Immutable = 4, - Max_Iterations_Reached = 5, - Buffer_Overflow = 6, - Integer_Overflow = 7, + Assignment_To_Immutable = 10, + Max_Iterations_Reached = 11, + Buffer_Overflow = 12, + Integer_Overflow = 13, + Integer_Underflow = 14, - Division_by_Zero = 8, - Math_Domain_Error = 9, + Division_by_Zero = 30, + Math_Domain_Error = 31, Cannot_Open_File = 50, Cannot_Read_File = 51, diff --git a/core/math/big/internal.odin b/core/math/big/internal.odin index 4702e76a3..abe592f9b 100644 --- a/core/math/big/internal.odin +++ b/core/math/big/internal.odin @@ -34,6 +34,10 @@ package math_big import "core:mem" import "core:intrinsics" import rnd "core:math/rand" +import "core:builtin" + +import "core:fmt" +__ :: fmt /* Low-level addition, unsigned. Handbook of Applied Cryptography, algorithm 14.7. @@ -1880,8 +1884,6 @@ internal_int_set_from_integer :: proc(dest: ^Int, src: $T, minimize := false, al where intrinsics.type_is_integer(T) { context.allocator = allocator - src := src - internal_error_if_immutable(dest) or_return /* Most internal procs asssume an Int to have already been initialize, @@ -1892,13 +1894,27 @@ internal_int_set_from_integer :: proc(dest: ^Int, src: $T, minimize := false, al dest.flags = {} // We're not -Inf, Inf, NaN or Immutable. dest.used = 0 - dest.sign = .Zero_or_Positive if src >= 0 else .Negative - src = internal_abs(src) + dest.sign = .Negative if src < 0 else .Zero_or_Positive - #no_bounds_check for src != 0 { - dest.digit[dest.used] = DIGIT(src) & _MASK + temp := src + + is_maximally_negative := src == min(T) + if is_maximally_negative { + /* + Prevent overflow on abs() + */ + temp += 1 + } + temp = -temp if temp < 0 else temp + + #no_bounds_check for temp != 0 { + dest.digit[dest.used] = DIGIT(temp) & _MASK dest.used += 1 - src >>= _DIGIT_BITS + temp >>= _DIGIT_BITS + } + + if is_maximally_negative { + return internal_sub(dest, dest, 1) } internal_zero_unused(dest) return nil @@ -2307,28 +2323,31 @@ internal_int_get_i32 :: proc(a: ^Int) -> (res: i32, err: Error) { } internal_get_i32 :: proc { internal_int_get_i32, } -/* - TODO: Think about using `count_bits` to check if the value could be returned completely, - and maybe return max(T), .Integer_Overflow if not? -*/ internal_int_get :: proc(a: ^Int, $T: typeid) -> (res: T, err: Error) where intrinsics.type_is_integer(T) { - size_in_bits := int(size_of(T) * 8) - i := int((size_in_bits + _DIGIT_BITS - 1) / _DIGIT_BITS) - i = min(int(a.used), i) - - #no_bounds_check for ; i >= 0; i -= 1 { - res <<= uint(0) if size_in_bits <= _DIGIT_BITS else _DIGIT_BITS - res |= T(a.digit[i]) - if size_in_bits <= _DIGIT_BITS { - break + /* + Calculate target bit size. + */ + target_bit_size := int(size_of(T) * 8) + when !intrinsics.type_is_unsigned(T) { + if a.sign == .Zero_or_Positive { + target_bit_size -= 1 } } + bits_used := internal_count_bits(a) + + if bits_used > target_bit_size { + if a.sign == .Negative { + return min(T), .Integer_Underflow + } + return max(T), .Integer_Overflow + } + + for i := a.used; i > 0; i -= 1 { + res <<= _DIGIT_BITS + res |= T(a.digit[i - 1]) + } when !intrinsics.type_is_unsigned(T) { - /* - Mask off sign bit. - */ - res ~= 1 << uint(size_in_bits - 1) /* Set the sign. */ diff --git a/tests/core/math/big/test.py b/tests/core/math/big/test.py index 6b17336bc..629e76e6e 100644 --- a/tests/core/math/big/test.py +++ b/tests/core/math/big/test.py @@ -127,17 +127,22 @@ def we_iterate(): # Error enum values # class Error(Enum): - Okay = 0 - Out_Of_Memory = 1 - Invalid_Pointer = 2 - Invalid_Argument = 3 - Unknown_Error = 4 - Max_Iterations_Reached = 5 - Buffer_Overflow = 6 - Integer_Overflow = 7 - Division_by_Zero = 8 - Math_Domain_Error = 9 - Unimplemented = 127 + Okay = 0 + Out_Of_Memory = 1 + Invalid_Pointer = 2 + Invalid_Argument = 3 + Unknown_Error = 4 + Assignment_To_Immutable = 10 + Max_Iterations_Reached = 11 + Buffer_Overflow = 12 + Integer_Overflow = 13 + Integer_Underflow = 14 + Division_by_Zero = 30 + Math_Domain_Error = 31 + Cannot_Open_File = 50 + Cannot_Read_File = 51 + Cannot_Write_File = 52 + Unimplemented = 127 # # Disable garbage collection From b2b79b86f00a229cc9d9cc0226ffb594a4e4d910 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Thu, 9 Dec 2021 16:31:54 +0100 Subject: [PATCH 02/14] [math/big] Return 0, .Integer_Underflow if trying to get a negative number to an unsigned int. --- core/math/big/internal.odin | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/core/math/big/internal.odin b/core/math/big/internal.odin index abe592f9b..70914228e 100644 --- a/core/math/big/internal.odin +++ b/core/math/big/internal.odin @@ -2332,7 +2332,12 @@ internal_int_get :: proc(a: ^Int, $T: typeid) -> (res: T, err: Error) where intr if a.sign == .Zero_or_Positive { target_bit_size -= 1 } + } else { + if a.sign == .Negative { + return 0, .Integer_Underflow + } } + bits_used := internal_count_bits(a) if bits_used > target_bit_size { From 1e9b30666fc9ff462877b413a92d15d11870ec35 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 9 Dec 2021 15:34:17 +0000 Subject: [PATCH 03/14] Minor style change --- core/math/big/common.odin | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/core/math/big/common.odin b/core/math/big/common.odin index 5b7d162bc..d534cc90e 100644 --- a/core/math/big/common.odin +++ b/core/math/big/common.odin @@ -215,7 +215,7 @@ _MIN_DIGIT_COUNT :: max(3, ((size_of(u128) + _DIGIT_BITS) - 1) / _DIGIT_BITS) /* Maximum number of digits. - Must be small enough such that `_bit_count` does not overflow. - - Must be small enough such that `_radix_size` for base 2 does not overflow. + - Must be small enough such that `_radix_size` for base 2 does not overflow. `_radix_size` needs two additional bytes for zero termination and sign. */ _MAX_BIT_COUNT :: (max(int) - 2) @@ -251,7 +251,7 @@ Order :: enum i8 { } Endianness :: enum i8 { - Little = -1, - Platform = 0, - Big = 1, -}; \ No newline at end of file + Little = -1, + Platform = 0, + Big = 1, +} \ No newline at end of file From 1e17d5d86f135d1c1f004a475a4700bd082773ba Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 9 Dec 2021 15:34:35 +0000 Subject: [PATCH 04/14] Add utility procedures to get low values --- core/math/big/internal.odin | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/core/math/big/internal.odin b/core/math/big/internal.odin index 4702e76a3..a5e1e7ba3 100644 --- a/core/math/big/internal.odin +++ b/core/math/big/internal.odin @@ -2307,6 +2307,35 @@ internal_int_get_i32 :: proc(a: ^Int) -> (res: i32, err: Error) { } internal_get_i32 :: proc { internal_int_get_i32, } +internal_get_low_u32 :: proc(a: ^Int) -> u32 #no_bounds_check { + if a == nil { + return 0 + } + + if a.used == 0 { + return 0 + } + + return u32(a.digit[0]) +} +internal_get_low_u64 :: proc(a: ^Int) -> u64 #no_bounds_check { + if a == nil { + return 0 + } + + if a.used == 0 { + return 0 + } + + v := u64(a.digit[0]) + when size_of(DIGIT) == 4 { + if a.used > 1 { + return u64(a.digit[1])<<32 | v + } + } + return v +} + /* TODO: Think about using `count_bits` to check if the value could be returned completely, and maybe return max(T), .Integer_Overflow if not? From 1d7c9cf87223971620e1812ca3c84c9581a1c43a Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 9 Dec 2021 15:35:00 +0000 Subject: [PATCH 05/14] Make `strconv` more robust --- core/strconv/strconv.odin | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/strconv/strconv.odin b/core/strconv/strconv.odin index 6b3a91b4c..6ea8b39e6 100644 --- a/core/strconv/strconv.odin +++ b/core/strconv/strconv.odin @@ -882,7 +882,9 @@ unquote_string :: proc(lit: string, allocator := context.allocator) -> (res: str return -1 } - assert(len(lit) >= 2) + if len(lit) < 2 { + return + } if lit[0] == '`' { return lit[1:len(lit)-1], false, true } From e2f53ee107862d7c5b7dca6df3e68add74273336 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 11 Dec 2021 12:02:23 +0000 Subject: [PATCH 06/14] Fix #1362 `strings.index_any` --- core/strings/strings.odin | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/strings/strings.odin b/core/strings/strings.odin index a8199e0cf..7a9744d83 100644 --- a/core/strings/strings.odin +++ b/core/strings/strings.odin @@ -504,8 +504,8 @@ index_any :: proc(s, chars: string) -> int { } } - for c in chars { - if i := index_rune(s, c); i >= 0 { + for c in s { + if i := index_rune(chars, c); i >= 0 { return i } } From 85f8c8df91022fae3fef996cd45b9e631e6b2a66 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 11 Dec 2021 12:04:34 +0000 Subject: [PATCH 07/14] Fix `fields_proc` in `strings` and `bytes` --- core/bytes/bytes.odin | 2 +- core/strings/strings.odin | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/bytes/bytes.odin b/core/bytes/bytes.odin index cbc1e2506..1e83b93c8 100644 --- a/core/bytes/bytes.odin +++ b/core/bytes/bytes.odin @@ -1143,7 +1143,7 @@ fields_proc :: proc(s: []byte, f: proc(rune) -> bool, allocator := context.alloc } if start >= 0 { - append(&subslices, s[start : end]) + append(&subslices, s[start : len(s)]) } return subslices[:] diff --git a/core/strings/strings.odin b/core/strings/strings.odin index 7a9744d83..72f29e5d6 100644 --- a/core/strings/strings.odin +++ b/core/strings/strings.odin @@ -1288,7 +1288,7 @@ fields_proc :: proc(s: string, f: proc(rune) -> bool, allocator := context.alloc } if start >= 0 { - append(&substrings, s[start : end]) + append(&substrings, s[start : len(s)]) } return substrings[:] From 84b84d9f7de0d17e74ca0b482f784497b509c282 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 11 Dec 2021 12:47:05 +0000 Subject: [PATCH 08/14] Fix `rat_set_f64` --- core/math/big/rat.odin | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/core/math/big/rat.odin b/core/math/big/rat.odin index 837af6fd3..c3efc30aa 100644 --- a/core/math/big/rat.odin +++ b/core/math/big/rat.odin @@ -42,9 +42,9 @@ rat_set_f64 :: proc(dst: ^Rat, f: f64, allocator := context.allocator) -> (err: dst.a.sign = .Negative if f < 0 else .Zero_or_Positive if shift > 0 { - internal_int_shl_digit(&dst.b, shift) or_return + internal_int_shl(&dst.b, &dst.b, shift) or_return } else { - internal_int_shl_digit(&dst.a, -shift) or_return + internal_int_shl(&dst.a, &dst.a, -shift) or_return } return internal_rat_norm(dst) @@ -389,9 +389,9 @@ internal_rat_to_float :: proc($T: typeid, z: ^Rat, allocator := context.allocato internal_int_abs(b2, b) or_return if shift := MSIZE2 - exp; shift > 0 { - internal_int_shl_digit(a2, shift) or_return - } else { - internal_int_shl_digit(b2, -shift) or_return + internal_int_shl(a2, a2, shift) or_return + } else if shift < 0 { + internal_int_shl(b2, b2, -shift) or_return } q, r := &Int{}, &Int{} From 938744b2760193ff1dffc8ae03c740e91a4dfec5 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sat, 11 Dec 2021 15:22:24 +0100 Subject: [PATCH 09/14] [math/big] Rename `internal_int_shl_digit` to `_private_int_shl_leg`. Same for the SHR variant. These are pure implementation details to shift by a leg/word at a time. Prevent accidental usage. --- core/math/big/internal.odin | 74 +-------------------- core/math/big/logical.odin | 33 +--------- core/math/big/private.odin | 118 +++++++++++++++++++++++++++------- tests/core/math/big/test.odin | 8 +-- tests/core/math/big/test.py | 32 ++++----- 5 files changed, 116 insertions(+), 149 deletions(-) diff --git a/core/math/big/internal.odin b/core/math/big/internal.odin index 69497b150..437f6e5fc 100644 --- a/core/math/big/internal.odin +++ b/core/math/big/internal.odin @@ -2648,7 +2648,7 @@ internal_int_shrmod :: proc(quotient, remainder, numerator: ^Int, bits: int, all Shift by as many digits in the bit count. */ if bits >= _DIGIT_BITS { - internal_shr_digit(quotient, bits / _DIGIT_BITS) or_return + _private_int_shr_leg(quotient, bits / _DIGIT_BITS) or_return } /* @@ -2687,37 +2687,6 @@ internal_int_shr :: proc(dest, source: ^Int, bits: int, allocator := context.all } internal_shr :: proc { internal_int_shr, } -/* - Shift right by `digits` * _DIGIT_BITS bits. -*/ -internal_int_shr_digit :: proc(quotient: ^Int, digits: int, allocator := context.allocator) -> (err: Error) { - context.allocator = allocator - - if digits <= 0 { return nil } - - /* - If digits > used simply zero and return. - */ - if digits > quotient.used { return internal_zero(quotient) } - - /* - Much like `int_shl_digit`, this is implemented using a sliding window, - except the window goes the other way around. - - b-2 | b-1 | b0 | b1 | b2 | ... | bb | ----> - /\ | ----> - \-------------------/ ----> - */ - - #no_bounds_check for x := 0; x < (quotient.used - digits); x += 1 { - quotient.digit[x] = quotient.digit[x + digits] - } - quotient.used -= digits - internal_zero_unused(quotient) - return internal_clamp(quotient) -} -internal_shr_digit :: proc { internal_int_shr_digit, } - /* Shift right by a certain bit count with sign extension. */ @@ -2756,7 +2725,7 @@ internal_int_shl :: proc(dest, src: ^Int, bits: int, allocator := context.alloca Shift by as many digits in the bit count as we have. */ if bits >= _DIGIT_BITS { - internal_shl_digit(dest, bits / _DIGIT_BITS) or_return + _private_int_shl_leg(dest, bits / _DIGIT_BITS) or_return } /* @@ -2786,45 +2755,6 @@ internal_int_shl :: proc(dest, src: ^Int, bits: int, allocator := context.alloca } internal_shl :: proc { internal_int_shl, } - -/* - Shift left by `digits` * _DIGIT_BITS bits. -*/ -internal_int_shl_digit :: proc(quotient: ^Int, digits: int, allocator := context.allocator) -> (err: Error) { - context.allocator = allocator - - if digits <= 0 { return nil } - - /* - No need to shift a zero. - */ - if #force_inline internal_is_zero(quotient) { - return nil - } - - /* - Resize `quotient` to accomodate extra digits. - */ - #force_inline internal_grow(quotient, quotient.used + digits) or_return - - /* - Increment the used by the shift amount then copy upwards. - */ - - /* - Much like `int_shr_digit`, this is implemented using a sliding window, - except the window goes the other way around. - */ - #no_bounds_check for x := quotient.used; x > 0; x -= 1 { - quotient.digit[x+digits-1] = quotient.digit[x-1] - } - - quotient.used += digits - mem.zero_slice(quotient.digit[:digits]) - return nil -} -internal_shl_digit :: proc { internal_int_shl_digit, } - /* Count bits in an `Int`. Assumes `a` not to be `nil` and to have been initialized. diff --git a/core/math/big/logical.odin b/core/math/big/logical.odin index dbcf566c8..e7e55cc47 100644 --- a/core/math/big/logical.odin +++ b/core/math/big/logical.odin @@ -86,21 +86,6 @@ int_shr :: proc(dest, source: ^Int, bits: int, allocator := context.allocator) - } shr :: proc { int_shr, } -/* - Shift right by `digits` * _DIGIT_BITS bits. -*/ -int_shr_digit :: proc(quotient: ^Int, digits: int, allocator := context.allocator) -> (err: Error) { - /* - Check that `quotient` is usable. - */ - assert_if_nil(quotient) - context.allocator = allocator - - internal_clear_if_uninitialized(quotient) or_return - return #force_inline internal_int_shr_digit(quotient, digits) -} -shr_digit :: proc { int_shr_digit, } - /* Shift right by a certain bit count with sign extension. */ @@ -124,20 +109,4 @@ int_shl :: proc(dest, src: ^Int, bits: int, allocator := context.allocator) -> ( internal_clear_if_uninitialized(dest, src) or_return return #force_inline internal_int_shl(dest, src, bits) } -shl :: proc { int_shl, } - - -/* - Shift left by `digits` * _DIGIT_BITS bits. -*/ -int_shl_digit :: proc(quotient: ^Int, digits: int, allocator := context.allocator) -> (err: Error) { - /* - Check that `quotient` is usable. - */ - assert_if_nil(quotient) - context.allocator = allocator - - internal_clear_if_uninitialized(quotient) or_return - return #force_inline internal_int_shl_digit(quotient, digits) -} -shl_digit :: proc { int_shl_digit, }; \ No newline at end of file +shl :: proc { int_shl, } \ No newline at end of file diff --git a/core/math/big/private.odin b/core/math/big/private.odin index 14a27f600..9989a208a 100644 --- a/core/math/big/private.odin +++ b/core/math/big/private.odin @@ -211,12 +211,12 @@ _private_int_mul_toom :: proc(dest, a, b: ^Int, allocator := context.allocator) /* P = b1*x^4+ S2*x^3+ S1*x^2+ a1*x + a0; */ - internal_shl_digit(b1, 4 * B) or_return - internal_shl_digit(S2, 3 * B) or_return + _private_int_shl_leg(b1, 4 * B) or_return + _private_int_shl_leg(S2, 3 * B) or_return internal_add(b1, b1, S2) or_return - internal_shl_digit(S1, 2 * B) or_return + _private_int_shl_leg(S1, 2 * B) or_return internal_add(b1, b1, S1) or_return - internal_shl_digit(a1, 1 * B) or_return + _private_int_shl_leg(a1, 1 * B) or_return internal_add(b1, b1, a1) or_return internal_add(dest, b1, a0) or_return @@ -317,8 +317,8 @@ _private_int_mul_karatsuba :: proc(dest, a, b: ^Int, allocator := context.alloca /* shift by B. */ - internal_shl_digit(t1, B) or_return /* t1 = (x0y0 + x1y1 - (x1-x0)*(y1-y0))<= n then x = x - n @@ -2026,7 +2026,7 @@ _private_int_reduce :: proc(x, m, mu: ^Int, allocator := context.allocator) -> ( /* q1 = x / b**(k-1) */ - internal_shr_digit(q, um - 1) + _private_int_shr_leg(q, um - 1) /* According to HAC this optimization is ok. @@ -2040,7 +2040,7 @@ _private_int_reduce :: proc(x, m, mu: ^Int, allocator := context.allocator) -> ( /* q3 = q2 / b**(k+1) */ - internal_shr_digit(q, um + 1) + _private_int_shr_leg(q, um + 1) /* x = x mod b**(k+1), quick (no division) @@ -2062,7 +2062,7 @@ _private_int_reduce :: proc(x, m, mu: ^Int, allocator := context.allocator) -> ( */ if internal_is_negative(x) { internal_set(q, 1) or_return - internal_shl_digit(q, um + 1) or_return + _private_int_shl_leg(q, um + 1) or_return internal_add(x, x, q) or_return } @@ -3192,6 +3192,74 @@ _private_copy_digits :: proc(dest, src: ^Int, digits: int, offset := int(0)) -> return nil } + +/* + Shift left by `digits` * _DIGIT_BITS bits. +*/ +_private_int_shl_leg :: proc(quotient: ^Int, digits: int, allocator := context.allocator) -> (err: Error) { + context.allocator = allocator + + if digits <= 0 { return nil } + + /* + No need to shift a zero. + */ + if #force_inline internal_is_zero(quotient) { + return nil + } + + /* + Resize `quotient` to accomodate extra digits. + */ + #force_inline internal_grow(quotient, quotient.used + digits) or_return + + /* + Increment the used by the shift amount then copy upwards. + */ + + /* + Much like `_private_int_shr_leg`, this is implemented using a sliding window, + except the window goes the other way around. + */ + #no_bounds_check for x := quotient.used; x > 0; x -= 1 { + quotient.digit[x+digits-1] = quotient.digit[x-1] + } + + quotient.used += digits + mem.zero_slice(quotient.digit[:digits]) + return nil +} + +/* + Shift right by `digits` * _DIGIT_BITS bits. +*/ +_private_int_shr_leg :: proc(quotient: ^Int, digits: int, allocator := context.allocator) -> (err: Error) { + context.allocator = allocator + + if digits <= 0 { return nil } + + /* + If digits > used simply zero and return. + */ + if digits > quotient.used { return internal_zero(quotient) } + + /* + Much like `int_shl_digit`, this is implemented using a sliding window, + except the window goes the other way around. + + b-2 | b-1 | b0 | b1 | b2 | ... | bb | ----> + /\ | ----> + \-------------------/ ----> + */ + + #no_bounds_check for x := 0; x < (quotient.used - digits); x += 1 { + quotient.digit[x] = quotient.digit[x + digits] + } + quotient.used -= digits + internal_zero_unused(quotient) + return internal_clamp(quotient) +} + /* ======================== End of private procedures ======================= diff --git a/tests/core/math/big/test.odin b/tests/core/math/big/test.odin index 07fa0364b..81f1956dc 100644 --- a/tests/core/math/big/test.odin +++ b/tests/core/math/big/test.odin @@ -208,7 +208,7 @@ print_to_buffer :: proc(val: ^big.Int) -> cstring { /* dest = shr_digit(src, digits) */ -@export test_shr_digit :: proc "c" (source: cstring, digits: int) -> (res: PyRes) { +@export test_shr_leg :: proc "c" (source: cstring, digits: int) -> (res: PyRes) { context = runtime.default_context() err: big.Error @@ -216,7 +216,7 @@ print_to_buffer :: proc(val: ^big.Int) -> cstring { defer big.internal_destroy(src) if err = big.atoi(src, string(source), 16); err != nil { return PyRes{res=":shr_digit:atoi(src):", err=err} } - if err = #force_inline big.internal_shr_digit(src, digits); err != nil { return PyRes{res=":shr_digit:shr_digit(src):", err=err} } + if err = #force_inline big._private_int_shr_leg(src, digits); err != nil { return PyRes{res=":shr_digit:shr_digit(src):", err=err} } r := print_to_buffer(src) return PyRes{res = r, err = nil} @@ -225,7 +225,7 @@ print_to_buffer :: proc(val: ^big.Int) -> cstring { /* dest = shl_digit(src, digits) */ -@export test_shl_digit :: proc "c" (source: cstring, digits: int) -> (res: PyRes) { +@export test_shl_leg :: proc "c" (source: cstring, digits: int) -> (res: PyRes) { context = runtime.default_context() err: big.Error @@ -233,7 +233,7 @@ print_to_buffer :: proc(val: ^big.Int) -> cstring { defer big.internal_destroy(src) if err = big.atoi(src, string(source), 16); err != nil { return PyRes{res=":shl_digit:atoi(src):", err=err} } - if err = #force_inline big.internal_shl_digit(src, digits); err != nil { return PyRes{res=":shl_digit:shr_digit(src):", err=err} } + if err = #force_inline big._private_int_shl_leg(src, digits); err != nil { return PyRes{res=":shl_digit:shr_digit(src):", err=err} } r := print_to_buffer(src) return PyRes{res = r, err = nil} diff --git a/tests/core/math/big/test.py b/tests/core/math/big/test.py index 629e76e6e..d292a3ff4 100644 --- a/tests/core/math/big/test.py +++ b/tests/core/math/big/test.py @@ -187,8 +187,8 @@ int_sqrt = load(l.test_sqrt, [c_char_p ], Res) int_root_n = load(l.test_root_n, [c_char_p, c_longlong], Res) # Logical operations -int_shl_digit = load(l.test_shl_digit, [c_char_p, c_longlong], Res) -int_shr_digit = load(l.test_shr_digit, [c_char_p, c_longlong], Res) +int_shl_leg = load(l.test_shl_leg, [c_char_p, c_longlong], Res) +int_shr_leg = load(l.test_shr_leg, [c_char_p, c_longlong], Res) int_shl = load(l.test_shl, [c_char_p, c_longlong], Res) int_shr = load(l.test_shr, [c_char_p, c_longlong], Res) int_shr_signed = load(l.test_shr_signed, [c_char_p, c_longlong], Res) @@ -402,17 +402,17 @@ def test_root_n(number = 0, root = 0, expected_error = Error.Okay): return test("test_root_n", res, [number, root], expected_error, expected_result) -def test_shl_digit(a = 0, digits = 0, expected_error = Error.Okay): +def test_shl_leg(a = 0, digits = 0, expected_error = Error.Okay): args = [arg_to_odin(a), digits] - res = int_shl_digit(*args) + res = int_shl_leg(*args) expected_result = None if expected_error == Error.Okay: expected_result = a << (digits * 60) - return test("test_shl_digit", res, [a, digits], expected_error, expected_result) + return test("test_shl_leg", res, [a, digits], expected_error, expected_result) -def test_shr_digit(a = 0, digits = 0, expected_error = Error.Okay): +def test_shr_leg(a = 0, digits = 0, expected_error = Error.Okay): args = [arg_to_odin(a), digits] - res = int_shr_digit(*args) + res = int_shr_leg(*args) expected_result = None if expected_error == Error.Okay: if a < 0: @@ -421,7 +421,7 @@ def test_shr_digit(a = 0, digits = 0, expected_error = Error.Okay): else: expected_result = a >> (digits * 60) - return test("test_shr_digit", res, [a, digits], expected_error, expected_result) + return test("test_shr_leg", res, [a, digits], expected_error, expected_result) def test_shl(a = 0, bits = 0, expected_error = Error.Okay): args = [arg_to_odin(a), bits] @@ -556,12 +556,12 @@ TESTS = { test_root_n: [ [ 1298074214633706907132624082305024, 2, Error.Okay, ], ], - test_shl_digit: [ + test_shl_leg: [ [ 3192, 1 ], [ 1298074214633706907132624082305024, 2 ], [ 1024, 3 ], ], - test_shr_digit: [ + test_shr_leg: [ [ 3680125442705055547392, 1 ], [ 1725436586697640946858688965569256363112777243042596638790631055949824, 2 ], [ 219504133884436710204395031992179571, 2 ], @@ -619,10 +619,10 @@ total_failures = 0 # test_shr_signed also tests shr, so we're not going to test shr randomly. # RANDOM_TESTS = [ - test_add, test_sub, test_mul, test_sqr, test_div, - test_log, test_pow, test_sqrt, test_root_n, - test_shl_digit, test_shr_digit, test_shl, test_shr_signed, - test_gcd, test_lcm, test_is_square, + test_add, test_sub, test_mul, test_sqr, + test_log, test_pow, test_sqrt, test_root_n, + test_shl_leg, test_shr_leg, test_shl, test_shr_signed, + test_gcd, test_lcm, test_is_square, test_div, ] SKIP_LARGE = [ test_pow, test_root_n, # test_gcd, @@ -719,9 +719,9 @@ if __name__ == '__main__': a = randint(1, 1 << BITS) b = TEST_ROOT_N_PARAMS[index] index = (index + 1) % len(TEST_ROOT_N_PARAMS) - elif test_proc == test_shl_digit: + elif test_proc == test_shl_leg: b = randint(0, 10); - elif test_proc == test_shr_digit: + elif test_proc == test_shr_leg: a = abs(a) b = randint(0, 10); elif test_proc == test_shl: From b7c78da1fbabca7288088e0ce257c06c058bcf73 Mon Sep 17 00:00:00 2001 From: Rehkitzdev Date: Sat, 11 Dec 2021 18:38:32 +0100 Subject: [PATCH 10/14] Fix storeInt call in webgl glue code --- vendor/wasm/WebGL/runtime.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/vendor/wasm/WebGL/runtime.js b/vendor/wasm/WebGL/runtime.js index b6994a8ec..18b540b5c 100644 --- a/vendor/wasm/WebGL/runtime.js +++ b/vendor/wasm/WebGL/runtime.js @@ -416,7 +416,7 @@ class WebGLInterface { log = log.substring(0, n); this.mem.loadBytes(buf_ptr, buf_len).set(new TextEncoder("utf-8").encode(log)) - storeInt(length_ptr, n); + this.mem.storeInt(length_ptr, n); } }, GetShaderInfoLog: (shader, buf_ptr, buf_len, length_ptr) => { @@ -429,7 +429,7 @@ class WebGLInterface { log = log.substring(0, n); this.mem.loadBytes(buf_ptr, buf_len).set(new TextEncoder("utf-8").encode(log)) - storeInt(length_ptr, n); + this.mem.storeInt(length_ptr, n); } }, GetShaderiv: (shader, pname, p) => { @@ -439,11 +439,11 @@ class WebGLInterface { if (log === null) { log = "(unknown error)"; } - storeInt(p, log.length+1); + this.mem.storeInt(p, log.length+1); } else if (pname == 35720) { let source = this.ctx.getShaderSource(this.shaders[shader]); let sourceLength = (source === null || source.length == 0) ? 0 : source.length+1; - storeInt(p, sourceLength); + this.mem.storeInt(p, sourceLength); } else { let param = this.ctx.getShaderParameter(this.shaders[shader], pname); this.mem.storeI32(p, param); @@ -994,7 +994,7 @@ class WebGLInterface { let n = Math.min(buf_len, name.length); name = name.substring(0, n); this.mem.loadBytes(buf_ptr, buf_len).set(new TextEncoder("utf-8").encode(name)) - storeInt(length_ptr, n); + this.mem.storeInt(length_ptr, n); }, UniformBlockBinding: (program, uniformBlockIndex, uniformBlockBinding) => { this.assertWebGL2(); From 08a081ed45520eac4c4ebd8501fa3ab7970b5c77 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 11 Dec 2021 17:42:58 +0000 Subject: [PATCH 11/14] Improve debug symbol retention with `-debug -opt:0` --- src/llvm_backend_opt.cpp | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/src/llvm_backend_opt.cpp b/src/llvm_backend_opt.cpp index de925655f..5b8468799 100644 --- a/src/llvm_backend_opt.cpp +++ b/src/llvm_backend_opt.cpp @@ -48,12 +48,6 @@ LLVMBool lb_must_preserve_predicate_callback(LLVMValueRef value, void *user_data return LLVMIsAAllocaInst(value) != nullptr; } -void lb_add_must_preserve_predicate_pass(lbModule *m, LLVMPassManagerRef fpm, i32 optimization_level) { - if (false && optimization_level == 0 && m->debug_builder) { - // LLVMAddInternalizePassWithMustPreservePredicate(fpm, m, lb_must_preserve_predicate_callback); - } -} - #if LLVM_VERSION_MAJOR < 12 #define LLVM_ADD_CONSTANT_VALUE_PASS(fpm) LLVMAddConstantPropagationPass(fpm) @@ -61,7 +55,10 @@ void lb_add_must_preserve_predicate_pass(lbModule *m, LLVMPassManagerRef fpm, i3 #define LLVM_ADD_CONSTANT_VALUE_PASS(fpm) #endif -void lb_basic_populate_function_pass_manager(LLVMPassManagerRef fpm) { +void lb_basic_populate_function_pass_manager(LLVMPassManagerRef fpm, i32 optimization_level) { + if (optimization_level == 0 && build_context.ODIN_DEBUG) { + return; + } LLVMAddPromoteMemoryToRegisterPass(fpm); LLVMAddMergedLoadStoreMotionPass(fpm); LLVM_ADD_CONSTANT_VALUE_PASS(fpm); @@ -78,14 +75,12 @@ void lb_populate_function_pass_manager(lbModule *m, LLVMPassManagerRef fpm, bool // TODO(bill): Determine which opt definitions should exist in the first place optimization_level = gb_clamp(optimization_level, 0, 2); - lb_add_must_preserve_predicate_pass(m, fpm, optimization_level); - if (ignore_memcpy_pass) { - lb_basic_populate_function_pass_manager(fpm); + lb_basic_populate_function_pass_manager(fpm, optimization_level); return; } else if (optimization_level == 0) { LLVMAddMemCpyOptPass(fpm); - lb_basic_populate_function_pass_manager(fpm); + lb_basic_populate_function_pass_manager(fpm, optimization_level); return; } @@ -96,7 +91,7 @@ void lb_populate_function_pass_manager(lbModule *m, LLVMPassManagerRef fpm, bool LLVMPassManagerBuilderPopulateFunctionPassManager(pmb, fpm); #else LLVMAddMemCpyOptPass(fpm); - lb_basic_populate_function_pass_manager(fpm); + lb_basic_populate_function_pass_manager(fpm, optimization_level); LLVMAddSCCPPass(fpm); @@ -114,11 +109,9 @@ void lb_populate_function_pass_manager_specific(lbModule *m, LLVMPassManagerRef // TODO(bill): Determine which opt definitions should exist in the first place optimization_level = gb_clamp(optimization_level, 0, 2); - lb_add_must_preserve_predicate_pass(m, fpm, optimization_level); - if (optimization_level == 0) { LLVMAddMemCpyOptPass(fpm); - lb_basic_populate_function_pass_manager(fpm); + lb_basic_populate_function_pass_manager(fpm, optimization_level); return; } @@ -191,6 +184,9 @@ void lb_populate_module_pass_manager(LLVMTargetMachineRef target_machine, LLVMPa // NOTE(bill): Treat -opt:3 as if it was -opt:2 // TODO(bill): Determine which opt definitions should exist in the first place optimization_level = gb_clamp(optimization_level, 0, 2); + if (optimization_level == 0 && build_context.ODIN_DEBUG) { + return; + } LLVMAddAlwaysInlinerPass(mpm); LLVMAddStripDeadPrototypesPass(mpm); From 4423bc0706a6a1a64cf419720fd65bc723fdf58a Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 12 Dec 2021 01:10:40 +0000 Subject: [PATCH 12/14] Fix typo --- core/strings/strings.odin | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/strings/strings.odin b/core/strings/strings.odin index 72f29e5d6..3f703372f 100644 --- a/core/strings/strings.odin +++ b/core/strings/strings.odin @@ -504,8 +504,8 @@ index_any :: proc(s, chars: string) -> int { } } - for c in s { - if i := index_rune(chars, c); i >= 0 { + for c, i in s { + if index_rune(chars, c) >= 0 { return i } } From 4ebdb6740ef041f4b600663b3b597e882c3fc42d Mon Sep 17 00:00:00 2001 From: gilles Date: Thu, 16 Dec 2021 18:20:10 +0100 Subject: [PATCH 13/14] fix math.prod accumulator was not initialized to one --- core/math/math.odin | 1 + 1 file changed, 1 insertion(+) diff --git a/core/math/math.odin b/core/math/math.odin index caaa6f51b..b81598da9 100644 --- a/core/math/math.odin +++ b/core/math/math.odin @@ -1196,6 +1196,7 @@ sum :: proc "contextless" (x: $T/[]$E) -> (res: E) prod :: proc "contextless" (x: $T/[]$E) -> (res: E) where intrinsics.type_is_numeric(E) { + res = 1 for i in x { res *= i } From 0548db423067bce16d45af651819bf56feb5d411 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 17 Dec 2021 11:06:17 +0000 Subject: [PATCH 14/14] Disallow `@(static)` and `@(thread_local)` within `defer` statements --- src/check_stmt.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 1a424240c..396388629 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -2243,6 +2243,9 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { error(e->token, "The 'static' attribute is not allowed to be applied to '_'"); } else { e->flags |= EntityFlag_Static; + if (ctx->in_defer) { + error(e->token, "'static' variables cannot be declared within a defer statement"); + } } } if (ac.thread_local_model != "") { @@ -2251,9 +2254,13 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { error(e->token, "The 'thread_local' attribute is not allowed to be applied to '_'"); } else { e->flags |= EntityFlag_Static; + if (ctx->in_defer) { + error(e->token, "'thread_local' variables cannot be declared within a defer statement"); + } } e->Variable.thread_local_model = ac.thread_local_model; } + if (ac.is_static && ac.thread_local_model != "") { error(e->token, "The 'static' attribute is not needed if 'thread_local' is applied");