jitcraft/BytecodeEmitter.h
author David Anderson <dvander@alliedmods.net>
Tue Jul 10 02:11:13 2012 -0700 (2012-07-10)
changeset 68 b40b0e18a918
parent 64 11c81eb30246
child 69 dfce0539a77d
permissions -rw-r--r--
Added support for ISourcePawnEngine natives.
     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(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 callnative(unsigned index, unsigned argc) {
   342         recordOops();
   343         ensure(OP_CALLNATIVE_LENGTH);
   344         writeOp(OP_CALLNATIVE);
   345         writeUint32(index);
   346         writeUint32(argc);
   347     }
   348     void getarray(PrimitiveType type) {
   349         switch (type) {
   350           case PrimitiveType_Float:
   351             singleByteOp(OP_GETARRAY_F);
   352             break;
   353           case PrimitiveType_Int32:
   354             singleByteOp(OP_GETARRAY_I);
   355             break;
   356           default:
   357             assert(type == PrimitiveType_Char);
   358             singleByteOp(OP_GETARRAY_C);
   359             break;
   360         }
   361     }
   362     void setarray(PrimitiveType type) {
   363         switch (type) {
   364           case PrimitiveType_Float:
   365             singleByteOp(OP_SETARRAY_F);
   366             break;
   367           case PrimitiveType_Int32:
   368             singleByteOp(OP_SETARRAY_I);
   369             break;
   370           default:
   371             assert(type == PrimitiveType_Char);
   372             singleByteOp(OP_SETARRAY_C);
   373             break;
   374         }
   375     }
   376     void getarray_o() {
   377         singleByteOp(OP_GETARRAY_O);
   378         markOop(1);
   379     }
   380     void bounds() {
   381         singleByteOp(OP_BOUNDS);
   382         markOop(2);
   383     }
   384     void float_(float value) {
   385         ensure(OP_FLOAT_LENGTH);
   386         writeOp(OP_FLOAT);
   387         writeFloat(value);
   388     }
   389     void cvt_i2f() {
   390         singleByteOp(OP_CVT_I2F);
   391     }
   392     void newarray_lifo(Handle<Map> map) {
   393         recordOops();
   394         ensure(OP_NEWARRAY_LIFO_LENGTH);
   395         writeOp(OP_NEWARRAY_LIFO);
   396         writeObject(map);
   397         markOop(1);
   398     }
   399     void savelifo(unsigned slot) {
   400         ensure(OP_SAVELIFO_LENGTH);
   401         writeOp(OP_SAVELIFO);
   402         writeUint32(slot);
   403     }
   404     void unwindlifo(unsigned slot) {
   405         ensure(OP_UNWINDLIFO_LENGTH);
   406         writeOp(OP_UNWINDLIFO);
   407         writeUint32(slot);
   408     }
   409     void newref(Variable *var) {
   410         recordOops();
   411         if (var->location() == Variable::STACK) {
   412             ensure(OP_VARREF_LIFO_LENGTH);
   413             writeOp(OP_VARREF_LIFO);
   414         } else {
   415             assert(var->location() == Variable::PARAMETER);
   416             ensure(OP_ARGREF_LIFO_LENGTH);
   417             writeOp(OP_ARGREF_LIFO);
   418         }
   419         writeUint32(var->slot());
   420         markOop(1);
   421     }
   422 
   423     void slotref_lifo() {
   424         recordOops();
   425         singleByteOp(OP_SLOTREF_LIFO);
   426         markOop(1);
   427     }
   428     void arrayref_lifo() {
   429         recordOops();
   430         singleByteOp(OP_ARRAYREF_LIFO);
   431         markOop(1);
   432     }
   433     void getref(PrimitiveType type) {
   434         if (type == PrimitiveType_Int32) {
   435             singleByteOp(OP_GETREF_I);
   436         } else {
   437             assert(type == PrimitiveType_Float);
   438             singleByteOp(OP_GETREF_F);
   439         }
   440     }
   441     void setref(PrimitiveType type) {
   442         if (type == PrimitiveType_Int32) {
   443             singleByteOp(OP_SETREF_I);
   444         } else {
   445             assert(type == PrimitiveType_Float);
   446             singleByteOp(OP_SETREF_F);
   447         }
   448     }
   449     void object(Handle<Object> obj) {
   450         ensure(OP_OBJECT_LENGTH);
   451         writeOp(OP_OBJECT);
   452         writeObject(obj);
   453         markOop(1);
   454     }
   455     void arraycopy() {
   456         singleByteOp(OP_ARRAYCOPY);
   457         markOop(1);
   458     }
   459 
   460     void dup() {
   461         bool oop = getOop(1);
   462         singleByteOp(OP_DUP);
   463         markOop(1, oop);
   464     }
   465     void dup2() {
   466         bool oop2 = getOop(2);
   467         bool oop1 = getOop(1);
   468 
   469         singleByteOp(OP_DUP2);
   470 
   471         markOop(2, oop2);
   472         markOop(1, oop1);
   473     }
   474     void swap() {
   475         bool oop2 = getOop(2);
   476         bool oop1 = getOop(1);
   477 
   478         singleByteOp(OP_SWAP);
   479 
   480         markOop(2, oop1);
   481         markOop(1, oop2);
   482     }
   483     void roll3() {
   484         bool oop3 = getOop(3);
   485         bool oop2 = getOop(2);
   486         bool oop1 = getOop(1);
   487 
   488         singleByteOp(OP_ROLL3);
   489 
   490         markOop(3, oop2);
   491         markOop(2, oop1);
   492         markOop(1, oop3);
   493     }
   494     void pick2() {
   495         bool oop2 = getOop(2);
   496         bool oop1 = getOop(1);
   497 
   498         singleByteOp(OP_PICK2);
   499 
   500         markOop(3, oop2);
   501         markOop(2, oop1);
   502         markOop(1, oop2);
   503     }
   504     void pick3() {
   505         bool oop3 = getOop(3);
   506         bool oop2 = getOop(2);
   507         bool oop1 = getOop(1);
   508 
   509         singleByteOp(OP_PICK3);
   510 
   511         markOop(4, oop3);
   512         markOop(3, oop2);
   513         markOop(2, oop1);
   514         markOop(1, oop3);
   515     }
   516 
   517     // Helpers.
   518     void get(Variable *var) {
   519         if (var->location() == Variable::STACK) {
   520             getlocal(var);
   521         } else {
   522             assert(var->location() == Variable::PARAMETER);
   523             getarg(var);
   524         }
   525     }
   526     void set(Variable *var) {
   527         assert(!var->type()->isReference());
   528         if (var->location() == Variable::STACK) {
   529             setlocal(var);
   530         } else {
   531             assert(var->location() == Variable::PARAMETER);
   532             setarg(var);
   533         }
   534     }
   535 };
   536 
   537 }
   538 
   539 #endif // _include_bytecode_emitter_h_