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 "AST.h" asherkin@263: #include "HIR.h" asherkin@263: #include "BytecodeEmitter.h" asherkin@263: #include "../Strings.h" asherkin@263: #include "../Heap-inl.h" asherkin@263: #include "../Handles-inl.h" asherkin@263: asherkin@263: using namespace ke; asherkin@263: asherkin@263: class HIRTranslator : public HIRVisitor asherkin@263: { asherkin@263: public: asherkin@263: HIRTranslator(Zone *zone, BytecodeEmitter &emitter) asherkin@263: : zone_(zone), asherkin@263: emitter_(emitter) asherkin@263: { asherkin@263: } asherkin@263: asherkin@263: void translate(HIR *hir) { asherkin@263: hir->accept(this); asherkin@263: } asherkin@263: asherkin@263: void visitBoolean(HBoolean *hir); asherkin@263: void visitFloat(HFloat *hir); asherkin@263: void visitInteger(HInteger *hir); asherkin@263: void visitArray(HArray *hir); asherkin@263: void visitCall(HCall *call); asherkin@263: void visitStore(HStore *store); asherkin@263: void visitUnary(HUnary *unary); asherkin@263: void visitBinary(HBinary *binary); asherkin@263: void visitLocal(HLocal *local); asherkin@263: void visitGlobal(HGlobal *global); asherkin@263: void visitConvert(HConvert *convert); asherkin@263: void visitIndex(HIndex *index); asherkin@263: void visitField(HField *field); asherkin@263: void visitPostIncDec(HPostIncDec *hir); asherkin@263: void visitToRef(HToRef *hir); asherkin@263: void visitNewDependentArray(HNewDependentArray *hir); asherkin@263: void visitImport(HImport *import); asherkin@263: asherkin@263: private: asherkin@263: Zone *zone_; asherkin@263: BytecodeEmitter &emitter_; asherkin@263: }; asherkin@263: asherkin@263: #define __ emitter_. asherkin@263: asherkin@263: void asherkin@263: HIRTranslator::visitBoolean(HBoolean *hir) asherkin@263: { asherkin@263: if (hir->value()) asherkin@263: __ true_(); asherkin@263: else asherkin@263: __ false_(); asherkin@263: } asherkin@263: asherkin@263: void asherkin@263: HIRTranslator::visitFloat(HFloat *hir) asherkin@263: { asherkin@263: __ float_(hir->value()); asherkin@263: } asherkin@263: asherkin@263: void asherkin@263: HIRTranslator::visitInteger(HInteger *hir) asherkin@263: { asherkin@263: __ int_(hir->value()); asherkin@263: } asherkin@263: asherkin@263: void asherkin@263: HIRTranslator::visitArray(HArray *hir) asherkin@263: { asherkin@263: __ object(hir->array()); asherkin@263: asherkin@263: if (hir->duplicate()) asherkin@263: __ duparray(); asherkin@263: } asherkin@263: asherkin@263: void asherkin@263: HIRTranslator::visitCall(HCall *call) asherkin@263: { asherkin@263: Local fun(zone_, FunctionType::cast(call->callee()->type())); asherkin@263: asherkin@263: assert(call->args()->length() == fun->parameters()->length()); asherkin@263: asherkin@263: __ note_position(call->node()->pos()); asherkin@263: asherkin@263: unsigned argc = call->args()->length(); asherkin@263: for (size_t i = call->args()->length() - 1; i < call->args()->length(); i--) { asherkin@263: HIR *hir = call->args()->at(i); asherkin@263: hir->accept(this); asherkin@263: } asherkin@263: asherkin@263: call->callee()->accept(this); asherkin@263: __ call(fun, argc); asherkin@263: } asherkin@263: asherkin@263: void asherkin@263: HGlobal::bind(HIRVisitor *visitor, BytecodeEmitter &emitter_) asherkin@263: { asherkin@263: } asherkin@263: asherkin@263: void asherkin@263: HGlobal::hold(BytecodeEmitter &emitter_) asherkin@263: { asherkin@263: } asherkin@263: asherkin@263: void asherkin@263: HGlobal::swapAndPick(BytecodeEmitter &emitter_) asherkin@263: { asherkin@263: __ dup(); asherkin@263: } asherkin@263: asherkin@263: void asherkin@263: HGlobal::store(BytecodeEmitter &emitter_) asherkin@263: { asherkin@263: __ setglobal(sym_); asherkin@263: } asherkin@263: asherkin@263: void asherkin@263: HGlobal::load(BytecodeEmitter &emitter_) asherkin@263: { asherkin@263: __ getglobal(sym_); asherkin@263: } asherkin@263: asherkin@263: bool asherkin@263: HGlobal::ref(BytecodeEmitter &emitter_) asherkin@263: { asherkin@263: return false; asherkin@263: } asherkin@263: asherkin@263: static inline Type * asherkin@263: MaybeDereference(Type *type) asherkin@263: { asherkin@263: if (type->isReference()) asherkin@263: return ReferenceType::cast(type)->contained(); asherkin@263: return type; asherkin@263: } asherkin@263: asherkin@263: HLocal::HLocal(AstNode *node, VariableSymbol *sym) asherkin@263: : HLValue(node, MaybeDereference(sym->type())), asherkin@263: sym_(sym) asherkin@263: { asherkin@263: } asherkin@263: asherkin@263: void asherkin@263: HLocal::bind(HIRVisitor *visitor, BytecodeEmitter &emitter_) asherkin@263: { asherkin@263: if (sym_->type()->isReference()) { asherkin@263: if (sym_->storage() == VariableSymbol::Local) asherkin@263: __ getlocal(sym_); asherkin@263: else if (sym_->storage() == VariableSymbol::Arg) asherkin@263: __ getarg(sym_); asherkin@263: else asherkin@263: assert(false); asherkin@263: } asherkin@263: } asherkin@263: asherkin@263: void asherkin@263: HLocal::hold(BytecodeEmitter &emitter_) asherkin@263: { asherkin@263: if (sym_->type()->isReference()) asherkin@263: __ dup(); asherkin@263: } asherkin@263: asherkin@263: void asherkin@263: HLocal::swapAndPick(BytecodeEmitter &emitter_) asherkin@263: { asherkin@263: if (sym_->type()->isReference()) { asherkin@263: __ swap(); asherkin@263: __ pick2(); asherkin@263: } else { asherkin@263: __ dup(); asherkin@263: } asherkin@263: } asherkin@263: asherkin@263: void asherkin@263: HLocal::store(BytecodeEmitter &emitter_) asherkin@263: { asherkin@263: if (sym_->type()->isReference()) { asherkin@263: __ setref(ReferenceType::cast(sym_->type())->contained()); asherkin@263: } else { asherkin@263: if (sym_->storage() == VariableSymbol::Local) asherkin@263: __ setlocal(sym_); asherkin@263: else if (sym_->storage() == VariableSymbol::Arg) asherkin@263: __ setarg(sym_); asherkin@263: else asherkin@263: assert(false); asherkin@263: } asherkin@263: } asherkin@263: asherkin@263: void asherkin@263: HLocal::load(BytecodeEmitter &emitter_) asherkin@263: { asherkin@263: if (sym_->type()->isReference()) asherkin@263: __ getref(ReferenceType::cast(sym_->type())->contained()); asherkin@263: else if (sym_->storage() == VariableSymbol::Local) asherkin@263: __ getlocal(sym_); asherkin@263: else if (sym_->storage() == VariableSymbol::Arg) asherkin@263: __ getarg(sym_); asherkin@263: else asherkin@263: assert(false); asherkin@263: } asherkin@263: asherkin@263: bool asherkin@263: HLocal::ref(BytecodeEmitter &emitter_) asherkin@263: { asherkin@263: if (sym_->storage() == VariableSymbol::Heap) { asherkin@263: // Closing over a reference should never pass SemA. asherkin@263: assert(!sym_->type()->isReference()); asherkin@263: asherkin@263: // We don't support direct references to heap-allocated locals. This asherkin@263: // should probably be banned in SemA? But for now, just assert. :TODO: asherkin@263: assert(false); asherkin@263: return false; asherkin@263: } asherkin@263: asherkin@263: if (sym_->type()->isReference()) { asherkin@263: if (sym_->storage() == VariableSymbol::Local) asherkin@263: __ getlocal(sym_); asherkin@263: else if (sym_->storage() == VariableSymbol::Arg) asherkin@263: __ getarg(sym_); asherkin@263: else asherkin@263: assert(false); asherkin@263: } else { asherkin@263: __ newref(sym_); asherkin@263: } asherkin@263: asherkin@263: return true; asherkin@263: } asherkin@263: asherkin@263: void asherkin@263: HIndex::bind(HIRVisitor *visitor, BytecodeEmitter &emitter_) asherkin@263: { asherkin@263: __ note_position(node()->pos()); asherkin@263: left_->accept(visitor); asherkin@263: right_->accept(visitor); asherkin@263: } asherkin@263: asherkin@263: void asherkin@263: HIndex::hold(BytecodeEmitter &emitter_) asherkin@263: { asherkin@263: __ dup2(); asherkin@263: } asherkin@263: asherkin@263: void asherkin@263: HIndex::swapAndPick(BytecodeEmitter &emitter_) asherkin@263: { asherkin@263: // BASE INDEX VALUE asherkin@263: __ roll3(); // INDEX VALUE BASE asherkin@263: __ roll3(); // VALUE BASE INDEX asherkin@263: __ pick3(); // VALUE BASE INDEX VALUE asherkin@263: } asherkin@263: asherkin@263: void asherkin@263: HIndex::store(BytecodeEmitter &emitter_) asherkin@263: { asherkin@263: __ setelem(type()); asherkin@263: } asherkin@263: asherkin@263: void asherkin@263: HIndex::load(BytecodeEmitter &emitter_) asherkin@263: { asherkin@263: __ getelem(type()); asherkin@263: } asherkin@263: asherkin@263: bool asherkin@263: HIndex::ref(BytecodeEmitter &emitter_) asherkin@263: { asherkin@263: return false; asherkin@263: } asherkin@263: asherkin@263: void asherkin@263: HField::bind(HIRVisitor *visitor, BytecodeEmitter &emitter_) asherkin@263: { asherkin@263: __ note_position(node()->pos()); asherkin@263: base_->accept(visitor); asherkin@263: } asherkin@263: asherkin@263: void asherkin@263: HField::hold(BytecodeEmitter &emitter_) asherkin@263: { asherkin@263: __ dup(); asherkin@263: } asherkin@263: asherkin@263: void asherkin@263: HField::swapAndPick(BytecodeEmitter &emitter_) asherkin@263: { asherkin@263: // BASE VALUE asherkin@263: __ swap(); // VALUE BASE asherkin@263: __ pick2(); // VALUE BASE VALUE asherkin@263: } asherkin@263: asherkin@263: void asherkin@263: HField::store(BytecodeEmitter &emitter_) asherkin@263: { asherkin@263: __ setfield(index_, type()); asherkin@263: } asherkin@263: asherkin@263: void asherkin@263: HField::load(BytecodeEmitter &emitter_) asherkin@263: { asherkin@263: __ getfield(index_, type()); asherkin@263: } asherkin@263: asherkin@263: bool asherkin@263: HField::ref(BytecodeEmitter &emitter_) asherkin@263: { asherkin@263: return false; asherkin@263: } asherkin@263: asherkin@263: void asherkin@263: HIRTranslator::visitLocal(HLocal *local) asherkin@263: { asherkin@263: local->bind(this, emitter_); asherkin@263: local->load(emitter_); asherkin@263: } asherkin@263: asherkin@263: void asherkin@263: HIRTranslator::visitIndex(HIndex *index) asherkin@263: { asherkin@263: index->bind(this, emitter_); asherkin@263: index->load(emitter_); asherkin@263: } asherkin@263: asherkin@263: void asherkin@263: HIRTranslator::visitField(HField *field) asherkin@263: { asherkin@263: field->bind(this, emitter_); asherkin@263: field->load(emitter_); asherkin@263: } asherkin@263: asherkin@263: void asherkin@263: HIRTranslator::visitGlobal(HGlobal *global) asherkin@263: { asherkin@263: global->bind(this, emitter_); asherkin@263: global->load(emitter_); 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: case TOK_ASSIGN_ADD: asherkin@263: case TOK_INCREMENT: asherkin@263: return OP_ADD; asherkin@263: case TOK_MINUS: asherkin@263: case TOK_ASSIGN_SUB: asherkin@263: case TOK_DECREMENT: asherkin@263: return OP_SUB; asherkin@263: case TOK_STAR: asherkin@263: case TOK_ASSIGN_MUL: asherkin@263: return OP_MUL; asherkin@263: case TOK_SLASH: asherkin@263: case TOK_ASSIGN_DIV: asherkin@263: return OP_DIV; asherkin@263: case TOK_PERCENT: asherkin@263: case TOK_ASSIGN_MOD: asherkin@263: return OP_MOD; asherkin@263: case TOK_SHR: asherkin@263: case TOK_ASSIGN_SHR: asherkin@263: return OP_SHR; asherkin@263: case TOK_USHR: asherkin@263: case TOK_ASSIGN_USHR: asherkin@263: return OP_USHR; asherkin@263: case TOK_SHL: asherkin@263: case TOK_ASSIGN_SHL: asherkin@263: return OP_SHL; asherkin@263: case TOK_BITAND: asherkin@263: case TOK_ASSIGN_BITAND: asherkin@263: return OP_BITAND; asherkin@263: case TOK_BITOR: asherkin@263: case TOK_ASSIGN_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: HIRTranslator::visitStore(HStore *store) asherkin@263: { asherkin@263: store->lval()->bind(this, emitter_); asherkin@263: asherkin@263: if (store->kind() == TOK_ASSIGN) { asherkin@263: // Fixed-length arrays and structs have copy semantics. asherkin@263: Local type(zone_, store->lval()->type()); asherkin@263: if (type->isArray() && ArrayType::cast(type)->isFixedLength()) { asherkin@263: store->lval()->load(emitter_); asherkin@263: store->rval()->accept(this); asherkin@263: __ copyarray(); asherkin@263: } else if (type->isStruct()) { asherkin@263: store->lval()->load(emitter_); asherkin@263: store->rval()->accept(this); asherkin@263: __ copystruct(); asherkin@263: } else { asherkin@263: store->rval()->accept(this); asherkin@263: store->lval()->store(emitter_); asherkin@263: } asherkin@263: } else { asherkin@263: // Advanced case: dup the ref, load, op, store. asherkin@263: store->lval()->hold(emitter_); asherkin@263: store->lval()->load(emitter_); asherkin@263: store->rval()->accept(this); asherkin@263: __ binary(TokenToOpcode(store->kind())); asherkin@263: store->lval()->store(emitter_); asherkin@263: } asherkin@263: } asherkin@263: asherkin@263: void asherkin@263: HIRTranslator::visitPostIncDec(HPostIncDec *hir) asherkin@263: { asherkin@263: hir->lval()->bind(this, emitter_); asherkin@263: hir->lval()->hold(emitter_); asherkin@263: hir->lval()->load(emitter_); asherkin@263: hir->lval()->swapAndPick(emitter_); asherkin@263: hir->rval()->accept(this); asherkin@263: __ binary(TokenToOpcode(hir->kind())); asherkin@263: hir->lval()->store(emitter_); asherkin@263: __ pop(); asherkin@263: } asherkin@263: asherkin@263: void asherkin@263: HIRTranslator::visitBinary(HBinary *binary) asherkin@263: { asherkin@263: binary->left()->accept(this); asherkin@263: binary->right()->accept(this); asherkin@263: asherkin@263: // We define three sets of math operators: asherkin@263: // asherkin@263: // (1) ADD, SUB, MUL, DIV, MOD, which take two equal types and produce asherkin@263: // the same type. asherkin@263: // (2) LT, LE, GE, GT, EQ, NE, which take two equal types and produce asherkin@263: // a boolean. asherkin@263: // (3) SHL, SR, USHR, BITAND, BITOR, BITXOR, BITNOT, which take two asherkin@263: // equal integer types and return that integer type. asherkin@263: // asherkin@263: Opcode op = TokenToOpcode(binary->token()); asherkin@263: asherkin@263: if (op == OP_DIV) asherkin@263: __ note_position(binary->node()->pos()); asherkin@263: asherkin@263: if (op >= OP_ADD && op <= OP_NE) { asherkin@263: if (binary->type()->isFloat()) asherkin@263: op = Opcode(int(op) + int(OP_ADD_F - OP_ADD)); asherkin@263: } else { asherkin@263: assert(binary->type()->isInt32()); asherkin@263: } asherkin@263: asherkin@263: __ binary(op); asherkin@263: } asherkin@263: asherkin@263: void asherkin@263: HIRTranslator::visitUnary(HUnary *unary) asherkin@263: { asherkin@263: unary->in()->accept(this); asherkin@263: asherkin@263: switch (unary->token()) { asherkin@263: case TOK_TILDE: asherkin@263: __ bitnot(); asherkin@263: break; asherkin@263: asherkin@263: case TOK_MINUS: asherkin@263: if (unary->type()->isFloat()) asherkin@263: __ neg_f(); asherkin@263: else asherkin@263: __ neg(); asherkin@263: break; asherkin@263: asherkin@263: default: asherkin@263: assert(unary->token() == TOK_LABEL); asherkin@263: __ bitcast(unary->type()); asherkin@263: break; asherkin@263: } asherkin@263: } asherkin@263: asherkin@263: void asherkin@263: HIRTranslator::visitConvert(HConvert *convert) asherkin@263: { asherkin@263: convert->in()->accept(this); asherkin@263: __ unary(convert->opcode()); asherkin@263: } asherkin@263: asherkin@263: void asherkin@263: HIRTranslator::visitToRef(HToRef *hir) asherkin@263: { asherkin@263: HIR *in = hir->in(); asherkin@263: asherkin@263: // See if we can bind a direct reference. asherkin@263: if (in->isLValue() && in->toLValue()->ref(emitter_)) asherkin@263: return; asherkin@263: asherkin@263: // No, we can't. So instead we create an on-stack reference. asherkin@263: in->accept(this); asherkin@263: __ slotref_lifo(); asherkin@263: } asherkin@263: asherkin@263: void asherkin@263: HIRTranslator::visitNewDependentArray(HNewDependentArray *hir) asherkin@263: { asherkin@263: hir->base()->accept(this); asherkin@263: hir->index()->accept(this); asherkin@263: asherkin@263: Local type(zone_, ArrayType::cast(hir->type())); asherkin@263: asherkin@263: // The dependent map is used for the opcode, but is not attached. asherkin@263: Local map(zone_, ArrayMap::NewDependent(zone_, type)); asherkin@263: if (!map) asherkin@263: return; asherkin@263: asherkin@263: __ deparray(map); asherkin@263: } asherkin@263: asherkin@263: void asherkin@263: HIRTranslator::visitImport(HImport *import) asherkin@263: { asherkin@263: __ import(import->index()); asherkin@263: } asherkin@263: asherkin@263: void asherkin@263: ke::EmitHIR(Zone *zone, BytecodeEmitter &emitter, HIR *hir) asherkin@263: { asherkin@263: HIRTranslator translator(zone, emitter); asherkin@263: translator.translate(hir); asherkin@263: }