diff --git a/tools/qfcc/include/expr.h b/tools/qfcc/include/expr.h index 0f5f2624d..313eb96fb 100644 --- a/tools/qfcc/include/expr.h +++ b/tools/qfcc/include/expr.h @@ -402,6 +402,10 @@ typedef struct { bool lvalue; ///< rvalue if false } ex_xvalue_t; +typedef struct { + const expr_t *expr; +} ex_process_t; + typedef struct expr_s { expr_t *next; rua_loc_t loc; ///< source location of expression @@ -455,6 +459,7 @@ typedef struct expr_s { ex_switch_t switchblock; ///< switch block expression ex_caselabel_t caselabel; ///< case label expression ex_xvalue_t xvalue; ///< lvalue/rvalue specific expression + ex_process_t process; ///< expression than needs processing }; } expr_t; @@ -1022,6 +1027,7 @@ expr_t *new_switch_expr (const expr_t *test, const expr_t *body, const expr_t *break_label); expr_t *new_caselabel_expr (const expr_t *value, const expr_t *end_value); expr_t *new_xvalue_expr (const expr_t *expr, bool lvalue); +expr_t *new_process_expr (const expr_t *expr); /** Create an expression of the correct type that references the specified parameter slot. @@ -1147,6 +1153,7 @@ const expr_t *gather_factors (const type_t *type, int op, typedef struct rua_ctx_s rua_ctx_t; const expr_t *expr_process (const expr_t *expr, rua_ctx_t *ctx); +bool can_inline (const expr_t *expr, symbol_t *fsym); ///@} diff --git a/tools/qfcc/include/expr_names.h b/tools/qfcc/include/expr_names.h index 09bb6acdd..c19f3e79b 100644 --- a/tools/qfcc/include/expr_names.h +++ b/tools/qfcc/include/expr_names.h @@ -81,6 +81,7 @@ EX_EXPR(intrinsic) ///< intrinsic instruction expression (::ex_intrinsic_t) EX_EXPR(switch) ///< switch expression (::ex_switch_t) EX_EXPR(caselabel) ///< case expression (::ex_caselabel_t) EX_EXPR(xvalue) ///< xvalue expression (::ex_xvalue_t) +EX_EXPR(process) ///< expression that needs processing (::ex_process_t) #undef EX_EXPR diff --git a/tools/qfcc/include/function.h b/tools/qfcc/include/function.h index 38e624d24..ac91860a4 100644 --- a/tools/qfcc/include/function.h +++ b/tools/qfcc/include/function.h @@ -77,6 +77,7 @@ typedef struct genfunc_s { genparam_t *params; genparam_t *ret_type; const expr_t *expr; ///< inline or intrinsic + bool can_inline; } genfunc_t; typedef enum { @@ -169,7 +170,9 @@ typedef struct metafunc_s { rua_loc_t loc; ///< source location of the function mf_type_e meta_type; ///< is this function overloaded function_t *func; + genfunc_t *genfunc; const expr_t *expr; ///< inline or intrinsic + bool can_inline; } metafunc_t; extern function_t *current_func; diff --git a/tools/qfcc/source/expr.c b/tools/qfcc/source/expr.c index 33f31c015..d7abd9681 100644 --- a/tools/qfcc/source/expr.c +++ b/tools/qfcc/source/expr.c @@ -100,6 +100,7 @@ get_type (const expr_t *e) case ex_loop: case ex_select: case ex_message: + case ex_process: internal_error (e, "unexpected expression type: %s", expr_names[e->type]); case ex_label: @@ -1997,6 +1998,8 @@ has_function_call (const expr_t *e) case ex_xvalue: bug (e, "should xvalue happen here?"); return has_function_call (e->xvalue.expr); + case ex_process: + internal_error (e, "unexpected expression type"); case ex_count: break; } @@ -2322,6 +2325,17 @@ new_xvalue_expr (const expr_t *expr, bool lvalue) return xv; } +expr_t * +new_process_expr (const expr_t *expr) +{ + auto xv = new_expr (); + xv->type = ex_process; + xv->process = (ex_process_t) { + .expr = expr, + }; + return xv; +} + expr_t * new_decl (symbol_t *sym, const expr_t *init) { @@ -2855,3 +2869,161 @@ sizeof_expr (const expr_t *expr, const type_t *type) } return expr; } + +static bool +can_inline_list (const ex_list_t *list, symbol_t *fsym) +{ + int count = list_count (list); + const expr_t *arg_exprs[count + 1] = {}; + list_scatter (list, arg_exprs); + for (int i = 0; i < count; i++) { + if (!can_inline (arg_exprs[i], fsym)) { + return false; + } + } + return true; +} + +bool +can_inline (const expr_t *expr, symbol_t *fsym) +{ + if (!expr) { + return true; + } + switch (expr->type) { + case ex_block: + return can_inline_list (&expr->block.list, fsym); + case ex_expr: + if (expr->expr.op == QC_AND || expr->expr.op == QC_OR) { + //FIXME + return false; + } + return (can_inline (expr->expr.e1, fsym) + && can_inline (expr->expr.e2, fsym)); + case ex_uexpr: + return can_inline (expr->expr.e1, fsym); + case ex_symbol: + auto sym = expr->symbol; + if (sym->table == fsym->table + && strcmp (sym->name, fsym->name) == 0) { + // recursive function, however to get here, no conditional + // expresions were found + warning (expr, "infinite recursion in %s", fsym->name); + return false; + } + return true; + case ex_vector: + return can_inline_list (&expr->vector.list, fsym); + case ex_selector: + return can_inline (expr->selector.sel_ref, fsym); + case ex_message: + if (!can_inline (expr->message.receiver, fsym)) { + return false; + } + for (auto k = expr->message.message; k; k = k->next) { + if (!can_inline (k->expr, fsym)) { + return false; + } + } + return true; + case ex_compound: + for (auto ele = expr->compound.head; ele; ele = ele->next) { + if (!can_inline (ele->expr, fsym)) { + return false; + } + } + return true; + case ex_memset: + return (can_inline (expr->memset.dst, fsym) + && can_inline (expr->memset.val, fsym) + && can_inline (expr->memset.count, fsym)); + case ex_alias: + return (can_inline (expr->alias.expr, fsym) + && can_inline (expr->alias.offset, fsym)); + case ex_address: + return (can_inline (expr->address.lvalue, fsym) + && can_inline (expr->address.offset, fsym)); + case ex_assign: + return (can_inline (expr->assign.dst, fsym) + && can_inline (expr->assign.src, fsym)); + case ex_branch: + if (expr->branch.type != pr_branch_call) { + notice (expr, "%s", expr_names[expr->type]); + return false; + } + if (!can_inline (expr->branch.target, fsym)) { + notice (expr, "%s", expr_names[expr->type]); + return false; + } + auto args = (expr_t *) expr->branch.args; + if (!args) { + return true; + } + return can_inline_list (&args->list, fsym); + case ex_return: + if (expr->retrn.ret_val) { + return can_inline (expr->retrn.ret_val, fsym); + } + return true; + case ex_horizontal: + return can_inline (expr->hop.vec, fsym); + case ex_swizzle: + return can_inline (expr->swizzle.src, fsym); + case ex_extend: + return can_inline (expr->extend.src, fsym); + case ex_incop: + return can_inline (expr->incop.expr, fsym); + case ex_list: + return can_inline_list (&expr->list, fsym); + case ex_cond: + return (can_inline (expr->cond.test, fsym) + && can_inline (expr->cond.true_expr, fsym) + && can_inline (expr->cond.false_expr, fsym)); + case ex_field: + return (can_inline (expr->field.object, fsym) + && can_inline (expr->field.member, fsym)); + case ex_array: + return (can_inline (expr->array.base, fsym) + && can_inline (expr->array.index, fsym)); + case ex_decl: + return can_inline_list (&expr->decl.list, fsym); + case ex_xvalue: + auto xvalue = expr->xvalue.expr; + if (xvalue->type == ex_symbol + && xvalue->symbol->sy_type == sy_xvalue) { + if (expr->xvalue.lvalue) { + xvalue = xvalue->symbol->xvalue.lvalue; + } else { + xvalue = xvalue->symbol->xvalue.rvalue; + } + } + return can_inline (xvalue, fsym); + case ex_def: + case ex_temp: + case ex_nil: + case ex_value: + case ex_type: + case ex_adjstk: + case ex_with: + case ex_args: + return true; + case ex_loop://FIXME + case ex_state://FIXME + case ex_bool://FIXME + case ex_label://FIXME + case ex_labelref://FIXME + case ex_select://FIXME + case ex_switch: + return false; + case ex_error: + case ex_inout: + case ex_caselabel: + case ex_multivec: + case ex_intrinsic: + case ex_process: + case ex_count: + internal_error (expr, "unexpected expr %s", + expr_names[expr->type]); + } + internal_error (expr, "invald expr type %d", expr->type); +} diff --git a/tools/qfcc/source/expr_assign.c b/tools/qfcc/source/expr_assign.c index e52f7dff1..84614e86f 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_intrinsic: case ex_switch: case ex_caselabel: + case ex_process: break; case ex_cond: return (is_lvalue (expr->cond.true_expr) diff --git a/tools/qfcc/source/expr_call.c b/tools/qfcc/source/expr_call.c index c449cfc20..d26b1e67c 100644 --- a/tools/qfcc/source/expr_call.c +++ b/tools/qfcc/source/expr_call.c @@ -37,11 +37,13 @@ #include #include "tools/qfcc/include/algebra.h" +#include "tools/qfcc/include/defspace.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/shared.h" #include "tools/qfcc/include/symtab.h" #include "tools/qfcc/include/target.h" #include "tools/qfcc/include/type.h" @@ -203,6 +205,84 @@ check_arg_types (const expr_t **arguments, const type_t **arg_types, return err; } +static expr_t * +build_intrinsic_call (const expr_t *expr, const type_t *ftype, + const expr_t **arguments, int arg_count) +{ + for (int i = 0; i < arg_count; i++) { + if (is_reference (get_type (arguments[i]))) { + arguments[i] = pointer_deref (arguments[i]); + } + } + auto call = new_intrinsic_expr (nullptr); + call->intrinsic.opcode = expr->intrinsic.opcode; + call->intrinsic.res_type = ftype->func.ret_type; + list_append_list (&call->intrinsic.operands, + &expr->intrinsic.operands); + list_gather (&call->intrinsic.operands, arguments, arg_count); + return call; +} + +static expr_t * +build_inline_call (symbol_t *fsym, const type_t *ftype, + const expr_t **arguments, int arg_count) +{ + auto metafunc = fsym->metafunc; + auto func = metafunc->func; + + auto params = func->parameters; + auto locals = func->locals; + + for (auto p = fsym->params; p; p = p->next) { + if (!p->selector && !p->type && !p->name) { + internal_error (0, "inline variadic not implemented"); + } + 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) { + notice (0, "parameter name omitted"); + continue; + } + auto param = new_symbol_type (p->name, p->type); + symtab_addsymbol (params, param); + } + + auto call = new_block_expr (nullptr); + call->block.scope = locals; + + if (!is_void (ftype->func.ret_type)) { + auto spec = (specifier_t) { + .type = ftype->func.ret_type, + .storage = sc_local, + }; + auto decl = new_decl_expr (spec, locals); + auto ret = new_symbol (".ret"); + append_decl (decl, ret, nullptr); + append_expr (call, decl); + call->block.result = new_symbol_expr (ret); + } + auto expr = metafunc->expr; + if (expr->type == ex_block) { + expr->block.scope->parent = locals; + } + append_expr (call, expr); + + auto proc = new_process_expr (call); + + return proc; +} + static expr_t * build_args (const expr_t *(*arg_exprs)[2], int *arg_expr_count, const expr_t **arguments, const type_t **arg_types, @@ -301,26 +381,22 @@ build_function_call (const expr_t *fexpr, const type_t *ftype, return err; } - expr_t *call = nullptr; scoped_src_loc (fexpr); if (fexpr->type == ex_symbol && fexpr->symbol->sy_type == sy_func && fexpr->symbol->metafunc->expr) { - auto expr = fexpr->symbol->metafunc->expr; + auto fsym = fexpr->symbol; + auto metafunc = fsym->metafunc; + auto expr = metafunc->expr; if (expr->type == ex_intrinsic) { - for (int i = 0; i < arg_count; i++) { - if (is_reference (get_type (arguments[i]))) { - arguments[i] = pointer_deref (arguments[i]); - } - } - call = new_intrinsic_expr (nullptr); - call->intrinsic.opcode = expr->intrinsic.opcode; - call->intrinsic.res_type = ftype->func.ret_type; - list_append_list (&call->intrinsic.operands, - &expr->intrinsic.operands); - list_gather (&call->intrinsic.operands, arguments, arg_count); + return build_intrinsic_call (expr, ftype, arguments, arg_count); } + if (metafunc->can_inline) { + return build_inline_call (fsym, ftype, arguments, arg_count); + } + internal_error (fexpr, "calls to inline functions that cannot be " + "inlined not implemented"); } else { - call = new_block_expr (0); + auto call = new_block_expr (nullptr); call->block.is_call = 1; int num_args = 0; const expr_t *arg_exprs[arg_count + 1][2]; @@ -333,8 +409,8 @@ build_function_call (const expr_t *fexpr, const type_t *ftype, } auto ret_type = ftype->func.ret_type; call->block.result = new_call_expr (fexpr, arg_list, ret_type); + return call; } - return call; } const expr_t * diff --git a/tools/qfcc/source/expr_dag.c b/tools/qfcc/source/expr_dag.c index 26e136317..6acffde49 100644 --- a/tools/qfcc/source/expr_dag.c +++ b/tools/qfcc/source/expr_dag.c @@ -86,6 +86,7 @@ edag_add_expr (const expr_t *expr) case ex_intrinsic: case ex_switch: case ex_caselabel: + case ex_process: // these are never put in the dag return expr; case ex_list: diff --git a/tools/qfcc/source/expr_process.c b/tools/qfcc/source/expr_process.c index 8215067bc..a2658e53b 100644 --- a/tools/qfcc/source/expr_process.c +++ b/tools/qfcc/source/expr_process.c @@ -249,11 +249,16 @@ proc_block (const expr_t *expr, rua_ctx_t *ctx) const expr_t *result = nullptr; const expr_t *in[count + 1]; const expr_t *out[count + 1]; + const expr_t *err = nullptr; list_scatter (&expr->block.list, in); for (int i = 0; i < count; i++) { edag_flush (); auto e = expr_process (in[i], ctx); - if (e && !is_error (e)) { + if (is_error (e)) { + err = e; + continue; + } + if (e) { out[num_out++] = e; if (expr->block.result == in[i]) { result = e; @@ -261,6 +266,10 @@ proc_block (const expr_t *expr, rua_ctx_t *ctx) } } edag_flush (); + if (err) { + current_symtab = old_scope; + return err; + } scoped_src_loc (expr); auto block = new_block_expr (nullptr); @@ -706,5 +715,9 @@ expr_process (const expr_t *expr, rua_ctx_t *ctx) expr_names[expr->type]); } - return funcs[expr->type] (expr, ctx); + auto proc = funcs[expr->type] (expr, ctx); + if (proc && proc->type == ex_process) { + proc = expr_process (proc->process.expr, ctx); + } + return proc; } diff --git a/tools/qfcc/source/expr_type.c b/tools/qfcc/source/expr_type.c index 6eaf4978e..0f2156021 100644 --- a/tools/qfcc/source/expr_type.c +++ b/tools/qfcc/source/expr_type.c @@ -41,6 +41,7 @@ #include "tools/qfcc/include/function.h" #include "tools/qfcc/include/opcodes.h" #include "tools/qfcc/include/rua-lang.h" +#include "tools/qfcc/include/shared.h" #include "tools/qfcc/include/statements.h" #include "tools/qfcc/include/symtab.h" #include "tools/qfcc/include/type.h" @@ -706,6 +707,17 @@ type_parameter (symbol_t *sym, const expr_t *type) const type_t * resolve_type (const expr_t *te) { + if (te->type == ex_symbol) { + auto sym = te->symbol; + if (sym->type == sy_name) { + sym = symtab_lookup (current_symtab, sym->name); + if (sym && sym->sy_type == sy_type_param) { + te = sym->expr; + } + } else if (sym->sy_type == sy_type) { + return sym->type; + } + } if (te->type != ex_type) { internal_error (te, "not a type expression"); } diff --git a/tools/qfcc/source/function.c b/tools/qfcc/source/function.c index 48f9c167f..25716a456 100644 --- a/tools/qfcc/source/function.c +++ b/tools/qfcc/source/function.c @@ -58,6 +58,7 @@ #include "tools/qfcc/include/expr.h" #include "tools/qfcc/include/flow.h" #include "tools/qfcc/include/function.h" +#include "tools/qfcc/include/method.h" #include "tools/qfcc/include/opcodes.h" #include "tools/qfcc/include/options.h" #include "tools/qfcc/include/reloc.h" @@ -682,11 +683,11 @@ find_generic_function (genfunc_t **genfuncs, const expr_t *fexpr, } static symbol_t * -create_generic_sym (genfunc_t *g, const expr_t *fexpr, calltype_t *calltype) +create_generic_sym (genfunc_t *g, const expr_t *fexpr, calltype_t *calltype, + const type_t **types) { int num_params = calltype->num_params; auto call_params = calltype->params; - const type_t *types[g->num_types] = {}; const type_t *param_types[num_params]; param_qual_t param_quals[num_params]; const type_t *return_type; @@ -746,6 +747,7 @@ create_generic_sym (genfunc_t *g, const expr_t *fexpr, calltype_t *calltype) sym->metafunc = new_metafunc (); *sym->metafunc = *fsym->metafunc; sym->metafunc->expr = g->expr; + sym->metafunc->can_inline = g->can_inline; symtab_addsymbol (fsym->table, sym); } return sym; @@ -789,7 +791,8 @@ get_function (const char *name, specifier_t spec) } auto gen = find_generic_function (genfuncs, &fexpr, &calltype, false); if (gen) { - auto sym = create_generic_sym (gen, &fexpr, &calltype); + const type_t *ref_types[gen->num_types] = {}; + auto sym = create_generic_sym (gen, &fexpr, &calltype, ref_types); if (sym == fexpr.symbol || sym->metafunc == fexpr.symbol->metafunc) { internal_error (0, "genfunc oops"); @@ -859,6 +862,7 @@ function_symbol (specifier_t spec) .full_name = name, .loc = pr.loc, .meta_type = mf_generic, + .genfunc = genfunc, }; Hash_Add (metafuncs, func); Hash_Add (function_map, func); @@ -895,6 +899,46 @@ set_func_symbol (const expr_t *fexpr, metafunc_t *f) return nf; } +static void +build_generic_scope (symbol_t *fsym, symtab_t *parent, genfunc_t *genfunc, + const type_t **ref_types) +{ + auto func = fsym->metafunc->func; + func->label_scope = new_symtab (0, stab_label); + + auto parameters = new_symtab (parent, stab_param); + parameters->space = parent->space; + func->parameters = parameters; + + auto locals = new_symtab (parameters, stab_local); + locals->space = parent->space; + func->locals = locals; + + for (int i = 0; i < genfunc->num_types; i++) { + auto type = &genfunc->types[i]; + auto sym = new_symbol (type->name); + sym->sy_type = sy_type_param; + if (ref_types) { + sym->expr = new_type_expr (ref_types[i]); + } + symtab_addsymbol (func->parameters, sym); + } +} + +static function_t * +new_function (const char *name, const char *nice_name) +{ + function_t *f; + + ALLOC (1024, function_t, functions, f); + f->o_name = save_string (name); + f->s_name = ReuseString (name); + f->s_file = pr.loc.file; + if (!(f->name = nice_name)) + f->name = name; + return f; +} + const expr_t * find_function (const expr_t *fexpr, const expr_t *params) { @@ -932,7 +976,14 @@ find_function (const expr_t *fexpr, const expr_t *params) if (!gen) { return new_error_expr (); } - auto sym = create_generic_sym (gen, fexpr, &calltype); + const type_t *ref_types[gen->num_types] = {}; + auto sym = create_generic_sym (gen, fexpr, &calltype, ref_types); + if (gen->can_inline) { + // the call will be inlined, so a new scope is needed every + // time + sym->metafunc->func = new_function (sym->name, gen->name); + build_generic_scope (sym, current_symtab, gen, ref_types); + } return new_symbol_expr (sym); } @@ -1069,20 +1120,6 @@ build_scope (symbol_t *fsym, symtab_t *parent) current_target.build_scope (fsym); } -static function_t * -new_function (const char *name, const char *nice_name) -{ - function_t *f; - - ALLOC (1024, function_t, functions, f); - f->o_name = save_string (name); - f->s_name = ReuseString (name); - f->s_file = pr.loc.file; - if (!(f->name = nice_name)) - f->name = name; - return f; -} - function_t * make_function (symbol_t *sym, const char *nice_name, defspace_t *space, storage_class_t storage) @@ -1142,6 +1179,15 @@ begin_function (specifier_t spec, const char *nicename, symtab_t *parent) }); } + if (spec.is_generic) { + auto genfunc = sym->metafunc->genfunc; + func = new_function (sym->name, nicename); + sym->metafunc->func = func; + sym->metafunc->genfunc = genfunc; + build_generic_scope (sym, parent, genfunc, nullptr); + return func; + } + defspace_t *space = spec.is_far ? pr.far_data : sym->table->space; func = make_function (sym, nicename, space, spec.storage); @@ -1183,6 +1229,16 @@ build_code_function (specifier_t spec, const expr_t *state_expr, expr_t *statements, rua_ctx_t *ctx) { auto fsym = spec.sym; + if (fsym->metafunc->meta_type == mf_generic) { + auto genfunc = fsym->metafunc->genfunc; + if (genfunc->expr) { + error (statements, "%s already defined", fsym->name); + return; + } + genfunc->expr = statements; + genfunc->can_inline = can_inline (statements, fsym); + return; + } if (ctx) { statements = (expr_t *) expr_process (statements, ctx); } @@ -1271,9 +1327,7 @@ build_intrinsic_function (specifier_t spec, const expr_t *intrinsic) return; } if (sym->metafunc->meta_type == mf_generic) { - //FIXME find a better way to find the specific genfunc - auto genfunc = parse_generic_function (sym->name, spec); - genfunc = add_generic_function (genfunc); + auto genfunc = sym->metafunc->genfunc; if (genfunc->expr) { error (intrinsic, "%s already defined", sym->name); return;