2024-10-06 05:29:02 +00:00
|
|
|
/*
|
|
|
|
target_rua.c
|
|
|
|
|
|
|
|
Ruamoko progs backend.
|
|
|
|
|
|
|
|
Copyright (C) 2024 Bill Currie <bill@taniwha.org>
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
|
|
modify it under the terms of the GNU General Public License
|
|
|
|
as published by the Free Software Foundation; either version 2
|
|
|
|
of the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
|
|
|
|
See the GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program; if not, write to:
|
|
|
|
|
|
|
|
Free Software Foundation, Inc.
|
|
|
|
59 Temple Place - Suite 330
|
|
|
|
Boston, MA 02111-1307, USA
|
|
|
|
|
|
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
# include "config.h"
|
|
|
|
#endif
|
|
|
|
|
2024-10-07 00:42:34 +00:00
|
|
|
#include "QF/va.h"
|
2024-10-06 05:29:02 +00:00
|
|
|
#include "QF/progs/pr_comp.h"
|
|
|
|
|
2024-10-26 14:30:37 +00:00
|
|
|
#include "tools/qfcc/include/codespace.h"
|
|
|
|
#include "tools/qfcc/include/debug.h"
|
2024-10-07 00:42:34 +00:00
|
|
|
#include "tools/qfcc/include/defspace.h"
|
|
|
|
#include "tools/qfcc/include/diagnostic.h"
|
2024-10-26 14:30:37 +00:00
|
|
|
#include "tools/qfcc/include/emit.h"
|
|
|
|
#include "tools/qfcc/include/flow.h"
|
2024-10-06 05:29:02 +00:00
|
|
|
#include "tools/qfcc/include/function.h"
|
2024-10-07 00:42:34 +00:00
|
|
|
#include "tools/qfcc/include/options.h"
|
2024-10-26 14:30:37 +00:00
|
|
|
#include "tools/qfcc/include/qfcc.h"
|
|
|
|
#include "tools/qfcc/include/statements.h"
|
2024-10-07 00:42:34 +00:00
|
|
|
#include "tools/qfcc/include/strpool.h"
|
2024-12-06 17:38:00 +00:00
|
|
|
#include "tools/qfcc/include/switch.h"
|
2024-10-07 00:42:34 +00:00
|
|
|
#include "tools/qfcc/include/symtab.h"
|
2024-10-06 05:29:02 +00:00
|
|
|
#include "tools/qfcc/include/target.h"
|
|
|
|
#include "tools/qfcc/include/type.h"
|
|
|
|
|
2024-10-27 11:58:23 +00:00
|
|
|
// standardized base register to use for all locals (arguments, local defs,
|
|
|
|
// params)
|
|
|
|
#define LOCALS_REG 1
|
|
|
|
// keep the stack aligned to 8 words (32 bytes) so lvec etc can be used without
|
|
|
|
// having to do shenanigans with mixed-alignment stack frames
|
|
|
|
#define STACK_ALIGN 8
|
|
|
|
|
2024-10-06 05:29:02 +00:00
|
|
|
static bool
|
|
|
|
ruamoko_value_too_large (const type_t *val_type)
|
|
|
|
{
|
|
|
|
return type_size (val_type) > MAX_DEF_SIZE;
|
|
|
|
}
|
|
|
|
|
2024-10-07 00:42:34 +00:00
|
|
|
static void
|
|
|
|
create_param (symtab_t *parameters, symbol_t *param)
|
|
|
|
{
|
|
|
|
defspace_t *space = parameters->space;
|
|
|
|
def_t *def = new_def (param->name, 0, space, sc_param);
|
|
|
|
int size = type_size (param->type);
|
|
|
|
int alignment = param->type->alignment;
|
|
|
|
if (alignment < 4) {
|
|
|
|
alignment = 4;
|
|
|
|
}
|
|
|
|
def->offset = defspace_alloc_aligned_highwater (space, size, alignment);
|
|
|
|
def->type = param->type;
|
|
|
|
param->def = def;
|
|
|
|
param->sy_type = sy_def;
|
|
|
|
param->lvalue = !def->readonly;
|
|
|
|
symtab_addsymbol (parameters, param);
|
|
|
|
if (is_vector(param->type) && options.code.vector_components)
|
|
|
|
init_vector_components (param, 0, parameters);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
ruamoko_build_scope (symbol_t *fsym)
|
|
|
|
{
|
|
|
|
function_t *func = fsym->metafunc->func;
|
|
|
|
|
|
|
|
for (param_t *p = fsym->params; p; p = p->next) {
|
|
|
|
symbol_t *param;
|
|
|
|
if (!p->selector && !p->type && !p->name) {
|
|
|
|
// ellipsis marker
|
|
|
|
param = new_symbol_type (".args", &type_va_list);
|
|
|
|
} else {
|
|
|
|
if (!p->type) {
|
|
|
|
continue; // non-param selector
|
|
|
|
}
|
|
|
|
if (is_void (p->type)) {
|
|
|
|
if (p->name) {
|
|
|
|
error (0, "invalid parameter type for %s", p->name);
|
|
|
|
} else if (p != fsym->params || p->next) {
|
|
|
|
error (0, "void must be the only parameter");
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!p->name) {
|
|
|
|
error (0, "parameter name omitted");
|
|
|
|
p->name = save_string ("");
|
|
|
|
}
|
|
|
|
param = new_symbol_type (p->name, p->type);
|
|
|
|
}
|
|
|
|
create_param (func->parameters, param);
|
|
|
|
if (p->qual == pq_out) {
|
|
|
|
param->def->param = false;
|
|
|
|
param->def->out_param = true;
|
|
|
|
} else if (p->qual == pq_inout) {
|
|
|
|
param->def->out_param = true;
|
|
|
|
} else if (p->qual == pq_const) {
|
|
|
|
param->def->readonly = true;
|
|
|
|
}
|
|
|
|
param->def->reg = func->temp_reg;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-26 14:30:37 +00:00
|
|
|
static void
|
2024-10-27 11:58:23 +00:00
|
|
|
ruamoko_build_code (function_t *func, const expr_t *statements)
|
2024-10-26 14:30:37 +00:00
|
|
|
{
|
2024-10-27 11:58:23 +00:00
|
|
|
/* Create a function entry block to set up the stack frame and add the
|
|
|
|
* actual function code to that block. This ensure that the adjstk and
|
|
|
|
* with statements always come first, regardless of what ideas the
|
|
|
|
* optimizer gets.
|
|
|
|
*/
|
|
|
|
expr_t *e;
|
|
|
|
expr_t *entry = new_block_expr (0);
|
|
|
|
entry->loc = func->def->loc;
|
|
|
|
|
|
|
|
e = new_adjstk_expr (0, 0);
|
|
|
|
e->loc = entry->loc;
|
|
|
|
append_expr (entry, e);
|
|
|
|
|
|
|
|
e = new_with_expr (2, LOCALS_REG, new_short_expr (0));
|
|
|
|
e->loc = entry->loc;
|
|
|
|
append_expr (entry, e);
|
|
|
|
|
|
|
|
append_expr (entry, statements);
|
|
|
|
statements = entry;
|
|
|
|
|
|
|
|
/* Mark all local defs as using the base register used for stack
|
|
|
|
* references.
|
|
|
|
*/
|
|
|
|
func->temp_reg = LOCALS_REG;
|
|
|
|
for (def_t *def = func->locals->space->defs; def; def = def->next) {
|
|
|
|
if (def->local || def->param) {
|
|
|
|
def->reg = LOCALS_REG;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (def_t *def = func->parameters->space->defs; def; def = def->next) {
|
|
|
|
if (def->local || def->param) {
|
|
|
|
def->reg = LOCALS_REG;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func->code = pr.code->size;
|
|
|
|
lineno_base = func->def->loc.line;
|
|
|
|
func->sblock = make_statements (statements);
|
2024-10-26 14:30:37 +00:00
|
|
|
if (options.code.optimize) {
|
2024-10-27 11:58:23 +00:00
|
|
|
flow_data_flow (func);
|
2024-10-26 14:30:37 +00:00
|
|
|
} else {
|
2024-10-27 11:58:23 +00:00
|
|
|
statements_count_temps (func->sblock);
|
2024-10-26 14:30:37 +00:00
|
|
|
}
|
2024-10-27 11:58:23 +00:00
|
|
|
emit_statements (func->sblock);
|
|
|
|
|
|
|
|
defspace_sort_defs (func->parameters->space);
|
|
|
|
defspace_sort_defs (func->locals->space);
|
|
|
|
|
|
|
|
defspace_t *space = defspace_new (ds_virtual);
|
|
|
|
|
|
|
|
if (func->arguments) {
|
|
|
|
func->arguments->size = func->arguments->max_size;
|
|
|
|
merge_spaces (space, func->arguments, STACK_ALIGN);
|
|
|
|
func->arguments = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
merge_spaces (space, func->locals->space, STACK_ALIGN);
|
|
|
|
func->locals->space = space;
|
|
|
|
|
|
|
|
// allocate 0 words to force alignment and get the address
|
|
|
|
func->params_start = defspace_alloc_aligned_highwater (space, 0,
|
|
|
|
STACK_ALIGN);
|
|
|
|
|
|
|
|
dstatement_t *st = &pr.code->code[func->code];
|
|
|
|
if (pr.code->size > func->code && st->op == OP_ADJSTK) {
|
|
|
|
if (func->params_start) {
|
|
|
|
st->b = -func->params_start;
|
|
|
|
} else {
|
|
|
|
// skip over adjstk so a zero adjustment doesn't get executed
|
|
|
|
func->code += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
merge_spaces (space, func->parameters->space, STACK_ALIGN);
|
|
|
|
func->parameters->space = space;
|
|
|
|
|
|
|
|
// force the alignment again so the full stack slot is counted when
|
|
|
|
// the final parameter is smaller than STACK_ALIGN words
|
|
|
|
defspace_alloc_aligned_highwater (space, 0, STACK_ALIGN);
|
2024-10-26 14:30:37 +00:00
|
|
|
}
|
|
|
|
|
2024-11-18 05:55:32 +00:00
|
|
|
static int
|
|
|
|
copy_elements (expr_t *block, const expr_t *dst, const expr_t *src, int base)
|
|
|
|
{
|
|
|
|
int index = 0;
|
|
|
|
for (auto li = src->vector.list.head; li; li = li->next) {
|
|
|
|
auto e = li->expr;
|
|
|
|
if (e->type == ex_vector) {
|
|
|
|
index += copy_elements (block, dst, e, index + base);
|
|
|
|
} else {
|
|
|
|
auto type = get_type (e);
|
|
|
|
auto dst_ele = new_offset_alias_expr (type, dst, index + base);
|
|
|
|
append_expr (block, assign_expr (dst_ele, e));
|
|
|
|
index += type_width (type);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return index;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const expr_t *
|
|
|
|
ruamoko_assign_vector (const expr_t *dst, const expr_t *src)
|
|
|
|
{
|
|
|
|
expr_t *block = new_block_expr (0);
|
|
|
|
|
|
|
|
copy_elements (block, dst, src, 0);
|
|
|
|
block->block.result = dst;
|
|
|
|
return block;
|
|
|
|
}
|
|
|
|
|
2024-12-06 17:38:00 +00:00
|
|
|
static switch_block_t *switch_block;
|
|
|
|
const expr_t *
|
2024-12-07 14:52:50 +00:00
|
|
|
ruamoko_proc_switch (const expr_t *expr, rua_ctx_t *ctx)
|
2024-12-06 17:38:00 +00:00
|
|
|
{
|
|
|
|
scoped_src_loc (expr);
|
2024-12-07 14:52:50 +00:00
|
|
|
auto test = expr_process (expr->switchblock.test, ctx);
|
2024-12-06 17:38:00 +00:00
|
|
|
|
|
|
|
auto sb = switch_block;
|
|
|
|
switch_block = new_switch_block ();
|
|
|
|
switch_block->test = test;
|
2024-12-07 14:52:50 +00:00
|
|
|
auto body = expr_process (expr->switchblock.body, ctx);
|
2024-12-06 17:38:00 +00:00
|
|
|
|
|
|
|
auto break_label = expr->switchblock.break_label;
|
|
|
|
auto swtch = switch_expr (switch_block, break_label, body);
|
|
|
|
switch_block = sb;
|
|
|
|
return swtch;
|
|
|
|
}
|
|
|
|
|
|
|
|
const expr_t *
|
2024-12-07 14:52:50 +00:00
|
|
|
ruamoko_proc_caselabel (const expr_t *expr, rua_ctx_t *ctx)
|
2024-12-06 17:38:00 +00:00
|
|
|
{
|
|
|
|
scoped_src_loc (expr);
|
|
|
|
if (expr->caselabel.end_value) {
|
|
|
|
internal_error (expr, "case ranges not implemented");
|
|
|
|
}
|
2024-12-07 14:52:50 +00:00
|
|
|
auto value = expr_process (expr->caselabel.value, ctx);
|
2024-12-06 17:38:00 +00:00
|
|
|
if (is_error (value)) {
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
return case_label_expr (switch_block, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
const expr_t *
|
|
|
|
ruamoko_field_array (const expr_t *e)
|
|
|
|
{
|
|
|
|
ex_list_t list = {};
|
|
|
|
// convert the left-branching field/array expression chain to a list
|
|
|
|
while (e->type == ex_field || e->type == ex_array) {
|
|
|
|
for (; e->type == ex_field; e = e->field.object) {
|
|
|
|
list_prepend (&list, e);
|
|
|
|
}
|
|
|
|
for (; e->type == ex_array; e = e->array.base) {
|
|
|
|
list_prepend (&list, e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// e is now the base object of the field/array expression chain
|
|
|
|
|
|
|
|
int num_obj = list_count (&list);
|
|
|
|
const expr_t *objects[num_obj];
|
|
|
|
list_scatter (&list, objects);
|
|
|
|
for (int i = 0; i < num_obj; i++) {
|
|
|
|
auto obj = objects[i];
|
|
|
|
auto base_type = get_type (e);
|
|
|
|
if (obj->type == ex_field) {
|
|
|
|
if (obj->field.member->type != ex_symbol) {
|
|
|
|
internal_error (obj->field.member, "not a symbol");
|
|
|
|
}
|
|
|
|
auto sym = obj->field.member->symbol;
|
|
|
|
if (is_pointer (base_type)) {
|
|
|
|
auto offset = new_short_expr (sym->offset);
|
|
|
|
e = offset_pointer_expr (e, offset);
|
|
|
|
e = cast_expr (pointer_type (obj->field.type), e);
|
|
|
|
e = unary_expr ('.', e);
|
|
|
|
} else if (e->type == ex_uexpr && e->expr.op == '.') {
|
|
|
|
auto offset = new_short_expr (sym->offset);
|
|
|
|
e = offset_pointer_expr (e->expr.e1, offset);
|
|
|
|
e = cast_expr (pointer_type (obj->field.type), e);
|
|
|
|
e = unary_expr ('.', e);
|
|
|
|
} else {
|
|
|
|
e = new_offset_alias_expr (obj->field.type, e, sym->offset);
|
|
|
|
}
|
|
|
|
} else if (obj->type == ex_array) {
|
|
|
|
scoped_src_loc (obj->array.index);
|
|
|
|
int base_ind = 0;
|
|
|
|
if (is_array (base_type)) {
|
|
|
|
base_ind = base_type->array.base;
|
|
|
|
}
|
|
|
|
auto ele_type = obj->array.type;
|
|
|
|
auto base = new_int_expr (base_ind, false);
|
|
|
|
auto scale = new_int_expr (type_size (ele_type), false);
|
|
|
|
auto offset = binary_expr ('*', base, scale);
|
|
|
|
auto index = binary_expr ('*', obj->array.index, scale);
|
|
|
|
offset = binary_expr ('-', index, offset);
|
|
|
|
const expr_t *ptr;
|
|
|
|
if (is_array (base_type)) {
|
|
|
|
if (e->type == ex_uexpr && e->expr.op == '.') {
|
|
|
|
ptr = e->expr.e1;
|
|
|
|
} else {
|
|
|
|
auto alias = new_offset_alias_expr (ele_type, e, 0);
|
|
|
|
ptr = new_address_expr (ele_type, alias, 0);
|
|
|
|
}
|
|
|
|
} else if (is_nonscalar (base_type) || is_matrix (base_type)) {
|
|
|
|
auto alias = new_offset_alias_expr (ele_type, e, 0);
|
|
|
|
ptr = new_address_expr (ele_type, alias, 0);
|
|
|
|
} else {
|
|
|
|
ptr = e;
|
|
|
|
}
|
|
|
|
ptr = offset_pointer_expr (ptr, offset);
|
|
|
|
ptr = cast_expr (pointer_type (obj->field.type), ptr);
|
|
|
|
e = unary_expr ('.', ptr);
|
|
|
|
} else {
|
|
|
|
internal_error (obj, "what the what?!?");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return e;
|
|
|
|
}
|
|
|
|
|
|
|
|
const expr_t *
|
2024-12-07 14:52:50 +00:00
|
|
|
ruamoko_proc_address (const expr_t *expr, rua_ctx_t *ctx)
|
2024-12-06 17:38:00 +00:00
|
|
|
{
|
|
|
|
scoped_src_loc (expr);
|
2024-12-07 14:52:50 +00:00
|
|
|
auto e = expr_process (expr->expr.e1, ctx);
|
2024-12-06 17:38:00 +00:00
|
|
|
if (is_error (e)) {
|
|
|
|
return e;
|
|
|
|
}
|
|
|
|
if (e->type == ex_field || e->type == ex_array) {
|
|
|
|
e = ruamoko_field_array (e);
|
|
|
|
}
|
|
|
|
return address_expr (e, nullptr);
|
|
|
|
}
|
|
|
|
|
2025-01-18 05:05:09 +00:00
|
|
|
static const expr_t *
|
|
|
|
ruamoko_vector_compare (int op, const expr_t *e1, const expr_t *e2)
|
|
|
|
{
|
|
|
|
// both e1 and e2 should have the same types here
|
|
|
|
auto type = get_type (e1);
|
|
|
|
if (op != QC_EQ && op != QC_NE) {
|
|
|
|
return error (e2, "invalid comparison for %s", type->name);
|
|
|
|
}
|
|
|
|
int hop = op == QC_EQ ? '&' : '|';
|
|
|
|
auto e = new_binary_expr (op, e1, e2);
|
|
|
|
e->expr.type = bool_type (type);
|
|
|
|
e = new_horizontal_expr (hop, e, &type_int);
|
|
|
|
return e;
|
|
|
|
}
|
|
|
|
|
2024-10-06 05:29:02 +00:00
|
|
|
target_t ruamoko_target = {
|
|
|
|
.value_too_large = ruamoko_value_too_large,
|
2024-10-07 00:42:34 +00:00
|
|
|
.build_scope = ruamoko_build_scope,
|
2024-10-27 11:58:23 +00:00
|
|
|
.build_code = ruamoko_build_code,
|
2024-11-03 09:03:31 +00:00
|
|
|
.declare_sym = declare_def,
|
2024-11-17 07:17:50 +00:00
|
|
|
.initialized_temp = initialized_temp_expr,
|
2024-11-18 05:55:32 +00:00
|
|
|
.assign_vector = ruamoko_assign_vector,
|
2024-12-06 17:38:00 +00:00
|
|
|
.proc_switch = ruamoko_proc_switch,
|
|
|
|
.proc_caselabel = ruamoko_proc_caselabel,
|
|
|
|
.proc_address = ruamoko_proc_address,
|
2025-01-18 05:05:09 +00:00
|
|
|
.vector_compare = ruamoko_vector_compare,
|
2024-10-06 05:29:02 +00:00
|
|
|
};
|