jitcraft/BytecodeEmitter.h
author David Anderson <dvander@alliedmods.net>
Wed Jun 27 22:02:40 2012 -0700 (2012-06-27)
changeset 7 0478c27ba82c
parent 6 172dd7799d0a
child 12 75e385f68b7e
permissions -rw-r--r--
Added support for arrays of Floats.
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
#ifndef _include_bytecode_emitter_h_
dvander@0
    19
#define _include_bytecode_emitter_h_
dvander@0
    20
dvander@0
    21
#include <assert.h>
dvander@0
    22
#include <limits.h>
dvander@0
    23
#include "Opcodes.h"
dvander@0
    24
dvander@0
    25
namespace jc {
dvander@0
    26
dvander@0
    27
class Type;
dvander@0
    28
dvander@0
    29
class BytecodeEmitter
dvander@0
    30
{
dvander@0
    31
    bytecode *buffer_;
dvander@0
    32
    bytecode *buffer_pos_;
dvander@0
    33
    bytecode *buffer_end_;
dvander@0
    34
    bytecode inline_buffer_[512];
dvander@0
    35
    bool oom_;
dvander@0
    36
    unsigned maxStackDepth_;
dvander@0
    37
    unsigned stackDepth_;
dvander@0
    38
dvander@0
    39
  private:
dvander@0
    40
    static const size_t MAX_LENGTH = INT_MAX / 2;
dvander@0
    41
dvander@0
    42
  private:
dvander@0
    43
    bool growBuffer(size_t bytes);
dvander@0
    44
dvander@0
    45
    // Checking the return value is not necessary, since the inline buffer is
dvander@0
    46
    // used as a ballast.
dvander@0
    47
    bool ensure(size_t bytes) {
dvander@0
    48
        if (buffer_pos_ + bytes > buffer_end_)
dvander@0
    49
            return growBuffer(bytes);
dvander@0
    50
        return true;
dvander@0
    51
    }
dvander@0
    52
dvander@0
    53
    void writeUint8(unsigned value) {
dvander@0
    54
        assert(value >= 0 && value <= UCHAR_MAX);
dvander@0
    55
        *buffer_pos_++ = value;
dvander@0
    56
    }
dvander@4
    57
    void writeFloat(float value) {
dvander@4
    58
        *(float *)buffer_pos_ = value;
dvander@4
    59
        buffer_pos_ += sizeof(float);
dvander@4
    60
    }
dvander@0
    61
    void writeInt32(int value) {
dvander@0
    62
        *(int *)buffer_pos_ = value;
dvander@0
    63
        buffer_pos_ += sizeof(int);
dvander@0
    64
    }
dvander@0
    65
    void writeUint32(unsigned value) {
dvander@0
    66
        *(unsigned *)buffer_pos_ = value;
dvander@0
    67
        buffer_pos_ += sizeof(unsigned);
dvander@0
    68
    }
dvander@0
    69
    void writeOp(Opcode op) {
dvander@0
    70
        writeUint8(op);
dvander@0
    71
        stackDepth_ += OpcodeInfo[op].ndefs;
dvander@0
    72
        if (OpcodeInfo[op].nuses != -1)
dvander@0
    73
            stackDepth_ -= OpcodeInfo[op].nuses;
dvander@0
    74
        if (stackDepth_ > maxStackDepth_)
dvander@0
    75
            maxStackDepth_ = stackDepth_;
dvander@0
    76
    }
dvander@0
    77
dvander@0
    78
    int position() const {
dvander@0
    79
        assert(buffer_pos_ - buffer_ < INT_MAX / 2);
dvander@0
    80
        return int(buffer_pos_ - buffer_);
dvander@0
    81
    }
dvander@0
    82
dvander@0
    83
  public:
dvander@0
    84
    BytecodeEmitter();
dvander@0
    85
dvander@0
    86
    bytecode *buffer() const {
dvander@0
    87
        return buffer_;
dvander@0
    88
    }
dvander@0
    89
    unsigned length() const {
dvander@0
    90
        return unsigned(buffer_pos_ - buffer_);
dvander@0
    91
    }
dvander@0
    92
    bool oom() const {
dvander@0
    93
        return oom_;
dvander@0
    94
    }
dvander@0
    95
    unsigned stackDepth() const {
dvander@0
    96
        return stackDepth_;
dvander@0
    97
    }
dvander@0
    98
    unsigned maxStackDepth() const {
dvander@0
    99
        return maxStackDepth_;
dvander@0
   100
    }
dvander@0
   101
dvander@0
   102
  public:
dvander@0
   103
    // A label represents a local control-flow target in the bytecode. A label
dvander@0
   104
    // may be in three states:
dvander@0
   105
    //
dvander@0
   106
    // (1) Empty - the label is not used yet.
dvander@0
   107
    // (2) Pending - the label has incoming jumps, but the actual location of
dvander@0
   108
    //               is not yet known. The list of jumps is threaded through
dvander@0
   109
    //               the four bytes reserved for each emitted jump target. In
dvander@0
   110
    //               this case, the offset field is the location of the most
dvander@0
   111
    //               recently emitted incoming jump.
dvander@0
   112
    // (3) Bound   - the label's position has been pinned.
dvander@0
   113
    //
dvander@0
   114
    // A jump can be emitted to a label in any state. Binding a label
dvander@0
   115
    // immediately backpatches all pending incoming jumps. A label must be
dvander@0
   116
    // bound if it is used.
dvander@0
   117
    class Label
dvander@0
   118
    {
dvander@0
   119
        int offset_ : 31;
dvander@0
   120
        bool bound_ : 1;
dvander@0
   121
dvander@0
   122
        void setOffset(int offset) {
dvander@0
   123
            offset_ = offset;
dvander@0
   124
            assert(offset_ == offset);
dvander@0
   125
        }
dvander@0
   126
dvander@0
   127
      public:
dvander@0
   128
        static const int INVALID_OFFSET = -1;
dvander@0
   129
dvander@0
   130
      public:
dvander@0
   131
        Label()
dvander@0
   132
          : offset_(INVALID_OFFSET),
dvander@0
   133
            bound_(false)
dvander@0
   134
        {
dvander@0
   135
        }
dvander@0
   136
        ~Label()
dvander@0
   137
        {
dvander@0
   138
            assert(bound_ || offset_ == INVALID_OFFSET);
dvander@0
   139
        }
dvander@0
   140
        
dvander@0
   141
        bool bound() const {
dvander@0
   142
            return bound_;
dvander@0
   143
        }
dvander@0
   144
        int offset() const {
dvander@0
   145
            return offset_;
dvander@0
   146
        }
dvander@0
   147
        void bind(int offset) {
dvander@0
   148
            assert(!bound_);
dvander@0
   149
            setOffset(offset);
dvander@0
   150
            bound_ = true;
dvander@0
   151
        }
dvander@0
   152
        int link(int offset) {
dvander@0
   153
            assert(!bound_);
dvander@0
   154
            int old = offset_;
dvander@0
   155
            setOffset(offset);
dvander@0
   156
            return old;
dvander@0
   157
        }
dvander@0
   158
    };
dvander@0
   159
dvander@0
   160
  private:
dvander@0
   161
    int patchJumpAt(int at, int to) {
dvander@0
   162
        assert(at >= 0 && at < position());
dvander@0
   163
        int old = *(int *)(buffer_ + at + 1);
dvander@0
   164
        *(int *)(buffer_ + at + 1) = (to - at);
dvander@0
   165
        return old;
dvander@0
   166
    }
dvander@0
   167
dvander@0
   168
    void singleByteOp(Opcode op) {
dvander@0
   169
        assert(OpcodeInfo[op].length == 1);
dvander@0
   170
        ensure(OpcodeInfo[op].length);
dvander@0
   171
        writeOp(op);
dvander@0
   172
    }
dvander@0
   173
dvander@0
   174
  public:
dvander@0
   175
    void int_(int value) {
dvander@0
   176
        ensure(OP_INT);
dvander@0
   177
        writeOp(OP_INT);
dvander@0
   178
        writeInt32(value);
dvander@0
   179
    }
dvander@0
   180
    void return_() {
dvander@0
   181
        ensure(OP_RETURN);
dvander@0
   182
        writeOp(OP_RETURN);
dvander@0
   183
    }
dvander@0
   184
    void getlocal(unsigned local) {
dvander@0
   185
        ensure(OP_GETLOCAL_LENGTH);
dvander@0
   186
        writeOp(OP_GETLOCAL);
dvander@0
   187
        writeUint32(local);
dvander@0
   188
    }
dvander@0
   189
    void setlocal(unsigned local) {
dvander@0
   190
        ensure(OP_SETLOCAL_LENGTH);
dvander@0
   191
        writeOp(OP_SETLOCAL);
dvander@0
   192
        writeUint32(local);
dvander@0
   193
    }
dvander@0
   194
    void getarg(unsigned slot) {
dvander@0
   195
        ensure(OP_GETARG_LENGTH);
dvander@0
   196
        writeOp(OP_GETARG);
dvander@0
   197
        writeUint32(slot);
dvander@0
   198
    }
dvander@0
   199
    void setarg(unsigned slot) {
dvander@0
   200
        ensure(OP_SETARG_LENGTH);
dvander@0
   201
        writeOp(OP_SETARG);
dvander@0
   202
        writeUint32(slot);
dvander@0
   203
    }
dvander@0
   204
dvander@0
   205
    void j(Opcode op, Label *label) {
dvander@0
   206
        // Since jumps can skip around the stream, do not try to do anything
dvander@0
   207
        // in a potentially truncated state.
dvander@0
   208
        if (oom())
dvander@0
   209
            return;
dvander@0
   210
dvander@0
   211
        assert(op >= OP_JL && op <= OP_JUMP);
dvander@0
   212
        ensure(OP_JUMP_LENGTH);
dvander@0
   213
dvander@0
   214
        if (label->bound()) {
dvander@0
   215
            // We can immediately emit this jump.
dvander@0
   216
            int distance = label->offset() - position();
dvander@0
   217
            writeOp(op);
dvander@0
   218
            writeInt32(distance);
dvander@0
   219
        } else {
dvander@0
   220
            // We can't emit this jump yet. Add a placeholder.
dvander@0
   221
            int old = label->link(position());
dvander@0
   222
            writeOp(op);
dvander@0
   223
            writeInt32(old);
dvander@0
   224
        }
dvander@0
   225
    }
dvander@0
   226
    void bind(Label *label) {
dvander@0
   227
        if (oom())
dvander@0
   228
            return;
dvander@0
   229
dvander@0
   230
        assert(!label->bound());
dvander@0
   231
dvander@0
   232
        int offset = label->offset();
dvander@0
   233
        while (offset != Label::INVALID_OFFSET)
dvander@0
   234
            offset = patchJumpAt(offset, position());
dvander@0
   235
dvander@0
   236
        label->bind(position());
dvander@0
   237
    }
dvander@0
   238
    void jump(Label *label) {
dvander@0
   239
        j(OP_JUMP, label);
dvander@0
   240
    }
dvander@0
   241
dvander@0
   242
    void binary(Opcode op) {
dvander@0
   243
        singleByteOp(op);
dvander@0
   244
    }
dvander@0
   245
dvander@0
   246
    void pop() {
dvander@0
   247
        singleByteOp(OP_POP);
dvander@0
   248
    }
dvander@0
   249
    void stop() {
dvander@0
   250
        singleByteOp(OP_STOP);
dvander@0
   251
    }
dvander@0
   252
    void callglobal(unsigned nargs, unsigned index) {
dvander@0
   253
        ensure(OP_CALLGLOBAL_LENGTH);
dvander@0
   254
        writeOp(OP_CALLGLOBAL);
dvander@0
   255
        writeUint32(nargs);
dvander@0
   256
        writeUint32(index);
dvander@0
   257
dvander@0
   258
        stackDepth_ -= nargs;
dvander@0
   259
    }
dvander@6
   260
    void getarray_i() {
dvander@6
   261
        singleByteOp(OP_GETARRAY_I);
dvander@0
   262
    }
dvander@6
   263
    void setarray_i() {
dvander@6
   264
        singleByteOp(OP_SETARRAY_I);
dvander@0
   265
    }
dvander@7
   266
    void getarray_f() {
dvander@7
   267
        singleByteOp(OP_GETARRAY_F);
dvander@7
   268
    }
dvander@7
   269
    void setarray_f() {
dvander@7
   270
        singleByteOp(OP_SETARRAY_F);
dvander@7
   271
    }
dvander@6
   272
    void getarray_p() {
dvander@6
   273
        singleByteOp(OP_GETARRAY_P);
dvander@0
   274
    }
dvander@0
   275
    void stackarray(Type *type);
dvander@1
   276
    void unwind(unsigned slot) {
dvander@1
   277
        ensure(OP_UNWIND_LENGTH);
dvander@1
   278
        writeOp(OP_UNWIND);
dvander@1
   279
        writeUint32(slot);
dvander@1
   280
    }
dvander@0
   281
    void bounds_sta() {
dvander@0
   282
        singleByteOp(OP_BOUNDS_STA);
dvander@0
   283
    }
dvander@4
   284
    void float_(float value) {
dvander@4
   285
        ensure(OP_FLOAT_LENGTH);
dvander@4
   286
        writeOp(OP_FLOAT);
dvander@4
   287
        writeFloat(value);
dvander@4
   288
    }
dvander@4
   289
    void cvt_i2f() {
dvander@4
   290
        singleByteOp(OP_CVT_I2F);
dvander@4
   291
    }
dvander@0
   292
};
dvander@0
   293
dvander@0
   294
}
dvander@0
   295
dvander@0
   296
#endif // _include_bytecode_emitter_h_