diff -r b029d63565a2 -r c1ba166f3fc4 src/compiler/BytecodeEmitter.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/compiler/BytecodeEmitter.cpp Sat Nov 17 18:41:01 2012 -0800 @@ -0,0 +1,344 @@ +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#include +#include +#include "../Utility.h" +#include "../Types.h" +#include "../Zone.h" +#include "../Strings.h" +#include "../Zone.h" +#include "BytecodeEmitter.h" +#include "Symbols.h" +#include "Scopes.h" +#include "../Spaces-inl.h" +#include "../Heap-inl.h" + +using namespace ke; + +StringTable::StringTable() + : set_(ZoneAllocatorPolicy()), + initialized_(false) +{ +} + +bool +StringTable::initialize() +{ + assert(!initialized_); + initialized_ = true; + if (!set_.init(16)) + return false; + return true; +} + +bool +StringTable::add(Handle name, unsigned *indexp) +{ + if (!initialized_ && !initialize()) + return false; + + Set::Insert p = set_.findForAdd(*name); + if (p.found()) { + *indexp = p->index; + return true; + } + + if (strings_.length() > INT_MAX) { + ZONE()->reportAllocationOverflow(); + return false; + } + + Entry e(name, strings_.length()); + if (!strings_.append(e)) + return false; + + if (!set_.add(p, e)) + return false; + + *indexp = e.index; + + return true; +} + +void +StringTable::accept(VirtualObjectVisitor *visitor) +{ + for (size_t i = 0; i < strings_.length(); i++) + visitor->visit(reinterpret_cast(&strings_[i].string)); +} + +BytecodeEmitter::BytecodeEmitter(PoolAllocator &pool) + : AutoRooted(ZONE()), + pool_(pool), + buffer_(inline_buffer_), + buffer_pos_(buffer_), + buffer_end_(buffer_pos_ + ARRAY_LENGTH(inline_buffer_)), + oom_(false), + maxStackDepth_(0) +{ + lastCodePosition_ = 0; + firstLine_ = 1; +} + +BytecodeEmitter::~BytecodeEmitter() +{ + for (size_t i = 0; i < oopRecords_.length(); i++) + delete oopRecords_[i]; +} + +void +BytecodeEmitter::accept(VirtualObjectVisitor *visitor) +{ + for (size_t i = 0; i < objects_.length(); i++) + visitor->visit(&objects_[i]); + + strings_.accept(visitor); +} + +bool +BytecodeEmitter::growBuffer(size_t nbytes) +{ + if (oom_) { + buffer_pos_ = buffer_; + return false; + } + + size_t newSize = size_t((buffer_end_ - buffer_) * 2); + if (newSize >= MAX_LENGTH) { + oom_ = true; + buffer_pos_ = buffer_; + ZONE()->reportOutOfMemory(); + return false; + } + + bytecode *newBuffer; + if (buffer_ == inline_buffer_) + newBuffer = (bytecode *)malloc(newSize); + else + newBuffer = (bytecode *)realloc(buffer_, newSize); + + // If we can't allocate a buffer, reset the buffer to the start and set + // a flag to indicate out-of-memory. + if (!newBuffer) { + oom_ = true; + buffer_pos_ = buffer_; + ZONE()->reportOutOfMemory(); + return false; + } + + if (buffer_ == inline_buffer_) + memcpy(newBuffer, inline_buffer_, (buffer_pos_ - inline_buffer_)); + + buffer_pos_ = newBuffer + size_t(buffer_pos_ - buffer_); + buffer_ = newBuffer; + buffer_end_ = buffer_ + newSize; + assert(buffer_pos_ + nbytes < buffer_end_); + + return true; +} + +void +BytecodeEmitter::recordOops() +{ + OopRecord *record = new OopRecord(oopMap_.length(), position()); + + if (!record->slots.ensure(record->depth)) { + oom_ = true; + return; + } + + bool hadOops = false; + for (size_t i = 0; i < oopMap_.length(); i++) { + bool bit = oopMap_[i]; + record->slots.infallibleAppend(bit); + hadOops |= bit; + } + + if (!hadOops) { + delete record; + return; + } + + oom_ &= oopRecords_.append(record); +} + +void +BytecodeEmitter::note_position(const SourcePosition &pos) +{ + assert(pos.line > 0); + assert(unsigned(pos.line) >= firstLine_); + + unsigned lineDelta = unsigned(pos.line) - firstLine_; + unsigned pcDelta = unsigned(position()) - lastCodePosition_; + + sourcemap_.writeUint32(pcDelta); + sourcemap_.writeUint32(lineDelta); + sourcemap_.writeUint32(pos.col); + + lastCodePosition_ = unsigned(position()); +} + +void +BytecodeEmitter::getglobal(Symbol *sym) +{ + ensure(OP_GETGLOBAL_LENGTH); + writeOp(OP_GETGLOBAL); + writeUint32(addString(sym->name())); + if (sym->type()->isTraceable()) + markOop(1); +} +void +BytecodeEmitter::setglobal(Symbol *sym) +{ + ensure(OP_SETGLOBAL_LENGTH); + writeOp(OP_SETGLOBAL); + writeUint32(addString(sym->name())); + if (sym->type()->isTraceable()) + markOop(1); +} + +void +BytecodeEmitter::getlocal(VariableSymbol *var) +{ + ensure(OP_GETLOCAL_LENGTH); + writeOp(OP_GETLOCAL); + writeUint32(var->slot()); + if (var->type()->isTraceable()) + markOop(1); +} + +void +BytecodeEmitter::setlocal(VariableSymbol *var) +{ + ensure(OP_SETLOCAL_LENGTH); + writeOp(OP_SETLOCAL); + writeUint32(var->slot()); + if (var->type()->isTraceable()) + markOop(1); +} + +bool +BytecodeEmitter::allocate(VariableSymbol *sym) +{ + assert(sym->isVariable()); + assert(sym->scope()->kind() == Scope::BLOCK); + + switch (sym->scope()->kind()) { + case Scope::BLOCK: + { + // See overall comment on overflows. + assert(locals_.length() < UINT_MAX); + sym->allocate(VariableSymbol::Local, locals_.length()); + if (!locals_.append(sym)) + return false; + break; + } + + default: + assert(false); + return false; + } + + return true; +} + +Code * +BytecodeEmitter::finish() +{ + stop(); + + Zone *zone = ZONE(); + if (oom() || zone->hasPendingException()) + return NULL; + + Local bytes(zone, ByteArray::New(zone, length(), Heap::Tenure_Old)); + if (!bytes) + return NULL; + memcpy(bytes->raw(), buffer(), length()); + + Local objects(zone, FixedArray::New(zone, objects_.length(), Heap::Tenure_Old)); + if (!objects) + return NULL; + + for (size_t i = 0; i < objects_.length(); i++) + objects->set(zone, i, objects_[i]); + + // If any stack slots hold gc pointers at potential failure points in the + // bytecode, record them now. We do this by building a list of tuples; + // each tuple contains the pc offset, and a bitset containing which stack + // slots must be scanned by the gc. + Local safepoints(zone); + if (oopRecords_.length()) { + size_t bytesPerSafepoint = Code::BytesPerSafepoint(maxStackDepth()); + size_t gcmapSize = bytesPerSafepoint * oopRecords_.length(); + safepoints = ByteArray::New(zone, gcmapSize, Heap::Tenure_Old); + if (!safepoints) + return NULL; + + Address cursor = safepoints->raw(); + for (size_t i = 0; i < oopRecords_.length(); i++) { + OopRecord *record = oopRecords_[i]; + assert(record->depth <= maxStackDepth()); + + *reinterpret_cast(cursor) = record->offset; + + unsigned char *bits = reinterpret_cast(cursor + sizeof(unsigned)); + for (unsigned i = 0; i < record->depth; i++) + bits[i / 8] |= (1 << (i % 8)); + + cursor += bytesPerSafepoint; + } + + assert(cursor == safepoints->raw() + gcmapSize); + } + + Local sourcemap(zone); + if (sourcemap_.length()) { + sourcemap = ByteArray::New(zone, sourcemap_.length(), Heap::Tenure_Old); + if (!sourcemap) + return NULL; + + memcpy(sourcemap->raw(), sourcemap_.buffer(), sourcemap_.length()); + } + + Local strings(zone, FixedArray::New(zone, strings_.stringCount(), Heap::Tenure_Old)); + if (!strings) + return NULL; + for (size_t i = 0; i < strings_.stringCount(); i++) + strings->set(zone, i, strings_.stringAt(i)); + + Local locals(zone, FixedArray::New(zone, locals_.length(), Heap::Tenure_Old)); + if (!locals) + return NULL; + for (size_t i = 0; i < locals_.length(); i++) + locals->set(zone, i, locals_[i]->type()); + + Local code(zone); + + code = Code::New(zone, + bytes, + locals, + objects, + strings, + maxStackDepth(), + safepoints, + sourcemap); + + return code; +}