gen.c (43481B)
#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));
}
}