diff options
| author | Kimplul <kimi.h.kuparinen@gmail.com> | 2024-04-07 01:28:45 +0300 |
|---|---|---|
| committer | Kimplul <kimi.h.kuparinen@gmail.com> | 2024-04-07 01:28:45 +0300 |
| commit | 170f3ddc3d8967c0b5d4755c0221c396db215b2f (patch) | |
| tree | 00314209cd7878b9d5b3fe6fc2e0c7297f76dec8 | |
| parent | 2bdf1f8b1856bca8091d66a7a447e6c90993c297 (diff) | |
| download | ek-170f3ddc3d8967c0b5d4755c0221c396db215b2f.tar.gz ek-170f3ddc3d8967c0b5d4755c0221c396db215b2f.zip | |
implement some initial qbt backend stuff
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | Makefile | 2 | ||||
| -rw-r--r-- | check.log | 1 | ||||
| -rw-r--r-- | include/ek/ast.h | 55 | ||||
| -rw-r--r-- | include/ek/compiler.h | 2 | ||||
| -rw-r--r-- | include/ek/lower.h | 9 | ||||
| -rw-r--r-- | include/ek/ops.h | 55 | ||||
| -rw-r--r-- | include/ek/scope.h | 6 | ||||
| -rw-r--r-- | include/ek/vec.h | 38 | ||||
| -rw-r--r-- | src/actualize.c | 325 | ||||
| -rw-r--r-- | src/asm.c | 102 | ||||
| -rw-r--r-- | src/ast.c | 199 | ||||
| -rw-r--r-- | src/compiler.c | 20 | ||||
| -rw-r--r-- | src/lower.c | 858 | ||||
| -rw-r--r-- | src/main.c | 13 | ||||
| -rw-r--r-- | src/ops.c | 487 | ||||
| -rw-r--r-- | src/parser.y | 10 | ||||
| -rw-r--r-- | src/scope.c | 15 | ||||
| -rw-r--r-- | src/vec.c | 60 | ||||
| -rw-r--r-- | tests/if.ek | 23 | ||||
| -rw-r--r-- | tests/if2.ek | 29 | ||||
| -rw-r--r-- | tests/loop.ek | 15 | ||||
| -rw-r--r-- | tests/loops.ek | 4 | ||||
| -rw-r--r-- | tests/pointer_literal.ek | 5 |
24 files changed, 1431 insertions, 903 deletions
@@ -4,5 +4,6 @@ build gen ek *.t +*.log !gen/.gitkeep !include/ek @@ -1,7 +1,7 @@ DO != echo -n > deps.mk DEBUGFLAGS != [ $(RELEASE) ] && echo "-flto=auto -O2 -DNODEBUG" || echo "-O0 -DDEBUG" -CFLAGS = -Wall -Wextra -g +CFLAGS = -Wall -Wextra -Wconversion -g DEPFLAGS = -MT $@ -MMD -MP -MF $@.d LINTFLAGS = -fsyntax-only INCLUDEFLAGS = -Iinclude diff --git a/check.log b/check.log deleted file mode 100644 index 8b13789..0000000 --- a/check.log +++ /dev/null @@ -1 +0,0 @@ - diff --git a/include/ek/ast.h b/include/ek/ast.h index 7ab03ae..f7c6201 100644 --- a/include/ek/ast.h +++ b/include/ek/ast.h @@ -296,6 +296,8 @@ enum ast_flag { AST_FLAG_SHARED = (1 << 12), /** Struct/union is generic. */ AST_FLAG_GENERIC = (1 << 13), + AST_FLAG_NOMANGLE = (1 << 14), + AST_FLAG_DOEXPR = (1 << 15), }; struct ast_node; @@ -576,7 +578,7 @@ struct ast_type { struct { enum ast_primitive type; struct ast_node *def; // for possible user defined - // member functions + // member functions } _primitive; /** Array type. */ @@ -754,7 +756,7 @@ struct ast_node { /** Ek type. */ struct ast_node *type; - size_t reg; + size_t uses; /** Data relevant to kind. */ union { @@ -859,7 +861,8 @@ struct ast_node *gen_binop(enum ast_binops op, * @param expr Expression. * @return Corresponding AST node. */ -struct ast_node *gen_unop(enum ast_unops op, struct ast_node *expr, struct src_loc loc); +struct ast_node *gen_unop(enum ast_unops op, struct ast_node *expr, + struct src_loc loc); /** * Generate call. @@ -910,7 +913,8 @@ struct ast_node *gen_float(double dbl, struct src_loc loc); * @param from Where to assign from. * @return Corresponding AST node. */ -struct ast_node *gen_assign(struct ast_node *to, struct ast_node *from, struct src_loc loc); +struct ast_node *gen_assign(struct ast_node *to, struct ast_node *from, + struct src_loc loc); /** * Generate initialization. @@ -927,7 +931,8 @@ struct ast_node *gen_init(struct ast_node *body, struct src_loc loc); * @param body Body. * @return Corresponding AST node. */ -struct ast_node *gen_while(struct ast_node *cond, struct ast_node *body, struct src_loc loc); +struct ast_node *gen_while(struct ast_node *cond, struct ast_node *body, + struct src_loc loc); /** * Generate for loop. @@ -939,7 +944,8 @@ struct ast_node *gen_while(struct ast_node *cond, struct ast_node *body, struct * @return Corresponding AST node. */ struct ast_node *gen_for(struct ast_node *pre, struct ast_node *cond, - struct ast_node *post, struct ast_node *body, struct src_loc loc); + struct ast_node *post, struct ast_node *body, + struct src_loc loc); /** * Generate return. @@ -1001,7 +1007,8 @@ struct ast_node *gen_if(struct ast_node *cond, struct ast_node *body, * @param cases List of cases. * @return Corresponding AST node. */ -struct ast_node *gen_switch(struct ast_node *cond, struct ast_node *cases, struct src_loc loc); +struct ast_node *gen_switch(struct ast_node *cond, struct ast_node *cases, + struct src_loc loc); /** * Generate switch case. @@ -1010,9 +1017,11 @@ struct ast_node *gen_switch(struct ast_node *cond, struct ast_node *cases, struc * @param body Body. * @return Corresponding AST node. */ -struct ast_node *gen_case(struct ast_node *expr, struct ast_node *body, struct src_loc loc); +struct ast_node *gen_case(struct ast_node *expr, struct ast_node *body, + struct src_loc loc); -struct ast_node *gen_primitive(enum ast_primitive type, struct ast_node *def, struct src_loc loc); +struct ast_node *gen_primitive(enum ast_primitive type, struct ast_node *def, + struct src_loc loc); /** * Generate Ek type (besides primitive). * @@ -1054,7 +1063,8 @@ struct ast_node *gen_var(struct ast_node *id, struct ast_node *type, * @return Corresponding AST node. */ struct ast_node *gen_lambda(struct ast_node *captures, - struct ast_node *type, struct ast_node *body, struct src_loc loc); + struct ast_node *type, struct ast_node *body, + struct src_loc loc); /** * Generate procedure definition. @@ -1074,7 +1084,8 @@ struct ast_node *gen_proc(struct ast_node *id, struct ast_node *type, * @param id Name to do dot with. * @return Corresponding AST node. */ -struct ast_node *gen_dot(struct ast_node *expr, struct ast_node *id, struct src_loc loc); +struct ast_node *gen_dot(struct ast_node *expr, struct ast_node *id, + struct src_loc loc); /** * Generate enum definition. @@ -1094,7 +1105,8 @@ struct ast_node *gen_enum(struct ast_node *id, struct ast_node *type, * @param val Value of enumeration member. * @return Corresponding AST node. */ -struct ast_node *gen_val(struct ast_node *id, struct ast_node *val, struct src_loc loc); +struct ast_node *gen_val(struct ast_node *id, struct ast_node *val, + struct src_loc loc); /** * Generate alias definition. @@ -1103,7 +1115,8 @@ struct ast_node *gen_val(struct ast_node *id, struct ast_node *val, struct src_l * @param type Type to alias. * @return Corresponding AST node. */ -struct ast_node *gen_alias(struct ast_node *id, struct ast_node *type, struct src_loc loc); +struct ast_node *gen_alias(struct ast_node *id, struct ast_node *type, + struct src_loc loc); /** * Generate trait definition. @@ -1113,7 +1126,8 @@ struct ast_node *gen_alias(struct ast_node *id, struct ast_node *type, struct sr * @return Corresponding AST node. */ struct ast_node *gen_trait(struct ast_node *id, struct ast_node *params, - struct ast_node *raw_body, struct ast_node *body, struct src_loc loc); + struct ast_node *raw_body, struct ast_node *body, + struct src_loc loc); /** * Generate import; @@ -1130,7 +1144,8 @@ struct ast_node *gen_import(const char *file, struct src_loc loc); * @param type Type to cast expression result to. * @return Corresponding AST node. */ -struct ast_node *gen_cast(struct ast_node *expr, struct ast_node *type, struct src_loc loc); +struct ast_node *gen_cast(struct ast_node *expr, struct ast_node *type, + struct src_loc loc); /** * Generate embed. @@ -1198,7 +1213,8 @@ struct ast_node *gen_struct(struct ast_node *id, struct ast_node *generics, * @param type Enum type to fetch from. * @return Corresponding AST node. */ -struct ast_node *gen_fetch(struct ast_node *id, struct ast_node *type, struct src_loc loc); +struct ast_node *gen_fetch(struct ast_node *id, struct ast_node *type, + struct src_loc loc); /** * Generate empty AST node. @@ -1234,6 +1250,7 @@ int identical_ast_nodes(int exact, struct ast_node *left, * @param root AST node to dump. */ void dump_ast(int depth, struct ast_node *root); +void dump_ast_node(int depth, struct ast_node *node); /** * Add \p elem to end of \p list. @@ -1267,7 +1284,7 @@ void ast_clear_flags(struct ast_node *node, enum ast_flag flags); * @param flags Flags to check. * @return \c 1 if all \p flags are set, \c 0 othewise. */ -int ast_flags(struct ast_node *node, enum ast_flag flags); +unsigned ast_flags(struct ast_node *node, enum ast_flag flags); /** * Call external callback on all nodes in tree. @@ -1281,7 +1298,7 @@ int ast_call_on(int (*call)(struct ast_node *, void *), struct ast_node *node, void *data); int ast_call_on_chain(int (*call)(struct ast_node *, void *), - struct ast_node *node, void *data); + struct ast_node *node, void *data); /** * Number of elements in AST list. @@ -1314,7 +1331,7 @@ int same_id(struct ast_node *id1, struct ast_node *id2); int equiv_nodes(struct ast_node *n1, struct ast_node *n2); int equiv_node_chains(struct ast_node *c1, struct ast_node *c2); -#define foreach_node(iter, nodes)\ +#define foreach_node(iter, nodes) \ for (struct ast_node *iter = nodes; iter; iter = iter->next) #endif /* AST_H */ diff --git a/include/ek/compiler.h b/include/ek/compiler.h index 95bd836..cbe34e2 100644 --- a/include/ek/compiler.h +++ b/include/ek/compiler.h @@ -20,7 +20,7 @@ * @param file Root file to compile. * @return \c 0 if compilation was succesful, otherwise some non-zero value. */ -int compile(const char *input, const char *output); +int compile(const char *input); /** * Process a file, i.e. lex, parse and generate raw AST. diff --git a/include/ek/lower.h b/include/ek/lower.h new file mode 100644 index 0000000..af203d6 --- /dev/null +++ b/include/ek/lower.h @@ -0,0 +1,9 @@ +#ifndef EK_LOWER_H +#define EK_LOWER_H + +#include <ek/ast.h> +#include <stdio.h> + +int lower_actuals(struct scope *root); + +#endif /* EK_OPS_H */ diff --git a/include/ek/ops.h b/include/ek/ops.h deleted file mode 100644 index a7672d7..0000000 --- a/include/ek/ops.h +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef EK_OPS_H -#define EK_OPS_H - -#include <ek/ast.h> -#include <stdio.h> - -enum loc_kind { - LOC_NONE, LOC_REG, LOC_MEM -}; - -struct loc { - enum loc_kind kind; - struct loc *next; - size_t reg; - long long off; - size_t width; -}; - -enum opcode { - /* small subset for now */ - OP_LI, - OP_LA, - OP_ADD, - OP_ADDI, - OP_STT, - OP_LDT, - OP_STW, - OP_LDW, - OP_RET, - OP_MV, /* kind of meta op, will be realized as either load/store or register move */ - OP_LABEL, - OP_COMMENT, -}; - -struct op { - enum opcode opcode; - struct loc inputs; - struct loc outputs; - size_t loc; - union { - long long constant; - const char *string; - }; - struct op *next; -}; - -struct ops { - struct op *base; - struct op *head; -}; - -int lower_ops(struct scope *root, const char *fname); -int print_asm(struct ops *ops, FILE *f); - -#endif /* EK_OPS_H */ diff --git a/include/ek/scope.h b/include/ek/scope.h index 9c4224e..4cef3a1 100644 --- a/include/ek/scope.h +++ b/include/ek/scope.h @@ -83,8 +83,8 @@ struct scope { struct scope *children; /** - * List of actualized functions, - * shared between all scopes in the file. + * List of generic structs with actual arguments to generate before + * lowering */ struct actual *actuals; @@ -125,7 +125,7 @@ void destroy_actuals(struct actual *actuals); * @param scope Scope list belongs to. * @param visible List of visibles to destroy. */ -void destroy_visible(struct scope *scope, struct visible *visible); +void destroy_visible(struct visible *visible); /** * Destroy scope. diff --git a/include/ek/vec.h b/include/ek/vec.h new file mode 100644 index 0000000..8d5cae1 --- /dev/null +++ b/include/ek/vec.h @@ -0,0 +1,38 @@ +#ifndef VEC_H +#define VEC_H + +#include <stddef.h> + +struct vec { + size_t n; + size_t s; + size_t ns; + void *buf; +}; + +struct vec vec_create(size_t s); +void vec_destroy(struct vec *v); +void vec_reset(struct vec *v); + +size_t vec_len(struct vec *v); +void *vec_at(struct vec *v, size_t i); +void *vec_back(struct vec *v); +void *vec_pop(struct vec *v); +void vec_append(struct vec *v, void *n); + +#define foreach_vec(iter, v) \ + for (size_t iter = 0, __n = vec_len(&v); iter < __n; ++iter) + +#define vect_at(type, v, i) \ + *(type *)vec_at(&v, i) + +#define vect_append(type, v, e)\ + vec_append(&v, (type *)(e)) + +#define vect_back(type, v) \ + *(type *)vec_back(&v) + +#define vect_pop(type, v) \ + *(type *)vec_pop(&v) + +#endif /* VEC_H */ diff --git a/src/actualize.c b/src/actualize.c index 3460851..744dc1a 100644 --- a/src/actualize.c +++ b/src/actualize.c @@ -18,7 +18,8 @@ #define UNUSED(x) do { (void)(x); } while (0) -static int replace_type_id(struct ast_node *nodes, struct ast_node *id, struct ast_node *replacement); +static int replace_type_id(struct ast_node *nodes, struct ast_node *id, + struct ast_node *replacement); struct act_stack { struct ast_node *node; @@ -85,11 +86,14 @@ static struct ast_node *void_type() return v; } -/* very inefficient, should probably cache somewhere */ static struct ast_node *i27_type(struct scope *scope) { - struct ast_node *i27 = gen_id("i27", NULL_LOC()); + struct ast_node *i27 = gen_id(strdup("i27"), NULL_LOC()); struct ast_node *def = file_scope_find_type(scope, i27); + if (!def) { + error("missing definition of type 'i27'"); + return NULL; + } struct ast_node *a = gen_primitive(AST_I27, def, def->loc); ast_set_flags(a, AST_FLAG_INIT | AST_FLAG_ACTUAL); @@ -97,10 +101,29 @@ static struct ast_node *i27_type(struct scope *scope) return a; } +static struct ast_node *i9_type(struct scope *scope) +{ + struct ast_node *i27 = gen_id(strdup("i9"), NULL_LOC()); + struct ast_node *def = file_scope_find_type(scope, i27); + if (!def) { + error("missing definition of type 'i9'"); + return NULL; + } + + struct ast_node *a = gen_primitive(AST_I9, def, def->loc); + ast_set_flags(a, AST_FLAG_INIT | AST_FLAG_ACTUAL); + a->type = a; + return a; +} + static struct ast_node *str_type(struct scope *scope) { - struct ast_node *str = gen_id("str", NULL_LOC()); + struct ast_node *str = gen_id(strdup("str"), NULL_LOC()); struct ast_node *def = file_scope_find_type(scope, str); + if (!def) { + error("missing definition of type 'str'"); + return NULL; + } struct ast_node *a = gen_primitive(AST_STR, def, def->loc); ast_set_flags(a, AST_FLAG_INIT | AST_FLAG_ACTUAL); @@ -110,8 +133,12 @@ static struct ast_node *str_type(struct scope *scope) static struct ast_node *bool_type(struct scope *scope) { - struct ast_node *b = gen_id("bool", NULL_LOC()); + struct ast_node *b = gen_id(strdup("bool"), NULL_LOC()); struct ast_node *def = file_scope_find_type(scope, b); + if (!def) { + error("missing definition of type 'bool'"); + return NULL; + } struct ast_node *a = gen_primitive(AST_BOOL, def, def->loc); ast_set_flags(a, AST_FLAG_INIT | AST_FLAG_ACTUAL); @@ -155,7 +182,8 @@ static struct ast_node *find_label(struct act_state *state, if (prev) do { cur = prev->next; - if (same_id(AST_LABEL(prev->node).id, AST_LABEL(label).id)) + if (same_id(AST_LABEL(prev->node).id, + AST_LABEL(label).id)) return prev->node; } while ((prev = cur)); @@ -314,7 +342,8 @@ static int analyze_visibility(struct scope *scope, struct ast_node *node) case AST_IMPORT: { const char *file = AST_IMPORT(node).file; ret |= process_file(&scope, - ast_flags(node, AST_FLAG_PUBLIC), file); + (int)ast_flags(node, AST_FLAG_PUBLIC), + file); break; } @@ -379,8 +408,17 @@ static int analyze_var(struct scope *scope, struct ast_node *node) return actualize(&state, scope, node); } +static void set_type(struct ast_node *node, struct ast_node *type) +{ + assert(type->node_type == AST_TYPE); + node->type = clone_ast_node(type); +} + static int analyze_proc(struct scope *scope, struct ast_node *node) { + /* not sure if this is the best place for this */ + AST_PROC(node).id->scope = scope; + struct scope *proc_scope = create_scope(); scope_add_scope(scope, proc_scope); node->scope = proc_scope; @@ -389,14 +427,16 @@ static int analyze_proc(struct scope *scope, struct ast_node *node) struct act_state state = {0}; int ret = actualize(&state, proc_scope, sign); - node->type = sign; + set_type(node, sign); return ret; } -static struct ast_node *analyze_type_expand(struct scope *scope, struct ast_node *n) +static struct ast_node *analyze_type_expand(struct scope *scope, + struct ast_node *n) { assert(n->node_type == AST_TYPE_EXPAND); - struct ast_node *trait = file_scope_find_type(scope, AST_TYPE_EXPAND(n).id); + struct ast_node *trait = file_scope_find_type(scope, + AST_TYPE_EXPAND(n).id); if (!trait) { semantic_error(scope->fctx, n, "no such type"); return NULL; @@ -407,7 +447,8 @@ static struct ast_node *analyze_type_expand(struct scope *scope, struct ast_node return NULL; } - semantic_info(scope->fctx, n, "FIXME: skipping type param check for now"); + semantic_info(scope->fctx, n, + "FIXME: skipping type param check for now"); struct ast_node *body = AST_TRAIT(trait).raw_body; body = clone_ast_node(body); @@ -454,20 +495,22 @@ static int analyze_struct(struct scope *scope, struct ast_node *node) if (generics) ast_set_flags(node, AST_FLAG_GENERIC); - struct ast_node *type = gen_type(AST_TYPE_STRUCT, node, NULL, node->loc); + struct ast_node *type = gen_type(AST_TYPE_STRUCT, node, NULL, + node->loc); foreach_node(n, AST_STRUCT(node).body) { if (n->node_type != AST_TYPE_EXPAND) continue; - if (implements_trait(AST_STRUCT(node).body, AST_TYPE_EXPAND(n).id)) { + if (implements_trait(AST_STRUCT(node).body, + AST_TYPE_EXPAND(n).id)) { n->node_type = AST_EMPTY; continue; } if (same_id(AST_STRUCT(node).id, AST_TYPE_EXPAND(n).id)) { semantic_error(scope->fctx, n, - "recursive trait implementations not allowed"); + "recursive trait implementations not allowed"); return -1; } @@ -488,7 +531,7 @@ static int analyze_struct(struct scope *scope, struct ast_node *node) switch (n->node_type) { case AST_EMPTY: continue; case AST_ID: continue; - /* prototypes are checked later */ + /* prototypes are checked later */ case AST_PROC: if (!AST_PROC(n).body) continue; default: } @@ -514,9 +557,11 @@ static int analyze_struct(struct scope *scope, struct ast_node *node) if (AST_PROC(n).body) continue; - struct ast_node *proc = scope_find_proc(struct_scope, AST_PROC(n).id); + struct ast_node *proc = scope_find_proc(struct_scope, + AST_PROC(n).id); if (!proc) { - semantic_error(scope->fctx, n, "missing implementation"); + semantic_error(scope->fctx, n, + "missing implementation"); return -1; } @@ -556,7 +601,8 @@ static int analyze_trait(struct scope *scope, struct ast_node *node) continue; /* don't re-expand already implemented traits */ - if (implements_trait(AST_TRAIT(node).body, AST_TYPE_EXPAND(n).id)) { + if (implements_trait(AST_TRAIT(node).body, + AST_TYPE_EXPAND(n).id)) { /* not sure about this, but at least we don't have stray * type expands everywhere */ n->node_type = AST_EMPTY; @@ -565,7 +611,7 @@ static int analyze_trait(struct scope *scope, struct ast_node *node) if (same_id(AST_TRAIT(node).id, AST_TYPE_EXPAND(n).id)) { semantic_error(scope->fctx, n, - "recursive trait implementations not allowed"); + "recursive trait implementations not allowed"); return -1; } @@ -589,7 +635,7 @@ static int analyze_trait(struct scope *scope, struct ast_node *node) switch (n->node_type) { case AST_EMPTY: continue; case AST_ID: continue; - /* prototypes are added later */ + /* prototypes are added later */ case AST_PROC: if (!AST_PROC(n).body) continue; default: } @@ -619,9 +665,9 @@ static int analyze_trait(struct scope *scope, struct ast_node *node) } foreach_node(n, AST_TRAIT(node).body) { - if (n->node_type != AST_PROC); + if (n->node_type != AST_PROC) + continue; - struct act_state state = {0}; if (analyze_proc(trait_scope, n)) return -1; } @@ -645,7 +691,6 @@ static int analyze_signs(struct scope *scope, struct ast_node *node) static int analyze(struct scope *scope, struct ast_node *tree) { - struct ast_node *node = tree, *next; foreach_node(node, tree) { if (analyze_visibility(scope, node)) return -1; @@ -661,7 +706,7 @@ static int analyze(struct scope *scope, struct ast_node *tree) if (actualize(&state, scope, node)) return -1; - printf("actualized:\n"); + printf("//actualized:\n"); dump_ast_node(0, node); } @@ -784,31 +829,6 @@ static void actualize_trait_types(struct ast_node *params, assert(!args && !params); } -static int actualize_proc_call(struct act_state *state, - struct scope *scope, struct ast_node *call, - struct ast_node *proc) -{ - /* clone procedure definition to - * replace trait types with actual types and actualize it */ - struct ast_node *def = clone_ast_node(proc); - if (!def) { - /* internal error */ - internal_error("failed allocating actualization"); - return -1; - } - - struct ast_node *sign = AST_PROC(def).sign; - struct ast_node *params = AST_SIGN_TYPE(sign).params; - struct ast_node *args = AST_CALL(call).args; - actualize_trait_types(params, args); - - if (actualize(state, def->scope, def)) - return -1; - - call->type = AST_SIGN_TYPE(sign).ret; - return 0; -} - static int actualize_macro_expand(struct act_state *state, struct scope *scope, struct ast_node *macro_expand) @@ -885,13 +905,14 @@ static int actualize_call(struct act_state *state, struct ast_node *expr = AST_CALL(call).expr; if (AST_TYPE(expr->type).kind != AST_TYPE_SIGN) { char *tstr = type_str(expr->type); - semantic_info(scope->fctx, call, "not a callable type: %s", tstr); + semantic_info(scope->fctx, call, "not a callable type: %s", + tstr); free(tstr); return -1; } struct ast_node *sign = expr->type; - call->type = AST_SIGN_TYPE(sign).ret; + set_type(call, AST_SIGN_TYPE(sign).ret); return 0; } @@ -943,7 +964,7 @@ static int actualize_proc(struct act_state *state, if (actualize(&new_state, proc->scope, sign)) return -1; - proc->type = sign; + set_type(proc, sign); /* actualize body */ new_state.cur_proc = proc; @@ -974,6 +995,11 @@ static int actualize_proc(struct act_state *state, if (undefined_gotos(&new_state, scope)) return -1; + /* if we're main, don't mangle the entry point */ + struct ast_node *id = AST_PROC(proc).id; + if (strcmp("main", AST_ID(id).id) == 0) + ast_set_flags(id, AST_FLAG_NOMANGLE); + /* we have successfully actualized the procedure */ return 0; } @@ -1020,8 +1046,7 @@ static int actualize_binop(struct act_state *state, * should be allowed to operate on eachother */ /* types are the same, so the type of this expression is whichever */ - binop->type = left->type; - + set_type(binop, left->type); return 0; } @@ -1041,11 +1066,19 @@ static int actualize_block(struct act_state *state, } struct act_stack *defers = state->defer_stack; - if (actualize(state, block_scope, node->_block.body)) - return -1; + foreach_node(pt, node->_block.body) { + if (actualize(state, block_scope, pt)) + return -1; + } + + if (node->_block.body == NULL) { + node->type = void_type(); + node->_block.body = gen_empty(); + return 0; + } /* the block type is the last statement in the block's type */ - node->type = ast_last_node(node->_block.body)->type; + set_type(node, ast_last_node(node->_block.body)->type); if (!node->type) { semantic_error(scope->fctx, node, "unable to detect block type"); @@ -1072,6 +1105,8 @@ static int actualize_id(struct act_state *state, { UNUSED(state); assert(id && id->node_type == AST_ID); + id->scope = scope; + /** @todo vars and procs kind of override eachother, i.e. * do_something(){..} * ^() do_something; @@ -1086,13 +1121,15 @@ static int actualize_id(struct act_state *state, * */ struct ast_node *decl = file_scope_find_var(scope, id); if (decl) { - id->type = decl->type; + set_type(id, decl->type); + decl->uses++; return 0; } decl = file_scope_find_proc(scope, id); if (decl) { - id->type = decl->type; + set_type(id, decl->type); + decl->uses++; return 0; } @@ -1117,7 +1154,7 @@ static int actualize_var(struct act_state *state, if (init && init->node_type == AST_INIT) { assert(!init->type); - init->type = type; + set_type(init, type); /* TODO: some kind of check_init() */ } @@ -1135,14 +1172,18 @@ static int actualize_var(struct act_state *state, } } + /* this is important for lowering */ + AST_VAR(var).id->scope = scope; + var->scope = scope; + if (init) /* infer */ - var->type = init->type; + set_type(var, init->type); if (type) /* TODO: should there be some default value? */ /* declare */ - var->type = type; + set_type(var, type); /* an unnamed var is a var in a signature that should not produce a * warning on not being used (if I ever get around to adding those kinds @@ -1196,9 +1237,9 @@ static int struct_is_primitive(struct ast_node *s) return 1; /* special case of a special case? - if (strcmp(name, "str")) - return 1; - */ + if (strcmp(name, "str")) + return 1; + */ return 0; } @@ -1228,7 +1269,8 @@ static int actualize_type(struct act_state *state, } struct ast_node *exists = file_scope_find_type(scope, - AST_ID_TYPE(type).id); + AST_ID_TYPE( + type).id); if (!exists) { semantic_error(scope->fctx, type, "no such type"); EXIT_ACT(-1); @@ -1241,9 +1283,9 @@ static int actualize_type(struct act_state *state, } /* - if (actualize(state, exists->scope, exists)) - EXIT_ACT(-1); - */ + if (actualize(state, exists->scope, exists)) + EXIT_ACT(-1); + */ assert(AST_TYPE(type).next == NULL); if (exists->node_type == AST_ALIAS) { @@ -1253,28 +1295,30 @@ static int actualize_type(struct act_state *state, else if (exists->node_type == AST_TRAIT) { /* this is kind of weird, have to think about it */ *type = *gen_type(AST_TYPE_TRAIT, exists, - NULL, exists->loc); + NULL, exists->loc); } else if (exists->node_type == AST_STRUCT) { if (struct_is_primitive(exists)) { *type = *gen_primitive( - id_to_primitive(AST_STRUCT(exists).id), - exists, - exists->loc); + id_to_primitive(AST_STRUCT(exists).id), + exists, + exists->loc); } else { *type = *gen_type(AST_TYPE_STRUCT, exists, - NULL, exists->loc); + NULL, exists->loc); } } else if (exists->node_type == AST_ENUM) { *type = *gen_type(AST_TYPE_ENUM, exists, - NULL, exists->loc); + NULL, exists->loc); } break; } case AST_TYPE_CONSTRUCT: + /** @todo fully qualified constructs should be added to the + * actual list for code generation */ semantic_info(scope->fctx, type, "constructs unimplemented, continuing with compilation to see what breaks"); break; @@ -1311,19 +1355,22 @@ static int actualize_type(struct act_state *state, case AST_TYPE_TRAIT: { assert(ast_flags(type, AST_FLAG_ACTUAL)); - semantic_info(scope->fctx, type, "FIXME skipping trait type checks"); + semantic_info(scope->fctx, type, + "FIXME skipping trait type checks"); break; } case AST_TYPE_STRUCT: { assert(ast_flags(type, AST_FLAG_ACTUAL)); - semantic_info(scope->fctx, type, "FIXME skipping struct type checks"); + semantic_info(scope->fctx, type, + "FIXME skipping struct type checks"); break; } case AST_TYPE_PRIMITIVE: { assert(ast_flags(type, AST_FLAG_ACTUAL)); - semantic_info(scope->fctx, type, "FIXME skipping primitive type checks"); + semantic_info(scope->fctx, type, + "FIXME skipping primitive type checks"); break; } @@ -1344,7 +1391,8 @@ static int actualize_empty(struct act_state *state, * creating a function for */ struct ast_node *void_id = gen_id(strdup("void"), NULL_LOC()); if (!void_id) { - internal_error("couldn't allocate type id for empty statement\n"); + internal_error( + "couldn't allocate type id for empty statement\n"); return -1; } @@ -1601,7 +1649,7 @@ static int actualize_cast(struct act_state *state, return -1; if (proc_choice(expr, type)) { - cast->type = type; + set_type(cast, type); return match_proc(state, scope, cast); } @@ -1609,28 +1657,28 @@ static int actualize_cast(struct act_state *state, return -1; if (expr->node_type == AST_INIT) { - cast->type = type; + set_type(cast, type); return actualize_init_cast(state, scope, expr, type); } if (types_match(expr->type, type)) { - cast->type = type; + set_type(cast, type); return 0; } if (integral_type(expr->type) && integral_type(type)) { - cast->type = type; + set_type(cast, type); return 0; } if (pointer_type(expr->type) && pointer_type(type)) { - cast->type = type; + set_type(cast, type); return 0; } if (pointer_conversion(expr->type, type) || pointer_conversion(type, expr->type)) { - cast->type = type; + set_type(cast, type); return 0; } @@ -1650,19 +1698,17 @@ static int actualize_const(struct act_state *state, struct scope *scope, { UNUSED(state); assert(cons->node_type == AST_CONST); - if (AST_CONST(cons).kind == AST_CONST_INTEGER) { - /* error checking would be doog */ + if (AST_CONST(cons).kind == AST_CONST_INTEGER) cons->type = i27_type(scope); - return 0; - } - if (AST_CONST(cons).kind == AST_CONST_STRING) { + else if (AST_CONST(cons).kind == AST_CONST_STRING) cons->type = str_type(scope); + + if (cons->type) return 0; - } semantic_error(scope->fctx, cons, "unimplemented constant"); - return 0; + return 1; } static int actualize_alias(struct act_state *state, struct scope *scope, @@ -1709,7 +1755,7 @@ static int actualize_return(struct act_state *state, struct scope *scope, if (actualize(state, scope, expr)) return -1; - node->type = expr->type; + set_type(node, expr->type); } else { node->type = void_type(); @@ -1726,7 +1772,7 @@ static int actualize_return(struct act_state *state, struct scope *scope, "return type mismatch: %s", et); semantic_info(scope->fctx, ret, - "vs %s", rt); + "vs %s", rt); free(rt); free(et); return -1; @@ -1854,7 +1900,7 @@ static int actualize_unop(struct act_state *state, return -1; /* generally speaking */ - node->type = expr->type; + set_type(node, expr->type); switch (node->_unop.op) { case AST_DEREF: { @@ -1866,7 +1912,7 @@ static int actualize_unop(struct act_state *state, return -1; } - node->type = AST_PTR_TYPE(type).base; + set_type(node, AST_PTR_TYPE(type).base); assert(node->type); break; } @@ -1874,18 +1920,20 @@ static int actualize_unop(struct act_state *state, case AST_REF: { node->type = gen_type(AST_TYPE_POINTER, NULL, NULL, NULL_LOC()); - node->AST_TYPE(type).next = expr->type; + set_type(node->AST_TYPE(type).next, expr->type); break; } case AST_LNOT: { if (AST_TYPE(expr->type).kind != AST_TYPE_PRIMITIVE) { - semantic_error(scope->fctx, node, "'!' only implemented for primitive types"); + semantic_error(scope->fctx, node, + "'!' only implemented for primitive types"); return -1; } if (AST_PRIMITIVE_TYPE(expr->type).type == AST_VOID) { - semantic_error(scope->fctx, node, "'!' not implemented for void"); + semantic_error(scope->fctx, node, + "'!' not implemented for void"); return -1; } @@ -1909,7 +1957,7 @@ static int actualize_as(struct act_state *state, if (actualize(state, scope, type)) return -1; - as->type = type; + set_type(as, type); return 0; } @@ -1952,7 +2000,8 @@ next: return ast_call_on(_replace_type_id, node, data); } -static int replace_type_id(struct ast_node *nodes, struct ast_node *id, struct ast_node *replacement) +static int replace_type_id(struct ast_node *nodes, struct ast_node *id, + struct ast_node *replacement) { assert(replacement->node_type == AST_TYPE); struct ast_node *pair[2] = {id, replacement}; @@ -2050,29 +2099,29 @@ static int actualize_dot(struct act_state *state, default: { char *tstr = type_str(type); semantic_error(scope->fctx, node, - "illegal type in dot expression: %s", - tstr); + "illegal type in dot expression: %s", + tstr); free(tstr); - return -1; - } + return -1; + } } struct ast_node *exists = scope_find_var(def->scope, id); if (exists) { assert(exists->type); - node->type = exists->type; + set_type(node, exists->type); return 0; } exists = scope_find_proc(def->scope, id); if (exists) { assert(exists->type); - node->type = exists->type; + set_type(node, exists->type); return 0; } semantic_error(scope->fctx, node, - "does not have member"); + "does not have member"); return -1; } @@ -2102,7 +2151,7 @@ static int actualize_assign(struct act_state *state, struct scope *scope, return -1; if (from->node_type == AST_INIT) { - node->type = to->type; + set_type(node, to->type); return actualize_init_cast(state, scope, from, to->type); } @@ -2123,7 +2172,7 @@ static int actualize_assign(struct act_state *state, struct scope *scope, return -1; } - node->type = to->type; + set_type(node, to->type); return 0; } @@ -2153,7 +2202,7 @@ static int actualize_fetch(struct act_state *state, struct scope *scope, return -1; } - fetch->type = def->type; + set_type(fetch, def->type); return 0; } @@ -2176,7 +2225,7 @@ static int actualize_enum(struct act_state *state, struct scope *scope, node->type = type; struct ast_node *members = node->_enum.body; while (members) { - members->type = type; + set_type(members, type); if (members->_val.val) { struct ast_node *val = members->_val.val; if (actualize(state, enum_scope, val)) @@ -2208,6 +2257,56 @@ static int actualize_enum(struct act_state *state, struct scope *scope, return 0; } +static int actualize_if(struct act_state *state, struct scope *scope, + struct ast_node *node) +{ + assert(node->node_type == AST_IF); + if (actualize(state, scope, AST_IF(node).cond)) + return -1; + + if (actualize(state, scope, AST_IF(node).body)) + return -1; + + if (actualize(state, scope, AST_IF(node).els)) + return -1; + + if (ast_flags(node, AST_FLAG_DOEXPR)) { + struct ast_node *tt = ast_last_node(AST_IF(node).body)->type; + struct ast_node *ft = ast_last_node(AST_IF(node).els)->type; + if (!types_match(tt, ft)) { + semantic_error(scope->fctx, node, + "mismatched if/else body values"); + return -1; + } + + set_type(node, tt); + return 0; + } + + node->type = void_type(); + return 0; +} + +static int actualize_for(struct act_state *state, struct scope *scope, + struct ast_node *node) +{ + assert(node->node_type == AST_FOR); + if (actualize(state, scope, AST_FOR(node).pre)) + return -1; + + if (actualize(state, scope, AST_FOR(node).post)) + return -1; + + if (actualize(state, scope, AST_FOR(node).cond)) + return -1; + + if (actualize(state, scope, AST_FOR(node).body)) + return -1; + + node->type = void_type(); + return 0; +} + static int actualize(struct act_state *state, struct scope *scope, struct ast_node *node) { @@ -2261,6 +2360,8 @@ static int actualize(struct act_state *state, struct scope *scope, case AST_ASSIGN: ret |= actualize_assign(state, scope, node); break; case AST_FETCH: ret |= actualize_fetch(state, scope, node); break; case AST_ENUM: ret |= actualize_enum(state, scope, node); break; + case AST_IF: ret |= actualize_if(state, scope, node); break; + case AST_FOR: ret |= actualize_for(state, scope, node); break; default: /* more like internal_error, maybe? */ diff --git a/src/asm.c b/src/asm.c deleted file mode 100644 index 8e49aa9..0000000 --- a/src/asm.c +++ /dev/null @@ -1,102 +0,0 @@ -#include <ek/ops.h> -#include <stdio.h> -#include <stdlib.h> -#include <assert.h> - -/* I guess using the zero register might be okay in some scenarios, but for now - * I'll just keep it an illegal register */ -#define ASSERT_REG(x) {assert(x->kind == LOC_REG); assert( \ - x->reg > 0 && x->reg < 81);} -#define ASSERT_MEM(x) {assert(x->kind == LOC_MEM); assert( \ - x->reg > 0 && x->reg < 81);} - -static int print_comment(struct op *op, FILE *f) -{ - fprintf(f, "/* %s */\n", op->string); - return 0; -} - -static int print_label(struct op *op, FILE *f) -{ - fprintf(f, "%s:\n", op->string); - return 0; -} - -static int print_li(struct op *op, FILE *f) -{ - struct loc *o = &op->outputs; - assert(o->next == NULL); - /* unsure if this will always hold, but for now */ - ASSERT_REG(o); - - fprintf(f, "li x%zd, %lld\n", o->reg, op->constant); - return 0; -} - -static int print_mv(struct op *op, FILE *f) -{ - struct loc *i = &op->inputs; - struct loc *o = &op->outputs; - assert(i->next == NULL); - assert(o->next == NULL); - ASSERT_REG(i); - ASSERT_REG(o); - - fprintf(f, "mv x%zd, x%zd\n", o->reg, i->reg); - return 0; -} - -static int print_stt(struct op *op, FILE *f) -{ - struct loc *i = &op->inputs; - struct loc *o = &op->outputs; - assert(i->next == NULL); - assert(o->next == NULL); - ASSERT_REG(i); - ASSERT_MEM(o); - - fprintf(f, "st t, x%zd, %lld(x%zd)\n", i->reg, o->off, o->reg); - return 0; -} - -static int print_ret(struct op *op, FILE *f) -{ - (void)op; - /* technically speaking ret takes a number of inputs, but they should be - * marshaled into registers with moves etc. so don't worry about them - * here */ - fprintf(f, "jalr x0, 0(x21)\n"); - /* eventually add in proper ret alias to assembly language once I go - * through calling conventions etc. */ - return 0; -} - -static int print_op(struct op *op, FILE *f) -{ - int ret = 0; - switch (op->opcode) { - case OP_COMMENT: ret = print_comment(op, f); break; - case OP_LABEL: ret = print_label(op, f); break; - case OP_LI: ret = print_li(op, f); break; - case OP_MV: ret = print_mv(op, f); break; - case OP_STT: ret = print_stt(op, f); break; - case OP_RET: ret = print_ret(op, f); break; - default: abort(); - } - - return ret; -} - -int print_asm(struct ops *ops, FILE *f) -{ - int ret = 0; - struct op *op = ops->base; - while (op) { - if ((ret = print_op(op, f))) - break; - - op = op->next; - } - - return ret; -} @@ -136,7 +136,8 @@ struct ast_node *gen_binop(enum ast_binops op, return n; } -struct ast_node *gen_unop(enum ast_unops op, struct ast_node *expr, struct src_loc loc) +struct ast_node *gen_unop(enum ast_unops op, struct ast_node *expr, + struct src_loc loc) { ALLOC_NODE(n, "unop"); n->node_type = AST_UNOP; @@ -166,7 +167,8 @@ struct ast_node *gen_id(const char *id, struct src_loc loc) return n; } -struct ast_node *gen_assign(struct ast_node *to, struct ast_node *from, struct src_loc loc) +struct ast_node *gen_assign(struct ast_node *to, struct ast_node *from, + struct src_loc loc) { ALLOC_NODE(n, "assign"); n->node_type = AST_ASSIGN; @@ -205,7 +207,8 @@ struct ast_node *gen_string(const char *str, struct src_loc loc) return n; } -struct ast_node *gen_while(struct ast_node *cond, struct ast_node *body, struct src_loc loc) +struct ast_node *gen_while(struct ast_node *cond, struct ast_node *body, + struct src_loc loc) { ALLOC_NODE(n, "while"); n->node_type = AST_WHILE; @@ -216,7 +219,8 @@ struct ast_node *gen_while(struct ast_node *cond, struct ast_node *body, struct } struct ast_node *gen_for(struct ast_node *pre, struct ast_node *cond, - struct ast_node *post, struct ast_node *body, struct src_loc loc) + struct ast_node *post, struct ast_node *body, + struct src_loc loc) { ALLOC_NODE(n, "for"); n->node_type = AST_FOR; @@ -246,7 +250,8 @@ struct ast_node *gen_goto(struct ast_node *label, struct src_loc loc) return n; } -struct ast_node *gen_dot(struct ast_node *expr, struct ast_node *id, struct src_loc loc) +struct ast_node *gen_dot(struct ast_node *expr, struct ast_node *id, + struct src_loc loc) { ALLOC_NODE(n, "dot"); n->node_type = AST_DOT; @@ -274,7 +279,8 @@ struct ast_node *gen_ctrl(enum ast_ctrl_kind kind, struct src_loc loc) return n; } -struct ast_node *gen_fetch(struct ast_node *id, struct ast_node *type, struct src_loc loc) +struct ast_node *gen_fetch(struct ast_node *id, struct ast_node *type, + struct src_loc loc) { ALLOC_NODE(n, "fetch"); n->node_type = AST_FETCH; @@ -287,7 +293,7 @@ struct ast_node *gen_fetch(struct ast_node *id, struct ast_node *type, struct sr struct ast_node *gen_macro_construct(struct ast_node *id, struct ast_node *params, struct ast_node *body, - struct src_loc loc) + struct src_loc loc) { ALLOC_NODE(n, "macro_construct"); n->node_type = AST_MACRO_CONSTRUCT; @@ -310,7 +316,8 @@ struct ast_node *gen_if(struct ast_node *cond, struct ast_node *body, return n; } -struct ast_node *gen_switch(struct ast_node *cond, struct ast_node *cases, struct src_loc loc) +struct ast_node *gen_switch(struct ast_node *cond, struct ast_node *cases, + struct src_loc loc) { ALLOC_NODE(n, "switch"); n->node_type = AST_SWITCH; @@ -320,7 +327,8 @@ struct ast_node *gen_switch(struct ast_node *cond, struct ast_node *cases, struc return n; } -struct ast_node *gen_case(struct ast_node *cond, struct ast_node *body, struct src_loc loc) +struct ast_node *gen_case(struct ast_node *cond, struct ast_node *body, + struct src_loc loc) { ALLOC_NODE(n, "case"); /* TODO: a macro to map proc name to node type would make sure I don't @@ -334,7 +342,8 @@ struct ast_node *gen_case(struct ast_node *cond, struct ast_node *body, struct s return n; } -struct ast_node *gen_primitive(enum ast_primitive type, struct ast_node *def, struct src_loc loc) +struct ast_node *gen_primitive(enum ast_primitive type, struct ast_node *def, + struct src_loc loc) { ALLOC_NODE(n, "primitive"); n->node_type = AST_TYPE; @@ -465,7 +474,8 @@ struct ast_node *gen_proc(struct ast_node *id, struct ast_node *sign, } struct ast_node *gen_struct(struct ast_node *id, - struct ast_node *generics, struct ast_node *body, struct src_loc loc) + struct ast_node *generics, struct ast_node *body, + struct src_loc loc) { ALLOC_NODE(n, "struct"); n->node_type = AST_STRUCT; @@ -488,7 +498,8 @@ struct ast_node *gen_enum(struct ast_node *id, struct ast_node *type, return n; } -struct ast_node *gen_cast(struct ast_node *expr, struct ast_node *type, struct src_loc loc) +struct ast_node *gen_cast(struct ast_node *expr, struct ast_node *type, + struct src_loc loc) { ALLOC_NODE(n, "cast"); n->node_type = AST_CAST; @@ -498,7 +509,8 @@ struct ast_node *gen_cast(struct ast_node *expr, struct ast_node *type, struct s return n; } -struct ast_node *gen_val(struct ast_node *id, struct ast_node *val, struct src_loc loc) +struct ast_node *gen_val(struct ast_node *id, struct ast_node *val, + struct src_loc loc) { ALLOC_NODE(n, "val"); n->node_type = AST_VAL; @@ -508,7 +520,8 @@ struct ast_node *gen_val(struct ast_node *id, struct ast_node *val, struct src_l return n; } -struct ast_node *gen_alias(struct ast_node *id, struct ast_node *type, struct src_loc loc) +struct ast_node *gen_alias(struct ast_node *id, struct ast_node *type, + struct src_loc loc) { ALLOC_NODE(n, "alias"); n->node_type = AST_ALIAS; @@ -519,10 +532,10 @@ struct ast_node *gen_alias(struct ast_node *id, struct ast_node *type, struct sr } struct ast_node *gen_trait(struct ast_node *id, - struct ast_node *params, + struct ast_node *params, struct ast_node *raw_body, - struct ast_node *body, - struct src_loc loc) + struct ast_node *body, + struct src_loc loc) { ALLOC_NODE(n, "trait"); n->node_type = AST_TRAIT; @@ -626,6 +639,7 @@ static void dump(int depth, const char *fmt, ...) { va_list args; va_start(args, fmt); + printf("//"); for (int i = 0; i < depth; ++i) putchar('\t'); @@ -1171,33 +1185,37 @@ struct ast_node *clone_ast_node(struct ast_node *node) case AST_FETCH: new = gen_fetch(clone_ast_node(AST_FETCH(node).id), clone_ast_node(AST_FETCH(node).type), - node->loc); + node->loc); break; case AST_ASSIGN: new = gen_assign(clone_ast_node(AST_ASSIGN(node).to), clone_ast_node(AST_ASSIGN(node).from), - node->loc); + node->loc); break; - case AST_INIT: new = gen_init(clone_ast_node(AST_INIT(node).body), node->loc); + case AST_INIT: new = gen_init(clone_ast_node(AST_INIT(node).body), + node->loc); break; - case AST_SIZEOF: new = gen_sizeof(clone_ast_node(AST_SIZEOF(node).expr), node->loc); + case AST_SIZEOF: new = gen_sizeof(clone_ast_node(AST_SIZEOF(node).expr), + node->loc); break; case AST_DOT: new = gen_dot(clone_ast_node(AST_DOT(node).expr), clone_ast_node(AST_DOT(node).id), - node->loc); + node->loc); break; case AST_AS: new = gen_as(clone_ast_node(AST_AS(node).type), node->loc); break; - case AST_GOTO: new = gen_goto(clone_ast_node(AST_GOTO(node).label), node->loc); + case AST_GOTO: new = gen_goto(clone_ast_node(AST_GOTO(node).label), + node->loc); break; - case AST_LABEL: new = gen_label(clone_ast_node(AST_LABEL(node).id), node->loc); + case AST_LABEL: new = gen_label(clone_ast_node(AST_LABEL(node).id), + node->loc); break; case AST_BINOP: new = gen_binop(AST_BINOP(node).op, @@ -1208,7 +1226,7 @@ struct ast_node *clone_ast_node(struct ast_node *node) case AST_UNOP: new = gen_unop(AST_UNOP(node).op, clone_ast_node(AST_UNOP(node).expr), - node->loc); + node->loc); break; case AST_CALL: new = gen_call(clone_ast_node(AST_CALL(node).expr), @@ -1216,7 +1234,8 @@ struct ast_node *clone_ast_node(struct ast_node *node) node->loc); break; - case AST_DEFER: new = gen_defer(clone_ast_node(AST_DEFER(node).expr), node->loc); + case AST_DEFER: new = gen_defer(clone_ast_node(AST_DEFER(node).expr), + node->loc); break; case AST_MACRO_CONSTRUCT: new = gen_macro_construct( @@ -1234,7 +1253,7 @@ struct ast_node *clone_ast_node(struct ast_node *node) case AST_CAST: new = gen_cast(clone_ast_node(AST_CAST(node).expr), clone_ast_node(AST_CAST(node).type), - node->loc); + node->loc); break; case AST_PROC: new = gen_proc(clone_ast_node(AST_PROC(node).id), @@ -1244,36 +1263,36 @@ struct ast_node *clone_ast_node(struct ast_node *node) break; case AST_VAR: new = gen_var(clone_ast_node(AST_VAR(node).id), - clone_ast_node(AST_VAR(node).type), - clone_ast_node(AST_VAR(node).init), - node->loc); + clone_ast_node(AST_VAR(node).type), + clone_ast_node(AST_VAR(node).init), + node->loc); break; case AST_FOR: new = gen_for(clone_ast_node(AST_FOR(node).pre), clone_ast_node(AST_FOR(node).cond), clone_ast_node(AST_FOR(node).post), clone_ast_node(AST_FOR(node).body), - node->loc); + node->loc); break; case AST_WHILE: new = gen_while(clone_ast_node(AST_WHILE(node).cond), clone_ast_node(AST_WHILE(node).body), - node->loc); + node->loc); break; case AST_CTRL: new = gen_ctrl(AST_CTRL(node).kind, node->loc); break; - case AST_RETURN: new = gen_return(clone_ast_node(AST_RETURN(node).expr), node->loc); + case AST_RETURN: new = gen_return(clone_ast_node(AST_RETURN(node).expr), + node->loc); break; case AST_TYPE: - /* oh, if a node has a ->type it probably isn't cloned - * correctly... */ switch (node->_type.kind) { case AST_TYPE_PRIMITIVE: new = gen_primitive(AST_PRIMITIVE_TYPE(node).type, - AST_PRIMITIVE_TYPE(node).def, node->loc); + AST_PRIMITIVE_TYPE(node).def, + node->loc); break; case AST_TYPE_TRAIT: @@ -1285,9 +1304,11 @@ struct ast_node *clone_ast_node(struct ast_node *node) case AST_TYPE_CONSTRUCT: new = gen_type(AST_TYPE_CONSTRUCT, - clone_ast_node(AST_CONSTRUCT_TYPE(node).id), - clone_ast_node(AST_CONSTRUCT_TYPE(node).args), - node->loc); + clone_ast_node(AST_CONSTRUCT_TYPE( + node).id), + clone_ast_node(AST_CONSTRUCT_TYPE( + node).args), + node->loc); break; case AST_TYPE_ID: @@ -1306,9 +1327,9 @@ struct ast_node *clone_ast_node(struct ast_node *node) case AST_TYPE_POINTER: new = gen_type(AST_TYPE_POINTER, - clone_ast_node(AST_PTR_TYPE(node).base), - NULL, - node->loc); + clone_ast_node(AST_PTR_TYPE(node).base), + NULL, + node->loc); break; case AST_TYPE_STRUCT: @@ -1326,7 +1347,8 @@ struct ast_node *clone_ast_node(struct ast_node *node) case AST_TYPE_SIGN: new = gen_type(AST_TYPE_SIGN, - clone_ast_node(AST_SIGN_TYPE(node).params), + clone_ast_node(AST_SIGN_TYPE( + node).params), clone_ast_node(AST_SIGN_TYPE(node).ret), node->loc); break; @@ -1339,7 +1361,8 @@ struct ast_node *clone_ast_node(struct ast_node *node) case AST_BLOCK: /* TODO: should defers also be cloned? Probably? */ - new = gen_block(clone_ast_node(AST_BLOCK(node).body), node->loc); + new = gen_block(clone_ast_node(AST_BLOCK(node).body), + node->loc); break; case AST_IMPORT: @@ -1354,32 +1377,32 @@ struct ast_node *clone_ast_node(struct ast_node *node) new = gen_enum(clone_ast_node(AST_ENUM(node).id), clone_ast_node(AST_ENUM(node).type), clone_ast_node(AST_ENUM(node).body), - node->loc); + node->loc); break; case AST_STRUCT: new = gen_struct(clone_ast_node(AST_STRUCT(node).id), clone_ast_node(AST_STRUCT(node).generics), clone_ast_node(AST_STRUCT(node).body), - node->loc); + node->loc); break; case AST_VAL: new = gen_val(clone_ast_node(AST_VAL(node).id), clone_ast_node(AST_VAL(node).val), - node->loc); + node->loc); break; case AST_SWITCH: new = gen_switch(clone_ast_node(AST_SWITCH(node).cond), clone_ast_node(AST_SWITCH(node).cases), - node->loc); + node->loc); break; case AST_CASE: new = gen_case(clone_ast_node(AST_CASE(node).cond), clone_ast_node(AST_CASE(node).body), - node->loc); + node->loc); break; case AST_CONST: @@ -1389,7 +1412,8 @@ struct ast_node *clone_ast_node(struct ast_node *node) break; case AST_CONST_STRING: - new = gen_string(strdup(AST_CONST(node).str), node->loc); + new = gen_string(strdup(AST_CONST(node).str), + node->loc); break; } break; @@ -1405,13 +1429,13 @@ struct ast_node *clone_ast_node(struct ast_node *node) case AST_ALIAS: new = gen_alias(clone_ast_node(AST_ALIAS(node).id), clone_ast_node(AST_ALIAS(node).type), - node->loc); + node->loc); break; case AST_TRAIT: new = gen_trait(clone_ast_node(AST_TRAIT(node).id), clone_ast_node(AST_TRAIT(node).params), - clone_ast_node(AST_TRAIT(node).raw_body), + clone_ast_node(AST_TRAIT(node).raw_body), clone_ast_node(AST_TRAIT(node).body), node->loc); break; @@ -1420,7 +1444,7 @@ struct ast_node *clone_ast_node(struct ast_node *node) new = gen_if(clone_ast_node(AST_IF(node).cond), clone_ast_node(AST_IF(node).body), clone_ast_node(AST_IF(node).els), - node->loc); + node->loc); break; } @@ -1428,23 +1452,26 @@ struct ast_node *clone_ast_node(struct ast_node *node) assert(new); new->scope = node->scope; new->flags = node->flags; + new->uses = node->uses; new->loc = node->loc; new->next = clone_ast_node(node->next); - /* some special case handled type references for us */ - if (!new->type) - new->type = node->type; + if (node->type != node) + node->type = clone_ast_node(node->type); + else + node->type = node; return new; } -int ast_flags(struct ast_node *node, enum ast_flag flags) +unsigned ast_flags(struct ast_node *node, enum ast_flag flags) { return node->flags & flags; } static int call_on_assign(int (*call)(struct ast_node *, - void *), struct ast_node *node, void *data) + void *), struct ast_node *node, + void *data) { int ret = 0; ret |= call(AST_ASSIGN(node).to, data); @@ -1459,7 +1486,8 @@ static int call_on_init(int (*call)(struct ast_node *, } static int call_on_sizeof(int (*call)(struct ast_node *, - void *), struct ast_node *node, void *data) + void *), struct ast_node *node, + void *data) { return call(AST_SIZEOF(node).expr, data); } @@ -1525,7 +1553,8 @@ static int call_on_while(int (*call)(struct ast_node *, } static int call_on_return(int (*call)(struct ast_node *, - void *), struct ast_node *node, void *data) + void *), struct ast_node *node, + void *data) { return call(node->_return.expr, data); } @@ -1569,7 +1598,8 @@ static int call_on_enum(int (*call)(struct ast_node *, } static int call_on_struct(int (*call)(struct ast_node *, - void *), struct ast_node *node, void *data) + void *), struct ast_node *node, + void *data) { int ret = 0; ret |= call(node->_struct.id, data); @@ -1588,7 +1618,8 @@ static int call_on_val(int (*call)(struct ast_node *, } static int call_on_switch(int (*call)(struct ast_node *, - void *), struct ast_node *node, void *data) + void *), struct ast_node *node, + void *data) { int ret = 0; ret |= call(node->_switch.cond, data); @@ -1606,19 +1637,22 @@ static int call_on_case(int (*call)(struct ast_node *, } static int call_on_type_id(int (*call)(struct ast_node *, - void *), struct ast_node *node, void *data) + void *), struct ast_node *node, + void *data) { return call(AST_ID_TYPE(node).id, data); } static int call_on_type_arr(int (*call)(struct ast_node *, - void *), struct ast_node *node, void *data) + void *), struct ast_node *node, + void *data) { return call(AST_ARR_TYPE(node).size, data); } static int call_on_type_sign(int (*call)(struct ast_node *, - void *), struct ast_node *node, void *data) + void *), struct ast_node *node, + void *data) { int ret = 0; ret |= call(AST_SIGN_TYPE(node).params, data); @@ -1627,7 +1661,7 @@ static int call_on_type_sign(int (*call)(struct ast_node *, } static int call_on_type_construct(int (*call)(struct ast_node *, void *), - struct ast_node *node, void *data) + struct ast_node *node, void *data) { int ret = 0; ret |= call(AST_CONSTRUCT_TYPE(node).id, data); @@ -1636,13 +1670,13 @@ static int call_on_type_construct(int (*call)(struct ast_node *, void *), } static int call_on_type_pointer(int (*call)(struct ast_node *, void *), - struct ast_node *node, void *data) + struct ast_node *node, void *data) { return call(AST_PTR_TYPE(node).base, data); } static int call_on_type(int (*call)(struct ast_node *, void *), - struct ast_node *node, void *data) + struct ast_node *node, void *data) { int ret = 0; switch (AST_TYPE(node).kind) { @@ -1652,8 +1686,10 @@ static int call_on_type(int (*call)(struct ast_node *, void *), case AST_TYPE_ARR: ret = call_on_type_arr(call, node, data); break; case AST_TYPE_STRUCT: break; case AST_TYPE_SIGN: ret = call_on_type_sign(call, node, data); break; - case AST_TYPE_CONSTRUCT: ret = call_on_type_construct(call, node, data); break; - case AST_TYPE_POINTER: ret = call_on_type_pointer(call, node, data); break; + case AST_TYPE_CONSTRUCT: ret = call_on_type_construct(call, node, data); + break; + case AST_TYPE_POINTER: ret = call_on_type_pointer(call, node, data); + break; case AST_TYPE_PRIMITIVE: break; } @@ -1697,7 +1733,8 @@ static int call_on_call(int (*call)(struct ast_node *, } static int call_on_macro_construct(int (*call)(struct ast_node *, - void *), struct ast_node *node, void *data) + void *), struct ast_node *node, + void *data) { int ret = 0; ret |= call(AST_MACRO_CONSTRUCT(node).id, data); @@ -1732,7 +1769,8 @@ static int call_on_fetch(int (*call)(struct ast_node *, } static int call_on_macro_expand(int (*call)(struct ast_node *, - void *), struct ast_node *node, void *data) + void *), struct ast_node *node, + void *data) { int ret = 0; ret |= call(node->_macro_expand.id, data); @@ -1741,7 +1779,8 @@ static int call_on_macro_expand(int (*call)(struct ast_node *, } static int call_on_type_expand(int (*call)(struct ast_node *, - void *), struct ast_node *type_expand, void *data) + void *), + struct ast_node *type_expand, void *data) { int ret = 0; ret |= call(AST_TYPE_EXPAND(type_expand).id, data); @@ -1855,14 +1894,14 @@ int equiv_nodes(struct ast_node *n1, struct ast_node *n2) return 0; switch (n1->node_type) { - case AST_ID: - if (strcmp(AST_ID(n1).id, AST_ID(n2).id) != 0) - return 0; - - break; - default: - internal_error("unimplemented equivalency"); + case AST_ID: + if (strcmp(AST_ID(n1).id, AST_ID(n2).id) != 0) return 0; + + break; + default: + internal_error("unimplemented equivalency"); + return 0; } return 1; diff --git a/src/compiler.c b/src/compiler.c index 76b420a..b345e16 100644 --- a/src/compiler.c +++ b/src/compiler.c @@ -21,8 +21,8 @@ #include <ek/parser.h> #include <ek/debug.h> #include <ek/scope.h> +#include <ek/lower.h> #include <ek/path.h> -#include <ek/ops.h> #include <ek/res.h> /** @@ -140,8 +140,6 @@ int process_file(struct scope **scope, int public, const char *file) if (!cwd) goto out; - debug("(cwd:%s)(dir:%s)(base:%s)", cwd, dir, base); - if (*dir != 0 && chdir(dir)) { error("couldn't change to directory %s: %s", dir, strerror( errno)); @@ -152,7 +150,8 @@ int process_file(struct scope **scope, int public, const char *file) goto out; if (chdir(cwd)) { - error("couldn't change back to directory %s: %s\n", cwd, strerror( + error("couldn't change back to directory %s: %s\n", cwd, + strerror( errno)); goto out; } @@ -163,7 +162,7 @@ out: return res; } -int compile(const char *input, const char *output) { +int compile(const char *input) { int ret = -1; struct scope *root = NULL; if (process_file(&root, 0, input)) { @@ -173,16 +172,7 @@ int compile(const char *input, const char *output) { return ret; } - /* - if ((ret = actualize_main(root))) { - destroy_scope(root); - destroy_ast_nodes(); - error("compilation of %s stopped due to errors", input); - return ret; - } - */ - - if ((ret = lower_ops(root, output))) { + if ((ret = lower_actuals(root))) { destroy_scope(root); destroy_ast_nodes(); error("compilation of %s stopped due to errors", input); diff --git a/src/lower.c b/src/lower.c new file mode 100644 index 0000000..c78ac96 --- /dev/null +++ b/src/lower.c @@ -0,0 +1,858 @@ +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include <assert.h> + +#include <ek/lower.h> +#include <ek/scope.h> +#include <ek/vec.h> + +#define UNUSED(x) (void)x + +enum retval_kind { + REG_I27, + REG_I9, + CONST_I9, + CONST_I27, +}; + +struct retval { + enum retval_kind kind; + char *s; +}; + +struct lower_state { + struct vec top; + struct vec bottom; + struct vec out; + int64_t uniq; +}; + +static struct lower_state create_state() +{ + struct lower_state state; + state.top = vec_create(sizeof(char *)); + state.bottom = vec_create(sizeof(char *)); + state.out = vec_create(sizeof(char *)); + state.uniq = 0; + return state; +} + +static void destroy_state(struct lower_state *state) +{ + assert(vec_len(&state->top) == 0); + assert(vec_len(&state->bottom) == 0); + assert(vec_len(&state->out) == 0); + + vec_destroy(&state->top); + vec_destroy(&state->bottom); + vec_destroy(&state->out); +} + +static void push_loop(struct lower_state *s, char *top, char *bottom, char *out) +{ + vect_append(char *, s->top, &top); + vect_append(char *, s->bottom, &bottom); + vect_append(char *, s->out, &out); +} + +static void pop_loop(struct lower_state *s) +{ + char *top = vect_pop(char *, s->top); + char *bottom = vect_pop(char *, s->bottom); + char *out = vect_pop(char *, s->out); + + free(top); + free(bottom); + free(out); +} + +#define label_peek(v) \ + vect_back(char *, v) + +static int64_t retval_width(struct retval r) +{ + switch (r.kind) { + case REG_I27: return 3; + case REG_I9: return 1; + case CONST_I9: return 1; + case CONST_I27: return 3; + default: abort(); + } + + return 0; +} + +static const char *retval_kind_str(enum retval_kind kind) +{ + switch (kind) { + case REG_I27: return "i27"; + case REG_I9: return "i9"; + case CONST_I9: return "i9"; + case CONST_I27: return "i27"; + default: abort(); + } + + return 0; +} + +static const char *retval_type_str(struct retval r) +{ + /* I guess it saves a bit on typing? */ + return retval_kind_str(r.kind); +} + +static bool retval_is_const(struct retval r) +{ + return r.kind == CONST_I9 || r.kind == CONST_I27; +} + +static bool is_i9(struct ast_node *n) +{ + if (AST_TYPE(n->type).kind != AST_TYPE_PRIMITIVE) + return false; + + return AST_PRIMITIVE_TYPE(n->type).type == AST_I9; +} + +#define retval_create() \ + vec_create(sizeof(struct retval)) + +#define foreach_retval(ri, retval) \ + foreach_vec(ri, retval) + +#define retval_at(rv, ri) \ + vect_at(struct retval, rv, ri) + +static void retval_destroy(struct vec *retval) +{ + foreach_retval(ri, *retval) { + struct retval s = retval_at(*retval, ri); + free(s.s); + } + + vec_destroy(retval); +} + +static struct retval build_retval(enum retval_kind kind, char *s) +{ + return (struct retval){.kind = kind, .s = s}; +} + +static __attribute__((format (printf, 1, 2))) +char *build_str(const char *fmt, ...) { + va_list args1, args2; + va_start(args1, fmt); + va_copy(args2, args1); + + /* I don't expect this to fail, although I guess it could */ + size_t size = (size_t)vsnprintf(NULL, 0, fmt, args1); + va_end(args1); + + char *buf = malloc(size + 1); + vsnprintf(buf, size + 1, fmt, args2); + + va_end(args2); + return buf; +} + +static size_t get_scope_number(struct ast_node *id) +{ + /** @todo this mirrors what's in actualize.c:actualize_id, same comments + * apply */ + struct ast_node *def = file_scope_find_var(id->scope, id); + if (def) + return def->scope->number; + + def = file_scope_find_proc(id->scope, id); + if (def) + return def->scope->number; + + return 0; +} + +static char *mangle_idx(struct ast_node *id, size_t idx) +{ + assert(id->node_type == AST_ID); + assert(id->scope); + const char *name = AST_ID(id).id; + /* oh wait, I need to do a variable lookup on the ID, not use the ID's + * scope number, duh */ + size_t number = get_scope_number(id); + + if (ast_flags(id, AST_FLAG_NOMANGLE)) + return strdup(name); + + return build_str("%s_s%zif%zi", name, number, idx); +} + +static char *mangle(struct ast_node *id) +{ + return mangle_idx(id, 0); +} + +static int lower_expr(struct lower_state *s, struct ast_node *e, + struct vec *retval); +static int lower_statement(struct lower_state *s, struct ast_node *n); + +static void output_id(struct ast_node *id) +{ + char *name = mangle(id); + printf("%s", name); + free(name); +} + +static int lower_global_var(struct ast_node *n) +{ + /* trivial types are reasonably easy, but stuff like compound types need + * a lot of work */ + struct ast_node *type = AST_VAR(n).type; + if (AST_TYPE(type).kind != AST_TYPE_PRIMITIVE) { + semantic_error(n->scope->fctx, n, + "only primitive globals currently implemented"); + return -1; + } + + struct ast_node *id = AST_VAR(n).id; + struct ast_node *init = AST_VAR(n).init; + if (init->node_type != AST_CONST) { + semantic_error(n->scope->fctx, n, + "constant expressions currently not implemented"); + return -1; + } + + output_id(id); + printf(" = "); + + /* hmm, this might be useful elsewhere as well */ + switch (AST_PRIMITIVE_TYPE(type).type) { + case AST_I27: printf("i27 %lli", AST_CONST(init).integer); break; + case AST_I9: printf("i9 %lli", AST_CONST(init).integer); break; + default: + semantic_error(n->scope->fctx, n, + "unhandled primitive type"); + return -1; + } + + printf(";\n"); + return 0; +} + +static int lower_param(struct lower_state *s, struct ast_node *p) +{ + UNUSED(s); + assert(p->node_type == AST_VAR); + struct ast_node *type = AST_VAR(p).type; + if (AST_TYPE(type).kind != AST_TYPE_PRIMITIVE) { + semantic_error(p->scope->fctx, p, + "only primitive params currently implemented"); + return -1; + } + + assert(AST_VAR(p).init == NULL); + + switch (AST_PRIMITIVE_TYPE(type).type) { + case AST_I27: printf("i27 "); break; + case AST_I9: printf("i9 "); break; + default: + semantic_error(p->scope->fctx, p, + "unhandled primitive type"); + return -1; + } + + struct ast_node *id = AST_VAR(p).id; + output_id(id); + printf(","); + return 0; +} + +static int lower_params(struct lower_state *s, struct ast_node *params) +{ + for (struct ast_node *p = params; p; p = p->next) { + if (lower_param(s, p)) + return -1; + } + + return 0; +} + +static int lower_var(struct lower_state *s, struct ast_node *v, + struct vec *retval) +{ + assert(v->node_type == AST_VAR); + struct vec input = retval_create(); + if (lower_expr(s, AST_VAR(v).init, &input)) + return -1; + + struct ast_node *id = AST_VAR(v).id; + /* if we have a struct, we should add the member name to the base name + * */ + foreach_retval(ri, input) { + struct retval r = retval_at(input, ri); + char *name = mangle_idx(id, ri); + /* I assume we're always dealing with i27 for now */ + /** @todo qbt could maybe skip the type stuff except for casts */ + printf("i27 %s = %s;\n", name, r.s); + struct retval n = build_retval(REG_I27, name); + vec_append(retval, &n); + } + + retval_destroy(&input); + return 0; +} + +static void do_const_store(struct lower_state *s, struct vec *from, + struct retval t, struct retval o) +{ + UNUSED(s); + /* I know, kind of silly to swap back and forth between string/int but + * good enough for now */ + int64_t addr = strtoll(t.s, 0, 0) + strtoll(o.s, 0, 0); + foreach_retval(ri, *from) { + struct retval r = retval_at(*from, ri); + /* doesn't really take into account possible padding etc, should + * probably fix at some point */ + printf("%s >> %s (%zi);\n", + r.s, retval_type_str(r), addr); + + addr += retval_width(r); + } +} + +static void do_store(struct lower_state *s, struct vec *from, struct vec *to, + struct vec *off) +{ + assert(vec_len(to) == 1); + struct retval t = retval_at(*to, 0); + + struct retval o = build_retval(CONST_I27, "0"); + if (off) + o = retval_at(*off, 0); + + if (retval_is_const(t) && retval_is_const(o)) { + do_const_store(s, from, t, o); + return; + } + + if (retval_is_const(t)) { + /* o must be register, so swap around for the format to make + * sense */ + struct retval tmp = t; t = o; o = tmp; + } + + assert(retval_is_const(o)); + + int64_t addr = strtoll(o.s, 0, 0); + foreach_retval(ri, *from) { + struct retval r = retval_at(*from, ri); + /* doesn't really take into account possible padding etc, should + * probably fix at some point */ + printf("%s >> %s %s %zi;\n", + r.s, retval_type_str(r), t.s, addr); + + addr += retval_width(r); + } +} + +static int lower_cast(struct lower_state *s, struct ast_node *e, + struct vec *retval) +{ + assert(e->node_type == AST_CAST); + /** @todo make sure actualize removes casts that aren't of these types + * */ + assert(AST_TYPE(e->type).kind == AST_TYPE_PRIMITIVE + || AST_TYPE(e->type).kind == AST_TYPE_POINTER); + + if (lower_expr(s, AST_CAST(e).expr, retval)) + return -1; + + enum retval_kind kind = REG_I27; + if (is_i9(e)) + kind = REG_I9; + + foreach_retval(ri, *retval) { + struct retval r = retval_at(*retval, ri); + /* build new temporary cast result and replace the previous + * retval */ + char *s = build_str("%s%s", "cast_", r.s); + + printf("%s %s = %s;\n", retval_kind_str(kind), s, r.s); + free(r.s); + + r.s = s; + r.kind = kind; + retval_at(*retval, ri) = r; + } + + return 0; +} + +static int lower_const(struct lower_state *s, struct ast_node *c, + struct vec *retval) +{ + UNUSED(s); + assert(c->node_type == AST_CONST); + if (AST_CONST(c).kind == AST_CONST_STRING) { + /* requires pushing strings as variables and replacing them with + * references */ + semantic_error(c->scope->fctx, c, + "string constant lowering not yet implemented"); + return -1; + } + + enum retval_kind type = CONST_I27; + if (AST_PRIMITIVE_TYPE(c->type).type == AST_I9) + type = CONST_I9; + + char *str = build_str("%lli", (long long int)AST_CONST(c).integer); + struct retval r = build_retval(type, str); + vec_append(retval, &r); + return 0; +} + +static int lower_assign(struct lower_state *s, struct ast_node *a, + struct vec *retval) +{ +#define IS_DEREF(t) (t->node_type == AST_UNOP && AST_UNOP(t).op == AST_DEREF) +#define IS_ARR(t) (t->node_type == AST_ARR_ACCESS) + + assert(a->node_type == AST_ASSIGN); + if (lower_expr(s, AST_ASSIGN(a).from, retval)) + return -1; + + struct vec loc = retval_create(); + struct vec off = retval_create(); + + struct ast_node *to = AST_ASSIGN(a).to; + struct ast_node *base = to; + if (IS_DEREF(to)) + base = AST_UNOP(to).expr; + else if (IS_ARR(to)) { + base = AST_ARR_ACCESS(to).base; + if (lower_expr(s, AST_ARR_ACCESS(to).idx, &off)) { + retval_destroy(&loc); + retval_destroy(&off); + return -1; + } + } + + if (lower_expr(s, base, &loc)) { + retval_destroy(&loc); + retval_destroy(&off); + return -1; + } + + if (IS_DEREF(to)) { + do_store(s, retval, &loc, NULL); + } + else if (IS_ARR(to)) { + do_store(s, retval, &loc, &off); + } else { + assert(vec_len(retval) == vec_len(&loc)); + foreach_retval(ri, *retval) { + struct retval to = retval_at(loc, ri); + struct retval from = retval_at(*retval, ri); + printf("i27 %s = %s;\n", to.s, from.s); + } + } + + retval_destroy(&loc); + retval_destroy(&off); + return 0; +#undef IS_DEREF +#undef IS_ARR +} + +static int lower_id(struct lower_state *s, struct ast_node *id, + struct vec *retval) +{ + UNUSED(s); + assert(id->node_type == AST_ID); + char *m = mangle(id); + + struct ast_node *type = id->type; + if (AST_TYPE(type).kind != AST_TYPE_PRIMITIVE + && AST_TYPE(type).kind != AST_TYPE_POINTER + && AST_TYPE(type).kind != AST_TYPE_SIGN) { + semantic_error(id->scope->fctx, id, + "only primitive ids currently implemented"); + return -1; + } + + enum retval_kind kind = REG_I27; + if (is_i9(id)) + kind = REG_I9; + + /* this likely isn't enough and we need to add the & to most things we + * want to take the address of */ + if (AST_TYPE(type).kind == AST_TYPE_SIGN) { + char *o = m; + m = build_str("&%s", m); + free(o); + } + + struct retval r = build_retval(kind, m); + vec_append(retval, &r); + return 0; +} + +static int lower_return(struct lower_state *s, struct ast_node *r, + struct vec *retval) +{ + assert(r->node_type == AST_RETURN); + if (lower_expr(s, AST_RETURN(r).expr, retval)) + return -1; + + printf("=> ( "); + + foreach_retval(ri, *retval) { + struct retval r = retval_at(*retval, ri); + printf("%s, ", r.s); + } + + printf(" );\n"); + return 0; +} + +static int lower_if(struct lower_state *s, struct ast_node *i, + struct vec *retval) +{ + assert(i->node_type == AST_IF); + if (lower_expr(s, AST_IF(i).cond, retval)) + return -1; + + assert(vec_len(retval) == 1); + /* helps readability a little bit */ + long long uniq = s->uniq++; + printf("if%lli:\n", uniq); + + char *bottom = build_str("if_else%lli", uniq); + char *out = build_str("if_out%lli", uniq); + + printf("! %s -> %s;\n", (retval_at(*retval, 0)).s, bottom); + + /* a block counts as a statement in this case */ + if (lower_statement(s, AST_IF(i).body)) { + free(bottom); + return -1; + } + + printf("-> %s;\n", out); + printf("%s:\n", bottom); + free(bottom); + + if (AST_IF(i).els && lower_statement(s, AST_IF(i).els)) { + free(out); + return -1; + } + + printf("%s:\n", out); + free(out); + return 0; +} + +static int lower_for(struct lower_state *s, struct ast_node *f, + struct vec *retval) +{ + assert(f->node_type == AST_FOR); + if (lower_statement(s, AST_FOR(f).pre)) + return -1; + + long long uniq = s->uniq++; + + char *top = build_str("for_top%lli", uniq); + char *bottom = build_str("for_bottom%lli", uniq); + char *out = build_str("for_out%lli", uniq); + + push_loop(s, top, bottom, out); + + printf("-> %s;\n", out); + + printf("%s:\n", top); + if (lower_statement(s, AST_FOR(f).body)) { + pop_loop(s); + return -1; + } + + printf("%s:\n", bottom); + if (lower_statement(s, AST_FOR(f).post)) { + pop_loop(s); + return -1; + } + + printf("%s:\n", out); + if (lower_expr(s, AST_FOR(f).cond, retval)) { + pop_loop(s); + return -1; + } + + assert(vec_len(retval) == 1); + printf("%s -> %s;\n", (retval_at(*retval, 0)).s, top); + + pop_loop(s); + return 0; +} + +static int lower_expr_if(struct lower_state *s, struct ast_node *i, + struct vec *retval) +{ + semantic_error(i->scope->fctx, i, + "expr if unimplemented"); + return 0; +} + +static int lower_binop(struct lower_state *s, struct ast_node *i, + struct vec *retval) +{ + struct vec l = retval_create(); + struct vec r = retval_create(); + + if (lower_expr(s, AST_BINOP(i).left, &l)) { + retval_destroy(&l); + retval_destroy(&r); + return -1; + } + + if (lower_expr(s, AST_BINOP(i).right, &r)) { + retval_destroy(&l); + retval_destroy(&r); + return -1; + } + + assert(vec_len(&l) == 1); + assert(vec_len(&r) == 1); + + char *name = build_str("tmp%lli", (long long)s->uniq++); + struct retval ret = build_retval(REG_I27, name); + vec_append(retval, &ret); + + char *op = ""; + switch (AST_BINOP(i).op) { + case AST_ADD: op = "+"; break; + case AST_SUB: op = "-"; break; + case AST_MUL: op = "*"; break; + case AST_DIV: op = "/"; break; + case AST_REM: op = "%"; break; + case AST_LSHIFT: op = "<<"; break; + case AST_RSHIFT: op = ">>"; break; + case AST_LT: op = "<"; break; + case AST_GT: op = ">"; break; + case AST_LE: op = "<="; break; + case AST_GE: op = ">="; break; + case AST_NE: op = "!="; break; + case AST_EQ: op = "=="; break; + default: semantic_error(i->scope->fctx, i, + "unimplemented binary operation"); + retval_destroy(&l); + retval_destroy(&r); + return -1; + } + + printf("i27 %s = %s %s %s;\n", name, + (retval_at(l, 0)).s, + op, + (retval_at(r, 0)).s); + + retval_destroy(&l); + retval_destroy(&r); + return 0; +} + +static int lower_call(struct lower_state *s, struct ast_node *c, + struct vec *retval) +{ + assert(c->node_type == AST_CALL); + + struct vec call = retval_create(); + if (lower_expr(s, AST_CALL(c).expr, &call)) { + retval_destroy(&call); + return -1; + } + + /* collect all args */ + struct vec args = retval_create(); + foreach_node(a, AST_CALL(c).args) { + struct vec arg = retval_create(); + if (lower_expr(s, a, &arg)) { + retval_destroy(&arg); + retval_destroy(&args); + return -1; + } + + foreach_retval(ri, arg) { + struct retval r = retval_at(arg, ri); + /* very important! */ + r.s = strdup(r.s); + vec_append(&args, &r); + } + + retval_destroy(&arg); + } + + assert(vec_len(&call) == 1); + printf("%s (", (retval_at(call, 0)).s); + retval_destroy(&call); + + foreach_retval(ri, args) { + struct retval r = retval_at(args, ri); + printf("%s, ", r.s); + } + +#define IS_VOID(t) \ + (t->node_type == AST_TYPE && AST_TYPE(t).kind == AST_TYPE_PRIMITIVE && \ + AST_PRIMITIVE_TYPE(t).type == AST_VOID) + + if (!IS_VOID(c->type)) { + semantic_error(c->scope->fctx, c, + "only void return type implemented"); + retval_destroy(&args); + return -1; + } + + printf(") => ();\n"); + retval_destroy(&args); + return 0; +} + +static int lower_expr(struct lower_state *s, struct ast_node *e, + struct vec *retval) +{ + if (!e) + return 0; + + switch (e->node_type) { + case AST_ID: return lower_id(s, e, retval); + /* var is considered an expression in this case */ + case AST_VAR: return lower_var(s, e, retval); + case AST_CAST: return lower_cast(s, e, retval); + case AST_CONST: return lower_const(s, e, retval); + case AST_RETURN: return lower_return(s, e, retval); + case AST_ASSIGN: return lower_assign(s, e, retval); + case AST_BINOP: return lower_binop(s, e, retval); + case AST_CALL: return lower_call(s, e, retval); + case AST_IF: return lower_expr_if(s, e, retval); + default: + semantic_error(e->scope->fctx, e, + "unhandled expr in lowering"); + return -1; + } + + return 0; +} + +static int lower_block(struct lower_state *s, struct ast_node *body) +{ + assert(body->node_type == AST_BLOCK); + assert(!ast_flags(body, AST_FLAG_DOEXPR)); + struct ast_node *stmt = AST_BLOCK(body).body; + for (; stmt; stmt = stmt->next) { + if (lower_statement(s, stmt)) + return -1; + } + + return 0; +} + +static int lower_statement(struct lower_state *s, struct ast_node *n) +{ + struct vec retval = retval_create(); + + int ret = 0; + switch (n->node_type) { + case AST_RETURN: ret = lower_return(s, n, &retval); break; + case AST_IF: ret = lower_if(s, n, &retval); break; + case AST_FOR: ret = lower_for(s, n, &retval); break; + case AST_BLOCK: ret = lower_block(s, n); break; + default: ret = lower_expr(s, n, &retval); break; + } + + retval_destroy(&retval); + return ret; +} + +static int lower_proc(struct ast_node *n) +{ + assert(n->node_type == AST_PROC); + /* nobody uses the proc, so no need to do anything */ + if (n->uses == 0 && !ast_flags(AST_PROC(n).id, AST_FLAG_NOMANGLE)) + return 0; + + struct lower_state state = create_state(); + + /* name */ + struct ast_node *id = AST_PROC(n).id; + output_id(id); + + /* args */ + printf("("); + + struct ast_node *sign = AST_PROC(n).sign; + if (lower_params(&state, AST_SIGN_TYPE(sign).params)) { + destroy_state(&state); + return -1; + } + + /* no return type currently supported by qbt */ + printf(")\n"); + + /* body */ + printf("{\n"); + + if (lower_block(&state, AST_PROC(n).body)) { + destroy_state(&state); + return -1; + } + + printf("}\n"); + destroy_state(&state); + return 0; +} + +static int lower_actual(struct ast_node *n) +{ + assert(AST_TYPE(n).kind == AST_TYPE_CONSTRUCT); + return 0; +} + +static int _lower_actuals(struct scope *root) +{ + /* go through all child scopes but only do actual work on file-scope + * includes are allowed inside procs etc to make something only locally + * visible */ + for (struct scope *c = root->children; c; c = c->next) { + if (_lower_actuals(c)) + return -1; + } + + if (!scope_flags(root, SCOPE_FILE)) + return 0; + + for (struct visible *v = root->vars; v; v = v->next) { + assert(v->node); + if (lower_global_var(v->node)) + return -1; + } + + for (struct visible *p = root->procs; p; p = p->next) { + assert(p->node); + if (lower_proc(p->node)) + return -1; + } + + return 0; +} + +int lower_actuals(struct scope *root) +{ + int ret = _lower_actuals(root); + /* actuals are currently global, would it make more sense for them to be + * scope-local? */ + for (struct actual *a = root->actuals; a; a = a->next) { + assert(a->node); + if (lower_actual(a->node)) + return -1; + } + + return ret; +} @@ -22,13 +22,11 @@ * some way to make flag handling more generic */ static const char *cmdline_usage = - "ek compiler usage:\n" + "ek frontend usage:\n" " ek [-I <dir>...] [-o <outfile>] infile\n" " -h Show usage (this)\n" " -I <dir> Add directory to import path\n" " infile Top file(s) to compile\n" - " -o <outfile> Name of output assembly file\n" - " (infile minus file extension if not given)\n" ; /** Print usage of compiler. */ @@ -50,13 +48,8 @@ static void usage() int main(int argc, char *argv[]) { int opt; - const char *output = "e.t"; - while ((opt = getopt(argc, argv, "hI:o:")) != -1) { + while ((opt = getopt(argc, argv, "hI:")) != -1) { switch (opt) { - case 'o': - output = optarg; - break; - case 'I': add_import_path(optarg); break; @@ -84,5 +77,5 @@ int main(int argc, char *argv[]) } const char *input = argv[optind]; - return compile(input, output); + return compile(input); } diff --git a/src/ops.c b/src/ops.c deleted file mode 100644 index cc2650c..0000000 --- a/src/ops.c +++ /dev/null @@ -1,487 +0,0 @@ -#include <ek/ops.h> -#include <ek/scope.h> -#include <stdbool.h> -#include <stdlib.h> -#include <string.h> -#include <assert.h> - -/* hopefully not too difficult to follow what's goind on, but to start with we - * assume we have an effectively infinite amount of virtual registers and we - * move everything down into them. Typically the top output is used as input in - * some other step. */ - -static void set_reg(struct loc *loc, size_t reg) -{ - loc->kind = LOC_REG; - loc->reg = reg; -} - -static void set_mem(struct loc *loc, size_t reg, long long off, size_t width) -{ - loc->kind = LOC_MEM; - loc->reg = reg; - loc->off = off; - loc->width = width; -} - -static size_t trivial_type_width(struct ast_node *type) -{ - switch (AST_TYPE(type).kind) { - case AST_TYPE_POINTER: return 3; - case AST_TYPE_PRIMITIVE: { - if (AST_PRIMITIVE_TYPE(type).type == AST_I27) - return 3; - - if (AST_PRIMITIVE_TYPE(type).type == AST_I9) - return 1; - - abort(); - break; - } - default: abort(); - } - return 3; -} - -static struct ops *create_ops() -{ - struct ops *ops = calloc(1, sizeof(struct ops)); - - struct op *op = calloc(1, sizeof(struct op)); - op->opcode = OP_COMMENT; - op->string = strdup("start"); - - ops->base = op; - ops->head = op; - return ops; -} - -static void destroy_ops(struct ops *ops) -{ - if (!ops) - return; - - struct op *op = ops->base; - while (op) { - struct op *prev = op; - op = op->next; - - switch (prev->opcode) { - case OP_COMMENT: free((void *)prev->string); break; - case OP_LABEL: free((void *)prev->string); break; - default: - } - - free(prev); - } - - free(ops); -} - -static size_t next_virtual_reg() -{ - static size_t reg = 1; - return reg++; -} - -static int lower_op(struct ast_node *n, struct ops *ops); - -#define HEAD_OUTPUTS(ops) ops->head->outputs -static struct op *op_head(struct ops *ops) -{ - return ops->head; -} - -static struct op *append_op(struct ops *ops, enum opcode opcode) -{ - static size_t i = 1; - struct op *op = op_head(ops); - struct op *n = calloc(1, sizeof(struct op)); - n->opcode = opcode; - op->next = n; - op->loc = i++; - ops->head = n; - return n; -} - -/* this should really only be called after lifetime analysis, should I add in - * some checks against incorrect use...? */ -static struct op *insert_op_after(struct op *op, enum opcode opcode) -{ - struct op *n = calloc(1, sizeof(struct op)); - n->opcode = opcode; - n->next = op->next; - op->next = n; - return n; -} - -static int lower_proc(struct ast_node *n, struct ops *ops) -{ - struct op *op = append_op(ops, OP_LABEL); - /** @todo name mangling */ - struct ast_node *id = AST_PROC(n).id; - op->string = strdup(AST_ID(id).id); - int ret = lower_op(AST_PROC(n).body, ops); - if (ret) - return ret; - - return 0; -} - -static int lower_block(struct ast_node *n, struct ops *ops) -{ - struct ast_node *b = AST_BLOCK(n).body; - while (b) { - int ret = lower_op(b, ops); - if (ret) - return ret; - - b = b->next; - } - - return 0; -} - -static int lower_var(struct ast_node *n, struct ops *ops) -{ - struct ast_node *d = scope_find_var(n->scope, AST_VAR(n).id); - /* structs should be handled as well, would it be better to try and fit - * them into regs or just dump them on the stack to make sure we don't - * immediately run out of registers? */ - struct ast_node *type = d->type; - if (AST_TYPE(type).kind != AST_TYPE_PRIMITIVE - && AST_TYPE(type).kind != AST_TYPE_POINTER) { - semantic_error(n->scope->fctx, n, - "only trivial type lowering implemented"); - return -1; - } - - d->reg = next_virtual_reg(); - - if (AST_VAR(n).init) { - int ret = lower_op(AST_VAR(n).init, ops); - if (ret) - return ret; - - struct loc inputs = HEAD_OUTPUTS(ops); - /* work with only primitive types for now */ - assert(inputs.next == NULL); - - struct op *op = append_op(ops, OP_MV); - op->inputs = inputs; - set_reg(&op->outputs, d->reg); - } - - return 0; -} - -static int lower_cast(struct ast_node *n, struct ops *ops) -{ - /** @todo lob off extra trits or something? */ - return lower_op(AST_CAST(n).expr, ops); -} - -static int lower_const(struct ast_node *n, struct ops *ops) -{ - if (AST_CONST(n).kind != AST_CONST_INTEGER) { - semantic_error(n->scope->fctx, n, - "only integer constant lowering implemented"); - return -1; - } - - struct op *op = append_op(ops, OP_LI); - op->constant = AST_CONST(n).integer; - set_reg(&op->outputs, next_virtual_reg()); - return 0; -} - -static int lower_assign(struct ast_node *n, struct ops *ops) -{ - int ret = lower_op(AST_ASSIGN(n).from, ops); - if (ret) - return ret; - - struct loc from = HEAD_OUTPUTS(ops); - - ret = lower_op(AST_ASSIGN(n).to, ops); - if (ret) - return ret; - - struct loc to = HEAD_OUTPUTS(ops); - struct op *op = append_op(ops, OP_MV); - op->inputs = from; - op->outputs = to; - return 0; -} - -static int lower_unop(struct ast_node *n, struct ops *ops) -{ - if (AST_UNOP(n).op != AST_DEREF) { - semantic_error(n->scope->fctx, n, - "unop lowering not implemented"); - return -1; - } - - if (AST_UNOP(n).op == AST_DEREF) { - int ret = lower_op(AST_UNOP(n).expr, ops); - if (ret) - return ret; - - struct loc *l = &HEAD_OUTPUTS(ops); - if (l->kind == LOC_MEM) { - /* load value from memory and use it as the next step - * location */ - struct op *op = append_op(ops, OP_LDW); - op->inputs = *l; - /* hard coded 3 for now, pointer is three trytes */ - set_mem(&op->outputs, next_virtual_reg(), 0, 3); - return 0; - } - - /* use the value as if it was a memory location */ - size_t w = trivial_type_width(n->type); - set_mem(l, l->reg, 0, w); - return 0; - } - - return -1; -} - -static int lower_id(struct ast_node *n, struct ops *ops) -{ - struct ast_node *d = scope_find_var(n->scope, n); - assert(d->reg); - - /* feels like a massive hack, but gives fairly readable debug output so - * I'll keep it for now. Eventually once I figure out what I'm doing - * this whole system will probably have to be rewritten anyway. */ - struct op *op = append_op(ops, OP_COMMENT); - op->string = strdup(AST_ID(n).id); - set_reg(&op->outputs, d->reg); - return 0; -} - -static int lower_ret(struct ast_node *n, struct ops *ops) -{ - if (AST_RETURN(n).expr) { - int ret = lower_op(AST_RETURN(n).expr, ops); - if (ret) - return ret; - } - - struct op *op = append_op(ops, OP_RET); - if (AST_RETURN(n).expr) - set_reg(&op->inputs, HEAD_OUTPUTS(ops).reg); - - return 0; -} - -static int lower_op(struct ast_node *n, struct ops *ops) -{ - int ret = 0; - switch (n->node_type) { - case AST_PROC: ret = lower_proc(n, ops); break; - case AST_BLOCK: ret = lower_block(n, ops); break; - case AST_VAR: ret = lower_var(n, ops); break; - case AST_CAST: ret = lower_cast(n, ops); break; - case AST_CONST: ret = lower_const(n, ops); break; - case AST_ASSIGN: ret = lower_assign(n, ops); break; - case AST_UNOP: ret = lower_unop(n, ops); break; - case AST_ID: ret = lower_id(n, ops); break; - case AST_RETURN: ret = lower_ret(n, ops); break; - default: - semantic_error(n->scope->fctx, n, "unimplemented lowering"); - return -1; - } - - if (ret) - return ret; - - return 0; -} - -static void print_locs(struct loc *locs) -{ - if (locs->kind == LOC_NONE) - return; - - printf(" ( "); - - while (locs) { - if (locs->kind == LOC_MEM) - printf("%lld(", locs->off); - - printf("r%zd", locs->reg); - - if (locs->kind == LOC_MEM) - printf(", %zd)", locs->width); - - printf(" "); - locs = locs->next; - } - - printf(")"); -} - -static void print_op(struct op *op) -{ - if (!op) - return; - - print_locs(&op->outputs); - putchar(' '); - switch (op->opcode) { - case OP_LABEL: printf("%s:", op->string); break; - case OP_COMMENT: printf("/* %s */", op->string); break; - case OP_LI: printf("li %lld", op->constant); break; - case OP_LA: printf("la %s", op->string); break; - case OP_ADD: printf("add"); break; - case OP_ADDI: printf("addi %lld", op->constant); break; - case OP_STT: printf("stt"); break; - case OP_LDT: printf("ldt"); break; - case OP_STW: printf("stw"); break; - case OP_LDW: printf("ldw"); break; - case OP_RET: printf("ret"); break; - case OP_MV: printf("mv"); break; - default: printf("unimp"); break; - } - putchar(' '); - print_locs(&op->inputs); - printf("\n"); -} - -static void print_ops(struct ops *ops) -{ - if (!ops) - return; - - struct op *base = ops->base; - while (base) { - print_op(base); - base = base->next; - } -} - -static enum opcode st_opc(struct loc *loc) -{ - switch (loc->width) { - case 1: return OP_STT; - case 3: return OP_STW; - } - - abort(); - return OP_STW; -} - -static enum opcode ld_opc(struct loc *loc) -{ - switch (loc->width) { - case 1: return OP_LDT; - case 3: return OP_LDW; - } - - abort(); - return OP_LDW; -} - -static int realize_moves(struct ops *ops) -{ - /* completely arbitrary and *will* have to be made better in the near - * future */ - static const size_t tmp_reg = 9; - struct op *prev = NULL; - struct op *op = ops->base; - for (; op; prev = op, op = op->next) { - if (op->opcode != OP_MV) { - continue; - } - - struct loc *i = &op->inputs; - struct loc *o = &op->outputs; - assert(i->next == NULL); - assert(i->next == NULL); - - if (i->kind == LOC_REG && o->kind == LOC_REG) { - /* if move is between the same register, skip it */ - if (i->reg == o->reg && prev) - prev->next = op->next; - - } - else if (i->kind == LOC_REG && o->kind == LOC_MEM) { - op->opcode = st_opc(o); - } - else if (i->kind == LOC_MEM && o->kind == LOC_REG) { - op->opcode = ld_opc(i); - } - else if (i->kind == LOC_MEM && o->kind == LOC_MEM) { - op->opcode = ld_opc(i); - /* our input/output are references, so call this before - * setting registers for our original operation. Also, - * at this point the lifetime stuff is finished, so no - * big deal that our node IDs change */ - struct op *n = insert_op_after(op, st_opc(o)); - set_mem(&n->outputs, o->reg, o->off, o->width); - set_reg(&n->inputs, tmp_reg); - set_reg(&op->outputs, tmp_reg); - } - } - - return 0; -} - -static int alloc_regs(struct ops *ops) -{ - /** @todo analyze lifetime, for now just convert moves to correct ldst - * etc. */ - /** @todo lifetime analysis could be done by looping over all ops, and - * when we encounter a virtual register we haven't seen before, add it - * to a list. When we encounter it used again, extend its lifetime to - * wherever we are in the function. */ - return realize_moves(ops); -} - -int lower_ops(struct scope *root, const char *fname) -{ - FILE *f = fopen(fname, "w"); - - /* main should probably be mangled here as well */ - fprintf(f, "jal x21, main\n"); - /* tell simulator to turn off (very much temp) */ - fprintf(f, "li x1, 3\n"); - fprintf(f, "csrrw mpower, x0, x1\n"); - - /* this can potentially be parallelized in the future */ - int ret = 0; - for (struct actual *a = root->actuals; a; a = a->next) { - assert(a->node); - - struct ops *ops = create_ops(); - if ((ret = lower_op(a->node, ops))) { - destroy_ops(ops); - break; - } - - printf("Lowered ops before lifetime analysis:\n"); - print_ops(ops); - - if ((ret = alloc_regs(ops))) { - destroy_ops(ops); - break; - } - - printf("Lowered ops after lifetime analysis:\n"); - print_ops(ops); - - if ((ret = print_asm(ops, f))) { - /* kind of silly as of now but eh */ - destroy_ops(ops); - break; - } - - destroy_ops(ops); - } - - fclose(f); - return ret; -} - diff --git a/src/parser.y b/src/parser.y index 14fbb50..d6aefc8 100644 --- a/src/parser.y +++ b/src/parser.y @@ -375,11 +375,11 @@ expr * "do" looks a bit cleaner. There is the slight annoyance that a very * long do {} ... might have a 'while'; at the end, not sure if do ... * while should be removed from the language altogether or what */ - | "do" body { $$ = $2; } - | "do" expr_if { $$ = $2; } - | "do" "const" expr_if { $$ = $3; } - | "do" switch { $$ = $2; } - | "do" "const" switch { $$ = $3; } + | "do" body { $$ = $2; ast_set_flags($$, AST_FLAG_DOEXPR); } + | "do" expr_if { $$ = $2; ast_set_flags($$, AST_FLAG_DOEXPR); } + | "do" "const" expr_if { $$ = $3; ast_set_flags($$, AST_FLAG_DOEXPR); } + | "do" switch { $$ = $2; ast_set_flags($$, AST_FLAG_DOEXPR); } + | "do" "const" switch { $$ = $3; ast_set_flags($$, AST_FLAG_DOEXPR); } | expr "(" opt_exprs ")" { $$ = gen_call($1, $3, src_loc(@$)); } | expr "[" expr "]" { $$ = gen_arr_access($1, $3, src_loc(@$)); } | "sizeof" expr { $$ = gen_sizeof($2, src_loc(@$)); } diff --git a/src/scope.c b/src/scope.c index 48d3bc5..205e858 100644 --- a/src/scope.c +++ b/src/scope.c @@ -32,7 +32,7 @@ struct scope *create_scope() return scope; } -void destroy_visible(struct scope *scope, struct visible *visible) +void destroy_visible(struct visible *visible) { struct visible *prev = visible, *cur; if (prev) @@ -53,10 +53,10 @@ void destroy_scope(struct scope *scope) free((void *)scope->fctx.fname); } - destroy_visible(scope, scope->vars); - destroy_visible(scope, scope->procs); - destroy_visible(scope, scope->macros); - destroy_visible(scope, scope->types); + destroy_visible(scope->vars); + destroy_visible(scope->procs); + destroy_visible(scope->macros); + destroy_visible(scope->types); struct scope *prev = scope->children, *cur; if (prev) @@ -179,7 +179,8 @@ int scope_add_type(struct scope *scope, struct ast_node *id, int scope_add_macro(struct scope *scope, struct ast_node *macro) { assert(macro->node_type == AST_MACRO_CONSTRUCT); - struct ast_node *exists = file_scope_find_macro(scope, AST_MACRO_CONSTRUCT( + struct ast_node *exists = file_scope_find_macro(scope, + AST_MACRO_CONSTRUCT( macro).id); if (exists) { semantic_error(scope->fctx, macro, "macro redefined"); @@ -362,7 +363,7 @@ static int add_actual(struct actual *actuals, struct ast_node *node) return 0; } - /* TODO: check that there isn't already an actual like ours? */ + /* TODO: check that there isn't already an actual like ours */ struct actual *actual = calloc(1, sizeof(struct actual)); if (!actual) return -1; diff --git a/src/vec.c b/src/vec.c new file mode 100644 index 0000000..3f1aac6 --- /dev/null +++ b/src/vec.c @@ -0,0 +1,60 @@ +#include <stdlib.h> +#include <assert.h> +#include <string.h> + +#include <ek/vec.h> + +struct vec vec_create(size_t ns) +{ + return (struct vec) { + .n = 0, + .s = 1, + .ns = ns, + .buf = malloc(ns), + }; +} + +size_t vec_len(struct vec *v) +{ + return v->n; +} + +void *vec_at(struct vec *v, size_t i) +{ + assert(i < v->n && "out of vector bounds"); + return v->buf + i * v->ns; +} + +void *vec_back(struct vec *v) +{ + assert(v->n); + return v->buf + (v->n - 1) * v->ns; +} + +void *vec_pop(struct vec *v) +{ + assert(v->n && "attempting to pop empty vector"); + v->n--; + return v->buf + v->n * v->ns; +} + +void vec_append(struct vec *v, void *n) +{ + v->n++; + if (v->n >= v->s) { + v->s *= 2; + v->buf = realloc(v->buf, v->s * v->ns); + } + + void *p = vec_at(v, v->n - 1); + memcpy(p, n, v->ns); +} + +void vec_reset(struct vec *v) +{ + v->n = 0; +} + +void vec_destroy(struct vec *v) { + free(v->buf); +} diff --git a/tests/if.ek b/tests/if.ek new file mode 100644 index 0000000..ba8077b --- /dev/null +++ b/tests/if.ek @@ -0,0 +1,23 @@ +typedef i9 {} +typedef i27 {} + +putchar(i9 c) +{ + *i9 p = 19683 as *i9; + p* = c; +} + +main() +{ + if 0 { + putchar('0'); + } + else if 1 { + putchar('1'); + } + else { + putchar('1'); + } + + putchar('\n'); +} diff --git a/tests/if2.ek b/tests/if2.ek new file mode 100644 index 0000000..b41b389 --- /dev/null +++ b/tests/if2.ek @@ -0,0 +1,29 @@ +typedef i9 {} +typedef i27 {} + +putchar(i9 c) +{ + *i9 p = 19683 as *i9; + p* = c; +} + +main() +{ + if 0 { + for i27 i = 0; i < 5; i = i + 1 { + putchar('0' + i); + } + } + else if 1 { + for i27 i = 0; i < 5; i = i + 1 { + putchar('A' + i); + } + } + else { + for i27 i = 0; i < 5; i = i + 1 { + putchar('K' + i); + } + } + + putchar('\n'); +} diff --git a/tests/loop.ek b/tests/loop.ek new file mode 100644 index 0000000..301e7ea --- /dev/null +++ b/tests/loop.ek @@ -0,0 +1,15 @@ +typedef i9 {} +typedef i27 {} + +putchar(i9 c) +{ + *i9 p = 19683 as *i9; + p* = c; +} + +main() +{ + for i27 i = 0; i < 5; i = i + 1 { + putchar('A' + i); + } +} diff --git a/tests/loops.ek b/tests/loops.ek deleted file mode 100644 index 5b6f702..0000000 --- a/tests/loops.ek +++ /dev/null @@ -1,4 +0,0 @@ -/* this should error out with some decent message about type loops or something - * */ -f(=>typeof main()){} -main(=> typeof f()){} diff --git a/tests/pointer_literal.ek b/tests/pointer_literal.ek index 09889d2..dbb2bef 100644 --- a/tests/pointer_literal.ek +++ b/tests/pointer_literal.ek @@ -1,5 +1,8 @@ +typedef i9 {} +typedef i27 {} + main() { *i9 p = 19683 as *i9; - *p = 'A' as i9; + p* = 'A' as i9; } |
