Add an interpreter because. (bug 5902, r=ds).
authorDavid Anderson <dvander@alliedmods.net>
Sun Sep 01 00:23:44 2013 -0700 (2013-09-01)
changeset 41163e58428d988f
parent 4115 b5511a1fa380
child 4117 34801001399a
Add an interpreter because. (bug 5902, r=ds).
configs/core.cfg
core/sm_srvcmds.cpp
core/sourcemod.cpp
public/sourcepawn/sp_vm_api.h
sourcepawn/jit/AMBuilder
sourcepawn/jit/Makefile.shell
sourcepawn/jit/dll_exports.cpp
sourcepawn/jit/engine2.cpp
sourcepawn/jit/engine2.h
sourcepawn/jit/interpreter.cpp
sourcepawn/jit/interpreter.h
sourcepawn/jit/jit_shared.h
sourcepawn/jit/sp_vm_basecontext.cpp
sourcepawn/jit/x86/jit_x86.cpp
sourcepawn/jit/x86/jit_x86.h
     1.1 --- a/configs/core.cfg	Sat Aug 31 19:50:36 2013 -0700
     1.2 +++ b/configs/core.cfg	Sun Sep 01 00:23:44 2013 -0700
     1.3 @@ -132,5 +132,13 @@
     1.4  	 * passed. You can disable this feature by setting the value to "0".
     1.5  	 */
     1.6  	"SlowScriptTimeout"	"8"
     1.7 +
     1.8 +	/**
     1.9 +	 * Disable the SourcePawn just-in-time compiler. This is intended for C++ developers using
    1.10 +	 * debugging tools and finding it difficult to inspect JIT stack frames. The interpreter
    1.11 +	 * is not as well-tested as the JIT and will make script execution roughly 10X slower, so
    1.12 +	 * it is not intended for general purpose use.
    1.13 +	 */
    1.14 +	"DisableJIT"	"no"
    1.15  }
    1.16  
     2.1 --- a/core/sm_srvcmds.cpp	Sat Aug 31 19:50:36 2013 -0700
     2.2 +++ b/core/sm_srvcmds.cpp	Sun Sep 01 00:23:44 2013 -0700
     2.3 @@ -334,7 +334,10 @@
     2.4  	{
     2.5  		ConsolePrint(" SourceMod Version Information:");
     2.6  		ConsolePrint("    SourceMod Version: %s", SM_VERSION_STRING);
     2.7 -		ConsolePrint("    SourcePawn Engine: %s (build %s)", g_pSourcePawn2->GetEngineName(), g_pSourcePawn2->GetVersionString());
     2.8 +		if (g_pSourcePawn2->IsJitEnabled())
     2.9 +			ConsolePrint("    SourcePawn Engine: %s (build %s)", g_pSourcePawn2->GetEngineName(), g_pSourcePawn2->GetVersionString());
    2.10 +		else
    2.11 +			ConsolePrint("    SourcePawn Engine: %s (build %s NO JIT)", g_pSourcePawn2->GetEngineName(), g_pSourcePawn2->GetVersionString());
    2.12  		ConsolePrint("    SourcePawn API: v1 = %d, v2 = %d", g_pSourcePawn->GetEngineAPIVersion(), g_pSourcePawn2->GetAPIVersion());
    2.13  		ConsolePrint("    Compiled on: %s %s", __DATE__, __TIME__);
    2.14  		ConsolePrint("    Build ID: %s", SM_BUILD_UNIQUEID);
     3.1 --- a/core/sourcemod.cpp	Sat Aug 31 19:50:36 2013 -0700
     3.2 +++ b/core/sourcemod.cpp	Sun Sep 01 00:23:44 2013 -0700
     3.3 @@ -61,6 +61,7 @@
     3.4  IGameConfig *g_pGameConf = NULL;
     3.5  bool g_Loaded = false;
     3.6  bool sm_show_debug_spew = false;
     3.7 +bool sm_disable_jit = false;
     3.8  
     3.9  typedef ISourcePawnEngine *(*GET_SP_V1)();
    3.10  typedef ISourcePawnEngine2 *(*GET_SP_V2)();
    3.11 @@ -125,6 +126,14 @@
    3.12  
    3.13  		return ConfigResult_Accept;
    3.14  	}
    3.15 +	else if (strcasecmp(key, "DisableJIT") == 0)
    3.16 +	{
    3.17 +		sm_disable_jit = (strcasecmp(value, "yes") == 0) ? true : false;
    3.18 +		if (g_pSourcePawn2)
    3.19 +			g_pSourcePawn2->SetJitEnabled(!sm_disable_jit);
    3.20 +
    3.21 +		return ConfigResult_Accept;
    3.22 +	}
    3.23  
    3.24  	return ConfigResult_Ignore;
    3.25  }
    3.26 @@ -242,6 +251,9 @@
    3.27  
    3.28  	g_pSourcePawn2->SetDebugListener(logicore.debugger);
    3.29  
    3.30 +	if (sm_disable_jit)
    3.31 +		g_pSourcePawn2->SetJitEnabled(!sm_disable_jit);
    3.32 +
    3.33  	sSourceModInitialized = true;
    3.34  
    3.35  	/* Hook this now so we can detect startup without calling StartSourceMod() */
     4.1 --- a/public/sourcepawn/sp_vm_api.h	Sat Aug 31 19:50:36 2013 -0700
     4.2 +++ b/public/sourcepawn/sp_vm_api.h	Sun Sep 01 00:23:44 2013 -0700
     4.3 @@ -40,7 +40,7 @@
     4.4  
     4.5  /** SourcePawn Engine API Version */
     4.6  #define SOURCEPAWN_ENGINE_API_VERSION	4
     4.7 -#define SOURCEPAWN_ENGINE2_API_VERSION	5
     4.8 +#define SOURCEPAWN_ENGINE2_API_VERSION	6
     4.9  
    4.10  #if !defined SOURCEMOD_BUILD
    4.11  #define SOURCEMOD_BUILD
    4.12 @@ -1295,6 +1295,21 @@
    4.13  		 * @return			True on success, false on failure.
    4.14  		 */
    4.15  		virtual bool InstallWatchdogTimer(size_t timeout_ms) =0;
    4.16 +
    4.17 +		/**
    4.18 +		 * @brief Sets whether the JIT is enabled or disabled.
    4.19 +		 *
    4.20 +		 * @param enabled	True or false to enable or disable.
    4.21 +		 * @return			True if successful, false otherwise.
    4.22 +		 */
    4.23 +		virtual bool SetJitEnabled(bool enabled) =0;
    4.24 +
    4.25 +		/**
    4.26 +		 * @brief Returns whether the JIT is enabled.
    4.27 +		 *
    4.28 +		 * @return			True if the JIT is enabled, false otherwise.
    4.29 +		 */
    4.30 +		virtual bool IsJitEnabled() =0;
    4.31  	};
    4.32  };
    4.33  
     5.1 --- a/sourcepawn/jit/AMBuilder	Sat Aug 31 19:50:36 2013 -0700
     5.2 +++ b/sourcepawn/jit/AMBuilder	Sun Sep 01 00:23:44 2013 -0700
     5.3 @@ -31,6 +31,7 @@
     5.4  	'sp_vm_engine.cpp',
     5.5  	'sp_vm_function.cpp',
     5.6  	'opcodes.cpp',
     5.7 +	'interpreter.cpp',
     5.8  	'watchdog_timer.cpp',
     5.9  	'x86/jit_x86.cpp',
    5.10  	'zlib/adler32.c',
     6.1 --- a/sourcepawn/jit/Makefile.shell	Sat Aug 31 19:50:36 2013 -0700
     6.2 +++ b/sourcepawn/jit/Makefile.shell	Sun Sep 01 00:23:44 2013 -0700
     6.3 @@ -20,6 +20,7 @@
     6.4  	jit_function.cpp	\
     6.5  	opcodes.cpp		\
     6.6  	watchdog_timer.cpp \
     6.7 +	interpreter.cpp \
     6.8  	md5/md5.cpp			\
     6.9  	zlib/adler32.c		\
    6.10  	zlib/compress.c		\
    6.11 @@ -51,7 +52,8 @@
    6.12  LINK = -m32 -lm -lpthread -lrt
    6.13  
    6.14  INCLUDE = -I. -I.. -I$(SMSDK)/public -I$(SMSDK)/public/jit -I$(SMSDK)/public/jit/x86 \
    6.15 -	-I$(SMSDK)/public/sourcepawn -I$(MMSOURCE17)/core/sourcehook -I$(SMSDK)/knight/shared -Ix86
    6.16 +	-I$(SMSDK)/public/sourcepawn -I$(MMSOURCE17)/core/sourcehook -I$(SMSDK)/knight/shared -Ix86 \
    6.17 +	-I$(SMSDK)/public/amtl
    6.18  
    6.19  CFLAGS += -D_LINUX -Dstricmp=strcasecmp -D_stricmp=strcasecmp -D_strnicmp=strncasecmp -Dstrnicmp=strncasecmp \
    6.20  	-D_snprintf=snprintf -D_vsnprintf=vsnprintf -D_alloca=alloca -Dstrcmpi=strcasecmp -Wall -DHAVE_STDINT_H \
     7.1 --- a/sourcepawn/jit/dll_exports.cpp	Sat Aug 31 19:50:36 2013 -0700
     7.2 +++ b/sourcepawn/jit/dll_exports.cpp	Sun Sep 01 00:23:44 2013 -0700
     7.3 @@ -219,6 +219,9 @@
     7.4  		return 1;
     7.5  	}
     7.6  
     7.7 +	if (getenv("DISABLE_JIT"))
     7.8 +		g_engine2.SetJitEnabled(false);
     7.9 +
    7.10  	ShellDebugListener debug;
    7.11  	g_engine1.SetDebugListener(&debug);
    7.12  	g_engine2.InstallWatchdogTimer(5000);
     8.1 --- a/sourcepawn/jit/engine2.cpp	Sat Aug 31 19:50:36 2013 -0700
     8.2 +++ b/sourcepawn/jit/engine2.cpp	Sun Sep 01 00:23:44 2013 -0700
     8.3 @@ -15,6 +15,7 @@
     8.4  SourcePawnEngine2::SourcePawnEngine2()
     8.5  {
     8.6  	m_Profiler = NULL;
     8.7 +	jit_enabled_ = true;
     8.8  }
     8.9  
    8.10  IPluginRuntime *SourcePawnEngine2::LoadPlugin(ICompilation *co, const char *file, int *err)
    8.11 @@ -146,7 +147,7 @@
    8.12  
    8.13  const char *SourcePawnEngine2::GetEngineName()
    8.14  {
    8.15 -	return "SourcePawn 1.1, jit-x86";
    8.16 +	return "SourcePawn 1.2, jit-x86";
    8.17  }
    8.18  
    8.19  const char *SourcePawnEngine2::GetVersionString()
     9.1 --- a/sourcepawn/jit/engine2.h	Sat Aug 31 19:50:36 2013 -0700
     9.2 +++ b/sourcepawn/jit/engine2.h	Sun Sep 01 00:23:44 2013 -0700
     9.3 @@ -1,3 +1,4 @@
     9.4 +// vim: set ts=4 sw=4 tw=99 noet:
     9.5  #ifndef _INCLUDE_SOURCEPAWN_ENGINE_2_H_
     9.6  #define _INCLUDE_SOURCEPAWN_ENGINE_2_H_
     9.7  
     9.8 @@ -27,10 +28,20 @@
     9.9  		void Shutdown();
    9.10  		IPluginRuntime *CreateEmptyRuntime(const char *name, uint32_t memory);
    9.11  		bool InstallWatchdogTimer(size_t timeout_ms);
    9.12 +
    9.13 +		bool SetJitEnabled(bool enabled) {
    9.14 +			jit_enabled_ = enabled;
    9.15 +			return true;
    9.16 +	    }
    9.17 +
    9.18 +		bool IsJitEnabled() {
    9.19 +			return jit_enabled_;
    9.20 +		}
    9.21  	public:
    9.22  		IProfiler *GetProfiler();
    9.23  	private:
    9.24  		IProfiler *m_Profiler;
    9.25 +		bool jit_enabled_;
    9.26  	};
    9.27  }
    9.28  
    10.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    10.2 +++ b/sourcepawn/jit/interpreter.cpp	Sun Sep 01 00:23:44 2013 -0700
    10.3 @@ -0,0 +1,965 @@
    10.4 +// vim: set ts=8 sts=2 sw=2 tw=99 et:
    10.5 +//
    10.6 +// This file is part of SourcePawn.
    10.7 +// 
    10.8 +// SourcePawn is free software: you can redistribute it and/or modify
    10.9 +// it under the terms of the GNU General Public License as published by
   10.10 +// the Free Software Foundation, either version 3 of the License, or
   10.11 +// (at your option) any later version.
   10.12 +// 
   10.13 +// SourcePawn is distributed in the hope that it will be useful,
   10.14 +// but WITHOUT ANY WARRANTY; without even the implied warranty of
   10.15 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   10.16 +// GNU General Public License for more details.
   10.17 +// 
   10.18 +// You should have received a copy of the GNU General Public License
   10.19 +// along with SourcePawn.  If not, see <http://www.gnu.org/licenses/>.
   10.20 +#include <limits.h>
   10.21 +#include <string.h>
   10.22 +#include "interpreter.h"
   10.23 +#include "opcodes.h"
   10.24 +#include "watchdog_timer.h"
   10.25 +
   10.26 +#define STACK_MARGIN 64
   10.27 +
   10.28 +using namespace SourcePawn;
   10.29 +
   10.30 +static inline bool
   10.31 +IsValidOffset(uint32_t cip)
   10.32 +{
   10.33 +  return cip % 4 == 0;
   10.34 +}
   10.35 +
   10.36 +static inline cell_t
   10.37 +Read(const sp_plugin_t *plugin, cell_t offset)
   10.38 +{
   10.39 +  return *reinterpret_cast<cell_t *>(plugin->memory + offset);
   10.40 +}
   10.41 +
   10.42 +static inline void
   10.43 +Write(const sp_plugin_t *plugin, cell_t offset, cell_t value)
   10.44 +{
   10.45 +  *reinterpret_cast<cell_t *>(plugin->memory + offset) = value;
   10.46 +}
   10.47 +
   10.48 +static inline cell_t *
   10.49 +Jump(const sp_plugin_t *plugin, sp_context_t *ctx, cell_t target)
   10.50 +{
   10.51 +  if (!IsValidOffset(target) || uint32_t(target) >= plugin->pcode_size) {
   10.52 +    ctx->err = SP_ERROR_INVALID_INSTRUCTION;
   10.53 +    return NULL;
   10.54 +  }
   10.55 +
   10.56 +  return reinterpret_cast<cell_t *>(plugin->pcode + target);
   10.57 +}
   10.58 +
   10.59 +static inline cell_t *
   10.60 +JumpTarget(const sp_plugin_t *plugin, sp_context_t *ctx, cell_t *cip, bool cond)
   10.61 +{
   10.62 +  if (!cond)
   10.63 +    return cip + 1;
   10.64 +
   10.65 +  cell_t target = *cip;
   10.66 +  if (!IsValidOffset(target) || uint32_t(target) >= plugin->pcode_size) {
   10.67 +    ctx->err = SP_ERROR_INVALID_INSTRUCTION;
   10.68 +    return NULL;
   10.69 +  }
   10.70 +
   10.71 +  cell_t *next = reinterpret_cast<cell_t *>(plugin->pcode + target);
   10.72 +  if (next < cip && !g_WatchdogTimer.HandleInterrupt()) {
   10.73 +    ctx->err = SP_ERROR_TIMEOUT;
   10.74 +    return NULL;
   10.75 +  }
   10.76 +
   10.77 +  return next;
   10.78 +}
   10.79 +
   10.80 +static inline bool
   10.81 +CheckAddress(const sp_plugin_t *plugin, sp_context_t *ctx, cell_t *stk, cell_t addr)
   10.82 +{
   10.83 +  if (uint32_t(addr) >= plugin->mem_size) {
   10.84 +    ctx->err = SP_ERROR_MEMACCESS;
   10.85 +    return false;
   10.86 +  }
   10.87 +
   10.88 +  if (addr < ctx->hp)
   10.89 +    return true;
   10.90 +
   10.91 +  if (reinterpret_cast<cell_t *>(plugin->memory + addr) < stk) {
   10.92 +    ctx->err = SP_ERROR_MEMACCESS;
   10.93 +    return false;
   10.94 +  }
   10.95 +
   10.96 +  return true;
   10.97 +}
   10.98 +
   10.99 +int
  10.100 +PopTrackerAndSetHeap(BaseRuntime *rt)
  10.101 +{
  10.102 +  sp_context_t *ctx = rt->GetBaseContext()->GetCtx();
  10.103 +  tracker_t *trk = ctx->tracker;
  10.104 +  assert(trk->pCur > trk->pBase);
  10.105 +
  10.106 +  trk->pCur--;
  10.107 +  if (trk->pCur < trk->pBase)
  10.108 +    return SP_ERROR_TRACKER_BOUNDS;
  10.109 +
  10.110 +  ucell_t amt = *trk->pCur;
  10.111 +  if (amt > (ctx->hp - rt->plugin()->data_size))
  10.112 +    return SP_ERROR_HEAPMIN;
  10.113 +
  10.114 +  ctx->hp -= amt;
  10.115 +  return SP_ERROR_NONE;
  10.116 +}
  10.117 +
  10.118 +int
  10.119 +PushTracker(sp_context_t *ctx, size_t amount)
  10.120 +{
  10.121 +  tracker_t *trk = ctx->tracker;
  10.122 +
  10.123 +  if ((size_t)(trk->pCur - trk->pBase) >= trk->size)
  10.124 +    return SP_ERROR_TRACKER_BOUNDS;
  10.125 +
  10.126 +  if (trk->pCur + 1 - (trk->pBase + trk->size) == 0) {
  10.127 +    size_t disp = trk->size - 1;
  10.128 +    trk->size *= 2;
  10.129 +    trk->pBase = (ucell_t *)realloc(trk->pBase, trk->size * sizeof(cell_t));
  10.130 +
  10.131 +    if (!trk->pBase)
  10.132 +      return SP_ERROR_TRACKER_BOUNDS;
  10.133 +
  10.134 +    trk->pCur = trk->pBase + disp;
  10.135 +  }
  10.136 +
  10.137 +  *trk->pCur++ = amount;
  10.138 +  return SP_ERROR_NONE;
  10.139 +}
  10.140 +
  10.141 +cell_t
  10.142 +NativeCallback(sp_context_t *ctx, ucell_t native_idx, cell_t *params)
  10.143 +{
  10.144 +  cell_t save_sp = ctx->sp;
  10.145 +  cell_t save_hp = ctx->hp;
  10.146 +
  10.147 +  ctx->n_idx = native_idx;
  10.148 +
  10.149 +  sp_native_t *native = &ctx->plugin->natives[native_idx];
  10.150 +
  10.151 +  if (native->status == SP_NATIVE_UNBOUND) {
  10.152 +    ctx->n_err = SP_ERROR_INVALID_NATIVE;
  10.153 +    return 0;
  10.154 +  }
  10.155 +
  10.156 +  cell_t result = native->pfn(ctx->basecx, params);
  10.157 +
  10.158 +  if (ctx->n_err != SP_ERROR_NONE)
  10.159 +    return result;
  10.160 +
  10.161 +  if (save_sp != ctx->sp) {
  10.162 +    ctx->n_err = SP_ERROR_STACKLEAK;
  10.163 +    return result;
  10.164 +  }
  10.165 +  if (save_hp != ctx->hp) {
  10.166 +    ctx->n_err = SP_ERROR_HEAPLEAK;
  10.167 +    return result;
  10.168 +  }
  10.169 +
  10.170 +  return result;
  10.171 +}
  10.172 +
  10.173 +static inline bool
  10.174 +GenerateArray(BaseRuntime *rt, sp_context_t *ctx, cell_t dims, cell_t *stk, bool autozero)
  10.175 +{
  10.176 +  if (dims == 1) {
  10.177 +    uint32_t size = *stk;
  10.178 +    if (size == 0 || !ke::IsUint32MultiplySafe(size, 4)) {
  10.179 +      ctx->err = SP_ERROR_ARRAY_TOO_BIG;
  10.180 +      return false;
  10.181 +    }
  10.182 +    *stk = ctx->hp;
  10.183 +
  10.184 +    uint32_t bytes = size * 4;
  10.185 +
  10.186 +    ctx->hp += bytes;
  10.187 +    if (uintptr_t(ctx->plugin->memory + ctx->hp) >= uintptr_t(stk)) {
  10.188 +      ctx->err = SP_ERROR_HEAPLOW;
  10.189 +      return false;
  10.190 +    }
  10.191 +
  10.192 +    if ((ctx->err = PushTracker(ctx, bytes)) != SP_ERROR_NONE)
  10.193 +      return false;
  10.194 +
  10.195 +    if (autozero)
  10.196 +      memset(ctx->plugin->memory + ctx->hp, 0, bytes);
  10.197 +
  10.198 +    return true;
  10.199 +  }
  10.200 +
  10.201 +  if ((ctx->err = GenerateFullArray(rt, dims, stk, autozero)) != SP_ERROR_NONE)
  10.202 +    return false;
  10.203 +
  10.204 +  return true;
  10.205 +}
  10.206 +
  10.207 +int
  10.208 +Interpret(BaseRuntime *rt, uint32_t aCodeStart, cell_t *rval)
  10.209 +{
  10.210 +  const sp_plugin_t *plugin = rt->plugin();
  10.211 +  cell_t *code = reinterpret_cast<cell_t *>(plugin->pcode);
  10.212 +  cell_t *codeend = reinterpret_cast<cell_t *>(plugin->pcode + plugin->pcode_size);
  10.213 +
  10.214 +  if (!IsValidOffset(aCodeStart) || aCodeStart > plugin->pcode_size)
  10.215 +    return SP_ERROR_INVALID_INSTRUCTION;
  10.216 +
  10.217 +  sp_context_t *ctx = rt->GetBaseContext()->GetCtx();
  10.218 +  ctx->err = SP_ERROR_NONE;
  10.219 +
  10.220 +  // Save the original frm. BaseContext won't, and if we error, we won't hit
  10.221 +  // the stack unwinding code.
  10.222 +  cell_t orig_frm = ctx->frm;
  10.223 +
  10.224 +  cell_t pri = 0;
  10.225 +  cell_t alt = 0;
  10.226 +  cell_t *cip = code + (aCodeStart / 4);
  10.227 +  cell_t *stk = reinterpret_cast<cell_t *>(plugin->memory + ctx->sp);
  10.228 +
  10.229 +  for (;;) {
  10.230 +    if (cip >= codeend) {
  10.231 +      ctx->err = SP_ERROR_INVALID_INSTRUCTION;
  10.232 +      goto error;
  10.233 +    }
  10.234 +
  10.235 +#if 0
  10.236 +    SpewOpcode(plugin, reinterpret_cast<cell_t *>(plugin->pcode + aCodeStart), cip);
  10.237 +#endif
  10.238 +
  10.239 +    OPCODE op = (OPCODE)*cip++;
  10.240 +
  10.241 +    switch (op) {
  10.242 +      case OP_MOVE_PRI:
  10.243 +        pri = alt;
  10.244 +        break;
  10.245 +      case OP_MOVE_ALT:
  10.246 +        alt = pri;
  10.247 +        break;
  10.248 +
  10.249 +      case OP_XCHG:
  10.250 +      {
  10.251 +        cell_t tmp = pri;
  10.252 +        pri = alt;
  10.253 +        alt = tmp;
  10.254 +        break;
  10.255 +      }
  10.256 +
  10.257 +      case OP_ZERO:
  10.258 +        Write(plugin, *cip++, 0);
  10.259 +        break;
  10.260 +
  10.261 +      case OP_ZERO_S:
  10.262 +        Write(plugin, *cip++, 0);
  10.263 +        break;
  10.264 +
  10.265 +      case OP_PUSH_PRI:
  10.266 +        *--stk = pri;
  10.267 +        break;
  10.268 +      case OP_PUSH_ALT:
  10.269 +        *--stk = alt;
  10.270 +        break;
  10.271 +
  10.272 +      case OP_PUSH_C:
  10.273 +      case OP_PUSH2_C:
  10.274 +      case OP_PUSH3_C:
  10.275 +      case OP_PUSH4_C:
  10.276 +      case OP_PUSH5_C:
  10.277 +      {
  10.278 +        int n = 1;
  10.279 +        if (op >= OP_PUSH2_C)
  10.280 +          n = ((op - OP_PUSH2_C) / 4) + 2;
  10.281 +
  10.282 +        int i = 1;
  10.283 +        do {
  10.284 +          *--stk = *cip++;
  10.285 +        } while (i++ < n);
  10.286 +        break;
  10.287 +      }
  10.288 +
  10.289 +      case OP_PUSH_ADR:
  10.290 +      case OP_PUSH2_ADR:
  10.291 +      case OP_PUSH3_ADR:
  10.292 +      case OP_PUSH4_ADR:
  10.293 +      case OP_PUSH5_ADR:
  10.294 +      {
  10.295 +        int n = 1;
  10.296 +        if (op >= OP_PUSH2_ADR)
  10.297 +          n = ((op - OP_PUSH2_ADR) / 4) + 2;
  10.298 +
  10.299 +        int i = 1;
  10.300 +
  10.301 +        do {
  10.302 +          cell_t addr = ctx->frm + *cip++;
  10.303 +          *--stk = addr;
  10.304 +        } while (i++ < n);
  10.305 +        break;
  10.306 +      }
  10.307 +
  10.308 +      case OP_PUSH_S:
  10.309 +      case OP_PUSH2_S:
  10.310 +      case OP_PUSH3_S:
  10.311 +      case OP_PUSH4_S:
  10.312 +      case OP_PUSH5_S:
  10.313 +      {
  10.314 +        int n = 1;
  10.315 +        if (op >= OP_PUSH2_S)
  10.316 +          n = ((op - OP_PUSH2_S) / 4) + 2;
  10.317 +
  10.318 +        int i = 1;
  10.319 +        do {
  10.320 +          cell_t value = Read(plugin, ctx->frm + *cip++);
  10.321 +          *--stk = value;
  10.322 +        } while (i++ < n);
  10.323 +        break;
  10.324 +      }
  10.325 +
  10.326 +      case OP_PUSH:
  10.327 +      case OP_PUSH2:
  10.328 +      case OP_PUSH3:
  10.329 +      case OP_PUSH4:
  10.330 +      case OP_PUSH5:
  10.331 +      {
  10.332 +        int n = 1;
  10.333 +        if (op >= OP_PUSH2)
  10.334 +          n = ((op - OP_PUSH2) / 4) + 2;
  10.335 +
  10.336 +        int i = 1;
  10.337 +        do {
  10.338 +          cell_t value = Read(plugin, *cip++);
  10.339 +          *--stk = value;
  10.340 +        } while (i++ < n);
  10.341 +        break;
  10.342 +      }
  10.343 +
  10.344 +      case OP_ZERO_PRI:
  10.345 +        pri = 0;
  10.346 +        break;
  10.347 +      case OP_ZERO_ALT:
  10.348 +        alt = 0;
  10.349 +        break;
  10.350 +
  10.351 +      case OP_ADD:
  10.352 +        pri += alt;
  10.353 +        break;
  10.354 +
  10.355 +      case OP_SUB:
  10.356 +        pri -= alt;
  10.357 +        break;
  10.358 +
  10.359 +      case OP_SUB_ALT:
  10.360 +        pri = alt - pri;
  10.361 +        break;
  10.362 +
  10.363 +      case OP_PROC:
  10.364 +      {
  10.365 +        *--stk = ctx->frm;
  10.366 +        *--stk = 0;
  10.367 +        ctx->frm = uintptr_t(stk) - uintptr_t(plugin->memory);
  10.368 +        break;
  10.369 +      }
  10.370 +
  10.371 +      case OP_IDXADDR_B:
  10.372 +        pri <<= *cip++;
  10.373 +        pri += alt;
  10.374 +        break;
  10.375 +
  10.376 +      case OP_SHL:
  10.377 +        pri <<= alt;
  10.378 +        break;
  10.379 +
  10.380 +      case OP_SHR:
  10.381 +        pri = unsigned(pri) >> unsigned(alt);
  10.382 +        break;
  10.383 +
  10.384 +      case OP_SSHR:
  10.385 +        pri >>= alt;
  10.386 +        break;
  10.387 +
  10.388 +      case OP_SHL_C_PRI:
  10.389 +        pri <<= *cip++;
  10.390 +        break;
  10.391 +      case OP_SHL_C_ALT:
  10.392 +        alt <<= *cip++;
  10.393 +        break;
  10.394 +
  10.395 +      case OP_SHR_C_PRI:
  10.396 +        pri >>= *cip++;
  10.397 +        break;
  10.398 +      case OP_SHR_C_ALT:
  10.399 +        alt >>= *cip++;
  10.400 +        break;
  10.401 +
  10.402 +      case OP_SMUL:
  10.403 +        pri *= alt;
  10.404 +        break;
  10.405 +
  10.406 +      case OP_NOT:
  10.407 +        pri = pri ? 0 : 1;
  10.408 +        break;
  10.409 +
  10.410 +      case OP_NEG:
  10.411 +        pri = -pri;
  10.412 +        break;
  10.413 +
  10.414 +      case OP_XOR:
  10.415 +        pri ^= alt;
  10.416 +        break;
  10.417 +
  10.418 +      case OP_OR:
  10.419 +        pri |= alt;
  10.420 +        break;
  10.421 +
  10.422 +      case OP_AND:
  10.423 +        pri &= alt;
  10.424 +        break;
  10.425 +
  10.426 +      case OP_INVERT:
  10.427 +        pri = ~pri;
  10.428 +        break;
  10.429 +
  10.430 +      case OP_ADD_C:
  10.431 +        pri += *cip++;
  10.432 +        break;
  10.433 +
  10.434 +      case OP_SMUL_C:
  10.435 +        pri *= *cip++;
  10.436 +        break;
  10.437 +
  10.438 +      case OP_EQ:
  10.439 +        pri = pri == alt;
  10.440 +        break;
  10.441 +
  10.442 +      case OP_NEQ:
  10.443 +        pri = pri != alt;
  10.444 +        break;
  10.445 +
  10.446 +      case OP_SLESS:
  10.447 +        pri = pri < alt;
  10.448 +        break;
  10.449 +
  10.450 +      case OP_SLEQ:
  10.451 +        pri = pri <= alt;
  10.452 +        break;
  10.453 +
  10.454 +      case OP_SGRTR:
  10.455 +        pri = pri > alt;
  10.456 +        break;
  10.457 +        
  10.458 +      case OP_SGEQ:
  10.459 +        pri = pri >= alt;
  10.460 +        break;
  10.461 +
  10.462 +      case OP_EQ_C_PRI:
  10.463 +        pri = pri == *cip++;
  10.464 +        break;
  10.465 +      case OP_EQ_C_ALT:
  10.466 +        pri = alt == *cip++;
  10.467 +        break;
  10.468 +
  10.469 +      case OP_INC_PRI:
  10.470 +        pri++;
  10.471 +        break;
  10.472 +      case OP_INC_ALT:
  10.473 +        alt++;
  10.474 +        break;
  10.475 +
  10.476 +      case OP_INC:
  10.477 +      {
  10.478 +        cell_t offset = *cip++;
  10.479 +        Write(plugin, offset, Read(plugin, offset) + 1);
  10.480 +        break;
  10.481 +      }
  10.482 +
  10.483 +      case OP_INC_S:
  10.484 +      {
  10.485 +        cell_t offset = *cip++;
  10.486 +        cell_t value = Read(plugin, ctx->frm + offset);
  10.487 +        Write(plugin, ctx->frm + offset, value + 1);
  10.488 +        break;
  10.489 +      }
  10.490 +
  10.491 +      case OP_INC_I:
  10.492 +        if (!CheckAddress(plugin, ctx, stk, pri))
  10.493 +          goto error;
  10.494 +        Write(plugin, pri, Read(plugin, pri) + 1);
  10.495 +        break;
  10.496 +
  10.497 +      case OP_DEC_PRI:
  10.498 +        pri--;
  10.499 +        break;
  10.500 +      case OP_DEC_ALT:
  10.501 +        alt--;
  10.502 +        break;
  10.503 +
  10.504 +      case OP_DEC:
  10.505 +      {
  10.506 +        cell_t offset = *cip++;
  10.507 +        Write(plugin, offset, Read(plugin, offset) - 1);
  10.508 +        break;
  10.509 +      }
  10.510 +
  10.511 +      case OP_DEC_S:
  10.512 +      {
  10.513 +        cell_t offset = *cip++;
  10.514 +        cell_t value = Read(plugin, ctx->frm + offset);
  10.515 +        Write(plugin, ctx->frm + offset, value - 1);
  10.516 +        break;
  10.517 +      }
  10.518 +
  10.519 +      case OP_DEC_I:
  10.520 +        if (!CheckAddress(plugin, ctx, stk, pri))
  10.521 +          goto error;
  10.522 +        Write(plugin, pri, Read(plugin, pri) - 1);
  10.523 +        break;
  10.524 +
  10.525 +      case OP_LOAD_PRI:
  10.526 +        pri = Read(plugin, *cip++);
  10.527 +        break;
  10.528 +      case OP_LOAD_ALT:
  10.529 +        alt = Read(plugin, *cip++);
  10.530 +        break;
  10.531 +
  10.532 +      case OP_LOAD_S_PRI:
  10.533 +        pri = Read(plugin, ctx->frm + *cip++);
  10.534 +        break;
  10.535 +      case OP_LOAD_S_ALT:
  10.536 +        alt = Read(plugin, ctx->frm + *cip++);
  10.537 +        break;
  10.538 +
  10.539 +      case OP_LOAD_S_BOTH:
  10.540 +        pri = Read(plugin, ctx->frm + *cip++);
  10.541 +        alt = Read(plugin, ctx->frm + *cip++);
  10.542 +        break;
  10.543 +
  10.544 +      case OP_LREF_S_PRI:
  10.545 +      {
  10.546 +        pri = Read(plugin, ctx->frm + *cip++);
  10.547 +        pri = Read(plugin, pri);
  10.548 +        break;
  10.549 +      }
  10.550 +
  10.551 +      case OP_LREF_S_ALT:
  10.552 +      {
  10.553 +        alt = Read(plugin, ctx->frm + *cip++);
  10.554 +        alt = Read(plugin, alt);
  10.555 +        break;
  10.556 +      }
  10.557 +
  10.558 +      case OP_CONST_PRI:
  10.559 +        pri = *cip++;
  10.560 +        break;
  10.561 +      case OP_CONST_ALT:
  10.562 +        alt = *cip++;
  10.563 +        break;
  10.564 +
  10.565 +      case OP_ADDR_PRI:
  10.566 +        pri = ctx->frm + *cip++;
  10.567 +        break;
  10.568 +      case OP_ADDR_ALT:
  10.569 +        alt = ctx->frm + *cip++;
  10.570 +        break;
  10.571 +
  10.572 +      case OP_STOR_PRI:
  10.573 +        Write(plugin, *cip++, pri);
  10.574 +        break;
  10.575 +      case OP_STOR_ALT:
  10.576 +        Write(plugin, *cip++, alt);
  10.577 +        break;
  10.578 +
  10.579 +      case OP_STOR_S_PRI:
  10.580 +        Write(plugin, ctx->frm + *cip++, pri);
  10.581 +        break;
  10.582 +      case OP_STOR_S_ALT:
  10.583 +        Write(plugin, ctx->frm +*cip++, alt);
  10.584 +        break;
  10.585 +
  10.586 +      case OP_IDXADDR:
  10.587 +        pri = alt + pri * 4;
  10.588 +        break;
  10.589 +
  10.590 +      case OP_SREF_S_PRI:
  10.591 +      {
  10.592 +        cell_t offset = *cip++;
  10.593 +        cell_t addr = Read(plugin, ctx->frm + offset);
  10.594 +        Write(plugin, addr, pri);
  10.595 +        break;
  10.596 +      }
  10.597 +
  10.598 +      case OP_SREF_S_ALT:
  10.599 +      {
  10.600 +        cell_t offset = *cip++;
  10.601 +        cell_t addr = Read(plugin, ctx->frm + offset);
  10.602 +        Write(plugin, addr, alt);
  10.603 +        break;
  10.604 +      }
  10.605 +
  10.606 +      case OP_POP_PRI:
  10.607 +        pri = *stk++;
  10.608 +        break;
  10.609 +      case OP_POP_ALT:
  10.610 +        alt = *stk++;
  10.611 +        break;
  10.612 +
  10.613 +      case OP_SWAP_PRI:
  10.614 +      case OP_SWAP_ALT:
  10.615 +      {
  10.616 +        cell_t reg = (op == OP_SREF_S_PRI) ? pri : alt;
  10.617 +        cell_t temp = *stk;
  10.618 +        *stk = reg;
  10.619 +        reg = temp;
  10.620 +        break;
  10.621 +      }
  10.622 +
  10.623 +      case OP_LIDX:
  10.624 +        pri = alt + pri * 4;
  10.625 +        if (!CheckAddress(plugin, ctx, stk, pri))
  10.626 +          goto error;
  10.627 +        pri = Read(plugin, pri);
  10.628 +        break;
  10.629 +
  10.630 +      case OP_LIDX_B:
  10.631 +      {
  10.632 +        cell_t val = *cip++;
  10.633 +        pri = alt + (pri << val);
  10.634 +        if (!CheckAddress(plugin, ctx, stk, pri))
  10.635 +          goto error;
  10.636 +        pri = Read(plugin, pri);
  10.637 +        break;
  10.638 +      }
  10.639 +
  10.640 +      case OP_CONST:
  10.641 +      {
  10.642 +        cell_t offset = *cip++;
  10.643 +        cell_t value = *cip++;
  10.644 +        Write(plugin, offset, value);
  10.645 +        break;
  10.646 +      }
  10.647 +
  10.648 +      case OP_CONST_S:
  10.649 +      {
  10.650 +        cell_t offset = *cip++;
  10.651 +        cell_t value = *cip++;
  10.652 +        Write(plugin, ctx->frm + offset, value);
  10.653 +        break;
  10.654 +      }
  10.655 +
  10.656 +      case OP_LOAD_I:
  10.657 +        if (!CheckAddress(plugin, ctx, stk, pri))
  10.658 +          goto error;
  10.659 +        pri = Read(plugin, pri);
  10.660 +        break;
  10.661 +
  10.662 +      case OP_STOR_I:
  10.663 +        if (!CheckAddress(plugin, ctx, stk, alt))
  10.664 +          goto error;
  10.665 +        Write(plugin, alt, pri);
  10.666 +        break;
  10.667 +
  10.668 +      case OP_SDIV:
  10.669 +      case OP_SDIV_ALT:
  10.670 +      {
  10.671 +        cell_t dividend = (op == OP_SDIV) ? pri : alt;
  10.672 +        cell_t divisor = (op == OP_SDIV) ? alt : pri;
  10.673 +        if (divisor == 0) {
  10.674 +          ctx->err = SP_ERROR_DIVIDE_BY_ZERO;
  10.675 +          goto error;
  10.676 +        }
  10.677 +        if (dividend == INT_MIN && divisor == -1) {
  10.678 +          ctx->err = SP_ERROR_INTEGER_OVERFLOW;
  10.679 +          goto error;
  10.680 +        }
  10.681 +        pri = dividend / divisor;
  10.682 +        alt = dividend % divisor;
  10.683 +        break;
  10.684 +      }
  10.685 +      
  10.686 +      case OP_LODB_I:
  10.687 +      {
  10.688 +        cell_t val = *cip++;
  10.689 +        if (!CheckAddress(plugin, ctx, stk, pri))
  10.690 +          goto error;
  10.691 +        pri = Read(plugin, pri);
  10.692 +        if (val == 1)
  10.693 +          pri &= 0xff;
  10.694 +        else if (val == 2)
  10.695 +          pri &= 0xffff;
  10.696 +        break;
  10.697 +      }
  10.698 +
  10.699 +      case OP_STRB_I:
  10.700 +      {
  10.701 +        cell_t val = *cip++;
  10.702 +        if (!CheckAddress(plugin, ctx, stk, alt))
  10.703 +          goto error;
  10.704 +        if (val == 1)
  10.705 +          *reinterpret_cast<int8_t *>(plugin->memory + alt) = pri;
  10.706 +        else if (val == 2)
  10.707 +          *reinterpret_cast<int16_t *>(plugin->memory + alt) = pri;
  10.708 +        else if (val == 4)
  10.709 +          *reinterpret_cast<int32_t *>(plugin->memory + alt) = pri;
  10.710 +        break;
  10.711 +      }
  10.712 +
  10.713 +      case OP_RETN:
  10.714 +      {
  10.715 +        stk++;
  10.716 +        ctx->frm = *stk++;
  10.717 +        stk += *stk + 1;
  10.718 +        *rval = pri;
  10.719 +        ctx->err = SP_ERROR_NONE;
  10.720 +        goto done;
  10.721 +      }
  10.722 +
  10.723 +      case OP_MOVS:
  10.724 +      {
  10.725 +        uint8_t *src = plugin->memory + pri;
  10.726 +        uint8_t *dest = plugin->memory + alt;
  10.727 +        memcpy(dest, src, *cip++);
  10.728 +        break;
  10.729 +      }
  10.730 +
  10.731 +      case OP_FILL:
  10.732 +      {
  10.733 +        uint8_t *dest = plugin->memory + alt;
  10.734 +        memset(dest, pri, *cip++);
  10.735 +        break;
  10.736 +      }
  10.737 +
  10.738 +      case OP_STRADJUST_PRI:
  10.739 +        pri += 4;
  10.740 +        pri >>= 2;
  10.741 +        break;
  10.742 +
  10.743 +      case OP_STACK:
  10.744 +      {
  10.745 +        cell_t amount = *cip++;
  10.746 +        if (!IsValidOffset(amount)) {
  10.747 +          ctx->err = SP_ERROR_INVALID_INSTRUCTION;
  10.748 +          goto error;
  10.749 +        }
  10.750 +
  10.751 +        stk += amount / 4;
  10.752 +        if (amount > 0) {
  10.753 +          if (uintptr_t(stk) >= uintptr_t(plugin->memory + plugin->mem_size)) {
  10.754 +            ctx->err = SP_ERROR_STACKMIN;
  10.755 +            goto error;
  10.756 +          }
  10.757 +        } else {
  10.758 +          if (uintptr_t(stk) < uintptr_t(plugin->memory + ctx->hp + STACK_MARGIN)) {
  10.759 +            ctx->err = SP_ERROR_STACKLOW;
  10.760 +            goto error;
  10.761 +          }
  10.762 +        }
  10.763 +        break;
  10.764 +      }
  10.765 +
  10.766 +      case OP_HEAP:
  10.767 +      {
  10.768 +        cell_t amount = *cip++;
  10.769 +
  10.770 +        alt = ctx->hp;
  10.771 +        ctx->hp += amount;
  10.772 +
  10.773 +        if (amount > 0) {
  10.774 +          if (uintptr_t(plugin->memory + ctx->hp) > uintptr_t(stk)) {
  10.775 +            ctx->err = SP_ERROR_HEAPLOW;
  10.776 +            goto error;
  10.777 +          }
  10.778 +        } else {
  10.779 +          if (uint32_t(ctx->hp) < plugin->data_size) {
  10.780 +            ctx->err = SP_ERROR_HEAPMIN;
  10.781 +            goto error;
  10.782 +          }
  10.783 +        }
  10.784 +        break;
  10.785 +      }
  10.786 +
  10.787 +      case OP_JUMP:
  10.788 +        if ((cip = JumpTarget(plugin, ctx, cip, true)) == NULL)
  10.789 +          goto error;
  10.790 +        break;
  10.791 +
  10.792 +      case OP_JZER:
  10.793 +        if ((cip = JumpTarget(plugin, ctx, cip, pri == 0)) == NULL)
  10.794 +          goto error;
  10.795 +        break;
  10.796 +      case OP_JNZ:
  10.797 +        if ((cip = JumpTarget(plugin, ctx, cip, pri != 0)) == NULL)
  10.798 +          goto error;
  10.799 +        break;
  10.800 +
  10.801 +      case OP_JEQ:
  10.802 +        if ((cip = JumpTarget(plugin, ctx, cip, pri == alt)) == NULL)
  10.803 +          goto error;
  10.804 +        break;
  10.805 +      case OP_JNEQ:
  10.806 +        if ((cip = JumpTarget(plugin, ctx, cip, pri != alt)) == NULL)
  10.807 +          goto error;
  10.808 +        break;
  10.809 +      case OP_JSLESS:
  10.810 +        if ((cip = JumpTarget(plugin, ctx, cip, pri < alt)) == NULL)
  10.811 +          goto error;
  10.812 +        break;
  10.813 +      case OP_JSLEQ:
  10.814 +        if ((cip = JumpTarget(plugin, ctx, cip, pri <= alt)) == NULL)
  10.815 +          goto error;
  10.816 +        break;
  10.817 +      case OP_JSGRTR:
  10.818 +        if ((cip = JumpTarget(plugin, ctx, cip, pri > alt)) == NULL)
  10.819 +          goto error;
  10.820 +        break;
  10.821 +      case OP_JSGEQ:
  10.822 +        if ((cip = JumpTarget(plugin, ctx, cip, pri >= alt)) == NULL)
  10.823 +          goto error;
  10.824 +        break;
  10.825 +
  10.826 +      case OP_TRACKER_PUSH_C:
  10.827 +      {
  10.828 +        cell_t amount = *cip++;
  10.829 +        int error = PushTracker(ctx, amount * 4);
  10.830 +        if (error != SP_ERROR_NONE) {
  10.831 +          ctx->err = error;
  10.832 +          goto error;
  10.833 +        }
  10.834 +        break;
  10.835 +      }
  10.836 +
  10.837 +      case OP_TRACKER_POP_SETHEAP:
  10.838 +      {
  10.839 +        int error = PopTrackerAndSetHeap(rt);
  10.840 +        if (error != SP_ERROR_NONE) {
  10.841 +          ctx->err = error;
  10.842 +          goto error;
  10.843 +        }
  10.844 +        break;
  10.845 +      }
  10.846 +
  10.847 +      case OP_BREAK:
  10.848 +        ctx->cip = uintptr_t(cip - 1) - uintptr_t(plugin->pcode);
  10.849 +        break;
  10.850 +
  10.851 +      case OP_BOUNDS:
  10.852 +      {
  10.853 +        cell_t value = *cip++;
  10.854 +        if (uint32_t(pri) > uint32_t(value)) {
  10.855 +          ctx->err = SP_ERROR_ARRAY_BOUNDS;
  10.856 +          goto error;
  10.857 +        }
  10.858 +        break;
  10.859 +      }
  10.860 +
  10.861 +      case OP_CALL:
  10.862 +      {
  10.863 +        cell_t offset = *cip++;
  10.864 +
  10.865 +        if (!IsValidOffset(offset) || uint32_t(offset) >= plugin->pcode_size) {
  10.866 +          ctx->err = SP_ERROR_INSTRUCTION_PARAM;
  10.867 +          goto error;
  10.868 +        }
  10.869 +
  10.870 +        if (ctx->rp >= SP_MAX_RETURN_STACK) {
  10.871 +          ctx->err = SP_ERROR_STACKLOW;
  10.872 +          goto error;
  10.873 +        }
  10.874 +
  10.875 +        // For debugging.
  10.876 +        uintptr_t rcip = uintptr_t(cip - 2) - uintptr_t(plugin->pcode);
  10.877 +        ctx->rstk_cips[ctx->rp++] = rcip;
  10.878 +        ctx->cip = offset;
  10.879 +        ctx->sp = uintptr_t(stk) - uintptr_t(plugin->memory);
  10.880 +
  10.881 +        int err = Interpret(rt, offset, &pri);
  10.882 +
  10.883 +        stk = reinterpret_cast<cell_t *>(plugin->memory + ctx->sp);
  10.884 +        ctx->cip = rcip;
  10.885 +        ctx->rp--;
  10.886 +
  10.887 +        if (err != SP_ERROR_NONE)
  10.888 +          goto error;
  10.889 +        break;
  10.890 +      }
  10.891 +
  10.892 +      case OP_GENARRAY:
  10.893 +      case OP_GENARRAY_Z:
  10.894 +      {
  10.895 +        cell_t val = *cip++;
  10.896 +        if (!GenerateArray(rt, ctx, val, stk, op == OP_GENARRAY_Z))
  10.897 +          goto error;
  10.898 +
  10.899 +        stk += (val - 1) * 4;
  10.900 +        break;
  10.901 +      }
  10.902 +
  10.903 +      case OP_SYSREQ_C:
  10.904 +      case OP_SYSREQ_N:
  10.905 +      {
  10.906 +        uint32_t native_index = *cip++;
  10.907 +
  10.908 +        if (native_index >= plugin->num_natives) {
  10.909 +          ctx->err = SP_ERROR_INSTRUCTION_PARAM;
  10.910 +          goto error;
  10.911 +        }
  10.912 +
  10.913 +        uint32_t num_params;
  10.914 +        if (op == OP_SYSREQ_N) {
  10.915 +          num_params = *cip++;
  10.916 +          *--stk = num_params;
  10.917 +        }
  10.918 +
  10.919 +        ctx->sp = uintptr_t(stk) - uintptr_t(plugin->memory);
  10.920 +        pri = NativeCallback(ctx, native_index, stk);
  10.921 +        if (ctx->n_err != SP_ERROR_NONE) {
  10.922 +          ctx->err = ctx->n_err;
  10.923 +          goto error;
  10.924 +        }
  10.925 +
  10.926 +        if (op == OP_SYSREQ_N)
  10.927 +          stk += num_params + 1;
  10.928 +        break;
  10.929 +      }
  10.930 +
  10.931 +      case OP_SWITCH:
  10.932 +      {
  10.933 +        cell_t offset = *cip++;
  10.934 +        cell_t *table = reinterpret_cast<cell_t *>(plugin->pcode + offset + sizeof(cell_t));
  10.935 +
  10.936 +        size_t ncases = *table++;
  10.937 +        cell_t target = *table++;   // default case
  10.938 +
  10.939 +        for (size_t i = 0; i < ncases; i++) {
  10.940 +          if (table[i * 2] == pri) {
  10.941 +            target = table[i * 2 + 1];
  10.942 +            break;
  10.943 +          }
  10.944 +        }
  10.945 +
  10.946 +        if ((cip = Jump(plugin, ctx, target)) == NULL)
  10.947 +          goto error;
  10.948 +        break;
  10.949 +      }
  10.950 +
  10.951 +      default:
  10.952 +      {
  10.953 +        ctx->err = SP_ERROR_INVALID_INSTRUCTION;
  10.954 +        goto error;
  10.955 +      }
  10.956 +    } // switch
  10.957 +  }
  10.958 +
  10.959 + done:
  10.960 +  assert(orig_frm == ctx->frm);
  10.961 +  ctx->sp = uintptr_t(stk) - uintptr_t(plugin->memory);
  10.962 +  return ctx->err;
  10.963 +
  10.964 + error:
  10.965 +  ctx->frm = orig_frm;
  10.966 +  goto done;
  10.967 +}
  10.968 +
    11.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    11.2 +++ b/sourcepawn/jit/interpreter.h	Sun Sep 01 00:23:44 2013 -0700
    11.3 @@ -0,0 +1,39 @@
    11.4 +// vim: set ts=8 sts=2 sw=2 tw=99 et:
    11.5 +//
    11.6 +// This file is part of SourcePawn.
    11.7 +// 
    11.8 +// SourcePawn is free software: you can redistribute it and/or modify
    11.9 +// it under the terms of the GNU General Public License as published by
   11.10 +// the Free Software Foundation, either version 3 of the License, or
   11.11 +// (at your option) any later version.
   11.12 +// 
   11.13 +// SourcePawn is distributed in the hope that it will be useful,
   11.14 +// but WITHOUT ANY WARRANTY; without even the implied warranty of
   11.15 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   11.16 +// GNU General Public License for more details.
   11.17 +// 
   11.18 +// You should have received a copy of the GNU General Public License
   11.19 +// along with SourcePawn.  If not, see <http://www.gnu.org/licenses/>.
   11.20 +#ifndef _include_sourcepawn_interpreter_h_
   11.21 +#define _include_sourcepawn_interpreter_h_
   11.22 +
   11.23 +#include <sp_vm_types.h>
   11.24 +#include <sp_vm_api.h>
   11.25 +#include "BaseRuntime.h"
   11.26 +#include "sp_vm_basecontext.h"
   11.27 +
   11.28 +struct tracker_t
   11.29 +{
   11.30 +  size_t size; 
   11.31 +  ucell_t *pBase; 
   11.32 +  ucell_t *pCur;
   11.33 +};
   11.34 +
   11.35 +int Interpret(BaseRuntime *rt, uint32_t aCodeStart, cell_t *rval);
   11.36 +
   11.37 +int GenerateFullArray(BaseRuntime *rt, uint32_t argc, cell_t *argv, int autozero);
   11.38 +cell_t NativeCallback(sp_context_t *ctx, ucell_t native_idx, cell_t *params);
   11.39 +int PopTrackerAndSetHeap(BaseRuntime *rt);
   11.40 +int PushTracker(sp_context_t *ctx, size_t amount);
   11.41 +
   11.42 +#endif // _include_sourcepawn_interpreter_h_
    12.1 --- a/sourcepawn/jit/jit_shared.h	Sat Aug 31 19:50:36 2013 -0700
    12.2 +++ b/sourcepawn/jit/jit_shared.h	Sun Sep 01 00:23:44 2013 -0700
    12.3 @@ -67,6 +67,9 @@
    12.4  	} sp_plugin_t;
    12.5  }
    12.6  
    12.7 +struct tracker_t;
    12.8 +class BaseContext;
    12.9 +
   12.10  typedef struct sp_context_s
   12.11  {
   12.12  	cell_t			hp;				/**< Heap pointer */
   12.13 @@ -74,8 +77,12 @@
   12.14  	cell_t			frm;			/**< Frame pointer */
   12.15  	cell_t			rval;			/**< Return value from InvokeFunction() */
   12.16  	int32_t			cip;			/**< Code pointer last error occurred in */
   12.17 +	int32_t			err;			/**< Error last set by interpreter */
   12.18  	int32_t			n_err;			/**< Error code set by a native */
   12.19  	uint32_t		n_idx;			/**< Current native index being executed */
   12.20 +	tracker_t 		*tracker;
   12.21 +	sp_plugin_t 	*plugin;
   12.22 +	BaseContext		*basecx;
   12.23  	void *			vm[8];			/**< VM-specific pointers */
   12.24  	cell_t			rp;				/**< Return stack pointer */
   12.25  	cell_t			rstk_cips[SP_MAX_RETURN_STACK];
    13.1 --- a/sourcepawn/jit/sp_vm_basecontext.cpp	Sat Aug 31 19:50:36 2013 -0700
    13.2 +++ b/sourcepawn/jit/sp_vm_basecontext.cpp	Sun Sep 01 00:23:44 2013 -0700
    13.3 @@ -38,6 +38,8 @@
    13.4  #include "sp_vm_engine.h"
    13.5  #include "watchdog_timer.h"
    13.6  #include "x86/jit_x86.h"
    13.7 +#include "engine2.h"
    13.8 +#include "interpreter.h"
    13.9  
   13.10  using namespace SourcePawn;
   13.11  
   13.12 @@ -595,7 +597,7 @@
   13.13  	}
   13.14  
   13.15  	/* See if we have to compile the callee. */
   13.16 -	if ((fn = m_pRuntime->m_PubJitFuncs[public_id]) == NULL)
   13.17 +	if (g_engine2.IsJitEnabled() && (fn = m_pRuntime->m_PubJitFuncs[public_id]) == NULL)
   13.18  	{
   13.19  		/* We might not have to - check pcode offset. */
   13.20  		fn = m_pRuntime->GetJittedFunctionByOffset(pubfunc->code_offs);
   13.21 @@ -646,7 +648,10 @@
   13.22  
   13.23  	/* Start the frame tracer */
   13.24  
   13.25 -	ir = g_Jit.InvokeFunction(m_pRuntime, fn, result);
   13.26 +	if (g_engine2.IsJitEnabled())
   13.27 +		ir = g_Jit.InvokeFunction(m_pRuntime, fn, result);
   13.28 +	else
   13.29 +		ir = Interpret(m_pRuntime, pubfunc->code_offs, result);
   13.30  
   13.31  	/* Restore some states, stop the frame tracer */
   13.32  
    14.1 --- a/sourcepawn/jit/x86/jit_x86.cpp	Sat Aug 31 19:50:36 2013 -0700
    14.2 +++ b/sourcepawn/jit/x86/jit_x86.cpp	Sun Sep 01 00:23:44 2013 -0700
    14.3 @@ -38,6 +38,7 @@
    14.4  #include "../BaseRuntime.h"
    14.5  #include "../sp_vm_basecontext.h"
    14.6  #include "watchdog_timer.h"
    14.7 +#include "interpreter.h"
    14.8  
    14.9  using namespace Knight;
   14.10  
   14.11 @@ -166,50 +167,8 @@
   14.12    return data_offs;
   14.13  }
   14.14  
   14.15 -static int
   14.16 -PopTrackerAndSetHeap(BaseRuntime *rt)
   14.17 -{
   14.18 -  sp_context_t *ctx = rt->GetBaseContext()->GetCtx();
   14.19 -  tracker_t *trk = (tracker_t *)(ctx->vm[JITVARS_TRACKER]);
   14.20 -  assert(trk->pCur > trk->pBase);
   14.21 -
   14.22 -  trk->pCur--;
   14.23 -  if (trk->pCur < trk->pBase)
   14.24 -    return SP_ERROR_TRACKER_BOUNDS;
   14.25 -
   14.26 -  ucell_t amt = *trk->pCur;
   14.27 -  if (amt > (ctx->hp - rt->plugin()->data_size))
   14.28 -    return SP_ERROR_HEAPMIN;
   14.29 -
   14.30 -  ctx->hp -= amt;
   14.31 -  return SP_ERROR_NONE;
   14.32 -}
   14.33 -
   14.34 -static int
   14.35 -PushTracker(sp_context_t *ctx, size_t amount)
   14.36 -{
   14.37 -  tracker_t *trk = (tracker_t *)(ctx->vm[JITVARS_TRACKER]);
   14.38 -
   14.39 -  if ((size_t)(trk->pCur - trk->pBase) >= trk->size)
   14.40 -    return SP_ERROR_TRACKER_BOUNDS;
   14.41 -
   14.42 -  if (trk->pCur + 1 - (trk->pBase + trk->size) == 0) {
   14.43 -    size_t disp = trk->size - 1;
   14.44 -    trk->size *= 2;
   14.45 -    trk->pBase = (ucell_t *)realloc(trk->pBase, trk->size * sizeof(cell_t));
   14.46 -
   14.47 -    if (!trk->pBase)
   14.48 -      return SP_ERROR_TRACKER_BOUNDS;
   14.49 -
   14.50 -    trk->pCur = trk->pBase + disp;
   14.51 -  }
   14.52 -
   14.53 -  *trk->pCur++ = amount;
   14.54 -  return SP_ERROR_NONE;
   14.55 -}
   14.56 -
   14.57 -static int
   14.58 -GenerateArray(BaseRuntime *rt, uint32_t argc, cell_t *argv, int autozero)
   14.59 +int
   14.60 +GenerateFullArray(BaseRuntime *rt, uint32_t argc, cell_t *argv, int autozero)
   14.61  {
   14.62    sp_context_t *ctx = rt->GetBaseContext()->GetCtx();
   14.63  
   14.64 @@ -342,55 +301,6 @@
   14.65    return SP_ERROR_NONE;
   14.66  }
   14.67  
   14.68 -static cell_t
   14.69 -NativeCallback(sp_context_t *ctx, ucell_t native_idx, cell_t *params)
   14.70 -{
   14.71 -  sp_native_t *native;
   14.72 -  cell_t save_sp = ctx->sp;
   14.73 -  cell_t save_hp = ctx->hp;
   14.74 -  sp_plugin_t *pl = (sp_plugin_t *)(ctx->vm[JITVARS_PLUGIN]);
   14.75 -
   14.76 -  ctx->n_idx = native_idx;
   14.77 -
   14.78 -  if (ctx->hp < (cell_t)pl->data_size) {
   14.79 -    ctx->n_err = SP_ERROR_HEAPMIN;
   14.80 -    return 0;
   14.81 -  }
   14.82 -
   14.83 -  if (ctx->hp + STACK_MARGIN > ctx->sp) {
   14.84 -    ctx->n_err = SP_ERROR_STACKLOW;
   14.85 -    return 0;
   14.86 -  }
   14.87 -
   14.88 -  if ((uint32_t)ctx->sp >= pl->mem_size) {
   14.89 -    ctx->n_err = SP_ERROR_STACKMIN;
   14.90 -    return 0;
   14.91 -  }
   14.92 -
   14.93 -  native = &pl->natives[native_idx];
   14.94 -
   14.95 -  if (native->status == SP_NATIVE_UNBOUND) {
   14.96 -    ctx->n_err = SP_ERROR_INVALID_NATIVE;
   14.97 -    return 0;
   14.98 -  }
   14.99 -
  14.100 -  cell_t result = native->pfn(GET_CONTEXT(ctx), params);
  14.101 -
  14.102 -  if (ctx->n_err != SP_ERROR_NONE)
  14.103 -    return result;
  14.104 -
  14.105 -  if (save_sp != ctx->sp) {
  14.106 -    ctx->n_err = SP_ERROR_STACKLEAK;
  14.107 -    return result;
  14.108 -  }
  14.109 -  if (save_hp != ctx->hp) {
  14.110 -    ctx->n_err = SP_ERROR_HEAPLEAK;
  14.111 -    return result;
  14.112 -  }
  14.113 -
  14.114 -  return result;
  14.115 -}
  14.116 -
  14.117  Compiler::Compiler(BaseRuntime *rt, cell_t pcode_offs)
  14.118    : rt_(rt),
  14.119      plugin_(rt->plugin()),
  14.120 @@ -1226,7 +1136,7 @@
  14.121        __ movl(alt, Operand(hpAddr()));
  14.122        __ addl(Operand(hpAddr()), amount);
  14.123  
  14.124 -      if (amount > 0) {
  14.125 +      if (amount < 0) {
  14.126          __ cmpl(Operand(hpAddr()), plugin_->data_size);
  14.127          __ j(below, &error_heap_min_);
  14.128        } else {
  14.129 @@ -1473,7 +1383,7 @@
  14.130      __ push(stk);
  14.131      __ push(val);
  14.132      __ push(intptr_t(rt_));
  14.133 -    __ call(ExternalAddress((void *)GenerateArray));
  14.134 +    __ call(ExternalAddress((void *)GenerateFullArray));
  14.135      __ addl(esp, 4 * sizeof(void *));
  14.136  
  14.137      // restore pri to tmp
  14.138 @@ -1921,16 +1831,13 @@
  14.139  void
  14.140  JITX86::SetupContextVars(BaseRuntime *runtime, BaseContext *pCtx, sp_context_t *ctx)
  14.141  {
  14.142 -  tracker_t *trk = new tracker_t;
  14.143 -
  14.144 -  ctx->vm[JITVARS_TRACKER] = trk;
  14.145 -  ctx->vm[JITVARS_BASECTX] = pCtx; /* GetDefaultContext() is not constructed yet */
  14.146 +  ctx->tracker = new tracker_t;
  14.147 +  ctx->tracker->pBase = (ucell_t *)malloc(1024);
  14.148 +  ctx->tracker->pCur = ctx->tracker->pBase;
  14.149 +  ctx->tracker->size = 1024 / sizeof(cell_t);
  14.150 +  ctx->basecx = pCtx;
  14.151    ctx->vm[JITVARS_PROFILER] = g_engine2.GetProfiler();
  14.152 -  ctx->vm[JITVARS_PLUGIN] = const_cast<sp_plugin_t *>(runtime->plugin());
  14.153 -
  14.154 -  trk->pBase = (ucell_t *)malloc(1024);
  14.155 -  trk->pCur = trk->pBase;
  14.156 -  trk->size = 1024 / sizeof(cell_t);
  14.157 +  ctx->plugin = const_cast<sp_plugin_t *>(runtime->plugin());
  14.158  }
  14.159  
  14.160  SPVM_NATIVE_FUNC
  14.161 @@ -1987,8 +1894,8 @@
  14.162  void
  14.163  JITX86::FreeContextVars(sp_context_t *ctx)
  14.164  {
  14.165 -  free(((tracker_t *)(ctx->vm[JITVARS_TRACKER]))->pBase);
  14.166 -  delete (tracker_t *)ctx->vm[JITVARS_TRACKER];
  14.167 +  free(ctx->tracker->pBase);
  14.168 +  delete ctx->tracker;
  14.169  }
  14.170  
  14.171  bool
    15.1 --- a/sourcepawn/jit/x86/jit_x86.h	Sat Aug 31 19:50:36 2013 -0700
    15.2 +++ b/sourcepawn/jit/x86/jit_x86.h	Sun Sep 01 00:23:44 2013 -0700
    15.3 @@ -36,22 +36,10 @@
    15.4  #define STACK_MARGIN            64      //8 parameters of safety, I guess
    15.5  #define JIT_FUNCMAGIC           0x214D4148  //magic function offset
    15.6  
    15.7 -#define JITVARS_TRACKER         0    //important: don't change this to avoid trouble
    15.8 -#define JITVARS_BASECTX         1    //important: don't change this aWOAWOGJQG I LIKE HAM
    15.9  #define JITVARS_PROFILER        2    //profiler
   15.10 -#define JITVARS_PLUGIN          3    //sp_plugin_t
   15.11  
   15.12  #define sDIMEN_MAX              5    //this must mirror what the compiler has.
   15.13  
   15.14 -#define GET_CONTEXT(c)  ((IPluginContext *)c->vm[JITVARS_BASECTX])
   15.15 -
   15.16 -typedef struct tracker_s
   15.17 -{
   15.18 -  size_t size; 
   15.19 -  ucell_t *pBase; 
   15.20 -  ucell_t *pCur;
   15.21 -} tracker_t;
   15.22 -
   15.23  typedef struct funcinfo_s
   15.24  {
   15.25    unsigned int magic;