diff options
Diffstat (limited to 'src/qml/compiler/qv4isel_moth.cpp')
-rw-r--r-- | src/qml/compiler/qv4isel_moth.cpp | 420 |
1 files changed, 131 insertions, 289 deletions
diff --git a/src/qml/compiler/qv4isel_moth.cpp b/src/qml/compiler/qv4isel_moth.cpp index b452c4b3d9..5dd722bfc2 100644 --- a/src/qml/compiler/qv4isel_moth.cpp +++ b/src/qml/compiler/qv4isel_moth.cpp @@ -46,8 +46,7 @@ #include <private/qv4regexpobject_p.h> #include <private/qv4compileddata_p.h> #include <private/qqmlengine_p.h> -#include "qml/qqmlaccessors_p.h" -#include "qml/qqmlpropertycache_p.h" +#include <wtf/MathExtras.h> #undef USE_TYPE_INFO @@ -151,171 +150,10 @@ inline bool isBoolType(IR::Expr *e) return (e->type == IR::BoolType); } -/* - * stack slot allocation: - * - * foreach bb do - * foreach stmt do - * if the current statement is not a phi-node: - * purge ranges that end before the current statement - * check for life ranges to activate, and if they don't have a stackslot associated then allocate one - * renumber temps to stack - * for phi nodes: check if all temps (src+dst) are assigned stack slots and marked as allocated - * if it's a jump: - * foreach phi node in the successor: - * allocate slots for each temp (both sources and targets) if they don't have one allocated already - * insert moves before the jump - */ -class AllocateStackSlots: protected ConvertTemps -{ - IR::LifeTimeIntervals::Ptr _intervals; - QVector<IR::LifeTimeInterval *> _unhandled; - QVector<IR::LifeTimeInterval *> _live; - QBitArray _slotIsInUse; - IR::Function *_function; - - int defPosition(IR::Stmt *s) const - { - return usePosition(s) + 1; - } - - int usePosition(IR::Stmt *s) const - { - return _intervals->positionForStatement(s); - } - -public: - AllocateStackSlots(const IR::LifeTimeIntervals::Ptr &intervals) - : _intervals(intervals) - , _slotIsInUse(intervals->size(), false) - , _function(0) - { - _live.reserve(8); - _unhandled = _intervals->intervals(); - } - - void forFunction(IR::Function *function) - { - IR::Optimizer::showMeTheCode(function, "Before stack slot allocation"); - _function = function; - toStackSlots(function); - } - -protected: - virtual int allocateFreeSlot() - { - for (int i = 0, ei = _slotIsInUse.size(); i != ei; ++i) { - if (!_slotIsInUse[i]) { - if (_nextUnusedStackSlot <= i) { - Q_ASSERT(_nextUnusedStackSlot == i); - _nextUnusedStackSlot = i + 1; - } - _slotIsInUse[i] = true; - return i; - } - } - - Q_UNREACHABLE(); - return -1; - } - - virtual void process(IR::Stmt *s) - { -// qDebug("L%d statement %d:", _currentBasicBlock->index, s->id); - - if (IR::Phi *phi = s->asPhi()) { - visitPhi(phi); - } else { - // purge ranges no longer alive: - for (int i = 0; i < _live.size(); ) { - const IR::LifeTimeInterval *lti = _live.at(i); - if (lti->end() < usePosition(s)) { -// qDebug() << "\t - moving temp" << lti->temp().index << "to handled, freeing slot" << _stackSlotForTemp[lti->temp().index]; - _live.remove(i); - Q_ASSERT(_slotIsInUse[_stackSlotForTemp[lti->temp().index]]); - _slotIsInUse[_stackSlotForTemp[lti->temp().index]] = false; - continue; - } else { - ++i; - } - } - - // active new ranges: - while (!_unhandled.isEmpty()) { - IR::LifeTimeInterval *lti = _unhandled.last(); - if (lti->start() > defPosition(s)) - break; // we're done - Q_ASSERT(!_stackSlotForTemp.contains(lti->temp().index)); - _stackSlotForTemp[lti->temp().index] = allocateFreeSlot(); -// qDebug() << "\t - activating temp" << lti->temp().index << "on slot" << _stackSlotForTemp[lti->temp().index]; - _live.append(lti); - _unhandled.removeLast(); - } - - s->accept(this); - } - - if (IR::Jump *jump = s->asJump()) { - IR::MoveMapping moves; - for (IR::Stmt *succStmt : jump->target->statements()) { - if (IR::Phi *phi = succStmt->asPhi()) { - forceActivation(*phi->targetTemp); - for (int i = 0, ei = phi->incoming.size(); i != ei; ++i) { - IR::Expr *e = phi->incoming[i]; - if (IR::Temp *t = e->asTemp()) { - forceActivation(*t); - } - if (jump->target->in[i] == _currentBasicBlock) - moves.add(phi->incoming[i], phi->targetTemp); - } - } else { - break; - } - } - moves.order(); - QList<IR::Move *> newMoves = moves.insertMoves(_currentBasicBlock, _function, true); - foreach (IR::Move *move, newMoves) - move->accept(this); - } - } - - void forceActivation(const IR::Temp &t) - { - if (_stackSlotForTemp.contains(t.index)) - return; - - int i = _unhandled.size() - 1; - for (; i >= 0; --i) { - IR::LifeTimeInterval *lti = _unhandled[i]; - if (lti->temp() == t) { - _live.append(lti); - _unhandled.remove(i); - break; - } - } - Q_ASSERT(i >= 0); // check that we always found the entry - - _stackSlotForTemp[t.index] = allocateFreeSlot(); -// qDebug() << "\t - force activating temp" << t.index << "on slot" << _stackSlotForTemp[t.index]; - } - - virtual void visitPhi(IR::Phi *phi) - { - Q_UNUSED(phi); -#if !defined(QT_NO_DEBUG) - Q_ASSERT(_stackSlotForTemp.contains(phi->targetTemp->index)); - Q_ASSERT(_slotIsInUse[_stackSlotForTemp[phi->targetTemp->index]]); - foreach (IR::Expr *e, phi->incoming) { - if (IR::Temp *t = e->asTemp()) - Q_ASSERT(_stackSlotForTemp.contains(t->index)); - } -#endif // defined(QT_NO_DEBUG) - } -}; } // anonymous namespace -InstructionSelection::InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) - : EvalInstructionSelection(execAllocator, module, jsGenerator) +InstructionSelection::InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator, EvalISelFactory *iselFactory) + : EvalInstructionSelection(execAllocator, module, jsGenerator, iselFactory) , qmlEngine(qmlEngine) , _block(0) , _codeStart(0) @@ -361,7 +199,7 @@ void InstructionSelection::run(int functionIndex) qEnvironmentVariableIsEmpty("QV4_NO_INTERPRETER_STACK_SLOT_ALLOCATION"); if (doStackSlotAllocation) { - AllocateStackSlots(opt.lifeTimeIntervals()).forFunction(_function); + IR::AllocateStackSlots(opt.lifeTimeIntervals()).forFunction(_function); } else { opt.convertOutOfSSA(); ConvertTemps().toStackSlots(_function); @@ -371,7 +209,7 @@ void InstructionSelection::run(int functionIndex) ConvertTemps().toStackSlots(_function); } - QSet<IR::Jump *> removableJumps = opt.calculateOptionalJumps(); + BitVector removableJumps = opt.calculateOptionalJumps(); qSwap(_removableJumps, removableJumps); IR::Stmt *cs = 0; @@ -413,6 +251,7 @@ void InstructionSelection::run(int functionIndex) if (s->location.startLine != currentLine) { blockNeedsDebugInstruction = false; currentLine = s->location.startLine; +#ifndef QT_NO_QML_DEBUGGER if (irModule->debugMode) { Instruction::Debug debug; debug.lineNumber = currentLine; @@ -422,10 +261,11 @@ void InstructionSelection::run(int functionIndex) line.lineNumber = currentLine; addInstruction(line); } +#endif } } - s->accept(this); + visit(s); } } @@ -452,7 +292,7 @@ QQmlRefPointer<QV4::CompiledData::CompilationUnit> InstructionSelection::backend { compilationUnit->codeRefs.resize(irModule->functions.size()); int i = 0; - foreach (IR::Function *irFunction, irModule->functions) + for (IR::Function *irFunction : qAsConst(irModule->functions)) compilationUnit->codeRefs[i++] = codeRefs[irFunction]; QQmlRefPointer<QV4::CompiledData::CompilationUnit> result; result.adopt(compilationUnit.take()); @@ -739,61 +579,20 @@ void InstructionSelection::setQObjectProperty(IR::Expr *source, IR::Expr *target addInstruction(store); } -void InstructionSelection::getQmlContextProperty(IR::Expr *source, IR::Member::MemberKind kind, - QQmlPropertyData *property, int index, - IR::Expr *target) -{ - if (property && property->hasAccessors() && property->isFullyResolved()) { - if (kind == IR::Member::MemberOfQmlScopeObject) { - if (property->propType == QMetaType::QReal) { - Instruction::LoadScopeObjectQRealPropertyDirectly load; - load.base = getParam(source); - load.accessors = property->accessors; - load.result = getResultParam(target); - addInstruction(load); - return; - } else if (property->isQObject()) { - Instruction::LoadScopeObjectQObjectPropertyDirectly load; - load.base = getParam(source); - load.accessors = property->accessors; - load.result = getResultParam(target); - addInstruction(load); - return; - } else if (property->propType == QMetaType::Int) { - Instruction::LoadScopeObjectIntPropertyDirectly load; - load.base = getParam(source); - load.accessors = property->accessors; - load.result = getResultParam(target); - addInstruction(load); - return; - } else if (property->propType == QMetaType::Bool) { - Instruction::LoadScopeObjectBoolPropertyDirectly load; - load.base = getParam(source); - load.accessors = property->accessors; - load.result = getResultParam(target); - addInstruction(load); - return; - } else if (property->propType == QMetaType::QString) { - Instruction::LoadScopeObjectQStringPropertyDirectly load; - load.base = getParam(source); - load.accessors = property->accessors; - load.result = getResultParam(target); - addInstruction(load); - return; - } - } - } - +void InstructionSelection::getQmlContextProperty(IR::Expr *source, IR::Member::MemberKind kind, int index, bool captureRequired, IR::Expr *target) +{ if (kind == IR::Member::MemberOfQmlScopeObject) { Instruction::LoadScopeObjectProperty load; load.base = getParam(source); load.propertyIndex = index; + load.captureRequired = captureRequired; load.result = getResultParam(target); addInstruction(load); } else if (kind == IR::Member::MemberOfQmlContextObject) { Instruction::LoadContextObjectProperty load; load.base = getParam(source); load.propertyIndex = index; + load.captureRequired = captureRequired; load.result = getResultParam(target); addInstruction(load); } else if (kind == IR::Member::MemberOfIdObjectsArray) { @@ -807,59 +606,8 @@ void InstructionSelection::getQmlContextProperty(IR::Expr *source, IR::Member::M } } -void InstructionSelection::getQObjectProperty(IR::Expr *base, QQmlPropertyData *property, bool captureRequired, bool isSingletonProperty, int attachedPropertiesId, IR::Expr *target) -{ - if (property && property->hasAccessors() && property->isFullyResolved()) { - if (!attachedPropertiesId && !isSingletonProperty) { - if (property->propType == QMetaType::QReal) { - Instruction::LoadQRealQObjectPropertyDirectly load; - load.base = getParam(base); - load.accessors = property->accessors; - load.coreIndex = property->coreIndex; - load.notifyIndex = captureRequired ? property->notifyIndex : -1; - load.result = getResultParam(target); - addInstruction(load); - return; - } else if (property->isQObject()) { - Instruction::LoadQObjectQObjectPropertyDirectly load; - load.base = getParam(base); - load.accessors = property->accessors; - load.coreIndex = property->coreIndex; - load.notifyIndex = captureRequired ? property->notifyIndex : -1; - load.result = getResultParam(target); - addInstruction(load); - return; - } else if (property->propType == QMetaType::Int) { - Instruction::LoadIntQObjectPropertyDirectly load; - load.base = getParam(base); - load.accessors = property->accessors; - load.coreIndex = property->coreIndex; - load.notifyIndex = captureRequired ? property->notifyIndex : -1; - load.result = getResultParam(target); - addInstruction(load); - return; - } else if (property->propType == QMetaType::Bool) { - Instruction::LoadBoolQObjectPropertyDirectly load; - load.base = getParam(base); - load.accessors = property->accessors; - load.coreIndex = property->coreIndex; - load.notifyIndex = captureRequired ? property->notifyIndex : -1; - load.result = getResultParam(target); - addInstruction(load); - return; - } else if (property->propType == QMetaType::QString) { - Instruction::LoadQStringQObjectPropertyDirectly load; - load.base = getParam(base); - load.accessors = property->accessors; - load.coreIndex = property->coreIndex; - load.notifyIndex = captureRequired ? property->notifyIndex : -1; - load.result = getResultParam(target); - addInstruction(load); - return; - } - } - } - const int propertyIndex = property->coreIndex; +void InstructionSelection::getQObjectProperty(IR::Expr *base, int propertyIndex, bool captureRequired, bool isSingletonProperty, int attachedPropertiesId, IR::Expr *target) +{ if (attachedPropertiesId != 0) { Instruction::LoadAttachedQObjectProperty load; load.propertyIndex = propertyIndex; @@ -1187,18 +935,25 @@ void InstructionSelection::prepareCallArgs(IR::ExprList *e, quint32 &argc, quint } } -void InstructionSelection::visitJump(IR::Jump *s) +void InstructionSelection::addDebugInstruction() { - if (s->target == _nextBlock) - return; - if (_removableJumps.contains(s)) - return; - +#ifndef QT_NO_QML_DEBUGGER if (blockNeedsDebugInstruction) { Instruction::Debug debug; debug.lineNumber = -int(currentLine); addInstruction(debug); } +#endif +} + +void InstructionSelection::visitJump(IR::Jump *s) +{ + if (s->target == _nextBlock) + return; + if (_removableJumps.at(_block->index())) + return; + + addDebugInstruction(); Instruction::Jump jump; jump.offset = 0; @@ -1209,11 +964,7 @@ void InstructionSelection::visitJump(IR::Jump *s) void InstructionSelection::visitCJump(IR::CJump *s) { - if (blockNeedsDebugInstruction) { - Instruction::Debug debug; - debug.lineNumber = -int(currentLine); - addInstruction(debug); - } + addDebugInstruction(); Param condition; if (IR::Temp *t = s->cond->asTemp()) { @@ -1248,12 +999,8 @@ void InstructionSelection::visitCJump(IR::CJump *s) void InstructionSelection::visitRet(IR::Ret *s) { - if (blockNeedsDebugInstruction) { - // this is required so stepOut will always be guaranteed to stop in every stack frame - Instruction::Debug debug; - debug.lineNumber = -int(currentLine); - addInstruction(debug); - } + // this is required so stepOut will always be guaranteed to stop in every stack frame + addDebugInstruction(); Instruction::Ret ret; ret.result = getParam(s->expr); @@ -1589,12 +1336,7 @@ void QV4::Moth::InstructionSelection::callBuiltinConvertThisToObject() ptrdiff_t InstructionSelection::addInstructionHelper(Instr::Type type, Instr &instr) { - -#ifdef MOTH_THREADED_INTERPRETER - instr.common.code = VME::instructionJumpTable()[static_cast<int>(type)]; -#else instr.common.instructionType = type; -#endif int instructionSize = Instr::size(type); if (_codeEnd - _codeNext < instructionSize) { @@ -1680,6 +1422,29 @@ CompilationUnit::~CompilationUnit() void CompilationUnit::linkBackendToEngine(QV4::ExecutionEngine *engine) { +#ifdef MOTH_THREADED_INTERPRETER + // link byte code against addresses of instructions + for (int i = 0; i < codeRefs.count(); ++i) { + QByteArray &codeRef = codeRefs[i]; + char *code = codeRef.data(); + int index = 0; + while (index < codeRef.size()) { + Instr *genericInstr = reinterpret_cast<Instr *>(code + index); + + switch (genericInstr->common.instructionType) { +#define LINK_INSTRUCTION(InstructionType, Member) \ + case Instr::InstructionType: \ + genericInstr->common.code = VME::instructionJumpTable()[static_cast<int>(genericInstr->common.instructionType)]; \ + index += InstrMeta<(int)Instr::InstructionType>::Size; \ + break; + + FOR_EACH_MOTH_INSTR(LINK_INSTRUCTION) + + } + } + } +#endif + runtimeFunctions.resize(data->functionTableSize); runtimeFunctions.fill(0); for (int i = 0 ;i < runtimeFunctions.size(); ++i) { @@ -1690,3 +1455,80 @@ void CompilationUnit::linkBackendToEngine(QV4::ExecutionEngine *engine) runtimeFunctions[i] = runtimeFunction; } } + +void CompilationUnit::prepareCodeOffsetsForDiskStorage(CompiledData::Unit *unit) +{ + const int codeAlignment = 16; + quint64 offset = WTF::roundUpToMultipleOf(codeAlignment, unit->unitSize); + Q_ASSERT(int(unit->functionTableSize) == codeRefs.size()); + for (int i = 0; i < codeRefs.size(); ++i) { + CompiledData::Function *compiledFunction = const_cast<CompiledData::Function *>(unit->functionAt(i)); + compiledFunction->codeOffset = offset; + compiledFunction->codeSize = codeRefs.at(i).size(); + offset = WTF::roundUpToMultipleOf(codeAlignment, offset + compiledFunction->codeSize); + } +} + +bool CompilationUnit::saveCodeToDisk(QIODevice *device, const CompiledData::Unit *unit, QString *errorString) +{ + Q_ASSERT(device->pos() == unit->unitSize); + Q_ASSERT(device->atEnd()); + Q_ASSERT(int(unit->functionTableSize) == codeRefs.size()); + + QByteArray padding; + + for (int i = 0; i < codeRefs.size(); ++i) { + const CompiledData::Function *compiledFunction = unit->functionAt(i); + + if (device->pos() > qint64(compiledFunction->codeOffset)) { + *errorString = QStringLiteral("Invalid state of cache file to write."); + return false; + } + + const quint64 paddingSize = compiledFunction->codeOffset - device->pos(); + padding.fill(0, paddingSize); + qint64 written = device->write(padding); + if (written != padding.size()) { + *errorString = device->errorString(); + return false; + } + + const void *codePtr = codeRefs.at(i).constData(); + written = device->write(reinterpret_cast<const char *>(codePtr), compiledFunction->codeSize); + if (written != qint64(compiledFunction->codeSize)) { + *errorString = device->errorString(); + return false; + } + } + return true; +} + +bool CompilationUnit::memoryMapCode(QString *errorString) +{ + Q_UNUSED(errorString); + codeRefs.resize(data->functionTableSize); + + const char *basePtr = reinterpret_cast<const char *>(data); + + for (uint i = 0; i < data->functionTableSize; ++i) { + const CompiledData::Function *compiledFunction = data->functionAt(i); + const char *codePtr = const_cast<const char *>(reinterpret_cast<const char *>(basePtr + compiledFunction->codeOffset)); +#ifdef MOTH_THREADED_INTERPRETER + // for the threaded interpreter we need to make a copy of the data because it needs to be + // modified for the instruction handler addresses. + QByteArray code(codePtr, compiledFunction->codeSize); +#else + QByteArray code = QByteArray::fromRawData(codePtr, compiledFunction->codeSize); +#endif + codeRefs[i] = code; + } + + return true; +} + +QQmlRefPointer<CompiledData::CompilationUnit> ISelFactory::createUnitForLoading() +{ + QQmlRefPointer<CompiledData::CompilationUnit> result; + result.adopt(new Moth::CompilationUnit); + return result; +} |