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