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