/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the V4VM module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifdef __clang__ # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wunused-parameter" #endif // __clang__ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __clang__ # pragma clang diagnostic pop #endif // __clang__ #include #include #include #include #include #include // These includes have to come last, because WTF/Platform.h defines some macros // with very unfriendly names that collide with class fields in LLVM. #include "qv4isel_llvm_p.h" #include "qv4_llvm_p.h" #include "qv4ir_p.h" namespace QQmlJS { int compileWithLLVM(IR::Module *module, const QString &fileName, LLVMOutputType outputType, int (*exec)(void *)) { Q_ASSERT(module); Q_ASSERT(exec || outputType != LLVMOutputJit); // TODO: should this be done here? LLVMInitializeX86TargetInfo(); LLVMInitializeX86Target(); LLVMInitializeX86AsmPrinter(); LLVMInitializeX86AsmParser(); LLVMInitializeX86Disassembler(); LLVMInitializeX86TargetMC(); //---- llvm::InitializeNativeTarget(); LLVMInstructionSelection llvmIsel(llvm::getGlobalContext()); const QString moduleName = QFileInfo(fileName).fileName(); llvm::StringRef moduleId(moduleName.toUtf8().constData()); llvm::Module *llvmModule = new llvm::Module(moduleId, llvmIsel.getContext()); if (outputType == LLVMOutputJit) { // The execution engine takes ownership of the model. No need to delete it anymore. std::string errStr; llvm::ExecutionEngine *execEngine = llvm::EngineBuilder(llvmModule) // .setUseMCJIT(true) .setErrorStr(&errStr).create(); if (!execEngine) { std::cerr << "Could not create LLVM JIT: " << errStr << std::endl; return EXIT_FAILURE; } llvm::FunctionPassManager functionPassManager(llvmModule); // Set up the optimizer pipeline. Start with registering info about how the // target lays out data structures. functionPassManager.add(new llvm::DataLayout(*execEngine->getDataLayout())); // Promote allocas to registers. functionPassManager.add(llvm::createPromoteMemoryToRegisterPass()); // Provide basic AliasAnalysis support for GVN. functionPassManager.add(llvm::createBasicAliasAnalysisPass()); // Do simple "peephole" optimizations and bit-twiddling optzns. functionPassManager.add(llvm::createInstructionCombiningPass()); // Reassociate expressions. functionPassManager.add(llvm::createReassociatePass()); // Eliminate Common SubExpressions. functionPassManager.add(llvm::createGVNPass()); // Simplify the control flow graph (deleting unreachable blocks, etc). functionPassManager.add(llvm::createCFGSimplificationPass()); functionPassManager.doInitialization(); llvmIsel.buildLLVMModule(module, llvmModule, &functionPassManager); llvm::Function *entryPoint = llvmModule->getFunction("%entry"); Q_ASSERT(entryPoint); void *funcPtr = execEngine->getPointerToFunction(entryPoint); return exec(funcPtr); } else { llvm::FunctionPassManager functionPassManager(llvmModule); // Set up the optimizer pipeline. // Promote allocas to registers. functionPassManager.add(llvm::createPromoteMemoryToRegisterPass()); // Provide basic AliasAnalysis support for GVN. functionPassManager.add(llvm::createBasicAliasAnalysisPass()); // Do simple "peephole" optimizations and bit-twiddling optzns. functionPassManager.add(llvm::createInstructionCombiningPass()); // Reassociate expressions. functionPassManager.add(llvm::createReassociatePass()); // Eliminate Common SubExpressions. functionPassManager.add(llvm::createGVNPass()); // Simplify the control flow graph (deleting unreachable blocks, etc). functionPassManager.add(llvm::createCFGSimplificationPass()); functionPassManager.doInitialization(); llvmIsel.buildLLVMModule(module, llvmModule, &functionPassManager); // TODO: if output type is .ll, print the module to file const std::string triple = llvm::sys::getDefaultTargetTriple(); std::string err; const llvm::Target *target = llvm::TargetRegistry::lookupTarget(triple, err); if (! err.empty()) { std::cerr << err << ", triple: " << triple << std::endl; assert(!"cannot create target for the host triple"); } std::string cpu; std::string features; llvm::TargetOptions options; llvm::TargetMachine *targetMachine = target->createTargetMachine(triple, cpu, features, options, llvm::Reloc::PIC_); assert(targetMachine); llvm::TargetMachine::CodeGenFileType ft; QString ofName; if (outputType == LLVMOutputObject) { ft = llvm::TargetMachine::CGFT_ObjectFile; ofName = fileName + QLatin1String(".o"); } else if (outputType == LLVMOutputAssembler) { ft = llvm::TargetMachine::CGFT_AssemblyFile; ofName = fileName + QLatin1String(".s"); } else { // ft is not used. ofName = fileName + QLatin1String(".ll"); } llvm::raw_fd_ostream dest(ofName.toUtf8().constData(), err, llvm::raw_fd_ostream::F_Binary); llvm::formatted_raw_ostream destf(dest); if (!err.empty()) { std::cerr << err << std::endl; delete llvmModule; } llvm::PassManager globalPassManager; globalPassManager.add(llvm::createScalarReplAggregatesPass()); globalPassManager.add(llvm::createInstructionCombiningPass()); globalPassManager.add(llvm::createGlobalOptimizerPass()); globalPassManager.add(llvm::createFunctionInliningPass(25)); // globalPassManager.add(llvm::createFunctionInliningPass(125)); if (outputType == LLVMOutputObject || outputType == LLVMOutputAssembler) { if (targetMachine->addPassesToEmitFile(globalPassManager, destf, ft)) { std::cerr << err << " (probably no DataLayout in TargetMachine)" << std::endl; } else { globalPassManager.run(*llvmModule); destf.flush(); dest.flush(); } } else { // .ll globalPassManager.run(*llvmModule); llvmModule->print(destf, 0); destf.flush(); dest.flush(); } delete llvmModule; return EXIT_SUCCESS; } } } // QQmlJS using namespace QQmlJS; namespace { QTextStream qerr(stderr, QIODevice::WriteOnly); } LLVMInstructionSelection::LLVMInstructionSelection(llvm::LLVMContext &context) : llvm::IRBuilder<>(context) , _llvmModule(0) , _llvmFunction(0) , _llvmValue(0) , _numberTy(0) , _valueTy(0) , _contextPtrTy(0) , _stringPtrTy(0) , _functionTy(0) , _allocaInsertPoint(0) , _function(0) , _block(0) , _fpm(0) { } void LLVMInstructionSelection::buildLLVMModule(IR::Module *module, llvm::Module *llvmModule, llvm::FunctionPassManager *fpm) { qSwap(_llvmModule, llvmModule); qSwap(_fpm, fpm); _numberTy = getDoubleTy(); std::string err; llvm::OwningPtr buffer; llvm::error_code ec = llvm::MemoryBuffer::getFile(llvm::StringRef("llvm_runtime.bc"), buffer); if (ec) { qWarning() << ec.message().c_str(); assert(!"cannot load QML/JS LLVM runtime, you can generate the runtime with the command `make llvm_runtime'"); } llvm::Module *llvmRuntime = llvm::getLazyBitcodeModule(buffer.get(), getContext(), &err); if (! err.empty()) { qWarning() << err.c_str(); assert(!"cannot load QML/JS LLVM runtime"); } err.clear(); llvm::Linker::LinkModules(_llvmModule, llvmRuntime, llvm::Linker::DestroySource, &err); if (! err.empty()) { qWarning() << err.c_str(); assert(!"cannot link the QML/JS LLVM runtime"); } _valueTy = _llvmModule->getTypeByName("struct.QQmlJS::VM::Value"); _contextPtrTy = _llvmModule->getTypeByName("struct.QQmlJS::VM::Context")->getPointerTo(); _stringPtrTy = _llvmModule->getTypeByName("struct.QQmlJS::VM::String")->getPointerTo(); { llvm::Type *args[] = { _contextPtrTy }; _functionTy = llvm::FunctionType::get(getVoidTy(), llvm::makeArrayRef(args), false); } foreach (IR::Function *function, module->functions) (void) compileLLVMFunction(function); qSwap(_fpm, fpm); qSwap(_llvmModule, llvmModule); } llvm::Function *LLVMInstructionSelection::getLLVMFunction(IR::Function *function) { llvm::Function *&f = _functionMap[function]; if (! f) { QString name = QStringLiteral("__qmljs_native_"); if (function->name) { if (*function->name == QStringLiteral("%entry")) name = *function->name; else name += *function->name; } f = llvm::Function::Create(_functionTy, llvm::Function::ExternalLinkage, // ### make it internal qPrintable(name), _llvmModule); } return f; } llvm::Function *LLVMInstructionSelection::compileLLVMFunction(IR::Function *function) { llvm::Function *llvmFunction = getLLVMFunction(function); QHash blockMap; QVector tempMap; qSwap(_llvmFunction, llvmFunction); qSwap(_function, function); qSwap(_tempMap, tempMap); qSwap(_blockMap, blockMap); // create the LLVM blocks foreach (IR::BasicBlock *block, _function->basicBlocks) (void) getLLVMBasicBlock(block); // entry block SetInsertPoint(getLLVMBasicBlock(_function->basicBlocks.first())); llvm::Instruction *allocaInsertPoint = new llvm::BitCastInst(llvm::UndefValue::get(getInt32Ty()), getInt32Ty(), "", GetInsertBlock()); qSwap(_allocaInsertPoint, allocaInsertPoint); for (int i = 0; i < _function->tempCount; ++i) { llvm::AllocaInst *t = newLLVMTemp(_valueTy); _tempMap.append(t); } foreach (llvm::Value *t, _tempMap) { CreateStore(llvm::Constant::getNullValue(_valueTy), t); } CreateCall(_llvmModule->getFunction("__qmljs_llvm_init_this_object"), _llvmFunction->arg_begin()); foreach (IR::BasicBlock *block, _function->basicBlocks) { qSwap(_block, block); SetInsertPoint(getLLVMBasicBlock(_block)); foreach (IR::Stmt *s, _block->statements) s->accept(this); qSwap(_block, block); } qSwap(_allocaInsertPoint, allocaInsertPoint); allocaInsertPoint->eraseFromParent(); qSwap(_blockMap, blockMap); qSwap(_tempMap, tempMap); qSwap(_function, function); qSwap(_llvmFunction, llvmFunction); // Validate the generated code, checking for consistency. llvm::verifyFunction(*llvmFunction); // Optimize the function. if (_fpm) _fpm->run(*llvmFunction); return llvmFunction; } llvm::BasicBlock *LLVMInstructionSelection::getLLVMBasicBlock(IR::BasicBlock *block) { llvm::BasicBlock *&llvmBlock = _blockMap[block]; if (! llvmBlock) llvmBlock = llvm::BasicBlock::Create(getContext(), llvm::Twine(), _llvmFunction); return llvmBlock; } llvm::Value *LLVMInstructionSelection::getLLVMValue(IR::Expr *expr) { llvm::Value *llvmValue = 0; if (expr) { qSwap(_llvmValue, llvmValue); expr->accept(this); qSwap(_llvmValue, llvmValue); } if (! llvmValue) { expr->dump(qerr);qerr<asTemp()) return getLLVMTemp(t); llvm::Value *addr = newLLVMTemp(_valueTy); CreateStore(getLLVMValue(expr), addr); return addr; } llvm::Value *LLVMInstructionSelection::getLLVMCondition(IR::Expr *expr) { llvm::Value *value = 0; if (IR::Temp *t = expr->asTemp()) { value = getLLVMTemp(t); } else { value = getLLVMValue(expr); if (! value) { Q_UNIMPLEMENTED(); return getInt1(false); } llvm::Value *tmp = newLLVMTemp(_valueTy); CreateStore(value, tmp); value = tmp; } return CreateCall2(_llvmModule->getFunction("__qmljs_llvm_to_boolean"), _llvmFunction->arg_begin(), value); } llvm::Value *LLVMInstructionSelection::getLLVMTemp(IR::Temp *temp) { if (temp->index < 0) { const int index = -temp->index -1; return CreateCall2(_llvmModule->getFunction("__qmljs_llvm_get_argument"), _llvmFunction->arg_begin(), getInt32(index)); } return _tempMap[temp->index]; } llvm::Value *LLVMInstructionSelection::getStringPtr(const QString &s) { llvm::Value *&value = _stringMap[s]; if (! value) { const QByteArray bytes = s.toUtf8(); value = CreateGlobalStringPtr(llvm::StringRef(bytes.constData(), bytes.size())); _stringMap[s] = value; } return value; } llvm::Value *LLVMInstructionSelection::getIdentifier(const QString &s) { llvm::Value *str = getStringPtr(s); llvm::Value *id = CreateCall2(_llvmModule->getFunction("__qmljs_identifier_from_utf8"), _llvmFunction->arg_begin(), str); return id; } void LLVMInstructionSelection::visitExp(IR::Exp *s) { getLLVMValue(s->expr); } void LLVMInstructionSelection::visitEnter(IR::Enter *) { Q_UNREACHABLE(); } void LLVMInstructionSelection::visitLeave(IR::Leave *) { Q_UNREACHABLE(); } void LLVMInstructionSelection::genMoveSubscript(IR::Move *s) { IR::Subscript *subscript = s->target->asSubscript(); llvm::Value *base = getLLVMTempReference(subscript->base); llvm::Value *index = getLLVMTempReference(subscript->index); llvm::Value *source = toValuePtr(s->source); CreateCall4(_llvmModule->getFunction("__qmljs_llvm_set_element"), _llvmFunction->arg_begin(), base, index, source); } void LLVMInstructionSelection::genMoveMember(IR::Move *s) { IR::Member *m = s->target->asMember(); llvm::Value *base = getLLVMTempReference(m->base); llvm::Value *name = getIdentifier(*m->name); llvm::Value *source = toValuePtr(s->source); CreateCall4(_llvmModule->getFunction("__qmljs_llvm_set_property"), _llvmFunction->arg_begin(), base, name, source); } void LLVMInstructionSelection::visitMove(IR::Move *s) { if (s->op == IR::OpInvalid) { if (s->target->asSubscript()) { genMoveSubscript(s); return; } else if (s->target->asMember()) { genMoveMember(s); return; } else if (IR::Name *n = s->target->asName()) { llvm::Value *name = getIdentifier(*n->id); llvm::Value *source = toValuePtr(s->source); CreateCall3(_llvmModule->getFunction("__qmljs_llvm_set_activation_property"), _llvmFunction->arg_begin(), name, source); return; } else if (IR::Temp *t = s->target->asTemp()) { llvm::Value *target = getLLVMTemp(t); llvm::Value *source = getLLVMValue(s->source); CreateStore(source, target); return; } } else { if (IR::Temp *t = s->target->asTemp()) { if (s->source->asTemp() || s->source->asConst()) { const char *opName = 0; switch (s->op) { case IR::OpBitAnd: opName = "__qmljs_llvm_bit_and"; break; case IR::OpBitOr: opName = "__qmljs_llvm_bit_or"; break; case IR::OpBitXor: opName = "__qmljs_llvm_bit_xor"; break; case IR::OpAdd: opName = "__qmljs_llvm_add"; break; case IR::OpSub: opName = "__qmljs_llvm_sub"; break; case IR::OpMul: opName = "__qmljs_llvm_mul"; break; case IR::OpDiv: opName = "__qmljs_llvm_div"; break; case IR::OpMod: opName = "__qmljs_llvm_mod"; break; case IR::OpLShift: opName = "__qmljs_llvm_shl"; break; case IR::OpRShift: opName = "__qmljs_llvm_shr"; break; case IR::OpURShift: opName = "__qmljs_llvm_ushr"; break; default: Q_UNREACHABLE(); break; } if (opName) { llvm::Value *target = getLLVMTemp(t); llvm::Value *s1 = toValuePtr(s->target); llvm::Value *s2 = toValuePtr(s->source); CreateCall4(_llvmModule->getFunction(opName), _llvmFunction->arg_begin(), target, s1, s2); return; } } } else if (IR::Name *n = s->target->asName()) { // inplace assignment, e.g. x += 1, ++x, ... if (s->source->asTemp() || s->source->asConst()) { const char *opName = 0; switch (s->op) { case IR::OpBitAnd: opName = "__qmljs_llvm_inplace_bit_and_name"; break; case IR::OpBitOr: opName = "__qmljs_llvm_inplace_bit_or_name"; break; case IR::OpBitXor: opName = "__qmljs_llvm_inplace_bit_xor_name"; break; case IR::OpAdd: opName = "__qmljs_llvm_inplace_add_name"; break; case IR::OpSub: opName = "__qmljs_llvm_inplace_sub_name"; break; case IR::OpMul: opName = "__qmljs_llvm_inplace_mul_name"; break; case IR::OpDiv: opName = "__qmljs_llvm_inplace_div_name"; break; case IR::OpMod: opName = "__qmljs_llvm_inplace_mod_name"; break; case IR::OpLShift: opName = "__qmljs_llvm_inplace_shl_name"; break; case IR::OpRShift: opName = "__qmljs_llvm_inplace_shr_name"; break; case IR::OpURShift: opName = "__qmljs_llvm_inplace_ushr_name"; break; default: Q_UNREACHABLE(); break; } if (opName) { llvm::Value *dst = getIdentifier(*n->id); llvm::Value *src = toValuePtr(s->source); CreateCall3(_llvmModule->getFunction(opName), _llvmFunction->arg_begin(), dst, src); return; } } } else if (IR::Subscript *ss = s->target->asSubscript()) { if (s->source->asTemp() || s->source->asConst()) { const char *opName = 0; switch (s->op) { case IR::OpBitAnd: opName = "__qmljs_llvm_inplace_bit_and_element"; break; case IR::OpBitOr: opName = "__qmljs_llvm_inplace_bit_or_element"; break; case IR::OpBitXor: opName = "__qmljs_llvm_inplace_bit_xor_element"; break; case IR::OpAdd: opName = "__qmljs_llvm_inplace_add_element"; break; case IR::OpSub: opName = "__qmljs_llvm_inplace_sub_element"; break; case IR::OpMul: opName = "__qmljs_llvm_inplace_mul_element"; break; case IR::OpDiv: opName = "__qmljs_llvm_inplace_div_element"; break; case IR::OpMod: opName = "__qmljs_llvm_inplace_mod_element"; break; case IR::OpLShift: opName = "__qmljs_llvm_inplace_shl_element"; break; case IR::OpRShift: opName = "__qmljs_llvm_inplace_shr_element"; break; case IR::OpURShift: opName = "__qmljs_llvm_inplace_ushr_element"; break; default: Q_UNREACHABLE(); break; } if (opName) { llvm::Value *base = getLLVMTemp(ss->base->asTemp()); llvm::Value *index = getLLVMTemp(ss->index->asTemp()); llvm::Value *value = toValuePtr(s->source); CreateCall4(_llvmModule->getFunction(opName), _llvmFunction->arg_begin(), base, index, value); // TODO: checkExceptions(); } return; } } else if (IR::Member *m = s->target->asMember()) { if (s->source->asTemp() || s->source->asConst()) { const char *opName = 0; switch (s->op) { case IR::OpBitAnd: opName = "__qmljs_llvm_inplace_bit_and_member"; break; case IR::OpBitOr: opName = "__qmljs_llvm_inplace_bit_or_member"; break; case IR::OpBitXor: opName = "__qmljs_llvm_inplace_bit_xor_member"; break; case IR::OpAdd: opName = "__qmljs_llvm_inplace_add_member"; break; case IR::OpSub: opName = "__qmljs_llvm_inplace_sub_member"; break; case IR::OpMul: opName = "__qmljs_llvm_inplace_mul_member"; break; case IR::OpDiv: opName = "__qmljs_llvm_inplace_div_member"; break; case IR::OpMod: opName = "__qmljs_llvm_inplace_mod_member"; break; case IR::OpLShift: opName = "__qmljs_llvm_inplace_shl_member"; break; case IR::OpRShift: opName = "__qmljs_llvm_inplace_shr_member"; break; case IR::OpURShift: opName = "__qmljs_llvm_inplace_ushr_member"; break; default: Q_UNREACHABLE(); break; } if (opName) { llvm::Value *base = getLLVMTemp(m->base->asTemp()); llvm::Value *member = getIdentifier(*m->name); llvm::Value *value = toValuePtr(s->source); CreateCall4(_llvmModule->getFunction(opName), _llvmFunction->arg_begin(), value, base, member); // TODO: checkExceptions(); } return; } } } // For anything else: s->dump(qerr, IR::Stmt::HIR); qerr << endl; Q_UNIMPLEMENTED(); return; } void LLVMInstructionSelection::visitJump(IR::Jump *s) { CreateBr(getLLVMBasicBlock(s->target)); } void LLVMInstructionSelection::visitCJump(IR::CJump *s) { CreateCondBr(getLLVMCondition(s->cond), getLLVMBasicBlock(s->iftrue), getLLVMBasicBlock(s->iffalse)); } void LLVMInstructionSelection::visitRet(IR::Ret *s) { IR::Temp *t = s->expr->asTemp(); assert(t != 0); llvm::Value *result = getLLVMTemp(t); llvm::Value *ctx = _llvmFunction->arg_begin(); CreateCall2(_llvmModule->getFunction("__qmljs_llvm_return"), ctx, result); CreateRetVoid(); } void LLVMInstructionSelection::visitConst(IR::Const *e) { llvm::Value *tmp = createValue(e); _llvmValue = CreateLoad(tmp); } void LLVMInstructionSelection::visitString(IR::String *e) { llvm::Value *tmp = newLLVMTemp(_valueTy); CreateCall3(_llvmModule->getFunction("__qmljs_llvm_init_string"), _llvmFunction->arg_begin(), tmp, getStringPtr(*e->value)); _llvmValue = CreateLoad(tmp); } void LLVMInstructionSelection::visitRegExp(IR::RegExp *e) { e->dump(qerr); qerr << endl; Q_UNIMPLEMENTED(); _llvmValue = llvm::Constant::getNullValue(_valueTy); } void LLVMInstructionSelection::visitName(IR::Name *e) { llvm::Value *result = newLLVMTemp(_valueTy); if (e->id == QStringLiteral("this")) { CreateCall2(_llvmModule->getFunction("__qmljs_llvm_get_this_object"), _llvmFunction->arg_begin(), result); } else { llvm::Value *name = getIdentifier(*e->id); CreateCall3(_llvmModule->getFunction("__qmljs_llvm_get_activation_property"), _llvmFunction->arg_begin(), result, name); } _llvmValue = CreateLoad(result); } void LLVMInstructionSelection::visitTemp(IR::Temp *e) { if (llvm::Value *t = getLLVMTemp(e)) { _llvmValue = CreateLoad(t); } } void LLVMInstructionSelection::visitClosure(IR::Closure *e) { llvm::Value *tmp = newLLVMTemp(_valueTy); llvm::Value *clos = getLLVMFunction(e->value); assert("!broken: pass function name!"); CreateCall3(_llvmModule->getFunction("__qmljs_llvm_init_native_function"), _llvmFunction->arg_begin(), tmp, clos); _llvmValue = CreateLoad(tmp); } void LLVMInstructionSelection::visitUnop(IR::Unop *e) { llvm::Value *result = newLLVMTemp(_valueTy); genUnop(result, e); _llvmValue = CreateLoad(result); } void LLVMInstructionSelection::visitBinop(IR::Binop *e) { llvm::Value *result = newLLVMTemp(_valueTy); genBinop(result, e); _llvmValue = CreateLoad(result); } void LLVMInstructionSelection::genUnop(llvm::Value *result, IR::Unop *e) { IR::Temp *t = e->expr->asTemp(); assert(t != 0); llvm::Value *expr = getLLVMTemp(t); llvm::Value *op = 0; switch (e->op) { default: Q_UNREACHABLE(); break; case IR::OpNot: op = _llvmModule->getFunction("__qmljs_llvm_not"); break; case IR::OpUMinus: op = _llvmModule->getFunction("__qmljs_llvm_uminus"); break; case IR::OpUPlus: op = _llvmModule->getFunction("__qmljs_llvm_uplus"); break; case IR::OpCompl: op = _llvmModule->getFunction("__qmljs_llvm_compl"); break; } CreateCall3(op, _llvmFunction->arg_begin(), result, expr); } void LLVMInstructionSelection::genBinop(llvm::Value *result, IR::Binop *e) { assert(e->left->asTemp() || e->left->asConst()); assert(e->right->asTemp() || e->right->asConst()); llvm::Value *left = toValuePtr(e->left); llvm::Value *right = toValuePtr(e->right); llvm::Value *op = 0; switch (e->op) { case IR::OpInvalid: case IR::OpIfTrue: case IR::OpNot: case IR::OpUMinus: case IR::OpUPlus: case IR::OpCompl: Q_UNREACHABLE(); break; case IR::OpIncrement: case IR::OpDecrement: assert(!"TODO!"); break; case IR::OpBitAnd: op = _llvmModule->getFunction("__qmljs_llvm_bit_and"); break; case IR::OpBitOr: op = _llvmModule->getFunction("__qmljs_llvm_bit_or"); break; case IR::OpBitXor: op = _llvmModule->getFunction("__qmljs_llvm_bit_xor"); break; case IR::OpAdd: op = _llvmModule->getFunction("__qmljs_llvm_add"); break; case IR::OpSub: op = _llvmModule->getFunction("__qmljs_llvm_sub"); break; case IR::OpMul: op = _llvmModule->getFunction("__qmljs_llvm_mul"); break; case IR::OpDiv: op = _llvmModule->getFunction("__qmljs_llvm_div"); break; case IR::OpMod: op = _llvmModule->getFunction("__qmljs_llvm_mod"); break; case IR::OpLShift: op = _llvmModule->getFunction("__qmljs_llvm_shl"); break; case IR::OpRShift: op = _llvmModule->getFunction("__qmljs_llvm_shr"); break; case IR::OpURShift: op = _llvmModule->getFunction("__qmljs_llvm_ushr"); break; case IR::OpGt: op = _llvmModule->getFunction("__qmljs_llvm_gt"); break; case IR::OpLt: op = _llvmModule->getFunction("__qmljs_llvm_lt"); break; case IR::OpGe: op = _llvmModule->getFunction("__qmljs_llvm_ge"); break; case IR::OpLe: op = _llvmModule->getFunction("__qmljs_llvm_le"); break; case IR::OpEqual: op = _llvmModule->getFunction("__qmljs_llvm_eq"); break; case IR::OpNotEqual: op = _llvmModule->getFunction("__qmljs_llvm_ne"); break; case IR::OpStrictEqual: op = _llvmModule->getFunction("__qmljs_llvm_se"); break; case IR::OpStrictNotEqual: op = _llvmModule->getFunction("__qmljs_llvm_sne"); break; case IR::OpInstanceof: op = _llvmModule->getFunction("__qmljs_llvm_instanceof"); break; case IR::OpIn: op = _llvmModule->getFunction("__qmljs_llvm_in"); break; case IR::OpAnd: case IR::OpOr: Q_UNREACHABLE(); break; } CreateCall4(op, _llvmFunction->arg_begin(), result, left, right); } llvm::AllocaInst *LLVMInstructionSelection::newLLVMTemp(llvm::Type *type, llvm::Value *size) { llvm::AllocaInst *addr = new llvm::AllocaInst(type, size, llvm::Twine(), _allocaInsertPoint); return addr; } llvm::Value * LLVMInstructionSelection::genArguments(IR::ExprList *exprs, int &argc) { llvm::Value *args = 0; argc = 0; for (IR::ExprList *it = exprs; it; it = it->next) ++argc; if (argc) args = newLLVMTemp(_valueTy, getInt32(argc)); else args = llvm::Constant::getNullValue(_valueTy->getPointerTo()); int i = 0; for (IR::ExprList *it = exprs; it; it = it->next) { llvm::Value *arg = getLLVMValue(it->expr); CreateStore(arg, CreateConstGEP1_32(args, i++)); } return args; } void LLVMInstructionSelection::genCallMember(IR::Call *e, llvm::Value *result) { if (! result) result = newLLVMTemp(_valueTy); IR::Member *m = e->base->asMember(); llvm::Value *thisObject = getLLVMTemp(m->base->asTemp()); llvm::Value *name = getIdentifier(*m->name); int argc = 0; llvm::Value *args = genArguments(e->args, argc); llvm::Value *actuals[] = { _llvmFunction->arg_begin(), result, thisObject, name, args, getInt32(argc) }; CreateCall(_llvmModule->getFunction("__qmljs_llvm_call_property"), llvm::ArrayRef(actuals)); _llvmValue = CreateLoad(result); } void LLVMInstructionSelection::genConstructMember(IR::New *e, llvm::Value *result) { if (! result) result = newLLVMTemp(_valueTy); IR::Member *m = e->base->asMember(); llvm::Value *thisObject = getLLVMTemp(m->base->asTemp()); llvm::Value *name = getIdentifier(*m->name); int argc = 0; llvm::Value *args = genArguments(e->args, argc); llvm::Value *actuals[] = { _llvmFunction->arg_begin(), result, thisObject, name, args, getInt32(argc) }; CreateCall(_llvmModule->getFunction("__qmljs_llvm_construct_property"), llvm::ArrayRef(actuals)); _llvmValue = CreateLoad(result); } void LLVMInstructionSelection::genCallTemp(IR::Call *e, llvm::Value *result) { if (! result) result = newLLVMTemp(_valueTy); llvm::Value *func = getLLVMTempReference(e->base); int argc = 0; llvm::Value *args = genArguments(e->args, argc); llvm::Value *thisObject = llvm::Constant::getNullValue(_valueTy->getPointerTo()); llvm::Value *actuals[] = { _llvmFunction->arg_begin(), result, thisObject, func, args, getInt32(argc) }; CreateCall(_llvmModule->getFunction("__qmljs_llvm_call_value"), actuals); _llvmValue = CreateLoad(result); } void LLVMInstructionSelection::genConstructTemp(IR::New *e, llvm::Value *result) { if (! result) result = newLLVMTemp(_valueTy); llvm::Value *func = getLLVMTempReference(e->base); int argc = 0; llvm::Value *args = genArguments(e->args, argc); llvm::Value *actuals[] = { _llvmFunction->arg_begin(), result, func, args, getInt32(argc) }; CreateCall(_llvmModule->getFunction("__qmljs_llvm_construct_value"), actuals); _llvmValue = CreateLoad(result); } void LLVMInstructionSelection::genCallName(IR::Call *e, llvm::Value *result) { IR::Name *base = e->base->asName(); if (! result) result = newLLVMTemp(_valueTy); if (! base->id) { switch (base->builtin) { case IR::Name::builtin_invalid: break; case IR::Name::builtin_typeof: CreateCall3(_llvmModule->getFunction("__qmljs_llvm_typeof"), _llvmFunction->arg_begin(), result, getLLVMTempReference(e->args->expr)); _llvmValue = CreateLoad(result); return; case IR::Name::builtin_throw: CreateCall2(_llvmModule->getFunction("__qmljs_llvm_throw"), _llvmFunction->arg_begin(), getLLVMTempReference(e->args->expr)); _llvmValue = llvm::UndefValue::get(_valueTy); return; case IR::Name::builtin_create_exception_handler: CreateCall2(_llvmModule->getFunction("__qmljs_llvm_create_exception_handler"), _llvmFunction->arg_begin(), result); _llvmValue = CreateLoad(result); return; case IR::Name::builtin_delete_exception_handler: CreateCall(_llvmModule->getFunction("__qmljs_llvm_delete_exception_handler"), _llvmFunction->arg_begin()); return; case IR::Name::builtin_get_exception: CreateCall2(_llvmModule->getFunction("__qmljs_llvm_get_exception"), _llvmFunction->arg_begin(), result); _llvmValue = CreateLoad(result); return; case IR::Name::builtin_foreach_iterator_object: CreateCall3(_llvmModule->getFunction("__qmljs_llvm_foreach_iterator_object"), _llvmFunction->arg_begin(), result, getLLVMTempReference(e->args->expr)); _llvmValue = CreateLoad(result); return; case IR::Name::builtin_foreach_next_property_name: CreateCall2(_llvmModule->getFunction("__qmljs_llvm_foreach_next_property_name"), result, getLLVMTempReference(e->args->expr)); _llvmValue = CreateLoad(result); return; case IR::Name::builtin_delete: { if (IR::Subscript *subscript = e->args->expr->asSubscript()) { CreateCall4(_llvmModule->getFunction("__qmljs_llvm_delete_subscript"), _llvmFunction->arg_begin(), result, getLLVMTempReference(subscript->base), getLLVMTempReference(subscript->index)); _llvmValue = CreateLoad(result); return; } else if (IR::Member *member = e->args->expr->asMember()) { CreateCall4(_llvmModule->getFunction("__qmljs_llvm_delete_member"), _llvmFunction->arg_begin(), result, getLLVMTempReference(member->base), getIdentifier(*member->name)); _llvmValue = CreateLoad(result); return; } else if (IR::Name *name = e->args->expr->asName()) { CreateCall3(_llvmModule->getFunction("__qmljs_llvm_delete_property"), _llvmFunction->arg_begin(), result, getIdentifier(*name->id)); _llvmValue = CreateLoad(result); return; } else { CreateCall3(_llvmModule->getFunction("__qmljs_llvm_delete_value"), _llvmFunction->arg_begin(), result, getLLVMTempReference(e->args->expr)); _llvmValue = CreateLoad(result); return; } } break; default: Q_UNREACHABLE(); } } else { llvm::Value *name = getIdentifier(*base->id); int argc = 0; llvm::Value *args = genArguments(e->args, argc); CreateCall5(_llvmModule->getFunction("__qmljs_llvm_call_activation_property"), _llvmFunction->arg_begin(), result, name, args, getInt32(argc)); _llvmValue = CreateLoad(result); } } void LLVMInstructionSelection::genConstructName(IR::New *e, llvm::Value *result) { IR::Name *base = e->base->asName(); if (! result) result = newLLVMTemp(_valueTy); if (! base->id) { Q_UNREACHABLE(); } else { llvm::Value *name = getIdentifier(*base->id); int argc = 0; llvm::Value *args = genArguments(e->args, argc); CreateCall5(_llvmModule->getFunction("__qmljs_llvm_construct_activation_property"), _llvmFunction->arg_begin(), result, name, args, getInt32(argc)); _llvmValue = CreateLoad(result); } } void LLVMInstructionSelection::visitCall(IR::Call *e) { if (e->base->asMember()) { genCallMember(e); } else if (e->base->asTemp()) { genCallTemp(e); } else if (e->base->asName()) { genCallName(e); } else if (IR::Temp *t = e->base->asTemp()) { llvm::Value *base = getLLVMTemp(t); int argc = 0; llvm::Value *args = genArguments(e->args, argc); llvm::Value *result = newLLVMTemp(_valueTy); CreateStore(llvm::Constant::getNullValue(_valueTy), result); CreateCall5(_llvmModule->getFunction("__qmljs_llvm_call_value"), _llvmFunction->arg_begin(), result, base, args, getInt32(argc)); _llvmValue = CreateLoad(result); } else { Q_UNIMPLEMENTED(); } } void LLVMInstructionSelection::visitNew(IR::New *e) { if (e->base->asMember()) { genConstructMember(e); } else if (e->base->asTemp()) { genConstructTemp(e); } else if (e->base->asName()) { genConstructName(e); } else if (IR::Temp *t = e->base->asTemp()) { llvm::Value *base = getLLVMTemp(t); int argc = 0; llvm::Value *args = genArguments(e->args, argc); llvm::Value *result = newLLVMTemp(_valueTy); CreateStore(llvm::Constant::getNullValue(_valueTy), result); CreateCall5(_llvmModule->getFunction("__qmljs_llvm_construct_value"), _llvmFunction->arg_begin(), result, base, args, getInt32(argc)); _llvmValue = CreateLoad(result); } else { Q_UNIMPLEMENTED(); } } void LLVMInstructionSelection::visitSubscript(IR::Subscript *e) { llvm::Value *result = newLLVMTemp(_valueTy); llvm::Value *base = getLLVMTempReference(e->base); llvm::Value *index = getLLVMTempReference(e->index); CreateCall4(_llvmModule->getFunction("__qmljs_llvm_get_element"), _llvmFunction->arg_begin(), result, base, index); _llvmValue = CreateLoad(result); } void LLVMInstructionSelection::visitMember(IR::Member *e) { llvm::Value *result = newLLVMTemp(_valueTy); llvm::Value *base = getLLVMTempReference(e->base); llvm::Value *name = getIdentifier(*e->name); CreateCall4(_llvmModule->getFunction("__qmljs_llvm_get_property"), _llvmFunction->arg_begin(), result, base, name); _llvmValue = CreateLoad(result); } llvm::Value *LLVMInstructionSelection::createValue(IR::Const *e) { llvm::Value *tmp = newLLVMTemp(_valueTy); switch (e->type) { case IR::UndefinedType: CreateCall(_llvmModule->getFunction("__qmljs_llvm_init_undefined"), tmp); break; case IR::NullType: CreateCall(_llvmModule->getFunction("__qmljs_llvm_init_null"), tmp); break; case IR::BoolType: CreateCall2(_llvmModule->getFunction("__qmljs_llvm_init_boolean"), tmp, getInt1(e->value ? 1 : 0)); break; case IR::NumberType: CreateCall2(_llvmModule->getFunction("__qmljs_llvm_init_number"), tmp, llvm::ConstantFP::get(_numberTy, e->value)); break; default: Q_UNREACHABLE(); } return tmp; } llvm::Value *LLVMInstructionSelection::toValuePtr(IR::Expr *e) { if (IR::Temp *t = e->asTemp()) { return getLLVMTemp(t); } else if (IR::Const *c = e->asConst()) { return createValue(c); } else { Q_UNREACHABLE(); } }