Files
ivy/lang/codegen/codegen.c

500 lines
10 KiB
C
Raw Normal View History

#include "codegen.h"
#include <ivy/lang/codegen.h>
2025-06-02 11:31:35 +01:00
#include <mie/ir/builder.h>
#include <mie/ir/module.h>
#include <mie/ir/value.h>
#include <stdlib.h>
#include <string.h>
/* new codegen layout:
* - traverse the AST in pre-order (i.e. in the order it's displayed by debug)
* - each node type (var, msg, expr) gets its own code generator type
* - expr code generator is limited to simple operator arithmetic.
* - when a code generator encounters a node of an equal or lower depth than
* the node that started it, it has reached the end of its subtree.
* - depth is supplied by the ast iterator.
* - need to devise a way for expression code generator to "return" a
* mie_value for its parent generator to use.
*/
static struct code_generator_state *get_current_generator_state(
struct ivy_codegen *gen)
{
b_queue_entry *entry = b_queue_last(&gen->c_state);
if (!entry) {
return NULL;
}
return b_unbox(struct code_generator_state, entry, s_entry);
}
static void code_generator_state_inherit(
struct code_generator_state *parent, struct code_generator_state *child)
{
child->s_loop_break_target = parent->s_loop_break_target;
child->s_loop_repeat_target = parent->s_loop_repeat_target;
}
enum ivy_status codegen_push_generator(
struct ivy_codegen *gen, enum code_generator_type gen_type,
uintptr_t argv, void *argp)
{
const struct code_generator *generator = get_code_generator(gen_type);
if (!generator) {
return IVY_ERR_INVALID_VALUE;
}
if (generator->g_state_size < sizeof(struct code_generator_state)) {
return IVY_ERR_INTERNAL_FAILURE;
}
struct code_generator_state *state = malloc(generator->g_state_size);
if (!state) {
return IVY_ERR_NO_MEMORY;
}
memset(state, 0x0, generator->g_state_size);
state->s_gen = generator;
enum ivy_status status = IVY_OK;
struct code_generator_state *parent_state
= get_current_generator_state(gen);
if (parent_state) {
code_generator_state_inherit(parent_state, state);
}
if (generator->g_state_init) {
status = generator->g_state_init(gen, state, argv, argp);
}
if (status != IVY_OK) {
free(state);
return status;
}
b_queue_push_back(&gen->c_state, &state->s_entry);
return IVY_OK;
}
enum ivy_status codegen_pop_generator(
struct ivy_codegen *gen, struct code_generator_value *result)
{
b_queue_entry *entry = b_queue_pop_back(&gen->c_state);
if (!entry) {
return IVY_ERR_BAD_STATE;
}
struct code_generator_state *state
= b_unbox(struct code_generator_state, entry, s_entry);
enum ivy_status status = IVY_OK;
if (state->s_gen->g_state_fini) {
status = state->s_gen->g_state_fini(gen, state, result);
}
if (status != IVY_OK) {
return status;
}
free(state);
return IVY_OK;
}
enum ivy_status codegen_define_variable(
struct ivy_codegen *gen, const char *ident, const struct codegen_var *var)
{
b_queue_entry *entry = b_queue_last(&gen->c_state);
if (!entry) {
return IVY_ERR_NO_ENTRY;
}
bool resolved = false;
while (entry) {
struct code_generator_state *state
= b_unbox(struct code_generator_state, entry, s_entry);
const struct code_generator *generator = state->s_gen;
enum ivy_status status = IVY_ERR_NO_ENTRY;
if (generator->g_define_var) {
status = generator->g_define_var(gen, state, ident, var);
}
if (status == IVY_OK) {
break;
}
entry = b_queue_prev(entry);
}
return IVY_ERR_NOT_SUPPORTED;
}
#if 0
enum ivy_status resolve_global_variable(
struct ivy_codegen *gen, const char *ident, struct codegen_var *out)
{
struct mie_value *var_ptr
= mie_builder_get_data_ptr(gen->c_builder, ident);
out->v_type = mie_ctx_get_type(gen->c_ctx, MIE_TYPE_ID);
out->v_node = NULL;
out->v_flags = CODEGEN_VAR_F_PTR;
out->v_value = var_ptr;
return IVY_OK;
}
#endif
enum ivy_status codegen_resolve_variable(
struct ivy_codegen *gen, const char *ident, struct codegen_var *out)
{
b_queue_entry *entry = b_queue_last(&gen->c_state);
if (!entry) {
return IVY_ERR_NO_ENTRY;
}
bool resolved = false;
while (entry) {
struct code_generator_state *state
= b_unbox(struct code_generator_state, entry, s_entry);
const struct code_generator *generator = state->s_gen;
enum ivy_status status = IVY_ERR_NO_ENTRY;
if (generator->g_resolve_var) {
status = generator->g_resolve_var(gen, state, ident, out);
}
if (status == IVY_OK) {
resolved = true;
break;
}
entry = b_queue_prev(entry);
}
#if 0
if (!resolved) {
return resolve_global_variable(gen, ident, out);
}
#endif
if (entry) {
entry = b_queue_next(entry);
}
while (entry) {
struct code_generator_state *state
= b_unbox(struct code_generator_state, entry, s_entry);
const struct code_generator *generator = state->s_gen;
enum ivy_status status = IVY_OK;
if (generator->g_capture_var) {
status = generator->g_capture_var(gen, state, ident, out);
}
if (status != IVY_OK) {
return status;
}
entry = b_queue_next(entry);
}
return IVY_OK;
}
struct mie_value *codegen_load_variable(
struct ivy_codegen *gen, struct codegen_var *var)
{
if (var->v_flags & CODEGEN_VAR_F_PTR) {
return mie_builder_load(
gen->c_builder, var->v_type, var->v_value, NULL);
}
return var->v_value;
}
void codegen_store_variable(
struct ivy_codegen *gen, struct codegen_var *var, struct mie_value *val)
{
if (!(var->v_flags & CODEGEN_VAR_F_PTR)) {
return;
}
mie_builder_store(gen->c_builder, val, var->v_value);
}
enum ivy_status ivy_codegen_create(struct mie_ctx *ctx, struct ivy_codegen **out)
{
struct ivy_codegen *gen = malloc(sizeof *gen);
if (!gen) {
return IVY_ERR_NO_MEMORY;
}
memset(gen, 0x0, sizeof *gen);
gen->c_ctx = ctx;
*out = gen;
return IVY_OK;
}
void ivy_codegen_destroy(struct ivy_codegen *gen)
{
if (gen->c_module) {
mie_value_destroy(MIE_VALUE(gen->c_module));
}
if (gen->c_builder) {
mie_builder_destroy(gen->c_builder);
}
free(gen);
}
enum ivy_status ivy_codegen_start_module(struct ivy_codegen *gen)
{
if (gen->c_module || gen->c_builder) {
return IVY_ERR_BAD_STATE;
}
gen->c_module = mie_module_create();
gen->c_builder = mie_builder_create(gen->c_ctx, gen->c_module);
return IVY_OK;
}
enum ivy_status ivy_codegen_end_module(
struct ivy_codegen *gen, struct mie_module **out)
{
if (!gen->c_module) {
return IVY_ERR_BAD_STATE;
}
struct mie_module *module = gen->c_module;
mie_builder_destroy(gen->c_builder);
gen->c_module = NULL;
gen->c_builder = NULL;
*out = module;
return IVY_OK;
}
struct mie_module *ivy_codegen_get_current_module(struct ivy_codegen *gen)
{
return gen->c_module;
}
static enum ivy_status pop_generator_recurse(
struct ivy_codegen *gen, size_t node_depth)
{
enum ivy_status status;
while (1) {
struct code_generator_value value = {0};
status = codegen_pop_generator(gen, &value);
if (status != IVY_OK) {
return status;
}
struct code_generator_state *state
= get_current_generator_state(gen);
if (!state) {
return IVY_OK;
}
struct code_generator_result result = {
.r_status = IVY_OK,
};
if (state->s_gen->g_value_received) {
state->s_gen->g_value_received(gen, state, &value);
}
bool should_pop = ((result.r_flags & CODEGEN_R_POP_GENERATOR) != 0)
|| node_depth <= state->s_depth;
if (result.r_status != IVY_OK) {
return result.r_status;
}
if (!should_pop) {
return IVY_OK;
}
}
return IVY_OK;
}
static bool node_is_expr(struct ivy_ast_node *node)
{
switch (node->n_type) {
case IVY_AST_OP:
case IVY_AST_MSG:
case IVY_AST_LAMBDA:
case IVY_AST_DISCARD:
case IVY_AST_INT:
case IVY_AST_DOUBLE:
case IVY_AST_C_TRUE:
case IVY_AST_C_FALSE:
case IVY_AST_C_NULL:
case IVY_AST_STRING:
case IVY_AST_FSTRING:
case IVY_AST_ATOM:
case IVY_AST_IDENT:
case IVY_AST_CASCADE:
case IVY_AST_MATCH:
case IVY_AST_COND_GROUP:
case IVY_AST_TUPLE:
case IVY_AST_PKG_STATIC:
case IVY_AST_PKG_DYNAMIC:
return true;
default:
return false;
}
}
enum ivy_status ivy_codegen_push_node(
struct ivy_codegen *gen, struct ivy_ast_node *node, size_t node_depth)
{
if (!gen->c_builder) {
return IVY_ERR_BAD_STATE;
}
if (b_queue_empty(&gen->c_state)) {
const struct code_generator *generator
= get_root_code_generator(node->n_type);
if (!generator) {
return IVY_ERR_BAD_SYNTAX;
}
codegen_push_generator(gen, generator->g_type, 0, NULL);
}
while (true) {
struct code_generator_state *state
= get_current_generator_state(gen);
if (!state) {
return IVY_ERR_BAD_STATE;
}
enum ivy_status status = IVY_OK;
struct code_generator_result result = {};
if (!state->s_root) {
state->s_root = node;
state->s_depth = node_depth;
} else if (node_depth <= state->s_depth) {
status = pop_generator_recurse(gen, node_depth);
}
if (status != IVY_OK) {
return status;
}
state = get_current_generator_state(gen);
const struct code_generator *generator = state->s_gen;
bool is_expr = node_is_expr(node);
code_generator_node_callback func
= generator->g_node_generators[node->n_type];
if (!func && is_expr) {
func = generator->g_expr_generator;
}
if (!func) {
return IVY_OK;
}
result = func(gen, state, node, node_depth);
if (result.r_status != IVY_OK) {
return result.r_status;
}
if (result.r_flags & CODEGEN_R_REPEAT_NODE) {
continue;
}
return result.r_status;
}
return IVY_OK;
}
enum ivy_status ivy_codegen_push_eof(struct ivy_codegen *gen)
{
enum ivy_status status = IVY_OK;
struct code_generator_result result = {
.r_status = IVY_OK,
};
while (!b_queue_empty(&gen->c_state)) {
struct code_generator_value value = {0};
status = codegen_pop_generator(gen, &value);
if (status != IVY_OK) {
return status;
}
struct code_generator_state *state
= get_current_generator_state(gen);
if (!state || !state->s_gen->g_value_received) {
continue;
}
result = state->s_gen->g_value_received(gen, state, &value);
if (result.r_status != IVY_OK) {
break;
}
}
return result.r_status;
}
struct mie_value *code_generator_value_get_mie_value(struct code_generator_value *vp)
{
if (vp->v_type != CODE_GENERATOR_VALUE_MIE_VALUE) {
return NULL;
}
return vp->v_value.mie_value;
}
void code_generator_value_set_mie_value(
struct code_generator_value *vp, struct mie_value *value)
{
vp->v_type = CODE_GENERATOR_VALUE_MIE_VALUE;
vp->v_value.mie_value = value;
}
extern struct mie_phi_edge *code_generator_value_get_phi_edge(
struct code_generator_value *vp)
{
if (vp->v_type != CODE_GENERATOR_VALUE_PHI_EDGE) {
return NULL;
}
return vp->v_value.phi_edge;
}
void code_generator_value_set_phi_edge(
struct code_generator_value *vp, struct mie_phi_edge *edge)
{
vp->v_type = CODE_GENERATOR_VALUE_PHI_EDGE;
vp->v_value.phi_edge = edge;
}