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