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