Managing bigint memory

This commit is contained in:
Krzosa Karol
2022-06-06 16:49:41 +02:00
parent 06d6ec7525
commit dc56bd54f3
6 changed files with 165 additions and 62 deletions

View File

@@ -3,9 +3,17 @@
// a copy of which can be found in the LICENSE file.
struct Token;
Allocator *bigint_allocator;
global S64 bigint_allocation_count;
function void parsing_error(Token *token, const char *str, ...);
#define malloc_arena(x) (bigint_allocation_count++, exp_alloc(&pernament_arena, x))
#define Set_BigInt_Allocator(x) BigInt_Allocator bigint_allocator(x)
struct BigInt_Allocator{
BigInt_Allocator(Allocator *allocator){bigint_allocator = allocator;}
~BigInt_Allocator(){bigint_allocator = 0;}
};
#define malloc_arena(x) (bigint_allocation_count++, exp_alloc(bigint_allocator, x, AF_ZeroMemory))
#define ALLOC_DIGITS(_digits) (uint64_t *)((_digits) ? malloc_arena(sizeof(uint64_t) * (_digits)) : NULL)
#define FATAL_ERROR(x) parsing_error(0, x)
@@ -95,6 +103,33 @@ bigint_mul(const BigInt *a, const BigInt *b){
return result;
}
function BigInt
bigint_add(const BigInt *a, const BigInt *b){
BigInt result;
bigint_add(&result, a, b);
return result;
}
function BigInt
bigint_copy(Allocator *allocator, BigInt *src){
BigInt dest = {};
if (src->digit_count == 0){
bigint_init_unsigned(&dest, 0);
return dest;
}
if(src->digit_count == 1){
dest.digit_count = 1;
dest.digit = src->digit;
dest.is_negative = src->is_negative;
return dest;
}
dest.is_negative = src->is_negative;
dest.digit_count = src->digit_count;
dest.digits = exp_alloc_array(allocator, uint64_t, dest.digit_count, AF_ZeroMemory);
memcpy(dest.digits, src->digits, sizeof(uint64_t) * dest.digit_count);
return dest;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
@@ -671,6 +706,8 @@ bool mul_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result)
void bigint_add(BigInt *dest, const BigInt *op1, const BigInt *op2)
{
assert(dest != op1);
assert(dest != op2);
if (op1->digit_count == 0)
{
return bigint_init_bigint(dest, op2);
@@ -893,6 +930,8 @@ static void mul_scalar(BigInt *dest, const BigInt *op, uint64_t scalar)
void bigint_mul(BigInt *dest, const BigInt *op1, const BigInt *op2)
{
assert(dest != op1);
assert(dest != op2);
if (op1->digit_count == 0 || op2->digit_count == 0)
{
return bigint_init_unsigned(dest, 0);
@@ -1958,6 +1997,7 @@ void bigint_print(BigInt *bigint, uint64_t base)
const char *bigint_to_error_string(Allocator *allocator, const BigInt *bigint, uint64_t base)
{
Set_BigInt_Allocator(allocator);
if (bigint->digit_count == 0)
{
return "0";
@@ -2021,6 +2061,7 @@ const char *bigint_to_error_string(Allocator *allocator, const BigInt *bigint, u
*(current++) = *ptr;
}
*(current++) = '\0';
return out;
}

View File

@@ -108,7 +108,6 @@ For now I don't thing it should be overloadable.
int main(){
// test_big_int();
test_os_memory();
thread_ctx_init();
test_unicode();

View File

@@ -319,7 +319,7 @@ function Ast_Atom *
ast_int(Token *pos, BigInt val){
AST_NEW(Atom, VALUE, pos, AST_EXPR | AST_ATOM);
result->type = untyped_int;
bigint_init_bigint(&result->big_int_val, &val);
result->big_int_val = bigint_copy(pctx->perm, &val);
return result;
}

View File

@@ -125,6 +125,7 @@ struct Lex_Stream{
};
struct Lexer{
Allocator *arena;
Lex_Stream stream;
Array<Token> tokens;
Intern_Table interns;
@@ -205,17 +206,23 @@ token_error(Token *t, String error_val){
}
function void
lex_parse_u64(Token *t){
lex_parse_u64(Lexer *lexer, Token *t){
Scratch scratch;
Set_BigInt_Allocator(scratch);
t->kind = TK_Integer;
BigInt m = bigint_u64(1); // @leak, it accumulates and potentially needs allocation
BigInt val10 = bigint_u64(10);
BigInt result = bigint_u64(0);
for(S64 i = t->len - 1; i >= 0; --i){
BigInt val = bigint_u64(t->str[i] - '0'); // I dont think this is a leak, too small
BigInt new_val = bigint_mul(&val, &m); // @leak
bigint_add(&t->int_val, &t->int_val, &new_val);
bigint_mul(&m, &m, &val10);
result = bigint_add(&result, &new_val); // @leak
m = bigint_mul(&m, &val10); // @leak
}
t->int_val = bigint_copy(lexer->arena, &result);
}
function void
@@ -332,7 +339,10 @@ lex_unwind_indent_stack(Token *t, Lex_Stream *s, Array<Token> *array){
}
function void
lex__stream(Intern_Table *table, Array<Token> *array, Lex_Stream *s){
lex__stream(Lexer *lexer, Lex_Stream *s){
Intern_Table *table = &lexer->interns;
Array<Token> *array = &lexer->tokens;
B32 beginning = true;
for(;;){
if(lexc(s) == 0 || s->iter >= s->stream.len){
@@ -594,7 +604,7 @@ lex__stream(Intern_Table *table, Array<Token> *array, Lex_Stream *s){
}
lex_set_len(s, &t);
if(found_dot) lex_parse_f64(&t);
else lex_parse_u64(&t);
else lex_parse_u64(lexer, &t);
} break;
@@ -633,6 +643,7 @@ lex__stream(Intern_Table *table, Array<Token> *array, Lex_Stream *s){
function void
lex_init(Allocator *token_string_arena, Allocator *map_allocator, Lexer *l){
l->arena = token_string_arena;
l->tokens = array_make<Token>(token_string_arena, 1024*2);
l->interns= intern_table_make(token_string_arena, map_allocator, 1024);
}
@@ -657,7 +668,7 @@ lex_restream(Lexer *lexer, String istream, String file){
Scratch scratch;
lexer->stream.indent_stack.allocator = scratch;
lexer->stream.indent_stack.add(&token_null);
lex__stream(&lexer->interns, &lexer->tokens, &lexer->stream);
lex__stream(lexer, &lexer->stream);
}
function Lexer

View File

@@ -21,7 +21,7 @@ unary_test :: ()
// var := -true
// var := +true
binary_test :: ()
binary_test :: (thing: S32 = 1051514424242424242442424242424252525252)
int_val :: 1000
add :: int_val + 10 + 2.242 + 124
mul :: 4 * 2
@@ -30,10 +30,13 @@ binary_test :: ()
bit_xor :: 8 ^ 7
character :: 'ó
boolean_equals :: true == false
boolean_var: Bool = boolean_equals
cast_value :: cast(4242: S32)
cast_value :: 4242 + cast(32: S32) + cast(42: S32)
value: S32 = cast_value
bvar2 := int_val > 1
if int_val < 1

View File

@@ -72,7 +72,7 @@ value_float(BigInt a){
}
function void
check_value_boundaries(Token *pos, Value *a){
check_value_bounds(Token *pos, Value *a){
if(!is_int(a->type)) return;
Scratch scratch;
@@ -101,25 +101,77 @@ convert_untyped(Token *pos, Value a, Ast_Resolved_Type *new_type){
else parsing_error(pos, "Type mismatch when converting from %s to %s", docname(a.type), docname(new_type));
a.type = new_type;
check_value_boundaries(pos, &a);
check_value_bounds(pos, &a);
return a;
}
/*
Type resolution cases
val := expr
convert untyped to typed default
check bounds
make new symbol
CONST :: expr
make new symbol
call(default:type = expr)
val: type = expr
convert untyped to typed based on type
make sure expr.type == type
check bounds
expr == expr
expr * expr
make sure compatible types, floats with ints are ok(convert to float)
if only one of them is typed convert the untyped to typed
if both types typed make sure they are the same
check bounds
!expr
make sure correct type
return bool
call(expr, expr)
convert untyped to matching typed
check if types match
cast(expr: type)
convert from untyped to typed
convert between typed to other typed
check if types compatible
*/
function void
match_values(Value *a, Value *b){
// @todo: We want match values to convert when int and float => float
// but also we want to unify types when one of them is typed and other untyped???
// Lastly maybe check if typed types are the same
if(is_int(a->type) && is_float(b->type))
match_values(Token *pos, Value *a, Value *b){
if(is_typed(a->type) && is_typed(b->type)){
if(a->type != b->type){
parsing_error(pos, "Type mismatch in match_values - left: %s right: %s", docname(a->type), docname(b->type));
}
}
if(is_untyped(a->type) && is_typed(b->type)){
assert(is_typed(b->type));
*a = convert_untyped(pos, *a, b->type);
}
else if(is_typed(a->type) && is_untyped(b->type)){
assert(is_typed(a->type));
*b = convert_untyped(pos, *b, a->type);
}
else if(is_int(a->type) && is_float(b->type)){
*a = value_float(a->big_int_val);
else if(is_float(a->type) && is_int(b->type))
}
else if(is_float(a->type) && is_int(b->type)){
*b = value_float(b->big_int_val);
}
}
function Value
compare_values(Token *pos, Token_Kind op, Value a, Value b){
if(!(is_numeric(a.type) && is_numeric(b.type))) parsing_error(pos, "Constant application of binary %s on values of type %s and %s is not allowed", name(op), docname(a.type), docname(a.type));
match_values(&a, &b);
match_values(pos, &a, &b);
B32 result = 0;
switch(a.type->kind){
@@ -164,13 +216,13 @@ compare_values(Token *pos, Token_Kind op, Value a, Value b){
function Value
eval_binary(Token *pos, Token_Kind op, Value a, Value b){
if(!(is_numeric(a.type) && is_numeric(b.type))) parsing_error(pos, "Constant application of binary %s on values of type %s and %s is not allowed", name(op), docname(a.type), docname(a.type));
match_values(&a, &b);
if(token_is_compare(op)){
return compare_values(pos, op, a, b);
}
if(!(is_numeric(a.type) && is_numeric(b.type))) parsing_error(pos, "Constant application of binary %s on values of type %s and %s is not allowed", name(op), docname(a.type), docname(a.type));
match_values(pos, &a, &b);
Value result = {};
result.type = a.type;
switch(a.type->kind){
@@ -285,6 +337,29 @@ try_untyping(Operand *op){
}
}
function void
resolve_var(Token *pos, Operand *expr, Ast_Resolved_Type *type = 0){
if(type == expr->type) {
assert(expr->type);
return;
}
if(!type)
try_untyping(expr);
else if(!expr->type)
expr->type = type;
else if(is_untyped(expr->type))
expr->value = convert_untyped(pos, expr->value, type);
if(type && expr->type != type){
parsing_error(pos, "Assigning but incompatible types, expression: %s expected var type: %s", docname(expr->type), docname(type));
}
type_complete(expr->type);
check_value_bounds(pos, &expr->value);
assert(expr->type);
}
#define rewrite_into_const(ast,T,s) _rewrite_into_const(ast,sizeof(T),s)
function void
_rewrite_into_const(Ast *node, U64 ast_size, Value value){
@@ -395,11 +470,7 @@ resolve_lambda(Ast_Lambda *lambda, Sym *sym = 0){
For(lambda->args){
Ast_Resolved_Type *type = resolve_typespec(it->typespec, AST_CANT_BE_NULL);
Operand default_value = resolve_expr(it->default_value, type);
if(default_value.type){
default_value.value = convert_untyped(it->pos, default_value.value, type);
if(default_value.type != type) parsing_error(it->pos, "Default value type and type declaration differ, expected %s got instead %s", docname(type), docname(default_value.type));
}
resolve_var(it->pos, &default_value, type);
args.add(type);
}
@@ -422,6 +493,7 @@ resolve_lambda(Ast_Lambda *lambda, Sym *sym = 0){
For(lambda->args){
S64 i = lambda->args.get_index(&it);
Ast_Resolved_Type *type = args[i];
sym_var(it->name, type, it, INSERT_INTO_SCOPE);
}
For(lambda->block->stmts){
@@ -671,7 +743,7 @@ resolve_expr(Ast_Expr *ast, Ast_Resolved_Type *expected_type, Sym *lambda_to_res
}
if(original_type != type) assert(expr.type == type);
check_value_boundaries(node->pos, &expr.value);
check_value_bounds(node->pos, &expr.value);
return expr;
BREAK();
}
@@ -713,9 +785,10 @@ resolve_expr(Ast_Expr *ast, Ast_Resolved_Type *expected_type, Sym *lambda_to_res
assert(node->left->kind == AST_IDENT);
Operand right = resolve_expr(node->right);
try_untyping(&right);
resolve_var(node->pos, &right);
assert(right.type);
Ast_Atom *atom = (Ast_Atom *)node->left;
auto atom = (Ast_Atom *)node->left;
sym_var(atom->intern_val, right, node, INSERT_INTO_SCOPE);
}
//-----------------------------------------------------------------------------
@@ -822,29 +895,12 @@ resolve_expr(Ast_Expr *ast, Ast_Resolved_Type *expected_type, Sym *lambda_to_res
else{
Operand left = resolve_expr(node->left);
Operand right = resolve_expr(node->right);
if(left.is_const && right.is_const){
Value value = eval_binary(node->pos, node->op, left.value, right.value);
if(left.is_const && right.is_const){
rewrite_into_const(node, Ast_Binary, value);
result = operand_const_rvalue(value);
}
else{
if(is_untyped(left.type)){
assert(is_typed(right.type));
left.value = convert_untyped(node->pos, left.value, right.type);
}
else if(is_untyped(right.type)){
assert(is_typed(left.type));
right.value = convert_untyped(node->pos, right.value, left.type);
}
if(left.type != right.type){
parsing_error(node->pos, "Type mismatch in binary operation %s - left: %s right: %s", name(node->op), docname(left.type), docname(right.type));
}
else{
result = operand_rvalue(left.type);
}
}
else result = operand_rvalue(left.type);
}
//-----------------------------------------------------------------------------
@@ -907,14 +963,8 @@ resolve_binding(Ast *ast, Sym *sym){
Ast_Resolved_Type *type = resolve_typespec(node->typespec, AST_CAN_BE_NULL);
Operand expr = resolve_expr(node->expr, type);
assert(expr.type != 0 || type != 0);
if(!type) try_untyping(&expr);
else if(!expr.type) expr.type = type;
else if(type == expr.type);
else if(is_untyped(expr.type)) expr.value = convert_untyped(node->pos, expr.value, type);
else invalid_codepath;
type_complete(expr.type);
resolve_var(node->pos, &expr, type);
assert(expr.type);
return expr;
BREAK();
}
@@ -945,7 +995,6 @@ resolve_sym(Sym *sym){
sym->state = SYM_RESOLVING;
{
Operand op = resolve_binding(sym->ast, sym);
sym->type = op.type;
sym->value = op.value;
}
sym->state = SYM_RESOLVED;