formatted source with clangd
This commit is contained in:
parent
ed728c8042
commit
e1649657fd
|
@ -1,3 +1,45 @@
|
|||
BasedOnStyle: Google
|
||||
IndentWidth: 4
|
||||
PointerAlignment: Left
|
||||
BreakAfterAttributes: Always
|
||||
BreakStringLiterals: true
|
||||
SpaceAfterCStyleCast: true
|
||||
SortIncludes: CaseSensitive
|
||||
UseTab: Never
|
||||
ReflowComments: false
|
||||
EmptyLineAfterAccessModifier: Never
|
||||
TabWidth: 4
|
||||
SpaceAroundPointerQualifiers: Default
|
||||
InsertNewlineAtEOF: true
|
||||
InsertBraces: true
|
||||
IndentCaseLabels: true
|
||||
IncludeBlocks: Regroup
|
||||
ContinuationIndentWidth: 2
|
||||
ColumnLimit: 80
|
||||
BreakBeforeBraces: Attach
|
||||
BinPackParameters: true
|
||||
BinPackArguments: true
|
||||
AllowAllArgumentsOnNextLine: true
|
||||
AllowShortBlocksOnASingleLine: Never
|
||||
AlignOperands: Align
|
||||
BreakBeforeBinaryOperators: NonAssignment
|
||||
AlignConsecutiveMacros: Consecutive
|
||||
AlignEscapedNewlines: Left
|
||||
AlignConsecutiveDeclarations: false
|
||||
AlignConsecutiveAssignments: Consecutive
|
||||
IndentCaseBlocks: true
|
||||
AlignArrayOfStructures: Left
|
||||
AlignAfterOpenBracket: Align
|
||||
AllowAllConstructorInitializersOnNextLine: true
|
||||
AllowAllParametersOfDeclarationOnNextLine: true
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: InlineOnly
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
AlwaysBreakAfterReturnType: None
|
||||
BracedInitializerIndentWidth: 4
|
||||
BreakConstructorInitializers: AfterColon
|
||||
DerivePointerAlignment: false
|
||||
IndentWrappedFunctionNames: true
|
||||
Language: Cpp
|
||||
SpaceAfterLogicalNot: false
|
||||
LineEnding: LF
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Define the target directory
|
||||
directory="./src"
|
||||
|
||||
# Check if the target is not a directory
|
||||
if [ ! -d "$directory" ]; then
|
||||
echo "Error: $directory is not a valid directory"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Use find to iterate over all files in the directory and subdirectories
|
||||
find "$directory" -type f | while read -r file; do
|
||||
if [[ "${file##*/}" =~ ^[a-z_]+\.c|h$ ]]; then
|
||||
echo "Formatting file: $file..."
|
||||
|
||||
clang-format -i "$file"
|
||||
fi
|
||||
done
|
153
src/ast/ast.c
153
src/ast/ast.c
|
@ -1,15 +1,18 @@
|
|||
|
||||
#include <assert.h>
|
||||
#include <ast/ast.h>
|
||||
#include <mem/cache.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/log.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);
|
||||
assert(kind < AST_ELEMENT_COUNT);
|
||||
|
||||
struct AST_Node_t *node = mem_alloc(MemoryNamespaceAst, 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");
|
||||
|
@ -18,24 +21,24 @@ struct AST_Node_t *AST_new_node(TokenLocation location, enum AST_SyntaxElement_t
|
|||
assert(node != NULL);
|
||||
|
||||
// init to discrete state
|
||||
node->parent = NULL;
|
||||
node->parent = NULL;
|
||||
node->children = mem_new_g_array(MemoryNamespaceAst, sizeof(AST_NODE_PTR));
|
||||
node->kind = kind;
|
||||
node->value = value;
|
||||
node->kind = kind;
|
||||
node->value = value;
|
||||
node->location = location;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
static const char* lookup_table[AST_ELEMENT_COUNT] = { "__UNINIT__" };
|
||||
static const char* lookup_table[AST_ELEMENT_COUNT] = {"__UNINIT__"};
|
||||
|
||||
void AST_init() {
|
||||
DEBUG("initializing global syntax tree...");
|
||||
|
||||
INFO("filling lookup table...");
|
||||
lookup_table[AST_Stmt] = "stmt";
|
||||
lookup_table[AST_Stmt] = "stmt";
|
||||
lookup_table[AST_Module] = "module";
|
||||
lookup_table[AST_Expr] = "expr";
|
||||
lookup_table[AST_Expr] = "expr";
|
||||
|
||||
lookup_table[AST_Add] = "+";
|
||||
lookup_table[AST_Sub] = "-";
|
||||
|
@ -43,53 +46,53 @@ void AST_init() {
|
|||
lookup_table[AST_Div] = "/";
|
||||
|
||||
lookup_table[AST_BitAnd] = "&";
|
||||
lookup_table[AST_BitOr] = "|";
|
||||
lookup_table[AST_BitOr] = "|";
|
||||
lookup_table[AST_BitXor] = "^";
|
||||
lookup_table[AST_BitNot] = "!";
|
||||
|
||||
lookup_table[AST_Eq] = "==";
|
||||
lookup_table[AST_Less] = "<";
|
||||
lookup_table[AST_Eq] = "==";
|
||||
lookup_table[AST_Less] = "<";
|
||||
lookup_table[AST_Greater] = ">";
|
||||
|
||||
lookup_table[AST_BoolAnd] = "&&";
|
||||
lookup_table[AST_BoolOr] = "||";
|
||||
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_While] = "while";
|
||||
lookup_table[AST_If] = "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_Def] = "def";
|
||||
lookup_table[AST_Def] = "def";
|
||||
|
||||
lookup_table[AST_Typedef] = "typedef";
|
||||
lookup_table[AST_Box] = "box";
|
||||
lookup_table[AST_FunDecl] = "fun";
|
||||
lookup_table[AST_FunDef] = "fun";
|
||||
lookup_table[AST_Typedef] = "typedef";
|
||||
lookup_table[AST_Box] = "box";
|
||||
lookup_table[AST_FunDecl] = "fun";
|
||||
lookup_table[AST_FunDef] = "fun";
|
||||
lookup_table[AST_ProcDecl] = "fun";
|
||||
lookup_table[AST_ProcDef] = "fun";
|
||||
lookup_table[AST_ProcDef] = "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_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_Return] = "ret";
|
||||
lookup_table[AST_Reference] = "ref";
|
||||
lookup_table[AST_Return] = "ret";
|
||||
}
|
||||
|
||||
const char* AST_node_to_string(const struct AST_Node_t* node) {
|
||||
|
@ -98,7 +101,7 @@ const char* AST_node_to_string(const struct AST_Node_t* node) {
|
|||
|
||||
const char* string;
|
||||
|
||||
switch(node->kind) {
|
||||
switch (node->kind) {
|
||||
case AST_Int:
|
||||
case AST_Char:
|
||||
case AST_Float:
|
||||
|
@ -131,7 +134,7 @@ static inline unsigned long int max(unsigned long int a, unsigned long int b) {
|
|||
return a > b ? a : 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);
|
||||
assert(owner != NULL);
|
||||
assert(child != NULL);
|
||||
|
@ -140,11 +143,15 @@ void AST_push_node(struct AST_Node_t *owner, struct AST_Node_t *child) {
|
|||
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);
|
||||
|
||||
if (owner->location.file == NULL) {
|
||||
owner->location.file = child->location.file;
|
||||
|
@ -155,7 +162,7 @@ void AST_push_node(struct AST_Node_t *owner, struct AST_Node_t *child) {
|
|||
g_array_append_val(owner->children, 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);
|
||||
assert(owner != NULL);
|
||||
assert(owner->children != NULL);
|
||||
|
@ -174,7 +181,8 @@ struct AST_Node_t *AST_get_node(struct AST_Node_t *owner, const size_t idx) {
|
|||
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->children != NULL);
|
||||
assert(idx < owner->children->len);
|
||||
|
@ -187,7 +195,8 @@ struct AST_Node_t* AST_remove_child(struct AST_Node_t* owner, const size_t idx)
|
|||
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(child != NULL);
|
||||
assert(owner->children != NULL);
|
||||
|
@ -201,14 +210,14 @@ struct AST_Node_t* AST_detach_child(struct AST_Node_t* owner, const struct AST_N
|
|||
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);
|
||||
|
||||
DEBUG("Deleting AST node: %p", node);
|
||||
|
||||
if (node->parent != NULL) {
|
||||
[[maybe_unused]]
|
||||
const struct AST_Node_t* child = AST_detach_child(node->parent, node);
|
||||
[[maybe_unused]] const struct AST_Node_t* child =
|
||||
AST_detach_child(node->parent, node);
|
||||
assert(child == node);
|
||||
}
|
||||
|
||||
|
@ -224,8 +233,8 @@ void AST_delete_node(struct AST_Node_t *node) {
|
|||
mem_free(node);
|
||||
}
|
||||
|
||||
static void AST_visit_nodes_recurse2(struct AST_Node_t *root,
|
||||
void (*for_each)(struct AST_Node_t *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);
|
||||
|
@ -235,12 +244,13 @@ static void AST_visit_nodes_recurse2(struct AST_Node_t *root,
|
|||
(for_each)(root, depth);
|
||||
|
||||
for (size_t i = 0; i < root->children->len; i++) {
|
||||
AST_visit_nodes_recurse2(g_array_index(root->children, AST_NODE_PTR, i), for_each, depth + 1);
|
||||
AST_visit_nodes_recurse2(g_array_index(root->children, AST_NODE_PTR, i),
|
||||
for_each, depth + 1);
|
||||
}
|
||||
}
|
||||
|
||||
void AST_visit_nodes_recurse(struct AST_Node_t *root,
|
||||
void (*for_each)(struct AST_Node_t *node,
|
||||
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);
|
||||
|
||||
|
@ -250,24 +260,28 @@ void AST_visit_nodes_recurse(struct AST_Node_t *root,
|
|||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < node->children->len; i++) {
|
||||
AST_fprint_graphviz_node_definition(stream, g_array_index(node->children, AST_NODE_PTR, i));
|
||||
AST_fprint_graphviz_node_definition(
|
||||
stream, g_array_index(node->children, AST_NODE_PTR, 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);
|
||||
|
||||
assert(stream != NULL);
|
||||
|
@ -298,16 +312,17 @@ void AST_fprint_graphviz(FILE* stream, const struct AST_Node_t* root) {
|
|||
fprintf(stream, "}\n");
|
||||
}
|
||||
|
||||
AST_NODE_PTR AST_get_node_by_kind(AST_NODE_PTR owner, enum AST_SyntaxElement_t kind) {
|
||||
for (size_t i = 0; i < owner->children->len; i++) {
|
||||
AST_NODE_PTR child = AST_get_node(owner, i);
|
||||
AST_NODE_PTR AST_get_node_by_kind(AST_NODE_PTR owner,
|
||||
enum AST_SyntaxElement_t kind) {
|
||||
for (size_t i = 0; i < owner->children->len; i++) {
|
||||
AST_NODE_PTR child = AST_get_node(owner, i);
|
||||
|
||||
if (child->kind == kind) {
|
||||
return child;
|
||||
if (child->kind == kind) {
|
||||
return child;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void AST_merge_modules(AST_NODE_PTR dst, size_t k, AST_NODE_PTR src) {
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
#ifndef _AST_H_
|
||||
#define _AST_H_
|
||||
|
||||
#include <stdio.h>
|
||||
#include <io/files.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/**
|
||||
* @brief The type of a AST node
|
||||
|
@ -50,10 +50,10 @@ enum AST_SyntaxElement_t {
|
|||
AST_Greater,
|
||||
AST_Less,
|
||||
// Casts
|
||||
AST_Typecast, // type cast
|
||||
AST_Transmute, // reinterpret cast
|
||||
AST_Call, // function call
|
||||
AST_Macro, // builtin functions: lineno(), filename(), ...
|
||||
AST_Typecast, // type cast
|
||||
AST_Transmute, // reinterpret cast
|
||||
AST_Call, // function call
|
||||
AST_Macro, // builtin functions: lineno(), filename(), ...
|
||||
// Defintions
|
||||
AST_Typedef,
|
||||
AST_Box,
|
||||
|
@ -93,11 +93,12 @@ enum AST_SyntaxElement_t {
|
|||
* Every node can have one ancestor (parent) but multiple (also none) children.
|
||||
* Nodes have two properties:
|
||||
* - kind: The type of the node. Such as AST_Expr, AST_Add, ...
|
||||
* - 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
|
||||
*/
|
||||
typedef struct AST_Node_t {
|
||||
// parent node that owns this node
|
||||
struct AST_Node_t *parent;
|
||||
struct AST_Node_t* parent;
|
||||
|
||||
// type of AST node: if, declaration, ...
|
||||
enum AST_SyntaxElement_t kind;
|
||||
|
@ -116,43 +117,44 @@ typedef struct AST_Node_t {
|
|||
typedef struct AST_Node_t* AST_NODE_PTR;
|
||||
|
||||
/**
|
||||
* @brief Initalize the global state of this module. Required for some functionality to work correctly.
|
||||
* @brief Initalize the global state of this module. Required for some
|
||||
* functionality to work correctly.
|
||||
*/
|
||||
void AST_init(void);
|
||||
|
||||
/**
|
||||
* @brief Returns the string representation of the supplied node
|
||||
* @attention The retuned pointer is not to be freed as it may either be a statically stored string or
|
||||
* used by the node after this function call.
|
||||
* @attention The retuned pointer is not to be freed as it may either be a
|
||||
* statically stored string or used by the node after this function call.
|
||||
* @param node to return string representation of
|
||||
* @return string represenation of the node
|
||||
*/
|
||||
[[maybe_unused]]
|
||||
[[gnu::nonnull(1)]]
|
||||
[[maybe_unused]] [[gnu::nonnull(1)]]
|
||||
const char* AST_node_to_string(const struct AST_Node_t* node);
|
||||
|
||||
/**
|
||||
* @brief Create a new node struct on the system heap. Initializes the struct with the given values.
|
||||
* All other fields are set to either NULL or 0. No allocation for children array is preformed.
|
||||
* @attention parameter value can be NULL in case no value can be provided for the node
|
||||
* @brief Create a new node struct on the system heap. Initializes the struct
|
||||
* with the given values. All other fields are set to either NULL or 0. No
|
||||
* allocation for children array is preformed.
|
||||
* @attention parameter value can be NULL in case no value can be provided for
|
||||
* the node
|
||||
* @param kind the type of this node
|
||||
* @param value an optional value for this node
|
||||
* @return
|
||||
*/
|
||||
[[maybe_unused]]
|
||||
[[nodiscard("pointer must be freed")]]
|
||||
[[gnu::returns_nonnull]]
|
||||
struct AST_Node_t *AST_new_node(TokenLocation location, enum AST_SyntaxElement_t kind, const char* value);
|
||||
[[maybe_unused]] [[nodiscard("pointer must be freed")]] [[gnu::returns_nonnull]]
|
||||
struct AST_Node_t* AST_new_node(TokenLocation location,
|
||||
enum AST_SyntaxElement_t kind,
|
||||
const char* value);
|
||||
|
||||
/**
|
||||
* @brief Deallocate this node and all of its children.
|
||||
* @attention This function will detach this node from its parent if one is present
|
||||
* Use of the supplied node after this call is undefined behavior
|
||||
* @attention This function will detach this node from its parent if one is
|
||||
* present Use of the supplied node after this call is undefined behavior
|
||||
* @param node The node to deallocate
|
||||
*/
|
||||
[[maybe_unused]]
|
||||
[[gnu::nonnull(1)]]
|
||||
void AST_delete_node(struct AST_Node_t * node);
|
||||
[[maybe_unused]] [[gnu::nonnull(1)]]
|
||||
void AST_delete_node(struct AST_Node_t* node);
|
||||
|
||||
/**
|
||||
* @brief Add a new child node to a parent node
|
||||
|
@ -160,59 +162,58 @@ void AST_delete_node(struct AST_Node_t * node);
|
|||
* @param owner node to add a child to
|
||||
* @param child node to be added as a child
|
||||
*/
|
||||
[[maybe_unused]]
|
||||
[[gnu::nonnull(1), gnu::nonnull(2)]]
|
||||
void AST_push_node(struct AST_Node_t *owner, struct AST_Node_t *child);
|
||||
[[maybe_unused]] [[gnu::nonnull(1), gnu::nonnull(2)]]
|
||||
void AST_push_node(struct AST_Node_t* owner, struct AST_Node_t* child);
|
||||
|
||||
/**
|
||||
* @brief Remove the specified child from the owner.
|
||||
* @attention The parent of the removed node is set to NULL.
|
||||
* The returned pointer is still valid. It must be freed at some pointer later.
|
||||
* The returned pointer is still valid. It must be freed at some
|
||||
* pointer later.
|
||||
* @param owner Node to remove the child from
|
||||
* @param idx the index of the child to remove
|
||||
* @return a pointer to the child which was removed
|
||||
*/
|
||||
[[maybe_unused]]
|
||||
[[nodiscard("pointer must be freed")]]
|
||||
[[gnu::nonnull(1)]]
|
||||
[[maybe_unused]] [[nodiscard("pointer must be freed")]] [[gnu::nonnull(1)]]
|
||||
struct AST_Node_t* AST_remove_child(struct AST_Node_t* owner, size_t idx);
|
||||
|
||||
/**
|
||||
* @brief Detach a child from its parent. This involves removing the child from its parent
|
||||
* and marking the parent of the child as NULL.
|
||||
* @attention The returned pointer is still valid. It must be freed at some pointer later.
|
||||
* @brief Detach a child from its parent. This involves removing the child from
|
||||
* its parent and marking the parent of the child as NULL.
|
||||
* @attention The returned pointer is still valid. It must be freed at some
|
||||
* pointer later.
|
||||
* @param owner the owner to remove the child from
|
||||
* @param child the child to detach
|
||||
* @return a pointer to child detached
|
||||
*/
|
||||
[[nodiscard("pointer must be freed")]]
|
||||
[[gnu::nonnull(1), gnu::nonnull(1)]]
|
||||
struct AST_Node_t* AST_detach_child(struct AST_Node_t* owner, const struct AST_Node_t* child);
|
||||
[[nodiscard("pointer must be freed")]] [[gnu::nonnull(1), gnu::nonnull(1)]]
|
||||
struct AST_Node_t* AST_detach_child(struct AST_Node_t* owner,
|
||||
const struct AST_Node_t* child);
|
||||
|
||||
/**
|
||||
* @brief Return a pointer to the n-th child of a node
|
||||
* @attention Pointer to childen nodes will never change.
|
||||
* However, the index a node is stored within a parent can change
|
||||
* if a child of lower index is removed, thus reducing the childrens index by one.
|
||||
* if a child of lower index is removed, thus reducing the childrens
|
||||
* index by one.
|
||||
* @param owner the parent node which owns the children
|
||||
* @param idx the index of the child to get a pointer to
|
||||
* @return a pointer to the n-th child of the owner node
|
||||
*/
|
||||
[[maybe_unused]]
|
||||
[[gnu::nonnull(1)]]
|
||||
struct AST_Node_t *AST_get_node(struct AST_Node_t *owner, size_t idx);
|
||||
[[maybe_unused]] [[gnu::nonnull(1)]]
|
||||
struct AST_Node_t* AST_get_node(struct AST_Node_t* owner, size_t idx);
|
||||
|
||||
AST_NODE_PTR AST_get_last_node(AST_NODE_PTR node);
|
||||
|
||||
/**
|
||||
* @brief Execute a function for every child, grandchild, ... and the supplied node as topmost ancestor
|
||||
* @brief Execute a function for every child, grandchild, ... and the supplied
|
||||
* node as topmost ancestor
|
||||
* @param root the root to recursively execute a function for
|
||||
* @param for_each the function to execute
|
||||
*/
|
||||
[[maybe_unused]]
|
||||
[[gnu::nonnull(1), gnu::nonnull(2)]]
|
||||
void AST_visit_nodes_recurse(struct AST_Node_t *root,
|
||||
void (*for_each)(struct AST_Node_t *node,
|
||||
[[maybe_unused]] [[gnu::nonnull(1), gnu::nonnull(2)]]
|
||||
void AST_visit_nodes_recurse(struct AST_Node_t* root,
|
||||
void (*for_each)(struct AST_Node_t* node,
|
||||
size_t depth));
|
||||
|
||||
/**
|
||||
|
@ -220,11 +221,11 @@ void AST_visit_nodes_recurse(struct AST_Node_t *root,
|
|||
* @param stream The stream to print to. Can be a file, stdout, ...
|
||||
* @param node the topmost ancestor
|
||||
*/
|
||||
[[maybe_unused]]
|
||||
[[gnu::nonnull(1), gnu::nonnull(2)]]
|
||||
[[maybe_unused]] [[gnu::nonnull(1), gnu::nonnull(2)]]
|
||||
void AST_fprint_graphviz(FILE* stream, const struct AST_Node_t* node);
|
||||
|
||||
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);
|
||||
|
||||
[[gnu::nonnull(1), gnu::nonnull(3)]]
|
||||
void AST_merge_modules(AST_NODE_PTR dst, size_t i, AST_NODE_PTR src);
|
||||
|
|
154
src/cfg/opt.c
154
src/cfg/opt.c
|
@ -2,14 +2,14 @@
|
|||
// Created by servostar on 5/31/24.
|
||||
//
|
||||
|
||||
#include <assert.h>
|
||||
#include <cfg/opt.h>
|
||||
#include <io/files.h>
|
||||
#include <link/driver.h>
|
||||
#include <mem/cache.h>
|
||||
#include <string.h>
|
||||
#include <sys/log.h>
|
||||
#include <io/files.h>
|
||||
#include <assert.h>
|
||||
#include <toml.h>
|
||||
#include <mem/cache.h>
|
||||
#include <link/driver.h>
|
||||
|
||||
static GHashTable* args = NULL;
|
||||
|
||||
|
@ -34,14 +34,15 @@ void parse_options(int argc, char* argv[]) {
|
|||
for (int i = 0; i < argc; i++) {
|
||||
Option* option = mem_alloc(MemoryNamespaceOpt, sizeof(Option));
|
||||
option->is_opt = g_str_has_prefix(argv[i], "--");
|
||||
option->string = mem_strdup(MemoryNamespaceOpt, 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;
|
||||
|
||||
char* equals = strchr(option->string, '=');
|
||||
if (equals != NULL) {
|
||||
option->value = equals + 1;
|
||||
*equals = 0;
|
||||
*equals = 0;
|
||||
}
|
||||
|
||||
g_hash_table_insert(args, (gpointer) option->string, (gpointer) option);
|
||||
|
@ -94,17 +95,18 @@ TargetConfig* default_target_config() {
|
|||
|
||||
TargetConfig* config = mem_alloc(MemoryNamespaceOpt, sizeof(TargetConfig));
|
||||
|
||||
config->name = mem_strdup(MemoryNamespaceOpt, "out");
|
||||
config->print_ast = false;
|
||||
config->print_asm = false;
|
||||
config->print_ir = false;
|
||||
config->driver = mem_strdup(MemoryNamespaceOpt, DEFAULT_DRIVER);
|
||||
config->mode = Application;
|
||||
config->archive_directory = mem_strdup(MemoryNamespaceOpt, "archive");
|
||||
config->output_directory = mem_strdup(MemoryNamespaceOpt, "bin");
|
||||
config->name = mem_strdup(MemoryNamespaceOpt, "out");
|
||||
config->print_ast = false;
|
||||
config->print_asm = false;
|
||||
config->print_ir = false;
|
||||
config->driver = mem_strdup(MemoryNamespaceOpt, DEFAULT_DRIVER);
|
||||
config->mode = Application;
|
||||
config->archive_directory = mem_strdup(MemoryNamespaceOpt, "archive");
|
||||
config->output_directory = mem_strdup(MemoryNamespaceOpt, "bin");
|
||||
config->optimization_level = 1;
|
||||
config->root_module = NULL;
|
||||
config->link_search_paths = mem_new_g_array(MemoryNamespaceOpt, sizeof(char*));
|
||||
config->root_module = NULL;
|
||||
config->link_search_paths =
|
||||
mem_new_g_array(MemoryNamespaceOpt, sizeof(char*));
|
||||
config->lld_fatal_warnings = FALSE;
|
||||
config->gsc_fatal_warnings = FALSE;
|
||||
config->import_paths = mem_new_g_array(MemoryNamespaceOpt, sizeof(char*));
|
||||
|
@ -148,7 +150,8 @@ TargetConfig* default_target_config_from_args() {
|
|||
} else if (strcmp(opt->value, "lib") == 0) {
|
||||
config->mode = Library;
|
||||
} else {
|
||||
print_message(Warning, "Invalid compilation mode: %s", opt->value);
|
||||
print_message(Warning, "Invalid compilation mode: %s",
|
||||
opt->value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -169,7 +172,7 @@ TargetConfig* default_target_config_from_args() {
|
|||
}
|
||||
}
|
||||
|
||||
char* cwd = g_get_current_dir();
|
||||
char* cwd = g_get_current_dir();
|
||||
char* cached_cwd = mem_strdup(MemoryNamespaceOpt, cwd);
|
||||
g_array_append_val(config->link_search_paths, cached_cwd);
|
||||
free(cwd);
|
||||
|
@ -180,10 +183,10 @@ TargetConfig* default_target_config_from_args() {
|
|||
if (opt->value != NULL) {
|
||||
|
||||
const char* start = opt->value;
|
||||
const char* end = NULL;
|
||||
while((end = strchr(start, ',')) != NULL) {
|
||||
const char* end = NULL;
|
||||
while ((end = strchr(start, ',')) != NULL) {
|
||||
|
||||
const int len = end - start;
|
||||
const int len = end - start;
|
||||
char* link_path = mem_alloc(MemoryNamespaceOpt, len + 1);
|
||||
memcpy(link_path, start, len);
|
||||
link_path[len] = 0;
|
||||
|
@ -211,10 +214,12 @@ TargetConfig* default_target_config_from_args() {
|
|||
} else {
|
||||
|
||||
if (files->len > 1) {
|
||||
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 = mem_strdup(MemoryNamespaceOpt, g_array_index(files, char*, 0));
|
||||
config->root_module =
|
||||
mem_strdup(MemoryNamespaceOpt, g_array_index(files, char*, 0));
|
||||
}
|
||||
|
||||
char* default_import_path = mem_strdup(MemoryNamespaceOpt, ".");
|
||||
|
@ -226,10 +231,10 @@ TargetConfig* default_target_config_from_args() {
|
|||
if (opt->value != NULL) {
|
||||
|
||||
const char* start = opt->value;
|
||||
const char* end = NULL;
|
||||
while((end = strchr(start, ',')) != NULL) {
|
||||
const char* end = NULL;
|
||||
while ((end = strchr(start, ',')) != NULL) {
|
||||
|
||||
const int len = end - start;
|
||||
const int len = end - start;
|
||||
char* import_path = mem_alloc(MemoryNamespaceOpt, len + 1);
|
||||
memcpy(import_path, start, len);
|
||||
import_path[len] = 0;
|
||||
|
@ -256,39 +261,43 @@ TargetConfig* default_target_config_from_args() {
|
|||
void print_help(void) {
|
||||
DEBUG("printing help dialog...");
|
||||
|
||||
const char *lines[] = {
|
||||
const char* lines[] = {
|
||||
"Gemstone Compiler (c) GPL-2.0",
|
||||
"Build a project target: gsc build [target]|all",
|
||||
"Compile non-project file: gsc compile <target-options> [file]",
|
||||
"Output information: gsc <option>",
|
||||
"Target options:",
|
||||
" --print-ast print resulting abstract syntax tree to a file",
|
||||
" --print-ast print resulting abstract syntax tree to a "
|
||||
"file",
|
||||
" --print-asm print resulting assembly language to a file",
|
||||
" --print-ir print resulting LLVM-IR to a file",
|
||||
" --mode=[app|lib] set the compilation mode to either application or library",
|
||||
" --mode=[app|lib] set the compilation mode to either "
|
||||
"application or library",
|
||||
" --output=name name of output files without extension",
|
||||
" --driver set binary driver to use",
|
||||
" --link-paths=[paths,] set a list of directories to for libraries in",
|
||||
" --link-paths=[paths,] set a list of directories to for libraries "
|
||||
"in",
|
||||
" --all-fatal-warnings treat all warnings as errors",
|
||||
" --lld-fatal-warnings treat linker warnings as errors",
|
||||
" --gsc-fatal-warnings treat parser warnings as errors",
|
||||
"Options:",
|
||||
" --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",
|
||||
" --list-targets print a list of all available targets supported",
|
||||
" --list-driver print a list of all available binary driver",
|
||||
" --help print this help dialog",
|
||||
" --color-always always colorize output",
|
||||
" --print-gc-stats print statistics of the garbage collector"
|
||||
};
|
||||
" --print-gc-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++) {
|
||||
printf("%s\n", lines[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void get_bool(bool* boolean, const toml_table_t *table, const char* name) {
|
||||
static void get_bool(bool* boolean, const toml_table_t* table,
|
||||
const char* name) {
|
||||
DEBUG("retrieving boolean %s", name);
|
||||
|
||||
const toml_datum_t datum = toml_bool_in(table, name);
|
||||
|
@ -299,7 +308,8 @@ static void get_bool(bool* boolean, const toml_table_t *table, const char* name)
|
|||
}
|
||||
}
|
||||
|
||||
static void get_str(char** string, const toml_table_t *table, const char* name) {
|
||||
static void get_str(char** string, const toml_table_t* table,
|
||||
const char* name) {
|
||||
DEBUG("retrieving string %s", name);
|
||||
|
||||
const toml_datum_t datum = toml_string_in(table, name);
|
||||
|
@ -310,7 +320,7 @@ static void get_str(char** string, const toml_table_t *table, const char* name)
|
|||
}
|
||||
}
|
||||
|
||||
static void get_int(int* integer, const toml_table_t *table, const char* name) {
|
||||
static void get_int(int* integer, const toml_table_t* table, const char* name) {
|
||||
DEBUG("retrieving integer %s", name);
|
||||
|
||||
const toml_datum_t datum = toml_int_in(table, name);
|
||||
|
@ -321,7 +331,8 @@ static void get_int(int* integer, const toml_table_t *table, const char* name) {
|
|||
}
|
||||
}
|
||||
|
||||
static void get_array(GArray* array, const toml_table_t *table, const char* name) {
|
||||
static void get_array(GArray* array, const toml_table_t* table,
|
||||
const char* name) {
|
||||
const toml_array_t* toml_array = toml_array_in(table, name);
|
||||
|
||||
if (toml_array) {
|
||||
|
@ -336,7 +347,8 @@ static void get_array(GArray* array, const toml_table_t *table, const char* name
|
|||
}
|
||||
}
|
||||
|
||||
static int parse_project_table(ProjectConfig *config, const toml_table_t *project_table) {
|
||||
static int parse_project_table(ProjectConfig* config,
|
||||
const toml_table_t* project_table) {
|
||||
DEBUG("parsing project table...");
|
||||
|
||||
// project name
|
||||
|
@ -350,14 +362,15 @@ static int parse_project_table(ProjectConfig *config, const toml_table_t *projec
|
|||
get_str(&config->name, project_table, "name");
|
||||
|
||||
// author names
|
||||
toml_array_t *authors = toml_array_in(project_table, "authors");
|
||||
toml_array_t* authors = toml_array_in(project_table, "authors");
|
||||
if (authors) {
|
||||
config->authors = mem_new_g_array(MemoryNamespaceOpt, sizeof(char *));
|
||||
config->authors = mem_new_g_array(MemoryNamespaceOpt, sizeof(char*));
|
||||
|
||||
for (int i = 0;; i++) {
|
||||
toml_datum_t author = toml_string_at(authors, i);
|
||||
if (!author.ok)
|
||||
if (!author.ok) {
|
||||
break;
|
||||
}
|
||||
|
||||
g_array_append_val(config->authors, author.u.s);
|
||||
}
|
||||
|
@ -383,11 +396,13 @@ static int get_mode_from_str(TargetCompilationMode* mode, const char* name) {
|
|||
*mode = Library;
|
||||
return PROJECT_OK;
|
||||
}
|
||||
print_message(Error, "Invalid project configuration, mode is invalid: %s", name);
|
||||
print_message(Error, "Invalid project configuration, mode is invalid: %s",
|
||||
name);
|
||||
return PROJECT_SEMANTIC_ERR;
|
||||
}
|
||||
|
||||
static int parse_target(const ProjectConfig *config, const toml_table_t *target_table, const char* name) {
|
||||
static int parse_target(const ProjectConfig* config,
|
||||
const toml_table_t* target_table, const char* name) {
|
||||
DEBUG("parsing target table...");
|
||||
|
||||
TargetConfig* target_config = default_target_config();
|
||||
|
@ -402,8 +417,10 @@ static int parse_target(const ProjectConfig *config, const toml_table_t *target_
|
|||
get_str(&target_config->root_module, target_table, "root");
|
||||
get_str(&target_config->output_directory, target_table, "output");
|
||||
get_str(&target_config->archive_directory, target_table, "archive");
|
||||
get_bool(&target_config->lld_fatal_warnings, target_table, "lld_fatal_warnings");
|
||||
get_bool(&target_config->gsc_fatal_warnings, target_table, "gsc_fatal_warnings");
|
||||
get_bool(&target_config->lld_fatal_warnings, target_table,
|
||||
"lld_fatal_warnings");
|
||||
get_bool(&target_config->gsc_fatal_warnings, target_table,
|
||||
"gsc_fatal_warnings");
|
||||
|
||||
get_int(&target_config->optimization_level, target_table, "opt");
|
||||
|
||||
|
@ -413,7 +430,7 @@ static int parse_target(const ProjectConfig *config, const toml_table_t *target_
|
|||
if (err != PROJECT_OK) {
|
||||
return err;
|
||||
}
|
||||
char* cwd = g_get_current_dir();
|
||||
char* cwd = g_get_current_dir();
|
||||
char* cached_cwd = mem_strdup(MemoryNamespaceOpt, cwd);
|
||||
free(cwd);
|
||||
|
||||
|
@ -428,54 +445,59 @@ static int parse_target(const ProjectConfig *config, const toml_table_t *target_
|
|||
return PROJECT_OK;
|
||||
}
|
||||
|
||||
static int parse_targets(ProjectConfig *config, const toml_table_t *root) {
|
||||
static int parse_targets(ProjectConfig* config, const toml_table_t* root) {
|
||||
DEBUG("parsing targets of project \"%s\"", config->name);
|
||||
|
||||
toml_table_t *targets = toml_table_in(root, "target");
|
||||
toml_table_t* targets = toml_table_in(root, "target");
|
||||
if (targets == NULL) {
|
||||
print_message(Warning, "Project has no targets");
|
||||
return PROJECT_SEMANTIC_ERR;
|
||||
}
|
||||
|
||||
config->targets = mem_new_g_hash_table(MemoryNamespaceOpt, g_str_hash, g_str_equal);
|
||||
config->targets =
|
||||
mem_new_g_hash_table(MemoryNamespaceOpt, g_str_hash, g_str_equal);
|
||||
|
||||
for (int i = 0; i < toml_table_ntab(targets); i++) {
|
||||
const char *key = toml_key_in(targets, i);
|
||||
const char* key = toml_key_in(targets, i);
|
||||
|
||||
if (key == NULL)
|
||||
if (key == NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
toml_table_t *target = toml_table_in(targets, key);
|
||||
parse_target(config, target, mem_strdup(MemoryNamespaceOpt, (char*) key));
|
||||
toml_table_t* target = toml_table_in(targets, key);
|
||||
parse_target(config, target,
|
||||
mem_strdup(MemoryNamespaceOpt, (char*) key));
|
||||
}
|
||||
|
||||
return PROJECT_OK;
|
||||
}
|
||||
|
||||
int load_project_config(ProjectConfig *config) {
|
||||
int load_project_config(ProjectConfig* config) {
|
||||
DEBUG("loading project config...");
|
||||
|
||||
FILE *config_file = fopen(PROJECT_CONFIG_FILE, "r");
|
||||
FILE* config_file = fopen(PROJECT_CONFIG_FILE, "r");
|
||||
if (config_file == NULL) {
|
||||
print_message(Error, "Cannot open file %s: %s", PROJECT_CONFIG_FILE, strerror(errno));
|
||||
print_message(Error, "Cannot open file %s: %s", PROJECT_CONFIG_FILE,
|
||||
strerror(errno));
|
||||
return PROJECT_SEMANTIC_ERR;
|
||||
}
|
||||
|
||||
char err_buf[TOML_ERROR_MSG_BUF];
|
||||
|
||||
toml_table_t *conf = toml_parse_file(config_file, err_buf, sizeof(err_buf));
|
||||
toml_table_t* conf = toml_parse_file(config_file, err_buf, sizeof(err_buf));
|
||||
fclose(config_file);
|
||||
|
||||
if (conf != NULL) {
|
||||
int status = PROJECT_SEMANTIC_ERR;
|
||||
toml_table_t *project = toml_table_in(conf, "project");
|
||||
int status = PROJECT_SEMANTIC_ERR;
|
||||
toml_table_t* project = toml_table_in(conf, "project");
|
||||
|
||||
if (project != NULL) {
|
||||
if (parse_project_table(config, project) == PROJECT_OK) {
|
||||
status = parse_targets(config, conf);
|
||||
}
|
||||
} else {
|
||||
print_message(Error, "Invalid project configuration: missing project table.");
|
||||
print_message(
|
||||
Error, "Invalid project configuration: missing project table.");
|
||||
}
|
||||
|
||||
toml_free(conf);
|
||||
|
@ -528,7 +550,8 @@ void delete_project_config(ProjectConfig* config) {
|
|||
|
||||
char* key;
|
||||
TargetConfig* val;
|
||||
while (g_hash_table_iter_next(&iter, (gpointer) &key, (gpointer) &val)) {
|
||||
while (
|
||||
g_hash_table_iter_next(&iter, (gpointer) &key, (gpointer) &val)) {
|
||||
delete_target_config(val);
|
||||
}
|
||||
|
||||
|
@ -539,14 +562,15 @@ void delete_project_config(ProjectConfig* config) {
|
|||
}
|
||||
|
||||
ProjectConfig* default_project_config() {
|
||||
ProjectConfig* config = mem_alloc(MemoryNamespaceOpt, sizeof(ProjectConfig));
|
||||
ProjectConfig* config =
|
||||
mem_alloc(MemoryNamespaceOpt, sizeof(ProjectConfig));
|
||||
|
||||
config->authors = NULL;
|
||||
config->name = NULL;
|
||||
config->name = NULL;
|
||||
config->targets = NULL;
|
||||
config->license = NULL;
|
||||
config->version = NULL;
|
||||
config->desc = NULL;
|
||||
config->desc = NULL;
|
||||
|
||||
return config;
|
||||
}
|
||||
|
|
|
@ -8,10 +8,10 @@
|
|||
#include <glib.h>
|
||||
|
||||
#define MAX_TARGETS_PER_PROJECT 100
|
||||
#define PROJECT_CONFIG_FILE "build.toml"
|
||||
#define PROJECT_CONFIG_FILE "build.toml"
|
||||
|
||||
#define PROJECT_OK 0
|
||||
#define PROJECT_TOML_ERR 1
|
||||
#define PROJECT_OK 0
|
||||
#define PROJECT_TOML_ERR 1
|
||||
#define PROJECT_SEMANTIC_ERR 2
|
||||
|
||||
#define TOML_ERROR_MSG_BUF 256
|
||||
|
@ -49,7 +49,8 @@ typedef struct TargetConfig_t {
|
|||
char* root_module;
|
||||
// output directory for binaries
|
||||
char* output_directory;
|
||||
// output directory for intermediate representations (LLVM-IR, Assembly, ...)
|
||||
// output directory for intermediate representations (LLVM-IR, Assembly,
|
||||
// ...)
|
||||
char* archive_directory;
|
||||
// binary driver for executable generation
|
||||
char* driver;
|
||||
|
@ -130,7 +131,7 @@ TargetConfig* default_target_config_from_args();
|
|||
* @return
|
||||
*/
|
||||
[[gnu::nonnull(1)]]
|
||||
int load_project_config(ProjectConfig *config);
|
||||
int load_project_config(ProjectConfig* config);
|
||||
|
||||
/**
|
||||
* @brief Print a help dialog to stdout.
|
||||
|
@ -182,10 +183,9 @@ const Option* get_option(const char* option);
|
|||
* @param command
|
||||
* @return an array of options that followed command.
|
||||
*/
|
||||
[[gnu::nonnull(1)]]
|
||||
[[nodiscard("must be freed")]]
|
||||
[[gnu::nonnull(1)]] [[nodiscard("must be freed")]]
|
||||
GArray* get_non_options_after(const char* command);
|
||||
|
||||
void init_toml();
|
||||
|
||||
#endif //GEMSTONE_OPT_H
|
||||
#endif // GEMSTONE_OPT_H
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
|
||||
#include "set/types.h"
|
||||
|
||||
#include <codegen/backend.h>
|
||||
#include <sys/log.h>
|
||||
|
||||
|
@ -12,18 +13,19 @@ static struct CodegenBackend_t {
|
|||
|
||||
BackendError new_backend_error(BackendErrorKind kind) {
|
||||
BackendError error;
|
||||
error.kind = kind;
|
||||
error.kind = kind;
|
||||
error.impl.ast_node = NULL;
|
||||
error.impl.message = NULL;
|
||||
error.impl.message = NULL;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
BackendError new_backend_impl_error(BackendErrorKind kind, AST_NODE_PTR node, const char* message) {
|
||||
BackendError new_backend_impl_error(BackendErrorKind kind, AST_NODE_PTR node,
|
||||
const char* message) {
|
||||
BackendError error;
|
||||
error.kind = kind;
|
||||
error.kind = kind;
|
||||
error.impl.ast_node = node;
|
||||
error.impl.message = message;
|
||||
error.impl.message = message;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
@ -39,7 +41,8 @@ BackendError init_backend(void) {
|
|||
BackendError code = CodegenBackend.init_func();
|
||||
|
||||
if (code.kind != Success) {
|
||||
ERROR("failed to initialize backend: %s with code: %ld", CodegenBackend.name, code);
|
||||
ERROR("failed to initialize backend: %s with code: %ld",
|
||||
CodegenBackend.name, code);
|
||||
return code;
|
||||
}
|
||||
|
||||
|
@ -57,18 +60,21 @@ BackendError deinit_backend(void) {
|
|||
BackendError code = CodegenBackend.deinit_func();
|
||||
|
||||
if (code.kind != Success) {
|
||||
ERROR("failed to undo initialization of backend: %s with code: %ld", CodegenBackend.name, code);
|
||||
ERROR("failed to undo initialization of backend: %s with code: %ld",
|
||||
CodegenBackend.name, code);
|
||||
return code;
|
||||
}
|
||||
|
||||
return new_backend_error(Success);
|
||||
}
|
||||
|
||||
BackendError set_backend(const codegen_init init_func, const codegen_deinit deinit_func, const codegen codegen_func, const char* name) {
|
||||
CodegenBackend.init_func = init_func;
|
||||
CodegenBackend.deinit_func = deinit_func;
|
||||
BackendError set_backend(const codegen_init init_func,
|
||||
const codegen_deinit deinit_func,
|
||||
const codegen codegen_func, const char* name) {
|
||||
CodegenBackend.init_func = init_func;
|
||||
CodegenBackend.deinit_func = deinit_func;
|
||||
CodegenBackend.codegen_func = codegen_func;
|
||||
CodegenBackend.name = name;
|
||||
CodegenBackend.name = name;
|
||||
|
||||
return new_backend_error(Success);
|
||||
}
|
||||
|
@ -83,7 +89,8 @@ BackendError generate_code(const Module* root, const TargetConfig* target) {
|
|||
|
||||
BackendError code = CodegenBackend.codegen_func(root, target);
|
||||
if (code.kind) {
|
||||
ERROR("code generation of backend: %s failed with code: %ld `%s`", CodegenBackend.name, code, code.impl.message);
|
||||
ERROR("code generation of backend: %s failed with code: %ld `%s`",
|
||||
CodegenBackend.name, code, code.impl.message);
|
||||
return code;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
#ifndef CODEGN_BACKEND_H_
|
||||
#define CODEGN_BACKEND_H_
|
||||
|
||||
#include <set/types.h>
|
||||
#include <ast/ast.h>
|
||||
#include <cfg/opt.h>
|
||||
#include <set/types.h>
|
||||
|
||||
typedef struct BackendImplError_t {
|
||||
// faulty AST node
|
||||
|
@ -28,8 +28,8 @@ typedef struct BackendError_t {
|
|||
} BackendError;
|
||||
|
||||
/**
|
||||
* @brief Function called by the compiler backend to generate an intermediate format
|
||||
* from AST. Returns a custom container for its intermediate language.
|
||||
* @brief Function called by the compiler backend to generate an intermediate
|
||||
* format from AST. Returns a custom container for its intermediate language.
|
||||
*/
|
||||
typedef BackendError (*codegen)(const Module*, const TargetConfig* target);
|
||||
|
||||
|
@ -45,51 +45,55 @@ typedef BackendError (*codegen_deinit)(void);
|
|||
|
||||
/**
|
||||
* @brief Set the backend functions to use
|
||||
*
|
||||
* @param init_func the function to call for initializing the backend
|
||||
* @param deinit_func the function to call for undoing the initialization of the backend
|
||||
*
|
||||
* @param init_func the function to call for initializing the backend
|
||||
* @param deinit_func the function to call for undoing the initialization of the
|
||||
* backend
|
||||
* @param codegen_func the function to call when generating code
|
||||
* @param name name of the backend
|
||||
*/
|
||||
[[nodiscard]]
|
||||
[[gnu::nonnull(1), gnu::nonnull(2), gnu::nonnull(3), gnu::nonnull(3)]]
|
||||
BackendError set_backend(const codegen_init init_func, const codegen_deinit deinit_func, const codegen codegen_func, const char* name);
|
||||
[[nodiscard]] [[gnu::nonnull(1), gnu::nonnull(2), gnu::nonnull(3),
|
||||
gnu::nonnull(3)]]
|
||||
BackendError set_backend(const codegen_init init_func,
|
||||
const codegen_deinit deinit_func,
|
||||
const codegen codegen_func, const char* name);
|
||||
|
||||
/**
|
||||
* @brief Call the initialization function of the backend
|
||||
*
|
||||
* @return BackendError
|
||||
*
|
||||
* @return BackendError
|
||||
*/
|
||||
[[nodiscard]]
|
||||
BackendError init_backend(void);
|
||||
|
||||
/**
|
||||
* @brief Call the undo initialization function of the backend
|
||||
*
|
||||
* @return BackendError
|
||||
*
|
||||
* @return BackendError
|
||||
*/
|
||||
[[nodiscard]]
|
||||
BackendError deinit_backend(void);
|
||||
|
||||
/**
|
||||
* @brief Generate intermediate code with the registered backend
|
||||
*
|
||||
*
|
||||
* @param root the root node of the AST
|
||||
* @param code output pointer to the intermediate code
|
||||
* @return BackendError
|
||||
* @return BackendError
|
||||
*/
|
||||
[[nodiscard]]
|
||||
BackendError generate_code(const Module* root, const TargetConfig* target);
|
||||
|
||||
/**
|
||||
* @brief Create a new backend error
|
||||
*
|
||||
*
|
||||
* @param kind must ne != Implementation
|
||||
* @return BackendError
|
||||
* @return BackendError
|
||||
*/
|
||||
BackendError new_backend_error(BackendErrorKind kind);
|
||||
|
||||
BackendError new_backend_impl_error(BackendErrorKind kind, AST_NODE_PTR node, const char* message);
|
||||
BackendError new_backend_impl_error(BackendErrorKind kind, AST_NODE_PTR node,
|
||||
const char* message);
|
||||
|
||||
#define SUCCESS new_backend_error(Success)
|
||||
|
||||
|
|
149
src/compiler.c
149
src/compiler.c
|
@ -2,42 +2,41 @@
|
|||
// Created by servostar on 6/2/24.
|
||||
//
|
||||
|
||||
#include <compiler.h>
|
||||
#include <ast/ast.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/log.h>
|
||||
#include <yacc/parser.tab.h>
|
||||
#include <lex/util.h>
|
||||
#include <io/files.h>
|
||||
#include <assert.h>
|
||||
#include <ast/ast.h>
|
||||
#include <cfg/opt.h>
|
||||
#include <codegen/backend.h>
|
||||
#include <compiler.h>
|
||||
#include <io/files.h>
|
||||
#include <lex/util.h>
|
||||
#include <llvm/backend.h>
|
||||
#include <mem/cache.h>
|
||||
#include <set/set.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/log.h>
|
||||
#include <yacc/parser.tab.h>
|
||||
|
||||
#define GRAPHVIZ_FILE_EXTENSION "gv"
|
||||
|
||||
extern void yyrestart(FILE *);
|
||||
extern void yyrestart(FILE*);
|
||||
|
||||
// Module AST node used by the parser for AST construction.
|
||||
[[maybe_unused]]
|
||||
AST_NODE_PTR root;
|
||||
[[maybe_unused]] AST_NODE_PTR root;
|
||||
// Current file which gets compiled the parser.
|
||||
// NOTE: due to global state no concurrent compilation is possible
|
||||
// on parser level.
|
||||
[[maybe_unused]]
|
||||
ModuleFile *current_file;
|
||||
[[maybe_unused]] ModuleFile* current_file;
|
||||
|
||||
/**
|
||||
* @brief Compile the specified file into AST
|
||||
* @param ast Initialized AST module node to build program rules
|
||||
* @param file The file to be processed
|
||||
* @return EXIT_SUCCESS in case the parsing was success full anything lese if not
|
||||
* @return EXIT_SUCCESS in case the parsing was success full anything lese if
|
||||
* not
|
||||
*/
|
||||
[[nodiscard("AST may be in invalid state")]]
|
||||
[[gnu::nonnull(1), gnu::nonnull(1)]]
|
||||
static int compile_file_to_ast(AST_NODE_PTR ast, ModuleFile *file) {
|
||||
[[nodiscard("AST may be in invalid state")]] [[gnu::nonnull(1),
|
||||
gnu::nonnull(1)]]
|
||||
static int compile_file_to_ast(AST_NODE_PTR ast, ModuleFile* file) {
|
||||
assert(file->path != NULL);
|
||||
assert(ast != NULL);
|
||||
|
||||
|
@ -45,15 +44,16 @@ static int compile_file_to_ast(AST_NODE_PTR ast, ModuleFile *file) {
|
|||
|
||||
if (file->handle == NULL) {
|
||||
INFO("unable to open file: %s", file->path);
|
||||
print_message(Error, "Cannot open file %s: %s", file->path, strerror(errno));
|
||||
print_message(Error, "Cannot open file %s: %s", file->path,
|
||||
strerror(errno));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
DEBUG("parsing file: %s", file->path);
|
||||
// setup global state
|
||||
root = ast;
|
||||
root = ast;
|
||||
current_file = file;
|
||||
yyin = file->handle;
|
||||
yyin = file->handle;
|
||||
yyrestart(yyin);
|
||||
lex_reset();
|
||||
|
||||
|
@ -72,7 +72,7 @@ static int compile_file_to_ast(AST_NODE_PTR ast, ModuleFile *file) {
|
|||
* @param target
|
||||
* @return EXIT_SUCCESS if successful EXIT_FAILURE otherwise
|
||||
*/
|
||||
static int setup_target_environment(const TargetConfig *target) {
|
||||
static int setup_target_environment(const TargetConfig* target) {
|
||||
DEBUG("setting up environment for target: %s", target->name);
|
||||
|
||||
assert(target->output_directory != NULL);
|
||||
|
@ -80,21 +80,23 @@ static int setup_target_environment(const TargetConfig *target) {
|
|||
|
||||
int result = create_directory(target->archive_directory);
|
||||
if (result != 0 && errno != EEXIST) {
|
||||
const char *message = get_last_error();
|
||||
const char* message = get_last_error();
|
||||
assert(message != NULL);
|
||||
|
||||
print_message(Error, "Unable to create directory: %s: %s", target->archive_directory, message);
|
||||
free((void *) message);
|
||||
print_message(Error, "Unable to create directory: %s: %s",
|
||||
target->archive_directory, message);
|
||||
free((void*) message);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
result = create_directory(target->output_directory);
|
||||
if (result != 0 && errno != EEXIST) {
|
||||
const char *message = get_last_error();
|
||||
const char* message = get_last_error();
|
||||
assert(message != NULL);
|
||||
|
||||
print_message(Error, "Unable to create directory: %s: %s", target->output_directory, message);
|
||||
free((void *) message);
|
||||
print_message(Error, "Unable to create directory: %s: %s",
|
||||
target->output_directory, message);
|
||||
free((void*) message);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
|
@ -104,11 +106,12 @@ static int setup_target_environment(const TargetConfig *target) {
|
|||
}
|
||||
|
||||
/**
|
||||
* @brief Print the supplied AST of the specified target to a graphviz ".gv" file
|
||||
* @brief Print the supplied AST of the specified target to a graphviz ".gv"
|
||||
* file
|
||||
* @param ast
|
||||
* @param target
|
||||
*/
|
||||
static void print_ast_to_file(AST_NODE_PTR ast, const TargetConfig *target) {
|
||||
static void print_ast_to_file(AST_NODE_PTR ast, const TargetConfig* target) {
|
||||
assert(ast != NULL);
|
||||
assert(target != NULL);
|
||||
DEBUG("printing AST to file: %s", target->name);
|
||||
|
@ -120,16 +123,18 @@ static void print_ast_to_file(AST_NODE_PTR ast, const TargetConfig *target) {
|
|||
|
||||
// create file path to write graphviz to
|
||||
// basename of ile
|
||||
char* filename = g_strjoin(".", target->name, GRAPHVIZ_FILE_EXTENSION, NULL);
|
||||
char* filename =
|
||||
g_strjoin(".", target->name, GRAPHVIZ_FILE_EXTENSION, NULL);
|
||||
// relative path to file
|
||||
char *path = g_build_filename(target->archive_directory, filename, NULL);
|
||||
char* path = g_build_filename(target->archive_directory, filename, NULL);
|
||||
|
||||
DEBUG("Opening file to graph: %s", path);
|
||||
|
||||
FILE *output = fopen(path, "w");
|
||||
FILE* output = fopen(path, "w");
|
||||
if (output == NULL) {
|
||||
char *message = (char*) get_last_error();
|
||||
print_message(Error, "Unable to open file for syntax tree at: %s: %s", path, message);
|
||||
char* message = (char*) get_last_error();
|
||||
print_message(Error, "Unable to open file for syntax tree at: %s: %s",
|
||||
path, message);
|
||||
free(message);
|
||||
} else {
|
||||
DEBUG("writing graph to file...");
|
||||
|
@ -145,7 +150,8 @@ static void print_ast_to_file(AST_NODE_PTR ast, const TargetConfig *target) {
|
|||
g_free(path);
|
||||
}
|
||||
|
||||
static int run_backend_codegen(const Module* module, const TargetConfig* target) {
|
||||
static int run_backend_codegen(const Module* module,
|
||||
const TargetConfig* target) {
|
||||
DEBUG("initializing LLVM codegen backend...");
|
||||
llvm_backend_init();
|
||||
|
||||
|
@ -173,20 +179,23 @@ static int run_backend_codegen(const Module* module, const TargetConfig* target)
|
|||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
const char* get_absolute_import_path(const TargetConfig* config, const char* import_target_name) {
|
||||
const char* get_absolute_import_path(const TargetConfig* config,
|
||||
const char* import_target_name) {
|
||||
INFO("resolving absolute path for import target: %s", import_target_name);
|
||||
|
||||
if (!g_str_has_suffix(import_target_name, ".gsc")) {
|
||||
char* full_filename = g_strjoin("", import_target_name, ".gsc", NULL);
|
||||
import_target_name = mem_strdup(MemoryNamespaceLld, full_filename);
|
||||
import_target_name = mem_strdup(MemoryNamespaceLld, full_filename);
|
||||
g_free(full_filename);
|
||||
}
|
||||
|
||||
for (guint i = 0; i < config->import_paths->len; i++) {
|
||||
const char* import_directory_path = g_array_index(config->import_paths, char*, i);
|
||||
const char* import_directory_path =
|
||||
g_array_index(config->import_paths, char*, i);
|
||||
|
||||
char* path = g_build_filename(import_directory_path, import_target_name, NULL);
|
||||
char* cwd = g_get_current_dir();
|
||||
char* path =
|
||||
g_build_filename(import_directory_path, import_target_name, NULL);
|
||||
char* cwd = g_get_current_dir();
|
||||
char* canonical = g_canonicalize_filename(path, cwd);
|
||||
|
||||
const gboolean exists = g_file_test(canonical, G_FILE_TEST_EXISTS);
|
||||
|
@ -208,9 +217,13 @@ const char* get_absolute_import_path(const TargetConfig* config, const char* imp
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static int compile_module_with_dependencies(ModuleFileStack *unit, ModuleFile* file, const TargetConfig *target, AST_NODE_PTR root_module) {
|
||||
static int compile_module_with_dependencies(ModuleFileStack* unit,
|
||||
ModuleFile* file,
|
||||
const TargetConfig* target,
|
||||
AST_NODE_PTR root_module) {
|
||||
|
||||
GHashTable* imports = mem_new_g_hash_table(MemoryNamespaceAst, g_str_hash, g_str_equal);
|
||||
GHashTable* imports =
|
||||
mem_new_g_hash_table(MemoryNamespaceAst, g_str_hash, g_str_equal);
|
||||
|
||||
if (compile_file_to_ast(root_module, file) == EXIT_SUCCESS) {
|
||||
|
||||
|
@ -219,9 +232,11 @@ static int compile_module_with_dependencies(ModuleFileStack *unit, ModuleFile* f
|
|||
|
||||
if (child->kind == AST_Import || child->kind == AST_Include) {
|
||||
|
||||
const char* path = get_absolute_import_path(target, child->value);
|
||||
const char* path =
|
||||
get_absolute_import_path(target, child->value);
|
||||
if (path == NULL) {
|
||||
print_message(Error, "Cannot resolve path for import: `%s`", child->value);
|
||||
print_message(Error, "Cannot resolve path for import: `%s`",
|
||||
child->value);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
|
@ -229,10 +244,12 @@ static int compile_module_with_dependencies(ModuleFileStack *unit, ModuleFile* f
|
|||
continue;
|
||||
}
|
||||
|
||||
ModuleFile *imported_file = push_file(unit, path);
|
||||
AST_NODE_PTR imported_module = AST_new_node(empty_location(imported_file), AST_Module, NULL);
|
||||
ModuleFile* imported_file = push_file(unit, path);
|
||||
AST_NODE_PTR imported_module =
|
||||
AST_new_node(empty_location(imported_file), AST_Module, NULL);
|
||||
|
||||
if (compile_file_to_ast(imported_module, imported_file) == EXIT_SUCCESS) {
|
||||
if (compile_file_to_ast(imported_module, imported_file)
|
||||
== EXIT_SUCCESS) {
|
||||
AST_merge_modules(root_module, i + 1, imported_module);
|
||||
} else {
|
||||
return EXIT_FAILURE;
|
||||
|
@ -241,7 +258,8 @@ static int compile_module_with_dependencies(ModuleFileStack *unit, ModuleFile* f
|
|||
g_hash_table_insert(imports, (gpointer) path, NULL);
|
||||
|
||||
gchar* directory = g_path_get_dirname(path);
|
||||
gchar* cached_directory = mem_strdup(MemoryNamespaceLld, directory);
|
||||
gchar* cached_directory =
|
||||
mem_strdup(MemoryNamespaceLld, directory);
|
||||
g_free(directory);
|
||||
g_array_append_val(target->import_paths, cached_directory);
|
||||
}
|
||||
|
@ -258,13 +276,14 @@ static int compile_module_with_dependencies(ModuleFileStack *unit, ModuleFile* f
|
|||
* @param unit
|
||||
* @param target
|
||||
*/
|
||||
static int build_target(ModuleFileStack *unit, const TargetConfig *target) {
|
||||
static int build_target(ModuleFileStack* unit, const TargetConfig* target) {
|
||||
int err = EXIT_SUCCESS;
|
||||
|
||||
print_message(Info, "Building target: %s", target->name);
|
||||
|
||||
ModuleFile *file = push_file(unit, target->root_module);
|
||||
AST_NODE_PTR root_module = AST_new_node(empty_location(file), AST_Module, NULL);
|
||||
ModuleFile* file = push_file(unit, target->root_module);
|
||||
AST_NODE_PTR root_module =
|
||||
AST_new_node(empty_location(file), AST_Module, NULL);
|
||||
|
||||
err = compile_module_with_dependencies(unit, file, target, root_module);
|
||||
if (err == EXIT_SUCCESS) {
|
||||
|
@ -273,7 +292,7 @@ static int build_target(ModuleFileStack *unit, const TargetConfig *target) {
|
|||
if (err == 0) {
|
||||
|
||||
print_ast_to_file(root_module, target);
|
||||
Module *module = create_set(root_module);
|
||||
Module* module = create_set(root_module);
|
||||
|
||||
if (module != NULL) {
|
||||
err = run_backend_codegen(module, target);
|
||||
|
@ -300,10 +319,10 @@ static int build_target(ModuleFileStack *unit, const TargetConfig *target) {
|
|||
* Creates a single target by the given command line arguments.
|
||||
* @param unit
|
||||
*/
|
||||
static int compile_file(ModuleFileStack *unit) {
|
||||
static int compile_file(ModuleFileStack* unit) {
|
||||
INFO("compiling basic files...");
|
||||
|
||||
TargetConfig *target = default_target_config_from_args();
|
||||
TargetConfig* target = default_target_config_from_args();
|
||||
|
||||
if (target->root_module == NULL) {
|
||||
print_message(Error, "No input file specified.");
|
||||
|
@ -323,7 +342,8 @@ static int compile_file(ModuleFileStack *unit) {
|
|||
* @param unit
|
||||
* @param config
|
||||
*/
|
||||
static int build_project_targets(ModuleFileStack *unit, const ProjectConfig *config) {
|
||||
static int build_project_targets(ModuleFileStack* unit,
|
||||
const ProjectConfig* config) {
|
||||
int err = EXIT_SUCCESS;
|
||||
|
||||
if (is_option_set("all")) {
|
||||
|
@ -332,9 +352,10 @@ static int build_project_targets(ModuleFileStack *unit, const ProjectConfig *con
|
|||
|
||||
g_hash_table_iter_init(&iter, config->targets);
|
||||
|
||||
char *key;
|
||||
TargetConfig *val;
|
||||
while (g_hash_table_iter_next(&iter, (gpointer) &key, (gpointer) &val)) {
|
||||
char* key;
|
||||
TargetConfig* val;
|
||||
while (
|
||||
g_hash_table_iter_next(&iter, (gpointer) &key, (gpointer) &val)) {
|
||||
err = build_target(unit, val);
|
||||
}
|
||||
return err;
|
||||
|
@ -345,10 +366,11 @@ static int build_project_targets(ModuleFileStack *unit, const ProjectConfig *con
|
|||
|
||||
if (targets != NULL) {
|
||||
for (guint i = 0; i < targets->len; i++) {
|
||||
const char *target_name = g_array_index(targets, const char*, i);
|
||||
const char* target_name = g_array_index(targets, const char*, i);
|
||||
|
||||
if (g_hash_table_contains(config->targets, target_name)) {
|
||||
err = build_target(unit, g_hash_table_lookup(config->targets, target_name));
|
||||
err = build_target(
|
||||
unit, g_hash_table_lookup(config->targets, target_name));
|
||||
} else {
|
||||
print_message(Error, "Unknown target: %s", target_name);
|
||||
}
|
||||
|
@ -363,12 +385,13 @@ static int build_project_targets(ModuleFileStack *unit, const ProjectConfig *con
|
|||
}
|
||||
|
||||
/**
|
||||
* @brief Build targets from project. Configuration is provided by command line arguments.
|
||||
* @brief Build targets from project. Configuration is provided by command line
|
||||
* arguments.
|
||||
* @param unit File storage
|
||||
*/
|
||||
static int build_project(ModuleFileStack *unit) {
|
||||
ProjectConfig *config = default_project_config();
|
||||
int err = load_project_config(config);
|
||||
static int build_project(ModuleFileStack* unit) {
|
||||
ProjectConfig* config = default_project_config();
|
||||
int err = load_project_config(config);
|
||||
|
||||
if (err == PROJECT_OK) {
|
||||
err = build_project_targets(unit, config);
|
||||
|
|
|
@ -11,4 +11,4 @@
|
|||
*/
|
||||
int run_compiler();
|
||||
|
||||
#endif //GEMSTONE_COMPILER_H
|
||||
#endif // GEMSTONE_COMPILER_H
|
||||
|
|
116
src/io/files.c
116
src/io/files.c
|
@ -2,13 +2,13 @@
|
|||
// Created by servostar on 5/30/24.
|
||||
//
|
||||
|
||||
#include <assert.h>
|
||||
#include <io/files.h>
|
||||
#include <mem/cache.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/log.h>
|
||||
#include <assert.h>
|
||||
#include <sys/col.h>
|
||||
#include <mem/cache.h>
|
||||
#include <sys/log.h>
|
||||
|
||||
#ifdef __unix__
|
||||
|
||||
|
@ -37,29 +37,30 @@ ModuleFileStack new_file_stack() {
|
|||
return stack;
|
||||
}
|
||||
|
||||
ModuleFile *push_file(ModuleFileStack *stack, const char *path) {
|
||||
ModuleFile* push_file(ModuleFileStack* stack, const char* path) {
|
||||
assert(stack != NULL);
|
||||
|
||||
// lazy init of heap stack
|
||||
if (stack->files == NULL) {
|
||||
stack->files = mem_new_g_array(MemoryNamespaceStatic, sizeof(ModuleFile*));
|
||||
stack->files =
|
||||
mem_new_g_array(MemoryNamespaceStatic, sizeof(ModuleFile*));
|
||||
}
|
||||
|
||||
ModuleFile* new_file = mem_alloc(MemoryNamespaceStatic, sizeof(ModuleFile));
|
||||
new_file->handle = NULL;
|
||||
new_file->path = path;
|
||||
new_file->handle = NULL;
|
||||
new_file->path = path;
|
||||
new_file->statistics.warning_count = 0;
|
||||
new_file->statistics.error_count = 0;
|
||||
new_file->statistics.info_count = 0;
|
||||
new_file->statistics.error_count = 0;
|
||||
new_file->statistics.info_count = 0;
|
||||
|
||||
g_array_append_val(stack->files, new_file);
|
||||
|
||||
return new_file;
|
||||
}
|
||||
|
||||
void delete_files(ModuleFileStack *stack) {
|
||||
void delete_files(ModuleFileStack* stack) {
|
||||
for (size_t i = 0; i < stack->files->len; i++) {
|
||||
const ModuleFile *file = g_array_index(stack->files, ModuleFile*, i);
|
||||
const ModuleFile* file = g_array_index(stack->files, ModuleFile*, i);
|
||||
|
||||
if (file->handle != NULL) {
|
||||
DEBUG("closing file: %s", file->path);
|
||||
|
@ -78,7 +79,7 @@ void delete_files(ModuleFileStack *stack) {
|
|||
#define SEEK_BUF_BYTES 256
|
||||
|
||||
// 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 (feof(stream)) {
|
||||
buffer[0] = '\n';
|
||||
buffer[1] = 0;
|
||||
|
@ -92,7 +93,8 @@ static void custom_fgets(char *buffer, size_t n, FILE *stream) {
|
|||
}
|
||||
}
|
||||
|
||||
void print_diagnostic(TokenLocation *location, Message kind, const char *message, ...) {
|
||||
void print_diagnostic(TokenLocation* location, Message kind,
|
||||
const char* message, ...) {
|
||||
assert(location->file != NULL);
|
||||
assert(location->file->handle != NULL);
|
||||
assert(location != NULL);
|
||||
|
@ -101,37 +103,39 @@ void print_diagnostic(TokenLocation *location, Message kind, const char *message
|
|||
// reset to start
|
||||
rewind(location->file->handle);
|
||||
|
||||
char *buffer = alloca(SEEK_BUF_BYTES);
|
||||
char* buffer = alloca(SEEK_BUF_BYTES);
|
||||
unsigned long int line_count = 1;
|
||||
|
||||
// seek to first line
|
||||
while (line_count < location->line_start && fgets(buffer, SEEK_BUF_BYTES, location->file->handle) != NULL) {
|
||||
while (line_count < location->line_start
|
||||
&& fgets(buffer, SEEK_BUF_BYTES, location->file->handle) != NULL) {
|
||||
line_count += strchr(buffer, '\n') != NULL;
|
||||
}
|
||||
|
||||
const char *accent_color = RESET;
|
||||
const char *kind_text = "unknown";
|
||||
const char* accent_color = RESET;
|
||||
const char* kind_text = "unknown";
|
||||
switch (kind) {
|
||||
case Info:
|
||||
kind_text = "info";
|
||||
kind_text = "info";
|
||||
accent_color = CYAN;
|
||||
location->file->statistics.info_count++;
|
||||
break;
|
||||
case Warning:
|
||||
kind_text = "warning";
|
||||
kind_text = "warning";
|
||||
accent_color = YELLOW;
|
||||
location->file->statistics.warning_count++;
|
||||
break;
|
||||
case Error:
|
||||
kind_text = "error";
|
||||
kind_text = "error";
|
||||
accent_color = RED;
|
||||
location->file->statistics.error_count++;
|
||||
break;
|
||||
}
|
||||
|
||||
const char *absolute_path = get_absolute_path(location->file->path);
|
||||
const char* absolute_path = get_absolute_path(location->file->path);
|
||||
|
||||
printf("%s%s:%ld:%s %s%s:%s ", BOLD, absolute_path, location->line_start, RESET, accent_color, kind_text, RESET);
|
||||
printf("%s%s:%ld:%s %s%s:%s ", BOLD, absolute_path, location->line_start,
|
||||
RESET, accent_color, kind_text, RESET);
|
||||
|
||||
va_list args;
|
||||
va_start(args, message);
|
||||
|
@ -142,9 +146,10 @@ void print_diagnostic(TokenLocation *location, Message kind, const char *message
|
|||
|
||||
printf("\n");
|
||||
|
||||
mem_free((void *) absolute_path);
|
||||
mem_free((void*) absolute_path);
|
||||
|
||||
const unsigned long int lines = location->line_end - location->line_start + 1;
|
||||
const unsigned long int lines =
|
||||
location->line_end - location->line_start + 1;
|
||||
|
||||
for (unsigned long int l = 0; l < lines; l++) {
|
||||
printf(" %4ld | ", location->line_start + l);
|
||||
|
@ -166,11 +171,13 @@ void print_diagnostic(TokenLocation *location, Message kind, const char *message
|
|||
printf("%s", accent_color);
|
||||
|
||||
chars = 0;
|
||||
limit = min(location->col_end - location->col_start + 1, SEEK_BUF_BYTES);
|
||||
limit =
|
||||
min(location->col_end - location->col_start + 1, SEEK_BUF_BYTES);
|
||||
while (limit > 0) {
|
||||
custom_fgets(buffer, (int) limit, location->file->handle);
|
||||
chars += printf("%s", buffer);
|
||||
limit = min(location->col_end - location->col_start + 1 - chars, SEEK_BUF_BYTES);
|
||||
limit = min(location->col_end - location->col_start + 1 - chars,
|
||||
SEEK_BUF_BYTES);
|
||||
|
||||
if (strchr(buffer, '\n') != NULL) {
|
||||
goto cont;
|
||||
|
@ -185,7 +192,7 @@ void print_diagnostic(TokenLocation *location, Message kind, const char *message
|
|||
printf("%s", buffer);
|
||||
} while (strchr(buffer, '\n') == NULL);
|
||||
|
||||
cont:
|
||||
cont:
|
||||
printf("%s", RESET);
|
||||
}
|
||||
|
||||
|
@ -196,22 +203,25 @@ void print_diagnostic(TokenLocation *location, Message kind, const char *message
|
|||
|
||||
printf("%s", accent_color);
|
||||
printf("^");
|
||||
for (unsigned long int 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("%s\n\n", RESET);
|
||||
}
|
||||
|
||||
TokenLocation new_location(unsigned long int line_start, unsigned long int col_start, unsigned long int line_end,
|
||||
TokenLocation new_location(unsigned long int line_start,
|
||||
unsigned long int col_start,
|
||||
unsigned long int line_end,
|
||||
unsigned long int col_end, ModuleFile* file) {
|
||||
TokenLocation location;
|
||||
|
||||
location.line_start = line_start;
|
||||
location.line_end = line_end;
|
||||
location.col_start = col_start;
|
||||
location.col_end = col_end;
|
||||
location.file = file;
|
||||
location.line_end = line_end;
|
||||
location.col_start = col_start;
|
||||
location.col_end = col_end;
|
||||
location.file = file;
|
||||
|
||||
return location;
|
||||
}
|
||||
|
@ -220,16 +230,18 @@ TokenLocation empty_location(ModuleFile* file) {
|
|||
TokenLocation location;
|
||||
|
||||
location.line_start = 0;
|
||||
location.line_end = 0;
|
||||
location.col_start = 0;
|
||||
location.col_end = 0;
|
||||
location.file = file;
|
||||
location.line_end = 0;
|
||||
location.col_start = 0;
|
||||
location.col_end = 0;
|
||||
location.file = file;
|
||||
|
||||
return location;
|
||||
}
|
||||
|
||||
void print_file_statistics(ModuleFile *file) {
|
||||
if (file->statistics.info_count + file->statistics.warning_count + file->statistics.error_count < 1) {
|
||||
void print_file_statistics(ModuleFile* file) {
|
||||
if (file->statistics.info_count + file->statistics.warning_count
|
||||
+ file->statistics.error_count
|
||||
< 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -250,11 +262,11 @@ void print_file_statistics(ModuleFile *file) {
|
|||
printf("\n\n");
|
||||
}
|
||||
|
||||
void print_unit_statistics(ModuleFileStack *file_stack) {
|
||||
void print_unit_statistics(ModuleFileStack* file_stack) {
|
||||
FileDiagnosticStatistics stats;
|
||||
stats.info_count = 0;
|
||||
stats.info_count = 0;
|
||||
stats.warning_count = 0;
|
||||
stats.error_count = 0;
|
||||
stats.error_count = 0;
|
||||
|
||||
for (size_t i = 0; i < file_stack->files->len; i++) {
|
||||
ModuleFile* file = g_array_index(file_stack->files, ModuleFile*, i);
|
||||
|
@ -285,20 +297,20 @@ void print_unit_statistics(ModuleFileStack *file_stack) {
|
|||
printf("\n\n");
|
||||
}
|
||||
|
||||
void print_message(Message kind, const char *fmt, ...) {
|
||||
const char *accent_color = RESET;
|
||||
const char *kind_text = "unknown";
|
||||
void print_message(Message kind, const char* fmt, ...) {
|
||||
const char* accent_color = RESET;
|
||||
const char* kind_text = "unknown";
|
||||
switch (kind) {
|
||||
case Info:
|
||||
kind_text = "info";
|
||||
kind_text = "info";
|
||||
accent_color = CYAN;
|
||||
break;
|
||||
case Warning:
|
||||
kind_text = "warning";
|
||||
kind_text = "warning";
|
||||
accent_color = YELLOW;
|
||||
break;
|
||||
case Error:
|
||||
kind_text = "error";
|
||||
kind_text = "error";
|
||||
accent_color = RED;
|
||||
break;
|
||||
}
|
||||
|
@ -313,7 +325,7 @@ void print_message(Message kind, const char *fmt, ...) {
|
|||
va_end(args);
|
||||
}
|
||||
|
||||
int create_directory(const char *path) {
|
||||
int create_directory(const char* path) {
|
||||
assert(path != NULL);
|
||||
|
||||
DEBUG("creating directory: %s", path);
|
||||
|
@ -321,16 +333,16 @@ int create_directory(const char *path) {
|
|||
return g_mkdir_with_parents(path, 0755);
|
||||
}
|
||||
|
||||
const char *get_last_error() {
|
||||
const char* get_last_error() {
|
||||
return mem_strdup(MemoryNamespaceIo, strerror(errno));
|
||||
}
|
||||
|
||||
const char *get_absolute_path(const char *path) {
|
||||
const char* get_absolute_path(const char* path) {
|
||||
assert(path != NULL);
|
||||
|
||||
DEBUG("resolving absolute path of: %s", path);
|
||||
|
||||
char* cwd = g_get_current_dir();
|
||||
char* cwd = g_get_current_dir();
|
||||
char* canonical = g_canonicalize_filename(path, cwd);
|
||||
g_free(cwd);
|
||||
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
#ifndef GEMSTONE_FILES_H
|
||||
#define GEMSTONE_FILES_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <glib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#if defined(WIN32) || defined(_WIN32)
|
||||
#define PATH_SEPARATOR "\\"
|
||||
|
@ -21,20 +21,16 @@ typedef struct FileDiagnosticStatistics_t {
|
|||
} FileDiagnosticStatistics;
|
||||
|
||||
typedef struct ModuleFile_t {
|
||||
const char *path;
|
||||
FILE *handle;
|
||||
const char* path;
|
||||
FILE* handle;
|
||||
FileDiagnosticStatistics statistics;
|
||||
} ModuleFile;
|
||||
|
||||
typedef struct ModuleFileStack_t {
|
||||
GArray *files;
|
||||
GArray* files;
|
||||
} ModuleFileStack;
|
||||
|
||||
typedef enum Message_t {
|
||||
Info,
|
||||
Warning,
|
||||
Error
|
||||
} Message;
|
||||
typedef enum Message_t { Info, Warning, Error } Message;
|
||||
|
||||
typedef struct TokenLocation_t {
|
||||
unsigned long int line_start;
|
||||
|
@ -58,14 +54,14 @@ ModuleFileStack new_file_stack();
|
|||
* @return A new file module
|
||||
*/
|
||||
[[gnu::nonnull(1), gnu::nonnull(2)]]
|
||||
ModuleFile *push_file(ModuleFileStack *stack, const char *path);
|
||||
ModuleFile* push_file(ModuleFileStack* stack, const char* path);
|
||||
|
||||
/**
|
||||
* @brief Delete all files in the stack and the stack itself
|
||||
* @param stack
|
||||
*/
|
||||
[[gnu::nonnull(1)]]
|
||||
void delete_files(ModuleFileStack *stack);
|
||||
void delete_files(ModuleFileStack* stack);
|
||||
|
||||
/**
|
||||
* Create a new token location
|
||||
|
@ -75,7 +71,9 @@ void delete_files(ModuleFileStack *stack);
|
|||
* @param col_end
|
||||
* @return
|
||||
*/
|
||||
TokenLocation new_location(unsigned long int line_start, unsigned long int col_start, unsigned long int line_end,
|
||||
TokenLocation new_location(unsigned long int line_start,
|
||||
unsigned long int col_start,
|
||||
unsigned long int line_end,
|
||||
unsigned long int col_end, ModuleFile* file);
|
||||
|
||||
/**
|
||||
|
@ -92,7 +90,8 @@ TokenLocation empty_location(ModuleFile* file);
|
|||
* @param message
|
||||
*/
|
||||
[[gnu::nonnull(1)]]
|
||||
void print_diagnostic(TokenLocation *location, Message kind, const char *message, ...);
|
||||
void print_diagnostic(TokenLocation* location, Message kind,
|
||||
const char* message, ...);
|
||||
|
||||
[[gnu::nonnull(2)]]
|
||||
/**
|
||||
|
@ -101,25 +100,27 @@ void print_diagnostic(TokenLocation *location, Message kind, const char *message
|
|||
* @param fmt
|
||||
* @param ...
|
||||
*/
|
||||
void print_message(Message kind, const char *fmt, ...);
|
||||
void print_message(Message kind, const char* fmt, ...);
|
||||
|
||||
/**
|
||||
* @brief Print statistics about a specific file.
|
||||
* Will print the amount of infos, warning and errors emitted during compilation.
|
||||
* Will print the amount of infos, warning and errors emitted during
|
||||
* compilation.
|
||||
* @param file
|
||||
*/
|
||||
[[gnu::nonnull(1)]]
|
||||
void print_file_statistics(ModuleFile *file);
|
||||
void print_file_statistics(ModuleFile* file);
|
||||
|
||||
/**
|
||||
* @brief Print statistics of all files in the module stack.
|
||||
* @param file_stack
|
||||
*/
|
||||
[[gnu::nonnull(1)]]
|
||||
void print_unit_statistics(ModuleFileStack *file_stack);
|
||||
void print_unit_statistics(ModuleFileStack* file_stack);
|
||||
|
||||
/**
|
||||
* @brief Create a new directory. Will return EEXISTS in case the directory already exists.
|
||||
* @brief Create a new directory. Will return EEXISTS in case the directory
|
||||
* already exists.
|
||||
* @param path
|
||||
* @return 0 if successful, anything else otherwise
|
||||
*/
|
||||
|
@ -138,8 +139,7 @@ const char* get_last_error();
|
|||
* @param path
|
||||
* @return
|
||||
*/
|
||||
[[gnu::nonnull(1)]]
|
||||
[[nodiscard("pointer must be freed")]]
|
||||
[[gnu::nonnull(1)]] [[nodiscard("pointer must be freed")]]
|
||||
const char* get_absolute_path(const char* path);
|
||||
|
||||
#endif //GEMSTONE_FILES_H
|
||||
#endif // GEMSTONE_FILES_H
|
||||
|
|
125
src/lex/util.c
125
src/lex/util.c
|
@ -1,88 +1,89 @@
|
|||
|
||||
#include <lex/util.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <glib.h>
|
||||
#include <lex/util.h>
|
||||
#include <mem/cache.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
// implementation based on:
|
||||
// https://github.com/sunxfancy/flex-bison-examples/blob/master/error-handling/ccalc.c
|
||||
|
||||
char* buffer = NULL;
|
||||
|
||||
static int eof = 0;
|
||||
static int nRow = 0;
|
||||
static int nBuffer = 0;
|
||||
static int lBuffer = 0;
|
||||
static int nTokenStart = 0;
|
||||
static int nTokenLength = 0;
|
||||
static int eof = 0;
|
||||
static int nRow = 0;
|
||||
static int nBuffer = 0;
|
||||
static int lBuffer = 0;
|
||||
static int nTokenStart = 0;
|
||||
static int nTokenLength = 0;
|
||||
static int nTokenNextStart = 0;
|
||||
|
||||
void lex_init(void) {
|
||||
buffer = mem_alloc(MemoryNamespaceStatic, MAX_READ_BUFFER_SIZE);
|
||||
buffer = mem_alloc(MemoryNamespaceStatic, MAX_READ_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
void lex_reset(void) {
|
||||
eof = 0;
|
||||
nRow = 0;
|
||||
nBuffer = 0;
|
||||
lBuffer = 0;
|
||||
nTokenStart = 0;
|
||||
nTokenLength = 0;
|
||||
eof = 0;
|
||||
nRow = 0;
|
||||
nBuffer = 0;
|
||||
lBuffer = 0;
|
||||
nTokenStart = 0;
|
||||
nTokenLength = 0;
|
||||
nTokenNextStart = 0;
|
||||
}
|
||||
|
||||
void beginToken(char *t) {
|
||||
nTokenStart = nTokenNextStart;
|
||||
nTokenLength = (int) strlen(t);
|
||||
nTokenNextStart = nBuffer + 1;
|
||||
void beginToken(char* t) {
|
||||
nTokenStart = nTokenNextStart;
|
||||
nTokenLength = (int) strlen(t);
|
||||
nTokenNextStart = nBuffer + 1;
|
||||
|
||||
yylloc.first_line = nRow;
|
||||
yylloc.first_column = nTokenStart;
|
||||
yylloc.last_line = nRow;
|
||||
yylloc.last_column = nTokenStart + nTokenLength - 1;
|
||||
yylloc.first_line = nRow;
|
||||
yylloc.first_column = nTokenStart;
|
||||
yylloc.last_line = nRow;
|
||||
yylloc.last_column = nTokenStart + nTokenLength - 1;
|
||||
}
|
||||
|
||||
int nextChar(char *dst) {
|
||||
int frc;
|
||||
int nextChar(char* dst) {
|
||||
int frc;
|
||||
|
||||
if (eof)
|
||||
return 0;
|
||||
|
||||
while (nBuffer >= lBuffer) {
|
||||
frc = getNextLine();
|
||||
if (frc != 0) {
|
||||
return 0;
|
||||
if (eof) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
dst[0] = buffer[nBuffer];
|
||||
nBuffer += 1;
|
||||
while (nBuffer >= lBuffer) {
|
||||
frc = getNextLine();
|
||||
if (frc != 0) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return dst[0] != 0;
|
||||
dst[0] = buffer[nBuffer];
|
||||
nBuffer += 1;
|
||||
|
||||
return dst[0] != 0;
|
||||
}
|
||||
|
||||
int getNextLine(void) {
|
||||
char *p;
|
||||
char* p;
|
||||
|
||||
nBuffer = 0;
|
||||
nTokenStart = -1;
|
||||
nTokenNextStart = 1;
|
||||
eof = 0;
|
||||
nBuffer = 0;
|
||||
nTokenStart = -1;
|
||||
nTokenNextStart = 1;
|
||||
eof = 0;
|
||||
|
||||
p = fgets(buffer, MAX_READ_BUFFER_SIZE, yyin);
|
||||
if (p == NULL) {
|
||||
if (ferror(yyin)) {
|
||||
return -1;
|
||||
p = fgets(buffer, MAX_READ_BUFFER_SIZE, yyin);
|
||||
if (p == NULL) {
|
||||
if (ferror(yyin)) {
|
||||
return -1;
|
||||
}
|
||||
eof = 1;
|
||||
return 1;
|
||||
}
|
||||
eof = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
nRow += 1;
|
||||
lBuffer = (int) strlen(buffer);
|
||||
nRow += 1;
|
||||
lBuffer = (int) strlen(buffer);
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct ConstEscSeq {
|
||||
|
@ -91,22 +92,10 @@ struct ConstEscSeq {
|
|||
};
|
||||
|
||||
static struct ConstEscSeq sequences[] = {
|
||||
{
|
||||
"\\n",
|
||||
"\n"
|
||||
},
|
||||
{
|
||||
"\\\\",
|
||||
"\\"
|
||||
},
|
||||
{
|
||||
"\\t",
|
||||
"\t"
|
||||
},
|
||||
{
|
||||
"\\r",
|
||||
"\r"
|
||||
}
|
||||
{"\\n", "\n"},
|
||||
{"\\\\", "\\"},
|
||||
{"\\t", "\t"},
|
||||
{"\\r", "\r"}
|
||||
};
|
||||
|
||||
char* collapse_escape_sequences(char* string) {
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
#ifndef LEX_UTIL_H_
|
||||
#define LEX_UTIL_H_
|
||||
|
||||
#include <yacc/parser.tab.h>
|
||||
#include <stdio.h>
|
||||
#include <yacc/parser.tab.h>
|
||||
|
||||
#define MAX_READ_BUFFER_SIZE 1000
|
||||
|
||||
|
@ -23,13 +23,13 @@ void lex_reset(void);
|
|||
* @param t the text of the token. Must be null terminated
|
||||
*/
|
||||
[[gnu::nonnull(1)]]
|
||||
void beginToken(char *t);
|
||||
void beginToken(char* t);
|
||||
|
||||
/**
|
||||
* @brief Stores the next character into the supplied buffer
|
||||
* @param dst the buffer to store character in
|
||||
*/
|
||||
int nextChar(char *dst);
|
||||
int nextChar(char* dst);
|
||||
|
||||
/**
|
||||
* @brief Reads the next line from yyin into a global buffer
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
// Created by servostar on 18.07.24.
|
||||
//
|
||||
|
||||
#include <link/clang/driver.h>
|
||||
#include <io/files.h>
|
||||
#include <link/clang/driver.h>
|
||||
#include <mem/cache.h>
|
||||
|
||||
bool clang_link(TargetLinkConfig* config) {
|
||||
|
@ -14,7 +14,8 @@ bool clang_link(TargetLinkConfig* config) {
|
|||
|
||||
for (guint i = 0; i < config->object_file_names->len; i++) {
|
||||
g_string_append(commandString, " ");
|
||||
g_string_append(commandString, g_array_index(config->object_file_names, char*, i));
|
||||
g_string_append(commandString,
|
||||
g_array_index(config->object_file_names, char*, i));
|
||||
}
|
||||
|
||||
g_string_append(commandString, " -o ");
|
||||
|
@ -33,9 +34,9 @@ bool clang_link(TargetLinkConfig* config) {
|
|||
|
||||
BinaryDriver* clang_get_driver() {
|
||||
|
||||
BinaryDriver* driver = mem_alloc(MemoryNamespaceLld, sizeof (BinaryDriver));
|
||||
BinaryDriver* driver = mem_alloc(MemoryNamespaceLld, sizeof(BinaryDriver));
|
||||
|
||||
driver->name = "clang";
|
||||
driver->name = "clang";
|
||||
driver->link_func = &clang_link;
|
||||
|
||||
return driver;
|
||||
|
|
|
@ -17,4 +17,4 @@ typedef struct BinaryDriver_t {
|
|||
driver_link link_func;
|
||||
} BinaryDriver;
|
||||
|
||||
#endif //GEMSTONE_DRIVER_H
|
||||
#endif // GEMSTONE_DRIVER_H
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
// Created by servostar on 18.07.24.
|
||||
//
|
||||
|
||||
#include <link/clang/driver.h>
|
||||
#include <io/files.h>
|
||||
#include <link/clang/driver.h>
|
||||
#include <mem/cache.h>
|
||||
|
||||
bool gcc_link(TargetLinkConfig* config) {
|
||||
|
@ -14,7 +14,8 @@ bool gcc_link(TargetLinkConfig* config) {
|
|||
|
||||
for (guint i = 0; i < config->object_file_names->len; i++) {
|
||||
g_string_append(commandString, " ");
|
||||
g_string_append(commandString, g_array_index(config->object_file_names, char*, i));
|
||||
g_string_append(commandString,
|
||||
g_array_index(config->object_file_names, char*, i));
|
||||
}
|
||||
|
||||
g_string_append(commandString, " -o ");
|
||||
|
@ -33,9 +34,9 @@ bool gcc_link(TargetLinkConfig* config) {
|
|||
|
||||
BinaryDriver* gcc_get_driver() {
|
||||
|
||||
BinaryDriver* driver = mem_alloc(MemoryNamespaceLld, sizeof (BinaryDriver));
|
||||
BinaryDriver* driver = mem_alloc(MemoryNamespaceLld, sizeof(BinaryDriver));
|
||||
|
||||
driver->name = "gcc";
|
||||
driver->name = "gcc";
|
||||
driver->link_func = &gcc_link;
|
||||
|
||||
return driver;
|
||||
|
|
|
@ -2,18 +2,14 @@
|
|||
// Created by servostar on 18.07.24.
|
||||
//
|
||||
|
||||
#include <io/files.h>
|
||||
#include <link/clang/driver.h>
|
||||
#include <link/gcc/driver.h>
|
||||
#include <link/lib.h>
|
||||
#include <mem/cache.h>
|
||||
#include <sys/log.h>
|
||||
#include <io/files.h>
|
||||
|
||||
#include <link/clang/driver.h>
|
||||
#include <link/gcc/driver.h>
|
||||
|
||||
static driver_init AVAILABLE_DRIVER[] = {
|
||||
clang_get_driver,
|
||||
gcc_get_driver
|
||||
};
|
||||
static driver_init AVAILABLE_DRIVER[] = {clang_get_driver, gcc_get_driver};
|
||||
|
||||
static GHashTable* binary_driver = NULL;
|
||||
|
||||
|
@ -21,9 +17,11 @@ void link_init() {
|
|||
INFO("initializing binary driver...");
|
||||
|
||||
if (binary_driver == NULL) {
|
||||
binary_driver = mem_new_g_hash_table(MemoryNamespaceLld, g_str_hash, g_str_equal);
|
||||
binary_driver =
|
||||
mem_new_g_hash_table(MemoryNamespaceLld, g_str_hash, g_str_equal);
|
||||
|
||||
for (unsigned long int i = 0; i < sizeof(AVAILABLE_DRIVER)/sizeof(driver_init); i++) {
|
||||
for (unsigned long int i = 0;
|
||||
i < sizeof(AVAILABLE_DRIVER) / sizeof(driver_init); i++) {
|
||||
BinaryDriver* driver = AVAILABLE_DRIVER[i]();
|
||||
|
||||
if (driver == NULL) {
|
||||
|
@ -44,16 +42,18 @@ bool link_run(TargetLinkConfig* config) {
|
|||
if (g_hash_table_contains(binary_driver, config->driver)) {
|
||||
print_message(Info, "Invoking binary driver: %s", config->driver);
|
||||
|
||||
BinaryDriver* driver = g_hash_table_lookup(binary_driver, config->driver);
|
||||
BinaryDriver* driver =
|
||||
g_hash_table_lookup(binary_driver, config->driver);
|
||||
|
||||
if (!driver->link_func(config)) {
|
||||
if (!driver->link_func(config)) {
|
||||
print_message(Error, "Driver %s failed", config->driver);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
} else {
|
||||
print_message(Error, "Binary driver not available: `%s`", config->driver);
|
||||
print_message(Error, "Binary driver not available: `%s`",
|
||||
config->driver);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,4 +15,4 @@ bool link_run(TargetLinkConfig*);
|
|||
|
||||
void link_print_available_driver();
|
||||
|
||||
#endif //GEMSTONE_LIB_H
|
||||
#endif // GEMSTONE_LIB_H
|
||||
|
|
|
@ -6,29 +6,29 @@
|
|||
#include <llvm-c/TargetMachine.h>
|
||||
#include <llvm/backend.h>
|
||||
#include <llvm/parser.h>
|
||||
#include <sys/log.h>
|
||||
#include <mem/cache.h>
|
||||
#include <sys/log.h>
|
||||
|
||||
Target create_native_target() {
|
||||
DEBUG("creating native target...");
|
||||
Target target;
|
||||
|
||||
target.name.str = "tmp";
|
||||
target.name.str = "tmp";
|
||||
target.name.allocation = NONE;
|
||||
|
||||
target.triple.str = LLVMGetDefaultTargetTriple();
|
||||
target.triple.str = LLVMGetDefaultTargetTriple();
|
||||
target.triple.allocation = LLVM;
|
||||
assert(target.triple.str != NULL);
|
||||
|
||||
target.cpu.str = LLVMGetHostCPUName();
|
||||
target.cpu.str = LLVMGetHostCPUName();
|
||||
target.cpu.allocation = LLVM;
|
||||
assert(target.cpu.str != NULL);
|
||||
|
||||
target.features.str = LLVMGetHostCPUFeatures();
|
||||
target.features.str = LLVMGetHostCPUFeatures();
|
||||
target.features.allocation = LLVM;
|
||||
assert(target.features.str != NULL);
|
||||
|
||||
target.opt = LLVMCodeGenLevelNone;
|
||||
target.opt = LLVMCodeGenLevelNone;
|
||||
target.reloc = LLVMRelocDefault;
|
||||
target.model = LLVMCodeModelDefault;
|
||||
|
||||
|
@ -55,7 +55,7 @@ static char* create_target_output_name(const TargetConfig* config) {
|
|||
prefix = "lib";
|
||||
}
|
||||
|
||||
char* name = g_strjoin("", prefix, config->name, NULL);
|
||||
char* name = g_strjoin("", prefix, config->name, NULL);
|
||||
char* cached_name = mem_strdup(MemoryNamespaceLlvm, name);
|
||||
g_free(name);
|
||||
|
||||
|
@ -67,13 +67,13 @@ Target create_target_from_config(const TargetConfig* config) {
|
|||
|
||||
Target target = create_native_target();
|
||||
|
||||
target.name.str = create_target_output_name(config);
|
||||
target.name.allocation = NONE; // freed later by compiler
|
||||
target.name.str = create_target_output_name(config);
|
||||
target.name.allocation = NONE; // freed later by compiler
|
||||
|
||||
target.opt = llvm_opt_from_int(config->optimization_level);
|
||||
|
||||
INFO("Configured target: %s/%d: (%s) on %s { %s }", target.name.str, target.opt,
|
||||
target.triple.str, target.cpu.str, target.features.str);
|
||||
INFO("Configured target: %s/%d: (%s) on %s { %s }", target.name.str,
|
||||
target.opt, target.triple.str, target.cpu.str, target.features.str);
|
||||
|
||||
return target;
|
||||
}
|
||||
|
@ -116,8 +116,8 @@ static BackendError llvm_backend_codegen_deinit(void) {
|
|||
|
||||
void llvm_backend_init() {
|
||||
BackendError err =
|
||||
set_backend(&llvm_backend_codegen_init, &llvm_backend_codegen_deinit,
|
||||
&llvm_backend_codegen, "LLVM");
|
||||
set_backend(&llvm_backend_codegen_init, &llvm_backend_codegen_deinit,
|
||||
&llvm_backend_codegen, "LLVM");
|
||||
|
||||
if (err.kind != Success) {
|
||||
PANIC("unable to init llvm backend: %ld", err);
|
||||
|
|
|
@ -4,11 +4,7 @@
|
|||
|
||||
#include <llvm-c/TargetMachine.h>
|
||||
|
||||
enum StringAllocation_t {
|
||||
LLVM,
|
||||
LIBC,
|
||||
NONE
|
||||
};
|
||||
enum StringAllocation_t { LLVM, LIBC, NONE };
|
||||
|
||||
typedef struct String_t {
|
||||
enum StringAllocation_t allocation;
|
||||
|
|
|
@ -2,23 +2,26 @@
|
|||
// Created by servostar on 6/4/24.
|
||||
//
|
||||
|
||||
#include <link/lib.h>
|
||||
#include <llvm/link/lld.h>
|
||||
#include <sys/log.h>
|
||||
#include <mem/cache.h>
|
||||
#include <sys/col.h>
|
||||
#include <link/lib.h>
|
||||
#include <sys/log.h>
|
||||
|
||||
const char* get_absolute_link_path(const TargetConfig* config, const char* link_target_name) {
|
||||
const char* get_absolute_link_path(const TargetConfig* config,
|
||||
const char* link_target_name) {
|
||||
INFO("resolving absolute path for link target: %s", link_target_name);
|
||||
|
||||
for (guint i = 0; i < config->link_search_paths->len; i++) {
|
||||
const char* link_directory_path = g_array_index(config->link_search_paths, char*, i);
|
||||
const char* link_directory_path =
|
||||
g_array_index(config->link_search_paths, char*, i);
|
||||
|
||||
INFO("searching at: %s", link_directory_path);
|
||||
|
||||
char* path = g_build_filename(link_directory_path, link_target_name, NULL);
|
||||
char* cwd = g_get_current_dir();
|
||||
char* canonical = g_canonicalize_filename(path, cwd);
|
||||
char* path =
|
||||
g_build_filename(link_directory_path, link_target_name, NULL);
|
||||
char* cwd = g_get_current_dir();
|
||||
char* canonical = g_canonicalize_filename(path, cwd);
|
||||
char* cached_canonical = mem_strdup(MemoryNamespaceLld, canonical);
|
||||
|
||||
const gboolean exists = g_file_test(canonical, G_FILE_TEST_EXISTS);
|
||||
|
@ -38,21 +41,28 @@ const char* get_absolute_link_path(const TargetConfig* config, const char* link_
|
|||
return NULL;
|
||||
}
|
||||
|
||||
TargetLinkConfig* lld_create_link_config(__attribute__((unused)) const Target* target, const TargetConfig* target_config, const Module* module) {
|
||||
TargetLinkConfig* lld_create_link_config(__attribute__((unused))
|
||||
const Target* target,
|
||||
const TargetConfig* target_config,
|
||||
const Module* module) {
|
||||
DEBUG("generating link configuration");
|
||||
|
||||
TargetLinkConfig* config = mem_alloc(MemoryNamespaceLld, sizeof(TargetLinkConfig));
|
||||
TargetLinkConfig* config =
|
||||
mem_alloc(MemoryNamespaceLld, sizeof(TargetLinkConfig));
|
||||
|
||||
config->fatal_warnings = target_config->lld_fatal_warnings;
|
||||
config->object_file_names = mem_new_g_array(MemoryNamespaceLld, sizeof(char*));
|
||||
config->object_file_names =
|
||||
mem_new_g_array(MemoryNamespaceLld, sizeof(char*));
|
||||
config->colorize = stdout_supports_ansi_esc();
|
||||
config->driver = target_config->driver;
|
||||
config->driver = target_config->driver;
|
||||
|
||||
// append build object file
|
||||
char* basename = g_strjoin(".", target_config->name, "o", NULL);
|
||||
char* filename = g_build_filename(target_config->archive_directory, basename, NULL);
|
||||
char* filename =
|
||||
g_build_filename(target_config->archive_directory, basename, NULL);
|
||||
g_free(basename);
|
||||
const char* target_object = get_absolute_link_path(target_config, (const char*) filename);
|
||||
const char* target_object =
|
||||
get_absolute_link_path(target_config, (const char*) filename);
|
||||
if (target_object == NULL) {
|
||||
ERROR("failed to resolve path to target object: %s", filename);
|
||||
g_free(filename);
|
||||
|
@ -65,7 +75,8 @@ TargetLinkConfig* lld_create_link_config(__attribute__((unused)) const Target* t
|
|||
{
|
||||
// output file after linking
|
||||
basename = g_strjoin(".", target_config->name, "out", NULL);
|
||||
filename = g_build_filename(target_config->output_directory, basename, NULL);
|
||||
filename =
|
||||
g_build_filename(target_config->output_directory, basename, NULL);
|
||||
|
||||
config->output_file = mem_strdup(MemoryNamespaceLld, filename);
|
||||
|
||||
|
@ -83,10 +94,14 @@ TargetLinkConfig* lld_create_link_config(__attribute__((unused)) const Target* t
|
|||
|
||||
const char* library = g_strjoin("", "libgsc", dependency, ".a", NULL);
|
||||
|
||||
const char* dependency_object = get_absolute_link_path(target_config, library);
|
||||
const char* dependency_object =
|
||||
get_absolute_link_path(target_config, library);
|
||||
if (dependency_object == NULL) {
|
||||
ERROR("failed to resolve path to dependency object: %s", library);
|
||||
print_message(Warning, "failed to resolve path to dependency object: %s", dependency); lld_delete_link_config(config);
|
||||
print_message(Warning,
|
||||
"failed to resolve path to dependency object: %s",
|
||||
dependency);
|
||||
lld_delete_link_config(config);
|
||||
lld_delete_link_config(config);
|
||||
g_free((void*) library);
|
||||
return NULL;
|
||||
|
|
|
@ -8,7 +8,10 @@
|
|||
#include <codegen/backend.h>
|
||||
#include <llvm/backend.h>
|
||||
|
||||
TargetLinkConfig* lld_create_link_config(__attribute__((unused)) const Target * target, const TargetConfig* target_config, const Module* module);
|
||||
TargetLinkConfig* lld_create_link_config(__attribute__((unused))
|
||||
const Target* target,
|
||||
const TargetConfig* target_config,
|
||||
const Module* module);
|
||||
|
||||
BackendError lld_link_target(TargetLinkConfig* config);
|
||||
|
||||
|
|
|
@ -4,16 +4,16 @@
|
|||
|
||||
#include <llvm/llvm-ir/expr.h>
|
||||
#include <llvm/llvm-ir/types.h>
|
||||
#include <sys/log.h>
|
||||
#include <mem/cache.h>
|
||||
#include <sys/log.h>
|
||||
|
||||
BackendError impl_bitwise_operation(LLVMBackendCompileUnit *unit,
|
||||
LLVMLocalScope *scope,
|
||||
BackendError impl_bitwise_operation(LLVMBackendCompileUnit* unit,
|
||||
LLVMLocalScope* scope,
|
||||
LLVMBuilderRef builder,
|
||||
Operation *operation,
|
||||
LLVMValueRef *llvm_result) {
|
||||
Expression *rhs = NULL;
|
||||
Expression *lhs = NULL;
|
||||
Operation* operation,
|
||||
LLVMValueRef* llvm_result) {
|
||||
Expression* rhs = NULL;
|
||||
Expression* lhs = NULL;
|
||||
LLVMValueRef llvm_rhs = NULL;
|
||||
LLVMValueRef llvm_lhs = NULL;
|
||||
|
||||
|
@ -32,13 +32,16 @@ BackendError impl_bitwise_operation(LLVMBackendCompileUnit *unit,
|
|||
|
||||
switch (operation->impl.bitwise) {
|
||||
case BitwiseAnd:
|
||||
*llvm_result = LLVMBuildAnd(builder, llvm_lhs, llvm_rhs, "bitwise and");
|
||||
*llvm_result =
|
||||
LLVMBuildAnd(builder, llvm_lhs, llvm_rhs, "bitwise and");
|
||||
break;
|
||||
case BitwiseOr:
|
||||
*llvm_result = LLVMBuildOr(builder, llvm_lhs, llvm_rhs, "bitwise or");
|
||||
*llvm_result =
|
||||
LLVMBuildOr(builder, llvm_lhs, llvm_rhs, "bitwise or");
|
||||
break;
|
||||
case BitwiseXor:
|
||||
*llvm_result = LLVMBuildXor(builder, llvm_lhs, llvm_rhs, "bitwise xor");
|
||||
*llvm_result =
|
||||
LLVMBuildXor(builder, llvm_lhs, llvm_rhs, "bitwise xor");
|
||||
break;
|
||||
case BitwiseNot:
|
||||
*llvm_result = LLVMBuildNot(builder, llvm_rhs, "bitwise not");
|
||||
|
@ -50,15 +53,15 @@ BackendError impl_bitwise_operation(LLVMBackendCompileUnit *unit,
|
|||
|
||||
/**
|
||||
* @brief Convert any integral type (integer) to a boolean value.
|
||||
* A boolean value hereby meaning an integer of the same type as the input
|
||||
* value but with the value of either 0 or one.
|
||||
* A boolean value hereby meaning an integer of the same type as the
|
||||
* input value but with the value of either 0 or one.
|
||||
* @param builder
|
||||
* @param integral
|
||||
* @return
|
||||
*/
|
||||
[[maybe_unused]]
|
||||
static LLVMValueRef convert_integral_to_boolean(
|
||||
LLVMBuilderRef builder, LLVMValueRef integral) {
|
||||
[[maybe_unused]]
|
||||
static LLVMValueRef convert_integral_to_boolean(LLVMBuilderRef builder,
|
||||
LLVMValueRef integral) {
|
||||
// type of input
|
||||
LLVMTypeRef valueType = LLVMTypeOf(integral);
|
||||
// zero value of same type as integral
|
||||
|
@ -67,13 +70,13 @@ static LLVMValueRef convert_integral_to_boolean(
|
|||
return LLVMBuildICmp(builder, LLVMIntNE, zero, integral, "to boolean");
|
||||
}
|
||||
|
||||
BackendError impl_logical_operation(LLVMBackendCompileUnit *unit,
|
||||
LLVMLocalScope *scope,
|
||||
BackendError impl_logical_operation(LLVMBackendCompileUnit* unit,
|
||||
LLVMLocalScope* scope,
|
||||
LLVMBuilderRef builder,
|
||||
Operation *operation,
|
||||
LLVMValueRef *llvm_result) {
|
||||
Expression *rhs = NULL;
|
||||
Expression *lhs = NULL;
|
||||
Operation* operation,
|
||||
LLVMValueRef* llvm_result) {
|
||||
Expression* rhs = NULL;
|
||||
Expression* lhs = NULL;
|
||||
LLVMValueRef llvm_rhs = NULL;
|
||||
LLVMValueRef llvm_lhs = NULL;
|
||||
|
||||
|
@ -92,13 +95,16 @@ BackendError impl_logical_operation(LLVMBackendCompileUnit *unit,
|
|||
|
||||
switch (operation->impl.logical) {
|
||||
case LogicalAnd:
|
||||
*llvm_result = LLVMBuildAnd(builder, llvm_lhs, llvm_rhs, "logical and");
|
||||
*llvm_result =
|
||||
LLVMBuildAnd(builder, llvm_lhs, llvm_rhs, "logical and");
|
||||
break;
|
||||
case LogicalOr:
|
||||
*llvm_result = LLVMBuildOr(builder, llvm_lhs, llvm_rhs, "logical or");
|
||||
*llvm_result =
|
||||
LLVMBuildOr(builder, llvm_lhs, llvm_rhs, "logical or");
|
||||
break;
|
||||
case LogicalXor:
|
||||
*llvm_result = LLVMBuildXor(builder, llvm_lhs, llvm_rhs, "logical xor");
|
||||
*llvm_result =
|
||||
LLVMBuildXor(builder, llvm_lhs, llvm_rhs, "logical xor");
|
||||
break;
|
||||
case LogicalNot:
|
||||
*llvm_result = LLVMBuildNot(builder, llvm_rhs, "logical not");
|
||||
|
@ -108,7 +114,7 @@ BackendError impl_logical_operation(LLVMBackendCompileUnit *unit,
|
|||
return SUCCESS;
|
||||
}
|
||||
|
||||
static LLVMBool is_floating_point(Type *value) {
|
||||
static LLVMBool is_floating_point(Type* value) {
|
||||
if (value->kind == TypeKindPrimitive) {
|
||||
return value->impl.primitive == Float;
|
||||
}
|
||||
|
@ -120,7 +126,7 @@ static LLVMBool is_floating_point(Type *value) {
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
static LLVMBool is_integral(Type *value) {
|
||||
static LLVMBool is_integral(Type* value) {
|
||||
if (value->kind == TypeKindPrimitive) {
|
||||
return value->impl.primitive == Int;
|
||||
}
|
||||
|
@ -132,13 +138,13 @@ static LLVMBool is_integral(Type *value) {
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
BackendError impl_relational_operation(LLVMBackendCompileUnit *unit,
|
||||
LLVMLocalScope *scope,
|
||||
BackendError impl_relational_operation(LLVMBackendCompileUnit* unit,
|
||||
LLVMLocalScope* scope,
|
||||
LLVMBuilderRef builder,
|
||||
Operation *operation,
|
||||
LLVMValueRef *llvm_result) {
|
||||
Expression *rhs = NULL;
|
||||
Expression *lhs = NULL;
|
||||
Operation* operation,
|
||||
LLVMValueRef* llvm_result) {
|
||||
Expression* rhs = NULL;
|
||||
Expression* lhs = NULL;
|
||||
LLVMValueRef llvm_rhs = NULL;
|
||||
LLVMValueRef llvm_lhs = NULL;
|
||||
|
||||
|
@ -151,39 +157,41 @@ BackendError impl_relational_operation(LLVMBackendCompileUnit *unit,
|
|||
|
||||
if (is_integral(rhs->result)) {
|
||||
// integral type
|
||||
LLVMIntPredicate operator = 0;
|
||||
LLVMIntPredicate operator= 0;
|
||||
|
||||
switch (operation->impl.relational) {
|
||||
case Equal:
|
||||
operator = LLVMIntEQ;
|
||||
operator= LLVMIntEQ;
|
||||
break;
|
||||
case Greater:
|
||||
operator = LLVMIntSGT;
|
||||
operator= LLVMIntSGT;
|
||||
break;
|
||||
case Less:
|
||||
operator = LLVMIntSLT;
|
||||
operator= LLVMIntSLT;
|
||||
break;
|
||||
}
|
||||
|
||||
*llvm_result = LLVMBuildICmp(builder, operator, llvm_lhs, llvm_rhs, "integral comparison");
|
||||
*llvm_result = LLVMBuildICmp(builder, operator, llvm_lhs, llvm_rhs,
|
||||
"integral comparison");
|
||||
|
||||
} else if (is_floating_point(rhs->result)) {
|
||||
// integral type
|
||||
LLVMRealPredicate operator = 0;
|
||||
LLVMRealPredicate operator= 0;
|
||||
|
||||
switch (operation->impl.relational) {
|
||||
case Equal:
|
||||
operator = LLVMRealOEQ;
|
||||
operator= LLVMRealOEQ;
|
||||
break;
|
||||
case Greater:
|
||||
operator = LLVMRealOGT;
|
||||
operator= LLVMRealOGT;
|
||||
break;
|
||||
case Less:
|
||||
operator = LLVMRealOLT;
|
||||
operator= LLVMRealOLT;
|
||||
break;
|
||||
}
|
||||
|
||||
*llvm_result = LLVMBuildFCmp(builder, operator, llvm_lhs, llvm_rhs, "floating point comparison");
|
||||
*llvm_result = LLVMBuildFCmp(builder, operator, llvm_lhs, llvm_rhs,
|
||||
"floating point comparison");
|
||||
} else {
|
||||
PANIC("invalid type for relational operator");
|
||||
}
|
||||
|
@ -193,13 +201,13 @@ BackendError impl_relational_operation(LLVMBackendCompileUnit *unit,
|
|||
return SUCCESS;
|
||||
}
|
||||
|
||||
BackendError impl_arithmetic_operation(LLVMBackendCompileUnit *unit,
|
||||
LLVMLocalScope *scope,
|
||||
BackendError impl_arithmetic_operation(LLVMBackendCompileUnit* unit,
|
||||
LLVMLocalScope* scope,
|
||||
LLVMBuilderRef builder,
|
||||
Operation *operation,
|
||||
LLVMValueRef *llvm_result) {
|
||||
Expression *rhs = NULL;
|
||||
Expression *lhs = NULL;
|
||||
Operation* operation,
|
||||
LLVMValueRef* llvm_result) {
|
||||
Expression* rhs = NULL;
|
||||
Expression* lhs = NULL;
|
||||
LLVMValueRef llvm_rhs = NULL;
|
||||
LLVMValueRef llvm_lhs = NULL;
|
||||
|
||||
|
@ -220,19 +228,24 @@ BackendError impl_arithmetic_operation(LLVMBackendCompileUnit *unit,
|
|||
|
||||
switch (operation->impl.arithmetic) {
|
||||
case Add:
|
||||
*llvm_result = LLVMBuildNSWAdd(builder, llvm_lhs, llvm_rhs, "signed integer addition");
|
||||
*llvm_result = LLVMBuildNSWAdd(builder, llvm_lhs, llvm_rhs,
|
||||
"signed integer addition");
|
||||
break;
|
||||
case Sub:
|
||||
*llvm_result = LLVMBuildNSWSub(builder, llvm_lhs, llvm_rhs, "signed integer subtraction");
|
||||
*llvm_result = LLVMBuildNSWSub(builder, llvm_lhs, llvm_rhs,
|
||||
"signed integer subtraction");
|
||||
break;
|
||||
case Mul:
|
||||
*llvm_result = LLVMBuildNSWMul(builder, llvm_lhs, llvm_rhs, "signed integer multiply");
|
||||
*llvm_result = LLVMBuildNSWMul(builder, llvm_lhs, llvm_rhs,
|
||||
"signed integer multiply");
|
||||
break;
|
||||
case Div:
|
||||
*llvm_result = LLVMBuildSDiv(builder, llvm_lhs, llvm_rhs, "signed integer divide");
|
||||
*llvm_result = LLVMBuildSDiv(builder, llvm_lhs, llvm_rhs,
|
||||
"signed integer divide");
|
||||
break;
|
||||
case Negate:
|
||||
*llvm_result = LLVMBuildNeg(builder, llvm_rhs, "signed integer negate");
|
||||
*llvm_result =
|
||||
LLVMBuildNeg(builder, llvm_rhs, "signed integer negate");
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -240,19 +253,24 @@ BackendError impl_arithmetic_operation(LLVMBackendCompileUnit *unit,
|
|||
|
||||
switch (operation->impl.arithmetic) {
|
||||
case Add:
|
||||
*llvm_result = LLVMBuildFAdd(builder, llvm_lhs, llvm_rhs, "floating point addition");
|
||||
*llvm_result = LLVMBuildFAdd(builder, llvm_lhs, llvm_rhs,
|
||||
"floating point addition");
|
||||
break;
|
||||
case Sub:
|
||||
*llvm_result = LLVMBuildFSub(builder, llvm_lhs, llvm_rhs, "floating point subtraction");
|
||||
*llvm_result = LLVMBuildFSub(builder, llvm_lhs, llvm_rhs,
|
||||
"floating point subtraction");
|
||||
break;
|
||||
case Mul:
|
||||
*llvm_result = LLVMBuildFMul(builder, llvm_lhs, llvm_rhs, "floating point multiply");
|
||||
*llvm_result = LLVMBuildFMul(builder, llvm_lhs, llvm_rhs,
|
||||
"floating point multiply");
|
||||
break;
|
||||
case Div:
|
||||
*llvm_result = LLVMBuildFDiv(builder, llvm_lhs, llvm_rhs, "floating point divide");
|
||||
*llvm_result = LLVMBuildFDiv(builder, llvm_lhs, llvm_rhs,
|
||||
"floating point divide");
|
||||
break;
|
||||
case Negate:
|
||||
*llvm_result = LLVMBuildFNeg(builder, llvm_rhs, "floating point negate");
|
||||
*llvm_result =
|
||||
LLVMBuildFNeg(builder, llvm_rhs, "floating point negate");
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -263,9 +281,9 @@ BackendError impl_arithmetic_operation(LLVMBackendCompileUnit *unit,
|
|||
return SUCCESS;
|
||||
}
|
||||
|
||||
BackendError impl_operation(LLVMBackendCompileUnit *unit, LLVMLocalScope *scope,
|
||||
LLVMBuilderRef builder, Operation *operation,
|
||||
LLVMValueRef *llvm_result) {
|
||||
BackendError impl_operation(LLVMBackendCompileUnit* unit, LLVMLocalScope* scope,
|
||||
LLVMBuilderRef builder, Operation* operation,
|
||||
LLVMValueRef* llvm_result) {
|
||||
BackendError err;
|
||||
|
||||
switch (operation->kind) {
|
||||
|
@ -292,9 +310,9 @@ BackendError impl_operation(LLVMBackendCompileUnit *unit, LLVMLocalScope *scope,
|
|||
return err;
|
||||
}
|
||||
|
||||
BackendError impl_transmute(LLVMBackendCompileUnit *unit, LLVMLocalScope *scope,
|
||||
LLVMBuilderRef builder, Transmute *transmute,
|
||||
LLVMValueRef *llvm_result) {
|
||||
BackendError impl_transmute(LLVMBackendCompileUnit* unit, LLVMLocalScope* scope,
|
||||
LLVMBuilderRef builder, Transmute* transmute,
|
||||
LLVMValueRef* llvm_result) {
|
||||
|
||||
LLVMValueRef operand = NULL;
|
||||
impl_expr(unit, scope, builder, transmute->operand, FALSE, 0, &operand);
|
||||
|
@ -305,13 +323,13 @@ BackendError impl_transmute(LLVMBackendCompileUnit *unit, LLVMLocalScope *scope,
|
|||
// if target type is valid
|
||||
if (err.kind == Success) {
|
||||
*llvm_result =
|
||||
LLVMBuildBitCast(builder, operand, target_type, "transmute");
|
||||
LLVMBuildBitCast(builder, operand, target_type, "transmute");
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static LLVMBool is_type_signed(const Type *type) {
|
||||
static LLVMBool is_type_signed(const Type* type) {
|
||||
switch (type->kind) {
|
||||
case TypeKindPrimitive:
|
||||
return 1;
|
||||
|
@ -322,10 +340,9 @@ static LLVMBool is_type_signed(const Type *type) {
|
|||
}
|
||||
}
|
||||
|
||||
BackendError impl_typecast(LLVMBackendCompileUnit *unit, LLVMLocalScope *scope,
|
||||
LLVMBuilderRef builder, TypeCast *typecast,
|
||||
bool reference,
|
||||
LLVMValueRef *llvm_result) {
|
||||
BackendError impl_typecast(LLVMBackendCompileUnit* unit, LLVMLocalScope* scope,
|
||||
LLVMBuilderRef builder, TypeCast* typecast,
|
||||
bool reference, LLVMValueRef* llvm_result) {
|
||||
LLVMValueRef operand = NULL;
|
||||
impl_expr(unit, scope, builder, typecast->operand, reference, 0, &operand);
|
||||
|
||||
|
@ -340,17 +357,17 @@ BackendError impl_typecast(LLVMBackendCompileUnit *unit, LLVMLocalScope *scope,
|
|||
LLVMBool dst_signed = is_type_signed(typecast->targetType);
|
||||
LLVMBool src_signed = is_type_signed(typecast->operand->result);
|
||||
const LLVMOpcode opcode =
|
||||
LLVMGetCastOpcode(operand, src_signed, target_type, dst_signed);
|
||||
LLVMGetCastOpcode(operand, src_signed, target_type, dst_signed);
|
||||
*llvm_result =
|
||||
LLVMBuildCast(builder, opcode, operand, target_type, "expr.typecast");
|
||||
LLVMBuildCast(builder, opcode, operand, target_type, "expr.typecast");
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
BackendError impl_variable_load(LLVMBackendCompileUnit *unit, LLVMLocalScope *scope,
|
||||
LLVMBuilderRef builder, Variable *variable,
|
||||
LLVMBool reference,
|
||||
LLVMValueRef *llvm_result) {
|
||||
BackendError impl_variable_load(LLVMBackendCompileUnit* unit,
|
||||
LLVMLocalScope* scope, LLVMBuilderRef builder,
|
||||
Variable* variable, LLVMBool reference,
|
||||
LLVMValueRef* llvm_result) {
|
||||
|
||||
LLVMValueRef llvm_variable = get_variable(scope, variable->name);
|
||||
|
||||
|
@ -363,7 +380,8 @@ BackendError impl_variable_load(LLVMBackendCompileUnit *unit, LLVMLocalScope *sc
|
|||
}
|
||||
|
||||
if (llvm_variable == NULL) {
|
||||
return new_backend_impl_error(Implementation, NULL, "Variable not found");
|
||||
return new_backend_impl_error(Implementation, NULL,
|
||||
"Variable not found");
|
||||
}
|
||||
|
||||
if (reference) {
|
||||
|
@ -379,7 +397,8 @@ BackendError impl_variable_load(LLVMBackendCompileUnit *unit, LLVMLocalScope *sc
|
|||
get_type_impl(unit, scope->func_scope->global_scope, type, &llvm_type);
|
||||
|
||||
if (LLVMGetTypeKind(LLVMTypeOf(llvm_variable)) == LLVMPointerTypeKind) {
|
||||
*llvm_result = LLVMBuildLoad2(builder, llvm_type, llvm_variable, "");
|
||||
*llvm_result =
|
||||
LLVMBuildLoad2(builder, llvm_type, llvm_variable, "");
|
||||
} else {
|
||||
*llvm_result = llvm_variable;
|
||||
}
|
||||
|
@ -388,14 +407,15 @@ BackendError impl_variable_load(LLVMBackendCompileUnit *unit, LLVMLocalScope *sc
|
|||
return SUCCESS;
|
||||
}
|
||||
|
||||
BackendError impl_parameter_load(LLVMBackendCompileUnit *unit, LLVMLocalScope *scope,
|
||||
LLVMBuilderRef builder, Parameter *parameter,
|
||||
LLVMBool reference,
|
||||
LLVMValueRef *llvm_result) {
|
||||
BackendError impl_parameter_load(LLVMBackendCompileUnit* unit,
|
||||
LLVMLocalScope* scope, LLVMBuilderRef builder,
|
||||
Parameter* parameter, LLVMBool reference,
|
||||
LLVMValueRef* llvm_result) {
|
||||
|
||||
LLVMValueRef llvm_variable = NULL;
|
||||
if (g_hash_table_contains(scope->func_scope->params, parameter->name)) {
|
||||
llvm_variable = g_hash_table_lookup(scope->func_scope->params, parameter->name);
|
||||
llvm_variable =
|
||||
g_hash_table_lookup(scope->func_scope->params, parameter->name);
|
||||
}
|
||||
|
||||
Type* type;
|
||||
|
@ -410,7 +430,8 @@ BackendError impl_parameter_load(LLVMBackendCompileUnit *unit, LLVMLocalScope *s
|
|||
type = decl.type;
|
||||
|
||||
if (llvm_variable == NULL) {
|
||||
return new_backend_impl_error(Implementation, NULL, "Variable not found");
|
||||
return new_backend_impl_error(Implementation, NULL,
|
||||
"Variable not found");
|
||||
}
|
||||
|
||||
if (decl.qualifier == In || reference) {
|
||||
|
@ -422,7 +443,8 @@ BackendError impl_parameter_load(LLVMBackendCompileUnit *unit, LLVMLocalScope *s
|
|||
get_type_impl(unit, scope->func_scope->global_scope, type, &llvm_type);
|
||||
|
||||
if (LLVMGetTypeKind(LLVMTypeOf(llvm_variable)) == LLVMPointerTypeKind) {
|
||||
*llvm_result = LLVMBuildLoad2(builder, llvm_type, llvm_variable, "");
|
||||
*llvm_result =
|
||||
LLVMBuildLoad2(builder, llvm_type, llvm_variable, "");
|
||||
} else {
|
||||
*llvm_result = llvm_variable;
|
||||
}
|
||||
|
@ -431,11 +453,12 @@ BackendError impl_parameter_load(LLVMBackendCompileUnit *unit, LLVMLocalScope *s
|
|||
return SUCCESS;
|
||||
}
|
||||
|
||||
BackendError impl_address_of(LLVMBackendCompileUnit *unit, LLVMLocalScope *scope,
|
||||
LLVMBuilderRef builder, AddressOf* addressOf,
|
||||
LLVMValueRef *llvm_result) {
|
||||
BackendError impl_address_of(LLVMBackendCompileUnit* unit,
|
||||
LLVMLocalScope* scope, LLVMBuilderRef builder,
|
||||
AddressOf* addressOf, LLVMValueRef* llvm_result) {
|
||||
|
||||
BackendError err = impl_expr(unit, scope, builder, addressOf->variable, TRUE, 0, llvm_result);
|
||||
BackendError err = impl_expr(unit, scope, builder, addressOf->variable,
|
||||
TRUE, 0, llvm_result);
|
||||
|
||||
if (err.kind != Success) {
|
||||
return err;
|
||||
|
@ -444,45 +467,49 @@ BackendError impl_address_of(LLVMBackendCompileUnit *unit, LLVMLocalScope *scope
|
|||
return SUCCESS;
|
||||
}
|
||||
|
||||
BackendError impl_deref(LLVMBackendCompileUnit *unit, LLVMLocalScope *scope,
|
||||
LLVMBuilderRef builder, Dereference* dereference,
|
||||
bool reference,
|
||||
uint32_t deref_depth,
|
||||
LLVMValueRef *llvm_result) {
|
||||
BackendError impl_deref(LLVMBackendCompileUnit* unit, LLVMLocalScope* scope,
|
||||
LLVMBuilderRef builder, Dereference* dereference,
|
||||
bool reference, uint32_t deref_depth,
|
||||
LLVMValueRef* llvm_result) {
|
||||
BackendError err;
|
||||
|
||||
LLVMValueRef llvm_pointer = NULL;
|
||||
err = impl_expr(unit, scope, builder, dereference->variable, false, deref_depth + 1, &llvm_pointer);
|
||||
err = impl_expr(unit, scope, builder, dereference->variable, false,
|
||||
deref_depth + 1, &llvm_pointer);
|
||||
if (err.kind != Success) {
|
||||
return err;
|
||||
}
|
||||
|
||||
LLVMTypeRef llvm_deref_type = NULL;
|
||||
err = get_type_impl(unit, scope->func_scope->global_scope, dereference->variable->result->impl.reference, &llvm_deref_type);
|
||||
err = get_type_impl(unit, scope->func_scope->global_scope,
|
||||
dereference->variable->result->impl.reference,
|
||||
&llvm_deref_type);
|
||||
if (err.kind != Success) {
|
||||
return err;
|
||||
}
|
||||
|
||||
LLVMValueRef* index = mem_alloc(MemoryNamespaceLlvm, sizeof(LLVMValueRef));
|
||||
err = impl_expr(unit, scope, builder, dereference->index, FALSE, deref_depth + 1, index);
|
||||
err = impl_expr(unit, scope, builder, dereference->index, FALSE,
|
||||
deref_depth + 1, index);
|
||||
if (err.kind != Success) {
|
||||
return err;
|
||||
}
|
||||
|
||||
*llvm_result = LLVMBuildGEP2(builder, llvm_deref_type, llvm_pointer, index, 1, "expr.deref.gep2");
|
||||
*llvm_result = LLVMBuildGEP2(builder, llvm_deref_type, llvm_pointer, index,
|
||||
1, "expr.deref.gep2");
|
||||
|
||||
if (!reference || deref_depth > 0) {
|
||||
*llvm_result = LLVMBuildLoad2(builder, llvm_deref_type, *llvm_result, "expr.deref.load");
|
||||
*llvm_result = LLVMBuildLoad2(builder, llvm_deref_type, *llvm_result,
|
||||
"expr.deref.load");
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
BackendError impl_expr(LLVMBackendCompileUnit *unit, LLVMLocalScope *scope,
|
||||
LLVMBuilderRef builder, Expression *expr,
|
||||
LLVMBool reference,
|
||||
uint32_t deref_depth,
|
||||
LLVMValueRef *llvm_result) {
|
||||
BackendError impl_expr(LLVMBackendCompileUnit* unit, LLVMLocalScope* scope,
|
||||
LLVMBuilderRef builder, Expression* expr,
|
||||
LLVMBool reference, uint32_t deref_depth,
|
||||
LLVMValueRef* llvm_result) {
|
||||
DEBUG("implementing expression: %ld", expr->kind);
|
||||
BackendError err = SUCCESS;
|
||||
|
||||
|
@ -497,8 +524,7 @@ BackendError impl_expr(LLVMBackendCompileUnit *unit, LLVMLocalScope *scope,
|
|||
break;
|
||||
case ExpressionKindTypeCast:
|
||||
err = impl_typecast(unit, scope, builder, &expr->impl.typecast,
|
||||
reference,
|
||||
llvm_result);
|
||||
reference, llvm_result);
|
||||
break;
|
||||
case ExpressionKindOperation:
|
||||
err = impl_operation(unit, scope, builder, &expr->impl.operation,
|
||||
|
@ -506,13 +532,12 @@ BackendError impl_expr(LLVMBackendCompileUnit *unit, LLVMLocalScope *scope,
|
|||
break;
|
||||
case ExpressionKindVariable:
|
||||
err = impl_variable_load(unit, scope, builder, expr->impl.variable,
|
||||
reference,
|
||||
llvm_result);
|
||||
reference, llvm_result);
|
||||
break;
|
||||
case ExpressionKindParameter:
|
||||
err = impl_parameter_load(unit, scope, builder, expr->impl.parameter,
|
||||
reference,
|
||||
llvm_result);
|
||||
err =
|
||||
impl_parameter_load(unit, scope, builder, expr->impl.parameter,
|
||||
reference, llvm_result);
|
||||
break;
|
||||
case ExpressionKindAddressOf:
|
||||
err = impl_address_of(unit, scope, builder, &expr->impl.addressOf,
|
||||
|
@ -520,15 +545,15 @@ BackendError impl_expr(LLVMBackendCompileUnit *unit, LLVMLocalScope *scope,
|
|||
break;
|
||||
case ExpressionKindDereference:
|
||||
err = impl_deref(unit, scope, builder, &expr->impl.dereference,
|
||||
reference,
|
||||
deref_depth,
|
||||
llvm_result);
|
||||
reference, deref_depth, llvm_result);
|
||||
break;
|
||||
case ExpressionKindFunctionCall:
|
||||
err = impl_func_call(unit, builder, scope, expr->impl.call, llvm_result);
|
||||
err = impl_func_call(unit, builder, scope, expr->impl.call,
|
||||
llvm_result);
|
||||
break;
|
||||
default:
|
||||
err = new_backend_impl_error(Implementation, NULL, "unknown expression");
|
||||
err = new_backend_impl_error(Implementation, NULL,
|
||||
"unknown expression");
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,10 +10,9 @@
|
|||
#include <llvm/llvm-ir/func.h>
|
||||
#include <llvm/parser.h>
|
||||
|
||||
BackendError impl_expr(LLVMBackendCompileUnit *unit, LLVMLocalScope *scope,
|
||||
LLVMBuilderRef builder, Expression *expr,
|
||||
LLVMBool reference,
|
||||
uint32_t deref_depth,
|
||||
LLVMValueRef *llvm_result);
|
||||
BackendError impl_expr(LLVMBackendCompileUnit* unit, LLVMLocalScope* scope,
|
||||
LLVMBuilderRef builder, Expression* expr,
|
||||
LLVMBool reference, uint32_t deref_depth,
|
||||
LLVMValueRef* llvm_result);
|
||||
|
||||
#endif // LLVM_BACKEND_EXPR_H
|
||||
#endif // LLVM_BACKEND_EXPR_H
|
||||
|
|
|
@ -2,21 +2,21 @@
|
|||
#include <codegen/backend.h>
|
||||
#include <llvm-c/Core.h>
|
||||
#include <llvm-c/Types.h>
|
||||
#include <llvm/llvm-ir/expr.h>
|
||||
#include <llvm/llvm-ir/func.h>
|
||||
#include <llvm/parser.h>
|
||||
#include <llvm/llvm-ir/types.h>
|
||||
#include <llvm/llvm-ir/stmt.h>
|
||||
#include <llvm/llvm-ir/types.h>
|
||||
#include <llvm/llvm-ir/variables.h>
|
||||
#include <llvm/parser.h>
|
||||
#include <mem/cache.h>
|
||||
#include <set/types.h>
|
||||
#include <sys/log.h>
|
||||
#include <mem/cache.h>
|
||||
#include <llvm/llvm-ir/expr.h>
|
||||
|
||||
LLVMLocalScope* new_local_scope(LLVMLocalScope* parent) {
|
||||
LLVMLocalScope* scope = malloc(sizeof(LLVMLocalScope));
|
||||
|
||||
scope->func_scope = parent->func_scope;
|
||||
scope->vars = g_hash_table_new(g_str_hash, g_str_equal);
|
||||
scope->func_scope = parent->func_scope;
|
||||
scope->vars = g_hash_table_new(g_str_hash, g_str_equal);
|
||||
scope->parent_scope = parent;
|
||||
|
||||
return scope;
|
||||
|
@ -27,8 +27,7 @@ void delete_local_scope(LLVMLocalScope* scope) {
|
|||
free(scope);
|
||||
}
|
||||
|
||||
LLVMValueRef get_parameter(const LLVMFuncScope* scope,
|
||||
const char* name) {
|
||||
LLVMValueRef get_parameter(const LLVMFuncScope* scope, const char* name) {
|
||||
if (g_hash_table_contains(scope->params, name)) {
|
||||
return g_hash_table_lookup(scope->params, name);
|
||||
}
|
||||
|
@ -45,7 +44,8 @@ LLVMValueRef get_variable(const LLVMLocalScope* scope, const char* name) {
|
|||
return get_variable(scope->parent_scope, name);
|
||||
}
|
||||
|
||||
LLVMValueRef global_var = get_global_variable(scope->func_scope->global_scope, (char*) name);
|
||||
LLVMValueRef global_var =
|
||||
get_global_variable(scope->func_scope->global_scope, (char*) name);
|
||||
return global_var;
|
||||
}
|
||||
|
||||
|
@ -63,7 +63,8 @@ LLVMBool is_parameter(const LLVMLocalScope* scope, const char* name) {
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
LLVMValueRef global_var = get_global_variable(scope->func_scope->global_scope, (char*) name);
|
||||
LLVMValueRef global_var =
|
||||
get_global_variable(scope->func_scope->global_scope, (char*) name);
|
||||
return global_var != NULL;
|
||||
}
|
||||
|
||||
|
@ -77,17 +78,17 @@ BackendError impl_param_type(LLVMBackendCompileUnit* unit,
|
|||
|
||||
if (param->kind == ParameterDeclarationKind) {
|
||||
gemstone_type = param->impl.declaration.type;
|
||||
qualifier = param->impl.declaration.qualifier;
|
||||
qualifier = param->impl.declaration.qualifier;
|
||||
} else {
|
||||
gemstone_type = param->impl.definiton.declaration.type;
|
||||
qualifier = param->impl.definiton.declaration.qualifier;
|
||||
qualifier = param->impl.definiton.declaration.qualifier;
|
||||
}
|
||||
|
||||
// wrap output variables as pointers
|
||||
if (qualifier == Out || qualifier == InOut) {
|
||||
Type* reference_type = alloca(sizeof(Type));
|
||||
|
||||
reference_type->kind = TypeKindReference;
|
||||
reference_type->kind = TypeKindReference;
|
||||
reference_type->impl.reference = gemstone_type;
|
||||
|
||||
gemstone_type = reference_type;
|
||||
|
@ -104,7 +105,8 @@ BackendError impl_func_type(LLVMBackendCompileUnit* unit,
|
|||
DEBUG("implementing function declaration: %s()", func->name);
|
||||
BackendError err = SUCCESS;
|
||||
|
||||
GArray* llvm_params = mem_new_g_array(MemoryNamespaceLlvm, sizeof(LLVMTypeRef));
|
||||
GArray* llvm_params =
|
||||
mem_new_g_array(MemoryNamespaceLlvm, sizeof(LLVMTypeRef));
|
||||
GArray* func_params = NULL;
|
||||
|
||||
if (func->kind == FunctionDeclarationKind) {
|
||||
|
@ -117,7 +119,7 @@ BackendError impl_func_type(LLVMBackendCompileUnit* unit,
|
|||
Parameter* param = &g_array_index(func_params, Parameter, i);
|
||||
|
||||
LLVMTypeRef llvm_type = NULL;
|
||||
err = impl_param_type(unit, scope, param, &llvm_type);
|
||||
err = impl_param_type(unit, scope, param, &llvm_type);
|
||||
|
||||
if (err.kind != Success) {
|
||||
return err;
|
||||
|
@ -131,17 +133,19 @@ BackendError impl_func_type(LLVMBackendCompileUnit* unit,
|
|||
LLVMTypeRef llvm_return_type = LLVMVoidTypeInContext(unit->context);
|
||||
if (func->kind == FunctionDeclarationKind) {
|
||||
if (func->impl.declaration.return_value != NULL) {
|
||||
err = get_type_impl(unit, scope, func->impl.declaration.return_value, &llvm_return_type);
|
||||
err =
|
||||
get_type_impl(unit, scope, func->impl.declaration.return_value,
|
||||
&llvm_return_type);
|
||||
}
|
||||
} else {
|
||||
if (func->impl.definition.return_value != NULL) {
|
||||
err = get_type_impl(unit, scope, func->impl.definition.return_value, &llvm_return_type);
|
||||
err = get_type_impl(unit, scope, func->impl.definition.return_value,
|
||||
&llvm_return_type);
|
||||
}
|
||||
}
|
||||
|
||||
LLVMTypeRef llvm_fun_type =
|
||||
LLVMFunctionType(llvm_return_type,
|
||||
(LLVMTypeRef*)llvm_params->data, llvm_params->len, 0);
|
||||
LLVMTypeRef llvm_fun_type = LLVMFunctionType(
|
||||
llvm_return_type, (LLVMTypeRef*) llvm_params->data, llvm_params->len, 0);
|
||||
|
||||
*llvm_fun = LLVMAddFunction(unit->module, func->name, llvm_fun_type);
|
||||
|
||||
|
@ -151,8 +155,8 @@ BackendError impl_func_type(LLVMBackendCompileUnit* unit,
|
|||
}
|
||||
|
||||
BackendError impl_func_def(LLVMBackendCompileUnit* unit,
|
||||
LLVMGlobalScope* global_scope,
|
||||
Function* func, const char* name) {
|
||||
LLVMGlobalScope* global_scope, Function* func,
|
||||
const char* name) {
|
||||
BackendError err = SUCCESS;
|
||||
|
||||
LLVMValueRef llvm_func = LLVMGetNamedFunction(unit->module, name);
|
||||
|
@ -162,57 +166,66 @@ BackendError impl_func_def(LLVMBackendCompileUnit* unit,
|
|||
// NOTE: lives till the end of the function
|
||||
LLVMFuncScope* func_scope = alloca(sizeof(LLVMFuncScope));
|
||||
|
||||
func_scope->llvm_func = llvm_func;
|
||||
func_scope->llvm_func = llvm_func;
|
||||
func_scope->global_scope = global_scope;
|
||||
func_scope->params = g_hash_table_new(g_str_hash, g_str_equal);
|
||||
func_scope->params = g_hash_table_new(g_str_hash, g_str_equal);
|
||||
|
||||
// create function body builder
|
||||
LLVMBasicBlockRef entry =
|
||||
LLVMAppendBasicBlockInContext(unit->context, llvm_func, "func.entry");
|
||||
LLVMAppendBasicBlockInContext(unit->context, llvm_func, "func.entry");
|
||||
LLVMBuilderRef builder = LLVMCreateBuilderInContext(unit->context);
|
||||
LLVMPositionBuilderAtEnd(builder, entry);
|
||||
|
||||
// create value references for parameter
|
||||
for (guint i = 0; i < func->impl.definition.parameter->len; i++) {
|
||||
Parameter* param = &g_array_index(func->impl.definition.parameter, Parameter, i);
|
||||
Parameter* param =
|
||||
&g_array_index(func->impl.definition.parameter, Parameter, i);
|
||||
LLVMValueRef llvm_param = LLVMGetParam(llvm_func, i);
|
||||
|
||||
if (llvm_param == NULL) {
|
||||
return new_backend_impl_error(Implementation, NULL, "invalid parameter");
|
||||
return new_backend_impl_error(Implementation, NULL,
|
||||
"invalid parameter");
|
||||
}
|
||||
|
||||
g_hash_table_insert(func_scope->params, (gpointer)param->name, llvm_param);
|
||||
g_hash_table_insert(func_scope->params, (gpointer) param->name,
|
||||
llvm_param);
|
||||
}
|
||||
|
||||
LLVMBasicBlockRef llvm_start_body_block = NULL;
|
||||
LLVMBasicBlockRef llvm_end_body_block = NULL;
|
||||
err = impl_block(unit, builder, func_scope, &llvm_start_body_block, &llvm_end_body_block, func->impl.definition.body);
|
||||
LLVMBasicBlockRef llvm_end_body_block = NULL;
|
||||
err = impl_block(unit, builder, func_scope, &llvm_start_body_block,
|
||||
&llvm_end_body_block, func->impl.definition.body);
|
||||
|
||||
if (err.kind == Success) {
|
||||
LLVMPositionBuilderAtEnd(builder, entry);
|
||||
LLVMBuildBr(builder, llvm_start_body_block);
|
||||
|
||||
LLVMValueRef terminator = LLVMGetBasicBlockTerminator(llvm_end_body_block);
|
||||
LLVMValueRef terminator =
|
||||
LLVMGetBasicBlockTerminator(llvm_end_body_block);
|
||||
if (terminator == NULL) {
|
||||
// insert returning end block
|
||||
LLVMBasicBlockRef end_block =
|
||||
LLVMAppendBasicBlockInContext(unit->context, llvm_func, "func.end");
|
||||
LLVMBasicBlockRef end_block = LLVMAppendBasicBlockInContext(
|
||||
unit->context, llvm_func, "func.end");
|
||||
LLVMPositionBuilderAtEnd(builder, end_block);
|
||||
|
||||
LLVMValueRef llvm_return = NULL;
|
||||
if (func->kind == FunctionDeclarationKind) {
|
||||
if (func->impl.declaration.return_value != NULL) {
|
||||
err = get_type_default_value(unit, global_scope, func->impl.declaration.return_value, &llvm_return);
|
||||
err = get_type_default_value(
|
||||
unit, global_scope,
|
||||
func->impl.declaration.return_value, &llvm_return);
|
||||
if (err.kind != Success) {
|
||||
return err;
|
||||
}
|
||||
LLVMBuildRet(builder, llvm_return);
|
||||
}else {
|
||||
} else {
|
||||
LLVMBuildRetVoid(builder);
|
||||
}
|
||||
} else {
|
||||
if (func->impl.definition.return_value != NULL) {
|
||||
err = get_type_default_value(unit, global_scope, func->impl.definition.return_value, &llvm_return);
|
||||
err = get_type_default_value(
|
||||
unit, global_scope,
|
||||
func->impl.definition.return_value, &llvm_return);
|
||||
if (err.kind != Success) {
|
||||
return err;
|
||||
}
|
||||
|
@ -237,7 +250,8 @@ BackendError impl_func_def(LLVMBackendCompileUnit* unit,
|
|||
}
|
||||
|
||||
BackendError impl_function_types(LLVMBackendCompileUnit* unit,
|
||||
LLVMGlobalScope* scope, GHashTable* functions) {
|
||||
LLVMGlobalScope* scope,
|
||||
GHashTable* functions) {
|
||||
DEBUG("implementing functions...");
|
||||
GHashTableIter iterator;
|
||||
g_hash_table_iter_init(&iterator, functions);
|
||||
|
@ -271,7 +285,7 @@ BackendError impl_functions(LLVMBackendCompileUnit* unit,
|
|||
Function* func = (Function*) val;
|
||||
|
||||
if (func->kind != FunctionDeclarationKind) {
|
||||
err = impl_func_def(unit, scope, func, (const char*)key);
|
||||
err = impl_func_def(unit, scope, func, (const char*) key);
|
||||
}
|
||||
|
||||
if (err.kind != Success) {
|
||||
|
@ -285,22 +299,23 @@ BackendError impl_functions(LLVMBackendCompileUnit* unit,
|
|||
return err;
|
||||
}
|
||||
|
||||
gboolean is_parameter_out(Parameter *param) {
|
||||
gboolean is_parameter_out(Parameter* param) {
|
||||
gboolean is_out = FALSE;
|
||||
|
||||
if (param->kind == ParameterDeclarationKind) {
|
||||
is_out = param->impl.declaration.qualifier == Out || param->impl.declaration.qualifier == InOut;
|
||||
is_out = param->impl.declaration.qualifier == Out
|
||||
|| param->impl.declaration.qualifier == InOut;
|
||||
} else {
|
||||
is_out = param->impl.definiton.declaration.qualifier == Out ||
|
||||
param->impl.definiton.declaration.qualifier == InOut;
|
||||
is_out = param->impl.definiton.declaration.qualifier == Out
|
||||
|| param->impl.definiton.declaration.qualifier == InOut;
|
||||
}
|
||||
|
||||
return is_out;
|
||||
}
|
||||
|
||||
BackendError impl_func_call(LLVMBackendCompileUnit *unit,
|
||||
LLVMBuilderRef builder, LLVMLocalScope *scope,
|
||||
const FunctionCall *call,
|
||||
BackendError impl_func_call(LLVMBackendCompileUnit* unit,
|
||||
LLVMBuilderRef builder, LLVMLocalScope* scope,
|
||||
const FunctionCall* call,
|
||||
LLVMValueRef* return_value) {
|
||||
DEBUG("implementing function call...");
|
||||
BackendError err = SUCCESS;
|
||||
|
@ -310,10 +325,11 @@ BackendError impl_func_call(LLVMBackendCompileUnit *unit,
|
|||
// prevent memory allocation when number of bytes would be zero
|
||||
// avoid going of assertion in memory cache
|
||||
if (call->expressions->len > 0) {
|
||||
arguments = mem_alloc(MemoryNamespaceLlvm, sizeof(LLVMValueRef) * call->expressions->len);
|
||||
arguments = mem_alloc(MemoryNamespaceLlvm,
|
||||
sizeof(LLVMValueRef) * call->expressions->len);
|
||||
|
||||
for (size_t i = 0; i < call->expressions->len; i++) {
|
||||
Expression *arg = g_array_index(call->expressions, Expression*, i);
|
||||
Expression* arg = g_array_index(call->expressions, Expression*, i);
|
||||
|
||||
GArray* param_list;
|
||||
if (call->function->kind == FunctionDeclarationKind) {
|
||||
|
@ -325,18 +341,24 @@ BackendError impl_func_call(LLVMBackendCompileUnit *unit,
|
|||
Parameter param = g_array_index(param_list, Parameter, i);
|
||||
|
||||
LLVMValueRef llvm_arg = NULL;
|
||||
err = impl_expr(unit, scope, builder, arg, is_parameter_out(¶m), 0, &llvm_arg);
|
||||
err = impl_expr(unit, scope, builder, arg, is_parameter_out(¶m),
|
||||
0, &llvm_arg);
|
||||
|
||||
if (err.kind != Success) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (is_parameter_out(¶m)) {
|
||||
if ((arg->kind == ExpressionKindParameter && !is_parameter_out(arg->impl.parameter)) || arg->kind != ExpressionKindParameter) {
|
||||
LLVMValueRef index = LLVMConstInt(LLVMInt32Type(), 0, false);
|
||||
if ((arg->kind == ExpressionKindParameter
|
||||
&& !is_parameter_out(arg->impl.parameter))
|
||||
|| arg->kind != ExpressionKindParameter) {
|
||||
LLVMValueRef index =
|
||||
LLVMConstInt(LLVMInt32Type(), 0, false);
|
||||
LLVMTypeRef llvm_type = NULL;
|
||||
get_type_impl(unit, scope->func_scope->global_scope, param.impl.declaration.type, &llvm_type);
|
||||
llvm_arg = LLVMBuildGEP2(builder, llvm_type, llvm_arg, &index, 1, "");
|
||||
get_type_impl(unit, scope->func_scope->global_scope,
|
||||
param.impl.declaration.type, &llvm_type);
|
||||
llvm_arg = LLVMBuildGEP2(builder, llvm_type, llvm_arg,
|
||||
&index, 1, "");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -345,16 +367,20 @@ BackendError impl_func_call(LLVMBackendCompileUnit *unit,
|
|||
}
|
||||
|
||||
if (err.kind == Success) {
|
||||
LLVMValueRef llvm_func = LLVMGetNamedFunction(unit->module, call->function->name);
|
||||
LLVMValueRef llvm_func =
|
||||
LLVMGetNamedFunction(unit->module, call->function->name);
|
||||
|
||||
if (llvm_func == NULL) {
|
||||
return new_backend_impl_error(Implementation, NULL, "no declared function");
|
||||
return new_backend_impl_error(Implementation, NULL,
|
||||
"no declared function");
|
||||
}
|
||||
|
||||
LLVMTypeRef llvm_func_type = g_hash_table_lookup(scope->func_scope->global_scope->functions, call->function->name);
|
||||
LLVMTypeRef llvm_func_type = g_hash_table_lookup(
|
||||
scope->func_scope->global_scope->functions, call->function->name);
|
||||
|
||||
LLVMValueRef value = LLVMBuildCall2(builder, llvm_func_type, llvm_func, arguments, call->expressions->len,
|
||||
"");
|
||||
LLVMValueRef value =
|
||||
LLVMBuildCall2(builder, llvm_func_type, llvm_func, arguments,
|
||||
call->expressions->len, "");
|
||||
|
||||
if (NULL != return_value) {
|
||||
*return_value = value;
|
||||
|
@ -363,4 +389,3 @@ BackendError impl_func_call(LLVMBackendCompileUnit *unit,
|
|||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,9 +2,8 @@
|
|||
#ifndef LLVM_BACKEND_FUNC_H_
|
||||
#define LLVM_BACKEND_FUNC_H_
|
||||
|
||||
#include <llvm/parser.h>
|
||||
|
||||
#include <glib.h>
|
||||
#include <llvm/parser.h>
|
||||
|
||||
typedef struct LLVMFuncScope_t {
|
||||
LLVMGlobalScope* global_scope;
|
||||
|
@ -33,16 +32,14 @@ LLVMValueRef get_parameter(const LLVMFuncScope* scope, const char* name);
|
|||
LLVMBool is_parameter(const LLVMLocalScope* scope, const char* name);
|
||||
|
||||
BackendError impl_function_types(LLVMBackendCompileUnit* unit,
|
||||
LLVMGlobalScope* scope,
|
||||
GHashTable* variables);
|
||||
LLVMGlobalScope* scope, GHashTable* variables);
|
||||
|
||||
BackendError impl_functions(LLVMBackendCompileUnit* unit,
|
||||
LLVMGlobalScope* scope,
|
||||
GHashTable* variables);
|
||||
LLVMGlobalScope* scope, GHashTable* variables);
|
||||
|
||||
BackendError impl_func_call(LLVMBackendCompileUnit *unit,
|
||||
LLVMBuilderRef builder, LLVMLocalScope *scope,
|
||||
const FunctionCall *call,
|
||||
BackendError impl_func_call(LLVMBackendCompileUnit* unit,
|
||||
LLVMBuilderRef builder, LLVMLocalScope* scope,
|
||||
const FunctionCall* call,
|
||||
LLVMValueRef* return_value);
|
||||
|
||||
#endif // LLVM_BACKEND_FUNC_H_
|
||||
|
|
|
@ -2,72 +2,74 @@
|
|||
// Created by servostar on 5/28/24.
|
||||
//
|
||||
|
||||
#include <assert.h>
|
||||
#include <codegen/backend.h>
|
||||
#include <sys/log.h>
|
||||
#include <llvm/parser.h>
|
||||
#include <llvm/llvm-ir/stmt.h>
|
||||
#include <llvm/llvm-ir/expr.h>
|
||||
#include <llvm/llvm-ir/func.h>
|
||||
#include <llvm/llvm-ir/stmt.h>
|
||||
#include <llvm/llvm-ir/types.h>
|
||||
#include <assert.h>
|
||||
#include <llvm/parser.h>
|
||||
#include <mem/cache.h>
|
||||
#include <sys/log.h>
|
||||
|
||||
BackendError impl_param_load(
|
||||
LLVMBackendCompileUnit *unit,
|
||||
LLVMBuilderRef builder,
|
||||
LLVMLocalScope *scope,
|
||||
const StorageExpr *expr,
|
||||
LLVMValueRef* storage_target) {
|
||||
BackendError impl_param_load(LLVMBackendCompileUnit* unit,
|
||||
LLVMBuilderRef builder, LLVMLocalScope* scope,
|
||||
const StorageExpr* expr,
|
||||
LLVMValueRef* storage_target) {
|
||||
BackendError err = SUCCESS;
|
||||
|
||||
if (expr->impl.parameter->impl.declaration.qualifier == Out || expr->impl.parameter->impl.declaration.qualifier == InOut) {
|
||||
if (expr->impl.parameter->impl.declaration.qualifier == Out
|
||||
|| expr->impl.parameter->impl.declaration.qualifier == InOut) {
|
||||
LLVMTypeRef llvm_type = NULL;
|
||||
err = get_type_impl(unit, scope->func_scope->global_scope, expr->impl.parameter->impl.declaration.type, &llvm_type);
|
||||
err = get_type_impl(unit, scope->func_scope->global_scope,
|
||||
expr->impl.parameter->impl.declaration.type,
|
||||
&llvm_type);
|
||||
if (err.kind != Success) {
|
||||
return err;
|
||||
}
|
||||
|
||||
*storage_target = LLVMBuildLoad2(builder, llvm_type, *storage_target, "strg.param.out.load");
|
||||
*storage_target = LLVMBuildLoad2(builder, llvm_type, *storage_target,
|
||||
"strg.param.out.load");
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
BackendError impl_storage_expr(
|
||||
LLVMBackendCompileUnit *unit,
|
||||
LLVMBuilderRef
|
||||
builder,
|
||||
LLVMLocalScope *scope,
|
||||
const StorageExpr *expr,
|
||||
LLVMValueRef* storage_target) {
|
||||
BackendError impl_storage_expr(LLVMBackendCompileUnit* unit,
|
||||
LLVMBuilderRef builder, LLVMLocalScope* scope,
|
||||
const StorageExpr* expr,
|
||||
LLVMValueRef* storage_target) {
|
||||
|
||||
BackendError err = SUCCESS;
|
||||
|
||||
switch (expr->kind) {
|
||||
case StorageExprKindVariable:
|
||||
*storage_target =
|
||||
get_variable(scope, expr->impl.variable->name);
|
||||
*storage_target = get_variable(scope, expr->impl.variable->name);
|
||||
break;
|
||||
case StorageExprKindParameter:
|
||||
*storage_target =
|
||||
get_parameter(scope->func_scope, expr->impl.parameter->name);
|
||||
get_parameter(scope->func_scope, expr->impl.parameter->name);
|
||||
break;
|
||||
case StorageExprKindDereference:
|
||||
|
||||
LLVMValueRef index = NULL;
|
||||
err = impl_expr(unit, scope, builder, expr->impl.dereference.index, false, 0, &index);
|
||||
err = impl_expr(unit, scope, builder, expr->impl.dereference.index,
|
||||
false, 0, &index);
|
||||
if (err.kind != Success) {
|
||||
return err;
|
||||
}
|
||||
|
||||
LLVMValueRef array = NULL;
|
||||
err = impl_storage_expr(unit, builder, scope, expr->impl.dereference.array, &array);
|
||||
err = impl_storage_expr(unit, builder, scope,
|
||||
expr->impl.dereference.array, &array);
|
||||
if (err.kind != Success) {
|
||||
return err;
|
||||
}
|
||||
|
||||
if (expr->impl.dereference.array->kind == StorageExprKindParameter) {
|
||||
err = impl_param_load(unit, builder, scope, expr->impl.dereference.array, &array);
|
||||
if (expr->impl.dereference.array->kind
|
||||
== StorageExprKindParameter) {
|
||||
err = impl_param_load(unit, builder, scope,
|
||||
expr->impl.dereference.array, &array);
|
||||
if (err.kind != Success) {
|
||||
return err;
|
||||
}
|
||||
|
@ -75,21 +77,26 @@ BackendError impl_storage_expr(
|
|||
|
||||
if (true) {
|
||||
LLVMTypeRef deref_type = NULL;
|
||||
err = get_type_impl(unit, scope->func_scope->global_scope, expr->impl.dereference.array->target_type, &deref_type);
|
||||
err = get_type_impl(unit, scope->func_scope->global_scope,
|
||||
expr->impl.dereference.array->target_type,
|
||||
&deref_type);
|
||||
if (err.kind != Success) {
|
||||
return err;
|
||||
}
|
||||
|
||||
array = LLVMBuildLoad2(builder, deref_type, array, "strg.deref.indirect-load");
|
||||
array = LLVMBuildLoad2(builder, deref_type, array,
|
||||
"strg.deref.indirect-load");
|
||||
}
|
||||
|
||||
LLVMTypeRef deref_type = NULL;
|
||||
err = get_type_impl(unit, scope->func_scope->global_scope, expr->target_type, &deref_type);
|
||||
err = get_type_impl(unit, scope->func_scope->global_scope,
|
||||
expr->target_type, &deref_type);
|
||||
if (err.kind != Success) {
|
||||
return err;
|
||||
}
|
||||
|
||||
*storage_target = LLVMBuildGEP2(builder, deref_type, array, &index, 1, "strg.deref");
|
||||
*storage_target = LLVMBuildGEP2(builder, deref_type, array, &index,
|
||||
1, "strg.deref");
|
||||
|
||||
break;
|
||||
case StorageExprKindBoxAccess:
|
||||
|
@ -100,24 +107,22 @@ BackendError impl_storage_expr(
|
|||
return err;
|
||||
}
|
||||
|
||||
BackendError impl_assign_stmt(
|
||||
LLVMBackendCompileUnit *unit,
|
||||
LLVMBuilderRef
|
||||
builder,
|
||||
LLVMLocalScope *scope,
|
||||
const Assignment *assignment
|
||||
) {
|
||||
BackendError impl_assign_stmt(LLVMBackendCompileUnit* unit,
|
||||
LLVMBuilderRef builder, LLVMLocalScope* scope,
|
||||
const Assignment* assignment) {
|
||||
BackendError err = SUCCESS;
|
||||
DEBUG("implementing assignment for variable: %p", assignment);
|
||||
|
||||
LLVMValueRef llvm_value = NULL;
|
||||
err = impl_expr(unit, scope, builder, assignment->value, false, 0, &llvm_value);
|
||||
err =
|
||||
impl_expr(unit, scope, builder, assignment->value, false, 0, &llvm_value);
|
||||
if (err.kind != Success) {
|
||||
return err;
|
||||
}
|
||||
|
||||
LLVMValueRef llvm_array = NULL;
|
||||
err = impl_storage_expr(unit, builder, scope, assignment->destination, &llvm_array);
|
||||
err = impl_storage_expr(unit, builder, scope, assignment->destination,
|
||||
&llvm_array);
|
||||
if (err.kind != Success) {
|
||||
return err;
|
||||
}
|
||||
|
@ -127,16 +132,18 @@ BackendError impl_assign_stmt(
|
|||
return err;
|
||||
}
|
||||
|
||||
BackendError impl_basic_block(LLVMBackendCompileUnit *unit,
|
||||
LLVMBuilderRef builder, LLVMLocalScope *scope,
|
||||
const Block *block, LLVMBasicBlockRef *llvm_start_block, LLVMBasicBlockRef *llvm_end_block) {
|
||||
BackendError impl_basic_block(LLVMBackendCompileUnit* unit,
|
||||
LLVMBuilderRef builder, LLVMLocalScope* scope,
|
||||
const Block* block,
|
||||
LLVMBasicBlockRef* llvm_start_block,
|
||||
LLVMBasicBlockRef* llvm_end_block) {
|
||||
DEBUG("implementing basic block...");
|
||||
BackendError err = SUCCESS;
|
||||
|
||||
LLVMLocalScope *block_scope = new_local_scope(scope);
|
||||
LLVMLocalScope* block_scope = new_local_scope(scope);
|
||||
// append a new LLVM basic block
|
||||
*llvm_start_block = LLVMAppendBasicBlockInContext(unit->context, scope->func_scope->llvm_func,
|
||||
"stmt.block.start");
|
||||
*llvm_start_block = LLVMAppendBasicBlockInContext(
|
||||
unit->context, scope->func_scope->llvm_func, "stmt.block.start");
|
||||
LLVMPositionBuilderAtEnd(builder, *llvm_start_block);
|
||||
|
||||
LLVMBasicBlockRef end_previous_block = *llvm_start_block;
|
||||
|
@ -148,8 +155,9 @@ BackendError impl_basic_block(LLVMBackendCompileUnit *unit,
|
|||
Statement* stmt = g_array_index(block->statemnts, Statement*, i);
|
||||
|
||||
LLVMBasicBlockRef llvm_next_start_block = NULL;
|
||||
LLVMBasicBlockRef llvm_next_end_block = NULL;
|
||||
err = impl_stmt(unit, builder, scope, stmt, &llvm_next_start_block, &llvm_next_end_block);
|
||||
LLVMBasicBlockRef llvm_next_end_block = NULL;
|
||||
err = impl_stmt(unit, builder, scope, stmt, &llvm_next_start_block,
|
||||
&llvm_next_end_block);
|
||||
if (err.kind != Success) {
|
||||
return err;
|
||||
}
|
||||
|
@ -164,8 +172,8 @@ BackendError impl_basic_block(LLVMBackendCompileUnit *unit,
|
|||
}
|
||||
|
||||
if (terminated) {
|
||||
end_previous_block = LLVMAppendBasicBlockInContext(unit->context, scope->func_scope->llvm_func,
|
||||
"ret.after");
|
||||
end_previous_block = LLVMAppendBasicBlockInContext(
|
||||
unit->context, scope->func_scope->llvm_func, "ret.after");
|
||||
LLVMPositionBuilderAtEnd(builder, end_previous_block);
|
||||
}
|
||||
}
|
||||
|
@ -177,30 +185,32 @@ BackendError impl_basic_block(LLVMBackendCompileUnit *unit,
|
|||
return err;
|
||||
}
|
||||
|
||||
BackendError impl_while(LLVMBackendCompileUnit *unit,
|
||||
LLVMBuilderRef builder, LLVMLocalScope *scope,
|
||||
BackendError impl_while(LLVMBackendCompileUnit* unit, LLVMBuilderRef builder,
|
||||
LLVMLocalScope* scope,
|
||||
LLVMBasicBlockRef* llvm_start_block,
|
||||
LLVMBasicBlockRef* llvm_end_block,
|
||||
const While *while_stmt) {
|
||||
const While* while_stmt) {
|
||||
DEBUG("implementing while...");
|
||||
BackendError err;
|
||||
|
||||
// Create condition block
|
||||
LLVMBasicBlockRef while_cond_block = LLVMAppendBasicBlockInContext(unit->context, scope->func_scope->llvm_func,
|
||||
"loop.while.cond");
|
||||
LLVMBasicBlockRef while_cond_block = LLVMAppendBasicBlockInContext(
|
||||
unit->context, scope->func_scope->llvm_func, "loop.while.cond");
|
||||
*llvm_start_block = while_cond_block;
|
||||
LLVMPositionBuilderAtEnd(builder, while_cond_block);
|
||||
// Resolve condition in block to a variable
|
||||
LLVMValueRef cond_result = NULL;
|
||||
err = impl_expr(unit, scope, builder, (Expression *) while_stmt->conditon, FALSE, 0, &cond_result);
|
||||
err = impl_expr(unit, scope, builder, (Expression*) while_stmt->conditon,
|
||||
FALSE, 0, &cond_result);
|
||||
if (err.kind != Success) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// build body of loop
|
||||
LLVMBasicBlockRef while_start_body_block = NULL;
|
||||
LLVMBasicBlockRef while_end_body_block = NULL;
|
||||
err = impl_basic_block(unit, builder, scope, &while_stmt->block, &while_start_body_block, &while_end_body_block);
|
||||
LLVMBasicBlockRef while_end_body_block = NULL;
|
||||
err = impl_basic_block(unit, builder, scope, &while_stmt->block,
|
||||
&while_start_body_block, &while_end_body_block);
|
||||
if (err.kind != Success) {
|
||||
return err;
|
||||
}
|
||||
|
@ -210,11 +220,12 @@ BackendError impl_while(LLVMBackendCompileUnit *unit,
|
|||
LLVMBuildBr(builder, while_cond_block);
|
||||
|
||||
// builder will continue after the loop
|
||||
LLVMBasicBlockRef while_after_block = LLVMAppendBasicBlockInContext(unit->context, scope->func_scope->llvm_func,
|
||||
"loop.while.after");
|
||||
LLVMBasicBlockRef while_after_block = LLVMAppendBasicBlockInContext(
|
||||
unit->context, scope->func_scope->llvm_func, "loop.while.after");
|
||||
// build conditional branch at end of condition block
|
||||
LLVMPositionBuilderAtEnd(builder, while_cond_block);
|
||||
LLVMBuildCondBr(builder, cond_result, while_start_body_block, while_after_block);
|
||||
LLVMBuildCondBr(builder, cond_result, while_start_body_block,
|
||||
while_after_block);
|
||||
|
||||
LLVMPositionBuilderAtEnd(builder, while_after_block);
|
||||
|
||||
|
@ -223,46 +234,52 @@ BackendError impl_while(LLVMBackendCompileUnit *unit,
|
|||
return err;
|
||||
}
|
||||
|
||||
BackendError
|
||||
impl_cond_block(LLVMBackendCompileUnit *unit, LLVMBuilderRef builder, LLVMLocalScope *scope, Expression *cond,
|
||||
const Block *block, LLVMBasicBlockRef *cond_block, LLVMBasicBlockRef *start_body_block, LLVMBasicBlockRef *end_body_block,
|
||||
LLVMValueRef *llvm_cond) {
|
||||
BackendError impl_cond_block(LLVMBackendCompileUnit* unit,
|
||||
LLVMBuilderRef builder, LLVMLocalScope* scope,
|
||||
Expression* cond, const Block* block,
|
||||
LLVMBasicBlockRef* cond_block,
|
||||
LLVMBasicBlockRef* start_body_block,
|
||||
LLVMBasicBlockRef* end_body_block,
|
||||
LLVMValueRef* llvm_cond) {
|
||||
BackendError err;
|
||||
|
||||
*cond_block = LLVMAppendBasicBlockInContext(unit->context, scope->func_scope->llvm_func,
|
||||
"stmt.branch.cond");
|
||||
*cond_block = LLVMAppendBasicBlockInContext(
|
||||
unit->context, scope->func_scope->llvm_func, "stmt.branch.cond");
|
||||
LLVMPositionBuilderAtEnd(builder, *cond_block);
|
||||
// Resolve condition in block to a variable
|
||||
err = impl_expr(unit, scope, builder, cond, FALSE, 0, llvm_cond);
|
||||
if (err.kind == Success) {
|
||||
// build body of loop
|
||||
err = impl_basic_block(unit, builder, scope, block, start_body_block, end_body_block);
|
||||
err = impl_basic_block(unit, builder, scope, block, start_body_block,
|
||||
end_body_block);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
BackendError impl_branch(LLVMBackendCompileUnit *unit,
|
||||
LLVMBuilderRef builder, LLVMLocalScope *scope,
|
||||
BackendError impl_branch(LLVMBackendCompileUnit* unit, LLVMBuilderRef builder,
|
||||
LLVMLocalScope* scope,
|
||||
LLVMBasicBlockRef* branch_start_block,
|
||||
LLVMBasicBlockRef* branch_end_block,
|
||||
const Branch *branch) {
|
||||
const Branch* branch) {
|
||||
BackendError err = SUCCESS;
|
||||
|
||||
GArray *cond_blocks = g_array_new(FALSE, FALSE, sizeof(LLVMBasicBlockRef));
|
||||
GArray *start_body_blocks = g_array_new(FALSE, FALSE, sizeof(LLVMBasicBlockRef));
|
||||
GArray *end_body_blocks = g_array_new(FALSE, FALSE, sizeof(LLVMBasicBlockRef));
|
||||
GArray *cond_values = g_array_new(FALSE, FALSE, sizeof(LLVMValueRef));
|
||||
GArray* cond_blocks = g_array_new(FALSE, FALSE, sizeof(LLVMBasicBlockRef));
|
||||
GArray* start_body_blocks =
|
||||
g_array_new(FALSE, FALSE, sizeof(LLVMBasicBlockRef));
|
||||
GArray* end_body_blocks =
|
||||
g_array_new(FALSE, FALSE, sizeof(LLVMBasicBlockRef));
|
||||
GArray* cond_values = g_array_new(FALSE, FALSE, sizeof(LLVMValueRef));
|
||||
|
||||
// add If to arrays
|
||||
{
|
||||
LLVMBasicBlockRef cond_block = NULL;
|
||||
LLVMBasicBlockRef cond_block = NULL;
|
||||
LLVMBasicBlockRef start_body_block = NULL;
|
||||
LLVMBasicBlockRef end_body_block = NULL;
|
||||
LLVMValueRef cond_value = NULL;
|
||||
LLVMBasicBlockRef end_body_block = NULL;
|
||||
LLVMValueRef cond_value = NULL;
|
||||
|
||||
err = impl_cond_block(unit, builder, scope, branch->ifBranch.conditon, &branch->ifBranch.block,
|
||||
&cond_block,
|
||||
err = impl_cond_block(unit, builder, scope, branch->ifBranch.conditon,
|
||||
&branch->ifBranch.block, &cond_block,
|
||||
&start_body_block, &end_body_block, &cond_value);
|
||||
|
||||
g_array_append_val(cond_blocks, cond_block);
|
||||
|
@ -274,15 +291,16 @@ BackendError impl_branch(LLVMBackendCompileUnit *unit,
|
|||
// generate else if(s)
|
||||
if (branch->elseIfBranches != NULL) {
|
||||
for (size_t i = 0; i < branch->elseIfBranches->len; i++) {
|
||||
LLVMBasicBlockRef cond_block = NULL;
|
||||
LLVMBasicBlockRef cond_block = NULL;
|
||||
LLVMBasicBlockRef start_body_block = NULL;
|
||||
LLVMBasicBlockRef end_body_block = NULL;
|
||||
LLVMValueRef cond_value = NULL;
|
||||
LLVMBasicBlockRef end_body_block = NULL;
|
||||
LLVMValueRef cond_value = NULL;
|
||||
|
||||
ElseIf *elseIf = ((ElseIf *) branch->elseIfBranches->data) + i;
|
||||
ElseIf* elseIf = ((ElseIf*) branch->elseIfBranches->data) + i;
|
||||
|
||||
err = impl_cond_block(unit, builder, scope, elseIf->conditon, &elseIf->block, &cond_block,
|
||||
&start_body_block, &end_body_block, &cond_value);
|
||||
err = impl_cond_block(
|
||||
unit, builder, scope, elseIf->conditon, &elseIf->block,
|
||||
&cond_block, &start_body_block, &end_body_block, &cond_value);
|
||||
|
||||
g_array_append_val(cond_blocks, cond_block);
|
||||
g_array_append_val(start_body_blocks, start_body_block);
|
||||
|
@ -296,13 +314,14 @@ BackendError impl_branch(LLVMBackendCompileUnit *unit,
|
|||
// else block
|
||||
if (branch->elseBranch.block.statemnts != NULL) {
|
||||
LLVMBasicBlockRef start_else_block = NULL;
|
||||
err = impl_basic_block(unit, builder, scope, &branch->elseBranch.block, &start_else_block, &after_block);
|
||||
err = impl_basic_block(unit, builder, scope, &branch->elseBranch.block,
|
||||
&start_else_block, &after_block);
|
||||
g_array_append_val(cond_blocks, start_else_block);
|
||||
}
|
||||
|
||||
if (after_block == NULL) {
|
||||
after_block = LLVMAppendBasicBlockInContext(unit->context, scope->func_scope->llvm_func,
|
||||
"stmt.branch.after");
|
||||
after_block = LLVMAppendBasicBlockInContext(
|
||||
unit->context, scope->func_scope->llvm_func, "stmt.branch.after");
|
||||
}
|
||||
|
||||
LLVMPositionBuilderAtEnd(builder, after_block);
|
||||
|
@ -313,10 +332,14 @@ BackendError impl_branch(LLVMBackendCompileUnit *unit,
|
|||
}
|
||||
|
||||
for (size_t i = 0; i < cond_blocks->len - 1; i++) {
|
||||
LLVMBasicBlockRef next_block = g_array_index(cond_blocks, LLVMBasicBlockRef, i + 1);
|
||||
LLVMBasicBlockRef cond_block = g_array_index(cond_blocks, LLVMBasicBlockRef, i);
|
||||
LLVMBasicBlockRef start_body_block = g_array_index(start_body_blocks, LLVMBasicBlockRef, i);
|
||||
LLVMBasicBlockRef end_body_block = g_array_index(end_body_blocks, LLVMBasicBlockRef, i);
|
||||
LLVMBasicBlockRef next_block =
|
||||
g_array_index(cond_blocks, LLVMBasicBlockRef, i + 1);
|
||||
LLVMBasicBlockRef cond_block =
|
||||
g_array_index(cond_blocks, LLVMBasicBlockRef, i);
|
||||
LLVMBasicBlockRef start_body_block =
|
||||
g_array_index(start_body_blocks, LLVMBasicBlockRef, i);
|
||||
LLVMBasicBlockRef end_body_block =
|
||||
g_array_index(end_body_blocks, LLVMBasicBlockRef, i);
|
||||
LLVMValueRef cond_value = g_array_index(cond_values, LLVMValueRef, i);
|
||||
|
||||
LLVMPositionBuilderAtEnd(builder, cond_block);
|
||||
|
@ -327,7 +350,7 @@ BackendError impl_branch(LLVMBackendCompileUnit *unit,
|
|||
}
|
||||
|
||||
*branch_start_block = g_array_index(cond_blocks, LLVMBasicBlockRef, 0);
|
||||
*branch_end_block = after_block;
|
||||
*branch_end_block = after_block;
|
||||
|
||||
g_array_free(cond_blocks, TRUE);
|
||||
g_array_free(start_body_blocks, TRUE);
|
||||
|
@ -337,15 +360,14 @@ BackendError impl_branch(LLVMBackendCompileUnit *unit,
|
|||
return err;
|
||||
}
|
||||
|
||||
BackendError impl_decl(LLVMBackendCompileUnit *unit,
|
||||
LLVMBuilderRef builder,
|
||||
LLVMLocalScope *scope,
|
||||
VariableDeclaration *decl,
|
||||
const char *name) {
|
||||
BackendError impl_decl(LLVMBackendCompileUnit* unit, LLVMBuilderRef builder,
|
||||
LLVMLocalScope* scope, VariableDeclaration* decl,
|
||||
const char* name) {
|
||||
DEBUG("implementing local declaration: %s", name);
|
||||
BackendError err = SUCCESS;
|
||||
BackendError err = SUCCESS;
|
||||
LLVMTypeRef llvm_type = NULL;
|
||||
err = get_type_impl(unit, scope->func_scope->global_scope, decl->type, &llvm_type);
|
||||
err = get_type_impl(unit, scope->func_scope->global_scope, decl->type,
|
||||
&llvm_type);
|
||||
|
||||
if (err.kind != Success) {
|
||||
return err;
|
||||
|
@ -355,7 +377,8 @@ BackendError impl_decl(LLVMBackendCompileUnit *unit,
|
|||
LLVMValueRef local = LLVMBuildAlloca(builder, llvm_type, name);
|
||||
|
||||
LLVMValueRef initial_value = NULL;
|
||||
err = get_type_default_value(unit, scope->func_scope->global_scope, decl->type, &initial_value);
|
||||
err = get_type_default_value(unit, scope->func_scope->global_scope,
|
||||
decl->type, &initial_value);
|
||||
|
||||
if (err.kind == Success) {
|
||||
DEBUG("setting default value...");
|
||||
|
@ -368,10 +391,8 @@ BackendError impl_decl(LLVMBackendCompileUnit *unit,
|
|||
return err;
|
||||
}
|
||||
|
||||
BackendError impl_return(LLVMBackendCompileUnit *unit,
|
||||
LLVMBuilderRef builder,
|
||||
LLVMLocalScope *scope,
|
||||
Return *returnStmt) {
|
||||
BackendError impl_return(LLVMBackendCompileUnit* unit, LLVMBuilderRef builder,
|
||||
LLVMLocalScope* scope, Return* returnStmt) {
|
||||
BackendError err = SUCCESS;
|
||||
|
||||
LLVMValueRef expr = NULL;
|
||||
|
@ -385,22 +406,22 @@ BackendError impl_return(LLVMBackendCompileUnit *unit,
|
|||
return err;
|
||||
}
|
||||
|
||||
BackendError impl_def(LLVMBackendCompileUnit *unit,
|
||||
LLVMBuilderRef builder,
|
||||
LLVMLocalScope *scope,
|
||||
VariableDefiniton *def,
|
||||
const char *name) {
|
||||
BackendError impl_def(LLVMBackendCompileUnit* unit, LLVMBuilderRef builder,
|
||||
LLVMLocalScope* scope, VariableDefiniton* def,
|
||||
const char* name) {
|
||||
DEBUG("implementing local definition: %s", name);
|
||||
BackendError err = SUCCESS;
|
||||
BackendError err = SUCCESS;
|
||||
LLVMTypeRef llvm_type = NULL;
|
||||
err = get_type_impl(unit, scope->func_scope->global_scope, def->declaration.type, &llvm_type);
|
||||
err = get_type_impl(unit, scope->func_scope->global_scope,
|
||||
def->declaration.type, &llvm_type);
|
||||
|
||||
if (err.kind != Success) {
|
||||
return err;
|
||||
}
|
||||
|
||||
LLVMValueRef initial_value = NULL;
|
||||
err = impl_expr(unit, scope, builder, def->initializer, FALSE, 0, &initial_value);
|
||||
err = impl_expr(unit, scope, builder, def->initializer, FALSE, 0,
|
||||
&initial_value);
|
||||
if (err.kind != Success) {
|
||||
return err;
|
||||
}
|
||||
|
@ -414,45 +435,52 @@ BackendError impl_def(LLVMBackendCompileUnit *unit,
|
|||
return err;
|
||||
}
|
||||
|
||||
BackendError impl_var(LLVMBackendCompileUnit *unit,
|
||||
LLVMBuilderRef builder,
|
||||
LLVMLocalScope *scope,
|
||||
Variable *var) {
|
||||
BackendError impl_var(LLVMBackendCompileUnit* unit, LLVMBuilderRef builder,
|
||||
LLVMLocalScope* scope, Variable* var) {
|
||||
BackendError err;
|
||||
|
||||
switch (var->kind) {
|
||||
case VariableKindDeclaration:
|
||||
err = impl_decl(unit, builder, scope, &var->impl.declaration, var->name);
|
||||
err = impl_decl(unit, builder, scope, &var->impl.declaration,
|
||||
var->name);
|
||||
break;
|
||||
case VariableKindDefinition:
|
||||
err = impl_def(unit, builder, scope, &var->impl.definiton, var->name);
|
||||
err =
|
||||
impl_def(unit, builder, scope, &var->impl.definiton, var->name);
|
||||
break;
|
||||
default:
|
||||
err = new_backend_impl_error(Implementation, NULL, "Unexpected variable kind in statement");
|
||||
err = new_backend_impl_error(
|
||||
Implementation, NULL, "Unexpected variable kind in statement");
|
||||
break;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
BackendError impl_stmt(LLVMBackendCompileUnit *unit, LLVMBuilderRef builder, LLVMLocalScope *scope, Statement *stmt, LLVMBasicBlockRef* llvm_start_block, LLVMBasicBlockRef* llvm_end_block) {
|
||||
BackendError impl_stmt(LLVMBackendCompileUnit* unit, LLVMBuilderRef builder,
|
||||
LLVMLocalScope* scope, Statement* stmt,
|
||||
LLVMBasicBlockRef* llvm_start_block,
|
||||
LLVMBasicBlockRef* llvm_end_block) {
|
||||
assert(stmt != NULL);
|
||||
DEBUG("implementing statement: %ld", stmt->kind);
|
||||
BackendError err;
|
||||
|
||||
switch (stmt->kind) {
|
||||
case StatementKindAssignment:
|
||||
err = impl_assign_stmt(unit, builder, scope, &stmt->impl.assignment);
|
||||
err =
|
||||
impl_assign_stmt(unit, builder, scope, &stmt->impl.assignment);
|
||||
break;
|
||||
case StatementKindBranch:
|
||||
err = impl_branch(unit, builder, scope, llvm_start_block, llvm_end_block, &stmt->impl.branch);
|
||||
err = impl_branch(unit, builder, scope, llvm_start_block,
|
||||
llvm_end_block, &stmt->impl.branch);
|
||||
break;
|
||||
case StatementKindDeclaration:
|
||||
case StatementKindDefinition:
|
||||
err = impl_var(unit, builder, scope, stmt->impl.variable);
|
||||
break;
|
||||
case StatementKindWhile:
|
||||
err = impl_while(unit, builder, scope, llvm_start_block, llvm_end_block, &stmt->impl.whileLoop);
|
||||
err = impl_while(unit, builder, scope, llvm_start_block,
|
||||
llvm_end_block, &stmt->impl.whileLoop);
|
||||
break;
|
||||
case StatementKindFunctionCall:
|
||||
err = impl_func_call(unit, builder, scope, &stmt->impl.call, NULL);
|
||||
|
@ -461,27 +489,28 @@ BackendError impl_stmt(LLVMBackendCompileUnit *unit, LLVMBuilderRef builder, LLV
|
|||
err = impl_return(unit, builder, scope, &stmt->impl.returnStmt);
|
||||
break;
|
||||
default:
|
||||
err = new_backend_impl_error(Implementation, NULL, "Unexpected statement kind");
|
||||
err = new_backend_impl_error(Implementation, NULL,
|
||||
"Unexpected statement kind");
|
||||
break;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
BackendError impl_block(LLVMBackendCompileUnit *unit,
|
||||
LLVMBuilderRef builder, LLVMFuncScope *scope,
|
||||
BackendError impl_block(LLVMBackendCompileUnit* unit, LLVMBuilderRef builder,
|
||||
LLVMFuncScope* scope,
|
||||
LLVMBasicBlockRef* llvm_start_block,
|
||||
LLVMBasicBlockRef* llvm_end_block,
|
||||
const Block *block) {
|
||||
LLVMBasicBlockRef* llvm_end_block, const Block* block) {
|
||||
DEBUG("Implementing function block...");
|
||||
BackendError err = SUCCESS;
|
||||
|
||||
LLVMLocalScope *function_entry_scope = malloc(sizeof(LLVMLocalScope));
|
||||
function_entry_scope->func_scope = scope;
|
||||
LLVMLocalScope* function_entry_scope = malloc(sizeof(LLVMLocalScope));
|
||||
function_entry_scope->func_scope = scope;
|
||||
function_entry_scope->vars = g_hash_table_new(g_str_hash, g_str_equal);
|
||||
function_entry_scope->parent_scope = NULL;
|
||||
|
||||
err = impl_basic_block(unit, builder, function_entry_scope, block, llvm_start_block, llvm_end_block);
|
||||
err = impl_basic_block(unit, builder, function_entry_scope, block,
|
||||
llvm_start_block, llvm_end_block);
|
||||
|
||||
delete_local_scope(function_entry_scope);
|
||||
|
||||
|
|
|
@ -7,13 +7,14 @@
|
|||
|
||||
#include <llvm/llvm-ir/func.h>
|
||||
|
||||
BackendError impl_block(LLVMBackendCompileUnit *unit,
|
||||
LLVMBuilderRef builder, LLVMFuncScope *scope,
|
||||
BackendError impl_block(LLVMBackendCompileUnit* unit, LLVMBuilderRef builder,
|
||||
LLVMFuncScope* scope,
|
||||
LLVMBasicBlockRef* llvm_start_block,
|
||||
LLVMBasicBlockRef* llvm_end_block,
|
||||
const Block *block);
|
||||
LLVMBasicBlockRef* llvm_end_block, const Block* block);
|
||||
|
||||
BackendError impl_stmt(LLVMBackendCompileUnit *unit, LLVMBuilderRef builder, LLVMLocalScope *scope, Statement *stmt,
|
||||
LLVMBasicBlockRef *llvm_start_block, LLVMBasicBlockRef *llvm_end_block);
|
||||
BackendError impl_stmt(LLVMBackendCompileUnit* unit, LLVMBuilderRef builder,
|
||||
LLVMLocalScope* scope, Statement* stmt,
|
||||
LLVMBasicBlockRef* llvm_start_block,
|
||||
LLVMBasicBlockRef* llvm_end_block);
|
||||
|
||||
#endif // LLVM_BACKEND_STMT_H
|
||||
|
|
|
@ -3,12 +3,12 @@
|
|||
#include <llvm-c/Types.h>
|
||||
#include <llvm/llvm-ir/types.h>
|
||||
#include <llvm/parser.h>
|
||||
#include <mem/cache.h>
|
||||
#include <set/set.h>
|
||||
#include <set/types.h>
|
||||
#include <sys/log.h>
|
||||
#include <set/set.h>
|
||||
#include <mem/cache.h>
|
||||
|
||||
#define BASE_BYTES 4
|
||||
#define BASE_BYTES 4
|
||||
#define BITS_PER_BYTE 8
|
||||
|
||||
static BackendError get_const_primitive_value(PrimitiveType primitive,
|
||||
|
@ -24,7 +24,7 @@ static BackendError get_const_primitive_value(PrimitiveType primitive,
|
|||
break;
|
||||
case Char:
|
||||
gunichar codepoint = g_utf8_get_char(value);
|
||||
*llvm_value = LLVMConstInt(llvm_type, codepoint, false);
|
||||
*llvm_value = LLVMConstInt(llvm_type, codepoint, false);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -39,28 +39,35 @@ static BackendError get_const_composite_value(CompositeType composite,
|
|||
llvm_value);
|
||||
}
|
||||
|
||||
BackendError impl_reference_const(LLVMBackendCompileUnit* unit, TypeValue* value, LLVMValueRef* llvm_value) {
|
||||
BackendError impl_reference_const(LLVMBackendCompileUnit* unit,
|
||||
TypeValue* value, LLVMValueRef* llvm_value) {
|
||||
BackendError err = SUCCESS;
|
||||
if (compareTypes(value->type, (Type*) &StringLiteralType)) {
|
||||
// is string literal
|
||||
LLVMValueRef string_value = LLVMConstString(value->value, strlen(value->value), false);
|
||||
LLVMValueRef string_value =
|
||||
LLVMConstString(value->value, strlen(value->value), false);
|
||||
|
||||
char uuid[9];
|
||||
sprintf(uuid, "%08x", g_str_hash(value->value));
|
||||
|
||||
LLVMValueRef string_global = LLVMAddGlobal(unit->module, LLVMTypeOf(string_value), uuid);
|
||||
LLVMValueRef string_global =
|
||||
LLVMAddGlobal(unit->module, LLVMTypeOf(string_value), uuid);
|
||||
LLVMSetInitializer(string_global, string_value);
|
||||
LLVMSetGlobalConstant(string_global, true);
|
||||
LLVMSetUnnamedAddress(string_global, LLVMGlobalUnnamedAddr);
|
||||
LLVMSetAlignment(string_global, 1);
|
||||
|
||||
// Cast the global variable to a pointer type if needed
|
||||
LLVMTypeRef i8_ptr_type = LLVMPointerType(LLVMInt8TypeInContext(unit->context), 0);
|
||||
LLVMValueRef global_str_ptr = LLVMConstBitCast(string_global, i8_ptr_type);
|
||||
LLVMTypeRef i8_ptr_type =
|
||||
LLVMPointerType(LLVMInt8TypeInContext(unit->context), 0);
|
||||
LLVMValueRef global_str_ptr =
|
||||
LLVMConstBitCast(string_global, i8_ptr_type);
|
||||
|
||||
*llvm_value = global_str_ptr;
|
||||
} else {
|
||||
err = new_backend_impl_error(Implementation, value->nodePtr, "reference initializer can only be string literals");
|
||||
err = new_backend_impl_error(
|
||||
Implementation, value->nodePtr,
|
||||
"reference initializer can only be string literals");
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
@ -79,22 +86,22 @@ BackendError get_const_type_value(LLVMBackendCompileUnit* unit,
|
|||
|
||||
switch (gemstone_value->type->kind) {
|
||||
case TypeKindPrimitive:
|
||||
err = get_const_primitive_value(gemstone_value->type->impl.primitive,
|
||||
llvm_type, gemstone_value->value,
|
||||
llvm_value);
|
||||
err = get_const_primitive_value(
|
||||
gemstone_value->type->impl.primitive, llvm_type,
|
||||
gemstone_value->value, llvm_value);
|
||||
break;
|
||||
case TypeKindComposite:
|
||||
err = get_const_composite_value(gemstone_value->type->impl.composite,
|
||||
llvm_type, gemstone_value->value,
|
||||
llvm_value);
|
||||
err = get_const_composite_value(
|
||||
gemstone_value->type->impl.composite, llvm_type,
|
||||
gemstone_value->value, llvm_value);
|
||||
break;
|
||||
case TypeKindReference:
|
||||
err = impl_reference_const(unit, gemstone_value, llvm_value);
|
||||
break;
|
||||
case TypeKindBox:
|
||||
err =
|
||||
new_backend_impl_error(Implementation, gemstone_value->nodePtr,
|
||||
"boxes cannot be constant value");
|
||||
new_backend_impl_error(Implementation, gemstone_value->nodePtr,
|
||||
"boxes cannot be constant value");
|
||||
break;
|
||||
default:
|
||||
PANIC("invalid value kind: %ld", gemstone_value->type->kind);
|
||||
|
@ -128,7 +135,7 @@ BackendError impl_primtive_type(LLVMBackendCompileUnit* unit,
|
|||
|
||||
BackendError impl_integral_type(LLVMBackendCompileUnit* unit, Scale scale,
|
||||
LLVMTypeRef* llvm_type) {
|
||||
size_t bits = (int)(BASE_BYTES * scale) * BITS_PER_BYTE;
|
||||
size_t bits = (int) (BASE_BYTES * scale) * BITS_PER_BYTE;
|
||||
DEBUG("implementing integral type of size: %ld", bits);
|
||||
LLVMTypeRef integral_type = LLVMIntTypeInContext(unit->context, bits);
|
||||
|
||||
|
@ -142,7 +149,7 @@ BackendError impl_float_type(LLVMBackendCompileUnit* unit, Scale scale,
|
|||
DEBUG("implementing floating point...");
|
||||
LLVMTypeRef float_type = NULL;
|
||||
|
||||
size_t bytes = (int)(scale * BASE_BYTES);
|
||||
size_t bytes = (int) (scale * BASE_BYTES);
|
||||
DEBUG("requested float of bytes: %ld", bytes);
|
||||
switch (bytes) {
|
||||
case 2:
|
||||
|
@ -188,8 +195,8 @@ BackendError impl_composite_type(LLVMBackendCompileUnit* unit,
|
|||
} else {
|
||||
ERROR("unsigned floating point not supported");
|
||||
err = new_backend_impl_error(
|
||||
Implementation, composite->nodePtr,
|
||||
"unsigned floating-point not supported");
|
||||
Implementation, composite->nodePtr,
|
||||
"unsigned floating-point not supported");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
@ -228,7 +235,7 @@ BackendError get_type_impl(LLVMBackendCompileUnit* unit, LLVMGlobalScope* scope,
|
|||
break;
|
||||
case TypeKindBox:
|
||||
err =
|
||||
impl_box_type(unit, scope, gemstone_type->impl.box, llvm_type);
|
||||
impl_box_type(unit, scope, gemstone_type->impl.box, llvm_type);
|
||||
break;
|
||||
default:
|
||||
PANIC("invalid type kind: %ld", gemstone_type->kind);
|
||||
|
@ -252,9 +259,9 @@ BackendError impl_box_type(LLVMBackendCompileUnit* unit, LLVMGlobalScope* scope,
|
|||
|
||||
DEBUG("implementing box members...");
|
||||
while (g_hash_table_iter_next(&iterator, &key, &val) != FALSE) {
|
||||
Type* member_type = ((BoxMember*)val)->type;
|
||||
Type* member_type = ((BoxMember*) val)->type;
|
||||
|
||||
DEBUG("implementing member: %s ", ((BoxMember*)val)->name);
|
||||
DEBUG("implementing member: %s ", ((BoxMember*) val)->name);
|
||||
|
||||
LLVMTypeRef llvm_local_type = NULL;
|
||||
err = get_type_impl(unit, scope, member_type, &llvm_local_type);
|
||||
|
@ -269,7 +276,7 @@ BackendError impl_box_type(LLVMBackendCompileUnit* unit, LLVMGlobalScope* scope,
|
|||
|
||||
if (err.kind == Success) {
|
||||
*llvm_type =
|
||||
LLVMStructType((LLVMTypeRef*)members->data, members->len, 0);
|
||||
LLVMStructType((LLVMTypeRef*) members->data, members->len, 0);
|
||||
}
|
||||
|
||||
g_array_free(members, FALSE);
|
||||
|
@ -284,7 +291,7 @@ BackendError impl_reference_type(LLVMBackendCompileUnit* unit,
|
|||
DEBUG("implementing reference type...");
|
||||
BackendError err = SUCCESS;
|
||||
LLVMTypeRef type = NULL;
|
||||
err = get_type_impl(unit, scope, reference, &type);
|
||||
err = get_type_impl(unit, scope, reference, &type);
|
||||
|
||||
if (err.kind == Success) {
|
||||
*llvm_type = LLVMPointerType(type, 0);
|
||||
|
@ -302,16 +309,18 @@ BackendError impl_type(LLVMBackendCompileUnit* unit, Type* gemstone_type,
|
|||
err = get_type_impl(unit, scope, gemstone_type, &llvm_type);
|
||||
|
||||
if (err.kind == Success) {
|
||||
g_hash_table_insert(scope->types, (gpointer)alias, llvm_type);
|
||||
g_hash_table_insert(scope->types, (gpointer) alias, llvm_type);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
BackendError impl_type_define(LLVMBackendCompileUnit* unit, Typedefine* gemstone_type,
|
||||
const char* alias, LLVMGlobalScope* scope) {
|
||||
BackendError impl_type_define(LLVMBackendCompileUnit* unit,
|
||||
Typedefine* gemstone_type, const char* alias,
|
||||
LLVMGlobalScope* scope) {
|
||||
BackendError err = SUCCESS;
|
||||
DEBUG("implementing type of kind: %ld as %s", gemstone_type->type->kind, alias);
|
||||
DEBUG("implementing type of kind: %ld as %s", gemstone_type->type->kind,
|
||||
alias);
|
||||
|
||||
err = impl_type(unit, gemstone_type->type, alias, scope);
|
||||
|
||||
|
@ -330,7 +339,8 @@ BackendError impl_types(LLVMBackendCompileUnit* unit, LLVMGlobalScope* scope,
|
|||
BackendError err = SUCCESS;
|
||||
|
||||
while (g_hash_table_iter_next(&iterator, &key, &val) != FALSE) {
|
||||
err = impl_type_define(unit, (Typedefine*) val, (const char*)key, scope);
|
||||
err =
|
||||
impl_type_define(unit, (Typedefine*) val, (const char*) key, scope);
|
||||
|
||||
if (err.kind != Success) {
|
||||
break;
|
||||
|
@ -375,8 +385,8 @@ BackendError get_composite_default_value(CompositeType* composite,
|
|||
err = get_primitive_default_value(Float, llvm_type, llvm_value);
|
||||
} else {
|
||||
err = new_backend_impl_error(
|
||||
Implementation, composite->nodePtr,
|
||||
"unsigned floating-point not supported");
|
||||
Implementation, composite->nodePtr,
|
||||
"unsigned floating-point not supported");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
@ -411,7 +421,7 @@ BackendError get_box_default_value(LLVMBackendCompileUnit* unit,
|
|||
GArray* constants = g_array_new(FALSE, FALSE, sizeof(LLVMValueRef));
|
||||
|
||||
while (g_hash_table_iter_next(&iterator, &key, &val) != FALSE) {
|
||||
Type* member_type = ((BoxMember*)val)->type;
|
||||
Type* member_type = ((BoxMember*) val)->type;
|
||||
|
||||
LLVMValueRef constant = NULL;
|
||||
err = get_type_default_value(unit, scope, member_type, &constant);
|
||||
|
@ -424,7 +434,7 @@ BackendError get_box_default_value(LLVMBackendCompileUnit* unit,
|
|||
DEBUG("build %ld member default values", constants->len);
|
||||
|
||||
*llvm_value = LLVMConstNamedStruct(
|
||||
llvm_type, (LLVMValueRef*)constants->data, constants->len);
|
||||
llvm_type, (LLVMValueRef*) constants->data, constants->len);
|
||||
|
||||
g_array_free(constants, FALSE);
|
||||
|
||||
|
|
|
@ -23,4 +23,4 @@ BackendError get_const_type_value(LLVMBackendCompileUnit* unit,
|
|||
TypeValue* gemstone_value,
|
||||
LLVMValueRef* llvm_value);
|
||||
|
||||
#endif // LLVM_BACKEND_TYPES_H_
|
||||
#endif // LLVM_BACKEND_TYPES_H_
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
|
||||
#include "expr.h"
|
||||
|
||||
#include <codegen/backend.h>
|
||||
#include <llvm-c/Core.h>
|
||||
#include <llvm-c/Types.h>
|
||||
|
@ -7,16 +9,14 @@
|
|||
#include <set/types.h>
|
||||
#include <sys/log.h>
|
||||
|
||||
#include "expr.h"
|
||||
|
||||
BackendError impl_global_declaration(LLVMBackendCompileUnit* unit,
|
||||
LLVMGlobalScope* scope,
|
||||
VariableDeclaration* decl,
|
||||
const char* name) {
|
||||
DEBUG("implementing global declaration: %s", name);
|
||||
BackendError err = SUCCESS;
|
||||
BackendError err = SUCCESS;
|
||||
LLVMTypeRef llvm_type = NULL;
|
||||
err = get_type_impl(unit, scope, decl->type, &llvm_type);
|
||||
err = get_type_impl(unit, scope, decl->type, &llvm_type);
|
||||
|
||||
if (err.kind != Success) {
|
||||
return err;
|
||||
|
@ -31,7 +31,7 @@ BackendError impl_global_declaration(LLVMBackendCompileUnit* unit,
|
|||
if (err.kind == Success) {
|
||||
DEBUG("setting default value...");
|
||||
LLVMSetInitializer(global, initial_value);
|
||||
g_hash_table_insert(scope->variables, (gpointer)name, global);
|
||||
g_hash_table_insert(scope->variables, (gpointer) name, global);
|
||||
} else {
|
||||
ERROR("unable to initialize global variable: %s", err.impl.message);
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ BackendError impl_global_definiton(LLVMBackendCompileUnit* unit,
|
|||
LLVMGlobalScope* scope,
|
||||
VariableDefiniton* def, const char* name) {
|
||||
DEBUG("implementing global definition: %s", name);
|
||||
BackendError err = SUCCESS;
|
||||
BackendError err = SUCCESS;
|
||||
LLVMTypeRef llvm_type = NULL;
|
||||
err = get_type_impl(unit, scope, def->declaration.type, &llvm_type);
|
||||
|
||||
|
@ -56,12 +56,13 @@ BackendError impl_global_definiton(LLVMBackendCompileUnit* unit,
|
|||
|
||||
// FIXME: resolve initializer expression!
|
||||
LLVMValueRef initial_value = NULL;
|
||||
err = get_const_type_value(unit, scope, &def->initializer->impl.constant, &initial_value);
|
||||
err = get_const_type_value(unit, scope, &def->initializer->impl.constant,
|
||||
&initial_value);
|
||||
|
||||
if (err.kind == Success) {
|
||||
DEBUG("setting default value");
|
||||
LLVMSetInitializer(global, initial_value);
|
||||
g_hash_table_insert(scope->variables, (gpointer)name, global);
|
||||
g_hash_table_insert(scope->variables, (gpointer) name, global);
|
||||
}
|
||||
|
||||
return err;
|
||||
|
@ -76,7 +77,7 @@ BackendError impl_global_variable(LLVMBackendCompileUnit* unit,
|
|||
switch (gemstone_var->kind) {
|
||||
case VariableKindDeclaration:
|
||||
err = impl_global_declaration(
|
||||
unit, scope, &gemstone_var->impl.declaration, alias);
|
||||
unit, scope, &gemstone_var->impl.declaration, alias);
|
||||
break;
|
||||
case VariableKindDefinition:
|
||||
err = impl_global_definiton(unit, scope,
|
||||
|
@ -108,7 +109,7 @@ BackendError impl_global_variables(LLVMBackendCompileUnit* unit,
|
|||
size_t variable_count = 0;
|
||||
while (g_hash_table_iter_next(&iterator, &key, &val) != FALSE) {
|
||||
err =
|
||||
impl_global_variable(unit, (Variable*)val, (const char*)key, scope);
|
||||
impl_global_variable(unit, (Variable*) val, (const char*) key, scope);
|
||||
|
||||
if (err.kind != Success) {
|
||||
break;
|
||||
|
|
|
@ -14,4 +14,4 @@ BackendError impl_global_variables(LLVMBackendCompileUnit* unit,
|
|||
|
||||
LLVMValueRef get_global_variable(LLVMGlobalScope* scope, char* name);
|
||||
|
||||
#endif // LLVM_BACKEND_VARIABLES_H_
|
||||
#endif // LLVM_BACKEND_VARIABLES_H_
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
|
||||
#include <codegen/backend.h>
|
||||
#include <llvm-c/Analysis.h>
|
||||
#include <llvm-c/Core.h>
|
||||
#include <llvm-c/Target.h>
|
||||
#include <llvm-c/TargetMachine.h>
|
||||
#include <llvm-c/Types.h>
|
||||
#include <llvm-c/Analysis.h>
|
||||
#include <llvm/backend.h>
|
||||
#include <llvm/parser.h>
|
||||
#include <llvm/link/lld.h>
|
||||
#include <llvm/llvm-ir/func.h>
|
||||
#include <llvm/llvm-ir/types.h>
|
||||
#include <llvm/llvm-ir/variables.h>
|
||||
#include <llvm/llvm-ir/func.h>
|
||||
#include <llvm/parser.h>
|
||||
#include <set/types.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/log.h>
|
||||
#include <llvm/link/lld.h>
|
||||
|
||||
BackendError export_IR(LLVMBackendCompileUnit* unit, const Target* target,
|
||||
const TargetConfig* config) {
|
||||
|
@ -28,7 +28,8 @@ BackendError export_IR(LLVMBackendCompileUnit* unit, const Target* target,
|
|||
|
||||
char* basename = g_strjoin(".", target->name.str, "ll", NULL);
|
||||
// construct file name
|
||||
const char* filename = g_build_filename(config->archive_directory, basename, NULL);
|
||||
const char* filename =
|
||||
g_build_filename(config->archive_directory, basename, NULL);
|
||||
|
||||
INFO("Writing LLVM-IR to %s", filename);
|
||||
|
||||
|
@ -51,7 +52,7 @@ BackendError export_IR(LLVMBackendCompileUnit* unit, const Target* target,
|
|||
|
||||
INFO("%ld bytes written to %s", bytes, filename);
|
||||
|
||||
g_free((char*)filename);
|
||||
g_free((char*) filename);
|
||||
g_free(basename);
|
||||
|
||||
// clean up LLVM-IR string
|
||||
|
@ -72,11 +73,13 @@ BackendError emit_module_to_file(LLVMBackendCompileUnit* unit,
|
|||
switch (file_type) {
|
||||
case LLVMAssemblyFile:
|
||||
basename = g_strjoin(".", config->name, "s", NULL);
|
||||
filename = g_build_filename(config->archive_directory, basename, NULL);
|
||||
filename =
|
||||
g_build_filename(config->archive_directory, basename, NULL);
|
||||
break;
|
||||
case LLVMObjectFile:
|
||||
basename = g_strjoin(".", config->name, "o", NULL);
|
||||
filename = g_build_filename(config->archive_directory, basename, NULL);
|
||||
filename =
|
||||
g_build_filename(config->archive_directory, basename, NULL);
|
||||
break;
|
||||
default:
|
||||
return new_backend_impl_error(Implementation, NULL,
|
||||
|
@ -86,10 +89,11 @@ BackendError emit_module_to_file(LLVMBackendCompileUnit* unit,
|
|||
INFO("export to file: %s", filename);
|
||||
|
||||
if (LLVMTargetMachineEmitToFile(target_machine, unit->module, filename,
|
||||
file_type, &error) != 0) {
|
||||
file_type, &error)
|
||||
!= 0) {
|
||||
ERROR("failed to emit code: %s", error);
|
||||
err =
|
||||
new_backend_impl_error(Implementation, NULL, "failed to emit code");
|
||||
new_backend_impl_error(Implementation, NULL, "failed to emit code");
|
||||
|
||||
} else {
|
||||
print_message(Info, "Generated code was written to: %s", filename);
|
||||
|
@ -110,7 +114,7 @@ BackendError export_object(LLVMBackendCompileUnit* unit, const Target* target,
|
|||
target->triple.str, target->features.str);
|
||||
|
||||
LLVMTargetRef llvm_target = NULL;
|
||||
char* error = NULL;
|
||||
char* error = NULL;
|
||||
|
||||
LLVMInitializeAllTargets();
|
||||
LLVMInitializeAllTargetInfos();
|
||||
|
@ -120,8 +124,8 @@ BackendError export_object(LLVMBackendCompileUnit* unit, const Target* target,
|
|||
LLVMInitializeAllAsmPrinters();
|
||||
|
||||
DEBUG("creating target...");
|
||||
if (LLVMGetTargetFromTriple(target->triple.str, &llvm_target, &error) !=
|
||||
0) {
|
||||
if (LLVMGetTargetFromTriple(target->triple.str, &llvm_target, &error)
|
||||
!= 0) {
|
||||
ERROR("failed to create target machine: %s", error);
|
||||
err = new_backend_impl_error(Implementation, NULL,
|
||||
"unable to create target machine");
|
||||
|
@ -131,8 +135,8 @@ BackendError export_object(LLVMBackendCompileUnit* unit, const Target* target,
|
|||
|
||||
DEBUG("Creating target machine...");
|
||||
LLVMTargetMachineRef target_machine = LLVMCreateTargetMachine(
|
||||
llvm_target, target->triple.str, target->cpu.str, target->features.str,
|
||||
target->opt, target->reloc, target->model);
|
||||
llvm_target, target->triple.str, target->cpu.str, target->features.str,
|
||||
target->opt, target->reloc, target->model);
|
||||
|
||||
print_message(Info, "Generating code for: %s", target->triple.str);
|
||||
|
||||
|
@ -145,8 +149,8 @@ BackendError export_object(LLVMBackendCompileUnit* unit, const Target* target,
|
|||
return err;
|
||||
}
|
||||
|
||||
err = emit_module_to_file(unit, target_machine, LLVMObjectFile, error,
|
||||
config);
|
||||
err =
|
||||
emit_module_to_file(unit, target_machine, LLVMObjectFile, error, config);
|
||||
|
||||
LLVMDisposeTargetMachine(target_machine);
|
||||
|
||||
|
@ -226,7 +230,8 @@ static BackendError build_module(LLVMBackendCompileUnit* unit,
|
|||
char* error = NULL;
|
||||
if (LLVMVerifyModule(unit->module, LLVMReturnStatusAction, &error)) {
|
||||
print_message(Error, "Unable to compile due to: %s", error);
|
||||
err = new_backend_impl_error(Implementation, NULL, "LLVM backend verification error, see stdout");
|
||||
err = new_backend_impl_error(
|
||||
Implementation, NULL, "LLVM backend verification error, see stdout");
|
||||
}
|
||||
LLVMDisposeMessage(error);
|
||||
|
||||
|
@ -246,7 +251,7 @@ BackendError parse_module(const Module* module, const TargetConfig* config) {
|
|||
DEBUG("creating LLVM context and module");
|
||||
unit->context = LLVMContextCreate();
|
||||
unit->module =
|
||||
LLVMModuleCreateWithNameInContext(config->root_module, unit->context);
|
||||
LLVMModuleCreateWithNameInContext(config->root_module, unit->context);
|
||||
|
||||
LLVMGlobalScope* global_scope = new_global_scope(module);
|
||||
|
||||
|
@ -260,14 +265,16 @@ BackendError parse_module(const Module* module, const TargetConfig* config) {
|
|||
err = export_module(unit, &target, config);
|
||||
if (err.kind == Success) {
|
||||
if (config->mode == Application) {
|
||||
TargetLinkConfig* link_config = lld_create_link_config(&target, config, module);
|
||||
TargetLinkConfig* link_config =
|
||||
lld_create_link_config(&target, config, module);
|
||||
|
||||
if (link_config != NULL) {
|
||||
err = lld_link_target(link_config);
|
||||
|
||||
lld_delete_link_config(link_config);
|
||||
} else {
|
||||
err = new_backend_impl_error(Implementation, NULL, "libclang error");
|
||||
err = new_backend_impl_error(Implementation, NULL,
|
||||
"libclang error");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -289,10 +296,10 @@ LLVMGlobalScope* new_global_scope(const Module* module) {
|
|||
DEBUG("creating global scope...");
|
||||
LLVMGlobalScope* scope = malloc(sizeof(LLVMGlobalScope));
|
||||
|
||||
scope->module = (Module*) module;
|
||||
scope->module = (Module*) module;
|
||||
scope->functions = g_hash_table_new(g_str_hash, g_str_equal);
|
||||
scope->variables = g_hash_table_new(g_str_hash, g_str_equal);
|
||||
scope->types = g_hash_table_new(g_str_hash, g_str_equal);
|
||||
scope->types = g_hash_table_new(g_str_hash, g_str_equal);
|
||||
|
||||
return scope;
|
||||
}
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
#ifndef LLVM_BACKEND_PARSE_H_
|
||||
#define LLVM_BACKEND_PARSE_H_
|
||||
|
||||
#include <set/types.h>
|
||||
#include <codegen/backend.h>
|
||||
#include <llvm-c/Types.h>
|
||||
#include <llvm-c/Core.h>
|
||||
#include <llvm-c/Types.h>
|
||||
#include <set/types.h>
|
||||
|
||||
typedef struct LLVMBackendCompileUnit_t {
|
||||
LLVMContextRef context;
|
||||
|
|
18
src/main.c
18
src/main.c
|
@ -1,25 +1,27 @@
|
|||
#include <ast/ast.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/log.h>
|
||||
#include <sys/col.h>
|
||||
#include <lex/util.h>
|
||||
#include <cfg/opt.h>
|
||||
#include <compiler.h>
|
||||
#include <lex/util.h>
|
||||
#include <link/lib.h>
|
||||
#include <llvm/parser.h>
|
||||
#include <mem/cache.h>
|
||||
#include <link/lib.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/col.h>
|
||||
#include <sys/log.h>
|
||||
|
||||
/**
|
||||
* @brief Log a debug message to inform about beginning exit procedures
|
||||
*
|
||||
*/
|
||||
void notify_exit(void) { DEBUG("Exiting gemstone..."); }
|
||||
void notify_exit(void) {
|
||||
DEBUG("Exiting gemstone...");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Run compiler setup here
|
||||
*
|
||||
*/
|
||||
void setup(int argc, char *argv[]) {
|
||||
void setup(int argc, char* argv[]) {
|
||||
mem_init();
|
||||
|
||||
// setup preample
|
||||
|
@ -46,7 +48,7 @@ void setup(int argc, char *argv[]) {
|
|||
DEBUG("finished starting up gemstone...");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int main(int argc, char* argv[]) {
|
||||
if (argc <= 1) {
|
||||
print_help();
|
||||
exit(1);
|
||||
|
|
136
src/mem/cache.c
136
src/mem/cache.c
|
@ -2,12 +2,12 @@
|
|||
// Created by servostar on 6/5/24.
|
||||
//
|
||||
|
||||
#include <mem/cache.h>
|
||||
#include <sys/log.h>
|
||||
#include <glib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <cfg/opt.h>
|
||||
#include <glib.h>
|
||||
#include <mem/cache.h>
|
||||
#include <string.h>
|
||||
#include <sys/log.h>
|
||||
|
||||
static GHashTable* namespaces = NULL;
|
||||
|
||||
|
@ -39,16 +39,25 @@ typedef struct MemoryNamespace_t {
|
|||
|
||||
typedef MemoryNamespace* MemoryNamespaceRef;
|
||||
|
||||
static void namespace_statistics_print(MemoryNamespaceStatistic* memoryNamespaceStatistic, char* name) {
|
||||
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(" 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");
|
||||
}
|
||||
|
||||
|
@ -58,14 +67,14 @@ static void* namespace_malloc(MemoryNamespaceRef memoryNamespace, size_t size) {
|
|||
|
||||
MemoryBlock block;
|
||||
block.block_ptr = malloc(size);
|
||||
block.kind = GenericBlock;
|
||||
block.kind = GenericBlock;
|
||||
|
||||
if (block.block_ptr == NULL) {
|
||||
memoryNamespace->statistic.faulty_allocations ++;
|
||||
memoryNamespace->statistic.faulty_allocations++;
|
||||
} else {
|
||||
g_array_append_val(memoryNamespace->blocks, block);
|
||||
|
||||
memoryNamespace->statistic.allocation_count ++;
|
||||
memoryNamespace->statistic.allocation_count++;
|
||||
memoryNamespace->statistic.bytes_allocated += size;
|
||||
}
|
||||
|
||||
|
@ -86,9 +95,11 @@ static void namespace_free_block(MemoryBlock block) {
|
|||
}
|
||||
}
|
||||
|
||||
static gboolean namespace_free(MemoryNamespaceRef memoryNamespace, void* block) {
|
||||
static gboolean namespace_free(MemoryNamespaceRef memoryNamespace,
|
||||
void* block) {
|
||||
for (guint i = 0; i < memoryNamespace->blocks->len; i++) {
|
||||
MemoryBlock current_block = g_array_index(memoryNamespace->blocks, MemoryBlock, i);
|
||||
MemoryBlock current_block =
|
||||
g_array_index(memoryNamespace->blocks, MemoryBlock, i);
|
||||
|
||||
if (current_block.block_ptr == block) {
|
||||
assert(block != NULL);
|
||||
|
@ -105,19 +116,22 @@ static gboolean namespace_free(MemoryNamespaceRef memoryNamespace, void* block)
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
static void* namespace_realloc(MemoryNamespaceRef memoryNamespace, void* block, size_t size) {
|
||||
static void* namespace_realloc(MemoryNamespaceRef memoryNamespace, void* block,
|
||||
size_t size) {
|
||||
void* reallocated_block = NULL;
|
||||
|
||||
for (guint i = 0; i < memoryNamespace->blocks->len; i++) {
|
||||
MemoryBlock current_block = g_array_index(memoryNamespace->blocks, MemoryBlock, i);
|
||||
MemoryBlock current_block =
|
||||
g_array_index(memoryNamespace->blocks, MemoryBlock, i);
|
||||
|
||||
if (current_block.block_ptr == block) {
|
||||
reallocated_block = realloc(current_block.block_ptr, size);
|
||||
|
||||
if (reallocated_block != NULL) {
|
||||
g_array_index(memoryNamespace->blocks, MemoryBlock, i).block_ptr = reallocated_block;
|
||||
g_array_index(memoryNamespace->blocks, MemoryBlock, i)
|
||||
.block_ptr = reallocated_block;
|
||||
memoryNamespace->statistic.bytes_allocated += size;
|
||||
memoryNamespace->statistic.reallocation_count ++;
|
||||
memoryNamespace->statistic.reallocation_count++;
|
||||
} else {
|
||||
memoryNamespace->statistic.faulty_reallocations++;
|
||||
}
|
||||
|
@ -137,51 +151,55 @@ static void namespace_delete(MemoryNamespaceRef memoryNamespace) {
|
|||
static void namespace_purge(MemoryNamespaceRef memoryNamespace) {
|
||||
|
||||
for (guint i = 0; i < memoryNamespace->blocks->len; i++) {
|
||||
MemoryBlock current_block = g_array_index(memoryNamespace->blocks, MemoryBlock, i);
|
||||
MemoryBlock current_block =
|
||||
g_array_index(memoryNamespace->blocks, MemoryBlock, i);
|
||||
|
||||
namespace_free_block(current_block);
|
||||
|
||||
memoryNamespace->statistic.purged_free_count ++;
|
||||
memoryNamespace->statistic.purged_free_count++;
|
||||
}
|
||||
|
||||
g_array_remove_range(memoryNamespace->blocks, 0, memoryNamespace->blocks->len);
|
||||
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(MemoryBlock));
|
||||
memoryNamespace->statistic.bytes_allocated = 0;
|
||||
memoryNamespace->statistic.allocation_count = 0;
|
||||
memoryNamespace->statistic.manual_free_count = 0;
|
||||
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;
|
||||
memoryNamespace->statistic.faulty_allocations = 0;
|
||||
memoryNamespace->statistic.purged_free_count = 0;
|
||||
memoryNamespace->statistic.reallocation_count = 0;
|
||||
|
||||
return memoryNamespace;
|
||||
}
|
||||
|
||||
GArray *namespace_new_g_array(MemoryNamespaceRef namespace, guint size) {
|
||||
GArray* namespace_new_g_array(MemoryNamespaceRef namespace, guint size) {
|
||||
MemoryBlock block;
|
||||
block.block_ptr = g_array_new(FALSE, FALSE, size);
|
||||
block.kind = GLIB_Array;
|
||||
block.kind = GLIB_Array;
|
||||
|
||||
g_array_append_val(namespace->blocks, block);
|
||||
namespace->statistic.bytes_allocated += sizeof(GArray*);
|
||||
namespace->statistic.allocation_count ++;
|
||||
namespace->statistic.allocation_count++;
|
||||
|
||||
return block.block_ptr;
|
||||
}
|
||||
|
||||
GHashTable *namespace_new_g_hash_table(MemoryNamespaceRef namespace, GHashFunc hash_func, GEqualFunc key_equal_func) {
|
||||
GHashTable* namespace_new_g_hash_table(MemoryNamespaceRef namespace,
|
||||
GHashFunc hash_func,
|
||||
GEqualFunc key_equal_func) {
|
||||
MemoryBlock block;
|
||||
block.block_ptr = g_hash_table_new(hash_func, key_equal_func);
|
||||
block.kind = GLIB_HashTable;
|
||||
block.kind = GLIB_HashTable;
|
||||
|
||||
g_array_append_val(namespace->blocks, block);
|
||||
namespace->statistic.bytes_allocated += sizeof(GHashTable*);
|
||||
namespace->statistic.allocation_count ++;
|
||||
namespace->statistic.allocation_count++;
|
||||
|
||||
return block.block_ptr;
|
||||
}
|
||||
|
@ -193,12 +211,13 @@ static void cleanup() {
|
|||
}
|
||||
|
||||
GHashTableIter iter;
|
||||
char* name = NULL;
|
||||
char* name = NULL;
|
||||
MemoryNamespaceRef memoryNamespace = NULL;
|
||||
|
||||
g_hash_table_iter_init(&iter, namespaces);
|
||||
|
||||
while (g_hash_table_iter_next(&iter, (gpointer) &name, (gpointer) &memoryNamespace)) {
|
||||
while (g_hash_table_iter_next(&iter, (gpointer) &name,
|
||||
(gpointer) &memoryNamespace)) {
|
||||
assert(name != NULL);
|
||||
assert(memoryNamespace != NULL);
|
||||
|
||||
|
@ -229,7 +248,7 @@ static MemoryNamespaceRef check_namespace(MemoryNamespaceName name) {
|
|||
}
|
||||
}
|
||||
|
||||
void *mem_alloc(MemoryNamespaceName name, size_t size) {
|
||||
void* mem_alloc(MemoryNamespaceName name, size_t size) {
|
||||
MemoryNamespaceRef cache = check_namespace(name);
|
||||
|
||||
if (cache == NULL) {
|
||||
|
@ -239,7 +258,7 @@ void *mem_alloc(MemoryNamespaceName name, size_t size) {
|
|||
return namespace_malloc(cache, size);
|
||||
}
|
||||
|
||||
void *mem_realloc(MemoryNamespaceName name, void *ptr, size_t size) {
|
||||
void* mem_realloc(MemoryNamespaceName name, void* ptr, size_t size) {
|
||||
MemoryNamespaceRef cache = check_namespace(name);
|
||||
|
||||
if (cache == NULL) {
|
||||
|
@ -249,7 +268,7 @@ void *mem_realloc(MemoryNamespaceName name, void *ptr, size_t size) {
|
|||
return namespace_realloc(cache, ptr, size);
|
||||
}
|
||||
|
||||
void mem_free_from(MemoryNamespaceName name, void *memory) {
|
||||
void mem_free_from(MemoryNamespaceName name, void* memory) {
|
||||
MemoryNamespaceRef cache = check_namespace(name);
|
||||
|
||||
namespace_free(cache, memory);
|
||||
|
@ -261,7 +280,8 @@ void mem_free(void* memory) {
|
|||
MemoryNamespaceRef memoryNamespace;
|
||||
|
||||
g_hash_table_iter_init(&iter, namespaces);
|
||||
while (g_hash_table_iter_next(&iter, (gpointer) &name, (gpointer) &memoryNamespace)) {
|
||||
while (g_hash_table_iter_next(&iter, (gpointer) &name,
|
||||
(gpointer) &memoryNamespace)) {
|
||||
|
||||
if (namespace_free(memoryNamespace, memory)) {
|
||||
break;
|
||||
|
@ -284,7 +304,7 @@ char* mem_strdup(MemoryNamespaceName name, char* string) {
|
|||
}
|
||||
|
||||
void* mem_clone(MemoryNamespaceName name, void* data, size_t size) {
|
||||
void *clone = mem_alloc(name, size);
|
||||
void* clone = mem_alloc(name, size);
|
||||
|
||||
memcpy(clone, data, size);
|
||||
|
||||
|
@ -297,31 +317,36 @@ void print_memory_statistics() {
|
|||
MemoryNamespaceRef memoryNamespace;
|
||||
|
||||
MemoryNamespaceStatistic total;
|
||||
total.bytes_allocated = 0;
|
||||
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;
|
||||
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)) {
|
||||
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.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;
|
||||
total.reallocation_count +=
|
||||
memoryNamespace->statistic.reallocation_count;
|
||||
}
|
||||
|
||||
namespace_statistics_print(&total, "summary");
|
||||
|
||||
printf("Note: untracked are memory allocations from external libraries and non-gc managed components.\n");
|
||||
printf("Note: untracked are memory allocations from external libraries and "
|
||||
"non-gc managed components.\n");
|
||||
}
|
||||
|
||||
GArray* mem_new_g_array(MemoryNamespaceName name, guint element_size) {
|
||||
|
@ -334,7 +359,8 @@ GArray* mem_new_g_array(MemoryNamespaceName name, guint element_size) {
|
|||
return namespace_new_g_array(cache, element_size);
|
||||
}
|
||||
|
||||
GHashTable* mem_new_g_hash_table(MemoryNamespaceName name, GHashFunc hash_func, GEqualFunc key_equal_func) {
|
||||
GHashTable* mem_new_g_hash_table(MemoryNamespaceName name, GHashFunc hash_func,
|
||||
GEqualFunc key_equal_func) {
|
||||
MemoryNamespaceRef cache = check_namespace(name);
|
||||
|
||||
if (cache == NULL) {
|
||||
|
|
|
@ -5,21 +5,21 @@
|
|||
#ifndef GEMSTONE_CACHE_H
|
||||
#define GEMSTONE_CACHE_H
|
||||
|
||||
#include <glib.h>
|
||||
#include <mem/cache.h>
|
||||
#include <stddef.h>
|
||||
#include <glib.h>
|
||||
|
||||
typedef char* MemoryNamespaceName;
|
||||
|
||||
#define MemoryNamespaceAst "AST"
|
||||
#define MemoryNamespaceLex "Lexer"
|
||||
#define MemoryNamespaceLog "Logging"
|
||||
#define MemoryNamespaceOpt "Options"
|
||||
#define MemoryNamespaceTOML "TOML"
|
||||
#define MemoryNamespaceSet "SET"
|
||||
#define MemoryNamespaceLlvm "LLVM"
|
||||
#define MemoryNamespaceLld "LLD"
|
||||
#define MemoryNamespaceIo "I/O"
|
||||
#define MemoryNamespaceAst "AST"
|
||||
#define MemoryNamespaceLex "Lexer"
|
||||
#define MemoryNamespaceLog "Logging"
|
||||
#define MemoryNamespaceOpt "Options"
|
||||
#define MemoryNamespaceTOML "TOML"
|
||||
#define MemoryNamespaceSet "SET"
|
||||
#define MemoryNamespaceLlvm "LLVM"
|
||||
#define MemoryNamespaceLld "LLD"
|
||||
#define MemoryNamespaceIo "I/O"
|
||||
#define MemoryNamespaceStatic "Static"
|
||||
|
||||
/**
|
||||
|
@ -44,7 +44,7 @@ void* mem_alloc(MemoryNamespaceName name, size_t size);
|
|||
* @param size
|
||||
* @return pointer to the block
|
||||
*/
|
||||
void* mem_realloc(MemoryNamespaceName name, void *ptr, size_t size);
|
||||
void* mem_realloc(MemoryNamespaceName name, void* ptr, size_t size);
|
||||
|
||||
/**
|
||||
* @brief Free a block of memory from a specified namespace.
|
||||
|
@ -57,8 +57,8 @@ 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.
|
||||
* @attention In case the namespace of the block is known, consider using
|
||||
* mem_free_from() to avoid unnecessary overhead.
|
||||
* @param name
|
||||
* @param memory
|
||||
*/
|
||||
|
@ -91,6 +91,7 @@ void print_memory_statistics();
|
|||
|
||||
GArray* mem_new_g_array(MemoryNamespaceName name, guint element_size);
|
||||
|
||||
GHashTable* mem_new_g_hash_table(MemoryNamespaceName name, GHashFunc hash_func, GEqualFunc key_equal_func);
|
||||
GHashTable* mem_new_g_hash_table(MemoryNamespaceName name, GHashFunc hash_func,
|
||||
GEqualFunc key_equal_func);
|
||||
|
||||
#endif //GEMSTONE_CACHE_H
|
||||
#endif // GEMSTONE_CACHE_H
|
||||
|
|
1685
src/set/set.c
1685
src/set/set.c
File diff suppressed because it is too large
Load Diff
|
@ -4,16 +4,16 @@
|
|||
#include <ast/ast.h>
|
||||
#include <set/types.h>
|
||||
|
||||
#define SEMANTIC_OK 0
|
||||
#define SEMANTIC_OK 0
|
||||
#define SEMANTIC_ERROR 1
|
||||
|
||||
// type of string literal
|
||||
extern const Type StringLiteralType;
|
||||
|
||||
Module * create_set(AST_NODE_PTR rootNodePtr );
|
||||
Module* create_set(AST_NODE_PTR rootNodePtr);
|
||||
|
||||
void delete_set(Module* module);
|
||||
|
||||
bool compareTypes(Type *leftType, Type *rightType);
|
||||
bool compareTypes(Type* leftType, Type* rightType);
|
||||
|
||||
#endif
|
||||
|
|
131
src/set/types.h
131
src/set/types.h
|
@ -2,28 +2,28 @@
|
|||
#ifndef SET_TYPES_H_
|
||||
#define SET_TYPES_H_
|
||||
|
||||
#include <glib.h>
|
||||
#include <ast/ast.h>
|
||||
#include <glib.h>
|
||||
|
||||
// with of primitive types (int/float) in bytes
|
||||
#define BASE_BYTES 4
|
||||
|
||||
/**
|
||||
* @brief Primitive types form the basis of all other types.
|
||||
*
|
||||
*
|
||||
*/
|
||||
typedef enum PrimitiveType_t {
|
||||
// 4 byte signed integer in two's complement
|
||||
Int =0,
|
||||
Int = 0,
|
||||
// 4 byte IEEE-754 single precision
|
||||
Float =1,
|
||||
Float = 1,
|
||||
// 4 byte encoded UTF-8 codepoint
|
||||
Char = 2,
|
||||
} PrimitiveType;
|
||||
|
||||
/**
|
||||
* @brief Represents the sign of a composite type.
|
||||
*
|
||||
*
|
||||
*/
|
||||
typedef enum Sign_t {
|
||||
// type has no sign bit
|
||||
|
@ -36,13 +36,13 @@ typedef enum Sign_t {
|
|||
* @brief Represents the scale of composite type which is multiplied
|
||||
* with the base size in order to retrieve the the composites size.
|
||||
* @attention Valid value are: { 1/8, 1/4, 1/2, 1, 2, 4, 8 }
|
||||
*
|
||||
*
|
||||
*/
|
||||
typedef double Scale;
|
||||
|
||||
/**
|
||||
* @brief A composite type is an extended definition of a primitive type.
|
||||
*
|
||||
*
|
||||
*/
|
||||
typedef struct CompositeType_t {
|
||||
// sign of composite
|
||||
|
@ -54,7 +54,7 @@ typedef struct CompositeType_t {
|
|||
|
||||
/**
|
||||
* @brief Specifies the specific type of the generic type struct.
|
||||
*
|
||||
*
|
||||
*/
|
||||
typedef enum TypeKind_t {
|
||||
TypeKindPrimitive,
|
||||
|
@ -67,8 +67,9 @@ typedef struct Type_t Type;
|
|||
|
||||
/**
|
||||
* @brief Reference points to a type.
|
||||
* @attention Can be nested. A reference can point to another reference: REF -> REF -> REF -> Primitive
|
||||
*
|
||||
* @attention Can be nested. A reference can point to another reference: REF ->
|
||||
* REF -> REF -> Primitive
|
||||
*
|
||||
*/
|
||||
typedef Type* ReferenceType;
|
||||
|
||||
|
@ -88,12 +89,12 @@ typedef struct BoxMember_t {
|
|||
|
||||
/**
|
||||
* @brief Essentially a g lorified struct
|
||||
*
|
||||
*
|
||||
*/
|
||||
typedef struct BoxType_t {
|
||||
// hashtable of members.
|
||||
// Associates the memebers name (const char*) with its type (BoxMember)
|
||||
GHashTable* member; //BoxMember Pointer
|
||||
// Associates the memebers name (const char*) with its type (BoxMember)
|
||||
GHashTable* member; // BoxMember Pointer
|
||||
AST_NODE_PTR nodePtr;
|
||||
} BoxType;
|
||||
|
||||
|
@ -101,7 +102,8 @@ typedef struct Variable_t Variable;
|
|||
|
||||
typedef struct BoxAccess_t {
|
||||
// list of recursive box accesses
|
||||
// contains a list of BoxMembers (each specifying their own type, name and box type)
|
||||
// contains a list of BoxMembers (each specifying their own type, name and
|
||||
// box type)
|
||||
GArray* member;
|
||||
// box variable to access
|
||||
Variable* variable;
|
||||
|
@ -124,19 +126,18 @@ typedef struct Type_t {
|
|||
|
||||
typedef struct Typedefine_t {
|
||||
const char* name;
|
||||
Type *type;
|
||||
Type* type;
|
||||
AST_NODE_PTR nodePtr;
|
||||
} Typedefine;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Reprents the value of type. Can be used to definitions, initialization and for expressions contants.
|
||||
*
|
||||
* @brief Reprents the value of type. Can be used to definitions, initialization
|
||||
* and for expressions contants.
|
||||
*
|
||||
*/
|
||||
typedef struct TypeValue_t {
|
||||
// the type
|
||||
Type *type;
|
||||
Type* type;
|
||||
// UTF-8 representation of the type's value
|
||||
const char* value;
|
||||
AST_NODE_PTR nodePtr;
|
||||
|
@ -148,7 +149,7 @@ typedef struct TypeValue_t {
|
|||
|
||||
/**
|
||||
* @brief Specifies a parameters I/O properties
|
||||
*
|
||||
*
|
||||
*/
|
||||
typedef enum IO_Qualifier_t {
|
||||
// Can be read from but not written to.
|
||||
|
@ -164,23 +165,23 @@ typedef enum IO_Qualifier_t {
|
|||
|
||||
/**
|
||||
* @brief A functions parameter declaration.
|
||||
*
|
||||
*
|
||||
*/
|
||||
typedef struct ParameterDeclaration_t {
|
||||
Type *type;
|
||||
Type* type;
|
||||
IO_Qualifier qualifier;
|
||||
AST_NODE_PTR nodePtr;
|
||||
} ParameterDeclaration;
|
||||
|
||||
/**
|
||||
* @brief A functions parameter.
|
||||
*
|
||||
*
|
||||
*/
|
||||
typedef struct ParameterDefinition_t {
|
||||
ParameterDeclaration declaration;
|
||||
// value to initalize the declaration with
|
||||
// NOTE: type of initializer and declaration MUST be equal
|
||||
Expression *initializer;
|
||||
Expression* initializer;
|
||||
AST_NODE_PTR nodePtr;
|
||||
} ParameterDefinition;
|
||||
|
||||
|
@ -191,18 +192,18 @@ typedef enum ParameterKind_t {
|
|||
|
||||
/**
|
||||
* @brief A parameter can either be a declaration or a definition
|
||||
*
|
||||
*
|
||||
*/
|
||||
typedef struct Parameter_t {
|
||||
const char* name;
|
||||
|
||||
|
||||
ParameterKind kind;
|
||||
union ParameterImplementation {
|
||||
ParameterDeclaration declaration;
|
||||
ParameterDefinition definiton;
|
||||
} impl;
|
||||
AST_NODE_PTR nodePtr;
|
||||
} Parameter; // fix typo
|
||||
} Parameter; // fix typo
|
||||
|
||||
typedef enum FunctionKind_t {
|
||||
FunctionDeclarationKind,
|
||||
|
@ -211,19 +212,21 @@ typedef enum FunctionKind_t {
|
|||
|
||||
typedef struct FunctionDefinition_t {
|
||||
// hashtable of parameters
|
||||
// associates a parameters name (const char*) with its parameter declaration (ParameterDeclaration)
|
||||
// associates a parameters name (const char*) with its parameter declaration
|
||||
// (ParameterDeclaration)
|
||||
GArray* parameter; // Parameter
|
||||
Type* return_value;
|
||||
AST_NODE_PTR nodePtr;
|
||||
// body of function
|
||||
Block *body;
|
||||
Block* body;
|
||||
// name of function
|
||||
const char* name;
|
||||
} FunctionDefinition;
|
||||
|
||||
typedef struct FunctionDeclaration_t {
|
||||
// hashtable of parameters
|
||||
// associates a parameters name (const char*) with its parameter declaration (ParameterDeclaration)
|
||||
// associates a parameters name (const char*) with its parameter declaration
|
||||
// (ParameterDeclaration)
|
||||
GArray* parameter; // Parameter
|
||||
AST_NODE_PTR nodePtr;
|
||||
Type* return_value;
|
||||
|
@ -237,7 +240,7 @@ typedef struct Function_t {
|
|||
FunctionDeclaration declaration;
|
||||
} impl;
|
||||
AST_NODE_PTR nodePtr;
|
||||
const char * name;
|
||||
const char* name;
|
||||
} Function;
|
||||
|
||||
Parameter get_param_from_func(Function* func, size_t index);
|
||||
|
@ -246,27 +249,24 @@ Parameter get_param_from_func(Function* func, size_t index);
|
|||
// | Variables |
|
||||
// '------------------------------------------------'
|
||||
|
||||
typedef enum StorageQualifier_t {
|
||||
Local,
|
||||
Static,
|
||||
Global
|
||||
} StorageQualifier;
|
||||
typedef enum StorageQualifier_t { Local, Static, Global } StorageQualifier;
|
||||
|
||||
typedef struct VariableDeclaration_t {
|
||||
StorageQualifier qualifier;
|
||||
Type *type;
|
||||
Type* type;
|
||||
AST_NODE_PTR nodePtr;
|
||||
} VariableDeclaration;
|
||||
|
||||
/**
|
||||
* @brief Definition of a variable
|
||||
*
|
||||
* @attention NOTE: The types of the initializer and the declaration must be equal
|
||||
*
|
||||
*
|
||||
* @attention NOTE: The types of the initializer and the declaration must be
|
||||
* equal
|
||||
*
|
||||
*/
|
||||
typedef struct VariableDefiniton_t {
|
||||
VariableDeclaration declaration;
|
||||
Expression *initializer;
|
||||
Expression* initializer;
|
||||
AST_NODE_PTR nodePtr;
|
||||
} VariableDefiniton;
|
||||
|
||||
|
@ -291,7 +291,7 @@ typedef struct Dereference_t {
|
|||
Expression* index;
|
||||
Expression* variable;
|
||||
AST_NODE_PTR nodePtr;
|
||||
}Dereference;
|
||||
} Dereference;
|
||||
|
||||
typedef struct StorageExpr_t StorageExpr;
|
||||
|
||||
|
@ -304,23 +304,24 @@ typedef struct StorageDereference_t {
|
|||
typedef struct AddressOf_t {
|
||||
Expression* variable;
|
||||
AST_NODE_PTR node_ptr;
|
||||
}AddressOf;
|
||||
} AddressOf;
|
||||
|
||||
// .------------------------------------------------.
|
||||
// | Casts |
|
||||
// '------------------------------------------------'
|
||||
|
||||
/**
|
||||
* @brief Perform a type cast, converting a value to different type whilest preserving as much of the original
|
||||
* values information.
|
||||
* @brief Perform a type cast, converting a value to different type whilest
|
||||
* preserving as much of the original values information.
|
||||
*
|
||||
* @attention NOTE: Must check wether the given value's type can be parsed into
|
||||
* the target type without loss.
|
||||
* Lossy mean possibly loosing information such when casting a float into an int (no fraction anymore).
|
||||
* Lossy mean possibly loosing information such when casting a float into
|
||||
* an int (no fraction anymore).
|
||||
*
|
||||
*/
|
||||
typedef struct TypeCast_t {
|
||||
Type *targetType;
|
||||
Type* targetType;
|
||||
Expression* operand;
|
||||
AST_NODE_PTR nodePtr;
|
||||
} TypeCast;
|
||||
|
@ -328,12 +329,12 @@ typedef struct TypeCast_t {
|
|||
/**
|
||||
* @brief Perform a reinterpret cast.
|
||||
*
|
||||
* @attention NOTE: The given value's type must have the size in bytes as the target type.
|
||||
* Transmuting a short int into a float should yield an error.
|
||||
*
|
||||
* @attention NOTE: The given value's type must have the size in bytes as the
|
||||
* target type. Transmuting a short int into a float should yield an error.
|
||||
*
|
||||
*/
|
||||
typedef struct Transmute_t {
|
||||
Type *targetType;
|
||||
Type* targetType;
|
||||
Expression* operand;
|
||||
AST_NODE_PTR nodePtr;
|
||||
} Transmute;
|
||||
|
@ -344,7 +345,7 @@ typedef struct Transmute_t {
|
|||
|
||||
/**
|
||||
* @brief Represents the arithmetic operator.
|
||||
*
|
||||
*
|
||||
*/
|
||||
typedef enum ArithmeticOperator_t {
|
||||
Add,
|
||||
|
@ -360,13 +361,9 @@ typedef enum ArithmeticOperator_t {
|
|||
|
||||
/**
|
||||
* @brief Represents the relational operator.
|
||||
*
|
||||
*
|
||||
*/
|
||||
typedef enum RelationalOperator_t {
|
||||
Equal,
|
||||
Greater,
|
||||
Less
|
||||
} RelationalOperator;
|
||||
typedef enum RelationalOperator_t { Equal, Greater, Less } RelationalOperator;
|
||||
|
||||
// .------------------------------------------------.
|
||||
// | Boolean |
|
||||
|
@ -420,11 +417,11 @@ typedef struct Operation_t {
|
|||
union OperationImplementation {
|
||||
ArithmeticOperator arithmetic;
|
||||
RelationalOperator relational;
|
||||
BooleanOperator boolean;
|
||||
BooleanOperator boolean;
|
||||
LogicalOperator logical;
|
||||
BitwiseOperator bitwise;
|
||||
} impl;
|
||||
GArray* operands; //Expression*
|
||||
GArray* operands; // Expression*
|
||||
AST_NODE_PTR nodePtr;
|
||||
} Operation;
|
||||
|
||||
|
@ -498,7 +495,7 @@ typedef struct Block_t {
|
|||
// '------------------------------------------------'
|
||||
|
||||
typedef struct While_t {
|
||||
Expression *conditon;
|
||||
Expression* conditon;
|
||||
Block block;
|
||||
AST_NODE_PTR nodePtr;
|
||||
} While;
|
||||
|
@ -508,13 +505,13 @@ typedef struct While_t {
|
|||
// '------------------------------------------------'
|
||||
|
||||
typedef struct If_t {
|
||||
Expression *conditon;
|
||||
Expression* conditon;
|
||||
Block block;
|
||||
AST_NODE_PTR nodePtr;
|
||||
} If;
|
||||
|
||||
typedef struct ElseIf_t {
|
||||
Expression *conditon;
|
||||
Expression* conditon;
|
||||
Block block;
|
||||
AST_NODE_PTR nodePtr;
|
||||
} ElseIf;
|
||||
|
@ -584,7 +581,7 @@ typedef struct Statement_t {
|
|||
While whileLoop;
|
||||
Branch branch;
|
||||
Assignment assignment;
|
||||
Variable *variable;
|
||||
Variable* variable;
|
||||
Return returnStmt;
|
||||
} impl;
|
||||
AST_NODE_PTR nodePtr;
|
||||
|
@ -595,7 +592,7 @@ typedef struct Statement_t {
|
|||
// '------------------------------------------------'
|
||||
|
||||
typedef struct Module_t {
|
||||
GHashTable* boxes; //BoxType
|
||||
GHashTable* boxes; // BoxType
|
||||
GHashTable* types; //
|
||||
GHashTable* functions;
|
||||
GHashTable* variables;
|
||||
|
@ -638,7 +635,7 @@ void delete_typecast(TypeCast* cast);
|
|||
|
||||
void delete_box_member(BoxMember* member);
|
||||
|
||||
void delete_box_type(BoxType *box_type);
|
||||
void delete_box_type(BoxType* box_type);
|
||||
|
||||
void delete_composite([[maybe_unused]] CompositeType* composite);
|
||||
|
||||
|
|
104
src/sys/col.c
104
src/sys/col.c
|
@ -1,9 +1,9 @@
|
|||
|
||||
#include <cfg/opt.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/col.h>
|
||||
#include <sys/log.h>
|
||||
#include <cfg/opt.h>
|
||||
|
||||
#ifdef __unix__
|
||||
#include <unistd.h>
|
||||
|
@ -11,47 +11,47 @@
|
|||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
char *RED;
|
||||
char *YELLOW;
|
||||
char *MAGENTA;
|
||||
char *CYAN;
|
||||
char *GREEN;
|
||||
char *RESET;
|
||||
char *BOLD;
|
||||
char *FAINT;
|
||||
char* RED;
|
||||
char* YELLOW;
|
||||
char* MAGENTA;
|
||||
char* CYAN;
|
||||
char* GREEN;
|
||||
char* RESET;
|
||||
char* BOLD;
|
||||
char* FAINT;
|
||||
|
||||
void col_init(void) {
|
||||
if (stdout_supports_ansi_esc()) {
|
||||
enable_ansi_colors();
|
||||
} else {
|
||||
disable_ansi_colors();
|
||||
}
|
||||
if (stdout_supports_ansi_esc()) {
|
||||
enable_ansi_colors();
|
||||
} else {
|
||||
disable_ansi_colors();
|
||||
}
|
||||
}
|
||||
|
||||
void disable_ansi_colors() {
|
||||
DEBUG("disabling ANSI escape codes");
|
||||
DEBUG("disabling ANSI escape codes");
|
||||
|
||||
RED = "";
|
||||
YELLOW = "";
|
||||
MAGENTA = "";
|
||||
CYAN = "";
|
||||
GREEN = "";
|
||||
RESET = "";
|
||||
BOLD = "";
|
||||
FAINT = "";
|
||||
RED = "";
|
||||
YELLOW = "";
|
||||
MAGENTA = "";
|
||||
CYAN = "";
|
||||
GREEN = "";
|
||||
RESET = "";
|
||||
BOLD = "";
|
||||
FAINT = "";
|
||||
}
|
||||
|
||||
void enable_ansi_colors() {
|
||||
DEBUG("enabling ANSI escape codes");
|
||||
DEBUG("enabling ANSI escape codes");
|
||||
|
||||
RED = "\x1b[31m";
|
||||
YELLOW = "\x1b[33m";
|
||||
MAGENTA = "\x1b[35m";
|
||||
CYAN = "\x1b[36m";
|
||||
GREEN = "\x1b[32m";
|
||||
RESET = "\x1b[0m";
|
||||
BOLD = "\x1b[1m";
|
||||
FAINT = "\x1b[2m";
|
||||
RED = "\x1b[31m";
|
||||
YELLOW = "\x1b[33m";
|
||||
MAGENTA = "\x1b[35m";
|
||||
CYAN = "\x1b[36m";
|
||||
GREEN = "\x1b[32m";
|
||||
RESET = "\x1b[0m";
|
||||
BOLD = "\x1b[1m";
|
||||
FAINT = "\x1b[2m";
|
||||
}
|
||||
|
||||
int stdout_supports_ansi_esc() {
|
||||
|
@ -61,31 +61,33 @@ int stdout_supports_ansi_esc() {
|
|||
}
|
||||
|
||||
#ifdef __unix__
|
||||
// check if TTY
|
||||
if (isatty(STDOUT_FILENO)) {
|
||||
const char *colors = getenv("COLORTERM");
|
||||
// check if colors are set and allowed
|
||||
if (colors != NULL && (strcmp(colors, "truecolor") == 0 || strcmp(colors, "24bit") == 0)) {
|
||||
return ANSI_ENABLED;
|
||||
// check if TTY
|
||||
if (isatty(STDOUT_FILENO)) {
|
||||
const char* colors = getenv("COLORTERM");
|
||||
// check if colors are set and allowed
|
||||
if (colors != NULL
|
||||
&& (strcmp(colors, "truecolor") == 0
|
||||
|| strcmp(colors, "24bit") == 0)) {
|
||||
return ANSI_ENABLED;
|
||||
}
|
||||
}
|
||||
}
|
||||
#elif defined(_WIN32) || defined(WIN32)
|
||||
// see:
|
||||
// https://stackoverflow.com/questions/63913005/how-to-test-if-console-supports-ansi-color-codes
|
||||
DWORD mode;
|
||||
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
// see:
|
||||
// https://stackoverflow.com/questions/63913005/how-to-test-if-console-supports-ansi-color-codes
|
||||
DWORD mode;
|
||||
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
|
||||
if (!GetConsoleMode(hConsole, &mode)) {
|
||||
return ASNI_DISABLED;
|
||||
}
|
||||
if (!GetConsoleMode(hConsole, &mode)) {
|
||||
return ASNI_DISABLED;
|
||||
}
|
||||
|
||||
if ((mode & ENABLE_VIRTUAL_TERMINAL_INPUT) |
|
||||
(mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING)) {
|
||||
return ANSI_ENABLED;
|
||||
}
|
||||
if ((mode & ENABLE_VIRTUAL_TERMINAL_INPUT)
|
||||
| (mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING)) {
|
||||
return ANSI_ENABLED;
|
||||
}
|
||||
#else
|
||||
#warning "unsupported platform, ASNI escape codes disabled by default"
|
||||
#endif
|
||||
|
||||
return ASNI_DISABLED;
|
||||
return ASNI_DISABLED;
|
||||
}
|
||||
|
|
|
@ -2,19 +2,19 @@
|
|||
#ifndef COLORS_H_
|
||||
#define COLORS_H_
|
||||
|
||||
#define ANSI_ENABLED 1
|
||||
#define ANSI_ENABLED 1
|
||||
#define ASNI_DISABLED 0
|
||||
|
||||
// Common escape codes
|
||||
// can be used to print colored text
|
||||
extern char *RED;
|
||||
extern char *YELLOW;
|
||||
extern char *MAGENTA;
|
||||
extern char *CYAN;
|
||||
extern char *GREEN;
|
||||
extern char *RESET;
|
||||
extern char *BOLD;
|
||||
extern char *FAINT;
|
||||
extern char* RED;
|
||||
extern char* YELLOW;
|
||||
extern char* MAGENTA;
|
||||
extern char* CYAN;
|
||||
extern char* GREEN;
|
||||
extern char* RESET;
|
||||
extern char* BOLD;
|
||||
extern char* FAINT;
|
||||
|
||||
/**
|
||||
* @brief Initialize global state
|
||||
|
@ -28,14 +28,17 @@ void col_init(void);
|
|||
void enable_ansi_colors();
|
||||
|
||||
/**
|
||||
* @brief Disable ANSI escape codes. This will set all the above global strings to be empty.
|
||||
* @brief Disable ANSI escape codes. This will set all the above global strings
|
||||
* to be empty.
|
||||
*/
|
||||
void disable_ansi_colors();
|
||||
|
||||
/**
|
||||
* @brief Check if stdout may support ANSI escape codes.
|
||||
* @attention This function may report escape codes to be unavailable even if they actually are.
|
||||
* @return ANSI_ENABLED if escape sequences are supported ASNI_DISABLED otherwise
|
||||
* @attention This function may report escape codes to be unavailable even if
|
||||
* they actually are.
|
||||
* @return ANSI_ENABLED if escape sequences are supported ASNI_DISABLED
|
||||
* otherwise
|
||||
*/
|
||||
[[nodiscard]]
|
||||
int stdout_supports_ansi_esc();
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
|
||||
#include <assert.h>
|
||||
#include <cfg/opt.h>
|
||||
#include <mem/cache.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/log.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <cfg/opt.h>
|
||||
#include <mem/cache.h>
|
||||
#include <sys/log.h>
|
||||
|
||||
static struct Logger_t {
|
||||
FILE** streams;
|
||||
|
@ -15,13 +15,11 @@ static struct Logger_t {
|
|||
|
||||
int runtime_log_level = LOG_LEVEL_WARNING;
|
||||
|
||||
void set_log_level(int level)
|
||||
{
|
||||
void set_log_level(int level) {
|
||||
runtime_log_level = level;
|
||||
}
|
||||
|
||||
void log_init()
|
||||
{
|
||||
void log_init() {
|
||||
if (is_option_set("verbose")) {
|
||||
set_log_level(LOG_LEVEL_INFORMATION);
|
||||
} else if (is_option_set("debug")) {
|
||||
|
@ -32,30 +30,26 @@ void log_init()
|
|||
log_register_stream(LOG_DEFAULT_STREAM);
|
||||
}
|
||||
|
||||
void log_register_stream(FILE* restrict stream)
|
||||
{
|
||||
void log_register_stream(FILE* restrict stream) {
|
||||
// replace runtime check with assertion
|
||||
// only to be used in debug target
|
||||
assert(stream != NULL);
|
||||
|
||||
if (GlobalLogger.stream_count == 0)
|
||||
{
|
||||
GlobalLogger.streams = (FILE**) mem_alloc(MemoryNamespaceLog, sizeof(FILE*));
|
||||
if (GlobalLogger.stream_count == 0) {
|
||||
GlobalLogger.streams =
|
||||
(FILE**) mem_alloc(MemoryNamespaceLog, sizeof(FILE*));
|
||||
GlobalLogger.stream_count = 1;
|
||||
|
||||
if (GlobalLogger.streams == NULL)
|
||||
{
|
||||
if (GlobalLogger.streams == NULL) {
|
||||
PANIC("failed to allocate stream buffer");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
GlobalLogger.stream_count++;
|
||||
size_t bytes = GlobalLogger.stream_count * sizeof(FILE*);
|
||||
GlobalLogger.streams = (FILE**) mem_realloc(MemoryNamespaceLog, GlobalLogger.streams, bytes);
|
||||
GlobalLogger.streams =
|
||||
(FILE**) mem_realloc(MemoryNamespaceLog, GlobalLogger.streams, bytes);
|
||||
|
||||
if (GlobalLogger.streams == NULL)
|
||||
{
|
||||
if (GlobalLogger.streams == NULL) {
|
||||
PANIC("failed to reallocate stream buffer");
|
||||
}
|
||||
}
|
||||
|
@ -63,32 +57,21 @@ void log_register_stream(FILE* restrict stream)
|
|||
GlobalLogger.streams[GlobalLogger.stream_count - 1] = stream;
|
||||
}
|
||||
|
||||
static void vflogf(
|
||||
FILE* restrict stream,
|
||||
const char* restrict level,
|
||||
const char* restrict file,
|
||||
const unsigned long line,
|
||||
const char* restrict func,
|
||||
const char* restrict format,
|
||||
va_list args)
|
||||
{
|
||||
static void vflogf(FILE* restrict stream, const char* restrict level,
|
||||
const char* restrict file, const unsigned long line,
|
||||
const char* restrict func, const char* restrict format,
|
||||
va_list args) {
|
||||
fprintf(stream, "%s in %s() at %s:%lu: ", level, func, file, line);
|
||||
vfprintf(stream, format, args);
|
||||
}
|
||||
|
||||
void syslog_logf(
|
||||
const char* restrict level,
|
||||
const char* restrict file,
|
||||
const unsigned long line,
|
||||
const char* restrict func,
|
||||
const char* restrict format,
|
||||
...)
|
||||
{
|
||||
void syslog_logf(const char* restrict level, const char* restrict file,
|
||||
const unsigned long line, const char* restrict func,
|
||||
const char* restrict format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
|
||||
for (size_t i = 0; i < GlobalLogger.stream_count; i++)
|
||||
{
|
||||
for (size_t i = 0; i < GlobalLogger.stream_count; i++) {
|
||||
FILE* stream = GlobalLogger.streams[i];
|
||||
|
||||
vflogf(stream, level, file, line, func, format, args);
|
||||
|
@ -97,13 +80,9 @@ void syslog_logf(
|
|||
va_end(args);
|
||||
}
|
||||
|
||||
void syslog_panicf(
|
||||
const char* restrict file,
|
||||
const unsigned long line,
|
||||
const char* restrict func,
|
||||
const char* restrict format,
|
||||
...)
|
||||
{
|
||||
void syslog_panicf(const char* restrict file, const unsigned long line,
|
||||
const char* restrict func, const char* restrict format,
|
||||
...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
|
||||
|
@ -114,13 +93,9 @@ void syslog_panicf(
|
|||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
void syslog_fatalf(
|
||||
const char* restrict file,
|
||||
const unsigned long line,
|
||||
const char* restrict func,
|
||||
const char* restrict format,
|
||||
...)
|
||||
{
|
||||
void syslog_fatalf(const char* restrict file, const unsigned long line,
|
||||
const char* restrict func, const char* restrict format,
|
||||
...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
|
||||
|
|
122
src/sys/log.h
122
src/sys/log.h
|
@ -5,10 +5,10 @@
|
|||
|
||||
#define LOG_DEFAULT_STREAM stderr
|
||||
|
||||
#define LOG_LEVEL_ERROR 3
|
||||
#define LOG_LEVEL_WARNING 2
|
||||
#define LOG_LEVEL_INFORMATION 1
|
||||
#define LOG_LEVEL_DEBUG 0
|
||||
#define LOG_LEVEL_ERROR 3
|
||||
#define LOG_LEVEL_WARNING 2
|
||||
#define LOG_LEVEL_INFORMATION 1
|
||||
#define LOG_LEVEL_DEBUG 0
|
||||
|
||||
#define LOG_LEVEL LOG_LEVEL_DEBUG
|
||||
|
||||
|
@ -23,9 +23,11 @@
|
|||
// generally not defined by GCC < 11.3 and MSVC
|
||||
#ifndef __FILE_NAME__
|
||||
#if defined(_WIN32) || defined(_WIN64) || defined(_MSC_VER)
|
||||
#define __FILE_NAME__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__)
|
||||
#define __FILE_NAME__ \
|
||||
(strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__)
|
||||
#else
|
||||
#define __FILE_NAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
|
||||
#define __FILE_NAME__ \
|
||||
(strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
@ -34,106 +36,104 @@
|
|||
* This macro will print debug information to stderr and call abort() to
|
||||
* performa a ungracefull exit. No clean up possible.
|
||||
*/
|
||||
#define PANIC(format, ...) syslog_panicf(__FILE_NAME__, __LINE__, __func__, format"\n", ##__VA_ARGS__)
|
||||
#define PANIC(format, ...) \
|
||||
syslog_panicf(__FILE_NAME__, __LINE__, __func__, format "\n", ##__VA_ARGS__)
|
||||
|
||||
/**
|
||||
* @brief Panic is used in cases where the process is in an invalid or undefined state.
|
||||
* This macro will print debug information to stderr and call exit() to
|
||||
* initiate a gracefull exit, giving the process the opportunity to clean up.
|
||||
* @brief Panic is used in cases where the process is in an invalid or undefined
|
||||
* state. This macro will print debug information to stderr and call exit() to
|
||||
* initiate a gracefull exit, giving the process the opportunity to clean
|
||||
* up.
|
||||
*/
|
||||
#define FATAL(format, ...) syslog_fatalf(__FILE_NAME__, __LINE__, __func__, format"\n", ##__VA_ARGS__)
|
||||
#define FATAL(format, ...) \
|
||||
syslog_fatalf(__FILE_NAME__, __LINE__, __func__, format "\n", ##__VA_ARGS__)
|
||||
|
||||
/*
|
||||
Standard log macros. These will not terminate the application.
|
||||
Can be turned off by setting LOG_LEVEL. All logs which have smaller log numbers
|
||||
will not print.
|
||||
*/
|
||||
#define ERROR(format, ...) __LOG(LOG_STRING_ERROR, LOG_LEVEL_ERROR, format"\n", ##__VA_ARGS__)
|
||||
#define WARN(format, ...) __LOG(LOG_STRING_WARNING, LOG_LEVEL_WARNING, format"\n", ##__VA_ARGS__)
|
||||
#define INFO(format, ...) __LOG(LOG_STRING_INFORMATION, LOG_LEVEL_INFORMATION, format"\n", ##__VA_ARGS__)
|
||||
#define DEBUG(format, ...) __LOG(LOG_STRING_DEBUG, LOG_LEVEL_DEBUG, format"\n", ##__VA_ARGS__)
|
||||
#define ERROR(format, ...) \
|
||||
__LOG(LOG_STRING_ERROR, LOG_LEVEL_ERROR, format "\n", ##__VA_ARGS__)
|
||||
#define WARN(format, ...) \
|
||||
__LOG(LOG_STRING_WARNING, LOG_LEVEL_WARNING, format "\n", ##__VA_ARGS__)
|
||||
#define INFO(format, ...) \
|
||||
__LOG(LOG_STRING_INFORMATION, LOG_LEVEL_INFORMATION, format "\n", \
|
||||
##__VA_ARGS__)
|
||||
#define DEBUG(format, ...) \
|
||||
__LOG(LOG_STRING_DEBUG, LOG_LEVEL_DEBUG, format "\n", ##__VA_ARGS__)
|
||||
|
||||
extern int runtime_log_level;
|
||||
|
||||
#define __LOG(level, priority, format, ...) \
|
||||
do { \
|
||||
if (LOG_LEVEL <= priority) \
|
||||
if (runtime_log_level <= priority) \
|
||||
syslog_logf(level, __FILE_NAME__, __LINE__, __func__, format, ##__VA_ARGS__); \
|
||||
} while(0)
|
||||
#define __LOG(level, priority, format, ...) \
|
||||
do { \
|
||||
if (LOG_LEVEL <= priority) \
|
||||
if (runtime_log_level <= priority) \
|
||||
syslog_logf(level, __FILE_NAME__, __LINE__, __func__, format, \
|
||||
##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* @brief Set the runtime log level. Must be one of: LOG_LEVEL_ERROR, LOG_LEVEL_WARNING,
|
||||
* LOG_LEVEL_INFORMATION, LOG_LEVEL_DEBUG
|
||||
* @brief Set the runtime log level. Must be one of: LOG_LEVEL_ERROR,
|
||||
* LOG_LEVEL_WARNING, LOG_LEVEL_INFORMATION, LOG_LEVEL_DEBUG
|
||||
* @param level the new log level
|
||||
*/
|
||||
void set_log_level(int level);
|
||||
|
||||
/**
|
||||
* @brief Log a message into all registered streams
|
||||
*
|
||||
*
|
||||
* @param level of the message
|
||||
* @param file origin of the message cause
|
||||
* @param line line in which log call was made
|
||||
* @param func function the log call was done in
|
||||
* @param func function the log call was done in
|
||||
* @param format the format to print following args in
|
||||
* @param ...
|
||||
* @param ...
|
||||
*/
|
||||
[[gnu::nonnull(1), gnu::nonnull(2), gnu::nonnull(4)]]
|
||||
void syslog_logf(
|
||||
const char* restrict level,
|
||||
const char* restrict file,
|
||||
unsigned long line,
|
||||
const char* restrict func,
|
||||
const char* restrict format,
|
||||
...);
|
||||
void syslog_logf(const char* restrict level, const char* restrict file,
|
||||
unsigned long line, const char* restrict func,
|
||||
const char* restrict format, ...);
|
||||
|
||||
/**
|
||||
* @brief Log a panic message to stderr and perform gracefull crash with exit() denoting a failure
|
||||
*
|
||||
* @brief Log a panic message to stderr and perform gracefull crash with exit()
|
||||
* denoting a failure
|
||||
*
|
||||
* @param file origin of the message cause
|
||||
* @param line line in which log call was made
|
||||
* @param func function the log call was done in
|
||||
* @param func function the log call was done in
|
||||
* @param format the format to print following args in
|
||||
* @param ...
|
||||
* @param ...
|
||||
*/
|
||||
[[noreturn]]
|
||||
[[gnu::nonnull(1), gnu::nonnull(3), gnu::nonnull(4)]]
|
||||
void syslog_panicf(
|
||||
const char* restrict file,
|
||||
unsigned long line,
|
||||
const char* restrict func,
|
||||
const char* restrict format,
|
||||
...);
|
||||
[[noreturn]] [[gnu::nonnull(1), gnu::nonnull(3), gnu::nonnull(4)]]
|
||||
void syslog_panicf(const char* restrict file, unsigned long line,
|
||||
const char* restrict func, const char* restrict format, ...);
|
||||
|
||||
/**
|
||||
* @brief Log a critical message to stderr and perform ungracefull crash with abort()
|
||||
*
|
||||
* @brief Log a critical message to stderr and perform ungracefull crash with
|
||||
* abort()
|
||||
*
|
||||
* @param file origin of the message cause
|
||||
* @param line line in which log call was made
|
||||
* @param func function the log call was done in
|
||||
* @param func function the log call was done in
|
||||
* @param format the format to print following args in
|
||||
* @param ...
|
||||
* @param ...
|
||||
*/
|
||||
[[noreturn]]
|
||||
[[gnu::nonnull(1), gnu::nonnull(3), gnu::nonnull(4)]]
|
||||
void syslog_fatalf(
|
||||
const char* restrict file,
|
||||
unsigned long line,
|
||||
const char* restrict func,
|
||||
const char* restrict format,
|
||||
...);
|
||||
[[noreturn]] [[gnu::nonnull(1), gnu::nonnull(3), gnu::nonnull(4)]]
|
||||
void syslog_fatalf(const char* restrict file, unsigned long line,
|
||||
const char* restrict func, const char* restrict format, ...);
|
||||
|
||||
/**
|
||||
* @brief Initialize the logger by registering stderr as stream
|
||||
*
|
||||
*
|
||||
*/
|
||||
void log_init(void);
|
||||
|
||||
/**
|
||||
* @brief Register a stream as output source. Must be freed manually at exit if necessary
|
||||
*
|
||||
* @param stream
|
||||
* @brief Register a stream as output source. Must be freed manually at exit if
|
||||
* necessary
|
||||
*
|
||||
* @param stream
|
||||
*/
|
||||
[[gnu::nonnull(1)]]
|
||||
void log_register_stream(FILE* restrict stream);
|
||||
|
|
Loading…
Reference in New Issue