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: */ danderson@92: #include 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@85: #include "Environments.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@28: locals_(NULL), dvander@10: scope_(NULL), danderson@91: moduleData_(module), 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@75: if (b->token() >= TOK_EQUALS && b->token() <= TOK_OR) dvander@0: return b->token(); dvander@79: } else if (expr->isUnaryExpression()) { dvander@79: UnaryExpression *u = expr->toUnaryExpression(); dvander@79: if (u->token() == TOK_NOT) dvander@79: return TOK_NOT; 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@82: case TOK_PERCENT: dvander@82: return OP_MOD; 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@74: case TOK_GT: dvander@74: return OP_GT; dvander@74: case TOK_GE: dvander@74: return OP_GE; dvander@74: case TOK_LT: dvander@74: return OP_LT; dvander@74: case TOK_LE: dvander@74: return OP_LE; dvander@74: case TOK_EQUALS: dvander@74: return OP_EQ; dvander@74: case TOK_NOTEQUALS: dvander@74: return OP_NE; 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@85: } else if (var->location() == Variable::ENVIRONMENT) { dvander@85: getEnvSlot(var); 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@73: case BoundName::FUNCTION: dvander@73: { dvander@73: NamedFunction *fun = name->binding()->toNamedFunction(); dvander@73: __ module(); dvander@73: __ getmodule_fn(fun->slot()); dvander@73: break; dvander@73: } dvander@73: 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@72: if (!node->expression()->type()->isVoid()) dvander@72: __ 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@70: __ note_position(index->pos()); 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@70: __ note_position(node->pos()); 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@70: __ note_position(node->pos()); 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@70: __ note_position(index->pos()); dvander@70: 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@70: __ note_position(node->pos()); 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@74: static inline Type * dvander@74: GetCoercionType(TokenKind tok, Type *left, Type *right) dvander@74: { dvander@74: if (left->isFloat()) dvander@74: return left; dvander@74: return right; dvander@74: } dvander@74: dvander@4: void dvander@78: BytecodeCompiler::visit(UnaryExpression *node) dvander@78: { dvander@78: switch (node->token()) { dvander@80: case TOK_MINUS: dvander@80: { dvander@80: Local type(zone_, node->expression()->type()); dvander@80: node->expression()->accept(this); dvander@80: dvander@80: if (node->type()->isFloat()) dvander@80: __ neg_f(); dvander@80: else dvander@80: __ neg(); dvander@80: break; dvander@80: } dvander@80: dvander@79: case TOK_NOT: dvander@79: { dvander@79: Local type(zone_, node->expression()->type()); dvander@79: node->expression()->accept(this); dvander@79: dvander@79: if (node->type()->isFloat()) dvander@79: __ not_f(); dvander@79: else dvander@79: __ not_(); dvander@79: break; dvander@79: } dvander@79: dvander@142: case TOK_LABEL: dvander@142: { dvander@142: Local from(zone_, node->expression()->type()); dvander@142: Local to(zone_, node->type()); dvander@142: dvander@142: node->expression()->accept(this); dvander@142: if (!from->isFloat() && to->isFloat()) dvander@142: __ bits_f2i(); dvander@142: else if (from->isFloat() && !to->isFloat()) dvander@142: __ bits_i2f(); dvander@142: break; dvander@142: } dvander@142: dvander@78: default: dvander@78: assert(node->token() == TOK_TILDE); dvander@78: node->expression()->accept(this); dvander@78: __ bitnot(); dvander@78: break; dvander@78: } dvander@78: } dvander@78: dvander@78: void dvander@0: BytecodeCompiler::visit(BinaryExpression *node) dvander@0: { dvander@75: if (node->token() == TOK_AND || node->token() == TOK_OR) { dvander@75: Label success, failure, done; dvander@75: visitForTest(node, &success, &failure, &success); dvander@75: __ bind(&success); dvander@75: __ int_(1); dvander@75: __ drop(1); // Otherwise, the stack depth will be +1 too much. dvander@75: __ jump(&done); dvander@75: __ bind(&failure); dvander@75: __ int_(0); dvander@75: __ bind(&done); dvander@75: return; dvander@75: } dvander@75: dvander@74: Local leftType(zone_, node->left()->type()); dvander@74: Local rightType(zone_, node->right()->type()); dvander@74: Local coerce(zone_, GetCoercionType(node->token(), leftType, rightType)); dvander@74: dvander@74: visitForALU(coerce, node->left()); dvander@74: visitForALU(coerce, 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@70: dvander@70: __ note_position(node->pos()); 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@70: __ note_position(node->pos()); 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@72: if (node->expression()) { dvander@0: node->expression()->accept(this); dvander@72: __ return_(); dvander@72: } else { dvander@72: __ returnvoid(); dvander@72: } dvander@0: } dvander@0: dvander@0: void dvander@75: BytecodeCompiler::visitForTest(Expression *expression, dvander@75: Label *trueBranch, dvander@75: Label *falseBranch, dvander@75: Label *fallthrough) dvander@0: { dvander@0: TokenKind token = ToCompare(expression); dvander@79: if (token == TOK_EOF || token == TOK_NOT) { dvander@79: if (token == TOK_EOF) dvander@79: expression->accept(this); dvander@79: else dvander@79: expression->toUnaryExpression()->expression()->accept(this); dvander@79: dvander@79: bool jumpOnTrue = (fallthrough == falseBranch) ^ (token == TOK_NOT); dvander@74: dvander@79: Opcode op = jumpOnTrue ? OP_JT : OP_JF; dvander@74: if (expression->type()->isFloat()) dvander@74: op = (Opcode)(int(op) + (OP_JL_F - OP_JL)); dvander@74: dvander@79: if (fallthrough == falseBranch) dvander@79: __ j(op, trueBranch); dvander@75: else dvander@79: __ j(op, falseBranch); dvander@75: return; dvander@75: } dvander@75: dvander@75: BinaryExpression *binary = expression->toBinaryExpression(); dvander@75: dvander@75: if (token == TOK_AND || token == TOK_OR) { dvander@75: Label next; dvander@75: if (token == TOK_AND) dvander@75: visitForTest(binary->left(), &next, falseBranch, &next); dvander@75: else dvander@75: visitForTest(binary->left(), trueBranch, &next, &next); dvander@75: __ bind(&next); dvander@75: visitForTest(binary->right(), trueBranch, falseBranch, fallthrough); dvander@0: return; dvander@0: } dvander@0: dvander@0: Opcode op = CompareTokenToJump(token); dvander@75: if (fallthrough == trueBranch) dvander@0: op = Invert(op); dvander@0: dvander@74: Local leftType(zone_, binary->left()->type()); dvander@74: Local rightType(zone_, binary->right()->type()); dvander@74: Local coerce(zone_, GetCoercionType(binary->token(), leftType, rightType)); dvander@74: dvander@74: visitForALU(coerce, binary->left()); dvander@74: visitForALU(coerce, binary->right()); dvander@74: if (coerce->isFloat()) dvander@74: op = (Opcode)(int(op) + (OP_JL_F - OP_JL)); dvander@74: dvander@75: if (fallthrough == trueBranch) dvander@75: __ j(op, falseBranch); dvander@75: else dvander@75: __ j(op, trueBranch); 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@75: visitForTest(node->condition(), &header, &join, &join); 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@75: Label body; dvander@75: dvander@75: visitForTest(node->condition(), &body, &join, &body); dvander@75: __ bind(&body); dvander@9: node->body()->accept(this); dvander@9: __ jump(&header); dvander@9: } else { dvander@9: node->body()->accept(this); dvander@75: visitForTest(node->condition(), &header, &join, &join); 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@70: __ note_position(node->pos()); dvander@21: __ object(array); dvander@21: } dvander@21: dvander@21: void dvander@85: BytecodeCompiler::getEnvSlot(Variable *var) dvander@85: { dvander@85: assert(var->scope()->kind() == Scope::GLOBAL); dvander@85: __ getglobal(var); dvander@85: } dvander@85: dvander@85: void dvander@85: BytecodeCompiler::setEnvSlot(Variable *var) dvander@85: { dvander@85: assert(var->scope()->kind() == Scope::GLOBAL); dvander@85: __ setglobal(var); dvander@85: } dvander@85: dvander@85: void dvander@0: BytecodeCompiler::visit(VariableDeclaration *node) dvander@0: { dvander@0: Variable *var = node->variable(); dvander@0: dvander@30: Local type(zone_, var->type()); dvander@0: if (type->isArray()) { dvander@30: Local map(zone_, BuildArrayMap(zone_, type)); dvander@70: __ note_position(node->pos()); dvander@85: if (var->scope()->kind() == Scope::GLOBAL) dvander@85: __ newarray(map, Heap::Tenure_Old); dvander@85: else if (var->needsHeapAllocation()) dvander@85: __ newarray(map, Heap::Tenure_Default); dvander@85: else dvander@85: __ newarray(map, Heap::Tenure_Stack); dvander@21: dvander@21: if (node->initialization()) { dvander@21: node->initialization()->accept(this); dvander@70: __ note_position(node->pos()); 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@85: dvander@85: if (var->location() == Variable::STACK) { dvander@85: __ setlocal(var); dvander@85: locals_->set(zone_, var->slot(), var->type()); dvander@85: } else { dvander@85: assert(var->location() == Variable::ENVIRONMENT); dvander@85: setEnvSlot(var); dvander@85: } dvander@85: dvander@0: __ pop(); 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@73: // Sniff out natives, which have a specific opcode and are guaranteed to dvander@73: // be called via name proxy. dvander@73: NamedFunction *native = NULL; dvander@73: if (funType->isNative()) { dvander@73: native = node->callee()->toNameProxy()->binding()->toNamedFunction(); dvander@73: } dvander@73: dvander@68: for (size_t i = node->arguments()->length() - 1; i < node->arguments()->length(); i--) { dvander@0: Expression *arg = node->arguments()->at(i); dvander@14: dvander@68: // We need to compute a reference if the parameter was in the form: dvander@68: // f(&a) dvander@68: // Note this form does not allow: f({}) dvander@68: // dvander@68: // We also need to compute references for all variadic native dvander@68: // arguments, but arrays and references are already in the dvander@68: // appropriate form. dvander@32: Local given(zone_, arg->type()); dvander@69: bool mustPushReference = false; dvander@69: if (i >= funType->parameters()->length()) { dvander@68: assert(funType->isNative() && funType->isNativeVariadic()); dvander@69: mustPushReference = !given->isArray() && !given->isReference(); dvander@69: } else if (funType->parameterAt(i)->isReference()) { dvander@69: mustPushReference = !given->isReference(); dvander@69: } dvander@68: dvander@68: // If a native is passed to a native, it must only be one level deep. dvander@68: assert(!funType->isNative() || !given->isArray() || given->levels() == 1); dvander@68: dvander@68: if (mustPushReference) { dvander@68: 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@70: __ note_position(node->pos()); 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@70: __ note_position(expr->pos()); 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@70: __ note_position(node->pos()); dvander@14: __ slotref_lifo(); dvander@14: } dvander@14: } else { dvander@14: arg->accept(this); dvander@14: } dvander@0: } dvander@0: dvander@70: __ note_position(node->pos()); dvander@73: if (native) { dvander@73: __ callnative(native->slot(), node->arguments()->length()); dvander@73: __ drop(node->arguments()->length()); dvander@73: } else { dvander@73: node->callee()->accept(this); dvander@73: __ call(funType); dvander@73: __ drop(node->arguments()->length() + 1); dvander@73: } dvander@0: } dvander@0: dvander@0: void dvander@83: BytecodeCompiler::visit(TernaryExpression *node) dvander@83: { dvander@83: Label success, failure, join; dvander@83: dvander@83: visitForTest(node->condition(), &success, &failure, &success); dvander@83: __ bind(&success); dvander@83: node->left()->accept(this); dvander@83: __ drop(1); dvander@83: __ jump(&join); dvander@83: __ bind(&failure); dvander@83: node->right()->accept(this); dvander@83: __ bind(&join); dvander@83: } dvander@83: dvander@83: void dvander@0: BytecodeCompiler::visit(IfStatement *node) dvander@0: { dvander@75: Label join; dvander@0: if (!node->ifFalse()) { dvander@75: Label success; dvander@75: dvander@75: visitForTest(node->condition(), &success, &join, &success); dvander@75: __ bind(&success); dvander@0: node->ifTrue()->accept(this); dvander@0: } else { dvander@75: Label success, next; dvander@75: dvander@75: visitForTest(node->condition(), &success, &next, &success); dvander@75: __ bind(&success); dvander@0: node->ifTrue()->accept(this); dvander@0: __ jump(&join); dvander@75: __ bind(&next); dvander@0: node->ifFalse()->accept(this); dvander@0: } dvander@75: dvander@75: __ bind(&join); dvander@0: } dvander@0: dvander@0: void dvander@0: BytecodeCompiler::visit(FunctionStatement *node) dvander@0: { dvander@137: if (!node->fun()->used()) dvander@137: return; dvander@137: dvander@68: if (node->fun()->isNative()) { dvander@68: assert(!moduleData_->natives->at(node->fun()->slot())); dvander@68: dvander@68: Local type(zone_, node->fun()->type()); dvander@68: Local name(zone_, node->fun()->name()); dvander@68: Local native(zone_, Native::New(zone_, type, name)); dvander@68: if (!native) dvander@68: return; dvander@68: dvander@68: moduleData_->natives->set(zone_, node->fun()->slot(), native); dvander@68: return; dvander@68: } dvander@68: 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@85: Code * dvander@85: BytecodeCompiler::compileCode(Scope *scope, unsigned lineno) dvander@0: { dvander@72: // STOP doubles as RETURNVOID. dvander@0: __ stop(); dvander@0: dvander@69: if (__ oom() || zone_->hasPendingException()) 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@85: for (size_t i = 0; i < 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@70: Local sourcemap(zone_); dvander@70: if (__ sourcemapLength()) { dvander@70: sourcemap = ByteArray::New(zone_, __ sourcemapLength(), Heap::Tenure_Old); dvander@70: if (!sourcemap) dvander@70: return NULL; dvander@70: dvander@70: memcpy(sourcemap->raw(), __ sourcemap(), __ sourcemapLength()); dvander@70: } dvander@70: dvander@70: Local code(zone_); dvander@70: dvander@70: code = Code::New(zone_, dvander@70: bytes, dvander@70: locals_, dvander@70: objects, dvander@70: __ maxStackDepth(), dvander@70: moduleData_->module, dvander@70: safepoints, dvander@70: sourcemap, dvander@85: lineno); dvander@85: return code; dvander@85: } dvander@85: dvander@85: Function * dvander@85: BytecodeCompiler::compileFunction(ModuleData *module, FunctionStatement *node) dvander@85: { dvander@85: // Note: No need to record a life slot here, since it's part of the Frame. dvander@85: AutoEnterScope functionScope(this, node->scope()); dvander@85: dvander@85: // Set the initial line, because line mappings are deltas. dvander@85: __ setFirstLine(node->pos().line); dvander@85: locals_ = FixedArray::New(zone_, node->scope()->maxContainedLocals(), Heap::Tenure_Old); dvander@85: dvander@85: node->body()->accept(this); dvander@85: dvander@85: unsigned nargs = node->scope()->numArguments(); dvander@85: Local args(zone_, FixedArray::New(zone_, nargs, Heap::Tenure_Old)); dvander@85: if (!args) dvander@85: return NULL; dvander@85: dvander@85: for (size_t i = 0; i < node->scope()->numArguments(); i++) dvander@85: args->set(zone_, i, node->scope()->getArgument(i)->type()); dvander@85: dvander@85: dvander@85: Local code(zone_, compileCode(node->scope(), node->pos().line)); 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@85: AutoEnterScope scope(this, tree_->scope()); dvander@85: 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@68: dvander@68: moduleData_->natives = FixedArray::New(zone_, tree_->scope()->numNatives(), Heap::Tenure_Old); dvander@68: if (!moduleData_->natives) dvander@68: 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@69: if (zone_->hasPendingException()) dvander@69: return NULL; dvander@69: dvander@0: #ifndef NDEBUG dvander@0: for (size_t i = 0; i < tree_->scope()->numFunctions(); i++) dvander@65: assert(moduleData_->module->getFunction(i)); dvander@68: for (size_t i = 0; i < tree_->scope()->numNatives(); i++) dvander@68: assert(moduleData_->natives->at(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@68: moduleData_->module->setNatives(moduleData_->natives); dvander@68: dvander@85: if (scope_->numEnvironmentVars()) { dvander@85: Local globalMap(zone_, scope_->createEnvironmentMap(zone_)); dvander@85: if (!globalMap) dvander@85: return NULL; dvander@85: dvander@85: Local environment(zone_, Environment::New(zone_, globalMap)); dvander@85: if (!environment) dvander@85: return NULL; dvander@85: dvander@85: moduleData_->module->setEnvironment(environment); dvander@85: } dvander@85: dvander@85: Local code(zone_, compileCode(tree_->scope(), 1)); dvander@85: if (!code) dvander@85: return NULL; dvander@85: dvander@85: Disassemble(code, stdout); dvander@85: dvander@85: moduleData_->module->setCode(code); dvander@85: dvander@65: return moduleData_->module; dvander@0: }