summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Michael Smith <mikesmiffy128@gmail.com> 2026-04-03 21:45:17 +0100
committerGravatar Michael Smith <mikesmiffy128@gmail.com> 2026-04-04 01:37:54 +0100
commit2ee93c3f46f8e948d2331eab46c95322a88874ee (patch)
treeb3a39f8e79ef725579b569748ac4be28b82b1266
parente174011b5e7390dc8714910185aaa40d0a44702f (diff)
downloadsst-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.c16
-rw-r--r--src/con_.S20
-rw-r--r--src/con_.c21
-rw-r--r--src/con_.h7
-rw-r--r--src/sst.c5
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_";
diff --git a/src/con_.S b/src/con_.S
index 0c6ec2d..a9a7447 100644
--- a/src/con_.S
+++ b/src/con_.S
@@ -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
diff --git a/src/con_.c b/src/con_.c
index 54574bc..1d7d06e 100644
--- a/src/con_.c
+++ b/src/con_.c
@@ -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) {
diff --git a/src/con_.h b/src/con_.h
index 5de5608..9202176 100644
--- a/src/con_.h
+++ b/src/con_.h
@@ -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, \
diff --git a/src/sst.c b/src/sst.c
index e732073..bbcae79 100644
--- a/src/sst.c
+++ b/src/sst.c
@@ -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,