ox

The Ox programming language, compiler and tools (WIP)
Log | Files | Refs | README | LICENSE

commit 76bf8fafa4fa760fbe7c5ecf65ef60c753a1b7a9
parent a670812602ab986eb7543f905b4734b2f94de40c
Author: citbl <citbl@citbl.org>
Date:   Thu, 25 Jun 2026 17:14:22 +1000

gen

Diffstat:
Asrc/gen.c | 1428+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/gen.h | 20--------------------
Dsrc/gen/gen.c | 1372-------------------------------------------------------------------------------
3 files changed, 1428 insertions(+), 1392 deletions(-)

diff --git a/src/gen.c b/src/gen.c @@ -0,0 +1,1428 @@ +#include "gen.h" +#include "parser.h" +#include "utils.h" +#include "sem.h" + +#include <_string.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <sys/param.h> + +static gcc_jit_type* type_boolean; +static gcc_jit_type* type_int; +static gcc_jit_type* type_i8; +static gcc_jit_type* type_i16; +static gcc_jit_type* type_i32; +static gcc_jit_type* type_i64; +static gcc_jit_type* type_i128; +static gcc_jit_type* type_u8; +static gcc_jit_type* type_u16; +static gcc_jit_type* type_u32; +static gcc_jit_type* type_u64; +static gcc_jit_type* type_u128; +static gcc_jit_type* type_f32; +static gcc_jit_type* type_f64; +static gcc_jit_type* type_f128; +static gcc_jit_type* type_uint; +static gcc_jit_type* type_void; +static gcc_jit_type* type_cstr; +static gcc_jit_type* type_char; +static gcc_jit_type* type_voidp; + +static const char* type_func = "function"; +static const char* type_var = "variable"; +static const char* type_struct = "struct"; + +static gcc_jit_location* +loc_from_node(Gen* gen, Node* node) +{ + if (node->filename == NULL) return NULL; + return gcc_jit_context_new_location(gen->ctx, node->filename, node->line, node->col); +} + +static void +push_loop(Gen* gen, gcc_jit_block* brk, gcc_jit_block* cont) +{ + LoopContext* lctx = (LoopContext*)malloc(sizeof(LoopContext)); + if (lctx == NULL) { panic("push_loop: could not alloc"); } + lctx->break_target = brk; + lctx->continue_target = cont; + lctx->prev = gen->loop; + gen->loop = lctx; +} + +static void +pop_loop(Gen* gen) +{ + LoopContext* lctx = gen->loop; + gen->loop = lctx->prev; + free(lctx); +} + +static inline gcc_jit_block* +current_break(Gen* gen) +{ + return gen->loop ? gen->loop->break_target : NULL; +} + +static inline gcc_jit_block* +current_continue(Gen* gen) +{ + return gen->loop ? gen->loop->continue_target : NULL; +} + +__attribute__((unused)) static const char* +get_english_type(gcc_jit_type* T) +{ + return gcc_jit_object_get_debug_string(gcc_jit_type_as_object(T)); +} + +Gen +gen_init(Scope* scope, const char* src, Node* node, bool quiet) +{ + if (scope == NULL || src == NULL) { panic("gen_init: no Scope or AST provided"); } + + gcc_jit_context* ctx; + + ctx = gcc_jit_context_acquire(); + + if (!ctx) { panic("could not acquire gcc jit context"); } + + // needs loc* to work + // gcc_jit_context_set_bool_option(ctx, GCC_JIT_BOOL_OPTION_DEBUGINFO, + // 1); high level + + gcc_jit_context_set_bool_option(ctx, GCC_JIT_BOOL_OPTION_DEBUGINFO, 1); + + if (quiet == false) { gcc_jit_context_set_bool_option(ctx, GCC_JIT_BOOL_OPTION_DUMP_INITIAL_GIMPLE, 1); } + + // gcc_jit_context_set_bool_option(ctx, + // GCC_JIT_BOOL_OPTION_DUMP_SUMMARY, 1); + + gcc_jit_context_set_str_option(ctx, GCC_JIT_STR_OPTION_PROGNAME, "ox"); + // keep FP + gcc_jit_context_add_driver_option(ctx, "-fno-omit-frame-pointer"); + + gcc_jit_context_set_int_option(ctx, + GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, + /*0-3 for O3*/ 0); + + type_boolean = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_BOOL); + + type_int = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_INT64_T); + type_i8 = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_INT8_T); + type_i16 = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_INT16_T); + type_i32 = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_INT32_T); + type_i64 = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_INT64_T); + type_i128 = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_INT128_T); + + type_u8 = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UINT8_T); + type_u16 = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UINT16_T); + type_u32 = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UINT32_T); + type_u64 = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UINT64_T); + type_u128 = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UINT128_T); + + type_f32 = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_FLOAT); + type_f64 = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_DOUBLE); + type_f128 = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_LONG_DOUBLE); + + type_uint = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UINT64_T); + type_char = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_CHAR); + type_void = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_VOID); + type_cstr = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_CONST_CHAR_PTR); + type_voidp = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_VOID_PTR); + /* gcc_jit_type_dyncast_function_ptr_type; */ + + gcc_jit_location* loc = gcc_jit_context_new_location(ctx, node->filename, 0, 0); + + gcc_jit_param* pm_puts[] = { gcc_jit_context_new_param(ctx, loc, type_cstr, "s") }; + gcc_jit_function* fn_puts = gcc_jit_context_new_function(ctx, loc, GCC_JIT_FUNCTION_IMPORTED, type_int, "puts", 1, pm_puts, 0); + + gcc_jit_param* pm_printf[] = { gcc_jit_context_new_param(ctx, loc, type_cstr, "fmt") }; + gcc_jit_function* fn_printf = gcc_jit_context_new_function(ctx, + loc, + GCC_JIT_FUNCTION_IMPORTED, + type_int, + "printf", + 1, + pm_printf, + /*is_variadic=*/1); + + return (Gen) { + .ctx = ctx, + .scope = scope, + .prev_func = NULL, + .curr_func = NULL, + .prev_block = NULL, + .curr_block = NULL, + .puts_fn = fn_puts, + .printf_fn = fn_printf, + .src = src, + }; +} + +static void +push_scope(Gen* gen) +{ + printf("push_scope\n"); + Scope* scope = calloc(1, sizeof(Scope)); + *scope = (Scope) { 0 }; + scope->symbols = (Symbol**)calloc(16, sizeof(Symbol*)); + if (scope->symbols == NULL) panic("push_scope: symbols: could not alloc"); + scope->cap = 16; + scope->len = 0; + + scope->parent = gen->scope; + gen->scope = scope; +} + +static void +pop_scope(Gen* gen) +{ + printf("pop_scope\n"); + Scope* s = gen->scope; + gen->scope = s->parent; +} + +static Symbol* +lookup_symbol(Gen* gen, Scope* scope, Span name) +{ + const char* ident_name = span_str(gen->src, name, (char[IDENTSZ]) { 0 }); + printf("lookup_symbol: %s\n", ident_name); + // Look up the symbol in the current scope // TODO look up in parent + // scope with utility function and recursion + for (size_t i = 0; i < scope->len; i++) { + Symbol* sym = scope->symbols[i]; + const char* sym_name = span_str(gen->src, sym->name, (char[IDENTSZ]) { 0 }); + // printf("symbol %s\n", sym_name); + // printf("ident %s\n", ident_name); + if (strcmp(sym_name, ident_name) == 0) { + printf("found symbol %s\n", sym_name); + return sym; + } + } + printf("find_symbol: not found locally: %s\n", ident_name); + + if (scope->parent != NULL) { + printf("looking up symbol %s in parent\n", ident_name); + return lookup_symbol(gen, scope->parent, name); + } + + return NULL; +} + +static void +add_symbol(Gen* gen, Symbol* sym) +{ + if (gen->scope->len == gen->scope->cap) { + gen->scope->cap *= 2; + gen->scope->symbols = (Symbol**)realloc(gen->scope->symbols, sizeof(Symbol*) * gen->scope->cap); + } + + gen->scope->symbols[gen->scope->len++] = sym; + + printf("add_symbol: we now have %zu symbols: ", gen->scope->len); + for (size_t i = 0; i < gen->scope->len; i++) { + Symbol* symy = gen->scope->symbols[i]; + const char* name = span_str(gen->src, symy->name, (char[IDENTSZ]) { 0 }); + printf("%s, ", name); + } + printf("\n"); +} + +static gcc_jit_rvalue* +handle_ident_call(Gen* gen, Node* node) +{ + // Look up the symbol in the current scope // TODO look up in parent + // scope with utility function and recursion + Symbol* sym = lookup_symbol(gen, gen->scope, node->data.ident.name); + + if (sym == NULL) { + panic_at(node, + "handle_ident_call: undefined variable: %s\n", + span_str(gen->src, node->data.ident.name, (char[IDENTSZ]) { 0 })); + } + return gcc_jit_lvalue_as_rvalue(sym->d.lvalue); +} + +static gcc_jit_rvalue* handle_expr(Gen*, Node*); + +static inline int +is_intlike(gcc_jit_context* ctx, gcc_jit_type* t) // TODO support all new types +{ + return t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_INT) || t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_INT) + || t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_SHORT) + || t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_SHORT) + || t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_LONG) + || t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_LONG) + || t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_LONG_LONG) + || t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_LONG_LONG) + || t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_INT64_T) || t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_INT32_T) + || t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_CHAR) + || t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_CHAR); +} + +static inline int +is_floatlike(gcc_jit_context* ctx, gcc_jit_type* t) +{ + return t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_FLOAT) || t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_DOUBLE) + || t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_LONG_DOUBLE); +} + +static inline int +is_cstr(gcc_jit_context* ctx, gcc_jit_type* t) +{ + return t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_CONST_CHAR_PTR); +} + +// > < >= <= + - / * % +static gcc_jit_rvalue* +handle_bin_expr(Gen* gen, Node* node, int gcc_jit_op, bool cmp) +{ + Node* lhs = node->data.binary_expr.lhs; + Node* rhs = node->data.binary_expr.rhs; + gcc_jit_context* ctx = gen->ctx; + gcc_jit_location* loc = loc_from_node(gen, node); + + gcc_jit_rvalue* L = handle_expr(gen, lhs); + gcc_jit_rvalue* R = handle_expr(gen, rhs); + gcc_jit_type* Lt = gcc_jit_rvalue_get_type(L); + gcc_jit_type* Rt = gcc_jit_rvalue_get_type(R); + gcc_jit_rvalue* result = NULL; + + // floats: cast both to double + if (is_floatlike(ctx, Lt) || is_floatlike(ctx, Rt)) { + gcc_jit_type* T = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_DOUBLE); + L = gcc_jit_context_new_cast(ctx, loc, L, T); + R = gcc_jit_context_new_cast(ctx, loc, R, T); + result = cmp ? gcc_jit_context_new_comparison(ctx, loc, gcc_jit_op, L, R) + : gcc_jit_context_new_binary_op(ctx, loc, gcc_jit_op, T, L, R); + ; + } + + // integers: cast both to signed/unsigned long long (cheap, predictable) + if (is_intlike(ctx, Lt) && is_intlike(ctx, Rt)) { + int any_unsigned = (Lt == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_CHAR)) + || (Lt == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_SHORT)) + || (Lt == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_INT)) + || (Lt == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_LONG)) + || (Lt == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_LONG_LONG)) + || (Rt == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_CHAR)) + || (Rt == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_SHORT)) + || (Rt == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_INT)) + || (Rt == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_LONG)) + || (Rt == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_LONG_LONG)); + + gcc_jit_type* T = gcc_jit_context_get_type(ctx, any_unsigned ? GCC_JIT_TYPE_UINT64_T : GCC_JIT_TYPE_INT64_T); + + L = gcc_jit_context_new_cast(ctx, loc, L, T); + R = gcc_jit_context_new_cast(ctx, loc, R, T); + result = cmp ? gcc_jit_context_new_comparison(ctx, loc, gcc_jit_op, L, R) + : gcc_jit_context_new_binary_op(ctx, loc, gcc_jit_op, T, L, R); + } + + // pointers: either reject or compare addresses (UB in C for unrelated objs). + if ( +#ifdef GCC_JIT_HAVE_gcc_jit_type_is_pointer + gcc_jit_type_is_pointer(Lt) && gcc_jit_type_is_pointer(Rt) +#else + 0 +#endif + ) { + gcc_jit_type* T = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_LONG_LONG); + L = gcc_jit_context_new_cast(ctx, loc, L, T); + R = gcc_jit_context_new_cast(ctx, loc, R, T); + result = cmp ? gcc_jit_context_new_comparison(ctx, loc, gcc_jit_op, L, R) + : gcc_jit_context_new_binary_op(ctx, loc, gcc_jit_op, T, L, R); + } + + return result; +} + +static gcc_jit_rvalue* +handle_binary_expr(Gen* gen, Node* node) +{ + int gcc_jit_op = -1; + OpType op = node->data.binary_expr.op; + + switch (op) { + case OP_PLUS: + gcc_jit_op = GCC_JIT_BINARY_OP_PLUS; + return handle_bin_expr(gen, node, gcc_jit_op, false); + break; + case OP_MINUS: + gcc_jit_op = GCC_JIT_BINARY_OP_MINUS; + return handle_bin_expr(gen, node, gcc_jit_op, false); + break; + case OP_MUL: + gcc_jit_op = GCC_JIT_BINARY_OP_MULT; + return handle_bin_expr(gen, node, gcc_jit_op, false); + break; + case OP_DIV: + gcc_jit_op = GCC_JIT_BINARY_OP_DIVIDE; + return handle_bin_expr(gen, node, gcc_jit_op, false); + break; + case OP_MOD: + gcc_jit_op = GCC_JIT_BINARY_OP_MODULO; + return handle_bin_expr(gen, node, gcc_jit_op, false); + break; + case OP_LT: + gcc_jit_op = GCC_JIT_COMPARISON_LT; + break; + case OP_GT: + gcc_jit_op = GCC_JIT_COMPARISON_GT; + break; + case OP_GT_EQ: + gcc_jit_op = GCC_JIT_COMPARISON_GE; + break; + case OP_LT_EQ: + gcc_jit_op = GCC_JIT_COMPARISON_LE; + break; + default: + printf("handle_binary_expr unhandled OpType %d (can be ignored in var assignment " + "(for now))\n", + op); + return NULL; + break; + } + + if (gcc_jit_op < 0) { + // unsupported types (e.g., strings or structs) + printf("handle_binary_expr OP_LT: unsupported operand types\n"); + return NULL; + } + + return handle_bin_expr(gen, node, gcc_jit_op, true); +} + +static gcc_jit_rvalue* +handle_unary_expr(Gen* gen, Node* node) +{ + gcc_jit_context* ctx = gen->ctx; + gcc_jit_block* bb = gen->curr_block; + + Node* opnd = node->data.unary_expr.operand; + Symbol* sym = lookup_symbol(gen, gen->scope, opnd->data.ident.name); + if (sym == NULL) { + panic_at(node, + "handle_unary_expr: undefined variable: %s\n", + span_str(gen->src, opnd->data.ident.name, (char[IDENTSZ]) { 0 })); + } + + gcc_jit_lvalue* lv = sym->d.lvalue; + if (lv == NULL) { + printf("handle_unary_expr: no lvalue\n"); + return NULL; + } + gcc_jit_rvalue* orig = gcc_jit_lvalue_as_rvalue(lv); + gcc_jit_type* ty = gcc_jit_rvalue_get_type(orig); + gcc_jit_rvalue* one = gcc_jit_context_one(ctx, ty); + + gcc_jit_rvalue* inc = gcc_jit_context_new_binary_op(ctx, NULL, GCC_JIT_BINARY_OP_PLUS, ty, orig, one); + switch (node->data.unary_expr.op) { + case OPER_POSTINC: { // i++ + + if (!sym->is_variadic) { + panic_at(node, + "OPER_POSTINC: cannot change non variadic variable %s\n", + span_str(gen->src, opnd->data.ident_type.name, (char[IDENTSZ]) { 0 })); + } else if (sym->is_comp_time) { + panic_at(node, + "OPER_POSTINC: cannot change comp time constant %s\n", + span_str(gen->src, opnd->data.ident_type.name, (char[IDENTSZ]) { 0 })); + } + gcc_jit_block_add_assignment(bb, NULL, lv, inc); + return orig; // return value before incr as ++ is postfix + + break; + } + default: + printf("handle_unary_expr, unhandled op %d\n", node->data.unary_expr.op); + return NULL; + break; + } + return NULL; +} + +static gcc_jit_rvalue* +emit_literal_bool(Gen* gen, Node* node) +{ + int v = node->data.boolean.value ? 1 : 0; + return gcc_jit_context_new_rvalue_from_int(gen->ctx, type_boolean, v); +} + +static gcc_jit_rvalue* +emit_literal_string(Gen* gen, Node* node) +{ + size_t len = node->data.string.value.end - node->data.string.value.start; + char* str = calloc(len + 1, sizeof(char)); + if (str == NULL) panic_at(node, "emit_literal_string: could not alloc"); + memcpy(str, gen->src + node->data.string.value.start, len); + str[len] = '\0'; + return gcc_jit_context_new_string_literal(gen->ctx, str); +} + +static gcc_jit_rvalue* +emit_literal_int(Gen* gen, Node* node) +{ + return gcc_jit_context_new_rvalue_from_int(gen->ctx, type_int, (int)node->data.number.value); +} + +static gcc_jit_rvalue* +emit_literal_float(Gen* gen, Node* node) +{ + return gcc_jit_context_new_rvalue_from_double(gen->ctx, type_f32, node->data.number.value); +} + +static void +build_program(Gen* gen, Node* node) +{ + size_t cnt = node->data.program.len; + for (size_t i = 0; i < cnt; i++) { + gen_next(gen, node->data.program.decl[i]); + } +} + +static gcc_jit_rvalue* +lower_builtin_print(Gen* gen, Node* node) +{ + printf("lower_builtin_print\n"); + + size_t argc = node->data.call_expr.len; + + // for (size_t i = 0; i < argc; i++) { + // Node* arg = node->data.call_expr.args[i]; + // } + + // 1-arg, treat as puts(arg) + if (argc == 1) { + gcc_jit_rvalue* arg = handle_expr(gen, node->data.call_expr.args[0]); + gcc_jit_type* t = gcc_jit_rvalue_get_type(arg); + gcc_jit_location* loc = loc_from_node(gen, node->data.call_expr.args[0]); + // print a string + if (is_cstr(gen->ctx, t)) { + gcc_jit_rvalue* args[] = { arg }; + return gcc_jit_context_new_call(gen->ctx, loc, gen->puts_fn, 1, args); + } + // print an integer or bool + else if (is_intlike(gen->ctx, t) || t == type_boolean) { + // cast to int for a clean %d + // gcc_jit_type* t_int + // = gcc_jit_context_get_type(gen->ctx, GCC_JIT_TYPE_INT64_T); + gcc_jit_rvalue* fmt = gcc_jit_context_new_string_literal(gen->ctx, "%d\n"); + gcc_jit_rvalue* ival = arg; + gcc_jit_rvalue* args[] = { fmt, ival }; + return gcc_jit_context_new_call(gen->ctx, loc, gen->printf_fn, 2, args); + } + // cast common cases to const char* + // if (gcc_jit_rvalue_get_type(arg) != type_cstr) arg = + // gcc_jit_context_new_cast(gen->ctx, loc_from_node(gen, node), + // arg, type_cstr); + + // gcc_jit_rvalue* args[] = { arg }; + // return gcc_jit_context_new_call( + // gen->ctx, loc_from_node(gen, node), gen->puts_fn, 1, args); + + // gcc_jit_type* type_of_args = gcc_jit_rvalue_get_type(arg); + // switch (type_of_args) { + // case GCC_JIT_TYPE_CONST_CHAR_PTR: + + // break; + // default: + // printf("lower_builtin_print: unhandled type passed %s\n", + // type_of_args); break; + // } + } + + return NULL; + + // softpanic("we don't currently handle formatted strings to print"); + + // n>=1, treat as printf(fmt, ...) // Part of TODO about args as list + // and not + // + // through each args, form the ("formatted %s string %d etc.", str, + // intv) for clib's printf + + // TODO we're talking about formatting here, which we plan on doing as a + // string interpolation, something along the lines of {{variable}} + // without defining its type would involve lookup split of the string + // and then formatting + + // we need to discuss and decide what we'd do when the user inevitably + // would print out a ref to a struct. Do we say [[struct]] or do we have + // some automatic unwrap and display of struct data... probably, yes. + + gcc_jit_rvalue** args = (gcc_jit_rvalue**)calloc(argc, sizeof(gcc_jit_rvalue*)); + + for (size_t i = 0; i < argc; i++) { + gcc_jit_rvalue* arg = handle_expr(gen, node->data.call_expr.args[i]); + if (i == 0) { + if (gcc_jit_rvalue_get_type(arg) != type_cstr) { + // note this is probably not going to work as + // limited cast supported and string isn't one + // of them + arg = gcc_jit_context_new_cast(gen->ctx, loc_from_node(gen, node), arg, type_cstr); + } + } else { + // + // simple widening for common scalar types + // + gcc_jit_type* ty = gcc_jit_rvalue_get_type(arg); + if (ty == type_int) { + arg = gcc_jit_context_new_cast(gen->ctx, loc_from_node(gen, node), arg, type_cstr); + } else if (ty == type_f32) { // TODO print() for other types + // variadics already promote float→double; + // double is + } else if (ty == type_cstr) { + // leave as const char* + } else if (ty == type_boolean) { + arg = gcc_jit_context_new_cast(gen->ctx, loc_from_node(gen, node), arg, type_cstr); + } else { + // fallback: pass pointer as void* + arg = gcc_jit_context_new_cast(gen->ctx, + loc_from_node(gen, node), + arg, + gcc_jit_context_get_type(gen->ctx, GCC_JIT_TYPE_VOID_PTR)); + } + } + // TODO auto grow + args[i] = arg; + } + return gcc_jit_context_new_call(gen->ctx, NULL, gen->printf_fn, argc, args); + return NULL; +} + +static gcc_jit_function* +lookup_function(Gen* gen, Scope* scope, const char* func_name) +{ + for (size_t i = 0; i < scope->len; i++) { + Symbol* sym = scope->symbols[i]; + + if (sym->ctype == type_voidp) { + const char* name = span_str(gen->src, sym->name, (char[IDENTSZ]) { 0 }); + if (strcmp(name, func_name) == 0) { + return sym->d.funcvalue; // + } + } + } + + printf("lookup_function: not found locally: %s\n", func_name); + + if (scope->parent != NULL) { + printf("looking up function %s in parent\n", func_name); + return lookup_function(gen, scope->parent, func_name); + } + + return NULL; +} + +static gcc_jit_rvalue* +handle_func_call(Gen* gen, Node* node) +{ + Node* fcallee = node->data.call_expr.callee; + const char* func_name = span_str(gen->src, fcallee->data.ident.name, (char[IDENTSZ]) { 0 }); + + // short circuit to print + if (strcmp(func_name, "print") == 0) return lower_builtin_print(gen, node); + + gcc_jit_function* callee = lookup_function(gen, gen->scope, func_name); + gcc_jit_location* loc = loc_from_node(gen, node); + + // args handling + size_t argc = node->data.call_expr.len; + + // alloc args + gcc_jit_rvalue** args = NULL; + + if (argc > 0) { + args = calloc(argc, sizeof *args); // or alloca, or a fixed upper bound + for (size_t i = 0; i < argc; ++i) { + args[i] = handle_expr(gen, node->data.call_expr.args[i]); + } + } + + for (size_t i = 0; i < argc; i++) { + args[i] = handle_expr(gen, node->data.call_expr.args[i]); + } + + /* + When generating the identifier expression message in the AST: + Lookup env["message"] -> you get a gcc_jit_param* or gcc_jit_lvalue*. + Use gcc_jit_param_as_rvalue / gcc_jit_lvalue_as_rvalue. + + gcc_jit_rvalue *msg = gcc_jit_param_as_rvalue(callee_param); + + gcc_jit_rvalue *args[1] = { msg }; + gcc_jit_block_add_eval( + b, loc, + gcc_jit_context_new_call(ctx, loc, print_fn, 1, args)); + */ + + // gcc_jit_rvalue *msg = gcc_jit_param_as_rvalue(callee_param); + // gcc_jit_rvalue *args[1] = { msg }; + assert(callee != NULL && "callee not found"); + return gcc_jit_context_new_call(gen->ctx, loc, callee, argc, args); + + // TODO consider + /* + // calling bob() directly + void emit_direct_call(gcc_jit_context *ctxt, SymbolId bob, gcc_jit_function *caller) { + GccObj *cal = ensure_func(ctxt, bob); + gcc_jit_block *b = gcc_jit_function_new_block(caller, "call_bob"); + gcc_jit_rvalue *args[] = {}; + gcc_jit_block_add_eval(b, NULL, gcc_jit_context_new_call(ctxt, cal->func, 0, args)); + gcc_jit_block_end_with_void_return(b, NULL); + } + */ + + // return gcc_jit_context_new_call(gen->ctx, NULL, callee, 0, NULL); + + // TODO handle return values + // almost unrelated could be useful to deal with return values: + // gcc_jit_rvalue* rv = handle_func_call(gen, node); + // if (rv) { gcc_jit_block_add_eval(gen->curr_block, loc_from_node(gen, node), rv); } + // return false; + return NULL; +} + +static gcc_jit_rvalue* +handle_expr(Gen* gen, Node* node) +{ + switch (node->type) { + case NODE_INT_LITERAL: + return emit_literal_int(gen, node); + break; + case NODE_FLOAT_LITERAL: + return emit_literal_float(gen, node); + break; + case NODE_STRING_LITERAL: + return emit_literal_string(gen, node); + break; + case NODE_BOOL_LITERAL: + return emit_literal_bool(gen, node); + case NODE_CALL_EXPR: + return handle_func_call(gen, node); + break; + case NODE_IDENT: + return handle_ident_call(gen, node); + break; + case NODE_UNARY_EXPR: // ++ etc. + return handle_unary_expr(gen, node); + break; + case NODE_BINARY_EXPR: + return handle_binary_expr(gen, node); + break; + + // case NODE_IDENT: { + // return NULL; // fixme + // break; + // } + default: + printf("handle_expr unhandled, %s\n", node_type_str(node->type)); + } + return NULL; +} + +static gcc_jit_type* +ox_type_to_c_type(Gen* gen, Node* node) +{ + if (node == NULL) return type_void; + + const char* type_name = span_str(gen->src, node->data.ident.name, (char[IDENTSZ]) { 0 }); + + if (strcmp(type_name, "int") == 0) { + return type_int; + + } else if (strcmp(type_name, "i8") == 0) { + return type_i8; + } else if (strcmp(type_name, "i16") == 0) { + return type_i16; + } else if (strcmp(type_name, "i32") == 0) { + return type_i32; + } else if (strcmp(type_name, "i64") == 0) { + return type_i64; + } else if (strcmp(type_name, "i128") == 0) { + return type_i128; + + } else if (strcmp(type_name, "u8") == 0) { + return type_u8; + } else if (strcmp(type_name, "u16") == 0) { + return type_u16; + } else if (strcmp(type_name, "u32") == 0) { + return type_u32; + } else if (strcmp(type_name, "u64") == 0) { + return type_u64; + } else if (strcmp(type_name, "u128") == 0) { + return type_u128; + + } else if (strcmp(type_name, "f32") == 0) { + return type_f32; + } else if (strcmp(type_name, "f64") == 0) { + return type_f64; + } else if (strcmp(type_name, "f128") == 0) { + return type_f128; + + } else if (strcmp(type_name, "str") == 0) { + return type_cstr; + } else if (strcmp(type_name, "float") == 0) { + return type_f32; + } else if (strcmp(type_name, "double") == 0) { + return type_f64; + } else if (strcmp(type_name, "longdouble") == 0) { + return type_f128; + } else if (strcmp(type_name, "uint") == 0) { + return type_uint; + } else if (strcmp(type_name, "void") == 0) { + return type_void; + } else { + // TODO custom user types, lookup by name to see if it exists, and if it does allow it. + // A user type is Capitalised and should be listed somewhere... + // otherwise panic + panic_at(node, "Unknown type in gen: %s", type_name); + } + return NULL; +} + +static void +print_symbols_here(Gen* gen) +{ + for (size_t i = 0; i < gen->scope->len; i++) { + Symbol* sym = gen->scope->symbols[i]; + const char* name = span_str(gen->src, sym->name, (char[IDENTSZ]) { 0 }); + printf("[%zu/%zu] symbol: %s (%s)\n", i + 1, gen->scope->len, name, sym->english_type); + } +} + +static bool build_block(Gen*, Node*); +static bool build_statement(Gen*, Node*); + +static gcc_jit_rvalue* build_bool_value(Gen*, Node*); + +static int block_counter = 0, loop_counter = 0, while_counter = 0; + +static bool +build_while_statement(Gen* gen, Node* node) +{ + gcc_jit_location* loc = loc_from_node(gen, node); + + Node* cond = node->data.while_statement.cond; + Node* body = node->data.while_statement.body; + + while_counter++; + char label_cond[64], label_body[64], label_end[64]; + snprintf(label_cond, sizeof label_cond, "while.cond%d", while_counter); + snprintf(label_body, sizeof label_body, "while.body%d", while_counter); + snprintf(label_end, sizeof label_end, "while.end%d", while_counter); + + gcc_jit_block* cond_block = gcc_jit_function_new_block(gen->curr_func, label_cond); + gcc_jit_block* body_block = gcc_jit_function_new_block(gen->curr_func, label_body); + gcc_jit_block* end_block = gcc_jit_function_new_block(gen->curr_func, label_end); + + /* jump from current block into the loop */ + gcc_jit_block_end_with_jump(gen->curr_block, loc, cond_block); + + gen->curr_block = cond_block; + gcc_jit_rvalue* cnd = NULL; + + if (cond != NULL) { + cnd = build_bool_value(gen, cond); + gcc_jit_block_end_with_conditional(cond_block, loc, cnd, body_block, end_block); + } else { + /* while (1) */ + gcc_jit_block_end_with_jump(cond_block, loc, body_block); + } + + gcc_jit_block* continue_target = cond != NULL ? cond_block : body_block; + push_loop(gen, /*break to*/ end_block, /*continue to*/ continue_target); + + gen->curr_block = body_block; + bool body_ended = build_block(gen, body); + if (!body_ended) { gcc_jit_block_end_with_jump(gen->curr_block, loc, continue_target); } + + pop_loop(gen); + + /* resume after loop */ + gen->curr_block = end_block; + return false; +} + +static bool +build_for_statement(Gen* gen, Node* node) +{ + gcc_jit_location* loc = loc_from_node(gen, node); + + Node* init = node->data.for_statement.init; + Node* cond = node->data.for_statement.cond; + Node* step = node->data.for_statement.increment; + Node* body = node->data.for_statement.body; + + loop_counter++; + + char label_cond[64], label_body[64], label_step[64], label_end[64]; + snprintf(label_cond, 64, "for.cond%d", loop_counter); + snprintf(label_body, 64, "for.body%d", loop_counter); + snprintf(label_step, 64, "for.step%d", loop_counter); + snprintf(label_end, 64, "for.end%d", loop_counter); + + gcc_jit_block* cond_block = gcc_jit_function_new_block(gen->curr_func, label_cond); + gcc_jit_block* body_block = gcc_jit_function_new_block(gen->curr_func, label_body); + gcc_jit_block* step_block = gcc_jit_function_new_block(gen->curr_func, label_step); + gcc_jit_block* end_block = gcc_jit_function_new_block(gen->curr_func, label_end); + + // gcc_jit_block* saved_break = gen->loop->break_target; + // gcc_jit_block* saved_cont = gen->loop->continue_target; + // gen->loop->break_target = end_block; + // gen->loop->continue_target = step_block; + + // header, e.g. for(int = 0 <- + if (init) { build_statement(gen, init); } + + // jump to cond e.g. ; i < 5 <- + gcc_jit_block_end_with_jump(gen->curr_block, loc, cond_block); + + // cond: evaluate + gen->curr_block = cond_block; + gcc_jit_rvalue* cnd = NULL; + + if (cond != NULL) { + cnd = build_bool_value(gen, cond); + gcc_jit_block_end_with_conditional(cond_block, loc, cnd, body_block, end_block); + } else { + gcc_jit_block_end_with_jump(cond_block, loc, body_block); // presume for(;;) + } + + push_loop(gen, /*break to*/ end_block, /*continue to*/ step_block); + + // build for body + + gen->curr_block = body_block; + bool for_body_ended = build_block(gen, body); + if (!for_body_ended) { gcc_jit_block_end_with_jump(gen->curr_block, loc, step_block); } + + pop_loop(gen); + + // step incr etc. + + gen->curr_block = step_block; + if (step) { build_statement(gen, step); } + gcc_jit_block_end_with_jump(step_block, loc, cond != NULL ? cond_block : body_block); + + // resume after loop + + gen->curr_block = end_block; + return false; +} + +static bool +build_if_statement(Gen* gen, Node* node) +{ + gcc_jit_location* loc = loc_from_node(gen, node); + // build the condition + + OpType op = node->data.if_statement.cond->data.binary_expr.op; + Node* lhs = node->data.if_statement.cond->data.binary_expr.lhs; + Node* rhs = node->data.if_statement.cond->data.binary_expr.rhs; + + Node* then_body = node->data.if_statement.then_body; + Node* else_body = node->data.if_statement.else_body; + + enum gcc_jit_comparison cmp; + switch (op) { + case OP_EQUALITY: + cmp = GCC_JIT_COMPARISON_EQ; + break; + case OP_INEQUALITY: + cmp = GCC_JIT_COMPARISON_NE; + break; + case OP_GT: + cmp = GCC_JIT_COMPARISON_GT; + break; + case OP_GT_EQ: + cmp = GCC_JIT_COMPARISON_GE; + break; + case OP_LT_EQ: + cmp = GCC_JIT_COMPARISON_LE; + break; + case OP_LT: + cmp = GCC_JIT_COMPARISON_LT; + break; + default: + printf("/!\\ build_statement NODE_IF unhandled, %d\n", op); + cmp = GCC_JIT_COMPARISON_NE; + } + + gcc_jit_rvalue* lhs_val = handle_expr(gen, lhs); + gcc_jit_rvalue* rhs_val = handle_expr(gen, rhs); + + gcc_jit_rvalue* cond = gcc_jit_context_new_comparison(gen->ctx, loc, cmp, lhs_val, rhs_val); + + // create the BLOCKS + + // labels + block_counter++; + char label_then[64], label_else[64], label_end[64]; + snprintf(label_then, 64, "if.then%d", block_counter); + snprintf(label_else, 64, "if.else%d", block_counter); + snprintf(label_end, 64, "if.end%d", block_counter); + + // blocks + gcc_jit_block* then_bb = gcc_jit_function_new_block(gen->curr_func, label_then); + gcc_jit_block* else_bb = else_body ? gcc_jit_function_new_block(gen->curr_func, label_else) : NULL; + gcc_jit_block* merge_bb = NULL; + + if (!else_bb) { + // no else: need merge now for the false edge + merge_bb = gcc_jit_function_new_block(gen->curr_func, label_end); + gcc_jit_block_end_with_conditional(gen->curr_block, loc, cond, then_bb, merge_bb); + } else { + // with else: branch to then/else; decide on merge later + gcc_jit_block_end_with_conditional(gen->curr_block, loc, cond, then_bb, else_bb); + } + + // THEN + gen->curr_block = then_bb; + bool then_ended = build_block(gen, then_body); + gcc_jit_block* then_open = gen->curr_block; // last open block in THEN + // (may differ from then_bb) + + // ELSE + bool else_ended = false; + gcc_jit_block* else_open = NULL; + if (else_bb) { + gen->curr_block = else_bb; + else_ended = build_block(gen, else_body); + else_open = gen->curr_block; // last open block in ELSE + } + + // If both branches ended, no merge needed, we notify we have ended as + // well + if (else_bb && then_ended && else_ended) return true; + + // Ensure we have a merge if any branch continues. + if (!merge_bb) merge_bb = gcc_jit_function_new_block(gen->curr_func, label_end); + + if (!then_ended) gcc_jit_block_end_with_jump(then_open, loc, merge_bb); + if (else_bb && !else_ended) gcc_jit_block_end_with_jump(else_open, loc, merge_bb); + + gen->curr_block = merge_bb; + return false; +} + +static bool +build_var_decl_statement(Gen* gen, Node* node) +{ + gcc_jit_location* loc = loc_from_node(gen, node); + const char* var_name = span_str(gen->src, node->data.var_decl.name, (char[IDENTSZ]) { 0 }); + gcc_jit_type* declared_type = ox_type_to_c_type(gen, node->data.var_decl.type); + + gcc_jit_lvalue* var_decl = NULL; + + gcc_jit_rvalue* rvalue = NULL; + Node* init = node->data.var_decl.init; + if (init != NULL) { rvalue = handle_expr(gen, init); } + + if (gen->curr_func == NULL && gen->curr_block == NULL) { + // global var + var_decl = gcc_jit_context_new_global(gen->ctx, loc, GCC_JIT_GLOBAL_INTERNAL, declared_type, strdup(var_name)); + if (rvalue) gcc_jit_global_set_initializer_rvalue(var_decl, rvalue); + } else { + // local var + var_decl = gcc_jit_function_new_local(gen->curr_func, loc, declared_type, strdup(var_name)); + if (rvalue) gcc_jit_block_add_assignment(gen->curr_block, loc, var_decl, rvalue); + } + + // this is not an error, there is just no initial value + // if (node->data.var_decl.init == NULL) { panic("could not instanciate gcc jit new local"); } + + printf("adding 1 symbol: %s\n", var_name); + + Symbol* sym = (Symbol*)calloc(1, sizeof(Symbol)); + sym->name = node->data.var_decl.name; + sym->decl = node; + sym->ctype = declared_type; + sym->d.lvalue = var_decl; + sym->english_type = type_var; + sym->is_variadic = node->data.var_decl.type->data.ident_type.is_variadic; + sym->is_comp_time = node->data.var_decl.type->data.ident_type.is_comp_time; + add_symbol(gen, sym); // add var_decl symbol + return false; +} + +static void build_func_decl(Gen*, Node*); + +static gcc_jit_field* +build_field(Gen* gen, Node* node) +{ + const char* name = span_str(gen->src, node->data.field_decl.name, (char[IDENTSZ]) { 0 }); + gcc_jit_type* type = ox_type_to_c_type(gen, node->data.field_decl.type); + gcc_jit_field* field = gcc_jit_context_new_field(gen->ctx, NULL, type, strdup(name)); + // TODO build_field in struct to allow init_value (see field_decl.init) -- note, it cannot be done via gccjit, it has to be + // stored somewhere and then upon the user creating an instance of the type, we then evaluate the expression and provide the + // value as a default of the field. + return field; +} + +static bool +build_statement(Gen* gen, Node* node) +{ + gcc_jit_location* loc = loc_from_node(gen, node); + switch (node->type) { + case NODE_RETURN: { + Node* return_expr = node->data.ret.expr; + if (return_expr) { + gcc_jit_rvalue* ret_rval = handle_expr(gen, return_expr); + gcc_jit_type* expect_type = gcc_jit_function_get_return_type(gen->curr_func); + gcc_jit_type* ret_type = gcc_jit_rvalue_get_type(ret_rval); + types_match_or_panic(node, expect_type, ret_type); + gcc_jit_block_end_with_return(gen->curr_block, loc, ret_rval); + } else { + gcc_jit_block_end_with_void_return(gen->curr_block, loc); + } + gen->curr_block = NULL; // important + return true; // we end the block here + } + case NODE_BREAK: { + gcc_jit_block* t = current_break(gen); + if (!t) return true; // break outside the loop + gcc_jit_block_end_with_jump(gen->curr_block, loc, t); + gen->curr_block = NULL; + return true; + } + case NODE_CONTINUE: { + gcc_jit_block* t = current_continue(gen); + if (!t) return true; // continue outside of a loop + gcc_jit_block_end_with_jump(gen->curr_block, loc, t); + gen->curr_block = NULL; + return true; + } + case NODE_VAR_DECL: { + return build_var_decl_statement(gen, node); + } + case NODE_VAR_ASSIGN: { + gcc_jit_rvalue* rvalue = handle_expr(gen, node->data.var_assign.rhs); + // find the var_decl (d.lvalue) from the symbols table's symbol. + for (size_t i = 0; i < gen->scope->len; i++) { + Symbol* sym = gen->scope->symbols[i]; + if (span_ident_same(sym->name, node->data.var_assign.lhs->data.ident.name, gen->src)) { + // we found the symbol we are assigning to + // TODO check for variadic and comp_time + if (!sym->is_variadic) { + const char* name = span_str(gen->src, sym->name, (char[IDENTSZ]) { 0 }); + panic_at(node, "symbol %s is not variadic, cannot mutate.", name); + } + if (sym->is_comp_time) { + const char* name = span_str(gen->src, sym->name, (char[IDENTSZ]) { 0 }); + panic_at(node, "symbol %s is comp time, cannot mutate.", name); + } + + gcc_jit_lvalue* lvalue = sym->d.lvalue; + + // check the type of lvalue matches the rvalue + gcc_jit_type* ltype = sym->ctype; + gcc_jit_type* rtype = gcc_jit_rvalue_get_type(rvalue); + if (rtype != ltype) { + panic_at(node, + "right hand side of assigment " + "doesn't match the " + "type of the left hand side: %s <-- %s", + get_english_type(ltype), + get_english_type(rtype)); + } + if (lvalue) gcc_jit_block_add_assignment(gen->curr_block, loc, lvalue, rvalue); + } + } + break; + } + case NODE_BINARY_EXPR: { + Symbol* sym = lookup_symbol(gen, gen->scope, node->data.binary_expr.lhs->data.ident.name); + if (!sym) + panic_at(node, + "NODE_BINARY_EXPR: Undefined symbol: %s\n", + span_str(gen->src, node->data.ident.name, (char[IDENTSZ]) { 0 })); + + gcc_jit_lvalue* lv = sym->d.lvalue; + gcc_jit_rvalue* rhs = handle_expr(gen, node->data.binary_expr.rhs); + gcc_jit_rvalue* lhs = handle_expr(gen, node->data.binary_expr.lhs); + gcc_jit_rvalue* temp = gcc_jit_lvalue_as_rvalue(lv); + gcc_jit_type* lvtype = gcc_jit_rvalue_get_type(temp); + gcc_jit_rvalue* sum + = gcc_jit_context_new_binary_op(gen->ctx, loc_from_node(gen, node), GCC_JIT_BINARY_OP_PLUS, lvtype, lhs, rhs); + + gcc_jit_block_add_assignment(gen->curr_block, loc_from_node(gen, node), lv, sum); + } + case NODE_UNARY_EXPR: { + gcc_jit_rvalue* rv = handle_unary_expr(gen, node); + if (rv) gcc_jit_block_add_eval(gen->curr_block, loc_from_node(gen, node), rv); + break; + } + case NODE_EXPR_STATEMENT: { + gcc_jit_rvalue* rv = handle_expr(gen, node->data.expr_statement.expr); + if (rv) gcc_jit_block_add_eval(gen->curr_block, loc_from_node(gen, node), rv); + break; + } + case NODE_IF: + return build_if_statement(gen, node); + case NODE_FOR: + return build_for_statement(gen, node); + case NODE_WHILE: + return build_while_statement(gen, node); + case NODE_FUNCTION_DECL: + // "closure" + build_func_decl(gen, node); + return false; + case NODE_BLOCK: + build_block(gen, node); + default: + printf("--- WARNING: build_statement unhandled, %s\n", node_type_str(node->type)); + break; + } + return false; +} + +static gcc_jit_rvalue* +build_bool_value(Gen* gen, Node* node) +{ + gcc_jit_location* loc = loc_from_node(gen, node); + + switch (node->type) { + case NODE_BOOL_LITERAL: + /* true/false literal */ + return gcc_jit_context_new_rvalue_from_int(gen->ctx, type_boolean, node->data.boolean.value ? 1 : 0); + break; + case NODE_INT_LITERAL: { + /* while (1) style – C truthiness */ + gcc_jit_rvalue* iv = emit_literal_int(gen, node); + return gcc_jit_context_new_cast(gen->ctx, loc, iv, type_boolean); + } + case NODE_STRING_LITERAL: { + /* while("word") should also be truthy if the pointer is non null */ + gcc_jit_rvalue* v = emit_literal_string(gen, node); + gcc_jit_type* ty = gcc_jit_rvalue_get_type(v); + gcc_jit_rvalue* nullv = gcc_jit_context_null(gen->ctx, ty); + return gcc_jit_context_new_comparison(gen->ctx, loc, GCC_JIT_COMPARISON_NE, v, nullv); + } + case NODE_BINARY_EXPR: { + gcc_jit_rvalue* lvalue = handle_expr(gen, node->data.binary_expr.lhs); + gcc_jit_rvalue* rvalue = handle_expr(gen, node->data.binary_expr.rhs); + enum gcc_jit_comparison op; + switch (node->data.binary_expr.op) { + case OP_LT: + op = GCC_JIT_COMPARISON_LT; + break; + case OP_GT: + op = GCC_JIT_COMPARISON_GT; + break; + case OP_LT_EQ: + op = GCC_JIT_COMPARISON_LE; + break; + case OP_GT_EQ: + op = GCC_JIT_COMPARISON_GE; + break; + default: + printf("build_bool_rvalue nodebinary op unhandled, " + "%s\n", + node_type_str(node->type)); + return NULL; + } + return gcc_jit_context_new_comparison(gen->ctx, loc, op, lvalue, rvalue); + break; + } + default: + printf("/!\\ build_bool_rvalue unhandled, %s\n", node_type_str(node->type)); + break; + } + + return NULL; +} + +static gcc_jit_struct* +build_struct_block(Gen* gen, Node* body, const char* struct_name) +{ + if (body->data.block.len == 0) { panic_at(body, "No field definition were found in the struct block"); } + + // TODO build_struct_block do we need to push and pop scope? + + size_t fields_count = body->data.block.len; + gcc_jit_field** fields = (gcc_jit_field**)calloc(fields_count, sizeof(gcc_jit_field*)); + if (fields == NULL) { panic("build_struct_block: fields: could not alloc"); } + + for (size_t i = 0; i < fields_count; i++) { + gcc_jit_field* field = build_field(gen, body->data.block.stmts[i]); + fields[i] = field; + } + gcc_jit_location* loc = loc_from_node(gen, body); + gcc_jit_struct* strct = gcc_jit_context_new_struct_type(gen->ctx, loc, strdup(struct_name), fields_count, fields); + return strct; +} + +// build_block: returns true if block ended with a terminator +static bool +build_block(Gen* gen, Node* body) +{ + // TODO if there is no block ({ ... }), we presume there is only 1 statement. + // This might not always be valid, func declaration do require a block {}, but there might be cases... + // TODO FIXME: a symbol declared in a child block is accessible in the parent or in a sibling block... + if (body->data.block.len == 0) { return build_statement(gen, body); } + + for (size_t i = 0; i < body->data.block.len; i++) { + if (gen->curr_block == NULL) return true; + bool ended = build_statement(gen, body->data.block.stmts[i]); + if (ended) return true; + } + return false; +} + +static void +build_struct_decl(Gen* gen, Node* node) +{ + // TODO the exported vs internal structs, for now they're all exported. + // TODO opaque struct so we allow circular dependencies A {B*}, B {A*}; + const char* struct_name = span_str(gen->src, node->data.struct_decl.name, (char[IDENTSZ]) { 0 }); + gcc_jit_struct* user_struct_def = build_struct_block(gen, node->data.struct_decl.body, struct_name); + gcc_jit_type* user_struct_type = gcc_jit_struct_as_type(user_struct_def); + // TODO @next addsymbol struct + printf("adding 1 symbol: %s\n", struct_name); + + Symbol* sym = (Symbol*)calloc(1, sizeof(Symbol)); + sym->name = node->data.struct_decl.name; + sym->decl = node; + sym->ctype = user_struct_type; + sym->d.struct_value = user_struct_def; + sym->english_type = type_struct; + sym->is_variadic = false; + sym->is_comp_time = false; + add_symbol(gen, sym); // add struct_decl symbol +} + +static void +build_func_decl(Gen* gen, Node* node) +{ + const char* func_name = span_str(gen->src, node->data.function_decl.name, (char[IDENTSZ]) { 0 }); + + Node* return_type = node->data.function_decl.return_type; + gcc_jit_type* ret_type = ox_type_to_c_type(gen, return_type); + gcc_jit_location* loc = loc_from_node(gen, node); + + size_t argc = node->data.function_decl.p_len; + + gcc_jit_param** params = calloc(argc, sizeof(gcc_jit_param*)); + + // 1) Create ONE gcc_jit_param per AST param + for (size_t i = 0; i < argc; i++) { + Node* pnode = node->data.function_decl.params[i]; + const char* pname = span_str(gen->src, pnode->data.param.name, (char[IDENTSZ]) { 0 }); + + gcc_jit_type* declared_type = ox_type_to_c_type(gen, pnode->data.param.type); + + gcc_jit_param* p = gcc_jit_context_new_param(gen->ctx, loc, declared_type, strdup(pname)); + + params[i] = p; + } + + // 2) Create the function with those params + gcc_jit_function* func = gcc_jit_context_new_function(gen->ctx, + loc, + GCC_JIT_FUNCTION_EXPORTED, + ret_type, + strdup(func_name), + argc, + params, // <-- these are now owned by 'func' + 0); + + gcc_jit_block* block = gcc_jit_function_new_block(func, "entry"); + + gcc_jit_function* prev_func = gen->curr_func; + gcc_jit_block* prev_block = gen->curr_block; + gen->curr_func = func; + gen->curr_block = block; + + // 3) Add the function symbol to the current (enclosing) scope + + Symbol* sym = (Symbol*)calloc(1, sizeof(Symbol)); + sym->name = node->data.function_decl.name; + sym->decl = node; + sym->ctype = type_voidp; // whatever you use for function type metadata + sym->d.funcvalue = func; + sym->english_type = type_func; + add_symbol(gen, sym); // add function symbol -- to the 'parent scope', before we push_scope + + // ENTER FUNCTION SCOPE + push_scope(gen); + + // 4) Add parameter symbols to the FUNCTION scope, reusing the same gcc_jit_param* + for (size_t i = 0; i < argc; i++) { + Node* pnode = node->data.function_decl.params[i]; + + gcc_jit_param* p = params[i]; // <-- reuse, DO NOT new_param again + + gcc_jit_type* declared_type = ox_type_to_c_type(gen, pnode->data.param.type); + + Symbol* ps = (Symbol*)calloc(1, sizeof(Symbol)); + ps->name = pnode->data.param.name; // param identifier span + ps->decl = pnode; + ps->ctype = declared_type; + ps->d.param = p; // <-- this is associated with 'func' + ps->english_type = type_var; // or type_param if you distinguish + add_symbol(gen, ps); // add param symbol + } + + print_symbols_here(gen); + + build_block(gen, node->data.function_decl.body); + + if (gen->curr_block == NULL) { + // no open block: all paths already terminated, don't add an implicit return + } else if (ret_type == type_int) { + gcc_jit_rvalue* ret_value = gcc_jit_context_new_rvalue_from_int(gen->ctx, type_int, 0); + gcc_jit_block_end_with_return(gen->curr_block, loc, ret_value); + } else if (ret_type == type_void) { + gcc_jit_block_end_with_void_return(gen->curr_block, loc); + } else { + printf("build_func_decl unhandled return type in func: %s - " + "defaulting to void\n", + func_name); + gcc_jit_block_end_with_void_return(gen->curr_block, loc); + } + + // LEAVE FUNCTION SCOPE + pop_scope(gen); + + gen->curr_func = prev_func; + gen->curr_block = prev_block; +} + +void +gen_next(Gen* gen, Node* node) +{ + // printf("gen_next, %s\n", node_type_str(node->type)); + + switch (node->type) { + case NODE_PROGRAM: + build_program(gen, node); + break; + case NODE_FUNCTION_DECL: + build_func_decl(gen, node); + break; + case NODE_STRUCT_DECL: + build_struct_decl(gen, node); + break; + case NODE_STRING_LITERAL: + emit_literal_string(gen, node); + break; + case NODE_INT_LITERAL: + emit_literal_int(gen, node); + break; + case NODE_FLOAT_LITERAL: + emit_literal_float(gen, node); + break; + case NODE_VAR_DECL: + build_statement(gen, node); + break; + default: + printf("gen: unhandled, %s\n", node_type_str(node->type)); + } +} diff --git a/src/gen.h b/src/gen.h @@ -5,25 +5,5 @@ #include "types.h" -typedef struct LoopContext { - gcc_jit_block* break_target; - gcc_jit_block* continue_target; - struct LoopContext* prev; -} LoopContext; - -typedef struct { - gcc_jit_context* ctx; - gcc_jit_function* prev_func; - gcc_jit_function* curr_func; - gcc_jit_function* printf_fn; - gcc_jit_function* puts_fn; - gcc_jit_block* prev_block; - gcc_jit_block* curr_block; - // gcc_jit_type *type_kind; need type too? - Scope* scope; - LoopContext* loop; - const char* src; -} Gen; - Gen gen_init(Scope*, const char*, Node*, bool); void gen_next(Gen*, Node*); diff --git a/src/gen/gen.c b/src/gen/gen.c @@ -1,1372 +0,0 @@ -#include "../gen.h" -#include "../parser.h" -#include "../utils.h" -#include "../typer.h" - -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <assert.h> -#include <sys/param.h> - -static gcc_jit_type* type_boolean; -static gcc_jit_type* type_int; -static gcc_jit_type* type_i8; -static gcc_jit_type* type_i16; -static gcc_jit_type* type_i32; -static gcc_jit_type* type_i64; -static gcc_jit_type* type_i128; -static gcc_jit_type* type_u8; -static gcc_jit_type* type_u16; -static gcc_jit_type* type_u32; -static gcc_jit_type* type_u64; -static gcc_jit_type* type_u128; -static gcc_jit_type* type_f32; -static gcc_jit_type* type_f64; -static gcc_jit_type* type_f128; -static gcc_jit_type* type_uint; -static gcc_jit_type* type_void; -static gcc_jit_type* type_cstr; -static gcc_jit_type* type_char; -static gcc_jit_type* type_voidp; - -static const char* type_func = "function"; -static const char* type_var = "variable"; - -#define MAXARGS 16 - -static gcc_jit_location* -loc_from_node(Gen* gen, Node* node) -{ - if (node->filename == NULL) return NULL; - return gcc_jit_context_new_location(gen->ctx, node->filename, node->line, node->col); -} - -static void -push_loop(Gen* gen, gcc_jit_block* brk, gcc_jit_block* cont) -{ - LoopContext* lctx = (LoopContext*)malloc(sizeof(LoopContext)); - if (lctx == NULL) { panic("push_loop: could not alloc"); } - lctx->break_target = brk; - lctx->continue_target = cont; - lctx->prev = gen->loop; - gen->loop = lctx; -} - -static void -pop_loop(Gen* gen) -{ - LoopContext* lctx = gen->loop; - gen->loop = lctx->prev; - free(lctx); -} - -static inline gcc_jit_block* -current_break(Gen* gen) -{ - return gen->loop ? gen->loop->break_target : NULL; -} - -static inline gcc_jit_block* -current_continue(Gen* gen) -{ - return gen->loop ? gen->loop->continue_target : NULL; -} - -__attribute__((unused)) static const char* -get_english_type(gcc_jit_type* T) -{ - return gcc_jit_object_get_debug_string(gcc_jit_type_as_object(T)); -} - -Gen -gen_init(Scope* scope, const char* src, Node* node, bool quiet) -{ - if (scope == NULL || src == NULL) { panic("gen_init: no Scope or AST provided"); } - - gcc_jit_context* ctx; - - ctx = gcc_jit_context_acquire(); - - if (!ctx) { panic("could not acquire gcc jit context"); } - - // needs loc* to work - // gcc_jit_context_set_bool_option(ctx, GCC_JIT_BOOL_OPTION_DEBUGINFO, - // 1); high level - - gcc_jit_context_set_bool_option(ctx, GCC_JIT_BOOL_OPTION_DEBUGINFO, 1); - - if (quiet == false) { gcc_jit_context_set_bool_option(ctx, GCC_JIT_BOOL_OPTION_DUMP_INITIAL_GIMPLE, 1); } - - // gcc_jit_context_set_bool_option(ctx, - // GCC_JIT_BOOL_OPTION_DUMP_SUMMARY, 1); - - gcc_jit_context_set_str_option(ctx, GCC_JIT_STR_OPTION_PROGNAME, "ox"); - // keep FP - gcc_jit_context_add_driver_option(ctx, "-fno-omit-frame-pointer"); - - gcc_jit_context_set_int_option(ctx, - GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, - /*0-3 for O3*/ 0); - - type_boolean = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_BOOL); - - type_int = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_INT64_T); - type_i8 = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_INT8_T); - type_i16 = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_INT16_T); - type_i32 = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_INT32_T); - type_i64 = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_INT64_T); - type_i128 = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_INT128_T); - - type_u8 = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UINT8_T); - type_u16 = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UINT16_T); - type_u32 = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UINT32_T); - type_u64 = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UINT64_T); - type_u128 = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UINT128_T); - - type_f32 = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_FLOAT); - type_f64 = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_DOUBLE); - type_f128 = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_LONG_DOUBLE); - - type_uint = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UINT64_T); - type_char = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_CHAR); - type_void = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_VOID); - type_cstr = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_CONST_CHAR_PTR); - type_voidp = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_VOID_PTR); - /* gcc_jit_type_dyncast_function_ptr_type; */ - - gcc_jit_location* loc = gcc_jit_context_new_location(ctx, node->filename, 0, 0); - - gcc_jit_param* pm_puts[] = { gcc_jit_context_new_param(ctx, loc, type_cstr, "s") }; - gcc_jit_function* fn_puts = gcc_jit_context_new_function(ctx, loc, GCC_JIT_FUNCTION_IMPORTED, type_int, "puts", 1, pm_puts, 0); - - gcc_jit_param* pm_printf[] = { gcc_jit_context_new_param(ctx, loc, type_cstr, "fmt") }; - gcc_jit_function* fn_printf = gcc_jit_context_new_function(ctx, - loc, - GCC_JIT_FUNCTION_IMPORTED, - type_int, - "printf", - 1, - pm_printf, - /*is_variadic=*/1); - - return (Gen) { - .ctx = ctx, - .scope = scope, - .prev_func = NULL, - .curr_func = NULL, - .prev_block = NULL, - .curr_block = NULL, - .puts_fn = fn_puts, - .printf_fn = fn_printf, - .src = src, - }; -} - -static void -push_scope(Gen* gen) -{ - printf("push_scope\n"); - Scope* scope = calloc(1, sizeof(Scope)); - *scope = (Scope) { 0 }; - scope->symbols = (Symbol**)calloc(16, sizeof(Symbol*)); - if (scope->symbols == NULL) panic("push_scope: symbols: could not alloc"); - scope->cap = 16; - scope->len = 0; - - scope->parent = gen->scope; - gen->scope = scope; -} - -static void -pop_scope(Gen* gen) -{ - printf("pop_scope\n"); - Scope* s = gen->scope; - gen->scope = s->parent; -} - -static Symbol* -find_symbol(Gen* gen, Scope* scope, Span name) -{ - const char* ident_name = span_str(gen->src, name, (char[IDENTSZ]) { 0 }); - // Look up the symbol in the current scope // TODO look up in parent - // scope with utility function and recursion - for (size_t i = 0; i < scope->len; i++) { - Symbol* sym = scope->symbols[i]; - const char* sym_name = span_str(gen->src, sym->name, (char[IDENTSZ]) { 0 }); - // printf("symbol %s\n", sym_name); - // printf("ident %s\n", ident_name); - if (strcmp(sym_name, ident_name) == 0) { - printf("found symbol %s\n", sym_name); - return sym; - } - } - printf("find_symbol: not found locally: %s\n", ident_name); - - if (scope->parent != NULL) { - printf("looking up symbol %s in parent\n", ident_name); - return find_symbol(gen, scope->parent, name); - } - - return NULL; -} - -static void -add_symbol(Gen* gen, Symbol* sym) -{ - if (gen->scope->len == gen->scope->cap) { - gen->scope->cap *= 2; - gen->scope->symbols = (Symbol**)realloc(gen->scope->symbols, sizeof(Symbol*) * gen->scope->cap); - } - - gen->scope->symbols[gen->scope->len++] = sym; - - printf("add_symbol: we now have %zu symbols: ", gen->scope->len); - for (size_t i = 0; i < gen->scope->len; i++) { - Symbol* symy = gen->scope->symbols[i]; - const char* name = span_str(gen->src, symy->name, (char[IDENTSZ]) { 0 }); - printf("%s, ", name); - } - printf("\n"); -} - -static gcc_jit_rvalue* -handle_ident_call(Gen* gen, Node* node) -{ - // Look up the symbol in the current scope // TODO look up in parent - // scope with utility function and recursion - Symbol* sym = find_symbol(gen, gen->scope, node->data.ident.name); - - if (sym == NULL) { - panic_at(node, - "handle_ident_call: undefined variable: %s\n", - span_str(gen->src, node->data.ident.name, (char[IDENTSZ]) { 0 })); - } - return gcc_jit_lvalue_as_rvalue(sym->d.lvalue); -} - -static gcc_jit_rvalue* handle_expr(Gen*, Node*); - -static inline int -is_intlike(gcc_jit_context* ctx, gcc_jit_type* t) // TODO support all new types -{ - return t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_INT) || t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_INT) - || t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_SHORT) - || t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_SHORT) - || t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_LONG) - || t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_LONG) - || t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_LONG_LONG) - || t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_LONG_LONG) - || t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_INT64_T) || t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_INT32_T) - || t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_CHAR) - || t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_CHAR); -} - -static inline int -is_floatlike(gcc_jit_context* ctx, gcc_jit_type* t) -{ - return t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_FLOAT) || t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_DOUBLE) - || t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_LONG_DOUBLE); -} - -static inline int -is_cstr(gcc_jit_context* ctx, gcc_jit_type* t) -{ - return t == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_CONST_CHAR_PTR); -} - -// > < >= <= + - / * % -static gcc_jit_rvalue* -handle_bin_expr(Gen* gen, Node* node, int gcc_jit_op, bool cmp) -{ - Node* lhs = node->data.binary_expr.lhs; - Node* rhs = node->data.binary_expr.rhs; - gcc_jit_context* ctx = gen->ctx; - gcc_jit_location* loc = loc_from_node(gen, node); - - gcc_jit_rvalue* L = handle_expr(gen, lhs); - gcc_jit_rvalue* R = handle_expr(gen, rhs); - gcc_jit_type* Lt = gcc_jit_rvalue_get_type(L); - gcc_jit_type* Rt = gcc_jit_rvalue_get_type(R); - gcc_jit_rvalue* result = NULL; - - // floats: cast both to double - if (is_floatlike(ctx, Lt) || is_floatlike(ctx, Rt)) { - gcc_jit_type* T = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_DOUBLE); - L = gcc_jit_context_new_cast(ctx, loc, L, T); - R = gcc_jit_context_new_cast(ctx, loc, R, T); - result = cmp ? gcc_jit_context_new_comparison(ctx, loc, gcc_jit_op, L, R) - : gcc_jit_context_new_binary_op(ctx, loc, gcc_jit_op, T, L, R); - ; - } - - // integers: cast both to signed/unsigned long long (cheap, predictable) - if (is_intlike(ctx, Lt) && is_intlike(ctx, Rt)) { - int any_unsigned = (Lt == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_CHAR)) - || (Lt == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_SHORT)) - || (Lt == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_INT)) - || (Lt == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_LONG)) - || (Lt == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_LONG_LONG)) - || (Rt == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_CHAR)) - || (Rt == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_SHORT)) - || (Rt == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_INT)) - || (Rt == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_LONG)) - || (Rt == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_LONG_LONG)); - - gcc_jit_type* T = gcc_jit_context_get_type(ctx, any_unsigned ? GCC_JIT_TYPE_UINT64_T : GCC_JIT_TYPE_INT64_T); - - L = gcc_jit_context_new_cast(ctx, loc, L, T); - R = gcc_jit_context_new_cast(ctx, loc, R, T); - result = cmp ? gcc_jit_context_new_comparison(ctx, loc, gcc_jit_op, L, R) - : gcc_jit_context_new_binary_op(ctx, loc, gcc_jit_op, T, L, R); - } - - // pointers: either reject or compare addresses (UB in C for unrelated objs). - if ( -#ifdef GCC_JIT_HAVE_gcc_jit_type_is_pointer - gcc_jit_type_is_pointer(Lt) && gcc_jit_type_is_pointer(Rt) -#else - 0 -#endif - ) { - gcc_jit_type* T = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_LONG_LONG); - L = gcc_jit_context_new_cast(ctx, loc, L, T); - R = gcc_jit_context_new_cast(ctx, loc, R, T); - result = cmp ? gcc_jit_context_new_comparison(ctx, loc, gcc_jit_op, L, R) - : gcc_jit_context_new_binary_op(ctx, loc, gcc_jit_op, T, L, R); - } - - return result; -} - -static gcc_jit_rvalue* -handle_binary_expr(Gen* gen, Node* node) -{ - int gcc_jit_op = -1; - OpType op = node->data.binary_expr.op; - - switch (op) { - case OP_PLUS: - gcc_jit_op = GCC_JIT_BINARY_OP_PLUS; - return handle_bin_expr(gen, node, gcc_jit_op, false); - break; - case OP_MINUS: - gcc_jit_op = GCC_JIT_BINARY_OP_MINUS; - return handle_bin_expr(gen, node, gcc_jit_op, false); - break; - case OP_MUL: - gcc_jit_op = GCC_JIT_BINARY_OP_MULT; - return handle_bin_expr(gen, node, gcc_jit_op, false); - break; - case OP_DIV: - gcc_jit_op = GCC_JIT_BINARY_OP_DIVIDE; - return handle_bin_expr(gen, node, gcc_jit_op, false); - break; - case OP_MOD: - gcc_jit_op = GCC_JIT_BINARY_OP_MODULO; - return handle_bin_expr(gen, node, gcc_jit_op, false); - break; - case OP_LT: - gcc_jit_op = GCC_JIT_COMPARISON_LT; - break; - case OP_GT: - gcc_jit_op = GCC_JIT_COMPARISON_GT; - break; - case OP_GT_EQ: - gcc_jit_op = GCC_JIT_COMPARISON_GE; - break; - case OP_LT_EQ: - gcc_jit_op = GCC_JIT_COMPARISON_LE; - break; - default: - printf("handle_binary_expr unhandled OpType %d (can be ignored in var assignment " - "(for now))\n", - op); - return NULL; - break; - } - - if (gcc_jit_op < 0) { - // unsupported types (e.g., strings or structs) - printf("handle_binary_expr OP_LT: unsupported operand types\n"); - return NULL; - } - - return handle_bin_expr(gen, node, gcc_jit_op, true); -} - -static gcc_jit_rvalue* -handle_unary_expr(Gen* gen, Node* node) -{ - gcc_jit_context* ctx = gen->ctx; - gcc_jit_block* bb = gen->curr_block; - - Node* opnd = node->data.unary_expr.operand; - Symbol* sym = find_symbol(gen, gen->scope, opnd->data.ident.name); - if (sym == NULL) { - panic_at(node, - "handle_unary_expr: undefined variable: %s\n", - span_str(gen->src, opnd->data.ident.name, (char[IDENTSZ]) { 0 })); - } - - gcc_jit_lvalue* lv = sym->d.lvalue; - if (lv == NULL) { - printf("handle_unary_expr: no lvalue\n"); - return NULL; - } - gcc_jit_rvalue* orig = gcc_jit_lvalue_as_rvalue(lv); - gcc_jit_type* ty = gcc_jit_rvalue_get_type(orig); - gcc_jit_rvalue* one = gcc_jit_context_one(ctx, ty); - - gcc_jit_rvalue* inc = gcc_jit_context_new_binary_op(ctx, NULL, GCC_JIT_BINARY_OP_PLUS, ty, orig, one); - switch (node->data.unary_expr.op) { - case OPER_POSTINC: { // i++ - - if (!sym->is_variadic) { - panic_at(node, - "OPER_POSTINC: cannot change non variadic variable %s\n", - span_str(gen->src, opnd->data.ident_type.name, (char[IDENTSZ]) { 0 })); - } else if (sym->is_comp_time) { - panic_at(node, - "OPER_POSTINC: cannot change comp time constant %s\n", - span_str(gen->src, opnd->data.ident_type.name, (char[IDENTSZ]) { 0 })); - } - gcc_jit_block_add_assignment(bb, NULL, lv, inc); - return orig; // return value before incr as ++ is postfix - - break; - } - default: - printf("handle_unary_expr, unhandled op %d\n", node->data.unary_expr.op); - return NULL; - break; - } - return NULL; -} - -static gcc_jit_rvalue* -emit_literal_bool(Gen* gen, Node* node) -{ - int v = node->data.boolean.value ? 1 : 0; - return gcc_jit_context_new_rvalue_from_int(gen->ctx, type_boolean, v); -} - -static gcc_jit_rvalue* -emit_literal_string(Gen* gen, Node* node) -{ - size_t len = node->data.string.value.end - node->data.string.value.start; - char* str = calloc(len + 1, sizeof(char)); - if (str == NULL) panic_at(node, "emit_literal_string: could not alloc"); - memcpy(str, gen->src + node->data.string.value.start, len); - str[len] = '\0'; - return gcc_jit_context_new_string_literal(gen->ctx, str); -} - -static gcc_jit_rvalue* -emit_literal_int(Gen* gen, Node* node) -{ - return gcc_jit_context_new_rvalue_from_int(gen->ctx, type_int, (int)node->data.number.value); -} - -static gcc_jit_rvalue* -emit_literal_float(Gen* gen, Node* node) -{ - return gcc_jit_context_new_rvalue_from_double(gen->ctx, type_f32, node->data.number.value); -} - -static void -build_program(Gen* gen, Node* node) -{ - size_t cnt = node->data.program.len; - for (size_t i = 0; i < cnt; i++) { - gen_next(gen, node->data.program.decl[i]); - } -} - -static gcc_jit_rvalue* -lower_builtin_print(Gen* gen, Node* node) -{ - printf("lower_builtin_print\n"); - - size_t argc = node->data.call_expr.len; - - // for (size_t i = 0; i < argc; i++) { - // Node* arg = node->data.call_expr.args[i]; - // } - - // 1-arg, treat as puts(arg) - if (argc == 1) { - gcc_jit_rvalue* arg = handle_expr(gen, node->data.call_expr.args[0]); - gcc_jit_type* t = gcc_jit_rvalue_get_type(arg); - gcc_jit_location* loc = loc_from_node(gen, node->data.call_expr.args[0]); - // print a string - if (is_cstr(gen->ctx, t)) { - gcc_jit_rvalue* args[] = { arg }; - return gcc_jit_context_new_call(gen->ctx, loc, gen->puts_fn, 1, args); - } - // print an integer or bool - else if (is_intlike(gen->ctx, t) || t == type_boolean) { - // cast to int for a clean %d - // gcc_jit_type* t_int - // = gcc_jit_context_get_type(gen->ctx, GCC_JIT_TYPE_INT64_T); - gcc_jit_rvalue* fmt = gcc_jit_context_new_string_literal(gen->ctx, "%d\n"); - gcc_jit_rvalue* ival = arg; - gcc_jit_rvalue* args[] = { fmt, ival }; - return gcc_jit_context_new_call(gen->ctx, loc, gen->printf_fn, 2, args); - } - // cast common cases to const char* - // if (gcc_jit_rvalue_get_type(arg) != type_cstr) arg = - // gcc_jit_context_new_cast(gen->ctx, loc_from_node(gen, node), - // arg, type_cstr); - - // gcc_jit_rvalue* args[] = { arg }; - // return gcc_jit_context_new_call( - // gen->ctx, loc_from_node(gen, node), gen->puts_fn, 1, args); - - // gcc_jit_type* type_of_args = gcc_jit_rvalue_get_type(arg); - // switch (type_of_args) { - // case GCC_JIT_TYPE_CONST_CHAR_PTR: - - // break; - // default: - // printf("lower_builtin_print: unhandled type passed %s\n", - // type_of_args); break; - // } - } - - return NULL; - - // softpanic("we don't currently handle formatted strings to print"); - - // n>=1, treat as printf(fmt, ...) // Part of TODO about args as list - // and not - // - // through each args, form the ("formatted %s string %d etc.", str, - // intv) for clib's printf - - // TODO we're talking about formatting here, which we plan on doing as a - // string interpolation, something along the lines of {{variable}} - // without defining its type would involve lookup split of the string - // and then formatting - - // we need to discuss and decide what we'd do when the user inevitably - // would print out a ref to a struct. Do we say [[struct]] or do we have - // some automatic unwrap and display of struct data... probably, yes. - - gcc_jit_rvalue** args = (gcc_jit_rvalue**)calloc(MAXARGS, sizeof(gcc_jit_rvalue*)); - - if (argc > MAXARGS) { - panic_at(node, - "we do not currently support more than 16 args to a " - "print call"); - } - - for (size_t i = 0; i < argc; i++) { - gcc_jit_rvalue* arg = handle_expr(gen, node->data.call_expr.args[i]); - if (i == 0) { - if (gcc_jit_rvalue_get_type(arg) != type_cstr) { - // note this is probably not going to work as - // limited cast supported and string isn't one - // of them - arg = gcc_jit_context_new_cast(gen->ctx, loc_from_node(gen, node), arg, type_cstr); - } - } else { - // - // simple widening for common scalar types - // - gcc_jit_type* ty = gcc_jit_rvalue_get_type(arg); - if (ty == type_int) { - arg = gcc_jit_context_new_cast(gen->ctx, loc_from_node(gen, node), arg, type_cstr); - } else if (ty == type_f32) { // TODO print() for other types - // variadics already promote float→double; - // double is - } else if (ty == type_cstr) { - // leave as const char* - } else if (ty == type_boolean) { - arg = gcc_jit_context_new_cast(gen->ctx, loc_from_node(gen, node), arg, type_cstr); - } else { - // fallback: pass pointer as void* - arg = gcc_jit_context_new_cast(gen->ctx, - loc_from_node(gen, node), - arg, - gcc_jit_context_get_type(gen->ctx, GCC_JIT_TYPE_VOID_PTR)); - } - } - // TODO auto grow - args[i] = arg; - } - return gcc_jit_context_new_call(gen->ctx, NULL, gen->printf_fn, argc, args); - return NULL; -} - -static gcc_jit_function* -lookup_function(Gen* gen, Scope* scope, const char* func_name) -{ - for (size_t i = 0; i < scope->len; i++) { - Symbol* sym = scope->symbols[i]; - - if (sym->ctype == type_voidp) { - const char* name = span_str(gen->src, sym->name, (char[IDENTSZ]) { 0 }); - if (strcmp(name, func_name) == 0) { - return sym->d.funcvalue; // - } - } - } - - printf("lookup_function: not found locally: %s\n", func_name); - - if (scope->parent != NULL) { - printf("looking up function %s in parent\n", func_name); - return lookup_function(gen, scope->parent, func_name); - } - - return NULL; -} - -static gcc_jit_rvalue* -handle_func_call(Gen* gen, Node* node) -{ - Node* fcallee = node->data.call_expr.callee; - const char* func_name = span_str(gen->src, fcallee->data.ident.name, (char[IDENTSZ]) { 0 }); - - // short circuit to print - if (strcmp(func_name, "print") == 0) return lower_builtin_print(gen, node); - - gcc_jit_function* callee = lookup_function(gen, gen->scope, func_name); - gcc_jit_location* loc = loc_from_node(gen, node); - - // args handling - size_t argc = node->data.call_expr.len; - - // alloc args - gcc_jit_rvalue** args = NULL; - - if (argc > 0) { - args = calloc(argc, sizeof *args); // or alloca, or a fixed upper bound - for (size_t i = 0; i < argc; ++i) { - args[i] = handle_expr(gen, node->data.call_expr.args[i]); - } - } - - for (size_t i = 0; i < argc; i++) { - args[i] = handle_expr(gen, node->data.call_expr.args[i]); - } - - /* - When generating the identifier expression message in the AST: - Lookup env["message"] -> you get a gcc_jit_param* or gcc_jit_lvalue*. - Use gcc_jit_param_as_rvalue / gcc_jit_lvalue_as_rvalue. - - gcc_jit_rvalue *msg = gcc_jit_param_as_rvalue(callee_param); - - gcc_jit_rvalue *args[1] = { msg }; - gcc_jit_block_add_eval( - b, loc, - gcc_jit_context_new_call(ctx, loc, print_fn, 1, args)); - */ - - // gcc_jit_rvalue *msg = gcc_jit_param_as_rvalue(callee_param); - // gcc_jit_rvalue *args[1] = { msg }; - assert(callee != NULL && "callee not found"); - return gcc_jit_context_new_call(gen->ctx, loc, callee, argc, args); - - // TODO consider - /* - // calling bob() directly - void emit_direct_call(gcc_jit_context *ctxt, SymbolId bob, gcc_jit_function *caller) { - GccObj *cal = ensure_func(ctxt, bob); - gcc_jit_block *b = gcc_jit_function_new_block(caller, "call_bob"); - gcc_jit_rvalue *args[] = {}; - gcc_jit_block_add_eval(b, NULL, gcc_jit_context_new_call(ctxt, cal->func, 0, args)); - gcc_jit_block_end_with_void_return(b, NULL); - } - */ - - // return gcc_jit_context_new_call(gen->ctx, NULL, callee, 0, NULL); - - // TODO handle return values - // almost unrelated could be useful to deal with return values: - // gcc_jit_rvalue* rv = handle_func_call(gen, node); - // if (rv) { gcc_jit_block_add_eval(gen->curr_block, loc_from_node(gen, node), rv); } - // return false; - return NULL; -} - -static gcc_jit_rvalue* -handle_expr(Gen* gen, Node* node) -{ - switch (node->type) { - case NODE_INT_LITERAL: - return emit_literal_int(gen, node); - break; - case NODE_FLOAT_LITERAL: - return emit_literal_float(gen, node); - break; - case NODE_STRING_LITERAL: - return emit_literal_string(gen, node); - break; - case NODE_BOOL_LITERAL: - return emit_literal_bool(gen, node); - case NODE_CALL_EXPR: - return handle_func_call(gen, node); - break; - case NODE_IDENT: - return handle_ident_call(gen, node); - break; - case NODE_UNARY_EXPR: // ++ etc. - return handle_unary_expr(gen, node); - break; - case NODE_BINARY_EXPR: - return handle_binary_expr(gen, node); - break; - - // case NODE_IDENT: { - // return NULL; // fixme - // break; - // } - default: - printf("handle_expr unhandled, %s\n", node_type_str(node->type)); - } - return NULL; -} - -static gcc_jit_type* -ox_type_to_c_type(Gen* gen, Node* node) -{ - if (node == NULL) return type_void; - - const char* type_name = span_str(gen->src, node->data.ident.name, (char[IDENTSZ]) { 0 }); - - if (strcmp(type_name, "int") == 0) { - return type_int; - - } else if (strcmp(type_name, "i8") == 0) { - return type_i8; - } else if (strcmp(type_name, "i16") == 0) { - return type_i16; - } else if (strcmp(type_name, "i32") == 0) { - return type_i32; - } else if (strcmp(type_name, "i64") == 0) { - return type_i64; - } else if (strcmp(type_name, "i128") == 0) { - return type_i128; - - } else if (strcmp(type_name, "u8") == 0) { - return type_u8; - } else if (strcmp(type_name, "u16") == 0) { - return type_u16; - } else if (strcmp(type_name, "u32") == 0) { - return type_u32; - } else if (strcmp(type_name, "u64") == 0) { - return type_u64; - } else if (strcmp(type_name, "u128") == 0) { - return type_u128; - - } else if (strcmp(type_name, "f32") == 0) { - return type_f32; - } else if (strcmp(type_name, "f64") == 0) { - return type_f64; - } else if (strcmp(type_name, "f128") == 0) { - return type_f128; - - } else if (strcmp(type_name, "str") == 0) { - return type_cstr; - } else if (strcmp(type_name, "float") == 0) { - return type_f32; - } else if (strcmp(type_name, "double") == 0) { - return type_f64; - } else if (strcmp(type_name, "longdouble") == 0) { - return type_f128; - } else if (strcmp(type_name, "uint") == 0) { - return type_uint; - } else if (strcmp(type_name, "void") == 0) { - return type_void; - } else { - panic_at(node, "unhandled type in gen %s", type_name); - } - return NULL; -} - -static void -print_symbols_here(Gen* gen) -{ - for (size_t i = 0; i < gen->scope->len; i++) { - Symbol* sym = gen->scope->symbols[i]; - const char* name = span_str(gen->src, sym->name, (char[IDENTSZ]) { 0 }); - printf("[%zu/%zu] symbol: %s (%s)\n", i + 1, gen->scope->len, name, sym->english_type); - } -} - -static bool build_block(Gen*, Node*); -static bool build_statement(Gen*, Node*); - -static gcc_jit_rvalue* build_bool_value(Gen*, Node*); - -static int block_counter = 0, loop_counter = 0, while_counter = 0; - -static bool -build_while_statement(Gen* gen, Node* node) -{ - gcc_jit_location* loc = loc_from_node(gen, node); - - Node* cond = node->data.while_statement.cond; - Node* body = node->data.while_statement.body; - - while_counter++; - char label_cond[64], label_body[64], label_end[64]; - snprintf(label_cond, sizeof label_cond, "while.cond%d", while_counter); - snprintf(label_body, sizeof label_body, "while.body%d", while_counter); - snprintf(label_end, sizeof label_end, "while.end%d", while_counter); - - gcc_jit_block* cond_block = gcc_jit_function_new_block(gen->curr_func, label_cond); - gcc_jit_block* body_block = gcc_jit_function_new_block(gen->curr_func, label_body); - gcc_jit_block* end_block = gcc_jit_function_new_block(gen->curr_func, label_end); - - /* jump from current block into the loop */ - gcc_jit_block_end_with_jump(gen->curr_block, loc, cond_block); - - gen->curr_block = cond_block; - gcc_jit_rvalue* cnd = NULL; - - if (cond != NULL) { - cnd = build_bool_value(gen, cond); - gcc_jit_block_end_with_conditional(cond_block, loc, cnd, body_block, end_block); - } else { - /* while (1) */ - gcc_jit_block_end_with_jump(cond_block, loc, body_block); - } - - gcc_jit_block* continue_target = cond != NULL ? cond_block : body_block; - push_loop(gen, /*break to*/ end_block, /*continue to*/ continue_target); - - gen->curr_block = body_block; - bool body_ended = build_block(gen, body); - if (!body_ended) { gcc_jit_block_end_with_jump(gen->curr_block, loc, continue_target); } - - pop_loop(gen); - - /* resume after loop */ - gen->curr_block = end_block; - return false; -} - -static bool -build_for_statement(Gen* gen, Node* node) -{ - gcc_jit_location* loc = loc_from_node(gen, node); - - Node* init = node->data.for_statement.init; - Node* cond = node->data.for_statement.cond; - Node* step = node->data.for_statement.increment; - Node* body = node->data.for_statement.body; - - loop_counter++; - - char label_cond[64], label_body[64], label_step[64], label_end[64]; - snprintf(label_cond, 64, "for.cond%d", loop_counter); - snprintf(label_body, 64, "for.body%d", loop_counter); - snprintf(label_step, 64, "for.step%d", loop_counter); - snprintf(label_end, 64, "for.end%d", loop_counter); - - gcc_jit_block* cond_block = gcc_jit_function_new_block(gen->curr_func, label_cond); - gcc_jit_block* body_block = gcc_jit_function_new_block(gen->curr_func, label_body); - gcc_jit_block* step_block = gcc_jit_function_new_block(gen->curr_func, label_step); - gcc_jit_block* end_block = gcc_jit_function_new_block(gen->curr_func, label_end); - - // gcc_jit_block* saved_break = gen->loop->break_target; - // gcc_jit_block* saved_cont = gen->loop->continue_target; - // gen->loop->break_target = end_block; - // gen->loop->continue_target = step_block; - - // header, e.g. for(int = 0 <- - if (init) { build_statement(gen, init); } - - // jump to cond e.g. ; i < 5 <- - gcc_jit_block_end_with_jump(gen->curr_block, loc, cond_block); - - // cond: evaluate - gen->curr_block = cond_block; - gcc_jit_rvalue* cnd = NULL; - - if (cond != NULL) { - cnd = build_bool_value(gen, cond); - gcc_jit_block_end_with_conditional(cond_block, loc, cnd, body_block, end_block); - } else { - gcc_jit_block_end_with_jump(cond_block, loc, body_block); // presume for(;;) - } - - push_loop(gen, /*break to*/ end_block, /*continue to*/ step_block); - - // build for body - - gen->curr_block = body_block; - bool for_body_ended = build_block(gen, body); - if (!for_body_ended) { gcc_jit_block_end_with_jump(gen->curr_block, loc, step_block); } - - pop_loop(gen); - - // step incr etc. - - gen->curr_block = step_block; - if (step) { build_statement(gen, step); } - gcc_jit_block_end_with_jump(step_block, loc, cond != NULL ? cond_block : body_block); - - // resume after loop - - gen->curr_block = end_block; - return false; -} - -static bool -build_if_statement(Gen* gen, Node* node) -{ - gcc_jit_location* loc = loc_from_node(gen, node); - // build the condition - - OpType op = node->data.if_statement.cond->data.binary_expr.op; - Node* lhs = node->data.if_statement.cond->data.binary_expr.lhs; - Node* rhs = node->data.if_statement.cond->data.binary_expr.rhs; - - Node* then_body = node->data.if_statement.then_body; - Node* else_body = node->data.if_statement.else_body; - - enum gcc_jit_comparison cmp; - switch (op) { - case OP_EQUALITY: - cmp = GCC_JIT_COMPARISON_EQ; - break; - case OP_INEQUALITY: - cmp = GCC_JIT_COMPARISON_NE; - break; - case OP_GT: - cmp = GCC_JIT_COMPARISON_GT; - break; - case OP_GT_EQ: - cmp = GCC_JIT_COMPARISON_GE; - break; - case OP_LT_EQ: - cmp = GCC_JIT_COMPARISON_LE; - break; - case OP_LT: - cmp = GCC_JIT_COMPARISON_LT; - break; - default: - printf("/!\\ build_statement NODE_IF unhandled, %d\n", op); - cmp = GCC_JIT_COMPARISON_NE; - } - - gcc_jit_rvalue* lhs_val = handle_expr(gen, lhs); - gcc_jit_rvalue* rhs_val = handle_expr(gen, rhs); - - gcc_jit_rvalue* cond = gcc_jit_context_new_comparison(gen->ctx, loc, cmp, lhs_val, rhs_val); - - // create the BLOCKS - - // labels - block_counter++; - char label_then[64], label_else[64], label_end[64]; - snprintf(label_then, 64, "if.then%d", block_counter); - snprintf(label_else, 64, "if.else%d", block_counter); - snprintf(label_end, 64, "if.end%d", block_counter); - - // blocks - gcc_jit_block* then_bb = gcc_jit_function_new_block(gen->curr_func, label_then); - gcc_jit_block* else_bb = else_body ? gcc_jit_function_new_block(gen->curr_func, label_else) : NULL; - gcc_jit_block* merge_bb = NULL; - - if (!else_bb) { - // no else: need merge now for the false edge - merge_bb = gcc_jit_function_new_block(gen->curr_func, label_end); - gcc_jit_block_end_with_conditional(gen->curr_block, loc, cond, then_bb, merge_bb); - } else { - // with else: branch to then/else; decide on merge later - gcc_jit_block_end_with_conditional(gen->curr_block, loc, cond, then_bb, else_bb); - } - - // THEN - gen->curr_block = then_bb; - bool then_ended = build_block(gen, then_body); - gcc_jit_block* then_open = gen->curr_block; // last open block in THEN - // (may differ from then_bb) - - // ELSE - bool else_ended = false; - gcc_jit_block* else_open = NULL; - if (else_bb) { - gen->curr_block = else_bb; - else_ended = build_block(gen, else_body); - else_open = gen->curr_block; // last open block in ELSE - } - - // If both branches ended, no merge needed, we notify we have ended as - // well - if (else_bb && then_ended && else_ended) return true; - - // Ensure we have a merge if any branch continues. - if (!merge_bb) merge_bb = gcc_jit_function_new_block(gen->curr_func, label_end); - - if (!then_ended) gcc_jit_block_end_with_jump(then_open, loc, merge_bb); - if (else_bb && !else_ended) gcc_jit_block_end_with_jump(else_open, loc, merge_bb); - - gen->curr_block = merge_bb; - return false; -} - -static bool -build_var_decl_statement(Gen* gen, Node* node) -{ - gcc_jit_location* loc = loc_from_node(gen, node); - const char* var_name = span_str(gen->src, node->data.var_decl.name, (char[IDENTSZ]) { 0 }); - gcc_jit_type* declared_type = ox_type_to_c_type(gen, node->data.var_decl.type); - - gcc_jit_lvalue* var_decl = NULL; - - gcc_jit_rvalue* rvalue = NULL; - Node* init = node->data.var_decl.init; - if (init != NULL) { rvalue = handle_expr(gen, init); } - - if (gen->curr_func == NULL && gen->curr_block == NULL) { - // global var - var_decl = gcc_jit_context_new_global(gen->ctx, loc, GCC_JIT_GLOBAL_INTERNAL, declared_type, strdup(var_name)); - if (rvalue) gcc_jit_global_set_initializer_rvalue(var_decl, rvalue); - } else { - // local var - var_decl = gcc_jit_function_new_local(gen->curr_func, loc, declared_type, strdup(var_name)); - if (rvalue) gcc_jit_block_add_assignment(gen->curr_block, loc, var_decl, rvalue); - } - - // this is not an error, there is just no initial value - // if (node->data.var_decl.init == NULL) { panic("could not instanciate gcc jit new local"); } - - printf("adding 1 symbol: %s\n", var_name); - - Symbol* sym = (Symbol*)calloc(1, sizeof(Symbol)); - sym->name = node->data.var_decl.name; - sym->decl = node; - sym->ctype = declared_type; - sym->d.lvalue = var_decl; - sym->english_type = type_var; - sym->is_variadic = node->data.var_decl.type->data.ident_type.is_variadic; - sym->is_comp_time = node->data.var_decl.type->data.ident_type.is_comp_time; - add_symbol(gen, sym); // add var_decl symbol - return false; -} - -static void build_func_decl(Gen*, Node*); - -static bool -build_statement(Gen* gen, Node* node) -{ - gcc_jit_location* loc = loc_from_node(gen, node); - switch (node->type) { - case NODE_RETURN: { - Node* return_expr = node->data.ret.expr; - if (return_expr) { - gcc_jit_rvalue* ret_rval = handle_expr(gen, return_expr); - gcc_jit_type* expect_type = gcc_jit_function_get_return_type(gen->curr_func); - gcc_jit_type* ret_type = gcc_jit_rvalue_get_type(ret_rval); - types_match_or_panic(node, expect_type, ret_type); - gcc_jit_block_end_with_return(gen->curr_block, loc, ret_rval); - } else { - gcc_jit_block_end_with_void_return(gen->curr_block, loc); - } - gen->curr_block = NULL; // important - return true; // we end the block here - } - case NODE_BREAK: { - gcc_jit_block* t = current_break(gen); - if (!t) return true; // break outside the loop - gcc_jit_block_end_with_jump(gen->curr_block, loc, t); - gen->curr_block = NULL; - return true; - } - case NODE_CONTINUE: { - gcc_jit_block* t = current_continue(gen); - if (!t) return true; // continue outside of a loop - gcc_jit_block_end_with_jump(gen->curr_block, loc, t); - gen->curr_block = NULL; - return true; - } - case NODE_VAR_DECL: { - return build_var_decl_statement(gen, node); - } - case NODE_VAR_ASSIGN: { - gcc_jit_rvalue* rvalue = handle_expr(gen, node->data.var_assign.rhs); - // find the var_decl (d.lvalue) from the symbols table's symbol. - for (size_t i = 0; i < gen->scope->len; i++) { - Symbol* sym = gen->scope->symbols[i]; - if (span_ident_same(sym->name, node->data.var_assign.lhs->data.ident.name, gen->src)) { - // we found the symbol we are assigning to - // TODO check for variadic and comp_time - if (!sym->is_variadic) { - const char* name = span_str(gen->src, sym->name, (char[IDENTSZ]) { 0 }); - panic_at(node, "symbol %s is not variadic, cannot mutate.", name); - } - if (sym->is_comp_time) { - const char* name = span_str(gen->src, sym->name, (char[IDENTSZ]) { 0 }); - panic_at(node, "symbol %s is comp time, cannot mutate.", name); - } - - gcc_jit_lvalue* lvalue = sym->d.lvalue; - - // check the type of lvalue matches the rvalue - gcc_jit_type* ltype = sym->ctype; - gcc_jit_type* rtype = gcc_jit_rvalue_get_type(rvalue); - if (rtype != ltype) { - panic_at(node, - "right hand side of assigment " - "doesn't match the " - "type of the left hand side: %s <-- %s", - get_english_type(ltype), - get_english_type(rtype)); - } - if (lvalue) gcc_jit_block_add_assignment(gen->curr_block, loc, lvalue, rvalue); - } - } - break; - } - case NODE_BINARY_EXPR: { - Symbol* sym = find_symbol(gen, gen->scope, node->data.binary_expr.lhs->data.ident.name); - if (!sym) - panic_at(node, - "NODE_BINARY_EXPR: Undefined symbol: %s\n", - span_str(gen->src, node->data.ident.name, (char[IDENTSZ]) { 0 })); - - gcc_jit_lvalue* lv = sym->d.lvalue; - gcc_jit_rvalue* rhs = handle_expr(gen, node->data.binary_expr.rhs); - gcc_jit_rvalue* lhs = handle_expr(gen, node->data.binary_expr.lhs); - gcc_jit_rvalue* temp = gcc_jit_lvalue_as_rvalue(lv); - gcc_jit_type* lvtype = gcc_jit_rvalue_get_type(temp); - gcc_jit_rvalue* sum - = gcc_jit_context_new_binary_op(gen->ctx, loc_from_node(gen, node), GCC_JIT_BINARY_OP_PLUS, lvtype, lhs, rhs); - - gcc_jit_block_add_assignment(gen->curr_block, loc_from_node(gen, node), lv, sum); - } - case NODE_UNARY_EXPR: { - gcc_jit_rvalue* rv = handle_unary_expr(gen, node); - if (rv) gcc_jit_block_add_eval(gen->curr_block, loc_from_node(gen, node), rv); - break; - } - case NODE_EXPR_STATEMENT: { - gcc_jit_rvalue* rv = handle_expr(gen, node->data.expr_statement.expr); - if (rv) gcc_jit_block_add_eval(gen->curr_block, loc_from_node(gen, node), rv); - break; - } - case NODE_IF: - return build_if_statement(gen, node); - case NODE_FOR: - return build_for_statement(gen, node); - case NODE_WHILE: - return build_while_statement(gen, node); - case NODE_FUNCTION_DECL: - // "closure" - build_func_decl(gen, node); - return false; - case NODE_BLOCK: - build_block(gen, node); - default: - printf("--- WARNING: build_statement unhandled, %s\n", node_type_str(node->type)); - break; - } - return false; -} - -static gcc_jit_rvalue* -build_bool_value(Gen* gen, Node* node) -{ - gcc_jit_location* loc = loc_from_node(gen, node); - - switch (node->type) { - case NODE_BOOL_LITERAL: - /* true/false literal */ - return gcc_jit_context_new_rvalue_from_int(gen->ctx, type_boolean, node->data.boolean.value ? 1 : 0); - break; - case NODE_INT_LITERAL: { - /* while (1) style – C truthiness */ - gcc_jit_rvalue* iv = emit_literal_int(gen, node); - return gcc_jit_context_new_cast(gen->ctx, loc, iv, type_boolean); - } - case NODE_STRING_LITERAL: { - /* while("word") should also be truthy if the pointer is non null */ - gcc_jit_rvalue* v = emit_literal_string(gen, node); - gcc_jit_type* ty = gcc_jit_rvalue_get_type(v); - gcc_jit_rvalue* nullv = gcc_jit_context_null(gen->ctx, ty); - return gcc_jit_context_new_comparison(gen->ctx, loc, GCC_JIT_COMPARISON_NE, v, nullv); - } - case NODE_BINARY_EXPR: { - gcc_jit_rvalue* lvalue = handle_expr(gen, node->data.binary_expr.lhs); - gcc_jit_rvalue* rvalue = handle_expr(gen, node->data.binary_expr.rhs); - enum gcc_jit_comparison op; - switch (node->data.binary_expr.op) { - case OP_LT: - op = GCC_JIT_COMPARISON_LT; - break; - case OP_GT: - op = GCC_JIT_COMPARISON_GT; - break; - case OP_LT_EQ: - op = GCC_JIT_COMPARISON_LE; - break; - case OP_GT_EQ: - op = GCC_JIT_COMPARISON_GE; - break; - default: - printf("build_bool_rvalue nodebinary op unhandled, " - "%s\n", - node_type_str(node->type)); - return NULL; - } - return gcc_jit_context_new_comparison(gen->ctx, loc, op, lvalue, rvalue); - break; - } - default: - printf("/!\\ build_bool_rvalue unhandled, %s\n", node_type_str(node->type)); - break; - } - - return NULL; -} - -// build_block: returns true if block ended with a terminator -static bool -build_block(Gen* gen, Node* body) -{ - // TODO if there is no block ({ ... }), we presume there is only 1 statement. - // This might not always be valid, func declaration do require a block {}, but there might be cases... - // FIXME: a symbol declared in a child block is accessible in the parent or in a sibling block... - if (body->data.block.len == 0) { return build_statement(gen, body); } - - for (size_t i = 0; i < body->data.block.len; i++) { - if (gen->curr_block == NULL) return true; - bool ended = build_statement(gen, body->data.block.stmts[i]); - if (ended) return true; - } - return false; -} - -static void -build_func_decl(Gen* gen, Node* node) -{ - const char* func_name = span_str(gen->src, node->data.function_decl.name, (char[IDENTSZ]) { 0 }); - - Node* return_type = node->data.function_decl.return_type; - gcc_jit_type* ret_type = ox_type_to_c_type(gen, return_type); - gcc_jit_location* loc = loc_from_node(gen, node); - - size_t argc = node->data.function_decl.p_len; - - gcc_jit_param** params = calloc(argc, sizeof(gcc_jit_param*)); - - // 1) Create ONE gcc_jit_param per AST param - for (size_t i = 0; i < argc; i++) { - Node* pnode = node->data.function_decl.params[i]; - const char* pname = span_str(gen->src, pnode->data.param.name, (char[IDENTSZ]) { 0 }); - - gcc_jit_type* declared_type = ox_type_to_c_type(gen, pnode->data.param.type); - - gcc_jit_param* p = gcc_jit_context_new_param(gen->ctx, loc, declared_type, strdup(pname)); - - params[i] = p; - } - - // 2) Create the function with those params - gcc_jit_function* func = gcc_jit_context_new_function(gen->ctx, - loc, - GCC_JIT_FUNCTION_EXPORTED, - ret_type, - strdup(func_name), - argc, - params, // <-- these are now owned by 'func' - 0); - - gcc_jit_block* block = gcc_jit_function_new_block(func, "entry"); - - gcc_jit_function* prev_func = gen->curr_func; - gcc_jit_block* prev_block = gen->curr_block; - gen->curr_func = func; - gen->curr_block = block; - - // 3) Add the function symbol to the current (enclosing) scope - Symbol* sym = (Symbol*)calloc(1, sizeof(Symbol)); - sym->name = node->data.function_decl.name; - sym->decl = node; - sym->ctype = type_voidp; // whatever you use for function type metadata - sym->d.funcvalue = func; - sym->english_type = type_func; - add_symbol(gen, sym); // add function symbol -- to the 'parent scope', before we push_scope - - // ENTER FUNCTION SCOPE - push_scope(gen); - - // 4) Add parameter symbols to the FUNCTION scope, reusing the same gcc_jit_param* - for (size_t i = 0; i < argc; i++) { - Node* pnode = node->data.function_decl.params[i]; - - gcc_jit_param* p = params[i]; // <-- reuse, DO NOT new_param again - - gcc_jit_type* declared_type = ox_type_to_c_type(gen, pnode->data.param.type); - - Symbol* ps = (Symbol*)calloc(1, sizeof(Symbol)); - ps->name = pnode->data.param.name; // param identifier span - ps->decl = pnode; - ps->ctype = declared_type; - ps->d.param = p; // <-- this is associated with 'func' - ps->english_type = type_var; // or type_param if you distinguish - add_symbol(gen, ps); // add param symbol - } - - print_symbols_here(gen); - - build_block(gen, node->data.function_decl.body); - - if (gen->curr_block == NULL) { - // no open block: all paths already terminated, don't add an implicit return - } else if (ret_type == type_int) { - gcc_jit_rvalue* ret_value = gcc_jit_context_new_rvalue_from_int(gen->ctx, type_int, 0); - gcc_jit_block_end_with_return(gen->curr_block, loc, ret_value); - } else if (ret_type == type_void) { - gcc_jit_block_end_with_void_return(gen->curr_block, loc); - } else { - printf("build_func_decl unhandled return type in func: %s - " - "defaulting to void\n", - func_name); - gcc_jit_block_end_with_void_return(gen->curr_block, loc); - } - - // LEAVE FUNCTION SCOPE - pop_scope(gen); - - gen->curr_func = prev_func; - gen->curr_block = prev_block; -} - -void -gen_next(Gen* gen, Node* node) -{ - // printf("gen_next, %s\n", node_type_str(node->type)); - - switch (node->type) { - case NODE_PROGRAM: - build_program(gen, node); - break; - case NODE_FUNCTION_DECL: - build_func_decl(gen, node); - break; - case NODE_STRING_LITERAL: - emit_literal_string(gen, node); - break; - case NODE_INT_LITERAL: - emit_literal_int(gen, node); - break; - case NODE_FLOAT_LITERAL: - emit_literal_float(gen, node); - break; - case NODE_VAR_DECL: - build_statement(gen, node); - break; - default: - printf("gen: unhandled, %s\n", node_type_str(node->type)); - } -}