sourcepawn/jit/interpreter.cpp
author Kyle Sanderson <kyle.leet@gmail.com>
Sun May 11 14:36:32 2014 -0700 (2014-05-11)
changeset 4358 52aa5fab9866
parent 4156 319e80e8e97f
permissions -rw-r--r--
Optimize native call if statically bound (bug 5842, r=ds,fyren f=dvander).
Original patch written by David Anderson (original r from Fyren).
- Updated to function with the current JIT compiler.
- Took in clean-ups from later-dated patches.
     1 // vim: set ts=8 sts=2 sw=2 tw=99 et:
     2 //
     3 // This file is part of SourcePawn.
     4 // 
     5 // SourcePawn is free software: you can redistribute it and/or modify
     6 // it under the terms of the GNU General Public License as published by
     7 // the Free Software Foundation, either version 3 of the License, or
     8 // (at your option) any later version.
     9 // 
    10 // SourcePawn is distributed in the hope that it will be useful,
    11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13 // GNU General Public License for more details.
    14 // 
    15 // You should have received a copy of the GNU General Public License
    16 // along with SourcePawn.  If not, see <http://www.gnu.org/licenses/>.
    17 #include <limits.h>
    18 #include <string.h>
    19 #include "interpreter.h"
    20 #include "opcodes.h"
    21 #include "watchdog_timer.h"
    22 
    23 #define STACK_MARGIN 64
    24 
    25 using namespace SourcePawn;
    26 
    27 static inline bool
    28 IsValidOffset(uint32_t cip)
    29 {
    30   return cip % 4 == 0;
    31 }
    32 
    33 static inline cell_t
    34 Read(const sp_plugin_t *plugin, cell_t offset)
    35 {
    36   return *reinterpret_cast<cell_t *>(plugin->memory + offset);
    37 }
    38 
    39 static inline void
    40 Write(const sp_plugin_t *plugin, cell_t offset, cell_t value)
    41 {
    42   *reinterpret_cast<cell_t *>(plugin->memory + offset) = value;
    43 }
    44 
    45 static inline cell_t *
    46 Jump(const sp_plugin_t *plugin, sp_context_t *ctx, cell_t target)
    47 {
    48   if (!IsValidOffset(target) || uint32_t(target) >= plugin->pcode_size) {
    49     ctx->err = SP_ERROR_INVALID_INSTRUCTION;
    50     return NULL;
    51   }
    52 
    53   return reinterpret_cast<cell_t *>(plugin->pcode + target);
    54 }
    55 
    56 static inline cell_t *
    57 JumpTarget(const sp_plugin_t *plugin, sp_context_t *ctx, cell_t *cip, bool cond)
    58 {
    59   if (!cond)
    60     return cip + 1;
    61 
    62   cell_t target = *cip;
    63   if (!IsValidOffset(target) || uint32_t(target) >= plugin->pcode_size) {
    64     ctx->err = SP_ERROR_INVALID_INSTRUCTION;
    65     return NULL;
    66   }
    67 
    68   cell_t *next = reinterpret_cast<cell_t *>(plugin->pcode + target);
    69   if (next < cip && !g_WatchdogTimer.HandleInterrupt()) {
    70     ctx->err = SP_ERROR_TIMEOUT;
    71     return NULL;
    72   }
    73 
    74   return next;
    75 }
    76 
    77 static inline bool
    78 CheckAddress(const sp_plugin_t *plugin, sp_context_t *ctx, cell_t *stk, cell_t addr)
    79 {
    80   if (uint32_t(addr) >= plugin->mem_size) {
    81     ctx->err = SP_ERROR_MEMACCESS;
    82     return false;
    83   }
    84 
    85   if (addr < ctx->hp)
    86     return true;
    87 
    88   if (reinterpret_cast<cell_t *>(plugin->memory + addr) < stk) {
    89     ctx->err = SP_ERROR_MEMACCESS;
    90     return false;
    91   }
    92 
    93   return true;
    94 }
    95 
    96 int
    97 PopTrackerAndSetHeap(BaseRuntime *rt)
    98 {
    99   sp_context_t *ctx = rt->GetBaseContext()->GetCtx();
   100   tracker_t *trk = ctx->tracker;
   101   assert(trk->pCur > trk->pBase);
   102 
   103   trk->pCur--;
   104   if (trk->pCur < trk->pBase)
   105     return SP_ERROR_TRACKER_BOUNDS;
   106 
   107   ucell_t amt = *trk->pCur;
   108   if (amt > (ctx->hp - rt->plugin()->data_size))
   109     return SP_ERROR_HEAPMIN;
   110 
   111   ctx->hp -= amt;
   112   return SP_ERROR_NONE;
   113 }
   114 
   115 int
   116 PushTracker(sp_context_t *ctx, size_t amount)
   117 {
   118   tracker_t *trk = ctx->tracker;
   119 
   120   if ((size_t)(trk->pCur - trk->pBase) >= trk->size)
   121     return SP_ERROR_TRACKER_BOUNDS;
   122 
   123   if (trk->pCur + 1 - (trk->pBase + trk->size) == 0) {
   124     size_t disp = trk->size - 1;
   125     trk->size *= 2;
   126     trk->pBase = (ucell_t *)realloc(trk->pBase, trk->size * sizeof(cell_t));
   127 
   128     if (!trk->pBase)
   129       return SP_ERROR_TRACKER_BOUNDS;
   130 
   131     trk->pCur = trk->pBase + disp;
   132   }
   133 
   134   *trk->pCur++ = amount;
   135   return SP_ERROR_NONE;
   136 }
   137 
   138 cell_t
   139 NativeCallback(sp_context_t *ctx, ucell_t native_idx, cell_t *params)
   140 {
   141   cell_t save_sp = ctx->sp;
   142   cell_t save_hp = ctx->hp;
   143 
   144   ctx->n_idx = native_idx;
   145 
   146   sp_native_t *native = &ctx->plugin->natives[native_idx];
   147 
   148   if (native->status == SP_NATIVE_UNBOUND) {
   149     ctx->n_err = SP_ERROR_INVALID_NATIVE;
   150     return 0;
   151   }
   152 
   153   cell_t result = native->pfn(ctx->basecx, params);
   154 
   155   if (ctx->n_err != SP_ERROR_NONE)
   156     return result;
   157 
   158   if (save_sp != ctx->sp) {
   159     ctx->n_err = SP_ERROR_STACKLEAK;
   160     return result;
   161   }
   162   if (save_hp != ctx->hp) {
   163     ctx->n_err = SP_ERROR_HEAPLEAK;
   164     return result;
   165   }
   166 
   167   return result;
   168 }
   169 
   170 cell_t
   171 BoundNativeCallback(sp_context_t *ctx, SPVM_NATIVE_FUNC pfn, cell_t *params)
   172 {
   173   cell_t save_sp = ctx->sp;
   174   cell_t save_hp = ctx->hp;
   175 
   176   cell_t result = pfn(ctx->basecx, params);
   177 
   178   if (ctx->n_err != SP_ERROR_NONE)
   179     return result;
   180 
   181   if (save_sp != ctx->sp) {
   182     ctx->n_err = SP_ERROR_STACKLEAK;
   183     return result;
   184   }
   185   if (save_hp != ctx->hp) {
   186     ctx->n_err = SP_ERROR_HEAPLEAK;
   187     return result;
   188   }
   189 
   190   return result;
   191 }
   192 
   193 static inline bool
   194 GenerateArray(BaseRuntime *rt, sp_context_t *ctx, cell_t dims, cell_t *stk, bool autozero)
   195 {
   196   if (dims == 1) {
   197     uint32_t size = *stk;
   198     if (size == 0 || !ke::IsUint32MultiplySafe(size, 4)) {
   199       ctx->err = SP_ERROR_ARRAY_TOO_BIG;
   200       return false;
   201     }
   202     *stk = ctx->hp;
   203 
   204     uint32_t bytes = size * 4;
   205 
   206     ctx->hp += bytes;
   207     if (uintptr_t(ctx->plugin->memory + ctx->hp) >= uintptr_t(stk)) {
   208       ctx->err = SP_ERROR_HEAPLOW;
   209       return false;
   210     }
   211 
   212     if ((ctx->err = PushTracker(ctx, bytes)) != SP_ERROR_NONE)
   213       return false;
   214 
   215     if (autozero)
   216       memset(ctx->plugin->memory + ctx->hp, 0, bytes);
   217 
   218     return true;
   219   }
   220 
   221   if ((ctx->err = GenerateFullArray(rt, dims, stk, autozero)) != SP_ERROR_NONE)
   222     return false;
   223 
   224   return true;
   225 }
   226 
   227 int
   228 Interpret(BaseRuntime *rt, uint32_t aCodeStart, cell_t *rval)
   229 {
   230   const sp_plugin_t *plugin = rt->plugin();
   231   cell_t *code = reinterpret_cast<cell_t *>(plugin->pcode);
   232   cell_t *codeend = reinterpret_cast<cell_t *>(plugin->pcode + plugin->pcode_size);
   233 
   234   if (!IsValidOffset(aCodeStart) || aCodeStart > plugin->pcode_size)
   235     return SP_ERROR_INVALID_INSTRUCTION;
   236 
   237   sp_context_t *ctx = rt->GetBaseContext()->GetCtx();
   238   ctx->err = SP_ERROR_NONE;
   239 
   240   // Save the original frm. BaseContext won't, and if we error, we won't hit
   241   // the stack unwinding code.
   242   cell_t orig_frm = ctx->frm;
   243 
   244   cell_t pri = 0;
   245   cell_t alt = 0;
   246   cell_t *cip = code + (aCodeStart / 4);
   247   cell_t *stk = reinterpret_cast<cell_t *>(plugin->memory + ctx->sp);
   248 
   249   for (;;) {
   250     if (cip >= codeend) {
   251       ctx->err = SP_ERROR_INVALID_INSTRUCTION;
   252       goto error;
   253     }
   254 
   255 #if 0
   256     SpewOpcode(plugin, reinterpret_cast<cell_t *>(plugin->pcode + aCodeStart), cip);
   257 #endif
   258 
   259     OPCODE op = (OPCODE)*cip++;
   260 
   261     switch (op) {
   262       case OP_MOVE_PRI:
   263         pri = alt;
   264         break;
   265       case OP_MOVE_ALT:
   266         alt = pri;
   267         break;
   268 
   269       case OP_XCHG:
   270       {
   271         cell_t tmp = pri;
   272         pri = alt;
   273         alt = tmp;
   274         break;
   275       }
   276 
   277       case OP_ZERO:
   278         Write(plugin, *cip++, 0);
   279         break;
   280 
   281       case OP_ZERO_S:
   282         Write(plugin, ctx->frm + *cip++, 0);
   283         break;
   284 
   285       case OP_PUSH_PRI:
   286         *--stk = pri;
   287         break;
   288       case OP_PUSH_ALT:
   289         *--stk = alt;
   290         break;
   291 
   292       case OP_PUSH_C:
   293       case OP_PUSH2_C:
   294       case OP_PUSH3_C:
   295       case OP_PUSH4_C:
   296       case OP_PUSH5_C:
   297       {
   298         int n = 1;
   299         if (op >= OP_PUSH2_C)
   300           n = ((op - OP_PUSH2_C) / 4) + 2;
   301 
   302         int i = 1;
   303         do {
   304           *--stk = *cip++;
   305         } while (i++ < n);
   306         break;
   307       }
   308 
   309       case OP_PUSH_ADR:
   310       case OP_PUSH2_ADR:
   311       case OP_PUSH3_ADR:
   312       case OP_PUSH4_ADR:
   313       case OP_PUSH5_ADR:
   314       {
   315         int n = 1;
   316         if (op >= OP_PUSH2_ADR)
   317           n = ((op - OP_PUSH2_ADR) / 4) + 2;
   318 
   319         int i = 1;
   320 
   321         do {
   322           cell_t addr = ctx->frm + *cip++;
   323           *--stk = addr;
   324         } while (i++ < n);
   325         break;
   326       }
   327 
   328       case OP_PUSH_S:
   329       case OP_PUSH2_S:
   330       case OP_PUSH3_S:
   331       case OP_PUSH4_S:
   332       case OP_PUSH5_S:
   333       {
   334         int n = 1;
   335         if (op >= OP_PUSH2_S)
   336           n = ((op - OP_PUSH2_S) / 4) + 2;
   337 
   338         int i = 1;
   339         do {
   340           cell_t value = Read(plugin, ctx->frm + *cip++);
   341           *--stk = value;
   342         } while (i++ < n);
   343         break;
   344       }
   345 
   346       case OP_PUSH:
   347       case OP_PUSH2:
   348       case OP_PUSH3:
   349       case OP_PUSH4:
   350       case OP_PUSH5:
   351       {
   352         int n = 1;
   353         if (op >= OP_PUSH2)
   354           n = ((op - OP_PUSH2) / 4) + 2;
   355 
   356         int i = 1;
   357         do {
   358           cell_t value = Read(plugin, *cip++);
   359           *--stk = value;
   360         } while (i++ < n);
   361         break;
   362       }
   363 
   364       case OP_ZERO_PRI:
   365         pri = 0;
   366         break;
   367       case OP_ZERO_ALT:
   368         alt = 0;
   369         break;
   370 
   371       case OP_ADD:
   372         pri += alt;
   373         break;
   374 
   375       case OP_SUB:
   376         pri -= alt;
   377         break;
   378 
   379       case OP_SUB_ALT:
   380         pri = alt - pri;
   381         break;
   382 
   383       case OP_PROC:
   384       {
   385         *--stk = ctx->frm;
   386         *--stk = 0;
   387         ctx->frm = uintptr_t(stk) - uintptr_t(plugin->memory);
   388         break;
   389       }
   390 
   391       case OP_IDXADDR_B:
   392         pri <<= *cip++;
   393         pri += alt;
   394         break;
   395 
   396       case OP_SHL:
   397         pri <<= alt;
   398         break;
   399 
   400       case OP_SHR:
   401         pri = unsigned(pri) >> unsigned(alt);
   402         break;
   403 
   404       case OP_SSHR:
   405         pri >>= alt;
   406         break;
   407 
   408       case OP_SHL_C_PRI:
   409         pri <<= *cip++;
   410         break;
   411       case OP_SHL_C_ALT:
   412         alt <<= *cip++;
   413         break;
   414 
   415       case OP_SHR_C_PRI:
   416         pri >>= *cip++;
   417         break;
   418       case OP_SHR_C_ALT:
   419         alt >>= *cip++;
   420         break;
   421 
   422       case OP_SMUL:
   423         pri *= alt;
   424         break;
   425 
   426       case OP_NOT:
   427         pri = pri ? 0 : 1;
   428         break;
   429 
   430       case OP_NEG:
   431         pri = -pri;
   432         break;
   433 
   434       case OP_XOR:
   435         pri ^= alt;
   436         break;
   437 
   438       case OP_OR:
   439         pri |= alt;
   440         break;
   441 
   442       case OP_AND:
   443         pri &= alt;
   444         break;
   445 
   446       case OP_INVERT:
   447         pri = ~pri;
   448         break;
   449 
   450       case OP_ADD_C:
   451         pri += *cip++;
   452         break;
   453 
   454       case OP_SMUL_C:
   455         pri *= *cip++;
   456         break;
   457 
   458       case OP_EQ:
   459         pri = pri == alt;
   460         break;
   461 
   462       case OP_NEQ:
   463         pri = pri != alt;
   464         break;
   465 
   466       case OP_SLESS:
   467         pri = pri < alt;
   468         break;
   469 
   470       case OP_SLEQ:
   471         pri = pri <= alt;
   472         break;
   473 
   474       case OP_SGRTR:
   475         pri = pri > alt;
   476         break;
   477         
   478       case OP_SGEQ:
   479         pri = pri >= alt;
   480         break;
   481 
   482       case OP_EQ_C_PRI:
   483         pri = pri == *cip++;
   484         break;
   485       case OP_EQ_C_ALT:
   486         pri = alt == *cip++;
   487         break;
   488 
   489       case OP_INC_PRI:
   490         pri++;
   491         break;
   492       case OP_INC_ALT:
   493         alt++;
   494         break;
   495 
   496       case OP_INC:
   497       {
   498         cell_t offset = *cip++;
   499         Write(plugin, offset, Read(plugin, offset) + 1);
   500         break;
   501       }
   502 
   503       case OP_INC_S:
   504       {
   505         cell_t offset = *cip++;
   506         cell_t value = Read(plugin, ctx->frm + offset);
   507         Write(plugin, ctx->frm + offset, value + 1);
   508         break;
   509       }
   510 
   511       case OP_INC_I:
   512         if (!CheckAddress(plugin, ctx, stk, pri))
   513           goto error;
   514         Write(plugin, pri, Read(plugin, pri) + 1);
   515         break;
   516 
   517       case OP_DEC_PRI:
   518         pri--;
   519         break;
   520       case OP_DEC_ALT:
   521         alt--;
   522         break;
   523 
   524       case OP_DEC:
   525       {
   526         cell_t offset = *cip++;
   527         Write(plugin, offset, Read(plugin, offset) - 1);
   528         break;
   529       }
   530 
   531       case OP_DEC_S:
   532       {
   533         cell_t offset = *cip++;
   534         cell_t value = Read(plugin, ctx->frm + offset);
   535         Write(plugin, ctx->frm + offset, value - 1);
   536         break;
   537       }
   538 
   539       case OP_DEC_I:
   540         if (!CheckAddress(plugin, ctx, stk, pri))
   541           goto error;
   542         Write(plugin, pri, Read(plugin, pri) - 1);
   543         break;
   544 
   545       case OP_LOAD_PRI:
   546         pri = Read(plugin, *cip++);
   547         break;
   548       case OP_LOAD_ALT:
   549         alt = Read(plugin, *cip++);
   550         break;
   551 
   552       case OP_LOAD_S_PRI:
   553         pri = Read(plugin, ctx->frm + *cip++);
   554         break;
   555       case OP_LOAD_S_ALT:
   556         alt = Read(plugin, ctx->frm + *cip++);
   557         break;
   558 
   559       case OP_LOAD_S_BOTH:
   560         pri = Read(plugin, ctx->frm + *cip++);
   561         alt = Read(plugin, ctx->frm + *cip++);
   562         break;
   563 
   564       case OP_LREF_S_PRI:
   565       {
   566         pri = Read(plugin, ctx->frm + *cip++);
   567         pri = Read(plugin, pri);
   568         break;
   569       }
   570 
   571       case OP_LREF_S_ALT:
   572       {
   573         alt = Read(plugin, ctx->frm + *cip++);
   574         alt = Read(plugin, alt);
   575         break;
   576       }
   577 
   578       case OP_CONST_PRI:
   579         pri = *cip++;
   580         break;
   581       case OP_CONST_ALT:
   582         alt = *cip++;
   583         break;
   584 
   585       case OP_ADDR_PRI:
   586         pri = ctx->frm + *cip++;
   587         break;
   588       case OP_ADDR_ALT:
   589         alt = ctx->frm + *cip++;
   590         break;
   591 
   592       case OP_STOR_PRI:
   593         Write(plugin, *cip++, pri);
   594         break;
   595       case OP_STOR_ALT:
   596         Write(plugin, *cip++, alt);
   597         break;
   598 
   599       case OP_STOR_S_PRI:
   600         Write(plugin, ctx->frm + *cip++, pri);
   601         break;
   602       case OP_STOR_S_ALT:
   603         Write(plugin, ctx->frm +*cip++, alt);
   604         break;
   605 
   606       case OP_IDXADDR:
   607         pri = alt + pri * 4;
   608         break;
   609 
   610       case OP_SREF_S_PRI:
   611       {
   612         cell_t offset = *cip++;
   613         cell_t addr = Read(plugin, ctx->frm + offset);
   614         Write(plugin, addr, pri);
   615         break;
   616       }
   617 
   618       case OP_SREF_S_ALT:
   619       {
   620         cell_t offset = *cip++;
   621         cell_t addr = Read(plugin, ctx->frm + offset);
   622         Write(plugin, addr, alt);
   623         break;
   624       }
   625 
   626       case OP_POP_PRI:
   627         pri = *stk++;
   628         break;
   629       case OP_POP_ALT:
   630         alt = *stk++;
   631         break;
   632 
   633       case OP_SWAP_PRI:
   634       case OP_SWAP_ALT:
   635       {
   636         cell_t reg = (op == OP_SREF_S_PRI) ? pri : alt;
   637         cell_t temp = *stk;
   638         *stk = reg;
   639         reg = temp;
   640         break;
   641       }
   642 
   643       case OP_LIDX:
   644         pri = alt + pri * 4;
   645         if (!CheckAddress(plugin, ctx, stk, pri))
   646           goto error;
   647         pri = Read(plugin, pri);
   648         break;
   649 
   650       case OP_LIDX_B:
   651       {
   652         cell_t val = *cip++;
   653         pri = alt + (pri << val);
   654         if (!CheckAddress(plugin, ctx, stk, pri))
   655           goto error;
   656         pri = Read(plugin, pri);
   657         break;
   658       }
   659 
   660       case OP_CONST:
   661       {
   662         cell_t offset = *cip++;
   663         cell_t value = *cip++;
   664         Write(plugin, offset, value);
   665         break;
   666       }
   667 
   668       case OP_CONST_S:
   669       {
   670         cell_t offset = *cip++;
   671         cell_t value = *cip++;
   672         Write(plugin, ctx->frm + offset, value);
   673         break;
   674       }
   675 
   676       case OP_LOAD_I:
   677         if (!CheckAddress(plugin, ctx, stk, pri))
   678           goto error;
   679         pri = Read(plugin, pri);
   680         break;
   681 
   682       case OP_STOR_I:
   683         if (!CheckAddress(plugin, ctx, stk, alt))
   684           goto error;
   685         Write(plugin, alt, pri);
   686         break;
   687 
   688       case OP_SDIV:
   689       case OP_SDIV_ALT:
   690       {
   691         cell_t dividend = (op == OP_SDIV) ? pri : alt;
   692         cell_t divisor = (op == OP_SDIV) ? alt : pri;
   693         if (divisor == 0) {
   694           ctx->err = SP_ERROR_DIVIDE_BY_ZERO;
   695           goto error;
   696         }
   697         if (dividend == INT_MIN && divisor == -1) {
   698           ctx->err = SP_ERROR_INTEGER_OVERFLOW;
   699           goto error;
   700         }
   701         pri = dividend / divisor;
   702         alt = dividend % divisor;
   703         break;
   704       }
   705       
   706       case OP_LODB_I:
   707       {
   708         cell_t val = *cip++;
   709         if (!CheckAddress(plugin, ctx, stk, pri))
   710           goto error;
   711         pri = Read(plugin, pri);
   712         if (val == 1)
   713           pri &= 0xff;
   714         else if (val == 2)
   715           pri &= 0xffff;
   716         break;
   717       }
   718 
   719       case OP_STRB_I:
   720       {
   721         cell_t val = *cip++;
   722         if (!CheckAddress(plugin, ctx, stk, alt))
   723           goto error;
   724         if (val == 1)
   725           *reinterpret_cast<int8_t *>(plugin->memory + alt) = pri;
   726         else if (val == 2)
   727           *reinterpret_cast<int16_t *>(plugin->memory + alt) = pri;
   728         else if (val == 4)
   729           *reinterpret_cast<int32_t *>(plugin->memory + alt) = pri;
   730         break;
   731       }
   732 
   733       case OP_RETN:
   734       {
   735         stk++;
   736         ctx->frm = *stk++;
   737         stk += *stk + 1;
   738         *rval = pri;
   739         ctx->err = SP_ERROR_NONE;
   740         goto done;
   741       }
   742 
   743       case OP_MOVS:
   744       {
   745         uint8_t *src = plugin->memory + pri;
   746         uint8_t *dest = plugin->memory + alt;
   747         memcpy(dest, src, *cip++);
   748         break;
   749       }
   750 
   751       case OP_FILL:
   752       {
   753         uint8_t *dest = plugin->memory + alt;
   754         memset(dest, pri, *cip++);
   755         break;
   756       }
   757 
   758       case OP_STRADJUST_PRI:
   759         pri += 4;
   760         pri >>= 2;
   761         break;
   762 
   763       case OP_STACK:
   764       {
   765         cell_t amount = *cip++;
   766         if (!IsValidOffset(amount)) {
   767           ctx->err = SP_ERROR_INVALID_INSTRUCTION;
   768           goto error;
   769         }
   770 
   771         stk += amount / 4;
   772         if (amount > 0) {
   773           if (uintptr_t(stk) >= uintptr_t(plugin->memory + plugin->mem_size)) {
   774             ctx->err = SP_ERROR_STACKMIN;
   775             goto error;
   776           }
   777         } else {
   778           if (uintptr_t(stk) < uintptr_t(plugin->memory + ctx->hp + STACK_MARGIN)) {
   779             ctx->err = SP_ERROR_STACKLOW;
   780             goto error;
   781           }
   782         }
   783         break;
   784       }
   785 
   786       case OP_HEAP:
   787       {
   788         cell_t amount = *cip++;
   789 
   790         alt = ctx->hp;
   791         ctx->hp += amount;
   792 
   793         if (amount > 0) {
   794           if (uintptr_t(plugin->memory + ctx->hp) > uintptr_t(stk)) {
   795             ctx->err = SP_ERROR_HEAPLOW;
   796             goto error;
   797           }
   798         } else {
   799           if (uint32_t(ctx->hp) < plugin->data_size) {
   800             ctx->err = SP_ERROR_HEAPMIN;
   801             goto error;
   802           }
   803         }
   804         break;
   805       }
   806 
   807       case OP_JUMP:
   808         if ((cip = JumpTarget(plugin, ctx, cip, true)) == NULL)
   809           goto error;
   810         break;
   811 
   812       case OP_JZER:
   813         if ((cip = JumpTarget(plugin, ctx, cip, pri == 0)) == NULL)
   814           goto error;
   815         break;
   816       case OP_JNZ:
   817         if ((cip = JumpTarget(plugin, ctx, cip, pri != 0)) == NULL)
   818           goto error;
   819         break;
   820 
   821       case OP_JEQ:
   822         if ((cip = JumpTarget(plugin, ctx, cip, pri == alt)) == NULL)
   823           goto error;
   824         break;
   825       case OP_JNEQ:
   826         if ((cip = JumpTarget(plugin, ctx, cip, pri != alt)) == NULL)
   827           goto error;
   828         break;
   829       case OP_JSLESS:
   830         if ((cip = JumpTarget(plugin, ctx, cip, pri < alt)) == NULL)
   831           goto error;
   832         break;
   833       case OP_JSLEQ:
   834         if ((cip = JumpTarget(plugin, ctx, cip, pri <= alt)) == NULL)
   835           goto error;
   836         break;
   837       case OP_JSGRTR:
   838         if ((cip = JumpTarget(plugin, ctx, cip, pri > alt)) == NULL)
   839           goto error;
   840         break;
   841       case OP_JSGEQ:
   842         if ((cip = JumpTarget(plugin, ctx, cip, pri >= alt)) == NULL)
   843           goto error;
   844         break;
   845 
   846       case OP_TRACKER_PUSH_C:
   847       {
   848         cell_t amount = *cip++;
   849         int error = PushTracker(ctx, amount * 4);
   850         if (error != SP_ERROR_NONE) {
   851           ctx->err = error;
   852           goto error;
   853         }
   854         break;
   855       }
   856 
   857       case OP_TRACKER_POP_SETHEAP:
   858       {
   859         int error = PopTrackerAndSetHeap(rt);
   860         if (error != SP_ERROR_NONE) {
   861           ctx->err = error;
   862           goto error;
   863         }
   864         break;
   865       }
   866 
   867       case OP_BREAK:
   868         ctx->cip = uintptr_t(cip - 1) - uintptr_t(plugin->pcode);
   869         break;
   870 
   871       case OP_BOUNDS:
   872       {
   873         cell_t value = *cip++;
   874         if (uint32_t(pri) > uint32_t(value)) {
   875           ctx->err = SP_ERROR_ARRAY_BOUNDS;
   876           goto error;
   877         }
   878         break;
   879       }
   880 
   881       case OP_CALL:
   882       {
   883         cell_t offset = *cip++;
   884 
   885         if (!IsValidOffset(offset) || uint32_t(offset) >= plugin->pcode_size) {
   886           ctx->err = SP_ERROR_INSTRUCTION_PARAM;
   887           goto error;
   888         }
   889 
   890         if (ctx->rp >= SP_MAX_RETURN_STACK) {
   891           ctx->err = SP_ERROR_STACKLOW;
   892           goto error;
   893         }
   894 
   895         // For debugging.
   896         uintptr_t rcip = uintptr_t(cip - 2) - uintptr_t(plugin->pcode);
   897         ctx->rstk_cips[ctx->rp++] = rcip;
   898         ctx->cip = offset;
   899         ctx->sp = uintptr_t(stk) - uintptr_t(plugin->memory);
   900 
   901         int err = Interpret(rt, offset, &pri);
   902 
   903         stk = reinterpret_cast<cell_t *>(plugin->memory + ctx->sp);
   904         ctx->cip = rcip;
   905         ctx->rp--;
   906 
   907         if (err != SP_ERROR_NONE)
   908           goto error;
   909         break;
   910       }
   911 
   912       case OP_GENARRAY:
   913       case OP_GENARRAY_Z:
   914       {
   915         cell_t val = *cip++;
   916         if (!GenerateArray(rt, ctx, val, stk, op == OP_GENARRAY_Z))
   917           goto error;
   918 
   919         stk += (val - 1) * 4;
   920         break;
   921       }
   922 
   923       case OP_SYSREQ_C:
   924       case OP_SYSREQ_N:
   925       {
   926         uint32_t native_index = *cip++;
   927 
   928         if (native_index >= plugin->num_natives) {
   929           ctx->err = SP_ERROR_INSTRUCTION_PARAM;
   930           goto error;
   931         }
   932 
   933         uint32_t num_params;
   934         if (op == OP_SYSREQ_N) {
   935           num_params = *cip++;
   936           *--stk = num_params;
   937         }
   938 
   939         ctx->sp = uintptr_t(stk) - uintptr_t(plugin->memory);
   940         pri = NativeCallback(ctx, native_index, stk);
   941         if (ctx->n_err != SP_ERROR_NONE) {
   942           ctx->err = ctx->n_err;
   943           goto error;
   944         }
   945 
   946         if (op == OP_SYSREQ_N)
   947           stk += num_params + 1;
   948         break;
   949       }
   950 
   951       case OP_SWITCH:
   952       {
   953         cell_t offset = *cip++;
   954         cell_t *table = reinterpret_cast<cell_t *>(plugin->pcode + offset + sizeof(cell_t));
   955 
   956         size_t ncases = *table++;
   957         cell_t target = *table++;   // default case
   958 
   959         for (size_t i = 0; i < ncases; i++) {
   960           if (table[i * 2] == pri) {
   961             target = table[i * 2 + 1];
   962             break;
   963           }
   964         }
   965 
   966         if ((cip = Jump(plugin, ctx, target)) == NULL)
   967           goto error;
   968         break;
   969       }
   970 
   971       default:
   972       {
   973         ctx->err = SP_ERROR_INVALID_INSTRUCTION;
   974         goto error;
   975       }
   976     } // switch
   977   }
   978 
   979  done:
   980   assert(orig_frm == ctx->frm);
   981   ctx->sp = uintptr_t(stk) - uintptr_t(plugin->memory);
   982   return ctx->err;
   983 
   984  error:
   985   ctx->frm = orig_frm;
   986   goto done;
   987 }
   988