src/BytecodeEmitter.h
author David Anderson <dvander@alliedmods.net>
Sun Sep 16 21:59:26 2012 -0700 (2012-09-16)
changeset 190 dd04fce774cd
parent 188 bab29bcec7fe
child 194 b029d63565a2
permissions -rw-r--r--
Enable dynamic array declarations.
     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 #include "CompactBuffer.h"
    28 #include "Array.h"
    29 
    30 namespace ke {
    31 
    32 class Type;
    33 class Map;
    34 class ArrayMap;
    35 
    36 typedef PoolList<ScopedRoot<Object> > ObjectList;
    37 
    38 struct OopRecord : public PoolObject
    39 {
    40     unsigned depth;
    41     unsigned offset;
    42     bool *slots;
    43 
    44     OopRecord(unsigned length, unsigned offset)
    45       : depth(length),
    46         offset(offset),
    47         slots(NULL)
    48     {
    49     }
    50 };
    51 
    52 class BytecodeEmitter
    53 {
    54   private:
    55     PoolAllocator &pool_;
    56 
    57     bytecode *buffer_;
    58     bytecode *buffer_pos_;
    59     bytecode *buffer_end_;
    60     bytecode inline_buffer_[512];
    61     bool oom_;
    62     unsigned maxStackDepth_;
    63     ObjectList objects_;
    64     PoolList<bool> oopMap_;
    65     PoolList<OopRecord *> oopRecords_;
    66     CompactBufferWriter sourcemap_;
    67     unsigned lastCodePosition_;
    68     unsigned firstLine_;
    69 
    70   private:
    71     // The maximum size of a code object must be such that the distance between
    72     // two instructions fits into a signed, 31-bit integer. This ensures that
    73     // all code-bounded buffers (like the number of object literals or
    74     // safepoints) does not need to be bounds checked. It also means we can add
    75     // any two offsets and be guaranteed that they don't overflow.
    76     static const size_t MAX_LENGTH = INT_MAX / 2;
    77 
    78   private:
    79     bool growBuffer(size_t bytes);
    80 
    81     // Checking the return value is not necessary, since the inline buffer is
    82     // used as a ballast.
    83     bool ensure(size_t bytes) {
    84         if (buffer_pos_ + bytes > buffer_end_)
    85             return growBuffer(bytes);
    86         return true;
    87     }
    88 
    89     void writeUint8(unsigned value) {
    90         assert(value <= UCHAR_MAX);
    91         *buffer_pos_++ = value;
    92     }
    93     void writeFloat(float value) {
    94         *(float *)buffer_pos_ = value;
    95         buffer_pos_ += sizeof(float);
    96     }
    97     void writeInt32(int value) {
    98         *(int *)buffer_pos_ = value;
    99         buffer_pos_ += sizeof(int);
   100     }
   101     void writeUint32(unsigned value) {
   102         *(unsigned *)buffer_pos_ = value;
   103         buffer_pos_ += sizeof(unsigned);
   104     }
   105     void writeOp(Opcode op) {
   106         if (oom_)
   107             return;
   108 
   109         writeUint8(op);
   110         for (int i = 0; i < OpcodeInfo[op].nuses; i++)
   111             oopMap_.pop();
   112         for (int i = 0; i < OpcodeInfo[op].ndefs; i++)
   113             oopMap_.append(false);
   114         if (oopMap_.length() > maxStackDepth_)
   115             maxStackDepth_ = oopMap_.length();
   116     }
   117     void writeObject(Object *obj) {
   118         writeUint32(objects_.length());
   119         ScopedRoot<Object> root(obj);
   120         if (!objects_.append(root))
   121             oom_ = true;
   122     }
   123 
   124     int position() const {
   125         assert(buffer_pos_ - buffer_ < INT_MAX / 2);
   126         return int(buffer_pos_ - buffer_);
   127     }
   128 
   129   public:
   130     BytecodeEmitter(PoolAllocator &pool);
   131 
   132     bytecode *buffer() const {
   133         return buffer_;
   134     }
   135     unsigned length() const {
   136         return unsigned(buffer_pos_ - buffer_);
   137     }
   138     bool oom() const {
   139         return oom_ || sourcemap_.oom();
   140     }
   141     unsigned stackDepth() const {
   142         return oopMap_.length();
   143     }
   144     unsigned maxStackDepth() const {
   145         return maxStackDepth_;
   146     }
   147     const ObjectList *objects() const {
   148         return &objects_;
   149     }
   150     PoolList<OopRecord *> *oopRecords() {
   151         return &oopRecords_;
   152     }
   153 
   154     void drop(unsigned amt) {
   155         assert(amt <= stackDepth());
   156         for (unsigned i = 0; i < amt; i++)
   157             oopMap_.pop();
   158     }
   159 
   160     void note_position(const SourcePosition &pos);
   161     uint8 *sourcemap() const {
   162         return sourcemap_.buffer();
   163     }
   164     size_t sourcemapLength() const {
   165         return sourcemap_.length();
   166     }
   167     void setFirstLine(unsigned line) {
   168         assert(!sourcemap_.length());
   169         firstLine_ = line;
   170     }
   171 
   172   public:
   173     // A label represents a local control-flow target in the bytecode. A label
   174     // may be in three states:
   175     //
   176     // (1) Empty - the label is not used yet.
   177     // (2) Pending - the label has incoming jumps, but the actual location of
   178     //               is not yet known. The list of jumps is threaded through
   179     //               the four bytes reserved for each emitted jump target. In
   180     //               this case, the offset field is the location of the most
   181     //               recently emitted incoming jump.
   182     // (3) Bound   - the label's position has been pinned.
   183     //
   184     // A jump can be emitted to a label in any state. Binding a label
   185     // immediately backpatches all pending incoming jumps. A label must be
   186     // bound if it is used.
   187     class Label
   188     {
   189         int offset_ : 31;
   190         bool bound_ : 1;
   191 
   192         void setOffset(int offset) {
   193             offset_ = offset;
   194             assert(offset_ == offset);
   195         }
   196 
   197       public:
   198         static const int INVALID_OFFSET = -1;
   199 
   200       public:
   201         Label()
   202           : offset_(INVALID_OFFSET),
   203             bound_(false)
   204         {
   205         }
   206         ~Label()
   207         {
   208             assert(bound_ || offset_ == INVALID_OFFSET);
   209         }
   210         
   211         bool bound() const {
   212             return bound_;
   213         }
   214         int offset() const {
   215             return offset_;
   216         }
   217         void bind(int offset) {
   218             assert(!bound_);
   219             setOffset(offset);
   220             bound_ = true;
   221         }
   222         int link(int offset) {
   223             assert(!bound_);
   224             int old = offset_;
   225             setOffset(offset);
   226             return old;
   227         }
   228     };
   229 
   230   private:
   231     int patchJumpAt(int at, int to) {
   232         assert(at >= 0 && at < position());
   233         int old = *(int *)(buffer_ + at + 1);
   234         *(int *)(buffer_ + at + 1) = (to - at);
   235         return old;
   236     }
   237 
   238     void singleByteOp(Opcode op) {
   239         assert(OpcodeInfo[op].length == 1);
   240         ensure(OpcodeInfo[op].length);
   241         writeOp(op);
   242     }
   243 
   244     // Mark a stack slot as having an object pointer.
   245     void markOop(unsigned depth, bool oop = true) {
   246         if (oom_)
   247             return;
   248         oopMap_[oopMap_.length() - depth] = oop;
   249     }
   250     bool getOop(unsigned depth) {
   251         if (oom_)
   252             return false;
   253         return oopMap_[oopMap_.length() - depth];
   254     }
   255 
   256     // Mark the current bytecode position as either re-entrant (meaning, it
   257     // can leave the interpreter or call to another function), or causing GC.
   258     // This will record which stack slots have objects and associate that
   259     // information with the current pc.
   260     void recordOops();
   261 
   262   public:
   263     void int_(int value) {
   264         ensure(OP_INT);
   265         writeOp(OP_INT);
   266         writeInt32(value);
   267     }
   268     void return_() {
   269         ensure(OP_RETURN);
   270         writeOp(OP_RETURN);
   271     }
   272     void getlocal(Variable *var) {
   273         ensure(OP_GETLOCAL_LENGTH);
   274         writeOp(OP_GETLOCAL);
   275         writeUint32(var->slot());
   276 
   277         if (var->type()->isTraceable())
   278             markOop(1);
   279     }
   280     void setlocal(Variable *var) {
   281         ensure(OP_SETLOCAL_LENGTH);
   282         writeOp(OP_SETLOCAL);
   283         writeUint32(var->slot());
   284 
   285         if (var->type()->isTraceable())
   286             markOop(1);
   287     }
   288     void getarg(Variable *var) {
   289         ensure(OP_GETARG_LENGTH);
   290         writeOp(OP_GETARG);
   291         writeUint32(var->slot());
   292 
   293         if (var->type()->isTraceable())
   294             markOop(1);
   295     }
   296     void setarg(Variable *var) {
   297         ensure(OP_SETARG_LENGTH);
   298         writeOp(OP_SETARG);
   299         writeUint32(var->slot());
   300 
   301         if (var->type()->isTraceable())
   302             markOop(1);
   303     }
   304 
   305     void j(Opcode op, Label *label) {
   306         // Since jumps can skip around the stream, do not try to do anything
   307         // in a potentially truncated state.
   308         if (oom())
   309             return;
   310 
   311         assert(op >= OP_JL && op <= OP_JUMP);
   312         ensure(OP_JUMP_LENGTH);
   313 
   314         if (label->bound()) {
   315             // We can immediately emit this jump.
   316             int distance = label->offset() - position();
   317             writeOp(op);
   318             writeInt32(distance);
   319         } else {
   320             // We can't emit this jump yet. Add a placeholder.
   321             int old = label->link(position());
   322             writeOp(op);
   323             writeInt32(old);
   324         }
   325     }
   326     void bind(Label *label) {
   327         if (oom())
   328             return;
   329 
   330         assert(!label->bound());
   331 
   332         int offset = label->offset();
   333         while (offset != Label::INVALID_OFFSET)
   334             offset = patchJumpAt(offset, position());
   335 
   336         label->bind(position());
   337     }
   338     void jump(Label *label) {
   339         j(OP_JUMP, label);
   340     }
   341 
   342     void binary(Opcode op) {
   343         singleByteOp(op);
   344     }
   345 
   346     void pop() {
   347         singleByteOp(OP_POP);
   348     }
   349     void stop() {
   350         singleByteOp(OP_STOP);
   351     }
   352     void call(Handle<Type> fun) {
   353         recordOops();
   354         ensure(OP_CALL_LENGTH);
   355         writeOp(OP_CALL);
   356 
   357         if (fun->returnType()->isVoid())
   358             drop(1);
   359         else if (fun->returnType()->isTraceable())
   360             markOop(1);
   361     }
   362     void callnative(unsigned index, unsigned argc) {
   363         recordOops();
   364         ensure(OP_CALLNATIVE_LENGTH);
   365         writeOp(OP_CALLNATIVE);
   366         writeUint32(index);
   367         writeUint32(argc);
   368     }
   369 
   370     void getelem(Type *type) {
   371         singleByteOp(OP_GETELEM);
   372 
   373         if (type->isTraceable())
   374             markOop(1);
   375     }
   376     void setelem(Type *type) {
   377         singleByteOp(OP_SETELEM);
   378     }
   379 
   380     void getfield(unsigned index, Type *type) {
   381         ensure(OP_GETFIELD_LENGTH);
   382         writeOp(OP_GETFIELD);
   383         writeUint32(index);
   384 
   385         if (type->isTraceable())
   386             markOop(1);
   387     }
   388     void setfield(unsigned index, Type *type) {
   389         ensure(OP_SETFIELD_LENGTH);
   390         writeOp(OP_SETFIELD);
   391         writeUint32(index);
   392 
   393         if (type->isTraceable())
   394             markOop(1);
   395     }
   396 
   397     void getglobal(Variable *var) {
   398         ensure(OP_GETGLOBAL_F_LENGTH);
   399         if (var->type()->isPrimitive()) {
   400             switch (var->type()->primitive()) {
   401               case PrimitiveType_Float:
   402                 writeOp(OP_GETGLOBAL_F);
   403                 break;
   404               default:
   405                 assert(var->type()->primitive() == PrimitiveType_Int32);
   406                 writeOp(OP_GETGLOBAL_I);
   407                 break;
   408             }
   409         } else {
   410             writeOp(OP_GETGLOBAL_O);
   411             markOop(1);
   412         }
   413         writeUint32(var->slot());
   414     }
   415     void setglobal(Variable *var) {
   416         ensure(OP_SETGLOBAL_F_LENGTH);
   417         if (var->type()->isPrimitive()) {
   418             switch (var->type()->primitive()) {
   419               case PrimitiveType_Float:
   420                 writeOp(OP_SETGLOBAL_F);
   421                 break;
   422               default:
   423                 assert(var->type()->primitive() == PrimitiveType_Int32);
   424                 writeOp(OP_SETGLOBAL_I);
   425                 break;
   426             }
   427         } else {
   428             writeOp(OP_SETGLOBAL_O);
   429         }
   430         writeUint32(var->slot());
   431     }
   432 
   433     void float_(float value) {
   434         ensure(OP_FLOAT_LENGTH);
   435         writeOp(OP_FLOAT);
   436         writeFloat(value);
   437     }
   438     void cvt_i2f() {
   439         singleByteOp(OP_CVT_I2F);
   440     }
   441     void newarray(Opcode op, Handle<ArrayMap> map, Heap::Tenure tenure) {
   442         assert(op == OP_NEWFIXED ||
   443                op == OP_NEWEMPTY ||
   444                op == OP_NEWSIZED);
   445 
   446         recordOops();
   447         ensure(OpcodeInfo[op].length);
   448         writeOp(op);
   449         writeUint8(tenure);
   450         writeObject(map);
   451 
   452         if (op == OP_NEWSIZED)
   453             drop(map->type()->levels());
   454 
   455         markOop(1);
   456     }
   457     void newstruct(Handle<Map> map, Heap::Tenure tenure) {
   458         recordOops();
   459         ensure(OP_NEWSTRUCT_LENGTH);
   460         writeOp(OP_NEWSTRUCT);
   461         writeUint8(tenure);
   462         writeObject(map);
   463         markOop(1);
   464     }
   465     void savelifo(unsigned slot) {
   466         ensure(OP_SAVELIFO_LENGTH);
   467         writeOp(OP_SAVELIFO);
   468         writeUint32(slot);
   469     }
   470     void unwindlifo(unsigned slot) {
   471         ensure(OP_UNWINDLIFO_LENGTH);
   472         writeOp(OP_UNWINDLIFO);
   473         writeUint32(slot);
   474     }
   475     void newref(Variable *var) {
   476         recordOops();
   477         if (var->location() == Variable::STACK) {
   478             ensure(OP_VARREF_LIFO_LENGTH);
   479             writeOp(OP_VARREF_LIFO);
   480         } else {
   481             assert(var->location() == Variable::PARAMETER);
   482             ensure(OP_ARGREF_LIFO_LENGTH);
   483             writeOp(OP_ARGREF_LIFO);
   484         }
   485         writeUint32(var->slot());
   486         markOop(1);
   487     }
   488 
   489     void slotref_lifo() {
   490         recordOops();
   491         singleByteOp(OP_SLOTREF_LIFO);
   492         markOop(1);
   493     }
   494     void fieldref_lifo(unsigned index) {
   495         recordOops();
   496         ensure(OP_FIELDREF_LIFO_LENGTH);
   497         writeOp(OP_FIELDREF_LIFO);
   498         writeUint32(index);
   499         markOop(1);
   500     }
   501     void getref(Type *type) {
   502         if (type->pod() == PrimitiveType_Int32) {
   503             singleByteOp(OP_GETREF_I);
   504         } else {
   505             assert(type->pod() == PrimitiveType_Float);
   506             singleByteOp(OP_GETREF_F);
   507         }
   508     }
   509     void setref(Type *type) {
   510         if (type->pod() == PrimitiveType_Int32) {
   511             singleByteOp(OP_SETREF_I);
   512         } else {
   513             assert(type->pod() == PrimitiveType_Float);
   514             singleByteOp(OP_SETREF_F);
   515         }
   516     }
   517     void object(Handle<Object> obj) {
   518         ensure(OP_OBJECT_LENGTH);
   519         writeOp(OP_OBJECT);
   520         writeObject(obj);
   521         markOop(1);
   522     }
   523     void copyarray() {
   524         singleByteOp(OP_COPYARRAY);
   525         markOop(1);
   526     }
   527     void copystruct() {
   528         singleByteOp(OP_COPYSTRUCT);
   529         markOop(1);
   530     }
   531 
   532     void dup() {
   533         bool oop = getOop(1);
   534         singleByteOp(OP_DUP);
   535         markOop(1, oop);
   536     }
   537     void dup2() {
   538         bool oop2 = getOop(2);
   539         bool oop1 = getOop(1);
   540 
   541         singleByteOp(OP_DUP2);
   542 
   543         markOop(2, oop2);
   544         markOop(1, oop1);
   545     }
   546     void swap() {
   547         bool oop2 = getOop(2);
   548         bool oop1 = getOop(1);
   549 
   550         singleByteOp(OP_SWAP);
   551 
   552         markOop(2, oop1);
   553         markOop(1, oop2);
   554     }
   555     void roll3() {
   556         bool oop3 = getOop(3);
   557         bool oop2 = getOop(2);
   558         bool oop1 = getOop(1);
   559 
   560         singleByteOp(OP_ROLL3);
   561 
   562         markOop(3, oop2);
   563         markOop(2, oop1);
   564         markOop(1, oop3);
   565     }
   566     void pick2() {
   567         bool oop2 = getOop(2);
   568         bool oop1 = getOop(1);
   569 
   570         singleByteOp(OP_PICK2);
   571 
   572         markOop(3, oop2);
   573         markOop(2, oop1);
   574         markOop(1, oop2);
   575     }
   576     void pick3() {
   577         bool oop3 = getOop(3);
   578         bool oop2 = getOop(2);
   579         bool oop1 = getOop(1);
   580 
   581         singleByteOp(OP_PICK3);
   582 
   583         markOop(4, oop3);
   584         markOop(3, oop2);
   585         markOop(2, oop1);
   586         markOop(1, oop3);
   587     }
   588 
   589     void returnvoid() {
   590         singleByteOp(OP_RETURNVOID);
   591     }
   592     void module() {
   593         singleByteOp(OP_MODULE);
   594         markOop(1);
   595     }
   596     void getmodule_fn(unsigned index) {
   597         ensure(OP_GETMODULE_FN_LENGTH);
   598         writeOp(OP_GETMODULE_FN);
   599         writeUint32(index);
   600         markOop(1);
   601     }
   602     void bitnot() {
   603         singleByteOp(OP_BITNOT);
   604     }
   605     void not_() {
   606         singleByteOp(OP_NOT);
   607     }
   608     void not_f() {
   609         singleByteOp(OP_NOT_F);
   610     }
   611     void neg() {
   612         singleByteOp(OP_NEG);
   613     }
   614     void neg_f() {
   615         singleByteOp(OP_NEG_F);
   616     }
   617     void bits_i2f() {
   618         singleByteOp(OP_BITS_I2F);
   619     }
   620     void bits_f2i() {
   621         singleByteOp(OP_BITS_F2I);
   622     }
   623     void true_() {
   624         singleByteOp(OP_TRUE);
   625     }
   626     void false_() {
   627         singleByteOp(OP_FALSE);
   628     }
   629 
   630     void tableswitch(int low, int high, Label *def, Label **labels) {
   631         unsigned tablesize = TableSwitchEntries(low, high);
   632         assert(tablesize <= MAX_TABLESWITCH_ENTRIES);
   633 
   634         ensure(OP_TABLESWITCH_LENGTH);
   635         writeOp(OP_TABLESWITCH);
   636         writeInt32(low);
   637         writeInt32(high);
   638         jump(def);
   639 
   640         // When labels are patched, we assume each position is a jump. Rather
   641         // than hack position offsets to make labels work without actual
   642         // incoming jumps, we emit a jump inline.
   643         for (unsigned i = 0; i < tablesize; i++)
   644             jump(labels[i]);
   645     }
   646 
   647     // Helpers.
   648     void get(Variable *var) {
   649         if (var->location() == Variable::STACK) {
   650             getlocal(var);
   651         } else {
   652             assert(var->location() == Variable::PARAMETER);
   653             getarg(var);
   654         }
   655     }
   656     void set(Variable *var) {
   657         assert(!var->type()->isReference());
   658         if (var->location() == Variable::STACK) {
   659             setlocal(var);
   660         } else {
   661             assert(var->location() == Variable::PARAMETER);
   662             setarg(var);
   663         }
   664     }
   665 };
   666 
   667 }
   668 
   669 #endif // _include_bytecode_emitter_h_