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