diff --git a/.github/workflows/msys2-cross-compile.yml b/.github/workflows/msys2-cross-compile.yml new file mode 100644 index 0000000..770ea3b --- /dev/null +++ b/.github/workflows/msys2-cross-compile.yml @@ -0,0 +1,20 @@ +name: MSYS2 +on: [push, pull_request] + +jobs: + msys2-mingw64: + runs-on: windows-latest + defaults: + run: + shell: msys2 {0} + steps: + - uses: actions/checkout@v3 + - uses: msys2/setup-msys2@v2 + with: + msystem: MINGW64 + update: true + install: mingw-w64-x86_64-gcc mingw-w64-x86_64-glib2 bison flex mingw-w64-x86_64-llvm cmake git make + - name: CI-Build + run: | + echo 'Running in MSYS2!' + ./ci-build.sh \ No newline at end of file diff --git a/README.md b/README.md index faca249..daaaf32 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,31 @@ -# Gemstone -Gemstone is a programming language compiler written in C with lex and yacc. +
+ + gemstone logo + +
+
+ +## Gemstone + +Gemstone is a programming language compiler (short: GSC) written in C based on flex and GNU bison. +It uses LLVM to produce optimized native binaries for many platforms and uses its own builtin build system for more complex project management. ## Dependencies (build) ### Windows 11 -For setup instruction see issue #30 - -Requires: -- Microsoft Build Tools 2022 (includes: CMake, MSVC) -- WinFlexBison [find it here](https://github.com/lexxmark/winflexbison) (needs to be in PATH) +#### MSYS2 +Install MSYS2 under Windows 11. Open the MingGW64 environment. +Install the following packages: +``` +pacman -S mingw-w64-x86_64-gcc mingw-w64-x86_64-glib2 bison flex mingw-w64-x86_64-llvm cmake git make +``` +Clone the repository and build the gemstone compiler: +``` +cmake . && make release +``` ### GNU/Linux Requires: @@ -20,6 +34,8 @@ Requires: - Make - bison - flex +- LLVM +- Glib 2.0 ## Writing Tests diff --git a/ci-build.sh b/ci-build.sh new file mode 100755 index 0000000..8b09b06 --- /dev/null +++ b/ci-build.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +cmake . +make release diff --git a/src/ast/ast.c b/src/ast/ast.c index 4e3ba6f..a9c1af6 100644 --- a/src/ast/ast.c +++ b/src/ast/ast.c @@ -1,121 +1,121 @@ #include #include -#include #include #include +#include struct AST_Node_t *AST_new_node(TokenLocation location, enum AST_SyntaxElement_t kind, const char* value) { - DEBUG("creating new AST node: %d \"%s\"", kind, value); - assert(kind < AST_ELEMENT_COUNT); + DEBUG("creating new AST node: %d \"%s\"", kind, value); + assert(kind < AST_ELEMENT_COUNT); - struct AST_Node_t *node = malloc(sizeof(struct AST_Node_t)); + struct AST_Node_t *node = mem_alloc(MemoryNamespaceAst, sizeof(struct AST_Node_t)); - if (node == NULL) { - PANIC("failed to allocate AST node"); - } + if (node == NULL) { + PANIC("failed to allocate AST node"); + } - assert(node != NULL); + assert(node != NULL); - // init to discrete state - node->parent = NULL; - node->children = NULL; - node->child_count = 0; - node->kind = kind; - node->value = value; - node->location = location; + // init to discrete state + node->parent = NULL; + node->children = NULL; + node->child_count = 0; + node->kind = kind; + node->value = value; + node->location = location; - return node; + return node; } static const char* lookup_table[AST_ELEMENT_COUNT] = { "__UNINIT__" }; void AST_init() { - DEBUG("initializing global syntax tree..."); + DEBUG("initializing global syntax tree..."); - INFO("filling lookup table..."); - lookup_table[AST_Stmt] = "stmt"; - lookup_table[AST_Module] = "module"; - lookup_table[AST_Expr] = "expr"; + INFO("filling lookup table..."); + lookup_table[AST_Stmt] = "stmt"; + lookup_table[AST_Module] = "module"; + lookup_table[AST_Expr] = "expr"; - lookup_table[AST_Add] = "+"; - lookup_table[AST_Sub] = "-"; - lookup_table[AST_Mul] = "*"; - lookup_table[AST_Div] = "/"; + lookup_table[AST_Add] = "+"; + lookup_table[AST_Sub] = "-"; + lookup_table[AST_Mul] = "*"; + lookup_table[AST_Div] = "/"; - lookup_table[AST_BitAnd] = "&"; - lookup_table[AST_BitOr] = "|"; - lookup_table[AST_BitXor] = "^"; - lookup_table[AST_BitNot] = "!"; + lookup_table[AST_BitAnd] = "&"; + lookup_table[AST_BitOr] = "|"; + lookup_table[AST_BitXor] = "^"; + lookup_table[AST_BitNot] = "!"; - lookup_table[AST_Eq] = "=="; - lookup_table[AST_Less] = "<"; - lookup_table[AST_Greater] = ">"; + lookup_table[AST_Eq] = "=="; + lookup_table[AST_Less] = "<"; + lookup_table[AST_Greater] = ">"; - lookup_table[AST_BoolAnd] = "&&"; - lookup_table[AST_BoolOr] = "||"; - lookup_table[AST_BoolXor] = "^^"; - lookup_table[AST_BoolNot] = "!!"; + lookup_table[AST_BoolAnd] = "&&"; + lookup_table[AST_BoolOr] = "||"; + lookup_table[AST_BoolXor] = "^^"; + lookup_table[AST_BoolNot] = "!!"; - lookup_table[AST_While] = "while"; - lookup_table[AST_If] = "if"; - lookup_table[AST_IfElse] = "else if"; - lookup_table[AST_Else] = "else"; + lookup_table[AST_While] = "while"; + lookup_table[AST_If] = "if"; + lookup_table[AST_IfElse] = "else if"; + lookup_table[AST_Else] = "else"; - lookup_table[AST_Decl] = "decl"; - lookup_table[AST_Assign] = "assign"; - lookup_table[AST_Def] = "def"; + lookup_table[AST_Decl] = "decl"; + lookup_table[AST_Assign] = "assign"; + lookup_table[AST_Def] = "def"; - lookup_table[AST_Typedef] = "typedef"; - lookup_table[AST_Box] = "box"; - lookup_table[AST_Fun] = "fun"; + lookup_table[AST_Typedef] = "typedef"; + lookup_table[AST_Box] = "box"; + lookup_table[AST_Fun] = "fun"; - lookup_table[AST_Call] = "funcall"; - lookup_table[AST_Typecast] = "typecast"; - lookup_table[AST_Transmute] = "transmute"; - lookup_table[AST_Condition] = "condition"; - lookup_table[AST_List] = "list"; - lookup_table[AST_ExprList] = "expr list"; - lookup_table[AST_ArgList] = "arg list"; - lookup_table[AST_ParamList] = "param list"; - lookup_table[AST_StmtList] = "stmt list"; - lookup_table[AST_IdentList] = "ident list"; - lookup_table[AST_Type] = "type"; - lookup_table[AST_Negate] = "-"; - lookup_table[AST_Parameter] = "parameter"; - lookup_table[AST_ParamDecl] = "parameter-declaration"; - lookup_table[AST_AddressOf] = "address of"; - lookup_table[AST_Dereference] = "deref"; - lookup_table[AST_Reference] = "ref"; + lookup_table[AST_Call] = "funcall"; + lookup_table[AST_Typecast] = "typecast"; + lookup_table[AST_Transmute] = "transmute"; + lookup_table[AST_Condition] = "condition"; + lookup_table[AST_List] = "list"; + lookup_table[AST_ExprList] = "expr list"; + lookup_table[AST_ArgList] = "arg list"; + lookup_table[AST_ParamList] = "param list"; + lookup_table[AST_StmtList] = "stmt list"; + lookup_table[AST_IdentList] = "ident list"; + lookup_table[AST_Type] = "type"; + lookup_table[AST_Negate] = "-"; + lookup_table[AST_Parameter] = "parameter"; + lookup_table[AST_ParamDecl] = "parameter-declaration"; + lookup_table[AST_AddressOf] = "address of"; + lookup_table[AST_Dereference] = "deref"; + lookup_table[AST_Reference] = "ref"; } const char* AST_node_to_string(const struct AST_Node_t* node) { - DEBUG("converting AST node to string: %p", node); - assert(node != NULL); + DEBUG("converting AST node to string: %p", node); + assert(node != NULL); - const char* string; + const char* string; - switch(node->kind) { - case AST_Int: - case AST_Float: - case AST_String: - case AST_Ident: - case AST_Macro: - case AST_Import: - case AST_Storage: - case AST_Typekind: - case AST_Sign: - case AST_Scale: - case AST_Qualifyier: - string = node->value; - break; - default: - string = lookup_table[node->kind]; - } + switch(node->kind) { + case AST_Int: + case AST_Float: + case AST_String: + case AST_Ident: + case AST_Macro: + case AST_Import: + case AST_Storage: + case AST_Typekind: + case AST_Sign: + case AST_Scale: + case AST_Qualifyier: + string = node->value; + break; + default: + string = lookup_table[node->kind]; + } - assert(string != NULL); + assert(string != NULL); - return string; + return string; } static inline unsigned long int min(unsigned long int a, unsigned long int b) { @@ -127,181 +127,180 @@ static inline unsigned long int max(unsigned long int a, unsigned long int b) { } void AST_push_node(struct AST_Node_t *owner, struct AST_Node_t *child) { - DEBUG("Adding new node %p to %p", child, owner); - assert(owner != NULL); - assert(child != NULL); + DEBUG("Adding new node %p to %p", child, owner); + assert(owner != NULL); + assert(child != NULL); - // if there are no children for now - if (owner->child_count == 0) { - DEBUG("Allocating new children array"); - owner->children = malloc(sizeof(struct AST_Node_t *)); + // if there are no children for now + if (owner->child_count == 0) { + DEBUG("Allocating new children array"); + owner->children = mem_alloc(MemoryNamespaceAst, sizeof(struct AST_Node_t *)); - } else { - DEBUG("Rellocating old children array"); - const size_t size = sizeof(struct AST_Node_t *) * (owner->child_count + 1); - owner->children = realloc(owner->children, size); - } + } else { + DEBUG("Rellocating old children array"); + const size_t size = sizeof(struct AST_Node_t *) * (owner->child_count + 1); + owner->children = mem_realloc(MemoryNamespaceAst, owner->children, size); + } - if (owner->children == NULL) { - PANIC("failed to allocate children array of AST node"); - } + if (owner->children == NULL) { + PANIC("failed to allocate children array of AST node"); + } - owner->location.col_end = max(owner->location.col_end, child->location.col_end); - owner->location.line_end = max(owner->location.line_end, child->location.line_end); + owner->location.col_end = max(owner->location.col_end, child->location.col_end); + owner->location.line_end = max(owner->location.line_end, child->location.line_end); - owner->location.col_start = min(owner->location.col_start, child->location.col_start); - owner->location.line_start = min(owner->location.line_start, child->location.line_start); + owner->location.col_start = min(owner->location.col_start, child->location.col_start); + owner->location.line_start = min(owner->location.line_start, child->location.line_start); - assert(owner->children != NULL); + assert(owner->children != NULL); - owner->children[owner->child_count++] = child; + owner->children[owner->child_count++] = child; } struct AST_Node_t *AST_get_node(struct AST_Node_t *owner, const size_t idx) { - DEBUG("retrvieng node %d from %p", idx, owner); - assert(owner != NULL); - assert(owner->children != NULL); - assert(idx < owner->child_count); + DEBUG("retrvieng node %d from %p", idx, owner); + assert(owner != NULL); + assert(owner->children != NULL); + assert(idx < owner->child_count); - if (owner->children == NULL) { - PANIC("AST owner node has no children"); - } + if (owner->children == NULL) { + PANIC("AST owner node has no children"); + } - struct AST_Node_t *child = owner->children[idx]; + struct AST_Node_t *child = owner->children[idx]; - if (child == NULL) { - PANIC("child node is NULL"); - } + if (child == NULL) { + PANIC("child node is NULL"); + } - return child; + return child; } struct AST_Node_t* AST_remove_child(struct AST_Node_t* owner, const size_t idx) { - assert(owner != NULL); - assert(owner->children != NULL); - assert(idx < owner->child_count); + assert(owner != NULL); + assert(owner->children != NULL); + assert(idx < owner->child_count); - struct AST_Node_t* child = owner->children[idx]; + struct AST_Node_t* child = owner->children[idx]; - child->parent = NULL; + child->parent = NULL; - owner->child_count--; + owner->child_count--; - // shift back every following element by one - for (size_t i = idx; i < owner->child_count; i++) { - owner->children[i] = owner->children[i + 1]; - } + // shift back every following element by one + for (size_t i = idx; i < owner->child_count; i++) { + owner->children[i] = owner->children[i + 1]; + } - return child; + return child; } struct AST_Node_t* AST_detach_child(struct AST_Node_t* owner, const struct AST_Node_t* child) { - assert(owner != NULL); - assert(child != NULL); - assert(owner->children != NULL); + assert(owner != NULL); + assert(child != NULL); + assert(owner->children != NULL); - for (size_t i = 0; i < owner->child_count; i++) { - if (owner->children[i] == child) { - return AST_remove_child(owner, i); + for (size_t i = 0; i < owner->child_count; i++) { + if (owner->children[i] == child) { + return AST_remove_child(owner, i); + } } - } - PANIC("Child to detach not a child of parent"); + PANIC("Child to detach not a child of parent"); } void AST_delete_node(struct AST_Node_t *node) { - assert(node != NULL); + assert(node != NULL); - DEBUG("Deleting AST node: %p", node); + DEBUG("Deleting AST node: %p", node); - if (node->children == NULL) { - return; - } + if (node->parent != NULL) { + [[maybe_unused]] + const struct AST_Node_t* child = AST_detach_child(node->parent, node); + assert(child == node); + } - if (node->parent != NULL) { - const struct AST_Node_t* child = AST_detach_child(node->parent, node); - assert(child == node); - } + if (node->children != NULL) { + for (size_t i = 0; i < node->child_count; i++) { + // prevent detach of children node + node->children[i]->parent = NULL; + AST_delete_node(node->children[i]); + } + mem_free(node->children); + } - for (size_t i = 0; i < node->child_count; i++) { - // prevent detach of children node - node->children[i]->parent = NULL; - AST_delete_node(node->children[i]); - } - - free(node->children); - free(node); + mem_free(node); } static void AST_visit_nodes_recurse2(struct AST_Node_t *root, - void (*for_each)(struct AST_Node_t *node, - size_t depth), - const size_t depth) { - DEBUG("Recursive visit of %p at %d with %p", root, depth, for_each); + void (*for_each)(struct AST_Node_t *node, + size_t depth), + const size_t depth) { + DEBUG("Recursive visit of %p at %d with %p", root, depth, for_each); - assert(root != NULL); + assert(root != NULL); - (for_each)(root, depth); + (for_each)(root, depth); - for (size_t i = 0; i < root->child_count; i++) { - AST_visit_nodes_recurse2(root->children[i], for_each, depth + 1); - } + for (size_t i = 0; i < root->child_count; i++) { + AST_visit_nodes_recurse2(root->children[i], for_each, depth + 1); + } } void AST_visit_nodes_recurse(struct AST_Node_t *root, void (*for_each)(struct AST_Node_t *node, size_t depth)) { - DEBUG("Starting recursive visit of %p with %p", root, for_each); + DEBUG("Starting recursive visit of %p with %p", root, for_each); - assert(root != NULL); - assert(for_each != NULL); + assert(root != NULL); + assert(for_each != NULL); - AST_visit_nodes_recurse2(root, for_each, 0); + AST_visit_nodes_recurse2(root, for_each, 0); } static void AST_fprint_graphviz_node_definition(FILE* stream, const struct AST_Node_t* node) { - DEBUG("Printing graphviz definition of %p", node); + DEBUG("Printing graphviz definition of %p", node); - assert(stream != NULL); - assert(node != NULL); + assert(stream != NULL); + assert(node != NULL); - fprintf(stream, "\tnode%p [label=\"%s\"]\n", (void*) node, AST_node_to_string(node)); + fprintf(stream, "\tnode%p [label=\"%s\"]\n", (void*) node, AST_node_to_string(node)); - if (node->children == NULL) { - return; - } + if (node->children == NULL) { + return; + } - for (size_t i = 0; i < node->child_count; i++) { - AST_fprint_graphviz_node_definition(stream, node->children[i]); - } + for (size_t i = 0; i < node->child_count; i++) { + AST_fprint_graphviz_node_definition(stream, node->children[i]); + } } static void AST_fprint_graphviz_node_connection(FILE* stream, const struct AST_Node_t* node) { - DEBUG("Printing graphviz connection of %p", node); + DEBUG("Printing graphviz connection of %p", node); - assert(stream != NULL); - assert(node != NULL); + assert(stream != NULL); + assert(node != NULL); - if (node->children == NULL) { - return; - } + if (node->children == NULL) { + return; + } - for (size_t i = 0; i < node->child_count; i++) { - fprintf(stream, "\tnode%p -- node%p\n", (void*) node, (void*) node->children[i]); - AST_fprint_graphviz_node_connection(stream, node->children[i]); - } + for (size_t i = 0; i < node->child_count; i++) { + fprintf(stream, "\tnode%p -- node%p\n", (void*) node, (void*) node->children[i]); + AST_fprint_graphviz_node_connection(stream, node->children[i]); + } } void AST_fprint_graphviz(FILE* stream, const struct AST_Node_t* root) { - DEBUG("Starting print of graphviz graph of %p", root); + DEBUG("Starting print of graphviz graph of %p", root); - assert(stream != NULL); - assert(root != NULL); + assert(stream != NULL); + assert(root != NULL); - fprintf(stream, "graph {\n"); + fprintf(stream, "graph {\n"); - AST_fprint_graphviz_node_definition(stream, root); - AST_fprint_graphviz_node_connection(stream, root); + AST_fprint_graphviz_node_definition(stream, root); + AST_fprint_graphviz_node_connection(stream, root); - fprintf(stream, "}\n"); + fprintf(stream, "}\n"); } diff --git a/src/ast/ast.h b/src/ast/ast.h index 519f10a..da3cd41 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -12,74 +12,74 @@ * variants of this enum. */ enum AST_SyntaxElement_t { - AST_Stmt = 0, - AST_Module, - AST_Expr, - // Literals - AST_Int, - AST_Float, - AST_String, - // Control flow - AST_While, - AST_If, - AST_IfElse, - AST_Else, - AST_Condition, - // Variable management - AST_Decl, - AST_Assign, - AST_Def, - AST_Ident, - // Arithmetic operators - AST_Add, - AST_Sub, - AST_Mul, - AST_Div, - // Bitwise operators - AST_BitAnd, - AST_BitOr, - AST_BitXor, - AST_BitNot, - // Boolean operators - AST_BoolAnd, - AST_BoolOr, - AST_BoolXor, - AST_BoolNot, - // Logical operators - AST_Eq, - AST_Greater, - AST_Less, - // Casts - AST_Typecast, // type cast - AST_Transmute, // reinterpret cast - AST_Call, // function call - AST_Macro, // builtin functions: lineno(), filename(), ... - // Defintions - AST_Typedef, - AST_Box, - AST_Fun, - AST_Import, - // amount of variants - // in this enums - AST_List, - AST_ExprList, - AST_ArgList, - AST_ParamList, - AST_StmtList, - AST_IdentList, - AST_Storage, - AST_Type, - AST_Typekind, - AST_Sign, - AST_Scale, - AST_Negate, - AST_Parameter, - AST_Qualifyier, - AST_ParamDecl, - AST_AddressOf, - AST_Dereference, - AST_Reference, - AST_ELEMENT_COUNT + AST_Stmt = 0, + AST_Module, + AST_Expr, + // Literals + AST_Int, + AST_Float, + AST_String, + // Control flow + AST_While, + AST_If, + AST_IfElse, + AST_Else, + AST_Condition, + // Variable management + AST_Decl, + AST_Assign, + AST_Def, + AST_Ident, + // Arithmetic operators + AST_Add, + AST_Sub, + AST_Mul, + AST_Div, + // Bitwise operators + AST_BitAnd, + AST_BitOr, + AST_BitXor, + AST_BitNot, + // Boolean operators + AST_BoolAnd, + AST_BoolOr, + AST_BoolXor, + AST_BoolNot, + // Logical operators + AST_Eq, + AST_Greater, + AST_Less, + // Casts + AST_Typecast, // type cast + AST_Transmute, // reinterpret cast + AST_Call, // function call + AST_Macro, // builtin functions: lineno(), filename(), ... + // Defintions + AST_Typedef, + AST_Box, + AST_Fun, + AST_Import, + // amount of variants + // in this enums + AST_List, + AST_ExprList, + AST_ArgList, + AST_ParamList, + AST_StmtList, + AST_IdentList, + AST_Storage, + AST_Type, + AST_Typekind, + AST_Sign, + AST_Scale, + AST_Negate, + AST_Parameter, + AST_Qualifyier, + AST_ParamDecl, + AST_AddressOf, + AST_Dereference, + AST_Reference, + AST_ELEMENT_COUNT }; /** @@ -90,21 +90,21 @@ enum AST_SyntaxElement_t { * - value: A string representing an optional value. Can be a integer literal for kind AST_int */ struct AST_Node_t { - // parent node that owns this node - struct AST_Node_t *parent; + // parent node that owns this node + struct AST_Node_t *parent; - // type of AST node: if, declaration, ... - enum AST_SyntaxElement_t kind; - // optional value: integer literal, string literal, ... - const char* value; + // type of AST node: if, declaration, ... + enum AST_SyntaxElement_t kind; + // optional value: integer literal, string literal, ... + const char* value; - TokenLocation location; + TokenLocation location; - // number of child nodes ownd by this node - // length of children array - size_t child_count; - // variable amount of child nodes - struct AST_Node_t **children; + // number of child nodes ownd by this node + // length of children array + size_t child_count; + // variable amount of child nodes + struct AST_Node_t **children; }; /** @@ -219,4 +219,4 @@ void AST_visit_nodes_recurse(struct AST_Node_t *root, [[gnu::nonnull(1), gnu::nonnull(2)]] void AST_fprint_graphviz(FILE* stream, const struct AST_Node_t* node); -#endif +#endif \ No newline at end of file diff --git a/src/cfg/opt.c b/src/cfg/opt.c index 6de694e..9829cb2 100644 --- a/src/cfg/opt.c +++ b/src/cfg/opt.c @@ -8,6 +8,7 @@ #include #include #include +#include static GHashTable* args = NULL; @@ -17,8 +18,8 @@ static void clean(void) { g_hash_table_iter_init(&iter, args); while (g_hash_table_iter_next(&iter, &key, &value)) { - free(value); - free(key); + mem_free(value); + mem_free(key); } g_hash_table_destroy(args); @@ -30,9 +31,9 @@ void parse_options(int argc, char* argv[]) { atexit(clean); for (int i = 0; i < argc; i++) { - Option* option = malloc(sizeof(Option)); + Option* option = mem_alloc(MemoryNamespaceOpt, sizeof(Option)); option->is_opt = g_str_has_prefix(argv[i], "--"); - option->string = strdup(argv[i] + (option->is_opt ? 2 : 0)); + option->string = mem_strdup(MemoryNamespaceOpt, argv[i] + (option->is_opt ? 2 : 0)); option->index = i; option->value = NULL; @@ -91,15 +92,15 @@ GArray* get_non_options_after(const char* command) { TargetConfig* default_target_config() { DEBUG("generating default target config..."); - TargetConfig* config = malloc(sizeof(TargetConfig)); + TargetConfig* config = mem_alloc(MemoryNamespaceOpt, sizeof(TargetConfig)); - config->name = strdup("out"); + config->name = mem_strdup(MemoryNamespaceOpt, "out"); config->print_ast = false; config->print_asm = false; config->print_ir = false; config->mode = Application; - config->archive_directory = strdup("archive"); - config->output_directory = strdup("bin"); + config->archive_directory = mem_strdup(MemoryNamespaceOpt, "archive"); + config->output_directory = mem_strdup(MemoryNamespaceOpt, "bin"); config->optimization_level = 1; config->root_module = NULL; @@ -141,7 +142,7 @@ TargetConfig* default_target_config_from_args() { const Option* opt = get_option("output"); if (opt->value != NULL) { - config->name = strdup(opt->value); + config->name = mem_strdup(MemoryNamespaceOpt, (char*) opt->value); } } @@ -155,7 +156,7 @@ TargetConfig* default_target_config_from_args() { print_message(Warning, "Got more than one file to compile, using first, ignoring others."); } - config->root_module = strdup( ((char**) files->data) [0]); + config->root_module = mem_strdup(MemoryNamespaceOpt, ((char**) files->data) [0]); g_array_free(files, TRUE); } @@ -178,10 +179,11 @@ void print_help(void) { " --mode=[app|lib] set the compilation mode to either application or library", " --output=name name of output files without extension", "Options:", - " --verbose print logs with level information or higher", - " --debug print debug logs (if not disabled at compile time)", - " --version print the version", - " --help print this hel dialog", + " --verbose print logs with level information or higher", + " --debug print debug logs (if not disabled at compile time)", + " --version print the version", + " --help print this hel dialog", + " --print-memory-stats print statistics of the garbage collector" }; for (unsigned int i = 0; i < sizeof(lines) / sizeof(const char *); i++) { @@ -361,32 +363,32 @@ int load_project_config(ProjectConfig *config) { void delete_target_config(TargetConfig* config) { if (config->root_module != NULL) { - free(config->root_module); + mem_free(config->root_module); } if (config->archive_directory != NULL) { - free(config->archive_directory); + mem_free(config->archive_directory); } if (config->name != NULL) { - free(config->name); + mem_free(config->name); } if (config->output_directory != NULL) { - free(config->output_directory); + mem_free(config->output_directory); } - free(config); + mem_free(config); } void delete_project_config(ProjectConfig* config) { if (config->name != NULL) { - free(config->name); + mem_free(config->name); } if (config->authors != NULL) { g_array_free(config->authors, TRUE); } if (config->desc != NULL) { - free(config->desc); + mem_free(config->desc); } if (config->license != NULL) { - free(config->license); + mem_free(config->license); } if (config->targets != NULL) { GHashTableIter iter; @@ -402,11 +404,11 @@ void delete_project_config(ProjectConfig* config) { g_hash_table_destroy(config->targets); } - free(config); + mem_free_from(MemoryNamespaceOpt, config); } ProjectConfig* default_project_config() { - ProjectConfig* config = malloc(sizeof(ProjectConfig)); + ProjectConfig* config = mem_alloc(MemoryNamespaceOpt, sizeof(ProjectConfig)); config->authors = NULL; config->name = NULL; diff --git a/src/compiler.c b/src/compiler.c index f614c7f..dd2d388 100644 --- a/src/compiler.c +++ b/src/compiler.c @@ -11,6 +11,7 @@ #include #include #include +#include #include extern void yyrestart(FILE *); @@ -150,6 +151,9 @@ static void build_target(ModuleFileStack *unit, const TargetConfig *target) { AST_delete_node(ast); + mem_purge_namespace(MemoryNamespaceLex); + mem_purge_namespace(MemoryNamespaceAst); + print_file_statistics(file); } diff --git a/src/io/files.c b/src/io/files.c index 6e5ab34..9e58a10 100644 --- a/src/io/files.c +++ b/src/io/files.c @@ -8,6 +8,7 @@ #include #include #include +#include #ifdef __unix__ @@ -15,9 +16,15 @@ #define MAX_PATH_BYTES PATH_MAX +#define min(a, b) ((a) > (b) ? (b) : (a)) + #elif defined(_WIN32) || defined(WIN32) #include +// for _fullpath +#include +// for _mkdir +#include #define MAX_PATH_BYTES _MAX_PATH @@ -67,10 +74,6 @@ void delete_files(ModuleFileStack *stack) { // seeking the current line in print_diagnostic() #define SEEK_BUF_BYTES 256 -static inline unsigned long int min(unsigned long int a, unsigned long int b) { - return a > b ? b : a; -} - // behaves like fgets except that it has defined behavior when n == 1 static void custom_fgets(char *buffer, size_t n, FILE *stream) { if (n == 1) { @@ -130,17 +133,17 @@ void print_diagnostic(ModuleFile *file, TokenLocation *location, Message kind, c printf("\n"); - free((void *) absolute_path); + mem_free((void *) absolute_path); - const size_t lines = location->line_end - location->line_start + 1; + const unsigned long int lines = location->line_end - location->line_start + 1; - for (size_t l = 0; l < lines; l++) { + for (unsigned long int l = 0; l < lines; l++) { printf(" %4ld | ", location->line_start + l); - size_t chars = 0; + unsigned long int chars = 0; // print line before token group start - size_t limit = min(location->col_start, SEEK_BUF_BYTES); + unsigned long int limit = min(location->col_start, SEEK_BUF_BYTES); while (limit > 1) { custom_fgets(buffer, (int) limit, file->handle); chars += printf("%s", buffer); @@ -178,13 +181,13 @@ void print_diagnostic(ModuleFile *file, TokenLocation *location, Message kind, c } printf(" | "); - for (size_t i = 1; i < location->col_start; i++) { + for (unsigned long int i = 1; i < location->col_start; i++) { printf(" "); } printf("%s", accent_color); printf("^"); - for (size_t i = 0; i < location->col_end - location->col_start; i++) { + for (unsigned long int i = 0; i < location->col_end - location->col_start; i++) { printf("~"); } @@ -315,7 +318,7 @@ int create_directory(const char *path) { } const char *get_last_error() { - return strdup(strerror(errno)); + return mem_strdup(MemoryNamespaceIo, strerror(errno)); } const char *get_absolute_path(const char *path) { @@ -330,10 +333,10 @@ const char *get_absolute_path(const char *path) { #elif defined(_WIN32) || defined(WIN32) // use Windows CRT specific function char absolute_path[MAX_PATH_BYTES]; - _fullpath(path, absolute_path, _MAX_PATH); + _fullpath((char*) path, absolute_path, _MAX_PATH); #endif - return strdup(absolute_path); + return mem_strdup(MemoryNamespaceIo, absolute_path); } const char* make_file_path(const char* name, const char* ext, int count, ...) { diff --git a/src/io/files.h b/src/io/files.h index 1e436b5..2d6c563 100644 --- a/src/io/files.h +++ b/src/io/files.h @@ -15,9 +15,9 @@ #endif typedef struct FileDiagnosticStatistics_t { - size_t error_count; - size_t warning_count; - size_t info_count; + unsigned long int error_count; + unsigned long int warning_count; + unsigned long int info_count; } FileDiagnosticStatistics; typedef struct ModuleFile_t { diff --git a/src/lex/lexer.l b/src/lex/lexer.l index af4dc21..0491c3b 100644 --- a/src/lex/lexer.l +++ b/src/lex/lexer.l @@ -3,6 +3,7 @@ #include #include #include + #include int yyLineNumber = 1; @@ -89,20 +90,20 @@ "lineno" {DEBUG("\"%s\" tokenized with \'FunLineno\'", yytext); return(FunLineno);}; "extsupport" {DEBUG("\"%s\" tokenized with \'FunExtsupport\'", yytext); return(FunExtsupport);}; -[0-9]+ {DEBUG("\"%s\" tokenized with \'ValInt\'", yytext); yylval.string = strdup(yytext); return(ValInt); }; -[0-9]*\.[0-9]+ {DEBUG("\"%s\" tokenized with \'ValFloat\'", yytext); yylval.string = strdup(yytext); return(ValFloat);}; -[a-zA-Z_0-9]+ {DEBUG("\"%s\" tokenized with \'Ident\'", yytext); yylval.string = strdup(yytext); return(Ident); }; +[0-9]+ {DEBUG("\"%s\" tokenized with \'ValInt\'", yytext); yylval.string = mem_strdup(MemoryNamespaceLex, yytext); return(ValInt); }; +[0-9]*\.[0-9]+ {DEBUG("\"%s\" tokenized with \'ValFloat\'", yytext); yylval.string = mem_strdup(MemoryNamespaceLex, yytext); return(ValFloat);}; +[a-zA-Z_0-9]+ {DEBUG("\"%s\" tokenized with \'Ident\'", yytext); yylval.string = mem_strdup(MemoryNamespaceLex, yytext); return(Ident); }; \"([^\"\n])*\" { yytext = yytext +1; yytext[yyleng - 2] = 0; - DEBUG("\"%s\" tokenized with \'ValStr\'", yytext); yylval.string = strdup(yytext); return(ValStr);}; + DEBUG("\"%s\" tokenized with \'ValStr\'", yytext); yylval.string = mem_strdup(MemoryNamespaceLex, yytext); return(ValStr);}; \"\"\"[^\"]*\"\"\" { yytext = yytext +3; yytext[yyleng - 6] = 0; - DEBUG("\"%s\" tokenized with \'ValMultistr\'", yytext); yylval.string = strdup(yytext); return(ValMultistr);}; + DEBUG("\"%s\" tokenized with \'ValMultistr\'", yytext); yylval.string = mem_strdup(MemoryNamespaceLex, yytext); return(ValMultistr);}; [ \r\t] { /* ignore whitespace */ }; . { return yytext[0]; /* passthrough unknown token, let parser handle the error */ }; %% diff --git a/src/lex/util.c b/src/lex/util.c index 81f2855..83966da 100644 --- a/src/lex/util.c +++ b/src/lex/util.c @@ -2,6 +2,8 @@ #include #include #include +#include +#include // implementation based on: // https://github.com/sunxfancy/flex-bison-examples/blob/master/error-handling/ccalc.c @@ -16,13 +18,8 @@ static int nTokenStart = 0; static int nTokenLength = 0; static int nTokenNextStart = 0; -static void lex_deinit(void) { - free(buffer); -} - void lex_init(void) { - buffer = malloc(MAX_READ_BUFFER_SIZE); - atexit(lex_deinit); + buffer = mem_alloc(MemoryNamespaceStatic, MAX_READ_BUFFER_SIZE); } void lex_reset(void) { diff --git a/src/main.c b/src/main.c index 1f315e1..b987a6a 100644 --- a/src/main.c +++ b/src/main.c @@ -5,6 +5,7 @@ #include #include #include +#include /** * @brief Log a debug message to inform about beginning exit procedures @@ -17,6 +18,8 @@ void notify_exit(void) { DEBUG("Exiting gemstone..."); } * */ void setup(int argc, char *argv[]) { + mem_init(); + // setup preample parse_options(argc, argv); @@ -56,6 +59,10 @@ int main(int argc, char *argv[]) { } run_compiler(); - + + if (is_option_set("print-memory-stats")) { + print_memory_statistics(); + } + return 0; } diff --git a/src/mem/cache.c b/src/mem/cache.c new file mode 100644 index 0000000..57779c2 --- /dev/null +++ b/src/mem/cache.c @@ -0,0 +1,271 @@ +// +// Created by servostar on 6/5/24. +// + +#include +#include +#include +#include +#include + +static GHashTable* namespaces = NULL; + +typedef struct MemoryNamespaceStatistic_t { + size_t bytes_allocated; + size_t allocation_count; + size_t reallocation_count; + size_t manual_free_count; + size_t faulty_reallocations; + size_t faulty_allocations; + size_t purged_free_count; +} MemoryNamespaceStatistic; + +typedef struct MemoryNamespace_t { + MemoryNamespaceStatistic statistic; + GArray* blocks; +} MemoryNamespace; + +typedef MemoryNamespace* MemoryNamespaceRef; + +static void namespace_statistics_print(MemoryNamespaceStatistic* memoryNamespaceStatistic, char* name) { + printf("Memory namespace statistics: `%s`\n", name); + printf("------------------------------\n"); + printf(" allocated bytes: %ld\n", memoryNamespaceStatistic->bytes_allocated); + printf(" allocations: %ld\n", memoryNamespaceStatistic->allocation_count); + printf(" reallocations: %ld\n", memoryNamespaceStatistic->reallocation_count); + printf(" frees: %ld\n", memoryNamespaceStatistic->manual_free_count); + printf(" faulty allocations: %ld\n", memoryNamespaceStatistic->faulty_allocations); + printf(" faulty reallocations: %ld\n", memoryNamespaceStatistic->faulty_reallocations); + printf(" purged allocations: %ld\n", memoryNamespaceStatistic->purged_free_count); + printf("\n"); +} + +static void* namespace_malloc(MemoryNamespaceRef memoryNamespace, size_t size) { + assert(memoryNamespace != NULL); + assert(size != 0); + + void* block = malloc(size); + + if (block == NULL) { + memoryNamespace->statistic.faulty_allocations ++; + } else { + g_array_append_val(memoryNamespace->blocks, block); + + memoryNamespace->statistic.allocation_count ++; + memoryNamespace->statistic.bytes_allocated += size; + } + + return block; +} + +static gboolean namespace_free(MemoryNamespaceRef memoryNamespace, void* block) { + for (guint i = 0; i < memoryNamespace->blocks->len; i++) { + void* current_block = g_array_index(memoryNamespace->blocks, void*, i); + + if (current_block == block) { + assert(block != NULL); + + free(block); + g_array_remove_index(memoryNamespace->blocks, i); + + memoryNamespace->statistic.manual_free_count++; + return TRUE; + } + } + return FALSE; +} + +static void* namespace_realloc(MemoryNamespaceRef memoryNamespace, void* block, size_t size) { + void* reallocated_block = NULL; + + for (guint i = 0; i < memoryNamespace->blocks->len; i++) { + void* current_block = g_array_index(memoryNamespace->blocks, void*, i); + + if (current_block == block) { + reallocated_block = realloc(block, size); + + if (reallocated_block != NULL) { + g_array_index(memoryNamespace->blocks, void*, i) = reallocated_block; + memoryNamespace->statistic.bytes_allocated += size; + memoryNamespace->statistic.reallocation_count ++; + } else { + memoryNamespace->statistic.faulty_reallocations++; + } + + break; + } + } + + return reallocated_block; +} + +static void namespace_delete(MemoryNamespaceRef memoryNamespace) { + g_array_free(memoryNamespace->blocks, TRUE); + free(memoryNamespace); +} + +static void namespace_purge(MemoryNamespaceRef memoryNamespace) { + + for (guint i = 0; i < memoryNamespace->blocks->len; i++) { + void* current_block = g_array_index(memoryNamespace->blocks, void*, i); + + free(current_block); + + memoryNamespace->statistic.purged_free_count ++; + } + + g_array_remove_range(memoryNamespace->blocks, 0, memoryNamespace->blocks->len); +} + +static MemoryNamespaceRef namespace_new() { + MemoryNamespaceRef memoryNamespace = malloc(sizeof(MemoryNamespace)); + + memoryNamespace->blocks = g_array_new(FALSE, FALSE, sizeof(void*)); + memoryNamespace->statistic.bytes_allocated = 0; + memoryNamespace->statistic.allocation_count = 0; + memoryNamespace->statistic.manual_free_count = 0; + memoryNamespace->statistic.faulty_reallocations = 0; + memoryNamespace->statistic.faulty_allocations = 0; + memoryNamespace->statistic.purged_free_count = 0; + memoryNamespace->statistic.reallocation_count = 0; + + return memoryNamespace; +} + +static void cleanup() { + if (namespaces == NULL) { + printf("==> Memory cache was unused <==\n"); + return; + } + + GHashTableIter iter; + char* name = NULL; + MemoryNamespaceRef memoryNamespace = NULL; + + g_hash_table_iter_init(&iter, namespaces); + + while (g_hash_table_iter_next(&iter, (gpointer) &name, (gpointer) &memoryNamespace)) { + assert(name != NULL); + assert(memoryNamespace != NULL); + + namespace_purge(memoryNamespace); + namespace_delete(memoryNamespace); + } +} + +void mem_init() { + atexit(cleanup); +} + +static MemoryNamespaceRef check_namespace(MemoryNamespaceName name) { + if (namespaces == NULL) { + namespaces = g_hash_table_new(g_str_hash, g_str_equal); + } + + if (g_hash_table_contains(namespaces, name)) { + + return g_hash_table_lookup(namespaces, name); + + } else { + MemoryNamespaceRef namespace = namespace_new(); + + g_hash_table_insert(namespaces, name, namespace); + + return namespace; + } +} + +void *mem_alloc(MemoryNamespaceName name, size_t size) { + MemoryNamespaceRef cache = check_namespace(name); + + if (cache == NULL) { + PANIC("memory namespace not created"); + } + + return namespace_malloc(cache, size); +} + +void *mem_realloc(MemoryNamespaceName name, void *ptr, size_t size) { + MemoryNamespaceRef cache = check_namespace(name); + + if (cache == NULL) { + PANIC("memory namespace not created"); + } + + return namespace_realloc(cache, ptr, size); +} + +void mem_free_from(MemoryNamespaceName name, void *memory) { + MemoryNamespaceRef cache = check_namespace(name); + + namespace_free(cache, memory); +} + +void mem_free(void* memory) { + GHashTableIter iter; + char* name; + MemoryNamespaceRef memoryNamespace; + + g_hash_table_iter_init(&iter, namespaces); + while (g_hash_table_iter_next(&iter, (gpointer) &name, (gpointer) &memoryNamespace)) { + + if (namespace_free(memoryNamespace, memory)) { + break; + } + } +} + +void mem_purge_namespace(MemoryNamespaceName name) { + if (g_hash_table_contains(namespaces, name)) { + MemoryNamespaceRef cache = g_hash_table_lookup(namespaces, name); + + namespace_purge(cache); + } else { + PANIC("purging invalid namespace: %s", name); + } +} + +char* mem_strdup(MemoryNamespaceName name, char* string) { + return mem_clone(name, string, strlen(string) + 1); +} + +void* mem_clone(MemoryNamespaceName name, void* data, size_t size) { + void *clone = mem_alloc(name, size); + + memcpy(clone, data, size); + + return clone; +} + +void print_memory_statistics() { + GHashTableIter iter; + char* name; + MemoryNamespaceRef memoryNamespace; + + MemoryNamespaceStatistic total; + total.bytes_allocated = 0; + total.faulty_reallocations = 0; + total.faulty_allocations = 0; + total.manual_free_count = 0; + total.allocation_count = 0; + total.purged_free_count = 0; + total.reallocation_count = 0; + + g_hash_table_iter_init(&iter, namespaces); + while (g_hash_table_iter_next(&iter, (gpointer) &name, (gpointer) &memoryNamespace)) { + + namespace_statistics_print(&memoryNamespace->statistic, name); + + total.bytes_allocated += memoryNamespace->statistic.bytes_allocated; + total.faulty_reallocations += memoryNamespace->statistic.faulty_reallocations; + total.faulty_allocations += memoryNamespace->statistic.faulty_allocations; + total.manual_free_count += memoryNamespace->statistic.manual_free_count; + total.allocation_count += memoryNamespace->statistic.allocation_count; + total.purged_free_count += memoryNamespace->statistic.purged_free_count; + total.reallocation_count += memoryNamespace->statistic.reallocation_count; + } + + namespace_statistics_print(&total, "summary"); + + printf("Note: untracked are memory allocations from external libraries.\n"); +} diff --git a/src/mem/cache.h b/src/mem/cache.h new file mode 100644 index 0000000..d6ac067 --- /dev/null +++ b/src/mem/cache.h @@ -0,0 +1,89 @@ +// +// Created by servostar on 6/5/24. +// + +#ifndef GEMSTONE_CACHE_H +#define GEMSTONE_CACHE_H + +#include +#include + +typedef char* MemoryNamespaceName; + +#define MemoryNamespaceAst "AST" +#define MemoryNamespaceLex "Lexer" +#define MemoryNamespaceLog "Logging" +#define MemoryNamespaceOpt "Options" +#define MemoryNamespaceSet "SET" +#define MemoryNamespaceLlvm "LLVM" +#define MemoryNamespaceIo "I/O" +#define MemoryNamespaceStatic "Static" + +/** + * @brief Initialize the garbage collector. + * Must be done to ensure cleanup of memory. + */ +void mem_init(); + +/** + * @brief Allocate a block of memory in the specified namespace. + * @attention Must only be freed with mem_free() or mem_free_from() + * @param name + * @param size + * @return pointer to the block + */ +void* mem_alloc(MemoryNamespaceName name, size_t size); + +/** + * @brief Reallocate a block of memory in the specified namespace. + * @attention Must only be freed with mem_free() or mem_free_from() + * @param name + * @param size + * @return pointer to the block + */ +void* mem_realloc(MemoryNamespaceName name, void *ptr, size_t size); + +/** + * @brief Free a block of memory from a specified namespace. + * Invoking multiple times on the same pointer will do nothing. + * @param name + * @param memory + */ +void mem_free_from(MemoryNamespaceName name, void* memory); + +/** + * @brief Free a block of memory. + * Invoking multiple times on the same pointer will do nothing. + * @attention In case the namespace of the block is known, consider using mem_free_from() + * to avoid unnecessary overhead. + * @param name + * @param memory + */ +void mem_free(void* memory); + +/** + * @brief Delete all memory from the given namespace. + * @param name + */ +void mem_purge_namespace(MemoryNamespaceName name); + +/** + * @brief Duplicate the given string with memory in the given namespace. + * @param name + * @param string + * @return + */ +char* mem_strdup(MemoryNamespaceName name, char* string); + +/** + * @brief Duplicate the given block of data with memory in the given namespace. + * @param name + * @param data + * @param size + * @return + */ +void* mem_clone(MemoryNamespaceName name, void* data, size_t size); + +void print_memory_statistics(); + +#endif //GEMSTONE_CACHE_H diff --git a/src/sys/col.c b/src/sys/col.c index 76260ec..afd78ba 100644 --- a/src/sys/col.c +++ b/src/sys/col.c @@ -71,8 +71,7 @@ int stdout_supports_ansi_esc() { HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); if (!GetConsoleMode(hConsole, &mode)) { - ERROR("failed to get console mode"); - return ANSI_ENABLED; + return ASNI_DISABLED; } if ((mode & ENABLE_VIRTUAL_TERMINAL_INPUT) | diff --git a/src/sys/log.c b/src/sys/log.c index 7dcf94e..ca00b73 100644 --- a/src/sys/log.c +++ b/src/sys/log.c @@ -6,6 +6,7 @@ #include #include #include +#include static struct Logger_t { FILE** streams; @@ -39,7 +40,7 @@ void log_register_stream(FILE* restrict stream) if (GlobalLogger.stream_count == 0) { - GlobalLogger.streams = (FILE**) malloc(sizeof(FILE*)); + GlobalLogger.streams = (FILE**) mem_alloc(MemoryNamespaceLog, sizeof(FILE*)); GlobalLogger.stream_count = 1; if (GlobalLogger.streams == NULL) @@ -51,7 +52,7 @@ void log_register_stream(FILE* restrict stream) { GlobalLogger.stream_count++; size_t bytes = GlobalLogger.stream_count * sizeof(FILE*); - GlobalLogger.streams = (FILE**) realloc(GlobalLogger.streams, bytes); + GlobalLogger.streams = (FILE**) mem_realloc(MemoryNamespaceLog, GlobalLogger.streams, bytes); if (GlobalLogger.streams == NULL) { diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 761b32c..4ca6308 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -10,4 +10,5 @@ add_subdirectory(logging) add_subdirectory(input_file) add_subdirectory(ast) add_subdirectory(glib) -add_subdirectory(project) \ No newline at end of file +add_subdirectory(project) +add_subdirectory(cache) diff --git a/tests/ast/CMakeLists.txt b/tests/ast/CMakeLists.txt index 5bd19b6..6e20067 100644 --- a/tests/ast/CMakeLists.txt +++ b/tests/ast/CMakeLists.txt @@ -26,6 +26,7 @@ add_executable(ast_build_tree ${PROJECT_SOURCE_DIR}/src/io/files.c ${PROJECT_SOURCE_DIR}/src/sys/col.c ${PROJECT_SOURCE_DIR}/src/cfg/opt.c + ${PROJECT_SOURCE_DIR}/src/mem/cache.c ${PROJECT_SOURCE_DIR}/dep/tomlc99/toml.c build_tree.c) set_target_properties(ast_build_tree @@ -47,6 +48,7 @@ add_executable(ast_print_node ${PROJECT_SOURCE_DIR}/src/io/files.c ${PROJECT_SOURCE_DIR}/src/sys/col.c ${PROJECT_SOURCE_DIR}/src/cfg/opt.c + ${PROJECT_SOURCE_DIR}/src/mem/cache.c ${PROJECT_SOURCE_DIR}/dep/tomlc99/toml.c print_node.c) set_target_properties(ast_print_node @@ -68,6 +70,7 @@ add_executable(ast_graphviz ${PROJECT_SOURCE_DIR}/src/io/files.c ${PROJECT_SOURCE_DIR}/src/sys/col.c ${PROJECT_SOURCE_DIR}/src/cfg/opt.c + ${PROJECT_SOURCE_DIR}/src/mem/cache.c ${PROJECT_SOURCE_DIR}/dep/tomlc99/toml.c print_graphviz.c) set_target_properties(ast_graphviz diff --git a/tests/ast/build_tree.c b/tests/ast/build_tree.c index 2d2459b..ddc3b66 100644 --- a/tests/ast/build_tree.c +++ b/tests/ast/build_tree.c @@ -4,6 +4,7 @@ #include #include +#include void generate_statement(const AST_NODE_PTR stmt) { const AST_NODE_PTR add = AST_new_node(empty_location(), AST_Add, NULL); @@ -29,6 +30,7 @@ void generate_branch(const AST_NODE_PTR stmt) { } int main(void) { + mem_init(); const AST_NODE_PTR root = AST_new_node(empty_location(), AST_Stmt, NULL); diff --git a/tests/ast/gen_graph.c b/tests/ast/gen_graph.c index c4e2f92..53ef3cc 100644 --- a/tests/ast/gen_graph.c +++ b/tests/ast/gen_graph.c @@ -4,8 +4,10 @@ #include #include +#include int main(void) { + mem_init(); struct AST_Node_t* node = AST_new_node(empty_location(), AST_If, NULL); diff --git a/tests/ast/print_graphviz.c b/tests/ast/print_graphviz.c index c0f69af..888a29d 100644 --- a/tests/ast/print_graphviz.c +++ b/tests/ast/print_graphviz.c @@ -4,6 +4,7 @@ #include #include +#include void generate_statement(const AST_NODE_PTR stmt) { const AST_NODE_PTR add = AST_new_node(empty_location(), AST_Add, NULL); @@ -29,7 +30,7 @@ void generate_branch(const AST_NODE_PTR stmt) { } int main(void) { - + mem_init(); AST_init(); const AST_NODE_PTR root = AST_new_node(empty_location(), AST_Stmt, NULL); diff --git a/tests/ast/print_node.c b/tests/ast/print_node.c index 47db685..b76285a 100644 --- a/tests/ast/print_node.c +++ b/tests/ast/print_node.c @@ -3,9 +3,10 @@ // #include +#include int main(void) { - + mem_init(); AST_init(); const AST_NODE_PTR node = AST_new_node(empty_location(), 0, "value"); diff --git a/tests/cache/CMakeLists.txt b/tests/cache/CMakeLists.txt new file mode 100644 index 0000000..586cef3 --- /dev/null +++ b/tests/cache/CMakeLists.txt @@ -0,0 +1,38 @@ +include(CTest) + +include_directories(${PROJECT_SOURCE_DIR}/src) + +# ------------------------------------------------ # +# Setup Glib 2.0 # +# ------------------------------------------------ # + +find_package(PkgConfig REQUIRED) +pkg_search_module(GLIB REQUIRED IMPORTED_TARGET glib-2.0) +include_directories(PRIVATE ${GLIB_INCLUDE_DIRS}) + +# ------------------------------------------------ # +# Setup TOML-C99 # +# ------------------------------------------------ # + +include_directories(${PROJECT_SOURCE_DIR}/dep/tomlc99) + +# ------------------------------------------------------- # +# CTEST 1 +# test the memory cache + +add_executable(cache + ${PROJECT_SOURCE_DIR}/src/sys/log.c + ${PROJECT_SOURCE_DIR}/src/sys/col.c + ${PROJECT_SOURCE_DIR}/src/cfg/opt.c + ${PROJECT_SOURCE_DIR}/src/io/files.c + ${PROJECT_SOURCE_DIR}/src/mem/cache.c + cache_test.c) +set_target_properties(cache + PROPERTIES + OUTPUT_NAME "cache" + RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/tests/cache) +target_link_libraries(cache PkgConfig::GLIB) +target_link_libraries(cache tomlc99) +add_test(NAME cache + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + COMMAND ${GEMSTONE_BINARY_DIR}/tests/cache/cache) diff --git a/tests/cache/cache_test.c b/tests/cache/cache_test.c new file mode 100644 index 0000000..c49094a --- /dev/null +++ b/tests/cache/cache_test.c @@ -0,0 +1,28 @@ +// +// Created by servostar on 6/5/24. +// + +#include +#include +#include +#include + +int main(int argc, char* argv[]) { + mem_init(); + parse_options(argc, argv); + log_init(); + set_log_level(LOG_LEVEL_DEBUG); + col_init(); + + for (int i = 0; i < 3; i++) { + void* data = mem_alloc(MemoryNamespaceAst, 457); + mem_realloc(MemoryNamespaceAst, data, 200); + mem_free(data); + } + + mem_purge_namespace(MemoryNamespaceOpt); + + print_memory_statistics(); + + return 0; +} diff --git a/tests/logging/CMakeLists.txt b/tests/logging/CMakeLists.txt index 2bfa385..5e9cdd0 100644 --- a/tests/logging/CMakeLists.txt +++ b/tests/logging/CMakeLists.txt @@ -25,6 +25,7 @@ add_executable(logging_output ${PROJECT_SOURCE_DIR}/src/sys/col.c ${PROJECT_SOURCE_DIR}/src/cfg/opt.c ${PROJECT_SOURCE_DIR}/src/io/files.c + ${PROJECT_SOURCE_DIR}/src/mem/cache.c output.c) set_target_properties(logging_output PROPERTIES @@ -45,6 +46,7 @@ add_executable(logging_panic ${PROJECT_SOURCE_DIR}/src/sys/col.c ${PROJECT_SOURCE_DIR}/src/cfg/opt.c ${PROJECT_SOURCE_DIR}/src/io/files.c + ${PROJECT_SOURCE_DIR}/src/mem/cache.c panic.c) set_target_properties(logging_panic PROPERTIES @@ -65,6 +67,7 @@ add_executable(logging_streams ${PROJECT_SOURCE_DIR}/src/sys/col.c ${PROJECT_SOURCE_DIR}/src/cfg/opt.c ${PROJECT_SOURCE_DIR}/src/io/files.c + ${PROJECT_SOURCE_DIR}/src/mem/cache.c streams.c) set_target_properties(logging_streams PROPERTIES @@ -85,6 +88,7 @@ add_executable(logging_level ${PROJECT_SOURCE_DIR}/src/sys/col.c ${PROJECT_SOURCE_DIR}/src/cfg/opt.c ${PROJECT_SOURCE_DIR}/src/io/files.c + ${PROJECT_SOURCE_DIR}/src/mem/cache.c level.c) set_target_properties(logging_level PROPERTIES diff --git a/tests/logging/level.c b/tests/logging/level.c index ed89205..c46cf5d 100644 --- a/tests/logging/level.c +++ b/tests/logging/level.c @@ -5,10 +5,12 @@ #include "sys/log.h" #include #include +#include #define LOG_LEVEL LOG_LEVEL_WARNING int main(int argc, char* argv[]) { + mem_init(); parse_options(argc, argv); log_init(); set_log_level(LOG_LEVEL_DEBUG); diff --git a/tests/logging/output.c b/tests/logging/output.c index a6cfdb2..b64840f 100644 --- a/tests/logging/output.c +++ b/tests/logging/output.c @@ -5,8 +5,10 @@ #include "sys/log.h" #include #include +#include int main(int argc, char* argv[]) { + mem_init(); parse_options(argc, argv); log_init(); set_log_level(LOG_LEVEL_DEBUG); diff --git a/tests/logging/panic.c b/tests/logging/panic.c index f64f3a1..5502dd9 100644 --- a/tests/logging/panic.c +++ b/tests/logging/panic.c @@ -4,8 +4,10 @@ #include "sys/log.h" #include +#include int main(int argc, char* argv[]) { + mem_init(); parse_options(argc, argv); log_init(); set_log_level(LOG_LEVEL_DEBUG); diff --git a/tests/logging/streams.c b/tests/logging/streams.c index 5dd7cc2..ded5461 100644 --- a/tests/logging/streams.c +++ b/tests/logging/streams.c @@ -5,6 +5,7 @@ #include "sys/log.h" #include #include +#include static FILE* file; @@ -15,6 +16,7 @@ void close_file(void) { } int main(int argc, char* argv[]) { + mem_init(); parse_options(argc, argv); log_init(); set_log_level(LOG_LEVEL_DEBUG);