Browse Source

BigInt support in the constant system

gingerBill 7 years ago
parent
commit
c3c7834246
13 changed files with 1670 additions and 347 deletions
  1. 79 79
      core/types/types.odin
  2. 2 2
      core/unicode/utf16/utf16.odin
  3. 1262 0
      src/big_int.cpp
  4. 136 111
      src/check_expr.cpp
  5. 10 11
      src/check_stmt.cpp
  6. 25 6
      src/check_type.cpp
  7. 44 2
      src/common.cpp
  8. 57 49
      src/exact_value.cpp
  9. 2 2
      src/ir.cpp
  10. 12 3
      src/ir_print.cpp
  11. 4 2
      src/main.cpp
  12. 27 80
      src/parser.cpp
  13. 10 0
      src/tokenizer.cpp

+ 79 - 79
core/types/types.odin

@@ -1,8 +1,8 @@
 package types
 
-import "core:runtime"
+import rt "core:runtime"
 
-are_types_identical :: proc(a, b: ^runtime.Type_Info) -> bool {
+are_types_identical :: proc(a, b: ^rt.Type_Info) -> bool {
 	if a == b do return true;
 
 	if (a == nil && b != nil) ||
@@ -17,47 +17,47 @@ are_types_identical :: proc(a, b: ^runtime.Type_Info) -> bool {
 	}
 
 	switch x in a.variant {
-	case runtime.Type_Info_Named:
-		y, ok := b.variant.(runtime.Type_Info_Named);
+	case rt.Type_Info_Named:
+		y, ok := b.variant.(rt.Type_Info_Named);
 		if !ok do return false;
 		return x.base == y.base;
 
-	case runtime.Type_Info_Integer:
-		y, ok := b.variant.(runtime.Type_Info_Integer);
+	case rt.Type_Info_Integer:
+		y, ok := b.variant.(rt.Type_Info_Integer);
 		if !ok do return false;
 		return x.signed == y.signed;
 
-	case runtime.Type_Info_Rune:
-		_, ok := b.variant.(runtime.Type_Info_Rune);
+	case rt.Type_Info_Rune:
+		_, ok := b.variant.(rt.Type_Info_Rune);
 		return ok;
 
-	case runtime.Type_Info_Float:
-		_, ok := b.variant.(runtime.Type_Info_Float);
+	case rt.Type_Info_Float:
+		_, ok := b.variant.(rt.Type_Info_Float);
 		return ok;
 
-	case runtime.Type_Info_Complex:
-		_, ok := b.variant.(runtime.Type_Info_Complex);
+	case rt.Type_Info_Complex:
+		_, ok := b.variant.(rt.Type_Info_Complex);
 		return ok;
 
-	case runtime.Type_Info_String:
-		_, ok := b.variant.(runtime.Type_Info_String);
+	case rt.Type_Info_String:
+		_, ok := b.variant.(rt.Type_Info_String);
 		return ok;
 
-	case runtime.Type_Info_Boolean:
-		_, ok := b.variant.(runtime.Type_Info_Boolean);
+	case rt.Type_Info_Boolean:
+		_, ok := b.variant.(rt.Type_Info_Boolean);
 		return ok;
 
-	case runtime.Type_Info_Any:
-		_, ok := b.variant.(runtime.Type_Info_Any);
+	case rt.Type_Info_Any:
+		_, ok := b.variant.(rt.Type_Info_Any);
 		return ok;
 
-	case runtime.Type_Info_Pointer:
-		y, ok := b.variant.(runtime.Type_Info_Pointer);
+	case rt.Type_Info_Pointer:
+		y, ok := b.variant.(rt.Type_Info_Pointer);
 		if !ok do return false;
 		return are_types_identical(x.elem, y.elem);
 
-	case runtime.Type_Info_Procedure:
-		y, ok := b.variant.(runtime.Type_Info_Procedure);
+	case rt.Type_Info_Procedure:
+		y, ok := b.variant.(rt.Type_Info_Procedure);
 		if !ok do return false;
 		switch {
 		case x.variadic   != y.variadic,
@@ -67,24 +67,24 @@ are_types_identical :: proc(a, b: ^runtime.Type_Info) -> bool {
 
 		return are_types_identical(x.params, y.params) && are_types_identical(x.results, y.results);
 
-	case runtime.Type_Info_Array:
-		y, ok := b.variant.(runtime.Type_Info_Array);
+	case rt.Type_Info_Array:
+		y, ok := b.variant.(rt.Type_Info_Array);
 		if !ok do return false;
 		if x.count != y.count do return false;
 		return are_types_identical(x.elem, y.elem);
 
-	case runtime.Type_Info_Dynamic_Array:
-		y, ok := b.variant.(runtime.Type_Info_Dynamic_Array);
+	case rt.Type_Info_Dynamic_Array:
+		y, ok := b.variant.(rt.Type_Info_Dynamic_Array);
 		if !ok do return false;
 		return are_types_identical(x.elem, y.elem);
 
-	case runtime.Type_Info_Slice:
-		y, ok := b.variant.(runtime.Type_Info_Slice);
+	case rt.Type_Info_Slice:
+		y, ok := b.variant.(rt.Type_Info_Slice);
 		if !ok do return false;
 		return are_types_identical(x.elem, y.elem);
 
-	case runtime.Type_Info_Tuple:
-		y, ok := b.variant.(runtime.Type_Info_Tuple);
+	case rt.Type_Info_Tuple:
+		y, ok := b.variant.(rt.Type_Info_Tuple);
 		if !ok do return false;
 		if len(x.types) != len(y.types) do return false;
 		for _, i in x.types {
@@ -95,8 +95,8 @@ are_types_identical :: proc(a, b: ^runtime.Type_Info) -> bool {
 		}
 		return true;
 
-	case runtime.Type_Info_Struct:
-		y, ok := b.variant.(runtime.Type_Info_Struct);
+	case rt.Type_Info_Struct:
+		y, ok := b.variant.(rt.Type_Info_Struct);
 		if !ok do return false;
 	   	switch {
 		case len(x.types)   != len(y.types),
@@ -114,8 +114,8 @@ are_types_identical :: proc(a, b: ^runtime.Type_Info) -> bool {
 		}
 		return true;
 
-	case runtime.Type_Info_Union:
-		y, ok := b.variant.(runtime.Type_Info_Union);
+	case rt.Type_Info_Union:
+		y, ok := b.variant.(rt.Type_Info_Union);
 		if !ok do return false;
 		if len(x.variants) != len(y.variants) do return false;
 
@@ -125,17 +125,17 @@ are_types_identical :: proc(a, b: ^runtime.Type_Info) -> bool {
 		}
 		return true;
 
-	case runtime.Type_Info_Enum:
+	case rt.Type_Info_Enum:
 		// NOTE(bill): Should be handled above
 		return false;
 
-	case runtime.Type_Info_Map:
-		y, ok := b.variant.(runtime.Type_Info_Map);
+	case rt.Type_Info_Map:
+		y, ok := b.variant.(rt.Type_Info_Map);
 		if !ok do return false;
 		return are_types_identical(x.key, y.key) && are_types_identical(x.value, y.value);
 
-	case runtime.Type_Info_Bit_Field:
-		y, ok := b.variant.(runtime.Type_Info_Bit_Field);
+	case rt.Type_Info_Bit_Field:
+		y, ok := b.variant.(rt.Type_Info_Bit_Field);
 		if !ok do return false;
 		if len(x.names) != len(y.names) do return false;
 
@@ -156,101 +156,101 @@ are_types_identical :: proc(a, b: ^runtime.Type_Info) -> bool {
 }
 
 
-is_signed :: proc(info: ^runtime.Type_Info) -> bool {
+is_signed :: proc(info: ^rt.Type_Info) -> bool {
 	if info == nil do return false;
-	switch i in runtime.type_info_base(info).variant {
-	case runtime.Type_Info_Integer: return i.signed;
-	case runtime.Type_Info_Float:   return true;
+	switch i in rt.type_info_base(info).variant {
+	case rt.Type_Info_Integer: return i.signed;
+	case rt.Type_Info_Float:   return true;
 	}
 	return false;
 }
-is_integer :: proc(info: ^runtime.Type_Info) -> bool {
+is_integer :: proc(info: ^rt.Type_Info) -> bool {
 	if info == nil do return false;
-	_, ok := runtime.type_info_base(info).variant.(runtime.Type_Info_Integer);
+	_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Integer);
 	return ok;
 }
-is_rune :: proc(info: ^runtime.Type_Info) -> bool {
+is_rune :: proc(info: ^rt.Type_Info) -> bool {
 	if info == nil do return false;
-	_, ok := runtime.type_info_base(info).variant.(runtime.Type_Info_Rune);
+	_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Rune);
 	return ok;
 }
-is_float :: proc(info: ^runtime.Type_Info) -> bool {
+is_float :: proc(info: ^rt.Type_Info) -> bool {
 	if info == nil do return false;
-	_, ok := runtime.type_info_base(info).variant.(runtime.Type_Info_Float);
+	_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Float);
 	return ok;
 }
-is_complex :: proc(info: ^runtime.Type_Info) -> bool {
+is_complex :: proc(info: ^rt.Type_Info) -> bool {
 	if info == nil do return false;
-	_, ok := runtime.type_info_base(info).variant.(runtime.Type_Info_Complex);
+	_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Complex);
 	return ok;
 }
-is_any :: proc(info: ^runtime.Type_Info) -> bool {
+is_any :: proc(info: ^rt.Type_Info) -> bool {
 	if info == nil do return false;
-	_, ok := runtime.type_info_base(info).variant.(runtime.Type_Info_Any);
+	_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Any);
 	return ok;
 }
-is_string :: proc(info: ^runtime.Type_Info) -> bool {
+is_string :: proc(info: ^rt.Type_Info) -> bool {
 	if info == nil do return false;
-	_, ok := runtime.type_info_base(info).variant.(runtime.Type_Info_String);
+	_, ok := rt.type_info_base(info).variant.(rt.Type_Info_String);
 	return ok;
 }
-is_boolean :: proc(info: ^runtime.Type_Info) -> bool {
+is_boolean :: proc(info: ^rt.Type_Info) -> bool {
 	if info == nil do return false;
-	_, ok := runtime.type_info_base(info).variant.(runtime.Type_Info_Boolean);
+	_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Boolean);
 	return ok;
 }
-is_pointer :: proc(info: ^runtime.Type_Info) -> bool {
+is_pointer :: proc(info: ^rt.Type_Info) -> bool {
 	if info == nil do return false;
-	_, ok := runtime.type_info_base(info).variant.(runtime.Type_Info_Pointer);
+	_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Pointer);
 	return ok;
 }
-is_procedure :: proc(info: ^runtime.Type_Info) -> bool {
+is_procedure :: proc(info: ^rt.Type_Info) -> bool {
 	if info == nil do return false;
-	_, ok := runtime.type_info_base(info).variant.(runtime.Type_Info_Procedure);
+	_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Procedure);
 	return ok;
 }
-is_array :: proc(info: ^runtime.Type_Info) -> bool {
+is_array :: proc(info: ^rt.Type_Info) -> bool {
 	if info == nil do return false;
-	_, ok := runtime.type_info_base(info).variant.(runtime.Type_Info_Array);
+	_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Array);
 	return ok;
 }
-is_dynamic_array :: proc(info: ^runtime.Type_Info) -> bool {
+is_dynamic_array :: proc(info: ^rt.Type_Info) -> bool {
 	if info == nil do return false;
-	_, ok := runtime.type_info_base(info).variant.(runtime.Type_Info_Dynamic_Array);
+	_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Dynamic_Array);
 	return ok;
 }
-is_dynamic_map :: proc(info: ^runtime.Type_Info) -> bool {
+is_dynamic_map :: proc(info: ^rt.Type_Info) -> bool {
 	if info == nil do return false;
-	_, ok := runtime.type_info_base(info).variant.(runtime.Type_Info_Map);
+	_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Map);
 	return ok;
 }
-is_slice :: proc(info: ^runtime.Type_Info) -> bool {
+is_slice :: proc(info: ^rt.Type_Info) -> bool {
 	if info == nil do return false;
-	_, ok := runtime.type_info_base(info).variant.(runtime.Type_Info_Slice);
+	_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Slice);
 	return ok;
 }
-is_tuple :: proc(info: ^runtime.Type_Info) -> bool {
+is_tuple :: proc(info: ^rt.Type_Info) -> bool {
 	if info == nil do return false;
-	_, ok := runtime.type_info_base(info).variant.(runtime.Type_Info_Tuple);
+	_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Tuple);
 	return ok;
 }
-is_struct :: proc(info: ^runtime.Type_Info) -> bool {
+is_struct :: proc(info: ^rt.Type_Info) -> bool {
 	if info == nil do return false;
-	s, ok := runtime.type_info_base(info).variant.(runtime.Type_Info_Struct);
+	s, ok := rt.type_info_base(info).variant.(rt.Type_Info_Struct);
 	return ok && !s.is_raw_union;
 }
-is_raw_union :: proc(info: ^runtime.Type_Info) -> bool {
+is_raw_union :: proc(info: ^rt.Type_Info) -> bool {
 	if info == nil do return false;
-	s, ok := runtime.type_info_base(info).variant.(runtime.Type_Info_Struct);
+	s, ok := rt.type_info_base(info).variant.(rt.Type_Info_Struct);
 	return ok && s.is_raw_union;
 }
-is_union :: proc(info: ^runtime.Type_Info) -> bool {
+is_union :: proc(info: ^rt.Type_Info) -> bool {
 	if info == nil do return false;
-	_, ok := runtime.type_info_base(info).variant.(runtime.Type_Info_Union);
+	_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Union);
 	return ok;
 }
-is_enum :: proc(info: ^runtime.Type_Info) -> bool {
+is_enum :: proc(info: ^rt.Type_Info) -> bool {
 	if info == nil do return false;
-	_, ok := runtime.type_info_base(info).variant.(runtime.Type_Info_Enum);
+	_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Enum);
 	return ok;
 }

+ 2 - 2
core/unicode/utf16/utf16.odin

@@ -1,7 +1,7 @@
 package utf16
 
-REPLACEMENT_CHAR :: '\uFFFD';
-MAX_RUNE         :: '\U0010FFFF';
+REPLACEMENT_CHAR :: '\ufffd';
+MAX_RUNE         :: '\U0010ffff';
 
 _surr1           :: 0xd800;
 _surr2           :: 0xdc00;

+ 1262 - 0
src/big_int.cpp

@@ -0,0 +1,1262 @@
+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(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_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_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;
+	}
+
+	big_int_alloc(dst, cast(i32)(x->len - word_shift_len), dst->len);
+	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;
+		goto end;
+	} else if (m == 0) {
+		goto end;
+	}
+	big_int_alloc(&q, m, m);
+	bi__divWVW(&q, 0, x, y, &r);
+	big_int_normalize(&q);
+end:
+	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;
+	}
+
+	GB_ASSERT(x->neg == y->neg);
+
+	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);
+
+	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;
+	}
+	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;
+	}
+
+	GB_ASSERT(x->neg == y->neg);
+
+	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);
+
+	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_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;
+	}
+
+	GB_ASSERT(x->neg == y->neg);
+
+	dst->neg = false;
+	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_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;
+	}
+
+	GB_ASSERT(x->neg == y->neg);
+
+	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);
+}
+
+
+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;
+			}
+		}
+		goto end;
+	}
+
+	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;
+	}
+
+end:
+	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);
+}

+ 136 - 111
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);

+ 10 - 11
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)) {

+ 25 - 6
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;
 		}
 	}

+ 44 - 2
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"

+ 57 - 49
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,37 @@ 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_rem(&c, a, b);
+			big_int_add_eq(&c, b);
+			big_int_rem_eq(&c, 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 +647,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;
 	}

+ 2 - 2
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);

+ 12 - 3
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;
 	}

+ 4 - 2
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());

+ 27 - 80
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;
 	}

+ 10 - 0
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);