From 36cf1f948e883721e39881e5581dac022a3e8318 Mon Sep 17 00:00:00 2001 From: Bill Currie Date: Wed, 11 Dec 2024 03:06:50 +0900 Subject: [PATCH] [qfcc] Add bypass scopes Because the symbol tables for generic functions are ephemeral (as such), they need to be easily removed from the scope chain, it's easiest if definitions are never added to them (instead, they get added to the parent symbol table). This keeps handling of function declarations or definitions and their parameter scopes simple as the function gets put in the global scope still, and the parameter scope simply gets reconnected to the global scope (really, the generic scope's parent) when the parameter scope is popped within a generic scope. --- tools/qfcc/include/function.h | 14 ++- tools/qfcc/include/rua-lang.h | 1 + tools/qfcc/include/specifier.h | 1 + tools/qfcc/include/symtab.h | 1 + tools/qfcc/source/function.c | 80 +++++++++-------- tools/qfcc/source/glsl-builtins.c | 6 +- tools/qfcc/source/glsl-parse.y | 9 +- tools/qfcc/source/qc-parse.y | 137 +++++++++++++++++++----------- tools/qfcc/source/qp-parse.y | 43 ++++++---- tools/qfcc/source/symtab.c | 6 ++ 10 files changed, 179 insertions(+), 119 deletions(-) diff --git a/tools/qfcc/include/function.h b/tools/qfcc/include/function.h index 24f87cd19..38e624d24 100644 --- a/tools/qfcc/include/function.h +++ b/tools/qfcc/include/function.h @@ -212,14 +212,12 @@ function_t *make_function (symbol_t *sym, const char *nice_name, enum storage_class_e storage); symbol_t *function_symbol (specifier_t spec); const expr_t *find_function (const expr_t *fexpr, const expr_t *params); -function_t *begin_function (symbol_t *sym, const char *nicename, - symtab_t *parent, int far, - enum storage_class_e storage); -function_t *build_code_function (symbol_t *fsym, const expr_t *state_expr, - expr_t *statements, rua_ctx_t *ctx); -function_t *build_builtin_function (symbol_t *sym, const char *ext_name, - const expr_t *bi_val, int far, - enum storage_class_e storage); +function_t *begin_function (specifier_t spec, const char *nicename, + symtab_t *parent); +void build_code_function (specifier_t spec, const expr_t *state_expr, + expr_t *statements, rua_ctx_t *ctx); +void build_builtin_function (specifier_t spec, const char *ext_name, + const expr_t *bi_val); void build_intrinsic_function (specifier_t spec, const expr_t *intrinsic); void emit_function (function_t *f, expr_t *e); void clear_functions (void); diff --git a/tools/qfcc/include/rua-lang.h b/tools/qfcc/include/rua-lang.h index de3fbb92b..630c43eb1 100644 --- a/tools/qfcc/include/rua-lang.h +++ b/tools/qfcc/include/rua-lang.h @@ -84,6 +84,7 @@ typedef struct rua_tok_s { typedef struct { symtab_t *symtab; function_t *function; + specifier_t spec; } funcstate_t; typedef union rua_val_s { diff --git a/tools/qfcc/include/specifier.h b/tools/qfcc/include/specifier.h index bcc0ce8a9..95f99f047 100644 --- a/tools/qfcc/include/specifier.h +++ b/tools/qfcc/include/specifier.h @@ -81,6 +81,7 @@ typedef struct specifier_s { bool is_generic:1; bool is_generic_block:1; bool is_function:1;//FIXME do proper void(*)() -> ev_func + bool is_far:1; }; unsigned spec_bits; }; diff --git a/tools/qfcc/include/symtab.h b/tools/qfcc/include/symtab.h index 1f31be573..921ad65db 100644 --- a/tools/qfcc/include/symtab.h +++ b/tools/qfcc/include/symtab.h @@ -107,6 +107,7 @@ typedef enum { stab_enum, stab_label, stab_block, + stab_bypass, ///< symbols are added to parent } stab_type_e; typedef struct symtab_s { diff --git a/tools/qfcc/source/function.c b/tools/qfcc/source/function.c index 8a1474197..48f9c167f 100644 --- a/tools/qfcc/source/function.c +++ b/tools/qfcc/source/function.c @@ -1010,11 +1010,19 @@ value_too_large (const type_t *val_type) static void check_function (symbol_t *fsym) { + function_t *func = fsym->metafunc->func; param_t *params = fsym->params; param_t *p; int i; auto ret_type = fsym->type->func.ret_type; + if (!func) { + internal_error (0, "function %s not defined", fsym->name); + } + if (!is_func (func->type)) { + internal_error (0, "function type %s not a funciton", fsym->name); + } + if (!ret_type || !type_size (ret_type)) { error (0, "return type is an incomplete type"); return; @@ -1048,15 +1056,6 @@ build_scope (symbol_t *fsym, symtab_t *parent) symtab_t *parameters; symtab_t *locals; - if (!func) { - internal_error (0, "function %s not defined", fsym->name); - } - if (!is_func (func->type)) { - internal_error (0, "function type %s not a funciton", fsym->name); - } - - check_function (fsym); - func->label_scope = new_symtab (0, stab_label); parameters = new_symtab (parent, stab_param); @@ -1122,9 +1121,9 @@ add_function (function_t *f) } function_t * -begin_function (symbol_t *sym, const char *nicename, symtab_t *parent, - int far, storage_class_t storage) +begin_function (specifier_t spec, const char *nicename, symtab_t *parent) { + auto sym = spec.sym; if (sym->sy_type != sy_func) { error (0, "%s is not a function", sym->name); sym = new_symbol_type (sym->name, &type_func); @@ -1143,9 +1142,9 @@ begin_function (symbol_t *sym, const char *nicename, symtab_t *parent, }); } - defspace_t *space = far ? pr.far_data : sym->table->space; + defspace_t *space = spec.is_far ? pr.far_data : sym->table->space; - func = make_function (sym, nicename, space, storage); + func = make_function (sym, nicename, space, spec.storage); if (!func->def->external) { func->def->initialized = 1; func->def->constant = 1; @@ -1163,6 +1162,8 @@ begin_function (symbol_t *sym, const char *nicename, symtab_t *parent, func->line_info = lineno - pr.linenos; } + check_function (sym); + build_scope (sym, parent); return func; } @@ -1177,58 +1178,60 @@ build_function (symbol_t *fsym) } } -function_t * -build_code_function (symbol_t *fsym, const expr_t *state_expr, +void +build_code_function (specifier_t spec, const expr_t *state_expr, expr_t *statements, rua_ctx_t *ctx) { + auto fsym = spec.sym; if (ctx) { statements = (expr_t *) expr_process (statements, ctx); } - if (fsym->sy_type != sy_func) // probably in error recovery - return 0; + if (fsym->sy_type != sy_func) { // probably in error recovery + return; + } build_function (fsym); if (state_expr) { prepend_expr (statements, state_expr); } function_t *func = fsym->metafunc->func; current_target.build_code (func, statements); - - return fsym->metafunc->func; } -function_t * -build_builtin_function (symbol_t *sym, const char *ext_name, - const expr_t *bi_val, int far, storage_class_t storage) +void +build_builtin_function (specifier_t spec, const char *ext_name, + const expr_t *bi_val) { + auto sym = spec.sym; int bi; if (sym->sy_type != sy_func) { error (bi_val, "%s is not a function", sym->name); - return 0; + return; } if (!is_int_val (bi_val) && !(type_default != &type_int && is_float_val (bi_val))) { error (bi_val, "invalid constant for = #"); - return 0; + return; } if (sym->metafunc->meta_type == mf_generic) { - return 0; + return; } function_t *func = sym->metafunc->func; if (func && func->def && func->def->initialized) { error (bi_val, "%s redefined", sym->name); - return 0; + return; } - defspace_t *space = far ? pr.far_data : sym->table->space; - func = make_function (sym, nullptr, space, storage); + defspace_t *space = spec.is_far ? pr.far_data : sym->table->space; + func = make_function (sym, nullptr, space, spec.storage); if (ext_name) { func->s_name = ReuseString (ext_name); } - if (func->def->external) - return 0; + if (func->def->external) { + return; + } func->def->initialized = 1; func->def->constant = 1; @@ -1245,17 +1248,18 @@ build_builtin_function (symbol_t *sym, const char *ext_name, } if (bi < 0) { error (bi_val, "builtin functions must be positive or 0"); - return 0; + return; } func->builtin = bi; reloc_def_func (func, func->def); build_function (sym); + check_function (sym); + // for debug info build_scope (sym, current_symtab); func->parameters->space->size = 0; func->locals->space = func->parameters->space; - return func; } void @@ -1315,8 +1319,12 @@ emit_ctor (void) return; } - auto ctor_sym = new_symbol_type (".ctor", &type_func); - ctor_sym = function_symbol ((specifier_t) { .sym = ctor_sym }); - current_func = begin_function (ctor_sym, 0, current_symtab, 1, sc_static); - build_code_function (ctor_sym, 0, pr.ctor_exprs, nullptr); + auto spec = (specifier_t) { + .sym = new_symbol_type (".ctor", &type_func), + .storage = sc_static, + .is_far = true, + }; + spec.sym = function_symbol (spec); + current_func = begin_function (spec, nullptr, current_symtab); + build_code_function (spec, 0, pr.ctor_exprs, nullptr); } diff --git a/tools/qfcc/source/glsl-builtins.c b/tools/qfcc/source/glsl-builtins.c index 7e1dc21e7..87028c31b 100644 --- a/tools/qfcc/source/glsl-builtins.c +++ b/tools/qfcc/source/glsl-builtins.c @@ -437,9 +437,11 @@ SRC_LINE "genUType clamp(genUType x, genUType minVal, genUType maxVal);" "\n" "genUType clamp(genUType x, uint minVal, uint maxVal);" "\n" "genFType mix(genFType x, genFType y, genFType a) = " GLSL(46) ";" "\n" -"genFType mix(genFType x, genFType y, float a) = " GLSL(46) ";" "\n" +"genFType mix(genFType x, genFType y, float a)" "\n" +"{ return mix (x, y, (genFType) a); }" "\n" "genDType mix(genDType x, genDType y, genDType a) = " GLSL(46) ";" "\n" -"genDType mix(genDType x, genDType y, double a) = " GLSL(46) ";" "\n" +"genDType mix(genDType x, genDType y, double a)" "\n" +"{ return mix (x, y, (genDType) a); }" "\n" "genFType mix(genFType x, genFType y, genBType a) = " SPV(169) ";" "\n" "genDType mix(genDType x, genDType y, genBType a) = " SPV(169) ";" "\n" "genIType mix(genIType x, genIType y, genBType a) = " SPV(169) ";" "\n" diff --git a/tools/qfcc/source/glsl-parse.y b/tools/qfcc/source/glsl-parse.y index ce49fdf4b..32ccd1238 100644 --- a/tools/qfcc/source/glsl-parse.y +++ b/tools/qfcc/source/glsl-parse.y @@ -332,19 +332,16 @@ function_definition auto spec = $1; spec.sym->params = spec.params; spec.is_overload = true; - auto sym = function_symbol (spec); - current_func = begin_function (sym, nullptr, current_symtab, - false, spec.storage); + spec.sym = function_symbol (spec); + current_func = begin_function (spec, nullptr, current_symtab); current_symtab = current_func->locals; current_storage = sc_local; - spec.sym = sym; $1 = spec; } compound_statement_no_new_scope { auto spec = $1; - auto sym = spec.sym; - build_code_function (sym, nullptr, (expr_t *) $3, ctx); + build_code_function (spec, nullptr, (expr_t *) $3, ctx); current_symtab = $2; current_storage = sc_global;//FIXME current_func = nullptr; diff --git a/tools/qfcc/source/qc-parse.y b/tools/qfcc/source/qc-parse.y index 27b6c44e9..44606b544 100644 --- a/tools/qfcc/source/qc-parse.y +++ b/tools/qfcc/source/qc-parse.y @@ -257,8 +257,12 @@ static expr_t *local_expr; static void end_generic_scope (void) { - generic_scope = false; + if (generic_symtab != current_symtab || !generic_symtab->parent) { + internal_error (0, "scope stack tangled?"); + } + current_symtab = generic_symtab->parent; generic_symtab = nullptr; + generic_scope = false; } static void @@ -662,6 +666,23 @@ decl_expr (specifier_t spec, const expr_t *init, rua_ctx_t *ctx) return append_decl (decl, sym, init); } +static symtab_t * +pop_scope (symtab_t *current) +{ + auto parent = current->parent; + if (!parent) { + internal_error (0, "scope stack underflow"); + } + if (parent->type == stab_bypass) { + if (!parent->parent) { + internal_error (0, "bypass scope with no parent"); + } + // reconnect the current scope to the parent of the bypassed scope + current->parent = parent->parent; + } + return parent; +} + %} %expect 2 @@ -681,7 +702,7 @@ program external_def_list : /* empty */ { - if (!generic_block) { + if (!current_symtab) { current_symtab = pr.symtab; } } @@ -832,8 +853,8 @@ qc_nocode_func const expr_t *bi_val = expr_process ($4, ctx); spec.is_overload |= ctx->language->always_overload; - symbol_t *sym = function_symbol (spec); - build_builtin_function (sym, nullptr, bi_val, 0, spec.storage); + spec.sym = function_symbol (spec); + build_builtin_function (spec, nullptr, bi_val); } | identifier '=' intrinsic { @@ -858,24 +879,24 @@ qc_code_func : identifier '=' optional_state_expr save_storage { - $$ = (funcstate_t) { - .symtab = current_symtab, + specifier_t spec = qc_set_symbol ($0, $1); + auto fs = (funcstate_t) { .function = current_func, }; - specifier_t spec = qc_set_symbol ($0, $1); spec.is_overload |= ctx->language->always_overload; - symbol_t *sym = function_symbol (spec); - current_func = begin_function (sym, 0, current_symtab, 0, - spec.storage); + spec.sym = function_symbol (spec); + current_func = begin_function (spec, nullptr, current_symtab); current_symtab = current_func->locals; current_storage = sc_local; - $1 = sym; + fs.spec = spec; + $$ = fs; } compound_statement_ns { - build_code_function ($1, $3, $6, ctx); - current_symtab = $5.symtab; - current_func = $5.function; + auto fs = $5; + build_code_function (fs.spec, $3, $6, ctx); + current_symtab = pop_scope (current_func->parameters); + current_func = fs.function; restore_storage ($4); } ; @@ -1171,29 +1192,34 @@ save_storage ; function_body - : method_optional_state_expr + : method_optional_state_expr[state] { - specifier_t spec = default_type ($0, $0.sym); + specifier_t spec = $0; + if (!spec.is_generic) { + spec = default_type (spec, spec.sym); + } spec.is_overload |= ctx->language->always_overload; - $$ = function_symbol (spec); + spec.sym = function_symbol (spec); + $$ = spec; } - save_storage + save_storage[storage] { $$ = (funcstate_t) { - .symtab = current_symtab, .function = current_func, }; - current_func = begin_function ($2, 0, current_symtab, 0, - $-1.storage); + auto spec = $2; + current_func = begin_function (spec, nullptr, current_symtab); current_symtab = current_func->locals; current_storage = sc_local; } - compound_statement_ns + compound_statement_ns[body] { - build_code_function ($2, $1, $5, ctx); - current_symtab = $4.symtab; - current_func = $4.function; - restore_storage ($3); + auto spec = $2; + auto funcstate = $4; + build_code_function (spec, $state, $body, ctx); + current_symtab = pop_scope (current_func->parameters); + current_func = funcstate.function; + restore_storage ($storage); } | '=' '#' expr ';' { @@ -1201,8 +1227,8 @@ function_body const expr_t *bi_val = expr_process ($3, ctx); spec.is_overload |= ctx->language->always_overload; - symbol_t *sym = function_symbol (spec); - build_builtin_function (sym, nullptr, bi_val, 0, spec.storage); + spec.sym = function_symbol (spec); + build_builtin_function (spec, nullptr, bi_val); } | '=' intrinsic { @@ -1232,6 +1258,9 @@ storage_class } else { generic_scope = true; } + generic_symtab->type = stab_bypass; + generic_symtab->parent = current_symtab; + current_symtab = generic_symtab; } | ATTRIBUTE '(' attribute_list ')' { @@ -1380,7 +1409,7 @@ optional_enum_list enum_list : '{' enum_init enumerator_list optional_comma '}' { - current_symtab = current_symtab->parent; + current_symtab = pop_scope (current_symtab); $$ = finish_enum ($3); } ; @@ -1467,7 +1496,7 @@ struct_list { symbol_t *sym; symtab_t *symtab = current_symtab; - current_symtab = symtab->parent; + current_symtab = pop_scope (symtab); if ($1) { sym = $2; @@ -1841,7 +1870,7 @@ end_scope : /* empty */ { if (!options.traditional) { - current_symtab = current_symtab->parent; + current_symtab = pop_scope (current_symtab); } } ; @@ -1980,7 +2009,7 @@ statement } compound_statement { - current_symtab = current_symtab->parent; + current_symtab = pop_scope (current_symtab); $$ = $9; } | ALGEBRA '(' TYPE_SPEC ')' @@ -1990,7 +2019,7 @@ statement } compound_statement { - current_symtab = current_symtab->parent; + current_symtab = pop_scope (current_symtab); $$ = $6; } | ALGEBRA '(' TYPE_NAME ')' @@ -2000,7 +2029,7 @@ statement } compound_statement { - current_symtab = current_symtab->parent; + current_symtab = pop_scope (current_symtab); $$ = $6; } | comma_expr ';' @@ -2523,7 +2552,7 @@ ivar_decl_list { symtab_t *tab = $1; $$ = current_symtab; - current_symtab = tab->parent; + current_symtab = pop_scope (tab); tab->parent = 0; tab = $$->parent; // preserve the ivars inheritance chain @@ -2615,14 +2644,18 @@ methoddef symbol_t *sym = $4; symtab_t *ivar_scope; - $$ = (funcstate_t) { - .symtab = current_symtab, - .function = current_func, + auto spec = (specifier_t) { + .sym = sym, + .storage = sc_static, + .is_far = true, }; ivar_scope = class_ivar_scope (current_class, current_symtab); - current_func = begin_function (sym, nicename, ivar_scope, 1, - sc_static); + $$ = (funcstate_t) { + .symtab = ivar_scope, + .function = current_func, + }; + current_func = begin_function (spec, nicename, ivar_scope); class_finish_ivar_scope (current_class, ivar_scope, current_func->locals); method->func = sym->metafunc->func; @@ -2632,22 +2665,29 @@ methoddef } compound_statement_ns { - build_code_function ($4, $3, $7, ctx); - current_symtab = $6.symtab; - current_func = $6.function; + auto fs = $6; + auto fsym = $4; + fs.spec.sym = fsym; + build_code_function (fs.spec, $3, $7, ctx); + current_symtab = pop_scope (fs.symtab); + current_func = fs.function; restore_storage ($5); } | ci methoddecl '=' '#' const ';' { - symbol_t *sym; method_t *method = $2; const expr_t *bi_val = expr_process ($5, ctx); method->instance = $1; method = class_find_method (current_class, method); - sym = method_symbol (current_class, method); - build_builtin_function (sym, nullptr, bi_val, 1, sc_static); - method->func = sym->metafunc->func; + + auto spec = (specifier_t) { + .sym = method_symbol (current_class, method), + .storage = sc_static, + .is_far = true, + }; + build_builtin_function (spec, nullptr, bi_val); + method->func = spec.sym->metafunc->func; method->def = method->func->def; } ; @@ -3127,9 +3167,6 @@ qc_keyword_or_id (QC_YYSTYPE *lval, const char *token, rua_ctx_t *ctx) } symbol_t *sym = nullptr; - if (generic_symtab) { - sym = symtab_lookup (generic_symtab, token); - } if (!sym) { sym = symtab_lookup (current_symtab, token); } diff --git a/tools/qfcc/source/qp-parse.y b/tools/qfcc/source/qp-parse.y index 0b091d773..a988b2902 100644 --- a/tools/qfcc/source/qp-parse.y +++ b/tools/qfcc/source/qp-parse.y @@ -161,14 +161,17 @@ build_dotmain (symbol_t *program, rua_ctx_t *ctx) exitcode = new_symbol_expr (symtab_lookup (current_symtab, "ExitCode")); - current_func = begin_function (dotmain, 0, current_symtab, 0, - current_storage); + auto spec = (specifier_t) { + .sym = dotmain, + .storage = current_storage, + }; + current_func = begin_function (spec, nullptr, current_symtab); code = new_block_expr (0); code->block.scope = current_func->locals; auto call = new_call_expr (new_symbol_expr (program), nullptr, nullptr); append_expr (code, call); append_expr (code, new_return_expr (exitcode)); - build_code_function (dotmain, 0, code, ctx); + build_code_function (spec, 0, code, ctx); } static const expr_t * @@ -281,10 +284,13 @@ program symtab_removesymbol (current_symtab, $1); symtab_addsymbol (current_symtab, $1); - current_func = begin_function ($1, 0, current_symtab, 0, - current_storage); + auto spec = (specifier_t) { + .sym = $1, + .storage = current_storage, + }; + current_func = begin_function (spec, nullptr, current_symtab); current_symtab = current_func->locals; - build_code_function ($1, 0, $4, ctx); + build_code_function (spec, 0, $4, ctx); current_symtab = st; build_dotmain ($1, ctx); @@ -367,25 +373,25 @@ subprogram_declarations subprogram_declaration : subprogram_head ';' { - $$.storage = current_storage; auto sym = $1; // always an sy_xvalue with callable symbol in lvalue and // actual function symbol in rvalue auto csym = (symbol_t *) sym->xvalue.lvalue; auto fsym = (symbol_t *) sym->xvalue.rvalue; - current_func = begin_function (fsym, sym->name, current_symtab, 0, - current_storage); + auto spec = (specifier_t) { + .sym = fsym, + .storage = current_storage, + }; + current_func = begin_function (spec, sym->name, current_symtab); current_symtab = current_func->locals; current_storage = sc_local; // null for procedures, valid symbol expression for functions csym->xvalue.lvalue = function_return (current_func); + $$ = spec; } declarations compound_statement ';' { - auto sym = $1; - // always an sy_xvalue with callable symbol in lvalue and - // actual function symbol in rvalue - auto fsym = (symbol_t *) sym->xvalue.rvalue; + auto spec = $3; auto statements = $5; if (!statements) { statements = new_block_expr (0); @@ -393,17 +399,20 @@ subprogram_declaration } auto ret_expr = new_return_expr (function_return (current_func)); append_expr (statements, ret_expr); - build_code_function (fsym, 0, statements, ctx); + build_code_function (spec, 0, statements, ctx); current_symtab = current_func->parameters->parent; - current_storage = $3.storage; + current_storage = spec.storage; } | subprogram_head ASSIGNOP '#' VALUE ';' { auto sym = $1; // always an sy_xvalue with callable symbol in lvalue and // actual function symbol in rvalue - auto fsym = (symbol_t *) sym->xvalue.rvalue; - build_builtin_function (fsym, sym->name, $4, 0, current_storage); + auto spec = (specifier_t) { + .sym = (symbol_t *) sym->xvalue.rvalue, + .storage = current_storage, + }; + build_builtin_function (spec, sym->name, $4); } ; diff --git a/tools/qfcc/source/symtab.c b/tools/qfcc/source/symtab.c index 78cd81409..ba93224fe 100644 --- a/tools/qfcc/source/symtab.c +++ b/tools/qfcc/source/symtab.c @@ -152,6 +152,12 @@ symtab_addsymbol (symtab_t *symtab, symbol_t *symbol) if (symbol->table) internal_error (0, "symbol '%s' is already in another symbol table", symbol->name); + if (symtab->type == stab_bypass) { + if (!symtab->parent) { + internal_error (0, "bypass symbol table with no parent"); + } + symtab = symtab->parent; + } if ((s = Hash_Find (symtab->tab, symbol->name))) return s; Hash_Add (symtab->tab, symbol);