diff options
| author | gingerBill <gingerBill@users.noreply.github.com> | 2018-07-28 18:39:15 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2018-07-28 18:39:15 +0100 |
| commit | 8d2c4a78a19774d98f5603d78ab6520f39d18bcd (patch) | |
| tree | 95284815f9ec477819bbfd65e90c7cdae757a878 /src | |
| parent | 1ab40d86009ff569b66650eb35b050a68e11df89 (diff) | |
| parent | 8504ff920b057007382bbeefcaa8a40e35689526 (diff) | |
Merge pull request #238 from odin-lang/big-int
Big int
Diffstat (limited to 'src')
| -rw-r--r-- | src/big_int.cpp | 1408 | ||||
| -rw-r--r-- | src/check_expr.cpp | 247 | ||||
| -rw-r--r-- | src/check_stmt.cpp | 21 | ||||
| -rw-r--r-- | src/check_type.cpp | 31 | ||||
| -rw-r--r-- | src/common.cpp | 46 | ||||
| -rw-r--r-- | src/exact_value.cpp | 98 | ||||
| -rw-r--r-- | src/ir.cpp | 4 | ||||
| -rw-r--r-- | src/ir_print.cpp | 15 | ||||
| -rw-r--r-- | src/main.cpp | 6 | ||||
| -rw-r--r-- | src/parser.cpp | 107 | ||||
| -rw-r--r-- | src/tokenizer.cpp | 10 |
11 files changed, 1727 insertions, 266 deletions
diff --git a/src/big_int.cpp b/src/big_int.cpp new file mode 100644 index 000000000..fada34115 --- /dev/null +++ b/src/big_int.cpp @@ -0,0 +1,1408 @@ +struct BigInt { + union { + u64 word; + u64 *words; + } d; + i32 len; + b32 neg; +}; + + +BigInt const BIG_INT_ZERO = {{0}, 0, false}; +BigInt const BIG_INT_ONE = {{1}, 1, false}; +BigInt const BIG_INT_NEG_ONE = {{1}, 1, true}; + + +gb_global Arena global_big_int_arena = {0}; + +#if defined(GB_COMPILER_MSVC) && defined(GB_ARCH_64_BIT) +// URL(bill): https://stackoverflow.com/questions/8453146/128-bit-division-intrinsic-in-visual-c/8456388#8456388 +u8 udiv128_data[] = { + 0x48, 0x89, 0xD0, // mov rax,rdx + 0x48, 0x89, 0xCA, // mov rdx,rcx + 0x49, 0xF7, 0xF0, // div r8 + 0x49, 0x89, 0x11, // mov [r9],rdx + 0xC3 // ret +}; +u64 (__fastcall *unsafe_udiv128)(u64 numhi, u64 numlo, u64 den, u64* rem) = (u64 (__fastcall *)(u64, u64, u64, u64*))&udiv128_data[0]; +#endif + +void global_big_int_init(void) { + arena_init(&global_big_int_arena, heap_allocator()); + +#if defined(GB_COMPILER_MSVC) && defined(GB_ARCH_64_BIT) + DWORD dummy; + VirtualProtect(udiv128_data, sizeof(udiv128_data), PAGE_EXECUTE_READWRITE, &dummy); +#endif +} + +gb_inline gbAllocator big_int_allocator(void) { + return arena_allocator(&global_big_int_arena); +} + +void big_int_alloc(BigInt *dst, isize word_len, isize word_cap) { + GB_ASSERT_MSG(word_len <= word_cap, "%td %td", word_len, word_cap); + if (word_cap < dst->len) { + dst->len = cast(i32)word_len; + } else { + dst->len = cast(i32)word_len; + dst->d.words = gb_alloc_array(big_int_allocator(), u64, word_cap); + } +} + +void big_int_from_u64(BigInt *dst, u64 x); +void big_int_from_i64(BigInt *dst, i64 x); +void big_int_init (BigInt *dst, BigInt const *src); +void big_int_from_string(BigInt *dst, String const &s); + +BigInt big_int_make(BigInt const *b, bool abs=false); +BigInt big_int_make_abs(BigInt const *b); +BigInt big_int_make_u64(u64 x); +BigInt big_int_make_i64(i64 x); + +u64 big_int_to_u64 (BigInt const *x); +i64 big_int_to_i64 (BigInt const *x); +f64 big_int_to_f64 (BigInt const *x); +String big_int_to_string(gbAllocator allocator, BigInt const *x, u64 base = 10); + +gb_inline u64 const *big_int_ptr(BigInt const *b) { + if (b->len <= 1) { + return &b->d.word; + } + return b->d.words; +} +gb_inline u64 *big_int_ptr(BigInt *b) { + if (b->len <= 1) { + return &b->d.word; + } + return b->d.words; +} + +void big_int_add (BigInt *dst, BigInt const *x, BigInt const *y); +void big_int_sub (BigInt *dst, BigInt const *x, BigInt const *y); +void big_int_shl (BigInt *dst, BigInt const *x, BigInt const *y); +void big_int_shr (BigInt *dst, BigInt const *x, BigInt const *y); +void big_int_mul (BigInt *dst, BigInt const *x, BigInt const *y); +void big_int_mul_u64(BigInt *dst, BigInt const *x, u64 y); + +void big_int_quo_rem(BigInt const *x, BigInt const *y, BigInt *q, BigInt *r); +void big_int_quo (BigInt *z, BigInt const *x, BigInt const *y); +void big_int_rem (BigInt *z, BigInt const *x, BigInt const *y); + +void big_int_euclidean_div(BigInt *z, BigInt const *x, BigInt const *y); +void big_int_euclidean_mod(BigInt *z, BigInt const *x, BigInt const *y); + +void big_int_and (BigInt *dst, BigInt const *x, BigInt const *y); +void big_int_and_not(BigInt *dst, BigInt const *x, BigInt const *y); +void big_int_xor (BigInt *dst, BigInt const *x, BigInt const *y); +void big_int_or (BigInt *dst, BigInt const *x, BigInt const *y); +void big_int_not (BigInt *dst, BigInt const *x); + + +void big_int_add_eq(BigInt *dst, BigInt const *x); +void big_int_sub_eq(BigInt *dst, BigInt const *x); +void big_int_shl_eq(BigInt *dst, BigInt const *x); +void big_int_shr_eq(BigInt *dst, BigInt const *x); +void big_int_mul_eq(BigInt *dst, BigInt const *x); + +void big_int_quo_eq(BigInt *dst, BigInt const *x); +void big_int_rem_eq(BigInt *dst, BigInt const *x); + + +void big_int_add_eq(BigInt *dst, BigInt const *x) { + BigInt res = {}; + big_int_init(&res, dst); + big_int_add(dst, &res, x); +} +void big_int_sub_eq(BigInt *dst, BigInt const *x) { + BigInt res = {}; + big_int_init(&res, dst); + big_int_sub(dst, &res, x); +} +void big_int_shl_eq(BigInt *dst, BigInt const *x) { + BigInt res = {}; + big_int_init(&res, dst); + big_int_shl(dst, &res, x); +} +void big_int_shr_eq(BigInt *dst, BigInt const *x) { + BigInt res = {}; + big_int_init(&res, dst); + big_int_shr(dst, &res, x); +} +void big_int_mul_eq(BigInt *dst, BigInt const *x) { + BigInt res = {}; + big_int_init(&res, dst); + big_int_mul(dst, &res, x); +} +void big_int_quo_eq(BigInt *dst, BigInt const *x) { + BigInt res = {}; + big_int_init(&res, dst); + big_int_quo(dst, &res, x); +} +void big_int_rem_eq(BigInt *dst, BigInt const *x) { + BigInt res = {}; + big_int_init(&res, dst); + big_int_rem(dst, &res, x); +} + + + +void big_int_normalize(BigInt *dst) { + u64 const *words = big_int_ptr(dst); + + i32 count_minus_one = -1; + for (i32 i = 0; i < dst->len; i++) { + u64 word = words[i]; + if (word != 0) { + count_minus_one = i; + } + } + + if (count_minus_one < 0) { + dst->neg = false; + } + dst->len = count_minus_one+1; + if (count_minus_one == 0) { + dst->d.word = words[0]; + } +} + +i64 big_int_sign(BigInt const *x) { + if (x->len == 0) { + return 0; + } + return x->neg ? -1 : +1; +} + + +void big_int_from_u64(BigInt *dst, u64 x) { + if (x == 0) { + dst->len = 0; + dst->neg = false; + } else { + dst->len = 1; + dst->d.word = x; + dst->neg = false; + } +} +void big_int_from_i64(BigInt *dst, i64 x) { + if (x >= 0) { + big_int_from_u64(dst, cast(u64)x); + return; + } else { + dst->len = 1; + dst->d.word = (cast(u64)(-(x+1ll))) + 1ull; + dst->neg = true; + } + +} +void big_int_init(BigInt *dst, BigInt const *src) { + if (dst == src) { + return; + } + if (src->len == 0) { + big_int_from_u64(dst, 0); + return; + } else if (src->len == 1) { + dst->len = 1; + dst->d.word = src->d.word; + dst->neg = src->neg; + return; + } + + + dst->neg = src->neg; + big_int_alloc(dst, src->len, src->len); + u64 const *s = big_int_ptr(src); + gb_memmove(dst->d.words, s, gb_size_of(u64)*dst->len); +} + +BigInt big_int_make(BigInt const *b, bool abs) { + BigInt i = {}; + big_int_init(&i, b); + if (abs) i.neg = false; + return i; +} +BigInt big_int_make_abs(BigInt const *b) { + return big_int_make(b, true); +} +BigInt big_int_alias_abs(BigInt const *b) { + BigInt x = *b; + x.neg = false; + return x; +} + + +BigInt big_int_make_u64(u64 x) { + BigInt i = {}; + big_int_from_u64(&i, x); + return i; +} +BigInt big_int_make_i64(i64 x) { + BigInt i = {}; + big_int_from_i64(&i, x); + return i; +} + + +void big_int_from_string(BigInt *dst, String const &s) { + u64 base = 10; + bool has_prefix = false; + if (s.len > 2 && s[0] == '0') { + switch (s[1]) { + case 'b': base = 2; has_prefix = true; break; + case 'o': base = 8; has_prefix = true; break; + case 'd': base = 10; has_prefix = true; break; + case 'z': base = 12; has_prefix = true; break; + case 'x': base = 16; has_prefix = true; break; + case 'h': base = 16; has_prefix = true; break; + } + } + + u8 *text = s.text; + isize len = s.len; + if (has_prefix) { + text += 2; + len -= 2; + } + + BigInt result = {}; + BigInt val = {}; + BigInt b = {}; + big_int_from_u64(&b, base); + val.len = 1; + val.d.word = 100000000ull; + + // for (isize i = 0; i < len; i++) { + // Rune r = cast(Rune)text[i]; + // if (r == '_') { + // continue; + // } + // u64 v = u64_digit_value(r); + // if (v >= base) { + // break; + // } + // val.d.word = v; + + // big_int_mul_eq(&result, &b); + // big_int_add_eq(&result, &val); + // } + *dst = result; +} + + + +u64 big_int_to_u64(BigInt const *x) { + GB_ASSERT(!x->neg); + switch (x->len) { + case 0: return 0; + case 1: return x->d.word; + } + GB_PANIC("BigInt too big for u64"); + return 0; +} + +i64 big_int_to_i64(BigInt const *x) { + switch (x->len) { + case 0: + return 0; + case 1: + if (x->neg) { + if (x->d.word <= 9223372036854775808ull) { // 2^63 - 1 + return (-cast(i64)(x->d.word-1ull)) - 1ll; + } else { + GB_PANIC("BigInt too big for i64"); + } + } else { + return cast(i64)x->d.word; + } + break; + } + + GB_PANIC("BigInt too big for i64"); + return 0; +} + +f64 big_int_to_f64(BigInt const *x) { + if (x->neg) { + i64 i = big_int_to_i64(x); + return cast(f64)i; + } + u64 u = big_int_to_u64(x); + return cast(f64)u; +} + +bool bi__alias(BigInt const *dst, BigInt const *src) { + if (dst == src) { + return true; + } + if (dst->len > 1 && src->len > 1) { + return dst->d.words == src->d.words; + } + return false; +} + + +void big_int_neg(BigInt *dst, BigInt const *x) { + big_int_init(dst, x); + dst->neg = !dst->neg; + big_int_normalize(dst); +} + + +int big_int_cmp(BigInt const *x, BigInt const *y) { + if (x == y) { + return 0; + } else if (x->neg && !y->neg) { + return -1; + } else if (!x->neg && y->neg) { + return +1; + } else if (x->len > y->len) { + return x->neg ? -1 : +1; + } else if (y->len > x->len) { + return x->neg ? +1 : -1; + } else if (x->len == 0) { + return 0; + } + + u64 const *xd = big_int_ptr(x); + u64 const *yd = big_int_ptr(y); + + for (i32 i = x->len; i >= 0; i--) { + u64 a = xd[i]; + u64 b = yd[i]; + + if (a > b) { + return x->neg ? -1 : +1; + } + if (a < b) { + return x->neg ? +1 : -1; + } + } + + return 0; +} + +int big_int_cmp_zero(BigInt const *x) { + if (x->len == 0) { + return 0; + } + return x->neg ? -1 : +1; +} + +bool big_int_is_zero(BigInt const *x) { + if (x->len == 0) { + return true; + } + return false; +} + + + + +void big_int_add(BigInt *dst, BigInt const *x, BigInt const *y) { + if (x->len == 0) { + big_int_init(dst, y); + return; + } + if (y->len == 0) { + big_int_init(dst, x); + return; + } + + if (x->neg == y->neg) { + dst->neg = x->neg; + + u64 const *x_words = big_int_ptr(x); + u64 const *y_words = big_int_ptr(y); + u64 overflow = cast(u64)add_overflow_u64(x_words[0], y_words[0], &dst->d.word); + if (overflow == 0 && x->len == 1 && y->len == 1) { + dst->len = 1; + big_int_normalize(dst); + return; + } + + u64 first_word = dst->d.word; + big_int_alloc(dst, 0, gb_max(x->len, y->len)+1); + GB_ASSERT(dst->len > 1); + dst->d.words[0] = first_word; + + i32 i = 1; + for (;;) { + u64 v = overflow; + overflow = 0; + + bool found_word = false; + if (i < x->len) { + found_word = true; + overflow += add_overflow_u64(v, x_words[i], &v); + } + if (i < y->len) { + found_word = true; + overflow += add_overflow_u64(v, y_words[i], &v); + } + + dst->d.words[i] = v; + i += 1; + + if (!found_word) { + dst->len = i; + big_int_normalize(dst); + return; + } + } + } else { + BigInt const *pos = nullptr; + BigInt const *neg = nullptr; + if (x->neg) { + neg = x; + pos = y; + } else { + GB_ASSERT(y->neg); + pos = x; + neg = y; + } + + BigInt neg_abs = {}; + big_int_neg(&neg_abs, neg); + BigInt const *bigger = nullptr; + BigInt const *smaller = nullptr; + + int cmp = big_int_cmp(pos, &neg_abs); + dst->neg = cmp < 0; + switch (cmp) { + case 0: + big_int_from_u64(dst, 0); + return; + case -1: + bigger = &neg_abs; + smaller = pos; + break; + case +1: + bigger = pos; + smaller = &neg_abs; + break; + default: + GB_PANIC("Invalid bit_int_cmp value"); + return; + } + + u64 const *bigger_words = big_int_ptr(bigger); + u64 const *smaller_words = big_int_ptr(smaller); + u64 overflow = cast(u64)sub_overflow_u64(bigger_words[0], smaller_words[0], &dst->d.word); + if (overflow == 0 && bigger->len == 1 && smaller->len == 1) { + dst->len = 1; + big_int_normalize(dst); + return; + } + + u64 first_word = dst->d.word; + big_int_alloc(dst, 0, bigger->len); + GB_ASSERT(dst->len > 1); + dst->d.words[0] = first_word; + + i32 i = 0; + while (i < bigger->len) { + u64 v = bigger_words[i]; + u64 prev_overflow = overflow; + overflow = 0; + + bool found_word = false; + if (i < smaller->len) { + found_word = true; + overflow += sub_overflow_u64(v, smaller_words[i], &v); + } + if (sub_overflow_u64(v, prev_overflow, &v)) { + found_word = true; + overflow += 1; + } + dst->d.words[i] = v; + i += 1; + + if (!found_word) { + break; + } + } + + GB_ASSERT(overflow == 0); + dst->len = i; + big_int_normalize(dst); + return; + } +} + + +void big_int_sub(BigInt *dst, BigInt const *x, BigInt const *y) { + BigInt neg_y = {}; + big_int_neg(&neg_y, y); + big_int_add(dst, x, &neg_y); + return; +} + + +void big_int_shl(BigInt *dst, BigInt const *x, BigInt const *y) { + GB_ASSERT(!y->neg); + + if (y->len == 0) { + big_int_init(dst, x); + return; + } + + if (y->len == 0) { + big_int_from_u64(dst, 0); + return; + } + + if (y->len != 1) { + GB_PANIC("SHL value greater than 64 bits!"); + } + + u64 const *xd = big_int_ptr(x); + u64 shift_amount = big_int_to_u64(y); + if (x->len == 1 && shift_amount < 64) { + dst->d.word = xd[0] << shift_amount; + if (dst->d.word > xd[0]) { + dst->len = 1; + dst->neg = x->neg; + return; + } + } + + u64 word_shift_len = shift_amount / 64; + u64 remaining_shift_len = shift_amount % 64; + + big_int_alloc(dst, cast(i32)word_shift_len, x->len + word_shift_len + 1); + GB_ASSERT((x->len + word_shift_len + 1) > 1); + + u64 carry = 0; + for (i32 i = 0; i < x->len; i++) { + u64 word = xd[i]; + dst->d.words[dst->len] = carry | (word << remaining_shift_len); + dst->len += 1; + if (remaining_shift_len > 0) { + carry = word >> (64 - remaining_shift_len); + } else { + carry = 0; + } + } +} + +void big_int_shr(BigInt *dst, BigInt const *x, BigInt const *y) { + GB_ASSERT(!y->neg); + + if (x->len == 0) { + big_int_from_u64(dst, 0); + return; + } + + if (y->len == 0) { + big_int_init(dst, x); + return; + } + + if (y->len != 1) { + GB_PANIC("SHR value greater than 64 bits!"); + } + + u64 const *xd = big_int_ptr(x); + u64 shift_amount = big_int_to_u64(y); + + if (x->len == 1) { + dst->d.word = xd[0] >> shift_amount; + dst->len = 1; + dst->neg = x->neg; + big_int_normalize(dst); + return; + } + + u64 word_shift_len = shift_amount / 64ull; + u64 remaining_shift_len = shift_amount % 64ull; + + if (word_shift_len >= x->len) { + big_int_from_u64(dst, 0); + return; + } + + i32 len = cast(i32)(x->len - word_shift_len); + i32 cap = gb_max(len, dst->len); + big_int_alloc(dst, len, cap); + GB_ASSERT(dst->len >= 1); + + u64 carry = 0; + for (i32 src_idx = x->len - 1; src_idx >= 0; src_idx--) { + u64 v = xd[src_idx]; + u64 dst_idx = src_idx - word_shift_len; + + dst->d.words[dst_idx] = carry | (v >> remaining_shift_len); + + carry = v << (64ull - remaining_shift_len); + } +} + +void big_int_mul_u64(BigInt *dst, BigInt const *x, u64 y) { + BigInt v64 = {}; + big_int_from_u64(&v64, 64); + + big_int_from_u64(dst, 0); + + u64 const *xd = big_int_ptr(x); + for (i32 i = x->len-1; i >= 0; i--) { + BigInt shifted = {}; + big_int_shl(&shifted, dst, &v64); + + u64 result_u64 = 0; + u64 carry_u64 = 0; + mul_overflow_u64(y, xd[i], &result_u64, &carry_u64); + + BigInt result; + BigInt carry; + BigInt carry_shifted; + big_int_from_u64(&result, result_u64); + big_int_from_u64(&carry, carry_u64); + big_int_shl(&carry_shifted, &carry, &v64); + + BigInt tmp; + big_int_add(&tmp, &shifted, &carry_shifted); + big_int_add(dst, &tmp, &result); + } +} + + +void big_int_mul(BigInt *z, BigInt const *x, BigInt const *y) { + if (x->len == 0 || y->len == 0) { + return big_int_from_u64(z, 0); + } + u64 const *xd = big_int_ptr(x); + u64 const *yd = big_int_ptr(y); + + u64 carry = 0; + mul_overflow_u64(xd[0], yd[0], &z->d.word, &carry); + if (carry == 0 && x->len == 1 && y->len == 1) { + z->neg = (x->neg != y->neg); + z->len = 1; + big_int_normalize(z); + return; + } + + big_int_from_u64(z, 0); + i32 len = x->len+y->len; + big_int_alloc(z, len, len); + u64 *zd = big_int_ptr(z); + + for (i32 i = 0; i < y->len; i++) { + u64 d = yd[i]; + if (d != 0) { + u64 *z = zd+i; + i32 n = x->len; + u64 c = 0; + for (i32 j = 0; j < n; j++) { + u64 z1 = 0; + u64 z00 = 0; + mul_overflow_u64(xd[j], d, &z00, &z1); + u64 z0 = z00 + z[j]; + if (z0 < z00) { + z1 += 1; + } + z[j] = z0 + c; + c = 0; + if (z[j] < z0) { + c = 1; + } + c += z1; + } + + zd[n+i] = c; + } + } + + z->neg = (x->neg != y->neg); + big_int_normalize(z); +} + + +u64 leading_zeros_u64(u64 x) { +#if defined(GB_COMPILER_MSVC) + return __lzcnt64(x); +#else + return cast(u64)__builtin_clz(cast(unsigned long long)x); +#endif +} + + +void bi__divWW(u64 u1, u64 u0, u64 y, u64 *q, u64 *r) { +#if defined(GB_COMPILER_MSVC) && defined(GB_ARCH_64_BIT) + *q = unsafe_udiv128(u1, u0, y, r); +#else + // NOTE(bill): q = (u1<<64 + u0 - r)/y + // Hacker's Delight page 152 + if (u1 >= y) { + *q = ~cast(u64)0ull; + *r = ~cast(u64)0ull; + return; + } + + + u64 s = leading_zeros_u64(y); + y <<= s; + + static u64 const B = 1ull<<32ull; + static u64 const M = B-1ull; + + u64 vn1 = y >> 32ull; + u64 vn0 = y & M; + u64 un32 = (u1<<s) | (u0>>(64ull-s)); + u64 un10 = u0 << s; + u64 un1 = un10 >> 32ull; + u64 un0 = un10 & M; + u64 q1 = un32 / vn1; + u64 rhat = un32 - (q1*vn1); + + while ((q1 >= B) || ((q1*vn0) > (B*rhat+un1))) { + q1 -= 1; + rhat += vn1; + if (rhat >= B) { + break; + } + } + + u64 un21 = (un32*B) + un1 - (q1*y); + u64 q0 = un21 / vn1; + rhat = un21 - (q0*vn1); + + while ((q0 >= B) || ((q0*vn0) > (B*rhat+un0))) { + q0 -= 1; + rhat += vn1; + if (rhat >= B) { + break; + } + } + + *q = q1*B + q0; + *r = (un21*B + un0 - q0*y) >> s; +#endif +} + + +void bi__divWVW(BigInt *z, u64 xn, BigInt const *x, u64 y, u64 *r_) { + GB_ASSERT(x->len >= z->len); + u64 r = xn; + u64 const *xd = big_int_ptr(x); + u64 *zd = big_int_ptr(z); + for (i32 i = z->len-1; i >= 0; i--) { + u64 u1 = r; + u64 u0 = xd[i]; + bi__divWW(r, xd[i], y, &zd[i], &r); + } + if (r_) *r_ = r; +} + +void bi__divW(BigInt const *x, u64 y, BigInt *q_, u64 *r_) { + BigInt q = {}; + u64 r = 0; + i32 m = x->len; + if (y == 0) { + GB_PANIC("division by zero"); + } else if (y == 1) { + q = *x; + } else if (m == 0) { + // okay + } else { + big_int_alloc(&q, m, m); + bi__divWVW(&q, 0, x, y, &r); + big_int_normalize(&q); + } + if (q_) *q_ = q; + if (r_) *r_ = r; +} + +u64 shlVU(BigInt *z, BigInt const *x, u64 s) { + u64 c = 0; + i32 n = z->len; + + u64 const *xd = big_int_ptr(x); + u64 *zd = big_int_ptr(z); + + if (n > 0) { + u64 s1 = 64 - s; + u64 w1 = xd[n-1]; + c = w1 >> s1; + for (i32 i = n-1; i > 0; i--) { + u64 w = w1; + w1 = xd[i-1]; + zd[i] = (w<<s) | (w1>>s1); + } + zd[0] = w1 << s; + } + return c; +} + +u64 mulAddVWW(BigInt *z, BigInt const *x, u64 y, u64 r) { + u64 c = r; + u64 const *xd = big_int_ptr(x); + u64 *zd = big_int_ptr(z); + + for (i32 i = 0; i < z->len; i++) { + u64 a, b; + mul_overflow_u64(xd[i], y, &a, &b); + zd[i] = b + c; + if (zd[i] < b) { + a += 1; + } + c = a; + } + return c; +} + +bool bi__greater_than(u64 x1, u64 x2, u64 y1, u64 y2) { + return x1 > y1 || x1 == x2 && x2 > y2; +} + +void bi__div_large(BigInt const *a, BigInt const *b, BigInt *q, BigInt *r) { + i32 n = b->len; + i32 m = a->len - n; + + BigInt u = *a; + BigInt v = *b; + + big_int_alloc(q, m+1, m+1); + + BigInt qhatv = {{cast(u64)n+1ull}, 1, false}; + + big_int_alloc(&u, a->len+1, a->len+1); + + u64 *ud = big_int_ptr(&u); + u64 *vd = big_int_ptr(&v); + + u64 shift = leading_zeros_u64(vd[n-1]); + if (shift > 0) { + BigInt v1 = {{cast(u64)n}, 1, false}; + shlVU(&v1, &v, shift); + v = v1; + vd = big_int_ptr(&v); + } + + BigInt uu = u; + uu.len = a->len; + ud[a->len] = shlVU(&uu, a, shift); +} + +void big_int_quo_rem_unsigned(BigInt const *a, BigInt const *b, BigInt *q_, BigInt *r_) { + if (b->len == 0) { + GB_PANIC("division by zero"); + } else if (b->len == 1 && b->d.word == 0) { + GB_PANIC("division by zero"); + } + + BigInt x = *a; x.neg = false; + BigInt y = *b; y.neg = false; + BigInt q = {}; + BigInt r = {}; + + int cmp = big_int_cmp(&x, &y); + + if (cmp < 0) { + q = BIG_INT_ZERO; + r = x; + goto end; + } else if (cmp == 0) { + q = BIG_INT_ONE; + r = BIG_INT_ZERO; + goto end; + } + + if (y.len == 1) { + if (y.d.word == 0) { + GB_PANIC("division by zero"); + } else if (y.d.word == 1) { + q = x; + r = BIG_INT_ZERO; + goto end; + } else if (x.len == 0) { + q = BIG_INT_ZERO; + r = BIG_INT_ZERO; + goto end; + } + u64 rr = 0; + bi__divW(&x, y.d.word, &q, &rr); + big_int_from_u64(&r, rr); + goto end; + } + + GB_PANIC("Division of a large denominator not yet supported"); + bi__div_large(&x, &y, &q, &r); + +end: + big_int_normalize(&q); + big_int_normalize(&r); + if (q_) *q_ = q; + if (r_) *r_ = r; + return; +} + +// `big_int_quo_rem` sets z to the quotient x/y and r to the remainder x%y +// and returns the pair (z, r) for y != 0. +// if y == 0, a division-by-zero run-time panic occurs. +// +// q = x/y with the result truncated to zero +// r = x - y*q +void big_int_quo_rem(BigInt const *x, BigInt const *y, BigInt *q_, BigInt *r_) { + BigInt q = {}; + BigInt r = {}; + + big_int_quo_rem_unsigned(x, y, &q, &r); + q.neg = q.len > 0 && x->neg != y->neg; + r.neg = r.len > 0 && x->neg; + + if (q_) *q_ = q; + if (r_) *r_ = r; +} + +void big_int_quo(BigInt *z, BigInt const *x, BigInt const *y) { + BigInt r = {}; + big_int_quo_rem_unsigned(x, y, z, &r); + z->neg = z->len > 0 && x->neg != y->neg; +} + +void big_int_rem(BigInt *z, BigInt const *x, BigInt const *y) { + BigInt q = {}; + big_int_quo_rem_unsigned(x, y, &q, z); + z->neg = z->len > 0 && x->neg; +} + + + +void big_int_euclidean_div(BigInt *z, BigInt const *x, BigInt const *y) { + BigInt r = {}; + big_int_quo_rem(x, y, z, &r); + if (r.neg) { + if (y->neg) { + big_int_add(z, z, &BIG_INT_ONE); + } else { + big_int_sub(z, z, &BIG_INT_ONE); + } + } +} + + +void big_int_euclidean_mod(BigInt *z, BigInt const *x, BigInt const *y) { + BigInt y0 = {}; + big_int_init(&y0, y); + + BigInt q = {}; + big_int_quo_rem(x, y, &q, z); + if (z->neg) { + if (y0.neg) { + big_int_sub(z, z, &y0); + } else { + big_int_add(z, z, &y0); + } + } +} + + +void big_int_and(BigInt *dst, BigInt const *x, BigInt const *y) { + if (x->len == 0 || y->len == 0) { + big_int_from_u64(dst, 0); + return; + } + + if (x->neg == y->neg) { + if (x->neg) { + // (-x) & (-y) == ~(x-1) & ~(x-y) == ~((x-1) | (y-1)) == -(((x-1) | (y-1)) + 1) + BigInt x1 = big_int_make_abs(x); + BigInt y1 = big_int_make_abs(y); + BigInt z1 = {}; + + big_int_sub_eq(&x1, &BIG_INT_ONE); + big_int_sub_eq(&y1, &BIG_INT_ONE); + big_int_or(&z1, &x1, &y1); + big_int_add(dst, &z1, &BIG_INT_ONE); + dst->neg = true; // NOTE(bill): dst cannot be 0 as x and y are both negative + big_int_normalize(dst); + return; + } + u64 const *xd = big_int_ptr(x); + u64 const *yd = big_int_ptr(y); + + if (x->len == 1 && y->len == 1) { + dst->len = 1; + dst->d.word = xd[0] & yd[0]; + return; + } + + i32 len = gb_max(x->len, y->len); + big_int_alloc(dst, len, len); + GB_ASSERT(dst->len > 1); + + i32 i = 0; + for (; i < x->len && i < y->len; i++) { + dst->d.words[i] = xd[i] & yd[i]; + } + for (; i < len; i++) { + dst->d.words[i] = 0; + } + dst->neg = false; + big_int_normalize(dst); + return; + } + if (x->neg) { + BigInt const *tmp = x; + x = y; + y = tmp; + // NOTE(bill): AND is symmetric + } + + + // x & (-y) == x &~ (y-1) + + dst->neg = false; + BigInt x1 = big_int_make_abs(x); + BigInt y1 = big_int_make_abs(y); + big_int_sub_eq(&y1, &BIG_INT_ONE); + big_int_and_not(dst, &x1, &y1); + big_int_normalize(dst); +} + +void big_int__and_not_abs(BigInt *dst, BigInt const *x, BigInt const *y) { + u64 const *xd = big_int_ptr(x); + u64 const *yd = big_int_ptr(y); + + if (x->len == 1 && y->len == 1) { + dst->len = 1; + dst->d.word = xd[0] & (~yd[0]); + return; + } + + i32 len = gb_max(x->len, y->len); + big_int_alloc(dst, len, len); + GB_ASSERT(dst->len > 1); + + i32 i = 0; + for (; i < x->len && i < y->len; i++) { + dst->d.words[i] = xd[i] & (~yd[i]); + } + if (i < x->len) { + for (; i < len; i++) { + dst->d.words[i] = xd[i]; + } + } + if (i < y->len) { + for (; i < len; i++) { + dst->d.words[i] = yd[i]; + } + } + big_int_normalize(dst); +} + +void big_int_and_not(BigInt *dst, BigInt const *x, BigInt const *y) { + if (x->len == 0) { + big_int_init(dst, y); + return; + } + if (y->len == 0) { + big_int_init(dst, x); + return; + } + + if (x->neg == y->neg) { + if (x->neg) { + // (-x) &~ (-y) == ~(x-1) &~ ~(y-1) == ~(x-1) & (y-1) == (y-1) &~ (x-1) + BigInt x1 = big_int_make_abs(x); + BigInt y1 = big_int_make_abs(y); + big_int_sub_eq(&x1, &BIG_INT_ONE); + big_int_sub_eq(&y1, &BIG_INT_ONE); + + big_int__and_not_abs(dst, &y1, &x1); + dst->neg = false; + return; + } + + big_int__and_not_abs(dst, x, y); + dst->neg = false; + return; + } + + if (x->neg) { + // (-x) &~ y == ~(x-1) &~ y == ~(x-1) & ~y == ~((x-1) | y) == -(((x-1) | y) + 1) + BigInt x1 = big_int_make_abs(x); + BigInt y1 = big_int_make_abs(y); + big_int_sub_eq(&x1, &BIG_INT_ONE); + + BigInt z1 = {}; + big_int_or(&z1, &x1, &y1); + big_int_add(dst, &z1, &BIG_INT_ONE); + dst->neg = true; + return; + } + + // x &~ (-y) == x &~ ~(y-1) == x & (y-1) + BigInt x1 = big_int_make_abs(x); + BigInt y1 = big_int_make_abs(y); + big_int_sub_eq(&y1, &BIG_INT_ONE); + big_int_and(dst, &x1, &y1); + dst->neg = false; + return; +} + + +void big_int__xor_abs(BigInt *dst, BigInt const *x, BigInt const *y) { + u64 const *xd = big_int_ptr(x); + u64 const *yd = big_int_ptr(y); + + if (x->len == 1 && y->len == 1) { + dst->len = 1; + dst->d.word = xd[0] ^ yd[0]; + return; + } + + i32 len = gb_max(x->len, y->len); + big_int_alloc(dst, len, len); + GB_ASSERT(dst->len > 1); + + i32 i = 0; + for (; i < x->len && i < y->len; i++) { + dst->d.words[i] = xd[i] ^ yd[i]; + } + for (; i < len; i++) { + if (i < x->len) { + dst->d.words[i] = xd[i]; + } + if (i < y->len) { + dst->d.words[i] = yd[i]; + } + } + big_int_normalize(dst); +} + +void big_int_xor(BigInt *dst, BigInt const *x, BigInt const *y) { + if (x->len == 0) { + big_int_init(dst, y); + return; + } else if (y->len == 0) { + big_int_init(dst, x); + return; + } + + if (x->neg == y->neg) { + if (x->neg) { + // (-x) ^ (-y) == ~(x-1) ^ ~(y-1) == (x-1) ^ (y-1) + BigInt x1 = big_int_make_abs(x); + BigInt y1 = big_int_make_abs(y); + big_int_sub_eq(&x1, &BIG_INT_ONE); + big_int_sub_eq(&y1, &BIG_INT_ONE); + big_int__xor_abs(dst, &x1, &y1); + dst->neg = false; + return; + } + + big_int__xor_abs(dst, x, y); + dst->neg = false; + return; + } + + // x->neg != y->neg + if (x->neg) { + BigInt const *tmp = x; + x = y; + y = tmp; + } + + // x ^ (-y) == x ^ ~(y-1) == ~(x ^ (y-1)) == -((x ^ (y-1)) + 1) + + dst->neg = false; + BigInt y1 = big_int_make_abs(y); + big_int_sub_eq(&y1, &BIG_INT_ONE); + big_int__xor_abs(dst, x, &y1); + big_int_add_eq(dst, &BIG_INT_ONE); + dst->neg = true; + return; +} + + +void big_int_or(BigInt *dst, BigInt const *x, BigInt const *y) { + if (x->len == 0) { + big_int_init(dst, y); + return; + } else if (y->len == 0) { + big_int_init(dst, x); + return; + } + + if (x->neg == y->neg) { + if (x->neg) { + // (-x) || (-y) == ~(x-1) | ~(y-1) == ~((x-1) & (y-1)) == -(((x-1) & (y-1)) + 1) + BigInt x1 = big_int_make_abs(x); + BigInt y1 = big_int_make_abs(y); + big_int_sub_eq(&x1, &BIG_INT_ONE); + big_int_sub_eq(&y1, &BIG_INT_ONE); + big_int_and(dst, &x1, &y1); + big_int_add_eq(dst, &BIG_INT_ONE); + dst->neg = true; + return; + } + + dst->neg = x->neg; + u64 const *xd = big_int_ptr(x); + u64 const *yd = big_int_ptr(y); + + if (x->len == 1 && y->len == 1) { + dst->len = 1; + dst->d.word = xd[0] | yd[0]; + return; + } + + i32 len = gb_max(x->len, y->len); + big_int_alloc(dst, len, len); + GB_ASSERT(dst->len > 1); + + for (i32 i = 0; i < len; i++) { + u64 word = 0; + if (i < x->len) { + word |= xd[i]; + } + if (i < y->len) { + word |= yd[i]; + } + + dst->d.words[i] = word; + } + big_int_normalize(dst); + } + + if (x->neg) { + BigInt const *tmp = x; + x = y; + y = tmp; + } + + // x | (-y) == x | ~(y-1) == ~((y-1) &~ x) == -(~((y-1) &~ x) + 1) + BigInt y1 = big_int_make_abs(y); + big_int_sub_eq(&y1, &BIG_INT_ONE); + big_int__and_not_abs(dst, &y1, x); + big_int_add_eq(dst, &BIG_INT_ONE); + dst->neg = true; + return; +} + + +void bit_int_not(BigInt *dst, BigInt const *x, u64 bit_count, bool is_signed) { + if (bit_count == 0) { + big_int_from_u64(dst, 0); + return; + } + + dst->neg = false; + u64 const *xd = big_int_ptr(x); + if (bit_count <= 64) { + dst->len = 1; + if (x->len == 0) { + if (bit_count == 64) { + dst->d.word = ~cast(u64)0ull; + } else { + dst->d.word = (1ull << bit_count) - 1ull; + } + } else if (x->len == 1) { + dst->d.word = ~xd[0]; + if (bit_count != 64) { + u64 mask = (1ull << bit_count) - 1ull; + dst->d.word &= mask; + } + } + } else { + dst->len = cast(i32)((bit_count+63ull) / 64ull); + GB_ASSERT(dst->len >= x->len); + big_int_alloc(dst, dst->len, dst->len); + GB_ASSERT(dst->len > 1); + + i32 i = 0; + for (; i < x->len; i++) { + dst->d.words[i] = ~xd[i]; + } + for (; i < dst->len; i++) { + dst->d.words[i] = ~cast(u64)0ull; + } + + i32 word_idx = cast(i32)(cast(u64)dst->len - (bit_count/64ull)-1ull); + u32 word_bit_idx = bit_count % 64; + if (word_idx < dst->len) { + u64 mask = (1ull << word_bit_idx) - 1ull; + dst->d.words[word_idx] &= mask; + } + } + + big_int_normalize(dst); + + if (is_signed) { + BigInt prec = big_int_make_u64(bit_count-1); + BigInt mask = {}; + BigInt mask_minus_one = {}; + big_int_shl(&mask, &BIG_INT_ONE, &prec); + big_int_sub(&mask_minus_one, &mask, &BIG_INT_ONE); + + BigInt a = {}; + BigInt b = {}; + big_int_and(&a, dst, &mask_minus_one); + big_int_and(&b, dst, &mask); + big_int_sub(dst, &a, &b); + } +} + + +char digit_to_char(u8 digit) { + GB_ASSERT(digit < 16); + if (digit <= 9) { + return digit + '0'; + } else if (digit <= 15) { + return digit + 'a'; + } + return '0'; +} + +String big_int_to_string(gbAllocator allocator, BigInt const *x, u64 base) { + GB_ASSERT(base <= 16); + + + if ((x->len == 0) && (x->len == 1 && x->d.word == 0)) { + u8 *buf = gb_alloc_array(allocator, u8, 1); + buf[0] = '0'; + return make_string(buf, 1); + } + + Array<char> buf = {}; + array_init(&buf, allocator, 0, 32); + + BigInt v = *x; + + if (v.neg) { + array_add(&buf, '-'); + v.neg = false; + big_int_normalize(&v); + } + + isize first_word_idx = buf.count; + + BigInt r = {}; + BigInt b = {}; + big_int_from_u64(&b, base); + + u8 digit = 0; + while (big_int_cmp(&v, &b) >= 0) { + big_int_quo_rem(&v, &b, &v, &r); + digit = cast(u8)big_int_to_u64(&r); + array_add(&buf, digit_to_char(digit)); + } + + big_int_rem(&r, &v, &b); + digit = cast(u8)big_int_to_u64(&r); + array_add(&buf, digit_to_char(digit)); + + + for (isize i = first_word_idx; i < buf.count/2; i++) { + isize j = buf.count + first_word_idx - i - 1; + char tmp = buf[i]; + buf[i] = buf[j]; + buf[j] = tmp; + } + + return make_string(cast(u8 *)buf.data, buf.count); +} diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 8b3e6dfe7..42c9e57bc 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -813,7 +813,7 @@ bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, Type *source, if (e->Constant.value.kind != ExactValue_Integer) { return false; } - i64 count = e->Constant.value.value_integer; + i64 count = big_int_to_i64(&e->Constant.value.value_integer); if (count != source->Array.count) { return false; } @@ -1257,33 +1257,44 @@ bool check_representable_as_constant(CheckerContext *c, ExactValue in_value, Typ return true; } - i64 i = v.value_integer; - u64 u = bit_cast<u64>(i); + BigInt i = v.value_integer; + i64 bit_size = type_size_of(type); - u64 umax = unsigned_integer_maxs[bit_size]; - i64 imin = signed_integer_mins[bit_size]; - i64 imax = signed_integer_maxs[bit_size]; + BigInt umax = {}; + BigInt imin = {}; + BigInt imax = {}; + + big_int_from_u64(&umax, unsigned_integer_maxs[bit_size]); + big_int_from_i64(&imin, signed_integer_mins[bit_size]); + big_int_from_i64(&imax, signed_integer_maxs[bit_size]); switch (type->Basic.kind) { case Basic_rune: case Basic_i8: case Basic_i16: case Basic_i32: + case Basic_i64: case Basic_int: - return imin <= i && i <= imax; + { + // return imin <= i && i <= imax; + int a = big_int_cmp(&imin, &i); + int b = big_int_cmp(&i, &imax); + return (a <= 0) && (b <= 0); + } case Basic_u8: case Basic_u16: case Basic_u32: + case Basic_u64: case Basic_uint: case Basic_uintptr: - return 0ull <= u && u <= umax; - case Basic_u64: - return 0ull <= i; + { + // return 0ull <= i && i <= umax; + int b = big_int_cmp(&i, &umax); + return !i.neg && (b <= 0); + } - case Basic_i64: - return true; case Basic_UntypedInteger: return true; @@ -1357,14 +1368,9 @@ void check_is_expressible(CheckerContext *c, Operand *o, Type *type) { if (!is_type_integer(o->type) && is_type_integer(type)) { error(o->expr, "'%s' truncated to '%s'", a, b); } else { - char buf[127] = {}; - String str = {}; - i64 i = o->value.value_integer; - if (is_type_unsigned(o->type)) { - str = u64_to_string(bit_cast<u64>(i), buf, gb_size_of(buf)); - } else { - str = i64_to_string(i, buf, gb_size_of(buf)); - } + gbAllocator ha = heap_allocator(); + String str = big_int_to_string(ha, &o->value.value_integer); + defer (gb_free(ha, str.text)); error(o->expr, "'%s = %.*s' overflows '%s'", a, LIT(str), b); } } else { @@ -1444,10 +1450,7 @@ void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast *node) { } - i32 precision = 0; - if (is_type_unsigned(type)) { - precision = cast(i32)(8 * type_size_of(type)); - } + if (op.kind == Token_Xor && is_type_untyped(type)) { gbString err_str = expr_to_string(node); error(op, "Bitwise not cannot be applied to untyped constants '%s'", err_str); @@ -1463,7 +1466,17 @@ void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast *node) { return; } - o->value = exact_unary_operator_value(op.kind, o->value, precision, is_type_unsigned(type)); + i32 precision = 0; + if (is_type_typed(type)) { + precision = cast(i32)(8 * type_size_of(type)); + } + + bool is_unsigned = is_type_unsigned(type); + if (is_type_rune(type)) { + GB_ASSERT(!is_unsigned); + } + + o->value = exact_unary_operator_value(op.kind, o->value, precision, is_unsigned); if (is_type_typed(type)) { if (node != nullptr) { @@ -1638,8 +1651,10 @@ void check_shift(CheckerContext *c, Operand *x, Operand *y, Ast *node) { return; } - i64 amount = y_val.value_integer; - if (amount > 128) { + BigInt max_shift = {}; + big_int_from_u64(&max_shift, 128); + + if (big_int_cmp(&y_val.value_integer, &max_shift) > 0) { gbString err_str = expr_to_string(y->expr); error(node, "Shift amount too large: '%s'", err_str); gb_string_free(err_str); @@ -1653,7 +1668,7 @@ void check_shift(CheckerContext *c, Operand *x, Operand *y, Ast *node) { x->type = t_untyped_integer; } - x->value = exact_value_shift(be->op.kind, x_val, exact_value_i64(amount)); + x->value = exact_value_shift(be->op.kind, x_val, y_val); if (is_type_typed(x->type)) { check_is_expressible(c, x, base_type(x->type)); @@ -1673,7 +1688,7 @@ void check_shift(CheckerContext *c, Operand *x, Operand *y, Ast *node) { } } - if (y->mode == Addressing_Constant && y->value.value_integer < 0) { + if (y->mode == Addressing_Constant && y->value.value_integer.neg) { gbString err_str = expr_to_string(y->expr); error(node, "Shift amount cannot be negative: '%s'", err_str); gb_string_free(err_str); @@ -1691,60 +1706,60 @@ void check_shift(CheckerContext *c, Operand *x, Operand *y, Ast *node) { } -Operand check_ptr_addition(CheckerContext *c, TokenKind op, Operand *ptr, Operand *offset, Ast *node) { - GB_ASSERT(node->kind == Ast_BinaryExpr); - ast_node(be, BinaryExpr, node); - GB_ASSERT(is_type_pointer(ptr->type)); - GB_ASSERT(is_type_integer(offset->type)); - GB_ASSERT(op == Token_Add || op == Token_Sub); - - Operand operand = {}; - operand.mode = Addressing_Value; - operand.type = ptr->type; - operand.expr = node; - - if (base_type(ptr->type) == t_rawptr) { - gbString str = type_to_string(ptr->type); - error(node, "Invalid pointer type for pointer arithmetic: '%s'", str); - gb_string_free(str); - operand.mode = Addressing_Invalid; - return operand; - } - -#if defined(NO_POINTER_ARITHMETIC) - operand.mode = Addressing_Invalid; - error(operand.expr, "Pointer arithmetic is not supported"); - return operand; -#else - - Type *base_ptr = base_type(ptr->type); GB_ASSERT(base_ptr->kind == Type_Pointer); - Type *elem = base_ptr->Pointer.elem; - i64 elem_size = type_size_of(elem); - - if (elem_size <= 0) { - gbString str = type_to_string(elem); - error(node, "Size of pointer's element type '%s' is zero and cannot be used for pointer arithmetic", str); - gb_string_free(str); - operand.mode = Addressing_Invalid; - return operand; - } - - if (ptr->mode == Addressing_Constant && offset->mode == Addressing_Constant) { - i64 ptr_val = ptr->value.value_pointer; - i64 offset_val = exact_value_to_integer(offset->value).value_integer; - i64 new_ptr_val = ptr_val; - if (op == Token_Add) { - new_ptr_val += elem_size*offset_val; - } else { - new_ptr_val -= elem_size*offset_val; - } - operand.mode = Addressing_Constant; - operand.value = exact_value_pointer(new_ptr_val); - } - - return operand; -#endif -} +// Operand check_ptr_addition(CheckerContext *c, TokenKind op, Operand *ptr, Operand *offset, Ast *node) { +// GB_ASSERT(node->kind == Ast_BinaryExpr); +// ast_node(be, BinaryExpr, node); +// GB_ASSERT(is_type_pointer(ptr->type)); +// GB_ASSERT(is_type_integer(offset->type)); +// GB_ASSERT(op == Token_Add || op == Token_Sub); + +// Operand operand = {}; +// operand.mode = Addressing_Value; +// operand.type = ptr->type; +// operand.expr = node; + +// if (base_type(ptr->type) == t_rawptr) { +// gbString str = type_to_string(ptr->type); +// error(node, "Invalid pointer type for pointer arithmetic: '%s'", str); +// gb_string_free(str); +// operand.mode = Addressing_Invalid; +// return operand; +// } + +// #if defined(NO_POINTER_ARITHMETIC) +// operand.mode = Addressing_Invalid; +// error(operand.expr, "Pointer arithmetic is not supported"); +// return operand; +// #else + +// Type *base_ptr = base_type(ptr->type); GB_ASSERT(base_ptr->kind == Type_Pointer); +// Type *elem = base_ptr->Pointer.elem; +// i64 elem_size = type_size_of(elem); + +// if (elem_size <= 0) { +// gbString str = type_to_string(elem); +// error(node, "Size of pointer's element type '%s' is zero and cannot be used for pointer arithmetic", str); +// gb_string_free(str); +// operand.mode = Addressing_Invalid; +// return operand; +// } + +// if (ptr->mode == Addressing_Constant && offset->mode == Addressing_Constant) { +// i64 ptr_val = ptr->value.value_pointer; +// i64 offset_val = exact_value_to_integer(offset->value).value_integer; +// i64 new_ptr_val = ptr_val; +// if (op == Token_Add) { +// new_ptr_val += elem_size*offset_val; +// } else { +// new_ptr_val -= elem_size*offset_val; +// } +// operand.mode = Addressing_Constant; +// operand.value = exact_value_pointer(new_ptr_val); +// } + +// return operand; +// #endif +// } @@ -2030,24 +2045,24 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node) { return; } - if (op.kind == Token_Add || op.kind == Token_Sub) { - if (is_type_pointer(x->type) && is_type_integer(y->type)) { - *x = check_ptr_addition(c, op.kind, x, y, node); - return; - } else if (is_type_integer(x->type) && is_type_pointer(y->type)) { - if (op.kind == Token_Sub) { - gbString lhs = expr_to_string(x->expr); - gbString rhs = expr_to_string(y->expr); - error(node, "Invalid pointer arithmetic, did you mean '%s %.*s %s'?", rhs, LIT(op.string), lhs); - gb_string_free(rhs); - gb_string_free(lhs); - x->mode = Addressing_Invalid; - return; - } - *x = check_ptr_addition(c, op.kind, y, x, node); - return; - } - } + // if (op.kind == Token_Add || op.kind == Token_Sub) { + // if (is_type_pointer(x->type) && is_type_integer(y->type)) { + // *x = check_ptr_addition(c, op.kind, x, y, node); + // return; + // } else if (is_type_integer(x->type) && is_type_pointer(y->type)) { + // if (op.kind == Token_Sub) { + // gbString lhs = expr_to_string(x->expr); + // gbString rhs = expr_to_string(y->expr); + // error(node, "Invalid pointer arithmetic, did you mean '%s %.*s %s'?", rhs, LIT(op.string), lhs); + // gb_string_free(rhs); + // gb_string_free(lhs); + // x->mode = Addressing_Invalid; + // return; + // } + // *x = check_ptr_addition(c, op.kind, y, x, node); + // return; + // } + // } convert_to_typed(c, x, y->type); if (x->mode == Addressing_Invalid) { @@ -2108,7 +2123,7 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node) { bool fail = false; switch (y->value.kind) { case ExactValue_Integer: - if (y->value.value_integer == 0 ) { + if (big_int_is_zero(&y->value.value_integer)) { fail = true; } break; @@ -2246,7 +2261,7 @@ void convert_untyped_error(CheckerContext *c, Operand *operand, Type *target_typ char *extra_text = ""; if (operand->mode == Addressing_Constant) { - if (operand->value.value_integer == 0) { + if (big_int_is_zero(&operand->value.value_integer)) { if (make_string_c(expr_str) != "nil") { // HACK NOTE(bill): Just in case // NOTE(bill): Doesn't matter what the type is as it's still zero in the union extra_text = " - Did you want 'nil'?"; @@ -2495,8 +2510,8 @@ bool check_index_value(CheckerContext *c, bool open_range, Ast *index_value, i64 if (operand.mode == Addressing_Constant && (c->stmt_state_flags & StmtStateFlag_no_bounds_check) == 0) { - i64 i = exact_value_to_integer(operand.value).value_integer; - if (i < 0) { + BigInt i = exact_value_to_integer(operand.value).value_integer; + if (i.neg) { gbString expr_str = expr_to_string(operand.expr); error(operand.expr, "Index '%s' cannot be a negative value", expr_str); gb_string_free(expr_str); @@ -2505,13 +2520,21 @@ bool check_index_value(CheckerContext *c, bool open_range, Ast *index_value, i64 } if (max_count >= 0) { // NOTE(bill): Do array bound checking - if (value) *value = i; + i64 v = -1; + if (i.len <= 1) { + v = big_int_to_i64(&i); + } + if (value) *value = v; bool out_of_bounds = false; if (open_range) { - out_of_bounds = i > max_count; + out_of_bounds = v > max_count; } else { - out_of_bounds = i >= max_count; + out_of_bounds = v >= max_count; + } + if (v < 0) { + out_of_bounds = true; } + if (out_of_bounds) { gbString expr_str = expr_to_string(operand.expr); error(operand.expr, "Index '%s' is out of bounds range 0..<%lld", expr_str, max_count); @@ -3399,12 +3422,14 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 return false; } - if (op.value.value_integer < 0) { + if (op.value.value_integer.neg) { error(op.expr, "Negative 'swizzle' index"); return false; } - if (max_count <= op.value.value_integer) { + BigInt mc = {}; + big_int_from_i64(&mc, max_count); + if (big_int_cmp(&mc, &op.value.value_integer) <= 0) { error(op.expr, "'swizzle' index exceeds length"); return false; } @@ -3804,7 +3829,7 @@ break; if (operand->mode == Addressing_Constant) { switch (operand->value.kind) { case ExactValue_Integer: - operand->value.value_integer = gb_abs(operand->value.value_integer); + operand->value.value_integer.neg = false; break; case ExactValue_Float: operand->value.value_float = gb_abs(operand->value.value_float); diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 3ce7373b6..453127f0c 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -260,18 +260,17 @@ Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, Operand *rhs) if (rhs->mode == Addressing_Constant) { ExactValue v = exact_value_to_integer(rhs->value); if (v.kind == ExactValue_Integer) { - i64 i = v.value_integer; - u64 u = bit_cast<u64>(i); - u64 umax = ~cast(u64)0ull; - if (lhs_bits < 64) { - umax = (1ull << cast(u64)lhs_bits) - 1ull; - } - i64 imax = 1ll << (cast(i64)lhs_bits-1ll); - - bool ok = !(u < 0 || u > umax); + BigInt i = v.value_integer; + if (!i.neg) { + u64 imax_ = ~cast(u64)0ull; + if (lhs_bits < 64) { + imax_ = (1ull << cast(u64)lhs_bits) - 1ull; + } - if (ok) { - return rhs->type; + BigInt imax = big_int_make_u64(imax_); + if (big_int_cmp(&i, &imax) > 0) { + return rhs->type; + } } } } else if (is_type_integer(rhs->type)) { diff --git a/src/check_type.cpp b/src/check_type.cpp index 931117f79..b73389865 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -136,7 +136,15 @@ bool check_custom_align(CheckerContext *ctx, Ast *node, i64 *align_) { Type *type = base_type(o.type); if (is_type_untyped(type) || is_type_integer(type)) { if (o.value.kind == ExactValue_Integer) { - i64 align = o.value.value_integer; + BigInt v = o.value.value_integer; + if (v.len > 1) { + gbAllocator a = heap_allocator(); + String str = big_int_to_string(a, &v); + error(node, "#align too large, %.*s", LIT(str)); + gb_free(a, str.text); + return false; + } + i64 align = big_int_to_i64(&v); if (align < 1 || !gb_is_power_of_two(cast(isize)align)) { error(node, "#align must be a power of 2, got %lld", align); return false; @@ -668,7 +676,7 @@ void check_bit_field_type(CheckerContext *ctx, Type *bit_field_type, Ast *node) error(value, "Bit field bit size must be a constant integer"); continue; } - i64 bits_ = v.value_integer; + i64 bits_ = big_int_to_i64(&v.value_integer); // TODO(bill): what if the integer is huge? if (bits_ < 0 || bits_ > 64) { error(value, "Bit field's bit size must be within the range 1...64, got %lld", cast(long long)bits_); continue; @@ -1601,11 +1609,22 @@ i64 check_array_count(CheckerContext *ctx, Operand *o, Ast *e) { Type *type = base_type(o->type); if (is_type_untyped(type) || is_type_integer(type)) { if (o->value.kind == ExactValue_Integer) { - i64 count = o->value.value_integer; - if (count >= 0) { - return count; + BigInt count = o->value.value_integer; + if (o->value.value_integer.neg) { + gbAllocator a = heap_allocator(); + String str = big_int_to_string(a, &count); + error(e, "Invalid negative array count, %.*s", LIT(str)); + gb_free(a, str.text); + return 0; + } + switch (count.len) { + case 0: return 0; + case 1: return count.d.word; } - error(e, "Invalid negative array count %lld", cast(long long)count); + gbAllocator a = heap_allocator(); + String str = big_int_to_string(a, &count); + error(e, "Array count too large, %.*s", LIT(str)); + gb_free(a, str.text); return 0; } } diff --git a/src/common.cpp b/src/common.cpp index adeebc410..e7024ae43 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -3,6 +3,10 @@ #include <xmmintrin.h> #endif +#if defined(GB_COMPILER_MSVC) +#include <intrin.h> +#endif + #define GB_IMPLEMENTATION #include "gb/gb.h" @@ -200,7 +204,7 @@ u64 u64_from_string(String string) { } String u64_to_string(u64 v, char *out_buf, isize out_buf_len) { - char buf[200] = {0}; + char buf[32] = {0}; isize i = gb_size_of(buf); u64 b = 10; @@ -215,7 +219,7 @@ String u64_to_string(u64 v, char *out_buf, isize out_buf_len) { return make_string(cast(u8 *)out_buf, len); } String i64_to_string(i64 a, char *out_buf, isize out_buf_len) { - char buf[200] = {0}; + char buf[32] = {0}; isize i = gb_size_of(buf); bool negative = false; if (a < 0) { @@ -276,6 +280,44 @@ gb_global u64 const unsigned_integer_maxs[] = { }; +bool add_overflow_u64(u64 x, u64 y, u64 *result) { + *result = x + y; + return *result < x || *result < y; +} + +bool sub_overflow_u64(u64 x, u64 y, u64 *result) { + *result = x - y; + return *result > x; +} + +void mul_overflow_u64(u64 x, u64 y, u64 *lo, u64 *hi) { +#if defined(GB_COMPILER_MSVC) + *lo = _umul128(x, y, hi); +#else + // URL(bill): https://stackoverflow.com/questions/25095741/how-can-i-multiply-64-bit-operands-and-get-128-bit-result-portably#25096197 + u64 u1, v1, w1, t, w3, k; + + u1 = (x & 0xffffffff); + v1 = (y & 0xffffffff); + t = (u1 * v1); + w3 = (t & 0xffffffff); + k = (t >> 32); + + x >>= 32; + t = (x * v1) + k; + k = (t & 0xffffffff); + w1 = (t >> 32); + + y >>= 32; + t = (u1 * y) + k; + k = (t >> 32); + + *hi = (x * y) + w1 + k; + *lo = (t << 32) + w3; +#endif +} + + #include "map.cpp" #include "ptr_set.cpp" diff --git a/src/exact_value.cpp b/src/exact_value.cpp index 1f07e2ee6..e339e876b 100644 --- a/src/exact_value.cpp +++ b/src/exact_value.cpp @@ -33,7 +33,7 @@ struct ExactValue { union { bool value_bool; String value_string; - i64 value_integer; // NOTE(bill): This must be an integer and not a pointer + BigInt value_integer; // NOTE(bill): This must be an integer and not a pointer f64 value_float; i64 value_pointer; Complex128 value_complex; @@ -54,8 +54,14 @@ HashKey hash_exact_value(ExactValue v) { return hash_integer(u64(v.value_bool)); case ExactValue_String: return hash_string(v.value_string); - case ExactValue_Integer: - return hash_integer(u64(v.value_integer)); + case ExactValue_Integer: { + u64 *d = big_int_ptr(&v.value_integer); + u64 x = 0; + for (i32 i = 0; i < v.value_integer.len; i++) { + x |= d[i]; + } + return hash_integer(x); + } case ExactValue_Float: return hash_f64(v.value_float); case ExactValue_Pointer: @@ -94,13 +100,13 @@ ExactValue exact_value_string(String string) { ExactValue exact_value_i64(i64 i) { ExactValue result = {ExactValue_Integer}; - result.value_integer = i; + big_int_from_i64(&result.value_integer, i); return result; } ExactValue exact_value_u64(u64 i) { ExactValue result = {ExactValue_Integer}; - result.value_integer = i64(i); + big_int_from_u64(&result.value_integer, i); return result; } @@ -293,7 +299,7 @@ ExactValue exact_value_to_integer(ExactValue v) { ExactValue exact_value_to_float(ExactValue v) { switch (v.kind) { case ExactValue_Integer: - return exact_value_float(cast(f64)v.value_integer); + return exact_value_float(big_int_to_f64(&v.value_integer)); case ExactValue_Float: return v; } @@ -304,7 +310,7 @@ ExactValue exact_value_to_float(ExactValue v) { ExactValue exact_value_to_complex(ExactValue v) { switch (v.kind) { case ExactValue_Integer: - return exact_value_complex(cast(f64)v.value_integer, 0); + return exact_value_complex(big_int_to_f64(&v.value_integer), 0); case ExactValue_Float: return exact_value_complex(v.value_float, 0); case ExactValue_Complex: @@ -371,8 +377,8 @@ ExactValue exact_unary_operator_value(TokenKind op, ExactValue v, i32 precision, case ExactValue_Invalid: return v; case ExactValue_Integer: { - ExactValue i = v; - i.value_integer = -i.value_integer; + ExactValue i = {ExactValue_Integer}; + big_int_neg(&i.value_integer, &v.value_integer); return i; } case ExactValue_Float: { @@ -390,24 +396,18 @@ ExactValue exact_unary_operator_value(TokenKind op, ExactValue v, i32 precision, } case Token_Xor: { - i64 i = 0; switch (v.kind) { case ExactValue_Invalid: return v; - case ExactValue_Integer: - i = ~v.value_integer; - break; + case ExactValue_Integer: { + GB_ASSERT(precision != 0); + ExactValue i = {ExactValue_Integer}; + bit_int_not(&i.value_integer, &v.value_integer, precision, !is_unsigned); + return i; + } default: goto failure; } - - // NOTE(bill): unsigned integers will be negative and will need to be - // limited to the types precision - // IMPORTANT NOTE(bill): Max precision is 64 bits as that's how integers are stored - if (is_unsigned) { - i = i & unsigned_integer_maxs[precision/8]; - } - return exact_value_i64(i); } case Token_Not: { @@ -472,10 +472,10 @@ void match_exact_values(ExactValue *x, ExactValue *y) { return; case ExactValue_Float: // TODO(bill): Is this good enough? - *x = exact_value_float(cast(f64)x->value_integer); + *x = exact_value_float(big_int_to_f64(&x->value_integer)); return; case ExactValue_Complex: - *x = exact_value_complex(cast(f64)x->value_integer, 0); + *x = exact_value_complex(big_int_to_f64(&x->value_integer), 0); return; } break; @@ -513,28 +513,29 @@ ExactValue exact_binary_operator_value(TokenKind op, ExactValue x, ExactValue y) break; case ExactValue_Integer: { - i64 a = x.value_integer; - i64 b = y.value_integer; - i64 c = 0ll; + BigInt const *a = &x.value_integer; + BigInt const *b = &y.value_integer; + BigInt c = {}; switch (op) { - case Token_Add: c = a + b; break; - case Token_Sub: c = a - b; break; - case Token_Mul: c = a * b; break; - case Token_Quo: return exact_value_float(fmod(cast(f64)a, cast(f64)b)); - case Token_QuoEq: c = a / b; break; // NOTE(bill): Integer division - case Token_Mod: c = a % b; break; - case Token_ModMod: c = ((a % b) + b) % b; break; - case Token_And: c = a & b; break; - case Token_Or: c = a | b; break; - case Token_Xor: c = a ^ b; break; - case Token_AndNot: c = a & (~b); break; - case Token_Shl: c = a << b; break; - case Token_Shr: c = a >> b; break; + case Token_Add: big_int_add(&c, a, b); break; + case Token_Sub: big_int_sub(&c, a, b); break; + case Token_Mul: big_int_mul(&c, a, b); break; + case Token_Quo: return exact_value_float(fmod(big_int_to_f64(a), big_int_to_f64(b))); + case Token_QuoEq: big_int_quo(&c, a, b); break; // NOTE(bill): Integer division + case Token_Mod: big_int_rem(&c, a, b); break; + case Token_ModMod: big_int_euclidean_mod(&c, a, b); break; + case Token_And: big_int_and(&c, a, b); break; + case Token_Or: big_int_or(&c, a, b); break; + case Token_Xor: big_int_xor(&c, a, b); break; + case Token_AndNot: big_int_and_not(&c, a, b); break; + case Token_Shl: big_int_shl(&c, a, b); break; + case Token_Shr: big_int_shr(&c, a, b); break; default: goto error; } - return exact_value_i64(c); - break; + ExactValue res = {ExactValue_Integer}; + res.value_integer = c; + return res; } case ExactValue_Float: { @@ -638,15 +639,14 @@ bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y) { break; case ExactValue_Integer: { - i64 a = x.value_integer; - i64 b = y.value_integer; + i32 cmp = big_int_cmp(&x.value_integer, &y.value_integer); switch (op) { - case Token_CmpEq: return a == b; - case Token_NotEq: return a != b; - case Token_Lt: return a < b; - case Token_LtEq: return a <= b; - case Token_Gt: return a > b; - case Token_GtEq: return a >= b; + case Token_CmpEq: return cmp == 0; + case Token_NotEq: return cmp != 0; + case Token_Lt: return cmp < 0; + case Token_LtEq: return cmp <= 0; + case Token_Gt: return cmp > 0; + case Token_GtEq: return cmp >= 0; } break; } diff --git a/src/ir.cpp b/src/ir.cpp index 75fd2c592..98bfbb99f 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -4778,7 +4778,7 @@ irValue *ir_build_builtin_proc(irProcedure *proc, Ast *expr, TypeAndValue tv, Bu GB_ASSERT(is_type_integer(tv.type)); GB_ASSERT(tv.value.kind == ExactValue_Integer); - i32 src_index = cast(i32)tv.value.value_integer; + i32 src_index = cast(i32)big_int_to_i64(&tv.value.value_integer); i32 dst_index = i-1; irValue *src_elem = ir_emit_array_epi(proc, src, src_index); @@ -5639,7 +5639,7 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) { Type *selector_type = base_type(type_of_expr(se->selector)); GB_ASSERT_MSG(is_type_integer(selector_type), "%s", type_to_string(selector_type)); ExactValue val = type_and_value_of_expr(sel).value; - i64 index = val.value_integer; + i64 index = big_int_to_i64(&val.value_integer); Selection sel = lookup_field_from_index(type, index); GB_ASSERT(sel.entity != nullptr); diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 88b172d7f..ac1c20d90 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -65,6 +65,15 @@ void ir_write_i64(irFileBuffer *f, i64 i) { String str = i64_to_string(i, f->buf, IR_FILE_BUFFER_BUF_LEN-1); ir_write_string(f, str); } +void ir_write_big_int(irFileBuffer *f, BigInt const &x) { + i64 i = 0; + if (x.neg) { + i = big_int_to_i64(&x); + } else { + i = cast(i64)big_int_to_u64(&x); + } + ir_write_i64(f, i); +} void ir_file_write(irFileBuffer *f, void *data, isize len) { ir_file_buffer_write(f, data, len); @@ -587,19 +596,19 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type * } case ExactValue_Integer: { if (is_type_pointer(type)) { - if (value.value_integer == 0) { + if (big_int_is_zero(&value.value_integer)) { ir_write_str_lit(f, "null"); } else { ir_write_str_lit(f, "inttoptr ("); ir_print_type(f, m, t_int); ir_write_byte(f, ' '); - ir_write_i64(f, value.value_integer); + ir_write_big_int(f, value.value_integer); ir_write_str_lit(f, " to "); ir_print_type(f, m, t_rawptr); ir_write_str_lit(f, ")"); } } else { - ir_write_i64(f, value.value_integer); + ir_write_big_int(f, value.value_integer); } break; } diff --git a/src/main.cpp b/src/main.cpp index 996d33089..d266bff09 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4,6 +4,7 @@ #include "timings.cpp" #include "build_settings.cpp" #include "tokenizer.cpp" +#include "big_int.cpp" #include "exact_value.cpp" #include "parser.hpp" @@ -408,7 +409,7 @@ bool parse_build_flags(Array<String> args) { } case BuildFlag_OptimizationLevel: GB_ASSERT(value.kind == ExactValue_Integer); - build_context.optimization_level = cast(i32)value.value_integer; + build_context.optimization_level = cast(i32)big_int_to_i64(&value.value_integer); break; case BuildFlag_ShowTimings: GB_ASSERT(value.kind == ExactValue_Invalid); @@ -416,7 +417,7 @@ bool parse_build_flags(Array<String> args) { break; case BuildFlag_ThreadCount: { GB_ASSERT(value.kind == ExactValue_Integer); - isize count = cast(isize)value.value_integer; + isize count = cast(isize)big_int_to_i64(&value.value_integer); if (count <= 0) { gb_printf_err("%.*s expected a positive non-zero number, got %.*s\n", LIT(name), LIT(param)); build_context.thread_count = 0; @@ -716,6 +717,7 @@ int main(int arg_count, char **arg_ptr) { init_string_buffer_memory(); init_global_error_collector(); + global_big_int_init(); arena_init(&global_ast_arena, heap_allocator()); array_init(&library_collections, heap_allocator()); diff --git a/src/parser.cpp b/src/parser.cpp index 39f67b4c4..c2e0130ce 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -3470,22 +3470,6 @@ Ast *parse_import_decl(AstFile *f, ImportDeclKind kind) { return s; } -// Ast *parse_export_decl(AstFile *f) { -// CommentGroup *docs = f->lead_comment; -// Token token = expect_token(f, Token_export); -// Token file_path = expect_token_after(f, Token_String, "export"); -// Ast *s = nullptr; -// if (f->curr_proc != nullptr) { -// syntax_error(token, "You cannot use 'export' within a procedure. This must be done at the file scope"); -// s = ast_bad_decl(f, token, file_path); -// } else { -// s = ast_export_decl(f, token, file_path, docs, f->line_comment); -// array_add(&f->imports_and_exports, s); -// } -// expect_semicolon(f, s); -// return s; -// } - Ast *parse_foreign_decl(AstFile *f) { CommentGroup *docs = f->lead_comment; Token token = expect_token(f, Token_foreign); @@ -3668,17 +3652,6 @@ Ast *parse_stmt(AstFile *f) { Token name = expect_token(f, Token_Ident); String tag = name.string; - // if (tag == "shared_global_scope") { - // if (f->curr_proc == nullptr) { - // f->is_global_scope = true; - // s = ast_empty_stmt(f, f->curr_token); - // } else { - // syntax_error(token, "You cannot use #shared_global_scope within a procedure. This must be done at the file scope"); - // s = ast_bad_decl(f, token, f->curr_token); - // } - // expect_semicolon(f, s); - // return s; - // } else if (tag == "bounds_check") { s = parse_stmt(f); s->stmt_state_flags |= StmtStateFlag_bounds_check; @@ -3734,9 +3707,7 @@ Ast *parse_stmt(AstFile *f) { return s; } - syntax_error(token, - "Expected a statement, got '%.*s'", - LIT(token_strings[token.kind])); + syntax_error(token, "Expected a statement, got '%.*s'", LIT(token_strings[token.kind])); fix_advance_to_next_stmt(f); return ast_bad_stmt(f, token, f->curr_token); } @@ -3938,39 +3909,25 @@ bool try_add_import_path(Parser *p, String const &path, String const &rel_path, } - if (rd_err != ReadDirectory_None) { - if (pos.line != 0) { - gb_printf_err("%.*s(%td:%td) ", LIT(pos.file), pos.line, pos.column); - } - gb_mutex_lock(&global_error_collector.mutex); - defer (gb_mutex_unlock(&global_error_collector.mutex)); - global_error_collector.count++; - - - switch (rd_err) { - case ReadDirectory_InvalidPath: - gb_printf_err("Invalid path: %.*s\n", LIT(rel_path)); - return false; - - case ReadDirectory_NotExists: - gb_printf_err("Path does not exist: %.*s\n", LIT(rel_path)); - return false; - - case ReadDirectory_NotDir: - gb_printf_err("Expected a directory for a package, got a file: %.*s\n", LIT(rel_path)); - return false; - - case ReadDirectory_Unknown: - gb_printf_err("Unknown error whilst reading path %.*s\n", LIT(rel_path)); - return false; - case ReadDirectory_Permission: - gb_printf_err("Unknown error whilst reading path %.*s\n", LIT(rel_path)); - return false; - - case ReadDirectory_Empty: - gb_printf_err("Empty directory: %.*s\n", LIT(rel_path)); - return false; - } + switch (rd_err) { + case ReadDirectory_InvalidPath: + error(pos, "Invalid path: %.*s", LIT(rel_path)); + return false; + case ReadDirectory_NotExists: + error(pos, "Path does not exist: %.*s", LIT(rel_path)); + return false; + case ReadDirectory_Permission: + error(pos, "Unknown error whilst reading path %.*s", LIT(rel_path)); + return false; + case ReadDirectory_NotDir: + error(pos, "Expected a directory for a package, got a file: %.*s", LIT(rel_path)); + return false; + case ReadDirectory_Empty: + error(pos, "Empty directory: %.*s", LIT(rel_path)); + return false; + case ReadDirectory_Unknown: + error(pos, "Unknown error whilst reading path %.*s", LIT(rel_path)); + return false; } for_array(list_index, list) { @@ -4353,44 +4310,34 @@ ParseFileError process_imported_file(Parser *p, ImportedFile imported_file) { ParseFileError err = init_ast_file(file, fi->fullpath, &err_pos); if (err != ParseFile_None) { - gb_mutex_lock(&global_error_collector.mutex); - defer (gb_mutex_unlock(&global_error_collector.mutex)); - global_error_collector.count++; - if (err == ParseFile_EmptyFile) { if (fi->fullpath == p->init_fullpath) { - gb_printf_err("Initial file is empty - %.*s\n", LIT(p->init_fullpath)); + error(pos, "Initial file is empty - %.*s\n", LIT(p->init_fullpath)); gb_exit(1); } goto skip; } - if (pos.line != 0) { - gb_printf_err("%.*s(%td:%td) ", LIT(pos.file), pos.line, pos.column); - } - gb_printf_err("Failed to parse file: %.*s\n\t", LIT(fi->name)); switch (err) { case ParseFile_WrongExtension: - gb_printf_err("Invalid file extension: File must have the extension '.odin'"); + error(pos, "Failed to parse file: %.*s; invalid file extension: File must have the extension '.odin'", LIT(fi->name)); break; case ParseFile_InvalidFile: - gb_printf_err("Invalid file or cannot be found"); + error(pos, "Failed to parse file: %.*s; invalid file or cannot be found", LIT(fi->name)); break; case ParseFile_Permission: - gb_printf_err("File permissions problem"); + error(pos, "Failed to parse file: %.*s; file permissions problem", LIT(fi->name)); break; case ParseFile_NotFound: - gb_printf_err("File cannot be found ('%.*s')", LIT(fi->fullpath)); + error(pos, "Failed to parse file: %.*s; file cannot be found ('%.*s')", LIT(fi->name), LIT(fi->fullpath)); break; case ParseFile_InvalidToken: - gb_printf_err("Invalid token found in file at (%td:%td)", err_pos.line, err_pos.column); + error(pos, "Failed to parse file: %.*s; invalid token found in file at (%td:%td)", LIT(fi->name), err_pos.line, err_pos.column); break; case ParseFile_EmptyFile: - gb_printf_err("File contains no tokens"); + error(pos, "Failed to parse file: %.*s; file contains no tokens", LIT(fi->name)); break; } - gb_printf_err("\n"); - return err; } diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index da0e6517d..c1f4bd4f1 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -292,6 +292,16 @@ void error(Token token, char *fmt, ...) { va_end(va); } +void error(TokenPos pos, char *fmt, ...) { + va_list va; + va_start(va, fmt); + Token token = {}; + token.pos = pos; + error_va(token, fmt, va); + va_end(va); +} + + void syntax_error(Token token, char *fmt, ...) { va_list va; va_start(va, fmt); |