diff -r 06a0015b6a5c -r 32ad0523b309 src/BytecodeCompiler.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/BytecodeCompiler.cpp Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,1096 @@ +/* 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 "BytecodeCompiler.h" +#include "Scopes.h" +#include "Modules.h" +#include "Types.h" +#include "Array.h" +#include "Strings.h" +#include "Zone.h" +#include "Environments.h" +#include "Heap-inl.h" + +using namespace ke; + +typedef BytecodeEmitter::Label Label; + +#define __ emitter_. + +BytecodeCompiler::BytecodeCompiler(Zone *zone, ParseTree *tree, ModuleData *module) + : zone_(zone), + tree_(tree), + emitter_(zone->pool()), + moduleData_(module), + locals_(NULL), + scope_(NULL), + loop_(NULL) +{ +} + +static inline TokenKind +ToCompare(Expression *expr) +{ + if (expr->isBinaryExpression()) { + BinaryExpression *b = expr->toBinaryExpression(); + if (b->token() >= TOK_EQUALS && b->token() <= TOK_OR) + return b->token(); + } else if (expr->isUnaryExpression()) { + UnaryExpression *u = expr->toUnaryExpression(); + if (u->token() == TOK_NOT) + return TOK_NOT; + } + return TOK_EOF; +} + +static inline Opcode +CompareTokenToJump(TokenKind kind) +{ + switch (kind) { + case TOK_EQUALS: + return OP_JEQ; + case TOK_NOTEQUALS: + return OP_JNE; + case TOK_GT: + return OP_JG; + case TOK_GE: + return OP_JGE; + case TOK_LT: + return OP_JL; + default: + assert(kind == TOK_LE); + return OP_JLE; + } +} + +static inline Opcode +TokenToOpcode(TokenKind kind) +{ + switch (kind) { + case TOK_PLUS: + return OP_ADD; + case TOK_MINUS: + return OP_SUB; + case TOK_STAR: + return OP_MUL; + case TOK_SLASH: + return OP_DIV; + case TOK_PERCENT: + return OP_MOD; + case TOK_SHR: + return OP_SHR; + case TOK_USHR: + return OP_USHR; + case TOK_SHL: + return OP_SHL; + case TOK_BITAND: + return OP_BITAND; + case TOK_BITOR: + return OP_BITOR; + case TOK_GT: + return OP_GT; + case TOK_GE: + return OP_GE; + case TOK_LT: + return OP_LT; + case TOK_LE: + return OP_LE; + case TOK_EQUALS: + return OP_EQ; + case TOK_NOTEQUALS: + return OP_NE; + default: + assert(kind == TOK_BITXOR); + return OP_BITXOR; + } +} + +void +BytecodeCompiler::recordLifoSlot(Scope *scope) +{ + if (!scope) + return; + + if (Variable *var = scope->lifoSlot()) { + locals_->set(zone_, var->slot(), var->type()); + __ savelifo(var->slot()); + } +} + +void +BytecodeCompiler::unwindTo(Scope *scope) +{ + // Note: we only need to unwind to the outermost scope's LIFO slot. + Variable *var = NULL; + for (Scope *current = scope_; current != scope; current = current->enclosing()) { + if (Variable *slot = current->lifoSlot()) + var = slot; + } + + if (var) + __ unwindlifo(var->slot()); +} + +void +BytecodeCompiler::visit(NameProxy *name) +{ + switch (name->binding()->kind()) { + case BoundName::VARIABLE: + { + Variable *var = name->binding()->toVariable(); + + if (var->location() == Variable::STACK) { + __ getlocal(var); + assert(!name->type()->isReference()); + } else if (var->location() == Variable::ENVIRONMENT) { + getEnvSlot(var); + } else { + assert(var->location() == Variable::PARAMETER); + __ getarg(var); + + // If this argument is actually a reference, dereference it now. + if (var->type()->isReference()) + __ getref(var->type()->contained()->primitive()); + } + break; + } + + case BoundName::FUNCTION: + { + NamedFunction *fun = name->binding()->toNamedFunction(); + __ module(); + __ getmodule_fn(fun->slot()); + break; + } + + default: + { + NamedConstant *constant = name->binding()->toNamedConstant(); + __ int_(constant->value().toInt()); + break; + } + } +} + +void +BytecodeCompiler::visit(ExpressionStatement *node) +{ + // Expressions leave their value on the top of the stack, so we make sure + // to balance the stack if they're used as a statement. + node->expression()->accept(this); + if (!node->expression()->type()->isVoid()) + __ pop(); +} + +void +BytecodeCompiler::visit(Assignment *node) +{ + // The first step is three sub-steps: + // + // (1) Bind the left-hand side. + // -- If a cumulative operation (+=, etc): + // (2) Save a copy of the binding on the stack. + // (3) Dereference the binding to get a value. + // + if (node->lvalue()->isNameProxy()) { + NameProxy *proxy = node->lvalue()->toNameProxy(); + Variable *var = proxy->binding()->toVariable(); + + // Bind. + if (var->type()->isReference() || var->type()->isArray()) + __ get(var); + + // Get the value if needed. + if (node->token() != TOK_ASSIGN) { + // += etc is not allowed on arrays. + assert(!var->type()->isArray()); + + if (var->type()->isReference()) { + __ dup(); + __ getref(var->type()->contained()->primitive()); + } else { + __ get(var); + } + } + } else { + IndexExpression *index = node->lvalue()->toIndexExpression(); + __ note_position(index->pos()); + + // Note: Eventually, watch out for a whole here. An expression like: + // + // (a[4] = (5, a = [])) + // + // Could shrink array, so we need to either bind a[i] to a Reference, + // or keep the bounds check near the set. This is just a semantic + // decision. + index->left()->accept(this); + index->right()->accept(this); + + if (node->token() != TOK_ASSIGN) { + __ dup2(); + __ bounds(); + if (node->type()->isArray()) + __ getarray_o(); + else + __ getarray(node->type()->primitive()); + } + } + + if (node->token() == TOK_ASSIGN) { + node->expression()->accept(this); + } else { + visitForALU(node->type(), node->expression()); + + TokenKind alu = TokenKind(int(node->token()) - (TOK_ASSIGN_ADD - TOK_PLUS)); + __ note_position(node->pos()); + __ binary(TokenToOpcode(alu)); + } + + // Emit the store. + if (node->expression()->type()->isArray()) { + // Unlike references, which can only be computed from a NamePRoxy, + // arrays can be computed from multiple lvalue types. + __ note_position(node->pos()); + __ arraycopy(); + } else if (node->lvalue()->isNameProxy()) { + NameProxy *proxy = node->lvalue()->toNameProxy(); + Variable *var = proxy->binding()->toVariable(); + + if (var->type()->isReference()) + __ setref(var->type()->contained()->primitive()); + else + __ set(var); + } else { + IndexExpression *index = node->lvalue()->toIndexExpression(); + // :TODO: get rid of the BOUNDS instruction, embed it in g/setarray*? + // We can't emit it here because the stack is slightly out of position. + // __ bounds(); + __ setarray(index->left()->type()->contained()->primitive()); + } +} + +void +BytecodeCompiler::visit(IncDecExpression *node) +{ + // In the postfix case, the logic looks like: + // (1) Bind -> REF + // (2) Dup -> REF REF + // (3) Fetch -> REF VAL + // (4) Swap -> VAL REF + // (5) Pick -> VAL REF VAL + // (6) Add -> VAL REF VAL+1 + // (7) Store -> VAL VAL+1 + // (8) Drop -> VAL + // + // In the prefix case, the logic looks like: + // (1) Bind -> REF + // (2) Dup -> REF REF + // (3) Fetch -> REF VAL + // -- + // -- + // (6) Add -> REF VAL+1 + // (7) Store -> VAL+1 + // -- + // + // This is squirrely enough such that we don't bother sharing too much + // with the above. + // + // Note: We simulate a "Pick" operation with a dup2+pop. + + // This is steps (1) to (5). + Expression *expr = node->expression(); + if (expr->isNameProxy()) { + NameProxy *proxy = expr->toNameProxy(); + Variable *var = proxy->binding()->toVariable(); + + // Bind. + if (var->type()->isReference() || var->type()->isArray()) + __ get(var); + + // Dup and fetch. + if (var->type()->isReference()) { + __ dup(); + __ getref(node->type()->primitive()); + if (node->postfix()) { + __ swap(); + __ pick2(); + } + } else { + assert(!var->type()->isArray()); + __ get(var); + if (node->postfix()) + __ dup(); + } + } else { + IndexExpression *index = expr->toIndexExpression(); + __ note_position(index->pos()); + + index->left()->accept(this); + index->right()->accept(this); + + assert(!node->type()->isArray()); + __ bounds(); + __ dup2(); + __ getarray(node->type()->primitive()); + + if (node->postfix()) { + // The stack looks like this now: + // ARRAY INDEX VALUE + // + // So we must use a PICK operation to rotate the stack into position. + __ roll3(); // INDEX VALUE ARRAY + __ roll3(); // VALUE ARRAY INDEX + __ pick3(); // VALUE ARRAY INDEX VALUE + } + } + + // Perform step (6). + if (node->type()->isFloat()) { + __ float_(1.0); + } else { + assert(node->type()->isInt32()); + __ int_(1); + } + + __ note_position(node->pos()); + if (node->token() == TOK_INCREMENT) + __ binary(OP_ADD); + else + __ binary(OP_SUB); + + // Perform step (7). + if (expr->isNameProxy()) { + NameProxy *proxy = expr->toNameProxy(); + Variable *var = proxy->binding()->toVariable(); + + if (var->type()->isReference()) + __ setref(node->type()->primitive()); + else + __ set(var); + } else { + __ setarray(node->type()->primitive()); + } + + // Perform step (8). + if (node->postfix()) + __ pop(); +} + +void +BytecodeCompiler::visitForALU(Type *needed, Expression *node) +{ + node->accept(this); + if (needed->isFloat() && !node->type()->isFloat()) + __ cvt_i2f(); + else + assert(needed->primitive() == node->type()->primitive()); +} + +static inline Type * +GetCoercionType(TokenKind tok, Type *left, Type *right) +{ + if (left->isFloat()) + return left; + return right; +} + +void +BytecodeCompiler::visit(UnaryExpression *node) +{ + switch (node->token()) { + case TOK_MINUS: + { + Local type(zone_, node->expression()->type()); + node->expression()->accept(this); + + if (node->type()->isFloat()) + __ neg_f(); + else + __ neg(); + break; + } + + case TOK_NOT: + { + Local type(zone_, node->expression()->type()); + node->expression()->accept(this); + + if (node->type()->isFloat()) + __ not_f(); + else + __ not_(); + break; + } + + default: + assert(node->token() == TOK_TILDE); + node->expression()->accept(this); + __ bitnot(); + break; + } +} + +void +BytecodeCompiler::visit(BinaryExpression *node) +{ + if (node->token() == TOK_AND || node->token() == TOK_OR) { + Label success, failure, done; + visitForTest(node, &success, &failure, &success); + __ bind(&success); + __ int_(1); + __ drop(1); // Otherwise, the stack depth will be +1 too much. + __ jump(&done); + __ bind(&failure); + __ int_(0); + __ bind(&done); + return; + } + + Local leftType(zone_, node->left()->type()); + Local rightType(zone_, node->right()->type()); + Local coerce(zone_, GetCoercionType(node->token(), leftType, rightType)); + + visitForALU(coerce, node->left()); + visitForALU(coerce, node->right()); + + Opcode op = TokenToOpcode(node->token()); + if (node->type()->isFloat()) + op = (Opcode)(int(op) + (OP_ADD_F - OP_ADD)); + + __ note_position(node->pos()); + __ binary(op); +} + +void +BytecodeCompiler::visit(IndexExpression *node) +{ + node->left()->accept(this); + node->right()->accept(this); + __ note_position(node->pos()); + __ bounds(); + + if (node->type()->isArray()) + __ getarray_o(); + else + __ getarray(node->type()->primitive()); +} + +void +BytecodeCompiler::visit(ReturnStatement *node) +{ + if (node->expression()) { + node->expression()->accept(this); + __ return_(); + } else { + __ returnvoid(); + } +} + +void +BytecodeCompiler::visitForTest(Expression *expression, + Label *trueBranch, + Label *falseBranch, + Label *fallthrough) +{ + TokenKind token = ToCompare(expression); + if (token == TOK_EOF || token == TOK_NOT) { + if (token == TOK_EOF) + expression->accept(this); + else + expression->toUnaryExpression()->expression()->accept(this); + + bool jumpOnTrue = (fallthrough == falseBranch) ^ (token == TOK_NOT); + + Opcode op = jumpOnTrue ? OP_JT : OP_JF; + if (expression->type()->isFloat()) + op = (Opcode)(int(op) + (OP_JL_F - OP_JL)); + + if (fallthrough == falseBranch) + __ j(op, trueBranch); + else + __ j(op, falseBranch); + return; + } + + BinaryExpression *binary = expression->toBinaryExpression(); + + if (token == TOK_AND || token == TOK_OR) { + Label next; + if (token == TOK_AND) + visitForTest(binary->left(), &next, falseBranch, &next); + else + visitForTest(binary->left(), trueBranch, &next, &next); + __ bind(&next); + visitForTest(binary->right(), trueBranch, falseBranch, fallthrough); + return; + } + + Opcode op = CompareTokenToJump(token); + if (fallthrough == trueBranch) + op = Invert(op); + + Local leftType(zone_, binary->left()->type()); + Local rightType(zone_, binary->right()->type()); + Local coerce(zone_, GetCoercionType(binary->token(), leftType, rightType)); + + visitForALU(coerce, binary->left()); + visitForALU(coerce, binary->right()); + if (coerce->isFloat()) + op = (Opcode)(int(op) + (OP_JL_F - OP_JL)); + + if (fallthrough == trueBranch) + __ j(op, falseBranch); + else + __ j(op, trueBranch); +} + +void +BytecodeCompiler::visit(ForStatement *node) +{ + AutoEnterScope forScope(this, node->scope()); + + recordLifoSlot(node->scope()); + + Label join, update; + LoopScope loop(scope_, &join, &update, &loop_); + + if (node->initialization()) + node->initialization()->accept(this); + + Label header, test; + if (node->condition()) + __ jump(&test); + + __ bind(&header); + node->body()->accept(this); + + __ bind(&update); + if (node->update()) + node->update()->accept(this); + + if (node->condition()) { + __ bind(&test); + visitForTest(node->condition(), &header, &join, &join); + } else { + __ jump(&header); + } + + __ bind(&join); + + if (node->scope()) + unwindTo(node->scope()->enclosing()); + assert(__ stackDepth() == 0); +} + +void +BytecodeCompiler::visit(WhileStatement *node) +{ + Label header, join; + + __ bind(&header); + + LoopScope loop(scope_, &join, &header, &loop_); + + if (node->token() == TOK_WHILE) { + Label body; + + visitForTest(node->condition(), &body, &join, &body); + __ bind(&body); + node->body()->accept(this); + __ jump(&header); + } else { + node->body()->accept(this); + visitForTest(node->condition(), &header, &join, &join); + } + + __ bind(&join); +} + +void +BytecodeCompiler::visit(BreakStatement *node) +{ + unwindTo(loop_->scope()); + __ jump(loop_->break_()); +} + +void +BytecodeCompiler::visit(ContinueStatement *node) +{ + unwindTo(loop_->scope()); + __ jump(loop_->continue_()); +} + +void +BytecodeCompiler::visit(BlockStatement *node) +{ + AutoEnterScope blockScope(this, node->scope()); + + recordLifoSlot(node->scope()); + + for (size_t i = 0; i < node->statements()->length(); i++) { + Statement *statement = node->statements()->at(i); + statement->accept(this); + assert(__ stackDepth() == 0); + } + + // We don't bother emitting an "unwind" instruction for function scopes, + // since this is taken care of by the calling convention. + if (node->scope() && node->scope()->enclosing()->kind() != Scope::FUNCTION) + unwindTo(node->scope()->enclosing()); +} + +void +BytecodeCompiler::visit(IntegerLiteral *node) +{ + __ int_(node->value()); +} + +void +BytecodeCompiler::visit(FloatLiteral *node) +{ + __ float_((float)node->value()); +} + +static inline ArrayMap * +BuildArrayMap(Zone *zone, Handle type) +{ + Local contained(zone); + if (type->levels() > 1) { + Local containedType(zone, type->contained()); + contained = BuildArrayMap(zone, containedType); + if (!contained) + return NULL; + } + + return ArrayMap::New(zone, type, contained); +} + +void +BytecodeCompiler::visit(StringLiteral *node) +{ + Local type(zone_, node->type()); + Local map(zone_, BuildArrayMap(zone_, type)); + if (!map) + return; + + Local array(zone_, Array::New(zone_, map, node->string()->length(), Heap::Tenure_Old)); + if (!array) + return; + + assert(array->nelements() >= node->string()->length()); + memcpy(array->chars(), node->string()->raw(), node->string()->length() * sizeof(char)); + + __ note_position(node->pos()); + __ object(array); +} + +void +BytecodeCompiler::getEnvSlot(Variable *var) +{ + assert(var->scope()->kind() == Scope::GLOBAL); + __ getglobal(var); +} + +void +BytecodeCompiler::setEnvSlot(Variable *var) +{ + assert(var->scope()->kind() == Scope::GLOBAL); + __ setglobal(var); +} + +void +BytecodeCompiler::visit(VariableDeclaration *node) +{ + Variable *var = node->variable(); + + Local type(zone_, var->type()); + if (type->isArray()) { + Local map(zone_, BuildArrayMap(zone_, type)); + __ note_position(node->pos()); + if (var->scope()->kind() == Scope::GLOBAL) + __ newarray(map, Heap::Tenure_Old); + else if (var->needsHeapAllocation()) + __ newarray(map, Heap::Tenure_Default); + else + __ newarray(map, Heap::Tenure_Stack); + + if (node->initialization()) { + node->initialization()->accept(this); + __ note_position(node->pos()); + __ arraycopy(); + } + } else { + if (node->initialization()) { + node->initialization()->accept(this); + } else { + if (!node->postDimensions()) + __ int_(0); + } + } + + if (var->location() == Variable::STACK) { + __ setlocal(var); + locals_->set(zone_, var->slot(), var->type()); + } else { + assert(var->location() == Variable::ENVIRONMENT); + setEnvSlot(var); + } + + __ pop(); +} + +void +BytecodeCompiler::visit(CallExpression *node) +{ + Local funType(zone_, node->callee()->type()); + + // Sniff out natives, which have a specific opcode and are guaranteed to + // be called via name proxy. + NamedFunction *native = NULL; + if (funType->isNative()) { + native = node->callee()->toNameProxy()->binding()->toNamedFunction(); + } + + for (size_t i = node->arguments()->length() - 1; i < node->arguments()->length(); i--) { + Expression *arg = node->arguments()->at(i); + + // We need to compute a reference if the parameter was in the form: + // f(&a) + // Note this form does not allow: f({}) + // + // We also need to compute references for all variadic native + // arguments, but arrays and references are already in the + // appropriate form. + Local given(zone_, arg->type()); + bool mustPushReference = false; + if (i >= funType->parameters()->length()) { + assert(funType->isNative() && funType->isNativeVariadic()); + mustPushReference = !given->isArray() && !given->isReference(); + } else if (funType->parameterAt(i)->isReference()) { + mustPushReference = !given->isReference(); + } + + // If a native is passed to a native, it must only be one level deep. + assert(!funType->isNative() || !given->isArray() || given->levels() == 1); + + if (mustPushReference) { + if (arg->isNameProxy()) { + // :TODO: test with functions and globals + // Compute a reference to the name. + Variable *var = arg->toNameProxy()->binding()->toVariable(); + __ note_position(node->pos()); + __ newref(var); + } else if (arg->isIndexExpression()) { + IndexExpression *expr = arg->toIndexExpression(); + expr->left()->accept(this); + expr->right()->accept(this); + __ note_position(expr->pos()); + __ bounds(); + __ arrayref_lifo(); + } else { + // We don't know how to, or can't, build a direct reference to + // this type. Make an indirect one, by taking the top of the + // stack and boxing it up. + arg->accept(this); + __ note_position(node->pos()); + __ slotref_lifo(); + } + } else { + arg->accept(this); + } + } + + __ note_position(node->pos()); + if (native) { + __ callnative(native->slot(), node->arguments()->length()); + __ drop(node->arguments()->length()); + } else { + node->callee()->accept(this); + __ call(funType); + __ drop(node->arguments()->length() + 1); + } +} + +void +BytecodeCompiler::visit(TernaryExpression *node) +{ + Label success, failure, join; + + visitForTest(node->condition(), &success, &failure, &success); + __ bind(&success); + node->left()->accept(this); + __ drop(1); + __ jump(&join); + __ bind(&failure); + node->right()->accept(this); + __ bind(&join); +} + +void +BytecodeCompiler::visit(IfStatement *node) +{ + Label join; + if (!node->ifFalse()) { + Label success; + + visitForTest(node->condition(), &success, &join, &success); + __ bind(&success); + node->ifTrue()->accept(this); + } else { + Label success, next; + + visitForTest(node->condition(), &success, &next, &success); + __ bind(&success); + node->ifTrue()->accept(this); + __ jump(&join); + __ bind(&next); + node->ifFalse()->accept(this); + } + + __ bind(&join); +} + +void +BytecodeCompiler::visit(FunctionStatement *node) +{ + if (node->fun()->isNative()) { + assert(!moduleData_->natives->at(node->fun()->slot())); + + Local type(zone_, node->fun()->type()); + Local name(zone_, node->fun()->name()); + Local native(zone_, Native::New(zone_, type, name)); + if (!native) + return; + + moduleData_->natives->set(zone_, node->fun()->slot(), native); + return; + } + + // If the function has no body, then it's just a declaration. + if (!node->body()) { + assert(node->fun()->isForward() || node->fun()->isPublic()); + return; + } + + BytecodeCompiler child(zone_, tree_, moduleData_); + Local fun(zone_, child.compileFunction(moduleData_, node)); + if (!fun) + return; + + assert(!moduleData_->module->getFunction(node->fun()->slot())); + moduleData_->module->setFunction(zone_, node->fun()->slot(), fun); +} + +void +BytecodeCompiler::visit(EnumStatement *node) +{ + // :TODO: eventually, we'll export these. +} + +Code * +BytecodeCompiler::compileCode(Scope *scope, unsigned lineno) +{ + // STOP doubles as RETURNVOID. + __ stop(); + + if (__ oom() || zone_->hasPendingException()) + return NULL; + + Local bytes(zone_, ByteArray::New(zone_, __ length() * sizeof(bytecode), Heap::Tenure_Old)); + if (!bytes) + return NULL; + memcpy(bytes->raw(), __ buffer(), __ length()); + + for (size_t i = 0; i < scope->maxContainedLocals(); i++) + assert(locals_->at(i)); + + 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()->at(i))); + + Local safepoints(zone_); + + // 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. + 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()->at(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 (__ sourcemapLength()) { + sourcemap = ByteArray::New(zone_, __ sourcemapLength(), Heap::Tenure_Old); + if (!sourcemap) + return NULL; + + memcpy(sourcemap->raw(), __ sourcemap(), __ sourcemapLength()); + } + + Local code(zone_); + + code = Code::New(zone_, + bytes, + locals_, + objects, + __ maxStackDepth(), + moduleData_->module, + safepoints, + sourcemap, + lineno); + return code; +} + +Function * +BytecodeCompiler::compileFunction(ModuleData *module, FunctionStatement *node) +{ + // Note: No need to record a life slot here, since it's part of the Frame. + AutoEnterScope functionScope(this, node->scope()); + + // Set the initial line, because line mappings are deltas. + __ setFirstLine(node->pos().line); + locals_ = FixedArray::New(zone_, node->scope()->maxContainedLocals(), Heap::Tenure_Old); + + node->body()->accept(this); + + unsigned nargs = node->scope()->numArguments(); + Local args(zone_, FixedArray::New(zone_, nargs, Heap::Tenure_Old)); + if (!args) + return NULL; + + for (size_t i = 0; i < node->scope()->numArguments(); i++) + args->set(zone_, i, node->scope()->getArgument(i)->type()); + + + Local code(zone_, compileCode(node->scope(), node->pos().line)); + if (!code) + return NULL; + + Local type(zone_, node->fun()->type()); + Local name(zone_, node->fun()->name()); + Local fun(zone_, Function::New(zone_, code, type, name, node->pos())); + if (!fun) + return NULL; + + // If this function had a forward, export it as a named public. + if (node->fun()->isPublic()) { + assert(node->fun()->isPublic()); + ScopedRoot root(fun); + if (!root) + return NULL; + if (!moduleData_->publics.append(root)) + return NULL; + } + + Disassemble(code, stdout); + + return fun; +} + +Module * +BytecodeCompiler::compile() +{ + AutoEnterScope scope(this, tree_->scope()); + + ModuleData moduleData; + + moduleData_ = &moduleData; + moduleData_->module = Module::New(zone_, tree_->scope()->numFunctions()); + + if (!moduleData_) + return NULL; + + moduleData_->natives = FixedArray::New(zone_, tree_->scope()->numNatives(), Heap::Tenure_Old); + if (!moduleData_->natives) + return NULL; + + for (size_t i = 0; i < tree_->statements()->length(); i++) { + Statement *statement = tree_->statements()->at(i); + statement->accept(this); + } + + if (zone_->hasPendingException()) + return NULL; + +#ifndef NDEBUG + for (size_t i = 0; i < tree_->scope()->numFunctions(); i++) + assert(moduleData_->module->getFunction(i)); + for (size_t i = 0; i < tree_->scope()->numNatives(); i++) + assert(moduleData_->natives->at(i)); +#endif + + if (moduleData_->publics.length()) { + Local fun(zone_); + Local list(zone_, FixedArray::New(zone_, moduleData_->publics.length(), + Heap::Tenure_Old)); + if (!list) + return NULL; + + for (size_t i = 0; i < moduleData_->publics.length(); i++) + list->set(zone_, i, moduleData_->publics[i]); + + moduleData_->module->setPublics(list); + } + + moduleData_->module->setNatives(moduleData_->natives); + + if (scope_->numEnvironmentVars()) { + Local globalMap(zone_, scope_->createEnvironmentMap(zone_)); + if (!globalMap) + return NULL; + + Local environment(zone_, Environment::New(zone_, globalMap)); + if (!environment) + return NULL; + + moduleData_->module->setEnvironment(environment); + } + + Local code(zone_, compileCode(tree_->scope(), 1)); + if (!code) + return NULL; + + Disassemble(code, stdout); + + moduleData_->module->setCode(code); + + return moduleData_->module; +}