sourcepawn/jit/interpreter.cpp
author David Anderson <dvander@alliedmods.net>
Sun Sep 01 00:23:44 2013 -0700 (2013-09-01)
changeset 4116 3e58428d988f
child 4156 319e80e8e97f
permissions -rw-r--r--
Add an interpreter because. (bug 5902, r=ds).
     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 static inline bool
   171 GenerateArray(BaseRuntime *rt, sp_context_t *ctx, cell_t dims, cell_t *stk, bool autozero)
   172 {
   173   if (dims == 1) {
   174     uint32_t size = *stk;
   175     if (size == 0 || !ke::IsUint32MultiplySafe(size, 4)) {
   176       ctx->err = SP_ERROR_ARRAY_TOO_BIG;
   177       return false;
   178     }
   179     *stk = ctx->hp;
   180 
   181     uint32_t bytes = size * 4;
   182 
   183     ctx->hp += bytes;
   184     if (uintptr_t(ctx->plugin->memory + ctx->hp) >= uintptr_t(stk)) {
   185       ctx->err = SP_ERROR_HEAPLOW;
   186       return false;
   187     }
   188 
   189     if ((ctx->err = PushTracker(ctx, bytes)) != SP_ERROR_NONE)
   190       return false;
   191 
   192     if (autozero)
   193       memset(ctx->plugin->memory + ctx->hp, 0, bytes);
   194 
   195     return true;
   196   }
   197 
   198   if ((ctx->err = GenerateFullArray(rt, dims, stk, autozero)) != SP_ERROR_NONE)
   199     return false;
   200 
   201   return true;
   202 }
   203 
   204 int
   205 Interpret(BaseRuntime *rt, uint32_t aCodeStart, cell_t *rval)
   206 {
   207   const sp_plugin_t *plugin = rt->plugin();
   208   cell_t *code = reinterpret_cast<cell_t *>(plugin->pcode);
   209   cell_t *codeend = reinterpret_cast<cell_t *>(plugin->pcode + plugin->pcode_size);
   210 
   211   if (!IsValidOffset(aCodeStart) || aCodeStart > plugin->pcode_size)
   212     return SP_ERROR_INVALID_INSTRUCTION;
   213 
   214   sp_context_t *ctx = rt->GetBaseContext()->GetCtx();
   215   ctx->err = SP_ERROR_NONE;
   216 
   217   // Save the original frm. BaseContext won't, and if we error, we won't hit
   218   // the stack unwinding code.
   219   cell_t orig_frm = ctx->frm;
   220 
   221   cell_t pri = 0;
   222   cell_t alt = 0;
   223   cell_t *cip = code + (aCodeStart / 4);
   224   cell_t *stk = reinterpret_cast<cell_t *>(plugin->memory + ctx->sp);
   225 
   226   for (;;) {
   227     if (cip >= codeend) {
   228       ctx->err = SP_ERROR_INVALID_INSTRUCTION;
   229       goto error;
   230     }
   231 
   232 #if 0
   233     SpewOpcode(plugin, reinterpret_cast<cell_t *>(plugin->pcode + aCodeStart), cip);
   234 #endif
   235 
   236     OPCODE op = (OPCODE)*cip++;
   237 
   238     switch (op) {
   239       case OP_MOVE_PRI:
   240         pri = alt;
   241         break;
   242       case OP_MOVE_ALT:
   243         alt = pri;
   244         break;
   245 
   246       case OP_XCHG:
   247       {
   248         cell_t tmp = pri;
   249         pri = alt;
   250         alt = tmp;
   251         break;
   252       }
   253 
   254       case OP_ZERO:
   255         Write(plugin, *cip++, 0);
   256         break;
   257 
   258       case OP_ZERO_S:
   259         Write(plugin, *cip++, 0);
   260         break;
   261 
   262       case OP_PUSH_PRI:
   263         *--stk = pri;
   264         break;
   265       case OP_PUSH_ALT:
   266         *--stk = alt;
   267         break;
   268 
   269       case OP_PUSH_C:
   270       case OP_PUSH2_C:
   271       case OP_PUSH3_C:
   272       case OP_PUSH4_C:
   273       case OP_PUSH5_C:
   274       {
   275         int n = 1;
   276         if (op >= OP_PUSH2_C)
   277           n = ((op - OP_PUSH2_C) / 4) + 2;
   278 
   279         int i = 1;
   280         do {
   281           *--stk = *cip++;
   282         } while (i++ < n);
   283         break;
   284       }
   285 
   286       case OP_PUSH_ADR:
   287       case OP_PUSH2_ADR:
   288       case OP_PUSH3_ADR:
   289       case OP_PUSH4_ADR:
   290       case OP_PUSH5_ADR:
   291       {
   292         int n = 1;
   293         if (op >= OP_PUSH2_ADR)
   294           n = ((op - OP_PUSH2_ADR) / 4) + 2;
   295 
   296         int i = 1;
   297 
   298         do {
   299           cell_t addr = ctx->frm + *cip++;
   300           *--stk = addr;
   301         } while (i++ < n);
   302         break;
   303       }
   304 
   305       case OP_PUSH_S:
   306       case OP_PUSH2_S:
   307       case OP_PUSH3_S:
   308       case OP_PUSH4_S:
   309       case OP_PUSH5_S:
   310       {
   311         int n = 1;
   312         if (op >= OP_PUSH2_S)
   313           n = ((op - OP_PUSH2_S) / 4) + 2;
   314 
   315         int i = 1;
   316         do {
   317           cell_t value = Read(plugin, ctx->frm + *cip++);
   318           *--stk = value;
   319         } while (i++ < n);
   320         break;
   321       }
   322 
   323       case OP_PUSH:
   324       case OP_PUSH2:
   325       case OP_PUSH3:
   326       case OP_PUSH4:
   327       case OP_PUSH5:
   328       {
   329         int n = 1;
   330         if (op >= OP_PUSH2)
   331           n = ((op - OP_PUSH2) / 4) + 2;
   332 
   333         int i = 1;
   334         do {
   335           cell_t value = Read(plugin, *cip++);
   336           *--stk = value;
   337         } while (i++ < n);
   338         break;
   339       }
   340 
   341       case OP_ZERO_PRI:
   342         pri = 0;
   343         break;
   344       case OP_ZERO_ALT:
   345         alt = 0;
   346         break;
   347 
   348       case OP_ADD:
   349         pri += alt;
   350         break;
   351 
   352       case OP_SUB:
   353         pri -= alt;
   354         break;
   355 
   356       case OP_SUB_ALT:
   357         pri = alt - pri;
   358         break;
   359 
   360       case OP_PROC:
   361       {
   362         *--stk = ctx->frm;
   363         *--stk = 0;
   364         ctx->frm = uintptr_t(stk) - uintptr_t(plugin->memory);
   365         break;
   366       }
   367 
   368       case OP_IDXADDR_B:
   369         pri <<= *cip++;
   370         pri += alt;
   371         break;
   372 
   373       case OP_SHL:
   374         pri <<= alt;
   375         break;
   376 
   377       case OP_SHR:
   378         pri = unsigned(pri) >> unsigned(alt);
   379         break;
   380 
   381       case OP_SSHR:
   382         pri >>= alt;
   383         break;
   384 
   385       case OP_SHL_C_PRI:
   386         pri <<= *cip++;
   387         break;
   388       case OP_SHL_C_ALT:
   389         alt <<= *cip++;
   390         break;
   391 
   392       case OP_SHR_C_PRI:
   393         pri >>= *cip++;
   394         break;
   395       case OP_SHR_C_ALT:
   396         alt >>= *cip++;
   397         break;
   398 
   399       case OP_SMUL:
   400         pri *= alt;
   401         break;
   402 
   403       case OP_NOT:
   404         pri = pri ? 0 : 1;
   405         break;
   406 
   407       case OP_NEG:
   408         pri = -pri;
   409         break;
   410 
   411       case OP_XOR:
   412         pri ^= alt;
   413         break;
   414 
   415       case OP_OR:
   416         pri |= alt;
   417         break;
   418 
   419       case OP_AND:
   420         pri &= alt;
   421         break;
   422 
   423       case OP_INVERT:
   424         pri = ~pri;
   425         break;
   426 
   427       case OP_ADD_C:
   428         pri += *cip++;
   429         break;
   430 
   431       case OP_SMUL_C:
   432         pri *= *cip++;
   433         break;
   434 
   435       case OP_EQ:
   436         pri = pri == alt;
   437         break;
   438 
   439       case OP_NEQ:
   440         pri = pri != alt;
   441         break;
   442 
   443       case OP_SLESS:
   444         pri = pri < alt;
   445         break;
   446 
   447       case OP_SLEQ:
   448         pri = pri <= alt;
   449         break;
   450 
   451       case OP_SGRTR:
   452         pri = pri > alt;
   453         break;
   454         
   455       case OP_SGEQ:
   456         pri = pri >= alt;
   457         break;
   458 
   459       case OP_EQ_C_PRI:
   460         pri = pri == *cip++;
   461         break;
   462       case OP_EQ_C_ALT:
   463         pri = alt == *cip++;
   464         break;
   465 
   466       case OP_INC_PRI:
   467         pri++;
   468         break;
   469       case OP_INC_ALT:
   470         alt++;
   471         break;
   472 
   473       case OP_INC:
   474       {
   475         cell_t offset = *cip++;
   476         Write(plugin, offset, Read(plugin, offset) + 1);
   477         break;
   478       }
   479 
   480       case OP_INC_S:
   481       {
   482         cell_t offset = *cip++;
   483         cell_t value = Read(plugin, ctx->frm + offset);
   484         Write(plugin, ctx->frm + offset, value + 1);
   485         break;
   486       }
   487 
   488       case OP_INC_I:
   489         if (!CheckAddress(plugin, ctx, stk, pri))
   490           goto error;
   491         Write(plugin, pri, Read(plugin, pri) + 1);
   492         break;
   493 
   494       case OP_DEC_PRI:
   495         pri--;
   496         break;
   497       case OP_DEC_ALT:
   498         alt--;
   499         break;
   500 
   501       case OP_DEC:
   502       {
   503         cell_t offset = *cip++;
   504         Write(plugin, offset, Read(plugin, offset) - 1);
   505         break;
   506       }
   507 
   508       case OP_DEC_S:
   509       {
   510         cell_t offset = *cip++;
   511         cell_t value = Read(plugin, ctx->frm + offset);
   512         Write(plugin, ctx->frm + offset, value - 1);
   513         break;
   514       }
   515 
   516       case OP_DEC_I:
   517         if (!CheckAddress(plugin, ctx, stk, pri))
   518           goto error;
   519         Write(plugin, pri, Read(plugin, pri) - 1);
   520         break;
   521 
   522       case OP_LOAD_PRI:
   523         pri = Read(plugin, *cip++);
   524         break;
   525       case OP_LOAD_ALT:
   526         alt = Read(plugin, *cip++);
   527         break;
   528 
   529       case OP_LOAD_S_PRI:
   530         pri = Read(plugin, ctx->frm + *cip++);
   531         break;
   532       case OP_LOAD_S_ALT:
   533         alt = Read(plugin, ctx->frm + *cip++);
   534         break;
   535 
   536       case OP_LOAD_S_BOTH:
   537         pri = Read(plugin, ctx->frm + *cip++);
   538         alt = Read(plugin, ctx->frm + *cip++);
   539         break;
   540 
   541       case OP_LREF_S_PRI:
   542       {
   543         pri = Read(plugin, ctx->frm + *cip++);
   544         pri = Read(plugin, pri);
   545         break;
   546       }
   547 
   548       case OP_LREF_S_ALT:
   549       {
   550         alt = Read(plugin, ctx->frm + *cip++);
   551         alt = Read(plugin, alt);
   552         break;
   553       }
   554 
   555       case OP_CONST_PRI:
   556         pri = *cip++;
   557         break;
   558       case OP_CONST_ALT:
   559         alt = *cip++;
   560         break;
   561 
   562       case OP_ADDR_PRI:
   563         pri = ctx->frm + *cip++;
   564         break;
   565       case OP_ADDR_ALT:
   566         alt = ctx->frm + *cip++;
   567         break;
   568 
   569       case OP_STOR_PRI:
   570         Write(plugin, *cip++, pri);
   571         break;
   572       case OP_STOR_ALT:
   573         Write(plugin, *cip++, alt);
   574         break;
   575 
   576       case OP_STOR_S_PRI:
   577         Write(plugin, ctx->frm + *cip++, pri);
   578         break;
   579       case OP_STOR_S_ALT:
   580         Write(plugin, ctx->frm +*cip++, alt);
   581         break;
   582 
   583       case OP_IDXADDR:
   584         pri = alt + pri * 4;
   585         break;
   586 
   587       case OP_SREF_S_PRI:
   588       {
   589         cell_t offset = *cip++;
   590         cell_t addr = Read(plugin, ctx->frm + offset);
   591         Write(plugin, addr, pri);
   592         break;
   593       }
   594 
   595       case OP_SREF_S_ALT:
   596       {
   597         cell_t offset = *cip++;
   598         cell_t addr = Read(plugin, ctx->frm + offset);
   599         Write(plugin, addr, alt);
   600         break;
   601       }
   602 
   603       case OP_POP_PRI:
   604         pri = *stk++;
   605         break;
   606       case OP_POP_ALT:
   607         alt = *stk++;
   608         break;
   609 
   610       case OP_SWAP_PRI:
   611       case OP_SWAP_ALT:
   612       {
   613         cell_t reg = (op == OP_SREF_S_PRI) ? pri : alt;
   614         cell_t temp = *stk;
   615         *stk = reg;
   616         reg = temp;
   617         break;
   618       }
   619 
   620       case OP_LIDX:
   621         pri = alt + pri * 4;
   622         if (!CheckAddress(plugin, ctx, stk, pri))
   623           goto error;
   624         pri = Read(plugin, pri);
   625         break;
   626 
   627       case OP_LIDX_B:
   628       {
   629         cell_t val = *cip++;
   630         pri = alt + (pri << val);
   631         if (!CheckAddress(plugin, ctx, stk, pri))
   632           goto error;
   633         pri = Read(plugin, pri);
   634         break;
   635       }
   636 
   637       case OP_CONST:
   638       {
   639         cell_t offset = *cip++;
   640         cell_t value = *cip++;
   641         Write(plugin, offset, value);
   642         break;
   643       }
   644 
   645       case OP_CONST_S:
   646       {
   647         cell_t offset = *cip++;
   648         cell_t value = *cip++;
   649         Write(plugin, ctx->frm + offset, value);
   650         break;
   651       }
   652 
   653       case OP_LOAD_I:
   654         if (!CheckAddress(plugin, ctx, stk, pri))
   655           goto error;
   656         pri = Read(plugin, pri);
   657         break;
   658 
   659       case OP_STOR_I:
   660         if (!CheckAddress(plugin, ctx, stk, alt))
   661           goto error;
   662         Write(plugin, alt, pri);
   663         break;
   664 
   665       case OP_SDIV:
   666       case OP_SDIV_ALT:
   667       {
   668         cell_t dividend = (op == OP_SDIV) ? pri : alt;
   669         cell_t divisor = (op == OP_SDIV) ? alt : pri;
   670         if (divisor == 0) {
   671           ctx->err = SP_ERROR_DIVIDE_BY_ZERO;
   672           goto error;
   673         }
   674         if (dividend == INT_MIN && divisor == -1) {
   675           ctx->err = SP_ERROR_INTEGER_OVERFLOW;
   676           goto error;
   677         }
   678         pri = dividend / divisor;
   679         alt = dividend % divisor;
   680         break;
   681       }
   682       
   683       case OP_LODB_I:
   684       {
   685         cell_t val = *cip++;
   686         if (!CheckAddress(plugin, ctx, stk, pri))
   687           goto error;
   688         pri = Read(plugin, pri);
   689         if (val == 1)
   690           pri &= 0xff;
   691         else if (val == 2)
   692           pri &= 0xffff;
   693         break;
   694       }
   695 
   696       case OP_STRB_I:
   697       {
   698         cell_t val = *cip++;
   699         if (!CheckAddress(plugin, ctx, stk, alt))
   700           goto error;
   701         if (val == 1)
   702           *reinterpret_cast<int8_t *>(plugin->memory + alt) = pri;
   703         else if (val == 2)
   704           *reinterpret_cast<int16_t *>(plugin->memory + alt) = pri;
   705         else if (val == 4)
   706           *reinterpret_cast<int32_t *>(plugin->memory + alt) = pri;
   707         break;
   708       }
   709 
   710       case OP_RETN:
   711       {
   712         stk++;
   713         ctx->frm = *stk++;
   714         stk += *stk + 1;
   715         *rval = pri;
   716         ctx->err = SP_ERROR_NONE;
   717         goto done;
   718       }
   719 
   720       case OP_MOVS:
   721       {
   722         uint8_t *src = plugin->memory + pri;
   723         uint8_t *dest = plugin->memory + alt;
   724         memcpy(dest, src, *cip++);
   725         break;
   726       }
   727 
   728       case OP_FILL:
   729       {
   730         uint8_t *dest = plugin->memory + alt;
   731         memset(dest, pri, *cip++);
   732         break;
   733       }
   734 
   735       case OP_STRADJUST_PRI:
   736         pri += 4;
   737         pri >>= 2;
   738         break;
   739 
   740       case OP_STACK:
   741       {
   742         cell_t amount = *cip++;
   743         if (!IsValidOffset(amount)) {
   744           ctx->err = SP_ERROR_INVALID_INSTRUCTION;
   745           goto error;
   746         }
   747 
   748         stk += amount / 4;
   749         if (amount > 0) {
   750           if (uintptr_t(stk) >= uintptr_t(plugin->memory + plugin->mem_size)) {
   751             ctx->err = SP_ERROR_STACKMIN;
   752             goto error;
   753           }
   754         } else {
   755           if (uintptr_t(stk) < uintptr_t(plugin->memory + ctx->hp + STACK_MARGIN)) {
   756             ctx->err = SP_ERROR_STACKLOW;
   757             goto error;
   758           }
   759         }
   760         break;
   761       }
   762 
   763       case OP_HEAP:
   764       {
   765         cell_t amount = *cip++;
   766 
   767         alt = ctx->hp;
   768         ctx->hp += amount;
   769 
   770         if (amount > 0) {
   771           if (uintptr_t(plugin->memory + ctx->hp) > uintptr_t(stk)) {
   772             ctx->err = SP_ERROR_HEAPLOW;
   773             goto error;
   774           }
   775         } else {
   776           if (uint32_t(ctx->hp) < plugin->data_size) {
   777             ctx->err = SP_ERROR_HEAPMIN;
   778             goto error;
   779           }
   780         }
   781         break;
   782       }
   783 
   784       case OP_JUMP:
   785         if ((cip = JumpTarget(plugin, ctx, cip, true)) == NULL)
   786           goto error;
   787         break;
   788 
   789       case OP_JZER:
   790         if ((cip = JumpTarget(plugin, ctx, cip, pri == 0)) == NULL)
   791           goto error;
   792         break;
   793       case OP_JNZ:
   794         if ((cip = JumpTarget(plugin, ctx, cip, pri != 0)) == NULL)
   795           goto error;
   796         break;
   797 
   798       case OP_JEQ:
   799         if ((cip = JumpTarget(plugin, ctx, cip, pri == alt)) == NULL)
   800           goto error;
   801         break;
   802       case OP_JNEQ:
   803         if ((cip = JumpTarget(plugin, ctx, cip, pri != alt)) == NULL)
   804           goto error;
   805         break;
   806       case OP_JSLESS:
   807         if ((cip = JumpTarget(plugin, ctx, cip, pri < alt)) == NULL)
   808           goto error;
   809         break;
   810       case OP_JSLEQ:
   811         if ((cip = JumpTarget(plugin, ctx, cip, pri <= alt)) == NULL)
   812           goto error;
   813         break;
   814       case OP_JSGRTR:
   815         if ((cip = JumpTarget(plugin, ctx, cip, pri > alt)) == NULL)
   816           goto error;
   817         break;
   818       case OP_JSGEQ:
   819         if ((cip = JumpTarget(plugin, ctx, cip, pri >= alt)) == NULL)
   820           goto error;
   821         break;
   822 
   823       case OP_TRACKER_PUSH_C:
   824       {
   825         cell_t amount = *cip++;
   826         int error = PushTracker(ctx, amount * 4);
   827         if (error != SP_ERROR_NONE) {
   828           ctx->err = error;
   829           goto error;
   830         }
   831         break;
   832       }
   833 
   834       case OP_TRACKER_POP_SETHEAP:
   835       {
   836         int error = PopTrackerAndSetHeap(rt);
   837         if (error != SP_ERROR_NONE) {
   838           ctx->err = error;
   839           goto error;
   840         }
   841         break;
   842       }
   843 
   844       case OP_BREAK:
   845         ctx->cip = uintptr_t(cip - 1) - uintptr_t(plugin->pcode);
   846         break;
   847 
   848       case OP_BOUNDS:
   849       {
   850         cell_t value = *cip++;
   851         if (uint32_t(pri) > uint32_t(value)) {
   852           ctx->err = SP_ERROR_ARRAY_BOUNDS;
   853           goto error;
   854         }
   855         break;
   856       }
   857 
   858       case OP_CALL:
   859       {
   860         cell_t offset = *cip++;
   861 
   862         if (!IsValidOffset(offset) || uint32_t(offset) >= plugin->pcode_size) {
   863           ctx->err = SP_ERROR_INSTRUCTION_PARAM;
   864           goto error;
   865         }
   866 
   867         if (ctx->rp >= SP_MAX_RETURN_STACK) {
   868           ctx->err = SP_ERROR_STACKLOW;
   869           goto error;
   870         }
   871 
   872         // For debugging.
   873         uintptr_t rcip = uintptr_t(cip - 2) - uintptr_t(plugin->pcode);
   874         ctx->rstk_cips[ctx->rp++] = rcip;
   875         ctx->cip = offset;
   876         ctx->sp = uintptr_t(stk) - uintptr_t(plugin->memory);
   877 
   878         int err = Interpret(rt, offset, &pri);
   879 
   880         stk = reinterpret_cast<cell_t *>(plugin->memory + ctx->sp);
   881         ctx->cip = rcip;
   882         ctx->rp--;
   883 
   884         if (err != SP_ERROR_NONE)
   885           goto error;
   886         break;
   887       }
   888 
   889       case OP_GENARRAY:
   890       case OP_GENARRAY_Z:
   891       {
   892         cell_t val = *cip++;
   893         if (!GenerateArray(rt, ctx, val, stk, op == OP_GENARRAY_Z))
   894           goto error;
   895 
   896         stk += (val - 1) * 4;
   897         break;
   898       }
   899 
   900       case OP_SYSREQ_C:
   901       case OP_SYSREQ_N:
   902       {
   903         uint32_t native_index = *cip++;
   904 
   905         if (native_index >= plugin->num_natives) {
   906           ctx->err = SP_ERROR_INSTRUCTION_PARAM;
   907           goto error;
   908         }
   909 
   910         uint32_t num_params;
   911         if (op == OP_SYSREQ_N) {
   912           num_params = *cip++;
   913           *--stk = num_params;
   914         }
   915 
   916         ctx->sp = uintptr_t(stk) - uintptr_t(plugin->memory);
   917         pri = NativeCallback(ctx, native_index, stk);
   918         if (ctx->n_err != SP_ERROR_NONE) {
   919           ctx->err = ctx->n_err;
   920           goto error;
   921         }
   922 
   923         if (op == OP_SYSREQ_N)
   924           stk += num_params + 1;
   925         break;
   926       }
   927 
   928       case OP_SWITCH:
   929       {
   930         cell_t offset = *cip++;
   931         cell_t *table = reinterpret_cast<cell_t *>(plugin->pcode + offset + sizeof(cell_t));
   932 
   933         size_t ncases = *table++;
   934         cell_t target = *table++;   // default case
   935 
   936         for (size_t i = 0; i < ncases; i++) {
   937           if (table[i * 2] == pri) {
   938             target = table[i * 2 + 1];
   939             break;
   940           }
   941         }
   942 
   943         if ((cip = Jump(plugin, ctx, target)) == NULL)
   944           goto error;
   945         break;
   946       }
   947 
   948       default:
   949       {
   950         ctx->err = SP_ERROR_INVALID_INSTRUCTION;
   951         goto error;
   952       }
   953     } // switch
   954   }
   955 
   956  done:
   957   assert(orig_frm == ctx->frm);
   958   ctx->sp = uintptr_t(stk) - uintptr_t(plugin->memory);
   959   return ctx->err;
   960 
   961  error:
   962   ctx->frm = orig_frm;
   963   goto done;
   964 }
   965