diff --git a/.gitignore b/.gitignore index 361488f316..875aa6d3ac 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ build-llvm-debug/ /.cproject /.project /.settings/ +build-llvm-debug/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 033eda9570..1098132476 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,7 +44,8 @@ include_directories( set(ZIG_SOURCES "${CMAKE_SOURCE_DIR}/src/analyze.cpp" "${CMAKE_SOURCE_DIR}/src/ast_render.cpp" - "${CMAKE_SOURCE_DIR}/src/bignum.cpp" + "${CMAKE_SOURCE_DIR}/src/bigfloat.cpp" + "${CMAKE_SOURCE_DIR}/src/bigint.cpp" "${CMAKE_SOURCE_DIR}/src/buffer.cpp" "${CMAKE_SOURCE_DIR}/src/c_tokenizer.cpp" "${CMAKE_SOURCE_DIR}/src/codegen.cpp" diff --git a/doc/langref.md b/doc/langref.md index 4b3e1a706f..ad9f39b214 100644 --- a/doc/langref.md +++ b/doc/langref.md @@ -143,7 +143,7 @@ StructLiteralField = "." Symbol "=" Expression PrefixOp = "!" | "-" | "~" | "*" | ("&" option("const") option("volatile")) | "?" | "%" | "%%" | "??" | "-%" -PrimaryExpression = Number | String | CharLiteral | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | (option("extern") FnProto) | AsmExpression | ("error" "." Symbol) | ContainerDecl +PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | (option("extern") FnProto) | AsmExpression | ("error" "." Symbol) | ContainerDecl ArrayType = "[" option(Expression) "]" option("const") TypeExpr diff --git a/src/all_types.hpp b/src/all_types.hpp index 7a9dcdc369..1cef4091f0 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -13,7 +13,8 @@ #include "zig_llvm.hpp" #include "hash_map.hpp" #include "errmsg.hpp" -#include "bignum.hpp" +#include "bigint.hpp" +#include "bigfloat.hpp" #include "target.hpp" struct AstNode; @@ -215,6 +216,11 @@ struct ConstGlobalRefs { LLVMValueRef llvm_global; }; +enum ConstNumLitKind { + ConstNumLitKindInt, + ConstNumLitKindFloat, +}; + struct ConstExprValue { TypeTableEntry *type; ConstValSpecial special; @@ -222,7 +228,8 @@ struct ConstExprValue { union { // populated if special == ConstValSpecialStatic - BigNum x_bignum; + BigInt x_bigint; + BigFloat x_bigfloat; bool x_bool; ConstFn x_fn; ConstBoundFnValue x_bound_fn; @@ -347,7 +354,8 @@ enum NodeType { NodeTypeTestDecl, NodeTypeBinOpExpr, NodeTypeUnwrapErrorExpr, - NodeTypeNumberLiteral, + NodeTypeFloatLiteral, + NodeTypeIntLiteral, NodeTypeStringLiteral, NodeTypeCharLiteral, NodeTypeSymbol, @@ -748,14 +756,18 @@ struct AstNodeCharLiteral { uint8_t value; }; -struct AstNodeNumberLiteral { - BigNum *bignum; +struct AstNodeFloatLiteral { + BigFloat *bigfloat; // overflow is true if when parsing the number, we discovered it would not - // fit without losing data in a uint64_t or double + // fit without losing data in a double bool overflow; }; +struct AstNodeIntLiteral { + BigInt *bigint; +}; + struct AstNodeStructValueField { Buf *name; AstNode *expr; @@ -854,7 +866,8 @@ struct AstNode { AstNodeStructField struct_field; AstNodeStringLiteral string_literal; AstNodeCharLiteral char_literal; - AstNodeNumberLiteral number_literal; + AstNodeFloatLiteral float_literal; + AstNodeIntLiteral int_literal; AstNodeContainerInitExpr container_init_expr; AstNodeStructValueField struct_val_field; AstNodeNullLiteral null_literal; diff --git a/src/analyze.cpp b/src/analyze.cpp index 85ef997502..574fa74879 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -2194,7 +2194,8 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) { case NodeTypeFnCallExpr: case NodeTypeArrayAccessExpr: case NodeTypeSliceExpr: - case NodeTypeNumberLiteral: + case NodeTypeFloatLiteral: + case NodeTypeIntLiteral: case NodeTypeStringLiteral: case NodeTypeCharLiteral: case NodeTypeBoolLiteral: @@ -3247,10 +3248,17 @@ static uint32_t hash_const_val(ConstExprValue *const_val) { case TypeTableEntryIdInt: case TypeTableEntryIdNumLitInt: case TypeTableEntryIdEnumTag: - return ((uint32_t)(bignum_to_twos_complement(&const_val->data.x_bignum) % UINT32_MAX)) * (uint32_t)1331471175; + { + uint32_t result = 1331471175; + for (size_t i = 0; i < const_val->data.x_bigint.digit_count; i += 1) { + uint64_t digit = bigint_ptr(&const_val->data.x_bigint)[i]; + result ^= ((uint32_t)(digit >> 32)) ^ (uint32_t)(result); + } + return result; + } case TypeTableEntryIdFloat: case TypeTableEntryIdNumLitFloat: - return (uint32_t)(const_val->data.x_bignum.data.x_float * (uint32_t)UINT32_MAX); + return (uint32_t)(const_val->data.x_bigfloat.value * (uint32_t)UINT32_MAX); case TypeTableEntryIdArgTuple: return (uint32_t)const_val->data.x_arg_tuple.start_index * (uint32_t)281907309 + (uint32_t)const_val->data.x_arg_tuple.end_index * (uint32_t)2290442768; @@ -3473,7 +3481,7 @@ void init_const_str_lit(CodeGen *g, ConstExprValue *const_val, Buf *str) { ConstExprValue *this_char = &const_val->data.x_array.s_none.elements[i]; this_char->special = ConstValSpecialStatic; this_char->type = g->builtin_types.entry_u8; - bignum_init_unsigned(&this_char->data.x_bignum, (uint8_t)buf_ptr(str)[i]); + bigint_init_unsigned(&this_char->data.x_bigint, (uint8_t)buf_ptr(str)[i]); } } @@ -3494,12 +3502,12 @@ void init_const_c_str_lit(CodeGen *g, ConstExprValue *const_val, Buf *str) { ConstExprValue *this_char = &array_val->data.x_array.s_none.elements[i]; this_char->special = ConstValSpecialStatic; this_char->type = g->builtin_types.entry_u8; - bignum_init_unsigned(&this_char->data.x_bignum, (uint8_t)buf_ptr(str)[i]); + bigint_init_unsigned(&this_char->data.x_bigint, (uint8_t)buf_ptr(str)[i]); } ConstExprValue *null_char = &array_val->data.x_array.s_none.elements[len_with_null - 1]; null_char->special = ConstValSpecialStatic; null_char->type = g->builtin_types.entry_u8; - bignum_init_unsigned(&null_char->data.x_bignum, 0); + bigint_init_unsigned(&null_char->data.x_bigint, 0); // then make the pointer point to it const_val->special = ConstValSpecialStatic; @@ -3518,8 +3526,8 @@ ConstExprValue *create_const_c_str_lit(CodeGen *g, Buf *str) { void init_const_unsigned_negative(ConstExprValue *const_val, TypeTableEntry *type, uint64_t x, bool negative) { const_val->special = ConstValSpecialStatic; const_val->type = type; - bignum_init_unsigned(&const_val->data.x_bignum, x); - const_val->data.x_bignum.is_negative = negative; + bigint_init_unsigned(&const_val->data.x_bigint, x); + const_val->data.x_bigint.is_negative = negative; } ConstExprValue *create_const_unsigned_negative(TypeTableEntry *type, uint64_t x, bool negative) { @@ -3539,7 +3547,7 @@ ConstExprValue *create_const_usize(CodeGen *g, uint64_t x) { void init_const_signed(ConstExprValue *const_val, TypeTableEntry *type, int64_t x) { const_val->special = ConstValSpecialStatic; const_val->type = type; - bignum_init_signed(&const_val->data.x_bignum, x); + bigint_init_signed(&const_val->data.x_bigint, x); } ConstExprValue *create_const_signed(TypeTableEntry *type, int64_t x) { @@ -3551,7 +3559,7 @@ ConstExprValue *create_const_signed(TypeTableEntry *type, int64_t x) { void init_const_float(ConstExprValue *const_val, TypeTableEntry *type, double value) { const_val->special = ConstValSpecialStatic; const_val->type = type; - bignum_init_float(&const_val->data.x_bignum, value); + bigfloat_init_float(&const_val->data.x_bigfloat, value); } ConstExprValue *create_const_float(TypeTableEntry *type, double value) { @@ -3788,12 +3796,13 @@ bool const_values_equal(ConstExprValue *a, ConstExprValue *b) { return a->data.x_fn.fn_entry == b->data.x_fn.fn_entry; case TypeTableEntryIdBool: return a->data.x_bool == b->data.x_bool; - case TypeTableEntryIdInt: case TypeTableEntryIdFloat: case TypeTableEntryIdNumLitFloat: + return bigfloat_cmp(&a->data.x_bigfloat, &b->data.x_bigfloat) == CmpEQ; + case TypeTableEntryIdInt: case TypeTableEntryIdNumLitInt: case TypeTableEntryIdEnumTag: - return bignum_cmp_eq(&a->data.x_bignum, &b->data.x_bignum); + return bigint_cmp(&a->data.x_bigint, &b->data.x_bigint) == CmpEQ; case TypeTableEntryIdPointer: if (a->data.x_ptr.special != b->data.x_ptr.special) return false; @@ -3876,58 +3885,47 @@ bool const_values_equal(ConstExprValue *a, ConstExprValue *b) { zig_unreachable(); } -uint64_t max_unsigned_val(TypeTableEntry *type_entry) { - assert(type_entry->id == TypeTableEntryIdInt); - if (type_entry->data.integral.bit_count == 64) { - return UINT64_MAX; - } else { - return (((uint64_t)1) << type_entry->data.integral.bit_count) - 1; - } -} - -static int64_t max_signed_val(TypeTableEntry *type_entry) { - assert(type_entry->id == TypeTableEntryIdInt); - - if (type_entry->data.integral.bit_count == 64) { - return INT64_MAX; - } else { - return (((uint64_t)1) << (type_entry->data.integral.bit_count - 1)) - 1; - } -} - -int64_t min_signed_val(TypeTableEntry *type_entry) { - assert(type_entry->id == TypeTableEntryIdInt); - if (type_entry->data.integral.bit_count == 64) { - return INT64_MIN; - } else { - return -((int64_t)(((uint64_t)1) << (type_entry->data.integral.bit_count - 1))); - } -} - -void eval_min_max_value_int(CodeGen *g, TypeTableEntry *int_type, BigNum *bignum, bool is_max) { +void eval_min_max_value_int(CodeGen *g, TypeTableEntry *int_type, BigInt *bigint, bool is_max) { assert(int_type->id == TypeTableEntryIdInt); + if (int_type->data.integral.bit_count == 0) { + bigint_init_unsigned(bigint, 0); + return; + } if (is_max) { - if (int_type->data.integral.is_signed) { - int64_t val = max_signed_val(int_type); - bignum_init_signed(bignum, val); - } else { - uint64_t val = max_unsigned_val(int_type); - bignum_init_unsigned(bignum, val); - } + // is_signed=true (1 << (bit_count - 1)) - 1 + // is_signed=false (1 << (bit_count - 0)) - 1 + BigInt one = {0}; + bigint_init_unsigned(&one, 1); + + size_t shift_amt = int_type->data.integral.bit_count - (int_type->data.integral.is_signed ? 1 : 0); + BigInt bit_count_bi = {0}; + bigint_init_unsigned(&bit_count_bi, shift_amt); + + BigInt shifted_bi = {0}; + bigint_shl(&shifted_bi, &one, &bit_count_bi); + + bigint_sub(bigint, &shifted_bi, &one); + } else if (int_type->data.integral.is_signed) { + // - (1 << (bit_count - 1)) + BigInt one = {0}; + bigint_init_unsigned(&one, 1); + + BigInt bit_count_bi = {0}; + bigint_init_unsigned(&bit_count_bi, int_type->data.integral.bit_count - 1); + + BigInt shifted_bi = {0}; + bigint_shl(&shifted_bi, &one, &bit_count_bi); + + bigint_negate(bigint, &shifted_bi); } else { - if (int_type->data.integral.is_signed) { - int64_t val = min_signed_val(int_type); - bignum_init_signed(bignum, val); - } else { - bignum_init_unsigned(bignum, 0); - } + bigint_init_unsigned(bigint, 0); } } void eval_min_max_value(CodeGen *g, TypeTableEntry *type_entry, ConstExprValue *const_val, bool is_max) { if (type_entry->id == TypeTableEntryIdInt) { const_val->special = ConstValSpecialStatic; - eval_min_max_value_int(g, type_entry, &const_val->data.x_bignum, is_max); + eval_min_max_value_int(g, type_entry, &const_val->data.x_bigint, is_max); } else if (type_entry->id == TypeTableEntryIdFloat) { zig_panic("TODO analyze_min_max_value float"); } else if (type_entry->id == TypeTableEntryIdBool) { @@ -3967,33 +3965,16 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { buf_appendf(buf, "{}"); return; case TypeTableEntryIdNumLitFloat: - buf_appendf(buf, "%f", const_val->data.x_bignum.data.x_float); + case TypeTableEntryIdFloat: + bigfloat_write_buf(buf, &const_val->data.x_bigfloat); return; case TypeTableEntryIdNumLitInt: - { - BigNum *bignum = &const_val->data.x_bignum; - const char *negative_str = bignum->is_negative ? "-" : ""; - buf_appendf(buf, "%s%" ZIG_PRI_llu, negative_str, bignum->data.x_uint); - return; - } + case TypeTableEntryIdInt: + bigint_write_buf(buf, &const_val->data.x_bigint, 10); + return; case TypeTableEntryIdMetaType: buf_appendf(buf, "%s", buf_ptr(&const_val->data.x_type->name)); return; - case TypeTableEntryIdInt: - { - BigNum *bignum = &const_val->data.x_bignum; - assert(bignum->kind == BigNumKindInt); - const char *negative_str = bignum->is_negative ? "-" : ""; - buf_appendf(buf, "%s%" ZIG_PRI_llu, negative_str, bignum->data.x_uint); - } - return; - case TypeTableEntryIdFloat: - { - BigNum *bignum = &const_val->data.x_bignum; - assert(bignum->kind == BigNumKindFloat); - buf_appendf(buf, "%f", bignum->data.x_float); - } - return; case TypeTableEntryIdUnreachable: buf_appendf(buf, "@unreachable()"); return; @@ -4060,7 +4041,7 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { buf_append_char(buf, '"'); for (uint64_t i = 0; i < len; i += 1) { ConstExprValue *child_value = &const_val->data.x_array.s_none.elements[i]; - uint64_t big_c = child_value->data.x_bignum.data.x_uint; + uint64_t big_c = bigint_as_unsigned(&child_value->data.x_bigint); assert(big_c <= UINT8_MAX); uint8_t c = (uint8_t)big_c; if (c == '"') { @@ -4146,7 +4127,8 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { case TypeTableEntryIdEnumTag: { TypeTableEntry *enum_type = type_entry->data.enum_tag.enum_type; - TypeEnumField *field = &enum_type->data.enumeration.fields[const_val->data.x_bignum.data.x_uint]; + size_t field_index = bigint_as_unsigned(&const_val->data.x_bigint); + TypeEnumField *field = &enum_type->data.enumeration.fields[field_index]; buf_appendf(buf, "%s.%s", buf_ptr(&enum_type->name), buf_ptr(field->name)); return; } diff --git a/src/analyze.hpp b/src/analyze.hpp index 439d6c7eeb..90600da790 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -84,9 +84,7 @@ void complete_enum(CodeGen *g, TypeTableEntry *enum_type); bool ir_get_var_is_comptime(VariableTableEntry *var); bool const_values_equal(ConstExprValue *a, ConstExprValue *b); void eval_min_max_value(CodeGen *g, TypeTableEntry *type_entry, ConstExprValue *const_val, bool is_max); -void eval_min_max_value_int(CodeGen *g, TypeTableEntry *int_type, BigNum *bignum, bool is_max); -int64_t min_signed_val(TypeTableEntry *type_entry); -uint64_t max_unsigned_val(TypeTableEntry *type_entry); +void eval_min_max_value_int(CodeGen *g, TypeTableEntry *int_type, BigInt *bigint, bool is_max); void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val); void define_local_param_variables(CodeGen *g, FnTableEntry *fn_table_entry, VariableTableEntry **arg_vars); diff --git a/src/ast_render.cpp b/src/ast_render.cpp index 3def04801b..0b6e6c149b 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -182,8 +182,10 @@ static const char *node_type_str(NodeType node_type) { return "ErrorValueDecl"; case NodeTypeTestDecl: return "TestDecl"; - case NodeTypeNumberLiteral: - return "NumberLiteral"; + case NodeTypeIntLiteral: + return "IntLiteral"; + case NodeTypeFloatLiteral: + return "FloatLiteral"; case NodeTypeStringLiteral: return "StringLiteral"; case NodeTypeCharLiteral: @@ -536,17 +538,20 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { render_node_ungrouped(ar, node->data.bin_op_expr.op2); if (!grouped) fprintf(ar->f, ")"); break; - case NodeTypeNumberLiteral: - switch (node->data.number_literal.bignum->kind) { - case BigNumKindInt: - { - const char *negative_str = node->data.number_literal.bignum->is_negative ? "-" : ""; - fprintf(ar->f, "%s%" ZIG_PRI_llu, negative_str, node->data.number_literal.bignum->data.x_uint); - } - break; - case BigNumKindFloat: - fprintf(ar->f, "%f", node->data.number_literal.bignum->data.x_float); - break; + case NodeTypeFloatLiteral: + { + Buf rendered_buf = BUF_INIT; + buf_resize(&rendered_buf, 0); + bigfloat_write_buf(&rendered_buf, node->data.float_literal.bigfloat); + fprintf(ar->f, "%s", buf_ptr(&rendered_buf)); + } + break; + case NodeTypeIntLiteral: + { + Buf rendered_buf = BUF_INIT; + buf_resize(&rendered_buf, 0); + bigint_write_buf(&rendered_buf, node->data.int_literal.bigint, 10); + fprintf(ar->f, "%s", buf_ptr(&rendered_buf)); } break; case NodeTypeStringLiteral: diff --git a/src/bigfloat.cpp b/src/bigfloat.cpp new file mode 100644 index 0000000000..3986efc19d --- /dev/null +++ b/src/bigfloat.cpp @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2017 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#include "bigfloat.hpp" +#include "bigint.hpp" +#include "buffer.hpp" +#include +#include + +void bigfloat_init_float(BigFloat *dest, long double x) { + dest->value = x; +} + +void bigfloat_init_bigfloat(BigFloat *dest, const BigFloat *x) { + dest->value = x->value; +} + +void bigfloat_init_bigint(BigFloat *dest, const BigInt *op) { + dest->value = 0.0; + if (op->digit_count == 0) + return; + + long double base = (long double)UINT64_MAX; + const uint64_t *digits = bigint_ptr(op); + + for (size_t i = op->digit_count - 1;;) { + uint64_t digit = digits[i]; + dest->value *= base; + dest->value += (long double)digit; + + if (i == 0) { + if (op->is_negative) { + dest->value = -dest->value; + } + return; + } + i -= 1; + } +} + +int bigfloat_init_buf_base10(BigFloat *dest, const uint8_t *buf_ptr, size_t buf_len) { + char *str_begin = (char *)buf_ptr; + char *str_end; + errno = 0; + dest->value = strtold(str_begin, &str_end); + if (errno) { + return ErrorOverflow; + } + assert(str_end <= ((char*)buf_ptr) + buf_len); + return 0; +} + +void bigfloat_add(BigFloat *dest, const BigFloat *op1, const BigFloat *op2) { + dest->value = op1->value + op2->value; +} + +void bigfloat_negate(BigFloat *dest, const BigFloat *op) { + dest->value = -op->value; +} + +void bigfloat_sub(BigFloat *dest, const BigFloat *op1, const BigFloat *op2) { + dest->value = op1->value - op2->value; +} + +void bigfloat_mul(BigFloat *dest, const BigFloat *op1, const BigFloat *op2) { + dest->value = op1->value * op2->value; +} + +void bigfloat_div(BigFloat *dest, const BigFloat *op1, const BigFloat *op2) { + dest->value = op1->value / op2->value; +} + +void bigfloat_div_trunc(BigFloat *dest, const BigFloat *op1, const BigFloat *op2) { + dest->value = op1->value / op2->value; + if (dest->value >= 0.0) { + dest->value = floorl(dest->value); + } else { + dest->value = ceill(dest->value); + } +} + +void bigfloat_div_floor(BigFloat *dest, const BigFloat *op1, const BigFloat *op2) { + dest->value = floorl(op1->value / op2->value); +} + +void bigfloat_rem(BigFloat *dest, const BigFloat *op1, const BigFloat *op2) { + dest->value = fmodl(op1->value, op2->value); +} + +void bigfloat_mod(BigFloat *dest, const BigFloat *op1, const BigFloat *op2) { + dest->value = fmodl(fmodl(op1->value, op2->value) + op2->value, op2->value); +} + +void bigfloat_write_buf(Buf *buf, const BigFloat *op) { + buf_appendf(buf, "%Lf", op->value); +} + +Cmp bigfloat_cmp(const BigFloat *op1, const BigFloat *op2) { + if (op1->value > op2->value) { + return CmpGT; + } else if (op1->value < op2->value) { + return CmpLT; + } else { + return CmpEQ; + } +} + +// TODO this is wrong when compiler running on big endian systems. caught by tests +void bigfloat_write_ieee597(const BigFloat *op, uint8_t *buf, size_t bit_count, bool is_big_endian) { + if (bit_count == 32) { + float f32 = op->value; + memcpy(buf, &f32, 4); + } else if (bit_count == 64) { + double f64 = op->value; + memcpy(buf, &f64, 8); + } else { + zig_unreachable(); + } +} + +// TODO this is wrong when compiler running on big endian systems. caught by tests +void bigfloat_read_ieee597(BigFloat *dest, const uint8_t *buf, size_t bit_count, bool is_big_endian) { + if (bit_count == 32) { + float f32; + memcpy(&f32, buf, 4); + dest->value = f32; + } else if (bit_count == 64) { + double f64; + memcpy(&f64, buf, 8); + dest->value = f64; + } else { + zig_unreachable(); + } +} + +double bigfloat_to_double(const BigFloat *bigfloat) { + return bigfloat->value; +} + +Cmp bigfloat_cmp_zero(const BigFloat *bigfloat) { + if (bigfloat->value < 0.0) { + return CmpLT; + } else if (bigfloat->value > 0.0) { + return CmpGT; + } else { + return CmpEQ; + } +} diff --git a/src/bigfloat.hpp b/src/bigfloat.hpp new file mode 100644 index 0000000000..2f32f37110 --- /dev/null +++ b/src/bigfloat.hpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2017 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#ifndef ZIG_BIGFLOAT_HPP +#define ZIG_BIGFLOAT_HPP + +#include "bigint.hpp" +#include "error.hpp" +#include +#include + +struct BigFloat { + long double value; +}; + +struct Buf; + +void bigfloat_init_float(BigFloat *dest, long double x); +void bigfloat_init_bigfloat(BigFloat *dest, const BigFloat *x); +void bigfloat_init_bigint(BigFloat *dest, const BigInt *op); +int bigfloat_init_buf_base10(BigFloat *dest, const uint8_t *buf_ptr, size_t buf_len); + +double bigfloat_to_double(const BigFloat *bigfloat); + +void bigfloat_add(BigFloat *dest, const BigFloat *op1, const BigFloat *op2); +void bigfloat_negate(BigFloat *dest, const BigFloat *op); +void bigfloat_sub(BigFloat *dest, const BigFloat *op1, const BigFloat *op2); +void bigfloat_mul(BigFloat *dest, const BigFloat *op1, const BigFloat *op2); +void bigfloat_div(BigFloat *dest, const BigFloat *op1, const BigFloat *op2); +void bigfloat_div_trunc(BigFloat *dest, const BigFloat *op1, const BigFloat *op2); +void bigfloat_div_floor(BigFloat *dest, const BigFloat *op1, const BigFloat *op2); +void bigfloat_rem(BigFloat *dest, const BigFloat *op1, const BigFloat *op2); +void bigfloat_mod(BigFloat *dest, const BigFloat *op1, const BigFloat *op2); +void bigfloat_write_buf(Buf *buf, const BigFloat *op); +Cmp bigfloat_cmp(const BigFloat *op1, const BigFloat *op2); +void bigfloat_write_ieee597(const BigFloat *op, uint8_t *buf, size_t bit_count, bool is_big_endian); +void bigfloat_read_ieee597(BigFloat *dest, const uint8_t *buf, size_t bit_count, bool is_big_endian); + + +// convenience functions +Cmp bigfloat_cmp_zero(const BigFloat *bigfloat); + +#endif diff --git a/src/bigint.cpp b/src/bigint.cpp new file mode 100644 index 0000000000..3dc0f7d08a --- /dev/null +++ b/src/bigint.cpp @@ -0,0 +1,1088 @@ +/* + * Copyright (c) 2017 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#include "bigfloat.hpp" +#include "bigint.hpp" +#include "buffer.hpp" +#include "list.hpp" +#include "os.hpp" + +static void bigint_normalize(BigInt *dest) { + const uint64_t *digits = bigint_ptr(dest); + + size_t last_nonzero_digit = SIZE_MAX; + for (size_t i = 0; i < dest->digit_count; i += 1) { + uint64_t digit = digits[i]; + if (digit != 0) { + last_nonzero_digit = i; + } + } + if (last_nonzero_digit == SIZE_MAX) { + dest->is_negative = false; + dest->digit_count = 0; + } else { + dest->digit_count = last_nonzero_digit + 1; + if (last_nonzero_digit == 0) { + dest->data.digit = digits[0]; + } + } +} + +static uint8_t digit_to_char(uint8_t digit, bool uppercase) { + if (digit <= 9) { + return digit + '0'; + } else if (digit <= 35) { + return digit + (uppercase ? 'A' : 'a'); + } else { + zig_unreachable(); + } +} + +size_t bigint_bits_needed(const BigInt *op) { + size_t full_bits = op->digit_count * 64; + size_t leading_zero_count = bigint_clz(op, full_bits); + size_t bits_needed = full_bits - leading_zero_count; + return bits_needed + op->is_negative; +} + +static void to_twos_complement(BigInt *dest, const BigInt *op, size_t bit_count) { + if (bit_count == 0 || op->digit_count == 0) { + bigint_init_unsigned(dest, 0); + return; + } + if (op->is_negative) { + BigInt negated = {0}; + bigint_negate(&negated, op); + + BigInt inverted = {0}; + bigint_not(&inverted, &negated, bit_count, false); + + BigInt one = {0}; + bigint_init_unsigned(&one, 1); + + bigint_add(dest, &inverted, &one); + return; + } + + dest->is_negative = false; + const uint64_t *op_digits = bigint_ptr(op); + if (op->digit_count == 1) { + dest->data.digit = op_digits[0]; + if (bit_count < 64) { + dest->data.digit &= (1ULL << bit_count) - 1; + } + dest->digit_count = 1; + bigint_normalize(dest); + return; + } + size_t digits_to_copy = bit_count / 64; + size_t leftover_bits = bit_count % 64; + dest->digit_count = digits_to_copy + ((leftover_bits == 0) ? 0 : 1); + dest->data.digits = allocate_nonzero(dest->digit_count); + for (size_t i = 0; i < digits_to_copy; i += 1) { + uint64_t digit = (i < op->digit_count) ? op_digits[i] : 0; + dest->data.digits[i] = digit; + } + if (leftover_bits != 0) { + uint64_t digit = (digits_to_copy < op->digit_count) ? op_digits[digits_to_copy] : 0; + dest->data.digits[digits_to_copy] = digit & ((1ULL << leftover_bits) - 1); + } + bigint_normalize(dest); +} + +static bool bit_at_index(const BigInt *bi, size_t index) { + size_t digit_index = bi->digit_count - (index / 64) - 1; + size_t digit_bit_index = index % 64; + const uint64_t *digits = bigint_ptr(bi); + uint64_t digit = digits[digit_index]; + return ((digit >> digit_bit_index) & 0x1) == 0x1; +} + +static void from_twos_complement(BigInt *dest, const BigInt *src, size_t bit_count, bool is_signed) { + assert(!src->is_negative); + + if (bit_count == 0 || src->digit_count == 0) { + bigint_init_unsigned(dest, 0); + return; + } + + if (is_signed && bit_at_index(src, bit_count - 1)) { + BigInt negative_one = {0}; + bigint_init_signed(&negative_one, -1); + + BigInt minus_one = {0}; + bigint_add(&minus_one, src, &negative_one); + + BigInt inverted = {0}; + bigint_not(&inverted, &minus_one, bit_count, false); + + bigint_negate(dest, &inverted); + return; + + } + + bigint_init_bigint(dest, src); +} + +void bigint_init_unsigned(BigInt *dest, uint64_t x) { + if (x == 0) { + dest->digit_count = 0; + dest->is_negative = false; + return; + } + dest->digit_count = 1; + dest->data.digit = x; + dest->is_negative = false; +} + +void bigint_init_signed(BigInt *dest, int64_t x) { + if (x >= 0) { + return bigint_init_unsigned(dest, x); + } + dest->is_negative = true; + dest->digit_count = 1; + dest->data.digit = ((uint64_t)(-(x + 1))) + 1; +} + +void bigint_init_bigint(BigInt *dest, const BigInt *src) { + if (src->digit_count == 0) { + return bigint_init_unsigned(dest, 0); + } else if (src->digit_count == 1) { + dest->digit_count = 1; + dest->data.digit = src->data.digit; + dest->is_negative = src->is_negative; + return; + } + dest->is_negative = src->is_negative; + dest->digit_count = src->digit_count; + dest->data.digits = allocate_nonzero(dest->digit_count); + memcpy(dest->data.digits, src->data.digits, sizeof(uint64_t) * dest->digit_count); +} + +void bigint_init_bigfloat(BigInt *dest, const BigFloat *op) { + if (op->value >= 0) { + bigint_init_unsigned(dest, op->value); + } else { + bigint_init_unsigned(dest, -op->value); + dest->is_negative = true; + } +} + +bool bigint_fits_in_bits(const BigInt *bn, size_t bit_count, bool is_signed) { + assert(bn->digit_count != 1 || bn->data.digit != 0); + if (bit_count == 0) { + return bigint_cmp_zero(bn) == CmpEQ; + } + if (bn->digit_count == 0) { + return true; + } + + if (!is_signed) { + size_t full_bits = bn->digit_count * 64; + size_t leading_zero_count = bigint_clz(bn, full_bits); + return bit_count >= full_bits - leading_zero_count; + } + + BigInt one = {0}; + bigint_init_unsigned(&one, 1); + + BigInt shl_amt = {0}; + bigint_init_unsigned(&shl_amt, bit_count - 1); + + BigInt max_value_plus_one = {0}; + bigint_shl(&max_value_plus_one, &one, &shl_amt); + + BigInt max_value = {0}; + bigint_sub(&max_value, &max_value_plus_one, &one); + + BigInt min_value = {0}; + bigint_negate(&min_value, &max_value_plus_one); + + Cmp min_cmp = bigint_cmp(bn, &min_value); + Cmp max_cmp = bigint_cmp(bn, &max_value); + + return (min_cmp == CmpGT || min_cmp == CmpEQ) && (max_cmp == CmpLT || max_cmp == CmpEQ); +} + +void bigint_write_twos_complement(const BigInt *big_int, uint8_t *buf, size_t bit_count, bool is_big_endian) { + if (bit_count == 0) + return; + + BigInt twos_comp = {0}; + to_twos_complement(&twos_comp, big_int, bit_count); + + const uint64_t *twos_comp_digits = bigint_ptr(&twos_comp); + + size_t bits_in_last_digit = bit_count % 64; + size_t bytes_in_last_digit = (bits_in_last_digit + 7) / 8; + size_t unwritten_byte_count = 8 - bytes_in_last_digit; + + if (is_big_endian) { + size_t last_digit_index = (bit_count - 1) / 64; + size_t digit_index = last_digit_index; + size_t buf_index = 0; + for (;;) { + uint64_t x = (digit_index < twos_comp.digit_count) ? twos_comp_digits[digit_index] : 0; + + for (size_t byte_index = 7;;) { + uint8_t byte = x & 0xff; + if (digit_index == last_digit_index) { + buf[buf_index + byte_index - unwritten_byte_count] = byte; + if (byte_index == unwritten_byte_count) break; + } else { + buf[buf_index + byte_index] = byte; + } + + if (byte_index == 0) break; + byte_index -= 1; + x >>= 8; + } + + if (digit_index == 0) break; + digit_index -= 1; + if (digit_index == last_digit_index) { + buf_index += bytes_in_last_digit; + } else { + buf_index += 8; + } + } + } else { + size_t digit_count = (bit_count + 63) / 64; + size_t buf_index = 0; + for (size_t digit_index = 0; digit_index < digit_count; digit_index += 1) { + uint64_t x = (digit_index < twos_comp.digit_count) ? twos_comp_digits[digit_index] : 0; + + for (size_t byte_index = 0; byte_index < 8; byte_index += 1) { + uint8_t byte = x & 0xff; + buf[buf_index] = byte; + buf_index += 1; + if (buf_index >= unwritten_byte_count) { + break; + } + x >>= 8; + } + } + } +} + + +void bigint_read_twos_complement(BigInt *dest, const uint8_t *buf, size_t bit_count, bool is_big_endian, + bool is_signed) +{ + if (bit_count == 0) { + bigint_init_unsigned(dest, 0); + return; + } + + dest->digit_count = (bit_count + 63) / 64; + uint64_t *digits; + if (dest->digit_count == 1) { + digits = &dest->data.digit; + } else { + digits = allocate_nonzero(dest->digit_count); + dest->data.digits = digits; + } + + size_t bits_in_last_digit = bit_count % 64; + if (bits_in_last_digit == 0) { + bits_in_last_digit = 64; + } + size_t bytes_in_last_digit = (bits_in_last_digit + 7) / 8; + size_t unread_byte_count = 8 - bytes_in_last_digit; + + if (is_big_endian) { + size_t buf_index = 0; + uint64_t digit = 0; + for (size_t byte_index = unread_byte_count; byte_index < 8; byte_index += 1) { + uint8_t byte = buf[buf_index]; + buf_index += 1; + digit <<= 8; + digit |= byte; + } + digits[dest->digit_count - 1] = digit; + for (size_t digit_index = 1; digit_index < dest->digit_count; digit_index += 1) { + digit = 0; + for (size_t byte_index = 0; byte_index < 8; byte_index += 1) { + uint8_t byte = buf[buf_index]; + buf_index += 1; + digit <<= 8; + digit |= byte; + } + digits[dest->digit_count - 1 - digit_index] = digit; + } + } else { + size_t buf_index = 0; + for (size_t digit_index = 0; digit_index < dest->digit_count; digit_index += 1) { + uint64_t digit = 0; + size_t end_byte_index = (digit_index == dest->digit_count - 1) ? bytes_in_last_digit : 8; + for (size_t byte_index = 0; byte_index < end_byte_index; byte_index += 1) { + uint64_t byte = buf[buf_index]; + buf_index += 1; + + digit |= byte << (8 * byte_index); + } + digits[digit_index] = digit; + } + } + + if (is_signed) { + bigint_normalize(dest); + BigInt tmp = {0}; + bigint_init_bigint(&tmp, dest); + from_twos_complement(dest, &tmp, bit_count, true); + } else { + dest->is_negative = false; + bigint_normalize(dest); + } +} + +static bool add_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result) { + return __builtin_uaddll_overflow((unsigned long long)op1, (unsigned long long)op2, + (unsigned long long *)result); +} + +static bool sub_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result) { + return __builtin_usubll_overflow((unsigned long long)op1, (unsigned long long)op2, + (unsigned long long *)result); +} + +static bool mul_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result) { + return __builtin_umulll_overflow((unsigned long long)op1, (unsigned long long)op2, + (unsigned long long *)result); +} + +void bigint_add(BigInt *dest, const BigInt *op1, const BigInt *op2) { + if (op1->digit_count == 0) { + return bigint_init_bigint(dest, op2); + } + if (op2->digit_count == 0) { + return bigint_init_bigint(dest, op1); + } + if (op1->is_negative == op2->is_negative) { + dest->is_negative = op1->is_negative; + + const uint64_t *op1_digits = bigint_ptr(op1); + const uint64_t *op2_digits = bigint_ptr(op2); + uint64_t overflow = add_u64_overflow(op1_digits[0], op2_digits[0], &dest->data.digit); + if (overflow == 0 && op1->digit_count == 1 && op2->digit_count == 1) { + dest->digit_count = 1; + bigint_normalize(dest); + return; + } + // TODO this code path is untested + size_t i = 1; + uint64_t first_digit = dest->data.digit; + dest->data.digits = allocate_nonzero(max(op1->digit_count, op2->digit_count) + 1); + dest->data.digits[0] = first_digit; + + for (;;) { + bool found_digit = false; + uint64_t x = overflow; + overflow = 0; + + if (i < op1->digit_count) { + found_digit = true; + uint64_t digit = op1_digits[i]; + overflow += add_u64_overflow(x, digit, &x); + } + + if (i < op2->digit_count) { + found_digit = true; + uint64_t digit = op2_digits[i]; + overflow += add_u64_overflow(x, digit, &x); + } + + dest->data.digits[i] = x; + x += 1; + + if (!found_digit) { + break; + } + } + if (overflow > 0) { + dest->data.digits[i] = overflow; + } + bigint_normalize(dest); + return; + } + const BigInt *op_pos; + const BigInt *op_neg; + if (op1->is_negative) { + op_neg = op1; + op_pos = op2; + } else { + op_pos = op1; + op_neg = op2; + } + + BigInt op_neg_abs = {0}; + bigint_negate(&op_neg_abs, op_neg); + const BigInt *bigger_op; + const BigInt *smaller_op; + switch (bigint_cmp(op_pos, &op_neg_abs)) { + case CmpEQ: + bigint_init_unsigned(dest, 0); + return; + case CmpLT: + bigger_op = &op_neg_abs; + smaller_op = op_pos; + dest->is_negative = true; + break; + case CmpGT: + bigger_op = op_pos; + smaller_op = &op_neg_abs; + dest->is_negative = false; + break; + } + const uint64_t *bigger_op_digits = bigint_ptr(bigger_op); + const uint64_t *smaller_op_digits = bigint_ptr(smaller_op); + uint64_t overflow = sub_u64_overflow(bigger_op_digits[0], smaller_op_digits[0], &dest->data.digit); + if (overflow == 0 && bigger_op->digit_count == 1 && smaller_op->digit_count == 1) { + dest->digit_count = 1; + bigint_normalize(dest); + return; + } + uint64_t first_digit = dest->data.digit; + dest->data.digits = allocate_nonzero(bigger_op->digit_count); + dest->data.digits[0] = first_digit; + size_t i = 1; + + for (;;) { + bool found_digit = false; + uint64_t x = bigger_op_digits[i]; + uint64_t prev_overflow = overflow; + overflow = 0; + + if (i < smaller_op->digit_count) { + found_digit = true; + uint64_t digit = smaller_op_digits[i]; + overflow += sub_u64_overflow(x, digit, &x); + } + if (sub_u64_overflow(x, prev_overflow, &x)) { + found_digit = true; + overflow += 1; + } + dest->data.digits[i] = x; + i += 1; + + if (!found_digit) + break; + } + assert(overflow == 0); + dest->digit_count = i; + bigint_normalize(dest); +} + +void bigint_add_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed) { + BigInt unwrapped = {0}; + bigint_add(&unwrapped, op1, op2); + bigint_truncate(dest, &unwrapped, bit_count, is_signed); +} + +void bigint_sub(BigInt *dest, const BigInt *op1, const BigInt *op2) { + BigInt op2_negated = {0}; + bigint_negate(&op2_negated, op2); + return bigint_add(dest, op1, &op2_negated); +} + +void bigint_sub_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed) { + BigInt op2_negated = {0}; + bigint_negate(&op2_negated, op2); + return bigint_add_wrap(dest, op1, &op2_negated, bit_count, is_signed); +} + +static void mul_overflow(uint64_t x, uint64_t y, uint64_t *result, uint64_t *carry) { + if (!mul_u64_overflow(x, y, result)) { + *carry = 0; + return; + } + zig_panic("TODO bigint_mul with big numbers"); + + //unsigned __int128 big_x = x; + //unsigned __int128 big_y = y; + //unsigned __int128 big_result = big_x * big_y; + //*carry = big_result >> 64; +} + +void bigint_mul(BigInt *dest, const BigInt *op1, const BigInt *op2) { + if (op1->digit_count == 0 || op2->digit_count == 0) { + return bigint_init_unsigned(dest, 0); + } + const uint64_t *op1_digits = bigint_ptr(op1); + const uint64_t *op2_digits = bigint_ptr(op2); + + uint64_t carry; + mul_overflow(op1_digits[0], op2_digits[0], &dest->data.digit, &carry); + if (carry == 0 && op1->digit_count == 1 && op2->digit_count == 1) { + dest->is_negative = (op1->is_negative != op2->is_negative); + dest->digit_count = 1; + bigint_normalize(dest); + return; + } + zig_panic("TODO bigint_mul with big numbers"); +} + +void bigint_mul_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed) { + BigInt unwrapped = {0}; + bigint_mul(&unwrapped, op1, op2); + bigint_truncate(dest, &unwrapped, bit_count, is_signed); +} + +void bigint_div_trunc(BigInt *dest, const BigInt *op1, const BigInt *op2) { + assert(op2->digit_count != 0); // division by zero + if (op1->digit_count == 0) { + bigint_init_unsigned(dest, 0); + return; + } + if (op1->digit_count != 1 || op2->digit_count != 1) { + zig_panic("TODO bigint div_trunc with >1 digits"); + } + const uint64_t *op1_digits = bigint_ptr(op1); + const uint64_t *op2_digits = bigint_ptr(op2); + dest->data.digit = op1_digits[0] / op2_digits[0]; + dest->digit_count = 1; + dest->is_negative = op1->is_negative != op2->is_negative; + bigint_normalize(dest); +} + +void bigint_div_floor(BigInt *dest, const BigInt *op1, const BigInt *op2) { + if (op1->is_negative != op2->is_negative) { + bigint_div_trunc(dest, op1, op2); + BigInt mult_again = {0}; + bigint_mul(&mult_again, dest, op2); + mult_again.is_negative = op1->is_negative; + if (bigint_cmp(&mult_again, op1) != CmpEQ) { + BigInt tmp = {0}; + bigint_init_bigint(&tmp, dest); + BigInt neg_one = {0}; + bigint_init_signed(&neg_one, -1); + bigint_add(dest, &tmp, &neg_one); + } + bigint_normalize(dest); + } else { + bigint_div_trunc(dest, op1, op2); + } +} + +void bigint_rem(BigInt *dest, const BigInt *op1, const BigInt *op2) { + assert(op2->digit_count != 0); // division by zero + if (op1->digit_count == 0) { + bigint_init_unsigned(dest, 0); + return; + } + const uint64_t *op1_digits = bigint_ptr(op1); + const uint64_t *op2_digits = bigint_ptr(op2); + if (op2->digit_count == 2 && op2_digits[0] == 0 && op2_digits[1] == 1) { + // special case this divisor + bigint_init_unsigned(dest, op1_digits[0]); + dest->is_negative = op1->is_negative; + bigint_normalize(dest); + return; + } + if (op1->digit_count != 1 || op2->digit_count != 1) { + zig_panic("TODO bigint rem with >1 digits"); + } + dest->data.digit = op1_digits[0] % op2_digits[0]; + dest->digit_count = 1; + dest->is_negative = op1->is_negative; + bigint_normalize(dest); +} + +void bigint_mod(BigInt *dest, const BigInt *op1, const BigInt *op2) { + if (op1->is_negative) { + BigInt first_rem; + bigint_rem(&first_rem, op1, op2); + first_rem.is_negative = !op2->is_negative; + BigInt op2_minus_rem; + bigint_add(&op2_minus_rem, op2, &first_rem); + bigint_rem(dest, &op2_minus_rem, op2); + dest->is_negative = false; + } else { + bigint_rem(dest, op1, op2); + dest->is_negative = false; + } +} + +void bigint_or(BigInt *dest, const BigInt *op1, const BigInt *op2) { + if (op1->digit_count == 0) { + return bigint_init_bigint(dest, op2); + } + if (op2->digit_count == 0) { + return bigint_init_bigint(dest, op1); + } + if (op1->is_negative || op2->is_negative) { + // TODO this code path is untested + size_t big_bit_count = max(bigint_bits_needed(op1), bigint_bits_needed(op2)); + + BigInt twos_comp_op1 = {0}; + to_twos_complement(&twos_comp_op1, op1, big_bit_count); + + BigInt twos_comp_op2 = {0}; + to_twos_complement(&twos_comp_op2, op2, big_bit_count); + + BigInt twos_comp_dest = {0}; + bigint_or(&twos_comp_dest, &twos_comp_op1, &twos_comp_op2); + + from_twos_complement(dest, &twos_comp_dest, big_bit_count, true); + } else { + dest->is_negative = false; + const uint64_t *op1_digits = bigint_ptr(op1); + const uint64_t *op2_digits = bigint_ptr(op2); + if (op1->digit_count == 1 && op2->digit_count == 1) { + dest->digit_count = 1; + dest->data.digit = op1_digits[0] | op2_digits[0]; + bigint_normalize(dest); + return; + } + // TODO this code path is untested + uint64_t first_digit = dest->data.digit; + dest->digit_count = max(op1->digit_count, op2->digit_count); + dest->data.digits = allocate_nonzero(dest->digit_count); + dest->data.digits[0] = first_digit; + size_t i = 1; + for (; i < dest->digit_count; i += 1) { + uint64_t digit = 0; + if (i < op1->digit_count) { + digit |= op1_digits[i]; + } + if (i < op2->digit_count) { + digit |= op2_digits[i]; + } + dest->data.digits[i] = digit; + } + bigint_normalize(dest); + } +} + +void bigint_and(BigInt *dest, const BigInt *op1, const BigInt *op2) { + if (op1->digit_count == 0 || op2->digit_count == 0) { + return bigint_init_unsigned(dest, 0); + } + if (op1->is_negative || op2->is_negative) { + // TODO this code path is untested + size_t big_bit_count = max(bigint_bits_needed(op1), bigint_bits_needed(op2)); + + BigInt twos_comp_op1 = {0}; + to_twos_complement(&twos_comp_op1, op1, big_bit_count); + + BigInt twos_comp_op2 = {0}; + to_twos_complement(&twos_comp_op2, op2, big_bit_count); + + BigInt twos_comp_dest = {0}; + bigint_and(&twos_comp_dest, &twos_comp_op1, &twos_comp_op2); + + from_twos_complement(dest, &twos_comp_dest, big_bit_count, true); + } else { + dest->is_negative = false; + const uint64_t *op1_digits = bigint_ptr(op1); + const uint64_t *op2_digits = bigint_ptr(op2); + if (op1->digit_count == 1 && op2->digit_count == 1) { + dest->digit_count = 1; + dest->data.digit = op1_digits[0] & op2_digits[0]; + bigint_normalize(dest); + return; + } + // TODO this code path is untested + uint64_t first_digit = dest->data.digit; + dest->digit_count = max(op1->digit_count, op2->digit_count); + dest->data.digits = allocate_nonzero(dest->digit_count); + dest->data.digits[0] = first_digit; + size_t i = 1; + for (; i < op1->digit_count && i < op2->digit_count; i += 1) { + dest->data.digits[i] = op1_digits[i] & op2_digits[i]; + } + for (; i < dest->digit_count; i += 1) { + dest->data.digits[i] = 0; + } + bigint_normalize(dest); + } +} + +void bigint_xor(BigInt *dest, const BigInt *op1, const BigInt *op2) { + if (op1->is_negative || op2->is_negative) { + // TODO this code path is untested + size_t big_bit_count = max(bigint_bits_needed(op1), bigint_bits_needed(op2)); + + BigInt twos_comp_op1 = {0}; + to_twos_complement(&twos_comp_op1, op1, big_bit_count); + + BigInt twos_comp_op2 = {0}; + to_twos_complement(&twos_comp_op2, op2, big_bit_count); + + BigInt twos_comp_dest = {0}; + bigint_xor(&twos_comp_dest, &twos_comp_op1, &twos_comp_op2); + + from_twos_complement(dest, &twos_comp_dest, big_bit_count, true); + } else { + dest->is_negative = false; + const uint64_t *op1_digits = bigint_ptr(op1); + const uint64_t *op2_digits = bigint_ptr(op2); + if (op1->digit_count == 1 && op2->digit_count == 1) { + dest->digit_count = 1; + dest->data.digit = op1_digits[0] ^ op2_digits[0]; + bigint_normalize(dest); + return; + } + // TODO this code path is untested + uint64_t first_digit = dest->data.digit; + dest->digit_count = max(op1->digit_count, op2->digit_count); + dest->data.digits = allocate_nonzero(dest->digit_count); + dest->data.digits[0] = first_digit; + size_t i = 1; + for (; i < op1->digit_count && i < op2->digit_count; i += 1) { + dest->data.digits[i] = op1_digits[i] ^ op2_digits[i]; + } + for (; i < dest->digit_count; i += 1) { + if (i < op1->digit_count) { + dest->data.digits[i] = op1_digits[i]; + } + if (i < op2->digit_count) { + dest->data.digits[i] = op2_digits[i]; + } + } + bigint_normalize(dest); + } +} + +void bigint_shl(BigInt *dest, const BigInt *op1, const BigInt *op2) { + assert(!op2->is_negative); + + if (op2->digit_count == 0) { + bigint_init_bigint(dest, op1); + return; + } + + if (op1->digit_count == 0) { + bigint_init_unsigned(dest, 0); + return; + } + + if (op2->digit_count != 1) { + zig_panic("TODO shift left by amount greater than 64 bit integer"); + } + + const uint64_t *op1_digits = bigint_ptr(op1); + uint64_t shift_amt = bigint_as_unsigned(op2); + + if (op1->digit_count == 1) { + dest->data.digit = op1_digits[0] << shift_amt; + if (dest->data.digit > op1_digits[0]) { + dest->digit_count = 1; + dest->is_negative = op1->is_negative; + return; + } + } + + uint64_t digit_shift_count = shift_amt / 64; + uint64_t leftover_shift_count = shift_amt % 64; + + dest->data.digits = allocate(op1->digit_count + digit_shift_count + 1); + dest->digit_count = digit_shift_count; + uint64_t carry = 0; + for (size_t i = 0; i < op1->digit_count; i += 1) { + uint64_t digit = op1_digits[i]; + dest->data.digits[dest->digit_count] = carry | (digit << leftover_shift_count); + dest->digit_count += 1; + if (leftover_shift_count > 0) { + carry = digit >> (64 - leftover_shift_count); + } else { + carry = 0; + } + } + dest->data.digits[dest->digit_count] = carry; + dest->digit_count += 1; + dest->is_negative = op1->is_negative; + bigint_normalize(dest); +} + +void bigint_shl_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed) { + BigInt unwrapped = {0}; + bigint_shl(&unwrapped, op1, op2); + bigint_truncate(dest, &unwrapped, bit_count, is_signed); +} + +void bigint_shr(BigInt *dest, const BigInt *op1, const BigInt *op2) { + assert(!op2->is_negative); + + if (op1->digit_count == 0) { + return bigint_init_unsigned(dest, 0); + } + + if (op2->digit_count == 0) { + return bigint_init_bigint(dest, op1); + } + + if (op2->digit_count != 1) { + zig_panic("TODO shift right by amount greater than 64 bit integer"); + } + + const uint64_t *op1_digits = bigint_ptr(op1); + uint64_t shift_amt = bigint_as_unsigned(op2); + + if (op1->digit_count == 1) { + dest->data.digit = op1_digits[0] >> shift_amt; + dest->digit_count = 1; + dest->is_negative = op1->is_negative; + bigint_normalize(dest); + return; + } + + // TODO this code path is untested + size_t digit_shift_count = shift_amt / 64; + size_t leftover_shift_count = shift_amt % 64; + + if (digit_shift_count >= op1->digit_count) { + return bigint_init_unsigned(dest, 0); + } + + dest->digit_count = op1->digit_count - digit_shift_count; + dest->data.digits = allocate(dest->digit_count); + uint64_t carry = 0; + for (size_t op_digit_index = op1->digit_count - 1;;) { + uint64_t digit = op1_digits[op_digit_index]; + size_t dest_digit_index = op_digit_index - digit_shift_count; + dest->data.digits[dest_digit_index] = carry | (digit >> leftover_shift_count); + carry = (0xffffffffffffffffULL << leftover_shift_count) & digit; + + if (dest_digit_index == 0) { break; } + op_digit_index -= 1; + } + dest->is_negative = op1->is_negative; + bigint_normalize(dest); +} + +void bigint_negate(BigInt *dest, const BigInt *op) { + bigint_init_bigint(dest, op); + dest->is_negative = !dest->is_negative; + bigint_normalize(dest); +} + +void bigint_negate_wrap(BigInt *dest, const BigInt *op, size_t bit_count) { + BigInt zero; + bigint_init_unsigned(&zero, 0); + bigint_sub_wrap(dest, &zero, op, bit_count, true); +} + +void bigint_not(BigInt *dest, const BigInt *op, size_t bit_count, bool is_signed) { + if (bit_count == 0) { + bigint_init_unsigned(dest, 0); + return; + } + + if (is_signed) { + BigInt twos_comp = {0}; + to_twos_complement(&twos_comp, op, bit_count); + + BigInt inverted = {0}; + bigint_not(&inverted, &twos_comp, bit_count, false); + + from_twos_complement(dest, &inverted, bit_count, true); + return; + } + + assert(!op->is_negative); + + dest->is_negative = false; + const uint64_t *op_digits = bigint_ptr(op); + if (bit_count <= 64) { + dest->digit_count = 1; + if (op->digit_count == 0) { + if (bit_count == 64) { + dest->data.digit = UINT64_MAX; + } else { + dest->data.digit = (1ULL << bit_count) - 1; + } + } else if (op->digit_count == 1) { + dest->data.digit = ~op_digits[0]; + if (bit_count != 64) { + uint64_t mask = (1ULL << bit_count) - 1; + dest->data.digit &= mask; + } + } + bigint_normalize(dest); + return; + } + // TODO this code path is untested + dest->digit_count = bit_count / 64; + assert(dest->digit_count >= op->digit_count); + dest->data.digits = allocate_nonzero(dest->digit_count); + size_t i = 0; + for (; i < op->digit_count; i += 1) { + dest->data.digits[i] = ~op_digits[i]; + } + for (; i < dest->digit_count; i += 1) { + dest->data.digits[i] = 0xffffffffffffffffULL; + } + size_t digit_index = dest->digit_count - (bit_count / 64) - 1; + size_t digit_bit_index = bit_count % 64; + if (digit_index < dest->digit_count) { + uint64_t mask = (1ULL << digit_bit_index) - 1; + dest->data.digits[digit_index] &= mask; + } + bigint_normalize(dest); +} + +void bigint_truncate(BigInt *dest, const BigInt *op, size_t bit_count, bool is_signed) { + BigInt twos_comp; + to_twos_complement(&twos_comp, op, bit_count); + from_twos_complement(dest, &twos_comp, bit_count, is_signed); +} + +Cmp bigint_cmp(const BigInt *op1, const BigInt *op2) { + if (op1->is_negative && !op2->is_negative) { + return CmpLT; + } else if (!op1->is_negative && op2->is_negative) { + return CmpGT; + } else if (op1->digit_count > op2->digit_count) { + return op1->is_negative ? CmpLT : CmpGT; + } else if (op2->digit_count > op1->digit_count) { + return op1->is_negative ? CmpGT : CmpLT; + } else if (op1->digit_count == 0) { + return CmpEQ; + } + const uint64_t *op1_digits = bigint_ptr(op1); + const uint64_t *op2_digits = bigint_ptr(op2); + for (size_t i = op1->digit_count - 1; ;) { + uint64_t op1_digit = op1_digits[i]; + uint64_t op2_digit = op2_digits[i]; + + if (op1_digit > op2_digit) { + return op1->is_negative ? CmpLT : CmpGT; + } + if (op1_digit < op2_digit) { + return op1->is_negative ? CmpGT : CmpLT; + } + + if (i == 0) { + return CmpEQ; + } + i -= 1; + } +} + +void bigint_write_buf(Buf *buf, const BigInt *op, uint64_t base) { + if (op->digit_count == 0) { + buf_append_char(buf, '0'); + return; + } + if (op->is_negative) { + buf_append_char(buf, '-'); + } + if (op->digit_count == 1 && base == 10) { + buf_appendf(buf, "%" ZIG_PRI_u64, op->data.digit); + return; + } + // TODO this code path is untested + size_t first_digit_index = buf_len(buf); + + BigInt digit_bi = {0}; + BigInt a1 = {0}; + BigInt a2 = {0}; + + BigInt *a = &a1; + BigInt *other_a = &a2; + bigint_init_bigint(a, op); + + BigInt base_bi = {0}; + bigint_init_unsigned(&base_bi, 10); + + for (;;) { + bigint_rem(&digit_bi, a, &base_bi); + uint8_t digit = bigint_as_unsigned(&digit_bi); + buf_append_char(buf, digit_to_char(digit, false)); + bigint_div_trunc(other_a, a, &base_bi); + { + BigInt *tmp = a; + a = other_a; + other_a = tmp; + } + if (bigint_cmp_zero(a) == CmpEQ) { + break; + } + } + + // reverse + for (size_t i = first_digit_index; i < buf_len(buf); i += 1) { + size_t other_i = buf_len(buf) + first_digit_index - i - 1; + uint8_t tmp = buf_ptr(buf)[i]; + buf_ptr(buf)[i] = buf_ptr(buf)[other_i]; + buf_ptr(buf)[other_i] = tmp; + } +} + +size_t bigint_ctz(const BigInt *bi, size_t bit_count) { + if (bit_count == 0) + return 0; + if (bi->digit_count == 0) + return bit_count; + + BigInt twos_comp = {0}; + to_twos_complement(&twos_comp, bi, bit_count); + + size_t count = 0; + for (size_t i = 0; i < bit_count; i += 1) { + if (bit_at_index(&twos_comp, i)) + return count; + count += 1; + } + return count; +} + +size_t bigint_clz(const BigInt *bi, size_t bit_count) { + if (bi->is_negative || bit_count == 0) + return 0; + if (bi->digit_count == 0) + return bit_count; + + size_t count = 0; + for (size_t i = bit_count - 1;;) { + if (bit_at_index(bi, i)) + return count; + count += 1; + + if (i == 0) break; + i -= 1; + } + return count; +} + +uint64_t bigint_as_unsigned(const BigInt *bigint) { + assert(!bigint->is_negative); + if (bigint->digit_count == 0) { + return 0; + } else if (bigint->digit_count == 1) { + return bigint->data.digit; + } else { + zig_unreachable(); + } +} + +int64_t bigint_as_signed(const BigInt *bigint) { + if (bigint->digit_count == 0) { + return 0; + } else if (bigint->digit_count == 1) { + if (bigint->is_negative) { + // TODO this code path is untested + if (bigint->data.digit <= 9223372036854775808ULL) { + return (-((int64_t)(bigint->data.digit - 1))) - 1; + } else { + zig_unreachable(); + } + } else { + return bigint->data.digit; + } + } else { + zig_unreachable(); + } +} + +Cmp bigint_cmp_zero(const BigInt *op) { + if (op->digit_count == 0) { + return CmpEQ; + } + return op->is_negative ? CmpLT : CmpGT; +} diff --git a/src/bigint.hpp b/src/bigint.hpp new file mode 100644 index 0000000000..3c29106365 --- /dev/null +++ b/src/bigint.hpp @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2017 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#ifndef ZIG_BIGINT_HPP +#define ZIG_BIGINT_HPP + +#include +#include + +struct BigInt { + size_t digit_count; + union { + uint64_t digit; + uint64_t *digits; // Least significant digit first + } data; + bool is_negative; +}; + +struct Buf; +struct BigFloat; + +enum Cmp { + CmpLT, + CmpGT, + CmpEQ, +}; + +void bigint_init_unsigned(BigInt *dest, uint64_t x); +void bigint_init_signed(BigInt *dest, int64_t x); +void bigint_init_bigint(BigInt *dest, const BigInt *src); +void bigint_init_bigfloat(BigInt *dest, const BigFloat *op); + +// panics if number won't fit +uint64_t bigint_as_unsigned(const BigInt *bigint); +int64_t bigint_as_signed(const BigInt *bigint); + +static inline const uint64_t *bigint_ptr(const BigInt *bigint) { + if (bigint->digit_count == 1) { + return &bigint->data.digit; + } else { + return bigint->data.digits; + } +} + +bool bigint_fits_in_bits(const BigInt *bn, size_t bit_count, bool is_signed); +void bigint_write_twos_complement(const BigInt *big_int, uint8_t *buf, size_t bit_count, bool is_big_endian); +void bigint_read_twos_complement(BigInt *dest, const uint8_t *buf, size_t bit_count, bool is_big_endian, + bool is_signed); +void bigint_add(BigInt *dest, const BigInt *op1, const BigInt *op2); +void bigint_add_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed); +void bigint_sub(BigInt *dest, const BigInt *op1, const BigInt *op2); +void bigint_sub_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed); +void bigint_mul(BigInt *dest, const BigInt *op1, const BigInt *op2); +void bigint_mul_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed); +void bigint_div_trunc(BigInt *dest, const BigInt *op1, const BigInt *op2); +void bigint_div_floor(BigInt *dest, const BigInt *op1, const BigInt *op2); +void bigint_rem(BigInt *dest, const BigInt *op1, const BigInt *op2); +void bigint_mod(BigInt *dest, const BigInt *op1, const BigInt *op2); + +void bigint_or(BigInt *dest, const BigInt *op1, const BigInt *op2); +void bigint_and(BigInt *dest, const BigInt *op1, const BigInt *op2); +void bigint_xor(BigInt *dest, const BigInt *op1, const BigInt *op2); + +void bigint_shl(BigInt *dest, const BigInt *op1, const BigInt *op2); +void bigint_shl_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed); +void bigint_shr(BigInt *dest, const BigInt *op1, const BigInt *op2); + +void bigint_negate(BigInt *dest, const BigInt *op); +void bigint_negate_wrap(BigInt *dest, const BigInt *op, size_t bit_count); +void bigint_not(BigInt *dest, const BigInt *op, size_t bit_count, bool is_signed); +void bigint_truncate(BigInt *dest, const BigInt *op, size_t bit_count, bool is_signed); + +Cmp bigint_cmp(const BigInt *op1, const BigInt *op2); + +void bigint_write_buf(Buf *buf, const BigInt *op, uint64_t base); + +size_t bigint_ctz(const BigInt *bi, size_t bit_count); +size_t bigint_clz(const BigInt *bi, size_t bit_count); + +size_t bigint_bits_needed(const BigInt *op); + + +// convenience functions +Cmp bigint_cmp_zero(const BigInt *op); + +#endif diff --git a/src/bignum.cpp b/src/bignum.cpp deleted file mode 100644 index e1848aafc2..0000000000 --- a/src/bignum.cpp +++ /dev/null @@ -1,535 +0,0 @@ -/* - * Copyright (c) 2016 Andrew Kelley - * - * This file is part of zig, which is MIT licensed. - * See http://opensource.org/licenses/MIT - */ - -#include "bignum.hpp" -#include "buffer.hpp" -#include "os.hpp" - -#include -#include -#include - -static void bignum_normalize(BigNum *bn) { - assert(bn->kind == BigNumKindInt); - if (bn->data.x_uint == 0) { - bn->is_negative = false; - } -} - -void bignum_init_float(BigNum *dest, double x) { - dest->kind = BigNumKindFloat; - dest->is_negative = false; - dest->data.x_float = x; -} - -void bignum_init_unsigned(BigNum *dest, uint64_t x) { - dest->kind = BigNumKindInt; - dest->is_negative = false; - dest->data.x_uint = x; -} - -void bignum_init_signed(BigNum *dest, int64_t x) { - dest->kind = BigNumKindInt; - if (x < 0) { - dest->is_negative = true; - dest->data.x_uint = ((uint64_t)(-(x + 1))) + 1; - } else { - dest->is_negative = false; - dest->data.x_uint = x; - } -} - -void bignum_init_bignum(BigNum *dest, BigNum *src) { - safe_memcpy(dest, src, 1); -} - -static int u64_log2(uint64_t x) { - int result = 0; - for (; x != 0; x >>= 1) { - result += 1; - } - return result; -} - -bool bignum_fits_in_bits(BigNum *bn, int bit_count, bool is_signed) { - assert(bn->kind == BigNumKindInt); - - if (is_signed) { - uint64_t max_neg; - uint64_t max_pos; - if (bit_count < 64) { - max_neg = (1ULL << (bit_count - 1)); - max_pos = max_neg - 1; - } else { - max_pos = ((uint64_t)INT64_MAX); - max_neg = max_pos + 1; - } - uint64_t max_val = bn->is_negative ? max_neg : max_pos; - return bn->data.x_uint <= max_val; - } else { - if (bn->is_negative) { - return bn->data.x_uint == 0; - } else { - int required_bit_count = u64_log2(bn->data.x_uint); - return bit_count >= required_bit_count; - } - } -} - -void bignum_truncate(BigNum *bn, int bit_count) { - assert(bn->kind == BigNumKindInt); - // TODO handle case when negative = true - if (bit_count < 64) { - bn->data.x_uint &= (1LL << bit_count) - 1; - } -} - -uint64_t bignum_to_twos_complement(BigNum *bn) { - assert(bn->kind == BigNumKindInt); - - if (bn->is_negative) { - int64_t x = bn->data.x_uint; - return -x; - } else { - return bn->data.x_uint; - } -} - -// returns true if overflow happened -bool bignum_add(BigNum *dest, BigNum *op1, BigNum *op2) { - assert(op1->kind == op2->kind); - dest->kind = op1->kind; - - if (dest->kind == BigNumKindFloat) { - dest->data.x_float = op1->data.x_float + op2->data.x_float; - return false; - } - - if (op1->is_negative == op2->is_negative) { - dest->is_negative = op1->is_negative; - return __builtin_uaddll_overflow(op1->data.x_uint, op2->data.x_uint, &dest->data.x_uint); - } else if (!op1->is_negative && op2->is_negative) { - if (__builtin_usubll_overflow(op1->data.x_uint, op2->data.x_uint, &dest->data.x_uint)) { - dest->data.x_uint = (UINT64_MAX - dest->data.x_uint) + 1; - dest->is_negative = true; - bignum_normalize(dest); - return false; - } else { - bignum_normalize(dest); - return false; - } - } else { - return bignum_add(dest, op2, op1); - } -} - -void bignum_negate(BigNum *dest, BigNum *op) { - dest->kind = op->kind; - - if (dest->kind == BigNumKindFloat) { - dest->data.x_float = -op->data.x_float; - } else { - dest->data.x_uint = op->data.x_uint; - dest->is_negative = !op->is_negative; - bignum_normalize(dest); - } -} - -void bignum_not(BigNum *dest, BigNum *op, int bit_count, bool is_signed) { - assert(op->kind == BigNumKindInt); - uint64_t bits = ~bignum_to_twos_complement(op); - if (bit_count < 64) { - bits &= (1LL << bit_count) - 1; - } - if (is_signed) - bignum_init_signed(dest, bits); - else - bignum_init_unsigned(dest, bits); -} - -void bignum_cast_to_float(BigNum *dest, BigNum *op) { - assert(op->kind == BigNumKindInt); - dest->kind = BigNumKindFloat; - - dest->data.x_float = (double)op->data.x_uint; - - if (op->is_negative) { - dest->data.x_float = -dest->data.x_float; - } -} - -void bignum_cast_to_int(BigNum *dest, BigNum *op) { - assert(op->kind == BigNumKindFloat); - dest->kind = BigNumKindInt; - - if (op->data.x_float >= 0) { - dest->data.x_uint = (unsigned long long)op->data.x_float; - dest->is_negative = false; - } else { - dest->data.x_uint = (unsigned long long)-op->data.x_float; - dest->is_negative = true; - } -} - -bool bignum_sub(BigNum *dest, BigNum *op1, BigNum *op2) { - BigNum op2_negated; - bignum_negate(&op2_negated, op2); - return bignum_add(dest, op1, &op2_negated); -} - -bool bignum_mul(BigNum *dest, BigNum *op1, BigNum *op2) { - assert(op1->kind == op2->kind); - dest->kind = op1->kind; - - if (dest->kind == BigNumKindFloat) { - dest->data.x_float = op1->data.x_float * op2->data.x_float; - return false; - } - - if (__builtin_umulll_overflow(op1->data.x_uint, op2->data.x_uint, &dest->data.x_uint)) { - return true; - } - - dest->is_negative = op1->is_negative != op2->is_negative; - bignum_normalize(dest); - return false; -} - -bool bignum_div(BigNum *dest, BigNum *op1, BigNum *op2) { - assert(op1->kind == op2->kind); - dest->kind = op1->kind; - - if (dest->kind == BigNumKindFloat) { - dest->data.x_float = op1->data.x_float / op2->data.x_float; - } else { - return bignum_div_trunc(dest, op1, op2); - } - return false; -} - -bool bignum_div_trunc(BigNum *dest, BigNum *op1, BigNum *op2) { - assert(op1->kind == op2->kind); - dest->kind = op1->kind; - - if (dest->kind == BigNumKindFloat) { - double result = op1->data.x_float / op2->data.x_float; - if (result >= 0) { - dest->data.x_float = floor(result); - } else { - dest->data.x_float = ceil(result); - } - } else { - dest->data.x_uint = op1->data.x_uint / op2->data.x_uint; - dest->is_negative = op1->is_negative != op2->is_negative; - bignum_normalize(dest); - } - return false; -} - -bool bignum_div_floor(BigNum *dest, BigNum *op1, BigNum *op2) { - assert(op1->kind == op2->kind); - dest->kind = op1->kind; - - if (dest->kind == BigNumKindFloat) { - dest->data.x_float = floor(op1->data.x_float / op2->data.x_float); - } else { - if (op1->is_negative != op2->is_negative) { - uint64_t result = op1->data.x_uint / op2->data.x_uint; - if (result * op2->data.x_uint == op1->data.x_uint) { - dest->data.x_uint = result; - } else { - dest->data.x_uint = result + 1; - } - dest->is_negative = true; - } else { - dest->data.x_uint = op1->data.x_uint / op2->data.x_uint; - dest->is_negative = false; - } - } - return false; -} - -bool bignum_rem(BigNum *dest, BigNum *op1, BigNum *op2) { - assert(op1->kind == op2->kind); - dest->kind = op1->kind; - - if (dest->kind == BigNumKindFloat) { - dest->data.x_float = fmod(op1->data.x_float, op2->data.x_float); - } else { - dest->data.x_uint = op1->data.x_uint % op2->data.x_uint; - dest->is_negative = op1->is_negative; - bignum_normalize(dest); - } - return false; -} - -bool bignum_mod(BigNum *dest, BigNum *op1, BigNum *op2) { - assert(op1->kind == op2->kind); - dest->kind = op1->kind; - - if (dest->kind == BigNumKindFloat) { - dest->data.x_float = fmod(fmod(op1->data.x_float, op2->data.x_float) + op2->data.x_float, op2->data.x_float); - } else { - if (op1->is_negative) { - dest->data.x_uint = (op2->data.x_uint - op1->data.x_uint % op2->data.x_uint) % op2->data.x_uint; - } else { - dest->data.x_uint = op1->data.x_uint % op2->data.x_uint; - } - dest->is_negative = false; - bignum_normalize(dest); - } - return false; -} - -bool bignum_or(BigNum *dest, BigNum *op1, BigNum *op2) { - assert(op1->kind == BigNumKindInt); - assert(op2->kind == BigNumKindInt); - - assert(!op1->is_negative); - assert(!op2->is_negative); - - dest->kind = BigNumKindInt; - dest->data.x_uint = op1->data.x_uint | op2->data.x_uint; - return false; -} - -bool bignum_and(BigNum *dest, BigNum *op1, BigNum *op2) { - assert(op1->kind == BigNumKindInt); - assert(op2->kind == BigNumKindInt); - - assert(!op1->is_negative); - assert(!op2->is_negative); - - dest->kind = BigNumKindInt; - dest->data.x_uint = op1->data.x_uint & op2->data.x_uint; - return false; -} - -bool bignum_xor(BigNum *dest, BigNum *op1, BigNum *op2) { - assert(op1->kind == BigNumKindInt); - assert(op2->kind == BigNumKindInt); - - assert(!op1->is_negative); - assert(!op2->is_negative); - - dest->kind = BigNumKindInt; - dest->data.x_uint = op1->data.x_uint ^ op2->data.x_uint; - return false; -} - -bool bignum_shl(BigNum *dest, BigNum *op1, BigNum *op2) { - assert(op1->kind == BigNumKindInt); - assert(op2->kind == BigNumKindInt); - - assert(!op1->is_negative); - assert(!op2->is_negative); - - dest->kind = BigNumKindInt; - dest->data.x_uint = op1->data.x_uint << op2->data.x_uint; - return false; -} - -bool bignum_shr(BigNum *dest, BigNum *op1, BigNum *op2) { - assert(op1->kind == BigNumKindInt); - assert(op2->kind == BigNumKindInt); - - assert(!op1->is_negative); - assert(!op2->is_negative); - - dest->kind = BigNumKindInt; - dest->data.x_uint = op1->data.x_uint >> op2->data.x_uint; - return false; -} - - -Buf *bignum_to_buf(BigNum *bn) { - if (bn->kind == BigNumKindFloat) { - return buf_sprintf("%f", bn->data.x_float); - } else { - const char *neg = bn->is_negative ? "-" : ""; - return buf_sprintf("%s%" ZIG_PRI_llu "", neg, bn->data.x_uint); - } -} - -bool bignum_cmp_eq(BigNum *op1, BigNum *op2) { - assert(op1->kind == op2->kind); - if (op1->kind == BigNumKindFloat) { - return op1->data.x_float == op2->data.x_float; - } else { - return op1->data.x_uint == op2->data.x_uint && - (op1->is_negative == op2->is_negative || op1->data.x_uint == 0); - } -} - -bool bignum_cmp_neq(BigNum *op1, BigNum *op2) { - return !bignum_cmp_eq(op1, op2); -} - -bool bignum_cmp_lt(BigNum *op1, BigNum *op2) { - return !bignum_cmp_gte(op1, op2); -} - -bool bignum_cmp_gt(BigNum *op1, BigNum *op2) { - return !bignum_cmp_lte(op1, op2); -} - -bool bignum_cmp_lte(BigNum *op1, BigNum *op2) { - assert(op1->kind == op2->kind); - if (op1->kind == BigNumKindFloat) { - return (op1->data.x_float <= op2->data.x_float); - } - - // assume normalized is_negative - if (!op1->is_negative && !op2->is_negative) { - return op1->data.x_uint <= op2->data.x_uint; - } else if (op1->is_negative && op2->is_negative) { - return op1->data.x_uint >= op2->data.x_uint; - } else if (op1->is_negative && !op2->is_negative) { - return true; - } else { - return false; - } -} - -bool bignum_cmp_gte(BigNum *op1, BigNum *op2) { - assert(op1->kind == op2->kind); - - if (op1->kind == BigNumKindFloat) { - return (op1->data.x_float >= op2->data.x_float); - } - - // assume normalized is_negative - if (!op1->is_negative && !op2->is_negative) { - return op1->data.x_uint >= op2->data.x_uint; - } else if (op1->is_negative && op2->is_negative) { - return op1->data.x_uint <= op2->data.x_uint; - } else if (op1->is_negative && !op2->is_negative) { - return false; - } else { - return true; - } -} - -bool bignum_increment_by_scalar(BigNum *bignum, uint64_t scalar) { - assert(bignum->kind == BigNumKindInt); - assert(!bignum->is_negative); - return __builtin_uaddll_overflow(bignum->data.x_uint, scalar, &bignum->data.x_uint); -} - -bool bignum_multiply_by_scalar(BigNum *bignum, uint64_t scalar) { - assert(bignum->kind == BigNumKindInt); - assert(!bignum->is_negative); - return __builtin_umulll_overflow(bignum->data.x_uint, scalar, &bignum->data.x_uint); -} - -uint32_t bignum_ctz(BigNum *bignum, uint32_t bit_count) { - assert(bignum->kind == BigNumKindInt); - - uint64_t x = bignum_to_twos_complement(bignum); - uint32_t result = 0; - for (uint32_t i = 0; i < bit_count; i += 1) { - if ((x & 0x1) != 0) - break; - - result += 1; - x = x >> 1; - } - return result; -} - -uint32_t bignum_clz(BigNum *bignum, uint32_t bit_count) { - assert(bignum->kind == BigNumKindInt); - - if (bit_count == 0) - return 0; - - uint64_t x = bignum_to_twos_complement(bignum); - uint64_t mask = ((uint64_t)1) << ((uint64_t)bit_count - 1); - uint32_t result = 0; - for (uint32_t i = 0; i < bit_count; i += 1) { - if ((x & mask) != 0) - break; - - result += 1; - x = x << 1; - } - return result; -} - -void bignum_write_twos_complement(BigNum *bn, uint8_t *buf, int bit_count, bool is_big_endian) { - assert(bn->kind == BigNumKindInt); - uint64_t x = bignum_to_twos_complement(bn); - - int byte_count = (bit_count + 7) / 8; - for (int i = 0; i < byte_count; i += 1) { - uint8_t le_byte = (x >> (i * 8)) & 0xff; - if (is_big_endian) { - buf[byte_count - i - 1] = le_byte; - } else { - buf[i] = le_byte; - } - } -} - -void bignum_read_twos_complement(BigNum *bn, uint8_t *buf, int bit_count, bool is_big_endian, bool is_signed) { - int byte_count = (bit_count + 7) / 8; - - uint64_t twos_comp = 0; - for (int i = 0; i < byte_count; i += 1) { - uint8_t be_byte; - if (is_big_endian) { - be_byte = buf[i]; - } else { - be_byte = buf[byte_count - i - 1]; - } - - twos_comp <<= 8; - twos_comp |= be_byte; - } - - uint8_t be_byte = buf[is_big_endian ? 0 : byte_count - 1]; - if (is_signed && ((be_byte >> 7) & 0x1) != 0) { - bn->is_negative = true; - uint64_t mask = 0; - for (int i = 0; i < bit_count; i += 1) { - mask <<= 1; - mask |= 1; - } - bn->data.x_uint = ((~twos_comp) & mask) + 1; - } else { - bn->data.x_uint = twos_comp; - } - bn->kind = BigNumKindInt; -} - -void bignum_write_ieee597(BigNum *bn, uint8_t *buf, int bit_count, bool is_big_endian) { - assert(bn->kind == BigNumKindFloat); - if (bit_count == 32) { - float f32 = bn->data.x_float; - memcpy(buf, &f32, 4); - } else if (bit_count == 64) { - double f64 = bn->data.x_float; - memcpy(buf, &f64, 8); - } else { - zig_unreachable(); - } -} - -void bignum_read_ieee597(BigNum *bn, uint8_t *buf, int bit_count, bool is_big_endian) { - bn->kind = BigNumKindFloat; - if (bit_count == 32) { - float f32; - memcpy(&f32, buf, 4); - bn->data.x_float = f32; - } else if (bit_count == 64) { - double f64; - memcpy(&f64, buf, 8); - bn->data.x_float = f64; - } else { - zig_unreachable(); - } -} diff --git a/src/bignum.hpp b/src/bignum.hpp deleted file mode 100644 index 57ae4ab688..0000000000 --- a/src/bignum.hpp +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (c) 2016 Andrew Kelley - * - * This file is part of zig, which is MIT licensed. - * See http://opensource.org/licenses/MIT - */ - -#ifndef ZIG_BIGNUM_HPP -#define ZIG_BIGNUM_HPP - -#include - -enum BigNumKind { - BigNumKindInt, - BigNumKindFloat, -}; - -struct BigNum { - BigNumKind kind; - bool is_negative; - union { - unsigned long long x_uint; - double x_float; - } data; -}; - -void bignum_init_float(BigNum *dest, double x); -void bignum_init_unsigned(BigNum *dest, uint64_t x); -void bignum_init_signed(BigNum *dest, int64_t x); -void bignum_init_bignum(BigNum *dest, BigNum *src); - -bool bignum_fits_in_bits(BigNum *bn, int bit_count, bool is_signed); -uint64_t bignum_to_twos_complement(BigNum *bn); - -void bignum_write_twos_complement(BigNum *bn, uint8_t *buf, int bit_count, bool is_big_endian); -void bignum_write_ieee597(BigNum *bn, uint8_t *buf, int bit_count, bool is_big_endian); -void bignum_read_twos_complement(BigNum *bn, uint8_t *buf, int bit_count, bool is_big_endian, bool is_signed); -void bignum_read_ieee597(BigNum *bn, uint8_t *buf, int bit_count, bool is_big_endian); - -// returns true if overflow happened -bool bignum_add(BigNum *dest, BigNum *op1, BigNum *op2); -bool bignum_sub(BigNum *dest, BigNum *op1, BigNum *op2); -bool bignum_mul(BigNum *dest, BigNum *op1, BigNum *op2); -bool bignum_div(BigNum *dest, BigNum *op1, BigNum *op2); -bool bignum_div_trunc(BigNum *dest, BigNum *op1, BigNum *op2); -bool bignum_div_floor(BigNum *dest, BigNum *op1, BigNum *op2); -bool bignum_rem(BigNum *dest, BigNum *op1, BigNum *op2); -bool bignum_mod(BigNum *dest, BigNum *op1, BigNum *op2); - -bool bignum_or(BigNum *dest, BigNum *op1, BigNum *op2); -bool bignum_and(BigNum *dest, BigNum *op1, BigNum *op2); -bool bignum_xor(BigNum *dest, BigNum *op1, BigNum *op2); -bool bignum_shl(BigNum *dest, BigNum *op1, BigNum *op2); -bool bignum_shr(BigNum *dest, BigNum *op1, BigNum *op2); - -void bignum_negate(BigNum *dest, BigNum *op); -void bignum_cast_to_float(BigNum *dest, BigNum *op); -void bignum_cast_to_int(BigNum *dest, BigNum *op); -void bignum_not(BigNum *dest, BigNum *op, int bit_count, bool is_signed); - -void bignum_truncate(BigNum *dest, int bit_count); - -// returns the result of the comparison -bool bignum_cmp_eq(BigNum *op1, BigNum *op2); -bool bignum_cmp_neq(BigNum *op1, BigNum *op2); -bool bignum_cmp_lt(BigNum *op1, BigNum *op2); -bool bignum_cmp_gt(BigNum *op1, BigNum *op2); -bool bignum_cmp_lte(BigNum *op1, BigNum *op2); -bool bignum_cmp_gte(BigNum *op1, BigNum *op2); - -// helper functions -bool bignum_increment_by_scalar(BigNum *bignum, uint64_t scalar); -bool bignum_multiply_by_scalar(BigNum *bignum, uint64_t scalar); - -struct Buf; -Buf *bignum_to_buf(BigNum *bn); - -uint32_t bignum_ctz(BigNum *bignum, uint32_t bit_count); -uint32_t bignum_clz(BigNum *bignum, uint32_t bit_count); - -#endif diff --git a/src/codegen.cpp b/src/codegen.cpp index e9a263a6fa..2a3b4d7938 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1203,6 +1203,23 @@ enum DivKind { DivKindExact, }; +static LLVMValueRef bigint_to_llvm_const(LLVMTypeRef type_ref, BigInt *bigint) { + if (bigint->digit_count == 0) { + return LLVMConstNull(type_ref); + } + LLVMValueRef unsigned_val = LLVMConstIntOfArbitraryPrecision(type_ref, + bigint->digit_count, bigint_ptr(bigint)); + if (bigint->is_negative) { + return LLVMConstNeg(unsigned_val); + } else { + return unsigned_val; + } +} + +static LLVMValueRef bigfloat_to_llvm_const(LLVMTypeRef type_ref, BigFloat *bigfloat) { + return LLVMConstReal(type_ref, bigfloat_to_double(bigfloat)); +} + static LLVMValueRef gen_div(CodeGen *g, bool want_debug_safety, bool want_fast_math, LLVMValueRef val1, LLVMValueRef val2, TypeTableEntry *type_entry, DivKind div_kind) @@ -1230,7 +1247,9 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_debug_safety, bool want_fast_m if (type_entry->id == TypeTableEntryIdInt && type_entry->data.integral.is_signed) { LLVMValueRef neg_1_value = LLVMConstInt(type_entry->type_ref, -1, true); - LLVMValueRef int_min_value = LLVMConstInt(type_entry->type_ref, min_signed_val(type_entry), true); + BigInt int_min_bi = {0}; + eval_min_max_value_int(g, type_entry, &int_min_bi, false); + LLVMValueRef int_min_value = bigint_to_llvm_const(type_entry->type_ref, &int_min_bi); LLVMBasicBlockRef overflow_ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivOverflowOk"); LLVMBasicBlockRef overflow_fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivOverflowFail"); LLVMValueRef num_is_int_min = LLVMBuildICmp(g->builder, LLVMIntEQ, val1, int_min_value, ""); @@ -1765,8 +1784,13 @@ static LLVMValueRef ir_render_int_to_err(CodeGen *g, IrExecutable *executable, I LLVMValueRef zero = LLVMConstNull(actual_type->type_ref); LLVMValueRef neq_zero_bit = LLVMBuildICmp(g->builder, LLVMIntNE, target_val, zero, ""); LLVMValueRef ok_bit; - uint64_t biggest_possible_err_val = max_unsigned_val(actual_type); - if (biggest_possible_err_val < g->error_decls.length) { + + BigInt biggest_possible_err_val = {0}; + eval_min_max_value_int(g, actual_type, &biggest_possible_err_val, true); + + if (bigint_fits_in_bits(&biggest_possible_err_val, 64, false) && + bigint_as_unsigned(&biggest_possible_err_val) < g->error_decls.length) + { ok_bit = neq_zero_bit; } else { LLVMValueRef error_value_count = LLVMConstInt(actual_type->type_ref, g->error_decls.length, false); @@ -3317,7 +3341,6 @@ static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, Con LLVMValueRef int_val = gen_const_val(g, const_val); return LLVMConstZExt(int_val, big_int_type_ref); } - return LLVMConstInt(big_int_type_ref, bignum_to_twos_complement(&const_val->data.x_bignum), false); case TypeTableEntryIdFloat: { LLVMValueRef float_val = gen_const_val(g, const_val); @@ -3374,21 +3397,13 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) { switch (type_entry->id) { case TypeTableEntryIdInt: case TypeTableEntryIdEnumTag: - return LLVMConstInt(type_entry->type_ref, bignum_to_twos_complement(&const_val->data.x_bignum), false); + return bigint_to_llvm_const(type_entry->type_ref, &const_val->data.x_bigint); case TypeTableEntryIdPureError: assert(const_val->data.x_pure_err); return LLVMConstInt(g->builtin_types.entry_pure_error->type_ref, const_val->data.x_pure_err->value, false); case TypeTableEntryIdFloat: - if (const_val->data.x_bignum.kind == BigNumKindFloat) { - return LLVMConstReal(type_entry->type_ref, const_val->data.x_bignum.data.x_float); - } else { - double x = (double)const_val->data.x_bignum.data.x_uint; - if (const_val->data.x_bignum.is_negative) { - x = -x; - } - return LLVMConstReal(type_entry->type_ref, x); - } + return bigfloat_to_llvm_const(type_entry->type_ref, &const_val->data.x_bigfloat); case TypeTableEntryIdBool: if (const_val->data.x_bool) { return LLVMConstAllOnes(LLVMInt1Type()); @@ -3866,7 +3881,7 @@ static void do_code_gen(CodeGen *g) { ConstExprValue *const_val = var->value; assert(const_val->special != ConstValSpecialRuntime); TypeTableEntry *var_type = g->builtin_types.entry_f64; - LLVMValueRef init_val = LLVMConstReal(var_type->type_ref, const_val->data.x_bignum.data.x_float); + LLVMValueRef init_val = bigfloat_to_llvm_const(var_type->type_ref, &const_val->data.x_bigfloat); gen_global_var(g, var, init_val, var_type); continue; } @@ -3875,10 +3890,9 @@ static void do_code_gen(CodeGen *g) { // Generate debug info for it but that's it. ConstExprValue *const_val = var->value; assert(const_val->special != ConstValSpecialRuntime); - TypeTableEntry *var_type = const_val->data.x_bignum.is_negative ? - g->builtin_types.entry_isize : g->builtin_types.entry_usize; - LLVMValueRef init_val = LLVMConstInt(var_type->type_ref, - bignum_to_twos_complement(&const_val->data.x_bignum), false); + size_t bits_needed = bigint_bits_needed(&const_val->data.x_bigint); + TypeTableEntry *var_type = get_int_type(g, const_val->data.x_bigint.is_negative, bits_needed); + LLVMValueRef init_val = bigint_to_llvm_const(var_type->type_ref, &const_val->data.x_bigint); gen_global_var(g, var, init_val, var_type); continue; } diff --git a/src/ir.cpp b/src/ir.cpp index d1f14baf8e..0f6b70316e 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -656,16 +656,23 @@ static IrInstruction *ir_build_const_uint(IrBuilder *irb, Scope *scope, AstNode IrInstructionConst *const_instruction = ir_build_instruction(irb, scope, source_node); const_instruction->base.value.type = irb->codegen->builtin_types.entry_num_lit_int; const_instruction->base.value.special = ConstValSpecialStatic; - bignum_init_unsigned(&const_instruction->base.value.data.x_bignum, value); + bigint_init_unsigned(&const_instruction->base.value.data.x_bigint, value); return &const_instruction->base; } -static IrInstruction *ir_build_const_bignum(IrBuilder *irb, Scope *scope, AstNode *source_node, BigNum *bignum) { +static IrInstruction *ir_build_const_bigint(IrBuilder *irb, Scope *scope, AstNode *source_node, BigInt *bigint) { IrInstructionConst *const_instruction = ir_build_instruction(irb, scope, source_node); - const_instruction->base.value.type = (bignum->kind == BigNumKindInt) ? - irb->codegen->builtin_types.entry_num_lit_int : irb->codegen->builtin_types.entry_num_lit_float; + const_instruction->base.value.type = irb->codegen->builtin_types.entry_num_lit_int; const_instruction->base.value.special = ConstValSpecialStatic; - const_instruction->base.value.data.x_bignum = *bignum; + bigint_init_bigint(&const_instruction->base.value.data.x_bigint, bigint); + return &const_instruction->base; +} + +static IrInstruction *ir_build_const_bigfloat(IrBuilder *irb, Scope *scope, AstNode *source_node, BigFloat *bigfloat) { + IrInstructionConst *const_instruction = ir_build_instruction(irb, scope, source_node); + const_instruction->base.value.type = irb->codegen->builtin_types.entry_num_lit_float; + const_instruction->base.value.special = ConstValSpecialStatic; + bigfloat_init_bigfloat(&const_instruction->base.value.data.x_bigfloat, bigfloat); return &const_instruction->base; } @@ -680,7 +687,7 @@ static IrInstruction *ir_build_const_usize(IrBuilder *irb, Scope *scope, AstNode IrInstructionConst *const_instruction = ir_build_instruction(irb, scope, source_node); const_instruction->base.value.type = irb->codegen->builtin_types.entry_usize; const_instruction->base.value.special = ConstValSpecialStatic; - bignum_init_unsigned(&const_instruction->base.value.data.x_bignum, value); + bigint_init_unsigned(&const_instruction->base.value.data.x_bigint, value); return &const_instruction->base; } @@ -3687,15 +3694,21 @@ static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node) zig_unreachable(); } -static IrInstruction *ir_gen_num_lit(IrBuilder *irb, Scope *scope, AstNode *node) { - assert(node->type == NodeTypeNumberLiteral); +static IrInstruction *ir_gen_int_lit(IrBuilder *irb, Scope *scope, AstNode *node) { + assert(node->type == NodeTypeIntLiteral); - if (node->data.number_literal.overflow) { - add_node_error(irb->codegen, node, buf_sprintf("number literal too large to be represented in any type")); + return ir_build_const_bigint(irb, scope, node, node->data.int_literal.bigint); +} + +static IrInstruction *ir_gen_float_lit(IrBuilder *irb, Scope *scope, AstNode *node) { + assert(node->type == NodeTypeFloatLiteral); + + if (node->data.float_literal.overflow) { + add_node_error(irb->codegen, node, buf_sprintf("float literal too large to be represented in any type")); return irb->codegen->invalid_instruction; } - return ir_build_const_bignum(irb, scope, node, node->data.number_literal.bignum); + return ir_build_const_bigfloat(irb, scope, node, node->data.float_literal.bigfloat); } static IrInstruction *ir_gen_char_lit(IrBuilder *irb, Scope *scope, AstNode *node) { @@ -5933,8 +5946,10 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop return ir_gen_node_raw(irb, node->data.grouped_expr, scope, lval); case NodeTypeBinOpExpr: return ir_lval_wrap(irb, scope, ir_gen_bin_op(irb, scope, node), lval); - case NodeTypeNumberLiteral: - return ir_lval_wrap(irb, scope, ir_gen_num_lit(irb, scope, node), lval); + case NodeTypeIntLiteral: + return ir_lval_wrap(irb, scope, ir_gen_int_lit(irb, scope, node), lval); + case NodeTypeFloatLiteral: + return ir_lval_wrap(irb, scope, ir_gen_float_lit(irb, scope, node), lval); case NodeTypeCharLiteral: return ir_lval_wrap(irb, scope, ir_gen_char_lit(irb, scope, node), lval); case NodeTypeSymbol: @@ -6184,6 +6199,13 @@ static bool ir_emit_global_runtime_side_effect(IrAnalyze *ira, IrInstruction *so return true; } +static bool const_val_fits_in_num_lit(ConstExprValue *const_val, TypeTableEntry *num_lit_type) { + return ((num_lit_type->id == TypeTableEntryIdNumLitFloat && + (const_val->type->id == TypeTableEntryIdFloat || const_val->type->id == TypeTableEntryIdNumLitFloat)) || + (num_lit_type->id == TypeTableEntryIdNumLitInt && + (const_val->type->id == TypeTableEntryIdInt || const_val->type->id == TypeTableEntryIdNumLitInt))); +} + static bool ir_num_lit_fits_in_other_type(IrAnalyze *ira, IrInstruction *instruction, TypeTableEntry *other_type) { if (type_is_invalid(other_type)) { return false; @@ -6191,44 +6213,51 @@ static bool ir_num_lit_fits_in_other_type(IrAnalyze *ira, IrInstruction *instruc ConstExprValue *const_val = &instruction->value; assert(const_val->special != ConstValSpecialRuntime); + + bool const_val_is_int = (const_val->type->id == TypeTableEntryIdInt || + const_val->type->id == TypeTableEntryIdNumLitInt); + bool const_val_is_float = (const_val->type->id == TypeTableEntryIdFloat || + const_val->type->id == TypeTableEntryIdNumLitFloat); if (other_type->id == TypeTableEntryIdFloat) { return true; - } else if (other_type->id == TypeTableEntryIdInt && - const_val->data.x_bignum.kind == BigNumKindInt) - { - if (bignum_fits_in_bits(&const_val->data.x_bignum, other_type->data.integral.bit_count, + } else if (other_type->id == TypeTableEntryIdInt && const_val_is_int) { + if (bigint_fits_in_bits(&const_val->data.x_bigint, other_type->data.integral.bit_count, other_type->data.integral.is_signed)) { return true; } - } else if ((other_type->id == TypeTableEntryIdNumLitFloat && const_val->data.x_bignum.kind == BigNumKindFloat) || - (other_type->id == TypeTableEntryIdNumLitInt && const_val->data.x_bignum.kind == BigNumKindInt )) - { + } else if (const_val_fits_in_num_lit(const_val, other_type)) { return true; } else if (other_type->id == TypeTableEntryIdMaybe) { TypeTableEntry *child_type = other_type->data.maybe.child_type; - if ((child_type->id == TypeTableEntryIdNumLitFloat && const_val->data.x_bignum.kind == BigNumKindFloat) || - (child_type->id == TypeTableEntryIdNumLitInt && const_val->data.x_bignum.kind == BigNumKindInt )) - { + if (const_val_fits_in_num_lit(const_val, child_type)) { return true; - } else if (child_type->id == TypeTableEntryIdInt && const_val->data.x_bignum.kind == BigNumKindInt) { - if (bignum_fits_in_bits(&const_val->data.x_bignum, + } else if (child_type->id == TypeTableEntryIdInt && const_val_is_int) { + if (bigint_fits_in_bits(&const_val->data.x_bigint, child_type->data.integral.bit_count, child_type->data.integral.is_signed)) { return true; } - } else if (child_type->id == TypeTableEntryIdFloat && const_val->data.x_bignum.kind == BigNumKindFloat) { + } else if (child_type->id == TypeTableEntryIdFloat && const_val_is_float) { return true; } } - const char *num_lit_str = (const_val->data.x_bignum.kind == BigNumKindFloat) ? "float" : "integer"; + const char *num_lit_str; + Buf *val_buf = buf_alloc(); + if (const_val_is_float) { + num_lit_str = "float"; + bigfloat_write_buf(val_buf, &const_val->data.x_bigfloat); + } else { + num_lit_str = "integer"; + bigint_write_buf(val_buf, &const_val->data.x_bigint, 10); + } ir_add_error(ira, instruction, buf_sprintf("%s value %s cannot be implicitly casted to type '%s'", num_lit_str, - buf_ptr(bignum_to_buf(&const_val->data.x_bignum)), + buf_ptr(val_buf), buf_ptr(&other_type->name))); return false; } @@ -6643,7 +6672,13 @@ static void eval_const_expr_implicit_cast(CastOp cast_op, break; } case CastOpNumLitToConcrete: - const_val->data.x_bignum = other_val->data.x_bignum; + if (other_val->type->id == TypeTableEntryIdNumLitFloat) { + bigfloat_init_bigfloat(&const_val->data.x_bigfloat, &other_val->data.x_bigfloat); + } else if (other_val->type->id == TypeTableEntryIdNumLitInt) { + bigint_init_bigint(&const_val->data.x_bigint, &other_val->data.x_bigint); + } else { + zig_unreachable(); + } const_val->type = new_type; break; case CastOpResizeSlice: @@ -6651,15 +6686,15 @@ static void eval_const_expr_implicit_cast(CastOp cast_op, // can't do it break; case CastOpIntToFloat: - bignum_cast_to_float(&const_val->data.x_bignum, &other_val->data.x_bignum); + bigfloat_init_bigint(&const_val->data.x_bigfloat, &other_val->data.x_bigint); const_val->special = ConstValSpecialStatic; break; case CastOpFloatToInt: - bignum_cast_to_int(&const_val->data.x_bignum, &other_val->data.x_bignum); + bigint_init_bigfloat(&const_val->data.x_bigint, &other_val->data.x_bigfloat); const_val->special = ConstValSpecialStatic; break; case CastOpBoolToInt: - bignum_init_unsigned(&const_val->data.x_bignum, other_val->data.x_bool ? 1 : 0); + bigint_init_unsigned(&const_val->data.x_bigint, other_val->data.x_bool ? 1 : 0); const_val->special = ConstValSpecialStatic; break; } @@ -6878,7 +6913,7 @@ static TypeTableEntry *ir_analyze_const_ptr(IrAnalyze *ira, IrInstruction *instr static TypeTableEntry *ir_analyze_const_usize(IrAnalyze *ira, IrInstruction *instruction, uint64_t value) { ConstExprValue *const_val = ir_build_const_from(ira, instruction); - bignum_init_unsigned(&const_val->data.x_bignum, value); + bigint_init_unsigned(&const_val->data.x_bigint, value); return ira->codegen->builtin_types.entry_usize; } @@ -7239,12 +7274,12 @@ static IrInstruction *ir_analyze_widen_or_shorten(IrAnalyze *ira, IrInstruction if (!val) return ira->codegen->invalid_instruction; if (wanted_type->id == TypeTableEntryIdInt) { - if (val->data.x_bignum.is_negative && !wanted_type->data.integral.is_signed) { + if (bigint_cmp_zero(&val->data.x_bigint) == CmpLT && !wanted_type->data.integral.is_signed) { ir_add_error(ira, source_instr, buf_sprintf("attempt to cast negative value to unsigned integer")); return ira->codegen->invalid_instruction; } - if (!bignum_fits_in_bits(&val->data.x_bignum, wanted_type->data.integral.bit_count, + if (!bigint_fits_in_bits(&val->data.x_bigint, wanted_type->data.integral.bit_count, wanted_type->data.integral.is_signed)) { ir_add_error(ira, source_instr, @@ -7255,7 +7290,11 @@ static IrInstruction *ir_analyze_widen_or_shorten(IrAnalyze *ira, IrInstruction } IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type); - result->value.data.x_bignum = val->data.x_bignum; + if (wanted_type->id == TypeTableEntryIdInt) { + bigint_init_bigint(&result->value.data.x_bigint, &val->data.x_bigint); + } else { + bigfloat_init_bigfloat(&result->value.data.x_bigfloat, &val->data.x_bigfloat); + } result->value.type = wanted_type; return result; } @@ -7278,7 +7317,7 @@ static IrInstruction *ir_analyze_ptr_to_int(IrAnalyze *ira, IrInstruction *sourc if (val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr) { IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type); - bignum_init_unsigned(&result->value.data.x_bignum, val->data.x_ptr.data.hard_coded_addr.addr); + bigint_init_unsigned(&result->value.data.x_bigint, val->data.x_ptr.data.hard_coded_addr.addr); return result; } } @@ -7299,9 +7338,20 @@ static IrInstruction *ir_analyze_int_to_enum(IrAnalyze *ira, IrInstruction *sour ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); if (!val) return ira->codegen->invalid_instruction; + BigInt enum_member_count; + bigint_init_unsigned(&enum_member_count, wanted_type->data.enumeration.src_field_count); + if (bigint_cmp(&val->data.x_bigint, &enum_member_count) != CmpLT) { + Buf *val_buf = buf_alloc(); + bigint_write_buf(val_buf, &val->data.x_bigint, 10); + ir_add_error(ira, source_instr, + buf_sprintf("integer value %s too big for enum '%s' which has %" PRIu32 " fields", + buf_ptr(val_buf), buf_ptr(&wanted_type->name), wanted_type->data.enumeration.src_field_count)); + return ira->codegen->invalid_instruction; + } + IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type); - result->value.data.x_enum.tag = val->data.x_bignum.data.x_uint; + result->value.data.x_enum.tag = bigint_as_unsigned(&val->data.x_bigint); return result; } @@ -7320,7 +7370,13 @@ static IrInstruction *ir_analyze_number_to_literal(IrAnalyze *ira, IrInstruction IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type); - bignum_init_bignum(&result->value.data.x_bignum, &val->data.x_bignum); + if (wanted_type->id == TypeTableEntryIdNumLitFloat) { + bigfloat_init_bigfloat(&result->value.data.x_bigfloat, &val->data.x_bigfloat); + } else if (wanted_type->id == TypeTableEntryIdNumLitInt) { + bigint_init_bigint(&result->value.data.x_bigint, &val->data.x_bigint); + } else { + zig_unreachable(); + } return result; } @@ -7336,13 +7392,17 @@ static IrInstruction *ir_analyze_int_to_err(IrAnalyze *ira, IrInstruction *sourc IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, source_instr->source_node, ira->codegen->builtin_types.entry_pure_error); - uint64_t index = val->data.x_bignum.data.x_uint; - if (index == 0 || index >= ira->codegen->error_decls.length) { + BigInt err_count; + bigint_init_unsigned(&err_count, ira->codegen->error_decls.length); + if (bigint_cmp_zero(&val->data.x_bigint) == CmpEQ || bigint_cmp(&val->data.x_bigint, &err_count) != CmpLT) { + Buf *val_buf = buf_alloc(); + bigint_write_buf(val_buf, &val->data.x_bigint, 10); ir_add_error(ira, source_instr, - buf_sprintf("integer value %" ZIG_PRI_u64 " represents no error", index)); + buf_sprintf("integer value %s represents no error", buf_ptr(val_buf))); return ira->codegen->invalid_instruction; } + size_t index = bigint_as_unsigned(&val->data.x_bigint); AstNode *error_decl_node = ira->codegen->error_decls.at(index); result->value.data.x_pure_err = error_decl_node->data.error_value_decl.err; return result; @@ -7378,9 +7438,9 @@ static IrInstruction *ir_analyze_err_to_int(IrAnalyze *ira, IrInstruction *sourc } result->value.type = wanted_type; uint64_t err_value = err ? err->value : 0; - bignum_init_unsigned(&result->value.data.x_bignum, err_value); + bigint_init_unsigned(&result->value.data.x_bigint, err_value); - if (!bignum_fits_in_bits(&result->value.data.x_bignum, + if (!bigint_fits_in_bits(&result->value.data.x_bigint, wanted_type->data.integral.bit_count, wanted_type->data.integral.is_signed)) { ir_add_error_node(ira, source_instr->source_node, @@ -7392,9 +7452,9 @@ static IrInstruction *ir_analyze_err_to_int(IrAnalyze *ira, IrInstruction *sourc return result; } - BigNum bn; - bignum_init_unsigned(&bn, ira->codegen->error_decls.length); - if (!bignum_fits_in_bits(&bn, wanted_type->data.integral.bit_count, wanted_type->data.integral.is_signed)) { + BigInt bn; + bigint_init_unsigned(&bn, ira->codegen->error_decls.length); + if (!bigint_fits_in_bits(&bn, wanted_type->data.integral.bit_count, wanted_type->data.integral.is_signed)) { ir_add_error_node(ira, source_instr->source_node, buf_sprintf("too many error values to fit in '%s'", buf_ptr(&wanted_type->name))); return ira->codegen->invalid_instruction; @@ -7861,7 +7921,7 @@ static bool ir_resolve_usize(IrAnalyze *ira, IrInstruction *value, uint64_t *out if (!const_val) return false; - *out = const_val->data.x_bignum.data.x_uint; + *out = bigint_as_unsigned(&const_val->data.x_bigint); return true; } @@ -7941,7 +8001,7 @@ static Buf *ir_resolve_str(IrAnalyze *ira, IrInstruction *value) { assert(ptr_field->data.x_ptr.special == ConstPtrSpecialBaseArray); ConstExprValue *array_val = ptr_field->data.x_ptr.data.base_array.array_val; expand_undef_array(ira->codegen, array_val); - size_t len = len_field->data.x_bignum.data.x_uint; + size_t len = bigint_as_unsigned(&len_field->data.x_bigint); Buf *result = buf_alloc(); buf_resize(result, len); for (size_t i = 0; i < len; i += 1) { @@ -7951,7 +8011,7 @@ static Buf *ir_resolve_str(IrAnalyze *ira, IrInstruction *value) { ir_add_error(ira, casted_value, buf_sprintf("use of undefined value")); return nullptr; } - uint64_t big_c = char_val->data.x_bignum.data.x_uint; + uint64_t big_c = bigint_as_unsigned(&char_val->data.x_bigint); assert(big_c <= UINT8_MAX); uint8_t c = (uint8_t)big_c; buf_ptr(result)[i] = c; @@ -8039,6 +8099,24 @@ static TypeTableEntry *ir_analyze_bin_op_bool(IrAnalyze *ira, IrInstructionBinOp return bool_type; } +static bool resolve_cmp_op_id(IrBinOp op_id, Cmp cmp) { + if (op_id == IrBinOpCmpEq) { + return cmp == CmpEQ; + } else if (op_id == IrBinOpCmpNotEq) { + return cmp != CmpEQ; + } else if (op_id == IrBinOpCmpLessThan) { + return cmp == CmpLT; + } else if (op_id == IrBinOpCmpGreaterThan) { + return cmp == CmpGT; + } else if (op_id == IrBinOpCmpLessOrEq) { + return cmp != CmpGT; + } else if (op_id == IrBinOpCmpGreaterOrEq) { + return cmp != CmpLT; + } else { + zig_unreachable(); + } +} + static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) { IrInstruction *op1 = bin_op_instruction->op1->other; IrInstruction *op2 = bin_op_instruction->op2->other; @@ -8157,30 +8235,13 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp ConstExprValue *op1_val = &casted_op1->value; ConstExprValue *op2_val = &casted_op2->value; if ((value_is_comptime(op1_val) && value_is_comptime(op2_val)) || resolved_type->id == TypeTableEntryIdVoid) { - bool type_can_gt_lt_cmp = (resolved_type->id == TypeTableEntryIdNumLitFloat || - resolved_type->id == TypeTableEntryIdNumLitInt || - resolved_type->id == TypeTableEntryIdFloat || - resolved_type->id == TypeTableEntryIdInt); bool answer; - if (type_can_gt_lt_cmp) { - bool (*bignum_cmp)(BigNum *, BigNum *); - if (op_id == IrBinOpCmpEq) { - bignum_cmp = bignum_cmp_eq; - } else if (op_id == IrBinOpCmpNotEq) { - bignum_cmp = bignum_cmp_neq; - } else if (op_id == IrBinOpCmpLessThan) { - bignum_cmp = bignum_cmp_lt; - } else if (op_id == IrBinOpCmpGreaterThan) { - bignum_cmp = bignum_cmp_gt; - } else if (op_id == IrBinOpCmpLessOrEq) { - bignum_cmp = bignum_cmp_lte; - } else if (op_id == IrBinOpCmpGreaterOrEq) { - bignum_cmp = bignum_cmp_gte; - } else { - zig_unreachable(); - } - - answer = bignum_cmp(&op1_val->data.x_bignum, &op2_val->data.x_bignum); + if (resolved_type->id == TypeTableEntryIdNumLitFloat || resolved_type->id == TypeTableEntryIdFloat) { + Cmp cmp_result = bigfloat_cmp(&op1_val->data.x_bigfloat, &op2_val->data.x_bigfloat); + answer = resolve_cmp_op_id(op_id, cmp_result); + } else if (resolved_type->id == TypeTableEntryIdNumLitInt || resolved_type->id == TypeTableEntryIdInt) { + Cmp cmp_result = bigint_cmp(&op1_val->data.x_bigint, &op2_val->data.x_bigint); + answer = resolve_cmp_op_id(op_id, cmp_result); } else { bool are_equal = resolved_type->id == TypeTableEntryIdVoid || const_values_equal(op1_val, op2_val); if (op_id == IrBinOpCmpEq) { @@ -8220,7 +8281,7 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp } else { known_left_val = nullptr; } - if (known_left_val != nullptr && known_left_val->data.x_bignum.data.x_uint == 0 && + if (known_left_val != nullptr && bigint_cmp_zero(&known_left_val->data.x_bigint) == CmpEQ && (flipped_op_id == IrBinOpCmpLessOrEq || flipped_op_id == IrBinOpCmpGreaterThan)) { bool answer = (flipped_op_id == IrBinOpCmpLessOrEq); @@ -8236,101 +8297,35 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp return ira->codegen->builtin_types.entry_bool; } -enum EvalBigNumSpecial { - EvalBigNumSpecialNone, - EvalBigNumSpecialWrapping, - EvalBigNumSpecialExact, -}; - -static int ir_eval_bignum(ConstExprValue *op1_val, ConstExprValue *op2_val, - ConstExprValue *out_val, bool (*bignum_fn)(BigNum *, BigNum *, BigNum *), - TypeTableEntry *type, EvalBigNumSpecial special) +static int ir_eval_math_op(TypeTableEntry *type_entry, ConstExprValue *op1_val, + IrBinOp op_id, ConstExprValue *op2_val, ConstExprValue *out_val) { - bool is_int = false; - bool is_float = false; - if (type->id == TypeTableEntryIdInt || - type->id == TypeTableEntryIdNumLitInt) - { + bool is_int; + bool is_float; + Cmp op2_zcmp; + if (type_entry->id == TypeTableEntryIdInt || type_entry->id == TypeTableEntryIdNumLitInt) { is_int = true; - } else if (type->id == TypeTableEntryIdFloat || - type->id == TypeTableEntryIdNumLitFloat) + is_float = false; + op2_zcmp = bigint_cmp_zero(&op2_val->data.x_bigint); + } else if (type_entry->id == TypeTableEntryIdFloat || + type_entry->id == TypeTableEntryIdNumLitFloat) { + is_int = false; is_float = true; + op2_zcmp = bigfloat_cmp_zero(&op2_val->data.x_bigfloat); } else { zig_unreachable(); } - if (bignum_fn == bignum_div || bignum_fn == bignum_rem || bignum_fn == bignum_mod || - bignum_fn == bignum_div_trunc || bignum_fn == bignum_div_floor) + + if ((op_id == IrBinOpDivUnspecified || op_id == IrBinOpRemRem || op_id == IrBinOpRemMod || + op_id == IrBinOpDivTrunc || op_id == IrBinOpDivFloor) && op2_zcmp == CmpEQ) { - if ((is_int && op2_val->data.x_bignum.data.x_uint == 0) || - (is_float && op2_val->data.x_bignum.data.x_float == 0.0)) - { - return ErrorDivByZero; - } + return ErrorDivByZero; } - if (bignum_fn == bignum_rem || bignum_fn == bignum_mod) { - BigNum zero; - if (is_float) { - bignum_init_float(&zero, 0.0); - } else { - bignum_init_unsigned(&zero, 0); - } - if (bignum_cmp_lt(&op2_val->data.x_bignum, &zero)) { - return ErrorNegativeDenominator; - } + if ((op_id == IrBinOpRemRem || op_id == IrBinOpRemMod) && op2_zcmp == CmpLT) { + return ErrorNegativeDenominator; } - if (special == EvalBigNumSpecialExact) { - assert(bignum_fn == bignum_div); - BigNum remainder; - if (bignum_rem(&remainder, &op1_val->data.x_bignum, &op2_val->data.x_bignum)) { - return ErrorOverflow; - } - BigNum zero; - if (is_float) { - bignum_init_float(&zero, 0.0); - } else { - bignum_init_unsigned(&zero, 0); - } - if (bignum_cmp_neq(&remainder, &zero)) { - return ErrorExactDivRemainder; - } - } - - bool overflow = bignum_fn(&out_val->data.x_bignum, &op1_val->data.x_bignum, &op2_val->data.x_bignum); - if (overflow) { - if (special == EvalBigNumSpecialWrapping) { - zig_panic("TODO compiler bug, implement compile-time wrapping arithmetic for >= 64 bit ints"); - } else { - return ErrorOverflow; - } - } - - if (type->id == TypeTableEntryIdInt && !bignum_fits_in_bits(&out_val->data.x_bignum, - type->data.integral.bit_count, type->data.integral.is_signed)) - { - if (special == EvalBigNumSpecialWrapping) { - if (type->data.integral.is_signed) { - out_val->data.x_bignum.data.x_uint = max_unsigned_val(type) - out_val->data.x_bignum.data.x_uint + 1; - out_val->data.x_bignum.is_negative = !out_val->data.x_bignum.is_negative; - } else if (out_val->data.x_bignum.is_negative) { - out_val->data.x_bignum.data.x_uint = max_unsigned_val(type) - out_val->data.x_bignum.data.x_uint + 1; - out_val->data.x_bignum.is_negative = false; - } else { - bignum_truncate(&out_val->data.x_bignum, type->data.integral.bit_count); - } - } else { - return ErrorOverflow; - } - } - - out_val->special = ConstValSpecialStatic; - return 0; -} - -static int ir_eval_math_op(TypeTableEntry *canon_type, ConstExprValue *op1_val, - IrBinOp op_id, ConstExprValue *op2_val, ConstExprValue *out_val) -{ switch (op_id) { case IrBinOpInvalid: case IrBinOpBoolOr: @@ -8346,43 +8341,128 @@ static int ir_eval_math_op(TypeTableEntry *canon_type, ConstExprValue *op1_val, case IrBinOpRemUnspecified: zig_unreachable(); case IrBinOpBinOr: - return ir_eval_bignum(op1_val, op2_val, out_val, bignum_or, canon_type, EvalBigNumSpecialNone); + assert(is_int); + bigint_or(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); + break; case IrBinOpBinXor: - return ir_eval_bignum(op1_val, op2_val, out_val, bignum_xor, canon_type, EvalBigNumSpecialNone); + assert(is_int); + bigint_xor(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); + break; case IrBinOpBinAnd: - return ir_eval_bignum(op1_val, op2_val, out_val, bignum_and, canon_type, EvalBigNumSpecialNone); + assert(is_int); + bigint_and(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); + break; case IrBinOpBitShiftLeft: - return ir_eval_bignum(op1_val, op2_val, out_val, bignum_shl, canon_type, EvalBigNumSpecialNone); + assert(is_int); + bigint_shl(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); + break; case IrBinOpBitShiftLeftWrap: - return ir_eval_bignum(op1_val, op2_val, out_val, bignum_shl, canon_type, EvalBigNumSpecialWrapping); + assert(type_entry->id == TypeTableEntryIdInt); + bigint_shl_wrap(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint, + type_entry->data.integral.bit_count, type_entry->data.integral.is_signed); + break; case IrBinOpBitShiftRight: - return ir_eval_bignum(op1_val, op2_val, out_val, bignum_shr, canon_type, EvalBigNumSpecialNone); + assert(is_int); + bigint_shr(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); + break; case IrBinOpAdd: - return ir_eval_bignum(op1_val, op2_val, out_val, bignum_add, canon_type, EvalBigNumSpecialNone); + if (is_int) { + bigint_add(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); + } else { + bigfloat_add(&out_val->data.x_bigfloat, &op1_val->data.x_bigfloat, &op2_val->data.x_bigfloat); + } + break; case IrBinOpAddWrap: - return ir_eval_bignum(op1_val, op2_val, out_val, bignum_add, canon_type, EvalBigNumSpecialWrapping); + assert(type_entry->id == TypeTableEntryIdInt); + bigint_add_wrap(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint, + type_entry->data.integral.bit_count, type_entry->data.integral.is_signed); + break; case IrBinOpSub: - return ir_eval_bignum(op1_val, op2_val, out_val, bignum_sub, canon_type, EvalBigNumSpecialNone); + if (is_int) { + bigint_sub(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); + } else { + bigfloat_sub(&out_val->data.x_bigfloat, &op1_val->data.x_bigfloat, &op2_val->data.x_bigfloat); + } + break; case IrBinOpSubWrap: - return ir_eval_bignum(op1_val, op2_val, out_val, bignum_sub, canon_type, EvalBigNumSpecialWrapping); + assert(type_entry->id == TypeTableEntryIdInt); + bigint_sub_wrap(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint, + type_entry->data.integral.bit_count, type_entry->data.integral.is_signed); + break; case IrBinOpMult: - return ir_eval_bignum(op1_val, op2_val, out_val, bignum_mul, canon_type, EvalBigNumSpecialNone); + if (is_int) { + bigint_mul(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); + } else { + bigfloat_mul(&out_val->data.x_bigfloat, &op1_val->data.x_bigfloat, &op2_val->data.x_bigfloat); + } + break; case IrBinOpMultWrap: - return ir_eval_bignum(op1_val, op2_val, out_val, bignum_mul, canon_type, EvalBigNumSpecialWrapping); + assert(type_entry->id == TypeTableEntryIdInt); + bigint_mul_wrap(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint, + type_entry->data.integral.bit_count, type_entry->data.integral.is_signed); + break; case IrBinOpDivUnspecified: - return ir_eval_bignum(op1_val, op2_val, out_val, bignum_div, canon_type, EvalBigNumSpecialNone); + assert(is_float); + bigfloat_div(&out_val->data.x_bigfloat, &op1_val->data.x_bigfloat, &op2_val->data.x_bigfloat); + break; case IrBinOpDivTrunc: - return ir_eval_bignum(op1_val, op2_val, out_val, bignum_div_trunc, canon_type, EvalBigNumSpecialNone); + if (is_int) { + bigint_div_trunc(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); + } else { + bigfloat_div_trunc(&out_val->data.x_bigfloat, &op1_val->data.x_bigfloat, &op2_val->data.x_bigfloat); + } + break; case IrBinOpDivFloor: - return ir_eval_bignum(op1_val, op2_val, out_val, bignum_div_floor, canon_type, EvalBigNumSpecialNone); + if (is_int) { + bigint_div_floor(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); + } else { + bigfloat_div_floor(&out_val->data.x_bigfloat, &op1_val->data.x_bigfloat, &op2_val->data.x_bigfloat); + } + break; case IrBinOpDivExact: - return ir_eval_bignum(op1_val, op2_val, out_val, bignum_div, canon_type, EvalBigNumSpecialExact); + if (is_int) { + bigint_div_trunc(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); + BigInt remainder; + bigint_rem(&remainder, &op1_val->data.x_bigint, &op2_val->data.x_bigint); + if (bigint_cmp_zero(&remainder) != CmpEQ) { + return ErrorExactDivRemainder; + } + } else { + bigfloat_div_trunc(&out_val->data.x_bigfloat, &op1_val->data.x_bigfloat, &op2_val->data.x_bigfloat); + BigFloat remainder; + bigfloat_rem(&remainder, &op1_val->data.x_bigfloat, &op2_val->data.x_bigfloat); + if (bigfloat_cmp_zero(&remainder) != CmpEQ) { + return ErrorExactDivRemainder; + } + } + break; case IrBinOpRemRem: - return ir_eval_bignum(op1_val, op2_val, out_val, bignum_rem, canon_type, EvalBigNumSpecialNone); + if (is_int) { + bigint_rem(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); + } else { + bigfloat_rem(&out_val->data.x_bigfloat, &op1_val->data.x_bigfloat, &op2_val->data.x_bigfloat); + } + break; case IrBinOpRemMod: - return ir_eval_bignum(op1_val, op2_val, out_val, bignum_mod, canon_type, EvalBigNumSpecialNone); + if (is_int) { + bigint_mod(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); + } else { + bigfloat_mod(&out_val->data.x_bigfloat, &op1_val->data.x_bigfloat, &op2_val->data.x_bigfloat); + } + break; } - zig_unreachable(); + + if (type_entry->id == TypeTableEntryIdInt) { + if (!bigint_fits_in_bits(&out_val->data.x_bigint, type_entry->data.integral.bit_count, + type_entry->data.integral.is_signed)) + { + return ErrorOverflow; + } + } + + out_val->type = type_entry; + out_val->special = ConstValSpecialStatic; + return 0; } static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) { @@ -8395,31 +8475,32 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp IrBinOp op_id = bin_op_instruction->op_id; bool is_int = resolved_type->id == TypeTableEntryIdInt || resolved_type->id == TypeTableEntryIdNumLitInt; - bool is_signed = ((resolved_type->id == TypeTableEntryIdInt && resolved_type->data.integral.is_signed) || - resolved_type->id == TypeTableEntryIdFloat || - (resolved_type->id == TypeTableEntryIdNumLitFloat && - (op1->value.data.x_bignum.data.x_float < 0.0 || op2->value.data.x_bignum.data.x_float < 0.0)) || - (resolved_type->id == TypeTableEntryIdNumLitInt && - (op1->value.data.x_bignum.is_negative || op2->value.data.x_bignum.is_negative))); - if (op_id == IrBinOpDivUnspecified) { - if (is_int && is_signed) { + bool is_float = resolved_type->id == TypeTableEntryIdFloat || resolved_type->id == TypeTableEntryIdNumLitFloat; + bool is_signed_div = ( + (resolved_type->id == TypeTableEntryIdInt && resolved_type->data.integral.is_signed) || + resolved_type->id == TypeTableEntryIdFloat || + (resolved_type->id == TypeTableEntryIdNumLitFloat && + ((bigfloat_cmp_zero(&op1->value.data.x_bigfloat) != CmpGT) != + (bigfloat_cmp_zero(&op2->value.data.x_bigfloat) != CmpGT))) || + (resolved_type->id == TypeTableEntryIdNumLitInt && + ((bigint_cmp_zero(&op1->value.data.x_bigint) != CmpGT) != + (bigint_cmp_zero(&op2->value.data.x_bigint) != CmpGT))) + ); + if (op_id == IrBinOpDivUnspecified && is_int) { + if (is_signed_div) { bool ok = false; if (instr_is_comptime(op1) && instr_is_comptime(op2)) { - if (op2->value.data.x_bignum.data.x_uint == 0) { + if (bigint_cmp_zero(&op2->value.data.x_bigint) == CmpEQ) { // the division by zero error will be caught later, but we don't have a // division function ambiguity problem. op_id = IrBinOpDivTrunc; ok = true; } else { - BigNum trunc_result; - BigNum floor_result; - if (bignum_div_trunc(&trunc_result, &op1->value.data.x_bignum, &op2->value.data.x_bignum)) { - zig_unreachable(); - } - if (bignum_div_floor(&floor_result, &op1->value.data.x_bignum, &op2->value.data.x_bignum)) { - zig_unreachable(); - } - if (bignum_cmp_eq(&trunc_result, &floor_result)) { + BigInt trunc_result; + BigInt floor_result; + bigint_div_trunc(&trunc_result, &op1->value.data.x_bigint, &op2->value.data.x_bigint); + bigint_div_floor(&floor_result, &op1->value.data.x_bigint, &op2->value.data.x_bigint); + if (bigint_cmp(&trunc_result, &floor_result) == CmpEQ) { ok = true; op_id = IrBinOpDivTrunc; } @@ -8432,29 +8513,37 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp buf_ptr(&op2->value.type->name))); return ira->codegen->builtin_types.entry_invalid; } - } else if (is_int) { + } else { op_id = IrBinOpDivTrunc; } } else if (op_id == IrBinOpRemUnspecified) { - if (is_signed) { + if (is_signed_div && (is_int || is_float)) { bool ok = false; if (instr_is_comptime(op1) && instr_is_comptime(op2)) { - if ((is_int && op2->value.data.x_bignum.data.x_uint == 0) || - (!is_int && op2->value.data.x_bignum.data.x_float == 0.0)) - { - // the division by zero error will be caught later, but we don't - // have a remainder function ambiguity problem - ok = true; + if (is_int) { + if (bigint_cmp_zero(&op2->value.data.x_bigint) == CmpEQ) { + // the division by zero error will be caught later, but we don't + // have a remainder function ambiguity problem + ok = true; + } else { + BigInt rem_result; + BigInt mod_result; + bigint_rem(&rem_result, &op1->value.data.x_bigint, &op2->value.data.x_bigint); + bigint_mod(&mod_result, &op1->value.data.x_bigint, &op2->value.data.x_bigint); + ok = bigint_cmp(&rem_result, &mod_result) == CmpEQ; + } } else { - BigNum rem_result; - BigNum mod_result; - if (bignum_rem(&rem_result, &op1->value.data.x_bignum, &op2->value.data.x_bignum)) { - zig_unreachable(); + if (bigfloat_cmp_zero(&op2->value.data.x_bigfloat) == CmpEQ) { + // the division by zero error will be caught later, but we don't + // have a remainder function ambiguity problem + ok = true; + } else { + BigFloat rem_result; + BigFloat mod_result; + bigfloat_rem(&rem_result, &op1->value.data.x_bigfloat, &op2->value.data.x_bigfloat); + bigfloat_mod(&mod_result, &op1->value.data.x_bigfloat, &op2->value.data.x_bigfloat); + ok = bigfloat_cmp(&rem_result, &mod_result) == CmpEQ; } - if (bignum_mod(&mod_result, &op1->value.data.x_bignum, &op2->value.data.x_bignum)) { - zig_unreachable(); - } - ok = bignum_cmp_eq(&rem_result, &mod_result); } } if (!ok) { @@ -8468,21 +8557,18 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp op_id = IrBinOpRemRem; } - if (resolved_type->id == TypeTableEntryIdInt || - resolved_type->id == TypeTableEntryIdNumLitInt) - { + if (is_int) { // int - } else if ((resolved_type->id == TypeTableEntryIdFloat || - resolved_type->id == TypeTableEntryIdNumLitFloat) && + } else if (is_float && (op_id == IrBinOpAdd || - op_id == IrBinOpSub || - op_id == IrBinOpMult || - op_id == IrBinOpDivUnspecified || - op_id == IrBinOpDivTrunc || - op_id == IrBinOpDivFloor || - op_id == IrBinOpDivExact || - op_id == IrBinOpRemRem || - op_id == IrBinOpRemMod)) + op_id == IrBinOpSub || + op_id == IrBinOpMult || + op_id == IrBinOpDivUnspecified || + op_id == IrBinOpDivTrunc || + op_id == IrBinOpDivFloor || + op_id == IrBinOpDivExact || + op_id == IrBinOpRemRem || + op_id == IrBinOpRemMod)) { // float } else { @@ -8494,6 +8580,18 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp return ira->codegen->builtin_types.entry_invalid; } + if (resolved_type->id == TypeTableEntryIdNumLitInt) { + if (op_id == IrBinOpBitShiftLeftWrap) { + op_id = IrBinOpBitShiftLeft; + } else if (op_id == IrBinOpAddWrap) { + op_id = IrBinOpAdd; + } else if (op_id == IrBinOpSubWrap) { + op_id = IrBinOpSub; + } else if (op_id == IrBinOpMultWrap) { + op_id = IrBinOpMult; + } + } + IrInstruction *casted_op1 = ir_implicit_cast(ira, op1, resolved_type); if (casted_op1 == ira->codegen->invalid_instruction) return ira->codegen->builtin_types.entry_invalid; @@ -8502,8 +8600,7 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp if (casted_op2 == ira->codegen->invalid_instruction) return ira->codegen->builtin_types.entry_invalid; - - if (casted_op1->value.special != ConstValSpecialRuntime && casted_op2->value.special != ConstValSpecialRuntime) { + if (instr_is_comptime(casted_op1) && instr_is_comptime(casted_op2)) { ConstExprValue *op1_val = &casted_op1->value; ConstExprValue *op2_val = &casted_op2->value; ConstExprValue *out_val = &bin_op_instruction->base.value; @@ -8704,17 +8801,17 @@ static TypeTableEntry *ir_analyze_array_mult(IrAnalyze *ira, IrInstructionBinOp } uint64_t old_array_len = array_type->data.array.len; + uint64_t new_array_len; - BigNum array_len; - bignum_init_unsigned(&array_len, old_array_len); - if (bignum_multiply_by_scalar(&array_len, mult_amt)) { + if (__builtin_umulll_overflow((unsigned long long)old_array_len, (unsigned long long)mult_amt, + (unsigned long long*)&new_array_len)) + { ir_add_error(ira, &instruction->base, buf_sprintf("operation results in overflow")); return ira->codegen->builtin_types.entry_invalid; } ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); - uint64_t new_array_len = array_len.data.x_uint; out_val->data.x_array.s_none.elements = create_const_vals(new_array_len); expand_undef_array(ira->codegen, array_val); @@ -9581,9 +9678,10 @@ static TypeTableEntry *ir_analyze_negation(IrAnalyze *ira, IrInstructionUnOp *un bool is_wrap_op = (un_op_instruction->op_id == IrUnOpNegationWrap); + bool is_float = (expr_type->id == TypeTableEntryIdFloat || expr_type->id == TypeTableEntryIdNumLitFloat); + if ((expr_type->id == TypeTableEntryIdInt && expr_type->data.integral.is_signed) || - expr_type->id == TypeTableEntryIdNumLitInt || - ((expr_type->id == TypeTableEntryIdFloat || expr_type->id == TypeTableEntryIdNumLitFloat) && !is_wrap_op)) + expr_type->id == TypeTableEntryIdNumLitInt || (is_float && !is_wrap_op)) { if (instr_is_comptime(value)) { ConstExprValue *target_const_val = ir_resolve_const(ira, value, UndefBad); @@ -9591,19 +9689,19 @@ static TypeTableEntry *ir_analyze_negation(IrAnalyze *ira, IrInstructionUnOp *un return ira->codegen->builtin_types.entry_invalid; ConstExprValue *out_val = ir_build_const_from(ira, &un_op_instruction->base); - bignum_negate(&out_val->data.x_bignum, &target_const_val->data.x_bignum); - if (expr_type->id == TypeTableEntryIdFloat || - expr_type->id == TypeTableEntryIdNumLitFloat || - expr_type->id == TypeTableEntryIdNumLitInt) - { + if (is_float) { + bigfloat_negate(&out_val->data.x_bigfloat, &target_const_val->data.x_bigfloat); + } else if (is_wrap_op) { + bigint_negate_wrap(&out_val->data.x_bigint, &target_const_val->data.x_bigint, + expr_type->data.integral.bit_count); + } else { + bigint_negate(&out_val->data.x_bigint, &target_const_val->data.x_bigint); + } + if (is_wrap_op || is_float || expr_type->id == TypeTableEntryIdNumLitInt) { return expr_type; } - bool overflow = !bignum_fits_in_bits(&out_val->data.x_bignum, expr_type->data.integral.bit_count, true); - if (is_wrap_op) { - if (overflow) - out_val->data.x_bignum.is_negative = true; - } else if (overflow) { + if (!bigint_fits_in_bits(&out_val->data.x_bigint, expr_type->data.integral.bit_count, true)) { ir_add_error(ira, &un_op_instruction->base, buf_sprintf("negation caused overflow")); return ira->codegen->builtin_types.entry_invalid; } @@ -9632,7 +9730,7 @@ static TypeTableEntry *ir_analyze_bin_not(IrAnalyze *ira, IrInstructionUnOp *ins return ira->codegen->builtin_types.entry_invalid; ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); - bignum_not(&out_val->data.x_bignum, &target_const_val->data.x_bignum, + bigint_not(&out_val->data.x_bigint, &target_const_val->data.x_bigint, expr_type->data.integral.bit_count, expr_type->data.integral.is_signed); return expr_type; } @@ -9887,12 +9985,12 @@ static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruc return_type = get_pointer_to_type_extra(ira->codegen, child_type, ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, 0, 0); } else { - ConstExprValue *elem_val = ir_resolve_const(ira, elem_index, UndefBad); - if (!elem_val) + uint64_t elem_val_scalar; + if (!ir_resolve_usize(ira, elem_index, &elem_val_scalar)) return ira->codegen->builtin_types.entry_invalid; size_t bit_width = type_size_bits(ira->codegen, child_type); - size_t bit_offset = bit_width * elem_val->data.x_bignum.data.x_uint; + size_t bit_offset = bit_width * elem_val_scalar; return_type = get_pointer_to_type_extra(ira->codegen, child_type, ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, @@ -9909,10 +10007,10 @@ static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruc ConstExprValue *args_val = const_ptr_pointee(ira->codegen, ptr_val); size_t start = args_val->data.x_arg_tuple.start_index; size_t end = args_val->data.x_arg_tuple.end_index; - ConstExprValue *elem_index_val = ir_resolve_const(ira, elem_index, UndefBad); - if (!elem_index_val) + uint64_t elem_index_val; + if (!ir_resolve_usize(ira, elem_index, &elem_index_val)) return ira->codegen->builtin_types.entry_invalid; - size_t index = bignum_to_twos_complement(&elem_index_val->data.x_bignum); + size_t index = elem_index_val; size_t len = end - start; if (index >= len) { ir_add_error(ira, &elem_ptr_instruction->base, @@ -9945,7 +10043,7 @@ static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruc bool safety_check_on = elem_ptr_instruction->safety_check_on; if (instr_is_comptime(casted_elem_index)) { - uint64_t index = casted_elem_index->value.data.x_bignum.data.x_uint; + uint64_t index = bigint_as_unsigned(&casted_elem_index->value.data.x_bigint); if (array_type->id == TypeTableEntryIdArray) { uint64_t array_len = array_type->data.array.len; if (index >= array_len) { @@ -10021,7 +10119,7 @@ static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruc } ConstExprValue *len_field = &array_ptr_val->data.x_struct.fields[slice_len_index]; ConstExprValue *out_val = ir_build_const_from(ira, &elem_ptr_instruction->base); - uint64_t slice_len = len_field->data.x_bignum.data.x_uint; + uint64_t slice_len = bigint_as_unsigned(&len_field->data.x_bigint); if (index >= slice_len) { ir_add_error_node(ira, elem_ptr_instruction->base.source_node, buf_sprintf("index %" ZIG_PRI_u64 " outside slice of size %" ZIG_PRI_u64, @@ -11107,7 +11205,7 @@ static TypeTableEntry *ir_analyze_instruction_size_of(IrAnalyze *ira, { uint64_t size_in_bytes = type_size(ira->codegen, type_entry); ConstExprValue *out_val = ir_build_const_from(ira, &size_of_instruction->base); - bignum_init_unsigned(&out_val->data.x_bignum, size_in_bytes); + bigint_init_unsigned(&out_val->data.x_bigint, size_in_bytes); return ira->codegen->builtin_types.entry_num_lit_int; } } @@ -11213,10 +11311,10 @@ static TypeTableEntry *ir_analyze_instruction_ctz(IrAnalyze *ira, IrInstructionC return ira->codegen->builtin_types.entry_invalid; } else if (value->value.type->id == TypeTableEntryIdInt) { if (value->value.special != ConstValSpecialRuntime) { - uint32_t result = bignum_ctz(&value->value.data.x_bignum, + size_t result = bigint_ctz(&value->value.data.x_bigint, value->value.type->data.integral.bit_count); ConstExprValue *out_val = ir_build_const_from(ira, &ctz_instruction->base); - bignum_init_unsigned(&out_val->data.x_bignum, result); + bigint_init_unsigned(&out_val->data.x_bigint, result); return value->value.type; } @@ -11235,10 +11333,10 @@ static TypeTableEntry *ir_analyze_instruction_clz(IrAnalyze *ira, IrInstructionC return ira->codegen->builtin_types.entry_invalid; } else if (value->value.type->id == TypeTableEntryIdInt) { if (value->value.special != ConstValSpecialRuntime) { - uint32_t result = bignum_clz(&value->value.data.x_bignum, + size_t result = bigint_clz(&value->value.data.x_bigint, value->value.type->data.integral.bit_count); ConstExprValue *out_val = ir_build_const_from(ira, &clz_instruction->base); - bignum_init_unsigned(&out_val->data.x_bignum, result); + bigint_init_unsigned(&out_val->data.x_bigint, result); return value->value.type; } @@ -11272,7 +11370,7 @@ static IrInstruction *ir_analyze_enum_tag(IrAnalyze *ira, IrInstruction *source_ source_instr->scope, source_instr->source_node); const_instruction->base.value.type = tag_type; const_instruction->base.value.special = ConstValSpecialStatic; - bignum_init_unsigned(&const_instruction->base.value.data.x_bignum, val->data.x_enum.tag); + bigint_init_unsigned(&const_instruction->base.value.data.x_bigint, val->data.x_enum.tag); return &const_instruction->base; } @@ -11441,7 +11539,7 @@ static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira, TypeTableEntry *tag_type = target_type->data.enumeration.tag_type; if (pointee_val) { ConstExprValue *out_val = ir_build_const_from(ira, &switch_target_instruction->base); - bignum_init_unsigned(&out_val->data.x_bignum, pointee_val->data.x_enum.tag); + bigint_init_unsigned(&out_val->data.x_bigint, pointee_val->data.x_enum.tag); return tag_type; } @@ -11490,9 +11588,9 @@ static TypeTableEntry *ir_analyze_instruction_switch_var(IrAnalyze *ira, IrInstr if (!prong_val) return ira->codegen->builtin_types.entry_invalid; - TypeEnumField *field = &target_type->data.enumeration.fields[prong_val->data.x_bignum.data.x_uint]; + TypeEnumField *field; if (prong_value->value.type->id == TypeTableEntryIdEnumTag) { - field = &target_type->data.enumeration.fields[prong_val->data.x_bignum.data.x_uint]; + field = &target_type->data.enumeration.fields[bigint_as_unsigned(&prong_val->data.x_bigint)]; } else if (prong_value->value.type->id == TypeTableEntryIdEnum) { field = &target_type->data.enumeration.fields[prong_val->data.x_enum.tag]; } else { @@ -11619,7 +11717,7 @@ static TypeTableEntry *ir_analyze_instruction_array_len(IrAnalyze *ira, ConstExprValue *len_val = &array_value->value.data.x_struct.fields[slice_len_index]; if (len_val->special != ConstValSpecialRuntime) { return ir_analyze_const_usize(ira, &array_len_instruction->base, - len_val->data.x_bignum.data.x_uint); + bigint_as_unsigned(&len_val->data.x_bigint)); } } TypeStructField *field = &type_entry->data.structure.fields[slice_len_index]; @@ -11866,7 +11964,7 @@ static TypeTableEntry *ir_analyze_instruction_container_init_list(IrAnalyze *ira TypeTableEntry *enum_type = container_type_value->value.type->data.enum_tag.enum_type; - uint64_t tag_uint = tag_value->data.x_bignum.data.x_uint; + uint64_t tag_uint = bigint_as_unsigned(&tag_value->data.x_bigint); TypeEnumField *field = &enum_type->data.enumeration.fields[tag_uint]; TypeTableEntry *this_field_type = field->type_entry; @@ -12063,7 +12161,7 @@ static TypeTableEntry *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrIn if (instr_is_comptime(target)) { TypeTableEntry *enum_type = target->value.type->data.enum_tag.enum_type; - uint64_t tag_value = target->value.data.x_bignum.data.x_uint; + uint64_t tag_value = bigint_as_unsigned(&target->value.data.x_bigint); TypeEnumField *field = &enum_type->data.enumeration.fields[tag_value]; ConstExprValue *array_val = create_const_str_lit(ira->codegen, field->name); ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); @@ -12197,7 +12295,7 @@ static TypeTableEntry *ir_analyze_instruction_offset_of(IrAnalyze *ira, size_t byte_offset = LLVMOffsetOfElement(ira->codegen->target_data_ref, container_type->type_ref, field->gen_index); ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); - bignum_init_unsigned(&out_val->data.x_bignum, byte_offset); + bigint_init_unsigned(&out_val->data.x_bigint, byte_offset); return ira->codegen->builtin_types.entry_num_lit_int; } @@ -12506,8 +12604,8 @@ static TypeTableEntry *ir_analyze_instruction_truncate(IrAnalyze *ira, IrInstruc if (target->value.special == ConstValSpecialStatic) { ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); - bignum_init_bignum(&out_val->data.x_bignum, &target->value.data.x_bignum); - bignum_truncate(&out_val->data.x_bignum, dest_type->data.integral.bit_count); + bigint_truncate(&out_val->data.x_bigint, &target->value.data.x_bigint, dest_type->data.integral.bit_count, + dest_type->data.integral.is_signed); return dest_type; } @@ -12619,7 +12717,7 @@ static TypeTableEntry *ir_analyze_instruction_memset(IrAnalyze *ira, IrInstructi zig_unreachable(); } - size_t count = casted_count->value.data.x_bignum.data.x_uint; + size_t count = bigint_as_unsigned(&casted_count->value.data.x_bigint); size_t end = start + count; if (end > bound_end) { ir_add_error(ira, count_value, buf_sprintf("out of bounds pointer access")); @@ -12681,7 +12779,7 @@ static TypeTableEntry *ir_analyze_instruction_memcpy(IrAnalyze *ira, IrInstructi casted_count->value.special == ConstValSpecialStatic && casted_dest_ptr->value.data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { - size_t count = casted_count->value.data.x_bignum.data.x_uint; + size_t count = bigint_as_unsigned(&casted_count->value.data.x_bigint); ConstExprValue *dest_ptr_val = &casted_dest_ptr->value; ConstExprValue *dest_elements; @@ -12868,21 +12966,21 @@ static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructio case ConstPtrSpecialBaseArray: array_val = parent_ptr->data.x_ptr.data.base_array.array_val; abs_offset = parent_ptr->data.x_ptr.data.base_array.elem_index; - rel_end = len_val->data.x_bignum.data.x_uint; + rel_end = bigint_as_unsigned(&len_val->data.x_bigint); break; case ConstPtrSpecialBaseStruct: zig_panic("TODO slice const inner struct"); case ConstPtrSpecialHardCodedAddr: array_val = nullptr; abs_offset = 0; - rel_end = len_val->data.x_bignum.data.x_uint; + rel_end = bigint_as_unsigned(&len_val->data.x_bigint); break; } } else { zig_unreachable(); } - uint64_t start_scalar = casted_start->value.data.x_bignum.data.x_uint; + uint64_t start_scalar = bigint_as_unsigned(&casted_start->value.data.x_bigint); if (start_scalar > rel_end) { ir_add_error(ira, &instruction->base, buf_sprintf("out of bounds slice")); return ira->codegen->builtin_types.entry_invalid; @@ -12890,7 +12988,7 @@ static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructio uint64_t end_scalar; if (end) { - end_scalar = end->value.data.x_bignum.data.x_uint; + end_scalar = bigint_as_unsigned(&end->value.data.x_bigint); } else { end_scalar = rel_end; } @@ -12970,7 +13068,7 @@ static TypeTableEntry *ir_analyze_instruction_member_count(IrAnalyze *ira, IrIns } ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); - bignum_init_unsigned(&out_val->data.x_bignum, result); + bigint_init_unsigned(&out_val->data.x_bigint, result); return ira->codegen->builtin_types.entry_num_lit_int; } @@ -13011,7 +13109,7 @@ static TypeTableEntry *ir_analyze_instruction_alignof(IrAnalyze *ira, IrInstruct } else { uint64_t align_in_bytes = LLVMABIAlignmentOfType(ira->codegen->target_data_ref, type_entry->type_ref); ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); - bignum_init_unsigned(&out_val->data.x_bignum, align_in_bytes); + bigint_init_unsigned(&out_val->data.x_bigint, align_in_bytes); return ira->codegen->builtin_types.entry_num_lit_int; } } @@ -13060,29 +13158,32 @@ static TypeTableEntry *ir_analyze_instruction_overflow_op(IrAnalyze *ira, IrInst casted_result_ptr->value.special == ConstValSpecialStatic) { ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); - BigNum *op1_bignum = &casted_op1->value.data.x_bignum; - BigNum *op2_bignum = &casted_op2->value.data.x_bignum; + BigInt *op1_bigint = &casted_op1->value.data.x_bigint; + BigInt *op2_bigint = &casted_op2->value.data.x_bigint; ConstExprValue *pointee_val = const_ptr_pointee(ira->codegen, &casted_result_ptr->value); - BigNum *dest_bignum = &pointee_val->data.x_bignum; + BigInt *dest_bigint = &pointee_val->data.x_bigint; switch (instruction->op) { case IrOverflowOpAdd: - out_val->data.x_bool = bignum_add(dest_bignum, op1_bignum, op2_bignum); + bigint_add(dest_bigint, op1_bigint, op2_bigint); break; case IrOverflowOpSub: - out_val->data.x_bool = bignum_sub(dest_bignum, op1_bignum, op2_bignum); + bigint_sub(dest_bigint, op1_bigint, op2_bigint); break; case IrOverflowOpMul: - out_val->data.x_bool = bignum_mul(dest_bignum, op1_bignum, op2_bignum); + bigint_mul(dest_bigint, op1_bigint, op2_bigint); break; case IrOverflowOpShl: - out_val->data.x_bool = bignum_shl(dest_bignum, op1_bignum, op2_bignum); + bigint_shl(dest_bigint, op1_bigint, op2_bigint); break; } - if (!bignum_fits_in_bits(dest_bignum, dest_type->data.integral.bit_count, + if (!bigint_fits_in_bits(dest_bigint, dest_type->data.integral.bit_count, dest_type->data.integral.is_signed)) { out_val->data.x_bool = true; - bignum_truncate(dest_bignum, dest_type->data.integral.bit_count); + BigInt tmp_bigint; + bigint_init_bigint(&tmp_bigint, dest_bigint); + bigint_truncate(dest_bigint, &tmp_bigint, dest_type->data.integral.bit_count, + dest_type->data.integral.is_signed); } pointee_val->special = ConstValSpecialStatic; return ira->codegen->builtin_types.entry_bool; @@ -13301,14 +13402,14 @@ static TypeTableEntry *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira size_t start_index; size_t end_index; if (start_value->value.type->id == TypeTableEntryIdEnumTag) { - start_index = start_value->value.data.x_bignum.data.x_uint; + start_index = bigint_as_unsigned(&start_value->value.data.x_bigint); } else if (start_value->value.type->id == TypeTableEntryIdEnum) { start_index = start_value->value.data.x_enum.tag; } else { zig_unreachable(); } if (end_value->value.type->id == TypeTableEntryIdEnumTag) { - end_index = end_value->value.data.x_bignum.data.x_uint; + end_index = bigint_as_unsigned(&end_value->value.data.x_bigint); } else if (end_value->value.type->id == TypeTableEntryIdEnum) { end_index = end_value->value.data.x_enum.tag; } else { @@ -13357,7 +13458,7 @@ static TypeTableEntry *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira if (!end_val) return ira->codegen->builtin_types.entry_invalid; - AstNode *prev_node = rangeset_add_range(&rs, &start_val->data.x_bignum, &end_val->data.x_bignum, + AstNode *prev_node = rangeset_add_range(&rs, &start_val->data.x_bigint, &end_val->data.x_bigint, start_value->source_node); if (prev_node != nullptr) { ErrorMsg *msg = ir_add_error(ira, start_value, buf_sprintf("duplicate switch value")); @@ -13366,9 +13467,9 @@ static TypeTableEntry *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira } } if (!instruction->have_else_prong) { - BigNum min_val; + BigInt min_val; eval_min_max_value_int(ira->codegen, switch_type, &min_val, false); - BigNum max_val; + BigInt max_val; eval_min_max_value_int(ira->codegen, switch_type, &max_val, true); if (!rangeset_spans(&rs, &min_val, &max_val)) { ir_add_error(ira, &instruction->base, buf_sprintf("switch must handle all possibilities")); @@ -13503,16 +13604,18 @@ static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue buf[0] = val->data.x_bool ? 1 : 0; return; case TypeTableEntryIdInt: - bignum_write_twos_complement(&val->data.x_bignum, buf, val->type->data.integral.bit_count, codegen->is_big_endian); + bigint_write_twos_complement(&val->data.x_bigint, buf, val->type->data.integral.bit_count, + codegen->is_big_endian); return; case TypeTableEntryIdFloat: - bignum_write_ieee597(&val->data.x_bignum, buf, val->type->data.floating.bit_count, codegen->is_big_endian); + bigfloat_write_ieee597(&val->data.x_bigfloat, buf, val->type->data.floating.bit_count, + codegen->is_big_endian); return; case TypeTableEntryIdPointer: if (val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr) { - BigNum bn; - bignum_init_unsigned(&bn, val->data.x_ptr.data.hard_coded_addr.addr); - bignum_write_twos_complement(&bn, buf, codegen->builtin_types.entry_usize->data.integral.bit_count, codegen->is_big_endian); + BigInt bn; + bigint_init_unsigned(&bn, val->data.x_ptr.data.hard_coded_addr.addr); + bigint_write_twos_complement(&bn, buf, codegen->builtin_types.entry_usize->data.integral.bit_count, codegen->is_big_endian); return; } else { zig_unreachable(); @@ -13562,18 +13665,20 @@ static void buf_read_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue val->data.x_bool = (buf[0] != 0); return; case TypeTableEntryIdInt: - bignum_read_twos_complement(&val->data.x_bignum, buf, val->type->data.integral.bit_count, codegen->is_big_endian, - val->type->data.integral.is_signed); + bigint_read_twos_complement(&val->data.x_bigint, buf, val->type->data.integral.bit_count, + codegen->is_big_endian, val->type->data.integral.is_signed); return; case TypeTableEntryIdFloat: - bignum_read_ieee597(&val->data.x_bignum, buf, val->type->data.floating.bit_count, codegen->is_big_endian); + bigfloat_read_ieee597(&val->data.x_bigfloat, buf, val->type->data.floating.bit_count, + codegen->is_big_endian); return; case TypeTableEntryIdPointer: { val->data.x_ptr.special = ConstPtrSpecialHardCodedAddr; - BigNum bn; - bignum_read_twos_complement(&bn, buf, codegen->builtin_types.entry_usize->data.integral.bit_count, codegen->is_big_endian, false); - val->data.x_ptr.data.hard_coded_addr.addr = bignum_to_twos_complement(&bn); + BigInt bn; + bigint_read_twos_complement(&bn, buf, codegen->builtin_types.entry_usize->data.integral.bit_count, + codegen->is_big_endian, false); + val->data.x_ptr.data.hard_coded_addr.addr = bigint_as_unsigned(&bn); return; } case TypeTableEntryIdArray: @@ -13729,7 +13834,7 @@ static TypeTableEntry *ir_analyze_instruction_int_to_ptr(IrAnalyze *ira, IrInstr ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->data.x_ptr.special = ConstPtrSpecialHardCodedAddr; - out_val->data.x_ptr.data.hard_coded_addr.addr = bignum_to_twos_complement(&val->data.x_bignum); + out_val->data.x_ptr.data.hard_coded_addr.addr = bigint_as_unsigned(&val->data.x_bigint); return dest_type; } diff --git a/src/os.hpp b/src/os.hpp index f137baebfb..0a529fcbbd 100644 --- a/src/os.hpp +++ b/src/os.hpp @@ -13,6 +13,7 @@ #include "error.hpp" #include +#include enum TerminationId { TerminationIdClean, diff --git a/src/parser.cpp b/src/parser.cpp index 312e5c3418..ff2e76ac02 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -186,9 +186,14 @@ static Buf *token_buf(Token *token) { return &token->data.str_lit.str; } -static BigNum *token_bignum(Token *token) { - assert(token->id == TokenIdNumberLiteral); - return &token->data.num_lit.bignum; +static BigInt *token_bigint(Token *token) { + assert(token->id == TokenIdIntLiteral); + return &token->data.int_lit.bigint; +} + +static BigFloat *token_bigfloat(Token *token) { + assert(token->id == TokenIdFloatLiteral); + return &token->data.float_lit.bigfloat; } static uint8_t token_char_lit(Token *token) { @@ -660,16 +665,21 @@ static AstNode *ast_parse_comptime_expr(ParseContext *pc, size_t *token_index, b } /* -PrimaryExpression = Number | String | CharLiteral | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | (option("extern") FnProto) | AsmExpression | ("error" "." Symbol) | ContainerDecl +PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | (option("extern") FnProto) | AsmExpression | ("error" "." Symbol) | ContainerDecl KeywordLiteral = "true" | "false" | "null" | "continue" | "undefined" | "error" | "this" | "unreachable" */ static AstNode *ast_parse_primary_expr(ParseContext *pc, size_t *token_index, bool mandatory) { Token *token = &pc->tokens->at(*token_index); - if (token->id == TokenIdNumberLiteral) { - AstNode *node = ast_create_node(pc, NodeTypeNumberLiteral, token); - node->data.number_literal.bignum = token_bignum(token); - node->data.number_literal.overflow = token->data.num_lit.overflow; + if (token->id == TokenIdIntLiteral) { + AstNode *node = ast_create_node(pc, NodeTypeIntLiteral, token); + node->data.int_literal.bigint = token_bigint(token); + *token_index += 1; + return node; + } else if (token->id == TokenIdFloatLiteral) { + AstNode *node = ast_create_node(pc, NodeTypeFloatLiteral, token); + node->data.float_literal.bigfloat = token_bigfloat(token); + node->data.float_literal.overflow = token->data.float_lit.overflow; *token_index += 1; return node; } else if (token->id == TokenIdStringLiteral) { @@ -2629,7 +2639,10 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont visit_field(&node->data.unwrap_err_expr.symbol, visit, context); visit_field(&node->data.unwrap_err_expr.op2, visit, context); break; - case NodeTypeNumberLiteral: + case NodeTypeIntLiteral: + // none + break; + case NodeTypeFloatLiteral: // none break; case NodeTypeStringLiteral: diff --git a/src/range_set.cpp b/src/range_set.cpp index 6158f52a00..83f34d505a 100644 --- a/src/range_set.cpp +++ b/src/range_set.cpp @@ -1,11 +1,11 @@ #include "range_set.hpp" -AstNode *rangeset_add_range(RangeSet *rs, BigNum *first, BigNum *last, AstNode *source_node) { +AstNode *rangeset_add_range(RangeSet *rs, BigInt *first, BigInt *last, AstNode *source_node) { for (size_t i = 0; i < rs->src_range_list.length; i += 1) { RangeWithSrc *range_with_src = &rs->src_range_list.at(i); Range *range = &range_with_src->range; - if ((bignum_cmp_gte(first, &range->first) && bignum_cmp_lte(first, &range->last)) || - (bignum_cmp_gte(last, &range->first) && bignum_cmp_lte(last, &range->last))) + if ((bigint_cmp(first, &range->first) != CmpLT && bigint_cmp(first, &range->last) != CmpGT) || + (bigint_cmp(last, &range->first) != CmpLT && bigint_cmp(last, &range->last) != CmpGT)) { return range_with_src->source_node; } @@ -16,24 +16,22 @@ AstNode *rangeset_add_range(RangeSet *rs, BigNum *first, BigNum *last, AstNode * } -static bool add_range(ZigList *list, Range *new_range, BigNum *one) { +static bool add_range(ZigList *list, Range *new_range, BigInt *one) { for (size_t i = 0; i < list->length; i += 1) { Range *range = &list->at(i); - BigNum first_minus_one; - if (bignum_sub(&first_minus_one, &range->first, one)) - zig_unreachable(); + BigInt first_minus_one; + bigint_sub(&first_minus_one, &range->first, one); - if (bignum_cmp_eq(&new_range->last, &first_minus_one)) { + if (bigint_cmp(&new_range->last, &first_minus_one) == CmpEQ) { range->first = new_range->first; return true; } - BigNum last_plus_one; - if (bignum_add(&last_plus_one, &range->last, one)) - zig_unreachable(); + BigInt last_plus_one; + bigint_add(&last_plus_one, &range->last, one); - if (bignum_cmp_eq(&new_range->first, &last_plus_one)) { + if (bigint_cmp(&new_range->first, &last_plus_one) == CmpEQ) { range->last = new_range->last; return true; } @@ -42,7 +40,7 @@ static bool add_range(ZigList *list, Range *new_range, BigNum *one) { return false; } -bool rangeset_spans(RangeSet *rs, BigNum *first, BigNum *last) { +bool rangeset_spans(RangeSet *rs, BigInt *first, BigInt *last) { ZigList cur_list_value = {0}; ZigList other_list_value = {0}; ZigList *cur_list = &cur_list_value; @@ -54,8 +52,8 @@ bool rangeset_spans(RangeSet *rs, BigNum *first, BigNum *last) { cur_list->append({range->first, range->last}); } - BigNum one; - bignum_init_unsigned(&one, 1); + BigInt one; + bigint_init_unsigned(&one, 1); bool changes_made = true; while (changes_made) { @@ -73,9 +71,9 @@ bool rangeset_spans(RangeSet *rs, BigNum *first, BigNum *last) { if (cur_list->length != 1) return false; Range *range = &cur_list->at(0); - if (bignum_cmp_neq(&range->first, first)) + if (bigint_cmp(&range->first, first) != CmpEQ) return false; - if (bignum_cmp_neq(&range->last, last)) + if (bigint_cmp(&range->last, last) != CmpEQ) return false; return true; } diff --git a/src/range_set.hpp b/src/range_set.hpp index f3ae4a189f..9164a8b5c0 100644 --- a/src/range_set.hpp +++ b/src/range_set.hpp @@ -11,8 +11,8 @@ #include "all_types.hpp" struct Range { - BigNum first; - BigNum last; + BigInt first; + BigInt last; }; struct RangeWithSrc { @@ -24,7 +24,7 @@ struct RangeSet { ZigList src_range_list; }; -AstNode *rangeset_add_range(RangeSet *rs, BigNum *first, BigNum *last, AstNode *source_node); -bool rangeset_spans(RangeSet *rs, BigNum *first, BigNum *last); +AstNode *rangeset_add_range(RangeSet *rs, BigInt *first, BigInt *last, AstNode *source_node); +bool rangeset_spans(RangeSet *rs, BigInt *first, BigInt *last); #endif diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 7853d7f74c..071c87d9d3 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -225,13 +225,13 @@ struct Tokenize { uint32_t radix; int32_t exp_add_amt; bool is_exp_negative; - bool is_num_lit_float; size_t char_code_index; size_t char_code_end; bool unicode; uint32_t char_code; int exponent_in_bin_or_dec; - BigNum specified_exponent; + BigInt specified_exponent; + BigInt significand; }; __attribute__ ((format (printf, 2, 3))) @@ -255,8 +255,11 @@ static void tokenize_error(Tokenize *t, const char *format, ...) { static void set_token_id(Tokenize *t, Token *token, TokenId id) { token->id = id; - if (id == TokenIdNumberLiteral) { - token->data.num_lit.overflow = false; + if (id == TokenIdIntLiteral) { + bigint_init_unsigned(&token->data.int_lit.bigint, 0); + } else if (id == TokenIdFloatLiteral) { + bigfloat_init_float(&token->data.float_lit.bigfloat, 0.0); + token->data.float_lit.overflow = false; } else if (id == TokenIdStringLiteral || id == TokenIdSymbol) { memset(&token->data.str_lit.str, 0, sizeof(Buf)); buf_resize(&token->data.str_lit.str, 0); @@ -283,34 +286,40 @@ static void cancel_token(Tokenize *t) { } static void end_float_token(Tokenize *t) { - t->cur_tok->data.num_lit.bignum.kind = BigNumKindFloat; - if (t->radix == 10) { - char *str_begin = buf_ptr(t->buf) + t->cur_tok->start_pos; - char *str_end; - errno = 0; - t->cur_tok->data.num_lit.bignum.data.x_float = strtod(str_begin, &str_end); - if (errno) { - t->cur_tok->data.num_lit.overflow = true; - return; + uint8_t *ptr_buf = (uint8_t*)buf_ptr(t->buf) + t->cur_tok->start_pos; + size_t buf_len = t->cur_tok->end_pos - t->cur_tok->start_pos; + if (bigfloat_init_buf_base10(&t->cur_tok->data.float_lit.bigfloat, ptr_buf, buf_len)) { + t->cur_tok->data.float_lit.overflow = true; } - assert(str_end <= buf_ptr(t->buf) + t->cur_tok->end_pos); return; } + BigInt int_max; + bigint_init_unsigned(&int_max, INT_MAX); - if (t->specified_exponent.data.x_uint >= INT_MAX) { - t->cur_tok->data.num_lit.overflow = true; + if (bigint_cmp(&t->specified_exponent, &int_max) != CmpLT) { + t->cur_tok->data.float_lit.overflow = true; return; } - int64_t specified_exponent = t->specified_exponent.data.x_uint; + if (!bigint_fits_in_bits(&t->specified_exponent, 64, true)) { + t->cur_tok->data.float_lit.overflow = true; + return; + } + + int64_t specified_exponent = bigint_as_signed(&t->specified_exponent); if (t->is_exp_negative) { specified_exponent = -specified_exponent; } t->exponent_in_bin_or_dec = (int)(t->exponent_in_bin_or_dec + specified_exponent); - uint64_t significand = t->cur_tok->data.num_lit.bignum.data.x_uint; + if (!bigint_fits_in_bits(&t->significand, 64, false)) { + t->cur_tok->data.float_lit.overflow = true; + return; + } + + uint64_t significand = bigint_as_unsigned(&t->significand); uint64_t significand_bits; uint64_t exponent_bits; if (significand == 0) { @@ -325,7 +334,7 @@ static void end_float_token(Tokenize *t) { int significand_magnitude_in_bin = __builtin_clzll(1) - __builtin_clzll(significand); t->exponent_in_bin_or_dec += significand_magnitude_in_bin; if (!(-1023 <= t->exponent_in_bin_or_dec && t->exponent_in_bin_or_dec < 1023)) { - t->cur_tok->data.num_lit.overflow = true; + t->cur_tok->data.float_lit.overflow = true; return; } else { // this should chop off exactly one 1 bit from the top. @@ -335,20 +344,17 @@ static void end_float_token(Tokenize *t) { } } uint64_t double_bits = (exponent_bits << 52) | significand_bits; - safe_memcpy(&t->cur_tok->data.num_lit.bignum.data.x_float, (double *)&double_bits, 1); + double dbl_value; + safe_memcpy(&dbl_value, (double *)&double_bits, 1); + bigfloat_init_float(&t->cur_tok->data.float_lit.bigfloat, dbl_value); } static void end_token(Tokenize *t) { assert(t->cur_tok); t->cur_tok->end_pos = t->pos + 1; - if (t->cur_tok->id == TokenIdNumberLiteral) { - if (t->cur_tok->data.num_lit.overflow) { - return; - } - if (t->is_num_lit_float) { - end_float_token(t); - } + if (t->cur_tok->id == TokenIdFloatLiteral) { + end_float_token(t); } else if (t->cur_tok->id == TokenIdSymbol) { char *token_mem = buf_ptr(t->buf) + t->cur_tok->start_pos; int token_len = (int)(t->cur_tok->end_pos - t->cur_tok->start_pos); @@ -428,23 +434,21 @@ void tokenize(Buf *buf, Tokenization *out) { break; case '0': t.state = TokenizeStateZero; - begin_token(&t, TokenIdNumberLiteral); + begin_token(&t, TokenIdIntLiteral); t.radix = 10; t.exp_add_amt = 1; t.exponent_in_bin_or_dec = 0; - t.is_num_lit_float = false; - bignum_init_unsigned(&t.cur_tok->data.num_lit.bignum, 0); - bignum_init_unsigned(&t.specified_exponent, 0); + bigint_init_unsigned(&t.cur_tok->data.int_lit.bigint, 0); + bigint_init_unsigned(&t.specified_exponent, 0); break; case DIGIT_NON_ZERO: t.state = TokenizeStateNumber; - begin_token(&t, TokenIdNumberLiteral); + begin_token(&t, TokenIdIntLiteral); t.radix = 10; t.exp_add_amt = 1; t.exponent_in_bin_or_dec = 0; - t.is_num_lit_float = false; - bignum_init_unsigned(&t.cur_tok->data.num_lit.bignum, get_digit_value(c)); - bignum_init_unsigned(&t.specified_exponent, 0); + bigint_init_unsigned(&t.cur_tok->data.int_lit.bigint, get_digit_value(c)); + bigint_init_unsigned(&t.specified_exponent, 0); break; case '"': begin_token(&t, TokenIdStringLiteral); @@ -1182,7 +1186,9 @@ void tokenize(Buf *buf, Tokenization *out) { } if (is_exponent_signifier(c, t.radix)) { t.state = TokenizeStateFloatExponentUnsigned; - t.is_num_lit_float = true; + assert(t.cur_tok->id == TokenIdIntLiteral); + bigint_init_bigint(&t.significand, &t.cur_tok->data.int_lit.bigint); + set_token_id(&t, t.cur_tok, TokenIdFloatLiteral); break; } uint32_t digit_value = get_digit_value(c); @@ -1196,23 +1202,33 @@ void tokenize(Buf *buf, Tokenization *out) { t.state = TokenizeStateStart; continue; } - t.cur_tok->data.num_lit.overflow = t.cur_tok->data.num_lit.overflow || - bignum_multiply_by_scalar(&t.cur_tok->data.num_lit.bignum, t.radix); - t.cur_tok->data.num_lit.overflow = t.cur_tok->data.num_lit.overflow || - bignum_increment_by_scalar(&t.cur_tok->data.num_lit.bignum, digit_value); + BigInt digit_value_bi; + bigint_init_unsigned(&digit_value_bi, digit_value); + + BigInt radix_bi; + bigint_init_unsigned(&radix_bi, t.radix); + + BigInt multiplied; + bigint_mul(&multiplied, &t.cur_tok->data.int_lit.bigint, &radix_bi); + + bigint_add(&t.cur_tok->data.int_lit.bigint, &multiplied, &digit_value_bi); break; } case TokenizeStateNumberDot: - if (c == '.') { - t.pos -= 2; - end_token(&t); - t.state = TokenizeStateStart; + { + if (c == '.') { + t.pos -= 2; + end_token(&t); + t.state = TokenizeStateStart; + continue; + } + t.pos -= 1; + t.state = TokenizeStateFloatFraction; + assert(t.cur_tok->id == TokenIdIntLiteral); + bigint_init_bigint(&t.significand, &t.cur_tok->data.int_lit.bigint); + set_token_id(&t, t.cur_tok, TokenIdFloatLiteral); continue; } - t.pos -= 1; - t.state = TokenizeStateFloatFraction; - t.is_num_lit_float = true; - continue; case TokenizeStateFloatFraction: { if (is_exponent_signifier(c, t.radix)) { @@ -1236,10 +1252,16 @@ void tokenize(Buf *buf, Tokenization *out) { // end of the token. break; } - t.cur_tok->data.num_lit.overflow = t.cur_tok->data.num_lit.overflow || - bignum_multiply_by_scalar(&t.cur_tok->data.num_lit.bignum, t.radix); - t.cur_tok->data.num_lit.overflow = t.cur_tok->data.num_lit.overflow || - bignum_increment_by_scalar(&t.cur_tok->data.num_lit.bignum, digit_value); + BigInt digit_value_bi; + bigint_init_unsigned(&digit_value_bi, digit_value); + + BigInt radix_bi; + bigint_init_unsigned(&radix_bi, t.radix); + + BigInt multiplied; + bigint_mul(&multiplied, &t.significand, &radix_bi); + + bigint_add(&t.significand, &multiplied, &digit_value_bi); break; } case TokenizeStateFloatExponentUnsigned: @@ -1278,10 +1300,16 @@ void tokenize(Buf *buf, Tokenization *out) { // end of the token. break; } - t.cur_tok->data.num_lit.overflow = t.cur_tok->data.num_lit.overflow || - bignum_multiply_by_scalar(&t.specified_exponent, 10); - t.cur_tok->data.num_lit.overflow = t.cur_tok->data.num_lit.overflow || - bignum_increment_by_scalar(&t.specified_exponent, digit_value); + BigInt digit_value_bi; + bigint_init_unsigned(&digit_value_bi, digit_value); + + BigInt radix_bi; + bigint_init_unsigned(&radix_bi, 10); + + BigInt multiplied; + bigint_mul(&multiplied, &t.specified_exponent, &radix_bi); + + bigint_add(&t.specified_exponent, &multiplied, &digit_value_bi); } break; case TokenizeStateSawDash: @@ -1441,11 +1469,13 @@ const char * token_name(TokenId id) { case TokenIdDivEq: return "/="; case TokenIdDot: return "."; case TokenIdDoubleQuestion: return "??"; - case TokenIdEllipsis3: return "..."; case TokenIdEllipsis2: return ".."; + case TokenIdEllipsis3: return "..."; case TokenIdEof: return "EOF"; case TokenIdEq: return "="; case TokenIdFatArrow: return "=>"; + case TokenIdFloatLiteral: return "FloatLiteral"; + case TokenIdIntLiteral: return "IntLiteral"; case TokenIdKeywordAnd: return "and"; case TokenIdKeywordAsm: return "asm"; case TokenIdKeywordBreak: return "break"; @@ -1494,7 +1524,6 @@ const char * token_name(TokenId id) { case TokenIdMinusPercent: return "-%"; case TokenIdMinusPercentEq: return "-%="; case TokenIdModEq: return "%="; - case TokenIdNumberLiteral: return "NumberLiteral"; case TokenIdNumberSign: return "#"; case TokenIdPercent: return "%"; case TokenIdPercentDot: return "%."; diff --git a/src/tokenizer.hpp b/src/tokenizer.hpp index a5c81f7694..b49cbcbd95 100644 --- a/src/tokenizer.hpp +++ b/src/tokenizer.hpp @@ -9,7 +9,8 @@ #define ZIG_TOKENIZER_HPP #include "buffer.hpp" -#include "bignum.hpp" +#include "bigint.hpp" +#include "bigfloat.hpp" enum TokenId { TokenIdAmpersand, @@ -40,11 +41,13 @@ enum TokenId { TokenIdDivEq, TokenIdDot, TokenIdDoubleQuestion, - TokenIdEllipsis3, TokenIdEllipsis2, + TokenIdEllipsis3, TokenIdEof, TokenIdEq, TokenIdFatArrow, + TokenIdFloatLiteral, + TokenIdIntLiteral, TokenIdKeywordAnd, TokenIdKeywordAsm, TokenIdKeywordBreak, @@ -93,7 +96,6 @@ enum TokenId { TokenIdMinusPercent, TokenIdMinusPercentEq, TokenIdModEq, - TokenIdNumberLiteral, TokenIdNumberSign, TokenIdPercent, TokenIdPercentDot, @@ -118,13 +120,17 @@ enum TokenId { TokenIdTimesPercentEq, }; -struct TokenNumLit { - BigNum bignum; - // overflow is true if when parsing the number, we discovered it would not - // fit without losing data in a uint64_t or double +struct TokenFloatLit { + BigFloat bigfloat; + // overflow is true if when parsing the number, we discovered it would not fit + // without losing data bool overflow; }; +struct TokenIntLit { + BigInt bigint; +}; + struct TokenStrLit { Buf str; bool is_c_str; @@ -142,8 +148,11 @@ struct Token { size_t start_column; union { - // TokenIdNumberLiteral - TokenNumLit num_lit; + // TokenIdIntLiteral + TokenIntLit int_lit; + + // TokenIdFloatLiteral + TokenFloatLit float_lit; // TokenIdStringLiteral or TokenIdSymbol TokenStrLit str_lit; diff --git a/std/math/fabs.zig b/std/math/fabs.zig index 7030dcd8bb..185f134663 100644 --- a/std/math/fabs.zig +++ b/std/math/fabs.zig @@ -36,8 +36,8 @@ test "math.fabs" { } test "math.fabs32" { - assert(fabs64(1.0) == 1.0); - assert(fabs64(-1.0) == 1.0); + assert(fabs32(1.0) == 1.0); + assert(fabs32(-1.0) == 1.0); } test "math.fabs64" { diff --git a/std/math/log10.zig b/std/math/log10.zig index 50c942a8bc..144698b0cd 100644 --- a/std/math/log10.zig +++ b/std/math/log10.zig @@ -139,7 +139,7 @@ fn log10_64(x_: f64) -> f64 { // hi + lo = f - hfsq + s * (hfsq + R) ~ log(1 + f) var hi = f - hfsq; var hii = @bitCast(u64, hi); - hii &= @maxValue(u64) << 32; + hii &= u64(@maxValue(u64)) <<% 32; hi = @bitCast(f64, hii); const lo = f - hi - hfsq + s * (hfsq + R); diff --git a/std/math/log2.zig b/std/math/log2.zig index c136c7166c..eade3d28a7 100644 --- a/std/math/log2.zig +++ b/std/math/log2.zig @@ -133,7 +133,7 @@ fn log2_64(x_: f64) -> f64 { // hi + lo = f - hfsq + s * (hfsq + R) ~ log(1 + f) var hi = f - hfsq; var hii = @bitCast(u64, hi); - hii &= @maxValue(u64) << 32; + hii &= u64(@maxValue(u64)) <<% 32; hi = @bitCast(f64, hii); const lo = f - hi - hfsq + s * (hfsq + R); diff --git a/test/cases/math.zig b/test/cases/math.zig index 707ae6fb56..33708bd0d6 100644 --- a/test/cases/math.zig +++ b/test/cases/math.zig @@ -58,15 +58,33 @@ test "@shlWithOverflow" { } test "@clz" { - assert(@clz(u8(0b00001010)) == 4); - assert(@clz(u8(0b10001010)) == 0); - assert(@clz(u8(0b00000000)) == 8); + testClz(); + comptime testClz(); +} + +fn testClz() { + assert(clz(u8(0b00001010)) == 4); + assert(clz(u8(0b10001010)) == 0); + assert(clz(u8(0b00000000)) == 8); +} + +fn clz(x: var) -> usize { + @clz(x) } test "@ctz" { - assert(@ctz(u8(0b10100000)) == 5); - assert(@ctz(u8(0b10001010)) == 1); - assert(@ctz(u8(0b00000000)) == 8); + testCtz(); + comptime testCtz(); +} + +fn testCtz() { + assert(ctz(u8(0b10100000)) == 5); + assert(ctz(u8(0b10001010)) == 1); + assert(ctz(u8(0b00000000)) == 8); +} + +fn ctz(x: var) -> usize { + @ctz(x) } test "assignment operators" { @@ -229,3 +247,7 @@ test "allow signed integer division/remainder when values are comptime known and assert(5 % 3 == 2); assert(-6 % 3 == 0); } + +test "float literal parsing" { + comptime assert(0x1.0 == 1.0); +}