src/compiler/BytecodeEmitter.cpp
author Asher Baker <asherkin@gmail.com>
Mon Jun 10 15:52:44 2013 +0100 (2013-06-10)
changeset 265 3a903b89a514
parent 263 ba85a47ee414
permissions -rw-r--r--
Fixed type checking for multidimensional 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 #include <stdlib.h>
    20 #include <string.h>
    21 #include "../Utility.h"
    22 #include "../Types.h"
    23 #include "../Zone.h"
    24 #include "../Strings.h"
    25 #include "../Zone.h"
    26 #include "BytecodeEmitter.h"
    27 #include "Symbols.h"
    28 #include "Scopes.h"
    29 #include "../Spaces-inl.h"
    30 #include "../Heap-inl.h"
    31 
    32 using namespace ke;
    33 
    34 StringTable::StringTable()
    35   : set_(ZoneAllocatorPolicy()),
    36     initialized_(false)
    37 {
    38 }
    39 
    40 bool
    41 StringTable::initialize()
    42 {
    43     assert(!initialized_);
    44     initialized_ = true;
    45     if (!set_.init(16))
    46         return false;
    47     return true;
    48 }
    49 
    50 bool
    51 StringTable::add(Handle<String> name, unsigned *indexp)
    52 {
    53     if (!initialized_ && !initialize())
    54         return false;
    55 
    56     Set::Insert p = set_.findForAdd(*name);
    57     if (p.found()) {
    58         *indexp = p->index;
    59         return true;
    60     }
    61 
    62     if (strings_.length() > INT_MAX) {
    63         ZONE()->reportAllocationOverflow();
    64         return false;
    65     }
    66 
    67     Entry e(name, strings_.length());
    68     if (!strings_.append(e))
    69         return false;
    70 
    71     if (!set_.add(p, e))
    72         return false;
    73 
    74     *indexp = e.index;
    75 
    76     return true;
    77 }
    78 
    79 void
    80 StringTable::accept(VirtualObjectVisitor *visitor)
    81 {
    82     for (size_t i = 0; i < strings_.length(); i++)
    83         visitor->visit(reinterpret_cast<Object **>(&strings_[i].string));
    84 }
    85 
    86 BytecodeEmitter::BytecodeEmitter()
    87   : AutoRooted(ZONE()),
    88     buffer_(inline_buffer_),
    89     buffer_pos_(buffer_),
    90     buffer_end_(buffer_pos_ + ARRAY_LENGTH(inline_buffer_)),
    91     oom_(false),
    92     maxStackDepth_(0),
    93     loopDepth_(0),
    94     maxLoopDepth_(0)
    95 {
    96     lastCodePosition_ = 0;
    97     firstLine_ = 1;
    98 }
    99 
   100 BytecodeEmitter::~BytecodeEmitter()
   101 {
   102     for (size_t i = 0; i < oopRecords_.length(); i++)
   103         delete oopRecords_[i];
   104 }
   105 
   106 void
   107 BytecodeEmitter::accept(VirtualObjectVisitor *visitor)
   108 {
   109     for (size_t i = 0; i < objects_.length(); i++)
   110         visitor->visit(&objects_[i]);
   111 
   112     strings_.accept(visitor);
   113 }
   114 
   115 bool
   116 BytecodeEmitter::growBuffer(size_t nbytes)
   117 {
   118     if (oom_) {
   119         buffer_pos_ = buffer_;
   120         return false;
   121     }
   122 
   123     size_t newSize = size_t((buffer_end_ - buffer_) * 2);
   124     if (newSize >= MAX_LENGTH) {
   125         oom_ = true;
   126         buffer_pos_ = buffer_;
   127         ZONE()->reportOutOfMemory();
   128         return false;
   129     }
   130 
   131     bytecode *newBuffer;
   132     if (buffer_ == inline_buffer_)
   133         newBuffer = (bytecode *)malloc(newSize);
   134     else
   135         newBuffer = (bytecode *)realloc(buffer_, newSize);
   136 
   137     // If we can't allocate a buffer, reset the buffer to the start and set
   138     // a flag to indicate out-of-memory.
   139     if (!newBuffer) {
   140         oom_ = true;
   141         buffer_pos_ = buffer_;
   142         ZONE()->reportOutOfMemory();
   143         return false;
   144     }
   145 
   146     if (buffer_ == inline_buffer_)
   147         memcpy(newBuffer, inline_buffer_, (buffer_pos_ - inline_buffer_));
   148 
   149     buffer_pos_ = newBuffer + size_t(buffer_pos_ - buffer_);
   150     buffer_ = newBuffer;
   151     buffer_end_ = buffer_ + newSize;
   152     assert(buffer_pos_ + nbytes < buffer_end_);
   153 
   154     return true;
   155 }
   156 
   157 void
   158 BytecodeEmitter::recordOops()
   159 {
   160     OopRecord *record = new OopRecord(oopMap_.length(), position());
   161 
   162     if (!record->slots.ensure(record->depth)) {
   163         oom_ = true;
   164         return;
   165     }
   166 
   167     bool hadOops = false;
   168     for (size_t i = 0; i < oopMap_.length(); i++) {
   169         bool bit = oopMap_[i];
   170         record->slots.infallibleAppend(bit);
   171         hadOops |= bit;
   172     }
   173 
   174     if (!hadOops) {
   175         delete record;
   176         return;
   177     }
   178 
   179     oom_ &= oopRecords_.append(record);
   180 }
   181 
   182 void
   183 BytecodeEmitter::note_position(const SourcePosition &pos)
   184 {
   185     assert(pos.line > 0);
   186     assert(unsigned(pos.line) >= firstLine_);
   187 
   188     unsigned lineDelta = unsigned(pos.line) - firstLine_;
   189     unsigned pcDelta = unsigned(position()) - lastCodePosition_;
   190 
   191     sourcemap_.writeUint32(pcDelta);
   192     sourcemap_.writeUint32(lineDelta);
   193     sourcemap_.writeUint32(pos.col);
   194 
   195     lastCodePosition_ = unsigned(position());
   196 }
   197 
   198 void
   199 BytecodeEmitter::getglobal(Symbol *sym)
   200 {
   201     ensure(OP_GETGLOBAL_LENGTH);
   202     writeOp(OP_GETGLOBAL);
   203     writeUint32(addString(sym->name()));
   204     if (sym->type()->isTraceable())
   205         markOop(1);
   206 }
   207 void
   208 BytecodeEmitter::setglobal(Symbol *sym)
   209 {
   210     ensure(OP_SETGLOBAL_LENGTH);
   211     writeOp(OP_SETGLOBAL);
   212     writeUint32(addString(sym->name()));
   213     if (sym->type()->isTraceable())
   214         markOop(1);
   215 }
   216 
   217 void
   218 BytecodeEmitter::getlocal(VariableSymbol *var)
   219 {
   220     assert(var->storage() == VariableSymbol::Local);
   221     ensure(OP_GETLOCAL_LENGTH);
   222     writeOp(OP_GETLOCAL);
   223     writeUint32(var->slot());
   224     if (var->type()->isTraceable())
   225         markOop(1);
   226 }
   227 
   228 void
   229 BytecodeEmitter::setlocal(VariableSymbol *var)
   230 {
   231     assert(var->storage() == VariableSymbol::Local);
   232     ensure(OP_SETLOCAL_LENGTH);
   233     writeOp(OP_SETLOCAL);
   234     writeUint32(var->slot());
   235     if (var->type()->isTraceable())
   236         markOop(1);
   237 }
   238 
   239 void
   240 BytecodeEmitter::getarg(VariableSymbol *var)
   241 {
   242     assert(var->storage() == VariableSymbol::Arg);
   243     ensure(OP_GETARG_LENGTH);
   244     writeOp(OP_GETARG);
   245     writeUint32(var->slot());
   246     if (var->type()->isTraceable())
   247         markOop(1);
   248 }
   249 
   250 void
   251 BytecodeEmitter::setarg(VariableSymbol *var)
   252 {
   253     assert(var->storage() == VariableSymbol::Arg);
   254     ensure(OP_SETARG_LENGTH);
   255     writeOp(OP_SETARG);
   256     writeUint32(var->slot());
   257     if (var->type()->isTraceable())
   258         markOop(1);
   259 }
   260 
   261 bool
   262 BytecodeEmitter::allocate(VariableSymbol *sym)
   263 {
   264     assert(sym->isVariable());
   265     assert(sym->scope()->kind() == Scope::BLOCK);
   266 
   267     switch (sym->scope()->kind()) {
   268       case Scope::BLOCK:
   269       {
   270         // See overall comment on overflows.
   271         assert(locals_.length() < UINT_MAX);
   272         sym->allocate(VariableSymbol::Local, locals_.length());
   273         if (!locals_.append(sym))
   274             return false;
   275         break;
   276       }
   277 
   278       default:
   279         assert(false);
   280         return false;
   281     }
   282 
   283     return true;
   284 }
   285 
   286 void
   287 BytecodeEmitter::newref(VariableSymbol *var)
   288 {
   289     recordOops();
   290     if (var->storage() == VariableSymbol::Local) {
   291         ensure(OP_VARREF_LIFO_LENGTH);
   292         writeOp(OP_VARREF_LIFO);
   293     } else {
   294         assert(var->storage() == VariableSymbol::Arg);
   295         ensure(OP_ARGREF_LIFO_LENGTH);
   296         writeOp(OP_ARGREF_LIFO);
   297     }
   298     writeUint32(var->slot());
   299     markOop(1);
   300 }
   301 
   302 Code *
   303 BytecodeEmitter::finish()
   304 {
   305     stop();
   306 
   307     Zone *zone = ZONE();
   308     if (oom() || zone->hasPendingException())
   309         return NULL;
   310 
   311     Local<ByteArray> bytes(zone, ByteArray::New(zone, length(), Heap::Tenure_Old));
   312     if (!bytes)
   313         return NULL;
   314     memcpy(bytes->raw(), buffer(), length());
   315 
   316     Local<FixedArray> objects(zone, FixedArray::New(zone, objects_.length(), Heap::Tenure_Old));
   317     if (!objects)
   318         return NULL;
   319 
   320     for (size_t i = 0; i < objects_.length(); i++)
   321         objects->set(zone, i, objects_[i]);
   322 
   323     // If any stack slots hold gc pointers at potential failure points in the
   324     // bytecode, record them now. We do this by building a list of tuples;
   325     // each tuple contains the pc offset, and a bitset containing which stack
   326     // slots must be scanned by the gc.
   327     Local<ByteArray> safepoints(zone);
   328     if (oopRecords_.length()) {
   329         size_t bytesPerSafepoint = Code::BytesPerSafepoint(maxStackDepth());
   330         size_t gcmapSize = bytesPerSafepoint * oopRecords_.length();
   331         safepoints = ByteArray::New(zone, gcmapSize, Heap::Tenure_Old);
   332         if (!safepoints)
   333             return NULL;
   334 
   335         Address cursor = safepoints->raw();
   336         for (size_t i = 0; i < oopRecords_.length(); i++) {
   337             OopRecord *record = oopRecords_[i];
   338             assert(record->depth <= maxStackDepth());
   339 
   340             *reinterpret_cast<unsigned *>(cursor) = record->offset;
   341 
   342             unsigned char *bits = reinterpret_cast<unsigned char *>(cursor + sizeof(unsigned));
   343             for (unsigned i = 0; i < record->depth; i++)
   344                 bits[i / 8] |= (1 << (i % 8));
   345 
   346             cursor += bytesPerSafepoint;
   347         }
   348 
   349         assert(cursor == safepoints->raw() + gcmapSize);
   350     }
   351 
   352     Local<ByteArray> sourcemap(zone);
   353     if (sourcemap_.length()) {
   354         sourcemap = ByteArray::New(zone, sourcemap_.length(), Heap::Tenure_Old);
   355         if (!sourcemap)
   356             return NULL;
   357 
   358         memcpy(sourcemap->raw(), sourcemap_.buffer(), sourcemap_.length());
   359     }
   360 
   361     Local<FixedArray> strings(zone, FixedArray::New(zone, strings_.stringCount(), Heap::Tenure_Old));
   362     if (!strings)
   363         return NULL;
   364     for (size_t i = 0; i < strings_.stringCount(); i++)
   365         strings->set(zone, i, strings_.stringAt(i));
   366 
   367     Local<FixedArray> locals(zone, FixedArray::New(zone, locals_.length(), Heap::Tenure_Old));
   368     if (!locals)
   369         return NULL;
   370     for (size_t i = 0; i < locals_.length(); i++)
   371         locals->set(zone, i, locals_[i]->type());
   372 
   373     Local<Code> code(zone);
   374 
   375     code = Code::New(zone,
   376                      bytes,
   377                      locals,
   378                      objects,
   379                      strings,
   380                      maxStackDepth(),
   381                      maxLoopDepth_,
   382                      safepoints,
   383                      sourcemap);
   384 
   385     return code;
   386 }