From 1577ac3a29e942800f41dbcc20cd8eff6641e347 Mon Sep 17 00:00:00 2001
From: Rudolf Polzer <divVerent@gmail.com>
Date: Tue, 29 Oct 2024 16:51:51 -0400
Subject: [PATCH] Change engine VM memory layout as follows:

0 to 0x7FFFFFFF: reserved.
0x80000000 to 0x80000000 + num_globals - 1: global pointers.
0x80000000 + num_globals to 0x80000000 + num_globals + num_entities * num_entityfields - 1: entity field pointers.

Note that there are no instructions yet to _make_ global pointers, or to
load values from them.

This is the beginning of implementing the pointer instruction set.
---
 prvm_exec.c        |  82 +++++++++++++++------------------
 prvm_execprogram.h | 111 ++++++++++++++++++++++++++++-----------------
 2 files changed, 105 insertions(+), 88 deletions(-)

diff --git a/prvm_exec.c b/prvm_exec.c
index e7f54680..02f03398 100644
--- a/prvm_exec.c
+++ b/prvm_exec.c
@@ -860,7 +860,7 @@ static int PRVM_LeaveFunction (prvm_prog_t *prog)
 		f->tprofile_total += prog->stack[prog->depth].tprofile_acc;
 		f->builtinsprofile_total += prog->stack[prog->depth].builtinsprofile_acc;
 	}
-	
+
 	return prog->stack[prog->depth].s;
 }
 
@@ -922,6 +922,35 @@ extern cvar_t prvm_traceqc;
 extern cvar_t prvm_statementprofiling;
 extern qbool prvm_runawaycheck;
 
+#define PRVM_GLOBALSBASE 0x80000000
+
+// These do not change.
+#define CACHE_UNCHANGING() \
+	mstatement_t *cached_statements = prog->statements; \
+	qbool cached_allowworldwrites = prog->allowworldwrites; \
+	unsigned int cached_flag = prog->flag; \
+	unsigned int cached_vmglobals_1 = prog->numglobals - 1; \
+	unsigned int cached_vmglobals_3 = prog->numglobals - 3; \
+	unsigned int cached_vmglobalsstart = PRVM_GLOBALSBASE; \
+	unsigned int cached_vmglobal1 = cached_vmglobalsstart + 1; \
+	unsigned int cached_vmentity0start = cached_vmglobalsstart + prog->numglobals; \
+	unsigned int cached_vmentity1start = cached_vmentity0start + prog->entityfields; \
+	unsigned int cached_entityfields = prog->entityfields; \
+	unsigned int cached_entityfields_2 = prog->entityfields - 2; \
+	prvm_vec_t *globals = prog->globals.fp; \
+	prvm_vec_t *global1 = prog->globals.fp + 1
+
+// These may become out of date when a builtin is called, and are updated accordingly.
+#define CACHE_CHANGING(DECLARE) \
+	DECLARE(prvm_vec_t *) cached_edictsfields = prog->edictsfields.fp; \
+	DECLARE(prvm_vec_t *) cached_edictsfields_entity1 = cached_edictsfields + prog->entityfields; \
+	DECLARE(unsigned int) cached_entityfieldsarea_entityfields = prog->entityfieldsarea - prog->entityfields; \
+	DECLARE(unsigned int) cached_entityfieldsarea_entityfields_2 = prog->entityfieldsarea - prog->entityfields - 2; \
+	DECLARE(unsigned int) cached_max_edicts = prog->max_edicts
+
+#define DO_DECLARE(t) t
+#define NO_DECLARE(t)
+
 #ifdef PROFILING
 #ifdef CONFIG_MENU
 /*
@@ -940,21 +969,8 @@ void MVM_ExecuteProgram (prvm_prog_t *prog, func_t fnum, const char *errormessag
 	double  calltime;
 	double tm, starttm;
 	prvm_vec_t tempfloat;
-	// these may become out of date when a builtin is called, and are updated accordingly
-	prvm_vec_t *cached_edictsfields = prog->edictsfields.fp;
-	unsigned int cached_entityfields = prog->entityfields;
-	unsigned int cached_entityfields_3 = prog->entityfields - 3;
-	unsigned int cached_entityfieldsarea = prog->entityfieldsarea;
-	unsigned int cached_entityfieldsarea_entityfields = prog->entityfieldsarea - prog->entityfields;
-	unsigned int cached_entityfieldsarea_3 = prog->entityfieldsarea - 3;
-	unsigned int cached_entityfieldsarea_entityfields_3 = prog->entityfieldsarea - prog->entityfields - 3;
-	unsigned int cached_max_edicts = prog->max_edicts;
-	// these do not change
-	mstatement_t *cached_statements = prog->statements;
-	qbool cached_allowworldwrites = prog->allowworldwrites;
-	unsigned int cached_flag = prog->flag;
-
-	prvm_vec_t *globals = prog->globals.fp;
+	CACHE_UNCHANGING();
+	CACHE_CHANGING(DO_DECLARE);
 
 	calltime = Sys_DirtyTime();
 
@@ -1049,21 +1065,8 @@ void CLVM_ExecuteProgram (prvm_prog_t *prog, func_t fnum, const char *errormessa
 	double  calltime;
 	double tm, starttm;
 	prvm_vec_t tempfloat;
-	// these may become out of date when a builtin is called, and are updated accordingly
-	prvm_vec_t *cached_edictsfields = prog->edictsfields.fp;
-	unsigned int cached_entityfields = prog->entityfields;
-	unsigned int cached_entityfields_3 = prog->entityfields - 3;
-	unsigned int cached_entityfieldsarea = prog->entityfieldsarea;
-	unsigned int cached_entityfieldsarea_entityfields = prog->entityfieldsarea - prog->entityfields;
-	unsigned int cached_entityfieldsarea_3 = prog->entityfieldsarea - 3;
-	unsigned int cached_entityfieldsarea_entityfields_3 = prog->entityfieldsarea - prog->entityfields - 3;
-	unsigned int cached_max_edicts = prog->max_edicts;
-	// these do not change
-	mstatement_t *cached_statements = prog->statements;
-	qbool cached_allowworldwrites = prog->allowworldwrites;
-	unsigned int cached_flag = prog->flag;
-
-	prvm_vec_t *globals = prog->globals.fp;
+	CACHE_UNCHANGING();
+	CACHE_CHANGING(DO_DECLARE);
 
 	calltime = Sys_DirtyTime();
 
@@ -1162,21 +1165,8 @@ void PRVM_ExecuteProgram (prvm_prog_t *prog, func_t fnum, const char *errormessa
 	double  calltime;
 	double tm, starttm;
 	prvm_vec_t tempfloat;
-	// these may become out of date when a builtin is called, and are updated accordingly
-	prvm_vec_t *cached_edictsfields = prog->edictsfields.fp;
-	unsigned int cached_entityfields = prog->entityfields;
-	unsigned int cached_entityfields_3 = prog->entityfields - 3;
-	unsigned int cached_entityfieldsarea = prog->entityfieldsarea;
-	unsigned int cached_entityfieldsarea_entityfields = prog->entityfieldsarea - prog->entityfields;
-	unsigned int cached_entityfieldsarea_3 = prog->entityfieldsarea - 3;
-	unsigned int cached_entityfieldsarea_entityfields_3 = prog->entityfieldsarea - prog->entityfields - 3;
-	unsigned int cached_max_edicts = prog->max_edicts;
-	// these do not change
-	mstatement_t *cached_statements = prog->statements;
-	qbool cached_allowworldwrites = prog->allowworldwrites;
-	unsigned int cached_flag = prog->flag;
-
-	prvm_vec_t *globals = prog->globals.fp;
+	CACHE_UNCHANGING();
+	CACHE_CHANGING(DO_DECLARE);
 
 	calltime = Sys_DirtyTime();
 
diff --git a/prvm_execprogram.h b/prvm_execprogram.h
index 34dd8d29..68d0a673 100644
--- a/prvm_execprogram.h
+++ b/prvm_execprogram.h
@@ -1,5 +1,6 @@
 extern cvar_t prvm_garbagecollection_enable;
 int i;
+prvm_uint_t addr;
 // NEED to reset startst after calling this! startst may or may not be clobbered!
 #define ADVANCE_PROFILE_BEFORE_JUMP() \
 	prog->xfunction->profile += (st - startst); \
@@ -229,7 +230,7 @@ int i;
 	&&handle_OP_GE_I,
 	&&handle_OP_LT_I,
 	&&handle_OP_GT_I,
-	
+
 	&&handle_OP_LE_IF,
 	&&handle_OP_GE_IF,
 	&&handle_OP_LT_IF,
@@ -274,7 +275,7 @@ int i;
 	&&handle_OP_GSTOREP_ENT,
 	&&handle_OP_GSTOREP_FLD,
 	&&handle_OP_GSTOREP_S,
-	&&handle_OP_GSTOREP_FNC,		
+	&&handle_OP_GSTOREP_FNC,
 	&&handle_OP_GSTOREP_V,
 	&&handle_OP_GADDRESS,
 	&&handle_OP_GLOAD_I,
@@ -478,37 +479,60 @@ int i;
 			HANDLE_OPCODE(OP_STOREP_ENT):
 			HANDLE_OPCODE(OP_STOREP_FLD):		// integers
 			HANDLE_OPCODE(OP_STOREP_FNC):		// pointers
-				if ((prvm_uint_t)OPB->_int - cached_entityfields >= cached_entityfieldsarea_entityfields)
+				if ((addr = (prvm_uint_t)OPB->_int - cached_vmentity1start) < cached_entityfieldsarea_entityfields)
 				{
-					if ((prvm_uint_t)OPB->_int >= cached_entityfieldsarea)
-					{
-						PRE_ERROR();
-						prog->error_cmd("%s attempted to write to an out of bounds edict (%i)", prog->name, (int)OPB->_int);
-						goto cleanup;
-					}
-					if ((prvm_uint_t)OPB->_int < cached_entityfields && !cached_allowworldwrites)
+					// OK entity write.
+					ptr = (prvm_eval_t *)(cached_edictsfields_entity1 + addr);
+				}
+				else if ((addr = (prvm_uint_t)OPB->_int - cached_vmglobal1) < cached_vmglobals_1)
+				{
+					// OK global write.
+					ptr = (prvm_eval_t *)(global1 + addr);
+				}
+				else if ((prvm_uint_t)OPB->_int - cached_vmentity0start < cached_entityfields)
+				{
+					if (!cached_allowworldwrites)
 					{
 						PRE_ERROR();
 						VM_Warning(prog, "Attempted assignment to world.%s (edictnum 0 field %i)\n", PRVM_GetString(prog, PRVM_ED_FieldAtOfs(prog, OPB->_int)->s_name), (int)OPB->_int);
+						// Perform entity write anyway.
 					}
+					ptr = (prvm_eval_t *)(cached_edictsfields + OPB->_int);
+				}
+				else
+				{
+					PRE_ERROR();
+					prog->error_cmd("%s attempted to write to an out of bounds edict (%i)", prog->name, (int)OPB->_int);
+					goto cleanup;
 				}
-				ptr = (prvm_eval_t *)(cached_edictsfields + OPB->_int);
 				ptr->_int = OPA->_int;
 				DISPATCH_OPCODE();
 			HANDLE_OPCODE(OP_STOREP_S):
-				if ((prvm_uint_t)OPB->_int - cached_entityfields >= cached_entityfieldsarea_entityfields)
+				if ((addr = (prvm_uint_t)OPB->_int - cached_vmentity1start) < cached_entityfieldsarea_entityfields)
 				{
-					if ((prvm_uint_t)OPB->_int >= cached_entityfieldsarea)
-					{
-						PRE_ERROR();
-						prog->error_cmd("%s attempted to write to an out of bounds edict (%i)", prog->name, (int)OPB->_int);
-						goto cleanup;
-					}
-					if ((prvm_uint_t)OPB->_int < cached_entityfields && !cached_allowworldwrites)
+					// OK entity write.
+					ptr = (prvm_eval_t *)(cached_edictsfields_entity1 + addr);
+				}
+				else if ((addr = (prvm_uint_t)OPB->_int - cached_vmglobal1) < cached_vmglobals_1)
+				{
+					// OK global write.
+					ptr = (prvm_eval_t *)(global1 + addr);
+				}
+				else if ((prvm_uint_t)OPB->_int - cached_vmentity0start < cached_entityfields)
+				{
+					if (!cached_allowworldwrites)
 					{
 						PRE_ERROR();
 						VM_Warning(prog, "Attempted assignment to world.%s (edictnum 0 field %i)\n", PRVM_GetString(prog, PRVM_ED_FieldAtOfs(prog, OPB->_int)->s_name), (int)OPB->_int);
+						// Perform entity write anyway.
 					}
+					ptr = (prvm_eval_t *)(cached_edictsfields + OPB->_int);
+				}
+				else
+				{
+					PRE_ERROR();
+					prog->error_cmd("%s attempted to write to an out of bounds edict (%i)", prog->name, (int)OPB->_int);
+					goto cleanup;
 				}
 				// refresh the garbage collection on the string - this guards
 				// against a certain sort of repeated migration to earlier
@@ -516,25 +540,35 @@ int i;
 				// being freed for being unused
 				if(prvm_garbagecollection_enable.integer)
 					PRVM_GetString(prog, OPA->_int);
-				ptr = (prvm_eval_t *)(cached_edictsfields + OPB->_int);
 				ptr->_int = OPA->_int;
 				DISPATCH_OPCODE();
 			HANDLE_OPCODE(OP_STOREP_V):
-				if ((prvm_uint_t)OPB->_int - cached_entityfields > (prvm_uint_t)cached_entityfieldsarea_entityfields_3)
+				if ((addr = (prvm_uint_t)OPB->_int - cached_vmentity1start) < cached_entityfieldsarea_entityfields_2)
 				{
-					if ((prvm_uint_t)OPB->_int > cached_entityfieldsarea_3)
-					{
-						PRE_ERROR();
-						prog->error_cmd("%s attempted to write to an out of bounds edict (%i)", prog->name, (int)OPB->_int);
-						goto cleanup;
-					}
-					if ((prvm_uint_t)OPB->_int < cached_entityfields && !cached_allowworldwrites)
+					// OK entity write.
+					ptr = (prvm_eval_t *)(cached_edictsfields_entity1 + addr);
+				}
+				else if ((addr = (prvm_uint_t)OPB->_int - cached_vmglobal1) < cached_vmglobals_3)
+				{
+					// OK global write.
+					ptr = (prvm_eval_t *)(global1 + OPB->_int);
+				}
+				else if ((prvm_uint_t)OPB->_int - cached_vmentity0start < cached_entityfields_2)
+				{
+					if (!cached_allowworldwrites)
 					{
 						PRE_ERROR();
 						VM_Warning(prog, "Attempted assignment to world.%s (edictnum 0 field %i)\n", PRVM_GetString(prog, PRVM_ED_FieldAtOfs(prog, OPB->_int)->s_name), (int)OPB->_int);
+						// Perform entity write anyway.
 					}
+					ptr = (prvm_eval_t *)(cached_edictsfields + OPB->_int);
+				}
+				else
+				{
+					PRE_ERROR();
+					prog->error_cmd("%s attempted to write to an out of bounds edict (%i)", prog->name, (int)OPB->_int);
+					goto cleanup;
 				}
-				ptr = (prvm_eval_t *)(cached_edictsfields + OPB->_int);
 				ptr->ivector[0] = OPA->ivector[0];
 				ptr->ivector[1] = OPA->ivector[1];
 				ptr->ivector[2] = OPA->ivector[2];
@@ -561,7 +595,7 @@ int i;
 					goto cleanup;
 				}
 #endif
-				OPC->_int = OPA->edict * cached_entityfields + OPB->_int;
+				OPC->_int = cached_vmentity0start + OPA->edict * cached_entityfields + OPB->_int;
 				DISPATCH_OPCODE();
 
 			HANDLE_OPCODE(OP_LOAD_F):
@@ -613,7 +647,7 @@ int i;
 					prog->error_cmd("%s attempted to read an out of bounds edict number", prog->name);
 					goto cleanup;
 				}
-				if ((prvm_uint_t)OPB->_int > cached_entityfields_3)
+				if ((prvm_uint_t)OPB->_int >= cached_entityfields_2)
 				{
 					PRE_ERROR();
 					prog->error_cmd("%s attempted to read an invalid field in an edict (%i)", prog->name, (int)OPB->_int);
@@ -691,7 +725,7 @@ int i;
 			HANDLE_OPCODE(OP_CALL6):
 			HANDLE_OPCODE(OP_CALL7):
 			HANDLE_OPCODE(OP_CALL8):
-#ifdef PRVMTIMEPROFILING 
+#ifdef PRVMTIMEPROFILING
 				tm = Sys_DirtyTime();
 				prog->xfunction->tprofile += (tm - starttm >= 0 && tm - starttm < 1800) ? (tm - starttm) : 0;
 				starttm = tm;
@@ -724,21 +758,14 @@ int i;
 					if (builtinnumber < prog->numbuiltins && prog->builtins[builtinnumber])
 					{
 						prog->builtins[builtinnumber](prog);
-#ifdef PRVMTIMEPROFILING 
+#ifdef PRVMTIMEPROFILING
 						tm = Sys_DirtyTime();
 						enterfunc->tprofile += (tm - starttm >= 0 && tm - starttm < 1800) ? (tm - starttm) : 0;
 						prog->xfunction->tbprofile += (tm - starttm >= 0 && tm - starttm < 1800) ? (tm - starttm) : 0;
 						starttm = tm;
 #endif
 						// builtins may cause ED_Alloc() to be called, update cached variables
-						cached_edictsfields = prog->edictsfields.fp;
-						cached_entityfields = prog->entityfields;
-						cached_entityfields_3 = prog->entityfields - 3;
-						cached_entityfieldsarea = prog->entityfieldsarea;
-						cached_entityfieldsarea_entityfields = prog->entityfieldsarea - prog->entityfields;
-						cached_entityfieldsarea_3 = prog->entityfieldsarea - 3;
-						cached_entityfieldsarea_entityfields_3 = prog->entityfieldsarea - prog->entityfields - 3;
-						cached_max_edicts = prog->max_edicts;
+						CACHE_CHANGING(NO_DECLARE);
 						// these do not change
 						//cached_statements = prog->statements;
 						//cached_allowworldwrites = prog->allowworldwrites;
@@ -757,7 +784,7 @@ int i;
 
 			HANDLE_OPCODE(OP_DONE):
 			HANDLE_OPCODE(OP_RETURN):
-#ifdef PRVMTIMEPROFILING 
+#ifdef PRVMTIMEPROFILING
 				tm = Sys_DirtyTime();
 				prog->xfunction->tprofile += (tm - starttm >= 0 && tm - starttm < 1800) ? (tm - starttm) : 0;
 				starttm = tm;
-- 
2.39.5