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