Merge remote-tracking branch 'origin/main' into 85-implement-llvm-backend

# Conflicts:
#	src/ast/ast.h
#	src/cfg/opt.c
#	src/compiler.c
#	src/io/files.c
#	src/main.c
#	tests/CMakeLists.txt
This commit is contained in:
Sven Vogel 2024-06-08 22:39:51 +02:00
commit 77ef7d0f07
29 changed files with 842 additions and 340 deletions

View File

@ -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

View File

@ -1,17 +1,31 @@
# Gemstone
Gemstone is a programming language compiler written in C with lex and yacc. <div align="center">
<picture>
<img alt="gemstone logo" src="https://github.com/Servostar/gemstone/assets/72654954/fdb37c1b-81ca-4e6a-a9e9-c46effb12dae" width="75%">
</picture>
</div>
<br>
## 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) ## Dependencies (build)
### Windows 11 ### Windows 11
For setup instruction see issue #30 #### MSYS2
Requires:
- Microsoft Build Tools 2022 (includes: CMake, MSVC)
- WinFlexBison [find it here](https://github.com/lexxmark/winflexbison) (needs to be in PATH)
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 ### GNU/Linux
Requires: Requires:
@ -20,6 +34,8 @@ Requires:
- Make - Make
- bison - bison
- flex - flex
- LLVM
- Glib 2.0
## Writing Tests ## Writing Tests

4
ci-build.sh Executable file
View File

@ -0,0 +1,4 @@
#!/bin/sh
cmake .
make release

View File

@ -1,121 +1,121 @@
#include <ast/ast.h> #include <ast/ast.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include <sys/log.h> #include <sys/log.h>
#include <assert.h> #include <assert.h>
#include <mem/cache.h>
struct AST_Node_t *AST_new_node(TokenLocation location, enum AST_SyntaxElement_t kind, const char* value) { 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); DEBUG("creating new AST node: %d \"%s\"", kind, value);
assert(kind < AST_ELEMENT_COUNT); 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) { if (node == NULL) {
PANIC("failed to allocate AST node"); PANIC("failed to allocate AST node");
} }
assert(node != NULL); assert(node != NULL);
// init to discrete state // init to discrete state
node->parent = NULL; node->parent = NULL;
node->children = NULL; node->children = NULL;
node->child_count = 0; node->child_count = 0;
node->kind = kind; node->kind = kind;
node->value = value; node->value = value;
node->location = location; node->location = location;
return node; return node;
} }
static const char* lookup_table[AST_ELEMENT_COUNT] = { "__UNINIT__" }; static const char* lookup_table[AST_ELEMENT_COUNT] = { "__UNINIT__" };
void AST_init() { void AST_init() {
DEBUG("initializing global syntax tree..."); DEBUG("initializing global syntax tree...");
INFO("filling lookup table..."); INFO("filling lookup table...");
lookup_table[AST_Stmt] = "stmt"; lookup_table[AST_Stmt] = "stmt";
lookup_table[AST_Module] = "module"; lookup_table[AST_Module] = "module";
lookup_table[AST_Expr] = "expr"; lookup_table[AST_Expr] = "expr";
lookup_table[AST_Add] = "+"; lookup_table[AST_Add] = "+";
lookup_table[AST_Sub] = "-"; lookup_table[AST_Sub] = "-";
lookup_table[AST_Mul] = "*"; lookup_table[AST_Mul] = "*";
lookup_table[AST_Div] = "/"; lookup_table[AST_Div] = "/";
lookup_table[AST_BitAnd] = "&"; lookup_table[AST_BitAnd] = "&";
lookup_table[AST_BitOr] = "|"; lookup_table[AST_BitOr] = "|";
lookup_table[AST_BitXor] = "^"; lookup_table[AST_BitXor] = "^";
lookup_table[AST_BitNot] = "!"; lookup_table[AST_BitNot] = "!";
lookup_table[AST_Eq] = "=="; lookup_table[AST_Eq] = "==";
lookup_table[AST_Less] = "<"; lookup_table[AST_Less] = "<";
lookup_table[AST_Greater] = ">"; lookup_table[AST_Greater] = ">";
lookup_table[AST_BoolAnd] = "&&"; lookup_table[AST_BoolAnd] = "&&";
lookup_table[AST_BoolOr] = "||"; lookup_table[AST_BoolOr] = "||";
lookup_table[AST_BoolXor] = "^^"; lookup_table[AST_BoolXor] = "^^";
lookup_table[AST_BoolNot] = "!!"; lookup_table[AST_BoolNot] = "!!";
lookup_table[AST_While] = "while"; lookup_table[AST_While] = "while";
lookup_table[AST_If] = "if"; lookup_table[AST_If] = "if";
lookup_table[AST_IfElse] = "else if"; lookup_table[AST_IfElse] = "else if";
lookup_table[AST_Else] = "else"; lookup_table[AST_Else] = "else";
lookup_table[AST_Decl] = "decl"; lookup_table[AST_Decl] = "decl";
lookup_table[AST_Assign] = "assign"; lookup_table[AST_Assign] = "assign";
lookup_table[AST_Def] = "def"; lookup_table[AST_Def] = "def";
lookup_table[AST_Typedef] = "typedef"; lookup_table[AST_Typedef] = "typedef";
lookup_table[AST_Box] = "box"; lookup_table[AST_Box] = "box";
lookup_table[AST_Fun] = "fun"; lookup_table[AST_Fun] = "fun";
lookup_table[AST_Call] = "funcall"; lookup_table[AST_Call] = "funcall";
lookup_table[AST_Typecast] = "typecast"; lookup_table[AST_Typecast] = "typecast";
lookup_table[AST_Transmute] = "transmute"; lookup_table[AST_Transmute] = "transmute";
lookup_table[AST_Condition] = "condition"; lookup_table[AST_Condition] = "condition";
lookup_table[AST_List] = "list"; lookup_table[AST_List] = "list";
lookup_table[AST_ExprList] = "expr list"; lookup_table[AST_ExprList] = "expr list";
lookup_table[AST_ArgList] = "arg list"; lookup_table[AST_ArgList] = "arg list";
lookup_table[AST_ParamList] = "param list"; lookup_table[AST_ParamList] = "param list";
lookup_table[AST_StmtList] = "stmt list"; lookup_table[AST_StmtList] = "stmt list";
lookup_table[AST_IdentList] = "ident list"; lookup_table[AST_IdentList] = "ident list";
lookup_table[AST_Type] = "type"; lookup_table[AST_Type] = "type";
lookup_table[AST_Negate] = "-"; lookup_table[AST_Negate] = "-";
lookup_table[AST_Parameter] = "parameter"; lookup_table[AST_Parameter] = "parameter";
lookup_table[AST_ParamDecl] = "parameter-declaration"; lookup_table[AST_ParamDecl] = "parameter-declaration";
lookup_table[AST_AddressOf] = "address of"; lookup_table[AST_AddressOf] = "address of";
lookup_table[AST_Dereference] = "deref"; lookup_table[AST_Dereference] = "deref";
lookup_table[AST_Reference] = "ref"; lookup_table[AST_Reference] = "ref";
} }
const char* AST_node_to_string(const struct AST_Node_t* node) { const char* AST_node_to_string(const struct AST_Node_t* node) {
DEBUG("converting AST node to string: %p", node); DEBUG("converting AST node to string: %p", node);
assert(node != NULL); assert(node != NULL);
const char* string; const char* string;
switch(node->kind) { switch(node->kind) {
case AST_Int: case AST_Int:
case AST_Float: case AST_Float:
case AST_String: case AST_String:
case AST_Ident: case AST_Ident:
case AST_Macro: case AST_Macro:
case AST_Import: case AST_Import:
case AST_Storage: case AST_Storage:
case AST_Typekind: case AST_Typekind:
case AST_Sign: case AST_Sign:
case AST_Scale: case AST_Scale:
case AST_Qualifyier: case AST_Qualifyier:
string = node->value; string = node->value;
break; break;
default: default:
string = lookup_table[node->kind]; 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) { static inline unsigned long int min(unsigned long int a, unsigned long int b) {
@ -127,183 +127,182 @@ 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) { void AST_push_node(struct AST_Node_t *owner, struct AST_Node_t *child) {
DEBUG("Adding new node %p to %p", child, owner); DEBUG("Adding new node %p to %p", child, owner);
assert(owner != NULL); assert(owner != NULL);
assert(child != NULL); assert(child != NULL);
// if there are no children for now // if there are no children for now
if (owner->child_count == 0) { if (owner->child_count == 0) {
DEBUG("Allocating new children array"); DEBUG("Allocating new children array");
owner->children = malloc(sizeof(struct AST_Node_t *)); owner->children = mem_alloc(MemoryNamespaceAst, sizeof(struct AST_Node_t *));
} else { } else {
DEBUG("Rellocating old children array"); DEBUG("Rellocating old children array");
const size_t size = sizeof(struct AST_Node_t *) * (owner->child_count + 1); const size_t size = sizeof(struct AST_Node_t *) * (owner->child_count + 1);
owner->children = realloc(owner->children, size); owner->children = mem_realloc(MemoryNamespaceAst, owner->children, size);
} }
if (owner->children == NULL) { if (owner->children == NULL) {
PANIC("failed to allocate children array of AST node"); PANIC("failed to allocate children array of AST node");
} }
owner->location.col_end = max(owner->location.col_end, child->location.col_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.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.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.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) { struct AST_Node_t *AST_get_node(struct AST_Node_t *owner, const size_t idx) {
DEBUG("retrvieng node %d from %p", idx, owner); DEBUG("retrvieng node %d from %p", idx, owner);
assert(owner != NULL); assert(owner != NULL);
assert(owner->children != NULL); assert(owner->children != NULL);
assert(idx < owner->child_count); assert(idx < owner->child_count);
if (owner->children == NULL) { if (owner->children == NULL) {
PANIC("AST owner node has no children"); 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) { if (child == NULL) {
PANIC("child node is 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) { struct AST_Node_t* AST_remove_child(struct AST_Node_t* owner, const size_t idx) {
assert(owner != NULL); assert(owner != NULL);
assert(owner->children != NULL); assert(owner->children != NULL);
assert(idx < owner->child_count); 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 // shift back every following element by one
for (size_t i = idx; i < owner->child_count; i++) { for (size_t i = idx; i < owner->child_count; i++) {
owner->children[i] = owner->children[i + 1]; 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) { struct AST_Node_t* AST_detach_child(struct AST_Node_t* owner, const struct AST_Node_t* child) {
assert(owner != NULL); assert(owner != NULL);
assert(child != NULL); assert(child != NULL);
assert(owner->children != NULL); assert(owner->children != NULL);
for (size_t i = 0; i < owner->child_count; i++) { for (size_t i = 0; i < owner->child_count; i++) {
if (owner->children[i] == child) { if (owner->children[i] == child) {
return AST_remove_child(owner, i); 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) { 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) { if (node->parent != NULL) {
return; [[maybe_unused]]
} const struct AST_Node_t* child = AST_detach_child(node->parent, node);
assert(child == node);
}
if (node->parent != NULL) { if (node->children != NULL) {
const struct AST_Node_t* child = AST_detach_child(node->parent, node); for (size_t i = 0; i < node->child_count; i++) {
assert(child == node); // 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++) { mem_free(node);
// prevent detach of children node
node->children[i]->parent = NULL;
AST_delete_node(node->children[i]);
}
free(node->children);
free(node);
} }
static void AST_visit_nodes_recurse2(struct AST_Node_t *root, static void AST_visit_nodes_recurse2(struct AST_Node_t *root,
void (*for_each)(struct AST_Node_t *node, void (*for_each)(struct AST_Node_t *node,
size_t depth), size_t depth),
const size_t depth) { const size_t depth) {
DEBUG("Recursive visit of %p at %d with %p", root, depth, for_each); 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++) { for (size_t i = 0; i < root->child_count; i++) {
AST_visit_nodes_recurse2(root->children[i], for_each, depth + 1); AST_visit_nodes_recurse2(root->children[i], for_each, depth + 1);
} }
} }
void AST_visit_nodes_recurse(struct AST_Node_t *root, void AST_visit_nodes_recurse(struct AST_Node_t *root,
void (*for_each)(struct AST_Node_t *node, void (*for_each)(struct AST_Node_t *node,
size_t depth)) { 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(root != NULL);
assert(for_each != 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) { 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(stream != NULL);
assert(node != 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) { if (node->children == NULL) {
return; return;
} }
for (size_t i = 0; i < node->child_count; i++) { for (size_t i = 0; i < node->child_count; i++) {
AST_fprint_graphviz_node_definition(stream, node->children[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) { 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(stream != NULL);
assert(node != NULL); assert(node != NULL);
if (node->children == NULL) { if (node->children == NULL) {
return; return;
} }
for (size_t i = 0; i < node->child_count; i++) { for (size_t i = 0; i < node->child_count; i++) {
fprintf(stream, "\tnode%p -- node%p\n", (void*) node, (void*) node->children[i]); fprintf(stream, "\tnode%p -- node%p\n", (void*) node, (void*) node->children[i]);
AST_fprint_graphviz_node_connection(stream, node->children[i]); AST_fprint_graphviz_node_connection(stream, node->children[i]);
} }
} }
void AST_fprint_graphviz(FILE* stream, const struct AST_Node_t* root) { 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(stream != NULL);
assert(root != NULL); assert(root != NULL);
fprintf(stream, "graph {\n"); fprintf(stream, "graph {\n");
AST_fprint_graphviz_node_definition(stream, root); AST_fprint_graphviz_node_definition(stream, root);
AST_fprint_graphviz_node_connection(stream, root); AST_fprint_graphviz_node_connection(stream, root);
fprintf(stream, "}\n"); fprintf(stream, "}\n");
} }
AST_NODE_PTR AST_get_node_by_kind(AST_NODE_PTR owner, enum AST_SyntaxElement_t kind) { AST_NODE_PTR AST_get_node_by_kind(AST_NODE_PTR owner, enum AST_SyntaxElement_t kind) {

View File

@ -12,74 +12,74 @@
* variants of this enum. * variants of this enum.
*/ */
enum AST_SyntaxElement_t { enum AST_SyntaxElement_t {
AST_Stmt = 0, AST_Stmt = 0,
AST_Module, AST_Module,
AST_Expr, AST_Expr,
// Literals // Literals
AST_Int, AST_Int,
AST_Float, AST_Float,
AST_String, AST_String,
// Control flow // Control flow
AST_While, AST_While,
AST_If, AST_If,
AST_IfElse, AST_IfElse,
AST_Else, AST_Else,
AST_Condition, AST_Condition,
// Variable management // Variable management
AST_Decl, AST_Decl,
AST_Assign, AST_Assign,
AST_Def, AST_Def,
AST_Ident, AST_Ident,
// Arithmetic operators // Arithmetic operators
AST_Add, AST_Add,
AST_Sub, AST_Sub,
AST_Mul, AST_Mul,
AST_Div, AST_Div,
// Bitwise operators // Bitwise operators
AST_BitAnd, AST_BitAnd,
AST_BitOr, AST_BitOr,
AST_BitXor, AST_BitXor,
AST_BitNot, AST_BitNot,
// Boolean operators // Boolean operators
AST_BoolAnd, AST_BoolAnd,
AST_BoolOr, AST_BoolOr,
AST_BoolXor, AST_BoolXor,
AST_BoolNot, AST_BoolNot,
// Logical operators // Logical operators
AST_Eq, AST_Eq,
AST_Greater, AST_Greater,
AST_Less, AST_Less,
// Casts // Casts
AST_Typecast, // type cast AST_Typecast, // type cast
AST_Transmute, // reinterpret cast AST_Transmute, // reinterpret cast
AST_Call, // function call AST_Call, // function call
AST_Macro, // builtin functions: lineno(), filename(), ... AST_Macro, // builtin functions: lineno(), filename(), ...
// Defintions // Defintions
AST_Typedef, AST_Typedef,
AST_Box, AST_Box,
AST_Fun, AST_Fun,
AST_Import, AST_Import,
// amount of variants // amount of variants
// in this enums // in this enums
AST_List, AST_List,
AST_ExprList, AST_ExprList,
AST_ArgList, AST_ArgList,
AST_ParamList, AST_ParamList,
AST_StmtList, AST_StmtList,
AST_IdentList, AST_IdentList,
AST_Storage, AST_Storage,
AST_Type, AST_Type,
AST_Typekind, AST_Typekind,
AST_Sign, AST_Sign,
AST_Scale, AST_Scale,
AST_Negate, AST_Negate,
AST_Parameter, AST_Parameter,
AST_Qualifyier, AST_Qualifyier,
AST_ParamDecl, AST_ParamDecl,
AST_AddressOf, AST_AddressOf,
AST_Dereference, AST_Dereference,
AST_Reference, AST_Reference,
AST_ELEMENT_COUNT 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 * - value: A string representing an optional value. Can be a integer literal for kind AST_int
*/ */
struct AST_Node_t { struct AST_Node_t {
// parent node that owns this node // parent node that owns this node
struct AST_Node_t *parent; struct AST_Node_t *parent;
// type of AST node: if, declaration, ... // type of AST node: if, declaration, ...
enum AST_SyntaxElement_t kind; enum AST_SyntaxElement_t kind;
// optional value: integer literal, string literal, ... // optional value: integer literal, string literal, ...
const char* value; const char* value;
TokenLocation location; TokenLocation location;
// number of child nodes ownd by this node // number of child nodes ownd by this node
// length of children array // length of children array
size_t child_count; size_t child_count;
// variable amount of child nodes // variable amount of child nodes
struct AST_Node_t **children; struct AST_Node_t **children;
}; };
/** /**

View File

@ -8,6 +8,7 @@
#include <io/files.h> #include <io/files.h>
#include <assert.h> #include <assert.h>
#include <toml.h> #include <toml.h>
#include <mem/cache.h>
static GHashTable* args = NULL; static GHashTable* args = NULL;
@ -17,8 +18,8 @@ static void clean(void) {
g_hash_table_iter_init(&iter, args); g_hash_table_iter_init(&iter, args);
while (g_hash_table_iter_next(&iter, &key, &value)) { while (g_hash_table_iter_next(&iter, &key, &value)) {
free(value); mem_free(value);
free(key); mem_free(key);
} }
g_hash_table_destroy(args); g_hash_table_destroy(args);
@ -30,9 +31,9 @@ void parse_options(int argc, char* argv[]) {
atexit(clean); atexit(clean);
for (int i = 0; i < argc; i++) { 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->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->index = i;
option->value = NULL; option->value = NULL;
@ -91,15 +92,15 @@ GArray* get_non_options_after(const char* command) {
TargetConfig* default_target_config() { TargetConfig* default_target_config() {
DEBUG("generating 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_ast = false;
config->print_asm = false; config->print_asm = false;
config->print_ir = false; config->print_ir = false;
config->mode = Application; config->mode = Application;
config->archive_directory = strdup("archive"); config->archive_directory = mem_strdup(MemoryNamespaceOpt, "archive");
config->output_directory = strdup("bin"); config->output_directory = mem_strdup(MemoryNamespaceOpt, "bin");
config->optimization_level = 1; config->optimization_level = 1;
config->root_module = NULL; config->root_module = NULL;
config->link_search_paths = g_array_new(FALSE, FALSE, sizeof(char*)); config->link_search_paths = g_array_new(FALSE, FALSE, sizeof(char*));
@ -142,7 +143,7 @@ TargetConfig* default_target_config_from_args() {
const Option* opt = get_option("output"); const Option* opt = get_option("output");
if (opt->value != NULL) { if (opt->value != NULL) {
config->name = strdup(opt->value); config->name = mem_strdup(MemoryNamespaceOpt, (char*) opt->value);
} }
} }
@ -186,7 +187,7 @@ TargetConfig* default_target_config_from_args() {
print_message(Warning, "Got more than one file to compile, using first, ignoring others."); 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); g_array_free(files, TRUE);
} }
@ -210,12 +211,14 @@ void print_help(void) {
" --output=name name of output files without extension", " --output=name name of output files without extension",
" --link-paths=[paths,] set a list of directories to for libraries in", " --link-paths=[paths,] set a list of directories to for libraries in",
"Options:", "Options:",
" --verbose print logs with level information or higher", " --verbose print logs with level information or higher",
" --debug print debug logs (if not disabled at compile time)", " --debug print debug logs (if not disabled at compile time)",
" --version print the version", " --version print the version",
" --list-targets print a list of all available targets supported", " --list-targets print a list of all available targets supported",
" --help print this help dialog", " --help print this help dialog",
" --color-always always colorize output" " --color-always always colorize output",
" --version print the version",
" --print-memory-stats print statistics of the garbage collector"
}; };
for (unsigned int i = 0; i < sizeof(lines) / sizeof(const char *); i++) { for (unsigned int i = 0; i < sizeof(lines) / sizeof(const char *); i++) {
@ -395,16 +398,16 @@ int load_project_config(ProjectConfig *config) {
void delete_target_config(TargetConfig* config) { void delete_target_config(TargetConfig* config) {
if (config->root_module != NULL) { if (config->root_module != NULL) {
free(config->root_module); mem_free(config->root_module);
} }
if (config->archive_directory != NULL) { if (config->archive_directory != NULL) {
free(config->archive_directory); mem_free(config->archive_directory);
} }
if (config->name != NULL) { if (config->name != NULL) {
free(config->name); mem_free(config->name);
} }
if (config->output_directory != NULL) { if (config->output_directory != NULL) {
free(config->output_directory); mem_free(config->output_directory);
} }
if (config->link_search_paths) { if (config->link_search_paths) {
for (guint i = 0; i < config->link_search_paths->len; i++) { for (guint i = 0; i < config->link_search_paths->len; i++) {
@ -412,21 +415,21 @@ void delete_target_config(TargetConfig* config) {
} }
g_array_free(config->link_search_paths, TRUE); g_array_free(config->link_search_paths, TRUE);
} }
free(config); mem_free(config);
} }
void delete_project_config(ProjectConfig* config) { void delete_project_config(ProjectConfig* config) {
if (config->name != NULL) { if (config->name != NULL) {
free(config->name); mem_free(config->name);
} }
if (config->authors != NULL) { if (config->authors != NULL) {
g_array_free(config->authors, TRUE); g_array_free(config->authors, TRUE);
} }
if (config->desc != NULL) { if (config->desc != NULL) {
free(config->desc); mem_free(config->desc);
} }
if (config->license != NULL) { if (config->license != NULL) {
free(config->license); mem_free(config->license);
} }
if (config->targets != NULL) { if (config->targets != NULL) {
GHashTableIter iter; GHashTableIter iter;
@ -442,11 +445,11 @@ void delete_project_config(ProjectConfig* config) {
g_hash_table_destroy(config->targets); g_hash_table_destroy(config->targets);
} }
free(config); mem_free_from(MemoryNamespaceOpt, config);
} }
ProjectConfig* default_project_config() { ProjectConfig* default_project_config() {
ProjectConfig* config = malloc(sizeof(ProjectConfig)); ProjectConfig* config = mem_alloc(MemoryNamespaceOpt, sizeof(ProjectConfig));
config->authors = NULL; config->authors = NULL;
config->name = NULL; config->name = NULL;

View File

@ -13,6 +13,7 @@
#include <cfg/opt.h> #include <cfg/opt.h>
#include <codegen/backend.h> #include <codegen/backend.h>
#include <llvm/backend.h> #include <llvm/backend.h>
#include <mem/cache.h>
#define GRAPHVIZ_FILE_EXTENSION "gv" #define GRAPHVIZ_FILE_EXTENSION "gv"
@ -187,6 +188,9 @@ static void build_target(ModuleFileStack *unit, const TargetConfig *target) {
AST_delete_node(ast); AST_delete_node(ast);
mem_purge_namespace(MemoryNamespaceLex);
mem_purge_namespace(MemoryNamespaceAst);
print_file_statistics(file); print_file_statistics(file);
} }

View File

@ -6,6 +6,7 @@
#include <sys/log.h> #include <sys/log.h>
#include <assert.h> #include <assert.h>
#include <sys/col.h> #include <sys/col.h>
#include <mem/cache.h>
#ifdef __unix__ #ifdef __unix__
@ -13,9 +14,15 @@
#define MAX_PATH_BYTES PATH_MAX #define MAX_PATH_BYTES PATH_MAX
#define min(a, b) ((a) > (b) ? (b) : (a))
#elif defined(_WIN32) || defined(WIN32) #elif defined(_WIN32) || defined(WIN32)
#include <Windows.h> #include <Windows.h>
// for _fullpath
#include <stdlib.h>
// for _mkdir
#include <direct.h>
#define MAX_PATH_BYTES _MAX_PATH #define MAX_PATH_BYTES _MAX_PATH
@ -65,10 +72,6 @@ void delete_files(ModuleFileStack *stack) {
// seeking the current line in print_diagnostic() // seeking the current line in print_diagnostic()
#define SEEK_BUF_BYTES 256 #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 // behaves like fgets except that it has defined behavior when n == 1
static void custom_fgets(char *buffer, size_t n, FILE *stream) { static void custom_fgets(char *buffer, size_t n, FILE *stream) {
if (n == 1) { if (n == 1) {
@ -120,17 +123,17 @@ void print_diagnostic(ModuleFile *file, TokenLocation *location, Message kind, c
printf("%s%s:%ld:%s %s%s:%s %s\n", BOLD, absolute_path, location->line_start, RESET, accent_color, kind_text, RESET, printf("%s%s:%ld:%s %s%s:%s %s\n", BOLD, absolute_path, location->line_start, RESET, accent_color, kind_text, RESET,
message); message);
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); printf(" %4ld | ", location->line_start + l);
size_t chars = 0; unsigned long int chars = 0;
// print line before token group start // 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) { while (limit > 1) {
custom_fgets(buffer, (int) limit, file->handle); custom_fgets(buffer, (int) limit, file->handle);
chars += printf("%s", buffer); chars += printf("%s", buffer);
@ -168,13 +171,13 @@ void print_diagnostic(ModuleFile *file, TokenLocation *location, Message kind, c
} }
printf(" | "); printf(" | ");
for (size_t i = 1; i < location->col_start; i++) { for (unsigned long int i = 1; i < location->col_start; i++) {
printf(" "); printf(" ");
} }
printf("%s", accent_color); printf("%s", accent_color);
printf("^"); 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("~"); printf("~");
} }
@ -298,7 +301,7 @@ int create_directory(const char *path) {
} }
const char *get_last_error() { const char *get_last_error() {
return strdup(strerror(errno)); return mem_strdup(MemoryNamespaceIo, strerror(errno));
} }
const char *get_absolute_path(const char *path) { const char *get_absolute_path(const char *path) {

View File

@ -15,9 +15,9 @@
#endif #endif
typedef struct FileDiagnosticStatistics_t { typedef struct FileDiagnosticStatistics_t {
size_t error_count; unsigned long int error_count;
size_t warning_count; unsigned long int warning_count;
size_t info_count; unsigned long int info_count;
} FileDiagnosticStatistics; } FileDiagnosticStatistics;
typedef struct ModuleFile_t { typedef struct ModuleFile_t {

View File

@ -3,6 +3,7 @@
#include <yacc/parser.tab.h> #include <yacc/parser.tab.h>
#include <sys/log.h> #include <sys/log.h>
#include <lex/util.h> #include <lex/util.h>
#include <mem/cache.h>
int yyLineNumber = 1; int yyLineNumber = 1;
@ -89,20 +90,20 @@
"lineno" {DEBUG("\"%s\" tokenized with \'FunLineno\'", yytext); return(FunLineno);}; "lineno" {DEBUG("\"%s\" tokenized with \'FunLineno\'", yytext); return(FunLineno);};
"extsupport" {DEBUG("\"%s\" tokenized with \'FunExtsupport\'", yytext); return(FunExtsupport);}; "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]+ {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 = strdup(yytext); return(ValFloat);}; [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 = strdup(yytext); return(Ident); }; [a-zA-Z_0-9]+ {DEBUG("\"%s\" tokenized with \'Ident\'", yytext); yylval.string = mem_strdup(MemoryNamespaceLex, yytext); return(Ident); };
\"([^\"\n])*\" { \"([^\"\n])*\" {
yytext = yytext +1; yytext = yytext +1;
yytext[yyleng - 2] = 0; 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 = yytext +3;
yytext[yyleng - 4] = 0; yytext[yyleng - 4] = 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 */ }; [ \r\t] { /* ignore whitespace */ };
. { return yytext[0]; /* passthrough unknown token, let parser handle the error */ }; . { return yytext[0]; /* passthrough unknown token, let parser handle the error */ };
%% %%

View File

@ -2,6 +2,8 @@
#include <lex/util.h> #include <lex/util.h>
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <glib.h>
#include <mem/cache.h>
// implementation based on: // implementation based on:
// https://github.com/sunxfancy/flex-bison-examples/blob/master/error-handling/ccalc.c // 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 nTokenLength = 0;
static int nTokenNextStart = 0; static int nTokenNextStart = 0;
static void lex_deinit(void) {
free(buffer);
}
void lex_init(void) { void lex_init(void) {
buffer = malloc(MAX_READ_BUFFER_SIZE); buffer = mem_alloc(MemoryNamespaceStatic, MAX_READ_BUFFER_SIZE);
atexit(lex_deinit);
} }
void lex_reset(void) { void lex_reset(void) {

View File

@ -6,6 +6,7 @@
#include <cfg/opt.h> #include <cfg/opt.h>
#include <compiler.h> #include <compiler.h>
#include <llvm/parser.h> #include <llvm/parser.h>
#include <mem/cache.h>
/** /**
* @brief Log a debug message to inform about beginning exit procedures * @brief Log a debug message to inform about beginning exit procedures
@ -18,6 +19,8 @@ void notify_exit(void) { DEBUG("Exiting gemstone..."); }
* *
*/ */
void setup(int argc, char *argv[]) { void setup(int argc, char *argv[]) {
mem_init();
// setup preample // setup preample
parse_options(argc, argv); parse_options(argc, argv);
@ -63,5 +66,9 @@ int main(int argc, char *argv[]) {
run_compiler(); run_compiler();
if (is_option_set("print-memory-stats")) {
print_memory_statistics();
}
return 0; return 0;
} }

271
src/mem/cache.c Normal file
View File

@ -0,0 +1,271 @@
//
// Created by servostar on 6/5/24.
//
#include <mem/cache.h>
#include <sys/log.h>
#include <glib.h>
#include <string.h>
#include <assert.h>
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");
}

89
src/mem/cache.h Normal file
View File

@ -0,0 +1,89 @@
//
// Created by servostar on 6/5/24.
//
#ifndef GEMSTONE_CACHE_H
#define GEMSTONE_CACHE_H
#include <mem/cache.h>
#include <stddef.h>
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

View File

@ -76,8 +76,7 @@ int stdout_supports_ansi_esc() {
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
if (!GetConsoleMode(hConsole, &mode)) { if (!GetConsoleMode(hConsole, &mode)) {
ERROR("failed to get console mode"); return ASNI_DISABLED;
return ANSI_ENABLED;
} }
if ((mode & ENABLE_VIRTUAL_TERMINAL_INPUT) | if ((mode & ENABLE_VIRTUAL_TERMINAL_INPUT) |

View File

@ -6,6 +6,7 @@
#include <assert.h> #include <assert.h>
#include <string.h> #include <string.h>
#include <cfg/opt.h> #include <cfg/opt.h>
#include <mem/cache.h>
static struct Logger_t { static struct Logger_t {
FILE** streams; FILE** streams;
@ -39,7 +40,7 @@ void log_register_stream(FILE* restrict stream)
if (GlobalLogger.stream_count == 0) if (GlobalLogger.stream_count == 0)
{ {
GlobalLogger.streams = (FILE**) malloc(sizeof(FILE*)); GlobalLogger.streams = (FILE**) mem_alloc(MemoryNamespaceLog, sizeof(FILE*));
GlobalLogger.stream_count = 1; GlobalLogger.stream_count = 1;
if (GlobalLogger.streams == NULL) if (GlobalLogger.streams == NULL)
@ -51,7 +52,7 @@ void log_register_stream(FILE* restrict stream)
{ {
GlobalLogger.stream_count++; GlobalLogger.stream_count++;
size_t bytes = GlobalLogger.stream_count * sizeof(FILE*); 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) if (GlobalLogger.streams == NULL)
{ {

View File

@ -11,4 +11,5 @@ add_subdirectory(input_file)
add_subdirectory(ast) add_subdirectory(ast)
add_subdirectory(glib) add_subdirectory(glib)
add_subdirectory(llvm) add_subdirectory(llvm)
add_subdirectory(project) add_subdirectory(project)
add_subdirectory(cache)

View File

@ -26,6 +26,7 @@ add_executable(ast_build_tree
${PROJECT_SOURCE_DIR}/src/io/files.c ${PROJECT_SOURCE_DIR}/src/io/files.c
${PROJECT_SOURCE_DIR}/src/sys/col.c ${PROJECT_SOURCE_DIR}/src/sys/col.c
${PROJECT_SOURCE_DIR}/src/cfg/opt.c ${PROJECT_SOURCE_DIR}/src/cfg/opt.c
${PROJECT_SOURCE_DIR}/src/mem/cache.c
${PROJECT_SOURCE_DIR}/dep/tomlc99/toml.c ${PROJECT_SOURCE_DIR}/dep/tomlc99/toml.c
build_tree.c) build_tree.c)
set_target_properties(ast_build_tree 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/io/files.c
${PROJECT_SOURCE_DIR}/src/sys/col.c ${PROJECT_SOURCE_DIR}/src/sys/col.c
${PROJECT_SOURCE_DIR}/src/cfg/opt.c ${PROJECT_SOURCE_DIR}/src/cfg/opt.c
${PROJECT_SOURCE_DIR}/src/mem/cache.c
${PROJECT_SOURCE_DIR}/dep/tomlc99/toml.c ${PROJECT_SOURCE_DIR}/dep/tomlc99/toml.c
print_node.c) print_node.c)
set_target_properties(ast_print_node 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/io/files.c
${PROJECT_SOURCE_DIR}/src/sys/col.c ${PROJECT_SOURCE_DIR}/src/sys/col.c
${PROJECT_SOURCE_DIR}/src/cfg/opt.c ${PROJECT_SOURCE_DIR}/src/cfg/opt.c
${PROJECT_SOURCE_DIR}/src/mem/cache.c
${PROJECT_SOURCE_DIR}/dep/tomlc99/toml.c ${PROJECT_SOURCE_DIR}/dep/tomlc99/toml.c
print_graphviz.c) print_graphviz.c)
set_target_properties(ast_graphviz set_target_properties(ast_graphviz

View File

@ -4,6 +4,7 @@
#include <ast/ast.h> #include <ast/ast.h>
#include <sys/log.h> #include <sys/log.h>
#include <mem/cache.h>
void generate_statement(const AST_NODE_PTR stmt) { void generate_statement(const AST_NODE_PTR stmt) {
const AST_NODE_PTR add = AST_new_node(empty_location(), AST_Add, NULL); 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) { int main(void) {
mem_init();
const AST_NODE_PTR root = AST_new_node(empty_location(), AST_Stmt, NULL); const AST_NODE_PTR root = AST_new_node(empty_location(), AST_Stmt, NULL);

View File

@ -4,8 +4,10 @@
#include <ast/ast.h> #include <ast/ast.h>
#include <sys/log.h> #include <sys/log.h>
#include <mem/cache.h>
int main(void) { int main(void) {
mem_init();
struct AST_Node_t* node = AST_new_node(empty_location(), AST_If, NULL); struct AST_Node_t* node = AST_new_node(empty_location(), AST_If, NULL);

View File

@ -4,6 +4,7 @@
#include <ast/ast.h> #include <ast/ast.h>
#include <sys/log.h> #include <sys/log.h>
#include <mem/cache.h>
void generate_statement(const AST_NODE_PTR stmt) { void generate_statement(const AST_NODE_PTR stmt) {
const AST_NODE_PTR add = AST_new_node(empty_location(), AST_Add, NULL); 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) { int main(void) {
mem_init();
AST_init(); AST_init();
const AST_NODE_PTR root = AST_new_node(empty_location(), AST_Stmt, NULL); const AST_NODE_PTR root = AST_new_node(empty_location(), AST_Stmt, NULL);

View File

@ -3,9 +3,10 @@
// //
#include <ast/ast.h> #include <ast/ast.h>
#include <mem/cache.h>
int main(void) { int main(void) {
mem_init();
AST_init(); AST_init();
const AST_NODE_PTR node = AST_new_node(empty_location(), 0, "value"); const AST_NODE_PTR node = AST_new_node(empty_location(), 0, "value");

38
tests/cache/CMakeLists.txt vendored Normal file
View File

@ -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)

28
tests/cache/cache_test.c vendored Normal file
View File

@ -0,0 +1,28 @@
//
// Created by servostar on 6/5/24.
//
#include <sys/log.h>
#include <sys/col.h>
#include <cfg/opt.h>
#include <mem/cache.h>
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;
}

View File

@ -25,6 +25,7 @@ add_executable(logging_output
${PROJECT_SOURCE_DIR}/src/sys/col.c ${PROJECT_SOURCE_DIR}/src/sys/col.c
${PROJECT_SOURCE_DIR}/src/cfg/opt.c ${PROJECT_SOURCE_DIR}/src/cfg/opt.c
${PROJECT_SOURCE_DIR}/src/io/files.c ${PROJECT_SOURCE_DIR}/src/io/files.c
${PROJECT_SOURCE_DIR}/src/mem/cache.c
output.c) output.c)
set_target_properties(logging_output set_target_properties(logging_output
PROPERTIES PROPERTIES
@ -45,6 +46,7 @@ add_executable(logging_panic
${PROJECT_SOURCE_DIR}/src/sys/col.c ${PROJECT_SOURCE_DIR}/src/sys/col.c
${PROJECT_SOURCE_DIR}/src/cfg/opt.c ${PROJECT_SOURCE_DIR}/src/cfg/opt.c
${PROJECT_SOURCE_DIR}/src/io/files.c ${PROJECT_SOURCE_DIR}/src/io/files.c
${PROJECT_SOURCE_DIR}/src/mem/cache.c
panic.c) panic.c)
set_target_properties(logging_panic set_target_properties(logging_panic
PROPERTIES PROPERTIES
@ -65,6 +67,7 @@ add_executable(logging_streams
${PROJECT_SOURCE_DIR}/src/sys/col.c ${PROJECT_SOURCE_DIR}/src/sys/col.c
${PROJECT_SOURCE_DIR}/src/cfg/opt.c ${PROJECT_SOURCE_DIR}/src/cfg/opt.c
${PROJECT_SOURCE_DIR}/src/io/files.c ${PROJECT_SOURCE_DIR}/src/io/files.c
${PROJECT_SOURCE_DIR}/src/mem/cache.c
streams.c) streams.c)
set_target_properties(logging_streams set_target_properties(logging_streams
PROPERTIES PROPERTIES
@ -85,6 +88,7 @@ add_executable(logging_level
${PROJECT_SOURCE_DIR}/src/sys/col.c ${PROJECT_SOURCE_DIR}/src/sys/col.c
${PROJECT_SOURCE_DIR}/src/cfg/opt.c ${PROJECT_SOURCE_DIR}/src/cfg/opt.c
${PROJECT_SOURCE_DIR}/src/io/files.c ${PROJECT_SOURCE_DIR}/src/io/files.c
${PROJECT_SOURCE_DIR}/src/mem/cache.c
level.c) level.c)
set_target_properties(logging_level set_target_properties(logging_level
PROPERTIES PROPERTIES

View File

@ -5,10 +5,12 @@
#include "sys/log.h" #include "sys/log.h"
#include <sys/col.h> #include <sys/col.h>
#include <cfg/opt.h> #include <cfg/opt.h>
#include <mem/cache.h>
#define LOG_LEVEL LOG_LEVEL_WARNING #define LOG_LEVEL LOG_LEVEL_WARNING
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
mem_init();
parse_options(argc, argv); parse_options(argc, argv);
log_init(); log_init();
set_log_level(LOG_LEVEL_DEBUG); set_log_level(LOG_LEVEL_DEBUG);

View File

@ -5,8 +5,10 @@
#include "sys/log.h" #include "sys/log.h"
#include <sys/col.h> #include <sys/col.h>
#include <cfg/opt.h> #include <cfg/opt.h>
#include <mem/cache.h>
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
mem_init();
parse_options(argc, argv); parse_options(argc, argv);
log_init(); log_init();
set_log_level(LOG_LEVEL_DEBUG); set_log_level(LOG_LEVEL_DEBUG);

View File

@ -4,8 +4,10 @@
#include "sys/log.h" #include "sys/log.h"
#include <cfg/opt.h> #include <cfg/opt.h>
#include <mem/cache.h>
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
mem_init();
parse_options(argc, argv); parse_options(argc, argv);
log_init(); log_init();
set_log_level(LOG_LEVEL_DEBUG); set_log_level(LOG_LEVEL_DEBUG);

View File

@ -5,6 +5,7 @@
#include "sys/log.h" #include "sys/log.h"
#include <stdlib.h> #include <stdlib.h>
#include <cfg/opt.h> #include <cfg/opt.h>
#include <mem/cache.h>
static FILE* file; static FILE* file;
@ -15,6 +16,7 @@ void close_file(void) {
} }
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
mem_init();
parse_options(argc, argv); parse_options(argc, argv);
log_init(); log_init();
set_log_level(LOG_LEVEL_DEBUG); set_log_level(LOG_LEVEL_DEBUG);