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