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