diff options
author | Ulf Hermann <ulf.hermann@qt.io> | 2019-04-18 14:22:10 +0200 |
---|---|---|
committer | Ulf Hermann <ulf.hermann@qt.io> | 2019-04-29 09:16:34 +0000 |
commit | 9e5ca92712da3392d1f2957dc1e546cdddd1ce0a (patch) | |
tree | a6f12507c544a03f637dd96f150286325ad0d6a4 /src | |
parent | 9c25df6dd0412d0e4211370843d8cbb8aef1e512 (diff) |
Remove tracing JIT infrastructure
The tracing JIT won't be finished. Therefore, remove the parts that have
already been integrated.
Change-Id: If72036be904bd7fc17ba9bcba0a317f8ed6cb30d
Reviewed-by: Erik Verbruggen <erik.verbruggen@me.com>
Diffstat (limited to 'src')
47 files changed, 176 insertions, 9812 deletions
diff --git a/src/qml/compiler/qv4bytecodegenerator_p.h b/src/qml/compiler/qv4bytecodegenerator_p.h index 1d0a57c536..ab8661dbe3 100644 --- a/src/qml/compiler/qv4bytecodegenerator_p.h +++ b/src/qml/compiler/qv4bytecodegenerator_p.h @@ -66,8 +66,6 @@ namespace Moth { class BytecodeGenerator { public: - typedef CompiledData::Function::TraceInfoCount TraceInfoCount; - BytecodeGenerator(int line, bool debug) : startLine(line), debugMode(debug) {} @@ -164,15 +162,6 @@ public: addInstructionHelper(Moth::Instr::Type(InstrT), genericInstr); } - // Same as addInstruction, but also add a trace slot. Move only, because the instruction cannot - // be reused afterwards. - template<int InstrT> - void addTracingInstruction(InstrData<InstrT> data) - { - data.traceSlot = nextTraceInfo(); - addInstruction(data); - } - Q_REQUIRED_RESULT Jump jump() { QT_WARNING_PUSH @@ -184,12 +173,12 @@ QT_WARNING_POP Q_REQUIRED_RESULT Jump jumpTrue() { - return addTracingJumpInstruction(Instruction::JumpTrue()); + return addJumpInstruction(Instruction::JumpTrue()); } Q_REQUIRED_RESULT Jump jumpFalse() { - return addTracingJumpInstruction(Instruction::JumpFalse()); + return addJumpInstruction(Instruction::JumpFalse()); } Q_REQUIRED_RESULT Jump jumpNotUndefined() @@ -209,7 +198,7 @@ QT_WARNING_POP Instruction::CmpStrictEqual cmp; cmp.lhs = lhs; addInstruction(std::move(cmp)); - addTracingJumpInstruction(Instruction::JumpTrue()).link(target); + addJumpInstruction(Instruction::JumpTrue()).link(target); } void jumpStrictNotEqual(const StackSlot &lhs, const Label &target) @@ -217,7 +206,7 @@ QT_WARNING_POP Instruction::CmpStrictNotEqual cmp; cmp.lhs = lhs; addInstruction(std::move(cmp)); - addTracingJumpInstruction(Instruction::JumpTrue()).link(target); + addJumpInstruction(Instruction::JumpTrue()).link(target); } void setUnwindHandler(ExceptionHandler *handler) @@ -258,13 +247,6 @@ QT_WARNING_POP void finalize(Compiler::Context *context); template<int InstrT> - Jump addTracingJumpInstruction(InstrData<InstrT> &&data) - { - data.traceSlot = nextTraceInfo(); - return addJumpInstruction(data); - } - - template<int InstrT> Jump addJumpInstruction(const InstrData<InstrT> &data) { Instr genericInstr; @@ -275,9 +257,9 @@ QT_WARNING_POP void addCJumpInstruction(bool jumpOnFalse, const Label *trueLabel, const Label *falseLabel) { if (jumpOnFalse) - addTracingJumpInstruction(Instruction::JumpFalse()).link(*falseLabel); + addJumpInstruction(Instruction::JumpFalse()).link(*falseLabel); else - addTracingJumpInstruction(Instruction::JumpTrue()).link(*trueLabel); + addJumpInstruction(Instruction::JumpTrue()).link(*trueLabel); } void clearLastInstruction() @@ -285,27 +267,6 @@ QT_WARNING_POP lastInstrType = -1; } - TraceInfoCount nextTraceInfo() - { - // If tracing is disabled, use slot 0 to unconditionally store all trace info - if (nTraceInfos == CompiledData::Function::NoTracing()) - return TraceInfoCount(0); - return nTraceInfos++; - } - - void setTracing(bool onoff, int argumentCount) - { - if (onoff) - nTraceInfos = argumentCount; - else - nTraceInfos = CompiledData::Function::NoTracing(); - } - - TraceInfoCount traceInfoCount() const - { - return nTraceInfos; - } - void addLoopStart(const Label &start) { _labelInfos.push_back({ start.index }); @@ -346,8 +307,6 @@ private: int lastInstrType = -1; Moth::Instr lastInstr; - TraceInfoCount nTraceInfos = TraceInfoCount(0); - struct LabelInfo { int labelIndex; }; diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index 88d3dbe9c5..1537ce408d 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -289,13 +289,13 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr) case UMinus: { expr.loadInAccumulator(); Instruction::UMinus uminus = {}; - bytecodeGenerator->addTracingInstruction(uminus); + bytecodeGenerator->addInstruction(uminus); return Reference::fromAccumulator(this); } case UPlus: { expr.loadInAccumulator(); Instruction::UPlus uplus = {}; - bytecodeGenerator->addTracingInstruction(uplus); + bytecodeGenerator->addInstruction(uplus); return Reference::fromAccumulator(this); } case Not: { @@ -315,10 +315,10 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr) Reference e = expr.asLValue(); e.loadInAccumulator(); Instruction::UPlus uplus = {}; - bytecodeGenerator->addTracingInstruction(uplus); + bytecodeGenerator->addInstruction(uplus); Reference originalValue = Reference::fromStackSlot(this).storeRetainAccumulator(); Instruction::Increment inc = {}; - bytecodeGenerator->addTracingInstruction(inc); + bytecodeGenerator->addInstruction(inc); e.storeConsumeAccumulator(); return originalValue; } else { @@ -330,7 +330,7 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr) Reference e = expr.asLValue(); e.loadInAccumulator(); Instruction::Increment inc = {}; - bytecodeGenerator->addTracingInstruction(inc); + bytecodeGenerator->addInstruction(inc); if (exprAccept(nx)) return e.storeConsumeAccumulator(); else @@ -341,10 +341,10 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr) Reference e = expr.asLValue(); e.loadInAccumulator(); Instruction::UPlus uplus = {}; - bytecodeGenerator->addTracingInstruction(uplus); + bytecodeGenerator->addInstruction(uplus); Reference originalValue = Reference::fromStackSlot(this).storeRetainAccumulator(); Instruction::Decrement dec = {}; - bytecodeGenerator->addTracingInstruction(dec); + bytecodeGenerator->addInstruction(dec); e.storeConsumeAccumulator(); return originalValue; } else { @@ -356,7 +356,7 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr) Reference e = expr.asLValue(); e.loadInAccumulator(); Instruction::Decrement dec = {}; - bytecodeGenerator->addTracingInstruction(dec); + bytecodeGenerator->addInstruction(dec); if (exprAccept(nx)) return e.storeConsumeAccumulator(); else @@ -1139,7 +1139,7 @@ bool Codegen::visit(ArrayPattern *ast) index.loadInAccumulator(); Instruction::Increment inc = {}; - bytecodeGenerator->addTracingInstruction(inc); + bytecodeGenerator->addInstruction(inc); index.storeConsumeAccumulator(); }; @@ -1196,7 +1196,7 @@ bool Codegen::visit(ArrayPattern *ast) next.value = lhsValue.stackSlot(); next.done = iteratorDone.stackSlot(); bytecodeGenerator->addInstruction(next); - bytecodeGenerator->addTracingJumpInstruction(Instruction::JumpTrue()).link(end); + bytecodeGenerator->addJumpInstruction(Instruction::JumpTrue()).link(end); lhsValue.loadInAccumulator(); pushAccumulator(); @@ -1487,20 +1487,20 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re right.loadInAccumulator(); Instruction::Add add; add.lhs = left.stackSlot(); - bytecodeGenerator->addTracingInstruction(add); + bytecodeGenerator->addInstruction(add); break; } case QSOperator::Sub: { if (right.isConstant() && right.constant == Encode(int(1))) { left.loadInAccumulator(); Instruction::Decrement dec = {}; - bytecodeGenerator->addTracingInstruction(dec); + bytecodeGenerator->addInstruction(dec); } else { left = left.storeOnStack(); right.loadInAccumulator(); Instruction::Sub sub; sub.lhs = left.stackSlot(); - bytecodeGenerator->addTracingInstruction(sub); + bytecodeGenerator->addInstruction(sub); } break; } @@ -1517,7 +1517,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re right.loadInAccumulator(); Instruction::Mul mul; mul.lhs = left.stackSlot(); - bytecodeGenerator->addTracingInstruction(mul); + bytecodeGenerator->addInstruction(mul); break; } case QSOperator::Div: { @@ -1533,7 +1533,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re right.loadInAccumulator(); Instruction::Mod mod; mod.lhs = left.stackSlot(); - bytecodeGenerator->addTracingInstruction(mod); + bytecodeGenerator->addInstruction(mod); break; } case QSOperator::BitAnd: @@ -1902,7 +1902,7 @@ bool Codegen::visit(CallExpression *ast) call.thisObject = baseObject.stackSlot(); call.argc = calldata.argc; call.argv = calldata.argv; - bytecodeGenerator->addTracingInstruction(call); + bytecodeGenerator->addInstruction(call); } else { Instruction::TailCall call; call.func = base.stackSlot(); @@ -1931,14 +1931,14 @@ void Codegen::handleCall(Reference &base, Arguments calldata, int slotForFunctio call.lookupIndex = registerGetterLookup(base.propertyNameIndex); call.argc = calldata.argc; call.argv = calldata.argv; - bytecodeGenerator->addTracingInstruction(call); + bytecodeGenerator->addInstruction(call); } else { Instruction::CallProperty call; call.base = base.propertyBase.stackSlot(); call.name = base.propertyNameIndex; call.argc = calldata.argc; call.argv = calldata.argv; - bytecodeGenerator->addTracingInstruction(call); + bytecodeGenerator->addInstruction(call); } } else if (base.type == Reference::Subscript) { Instruction::CallElement call; @@ -1946,33 +1946,33 @@ void Codegen::handleCall(Reference &base, Arguments calldata, int slotForFunctio call.index = base.elementSubscript.stackSlot(); call.argc = calldata.argc; call.argv = calldata.argv; - bytecodeGenerator->addTracingInstruction(call); + bytecodeGenerator->addInstruction(call); } else if (base.type == Reference::Name) { if (base.name == QStringLiteral("eval")) { Instruction::CallPossiblyDirectEval call; call.argc = calldata.argc; call.argv = calldata.argv; - bytecodeGenerator->addTracingInstruction(call); + bytecodeGenerator->addInstruction(call); } else if (!disable_lookups && useFastLookups && base.global) { if (base.qmlGlobal) { Instruction::CallQmlContextPropertyLookup call; call.index = registerQmlContextPropertyGetterLookup(base.nameAsIndex()); call.argc = calldata.argc; call.argv = calldata.argv; - bytecodeGenerator->addTracingInstruction(call); + bytecodeGenerator->addInstruction(call); } else { Instruction::CallGlobalLookup call; call.index = registerGlobalGetterLookup(base.nameAsIndex()); call.argc = calldata.argc; call.argv = calldata.argv; - bytecodeGenerator->addTracingInstruction(call); + bytecodeGenerator->addInstruction(call); } } else { Instruction::CallName call; call.name = base.nameAsIndex(); call.argc = calldata.argc; call.argv = calldata.argv; - bytecodeGenerator->addTracingInstruction(call); + bytecodeGenerator->addInstruction(call); } } else if (base.type == Reference::SuperProperty) { Reference receiver = base.baseObject(); @@ -1989,14 +1989,14 @@ void Codegen::handleCall(Reference &base, Arguments calldata, int slotForFunctio call.thisObject = receiver.stackSlot(); call.argc = calldata.argc; call.argv = calldata.argv; - bytecodeGenerator->addTracingInstruction(call); + bytecodeGenerator->addInstruction(call); } else { Q_ASSERT(base.isStackSlot()); Instruction::CallValue call; call.name = base.stackSlot(); call.argc = calldata.argc; call.argv = calldata.argv; - bytecodeGenerator->addTracingInstruction(call); + bytecodeGenerator->addInstruction(call); } setExprResult(Reference::fromAccumulator(this)); @@ -2732,14 +2732,14 @@ bool Codegen::visit(TemplateLiteral *ast) Instruction::Add instr; instr.lhs = temp2; - bytecodeGenerator->addTracingInstruction(instr); + bytecodeGenerator->addInstruction(instr); } else { expr.loadInAccumulator(); } Instruction::Add instr; instr.lhs = temp; - bytecodeGenerator->addTracingInstruction(instr); + bytecodeGenerator->addInstruction(instr); } auto r = Reference::fromAccumulator(this); @@ -2997,7 +2997,6 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, bool savedFunctionEndsWithReturn = functionEndsWithReturn; functionEndsWithReturn = endsWithReturn(_module, body); - bytecodeGenerator->setTracing(_functionContext->canUseTracingJit(), _context->arguments.size()); // reserve the js stack frame (Context & js Function & accumulator) bytecodeGenerator->newRegisterArray(sizeof(CallData)/sizeof(Value) - 1 + _context->arguments.size()); @@ -3084,7 +3083,6 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, Q_ASSERT(_context == _functionContext); bytecodeGenerator->finalize(_context); _context->registerCountInFunction = bytecodeGenerator->registerCount(); - _context->nTraceInfos = bytecodeGenerator->traceInfoCount(); static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_BYTECODE"); if (showCode) { qDebug() << "=== Bytecode for" << _context->name << "strict mode" << _context->isStrict @@ -3288,7 +3286,7 @@ bool Codegen::visit(ForEachStatement *ast) next.value = lhsValue.stackSlot(); next.done = iteratorDone.stackSlot(); bytecodeGenerator->addInstruction(next); - bytecodeGenerator->addTracingJumpInstruction(Instruction::JumpTrue()).link(end); + bytecodeGenerator->addJumpInstruction(Instruction::JumpTrue()).link(end); // each iteration gets it's own context, as per spec { @@ -4225,7 +4223,7 @@ void Codegen::Reference::storeAccumulator() const Instruction::StoreElement store; store.base = elementBase; store.index = elementSubscript.stackSlot(); - codegen->bytecodeGenerator->addTracingInstruction(store); + codegen->bytecodeGenerator->addInstruction(store); } return; case Invalid: case Accumulator: @@ -4315,12 +4313,12 @@ QT_WARNING_POP if (!scope) { Instruction::LoadLocal load; load.index = index; - codegen->bytecodeGenerator->addTracingInstruction(load); + codegen->bytecodeGenerator->addInstruction(load); } else { Instruction::LoadScopedLocal load; load.index = index; load.scope = scope; - codegen->bytecodeGenerator->addTracingInstruction(load); + codegen->bytecodeGenerator->addInstruction(load); } tdzCheck(requiresTDZCheck); return; @@ -4344,16 +4342,16 @@ QT_WARNING_POP if (qmlGlobal) { Instruction::LoadQmlContextPropertyLookup load; load.index = codegen->registerQmlContextPropertyGetterLookup(nameAsIndex()); - codegen->bytecodeGenerator->addTracingInstruction(load); + codegen->bytecodeGenerator->addInstruction(load); } else { Instruction::LoadGlobalLookup load; load.index = codegen->registerGlobalGetterLookup(nameAsIndex()); - codegen->bytecodeGenerator->addTracingInstruction(load); + codegen->bytecodeGenerator->addInstruction(load); } } else { Instruction::LoadName load; load.name = nameAsIndex(); - codegen->bytecodeGenerator->addTracingInstruction(load); + codegen->bytecodeGenerator->addInstruction(load); } return; case Member: @@ -4362,11 +4360,11 @@ QT_WARNING_POP if (!disable_lookups && codegen->useFastLookups) { Instruction::GetLookup load; load.index = codegen->registerGetterLookup(propertyNameIndex); - codegen->bytecodeGenerator->addTracingInstruction(load); + codegen->bytecodeGenerator->addInstruction(load); } else { Instruction::LoadProperty load; load.name = propertyNameIndex; - codegen->bytecodeGenerator->addTracingInstruction(load); + codegen->bytecodeGenerator->addInstruction(load); } return; case Import: { @@ -4381,7 +4379,7 @@ QT_WARNING_POP tdzCheck(subscriptRequiresTDZCheck); Instruction::LoadElement load; load.base = elementBase; - codegen->bytecodeGenerator->addTracingInstruction(load); + codegen->bytecodeGenerator->addInstruction(load); } return; case Invalid: break; diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 4cfd2d86e8..dd7ba471c3 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -79,7 +79,7 @@ QT_BEGIN_NAMESPACE // Also change the comment behind the number to describe the latest change. This has the added // benefit that if another patch changes the version too, it will result in a merge conflict, and // not get removed silently. -#define QV4_DATA_STRUCTURE_VERSION 0x22 // Add trace slot to UPlus +#define QV4_DATA_STRUCTURE_VERSION 0x23 // Remove trace slots class QIODevice; class QQmlPropertyData; @@ -296,10 +296,6 @@ struct Function quint32_le nLabelInfos; size_t labelInfosOffset() const { return lineNumberOffset() + nLineNumbers * sizeof(CodeOffsetToLine); } - typedef quint16_le TraceInfoCount; - TraceInfoCount nTraceInfos; - static constexpr TraceInfoCount NoTracing() { return TraceInfoCount::max(); } - // Keep all unaligned data at the end quint8 flags; quint8 padding1; diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index 01c033cb2a..123d77f788 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -424,7 +424,6 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte Q_ASSERT(function->lineNumberOffset() == currentOffset); currentOffset += function->nLineNumbers * sizeof(CompiledData::CodeOffsetToLine); - function->nTraceInfos = irFunction->nTraceInfos; function->nRegisters = irFunction->registerCountInFunction; if (!irFunction->labelInfo.empty()) { diff --git a/src/qml/compiler/qv4compilercontext.cpp b/src/qml/compiler/qv4compilercontext.cpp index 52215c2ce6..d1a5fee92b 100644 --- a/src/qml/compiler/qv4compilercontext.cpp +++ b/src/qml/compiler/qv4compilercontext.cpp @@ -410,26 +410,4 @@ void Context::setupFunctionIndices(Moth::BytecodeGenerator *bytecodeGenerator) nRegisters = bytecodeGenerator->currentRegister() - registerOffset; } -bool Context::canUseTracingJit() const -{ -#if QT_CONFIG(qml_tracing) - static bool forceTracing = !qEnvironmentVariableIsEmpty("QV4_FORCE_TRACING"); - if (forceTracing) //### we can probably remove this when tracing is turned on by default - return true; // to be used by unittests - - static bool disableTracing = !qEnvironmentVariableIsEmpty("QV4_DISABLE_TRACING"); - if (disableTracing) - return false; - - static QStringList onlyTrace = - qEnvironmentVariable("QV4_ONLY_TRACE").split(QLatin1Char(','), QString::SkipEmptyParts); - if (!onlyTrace.isEmpty()) - return onlyTrace.contains(name); - - return true; -#else - return false; -#endif -} - QT_END_NAMESPACE diff --git a/src/qml/compiler/qv4compilercontext_p.h b/src/qml/compiler/qv4compilercontext_p.h index 57ef4be36e..f56942fffa 100644 --- a/src/qml/compiler/qv4compilercontext_p.h +++ b/src/qml/compiler/qv4compilercontext_p.h @@ -162,7 +162,6 @@ struct Context { int line = 0; int column = 0; int registerCountInFunction = 0; - uint nTraceInfos = 0; int functionIndex = -1; int blockIndex = -1; @@ -363,8 +362,6 @@ struct Context { return parent->canHaveTailCalls(); return false; } - - bool canUseTracingJit() const; }; diff --git a/src/qml/compiler/qv4instr_moth.cpp b/src/qml/compiler/qv4instr_moth.cpp index b019f191fa..e022d14264 100644 --- a/src/qml/compiler/qv4instr_moth.cpp +++ b/src/qml/compiler/qv4instr_moth.cpp @@ -171,8 +171,6 @@ QString dumpArguments(int argc, int argv, int nFormals) return QStringLiteral("(") + dumpRegister(argv, nFormals) + QStringLiteral(", ") + QString::number(argc) + QStringLiteral(")"); } -#define TRACE_SLOT QStringLiteral(" {%1}").arg(traceSlot) - void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*startLine*/, const QVector<CompiledData::CodeOffsetToLine> &lineNumberMapping) { MOTH_JUMP_TABLE; @@ -241,9 +239,9 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_BEGIN_INSTR(LoadLocal) if (index < nLocals) - d << "l" << index << TRACE_SLOT; + d << "l" << index; else - d << "a" << (index - nLocals) << TRACE_SLOT; + d << "a" << (index - nLocals); MOTH_END_INSTR(LoadLocal) MOTH_BEGIN_INSTR(StoreLocal) @@ -255,9 +253,9 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_BEGIN_INSTR(LoadScopedLocal) if (index < nLocals) - d << "l" << index << "@" << scope << TRACE_SLOT; + d << "l" << index << "@" << scope; else - d << "a" << (index - nLocals) << "@" << scope << TRACE_SLOT; + d << "a" << (index - nLocals) << "@" << scope; MOTH_END_INSTR(LoadScopedLocal) MOTH_BEGIN_INSTR(StoreScopedLocal) @@ -280,15 +278,15 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_END_INSTR(LoadClosure) MOTH_BEGIN_INSTR(LoadName) - d << name << TRACE_SLOT; + d << name; MOTH_END_INSTR(LoadName) MOTH_BEGIN_INSTR(LoadGlobalLookup) - d << index << TRACE_SLOT; + d << index; MOTH_END_INSTR(LoadGlobalLookup) MOTH_BEGIN_INSTR(LoadQmlContextPropertyLookup) - d << index << TRACE_SLOT; + d << index; MOTH_END_INSTR(LoadQmlContextPropertyLookup) MOTH_BEGIN_INSTR(StoreNameSloppy) @@ -300,20 +298,19 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_END_INSTR(StoreNameStrict) MOTH_BEGIN_INSTR(LoadElement) - d << dumpRegister(base, nFormals) << "[acc]" << TRACE_SLOT; + d << dumpRegister(base, nFormals) << "[acc]"; MOTH_END_INSTR(LoadElement) MOTH_BEGIN_INSTR(StoreElement) - d << dumpRegister(base, nFormals) << "[" << dumpRegister(index, nFormals) << "]" - << TRACE_SLOT; + d << dumpRegister(base, nFormals) << "[" << dumpRegister(index, nFormals) << "]"; MOTH_END_INSTR(StoreElement) MOTH_BEGIN_INSTR(LoadProperty) - d << "acc[" << name << "]" << TRACE_SLOT; + d << "acc[" << name << "]"; MOTH_END_INSTR(LoadProperty) MOTH_BEGIN_INSTR(GetLookup) - d << "acc(" << index << ")" << TRACE_SLOT; + d << "acc(" << index << ")"; MOTH_END_INSTR(GetLookup) MOTH_BEGIN_INSTR(StoreProperty) @@ -343,49 +340,48 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_END_INSTR(Resume) MOTH_BEGIN_INSTR(CallValue) - d << dumpRegister(name, nFormals) << dumpArguments(argc, argv, nFormals) << TRACE_SLOT; + d << dumpRegister(name, nFormals) << dumpArguments(argc, argv, nFormals); MOTH_END_INSTR(CallValue) MOTH_BEGIN_INSTR(CallWithReceiver) d << dumpRegister(name, nFormals) << dumpRegister(thisObject, nFormals) - << dumpArguments(argc, argv, nFormals) << TRACE_SLOT; + << dumpArguments(argc, argv, nFormals); MOTH_END_INSTR(CallWithReceiver) MOTH_BEGIN_INSTR(CallProperty) d << dumpRegister(base, nFormals) << "." << name << dumpArguments(argc, argv, nFormals) - << TRACE_SLOT; + ; MOTH_END_INSTR(CallProperty) MOTH_BEGIN_INSTR(CallPropertyLookup) d << dumpRegister(base, nFormals) << "." << lookupIndex - << dumpArguments(argc, argv, nFormals) << TRACE_SLOT; + << dumpArguments(argc, argv, nFormals); MOTH_END_INSTR(CallPropertyLookup) MOTH_BEGIN_INSTR(CallElement) d << dumpRegister(base, nFormals) << "[" << dumpRegister(index, nFormals) << "]" - << dumpArguments(argc, argv, nFormals) << TRACE_SLOT; + << dumpArguments(argc, argv, nFormals); MOTH_END_INSTR(CallElement) MOTH_BEGIN_INSTR(CallName) - d << name << dumpArguments(argc, argv, nFormals) << TRACE_SLOT; + d << name << dumpArguments(argc, argv, nFormals); MOTH_END_INSTR(CallName) MOTH_BEGIN_INSTR(CallPossiblyDirectEval) - d << dumpArguments(argc, argv, nFormals) << TRACE_SLOT; + d << dumpArguments(argc, argv, nFormals); MOTH_END_INSTR(CallPossiblyDirectEval) MOTH_BEGIN_INSTR(CallGlobalLookup) - d << index << dumpArguments(argc, argv, nFormals) << TRACE_SLOT; + d << index << dumpArguments(argc, argv, nFormals); MOTH_END_INSTR(CallGlobalLookup) MOTH_BEGIN_INSTR(CallQmlContextPropertyLookup) - d << index << dumpArguments(argc, argv, nFormals) << TRACE_SLOT; + d << index << dumpArguments(argc, argv, nFormals); MOTH_END_INSTR(CallQmlContextPropertyLookup) MOTH_BEGIN_INSTR(CallWithSpread) d << "new " << dumpRegister(func, nFormals) << dumpRegister(thisObject, nFormals) - << dumpArguments(argc, argv, nFormals) - << TRACE_SLOT; + << dumpArguments(argc, argv, nFormals); MOTH_END_INSTR(CallWithSpread) MOTH_BEGIN_INSTR(Construct) @@ -528,11 +524,11 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_END_INSTR(Jump) MOTH_BEGIN_INSTR(JumpTrue) - d << ABSOLUTE_OFFSET() << TRACE_SLOT; + d << ABSOLUTE_OFFSET(); MOTH_END_INSTR(JumpTrue) MOTH_BEGIN_INSTR(JumpFalse) - d << ABSOLUTE_OFFSET() << TRACE_SLOT; + d << ABSOLUTE_OFFSET(); MOTH_END_INSTR(JumpFalse) MOTH_BEGIN_INSTR(JumpNotUndefined) @@ -593,26 +589,22 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_END_INSTR(UNot) MOTH_BEGIN_INSTR(UPlus) - d << TRACE_SLOT; MOTH_END_INSTR(UPlus) MOTH_BEGIN_INSTR(UMinus) - d << TRACE_SLOT; MOTH_END_INSTR(UMinus) MOTH_BEGIN_INSTR(UCompl) MOTH_END_INSTR(UCompl) MOTH_BEGIN_INSTR(Increment) - d << TRACE_SLOT; MOTH_END_INSTR(Increment) MOTH_BEGIN_INSTR(Decrement) - d << TRACE_SLOT; MOTH_END_INSTR(Decrement) MOTH_BEGIN_INSTR(Add) - d << dumpRegister(lhs, nFormals) << ", acc" << TRACE_SLOT; + d << dumpRegister(lhs, nFormals) << ", acc"; MOTH_END_INSTR(Add) MOTH_BEGIN_INSTR(BitAnd) @@ -668,7 +660,7 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_END_INSTR(Exp) MOTH_BEGIN_INSTR(Mul) - d << dumpRegister(lhs, nFormals) << ", acc" << TRACE_SLOT; + d << dumpRegister(lhs, nFormals) << ", acc"; MOTH_END_INSTR(Mul) MOTH_BEGIN_INSTR(Div) @@ -676,11 +668,11 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_END_INSTR(Div) MOTH_BEGIN_INSTR(Mod) - d << dumpRegister(lhs, nFormals) << ", acc" << TRACE_SLOT; + d << dumpRegister(lhs, nFormals) << ", acc"; MOTH_END_INSTR(Mod) MOTH_BEGIN_INSTR(Sub) - d << dumpRegister(lhs, nFormals) << ", acc" << TRACE_SLOT; + d << dumpRegister(lhs, nFormals) << ", acc"; MOTH_END_INSTR(Sub) MOTH_BEGIN_INSTR(CmpIn) diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h index 6a8c9a9549..6421fc9d67 100644 --- a/src/qml/compiler/qv4instr_moth_p.h +++ b/src/qml/compiler/qv4instr_moth_p.h @@ -77,20 +77,20 @@ QT_BEGIN_NAMESPACE #define INSTR_StoreReg(op) INSTRUCTION(op, StoreReg, 1, reg) #define INSTR_MoveReg(op) INSTRUCTION(op, MoveReg, 2, srcReg, destReg) #define INSTR_LoadImport(op) INSTRUCTION(op, LoadImport, 1, index) -#define INSTR_LoadLocal(op) INSTRUCTION(op, LoadLocal, 2, index, traceSlot) +#define INSTR_LoadLocal(op) INSTRUCTION(op, LoadLocal, 1, index) #define INSTR_StoreLocal(op) INSTRUCTION(op, StoreLocal, 1, index) -#define INSTR_LoadScopedLocal(op) INSTRUCTION(op, LoadScopedLocal, 3, scope, index, traceSlot) +#define INSTR_LoadScopedLocal(op) INSTRUCTION(op, LoadScopedLocal, 2, scope, index) #define INSTR_StoreScopedLocal(op) INSTRUCTION(op, StoreScopedLocal, 2, scope, index) #define INSTR_LoadRuntimeString(op) INSTRUCTION(op, LoadRuntimeString, 1, stringId) #define INSTR_MoveRegExp(op) INSTRUCTION(op, MoveRegExp, 2, regExpId, destReg) #define INSTR_LoadClosure(op) INSTRUCTION(op, LoadClosure, 1, value) -#define INSTR_LoadName(op) INSTRUCTION(op, LoadName, 2, name, traceSlot) -#define INSTR_LoadGlobalLookup(op) INSTRUCTION(op, LoadGlobalLookup, 2, index, traceSlot) -#define INSTR_LoadQmlContextPropertyLookup(op) INSTRUCTION(op, LoadQmlContextPropertyLookup, 2, index, traceSlot) +#define INSTR_LoadName(op) INSTRUCTION(op, LoadName, 1, name) +#define INSTR_LoadGlobalLookup(op) INSTRUCTION(op, LoadGlobalLookup, 1, index) +#define INSTR_LoadQmlContextPropertyLookup(op) INSTRUCTION(op, LoadQmlContextPropertyLookup, 1, index) #define INSTR_StoreNameSloppy(op) INSTRUCTION(op, StoreNameSloppy, 1, name) #define INSTR_StoreNameStrict(op) INSTRUCTION(op, StoreNameStrict, 1, name) -#define INSTR_LoadProperty(op) INSTRUCTION(op, LoadProperty, 2, name, traceSlot) -#define INSTR_GetLookup(op) INSTRUCTION(op, GetLookup, 2, index, traceSlot) +#define INSTR_LoadProperty(op) INSTRUCTION(op, LoadProperty, 1, name) +#define INSTR_GetLookup(op) INSTRUCTION(op, GetLookup, 1, index) #define INSTR_LoadIdObject(op) INSTRUCTION(op, LoadIdObject, 2, index, base) #define INSTR_Yield(op) INSTRUCTION(op, Yield, 0) #define INSTR_YieldStar(op) INSTRUCTION(op, YieldStar, 0) @@ -100,18 +100,18 @@ QT_BEGIN_NAMESPACE #define INSTR_SetLookup(op) INSTRUCTION(op, SetLookup, 2, index, base) #define INSTR_LoadSuperProperty(op) INSTRUCTION(op, LoadSuperProperty, 1, property) #define INSTR_StoreSuperProperty(op) INSTRUCTION(op, StoreSuperProperty, 1, property) -#define INSTR_LoadElement(op) INSTRUCTION(op, LoadElement, 2, base, traceSlot) -#define INSTR_StoreElement(op) INSTRUCTION(op, StoreElement, 3, base, index, traceSlot) -#define INSTR_CallValue(op) INSTRUCTION(op, CallValue, 4, name, argc, argv, traceSlot) -#define INSTR_CallWithReceiver(op) INSTRUCTION(op, CallWithReceiver, 5, name, thisObject, argc, argv, traceSlot) -#define INSTR_CallProperty(op) INSTRUCTION(op, CallProperty, 5, name, base, argc, argv, traceSlot) -#define INSTR_CallPropertyLookup(op) INSTRUCTION(op, CallPropertyLookup, 5, lookupIndex, base, argc, argv, traceSlot) -#define INSTR_CallElement(op) INSTRUCTION(op, CallElement, 5, base, index, argc, argv, traceSlot) -#define INSTR_CallName(op) INSTRUCTION(op, CallName, 4, name, argc, argv, traceSlot) -#define INSTR_CallPossiblyDirectEval(op) INSTRUCTION(op, CallPossiblyDirectEval, 3, argc, argv, traceSlot) -#define INSTR_CallGlobalLookup(op) INSTRUCTION(op, CallGlobalLookup, 4, index, argc, argv, traceSlot) -#define INSTR_CallQmlContextPropertyLookup(op) INSTRUCTION(op, CallQmlContextPropertyLookup, 4, index, argc, argv, traceSlot) -#define INSTR_CallWithSpread(op) INSTRUCTION(op, CallWithSpread, 5, func, thisObject, argc, argv, traceSlot) +#define INSTR_LoadElement(op) INSTRUCTION(op, LoadElement, 1, base) +#define INSTR_StoreElement(op) INSTRUCTION(op, StoreElement, 2, base, index) +#define INSTR_CallValue(op) INSTRUCTION(op, CallValue, 3, name, argc, argv) +#define INSTR_CallWithReceiver(op) INSTRUCTION(op, CallWithReceiver, 4, name, thisObject, argc, argv) +#define INSTR_CallProperty(op) INSTRUCTION(op, CallProperty, 4, name, base, argc, argv) +#define INSTR_CallPropertyLookup(op) INSTRUCTION(op, CallPropertyLookup, 4, lookupIndex, base, argc, argv) +#define INSTR_CallElement(op) INSTRUCTION(op, CallElement, 4, base, index, argc, argv) +#define INSTR_CallName(op) INSTRUCTION(op, CallName, 3, name, argc, argv) +#define INSTR_CallPossiblyDirectEval(op) INSTRUCTION(op, CallPossiblyDirectEval, 2, argc, argv) +#define INSTR_CallGlobalLookup(op) INSTRUCTION(op, CallGlobalLookup, 3, index, argc, argv) +#define INSTR_CallQmlContextPropertyLookup(op) INSTRUCTION(op, CallQmlContextPropertyLookup, 3, index, argc, argv) +#define INSTR_CallWithSpread(op) INSTRUCTION(op, CallWithSpread, 4, func, thisObject, argc, argv) #define INSTR_Construct(op) INSTRUCTION(op, Construct, 3, func, argc, argv) #define INSTR_ConstructWithSpread(op) INSTRUCTION(op, ConstructWithSpread, 3, func, argc, argv) #define INSTR_SetUnwindHandler(op) INSTRUCTION(op, SetUnwindHandler, 1, offset) @@ -148,8 +148,8 @@ QT_BEGIN_NAMESPACE #define INSTR_LoadSuperConstructor(op) INSTRUCTION(op, LoadSuperConstructor, 0) #define INSTR_ToObject(op) INSTRUCTION(op, ToObject, 0) #define INSTR_Jump(op) INSTRUCTION(op, Jump, 1, offset) -#define INSTR_JumpTrue(op) INSTRUCTION(op, JumpTrue, 2, traceSlot, offset) -#define INSTR_JumpFalse(op) INSTRUCTION(op, JumpFalse, 2, traceSlot, offset) +#define INSTR_JumpTrue(op) INSTRUCTION(op, JumpTrue, 1, offset) +#define INSTR_JumpFalse(op) INSTRUCTION(op, JumpFalse, 1, offset) #define INSTR_JumpNotUndefined(op) INSTRUCTION(op, JumpNotUndefined, 1, offset) #define INSTR_JumpNoException(op) INSTRUCTION(op, JumpNoException, 1, offset) #define INSTR_CmpEqNull(op) INSTRUCTION(op, CmpEqNull, 0) @@ -167,12 +167,12 @@ QT_BEGIN_NAMESPACE #define INSTR_CmpIn(op) INSTRUCTION(op, CmpIn, 1, lhs) #define INSTR_CmpInstanceOf(op) INSTRUCTION(op, CmpInstanceOf, 1, lhs) #define INSTR_UNot(op) INSTRUCTION(op, UNot, 0) -#define INSTR_UPlus(op) INSTRUCTION(op, UPlus, 1, traceSlot) -#define INSTR_UMinus(op) INSTRUCTION(op, UMinus, 1, traceSlot) +#define INSTR_UPlus(op) INSTRUCTION(op, UPlus, 0) +#define INSTR_UMinus(op) INSTRUCTION(op, UMinus, 0) #define INSTR_UCompl(op) INSTRUCTION(op, UCompl, 0) -#define INSTR_Increment(op) INSTRUCTION(op, Increment, 1, traceSlot) -#define INSTR_Decrement(op) INSTRUCTION(op, Decrement, 1, traceSlot) -#define INSTR_Add(op) INSTRUCTION(op, Add, 2, lhs, traceSlot) +#define INSTR_Increment(op) INSTRUCTION(op, Increment, 0) +#define INSTR_Decrement(op) INSTRUCTION(op, Decrement, 0) +#define INSTR_Add(op) INSTRUCTION(op, Add, 1, lhs) #define INSTR_BitAnd(op) INSTRUCTION(op, BitAnd, 1, lhs) #define INSTR_BitOr(op) INSTRUCTION(op, BitOr, 1, lhs) #define INSTR_BitXor(op) INSTRUCTION(op, BitXor, 1, lhs) @@ -186,10 +186,10 @@ QT_BEGIN_NAMESPACE #define INSTR_ShrConst(op) INSTRUCTION(op, ShrConst, 1, rhs) #define INSTR_ShlConst(op) INSTRUCTION(op, ShlConst, 1, rhs) #define INSTR_Exp(op) INSTRUCTION(op, Exp, 1, lhs) -#define INSTR_Mul(op) INSTRUCTION(op, Mul, 2, lhs, traceSlot) +#define INSTR_Mul(op) INSTRUCTION(op, Mul, 1, lhs) #define INSTR_Div(op) INSTRUCTION(op, Div, 1, lhs) -#define INSTR_Mod(op) INSTRUCTION(op, Mod, 2, lhs, traceSlot) -#define INSTR_Sub(op) INSTRUCTION(op, Sub, 2, lhs, traceSlot) +#define INSTR_Mod(op) INSTRUCTION(op, Mod, 1, lhs) +#define INSTR_Sub(op) INSTRUCTION(op, Sub, 1, lhs) #define INSTR_LoadQmlImportedScripts(op) INSTRUCTION(op, LoadQmlImportedScripts, 1, result) #define INSTR_InitializeBlockDeadTemporalZone(op) INSTRUCTION(op, InitializeBlockDeadTemporalZone, 2, firstReg, count) #define INSTR_ThrowOnNullOrUndefined(op) INSTRUCTION(op, ThrowOnNullOrUndefined, 0) diff --git a/src/qml/configure.json b/src/qml/configure.json index ef6a9f5e7e..0f7de29594 100644 --- a/src/qml/configure.json +++ b/src/qml/configure.json @@ -8,7 +8,6 @@ "commandline": { "options": { "qml-network": "boolean", - "qml-tracing": "boolean", "qml-debug": "boolean" } }, @@ -107,14 +106,6 @@ Finally, ios and tvos can technically use the JIT but Apple does not allow it. Therefore, it's disabled by default." }, - "qml-tracing": { - "label": "QML tracing JIT support", - "purpose": "Provides a JIT that uses trace information generated by the interpreter.", - "section": "QML", - "condition": "features.qml-jit", - "output": [ "privateFeature" ], - "autoDetect": false - }, "qml-debug": { "label": "QML debugging and profiling support", "purpose": "Provides infrastructure and plugins for debugging and profiling.", @@ -209,7 +200,6 @@ "qml-network", "qml-debug", "qml-jit", - "qml-tracing", "qml-sequence-object", "qml-list-model", "qml-xml-http-request", diff --git a/src/qml/jit/jit.pri b/src/qml/jit/jit.pri index bc0d20dba7..503ce0ebcd 100644 --- a/src/qml/jit/jit.pri +++ b/src/qml/jit/jit.pri @@ -10,34 +10,3 @@ HEADERS += \ $$PWD/qv4baselinejit_p.h \ $$PWD/qv4baselineassembler_p.h \ $$PWD/qv4assemblercommon_p.h - -qtConfig(qml-tracing) { -SOURCES += \ - $$PWD/qv4ir.cpp \ - $$PWD/qv4operation.cpp \ - $$PWD/qv4node.cpp \ - $$PWD/qv4graph.cpp \ - $$PWD/qv4graphbuilder.cpp \ - $$PWD/qv4lowering.cpp \ - $$PWD/qv4tracingjit.cpp \ - $$PWD/qv4mi.cpp \ - $$PWD/qv4domtree.cpp \ - $$PWD/qv4schedulers.cpp \ - $$PWD/qv4blockscheduler.cpp \ - $$PWD/qv4loopinfo.cpp - -HEADERS += \ - $$PWD/qv4ir_p.h \ - $$PWD/qv4operation_p.h \ - $$PWD/qv4runtimesupport_p.h \ - $$PWD/qv4node_p.h \ - $$PWD/qv4graph_p.h \ - $$PWD/qv4graphbuilder_p.h \ - $$PWD/qv4lowering_p.h \ - $$PWD/qv4mi_p.h \ - $$PWD/qv4miblockset_p.h \ - $$PWD/qv4domtree_p.h \ - $$PWD/qv4schedulers_p.h \ - $$PWD/qv4blockscheduler_p.h \ - $$PWD/qv4loopinfo_p.h -} diff --git a/src/qml/jit/qv4baselinejit.cpp b/src/qml/jit/qv4baselinejit.cpp index 60880419a6..517f0940e5 100644 --- a/src/qml/jit/qv4baselinejit.cpp +++ b/src/qml/jit/qv4baselinejit.cpp @@ -149,7 +149,7 @@ void BaselineJIT::generate_LoadImport(int index) as->loadImport(index); } -void BaselineJIT::generate_LoadLocal(int index, int /*traceSlot*/) +void BaselineJIT::generate_LoadLocal(int index) { as->loadLocal(index); } @@ -160,7 +160,7 @@ void BaselineJIT::generate_StoreLocal(int index) as->storeLocal(index); } -void BaselineJIT::generate_LoadScopedLocal(int scope, int index, int /*traceSlot*/) +void BaselineJIT::generate_LoadScopedLocal(int scope, int index) { as->loadLocal(index, scope); } @@ -193,7 +193,7 @@ void BaselineJIT::generate_LoadClosure(int value) BASELINEJIT_GENERATE_RUNTIME_CALL(Closure, CallResultDestination::InAccumulator); } -void BaselineJIT::generate_LoadName(int name, int /*traceSlot*/) +void BaselineJIT::generate_LoadName(int name) { STORE_IP(); as->prepareCallWithArgCount(2); @@ -202,7 +202,7 @@ void BaselineJIT::generate_LoadName(int name, int /*traceSlot*/) BASELINEJIT_GENERATE_RUNTIME_CALL(LoadName, CallResultDestination::InAccumulator); } -void BaselineJIT::generate_LoadGlobalLookup(int index, int /*traceSlot*/) +void BaselineJIT::generate_LoadGlobalLookup(int index) { as->prepareCallWithArgCount(3); as->passInt32AsArg(index, 2); @@ -211,7 +211,7 @@ void BaselineJIT::generate_LoadGlobalLookup(int index, int /*traceSlot*/) BASELINEJIT_GENERATE_RUNTIME_CALL(LoadGlobalLookup, CallResultDestination::InAccumulator); } -void BaselineJIT::generate_LoadQmlContextPropertyLookup(int index, int /*traceSlot*/) +void BaselineJIT::generate_LoadQmlContextPropertyLookup(int index) { as->prepareCallWithArgCount(2); as->passInt32AsArg(index, 1); @@ -241,7 +241,7 @@ void BaselineJIT::generate_StoreNameStrict(int name) BASELINEJIT_GENERATE_RUNTIME_CALL(StoreNameStrict, CallResultDestination::Ignore); } -void BaselineJIT::generate_LoadElement(int base, int /*traceSlot*/) +void BaselineJIT::generate_LoadElement(int base) { STORE_IP(); STORE_ACC(); @@ -252,7 +252,7 @@ void BaselineJIT::generate_LoadElement(int base, int /*traceSlot*/) BASELINEJIT_GENERATE_RUNTIME_CALL(LoadElement, CallResultDestination::InAccumulator); } -void BaselineJIT::generate_StoreElement(int base, int index, int /*traceSlot*/) +void BaselineJIT::generate_StoreElement(int base, int index) { STORE_IP(); STORE_ACC(); @@ -264,7 +264,7 @@ void BaselineJIT::generate_StoreElement(int base, int index, int /*traceSlot*/) BASELINEJIT_GENERATE_RUNTIME_CALL(StoreElement, CallResultDestination::Ignore); } -void BaselineJIT::generate_LoadProperty(int name, int /*traceSlot*/) +void BaselineJIT::generate_LoadProperty(int name) { STORE_IP(); STORE_ACC(); @@ -275,7 +275,7 @@ void BaselineJIT::generate_LoadProperty(int name, int /*traceSlot*/) BASELINEJIT_GENERATE_RUNTIME_CALL(LoadProperty, CallResultDestination::InAccumulator); } -void BaselineJIT::generate_GetLookup(int index, int /*traceSlot*/) +void BaselineJIT::generate_GetLookup(int index) { STORE_IP(); STORE_ACC(); @@ -353,7 +353,7 @@ void BaselineJIT::generate_Resume(int) Q_UNREACHABLE(); } -void BaselineJIT::generate_CallValue(int name, int argc, int argv, int /*traceSlot*/) +void BaselineJIT::generate_CallValue(int name, int argc, int argv) { STORE_IP(); as->prepareCallWithArgCount(4); @@ -364,7 +364,7 @@ void BaselineJIT::generate_CallValue(int name, int argc, int argv, int /*traceSl BASELINEJIT_GENERATE_RUNTIME_CALL(CallValue, CallResultDestination::InAccumulator); } -void BaselineJIT::generate_CallWithReceiver(int name, int thisObject, int argc, int argv, int /*traceSlot*/) +void BaselineJIT::generate_CallWithReceiver(int name, int thisObject, int argc, int argv) { STORE_IP(); as->prepareCallWithArgCount(5); @@ -376,7 +376,7 @@ void BaselineJIT::generate_CallWithReceiver(int name, int thisObject, int argc, BASELINEJIT_GENERATE_RUNTIME_CALL(CallWithReceiver, CallResultDestination::InAccumulator); } -void BaselineJIT::generate_CallProperty(int name, int base, int argc, int argv, int /*traceSlot*/) +void BaselineJIT::generate_CallProperty(int name, int base, int argc, int argv) { STORE_IP(); as->prepareCallWithArgCount(5); @@ -388,7 +388,7 @@ void BaselineJIT::generate_CallProperty(int name, int base, int argc, int argv, BASELINEJIT_GENERATE_RUNTIME_CALL(CallProperty, CallResultDestination::InAccumulator); } -void BaselineJIT::generate_CallPropertyLookup(int lookupIndex, int base, int argc, int argv, int /*traceSlot*/) +void BaselineJIT::generate_CallPropertyLookup(int lookupIndex, int base, int argc, int argv) { STORE_IP(); as->prepareCallWithArgCount(5); @@ -400,7 +400,7 @@ void BaselineJIT::generate_CallPropertyLookup(int lookupIndex, int base, int arg BASELINEJIT_GENERATE_RUNTIME_CALL(CallPropertyLookup, CallResultDestination::InAccumulator); } -void BaselineJIT::generate_CallElement(int base, int index, int argc, int argv, int /*traceSlot*/) +void BaselineJIT::generate_CallElement(int base, int index, int argc, int argv) { STORE_IP(); as->prepareCallWithArgCount(5); @@ -412,7 +412,7 @@ void BaselineJIT::generate_CallElement(int base, int index, int argc, int argv, BASELINEJIT_GENERATE_RUNTIME_CALL(CallElement, CallResultDestination::InAccumulator); } -void BaselineJIT::generate_CallName(int name, int argc, int argv, int /*traceSlot*/) +void BaselineJIT::generate_CallName(int name, int argc, int argv) { STORE_IP(); as->prepareCallWithArgCount(4); @@ -423,7 +423,7 @@ void BaselineJIT::generate_CallName(int name, int argc, int argv, int /*traceSlo BASELINEJIT_GENERATE_RUNTIME_CALL(CallName, CallResultDestination::InAccumulator); } -void BaselineJIT::generate_CallPossiblyDirectEval(int argc, int argv, int /*traceSlot*/) +void BaselineJIT::generate_CallPossiblyDirectEval(int argc, int argv) { STORE_IP(); as->prepareCallWithArgCount(3); @@ -433,7 +433,7 @@ void BaselineJIT::generate_CallPossiblyDirectEval(int argc, int argv, int /*trac BASELINEJIT_GENERATE_RUNTIME_CALL(CallPossiblyDirectEval, CallResultDestination::InAccumulator); } -void BaselineJIT::generate_CallGlobalLookup(int index, int argc, int argv, int /*traceSlot*/) +void BaselineJIT::generate_CallGlobalLookup(int index, int argc, int argv) { STORE_IP(); as->prepareCallWithArgCount(4); @@ -444,8 +444,7 @@ void BaselineJIT::generate_CallGlobalLookup(int index, int argc, int argv, int / BASELINEJIT_GENERATE_RUNTIME_CALL(CallGlobalLookup, CallResultDestination::InAccumulator); } -void BaselineJIT::generate_CallQmlContextPropertyLookup(int index, int argc, int argv, - int /*traceSlot*/) +void BaselineJIT::generate_CallQmlContextPropertyLookup(int index, int argc, int argv) { STORE_IP(); as->prepareCallWithArgCount(4); @@ -456,7 +455,7 @@ void BaselineJIT::generate_CallQmlContextPropertyLookup(int index, int argc, int BASELINEJIT_GENERATE_RUNTIME_CALL(CallQmlContextPropertyLookup, CallResultDestination::InAccumulator); } -void BaselineJIT::generate_CallWithSpread(int func, int thisObject, int argc, int argv, int /*traceSlot*/) +void BaselineJIT::generate_CallWithSpread(int func, int thisObject, int argc, int argv) { STORE_IP(); as->prepareCallWithArgCount(5); @@ -775,12 +774,12 @@ void BaselineJIT::generate_Jump(int offset) labels.insert(as->jump(absoluteOffset(offset))); } -void BaselineJIT::generate_JumpTrue(int /*traceSlot*/, int offset) +void BaselineJIT::generate_JumpTrue(int offset) { labels.insert(as->jumpTrue(absoluteOffset(offset))); } -void BaselineJIT::generate_JumpFalse(int /*traceSlot*/, int offset) +void BaselineJIT::generate_JumpFalse(int offset) { labels.insert(as->jumpFalse(absoluteOffset(offset))); } @@ -829,12 +828,12 @@ void BaselineJIT::generate_CmpInstanceOf(int lhs) } void BaselineJIT::generate_UNot() { as->unot(); } -void BaselineJIT::generate_UPlus(int /*traceSlot*/) { as->toNumber(); } -void BaselineJIT::generate_UMinus(int /*traceSlot*/) { as->uminus(); } +void BaselineJIT::generate_UPlus() { as->toNumber(); } +void BaselineJIT::generate_UMinus() { as->uminus(); } void BaselineJIT::generate_UCompl() { as->ucompl(); } -void BaselineJIT::generate_Increment(int /*traceSlot*/) { as->inc(); } -void BaselineJIT::generate_Decrement(int /*traceSlot*/) { as->dec(); } -void BaselineJIT::generate_Add(int lhs, int /*traceSlot*/) { as->add(lhs); } +void BaselineJIT::generate_Increment() { as->inc(); } +void BaselineJIT::generate_Decrement() { as->dec(); } +void BaselineJIT::generate_Add(int lhs) { as->add(lhs); } void BaselineJIT::generate_BitAnd(int lhs) { as->bitAnd(lhs); } void BaselineJIT::generate_BitOr(int lhs) { as->bitOr(lhs); } @@ -858,10 +857,10 @@ void BaselineJIT::generate_Exp(int lhs) { as->passJSSlotAsArg(lhs, 0); BASELINEJIT_GENERATE_RUNTIME_CALL(Exp, CallResultDestination::InAccumulator); } -void BaselineJIT::generate_Mul(int lhs, int /*traceSlot*/) { as->mul(lhs); } +void BaselineJIT::generate_Mul(int lhs) { as->mul(lhs); } void BaselineJIT::generate_Div(int lhs) { as->div(lhs); } -void BaselineJIT::generate_Mod(int lhs, int /*traceSlot*/) { as->mod(lhs); } -void BaselineJIT::generate_Sub(int lhs, int /*traceSlot*/) { as->sub(lhs); } +void BaselineJIT::generate_Mod(int lhs) { as->mod(lhs); } +void BaselineJIT::generate_Sub(int lhs) { as->sub(lhs); } //void BaselineJIT::generate_BinopContext(int alu, int lhs) //{ diff --git a/src/qml/jit/qv4baselinejit_p.h b/src/qml/jit/qv4baselinejit_p.h index 941e0cd408..46622d29e6 100644 --- a/src/qml/jit/qv4baselinejit_p.h +++ b/src/qml/jit/qv4baselinejit_p.h @@ -87,22 +87,22 @@ public: void generate_StoreReg(int reg) override; void generate_MoveReg(int srcReg, int destReg) override; void generate_LoadImport(int index) override; - void generate_LoadLocal(int index, int traceSlot) override; + void generate_LoadLocal(int index) override; void generate_StoreLocal(int index) override; - void generate_LoadScopedLocal(int scope, int index, int traceSlot) override; + void generate_LoadScopedLocal(int scope, int index) override; void generate_StoreScopedLocal(int scope, int index) override; void generate_LoadRuntimeString(int stringId) override; void generate_MoveRegExp(int regExpId, int destReg) override; void generate_LoadClosure(int value) override; - void generate_LoadName(int name, int traceSlot) override; - void generate_LoadGlobalLookup(int index, int traceSlot) override; - void generate_LoadQmlContextPropertyLookup(int index, int traceSlot) override; + void generate_LoadName(int name) override; + void generate_LoadGlobalLookup(int index) override; + void generate_LoadQmlContextPropertyLookup(int index) override; void generate_StoreNameSloppy(int name) override; void generate_StoreNameStrict(int name) override; - void generate_LoadElement(int base, int traceSlot) override; - void generate_StoreElement(int base, int index, int traceSlot) override; - void generate_LoadProperty(int name, int traceSlot) override; - void generate_GetLookup(int index, int traceSlot) override; + void generate_LoadElement(int base) override; + void generate_StoreElement(int base, int index) override; + void generate_LoadProperty(int name) override; + void generate_GetLookup(int index) override; void generate_StoreProperty(int name, int base) override; void generate_SetLookup(int index, int base) override; void generate_LoadSuperProperty(int property) override; @@ -111,16 +111,16 @@ public: void generate_YieldStar() override; void generate_Resume(int) override; - void generate_CallValue(int name, int argc, int argv, int traceSlot) override; - void generate_CallWithReceiver(int name, int thisObject, int argc, int argv, int traceSlot) override; - void generate_CallProperty(int name, int base, int argc, int argv, int traceSlot) override; - void generate_CallPropertyLookup(int lookupIndex, int base, int argc, int argv, int traceSlot) override; - void generate_CallElement(int base, int index, int argc, int argv, int traceSlot) override; - void generate_CallName(int name, int argc, int argv, int traceSlot) override; - void generate_CallPossiblyDirectEval(int argc, int argv, int traceSlot) override; - void generate_CallGlobalLookup(int index, int argc, int argv, int traceSlot) override; - void generate_CallQmlContextPropertyLookup(int index, int argc, int argv, int traceSlot) override; - void generate_CallWithSpread(int func, int thisObject, int argc, int argv, int traceSlot) override; + void generate_CallValue(int name, int argc, int argv) override; + void generate_CallWithReceiver(int name, int thisObject, int argc, int argv) override; + void generate_CallProperty(int name, int base, int argc, int argv) override; + void generate_CallPropertyLookup(int lookupIndex, int base, int argc, int argv) override; + void generate_CallElement(int base, int index, int argc, int argv) override; + void generate_CallName(int name, int argc, int argv) override; + void generate_CallPossiblyDirectEval(int argc, int argv) override; + void generate_CallGlobalLookup(int index, int argc, int argv) override; + void generate_CallQmlContextPropertyLookup(int index, int argc, int argv) override; + void generate_CallWithSpread(int func, int thisObject, int argc, int argv) override; void generate_TailCall(int func, int thisObject, int argc, int argv) override; void generate_Construct(int func, int argc, int argv) override; void generate_ConstructWithSpread(int func, int argc, int argv) override; @@ -159,8 +159,8 @@ public: void generate_LoadSuperConstructor() override; void generate_ToObject() override; void generate_Jump(int offset) override; - void generate_JumpTrue(int traceSlot, int offset) override; - void generate_JumpFalse(int traceSlot, int offset) override; + void generate_JumpTrue(int offset) override; + void generate_JumpFalse(int offset) override; void generate_JumpNoException(int offset) override; void generate_JumpNotUndefined(int offset) override; void generate_CmpEqNull() override; @@ -178,12 +178,12 @@ public: void generate_CmpIn(int lhs) override; void generate_CmpInstanceOf(int lhs) override; void generate_UNot() override; - void generate_UPlus(int) override; - void generate_UMinus(int traceSlot) override; + void generate_UPlus() override; + void generate_UMinus() override; void generate_UCompl() override; - void generate_Increment(int traceSlot) override; - void generate_Decrement(int traceSlot) override; - void generate_Add(int lhs, int traceSlot) override; + void generate_Increment() override; + void generate_Decrement() override; + void generate_Add(int lhs) override; void generate_BitAnd(int lhs) override; void generate_BitOr(int lhs) override; void generate_BitXor(int lhs) override; @@ -197,10 +197,10 @@ public: void generate_ShrConst(int rhs) override; void generate_ShlConst(int rhs) override; void generate_Exp(int lhs) override; - void generate_Mul(int lhs, int traceSlot) override; + void generate_Mul(int lhs) override; void generate_Div(int lhs) override; - void generate_Mod(int lhs, int traceSlot) override; - void generate_Sub(int lhs, int traceSlot) override; + void generate_Mod(int lhs) override; + void generate_Sub(int lhs) override; void generate_InitializeBlockDeadTemporalZone(int firstReg, int count) override; void generate_ThrowOnNullOrUndefined() override; void generate_GetTemplateObject(int index) override; diff --git a/src/qml/jit/qv4blockscheduler.cpp b/src/qml/jit/qv4blockscheduler.cpp deleted file mode 100644 index 3e2bfe15c5..0000000000 --- a/src/qml/jit/qv4blockscheduler.cpp +++ /dev/null @@ -1,208 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <QtCore/qloggingcategory.h> - -#include "qv4blockscheduler_p.h" -#include "qv4domtree_p.h" -#include "qv4loopinfo_p.h" - -QT_BEGIN_NAMESPACE -namespace QV4 { -namespace IR { - -Q_LOGGING_CATEGORY(lcBlockScheduler, "qt.v4.ir.blockscheduler") - -bool BlockScheduler::checkCandidate(MIBlock *candidate) -{ - Q_ASSERT(loopInfo.loopHeaderFor(candidate) == currentGroup.group); - - for (MIBlock *pred : candidate->inEdges()) { - if (pred->isDeoptBlock()) - continue; - - if (emitted.alreadyProcessed(pred)) - continue; - - if (dominatorTree.dominates(candidate->index(), pred->index())) { - // this is a loop, where there in - // -> candidate edge is the jump back to the top of the loop. - continue; - } - - if (pred == candidate) - // this is a very tight loop, e.g.: - // L1: ... - // goto L1 - // This can happen when, for example, the basic-block merging gets rid of the empty - // body block. In this case, we can safely schedule this block (if all other - // incoming edges are either loop-back edges, or have been scheduled already). - continue; - - return false; // an incoming edge that is not yet emitted, and is not a back-edge - } - - if (loopInfo.isLoopHeader(candidate)) { - // postpone everything, and schedule the loop first. - postponedGroups.push(currentGroup); - currentGroup = WorkForGroup(candidate); - } - - return true; -} - -MIBlock *BlockScheduler::pickNext() -{ - while (true) { - while (currentGroup.postponed.isEmpty()) { - if (postponedGroups.isEmpty()) - return nullptr; - if (currentGroup.group) // record the first and the last node of a group - loopsStartEnd[currentGroup.group] = sequence.back(); - currentGroup = postponedGroups.pop(); - } - - MIBlock *next = currentGroup.postponed.pop(); - if (checkCandidate(next)) - return next; - } - - Q_UNREACHABLE(); - return nullptr; -} - -void BlockScheduler::emitBlock(MIBlock *bb) -{ - if (emitted.alreadyProcessed(bb)) - return; - - sequence.push_back(bb); - emitted.markAsProcessed(bb); -} - -void BlockScheduler::schedule(MIBlock *functionEntryPoint) -{ - MIBlock *next = functionEntryPoint; - - while (next) { - emitBlock(next); - // postpone all outgoing edges, if they were not already processed - QVarLengthArray<MIBlock *, 32> nonExceptionEdges; - // first postpone all exception edges, so they will be processed last - for (int i = next->outEdges().size(); i != 0; ) { - --i; - MIBlock *out = next->outEdges().at(i); - if (emitted.alreadyProcessed(out)) - continue; - if (out == nullptr) - continue; - if (out->instructions().front().opcode() == Meta::OnException) - postpone(out); - else - nonExceptionEdges.append(out); - } - for (MIBlock *edge : nonExceptionEdges) - postpone(edge); - next = pickNext(); - } - - // finally schedule all de-optimization blocks at the end - for (auto bb : dominatorTree.function()->blocks()) { - if (bb->isDeoptBlock()) - emitBlock(bb); - } -} - -void BlockScheduler::postpone(MIBlock *bb) -{ - if (currentGroup.group == loopInfo.loopHeaderFor(bb)) { - currentGroup.postponed.append(bb); - return; - } - - for (int i = postponedGroups.size(); i != 0; ) { - --i; - WorkForGroup &g = postponedGroups[i]; - if (g.group == loopInfo.loopHeaderFor(bb)) { - g.postponed.append(bb); - return; - } - } - - Q_UNREACHABLE(); -} - -void BlockScheduler::dump() const -{ - if (!lcBlockScheduler().isDebugEnabled()) - return; - - QString s = QStringLiteral("Scheduled blocks:\n"); - for (auto *bb : sequence) { - s += QLatin1String(" L") + QString::number(bb->index()); - MIBlock *loopEnd = loopsStartEnd[bb]; - if (loopEnd) - s += QLatin1String(", loop start, ends at L") + QString::number(loopEnd->index()); - s += QLatin1Char('\n'); - } - qCDebug(lcBlockScheduler).noquote().nospace() << s; -} - -BlockScheduler::BlockScheduler(const DominatorTree &dominatorTree, const LoopInfo &loopInfo) - : dominatorTree(dominatorTree) - , loopInfo(loopInfo) - , sequence(0) - , emitted(dominatorTree.function()) -{ - schedule(dominatorTree.function()->blocks().front()); - - dump(); - - if (dominatorTree.function()->blockCount() != sequence.size()) { - qFatal("The block scheduler did not schedule all blocks. This is most likely due to" - "a non-natural loop."); - // Usually caused by having an execution path that manages to skip over unwind handler - // reset: any exception happening after will jump back to the unwind handler, and thereby - // creating a loop that can be entered in 2 different ways. - } -} - -} // IR namespace -} // QV4 namespace -QT_END_NAMESPACE diff --git a/src/qml/jit/qv4blockscheduler_p.h b/src/qml/jit/qv4blockscheduler_p.h deleted file mode 100644 index 1289fda9f0..0000000000 --- a/src/qml/jit/qv4blockscheduler_p.h +++ /dev/null @@ -1,155 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QV4BLOCKSCHEDULER_P_H -#define QV4BLOCKSCHEDULER_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QtCore/qstack.h> - -#include "qv4mi_p.h" -#include "qv4util_p.h" - -QT_REQUIRE_CONFIG(qml_tracing); - -QT_BEGIN_NAMESPACE - -namespace QV4 { -namespace IR { - -class DominatorTree; -class LoopInfo; - -// High-level algorithm: -// 0. start with the first node (the start node) of a function -// 1. emit the node -// 2. add all outgoing edges that are not yet emitted to the postponed stack -// 3. When the postponed stack is empty, pop a stack from the loop stack. If that is empty too, -// we're done. -// 4. pop a node from the postponed stack, and check if it can be scheduled: -// a. if all incoming edges are scheduled, go to 4. -// b. if an incoming edge is unscheduled, but it's a back-edge (an edge in a loop that jumps -// back to the start of the loop), ignore it -// c. if there is any unscheduled edge that is not a back-edge, ignore this node, and go to 4. -// 5. if this node is the start of a loop, push the postponed stack on the loop stack. -// 6. go back to 1. -// -// The postponing action in step 2 will put the node into its containing group. The case where this -// is important is when a (labeled) continue or a (labeled) break statement occur in a loop: the -// outgoing edge points to a node that is not part of the current loop (and possibly not of the -// parent loop). -// -// Linear scan register allocation benefits greatly from short life-time intervals with few holes -// (see for example section 4 (Lifetime Analysis) of [Wimmer1]). This algorithm makes sure that the -// blocks of a group are scheduled together, with no non-loop blocks in between. This applies -// recursively for nested loops. It also schedules groups of if-then-else-endif blocks together for -// the same reason. -class BlockScheduler -{ - const DominatorTree &dominatorTree; - const LoopInfo &loopInfo; - - struct WorkForGroup - { - MIBlock *group; - QStack<MIBlock *> postponed; - - WorkForGroup(MIBlock *group = nullptr) : group(group) {} - }; - WorkForGroup currentGroup; - QStack<WorkForGroup> postponedGroups; - std::vector<MIBlock *> sequence; - - class ProcessedBlocks - { - BitVector processed; - - public: - ProcessedBlocks(MIFunction *function) - : processed(int(function->blockCount()), false) - {} - - bool alreadyProcessed(MIBlock *bb) const - { - Q_ASSERT(bb); - - return processed.at(bb->index()); - } - - void markAsProcessed(MIBlock *bb) - { - processed.setBit(bb->index()); - } - } emitted; - QHash<MIBlock *, MIBlock *> loopsStartEnd; - - bool checkCandidate(MIBlock *candidate); - MIBlock *pickNext(); - void emitBlock(MIBlock *bb); - void schedule(MIBlock *functionEntryPoint); - void postpone(MIBlock *bb); - void dump() const; - -public: - BlockScheduler(const DominatorTree &dominatorTree, const LoopInfo &loopInfo); - - const std::vector<MIBlock *> &scheduledBlockSequence() const - { return sequence; } - - QHash<MIBlock *, MIBlock *> loopEndsByStartBlock() const - { return loopsStartEnd; } -}; - - -} // namespace IR -} // namespace QV4 - -QT_END_NAMESPACE - -#endif // QV4BLOCKSCHEDULER_P_H diff --git a/src/qml/jit/qv4domtree.cpp b/src/qml/jit/qv4domtree.cpp deleted file mode 100644 index 9484f4e2dc..0000000000 --- a/src/qml/jit/qv4domtree.cpp +++ /dev/null @@ -1,436 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <QtCore/qloggingcategory.h> -#include <QtCore/qbuffer.h> - -#include "qv4domtree_p.h" - -QT_BEGIN_NAMESPACE -namespace QV4 { -namespace IR { - -Q_LOGGING_CATEGORY(lcDomTree, "qt.v4.ir.domTree") -Q_LOGGING_CATEGORY(lcDomFrontier, "qt.v4.ir.domFrontier") - -DominatorTree::DominatorTree(MIFunction *f) - : m_function(f) - , m_data(new Data) -{ - calculateIDoms(); - m_data.reset(); -} - -void DominatorTree::dumpImmediateDominators() const -{ - QBuffer buf; - buf.open(QIODevice::WriteOnly); - QTextStream qout(&buf); - qout << "Immediate dominators for " << m_function->irFunction()->name() << ":" << endl; - for (MIBlock *to : m_function->blocks()) { - MIBlock::Index from = m_idom.at(to->index()); - if (from != MIBlock::InvalidIndex) - qout << " " << from; - else - qout << " (none)"; - qout << " dominates " << to->index() << endl; - } - qCDebug(lcDomTree, "%s", buf.data().constData()); -} - -void DominatorTree::setImmediateDominator(MIBlock::Index dominated, MIBlock::Index dominator) -{ - if (m_idom.size() <= size_t(dominated)) - m_idom.resize(dominated + 1); - m_idom[dominated] = dominator; -} - -bool DominatorTree::dominates(MIBlock::Index dominator, MIBlock::Index dominated) const -{ - // dominator can be Invalid when the dominated block has no dominator (i.e. the start node) - Q_ASSERT(dominated != MIBlock::InvalidIndex); - - if (dominator == dominated) - return false; - - for (MIBlock::Index it = m_idom[dominated]; it != MIBlock::InvalidIndex; it = m_idom[it]) { - if (it == dominator) - return true; - } - - return false; -} - -// Calculate a depth-first iteration order on the nodes of the dominator tree. -// -// The order of the nodes in the vector is not the same as one where a recursive depth-first -// iteration is done on a tree. Rather, the nodes are (reverse) sorted on tree depth. -// So for the: -// 1 dominates 2 -// 2 dominates 3 -// 3 dominates 4 -// 2 dominates 5 -// the order will be: -// 4, 3, 5, 2, 1 -// or: -// 4, 5, 3, 2, 1 -// So the order of nodes on the same depth is undefined, but it will be after the nodes -// they dominate, and before the nodes that dominate them. -// -// The reason for this order is that a proper DFS pre-/post-order would require inverting -// the idom vector by either building a real tree datastructure or by searching the idoms -// for siblings and children. Both have a higher time complexity than sorting by depth. -std::vector<MIBlock *> DominatorTree::calculateDFNodeIterOrder() const -{ - std::vector<int> depths = calculateNodeDepths(); - std::vector<MIBlock *> order = m_function->blocks(); - std::sort(order.begin(), order.end(), [&depths](MIBlock *one, MIBlock *two) -> bool { - return depths.at(one->index()) > depths.at(two->index()); - }); - return order; -} - -// Algorithm: -// - for each node: -// - get the depth of a node. If it's unknown (-1): -// - get the depth of the immediate dominator. -// - if that's unknown too, calculate it by calling calculateNodeDepth -// - set the current node's depth to that of immediate dominator + 1 -std::vector<int> DominatorTree::calculateNodeDepths() const -{ - std::vector<int> nodeDepths(size_t(m_function->blockCount()), -1); - for (MIBlock *bb : m_function->blocks()) { - int &bbDepth = nodeDepths[bb->index()]; - if (bbDepth == -1) { - const int immDom = m_idom[bb->index()]; - if (immDom == -1) { - // no immediate dominator, so it's either the start block, or an unreachable block - bbDepth = 0; - } else { - int immDomDepth = nodeDepths[immDom]; - if (immDomDepth == -1) - immDomDepth = calculateNodeDepth(immDom, nodeDepths); - bbDepth = immDomDepth + 1; - } - } - } - return nodeDepths; -} - -// Algorithm: -// - search for the first dominator of a node that has a known depth. As all nodes are -// reachable from the start node, and that node's depth is 0, this is finite. -// - while doing that search, put all unknown nodes in the worklist -// - pop all nodes from the worklist, and set their depth to the previous' (== dominating) -// node's depth + 1 -// This way every node's depth is calculated once, and the complexity is O(n). -int DominatorTree::calculateNodeDepth(MIBlock::Index nodeIdx, std::vector<int> &nodeDepths) const -{ - std::vector<int> worklist; - worklist.reserve(8); - int depth = -1; - - do { - worklist.push_back(nodeIdx); - nodeIdx = m_idom[nodeIdx]; - depth = nodeDepths[nodeIdx]; - } while (depth == -1); - - for (auto it = worklist.rbegin(), eit = worklist.rend(); it != eit; ++it) - nodeDepths[*it] = ++depth; - - return depth; -} - -namespace { -struct DFSTodo { - MIBlock::Index node = MIBlock::InvalidIndex; - MIBlock::Index parent = MIBlock::InvalidIndex; - - DFSTodo() = default; - DFSTodo(MIBlock::Index node, MIBlock::Index parent) - : node(node) - , parent(parent) - {} -}; -} // anonymous namespace - -void DominatorTree::dfs(MIBlock::Index node) -{ - std::vector<DFSTodo> worklist; - worklist.reserve(m_data->vertex.capacity() / 2); - DFSTodo todo(node, MIBlock::InvalidIndex); - - while (true) { - MIBlock::Index n = todo.node; - - if (m_data->dfnum[n] == 0) { - m_data->dfnum[n] = m_data->size; - m_data->vertex[m_data->size] = n; - m_data->parent[n] = todo.parent; - ++m_data->size; - - MIBlock::OutEdges out = m_function->block(n)->outEdges(); - for (int i = out.size() - 1; i > 0; --i) - worklist.emplace_back(out[i]->index(), n); - - if (!out.isEmpty()) { - todo.node = out.first()->index(); - todo.parent = n; - continue; - } - } - - if (worklist.empty()) - break; - - todo = worklist.back(); - worklist.pop_back(); - } -} - -void DominatorTree::link(MIBlock::Index p, MIBlock::Index n) -{ - m_data->ancestor[n] = p; - m_data->best[n] = n; -} - -void DominatorTree::calculateIDoms() -{ - Q_ASSERT(m_function->block(0)->inEdges().count() == 0); - - const size_t bbCount = m_function->blockCount(); - m_data->vertex = std::vector<MIBlock::Index>(bbCount, MIBlock::InvalidIndex); - m_data->parent = std::vector<MIBlock::Index>(bbCount, MIBlock::InvalidIndex); - m_data->dfnum = std::vector<unsigned>(bbCount, 0); - m_data->semi = std::vector<MIBlock::Index>(bbCount, MIBlock::InvalidIndex); - m_data->ancestor = std::vector<MIBlock::Index>(bbCount, MIBlock::InvalidIndex); - m_idom = std::vector<MIBlock::Index>(bbCount, MIBlock::InvalidIndex); - m_data->samedom = std::vector<MIBlock::Index>(bbCount, MIBlock::InvalidIndex); - m_data->best = std::vector<MIBlock::Index>(bbCount, MIBlock::InvalidIndex); - - QHash<MIBlock::Index, std::vector<MIBlock::Index>> bucket; - bucket.reserve(int(bbCount)); - - dfs(m_function->block(0)->index()); - - std::vector<MIBlock::Index> worklist; - worklist.reserve(m_data->vertex.capacity() / 2); - - for (int i = m_data->size - 1; i > 0; --i) { - MIBlock::Index n = m_data->vertex[i]; - MIBlock::Index p = m_data->parent[n]; - MIBlock::Index s = p; - - for (auto inEdge : m_function->block(n)->inEdges()) { - if (inEdge->isDeoptBlock()) - continue; - MIBlock::Index v = inEdge->index(); - MIBlock::Index ss = MIBlock::InvalidIndex; - if (m_data->dfnum[v] <= m_data->dfnum[n]) - ss = v; - else - ss = m_data->semi[ancestorWithLowestSemi(v, worklist)]; - if (m_data->dfnum[ss] < m_data->dfnum[s]) - s = ss; - } - m_data->semi[n] = s; - bucket[s].push_back(n); - link(p, n); - if (bucket.contains(p)) { - for (MIBlock::Index v : bucket[p]) { - MIBlock::Index y = ancestorWithLowestSemi(v, worklist); - MIBlock::Index semi_v = m_data->semi[v]; - if (m_data->semi[y] == semi_v) - m_idom[v] = semi_v; - else - m_data->samedom[v] = y; - } - bucket.remove(p); - } - } - - for (unsigned i = 1; i < m_data->size; ++i) { - MIBlock::Index n = m_data->vertex[i]; - Q_ASSERT(n != MIBlock::InvalidIndex); - Q_ASSERT(!bucket.contains(n)); - Q_ASSERT(m_data->ancestor[n] != MIBlock::InvalidIndex); - Q_ASSERT((m_data->semi[n] != MIBlock::InvalidIndex - && m_data->dfnum[m_data->ancestor[n]] <= m_data->dfnum[m_data->semi[n]]) - || m_data->semi[n] == n); - MIBlock::Index sdn = m_data->samedom[n]; - if (sdn != MIBlock::InvalidIndex) - m_idom[n] = m_idom[sdn]; - } - - if (lcDomTree().isDebugEnabled()) - dumpImmediateDominators(); - - m_data.reset(nullptr); -} - -MIBlock::Index DominatorTree::ancestorWithLowestSemi(MIBlock::Index v, - std::vector<MIBlock::Index> &worklist) -{ - worklist.clear(); - for (MIBlock::Index it = v; it != MIBlock::InvalidIndex; it = m_data->ancestor[it]) - worklist.push_back(it); - - if (worklist.size() < 2) - return m_data->best[v]; - - MIBlock::Index b = MIBlock::InvalidIndex; - MIBlock::Index last = worklist.back(); - Q_ASSERT(worklist.size() <= INT_MAX); - for (int it = static_cast<int>(worklist.size()) - 2; it >= 0; --it) { - MIBlock::Index bbIt = worklist[it]; - m_data->ancestor[bbIt] = last; - MIBlock::Index &best_it = m_data->best[bbIt]; - if (b != MIBlock::InvalidIndex - && m_data->dfnum[m_data->semi[b]] < m_data->dfnum[m_data->semi[best_it]]) { - best_it = b; - } else { - b = best_it; - } - } - return b; -} - -void DominatorFrontier::compute(const DominatorTree &domTree) -{ - struct NodeProgress { - std::vector<MIBlock::Index> children; - std::vector<MIBlock::Index> todo; - }; - - MIFunction *function = domTree.function(); - m_df.resize(function->blockCount()); - - // compute children of each node in the dominator tree - std::vector<std::vector<MIBlock::Index> > children; // BasicBlock index -> children - children.resize(function->blockCount()); - for (MIBlock *n : function->blocks()) { - const MIBlock::Index nodeIndex = n->index(); - Q_ASSERT(function->block(nodeIndex) == n); - const MIBlock::Index nodeDominator = domTree.immediateDominator(nodeIndex); - if (nodeDominator == MIBlock::InvalidIndex) - continue; // there is no dominator to add this node to as a child (e.g. the start node) - children[nodeDominator].push_back(nodeIndex); - } - - // Fill the worklist and initialize the node status for each basic-block - std::vector<NodeProgress> nodeStatus; - nodeStatus.resize(function->blockCount()); - std::vector<MIBlock::Index> worklist; - worklist.reserve(function->blockCount()); - for (MIBlock *bb : function->blocks()) { - MIBlock::Index nodeIndex = bb->index(); - worklist.push_back(nodeIndex); - NodeProgress &np = nodeStatus[nodeIndex]; - np.children = children[nodeIndex]; - np.todo = children[nodeIndex]; - } - - BitVector DF_done(int(function->blockCount()), false); - - while (!worklist.empty()) { - MIBlock::Index node = worklist.back(); - - if (DF_done.at(node)) { - worklist.pop_back(); - continue; - } - - NodeProgress &np = nodeStatus[node]; - auto it = np.todo.begin(); - while (it != np.todo.end()) { - if (DF_done.at(*it)) { - it = np.todo.erase(it); - } else { - worklist.push_back(*it); - break; - } - } - - if (np.todo.empty()) { - MIBlockSet &miBlockSet = m_df[node]; - miBlockSet.init(function); - for (MIBlock *y : function->block(node)->outEdges()) { - if (domTree.immediateDominator(y->index()) != node) - miBlockSet.insert(y); - } - for (MIBlock::Index child : np.children) { - const MIBlockSet &ws = m_df[child]; - for (auto w : ws) { - const MIBlock::Index wIndex = w->index(); - if (node == wIndex || !domTree.dominates(node, w->index())) - miBlockSet.insert(w); - } - } - DF_done.setBit(node); - worklist.pop_back(); - } - } - - if (lcDomFrontier().isDebugEnabled()) - dump(domTree.function()); -} - -void DominatorFrontier::dump(MIFunction *function) -{ - QBuffer buf; - buf.open(QIODevice::WriteOnly); - QTextStream qout(&buf); - qout << "Dominator Frontiers:" << endl; - for (MIBlock *n : function->blocks()) { - qout << "\tDF[" << n->index() << "]: {"; - const MIBlockSet &SList = m_df[n->index()]; - for (MIBlockSet::const_iterator i = SList.begin(), ei = SList.end(); i != ei; ++i) { - if (i != SList.begin()) - qout << ", "; - qout << (*i)->index(); - } - qout << "}" << endl; - } - qCDebug(lcDomFrontier, "%s", buf.data().constData()); -} - -} // IR namespace -} // QV4 namespace -QT_END_NAMESPACE diff --git a/src/qml/jit/qv4domtree_p.h b/src/qml/jit/qv4domtree_p.h deleted file mode 100644 index 703e17ab61..0000000000 --- a/src/qml/jit/qv4domtree_p.h +++ /dev/null @@ -1,136 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QV4DOMTREE_P_H -#define QV4DOMTREE_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qv4mi_p.h" -#include "qv4miblockset_p.h" - -QT_REQUIRE_CONFIG(qml_tracing); - -QT_BEGIN_NAMESPACE - -namespace QV4 { -namespace IR { - -class DominatorTree -{ - Q_DISABLE_COPY_MOVE(DominatorTree) - -public: - DominatorTree(MIFunction *f); - ~DominatorTree() = default; - - void dumpImmediateDominators() const; - MIFunction *function() const - { return m_function; } - - void setImmediateDominator(MIBlock::Index dominated, MIBlock::Index dominator); - - MIBlock::Index immediateDominator(MIBlock::Index blockIndex) const - { return m_idom[blockIndex]; } - - bool dominates(MIBlock::Index dominator, MIBlock::Index dominated) const; - - bool insideSameDominatorChain(MIBlock::Index one, MIBlock::Index other) const - { return one == other || dominates(one, other) || dominates(other, one); } - - std::vector<MIBlock *> calculateDFNodeIterOrder() const; - - std::vector<int> calculateNodeDepths() const; - -private: // functions - int calculateNodeDepth(MIBlock::Index nodeIdx, std::vector<int> &nodeDepths) const; - void link(MIBlock::Index p, MIBlock::Index n); - void calculateIDoms(); - void dfs(MIBlock::Index node); - MIBlock::Index ancestorWithLowestSemi(MIBlock::Index v, std::vector<MIBlock::Index> &worklist); - -private: // data - struct Data { - std::vector<unsigned> dfnum; // MIBlock index -> dfnum - std::vector<MIBlock::Index> vertex; - std::vector<MIBlock::Index> parent; // MIBlock index -> parent MIBlock index - std::vector<MIBlock::Index> ancestor; // MIBlock index -> ancestor MIBlock index - std::vector<MIBlock::Index> best; // MIBlock index -> best MIBlock index - std::vector<MIBlock::Index> semi; // MIBlock index -> semi dominator MIBlock index - std::vector<MIBlock::Index> samedom; // MIBlock index -> same dominator MIBlock index - unsigned size = 0; - }; - - MIFunction *m_function; - QScopedPointer<Data> m_data; - std::vector<MIBlock::Index> m_idom; // MIBlock index -> immediate dominator MIBlock index -}; - -class DominatorFrontier -{ -public: - DominatorFrontier(const DominatorTree &domTree) - { compute(domTree); } - - const MIBlockSet &operator[](MIBlock *n) const - { return m_df[n->index()]; } - -private: // functions - void compute(const DominatorTree &domTree); - void dump(MIFunction *function); - -private: // data - std::vector<MIBlockSet> m_df; // MIBlock index -> dominator frontier -}; - -} // namespace IR -} // namespace QV4 - -QT_END_NAMESPACE - -#endif // QV4DOMTREE_P_H diff --git a/src/qml/jit/qv4graph.cpp b/src/qml/jit/qv4graph.cpp deleted file mode 100644 index 4025ceb993..0000000000 --- a/src/qml/jit/qv4graph.cpp +++ /dev/null @@ -1,103 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4graph_p.h" -#include "qv4operation_p.h" - -QT_REQUIRE_CONFIG(qml_tracing); - -QT_BEGIN_NAMESPACE -namespace QV4 { -namespace IR { - -Graph *Graph::create(Function *function) -{ - auto storage = function->pool()->allocate(sizeof(Graph)); - auto g = new (storage) Graph(function); - g->m_undefinedNode = g->createNode(g->opBuilder()->get<Meta::Undefined>()); - g->m_emptyNode = g->createNode(g->opBuilder()->get<Meta::Empty>()); - g->m_nullNode = g->createNode(g->opBuilder()->getConstant(QV4::Value::nullValue())); - g->m_trueNode = g->createNode(g->opBuilder()->getConstant(QV4::Value::fromBoolean(true))); - g->m_falseNode = g->createNode(g->opBuilder()->getConstant(QV4::Value::fromBoolean(false))); - return g; -} - -Graph::MemoryPool *Graph::pool() const -{ - return m_function->pool(); -} - -Node *Graph::createNode(const Operation *op, Node *const operands[], size_t opCount, - bool incomplete) -{ - return Node::create(pool(), m_nextNodeId++, op, opCount, operands, incomplete); -} - -Node *Graph::createConstantBoolNode(bool value) -{ - return createNode(opBuilder()->getConstant(Primitive::fromBoolean(value))); -} - -Node *Graph::createConstantIntNode(int value) -{ - return createNode(opBuilder()->getConstant(Primitive::fromInt32(value))); -} - -Graph::Graph(Function *function) - : m_function(function) - , m_opBuilder(OperationBuilder::create(pool())) -{} - -Node *Graph::createConstantHeapNode(Heap::Base *heap) -{ - return createNode(opBuilder()->getConstant(Primitive::fromHeapObject(heap))); -} - -void Graph::addEndInput(Node *n) -{ - if (m_endNode) { - auto newEnd = m_opBuilder->getEnd(m_endNode->operation()->controlInputCount() + 1); - m_endNode->setOperation(newEnd); - m_endNode->addInput(m_function->pool(), n); - } -} - -} // IR namespace -} // QV4 namespace -QT_END_NAMESPACE diff --git a/src/qml/jit/qv4graph_p.h b/src/qml/jit/qv4graph_p.h deleted file mode 100644 index 4706399c94..0000000000 --- a/src/qml/jit/qv4graph_p.h +++ /dev/null @@ -1,146 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QV4GRAPH_P_H -#define QV4GRAPH_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qqmljsmemorypool_p.h> -#include <private/qv4global_p.h> -#include <private/qv4node_p.h> - -#include <array> - -QT_REQUIRE_CONFIG(qml_tracing); - -QT_BEGIN_NAMESPACE - -namespace QV4 { -namespace IR { - -class Function; -class Operation; -class OperationBuilder; - -class Graph final -{ - Q_DISABLE_COPY_MOVE(Graph) - -public: - using MemoryPool = QQmlJS::MemoryPool; - -public: - static Graph *create(Function *function); - ~Graph() = delete; - - MemoryPool *pool() const; - OperationBuilder *opBuilder() const - { return m_opBuilder; } - - Node *createNode(const Operation *op, Node * const operands[] = nullptr, size_t opCount = 0, - bool incomplete = false); - template <typename... Nodes> - Node *createNode(Operation *op, Nodes*... nodes) { - std::array<Node *, sizeof...(nodes)> nodesArray {{ nodes... }}; - return createNode(op, nodesArray.data(), nodesArray.size()); - } - Node *createConstantBoolNode(bool value); - Node *createConstantIntNode(int value); - Node *createConstantHeapNode(Heap::Base *heap); - - Node *undefinedNode() const { return m_undefinedNode; } - Node *emptyNode() const { return m_emptyNode; } - Node *nullNode() const { return m_nullNode; } - Node *trueConstant() const { return m_trueNode; } - Node *falseConstant() const { return m_falseNode; } - - Node *startNode() const { return m_startNode; } - Node *engineNode() const { return m_engineNode; } - Node *functionNode() const { return m_functionNode; } - Node *cppFrameNode() const { return m_cppFrameNode; } - Node *endNode() const { return m_endNode; } - Node *initialFrameState() const { return m_initialFrameState; } - void setStartNode(Node *n) { m_startNode = n; } - void setEngineNode(Node *n) { m_engineNode = n; } - void setFunctionNode(Node *n) { m_functionNode = n; } - void setCppFrameNode(Node *n) { m_cppFrameNode = n; } - void setEndNode(Node *n) { m_endNode = n; } - void setInitialFrameState(Node *n) { m_initialFrameState = n; } - - unsigned nodeCount() const - { return unsigned(m_nextNodeId); } - - void addEndInput(Node *n); - -private: // types and methods - Graph(Function *function); - -private: // fields - Function *m_function; - OperationBuilder *m_opBuilder; - Node::Id m_nextNodeId = 0; - Node *m_undefinedNode = nullptr; - Node *m_emptyNode = nullptr; - Node *m_nullNode = nullptr; - Node *m_trueNode = nullptr; - Node *m_falseNode = nullptr; - Node *m_startNode = nullptr; - Node *m_engineNode = nullptr; - Node *m_functionNode = nullptr; - Node *m_cppFrameNode = nullptr; - Node *m_endNode = nullptr; - Node *m_initialFrameState = nullptr; -}; - -} // namespace IR -} // namespace QV4 - -QT_END_NAMESPACE - -#endif // QV4GRAPH_P_H diff --git a/src/qml/jit/qv4graphbuilder.cpp b/src/qml/jit/qv4graphbuilder.cpp deleted file mode 100644 index 2c073701ee..0000000000 --- a/src/qml/jit/qv4graphbuilder.cpp +++ /dev/null @@ -1,1683 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <QtCore/qloggingcategory.h> - -#include "qv4graphbuilder_p.h" -#include "qv4function_p.h" -#include "qv4lookup_p.h" -#include "qv4stackframe_p.h" -#include "qv4operation_p.h" - -QT_BEGIN_NAMESPACE -namespace QV4 { -namespace IR { - -Q_LOGGING_CATEGORY(lcIRGraphBuilder, "qt.v4.ir.graphbuilder") - -using MemoryPool = QQmlJS::MemoryPool; - -namespace { -template <typename T, size_t N> -char (&ArraySizeHelper(T (&array)[N]))[N]; -template <typename T, size_t N> -char (&ArraySizeHelper(const T (&array)[N]))[N]; - -template <typename Array> -inline size_t arraySize(const Array &array) -{ - Q_UNUSED(array); // for MSVC - return sizeof(ArraySizeHelper(array)); -} -} // anonymous namespace - -class GraphBuilder::InterpreterEnvironment -{ -public: - struct FrameState: public QQmlJS::FixedPoolArray<Node *> - { - FrameState(MemoryPool *pool, int totalSlotCount) - : FixedPoolArray(pool, totalSlotCount) - {} - - Node *&unwindHandlerOffset() - { return at(size() - 1); } - - static FrameState *create(MemoryPool *pool, int jsSlotCount) - { - auto totalSlotCount = jsSlotCount; - auto fs = pool->New<FrameState>(pool, totalSlotCount); - return fs; - } - - static FrameState *clone(MemoryPool *pool, FrameState *other) - { - FrameState *fs = create(pool, other->size()); - - for (int i = 0, ei = other->size(); i != ei; ++i) - fs->at(i) = other->at(i); - - return fs; - } - }; - -public: - InterpreterEnvironment(GraphBuilder *graphBuilder, Node *controlDependency) - : m_graphBuilder(graphBuilder) - , m_effectDependency(controlDependency) - , m_controlDependency(controlDependency) - , m_currentFrame(nullptr) - {} - - void createEnvironment() - { - Function *f = function(); - QV4::Function *v4Function = f->v4Function(); - const size_t nRegisters = v4Function->compiledFunction->nRegisters; - - // 1 extra slot for the unwindHandlerOffset - m_currentFrame = FrameState::create(graph()->pool(), int(nRegisters + 1)); - } - - void setupStartEnvironment() - { - Function *f = function(); - QV4::Function *v4Function = f->v4Function(); - const size_t nFormals = v4Function->compiledFunction->nFormals; - const size_t nRegisters = v4Function->compiledFunction->nRegisters; - - createEnvironment(); - - Node *startNode = graph()->startNode(); - auto opB = opBuilder(); - auto create = [&](int index, const char *name) { - m_currentFrame->at(index) = graph()->createNode( - opB->getParam(index, f->addString(QLatin1String(name))), startNode); - }; - create(0, "%function"); - create(1, "%context"); - create(2, "%acc"); - create(3, "%this"); - create(4, "%newTarget"); - create(5, "%argc"); - const quint32_le *formalNameIdx = v4Function->compiledFunction->formalsTable(); - for (size_t i = 0; i < nFormals; ++i, ++formalNameIdx) { - const int slot = int(CallData::HeaderSize() + i); - Q_ASSERT(*formalNameIdx <= quint32(std::numeric_limits<int>::max())); - auto op = opB->getParam( - slot, - f->addString(v4Function->compilationUnit->stringAt(int(*formalNameIdx)))); - Node *argNode = graph()->createNode(op, startNode); - m_currentFrame->at(slot) = argNode; - } - Node *undefinedNode = graph()->undefinedNode(); - Node *emptyNode = graph()->emptyNode(); - const auto firstDeadZoneRegister - = v4Function->compiledFunction->firstTemporalDeadZoneRegister; - const auto registerDeadZoneSize - = v4Function->compiledFunction->sizeOfRegisterTemporalDeadZone; - for (size_t i = CallData::HeaderSize() + nFormals; i < nRegisters; ++i) { - const bool isDead = i >= firstDeadZoneRegister - && i < size_t(firstDeadZoneRegister + registerDeadZoneSize); - m_currentFrame->at(int(i)) = isDead ? emptyNode : undefinedNode; - } - setUnwindHandlerOffset(0); - } - - Function *function() const { return m_graphBuilder->function(); } - Graph *graph() const { return function()->graph(); } - OperationBuilder *opBuilder() const { return graph()->opBuilder(); } - GraphBuilder *graphBuilder() const { return m_graphBuilder; } - - Node *bindAcc(Node *node) - { - bindNodeToSlot(node, CallData::Accumulator); - return node; - } - - Node *accumulator() const - { return slot(CallData::Accumulator); } - - Node *bindNodeToSlot(Node *node, int slot) - { - m_currentFrame->at(size_t(slot)) = node; - return node; - } - - Node *slot(int slot) const - { return m_currentFrame->at(slot); } - - int slotCount() const - { return m_currentFrame->size(); } - - Node *effectDependency() const - { return m_effectDependency; } - - void setEffectDependency(Node *newNode) - { m_effectDependency = newNode; } - - Node *controlDependency() const - { return m_controlDependency; } - - void setControlDependency(Node *newNode) - { m_controlDependency = newNode; } - - Node *createFrameState() - { - return graph()->createNode(graphBuilder()->opBuilder()->getFrameState(slotCount()), - m_currentFrame->begin(), slotCount()); - } - - Node *merge(InterpreterEnvironment *other); - - InterpreterEnvironment *copy() const - { - auto *newEnv = graph()->pool()->New<InterpreterEnvironment>(graphBuilder(), - controlDependency()); - newEnv->setEffectDependency(effectDependency()); - newEnv->m_currentFrame = FrameState::clone(graph()->pool(), m_currentFrame); - return newEnv; - } - - int unwindHandlerOffset() const - { - auto uhOp = m_currentFrame->unwindHandlerOffset()->operation(); - Q_ASSERT(uhOp->kind() == Meta::Constant); - return ConstantPayload::get(*uhOp)->value().int_32(); - } - - void setUnwindHandlerOffset(int newOffset) - { m_currentFrame->unwindHandlerOffset() = graphBuilder()->createConstant(newOffset); } - - FrameState *frameState() const - { return m_currentFrame; } - -private: - GraphBuilder *m_graphBuilder; - Node *m_effectDependency; - Node *m_controlDependency; - FrameState *m_currentFrame; -}; - -namespace { -class InterpreterSubEnvironment final -{ - Q_DISABLE_COPY_MOVE(InterpreterSubEnvironment) - -public: - explicit InterpreterSubEnvironment(GraphBuilder *builder) - : m_builder(builder) - , m_parent(builder->env()->copy()) - {} - - ~InterpreterSubEnvironment() - { m_builder->setEnv(m_parent); } - -private: - GraphBuilder *m_builder; - GraphBuilder::InterpreterEnvironment *m_parent; -}; -} // anonymous namespace - -Node *GraphBuilder::InterpreterEnvironment::merge(InterpreterEnvironment *other) -{ - Q_ASSERT(m_currentFrame->size() == other->m_currentFrame->size()); - - auto gb = graphBuilder(); - Node *mergedControl = gb->mergeControl(controlDependency(), other->controlDependency()); - setControlDependency(mergedControl); - setEffectDependency(gb->mergeEffect(effectDependency(), other->effectDependency(), mergedControl)); - - // insert/update phi nodes, but not for the unwind handler: - for (int i = 0, ei = m_currentFrame->size() - 1; i != ei; ++i) { - //### use lifeness info to trim this! - m_currentFrame->at(i) = gb->mergeValue(m_currentFrame->at(i), - other->m_currentFrame->at(i), - mergedControl); - } - Q_ASSERT(unwindHandlerOffset() >= 0); // specifically: don't crash - return mergedControl; -} - -void GraphBuilder::buildGraph(IR::Function *function) -{ - const char *code = function->v4Function()->codeData; - uint len = function->v4Function()->compiledFunction->codeSize; - - GraphBuilder builder(function); - builder.startGraph(); - - InterpreterEnvironment initial(&builder, function->graph()->startNode()); - initial.setupStartEnvironment(); - builder.setEnv(&initial); - builder.graph()->setInitialFrameState(initial.createFrameState()); - builder.decode(code, len); - builder.endGraph(); -}; - -GraphBuilder::GraphBuilder(IR::Function *function) - : m_func(function) - , m_graph(function->graph()) - , m_currentEnv(nullptr) -{ - for (unsigned i = 0, ei = m_func->v4Function()->compiledFunction->nLabelInfos; i != ei; ++i) { - unsigned label = m_func->v4Function()->compiledFunction->labelInfoTable()[i]; - m_labelInfos.emplace_back(label); - if (lcIRGraphBuilder().isDebugEnabled()) { - const LabelInfo &li = m_labelInfos.back(); - qCDebug(lcIRGraphBuilder) << "Loop start at" << li.labelOffset; - } - } -} - -void GraphBuilder::startGraph() -{ - size_t nValuesOut = 1 + CallData::HeaderSize() - + m_func->v4Function()->compiledFunction->nFormals; - Node *start = m_graph->createNode(opBuilder()->getStart(uint16_t(nValuesOut)), nullptr, 0); - m_func->nodeInfo(start)->setBytecodeOffsets(0, 0); - m_graph->setStartNode(start); - m_graph->setEngineNode(m_graph->createNode(opBuilder()->get<Meta::Engine>(), &start, 1)); - auto frame = m_graph->createNode(opBuilder()->get<Meta::CppFrame>(), &start, 1); - m_graph->setCppFrameNode(frame); - m_graph->setFunctionNode(m_graph->createNode(opBuilder()->get<Meta::Function>(), - &frame, 1)); -} - -void GraphBuilder::endGraph() -{ - const auto inputCount = uint16_t(m_exitControls.size()); - Node **inputs = &m_exitControls.front(); - Q_ASSERT(m_graph->endNode() == nullptr); - m_graph->setEndNode(m_graph->createNode(opBuilder()->getEnd(inputCount), inputs, inputCount)); -} - -Node *GraphBuilder::bindAcc(Node *n) -{ - return env()->bindAcc(n); -} - -/* IMPORTANT!!! - * - * This might change the success environment, so don't call: - * env()->bindAcc(createNode(...)) - * because the binding should only happen on success, but the call to env() will get the - * environment from *before* the new success environment was created. Instead, do: - * bindAcc(createNode(....)) - */ -Node *GraphBuilder::createAndLinkNode(Operation *op, Node *operands[], size_t opCount, - bool incomplete) -{ - Q_ASSERT(op->effectInputCount() < 2); - Q_ASSERT(op->controlInputCount() < 2); - - QVarLengthArray<Node *, 32> inputs(static_cast<int>(opCount)); - std::copy_n(operands, opCount, inputs.data()); - - if (op->effectInputCount() == 1) - inputs.append(env()->effectDependency()); - if (op->controlInputCount() == 1) - inputs.append(env()->controlDependency()); - if (op->hasFrameStateInput()) - inputs.append(env()->createFrameState()); - - Node *node = m_graph->createNode(op, inputs.data(), inputs.size(), incomplete); - - if (op->needsBytecodeOffsets()) { - m_func->nodeInfo(node)->setBytecodeOffsets(currentInstructionOffset(), - nextInstructionOffset()); - } - - if (op->effectOutputCount() > 0) - env()->setEffectDependency(node); - if (op->controlOutputCount() > 0) - env()->setControlDependency(node); - - if (op->canThrow() && env()->unwindHandlerOffset()) { - InterpreterSubEnvironment successEnv(this); - Node *control = env()->controlDependency(); - control = m_graph->createNode(opBuilder()->get<Meta::OnException>(), &control, 1); - env()->setControlDependency(control); - auto unwindHandlerOffset = env()->unwindHandlerOffset(); - mergeIntoSuccessor(unwindHandlerOffset); - } - - return node; -} - -Node *GraphBuilder::createNode(Operation *op, bool incomplete) -{ - return createAndLinkNode(op, nullptr, 0, incomplete); -} - -Node *GraphBuilder::createNode(Operation *op, Node *n1) -{ - Node *buf[] = { n1 }; - return createAndLinkNode(op, buf, arraySize(buf)); -} - -Node *GraphBuilder::createNode(Operation *op, Node *n1, Node *n2) -{ - Node *buf[] = { n1, n2 }; - return createAndLinkNode(op, buf, arraySize(buf)); -} - -Node *GraphBuilder::createNode(Operation *op, Node *n1, Node *n2, Node *n3) -{ - Node *buf[] = { n1, n2, n3 }; - return createAndLinkNode(op, buf, arraySize(buf)); -} - -Node *GraphBuilder::createNode(Operation *op, Node *n1, Node *n2, Node *n3, Node *n4) -{ - Node *buf[] = { n1, n2, n3, n4 }; - return createAndLinkNode(op, buf, arraySize(buf)); -} - -Node *GraphBuilder::createRegion(unsigned nControlInputs) -{ - return createNode(opBuilder()->getRegion(nControlInputs), true); -} - -Node *GraphBuilder::createIfTrue() -{ - return createNode(opBuilder()->get<Meta::IfTrue>()); -} - -Node *GraphBuilder::createIfFalse() -{ - return createNode(opBuilder()->get<Meta::IfFalse>()); -} - -Node *GraphBuilder::createConstant(int v) -{ - return m_graph->createNode(opBuilder()->getConstant(Primitive::fromInt32(v))); -} - -Node *GraphBuilder::createPhi(unsigned nInputs, Node *input, Node *control) -{ - auto phiOp = opBuilder()->getPhi(nInputs); - QVarLengthArray<Node *, 32> buffer(int(nInputs + 1)); - std::fill_n(buffer.data(), nInputs, input); - buffer[int(nInputs)] = control; - return m_graph->createNode(phiOp, buffer.data(), nInputs + 1, true); -} - -Node *GraphBuilder::createEffectPhi(unsigned nInputs, Node *input, Node *control) -{ - auto phiOp = opBuilder()->getEffectPhi(nInputs); - QVarLengthArray<Node *, 32> buffer(int(nInputs + 1)); - std::fill_n(buffer.data(), nInputs, input); - buffer[int(nInputs)] = control; - return m_graph->createNode(phiOp, buffer.data(), nInputs + 1, true); -} - -Node *GraphBuilder::createHandleUnwind(int offset) -{ - return createNode(opBuilder()->getHandleUnwind(offset)); -} - -Node *GraphBuilder::mergeControl(Node *c1, Node *c2) -{ - if (c1->operation()->kind() == Meta::Region) { - const unsigned nInputs = c1->operation()->controlInputCount() + 1; - c1->addInput(m_graph->pool(), c2); - c1->setOperation(opBuilder()->getRegion(nInputs)); - return c1; - } - auto op = opBuilder()->getRegion(2); - Node *inputs[] = { c1, c2 }; - return m_graph->createNode(op, inputs, 2); -} - -Node *GraphBuilder::mergeEffect(Node *e1, Node *e2, Node *control) -{ - const unsigned nInputs = control->operation()->controlInputCount(); - if (e1->operation()->kind() == Meta::EffectPhi && e1->controlInput() == control) { - e1->insertInput(m_graph->pool(), nInputs - 1, e2); - e1->setOperation(opBuilder()->getEffectPhi(nInputs)); - return e1; - } - - if (e1 != e2) { - Node *phi = createEffectPhi(nInputs, e1, control); - phi->replaceInput(nInputs - 1, e2); - return phi; - } - - return e1; -} - -Node *GraphBuilder::mergeValue(Node *v1, Node *v2, Node *control) -{ - const unsigned nInputs = control->operation()->controlInputCount(); - if (v1->operation()->kind() == Meta::Phi && v1->controlInput() == control) { - v1->insertInput(m_graph->pool(), nInputs - 1, v2); - v1->setOperation(opBuilder()->getPhi(nInputs)); - return v1; - } - - if (v1 != v2) { - Node *phi = createPhi(nInputs, v1, control); - phi->replaceInput(nInputs - 1, v2); - return phi; - } - - return v1; -} - -Node *GraphBuilder::createToBoolean(Node *input) -{ - return createNode(opBuilder()->get<Meta::ToBoolean>(), input); -} - -void GraphBuilder::populate(VarArgNodes &args, int argc, int argv) -{ - for (int i = 0; i < argc; ++i) - args.append(env()->slot(argv + i)); - Q_ASSERT(argc >= 0 && argc <= std::numeric_limits<uint16_t>::max()); -} - -void GraphBuilder::queueFunctionExit(Node *exitNode) -{ - m_exitControls.push_back(exitNode); - setEnv(nullptr); -} - -Node *GraphBuilder::mergeIntoSuccessor(int offset) -{ - InterpreterEnvironment *&successorEnvironment = m_envForOffset[offset]; - - Node *region = nullptr; - if (successorEnvironment == nullptr) { - region = createRegion(1); - successorEnvironment = env(); - } else { - // Merge any values which are live coming into the successor. - region = successorEnvironment->merge(env()); - } - setEnv(nullptr); - return region; -} - -const GraphBuilder::LabelInfo *GraphBuilder::labelInfoAt(unsigned offset) const -{ - for (const LabelInfo &li : m_labelInfos) { - if (li.labelOffset == offset) - return &li; - } - return nullptr; -} - -const GraphBuilder::LabelInfo *GraphBuilder::isLoopStart(unsigned offset) const -{ - if (auto li = labelInfoAt(offset)) { - //### in the future, check if this is a loop start, or some other label - return li; - } - - return nullptr; -} - -void GraphBuilder::handleLoopStart(const LabelInfo &labelInfo) -{ - Q_ASSERT(env() != nullptr); - - // We unconditionally insert a region node with phi nodes here. Now there might already be - // such a node, (e.g. the region after an if-then-else), but for simplicity we ignore that. - // A subsequent pass will fold/remove chains of Region nodes. - //### FIXME: add a DCE pass - - const auto offset = int(labelInfo.labelOffset); - Node *control = createRegion(1); - env()->setControlDependency(control); - Node *effect = createEffectPhi(1, env()->effectDependency(), control); - env()->setEffectDependency(effect); - - // insert/update phi nodes, but not for the unwind handler: - for (int i = 0, ei = env()->slotCount() - 1; i != ei; ++i) { - //### use lifeness info to trim this further! - if (i == CallData::Accumulator) - continue; // should never be alive on loop entry - env()->bindNodeToSlot(createPhi(1, env()->slot(i), control), i); - } - - m_envForOffset.insert(offset, env()->copy()); -} - -void GraphBuilder::startUnwinding() -{ - if (int target = env()->unwindHandlerOffset()) { - mergeIntoSuccessor(target); - } else { - bindAcc(graph()->undefinedNode()); - generate_Ret(); - } -} - -void GraphBuilder::generate_Ret() -{ - Node* control = createNode(opBuilder()->get<Meta::Return>(), env()->accumulator()); - queueFunctionExit(control); -} - -void GraphBuilder::generate_Debug() { Q_UNREACHABLE(); } - -void GraphBuilder::generate_LoadConst(int index) -{ - auto func = function()->v4Function(); - Value v = func->compilationUnit->constants[index]; - bindAcc(createNode(opBuilder()->getConstant(v))); -} - -void GraphBuilder::generate_LoadZero() -{ - bindAcc(createConstant(0)); -} - -void GraphBuilder::generate_LoadTrue() -{ - bindAcc(m_graph->trueConstant()); -} - -void GraphBuilder::generate_LoadFalse() -{ - bindAcc(m_graph->falseConstant()); -} - -void GraphBuilder::generate_LoadNull() -{ - bindAcc(m_graph->nullNode()); -} - -void GraphBuilder::generate_LoadUndefined() -{ - bindAcc(m_graph->undefinedNode()); -} - -void GraphBuilder::generate_LoadInt(int value) -{ - bindAcc(m_graph->createNode(opBuilder()->getConstant(Primitive::fromInt32(value)))); -} - -void GraphBuilder::generate_MoveConst(int constIndex, int destTemp) -{ - auto func = function()->v4Function(); - Value v = func->compilationUnit->constants[constIndex]; - env()->bindNodeToSlot(createNode(opBuilder()->getConstant(v)), destTemp); -} - -void GraphBuilder::generate_LoadReg(int reg) -{ - bindAcc(env()->slot(reg)); -} - -void GraphBuilder::generate_StoreReg(int reg) -{ - Node *n = env()->accumulator(); - if (reg == CallData::This) - n = createNode(opBuilder()->get<Meta::StoreThis>(), n); - env()->bindNodeToSlot(n, reg); -} - -void GraphBuilder::generate_MoveReg(int srcReg, int destReg) -{ - env()->bindNodeToSlot(env()->slot(srcReg), destReg); -} - -void GraphBuilder::generate_LoadImport(int index) -{ - auto func = function()->v4Function(); - Value v = *func->compilationUnit->imports[index]; - bindAcc(createNode(opBuilder()->getConstant(v))); -} - -void GraphBuilder::generate_LoadLocal(int index, int /*traceSlot*/) -{ - bindAcc(createNode(opBuilder()->get<Meta::ScopedLoad>(), - createConstant(0), - createConstant(index))); -} - -void GraphBuilder::generate_StoreLocal(int index) -{ - createNode(opBuilder()->get<Meta::ScopedStore>(), - createConstant(0), - createConstant(index), - env()->accumulator()); -} - -void GraphBuilder::generate_LoadScopedLocal(int scope, int index, int /*traceSlot*/) -{ - bindAcc(createNode(opBuilder()->get<Meta::ScopedLoad>(), - createConstant(scope), - createConstant(index))); -} - -void GraphBuilder::generate_StoreScopedLocal(int scope, int index) -{ - createNode(opBuilder()->get<Meta::ScopedStore>(), - createConstant(scope), - createConstant(index), - env()->accumulator()); -} - -void GraphBuilder::generate_LoadRuntimeString(int stringId) -{ - auto func = function()->v4Function(); - Value v = Value::fromHeapObject(func->compilationUnit->runtimeStrings[stringId]); - bindAcc(createNode(opBuilder()->getConstant(v))); -} - -void GraphBuilder::generate_MoveRegExp(int regExpId, int destReg) -{ - env()->bindNodeToSlot(createNode(opBuilder()->get<Meta::LoadRegExp>(), - createConstant(regExpId)), destReg); -} - -void GraphBuilder::generate_LoadClosure(int value) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSLoadClosure>(), - createConstant(value))); -} - -void GraphBuilder::generate_LoadName(int name, int /*traceSlot*/) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSLoadName>(), - createConstant(name))); -} - -void GraphBuilder::generate_LoadGlobalLookup(int index, int /*traceSlot*/) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSLoadGlobalLookup>(), createConstant(index))); -} - -void GraphBuilder::generate_StoreNameSloppy(int name) -{ - createNode(opBuilder()->get<Meta::JSStoreNameSloppy>(), createConstant(name), env()->accumulator()); -} - -void GraphBuilder::generate_StoreNameStrict(int name) -{ - createNode(opBuilder()->get<Meta::JSStoreNameStrict>(), createConstant(name), env()->accumulator()); -} - -void GraphBuilder::generate_LoadElement(int base, int /*traceSlot*/) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSLoadElement>(), - env()->slot(base), - env()->accumulator())); -} - -void GraphBuilder::generate_StoreElement(int base, int index, int /*traceSlot*/) -{ - createNode(opBuilder()->get<Meta::JSStoreElement>(), - env()->slot(base), - env()->slot(index), - env()->accumulator()); -} - -void GraphBuilder::generate_LoadProperty(int name, int /*traceSlot*/) -{ - Node *n = createNode(opBuilder()->get<Meta::JSLoadProperty>(), - env()->accumulator(), - createConstant(name)); - bindAcc(n); -} - -void GraphBuilder::generate_GetLookup(int index, int /*traceSlot*/) -{ - Node *n = createNode(opBuilder()->get<Meta::JSGetLookup>(), - env()->accumulator(), - createConstant(index)); - bindAcc(n); -} - -void GraphBuilder::generate_StoreProperty(int name, int base) -{ - createNode(opBuilder()->get<Meta::JSStoreProperty>(), - env()->slot(base), - createConstant(name), - env()->accumulator()); -} - -void GraphBuilder::generate_SetLookup(int index, int base) -{ - - function()->v4Function()->isStrict() - ? createNode(opBuilder()->get<Meta::JSSetLookupStrict>(), env()->slot(base), - createConstant(index), env()->accumulator()) - : createNode(opBuilder()->get<Meta::JSSetLookupSloppy>(), env()->slot(base), - createConstant(index), env()->accumulator()); -} - -void GraphBuilder::generate_LoadSuperProperty(int property) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSLoadSuperProperty>(), - env()->slot(property))); -} - -void GraphBuilder::generate_StoreSuperProperty(int property) -{ - createNode(opBuilder()->get<Meta::JSStoreSuperProperty>(), - createConstant(property), - env()->accumulator()); -} - -void GraphBuilder::generate_LoadQmlContextPropertyLookup(int propertyIndex, int /*traceSlot*/) -{ - bindAcc(createNode(opBuilder()->get<Meta::QMLLoadQmlContextPropertyLookup>(), - createConstant(propertyIndex))); -} - -void GraphBuilder::generate_Yield() { Q_UNREACHABLE(); } -void GraphBuilder::generate_YieldStar() { Q_UNREACHABLE(); } -void GraphBuilder::generate_Resume(int /*offset*/) { Q_UNREACHABLE(); } - -void GraphBuilder::finalizeCall(Operation::Kind kind, VarArgNodes &args, int argc, int argv) -{ - populate(args, argc, argv); - bindAcc(createAndLinkNode(opBuilder()->getJSVarArgsCall(kind, uint16_t(args.size())), - args.data(), size_t(args.size()))); -} - -void GraphBuilder::generate_CallValue(int name, int argc, int argv, int /*traceSlot*/) -{ - VarArgNodes args; - args.append(env()->slot(name)); - finalizeCall(Meta::JSCallValue, args, argc, argv); -} - -void GraphBuilder::generate_CallWithReceiver(int name, int thisObject, int argc, int argv, - int /*traceSlot*/) -{ - VarArgNodes args; - args.append(env()->slot(name)); - args.append(env()->slot(thisObject)); - finalizeCall(Meta::JSCallWithReceiver, args, argc, argv); -} - -void GraphBuilder::generate_CallProperty(int name, int base, int argc, int argv, int /*traceSlot*/) -{ - VarArgNodes args; - args.append(env()->slot(base)); - args.append(createConstant(name)); - finalizeCall(Meta::JSCallProperty, args, argc, argv); -} - -void GraphBuilder::generate_CallPropertyLookup(int lookupIndex, int base, int argc, int argv, - int /*traceSlot*/) -{ - VarArgNodes args; - args.append(env()->slot(base)); - args.append(createConstant(lookupIndex)); - finalizeCall(Meta::JSCallLookup, args, argc, argv); -} - -void GraphBuilder::generate_CallElement(int base, int index, int argc, int argv, int /*traceSlot*/) -{ - VarArgNodes args; - args.append(env()->slot(base)); - args.append(env()->slot(index)); - finalizeCall(Meta::JSCallElement, args, argc, argv); -} - -void GraphBuilder::generate_CallName(int name, int argc, int argv, int /*traceSlot*/) -{ - VarArgNodes args; - args.append(createConstant(name)); - finalizeCall(Meta::JSCallName, args, argc, argv); -} - -void GraphBuilder::generate_CallPossiblyDirectEval(int argc, int argv, int /*traceSlot*/) -{ - VarArgNodes args; - finalizeCall(Meta::JSCallPossiblyDirectEval, args, argc, argv); -} - -void GraphBuilder::generate_CallGlobalLookup(int index, int argc, int argv, int /*traceSlot*/) -{ - VarArgNodes args; - args.append(createConstant(index)); - finalizeCall(Meta::JSCallGlobalLookup, args, argc, argv); -} - -void GraphBuilder::generate_CallQmlContextPropertyLookup(int index, int argc, int argv, - int /*traceSlot*/) -{ - VarArgNodes args; - args.append(createConstant(index)); - finalizeCall(Meta::QMLCallQmlContextPropertyLookup, args, argc, argv); -} - -void GraphBuilder::generate_SetUnwindHandler(int offset) -{ - m_currentUnwindHandlerOffset = offset ? absoluteOffset(offset) : 0; - env()->setUnwindHandlerOffset(m_currentUnwindHandlerOffset); -} - -void GraphBuilder::generate_UnwindDispatch() -{ - auto e = createNode(opBuilder()->get<Meta::HasException>(), graph()->engineNode()); - createNode(opBuilder()->get<Meta::Branch>(), e); - { - InterpreterSubEnvironment subEnvironment(this); - createIfTrue(); - startUnwinding(); - } - - createIfFalse(); - - const auto unwindHandlerOffset = env()->unwindHandlerOffset(); - const auto fallthroughSuccessor = nextInstructionOffset(); - auto nContinuations = m_func->unwindLabelOffsets().size() + 1; - if (unwindHandlerOffset) - ++nContinuations; - Q_ASSERT(nContinuations <= std::numeric_limits<unsigned>::max()); - createNode(opBuilder()->getUnwindDispatch(unsigned(nContinuations), unwindHandlerOffset, - fallthroughSuccessor)); - - { - InterpreterSubEnvironment fallthroughEnv(this); - mergeIntoSuccessor(fallthroughSuccessor); - } - - if (unwindHandlerOffset) { - InterpreterSubEnvironment unwindHandlerEnv(this); - createHandleUnwind(unwindHandlerOffset); - mergeIntoSuccessor(unwindHandlerOffset); - } - - for (int unwindLabelOffset : m_func->unwindLabelOffsets()) { - if (unwindLabelOffset <= currentInstructionOffset()) - continue; - InterpreterSubEnvironment unwindLabelEnv(this); - createHandleUnwind(unwindLabelOffset); - mergeIntoSuccessor(unwindLabelOffset); - } - - setEnv(nullptr); -} - -void GraphBuilder::generate_UnwindToLabel(int level, int offset) -{ - //### For de-optimization, the relative offset probably also needs to be stored - int unwinder = absoluteOffset(offset); - createNode(opBuilder()->get<Meta::UnwindToLabel>(), - createConstant(level), - createConstant(unwinder)); - m_func->addUnwindLabelOffset(unwinder); - startUnwinding(); -} - -void GraphBuilder::generate_DeadTemporalZoneCheck(int name) -{ - Node *check = createNode(opBuilder()->get<Meta::IsEmpty>(), env()->accumulator()); - createNode(opBuilder()->get<Meta::Branch>(), check); - - { //### it's probably better to handle this by de-optimizing - InterpreterSubEnvironment subEnvironment(this); - createIfTrue(); - createNode(opBuilder()->get<Meta::ThrowReferenceError>(), - createConstant(name)); - startUnwinding(); - } - - createIfFalse(); -} - -void GraphBuilder::generate_ThrowException() -{ - createNode(opBuilder()->get<Meta::Throw>(), env()->accumulator()); - startUnwinding(); -} - -void GraphBuilder::generate_GetException() -{ - bindAcc(createNode(opBuilder()->get<Meta::GetException>())); -} - -void GraphBuilder::generate_SetException() -{ - createNode(opBuilder()->get<Meta::SetException>(), - env()->accumulator()); -} - -void GraphBuilder::generate_CreateCallContext() -{ - createNode(opBuilder()->get<Meta::JSCreateCallContext>()); -} - -void GraphBuilder::generate_PushCatchContext(int index, int name) -{ - createNode(opBuilder()->get<Meta::JSCreateCatchContext>(), - createConstant(index), - createConstant(name)); -} - -void GraphBuilder::generate_PushWithContext() -{ - bindAcc(createNode(opBuilder()->get<Meta::JSCreateWithContext>(), - env()->accumulator())); -} - -void GraphBuilder::generate_PushBlockContext(int index) -{ - createNode(opBuilder()->get<Meta::JSCreateBlockContext>(), - createConstant(index)); -} - -void GraphBuilder::generate_CloneBlockContext() -{ - createNode(opBuilder()->get<Meta::JSCloneBlockContext>()); -} - -void GraphBuilder::generate_PushScriptContext(int index) -{ - createNode(opBuilder()->get<Meta::JSCreateScriptContext>(), createConstant(index)); -} - -void GraphBuilder::generate_PopScriptContext() -{ - createNode(opBuilder()->get<Meta::JSPopScriptContext>()); -} - -void GraphBuilder::generate_PopContext() -{ - createNode(opBuilder()->get<Meta::PopContext>()); -} - -void GraphBuilder::generate_GetIterator(int iterator) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSGetIterator>(), - env()->accumulator(), - createConstant(iterator))); -} - -void GraphBuilder::generate_IteratorNextAndFriends_TrailingStuff(Node *iterationNode, - int resultSlot) -{ - // See generate_IteratorNext for why this method exists. - - // check that no-one messed around with the operation and made it throwing - Q_ASSERT(iterationNode->operation()->controlOutputCount() == 1); - - // check that it's in the effect chain, because HasException relies on that - Q_ASSERT(iterationNode->operation()->effectOutputCount() == 1); - - env()->bindNodeToSlot(createNode(opBuilder()->get<Meta::SelectOutput>(), - iterationNode, - createConstant(1), - graph()->undefinedNode()), - resultSlot); - // Note: the following will NOT set the accumulator, because it contains the return value of - // the runtime call! - Node *ehCheck = createNode(opBuilder()->get<Meta::HasException>(), graph()->engineNode()); - createNode(opBuilder()->get<Meta::Branch>(), ehCheck); - - { // EH path: - InterpreterSubEnvironment subEnvironment(this); - createIfTrue(); - if (auto ehOffset = env()->unwindHandlerOffset()) { - // Ok, there is an exception handler, so go there: - mergeIntoSuccessor(ehOffset); - } else { - // No Exception Handler, so keep the exception set in the engine, and leave the function - // a.s.a.p.: - bindAcc(graph()->undefinedNode()); - generate_Ret(); - } - } - - // Normal control flow: - createIfFalse(); -} - -void GraphBuilder::generate_IteratorNext(int value, int done) -{ - // The way we model exceptions in the graph is that a runtime function will either succeed and - // return a value, or it fails and throws an exception. If it throws, the return value is not - // used because the method did not complete normally, and therefore it might be tainted. - // - // This is a problem for (and only for) IteratorNext and IteratorNextForYieldStart. - // - // What would happen in the normal case, is that the return value (done) is not used/assigned - // when IteratorNext throws, because the exception handling path is chosen. However, the - // interpreter *does* assign it, and will only check for an exception *after* that assignment. - // - // So, in order to work around this odd-duck behavior, we mark the operation as NoThrow, - // override the runtime method and flag it to not throw, and insert extra exception check nodes - // after the SelectOutput that follows the IteratorNext(ForYieldStar). - // - // Also note that the IteratorNext and IteratorNextForYieldStar are the only operations that - // have an inout parameter, and thus require a SelectOutput node to retrieve this. - - Node *n = createNode(opBuilder()->get<Meta::JSIteratorNext>(), - env()->accumulator(), - graph()->undefinedNode()); - bindAcc(n); - env()->bindNodeToSlot(n, done); - generate_IteratorNextAndFriends_TrailingStuff(n, value); -} - -void GraphBuilder::generate_IteratorNextForYieldStar(int iterator, int object) -{ - // Please, PLEASE read the comment in generate_IteratorNext. - Node *n = createNode(opBuilder()->get<Meta::JSIteratorNextForYieldStar>(), - env()->accumulator(), - env()->slot(iterator), - graph()->undefinedNode()); - // Note: the following is a tiny bit different from what generate_IteratorNext does. - bindAcc(n); - generate_IteratorNextAndFriends_TrailingStuff(n, object); -} - -void GraphBuilder::generate_IteratorClose(int done) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSIteratorClose>(), - env()->accumulator(), - env()->slot(done))); -} - -void GraphBuilder::generate_DestructureRestElement() -{ - bindAcc(createNode(opBuilder()->get<Meta::JSDestructureRestElement>(), - env()->accumulator())); -} - -void GraphBuilder::generate_DeleteProperty(int base, int index) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSDeleteProperty>(), - env()->slot(base), - env()->slot(index))); -} - -void GraphBuilder::generate_DeleteName(int name) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSDeleteName>(), - createConstant(name))); -} - -void GraphBuilder::generate_TypeofName(int name) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSTypeofName>(), - createConstant(name))); -} - -void GraphBuilder::generate_TypeofValue() -{ - bindAcc(createNode(opBuilder()->get<Meta::JSTypeofValue>(), env()->accumulator())); -} - -void GraphBuilder::generate_DeclareVar(int varName, int isDeletable) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSDeclareVar>(), - createConstant(isDeletable), - createConstant(varName))); -} - -void GraphBuilder::generate_DefineArray(int argc, int argv) -{ - VarArgNodes args; - finalizeCall(Meta::JSDefineArray, args, argc, argv); -} - -void GraphBuilder::generate_DefineObjectLiteral(int internalClassId, int argc, int argv) -{ - VarArgNodes args; - args.append(createConstant(internalClassId)); - finalizeCall(Meta::JSDefineObjectLiteral, args, argc, argv); -} - -void GraphBuilder::generate_CreateClass(int classIndex, int heritage, int computedNames) -{ - int argc = 0; - int argv = computedNames; - - const QV4::CompiledData::Class *cls = function()->v4Function()->compilationUnit->unitData() - ->classAt(classIndex); - const CompiledData::Method *methods = cls->methodTable(); - for (uint i = 0; i < cls->nStaticMethods + cls->nMethods; ++i) { - if (methods[i].name == std::numeric_limits<unsigned>::max()) - ++argc; - } - - VarArgNodes args; - args.append(createConstant(classIndex)); - args.append(env()->slot(heritage)); - finalizeCall(Meta::JSCreateClass, args, argc, argv); -} - -void GraphBuilder::generate_CreateMappedArgumentsObject() -{ - bindAcc(createNode(opBuilder()->get<Meta::JSCreateMappedArgumentsObject>())); -} - -void GraphBuilder::generate_CreateUnmappedArgumentsObject() -{ - bindAcc(createNode(opBuilder()->get<Meta::JSCreateUnmappedArgumentsObject>())); -} - -void GraphBuilder::generate_CreateRestParameter(int argIndex) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSCreateRestParameter>(), - createConstant(argIndex))); -} - -void GraphBuilder::generate_ConvertThisToObject() -{ - Node* control = createNode(opBuilder()->get<Meta::JSThisToObject>(), - env()->slot(CallData::This)); - env()->bindNodeToSlot(control, CallData::This); -} - -void GraphBuilder::generate_LoadSuperConstructor() -{ - bindAcc(createNode(opBuilder()->get<Meta::JSLoadSuperConstructor>(), - env()->slot(CallData::Function))); -} - -void GraphBuilder::generate_ToObject() -{ - bindAcc(createNode(opBuilder()->get<Meta::ToObject>(), - env()->accumulator())); -} - -void GraphBuilder::generate_CallWithSpread(int func, int thisObject, int argc, int argv, - int /*traceSlot*/) -{ - VarArgNodes args; - args.append(env()->slot(func)); - args.append(env()->slot(thisObject)); - finalizeCall(Meta::JSCallWithSpread, args, argc, argv); -} - -void GraphBuilder::generate_TailCall(int func, int thisObject, int argc, int argv) -{ - VarArgNodes args; - args.append(env()->slot(func)); - args.append(env()->slot(thisObject)); - populate(args, argc, argv); - Node *n = createAndLinkNode(opBuilder()->getJSTailCall(uint16_t(args.size())), args.data(), - size_t(args.size())); - queueFunctionExit(n); -} - -void GraphBuilder::generate_Construct(int func, int argc, int argv) -{ - VarArgNodes args; - args.append(env()->slot(func)); - args.append(env()->accumulator()); - finalizeCall(Meta::JSConstruct, args, argc, argv); -} - -void GraphBuilder::generate_ConstructWithSpread(int func, int argc, int argv) -{ - VarArgNodes args; - args.append(env()->slot(func)); - args.append(env()->accumulator()); - finalizeCall(Meta::JSConstructWithSpread, args, argc, argv); -} - -void GraphBuilder::generate_Jump(int offset) -{ - auto jumpTarget = absoluteOffset(offset); - mergeIntoSuccessor(jumpTarget); -} - -void GraphBuilder::generate_JumpTrue(int /*traceSlot*/, int offset) -{ - createNode(opBuilder()->get<Meta::Branch>(), createToBoolean(env()->accumulator())); - - { - InterpreterSubEnvironment subEnvironment(this); - auto jumpTarget = absoluteOffset(offset); - createIfTrue(); - mergeIntoSuccessor(jumpTarget); - } - - createIfFalse(); -} - -void GraphBuilder::generate_JumpFalse(int traceSlot, int offset) -{ - generate_JumpFalse(env()->accumulator(), traceSlot, offset); -} - -void GraphBuilder::generate_JumpFalse(Node *condition, int /*traceSlot*/, int offset) -{ - createNode(opBuilder()->get<Meta::Branch>(), createToBoolean(condition)); - - { - InterpreterSubEnvironment subEnvironment(this); - auto jumpTarget = absoluteOffset(offset); - createIfFalse(); - mergeIntoSuccessor(jumpTarget); - } - - createIfTrue(); -} - -void GraphBuilder::generate_JumpNoException(int offset) -{ - auto e = createNode(opBuilder()->get<Meta::HasException>(), graph()->engineNode()); - createNode(opBuilder()->get<Meta::Branch>(), e); - - { - InterpreterSubEnvironment subEnvironment(this); - auto jumpTarget = absoluteOffset(offset); - createIfFalse(); - mergeIntoSuccessor(jumpTarget); - } - - createIfTrue(); -} - -void GraphBuilder::generate_JumpNotUndefined(int offset) -{ - Node *condition = createNode(opBuilder()->get<Meta::JSStrictEqual>(), - env()->accumulator(), - graph()->undefinedNode()); - generate_JumpFalse(condition, NoTraceSlot, offset); -} - -void GraphBuilder::generate_CmpEqNull() -{ - bindAcc(createNode(opBuilder()->get<Meta::JSEqual>(), - env()->accumulator(), - graph()->nullNode())); -} - -void GraphBuilder::generate_CmpNeNull() -{ - generate_CmpEqNull(); - bindAcc(createNode(opBuilder()->get<Meta::BooleanNot>(), - env()->accumulator())); -} - -void GraphBuilder::generate_CmpEqInt(int lhs) -{ - auto left = createConstant(lhs); - Node* control = createNode(opBuilder()->get<Meta::JSEqual>(), - left, - env()->accumulator()); - bindAcc(control); -} - -void GraphBuilder::generate_CmpNeInt(int lhs) -{ - generate_CmpEqInt(lhs); - bindAcc(createNode(opBuilder()->get<Meta::BooleanNot>(), - env()->accumulator())); -} - -void GraphBuilder::generate_CmpEq(int lhs) -{ - Node* control = createNode(opBuilder()->get<Meta::JSEqual>(), - env()->slot(lhs), - env()->accumulator()); - bindAcc(control); -} - -void GraphBuilder::generate_CmpNe(int lhs) -{ - generate_CmpEq(lhs); - bindAcc(createNode(opBuilder()->get<Meta::BooleanNot>(), - env()->accumulator())); -} - -void GraphBuilder::generate_CmpGt(int lhs) -{ - Node* control = createNode(opBuilder()->get<Meta::JSGreaterThan>(), - env()->slot(lhs), - env()->accumulator()); - bindAcc(control); -} - -void GraphBuilder::generate_CmpGe(int lhs) -{ - Node* control = createNode(opBuilder()->get<Meta::JSGreaterEqual>(), - env()->slot(lhs), - env()->accumulator()); - bindAcc(control); -} - -void GraphBuilder::generate_CmpLt(int lhs) -{ - Node* control = createNode(opBuilder()->get<Meta::JSLessThan>(), - env()->slot(lhs), - env()->accumulator()); - bindAcc(control); -} - -void GraphBuilder::generate_CmpLe(int lhs) -{ - Node* control = createNode(opBuilder()->get<Meta::JSLessEqual>(), - env()->slot(lhs), - env()->accumulator()); - bindAcc(control); -} - -void GraphBuilder::generate_CmpStrictEqual(int lhs) -{ - Node* control = createNode(opBuilder()->get<Meta::JSStrictEqual>(), - env()->slot(lhs), - env()->accumulator()); - bindAcc(control); -} - -void GraphBuilder::generate_CmpStrictNotEqual(int lhs) -{ - generate_CmpStrictEqual(lhs); - bindAcc(createNode(opBuilder()->get<Meta::BooleanNot>(), - env()->accumulator())); -} - -void GraphBuilder::generate_CmpIn(int lhs) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSIn>(), env()->slot(lhs), - env()->accumulator())); -} - -void GraphBuilder::generate_CmpInstanceOf(int lhs) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSInstanceOf>(), env()->slot(lhs), - env()->accumulator())); -} - -void GraphBuilder::generate_UNot() -{ - bindAcc(createNode(opBuilder()->get<Meta::BooleanNot>(), - createToBoolean(env()->accumulator()))); -} - -void GraphBuilder::generate_UPlus(int /*traceSlot*/) -{ - Node* control = createNode(opBuilder()->get<Meta::JSToNumber>(), - env()->accumulator()); - bindAcc(control); -} - -void GraphBuilder::generate_UMinus(int /*traceSlot*/) -{ - Node* control = createNode(opBuilder()->get<Meta::JSNegate>(), - env()->accumulator()); - bindAcc(control); -} - -void GraphBuilder::generate_UCompl() -{ - bindAcc(createNode(opBuilder()->get<Meta::JSBitXor>(), - env()->accumulator(), - createConstant(-1))); -} - -void GraphBuilder::generate_Increment(int /*traceSlot*/) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSAdd>(), - env()->accumulator(), - createConstant(1))); -} - - -void GraphBuilder::generate_Decrement(int /*traceSlot*/) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSSubtract>(), - env()->accumulator(), - createConstant(1))); -} - -void GraphBuilder::generate_Add(int lhs, int /*traceSlot*/) -{ - Node* control = createNode(opBuilder()->get<Meta::JSAdd>(), - env()->slot(lhs), - env()->accumulator()); - bindAcc(control); -} - -void GraphBuilder::generate_BitAnd(int lhs) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSBitAnd>(), - env()->slot(lhs), - env()->accumulator())); -} - -void GraphBuilder::generate_BitOr(int lhs) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSBitOr>(), - env()->slot(lhs), - env()->accumulator())); -} - -void GraphBuilder::generate_BitXor(int lhs) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSBitXor>(), - env()->slot(lhs), - env()->accumulator())); -} - -void GraphBuilder::generate_UShr(int lhs) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSUnsignedShiftRight>(), - env()->slot(lhs), - env()->accumulator())); -} - -void GraphBuilder::generate_Shr(int lhs) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSShiftRight>(), - env()->slot(lhs), - env()->accumulator())); -} - -void GraphBuilder::generate_Shl(int lhs) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSShiftLeft>(), - env()->slot(lhs), - env()->accumulator())); -} - - -void GraphBuilder::generate_BitAndConst(int rhs) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSBitAnd>(), - env()->accumulator(), - createConstant(rhs))); -} - -void GraphBuilder::generate_BitOrConst(int rhs) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSBitOr>(), - env()->accumulator(), - createConstant(rhs))); -} - -void GraphBuilder::generate_BitXorConst(int rhs) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSBitXor>(), - env()->accumulator(), - createConstant(rhs))); -} - -void GraphBuilder::generate_UShrConst(int rhs) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSUnsignedShiftRight>(), - env()->accumulator(), - createConstant(rhs))); -} - -void GraphBuilder::generate_ShrConst(int rhs) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSShiftRight>(), - env()->accumulator(), - createConstant(rhs))); -} - -void GraphBuilder::generate_ShlConst(int rhs) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSShiftLeft>(), - env()->accumulator(), - createConstant(rhs))); -} - -void GraphBuilder::generate_Exp(int lhs) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSExponentiate>(), - env()->slot(lhs), - env()->accumulator())); -} - -void GraphBuilder::generate_Mul(int lhs, int /*traceSlot*/) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSMultiply>(), - env()->slot(lhs), - env()->accumulator())); -} - -void GraphBuilder::generate_Div(int lhs) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSDivide>(), - env()->slot(lhs), - env()->accumulator())); -} - -void GraphBuilder::generate_Mod(int lhs, int /*traceSlot*/) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSModulo>(), - env()->slot(lhs), - env()->accumulator())); -} - -void GraphBuilder::generate_Sub(int lhs, int /*traceSlot*/) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSSubtract>(), - env()->slot(lhs), - env()->accumulator())); -} - -void GraphBuilder::generate_InitializeBlockDeadTemporalZone(int firstReg, int count) -{ - for (int reg = firstReg; reg < firstReg + count; ++reg) - env()->bindNodeToSlot(graph()->emptyNode(), reg); -} - -void GraphBuilder::generate_ThrowOnNullOrUndefined() -{ - createNode(opBuilder()->get<Meta::JSThrowOnNullOrUndefined>(), - env()->accumulator()); -} - -void GraphBuilder::generate_GetTemplateObject(int index) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSGetTemplateObject>(), - createConstant(index))); -} - -GraphBuilder::Verdict GraphBuilder::startInstruction(Moth::Instr::Type /*instr*/) -{ - // This handles a couple of cases on how flow control can end up at this instruction. - - const auto off = currentInstructionOffset(); - if (auto newEnv = m_envForOffset[off]) { - // Ok, there was a jump from before to this point (which registered an environment), so we - // have two options: - if (env() != newEnv && env() != nullptr) { - // There is a current environment different from the environment active when we took the - // jump. This happens with e.g. an if-then-else: - // - // acc = condition - // JumpFalse else-block - // ... then block - // Jump end-if - // else-block: - // ... else block - // end-if: - // .. some instruction <--- we're here - // - // in that case we merge the after-else environment into the after-then environment: - newEnv->merge(env()); - } else { - // There is not a current environment. This can happen with e.g. a loop: - // loop-start: - // acc = condition - // JumpFalse loop-end - // ... loop body - // Jump loop-start - // loop-end: - // .... some instruction <--- we're here - // - // The last jump of the loop will clear the environment, so at this point we only have - // the environment registered by the JumpFalse. This is the asy case: no merges, just - // take the registered environment unchanged. - } - - // Leave the merged environment as-is, and continue with a copy. We cannot change the - // registered environment in case this point also happens to be a loop start. - setEnv(newEnv->copy()); - } - - if (env() == nullptr) { - // Ok, there is no environment, meaning nobody jumped to this instruction, and the previous - // instruction doesn't let control flow end up here. So, this is dead code. - // This can happen for JS like: - // - // if (condition) { - // return something - // } else { - // return somethingElse - // } - // someCode <--- we're here - return SkipInstruction; - } - - const LabelInfo *info = isLoopStart(off); - if (info && env()) { - // Ok, this instruction is the start of a loop, meaning there will be a jump backwards to - // this point. Make sure there is a Region node with Phi nodes here. - handleLoopStart(*info); - } - - return ProcessInstruction; -} - -void GraphBuilder::endInstruction(Moth::Instr::Type /*instr*/) {} - -} // IR namespace -} // QV4 namespace -QT_END_NAMESPACE diff --git a/src/qml/jit/qv4graphbuilder_p.h b/src/qml/jit/qv4graphbuilder_p.h deleted file mode 100644 index 450d8640b7..0000000000 --- a/src/qml/jit/qv4graphbuilder_p.h +++ /dev/null @@ -1,298 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QV4GRAPHBUILDER_P_H -#define QV4GRAPHBUILDER_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qv4global_p.h> -#include <private/qv4bytecodehandler_p.h> -#include <private/qv4ir_p.h> -#include "qv4graph_p.h" - -QT_REQUIRE_CONFIG(qml_tracing); - -QT_BEGIN_NAMESPACE - -namespace QV4 { -namespace IR { - -// The graph builder walks the byte-code, and produces a graph. The graph is a digraph, where the -// nodes have operations, and the edges are dependencies (or inputs). -class GraphBuilder: protected Moth::ByteCodeHandler -{ - Q_DISABLE_COPY_MOVE(GraphBuilder) - - enum { NoTraceSlot = -1 }; - - struct LabelInfo { //### extend this to also capture the amount of slots that are live - LabelInfo() = default; - LabelInfo(unsigned label) : labelOffset(label) {} - unsigned labelOffset = 0; - }; - -public: - static void buildGraph(IR::Function *function); - - class InterpreterEnvironment; - - void setEnv(InterpreterEnvironment *newEnv) - { m_currentEnv = newEnv; } - - InterpreterEnvironment *env() const - { return m_currentEnv; } - -private: - GraphBuilder(IR::Function *function); - ~GraphBuilder() override = default; - - void startGraph(); - void endGraph(); - - Node *bindAcc(Node *n); - Node *createAndLinkNode(Operation *op, Node *operands[], size_t opCount, bool incomplete = false); - Node *createNode(Operation *op, bool incomplete = false); - Node *createNode(Operation *op, Node *n1); - Node *createNode(Operation *op, Node *n1, Node *n2); - Node *createNode(Operation *op, Node *n1, Node *n2, Node *n3); - Node *createNode(Operation *op, Node *n1, Node *n2, Node *n3, Node *n4); - Node *createRegion(unsigned nControlInputs); - Node *createIfTrue(); - Node *createIfFalse(); - Node *createConstant(int v); - Node *createPhi(unsigned nInputs, Node *input, Node *control); - Node *createEffectPhi(unsigned nInputs, Node *input, Node *control); - Node *createHandleUnwind(int offset); - Node *mergeControl(Node *c1, Node *c2); - Node *mergeEffect(Node *e1, Node *e2, Node *control); - Node *mergeValue(Node *v1, Node *v2, Node *control); - - Node *createToBoolean(Node *input); - - using VarArgNodes = QVarLengthArray<Node *, 32>; - void populate(VarArgNodes &args, int argc, int argv); - - void queueFunctionExit(Node *exitNode); - - Function *function() const - { return m_func; } - - Graph *graph() - { return m_graph; } - - Node *mergeIntoSuccessor(int offset); - - OperationBuilder *opBuilder() const - { return m_graph->opBuilder(); } - - int absoluteOffset(int offset) const - { return offset + nextInstructionOffset(); } - - const LabelInfo *labelInfoAt(unsigned offset) const; - const LabelInfo *isLoopStart(unsigned offset) const; - void handleLoopStart(const LabelInfo &labelInfo); - void startUnwinding(); - -protected: // ByteCodeHandler - void generate_Ret() override; - void generate_Debug() override; - void generate_LoadConst(int index) override; - void generate_LoadZero() override; - void generate_LoadTrue() override; - void generate_LoadFalse() override; - void generate_LoadNull() override; - void generate_LoadUndefined() override; - void generate_LoadInt(int value) override; - void generate_MoveConst(int constIndex, int destTemp) override; - void generate_LoadReg(int reg) override; - void generate_StoreReg(int reg) override; - void generate_MoveReg(int srcReg, int destReg) override; - void generate_LoadImport(int index) override; - void generate_LoadLocal(int index, int traceSlot) override; - void generate_StoreLocal(int index) override; - void generate_LoadScopedLocal(int scope, int index, int traceSlot) override; - void generate_StoreScopedLocal(int scope, int index) override; - void generate_LoadRuntimeString(int stringId) override; - void generate_MoveRegExp(int regExpId, int destReg) override; - void generate_LoadClosure(int value) override; - void generate_LoadName(int name, int traceSlot) override; - void generate_LoadGlobalLookup(int index, int traceSlot) override; - void generate_StoreNameSloppy(int name) override; - void generate_StoreNameStrict(int name) override; - void generate_LoadElement(int base, int traceSlot) override; - void generate_StoreElement(int base, int index, int traceSlot) override; - void generate_LoadProperty(int name, int traceSlot) override; - void generate_GetLookup(int index, int traceSlot) override; - void generate_StoreProperty(int name, int base) override; - void generate_SetLookup(int index, int base) override; - void generate_LoadSuperProperty(int property) override; - void generate_StoreSuperProperty(int property) override; - void generate_LoadQmlContextPropertyLookup(int property, int traceSlot) override; - void generate_Yield() override; - void generate_YieldStar() override; - void generate_Resume(int offset) override; - void finalizeCall(Operation::Kind kind, VarArgNodes &args, int argc, int argv); - void generate_CallValue(int name, int argc, int argv, int traceSlot) override; - void generate_CallWithReceiver(int name, int thisObject, int argc, int argv, - int traceSlot) override; - void generate_CallProperty(int name, int base, int argc, int argv, int traceSlot) override; - void generate_CallPropertyLookup(int lookupIndex, int base, int argc, int argv, - int traceSlot) override; - void generate_CallElement(int base, int index, int argc, int argv, int traceSlot) override; - void generate_CallName(int name, int argc, int argv, int traceSlot) override; - void generate_CallPossiblyDirectEval(int argc, int argv, int traceSlot) override; - void generate_CallGlobalLookup(int index, int argc, int argv, int traceSlot) override; - void generate_CallQmlContextPropertyLookup(int index, int argc, int argv, int traceSlot) override; - void generate_SetUnwindHandler(int offset) override; - void generate_UnwindDispatch() override; - void generate_UnwindToLabel(int level, int offset) override; - void generate_DeadTemporalZoneCheck(int name) override; - void generate_ThrowException() override; - void generate_GetException() override; - void generate_SetException() override; - void generate_CreateCallContext() override; - void generate_PushCatchContext(int index, int name) override; - void generate_PushWithContext() override; - void generate_PushBlockContext(int index) override; - void generate_CloneBlockContext() override; - void generate_PushScriptContext(int index) override; - void generate_PopScriptContext() override; - void generate_PopContext() override; - void generate_GetIterator(int iterator) override; - void generate_IteratorNextAndFriends_TrailingStuff(Node *iterationNode, int resultSlot); - void generate_IteratorNext(int value, int done) override; - void generate_IteratorNextForYieldStar(int iterator, int object) override; - void generate_IteratorClose(int done) override; - void generate_DestructureRestElement() override; - void generate_DeleteProperty(int base, int index) override; - void generate_DeleteName(int name) override; - void generate_TypeofName(int name) override; - void generate_TypeofValue() override; - void generate_DeclareVar(int varName, int isDeletable) override; - void generate_DefineArray(int argc, int argv) override; - void generate_DefineObjectLiteral(int internalClassId, int argc, int argv) override; - void generate_CreateClass(int classIndex, int heritage, int computedNames) override; - void generate_CreateMappedArgumentsObject() override; - void generate_CreateUnmappedArgumentsObject() override; - void generate_CreateRestParameter(int argIndex) override; - void generate_ConvertThisToObject() override; - void generate_LoadSuperConstructor() override; - void generate_ToObject() override; - void generate_CallWithSpread(int func, int thisObject, int argc, int argv, - int traceSlot) override; - void generate_TailCall(int func, int thisObject, int argc, int argv) override; - void generate_Construct(int func, int argc, int argv) override; - void generate_ConstructWithSpread(int func, int argc, int argv) override; - void generate_Jump(int offset) override; - void generate_JumpTrue(int traceSlot, int offset) override; - void generate_JumpFalse(int traceSlot, int offset) override; - void generate_JumpFalse(Node *condition, int traceSlot, int offset); - void generate_JumpNoException(int offset) override; - void generate_JumpNotUndefined(int offset) override; - void generate_CmpEqNull() override; - void generate_CmpNeNull() override; - void generate_CmpEqInt(int lhs) override; - void generate_CmpNeInt(int lhs) override; - void generate_CmpEq(int lhs) override; - void generate_CmpNe(int lhs) override; - void generate_CmpGt(int lhs) override; - void generate_CmpGe(int lhs) override; - void generate_CmpLt(int lhs) override; - void generate_CmpLe(int lhs) override; - void generate_CmpStrictEqual(int lhs) override; - void generate_CmpStrictNotEqual(int lhs) override; - void generate_CmpIn(int lhs) override; - void generate_CmpInstanceOf(int lhs) override; - void generate_UNot() override; - void generate_UPlus(int traceSlot) override; - void generate_UMinus(int traceSlot) override; - void generate_UCompl() override; - void generate_Increment(int traceSlot) override; - void generate_Decrement(int traceSlot) override; - void generate_Add(int lhs, int traceSlot) override; - void generate_BitAnd(int lhs) override; - void generate_BitOr(int lhs) override; - void generate_BitXor(int lhs) override; - void generate_UShr(int lhs) override; - void generate_Shr(int lhs) override; - void generate_Shl(int lhs) override; - void generate_BitAndConst(int rhs) override; - void generate_BitOrConst(int rhs) override; - void generate_BitXorConst(int rhs) override; - void generate_UShrConst(int rhs) override; - void generate_ShrConst(int rhs) override; - void generate_ShlConst(int rhs) override; - void generate_Exp(int lhs) override; - void generate_Mul(int lhs, int traceSlot) override; - void generate_Div(int lhs) override; - void generate_Mod(int lhs, int traceSlot) override; - void generate_Sub(int lhs, int traceSlot) override; - void generate_InitializeBlockDeadTemporalZone(int firstReg, int count) override; - void generate_ThrowOnNullOrUndefined() override; - void generate_GetTemplateObject(int index) override; - - Verdict startInstruction(Moth::Instr::Type instr) override; - void endInstruction(Moth::Instr::Type instr) override; - -private: - IR::Function *m_func; - Graph *m_graph; - InterpreterEnvironment *m_currentEnv; - std::vector<Node *> m_exitControls; - QHash<int, InterpreterEnvironment *> m_envForOffset; - std::vector<LabelInfo> m_labelInfos; - int m_currentUnwindHandlerOffset = 0; -}; - -} // namespace IR -} // namespace QV4 - -QT_END_NAMESPACE - -#endif // QV4GRAPHBUILDER_P_H diff --git a/src/qml/jit/qv4ir.cpp b/src/qml/jit/qv4ir.cpp deleted file mode 100644 index cb3eeeec60..0000000000 --- a/src/qml/jit/qv4ir.cpp +++ /dev/null @@ -1,382 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <private/qqmlglobal_p.h> -#include "qv4ir_p.h" -#include "qv4node_p.h" -#include "qv4function_p.h" -#include <qv4graph_p.h> -#include "qv4stackframe_p.h" -#include "qv4operation_p.h" -#include "qv4util_p.h" - -#include <QtCore/qloggingcategory.h> -#include <QtCore/qjsonobject.h> -#include <QtCore/qjsonarray.h> -#include <QtCore/qfile.h> - -QT_BEGIN_NAMESPACE -namespace QV4 { -namespace IR { - -Q_LOGGING_CATEGORY(lcJsonIR, "qt.v4.ir.json"); -Q_LOGGING_CATEGORY(lcDotIR, "qt.v4.ir.dot"); -Q_LOGGING_CATEGORY(lcVerify, "qt.v4.ir.verify"); - -Function::Function(QV4::Function *qv4Function) - : qv4Function(qv4Function) - , m_graph(Graph::create(this)) - , m_dumper(nullptr) - , m_nodeInfo(128, nullptr) -{ -} - -Function::~Function() -{ - delete m_dumper; -} - -QString Function::name() const -{ - QString name; - if (auto n = v4Function()->name()) - name = n->toQString(); - if (name.isEmpty()) - name = QString::asprintf("%p", v4Function()); - auto loc = v4Function()->sourceLocation(); - return name + QStringLiteral(" (%1:%2:%3)").arg(loc.sourceFile, QString::number(loc.line), - QString::number(loc.column)); -} - -void Function::dump(const QString &description) const -{ - Dumper::dump(this, description); -} - -void Function::dump() const -{ - dump(QStringLiteral("Debug:")); -} - -Dumper *Function::dumper() const -{ - if (!m_dumper) - m_dumper = new Dumper(this); - return m_dumper; -} - -Function::StringId Function::addString(const QString &s) -{ - m_stringPool.push_back(s); - return m_stringPool.size() - 1; -} - -NodeInfo *Function::nodeInfo(Node *n, bool createIfNecessary) const -{ - if (n->id() >= m_nodeInfo.size()) - m_nodeInfo.resize(n->id() * 2, nullptr); - - NodeInfo *&info = m_nodeInfo[n->id()]; - if (info == nullptr && createIfNecessary) { - info = m_pool.New<NodeInfo>(); - info->setType(n->operation()->type()); - } - return info; -} - -void Function::copyBytecodeOffsets(Node *from, Node *to) -{ - auto toInfo = nodeInfo(to); - if (auto fromInfo = nodeInfo(from)) { - toInfo->setBytecodeOffsets(fromInfo->currentInstructionOffset(), - fromInfo->nextInstructionOffset()); - } -} - -Dumper::Dumper(const Function *f) -{ - if (!f) - return; -} - -void Dumper::dump(const Function *f, const QString &description) -{ - if (false && lcJsonIR().isDebugEnabled()) { - Dumper *dumper = f->dumper(); - - qCDebug(lcJsonIR).noquote().nospace() << description + QLatin1String(":\n"); - for (const auto &line : dumper->dump(f).split('\n')) - qCDebug(lcJsonIR).noquote().nospace() << line; - } - - if (lcDotIR().isDebugEnabled()) - dot(f, description); -} - -QByteArray Dumper::dump(const Function *f) -{ - QJsonObject fo; - - { - QString name; - if (auto n = f->v4Function()->name()) - name = n->toQString(); - fo[QLatin1String("_searchKey")] = QStringLiteral("function %1").arg(name); - if (name.isEmpty()) - name = QString::asprintf("%p", f->v4Function()); - fo[QLatin1String("name")] = name; - } - - auto loc = f->v4Function()->sourceLocation(); - fo[QLatin1String("source")] = loc.sourceFile; - fo[QLatin1String("line")] = loc.line; - fo[QLatin1String("column")] = loc.column; - - { - QJsonArray gn; - QJsonArray ge; - NodeCollector nodes(f->graph(), /*collectUses =*/ true); - nodes.sortById(); - for (Node *n : nodes.reachable()) { - gn.append(dump(n, f)); - int inputIndex = 0; - for (Node *input : n->inputs()) { - QJsonObject edge; - edge[QLatin1String("from")] = int(input->id()); - edge[QLatin1String("to")] = int(n->id()); - edge[QLatin1String("index")] = inputIndex; - if (inputIndex < n->operation()->valueInputCount()) { - edge[QLatin1String("type")] = QLatin1String("value"); - } else if (inputIndex < n->operation()->valueInputCount() - + n->operation()->effectInputCount()) { - edge[QLatin1String("type")] = QLatin1String("effect"); - } else { - edge[QLatin1String("type")] = QLatin1String("control"); - } - Q_ASSERT(inputIndex < n->operation()->valueInputCount() - + n->operation()->effectInputCount() - + n->operation()->controlInputCount()); - ge.append(edge); - ++inputIndex; - } - } - QJsonObject g; - g[QLatin1String("nodes")] = gn; - g[QLatin1String("edges")] = ge; - fo[QLatin1String("graph")] = g; - } - - m_doc.setObject(fo); - return m_doc.toJson(QJsonDocument::Indented); -} - -QJsonValue toJSonValue(QV4::Value v) -{ - switch (v.type()) { - case QV4::Value::Undefined_Type: return QJsonValue(QJsonValue::Undefined); - case QV4::Value::Null_Type: return QJsonValue(QJsonValue::Null); - case QV4::Value::Boolean_Type: return QJsonValue(v.booleanValue()); - case QV4::Value::Integer_Type: return QJsonValue(v.int_32()); - case QV4::Value::Managed_Type: - if (String *s = v.stringValue()) - return QJsonValue(s->toQString()); - else - return QJsonValue(QLatin1String("<managed>")); - default: return QJsonValue(v.doubleValue()); - } -} - -QJsonValue Dumper::dump(const Node * const node, const Function *f) -{ - QJsonObject n; - n[QLatin1String("id")] = int(node->id()); - n[QLatin1String("kind")] = node->operation()->debugString(); - switch (node->operation()->kind()) { - case Meta::Parameter: { - auto info = ParameterPayload::get(*node->operation()); - n[QLatin1String("name")] = f->string(info->stringId()); - n[QLatin1String("index")] = int(info->parameterIndex()); - break; - } - case Meta::Constant: { - auto info = ConstantPayload::get(*node->operation()); - n[QLatin1String("value")] = toJSonValue(info->value()); - break; - } - default: - break; - } - return n; -} - -void Dumper::dot(const Function *f, const QString &description) -{ - static const bool skipFramestate = qEnvironmentVariableIsSet("QV4_JIT_DOT_SKIP_FRAMESTATE"); - - auto node = [](Node *n) { - return QStringLiteral("n%1[label=\"%1: %2%3\"];\n").arg(QString::number(n->id()), - n->operation()->debugString(), - n->isDead() ? QStringLiteral(" (dead)") - : QString()); - }; - - Graph *g = f->graph(); - QString out; - out += QLatin1Char('\n'); - out += QStringLiteral("digraph{root=\"n%1\" label=\"%2\";" - "node[shape=rect];" - "edge[dir=back fontsize=10];\n") - .arg(g->startNode()->id()) - .arg(description); - out += node(g->startNode()); - const bool dumpUses = false; // set to true to see all nodes - NodeCollector nodes(g, dumpUses, skipFramestate); - for (Node *n : nodes.reachable()) { - if (n == g->startNode()) - continue; - - out += node(n); - - unsigned inputIndex = 0; - for (Node *input : n->inputs()) { - if (input == nullptr) - continue; - out += QStringLiteral("n%2->n%1[style=").arg(QString::number(n->id()), - QString::number(input->id())); - if (inputIndex < n->operation()->valueInputCount() || - inputIndex == n->operation()->indexOfFrameStateInput()) { - out += QStringLiteral("solid headlabel=\"%1\"").arg(inputIndex); - } else if (inputIndex < unsigned(n->operation()->valueInputCount() - + n->operation()->effectInputCount())) { - out += QStringLiteral("dotted headlabel=\"%1\"").arg(inputIndex); - } else { - out += QStringLiteral("dashed headlabel=\"%1\"").arg(inputIndex); - } - out += QStringLiteral("];\n"); - ++inputIndex; - } - } - out += QStringLiteral("}\n"); - qCDebug(lcDotIR).nospace().noquote() << out; - - QFile of(description + QStringLiteral(".dot")); - of.open(QIODevice::WriteOnly); - of.write(out.toUtf8()); - of.close(); -} - -void Function::verify() const -{ -#ifndef QT_NO_DEBUG - unsigned problemsFound = 0; - - auto verifyNodeAgainstOperation = [&problemsFound](const Node *n) { - const Operation *op = n->operation(); - if (op->totalInputCount() != n->inputCount()) { - ++problemsFound; - qCDebug(lcVerify()) << "Node" << n->id() << "has" << n->inputCount() - << "inputs, but it's operation" << op->debugString() - << "requires" << op->totalInputCount() << "inputs"; - } - - if (n->opcode() == Meta::Phi || n->opcode() == Meta::EffectPhi) { - if (n->controlInput()->opcode() != Meta::Region) { - ++problemsFound; - qCDebug(lcVerify()) << "Control input of phi node" << n->id() << "is not a region"; - } - if (n->controlInput()->inputCount() + 1 != n->inputCount()) { - ++problemsFound; - qCDebug(lcVerify()) << "Control input of phi node" << n->id() - << "has" << n->controlInput()->inputCount() - << "inputs while phi node has" << n->inputCount() - << "inputs"; - } - } - - //### todo: verify outputs: value outputs are allowed to be unused, but the effect and - // control outputs have to be linked up, except: - //### todo: verify if no use is a nullptr, except for operations that can throw, where the - // last one is allowed to be a nullptr when an unwind handler is missing. - }; - - NodeWorkList todo(graph()); - todo.enqueue(graph()->endNode()); - while (Node *n = todo.dequeueNextNodeForVisiting()) { - todo.enqueueAllInputs(n); - todo.enqueueAllUses(n); - - verifyNodeAgainstOperation(n); - } - //### TODO: - if (problemsFound != 0) { - dump(QStringLiteral("Problematic graph")); - qFatal("Found %u problems during graph verification!", problemsFound); - } -#endif // QT_NO_xDEBUG -} - -QString Type::debugString() const -{ - if (isNone()) - return QStringLiteral("none"); - if (isInvalid()) - return QStringLiteral("invalid"); - - QStringList s; - if (m_t & Bool) - s += QStringLiteral("boolean"); - if (m_t & Int32) - s += QStringLiteral("int32"); - if (m_t & Double) - s += QStringLiteral("double"); - if (m_t & Undefined) - s += QStringLiteral("undefined"); - if (m_t & Null) - s += QStringLiteral("null"); - if (m_t & Empty) - s += QStringLiteral("empty"); - if (m_t & RawPointer) - s += QStringLiteral("raw pointer"); - - return s.join(QLatin1String(" ")); -} - -} // IR namespace -} // QV4 namespace -QT_END_NAMESPACE diff --git a/src/qml/jit/qv4ir_p.h b/src/qml/jit/qv4ir_p.h deleted file mode 100644 index e21a80528d..0000000000 --- a/src/qml/jit/qv4ir_p.h +++ /dev/null @@ -1,228 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QV4IR_P_H -#define QV4IR_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qv4function_p.h> -#include <QtCore/qjsondocument.h> - -QT_REQUIRE_CONFIG(qml_tracing); - -QT_BEGIN_NAMESPACE - -namespace QV4 { -namespace IR { - -class Dumper; -class Graph; - -class Node; -class NodeInfo; - -class Function -{ - Q_DISABLE_COPY_MOVE(Function) -public: - Function(QV4::Function *qv4Function); - ~Function(); - - void verify() const; - - QV4::Function *v4Function() const - { return qv4Function; } - - QString name() const; - - QQmlJS::MemoryPool *pool() - { return &m_pool; } - - Graph *graph() const - { return m_graph; } - - void dump(const QString &description) const; - void dump() const; // for calling in the debugger - Dumper *dumper() const; - - using StringId = size_t; - StringId addString(const QString &s); - QString string(StringId id) const - { return m_stringPool[id]; } - - NodeInfo *nodeInfo(Node *n, bool createIfNecessary = true) const; - void copyBytecodeOffsets(Node *from, Node *to); - - void addUnwindLabelOffset(int absoluteOffset) - { m_unwindLabelOffsets.push_back(absoluteOffset); } - - const std::vector<int> &unwindLabelOffsets() const - { return m_unwindLabelOffsets; } - -private: - QV4::Function *qv4Function; - mutable QQmlJS::MemoryPool m_pool; - Graph *m_graph; - mutable Dumper *m_dumper; - std::vector<QString> m_stringPool; - mutable std::vector<NodeInfo *> m_nodeInfo; //### move the into the _pool - std::vector<int> m_unwindLabelOffsets; -}; - -class Dumper -{ - Q_DISABLE_COPY_MOVE(Dumper) - -public: - Dumper(const Function *f); - ~Dumper() = default; - - static void dump(const Function *f, const QString &description); - static void dot(const Function *f, const QString &description); - -private: - QByteArray dump(const Function *f); - QJsonValue dump(const Node *node, const Function *f); - -private: - QJsonDocument m_doc; -}; - -class Type -{ - // None is for nodes with no type (e.g. a Return) - // The others form a lattice: - // Any -> Object -> Invalid - // ^^^ -> Number -> Integral -> Int32 -> ^^^^^^^ - // ^^^ -> Number -> Integral -> UInt32 -> ^^^^^^^ - // ^^^ -> Number -> Integral -> Bool -> ^^^^^^^ - // ^^^ -> Number -> Double -> ^^^^^^^ - // ^^^ -> Undefined -> ^^^^^^^ - // ^^^ -> Null -> ^^^^^^^ - // ^^^ -> Empty -> ^^^^^^^ - enum InternalType: int16_t { - None = 0, - - Object = 1 << 0, - Bool = 1 << 1, - Int32 = 1 << 2, - UInt32 = 1 << 3, - Double = 1 << 4, - Undefined = 1 << 5, - Null = 1 << 6, - Empty = 1 << 7, - RawPointer = 1 << 8, - Invalid = -1, - - Integral = Int32 | UInt32 | Bool, - Number = Integral | Double, - Any = Object | Number | Undefined | Empty | Null, - }; - - Type(InternalType t) : m_t(t) {} - -public: - Type() = default; - - bool operator==(const Type &other) const - { return m_t == other.m_t; } - - static Type noneType() { return Type(None); } - static Type anyType() { return Type(Any); } - static Type undefinedType() { return Type(Undefined); } - static Type emptyType() { return Type(Empty); } - static Type booleanType() { return Type(Bool); } - static Type int32Type() { return Type(Int32); } - static Type doubleType() { return Type(Double); } - static Type numberType() { return Type(Number); } - static Type nullType() { return Type(Null); } - static Type objectType() { return Type(Object); } - static Type rawPointerType() { return Type(RawPointer); } - - bool isAny() const { return m_t == Any; } - bool isBoolean() const { return m_t == Bool; } - bool isInt32() const { return m_t == Int32; } - bool isInvalid() const { return m_t == Invalid; } - bool isNone() const { return m_t == None; } - bool isDouble() const { return m_t == Double; } - bool isUndefined() const { return m_t == Undefined; } - bool isNull() const { return m_t == Null; } - bool isEmpty() const { return m_t == Empty; } - bool isObject() const { return m_t == Object; } - bool isRawPointer() const { return m_t == RawPointer; } - bool isIntegral() const { return matches(Integral); } - bool isNumber() const { return matches(Number); } - - Type operator|(Type other) const - { return Type(InternalType(int16_t(m_t) | int16_t(other.m_t))); } - - Type &operator|=(Type other) - { - m_t = (InternalType(int16_t(m_t) | int16_t(other.m_t))); - return *this; - } - - QString debugString() const; - -private: - bool matches(InternalType it) const - { - return (m_t & ~it) == 0 && (m_t & it) != 0; - } - -private: - InternalType m_t = None; -}; - -} // namespace IR -} // namespace QV4 - -QT_END_NAMESPACE - -#endif // QV4IR_P_H diff --git a/src/qml/jit/qv4loopinfo.cpp b/src/qml/jit/qv4loopinfo.cpp deleted file mode 100644 index 0366c49e30..0000000000 --- a/src/qml/jit/qv4loopinfo.cpp +++ /dev/null @@ -1,199 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <QtCore/qloggingcategory.h> - -#include "qv4loopinfo_p.h" -#include "qv4domtree_p.h" - -QT_BEGIN_NAMESPACE -namespace QV4 { -namespace IR { - -Q_LOGGING_CATEGORY(lcLoopinfo, "qt.v4.ir.loopinfo") - -void LoopInfo::detectLoops() -{ - blockInfos.resize(dt.function()->blockCount()); - - std::vector<MIBlock *> backedges; - backedges.reserve(4); - - const auto order = dt.calculateDFNodeIterOrder(); - for (MIBlock *bb : order) { - if (bb->isDeoptBlock()) - continue; - - backedges.clear(); - - for (MIBlock *pred : bb->inEdges()) { - if (bb == pred || dt.dominates(bb->index(), pred->index())) - backedges.push_back(pred); - } - - if (!backedges.empty()) - subLoop(bb, backedges); - } - - collectLoopExits(); - - dump(); -} - -void LoopInfo::collectLoopExits() -{ - for (MIBlock::Index i = 0, ei = MIBlock::Index(blockInfos.size()); i != ei; ++i) { - BlockInfo &bi = blockInfos[i]; - MIBlock *currentBlock = dt.function()->block(i); - if (bi.isLoopHeader) { - for (MIBlock *outEdge : currentBlock->outEdges()) { - if (outEdge != currentBlock && !inLoopOrSubLoop(outEdge, currentBlock)) - bi.loopExits.push_back(outEdge); - } - } - if (MIBlock *containingLoop = bi.loopHeader) { - BlockInfo &loopInfo = blockInfos[containingLoop->index()]; - for (MIBlock *outEdge : currentBlock->outEdges()) { - if (outEdge != containingLoop && !inLoopOrSubLoop(outEdge, containingLoop)) - loopInfo.loopExits.push_back(outEdge); - } - } - } -} - -bool LoopInfo::inLoopOrSubLoop(MIBlock *block, MIBlock *loopHeader) const -{ - const BlockInfo &bi = blockInfos[block->index()]; - MIBlock *loopHeaderForBlock = bi.loopHeader; - if (loopHeaderForBlock == nullptr) - return false; // block is not in any loop - - while (loopHeader) { - if (loopHeader == loopHeaderForBlock) - return true; - // look into the parent loop of loopHeader to see if block is contained there - loopHeader = blockInfos[loopHeader->index()].loopHeader; - } - - return false; -} - -void LoopInfo::subLoop(MIBlock *loopHead, const std::vector<MIBlock *> &backedges) -{ - blockInfos[loopHead->index()].isLoopHeader = true; - - std::vector<MIBlock *> worklist; - worklist.reserve(backedges.size() + 8); - worklist.insert(worklist.end(), backedges.begin(), backedges.end()); - while (!worklist.empty()) { - MIBlock *predIt = worklist.back(); - worklist.pop_back(); - - MIBlock *subloop = blockInfos[predIt->index()].loopHeader; - if (subloop) { - // This is a discovered block. Find its outermost discovered loop. - while (MIBlock *parentLoop = blockInfos[subloop->index()].loopHeader) - subloop = parentLoop; - - // If it is already discovered to be a subloop of this loop, continue. - if (subloop == loopHead) - continue; - - // Yay, it's a subloop of this loop. - blockInfos[subloop->index()].loopHeader = loopHead; - predIt = subloop; - - // Add all predecessors of the subloop header to the worklist, as long as - // those predecessors are not in the current subloop. It might be the case - // that they are in other loops, which we will then add as a subloop to the - // current loop. - for (MIBlock *predIn : predIt->inEdges()) - if (blockInfos[predIn->index()].loopHeader != subloop) - worklist.push_back(predIn); - } else { - if (predIt == loopHead) - continue; - - // This is an undiscovered block. Map it to the current loop. - blockInfos[predIt->index()].loopHeader = loopHead; - - // Add all incoming edges to the worklist. - for (MIBlock *bb : predIt->inEdges()) - worklist.push_back(bb); - } - } -} - -void LoopInfo::dump() const -{ - if (!lcLoopinfo().isDebugEnabled()) - return; - - QString s = QStringLiteral("Loop information:\n"); - for (size_t i = 0, ei = blockInfos.size(); i != ei; ++i) { - const BlockInfo &bi = blockInfos[i]; - s += QStringLiteral(" %1 : is loop header: %2, contained in loop header's loop: ") - .arg(i).arg(bi.isLoopHeader ? QLatin1String("yes") : QLatin1String("no")); - if (bi.loopHeader) - s += QString::number(bi.loopHeader->index()); - else - s += QLatin1String("<none>"); - if (bi.isLoopHeader) { - s += QStringLiteral(", loop exits: "); - if (bi.loopExits.empty()) { - s += QLatin1String("<none>"); - } else { - bool first = true; - for (MIBlock *exit : bi.loopExits) { - if (first) - first = false; - else - s += QStringLiteral(", "); - s += QString::number(exit->index()); - } - } - } - s += QLatin1Char('\n'); - } - qCDebug(lcLoopinfo).noquote().nospace() << s; -} - -} // IR namespace -} // QV4 namespace -QT_END_NAMESPACE diff --git a/src/qml/jit/qv4loopinfo_p.h b/src/qml/jit/qv4loopinfo_p.h deleted file mode 100644 index 6a865e6dc6..0000000000 --- a/src/qml/jit/qv4loopinfo_p.h +++ /dev/null @@ -1,159 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QV4LOOPINFO_P_H -#define QV4LOOPINFO_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qv4mi_p.h" - -#include <QHash> - -QT_REQUIRE_CONFIG(qml_tracing); - -QT_BEGIN_NAMESPACE - -namespace QV4 { -namespace IR { - -class DominatorTree; - -// Detect all (sub-)loops in a function. -// -// Doing loop detection on the CFG is better than relying on the statement information in -// order to mark loops. Although JavaScript only has natural loops, it can still be the case -// that something is not a loop even though a loop-like-statement is in the source. For -// example: -// while (true) { -// if (i > 0) -// break; -// else -// break; -// } -// -// Algorithm: -// - do a DFS on the dominator tree, where for each node: -// - collect all back-edges -// - if there are back-edges, the node is a loop-header for a new loop, so: -// - walk the CFG is reverse-direction, and for every node: -// - if the node already belongs to a loop, we've found a nested loop: -// - get the loop-header for the (outermost) nested loop -// - add that loop-header to the current loop -// - continue by walking all incoming edges that do not yet belong to the current loop -// - if the node does not belong to a loop yet, add it to the current loop, and -// go on with all incoming edges -// -// Loop-header detection by checking for back-edges is very straight forward: a back-edge is -// an incoming edge where the other node is dominated by the current node. Meaning: all -// execution paths that reach that other node have to go through the current node, that other -// node ends with a (conditional) jump back to the loop header. -// -// The exact order of the DFS on the dominator tree is not important. The only property has to -// be that a node is only visited when all the nodes it dominates have been visited before. -// The reason for the DFS is that for nested loops, the inner loop's loop-header is dominated -// by the outer loop's header. So, by visiting depth-first, sub-loops are identified before -// their containing loops, which makes nested-loop identification free. An added benefit is -// that the nodes for those sub-loops are only processed once. -// -// Note: independent loops that share the same header are merged together. For example, in -// the code snippet below, there are 2 back-edges into the loop-header, but only one single -// loop will be detected. -// while (a) { -// if (b) -// continue; -// else -// continue; -// } -class LoopInfo -{ - Q_DISABLE_COPY_MOVE(LoopInfo) - - struct BlockInfo - { - MIBlock *loopHeader = nullptr; - bool isLoopHeader = false; - std::vector<MIBlock *> loopExits; - }; - -public: - LoopInfo(const DominatorTree &dt) - : dt(dt) - {} - - ~LoopInfo() = default; - - void detectLoops(); - - MIBlock *loopHeaderFor(MIBlock *bodyBlock) const - { return blockInfos[bodyBlock->index()].loopHeader; } - - bool isLoopHeader(MIBlock *block) const - { return blockInfos[block->index()].isLoopHeader; } - - const std::vector<MIBlock *> loopExitsForLoop(MIBlock *loopHeader) const - { return blockInfos[loopHeader->index()].loopExits; } - -private: - void subLoop(MIBlock *loopHead, const std::vector<MIBlock *> &backedges); - void collectLoopExits(); - bool inLoopOrSubLoop(MIBlock *block, MIBlock *loopHeader) const; - - void dump() const; - -private: - const DominatorTree &dt; - std::vector<BlockInfo> blockInfos; -}; - -} // namespace IR -} // namespace QV4 - -QT_END_NAMESPACE - -#endif // QV4LOOPINFO_P_H diff --git a/src/qml/jit/qv4lowering.cpp b/src/qml/jit/qv4lowering.cpp deleted file mode 100644 index 3b3711e7fa..0000000000 --- a/src/qml/jit/qv4lowering.cpp +++ /dev/null @@ -1,200 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <QLoggingCategory> - -#include "qv4lowering_p.h" -#include "qv4graph_p.h" - -QT_REQUIRE_CONFIG(qml_tracing); - -QT_BEGIN_NAMESPACE -namespace QV4 { -namespace IR { - -Q_LOGGING_CATEGORY(lcLower, "qt.v4.ir.lowering") - -GenericLowering::GenericLowering(Function &f) - : m_function(f) -{} - -void GenericLowering::lower() -{ - NodeWorkList worklist(graph()); - // The order doesn't really matter for generic lowering, as long as it's done in 1 pass, and - // have any clean-up done afterwards. - worklist.enqueueAllInputs(graph()->endNode()); - - while (Node *n = worklist.dequeueNextNodeForVisiting()) { - worklist.enqueueAllInputs(n); - - if (!CallPayload::isRuntimeCall(n->opcode())) - continue; - - if (CallPayload::isVarArgsCall(n->opcode())) - replaceWithVarArgsCall(n); - else - replaceWithCall(n); - } -} - -void GenericLowering::replaceWithCall(Node *n) -{ - auto newOp = opBuilder()->getCall(n->opcode()); - - QVarLengthArray<Node *, 32> args; - if (CallPayload::takesEngineAsArg(n->opcode(), 0)) - args.append(graph()->engineNode()); - if (CallPayload::takesFunctionAsArg(n->opcode(), args.size())) - args.append(graph()->functionNode()); - if (CallPayload::takesFrameAsArg(n->opcode(), args.size())) - args.append(graph()->cppFrameNode()); - const int extraLeadingArguments = args.size(); - - for (unsigned arg = 0, earg = n->inputCount(); arg != earg; ++arg) { - Node *input = n->input(arg); - if (input->opcode() == Meta::FrameState) - continue; - - if (arg >= n->operation()->valueInputCount()) { - // effect or control input - args.append(input); - continue; - } - - if (CallPayload::needsStorageOnJSStack(n->opcode(), args.size(), input->operation(), - function().nodeInfo(input)->type())) - input = graph()->createNode(opBuilder()->get<Meta::Alloca>(), input); - - args.append(input); - } - - Node *newCall = graph()->createNode(newOp, args.data(), args.size()); - - qCDebug(lcLower) << "replacing node" << n->id() << n->operation()->debugString() - << "with node" << newCall->id() << newOp->debugString(); - qCDebug(lcLower) << "... old node #inputs:" << n->inputCount(); - qCDebug(lcLower) << "... old node #uses:" << n->useCount(); - - function().nodeInfo(newCall)->setType(CallPayload::returnType(n->opcode())); - n->replaceAllUsesWith(newCall); - n->kill(); - - qCDebug(lcLower) << "... new node #inputs:" << newCall->inputCount(); - qCDebug(lcLower) << "... new node #uses:" << newCall->useCount(); - - for (Node *use : newCall->uses()) { - // fix-up indices for SelectOutput: - if (use->opcode() == Meta::SelectOutput) { - const int oldIndex = ConstantPayload::get(*use->input(1)->operation())->value().int_32(); - const int newIndex = oldIndex + extraLeadingArguments; - use->replaceInput(1, graph()->createConstantIntNode(newIndex)); - use->replaceInput(2, newCall->input(newIndex)); - break; - } - } -} - -void GenericLowering::replaceWithVarArgsCall(Node *n) -{ - const bool isTailCall = n->opcode() == Meta::JSTailCall; - Operation *newOp = isTailCall ? opBuilder()->getTailCall() - : opBuilder()->getCall(n->opcode()); - - //### optimize this for 0 and 1 argument: we don't need to create a VarArgs array for these cases - - const unsigned varArgsStart = CallPayload::varArgsStart(n->opcode()) - 1; // subtract 1 because the runtime calls all take the engine argument as arg0, which isn't in the graph before lowering. - Node *vaAlloc = graph()->createNode( - opBuilder()->get<Meta::VAAlloc>(), - graph()->createConstantIntNode(n->operation()->valueInputCount() - varArgsStart), - n->effectInput()); - QVarLengthArray<Node *, 32> vaSealIn; - vaSealIn.append(vaAlloc); - for (unsigned i = varArgsStart, ei = n->operation()->valueInputCount(); i != ei; ++i) { - vaSealIn.append(graph()->createNode(opBuilder()->get<Meta::VAStore>(), vaAlloc, - graph()->createConstantIntNode(vaSealIn.size() - 1), - n->input(i))); - } - vaSealIn.append(vaAlloc); - Node *vaSeal = graph()->createNode(opBuilder()->getVASeal(vaSealIn.size() - 2), - vaSealIn.data(), - vaSealIn.size()); - QVarLengthArray<Node *, 8> callArgs; - if (isTailCall) - callArgs.append(graph()->cppFrameNode()); - callArgs.append(graph()->engineNode()); - for (unsigned i = 0; i != varArgsStart; ++i) { - Node *input = n->input(i); - if (CallPayload::needsStorageOnJSStack(n->opcode(), callArgs.size(), input->operation(), - function().nodeInfo(input)->type())) - input = graph()->createNode(opBuilder()->get<Meta::Alloca>(), input); - callArgs.append(input); - } - callArgs.append(vaSeal); // args - if (n->opcode() != Meta::JSCreateClass) // JSCreateClass is the odd duck - callArgs.append(graph()->createConstantIntNode(vaSealIn.size() - 2)); // argc - callArgs.append(vaSeal); // effect - callArgs.append(n->controlInput(0)); // control flow - Node *newCall = graph()->createNode(newOp, callArgs.data(), unsigned(callArgs.size())); - - qCDebug(lcLower) << "replacing node" << n->id() << n->operation()->debugString() - << "with node" << newCall->id() << newOp->debugString(); - qCDebug(lcLower) << "... old node #inputs:" << n->inputCount(); - qCDebug(lcLower) << "... old node #uses:" << n->useCount(); - - n->replaceAllUsesWith(newCall); - n->kill(); - - qCDebug(lcLower) << "... new node #inputs:" << newCall->inputCount(); - qCDebug(lcLower) << "... new node #uses:" << newCall->useCount(); -} - -bool GenericLowering::allUsesAsUnboxedBool(Node *n) -{ - for (Node *use : n->uses()) { - if (use->operation()->kind() != Meta::Branch) - return false; - } - - return true; -} - -} // IR namespace -} // QV4 namespace -QT_END_NAMESPACE diff --git a/src/qml/jit/qv4lowering_p.h b/src/qml/jit/qv4lowering_p.h deleted file mode 100644 index 0b482bc9f0..0000000000 --- a/src/qml/jit/qv4lowering_p.h +++ /dev/null @@ -1,107 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QV4LOWERING_P_H -#define QV4LOWERING_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qqmljsmemorypool_p.h> -#include <private/qv4global_p.h> -#include <private/qv4ir_p.h> -#include <private/qv4util_p.h> -#include <private/qv4node_p.h> -#include <private/qv4graph_p.h> - -QT_REQUIRE_CONFIG(qml_tracing); - -QT_BEGIN_NAMESPACE - -namespace QV4 { -namespace IR { - -// Lowering replaces JS level operations with lower level ones. E.g. a JSAdd is lowered to an AddI32 -// if both inputs and the output are 32bit integers, or to a runtime call in all other cases. This -// transforms the graph into something that is closer to actual executable code. - - -// Last lowering phase: replace all JSOperations that are left with runtime calls. There is nothing -// smart here, all that should have been done before this phase. -class GenericLowering final -{ - Q_DISABLE_COPY(GenericLowering) - -public: - GenericLowering(Function &f); - - void lower(); - -private: - void replaceWithCall(Node *n); - void replaceWithVarArgsCall(Node *n); - static bool allUsesAsUnboxedBool(Node *n); - - Function &function() - { return m_function; } - - Graph *graph() - { return function().graph(); } - - OperationBuilder *opBuilder() - { return graph()->opBuilder(); } - -private: - Function &m_function; -}; - -} // namespace IR -} // namespace QV4 - -QT_END_NAMESPACE - -#endif // QV4LOWERING_P_H diff --git a/src/qml/jit/qv4mi.cpp b/src/qml/jit/qv4mi.cpp deleted file mode 100644 index f0b172243d..0000000000 --- a/src/qml/jit/qv4mi.cpp +++ /dev/null @@ -1,251 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <QtCore/qloggingcategory.h> -#include <private/qqmlglobal_p.h> - -#include "qv4mi_p.h" -#include "qv4node_p.h" - -QT_BEGIN_NAMESPACE -namespace QV4 { -namespace IR { - -Q_LOGGING_CATEGORY(lcMI, "qt.v4.ir.mi") - -QString MIOperand::debugString() const -{ - switch (kind()) { - case Invalid: return QStringLiteral("<<INVALID>>"); - case Constant: return ConstantPayload::debugString(constantValue()); - case VirtualRegister: return QStringLiteral("vreg%1").arg(virtualRegister()); - case EngineRegister: return QStringLiteral("engine"); - case CppFrameRegister: return QStringLiteral("cppFrame"); - case Function: return QStringLiteral("function"); - case JSStackSlot: return QStringLiteral("jsstack[%1]").arg(stackSlot()); - case BoolStackSlot: return QStringLiteral("bstack[%1]").arg(stackSlot()); - case JumpTarget: return targetBlock() ? QStringLiteral("L%1").arg(targetBlock()->index()) - : QStringLiteral("<<INVALID BLOCK>>"); - default: Q_UNREACHABLE(); - } -} - -MIInstr *MIInstr::create(QQmlJS::MemoryPool *pool, Node *irNode, unsigned nOperands) -{ - return pool->New<MIInstr>(irNode, pool, nOperands); -} - -static QString commentIndent(const QString &line) -{ - int spacing = std::max(70 - line.length(), 1); - return line + QString(spacing, QLatin1Char(' ')); -} - -static QString indent(int nr) -{ - QString s = nr == -1 ? QString() : QString::number(nr); - int padding = 6 - s.size(); - if (padding > 0) - s = QString(padding, QLatin1Char(' ')) + s; - return s; -} - -MIFunction::MIFunction(Function *irFunction) - : m_irFunction(irFunction) -{} - -void MIFunction::renumberBlocks() -{ - for (size_t i = 0, ei = m_blocks.size(); i != ei; ++i) { - MIBlock *b = m_blocks[i]; - b->setIndex(unsigned(i)); - } -} - -void MIFunction::renumberInstructions() -{ - int pos = 0; - for (MIBlock *b : m_blocks) { - for (MIInstr &instr : b->instructions()) { - pos += 2; - instr.setPosition(pos); - } - } -} - -void MIFunction::dump(const QString &description) const -{ - if (!lcMI().isDebugEnabled()) - return; - - QString s = description + QLatin1String(":\n"); - QString name; - if (auto n = irFunction()->v4Function()->name()) - name = n->toQString(); - if (name.isEmpty()) - QString::asprintf("%p", static_cast<void *>(irFunction()->v4Function())); - QString line = QStringLiteral("function %1 {").arg(name); - auto loc = irFunction()->v4Function()->sourceLocation(); - s += commentIndent(line) + QStringLiteral("; %1:%2:%3\n").arg(loc.sourceFile, - QString::number(loc.line), - QString::number(loc.column)); - for (const MIBlock *b : blocks()) { - line = QStringLiteral("L%1").arg(b->index()); - bool first = true; - if (!b->arguments().empty()) { - line += QLatin1Char('('); - for (const MIOperand &arg : b->arguments()) { - if (first) - first = false; - else - line += QStringLiteral(", "); - line += arg.debugString(); - } - line += QLatin1Char(')'); - } - line += QLatin1Char(':'); - line = commentIndent(line) + QStringLiteral("; preds: "); - if (b->inEdges().isEmpty()) { - line += QStringLiteral("<none>"); - } else { - bool first = true; - for (MIBlock *in : b->inEdges()) { - if (first) - first = false; - else - line += QStringLiteral(", "); - line += QStringLiteral("L%1").arg(in->index()); - } - } - s += line + QLatin1Char('\n'); - for (const MIInstr &i : b->instructions()) { - line = indent(i.position()) + QLatin1String(": "); - if (i.hasDestination()) - line += i.destination().debugString() + QStringLiteral(" = "); - line += i.irNode()->operation()->debugString(); - bool first = true; - for (const MIOperand &op : i.operands()) { - if (first) - first = false; - else - line += QLatin1Char(','); - line += QLatin1Char(' ') + op.debugString(); - } - line = commentIndent(line) + QStringLiteral("; node-id: %1").arg(i.irNode()->id()); - if (i.irNode()->operation()->needsBytecodeOffsets()) - line += QStringLiteral(", bytecode-offset: %1").arg(irFunction()->nodeInfo(i.irNode())->currentInstructionOffset()); - s += line + QLatin1Char('\n'); - } - s += commentIndent(QString()) + QStringLiteral("; succs: "); - if (b->outEdges().isEmpty()) { - s += QStringLiteral("<none>"); - } else { - bool first = true; - for (MIBlock *succ : b->outEdges()) { - if (first) - first = false; - else - s += QStringLiteral(", "); - s += QStringLiteral("L%1").arg(succ->index()); - } - } - s += QLatin1Char('\n'); - } - s += QLatin1Char('}'); - - for (const QStringRef &line : s.splitRef('\n')) - qCDebug(lcMI).noquote().nospace() << line; -} - -unsigned MIFunction::extraJSSlots() const -{ - uint interpreterFrameSize = CppStackFrame::requiredJSStackFrameSize(irFunction()->v4Function()); - if (m_jsSlotCount <= interpreterFrameSize) - return 0; - return m_jsSlotCount - interpreterFrameSize; -} - -void MIFunction::setStartBlock(MIBlock *newStartBlock) -{ - auto it = std::find(m_blocks.begin(), m_blocks.end(), newStartBlock); - Q_ASSERT(it != m_blocks.end()); - std::swap(*m_blocks.begin(), *it); -} - -void MIFunction::setStackSlotCounts(unsigned dword, unsigned qword, unsigned js) -{ - m_vregCount = 0; - m_dwordSlotCount = dword; - m_qwordSlotCount = qword; - m_jsSlotCount = js; -} - -void MIFunction::verifyCFG() const -{ - if (block(MIFunction::StartBlockIndex)->instructions().front().opcode() != Meta::Start) - qFatal("MIFunction block 0 is not the start block"); - - for (MIBlock *b : m_blocks) { - for (MIBlock *in : b->inEdges()) { - if (!in->outEdges().contains(b)) - qFatal("block %u has incoming edge from block %u, " - "but does not appear in that block's outgoing edges", - b->index(), in->index()); - } - for (MIBlock *out : b->outEdges()) { - if (!out->inEdges().contains(b)) - qFatal("block %u has outgoing edge from block %u, " - "but does not appear in that block's incoming edges", - b->index(), out->index()); - } - } -} - -MIBlock *MIBlock::findEdgeTo(Operation::Kind target) const -{ - for (MIBlock *outEdge : outEdges()) { - if (outEdge->instructions().front().opcode() == target) - return outEdge; - } - return nullptr; -} - -} // IR namespace -} // QV4 namespace -QT_END_NAMESPACE diff --git a/src/qml/jit/qv4mi_p.h b/src/qml/jit/qv4mi_p.h deleted file mode 100644 index f976d1dc94..0000000000 --- a/src/qml/jit/qv4mi_p.h +++ /dev/null @@ -1,627 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QV4MI_P_H -#define QV4MI_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qv4global_p.h> -#include <private/qv4ir_p.h> -#include <private/qv4node_p.h> -#include <private/qv4operation_p.h> - -#include <llvm/ADT/iterator.h> -#include <llvm/ADT/iterator_range.h> -#include <llvm/ADT/ilist.h> -#include <llvm/ADT/ilist_node.h> - -QT_REQUIRE_CONFIG(qml_tracing); - -QT_BEGIN_NAMESPACE - -namespace QV4 { -namespace IR { - -// This file contains the Machine Interface (MI) data structures, on which ultimately the assembler -// will operate: - -class MIFunction; // containing all basic blocks, and a reference to the IR function - -class MIBlock; // containing an ordered sequence of instructions - -class MIInstr; // containing operands, and a reference to the IR node, that indicates which - // operation is represented by an instruction - -class MIOperand; // contains a description of where to get/put the input/result of an operation - -// A detail about the stack slots: there two stacks, the JS stack and the native stack. A frame on -// the native stack is divided in two parts: the quad-word part and the double-word part. The -// qword part holds 64bit values, like doubles, and pointers on 64bit architectures. The dword part -// holds 32bit values, like int32s, booleans, and pointers on 32bit architectures. We need to know -// the type of value a slot holds, because if we have to move it to the JS stack, we have to box it -// correctly. -class MIOperand final -{ -public: - enum Kind { - Invalid = 0, - Constant, - VirtualRegister, - - EngineRegister, - CppFrameRegister, - Function, - - JSStackSlot, - BoolStackSlot, - - JumpTarget, - }; - - using List = QQmlJS::FixedPoolArray<MIOperand>; - -public: - MIOperand() = default; - - static MIOperand createConstant(Node *irNode) - { - MIOperand op; - op.m_kind = Constant; - op.m_irNode = irNode; - return op; - } - - static MIOperand createVirtualRegister(Node *irNode, unsigned vreg) - { - MIOperand op; - op.m_kind = VirtualRegister; - op.m_irNode = irNode; - op.m_vreg = vreg; - return op; - } - - static MIOperand createEngineRegister(Node *irNode) - { - MIOperand op; - op.m_kind = EngineRegister; - op.m_irNode = irNode; - return op; - } - - static MIOperand createCppFrameRegister(Node *irNode) - { - MIOperand op; - op.m_kind = CppFrameRegister; - op.m_irNode = irNode; - return op; - } - - static MIOperand createFunction(Node *irNode) - { - MIOperand op; - op.m_kind = Function; - op.m_irNode = irNode; - return op; - } - - static MIOperand createJSStackSlot(Node *irNode, unsigned slot) - { - MIOperand op; - op.m_kind = JSStackSlot; - op.m_irNode = irNode; - op.m_slot = slot; - return op; - } - - static MIOperand createBoolStackSlot(Node *irNode, unsigned slot) - { - MIOperand op; - op.m_kind = BoolStackSlot; - op.m_irNode = irNode; - op.m_slot = slot; - return op; - } - - //### or name this createDeoptBlock? - static MIOperand createJumpTarget(Node *irNode, MIBlock *targetBlock) - { - MIOperand op; - op.m_kind = JumpTarget; - op.m_irNode = irNode; - op.m_targetBlock = targetBlock; - return op; - } - - Kind kind() const - { return m_kind; } - - bool isValid() const - { return m_kind != Invalid; } - - bool isConstant() const - { return m_kind == Constant; } - - bool isVirtualRegister() const - { return kind() == VirtualRegister; } - - bool isEngineRegister() const - { return kind() == EngineRegister; } - - bool isCppFrameRegister() const - { return kind() == CppFrameRegister; } - - bool isFunction() const - { return kind() == Function; } - - bool isJSStackSlot() const - { return kind() == JSStackSlot; } - - bool isBoolStackSlot() const - { return kind() == BoolStackSlot; } - - bool isStackSlot() const - { return isJSStackSlot() || isDWordSlot() || isQWordSlot(); } - - bool isJumpTarget() const - { return kind() == JumpTarget; } - - Node *irNode() const - { return m_irNode; } - - inline Type nodeType(MIFunction *f) const; - - QString debugString() const; - - QV4::Value constantValue() const - { - Q_ASSERT(isConstant()); - if (irNode()->opcode() == Meta::Undefined) - return QV4::Value::undefinedValue(); - if (irNode()->opcode() == Meta::Empty) - return QV4::Value::emptyValue(); - return ConstantPayload::get(*irNode()->operation())->value(); - } - - unsigned virtualRegister() const - { Q_ASSERT(isVirtualRegister()); return m_vreg; } - - unsigned stackSlot() const - { Q_ASSERT(isStackSlot()); return m_slot; } - - MIBlock *targetBlock() const - { Q_ASSERT(isJumpTarget()); return m_targetBlock; } - - inline bool operator==(const MIOperand &other) const - { - if (kind() != other.kind()) - return false; - - if (isStackSlot()) - return stackSlot() == other.stackSlot(); - - switch (kind()) { - case MIOperand::Invalid: - return !other.isValid(); - case MIOperand::Constant: - return constantValue().asReturnedValue() == other.constantValue().asReturnedValue(); - case MIOperand::VirtualRegister: - return virtualRegister() == other.virtualRegister(); - case MIOperand::JumpTarget: - return targetBlock() == other.targetBlock(); - default: - Q_UNREACHABLE(); - return false; - } - } - - bool isDWordSlot() const - { - switch (kind()) { - case BoolStackSlot: - return true; - default: - return false; - } - } - - bool isQWordSlot() const - { - switch (kind()) { - //### TODO: double slots - default: - return false; - } - } - - bool overlaps(const MIOperand &other) const - { - if ((isDWordSlot() && other.isDWordSlot()) || (isQWordSlot() && other.isQWordSlot())) - ; // fine, these are the same - else if (kind() != other.kind()) - return false; - - if (isStackSlot()) - return stackSlot() == other.stackSlot(); - - return false; - } - -private: - Node *m_irNode = nullptr; - union { - unsigned m_vreg; - unsigned m_slot; - MIBlock *m_targetBlock = nullptr; - }; - Kind m_kind = Invalid; -}; - -template <typename NodeTy> struct MIInstrListParentType {}; -template <> struct MIInstrListParentType<MIInstr> { using type = MIBlock; }; - -template <typename NodeTy> class MIInstrList; - -template <typename MISubClass> -class MIInstrListTraits : public llvm::ilist_noalloc_traits<MISubClass> -{ -protected: - using ListTy = MIInstrList<MISubClass>; - using iterator = typename llvm::simple_ilist<MISubClass>::iterator; - using ItemParentClass = typename MIInstrListParentType<MISubClass>::type; - -public: - MIInstrListTraits() = default; - -protected: - void setListOwner(ItemParentClass *listOwner) - { m_owner = listOwner; } - -private: - ItemParentClass *m_owner = nullptr; - - /// getListOwner - Return the object that owns this list. If this is a list - /// of instructions, it returns the BasicBlock that owns them. - ItemParentClass *getListOwner() const { - return m_owner; - } - - static ListTy &getList(ItemParentClass *Par) { - return Par->*(Par->getSublistAccess()); - } - - static MIInstrListTraits<MISubClass> *getSymTab(ItemParentClass *Par) { - return Par ? toPtr(Par->getValueSymbolTable()) : nullptr; - } - -public: - void addNodeToList(MISubClass *V) { V->setParent(getListOwner()); } - void removeNodeFromList(MISubClass *V) { V->setParent(nullptr); } - void transferNodesFromList(MIInstrListTraits &L2, iterator first, - iterator last); -}; - -template <class T> -class MIInstrList: public llvm::iplist_impl<llvm::simple_ilist<T>, MIInstrListTraits<T>> -{ -public: - MIInstrList(typename MIInstrListTraits<T>::ItemParentClass *owner) - { this->setListOwner(owner); } -}; - -class MIInstr final : public llvm::ilist_node_with_parent<MIInstr, MIBlock> -{ - Q_DISABLE_COPY_MOVE(MIInstr) // heap use only! - -protected: - friend class QQmlJS::MemoryPool; - MIInstr() : m_operands(nullptr, 0) {} - - explicit MIInstr(Node *irNode, QQmlJS::MemoryPool *pool, unsigned nOperands) - : m_irNode(irNode) - , m_operands(pool, nOperands) - {} - - ~MIInstr() = default; - -public: - static MIInstr *create(QQmlJS::MemoryPool *pool, Node *irNode, unsigned nOperands); - - MIBlock *parent() const - { return m_parent; } - - MIBlock *getParent() const // for ilist_node_with_parent - { return parent(); } - - void setParent(MIBlock *parent) - { m_parent = parent; } - - Node *irNode() const - { return m_irNode; } - - Operation::Kind opcode() const - { return m_irNode->opcode(); } - - int position() const - { return m_position; } - - inline void insertBefore(MIInstr *insertPos); - inline void insertAfter(MIInstr *insertPos); - inline MIInstrList<MIInstr>::iterator eraseFromParent(); - - bool hasDestination() const - { return m_destination.isValid(); } - - MIOperand destination() const - { return m_destination; } - - void setDestination(const MIOperand &dest) - { m_destination = dest; } - - const MIOperand &operand(unsigned index) const - { return m_operands.at(index); } - - void setOperand(unsigned index, const MIOperand &op) - { m_operands.at(index) = op; } - - MIOperand &operand(unsigned index) - { return m_operands.at(index); } - - const MIOperand::List &operands() const - { return m_operands; } - - MIOperand::List &operands() - { return m_operands; } - -private: - friend MIFunction; - void setPosition(int newPosition) - { m_position = newPosition; } - -private: - MIBlock *m_parent = nullptr; - Node *m_irNode = nullptr; - int m_position = -1; - MIOperand m_destination; - MIOperand::List m_operands; -}; - -class MIBlock final -{ - Q_DISABLE_COPY_MOVE(MIBlock) - -public: - using Index = unsigned; - enum : Index { InvalidIndex = std::numeric_limits<Index>::max() }; - - using MIInstructionList = MIInstrList<MIInstr>; - - using InEdges = QVarLengthArray<MIBlock *, 4>; - using OutEdges = QVarLengthArray<MIBlock *, 2>; - -protected: - friend MIFunction; - explicit MIBlock(Index index) - : m_instructions(this), - m_index(index) - {} - - void setIndex(Index newIndex) - { m_index = newIndex; } - -public: - ~MIBlock() = default; - - const MIInstructionList &instructions() const - { return m_instructions; } - - MIInstructionList &instructions() - { return m_instructions; } - - static MIInstructionList MIBlock::*getSublistAccess(MIInstr * = nullptr) - { return &MIBlock::m_instructions; } - - void addArgument(MIOperand &&arg) - { m_arguments.push_back(arg); } - - const std::vector<MIOperand> &arguments() const - { return m_arguments; } - - std::vector<MIOperand> &arguments() - { return m_arguments; } - - void clearArguments() - { m_arguments.resize(0); } - - const InEdges &inEdges() const - { return m_inEdges; } - - void addInEdge(MIBlock *edge) - { m_inEdges.append(edge); } - - const OutEdges &outEdges() const - { return m_outEdges; } - - void addOutEdge(MIBlock *edge) - { m_outEdges.append(edge); } - - Index index() const - { return m_index; } - - MIBlock *findEdgeTo(Operation::Kind target) const; - - bool isDeoptBlock() const - { return m_isDeoptBlock; } - - void markAsDeoptBlock() - { m_isDeoptBlock = true; } - -private: - std::vector<MIOperand> m_arguments; - MIInstructionList m_instructions; - InEdges m_inEdges; - OutEdges m_outEdges; - Index m_index; - bool m_isDeoptBlock = false; -}; - -class MIFunction final -{ - Q_DISABLE_COPY_MOVE(MIFunction) - -public: - static constexpr MIBlock::Index StartBlockIndex = 0; - -public: - MIFunction(Function *irFunction); - ~MIFunction() - { qDeleteAll(m_blocks); } - - Function *irFunction() const - { return m_irFunction; } - - void setStartBlock(MIBlock *newStartBlock); - void renumberBlocks(); - void renumberInstructions(); - - void dump(const QString &description) const; - - size_t blockCount() const - { return blocks().size(); } - - MIBlock *block(MIBlock::Index index) const - { return m_blocks[index]; } - - const std::vector<MIBlock *> &blocks() const - { return m_blocks; } - - MIBlock *addBlock() - { - auto *b = new MIBlock(unsigned(m_blocks.size())); - m_blocks.push_back(b); - return b; - } - - void setBlockOrder(const std::vector<MIBlock *> &newSequence) - { m_blocks = newSequence; } - - unsigned vregCount() const - { return m_vregCount; } - - void setVregCount(unsigned vregCount) - { m_vregCount = vregCount; } - - unsigned dwordSlotCount() const - { return m_dwordSlotCount; } - - unsigned qwordSlotCount() const - { return m_qwordSlotCount; } - - unsigned jsSlotCount() const - { return m_jsSlotCount; } - - unsigned extraJSSlots() const; - - void setStackSlotCounts(unsigned dword, unsigned qword, unsigned js); - - void verifyCFG() const; - -private: - Function *m_irFunction = nullptr; - std::vector<MIBlock *> m_blocks; - unsigned m_vregCount = 0; - unsigned m_dwordSlotCount = 0; - unsigned m_qwordSlotCount = 0; - unsigned m_jsSlotCount = 0; -}; - -Type MIOperand::nodeType(MIFunction *f) const -{ - return f->irFunction()->nodeInfo(irNode())->type(); -} - -inline uint qHash(const MIOperand &key, uint seed) -{ - uint h = ::qHash(key.kind(), seed); - switch (key.kind()) { - case MIOperand::VirtualRegister: - h ^= key.virtualRegister(); - break; - case MIOperand::BoolStackSlot: Q_FALLTHROUGH(); - case MIOperand::JSStackSlot: - h ^= key.stackSlot(); - break; - default: - qFatal("%s: cannot hash %s", Q_FUNC_INFO, key.debugString().toUtf8().constData()); - } - return h; -} - -void MIInstr::insertBefore(MIInstr *insertPos) -{ - insertPos->parent()->instructions().insert(insertPos->getIterator(), this); -} - -void MIInstr::insertAfter(MIInstr *insertPos) -{ - insertPos->parent()->instructions().insert(++insertPos->getIterator(), this); -} - -MIInstrList<MIInstr>::iterator MIInstr::eraseFromParent() -{ - auto p = parent(); - setParent(nullptr); - return p->instructions().erase(getIterator()); -} - -} // namespace IR -} // namespace QV4 - -QT_END_NAMESPACE - -#endif // QV4MI_P_H diff --git a/src/qml/jit/qv4miblockset_p.h b/src/qml/jit/qv4miblockset_p.h deleted file mode 100644 index 5f814b99e0..0000000000 --- a/src/qml/jit/qv4miblockset_p.h +++ /dev/null @@ -1,291 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QV4MIBLOCKSET_P_H -#define QV4MIBLOCKSET_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qv4mi_p.h" -#include "qv4util_p.h" - -QT_REQUIRE_CONFIG(qml_tracing); - -QT_BEGIN_NAMESPACE - -namespace QV4 { -namespace IR { - -class MIBlockSet -{ - using Flags = BitVector; - - QVarLengthArray<MIBlock::Index, 8> blockNumbers; - Flags *blockFlags = nullptr; - MIFunction *function = nullptr; - enum { MaxVectorCapacity = 8 }; - -public: - class const_iterator; - friend class const_iterator; - -public: - MIBlockSet(MIFunction *f = nullptr) - { - if (f) - init(f); - } - - MIBlockSet(MIBlockSet &&other) noexcept - { - std::swap(blockNumbers, other.blockNumbers); - std::swap(blockFlags, other.blockFlags); - std::swap(function, other.function); - } - - MIBlockSet(const MIBlockSet &other) - : function(other.function) - { - if (other.blockFlags) - blockFlags = new Flags(*other.blockFlags); - blockNumbers = other.blockNumbers; - } - - MIBlockSet &operator=(const MIBlockSet &other) - { - if (blockFlags) { - delete blockFlags; - blockFlags = nullptr; - } - function = other.function; - if (other.blockFlags) - blockFlags = new Flags(*other.blockFlags); - blockNumbers = other.blockNumbers; - return *this; - } - - MIBlockSet &operator=(MIBlockSet &&other) noexcept - { - if (&other != this) { - std::swap(blockNumbers, other.blockNumbers); - - delete blockFlags; - blockFlags = other.blockFlags; - other.blockFlags = nullptr; - - function = other.function; - } - return *this; - } - - ~MIBlockSet() - { - delete blockFlags; - } - - void init(MIFunction *f) - { - Q_ASSERT(!function); - Q_ASSERT(f); - function = f; - } - - bool empty() const; - - void insert(MIBlock *bb) - { - Q_ASSERT(function); - - if (blockFlags) { - blockFlags->setBit(bb->index()); - return; - } - - for (unsigned int blockNumber : qAsConst(blockNumbers)) { - if (blockNumber == bb->index()) - return; - } - - if (blockNumbers.size() == MaxVectorCapacity) { - blockFlags = new Flags(int(function->blockCount()), false); - for (unsigned int blockNumber : qAsConst(blockNumbers)) { - blockFlags->setBit(int(blockNumber)); - } - blockNumbers.clear(); - blockFlags->setBit(int(bb->index())); - } else { - blockNumbers.append(bb->index()); - } - } - - void remove(MIBlock *bb) - { - Q_ASSERT(function); - - if (blockFlags) { - blockFlags->clearBit(bb->index()); - return; - } - - for (int i = 0; i < blockNumbers.size(); ++i) { - if (blockNumbers[i] == bb->index()) { - blockNumbers.remove(i); - return; - } - } - } - - const_iterator begin() const; - const_iterator end() const; - - void collectValues(std::vector<MIBlock *> &bbs) const; - - bool contains(MIBlock *bb) const - { - Q_ASSERT(function); - - if (blockFlags) - return blockFlags->at(bb->index()); - - for (unsigned int blockNumber : blockNumbers) { - if (blockNumber == bb->index()) - return true; - } - - return false; - } -}; - -class MIBlockSet::const_iterator -{ - const MIBlockSet &set; - // ### These two members could go into a union, but clang won't compile - // (https://codereview.qt-project.org/#change,74259) - QVarLengthArray<MIBlock::Index, 8>::const_iterator numberIt; - MIBlock::Index flagIt; - - friend class MIBlockSet; - const_iterator(const MIBlockSet &set, bool end) - : set(set) - { - if (end || !set.function) { - if (!set.blockFlags) - numberIt = set.blockNumbers.end(); - else - flagIt = set.blockFlags->size(); - } else { - if (!set.blockFlags) - numberIt = set.blockNumbers.begin(); - else - findNextWithFlags(0); - } - } - - void findNextWithFlags(int start) - { - flagIt = MIBlock::Index(set.blockFlags->findNext(start, true, /*wrapAround = */false)); - Q_ASSERT(flagIt <= MIBlock::Index(set.blockFlags->size())); - } - -public: - MIBlock *operator*() const - { - if (!set.blockFlags) - return set.function->block(*numberIt); - - Q_ASSERT(flagIt <= set.function->blockCount()); - return set.function->block(flagIt); - - } - - bool operator==(const const_iterator &other) const - { - if (&set != &other.set) - return false; - if (!set.blockFlags) - return numberIt == other.numberIt; - return flagIt == other.flagIt; - } - - bool operator!=(const const_iterator &other) const - { return !(*this == other); } - - const_iterator &operator++() - { - if (!set.blockFlags) - ++numberIt; - else - findNextWithFlags(flagIt + 1); - - return *this; - } -}; - -inline bool MIBlockSet::empty() const -{ return begin() == end(); } - -inline MIBlockSet::const_iterator MIBlockSet::begin() const -{ return const_iterator(*this, false); } - -inline MIBlockSet::const_iterator MIBlockSet::end() const -{ return const_iterator(*this, true); } - -inline void MIBlockSet::collectValues(std::vector<MIBlock *> &bbs) const -{ - Q_ASSERT(function); - - for (auto it : *this) - bbs.push_back(it); -} - -} // namespace IR -} // namespace QV4 - -QT_END_NAMESPACE - -#endif // QV4MIBLOCKSET_P_H diff --git a/src/qml/jit/qv4node.cpp b/src/qml/jit/qv4node.cpp deleted file mode 100644 index e059e9fef6..0000000000 --- a/src/qml/jit/qv4node.cpp +++ /dev/null @@ -1,215 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4node_p.h" -#include "qv4graph_p.h" - -QT_REQUIRE_CONFIG(qml_tracing); - -QT_BEGIN_NAMESPACE -namespace QV4 { -namespace IR { - -Node *Node::create(Node::MemoryPool *pool, Node::Id id, const Operation *op, size_t nInputs, - Node *const *inputs, bool inputsAreExtensible) -{ - size_t capacity = nInputs; - if (inputsAreExtensible) - capacity += 3; - - Node *node = new (pool->allocate(sizeof(Node))) Node(pool, id, op, unsigned(nInputs), - int(capacity)); - for (uint i = 0; i < capacity; ++i) - new (&node->m_inputs[int(i)]) Use(node); - for (size_t i = 0; i < nInputs; ++i) { - Q_ASSERT(inputs[i] != nullptr); - node->replaceInput(unsigned(i), inputs[i]); - } - - return node; -} - -void Node::addInput(MemoryPool *pool, Node *in) -{ - Q_ASSERT(in); - ++m_nInputs; - if (m_nInputs >= unsigned(m_inputs.size())) { - QQmlJS::FixedPoolArray<Use> oldInputs = m_inputs; - m_inputs = QQmlJS::FixedPoolArray<Use>(pool, int(m_nInputs + 3)); - for (Use &input : m_inputs) - new (&input) Use(this); - for (int i = 0, ei = oldInputs.size(); i != ei; ++i) { - Node *in = oldInputs[i].m_input; - oldInputs[i].set(nullptr); - m_inputs[i].set(in); - } - } - m_inputs.at(int(m_nInputs - 1)).set(in); -} - -void Node::removeInput(unsigned index) -{ - Q_ASSERT(index < inputCount()); - for (unsigned i = index, ei = inputCount(); i < ei - 1; ++i) - replaceInput(i, input(i + 1)); - trimInputCount(inputCount() - 1); -} - -void Node::removeInputs(unsigned start, unsigned count) -{ - for (unsigned idx = start; idx < start + count; ++idx) - m_inputs.at(int(idx)).set(nullptr); -} - -void Node::removeAllInputs() -{ - removeInputs(0, inputCount()); -} - -void Node::trimInputCount(unsigned newCount) -{ - unsigned currentCount = inputCount(); - if (newCount == currentCount) - return; - Q_ASSERT(newCount < currentCount); - removeInputs(newCount, currentCount - newCount); - m_nInputs = newCount; -} - -void Node::removeExceptionHandlerUse() -{ - for (Use* use = m_firstUse; use; use = use->m_next) { - if (use->m_input->opcode() == Meta::OnException) { - use->set(nullptr); - break; - } - } -} - -void Node::insertInput(Node::MemoryPool *pool, unsigned index, Node *newInput) -{ - Q_ASSERT(index < inputCount()); - addInput(pool, input(inputCount() - 1)); - for (unsigned i = inputCount() - 1; i > index; --i) - replaceInput(i, input(i - 1)); - replaceInput(index, newInput); -} - -void Node::replaceAllUsesWith(Node *replacement) -{ - for (Use *use = m_firstUse; use; ) { - Use *next = use->m_next; - const unsigned inIdx = use->inputIndex(); - use->user()->replaceInput(inIdx, replacement); - use = next; - } -} - -void Node::replaceUses(Node *newValueInput, Node *newEffectInput, Node *newControlInput) -{ - for (Use *use = m_firstUse; use; ) { - Use *next = use->m_next; - const Operation *inOp = use->user()->operation(); - const unsigned inIdx = use->inputIndex(); - if (inIdx < inOp->valueInputCount()) - use->user()->replaceInput(inIdx, newValueInput); - else if (inIdx < inOp->indexOfFirstControl()) - use->user()->replaceInput(inIdx, newEffectInput); - else - use->user()->replaceInput(inIdx, newControlInput); - use = next; - } -} - -Node *Node::firstValueUse() -{ - for (auto it = uses().begin(), eit = uses().end(); it != eit; ++it) { - if (it.isUsedAsValue()) - return *it; - } - return nullptr; -} - -Node::Node(MemoryPool *pool, Node::Id id, const Operation *op, unsigned nInputs, int capacity) - : m_op(op) - , m_inputs(pool, capacity) - , m_nInputs(nInputs) - , m_id(id) -{ -} - -NodeWorkList::NodeWorkList(const Graph *g) - : m_nodeState(g->nodeCount(), Unvisited) -{ m_worklist.reserve(64); } - -void NodeWorkList::reset() -{ - std::fill(m_nodeState.begin(), m_nodeState.end(), Unvisited); - m_worklist.clear(); - if (m_worklist.capacity() < 64) - m_worklist.reserve(64); -} - -NodeCollector::NodeCollector(const Graph *g, bool collectUses, bool skipFramestate) -{ - markReachable(g->endNode()); - for (size_t i = 0; i < m_reachable.size(); ++i) { // _reachable.size() is on purpose! - Node *n = m_reachable.at(i); - for (auto input : n->inputs()) { - if (input == nullptr) - continue; - if (isReachable(input->id())) - continue; - if (skipFramestate && input->opcode() == Meta::FrameState) - continue; - markReachable(input); - } - - if (collectUses) { - for (Node *use : n->uses()) { - if (use && !isReachable(use->id())) - markReachable(use); - } - } - } -} - -} // IR namespace -} // QV4 namespace -QT_END_NAMESPACE diff --git a/src/qml/jit/qv4node_p.h b/src/qml/jit/qv4node_p.h deleted file mode 100644 index 76065fb1bc..0000000000 --- a/src/qml/jit/qv4node_p.h +++ /dev/null @@ -1,626 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QV4NODE_P_H -#define QV4NODE_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qqmljsmemorypool_p.h> -#include <private/qv4global_p.h> -#include <private/qv4operation_p.h> -#include "qv4util_p.h" - -QT_REQUIRE_CONFIG(qml_tracing); - -QT_BEGIN_NAMESPACE - -namespace QV4 { -namespace IR { - -class Use -{ - Q_DISABLE_COPY_MOVE(Use) - -public: - Use(Node *user) - : m_user(user) - {} - - ~Use() - { - if (m_input) - removeFromList(); - } - - operator Node *() const { return m_input; } - Node *input() const { return m_input; } - Node *user() const { return m_user; } - - inline void set(Node *newInput); - - void validate() const - { - Q_ASSERT(m_user); - if (m_input) { - if (m_prev != nullptr) - Q_ASSERT(*m_prev == this); - if (m_next) { - Q_ASSERT(m_next->m_input == m_input); - Q_ASSERT(m_next->m_prev == &m_next); - m_next->validate(); - } - } - } - - inline int inputIndex() const; - -protected: - friend class Node; - - void addToList(Use **list) { - validate(); - m_next = *list; - if (m_next) - m_next->m_prev = &m_next; - m_prev = list; - *list = this; - validate(); - } - - void removeFromList() { - validate(); - Use **newPrev = m_prev; - *newPrev = m_next; - m_prev = nullptr; - if (m_next) - m_next->m_prev = newPrev; - m_next = nullptr; - m_input = nullptr; - validate(); - } - -private: - Node *m_input = nullptr; - Node *m_user = nullptr; - Use *m_next = nullptr; - Use **m_prev = nullptr; -}; - -// A node represents an calculation, action, or marker in the graph. Each node has an operation, -// input dependencies and uses. The operation indicates what kind of node it is, e.g.: JSAdd, -// Constant, Region, and so on. Two nodes can have the same operation, but different inputs. -// For example, the expressions 1 + 2 and 3 + 4 will each have a node with an JSAdd operation -// (which is exactly the same operation for both nodes), but the nodes have different inputs (1, and -// 2 in the first expression, while the second operation has 3 and 4 as inputs). -class Node final -{ - Q_DISABLE_COPY_MOVE(Node) - -public: - using Id = uint32_t; - using MemoryPool = QQmlJS::MemoryPool; - class Inputs; - -public: - static Node *create(MemoryPool *pool, Id id, const Operation *op, size_t nInputs, - Node * const *inputs, bool inputsAreExtensible = false); - ~Node() = delete; - - inline bool isDead() const; - inline void kill(); - - Id id() const { return m_id; } - - const Operation *operation() const - { return m_op; } - - void setOperation(const Operation *op) - { m_op = op; } - - Operation::Kind opcode() const - { return operation()->kind(); } - - inline Inputs inputs() const; - void addInput(MemoryPool *pool, Node *in); - void removeInput(unsigned index); - void removeInputs(unsigned start, unsigned count); - void removeAllInputs(); - uint32_t inputCount() const - { return m_nInputs; } - void trimInputCount(unsigned newCount); - - void removeExceptionHandlerUse(); - - Node *input(unsigned idx) const - { - Q_ASSERT(idx < inputCount()); - return m_inputs.at(idx); - } - - Node *effectInput(unsigned effectIndex = 0) const - { - if (operation()->effectInputCount() == 0) - return nullptr; - Q_ASSERT(effectIndex < operation()->effectInputCount()); - return input(operation()->indexOfFirstEffect() + effectIndex); - } - - Node *controlInput(unsigned controlIndex = 0) const - { - if (operation()->controlInputCount() == 0) - return nullptr; - Q_ASSERT(controlIndex < operation()->controlInputCount()); - return input(operation()->indexOfFirstControl() + controlIndex); - } - - Node *frameStateInput() const - { - if (operation()->hasFrameStateInput()) - return input(operation()->indexOfFrameStateInput()); - return nullptr; - } - - void setFrameStateInput(Node *newFramestate) - { - if (operation()->hasFrameStateInput()) - replaceInput(operation()->indexOfFrameStateInput(), newFramestate); - } - - void insertInput(MemoryPool *pool, unsigned index, Node *newInput); - - void replaceInput(Node *oldIn, Node *newIn) - { - for (unsigned i = 0, ei = inputCount(); i != ei; ++i) { - if (input(i) == oldIn) - replaceInput(i, newIn); - } - } - - void replaceInput(unsigned idx, Node *newIn) - { - m_inputs[idx].set(newIn); - } - - class Uses - { - public: - explicit Uses(Node *node) - : m_node(node) - {} - - class const_iterator; - inline const_iterator begin() const; - inline const_iterator end() const; - - bool isEmpty() const; - - private: - Node *m_node; - }; - - Uses uses() { return Uses(this); } - bool hasUses() const { return m_firstUse != nullptr; } - unsigned useCount() const - { - unsigned cnt = 0; - for (Use *it = m_firstUse; it; it = it->m_next) - ++cnt; - return cnt; - } - void replaceAllUsesWith(Node *replacement); - void replaceUses(Node *newValueInput, Node *newEffectInput, Node *newControlInput); - - Node *firstValueUse(); - -private: // types and utility methods - friend class Use; - Node(MemoryPool *pool, Id id, const Operation *op, unsigned nInputs, int capacity); - -private: // fields - Use *m_firstUse = nullptr; - const Operation *m_op = nullptr; - QQmlJS::FixedPoolArray<Use> m_inputs; - unsigned m_nInputs = 0; - Id m_id = 0; -}; - -void Use::set(Node *newInput) -{ - if (m_input) - removeFromList(); - m_input = newInput; - if (newInput) - addToList(&newInput->m_firstUse); -} - -class Node::Inputs final -{ -public: - using value_type = Node *; - - class const_iterator; - inline const_iterator begin() const; - inline const_iterator end() const; - - bool empty() const - { return m_nInputs == 0; } - - unsigned count() const - { return m_nInputs; } - - explicit Inputs(const Use *inputs, unsigned nInputs) - : m_inputs(inputs), m_nInputs(nInputs) - {} - -private: - const Use *m_inputs = nullptr; - unsigned m_nInputs = 0; -}; - -class Node::Inputs::const_iterator final -{ -public: - using iterator_category = std::forward_iterator_tag; - using difference_type = std::ptrdiff_t; - using value_type = Node *; - using pointer = const value_type *; - using reference = value_type &; - - Node *operator*() const - { return m_inputs->m_input; } - - bool operator==(const const_iterator &other) const - { return m_inputs == other.m_inputs; } - - bool operator!=(const const_iterator &other) const - { return !(*this == other); } - - const_iterator &operator++() - { ++m_inputs; return *this; } - - const_iterator& operator+=(difference_type offset) - { m_inputs += offset; return *this; } - - const_iterator operator+(difference_type offset) const - { return const_iterator(m_inputs + offset); } - - difference_type operator-(const const_iterator &other) const - { return m_inputs - other.m_inputs; } - -private: - friend class Node::Inputs; - - explicit const_iterator(const Use *inputs) - : m_inputs(inputs) - {} - - const Use *m_inputs; -}; - -Node::Inputs::const_iterator Node::Inputs::begin() const -{ return const_iterator(m_inputs); } - -Node::Inputs::const_iterator Node::Inputs::end() const -{ return const_iterator(m_inputs + m_nInputs); } - -Node::Inputs Node::inputs() const -{ - return Inputs(m_inputs.begin(), m_nInputs); -} - -class Node::Uses::const_iterator final -{ -public: - using iterator_category = std::forward_iterator_tag; - using difference_type = int; - using value_type = Node *; - using pointer = Node **; - using reference = Node *&; - - Node *operator*() const - { return m_current->user(); } - - bool operator==(const const_iterator &other) const - { return other.m_current == m_current; } - - bool operator!=(const const_iterator &other) const - { return other.m_current != m_current; } - - const_iterator &operator++() - { m_current = m_next; setNext(); return *this; } - - unsigned inputIndex() const - { return m_current->inputIndex(); } - - bool isUsedAsValue() const - { return inputIndex() < operator*()->operation()->valueInputCount(); } - - bool isUsedAsControl() const - { return operator*()->operation()->indexOfFirstControl() <= inputIndex(); } - -private: - friend class Node::Uses; - - const_iterator() = default; - - explicit const_iterator(Node* node) - : m_current(node->m_firstUse) - { setNext(); } - - void setNext() - { - if (m_current) - m_next = m_current->m_next; - else - m_next = nullptr; - } - -private: - Use *m_current = nullptr; - Use *m_next = nullptr; -}; - -Node::Uses::const_iterator Node::Uses::begin() const -{ return const_iterator(this->m_node); } - -Node::Uses::const_iterator Node::Uses::end() const -{ return const_iterator(); } - -int Use::inputIndex() const -{ - if (!m_user) - return -1; - return int(this - m_user->m_inputs.begin()); -} - -bool Node::isDead() const -{ - Inputs in = inputs(); - return !in.empty() && *in.begin() == nullptr; -} - -void Node::kill() -{ - removeAllInputs(); -} - -class NodeWorkList final -{ - enum State: uint8_t { - Unvisited = 0, - Queued, - Visited, - }; - -public: - NodeWorkList(const Graph *g); - - void reset(); - - bool enqueue(Node *n) - { - State &s = nodeState(n); - if (s == Queued || s == Visited) - return false; - - m_worklist.push_back(n); - s = Queued; - return true; - } - - void enqueue(const std::vector<Node *> &nodes) - { - m_worklist.insert(m_worklist.end(), nodes.begin(), nodes.end()); - for (Node *n : nodes) - nodeState(n) = Queued; - } - - void reEnqueue(Node *n) - { - if (!n) - return; - State &s = nodeState(n); - if (s == Queued) - return; - s = Queued; - m_worklist.push_back(n); - } - - void enqueueAllInputs(Node *n) - { - for (Node *input : n->inputs()) - enqueue(input); - } - - void reEnqueueAllInputs(Node *n) - { - for (Node *input : n->inputs()) - reEnqueue(input); - } - - void enqueueValueInputs(Node *n) - { - for (unsigned i = 0, ei = n->operation()->valueInputCount(); i != ei; ++i) - enqueue(n->input(i)); - } - - void enqueueEffectInputs(Node *n) - { - for (unsigned i = n->operation()->indexOfFirstEffect(), ei = n->operation()->effectInputCount(); i != ei; ++i) - enqueue(n->input(i)); - } - - void enqueueAllUses(Node *n) - { - for (Node *use : n->uses()) - enqueue(use); - } - - Node *dequeueNextNodeForVisiting() - { - while (!m_worklist.empty()) { - Node *n = m_worklist.back(); - m_worklist.pop_back(); - State &s = nodeState(n); - Q_ASSERT(s == Queued); - s = Visited; - return n; - } - - return nullptr; - } - - bool isVisited(Node *n) const - { return nodeState(n) == Visited; } - - bool isEmpty() const - { return m_worklist.empty(); } - - QString status(Node *n) const - { - QString s = QStringLiteral("status for node %1: ").arg(n->id()); - switch (nodeState(n)) { - case Queued: s += QLatin1String("queued"); break; - case Visited: s += QLatin1String("visited"); break; - case Unvisited: s += QLatin1String("unvisited"); break; - } - return s; - } - -private: - State &nodeState(Node *n) - { - const unsigned position(n->id()); - if (position >= m_nodeState.size()) - m_nodeState.resize(position + 1, Unvisited); - - return m_nodeState[position]; - } - - State nodeState(Node *n) const - { return m_nodeState[unsigned(n->id())]; } - -private: - std::vector<Node *> m_worklist; - std::vector<State> m_nodeState; -}; - -class NodeInfo -{ -public: - enum { NoInstructionOffset = -1 }; - -public: - NodeInfo() = default; - - Type type() const { return m_type; } - void setType(Type t) { m_type = t; } - - int currentInstructionOffset() const - { return m_currentInstructionOffset; } - - int nextInstructionOffset() const - { return m_nextInstructionOffset; } - - void setBytecodeOffsets(int current, int next) - { - Q_ASSERT(current != NoInstructionOffset); - Q_ASSERT(next != NoInstructionOffset); - m_currentInstructionOffset = current; - m_nextInstructionOffset = next; - } - -private: - Type m_type; - int m_currentInstructionOffset = NoInstructionOffset; - int m_nextInstructionOffset = NoInstructionOffset; -}; - -class NodeCollector -{ -public: - NodeCollector(const Graph *g, bool collectUses = false, bool skipFramestate = false); - - const std::vector<Node *> &reachable() const - { return m_reachable; } - - void sortById() - { - std::sort(m_reachable.begin(), m_reachable.end(), [](Node *n1, Node *n2) { - return n1->id() < n2->id(); - }); - } - - bool isReachable(Node::Id nodeId) const - { - if (nodeId >= Node::Id(m_isReachable.size())) - return false; - return m_isReachable.at(int(nodeId)); - } - - void markReachable(Node *node) - { - auto nodeId = node->id(); - m_reachable.push_back(node); - if (nodeId >= Node::Id(m_isReachable.size())) - m_isReachable.resize(int(nodeId + 1), false); - m_isReachable.setBit(int(nodeId)); - } - -private: - std::vector<Node *> m_reachable; - BitVector m_isReachable; -}; - -} // namespace IR -} // namespace QV4 - -QT_END_NAMESPACE - -#endif // QV4NODE_P_H diff --git a/src/qml/jit/qv4operation.cpp b/src/qml/jit/qv4operation.cpp deleted file mode 100644 index acd5328fd0..0000000000 --- a/src/qml/jit/qv4operation.cpp +++ /dev/null @@ -1,770 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4operation_p.h" -#include "qv4runtimesupport_p.h" - -QT_BEGIN_NAMESPACE -namespace QV4 { -namespace IR { - -OperationBuilder::OperationBuilder(QQmlJS::MemoryPool *graphPool) - : m_graphPool(graphPool) -{} - -OperationBuilder *OperationBuilder::create(QQmlJS::MemoryPool *pool) -{ - return pool->New<OperationBuilder>(pool); -} - -Operation *OperationBuilder::getConstant(Value v) -{ - Type t; - switch (v.type()) { - case Value::Undefined_Type: t = Type::undefinedType(); break; - case Value::Integer_Type: t = Type::int32Type(); break; - case Value::Boolean_Type: t = Type::booleanType(); break; - case Value::Null_Type: t = Type::nullType(); break; - case Value::Double_Type: t = Type::doubleType(); break; - case Value::Managed_Type: t = Type::objectType(); break; - default: - if (v.isEmpty()) - t = Type::emptyType(); - else - Q_UNREACHABLE(); - } - return OperationWithPayload<ConstantPayload>::create( - m_graphPool, Meta::Constant, 0, 0, 0, 1, 0, 0, t, Operation::NoFlags, - ConstantPayload(v)); -} - -Operation *OperationBuilder::getParam(unsigned index, const Function::StringId name) -{ - return OperationWithPayload<ParameterPayload>::create(m_graphPool, Meta::Parameter, - 1, 0, 0, - 1, 0, 0, - Type::anyType(), Operation::NoFlags, - ParameterPayload(index, name)); -} - -Operation *OperationBuilder::getRegion(unsigned nControlInputs) //### cache common operands in the static pool -{ - return Operation::create(m_graphPool, Meta::Region, - 0, 0, uint16_t(nControlInputs), - 0, 0, 1, - Type(), Operation::NoFlags); -} - -Operation *OperationBuilder::getPhi(unsigned nValueInputs) //### cache common operands in the static pool -{ - return Operation::create(m_graphPool, Meta::Phi, - uint16_t(nValueInputs), 0, 1, - 1, 0, 0, - Type::anyType(), Operation::NoFlags); -} - -Operation *OperationBuilder::getEffectPhi(unsigned nEffectInputs) //### cache common operands in the static pool -{ - return Operation::create(m_graphPool, Meta::EffectPhi, - 0, uint16_t(nEffectInputs), 1, - 0, 1, 0, - Type(), Operation::NoFlags); -} - -Operation *OperationBuilder::getUnwindDispatch(unsigned nContinuations, int unwindHandlerOffset, - int fallthroughSuccessor) -{ - return OperationWithPayload<UnwindDispatchPayload>::create( - m_graphPool, Meta::UnwindDispatch, - 0, 1, 1, 0, nContinuations, nContinuations, - Type(), Operation::NoFlags, - UnwindDispatchPayload(unwindHandlerOffset, - fallthroughSuccessor)); -} - -Operation *OperationBuilder::getHandleUnwind(int unwindHandlerOffset) -{ - return OperationWithPayload<HandleUnwindPayload>::create(m_graphPool, Meta::HandleUnwind, - 0, 1, 1, 0, 1, 1, - Type(), Operation::NoFlags, - HandleUnwindPayload(unwindHandlerOffset)); -} - -Operation *OperationBuilder::getFrameState(uint16_t frameSize) -{ - if (m_opFrameState == nullptr) - m_opFrameState = Operation::create(m_graphPool, Meta::FrameState, - frameSize, 0, 0, 0, 0, 1, - Type(), Operation::NoFlags); - else - Q_ASSERT(frameSize == m_opFrameState->valueInputCount()); - - return m_opFrameState; -} - -Operation *OperationBuilder::getStart(uint16_t outputCount) -{ - return Operation::create(m_graphPool, Meta::Start, - 0, 0, 0, - outputCount, 1, 1, - Type(), Operation::NoFlags); -} - -Operation *OperationBuilder::getEnd(uint16_t controlInputCount) -{ - return Operation::create(m_graphPool, Meta::End, - 0, 0, controlInputCount, - 0, 0, 0, - Type(), Operation::NoFlags); -} - -inline Operation *createOperation(Operation::Kind kind, QQmlJS::MemoryPool *staticPool) -{ - auto get = [&](uint16_t inValueCount, uint16_t inEffectCount, uint16_t inControlCount, - uint16_t outValueCount, uint16_t outEffectCount, uint16_t outControlCount, - Type (*typeCreator)(), uint8_t flags) { - return Operation::create(staticPool, kind, inValueCount, inEffectCount, inControlCount, - outValueCount, outEffectCount, outControlCount, typeCreator(), - flags); - }; - - using K = Operation::Kind; - using F = Operation::Flags; - const auto none = &Type::noneType; - const auto any = &Type::anyType; - const auto number = &Type::numberType; - const auto boolean = &Type::booleanType; - - switch (kind) { - case K::Undefined: - return OperationWithPayload<ConstantPayload>::create( - staticPool, K::Undefined, 0, 0, 0, 1, 0, 0, Type::undefinedType(), F::NoFlags, - ConstantPayload(Primitive::undefinedValue())); - case K::Empty: - return OperationWithPayload<ConstantPayload>::create( - staticPool, K::Constant, 0, 0, 0, 1, 0, 0, Type::emptyType(), Operation::NoFlags, - ConstantPayload(Primitive::emptyValue())); - case K::Engine: return get(1, 0, 0, 1, 0, 0, none, F::NoFlags); - case K::CppFrame: return get(1, 0, 0, 1, 0, 0, none, F::NoFlags); - case K::Function: return get(1, 0, 0, 1, 0, 0, none, F::NoFlags); - case K::Jump: return get(0, 0, 1, 0, 0, 1, none, F::NoFlags); - case K::Return: return get(1, 1, 1, 0, 0, 1, none, F::NoFlags); - case K::Branch: return get(1, 0, 1, 0, 0, 2, none, F::HasFrameStateInput | F::NeedsBytecodeOffsets); - case K::IfTrue: return get(0, 0, 1, 0, 0, 1, none, F::NoFlags); - case K::IfFalse: return get(0, 0, 1, 0, 0, 1, none, F::NoFlags); - case K::SelectOutput: return get(3, 1, 1, 1, 1, 1, any, F::NoFlags); - case K::Throw: return get(1, 1, 1, 0, 1, 1, any, F::NeedsBytecodeOffsets); - case K::OnException: return get(0, 0, 1, 0, 0, 1, none, F::NoFlags); - case K::ThrowReferenceError: return get(1, 1, 1, 0, 1, 1, any, F::NeedsBytecodeOffsets); - case K::UnwindToLabel: return get(2, 1, 1, 0, 1, 1, none, F::NoFlags); - case K::LoadRegExp: return get(1, 0, 0, 1, 0, 0, any, F::NoFlags); - case K::ScopedLoad: return get(2, 1, 0, 1, 1, 0, any, F::NoFlags); - case K::ScopedStore: return get(3, 1, 0, 0, 1, 0, none, F::NoFlags); - case K::JSLoadElement: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow); - case K::JSGetLookup: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow); - case K::JSLoadProperty: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow); - case K::JSStoreElement: return get(3, 1, 1, 0, 1, 2, none, F::CanThrow); - case K::JSSetLookupStrict: return get(3, 1, 1, 0, 1, 2, none, F::CanThrow); - case K::JSSetLookupSloppy: return get(3, 1, 1, 0, 1, 2, none, F::CanThrow); - case K::JSStoreProperty: return get(3, 1, 1, 0, 1, 2, none, F::CanThrow); - case K::JSLoadName: return get(1, 1, 1, 1, 1, 2, any, F::CanThrow); - case K::JSLoadGlobalLookup: return get(1, 1, 1, 1, 1, 2, any, F::CanThrow); - case K::JSStoreNameSloppy: return get(2, 1, 1, 0, 1, 2, none, F::CanThrow); - case K::JSStoreNameStrict: return get(2, 1, 1, 0, 1, 2, none, F::CanThrow); - case K::JSLoadSuperProperty: return get(1, 1, 1, 1, 1, 2, any, F::CanThrow); - case K::JSStoreSuperProperty: return get(2, 1, 1, 0, 1, 2, any, F::CanThrow); - case K::JSLoadClosure: return get(1, 1, 0, 1, 1, 0, any, F::Pure); - case K::JSGetIterator: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow); - - // special case: see GraphBuilder::generate_IteratorNext - case K::JSIteratorNext: return get(2, 1, 1, 2, 1, 1, any, F::NoFlags); - - // special case: see GraphBuilder::generate_IteratorNext - case K::JSIteratorNextForYieldStar: return get(3, 1, 1, 2, 1, 1, any, F::NoFlags); - - case K::JSIteratorClose: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow); - case K::JSDeleteProperty: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow); - case K::JSDeleteName: return get(1, 1, 1, 1, 1, 2, any, F::CanThrow); - case K::JSIn: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow); - case K::JSInstanceOf: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow); - case K::QMLLoadQmlContextPropertyLookup: return get(1, 1, 1, 1, 1, 2, any, F::CanThrow); - - case K::JSEqual: return get(2, 1, 1, 1, 1, 2, boolean, F::CanThrow); - case K::JSGreaterThan: return get(2, 1, 1, 1, 1, 2, boolean, F::CanThrow); - case K::JSGreaterEqual: return get(2, 1, 1, 1, 1, 2, boolean, F::CanThrow); - case K::JSLessThan: return get(2, 1, 1, 1, 1, 2, boolean, F::CanThrow); - case K::JSLessEqual: return get(2, 1, 1, 1, 1, 2, boolean, F::CanThrow); - case K::JSStrictEqual: return get(2, 1, 1, 1, 1, 2, boolean, F::CanThrow); - - case K::JSAdd: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow); - case K::JSSubtract: return get(2, 1, 1, 1, 1, 2, number, F::CanThrow); - case K::JSMultiply: return get(2, 1, 1, 1, 1, 2, number, F::CanThrow); - case K::JSDivide: return get(2, 1, 1, 1, 1, 2, number, F::CanThrow); - case K::JSModulo: return get(2, 1, 1, 1, 1, 2, number, F::CanThrow); - case K::JSExponentiate: return get(2, 1, 1, 1, 1, 2, number, F::CanThrow); - - case K::JSBitAnd: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow); - case K::JSBitOr: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow); - case K::JSBitXor: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow); - case K::JSUnsignedShiftRight: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow); - case K::JSShiftRight: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow); - case K::JSShiftLeft: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow); - - case K::JSNegate: return get(1, 1, 1, 1, 1, 2, number, F::CanThrow); - case K::JSToNumber: return get(1, 1, 1, 1, 1, 2, number, F::CanThrow); - case K::Alloca: return get(1, 0, 0, 1, 0, 0, none, F::NoFlags); - - //### it is questionable if VAAlloc/VASeal need effect edges - case K::VAAlloc: return get(1, 1, 0, 1, 1, 0, none, F::NoFlags); - - case K::VAStore: return get(3, 0, 0, 1, 0, 0, none, F::NoFlags); - - case K::JSTypeofName: return get(1, 1, 0, 1, 1, 0, any, F::NoFlags); - case K::JSTypeofValue: return get(1, 0, 0, 1, 0, 0, any, F::Pure); - case K::JSDeclareVar: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow); - case K::JSDestructureRestElement: return get(1, 1, 1, 1, 1, 2, any, F::CanThrow); - - case K::JSCreateCallContext: return get(0, 1, 1, 0, 1, 1, none, F::NoFlags); - case K::JSCreateCatchContext: return get(2, 1, 1, 1, 1, 1, none, F::NoFlags); - case K::JSCreateWithContext: return get(1, 1, 1, 1, 1, 1, any, F::NoFlags); - case K::JSCreateBlockContext: return get(1, 1, 1, 1, 1, 1, none, F::NoFlags); - case K::JSCloneBlockContext: return get(0, 1, 1, 0, 1, 1, none, F::NoFlags); - case K::JSCreateScriptContext: return get(1, 1, 1, 1, 1, 1, none, F::NoFlags); - case K::JSPopScriptContext: return get(0, 1, 1, 1, 1, 1, none, F::NoFlags); - case K::PopContext: return get(0, 1, 1, 0, 1, 1, none, F::NoFlags); - - case K::JSThisToObject: return get(1, 1, 1, 0, 1, 2, any, F::NoFlags); - case K::JSCreateMappedArgumentsObject: return get(0, 1, 0, 1, 1, 0, any, F::NoFlags); - case K::JSCreateUnmappedArgumentsObject: return get(0, 1, 0, 1, 1, 0, any, F::NoFlags); - case K::JSCreateRestParameter: return get(1, 0, 0, 1, 0, 0, any, F::NoFlags); - case K::JSLoadSuperConstructor: return get(1, 1, 1, 1, 1, 2, any, F::NoFlags); - case K::JSThrowOnNullOrUndefined: return get(1, 1, 1, 0, 1, 2, none, F::CanThrow); - case K::JSGetTemplateObject: return get(1, 0, 0, 1, 0, 0, any, F::NoFlags); - case K::StoreThis: return get(1, 1, 0, 1, 1, 0, any, F::NoFlags); - - case K::GetException: return get(0, 1, 0, 1, 1, 0, any, F::NoFlags); - case K::SetException: return get(1, 1, 0, 0, 1, 0, any, F::NoFlags); - - case K::ToObject: return get(1, 1, 1, 1, 1, 2, any, F::CanThrow); - case K::ToBoolean: return get(1, 0, 0, 1, 0, 0, boolean, F::Pure); - - case K::IsEmpty: return get(1, 0, 0, 1, 0, 0, boolean, F::Pure); - - case K::BooleanNot: return get(1, 0, 0, 1, 0, 0, boolean, F::NoFlags); - case K::HasException: return get(1, 1, 0, 1, 1, 0, boolean, F::NoFlags); - - case K::Swap: return get(0, 0, 0, 0, 0, 0, none, F::NoFlags); - case K::Move: return get(1, 0, 0, 1, 0, 0, none, F::NoFlags); - - default: // Non-static operations: - return nullptr; - } -} - -Operation *OperationBuilder::staticOperation(Operation::Kind kind) -{ - static QAtomicPointer<Operation *> ops; - if (Operation **staticOps = ops.load()) - return staticOps[kind]; - - static QAtomicInt initializing = 0; - if (initializing.testAndSetOrdered(0, 1)) { - // This is safe now, because we can only run this piece of code once during the life time - // of the application as we can only change initializing from 0 to 1 once. - Operation **staticOps = new Operation *[Meta::KindsEnd]; - static QQmlJS::MemoryPool pool; - for (int i = 0; i < Meta::KindsEnd; ++i) - staticOps[i] = createOperation(Operation::Kind(i), &pool); - bool success = ops.testAndSetOrdered(nullptr, staticOps); - Q_ASSERT(success); - } else { - // Unfortunately we need to busy wait now until the other thread finishes the static - // initialization; - while (!ops.load()) {} - } - - return ops.load()[kind]; -} - -Operation *OperationBuilder::getJSVarArgsCall(Operation::Kind kind, uint16_t argc) -{ - return Operation::create(m_graphPool, kind, - argc, 1, 1, 1, 1, 2, - Type::anyType(), Operation::CanThrow); -} - -Operation *OperationBuilder::getJSTailCall(uint16_t argc) -{ - return Operation::create(m_graphPool, Meta::JSTailCall, - argc, 1, 1, 0, 0, 1, - Type(), Operation::NoFlags); -} - -Operation *OperationBuilder::getTailCall() -{ - // special varargs call, takes cppframe, engine, func, thisObject, argv, argc - return Operation::create(m_graphPool, Meta::TailCall, - 6, 1, 1, 0, 0, 1, - Type(), Operation::NoFlags); -} - -Operation *OperationBuilder::getCall(Operation::Kind callee) -{ - const bool canThrow = CallPayload::canThrow(callee); - const Type retTy = CallPayload::returnType(callee); - uint16_t nControlInputs = 0; - uint16_t nControlOutputs = 0; - if (canThrow) { - nControlInputs = 1; - nControlOutputs += 2; - } - if (CallPayload::changesContext(callee)) { - nControlInputs = 1; - nControlOutputs = std::max<uint16_t>(nControlInputs, 1); - } - if (callee == Meta::Throw || callee == Meta::ThrowReferenceError || - callee == Meta::JSIteratorNext || callee == Meta::JSIteratorNextForYieldStar) { - nControlInputs = 1; - nControlOutputs = 1; - } - Operation::Flags flags = Operation::NoFlags; - if (canThrow) - flags = Operation::Flags(flags | Operation::CanThrow); - if (CallPayload::isPure(callee)) - flags = Operation::Flags(flags | Operation::Pure); - const uint16_t nEffects = (flags & Operation::Pure) ? 0 : 1; - const uint16_t nValueOutputs = retTy.isNone() ? 0 : 1; - const uint16_t nValueInputs = CallPayload::argc(callee); - - return OperationWithPayload<CallPayload>::create( - m_graphPool, Meta::Call, - nValueInputs, nEffects, nControlInputs, - nValueOutputs, nEffects, nControlOutputs, - retTy, flags, - CallPayload(callee)); -} - -Operation *OperationBuilder::getVASeal(uint16_t nElements) -{ - return Operation::create(m_graphPool, Meta::VASeal, - nElements + 1, 1, 0, 1, 1, 0, - Type::anyType(), Operation::NoFlags); -} - -QString Operation::debugString() const -{ - switch (kind()) { - - case Meta::Constant: - return QStringLiteral("Constant[%1]").arg(ConstantPayload::get(*this)->debugString()); - case Meta::Parameter: - return QStringLiteral("Parameter[%1]").arg(ParameterPayload::get(*this)->debugString()); - case Meta::Call: - return QStringLiteral("Call[%1]").arg(CallPayload::get(*this)->debugString()); - case Meta::UnwindDispatch: - return QStringLiteral("UnwindDispatch[%1]").arg(UnwindDispatchPayload::get(*this) - ->debugString()); - case Meta::HandleUnwind: - return QStringLiteral("HandleUnwind[%1]").arg(HandleUnwindPayload::get(*this) - ->debugString()); - - default: - return QString::fromLatin1(QMetaEnum::fromType<Meta::OpKind>().valueToKey(kind())); - } -} - -QString ConstantPayload::debugString() const -{ - return debugString(m_value); -} - -QString ConstantPayload::debugString(QV4::Value v) -{ - if (v.isManaged()) - return QString::asprintf("Ptr: %p", v.heapObject()); - if (v.isEmpty()) - return QStringLiteral("empty"); - return v.toQStringNoThrow(); -} - -QString ParameterPayload::debugString() const -{ - return QStringLiteral("%1").arg(m_index); -} - -QString UnwindDispatchPayload::debugString() const -{ - return QStringLiteral("%1, %2").arg(QString::number(m_fallthroughSuccessor), - QString::number(m_unwindHandlerOffset)); -} - -QString HandleUnwindPayload::debugString() const -{ - return QStringLiteral("%1").arg(m_unwindHandlerOffset); -} - -static Type translateType(RuntimeSupport::ArgumentType t) -{ - switch (t) { - case RuntimeSupport::ArgumentType::Int: return Type::int32Type(); - case RuntimeSupport::ArgumentType::Bool: return Type::booleanType(); - case RuntimeSupport::ArgumentType::Void: return Type(); - case RuntimeSupport::ArgumentType::Engine: return Type::rawPointerType(); - case RuntimeSupport::ArgumentType::ValueRef: return Type::anyType(); - case RuntimeSupport::ArgumentType::ValueArray: return Type::anyType(); - case RuntimeSupport::ArgumentType::ReturnedValue: return Type::anyType(); - default: Q_UNREACHABLE(); - } -} - -template<template<typename Operation> class M /* MetaOperation */, typename ReturnValue> -static ReturnValue operateOnRuntimeCall(Operation::Kind kind, bool abortOnMissingCall = true) -{ - using K = Operation::Kind; - using R = Runtime; - - switch (kind) { - case K::Throw: return M<R::ThrowException>::doIt(); - case K::ThrowReferenceError: return M<R::ThrowReferenceError>::doIt(); - - case K::JSEqual: return M<R::CompareEqual>::doIt(); - case K::JSGreaterThan: return M<R::CompareGreaterThan>::doIt(); - case K::JSGreaterEqual: return M<R::CompareGreaterEqual>::doIt(); - case K::JSLessThan: return M<R::CompareLessThan>::doIt(); - case K::JSLessEqual: return M<R::CompareLessEqual>::doIt(); - case K::JSStrictEqual: return M<R::CompareStrictEqual>::doIt(); - - case K::JSBitAnd: return M<R::BitAnd>::doIt(); - case K::JSBitOr: return M<R::BitOr>::doIt(); - case K::JSBitXor: return M<R::BitXor>::doIt(); - case K::JSUnsignedShiftRight: return M<R::UShr>::doIt(); - case K::JSShiftRight: return M<R::Shr>::doIt(); - case K::JSShiftLeft: return M<R::Shl>::doIt(); - - case K::JSAdd: return M<R::Add>::doIt(); - case K::JSSubtract: return M<R::Sub>::doIt(); - case K::JSMultiply: return M<R::Mul>::doIt(); - case K::JSDivide: return M<R::Div>::doIt(); - case K::JSModulo: return M<R::Mod>::doIt(); - case K::JSExponentiate: return M<R::Exp>::doIt(); - - case K::ToBoolean: return M<R::ToBoolean>::doIt(); - case K::ToObject: return M<R::ToObject>::doIt(); - - case K::JSNegate: return M<R::UMinus>::doIt(); - case K::JSToNumber: return M<R::ToNumber>::doIt(); - - case K::JSLoadName: return M<R::LoadName>::doIt(); - case K::JSLoadElement: return M<R::LoadElement>::doIt(); - case K::JSStoreElement: return M<R::StoreElement>::doIt(); - case K::JSGetLookup: return M<R::GetLookup>::doIt(); - case K::JSSetLookupStrict: return M<R::SetLookupStrict>::doIt(); - case K::JSSetLookupSloppy: return M<R::SetLookupSloppy>::doIt(); - case K::JSLoadProperty: return M<R::LoadProperty>::doIt(); - case K::JSStoreProperty: return M<R::StoreProperty>::doIt(); - case K::JSLoadGlobalLookup: return M<R::LoadGlobalLookup>::doIt(); - case K::JSStoreNameSloppy: return M<R::StoreNameSloppy>::doIt(); - case K::JSStoreNameStrict: return M<R::StoreNameStrict>::doIt(); - case K::JSLoadSuperProperty: return M<R::LoadSuperProperty>::doIt(); - case K::JSStoreSuperProperty: return M<R::StoreSuperProperty>::doIt(); - case K::JSLoadClosure: return M<R::Closure>::doIt(); - case K::JSGetIterator: return M<R::GetIterator>::doIt(); - case K::JSIteratorNext: return M<R::IteratorNext>::doIt(); - case K::JSIteratorNextForYieldStar: return M<R::IteratorNextForYieldStar>::doIt(); - case K::JSIteratorClose: return M<R::IteratorClose>::doIt(); - case K::JSDeleteProperty: return M<R::DeleteProperty>::doIt(); - case K::JSDeleteName: return M<R::DeleteName>::doIt(); - case K::JSIn: return M<R::In>::doIt(); - case K::JSInstanceOf: return M<R::Instanceof>::doIt(); - case K::QMLLoadQmlContextPropertyLookup: return M<R::LoadQmlContextPropertyLookup>::doIt(); - - case K::JSTypeofName: return M<R::TypeofName>::doIt(); - case K::JSTypeofValue: return M<R::TypeofValue>::doIt(); - case K::JSDeclareVar: return M<R::DeclareVar>::doIt(); - case K::JSDestructureRestElement: return M<R::DestructureRestElement>::doIt(); - case K::JSThisToObject: return M<R::ConvertThisToObject>::doIt(); - case K::JSCreateMappedArgumentsObject: return M<R::CreateMappedArgumentsObject>::doIt(); - case K::JSCreateUnmappedArgumentsObject: return M<R::CreateUnmappedArgumentsObject>::doIt(); - case K::JSCreateRestParameter: return M<R::CreateRestParameter>::doIt(); - case K::JSLoadSuperConstructor: return M<R::LoadSuperConstructor>::doIt(); - case K::JSThrowOnNullOrUndefined: return M<R::ThrowOnNullOrUndefined>::doIt(); - - case K::JSCreateCallContext: return M<R::PushCallContext>::doIt(); - case K::JSCreateCatchContext: return M<R::PushCatchContext>::doIt(); - case K::JSCreateWithContext: return M<R::PushWithContext>::doIt(); - case K::JSCreateBlockContext: return M<R::PushBlockContext>::doIt(); - case K::JSCloneBlockContext: return M<R::CloneBlockContext>::doIt(); - case K::JSCreateScriptContext: return M<R::PushScriptContext>::doIt(); - case K::JSPopScriptContext: return M<R::PopScriptContext>::doIt(); - - case K::LoadRegExp: return M<R::RegexpLiteral>::doIt(); - case K::JSGetTemplateObject: return M<R::GetTemplateObject>::doIt(); - - case K::JSCallName: return M<R::CallName>::doIt(); - case K::JSCallValue: return M<R::CallValue>::doIt(); - case K::JSCallElement: return M<R::CallElement>::doIt(); - case K::JSCallLookup: return M<R::CallPropertyLookup>::doIt(); - case K::JSCallProperty: return M<R::CallProperty>::doIt(); - case K::JSCallGlobalLookup: return M<R::CallGlobalLookup>::doIt(); - case K::JSCallPossiblyDirectEval: return M<R::CallPossiblyDirectEval>::doIt(); - case K::JSCallWithReceiver: return M<R::CallWithReceiver>::doIt(); - case K::JSDefineObjectLiteral: return M<R::ObjectLiteral>::doIt(); - case K::JSDefineArray: return M<R::ArrayLiteral>::doIt(); - case K::JSCallWithSpread: return M<R::CallWithSpread>::doIt(); - case K::JSConstruct: return M<R::Construct>::doIt(); - case K::JSConstructWithSpread: return M<R::ConstructWithSpread>::doIt(); - case K::JSTailCall: return M<R::TailCall>::doIt(); - case K::JSCreateClass: return M<R::CreateClass>::doIt(); - default: - if (abortOnMissingCall) - Q_UNREACHABLE(); - else - return ReturnValue(); - } -} - -template<typename Method> -struct IsRuntimeMethodOperation -{ - static constexpr bool doIt() { return true; } -}; - -bool CallPayload::isRuntimeCall(Operation::Kind m) -{ - return operateOnRuntimeCall<IsRuntimeMethodOperation, bool>(m, false); -} - -QString CallPayload::debugString() const -{ - return QString::fromLatin1(QMetaEnum::fromType<Meta::OpKind>().valueToKey(m_callee)); -} - -template<typename Method> -struct MethodArgcOperation -{ - static constexpr unsigned doIt() { return RuntimeSupport::argumentCount<Method>(); } -}; - -unsigned CallPayload::argc(Operation::Kind callee) -{ - return operateOnRuntimeCall<MethodArgcOperation, unsigned>(callee); -} - -template<typename Method> struct MethodArg1TyOperation { static constexpr RuntimeSupport::ArgumentType doIt() { return RuntimeSupport::arg1Type<Method>(); } }; -template<typename Method> struct MethodArg2TyOperation { static constexpr RuntimeSupport::ArgumentType doIt() { return RuntimeSupport::arg2Type<Method>(); } }; -template<typename Method> struct MethodArg3TyOperation { static constexpr RuntimeSupport::ArgumentType doIt() { return RuntimeSupport::arg3Type<Method>(); } }; -template<typename Method> struct MethodArg4TyOperation { static constexpr RuntimeSupport::ArgumentType doIt() { return RuntimeSupport::arg4Type<Method>(); } }; -template<typename Method> struct MethodArg5TyOperation { static constexpr RuntimeSupport::ArgumentType doIt() { return RuntimeSupport::arg5Type<Method>(); } }; -template<typename Method> struct MethodArg6TyOperation { static constexpr RuntimeSupport::ArgumentType doIt() { return RuntimeSupport::arg6Type<Method>(); } }; - -static RuntimeSupport::ArgumentType untranslatedArgumentType(Operation::Kind m, unsigned arg) -{ - if (m == Meta::JSTailCall) { - if (arg < 4) - return RuntimeSupport::ArgumentType::ValueRef; - else - return RuntimeSupport::ArgumentType::Invalid; - } - - switch (arg) { - case 0: return operateOnRuntimeCall<MethodArg1TyOperation, RuntimeSupport::ArgumentType>(m); - case 1: return operateOnRuntimeCall<MethodArg2TyOperation, RuntimeSupport::ArgumentType>(m); - case 2: return operateOnRuntimeCall<MethodArg3TyOperation, RuntimeSupport::ArgumentType>(m); - case 3: return operateOnRuntimeCall<MethodArg4TyOperation, RuntimeSupport::ArgumentType>(m); - case 4: return operateOnRuntimeCall<MethodArg5TyOperation, RuntimeSupport::ArgumentType>(m); - case 5: return operateOnRuntimeCall<MethodArg6TyOperation, RuntimeSupport::ArgumentType>(m); - default: return RuntimeSupport::ArgumentType::Invalid; - } -} - -bool CallPayload::needsStorageOnJSStack(Operation::Kind m, unsigned arg, const Operation *op, - Type nodeType) -{ - auto argTy = untranslatedArgumentType(m, arg); - if (argTy == RuntimeSupport::ArgumentType::ValueArray) - return true; - if (argTy != RuntimeSupport::ArgumentType::ValueRef) - return false; - - if (op->kind() == Meta::Constant) - return true; - - return !nodeType.isObject() && !nodeType.isRawPointer() && !nodeType.isAny(); -} - -template<typename Method> -struct MethodRetTyOperation -{ - static constexpr RuntimeSupport::ArgumentType doIt() { return RuntimeSupport::retType<Method>(); } -}; - -Type CallPayload::returnType(Operation::Kind m) -{ - if (m == Meta::JSTailCall) - return Type(); - - auto t = operateOnRuntimeCall<MethodRetTyOperation, RuntimeSupport::ArgumentType>(m); - return translateType(t); -} - -static int firstArgumentPositionForType(Operation::Kind m, RuntimeSupport::ArgumentType type) -{ - if (operateOnRuntimeCall<MethodArg1TyOperation, RuntimeSupport::ArgumentType>(m) == type) - return 1; - if (operateOnRuntimeCall<MethodArg2TyOperation, RuntimeSupport::ArgumentType>(m) == type) - return 2; - if (operateOnRuntimeCall<MethodArg3TyOperation, RuntimeSupport::ArgumentType>(m) == type) - return 3; - if (operateOnRuntimeCall<MethodArg4TyOperation, RuntimeSupport::ArgumentType>(m) == type) - return 4; - if (operateOnRuntimeCall<MethodArg5TyOperation, RuntimeSupport::ArgumentType>(m) == type) - return 5; - if (operateOnRuntimeCall<MethodArg6TyOperation, RuntimeSupport::ArgumentType>(m) == type) - return 6; - return -1; -} - -unsigned CallPayload::varArgsStart(Operation::Kind m) -{ - if (m == Meta::JSTailCall) - return 4 - 1; - - int pos = firstArgumentPositionForType(m, RuntimeSupport::ArgumentType::ValueArray) - 1; - Q_ASSERT(pos >= 0); - return pos; -} - -bool CallPayload::isVarArgsCall(Operation::Kind m) -{ - if (m == Meta::JSTailCall) - return true; - if (lastArgumentIsOutputValue(m)) - return false; - return firstArgumentPositionForType(m, RuntimeSupport::ArgumentType::ValueArray) != -1; -} - -bool CallPayload::isVarArgsCall() const -{ - return isVarArgsCall(m_callee); -} - -template<typename Method> -struct MethodsLastArgumentIsOutputValue -{ - static constexpr bool doIt() { return Method::lastArgumentIsOutputValue; } -}; - -bool CallPayload::lastArgumentIsOutputValue(Operation::Kind m) -{ - return operateOnRuntimeCall<MethodsLastArgumentIsOutputValue, bool>(m); -} - -template<typename Method> -struct MethodChangesContext -{ - static constexpr bool doIt() { return Method::changesContext; } -}; - -bool CallPayload::changesContext(Operation::Kind m) -{ - return operateOnRuntimeCall<MethodChangesContext, bool>(m); -} - -template<typename Method> -struct MethodIsPure -{ - static constexpr bool doIt() { return Method::pure; } -}; - -bool CallPayload::isPure(Operation::Kind m) -{ - return operateOnRuntimeCall<MethodIsPure, bool>(m); -} - -template<typename Method> -struct MethodCanThrow -{ - static constexpr bool doIt() { return Method::throws; } -}; - -bool CallPayload::canThrow(Operation::Kind m) -{ - switch (m) { - case Meta::Throw: Q_FALLTHROUGH(); - case Meta::ThrowReferenceError: - // the execution path following these instructions is already linked up to the exception handler - return false; - case Meta::JSIteratorNext: Q_FALLTHROUGH(); - case Meta::JSIteratorNextForYieldStar: - // special case: see GraphBuilder::generate_IteratorNext - return false; - default: - return operateOnRuntimeCall<MethodCanThrow, bool>(m); - } -} - -bool CallPayload::takesEngineAsArg(Operation::Kind m, int arg) -{ - return untranslatedArgumentType(m, arg) == RuntimeSupport::ArgumentType::Engine; -} - -bool CallPayload::takesFunctionAsArg(Operation::Kind m, int arg) -{ - return untranslatedArgumentType(m, arg) == RuntimeSupport::ArgumentType::Function; -} - -bool CallPayload::takesFrameAsArg(Operation::Kind m, int arg) -{ - return untranslatedArgumentType(m, arg) == RuntimeSupport::ArgumentType::Frame; -} - -template<typename Method> -struct GetMethodPtr -{ - static constexpr void *doIt() { return reinterpret_cast<void *>(&Method::call); } -}; - -void *CallPayload::getMethodPtr(Operation::Kind m) -{ - return operateOnRuntimeCall<GetMethodPtr, void *>(m); -} - -} // IR namespace -} // QV4 namespace -QT_END_NAMESPACE diff --git a/src/qml/jit/qv4operation_p.h b/src/qml/jit/qv4operation_p.h deleted file mode 100644 index 43214023e8..0000000000 --- a/src/qml/jit/qv4operation_p.h +++ /dev/null @@ -1,567 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QV4OPERATION_P_H -#define QV4OPERATION_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qv4ir_p.h> -#include <private/qqmljsmemorypool_p.h> - -#include <QtCore/qatomic.h> - -QT_REQUIRE_CONFIG(qml_tracing); - -QT_BEGIN_NAMESPACE - -namespace QV4 { -namespace IR { - -namespace Meta { -enum OpKind: uint16_t { - FrameState, - Start, - End, - - Undefined, - Constant, - Parameter, - Empty, - Engine, - CppFrame, - Function, - - Jump, - Return, - JSTailCall, - TailCall, - Branch, - IfTrue, - IfFalse, - Region, - OnException, - Phi, - EffectPhi, - SelectOutput, - UnwindDispatch, - UnwindToLabel, - HandleUnwind, - Throw, - ThrowReferenceError, - - Call, - - LoadRegExp, - ScopedLoad, - ScopedStore, - - JSLoadElement, - JSStoreElement, - JSGetLookup, - JSSetLookupStrict, - JSSetLookupSloppy, - JSLoadProperty, - JSStoreProperty, - JSLoadName, - JSLoadGlobalLookup, - JSStoreNameSloppy, - JSStoreNameStrict, - JSLoadSuperProperty, - JSStoreSuperProperty, - JSLoadClosure, - JSGetIterator, - JSIteratorNext, - JSIteratorNextForYieldStar, - JSIteratorClose, - JSDeleteProperty, - JSDeleteName, - JSIn, - JSInstanceOf, - - /* ok, these are qml object ops, but we don't care for now and treat them as JS */ - QMLLoadQmlContextPropertyLookup, - QMLCallQmlContextPropertyLookup, - - JSEqual, - JSGreaterThan, - JSGreaterEqual, - JSLessThan, - JSLessEqual, - JSStrictEqual, - - JSAdd, - JSSubtract, - JSMultiply, - JSDivide, - JSModulo, - JSExponentiate, - - JSBitAnd, - JSBitOr, - JSBitXor, - JSUnsignedShiftRight, - JSShiftRight, - JSShiftLeft, - - JSNegate, - JSToNumber, - - JSCallName, - JSCallValue, - JSCallElement, - JSCallProperty, - JSCallLookup, - JSCallGlobalLookup, - JSCallPossiblyDirectEval, - JSCallWithReceiver, - JSCallWithSpread, - JSDefineObjectLiteral, - JSDefineArray, - JSCreateClass, - JSConstruct, - JSConstructWithSpread, - - JSTypeofName, - JSTypeofValue, - JSDeclareVar, - JSDestructureRestElement, - JSThisToObject, - JSCreateMappedArgumentsObject, - JSCreateUnmappedArgumentsObject, - JSCreateRestParameter, - JSLoadSuperConstructor, - JSThrowOnNullOrUndefined, - JSGetTemplateObject, - StoreThis, - - JSCreateCallContext, - JSCreateCatchContext, - JSCreateWithContext, - JSCreateBlockContext, - JSCloneBlockContext, - JSCreateScriptContext, - JSPopScriptContext, - PopContext, - - GetException, - SetException, - - ToObject, - ToBoolean, - - //### do we need this? Or should a later phase generate JumpIsEmpty? - IsEmpty, - - Alloca, - VAAlloc, - VAStore, - VASeal, - - BooleanNot, - HasException, - - // Low level, used by the register allocator and stack allocator: - Swap, - Move, - KindsEnd -}; -Q_NAMESPACE -Q_ENUM_NS(OpKind) -} // namespace Ops - -class Operation -{ - Q_DISABLE_COPY_MOVE(Operation) - -public: - using Kind = Meta::OpKind; - - enum Flags: uint8_t { - NoFlags = 0, - ThrowsFlag = 1 << 0, - Pure = 1 << 1, // no read/write side effect, cannot throw, cannot deopt, and is idempotent - NeedsBytecodeOffsets = 1 << 2, - - CanThrow = ThrowsFlag | NeedsBytecodeOffsets, - - HasFrameStateInput = 1 << 3, - }; - -public: - static Operation *create(QQmlJS::MemoryPool *pool, Kind kind, uint16_t inValueCount, - uint16_t inEffectCount, uint16_t inControlCount, - uint16_t outValueCount, uint16_t outEffectCount, - uint16_t outControlCount, Type type, uint8_t flags) - { - return pool->New<Operation>(kind, inValueCount, inEffectCount, inControlCount, - outValueCount, outEffectCount, outControlCount, - type, Flags(flags)); - } - - Kind kind() const - { return m_kind; } - - bool isConstant() const - { - switch (kind()) { - case Meta::Undefined: Q_FALLTHROUGH(); - case Meta::Constant: - case Meta::Empty: - return true; - default: - return false; - } - } - - QString debugString() const; - - uint16_t valueInputCount() const { return m_inValueCount; } - uint16_t effectInputCount() const { return m_inEffectCount; } - uint16_t controlInputCount() const { return m_inControlCount; } - uint16_t valueOutputCount() const { return m_outValueCount; } - uint16_t effectOutputCount() const { return m_outEffectCount; } - uint16_t controlOutputCount() const { return m_outControlCount; } - - unsigned indexOfFirstEffect() const { return m_inValueCount; } - unsigned indexOfFirstControl() const { return m_inValueCount + m_inEffectCount; } - unsigned indexOfFrameStateInput() const - { - return hasFrameStateInput() ? indexOfFirstControl() + m_inControlCount - : std::numeric_limits<unsigned>::max(); - } - - Type type() const - { return m_type; } - - bool canThrow() const - { return m_flags & ThrowsFlag; } - - bool isPure() const - { return m_flags & Pure; } - - bool needsBytecodeOffsets() const - { return m_flags & NeedsBytecodeOffsets; } - - bool hasFrameStateInput() const - { return m_flags & HasFrameStateInput; } - - unsigned totalInputCount() const - { - return valueInputCount() + effectInputCount() + controlInputCount() + - (hasFrameStateInput() ? 1 : 0); - } - unsigned totalOutputCount() const { return valueOutputCount() + effectOutputCount() + controlOutputCount(); } - -protected: - friend class QQmlJS::MemoryPool; - Operation(Kind kind, - uint16_t inValueCount, uint16_t inEffectCount, uint16_t inControlCount, - uint16_t outValueCount, uint16_t outEffectCount, uint16_t outControlCount, - Type type, uint8_t flags) - : m_kind(kind) - , m_inValueCount(inValueCount) - , m_inEffectCount(inEffectCount) - , m_inControlCount(inControlCount) - , m_outValueCount(outValueCount) - , m_outEffectCount(outEffectCount) - , m_outControlCount(outControlCount) - , m_type(type) - , m_flags(Flags(flags)) - { - } - - ~Operation() = default; - -private: - Kind m_kind; - uint16_t m_inValueCount; - uint16_t m_inEffectCount; - uint16_t m_inControlCount; - uint16_t m_outValueCount; - uint16_t m_outEffectCount; - uint16_t m_outControlCount; - Type m_type; - Flags m_flags; -}; - -template <typename Payload> -class OperationWithPayload: public Operation -{ -public: - static OperationWithPayload *create(QQmlJS::MemoryPool *pool, Kind kind, - uint16_t inValueCount, uint16_t inEffectCount, uint16_t inControlCount, - uint16_t outValueCount, uint16_t outEffectCount, uint16_t outControlCount, - Type type, Flags flags, Payload payload) - { - return pool->New<OperationWithPayload>(kind, inValueCount, inEffectCount, inControlCount, - outValueCount, outEffectCount, outControlCount, - type, flags, payload); - } - - const Payload &payload() const - { return m_payload; } - -protected: - friend class QQmlJS::MemoryPool; - OperationWithPayload(Kind kind, - uint16_t inValueCount, uint16_t inEffectCount, uint16_t inControlCount, - uint16_t outValueCount, uint16_t outEffectCount, uint16_t outControlCount, - Type type, Flags flags, Payload payload) - : Operation(kind, - inValueCount, inEffectCount, inControlCount, - outValueCount, outEffectCount, outControlCount, - type, flags) - , m_payload(payload) - {} - - ~OperationWithPayload() = default; - -private: - Payload m_payload; -}; - -class ConstantPayload -{ -public: - explicit ConstantPayload(QV4::Value v) - : m_value(v) - {} - - QV4::Value value() const - { return m_value; } - - static const ConstantPayload *get(const Operation &op) - { - if (op.kind() != Meta::Constant) - return nullptr; - - return &static_cast<const OperationWithPayload<ConstantPayload>&>(op).payload(); - } - - QString debugString() const; - static QString debugString(QV4::Value v); - -private: - QV4::Value m_value; -}; - -class ParameterPayload -{ -public: - ParameterPayload(size_t index, Function::StringId stringId) - : m_index(index) - , m_stringId(stringId) - {} - - size_t parameterIndex() const - { return m_index; } - - Function::StringId stringId() const - { return m_stringId; } - - static const ParameterPayload *get(const Operation &op) - { - if (op.kind() != Meta::Parameter) - return nullptr; - - return &static_cast<const OperationWithPayload<ParameterPayload>&>(op).payload(); - } - - QString debugString() const; - -private: - size_t m_index; - Function::StringId m_stringId; -}; - -class CallPayload -{ -public: - CallPayload(Operation::Kind callee) - : m_callee(callee) - {} - - static const CallPayload *get(const Operation &op) - { - if (op.kind() != Meta::Call) - return nullptr; - - return &static_cast<const OperationWithPayload<CallPayload>&>(op).payload(); - } - - static bool isRuntimeCall(Operation::Kind m); - - Operation::Kind callee() const { return m_callee; } - QString debugString() const; - - unsigned argc() const { return argc(m_callee); } - static unsigned argc(Operation::Kind callee); - static bool needsStorageOnJSStack(Operation::Kind m, unsigned arg, const Operation *op, - Type nodeType); - static Type returnType(Operation::Kind m); - static int engineArgumentPosition(Operation::Kind m); - static int functionArgumentPosition(Operation::Kind m); - - static constexpr unsigned NoVarArgs = std::numeric_limits<unsigned>::max(); - static unsigned varArgsStart(Operation::Kind m); - static bool isVarArgsCall(Operation::Kind m); - bool isVarArgsCall() const; - static bool lastArgumentIsOutputValue(Operation::Kind m); - static bool changesContext(Operation::Kind m); - static bool isPure(Operation::Kind m); - static bool canThrow(Operation::Kind m); - static bool takesEngineAsArg(Operation::Kind m, int arg); - static bool takesFunctionAsArg(Operation::Kind m, int arg); - static bool takesFrameAsArg(Operation::Kind m, int arg); - static void *getMethodPtr(Operation::Kind m); - -private: - Operation::Kind m_callee; -}; - -class UnwindDispatchPayload -{ -public: - UnwindDispatchPayload(int unwindHandlerOffset, int fallthroughSuccessor) - : m_unwindHandlerOffset(unwindHandlerOffset) - , m_fallthroughSuccessor(fallthroughSuccessor) - {} - - int unwindHandlerOffset() const - { return m_unwindHandlerOffset; } - - int fallthroughSuccessor() const //### unused... - { return m_fallthroughSuccessor; } - - static const UnwindDispatchPayload *get(const Operation &op) - { - if (op.kind() != Meta::UnwindDispatch) - return nullptr; - - return &static_cast<const OperationWithPayload<UnwindDispatchPayload>&>(op).payload(); - } - - QString debugString() const; - -private: - int m_unwindHandlerOffset; - int m_fallthroughSuccessor; -}; - -class HandleUnwindPayload -{ -public: - HandleUnwindPayload(int unwindHandlerOffset) - : m_unwindHandlerOffset(unwindHandlerOffset) - {} - - int unwindHandlerOffset() const - { return m_unwindHandlerOffset; } - - static const HandleUnwindPayload *get(const Operation &op) - { - if (op.kind() != Meta::HandleUnwind) - return nullptr; - - return &static_cast<const OperationWithPayload<HandleUnwindPayload>&>(op).payload(); - } - - QString debugString() const; - -private: - int m_unwindHandlerOffset; -}; - -class OperationBuilder -{ - Q_DISABLE_COPY_MOVE(OperationBuilder) - - friend class QQmlJS::MemoryPool; - OperationBuilder(QQmlJS::MemoryPool *graphPool); - -public: - static OperationBuilder *create(QQmlJS::MemoryPool *pool); - ~OperationBuilder() = delete; - - Operation *getConstant(QV4::Value v); - Operation *getFrameState(uint16_t frameSize); - Operation *getStart(uint16_t outputCount); - Operation *getEnd(uint16_t controlInputCount); - Operation *getParam(unsigned index, Function::StringId name); - Operation *getRegion(unsigned nControlInputs); - Operation *getPhi(unsigned nValueInputs); - Operation *getEffectPhi(unsigned nEffectInputs); - Operation *getUnwindDispatch(unsigned nControlOutputs, int unwindHandlerOffset, int fallthroughSuccessor); - Operation *getHandleUnwind(int unwindHandlerOffset); - - template<Operation::Kind kind> - Operation *get() { - return staticOperation(kind); - } - - Operation *getVASeal(uint16_t nElements); - - Operation *getJSVarArgsCall(Operation::Kind kind, uint16_t argc); - Operation *getJSTailCall(uint16_t argc); - Operation *getTailCall(); - - Operation *getCall(Operation::Kind callee); - -private: - QQmlJS::MemoryPool *m_graphPool; // used to store per-graph nodes - Operation *m_opFrameState = nullptr; - static Operation *staticOperation(Operation::Kind kind); -}; - -} // namespace IR -} // namespace QV4 - -QT_END_NAMESPACE - -#endif // QV4OPERATION_P_H diff --git a/src/qml/jit/qv4runtimesupport_p.h b/src/qml/jit/qv4runtimesupport_p.h deleted file mode 100644 index 0dc6022331..0000000000 --- a/src/qml/jit/qv4runtimesupport_p.h +++ /dev/null @@ -1,255 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QV4RUNTIMESUPPORT_P_H -#define QV4RUNTIMESUPPORT_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <qv4runtimeapi_p.h> - -QT_REQUIRE_CONFIG(qml_tracing); - -QT_BEGIN_NAMESPACE - -namespace QV4 { -namespace IR { -namespace RuntimeSupport { - -template <typename T> -struct CountArguments { - static constexpr unsigned count = 0; -}; -template <typename RetTy, typename... Args> -struct CountArguments<RetTy (*)(Args... args)> { - static constexpr unsigned count = sizeof...(Args) ; -}; - -template<typename M> -static constexpr unsigned argumentCount() { - using type = decltype(&M::call); - return CountArguments<type>::count; -} - -enum class ArgumentType { - Invalid, - Engine, - Frame, - Function, - ValueRef, - ValueArray, - ReturnedValue, - Int, - Bool, - Void, -}; - - -template <typename T> -struct JavaScriptType -{ - // No default type. We want to make sure everything we do is actually recognized. -}; - -template <typename T> -struct ReturnValue -{ - // No default type. -}; - -template <int I, typename T> -struct Argument -{ - // For simplicity, we add a default here. Otherwise we would need to spell out more - // combinations of I and number of arguments of T. - static constexpr ArgumentType type = ArgumentType::Invalid; -}; - -template <typename RetTy, typename T, typename... Args> -struct Argument<1, RetTy (*)(T, Args... args)> { - static constexpr ArgumentType type = JavaScriptType<T>::type; -}; - -template <typename RetTy, typename Arg1, typename T, typename... Args> -struct Argument<2, RetTy (*)(Arg1, T, Args... args)> { - static constexpr ArgumentType type = JavaScriptType<T>::type; -}; - -template <typename RetTy, typename Arg1, typename Arg2, typename T, - typename... Args> -struct Argument<3, RetTy (*)(Arg1, Arg2, T, Args... args)> { - static constexpr ArgumentType type = JavaScriptType<T>::type; -}; - -template <typename RetTy, typename Arg1, typename Arg2, - typename Arg3, typename T, typename... Args> -struct Argument<4, RetTy (*)(Arg1, Arg2, Arg3, T, Args... args)> { - static constexpr ArgumentType type = JavaScriptType<T>::type; -}; - -template <typename RetTy, typename Arg1, typename Arg2, - typename Arg3, typename Arg4, typename T, typename... Args> -struct Argument<5, RetTy (*)(Arg1, Arg2, Arg3, Arg4, T, Args... args)> { - static constexpr ArgumentType type = JavaScriptType<T>::type; -}; - -template <typename RetTy, typename Arg1, typename Arg2, - typename Arg3, typename Arg4, typename Arg5, typename T, typename... Args> -struct Argument<6, RetTy (*)(Arg1, Arg2, Arg3, Arg4, Arg5, T, Args... args)> { - static constexpr ArgumentType type = JavaScriptType<T>::type; -}; - -template <typename RetTy, typename... Args> -struct ReturnValue<RetTy (*)(Args... args)> { - static constexpr ArgumentType type = JavaScriptType<RetTy>::type; -}; - -template<> -struct JavaScriptType<QV4::ExecutionEngine *> -{ - static constexpr ArgumentType type = ArgumentType::Engine; -}; - -template<> -struct JavaScriptType<QV4::CppStackFrame *> -{ - static constexpr ArgumentType type = ArgumentType::Frame; -}; - -template<> -struct JavaScriptType<QV4::Function *> -{ - static constexpr ArgumentType type = ArgumentType::Function; -}; - -template<> -struct JavaScriptType<const QV4::Value &> -{ - static constexpr ArgumentType type = ArgumentType::ValueRef; -}; - -template<> -// We need to pass Value * in order to match a parmeter Value[]. -struct JavaScriptType<QV4::Value *> -{ - static constexpr ArgumentType type = ArgumentType::ValueArray; -}; - -template<> -struct JavaScriptType<int> -{ - static constexpr ArgumentType type = ArgumentType::Int; -}; - -template<> -struct JavaScriptType<QV4::Bool> -{ - static constexpr ArgumentType type = ArgumentType::Bool; -}; - -template<> -struct JavaScriptType<QV4::ReturnedValue> -{ - static constexpr ArgumentType type = ArgumentType::ReturnedValue; -}; - -template<> -struct JavaScriptType<void> -{ - static constexpr ArgumentType type = ArgumentType::Void; -}; - -template<typename M> -static constexpr ArgumentType retType() { - using Type = decltype(&M::call); - return ReturnValue<Type>::type; -} - -template<typename M> -static constexpr ArgumentType arg1Type() { - using Type = decltype(&M::call); - return Argument<1, Type>::type; -} - -template<typename M> -static constexpr ArgumentType arg2Type() { - using Type = decltype(&M::call); - return Argument<2, Type>::type; -} - -template<typename M> -static constexpr ArgumentType arg3Type() { - using Type = decltype(&M::call); - return Argument<3, Type>::type; -} - -template<typename M> -static constexpr ArgumentType arg4Type() { - using Type = decltype(&M::call); - return Argument<4, Type>::type; -} - -template<typename M> -static constexpr ArgumentType arg5Type() { - using Type = decltype(&M::call); - return Argument<5, Type>::type; -} - -template<typename M> -static constexpr ArgumentType arg6Type() { - using Type = decltype(&M::call); - return Argument<6, Type>::type; -} - -} // namespace RuntimeSupport -} // namespace IR -} // namespace QV4 - -QT_END_NAMESPACE - -#endif // QV4RUNTIMESUPPORT_P_H diff --git a/src/qml/jit/qv4schedulers.cpp b/src/qml/jit/qv4schedulers.cpp deleted file mode 100644 index 0dffefa951..0000000000 --- a/src/qml/jit/qv4schedulers.cpp +++ /dev/null @@ -1,912 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <QtCore/qloggingcategory.h> - -#include "qv4schedulers_p.h" -#include "qv4util_p.h" -#include "qv4graph_p.h" -#include "qv4blockscheduler_p.h" -#include "qv4stackframe_p.h" - -QT_BEGIN_NAMESPACE -namespace QV4 { -namespace IR { - -Q_LOGGING_CATEGORY(lcSched, "qt.v4.ir.scheduling") -Q_LOGGING_CATEGORY(lcDotCFG, "qt.v4.ir.scheduling.cfg") - -static bool needsScheduling(Node *n) -{ - if (n->operation()->isConstant()) - return false; - switch (n->opcode()) { - case Meta::Function: Q_FALLTHROUGH(); - case Meta::CppFrame: - case Meta::Phi: - case Meta::EffectPhi: - return false; - default: - return true; - } -} - -bool NodeScheduler::canStartBlock(Node *node) const -{ - switch (node->operation()->kind()) { - case Meta::Start: Q_FALLTHROUGH(); - case Meta::IfTrue: - case Meta::IfFalse: - case Meta::Region: - case Meta::HandleUnwind: - case Meta::OnException: - return true; - - default: - return false; - } -} - -bool NodeScheduler::isControlFlowSplit(Node *node) const -{ - int nOutputs = node->operation()->controlOutputCount(); - if (nOutputs == 2) { - // if there is a "missing" control output, it's for exception flow without unwinder - int controlUses = 0; - auto uses = node->uses(); - for (auto it = uses.begin(), eit = uses.end(); it != eit; ++it) { - if (isLive(*it) && it.isUsedAsControl()) - ++controlUses; - } - return controlUses == 2; - } - return nOutputs > 2; -} - -bool NodeScheduler::isBlockTerminator(Node *node) const -{ - switch (node->operation()->kind()) { - case Meta::Branch: Q_FALLTHROUGH(); - case Meta::Jump: - case Meta::Return: - case Meta::TailCall: - case Meta::UnwindDispatch: - case Meta::End: - return true; - case Meta::Call: - return isControlFlowSplit(node); - default: - return false; - } -} - -MIBlock *NodeScheduler::getCommonDominator(MIBlock *one, MIBlock *other) const -{ - MIBlock::Index a = one->index(); - MIBlock::Index b = other->index(); - - while (a != b) { - if (m_dominatorDepthForBlock[a] < m_dominatorDepthForBlock[b]) - b = m_domTree->immediateDominator(b); - else - a = m_domTree->immediateDominator(a); - } - - return m_miFunction->block(a); -} - -// For Nodes that end up inside loops, it'd be great if we can move (hoist) them out of the loop. -// To do that, we need a block that preceeds the loop. (So the block before the loop header.) -// This function calculates that hoist block if the original block is in a loop. -MIBlock *NodeScheduler::getHoistBlock(MIBlock *block) const -{ - if (m_loopInfo->isLoopHeader(block)) - return m_miFunction->block(m_domTree->immediateDominator(block->index())); - - // make the loop header a candidate: - MIBlock *loopHeader = m_loopInfo->loopHeaderFor(block); - if (loopHeader == nullptr) - return nullptr; // block is not in a loop - - // And now the tricky part: block has to dominate all exits from the loop. If it does not do - // that, it meanse that there is an exit from the loop that can be reached before block. In - // that case, hoisting from "block" to "loopHeader" would mean there now is an extra calculation - // that is not needed for a certain loop exit. - for (MIBlock *outEdge : m_loopInfo->loopExitsForLoop(loopHeader)) { - if (getCommonDominator(block, outEdge) != block) - return nullptr; - } - - return m_miFunction->block(m_domTree->immediateDominator(loopHeader->index())); -} - -NodeScheduler::NodeScheduler(Function *irFunction) - : m_irFunction(irFunction) - , m_vregs(irFunction->graph()->nodeCount(), std::numeric_limits<unsigned>::max()) - , m_live(irFunction->graph(), /*collectUses =*/ false /* do explicitly NOT collect uses! */) -{ -} - -MIFunction *NodeScheduler::buildMIFunction() -{ - m_miFunction = new MIFunction(m_irFunction); - - // step 1: build the CFG - auto roots = buildCFG(); - m_miFunction->renumberBlocks(); - m_miFunction->dump(QStringLiteral("CFG after renumbering")); - - Q_ASSERT(m_miFunction->block(MIFunction::StartBlockIndex)->index() - == MIFunction::StartBlockIndex); - Q_ASSERT(m_miFunction->block(MIFunction::StartBlockIndex)->instructions().front().opcode() - == Meta::Start); - - // step 2: build the dominator tree - if (lcDotCFG().isDebugEnabled()) - dumpDotCFG(); - m_domTree.reset(new DominatorTree(m_miFunction)); - m_dominatorDepthForBlock = m_domTree->calculateNodeDepths(); - - // step 3: find loops - m_loopInfo.reset(new LoopInfo(*m_domTree.data())); - m_loopInfo->detectLoops(); - - // step 4: schedule early - scheduleEarly(roots); - showNodesByBlock(QStringLiteral("nodes per block after early scheduling")); - - // step 5: schedule late - scheduleLate(roots); - showNodesByBlock(QStringLiteral("nodes per block after late scheduling")); - - // step 6: schedule instructions in each block - scheduleNodesInBlock(); - - m_miFunction->dump(QStringLiteral("MI before block scheduling")); - - // step 7: order the basic blocks in the CFG - BlockScheduler blockScheduler(*m_domTree.data(), *m_loopInfo.data()); - m_miFunction->setBlockOrder(blockScheduler.scheduledBlockSequence()); - - // we're done - m_miFunction->renumberInstructions(); - m_miFunction->setVregCount(m_nextVReg); - m_miFunction->dump(QStringLiteral("MI after scheduling")); - return m_miFunction; -} - -static Node *splitEdge(Function *irFunction, Node *node, unsigned inputIndex) -{ - Graph *g = irFunction->graph(); - Node *in = node->input(inputIndex); - Node *region = g->createNode(g->opBuilder()->getRegion(1), &in, 1); - Node *jump = g->createNode(g->opBuilder()->get<Meta::Jump>(), ®ion, 1); - - qCDebug(lcSched) << "splitting critical edge from node" << node->id() - << "to node" << node->input(inputIndex)->id() - << "by inserting jump node" << jump->id() - << "and region node" << region->id(); - - node->replaceInput(inputIndex, jump); - return jump; -} - -// See Chapter 6.3.1 of https://scholarship.rice.edu/bitstream/handle/1911/96451/TR95-252.pdf for -// a description of the algorithm. -std::vector<Node *> NodeScheduler::buildCFG() -{ - std::vector<Node *> roots; - roots.reserve(32); - NodeWorkList todo(m_irFunction->graph()); - - auto enqueueControlInputs = [this, &todo](Node *node) { - for (unsigned i = 0, ei = node->operation()->controlInputCount(); i != ei; ++i) { - const auto inputIndex = node->operation()->indexOfFirstControl() + i; - Node *input = node->input(inputIndex); - Q_ASSERT(input); - if (node->operation()->kind() == Meta::Region - && node->operation()->controlInputCount() > 1 - && isControlFlowSplit(input)) { - // critical edge! - input = splitEdge(m_irFunction, node, inputIndex); - m_live.markReachable(input); - m_live.markReachable(input->controlInput(0)); - } - if (!isBlockTerminator(input)) { - auto g = m_irFunction->graph(); - Node *jump = g->createNode(g->opBuilder()->get<Meta::Jump>(), &input, 1); - node->replaceInput(inputIndex, jump); - m_live.markReachable(jump); - qCDebug(lcSched) << "inserting jump node" << jump->id() - << "between node" << node->id() - << "and node" << input->id(); - input = jump; - } - todo.enqueue(input); - } - }; - - // create the CFG by scheduling control dependencies that start/end blocks: - todo.enqueue(m_irFunction->graph()->endNode()); - while (Node *node = todo.dequeueNextNodeForVisiting()) { - Q_ASSERT(isBlockTerminator(node)); - - if (schedulerData(node)->minimumBlock) - continue; - - MIBlock *b = m_miFunction->addBlock(); - - qCDebug(lcSched) << "scheduling node" << node->id() << "as terminator for new block" - << b->index(); - b->instructions().push_front(createMIInstruction(node)); - placeFixed(node, b, Schedule); - roots.push_back(node); - - if (Node *framestate = node->frameStateInput()) { - placeFixed(framestate, b, DontSchedule); - qCDebug(lcSched) << ".. also scheduling framestate dependency node" << node->id() - << "in block" << b->index(); - } - - if (node->opcode() == Meta::End) { - enqueueControlInputs(node); - continue; - } - - while (true) { - Node *controlDependency = node->controlInput(0); - if (!controlDependency) - break; - if (todo.isVisited(controlDependency)) - break; - if (schedulerData(controlDependency)->isFixed) - break; - - if (controlDependency->opcode() == Meta::Start) { - qCDebug(lcSched) << "placing start node" << controlDependency->id() - << "in block" << b->index(); - handleStartNode(controlDependency, b); - placeFixed(controlDependency, b, Schedule); - roots.push_back(controlDependency); - break; // we're done with this block - } - if (isBlockTerminator(controlDependency)) { - qCDebug(lcSched) << "found terminator node" << controlDependency->id() - << "for another block, so finish block" << b->index(); - Node *merge = m_irFunction->graph()->createNode( - m_irFunction->graph()->opBuilder()->getRegion(1), &controlDependency, 1); - node->replaceInput(node->operation()->indexOfFirstControl(), merge); - addBlockStart(roots, merge, b); - placeFixed(merge, b, Schedule); - m_live.markReachable(merge); - todo.enqueue(controlDependency); - break; // we're done with this block - } - if (canStartBlock(controlDependency) - || schedulerData(controlDependency->controlInput())->isFixed) { - qCDebug(lcSched) << "found block start node" << controlDependency->id() - << "for this block, so finish block" << b->index(); - addBlockStart(roots, controlDependency, b); - placeFixed(controlDependency, b, Schedule); - roots.push_back(controlDependency); - enqueueControlInputs(controlDependency); - break; // we're done with this block - } - qCDebug(lcSched) << "skipping node" << controlDependency->id(); - node = controlDependency; - } - } - - // link the edges of the MIBlocks, and add basic-block arguments: - for (MIBlock *toBlock : m_miFunction->blocks()) { - Q_ASSERT(!toBlock->instructions().empty()); - MIInstr &instr = toBlock->instructions().front(); - Node *toNode = instr.irNode(); - const auto opcode = toNode->operation()->kind(); - if (opcode == Meta::Region) { - unsigned inputNr = 0; - for (Node *input : toNode->inputs()) { - MIBlock *fromBlock = schedulerData(input)->minimumBlock; - fromBlock->addOutEdge(toBlock); - toBlock->addInEdge(fromBlock); - MIInstr &fromTerminator = fromBlock->instructions().back(); - if (fromTerminator.irNode()->opcode() == Meta::Jump || - fromTerminator.irNode()->opcode() == Meta::UnwindDispatch) { - unsigned arg = 0; - for (const MIOperand &bbArg : toBlock->arguments()) { - fromTerminator.setOperand(arg++, - createMIOperand(bbArg.irNode()->input(inputNr))); - } - } - ++inputNr; - } - } else if (opcode == Meta::End) { - for (Node *input : toNode->inputs()) { - MIBlock *fromBlock = schedulerData(input)->minimumBlock; - fromBlock->addOutEdge(toBlock); - toBlock->addInEdge(fromBlock); - } - } else if (Node *fromNode = toNode->controlInput()) { - MIBlock *fromBlock = schedulerData(fromNode)->minimumBlock; - fromBlock->addOutEdge(toBlock); - toBlock->addInEdge(fromBlock); - } - } - - m_irFunction->dump(QStringLiteral("graph after building CFG")); - - auto startBlock = schedulerData(m_irFunction->graph()->startNode())->minimumBlock; - m_miFunction->setStartBlock(startBlock); - - if (lcSched().isDebugEnabled()) - m_miFunction->dump(QStringLiteral("control flow graph before renumbering")); - m_miFunction->verifyCFG(); - - return roots; -} - -// See Chapter 6.3.3 of https://scholarship.rice.edu/bitstream/handle/1911/96451/TR95-252.pdf for -// a description of the algorithm. -void NodeScheduler::scheduleEarly(const std::vector<Node *> &roots) -{ - // scheduling one node might have the effect of queueing its dependencies - NodeWorkList todo(m_irFunction->graph()); - for (Node *root : roots) { - todo.enqueue(root); - while (Node *node = todo.dequeueNextNodeForVisiting()) - scheduleEarly(node, todo); - } -} - -void NodeScheduler::scheduleEarly(Node *node, NodeWorkList &todo) -{ - qCDebug(lcSched) << "Scheduling node" << node->id() << "early..."; - - SchedulerData *sd = schedulerData(node); - - if (sd->isFixed) { - // Fixed nodes already know their schedule early position. - qCDebug(lcSched) << ".. Fixed node" << node->id() << "is on minimum block" - << sd->minimumBlock->index() - << "which has dominator depth" - << m_dominatorDepthForBlock[sd->minimumBlock->index()]; - } - - for (Node *use : node->uses()) { - if (isLive(use)) - propagateMinimumPosition(sd->minimumBlock, use, todo); - else - qCDebug(lcSched) << ".. Skipping node" << use->id() << "as it's not live"; - } -} - -void NodeScheduler::propagateMinimumPosition(MIBlock *newMinimumPosition, Node *toNode, - NodeWorkList &todo) -{ - Q_ASSERT(newMinimumPosition); - - SchedulerData *sd = schedulerData(toNode); - if (sd->isFixed) // nothing to do - return; - - MIBlock::Index minimumBlockIndex = sd->minimumBlock - ? sd->minimumBlock->index() - : MIFunction::StartBlockIndex; - Q_ASSERT(m_domTree->insideSameDominatorChain(newMinimumPosition->index(), minimumBlockIndex)); - if (sd->minimumBlock == nullptr - || m_dominatorDepthForBlock[newMinimumPosition->index()] - > m_dominatorDepthForBlock[minimumBlockIndex]) { - // ok, some input for toNode is scheduled *after* our current minimum depth, so we need - // to adjust out minimal position. (This might involve rescheduling toNode's uses.) - place(toNode, newMinimumPosition); - todo.reEnqueue(toNode); - qCDebug(lcSched) << ".. Propagating minimum block" << sd->minimumBlock->index() - << "which has dominator depth" - << m_dominatorDepthForBlock[newMinimumPosition->index()] - << "to use node" << toNode->id(); - } else { - qCDebug(lcSched) << ".. Minimum position" << newMinimumPosition->index() - << "is not better than" << minimumBlockIndex - << "for node" << toNode->id(); - } -} - -// See Chapter 6.3.4 of https://scholarship.rice.edu/bitstream/handle/1911/96451/TR95-252.pdf for -// a description of the algorithm. -// -// There is one extra detail not described in the thesis mentioned above: loop hoisting. Before we -// place a node in the latest block that dominates all uses, we check if we accidentally sink it -// *into* a loop (meaning the latest block is inside a loop, where it is not if the earliest -// possible block would be chosen). If we detect that a nodes is going to sink into a loop, we walk -// the dominator path from the latest block up to the earliest block, and pick the first block that -// is in the same loop (if any) as the earlieast block. -// -// As noted in the thesis, this strategy might enlongen life times, which could be harmful for -// values that are simple to re-materialized or re-calculate. -void NodeScheduler::scheduleLate(const std::vector<Node *> &roots) -{ - NodeWorkList todo(m_irFunction->graph()); - for (Node *root : roots) - todo.enqueue(root); - - while (Node *node = todo.dequeueNextNodeForVisiting()) - scheduleNodeLate(node, todo); -} - -void NodeScheduler::scheduleNodeLate(Node *node, NodeWorkList &todo) -{ - if (!needsScheduling(node)) - return; - qCDebug(lcSched) << "Scheduling node" << node->id() << "late..."; - - auto sd = schedulerData(node); - if (sd->unscheduledUses == SchedulerData::NotYetCalculated) { - sd->unscheduledUses = 0; - for (Node *use : node->uses()) { - if (!isLive(use)) - continue; - if (!needsScheduling(use)) - continue; - if (schedulerData(use)->isFixed) - continue; - todo.enqueue(use); - ++sd->unscheduledUses; - } - } - - if (sd->isFixed) { - qCDebug(lcSched) << ".. it's fixed"; - enqueueInputs(node, todo); - return; - } - - if (sd->unscheduledUses) { - qCDebug(lcSched).noquote() << ".. not all uses are fixed, postpone it."<< todo.status(node); - return; - } - - MIBlock *&minBlock = sd->minimumBlock; - if (minBlock == nullptr) - minBlock = m_miFunction->block(MIFunction::StartBlockIndex); - MIBlock *commonUseDominator = commonDominatorOfUses(node); - qCDebug(lcSched) << ".. common use dominator: block" << commonUseDominator->index(); - - // the minBlock has to dominate the uses, *and* the common dominator of the uses. - Q_ASSERT(minBlock->index() == commonUseDominator->index() || - m_domTree->dominates(minBlock->index(), commonUseDominator->index())); - - // we now found the deepest block, so use it as the target block: - MIBlock *targetBlock = commonUseDominator; - - if (node->opcode() == Meta::FrameState) { - // never hoist framestates: they're used (among other things) to keep their inputs alive, so - // hoisting them out would end the life-time of those inputs prematurely - } else { - // but we want to prevent blocks sinking into loops unnecessary - MIBlock *hoistBlock = getHoistBlock(targetBlock); - while (hoistBlock - && m_dominatorDepthForBlock[hoistBlock->index()] - >= m_dominatorDepthForBlock[minBlock->index()]) { - qCDebug(lcSched) << ".. hoisting node" << node->id() << "from block" - << targetBlock->index() << "to block" << hoistBlock->index(); - // ok, so there a) is a hoist block and b) it's deeper than the minimum block, - // so lift it up one level ... - targetBlock = hoistBlock; - // ... and see if we can lift it one more level - hoistBlock = getHoistBlock(targetBlock); - } - } - - qCDebug(lcSched) << ".. fixating it in block" << targetBlock->index() - << "where the minimum block was" << minBlock->index(); - - placeFixed(node, targetBlock, DontSchedule); - enqueueInputs(node, todo); -} - -void NodeScheduler::enqueueInputs(Node *node, NodeWorkList &todo) -{ - for (Node *input : node->inputs()) { - if (!input) - continue; - if (!needsScheduling(input)) - continue; - if (!isLive(input)) - continue; - auto sd = schedulerData(input); - if (sd->isFixed) - continue; - qCDebug(lcSched).noquote() << "... enqueueing input node" << input->id() - << todo.status(input); - if (sd->unscheduledUses != SchedulerData::NotYetCalculated) { - if (sd->unscheduledUses > 0) - --sd->unscheduledUses; - if (sd->unscheduledUses == 0) - todo.reEnqueue(input); - } else { - todo.reEnqueue(input); - } - } -} - -Node *NodeScheduler::firstNotFixedUse(Node *node) -{ - for (Node *use : node->uses()) { - if (!isLive(use)) - continue; - if (!schedulerData(use)->isFixed) - return use; - } - return nullptr; -} - -MIBlock *NodeScheduler::commonDominatorOfUses(Node *node) -{ - MIBlock *commonDominator = nullptr; - for (auto useIt = node->uses().begin(), useEIt = node->uses().end(); useIt != useEIt; ++useIt) { - Node *use = *useIt; - if (!isLive(use)) - continue; - // region nodes use other nodes through their control dependency. But those nodes should - // already have been placed as block terminators before. - Q_ASSERT(use->opcode() != Meta::Region); - if (use->opcode() == Meta::Phi || use->opcode() == Meta::EffectPhi) { - // find the predecessor block defining this input - Node *region = use->controlInput(0); - Node *input = region->controlInput(useIt.inputIndex()); - use = input; - } - auto minBlock = schedulerData(use)->minimumBlock; - if (commonDominator == nullptr) - commonDominator = minBlock; - else - commonDominator = getCommonDominator(commonDominator, minBlock); - } - return commonDominator; -} - -void NodeScheduler::scheduleNodesInBlock() -{ - auto startBlock = m_miFunction->block(MIFunction::StartBlockIndex); - for (Node *n : m_live.reachable()) { - auto sd = schedulerData(n); - if (!sd->minimumBlock) - sd->minimumBlock = startBlock; - } - - std::vector<std::vector<SchedulerData *>> nodesForBlock; - nodesForBlock.resize(m_miFunction->blockCount()); - - for (auto sd : m_schedulerData) { - if (sd == nullptr) - continue; - if (!isLive(sd->node)) - continue; - sd->unscheduledUses = 0; - for (Node *use : sd->node->uses()) { - if (!needsScheduling(use)) - continue; - if (schedulerData(use)->isScheduledInBlock) - continue; - if (schedulerData(use)->minimumBlock == sd->minimumBlock) - ++sd->unscheduledUses; - } - if (sd->unscheduledUses == 0) - nodesForBlock[sd->minimumBlock->index()].push_back(sd); - } - - NodeWorkList todo(m_irFunction->graph()); - for (MIBlock *b : m_miFunction->blocks()) { - qCDebug(lcSched) << "Scheduling inside block" << b->index(); - MIInstr *insertionPoint = &b->instructions().back(); - todo.enqueue(insertionPoint->irNode()); - scheduleNodesInBlock(insertionPoint, b, todo); - Q_ASSERT(todo.isEmpty()); - for (auto sd : nodesForBlock[b->index()]) { - if (!sd->isScheduledInBlock) - todo.enqueue(sd->node); - } - scheduleNodesInBlock(insertionPoint, b, todo); - Q_ASSERT(todo.isEmpty()); - todo.reset(); - } -} - -void NodeScheduler::scheduleNodesInBlock(MIInstr *&insertionPoint, MIBlock *b, NodeWorkList &todo) -{ - while (Node *n = todo.dequeueNextNodeForVisiting()) - scheduleNodeInBlock(n, insertionPoint, b, todo); -} - -void NodeScheduler::scheduleNodeInBlock(Node *node, MIInstr *&insertionPoint, MIBlock *b, - NodeWorkList &todo) -{ - Q_ASSERT(!node->isDead()); - - if (!isLive(node)) - return; - - if (!needsScheduling(node)) - return; - - auto nodeData = schedulerData(node); - if (nodeData->minimumBlock != b) - return; - - const bool wasAlreadyScheduled = nodeData->isScheduledInBlock; - if (!wasAlreadyScheduled) { - if (nodeData->unscheduledUses) - return; - - scheduleNodeNow(node, insertionPoint); - } - - if (Node *framestate = node->frameStateInput()) - scheduleNodeInBlock(framestate, insertionPoint, b, todo); - - for (Node *input : node->inputs()) { - if (!input) - continue; - if (!needsScheduling(input)) - continue; - if (!isLive(input)) - continue; - auto inputInfo = schedulerData(input); - if (inputInfo->isScheduledInBlock) - continue; - Q_ASSERT(inputInfo->minimumBlock != nullptr); - if (inputInfo->minimumBlock != b) - continue; - Q_ASSERT(!input->isDead()); - Q_ASSERT(inputInfo->unscheduledUses != SchedulerData::NotYetCalculated); - if (!wasAlreadyScheduled && inputInfo->unscheduledUses > 0) - --inputInfo->unscheduledUses; - if (inputInfo->unscheduledUses == 0) - todo.enqueue(input); - } -} - -void NodeScheduler::scheduleNodeNow(Node *node, MIInstr *&insertionPoint) -{ - qCDebug(lcSched) << ".. scheduling node" << node->id() - << "in block" << insertionPoint->parent()->index() - << "before node" << insertionPoint->irNode()->id(); - - MIInstr *newInstr = createMIInstruction(node); - newInstr->insertBefore(insertionPoint); - insertionPoint = newInstr; -} - -void NodeScheduler::place(Node *node, MIBlock *b) -{ - Q_ASSERT(!node->isDead()); - - if (b == nullptr) - return; - - schedulerData(node)->minimumBlock = b; -} - -void NodeScheduler::placeFixed(Node *node, MIBlock *b, ScheduleOrNot markScheduled) -{ - place(node, b); - auto sd = schedulerData(node); - Q_ASSERT(!sd->isFixed); - sd->isFixed = true; - sd->isScheduledInBlock = markScheduled == Schedule; -} - -unsigned NodeScheduler::vregForNode(Node *node) -{ - unsigned &vreg = m_vregs[unsigned(node->id())]; - if (vreg == std::numeric_limits<unsigned>::max()) - vreg = m_nextVReg++; - return vreg; -} - -void NodeScheduler::addBlockStart(std::vector<Node *> &roots, Node *startNode, MIBlock *block) -{ - block->instructions().insert(block->instructions().begin(), createMIInstruction(startNode)); - if (startNode->opcode() == Meta::Region) { - for (Node *use : startNode->uses()) { - if (use->opcode() == Meta::Phi && isLive(use)) { - block->addArgument(MIOperand::createVirtualRegister(use, vregForNode(use))); - placeFixed(use, block, Schedule); - roots.push_back(use); - } else if (use->opcode() == Meta::EffectPhi && isLive(use)) { - placeFixed(use, block, Schedule); - roots.push_back(use); - } - } - } -} - -void NodeScheduler::handleStartNode(Node *startNode, MIBlock *startBlock) -{ - startBlock->instructions().push_front(createMIInstruction(startNode)); - - QVarLengthArray<Node *, 32> args; - for (Node *use : startNode->uses()) { - switch (use->opcode()) { - case Meta::Engine: Q_FALLTHROUGH(); - case Meta::CppFrame: - case Meta::Function: - placeFixed(use, startBlock, Schedule); - break; - case Meta::Parameter: { - auto param = ParameterPayload::get(*use->operation()); - int idx = int(param->parameterIndex()); - if (args.size() <= idx) - args.resize(idx + 1); - args[int(idx)] = use; - placeFixed(use, startBlock, Schedule); - } - break; - default: - break; - } - } - - for (unsigned i = 0, ei = unsigned(args.size()); i != ei; ++i) { - if (Node *arg = args.at(int(i))) - startBlock->addArgument(MIOperand::createJSStackSlot(arg, i)); - } -} - -static Node *firstControlOutput(Node *n) -{ - for (auto it = n->uses().begin(), eit = n->uses().end(); it != eit; ++it) { - if (it.isUsedAsControl()) - return *it; - } - return nullptr; -} - -MIInstr *NodeScheduler::createMIInstruction(Node *node) -{ - const auto opcode = node->operation()->kind(); - - unsigned nArgs = 0; - switch (opcode) { - case Meta::UnwindDispatch: Q_FALLTHROUGH(); - case Meta::Jump: { - Node *target = firstControlOutput(node); - if (target->opcode() == Meta::Region) { - for (Node *n : target->uses()) { - if (n->opcode() == Meta::Phi && isLive(n)) - ++nArgs; - } - } - } - break; - case Meta::Branch: - nArgs = 1; - break; - case Meta::Return: - nArgs = 1; - break; - default: - nArgs = node->operation()->valueInputCount(); - break; - } - - MIInstr *instr = MIInstr::create(m_irFunction->pool(), node, nArgs); - for (unsigned i = 0, ei = node->operation()->valueInputCount(); i != ei; ++i) - instr->setOperand(i, createMIOperand(node->input(i))); - if (node->opcode() != Meta::Start && node->operation()->valueOutputCount() > 0) - instr->setDestination(createMIOperand(node)); - - schedulerData(node)->isScheduledInBlock = true; - return instr; -} - -MIOperand NodeScheduler::createMIOperand(Node *node) -{ - if (node->operation()->isConstant()) - return MIOperand::createConstant(node); - - auto opcode = node->operation()->kind(); - switch (opcode) { - case Meta::Parameter: - return MIOperand::createJSStackSlot( - node, unsigned(ParameterPayload::get(*node->operation())->parameterIndex())); - case Meta::Engine: - return MIOperand::createEngineRegister(node); - case Meta::CppFrame: - return MIOperand::createCppFrameRegister(node); - case Meta::Function: - return MIOperand::createFunction(node); - default: - if ((node->opcode() == Meta::Call - && CallPayload::get(*node->operation())->callee() == Meta::JSThisToObject) - || node->opcode() == Meta::StoreThis) { - return MIOperand::createJSStackSlot(node, CallData::This); - } else { - return MIOperand::createVirtualRegister(node, vregForNode(node)); - } - } -} - -void NodeScheduler::showNodesByBlock(const QString &description) const -{ - if (!lcSched().isDebugEnabled()) - return; - - qCDebug(lcSched) << description; - - for (MIBlock *b : m_miFunction->blocks()) { - QString s; - for (const SchedulerData *sd : m_schedulerData) { - if (!sd) - continue; - if (!isLive(sd->node)) - continue; - if (sd->minimumBlock == b) { - if (!s.isEmpty()) - s += QStringLiteral(", "); - s += QStringLiteral("%1 (%2)").arg(QString::number(sd->node->id()), - sd->node->operation()->debugString()); - } - } - if (s.isEmpty()) - s = QStringLiteral("<<none>>"); - qCDebug(lcSched, "Nodes in block %u: %s", b->index(), s.toUtf8().constData()); - } -} - -void NodeScheduler::dumpDotCFG() const -{ - QString out; - out += QLatin1Char('\n'); - out += QStringLiteral("digraph{root=\"L%1\" label=\"Control Flow Graph\";" - "node[shape=circle];edge[dir=forward fontsize=10]\n") - .arg(MIFunction::StartBlockIndex); - for (MIBlock *src : m_miFunction->blocks()) { - for (MIBlock *dst : src->outEdges()) { - out += QStringLiteral("L%1->L%2\n").arg(QString::number(src->index()), - QString::number(dst->index())); - } - } - out += QStringLiteral("}\n"); - qCDebug(lcDotCFG).nospace().noquote() << out; -} - -} // IR namespace -} // QV4 namespace -QT_END_NAMESPACE diff --git a/src/qml/jit/qv4schedulers_p.h b/src/qml/jit/qv4schedulers_p.h deleted file mode 100644 index f9179816df..0000000000 --- a/src/qml/jit/qv4schedulers_p.h +++ /dev/null @@ -1,162 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QV4SCHEDULER_P_H -#define QV4SCHEDULER_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qv4global_p.h" -#include "qv4mi_p.h" -#include "qv4node_p.h" -#include "qv4domtree_p.h" -#include "qv4loopinfo_p.h" - -QT_REQUIRE_CONFIG(qml_tracing); - -QT_BEGIN_NAMESPACE - -namespace QV4 { -namespace IR { - -// Node scheduling "flattens" the graph into basic blocks with an ordered list of instructions. -// -// The various steps are mentioned in buildMIFunction, but the general idea is described in -// https://scholarship.rice.edu/bitstream/handle/1911/96451/TR95-252.pdf in chapter 6. -class NodeScheduler final -{ - Q_DISABLE_COPY_MOVE(NodeScheduler) - - class SchedulerData final { - Q_DISABLE_COPY_MOVE(SchedulerData) - public: - static SchedulerData *create(QQmlJS::MemoryPool *pool) - { return pool->New<SchedulerData>(); } - - SchedulerData() = default; - ~SchedulerData() = default; - - Node *node = nullptr; - MIBlock *minimumBlock = nullptr; - bool isFixed = false; - bool isScheduledInBlock = false; - static constexpr unsigned NotYetCalculated = std::numeric_limits<unsigned>::max(); - unsigned unscheduledUses = NotYetCalculated; - }; - -public: - NodeScheduler(Function *irFunction); - ~NodeScheduler() = default; - - MIFunction *buildMIFunction(); - -private: - std::vector<Node *> buildCFG(); - void scheduleEarly(const std::vector<Node *> &roots); - void scheduleEarly(Node *node, NodeWorkList &todo); - void propagateMinimumPosition(MIBlock *newMinimumPosition, Node *toNode, NodeWorkList &todo); - void scheduleLate(const std::vector<Node *> &roots); - void scheduleNodeLate(Node *node, NodeWorkList &todo); - void enqueueInputs(Node *node, NodeWorkList &todo); - Node *firstNotFixedUse(Node *node); - MIBlock *commonDominatorOfUses(Node *node); - void scheduleNodesInBlock(); - void scheduleNodesInBlock(MIInstr *&insertionPoint, MIBlock *b, NodeWorkList &todo); - void scheduleNodeInBlock(Node *node, MIInstr *&insertionPoint, MIBlock *b, NodeWorkList &todo); - void scheduleNodeNow(Node *node, MIInstr *&insertionPoint); - - void place(Node *node, MIBlock *b); - enum ScheduleOrNot { DontSchedule, Schedule }; - void placeFixed(Node *node, MIBlock *b, ScheduleOrNot markScheduled); - unsigned vregForNode(Node *node); - void addBlockStart(std::vector<Node *> &roots, Node *startNode, MIBlock *block); - void enqueueControlInputs(Node *node); - void handleStartNode(Node *startNode, MIBlock *startBlock); - MIInstr *createMIInstruction(Node *node); - MIOperand createMIOperand(Node *node); - SchedulerData *schedulerData(Node *n) - { - if (Q_UNLIKELY(m_schedulerData.size() <= n->id())) - m_schedulerData.resize(n->id() + 8); - SchedulerData *&sd = m_schedulerData[n->id()]; - if (Q_UNLIKELY(sd == nullptr)) { - sd = SchedulerData::create(m_irFunction->pool()); - sd->node = n; - } - return sd; - } - bool isLive(Node *n) const - { return m_live.isReachable(n->id()); } - bool canStartBlock(Node *node) const; - bool isControlFlowSplit(Node *node) const; - bool isBlockTerminator(Node *node) const; - MIBlock *getCommonDominator(MIBlock *one, MIBlock *other) const; - MIBlock *getHoistBlock(MIBlock *block) const; - - void showNodesByBlock(const QString &description) const; - - void dumpDotCFG() const; - -private: - Function *m_irFunction = nullptr; - MIFunction *m_miFunction = nullptr; - QScopedPointer<LoopInfo> m_loopInfo; - QScopedPointer<DominatorTree> m_domTree; - std::vector<int> m_dominatorDepthForBlock; - std::vector<unsigned> m_vregs; - std::vector<SchedulerData *> m_schedulerData; - NodeCollector m_live; - unsigned m_nextVReg = 0; -}; - -} // namespace IR -} // namespace QV4 - -QT_END_NAMESPACE - -#endif // QV4SCHEDULER_P_H diff --git a/src/qml/jit/qv4tracingjit.cpp b/src/qml/jit/qv4tracingjit.cpp deleted file mode 100644 index c8974b3a1b..0000000000 --- a/src/qml/jit/qv4tracingjit.cpp +++ /dev/null @@ -1,91 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <QtCore/qloggingcategory.h> - -#include "qv4vme_moth_p.h" -#include "qv4graphbuilder_p.h" -#include "qv4lowering_p.h" -#include "qv4mi_p.h" -#include "qv4schedulers_p.h" - -QT_BEGIN_NAMESPACE - -Q_LOGGING_CATEGORY(lcTracing, "qt.v4.tracing") - -namespace QV4 { - -// This is the entry point for the "tracing JIT". It uses the sea-of-nodes concept as described in -// https://scholarship.rice.edu/bitstream/handle/1911/96451/TR95-252.pdf -// -// The minimal pipeline is as follows: -// - create the graph for the function -// - do generic lowering -// - schedule the nodes -// - run minimal stack slot allocation (no re-use of slots) -// - run the assembler -// -// This pipeline has no optimizations, and generates quite inefficient code. It does have the -// advantage that no trace information is used, so it can be used for testing where it replaces -// the baseline JIT. Any optimizations are additions to this pipeline. -// -// Note: generators (or resuming functions in general) are not supported by this JIT. -void Moth::runTracingJit(QV4::Function *function) -{ - IR::Function irFunction(function); - qCDebug(lcTracing).noquote() << "runTracingJit called for" << irFunction.name() << "..."; - - qCDebug(lcTracing).noquote().nospace() << function->traceInfoToString(); - - IR::GraphBuilder::buildGraph(&irFunction); - irFunction.dump(QStringLiteral("initial IR")); - irFunction.verify(); - - IR::GenericLowering(irFunction).lower(); - irFunction.dump(QStringLiteral("after generic lowering")); - irFunction.verify(); - - IR::NodeScheduler scheduler(&irFunction); - QScopedPointer<IR::MIFunction> miFunction(scheduler.buildMIFunction()); - miFunction->dump(QStringLiteral("initial MI")); - irFunction.verify(); -} - -} // QV4 namespace -QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index f374a092b7..a0b11c2c51 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -138,8 +138,6 @@ QT_BEGIN_NAMESPACE -Q_LOGGING_CATEGORY(lcTracingAll, "qt.v4.tracing.all") - using namespace QV4; #ifndef V4_BOOTSTRAP @@ -658,13 +656,6 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) ExecutionEngine::~ExecutionEngine() { - if (Q_UNLIKELY(lcTracingAll().isDebugEnabled())) { - for (auto cu : compilationUnits) { - for (auto f : qAsConst(cu->runtimeFunctions)) - qCDebug(lcTracingAll).noquote().nospace() << f->traceInfoToString(); - } - } - modules.clear(); delete m_multiplyWrappedQObjects; m_multiplyWrappedQObjects = nullptr; diff --git a/src/qml/jsruntime/qv4function.cpp b/src/qml/jsruntime/qv4function.cpp index 1bd4329fe8..debdf23d27 100644 --- a/src/qml/jsruntime/qv4function.cpp +++ b/src/qml/jsruntime/qv4function.cpp @@ -75,19 +75,12 @@ ReturnedValue Function::call(const Value *thisObject, const Value *argv, int arg Function *Function::create(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function) { - quint16 traceSlotCount = 0; -#if QT_CONFIG(qml_tracing) - traceSlotCount = function->nTraceInfos == CompiledData::Function::NoTracing() - ? 1 - : function->nTraceInfos; -#endif - quint8 *storage = new quint8[sizeof(Function) + traceSlotCount]; - return new(storage) Function(engine, unit, function); + return new Function(engine, unit, function); } void Function::destroy() { - delete[] reinterpret_cast<quint8 *>(this); + delete this; } Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function) @@ -111,13 +104,6 @@ Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, internalClass = ic->d(); nFormals = compiledFunction->nFormals; - -#if QT_CONFIG(qml_tracing) - if (tracingEnabled()) { - for (uint i = 0; i < function->nTraceInfos; ++i) - *traceInfo(i) = 0; - } -#endif } Function::~Function() @@ -188,22 +174,4 @@ QQmlSourceLocation Function::sourceLocation() const return QQmlSourceLocation(sourceFile(), compiledFunction->location.line, compiledFunction->location.column); } -QString Function::traceInfoToString() -{ - QString info = QLatin1String("=== Trace information for ") + name()->toQString() + QLatin1Char(':'); - if (!tracingEnabled()) - return info + QStringLiteral(" disabled. Interpreter call count: %1\n").arg(interpreterCallCount); - if (compiledFunction->nTraceInfos == 0) - return info + QLatin1String(" none.\n"); - - info += QLatin1Char('\n'); - for (uint i = 0, ei = compiledFunction->nTraceInfos; i < ei; ++i) { - auto bits = QString::number(*traceInfo(i), 2); - if (bits.size() < 8) - bits.prepend(QString(8 - bits.size(), '0')); - info += QStringLiteral(" %1: %2\n").arg(QString::number(i), bits); - } - return info; -} - QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4function_p.h b/src/qml/jsruntime/qv4function_p.h index f8125a58f8..01b212370d 100644 --- a/src/qml/jsruntime/qv4function_p.h +++ b/src/qml/jsruntime/qv4function_p.h @@ -123,31 +123,6 @@ public: return nullptr; return compilationUnit->runtimeFunctions[compiledFunction->nestedFunctionIndex]; } - - Q_NEVER_INLINE QString traceInfoToString(); - - quint8 *traceInfo(uint i) - { -#if QT_CONFIG(qml_tracing) - Q_ASSERT((tracingEnabled() && i < traceInfoCount()) || (i == 0)); - return reinterpret_cast<quint8 *>(this) + sizeof(Function) + i; -#else - Q_UNUSED(i); - return nullptr; -#endif - } - - quint32 traceInfoCount() const - { return compiledFunction->nTraceInfos; } - - bool tracingEnabled() const - { -#if QT_CONFIG(qml_tracing) - return traceInfoCount() != CompiledData::Function::NoTracing(); -#else - return false; -#endif - } }; } diff --git a/src/qml/jsruntime/qv4global_p.h b/src/qml/jsruntime/qv4global_p.h index 8a0ef5aa30..42b6edb6e2 100644 --- a/src/qml/jsruntime/qv4global_p.h +++ b/src/qml/jsruntime/qv4global_p.h @@ -236,20 +236,6 @@ struct IdentifierTable; class RegExpCache; class MultiplyWrappedQObjectMap; -enum class ObservedTraceValues : quint8 { - Integer = 1 << 0, - Boolean = 1 << 1, - Double = 1 << 2, - Other = 1 << 3, - TypeMask = Integer | Boolean | Double | Other, - - TruePathTaken = 1 << 0, - FalsePathTaken = 1 << 1, - - ArrayWasAccessed = 1 << 7, - ArrayAccessNeededFallback = 1 << 6, -}; - enum PropertyFlag { Attr_Data = 0, Attr_Accessor = 0x1, diff --git a/src/qml/jsruntime/qv4math_p.h b/src/qml/jsruntime/qv4math_p.h index a60a49a811..90246c4229 100644 --- a/src/qml/jsruntime/qv4math_p.h +++ b/src/qml/jsruntime/qv4math_p.h @@ -66,42 +66,27 @@ QT_BEGIN_NAMESPACE namespace QV4 { -static inline QMLJS_READONLY ReturnedValue add_int32(int a, int b, quint8 *traceInfo = nullptr) +static inline QMLJS_READONLY ReturnedValue add_int32(int a, int b) { int result; - if (Q_UNLIKELY(add_overflow(a, b, &result))) { - if (traceInfo) - *traceInfo |= quint8(QV4::ObservedTraceValues::Double); + if (Q_UNLIKELY(add_overflow(a, b, &result))) return Value::fromDouble(static_cast<double>(a) + b).asReturnedValue(); - } - if (traceInfo) - *traceInfo |= quint8(QV4::ObservedTraceValues::Integer); return Value::fromInt32(result).asReturnedValue(); } -static inline QMLJS_READONLY ReturnedValue sub_int32(int a, int b, quint8 *traceInfo = nullptr) +static inline QMLJS_READONLY ReturnedValue sub_int32(int a, int b) { int result; - if (Q_UNLIKELY(sub_overflow(a, b, &result))) { - if (traceInfo) - *traceInfo |= quint8(QV4::ObservedTraceValues::Double); + if (Q_UNLIKELY(sub_overflow(a, b, &result))) return Value::fromDouble(static_cast<double>(a) - b).asReturnedValue(); - } - if (traceInfo) - *traceInfo |= quint8(QV4::ObservedTraceValues::Integer); return Value::fromInt32(result).asReturnedValue(); } -static inline QMLJS_READONLY ReturnedValue mul_int32(int a, int b, quint8 *traceInfo = nullptr) +static inline QMLJS_READONLY ReturnedValue mul_int32(int a, int b) { int result; - if (Q_UNLIKELY(mul_overflow(a, b, &result))) { - if (traceInfo) - *traceInfo |= quint8(QV4::ObservedTraceValues::Double); + if (Q_UNLIKELY(mul_overflow(a, b, &result))) return Value::fromDouble(static_cast<double>(a) * b).asReturnedValue(); - } - if (traceInfo) - *traceInfo |= quint8(QV4::ObservedTraceValues::Integer); return Value::fromInt32(result).asReturnedValue(); } diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index 8f2b162106..e95dfa775f 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -717,30 +717,6 @@ ReturnedValue Runtime::LoadElement::call(ExecutionEngine *engine, const Value &o return getElementFallback(engine, object, index); } -ReturnedValue Runtime::LoadElement_Traced::call(ExecutionEngine *engine, const Value &object, const Value &index, quint8 *traceSlot) -{ - *traceSlot |= quint8(ObservedTraceValues::ArrayWasAccessed); - if (index.isPositiveInt()) { - uint idx = static_cast<uint>(index.int_32()); - if (Heap::Base *b = object.heapObject()) { - if (b->internalClass->vtable->isObject) { - Heap::Object *o = static_cast<Heap::Object *>(b); - if (o->arrayData && o->arrayData->type == Heap::ArrayData::Simple) { - Heap::SimpleArrayData *s = o->arrayData.cast<Heap::SimpleArrayData>(); - if (idx < s->values.size) - if (!s->data(idx).isEmpty()) - return s->data(idx).asReturnedValue(); - } - } - } - *traceSlot |= quint8(ObservedTraceValues::ArrayAccessNeededFallback); - return getElementIntFallback(engine, object, idx); - } - - *traceSlot |= quint8(ObservedTraceValues::ArrayAccessNeededFallback); - return getElementFallback(engine, object, index); -} - static Q_NEVER_INLINE bool setElementFallback(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value) { Scope scope(engine); @@ -796,30 +772,6 @@ void Runtime::StoreElement::call(ExecutionEngine *engine, const Value &object, c engine->throwTypeError(); } -void Runtime::StoreElement_traced::call(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value, quint8 *traceSlot) -{ - *traceSlot |= quint8(ObservedTraceValues::ArrayWasAccessed); - if (index.isPositiveInt()) { - uint idx = static_cast<uint>(index.int_32()); - if (Heap::Base *b = object.heapObject()) { - if (b->internalClass->vtable->isObject) { - Heap::Object *o = static_cast<Heap::Object *>(b); - if (o->arrayData && o->arrayData->type == Heap::ArrayData::Simple) { - Heap::SimpleArrayData *s = o->arrayData.cast<Heap::SimpleArrayData>(); - if (idx < s->values.size) { - s->setData(engine, idx, value); - return; - } - } - } - } - } - - *traceSlot |= quint8(ObservedTraceValues::ArrayAccessNeededFallback); - if (!setElementFallback(engine, object, index, value) && engine->currentStackFrame->v4Function->isStrict()) - engine->throwTypeError(); -} - ReturnedValue Runtime::GetIterator::call(ExecutionEngine *engine, const Value &in, int iterator) { Scope scope(engine); diff --git a/src/qml/jsruntime/qv4runtimeapi_p.h b/src/qml/jsruntime/qv4runtimeapi_p.h index 86cbccde23..13a73b7046 100644 --- a/src/qml/jsruntime/qv4runtimeapi_p.h +++ b/src/qml/jsruntime/qv4runtimeapi_p.h @@ -154,10 +154,6 @@ struct Q_QML_PRIVATE_EXPORT Runtime { { static void call(ExecutionEngine *, const Value &, const Value &, const Value &); }; - struct Q_QML_PRIVATE_EXPORT StoreElement_traced : Method<Throws::Yes> - { - static void call(ExecutionEngine *, const Value &, const Value &, const Value &, quint8 *); - }; struct Q_QML_PRIVATE_EXPORT LoadProperty : Method<Throws::Yes> { static ReturnedValue call(ExecutionEngine *, const Value &, int); @@ -170,10 +166,6 @@ struct Q_QML_PRIVATE_EXPORT Runtime { { static ReturnedValue call(ExecutionEngine *, const Value &, const Value &); }; - struct Q_QML_PRIVATE_EXPORT LoadElement_Traced : Method<Throws::Yes> - { - static ReturnedValue call(ExecutionEngine *, const Value &, const Value &, quint8 *); - }; struct Q_QML_PRIVATE_EXPORT LoadSuperProperty : Method<Throws::Yes> { static ReturnedValue call(ExecutionEngine *, const Value &); diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index 71e35bdc07..4c292d429a 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -350,82 +350,6 @@ static struct InstrCount { if (engine->hasException) \ goto handleUnwind -static inline void traceJumpTakesTruePath(bool truePathTaken, Function *f, int slot) -{ -#if QT_CONFIG(qml_tracing) - quint8 *traceInfo = f->traceInfo(slot); - Q_ASSERT(traceInfo); - *traceInfo |= truePathTaken ? quint8(ObservedTraceValues::TruePathTaken) - : quint8(ObservedTraceValues::FalsePathTaken); -#else - Q_UNUSED(truePathTaken); - Q_UNUSED(f); - Q_UNUSED(slot); -#endif -} - -static inline void traceValue(ReturnedValue acc, Function *f, int slot) -{ -#if QT_CONFIG(qml_tracing) - quint8 *traceInfo = f->traceInfo(slot); - Q_ASSERT(traceInfo); - switch (Primitive::fromReturnedValue(acc).type()) { - case QV4::Value::Integer_Type: - *traceInfo |= quint8(ObservedTraceValues::Integer); - break; - case QV4::Value::Boolean_Type: - *traceInfo |= quint8(ObservedTraceValues::Boolean); - break; - case QV4::Value::Double_Type: - *traceInfo |= quint8(ObservedTraceValues::Double); - break; - default: - *traceInfo |= quint8(ObservedTraceValues::Other); - break; - } -#else - Q_UNUSED(acc); - Q_UNUSED(f); - Q_UNUSED(slot); -#endif -} - -static inline void traceIntValue(Function *f, int slot) -{ -#if QT_CONFIG(qml_tracing) - quint8 *traceInfo = f->traceInfo(slot); - Q_ASSERT(traceInfo); - *traceInfo |= quint8(ObservedTraceValues::Integer); -#else - Q_UNUSED(f); - Q_UNUSED(slot); -#endif -} - -static inline void traceDoubleValue(Function *f, int slot) -{ -#if QT_CONFIG(qml_tracing) - quint8 *traceInfo = f->traceInfo(slot); - Q_ASSERT(traceInfo); - *traceInfo |= quint8(ObservedTraceValues::Double); -#else - Q_UNUSED(f); - Q_UNUSED(slot); -#endif -} - -static inline void traceOtherValue(Function *f, int slot) -{ -#if QT_CONFIG(qml_tracing) - quint8 *traceInfo = f->traceInfo(slot); - Q_ASSERT(traceInfo); - *traceInfo |= quint8(ObservedTraceValues::Other); -#else - Q_UNUSED(f); - Q_UNUSED(slot); -#endif -} - static inline Heap::CallContext *getScope(QV4::Value *stack, int level) { Heap::ExecutionContext *scope = static_cast<ExecutionContext &>(stack[CallData::Context]).d(); @@ -504,16 +428,10 @@ ReturnedValue VME::exec(CppStackFrame *frame, ExecutionEngine *engine) #if QT_CONFIG(qml_jit) if (debugger == nullptr) { if (function->jittedCode == nullptr) { - if (engine->canJIT(function)) { -#if QT_CONFIG(qml_tracing) - if (function->tracingEnabled()) - runTracingJit(function); - else -#endif - QV4::JIT::BaselineJIT(function).generate(); - } else { + if (engine->canJIT(function)) + QV4::JIT::BaselineJIT(function).generate(); + else ++function->interpreterCallCount; - } } } #endif // QT_CONFIG(qml_jit) @@ -525,22 +443,6 @@ ReturnedValue VME::exec(CppStackFrame *frame, ExecutionEngine *engine) ReturnedValue result; if (function->jittedCode != nullptr && debugger == nullptr) { result = function->jittedCode(frame, engine); - if (QV4::Value::fromReturnedValue(result).isEmpty()) { // de-optimize! - if (ShowWhenDeoptimiationHappens) { - // This is debug code, which is disabled by default, and completely removed by the - // compiler. - fprintf(stderr, "*********************** DEOPT! %s ***********************\n" - "*** deopt IP: %d, line: %d\n", - function->name()->toQString().toUtf8().constData(), - frame->instructionPointer, - frame->lineNumber()); - } - delete function->codeRef; - function->codeRef = nullptr; - function->jittedCode = nullptr; - function->interpreterCallCount = 0; // reset to restart tracing: apparently we didn't have enough info before - result = interpret(frame, engine, function->codeData + frame->instructionPointer); - } } else { // interpreter result = interpret(frame, engine, function->codeData); @@ -559,11 +461,6 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, QV4::ReturnedValue acc = accumulator.asReturnedValue(); Value *stack = reinterpret_cast<Value *>(frame->jsFrame); - if (function->tracingEnabled()) { - for (int i = 0; i < int(function->nFormals); ++i) - traceValue(frame->jsFrame->argument(i), function, i); - } - MOTH_JUMP_TABLE; for (;;) { @@ -622,7 +519,6 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, auto cc = static_cast<Heap::CallContext *>(stack[CallData::Context].m()); Q_ASSERT(cc->type != QV4::Heap::CallContext::Type_GlobalContext); acc = cc->locals[index].asReturnedValue(); - traceValue(acc, function, traceSlot); MOTH_END_INSTR(LoadLocal) MOTH_BEGIN_INSTR(StoreLocal) @@ -635,7 +531,6 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_BEGIN_INSTR(LoadScopedLocal) auto cc = getScope(stack, scope); acc = cc->locals[index].asReturnedValue(); - traceValue(acc, function, traceSlot); MOTH_END_INSTR(LoadScopedLocal) MOTH_BEGIN_INSTR(StoreScopedLocal) @@ -660,7 +555,6 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, STORE_IP(); acc = Runtime::LoadName::call(engine, name); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(LoadName) MOTH_BEGIN_INSTR(LoadGlobalLookup) @@ -668,7 +562,6 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, QV4::Lookup *l = function->compilationUnit->runtimeLookups + index; acc = l->globalGetter(l, engine); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(LoadGlobalLookup) MOTH_BEGIN_INSTR(LoadQmlContextPropertyLookup) @@ -676,7 +569,6 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, QV4::Lookup *l = function->compilationUnit->runtimeLookups + index; acc = l->qmlContextPropertyGetter(l, engine, nullptr); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(LoadQmlContextPropertyLookup) MOTH_BEGIN_INSTR(StoreNameStrict) @@ -696,25 +588,14 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_BEGIN_INSTR(LoadElement) STORE_IP(); STORE_ACC(); -#if QT_CONFIG(qml_tracing) - acc = Runtime::LoadElement_Traced::call(engine, STACK_VALUE(base), accumulator, function->traceInfo(traceSlot)); - traceValue(acc, function, traceSlot); -#else - Q_UNUSED(traceSlot); acc = Runtime::LoadElement::call(engine, STACK_VALUE(base), accumulator); -#endif CHECK_EXCEPTION; MOTH_END_INSTR(LoadElement) MOTH_BEGIN_INSTR(StoreElement) STORE_IP(); STORE_ACC(); -#if QT_CONFIG(qml_tracing) - Runtime::StoreElement_traced::call(engine, STACK_VALUE(base), STACK_VALUE(index), accumulator, function->traceInfo(traceSlot)); -#else - Q_UNUSED(traceSlot); Runtime::StoreElement::call(engine, STACK_VALUE(base), STACK_VALUE(index), accumulator); -#endif CHECK_EXCEPTION; MOTH_END_INSTR(StoreElement) @@ -723,7 +604,6 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, STORE_ACC(); acc = Runtime::LoadProperty::call(engine, accumulator, name); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(LoadProperty) MOTH_BEGIN_INSTR(GetLookup) @@ -742,7 +622,6 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, acc = l->getter(l, engine, accumulator); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(GetLookup) MOTH_BEGIN_INSTR(StoreProperty) @@ -816,7 +695,6 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, Value undef = Value::undefinedValue(); acc = static_cast<const FunctionObject &>(func).call(&undef, stack + argv, argc); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(CallValue) MOTH_BEGIN_INSTR(CallWithReceiver) @@ -828,14 +706,12 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, } acc = static_cast<const FunctionObject &>(func).call(stack + thisObject, stack + argv, argc); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(CallWithReceiver) MOTH_BEGIN_INSTR(CallProperty) STORE_IP(); acc = Runtime::CallProperty::call(engine, stack[base], name, stack + argv, argc); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(CallProperty) MOTH_BEGIN_INSTR(CallPropertyLookup) @@ -863,49 +739,42 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, acc = static_cast<FunctionObject &>(f).call(stack + base, stack + argv, argc); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(CallPropertyLookup) MOTH_BEGIN_INSTR(CallElement) STORE_IP(); acc = Runtime::CallElement::call(engine, stack[base], STACK_VALUE(index), stack + argv, argc); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(CallElement) MOTH_BEGIN_INSTR(CallName) STORE_IP(); acc = Runtime::CallName::call(engine, name, stack + argv, argc); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(CallName) MOTH_BEGIN_INSTR(CallPossiblyDirectEval) STORE_IP(); acc = Runtime::CallPossiblyDirectEval::call(engine, stack + argv, argc); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(CallPossiblyDirectEval) MOTH_BEGIN_INSTR(CallGlobalLookup) STORE_IP(); acc = Runtime::CallGlobalLookup::call(engine, index, stack + argv, argc); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(CallGlobalLookup) MOTH_BEGIN_INSTR(CallQmlContextPropertyLookup) STORE_IP(); acc = Runtime::CallQmlContextPropertyLookup::call(engine, index, stack + argv, argc); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(CallQmlContextPropertyLookup) MOTH_BEGIN_INSTR(CallWithSpread) STORE_IP(); acc = Runtime::CallWithSpread::call(engine, STACK_VALUE(func), STACK_VALUE(thisObject), stack + argv, argc); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(CallWithSpread) MOTH_BEGIN_INSTR(TailCall) @@ -1120,7 +989,6 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, takeJump = ACC.int_32(); else takeJump = ACC.toBoolean(); - traceJumpTakesTruePath(takeJump, function, traceSlot); if (takeJump) code += offset; MOTH_END_INSTR(JumpTrue) @@ -1131,7 +999,6 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, takeJump = !ACC.int_32(); else takeJump = !ACC.toBoolean(); - traceJumpTakesTruePath(!takeJump, function, traceSlot); if (takeJump) code += offset; MOTH_END_INSTR(JumpFalse) @@ -1291,12 +1158,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_END_INSTR(UNot) MOTH_BEGIN_INSTR(UPlus) - if (Q_LIKELY(ACC.isNumber())) { - if (ACC.isDouble()) - traceDoubleValue(function, traceSlot); - else - traceIntValue(function, traceSlot); - } else { + if (Q_UNLIKELY(!ACC.isNumber())) { acc = Encode(ACC.toNumberImpl()); CHECK_EXCEPTION; } @@ -1307,17 +1169,14 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, int a = ACC.int_32(); if (a == 0 || a == std::numeric_limits<int>::min()) { acc = Encode(-static_cast<double>(a)); - traceDoubleValue(function, traceSlot); } else { - acc = sub_int32(0, ACC.int_32(), function->traceInfo(traceSlot)); + acc = sub_int32(0, ACC.int_32()); } } else if (ACC.isDouble()) { acc ^= (1ull << 63); // simply flip sign bit - traceDoubleValue(function, traceSlot); } else { acc = Encode(-ACC.toNumberImpl()); CHECK_EXCEPTION; - traceOtherValue(function, traceSlot); } MOTH_END_INSTR(UMinus) @@ -1328,57 +1187,49 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_BEGIN_INSTR(Increment) if (Q_LIKELY(ACC.integerCompatible())) { - acc = add_int32(ACC.int_32(), 1, function->traceInfo(traceSlot)); + acc = add_int32(ACC.int_32(), 1); } else if (ACC.isDouble()) { acc = QV4::Encode(ACC.doubleValue() + 1.); - traceDoubleValue(function, traceSlot); } else { acc = Encode(ACC.toNumberImpl() + 1.); CHECK_EXCEPTION; - traceDoubleValue(function, traceSlot); } MOTH_END_INSTR(Increment) MOTH_BEGIN_INSTR(Decrement) if (Q_LIKELY(ACC.integerCompatible())) { - acc = sub_int32(ACC.int_32(), 1, function->traceInfo(traceSlot)); + acc = sub_int32(ACC.int_32(), 1); } else if (ACC.isDouble()) { acc = QV4::Encode(ACC.doubleValue() - 1.); - traceDoubleValue(function, traceSlot); } else { acc = Encode(ACC.toNumberImpl() - 1.); CHECK_EXCEPTION; - traceDoubleValue(function, traceSlot); } MOTH_END_INSTR(Decrement) MOTH_BEGIN_INSTR(Add) const Value left = STACK_VALUE(lhs); if (Q_LIKELY(Value::integerCompatible(left, ACC))) { - acc = add_int32(left.int_32(), ACC.int_32(), function->traceInfo(traceSlot)); + acc = add_int32(left.int_32(), ACC.int_32()); } else if (left.isNumber() && ACC.isNumber()) { acc = Encode(left.asDouble() + ACC.asDouble()); - traceDoubleValue(function, traceSlot); } else { STORE_ACC(); acc = Runtime::Add::call(engine, left, accumulator); CHECK_EXCEPTION; - traceOtherValue(function, traceSlot); } MOTH_END_INSTR(Add) MOTH_BEGIN_INSTR(Sub) const Value left = STACK_VALUE(lhs); if (Q_LIKELY(Value::integerCompatible(left, ACC))) { - acc = sub_int32(left.int_32(), ACC.int_32(), function->traceInfo(traceSlot)); + acc = sub_int32(left.int_32(), ACC.int_32()); } else if (left.isNumber() && ACC.isNumber()) { acc = Encode(left.asDouble() - ACC.asDouble()); - traceDoubleValue(function, traceSlot); } else { STORE_ACC(); acc = Runtime::Sub::call(left, accumulator); CHECK_EXCEPTION; - traceOtherValue(function, traceSlot); } MOTH_END_INSTR(Sub) @@ -1395,15 +1246,13 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_BEGIN_INSTR(Mul) const Value left = STACK_VALUE(lhs); if (Q_LIKELY(Value::integerCompatible(left, ACC))) { - acc = mul_int32(left.int_32(), ACC.int_32(), function->traceInfo(traceSlot)); + acc = mul_int32(left.int_32(), ACC.int_32()); } else if (left.isNumber() && ACC.isNumber()) { acc = Encode(left.asDouble() * ACC.asDouble()); - traceDoubleValue(function, traceSlot); } else { STORE_ACC(); acc = Runtime::Mul::call(left, accumulator); CHECK_EXCEPTION; - traceOtherValue(function, traceSlot); } MOTH_END_INSTR(Mul) @@ -1417,7 +1266,6 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, STORE_ACC(); acc = Runtime::Mod::call(STACK_VALUE(lhs), accumulator); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(Mod) MOTH_BEGIN_INSTR(BitAnd) diff --git a/src/qml/jsruntime/qv4vme_moth_p.h b/src/qml/jsruntime/qv4vme_moth_p.h index 4ac7120d36..8a76e60f20 100644 --- a/src/qml/jsruntime/qv4vme_moth_p.h +++ b/src/qml/jsruntime/qv4vme_moth_p.h @@ -58,8 +58,6 @@ QT_BEGIN_NAMESPACE namespace QV4 { namespace Moth { -void runTracingJit(QV4::Function *function); - class VME { public: diff --git a/src/qml/qtqmlglobal.h b/src/qml/qtqmlglobal.h index 348cfd86b8..a111f72e81 100644 --- a/src/qml/qtqmlglobal.h +++ b/src/qml/qtqmlglobal.h @@ -54,7 +54,6 @@ # define QT_FEATURE_qml_debug -1 # define QT_FEATURE_qml_sequence_object 1 # define QT_FEATURE_qml_jit -1 -# define QT_FEATURE_qml_tracing -1 #endif QT_BEGIN_NAMESPACE |