2012-05-31 17:02:05 +00:00
|
|
|
|
|
|
|
#include "qv4isel_llvm_p.h"
|
|
|
|
#include "qv4ir_p.h"
|
|
|
|
|
2012-06-05 10:15:41 +00:00
|
|
|
#include <llvm/Support/system_error.h>
|
|
|
|
#include <llvm/Support/MemoryBuffer.h>
|
|
|
|
#include <llvm/Bitcode/ReaderWriter.h>
|
|
|
|
#include <llvm/Linker.h>
|
|
|
|
|
2012-05-31 17:02:05 +00:00
|
|
|
using namespace QQmlJS;
|
|
|
|
|
|
|
|
LLVMInstructionSelection::LLVMInstructionSelection(llvm::LLVMContext &context)
|
|
|
|
: llvm::IRBuilder<>(context)
|
|
|
|
, _llvmModule(0)
|
|
|
|
, _llvmFunction(0)
|
|
|
|
, _llvmValue(0)
|
|
|
|
, _numberTy(0)
|
|
|
|
, _valueTy(0)
|
2012-06-05 10:15:41 +00:00
|
|
|
, _contextPtrTy(0)
|
|
|
|
, _stringPtrTy(0)
|
2012-05-31 17:02:05 +00:00
|
|
|
, _functionTy(0)
|
|
|
|
, _function(0)
|
|
|
|
, _block(0)
|
|
|
|
{
|
2012-06-05 10:15:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
llvm::Module *LLVMInstructionSelection::getLLVMModule(IR::Module *module)
|
|
|
|
{
|
|
|
|
llvm::Module *llvmModule = new llvm::Module("a.out", getContext());
|
|
|
|
qSwap(_llvmModule, llvmModule);
|
|
|
|
|
2012-05-31 17:02:05 +00:00
|
|
|
_numberTy = getDoubleTy();
|
|
|
|
|
2012-06-05 10:15:41 +00:00
|
|
|
std::string err;
|
|
|
|
|
|
|
|
llvm::OwningPtr<llvm::MemoryBuffer> 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'");
|
2012-05-31 17:02:05 +00:00
|
|
|
}
|
|
|
|
|
2012-06-05 10:15:41 +00:00
|
|
|
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");
|
2012-05-31 17:02:05 +00:00
|
|
|
}
|
|
|
|
|
2012-06-05 10:15:41 +00:00
|
|
|
_valueTy = _llvmModule->getTypeByName("struct.QQmlJS::VM::Value");
|
|
|
|
_contextPtrTy = _llvmModule->getTypeByName("struct.QQmlJS::VM::Context")->getPointerTo();
|
|
|
|
_stringPtrTy = _llvmModule->getTypeByName("struct.QQmlJS::VM::String")->getPointerTo();
|
|
|
|
|
2012-05-31 17:02:05 +00:00
|
|
|
{
|
2012-06-05 10:15:41 +00:00
|
|
|
llvm::Type *args[] = { _contextPtrTy };
|
2012-05-31 17:02:05 +00:00
|
|
|
_functionTy = llvm::FunctionType::get(getVoidTy(), llvm::makeArrayRef(args), false);
|
|
|
|
}
|
|
|
|
|
2012-06-05 10:15:41 +00:00
|
|
|
|
2012-05-31 17:02:05 +00:00
|
|
|
foreach (IR::Function *function, module->functions)
|
|
|
|
(void) getLLVMFunction(function);
|
|
|
|
qSwap(_llvmModule, llvmModule);
|
|
|
|
return llvmModule;
|
|
|
|
}
|
|
|
|
|
|
|
|
llvm::Function *LLVMInstructionSelection::getLLVMFunction(IR::Function *function)
|
|
|
|
{
|
|
|
|
llvm::Function *llvmFunction =
|
2012-06-05 10:15:41 +00:00
|
|
|
llvm::Function::Create(_functionTy, llvm::Function::ExternalLinkage, // ### make it internal
|
2012-05-31 17:02:05 +00:00
|
|
|
llvm::Twine(function->name ? qPrintable(*function->name) : 0), _llvmModule);
|
|
|
|
|
|
|
|
QHash<IR::BasicBlock *, llvm::BasicBlock *> blockMap;
|
|
|
|
QVector<llvm::Value *> tempMap;
|
|
|
|
|
|
|
|
qSwap(_llvmFunction, llvmFunction);
|
|
|
|
qSwap(_function, function);
|
|
|
|
qSwap(_tempMap, tempMap);
|
|
|
|
qSwap(_blockMap, blockMap);
|
|
|
|
|
|
|
|
// entry block
|
|
|
|
SetInsertPoint(getLLVMBasicBlock(_function->basicBlocks.first()));
|
|
|
|
for (int i = 0; i < _function->tempCount; ++i) {
|
|
|
|
llvm::AllocaInst *t = CreateAlloca(_valueTy, 0, llvm::StringRef("t"));
|
|
|
|
_tempMap.append(t);
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach (llvm::Value *t, _tempMap) {
|
|
|
|
CreateStore(llvm::Constant::getNullValue(_valueTy), t);
|
|
|
|
}
|
|
|
|
|
|
|
|
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(_blockMap, blockMap);
|
|
|
|
qSwap(_tempMap, tempMap);
|
|
|
|
qSwap(_function, function);
|
|
|
|
qSwap(_llvmFunction, 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) {
|
|
|
|
Q_UNIMPLEMENTED();
|
|
|
|
llvmValue = llvm::Constant::getNullValue(_valueTy);
|
|
|
|
}
|
|
|
|
return llvmValue;
|
|
|
|
}
|
|
|
|
|
|
|
|
llvm::Value *LLVMInstructionSelection::getLLVMCondition(IR::Expr *expr)
|
|
|
|
{
|
2012-06-05 11:15:03 +00:00
|
|
|
llvm::Value *value = 0;
|
|
|
|
if (IR::Temp *t = expr->asTemp()) {
|
|
|
|
value = getLLVMTemp(t);
|
|
|
|
} else {
|
|
|
|
value = getLLVMValue(expr);
|
|
|
|
if (! value) {
|
|
|
|
Q_UNIMPLEMENTED();
|
|
|
|
return getInt1(false);
|
2012-05-31 17:02:05 +00:00
|
|
|
}
|
2012-06-05 11:15:03 +00:00
|
|
|
|
|
|
|
llvm::Value *tmp = CreateAlloca(_valueTy);
|
|
|
|
CreateStore(value, tmp);
|
|
|
|
value = tmp;
|
2012-05-31 17:02:05 +00:00
|
|
|
}
|
|
|
|
|
2012-06-05 11:15:03 +00:00
|
|
|
return CreateCall2(_llvmModule->getFunction("__qmljs_llvm_to_boolean"),
|
|
|
|
_llvmFunction->arg_begin(),
|
|
|
|
value);
|
2012-05-31 17:02:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
llvm::Value *LLVMInstructionSelection::getLLVMTemp(IR::Temp *temp)
|
|
|
|
{
|
|
|
|
if (temp->index < 0) {
|
2012-06-05 10:15:41 +00:00
|
|
|
const int index = -temp->index -1;
|
|
|
|
return CreateCall2(_llvmModule->getFunction("__qmljs_llvm_get_argument"),
|
|
|
|
_llvmFunction->arg_begin(), getInt32(index));
|
2012-05-31 17:02:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return _tempMap[temp->index];
|
|
|
|
}
|
|
|
|
|
2012-06-05 10:15:41 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2012-05-31 17:02:05 +00:00
|
|
|
void LLVMInstructionSelection::visitExp(IR::Exp *s)
|
|
|
|
{
|
|
|
|
getLLVMValue(s->expr);
|
|
|
|
}
|
|
|
|
|
|
|
|
void LLVMInstructionSelection::visitEnter(IR::Enter *)
|
|
|
|
{
|
2012-06-05 10:15:41 +00:00
|
|
|
Q_UNREACHABLE();
|
2012-05-31 17:02:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void LLVMInstructionSelection::visitLeave(IR::Leave *)
|
|
|
|
{
|
2012-06-05 10:15:41 +00:00
|
|
|
Q_UNREACHABLE();
|
2012-05-31 17:02:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void LLVMInstructionSelection::visitMove(IR::Move *s)
|
|
|
|
{
|
|
|
|
if (IR::Temp *t = s->target->asTemp()) {
|
2012-06-05 10:15:41 +00:00
|
|
|
llvm::Value *target = getLLVMTemp(t);
|
|
|
|
llvm::Value *source = getLLVMValue(s->source);
|
|
|
|
assert(source);
|
|
|
|
if (source->getType()->getPointerTo() != target->getType()) {
|
|
|
|
source->dump();
|
|
|
|
assert(!"not cool");
|
2012-05-31 17:02:05 +00:00
|
|
|
}
|
2012-06-05 10:15:41 +00:00
|
|
|
CreateStore(source, target);
|
|
|
|
return;
|
2012-05-31 17:02:05 +00:00
|
|
|
}
|
|
|
|
Q_UNIMPLEMENTED();
|
|
|
|
}
|
|
|
|
|
|
|
|
void LLVMInstructionSelection::visitJump(IR::Jump *s)
|
|
|
|
{
|
|
|
|
CreateBr(getLLVMBasicBlock(s->target));
|
|
|
|
}
|
|
|
|
|
|
|
|
void LLVMInstructionSelection::visitCJump(IR::CJump *s)
|
|
|
|
{
|
|
|
|
CreateCondBr(getLLVMCondition(s->cond),
|
|
|
|
getLLVMBasicBlock(s->iftrue),
|
2012-06-05 10:15:41 +00:00
|
|
|
getLLVMBasicBlock(s->iffalse));
|
2012-05-31 17:02:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void LLVMInstructionSelection::visitRet(IR::Ret *s)
|
|
|
|
{
|
2012-06-05 10:15:41 +00:00
|
|
|
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();
|
2012-05-31 17:02:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void LLVMInstructionSelection::visitConst(IR::Const *e)
|
|
|
|
{
|
2012-06-05 10:15:41 +00:00
|
|
|
llvm::Value *k = llvm::ConstantFP::get(_numberTy, e->value);
|
|
|
|
llvm::Value *tmp = CreateAlloca(_valueTy);
|
|
|
|
CreateCall2(_llvmModule->getFunction("__qmljs_llvm_init_number"), tmp, k);
|
|
|
|
_llvmValue = CreateLoad(tmp);
|
2012-05-31 17:02:05 +00:00
|
|
|
}
|
|
|
|
|
2012-06-05 10:15:41 +00:00
|
|
|
void LLVMInstructionSelection::visitString(IR::String *e)
|
2012-05-31 17:02:05 +00:00
|
|
|
{
|
2012-06-05 10:15:41 +00:00
|
|
|
llvm::Value *tmp = CreateAlloca(_valueTy);
|
|
|
|
CreateCall3(_llvmModule->getFunction("__qmljs_llvm_init_string"),
|
|
|
|
_llvmFunction->arg_begin(), tmp,
|
|
|
|
getStringPtr(*e->value));
|
|
|
|
_llvmValue = CreateLoad(tmp);
|
2012-05-31 17:02:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void LLVMInstructionSelection::visitName(IR::Name *)
|
|
|
|
{
|
|
|
|
Q_UNIMPLEMENTED();
|
|
|
|
}
|
|
|
|
|
|
|
|
void LLVMInstructionSelection::visitTemp(IR::Temp *e)
|
|
|
|
{
|
|
|
|
if (llvm::Value *t = getLLVMTemp(e)) {
|
|
|
|
_llvmValue = CreateLoad(t);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void LLVMInstructionSelection::visitClosure(IR::Closure *)
|
|
|
|
{
|
|
|
|
Q_UNIMPLEMENTED();
|
|
|
|
}
|
|
|
|
|
2012-06-05 10:15:41 +00:00
|
|
|
void LLVMInstructionSelection::visitUnop(IR::Unop *e)
|
2012-05-31 17:02:05 +00:00
|
|
|
{
|
2012-06-05 10:15:41 +00:00
|
|
|
llvm::Value *expr = getLLVMValue(e->expr);
|
2012-05-31 17:02:05 +00:00
|
|
|
Q_UNIMPLEMENTED();
|
|
|
|
}
|
|
|
|
|
|
|
|
void LLVMInstructionSelection::visitBinop(IR::Binop *e)
|
|
|
|
{
|
2012-06-05 12:30:04 +00:00
|
|
|
llvm::Value *result = CreateAlloca(_valueTy);
|
|
|
|
genBinop(result, e);
|
|
|
|
_llvmValue = CreateLoad(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
void LLVMInstructionSelection::genBinop(llvm::Value *result, IR::Binop *e)
|
|
|
|
{
|
|
|
|
IR::Temp *t1 = e->left->asTemp();
|
|
|
|
IR::Temp *t2 = e->right->asTemp();
|
|
|
|
assert(t1 != 0);
|
|
|
|
assert(t2 != 0);
|
|
|
|
|
|
|
|
llvm::Value *left = getLLVMTemp(t1);
|
|
|
|
llvm::Value *right = getLLVMTemp(t2);
|
|
|
|
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::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);
|
2012-05-31 17:02:05 +00:00
|
|
|
}
|
|
|
|
|
2012-06-05 10:15:41 +00:00
|
|
|
void LLVMInstructionSelection::visitCall(IR::Call *e)
|
2012-05-31 17:02:05 +00:00
|
|
|
{
|
2012-06-05 10:15:41 +00:00
|
|
|
llvm::Value *base = getLLVMValue(e->base);
|
2012-05-31 17:02:05 +00:00
|
|
|
Q_UNIMPLEMENTED();
|
|
|
|
}
|
|
|
|
|
2012-06-05 10:15:41 +00:00
|
|
|
void LLVMInstructionSelection::visitNew(IR::New *e)
|
2012-05-31 17:02:05 +00:00
|
|
|
{
|
2012-06-05 10:15:41 +00:00
|
|
|
llvm::Value *base = getLLVMValue(e->base);
|
2012-05-31 17:02:05 +00:00
|
|
|
Q_UNIMPLEMENTED();
|
|
|
|
}
|
|
|
|
|
|
|
|
void LLVMInstructionSelection::visitSubscript(IR::Subscript *)
|
|
|
|
{
|
|
|
|
Q_UNIMPLEMENTED();
|
|
|
|
}
|
|
|
|
|
|
|
|
void LLVMInstructionSelection::visitMember(IR::Member *)
|
|
|
|
{
|
|
|
|
Q_UNIMPLEMENTED();
|
|
|
|
}
|
|
|
|
|