diff options
| author | 2026-04-03 21:45:17 +0100 | |
|---|---|---|
| committer | 2026-04-04 01:37:54 +0100 | |
| commit | 2ee93c3f46f8e948d2331eab46c95322a88874ee (patch) | |
| tree | b3a39f8e79ef725579b569748ac4be28b82b1266 | |
| parent | e174011b5e7390dc8714910185aaa40d0a44702f (diff) | |
| download | sst-2ee93c3f46f8e948d2331eab46c95322a88874ee.tar.gz sst-2ee93c3f46f8e948d2331eab46c95322a88874ee.zip | |
Size-optimise cvar value cleanup
Put all the cvars together contiguously; then we don't need to generate
lots of individual calls to extfree() and can instead simply iterate a
fixed-sized array.
| -rw-r--r-- | src/build/gluegen.c | 16 | ||||
| -rw-r--r-- | src/con_.S | 20 | ||||
| -rw-r--r-- | src/con_.c | 21 | ||||
| -rw-r--r-- | src/con_.h | 7 | ||||
| -rw-r--r-- | src/sst.c | 5 |
5 files changed, 47 insertions, 22 deletions
diff --git a/src/build/gluegen.c b/src/build/gluegen.c index b2887a9..3e04db8 100644 --- a/src/build/gluegen.c +++ b/src/build/gluegen.c @@ -867,22 +867,6 @@ F( " if (has_%.*s) _feat_end_%.*s();", } _( "}") _( "") -_( "static inline void freevars() {") - for (int i = 1; i < ncvars; ++i) { -F( " extfree(con_getvarcommon(%.*s)->strval);", - cvar_names[i].len, cvar_names[i].s) - } -_( "}") -_( "") -_( "static inline void shuntvars() {") -_( "#ifdef _WIN32") - for (int i = 1; i < ncvars; ++i) { -F( " __builtin_memmove(&%.*s->v1, &%.*s->v2, ", - cvar_names[i].len, cvar_names[i].s, cvar_names[i].len, cvar_names[i].s) -_( " sizeof(struct con_var_common));") - } -_( "#endif") -_( "}") for (int i = 1; i < nevents; ++i) { const char *prefix = event_predicateflags[i] ? "bool CHECK_" : "void EMIT_"; @@ -42,4 +42,24 @@ ASM_MANGLE(_con_colourmsg): .byte 0x25 .long ASM_MANGLE(_con_colourmsgf) +// The DEF_CVAR macros put stuff in .rdata$y/.rodata.cvars1. Sandwiching that +// between empty x/0 and z/2 sections gives us section start/end pointers to +// iterate the cvars, which is used for freeing the values on unload. Relies +// on the section merging not adding extra padding, which is fine with the +// linker + settings we use in practice. +#ifdef _WIN32 +.section .rdata$x, "rw" +#else +.section .rodata.cvars0, "rw" +#endif +.globl ASM_MANGLE(_con_vars_start) +ASM_MANGLE(_con_vars_start): +#ifdef _WIN32 +.section .rdata$z, "rw" +#else +.section .rodata.cvars2, "rw" +#endif +.globl ASM_MANGLE(_con_vars_end) +ASM_MANGLE(_con_vars_end): + // vi: sw=4 ts=4 noet tw=80 cc=80 @@ -22,6 +22,7 @@ #include <string.h> #include "abi.h" +#include "asm.h" #include "chunklets/x86.h" #include "con_.h" #include "engineapi.h" // for factories and rgba - XXX: is this a bit circular? @@ -503,6 +504,16 @@ static inline void badver() { con_warn("sst: error: this engine version is not yet supported\n"); } +extern struct con_var _con_vars_start, _con_vars_end; // con_.S + +static inline void shuntvars() { +#ifdef _WIN32 + for (struct con_var *v = &_con_vars_start; v != &_con_vars_end; ++v) { + __builtin_memmove(&v->v1, &v->v2, sizeof(struct con_var_common)); + } +#endif +} + bool con_detect(int pluginver) { if (_con_iface = factory_engine("VEngineCvar007", 0)) { // GENIUS HACK (BUT STILL BAD): Portal 2 has everything in ICvar shifted @@ -574,6 +585,7 @@ bool con_detect(int pluginver) { } if (_con_iface = factory_engine("VEngineCvar003", 0)) { #ifdef _WIN32 // there's no OE on linux! + shuntvars(); // just doing this as early as possible atm, it's fine _gametype_tag |= _gametype_tag_OE; // for deletion/unlinking on unload, we need an indirect linked list // pointer. calling GetCommands gives us a direct pointer. so we have to @@ -733,6 +745,12 @@ void con_init() { *pi++ = (void *)&GetSplitScreenPlayerSlot; } +static inline void freevars() { + for (struct con_var *v = &_con_vars_start; v != &_con_vars_end; ++v) { + extfree(con_getvarcommon(v)->strval); + } +} + void con_disconnect() { #ifdef _WIN32 if (linkedlist) { @@ -749,10 +767,11 @@ void con_disconnect() { pp = next; } } - return; + goto f; } #endif UnregisterConCommands(_con_iface, dllid); +f: freevars(); } struct con_var *con_findvar(const char *name) { @@ -273,7 +273,14 @@ extern struct _con_vtab_iconvar_wrap { } _con_vtab_iconvar_wrap; #define _con_vtab_iconvar _con_vtab_iconvar_wrap.vtable +#ifdef _WIN32 +#define _CON_VAR_SEG ".rdata$y" +#else +#define _CON_VAR_SEG ".rodata.cvars1" +#endif + #define _DEF_CVAR(name_, desc, value, hasmin_, min, hasmax_, max, flags_) \ + __attribute__((section(_CON_VAR_SEG))) \ static struct con_var _cvar_##name_ = { \ .base = { \ .vtable = _con_vtab_var, \ @@ -14,8 +14,6 @@ * PERFORMANCE OF THIS SOFTWARE. */ -#include <string.h> - #ifdef _WIN32 #include <Windows.h> #include <shlwapi.h> @@ -28,7 +26,6 @@ #include "engineapi.h" #include "errmsg.h" #include "event.h" -#include "extmalloc.h" // for freevars() in generated code #include "feature.h" #include "fixes.h" #include "gamedata.h" @@ -543,7 +540,6 @@ static opt_small void hook_plugin_unload_cbv2(struct con_cmdargs *args) { static inline bool do_load(ifacefactory enginef, ifacefactory serverf) { factory_engine = enginef; factory_server = serverf; if_cold (!engineapi_init(ifacever)) return false; - if (GAMETYPE_MATCHES(OE)) shuntvars(); // see also comment in con_detect() const void **p = vtable_firstdiff; if (GAMETYPE_MATCHES(Portal2)) *p++ = (void *)&nop_p_v; // ClientFullyConnect *p++ = (void *)&nop_p_v; // ClientDisconnect @@ -598,7 +594,6 @@ static inline void do_unload() { } endfeatures(); con_disconnect(); - freevars(); } static bool VCALLCONV Load(void *this, ifacefactory enginef, |
