From 37f08f9d4f34decd77b6a07449afe11cee4a5c54 Mon Sep 17 00:00:00 2001 From: Bill Currie Date: Mon, 24 Jan 2022 18:35:16 +0900 Subject: [PATCH] [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. --- libs/gamecode/pr_exec.c | 2 +- tools/qfcc/include/expr.h | 9 ++++ tools/qfcc/include/expr_names.h | 1 + tools/qfcc/source/dot_expr.c | 10 ++++ tools/qfcc/source/expr.c | 42 +++++++++++++-- tools/qfcc/source/expr_assign.c | 1 + tools/qfcc/source/function.c | 96 +++++++++++++++++++++++++-------- tools/qfcc/source/statements.c | 57 ++++++++++++++++++-- tools/qfcc/test/test-bi.c | 13 +++++ 9 files changed, 200 insertions(+), 31 deletions(-) diff --git a/libs/gamecode/pr_exec.c b/libs/gamecode/pr_exec.c index 519808418..80df7c310 100644 --- a/libs/gamecode/pr_exec.c +++ b/libs/gamecode/pr_exec.c @@ -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; } diff --git a/tools/qfcc/include/expr.h b/tools/qfcc/include/expr.h index aeb4e86ff..7783a572d 100644 --- a/tools/qfcc/include/expr.h +++ b/tools/qfcc/include/expr.h @@ -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); diff --git a/tools/qfcc/include/expr_names.h b/tools/qfcc/include/expr_names.h index 5af4e739b..820ba9826 100644 --- a/tools/qfcc/include/expr_names.h +++ b/tools/qfcc/include/expr_names.h @@ -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 ///@} diff --git a/tools/qfcc/source/dot_expr.c b/tools/qfcc/source/dot_expr.c index 3b918ca12..687cfb868 100644 --- a/tools/qfcc/source/dot_expr.c +++ b/tools/qfcc/source/dot_expr.c @@ -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; diff --git a/tools/qfcc/source/expr.c b/tools/qfcc/source/expr.c index 48476ab89..a5740700e 100644 --- a/tools/qfcc/source/expr.c +++ b/tools/qfcc/source/expr.c @@ -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])); diff --git a/tools/qfcc/source/expr_assign.c b/tools/qfcc/source/expr_assign.c index 0926931fb..9aa1f2784 100644 --- a/tools/qfcc/source/expr_assign.c +++ b/tools/qfcc/source/expr_assign.c @@ -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"); diff --git a/tools/qfcc/source/function.c b/tools/qfcc/source/function.c index 82982b955..81a20af46 100644 --- a/tools/qfcc/source/function.c +++ b/tools/qfcc/source/function.c @@ -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) { diff --git a/tools/qfcc/source/statements.c b/tools/qfcc/source/statements.c index 1168c3fcf..028ec9e96 100644 --- a/tools/qfcc/source/statements.c +++ b/tools/qfcc/source/statements.c @@ -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) { diff --git a/tools/qfcc/test/test-bi.c b/tools/qfcc/test/test-bi.c index 5e6a093ef..412a341bf 100644 --- a/tools/qfcc/test/test-bi.c +++ b/tools/qfcc/test/test-bi.c @@ -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);