dvander@195: /* vim: set ts=4 sw=4 tw=99 et: dvander@195: * dvander@195: * Copyright (C) 2012 David Anderson dvander@195: * dvander@195: * This file is part of JITCraft. dvander@195: * dvander@195: * JITCraft is free software: you can redistribute it and/or modify it under dvander@195: * the terms of the GNU General Public License as published by the Free dvander@195: * Software Foundation, either version 3 of the License, or (at your option) dvander@195: * any later version. dvander@195: * dvander@195: * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY dvander@195: * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS dvander@195: * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. dvander@195: * dvander@195: * You should have received a copy of the GNU General Public License along with dvander@195: * JITCraft. If not, see http://www.gnu.org/licenses/. dvander@195: */ dvander@195: #include dvander@195: #include "../Modules.h" dvander@195: #include "../Types.h" dvander@195: #include "../Array.h" dvander@195: #include "../Strings.h" dvander@195: #include "../Zone.h" dvander@195: #include "../Environments.h" dvander@195: #include "../Box.h" dvander@195: #include "../Heap-inl.h" dvander@195: #include "BytecodeCompiler.h" dvander@195: #include "Scopes.h" dvander@195: dvander@195: using namespace ke; dvander@195: dvander@195: typedef BytecodeEmitter::Label Label; dvander@195: dvander@195: #define __ emitter_. dvander@195: dvander@195: BytecodeCompiler::BytecodeCompiler(Zone *zone, ParseTree *tree, ModuleData *module) dvander@195: : zone_(zone), dvander@195: tree_(tree), dvander@195: emitter_(zone->pool()), dvander@195: locals_(NULL), dvander@195: scope_(NULL), dvander@195: moduleData_(module), dvander@195: loop_(NULL) dvander@195: { dvander@195: } dvander@195: dvander@195: static inline TokenKind dvander@195: ToCompare(Expression *expr) dvander@195: { dvander@195: if (expr->isBinaryExpression()) { dvander@195: BinaryExpression *b = expr->toBinaryExpression(); dvander@195: if (b->token() >= TOK_EQUALS && b->token() <= TOK_OR) dvander@195: return b->token(); dvander@195: } else if (expr->isUnaryExpression()) { dvander@195: UnaryExpression *u = expr->toUnaryExpression(); dvander@195: if (u->token() == TOK_NOT) dvander@195: return TOK_NOT; dvander@195: } dvander@195: return TOK_EOF; dvander@195: } dvander@195: dvander@195: static inline Opcode dvander@195: CompareTokenToJump(TokenKind kind) dvander@195: { dvander@195: switch (kind) { dvander@195: case TOK_EQUALS: dvander@195: return OP_JEQ; dvander@195: case TOK_NOTEQUALS: dvander@195: return OP_JNE; dvander@195: case TOK_GT: dvander@195: return OP_JG; dvander@195: case TOK_GE: dvander@195: return OP_JGE; dvander@195: case TOK_LT: dvander@195: return OP_JL; dvander@195: default: dvander@195: assert(kind == TOK_LE); dvander@195: return OP_JLE; dvander@195: } dvander@195: } dvander@195: dvander@195: static inline Opcode dvander@195: TokenToOpcode(TokenKind kind) dvander@195: { dvander@195: switch (kind) { dvander@195: case TOK_PLUS: dvander@195: return OP_ADD; dvander@195: case TOK_MINUS: dvander@195: return OP_SUB; dvander@195: case TOK_STAR: dvander@195: return OP_MUL; dvander@195: case TOK_SLASH: dvander@195: return OP_DIV; dvander@195: case TOK_PERCENT: dvander@195: return OP_MOD; dvander@195: case TOK_SHR: dvander@195: return OP_SHR; dvander@195: case TOK_USHR: dvander@195: return OP_USHR; dvander@195: case TOK_SHL: dvander@195: return OP_SHL; dvander@195: case TOK_BITAND: dvander@195: return OP_BITAND; dvander@195: case TOK_BITOR: dvander@195: return OP_BITOR; dvander@195: case TOK_GT: dvander@195: return OP_GT; dvander@195: case TOK_GE: dvander@195: return OP_GE; dvander@195: case TOK_LT: dvander@195: return OP_LT; dvander@195: case TOK_LE: dvander@195: return OP_LE; dvander@195: case TOK_EQUALS: dvander@195: return OP_EQ; dvander@195: case TOK_NOTEQUALS: dvander@195: return OP_NE; dvander@195: default: dvander@195: assert(kind == TOK_BITXOR); dvander@195: return OP_BITXOR; dvander@195: } dvander@195: } dvander@195: dvander@195: void dvander@195: BytecodeCompiler::recordLifoSlot(Scope *scope) dvander@195: { dvander@195: if (!scope) dvander@195: return; dvander@195: dvander@195: if (Variable *var = scope->lifoSlot()) { dvander@195: locals_->set(zone_, var->slot(), var->type()); dvander@195: __ savelifo(var->slot()); dvander@195: } dvander@195: } dvander@195: dvander@195: void dvander@195: BytecodeCompiler::unwindTo(Scope *scope) dvander@195: { dvander@195: // Note: we only need to unwind to the outermost scope's LIFO slot. dvander@195: Variable *var = NULL; dvander@195: for (Scope *current = scope_; current != scope; current = current->enclosing()) { dvander@195: if (Variable *slot = current->lifoSlot()) dvander@195: var = slot; dvander@195: } dvander@195: dvander@195: if (var) dvander@195: __ unwindlifo(var->slot()); dvander@195: } dvander@195: dvander@195: void dvander@195: BytecodeCompiler::visit(NameProxy *name) dvander@195: { dvander@195: switch (name->binding()->kind()) { dvander@195: case BoundName::VARIABLE: dvander@195: { dvander@195: Variable *var = name->binding()->toVariable(); dvander@195: dvander@195: if (var->location() == Variable::STACK) { dvander@195: __ getlocal(var); dvander@195: assert(!name->type()->isReference()); dvander@195: } else if (var->location() == Variable::ENVIRONMENT) { dvander@195: getEnvSlot(var); dvander@195: } else { dvander@195: assert(var->location() == Variable::PARAMETER); dvander@195: __ getarg(var); dvander@195: dvander@195: // If this argument is actually a reference, dereference it now. dvander@195: if (var->type()->isReference()) dvander@195: __ getref(var->type()->contained()); dvander@195: } dvander@195: break; dvander@195: } dvander@195: dvander@195: case BoundName::FUNCTION: dvander@195: { dvander@195: NamedFunction *fun = name->binding()->toNamedFunction(); dvander@195: __ module(); dvander@195: __ getmodule_fn(fun->slot()); dvander@195: break; dvander@195: } dvander@195: dvander@195: default: dvander@195: { dvander@195: NamedConstant *constant = name->binding()->toNamedConstant(); dvander@195: __ int_(constant->value().toInt()); dvander@195: break; dvander@195: } dvander@195: } dvander@195: } dvander@195: dvander@195: void dvander@195: BytecodeCompiler::visit(ExpressionStatement *node) dvander@195: { dvander@195: // Expressions leave their value on the top of the stack, so we make sure dvander@195: // to balance the stack if they're used as a statement. dvander@195: node->expression()->accept(this); dvander@195: if (!node->expression()->type()->isVoid()) dvander@195: __ pop(); dvander@195: } dvander@195: dvander@195: static inline bool dvander@195: IsCompositeValueType(Type *type) dvander@195: { dvander@195: return type->isStruct() || dvander@195: (type->isArray() && type->isFixedLength()); dvander@195: } dvander@195: dvander@195: static inline Array * dvander@195: StringLiteralToArray(Zone *zone, StringLiteral *node) dvander@195: { dvander@195: Local type(zone, node->type()); dvander@195: Local map(zone, ArrayMap::Attach(zone, type)); dvander@195: if (!map) dvander@195: return NULL; dvander@195: dvander@195: return Array::NewString(zone, dvander@195: map, dvander@195: node->string()->raw(), dvander@195: node->string()->length(), dvander@195: Heap::Tenure_Old); dvander@195: } dvander@195: dvander@195: void dvander@195: BytecodeCompiler::visit(Assignment *node) dvander@195: { dvander@195: Local left(zone_, node->type()); dvander@195: Local right(zone_, node->expression()->type()); dvander@195: dvander@195: // The first step is three sub-steps: dvander@195: // dvander@195: // (1) Bind the left-hand side. dvander@195: // -- If a cumulative operation (+=, etc): dvander@195: // (2) Save a copy of the binding on the stack. dvander@195: // (3) Dereference the binding to get a value. dvander@195: // dvander@195: if (node->lvalue()->isNameProxy()) { dvander@195: NameProxy *proxy = node->lvalue()->toNameProxy(); dvander@195: Variable *var = proxy->binding()->toVariable(); dvander@195: dvander@195: // We need to fetch the variable under the following circumstances: dvander@195: // (1) The variable is a reference, since we'll store into the ref. dvander@195: // (2) The variable is a fixed array, since we need to copy into it. dvander@195: // (3) The variable is a struct, since we need to copy into it. dvander@195: if (var->type()->isReference() || IsCompositeValueType(var->type())) dvander@195: __ get(var); dvander@195: dvander@195: // Get the value if needed. dvander@195: if (node->token() != TOK_ASSIGN) { dvander@195: // += etc is not allowed on arrays. dvander@195: assert(!var->type()->isArray()); dvander@195: dvander@195: if (var->location() == Variable::ENVIRONMENT) dvander@195: getEnvSlot(var); dvander@195: dvander@195: if (var->type()->isReference()) { dvander@195: __ dup(); dvander@195: __ getref(var->type()->contained()); dvander@195: } else if (var->location() != Variable::ENVIRONMENT) { dvander@195: __ get(var); dvander@195: } dvander@195: } dvander@195: } else if (node->lvalue()->isFieldExpression()) { dvander@195: FieldExpression *expr = node->lvalue()->toFieldExpression(); dvander@195: __ note_position(expr->pos()); dvander@195: dvander@195: expr->left()->accept(this); dvander@195: dvander@195: if (node->token() != TOK_ASSIGN) dvander@195: __ dup(); dvander@195: dvander@195: if (node->token() != TOK_ASSIGN || IsCompositeValueType(left)) dvander@195: __ getfield(expr->fieldIndex(), left); dvander@195: } else { dvander@195: IndexExpression *index = node->lvalue()->toIndexExpression(); dvander@195: __ note_position(index->pos()); dvander@195: dvander@195: index->left()->accept(this); dvander@195: index->right()->accept(this); dvander@195: dvander@195: if (node->token() != TOK_ASSIGN) dvander@195: __ dup2(); dvander@195: dvander@195: if (node->token() != TOK_ASSIGN || IsCompositeValueType(left)) dvander@195: __ getelem(left); dvander@195: } dvander@195: dvander@195: if (node->token() == TOK_ASSIGN) { dvander@195: // Special case: if the left-hand side is a string literal, we must dvander@195: // spawn a new copy (:TODO: something about const?) dvander@195: if (node->expression()->isStringLiteral() && !left->isFixedLength()) { dvander@195: Local string(zone_, dvander@195: StringLiteralToArray(zone_, node->expression()->toStringLiteral())); dvander@195: if (!string) dvander@195: return; dvander@195: dvander@195: Local arrayMap(zone_, ArrayMap::cast(string->map())); dvander@195: __ note_position(node->expression()->pos()); dvander@195: __ int_(string->length()); dvander@195: __ newarray(OP_NEWSIZED, arrayMap, Heap::Tenure_Default); dvander@195: __ object(string); dvander@195: __ copyarray(); dvander@195: } else { dvander@195: node->expression()->accept(this); dvander@195: } dvander@195: } else { dvander@195: visitForALU(left, node->expression()); dvander@195: dvander@195: TokenKind alu = TokenKind(int(node->token()) - (TOK_ASSIGN_ADD - TOK_PLUS)); dvander@195: __ note_position(node->pos()); dvander@195: __ binary(TokenToOpcode(alu)); dvander@195: } dvander@195: dvander@195: // Emit the store. dvander@195: if (left->isArray() && left->isFixedLength()) { dvander@195: // (x = y) returns the left-hand side; if |x| is a fixed-length array, dvander@195: // then |y| is as well, and we perform a deep copy. dvander@195: assert(right->isFixedLength()); dvander@195: __ note_position(node->pos()); dvander@195: __ copyarray(); dvander@195: } else if (left->isStruct()) { dvander@195: // Structs have value semantics, so emit a full copy. We don't break dvander@195: // this down into u-ops, to avoid unnecessary bytecode explosion. dvander@195: assert(right->isStruct()); dvander@195: __ note_position(node->pos()); dvander@195: __ copystruct(); dvander@195: } else if (node->lvalue()->isNameProxy()) { dvander@195: NameProxy *proxy = node->lvalue()->toNameProxy(); dvander@195: Variable *var = proxy->binding()->toVariable(); dvander@195: dvander@195: if (var->type()->isReference()) dvander@195: __ setref(var->type()->contained()); dvander@195: else if (var->location() == Variable::ENVIRONMENT) dvander@195: setEnvSlot(var); dvander@195: else dvander@195: __ set(var); dvander@195: } else if (node->lvalue()->isFieldExpression()) { dvander@195: FieldExpression *expr = node->lvalue()->toFieldExpression(); dvander@195: __ setfield(expr->fieldIndex(), left); dvander@195: } else { dvander@195: __ setelem(left); dvander@195: } dvander@195: } dvander@195: dvander@195: void dvander@195: BytecodeCompiler::visit(IncDecExpression *node) dvander@195: { dvander@195: // In the postfix case, the logic looks like: dvander@195: // (1) Bind -> REF dvander@195: // (2) Dup -> REF REF dvander@195: // (3) Fetch -> REF VAL dvander@195: // (4) Swap -> VAL REF dvander@195: // (5) Pick -> VAL REF VAL dvander@195: // (6) Add -> VAL REF VAL+1 dvander@195: // (7) Store -> VAL VAL+1 dvander@195: // (8) Drop -> VAL dvander@195: // dvander@195: // In the prefix case, the logic looks like: dvander@195: // (1) Bind -> REF dvander@195: // (2) Dup -> REF REF dvander@195: // (3) Fetch -> REF VAL dvander@195: // -- dvander@195: // -- dvander@195: // (6) Add -> REF VAL+1 dvander@195: // (7) Store -> VAL+1 dvander@195: // -- dvander@195: // dvander@195: // This is squirrely enough such that we don't bother sharing too much dvander@195: // with the above. dvander@195: // dvander@195: // Note: We simulate a "Pick" operation with a dup2+pop. dvander@195: dvander@195: // This is steps (1) to (5). dvander@195: Expression *expr = node->expression(); dvander@195: if (expr->isNameProxy()) { dvander@195: NameProxy *proxy = expr->toNameProxy(); dvander@195: Variable *var = proxy->binding()->toVariable(); dvander@195: dvander@195: // Bind. dvander@195: if (var->location() == Variable::ENVIRONMENT) dvander@195: getEnvSlot(var); dvander@195: else if (var->type()->isReference() || var->type()->isArray()) dvander@195: __ get(var); dvander@195: dvander@195: // Dup and fetch. dvander@195: if (var->type()->isReference()) { dvander@195: __ dup(); dvander@195: __ getref(node->type()); dvander@195: if (node->postfix()) { dvander@195: __ swap(); dvander@195: __ pick2(); dvander@195: } dvander@195: } else { dvander@195: assert(!var->type()->isArray()); dvander@195: if (var->location() != Variable::ENVIRONMENT) dvander@195: __ get(var); dvander@195: if (node->postfix()) dvander@195: __ dup(); dvander@195: } dvander@195: } else if (expr->isFieldExpression()) { dvander@195: FieldExpression *field = expr->toFieldExpression(); dvander@195: dvander@195: field->left()->accept(this); dvander@195: __ dup(); dvander@195: __ getfield(field->fieldIndex(), field->type()); dvander@195: dvander@195: if (node->postfix()) { dvander@195: // The stack looks like this now: dvander@195: // OBJECT VALUE dvander@195: __ swap(); // VALUE OBJECT dvander@195: __ pick2(); // VALUE OBJECT VALUE dvander@195: } dvander@195: } else { dvander@195: IndexExpression *index = expr->toIndexExpression(); dvander@195: __ note_position(index->pos()); dvander@195: dvander@195: index->left()->accept(this); dvander@195: index->right()->accept(this); dvander@195: dvander@195: assert(!node->type()->isArray()); dvander@195: __ dup2(); dvander@195: __ getelem(node->type()); dvander@195: dvander@195: if (node->postfix()) { dvander@195: // The stack looks like this now: dvander@195: // ARRAY INDEX VALUE dvander@195: // dvander@195: // So we must use a PICK operation to rotate the stack into position. dvander@195: __ roll3(); // INDEX VALUE ARRAY dvander@195: __ roll3(); // VALUE ARRAY INDEX dvander@195: __ pick3(); // VALUE ARRAY INDEX VALUE dvander@195: } dvander@195: } dvander@195: dvander@195: // Perform step (6). dvander@195: if (node->type()->isFloat()) { dvander@195: __ float_(1.0); dvander@195: } else { dvander@195: assert(node->type()->isInt32()); dvander@195: __ int_(1); dvander@195: } dvander@195: dvander@195: __ note_position(node->pos()); dvander@195: if (node->token() == TOK_INCREMENT) dvander@195: __ binary(OP_ADD); dvander@195: else dvander@195: __ binary(OP_SUB); dvander@195: dvander@195: // Perform step (7). dvander@195: if (expr->isNameProxy()) { dvander@195: NameProxy *proxy = expr->toNameProxy(); dvander@195: Variable *var = proxy->binding()->toVariable(); dvander@195: dvander@195: if (var->type()->isReference()) dvander@195: __ setref(node->type()); dvander@195: else if (var->location() == Variable::ENVIRONMENT) dvander@195: setEnvSlot(var); dvander@195: else dvander@195: __ set(var); dvander@195: } else if (expr->isFieldExpression()) { dvander@195: FieldExpression *field = expr->toFieldExpression(); dvander@195: __ setfield(field->fieldIndex(), field->type()); dvander@195: } else { dvander@195: assert(expr->isIndexExpression()); dvander@195: __ setelem(node->type()); dvander@195: } dvander@195: dvander@195: // Perform step (8). dvander@195: if (node->postfix()) dvander@195: __ pop(); dvander@195: } dvander@195: dvander@195: void dvander@195: BytecodeCompiler::visitForALU(Type *needed, Expression *node) dvander@195: { dvander@195: node->accept(this); dvander@195: if (needed->isFloat() && !node->type()->isFloat()) dvander@195: __ cvt_i2f(); dvander@195: else dvander@195: assert(needed->primitive() == node->type()->primitive()); dvander@195: } dvander@195: dvander@195: static inline Type * dvander@195: GetCoercionType(TokenKind tok, Type *left, Type *right) dvander@195: { dvander@195: if (left->isFloat()) dvander@195: return left; dvander@195: return right; dvander@195: } dvander@195: dvander@195: void dvander@195: BytecodeCompiler::visit(UnaryExpression *node) dvander@195: { dvander@195: Local out(zone_, ((Expression *)node)->type()); dvander@195: dvander@195: switch (node->token()) { dvander@195: case TOK_MINUS: dvander@195: { dvander@195: Local type(zone_, node->expression()->type()); dvander@195: node->expression()->accept(this); dvander@195: dvander@195: if (out->isFloat()) dvander@195: __ neg_f(); dvander@195: else dvander@195: __ neg(); dvander@195: break; dvander@195: } dvander@195: dvander@195: case TOK_NOT: dvander@195: { dvander@195: Local type(zone_, node->expression()->type()); dvander@195: node->expression()->accept(this); dvander@195: dvander@195: if (out->isFloat()) dvander@195: __ not_f(); dvander@195: else dvander@195: __ not_(); dvander@195: break; dvander@195: } dvander@195: dvander@195: case TOK_LABEL: dvander@195: { dvander@195: Local from(zone_, node->expression()->type()); dvander@195: dvander@195: node->expression()->accept(this); dvander@195: if (!from->isFloat() && out->isFloat()) dvander@195: __ bits_f2i(); dvander@195: else if (from->isFloat() && !out->isFloat()) dvander@195: __ bits_i2f(); dvander@195: break; dvander@195: } dvander@195: dvander@195: case TOK_SIZEOF: dvander@195: { dvander@195: Local type(zone_, node->expression()->type()); dvander@195: if (type->isFixedLength()) { dvander@195: __ int_(type->fixedLength()); dvander@195: } else { dvander@195: node->expression()->accept(this); dvander@195: __ sizeof_(); dvander@195: } dvander@195: break; dvander@195: } dvander@195: dvander@195: default: dvander@195: assert(node->token() == TOK_TILDE); dvander@195: node->expression()->accept(this); dvander@195: __ bitnot(); dvander@195: break; dvander@195: } dvander@195: } dvander@195: dvander@195: void dvander@195: BytecodeCompiler::visit(BinaryExpression *node) dvander@195: { dvander@195: if (node->token() == TOK_AND || node->token() == TOK_OR) { dvander@195: Label success, failure, done; dvander@195: visitForTest(node, &success, &failure, &success); dvander@195: __ bind(&success); dvander@195: __ int_(1); dvander@195: __ drop(1); // Otherwise, the stack depth will be +1 too much. dvander@195: __ jump(&done); dvander@195: __ bind(&failure); dvander@195: __ int_(0); dvander@195: __ bind(&done); dvander@195: return; dvander@195: } dvander@195: dvander@195: Local leftType(zone_, node->left()->type()); dvander@195: Local rightType(zone_, node->right()->type()); dvander@195: Local coerce(zone_, GetCoercionType(node->token(), leftType, rightType)); dvander@195: dvander@195: visitForALU(coerce, node->left()); dvander@195: visitForALU(coerce, node->right()); dvander@195: dvander@195: Opcode op = TokenToOpcode(node->token()); dvander@195: if (node->type()->isFloat()) dvander@195: op = (Opcode)(int(op) + (OP_ADD_F - OP_ADD)); dvander@195: dvander@195: __ note_position(node->pos()); dvander@195: __ binary(op); dvander@195: } dvander@195: dvander@195: void dvander@195: BytecodeCompiler::visit(IndexExpression *node) dvander@195: { dvander@195: node->left()->accept(this); dvander@195: node->right()->accept(this); dvander@195: __ note_position(node->pos()); dvander@195: __ getelem(node->type()); dvander@195: } dvander@195: dvander@195: void dvander@195: BytecodeCompiler::visit(FieldExpression *node) dvander@195: { dvander@195: node->left()->accept(this); dvander@195: __ getfield(node->fieldIndex(), node->type()); dvander@195: } dvander@195: dvander@195: void dvander@195: BytecodeCompiler::visit(ReturnStatement *node) dvander@195: { dvander@195: if (node->expression()) { dvander@195: node->expression()->accept(this); dvander@195: __ return_(); dvander@195: } else { dvander@195: __ returnvoid(); dvander@195: } dvander@195: } dvander@195: dvander@195: void dvander@195: BytecodeCompiler::visitForTest(Expression *expression, dvander@195: Label *trueBranch, dvander@195: Label *falseBranch, dvander@195: Label *fallthrough) dvander@195: { dvander@195: TokenKind token = ToCompare(expression); dvander@195: if (token == TOK_EOF || token == TOK_NOT) { dvander@195: if (token == TOK_EOF) dvander@195: expression->accept(this); dvander@195: else dvander@195: expression->toUnaryExpression()->expression()->accept(this); dvander@195: dvander@195: bool jumpOnTrue = (fallthrough == falseBranch) ^ (token == TOK_NOT); dvander@195: dvander@195: Opcode op = jumpOnTrue ? OP_JT : OP_JF; dvander@195: if (expression->type()->isFloat()) dvander@195: op = (Opcode)(int(op) + (OP_JL_F - OP_JL)); dvander@195: dvander@195: if (fallthrough == falseBranch) dvander@195: __ j(op, trueBranch); dvander@195: else dvander@195: __ j(op, falseBranch); dvander@195: return; dvander@195: } dvander@195: dvander@195: BinaryExpression *binary = expression->toBinaryExpression(); dvander@195: dvander@195: if (token == TOK_AND || token == TOK_OR) { dvander@195: Label next; dvander@195: if (token == TOK_AND) dvander@195: visitForTest(binary->left(), &next, falseBranch, &next); dvander@195: else dvander@195: visitForTest(binary->left(), trueBranch, &next, &next); dvander@195: __ bind(&next); dvander@195: visitForTest(binary->right(), trueBranch, falseBranch, fallthrough); dvander@195: return; dvander@195: } dvander@195: dvander@195: Opcode op = CompareTokenToJump(token); dvander@195: if (fallthrough == trueBranch) dvander@195: op = Invert(op); dvander@195: dvander@195: Local leftType(zone_, binary->left()->type()); dvander@195: Local rightType(zone_, binary->right()->type()); dvander@195: Local coerce(zone_, GetCoercionType(binary->token(), leftType, rightType)); dvander@195: dvander@195: visitForALU(coerce, binary->left()); dvander@195: visitForALU(coerce, binary->right()); dvander@195: if (coerce->isFloat()) dvander@195: op = (Opcode)(int(op) + (OP_JL_F - OP_JL)); dvander@195: dvander@195: if (fallthrough == trueBranch) dvander@195: __ j(op, falseBranch); dvander@195: else dvander@195: __ j(op, trueBranch); dvander@195: } dvander@195: dvander@195: void dvander@195: BytecodeCompiler::visit(ForStatement *node) dvander@195: { dvander@195: AutoEnterScope forScope(this, node->scope()); dvander@195: dvander@195: recordLifoSlot(node->scope()); dvander@195: dvander@195: Label join, update; dvander@195: LoopScope loop(scope_, &join, &update, &loop_); dvander@195: dvander@195: if (node->initialization()) dvander@195: node->initialization()->accept(this); dvander@195: dvander@195: Label header, test; dvander@195: if (node->condition()) dvander@195: __ jump(&test); dvander@195: dvander@195: __ bind(&header); dvander@195: node->body()->accept(this); dvander@195: dvander@195: __ bind(&update); dvander@195: if (node->update()) dvander@195: node->update()->accept(this); dvander@195: dvander@195: if (node->condition()) { dvander@195: __ bind(&test); dvander@195: visitForTest(node->condition(), &header, &join, &join); dvander@195: } else { dvander@195: __ jump(&header); dvander@195: } dvander@195: dvander@195: __ bind(&join); dvander@195: dvander@195: if (node->scope()) dvander@195: unwindTo(node->scope()->enclosing()); dvander@195: assert(__ stackDepth() == 0); dvander@195: } dvander@195: dvander@195: void dvander@195: BytecodeCompiler::visit(WhileStatement *node) dvander@195: { dvander@195: Label header, join; dvander@195: dvander@195: __ bind(&header); dvander@195: dvander@195: LoopScope loop(scope_, &join, &header, &loop_); dvander@195: dvander@195: if (node->token() == TOK_WHILE) { dvander@195: Label body; dvander@195: dvander@195: visitForTest(node->condition(), &body, &join, &body); dvander@195: __ bind(&body); dvander@195: node->body()->accept(this); dvander@195: __ jump(&header); dvander@195: } else { dvander@195: node->body()->accept(this); dvander@195: visitForTest(node->condition(), &header, &join, &join); dvander@195: } dvander@195: dvander@195: __ bind(&join); dvander@195: } dvander@195: dvander@195: void dvander@195: BytecodeCompiler::visit(BreakStatement *node) dvander@195: { dvander@195: unwindTo(loop_->scope()); dvander@195: __ jump(loop_->break_()); dvander@195: } dvander@195: dvander@195: void dvander@195: BytecodeCompiler::visit(ContinueStatement *node) dvander@195: { dvander@195: unwindTo(loop_->scope()); dvander@195: __ jump(loop_->continue_()); dvander@195: } dvander@195: dvander@195: void dvander@195: BytecodeCompiler::visit(BlockStatement *node) dvander@195: { dvander@195: AutoEnterScope blockScope(this, node->scope()); dvander@195: dvander@195: recordLifoSlot(node->scope()); dvander@195: dvander@195: for (size_t i = 0; i < node->statements()->length(); i++) { dvander@195: Statement *statement = node->statements()->at(i); dvander@195: statement->accept(this); dvander@195: assert(__ stackDepth() == 0); dvander@195: } dvander@195: dvander@195: // We don't bother emitting an "unwind" instruction for function scopes, dvander@195: // since this is taken care of by the calling convention. dvander@195: if (node->scope() && node->scope()->enclosing()->kind() != Scope::FUNCTION) dvander@195: unwindTo(node->scope()->enclosing()); dvander@195: } dvander@195: dvander@195: void dvander@195: BytecodeCompiler::visit(IntegerLiteral *node) dvander@195: { dvander@195: __ int_(node->value()); dvander@195: } dvander@195: dvander@195: void dvander@195: BytecodeCompiler::visit(BooleanLiteral *node) dvander@195: { dvander@195: if (node->token() == TOK_TRUE) dvander@195: __ true_(); dvander@195: else dvander@195: __ false_(); dvander@195: } dvander@195: dvander@195: void dvander@195: BytecodeCompiler::visit(FloatLiteral *node) dvander@195: { dvander@195: __ float_((float)node->value()); dvander@195: } dvander@195: dvander@195: void dvander@195: BytecodeCompiler::visit(StringLiteral *node) dvander@195: { dvander@195: Local array(zone_, StringLiteralToArray(zone_, node)); dvander@195: if (!array) dvander@195: return; dvander@195: dvander@195: __ note_position(node->pos()); dvander@195: __ object(array); dvander@195: } dvander@195: dvander@195: void dvander@195: BytecodeCompiler::getEnvSlot(Variable *var) dvander@195: { dvander@195: assert(var->scope()->kind() == Scope::GLOBAL); dvander@195: __ getglobal(var); dvander@195: } dvander@195: dvander@195: void dvander@195: BytecodeCompiler::setEnvSlot(Variable *var) dvander@195: { dvander@195: assert(var->scope()->kind() == Scope::GLOBAL); dvander@195: __ setglobal(var); dvander@195: } dvander@195: dvander@195: static bool dvander@195: FillArrayLiteral_String(Zone *zone, Handle array, ArrayLiteral *literal, unsigned *maxsize) dvander@195: { dvander@195: Local string(zone); dvander@195: for (size_t i = 0; i < literal->expressions()->length(); i++) { dvander@195: Expression *expr = literal->expressions()->at(i); dvander@195: StringLiteral *lit = expr->toStringLiteral(); dvander@195: if ((string = StringLiteralToArray(zone, lit)) == NULL) dvander@195: return false; dvander@195: dvander@195: array->writeObject(zone, i, string); dvander@195: dvander@195: if (string->length() > *maxsize) dvander@195: *maxsize = string->length(); dvander@195: dvander@195: assert(!array->type()->contained()->isFixedLength() || dvander@195: array->type()->contained()->fixedLength() >= string->length()); dvander@195: } dvander@195: dvander@195: return true; dvander@195: } dvander@195: dvander@195: static bool dvander@195: FillArrayLiteral_Primitive(Zone *zone, Handle array, ArrayLiteral *literal) dvander@195: { dvander@195: assert(array->type()->fixedLength() == literal->expressions()->length()); dvander@195: dvander@195: for (size_t i = 0; i < literal->expressions()->length(); i++) { dvander@195: Expression *expr = literal->expressions()->at(i); dvander@195: if (IntegerLiteral *lit = expr->asIntegerLiteral()) { dvander@195: array->writeInt32(i, lit->value()); dvander@195: continue; dvander@195: } dvander@195: if (BooleanLiteral *lit = expr->asBooleanLiteral()) { dvander@195: if (lit->token() == TOK_TRUE) dvander@195: array->writeInt32(i, 1); dvander@195: else dvander@195: array->writeInt32(i, 0); dvander@195: continue; dvander@195: } dvander@195: if (FloatLiteral *lit = expr->asFloatLiteral()) { dvander@195: array->writeFloat(i, (float)lit->value()); dvander@195: continue; dvander@195: } dvander@195: dvander@195: // Unrecognized literal type. dvander@195: assert(false); dvander@195: } dvander@195: dvander@195: return true; dvander@195: } dvander@195: dvander@195: static bool dvander@195: TraverseArrayLiteral(Zone *zone, ArrayLiteral *literal, Handle array, Handle type, dvander@195: unsigned *maxsize) dvander@195: { dvander@195: assert(type->fixedLength() == literal->expressions()->length()); dvander@195: dvander@195: Local contained(zone, type->contained()); dvander@195: if (contained->isCharArray()) dvander@195: return FillArrayLiteral_String(zone, array, literal, maxsize); dvander@195: dvander@195: if (!contained->isArray()) dvander@195: return FillArrayLiteral_Primitive(zone, array, literal); dvander@195: dvander@195: Local subarray(zone); dvander@195: for (size_t i = 0; i < literal->expressions()->length(); i++) { dvander@195: Expression *expr = literal->expressions()->at(i); dvander@195: subarray = Array::cast(array->readObject(i)); dvander@195: if (!TraverseArrayLiteral(zone, expr->toArrayLiteral(), subarray, contained, maxsize)) dvander@195: return false; dvander@195: } dvander@195: dvander@195: return true; dvander@195: } dvander@195: dvander@195: static Array * dvander@195: BuildArrayLiteral(Zone *zone, ArrayLiteral *literal, Handle map, unsigned *maxsize) dvander@195: { dvander@195: Local root(zone, Array::NewFixed(zone, map, Heap::Tenure_Old)); dvander@195: if (!root) dvander@195: return NULL; dvander@195: dvander@195: // This should have allocated everything in the array, so all we need to dvander@195: // do is traverse down to the lowest levels and copy things. dvander@195: Local type(zone, map->type()); dvander@195: if (!TraverseArrayLiteral(zone, literal, root, type, maxsize)) dvander@195: return NULL; dvander@195: dvander@195: return root; dvander@195: } dvander@195: dvander@195: static inline bool dvander@195: ArrayContainsCharBuffers(Type *type) dvander@195: { dvander@195: if (type->contained()->isArray()) dvander@195: return ArrayContainsCharBuffers(type->contained()); dvander@195: return type->isCharArray(); dvander@195: } dvander@195: dvander@195: void dvander@195: BytecodeCompiler::visit(VariableDeclaration *node) dvander@195: { dvander@195: Variable *var = node->variable(); dvander@195: dvander@195: Local type(zone_, var->type()); dvander@195: if (type->isArray()) { dvander@195: Local map(zone_, ArrayMap::Attach(zone_, type)); dvander@195: __ note_position(node->pos()); dvander@195: dvander@195: Local right(zone_); dvander@195: if (node->initialization()) dvander@195: right = node->initialization()->type(); dvander@195: dvander@195: // There are a few methods for allocating, here: dvander@195: // (1) If the array is fixed, dvander@195: // (a) Initializer not present, dvander@195: // NEWFIXED dvander@195: // (b) Left is const non-string: dvander@195: // dvander@195: // (b) Otherwise: dvander@195: // NEWFIXED dvander@195: // COPYARRAY dvander@195: // (2) If the array is not fixed, dvander@195: // (a) Initializer is not present, dvander@195: // NEWEMPTY dvander@195: // (b) Initializer is not a string literal, dvander@195: // or left is const: dvander@195: // dvander@195: // (c) Initializer is a string literal, dvander@195: // NEWSIZED dvander@195: // COPYARRAY dvander@195: // dvander@195: dvander@195: Heap::Tenure tenure = var->scope()->kind() == Scope::GLOBAL dvander@195: ? Heap::Tenure_Old dvander@195: : Heap::Tenure_Default; dvander@195: dvander@195: if (Expression *init = node->initialization()) { dvander@195: if (init->isArrayLiteral()) { dvander@195: unsigned maxsize = 0; dvander@195: Local array(zone_, BuildArrayLiteral(zone_, init->toArrayLiteral(), map, &maxsize)); dvander@195: if (!array) dvander@195: return; dvander@195: dvander@195: assert(type->isFixedLength()); dvander@195: dvander@195: if (type->isConst()) { dvander@195: __ object(array); dvander@195: } else if (ArrayContainsCharBuffers(type)) { dvander@195: // This array contains mutable char buffers, so dvander@195: // unfortunately this gets a little more complicated. We dvander@195: // compute the maximum size needed for the innermost dvander@195: // string, which is similar to SP1. This means we can use dvander@195: // CopyArray rather than individually emit opcodes to clone dvander@195: // every string. dvander@195: Local child(zone_, type); dvander@195: for (size_t i = 0; i < type->levels() - 1; i++) { dvander@195: __ int_(child->fixedLength()); dvander@195: child = child->contained(); dvander@195: } dvander@195: __ int_(maxsize); dvander@195: __ newarray(OP_NEWSIZED, map, tenure); dvander@195: __ object(array); dvander@195: __ copyarray(); dvander@195: } else { dvander@195: __ newarray(OP_NEWFIXED, map, tenure); dvander@195: __ object(array); dvander@195: __ copyarray(); dvander@195: } dvander@195: } else if (type->isFixedLength()) { dvander@195: // Note in the case of: dvander@195: // const String:buffer[50] = "hello"; dvander@195: // dvander@195: // We want to preserve sizeof(buffer) == 50. dvander@195: if (type->isConst() && !type->isCharArray()) { dvander@195: init->accept(this); dvander@195: } else { dvander@195: __ newarray(OP_NEWFIXED, map, tenure); dvander@195: init->accept(this); dvander@195: __ copyarray(); dvander@195: } dvander@195: } else if (!type->isConst() && init->isStringLiteral()) { dvander@195: // This is a degenerate case of ArrayLiteral. dvander@195: __ int_(right->fixedLength()); dvander@195: __ newarray(OP_NEWSIZED, map, tenure); dvander@195: init->accept(this); dvander@195: __ copyarray(); dvander@195: } else if (type->isConst()) { dvander@195: init->accept(this); dvander@195: } dvander@195: } else if (type->isFixedLength()) { dvander@195: __ newarray(OP_NEWFIXED, map, tenure); dvander@195: } else { dvander@195: #if 0 dvander@195: if (node->postDimensions()) { dvander@195: for (size_t i = 0; i < node->postDimensions()->length(); i++) { dvander@195: Expression *expr = node->postDimensions()->at(i); dvander@195: if (expr) dvander@195: expr->accept(this); dvander@195: else dvander@195: __ int_(0); dvander@195: } dvander@195: __ newarray(OP_NEWSIZED, map, tenure); dvander@195: } else { dvander@195: __ newarray(OP_NEWEMPTY, map, tenure); dvander@195: } dvander@195: #endif dvander@195: } dvander@195: } else if (type->isStruct()) { dvander@195: Local map(zone_, StructMap::cast(type->newMap())); dvander@195: dvander@195: // Because structs can contain arrays, any sort of composition dvander@195: // triggers non-LIFO allocation. This is just for correctness. dvander@195: // We could do better by: dvander@195: // (1) Limiting to structs that just (recursively) contain arrays, dvander@195: // (2) Swap Stack for Default tenure in Struct::New dvander@195: // (3) JIT analysis dvander@195: if (var->scope()->kind() == Scope::GLOBAL) dvander@195: __ newstruct(map, Heap::Tenure_Old); dvander@195: else if (var->needsHeapAllocation() || map->composed()) dvander@195: __ newstruct(map, Heap::Tenure_Default); dvander@195: else dvander@195: __ newstruct(map, Heap::Tenure_Stack); dvander@195: } else { dvander@195: if (node->initialization()) { dvander@195: node->initialization()->accept(this); dvander@195: } else { dvander@195: if (type->isInt32OrEnum() || type->isBool()) { dvander@195: __ int_(0); dvander@195: } else { dvander@195: assert(type->isFloat()); dvander@195: __ float_(0); dvander@195: } dvander@195: } dvander@195: } dvander@195: dvander@195: if (var->location() == Variable::STACK) { dvander@195: __ setlocal(var); dvander@195: locals_->set(zone_, var->slot(), var->type()); dvander@195: } else { dvander@195: assert(var->location() == Variable::ENVIRONMENT); dvander@195: setEnvSlot(var); dvander@195: } dvander@195: dvander@195: __ pop(); dvander@195: } dvander@195: dvander@195: void dvander@195: BytecodeCompiler::visit(CallExpression *node) dvander@195: { dvander@195: #if 0 dvander@195: Local funType(zone_, node->callee()->type()); dvander@195: dvander@195: // Sniff out natives, which have a specific opcode and are guaranteed to dvander@195: // be called via name proxy. dvander@195: NamedFunction *native = NULL; dvander@195: if (funType->isNative()) dvander@195: native = node->callee()->toNameProxy()->binding()->toNamedFunction(); dvander@195: dvander@195: Local given(zone_); dvander@195: for (size_t i = node->arguments()->length() - 1; i < node->arguments()->length(); i--) { dvander@195: Expression *arg = node->arguments()->at(i); dvander@195: dvander@195: given = arg->type(); dvander@195: dvander@195: // We need to compute a reference if the parameter was in the form: dvander@195: // f(&a) dvander@195: // Note this form does not allow: f({}) dvander@195: // dvander@195: // We also need to compute references for all variadic native dvander@195: // arguments, but arrays and references are already in the dvander@195: // appropriate form. dvander@195: bool mustPushReference = false; dvander@195: if (i >= funType->parameters()->length()) { dvander@195: assert(funType->isNative() && funType->isNativeVariadic()); dvander@195: mustPushReference = !given->isArray() && !given->isReference(); dvander@195: } else if (funType->parameterAt(i)->isReference()) { dvander@195: mustPushReference = !given->isReference(); dvander@195: } dvander@195: dvander@195: // If a native is passed to a native, it must only be one level deep. dvander@195: assert(!funType->isNative() || !given->isArray() || given->levels() == 1); dvander@195: dvander@195: if (mustPushReference) { dvander@195: if (arg->isNameProxy()) { dvander@195: // :TODO: test with functions and globals dvander@195: // Compute a reference to the name. dvander@195: Variable *var = arg->toNameProxy()->binding()->toVariable(); dvander@195: __ note_position(node->pos()); dvander@195: __ newref(var); dvander@195: } else if (arg->isFieldExpression()) { dvander@195: FieldExpression *expr = arg->toFieldExpression(); dvander@195: expr->left()->accept(this); dvander@195: __ note_position(expr->pos()); dvander@195: __ fieldref_lifo(expr->fieldIndex()); dvander@195: } else { dvander@195: // We don't know how to, or can't, build a direct reference to dvander@195: // this type. Make an indirect one, by taking the top of the dvander@195: // stack and boxing it up. dvander@195: arg->accept(this); dvander@195: __ note_position(node->pos()); dvander@195: __ slotref_lifo(); dvander@195: } dvander@195: } else { dvander@195: arg->accept(this); dvander@195: } dvander@195: } dvander@195: dvander@195: unsigned argc = node->arguments()->length(); dvander@195: dvander@195: // If there are missing arguments, then we're not done and have to push dvander@195: // default parameters. dvander@195: Local box(zone_); dvander@195: Local array(zone_); dvander@195: Local map(zone_); dvander@195: for (size_t i = node->arguments()->length(); i < funType->parameters()->length(); i++) { dvander@195: box = Box::cast(funType->defaults()->at(i)); dvander@195: dvander@195: if (box->type()->isPod()) { dvander@195: if (box->type()->pod() == PrimitiveType_Int32) dvander@195: __ int_(box->toInt32()); dvander@195: else if (box->type()->pod() == PrimitiveType_Float) dvander@195: __ float_(box->toFloat()); dvander@195: else dvander@195: assert(false); dvander@195: dvander@195: if (funType->parameterAt(i)->isReference()) { dvander@195: __ note_position(node->pos()); dvander@195: __ slotref_lifo(); dvander@195: } dvander@195: } else { dvander@195: assert(box->type()->isArray()); dvander@195: dvander@195: array = Array::cast(box->toObject()); dvander@195: if (funType->parameterAt(i)->isConst()) { dvander@195: __ object(array); dvander@195: } else { dvander@195: // We need to copy the array since it's mutable. Note that dvander@195: // although COPYARRAY returns its left-hand side, this may dvander@195: // change in the future, we do the same thing and dupe the dvander@195: // newly allocated array. dvander@195: map = ArrayMap::cast(array->map()); dvander@195: dvander@195: // Natives cannot escape array references, so we can allocate dvander@195: // them on the LIFO stack. dvander@195: if (funType->isNative()) dvander@195: __ newarray(OP_NEWFIXED, map, Heap::Tenure_Stack); dvander@195: else dvander@195: __ newarray(OP_NEWFIXED, map, Heap::Tenure_Default); dvander@195: __ dup(); dvander@195: __ object(array); dvander@195: __ copyarray(); dvander@195: __ pop(); dvander@195: } dvander@195: } dvander@195: dvander@195: argc++; dvander@195: } dvander@195: dvander@195: __ note_position(node->pos()); dvander@195: if (native) { dvander@195: __ callnative(native->slot(), argc); dvander@195: __ drop(argc); dvander@195: } else { dvander@195: node->callee()->accept(this); dvander@195: __ call(funType); dvander@195: __ drop(argc + 1); dvander@195: } dvander@195: #endif dvander@195: } dvander@195: dvander@195: void dvander@195: BytecodeCompiler::visit(TernaryExpression *node) dvander@195: { dvander@195: Label success, failure, join; dvander@195: dvander@195: visitForTest(node->condition(), &success, &failure, &success); dvander@195: __ bind(&success); dvander@195: node->left()->accept(this); dvander@195: __ drop(1); dvander@195: __ jump(&join); dvander@195: __ bind(&failure); dvander@195: node->right()->accept(this); dvander@195: __ bind(&join); dvander@195: } dvander@195: dvander@195: void dvander@195: BytecodeCompiler::visit(IfStatement *node) dvander@195: { dvander@195: Label join; dvander@195: if (!node->ifFalse()) { dvander@195: Label success; dvander@195: dvander@195: visitForTest(node->condition(), &success, &join, &success); dvander@195: __ bind(&success); dvander@195: node->ifTrue()->accept(this); dvander@195: } else { dvander@195: Label success, next; dvander@195: dvander@195: visitForTest(node->condition(), &success, &next, &success); dvander@195: __ bind(&success); dvander@195: node->ifTrue()->accept(this); dvander@195: __ jump(&join); dvander@195: __ bind(&next); dvander@195: node->ifFalse()->accept(this); dvander@195: } dvander@195: dvander@195: __ bind(&join); dvander@195: } dvander@195: dvander@195: bool dvander@195: BytecodeCompiler::tableswitch(SwitchStatement *node) dvander@195: { dvander@195: CaseValueList *values = node->caseValueList(); dvander@195: dvander@195: if (values->length() < 3) dvander@195: return false; dvander@195: dvander@195: // Find the delta between the largest and smallest value. dvander@195: int hi = values->at(values->length() - 1).box.toInt(); dvander@195: int lo = values->at(0).box.toInt(); dvander@195: assert(hi > lo); dvander@195: dvander@195: unsigned tablesize = TableSwitchEntries(lo, hi); dvander@195: if (tablesize > MAX_TABLESWITCH_ENTRIES) dvander@195: return false; dvander@195: dvander@195: Label def, join; dvander@195: dvander@195: // Create the list of labels that each statement will bind to. dvander@195: Label *labels = new Label[node->cases()->length()]; dvander@195: dvander@195: // Create the table of labels we'll use for each case. dvander@195: Label **table = new Label *[tablesize]; dvander@195: dvander@195: // Link all cases, by default, to the default label. dvander@195: for (size_t i = 0; i < tablesize; i++) dvander@195: table[i] = &def; dvander@195: dvander@195: // Now patch actual cases to their correct labels. dvander@195: for (size_t i = 0; i < values->length(); i++) { dvander@195: CaseValue &v = values->at(i); dvander@195: assert(v.statement < node->cases()->length()); dvander@195: dvander@195: unsigned index = unsigned(v.box.toInt() - lo); dvander@195: assert(index < tablesize); dvander@195: dvander@195: table[index] = &labels[v.statement]; dvander@195: } dvander@195: dvander@195: // Emit the tableswitch opcode and the jump table. dvander@195: __ tableswitch(lo, hi, &def, table); dvander@195: dvander@195: // We're done with the jump table now. dvander@195: delete [] table; dvander@195: dvander@195: // Emit each statement. dvander@195: for (size_t i = 0; i < node->cases()->length(); i++) { dvander@195: Statement *stmt = node->cases()->at(i)->statement(); dvander@195: dvander@195: __ bind(&labels[i]); dvander@195: stmt->accept(this); dvander@195: __ jump(&join); dvander@195: } dvander@195: dvander@195: // We're done with the statement labels. dvander@195: delete [] labels; dvander@195: dvander@195: // Emit the default case. dvander@195: __ bind(&def); dvander@195: if (node->defaultCase()) dvander@195: node->defaultCase()->accept(this); dvander@195: dvander@195: __ bind(&join); dvander@195: dvander@195: return true; dvander@195: } dvander@195: dvander@195: void dvander@195: BytecodeCompiler::visit(SwitchStatement *node) dvander@195: { dvander@195: node->expression()->accept(this); dvander@195: dvander@195: if (tableswitch(node)) dvander@195: return; dvander@195: dvander@195: Label join; dvander@195: dvander@195: for (size_t i = 0; i < node->cases()->length(); i++) { dvander@195: Case *c = node->cases()->at(i); dvander@195: dvander@195: Label success, fail; dvander@195: if (c->others()) { dvander@195: for (size_t j = 0; j < c->others()->length(); j++) { dvander@195: Expression *expr = c->others()->at(j); dvander@195: dvander@195: __ dup(); dvander@195: expr->accept(this); dvander@195: __ j(OP_JEQ, &success); dvander@195: } dvander@195: } dvander@195: dvander@195: // The final test. dvander@195: __ dup(); dvander@195: c->expression()->accept(this); dvander@195: __ j(OP_JNE, &fail); dvander@195: __ bind(&success); dvander@195: c->statement()->accept(this); dvander@195: __ jump(&join); dvander@195: __ bind(&fail); dvander@195: } dvander@195: dvander@195: if (node->defaultCase()) dvander@195: node->defaultCase()->accept(this); dvander@195: dvander@195: __ pop(); dvander@195: __ bind(&join); dvander@195: } dvander@195: dvander@195: void dvander@195: BytecodeCompiler::visit(FunctionStatement *node) dvander@195: { dvander@195: if (!node->fun()->used()) dvander@195: return; dvander@195: dvander@195: if (node->fun()->isNative()) { dvander@195: assert(!moduleData_->natives->at(node->fun()->slot())); dvander@195: dvander@195: Local type(zone_, FunctionType::cast(node->fun()->type())); dvander@195: Local name(zone_, node->fun()->name()); dvander@195: Local native(zone_, Native::New(zone_, type, name)); dvander@195: if (!native) dvander@195: return; dvander@195: dvander@195: moduleData_->natives->set(zone_, node->fun()->slot(), native); dvander@195: return; dvander@195: } dvander@195: dvander@195: // If the function has no body, then it's just a declaration. dvander@195: if (!node->body()) { dvander@195: assert(node->fun()->isForward() || node->fun()->isPublic()); dvander@195: return; dvander@195: } dvander@195: dvander@195: BytecodeCompiler child(zone_, tree_, moduleData_); dvander@195: Local fun(zone_, child.compileFunction(moduleData_, node)); dvander@195: if (!fun) dvander@195: return; dvander@195: dvander@195: assert(!moduleData_->module->getFunction(node->fun()->slot())); dvander@195: moduleData_->module->setFunction(zone_, node->fun()->slot(), fun); dvander@195: } dvander@195: dvander@195: void dvander@195: BytecodeCompiler::visit(FunctionTypeStatement *node) dvander@195: { dvander@195: } dvander@195: dvander@195: void dvander@195: BytecodeCompiler::visit(EnumStatement *node) dvander@195: { dvander@195: } dvander@195: dvander@195: void dvander@195: BytecodeCompiler::visit(StructureStatement *node) dvander@195: { dvander@195: } dvander@195: dvander@195: void dvander@195: BytecodeCompiler::visit(ArrayLiteral *node) dvander@195: { dvander@195: // We shouldn't reach these; they're handled manually when initializing dvander@195: // declared arrays. dvander@195: assert(false); dvander@195: } dvander@195: dvander@195: void dvander@195: BytecodeCompiler::visit(ImportStatement *node) dvander@195: { dvander@195: } dvander@195: dvander@195: Code * dvander@195: BytecodeCompiler::compileCode(Scope *scope, unsigned lineno) dvander@195: { dvander@195: // STOP doubles as RETURNVOID. dvander@195: __ stop(); dvander@195: dvander@195: if (__ oom() || zone_->hasPendingException()) dvander@195: return NULL; dvander@195: dvander@195: Local bytes(zone_, ByteArray::New(zone_, __ length() * sizeof(bytecode), Heap::Tenure_Old)); dvander@195: if (!bytes) dvander@195: return NULL; dvander@195: memcpy(bytes->raw(), __ buffer(), __ length()); dvander@195: dvander@195: for (size_t i = 0; i < scope->maxContainedLocals(); i++) dvander@195: assert(locals_->at(i)); dvander@195: dvander@195: Local objects(zone_, FixedArray::New(zone_, __ objects()->length(), Heap::Tenure_Old)); dvander@195: if (!objects) dvander@195: return NULL; dvander@195: dvander@195: for (size_t i = 0; i < __ objects()->length(); i++) dvander@195: objects->set(zone_, i, *(__ objects()->at(i))); dvander@195: dvander@195: Local safepoints(zone_); dvander@195: dvander@195: // If any stack slots hold gc pointers at potential failure points in the dvander@195: // bytecode, record them now. We do this by building a list of tuples; dvander@195: // each tuple contains the pc offset, and a bitset containing which stack dvander@195: // slots must be scanned by the gc. dvander@195: if (__ oopRecords()->length()) { dvander@195: size_t bytesPerSafepoint = Code::BytesPerSafepoint(__ maxStackDepth()); dvander@195: size_t gcmapSize = bytesPerSafepoint * __ oopRecords()->length(); dvander@195: safepoints = ByteArray::New(zone_, gcmapSize, Heap::Tenure_Old); dvander@195: if (!safepoints) dvander@195: return NULL; dvander@195: dvander@195: Address cursor = safepoints->raw(); dvander@195: for (size_t i = 0; i < __ oopRecords()->length(); i++) { dvander@195: OopRecord *record = __ oopRecords()->at(i); dvander@195: assert(record->depth <= __ maxStackDepth()); dvander@195: dvander@195: *reinterpret_cast(cursor) = record->offset; dvander@195: dvander@195: unsigned char *bits = reinterpret_cast(cursor + sizeof(unsigned)); dvander@195: for (unsigned i = 0; i < record->depth; i++) dvander@195: bits[i / 8] |= (1 << (i % 8)); dvander@195: dvander@195: cursor += bytesPerSafepoint; dvander@195: } dvander@195: dvander@195: assert(cursor == safepoints->raw() + gcmapSize); dvander@195: } dvander@195: dvander@195: Local sourcemap(zone_); dvander@195: if (__ sourcemapLength()) { dvander@195: sourcemap = ByteArray::New(zone_, __ sourcemapLength(), Heap::Tenure_Old); dvander@195: if (!sourcemap) dvander@195: return NULL; dvander@195: dvander@195: memcpy(sourcemap->raw(), __ sourcemap(), __ sourcemapLength()); dvander@195: } dvander@195: dvander@195: Local code(zone_); dvander@195: dvander@195: code = Code::New(zone_, dvander@195: bytes, dvander@195: locals_, dvander@195: objects, dvander@195: __ maxStackDepth(), dvander@195: moduleData_->module, dvander@195: safepoints, dvander@195: sourcemap, dvander@195: lineno); dvander@195: return code; dvander@195: } dvander@195: dvander@195: Function * dvander@195: BytecodeCompiler::compileFunction(ModuleData *module, FunctionStatement *node) dvander@195: { dvander@195: // Note: No need to record a life slot here, since it's part of the Frame. dvander@195: AutoEnterScope functionScope(this, node->scope()); dvander@195: dvander@195: // Set the initial line, because line mappings are deltas. dvander@195: __ setFirstLine(node->pos().line); dvander@195: locals_ = FixedArray::New(zone_, node->scope()->maxContainedLocals(), Heap::Tenure_Old); dvander@195: dvander@195: node->body()->accept(this); dvander@195: dvander@195: unsigned nargs = node->scope()->numArguments(); dvander@195: Local args(zone_, FixedArray::New(zone_, nargs, Heap::Tenure_Old)); dvander@195: if (!args) dvander@195: return NULL; dvander@195: dvander@195: for (size_t i = 0; i < node->scope()->numArguments(); i++) dvander@195: args->set(zone_, i, node->scope()->getArgument(i)->type()); dvander@195: dvander@195: dvander@195: Local code(zone_, compileCode(node->scope(), node->pos().line)); dvander@195: if (!code) dvander@195: return NULL; dvander@195: dvander@195: Local type(zone_, FunctionType::cast(node->fun()->type())); dvander@195: Local name(zone_, node->fun()->name()); dvander@195: Local fun(zone_, Function::New(zone_, code, type, name, node->pos())); dvander@195: if (!fun) dvander@195: return NULL; dvander@195: dvander@195: // If this function had a forward, export it as a named public. dvander@195: if (node->fun()->isPublic()) { dvander@195: assert(node->fun()->isPublic()); dvander@195: ScopedRoot root(fun); dvander@195: if (!root) dvander@195: return NULL; dvander@195: if (!moduleData_->publics.append(root)) dvander@195: return NULL; dvander@195: } dvander@195: dvander@195: #ifndef NDEBUG dvander@195: Disassemble(code, stdout); dvander@195: #endif dvander@195: dvander@195: return fun; dvander@195: } dvander@195: dvander@195: Module * dvander@195: BytecodeCompiler::compile() dvander@195: { dvander@195: AutoEnterScope scope(this, tree_->globalScope()); dvander@195: dvander@195: ModuleData moduleData; dvander@195: dvander@195: moduleData_ = &moduleData; dvander@195: moduleData_->module = Module::New(zone_, tree_->globalScope()->numFunctions()); dvander@195: dvander@195: if (!moduleData_) dvander@195: return NULL; dvander@195: dvander@195: moduleData_->natives = FixedArray::New(zone_, tree_->globalScope()->numNatives(), Heap::Tenure_Old); dvander@195: if (!moduleData_->natives) dvander@195: return NULL; dvander@195: dvander@195: for (size_t i = 0; i < tree_->statements()->length(); i++) { dvander@195: Statement *statement = tree_->statements()->at(i); dvander@195: statement->accept(this); dvander@195: } dvander@195: dvander@195: if (zone_->hasPendingException()) dvander@195: return NULL; dvander@195: dvander@195: #ifndef NDEBUG dvander@195: for (size_t i = 0; i < tree_->globalScope()->numFunctions(); i++) dvander@195: assert(moduleData_->module->getFunction(i)); dvander@195: for (size_t i = 0; i < tree_->globalScope()->numNatives(); i++) dvander@195: assert(moduleData_->natives->at(i)); dvander@195: #endif dvander@195: dvander@195: if (moduleData_->publics.length()) { dvander@195: Local fun(zone_); dvander@195: Local list(zone_, FixedArray::New(zone_, moduleData_->publics.length(), dvander@195: Heap::Tenure_Old)); dvander@195: if (!list) dvander@195: return NULL; dvander@195: dvander@195: for (size_t i = 0; i < moduleData_->publics.length(); i++) dvander@195: list->set(zone_, i, moduleData_->publics[i]); dvander@195: dvander@195: moduleData_->module->setPublics(list); dvander@195: } dvander@195: dvander@195: moduleData_->module->setNatives(moduleData_->natives); dvander@195: dvander@195: if (scope_->numEnvironmentVars()) { dvander@195: Local globalMap(zone_, scope_->createEnvironmentMap(zone_)); dvander@195: if (!globalMap) dvander@195: return NULL; dvander@195: dvander@195: Local environment(zone_, Environment::New(zone_, globalMap)); dvander@195: if (!environment) dvander@195: return NULL; dvander@195: dvander@195: moduleData_->module->setEnvironment(environment); dvander@195: } dvander@195: dvander@195: Local code(zone_, compileCode(tree_->globalScope(), 1)); dvander@195: if (!code) dvander@195: return NULL; dvander@195: dvander@195: Disassemble(code, stdout); dvander@195: dvander@195: moduleData_->module->setCode(code); dvander@195: dvander@195: return moduleData_->module; dvander@195: }