jitcraft/BytecodeEmitter.h
author David Anderson <dvander@alliedmods.net>
Tue Jul 03 13:53:34 2012 -0400 (2012-07-03)
changeset 46 735503e448a0
parent 33 95baf120f7d4
child 47 050939c6181e
permissions -rw-r--r--
Remove 'argc' field of OP_CALLGLOBAL.
dvander@33
     1
/* vim: set ts=4 sw=4 tw=99 et:
dvander@33
     2
 *
dvander@0
     3
 * Copyright (C) 2012 David Anderson
dvander@0
     4
 *
dvander@0
     5
 * This file is part of JITCraft.
dvander@0
     6
 *
dvander@0
     7
 * JITCraft is free software: you can redistribute it and/or modify it under
dvander@0
     8
 * the terms of the GNU General Public License as published by the Free
dvander@0
     9
 * Software Foundation, either version 3 of the License, or (at your option)
dvander@0
    10
 * any later version.
dvander@0
    11
 * 
dvander@0
    12
 * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY
dvander@0
    13
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
dvander@0
    14
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
dvander@0
    15
 *
dvander@0
    16
 * You should have received a copy of the GNU General Public License along with
dvander@0
    17
 * JITCraft. If not, see http://www.gnu.org/licenses/.
dvander@0
    18
 */
dvander@0
    19
#ifndef _include_bytecode_emitter_h_
dvander@0
    20
#define _include_bytecode_emitter_h_
dvander@0
    21
dvander@0
    22
#include <assert.h>
dvander@0
    23
#include <limits.h>
dvander@12
    24
#include <vector>
dvander@0
    25
#include "Opcodes.h"
dvander@14
    26
#include "NameBinding.h"
dvander@33
    27
#include "Handles.h"
dvander@0
    28
dvander@0
    29
namespace jc {
dvander@0
    30
dvander@0
    31
class Type;
dvander@12
    32
class Map;
dvander@0
    33
dvander@33
    34
typedef std::vector<ScopedRoot<Object> *> ObjectList;
dvander@33
    35
dvander@0
    36
class BytecodeEmitter
dvander@0
    37
{
dvander@0
    38
    bytecode *buffer_;
dvander@0
    39
    bytecode *buffer_pos_;
dvander@0
    40
    bytecode *buffer_end_;
dvander@0
    41
    bytecode inline_buffer_[512];
dvander@0
    42
    bool oom_;
dvander@0
    43
    unsigned maxStackDepth_;
dvander@0
    44
    unsigned stackDepth_;
dvander@33
    45
    ObjectList objects_;
dvander@0
    46
dvander@0
    47
  private:
dvander@0
    48
    static const size_t MAX_LENGTH = INT_MAX / 2;
dvander@0
    49
dvander@0
    50
  private:
dvander@0
    51
    bool growBuffer(size_t bytes);
dvander@0
    52
dvander@0
    53
    // Checking the return value is not necessary, since the inline buffer is
dvander@0
    54
    // used as a ballast.
dvander@0
    55
    bool ensure(size_t bytes) {
dvander@0
    56
        if (buffer_pos_ + bytes > buffer_end_)
dvander@0
    57
            return growBuffer(bytes);
dvander@0
    58
        return true;
dvander@0
    59
    }
dvander@0
    60
dvander@0
    61
    void writeUint8(unsigned value) {
dvander@0
    62
        assert(value >= 0 && value <= UCHAR_MAX);
dvander@0
    63
        *buffer_pos_++ = value;
dvander@0
    64
    }
dvander@4
    65
    void writeFloat(float value) {
dvander@4
    66
        *(float *)buffer_pos_ = value;
dvander@4
    67
        buffer_pos_ += sizeof(float);
dvander@4
    68
    }
dvander@0
    69
    void writeInt32(int value) {
dvander@0
    70
        *(int *)buffer_pos_ = value;
dvander@0
    71
        buffer_pos_ += sizeof(int);
dvander@0
    72
    }
dvander@0
    73
    void writeUint32(unsigned value) {
dvander@0
    74
        *(unsigned *)buffer_pos_ = value;
dvander@0
    75
        buffer_pos_ += sizeof(unsigned);
dvander@0
    76
    }
dvander@0
    77
    void writeOp(Opcode op) {
dvander@0
    78
        writeUint8(op);
dvander@0
    79
        stackDepth_ += OpcodeInfo[op].ndefs;
dvander@0
    80
        if (OpcodeInfo[op].nuses != -1)
dvander@0
    81
            stackDepth_ -= OpcodeInfo[op].nuses;
dvander@0
    82
        if (stackDepth_ > maxStackDepth_)
dvander@0
    83
            maxStackDepth_ = stackDepth_;
dvander@0
    84
    }
dvander@21
    85
    void writeObject(Object *obj) {
dvander@21
    86
        writeUint32(objects_.size());
dvander@33
    87
        ScopedRoot<Object> *root = new ScopedRoot<Object>(obj);
dvander@33
    88
        if (!root)
dvander@33
    89
            oom_ = true;
dvander@33
    90
        objects_.push_back(root);
dvander@12
    91
    }
dvander@0
    92
dvander@0
    93
    int position() const {
dvander@0
    94
        assert(buffer_pos_ - buffer_ < INT_MAX / 2);
dvander@0
    95
        return int(buffer_pos_ - buffer_);
dvander@0
    96
    }
dvander@0
    97
dvander@0
    98
  public:
dvander@0
    99
    BytecodeEmitter();
dvander@0
   100
dvander@0
   101
    bytecode *buffer() const {
dvander@0
   102
        return buffer_;
dvander@0
   103
    }
dvander@0
   104
    unsigned length() const {
dvander@0
   105
        return unsigned(buffer_pos_ - buffer_);
dvander@0
   106
    }
dvander@0
   107
    bool oom() const {
dvander@0
   108
        return oom_;
dvander@0
   109
    }
dvander@0
   110
    unsigned stackDepth() const {
dvander@0
   111
        return stackDepth_;
dvander@0
   112
    }
dvander@0
   113
    unsigned maxStackDepth() const {
dvander@0
   114
        return maxStackDepth_;
dvander@0
   115
    }
dvander@33
   116
    const ObjectList *objects() const {
dvander@21
   117
        return &objects_;
dvander@12
   118
    }
dvander@0
   119
dvander@46
   120
    void drop(unsigned amt) {
dvander@46
   121
        assert(amt <= stackDepth_);
dvander@46
   122
        stackDepth_ -= amt;
dvander@46
   123
    }
dvander@46
   124
dvander@0
   125
  public:
dvander@0
   126
    // A label represents a local control-flow target in the bytecode. A label
dvander@0
   127
    // may be in three states:
dvander@0
   128
    //
dvander@0
   129
    // (1) Empty - the label is not used yet.
dvander@0
   130
    // (2) Pending - the label has incoming jumps, but the actual location of
dvander@0
   131
    //               is not yet known. The list of jumps is threaded through
dvander@0
   132
    //               the four bytes reserved for each emitted jump target. In
dvander@0
   133
    //               this case, the offset field is the location of the most
dvander@0
   134
    //               recently emitted incoming jump.
dvander@0
   135
    // (3) Bound   - the label's position has been pinned.
dvander@0
   136
    //
dvander@0
   137
    // A jump can be emitted to a label in any state. Binding a label
dvander@0
   138
    // immediately backpatches all pending incoming jumps. A label must be
dvander@0
   139
    // bound if it is used.
dvander@0
   140
    class Label
dvander@0
   141
    {
dvander@0
   142
        int offset_ : 31;
dvander@0
   143
        bool bound_ : 1;
dvander@0
   144
dvander@0
   145
        void setOffset(int offset) {
dvander@0
   146
            offset_ = offset;
dvander@0
   147
            assert(offset_ == offset);
dvander@0
   148
        }
dvander@0
   149
dvander@0
   150
      public:
dvander@0
   151
        static const int INVALID_OFFSET = -1;
dvander@0
   152
dvander@0
   153
      public:
dvander@0
   154
        Label()
dvander@0
   155
          : offset_(INVALID_OFFSET),
dvander@0
   156
            bound_(false)
dvander@0
   157
        {
dvander@0
   158
        }
dvander@0
   159
        ~Label()
dvander@0
   160
        {
dvander@0
   161
            assert(bound_ || offset_ == INVALID_OFFSET);
dvander@0
   162
        }
dvander@0
   163
        
dvander@0
   164
        bool bound() const {
dvander@0
   165
            return bound_;
dvander@0
   166
        }
dvander@0
   167
        int offset() const {
dvander@0
   168
            return offset_;
dvander@0
   169
        }
dvander@0
   170
        void bind(int offset) {
dvander@0
   171
            assert(!bound_);
dvander@0
   172
            setOffset(offset);
dvander@0
   173
            bound_ = true;
dvander@0
   174
        }
dvander@0
   175
        int link(int offset) {
dvander@0
   176
            assert(!bound_);
dvander@0
   177
            int old = offset_;
dvander@0
   178
            setOffset(offset);
dvander@0
   179
            return old;
dvander@0
   180
        }
dvander@0
   181
    };
dvander@0
   182
dvander@0
   183
  private:
dvander@0
   184
    int patchJumpAt(int at, int to) {
dvander@0
   185
        assert(at >= 0 && at < position());
dvander@0
   186
        int old = *(int *)(buffer_ + at + 1);
dvander@0
   187
        *(int *)(buffer_ + at + 1) = (to - at);
dvander@0
   188
        return old;
dvander@0
   189
    }
dvander@0
   190
dvander@0
   191
    void singleByteOp(Opcode op) {
dvander@0
   192
        assert(OpcodeInfo[op].length == 1);
dvander@0
   193
        ensure(OpcodeInfo[op].length);
dvander@0
   194
        writeOp(op);
dvander@0
   195
    }
dvander@0
   196
dvander@0
   197
  public:
dvander@0
   198
    void int_(int value) {
dvander@0
   199
        ensure(OP_INT);
dvander@0
   200
        writeOp(OP_INT);
dvander@0
   201
        writeInt32(value);
dvander@0
   202
    }
dvander@0
   203
    void return_() {
dvander@0
   204
        ensure(OP_RETURN);
dvander@0
   205
        writeOp(OP_RETURN);
dvander@0
   206
    }
dvander@0
   207
    void getlocal(unsigned local) {
dvander@0
   208
        ensure(OP_GETLOCAL_LENGTH);
dvander@0
   209
        writeOp(OP_GETLOCAL);
dvander@0
   210
        writeUint32(local);
dvander@0
   211
    }
dvander@0
   212
    void setlocal(unsigned local) {
dvander@0
   213
        ensure(OP_SETLOCAL_LENGTH);
dvander@0
   214
        writeOp(OP_SETLOCAL);
dvander@0
   215
        writeUint32(local);
dvander@0
   216
    }
dvander@0
   217
    void getarg(unsigned slot) {
dvander@0
   218
        ensure(OP_GETARG_LENGTH);
dvander@0
   219
        writeOp(OP_GETARG);
dvander@0
   220
        writeUint32(slot);
dvander@0
   221
    }
dvander@0
   222
    void setarg(unsigned slot) {
dvander@0
   223
        ensure(OP_SETARG_LENGTH);
dvander@0
   224
        writeOp(OP_SETARG);
dvander@0
   225
        writeUint32(slot);
dvander@0
   226
    }
dvander@0
   227
dvander@0
   228
    void j(Opcode op, Label *label) {
dvander@0
   229
        // Since jumps can skip around the stream, do not try to do anything
dvander@0
   230
        // in a potentially truncated state.
dvander@0
   231
        if (oom())
dvander@0
   232
            return;
dvander@0
   233
dvander@0
   234
        assert(op >= OP_JL && op <= OP_JUMP);
dvander@0
   235
        ensure(OP_JUMP_LENGTH);
dvander@0
   236
dvander@0
   237
        if (label->bound()) {
dvander@0
   238
            // We can immediately emit this jump.
dvander@0
   239
            int distance = label->offset() - position();
dvander@0
   240
            writeOp(op);
dvander@0
   241
            writeInt32(distance);
dvander@0
   242
        } else {
dvander@0
   243
            // We can't emit this jump yet. Add a placeholder.
dvander@0
   244
            int old = label->link(position());
dvander@0
   245
            writeOp(op);
dvander@0
   246
            writeInt32(old);
dvander@0
   247
        }
dvander@0
   248
    }
dvander@0
   249
    void bind(Label *label) {
dvander@0
   250
        if (oom())
dvander@0
   251
            return;
dvander@0
   252
dvander@0
   253
        assert(!label->bound());
dvander@0
   254
dvander@0
   255
        int offset = label->offset();
dvander@0
   256
        while (offset != Label::INVALID_OFFSET)
dvander@0
   257
            offset = patchJumpAt(offset, position());
dvander@0
   258
dvander@0
   259
        label->bind(position());
dvander@0
   260
    }
dvander@0
   261
    void jump(Label *label) {
dvander@0
   262
        j(OP_JUMP, label);
dvander@0
   263
    }
dvander@0
   264
dvander@0
   265
    void binary(Opcode op) {
dvander@0
   266
        singleByteOp(op);
dvander@0
   267
    }
dvander@0
   268
dvander@0
   269
    void pop() {
dvander@0
   270
        singleByteOp(OP_POP);
dvander@0
   271
    }
dvander@0
   272
    void stop() {
dvander@0
   273
        singleByteOp(OP_STOP);
dvander@0
   274
    }
dvander@46
   275
    void callglobal(unsigned index) {
dvander@0
   276
        ensure(OP_CALLGLOBAL_LENGTH);
dvander@0
   277
        writeOp(OP_CALLGLOBAL);
dvander@0
   278
        writeUint32(index);
dvander@0
   279
    }
dvander@21
   280
    void getarray(PrimitiveType type) {
dvander@21
   281
        switch (type) {
dvander@21
   282
          case PrimitiveType_Float:
dvander@21
   283
            singleByteOp(OP_GETARRAY_F);
dvander@21
   284
            break;
dvander@21
   285
          case PrimitiveType_Int32:
dvander@21
   286
            singleByteOp(OP_GETARRAY_I);
dvander@21
   287
            break;
dvander@21
   288
          default:
dvander@21
   289
            assert(type == PrimitiveType_Char);
dvander@21
   290
            singleByteOp(OP_GETARRAY_C);
dvander@21
   291
            break;
dvander@21
   292
        }
dvander@0
   293
    }
dvander@21
   294
    void setarray(PrimitiveType type) {
dvander@21
   295
        switch (type) {
dvander@21
   296
          case PrimitiveType_Float:
dvander@21
   297
            singleByteOp(OP_SETARRAY_F);
dvander@21
   298
            break;
dvander@21
   299
          case PrimitiveType_Int32:
dvander@21
   300
            singleByteOp(OP_SETARRAY_I);
dvander@21
   301
            break;
dvander@21
   302
          default:
dvander@21
   303
            assert(type == PrimitiveType_Char);
dvander@21
   304
            singleByteOp(OP_SETARRAY_C);
dvander@21
   305
            break;
dvander@21
   306
        }
dvander@7
   307
    }
dvander@19
   308
    void getarray_o() {
dvander@19
   309
        singleByteOp(OP_GETARRAY_O);
dvander@0
   310
    }
dvander@19
   311
    void bounds() {
dvander@19
   312
        singleByteOp(OP_BOUNDS);
dvander@0
   313
    }
dvander@4
   314
    void float_(float value) {
dvander@4
   315
        ensure(OP_FLOAT_LENGTH);
dvander@4
   316
        writeOp(OP_FLOAT);
dvander@4
   317
        writeFloat(value);
dvander@4
   318
    }
dvander@4
   319
    void cvt_i2f() {
dvander@4
   320
        singleByteOp(OP_CVT_I2F);
dvander@4
   321
    }
dvander@33
   322
    void newarray_lifo(Handle<Map> map) {
dvander@19
   323
        ensure(OP_NEWARRAY_LIFO_LENGTH);
dvander@19
   324
        writeOp(OP_NEWARRAY_LIFO);
dvander@21
   325
        writeObject(map);
dvander@12
   326
    }
dvander@12
   327
    void savelifo(unsigned slot) {
dvander@12
   328
        ensure(OP_SAVELIFO_LENGTH);
dvander@12
   329
        writeOp(OP_SAVELIFO);
dvander@12
   330
        writeUint32(slot);
dvander@12
   331
    }
dvander@12
   332
    void unwindlifo(unsigned slot) {
dvander@12
   333
        ensure(OP_UNWINDLIFO_LENGTH);
dvander@12
   334
        writeOp(OP_UNWINDLIFO);
dvander@12
   335
        writeUint32(slot);
dvander@12
   336
    }
dvander@14
   337
    void newref(Variable *var) {
dvander@14
   338
        if (var->location() == Variable::STACK) {
dvander@14
   339
            ensure(OP_VARREF_LIFO_LENGTH);
dvander@14
   340
            writeOp(OP_VARREF_LIFO);
dvander@14
   341
        } else {
dvander@14
   342
            assert(var->location() == Variable::PARAMETER);
dvander@14
   343
            ensure(OP_ARGREF_LIFO_LENGTH);
dvander@14
   344
            writeOp(OP_ARGREF_LIFO);
dvander@14
   345
        }
dvander@14
   346
        writeUint32(var->slot());
dvander@14
   347
    }
dvander@16
   348
    void get(Variable *var) {
dvander@16
   349
        if (var->location() == Variable::STACK) {
dvander@16
   350
            getlocal(var->slot());
dvander@16
   351
        } else {
dvander@16
   352
            assert(var->location() == Variable::PARAMETER);
dvander@16
   353
            getarg(var->slot());
dvander@16
   354
        }
dvander@16
   355
    }
dvander@16
   356
    void set(Variable *var) {
dvander@16
   357
        assert(!var->type()->isReference());
dvander@16
   358
        if (var->location() == Variable::STACK) {
dvander@16
   359
            setlocal(var->slot());
dvander@16
   360
        } else {
dvander@16
   361
            assert(var->location() == Variable::PARAMETER);
dvander@16
   362
            setarg(var->slot());
dvander@16
   363
        }
dvander@16
   364
    }
dvander@14
   365
    void slotref_lifo() {
dvander@14
   366
        singleByteOp(OP_SLOTREF_LIFO);
dvander@14
   367
    }
dvander@17
   368
    void arrayref_lifo() {
dvander@17
   369
        singleByteOp(OP_ARRAYREF_LIFO);
dvander@17
   370
    }
dvander@22
   371
    void getref(PrimitiveType type) {
dvander@22
   372
        if (type == PrimitiveType_Int32) {
dvander@22
   373
            singleByteOp(OP_GETREF_I);
dvander@22
   374
        } else {
dvander@22
   375
            assert(type == PrimitiveType_Float);
dvander@22
   376
            singleByteOp(OP_GETREF_F);
dvander@22
   377
        }
dvander@14
   378
    }
dvander@22
   379
    void setref(PrimitiveType type) {
dvander@22
   380
        if (type == PrimitiveType_Int32) {
dvander@22
   381
            singleByteOp(OP_SETREF_I);
dvander@22
   382
        } else {
dvander@22
   383
            assert(type == PrimitiveType_Float);
dvander@22
   384
            singleByteOp(OP_SETREF_F);
dvander@22
   385
        }
dvander@14
   386
    }
dvander@33
   387
    void object(Handle<Object> obj) {
dvander@21
   388
        ensure(OP_OBJECT_LENGTH);
dvander@21
   389
        writeOp(OP_OBJECT);
dvander@21
   390
        writeObject(obj);
dvander@21
   391
    }
dvander@21
   392
    void arraycopy() {
dvander@21
   393
        singleByteOp(OP_ARRAYCOPY);
dvander@21
   394
    }
dvander@22
   395
    void dup() {
dvander@22
   396
        singleByteOp(OP_DUP);
dvander@22
   397
    }
dvander@22
   398
    void dup2() {
dvander@22
   399
        singleByteOp(OP_DUP2);
dvander@22
   400
    }
dvander@23
   401
    void swap() {
dvander@23
   402
        singleByteOp(OP_SWAP);
dvander@23
   403
    }
dvander@23
   404
    void roll3() {
dvander@23
   405
        singleByteOp(OP_ROLL3);
dvander@23
   406
    }
dvander@23
   407
    void pick2() {
dvander@23
   408
        singleByteOp(OP_PICK2);
dvander@23
   409
    }
dvander@23
   410
    void pick3() {
dvander@23
   411
        singleByteOp(OP_PICK3);
dvander@23
   412
    }
dvander@0
   413
};
dvander@0
   414
dvander@0
   415
}
dvander@0
   416
dvander@0
   417
#endif // _include_bytecode_emitter_h_