diff -r c4b1341297e5 -r ba85a47ee414 src/compiler/BytecodeEmitter.cpp --- a/src/compiler/BytecodeEmitter.cpp Mon Jun 10 00:46:29 2013 +0100 +++ b/src/compiler/BytecodeEmitter.cpp Mon Jun 10 01:04:43 2013 +0100 @@ -1,386 +1,386 @@ -/* 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() - : AutoRooted(ZONE()), - buffer_(inline_buffer_), - buffer_pos_(buffer_), - buffer_end_(buffer_pos_ + ARRAY_LENGTH(inline_buffer_)), - oom_(false), - maxStackDepth_(0), - loopDepth_(0), - maxLoopDepth_(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) -{ - assert(var->storage() == VariableSymbol::Local); - ensure(OP_GETLOCAL_LENGTH); - writeOp(OP_GETLOCAL); - writeUint32(var->slot()); - if (var->type()->isTraceable()) - markOop(1); -} - -void -BytecodeEmitter::setlocal(VariableSymbol *var) -{ - assert(var->storage() == VariableSymbol::Local); - ensure(OP_SETLOCAL_LENGTH); - writeOp(OP_SETLOCAL); - writeUint32(var->slot()); - if (var->type()->isTraceable()) - markOop(1); -} - -void -BytecodeEmitter::getarg(VariableSymbol *var) -{ - assert(var->storage() == VariableSymbol::Arg); - ensure(OP_GETARG_LENGTH); - writeOp(OP_GETARG); - writeUint32(var->slot()); - if (var->type()->isTraceable()) - markOop(1); -} - -void -BytecodeEmitter::setarg(VariableSymbol *var) -{ - assert(var->storage() == VariableSymbol::Arg); - ensure(OP_SETARG_LENGTH); - writeOp(OP_SETARG); - 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; -} - -void -BytecodeEmitter::newref(VariableSymbol *var) -{ - recordOops(); - if (var->storage() == VariableSymbol::Local) { - ensure(OP_VARREF_LIFO_LENGTH); - writeOp(OP_VARREF_LIFO); - } else { - assert(var->storage() == VariableSymbol::Arg); - ensure(OP_ARGREF_LIFO_LENGTH); - writeOp(OP_ARGREF_LIFO); - } - writeUint32(var->slot()); - markOop(1); -} - -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(), - maxLoopDepth_, - safepoints, - sourcemap); - - return code; -} +/* 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() + : AutoRooted(ZONE()), + buffer_(inline_buffer_), + buffer_pos_(buffer_), + buffer_end_(buffer_pos_ + ARRAY_LENGTH(inline_buffer_)), + oom_(false), + maxStackDepth_(0), + loopDepth_(0), + maxLoopDepth_(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) +{ + assert(var->storage() == VariableSymbol::Local); + ensure(OP_GETLOCAL_LENGTH); + writeOp(OP_GETLOCAL); + writeUint32(var->slot()); + if (var->type()->isTraceable()) + markOop(1); +} + +void +BytecodeEmitter::setlocal(VariableSymbol *var) +{ + assert(var->storage() == VariableSymbol::Local); + ensure(OP_SETLOCAL_LENGTH); + writeOp(OP_SETLOCAL); + writeUint32(var->slot()); + if (var->type()->isTraceable()) + markOop(1); +} + +void +BytecodeEmitter::getarg(VariableSymbol *var) +{ + assert(var->storage() == VariableSymbol::Arg); + ensure(OP_GETARG_LENGTH); + writeOp(OP_GETARG); + writeUint32(var->slot()); + if (var->type()->isTraceable()) + markOop(1); +} + +void +BytecodeEmitter::setarg(VariableSymbol *var) +{ + assert(var->storage() == VariableSymbol::Arg); + ensure(OP_SETARG_LENGTH); + writeOp(OP_SETARG); + 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; +} + +void +BytecodeEmitter::newref(VariableSymbol *var) +{ + recordOops(); + if (var->storage() == VariableSymbol::Local) { + ensure(OP_VARREF_LIFO_LENGTH); + writeOp(OP_VARREF_LIFO); + } else { + assert(var->storage() == VariableSymbol::Arg); + ensure(OP_ARGREF_LIFO_LENGTH); + writeOp(OP_ARGREF_LIFO); + } + writeUint32(var->slot()); + markOop(1); +} + +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(), + maxLoopDepth_, + safepoints, + sourcemap); + + return code; +}