You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ravi/src/ravi_ast_linearize.c

1019 lines
34 KiB

/*
Copyright (C) 2018-2020 Dibyendu Majumdar
*/
#include "ravi_ast.h"
#include "ptrlist.h"
#include "ravi_fnv_hash.h"
#include <string.h>
#include <stdlib.h>
#include <stddef.h>
static struct pseudo *linearize_expression(struct proc *proc, struct ast_node *expr);
static inline unsigned alloc_reg(struct pseudo_generator *generator) {
if (generator->free_pos > 0) {
return generator->free_regs[--generator->free_pos];
}
return generator->next_reg++;
}
static inline void free_reg(struct pseudo_generator *generator, unsigned reg) {
if (generator->free_pos == (sizeof generator->free_regs / sizeof generator->free_regs[0])) {
/* TODO proper error handling */
fprintf(stderr, "Out of register space\n");
abort();
}
generator->free_regs[generator->free_pos++] = (uint8_t)reg;
}
/* Linearizer - WIP */
void raviA_init_linearizer(struct linearizer *linearizer, struct ast_container *container) {
memset(linearizer, 0, sizeof *linearizer);
linearizer->ast_container = container;
dmrC_allocator_init(&linearizer->edge_allocator, "edge_allocator", sizeof(struct edge), sizeof(double), CHUNK);
dmrC_allocator_init(&linearizer->instruction_allocator, "instruction_allocator", sizeof(struct instruction),
sizeof(double), CHUNK);
dmrC_allocator_init(&linearizer->ptrlist_allocator, "ptrlist_allocator", sizeof(struct ptr_list), sizeof(double),
CHUNK);
dmrC_allocator_init(&linearizer->pseudo_allocator, "pseudo_allocator", sizeof(struct pseudo), sizeof(double), CHUNK);
dmrC_allocator_init(&linearizer->basic_block_allocator, "basic_block_allocator", sizeof(struct basic_block),
sizeof(double), CHUNK);
dmrC_allocator_init(&linearizer->proc_allocator, "proc_allocator", sizeof(struct proc), sizeof(double), CHUNK);
dmrC_allocator_init(&linearizer->unsized_allocator, "unsized_allocator", 0, sizeof(double), CHUNK);
dmrC_allocator_init(&linearizer->constant_allocator, "constant_allocator", sizeof(struct constant), sizeof(double),
CHUNK);
}
void raviA_destroy_linearizer(struct linearizer *linearizer) {
struct proc *proc;
FOR_EACH_PTR(linearizer->all_procs, proc) {
if (proc->constants)
set_destroy(proc->constants, NULL);
}
END_FOR_EACH_PTR(proc);
dmrC_allocator_destroy(&linearizer->edge_allocator);
dmrC_allocator_destroy(&linearizer->instruction_allocator);
dmrC_allocator_destroy(&linearizer->ptrlist_allocator);
dmrC_allocator_destroy(&linearizer->pseudo_allocator);
dmrC_allocator_destroy(&linearizer->basic_block_allocator);
dmrC_allocator_destroy(&linearizer->proc_allocator);
dmrC_allocator_destroy(&linearizer->unsized_allocator);
dmrC_allocator_destroy(&linearizer->constant_allocator);
}
static int compare_constants(const void *a, const void *b) {
const struct constant *c1 = (const struct constant *)a;
const struct constant *c2 = (const struct constant *)b;
if (c1->type != c2->type)
return 1;
if (c1->type == RAVI_TNUMINT)
return c1->i == c2->i;
else if (c1->type == RAVI_TNUMFLT)
return c1->n == c2->n;
else
return c1->s == c2->s;
}
static uint32_t hash_constant(const void *c) {
const struct constant *c1 = (const struct constant *)c;
if (c1->type == RAVI_TNUMINT)
return (int)c1->i;
else if (c1->type == RAVI_TNUMFLT)
return (int)c1->n; // FIXME maybe use Lua's hash gen
else
return c1->s->hash;
}
static const struct constant *add_constant(struct proc *proc, const struct constant *c) {
struct set_entry *entry = set_search(proc->constants, c);
if (entry == NULL) {
int reg = proc->num_constants++;
struct constant *c1 = dmrC_allocator_allocate(&proc->linearizer->constant_allocator, 0);
assert(c1);
memcpy(c1, c, sizeof(struct constant));
c1->index = reg;
set_add(proc->constants, c1);
// printf("Created new constant and assigned reg %d\n", reg);
return c1;
}
else {
const struct constant *c1 = entry->key;
// printf("Found constant at reg %d\n", c1->index);
return c1;
}
}
static const struct constant *allocate_constant(struct proc *proc, struct ast_node *node) {
assert(node->type == AST_LITERAL_EXPR);
struct constant c = {.type = node->literal_expr.type.type_code};
if (c.type == RAVI_TNUMINT)
c.i = node->literal_expr.u.i;
else if (c.type == RAVI_TNUMFLT)
c.n = node->literal_expr.u.n;
else
c.s = node->literal_expr.u.s;
return add_constant(proc, &c);
}
static const struct constant *allocate_constant_for_i(struct proc *proc, int i) {
struct constant c = {.type = RAVI_TNUMINT, .i = i};
return add_constant(proc, &c);
}
static const struct constant *allocate_string_constant(struct proc *proc, const TString *s) {
struct constant c = {.type = RAVI_TSTRING, .s = s};
return add_constant(proc, &c);
}
struct pseudo *allocate_symbol_pseudo(struct proc *proc, struct lua_symbol *sym, unsigned reg) {
assert(sym->var.pseudo == NULL);
struct pseudo *pseudo = dmrC_allocator_allocate(&proc->linearizer->pseudo_allocator, 0);
pseudo->type = PSEUDO_SYMBOL;
pseudo->symbol = sym;
pseudo->regnum = reg;
if (sym->symbol_type == SYM_LOCAL) {
sym->var.pseudo = pseudo;
}
return pseudo;
}
struct pseudo *allocate_constant_pseudo(struct proc *proc, const struct constant *constant) {
struct pseudo *pseudo = dmrC_allocator_allocate(&proc->linearizer->pseudo_allocator, 0);
pseudo->type = PSEUDO_CONSTANT;
pseudo->constant = constant;
pseudo->regnum = constant->index;
return pseudo;
}
struct pseudo *allocate_closure_pseudo(struct linearizer *linearizer, struct proc *proc) {
struct pseudo *pseudo = dmrC_allocator_allocate(&proc->linearizer->pseudo_allocator, 0);
pseudo->type = PSEUDO_PROC;
pseudo->proc = proc;
return pseudo;
}
struct pseudo *allocate_nil_pseudo(struct proc *proc) {
struct pseudo *pseudo = dmrC_allocator_allocate(&proc->linearizer->pseudo_allocator, 0);
pseudo->type = PSEUDO_NIL;
pseudo->proc = proc;
return pseudo;
}
struct pseudo *allocate_boolean_pseudo(struct proc *proc, bool is_true) {
struct pseudo *pseudo = dmrC_allocator_allocate(&proc->linearizer->pseudo_allocator, 0);
pseudo->type = is_true ? PSEUDO_TRUE : PSEUDO_FALSE;
pseudo->proc = proc;
return pseudo;
}
struct pseudo *allocate_temp_pseudo(struct proc *proc, ravitype_t type) {
struct pseudo_generator *gen;
enum pseudo_type pseudo_type;
switch (type) {
case RAVI_TNUMFLT:
gen = &proc->temp_int_pseudos;
pseudo_type = PSEUDO_TEMP_FLT;
break;
case RAVI_TNUMINT:
gen = &proc->temp_flt_pseudos;
pseudo_type = PSEUDO_TEMP_INT;
break;
default:
gen = &proc->temp_pseudos;
pseudo_type = PSEUDO_TEMP_ANY;
break;
}
unsigned reg = alloc_reg(gen);
struct pseudo *pseudo = dmrC_allocator_allocate(&proc->linearizer->pseudo_allocator, 0);
pseudo->type = pseudo_type;
pseudo->regnum = reg;
pseudo->temp_type = type;
return pseudo;
}
void free_temp_pseudo(struct proc *proc, struct pseudo *pseudo) {
struct pseudo_generator *gen;
switch (pseudo->type) {
case PSEUDO_TEMP_FLT:
gen = &proc->temp_int_pseudos;
break;
case PSEUDO_TEMP_INT:
gen = &proc->temp_flt_pseudos;
break;
case PSEUDO_TEMP_ANY:
gen = &proc->temp_pseudos;
break;
default:
// Not a temp, so no need to do anything
return;
}
free_reg(gen, pseudo->regnum);
}
/**
* Allocate a new proc. If there is a current proc, then the new proc gets added to the
* current procs children.
*/
static struct proc *allocate_proc(struct linearizer *linearizer, struct ast_node *function_expr) {
assert(function_expr->type == AST_FUNCTION_EXPR);
struct proc *proc = dmrC_allocator_allocate(&linearizer->proc_allocator, 0);
proc->function_expr = function_expr;
ptrlist_add((struct ptr_list **)&linearizer->all_procs, proc, &linearizer->ptrlist_allocator);
if (linearizer->current_proc) {
proc->parent = linearizer->current_proc;
ptrlist_add((struct ptr_list **)&linearizer->current_proc->procs, proc, &linearizer->ptrlist_allocator);
}
proc->constants = set_create(hash_constant, compare_constants);
proc->linearizer = linearizer;
return proc;
}
static void set_main_proc(struct linearizer *linearizer, struct proc *proc) {
assert(linearizer->main_proc == NULL);
assert(linearizer->current_proc == NULL);
linearizer->main_proc = proc;
assert(proc->function_expr->function_expr.parent_function == NULL);
}
static inline void set_current_proc(struct linearizer *linearizer, struct proc *proc) {
linearizer->current_proc = proc;
}
static void linearize_function_args(struct linearizer *linearizer) {
struct proc *proc = linearizer->current_proc;
struct ast_node *func_expr = proc->function_expr;
struct lua_symbol *sym;
FOR_EACH_PTR(func_expr->function_expr.args, sym) {
// printf("Assigning register %d to argument %s\n", (int)reg, getstr(sym->var.var_name));
}
END_FOR_EACH_PTR(sym);
}
static void linearize_statement(struct proc *proc, struct ast_node *node);
static void linearize_statement_list(struct proc *proc, struct ast_node_list *list) {
struct ast_node *node;
FOR_EACH_PTR(list, node) { linearize_statement(proc, node); }
END_FOR_EACH_PTR(node);
}
static struct instruction *alloc_instruction(struct proc *proc, enum opcode op) {
struct instruction *insn = dmrC_allocator_allocate(&proc->linearizer->instruction_allocator, 0);
insn->opcode = op;
return insn;
}
static struct pseudo *linearize_literal(struct proc *proc, struct ast_node *expr) {
assert(expr->type == AST_LITERAL_EXPR);
ravitype_t type = expr->literal_expr.type.type_code;
struct pseudo *pseudo = NULL;
switch (type) {
case RAVI_TNUMFLT:
case RAVI_TNUMINT:
case RAVI_TSTRING:
pseudo = allocate_constant_pseudo(proc, allocate_constant(proc, expr));
break;
case RAVI_TNIL:
pseudo = allocate_nil_pseudo(proc);
break;
case RAVI_TBOOLEAN:
pseudo = allocate_boolean_pseudo(proc, expr->literal_expr.u.i);
break;
default:
abort();
}
return pseudo;
}
static struct pseudo *linearize_unaryop(struct proc *proc, struct ast_node *node) {
UnOpr op = node->unary_expr.unary_op;
struct pseudo *subexpr = linearize_expression(proc, node->unary_expr.expr);
ravitype_t subexpr_type = node->unary_expr.expr->common_expr.type.type_code;
enum opcode targetop = op_nop;
switch (op) {
case OPR_MINUS:
if (subexpr_type == RAVI_TNUMINT)
targetop = op_unmi;
else if (subexpr_type == RAVI_TNUMFLT)
targetop = op_unmf;
else
targetop = op_unm;
break;
case OPR_LEN:
if (subexpr_type == RAVI_TARRAYINT || subexpr_type == RAVI_TARRAYFLT)
targetop = op_leni;
else
targetop = op_len;
break;
case OPR_TO_INTEGER:
targetop = subexpr_type != RAVI_TNUMINT ? op_toint : op_nop;
break;
case OPR_TO_NUMBER:
targetop = subexpr_type != RAVI_TNUMFLT ? op_toflt : op_nop;
break;
case OPR_TO_CLOSURE:
targetop = subexpr_type != RAVI_TFUNCTION ? op_toclosure : op_nop;
break;
case OPR_TO_STRING:
targetop = subexpr_type != RAVI_TSTRING ? op_tostring : op_nop;
break;
case OPR_TO_INTARRAY:
targetop = subexpr_type != RAVI_TARRAYINT ? op_toiarray : op_nop;
break;
case OPR_TO_NUMARRAY:
targetop = subexpr_type != RAVI_TARRAYFLT ? op_tofarray : op_nop;
break;
case OPR_TO_TABLE:
targetop = subexpr_type != RAVI_TTABLE ? op_totable : op_nop;
break;
case OPR_TO_TYPE:
targetop = op_totype;
break;
case OPR_NOT:
targetop = op_not;
break;
case OPR_BNOT:
targetop = op_bnot;
break;
default:
abort();
break;
}
if (targetop == op_nop) {
return subexpr;
}
struct instruction *insn = alloc_instruction(proc, targetop);
struct pseudo *target = subexpr;
if (op == OPR_TO_TYPE) {
const struct constant *tname_constant = allocate_string_constant(proc, node->unary_expr.type.type_name);
struct pseudo *tname_pseudo = allocate_constant_pseudo(proc, tname_constant);
ptrlist_add((struct ptr_list **)&insn->operands, tname_pseudo, &proc->linearizer->ptrlist_allocator);
}
else if (op == OPR_NOT || op == OPR_BNOT) {
ptrlist_add((struct ptr_list **)&insn->operands, target, &proc->linearizer->ptrlist_allocator);
target = allocate_temp_pseudo(proc, RAVI_TANY);
}
else if (op == OPR_MINUS || op == OPR_LEN) {
ptrlist_add((struct ptr_list **)&insn->operands, target, &proc->linearizer->ptrlist_allocator);
target = allocate_temp_pseudo(proc, subexpr_type);
}
ptrlist_add((struct ptr_list **)&insn->targets, target, &proc->linearizer->ptrlist_allocator);
ptrlist_add((struct ptr_list **)&proc->current_bb->insns, insn, &proc->linearizer->ptrlist_allocator);
return target;
}
/* Type checker - WIP */
static struct pseudo *linearize_binaryop(struct proc *proc, struct ast_node *node) {
BinOpr op = node->binary_expr.binary_op;
struct ast_node *e1 = node->binary_expr.expr_left;
struct ast_node *e2 = node->binary_expr.expr_right;
struct pseudo *operand1 = linearize_expression(proc, e1);
struct pseudo *operand2 = linearize_expression(proc, e2);
enum opcode targetop;
switch (op) {
case OPR_ADD:
targetop = op_add;
break;
case OPR_SUB:
targetop = op_sub;
break;
case OPR_MUL:
targetop = op_mul;
break;
case OPR_DIV:
targetop = op_div;
break;
case OPR_IDIV:
targetop = op_idiv;
break;
case OPR_BAND:
targetop = op_band;
break;
case OPR_BOR:
targetop = op_bor;
break;
case OPR_BXOR:
targetop = op_bxor;
break;
case OPR_SHL:
targetop = op_shl;
break;
case OPR_SHR:
targetop = op_shr;
break;
case OPR_EQ:
case OPR_NE:
targetop = op_eq;
break;
case OPR_LT:
case OPR_GT:
targetop = op_lt;
break;
case OPR_LE:
case OPR_GE:
targetop = op_le;
break;
case OPR_MOD:
targetop = op_mod;
break;
case OPR_POW:
targetop = op_pow;
break;
default:
abort();
}
ravitype_t t1 = e1->common_expr.type.type_code;
ravitype_t t2 = e2->common_expr.type.type_code;
bool swap = false;
switch (targetop) {
case op_add:
case op_mul:
swap = t1 == RAVI_TNUMINT && t2 == RAVI_TNUMFLT;
break;
case op_eq:
case op_lt:
case op_le:
swap = op == OPR_NE || op == OPR_GT || op == OPR_GE;
break;
}
if (swap) {
struct pseudo *temp;
struct ast_node *ntemp;
temp = operand1;
operand1 = operand2;
operand2 = temp;
ntemp = e1;
e1 = e2;
e2 = ntemp;
t1 = e1->common_expr.type.type_code;
t2 = e2->common_expr.type.type_code;
}
switch (targetop) {
case op_add:
case op_mul:
if (t1 == RAVI_TNUMFLT && t2 == RAVI_TNUMFLT)
targetop += 1;
else if (t1 == RAVI_TNUMFLT && t2 == RAVI_TNUMINT)
targetop += 2;
else if (t1 == RAVI_TNUMINT && t2 == RAVI_TNUMINT)
targetop += 3;
break;
case op_div:
case op_sub:
if (t1 == RAVI_TNUMFLT && t2 == RAVI_TNUMFLT)
targetop += 1;
else if (t1 == RAVI_TNUMFLT && t2 == RAVI_TNUMINT)
targetop += 2;
else if (t1 == RAVI_TNUMINT && t2 == RAVI_TNUMFLT)
targetop += 3;
else if (t1 == RAVI_TNUMINT && t2 == RAVI_TNUMINT)
targetop += 4;
break;
case op_band:
case op_bor:
case op_bxor:
case op_shl:
case op_shr:
if (t1 == RAVI_TNUMINT && t2 == RAVI_TNUMINT)
targetop += 1;
break;
case op_eq:
case op_le:
case op_lt:
if (t1 == RAVI_TNUMINT && t2 == RAVI_TNUMINT)
targetop += 1;
else if (t1 == RAVI_TNUMFLT && t2 == RAVI_TNUMFLT)
targetop += 2;
break;
}
ravitype_t target_type = node->binary_expr.type.type_code;
struct pseudo *target = allocate_temp_pseudo(proc, target_type);
struct instruction *insn = alloc_instruction(proc, targetop);
ptrlist_add((struct ptr_list **)&insn->operands, operand1, &proc->linearizer->ptrlist_allocator);
ptrlist_add((struct ptr_list **)&insn->operands, operand2, &proc->linearizer->ptrlist_allocator);
ptrlist_add((struct ptr_list **)&insn->targets, target, &proc->linearizer->ptrlist_allocator);
ptrlist_add((struct ptr_list **)&proc->current_bb->insns, insn, &proc->linearizer->ptrlist_allocator);
free_temp_pseudo(proc, operand1);
free_temp_pseudo(proc, operand2);
return target;
}
/* generates closure instruction - linearize a proc, and then add instruction to create closure from it */
static struct pseudo *linearize_function_expr(struct proc *proc, struct ast_node *expr) {
struct proc *curproc = proc->linearizer->current_proc;
struct proc *newproc = allocate_proc(proc->linearizer, expr);
set_current_proc(proc->linearizer, newproc);
// printf("linearizing function\n");
set_current_proc(proc->linearizer, curproc); // restore the proc
ravitype_t target_type = expr->function_expr.type.type_code;
struct pseudo *target = allocate_temp_pseudo(proc, target_type);
struct pseudo *operand = allocate_closure_pseudo(proc->linearizer, newproc);
struct instruction *insn = alloc_instruction(proc, op_closure);
ptrlist_add((struct ptr_list **)&insn->operands, operand, &proc->linearizer->ptrlist_allocator);
ptrlist_add((struct ptr_list **)&insn->targets, target, &proc->linearizer->ptrlist_allocator);
ptrlist_add((struct ptr_list **)&proc->current_bb->insns, insn, &proc->linearizer->ptrlist_allocator);
return target;
}
static struct pseudo *linearize_symbol_expression(struct proc *proc, struct ast_node *expr) {
struct lua_symbol *sym = expr->symbol_expr.var;
if (sym->symbol_type == SYM_GLOBAL) {
struct pseudo *target = allocate_temp_pseudo(proc, RAVI_TANY);
struct pseudo *operand = allocate_symbol_pseudo(proc, sym, 0); // no register actually
struct instruction *insn = alloc_instruction(proc, op_loadglobal);
ptrlist_add((struct ptr_list **)&insn->operands, operand, &proc->linearizer->ptrlist_allocator);
ptrlist_add((struct ptr_list **)&insn->targets, target, &proc->linearizer->ptrlist_allocator);
ptrlist_add((struct ptr_list **)&proc->current_bb->insns, insn, &proc->linearizer->ptrlist_allocator);
return target;
}
else if (sym->symbol_type == SYM_LOCAL) {
return sym->var.pseudo;
}
else {
abort();
return NULL;
}
}
static struct pseudo *linearize_index_expression(struct proc *proc, struct ast_node *expr) {
return linearize_expression(proc, expr->indexed_assign_expr.index_expr);
}
/*
* Suffixed expression examples:
* f()[1]
* x[1][2]
* x.y[1]
*/
static struct pseudo *linearize_suffixedexpr(struct proc *proc, struct ast_node *node) {
/* suffixedexp -> primaryexp { '.' NAME | '[' exp ']' | ':' NAME funcargs | funcargs } */
struct pseudo *prev_psedo = linearize_expression(proc, node->suffixed_expr.primary_expr);
struct ast_node *prev_node = node->suffixed_expr.primary_expr;
struct ast_node *this_node;
FOR_EACH_PTR(node->suffixed_expr.suffix_list, this_node) {
if (this_node->type == AST_Y_INDEX_EXPR) {
}
else if (this_node->type == AST_FIELD_SELECTOR_EXPR) {
}
else if (this_node->type == AST_FUNCTION_CALL_EXPR) {
}
prev_node = this_node;
}
END_FOR_EACH_PTR(node);
// copy_type(node->suffixed_expr.type, prev_node->common_expr.type);
return prev_psedo;
}
static int linearize_indexed_assign(struct proc *proc, struct pseudo *table, ravitype_t table_type,
struct ast_node *expr, int next) {
struct pseudo *index_pseudo;
if (expr->indexed_assign_expr.index_expr) {
index_pseudo = linearize_expression(proc, expr->indexed_assign_expr.index_expr);
// TODO check valid index
}
else {
const struct constant *constant = allocate_constant_for_i(proc, next++);
index_pseudo = allocate_constant_pseudo(proc, constant);
}
struct pseudo *value_pseudo = linearize_expression(proc, expr->indexed_assign_expr.value_expr);
// TODO validate the type of assignment
// Insert type assertions if needed
enum opcode op;
switch (table_type) {
case RAVI_TARRAYINT:
op = op_iaput;
if (expr->indexed_assign_expr.value_expr->common_expr.type.type_code == RAVI_TNUMINT) {
op = op_iaput_ival;
}
break;
case RAVI_TARRAYFLT:
op = op_faput;
if (expr->indexed_assign_expr.value_expr->common_expr.type.type_code == RAVI_TNUMFLT) {
op = op_faput_fval;
}
break;
default:
op = table_type == RAVI_TTABLE ? op_tput : op_put;
if (expr->indexed_assign_expr.index_expr &&
expr->indexed_assign_expr.index_expr->index_expr.expr->common_expr.type.type_code == RAVI_TNUMINT ||
!expr->indexed_assign_expr.index_expr) {
op += 1;
}
else if (expr->indexed_assign_expr.index_expr &&
expr->indexed_assign_expr.index_expr->index_expr.expr->common_expr.type.type_code == RAVI_TSTRING) {
op += 2;
}
break;
}
struct instruction *insn = alloc_instruction(proc, op);
ptrlist_add((struct ptr_list **)&insn->operands, index_pseudo, &proc->linearizer->ptrlist_allocator);
ptrlist_add((struct ptr_list **)&insn->operands, value_pseudo, &proc->linearizer->ptrlist_allocator);
ptrlist_add((struct ptr_list **)&insn->targets, table, &proc->linearizer->ptrlist_allocator);
ptrlist_add((struct ptr_list **)&proc->current_bb->insns, insn, &proc->linearizer->ptrlist_allocator);
free_temp_pseudo(proc, index_pseudo);
free_temp_pseudo(proc, value_pseudo);
return next;
}
static struct pseudo *linearize_table_constructor(struct proc *proc, struct ast_node *expr) {
/* constructor -> '{' [ field { sep field } [sep] ] '}' where sep -> ',' | ';' */
struct pseudo *target = allocate_temp_pseudo(proc, expr->table_expr.type.type_code);
enum opcode op = op_newtable;
if (expr->table_expr.type.type_code == RAVI_TARRAYINT)
op = op_newiarray;
else if (expr->table_expr.type.type_code == RAVI_TARRAYFLT)
op = op_newfarray;
struct instruction *insn = alloc_instruction(proc, op);
ptrlist_add((struct ptr_list **)&insn->targets, target, &proc->linearizer->ptrlist_allocator);
ptrlist_add((struct ptr_list **)&proc->current_bb->insns, insn, &proc->linearizer->ptrlist_allocator);
/*TODO process constructor elements */
struct ast_node *ia;
int i = 1;
FOR_EACH_PTR(expr->table_expr.expr_list, ia) {
i = linearize_indexed_assign(proc, target, expr->table_expr.type.type_code, ia, i);
}
END_FOR_EACH_PTR(ia);
return target;
}
static struct pseudo *linearize_expression(struct proc *proc, struct ast_node *expr) {
switch (expr->type) {
case AST_LITERAL_EXPR: {
return linearize_literal(proc, expr);
} break;
case AST_BINARY_EXPR: {
return linearize_binaryop(proc, expr);
} break;
case AST_FUNCTION_EXPR: {
return linearize_function_expr(proc, expr);
} break;
case AST_UNARY_EXPR: {
return linearize_unaryop(proc, expr);
} break;
case AST_SUFFIXED_EXPR: {
return linearize_suffixedexpr(proc, expr);
} break;
case AST_SYMBOL_EXPR: {
return linearize_symbol_expression(proc, expr);
} break;
case AST_TABLE_EXPR: {
return linearize_table_constructor(proc, expr);
} break;
case AST_Y_INDEX_EXPR:
case AST_FIELD_SELECTOR_EXPR: {
return linearize_index_expression(proc, expr);
} break;
default:
abort();
break;
}
assert(false);
return NULL;
}
static void linearize_expr_list(struct proc *proc, struct ast_node_list *expr_list, struct instruction *insn,
struct pseudo_list **pseudo_list) {
struct ast_node *expr;
FOR_EACH_PTR(expr_list, expr) {
struct pseudo *pseudo = linearize_expression(proc, expr);
ptrlist_add((struct ptr_list **)pseudo_list, pseudo, &proc->linearizer->ptrlist_allocator);
}
END_FOR_EACH_PTR(expr);
}
static void linearize_return(struct proc *proc, struct ast_node *node) {
assert(node->type == AST_RETURN_STMT);
struct instruction *insn = alloc_instruction(proc, op_ret);
linearize_expr_list(proc, node->return_stmt.expr_list, insn, &insn->operands);
ptrlist_add((struct ptr_list **)&proc->current_bb->insns, insn, &proc->linearizer->ptrlist_allocator);
// FIXME add edge to exit block
// FIXME terminate block
// FIXME free all temps
}
static void linearize_statement(struct proc *proc, struct ast_node *node) {
switch (node->type) {
case AST_FUNCTION_EXPR: {
/* args need type assertions but those have no ast - i.e. code gen should do it */
// typecheck_ast_list(container, function, node->function_expr.function_statement_list);
break;
}
case AST_NONE: {
break;
}
case AST_RETURN_STMT: {
linearize_return(proc, node);
// typecheck_ast_list(container, function, node->return_stmt.expr_list);
break;
}
case AST_LOCAL_STMT: {
// typecheck_local_statement(container, function, node);
break;
}
case AST_FUNCTION_STMT: {
// typecheck_ast_node(container, function, node->function_stmt.function_expr);
break;
}
case AST_LABEL_STMT: {
break;
}
case AST_GOTO_STMT: {
break;
}
case AST_DO_STMT: {
break;
}
case AST_EXPR_STMT: {
// typecheck_expr_statement(container, function, node);
break;
}
case AST_IF_STMT: {
// typecheck_if_statement(container, function, node);
break;
}
case AST_WHILE_STMT:
case AST_REPEAT_STMT: {
// typecheck_while_or_repeat_statement(container, function, node);
break;
}
case AST_FORIN_STMT: {
// typecheck_for_in_statment(container, function, node);
break;
}
case AST_FORNUM_STMT: {
// typecheck_for_num_statment(container, function, node);
break;
}
case AST_SUFFIXED_EXPR: {
// typecheck_suffixedexpr(container, function, node);
break;
}
case AST_FUNCTION_CALL_EXPR: {
// if (node->function_call_expr.method_name) {
//}
// else {
//}
// typecheck_ast_list(container, function, node->function_call_expr.arg_list);
break;
}
case AST_SYMBOL_EXPR: {
/* symbol type should have been set when symbol was created */
// copy_type(node->symbol_expr.type, node->symbol_expr.var->value_type);
break;
}
case AST_BINARY_EXPR: {
// typecheck_binaryop(container, function, node);
break;
}
case AST_UNARY_EXPR: {
// typecheck_unaryop(container, function, node);
break;
}
case AST_LITERAL_EXPR: {
/* type set during parsing */
break;
}
case AST_FIELD_SELECTOR_EXPR: {
// typecheck_ast_node(container, function, node->index_expr.expr);
break;
}
case AST_Y_INDEX_EXPR: {
// typecheck_ast_node(container, function, node->index_expr.expr);
break;
}
case AST_INDEXED_ASSIGN_EXPR: {
// if (node->indexed_assign_expr.index_expr) {
// typecheck_ast_node(container, function, node->indexed_assign_expr.index_expr);
//}
// typecheck_ast_node(container, function, node->indexed_assign_expr.value_expr);
// copy_type(node->indexed_assign_expr.type, node->indexed_assign_expr.value_expr->common_expr.type);
break;
}
case AST_TABLE_EXPR: {
// typecheck_ast_list(container, function, node->table_expr.expr_list);
break;
}
default:
assert(0);
}
}
/**
* Creates and initializes a basic block to be an empty block. Returns the new basic block.
*/
static struct basic_block *create_block(struct proc *proc) {
if (proc->node_count >= proc->allocated) {
unsigned new_size = proc->allocated + 25;
struct node **new_data =
dmrC_allocator_allocate(&proc->linearizer->unsized_allocator, new_size * sizeof(struct node *));
assert(new_data != NULL);
if (proc->node_count > 0) {
memcpy(new_data, proc->nodes, proc->allocated * sizeof(struct node *));
}
proc->allocated = new_size;
proc->nodes = new_data;
}
assert(proc->node_count < proc->allocated);
struct basic_block *new_block = dmrC_allocator_allocate(&proc->linearizer->basic_block_allocator, 0);
new_block->index = proc->node_count;
proc->nodes[proc->node_count++] = bb2n(new_block);
return new_block;
}
/**
* Takes a basic block as an argument and makes it the current block.
* All future instructions will be added to the end of this block
*/
static void start_block(struct proc *proc, struct basic_block *bb) { proc->current_bb = bb; }
/**
* Create the initial blocks entry and exit for the proc.
* sets current block to entry block.
*/
static void initialize_graph(struct proc *proc) {
assert(proc != NULL);
proc->entry = bb2n(create_block(proc));
proc->exit = bb2n(create_block(proc));
start_block(proc, n2bb(proc->entry));
}
/**
* Makes given scope the current scope, and allocates registers for locals.
*/
static void start_scope(struct linearizer *linearizer, struct proc *proc, struct block_scope *scope) {
proc->current_scope = scope;
struct lua_symbol *sym;
FOR_EACH_PTR(scope->symbol_list, sym) {
if (sym->symbol_type == SYM_LOCAL) {
uint8_t reg = alloc_reg(&proc->local_pseudos);
allocate_symbol_pseudo(proc, sym, reg);
// printf("Assigning register %d to local %s\n", (int)reg, getstr(sym->var.var_name));
}
}
END_FOR_EACH_PTR(sym);
}
/**
* Deallocate local registers when the scope ends, in reverse order
* so that we have a stack discipline, and then changes current scope to be the
* parent scope.
*/
static void end_scope(struct linearizer *linearizer, struct proc *proc) {
struct block_scope *scope = proc->current_scope;
struct lua_symbol *sym;
FOR_EACH_PTR_REVERSE(scope->symbol_list, sym) {
if (sym->symbol_type == SYM_LOCAL) {
struct pseudo *pseudo = sym->var.pseudo;
assert(pseudo && pseudo->type == PSEUDO_SYMBOL && pseudo->symbol == sym);
// printf("Free register %d for local %s\n", (int)pseudo->regnum, getstr(sym->var.var_name));
free_reg(&proc->local_pseudos, pseudo->regnum);
}
}
END_FOR_EACH_PTR_REVERSE(sym);
proc->current_scope = scope->parent;
}
static void linearize_function(struct linearizer *linearizer) {
struct proc *proc = linearizer->current_proc;
assert(proc != NULL);
struct ast_node *func_expr = proc->function_expr;
assert(func_expr->type == AST_FUNCTION_EXPR);
initialize_graph(proc);
start_scope(linearizer, proc, func_expr->function_expr.main_block);
linearize_function_args(linearizer);
linearize_statement_list(proc, func_expr->function_expr.function_statement_list);
end_scope(linearizer, proc);
}
void output_pseudo(struct pseudo *pseudo, membuff_t *mb) {
switch (pseudo->type) {
case PSEUDO_CONSTANT: {
const struct constant *constant = pseudo->constant;
const char *tc = "";
if (constant->type == RAVI_TNUMFLT) {
membuff_add_fstring(mb, "%.12f", constant->n);
tc = "flt";
}
else if (constant->type == RAVI_TNUMINT) {
membuff_add_fstring(mb, "%ld", constant->i);
tc = "int";
}
else {
membuff_add_fstring(mb, "'%s'", getstr(constant->s));
tc = "s";
}
membuff_add_fstring(mb, " K%s(%d)", tc, pseudo->regnum);
} break;
case PSEUDO_TEMP_INT:
membuff_add_fstring(mb, "Tint(%d)", pseudo->regnum);
break;
case PSEUDO_TEMP_FLT:
membuff_add_fstring(mb, "Tflt(%d)", pseudo->regnum);
break;
case PSEUDO_TEMP_ANY:
membuff_add_fstring(mb, "T(%d)", pseudo->regnum);
break;
case PSEUDO_PROC:
membuff_add_fstring(mb, "Proc(%p)", pseudo->proc);
break;
case PSEUDO_NIL:
membuff_add_string(mb, "nil");
break;
case PSEUDO_FALSE:
membuff_add_string(mb, "false");
break;
case PSEUDO_TRUE:
membuff_add_string(mb, "true");
break;
case PSEUDO_SYMBOL: {
switch (pseudo->symbol->symbol_type) {
case SYM_UPVALUE:
case SYM_LOCAL:
case SYM_GLOBAL: {
membuff_add_string(mb, getstr(pseudo->symbol->var.var_name));
break;
}
default:
assert(0);
}
} break;
}
}
static const char *op_codenames[] = {
"NOOP", "RET", "LOADK", "LOADNIL", "LOADBOOL", "ADD", "ADDff", "ADDfi", "ADDii", "SUB",
"SUBff", "SUBfi", "SUBif", "SUBii", "MUL", "MULff", "MULfi", "MULii", "DIV", "DIVff",
"DIVfi", "DIVif", "DIVii", "IDIV", "BAND", "BANDii", "BOR", "BORii", "BXOR", "BXORii",
"SHL", "SHLii", "SHR", "SHRii", "EQ", "EQii", "EQff", "LT", "LIii", "LTff",
"LE", "LEii", "LEff", "MOD", "POW", "CLOSURE", "UNM", "UNMi", "UNMf", "LEN",
"LENi", "TOINT", "TOFLT", "TOCLOSURE", "TOSTRING", "TOIARRAY", "TOFARRAY", "TOTABLE", "TOTYPE", "NOT",
"BNOT", "LOADGLOBAL", "NEWTABLE", "NEWIARRAY", "NEWFARRAY", "PUT", "PUTik", "PUTsk", "TPUT", "TPUTik",
"TPUTsk", "IAPUT", "IAPUTiv", "FAPUT", "FAPUTfv"};
void output_pseudo_list(struct pseudo_list *list, membuff_t *mb) {
struct pseudo *pseudo;
membuff_add_string(mb, " {");
int i = 0;
FOR_EACH_PTR(list, pseudo) {
if (i > 0)
membuff_add_string(mb, ", ");
output_pseudo(pseudo, mb);
i++;
}
END_FOR_EACH_PTR(pseudo);
membuff_add_string(mb, "}");
}
void output_instruction(struct instruction *insn, membuff_t *mb) {
membuff_add_fstring(mb, "\t%s", op_codenames[insn->opcode]);
if (insn->operands) {
output_pseudo_list(insn->operands, mb);
}
if (insn->targets) {
output_pseudo_list(insn->targets, mb);
}
membuff_add_string(mb, "\n");
}
void output_instructions(struct instruction_list *list, membuff_t *mb) {
struct instruction *insn;
FOR_EACH_PTR(list, insn) { output_instruction(insn, mb); }
END_FOR_EACH_PTR(insn);
}
void output_basic_block(struct basic_block *bb, membuff_t *mb) {
membuff_add_fstring(mb, "L%d\n", bb->index);
output_instructions(bb->insns, mb);
}
void output_proc(struct proc *proc, membuff_t *mb) {
struct basic_block *bb;
for (int i = 0; i < (int)proc->node_count; i++) {
bb = n2bb(proc->nodes[i]);
output_basic_block(bb, mb);
}
}
void raviA_ast_linearize(struct linearizer *linearizer) {
struct proc *proc = allocate_proc(linearizer, linearizer->ast_container->main_function);
set_main_proc(linearizer, proc);
set_current_proc(linearizer, proc);
linearize_function(linearizer);
}
void raviA_show_linearizer(struct linearizer *linearizer, membuff_t *mb) { output_proc(linearizer->main_proc, mb); }