dvander@28: /* vim: set ts=4 sw=4 tw=99 et: dvander@28: * 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 "BytecodeCompiler.h" dvander@0: #include "Scopes.h" dvander@0: #include "Modules.h" dvander@0: #include "Types.h" dvander@12: #include "Array.h" dvander@54: #include "Strings.h" dvander@12: #include "Zone.h" dvander@12: #include "Heap-inl.h" dvander@0: dvander@59: using namespace ke; dvander@0: dvander@0: typedef BytecodeEmitter::Label Label; dvander@0: dvander@0: #define __ emitter_. dvander@0: dvander@65: BytecodeCompiler::BytecodeCompiler(Zone *zone, ParseTree *tree, ModuleData *module) dvander@30: : zone_(zone), dvander@30: tree_(tree), dvander@47: emitter_(zone->pool()), dvander@65: moduleData_(module), dvander@28: locals_(NULL), dvander@10: scope_(NULL), dvander@28: loop_(NULL) dvander@0: { dvander@0: } dvander@0: dvander@0: static inline TokenKind dvander@0: ToCompare(Expression *expr) dvander@0: { dvander@0: if (expr->isBinaryExpression()) { dvander@0: BinaryExpression *b = expr->toBinaryExpression(); dvander@0: if (b->token() >= TOK_EQUALS && b->token() <= TOK_GE) dvander@0: return b->token(); dvander@0: } dvander@0: return TOK_EOF; dvander@0: } dvander@0: dvander@0: static inline Opcode dvander@0: CompareTokenToJump(TokenKind kind) dvander@0: { dvander@0: switch (kind) { dvander@0: case TOK_EQUALS: dvander@0: return OP_JEQ; dvander@0: case TOK_NOTEQUALS: dvander@0: return OP_JNE; dvander@0: case TOK_GT: dvander@0: return OP_JG; dvander@0: case TOK_GE: dvander@0: return OP_JGE; dvander@0: case TOK_LT: dvander@0: return OP_JL; dvander@28: default: dvander@28: assert(kind == TOK_LE); dvander@0: return OP_JLE; dvander@0: } dvander@0: } dvander@0: dvander@0: static inline Opcode dvander@0: TokenToOpcode(TokenKind kind) dvander@0: { dvander@0: switch (kind) { dvander@0: case TOK_PLUS: dvander@0: return OP_ADD; dvander@0: case TOK_MINUS: dvander@0: return OP_SUB; dvander@0: case TOK_STAR: dvander@0: return OP_MUL; dvander@0: case TOK_SLASH: dvander@0: return OP_DIV; dvander@8: case TOK_SHR: dvander@8: return OP_SHR; dvander@8: case TOK_USHR: dvander@8: return OP_USHR; dvander@8: case TOK_SHL: dvander@8: return OP_SHL; dvander@8: case TOK_BITAND: dvander@8: return OP_BITAND; dvander@8: case TOK_BITOR: dvander@8: return OP_BITOR; dvander@28: default: dvander@28: assert(kind == TOK_BITXOR); dvander@8: return OP_BITXOR; dvander@0: } dvander@0: } dvander@0: dvander@0: void dvander@12: BytecodeCompiler::recordLifoSlot(Scope *scope) dvander@1: { dvander@1: if (!scope) dvander@1: return; dvander@1: dvander@12: if (Variable *var = scope->lifoSlot()) { dvander@30: locals_->set(zone_, var->slot(), var->type()); dvander@12: __ savelifo(var->slot()); dvander@12: } dvander@1: } dvander@1: dvander@1: void dvander@10: BytecodeCompiler::unwindTo(Scope *scope) dvander@10: { dvander@12: // Note: we only need to unwind to the outermost scope's LIFO slot. dvander@12: Variable *var = NULL; dvander@10: for (Scope *current = scope_; current != scope; current = current->enclosing()) { dvander@12: if (Variable *slot = current->lifoSlot()) dvander@12: var = slot; dvander@10: } dvander@12: dvander@12: if (var) dvander@12: __ unwindlifo(var->slot()); dvander@10: } dvander@10: dvander@10: void dvander@0: BytecodeCompiler::visit(NameProxy *name) dvander@0: { dvander@2: switch (name->binding()->kind()) { dvander@2: case BoundName::VARIABLE: dvander@2: { dvander@2: Variable *var = name->binding()->toVariable(); dvander@0: dvander@2: if (var->location() == Variable::STACK) { dvander@47: __ getlocal(var); dvander@14: assert(!name->type()->isReference()); dvander@2: } else { dvander@2: assert(var->location() == Variable::PARAMETER); dvander@47: __ getarg(var); dvander@14: dvander@14: // If this argument is actually a reference, dereference it now. dvander@22: if (var->type()->isReference()) dvander@22: __ getref(var->type()->contained()->primitive()); dvander@2: } dvander@2: break; dvander@2: } dvander@2: dvander@2: default: dvander@2: { dvander@2: NamedConstant *constant = name->binding()->toNamedConstant(); dvander@2: __ int_(constant->value().toInt()); dvander@2: break; dvander@2: } dvander@0: } dvander@0: } dvander@0: dvander@0: void dvander@0: BytecodeCompiler::visit(ExpressionStatement *node) dvander@0: { dvander@0: // Expressions leave their value on the top of the stack, so we make sure dvander@0: // to balance the stack if they're used as a statement. dvander@0: node->expression()->accept(this); dvander@0: __ pop(); dvander@0: } dvander@0: dvander@0: void dvander@0: BytecodeCompiler::visit(Assignment *node) dvander@0: { dvander@22: // The first step is three sub-steps: dvander@22: // dvander@22: // (1) Bind the left-hand side. dvander@22: // -- If a cumulative operation (+=, etc): dvander@22: // (2) Save a copy of the binding on the stack. dvander@22: // (3) Dereference the binding to get a value. dvander@22: // dvander@0: if (node->lvalue()->isNameProxy()) { dvander@0: NameProxy *proxy = node->lvalue()->toNameProxy(); dvander@0: Variable *var = proxy->binding()->toVariable(); dvander@0: dvander@22: // Bind. dvander@21: if (var->type()->isReference() || var->type()->isArray()) dvander@16: __ get(var); dvander@14: dvander@22: // Get the value if needed. dvander@22: if (node->token() != TOK_ASSIGN) { dvander@22: // += etc is not allowed on arrays. dvander@22: assert(!var->type()->isArray()); dvander@22: dvander@22: if (var->type()->isReference()) { dvander@22: __ dup(); dvander@22: __ getref(var->type()->contained()->primitive()); dvander@22: } else { dvander@22: __ get(var); dvander@22: } dvander@22: } dvander@23: } else { dvander@21: IndexExpression *index = node->lvalue()->toIndexExpression(); dvander@21: dvander@21: // Note: Eventually, watch out for a whole here. An expression like: dvander@21: // dvander@21: // (a[4] = (5, a = [])) dvander@21: // dvander@21: // Could shrink array, so we need to either bind a[i] to a Reference, dvander@21: // or keep the bounds check near the set. This is just a semantic dvander@21: // decision. dvander@21: index->left()->accept(this); dvander@21: index->right()->accept(this); dvander@22: dvander@22: if (node->token() != TOK_ASSIGN) { dvander@22: __ dup2(); dvander@22: __ bounds(); dvander@22: if (node->type()->isArray()) dvander@22: __ getarray_o(); dvander@22: else dvander@22: __ getarray(node->type()->primitive()); dvander@22: } dvander@22: } dvander@22: dvander@22: if (node->token() == TOK_ASSIGN) { dvander@21: node->expression()->accept(this); dvander@22: } else { dvander@22: visitForALU(node->type(), node->expression()); dvander@22: dvander@22: TokenKind alu = TokenKind(int(node->token()) - (TOK_ASSIGN_ADD - TOK_PLUS)); dvander@22: __ binary(TokenToOpcode(alu)); dvander@21: } dvander@21: dvander@21: // Emit the store. dvander@21: if (node->expression()->type()->isArray()) { dvander@21: // Unlike references, which can only be computed from a NamePRoxy, dvander@21: // arrays can be computed from multiple lvalue types. dvander@21: __ arraycopy(); dvander@21: } else if (node->lvalue()->isNameProxy()) { dvander@21: NameProxy *proxy = node->lvalue()->toNameProxy(); dvander@21: Variable *var = proxy->binding()->toVariable(); dvander@14: dvander@22: if (var->type()->isReference()) dvander@22: __ setref(var->type()->contained()->primitive()); dvander@22: else dvander@16: __ set(var); dvander@21: } else { dvander@0: IndexExpression *index = node->lvalue()->toIndexExpression(); dvander@23: // :TODO: get rid of the BOUNDS instruction, embed it in g/setarray*? dvander@22: // We can't emit it here because the stack is slightly out of position. dvander@22: // __ bounds(); dvander@21: __ setarray(index->left()->type()->contained()->primitive()); dvander@0: } dvander@0: } dvander@0: dvander@0: void dvander@23: BytecodeCompiler::visit(IncDecExpression *node) dvander@23: { dvander@23: // In the postfix case, the logic looks like: dvander@23: // (1) Bind -> REF dvander@23: // (2) Dup -> REF REF dvander@23: // (3) Fetch -> REF VAL dvander@23: // (4) Swap -> VAL REF dvander@23: // (5) Pick -> VAL REF VAL dvander@23: // (6) Add -> VAL REF VAL+1 dvander@23: // (7) Store -> VAL VAL+1 dvander@23: // (8) Drop -> VAL dvander@23: // dvander@23: // In the prefix case, the logic looks like: dvander@23: // (1) Bind -> REF dvander@23: // (2) Dup -> REF REF dvander@23: // (3) Fetch -> REF VAL dvander@23: // -- dvander@23: // -- dvander@23: // (6) Add -> REF VAL+1 dvander@23: // (7) Store -> VAL+1 dvander@23: // -- dvander@23: // dvander@23: // This is squirrely enough such that we don't bother sharing too much dvander@23: // with the above. dvander@23: // dvander@23: // Note: We simulate a "Pick" operation with a dup2+pop. dvander@23: dvander@23: // This is steps (1) to (5). dvander@23: Expression *expr = node->expression(); dvander@23: if (expr->isNameProxy()) { dvander@23: NameProxy *proxy = expr->toNameProxy(); dvander@23: Variable *var = proxy->binding()->toVariable(); dvander@23: dvander@23: // Bind. dvander@23: if (var->type()->isReference() || var->type()->isArray()) dvander@23: __ get(var); dvander@23: dvander@23: // Dup and fetch. dvander@23: if (var->type()->isReference()) { dvander@23: __ dup(); dvander@23: __ getref(node->type()->primitive()); dvander@23: if (node->postfix()) { dvander@23: __ swap(); dvander@23: __ pick2(); dvander@23: } dvander@23: } else { dvander@23: assert(!var->type()->isArray()); dvander@23: __ get(var); dvander@23: if (node->postfix()) dvander@23: __ dup(); dvander@23: } dvander@23: } else { dvander@23: IndexExpression *index = expr->toIndexExpression(); dvander@23: index->left()->accept(this); dvander@23: index->right()->accept(this); dvander@23: dvander@23: assert(!node->type()->isArray()); dvander@23: __ bounds(); dvander@23: __ dup2(); dvander@23: __ getarray(node->type()->primitive()); dvander@23: dvander@23: if (node->postfix()) { dvander@23: // The stack looks like this now: dvander@23: // ARRAY INDEX VALUE dvander@23: // dvander@23: // So we must use a PICK operation to rotate the stack into position. dvander@23: __ roll3(); // INDEX VALUE ARRAY dvander@23: __ roll3(); // VALUE ARRAY INDEX dvander@23: __ pick3(); // VALUE ARRAY INDEX VALUE dvander@23: } dvander@23: } dvander@23: dvander@23: // Perform step (6). dvander@23: if (node->type()->isFloat()) { dvander@23: __ float_(1.0); dvander@23: } else { dvander@23: assert(node->type()->isInt32()); dvander@23: __ int_(1); dvander@23: } dvander@23: dvander@23: if (node->token() == TOK_INCREMENT) dvander@23: __ binary(OP_ADD); dvander@23: else dvander@23: __ binary(OP_SUB); dvander@23: dvander@23: // Perform step (7). dvander@23: if (expr->isNameProxy()) { dvander@23: NameProxy *proxy = expr->toNameProxy(); dvander@23: Variable *var = proxy->binding()->toVariable(); dvander@23: dvander@23: if (var->type()->isReference()) dvander@23: __ setref(node->type()->primitive()); dvander@23: else dvander@23: __ set(var); dvander@23: } else { dvander@23: __ setarray(node->type()->primitive()); dvander@23: } dvander@23: dvander@23: // Perform step (8). dvander@23: if (node->postfix()) dvander@23: __ pop(); dvander@23: } dvander@23: dvander@23: void dvander@4: BytecodeCompiler::visitForALU(Type *needed, Expression *node) dvander@4: { dvander@4: node->accept(this); dvander@4: if (needed->isFloat() && !node->type()->isFloat()) dvander@4: __ cvt_i2f(); dvander@22: else dvander@22: assert(needed->primitive() == node->type()->primitive()); dvander@4: } dvander@4: dvander@4: void dvander@0: BytecodeCompiler::visit(BinaryExpression *node) dvander@0: { dvander@4: visitForALU(node->type(), node->left()); dvander@4: visitForALU(node->type(), node->right()); dvander@0: dvander@0: Opcode op = TokenToOpcode(node->token()); dvander@4: if (node->type()->isFloat()) dvander@4: op = (Opcode)(int(op) + (OP_ADD_F - OP_ADD)); dvander@0: __ binary(op); dvander@0: } dvander@0: dvander@0: void dvander@0: BytecodeCompiler::visit(IndexExpression *node) dvander@0: { dvander@0: node->left()->accept(this); dvander@0: node->right()->accept(this); dvander@19: __ bounds(); dvander@0: dvander@21: if (node->type()->isArray()) dvander@19: __ getarray_o(); dvander@21: else dvander@21: __ getarray(node->type()->primitive()); dvander@0: } dvander@0: dvander@0: void dvander@0: BytecodeCompiler::visit(ReturnStatement *node) dvander@0: { dvander@0: if (node->expression()) dvander@0: node->expression()->accept(this); dvander@0: else dvander@0: __ int_(0); dvander@0: dvander@0: __ return_(); dvander@0: } dvander@0: dvander@0: void dvander@0: BytecodeCompiler::visitForTest(Expression *expression, Label *label, bool onTrue) dvander@0: { dvander@0: TokenKind token = ToCompare(expression); dvander@0: if (token == TOK_EOF) { dvander@0: expression->accept(this); dvander@0: if (onTrue) dvander@0: __ j(OP_JT, label); dvander@0: else dvander@0: __ j(OP_JF, label); dvander@0: return; dvander@0: } dvander@0: dvander@0: Opcode op = CompareTokenToJump(token); dvander@0: if (!onTrue) dvander@0: op = Invert(op); dvander@0: dvander@0: assert(expression->isBinaryExpression()); dvander@0: BinaryExpression *binary = expression->toBinaryExpression(); dvander@0: binary->left()->accept(this); dvander@0: binary->right()->accept(this); dvander@0: __ j(op, label); dvander@0: } dvander@0: dvander@0: void dvander@0: BytecodeCompiler::visit(ForStatement *node) dvander@0: { dvander@1: AutoEnterScope forScope(this, node->scope()); dvander@1: dvander@12: recordLifoSlot(node->scope()); dvander@12: dvander@10: Label join, update; dvander@10: LoopScope loop(scope_, &join, &update, &loop_); dvander@10: dvander@22: if (node->initialization()) dvander@22: node->initialization()->accept(this); dvander@0: dvander@0: Label header, test; dvander@0: if (node->condition()) dvander@0: __ jump(&test); dvander@0: dvander@0: __ bind(&header); dvander@0: node->body()->accept(this); dvander@0: dvander@10: __ bind(&update); dvander@0: if (node->update()) dvander@0: node->update()->accept(this); dvander@0: dvander@0: if (node->condition()) { dvander@0: __ bind(&test); dvander@0: visitForTest(node->condition(), &header, true); dvander@0: } else { dvander@0: __ jump(&header); dvander@0: } dvander@0: dvander@10: __ bind(&join); dvander@10: dvander@12: if (node->scope()) dvander@12: unwindTo(node->scope()->enclosing()); dvander@0: assert(__ stackDepth() == 0); dvander@0: } dvander@0: dvander@0: void dvander@9: BytecodeCompiler::visit(WhileStatement *node) dvander@9: { dvander@10: Label header, join; dvander@9: dvander@9: __ bind(&header); dvander@9: dvander@10: LoopScope loop(scope_, &join, &header, &loop_); dvander@10: dvander@9: if (node->token() == TOK_WHILE) { dvander@9: visitForTest(node->condition(), &join, false); dvander@9: node->body()->accept(this); dvander@9: __ jump(&header); dvander@9: } else { dvander@9: node->body()->accept(this); dvander@9: visitForTest(node->condition(), &header, true); dvander@9: } dvander@10: dvander@10: __ bind(&join); dvander@10: } dvander@10: dvander@10: void dvander@10: BytecodeCompiler::visit(BreakStatement *node) dvander@10: { dvander@10: unwindTo(loop_->scope()); dvander@10: __ jump(loop_->break_()); dvander@10: } dvander@10: dvander@10: void dvander@10: BytecodeCompiler::visit(ContinueStatement *node) dvander@10: { dvander@10: unwindTo(loop_->scope()); dvander@10: __ jump(loop_->continue_()); dvander@9: } dvander@9: dvander@9: void dvander@0: BytecodeCompiler::visit(BlockStatement *node) dvander@0: { dvander@1: AutoEnterScope blockScope(this, node->scope()); dvander@1: dvander@12: recordLifoSlot(node->scope()); dvander@12: dvander@0: for (size_t i = 0; i < node->statements()->length(); i++) { dvander@0: Statement *statement = node->statements()->at(i); dvander@0: statement->accept(this); dvander@0: assert(__ stackDepth() == 0); dvander@0: } dvander@1: dvander@1: // We don't bother emitting an "unwind" instruction for function scopes, dvander@1: // since this is taken care of by the calling convention. dvander@1: if (node->scope() && node->scope()->enclosing()->kind() != Scope::FUNCTION) dvander@12: unwindTo(node->scope()->enclosing()); dvander@0: } dvander@0: dvander@0: void dvander@4: BytecodeCompiler::visit(IntegerLiteral *node) dvander@0: { dvander@0: __ int_(node->value()); dvander@0: } dvander@0: dvander@0: void dvander@4: BytecodeCompiler::visit(FloatLiteral *node) dvander@4: { dvander@4: __ float_((float)node->value()); dvander@4: } dvander@4: dvander@12: static inline ArrayMap * dvander@27: BuildArrayMap(Zone *zone, Handle type) dvander@12: { dvander@27: Local contained(zone); dvander@12: if (type->levels() > 1) { dvander@27: Local containedType(zone, type->contained()); dvander@27: contained = BuildArrayMap(zone, containedType); dvander@12: if (!contained) dvander@12: return NULL; dvander@12: } dvander@12: dvander@12: return ArrayMap::New(zone, type, contained); dvander@12: } dvander@12: dvander@4: void dvander@21: BytecodeCompiler::visit(StringLiteral *node) dvander@21: { dvander@30: Local type(zone_, node->type()); dvander@30: Local map(zone_, BuildArrayMap(zone_, type)); dvander@21: if (!map) dvander@21: return; dvander@21: dvander@30: Local array(zone_, Array::New(zone_, map, node->string()->length(), Heap::Tenure_Old)); dvander@21: if (!array) dvander@21: return; dvander@21: dvander@21: assert(array->nelements() >= node->string()->length()); dvander@21: memcpy(array->chars(), node->string()->raw(), node->string()->length() * sizeof(char)); dvander@21: dvander@21: __ object(array); dvander@21: } dvander@21: dvander@21: void dvander@0: BytecodeCompiler::visit(VariableDeclaration *node) dvander@0: { dvander@0: Variable *var = node->variable(); dvander@0: assert(var->location() == Variable::STACK); dvander@0: dvander@30: Local type(zone_, var->type()); dvander@0: if (type->isArray()) { dvander@30: Local map(zone_, BuildArrayMap(zone_, type)); dvander@19: __ newarray_lifo(map); dvander@21: dvander@21: if (node->initialization()) { dvander@21: node->initialization()->accept(this); dvander@21: __ arraycopy(); dvander@21: } dvander@0: } else { dvander@0: if (node->initialization()) { dvander@0: node->initialization()->accept(this); dvander@0: } else { dvander@0: if (!node->postDimensions()) dvander@0: __ int_(0); dvander@0: } dvander@0: } dvander@47: __ setlocal(var); dvander@0: __ pop(); dvander@0: dvander@30: locals_->set(zone_, var->slot(), var->type()); dvander@0: } dvander@0: dvander@0: void dvander@0: BytecodeCompiler::visit(CallExpression *node) dvander@0: { dvander@32: Local funType(zone_, node->callee()->type()); dvander@14: dvander@55: for (size_t i = 0; i < node->arguments()->length(); i++) { dvander@0: Expression *arg = node->arguments()->at(i); dvander@14: dvander@32: Local given(zone_, arg->type()); dvander@32: Local actual(zone_, Type::cast(funType->parameters()->at(i))); dvander@14: if (actual->isReference()) { dvander@14: if (given->isReference()) { dvander@14: // The input type is already a reference, so we're just passing dvander@14: // it on to the callee. dvander@14: arg->accept(this); dvander@14: } else if (arg->isNameProxy()) { dvander@14: // :TODO: test with functions and globals dvander@14: // Compute a reference to the name. dvander@14: Variable *var = arg->toNameProxy()->binding()->toVariable(); dvander@14: __ newref(var); dvander@17: } else if (arg->isIndexExpression()) { dvander@17: IndexExpression *expr = arg->toIndexExpression(); dvander@17: expr->left()->accept(this); dvander@17: expr->right()->accept(this); dvander@19: __ bounds(); dvander@17: __ arrayref_lifo(); dvander@14: } else { dvander@14: // We don't know how to, or can't, build a direct reference to dvander@14: // this type. Make an indirect one, by taking the top of the dvander@14: // stack and boxing it up. dvander@16: arg->accept(this); dvander@14: __ slotref_lifo(); dvander@14: } dvander@14: } else { dvander@14: arg->accept(this); dvander@14: } dvander@0: } dvander@0: dvander@46: __ callglobal(node->callee()->toNameProxy()->binding()->toNamedFunction()->slot()); dvander@55: __ drop(node->arguments()->length()); dvander@0: } dvander@0: dvander@0: void dvander@0: BytecodeCompiler::visit(IfStatement *node) dvander@0: { dvander@0: if (!node->ifFalse()) { dvander@0: Label fail; dvander@0: visitForTest(node->condition(), &fail, false); dvander@0: node->ifTrue()->accept(this); dvander@0: __ bind(&fail); dvander@0: } else { dvander@0: Label fail, join; dvander@0: visitForTest(node->condition(), &fail, false); dvander@0: node->ifTrue()->accept(this); dvander@0: __ jump(&join); dvander@0: __ bind(&fail); dvander@0: node->ifFalse()->accept(this); dvander@0: __ bind(&join); dvander@0: } dvander@0: } dvander@0: dvander@0: void dvander@0: BytecodeCompiler::visit(FunctionStatement *node) dvander@0: { dvander@63: // If the function has no body, then it's just a declaration. dvander@63: if (!node->body()) { dvander@63: assert(node->fun()->isForward() || node->fun()->isPublic()); dvander@63: return; dvander@63: } dvander@63: dvander@65: BytecodeCompiler child(zone_, tree_, moduleData_); dvander@65: Local fun(zone_, child.compileFunction(moduleData_, node)); dvander@26: if (!fun) dvander@26: return; dvander@0: dvander@65: assert(!moduleData_->module->getFunction(node->fun()->slot())); dvander@65: moduleData_->module->setFunction(zone_, node->fun()->slot(), fun); dvander@0: } dvander@0: dvander@2: void dvander@2: BytecodeCompiler::visit(EnumStatement *node) dvander@2: { dvander@2: // :TODO: eventually, we'll export these. dvander@2: } dvander@2: dvander@26: Function * dvander@65: BytecodeCompiler::compileFunction(ModuleData *module, FunctionStatement *node) dvander@0: { dvander@12: // Note: No need to record a life slot here, since it's part of the Frame. dvander@1: AutoEnterScope functionScope(this, node->scope()); dvander@1: dvander@30: locals_ = FixedArray::New(zone_, node->scope()->maxContainedLocals(), Heap::Tenure_Old); dvander@0: dvander@0: node->body()->accept(this); dvander@0: dvander@0: __ stop(); dvander@0: dvander@0: if (__ oom()) dvander@12: return NULL; dvander@0: dvander@32: Local bytes(zone_, ByteArray::New(zone_, __ length() * sizeof(bytecode), Heap::Tenure_Old)); dvander@12: if (!bytes) dvander@12: return NULL; dvander@12: memcpy(bytes->raw(), __ buffer(), __ length()); dvander@0: dvander@0: unsigned nargs = node->scope()->numArguments(); dvander@32: Local args(zone_, FixedArray::New(zone_, nargs, Heap::Tenure_Old)); dvander@12: if (!args) dvander@12: return NULL; dvander@12: dvander@0: for (size_t i = 0; i < node->scope()->numArguments(); i++) dvander@30: args->set(zone_, i, node->scope()->getArgument(i)->type()); dvander@0: dvander@0: for (size_t i = 0; i < node->scope()->maxContainedLocals(); i++) dvander@12: assert(locals_->at(i)); dvander@0: dvander@58: Local objects(zone_, FixedArray::New(zone_, __ objects()->length(), Heap::Tenure_Old)); dvander@21: if (!objects) dvander@12: return NULL; dvander@0: dvander@58: for (size_t i = 0; i < __ objects()->length(); i++) dvander@33: objects->set(zone_, i, *(__ objects()->at(i))); dvander@12: dvander@47: Local safepoints(zone_); dvander@47: dvander@47: // If any stack slots hold gc pointers at potential failure points in the dvander@47: // bytecode, record them now. We do this by building a list of tuples; dvander@47: // each tuple contains the pc offset, and a bitset containing which stack dvander@47: // slots must be scanned by the gc. dvander@58: if (__ oopRecords()->length()) { dvander@47: size_t bytesPerSafepoint = Code::BytesPerSafepoint(__ maxStackDepth()); dvander@58: size_t gcmapSize = bytesPerSafepoint * __ oopRecords()->length(); dvander@47: safepoints = ByteArray::New(zone_, gcmapSize, Heap::Tenure_Old); dvander@47: if (!safepoints) dvander@47: return NULL; dvander@47: dvander@47: Address cursor = safepoints->raw(); dvander@58: for (size_t i = 0; i < __ oopRecords()->length(); i++) { dvander@47: OopRecord *record = __ oopRecords()->at(i); dvander@47: assert(record->depth <= __ maxStackDepth()); dvander@47: dvander@47: *reinterpret_cast(cursor) = record->offset; dvander@47: dvander@47: unsigned char *bits = reinterpret_cast(cursor + sizeof(unsigned)); dvander@47: for (unsigned i = 0; i < record->depth; i++) dvander@47: bits[i / 8] |= (1 << (i % 8)); dvander@47: dvander@47: cursor += bytesPerSafepoint; dvander@47: } dvander@47: dvander@47: assert(cursor == safepoints->raw() + gcmapSize); dvander@47: } dvander@47: dvander@65: Local code(zone_, Code::New(zone_, bytes, locals_, objects, __ maxStackDepth(), dvander@65: moduleData_->module, safepoints)); dvander@26: if (!code) dvander@26: return NULL; dvander@26: dvander@64: Local type(zone_, node->fun()->type()); dvander@64: Local name(zone_, node->fun()->name()); dvander@64: Local fun(zone_, Function::New(zone_, code, type, name, node->pos())); dvander@26: if (!fun) dvander@26: return NULL; dvander@64: dvander@64: // If this function had a forward, export it as a named public. dvander@64: if (node->fun()->isPublic()) { dvander@64: assert(node->fun()->isPublic()); dvander@64: ScopedRoot root(fun); dvander@64: if (!root) dvander@64: return NULL; dvander@65: if (!moduleData_->publics.append(root)) dvander@64: return NULL; dvander@64: } dvander@66: dvander@66: Disassemble(code, stdout); dvander@26: dvander@26: return fun; dvander@0: } dvander@0: dvander@0: Module * dvander@0: BytecodeCompiler::compile() dvander@0: { dvander@65: ModuleData moduleData; dvander@65: dvander@65: moduleData_ = &moduleData; dvander@65: moduleData_->module = Module::New(zone_, tree_->scope()->numFunctions()); dvander@65: dvander@65: if (!moduleData_) dvander@14: return NULL; dvander@0: dvander@0: for (size_t i = 0; i < tree_->statements()->length(); i++) { dvander@0: Statement *statement = tree_->statements()->at(i); dvander@0: statement->accept(this); dvander@0: } dvander@0: dvander@0: #ifndef NDEBUG dvander@0: for (size_t i = 0; i < tree_->scope()->numFunctions(); i++) dvander@65: assert(moduleData_->module->getFunction(i)); dvander@0: #endif dvander@0: dvander@65: if (moduleData_->publics.length()) { dvander@64: Local fun(zone_); dvander@65: Local list(zone_, FixedArray::New(zone_, moduleData_->publics.length(), dvander@65: Heap::Tenure_Old)); dvander@64: if (!list) dvander@64: return NULL; dvander@64: dvander@65: for (size_t i = 0; i < moduleData_->publics.length(); i++) dvander@65: list->set(zone_, i, moduleData_->publics[i]); dvander@65: dvander@65: moduleData_->module->setPublics(list); dvander@64: } dvander@64: dvander@65: return moduleData_->module; dvander@0: }