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