mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2025-04-07 01:42:04 +00:00
[qfcc] Implement parameter qualifiers in Ruamoko
Now parameters can be declared `const`, `@in`, `@out`, `@inout`. `@in` is redundant as it's the default, but I guess it's nice for self-documenting code. `const` marks the parameter as read-only in the function, `@out` and `@inout` allow the parameter to pass the value back out (by copy), but `@out` does not initialize the parameter before calling and returning without setting an `@out` parameter is an error (but unfortunately, currently detected only when optimizing). Unfortunately, it seems to have broken (only!) v6 progs when optimizing as the second parameter gets optimized out.
This commit is contained in:
parent
4dd461d1e6
commit
b58e7791fd
16 changed files with 269 additions and 30 deletions
|
@ -104,6 +104,7 @@ typedef struct def_s {
|
|||
bool external:1; ///< externally declared def
|
||||
bool local:1; ///< function local def
|
||||
bool param:1; ///< function param def
|
||||
bool out_param:1; ///< function out param def
|
||||
bool argument:1; ///< function argument def
|
||||
bool system:1; ///< system def
|
||||
bool nosave:1; ///< don't set DEF_SAVEGLOBAL
|
||||
|
|
|
@ -265,6 +265,11 @@ typedef struct {
|
|||
const type_t *ret_type; ///< void for non-call
|
||||
} ex_branch_t;
|
||||
|
||||
typedef struct {
|
||||
const expr_t *in; ///< source expression for arg
|
||||
const expr_t *out; ///< destination expression for arg
|
||||
} ex_inout_t;
|
||||
|
||||
typedef struct {
|
||||
const expr_t *ret_val;
|
||||
int at_return; ///< return void_return call through void
|
||||
|
@ -348,6 +353,7 @@ typedef struct expr_s {
|
|||
ex_address_t address; ///< alias expr params
|
||||
ex_assign_t assign; ///< assignment expr params
|
||||
ex_branch_t branch; ///< branch expr params
|
||||
ex_inout_t inout; ///< inout arg params
|
||||
ex_return_t retrn; ///< return expr params
|
||||
ex_adjstk_t adjstk; ///< stack adjust param
|
||||
ex_with_t with; ///< with expr param
|
||||
|
|
|
@ -58,6 +58,7 @@ EX_EXPR(alias) ///< view expression as different type (::ex_alias_t)
|
|||
EX_EXPR(address) ///< address of an lvalue expression (::ex_address_t)
|
||||
EX_EXPR(assign) ///< assignment of src expr to dst expr (::ex_assing_t)
|
||||
EX_EXPR(branch) ///< branch expression (::ex_branch_t)
|
||||
EX_EXPR(inout) ///< inout arg expression (::ex_inout_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)
|
||||
|
|
|
@ -163,6 +163,13 @@ typedef struct metafunc_s {
|
|||
|
||||
extern function_t *current_func;
|
||||
|
||||
typedef enum param_qual_e {
|
||||
pq_const,
|
||||
pq_in,
|
||||
pq_out,
|
||||
pq_inout,
|
||||
} param_qual_t;
|
||||
|
||||
/** Representation of a function parameter.
|
||||
\note The first two fields match the first two fields of keywordarg_t
|
||||
in method.h
|
||||
|
@ -173,6 +180,7 @@ typedef struct param_s {
|
|||
const type_t *type;
|
||||
const expr_t *type_expr;
|
||||
const char *name;
|
||||
param_qual_t qual;
|
||||
} param_t;
|
||||
|
||||
typedef struct expr_s expr_t;
|
||||
|
|
|
@ -49,6 +49,7 @@ typedef enum storage_class_e {
|
|||
|
||||
sc_in,
|
||||
sc_out,
|
||||
sc_inout,
|
||||
sc_uniform,
|
||||
sc_buffer,
|
||||
sc_shared,
|
||||
|
|
|
@ -35,10 +35,12 @@
|
|||
|
||||
#include "specifier.h"
|
||||
|
||||
typedef enum param_qual_e param_qual_t;
|
||||
typedef struct ty_func_s {
|
||||
const struct type_s *ret_type;
|
||||
int num_params;
|
||||
const struct type_s **param_types;
|
||||
param_qual_t *param_quals;
|
||||
union {
|
||||
struct {
|
||||
unsigned no_va_list:1;///< don't inject va_list for ... function
|
||||
|
|
|
@ -85,6 +85,7 @@ type_t type_SEL = {
|
|||
{{&type_selector}},
|
||||
};
|
||||
const type_t *IMP_params[] = { &type_id, &type_SEL };
|
||||
param_qual_t IMP_quals[] = { pq_in, pq_in };
|
||||
type_t type_IMP = {
|
||||
.type = ev_func,
|
||||
.name = "IMP",
|
||||
|
@ -92,7 +93,13 @@ type_t type_IMP = {
|
|||
.width = 1,
|
||||
.columns = 1,
|
||||
.meta = ty_basic,
|
||||
{{&type_id, -3, IMP_params, .no_va_list = 1}},
|
||||
.func = {
|
||||
.ret_type = &type_id,
|
||||
.num_params = -3,
|
||||
.param_types = IMP_params,
|
||||
.param_quals = IMP_quals,
|
||||
.no_va_list = 1,
|
||||
},
|
||||
};
|
||||
type_t type_super = {
|
||||
.type = ev_invalid,
|
||||
|
@ -106,6 +113,7 @@ type_t type_SuperPtr = {
|
|||
{{&type_super}},
|
||||
};
|
||||
const type_t *supermsg_params[] = { &type_SuperPtr, &type_SEL };
|
||||
param_qual_t supermsg_quals[] = { pq_in, pq_in };
|
||||
type_t type_supermsg = {
|
||||
.type = ev_func,
|
||||
.name = ".supermsg",
|
||||
|
@ -113,7 +121,12 @@ type_t type_supermsg = {
|
|||
.width = 1,
|
||||
.columns = 1,
|
||||
.meta = ty_basic,
|
||||
{{&type_id, -3, supermsg_params}},
|
||||
.func = {//FIXME is this right?
|
||||
.ret_type = &type_id,
|
||||
.num_params = -3,
|
||||
.param_types = supermsg_params,
|
||||
.param_quals = supermsg_quals,
|
||||
},
|
||||
};
|
||||
type_t type_method = {
|
||||
.type = ev_invalid,
|
||||
|
@ -146,13 +159,19 @@ type_t type_moduleptr = {
|
|||
const type_t *obj_exec_class_params[] = {
|
||||
&type_moduleptr,
|
||||
};
|
||||
param_qual_t obj_exec_class_quals[] = { pq_in };
|
||||
type_t type_exec_class = {
|
||||
.type = ev_func,
|
||||
.alignment = 1,
|
||||
.width = 1,
|
||||
.columns = 1,
|
||||
.meta = ty_basic,
|
||||
{{&type_void, 1, obj_exec_class_params}},
|
||||
.func = {
|
||||
.ret_type = &type_void,
|
||||
.num_params = 1,
|
||||
.param_types = obj_exec_class_params,
|
||||
.param_quals = obj_exec_class_quals,
|
||||
},
|
||||
};
|
||||
// the cast of 1 in the init is to ensure pointers to incomplete types
|
||||
// are never misidentified as id. It will be set to the correct value
|
||||
|
|
|
@ -95,6 +95,7 @@ set_storage_bits (def_t *def, storage_class_t storage)
|
|||
case sc_local:
|
||||
def->local = true;
|
||||
break;
|
||||
case sc_inout:
|
||||
case sc_param:
|
||||
def->local = true;
|
||||
def->param = true;
|
||||
|
|
|
@ -123,6 +123,12 @@ get_type (const expr_t *e)
|
|||
const type_t *type = 0;
|
||||
e = convert_name (e);
|
||||
switch (e->type) {
|
||||
case ex_inout:
|
||||
if (!e->inout.out) {
|
||||
internal_error (e, "inout with no out");
|
||||
}
|
||||
type = get_type (e->inout.out);
|
||||
break;
|
||||
case ex_branch:
|
||||
type = e->branch.ret_type;
|
||||
break;
|
||||
|
@ -1730,6 +1736,9 @@ has_function_call (const expr_t *e)
|
|||
return 0;
|
||||
}
|
||||
return has_function_call (e->branch.test);
|
||||
case ex_inout:
|
||||
// in is just a cast of out, if it's not null
|
||||
return has_function_call (e->inout.out);
|
||||
case ex_return:
|
||||
return has_function_call (e->retrn.ret_val);
|
||||
case ex_horizontal:
|
||||
|
@ -1887,6 +1896,7 @@ unary_expr (int op, const expr_t *e)
|
|||
return edag_add_expr (n);
|
||||
}
|
||||
case ex_branch:
|
||||
case ex_inout:
|
||||
return error (e, "invalid type for unary -");
|
||||
case ex_expr:
|
||||
case ex_bool:
|
||||
|
@ -2015,6 +2025,7 @@ unary_expr (int op, const expr_t *e)
|
|||
case ex_multivec:
|
||||
return algebra_dual (e);
|
||||
case ex_branch:
|
||||
case ex_inout:
|
||||
case ex_nil:
|
||||
return error (e, "invalid type for unary !");
|
||||
case ex_count:
|
||||
|
@ -2087,6 +2098,7 @@ unary_expr (int op, const expr_t *e)
|
|||
return error (e, "invalid type for unary ~");
|
||||
goto bitnot_expr;
|
||||
case ex_branch:
|
||||
case ex_inout:
|
||||
return error (e, "invalid type for unary ~");
|
||||
case ex_expr:
|
||||
case ex_bool:
|
||||
|
@ -2161,7 +2173,7 @@ build_function_call (const expr_t *fexpr, const type_t *ftype, const expr_t *par
|
|||
expr_t *call;
|
||||
const expr_t *err = 0;
|
||||
|
||||
int arg_count = params ? list_count (¶ms->list) :0;
|
||||
int arg_count = params ? list_count (¶ms->list) : 0;
|
||||
const expr_t *arguments[arg_count + 1];
|
||||
if (params) {
|
||||
list_scatter_rev (¶ms->list, arguments);
|
||||
|
@ -2231,17 +2243,42 @@ build_function_call (const expr_t *fexpr, const type_t *ftype, const expr_t *par
|
|||
" value", i + 1);
|
||||
}
|
||||
if (i < param_count) {
|
||||
auto param_type = ftype->func.param_types[i];
|
||||
if (e->type == ex_nil)
|
||||
e = convert_nil (e, t = ftype->func.param_types[i]);
|
||||
e = convert_nil (e, t = param_type);
|
||||
if (e->type == ex_bool)
|
||||
e = convert_from_bool (e, ftype->func.param_types[i]);
|
||||
if (e->type == ex_error)
|
||||
return e;
|
||||
if (!type_assignable (ftype->func.param_types[i], t)) {
|
||||
err = param_mismatch (e, i + 1, fexpr->symbol->name,
|
||||
ftype->func.param_types[i], t);
|
||||
e = convert_from_bool (e, param_type);
|
||||
if (e->type == ex_error) {
|
||||
err = e;
|
||||
continue;
|
||||
}
|
||||
t = ftype->func.param_types[i];
|
||||
auto param_qual = ftype->func.param_quals[i];
|
||||
if (param_qual == pq_out || param_qual == pq_inout) {
|
||||
//FIXME should be able to use something like *foo() as
|
||||
//an out or inout arg
|
||||
if (!is_lvalue (e) || has_function_call (e)) {
|
||||
error (e, "lvalue required for %s parameter",
|
||||
param_qual & pq_in ? "inout" : "out");
|
||||
}
|
||||
if (param_qual == pq_inout
|
||||
&& !type_assignable (param_type, t)) {
|
||||
err = param_mismatch (e, i + 1, fexpr->symbol->name,
|
||||
param_type, t);
|
||||
}
|
||||
// check assignment FROM parameter is ok, but only if
|
||||
// there wasn't an earlier error so param mismatch doesn't
|
||||
// get double-reported for inout params.
|
||||
if (!err && !type_assignable (t, param_type)) {
|
||||
err = param_mismatch (e, i + 1, fexpr->symbol->name,
|
||||
t, param_type);
|
||||
}
|
||||
} else {
|
||||
if (!type_assignable (param_type, t)) {
|
||||
err = param_mismatch (e, i + 1, fexpr->symbol->name,
|
||||
param_type, t);
|
||||
}
|
||||
}
|
||||
t = param_type;
|
||||
} else {
|
||||
if (e->type == ex_nil)
|
||||
e = convert_nil (e, t = type_nil);
|
||||
|
@ -2285,6 +2322,7 @@ build_function_call (const expr_t *fexpr, const type_t *ftype, const expr_t *par
|
|||
expr_t *args = new_list_expr (0);
|
||||
// args is built in reverse order so it matches params
|
||||
for (int i = 0; i < arg_count; i++) {
|
||||
auto param_qual = i < param_count ? ftype->func.param_quals[i] : pq_in;
|
||||
if (emit_args && i == param_count) {
|
||||
expr_prepend_expr (args, new_args_expr ());
|
||||
emit_args = false;
|
||||
|
@ -2308,7 +2346,20 @@ build_function_call (const expr_t *fexpr, const type_t *ftype, const expr_t *par
|
|||
arg_exprs[arg_expr_count][1] = tmp;
|
||||
arg_expr_count++;
|
||||
} else {
|
||||
e = cast_expr (arg_types[i], e);
|
||||
if (param_qual != pq_out) {
|
||||
// out parameters do not need to be sent so no need to cast
|
||||
}
|
||||
if (param_qual & pq_out) {
|
||||
auto inout = new_expr ();
|
||||
inout->type = ex_inout;
|
||||
if (param_qual == pq_inout) {
|
||||
inout->inout.in = cast_expr (arg_types[i], e);
|
||||
}
|
||||
inout->inout.out = e;
|
||||
e = inout;
|
||||
} else {
|
||||
e = cast_expr (arg_types[i], e);
|
||||
}
|
||||
expr_prepend_expr (args, e);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -92,7 +92,7 @@ is_lvalue (const expr_t *expr)
|
|||
case sy_name:
|
||||
break;
|
||||
case sy_var:
|
||||
return 1;
|
||||
return !expr->symbol->def->readonly;
|
||||
case sy_const:
|
||||
break;
|
||||
case sy_type:
|
||||
|
@ -130,6 +130,7 @@ is_lvalue (const expr_t *expr)
|
|||
}
|
||||
break;
|
||||
case ex_branch:
|
||||
case ex_inout:
|
||||
case ex_memset:
|
||||
case ex_compound:
|
||||
case ex_state:
|
||||
|
|
|
@ -72,6 +72,7 @@ edag_add_expr (const expr_t *expr)
|
|||
case ex_compound:
|
||||
case ex_memset:
|
||||
case ex_branch:
|
||||
case ex_inout:
|
||||
case ex_return:
|
||||
case ex_adjstk:
|
||||
case ex_with:
|
||||
|
|
|
@ -1413,7 +1413,9 @@ flow_uninit_scan_statements (flownode_t *node, set_t *defs, set_t *uninit)
|
|||
} else {
|
||||
def_t *def = flowvar_get_def (var);
|
||||
if (def) {
|
||||
if (options.warnings.uninited_variable) {
|
||||
if (def->out_param) {
|
||||
error (st->expr, "%s not initialized", def->name);
|
||||
} else if (options.warnings.uninited_variable) {
|
||||
warning (st->expr, "%s may be used uninitialized",
|
||||
def->name);
|
||||
}
|
||||
|
|
|
@ -361,6 +361,7 @@ new_param (const char *selector, const type_t *type, const char *name)
|
|||
.selector = selector,
|
||||
.type = find_type (type),
|
||||
.name = name,
|
||||
.qual = pq_in,
|
||||
};
|
||||
return param;
|
||||
}
|
||||
|
@ -374,6 +375,7 @@ new_generic_param (const expr_t *type_expr, const char *name)
|
|||
*param = (param_t) {
|
||||
.type_expr = type_expr,
|
||||
.name = name,
|
||||
.qual = pq_in,
|
||||
};
|
||||
return param;
|
||||
}
|
||||
|
@ -469,7 +471,8 @@ parse_params (const type_t *return_type, param_t *parms)
|
|||
}
|
||||
}
|
||||
if (count) {
|
||||
new->func.param_types = malloc (count * sizeof (type_t));
|
||||
new->func.param_types = malloc (count * sizeof (type_t *));
|
||||
new->func.param_quals = malloc (count * sizeof (param_qual_t));
|
||||
}
|
||||
for (p = parms; p; p = p->next) {
|
||||
if (!p->selector && !p->type && !p->name) {
|
||||
|
@ -483,6 +486,7 @@ parse_params (const type_t *return_type, param_t *parms)
|
|||
}
|
||||
auto ptype = unalias_type (p->type);
|
||||
new->func.param_types[new->func.num_params] = ptype;
|
||||
new->func.param_quals[new->func.num_params] = p->qual;
|
||||
new->func.num_params++;
|
||||
}
|
||||
}
|
||||
|
@ -989,6 +993,14 @@ build_v6p_scope (symbol_t *fsym)
|
|||
}
|
||||
param = new_symbol_type (p->name, p->type);
|
||||
initialize_def (param, 0, parameters->space, sc_param, locals);
|
||||
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;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
|
@ -1051,6 +1063,14 @@ build_rua_scope (symbol_t *fsym)
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -107,6 +107,7 @@ glsl_declare_block (specifier_t spec, symbol_t *block_sym,
|
|||
case sc_param:
|
||||
case sc_local:
|
||||
case sc_argument:
|
||||
case sc_inout:
|
||||
break;
|
||||
}
|
||||
if (!block_tab) {
|
||||
|
|
|
@ -520,15 +520,45 @@ make_ellipsis (void)
|
|||
static param_t *
|
||||
make_param (specifier_t spec)
|
||||
{
|
||||
//FIXME should not be sc_global
|
||||
if (spec.storage == sc_global) {
|
||||
spec.storage = sc_param;
|
||||
}
|
||||
|
||||
param_t *param;
|
||||
if (spec.type_expr) {
|
||||
auto param = new_generic_param (spec.type_expr, spec.sym->name);
|
||||
return param;
|
||||
param = new_generic_param (spec.type_expr, spec.sym->name);
|
||||
} else {
|
||||
spec = default_type (spec, spec.sym);
|
||||
spec.type = find_type (append_type (spec.sym->type, spec.type));
|
||||
auto param = new_param (0, spec.type, spec.sym->name);
|
||||
return param;
|
||||
param = new_param (0, spec.type, spec.sym->name);
|
||||
}
|
||||
if (spec.is_const) {
|
||||
if (spec.storage == sc_out) {
|
||||
error (0, "cannot use const with @out");
|
||||
} else if (spec.storage == sc_inout) {
|
||||
error (0, "cannot use const with @inout");
|
||||
} else {
|
||||
if (spec.storage != sc_in && spec.storage != sc_param) {
|
||||
internal_error (0, "unexpected parameter storage: %d",
|
||||
spec.storage);
|
||||
}
|
||||
}
|
||||
param->qual = pq_const;
|
||||
} else {
|
||||
if (spec.storage == sc_out) {
|
||||
param->qual = pq_out;
|
||||
} else if (spec.storage == sc_inout) {
|
||||
param->qual = pq_inout;
|
||||
} else {
|
||||
if (spec.storage != sc_in && spec.storage != sc_param) {
|
||||
internal_error (0, "unexpected parameter storage: %d",
|
||||
spec.storage);
|
||||
}
|
||||
param->qual = pq_in;
|
||||
}
|
||||
}
|
||||
return param;
|
||||
}
|
||||
|
||||
static param_t *
|
||||
|
@ -2776,6 +2806,7 @@ static keyword_t at_keywords[] = {
|
|||
{"sizeof", QC_SIZEOF },
|
||||
{"not", QC_NOT },
|
||||
{"auto", QC_TYPE_SPEC, .spec = { .type = &type_auto } },
|
||||
{"const", QC_TYPE_QUAL, .spec = { .is_const = true } },
|
||||
};
|
||||
|
||||
// These keywords require the QuakeForge VM to be of any use. ie, they cannot
|
||||
|
@ -2793,6 +2824,9 @@ static keyword_t qf_keywords[] = {
|
|||
{"@va_list", QC_TYPE_SPEC, .spec = { .type = &type_va_list } },
|
||||
{"@param", QC_TYPE_SPEC, .spec = { .type = &type_param } },
|
||||
{"@return", QC_AT_RETURN, },
|
||||
{"@in", QC_TYPE_QUAL, .spec = { .storage = sc_in } },
|
||||
{"@out", QC_TYPE_QUAL, .spec = { .storage = sc_out } },
|
||||
{"@inout", QC_TYPE_QUAL, .spec = { .storage = sc_inout } },
|
||||
|
||||
{"@hadamard", QC_HADAMARD, },
|
||||
{"@cross", QC_CROSS, },
|
||||
|
|
|
@ -1099,6 +1099,12 @@ expr_call_v6p (sblock_t *sblock, const expr_t *call, operand_t **op)
|
|||
continue;
|
||||
}
|
||||
ind--;
|
||||
if (a->type == ex_inout) {
|
||||
a = a->inout.in;
|
||||
if (!a) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
param = new_param_expr (get_type (a), ind);
|
||||
if (count && options.code.progsversion != PROG_ID_VERSION && ind < 2) {
|
||||
pref = "r";
|
||||
|
@ -1148,8 +1154,26 @@ expr_call_v6p (sblock_t *sblock, const expr_t *call, operand_t **op)
|
|||
*op = return_operand (call->branch.ret_type, call);
|
||||
}
|
||||
sblock_add_statement (sblock, s);
|
||||
sblock->next = new_sblock ();
|
||||
return sblock->next;
|
||||
|
||||
ind = count;
|
||||
for (auto li = args->list.head; li; li = li->next) {
|
||||
auto a = li->expr;
|
||||
if (a->type != ex_args) {
|
||||
ind--;
|
||||
}
|
||||
if (a->type != ex_inout) {
|
||||
continue;
|
||||
}
|
||||
a = a->inout.out;
|
||||
param = new_param_expr (get_type (a), ind);
|
||||
operand_t *p = nullptr;
|
||||
sblock = statement_subexpr (sblock, param, &p);
|
||||
operand_t *arg = nullptr;
|
||||
sblock = statement_subexpr (sblock, a, &arg);
|
||||
s = assign_statement (arg, p, a);
|
||||
sblock_add_statement (sblock, s);
|
||||
}
|
||||
return sblock;
|
||||
}
|
||||
|
||||
static sblock_t *
|
||||
|
@ -1167,6 +1191,7 @@ expr_call (sblock_t *sblock, const expr_t *call, operand_t **op)
|
|||
const expr_t *args_params = 0; // first arg in ...
|
||||
operand_t *use = 0;
|
||||
operand_t *kill = 0;
|
||||
operand_t *define = nullptr;
|
||||
int num_params = 0;
|
||||
|
||||
defspace_reset (arg_space);
|
||||
|
@ -1174,6 +1199,7 @@ expr_call (sblock_t *sblock, const expr_t *call, operand_t **op)
|
|||
|
||||
int num_args = list_count (&call->branch.args->list);
|
||||
const expr_t *args[num_args + 1];
|
||||
def_t *out_args[num_args + 1] = {};
|
||||
list_scatter_rev (&call->branch.args->list, args);
|
||||
int arg_num = 0;
|
||||
for (int i = 0; i < num_args; i++) {
|
||||
|
@ -1191,6 +1217,9 @@ expr_call (sblock_t *sblock, const expr_t *call, operand_t **op)
|
|||
alignment);
|
||||
def->type = arg_type;
|
||||
def->reg = current_func->temp_reg;
|
||||
if (a->type == ex_inout) {
|
||||
out_args[i] = def;
|
||||
}
|
||||
const expr_t *def_expr = new_def_expr (def);
|
||||
if (a->type == ex_args) {
|
||||
args_va_list = def_expr;
|
||||
|
@ -1201,20 +1230,35 @@ expr_call (sblock_t *sblock, const expr_t *call, operand_t **op)
|
|||
if (args_va_list) {
|
||||
num_params++;
|
||||
}
|
||||
const expr_t *assign = assign_expr (def_expr, a);
|
||||
sblock = statement_single (sblock, assign);
|
||||
auto src = a;
|
||||
if (a->type == ex_inout) {
|
||||
src = a->inout.in;
|
||||
}
|
||||
if (src) {
|
||||
const expr_t *assign = assign_expr (def_expr, src);
|
||||
sblock = statement_single (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
|
||||
// them
|
||||
// need two ops for the one def because there's two lists
|
||||
operand_t *u = def_operand (def, arg_type, call);
|
||||
operand_t *k = def_operand (def, arg_type, call);
|
||||
u->next = use;
|
||||
use = u;
|
||||
k->next = kill;
|
||||
kill = k;
|
||||
if (a->type != ex_inout || a->inout.in) {
|
||||
auto u = def_operand (def, arg_type, call);
|
||||
u->next = use;
|
||||
use = u;
|
||||
}
|
||||
if (a->type != ex_inout) {
|
||||
auto k = def_operand (def, arg_type, call);
|
||||
k->next = kill;
|
||||
kill = k;
|
||||
} else {
|
||||
// inout and out params define the argument
|
||||
auto d = def_operand (def, arg_type, call);
|
||||
d->next = define;
|
||||
define = d;
|
||||
}
|
||||
}
|
||||
if (args_va_list) {
|
||||
const expr_t *assign;
|
||||
|
@ -1249,7 +1293,25 @@ expr_call (sblock_t *sblock, const expr_t *call, operand_t **op)
|
|||
}
|
||||
s->use = use;
|
||||
s->kill = kill;
|
||||
s->def = define;
|
||||
sblock_add_statement (sblock, s);
|
||||
for (int i = 0; i < num_args; i++) {
|
||||
auto a = args[i];
|
||||
auto out = out_args[i];
|
||||
if (a->type != ex_inout) {
|
||||
continue;
|
||||
}
|
||||
auto dst_expr = a->inout.out;
|
||||
if (!dst_expr) {
|
||||
internal_error (dst_expr, "inout with no out");
|
||||
}
|
||||
if (!out) {
|
||||
internal_error (a, "no arg def for %d", i);
|
||||
}
|
||||
const expr_t *out_expr = new_def_expr (out);
|
||||
const expr_t *assign = assign_expr (dst_expr, out_expr);
|
||||
sblock = statement_single (sblock, assign);
|
||||
}
|
||||
return sblock;
|
||||
}
|
||||
|
||||
|
@ -1457,10 +1519,37 @@ statement_return (sblock_t *sblock, const expr_t *e)
|
|||
{
|
||||
const char *opcode;
|
||||
statement_t *s;
|
||||
operand_t *use = nullptr;
|
||||
|
||||
scoped_src_loc (e);
|
||||
debug (e, "RETURN");
|
||||
opcode = "return";
|
||||
if (current_func) {
|
||||
bool v6p = options.code.progsversion < PROG_VERSION;
|
||||
auto parameters = v6p ? current_func->locals : current_func->parameters;
|
||||
int ind = 0;
|
||||
for (auto p = parameters->symbols; p; p = p->next, ind++) {
|
||||
if (p->sy_type != sy_var || !p->def->out_param) {
|
||||
continue;
|
||||
}
|
||||
auto type = p->def->type;
|
||||
if (v6p) {
|
||||
auto param = new_param_expr (type, ind);
|
||||
operand_t *p_op = nullptr;
|
||||
sblock = statement_subexpr (sblock, param, &p_op);
|
||||
|
||||
auto a = new_def_expr (p->def);
|
||||
operand_t *out = nullptr;
|
||||
sblock = statement_subexpr (sblock, a, &out);
|
||||
s = assign_statement (p_op, out, e);
|
||||
sblock_add_statement (sblock, s);
|
||||
} else {
|
||||
auto u = def_operand (p->def, type, e);
|
||||
u->next = use;
|
||||
use = u;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!e->retrn.ret_val) {
|
||||
if (options.code.progsversion == PROG_ID_VERSION) {
|
||||
auto n = new_expr ();
|
||||
|
@ -1470,6 +1559,7 @@ statement_return (sblock_t *sblock, const expr_t *e)
|
|||
}
|
||||
}
|
||||
s = new_statement (st_func, opcode, e);
|
||||
s->use = use;
|
||||
if (options.code.progsversion < PROG_VERSION) {
|
||||
if (e->retrn.ret_val) {
|
||||
const expr_t *ret_val = e->retrn.ret_val;
|
||||
|
|
Loading…
Reference in a new issue