mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2025-02-25 13:11:00 +00:00
[qfcc] Split out the call related expression code
I've long felt build_function_call was getting a bit big, and expr.c especially so. This should make it easier to rewrite build_function_call for dealing with target-specific code. As a bonus, the int through ... warning is already cleaned up.
This commit is contained in:
parent
a0845e65fb
commit
bcfd1b7660
8 changed files with 453 additions and 397 deletions
|
@ -412,6 +412,8 @@ const expr_t *type_mismatch (const expr_t *e1, const expr_t *e2, int op);
|
||||||
|
|
||||||
const expr_t *param_mismatch (const expr_t *e, int param, const char *fn,
|
const expr_t *param_mismatch (const expr_t *e, int param, const char *fn,
|
||||||
const type_t *t1, const type_t *t2);
|
const type_t *t1, const type_t *t2);
|
||||||
|
const expr_t *reference_error (const expr_t *e, const type_t *dst,
|
||||||
|
const type_t *src);
|
||||||
const expr_t *test_error (const expr_t *e, const type_t *t);
|
const expr_t *test_error (const expr_t *e, const type_t *t);
|
||||||
|
|
||||||
/** Set the current source location for subsequent new expressions.
|
/** Set the current source location for subsequent new expressions.
|
||||||
|
@ -635,6 +637,8 @@ expr_t *new_nil_expr (void);
|
||||||
\return The new args expression node.
|
\return The new args expression node.
|
||||||
*/
|
*/
|
||||||
expr_t *new_args_expr (void);
|
expr_t *new_args_expr (void);
|
||||||
|
const expr_t *new_call_expr (const expr_t *func, const expr_t *args,
|
||||||
|
const type_t *ret_type);
|
||||||
|
|
||||||
/** Create a new value expression node.
|
/** Create a new value expression node.
|
||||||
|
|
||||||
|
@ -966,7 +970,6 @@ const expr_t *binary_expr (int op, const expr_t *e1, const expr_t *e2);
|
||||||
const expr_t *field_expr (const expr_t *e1, const expr_t *e2);
|
const expr_t *field_expr (const expr_t *e1, const expr_t *e2);
|
||||||
const expr_t *asx_expr (int op, const expr_t *e1, const expr_t *e2);
|
const expr_t *asx_expr (int op, const expr_t *e1, const expr_t *e2);
|
||||||
const expr_t *unary_expr (int op, const expr_t *e);
|
const expr_t *unary_expr (int op, const expr_t *e);
|
||||||
void vararg_integer (const expr_t *e);
|
|
||||||
const expr_t *build_function_call (const expr_t *fexpr, const type_t *ftype,
|
const expr_t *build_function_call (const expr_t *fexpr, const type_t *ftype,
|
||||||
const expr_t *params);
|
const expr_t *params);
|
||||||
const expr_t *function_expr (const expr_t *e1, const expr_t *e2);
|
const expr_t *function_expr (const expr_t *e1, const expr_t *e2);
|
||||||
|
@ -975,8 +978,6 @@ struct function_s;
|
||||||
const expr_t *branch_expr (int op, const expr_t *test, const expr_t *label);
|
const expr_t *branch_expr (int op, const expr_t *test, const expr_t *label);
|
||||||
const expr_t *goto_expr (const expr_t *label);
|
const expr_t *goto_expr (const expr_t *label);
|
||||||
const expr_t *jump_table_expr (const expr_t *table, const expr_t *index);
|
const expr_t *jump_table_expr (const expr_t *table, const expr_t *index);
|
||||||
const expr_t *call_expr (const expr_t *func, const expr_t *args,
|
|
||||||
const type_t *ret_type);
|
|
||||||
const expr_t *return_expr (struct function_s *f, const expr_t *e);
|
const expr_t *return_expr (struct function_s *f, const expr_t *e);
|
||||||
const expr_t *at_return_expr (struct function_s *f, const expr_t *e);
|
const expr_t *at_return_expr (struct function_s *f, const expr_t *e);
|
||||||
const expr_t *conditional_expr (const expr_t *cond, const expr_t *e1,
|
const expr_t *conditional_expr (const expr_t *cond, const expr_t *e1,
|
||||||
|
|
|
@ -41,6 +41,7 @@ typedef struct {
|
||||||
void (*build_code) (function_t *func, const expr_t *statements);
|
void (*build_code) (function_t *func, const expr_t *statements);
|
||||||
void (*declare_sym) (specifier_t spec, const expr_t *init,
|
void (*declare_sym) (specifier_t spec, const expr_t *init,
|
||||||
symtab_t *symtab, expr_t *block);
|
symtab_t *symtab, expr_t *block);
|
||||||
|
void (*vararg_int) (const expr_t *e);
|
||||||
} target_t;
|
} target_t;
|
||||||
|
|
||||||
extern target_t current_target;
|
extern target_t current_target;
|
||||||
|
|
|
@ -31,6 +31,7 @@ qfcc_SOURCES = \
|
||||||
tools/qfcc/source/expr_binary.c \
|
tools/qfcc/source/expr_binary.c \
|
||||||
tools/qfcc/source/expr_bool.c \
|
tools/qfcc/source/expr_bool.c \
|
||||||
tools/qfcc/source/expr_cast.c \
|
tools/qfcc/source/expr_cast.c \
|
||||||
|
tools/qfcc/source/expr_call.c \
|
||||||
tools/qfcc/source/expr_compound.c \
|
tools/qfcc/source/expr_compound.c \
|
||||||
tools/qfcc/source/expr_construct.c \
|
tools/qfcc/source/expr_construct.c \
|
||||||
tools/qfcc/source/expr_dag.c \
|
tools/qfcc/source/expr_dag.c \
|
||||||
|
|
|
@ -254,7 +254,7 @@ param_mismatch (const expr_t *e, int param, const char *fn,
|
||||||
get_type_string (t2));
|
get_type_string (t2));
|
||||||
}
|
}
|
||||||
|
|
||||||
static const expr_t *
|
const expr_t *
|
||||||
reference_error (const expr_t *e, const type_t *dst, const type_t *src)
|
reference_error (const expr_t *e, const type_t *dst, const type_t *src)
|
||||||
{
|
{
|
||||||
return error (e, "cannot bind reference of type %s to %s",
|
return error (e, "cannot bind reference of type %s to %s",
|
||||||
|
@ -1997,285 +1997,6 @@ asx_expr (int op, const expr_t *e1, const expr_t *e2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
vararg_integer (const expr_t *e)
|
|
||||||
{
|
|
||||||
if (is_int_val (e) && options.code.progsversion < PROG_VERSION
|
|
||||||
&& options.warnings.vararg_integer) {
|
|
||||||
warning (e, "passing int constant into ... function");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const expr_t *
|
|
||||||
build_function_call (const expr_t *fexpr, const type_t *ftype,
|
|
||||||
const expr_t *params)
|
|
||||||
{
|
|
||||||
int param_count = 0;
|
|
||||||
expr_t *call;
|
|
||||||
const expr_t *err = 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < arg_count; i++) {
|
|
||||||
auto e = arguments[i];
|
|
||||||
if (e->type == ex_error) {
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
if (e->type != ex_compound) {
|
|
||||||
arguments[i] = algebra_optimize (e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ftype->func.num_params < -1) {
|
|
||||||
if (options.code.max_params >= 0
|
|
||||||
&& arg_count > options.code.max_params) {
|
|
||||||
return error (fexpr, "more than %d parameters",
|
|
||||||
options.code.max_params);
|
|
||||||
}
|
|
||||||
if (-arg_count > ftype->func.num_params + 1) {
|
|
||||||
if (!options.traditional)
|
|
||||||
return error (fexpr, "too few arguments");
|
|
||||||
if (options.warnings.traditional)
|
|
||||||
warning (fexpr, "too few arguments");
|
|
||||||
}
|
|
||||||
param_count = -ftype->func.num_params - 1;
|
|
||||||
} else if (ftype->func.num_params >= 0) {
|
|
||||||
if (arg_count > ftype->func.num_params) {
|
|
||||||
return error (fexpr, "too many arguments");
|
|
||||||
} else if (arg_count < ftype->func.num_params) {
|
|
||||||
if (!options.traditional)
|
|
||||||
return error (fexpr, "too few arguments");
|
|
||||||
if (options.warnings.traditional)
|
|
||||||
warning (fexpr, "too few arguments");
|
|
||||||
}
|
|
||||||
param_count = ftype->func.num_params;
|
|
||||||
}
|
|
||||||
|
|
||||||
const type_t *arg_types[arg_count + 1];
|
|
||||||
// params is reversed (a, b, c) -> c, b, a
|
|
||||||
for (int i = 0; i < arg_count; i++) {
|
|
||||||
auto e = arguments[i];
|
|
||||||
const type_t *t;
|
|
||||||
|
|
||||||
if (e->type == ex_compound) {
|
|
||||||
if (i < param_count) {
|
|
||||||
t = ftype->func.param_types[i];
|
|
||||||
} else {
|
|
||||||
return error (e, "cannot pass compound initializer "
|
|
||||||
"through ...");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
t = get_type (e);
|
|
||||||
}
|
|
||||||
if (!t) {
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!type_size (t)) {
|
|
||||||
err = error (e, "type of formal parameter %d is incomplete",
|
|
||||||
i + 1);
|
|
||||||
}
|
|
||||||
if (!is_array (t) && value_too_large (t)) {
|
|
||||||
err = error (e, "formal parameter %d is too large to be passed by"
|
|
||||||
" value", i + 1);
|
|
||||||
}
|
|
||||||
if (i < param_count) {
|
|
||||||
auto param_type = ftype->func.param_types[i];
|
|
||||||
auto param_qual = ftype->func.param_quals[i];
|
|
||||||
|
|
||||||
if (is_reference (param_type) && param_qual != pq_in) {
|
|
||||||
internal_error (e, "qualified reference param (not yet)");
|
|
||||||
}
|
|
||||||
if (is_reference (param_type) && !is_reference (t)) {
|
|
||||||
if (!is_lvalue (e)) {
|
|
||||||
err = error (e, "cannot pass non-lvalue by reference");
|
|
||||||
}
|
|
||||||
if (!err && dereference_type (param_type) != t) {
|
|
||||||
err = reference_error (e, param_type, t);
|
|
||||||
}
|
|
||||||
} else if (!is_reference (param_type) && is_reference (t)) {
|
|
||||||
t = dereference_type (t);
|
|
||||||
}
|
|
||||||
if (e->type == ex_nil) {
|
|
||||||
t = param_type;
|
|
||||||
}
|
|
||||||
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) {
|
|
||||||
t = type_nil;
|
|
||||||
}
|
|
||||||
if (options.code.promote_float) {
|
|
||||||
if (is_scalar (get_type (e)) && is_float (get_type (e))) {
|
|
||||||
t = &type_double;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (is_scalar (get_type (e)) && is_double (get_type (e))) {
|
|
||||||
if (!e->implicit) {
|
|
||||||
warning (e, "passing double into ... function");
|
|
||||||
}
|
|
||||||
if (is_constant (e)) {
|
|
||||||
// don't auto-demote non-constant doubles
|
|
||||||
t = &type_float;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
vararg_integer (e);
|
|
||||||
}
|
|
||||||
arg_types[i] = t;
|
|
||||||
}
|
|
||||||
if (err) {
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool emit_args = false;
|
|
||||||
if (ftype->func.num_params < 0) {
|
|
||||||
emit_args = !ftype->func.no_va_list;
|
|
||||||
}
|
|
||||||
scoped_src_loc (fexpr);
|
|
||||||
call = new_block_expr (0);
|
|
||||||
call->block.is_call = 1;
|
|
||||||
int arg_expr_count = 0;
|
|
||||||
const expr_t *arg_exprs[arg_count + 1][2];
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto e = arguments[i];
|
|
||||||
if (e->type == ex_compound || e->type == ex_multivec) {
|
|
||||||
scoped_src_loc (e);
|
|
||||||
e = initialized_temp_expr (arg_types[i], e);
|
|
||||||
}
|
|
||||||
// FIXME this is target-specific info and should not be in the
|
|
||||||
// expression tree
|
|
||||||
// That, or always use a temp, since it should get optimized out
|
|
||||||
if (has_function_call (e)) {
|
|
||||||
scoped_src_loc (e);
|
|
||||||
auto cast = cast_expr (arg_types[i], e);
|
|
||||||
auto tmp = new_temp_def_expr (arg_types[i]);
|
|
||||||
expr_prepend_expr (args, tmp);
|
|
||||||
|
|
||||||
arg_exprs[arg_expr_count][0] = cast;
|
|
||||||
arg_exprs[arg_expr_count][1] = tmp;
|
|
||||||
arg_expr_count++;
|
|
||||||
} else {
|
|
||||||
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 {
|
|
||||||
if (is_reference (arg_types[i])) {
|
|
||||||
if (is_reference (get_type (e))) {
|
|
||||||
// just copy the param, so no op
|
|
||||||
} else {
|
|
||||||
e = address_expr (e, nullptr);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (is_reference (get_type (e))) {
|
|
||||||
e = pointer_deref (e);
|
|
||||||
}
|
|
||||||
e = cast_expr (arg_types[i], e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
expr_prepend_expr (args, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (emit_args) {
|
|
||||||
emit_args = false;
|
|
||||||
expr_prepend_expr (args, new_args_expr ());
|
|
||||||
}
|
|
||||||
for (int i = 0; i < arg_expr_count; i++) {
|
|
||||||
scoped_src_loc (arg_exprs[i][0]);
|
|
||||||
auto assign = assign_expr (arg_exprs[i][1], arg_exprs[i][0]);
|
|
||||||
append_expr (call, assign);
|
|
||||||
}
|
|
||||||
auto ret_type = ftype->func.ret_type;
|
|
||||||
call->block.result = call_expr (fexpr, args, ret_type);
|
|
||||||
return call;
|
|
||||||
}
|
|
||||||
|
|
||||||
const expr_t *
|
|
||||||
function_expr (const expr_t *fexpr, const expr_t *params)
|
|
||||||
{
|
|
||||||
if (params) {
|
|
||||||
for (auto p = params->list.head; p; p = p->next) {
|
|
||||||
p->expr = convert_name (p->expr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fexpr->type == ex_symbol && fexpr->symbol->sy_type == sy_type) {
|
|
||||||
return constructor_expr (fexpr, params);
|
|
||||||
}
|
|
||||||
|
|
||||||
fexpr = find_function (fexpr, params);
|
|
||||||
fexpr = convert_name (fexpr);
|
|
||||||
if (is_error (fexpr)) {
|
|
||||||
return fexpr;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto ftype = get_type (fexpr);
|
|
||||||
if (ftype->type != ev_func) {
|
|
||||||
if (fexpr->type == ex_symbol)
|
|
||||||
return error (fexpr, "Called object \"%s\" is not a function",
|
|
||||||
fexpr->symbol->name);
|
|
||||||
else
|
|
||||||
return error (fexpr, "Called object is not a function");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fexpr->type == ex_symbol && params && is_string_val (params)) {
|
|
||||||
// FIXME eww, I hate this, but it's needed :(
|
|
||||||
// FIXME make a qc hook? :)
|
|
||||||
if (strncmp (fexpr->symbol->name, "precache_sound", 14) == 0)
|
|
||||||
PrecacheSound (expr_string (params), fexpr->symbol->name[14]);
|
|
||||||
else if (strncmp (fexpr->symbol->name, "precache_model", 14) == 0)
|
|
||||||
PrecacheModel (expr_string (params), fexpr->symbol->name[14]);
|
|
||||||
else if (strncmp (fexpr->symbol->name, "precache_file", 13) == 0)
|
|
||||||
PrecacheFile (expr_string (params), fexpr->symbol->name[13]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return build_function_call (fexpr, ftype, params);
|
|
||||||
}
|
|
||||||
|
|
||||||
const expr_t *
|
const expr_t *
|
||||||
branch_expr (int op, const expr_t *test, const expr_t *label)
|
branch_expr (int op, const expr_t *test, const expr_t *label)
|
||||||
{
|
{
|
||||||
|
@ -2334,7 +2055,7 @@ jump_table_expr (const expr_t *table, const expr_t *index)
|
||||||
}
|
}
|
||||||
|
|
||||||
const expr_t *
|
const expr_t *
|
||||||
call_expr (const expr_t *func, const expr_t *args, const type_t *ret_type)
|
new_call_expr (const expr_t *func, const expr_t *args, const type_t *ret_type)
|
||||||
{
|
{
|
||||||
expr_t *branch = new_expr ();
|
expr_t *branch = new_expr ();
|
||||||
branch->type = ex_branch;
|
branch->type = ex_branch;
|
||||||
|
@ -2345,117 +2066,6 @@ call_expr (const expr_t *func, const expr_t *args, const type_t *ret_type)
|
||||||
return branch;
|
return branch;
|
||||||
}
|
}
|
||||||
|
|
||||||
const expr_t *
|
|
||||||
return_expr (function_t *f, const expr_t *e)
|
|
||||||
{
|
|
||||||
const type_t *t;
|
|
||||||
const type_t *ret_type = unalias_type (f->type->func.ret_type);
|
|
||||||
|
|
||||||
e = convert_name (e);
|
|
||||||
if (!e) {
|
|
||||||
if (!is_void(ret_type)) {
|
|
||||||
if (options.traditional) {
|
|
||||||
if (options.warnings.traditional)
|
|
||||||
warning (e,
|
|
||||||
"return from non-void function without a value");
|
|
||||||
// force a nil return value in case qf code is being generated
|
|
||||||
e = new_nil_expr ();
|
|
||||||
} else {
|
|
||||||
e = error (e, "return from non-void function without a value");
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// the traditional check above may have set e
|
|
||||||
if (!e) {
|
|
||||||
return new_return_expr (0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (e->type == ex_compound || e->type == ex_multivec) {
|
|
||||||
scoped_src_loc (e);
|
|
||||||
e = initialized_temp_expr (ret_type, e);
|
|
||||||
} else {
|
|
||||||
e = algebra_optimize (e);
|
|
||||||
}
|
|
||||||
|
|
||||||
t = get_type (e);
|
|
||||||
|
|
||||||
if (!t) {
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
if (is_void(ret_type)) {
|
|
||||||
if (!options.traditional)
|
|
||||||
return error (e, "returning a value for a void function");
|
|
||||||
if (options.warnings.traditional)
|
|
||||||
warning (e, "returning a value for a void function");
|
|
||||||
}
|
|
||||||
if (e->type == ex_bool) {
|
|
||||||
e = convert_from_bool (e, ret_type);
|
|
||||||
}
|
|
||||||
if (is_reference (t) && !is_reference (ret_type)) {
|
|
||||||
e = pointer_deref (e);
|
|
||||||
t = dereference_type (t);
|
|
||||||
}
|
|
||||||
if (is_float(ret_type) && is_int_val (e)) {
|
|
||||||
e = cast_expr (&type_float, e);
|
|
||||||
t = &type_float;
|
|
||||||
}
|
|
||||||
if (is_void(t)) {
|
|
||||||
if (e->type == ex_nil) {
|
|
||||||
t = ret_type;
|
|
||||||
e = convert_nil (e, t);
|
|
||||||
} else {
|
|
||||||
if (!options.traditional)
|
|
||||||
return error (e, "void value not ignored as it ought to be");
|
|
||||||
if (options.warnings.traditional)
|
|
||||||
warning (e, "void value not ignored as it ought to be");
|
|
||||||
//FIXME does anything need to be done here?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!type_assignable (ret_type, t)) {
|
|
||||||
if (!options.traditional)
|
|
||||||
return error (e, "type mismatch for return value of %s: %s -> %s",
|
|
||||||
f->sym->name, get_type_string (t),
|
|
||||||
get_type_string (ret_type));
|
|
||||||
if (options.warnings.traditional)
|
|
||||||
warning (e, "type mismatch for return value of %s",
|
|
||||||
f->sym->name);
|
|
||||||
} else {
|
|
||||||
if (ret_type != t) {
|
|
||||||
e = cast_expr (ret_type, e);
|
|
||||||
t = f->sym->type->func.ret_type;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (e->type == ex_vector) {
|
|
||||||
e = assign_expr (new_temp_def_expr (t), e);
|
|
||||||
}
|
|
||||||
return new_return_expr (e);
|
|
||||||
}
|
|
||||||
|
|
||||||
const expr_t *
|
|
||||||
at_return_expr (function_t *f, const expr_t *e)
|
|
||||||
{
|
|
||||||
const type_t *ret_type = unalias_type (f->type->func.ret_type);
|
|
||||||
|
|
||||||
if (!is_void(ret_type)) {
|
|
||||||
return error (e, "use of @return in non-void function");
|
|
||||||
}
|
|
||||||
if (is_nil (e)) {
|
|
||||||
// int or pointer 0 seems reasonable
|
|
||||||
return new_return_expr (new_int_expr (0, false));
|
|
||||||
} else if (!is_function_call (e)) {
|
|
||||||
return error (e, "@return value not a function");
|
|
||||||
}
|
|
||||||
const expr_t *call_expr = e->block.result->branch.target;
|
|
||||||
const auto call_type = get_type (call_expr);
|
|
||||||
if (!is_func (call_type) && !call_type->func.void_return) {
|
|
||||||
return error (e, "@return function not void_return");
|
|
||||||
}
|
|
||||||
expr_t *ret_expr = new_return_expr (e);
|
|
||||||
ret_expr->retrn.at_return = 1;
|
|
||||||
return ret_expr;
|
|
||||||
}
|
|
||||||
|
|
||||||
const expr_t *
|
const expr_t *
|
||||||
conditional_expr (const expr_t *cond, const expr_t *e1, const expr_t *e2)
|
conditional_expr (const expr_t *cond, const expr_t *e1, const expr_t *e2)
|
||||||
{
|
{
|
||||||
|
|
430
tools/qfcc/source/expr_call.c
Normal file
430
tools/qfcc/source/expr_call.c
Normal file
|
@ -0,0 +1,430 @@
|
||||||
|
/*
|
||||||
|
expr_call.c
|
||||||
|
|
||||||
|
Function call expression construction and manipulations
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
#ifdef HAVE_STRING_H
|
||||||
|
# include <string.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_STRINGS_H
|
||||||
|
# include <strings.h>
|
||||||
|
#endif
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "tools/qfcc/include/algebra.h"
|
||||||
|
#include "tools/qfcc/include/diagnostic.h"
|
||||||
|
#include "tools/qfcc/include/expr.h"
|
||||||
|
#include "tools/qfcc/include/function.h"
|
||||||
|
#include "tools/qfcc/include/idstuff.h"
|
||||||
|
#include "tools/qfcc/include/options.h"
|
||||||
|
#include "tools/qfcc/include/symtab.h"
|
||||||
|
#include "tools/qfcc/include/target.h"
|
||||||
|
#include "tools/qfcc/include/type.h"
|
||||||
|
|
||||||
|
const expr_t *
|
||||||
|
build_function_call (const expr_t *fexpr, const type_t *ftype,
|
||||||
|
const expr_t *params)
|
||||||
|
{
|
||||||
|
int param_count = 0;
|
||||||
|
expr_t *call;
|
||||||
|
const expr_t *err = 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < arg_count; i++) {
|
||||||
|
auto e = arguments[i];
|
||||||
|
if (e->type == ex_error) {
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
if (e->type != ex_compound) {
|
||||||
|
arguments[i] = algebra_optimize (e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ftype->func.num_params < -1) {
|
||||||
|
if (options.code.max_params >= 0
|
||||||
|
&& arg_count > options.code.max_params) {
|
||||||
|
return error (fexpr, "more than %d parameters",
|
||||||
|
options.code.max_params);
|
||||||
|
}
|
||||||
|
if (-arg_count > ftype->func.num_params + 1) {
|
||||||
|
if (!options.traditional)
|
||||||
|
return error (fexpr, "too few arguments");
|
||||||
|
if (options.warnings.traditional)
|
||||||
|
warning (fexpr, "too few arguments");
|
||||||
|
}
|
||||||
|
param_count = -ftype->func.num_params - 1;
|
||||||
|
} else if (ftype->func.num_params >= 0) {
|
||||||
|
if (arg_count > ftype->func.num_params) {
|
||||||
|
return error (fexpr, "too many arguments");
|
||||||
|
} else if (arg_count < ftype->func.num_params) {
|
||||||
|
if (!options.traditional)
|
||||||
|
return error (fexpr, "too few arguments");
|
||||||
|
if (options.warnings.traditional)
|
||||||
|
warning (fexpr, "too few arguments");
|
||||||
|
}
|
||||||
|
param_count = ftype->func.num_params;
|
||||||
|
}
|
||||||
|
|
||||||
|
const type_t *arg_types[arg_count + 1];
|
||||||
|
// params is reversed (a, b, c) -> c, b, a
|
||||||
|
for (int i = 0; i < arg_count; i++) {
|
||||||
|
auto e = arguments[i];
|
||||||
|
const type_t *t;
|
||||||
|
|
||||||
|
if (e->type == ex_compound) {
|
||||||
|
if (i < param_count) {
|
||||||
|
t = ftype->func.param_types[i];
|
||||||
|
} else {
|
||||||
|
return error (e, "cannot pass compound initializer "
|
||||||
|
"through ...");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
t = get_type (e);
|
||||||
|
}
|
||||||
|
if (!t) {
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!type_size (t)) {
|
||||||
|
err = error (e, "type of formal parameter %d is incomplete",
|
||||||
|
i + 1);
|
||||||
|
}
|
||||||
|
if (!is_array (t) && value_too_large (t)) {
|
||||||
|
err = error (e, "formal parameter %d is too large to be passed by"
|
||||||
|
" value", i + 1);
|
||||||
|
}
|
||||||
|
if (i < param_count) {
|
||||||
|
auto param_type = ftype->func.param_types[i];
|
||||||
|
auto param_qual = ftype->func.param_quals[i];
|
||||||
|
|
||||||
|
if (is_reference (param_type) && param_qual != pq_in) {
|
||||||
|
internal_error (e, "qualified reference param (not yet)");
|
||||||
|
}
|
||||||
|
if (is_reference (param_type) && !is_reference (t)) {
|
||||||
|
if (!is_lvalue (e)) {
|
||||||
|
err = error (e, "cannot pass non-lvalue by reference");
|
||||||
|
}
|
||||||
|
if (!err && dereference_type (param_type) != t) {
|
||||||
|
err = reference_error (e, param_type, t);
|
||||||
|
}
|
||||||
|
} else if (!is_reference (param_type) && is_reference (t)) {
|
||||||
|
t = dereference_type (t);
|
||||||
|
}
|
||||||
|
if (e->type == ex_nil) {
|
||||||
|
t = param_type;
|
||||||
|
}
|
||||||
|
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) {
|
||||||
|
t = type_nil;
|
||||||
|
}
|
||||||
|
if (options.code.promote_float) {
|
||||||
|
if (is_scalar (get_type (e)) && is_float (get_type (e))) {
|
||||||
|
t = &type_double;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (is_scalar (get_type (e)) && is_double (get_type (e))) {
|
||||||
|
if (!e->implicit) {
|
||||||
|
warning (e, "passing double into ... function");
|
||||||
|
}
|
||||||
|
if (is_constant (e)) {
|
||||||
|
// don't auto-demote non-constant doubles
|
||||||
|
t = &type_float;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (current_target.vararg_int) {
|
||||||
|
current_target.vararg_int (e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
arg_types[i] = t;
|
||||||
|
}
|
||||||
|
if (err) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool emit_args = false;
|
||||||
|
if (ftype->func.num_params < 0) {
|
||||||
|
emit_args = !ftype->func.no_va_list;
|
||||||
|
}
|
||||||
|
scoped_src_loc (fexpr);
|
||||||
|
call = new_block_expr (0);
|
||||||
|
call->block.is_call = 1;
|
||||||
|
int arg_expr_count = 0;
|
||||||
|
const expr_t *arg_exprs[arg_count + 1][2];
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto e = arguments[i];
|
||||||
|
if (e->type == ex_compound || e->type == ex_multivec) {
|
||||||
|
scoped_src_loc (e);
|
||||||
|
e = initialized_temp_expr (arg_types[i], e);
|
||||||
|
}
|
||||||
|
// FIXME this is target-specific info and should not be in the
|
||||||
|
// expression tree
|
||||||
|
// That, or always use a temp, since it should get optimized out
|
||||||
|
if (has_function_call (e)) {
|
||||||
|
scoped_src_loc (e);
|
||||||
|
auto cast = cast_expr (arg_types[i], e);
|
||||||
|
auto tmp = new_temp_def_expr (arg_types[i]);
|
||||||
|
expr_prepend_expr (args, tmp);
|
||||||
|
|
||||||
|
arg_exprs[arg_expr_count][0] = cast;
|
||||||
|
arg_exprs[arg_expr_count][1] = tmp;
|
||||||
|
arg_expr_count++;
|
||||||
|
} else {
|
||||||
|
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 {
|
||||||
|
if (is_reference (arg_types[i])) {
|
||||||
|
if (is_reference (get_type (e))) {
|
||||||
|
// just copy the param, so no op
|
||||||
|
} else {
|
||||||
|
e = address_expr (e, nullptr);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (is_reference (get_type (e))) {
|
||||||
|
e = pointer_deref (e);
|
||||||
|
}
|
||||||
|
e = cast_expr (arg_types[i], e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expr_prepend_expr (args, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (emit_args) {
|
||||||
|
emit_args = false;
|
||||||
|
expr_prepend_expr (args, new_args_expr ());
|
||||||
|
}
|
||||||
|
for (int i = 0; i < arg_expr_count; i++) {
|
||||||
|
scoped_src_loc (arg_exprs[i][0]);
|
||||||
|
auto assign = assign_expr (arg_exprs[i][1], arg_exprs[i][0]);
|
||||||
|
append_expr (call, assign);
|
||||||
|
}
|
||||||
|
auto ret_type = ftype->func.ret_type;
|
||||||
|
call->block.result = new_call_expr (fexpr, args, ret_type);
|
||||||
|
return call;
|
||||||
|
}
|
||||||
|
|
||||||
|
const expr_t *
|
||||||
|
function_expr (const expr_t *fexpr, const expr_t *params)
|
||||||
|
{
|
||||||
|
if (params) {
|
||||||
|
for (auto p = params->list.head; p; p = p->next) {
|
||||||
|
p->expr = convert_name (p->expr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fexpr->type == ex_symbol && fexpr->symbol->sy_type == sy_type) {
|
||||||
|
return constructor_expr (fexpr, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
fexpr = find_function (fexpr, params);
|
||||||
|
fexpr = convert_name (fexpr);
|
||||||
|
if (is_error (fexpr)) {
|
||||||
|
return fexpr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ftype = get_type (fexpr);
|
||||||
|
if (ftype->type != ev_func) {
|
||||||
|
if (fexpr->type == ex_symbol)
|
||||||
|
return error (fexpr, "Called object \"%s\" is not a function",
|
||||||
|
fexpr->symbol->name);
|
||||||
|
else
|
||||||
|
return error (fexpr, "Called object is not a function");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fexpr->type == ex_symbol && params && is_string_val (params)) {
|
||||||
|
// FIXME eww, I hate this, but it's needed :(
|
||||||
|
// FIXME make a qc hook? :)
|
||||||
|
if (strncmp (fexpr->symbol->name, "precache_sound", 14) == 0)
|
||||||
|
PrecacheSound (expr_string (params), fexpr->symbol->name[14]);
|
||||||
|
else if (strncmp (fexpr->symbol->name, "precache_model", 14) == 0)
|
||||||
|
PrecacheModel (expr_string (params), fexpr->symbol->name[14]);
|
||||||
|
else if (strncmp (fexpr->symbol->name, "precache_file", 13) == 0)
|
||||||
|
PrecacheFile (expr_string (params), fexpr->symbol->name[13]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return build_function_call (fexpr, ftype, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
const expr_t *
|
||||||
|
return_expr (function_t *f, const expr_t *e)
|
||||||
|
{
|
||||||
|
const type_t *t;
|
||||||
|
const type_t *ret_type = unalias_type (f->type->func.ret_type);
|
||||||
|
|
||||||
|
e = convert_name (e);
|
||||||
|
if (!e) {
|
||||||
|
if (!is_void(ret_type)) {
|
||||||
|
if (options.traditional) {
|
||||||
|
if (options.warnings.traditional)
|
||||||
|
warning (e,
|
||||||
|
"return from non-void function without a value");
|
||||||
|
// force a nil return value in case qf code is being generated
|
||||||
|
e = new_nil_expr ();
|
||||||
|
} else {
|
||||||
|
e = error (e, "return from non-void function without a value");
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// the traditional check above may have set e
|
||||||
|
if (!e) {
|
||||||
|
return new_return_expr (0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e->type == ex_compound || e->type == ex_multivec) {
|
||||||
|
scoped_src_loc (e);
|
||||||
|
e = initialized_temp_expr (ret_type, e);
|
||||||
|
} else {
|
||||||
|
e = algebra_optimize (e);
|
||||||
|
}
|
||||||
|
|
||||||
|
t = get_type (e);
|
||||||
|
|
||||||
|
if (!t) {
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
if (is_void(ret_type)) {
|
||||||
|
if (!options.traditional)
|
||||||
|
return error (e, "returning a value for a void function");
|
||||||
|
if (options.warnings.traditional)
|
||||||
|
warning (e, "returning a value for a void function");
|
||||||
|
}
|
||||||
|
if (e->type == ex_bool) {
|
||||||
|
e = convert_from_bool (e, ret_type);
|
||||||
|
}
|
||||||
|
if (is_reference (t) && !is_reference (ret_type)) {
|
||||||
|
e = pointer_deref (e);
|
||||||
|
t = dereference_type (t);
|
||||||
|
}
|
||||||
|
if (is_float(ret_type) && is_int_val (e)) {
|
||||||
|
e = cast_expr (&type_float, e);
|
||||||
|
t = &type_float;
|
||||||
|
}
|
||||||
|
if (is_void(t)) {
|
||||||
|
if (e->type == ex_nil) {
|
||||||
|
t = ret_type;
|
||||||
|
e = convert_nil (e, t);
|
||||||
|
} else {
|
||||||
|
if (!options.traditional)
|
||||||
|
return error (e, "void value not ignored as it ought to be");
|
||||||
|
if (options.warnings.traditional)
|
||||||
|
warning (e, "void value not ignored as it ought to be");
|
||||||
|
//FIXME does anything need to be done here?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!type_assignable (ret_type, t)) {
|
||||||
|
if (!options.traditional)
|
||||||
|
return error (e, "type mismatch for return value of %s: %s -> %s",
|
||||||
|
f->sym->name, get_type_string (t),
|
||||||
|
get_type_string (ret_type));
|
||||||
|
if (options.warnings.traditional)
|
||||||
|
warning (e, "type mismatch for return value of %s",
|
||||||
|
f->sym->name);
|
||||||
|
} else {
|
||||||
|
if (ret_type != t) {
|
||||||
|
e = cast_expr (ret_type, e);
|
||||||
|
t = f->sym->type->func.ret_type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (e->type == ex_vector) {
|
||||||
|
e = assign_expr (new_temp_def_expr (t), e);
|
||||||
|
}
|
||||||
|
return new_return_expr (e);
|
||||||
|
}
|
||||||
|
|
||||||
|
const expr_t *
|
||||||
|
at_return_expr (function_t *f, const expr_t *e)
|
||||||
|
{
|
||||||
|
const type_t *ret_type = unalias_type (f->type->func.ret_type);
|
||||||
|
|
||||||
|
if (!is_void(ret_type)) {
|
||||||
|
return error (e, "use of @return in non-void function");
|
||||||
|
}
|
||||||
|
if (is_nil (e)) {
|
||||||
|
// int or pointer 0 seems reasonable
|
||||||
|
return new_return_expr (new_int_expr (0, false));
|
||||||
|
} else if (!is_function_call (e)) {
|
||||||
|
return error (e, "@return value not a function");
|
||||||
|
}
|
||||||
|
const expr_t *call_expr = e->block.result->branch.target;
|
||||||
|
const auto call_type = get_type (call_expr);
|
||||||
|
if (!is_func (call_type) && !call_type->func.void_return) {
|
||||||
|
return error (e, "@return function not void_return");
|
||||||
|
}
|
||||||
|
expr_t *ret_expr = new_return_expr (e);
|
||||||
|
ret_expr->retrn.at_return = 1;
|
||||||
|
return ret_expr;
|
||||||
|
}
|
|
@ -361,7 +361,7 @@ function_call_or_method
|
||||||
auto func = exprs[count - 1];
|
auto func = exprs[count - 1];
|
||||||
auto args = new_list_expr (nullptr);
|
auto args = new_list_expr (nullptr);
|
||||||
list_gather (&args->list, exprs, count - 1);
|
list_gather (&args->list, exprs, count - 1);
|
||||||
$$ = call_expr (func, args, nullptr);
|
$$ = new_call_expr (func, args, nullptr);
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
|
@ -60,6 +60,7 @@
|
||||||
#include "tools/qfcc/include/strpool.h"
|
#include "tools/qfcc/include/strpool.h"
|
||||||
#include "tools/qfcc/include/struct.h"
|
#include "tools/qfcc/include/struct.h"
|
||||||
#include "tools/qfcc/include/symtab.h"
|
#include "tools/qfcc/include/symtab.h"
|
||||||
|
#include "tools/qfcc/include/target.h"
|
||||||
#include "tools/qfcc/include/type.h"
|
#include "tools/qfcc/include/type.h"
|
||||||
#include "tools/qfcc/include/value.h"
|
#include "tools/qfcc/include/value.h"
|
||||||
|
|
||||||
|
@ -743,7 +744,9 @@ method_check_params (method_t *method, const expr_t *args)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
vararg_integer (e);
|
if (current_target.vararg_int) {
|
||||||
|
current_target.vararg_int (e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return err;
|
return err;
|
||||||
|
|
|
@ -131,11 +131,20 @@ v6p_build_code (function_t *func, const expr_t *statements)
|
||||||
func->locals->space = space;
|
func->locals->space = space;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
vararg_int (const expr_t *e)
|
||||||
|
{
|
||||||
|
if (is_int_val (e) && options.warnings.vararg_integer) {
|
||||||
|
warning (e, "passing int constant into ... function");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
target_t v6_target = {
|
target_t v6_target = {
|
||||||
.value_too_large = v6_value_too_large,
|
.value_too_large = v6_value_too_large,
|
||||||
.build_scope = v6p_build_scope,
|
.build_scope = v6p_build_scope,
|
||||||
.build_code = v6p_build_code,
|
.build_code = v6p_build_code,
|
||||||
.declare_sym = declare_def,
|
.declare_sym = declare_def,
|
||||||
|
.vararg_int = vararg_int,
|
||||||
};
|
};
|
||||||
|
|
||||||
target_t v6p_target = {
|
target_t v6p_target = {
|
||||||
|
@ -143,4 +152,5 @@ target_t v6p_target = {
|
||||||
.build_scope = v6p_build_scope,
|
.build_scope = v6p_build_scope,
|
||||||
.build_code = v6p_build_code,
|
.build_code = v6p_build_code,
|
||||||
.declare_sym = declare_def,
|
.declare_sym = declare_def,
|
||||||
|
.vararg_int = vararg_int,
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue