jitcraft/BytecodeEmitter.h
author David Anderson <dvander@alliedmods.net>
Sun Jul 08 01:54:08 2012 -0700 (2012-07-08)
changeset 60 581a9c793062
parent 59 b7d61ddb3c8d
child 64 11c81eb30246
permissions -rw-r--r--
Initial import of some kind of API.
     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 "Opcodes.h"
    25 #include "NameBinding.h"
    26 #include "Handles.h"
    27 
    28 namespace ke {
    29 
    30 class Type;
    31 class Map;
    32 
    33 typedef PoolList<ScopedRoot<Object> *> ObjectList;
    34 
    35 struct OopRecord : public PoolObject
    36 {
    37     unsigned depth;
    38     unsigned offset;
    39     bool *slots;
    40 
    41     OopRecord(unsigned length, unsigned offset)
    42       : depth(length),
    43         offset(offset),
    44         slots(NULL)
    45     {
    46     }
    47 };
    48 
    49 class BytecodeEmitter
    50 {
    51   private:
    52     PoolAllocator &pool_;
    53 
    54     bytecode *buffer_;
    55     bytecode *buffer_pos_;
    56     bytecode *buffer_end_;
    57     bytecode inline_buffer_[512];
    58     bool oom_;
    59     unsigned maxStackDepth_;
    60     ObjectList objects_;
    61     PoolList<bool> oopMap_;
    62     PoolList<OopRecord *> oopRecords_;
    63 
    64   private:
    65     // The maximum size of a code object must be such that the distance between
    66     // two instructions fits into a signed, 31-bit integer. This ensures that
    67     // all code-bounded buffers (like the number of object literals or
    68     // safepoints) does not need to be bounds checked. It also means we can add
    69     // any two offsets and be guaranteed that they don't overflow.
    70     static const size_t MAX_LENGTH = INT_MAX / 2;
    71 
    72   private:
    73     bool growBuffer(size_t bytes);
    74 
    75     // Checking the return value is not necessary, since the inline buffer is
    76     // used as a ballast.
    77     bool ensure(size_t bytes) {
    78         if (buffer_pos_ + bytes > buffer_end_)
    79             return growBuffer(bytes);
    80         return true;
    81     }
    82 
    83     void writeUint8(unsigned value) {
    84         assert(value <= UCHAR_MAX);
    85         *buffer_pos_++ = value;
    86     }
    87     void writeFloat(float value) {
    88         *(float *)buffer_pos_ = value;
    89         buffer_pos_ += sizeof(float);
    90     }
    91     void writeInt32(int value) {
    92         *(int *)buffer_pos_ = value;
    93         buffer_pos_ += sizeof(int);
    94     }
    95     void writeUint32(unsigned value) {
    96         *(unsigned *)buffer_pos_ = value;
    97         buffer_pos_ += sizeof(unsigned);
    98     }
    99     void writeOp(Opcode op) {
   100         if (oom_)
   101             return;
   102 
   103         writeUint8(op);
   104         for (int i = 0; i < OpcodeInfo[op].nuses; i++)
   105             oopMap_.pop();
   106         for (int i = 0; i < OpcodeInfo[op].ndefs; i++)
   107             oopMap_.append(false);
   108         if (oopMap_.length() > maxStackDepth_)
   109             maxStackDepth_ = oopMap_.length();
   110     }
   111     void writeObject(Object *obj) {
   112         writeUint32(objects_.length());
   113         ScopedRoot<Object> *root = new ScopedRoot<Object>(obj);
   114         if (!root)
   115             oom_ = true;
   116         oom_ &= objects_.append(root);
   117     }
   118 
   119     int position() const {
   120         assert(buffer_pos_ - buffer_ < INT_MAX / 2);
   121         return int(buffer_pos_ - buffer_);
   122     }
   123 
   124   public:
   125     BytecodeEmitter(PoolAllocator &pool);
   126 
   127     bytecode *buffer() const {
   128         return buffer_;
   129     }
   130     unsigned length() const {
   131         return unsigned(buffer_pos_ - buffer_);
   132     }
   133     bool oom() const {
   134         return oom_;
   135     }
   136     unsigned stackDepth() const {
   137         return oopMap_.length();
   138     }
   139     unsigned maxStackDepth() const {
   140         return maxStackDepth_;
   141     }
   142     const ObjectList *objects() const {
   143         return &objects_;
   144     }
   145     PoolList<OopRecord *> *oopRecords() {
   146         return &oopRecords_;
   147     }
   148 
   149     void drop(unsigned amt) {
   150         assert(amt <= stackDepth());
   151         for (unsigned i = 0; i < amt; i++)
   152             oopMap_.pop();
   153     }
   154 
   155   public:
   156     // A label represents a local control-flow target in the bytecode. A label
   157     // may be in three states:
   158     //
   159     // (1) Empty - the label is not used yet.
   160     // (2) Pending - the label has incoming jumps, but the actual location of
   161     //               is not yet known. The list of jumps is threaded through
   162     //               the four bytes reserved for each emitted jump target. In
   163     //               this case, the offset field is the location of the most
   164     //               recently emitted incoming jump.
   165     // (3) Bound   - the label's position has been pinned.
   166     //
   167     // A jump can be emitted to a label in any state. Binding a label
   168     // immediately backpatches all pending incoming jumps. A label must be
   169     // bound if it is used.
   170     class Label
   171     {
   172         int offset_ : 31;
   173         bool bound_ : 1;
   174 
   175         void setOffset(int offset) {
   176             offset_ = offset;
   177             assert(offset_ == offset);
   178         }
   179 
   180       public:
   181         static const int INVALID_OFFSET = -1;
   182 
   183       public:
   184         Label()
   185           : offset_(INVALID_OFFSET),
   186             bound_(false)
   187         {
   188         }
   189         ~Label()
   190         {
   191             assert(bound_ || offset_ == INVALID_OFFSET);
   192         }
   193         
   194         bool bound() const {
   195             return bound_;
   196         }
   197         int offset() const {
   198             return offset_;
   199         }
   200         void bind(int offset) {
   201             assert(!bound_);
   202             setOffset(offset);
   203             bound_ = true;
   204         }
   205         int link(int offset) {
   206             assert(!bound_);
   207             int old = offset_;
   208             setOffset(offset);
   209             return old;
   210         }
   211     };
   212 
   213   private:
   214     int patchJumpAt(int at, int to) {
   215         assert(at >= 0 && at < position());
   216         int old = *(int *)(buffer_ + at + 1);
   217         *(int *)(buffer_ + at + 1) = (to - at);
   218         return old;
   219     }
   220 
   221     void singleByteOp(Opcode op) {
   222         assert(OpcodeInfo[op].length == 1);
   223         ensure(OpcodeInfo[op].length);
   224         writeOp(op);
   225     }
   226 
   227     // Mark a stack slot as having an object pointer.
   228     void markOop(unsigned depth, bool oop = true) {
   229         if (oom_)
   230             return;
   231         oopMap_[oopMap_.length() - depth] = oop;
   232     }
   233     bool getOop(unsigned depth) {
   234         if (oom_)
   235             return false;
   236         return oopMap_[oopMap_.length() - depth];
   237     }
   238 
   239     // Mark the current bytecode position as either re-entrant (meaning, it
   240     // can leave the interpreter or call to another function), or causing GC.
   241     // This will record which stack slots have objects and associate that
   242     // information with the current pc.
   243     void recordOops();
   244 
   245   public:
   246     void int_(int value) {
   247         ensure(OP_INT);
   248         writeOp(OP_INT);
   249         writeInt32(value);
   250     }
   251     void return_() {
   252         ensure(OP_RETURN);
   253         writeOp(OP_RETURN);
   254     }
   255     void getlocal(Variable *var) {
   256         ensure(OP_GETLOCAL_LENGTH);
   257         writeOp(OP_GETLOCAL);
   258         writeUint32(var->slot());
   259 
   260         if (var->type()->isTraceable())
   261             markOop(1);
   262     }
   263     void setlocal(Variable *var) {
   264         ensure(OP_SETLOCAL_LENGTH);
   265         writeOp(OP_SETLOCAL);
   266         writeUint32(var->slot());
   267 
   268         if (var->type()->isTraceable())
   269             markOop(1);
   270     }
   271     void getarg(Variable *var) {
   272         ensure(OP_GETARG_LENGTH);
   273         writeOp(OP_GETARG);
   274         writeUint32(var->slot());
   275 
   276         if (var->type()->isTraceable())
   277             markOop(1);
   278     }
   279     void setarg(Variable *var) {
   280         ensure(OP_SETARG_LENGTH);
   281         writeOp(OP_SETARG);
   282         writeUint32(var->slot());
   283 
   284         if (var->type()->isTraceable())
   285             markOop(1);
   286     }
   287 
   288     void j(Opcode op, Label *label) {
   289         // Since jumps can skip around the stream, do not try to do anything
   290         // in a potentially truncated state.
   291         if (oom())
   292             return;
   293 
   294         assert(op >= OP_JL && op <= OP_JUMP);
   295         ensure(OP_JUMP_LENGTH);
   296 
   297         if (label->bound()) {
   298             // We can immediately emit this jump.
   299             int distance = label->offset() - position();
   300             writeOp(op);
   301             writeInt32(distance);
   302         } else {
   303             // We can't emit this jump yet. Add a placeholder.
   304             int old = label->link(position());
   305             writeOp(op);
   306             writeInt32(old);
   307         }
   308     }
   309     void bind(Label *label) {
   310         if (oom())
   311             return;
   312 
   313         assert(!label->bound());
   314 
   315         int offset = label->offset();
   316         while (offset != Label::INVALID_OFFSET)
   317             offset = patchJumpAt(offset, position());
   318 
   319         label->bind(position());
   320     }
   321     void jump(Label *label) {
   322         j(OP_JUMP, label);
   323     }
   324 
   325     void binary(Opcode op) {
   326         singleByteOp(op);
   327     }
   328 
   329     void pop() {
   330         singleByteOp(OP_POP);
   331     }
   332     void stop() {
   333         singleByteOp(OP_STOP);
   334     }
   335     void callglobal(unsigned index) {
   336         recordOops();
   337         ensure(OP_CALLGLOBAL_LENGTH);
   338         writeOp(OP_CALLGLOBAL);
   339         writeUint32(index);
   340     }
   341     void getarray(PrimitiveType type) {
   342         switch (type) {
   343           case PrimitiveType_Float:
   344             singleByteOp(OP_GETARRAY_F);
   345             break;
   346           case PrimitiveType_Int32:
   347             singleByteOp(OP_GETARRAY_I);
   348             break;
   349           default:
   350             assert(type == PrimitiveType_Char);
   351             singleByteOp(OP_GETARRAY_C);
   352             break;
   353         }
   354     }
   355     void setarray(PrimitiveType type) {
   356         switch (type) {
   357           case PrimitiveType_Float:
   358             singleByteOp(OP_SETARRAY_F);
   359             break;
   360           case PrimitiveType_Int32:
   361             singleByteOp(OP_SETARRAY_I);
   362             break;
   363           default:
   364             assert(type == PrimitiveType_Char);
   365             singleByteOp(OP_SETARRAY_C);
   366             break;
   367         }
   368     }
   369     void getarray_o() {
   370         singleByteOp(OP_GETARRAY_O);
   371         markOop(1);
   372     }
   373     void bounds() {
   374         singleByteOp(OP_BOUNDS);
   375         markOop(2);
   376     }
   377     void float_(float value) {
   378         ensure(OP_FLOAT_LENGTH);
   379         writeOp(OP_FLOAT);
   380         writeFloat(value);
   381     }
   382     void cvt_i2f() {
   383         singleByteOp(OP_CVT_I2F);
   384     }
   385     void newarray_lifo(Handle<Map> map) {
   386         recordOops();
   387         ensure(OP_NEWARRAY_LIFO_LENGTH);
   388         writeOp(OP_NEWARRAY_LIFO);
   389         writeObject(map);
   390         markOop(1);
   391     }
   392     void savelifo(unsigned slot) {
   393         ensure(OP_SAVELIFO_LENGTH);
   394         writeOp(OP_SAVELIFO);
   395         writeUint32(slot);
   396     }
   397     void unwindlifo(unsigned slot) {
   398         ensure(OP_UNWINDLIFO_LENGTH);
   399         writeOp(OP_UNWINDLIFO);
   400         writeUint32(slot);
   401     }
   402     void newref(Variable *var) {
   403         recordOops();
   404         if (var->location() == Variable::STACK) {
   405             ensure(OP_VARREF_LIFO_LENGTH);
   406             writeOp(OP_VARREF_LIFO);
   407         } else {
   408             assert(var->location() == Variable::PARAMETER);
   409             ensure(OP_ARGREF_LIFO_LENGTH);
   410             writeOp(OP_ARGREF_LIFO);
   411         }
   412         writeUint32(var->slot());
   413         markOop(1);
   414     }
   415 
   416     void slotref_lifo() {
   417         recordOops();
   418         singleByteOp(OP_SLOTREF_LIFO);
   419         markOop(1);
   420     }
   421     void arrayref_lifo() {
   422         recordOops();
   423         singleByteOp(OP_ARRAYREF_LIFO);
   424         markOop(1);
   425     }
   426     void getref(PrimitiveType type) {
   427         if (type == PrimitiveType_Int32) {
   428             singleByteOp(OP_GETREF_I);
   429         } else {
   430             assert(type == PrimitiveType_Float);
   431             singleByteOp(OP_GETREF_F);
   432         }
   433     }
   434     void setref(PrimitiveType type) {
   435         if (type == PrimitiveType_Int32) {
   436             singleByteOp(OP_SETREF_I);
   437         } else {
   438             assert(type == PrimitiveType_Float);
   439             singleByteOp(OP_SETREF_F);
   440         }
   441     }
   442     void object(Handle<Object> obj) {
   443         ensure(OP_OBJECT_LENGTH);
   444         writeOp(OP_OBJECT);
   445         writeObject(obj);
   446         markOop(1);
   447     }
   448     void arraycopy() {
   449         singleByteOp(OP_ARRAYCOPY);
   450         markOop(1);
   451     }
   452 
   453     void dup() {
   454         bool oop = getOop(1);
   455         singleByteOp(OP_DUP);
   456         markOop(1, oop);
   457     }
   458     void dup2() {
   459         bool oop2 = getOop(2);
   460         bool oop1 = getOop(1);
   461 
   462         singleByteOp(OP_DUP2);
   463 
   464         markOop(2, oop2);
   465         markOop(1, oop1);
   466     }
   467     void swap() {
   468         bool oop2 = getOop(2);
   469         bool oop1 = getOop(1);
   470 
   471         singleByteOp(OP_SWAP);
   472 
   473         markOop(2, oop1);
   474         markOop(1, oop2);
   475     }
   476     void roll3() {
   477         bool oop3 = getOop(3);
   478         bool oop2 = getOop(2);
   479         bool oop1 = getOop(1);
   480 
   481         singleByteOp(OP_ROLL3);
   482 
   483         markOop(3, oop2);
   484         markOop(2, oop1);
   485         markOop(1, oop3);
   486     }
   487     void pick2() {
   488         bool oop2 = getOop(2);
   489         bool oop1 = getOop(1);
   490 
   491         singleByteOp(OP_PICK2);
   492 
   493         markOop(3, oop2);
   494         markOop(2, oop1);
   495         markOop(1, oop2);
   496     }
   497     void pick3() {
   498         bool oop3 = getOop(3);
   499         bool oop2 = getOop(2);
   500         bool oop1 = getOop(1);
   501 
   502         singleByteOp(OP_PICK3);
   503 
   504         markOop(4, oop3);
   505         markOop(3, oop2);
   506         markOop(2, oop1);
   507         markOop(1, oop3);
   508     }
   509 
   510     // Helpers.
   511     void get(Variable *var) {
   512         if (var->location() == Variable::STACK) {
   513             getlocal(var);
   514         } else {
   515             assert(var->location() == Variable::PARAMETER);
   516             getarg(var);
   517         }
   518     }
   519     void set(Variable *var) {
   520         assert(!var->type()->isReference());
   521         if (var->location() == Variable::STACK) {
   522             setlocal(var);
   523         } else {
   524             assert(var->location() == Variable::PARAMETER);
   525             setarg(var);
   526         }
   527     }
   528 };
   529 
   530 }
   531 
   532 #endif // _include_bytecode_emitter_h_