[qfcc] Build the Ruamoko function parameters

The parameter defs are allocated from the parameter space using a
minimum alignment of 4, and varargs functions get a va_list struct in
place of the ...

An "args" expression is unconditionally injected into the call arguments
list at the place where ... is in the list, with arguments passed
through ... coming after the ...

Arguments get through to functions now, but there's problems with taking
the address of local variables: currently done using constant pointer
defs, which can't work for the base register addressing used in Ruamoko
progs.

With the update to test-bi's printf (and a hack to qfcc for lea),
triangle.r actually works, printing the expected results (but -1 instead
of 1 for equality, though that too is actually expected). qfcc will take
a bit longer because it seems there are some design issues in address
expressions (ambiguity, and a few other things) that have pretty much
always been there.
This commit is contained in:
Bill Currie 2022-01-24 18:35:16 +09:00
parent 7e147e703c
commit 37f08f9d4f
9 changed files with 200 additions and 31 deletions

View file

@ -495,7 +495,7 @@ PR_SetupParams (progs_t *pr, int num_params, int min_alignment)
}
*pr->globals.stack = stack;
pr->pr_params[0] = pr->pr_globals + stack;
num_params = min (num_params, PR_MAX_PARAMS);
num_params = max (num_params, PR_MAX_PARAMS);
for (int i = 1; i < num_params; i++) {
pr->pr_params[i] = pr->pr_params[0] + i * 4;
}

View file

@ -488,6 +488,14 @@ expr_t *new_temp_def_expr (const struct type_s *type);
*/
expr_t *new_nil_expr (void);
/** Create a new args expression node
Marker between real parameters and those passed through ...
\return The new args expression node.
*/
expr_t *new_args_expr (void);
/** Create a new value expression node.
\param value The value to put in the expression node.
@ -725,6 +733,7 @@ void convert_name (expr_t *e);
expr_t *convert_vector (expr_t *e);
expr_t *append_expr (expr_t *block, expr_t *e);
expr_t *prepend_expr (expr_t *block, expr_t *e);
expr_t *reverse_expr_list (expr_t *e);
void print_expr (expr_t *e);

View file

@ -61,5 +61,6 @@ EX_EXPR(branch) ///< branch expression (::ex_branch_t)
EX_EXPR(return) ///< return expression (::ex_return_t)
EX_EXPR(adjstk) ///< stack adjust expression (::ex_adjstk_t)
EX_EXPR(with) ///< with expression (::ex_with_t)
EX_EXPR(args) ///< @args marker in parameter list. no data
///@}

View file

@ -654,6 +654,15 @@ print_with (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next)
e->e.with.mode, e->e.with.reg, e->line);
}
static void
print_args (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next)
{
int indent = level * 2 + 2;
dasprintf (dstr, "%*se_%p [label=\"...\\n%d\"];\n", indent, "", e,
e->line);
}
static void
_print_expr (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next)
{
@ -682,6 +691,7 @@ _print_expr (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next)
[ex_return] = print_return,
[ex_adjstk] = print_adjstk,
[ex_with] = print_with,
[ex_args] = print_args,
};
int indent = level * 2 + 2;

View file

@ -274,6 +274,8 @@ get_type (expr_t *e)
break;
case ex_assign:
return get_type (e->e.assign.dst);
case ex_args:
return &type_va_list;
case ex_count:
internal_error (e, "invalid expression");
}
@ -525,6 +527,10 @@ copy_expr (expr_t *e)
*n = *e;
n->e.with.with = copy_expr (e->e.with.with);
return n;
case ex_args:
n = new_expr ();
*n = *e;
return n;
case ex_count:
break;
}
@ -725,6 +731,14 @@ new_nil_expr (void)
return e;
}
expr_t *
new_args_expr (void)
{
expr_t *e = new_expr ();
e->type = ex_args;
return e;
}
expr_t *
new_value_expr (ex_value_t *value)
{
@ -1709,6 +1723,7 @@ has_function_call (expr_t *e)
case ex_memset:
case ex_adjstk:
case ex_with:
case ex_args:
return 0;
case ex_count:
break;
@ -1803,6 +1818,7 @@ unary_expr (int op, expr_t *e)
case ex_return:
case ex_adjstk:
case ex_with:
case ex_args:
internal_error (e, "unexpected expression type");
case ex_uexpr:
if (e->e.expr.op == '-') {
@ -1907,6 +1923,7 @@ unary_expr (int op, expr_t *e)
case ex_return:
case ex_adjstk:
case ex_with:
case ex_args:
internal_error (e, "unexpected expression type");
case ex_bool:
return new_bool_expr (e->e.bool.false_list,
@ -1989,6 +2006,7 @@ unary_expr (int op, expr_t *e)
case ex_return:
case ex_adjstk:
case ex_with:
case ex_args:
internal_error (e, "unexpected expression type");
case ex_uexpr:
if (e->e.expr.op == '~')
@ -2048,12 +2066,13 @@ build_function_call (expr_t *fexpr, const type_t *ftype, expr_t *params)
{
expr_t *e;
expr_t *p;
int arg_count = 0, parm_count = 0;
int arg_count = 0, param_count = 0;
int i;
expr_t *args = 0, **a = &args;
type_t *arg_types[PR_MAX_PARAMS];
expr_t *arg_exprs[PR_MAX_PARAMS][2];
int arg_expr_count = 0;
int emit_args = 0;
expr_t *assign;
expr_t *call;
expr_t *err = 0;
@ -2074,7 +2093,8 @@ build_function_call (expr_t *fexpr, const type_t *ftype, expr_t *params)
if (options.warnings.traditional)
warning (fexpr, "too few arguments");
}
parm_count = -ftype->t.func.num_params - 1;
param_count = -ftype->t.func.num_params - 1;
emit_args = 1;
} else if (ftype->t.func.num_params >= 0) {
if (arg_count > ftype->t.func.num_params) {
return error (fexpr, "too many arguments");
@ -2084,13 +2104,14 @@ build_function_call (expr_t *fexpr, const type_t *ftype, expr_t *params)
if (options.warnings.traditional)
warning (fexpr, "too few arguments");
}
parm_count = ftype->t.func.num_params;
param_count = ftype->t.func.num_params;
}
// params is reversed (a, b, c) -> c, b, a
for (i = arg_count - 1, e = params; i >= 0; i--, e = e->next) {
type_t *t;
if (e->type == ex_compound) {
if (i < parm_count) {
if (i < param_count) {
t = ftype->t.func.param_types[i];
} else {
return error (e, "cannot pass compound initializer "
@ -2109,7 +2130,7 @@ build_function_call (expr_t *fexpr, const type_t *ftype, expr_t *params)
if (type_size (t) > type_size (&type_param))
err = error (e, "formal parameter %d is too large to be passed by"
" value", i + 1);
if (i < parm_count) {
if (i < param_count) {
if (e->type == ex_nil)
convert_nil (e, t = ftype->t.func.param_types[i]);
if (e->type == ex_bool)
@ -2154,7 +2175,13 @@ build_function_call (expr_t *fexpr, const type_t *ftype, expr_t *params)
call = expr_file_line (new_block_expr (), fexpr);
call->e.block.is_call = 1;
// args is built in reverse order so it matches params
for (p = params, i = 0; p; p = p->next, i++) {
if (emit_args && arg_count - i == param_count) {
emit_args = 0;
*a = new_args_expr ();
a = &(*a)->next;
}
expr_t *e = p;
if (e->type == ex_compound) {
e = expr_file_line (initialized_temp_expr (arg_types[i], e), e);
@ -2175,6 +2202,11 @@ build_function_call (expr_t *fexpr, const type_t *ftype, expr_t *params)
}
a = &(*a)->next;
}
if (emit_args) {
emit_args = 0;
*a = new_args_expr ();
a = &(*a)->next;
}
for (i = 0; i < arg_expr_count - 1; i++) {
assign = assign_expr (arg_exprs[i][1], arg_exprs[i][0]);
append_expr (call, expr_file_line (assign, arg_exprs[i][0]));

View file

@ -140,6 +140,7 @@ is_lvalue (const expr_t *expr)
case ex_return:
case ex_adjstk:
case ex_with:
case ex_args:
break;
case ex_count:
internal_error (expr, "invalid expression");

View file

@ -486,33 +486,15 @@ check_function (symbol_t *fsym)
}
static void
build_scope (symbol_t *fsym, symtab_t *parent)
build_v6p_scope (symbol_t *fsym)
{
int i;
param_t *p;
symbol_t *args = 0;
symbol_t *param;
symtab_t *parameters;
symtab_t *locals;
symtab_t *parameters = fsym->s.func->parameters;
symtab_t *locals = fsym->s.func->locals;
check_function (fsym);
fsym->s.func->label_scope = new_symtab (0, stab_local);
parameters = new_symtab (parent, stab_local);
parameters->space = defspace_new (ds_virtual);
fsym->s.func->parameters = parameters;
locals = new_symtab (parameters, stab_local);
locals->space = defspace_new (ds_virtual);
fsym->s.func->locals = locals;
if (!fsym->s.func) {
internal_error (0, "function %s not defined", fsym->name);
}
if (!is_func (fsym->s.func->type)) {
internal_error (0, "function type %s not a funciton", fsym->name);
}
if (fsym->s.func->type->t.func.num_params < 0) {
args = new_symbol_type (".args", &type_va_list);
initialize_def (args, 0, parameters->space, sc_param, locals);
@ -541,6 +523,78 @@ build_scope (symbol_t *fsym, symtab_t *parent)
}
}
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->s.def = def;
param->sy_type = sy_var;
symtab_addsymbol (parameters, param);
}
static void
build_rua_scope (symbol_t *fsym)
{
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 (!p->name) {
error (0, "parameter name omitted");
p->name = save_string ("");
}
param = new_symbol_type (p->name, p->type);
}
create_param (fsym->s.func->parameters, param);
param->s.def->reg = fsym->s.func->temp_reg;;
}
}
static void
build_scope (symbol_t *fsym, symtab_t *parent)
{
symtab_t *parameters;
symtab_t *locals;
if (!fsym->s.func) {
internal_error (0, "function %s not defined", fsym->name);
}
if (!is_func (fsym->s.func->type)) {
internal_error (0, "function type %s not a funciton", fsym->name);
}
check_function (fsym);
fsym->s.func->label_scope = new_symtab (0, stab_local);
parameters = new_symtab (parent, stab_local);
parameters->space = defspace_new (ds_virtual);
fsym->s.func->parameters = parameters;
locals = new_symtab (parameters, stab_local);
locals->space = defspace_new (ds_virtual);
fsym->s.func->locals = locals;
if (options.code.progsversion == PROG_VERSION) {
build_rua_scope (fsym);
} else {
build_v6p_scope (fsym);
}
}
function_t *
new_function (const char *name, const char *nice_name)
{

View file

@ -1026,10 +1026,19 @@ expr_call_v6p (sblock_t *sblock, expr_t *call, operand_t **op)
statement_t *s;
// function arguments are in reverse order
for (a = args; a; a = a->next)
for (a = args; a; a = a->next) {
if (a->type == ex_args) {
// v6p uses callN and pr_argc
continue;
}
count++;
}
ind = count;
for (a = args; a; a = a->next) {
if (a->type == ex_args) {
// v6p uses callN and pr_argc
continue;
}
ind--;
param = new_param_expr (get_type (a), ind);
if (count && options.code.progsversion != PROG_ID_VERSION && ind < 2) {
@ -1091,8 +1100,11 @@ expr_call (sblock_t *sblock, expr_t *call, operand_t **op)
defspace_t *arg_space = current_func->arguments;
expr_t *func = call->e.branch.target;
expr_t *args = call->e.branch.args;
expr_t *args_va_list = 0; // .args (...) parameter
expr_t *args_params = 0; // first arg in ...
operand_t *use = 0;
operand_t *kill = 0;
int num_params = 0;
defspace_reset (arg_space);
@ -1111,9 +1123,21 @@ expr_call (sblock_t *sblock, expr_t *call, operand_t **op)
def->offset = defspace_alloc_aligned_highwater (arg_space, size,
alignment);
def->type = arg_type;
expr_t *assign = assign_expr (new_def_expr (def), a);
expr_file_line (assign, call);
sblock = statement_slist (sblock, assign);
def->reg = current_func->temp_reg;
expr_t *def_expr = expr_file_line (new_def_expr (def), call);
if (a->type == ex_args) {
args_va_list = def_expr;
} else {
if (args_va_list && !args_params) {
args_params = def_expr;
}
if (args_va_list) {
num_params++;
}
expr_t *assign = assign_expr (def_expr, a);
expr_file_line (assign, call);
sblock = statement_slist (sblock, assign);
}
// The call both uses and kills the arguments: use is obvious, but kill
// is because the callee has direct access to them and might modify
@ -1126,6 +1150,31 @@ expr_call (sblock_t *sblock, expr_t *call, operand_t **op)
k->next = kill;
kill = k;
}
if (args_va_list) {
expr_t *assign;
expr_t *count;
expr_t *list;
expr_t *args_count = field_expr (args_va_list,
new_name_expr ("count"));
expr_t *args_list = field_expr (args_va_list,
new_name_expr ("list"));
expr_file_line (args_count, call);
expr_file_line (args_list, call);
count = new_short_expr (num_params);
assign = assign_expr (args_count, count);
expr_file_line (assign, call);
sblock = statement_slist (sblock, assign);
if (args_params) {
list = address_expr (args_params, 0, &type_param);
} else {
list = new_nil_expr ();
}
assign = assign_expr (args_list, list);
expr_file_line (assign, call);
sblock = statement_slist (sblock, assign);
}
statement_t *s = new_statement (st_func, "call", call);
sblock = statement_subexpr (sblock, func, &s->opa);
if (!op) {

View file

@ -55,6 +55,19 @@ bi_printf (progs_t *pr)
else
dstring_clear (dstr);
if (pr->progs->version == PROG_VERSION) {
__auto_type va_list = &P_PACKED (pr, pr_va_list_t, 1);
count = va_list->count;
if (count) {
args = alloca (count * sizeof (pr_type_t *));
for (int i = 0; i < count; i++) {
args[i] = &pr->pr_globals[va_list->list + i * 4];
}
} else {
args = 0;
}
}
PR_Sprintf (pr, dstr, "bi_printf", fmt, count, args);
if (dstr->str)
fputs (dstr->str, stdout);