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.
This commit is contained in:
Bill Currie 2020-02-25 14:30:26 +09:00
parent 99c818adcc
commit 9bb68f2d8c
4 changed files with 71 additions and 27 deletions

View file

@ -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]; \ (pr)->pr_params[1] = (pr)->pr_real_params[1]; \
} while (0) } while (0)
/** Save the current parameters. /** \name Detouring Function Calls
\param pr pointer to ::progs_t VM struct
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(). /** Restore the parameters saved by PR_SaveParams().
\param pr pointer to ::progs_t VM struct \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. /** Push an execution frame onto the VM stack. Saves current execution state.
\param pr pointer to ::progs_t VM struct \param pr pointer to ::progs_t VM struct
@ -1662,9 +1710,6 @@ struct progs_s {
pr_type_t *pr_return; pr_type_t *pr_return;
pr_type_t *pr_params[MAX_PARMS]; pr_type_t *pr_params[MAX_PARMS];
pr_type_t *pr_real_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_size; ///< covers both params and return
int pr_param_alignment; ///< covers both params and return int pr_param_alignment; ///< covers both params and return
///@} ///@}

View file

@ -72,37 +72,40 @@ PR_RunError (progs_t * pr, const char *error, ...)
PR_Error (pr, "Program error: %s", string->str); PR_Error (pr, "Program error: %s", string->str);
} }
VISIBLE void VISIBLE pr_stashed_params_t *
PR_SaveParams (progs_t *pr) _PR_SaveParams (progs_t *pr, pr_stashed_params_t *params)
{ {
int i; int i;
int size = pr->pr_param_size * sizeof (pr_type_t); int size = pr->pr_param_size * sizeof (pr_type_t);
pr->pr_param_ptrs[0] = pr->pr_params[0]; params->param_ptrs[0] = pr->pr_params[0];
pr->pr_param_ptrs[1] = pr->pr_params[1]; params->param_ptrs[1] = pr->pr_params[1];
pr->pr_params[0] = pr->pr_real_params[0]; pr->pr_params[0] = pr->pr_real_params[0];
pr->pr_params[1] = pr->pr_real_params[1]; pr->pr_params[1] = pr->pr_real_params[1];
for (i = 0; i < pr->pr_argc; i++) { 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); pr->pr_real_params[i], size);
if (i < 2) if (i < 2) { //XXX FIXME what the what?!?
memcpy (pr->pr_real_params[i], pr->pr_param_ptrs[0], size); 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 VISIBLE void
PR_RestoreParams (progs_t *pr) PR_RestoreParams (progs_t *pr, pr_stashed_params_t *params)
{ {
int i; int i;
int size = pr->pr_param_size * sizeof (pr_type_t); int size = pr->pr_param_size * sizeof (pr_type_t);
pr->pr_params[0] = pr->pr_param_ptrs[0]; pr->pr_params[0] = params->param_ptrs[0];
pr->pr_params[1] = pr->pr_param_ptrs[1]; pr->pr_params[1] = params->param_ptrs[1];
pr->pr_argc = pr->pr_saved_argc; pr->pr_argc = params->argc;
for (i = 0; i < pr->pr_argc; i++) for (i = 0; i < pr->pr_argc; i++) {
memcpy (pr->pr_real_params[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 VISIBLE inline void

View file

@ -139,10 +139,6 @@ PR_ResolveGlobals (progs_t *pr)
goto error; goto error;
pr->pr_param_alignment = G_INT (pr, def->ofs); 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)); memcpy (pr->pr_real_params, pr->pr_params, sizeof (pr->pr_params));
if (!pr->globals.time) { if (!pr->globals.time) {
if ((def = PR_FindGlobal (pr, "time"))) if ((def = PR_FindGlobal (pr, "time")))

View file

@ -728,12 +728,12 @@ obj_send_initialize (progs_t *pr, pr_class_t *class)
sel = &G_STRUCT (pr, pr_sel_t, method->method_name); sel = &G_STRUCT (pr, pr_sel_t, method->method_name);
if (sel->sel_id == selector->sel_id) { if (sel->sel_id == selector->sel_id) {
PR_PushFrame (pr); PR_PushFrame (pr);
PR_SaveParams (pr); __auto_type params = PR_SaveParams (pr);
// param 0 is known to be the class pointer // param 0 is known to be the class pointer
P_POINTER (pr, 1) = method->method_name; P_POINTER (pr, 1) = method->method_name;
// pr->pr_argc is known to be 2 // pr->pr_argc is known to be 2
PR_ExecuteProgram (pr, method->method_imp); PR_ExecuteProgram (pr, method->method_imp);
PR_RestoreParams (pr); PR_RestoreParams (pr, params);
PR_PopFrame (pr); PR_PopFrame (pr);
return; return;
} }