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