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