From 9bb68f2d8c047a410651d97cf814bb0c9952a82c Mon Sep 17 00:00:00 2001 From: Bill Currie Date: Tue, 25 Feb 2020 14:30:26 +0900 Subject: [PATCH] Allow nested use of PR_SaveParams() PR_SaveParams() is required for implementing the +initialize diversion used by Objective-QuakeC because builtins do not have local def spaces (of course, a normal stack calling convention would help). However, it is entirely possible for a call to +initialize to trigger another call to +initialize, thus the need for stacking parameter stashes. As a bonus, this implementation cleans up some fields in progs_t. --- include/QF/progs.h | 59 +++++++++++++++++++++++++++++++++----- libs/gamecode/pr_exec.c | 31 +++++++++++--------- libs/gamecode/pr_resolve.c | 4 --- libs/ruamoko/rua_obj.c | 4 +-- 4 files changed, 71 insertions(+), 27 deletions(-) diff --git a/include/QF/progs.h b/include/QF/progs.h index 25f81f2b5..1162625ac 100644 --- a/include/QF/progs.h +++ b/include/QF/progs.h @@ -91,15 +91,63 @@ void PR_RunError (progs_t *pr, const char *error, ...) __attribute__((format(pri (pr)->pr_params[1] = (pr)->pr_real_params[1]; \ } while (0) -/** Save the current parameters. - \param pr pointer to ::progs_t VM struct +/** \name Detouring Function Calls + + These functions allow a builtin function that uses PR_CallFunction() to + safely insert a call to another VM function. The +initialize diversion + required by Objective-QuakeC uses this. + + PR_PushFrame (pr); + __auto_type params = PR_SaveParams (pr); + ... set up parameters to detour_function + PR_ExecuteProgram (pr, detour_function) + PR_RestoreParams (pr, params); + PR_PopFrame (pr); + */ -void PR_SaveParams (progs_t *pr); +///@{ +typedef struct pr_stashed_params_s { + pr_type_t *param_ptrs[2]; + int argc; + pr_type_t params[1]; +} pr_stashed_params_t; + +/** Save the current parameters to the provided stash. + + \warning The memory for the parameter stash is allocated using + alloca(). + + \param pr pointer to ::progs_t VM struct + \return Pointer to a newly allocated and initialized parameter + stash that has the current parameters saved to it. + \hideinitializer +*/ +#define PR_SaveParams(pr) \ + _PR_SaveParams((pr), \ + alloca (field_offset (pr_stashed_params_t, \ + params[(pr)->pr_argc \ + * (pr)->pr_param_size]))) + +/** [INTERNAL] Save the current parameters to the provided stash. + + \warning Requires \a params to be correctly allocated. Use + PR_SaveParams instead as it will create a suitable stash for + saving the parameters. + + \param pr pointer to ::progs_t VM struct + \param params location to save the parameters, must be of adequade size + to hold \a pr_argc * \a pr_param_size words in \a params + \return \a params Allows the likes of: + __auto_type params = PR_SaveParams (pr); +*/ +pr_stashed_params_t *_PR_SaveParams (progs_t *pr, pr_stashed_params_t *params); /** Restore the parameters saved by PR_SaveParams(). \param pr pointer to ::progs_t VM struct + \param params pointer to stash created by PR_SaveParams() */ -void PR_RestoreParams (progs_t *pr); +void PR_RestoreParams (progs_t *pr, pr_stashed_params_t *params); +///@} /** Push an execution frame onto the VM stack. Saves current execution state. \param pr pointer to ::progs_t VM struct @@ -1662,9 +1710,6 @@ struct progs_s { pr_type_t *pr_return; pr_type_t *pr_params[MAX_PARMS]; pr_type_t *pr_real_params[MAX_PARMS]; - pr_type_t *pr_param_ptrs[2]; - pr_type_t *pr_saved_params; - int pr_saved_argc; int pr_param_size; ///< covers both params and return int pr_param_alignment; ///< covers both params and return ///@} diff --git a/libs/gamecode/pr_exec.c b/libs/gamecode/pr_exec.c index f7becdfa5..e7e9d28eb 100644 --- a/libs/gamecode/pr_exec.c +++ b/libs/gamecode/pr_exec.c @@ -72,37 +72,40 @@ PR_RunError (progs_t * pr, const char *error, ...) PR_Error (pr, "Program error: %s", string->str); } -VISIBLE void -PR_SaveParams (progs_t *pr) +VISIBLE pr_stashed_params_t * +_PR_SaveParams (progs_t *pr, pr_stashed_params_t *params) { int i; int size = pr->pr_param_size * sizeof (pr_type_t); - pr->pr_param_ptrs[0] = pr->pr_params[0]; - pr->pr_param_ptrs[1] = pr->pr_params[1]; + params->param_ptrs[0] = pr->pr_params[0]; + params->param_ptrs[1] = pr->pr_params[1]; pr->pr_params[0] = pr->pr_real_params[0]; pr->pr_params[1] = pr->pr_real_params[1]; for (i = 0; i < pr->pr_argc; i++) { - memcpy (pr->pr_saved_params + i * pr->pr_param_size, + memcpy (params->params + i * pr->pr_param_size, pr->pr_real_params[i], size); - if (i < 2) - memcpy (pr->pr_real_params[i], pr->pr_param_ptrs[0], size); + if (i < 2) { //XXX FIXME what the what?!? + memcpy (pr->pr_real_params[i], params->param_ptrs[0], size); + } } - pr->pr_saved_argc = pr->pr_argc; + params->argc = pr->pr_argc; + return params; } VISIBLE void -PR_RestoreParams (progs_t *pr) +PR_RestoreParams (progs_t *pr, pr_stashed_params_t *params) { int i; int size = pr->pr_param_size * sizeof (pr_type_t); - pr->pr_params[0] = pr->pr_param_ptrs[0]; - pr->pr_params[1] = pr->pr_param_ptrs[1]; - pr->pr_argc = pr->pr_saved_argc; - for (i = 0; i < pr->pr_argc; i++) + pr->pr_params[0] = params->param_ptrs[0]; + pr->pr_params[1] = params->param_ptrs[1]; + pr->pr_argc = params->argc; + for (i = 0; i < pr->pr_argc; i++) { memcpy (pr->pr_real_params[i], - pr->pr_saved_params + i * pr->pr_param_size, size); + params->params + i * pr->pr_param_size, size); + } } VISIBLE inline void diff --git a/libs/gamecode/pr_resolve.c b/libs/gamecode/pr_resolve.c index 07ef44d60..0f7acaa8f 100644 --- a/libs/gamecode/pr_resolve.c +++ b/libs/gamecode/pr_resolve.c @@ -139,10 +139,6 @@ PR_ResolveGlobals (progs_t *pr) goto error; pr->pr_param_alignment = G_INT (pr, def->ofs); } - if (pr->pr_saved_params) - free (pr->pr_saved_params); - pr->pr_saved_params = calloc (pr->pr_param_size * MAX_PARMS, - sizeof (pr_type_t)); memcpy (pr->pr_real_params, pr->pr_params, sizeof (pr->pr_params)); if (!pr->globals.time) { if ((def = PR_FindGlobal (pr, "time"))) diff --git a/libs/ruamoko/rua_obj.c b/libs/ruamoko/rua_obj.c index efb193bc8..f44da87a9 100644 --- a/libs/ruamoko/rua_obj.c +++ b/libs/ruamoko/rua_obj.c @@ -728,12 +728,12 @@ obj_send_initialize (progs_t *pr, pr_class_t *class) sel = &G_STRUCT (pr, pr_sel_t, method->method_name); if (sel->sel_id == selector->sel_id) { PR_PushFrame (pr); - PR_SaveParams (pr); + __auto_type params = PR_SaveParams (pr); // param 0 is known to be the class pointer P_POINTER (pr, 1) = method->method_name; // pr->pr_argc is known to be 2 PR_ExecuteProgram (pr, method->method_imp); - PR_RestoreParams (pr); + PR_RestoreParams (pr, params); PR_PopFrame (pr); return; }