From 3d92e9061bfde6394a5c9e44b7aa739f505dfbb2 Mon Sep 17 00:00:00 2001 From: Alexander Hansen Date: Sat, 22 Mar 2025 19:31:29 +0100 Subject: [PATCH 1/2] compiler: refactor anytype '#' handling struct Type now has a property 'bool is_anytype' to represent the anytype. Anytype can be used to override the typechecker. For example, calling a memory allocation function returns a pointer, which can be a used as a pointer to any other type, whatever is put there. Anytype should have the highest native width of the architecture which is targeted, so 8 bytes for x86-64, and 2 bytes for AVR. --- ast/ast/ast_types.h | 5 +++++ ast/test/test_str_ast.c | 2 +- ast/util/copy_ast.c | 1 + ast/util/equals_ast.c | 4 ++++ ast/util/str_ast.c | 21 ++++++++++++++++--- compiler/main/typechecker/tc_ifstmt.c | 6 ++++++ compiler/main/typechecker/tc_retstmt.c | 3 ++- .../type_contains/tc_pointertype_contains.c | 2 +- .../type_contains/tc_type_contains.c | 4 ++++ compiler/main/typeinference/typeinfer_expr.c | 18 ++++++++++------ docs/html/grammar.html | 4 ++-- parser/main/astnodes/types/SimpleType.c | 2 -- parser/main/astnodes/types/Type.c | 5 +++++ parser/test/astnodes/types/SimpleTypeTest.c | 6 ++++-- 14 files changed, 65 insertions(+), 18 deletions(-) diff --git a/ast/ast/ast_types.h b/ast/ast/ast_types.h index 485572d42..39bd1a553 100644 --- a/ast/ast/ast_types.h +++ b/ast/ast/ast_types.h @@ -69,6 +69,11 @@ struct Type { struct TypeParam* type_param; struct ArrayType* array_type; struct PointerType* pointer_type; + + // '#', stands for anything, + // always has highest native width of the architecture, + // e.g. x86: 8 bytes, avr: 2 bytes + bool is_anytype; }; struct TypeParam { diff --git a/ast/test/test_str_ast.c b/ast/test/test_str_ast.c index 3d5c4eb78..b5beecead 100644 --- a/ast/test/test_str_ast.c +++ b/ast/test/test_str_ast.c @@ -104,7 +104,7 @@ void test_ast_str_expr() { char* s = str_expr(&e); - assert(strcmp(s, "-45*-45") == 0); + assert(strcmp(s, "-45 * -45") == 0); free_un_op_term(u2); free(s); diff --git a/ast/util/copy_ast.c b/ast/util/copy_ast.c index 3b73429cb..b3efdb44a 100644 --- a/ast/util/copy_ast.c +++ b/ast/util/copy_ast.c @@ -179,6 +179,7 @@ struct Type* copy_type(struct Type* t) { res->type_param = NULL; res->array_type = NULL; res->pointer_type = NULL; + res->is_anytype = t->is_anytype; if (t->basic_type != NULL) { res->basic_type = copy_basic_type(t->basic_type); } if (t->type_param != NULL) { res->type_param = copy_type_param(t->type_param); } diff --git a/ast/util/equals_ast.c b/ast/util/equals_ast.c index 454cf3027..6fecf7225 100644 --- a/ast/util/equals_ast.c +++ b/ast/util/equals_ast.c @@ -17,6 +17,10 @@ bool eq_type(struct Type* a, struct Type* b) { if (a->pointer_type != NULL) { return eq_pointertype(a->pointer_type, b->pointer_type); } + if (a->is_anytype && b->is_anytype) { + return true; + } + return false; } diff --git a/ast/util/str_ast.c b/ast/util/str_ast.c index 067ed4ebb..1ebe79c1d 100644 --- a/ast/util/str_ast.c +++ b/ast/util/str_ast.c @@ -293,6 +293,10 @@ char* str_type(struct Type* t) { if (t->pointer_type != NULL) { return str_pointer_type(t->pointer_type); } + if (t->is_anytype) { + return strdup("#"); + } + error("str_type"); return NULL; } @@ -494,7 +498,7 @@ char* str_expr(struct Expr* e) { return NULL; } - uint16_t l = strlen(strTerm1) + strlen(strO) + strlen(strTerm2) + 1; + uint16_t l = strlen(strTerm1) + strlen(strO) + strlen(strTerm2) + 3; char* res = malloc(sizeof(char) * l); @@ -502,7 +506,7 @@ char* str_expr(struct Expr* e) { return NULL; } - sprintf(res, "%s%s%s", strTerm1, strO, strTerm2); + sprintf(res, "%s %s %s", strTerm1, strO, strTerm2); free(strTerm1); @@ -512,6 +516,7 @@ char* str_expr(struct Expr* e) { return res; } +// @returns NULL on error char* str_op(enum OP o) { char* res = malloc(sizeof(char) * 16); @@ -533,8 +538,18 @@ char* str_op(enum OP o) { case OP_OR: str = "|"; break; case OP_XOR: str = "^"; break; + // relational + case OP_EQ: str = "=="; break; + case OP_NEQ: str = "!="; break; + case OP_GE: str = ">="; break; + case OP_GT: str = ">"; break; + case OP_LE: str = "<="; break; + case OP_LT: str = "<"; break; + //should not happen - default: str = "fix str_op"; break; + default: + free(res); + return NULL; } sprintf(res, "%s", str); diff --git a/compiler/main/typechecker/tc_ifstmt.c b/compiler/main/typechecker/tc_ifstmt.c index 3a53bfda9..ecb270930 100644 --- a/compiler/main/typechecker/tc_ifstmt.c +++ b/compiler/main/typechecker/tc_ifstmt.c @@ -35,6 +35,12 @@ bool tc_ifstmt(struct IfStmt* i, struct TCCtx* tcctx) { error_snippet(tcctx, msg, TC_ERR_CONDITION_REQUIRES_BOOL); + char* s2 = str_type(type); + + fprintf(stderr, "actual type: %s\n\n", s2); + + free(s2); + free(msg); return false; diff --git a/compiler/main/typechecker/tc_retstmt.c b/compiler/main/typechecker/tc_retstmt.c index 83a18ce0e..0da25685f 100644 --- a/compiler/main/typechecker/tc_retstmt.c +++ b/compiler/main/typechecker/tc_retstmt.c @@ -11,6 +11,7 @@ //Typechecker Includes #include "_tc.h" +#include "typechecker/type_contains/tc_type_contains.h" #include "typechecker/util/tc_errors.h" #include "typechecker/util/tc_utils.h" #include "tcctx.h" @@ -31,7 +32,7 @@ bool tc_retstmt(struct RetStmt* r, struct TCCtx* tcctx) { if (is_integer_type(returnType) && is_integer_type(returnedType)) { return true; } - if (!eq_type(returnType, returnedType)) { + if (!tc_type_contains(returnType, returnedType)) { char* s1 = str_type(returnType); char* s2 = str_type(returnedType); diff --git a/compiler/main/typechecker/type_contains/tc_pointertype_contains.c b/compiler/main/typechecker/type_contains/tc_pointertype_contains.c index 8e4fa2bd7..3b495b19c 100644 --- a/compiler/main/typechecker/type_contains/tc_pointertype_contains.c +++ b/compiler/main/typechecker/type_contains/tc_pointertype_contains.c @@ -22,5 +22,5 @@ bool tc_pointer_type_contains(struct PointerType* expect, struct Type* actual) { struct PointerType* apt = actual->pointer_type; // pointers of same underlying type can be assigned to each other - return eq_type(expect->element_type, apt->element_type); + return tc_type_contains(expect->element_type, apt->element_type); } diff --git a/compiler/main/typechecker/type_contains/tc_type_contains.c b/compiler/main/typechecker/type_contains/tc_type_contains.c index 9e7baf351..f48a123fb 100644 --- a/compiler/main/typechecker/type_contains/tc_type_contains.c +++ b/compiler/main/typechecker/type_contains/tc_type_contains.c @@ -9,6 +9,10 @@ bool tc_type_contains(struct Type* expect, struct Type* actual) { + if (expect->is_anytype || actual->is_anytype) { + return true; + } + if (expect->pointer_type != NULL) { return tc_pointer_type_contains(expect->pointer_type, actual); diff --git a/compiler/main/typeinference/typeinfer_expr.c b/compiler/main/typeinference/typeinfer_expr.c index 8f4e851b7..140cfacaf 100644 --- a/compiler/main/typeinference/typeinfer_expr.c +++ b/compiler/main/typeinference/typeinfer_expr.c @@ -21,7 +21,7 @@ static struct Type* infer_type_expr_primitive(struct ST* st, struct Expr2Types* static struct Type* infer_type_expr_both_tparam(struct ST* st, struct TypeParam* tp1, enum OP op, struct TypeParam* tp2); -static struct Type* infer_type_expr_ptr_arithmetic(struct ST* st, struct Type* t1, struct Type* t2); +static struct Type* infer_type_expr_ptr_arithmetic(struct ST* st, struct Type* t1, struct Type* t2, enum OP op); static void typeinfer_err_fatal(char* opt_str) { fprintf(stderr, "%s\n", opt_str); @@ -44,10 +44,10 @@ struct Type* infer_type_expr(struct ST* st, struct Expr* expr) { } if (type1->pointer_type) { - return infer_type_expr_ptr_arithmetic(st, type1, type2); + return infer_type_expr_ptr_arithmetic(st, type1, type2, op); } if (type2->pointer_type) { - return infer_type_expr_ptr_arithmetic(st, type2, type1); + return infer_type_expr_ptr_arithmetic(st, type2, type1, op); } struct BasicType* btw1 = type1->basic_type; @@ -82,7 +82,7 @@ struct Type* infer_type_expr(struct ST* st, struct Expr* expr) { return infer_type_expr_primitive(st, &e2t); } -static struct Type* infer_type_expr_ptr_arithmetic(struct ST* st, struct Type* t1, struct Type* t2) { +static struct Type* infer_type_expr_ptr_arithmetic(struct ST* st, struct Type* t1, struct Type* t2, enum OP op) { struct PointerType* pt = t1->pointer_type; @@ -92,9 +92,15 @@ static struct Type* infer_type_expr_ptr_arithmetic(struct ST* st, struct Type* t } if (t2->basic_type && t2->basic_type->simple_type) { - struct SimpleType* st = t2->basic_type->simple_type; + struct SimpleType* stype = t2->basic_type->simple_type; + + if (stype->primitive_type && stype->primitive_type->is_int_type) { + + if (op == OP_EQ || op == OP_NEQ || op == OP_GE || op == OP_GT || op == OP_LE || op == OP_LT) { + + return typeFromStrPrimitive(st, "bool"); + } - if (st->primitive_type && st->primitive_type->is_int_type) { return t1; } } diff --git a/docs/html/grammar.html b/docs/html/grammar.html index 0be27804a..954136fd1 100644 --- a/docs/html/grammar.html +++ b/docs/html/grammar.html @@ -60,7 +60,7 @@

Method

Types

- Type ::= BasicType | TypeParam | ArrayType | PointerType + Type ::= BasicType | TypeParam | ArrayType | PointerType | #

BasicType ::= SimpleType | '(' SubrType ')' @@ -78,7 +78,7 @@

Types

SubrType ::= '(' Type* ')' arrow Type

- StructType ::= uppercase alphanumeric* | "#" + StructType ::= uppercase alphanumeric*

TypeParam ::= "?T" digit diff --git a/parser/main/astnodes/types/SimpleType.c b/parser/main/astnodes/types/SimpleType.c index 77bc16dfc..fe07aa3f9 100644 --- a/parser/main/astnodes/types/SimpleType.c +++ b/parser/main/astnodes/types/SimpleType.c @@ -48,8 +48,6 @@ struct SimpleType* makeSimpleType(struct TokenList* tokens) { break; case TYPEID: - case ANYTYPE: - res->struct_type = makeStructType(copy); break; diff --git a/parser/main/astnodes/types/Type.c b/parser/main/astnodes/types/Type.c index a894a0e8e..ff5a8e978 100644 --- a/parser/main/astnodes/types/Type.c +++ b/parser/main/astnodes/types/Type.c @@ -73,6 +73,7 @@ struct Type* makeType2(struct TokenList* tokens) { res->type_param = NULL; res->array_type = NULL; res->pointer_type = NULL; + res->is_anytype = false; struct Token* head = list_head(copy); @@ -89,6 +90,10 @@ struct Type* makeType2(struct TokenList* tokens) { res->type_param = makeTypeParam(copy); if (res->type_param != NULL) { goto end; } break; + case ANYTYPE: + list_consume(copy, 1); + res->is_anytype = true; + goto end; default: res->basic_type = makeBasicType2(copy); if (res->basic_type != NULL) { goto end; } diff --git a/parser/test/astnodes/types/SimpleTypeTest.c b/parser/test/astnodes/types/SimpleTypeTest.c index 43f9d850b..300c6e515 100644 --- a/parser/test/astnodes/types/SimpleTypeTest.c +++ b/parser/test/astnodes/types/SimpleTypeTest.c @@ -8,6 +8,7 @@ #include "types/SimpleType.h" #include "types/BasicType.h" +#include "types/Type.h" #include "token/list/TokenList.h" #include "token/TokenKeys.h" @@ -56,13 +57,14 @@ int simpletype_test_typenode_parsing_anytype() { struct TokenList* list = makeTokenList(); list_add(list, makeToken2(ANYTYPE, "#")); - struct SimpleType* node = makeSimpleType(list); + struct Type* node = makeType2(list); assert(0 == list_size(list)); assert(node != NULL); + assert(node->is_anytype); freeTokenList(list); - free_simple_type(node); + free_type(node); return 1; } From e261ca3f2b855c7c577aad2df076de06b1ec0e87 Mon Sep 17 00:00:00 2001 From: Alexander Hansen Date: Sat, 22 Mar 2025 19:33:57 +0100 Subject: [PATCH 2/2] stdlib: implement a crappy allocator It allocates in multiples of systems page size. It does not do any internal bookkeeping at all. To summarize, it is very inefficient. But it should suffice for creating simple programs which need a heap. --- compiler/main/typeinference/typeinfer_expr.c | 6 + .../allocator/alloc_0_bytes/alloc_0_bytes.dg | 13 +++ .../alloc_0_bytes/alloc_0_bytes.exitcode | 1 + .../alloc_0_bytes/alloc_0_bytes.stdlib | 0 .../base/allocator/loop_test/loop_test.dg | 20 ++++ .../allocator/loop_test/loop_test.exitcode | 1 + .../base/allocator/loop_test/loop_test.stdlib | 0 .../base/allocator/no_overlap/no_overlap.dg | 19 +++ .../allocator/no_overlap/no_overlap.exitcode | 1 + .../allocator/no_overlap/no_overlap.stdlib | 0 .../allocator/realloc_test/realloc_test.dg | 35 ++++++ .../realloc_test/realloc_test.exitcode | 1 + .../realloc_test/realloc_test.stdlib | 0 .../allocator/use_allocator/use_allocator.dg | 34 ++++++ .../use_allocator/use_allocator.exitcode | 1 + .../use_allocator/use_allocator.stdlib | 0 stdlib/base/allocator.dg | 108 ++++++++++++++++++ 17 files changed, 240 insertions(+) create mode 100644 examples/stdlib/base/allocator/alloc_0_bytes/alloc_0_bytes.dg create mode 100644 examples/stdlib/base/allocator/alloc_0_bytes/alloc_0_bytes.exitcode create mode 100644 examples/stdlib/base/allocator/alloc_0_bytes/alloc_0_bytes.stdlib create mode 100644 examples/stdlib/base/allocator/loop_test/loop_test.dg create mode 100644 examples/stdlib/base/allocator/loop_test/loop_test.exitcode create mode 100644 examples/stdlib/base/allocator/loop_test/loop_test.stdlib create mode 100644 examples/stdlib/base/allocator/no_overlap/no_overlap.dg create mode 100644 examples/stdlib/base/allocator/no_overlap/no_overlap.exitcode create mode 100644 examples/stdlib/base/allocator/no_overlap/no_overlap.stdlib create mode 100644 examples/stdlib/base/allocator/realloc_test/realloc_test.dg create mode 100644 examples/stdlib/base/allocator/realloc_test/realloc_test.exitcode create mode 100644 examples/stdlib/base/allocator/realloc_test/realloc_test.stdlib create mode 100644 examples/stdlib/base/allocator/use_allocator/use_allocator.dg create mode 100644 examples/stdlib/base/allocator/use_allocator/use_allocator.exitcode create mode 100644 examples/stdlib/base/allocator/use_allocator/use_allocator.stdlib create mode 100644 stdlib/base/allocator.dg diff --git a/compiler/main/typeinference/typeinfer_expr.c b/compiler/main/typeinference/typeinfer_expr.c index 140cfacaf..ef7a6e8d4 100644 --- a/compiler/main/typeinference/typeinfer_expr.c +++ b/compiler/main/typeinference/typeinfer_expr.c @@ -91,6 +91,12 @@ static struct Type* infer_type_expr_ptr_arithmetic(struct ST* st, struct Type* t return NULL; } + if (t2->pointer_type) { + if (op == OP_EQ || op == OP_NEQ || op == OP_GE || op == OP_GT || op == OP_LE || op == OP_LT) { + return typeFromStrPrimitive(st, "bool"); + } + } + if (t2->basic_type && t2->basic_type->simple_type) { struct SimpleType* stype = t2->basic_type->simple_type; diff --git a/examples/stdlib/base/allocator/alloc_0_bytes/alloc_0_bytes.dg b/examples/stdlib/base/allocator/alloc_0_bytes/alloc_0_bytes.dg new file mode 100644 index 000000000..2bced2290 --- /dev/null +++ b/examples/stdlib/base/allocator/alloc_0_bytes/alloc_0_bytes.dg @@ -0,0 +1,13 @@ + +fn main () -> int { + + local *uint8 ptr; + + ptr = malloc(0); + + if ptr != 0 { + return 1; + } + + return 0; +} diff --git a/examples/stdlib/base/allocator/alloc_0_bytes/alloc_0_bytes.exitcode b/examples/stdlib/base/allocator/alloc_0_bytes/alloc_0_bytes.exitcode new file mode 100644 index 000000000..573541ac9 --- /dev/null +++ b/examples/stdlib/base/allocator/alloc_0_bytes/alloc_0_bytes.exitcode @@ -0,0 +1 @@ +0 diff --git a/examples/stdlib/base/allocator/alloc_0_bytes/alloc_0_bytes.stdlib b/examples/stdlib/base/allocator/alloc_0_bytes/alloc_0_bytes.stdlib new file mode 100644 index 000000000..e69de29bb diff --git a/examples/stdlib/base/allocator/loop_test/loop_test.dg b/examples/stdlib/base/allocator/loop_test/loop_test.dg new file mode 100644 index 000000000..580231e63 --- /dev/null +++ b/examples/stdlib/base/allocator/loop_test/loop_test.dg @@ -0,0 +1,20 @@ + +fn main () -> int { + + // test that the allocations do not exhaus memory + + for i in 0 .. 10000 { + + *uint64 p = malloc(3000); + + if p == 0 { + return 1; + } + + if free(p) != 0 { + return 1; + } + } + + return 0; +} diff --git a/examples/stdlib/base/allocator/loop_test/loop_test.exitcode b/examples/stdlib/base/allocator/loop_test/loop_test.exitcode new file mode 100644 index 000000000..573541ac9 --- /dev/null +++ b/examples/stdlib/base/allocator/loop_test/loop_test.exitcode @@ -0,0 +1 @@ +0 diff --git a/examples/stdlib/base/allocator/loop_test/loop_test.stdlib b/examples/stdlib/base/allocator/loop_test/loop_test.stdlib new file mode 100644 index 000000000..e69de29bb diff --git a/examples/stdlib/base/allocator/no_overlap/no_overlap.dg b/examples/stdlib/base/allocator/no_overlap/no_overlap.dg new file mode 100644 index 000000000..fa34e7e1d --- /dev/null +++ b/examples/stdlib/base/allocator/no_overlap/no_overlap.dg @@ -0,0 +1,19 @@ + +fn main () -> int { + + *uint64 ptr1 = malloc(30); + *uint64 ptr2 = malloc(30); + + if ((ptr1 + 30) > ptr2 && (ptr1 <= ptr2)){ + return 1; + } + + if ((ptr2 + 30) > ptr1 && (ptr2 <= ptr1)){ + return 1; + } + + free(ptr1); + free(ptr2); + + return 0; +} diff --git a/examples/stdlib/base/allocator/no_overlap/no_overlap.exitcode b/examples/stdlib/base/allocator/no_overlap/no_overlap.exitcode new file mode 100644 index 000000000..573541ac9 --- /dev/null +++ b/examples/stdlib/base/allocator/no_overlap/no_overlap.exitcode @@ -0,0 +1 @@ +0 diff --git a/examples/stdlib/base/allocator/no_overlap/no_overlap.stdlib b/examples/stdlib/base/allocator/no_overlap/no_overlap.stdlib new file mode 100644 index 000000000..e69de29bb diff --git a/examples/stdlib/base/allocator/realloc_test/realloc_test.dg b/examples/stdlib/base/allocator/realloc_test/realloc_test.dg new file mode 100644 index 000000000..eabae2c33 --- /dev/null +++ b/examples/stdlib/base/allocator/realloc_test/realloc_test.dg @@ -0,0 +1,35 @@ + +fn main () -> int { + + *uint8 p1 = malloc(4096); + + if p1 == 0 { + return 1; + } + + *p1 = 9; + + *uint8 p2 = realloc(p1, 8192); + + if p2 == 0 { + return 2; + } + + if *p2 != 9 { + return 3; + } + + if p1 == p2 { + // it should not re-use that block, it is too small + return 4; + } + + *uint8 p3 = realloc(p2, 4096); + + if p3 != p2 { + // it should re-use the block, it is big enough + return 5; + } + + return 0; +} diff --git a/examples/stdlib/base/allocator/realloc_test/realloc_test.exitcode b/examples/stdlib/base/allocator/realloc_test/realloc_test.exitcode new file mode 100644 index 000000000..573541ac9 --- /dev/null +++ b/examples/stdlib/base/allocator/realloc_test/realloc_test.exitcode @@ -0,0 +1 @@ +0 diff --git a/examples/stdlib/base/allocator/realloc_test/realloc_test.stdlib b/examples/stdlib/base/allocator/realloc_test/realloc_test.stdlib new file mode 100644 index 000000000..e69de29bb diff --git a/examples/stdlib/base/allocator/use_allocator/use_allocator.dg b/examples/stdlib/base/allocator/use_allocator/use_allocator.dg new file mode 100644 index 000000000..ad1af6f63 --- /dev/null +++ b/examples/stdlib/base/allocator/use_allocator/use_allocator.dg @@ -0,0 +1,34 @@ + +fn main () -> int { + + local *uint8 ptr; + + ptr = malloc(329); + + if ptr == 0 { + return 2; + } + + free(ptr); + + ptr = malloc(8349); + + if ptr == 0 { + return 3; + } + + local *uint8 p; + p = ptr; + + p += 100; + *p = 84; + p += 10; + *p = 92; + p -= 10; + + uint8 res = *p; + + free(ptr); + + return res; +} diff --git a/examples/stdlib/base/allocator/use_allocator/use_allocator.exitcode b/examples/stdlib/base/allocator/use_allocator/use_allocator.exitcode new file mode 100644 index 000000000..871727de1 --- /dev/null +++ b/examples/stdlib/base/allocator/use_allocator/use_allocator.exitcode @@ -0,0 +1 @@ +84 diff --git a/examples/stdlib/base/allocator/use_allocator/use_allocator.stdlib b/examples/stdlib/base/allocator/use_allocator/use_allocator.stdlib new file mode 100644 index 000000000..e69de29bb diff --git a/stdlib/base/allocator.dg b/stdlib/base/allocator.dg new file mode 100644 index 000000000..f3d15aed9 --- /dev/null +++ b/stdlib/base/allocator.dg @@ -0,0 +1,108 @@ + +// This is a really bad allocator. +// It allocates in multiples of page size. + +fn malloc(uint64 nbytes) -> *# { + + // PROT_READ = 0x1 + // PROT_WRITE = 0x2 + + // MAP_SHARED = 0x1 + // MAP_ANONYMOUS = 0x20 + + if nbytes == 0 { + return 0; + } + + uint64 n = 4096; + uint64 npages = 1; + + while n < nbytes { + n += 4096; + npages++; + } + + local *uint64 ptr; + ptr = mmap(0, n, 0x3, 0x21, -1, 0); + + // preserve number of bytes for calling munmap later + *ptr = n; + + return ptr + 16; +} + +// @returns 0 on success +fn free(*# ptr) -> int { + + *uint64 p = ptr; + + if p == 0 { + return -1; + } + + // obtain the real address + p -= 16; + + uint64 nbytes = *p; + + return munmap(p, nbytes); +} + +fn realloc(*# ptr, uint64 nbytes) -> *# { + + if ptr == 0 { + return malloc(nbytes); + } + + if nbytes == 0 { + free(ptr); + return 0; + } + + *uint64 p = ptr; + p -= 16; + uint64 old_size = *p; + + uint64 n = 4096; + uint64 npages = 1; + while n < nbytes { + n += 4096; + npages++; + } + + if (n <= old_size) { + return ptr; + } + + local *uint64 new_ptr; + new_ptr = malloc(nbytes); + + if new_ptr == 0 { + return 0; + } + + uint64 copy_size = old_size - 16; + if (copy_size > nbytes) { + copy_size = nbytes; + } + + local *# dst; + dst = new_ptr; + local *uint8 src; + src = ptr; + + *uint8 d = dst; + + uint64 i = 0; + while i < copy_size { + *d = *src; + d++; + src++; + i++; + } + + free(ptr); + + return dst; +} +