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@168: #include "Box.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@145: __ getref(var->type()->contained()); 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@184: static inline bool dvander@184: IsCompositeValueType(Type *type) dvander@184: { dvander@184: return type->isStruct() || dvander@184: (type->isArray() && type->isFixedLength()); dvander@184: } dvander@184: dvander@0: void dvander@0: BytecodeCompiler::visit(Assignment *node) dvander@0: { dvander@184: Local left(zone_, node->type()); dvander@184: Local right(zone_, node->expression()->type()); dvander@152: 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@184: // We need to fetch the variable under the following circumstances: dvander@184: // (1) The variable is a reference, since we'll store into the ref. dvander@184: // (2) The variable is a fixed array, since we need to copy into it. dvander@184: // (3) The variable is a struct, since we need to copy into it. dvander@184: if (var->type()->isReference() || IsCompositeValueType(var->type())) 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@175: if (var->location() == Variable::ENVIRONMENT) dvander@175: getEnvSlot(var); dvander@175: dvander@22: if (var->type()->isReference()) { dvander@22: __ dup(); dvander@145: __ getref(var->type()->contained()); dvander@175: } else if (var->location() != Variable::ENVIRONMENT) { dvander@22: __ get(var); dvander@22: } dvander@22: } dvander@148: } else if (node->lvalue()->isFieldExpression()) { dvander@148: FieldExpression *expr = node->lvalue()->toFieldExpression(); dvander@148: __ note_position(expr->pos()); dvander@148: dvander@148: expr->left()->accept(this); dvander@148: dvander@152: if (node->token() != TOK_ASSIGN) dvander@148: __ dup(); dvander@152: dvander@184: if (node->token() != TOK_ASSIGN || IsCompositeValueType(left)) dvander@184: __ getfield(expr->fieldIndex(), left); dvander@23: } else { dvander@21: IndexExpression *index = node->lvalue()->toIndexExpression(); dvander@70: __ note_position(index->pos()); dvander@21: dvander@21: index->left()->accept(this); dvander@21: index->right()->accept(this); dvander@22: dvander@152: if (node->token() != TOK_ASSIGN) dvander@22: __ dup2(); dvander@152: dvander@184: if (node->token() != TOK_ASSIGN || IsCompositeValueType(left)) dvander@184: __ getelem(left); dvander@22: } dvander@22: dvander@22: if (node->token() == TOK_ASSIGN) { dvander@21: node->expression()->accept(this); dvander@22: } else { dvander@184: visitForALU(left, 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@184: if (left->isArray() && left->isFixedLength()) { dvander@184: // (x = y) returns the left-hand side; if |x| is a fixed-length array, dvander@184: // then |y| is as well, and we perform a deep copy. dvander@184: assert(right->isFixedLength()); dvander@70: __ note_position(node->pos()); dvander@152: __ copyarray(); dvander@184: } else if (left->isStruct()) { dvander@152: // Structs have value semantics, so emit a full copy. We don't break dvander@152: // this down into u-ops, to avoid unnecessary bytecode explosion. dvander@184: assert(right->isStruct()); dvander@152: __ note_position(node->pos()); dvander@152: __ copystruct(); 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@145: __ setref(var->type()->contained()); dvander@175: else if (var->location() == Variable::ENVIRONMENT) dvander@175: setEnvSlot(var); dvander@22: else dvander@16: __ set(var); dvander@148: } else if (node->lvalue()->isFieldExpression()) { dvander@148: FieldExpression *expr = node->lvalue()->toFieldExpression(); dvander@184: __ setfield(expr->fieldIndex(), left); dvander@21: } else { dvander@184: __ setelem(left); 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@174: if (var->location() == Variable::ENVIRONMENT) dvander@174: getEnvSlot(var); dvander@174: else 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@145: __ getref(node->type()); dvander@23: if (node->postfix()) { dvander@23: __ swap(); dvander@23: __ pick2(); dvander@23: } dvander@23: } else { dvander@23: assert(!var->type()->isArray()); dvander@174: if (var->location() != Variable::ENVIRONMENT) dvander@174: __ get(var); dvander@23: if (node->postfix()) dvander@23: __ dup(); dvander@23: } dvander@149: } else if (expr->isFieldExpression()) { dvander@149: FieldExpression *field = expr->toFieldExpression(); dvander@149: dvander@149: field->left()->accept(this); dvander@149: __ dup(); dvander@149: __ getfield(field->fieldIndex(), field->type()); dvander@149: dvander@149: if (node->postfix()) { dvander@149: // The stack looks like this now: dvander@149: // OBJECT VALUE dvander@149: __ swap(); // VALUE OBJECT dvander@149: __ pick2(); // VALUE OBJECT VALUE dvander@149: } 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: __ dup2(); dvander@157: __ getelem(node->type()); 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@145: __ setref(node->type()); dvander@174: else if (var->location() == Variable::ENVIRONMENT) dvander@174: setEnvSlot(var); dvander@23: else dvander@23: __ set(var); dvander@149: } else if (expr->isFieldExpression()) { dvander@149: FieldExpression *field = expr->toFieldExpression(); dvander@149: __ setfield(field->fieldIndex(), field->type()); dvander@23: } else { dvander@149: assert(expr->isIndexExpression()); dvander@157: __ setelem(node->type()); 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@145: Local out(zone_, ((Expression *)node)->type()); dvander@145: 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@145: if (out->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@145: if (out->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: dvander@142: node->expression()->accept(this); dvander@145: if (!from->isFloat() && out->isFloat()) dvander@142: __ bits_f2i(); dvander@145: else if (from->isFloat() && !out->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@157: __ getelem(node->type()); dvander@0: } dvander@0: dvander@0: void dvander@148: BytecodeCompiler::visit(FieldExpression *node) dvander@148: { dvander@148: node->left()->accept(this); dvander@148: __ getfield(node->fieldIndex(), node->type()); dvander@148: } dvander@148: dvander@148: 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@143: BytecodeCompiler::visit(BooleanLiteral *node) dvander@143: { dvander@143: if (node->token() == TOK_TRUE) dvander@143: __ true_(); dvander@143: else dvander@143: __ false_(); dvander@143: } dvander@143: dvander@143: void dvander@4: BytecodeCompiler::visit(FloatLiteral *node) dvander@4: { dvander@4: __ float_((float)node->value()); dvander@4: } dvander@4: dvander@187: static inline Array * dvander@187: StringLiteralToArray(Zone *zone, StringLiteral *node) dvander@187: { dvander@187: Local type(zone, node->type()); dvander@187: Local map(zone, ArrayMap::Attach(zone, type)); dvander@187: if (!map) dvander@187: return NULL; dvander@187: dvander@187: return Array::NewString(zone, dvander@187: map, dvander@187: node->string()->raw(), dvander@187: node->string()->length(), dvander@187: Heap::Tenure_Old); dvander@187: } dvander@187: dvander@4: void dvander@21: BytecodeCompiler::visit(StringLiteral *node) dvander@21: { dvander@187: Local array(zone_, StringLiteralToArray(zone_, node)); dvander@21: if (!array) dvander@21: return; 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@187: static bool dvander@187: FillArrayLiteral_String(Zone *zone, Handle array, ArrayLiteral *literal, unsigned *maxsize) dvander@187: { dvander@187: Local string(zone); dvander@187: for (size_t i = 0; i < literal->expressions()->length(); i++) { dvander@187: Expression *expr = literal->expressions()->at(i); dvander@187: StringLiteral *lit = expr->toStringLiteral(); dvander@187: if ((string = StringLiteralToArray(zone, lit)) == NULL) dvander@187: return false; dvander@187: dvander@187: array->writeObject(zone, i, string); dvander@187: dvander@187: if (string->length() > *maxsize) dvander@187: *maxsize = string->length(); dvander@187: dvander@187: assert(!array->type()->contained()->isFixedLength() || dvander@187: array->type()->contained()->fixedLength() >= string->length()); dvander@187: } dvander@187: dvander@187: return true; dvander@187: } dvander@187: dvander@187: static bool dvander@187: FillArrayLiteral_Primitive(Zone *zone, Handle array, ArrayLiteral *literal) dvander@187: { dvander@187: assert(array->type()->fixedLength() == literal->expressions()->length()); dvander@187: dvander@187: for (size_t i = 0; i < literal->expressions()->length(); i++) { dvander@187: Expression *expr = literal->expressions()->at(i); dvander@187: if (IntegerLiteral *lit = expr->asIntegerLiteral()) { dvander@187: array->writeInt32(i, lit->value()); dvander@187: continue; dvander@187: } dvander@187: if (BooleanLiteral *lit = expr->asBooleanLiteral()) { dvander@187: if (lit->token() == TOK_TRUE) dvander@187: array->writeInt32(i, 1); dvander@187: else dvander@187: array->writeInt32(i, 0); dvander@187: continue; dvander@187: } dvander@187: if (FloatLiteral *lit = expr->asFloatLiteral()) { dvander@187: array->writeFloat(i, lit->value()); dvander@187: continue; dvander@187: } dvander@187: dvander@187: // Unrecognized literal type. dvander@187: assert(false); dvander@187: } dvander@187: dvander@187: return true; dvander@187: } dvander@187: dvander@187: static bool dvander@187: TraverseArrayLiteral(Zone *zone, ArrayLiteral *literal, Handle array, Handle type, dvander@187: unsigned *maxsize) dvander@187: { dvander@187: assert(type->fixedLength() == literal->expressions()->length()); dvander@187: dvander@187: Local contained(zone, type->contained()); dvander@187: if (contained->isCharArray()) dvander@187: return FillArrayLiteral_String(zone, array, literal, maxsize); dvander@187: dvander@187: if (!contained->isArray()) dvander@187: return FillArrayLiteral_Primitive(zone, array, literal); dvander@187: dvander@187: Local subarray(zone); dvander@187: for (size_t i = 0; i < literal->expressions()->length(); i++) { dvander@187: Expression *expr = literal->expressions()->at(i); dvander@187: subarray = Array::cast(array->readObject(i)); dvander@187: if (!TraverseArrayLiteral(zone, expr->toArrayLiteral(), subarray, contained, maxsize)) dvander@187: return false; dvander@187: } dvander@187: dvander@187: return true; dvander@187: } dvander@187: dvander@187: static Array * dvander@187: BuildArrayLiteral(Zone *zone, ArrayLiteral *literal, Handle map, unsigned *maxsize) dvander@187: { dvander@187: Local root(zone, Array::NewFixed(zone, map, Heap::Tenure_Old)); dvander@187: if (!root) dvander@187: return NULL; dvander@187: dvander@187: // This should have allocated everything in the array, so all we need to dvander@187: // do is traverse down to the lowest levels and copy things. dvander@187: Local type(zone, map->type()); dvander@187: if (!TraverseArrayLiteral(zone, literal, root, type, maxsize)) dvander@187: return NULL; dvander@187: dvander@187: return root; dvander@187: } dvander@187: dvander@187: static inline bool dvander@187: ArrayContainsCharBuffers(Type *type) dvander@187: { dvander@187: if (type->contained()->isArray()) dvander@187: return ArrayContainsCharBuffers(type->contained()); dvander@187: return type->isCharArray(); dvander@187: } dvander@187: 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@169: Local map(zone_, ArrayMap::Attach(zone_, type)); dvander@70: __ note_position(node->pos()); dvander@185: dvander@187: Local right(zone_); dvander@187: if (node->initialization()) dvander@187: right = node->initialization()->type(); dvander@187: dvander@187: // There are a few methods for allocating, here: dvander@187: // (1) If the array is fixed, dvander@187: // (a) Initializer not present, dvander@187: // NEWFIXED dvander@187: // (b) Left is const non-string: dvander@187: // dvander@187: // (b) Otherwise: dvander@187: // NEWFIXED dvander@187: // COPYARRAY dvander@187: // (2) If the array is not fixed, dvander@187: // (a) Initializer is not present, dvander@187: // NEWEMPTY dvander@187: // (b) Initializer is not a string literal, dvander@187: // or left is const: dvander@187: // dvander@187: // (c) Initializer is a string literal, dvander@187: // NEWSIZED dvander@187: // COPYARRAY dvander@187: // dvander@187: dvander@187: Heap::Tenure tenure = var->scope()->kind() == Scope::GLOBAL dvander@187: ? Heap::Tenure_Old dvander@187: : Heap::Tenure_Default; dvander@187: dvander@187: if (Expression *init = node->initialization()) { dvander@187: if (init->isArrayLiteral()) { dvander@187: unsigned maxsize = 0; dvander@187: Local array(zone_, BuildArrayLiteral(zone_, init->toArrayLiteral(), map, &maxsize)); dvander@187: if (!array) dvander@187: return; dvander@187: dvander@187: assert(type->isFixedLength()); dvander@185: dvander@187: if (type->isConst()) { dvander@187: __ object(array); dvander@187: } else if (ArrayContainsCharBuffers(type)) { dvander@187: // This array contains mutable char buffers, so dvander@187: // unfortunately this gets a little more complicated. We dvander@187: // compute the maximum size needed for the innermost dvander@187: // string, which is similar to SP1. This means we can use dvander@187: // CopyArray rather than individually emit opcodes to clone dvander@187: // every string. dvander@187: Local child(zone_, type); dvander@187: for (size_t i = 0; i < type->levels() - 1; i++) { dvander@187: __ int_(child->fixedLength()); dvander@187: child = child->contained(); dvander@187: } dvander@187: __ int_(maxsize); dvander@187: __ newarray(OP_NEWSIZED, map, tenure); dvander@187: __ object(array); dvander@187: __ copyarray(); dvander@187: } else { dvander@187: __ newarray(OP_NEWFIXED, map, tenure); dvander@187: __ object(array); dvander@187: __ copyarray(); dvander@187: } dvander@187: } else if (type->isFixedLength()) { dvander@187: // Note in the case of: dvander@187: // const String:buffer[50] = "hello"; dvander@187: // dvander@187: // We want to preserve sizeof(buffer) == 50. dvander@187: if (type->isConst() && !type->isCharArray()) { dvander@187: init->accept(this); dvander@187: } else { dvander@187: __ newarray(OP_NEWFIXED, map, tenure); dvander@187: init->accept(this); dvander@187: __ copyarray(); dvander@187: } dvander@187: } else if (!type->isConst() && init->isStringLiteral()) { dvander@187: // This is a degenerate case of ArrayLiteral. dvander@187: __ int_(right->fixedLength()); dvander@187: __ newarray(OP_NEWSIZED, map, tenure); dvander@187: init->accept(this); dvander@187: __ copyarray(); dvander@187: } else if (type->isConst()) { dvander@187: init->accept(this); dvander@187: } dvander@187: } else if (type->isFixedLength()) { dvander@187: __ newarray(OP_NEWFIXED, map, tenure); dvander@187: } else { dvander@190: if (node->postDimensions()) { dvander@190: for (size_t i = 0; i < node->postDimensions()->length(); i++) { dvander@190: Expression *expr = node->postDimensions()->at(i); dvander@190: if (expr) dvander@190: expr->accept(this); dvander@190: else dvander@190: __ int_(0); dvander@190: } dvander@190: __ newarray(OP_NEWSIZED, map, tenure); dvander@190: } else { dvander@190: __ newarray(OP_NEWEMPTY, map, tenure); dvander@190: } dvander@21: } dvander@147: } else if (type->isStruct()) { dvander@147: Local map(zone_, type->newMap()); dvander@147: if (var->scope()->kind() == Scope::GLOBAL) dvander@147: __ newstruct(map, Heap::Tenure_Old); dvander@147: else if (var->needsHeapAllocation()) dvander@147: __ newstruct(map, Heap::Tenure_Default); dvander@147: else dvander@147: __ newstruct(map, Heap::Tenure_Stack); dvander@0: } else { dvander@0: if (node->initialization()) { dvander@0: node->initialization()->accept(this); dvander@0: } else { dvander@147: if (type->isInt32OrEnum() || type->isBool()) { dvander@0: __ int_(0); dvander@147: } else { dvander@147: assert(type->isFloat()); dvander@147: __ float_(0); dvander@147: } 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@182: if (funType->isNative()) dvander@73: native = node->callee()->toNameProxy()->binding()->toNamedFunction(); dvander@73: dvander@168: Local given(zone_); 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@168: given = arg->type(); dvander@168: 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@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@164: } else if (arg->isFieldExpression()) { dvander@164: FieldExpression *expr = arg->toFieldExpression(); dvander@164: expr->left()->accept(this); dvander@164: __ note_position(expr->pos()); dvander@164: __ fieldref_lifo(expr->fieldIndex()); 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@168: unsigned argc = node->arguments()->length(); dvander@168: dvander@168: // If there are missing arguments, then we're not done and have to push dvander@168: // default parameters. dvander@168: Local box(zone_); dvander@169: Local array(zone_); dvander@169: Local map(zone_); dvander@168: for (size_t i = node->arguments()->length(); i < funType->parameters()->length(); i++) { dvander@168: box = Box::cast(funType->defaults()->at(i)); dvander@168: dvander@168: if (box->type()->isPod()) { dvander@168: if (box->type()->pod() == PrimitiveType_Int32) dvander@168: __ int_(box->toInt32()); dvander@168: else if (box->type()->pod() == PrimitiveType_Float) dvander@168: __ float_(box->toFloat()); dvander@168: else dvander@168: assert(false); dvander@169: dvander@169: if (funType->parameterAt(i)->isReference()) { dvander@169: __ note_position(node->pos()); dvander@169: __ slotref_lifo(); dvander@169: } dvander@168: } else { dvander@169: assert(box->type()->isArray()); dvander@168: dvander@169: array = Array::cast(box->toObject()); dvander@169: if (funType->parameterAt(i)->isConst()) { dvander@169: __ object(array); dvander@169: } else { dvander@169: // We need to copy the array since it's mutable. Note that dvander@169: // although COPYARRAY returns its left-hand side, this may dvander@169: // change in the future, we do the same thing and dupe the dvander@169: // newly allocated array. dvander@169: map = ArrayMap::cast(array->map()); dvander@182: dvander@182: // Natives cannot escape array references, so we can allocate dvander@182: // them on the LIFO stack. dvander@182: if (funType->isNative()) dvander@185: __ newarray(OP_NEWFIXED, map, Heap::Tenure_Stack); dvander@182: else dvander@185: __ newarray(OP_NEWFIXED, map, Heap::Tenure_Default); dvander@169: __ dup(); dvander@169: __ object(array); dvander@169: __ copyarray(); dvander@169: __ pop(); dvander@169: } dvander@168: } dvander@168: dvander@168: argc++; dvander@168: } dvander@168: dvander@70: __ note_position(node->pos()); dvander@73: if (native) { dvander@168: __ callnative(native->slot(), argc); dvander@168: __ drop(argc); dvander@73: } else { dvander@73: node->callee()->accept(this); dvander@73: __ call(funType); dvander@168: __ drop(argc + 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@172: bool dvander@172: BytecodeCompiler::tableswitch(SwitchStatement *node) dvander@172: { dvander@172: CaseValueList *values = node->caseValueList(); dvander@172: dvander@172: if (values->length() < 3) dvander@172: return false; dvander@172: dvander@172: // Find the delta between the largest and smallest value. dvander@172: int hi = values->at(values->length() - 1).box.toInt(); dvander@172: int lo = values->at(0).box.toInt(); dvander@172: assert(hi > lo); dvander@172: dvander@172: unsigned tablesize = TableSwitchEntries(lo, hi); dvander@172: if (tablesize > MAX_TABLESWITCH_ENTRIES) dvander@172: return false; dvander@172: dvander@172: Label def, join; dvander@172: dvander@172: // Create the list of labels that each statement will bind to. dvander@172: Label *labels = new Label[node->cases()->length()]; dvander@172: dvander@172: // Create the table of labels we'll use for each case. dvander@172: Label **table = new Label *[tablesize]; dvander@172: dvander@172: // Link all cases, by default, to the default label. dvander@172: for (size_t i = 0; i < tablesize; i++) dvander@172: table[i] = &def; dvander@172: dvander@172: // Now patch actual cases to their correct labels. dvander@172: for (size_t i = 0; i < values->length(); i++) { dvander@172: CaseValue &v = values->at(i); dvander@172: assert(v.statement < node->cases()->length()); dvander@172: dvander@172: unsigned index = unsigned(v.box.toInt() - lo); dvander@172: assert(index < tablesize); dvander@172: dvander@172: table[index] = &labels[v.statement]; dvander@172: } dvander@172: dvander@172: // Emit the tableswitch opcode and the jump table. dvander@172: __ tableswitch(lo, hi, &def, table); dvander@172: dvander@172: // We're done with the jump table now. dvander@172: delete [] table; dvander@172: dvander@172: // Emit each statement. dvander@172: for (size_t i = 0; i < node->cases()->length(); i++) { dvander@172: Statement *stmt = node->cases()->at(i)->statement(); dvander@172: dvander@172: __ bind(&labels[i]); dvander@172: stmt->accept(this); dvander@172: __ jump(&join); dvander@172: } dvander@172: dvander@172: // We're done with the statement labels. dvander@172: delete [] labels; dvander@172: dvander@172: // Emit the default case. dvander@172: __ bind(&def); dvander@172: if (node->defaultCase()) dvander@172: node->defaultCase()->accept(this); dvander@172: dvander@172: __ bind(&join); dvander@172: dvander@172: return true; dvander@172: } dvander@172: dvander@172: void dvander@172: BytecodeCompiler::visit(SwitchStatement *node) dvander@172: { dvander@172: node->expression()->accept(this); dvander@172: dvander@172: if (tableswitch(node)) dvander@172: return; dvander@172: dvander@172: Label join; dvander@172: dvander@172: for (size_t i = 0; i < node->cases()->length(); i++) { dvander@172: Case *c = node->cases()->at(i); dvander@172: dvander@172: Label success, fail; dvander@172: if (c->others()) { dvander@172: for (size_t j = 0; j < c->others()->length(); j++) { dvander@172: Expression *expr = c->others()->at(j); dvander@172: dvander@172: __ dup(); dvander@172: expr->accept(this); dvander@172: __ j(OP_JEQ, &success); dvander@172: } dvander@172: } dvander@172: dvander@172: // The final test. dvander@172: __ dup(); dvander@172: c->expression()->accept(this); dvander@172: __ j(OP_JNE, &fail); dvander@172: __ bind(&success); dvander@172: c->statement()->accept(this); dvander@172: __ jump(&join); dvander@172: __ bind(&fail); dvander@172: } dvander@172: dvander@172: if (node->defaultCase()) dvander@172: node->defaultCase()->accept(this); dvander@172: dvander@173: __ pop(); dvander@172: __ bind(&join); dvander@172: } dvander@172: 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@174: BytecodeCompiler::visit(FunctionTypeStatement *node) dvander@174: { dvander@174: } dvander@174: dvander@174: void dvander@2: BytecodeCompiler::visit(EnumStatement *node) dvander@2: { dvander@2: } dvander@2: dvander@146: void dvander@146: BytecodeCompiler::visit(StructureStatement *node) dvander@146: { dvander@146: } dvander@146: dvander@187: void dvander@187: BytecodeCompiler::visit(ArrayLiteral *node) dvander@187: { dvander@187: // We shouldn't reach these; they're handled manually when initializing dvander@187: // declared arrays. dvander@187: assert(false); dvander@187: } dvander@187: 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@153: #ifndef NDEBUG dvander@66: Disassemble(code, stdout); dvander@153: #endif 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: }