jitcraft/Interpreter.cpp
author David Anderson <dvander@alliedmods.net>
Thu Jun 28 00:26:55 2012 -0700 (2012-06-28)
changeset 8 069d207a8f2a
parent 7 0478c27ba82c
child 10 0c3bcd50e361
permissions -rw-r--r--
Added support for bitwise operators.
     1 /*
     2  * Copyright (C) 2012 David Anderson
     3  *
     4  * This file is part of JITCraft.
     5  *
     6  * JITCraft is free software: you can redistribute it and/or modify it under
     7  * the terms of the GNU General Public License as published by the Free
     8  * Software Foundation, either version 3 of the License, or (at your option)
     9  * any later version.
    10  * 
    11  * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY
    12  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    13  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
    14  *
    15  * You should have received a copy of the GNU General Public License along with
    16  * JITCraft. If not, see http://www.gnu.org/licenses/.
    17  */
    18 #include "Interpreter.h"
    19 #include "Modules.h"
    20 #include "Zone.h"
    21 #include "Types.h"
    22 
    23 using namespace jc;
    24 
    25 #define PUSH_I32(top, value)    \
    26     do {                        \
    27         top.sp->i32 = value;    \
    28         top.sp++;               \
    29    } while (0)
    30 
    31 #define PUSH_F(top, value)      \
    32     do {                        \
    33         top.sp->f = value;      \
    34         top.sp++;               \
    35    } while (0)
    36 
    37 static inline int
    38 POP_I32(Top &top)
    39 {
    40     int value = top.sp[-1].i32;
    41     top.sp--;
    42     return value;
    43 }
    44 
    45 static inline float
    46 POP_F(Top &top)
    47 {
    48     float value = top.sp[-1].f;
    49     top.sp--;
    50     return value;
    51 }
    52 
    53 static inline bool
    54 CheckDimensionSize(int size, unsigned width)
    55 {
    56     if (size <= 0) {
    57         assert(false);
    58         return false;
    59     }
    60     // One  word, because we hide stuff below and need to add that in.
    61     if (unsigned(size) >= (INT_MAX / width) - sizeof(unsigned)) {
    62         assert(false);
    63         return false;
    64     }
    65     return true;
    66 }
    67 
    68 struct ArrayBuilder
    69 {
    70     char *base;
    71     unsigned width;
    72     unsigned ndims;
    73     int *dims;
    74 };
    75 
    76 // Unlike the old SourcePawn VM, we do not allocate the final levels in one
    77 // contiguous block. This might result in different cache behavior, but who
    78 // knows. This algorithm is simpler and faster.
    79 //
    80 // In pawn, an array reference a[b][c]
    81 //
    82 //   (&a[b] + a[b])[c]
    83 //
    84 // That is, when hopping to the next array, the slot is relative to the
    85 // current address, not absolute.
    86 //
    87 static unsigned
    88 GenerateIndirectionVectors(const ArrayBuilder &builder, unsigned cursor, unsigned level)
    89 {
    90     unsigned current = cursor;
    91 
    92     // Reserve space for this level.
    93     cursor += builder.dims[level] *
    94               ((level == builder.ndims - 1) ? builder.width : sizeof(unsigned));
    95 
    96     // If we're at the last level, we're done.
    97     if (level == builder.ndims - 1)
    98         return cursor;
    99 
   100     for (unsigned i = 0; i < unsigned(builder.dims[level]); i++) {
   101         // Hide the number of elements in the next vector, right behind it.
   102         *reinterpret_cast<unsigned *>(builder.base + cursor) = builder.dims[level + 1];
   103         cursor += sizeof(unsigned);
   104 
   105         // The next hop will be generated at |cursor|, which will be read from |current|.
   106         *reinterpret_cast<unsigned *>(builder.base + current) = cursor - current;
   107 
   108         cursor = GenerateIndirectionVectors(builder, cursor, level + 1);
   109         current += sizeof(unsigned);
   110     }
   111 
   112     return cursor;
   113 }
   114 
   115 UnsafeArray *
   116 jc::AllocateUnsafeArray(unsigned width, unsigned ndims, int *dims)
   117 {
   118     // Sanity check.
   119     if (!CheckDimensionSize(dims[ndims - 1], width))
   120         return NULL;
   121 
   122     // All innermost vectors must be this size.
   123     size_t nbytes = dims[ndims - 1] * width + sizeof(unsigned);
   124 
   125     if (ndims == 1) {
   126         // Fast path, no complicated indirection vectors.
   127         unsigned *ptr = (unsigned *)ZONE()->unsafeStack().allocate(nbytes);
   128         if (!ptr)
   129             return NULL;
   130         memset(ptr, 0, nbytes);
   131 
   132         *ptr = dims[0];
   133 
   134         UnsafeArray *array = reinterpret_cast<UnsafeArray *>(ptr + 1);
   135         return array;
   136     }
   137 
   138     // Add space from the innermost indrect array to the outermost. Each time,
   139     // we must also add space for the indirection slots.
   140     for (unsigned i = ndims - 2; i < ndims; i--) {
   141         if (!CheckDimensionSize(dims[ndims - 1], sizeof(unsigned) * 2))
   142             return NULL;
   143 
   144         if (!IsUint32MultiplySafe(nbytes, dims[i])) {
   145             assert(false);
   146             return NULL;
   147         }
   148 
   149         nbytes *= dims[i];
   150 
   151         // Add space for the indirection vectors at this level.
   152         if (!IsUint32AddSafe(nbytes, dims[i] * sizeof(unsigned) + sizeof(unsigned))) {
   153             assert(false);
   154             return NULL;
   155         }
   156 
   157         // Reserve space for each indirection vector.
   158         nbytes += dims[i] * sizeof(unsigned);
   159 
   160         // Reserve space for our own indirection vector.
   161         nbytes += sizeof(unsigned);
   162     }
   163 
   164     void *base = ZONE()->unsafeStack().allocate(nbytes);
   165     if (!base)
   166         return NULL;
   167     memset(base, 0, nbytes);
   168 
   169     ArrayBuilder array;
   170     array.base = (char *)base;
   171     array.dims = dims;
   172     array.ndims = ndims;
   173     array.width = width;
   174 
   175     unsigned cursor = 0;
   176 
   177     // Hide the length behind the array.
   178     *reinterpret_cast<unsigned *>(array.base + cursor) = dims[0];
   179     cursor += sizeof(unsigned);
   180     
   181     cursor = GenerateIndirectionVectors(array, cursor, 0);
   182     assert(cursor == nbytes);
   183 
   184     return reinterpret_cast<UnsafeArray *>(reinterpret_cast<unsigned *>(base) + 1);
   185 }
   186 
   187 bool
   188 jc::Interpret(Frame *frame, LocalSlot *result)
   189 {
   190     Top &top = *ZONE()->stack().top();
   191     LocalSlot *locals = frame->locals();
   192 
   193     for (;;) {
   194         Opcode op = Opcode(*top.pc);
   195         switch (op) {
   196 
   197 case OP_NOP:
   198     break;
   199 
   200 case OP_INT:
   201     PUSH_I32(top, READ_INT32(top.pc));
   202     break;
   203 
   204 case OP_FLOAT:
   205     PUSH_F(top, READ_FLOAT(top.pc));
   206     break;
   207 
   208 case OP_RETURN:
   209 {
   210     *result = *--top.sp;
   211     if (!frame->prev())
   212         return true;
   213 
   214     frame = ZONE()->stack().popFrame(frame);
   215     locals = frame->locals();
   216     *top.sp++ = *result;
   217     top.pc += OpcodeInfo[*top.pc].length;
   218     continue;
   219 }
   220 
   221 #define RELATIONAL_JUMP(op)                 \
   222     {                                       \
   223         int right = POP_I32(top);           \
   224         int left = POP_I32(top);            \
   225         if (left op right) {                \
   226             top.pc += READ_INT32(top.pc);   \
   227             continue;                       \
   228         }                                   \
   229     }
   230 
   231 case OP_JL:
   232     RELATIONAL_JUMP(<);
   233     break;
   234 
   235 case OP_JLE:
   236     RELATIONAL_JUMP(<=);
   237     break;
   238 
   239 case OP_JG:
   240     RELATIONAL_JUMP(>);
   241     break;
   242 
   243 case OP_JGE:
   244     RELATIONAL_JUMP(>=);
   245     break;
   246 
   247 case OP_JEQ:
   248     RELATIONAL_JUMP(==);
   249     break;
   250 
   251 case OP_JNE:
   252     RELATIONAL_JUMP(!=);
   253     break;
   254 
   255 case OP_JT:
   256 {
   257     int value = POP_I32(top);
   258     if (value) {
   259         top.pc += READ_INT32(top.pc);
   260         continue;
   261     }
   262     break;
   263 }
   264 
   265 case OP_JF:
   266 {
   267     int value = POP_I32(top);
   268     if (!value) {
   269         top.pc += READ_INT32(top.pc);
   270         continue;
   271     }
   272     break;
   273 }
   274 
   275 case OP_JUMP:
   276     top.pc += READ_INT32(top.pc);
   277     continue;
   278 
   279 case OP_GETLOCAL:
   280 {
   281     int slot = READ_INT32(top.pc);
   282     *top.sp++ = locals[slot];
   283     break;
   284 }
   285 
   286 case OP_SETLOCAL:
   287 {
   288     int slot = READ_INT32(top.pc);
   289     locals[slot] = top.sp[-1];
   290     break;
   291 }
   292 
   293 case OP_ADD:
   294 {
   295     int right = POP_I32(top);
   296     int left = POP_I32(top);
   297     int result = left + right;
   298     PUSH_I32(top, result);
   299     break;
   300 }
   301 
   302 case OP_SUB:
   303 {
   304     int right = POP_I32(top);
   305     int left = POP_I32(top);
   306     int result = left - right;
   307     PUSH_I32(top, result);
   308     break;
   309 }
   310 
   311 case OP_MUL:
   312 {
   313     int right = POP_I32(top);
   314     int left = POP_I32(top);
   315     int result = left * right;
   316    PUSH_I32(top, result);
   317     break;
   318 }
   319 
   320 case OP_DIV:
   321 {
   322     int right = POP_I32(top);
   323     int left = POP_I32(top);
   324     if (!right)
   325         return false;
   326     int result = left / right;
   327     PUSH_I32(top, result);
   328     break;
   329 }
   330 
   331 case OP_ADD_F:
   332 {
   333     float right = POP_F(top);
   334     float left = POP_F(top);
   335     float result = left + right;
   336     PUSH_F(top, result);
   337     break;
   338 }
   339 
   340 case OP_SUB_F:
   341 {
   342     float right = POP_F(top);
   343     float left = POP_F(top);
   344     float result = left - right;
   345     PUSH_F(top, result);
   346     break;
   347 }
   348 
   349 case OP_MUL_F:
   350 {
   351     float right = POP_F(top);
   352     float left = POP_F(top);
   353     float result = left * right;
   354     PUSH_F(top, result);
   355     break;
   356 }
   357 
   358 case OP_DIV_F:
   359 {
   360     float right = POP_F(top);
   361     float left = POP_F(top);
   362     if (!right)
   363         return false;
   364     float result = left / right;
   365     PUSH_F(top, result);
   366     break;
   367 }
   368 
   369 case OP_SHL:
   370 {
   371     int right = POP_I32(top);
   372     int left = POP_I32(top);
   373     int result = left << right;
   374     PUSH_I32(top, result);
   375     break;
   376 }
   377 
   378 case OP_SHR:
   379 {
   380     int right = POP_I32(top);
   381     int left = POP_I32(top);
   382     int result = left >> right;
   383     PUSH_I32(top, result);
   384     break;
   385 }
   386 
   387 case OP_USHR:
   388 {
   389     int right = POP_I32(top);
   390     int left = POP_I32(top);
   391     int result = unsigned(left) >> unsigned(right);
   392     PUSH_I32(top, result);
   393     break;
   394 }
   395 
   396 case OP_BITAND:
   397 {
   398     int right = POP_I32(top);
   399     int left = POP_I32(top);
   400     int result = left & right;
   401     PUSH_I32(top, result);
   402     break;
   403 }
   404 
   405 case OP_BITOR:
   406 {
   407     int right = POP_I32(top);
   408     int left = POP_I32(top);
   409     int result = left | right;
   410     PUSH_I32(top, result);
   411     break;
   412 }
   413 
   414 case OP_BITXOR:
   415 {
   416     int right = POP_I32(top);
   417     int left = POP_I32(top);
   418     int result = left ^ right;
   419     PUSH_I32(top, result);
   420     break;
   421 }
   422 
   423 case OP_POP:
   424     top.sp--;
   425     break;
   426 
   427 case OP_STOP:
   428     assert(false);
   429     break;
   430 
   431 case OP_CALLGLOBAL:
   432 {
   433     unsigned argc = READ_UINT32(top.pc);
   434     unsigned index = READ_UINT32(top.pc + 4);
   435     Code *newCode = frame->code()->module()->getFunction(index);
   436     frame = ZONE()->stack().pushFrame(frame, newCode);
   437     assert(frame);
   438     assert(newCode->nargs() == argc);
   439 
   440     locals = frame->locals();
   441     continue;
   442 }
   443 
   444 case OP_GETARG:
   445 {
   446     unsigned slot = READ_UINT32(top.pc);
   447     *top.sp++ = frame->arg(slot);
   448     break;
   449 }
   450 
   451 case OP_SETARG:
   452 {
   453     unsigned slot = READ_UINT32(top.pc);
   454     frame->arg(slot) = *--top.sp;
   455     break;
   456 }
   457 
   458 case OP_STACKARRAY:
   459 {
   460     unsigned width = top.pc[1];
   461     unsigned ndims = top.pc[2];
   462     UnsafeArray *ptr = AllocateUnsafeArray(width, ndims, reinterpret_cast<int *>(top.pc + 3));
   463     if (!ptr)
   464         return false;
   465 
   466     top.sp->unsafeArray = ptr;
   467     top.sp++;
   468     top.pc += 3 + ndims * sizeof(int);
   469     continue;
   470 }
   471 
   472 case OP_BOUNDS_STA:
   473 {
   474     UnsafeArray *array = top.sp[-2].unsafeArray;
   475     int index = top.sp[-1].i32;
   476     if (index < 0 || unsigned(index) >= array->length())
   477         return false;
   478     break;
   479 }
   480 
   481 case OP_GETARRAY_I:
   482 {
   483     UnsafeArray *array = top.sp[-2].unsafeArray;
   484     int index = top.sp[-1].i32;
   485     top.sp[-2].i32 = array->readInt32(index);
   486     top.sp--;
   487     break;
   488 }
   489 
   490 case OP_GETARRAY_F:
   491 {
   492     UnsafeArray *array = top.sp[-2].unsafeArray;
   493     int index = top.sp[-1].i32;
   494     top.sp[-2].f = array->readFloat(index);
   495     top.sp--;
   496     break;
   497 }
   498 
   499 case OP_SETARRAY_I:
   500 {
   501     UnsafeArray *array = top.sp[-3].unsafeArray;
   502     int index = top.sp[-2].i32;
   503     array->writeInt32(index, top.sp[-1].i32);
   504     top.sp[-3] = top.sp[-1];
   505     top.sp -= 2;
   506     break;
   507 }
   508 
   509 case OP_SETARRAY_F:
   510 {
   511     UnsafeArray *array = top.sp[-3].unsafeArray;
   512     int index = top.sp[-2].i32;
   513     array->writeFloat(index, top.sp[-1].f);
   514     top.sp[-3] = top.sp[-1];
   515     top.sp -= 2;
   516     break;
   517 }
   518 
   519 case OP_GETARRAY_P:
   520 {
   521     UnsafeArray *array = top.sp[-2].unsafeArray;
   522     int index = top.sp[-1].i32;
   523     top.sp[-2].unsafeArray = array->indirect(index);
   524     top.sp--;
   525     break;
   526 }
   527 
   528 case OP_CVT_I2F:
   529 {
   530     int ival = POP_I32(top);
   531     float fval = (float)ival;
   532     PUSH_F(top, fval);
   533     break;
   534 }
   535 
   536 default:
   537     assert(false);
   538     return false;
   539 
   540         }
   541 
   542         top.pc += OpcodeInfo[op].length;
   543     }
   544 
   545     return true;
   546 }